Tue, 31 Dec 2019 14:07:42 +0100
add simple vfs implementation for testing
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2019 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 <ucx/list.h> #include "../daemon/session.h" #include "operation.h" #define WEBDAV_PATH_MAX 8192 WebdavOperation* webdav_operation_create( pool_handle_t *pool, WebdavBackend *dav, UcxList *requests, WebdavResponse *response) { WebdavOperation *op = pool_malloc(pool, sizeof(WebdavOperation)); ZERO(op, sizeof(WebdavOperation)); op->dav = dav; op->requests = requests; op->response = response; response->op = op; return op; } int webdav_op_propfind_begin( WebdavOperation *op, const char *path, VFS_DIR parent, struct stat *s) { // create WebdavResource object for requested resource WebdavResource *resource = op->response->addresource(op->response, path); if(!resource) { return REQ_ABORTED; } // store data that we need when the resource will be closed op->stat = s; op->parent = parent; // get first propfind object WebdavPropfindRequest *propfind = op->requests->data; // execute propfind_do of the first backend for the first resource int ret = REQ_PROCEED; if(op->dav->propfind_do(propfind, op->response, NULL, resource, s)) { ret = REQ_ABORTED; } else { // propfind_do successful, close resource if needed // closing the resource will execute propfind_do of all remaining // backends if(!resource->isclosed) { ret = resource->close(resource); } } return ret; } int webdav_op_propfind_children( WebdavOperation *op, VFSContext *vfs, char *path) { WebdavResponse *response = op->response; WebdavPropfindRequest *request = op->requests->data; UcxAllocator *a = session_get_allocator(request->sn); pool_handle_t *pool = request->sn->pool; UcxList *stack = ucx_list_prepend_a(a, NULL, path); UcxList *stack_end = stack; if(!stack) { return 1; } // reusable buffer for full child path char *newpath = NULL; size_t newpathlen = 0; int err = 0; while(stack && !err) { char *cur_path = stack->data; size_t parent_len = strlen(cur_path); if(parent_len > WEBDAV_PATH_MAX) { log_ereport(LOG_FAILURE, "webdav: maximal path length exceeded"); err = 1; break; } if(cur_path[parent_len-1] == '/') { parent_len--; } size_t max_child_len = WEBDAV_PATH_MAX - parent_len; // when newpath is initialized with the parent path // set path_buf_init to TRUE WSBool path_buf_init = FALSE; VFS_DIR dir = vfs_opendir(vfs, path); if(!dir) { log_ereport( LOG_FAILURE, "webdav: propfind: cannot open directory %d", vfs->vfs_errno); err = 1; break; } VFS_ENTRY f; while(vfs_readdir_stat(dir, &f)) { if(f.stat_errno != 0) { continue; } size_t child_len = strlen(f.name); if(child_len > max_child_len) { log_ereport(LOG_FAILURE, "webdav: maximal path length exceeded"); err = 1; break; } size_t childpathlen = parent_len + child_len + 1; // +1 '/' if(childpathlen > newpathlen) { // we're gonna need a bigger boa^H^H^Hbuffer if(newpath) { pool_free(pool, newpath); } newpath = pool_malloc(pool, childpathlen + 1); if(!newpath) { err = 1; break; } newpathlen = childpathlen; path_buf_init = FALSE; } // create full path string for this child if(!path_buf_init) { memcpy(newpath, cur_path, parent_len); newpath[parent_len] = '/'; } memcpy(newpath+parent_len+1, f.name, child_len); newpath[childpathlen] = 0; // propfind for this child if(webdav_op_propfind_begin(op, newpath, dir, &f.stat)) { err = 1; break; } // depth of -1 means infinity if(request->depth == -1 && S_ISDIR(f.stat.st_mode)) { char *pathcp = pool_malloc(pool, childpathlen + 1); memcpy(pathcp, newpath, childpathlen + 1); // add the newpath copy to the stack // stack_end is always not NULL here, because we remove // the first stack element at the end of the loop UcxList *newlistelm = ucx_list_append_a(a, stack_end, pathcp); if(!newlistelm) { err = 1; break; } stack_end = newlistelm; } } vfs_closedir(dir); if(cur_path != path) { pool_free(pool, cur_path); } stack = ucx_list_remove_a(a, stack, stack); } // in case of an error, we have to free all remaining stack elements UCX_FOREACH(elm, stack) { char *data = elm->data; if(data != path) { pool_free(pool, data); } } return err; } int webdav_op_propfiond_close_resource( WebdavOperation *op, WebdavResource *resource) { // start with second backend and request, because // the first one was already called by webdav_op_propfind_begin WebdavBackend *dav = op->dav->next; UcxList *request = op->requests->next; // call propfind_do of all remaining backends int ret = REQ_PROCEED; while(dav && request) { if(dav->propfind_do( request->data, op->response, op->parent, resource, op->stat)) { ret = REQ_ABORTED; } dav = dav->next; request = request->next; } return ret; } /* * Executes propfind_finish for each Backend */ int webdav_op_propfind_finish(WebdavOperation *op) { WebdavBackend *dav = op->dav; UcxList *requests = op->requests; int ret = REQ_PROCEED; while(dav && requests) { if(dav->propfind_finish(requests->data)) { ret = REQ_ABORTED; } dav = dav->next; requests = requests->next; } return ret; }