Sun, 30 Jan 2022 15:25:29 +0100
implement pg_vfs_open
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/development/postgresql_vfs.sql Sun Jan 30 15:25:29 2022 +0100 @@ -0,0 +1,9 @@ + +create table Resource ( + resource_id serial primary key, + parent_id int references Resource(resource_id), + nodename text not null, + iscollection boolean not null default(false), + + unique(parent_id, nodename) +);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/development/postgresql_vfs_testdata.sql Sun Jan 30 15:25:29 2022 +0100 @@ -0,0 +1,20 @@ + + +insert into Resource (nodename, iscollection) values ('', true); + +insert into Resource(parent_id, nodename) values +((select resource_id from Resource where parent_id is null), 'file1.txt'), +((select resource_id from Resource where parent_id is null), 'file2.txt'), +((select resource_id from Resource where parent_id is null), 'file3.txt'), +((select resource_id from Resource where parent_id is null), 'file4.txt'); + +insert into Resource(parent_id, nodename, iscollection) values +((select resource_id from Resource where parent_id is null), 'dir1', true); + +insert into Resource(parent_id, nodename, iscollection) values +((select resource_id from Resource where nodename = 'dir1'), 'dir2', true); + +insert into Resource(parent_id, nodename) values +((select resource_id from Resource where nodename = 'dir1'), 'd1file1.txt'), +((select resource_id from Resource where nodename = 'dir2'), 'd2file1.txt'), +((select resource_id from Resource where nodename = 'dir2'), 'd2file2.txt');
--- a/src/server/plugins/postgresql/service.c Fri Jan 28 16:01:05 2022 +0100 +++ b/src/server/plugins/postgresql/service.c Sun Jan 30 15:25:29 2022 +0100 @@ -107,5 +107,7 @@ net_printf(sn->csd, "%s", "</body></html>\n"); + PQclear(result); + return REQ_PROCEED; }
--- a/src/server/plugins/postgresql/vfs.c Fri Jan 28 16:01:05 2022 +0100 +++ b/src/server/plugins/postgresql/vfs.c Sun Jan 30 15:25:29 2022 +0100 @@ -28,6 +28,8 @@ #include "vfs.h" +#include "../../util/util.h" + static VFS pg_vfs_class = { pg_vfs_open, pg_vfs_stat, @@ -56,10 +58,43 @@ }; +/* + * SQL Queries + */ + +// Resolvs a path into resource_id and parent_id +// params: $1: path string +static const char *sql_resolve_path = + "with recursive resolvepath as (\n\ + select\n\ + resource_id,\n\ + parent_id,\n\ + iscollection,\n\ + regexp_split_to_array($1, '/') as pathelm,\n\ + '' as fullpath,\n\ + 1 as pathdepth\n\ + from Resource\n\ + where parent_id is null\n\ + union\n\ + select\n\ + r.resource_id,\n\ + r.parent_id,\n\ + iscollection,\n\ + p.pathelm,\n\ + p.fullpath || '/' || r.nodename,\n\ + p.pathdepth + 1\n\ + from Resource r\n\ + inner join resolvepath p on r.parent_id = p.resource_id\n\ + where p.pathelm[p.pathdepth+1] = r.nodename\n\ + )\ + select resource_id, parent_id, fullpath, iscollection from resolvepath\n\ + where fullpath = '$1;"; + + VFS* pg_vfs_create(Session *sn, Request *rq, pblock *pb) { // resourcepool is required - char *resource_pool = pblock_find("resourcepool", pb); + char *resource_pool = pblock_findval("resourcepool", pb); if(!resource_pool) { log_ereport(LOG_MISCONFIG, "pg_vfs_create: missing resourcepool parameter"); return NULL; @@ -95,10 +130,89 @@ } +int pg_resolve_path(VFSContext *ctx, const char *path, int64_t *parent_id, int64_t *resource_id, const char **resource_name, WSBool *iscollection) { + // basic path validation + if(!path) return 1; + size_t pathlen = strlen(path); + if(pathlen == 0) return 1; + if(path[0] != '/') { + return 1; + } + + // get last node of path + *resource_name = util_resource_name(path); + + VFS *vfs = ctx->vfs; + PgVFS *pg = vfs->instance; + + + PGresult *result = PQexecParams( + pg->connection, + sql_resolve_path, + 1, // number of parameters + NULL, + &path, // parameter value + NULL, + NULL, + 0); // 1: result in text format + + if(!result) return 1; + + int ret = 1; + int nfields = PQnfields(result); + int nrows = PQntuples(result); + if(nrows == 1 && nfields == 4) { + char *resource_id_str = PQgetvalue(result, 0, 0); + char *parent_id_str = PQgetvalue(result, 0, 1); + char *iscol = PQgetvalue(result, 0, 2); + if(resource_id_str && parent_id_str) { + if(util_strtoint(resource_id_str, resource_id) && util_strtoint(parent_id_str, parent_id)) { + ret = 0; // success + } + } + if(iscol) { + *iscollection = iscol[0] == 't' ? TRUE : FALSE; + } + } else { + ctx->vfs_errno = ENOENT; + } + + PQclear(result); + + return ret; +} + + /* -------------------------- VFS functions -------------------------- */ SYS_FILE pg_vfs_open(VFSContext *ctx, const char *path, int oflags) { + const char *resname; + int64_t resource_id, parent_id; + WSBool iscollection; + if(pg_resolve_path(ctx, path, &resource_id, &parent_id, &resname, &iscollection)) { + return NULL; + } + VFSFile *file = pool_malloc(ctx->pool, sizeof(VFSFile)); + if(!file) { + return NULL; + } + PgFile *pgfile = pool_malloc(ctx->pool, sizeof(PgFile)); + if(!pgfile) { + pool_free(ctx->pool, file); + return NULL; + } + + pgfile->iscollection = iscollection; + pgfile->resource_id = resource_id; + pgfile->parent_id = parent_id; + + file->ctx = ctx; + file->io = iscollection ? NULL : &pg_vfs_io_class; + file->fd = -1; + file->data = pgfile; + + return file; } int pg_vfs_stat(VFSContext *ctx, const char *path, struct stat *buf) {
--- a/src/server/plugins/postgresql/vfs.h Fri Jan 28 16:01:05 2022 +0100 +++ b/src/server/plugins/postgresql/vfs.h Sun Jan 30 15:25:29 2022 +0100 @@ -42,8 +42,24 @@ ResourceData *pg_resource; PGconn *connection; } PgVFS; - + +typedef struct PgFile { + int64_t resource_id; + int64_t parent_id; + WSBool iscollection; +} PgFile; + VFS* pg_vfs_create(Session *sn, Request *rq, pblock *pb); + + +/* + * Resolve a path into a parent_id and resource name + * + * Only absolute paths are supported, therefore path[0] must be '/' + * + * If the resource is not found, ctx->vfs_errno is set to ENOENT + */ +int pg_resolve_path(VFSContext *ctx, const char *path, int64_t *parent_id, int64_t *resource_id, const char **resource_name, WSBool *iscollection); SYS_FILE pg_vfs_open(VFSContext *ctx, const char *path, int oflags); int pg_vfs_stat(VFSContext *ctx, const char *path, struct stat *buf);
--- a/src/server/util/util.c Fri Jan 28 16:01:05 2022 +0100 +++ b/src/server/util/util.c Sun Jan 30 15:25:29 2022 +0100 @@ -419,6 +419,19 @@ } } +NSAPI_PUBLIC const char* util_resource_name(const char *url) { + scstr_t urlstr = scstr(url); + if(urlstr.ptr[urlstr.length-1] == '/') { + urlstr.length--; + } + scstr_t resname = scstrrchr(urlstr, '/'); + if(resname.length > 1) { + return resname.ptr+1; + } else { + return url; + } +} + /* ------------------------------ util_itoa ------------------------------- */ /*
--- a/src/server/util/util.h Fri Jan 28 16:01:05 2022 +0100 +++ b/src/server/util/util.h Sun Jan 30 15:25:29 2022 +0100 @@ -201,6 +201,7 @@ // new NSAPI_PUBLIC int util_strtoint(char *str, int64_t *value); +NSAPI_PUBLIC const char* util_resource_name(const char *url); // TODO //NSAPI_PUBLIC PRIntervalTime INTutil_getinterval(const char *v, PRIntervalTime def);