--- 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) {