src/server/daemon/vfs.c

changeset 385
a1f4cb076d2f
parent 366
47bc686fafe4
child 415
d938228c382e
--- a/src/server/daemon/vfs.c	Tue Aug 13 22:14:32 2019 +0200
+++ b/src/server/daemon/vfs.c	Sat Sep 24 16:26:10 2022 +0200
@@ -45,16 +45,19 @@
 #define VFS_MALLOC(pool, size) pool ? pool_malloc(pool, size) : malloc(size)
 #define VFS_FREE(pool, ptr) pool ? pool_free(pool, ptr) : free(ptr)
 
-static UcxMap *vfs_map;
+static UcxMap *vfs_type_map;
 
 static VFS sys_vfs = {
     sys_vfs_open,
     sys_vfs_stat,
     sys_vfs_fstat,
     sys_vfs_opendir,
+    sys_vfs_fdopendir,
     sys_vfs_mkdir,
     sys_vfs_unlink,
-    VFS_CHECKS_ACL
+    sys_vfs_rmdir,
+    VFS_CHECKS_ACL,
+    NULL
 };
 
 static VFS_IO sys_file_io = {
@@ -66,8 +69,9 @@
     sys_file_close,
     //sys_file_aioread,
     //sys_file_aiowrite,
-    NULL,
-    NULL
+    NULL,  // aioread
+    NULL,  // aiowrite
+    NULL   // getetag
 };
 
 static VFS_DIRIO sys_dir_io = {
@@ -75,21 +79,58 @@
     sys_dir_close
 };
 
