* deal properly with signals
[fusedav.git] / src / fusedav.c
blobd145e836b2a668a50b9652cf6a8d765b31d307d7
1 /* $Id$ */
3 /***
4 This file is part of fusedav.
6 fusedav is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 fusedav is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 License for more details.
16 You should have received a copy of the GNU General Public License
17 along with fusedav; if not, write to the Free Software Foundation,
18 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 ***/
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <signal.h>
26 #include <pthread.h>
27 #include <time.h>
28 #include <assert.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <dirent.h>
35 #include <errno.h>
36 #include <sys/statfs.h>
37 #include <getopt.h>
38 #include <attr/xattr.h>
40 #include <ne_request.h>
41 #include <ne_basic.h>
42 #include <ne_props.h>
43 #include <ne_utils.h>
44 #include <ne_socket.h>
45 #include <ne_auth.h>
46 #include <ne_dates.h>
47 #include <ne_redirect.h>
48 #include <ne_uri.h>
50 #include <fuse.h>
52 #include "statcache.h"
53 #include "filecache.h"
54 #include "session.h"
55 #include "openssl-thread.h"
56 #include "fusedav.h"
58 const ne_propname query_properties[] = {
59 { "DAV:", "resourcetype" },
60 { "http://apache.org/dav/props/", "executable" },
61 { "DAV:", "getcontentlength" },
62 { "DAV:", "getlastmodified" },
63 { "DAV:", "creationdate" },
64 { NULL, NULL }
67 mode_t mask = 0;
68 int debug = 0;
69 struct fuse* fuse = NULL;
70 ne_lock_store *lock_store = NULL;
71 struct ne_lock *lock = NULL;
72 int lock_thread_exit = 0;
74 #define MIME_XATTR "user.mime_type"
76 #define MAX_REDIRECTS 10
77 #define LOCK_TIMEOUT 8
79 struct fill_info {
80 void *buf;
81 fuse_fill_dir_t filler;
82 const char *root;
85 static int get_stat(const char *path, struct stat *stbuf);
87 static pthread_once_t path_cvt_once = PTHREAD_ONCE_INIT;
88 static pthread_key_t path_cvt_tsd_key;
90 static void path_cvt_tsd_key_init(void) {
91 pthread_key_create(&path_cvt_tsd_key, free);
94 static const char *path_cvt(const char *path) {
95 char *r, *t;
96 int l;
98 pthread_once(&path_cvt_once, path_cvt_tsd_key_init);
100 if ((r = pthread_getspecific(path_cvt_tsd_key)))
101 free(r);
103 t = malloc((l = strlen(base_directory)+strlen(path))+1);
104 assert(t);
105 sprintf(t, "%s%s", base_directory, path);
107 if (l > 1 && t[l-1] == '/')
108 t[l-1] = 0;
110 r = ne_path_escape(t);
111 free(t);
113 pthread_setspecific(path_cvt_tsd_key, r);
115 return r;
118 static int simple_propfind_with_redirect(
119 ne_session *session,
120 const char *path,
121 int depth,
122 const ne_propname *props,
123 ne_props_result results,
124 void *userdata) {
126 int i, ret;
128 for (i = 0; i < MAX_REDIRECTS; i++) {
129 const ne_uri *u;
131 if ((ret = ne_simple_propfind(session, path, depth, props, results, userdata)) != NE_REDIRECT)
132 return ret;
134 if (!(u = ne_redirect_location(session)))
135 break;
137 if (!session_is_local(u))
138 break;
140 if (debug)
141 fprintf(stderr, "REDIRECT FROM '%s' to '%s'\n", path, u->path);
143 path = u->path;
146 return ret;
149 static int proppatch_with_redirect(
150 ne_session *session,
151 const char *path,
152 const ne_proppatch_operation *ops) {
154 int i, ret;
156 for (i = 0; i < MAX_REDIRECTS; i++) {
157 const ne_uri *u;
159 if ((ret = ne_proppatch(session, path, ops)) != NE_REDIRECT)
160 return ret;
162 if (!(u = ne_redirect_location(session)))
163 break;
165 if (!session_is_local(u))
166 break;
168 if (debug)
169 fprintf(stderr, "REDIRECT FROM '%s' to '%s'\n", path, u->path);
171 path = u->path;
174 return ret;
178 static void fill_stat(struct stat* st, const ne_prop_result_set *results, int is_dir) {
179 const char *rt, *e, *gcl, *glm, *cd;
180 const ne_propname resourcetype = { "DAV:", "resourcetype" };
181 const ne_propname executable = { "http://apache.org/dav/props/", "executable" };
182 const ne_propname getcontentlength = { "DAV:", "getcontentlength" };
183 const ne_propname getlastmodified = { "DAV:", "getlastmodified" };
184 const ne_propname creationdate = { "DAV:", "creationdate" };
186 assert(st && results);
188 rt = ne_propset_value(results, &resourcetype);
189 e = ne_propset_value(results, &executable);
190 gcl = ne_propset_value(results, &getcontentlength);
191 glm = ne_propset_value(results, &getlastmodified);
192 cd = ne_propset_value(results, &creationdate);
194 memset(st, 0, sizeof(struct stat));
196 if (is_dir) {
197 st->st_mode = S_IFDIR | 0777;
198 st->st_nlink = 3; /* find will ignore this directory if nlin <= and st_size == 0 */
199 st->st_size = 4096;
200 } else {
201 st->st_mode = S_IFREG | (e && (*e == 'T' || *e == 't') ? 0777 : 0666);
202 st->st_nlink = 1;
203 st->st_size = gcl ? atoll(gcl) : 0;
206 st->st_atime = time(NULL);
207 st->st_mtime = glm ? ne_rfc1123_parse(glm) : 0;
208 st->st_ctime = cd ? ne_iso8601_parse(cd) : 0;
210 st->st_blocks = (st->st_size+511)/512;
211 /*fprintf(stderr, "a: %u; m: %u; c: %u\n", st->st_atime, st->st_mtime, st->st_ctime);*/
213 st->st_mode &= ~mask;
215 st->st_uid = getuid();
216 st->st_gid = getgid();
219 static char *strip_trailing_slash(char *fn, int *is_dir) {
220 size_t l = strlen(fn);
221 assert(fn);
222 assert(is_dir);
223 assert(l > 0);
225 if ((*is_dir = (fn[l-1] == '/')))
226 fn[l-1] = 0;
228 return fn;
231 static void getdir_propfind_callback(void *userdata, const char *href, const ne_prop_result_set *results) {
232 struct fill_info *f = userdata;
233 struct stat st;
234 char fn[PATH_MAX], *t;
235 int is_dir = 0;
237 assert(f);
239 strncpy(fn, href, sizeof(fn));
240 fn[sizeof(fn)-1] = 0;
241 strip_trailing_slash(fn, &is_dir);
243 if (strcmp(fn, f->root) && fn[0]) {
244 char *h;
246 if ((t = strrchr(fn, '/')))
247 t++;
248 else
249 t = fn;
251 dir_cache_add(f->root, t, is_dir);
252 f->filler(f->buf, h = ne_path_unescape(t), NULL, 0);
253 free(h);
256 fill_stat(&st, results, is_dir);
257 stat_cache_set(fn, &st);
260 static void getdir_cache_callback(
261 const char *root,
262 const char *fn,
263 int is_dir,
264 void *user) {
266 struct fill_info *f = user;
267 char path[PATH_MAX];
268 char *h;
270 assert(f);
272 snprintf(path, sizeof(path), "%s/%s", !strcmp(root, "/") ? "" : root, fn);
274 f->filler(f->buf, h = ne_path_unescape(fn), NULL, 0);
275 free(h);
278 static int dav_readdir(
279 const char *path,
280 void *buf,
281 fuse_fill_dir_t filler,
282 off_t offset,
283 struct fuse_file_info *fi) {
285 struct fill_info f;
286 ne_session *session;
288 path = path_cvt(path);
290 if (debug)
291 fprintf(stderr, "getdir(%s)\n", path);
293 f.buf = buf;
294 f.filler = filler;
295 f.root = path;
297 filler(buf, ".", NULL, 0);
298 filler(buf, "..", NULL, 0);
300 if (dir_cache_enumerate(path, getdir_cache_callback, &f) < 0) {
302 if (debug)
303 fprintf(stderr, "DIR-CACHE-MISS\n");
305 if (!(session = session_get(1)))
306 return -EIO;
308 dir_cache_begin(path);
310 if (simple_propfind_with_redirect(session, path, NE_DEPTH_ONE, query_properties, getdir_propfind_callback, &f) != NE_OK) {
311 dir_cache_finish(path, 2);
312 fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
313 return -ENOENT;
316 dir_cache_finish(path, 1);
319 return 0;
322 static void getattr_propfind_callback(void *userdata, const char *href, const ne_prop_result_set *results) {
323 struct stat *st = (struct stat*) userdata;
324 char fn[PATH_MAX];
325 int is_dir;
327 assert(st);
329 strncpy(fn, href, sizeof(fn));
330 fn[sizeof(fn)-1] = 0;
331 strip_trailing_slash(fn, &is_dir);
333 fill_stat(st, results, is_dir);
334 stat_cache_set(fn, st);
337 static int get_stat(const char *path, struct stat *stbuf) {
338 ne_session *session;
340 if (!(session = session_get(1)))
341 return -EIO;
343 if (stat_cache_get(path, stbuf) == 0) {
344 return stbuf->st_mode == 0 ? -ENOENT : 0;
345 } else {
346 if (debug)
347 fprintf(stderr, "STAT-CACHE-MISS\n");
349 if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, query_properties, getattr_propfind_callback, stbuf) != NE_OK) {
350 stat_cache_invalidate(path);
351 fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
352 return -ENOENT;
355 return 0;
359 static int dav_getattr(const char *path, struct stat *stbuf) {
360 path = path_cvt(path);
361 if (debug)
362 fprintf(stderr, "getattr(%s)\n", path);
363 return get_stat(path, stbuf);
366 static int dav_unlink(const char *path) {
367 int r;
368 struct stat st;
369 ne_session *session;
371 path = path_cvt(path);
373 if (debug)
374 fprintf(stderr, "unlink(%s)\n", path);
376 if (!(session = session_get(1)))
377 return -EIO;
379 if ((r = get_stat(path, &st)) < 0)
380 return r;
382 if (!S_ISREG(st.st_mode))
383 return -EISDIR;
385 if (ne_delete(session, path)) {
386 fprintf(stderr, "DELETE failed: %s\n", ne_get_error(session));
387 return -ENOENT;
390 stat_cache_invalidate(path);
391 dir_cache_invalidate_parent(path);
393 return 0;
396 static int dav_rmdir(const char *path) {
397 char fn[PATH_MAX];
398 int r;
399 struct stat st;
400 ne_session *session;
402 path = path_cvt(path);
404 if (debug)
405 fprintf(stderr, "rmdir(%s)\n", path);
407 if (!(session = session_get(1)))
408 return -EIO;
410 if ((r = get_stat(path, &st)) < 0)
411 return r;
413 if (!S_ISDIR(st.st_mode))
414 return -ENOTDIR;
416 snprintf(fn, sizeof(fn), "%s/", path);
418 if (ne_delete(session, fn)) {
419 fprintf(stderr, "DELETE failed: %s\n", ne_get_error(session));
420 return -ENOENT;
423 stat_cache_invalidate(path);
424 dir_cache_invalidate_parent(path);
426 return 0;
429 static int dav_mkdir(const char *path, mode_t mode) {
430 char fn[PATH_MAX];
431 ne_session *session;
433 path = path_cvt(path);
435 if (debug)
436 fprintf(stderr, "mkdir(%s)\n", path);
438 if (!(session = session_get(1)))
439 return -EIO;
441 snprintf(fn, sizeof(fn), "%s/", path);
443 if (ne_mkcol(session, fn)) {
444 fprintf(stderr, "MKCOL failed: %s\n", ne_get_error(session));
445 return -ENOENT;
448 stat_cache_invalidate(path);
449 dir_cache_invalidate_parent(path);
451 return 0;
454 static int dav_rename(const char *from, const char *to) {
455 ne_session *session;
456 int r = 0;
457 struct stat st;
458 char fn[PATH_MAX], *_from;
460 from = _from = strdup(path_cvt(from));
461 assert(from);
462 to = path_cvt(to);
464 if (debug)
465 fprintf(stderr, "rename(%s, %s)\n", from, to);
467 if (!(session = session_get(1))) {
468 r = -EIO;
469 goto finish;
472 if ((r = get_stat(from, &st)) < 0)
473 goto finish;
475 if (S_ISDIR(st.st_mode)) {
476 snprintf(fn, sizeof(fn), "%s/", from);
477 from = fn;
480 if (ne_move(session, 1, from, to)) {
481 fprintf(stderr, "MOVE failed: %s\n", ne_get_error(session));
482 r = -ENOENT;
483 goto finish;
486 stat_cache_invalidate(from);
487 stat_cache_invalidate(to);
489 dir_cache_invalidate_parent(from);
490 dir_cache_invalidate_parent(to);
492 finish:
494 free(_from);
496 return r;
499 static int dav_release(const char *path, struct fuse_file_info *info) {
500 void *f = NULL;
501 int r = 0;
502 ne_session *session;
504 path = path_cvt(path);
506 if (debug)
507 fprintf(stderr, "release(%s)\n", path);
509 if (!(session = session_get(1))) {
510 r = -EIO;
511 goto finish;
514 if (!(f = file_cache_get(path))) {
515 fprintf(stderr, "release() called for closed file\n");
516 r = -EFAULT;
517 goto finish;
520 if (file_cache_close(f) < 0) {
521 r = -errno;
522 goto finish;
525 finish:
526 if (f)
527 file_cache_unref(f);
529 return r;
532 static int dav_fsync(const char *path, __unused int isdatasync, struct fuse_file_info *info) {
533 void *f = NULL;
534 int r = 0;
535 ne_session *session;
537 path = path_cvt(path);
538 if (debug)
539 fprintf(stderr, "fsync(%s)\n", path);
541 if (!(session = session_get(1))) {
542 r = -EIO;
543 goto finish;
546 if (!(f = file_cache_get(path))) {
547 fprintf(stderr, "fsync() called for closed file\n");
548 r = -EFAULT;
549 goto finish;
552 if (file_cache_sync(f) < 0) {
553 r = -errno;
554 goto finish;
557 finish:
559 if (f)
560 file_cache_unref(f);
562 return r;
565 static int dav_mknod(const char *path, mode_t mode, dev_t rdev) {
566 char tempfile[PATH_MAX];
567 int fd;
568 ne_session *session;
570 path = path_cvt(path);
571 if (debug)
572 fprintf(stderr, "mknod(%s)\n", path);
574 if (!(session = session_get(1)))
575 return -EIO;
577 if (!S_ISREG(mode))
578 return -ENOTSUP;
580 snprintf(tempfile, sizeof(tempfile), "%s/fusedav-empty-XXXXXX", "/tmp");
581 if ((fd = mkstemp(tempfile)) < 0)
582 return -errno;
584 unlink(tempfile);
586 if (ne_put(session, path, fd)) {
587 fprintf(stderr, "mknod:PUT failed: %s\n", ne_get_error(session));
588 close(fd);
589 return -EACCES;
592 close(fd);
594 stat_cache_invalidate(path);
595 dir_cache_invalidate_parent(path);
597 return 0;
600 static int dav_open(const char *path, struct fuse_file_info *info) {
601 void *f;
603 if (debug)
604 fprintf(stderr, "open(%s)\n", path);
606 path = path_cvt(path);
608 if (!(f = file_cache_open(path, info->flags)))
609 return -errno;
611 file_cache_unref(f);
613 return 0;
616 static int dav_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *info) {
617 void *f = NULL;
618 ssize_t r;
620 path = path_cvt(path);
622 if (debug)
623 fprintf(stderr, "read(%s, %lu+%lu)\n", path, (unsigned long) offset, (unsigned long) size);
625 if (!(f = file_cache_get(path))) {
626 fprintf(stderr, "read() called for closed file\n");
627 r = -EFAULT;
628 goto finish;
631 if ((r = file_cache_read(f, buf, size, offset)) < 0) {
632 r = -errno;
633 goto finish;
636 fprintf(stderr, "read: %i\n", r);
638 finish:
639 if (f)
640 file_cache_unref(f);
642 return r;
645 static int dav_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *info) {
646 void *f = NULL;
647 ssize_t r;
649 path = path_cvt(path);
651 if (debug)
652 fprintf(stderr, "write(%s, %lu+%lu)\n", path, (unsigned long) offset, (unsigned long) size);
654 if (!(f = file_cache_get(path))) {
655 fprintf(stderr, "write() called for closed file\n");
656 r = -EFAULT;
657 goto finish;
660 if ((r = file_cache_write(f, buf, size, offset)) < 0) {
661 r = -errno;
662 goto finish;
665 finish:
666 if (f)
667 file_cache_unref(f);
669 return r;
673 static int dav_truncate(const char *path, off_t size) {
674 void *f = NULL;
675 int r = 0;
676 ne_session *session;
678 path = path_cvt(path);
680 if (debug)
681 fprintf(stderr, "truncate(%s, %lu)\n", path, (unsigned long) size);
683 if (!(session = session_get(1)))
684 r = -EIO;
685 goto finish;
687 if (!(f = file_cache_get(path))) {
688 fprintf(stderr, "truncate() called for closed file\n");
689 r = -EFAULT;
690 goto finish;
693 if (file_cache_truncate(f, size) < 0) {
694 r = -errno;
695 goto finish;
698 finish:
699 if (f)
700 file_cache_unref(f);
702 return r;
705 static int dav_utime(const char *path, struct utimbuf *buf) {
706 ne_session *session;
707 const ne_propname getlastmodified = { "DAV:", "getlastmodified" };
708 ne_proppatch_operation ops[2];
709 int r = 0;
710 char *date;
712 assert(path);
713 assert(buf);
715 path = path_cvt(path);
717 if (debug)
718 fprintf(stderr, "utime(%s, %lu, %lu)\n", path, (unsigned long) buf->actime, (unsigned long) buf->modtime);
720 ops[0].name = &getlastmodified;
721 ops[0].type = ne_propset;
722 ops[0].value = date = ne_rfc1123_date(buf->modtime);
723 ops[1].name = NULL;
725 if (!(session = session_get(1))) {
726 r = -EIO;
727 goto finish;
730 if (proppatch_with_redirect(session, path, ops)) {
731 fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
732 r = -ENOTSUP;
733 goto finish;
736 stat_cache_invalidate(path);
738 finish:
739 free(date);
741 return r;
744 static const char *fix_xattr(const char *name) {
745 assert(name);
747 if (!strcmp(name, MIME_XATTR))
748 return "user.webdav(DAV:;getcontenttype)";
750 return name;
753 struct listxattr_info {
754 char *list;
755 size_t space, size;
758 static int listxattr_iterator(
759 void *userdata,
760 const ne_propname *pname,
761 const char *value,
762 const ne_status *status) {
764 struct listxattr_info *l = userdata;
765 int n;
767 assert(l);
769 if (!value || !pname)
770 return -1;
772 if (l->list) {
773 n = snprintf(l->list, l->space, "user.webdav(%s;%s)", pname->nspace, pname->name) + 1;
775 if (n >= (int) l->space) {
776 l->size += l->space;
777 l->space = 0;
778 return 1;
780 } else {
781 l->size += n;
782 l->space -= n;
784 if (l->list)
785 l->list += n;
787 return 0;
789 } else {
790 /* Calculate space */
792 l->size += strlen(pname->nspace) + strlen(pname->name) + 15;
793 return 0;
797 static void listxattr_propfind_callback(void *userdata, const char *href, const ne_prop_result_set *results) {
798 struct listxattr_info *l = userdata;
799 ne_propset_iterate(results, listxattr_iterator, l);
802 static int dav_listxattr(
803 const char *path,
804 char *list,
805 size_t size) {
807 ne_session *session;
808 struct listxattr_info l;
811 assert(path);
813 path = path_cvt(path);
815 if (debug)
816 fprintf(stderr, "listxattr(%s, .., %lu)\n", path, (unsigned long) size);
818 if (list) {
819 l.list = list;
820 l.space = size-1;
821 l.size = 0;
823 if (l.space >= sizeof(MIME_XATTR)) {
824 memcpy(l.list, MIME_XATTR, sizeof(MIME_XATTR));
825 l.list += sizeof(MIME_XATTR);
826 l.space -= sizeof(MIME_XATTR);
827 l.size += sizeof(MIME_XATTR);
830 } else {
831 l.list = NULL;
832 l.space = 0;
833 l.size = sizeof(MIME_XATTR);
836 if (!(session = session_get(1)))
837 return -EIO;
839 if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, NULL, listxattr_propfind_callback, &l) != NE_OK) {
840 fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
841 return -EIO;
844 if (l.list) {
845 assert(l.space > 0);
846 *l.list = 0;
849 return l.size+1;
852 struct getxattr_info {
853 ne_propname propname;
854 char *value;
855 size_t space, size;
858 static int getxattr_iterator(
859 void *userdata,
860 const ne_propname *pname,
861 const char *value,
862 const ne_status *status) {
864 struct getxattr_info *g = userdata;
866 assert(g);
868 if (!value || !pname)
869 return -1;
871 if (strcmp(pname->nspace, g->propname.nspace) ||
872 strcmp(pname->name, g->propname.name))
873 return 0;
875 if (g->value) {
876 size_t l;
878 l = strlen(value);
880 if (l > g->space)
881 l = g->space;
883 memcpy(g->value, value, l);
884 g->size = l;
885 } else {
886 /* Calculate space */
888 g->size = strlen(value);
889 return 0;
892 return 0;
895 static void getxattr_propfind_callback(void *userdata, const char *href, const ne_prop_result_set *results) {
896 struct getxattr_info *g = userdata;
897 ne_propset_iterate(results, getxattr_iterator, g);
900 static int parse_xattr(const char *name, char *dnspace, size_t dnspace_length, char *dname, size_t dname_length) {
901 char *e;
902 size_t k;
904 assert(name);
905 assert(dnspace);
906 assert(dnspace_length);
907 assert(dname);
908 assert(dname_length);
910 if (strncmp(name, "user.webdav(", 12) ||
911 name[strlen(name)-1] != ')' ||
912 !(e = strchr(name+12, ';')))
913 return -1;
915 if ((k = strcspn(name+12, ";")) > dnspace_length-1)
916 return -1;
918 memcpy(dnspace, name+12, k);
919 dnspace[k] = 0;
921 e++;
923 if ((k = strlen(e)) > dname_length-1)
924 return -1;
926 assert(k > 0);
927 k--;
929 memcpy(dname, e, k);
930 dname[k] = 0;
932 return 0;
935 static int dav_getxattr(
936 const char *path,
937 const char *name,
938 char *value,
939 size_t size) {
941 ne_session *session;
942 struct getxattr_info g;
943 ne_propname props[2];
944 char dnspace[128], dname[128];
946 assert(path);
948 path = path_cvt(path);
949 name = fix_xattr(name);
951 if (debug)
952 fprintf(stderr, "getxattr(%s, %s, .., %lu)\n", path, name, (unsigned long) size);
954 if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0)
955 return -ENOATTR;
957 props[0].nspace = dnspace;
958 props[0].name = dname;
959 props[1].nspace = NULL;
960 props[1].name = NULL;
962 if (value) {
963 g.value = value;
964 g.space = size;
965 g.size = (size_t) -1;
966 } else {
967 g.value = NULL;
968 g.space = 0;
969 g.size = (size_t) -1;
972 g.propname = props[0];
974 if (!(session = session_get(1)))
975 return -EIO;
977 if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, props, getxattr_propfind_callback, &g) != NE_OK) {
978 fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
979 return -EIO;
982 if (g.size == (size_t) -1)
983 return -ENOATTR;
985 return g.size;
988 static int dav_setxattr(
989 const char *path,
990 const char *name,
991 const char *value,
992 size_t size,
993 int flags) {
995 ne_session *session;
996 ne_propname propname;
997 ne_proppatch_operation ops[2];
998 int r = 0;
999 char dnspace[128], dname[128];
1000 char *value_fixed = NULL;
1002 assert(path);
1003 assert(name);
1004 assert(value);
1006 path = path_cvt(path);
1007 name = fix_xattr(name);
1009 if (debug)
1010 fprintf(stderr, "setxattr(%s, %s)\n", path, name);
1012 if (flags) {
1013 r = ENOTSUP;
1014 goto finish;
1017 if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0) {
1018 r = -ENOATTR;
1019 goto finish;
1022 propname.nspace = dnspace;
1023 propname.name = dname;
1025 /* Add trailing NUL byte if required */
1026 if (!memchr(value, 0, size)) {
1027 value_fixed = malloc(size+1);
1028 assert(value_fixed);
1030 memcpy(value_fixed, value, size);
1031 value_fixed[size] = 0;
1033 value = value_fixed;
1036 ops[0].name = &propname;
1037 ops[0].type = ne_propset;
1038 ops[0].value = value;
1040 ops[1].name = NULL;
1042 if (!(session = session_get(1))) {
1043 r = -EIO;
1044 goto finish;
1047 if (proppatch_with_redirect(session, path, ops)) {
1048 fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1049 r = -ENOTSUP;
1050 goto finish;
1053 stat_cache_invalidate(path);
1055 finish:
1056 free(value_fixed);
1058 return r;
1061 static int dav_removexattr(const char *path, const char *name) {
1062 ne_session *session;
1063 ne_propname propname;
1064 ne_proppatch_operation ops[2];
1065 int r = 0;
1066 char dnspace[128], dname[128];
1068 assert(path);
1069 assert(name);
1071 path = path_cvt(path);
1072 name = fix_xattr(name);
1074 if (debug)
1075 fprintf(stderr, "removexattr(%s, %s)\n", path, name);
1077 if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0) {
1078 r = -ENOATTR;
1079 goto finish;
1082 propname.nspace = dnspace;
1083 propname.name = dname;
1085 ops[0].name = &propname;
1086 ops[0].type = ne_propremove;
1087 ops[0].value = NULL;
1089 ops[1].name = NULL;
1091 if (!(session = session_get(1))) {
1092 r = -EIO;
1093 goto finish;
1096 if (proppatch_with_redirect(session, path, ops)) {
1097 fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1098 r = -ENOTSUP;
1099 goto finish;
1102 stat_cache_invalidate(path);
1104 finish:
1106 return r;
1109 static int dav_chmod(const char *path, mode_t mode) {
1110 ne_session *session;
1111 const ne_propname executable = { "http://apache.org/dav/props/", "executable" };
1112 ne_proppatch_operation ops[2];
1113 int r = 0;
1115 assert(path);
1117 path = path_cvt(path);
1119 if (debug)
1120 fprintf(stderr, "chmod(%s, %04o)\n", path, mode);
1122 ops[0].name = &executable;
1123 ops[0].type = ne_propset;
1124 ops[0].value = mode & 0111 ? "T" : "F";
1125 ops[1].name = NULL;
1127 if (!(session = session_get(1))) {
1128 r = -EIO;
1129 goto finish;
1132 if (proppatch_with_redirect(session, path, ops)) {
1133 fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1134 r = -ENOTSUP;
1135 goto finish;
1138 stat_cache_invalidate(path);
1140 finish:
1142 return r;
1145 static struct fuse_operations dav_oper = {
1146 .getattr = dav_getattr,
1147 .readdir = dav_readdir,
1148 .mknod = dav_mknod,
1149 .mkdir = dav_mkdir,
1150 .unlink = dav_unlink,
1151 .rmdir = dav_rmdir,
1152 .rename = dav_rename,
1153 .chmod = dav_chmod,
1154 .truncate = dav_truncate,
1155 .utime = dav_utime,
1156 .open = dav_open,
1157 .read = dav_read,
1158 .write = dav_write,
1159 .release = dav_release,
1160 .fsync = dav_fsync,
1161 .setxattr = dav_setxattr,
1162 .getxattr = dav_getxattr,
1163 .listxattr = dav_listxattr,
1164 .removexattr = dav_removexattr,
1167 static void usage(char *argv0) {
1168 char *e;
1170 if ((e = strrchr(argv0, '/')))
1171 e++;
1172 else
1173 e = argv0;
1175 fprintf(stderr,
1176 "%s [-h] [-D] [-u USERNAME] [-p PASSWORD] [-o OPTIONS] URL MOUNTPOINT\n"
1177 "\t-h Show this help\n"
1178 "\t-D Enable debug mode\n"
1179 "\t-u Username if required\n"
1180 "\t-p Password if required\n"
1181 "\t-o Additional FUSE mount options\n",
1185 static void exit_handler(int s) {
1186 static const char m[] = "*** Caught signal ***\n";
1187 if(fuse != NULL)
1188 fuse_exit(fuse);
1189 write(2, m, strlen(m));
1192 static void empty_handler(int sig) {}
1194 static int setup_signal_handlers(void) {
1195 struct sigaction sa;
1196 sigset_t m;
1198 sa.sa_handler = exit_handler;
1199 sigemptyset(&(sa.sa_mask));
1200 sa.sa_flags = 0;
1202 if (sigaction(SIGHUP, &sa, NULL) == -1 ||
1203 sigaction(SIGINT, &sa, NULL) == -1 ||
1204 sigaction(SIGTERM, &sa, NULL) == -1) {
1206 fprintf(stderr, "Cannot set exit signal handlers: %s\n", strerror(errno));
1207 return -1;
1210 sa.sa_handler = SIG_IGN;
1212 if (sigaction(SIGPIPE, &sa, NULL) == -1) {
1213 fprintf(stderr, "Cannot set ignored signals: %s\n", strerror(errno));
1214 return -1;
1217 /* Used to shut down the locking thread */
1218 sa.sa_handler = empty_handler;
1220 if (sigaction(SIGUSR1, &sa, NULL) == -1) {
1221 fprintf(stderr, "Cannot set user signals: %s\n", strerror(errno));
1222 return -1;
1225 sigemptyset(&m);
1226 pthread_sigmask(SIG_BLOCK, &m, &m);
1227 sigdelset(&m, SIGHUP);
1228 sigdelset(&m, SIGINT);
1229 sigdelset(&m, SIGTERM);
1230 sigaddset(&m, SIGPIPE);
1231 sigaddset(&m, SIGUSR1);
1232 pthread_sigmask(SIG_SETMASK, &m, NULL);
1234 return 0;
1237 static int create_lock(void) {
1238 ne_session *session;
1239 char _owner[64], *owner;
1240 int i;
1241 int ret;
1243 lock = ne_lock_create();
1244 assert(lock);
1246 if (!(session = session_get(0)))
1247 return -1;
1249 if (!(owner = getenv("USER")))
1250 if (!(owner = getenv("LOGNAME"))) {
1251 snprintf(_owner, sizeof(_owner), "%lu", (unsigned long) getuid());
1252 owner = owner;
1255 ne_fill_server_uri(session, &lock->uri);
1257 lock->uri.path = strdup(base_directory);
1258 lock->depth = NE_DEPTH_INFINITE;
1259 lock->timeout = LOCK_TIMEOUT+2;
1260 lock->owner = strdup(owner);
1262 fprintf(stderr, "Acquiring lock...\n");
1264 for (i = 0; i < MAX_REDIRECTS; i++) {
1265 const ne_uri *u;
1267 if ((ret = ne_lock(session, lock)) != NE_REDIRECT)
1268 break;
1270 if (!(u = ne_redirect_location(session)))
1271 break;
1273 if (!session_is_local(u))
1274 break;
1276 if (debug)
1277 fprintf(stderr, "REDIRECT FROM '%s' to '%s'\n", lock->uri.path, u->path);
1279 free(lock->uri.path);
1280 lock->uri.path = strdup(u->path);
1283 if (ret) {
1284 fprintf(stderr, "LOCK failed: %s\n", ne_get_error(session));
1285 ne_lock_destroy(lock);
1286 lock = NULL;
1287 return -1;
1290 lock_store = ne_lockstore_create();
1291 assert(lock_store);
1293 ne_lockstore_add(lock_store, lock);
1295 return 0;
1298 static int remove_lock(void) {
1299 ne_session *session;
1301 assert(lock);
1303 if (!(session = session_get(0)))
1304 return -1;
1306 if (debug)
1307 fprintf(stderr, "Removing lock...\n");
1309 if (ne_unlock(session, lock)) {
1310 fprintf(stderr, "UNLOCK failed: %s\n", ne_get_error(session));
1311 return -1;
1314 return 0;
1317 static void *lock_thread_func(__unused void *p) {
1318 ne_session *session;
1319 sigset_t block;
1321 if (debug)
1322 fprintf(stderr, "lock_thread entering\n");
1324 if (!(session = session_get(1)))
1325 return NULL;
1327 sigemptyset(&block);
1328 sigaddset(&block, SIGUSR1);
1330 assert(lock);
1332 while (!lock_thread_exit) {
1333 int r;
1335 lock->timeout = LOCK_TIMEOUT;
1337 pthread_sigmask(SIG_BLOCK, &block, NULL);
1338 r = ne_lock_refresh(session, lock);
1339 pthread_sigmask(SIG_UNBLOCK, &block, NULL);
1341 if (r) {
1342 fprintf(stderr, "LOCK refresh failed: %s\n", ne_get_error(session));
1343 break;
1346 if (lock_thread_exit)
1347 break;
1349 sleep(LOCK_TIMEOUT);
1352 if (debug)
1353 fprintf(stderr, "lock_thread exiting\n");
1355 return NULL;
1358 int main(int argc, char *argv[]) {
1359 int c;
1360 char *u = NULL, *p = NULL, *o = NULL;
1361 int fuse_fd = -1;
1362 int ret = 1;
1363 char mountpoint[PATH_MAX];
1364 pthread_t lock_thread;
1365 int lock_thread_running = 0;
1366 int enable_locking = 0;
1368 static char *mount_args_strings[] = {
1369 NULL, /* path*/
1370 NULL, /* -o */
1371 NULL,
1372 NULL};
1374 struct fuse_args mount_args = {
1375 .argc = 1,
1376 .argv = mount_args_strings,
1377 .allocated = 0
1380 if (ne_sock_init()) {
1381 fprintf(stderr, "Failed to initialize libneon.\n");
1382 goto finish;
1385 openssl_thread_setup();
1387 mask = umask(0);
1388 umask(mask);
1390 cache_alloc();
1392 if (setup_signal_handlers() < 0)
1393 goto finish;
1395 while ((c = getopt(argc, argv, "hu:p:Do:Lt:")) != -1) {
1397 switch(c) {
1398 case 'u':
1399 u = optarg;
1400 break;
1402 case 'p':
1403 p = optarg;
1404 break;
1406 case 'D':
1407 debug = !debug;
1408 break;
1410 case 'o':
1411 o = optarg;
1412 break;
1414 case 'L':
1415 enable_locking = 1;
1416 break;
1418 case 'h':
1419 default:
1420 usage(argv[0]);
1421 goto finish;
1425 if (optind != argc-2) {
1426 usage(argv[0]);
1427 goto finish;
1430 if (session_set_uri(argv[optind], u, p) < 0) {
1431 usage(argv[0]);
1432 goto finish;
1435 if (argv[optind+1][0] == '/')
1436 snprintf(mountpoint, sizeof(mountpoint), "%s", argv[optind+1]);
1437 else {
1438 char *pwd = get_current_dir_name();
1439 snprintf(mountpoint, sizeof(mountpoint), "%s/%s", pwd, argv[optind+1]);
1440 free(pwd);
1443 mount_args_strings[0] = argv[optind];
1445 if (o) {
1446 mount_args_strings[1] = "-o";
1447 mount_args_strings[2] = o;
1448 mount_args.argc += 2;
1451 if ((fuse_fd = fuse_mount(mountpoint, &mount_args)) < 0) {
1452 fprintf(stderr, "Failed to mount FUSE file system.\n");
1453 goto finish;
1456 if (!(fuse = fuse_new(fuse_fd, &mount_args, &dav_oper, sizeof(dav_oper)))) {
1457 fprintf(stderr, "Failed to create FUSE object.\n");
1458 goto finish;
1461 if (enable_locking && create_lock() >= 0) {
1462 int r;
1463 if ((r = pthread_create(&lock_thread, NULL, lock_thread_func, NULL)) < 0) {
1464 fprintf(stderr, "pthread_create(): %s\n", strerror(r));
1465 goto finish;
1468 lock_thread_running = 1;
1471 fuse_loop_mt(fuse);
1473 if (debug)
1474 fprintf(stderr, "Exiting cleanly.\n");
1476 ret = 0;
1478 finish:
1480 if (lock_thread_running) {
1481 lock_thread_exit = 1;
1482 pthread_kill(lock_thread, SIGUSR1);
1483 pthread_join(lock_thread, NULL);
1484 remove_lock();
1485 ne_lockstore_destroy(lock_store);
1488 if (fuse)
1489 fuse_destroy(fuse);
1491 if (fuse_fd >= 0)
1492 fuse_unmount(mountpoint);
1494 file_cache_close_all();
1495 cache_free();
1496 session_free();
1497 openssl_thread_cleanup();
1499 return ret;