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 }
65 };
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;
73 int lock_timeout = 60;
75 #define MIME_XATTR "user.mime_type"
77 #define MAX_REDIRECTS 10
79 struct fill_info {
80 void *buf;
81 fuse_fill_dir_t filler;
82 const char *root;
83 };
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);
92 }
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;
116 }
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;
144 }
146 return ret;
147 }
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;
172 }
174 return ret;
175 }
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;
204 }
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();
217 }
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;
229 }
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);
252 f->filler(f->buf, h = ne_path_unescape(t), NULL, 0);
253 free(h);
254 }
256 fill_stat(&st, results, is_dir);
257 stat_cache_set(fn, &st);
258 }
260 static void getdir_cache_callback(
261 const char *root,
262 const char *fn,
263 void *user) {
265 struct fill_info *f = user;
266 char path[PATH_MAX];
267 char *h;
269 assert(f);
271 snprintf(path, sizeof(path), "%s/%s", !strcmp(root, "/") ? "" : root, fn);
273 f->filler(f->buf, h = ne_path_unescape(fn), NULL, 0);
274 free(h);
275 }
277 static int dav_readdir(
278 const char *path,
279 void *buf,
280 fuse_fill_dir_t filler,
281 __unused off_t offset,
282 __unused struct fuse_file_info *fi) {
284 struct fill_info f;
285 ne_session *session;
287 path = path_cvt(path);
289 if (debug)
290 fprintf(stderr, "getdir(%s)\n", path);
292 f.buf = buf;
293 f.filler = filler;
294 f.root = path;
296 filler(buf, ".", NULL, 0);
297 filler(buf, "..", NULL, 0);
299 if (dir_cache_enumerate(path, getdir_cache_callback, &f) < 0) {
301 if (debug)
302 fprintf(stderr, "DIR-CACHE-MISS\n");
304 if (!(session = session_get(1)))
305 return -EIO;
307 dir_cache_begin(path);
309 if (simple_propfind_with_redirect(session, path, NE_DEPTH_ONE, query_properties, getdir_propfind_callback, &f) != NE_OK) {
310 dir_cache_finish(path, 2);
311 fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
312 return -ENOENT;
313 }
315 dir_cache_finish(path, 1);
316 }
318 return 0;
319 }
321 static void getattr_propfind_callback(void *userdata, const char *href, const ne_prop_result_set *results) {
322 struct stat *st = (struct stat*) userdata;
323 char fn[PATH_MAX];
324 int is_dir;
326 assert(st);
328 strncpy(fn, href, sizeof(fn));
329 fn[sizeof(fn)-1] = 0;
330 strip_trailing_slash(fn, &is_dir);
332 fill_stat(st, results, is_dir);
333 stat_cache_set(fn, st);
334 }
336 static int get_stat(const char *path, struct stat *stbuf) {
337 ne_session *session;
339 if (!(session = session_get(1)))
340 return -EIO;
342 if (stat_cache_get(path, stbuf) == 0) {
343 return stbuf->st_mode == 0 ? -ENOENT : 0;
344 } else {
345 if (debug)
346 fprintf(stderr, "STAT-CACHE-MISS\n");
348 if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, query_properties, getattr_propfind_callback, stbuf) != NE_OK) {
349 stat_cache_invalidate(path);
350 fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
351 return -ENOENT;
352 }
354 return 0;
355 }
356 }
358 static int dav_getattr(const char *path, struct stat *stbuf) {
359 path = path_cvt(path);
360 if (debug)
361 fprintf(stderr, "getattr(%s)\n", path);
362 return get_stat(path, stbuf);
363 }
365 static int dav_unlink(const char *path) {
366 int r;
367 struct stat st;
368 ne_session *session;
370 path = path_cvt(path);
372 if (debug)
373 fprintf(stderr, "unlink(%s)\n", path);
375 if (!(session = session_get(1)))
376 return -EIO;
378 if ((r = get_stat(path, &st)) < 0)
379 return r;
381 if (!S_ISREG(st.st_mode))
382 return -EISDIR;
384 if (ne_delete(session, path)) {
385 fprintf(stderr, "DELETE failed: %s\n", ne_get_error(session));
386 return -ENOENT;
387 }
389 stat_cache_invalidate(path);
390 dir_cache_invalidate_parent(path);
392 return 0;
393 }
395 static int dav_rmdir(const char *path) {
396 char fn[PATH_MAX];
397 int r;
398 struct stat st;
399 ne_session *session;
401 path = path_cvt(path);
403 if (debug)
404 fprintf(stderr, "rmdir(%s)\n", path);
406 if (!(session = session_get(1)))
407 return -EIO;
409 if ((r = get_stat(path, &st)) < 0)
410 return r;
412 if (!S_ISDIR(st.st_mode))
413 return -ENOTDIR;
415 snprintf(fn, sizeof(fn), "%s/", path);
417 if (ne_delete(session, fn)) {
418 fprintf(stderr, "DELETE failed: %s\n", ne_get_error(session));
419 return -ENOENT;
420 }
422 stat_cache_invalidate(path);
423 dir_cache_invalidate_parent(path);
425 return 0;
426 }
428 static int dav_mkdir(const char *path, __unused mode_t mode) {
429 char fn[PATH_MAX];
430 ne_session *session;
432 path = path_cvt(path);
434 if (debug)
435 fprintf(stderr, "mkdir(%s)\n", path);
437 if (!(session = session_get(1)))
438 return -EIO;
440 snprintf(fn, sizeof(fn), "%s/", path);
442 if (ne_mkcol(session, fn)) {
443 fprintf(stderr, "MKCOL failed: %s\n", ne_get_error(session));
444 return -ENOENT;
445 }
447 stat_cache_invalidate(path);
448 dir_cache_invalidate_parent(path);
450 return 0;
451 }
453 static int dav_rename(const char *from, const char *to) {
454 ne_session *session;
455 int r = 0;
456 struct stat st;
457 char fn[PATH_MAX], *_from;
459 from = _from = strdup(path_cvt(from));
460 assert(from);
461 to = path_cvt(to);
463 if (debug)
464 fprintf(stderr, "rename(%s, %s)\n", from, to);
466 if (!(session = session_get(1))) {
467 r = -EIO;
468 goto finish;
469 }
471 if ((r = get_stat(from, &st)) < 0)
472 goto finish;
474 if (S_ISDIR(st.st_mode)) {
475 snprintf(fn, sizeof(fn), "%s/", from);
476 from = fn;
477 }
479 if (ne_move(session, 1, from, to)) {
480 fprintf(stderr, "MOVE failed: %s\n", ne_get_error(session));
481 r = -ENOENT;
482 goto finish;
483 }
485 stat_cache_invalidate(from);
486 stat_cache_invalidate(to);
488 dir_cache_invalidate_parent(from);
489 dir_cache_invalidate_parent(to);
491 finish:
493 free(_from);
495 return r;
496 }
498 static int dav_release(const char *path, __unused struct fuse_file_info *info) {
499 void *f = NULL;
500 int r = 0;
501 ne_session *session;
503 path = path_cvt(path);
505 if (debug)
506 fprintf(stderr, "release(%s)\n", path);
508 if (!(session = session_get(1))) {
509 r = -EIO;
510 goto finish;
511 }
513 if (!(f = file_cache_get(path))) {
514 fprintf(stderr, "release() called for closed file\n");
515 r = -EFAULT;
516 goto finish;
517 }
519 if (file_cache_close(f) < 0) {
520 r = -errno;
521 goto finish;
522 }
524 finish:
525 if (f)
526 file_cache_unref(f);
528 return r;
529 }
531 static int dav_fsync(const char *path, __unused int isdatasync, __unused struct fuse_file_info *info) {
532 void *f = NULL;
533 int r = 0;
534 ne_session *session;
536 path = path_cvt(path);
537 if (debug)
538 fprintf(stderr, "fsync(%s)\n", path);
540 if (!(session = session_get(1))) {
541 r = -EIO;
542 goto finish;
543 }
545 if (!(f = file_cache_get(path))) {
546 fprintf(stderr, "fsync() called for closed file\n");
547 r = -EFAULT;
548 goto finish;
549 }
551 if (file_cache_sync(f) < 0) {
552 r = -errno;
553 goto finish;
554 }
556 finish:
558 if (f)
559 file_cache_unref(f);
561 return r;
562 }
564 static int dav_mknod(const char *path, mode_t mode, __unused dev_t rdev) {
565 char tempfile[PATH_MAX];
566 int fd;
567 ne_session *session;
569 path = path_cvt(path);
570 if (debug)
571 fprintf(stderr, "mknod(%s)\n", path);
573 if (!(session = session_get(1)))
574 return -EIO;
576 if (!S_ISREG(mode))
577 return -ENOTSUP;
579 snprintf(tempfile, sizeof(tempfile), "%s/fusedav-empty-XXXXXX", "/tmp");
580 if ((fd = mkstemp(tempfile)) < 0)
581 return -errno;
583 unlink(tempfile);
585 if (ne_put(session, path, fd)) {
586 fprintf(stderr, "mknod:PUT failed: %s\n", ne_get_error(session));
587 close(fd);
588 return -EACCES;
589 }
591 close(fd);
593 stat_cache_invalidate(path);
594 dir_cache_invalidate_parent(path);
596 return 0;
597 }
599 static int dav_open(const char *path, struct fuse_file_info *info) {
600 void *f;
602 if (debug)
603 fprintf(stderr, "open(%s)\n", path);
605 path = path_cvt(path);
607 if (!(f = file_cache_open(path, info->flags)))
608 return -errno;
610 file_cache_unref(f);
612 return 0;
613 }
615 static int dav_read(const char *path, char *buf, size_t size, off_t offset, __unused struct fuse_file_info *info) {
616 void *f = NULL;
617 ssize_t r;
619 path = path_cvt(path);
621 if (debug)
622 fprintf(stderr, "read(%s, %lu+%lu)\n", path, (unsigned long) offset, (unsigned long) size);
624 if (!(f = file_cache_get(path))) {
625 fprintf(stderr, "read() called for closed file\n");
626 r = -EFAULT;
627 goto finish;
628 }
630 if ((r = file_cache_read(f, buf, size, offset)) < 0) {
631 r = -errno;
632 goto finish;
633 }
635 finish:
636 if (f)
637 file_cache_unref(f);
639 return r;
640 }
642 static int dav_write(const char *path, const char *buf, size_t size, off_t offset, __unused struct fuse_file_info *info) {
643 void *f = NULL;
644 ssize_t r;
646 path = path_cvt(path);
648 if (debug)
649 fprintf(stderr, "write(%s, %lu+%lu)\n", path, (unsigned long) offset, (unsigned long) size);
651 if (!(f = file_cache_get(path))) {
652 fprintf(stderr, "write() called for closed file\n");
653 r = -EFAULT;
654 goto finish;
655 }
657 if ((r = file_cache_write(f, buf, size, offset)) < 0) {
658 r = -errno;
659 goto finish;
660 }
662 finish:
663 if (f)
664 file_cache_unref(f);
666 return r;
667 }
670 static int dav_truncate(const char *path, off_t size) {
671 void *f = NULL;
672 int r = 0;
673 ne_session *session;
675 path = path_cvt(path);
677 if (debug)
678 fprintf(stderr, "truncate(%s, %lu)\n", path, (unsigned long) size);
680 if (!(session = session_get(1)))
681 r = -EIO;
682 goto finish;
684 if (!(f = file_cache_get(path))) {
685 fprintf(stderr, "truncate() called for closed file\n");
686 r = -EFAULT;
687 goto finish;
688 }
690 if (file_cache_truncate(f, size) < 0) {
691 r = -errno;
692 goto finish;
693 }
695 finish:
696 if (f)
697 file_cache_unref(f);
699 return r;
700 }
702 static int dav_utime(const char *path, struct utimbuf *buf) {
703 ne_session *session;
704 const ne_propname getlastmodified = { "DAV:", "getlastmodified" };
705 ne_proppatch_operation ops[2];
706 int r = 0;
707 char *date;
709 assert(path);
710 assert(buf);
712 path = path_cvt(path);
714 if (debug)
715 fprintf(stderr, "utime(%s, %lu, %lu)\n", path, (unsigned long) buf->actime, (unsigned long) buf->modtime);
717 ops[0].name = &getlastmodified;
718 ops[0].type = ne_propset;
719 ops[0].value = date = ne_rfc1123_date(buf->modtime);
720 ops[1].name = NULL;
722 if (!(session = session_get(1))) {
723 r = -EIO;
724 goto finish;
725 }
727 if (proppatch_with_redirect(session, path, ops)) {
728 fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
729 r = -ENOTSUP;
730 goto finish;
731 }
733 stat_cache_invalidate(path);
735 finish:
736 free(date);
738 return r;
739 }
741 static const char *fix_xattr(const char *name) {
742 assert(name);
744 if (!strcmp(name, MIME_XATTR))
745 return "user.webdav(DAV:;getcontenttype)";
747 return name;
748 }
750 struct listxattr_info {
751 char *list;
752 size_t space, size;
753 };
755 static int listxattr_iterator(
756 void *userdata,
757 const ne_propname *pname,
758 const char *value,
759 __unused const ne_status *status) {
761 struct listxattr_info *l = userdata;
762 int n;
764 assert(l);
766 if (!value || !pname)
767 return -1;
769 if (l->list) {
770 n = snprintf(l->list, l->space, "user.webdav(%s;%s)", pname->nspace, pname->name) + 1;
772 if (n >= (int) l->space) {
773 l->size += l->space;
774 l->space = 0;
775 return 1;
777 } else {
778 l->size += n;
779 l->space -= n;
781 if (l->list)
782 l->list += n;
784 return 0;
785 }
786 } else {
787 /* Calculate space */
789 l->size += strlen(pname->nspace) + strlen(pname->name) + 15;
790 return 0;
791 }
792 }
794 static void listxattr_propfind_callback(void *userdata, __unused const char *href, const ne_prop_result_set *results) {
795 struct listxattr_info *l = userdata;
796 ne_propset_iterate(results, listxattr_iterator, l);
797 }
799 static int dav_listxattr(
800 const char *path,
801 char *list,
802 size_t size) {
804 ne_session *session;
805 struct listxattr_info l;
808 assert(path);
810 path = path_cvt(path);
812 if (debug)
813 fprintf(stderr, "listxattr(%s, .., %lu)\n", path, (unsigned long) size);
815 if (list) {
816 l.list = list;
817 l.space = size-1;
818 l.size = 0;
820 if (l.space >= sizeof(MIME_XATTR)) {
821 memcpy(l.list, MIME_XATTR, sizeof(MIME_XATTR));
822 l.list += sizeof(MIME_XATTR);
823 l.space -= sizeof(MIME_XATTR);
824 l.size += sizeof(MIME_XATTR);
825 }
827 } else {
828 l.list = NULL;
829 l.space = 0;
830 l.size = sizeof(MIME_XATTR);
831 }
833 if (!(session = session_get(1)))
834 return -EIO;
836 if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, NULL, listxattr_propfind_callback, &l) != NE_OK) {
837 fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
838 return -EIO;
839 }
841 if (l.list) {
842 assert(l.space > 0);
843 *l.list = 0;
844 }
846 return l.size+1;
847 }
849 struct getxattr_info {
850 ne_propname propname;
851 char *value;
852 size_t space, size;
853 };
855 static int getxattr_iterator(
856 void *userdata,
857 const ne_propname *pname,
858 const char *value,
859 __unused const ne_status *status) {
861 struct getxattr_info *g = userdata;
863 assert(g);
865 if (!value || !pname)
866 return -1;
868 if (strcmp(pname->nspace, g->propname.nspace) ||
869 strcmp(pname->name, g->propname.name))
870 return 0;
872 if (g->value) {
873 size_t l;
875 l = strlen(value);
877 if (l > g->space)
878 l = g->space;
880 memcpy(g->value, value, l);
881 g->size = l;
882 } else {
883 /* Calculate space */
885 g->size = strlen(value);
886 return 0;
887 }
889 return 0;
890 }
892 static void getxattr_propfind_callback(void *userdata, __unused const char *href, const ne_prop_result_set *results) {
893 struct getxattr_info *g = userdata;
894 ne_propset_iterate(results, getxattr_iterator, g);
895 }
897 static int parse_xattr(const char *name, char *dnspace, size_t dnspace_length, char *dname, size_t dname_length) {
898 char *e;
899 size_t k;
901 assert(name);
902 assert(dnspace);
903 assert(dnspace_length);
904 assert(dname);
905 assert(dname_length);
907 if (strncmp(name, "user.webdav(", 12) ||
908 name[strlen(name)-1] != ')' ||
909 !(e = strchr(name+12, ';')))
910 return -1;
912 if ((k = strcspn(name+12, ";")) > dnspace_length-1)
913 return -1;
915 memcpy(dnspace, name+12, k);
916 dnspace[k] = 0;
918 e++;
920 if ((k = strlen(e)) > dname_length-1)
921 return -1;
923 assert(k > 0);
924 k--;
926 memcpy(dname, e, k);
927 dname[k] = 0;
929 return 0;
930 }
932 static int dav_getxattr(
933 const char *path,
934 const char *name,
935 char *value,
936 size_t size) {
938 ne_session *session;
939 struct getxattr_info g;
940 ne_propname props[2];
941 char dnspace[128], dname[128];
943 assert(path);
945 path = path_cvt(path);
946 name = fix_xattr(name);
948 if (debug)
949 fprintf(stderr, "getxattr(%s, %s, .., %lu)\n", path, name, (unsigned long) size);
951 if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0)
952 return -ENOATTR;
954 props[0].nspace = dnspace;
955 props[0].name = dname;
956 props[1].nspace = NULL;
957 props[1].name = NULL;
959 if (value) {
960 g.value = value;
961 g.space = size;
962 g.size = (size_t) -1;
963 } else {
964 g.value = NULL;
965 g.space = 0;
966 g.size = (size_t) -1;
967 }
969 g.propname = props[0];
971 if (!(session = session_get(1)))
972 return -EIO;
974 if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, props, getxattr_propfind_callback, &g) != NE_OK) {
975 fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
976 return -EIO;
977 }
979 if (g.size == (size_t) -1)
980 return -ENOATTR;
982 return g.size;
983 }
985 static int dav_setxattr(
986 const char *path,
987 const char *name,
988 const char *value,
989 size_t size,
990 int flags) {
992 ne_session *session;
993 ne_propname propname;
994 ne_proppatch_operation ops[2];
995 int r = 0;
996 char dnspace[128], dname[128];
997 char *value_fixed = NULL;
999 assert(path);
1000 assert(name);
1001 assert(value);
1003 path = path_cvt(path);
1004 name = fix_xattr(name);
1006 if (debug)
1007 fprintf(stderr, "setxattr(%s, %s)\n", path, name);
1009 if (flags) {
1010 r = ENOTSUP;
1011 goto finish;
1012 }
1014 if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0) {
1015 r = -ENOATTR;
1016 goto finish;
1017 }
1019 propname.nspace = dnspace;
1020 propname.name = dname;
1022 /* Add trailing NUL byte if required */
1023 if (!memchr(value, 0, size)) {
1024 value_fixed = malloc(size+1);
1025 assert(value_fixed);
1027 memcpy(value_fixed, value, size);
1028 value_fixed[size] = 0;
1030 value = value_fixed;
1031 }
1033 ops[0].name = &propname;
1034 ops[0].type = ne_propset;
1035 ops[0].value = value;
1037 ops[1].name = NULL;
1039 if (!(session = session_get(1))) {
1040 r = -EIO;
1041 goto finish;
1042 }
1044 if (proppatch_with_redirect(session, path, ops)) {
1045 fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1046 r = -ENOTSUP;
1047 goto finish;
1048 }
1050 stat_cache_invalidate(path);
1052 finish:
1053 free(value_fixed);
1055 return r;
1056 }
1058 static int dav_removexattr(const char *path, const char *name) {
1059 ne_session *session;
1060 ne_propname propname;
1061 ne_proppatch_operation ops[2];
1062 int r = 0;
1063 char dnspace[128], dname[128];
1065 assert(path);
1066 assert(name);
1068 path = path_cvt(path);
1069 name = fix_xattr(name);
1071 if (debug)
1072 fprintf(stderr, "removexattr(%s, %s)\n", path, name);
1074 if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0) {
1075 r = -ENOATTR;
1076 goto finish;
1077 }
1079 propname.nspace = dnspace;
1080 propname.name = dname;
1082 ops[0].name = &propname;
1083 ops[0].type = ne_propremove;
1084 ops[0].value = NULL;
1086 ops[1].name = NULL;
1088 if (!(session = session_get(1))) {
1089 r = -EIO;
1090 goto finish;
1091 }
1093 if (proppatch_with_redirect(session, path, ops)) {
1094 fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1095 r = -ENOTSUP;
1096 goto finish;
1097 }
1099 stat_cache_invalidate(path);
1101 finish:
1103 return r;
1104 }
1106 static int dav_chmod(const char *path, mode_t mode) {
1107 ne_session *session;
1108 const ne_propname executable = { "http://apache.org/dav/props/", "executable" };
1109 ne_proppatch_operation ops[2];
1110 int r = 0;
1112 assert(path);
1114 path = path_cvt(path);
1116 if (debug)
1117 fprintf(stderr, "chmod(%s, %04o)\n", path, mode);
1119 ops[0].name = &executable;
1120 ops[0].type = ne_propset;
1121 ops[0].value = mode & 0111 ? "T" : "F";
1122 ops[1].name = NULL;
1124 if (!(session = session_get(1))) {
1125 r = -EIO;
1126 goto finish;
1127 }
1129 if (proppatch_with_redirect(session, path, ops)) {
1130 fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1131 r = -ENOTSUP;
1132 goto finish;
1133 }
1135 stat_cache_invalidate(path);
1137 finish:
1139 return r;
1140 }
1142 static struct fuse_operations dav_oper = {
1143 .getattr = dav_getattr,
1144 .readdir = dav_readdir,
1145 .mknod = dav_mknod,
1146 .mkdir = dav_mkdir,
1147 .unlink = dav_unlink,
1148 .rmdir = dav_rmdir,
1149 .rename = dav_rename,
1150 .chmod = dav_chmod,
1151 .truncate = dav_truncate,
1152 .utime = dav_utime,
1153 .open = dav_open,
1154 .read = dav_read,
1155 .write = dav_write,
1156 .release = dav_release,
1157 .fsync = dav_fsync,
1158 .setxattr = dav_setxattr,
1159 .getxattr = dav_getxattr,
1160 .listxattr = dav_listxattr,
1161 .removexattr = dav_removexattr,
1162 };
1164 static void usage(char *argv0) {
1165 char *e;
1167 if ((e = strrchr(argv0, '/')))
1168 e++;
1169 else
1170 e = argv0;
1172 fprintf(stderr,
1173 "%s [-hDL] [-t SECS] [-u USERNAME] [-p PASSWORD] [-o OPTIONS] URL MOUNTPOINT\n"
1174 "\t-h Show this help\n"
1175 "\t-D Enable debug mode\n"
1176 "\t-u Username if required\n"
1177 "\t-p Password if required\n"
1178 "\t-o Additional FUSE mount options\n"
1179 "\t-L Locking the repository during mount\n"
1180 "\t-t Set lock timeout\n",
1181 e);
1182 }
1184 static void exit_handler(__unused int sig) {
1185 static const char m[] = "*** Caught signal ***\n";
1186 if(fuse != NULL)
1187 fuse_exit(fuse);
1188 write(2, m, strlen(m));
1189 }
1191 static void empty_handler(__unused int sig) {}
1193 static int setup_signal_handlers(void) {
1194 struct sigaction sa;
1195 sigset_t m;
1197 sa.sa_handler = exit_handler;
1198 sigemptyset(&(sa.sa_mask));
1199 sa.sa_flags = 0;
1201 if (sigaction(SIGHUP, &sa, NULL) == -1 ||
1202 sigaction(SIGINT, &sa, NULL) == -1 ||
1203 sigaction(SIGTERM, &sa, NULL) == -1) {
1205 fprintf(stderr, "Cannot set exit signal handlers: %s\n", strerror(errno));
1206 return -1;
1207 }
1209 sa.sa_handler = SIG_IGN;
1211 if (sigaction(SIGPIPE, &sa, NULL) == -1) {
1212 fprintf(stderr, "Cannot set ignored signals: %s\n", strerror(errno));
1213 return -1;
1214 }
1216 /* Used to shut down the locking thread */
1217 sa.sa_handler = empty_handler;
1219 if (sigaction(SIGUSR1, &sa, NULL) == -1) {
1220 fprintf(stderr, "Cannot set user signals: %s\n", strerror(errno));
1221 return -1;
1222 }
1224 sigemptyset(&m);
1225 pthread_sigmask(SIG_BLOCK, &m, &m);
1226 sigdelset(&m, SIGHUP);
1227 sigdelset(&m, SIGINT);
1228 sigdelset(&m, SIGTERM);
1229 sigaddset(&m, SIGPIPE);
1230 sigaddset(&m, SIGUSR1);
1231 pthread_sigmask(SIG_SETMASK, &m, NULL);
1233 return 0;
1234 }
1236 static int create_lock(void) {
1237 ne_session *session;
1238 char _owner[64], *owner;
1239 int i;
1240 int ret;
1242 lock = ne_lock_create();
1243 assert(lock);
1245 if (!(session = session_get(0)))
1246 return -1;
1248 if (!(owner = username))
1249 if (!(owner = getenv("USER")))
1250 if (!(owner = getenv("LOGNAME"))) {
1251 snprintf(_owner, sizeof(_owner), "%lu", (unsigned long) getuid());
1252 owner = owner;
1253 }
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;
1260 lock->owner = strdup(owner);
1262 if (debug)
1263 fprintf(stderr, "Acquiring lock...\n");
1265 for (i = 0; i < MAX_REDIRECTS; i++) {
1266 const ne_uri *u;
1268 if ((ret = ne_lock(session, lock)) != NE_REDIRECT)
1269 break;
1271 if (!(u = ne_redirect_location(session)))
1272 break;
1274 if (!session_is_local(u))
1275 break;
1277 if (debug)
1278 fprintf(stderr, "REDIRECT FROM '%s' to '%s'\n", lock->uri.path, u->path);
1280 free(lock->uri.path);
1281 lock->uri.path = strdup(u->path);
1282 }
1284 if (ret) {
1285 fprintf(stderr, "LOCK failed: %s\n", ne_get_error(session));
1286 ne_lock_destroy(lock);
1287 lock = NULL;
1288 return -1;
1289 }
1291 lock_store = ne_lockstore_create();
1292 assert(lock_store);
1294 ne_lockstore_add(lock_store, lock);
1296 return 0;
1297 }
1299 static int remove_lock(void) {
1300 ne_session *session;
1302 assert(lock);
1304 if (!(session = session_get(0)))
1305 return -1;
1307 if (debug)
1308 fprintf(stderr, "Removing lock...\n");
1310 if (ne_unlock(session, lock)) {
1311 fprintf(stderr, "UNLOCK failed: %s\n", ne_get_error(session));
1312 return -1;
1313 }
1315 return 0;
1316 }
1318 static void *lock_thread_func(__unused void *p) {
1319 ne_session *session;
1320 sigset_t block;
1322 if (debug)
1323 fprintf(stderr, "lock_thread entering\n");
1325 if (!(session = session_get(1)))
1326 return NULL;
1328 sigemptyset(&block);
1329 sigaddset(&block, SIGUSR1);
1331 assert(lock);
1333 while (!lock_thread_exit) {
1334 int r, t;
1336 lock->timeout = lock_timeout;
1338 pthread_sigmask(SIG_BLOCK, &block, NULL);
1339 r = ne_lock_refresh(session, lock);
1340 pthread_sigmask(SIG_UNBLOCK, &block, NULL);
1342 if (r) {
1343 fprintf(stderr, "LOCK refresh failed: %s\n", ne_get_error(session));
1344 break;
1345 }
1347 if (lock_thread_exit)
1348 break;
1350 t = lock_timeout/2;
1351 if (t <= 0)
1352 t = 1;
1353 sleep(t);
1354 }
1356 if (debug)
1357 fprintf(stderr, "lock_thread exiting\n");
1359 return NULL;
1360 }
1362 int main(int argc, char *argv[]) {
1363 int c;
1364 char *u = NULL, *p = NULL, *o = NULL;
1365 int fuse_fd = -1;
1366 int ret = 1;
1367 char mountpoint[PATH_MAX];
1368 pthread_t lock_thread;
1369 int lock_thread_running = 0;
1370 int enable_locking = 0;
1372 static char *mount_args_strings[] = {
1373 NULL, /* path*/
1374 NULL, /* -o */
1375 NULL,
1376 NULL};
1378 struct fuse_args mount_args = {
1379 .argc = 1,
1380 .argv = mount_args_strings,
1381 .allocated = 0
1382 };
1384 if (ne_sock_init()) {
1385 fprintf(stderr, "Failed to initialize libneon.\n");
1386 goto finish;
1387 }
1389 openssl_thread_setup();
1391 mask = umask(0);
1392 umask(mask);
1394 cache_alloc();
1396 if (setup_signal_handlers() < 0)
1397 goto finish;
1399 while ((c = getopt(argc, argv, "hu:p:Do:Lt:")) != -1) {
1401 switch(c) {
1402 case 'u':
1403 u = optarg;
1404 break;
1406 case 'p':
1407 p = optarg;
1408 break;
1410 case 'D':
1411 debug = !debug;
1412 break;
1414 case 'o':
1415 o = optarg;
1416 break;
1418 case 'L':
1419 enable_locking = 1;
1420 break;
1422 case 't':
1423 if ((lock_timeout = atoi(optarg)) < 0) {
1424 fprintf(stderr, "Invalid lock timeout '%s'\n", optarg);
1425 goto finish;
1426 }
1427 break;
1429 case 'h':
1430 ret = 0;
1432 /* fall through */
1433 default:
1434 usage(argv[0]);
1435 goto finish;
1436 }
1437 }
1439 if (optind != argc-2) {
1440 usage(argv[0]);
1441 goto finish;
1442 }
1444 if (session_set_uri(argv[optind], u, p) < 0) {
1445 usage(argv[0]);
1446 goto finish;
1447 }
1449 if (argv[optind+1][0] == '/')
1450 snprintf(mountpoint, sizeof(mountpoint), "%s", argv[optind+1]);
1451 else {
1452 char *pwd = get_current_dir_name();
1453 snprintf(mountpoint, sizeof(mountpoint), "%s/%s", pwd, argv[optind+1]);
1454 free(pwd);
1455 }
1457 mount_args_strings[0] = argv[optind];
1459 if (o) {
1460 mount_args_strings[1] = (char*) "-o";
1461 mount_args_strings[2] = o;
1462 mount_args.argc += 2;
1463 }
1465 if ((fuse_fd = fuse_mount(mountpoint, &mount_args)) < 0) {
1466 fprintf(stderr, "Failed to mount FUSE file system.\n");
1467 goto finish;
1468 }
1470 if (!(fuse = fuse_new(fuse_fd, &mount_args, &dav_oper, sizeof(dav_oper)))) {
1471 fprintf(stderr, "Failed to create FUSE object.\n");
1472 goto finish;
1473 }
1475 if (enable_locking && create_lock() >= 0) {
1476 int r;
1477 if ((r = pthread_create(&lock_thread, NULL, lock_thread_func, NULL)) < 0) {
1478 fprintf(stderr, "pthread_create(): %s\n", strerror(r));
1479 goto finish;
1480 }
1482 lock_thread_running = 1;
1483 }
1485 fuse_loop_mt(fuse);
1487 if (debug)
1488 fprintf(stderr, "Exiting cleanly.\n");
1490 ret = 0;
1492 finish:
1494 if (lock_thread_running) {
1495 lock_thread_exit = 1;
1496 pthread_kill(lock_thread, SIGUSR1);
1497 pthread_join(lock_thread, NULL);
1498 remove_lock();
1499 ne_lockstore_destroy(lock_store);
1500 }
1502 if (fuse)
1503 fuse_destroy(fuse);
1505 if (fuse_fd >= 0)
1506 fuse_unmount(mountpoint);
1508 file_cache_close_all();
1509 cache_free();
1510 session_free();
1511 openssl_thread_cleanup();
1513 return ret;
1514 }