diff -r f33974f0dce0 -r aa8393527b1e src/server/daemon/vfs.c --- a/src/server/daemon/vfs.c Thu Aug 31 16:29:49 2017 +0200 +++ b/src/server/daemon/vfs.c Sat Jan 13 19:01:00 2018 +0100 @@ -32,20 +32,42 @@ #include #include #include +#include #include #include "../util/pool.h" #include "netsite.h" #include "acl.h" #include "vfs.h" +#include "threadpools.h" +#include "event.h" + +#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 VFS sys_vfs = { + sys_vfs_open, + sys_vfs_stat, + sys_vfs_fstat, + sys_vfs_opendir, + sys_vfs_mkdir, + sys_vfs_unlink, + VFS_CHECKS_ACL +}; + static VFS_IO sys_file_io = { sys_file_read, sys_file_write, + sys_file_pread, + sys_file_pwrite, sys_file_seek, - sys_file_close + sys_file_close, + //sys_file_aioread, + //sys_file_aiowrite, + NULL, + NULL }; static VFS_DIRIO sys_dir_io = { @@ -77,7 +99,7 @@ VFSContext *ctx = pool_malloc(sn->pool, sizeof(VFSContext)); ctx->sn = sn; ctx->rq = rq; - ctx->vfs = rq->vfs; + ctx->vfs = rq->vfs ? rq->vfs : &sys_vfs; ctx->user = acllist_getuser(sn, rq, rq->acllist); ctx->acllist = rq->acllist; ctx->aclreqaccess = rq->aclreqaccess; @@ -87,36 +109,164 @@ } SYS_FILE vfs_open(VFSContext *ctx, char *path, int oflags) { + WS_ASSERT(ctx); + WS_ASSERT(path); + + uint32_t access_mask = ctx->aclreqaccess | acl_oflag2mask(oflags); + + // 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; + } + } + SYS_FILE file = ctx->vfs->open(ctx, path, oflags); + ctx->aclreqaccess = m; // restore original access mask + return file; +} + +SYS_FILE vfs_openRO(VFSContext *ctx, char *path) { + return vfs_open(ctx, path, O_RDONLY); +} + +SYS_FILE vfs_openWO(VFSContext *ctx, char *path) { + return vfs_open(ctx, path, O_WRONLY | O_CREAT); +} + +SYS_FILE vfs_openRW(VFSContext *ctx, char *path) { + return vfs_open(ctx, path, O_RDONLY | O_WRONLY | O_CREAT); +} + +int vfs_stat(VFSContext *ctx, char *path, struct stat *buf) { + WS_ASSERT(ctx); + WS_ASSERT(path); + + uint32_t access_mask = ctx->aclreqaccess | ACL_READ_ATTRIBUTES; + + // 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 -1; + } + } + int ret = ctx->vfs->stat(ctx, path, buf); + ctx->aclreqaccess = m; // restore original access mask + return ret; +} + +int vfs_fstat(VFSContext *ctx, SYS_FILE fd, struct stat *buf) { + WS_ASSERT(ctx); + WS_ASSERT(fd); + WS_ASSERT(buf); + + return ctx->vfs->fstat(ctx, fd, buf); +} + +void vfs_close(SYS_FILE fd) { + WS_ASSERT(fd); + + fd->io->close(fd); + if(fd->ctx) { + pool_free(fd->ctx->pool, fd); + } else { + free(fd); + } +} + +VFS_DIR vfs_opendir(VFSContext *ctx, char *path) { + WS_ASSERT(ctx); WS_ASSERT(path); - Session *sn; - Request *rq; - pool_handle_t *pool; - uint32_t access_mask; + uint32_t access_mask = ctx->aclreqaccess | ACL_LIST; - if(ctx) { - access_mask = ctx->aclreqaccess; - access_mask |= acl_oflag2mask(oflags); - if(!ctx->pool) { - // TODO: log warning - // broken VFSContext + // 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; } - if(ctx->vfs) { - // 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 - SYS_FILE file = ctx->vfs->open(ctx, path, oflags); - ctx->aclreqaccess = m; // restore original access mask - return file; - } else { - pool = ctx->pool; + } + VFS_DIR dir = ctx->vfs->opendir(ctx, path); + ctx->aclreqaccess = m; // restore original access mask + return dir; +} + +int vfs_readdir(VFS_DIR dir, VFS_ENTRY *entry) { + WS_ASSERT(dir); + WS_ASSERT(entry); + + return dir->io->readdir(dir, entry, 0); +} + +int vfs_readdir_stat(VFS_DIR dir, VFS_ENTRY *entry) { + WS_ASSERT(dir); + WS_ASSERT(entry); + + return dir->io->readdir(dir, entry, 1); +} + +void vfs_closedir(VFS_DIR dir) { + WS_ASSERT(dir); + + dir->io->close(dir); + if(dir->ctx) { + VFS_FREE(dir->ctx->pool, dir); + } else { + free(dir); + } +} + +int vfs_mkdir(VFSContext *ctx, 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) { + WS_ASSERT(ctx); + WS_ASSERT(path); + + return vfs_path_op(ctx, path, ctx->vfs->unlink, ACL_DELETE); +} + + +// private +int vfs_path_op(VFSContext *ctx, char *path, vfs_op_f op, uint32_t access) { + uint32_t access_mask = ctx->aclreqaccess; + access_mask |= access; + + // 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 function 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 -1; } - } else { - sn = NULL; - rq = NULL; - pool = NULL; - access_mask = acl_oflag2mask(oflags); } + int ret = op(ctx, path); + ctx->aclreqaccess = m; // restore original access mask + return ret; +} + +/* system vfs implementation */ + +SYS_FILE sys_vfs_open(VFSContext *ctx, char *path, int oflags) { + uint32_t access_mask = ctx->aclreqaccess; + pool_handle_t *pool = ctx->pool; // check ACLs SysACL sysacl; @@ -151,9 +301,7 @@ } } - - VFSFile *file = pool ? - pool_malloc(pool, sizeof(VFSFile)) : malloc(sizeof(VFSFile)); + VFSFile *file = VFS_MALLOC(pool, sizeof(VFSFile)); if(!file) { system_close(fd); return NULL; @@ -165,43 +313,8 @@ return file; } -SYS_FILE vfs_openRO(VFSContext *ctx, char *path) { - return vfs_open(ctx, path, O_RDONLY); -} - -SYS_FILE vfs_openWO(VFSContext *ctx, char *path) { - return vfs_open(ctx, path, O_WRONLY | O_CREAT); -} - -SYS_FILE vfs_openRW(VFSContext *ctx, char *path) { - return vfs_open(ctx, path, O_RDONLY | O_WRONLY | O_CREAT); -} - -int vfs_stat(VFSContext *ctx, char *path, struct stat *buf) { - Session *sn; - Request *rq; - uint32_t access_mask; - - if(ctx) { - access_mask = ctx->aclreqaccess; - access_mask |= ACL_READ_ATTRIBUTES; - if(!ctx->pool) { - // TODO: log warning - // broken VFSContext - } - if(ctx->vfs) { - // 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->fstat call - int ret = ctx->vfs->stat(ctx, path, buf); - ctx->aclreqaccess = m; // restore original access mask - return ret; - } - } else { - sn = NULL; - rq = NULL; - access_mask = ACL_READ_ATTRIBUTES; - } +int sys_vfs_stat(VFSContext *ctx, char *path, struct stat *buf) { + uint32_t access_mask = ctx->aclreqaccess; // check ACLs SysACL sysacl; @@ -228,17 +341,7 @@ return 0; } -int vfs_fstat(VFSContext *ctx, SYS_FILE fd, struct stat *buf) { - if(ctx) { - if(!ctx->pool) { - // TODO: log warning - // broken VFSContext - } - if(ctx->vfs) { - return ctx->vfs->fstat(ctx, fd, buf); - } - } - +int sys_vfs_fstat(VFSContext *ctx, SYS_FILE fd, struct stat *buf) { // stat if(fstat(fd->fd, buf)) { if(ctx) { @@ -250,46 +353,9 @@ return 0; } -void vfs_close(SYS_FILE fd) { - fd->io->close(fd); - if(fd->ctx) { - pool_free(fd->ctx->pool, fd); - } else { - free(fd); - } -} - -VFS_DIR vfs_opendir(VFSContext *ctx, char *path) { - WS_ASSERT(path); - - Session *sn; - Request *rq; - pool_handle_t *pool; - uint32_t access_mask; - - if(ctx) { - access_mask = ctx->aclreqaccess; - access_mask |= ACL_LIST; - if(!ctx->pool) { - // TODO: log warning - // broken VFSContext - } - if(ctx->vfs) { - // 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->opendir call - VFS_DIR dir = ctx->vfs->opendir(ctx, path); - ctx->aclreqaccess = m; // restore original access mask - return dir; - } else { - pool = ctx->pool; - } - } else { - sn = NULL; - rq = NULL; - pool = NULL; - access_mask = ACL_LIST; - } +VFS_DIR sys_vfs_opendir(VFSContext *ctx, char *path) { + uint32_t access_mask = ctx->aclreqaccess; + pool_handle_t *pool = ctx->pool; // check ACLs SysACL sysacl; @@ -327,26 +393,26 @@ return NULL; } - SysVFSDir *dir_data = pool ? - pool_malloc(pool, sizeof(SysVFSDir)) : malloc(sizeof(SysVFSDir)); + SysVFSDir *dir_data = VFS_MALLOC(pool, sizeof(SysVFSDir)); if(!dir_data) { closedir(sys_dir); return NULL; } long maxfilelen = fpathconf(dir_fd, _PC_NAME_MAX); size_t entry_len = offsetof(struct dirent, d_name) + maxfilelen + 1; - dir_data->cur = pool ? - pool_malloc(pool, entry_len) : malloc(entry_len); + 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 = pool ? - pool_malloc(pool, sizeof(VFSDir)) : malloc(sizeof(VFSDir)); + 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; @@ -356,74 +422,26 @@ return dir; } -int vfs_readdir(VFS_DIR dir, VFS_ENTRY *entry) { - return dir->io->readdir(dir, entry, 0); -} - -int vfs_readdir_stat(VFS_DIR dir, VFS_ENTRY *entry) { - return dir->io->readdir(dir, entry, 1); +int sys_vfs_mkdir(VFSContext *ctx, char *path) { + return sys_path_op(ctx, path, sys_mkdir); } -void vfs_closedir(VFS_DIR dir) { - dir->io->close(dir); - if(dir->ctx) { - pool_free(dir->ctx->pool, dir); - } else { - free(dir); - } -} - -int vfs_mkdir(VFSContext *ctx, char *path) { - if(ctx && ctx->vfs) { - return vfs_path_op(ctx, path, ctx->vfs->mkdir, ACL_ADD_FILE); - } else { - return sys_path_op(ctx, path, sys_mkdir, ACL_ADD_FILE); - } -} - -int vfs_unlink(VFSContext *ctx, char *path) { - if(ctx && ctx->vfs) { - return vfs_path_op(ctx, path, ctx->vfs->unlink, ACL_DELETE); - } else { - return sys_path_op(ctx, path, sys_unlink, ACL_DELETE); - } +int sys_vfs_unlink(VFSContext *ctx, char *path) { + return sys_path_op(ctx, path, sys_unlink); } -// private -int vfs_path_op(VFSContext *ctx, char *path, vfs_op_f op, uint32_t access) { - Session *sn; - Request *rq; - +int sys_path_op(VFSContext *ctx, char *path, sys_op_f op) { uint32_t access_mask = ctx->aclreqaccess; - access_mask |= access; - if(!ctx->pool) { - // TODO: log warning - // broken VFSContext - return -1; - } - - // 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 function call - int ret = op(ctx, path); - ctx->aclreqaccess = m; // restore original access mask - return ret; -} - -int sys_path_op(VFSContext *ctx, char *path, sys_op_f op, uint32_t access) { - if(ctx) { - access |= ctx->aclreqaccess; - } // check ACLs SysACL sysacl; - if(sys_acl_check(ctx, access, &sysacl)) { + if(sys_acl_check(ctx, access_mask, &sysacl)) { return -1; } if(sysacl.acl) { - if(!fs_acl_check(&sysacl, ctx->user, path, access)) { + if(!fs_acl_check(&sysacl, ctx->user, path, access_mask)) { acl_set_error_status(ctx->sn, ctx->rq, sysacl.acl, ctx->user); return -1; } @@ -432,10 +450,8 @@ // do path operation if(op(ctx, path, &sysacl)) { // error - if(ctx) { - ctx->vfs_errno = errno; - sys_set_error_status(ctx); - } + ctx->vfs_errno = errno; + sys_set_error_status(ctx); return -1; } @@ -482,6 +498,14 @@ return write(fd->fd, buf, nbyte); } +ssize_t sys_file_pread(SYS_FILE fd, void *buf, size_t nbyte, off_t offset) { + return pread(fd->fd, buf, nbyte, offset); +} + +ssize_t sys_file_pwrite(SYS_FILE fd, const void *buf, size_t nbyte, off_t offset) { + return pwrite(fd->fd, buf, nbyte, offset); +} + off_t sys_file_seek(SYS_FILE fd, off_t offset, int whence) { return lseek(fd->fd, offset, whence); } @@ -490,6 +514,19 @@ system_close(fd->fd); } +int sys_file_aioread(aiocb_s *aiocb) { + WS_ASSERT(aiocb->buf); + WS_ASSERT(aiocb->nbytes > 0); + return ev_aioread(aiocb->filedes->fd, aiocb); +} + +int sys_file_aiowrite(aiocb_s *aiocb) { + WS_ASSERT(aiocb->buf); + WS_ASSERT(aiocb->nbytes > 0); + return ev_aiowrite(aiocb->filedes->fd, aiocb); +} + + int sys_dir_read(VFS_DIR dir, VFS_ENTRY *entry, int getstat) { SysVFSDir *dirdata = dir->data; struct dirent *result = NULL; @@ -500,19 +537,13 @@ return sys_dir_read(dir, entry, getstat); } else { entry->name = name; -#ifndef OSX - /* TODO: - * implement alternative for fstat for OS X and other crappy - * Unices - */ if(getstat) { // TODO: check ACLs again for new path if(fstatat(dir->fd, result->d_name, &entry->stat, 0)) { entry->stat_errno = errno; } entry->stat_extra = NULL; - } -#endif + } return 1; } } else { @@ -563,6 +594,14 @@ return fd->io->write(fd, buf, nbyte); } +NSAPI_PUBLIC int system_pread(SYS_FILE fd, void *buf, int nbyte, off_t offset) { + return fd->io->pread(fd, buf, nbyte, offset); +} + +NSAPI_PUBLIC int system_pwrite(SYS_FILE fd, const void *buf, int nbyte, off_t offset) { + return fd->io->pwrite(fd, buf, nbyte, offset); +} + NSAPI_PUBLIC off_t system_lseek(SYS_FILE fd, off_t offset, int whence) { return fd->io->seek(fd, offset, whence); } @@ -571,3 +610,64 @@ vfs_close(fd); return 0; } + +// AIO API + +NSAPI_PUBLIC int system_aio_read(aiocb_s *aiocb) { + if(!aiocb->event || !aiocb->evhandler) { + return -1; + } + + SYS_FILE file = aiocb->filedes; + aiocb->event->object = (intptr_t)aiocb; + if(file->io->opt_aioread) { + return file->io->opt_aioread(aiocb); + } else { + vfs_queue_aio(aiocb, VFS_AIO_READ); + return 0; + } +} + +NSAPI_PUBLIC int system_aio_write(aiocb_s *aiocb) { + if(!aiocb->event || !aiocb->evhandler) { + return -1; + } + + SYS_FILE file = aiocb->filedes; + aiocb->event->object = (intptr_t)aiocb; + if(file->io->opt_aiowrite) { + return file->io->opt_aiowrite(aiocb); + } else { + vfs_queue_aio(aiocb, VFS_AIO_WRITE); + return 0; + } +} + +static void* vfs_aio_read(aiocb_s *aiocb) { + int result = system_pread(aiocb->filedes, aiocb->buf, aiocb->nbytes, aiocb->offset); + aiocb->result = result; + if(result < 0) { + aiocb->result_errno = errno; + } + event_send(aiocb->evhandler, aiocb->event); + return NULL; +} + +static void* vfs_aio_write(aiocb_s *aiocb) { + int result = system_pwrite(aiocb->filedes, aiocb->buf, aiocb->nbytes, aiocb->offset); + aiocb->result = result; + if(result < 0) { + aiocb->result_errno = errno; + } + event_send(aiocb->evhandler, aiocb->event); + return NULL; +} + +void vfs_queue_aio(aiocb_s *aiocb, VFSAioOp op) { + threadpool_t *pool = get_default_iopool(); // TODO: use specific IOPool + if(op == VFS_AIO_READ) { + threadpool_run(pool, (job_callback_f)vfs_aio_read, aiocb); + } else if(VFS_AIO_WRITE) { + threadpool_run(pool, (job_callback_f)vfs_aio_write, aiocb); + } +}