# HG changeset patch # User Olaf Wintermann # Date 1363471894 -3600 # Node ID 3a1d5a52adfc05173f49a4615aef0358839cf527 # Parent 5ec9abba1027c59d8f81163339a1bb28a9374818 new vfs api diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/admin/Makefile --- 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) $< diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/config/Makefile --- 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) $< diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/daemon/Makefile --- 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) $< diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/daemon/acl.c --- 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;iacenum;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 } diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/daemon/acl.h --- 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 diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/daemon/config.c --- 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; } diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/daemon/func.c --- 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; diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/daemon/httprequest.c --- 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; diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/daemon/objs.mk --- 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 diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/daemon/request.c --- 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; } diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/daemon/vfs.c --- /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 +#include +#include +#include + +#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); +} diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/daemon/vfs.h --- /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 */ + diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/public/nsapi.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 */ diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/safs/Makefile --- 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) $< diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/safs/service.c --- 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 #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; } diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/util/Makefile --- 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) $< diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/util/io.c --- 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 #include #include - +#include #include /* 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) { diff -r 5ec9abba1027 -r 3a1d5a52adfc src/server/util/io.h --- 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);