implement pg_vfs_open webdav

Sun, 30 Jan 2022 15:25:29 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 30 Jan 2022 15:25:29 +0100
branch
webdav
changeset 278
38bf7b42b58c
parent 277
7608af69739f
child 279
79029fe26aae

implement pg_vfs_open

doc/development/postgresql_vfs.sql file | annotate | diff | comparison | revisions
doc/development/postgresql_vfs_testdata.sql file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/service.c file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/vfs.c file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/vfs.h file | annotate | diff | comparison | revisions
src/server/util/util.c file | annotate | diff | comparison | revisions
src/server/util/util.h file | annotate | diff | comparison | revisions
--- /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);

mercurial