|
1 /* |
|
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
|
3 * |
|
4 * Copyright 2019 Olaf Wintermann. All rights reserved. |
|
5 * |
|
6 * Redistribution and use in source and binary forms, with or without |
|
7 * modification, are permitted provided that the following conditions are met: |
|
8 * |
|
9 * 1. Redistributions of source code must retain the above copyright |
|
10 * notice, this list of conditions and the following disclaimer. |
|
11 * |
|
12 * 2. Redistributions in binary form must reproduce the above copyright |
|
13 * notice, this list of conditions and the following disclaimer in the |
|
14 * documentation and/or other materials provided with the distribution. |
|
15 * |
|
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
26 * POSSIBILITY OF SUCH DAMAGE. |
|
27 */ |
|
28 |
|
29 #include <stdio.h> |
|
30 #include <stdlib.h> |
|
31 #include <errno.h> |
|
32 |
|
33 #include <ucx/list.h> |
|
34 |
|
35 #include "../daemon/session.h" |
|
36 #include "../util/pblock.h" |
|
37 |
|
38 #include "operation.h" |
|
39 |
|
40 #define WEBDAV_PATH_MAX 8192 |
|
41 |
|
42 |
|
43 size_t webdav_num_backends(WebdavBackend *dav) { |
|
44 size_t count = 0; |
|
45 while(dav) { |
|
46 count++; |
|
47 dav = dav->next; |
|
48 } |
|
49 return count; |
|
50 } |
|
51 |
|
52 /**************************************************************************** |
|
53 * |
|
54 * PROPFIND OPERATION |
|
55 * |
|
56 ****************************************************************************/ |
|
57 |
|
58 WebdavOperation* webdav_create_propfind_operation( |
|
59 Session *sn, |
|
60 Request *rq, |
|
61 WebdavBackend *dav, |
|
62 WebdavPList *reqprops, |
|
63 UcxList *requests, |
|
64 WebdavResponse *response) |
|
65 { |
|
66 WebdavOperation *op = pool_malloc(sn->pool, sizeof(WebdavOperation)); |
|
67 ZERO(op, sizeof(WebdavOperation)); |
|
68 op->dav = dav; |
|
69 op->sn = sn; |
|
70 op->rq = rq; |
|
71 op->reqprops = reqprops; |
|
72 op->requests = requests; |
|
73 op->response = response; |
|
74 op->response_close = webdav_op_propfiond_close_resource; |
|
75 response->op = op; |
|
76 |
|
77 return op; |
|
78 } |
|
79 |
|
80 int webdav_op_propfind_begin( |
|
81 WebdavOperation *op, |
|
82 const char *href, |
|
83 VFS_DIR parent, |
|
84 struct stat *s) |
|
85 { |
|
86 // create WebdavResource object for requested resource |
|
87 WebdavResource *resource = op->response->addresource(op->response, href); |
|
88 if(!resource) { |
|
89 return REQ_ABORTED; |
|
90 } |
|
91 |
|
92 // store data that we need when the resource will be closed |
|
93 op->stat = s; |
|
94 op->parent = parent; |
|
95 |
|
96 // get first propfind object |
|
97 WebdavPropfindRequest *propfind = op->requests->data; |
|
98 |
|
99 // execute propfind_do of the first backend for the first resource |
|
100 int ret = REQ_PROCEED; |
|
101 if(op->dav->propfind_do(propfind, op->response, NULL, resource, s)) { |
|
102 ret = REQ_ABORTED; |
|
103 } else { |
|
104 // propfind_do successful, close resource if needed |
|
105 // closing the resource will execute propfind_do of all remaining |
|
106 // backends |
|
107 if(!resource->isclosed) { |
|
108 ret = resource->close(resource); |
|
109 } |
|
110 } |
|
111 |
|
112 return ret; |
|
113 } |
|
114 |
|
115 typedef struct PathSearchElm { |
|
116 char *href; |
|
117 char *path; |
|
118 size_t hreflen; |
|
119 size_t pathlen; |
|
120 } PathSearchElm; |
|
121 |
|
122 /* |
|
123 * concats base + / + elm |
|
124 * if baseinit is true, only elm is copied |
|
125 */ |
|
126 static int path_buf_concat( |
|
127 pool_handle_t *pool, |
|
128 char **buf, |
|
129 size_t * restrict len, |
|
130 WSBool * restrict baseinit, |
|
131 const char *base, |
|
132 size_t baselen, |
|
133 const char *elm, |
|
134 size_t elmlen) |
|
135 { |
|
136 if(base[baselen-1] == '/') { |
|
137 baselen--; |
|
138 } |
|
139 |
|
140 size_t newlen = baselen + elmlen + 1; |
|
141 if(newlen > WEBDAV_PATH_MAX) { |
|
142 log_ereport(LOG_FAILURE, "webdav: maximal path length exceeded"); |
|
143 return 1; |
|
144 } |
|
145 |
|
146 // check if new path + terminator fits in the buffer |
|
147 if(newlen + 1 > *len) { |
|
148 *len = newlen + 128; |
|
149 char *newbuf = pool_realloc(pool, *buf, newlen); |
|
150 if(newbuf) { |
|
151 log_ereport(LOG_FAILURE, "webdav: path memory allocation failed"); |
|
152 return 1; |
|
153 } |
|
154 *baseinit = FALSE; |
|
155 |
|
156 *buf = newbuf; |
|
157 } |
|
158 |
|
159 // if baseinit is true, the parent is already in the buffer |
|
160 // and we don't need to memcpy it again |
|
161 if(!(*baseinit)) { |
|
162 memcpy(*buf, base, baselen); |
|
163 (*buf)[baselen] = '/'; |
|
164 *baseinit = TRUE; |
|
165 } |
|
166 // copy child and terminate string |
|
167 memcpy((*buf) + baselen + 1, elm, elmlen); |
|
168 (*buf)[newlen] = '\0'; |
|
169 |
|
170 return 0; |
|
171 } |
|
172 |
|
173 static int propfind_child_cb( |
|
174 VFSContext *vfs, |
|
175 const char *href, |
|
176 const char *path, |
|
177 VFSDir *parent, |
|
178 struct stat *s, |
|
179 void *op) |
|
180 { |
|
181 return webdav_op_propfind_begin(op, href, parent, s); |
|
182 } |
|
183 |
|
184 int webdav_op_propfind_children( |
|
185 WebdavOperation *op, |
|
186 VFSContext *vfs, |
|
187 const char *href, |
|
188 const char *path) |
|
189 { |
|
190 WebdavPropfindRequest *request = op->requests->data; |
|
191 return webdav_op_iterate_children( |
|
192 vfs, request->depth, href, path, propfind_child_cb, op); |
|
193 } |
|
194 |
|
195 int webdav_op_propfiond_close_resource( |
|
196 WebdavOperation *op, |
|
197 WebdavResource *resource) |
|
198 { |
|
199 // start with second backend and request, because |
|
200 // the first one was already called by webdav_op_propfind_begin |
|
201 WebdavBackend *dav = op->dav->next; |
|
202 UcxList *request = op->requests->next; |
|
203 |
|
204 // call propfind_do of all remaining backends |
|
205 int ret = REQ_PROCEED; |
|
206 while(dav && request) { |
|
207 if(dav->propfind_do( |
|
208 request->data, |
|
209 op->response, |
|
210 op->parent, |
|
211 resource, |
|
212 op->stat)) |
|
213 { |
|
214 ret = REQ_ABORTED; |
|
215 } |
|
216 |
|
217 dav = dav->next; |
|
218 request = request->next; |
|
219 } |
|
220 return ret; |
|
221 } |
|
222 |
|
223 /* |
|
224 * Executes propfind_finish for each Backend |
|
225 */ |
|
226 int webdav_op_propfind_finish(WebdavOperation *op) { |
|
227 WebdavBackend *dav = op->dav; |
|
228 UcxList *requests = op->requests; |
|
229 |
|
230 int ret = REQ_PROCEED; |
|
231 while(dav && requests) { |
|
232 if(dav->propfind_finish(requests->data)) { |
|
233 ret = REQ_ABORTED; |
|
234 } |
|
235 |
|
236 dav = dav->next; |
|
237 requests = requests->next; |
|
238 } |
|
239 return ret; |
|
240 } |
|
241 |
|
242 /**************************************************************************** |
|
243 * |
|
244 * PROPPATCH OPERATION |
|
245 * |
|
246 ****************************************************************************/ |
|
247 |
|
248 WebdavOperation* webdav_create_proppatch_operation( |
|
249 Session *sn, |
|
250 Request *rq, |
|
251 WebdavBackend *dav, |
|
252 WebdavProppatchRequest *proppatch, |
|
253 WebdavResponse *response) |
|
254 { |
|
255 WebdavOperation *op = pool_malloc(sn->pool, sizeof(WebdavOperation)); |
|
256 ZERO(op, sizeof(WebdavOperation)); |
|
257 op->dav = dav; |
|
258 op->sn = sn; |
|
259 op->rq = rq; |
|
260 op->reqprops = NULL; |
|
261 op->response = response; |
|
262 op->proppatch = proppatch; |
|
263 op->response_close = webdav_op_proppatch_close_resource; |
|
264 response->op = op; |
|
265 |
|
266 return op; |
|
267 } |
|
268 |
|
269 |
|
270 |
|
271 int webdav_op_proppatch( |
|
272 WebdavOperation *op, |
|
273 const char *href, |
|
274 const char *path) |
|
275 { |
|
276 WebdavProppatchRequest *orig_request = op->proppatch; |
|
277 UcxAllocator *a = session_get_allocator(op->sn); |
|
278 |
|
279 // create WebdavResource object for the requested resource |
|
280 WebdavResource *resource = op->response->addresource(op->response, href); |
|
281 if(!resource) { |
|
282 return REQ_ABORTED; |
|
283 } |
|
284 |
|
285 VFSContext *ctx = NULL; |
|
286 VFSFile *file = NULL; |
|
287 |
|
288 // requests for each backends |
|
289 WebdavProppatchRequest **requests = pool_calloc( |
|
290 op->sn->pool, |
|
291 webdav_num_backends(op->dav), |
|
292 sizeof(WebdavProppatchRequest*)); |
|
293 if(requests == NULL) { |
|
294 return REQ_ABORTED; |
|
295 } |
|
296 |
|
297 WebdavPList *prev_set = orig_request->set; |
|
298 WebdavPList *prev_remove = orig_request->remove; |
|
299 size_t set_count = orig_request->setcount; |
|
300 size_t remove_count = orig_request->removecount; |
|
301 |
|
302 int ret = REQ_PROCEED; |
|
303 |
|
304 // iterate backends and execute proppatch_do |
|
305 WebdavBackend *dav = op->dav; |
|
306 size_t numrequests = 0; |
|
307 while(dav) { |
|
308 WebdavPList *set = webdav_plist_clone_s( |
|
309 op->sn->pool, |
|
310 prev_set, |
|
311 &set_count); |
|
312 WebdavPList *remove = webdav_plist_clone_s( |
|
313 op->sn->pool, |
|
314 prev_remove, |
|
315 &remove_count); |
|
316 if((prev_set && !set) || (prev_remove && !remove)) { |
|
317 // clone failed, OOM |
|
318 ret = REQ_ABORTED; |
|
319 break; |
|
320 } |
|
321 |
|
322 // create new WebdavProppatchRequest object for this backend |
|
323 WebdavProppatchRequest *req = pool_malloc( |
|
324 op->sn->pool, |
|
325 sizeof(WebdavProppatchRequest)); |
|
326 memcpy(req, orig_request, sizeof(WebdavProppatchRequest)); |
|
327 req->dav = dav; |
|
328 req->set = orig_request->set; |
|
329 req->setcount = orig_request->setcount; |
|
330 req->remove = orig_request->remove; |
|
331 req->removecount = orig_request->removecount; |
|
332 req->userdata = NULL; |
|
333 |
|
334 // check if we need to open the file because the backend wants it |
|
335 if(!file && (dav->settings & WS_WEBDAV_PROPPATCH_USE_VFS) |
|
336 == WS_WEBDAV_PROPPATCH_USE_VFS) |
|
337 { |
|
338 ctx = vfs_request_context(op->sn, op->rq); |
|
339 if(!ctx) { |
|
340 ret = REQ_ABORTED; |
|
341 break; |
|
342 } |
|
343 |
|
344 file = vfs_open(ctx, path, O_RDONLY); |
|
345 if(!file) { |
|
346 protocol_status( |
|
347 op->sn, |
|
348 op->rq, |
|
349 util_errno2status(ctx->vfs_errno), |
|
350 NULL); |
|
351 ret = REQ_ABORTED; |
|
352 } |
|
353 } |
|
354 |
|
355 // execute proppatch_do |
|
356 if(dav->proppatch_do(req, resource, file, &set, &remove)) { |
|
357 // return later, because we need do execute proppatch_finish |
|
358 // for all successfully called backends |
|
359 ret = REQ_ABORTED; |
|
360 break; |
|
361 } |
|
362 |
|
363 // proppatch_do should remove all handled props from set and remove |
|
364 // in the next iteration, the backend must use these reduced lists |
|
365 prev_set = set; |
|
366 prev_remove = remove; |
|
367 |
|
368 requests[numrequests++] = req; |
|
369 |
|
370 // continue with next backend |
|
371 dav = dav->next; |
|
372 } |
|
373 |
|
374 WSBool commit = FALSE; |
|
375 if(ret == REQ_PROCEED && resource->err == 0) { |
|
376 // no errors, no properties with errors -> save the changes |
|
377 commit = TRUE; |
|
378 } |
|
379 |
|
380 // call proppatch_finish for each successfully called proppatch_do |
|
381 dav = op->dav; |
|
382 int i = 0; |
|
383 while(dav && i < numrequests) { |
|
384 if(dav->proppatch_finish(requests[i], resource, file, commit)) { |
|
385 ret = REQ_ABORTED; |
|
386 } |
|
387 i++; |
|
388 dav = dav->next; |
|
389 } |
|
390 |
|
391 if(file) { |
|
392 vfs_close(file); |
|
393 } |
|
394 |
|
395 if(resource->close(resource)) { |
|
396 ret = REQ_ABORTED; |
|
397 } |
|
398 |
|
399 return ret; |
|
400 } |
|
401 |
|
402 int webdav_op_proppatch_close_resource( |
|
403 WebdavOperation *op, |
|
404 WebdavResource *resource) |
|
405 { |
|
406 return 0; // NOP |
|
407 } |
|
408 |
|
409 |
|
410 /**************************************************************************** |
|
411 * |
|
412 * VFS OPERATION |
|
413 * |
|
414 ****************************************************************************/ |
|
415 |
|
416 WebdavVFSOperation* webdav_vfs_op( |
|
417 Session *sn, |
|
418 Request *rq, |
|
419 WebdavBackend *dav, |
|
420 WSBool precondition) |
|
421 { |
|
422 WebdavVFSOperation *op = pool_malloc(sn->pool, sizeof(WebdavVFSOperation)); |
|
423 if(!op) { |
|
424 return NULL; |
|
425 } |
|
426 ZERO(op, sizeof(WebdavVFSOperation)); |
|
427 |
|
428 op->sn = sn; |
|
429 op->rq = rq; |
|
430 op->dav = dav; |
|
431 op->stat = NULL; |
|
432 op->stat_errno = 0; |
|
433 |
|
434 // create VFS context |
|
435 VFSContext *vfs = vfs_request_context(sn, rq); |
|
436 if(!vfs) { |
|
437 pool_free(sn->pool, op); |
|
438 return NULL; |
|
439 } |
|
440 op->vfs = vfs; |
|
441 |
|
442 char *path = pblock_findkeyval(pb_key_path, rq->vars); |
|
443 op->path = path; |
|
444 |
|
445 return op; |
|
446 } |
|
447 |
|
448 WebdavVFSOperation webdav_vfs_sub_op( |
|
449 WebdavVFSOperation *op, |
|
450 char *path, |
|
451 struct stat *s) |
|
452 { |
|
453 WebdavVFSOperation sub; |
|
454 sub.dav = op->dav; |
|
455 sub.path = path; |
|
456 sub.sn = op->sn; |
|
457 sub.vfs = op->vfs; |
|
458 sub.path = path; |
|
459 sub.stat = s; |
|
460 sub.stat_errno = 0; |
|
461 return sub; |
|
462 } |
|
463 |
|
464 int webdav_op_iterate_children( |
|
465 VFSContext *vfs, |
|
466 int depth, |
|
467 const char *href, |
|
468 const char *path, |
|
469 vfs_op_child_func func, |
|
470 void *userdata) |
|
471 { |
|
472 UcxAllocator *a = session_get_allocator(vfs->sn); |
|
473 pool_handle_t *pool = vfs->sn->pool; |
|
474 |
|
475 PathSearchElm *start_elm = pool_malloc(pool, sizeof(PathSearchElm)); |
|
476 start_elm->href = pool_strdup(pool, href ? href : ""); |
|
477 start_elm->path = pool_strdup(pool, path ? path : ""); |
|
478 start_elm->hreflen = href ? strlen(href) : 0; |
|
479 start_elm->pathlen = path ? strlen(path) : 0; |
|
480 |
|
481 UcxList *stack = ucx_list_prepend_a(a, NULL, start_elm); |
|
482 UcxList *stack_end = stack; |
|
483 if(!stack) { |
|
484 return 1; |
|
485 } |
|
486 |
|
487 // reusable buffer for full child path and href |
|
488 char *newpath = pool_malloc(pool, 256); |
|
489 size_t newpathlen = 256; |
|
490 |
|
491 char *newhref = pool_malloc(pool, 256); |
|
492 size_t newhreflen = 256; |
|
493 |
|
494 int err = 0; |
|
495 while(stack && !err) { |
|
496 PathSearchElm *cur_elm = stack->data; |
|
497 |
|
498 // when newpath is initialized with the parent path |
|
499 // set path_buf_init to TRUE |
|
500 WSBool href_buf_init = FALSE; |
|
501 WSBool path_buf_init = FALSE; |
|
502 |
|
503 VFS_DIR dir = vfs_opendir(vfs, cur_elm->path); |
|
504 if(!dir) { |
|
505 log_ereport( |
|
506 LOG_FAILURE, |
|
507 "webdav: propfind: cannot open directory %d", |
|
508 vfs->vfs_errno); |
|
509 err = 1; |
|
510 break; |
|
511 } |
|
512 |
|
513 VFS_ENTRY f; |
|
514 while(vfs_readdir_stat(dir, &f)) { |
|
515 if(f.stat_errno != 0) { |
|
516 continue; |
|
517 } |
|
518 |
|
519 size_t child_len = strlen(f.name); |
|
520 |
|
521 // create new path and href for the child |
|
522 if(path_buf_concat( |
|
523 pool, |
|
524 &newhref, |
|
525 &newhreflen, |
|
526 &href_buf_init, |
|
527 cur_elm->href, |
|
528 cur_elm->hreflen, |
|
529 f.name, |
|
530 child_len)) |
|
531 { |
|
532 err = 1; |
|
533 break; |
|
534 } |
|
535 if(path_buf_concat( |
|
536 pool, |
|
537 &newpath, |
|
538 &newpathlen, |
|
539 &path_buf_init, |
|
540 cur_elm->path, |
|
541 cur_elm->pathlen, |
|
542 f.name, |
|
543 child_len)) |
|
544 { |
|
545 err = 1; |
|
546 break; |
|
547 } |
|
548 size_t childhreflen = cur_elm->hreflen + 1 + child_len; |
|
549 size_t childpathlen = cur_elm->pathlen + 1 + child_len; |
|
550 |
|
551 // execute callback func for this file |
|
552 if(func(vfs, newhref, newpath, dir, &f.stat, userdata)) { |
|
553 err = 1; |
|
554 break; |
|
555 } |
|
556 |
|
557 // depth of -1 means infinity |
|
558 if(depth == -1 && S_ISDIR(f.stat.st_mode)) { |
|
559 char *hrefcp = pool_malloc(pool, childhreflen + 1); |
|
560 memcpy(hrefcp, newhref, childhreflen + 1); |
|
561 hrefcp[childhreflen] = '\0'; |
|
562 |
|
563 char *pathcp = pool_malloc(pool, childpathlen + 1); |
|
564 memcpy(pathcp, newpath, childpathlen + 1); |
|
565 pathcp[childpathlen] = '\0'; |
|
566 |
|
567 PathSearchElm *new_elm = pool_malloc(pool, |
|
568 sizeof(PathSearchElm)); |
|
569 new_elm->href = hrefcp; |
|
570 new_elm->path = pathcp; |
|
571 new_elm->hreflen = childhreflen; |
|
572 new_elm->pathlen = childpathlen; |
|
573 |
|
574 // add the new_elm to the stack |
|
575 // stack_end is always not NULL here, because we remove |
|
576 // the first stack element at the end of the loop |
|
577 UcxList *newlistelm = ucx_list_append_a(a, stack_end, new_elm); |
|
578 if(!newlistelm) { |
|
579 err = 1; |
|
580 break; |
|
581 } |
|
582 stack_end = newlistelm; |
|
583 } |
|
584 } |
|
585 |
|
586 vfs_closedir(dir); |
|
587 |
|
588 pool_free(pool, cur_elm->path); |
|
589 pool_free(pool, cur_elm->href); |
|
590 pool_free(pool, cur_elm); |
|
591 |
|
592 stack = ucx_list_remove_a(a, stack, stack); |
|
593 } |
|
594 |
|
595 // in case of an error, we have to free all remaining stack elements |
|
596 UCX_FOREACH(elm, stack) { |
|
597 char *data = elm->data; |
|
598 if(data != path) { |
|
599 pool_free(pool, data); |
|
600 } |
|
601 } |
|
602 |
|
603 return err; |
|
604 } |
|
605 |
|
606 |
|
607 int webdav_vfs_stat(WebdavVFSOperation *op) { |
|
608 if(op->stat) { |
|
609 return 0; |
|
610 } else if(op->stat_errno != 0) { |
|
611 // previous stat failed |
|
612 return 1; |
|
613 } |
|
614 |
|
615 // stat file |
|
616 struct stat sbuf; |
|
617 int ret = vfs_stat(op->vfs, op->path, &sbuf); |
|
618 if(!ret) { |
|
619 // save result in op->stat and in s |
|
620 op->stat = pool_malloc(op->sn->pool, sizeof(struct stat)); |
|
621 if(op->stat) { |
|
622 memcpy(op->stat, &sbuf, sizeof(struct stat)); |
|
623 } else { |
|
624 ret = 1; |
|
625 op->stat_errno = ENOMEM; |
|
626 } |
|
627 } else { |
|
628 op->stat_errno = errno; |
|
629 } |
|
630 |
|
631 return ret; |
|
632 } |
|
633 |
|
634 int webdav_vfs_op_do(WebdavVFSOperation *op, WebdavVFSOpType type) { |
|
635 WSBool exec_vfs = TRUE; |
|
636 |
|
637 // requests for each backends |
|
638 WebdavVFSRequest **requests = pool_calloc( |
|
639 op->sn->pool, |
|
640 webdav_num_backends(op->dav), |
|
641 sizeof(WebdavVFSRequest*)); |
|
642 if(requests == NULL) { |
|
643 return REQ_ABORTED; |
|
644 } |
|
645 |
|
646 int ret = REQ_PROCEED; |
|
647 |
|
648 // call opt_* func for each backend |
|
649 WebdavBackend *dav = op->dav; |
|
650 int called_backends = 0; |
|
651 while(dav) { |
|
652 WebdavVFSRequest *request = NULL; |
|
653 |
|
654 // get vfs operation functions |
|
655 vfs_op_func op_func = NULL; |
|
656 vfs_op_finish_func op_finish_func = NULL; |
|
657 |
|
658 if(type == WEBDAV_VFS_MKDIR) { |
|
659 op_func = dav->opt_mkcol; |
|
660 op_finish_func = dav->opt_mkcol_finish; |
|
661 } else if(type == WEBDAV_VFS_DELETE) { |
|
662 op_func = dav->opt_delete; |
|
663 op_finish_func = dav->opt_delete_finish; |
|
664 } |
|
665 |
|
666 if(op_func || op_finish_func) { |
|
667 // we need a request object |
|
668 request = pool_malloc(op->sn->pool, sizeof(WebdavVFSRequest)); |
|
669 if(!request) { |
|
670 exec_vfs = FALSE; |
|
671 ret = REQ_ABORTED; |
|
672 break; |
|
673 } |
|
674 request->sn = op->sn; |
|
675 request->rq = op->rq; |
|
676 request->path = op->path; |
|
677 request->userdata = NULL; |
|
678 |
|
679 requests[called_backends] = request; |
|
680 } |
|
681 |
|
682 // exec backend func for this operation |
|
683 // this will set 'done' to TRUE, if no further vfs call is required |
|
684 WSBool done = FALSE; |
|
685 called_backends++; |
|
686 if(op_func) { |
|
687 if(op_func(request, &done)) { |
|
688 exec_vfs = FALSE; |
|
689 ret = REQ_ABORTED; |
|
690 break; |
|
691 } |
|
692 } |
|
693 if(done) { |
|
694 exec_vfs = FALSE; |
|
695 } |
|
696 |
|
697 dav = dav->next; |
|
698 } |
|
699 |
|
700 // if needed, call vfs func for this operation |
|
701 if(exec_vfs) { |
|
702 int r = 0; |
|
703 if(type == WEBDAV_VFS_MKDIR) { |
|
704 r = vfs_mkdir(op->vfs, op->path); |
|
705 } else if(type == WEBDAV_VFS_DELETE) { |
|
706 r = webdav_vfs_unlink(op); |
|
707 } |
|
708 |
|
709 if(r) { |
|
710 ret = REQ_ABORTED; |
|
711 } |
|
712 } |
|
713 |
|
714 WSBool success = ret == REQ_PROCEED ? TRUE : FALSE; |
|
715 |
|
716 // finish mkcol (cleanup) by calling opt_*_finish for each backend |
|
717 dav = op->dav; |
|
718 int i = 0; |
|
719 while(dav && i < called_backends) { |
|
720 // get vfs operation functions |
|
721 vfs_op_finish_func op_finish_func = NULL; |
|
722 |
|
723 if(type == WEBDAV_VFS_MKDIR) { |
|
724 op_finish_func = dav->opt_mkcol_finish; |
|
725 } else if(type == WEBDAV_VFS_DELETE) { |
|
726 op_finish_func = dav->opt_delete_finish; |
|
727 } |
|
728 |
|
729 if(op_finish_func) { |
|
730 if(op_finish_func(requests[i], success)) { |
|
731 ret = REQ_ABORTED; // don't exit loop |
|
732 } |
|
733 } |
|
734 |
|
735 dav = dav->next; |
|
736 i++; |
|
737 } |
|
738 |
|
739 return ret; |
|
740 } |
|
741 |
|
742 int webdav_vfs_unlink(WebdavVFSOperation *op) { |
|
743 // stat the file first, to check if the file is a directory |
|
744 if(webdav_vfs_stat(op)) { |
|
745 return 1; // error |
|
746 } else { |
|
747 if(!S_ISDIR(op->stat->st_mode)) { |
|
748 return vfs_unlink(op->vfs, op->path); |
|
749 } else { |
|
750 return vfs_rmdir(op->vfs, op->path); |
|
751 } |
|
752 } |
|
753 } |