8d3cdc72f3dda3abcdac12f7be15ab4fbb03aee4
1 /* $Id$ */
3 /***
4 Copyright (c) 2004-2006 Lennart Poettering
6 Permission is hereby granted, free of charge, to any person
7 obtaining a copy of this software and associated documentation files
8 (the "Software"), to deal in the Software without restriction,
9 including without limitation the rights to use, copy, modify, merge,
10 publish, distribute, sublicense, and/or sell copies of the Software,
11 and to permit persons to whom the Software is furnished to do so,
12 subject to the following conditions:
14 The above copyright notice and this permission notice shall be
15 included in all copies or substantial portions of the Software.
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25 ***/
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
31 #include <signal.h>
32 #include <pthread.h>
33 #include <time.h>
34 #include <assert.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <dirent.h>
41 #include <errno.h>
42 #include <sys/statfs.h>
43 #include <getopt.h>
44 #include <attr/xattr.h>
46 #include <ne_request.h>
47 #include <ne_basic.h>
48 #include <ne_props.h>
49 #include <ne_utils.h>
50 #include <ne_socket.h>
51 #include <ne_auth.h>
52 #include <ne_dates.h>
53 #include <ne_redirect.h>
54 #include <ne_uri.h>
56 #include <fuse.h>
58 #include "statcache.h"
59 #include "filecache.h"
60 #include "session.h"
61 #include "fusedav.h"
63 const ne_propname query_properties[] = {
64 { "DAV:", "resourcetype" },
65 { "http://apache.org/dav/props/", "executable" },
66 { "DAV:", "getcontentlength" },
67 { "DAV:", "getlastmodified" },
68 { "DAV:", "creationdate" },
69 { NULL, NULL }
70 };
72 mode_t mask = 0;
73 int debug = 0;
74 struct fuse* fuse = NULL;
75 ne_lock_store *lock_store = NULL;
76 struct ne_lock *lock = NULL;
77 int lock_thread_exit = 0;
78 int lock_timeout = 60;
80 #define MIME_XATTR "user.mime_type"
82 #define MAX_REDIRECTS 10
84 struct fill_info {
85 void *buf;
86 fuse_fill_dir_t filler;
87 const char *root;
88 };
90 static int get_stat(const char *path, struct stat *stbuf);
92 static pthread_once_t path_cvt_once = PTHREAD_ONCE_INIT;
93 static pthread_key_t path_cvt_tsd_key;
95 static void path_cvt_tsd_key_init(void) {
96 pthread_key_create(&path_cvt_tsd_key, free);
97 }
99 static const char *path_cvt(const char *path) {
100 char *r, *t;
101 int l;
103 pthread_once(&path_cvt_once, path_cvt_tsd_key_init);
105 if ((r = pthread_getspecific(path_cvt_tsd_key)))
106 free(r);
108 t = malloc((l = strlen(base_directory)+strlen(path))+1);
109 assert(t);
110 sprintf(t, "%s%s", base_directory, path);
112 if (l > 1 && t[l-1] == '/')
113 t[l-1] = 0;
115 r = ne_path_escape(t);
116 free(t);
118 pthread_setspecific(path_cvt_tsd_key, r);
120 return r;
121 }
123 static int simple_propfind_with_redirect(
124 ne_session *session,
125 const char *path,
126 int depth,
127 const ne_propname *props,
128 ne_props_result results,
129 void *userdata) {
131 int i, ret;
133 for (i = 0; i < MAX_REDIRECTS; i++) {
134 const ne_uri *u;
136 if ((ret = ne_simple_propfind(session, path, depth, props, results, userdata)) != NE_REDIRECT)
137 return ret;
139 if (!(u = ne_redirect_location(session)))
140 break;
142 if (!session_is_local(u))
143 break;
145 if (debug)
146 fprintf(stderr, "REDIRECT FROM '%s' to '%s'\n", path, u->path);
148 path = u->path;
149 }
151 return ret;
152 }
154 static int proppatch_with_redirect(
155 ne_session *session,
156 const char *path,
157 const ne_proppatch_operation *ops) {
159 int i, ret;
161 for (i = 0; i < MAX_REDIRECTS; i++) {
162 const ne_uri *u;
164 if ((ret = ne_proppatch(session, path, ops)) != NE_REDIRECT)
165 return ret;
167 if (!(u = ne_redirect_location(session)))
168 break;
170 if (!session_is_local(u))
171 break;
173 if (debug)
174 fprintf(stderr, "REDIRECT FROM '%s' to '%s'\n", path, u->path);
176 path = u->path;
177 }
179 return ret;
180 }
183 static void fill_stat(struct stat* st, const ne_prop_result_set *results, int is_dir) {
184 const char *rt, *e, *gcl, *glm, *cd;
185 const ne_propname resourcetype = { "DAV:", "resourcetype" };
186 const ne_propname executable = { "http://apache.org/dav/props/", "executable" };
187 const ne_propname getcontentlength = { "DAV:", "getcontentlength" };
188 const ne_propname getlastmodified = { "DAV:", "getlastmodified" };
189 const ne_propname creationdate = { "DAV:", "creationdate" };
191 assert(st && results);
193 rt = ne_propset_value(results, &resourcetype);
194 e = ne_propset_value(results, &executable);
195 gcl = ne_propset_value(results, &getcontentlength);
196 glm = ne_propset_value(results, &getlastmodified);
197 cd = ne_propset_value(results, &creationdate);
199 memset(st, 0, sizeof(struct stat));
201 if (is_dir) {
202 st->st_mode = S_IFDIR | 0777;
203 st->st_nlink = 3; /* find will ignore this directory if nlin <= and st_size == 0 */
204 st->st_size = 4096;
205 } else {
206 st->st_mode = S_IFREG | (e && (*e == 'T' || *e == 't') ? 0777 : 0666);
207 st->st_nlink = 1;
208 st->st_size = gcl ? atoll(gcl) : 0;
209 }
211 st->st_atime = time(NULL);
212 st->st_mtime = glm ? ne_rfc1123_parse(glm) : 0;
213 st->st_ctime = cd ? ne_iso8601_parse(cd) : 0;
215 st->st_blocks = (st->st_size+511)/512;
216 /*fprintf(stderr, "a: %u; m: %u; c: %u\n", st->st_atime, st->st_mtime, st->st_ctime);*/
218 st->st_mode &= ~mask;
220 st->st_uid = getuid();
221 st->st_gid = getgid();
222 }
224 static char *strip_trailing_slash(char *fn, int *is_dir) {
225 size_t l = strlen(fn);
226 assert(fn);
227 assert(is_dir);
228 assert(l > 0);
230 if ((*is_dir = (fn[l-1] == '/')))
231 fn[l-1] = 0;
233 return fn;
234 }
236 static void getdir_propfind_callback(void *userdata, const ne_uri *u, const ne_prop_result_set *results) {
237 struct fill_info *f = userdata;
238 struct stat st;
239 char fn[PATH_MAX], *t;
240 int is_dir = 0;
242 assert(f);
244 strncpy(fn, u->path, sizeof(fn));
245 fn[sizeof(fn)-1] = 0;
246 strip_trailing_slash(fn, &is_dir);
248 if (strcmp(fn, f->root) && fn[0]) {
249 char *h;
251 if ((t = strrchr(fn, '/')))
252 t++;
253 else
254 t = fn;
256 dir_cache_add(f->root, t);
257 f->filler(f->buf, h = ne_path_unescape(t), NULL, 0);
258 free(h);
259 }
261 fill_stat(&st, results, is_dir);
262 stat_cache_set(fn, &st);
263 }
265 static void getdir_cache_callback(
266 const char *root,
267 const char *fn,
268 void *user) {
270 struct fill_info *f = user;
271 char path[PATH_MAX];
272 char *h;
274 assert(f);
276 snprintf(path, sizeof(path), "%s/%s", !strcmp(root, "/") ? "" : root, fn);
278 f->filler(f->buf, h = ne_path_unescape(fn), NULL, 0);
279 free(h);
280 }
282 static int dav_readdir(
283 const char *path,
284 void *buf,
285 fuse_fill_dir_t filler,
286 __unused off_t offset,
287 __unused struct fuse_file_info *fi) {
289 struct fill_info f;
290 ne_session *session;
292 path = path_cvt(path);
294 if (debug)
295 fprintf(stderr, "getdir(%s)\n", path);
297 f.buf = buf;
298 f.filler = filler;
299 f.root = path;
301 filler(buf, ".", NULL, 0);
302 filler(buf, "..", NULL, 0);
304 if (dir_cache_enumerate(path, getdir_cache_callback, &f) < 0) {
306 if (debug)
307 fprintf(stderr, "DIR-CACHE-MISS\n");
309 if (!(session = session_get(1)))
310 return -EIO;
312 dir_cache_begin(path);
314 if (simple_propfind_with_redirect(session, path, NE_DEPTH_ONE, query_properties, getdir_propfind_callback, &f) != NE_OK) {
315 dir_cache_finish(path, 2);
316 fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
317 return -ENOENT;
318 }
320 dir_cache_finish(path, 1);
321 }
323 return 0;
324 }
326 static void getattr_propfind_callback(void *userdata, const ne_uri *u, const ne_prop_result_set *results) {
327 struct stat *st = (struct stat*) userdata;
328 char fn[PATH_MAX];
329 int is_dir;
331 assert(st);
333 strncpy(fn, u->path, sizeof(fn));
334 fn[sizeof(fn)-1] = 0;
335 strip_trailing_slash(fn, &is_dir);
337 fill_stat(st, results, is_dir);
338 stat_cache_set(fn, st);
339 }
341 static int get_stat(const char *path, struct stat *stbuf) {
342 ne_session *session;
344 if (!(session = session_get(1)))
345 return -EIO;
347 if (stat_cache_get(path, stbuf) == 0) {
348 return stbuf->st_mode == 0 ? -ENOENT : 0;
349 } else {
350 if (debug)
351 fprintf(stderr, "STAT-CACHE-MISS\n");
353 if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, query_properties, getattr_propfind_callback, stbuf) != NE_OK) {
354 stat_cache_invalidate(path);
355 fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
356 return -ENOENT;
357 }
359 return 0;
360 }
361 }
363 static int dav_getattr(const char *path, struct stat *stbuf) {
364 path = path_cvt(path);
365 if (debug)
366 fprintf(stderr, "getattr(%s)\n", path);
367 return get_stat(path, stbuf);
368 }
370 static int dav_unlink(const char *path) {
371 int r;
372 struct stat st;
373 ne_session *session;
375 path = path_cvt(path);
377 if (debug)
378 fprintf(stderr, "unlink(%s)\n", path);
380 if (!(session = session_get(1)))
381 return -EIO;
383 if ((r = get_stat(path, &st)) < 0)
384 return r;
386 if (!S_ISREG(st.st_mode))
387 return -EISDIR;
389 if (ne_delete(session, path)) {
390 fprintf(stderr, "DELETE failed: %s\n", ne_get_error(session));
391 return -ENOENT;
392 }
394 stat_cache_invalidate(path);
395 dir_cache_invalidate_parent(path);
397 return 0;
398 }
400 static int dav_rmdir(const char *path) {
401 char fn[PATH_MAX];
402 int r;
403 struct stat st;
404 ne_session *session;
406 path = path_cvt(path);
408 if (debug)
409 fprintf(stderr, "rmdir(%s)\n", path);
411 if (!(session = session_get(1)))
412 return -EIO;
414 if ((r = get_stat(path, &st)) < 0)
415 return r;
417 if (!S_ISDIR(st.st_mode))
418 return -ENOTDIR;
420 snprintf(fn, sizeof(fn), "%s/", path);
422 if (ne_delete(session, fn)) {
423 fprintf(stderr, "DELETE failed: %s\n", ne_get_error(session));
424 return -ENOENT;
425 }
427 stat_cache_invalidate(path);
428 dir_cache_invalidate_parent(path);
430 return 0;
431 }
433 static int dav_mkdir(const char *path, __unused mode_t mode) {
434 char fn[PATH_MAX];
435 ne_session *session;
437 path = path_cvt(path);
439 if (debug)
440 fprintf(stderr, "mkdir(%s)\n", path);
442 if (!(session = session_get(1)))
443 return -EIO;
445 snprintf(fn, sizeof(fn), "%s/", path);
447 if (ne_mkcol(session, fn)) {
448 fprintf(stderr, "MKCOL failed: %s\n", ne_get_error(session));
449 return -ENOENT;
450 }
452 stat_cache_invalidate(path);
453 dir_cache_invalidate_parent(path);
455 return 0;
456 }
458 static int dav_rename(const char *from, const char *to) {
459 ne_session *session;
460 int r = 0;
461 struct stat st;
462 char fn[PATH_MAX], *_from;
464 from = _from = strdup(path_cvt(from));
465 assert(from);
466 to = path_cvt(to);
468 if (debug)
469 fprintf(stderr, "rename(%s, %s)\n", from, to);
471 if (!(session = session_get(1))) {
472 r = -EIO;
473 goto finish;
474 }
476 if ((r = get_stat(from, &st)) < 0)
477 goto finish;
479 if (S_ISDIR(st.st_mode)) {
480 snprintf(fn, sizeof(fn), "%s/", from);
481 from = fn;
482 }
484 if (ne_move(session, 1, from, to)) {
485 fprintf(stderr, "MOVE failed: %s\n", ne_get_error(session));
486 r = -ENOENT;
487 goto finish;
488 }
490 stat_cache_invalidate(from);
491 stat_cache_invalidate(to);
493 dir_cache_invalidate_parent(from);
494 dir_cache_invalidate_parent(to);
496 finish:
498 free(_from);
500 return r;
501 }
503 static int dav_release(const char *path, __unused struct fuse_file_info *info) {
504 void *f = NULL;
505 int r = 0;
506 ne_session *session;
508 path = path_cvt(path);
510 if (debug)
511 fprintf(stderr, "release(%s)\n", path);
513 if (!(session = session_get(1))) {
514 r = -EIO;
515 goto finish;
516 }
518 if (!(f = file_cache_get(path))) {
519 fprintf(stderr, "release() called for closed file\n");
520 r = -EFAULT;
521 goto finish;
522 }
524 if (file_cache_close(f) < 0) {
525 r = -errno;
526 goto finish;
527 }
529 finish:
530 if (f)
531 file_cache_unref(f);
533 return r;
534 }
536 static int dav_fsync(const char *path, __unused int isdatasync, __unused struct fuse_file_info *info) {
537 void *f = NULL;
538 int r = 0;
539 ne_session *session;
541 path = path_cvt(path);
542 if (debug)
543 fprintf(stderr, "fsync(%s)\n", path);
545 if (!(session = session_get(1))) {
546 r = -EIO;
547 goto finish;
548 }
550 if (!(f = file_cache_get(path))) {
551 fprintf(stderr, "fsync() called for closed file\n");
552 r = -EFAULT;
553 goto finish;
554 }
556 if (file_cache_sync(f) < 0) {
557 r = -errno;
558 goto finish;
559 }
561 finish:
563 if (f)
564 file_cache_unref(f);
566 return r;
567 }
569 static int dav_mknod(const char *path, mode_t mode, __unused dev_t rdev) {
570 char tempfile[PATH_MAX];
571 int fd;
572 ne_session *session;
574 path = path_cvt(path);
575 if (debug)
576 fprintf(stderr, "mknod(%s)\n", path);
578 if (!(session = session_get(1)))
579 return -EIO;
581 if (!S_ISREG(mode))
582 return -ENOTSUP;
584 snprintf(tempfile, sizeof(tempfile), "%s/fusedav-empty-XXXXXX", "/tmp");
585 if ((fd = mkstemp(tempfile)) < 0)
586 return -errno;
588 unlink(tempfile);
590 if (ne_put(session, path, fd)) {
591 fprintf(stderr, "mknod:PUT failed: %s\n", ne_get_error(session));
592 close(fd);
593 return -EACCES;
594 }
596 close(fd);
598 stat_cache_invalidate(path);
599 dir_cache_invalidate_parent(path);
601 return 0;
602 }
604 static int dav_open(const char *path, struct fuse_file_info *info) {
605 void *f;
607 if (debug)
608 fprintf(stderr, "open(%s)\n", path);
610 path = path_cvt(path);
612 if (!(f = file_cache_open(path, info->flags)))
613 return -errno;
615 file_cache_unref(f);
617 return 0;
618 }
620 static int dav_read(const char *path, char *buf, size_t size, off_t offset, __unused struct fuse_file_info *info) {
621 void *f = NULL;
622 ssize_t r;
624 path = path_cvt(path);
626 if (debug)
627 fprintf(stderr, "read(%s, %lu+%lu)\n", path, (unsigned long) offset, (unsigned long) size);
629 if (!(f = file_cache_get(path))) {
630 fprintf(stderr, "read() called for closed file\n");
631 r = -EFAULT;
632 goto finish;
633 }
635 if ((r = file_cache_read(f, buf, size, offset)) < 0) {
636 r = -errno;
637 goto finish;
638 }
640 finish:
641 if (f)
642 file_cache_unref(f);
644 return r;
645 }
647 static int dav_write(const char *path, const char *buf, size_t size, off_t offset, __unused struct fuse_file_info *info) {
648 void *f = NULL;
649 ssize_t r;
651 path = path_cvt(path);
653 if (debug)
654 fprintf(stderr, "write(%s, %lu+%lu)\n", path, (unsigned long) offset, (unsigned long) size);
656 if (!(f = file_cache_get(path))) {
657 fprintf(stderr, "write() called for closed file\n");
658 r = -EFAULT;
659 goto finish;
660 }
662 if ((r = file_cache_write(f, buf, size, offset)) < 0) {
663 r = -errno;
664 goto finish;
665 }
667 finish:
668 if (f)
669 file_cache_unref(f);
671 return r;
672 }
675 static int dav_truncate(const char *path, off_t size) {
676 void *f = NULL;
677 int r = 0;
678 ne_session *session;
680 path = path_cvt(path);
682 if (debug)
683 fprintf(stderr, "truncate(%s, %lu)\n", path, (unsigned long) size);
685 if (!(session = session_get(1)))
686 r = -EIO;
687 goto finish;
689 if (!(f = file_cache_get(path))) {
690 fprintf(stderr, "truncate() called for closed file\n");
691 r = -EFAULT;
692 goto finish;
693 }
695 if (file_cache_truncate(f, size) < 0) {
696 r = -errno;
697 goto finish;
698 }
700 finish:
701 if (f)
702 file_cache_unref(f);
704 return r;
705 }
707 static int dav_utime(const char *path, struct utimbuf *buf) {
708 ne_session *session;
709 const ne_propname getlastmodified = { "DAV:", "getlastmodified" };
710 ne_proppatch_operation ops[2];
711 int r = 0;
712 char *date;
714 assert(path);
715 assert(buf);
717 path = path_cvt(path);
719 if (debug)
720 fprintf(stderr, "utime(%s, %lu, %lu)\n", path, (unsigned long) buf->actime, (unsigned long) buf->modtime);
722 ops[0].name = &getlastmodified;
723 ops[0].type = ne_propset;
724 ops[0].value = date = ne_rfc1123_date(buf->modtime);
725 ops[1].name = NULL;
727 if (!(session = session_get(1))) {
728 r = -EIO;
729 goto finish;
730 }
732 if (proppatch_with_redirect(session, path, ops)) {
733 fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
734 r = -ENOTSUP;
735 goto finish;
736 }
738 stat_cache_invalidate(path);
740 finish:
741 free(date);
743 return r;
744 }
746 static const char *fix_xattr(const char *name) {
747 assert(name);
749 if (!strcmp(name, MIME_XATTR))
750 return "user.webdav(DAV:;getcontenttype)";
752 return name;
753 }
755 struct listxattr_info {
756 char *list;
757 size_t space, size;
758 };
760 static int listxattr_iterator(
761 void *userdata,
762 const ne_propname *pname,
763 const char *value,
764 __unused const ne_status *status) {
766 struct listxattr_info *l = userdata;
767 int n;
769 assert(l);
771 if (!value || !pname)
772 return -1;
774 if (l->list) {
775 n = snprintf(l->list, l->space, "user.webdav(%s;%s)", pname->nspace, pname->name) + 1;
777 if (n >= (int) l->space) {
778 l->size += l->space;
779 l->space = 0;
780 return 1;
782 } else {
783 l->size += n;
784 l->space -= n;
786 if (l->list)
787 l->list += n;
789 return 0;
790 }
791 } else {
792 /* Calculate space */
794 l->size += strlen(pname->nspace) + strlen(pname->name) + 15;
795 return 0;
796 }
797 }
799 static void listxattr_propfind_callback(void *userdata, __unused const ne_uri *u, const ne_prop_result_set *results) {
800 struct listxattr_info *l = userdata;
801 ne_propset_iterate(results, listxattr_iterator, l);
802 }
804 static int dav_listxattr(
805 const char *path,
806 char *list,
807 size_t size) {
809 ne_session *session;
810 struct listxattr_info l;
813 assert(path);
815 path = path_cvt(path);
817 if (debug)
818 fprintf(stderr, "listxattr(%s, .., %lu)\n", path, (unsigned long) size);
820 if (list) {
821 l.list = list;
822 l.space = size-1;
823 l.size = 0;
825 if (l.space >= sizeof(MIME_XATTR)) {
826 memcpy(l.list, MIME_XATTR, sizeof(MIME_XATTR));
827 l.list += sizeof(MIME_XATTR);
828 l.space -= sizeof(MIME_XATTR);
829 l.size += sizeof(MIME_XATTR);
830 }
832 } else {
833 l.list = NULL;
834 l.space = 0;
835 l.size = sizeof(MIME_XATTR);
836 }
838 if (!(session = session_get(1)))
839 return -EIO;
841 if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, NULL, listxattr_propfind_callback, &l) != NE_OK) {
842 fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
843 return -EIO;
844 }
846 if (l.list) {
847 assert(l.space > 0);
848 *l.list = 0;
849 }
851 return l.size+1;
852 }
854 struct getxattr_info {
855 ne_propname propname;
856 char *value;
857 size_t space, size;
858 };
860 static int getxattr_iterator(
861 void *userdata,
862 const ne_propname *pname,
863 const char *value,
864 __unused const ne_status *status) {
866 struct getxattr_info *g = userdata;
868 assert(g);
870 if (!value || !pname)
871 return -1;
873 if (strcmp(pname->nspace, g->propname.nspace) ||
874 strcmp(pname->name, g->propname.name))
875 return 0;
877 if (g->value) {
878 size_t l;
880 l = strlen(value);
882 if (l > g->space)
883 l = g->space;
885 memcpy(g->value, value, l);
886 g->size = l;
887 } else {
888 /* Calculate space */
890 g->size = strlen(value);
891 return 0;
892 }
894 return 0;
895 }
897 static void getxattr_propfind_callback(void *userdata, __unused const ne_uri *u, const ne_prop_result_set *results) {
898 struct getxattr_info *g = userdata;
899 ne_propset_iterate(results, getxattr_iterator, g);
900 }
902 static int parse_xattr(const char *name, char *dnspace, size_t dnspace_length, char *dname, size_t dname_length) {
903 char *e;
904 size_t k;
906 assert(name);
907 assert(dnspace);
908 assert(dnspace_length);
909 assert(dname);
910 assert(dname_length);
912 if (strncmp(name, "user.webdav(", 12) ||
913 name[strlen(name)-1] != ')' ||
914 !(e = strchr(name+12, ';')))
915 return -1;
917 if ((k = strcspn(name+12, ";")) > dnspace_length-1)
918 return -1;
920 memcpy(dnspace, name+12, k);
921 dnspace[k] = 0;
923 e++;
925 if ((k = strlen(e)) > dname_length-1)
926 return -1;
928 assert(k > 0);
929 k--;
931 memcpy(dname, e, k);
932 dname[k] = 0;
934 return 0;
935 }
937 static int dav_getxattr(
938 const char *path,
939 const char *name,
940 char *value,
941 size_t size) {
943 ne_session *session;
944 struct getxattr_info g;
945 ne_propname props[2];
946 char dnspace[128], dname[128];
948 assert(path);
950 path = path_cvt(path);
951 name = fix_xattr(name);
953 if (debug)
954 fprintf(stderr, "getxattr(%s, %s, .., %lu)\n", path, name, (unsigned long) size);
956 if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0)
957 return -ENOATTR;
959 props[0].nspace = dnspace;
960 props[0].name = dname;
961 props[1].nspace = NULL;
962 props[1].name = NULL;
964 if (value) {
965 g.value = value;
966 g.space = size;
967 g.size = (size_t) -1;
968 } else {
969 g.value = NULL;
970 g.space = 0;
971 g.size = (size_t) -1;
972 }
974 g.propname = props[0];
976 if (!(session = session_get(1)))
977 return -EIO;
979 if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, props, getxattr_propfind_callback, &g) != NE_OK) {
980 fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
981 return -EIO;
982 }
984 if (g.size == (size_t) -1)
985 return -ENOATTR;
987 return g.size;
988 }
990 static int dav_setxattr(
991 const char *path,
992 const char *name,
993 const char *value,
994 size_t size,
995 int flags) {
997 ne_session *session;
998 ne_propname propname;
999 ne_proppatch_operation ops[2];
1000 int r = 0;
1001 char dnspace[128], dname[128];
1002 char *value_fixed = NULL;
1004 assert(path);
1005 assert(name);
1006 assert(value);
1008 path = path_cvt(path);
1009 name = fix_xattr(name);
1011 if (debug)
1012 fprintf(stderr, "setxattr(%s, %s)\n", path, name);
1014 if (flags) {
1015 r = ENOTSUP;
1016 goto finish;
1017 }
1019 if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0) {
1020 r = -ENOATTR;
1021 goto finish;
1022 }
1024 propname.nspace = dnspace;
1025 propname.name = dname;
1027 /* Add trailing NUL byte if required */
1028 if (!memchr(value, 0, size)) {
1029 value_fixed = malloc(size+1);
1030 assert(value_fixed);
1032 memcpy(value_fixed, value, size);
1033 value_fixed[size] = 0;
1035 value = value_fixed;
1036 }
1038 ops[0].name = &propname;
1039 ops[0].type = ne_propset;
1040 ops[0].value = value;
1042 ops[1].name = NULL;
1044 if (!(session = session_get(1))) {
1045 r = -EIO;
1046 goto finish;
1047 }
1049 if (proppatch_with_redirect(session, path, ops)) {
1050 fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1051 r = -ENOTSUP;
1052 goto finish;
1053 }
1055 stat_cache_invalidate(path);
1057 finish:
1058 free(value_fixed);
1060 return r;
1061 }
1063 static int dav_removexattr(const char *path, const char *name) {
1064 ne_session *session;
1065 ne_propname propname;
1066 ne_proppatch_operation ops[2];
1067 int r = 0;
1068 char dnspace[128], dname[128];
1070 assert(path);
1071 assert(name);
1073 path = path_cvt(path);
1074 name = fix_xattr(name);
1076 if (debug)
1077 fprintf(stderr, "removexattr(%s, %s)\n", path, name);
1079 if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0) {
1080 r = -ENOATTR;
1081 goto finish;
1082 }
1084 propname.nspace = dnspace;
1085 propname.name = dname;
1087 ops[0].name = &propname;
1088 ops[0].type = ne_propremove;
1089 ops[0].value = NULL;
1091 ops[1].name = NULL;
1093 if (!(session = session_get(1))) {
1094 r = -EIO;
1095 goto finish;
1096 }
1098 if (proppatch_with_redirect(session, path, ops)) {
1099 fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1100 r = -ENOTSUP;
1101 goto finish;
1102 }
1104 stat_cache_invalidate(path);
1106 finish:
1108 return r;
1109 }
1111 static int dav_chmod(const char *path, mode_t mode) {
1112 ne_session *session;
1113 const ne_propname executable = { "http://apache.org/dav/props/", "executable" };
1114 ne_proppatch_operation ops[2];
1115 int r = 0;
1117 assert(path);
1119 path = path_cvt(path);
1121 if (debug)
1122 fprintf(stderr, "chmod(%s, %04o)\n", path, mode);
1124 ops[0].name = &executable;
1125 ops[0].type = ne_propset;
1126 ops[0].value = mode & 0111 ? "T" : "F";
1127 ops[1].name = NULL;
1129 if (!(session = session_get(1))) {
1130 r = -EIO;
1131 goto finish;
1132 }
1134 if (proppatch_with_redirect(session, path, ops)) {
1135 fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1136 r = -ENOTSUP;
1137 goto finish;
1138 }
1140 stat_cache_invalidate(path);
1142 finish:
1144 return r;
1145 }
1147 static struct fuse_operations dav_oper = {
1148 .getattr = dav_getattr,
1149 .readdir = dav_readdir,
1150 .mknod = dav_mknod,
1151 .mkdir = dav_mkdir,
1152 .unlink = dav_unlink,
1153 .rmdir = dav_rmdir,
1154 .rename = dav_rename,
1155 .chmod = dav_chmod,
1156 .truncate = dav_truncate,
1157 .utime = dav_utime,
1158 .open = dav_open,
1159 .read = dav_read,
1160 .write = dav_write,
1161 .release = dav_release,
1162 .fsync = dav_fsync,
1163 .setxattr = dav_setxattr,
1164 .getxattr = dav_getxattr,
1165 .listxattr = dav_listxattr,
1166 .removexattr = dav_removexattr,
1167 };
1169 static void usage(char *argv0) {
1170 char *e;
1172 if ((e = strrchr(argv0, '/')))
1173 e++;
1174 else
1175 e = argv0;
1177 fprintf(stderr,
1178 "%s [-hDL] [-t SECS] [-u USERNAME] [-p PASSWORD] [-o OPTIONS] URL MOUNTPOINT\n"
1179 "\t-h Show this help\n"
1180 "\t-D Enable debug mode\n"
1181 "\t-u Username if required\n"
1182 "\t-p Password if required\n"
1183 "\t-o Additional FUSE mount options\n"
1184 "\t-L Locking the repository during mount\n"
1185 "\t-t Set lock timeout\n",
1186 e);
1187 }
1189 static void exit_handler(__unused int sig) {
1190 static const char m[] = "*** Caught signal ***\n";
1191 if(fuse != NULL)
1192 fuse_exit(fuse);
1193 write(2, m, strlen(m));
1194 }
1196 static void empty_handler(__unused int sig) {}
1198 static int setup_signal_handlers(void) {
1199 struct sigaction sa;
1200 sigset_t m;
1202 sa.sa_handler = exit_handler;
1203 sigemptyset(&(sa.sa_mask));
1204 sa.sa_flags = 0;
1206 if (sigaction(SIGHUP, &sa, NULL) == -1 ||
1207 sigaction(SIGINT, &sa, NULL) == -1 ||
1208 sigaction(SIGTERM, &sa, NULL) == -1) {
1210 fprintf(stderr, "Cannot set exit signal handlers: %s\n", strerror(errno));
1211 return -1;
1212 }
1214 sa.sa_handler = SIG_IGN;
1216 if (sigaction(SIGPIPE, &sa, NULL) == -1) {
1217 fprintf(stderr, "Cannot set ignored signals: %s\n", strerror(errno));
1218 return -1;
1219 }
1221 /* Used to shut down the locking thread */
1222 sa.sa_handler = empty_handler;
1224 if (sigaction(SIGUSR1, &sa, NULL) == -1) {
1225 fprintf(stderr, "Cannot set user signals: %s\n", strerror(errno));
1226 return -1;
1227 }
1229 sigemptyset(&m);
1230 pthread_sigmask(SIG_BLOCK, &m, &m);
1231 sigdelset(&m, SIGHUP);
1232 sigdelset(&m, SIGINT);
1233 sigdelset(&m, SIGTERM);
1234 sigaddset(&m, SIGPIPE);
1235 sigaddset(&m, SIGUSR1);
1236 pthread_sigmask(SIG_SETMASK, &m, NULL);
1238 return 0;
1239 }
1241 static int create_lock(void) {
1242 ne_session *session;
1243 char _owner[64], *owner;
1244 int i;
1245 int ret;
1247 lock = ne_lock_create();
1248 assert(lock);
1250 if (!(session = session_get(0)))
1251 return -1;
1253 if (!(owner = username))
1254 if (!(owner = getenv("USER")))
1255 if (!(owner = getenv("LOGNAME"))) {
1256 snprintf(_owner, sizeof(_owner), "%lu", (unsigned long) getuid());
1257 owner = owner;
1258 }
1260 ne_fill_server_uri(session, &lock->uri);
1262 lock->uri.path = strdup(base_directory);
1263 lock->depth = NE_DEPTH_INFINITE;
1264 lock->timeout = lock_timeout;
1265 lock->owner = strdup(owner);
1267 if (debug)
1268 fprintf(stderr, "Acquiring lock...\n");
1270 for (i = 0; i < MAX_REDIRECTS; i++) {
1271 const ne_uri *u;
1273 if ((ret = ne_lock(session, lock)) != NE_REDIRECT)
1274 break;
1276 if (!(u = ne_redirect_location(session)))
1277 break;
1279 if (!session_is_local(u))
1280 break;
1282 if (debug)
1283 fprintf(stderr, "REDIRECT FROM '%s' to '%s'\n", lock->uri.path, u->path);
1285 free(lock->uri.path);
1286 lock->uri.path = strdup(u->path);
1287 }
1289 if (ret) {
1290 fprintf(stderr, "LOCK failed: %s\n", ne_get_error(session));
1291 ne_lock_destroy(lock);
1292 lock = NULL;
1293 return -1;
1294 }
1296 lock_store = ne_lockstore_create();
1297 assert(lock_store);
1299 ne_lockstore_add(lock_store, lock);
1301 return 0;
1302 }
1304 static int remove_lock(void) {
1305 ne_session *session;
1307 assert(lock);
1309 if (!(session = session_get(0)))
1310 return -1;
1312 if (debug)
1313 fprintf(stderr, "Removing lock...\n");
1315 if (ne_unlock(session, lock)) {
1316 fprintf(stderr, "UNLOCK failed: %s\n", ne_get_error(session));
1317 return -1;
1318 }
1320 return 0;
1321 }
1323 static void *lock_thread_func(__unused void *p) {
1324 ne_session *session;
1325 sigset_t block;
1327 if (debug)
1328 fprintf(stderr, "lock_thread entering\n");
1330 if (!(session = session_get(1)))
1331 return NULL;
1333 sigemptyset(&block);
1334 sigaddset(&block, SIGUSR1);
1336 assert(lock);
1338 while (!lock_thread_exit) {
1339 int r, t;
1341 lock->timeout = lock_timeout;
1343 pthread_sigmask(SIG_BLOCK, &block, NULL);
1344 r = ne_lock_refresh(session, lock);
1345 pthread_sigmask(SIG_UNBLOCK, &block, NULL);
1347 if (r) {
1348 fprintf(stderr, "LOCK refresh failed: %s\n", ne_get_error(session));
1349 break;
1350 }
1352 if (lock_thread_exit)
1353 break;
1355 t = lock_timeout/2;
1356 if (t <= 0)
1357 t = 1;
1358 sleep(t);
1359 }
1361 if (debug)
1362 fprintf(stderr, "lock_thread exiting\n");
1364 return NULL;
1365 }
1367 int main(int argc, char *argv[]) {
1368 int c;
1369 char *u = NULL, *p = NULL, *o = NULL;
1370 int fuse_fd = -1;
1371 int ret = 1;
1372 char mountpoint[PATH_MAX];
1373 pthread_t lock_thread;
1374 int lock_thread_running = 0;
1375 int enable_locking = 0;
1377 static char *mount_args_strings[] = {
1378 NULL, /* path*/
1379 NULL, /* -o */
1380 NULL,
1381 NULL};
1383 struct fuse_args mount_args = {
1384 .argc = 1,
1385 .argv = mount_args_strings,
1386 .allocated = 0
1387 };
1389 if (ne_sock_init()) {
1390 fprintf(stderr, "Failed to initialize libneon.\n");
1391 goto finish;
1392 }
1394 if (!ne_has_support(NE_FEATURE_SSL) ||
1395 !ne_has_support(NE_FEATURE_TS_SSL) ||
1396 !ne_has_support(NE_FEATURE_LFS)) {
1397 fprintf(stderr, "fusedav requires libneon built with SSL, SSL thread safety and LFS enabled.\n");
1398 goto finish;
1399 }
1401 mask = umask(0);
1402 umask(mask);
1404 cache_alloc();
1406 if (setup_signal_handlers() < 0)
1407 goto finish;
1409 while ((c = getopt(argc, argv, "hu:p:Do:Lt:")) != -1) {
1411 switch(c) {
1412 case 'u':
1413 u = optarg;
1414 break;
1416 case 'p':
1417 p = optarg;
1418 break;
1420 case 'D':
1421 debug = !debug;
1422 break;
1424 case 'o':
1425 o = optarg;
1426 break;
1428 case 'L':
1429 enable_locking = 1;
1430 break;
1432 case 't':
1433 if ((lock_timeout = atoi(optarg)) < 0) {
1434 fprintf(stderr, "Invalid lock timeout '%s'\n", optarg);
1435 goto finish;
1436 }
1437 break;
1439 case 'h':
1440 ret = 0;
1442 /* fall through */
1443 default:
1444 usage(argv[0]);
1445 goto finish;
1446 }
1447 }
1449 if (optind != argc-2) {
1450 usage(argv[0]);
1451 goto finish;
1452 }
1454 if (session_set_uri(argv[optind], u, p) < 0) {
1455 usage(argv[0]);
1456 goto finish;
1457 }
1459 if (argv[optind+1][0] == '/')
1460 snprintf(mountpoint, sizeof(mountpoint), "%s", argv[optind+1]);
1461 else {
1462 char *pwd = get_current_dir_name();
1463 snprintf(mountpoint, sizeof(mountpoint), "%s/%s", pwd, argv[optind+1]);
1464 free(pwd);
1465 }
1467 mount_args_strings[0] = argv[optind];
1469 if (o) {
1470 mount_args_strings[1] = (char*) "-o";
1471 mount_args_strings[2] = o;
1472 mount_args.argc += 2;
1473 }
1475 if ((fuse_fd = fuse_mount(mountpoint, &mount_args)) < 0) {
1476 fprintf(stderr, "Failed to mount FUSE file system.\n");
1477 goto finish;
1478 }
1480 if (!(fuse = fuse_new(fuse_fd, &mount_args, &dav_oper, sizeof(dav_oper)))) {
1481 fprintf(stderr, "Failed to create FUSE object.\n");
1482 goto finish;
1483 }
1485 if (enable_locking && create_lock() >= 0) {
1486 int r;
1487 if ((r = pthread_create(&lock_thread, NULL, lock_thread_func, NULL)) < 0) {
1488 fprintf(stderr, "pthread_create(): %s\n", strerror(r));
1489 goto finish;
1490 }
1492 lock_thread_running = 1;
1493 }
1495 fuse_loop_mt(fuse);
1497 if (debug)
1498 fprintf(stderr, "Exiting cleanly.\n");
1500 ret = 0;
1502 finish:
1504 if (lock_thread_running) {
1505 lock_thread_exit = 1;
1506 pthread_kill(lock_thread, SIGUSR1);
1507 pthread_join(lock_thread, NULL);
1508 remove_lock();
1509 ne_lockstore_destroy(lock_store);
1510 }
1512 if (fuse)
1513 fuse_destroy(fuse);
1515 if (fuse_fd >= 0)
1516 fuse_unmount(mountpoint);
1518 file_cache_close_all();
1519 cache_free();
1520 session_free();
1522 return ret;
1523 }