src/server/webdav/operation.c

branch
webdav
changeset 245
a193c42fc809
parent 241
4adad7faf452
child 246
155bdef7fe7e
--- 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 <stdio.h>
 #include <stdlib.h>
+#include <errno.h>
 
 #include <ucx/list.h>
 
 #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;
+}

mercurial