src/server/webdav/operation.c

Tue, 31 Dec 2019 11:57:02 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Tue, 31 Dec 2019 11:57:02 +0100
branch
webdav
changeset 218
2ba512b284b9
parent 217
8ed14d76db42
child 220
2915b6c11aec
permissions
-rw-r--r--

add webdav_op_propfind_begin test that checks backend chaining

/*
 * 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;
}

mercurial