-int vfs_init() {
-    vfs_map = ucx_map_new(16);
-    if(!vfs_map) {
+int vfs_init(void) {
+    vfs_type_map = ucx_map_new(16);
+    if(!vfs_type_map) {
         return -1;
     }
     return 0;
 }
 
-void vfs_add(char *name, VFS *vfs) {
+int vfs_register_type(const char *name, vfs_init_func vfsInit, vfs_create_func vfsCreate) {
     WS_ASSERT(name);
     
-    if(!vfs_map) {
-        vfs_init();
+    if(!vfs_type_map) {
+        if(vfs_init()) {
+            return 1;
+        }
+    }
+    
+    VfsType *vfsType = malloc(sizeof(VfsType));
+    if(!vfsType) {
+        return 1;
     }
-    ucx_map_cstr_put(vfs_map, name, vfs);
+    vfsType->init = vfsInit;
+    vfsType->create = vfsCreate;
+    
+    return ucx_map_cstr_put(vfs_type_map, name, vfsType);
+}
+
+VfsType* vfs_get_type(scstr_t vfs_class) {
+    return ucx_map_sstr_get(vfs_type_map, vfs_class);
+}
+
+void* vfs_init_backend(ServerConfiguration *cfg, pool_handle_t *pool, VfsType *vfs_class, WSConfigNode *config, int *error) {
+    *error = 0;
+    if(vfs_class->init) {
+        void *initData = vfs_class->init(cfg, pool, config);
+        if(!initData) {
+            *error = 1;
+        }
+        return initData;
+    } else {
+        return NULL;
+    }
+}
+
+VFS* vfs_create(Session *sn, Request *rq, const char *vfs_class, pblock *pb, void *initData) {
+    VfsType *vfsType = ucx_map_cstr_get(vfs_type_map, vfs_class);
+    if(!vfsType) {
+        log_ereport(LOG_MISCONFIG, "vfs_create: unkown VFS type %s", vfs_class);
+        return NULL;
+    }
+    
+    return vfsType->create(sn, rq, pb, initData);
 }
 
 VFSContext* vfs_request_context(Session *sn, Request *rq) {
@@ -97,6 +138,9 @@
     WS_ASSERT(rq);
     
     VFSContext *ctx = pool_malloc(sn->pool, sizeof(VFSContext));
+    if(!ctx) {
+        return NULL;
+    }
     ctx->sn = sn;
     ctx->rq = rq;
     ctx->vfs = rq->vfs ? rq->vfs : &sys_vfs;
@@ -105,10 +149,11 @@
     ctx->aclreqaccess = rq->aclreqaccess;
     ctx->pool = sn->pool;
     ctx->vfs_errno = 0;
+    ctx->error_response_set = 0;
     return ctx;
 }
 
-SYS_FILE vfs_open(VFSContext *ctx, char *path, int oflags) {
+SYS_FILE vfs_open(VFSContext *ctx, const char *path, int oflags) {
     WS_ASSERT(ctx);
     WS_ASSERT(path);
     
@@ -125,23 +170,26 @@
         }
     }
     SYS_FILE file = ctx->vfs->open(ctx, path, oflags);
-    ctx->aclreqaccess = m; // restore original access mask
+    ctx->aclreqaccess = m; // restore original access mask 
+    if(!file && ctx) {
+        sys_set_error_status(ctx);
+    }
     return file;
 }
 
-SYS_FILE vfs_openRO(VFSContext *ctx, char *path) {
+SYS_FILE vfs_openRO(VFSContext *ctx, const char *path) {
     return vfs_open(ctx, path, O_RDONLY);
 }
 
-SYS_FILE vfs_openWO(VFSContext *ctx, char *path) {
+SYS_FILE vfs_openWO(VFSContext *ctx, const char *path) {
     return vfs_open(ctx, path, O_WRONLY | O_CREAT);
 }
 
-SYS_FILE vfs_openRW(VFSContext *ctx, char *path) {
+SYS_FILE vfs_openRW(VFSContext *ctx, const char *path) {
     return vfs_open(ctx, path, O_RDONLY | O_WRONLY | O_CREAT);
 }
 
-int vfs_stat(VFSContext *ctx, char *path, struct stat *buf) {
+int vfs_stat(VFSContext *ctx, const char *path, struct stat *buf) {
     WS_ASSERT(ctx);
     WS_ASSERT(path);
     
@@ -159,6 +207,9 @@
     }
     int ret = ctx->vfs->stat(ctx, path, buf);
     ctx->aclreqaccess = m; // restore original access mask
+    if(ret && ctx) {
+        sys_set_error_status(ctx);
+    }
     return ret;
 }
 
@@ -167,7 +218,20 @@
     WS_ASSERT(fd);
     WS_ASSERT(buf);
     
-    return ctx->vfs->fstat(ctx, fd, buf);
+    int ret = ctx->vfs->fstat(ctx, fd, buf);
+    if(ret && ctx) {
+        sys_set_error_status(ctx);
+    }
+    return ret;
+}
+
+const char * vfs_getetag(SYS_FILE fd) {
+    WS_ASSERT(fd);
+    
+    if(fd->io->opt_getetag) {
+        return fd->io->opt_getetag(fd);
+    }
+    return NULL;
 }
 
 void vfs_close(SYS_FILE fd) {
@@ -181,7 +245,7 @@
     }
 }
 
-VFS_DIR vfs_opendir(VFSContext *ctx, char *path) {
+VFS_DIR vfs_opendir(VFSContext *ctx, const char *path) {
     WS_ASSERT(ctx);
     WS_ASSERT(path);
     
@@ -199,6 +263,33 @@
     }
     VFS_DIR dir = ctx->vfs->opendir(ctx, path);
     ctx->aclreqaccess = m; // restore original access mask
+    if(!dir && ctx) {
+        sys_set_error_status(ctx);
+    }
+    return dir;
+}
+
+VFS_DIR vfs_fdopendir(VFSContext *ctx, SYS_FILE fd) {
+    WS_ASSERT(ctx);
+    WS_ASSERT(path);
+    
+    uint32_t access_mask = ctx->aclreqaccess | ACL_LIST;
+    
+    // ctx->aclreqaccess should be the complete access mask
+    uint32_t m = ctx->aclreqaccess; // save original access mask
+    ctx->aclreqaccess = access_mask; // set mask for vfs->open call
+    if((ctx->vfs->flags & VFS_CHECKS_ACL) != VFS_CHECKS_ACL) {
+        // VFS does not evaluates the ACL itself, so we have to do it here
+        SysACL sysacl;
+        if(sys_acl_check(ctx, access_mask, &sysacl)) {
+            return NULL;
+        }
+    }
+    VFS_DIR dir = ctx->vfs->fdopendir(ctx, fd);
+    ctx->aclreqaccess = m; // restore original access mask
+    if(!dir && ctx) {
+        sys_set_error_status(ctx);
+    }
     return dir;
 }
 
@@ -227,23 +318,29 @@
     }
 }
 
-int vfs_mkdir(VFSContext *ctx, char *path) {
+int vfs_mkdir(VFSContext *ctx, const char *path) {
     WS_ASSERT(ctx);
     WS_ASSERT(path);
     
     return vfs_path_op(ctx, path, ctx->vfs->mkdir, ACL_ADD_FILE);
 }
 
-int vfs_unlink(VFSContext *ctx, char *path) {
+int vfs_unlink(VFSContext *ctx, const char *path) {
     WS_ASSERT(ctx);
     WS_ASSERT(path);
     
     return vfs_path_op(ctx, path, ctx->vfs->unlink, ACL_DELETE);
 }
 
+int vfs_rmdir(VFSContext *ctx, const char *path) {
+    WS_ASSERT(ctx);
+    WS_ASSERT(path);
+    
+    return vfs_path_op(ctx, path, ctx->vfs->rmdir, ACL_DELETE);
+}
 
 // private
-int vfs_path_op(VFSContext *ctx, char *path, vfs_op_f op, uint32_t access) {  
+int vfs_path_op(VFSContext *ctx, const char *path, vfs_op_f op, uint32_t access) {  
     uint32_t access_mask = ctx->aclreqaccess;
     access_mask |= access;
     
@@ -259,12 +356,15 @@
     }
     int ret = op(ctx, path);
     ctx->aclreqaccess = m; // restore original access mask
+    if(ret && ctx) {
+        sys_set_error_status(ctx);
+    }
     return ret;
 }
 
 /* system vfs implementation */
 
-SYS_FILE sys_vfs_open(VFSContext *ctx, char *path, int oflags) {
+SYS_FILE sys_vfs_open(VFSContext *ctx, const char *path, int oflags) {
     uint32_t access_mask = ctx->aclreqaccess;
     pool_handle_t *pool = ctx->pool;
     
@@ -313,7 +413,7 @@
     return file;
 }
 
-int sys_vfs_stat(VFSContext *ctx, char *path, struct stat *buf) {
+int sys_vfs_stat(VFSContext *ctx, const char *path, struct stat *buf) {
     uint32_t access_mask = ctx->aclreqaccess;
     
     // check ACLs
@@ -353,7 +453,7 @@
     return 0;
 }
 
-VFS_DIR sys_vfs_opendir(VFSContext *ctx, char *path) {
+VFS_DIR sys_vfs_opendir(VFSContext *ctx, const char *path) {
     uint32_t access_mask = ctx->aclreqaccess;
     pool_handle_t *pool = ctx->pool;
     
@@ -386,6 +486,9 @@
     DIR *sys_dir = fdopendir(dir_fd);
 #endif
     if(!sys_dir) {
+        if(dir_fd > 0) {
+            close(dir_fd);
+        }
         if(ctx) {
             ctx->vfs_errno = errno;
             sys_set_error_status(ctx);
@@ -422,16 +525,76 @@
     return dir;
 }
 
-int sys_vfs_mkdir(VFSContext *ctx, char *path) {
+VFS_DIR sys_vfs_fdopendir(VFSContext *ctx, SYS_FILE fd) {
+    uint32_t access_mask = ctx->aclreqaccess;
+    pool_handle_t *pool = ctx->pool;
+    
+    // check ACLs
+    SysACL sysacl;
+    if(sys_acl_check(ctx, access_mask, &sysacl)) {
+        return NULL;
+    }
+    
+    if(sysacl.acl) {
+        if(!fs_acl_check_fd(&sysacl, ctx->user, fd->fd, access_mask)) {
+            acl_set_error_status(ctx->sn, ctx->rq, sysacl.acl, ctx->user);
+            return NULL;
+        }
+    }
+    
+    // open directory
+    DIR *sys_dir = fdopendir(fd->fd);
+    if(!sys_dir) {
+        if(ctx) {
+            ctx->vfs_errno = errno;
+            sys_set_error_status(ctx);
+        }
+        return NULL;
+    }
+    
+    SysVFSDir *dir_data = VFS_MALLOC(pool, sizeof(SysVFSDir));
+    if(!dir_data) {
+        closedir(sys_dir);
+        return NULL;
+    }
+    long maxfilelen = fpathconf(fd->fd, _PC_NAME_MAX);
+    size_t entry_len = offsetof(struct dirent, d_name) + maxfilelen + 1;
+    dir_data->cur = VFS_MALLOC(pool, entry_len);
+    if(!dir_data->cur) {
+        closedir(sys_dir);
+        VFS_FREE(pool, dir_data);
+        return NULL;
+    }
+    dir_data->dir = sys_dir;
+    
+    VFSDir *dir = VFS_MALLOC(pool, sizeof(VFSDir));
+    if(!dir) {
+        closedir(sys_dir);
+        VFS_FREE(pool, dir_data->cur);
+        VFS_FREE(pool, dir_data);
+        return NULL;
+    }
+    dir->ctx = ctx;
+    dir->data = dir_data;
+    dir->fd = fd->fd;
+    dir->io = &sys_dir_io;
+    return dir;
+}
+
+int sys_vfs_mkdir(VFSContext *ctx, const char *path) {
     return sys_path_op(ctx, path, sys_mkdir);
 }
 
-int sys_vfs_unlink(VFSContext *ctx, char *path) {
+int sys_vfs_unlink(VFSContext *ctx, const char *path) {
     return sys_path_op(ctx, path, sys_unlink);
 }
 
+int sys_vfs_rmdir(VFSContext *ctx, const char *path) {
+    return sys_path_op(ctx, path, sys_rmdir);
+}
 
-int sys_path_op(VFSContext *ctx, char *path, sys_op_f op) {
+
+int sys_path_op(VFSContext *ctx, const char *path, sys_op_f op) {
     uint32_t access_mask = ctx->aclreqaccess;
     
     // check ACLs
@@ -484,9 +647,10 @@
 }
 
 void sys_set_error_status(VFSContext *ctx) {
-    if(ctx->sn && ctx->rq) {
+    if(ctx->sn && ctx->rq && !ctx->error_response_set) {
         int status = util_errno2status(ctx->vfs_errno);
         protocol_status(ctx->sn, ctx->rq, status, NULL);
+        ctx->error_response_set = TRUE;
     }
 }
 
@@ -539,6 +703,7 @@
             entry->name = name;
             if(getstat) {
                 // TODO: check ACLs again for new path
+                entry->stat_errno = 0;
                 if(fstatat(dir->fd, result->d_name, &entry->stat, 0)) {
                     entry->stat_errno = errno;
                 }
@@ -567,7 +732,7 @@
     }
 }
 
-int sys_mkdir(VFSContext *ctx, char *path, SysACL *sysacl) {
+int sys_mkdir(VFSContext *ctx, const char *path, SysACL *sysacl) {
     mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
     int ret = mkdir(path, mode);
     if(ret == 0) {
@@ -580,10 +745,14 @@
     return ret;
 }
 
-int sys_unlink(VFSContext *ctx, char *path, SysACL *sysacl) {
+int sys_unlink(VFSContext *ctx, const char *path, SysACL *sysacl) {
     return unlink(path);
 }
 
+int sys_rmdir(VFSContext *ctx, const char *path, SysACL *sysacl) {
+    return rmdir(path);
+}
+
 /* public file api */
 
 NSAPI_PUBLIC int system_fread(SYS_FILE fd, void *buf, int nbyte) {

mercurial