new vfs api

Sat, 16 Mar 2013 23:11:34 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 16 Mar 2013 23:11:34 +0100
changeset 54
3a1d5a52adfc
parent 53
5ec9abba1027
child 55
b7908bf38f9f

new vfs api

src/server/admin/Makefile file | annotate | diff | comparison | revisions
src/server/config/Makefile file | annotate | diff | comparison | revisions
src/server/daemon/Makefile file | annotate | diff | comparison | revisions
src/server/daemon/acl.c file | annotate | diff | comparison | revisions
src/server/daemon/acl.h file | annotate | diff | comparison | revisions
src/server/daemon/config.c file | annotate | diff | comparison | revisions
src/server/daemon/func.c file | annotate | diff | comparison | revisions
src/server/daemon/httprequest.c file | annotate | diff | comparison | revisions
src/server/daemon/objs.mk file | annotate | diff | comparison | revisions
src/server/daemon/request.c file | annotate | diff | comparison | revisions
src/server/daemon/vfs.c file | annotate | diff | comparison | revisions
src/server/daemon/vfs.h file | annotate | diff | comparison | revisions
src/server/public/nsapi.h file | annotate | diff | comparison | revisions
src/server/safs/Makefile file | annotate | diff | comparison | revisions
src/server/safs/service.c file | annotate | diff | comparison | revisions
src/server/util/Makefile file | annotate | diff | comparison | revisions
src/server/util/io.c file | annotate | diff | comparison | revisions
src/server/util/io.h file | annotate | diff | comparison | revisions
--- a/src/server/admin/Makefile	Fri Mar 01 22:44:54 2013 +0100
+++ b/src/server/admin/Makefile	Sat Mar 16 23:11:34 2013 +0100
@@ -26,7 +26,7 @@
 # POSSIBILITY OF SUCH DAMAGE.
 #
 
-ADMIN_CFLAGS = -I/usr/include/mps -g
+ADMIN_CFLAGS = 
 
 $(ADMIN_OBJPRE)%.o: admin/%.c
 	$(CC) -o $@ -c $(ADMIN_CFLAGS) $(CFLAGS) $<
--- a/src/server/config/Makefile	Fri Mar 01 22:44:54 2013 +0100
+++ b/src/server/config/Makefile	Sat Mar 16 23:11:34 2013 +0100
@@ -27,8 +27,8 @@
 #
 
 $(CONF_OBJPRE)%.o: config/%.c
-	$(CC) -o $@ -c -g $(CFLAGS) $<
+	$(CC) -o $@ -c $(CFLAGS) $<
 
 $(CONF_OBJPRE)%.o: config/%.cpp
-	$(CXX) -o $@ -c -g $(CFLAGS) $<
+	$(CXX) -o $@ -c $(CFLAGS) $<
 	
--- a/src/server/daemon/Makefile	Fri Mar 01 22:44:54 2013 +0100
+++ b/src/server/daemon/Makefile	Sat Mar 16 23:11:34 2013 +0100
@@ -26,7 +26,7 @@
 # POSSIBILITY OF SUCH DAMAGE.
 #
 
-DMN_CFLAGS = -I/usr/include/mps -g
+DMN_CFLAGS = 
 
 $(DMN_OBJPRE)%.o: daemon/%.c
 	$(CC) -o $@ -c $(DMN_CFLAGS) $(CFLAGS) $<
--- a/src/server/daemon/acl.c	Fri Mar 01 22:44:54 2013 +0100
+++ b/src/server/daemon/acl.c	Sat Mar 16 23:11:34 2013 +0100
@@ -79,17 +79,25 @@
     acllist_add(sn, rq, acl, 0);
 }
 
+uint32_t acl_oflag2mask(int oflags) {
+    /* TODO:
+     * maybe there is a plattform where O_RDWR is not O_RDONLY | O_WRONLY
+     */
+    uint32_t access_mask = 0;
+    if((oflags & O_RDONLY) == O_RDONLY) {
+        access_mask |= ACL_READ_DATA;
+    }
+    if((oflags & O_WRONLY) == O_WRONLY) {
+        access_mask |= ACL_WRITE_DATA;
+    }
+    return access_mask;
+}
 
