diff -r 7608af69739f -r 38bf7b42b58c src/server/plugins/postgresql/vfs.c --- 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) {