# HG changeset patch # User Olaf Wintermann # Date 1643734070 -3600 # Node ID e9dc53661df414da3a6e80aef3e47d3237856e06 # Parent d0d5a970292fcba992506b2ebbc576c8187ce10d add pg vfs stat/fstat implementation diff -r d0d5a970292f -r e9dc53661df4 doc/development/postgresql_vfs.sql --- a/doc/development/postgresql_vfs.sql Mon Jan 31 21:33:46 2022 +0100 +++ b/doc/development/postgresql_vfs.sql Tue Feb 01 17:47:50 2022 +0100 @@ -3,7 +3,12 @@ resource_id serial primary key, parent_id int references Resource(resource_id), nodename text not null, - iscollection boolean not null default(false), + iscollection boolean not null default false, + + lastmodified timestamp not null default current_date, + creationdate timestamp not null default current_date, + contentlength bigint not null default 0, unique(parent_id, nodename) ); + diff -r d0d5a970292f -r e9dc53661df4 doc/development/postgresql_vfs_testdata.sql --- a/doc/development/postgresql_vfs_testdata.sql Mon Jan 31 21:33:46 2022 +0100 +++ b/doc/development/postgresql_vfs_testdata.sql Tue Feb 01 17:47:50 2022 +0100 @@ -1,20 +1,29 @@ - - -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); +do $$ +declare + res_id int; +begin + insert into Resource (nodename, iscollection) values ('', true); + res_id := lastval(); + + insert into Resource(parent_id, nodename) values + (res_id, 'file1.txt'), + (res_id, 'file2.txt'), + (res_id, 'file3.txt'), + (res_id, 'file4.txt'); + + insert into Resource(parent_id, nodename, iscollection) values + (res_id, 'dir1', true); + res_id := lastval(); + + insert into Resource(parent_id, nodename, iscollection) values + (res_id, 'dir2', true); + res_id := lastval(); + + insert into Resource(parent_id, nodename) values + (res_id, 'd1file1.txt'), + (res_id, 'd2file1.txt'), + (res_id, 'd2file2.txt'); -insert into Resource(parent_id, nodename, iscollection) values -((select resource_id from Resource where nodename = 'dir1'), 'dir2', true); +end $$; -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'); diff -r d0d5a970292f -r e9dc53661df4 src/server/plugins/postgresql/vfs.c --- a/src/server/plugins/postgresql/vfs.c Mon Jan 31 21:33:46 2022 +0100 +++ b/src/server/plugins/postgresql/vfs.c Tue Feb 01 17:47:50 2022 +0100 @@ -71,9 +71,12 @@ select\n\ resource_id,\n\ parent_id,\n\ + '' as fullpath,\n\ iscollection,\n\ + lastmodified,\n\ + creationdate,\n\ + contentlength,\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\ @@ -81,9 +84,12 @@ select\n\ r.resource_id,\n\ r.parent_id,\n\ + p.fullpath || '/' || r.nodename,\n\ r.iscollection,\n\ + r.lastmodified,\n\ + r.creationdate,\n\ + r.contentlength,\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\ @@ -94,12 +100,15 @@ // Same as sql_resolve_path, but it returns the root collection // params: $1: path string (should be '/') -static const char *sql_get_root = "select resource_id, parent_id, $1 as fullpath, true as iscollection from Resource where parent_id is null;"; +static const char *sql_get_root = "select resource_id, parent_id, $1 as fullpath, true as iscollection, lastmodified, creationdate, contentlength from Resource where parent_id is null;"; // Get all children of a specific collection // params: $1: parent resource_id -static const char *sql_get_children = "select resource_id, nodename, iscollection from Resource where parent_id = $1;"; +static const char *sql_get_children = "select resource_id, nodename, iscollection, lastmodified, creationdate, contentlength from Resource where parent_id = $1;"; +// Get resource +// params: $1: resource_id +static const char *sql_get_resource = "select resource_id, nodename, iscollection, lastmodified, creationdate, contentlength from Resource where resource_id = $1;"; VFS* pg_vfs_create(Session *sn, Request *rq, pblock *pb) { // resourcepool is required @@ -141,7 +150,15 @@ } -int pg_resolve_path(VFSContext *ctx, const char *path, int64_t *parent_id, int64_t *resource_id, const char **resource_name, WSBool *iscollection) { +int pg_resolve_path( + VFSContext *ctx, + const char *path, + int64_t *parent_id, + int64_t *resource_id, + const char **resource_name, + WSBool *iscollection, + struct stat *s) +{ // basic path validation if(!path) return 1; size_t pathlen = strlen(path); @@ -150,13 +167,21 @@ return 1; } + char *pathf = NULL; + if(pathlen > 1 && path[pathlen-1] == '/') { + pathf = malloc(pathlen); + memcpy(pathf, path, pathlen); + pathf[pathlen-1] = 0; // remove trailing '/' + path = pathf; + } + // get last node of path *resource_name = util_resource_name(path); VFS *vfs = ctx->vfs; PgVFS *pg = vfs->instance; - const char *sql = pathlen == 1 ? sql_get_root : sql_resolve_path; + const char *sql = pathlen == 1 ? sql_get_root : sql_resolve_path; PGresult *result = PQexecParams( pg->connection, sql, @@ -167,15 +192,22 @@ NULL, 0); // 0: result in text format + if(pathf) { + free(pathf); + } + if(!result) return 1; int ret = 1; - int nfields = PQnfields(result); + //int nfields = PQnfields(result); int nrows = PQntuples(result); - if(nrows == 1 && nfields == 4) { + if(nrows == 1) { char *resource_id_str = PQgetvalue(result, 0, 0); char *parent_id_str = PQgetvalue(result, 0, 1); char *iscol = PQgetvalue(result, 0, 3); + char *lastmodified = PQgetvalue(result, 0, 4); + char *creationdate = PQgetvalue(result, 0, 5); + char *contentlength = PQgetvalue(result, 0, 6); if(resource_id_str && parent_id_str) { if(util_strtoint(resource_id_str, resource_id)) { ret = 0; // success @@ -183,9 +215,31 @@ // optionally get parent_id util_strtoint(parent_id_str, parent_id); } - if(iscol) { + + if(iscollection && iscol) { *iscollection = iscol[0] == 't' ? TRUE : FALSE; } + + if(s) { + memset(s, 0, sizeof(struct stat)); + + s->st_ino = *resource_id; + if(iscol) { + s->st_mode |= 0x4000; + } + if(lastmodified) { + // TODO + } + if(creationdate) { + // TODO + } + if(contentlength) { + int64_t len; + if(util_strtoint(contentlength, &len)) { + s->st_size = len; + } + } + } } else { ctx->vfs_errno = ENOENT; } @@ -204,7 +258,8 @@ resource_id = -1; parent_id = -1; WSBool iscollection; - if(pg_resolve_path(ctx, path, &parent_id, &resource_id, &resname, &iscollection)) { + struct stat s; + if(pg_resolve_path(ctx, path, &parent_id, &resource_id, &resname, &iscollection, &s)) { return NULL; } @@ -221,9 +276,10 @@ pgfile->iscollection = iscollection; pgfile->resource_id = resource_id; pgfile->parent_id = parent_id; + pgfile->s = s; file->ctx = ctx; - file->io = iscollection ? NULL : &pg_vfs_io_class; + file->io = iscollection ? &pg_vfs_io_class : &pg_vfs_io_class; file->fd = -1; file->data = pgfile; @@ -231,11 +287,16 @@ } int pg_vfs_stat(VFSContext *ctx, const char *path, struct stat *buf) { - + int64_t parent_id, resource_id; + const char *resname; + WSBool iscollection; + return pg_resolve_path(ctx, path, &parent_id, &resource_id, &resname, &iscollection, buf); } int pg_vfs_fstat(VFSContext *ctx, SYS_FILE fd, struct stat *buf) { - + PgFile *pgfile = fd->data; + memcpy(buf, &pgfile->s, sizeof(struct stat)); + return 0; } VFS_DIR pg_vfs_opendir(VFSContext *ctx, const char *path) { @@ -334,7 +395,7 @@ sql_get_children, 1, // number of parameters NULL, - ¶m, // param: parent resource_id + ¶m, // param: parent resource_id NULL, NULL, 0); // 0: result in text format diff -r d0d5a970292f -r e9dc53661df4 src/server/plugins/postgresql/vfs.h --- a/src/server/plugins/postgresql/vfs.h Mon Jan 31 21:33:46 2022 +0100 +++ b/src/server/plugins/postgresql/vfs.h Tue Feb 01 17:47:50 2022 +0100 @@ -47,6 +47,8 @@ int64_t resource_id; int64_t parent_id; WSBool iscollection; + + struct stat s; } PgFile; typedef struct PgDir { @@ -66,7 +68,14 @@ * * 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); +int pg_resolve_path( + VFSContext *ctx, + const char *path, + int64_t *parent_id, + int64_t *resource_id, + const char **resource_name, + WSBool *iscollection, + struct stat *s); SYS_FILE pg_vfs_open(VFSContext *ctx, const char *path, int oflags); int pg_vfs_stat(VFSContext *ctx, const char *path, struct stat *buf);