src/server/daemon/vfs.c

changeset 193
aa8393527b1e
parent 171
af7e2d80dee6
parent 190
1f73302461e0
child 211
2160585200ac
--- 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 <stdlib.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <aio.h>
 #include <ucx/map.h>
 
 #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);
+    }
+}

mercurial