-int acl_evaluate(Session *sn, Request *rq, int access_mask) {
-    ACLListHandle *list = rq->acllist;
-    if(!list) {
-        return REQ_PROCEED;
+User* acllist_getuser(Session *sn, Request *rq, ACLListHandle *list) {
+    if(!sn || !rq || !list) {
+        return NULL;
     }
     
-    // we combine access_mask with the required access rights
-    access_mask = access_mask | rq->aclreqaccess;
-    
-    
     // get user
     User *user = NULL;
     if(list->defaultauthdb) {
@@ -99,62 +107,98 @@
             user = list->defaultauthdb->get_user(list->defaultauthdb, usr);
             if(!user) {
                 // wrong user name
-                return REQ_ABORTED;
+                return NULL;
             }
             if(!user->verify_password(user, pw)) {
                 // wrong password
                 user->free(user);
-                return REQ_ABORTED;
+                return NULL;
             }
             // ok - user is authenticated
         }
+    }
+    
+    return user;
+}
+
+void acl_set_error_status(Session *sn, Request *rq, ACLList *acl, User *user) {
+    if(!user) {
+        char *value = NULL;
+        if(acl->authprompt) {
+            size_t realmlen = strlen(acl->authprompt);
+            size_t len = realmlen + 16;
+            value = pool_malloc(sn->pool, len);
+            if(value) {
+                snprintf(
+                        value,
+                        len,
+                        "Basic realm=\"%s\"",
+                        acl->authprompt);
+            }
+        }
+        if(!value) {
+            value = "Basic realm=\"login\"";
+        }
+        pblock_nvinsert("www-authenticate", value, rq->srvhdrs);
+        protocol_status(sn, rq, PROTOCOL_UNAUTHORIZED, NULL);
     } else {
-        // TODO
+        protocol_status(sn, rq, PROTOCOL_FORBIDDEN, NULL);
+    }
+}
+
+int acl_evaluate(Session *sn, Request *rq, int access_mask) {
+    ACLListHandle *list = rq->acllist;
+    if(!list) {
+        return REQ_PROCEED;
+    }
+    
+    // we combine access_mask with the required access rights
+    access_mask |= rq->aclreqaccess;
+    
+    // get user
+    User *user = acllist_getuser(sn, rq, list);
+    
+    // evalutate all ACLs
+    ACLList *acl = acl_evallist(list, user, access_mask);
+    if(acl) {
+        acl_set_error_status(sn, rq, acl, user);
+        // TODO: don't free the user here
+        if(user) {
+            user->free(user);
+        }
         return REQ_ABORTED;
     }
     
+    // access allowed, we can free the user
+    if(user) {
+        user->free(user);
+    }
+    
+    return REQ_PROCEED;
+}
+
+ACLList* acl_evallist(ACLListHandle *list, User *user, int access_mask) {
+    if(!list) {
+        return NULL;
+    }
+    
     // evaluate each acl until one denies access
     ACLListElm *elm = list->listhead;
     while(elm) {
         ACLList *acl = elm->acl;
-        if(!wsacl_check(acl, user, access_mask)) {
+        if(!acl->check(acl, user, access_mask)) {
             // the acl denies access
-            
-            if(!user) {
-                char *value = NULL;
-                if(acl->authprompt) {
-                    size_t realmlen = strlen(acl->authprompt);
-                    size_t len = realmlen + 16;
-                    value = pool_malloc(sn->pool, len);
-                    if(value) {
-                        snprintf(
-                                value,
-                                len,
-                                "Basic realm=\"%s\"",
-                                acl->authprompt);
-                    }
-                }
-                if(!value) {
-                    value = "Basic realm=\"login\"";
-                }
-                pblock_nvinsert("www-authenticate", value, rq->srvhdrs);
-                protocol_status(sn, rq, PROTOCOL_UNAUTHORIZED, NULL);
-            } else {
-                user->free(user);
-            }
-            return REQ_ABORTED;
+            return acl;
         } 
         elm = elm->next;
     }
     
     // ok - all acls allowed access
-    if(user) {
-        user->free(user);
-    }
-    return REQ_PROCEED;
+    
+    return NULL;
 }
 
-int wsacl_affects_user(ACLEntry *ace, User *user) {
+int wsacl_affects_user(WSAce *ace, User *user) {
     int check_access = 0;
     
     /*
@@ -192,17 +236,16 @@
     return check_access;
 }
 
-int wsacl_check(ACLList *acl, User *user, int access_mask) { 
+int wsacl_check(WSAcl *acl, User *user, int access_mask) { 
     int allow = 0;
     uint32_t allowed_access = 0;
     // check each access control entry
     for(int i=0;i<acl->acenum;i++) {
-        ACLEntry *ace = acl->ace[i];   
+        WSAce *ace = acl->ace[i];   
         if(wsacl_affects_user(ace, user)) {
             if(ace->type == ACL_TYPE_ALLOWED) {
                 // add all new access rights 
-                allowed_access = allowed_access |
-                        (access_mask & ace->access_mask);
+                allowed_access |= (access_mask & ace->access_mask);
                 // check if we have all requested rights
                 if((allowed_access & access_mask) == access_mask) {
                     allow = 1;
@@ -221,5 +264,5 @@
     
     // TODO: events
     
-    return allow;
+    return allow; // allow is 0, if no ace set it to 1
 }
--- a/src/server/daemon/acl.h	Fri Mar 01 22:44:54 2013 +0100
+++ b/src/server/daemon/acl.h	Sat Mar 16 23:11:34 2013 +0100
@@ -36,16 +36,15 @@
 extern "C" {
 #endif
 
-typedef struct ACLList  ACLList;
-typedef struct ACLEntry ACLEntry;
 // ACLListHandle typedef in nsapi.h
+typedef struct ACLListElm ACLListElm;
+typedef struct ACLList ACLList;
 
-typedef struct ACLListElm ACLListElm;
+typedef struct WSAcl WSAcl;
+typedef struct WSAce WSAce;
 
 /*
  * a wrapper struct for acls
- * 
- * TODO: store more than one acl
  */
 struct ACLListHandle {
     AuthDB     *defaultauthdb;
@@ -59,22 +58,33 @@
 };
 
 /*
- * a access control list
+ * abstract ACL
+ */
+typedef int(*acl_check_f)(ACLList*, User*, int);
+struct ACLList {
+    AuthDB *authdb;
+    char   *authprompt;
+    int isextern;
+    /* int check(ACLList *acl, User *user, int access_mask) */
+    int(*check)(ACLList *acl, User *user, int access_mask);
+};
+
+/*
+ * a webserver access control list
  * 
  * Access control is determined by the ace field. The ece field is a separat
  * list for audit and alarm entries.
  */
-struct ACLList {
-    AuthDB *authdb;
-    char   *authprompt;
-    ACLEntry **ace; // access control entries
-    ACLEntry **ece; // event control entries (audit/alarm entries)
+struct WSAcl {
+    ACLList acl;
+    WSAce **ace; // access control entries
+    WSAce **ece; // event control entries (audit/alarm entries)
     int acenum; // number of aces
     int ecenum; // number of eces
 };
 
 
-struct ACLEntry {
+struct WSAce {
     char     *who; // user or group name
     uint32_t access_mask;
     uint16_t flags;
@@ -138,13 +148,48 @@
 void acllist_append(Session *sn, Request *rq, ACLList *acl);
 void acllist_prepend(Session *sn, Request *rq, ACLList *acl);
 
-// eval
+/*
+ * gets a access mask from open flags
+ */
+uint32_t acl_oflag2mask(int oflags);
+
+/*
+ * authenticates the user with the user database specified in the acl list
+ */
+User* acllist_getuser(Session *sn, Request *rq, ACLListHandle *list);
+
+/*
+ * sets the status to 403 or 401 and sets www-authenticate
+ * 
+ * use this only if a ACL denies access
+ */
+void acl_set_error_status(Session *sn, Request *rq, ACLList *acl, User *user);
+
+/*
+ * acl_evaluate
+ * 
+ * Evaluates all ACLs in rq->acllist. It combines rq->aclreqaccess and
+ * access_mask. If access is denied and no user is authenticated it sets the
+ * www-authenticate header and the status to 401 Unauthorized.
+ * 
+ * returns REQ_PROCEED if access is allowed or REQ_ABORTED if access is denied
+ */
 int acl_evaluate(Session *sn, Request *rq, int access_mask);
 
+/*
+ * acl_evallist
+ * 
+ * evalutes all ACLs in acllist
+ * 
+ * returns NULL if access is allowed or a pointer to the ACLList which
+ * denied access
+ */
+ACLList* acl_evallist(ACLListHandle *acllist, User *user, int access_mask);
+
 
 // private
-int wsacl_affects_user(ACLEntry *ace, User *user);
-int wsacl_check(ACLList *acl, User *user, int access_mask);
+int wsacl_affects_user(WSAce *ace, User *user);
+int wsacl_check(WSAcl *acl, User *user, int access_mask);
 
 
 #ifdef	__cplusplus
--- a/src/server/daemon/config.c	Fri Mar 01 22:44:54 2013 +0100
+++ b/src/server/daemon/config.c	Sat Mar 16 23:11:34 2013 +0100
@@ -689,15 +689,17 @@
 }
 
 ACLList* acl_config_convert(ServerConfiguration *cfg, ACLConfig *acl) {
-    ACLList *acllist = malloc(sizeof(ACLList));
-    acllist->authdb = NULL;
-    acllist->authprompt = NULL;
+    WSAcl *acllist = malloc(sizeof(WSAcl));
+    acllist->acl.check = (acl_check_f)wsacl_check;
+    acllist->acl.authdb = NULL;
+    acllist->acl.authprompt = NULL;
+    acllist->acl.isextern = 0;
     acllist->ace = NULL;
     acllist->ece = NULL;
     
     size_t s = ucx_list_size(acl->entries);
-    ACLEntry **aces = calloc(s, sizeof(ACLEntry*));
-    ACLEntry **eces = calloc(s, sizeof(ACLEntry*));
+    WSAce **aces = calloc(s, sizeof(WSAce*));
+    WSAce **eces = calloc(s, sizeof(WSAce*));
     int ai = 0;
     int ei = 0;
     
@@ -706,7 +708,7 @@
         ACEConfig *acecfg = elm->data;
         
         // copy data
-        ACLEntry *ace = malloc(sizeof(ACLEntry));
+        WSAce *ace = malloc(sizeof(WSAce));
         ace->access_mask = acecfg->access_mask;
         ace->flags = acecfg->flags;
         ace->type = acecfg->type;
@@ -724,13 +726,13 @@
     
     // create new entrie arrays with perfect fitting size
     if(ai > 0) {
-        acllist->ace = calloc(ai, sizeof(ACLEntry*));
+        acllist->ace = calloc(ai, sizeof(WSAce*));
     }
     if(ei > 0) {
-        acllist->ece = calloc(ei, sizeof(ACLEntry*));
+        acllist->ece = calloc(ei, sizeof(WSAce*));
     }
-    memcpy(acllist->ace, aces, ai*sizeof(ACLEntry*));
-    memcpy(acllist->ece, eces, ei*sizeof(ACLEntry*));
+    memcpy(acllist->ace, aces, ai*sizeof(WSAce*));
+    memcpy(acllist->ece, eces, ei*sizeof(WSAce*));
     acllist->acenum = ai;
     acllist->ecenum = ei;
     
@@ -744,12 +746,12 @@
         
         if(authdb_str.ptr) {
             AuthDB *authdb = ucx_map_sstr_get(cfg->authdbs, authdb_str);
-            acllist->authdb = authdb;
+            acllist->acl.authdb = authdb;
             if(authdb && prompt_str.ptr) {
-                acllist->authprompt = sstrdup(prompt_str).ptr;
+                acllist->acl.authprompt = sstrdup(prompt_str).ptr;
             }
         }
     }
     
-    return acllist;
+    return &acllist->acl;
 }
--- a/src/server/daemon/func.c	Fri Mar 01 22:44:54 2013 +0100
+++ b/src/server/daemon/func.c	Sat Mar 16 23:11:34 2013 +0100
@@ -74,7 +74,7 @@
     return func;
 }
 
-int func_exec (pblock *pb, Session *sn, Request *rq) {
+int func_exec(pblock *pb, Session *sn, Request *rq) {
     FuncStruct *func = func_resolve(pb, sn, rq);
     if(func == NULL) {
         return REQ_ABORTED;
--- a/src/server/daemon/httprequest.c	Fri Mar 01 22:44:54 2013 +0100
+++ b/src/server/daemon/httprequest.c	Sat Mar 16 23:11:34 2013 +0100
@@ -77,7 +77,8 @@
     sn->connection = request->connection;
     sn->netbuf = request->netbuf;
     sn->sn.pool = pool;
-    sn->sn.csd = stream_new_from_fd(pool, request->connection->fd);
+    //sn->sn.csd = stream_new_from_fd(pool, request->connection->fd);
+    sn->sn.csd = net_stream_from_fd(pool, request->connection->fd);
     sn->sn.client = pblock_create_pool(sn->sn.pool, 8);
     sn->sn.next = NULL;
     sn->sn.fill = 1;
--- a/src/server/daemon/objs.mk	Fri Mar 01 22:44:54 2013 +0100
+++ b/src/server/daemon/objs.mk	Sat Mar 16 23:11:34 2013 +0100
@@ -52,6 +52,7 @@
 DAEMONOBJ += error.o
 DAEMONOBJ += acl.o
 DAEMONOBJ += acldata.o
+DAEMONOBJ += vfs.o
 
 # add additional platform dependend objects
 # defined in generated config.mk
--- a/src/server/daemon/request.c	Fri Mar 01 22:44:54 2013 +0100
+++ b/src/server/daemon/request.c	Sat Mar 16 23:11:34 2013 +0100
@@ -98,6 +98,7 @@
 
     // new
     rq->status_num = -1;
+    rq->vfs = NULL;
 
     return 0;
 }
--- /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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/daemon/vfs.h	Sat Mar 16 23:11:34 2013 +0100
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+#ifndef VFS_H
+#define	VFS_H
+
+#include "../public/nsapi.h"
+#include "acl.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+typedef struct VFS_IO    VFS_IO;
+typedef struct VFSFile   VFSFile;
+
+struct VFS {
+    SYS_FILE (*open)(VFSContext *ctx, char *path, int oflags);
+    int (*stat)(VFSContext *ctx, char *path, struct stat *buf);
+    int (*fstat)(VFSContext *ctx, SYS_FILE fd, struct stat *buf);
+};
+
+struct VFSContext {
+    Session *sn;
+    Request *rq;
+    VFS *vfs;
+    pool_handle_t *pool;
+    User *user;
+    ACLListHandle *acllist;
+    uint32_t aclreqaccess;
+    int vfs_errno;
+};
+
+struct VFSFile {
+    VFSContext *ctx;
+    VFS_IO *io; // IO functions
+    void *data; // private data used by the VFSFile implementation
+    int fd; // native file descriptor if available, or -1
+};
+
+struct VFS_IO {
+    ssize_t (*read)(SYS_FILE fd, void *buf, size_t nbyte);
+    ssize_t (*write)(SYS_FILE fd, const void *buf, size_t nbyte);
+    void (*close)(SYS_FILE fd);
+};
+
+/*
+ * creates a VFSContext for a Request
+ * vfs calls will do ACL checks
+ */
+VFSContext* vfs_request_context(Session *sn, Request *rq);
+
+SYS_FILE vfs_open(VFSContext *ctx, char *path, int oflags);
+SYS_FILE vfs_openRO(VFSContext *ctx, char *path);
+SYS_FILE vfs_openWO(VFSContext *ctx, char *path);
+SYS_FILE vfs_openRW(VFSContext *ctx, char *path);
+int vfs_stat(VFSContext *ctx, char *path, struct stat *buf);
+int vfs_fstat(VFSContext *ctx, SYS_FILE fd, struct stat *buf);
+void vfs_close(SYS_FILE fd);
+
+// private
+int sys_acl_check(VFSContext *ctx, uint32_t acm, uid_t *uid, gid_t *gid);
+void sys_set_error_status(VFSContext *ctx);
+ssize_t sys_file_read(SYS_FILE fd, void *buf, size_t nbyte);
+ssize_t sys_file_write(SYS_FILE fd, const void *buf, size_t nbyte);
+void sys_file_close(SYS_FILE fd);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* VFS_H */
+
--- a/src/server/public/nsapi.h	Fri Mar 01 22:44:54 2013 +0100
+++ b/src/server/public/nsapi.h	Sat Mar 16 23:11:34 2013 +0100
@@ -386,12 +386,17 @@
 /* NOTE: both SYS_FILE and SYS_NETFD are actually NSPR PRFileDesc * and can */
 /*       be used with NSPR API calls (after casting them to PRFileDesc *) */
 
+// NOTE: no they are not NSPR PRFileDesc*
+//          they are VFSFile*
+// TODO: fix NOTE
+
+
 #ifndef SYS_FILE_T
-typedef void *SYS_FILE;
+typedef struct VFSFile *SYS_FILE;
 #define SYS_FILE_T void *
 #endif /* !SYS_FILE_T */
 
-#define SYS_ERROR_FD ((SYS_FILE)-1)
+#define SYS_ERROR_FD ((SYS_FILE)-1) // TODO: fix
 
 #ifndef SYS_NETFD_T
 typedef void *SYS_NETFD;
@@ -641,6 +646,8 @@
 
 typedef struct PListStruct_s PListStruct_s;
 typedef struct ACLListHandle ACLListHandle;
+typedef struct VFS           VFS;
+typedef struct VFSContext    VFSContext;
 
 #ifndef PR_AF_INET
 typedef union PRNetAddr PRNetAddr;
@@ -992,6 +999,8 @@
     /* 3.0 ACL list pointer */
     ACLListHandle *acllist;
     uint32_t aclreqaccess; /* new - required access rights */
+    
+    VFS *vfs; /* new - virtual file system */
 
     int request_is_cacheable;   /* */
     int directive_is_cacheable; /* set by SAFs with no external side effects that make decisions based solely on URI and path */
--- a/src/server/safs/Makefile	Fri Mar 01 22:44:54 2013 +0100
+++ b/src/server/safs/Makefile	Sat Mar 16 23:11:34 2013 +0100
@@ -26,7 +26,7 @@
 # POSSIBILITY OF SUCH DAMAGE.
 #
 
-SAFS_CFLAGS = -I/usr/include/mps -g
+SAFS_CFLAGS = 
 
 $(SAFS_OBJPRE)%.o: safs/%.c
 	$(CC) -o $@ -c $(SAFS_CFLAGS) $(CFLAGS) $<
--- a/src/server/safs/service.c	Fri Mar 01 22:44:54 2013 +0100
+++ b/src/server/safs/service.c	Sat Mar 16 23:11:34 2013 +0100
@@ -34,6 +34,7 @@
 #include "../util/io.h"
 #include "../util/pblock.h"
 #include "../daemon/protocol.h"
+#include "../daemon/vfs.h"
 
 #include <sys/sendfile.h>
 #include "../util/strbuf.h"
@@ -54,44 +55,29 @@
  *
  * adds content-length header and starts the response
  *
- * return the file descriptor or -1
+ * return the opened file
  */
-int prepare_service_file(Session *sn, Request *rq) {
+SYS_FILE prepare_service_file(Session *sn, Request *rq, struct stat *s) {
     char *ppath = pblock_findkeyval(pb_key_ppath, rq->vars);
 
     /* open the file */
-    int fd = open(ppath, O_RDONLY);
-    if(fd < 0) {
-        //perror("prepare_service_file: open");
-
-        int status = 500;
-        int en = errno;
-        switch(en) {
-            case EACCES: {
-                status = 403;
-                break;
-            }
-            case ENOENT: {
-                status = 404;
-                break;
-            }
-        }
-        protocol_status(sn, rq, status, NULL);
-        return -1;
+    VFSContext *vfs = vfs_request_context(sn, rq);
+    SYS_FILE fd = vfs_open(vfs, ppath, O_RDONLY);
+    if(!fd) {
+        // vfs_open sets http status code
+        return NULL;
     }
 
     /* get stat */
-    struct stat stat;
-    if (fstat(fd, &stat) != 0) {
+    if (vfs_fstat(vfs, fd, s) != 0) {
         //perror("prepare_service_file: stat");
-
         protocol_status(sn, rq, 500, NULL);
-        return -1;
+        return NULL;
     }
 
     /* add content-length header*/
     char contentLength[32];
-    int len = snprintf(contentLength, 32, "%d", stat.st_size);
+    int len = snprintf(contentLength, 32, "%d", s->st_size);
 
     pblock_kvinsert(pb_key_content_length, contentLength, len, rq->srvhdrs);
 
@@ -103,21 +89,33 @@
 }
 
 int send_file(pblock *pb, Session *sn, Request *rq) {
-    int fd = prepare_service_file(sn, rq);
-    if(fd < 0) {
+    struct stat s;
+    SYS_FILE fd = prepare_service_file(sn, rq, &s);
+    if(!fd) {
         // if an error occurs, prepare_service_file sets the http status code
         // we can just return REQ_ABORTED
         return REQ_ABORTED;
     }
+    
+    // send file
+    sendfiledata sfd;
+    sfd.fd = fd;
+    sfd.len = s.st_size;
+    sfd.offset = 0;
+    sfd.header = NULL;
+    sfd.trailer = NULL;
+    net_sendfile(sn->csd, &sfd);
+    
+    vfs_close(fd);
+    
 
     /* send file*/
-    SystemIOStream *io = (SystemIOStream*) sn->csd;
+    //SystemIOStream *io = (SystemIOStream*) sn->csd;
+    //off_t fileoffset = 0;
+    //int len = atoi(pblock_findkeyval(pb_key_content_length, rq->srvhdrs));
+    //sendfile(io->fd, fd->fd, &fileoffset, len);
 
-    off_t fileoffset = 0;
-    int len = atoi(pblock_findkeyval(pb_key_content_length, rq->srvhdrs));
-    sendfile(io->fd, fd, &fileoffset, len);
-
-    close(fd);
+    //close(fd);
 
     return REQ_PROCEED;
 }
--- a/src/server/util/Makefile	Fri Mar 01 22:44:54 2013 +0100
+++ b/src/server/util/Makefile	Sat Mar 16 23:11:34 2013 +0100
@@ -26,8 +26,7 @@
 # POSSIBILITY OF SUCH DAMAGE.
 #
 
-UTIL_CFLAGS = -I/usr/include/mps -g
-
+UTIL_CFLAGS = 
 $(UTIL_OBJPRE)%.o: util/%.c
 	$(CC) -o $@ -c $(UTIL_CFLAGS) $(CFLAGS) $<
 
--- a/src/server/util/io.c	Fri Mar 01 22:44:54 2013 +0100
+++ b/src/server/util/io.c	Sat Mar 16 23:11:34 2013 +0100
@@ -33,20 +33,23 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <sys/uio.h>
-
+#include <sys/sendfile.h>
 #include <limits.h> /* asprintf */
 
+#include "../daemon/vfs.h"
 #include "io.h"
 #include "pool.h"
 
 IOStream native_io_funcs = {
     system_write,
-    system_read
+    system_read,
+    NULL
 };
 
 IOStream net_io_funcs = {
     net_stream_write,
-    net_stream_read
+    net_stream_read,
+    net_stream_sendfile
 };
 
 
@@ -76,7 +79,7 @@
 }
 
 ssize_t net_stream_write(IOStream *st, void *buf, size_t nbytes) {
-    // TODO: implement
+    return write(((NetIOStream*)st)->fd, buf, nbytes);
 }
 
 ssize_t net_stream_read(IOStream *st, void *buf, size_t nbytes) {
@@ -89,6 +92,21 @@
     return r;
 }
 
+ssize_t net_stream_sendfile(IOStream *st, sendfiledata *sfd) {
+    NetIOStream *io = (NetIOStream*)st;
+    // TODO: header and trailer
+    ssize_t ret = 0;
+    off_t fileoffset = sfd->offset;
+    if(sfd->fd->fd != -1) {
+        ret = sendfile(io->fd, sfd->fd->fd, &fileoffset, sfd->len);
+    } else {
+        // TODO: regular copy
+        fprintf(stderr, "sendfile not implemented for SYS_FILE\n");
+    }
+    
+    return ret;
+}
+
 
 ssize_t net_read(SYS_NETFD fd, void *buf, size_t nbytes) {
     ssize_t r = ((IOStream*)fd)->read(fd, buf, nbytes);
@@ -116,6 +134,19 @@
     return len;
 }
 
+ssize_t net_sendfile(SYS_NETFD fd, sendfiledata *sfd) {
+    IOStream *out = fd;
+    if(out->sendfile) {
+        ssize_t r = ((IOStream*)fd)->sendfile(fd, sfd);
+        if(r < 0) {
+            return IO_ERROR;
+        }
+    } else {
+        fprintf(stderr, "stream does not support sendfile\n");
+    }
+    return IO_ERROR;
+}
+
 
 /* iovec buffer */
 iovec_buf_t *iovec_buf_create(pool_handle_t *pool) {
--- a/src/server/util/io.h	Fri Mar 01 22:44:54 2013 +0100
+++ b/src/server/util/io.h	Sat Mar 16 23:11:34 2013 +0100
@@ -39,10 +39,12 @@
 
 typedef ssize_t(*io_write_f)(IOStream *, void *, size_t);
 typedef ssize_t(*io_read_f)(IOStream *, void *, size_t);
+typedef ssize_t(*io_sendfile_f)(IOStream *, sendfiledata *);
 
 struct io_stream {
-    io_write_f write;
-    io_read_f  read;
+    io_write_f    write;
+    io_read_f     read;
+    io_sendfile_f sendfile;
 };
 
 typedef struct SystemIOStream {
@@ -61,6 +63,8 @@
 /* net_ functions */
 ssize_t net_read(SYS_NETFD fd, void *buf, size_t nbytes);
 ssize_t net_write(SYS_NETFD fd, void *buf, size_t nbytes);
+ssize_t net_printf(SYS_NETFD fd, char *format, ...);
+ssize_t net_sendfile(SYS_NETFD fd, sendfiledata *sfd);
 
 
 /* iovec buffer */
@@ -83,6 +87,7 @@
 
 ssize_t net_stream_write(IOStream *st, void *buf, size_t nbytes);
 ssize_t net_stream_read(IOStream *st, void *buf, size_t nbytes);
+ssize_t net_stream_sendfile(IOStream *st, sendfiledata *sfd);
 
 /* iovec buffer */
 iovec_buf_t *iovec_buf_create(pool_handle_t *pool);

mercurial