diff -r e59abb210584 -r a193c42fc809 src/server/webdav/operation.c --- a/src/server/webdav/operation.c Sat Feb 01 18:44:31 2020 +0100 +++ b/src/server/webdav/operation.c Sun Feb 02 17:42:05 2020 +0100 @@ -28,10 +28,12 @@ #include #include +#include #include #include "../daemon/session.h" +#include "../util/pblock.h" #include "operation.h" @@ -523,3 +525,196 @@ return 0; // NOP } + +/**************************************************************************** + * + * VFS OPERATION + * + ****************************************************************************/ + +WebdavVFSOperation* webdav_vfs_op( + Session *sn, + Request *rq, + WebdavBackend *dav, + WSBool precondition) +{ + WebdavVFSOperation *op = pool_malloc(sn->pool, sizeof(WebdavVFSOperation)); + if(!op) { + return NULL; + } + ZERO(op, sizeof(WebdavVFSOperation)); + + op->sn = sn; + op->rq = rq; + op->dav = dav; + op->stat = NULL; + op->stat_errno = 0; + + // create VFS context + VFSContext *vfs = vfs_request_context(sn, rq); + if(!vfs) { + pool_free(sn->pool, op); + return NULL; + } + op->vfs = vfs; + + char *path = pblock_findkeyval(pb_key_path, rq->vars); + op->path = path; + + return op; +} + +int webdav_vfs_stat(WebdavVFSOperation *op) { + if(op->stat) { + return 0; + } else if(op->stat_errno != 0) { + // previous stat failed + return 1; + } + + // stat file + struct stat sbuf; + int ret = vfs_stat(op->vfs, op->path, &sbuf); + if(!ret) { + // save result in op->stat and in s + op->stat = pool_malloc(op->sn->pool, sizeof(struct stat)); + if(op->stat) { + memcpy(op->stat, &sbuf, sizeof(struct stat)); + } else { + ret = 1; + op->stat_errno = ENOMEM; + } + } else { + op->stat_errno = errno; + } + + return ret; +} + +int webdav_vfs_op_do(WebdavVFSOperation *op, WebdavVFSOpType type) { + WSBool exec_vfs = TRUE; + + // requests for each backends + WebdavVFSRequest **requests = pool_calloc( + op->sn->pool, + webdav_num_backends(op->dav), + sizeof(WebdavVFSRequest*)); + if(requests == NULL) { + return REQ_ABORTED; + } + + int ret = REQ_PROCEED; + + // call opt_* func for each backend + WebdavBackend *dav = op->dav; + int called_backends = 0; + while(dav) { + WebdavVFSRequest *request = NULL; + + // get vfs operation functions + vfs_op_func op_func = NULL; + vfs_op_finish_func op_finish_func = NULL; + + if(type == WEBDAV_VFS_MKDIR) { + op_func = dav->opt_mkcol; + op_finish_func = dav->opt_mkcol_finish; + } else if(type == WEBDAV_VFS_DELETE) { + op_func = dav->opt_delete; + op_finish_func = dav->opt_delete_finish; + } + + if(op_func || op_finish_func) { + // we need a request object + request = pool_malloc(op->sn->pool, sizeof(WebdavVFSRequest)); + if(!request) { + exec_vfs = FALSE; + ret = REQ_ABORTED; + break; + } + request->sn = op->sn; + request->rq = op->rq; + request->path = op->path; + request->userdata = NULL; + + requests[called_backends] = request; + } + + // exec backend func for this operation + // this will set 'done' to TRUE, if no further vfs call is required + WSBool done = FALSE; + called_backends++; + if(op_func) { + if(op_func(request, &done)) { + exec_vfs = FALSE; + ret = REQ_ABORTED; + break; + } + } + if(done) { + exec_vfs = FALSE; + } + + dav = dav->next; + } + + // if needed, call vfs func for this operation + if(exec_vfs) { + int r = 0; + if(type == WEBDAV_VFS_MKDIR) { + r = vfs_mkdir(op->vfs, op->path); + } else if(type == WEBDAV_VFS_DELETE) { + r = webdav_vfs_unlink(op); + } + + if(r) { + ret = REQ_ABORTED; + } + } + + WSBool success = ret == REQ_PROCEED ? TRUE : FALSE; + + // finish mkcol (cleanup) by calling opt_*_finish for each backend + dav = op->dav; + int i = 0; + while(dav && i < called_backends) { + // get vfs operation functions + vfs_op_finish_func op_finish_func = NULL; + + if(type == WEBDAV_VFS_MKDIR) { + op_finish_func = dav->opt_mkcol_finish; + } else if(type == WEBDAV_VFS_DELETE) { + op_finish_func = dav->opt_delete_finish; + } + + if(op_finish_func) { + if(op_finish_func(requests[i], success)) { + ret = REQ_ABORTED; // don't exit loop + } + } + + dav = dav->next; + i++; + } + + return ret; +} + +int webdav_vfs_unlink(WebdavVFSOperation *op) { + // stat the file first, to check if the file is a directory + // deletion of simple files can be done just here, + // whereas deleting directories is more complicated + if(webdav_vfs_stat(op)) { + return 1; // error + } else { + int r = 0; + if(!S_ISDIR(op->stat->st_mode)) { + return vfs_unlink(op->vfs, op->path); + } + } + + // delete directory: + + + + return 0; +}