src/server/daemon/vfs.c

changeset 54
3a1d5a52adfc
child 55
b7908bf38f9f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/daemon/vfs.c	Sat Mar 16 23:11:34 2013 +0100
@@ -0,0 +1,265 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "../util/pool.h"
+#include "vfs.h"
+
+static VFS_IO sys_file_io = {
+    sys_file_read,
+    sys_file_write,
+    sys_file_close
+};
+
+VFSContext* vfs_request_context(Session *sn, Request *rq) {
+    VFSContext *ctx = pool_malloc(sn->pool, sizeof(VFSContext));
+    ctx->sn = sn;
+    ctx->rq = rq;
+    ctx->vfs = rq->vfs;
+    ctx->user = acllist_getuser(sn, rq, rq->acllist);
+    ctx->acllist = rq->acllist;
+    ctx->aclreqaccess = rq->aclreqaccess;
+    ctx->pool = sn->pool;
+    ctx->vfs_errno = 0;
+    return ctx;
+}
+
+SYS_FILE vfs_open(VFSContext *ctx, char *path, int oflags) {
+    Session *sn;
+    Request *rq;
+    pool_handle_t *pool;
+    uint32_t access_mask;
+    
+    if(ctx) {
+        access_mask = ctx->aclreqaccess;
+        access_mask |= acl_oflag2mask(oflags);
+        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->open call
+            SYS_FILE file = ctx->vfs->open(ctx, path, oflags);
+            ctx->aclreqaccess = m; // restore original access mask
+            return file;
+        } else {
+            pool = ctx->pool;
+        }
+    } else {
+        sn = NULL;
+        rq = NULL;
+        pool = NULL;
+        access_mask = acl_oflag2mask(oflags);
+    }
+    
+    // check ACLs
+    uid_t uid; // uid and gid will be initialized by sys_acl_check
+    gid_t gid;
+    if(sys_acl_check(ctx, access_mask, &uid, &gid)) {
+        return NULL;
+    }
+    
+    // open file
+    int fd = open(path, oflags);
+    if(fd == -1) {
+        if(ctx) {
+            ctx->vfs_errno = errno;
+            sys_set_error_status(ctx);
+        }
+        return NULL;
+    }
+    
+    VFSFile *file = pool ?
+            pool_malloc(pool, sizeof(VFSFile)) : malloc(sizeof(VFSFile));
+    if(!file) {
+        return NULL;
+    }
+    file->ctx = ctx;
+    file->data = NULL;
+    file->fd = fd;
+    file->io = &sys_file_io;
+    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);
+}
+
+SYS_FILE vfs_openRW(VFSContext *ctx, char *path) {
+    return vfs_open(ctx, path, O_RDONLY);
+}
+
+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;
+    }
+    
+    // check ACLs
+    uid_t uid; // uid and gid will be initialized by sys_acl_check
+    gid_t gid;
+    if(sys_acl_check(ctx, access_mask, &uid, &gid)) {
+        return NULL;
+    }
+    
+    // stat
+    if(stat(path, buf)) {
+        if(ctx) {
+            ctx->vfs_errno = errno;
+            sys_set_error_status(ctx);
+        }
+        return -1;
+    }
+    
+    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);
+        }
+    }
+    
+    // stat
+    if(fstat(fd->fd, buf)) {
+        if(ctx) {
+            ctx->vfs_errno = errno;
+        }
+        return -1;
+    }
+    
+    return 0;
+}
+
+void vfs_close(SYS_FILE fd) {
+    fd->io->close(fd);
+    if(fd->ctx) {
+        pool_free(fd->ctx->pool, fd);
+    } else {
+        free(fd);
+    }
+}
+
+
+// private
+
+int sys_acl_check(VFSContext *ctx, uint32_t acm, uid_t *uid, gid_t *gid) {
+    /*
+     * we don't allow remote root access, so a uid of 0 means that
+     * no file system acl check is needed
+     */
+    *uid = 0;
+    *gid = 0;
+    if(!ctx) {
+        return 0;
+    }
+    
+    ACLListHandle *acllist = ctx->acllist;
+    if(acllist) {
+        ACLListElm *elm = acllist->listhead;
+        while(elm) {
+            ACLList *acl = elm->acl;
+            if(acl->isextern) {
+                // TODO
+            } else if(!acl->check(acl, ctx->user, acm)) {
+                // access denied
+                if(ctx->sn && ctx->rq) {
+                    acl_set_error_status(ctx->sn, ctx->rq, acl, ctx->user);
+                }
+                return 1;        
+            } 
+            elm = elm->next;
+        }
+    }
+    
+    return 0;
+}
+
+void sys_set_error_status(VFSContext *ctx) {
+    if(ctx->sn && ctx->rq) {
+        int status = 500;
+        switch(ctx->vfs_errno) {
+            case EACCES: {
+                status = 403;
+                break;
+            }
+            case ENOENT: {
+                status = 404;
+                break;
+            }
+        }
+        protocol_status(ctx->sn, ctx->rq, status, NULL);
+    }
+}
+
+ssize_t sys_file_read(SYS_FILE fd, void *buf, size_t nbyte) {
+    return read(fd->fd, buf, nbyte);
+}
+
+ssize_t sys_file_write(SYS_FILE fd, const void *buf, size_t nbyte) {
+    return write(fd->fd, buf, nbyte);
+}
+
+void sys_file_close(SYS_FILE fd) {
+    close(fd->fd);
+}

mercurial