Code

git: remove $Id$ svn cruft
[fusedav.git] / src / fusedav.c
1 /***
2   Copyright (c) 2004-2006 Lennart Poettering
4   Permission is hereby granted, free of charge, to any person
5   obtaining a copy of this software and associated documentation files
6   (the "Software"), to deal in the Software without restriction,
7   including without limitation the rights to use, copy, modify, merge,
8   publish, distribute, sublicense, and/or sell copies of the Software,
9   and to permit persons to whom the Software is furnished to do so,
10   subject to the following conditions:
12   The above copyright notice and this permission notice shall be
13   included in all copies or substantial portions of the Software.
15   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22   SOFTWARE.
23 ***/
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
29 #include <signal.h>
30 #include <pthread.h>
31 #include <time.h>
32 #include <assert.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <dirent.h>
39 #include <errno.h>
40 #include <sys/statfs.h>
41 #include <getopt.h>
42 #include <attr/xattr.h>
44 #include <ne_request.h>
45 #include <ne_basic.h>
46 #include <ne_props.h>
47 #include <ne_utils.h>
48 #include <ne_socket.h>
49 #include <ne_auth.h>
50 #include <ne_dates.h>
51 #include <ne_redirect.h>
52 #include <ne_uri.h>
54 #include <fuse.h>
56 #include "statcache.h"
57 #include "filecache.h"
58 #include "session.h"
59 #include "fusedav.h"
61 const ne_propname query_properties[] = {
62     { "DAV:", "resourcetype" },
63     { "http://apache.org/dav/props/", "executable" },
64     { "DAV:", "getcontentlength" },
65     { "DAV:", "getlastmodified" },
66     { "DAV:", "creationdate" },
67     { NULL, NULL }
68 };
70 mode_t mask = 0;
71 int debug = 0;
72 struct fuse* fuse = NULL;
73 ne_lock_store *lock_store = NULL;
74 struct ne_lock *lock = NULL;
75 int lock_thread_exit = 0;
76 int lock_timeout = 60;
78 #define MIME_XATTR "user.mime_type"
80 #define MAX_REDIRECTS 10
82 struct fill_info {
83     void *buf;
84     fuse_fill_dir_t filler;
85     const char *root;
86 };
88 static int get_stat(const char *path, struct stat *stbuf);
90 static pthread_once_t path_cvt_once = PTHREAD_ONCE_INIT;
91 static pthread_key_t path_cvt_tsd_key;
93 static void path_cvt_tsd_key_init(void) {
94     pthread_key_create(&path_cvt_tsd_key, free);
95 }
97 static const char *path_cvt(const char *path) {
98     char *r, *t;
99     int l;
101     pthread_once(&path_cvt_once, path_cvt_tsd_key_init);
103     if ((r = pthread_getspecific(path_cvt_tsd_key)))
104         free(r);
106     t = malloc((l = strlen(base_directory)+strlen(path))+1);
107     assert(t);
108     sprintf(t, "%s%s", base_directory, path);
110     if (l > 1 && t[l-1] == '/')
111         t[l-1] = 0;
113     r = ne_path_escape(t);
114     free(t);
116     pthread_setspecific(path_cvt_tsd_key, r);
118     return r;
121 static int simple_propfind_with_redirect(
122         ne_session *session,
123         const char *path,
124         int depth,
125         const ne_propname *props,
126         ne_props_result results,
127         void *userdata) {
129     int i, ret;
131     for (i = 0; i < MAX_REDIRECTS; i++) {
132         const ne_uri *u;
134         if ((ret = ne_simple_propfind(session, path, depth, props, results, userdata)) != NE_REDIRECT)
135             return ret;
137         if (!(u = ne_redirect_location(session)))
138             break;
140         if (!session_is_local(u))
141             break;
143         if (debug)
144             fprintf(stderr, "REDIRECT FROM '%s' to '%s'\n", path, u->path);
146         path = u->path;
147     }
149     return ret;
152 static int proppatch_with_redirect(
153         ne_session *session,
154         const char *path,
155         const ne_proppatch_operation *ops) {
157     int i, ret;
159     for (i = 0; i < MAX_REDIRECTS; i++) {
160         const ne_uri *u;
162         if ((ret = ne_proppatch(session, path, ops)) != NE_REDIRECT)
163             return ret;
165         if (!(u = ne_redirect_location(session)))
166             break;
168         if (!session_is_local(u))
169             break;
171         if (debug)
172             fprintf(stderr, "REDIRECT FROM '%s' to '%s'\n", path, u->path);
174         path = u->path;
175     }
177     return ret;
181 static void fill_stat(struct stat* st, const ne_prop_result_set *results, int is_dir) {
182     const char *rt, *e, *gcl, *glm, *cd;
183     const ne_propname resourcetype = { "DAV:", "resourcetype" };
184     const ne_propname executable = { "http://apache.org/dav/props/", "executable" };
185     const ne_propname getcontentlength = { "DAV:", "getcontentlength" };
186     const ne_propname getlastmodified = { "DAV:", "getlastmodified" };
187     const ne_propname creationdate = { "DAV:", "creationdate" };
189     assert(st && results);
191     rt = ne_propset_value(results, &resourcetype);
192     e = ne_propset_value(results, &executable);
193     gcl = ne_propset_value(results, &getcontentlength);
194     glm = ne_propset_value(results, &getlastmodified);
195     cd = ne_propset_value(results, &creationdate);
197     memset(st, 0, sizeof(struct stat));
199     if (is_dir) {
200         st->st_mode = S_IFDIR | 0777;
201         st->st_nlink = 3;            /* find will ignore this directory if nlin <= and st_size == 0 */
202         st->st_size = 4096;
203     } else {
204         st->st_mode = S_IFREG | (e && (*e == 'T' || *e == 't') ? 0777 : 0666);
205         st->st_nlink = 1;
206         st->st_size = gcl ? atoll(gcl) : 0;
207     }
209     st->st_atime = time(NULL);
210     st->st_mtime = glm ? ne_rfc1123_parse(glm) : 0;
211     st->st_ctime = cd ? ne_iso8601_parse(cd) : 0;
213     st->st_blocks = (st->st_size+511)/512;
214     /*fprintf(stderr, "a: %u; m: %u; c: %u\n", st->st_atime, st->st_mtime, st->st_ctime);*/
216     st->st_mode &= ~mask;
218     st->st_uid = getuid();
219     st->st_gid = getgid();
222 static char *strip_trailing_slash(char *fn, int *is_dir) {
223     size_t l = strlen(fn);
224     assert(fn);
225     assert(is_dir);
226     assert(l > 0);
228     if ((*is_dir = (fn[l-1] == '/')))
229         fn[l-1] = 0;
231     return fn;
234 static void getdir_propfind_callback(void *userdata, const ne_uri *u, const ne_prop_result_set *results) {
235     struct fill_info *f = userdata;
236     struct stat st;
237     char fn[PATH_MAX], *t;
238     int is_dir = 0;
240     assert(f);
242     strncpy(fn, u->path, sizeof(fn));
243     fn[sizeof(fn)-1] = 0;
244     strip_trailing_slash(fn, &is_dir);
246     if (strcmp(fn, f->root) && fn[0]) {
247         char *h;
249         if ((t = strrchr(fn, '/')))
250             t++;
251         else
252             t = fn;
254         dir_cache_add(f->root, t);
255         f->filler(f->buf, h = ne_path_unescape(t), NULL, 0);
256         free(h);
257     }
259     fill_stat(&st, results, is_dir);
260     stat_cache_set(fn, &st);
263 static void getdir_cache_callback(
264         const char *root,
265         const char *fn,
266         void *user) {
268     struct fill_info *f = user;
269     char path[PATH_MAX];
270     char *h;
272     assert(f);
274     snprintf(path, sizeof(path), "%s/%s", !strcmp(root, "/") ? "" : root, fn);
276     f->filler(f->buf, h = ne_path_unescape(fn), NULL, 0);
277     free(h);
280 static int dav_readdir(
281         const char *path,
282         void *buf,
283         fuse_fill_dir_t filler,
284         __unused off_t offset,
285         __unused struct fuse_file_info *fi) {
287     struct fill_info f;
288     ne_session *session;
290     path = path_cvt(path);
292     if (debug)
293         fprintf(stderr, "getdir(%s)\n", path);
295     f.buf = buf;
296     f.filler = filler;
297     f.root = path;
299     filler(buf, ".", NULL, 0);
300     filler(buf, "..", NULL, 0);
302     if (dir_cache_enumerate(path, getdir_cache_callback, &f) < 0) {
304         if (debug)
305             fprintf(stderr, "DIR-CACHE-MISS\n");
307         if (!(session = session_get(1)))
308             return -EIO;
310         dir_cache_begin(path);
312         if (simple_propfind_with_redirect(session, path, NE_DEPTH_ONE, query_properties, getdir_propfind_callback, &f) != NE_OK) {
313             dir_cache_finish(path, 2);
314             fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
315             return -ENOENT;
316         }
318         dir_cache_finish(path, 1);
319     }
321     return 0;
324 static void getattr_propfind_callback(void *userdata, const ne_uri *u, const ne_prop_result_set *results) {
325     struct stat *st = (struct stat*) userdata;
326     char fn[PATH_MAX];
327     int is_dir;
329     assert(st);
331     strncpy(fn, u->path, sizeof(fn));
332     fn[sizeof(fn)-1] = 0;
333     strip_trailing_slash(fn, &is_dir);
335     fill_stat(st, results, is_dir);
336     stat_cache_set(fn, st);
339 static int get_stat(const char *path, struct stat *stbuf) {
340     ne_session *session;
342     if (!(session = session_get(1)))
343         return -EIO;
345     if (stat_cache_get(path, stbuf) == 0) {
346         return stbuf->st_mode == 0 ? -ENOENT : 0;
347     } else {
348         if (debug)
349             fprintf(stderr, "STAT-CACHE-MISS\n");
351         if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, query_properties, getattr_propfind_callback, stbuf) != NE_OK) {
352             stat_cache_invalidate(path);
353             fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
354             return -ENOENT;
355         }
357         return 0;
358     }
361 static int dav_getattr(const char *path, struct stat *stbuf) {
362     path = path_cvt(path);
363     if (debug)
364         fprintf(stderr, "getattr(%s)\n", path);
365     return get_stat(path, stbuf);
368 static int dav_unlink(const char *path) {
369     int r;
370     struct stat st;
371     ne_session *session;
373     path = path_cvt(path);
375     if (debug)
376         fprintf(stderr, "unlink(%s)\n", path);
378     if (!(session = session_get(1)))
379         return -EIO;
381     if ((r = get_stat(path, &st)) < 0)
382         return r;
384     if (!S_ISREG(st.st_mode))
385         return -EISDIR;
387     if (ne_delete(session, path)) {
388         fprintf(stderr, "DELETE failed: %s\n", ne_get_error(session));
389         return -ENOENT;
390     }
392     stat_cache_invalidate(path);
393     dir_cache_invalidate_parent(path);
395     return 0;
398 static int dav_rmdir(const char *path) {
399     char fn[PATH_MAX];
400     int r;
401     struct stat st;
402     ne_session *session;
404     path = path_cvt(path);
406     if (debug)
407         fprintf(stderr, "rmdir(%s)\n", path);
409     if (!(session = session_get(1)))
410         return -EIO;
412     if ((r = get_stat(path, &st)) < 0)
413         return r;
415     if (!S_ISDIR(st.st_mode))
416         return -ENOTDIR;
418     snprintf(fn, sizeof(fn), "%s/", path);
420     if (ne_delete(session, fn)) {
421         fprintf(stderr, "DELETE failed: %s\n", ne_get_error(session));
422         return -ENOENT;
423     }
425     stat_cache_invalidate(path);
426     dir_cache_invalidate_parent(path);
428     return 0;
431 static int dav_mkdir(const char *path, __unused mode_t mode) {
432     char fn[PATH_MAX];
433     ne_session *session;
435     path = path_cvt(path);
437     if (debug)
438         fprintf(stderr, "mkdir(%s)\n", path);
440     if (!(session = session_get(1)))
441         return -EIO;
443     snprintf(fn, sizeof(fn), "%s/", path);
445     if (ne_mkcol(session, fn)) {
446         fprintf(stderr, "MKCOL failed: %s\n", ne_get_error(session));
447         return -ENOENT;
448     }
450     stat_cache_invalidate(path);
451     dir_cache_invalidate_parent(path);
453     return 0;
456 static int dav_rename(const char *from, const char *to) {
457     ne_session *session;
458     int r = 0;
459     struct stat st;
460     char fn[PATH_MAX], *_from;
462     from = _from = strdup(path_cvt(from));
463     assert(from);
464     to = path_cvt(to);
466     if (debug)
467         fprintf(stderr, "rename(%s, %s)\n", from, to);
469     if (!(session = session_get(1))) {
470         r = -EIO;
471         goto finish;
472     }
474     if ((r = get_stat(from, &st)) < 0)
475         goto finish;
477     if (S_ISDIR(st.st_mode)) {
478         snprintf(fn, sizeof(fn), "%s/", from);
479         from = fn;
480     }
482     if (ne_move(session, 1, from, to)) {
483         fprintf(stderr, "MOVE failed: %s\n", ne_get_error(session));
484         r = -ENOENT;
485         goto finish;
486     }
488     stat_cache_invalidate(from);
489     stat_cache_invalidate(to);
491     dir_cache_invalidate_parent(from);
492     dir_cache_invalidate_parent(to);
494 finish:
496     free(_from);
498     return r;
501 static int dav_release(const char *path, __unused struct fuse_file_info *info) {
502     void *f = NULL;
503     int r = 0;
504     ne_session *session;
506     path = path_cvt(path);
508     if (debug)
509         fprintf(stderr, "release(%s)\n", path);
511     if (!(session = session_get(1))) {
512         r = -EIO;
513         goto finish;
514     }
516     if (!(f = file_cache_get(path))) {
517         fprintf(stderr, "release() called for closed file\n");
518         r = -EFAULT;
519         goto finish;
520     }
522     if (file_cache_close(f) < 0) {
523         r = -errno;
524         goto finish;
525     }
527 finish:
528     if (f)
529         file_cache_unref(f);
531     return r;
534 static int dav_fsync(const char *path, __unused int isdatasync, __unused struct fuse_file_info *info) {
535     void *f = NULL;
536     int r = 0;
537     ne_session *session;
539     path = path_cvt(path);
540     if (debug)
541         fprintf(stderr, "fsync(%s)\n", path);
543     if (!(session = session_get(1))) {
544         r = -EIO;
545         goto finish;
546     }
548     if (!(f = file_cache_get(path))) {
549         fprintf(stderr, "fsync() called for closed file\n");
550         r = -EFAULT;
551         goto finish;
552     }
554     if (file_cache_sync(f) < 0) {
555         r = -errno;
556         goto finish;
557     }
559 finish:
561     if (f)
562         file_cache_unref(f);
564     return r;
567 static int dav_mknod(const char *path, mode_t mode, __unused dev_t rdev) {
568     char tempfile[PATH_MAX];
569     int fd;
570     ne_session *session;
572     path = path_cvt(path);
573     if (debug)
574         fprintf(stderr, "mknod(%s)\n", path);
576     if (!(session = session_get(1)))
577         return -EIO;
579     if (!S_ISREG(mode))
580         return -ENOTSUP;
582     snprintf(tempfile, sizeof(tempfile), "%s/fusedav-empty-XXXXXX", "/tmp");
583     if ((fd = mkstemp(tempfile)) < 0)
584         return -errno;
586     unlink(tempfile);
588     if (ne_put(session, path, fd)) {
589         fprintf(stderr, "mknod:PUT failed: %s\n", ne_get_error(session));
590         close(fd);
591         return -EACCES;
592     }
594     close(fd);
596     stat_cache_invalidate(path);
597     dir_cache_invalidate_parent(path);
599     return 0;
602 static int dav_open(const char *path, struct fuse_file_info *info) {
603     void *f;
605     if (debug)
606         fprintf(stderr, "open(%s)\n", path);
608     path = path_cvt(path);
610     if (!(f = file_cache_open(path, info->flags)))
611         return -errno;
613     file_cache_unref(f);
615     return 0;
618 static int dav_read(const char *path, char *buf, size_t size, off_t offset, __unused struct fuse_file_info *info) {
619     void *f = NULL;
620     ssize_t r;
622     path = path_cvt(path);
624     if (debug)
625         fprintf(stderr, "read(%s, %lu+%lu)\n", path, (unsigned long) offset, (unsigned long) size);
627     if (!(f = file_cache_get(path))) {
628         fprintf(stderr, "read() called for closed file\n");
629         r = -EFAULT;
630         goto finish;
631     }
633     if ((r = file_cache_read(f, buf, size, offset)) < 0) {
634         r = -errno;
635         goto finish;
636     }
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, __unused 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;
658     }
660     if ((r = file_cache_write(f, buf, size, offset)) < 0) {
661         r = -errno;
662         goto finish;
663     }
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;
691     }
693     if (file_cache_truncate(f, size) < 0) {
694         r = -errno;
695         goto finish;
696     }
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;
728     }
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;
734     }
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;
756 };
758 static int listxattr_iterator(
759         void *userdata,
760         const ne_propname *pname,
761         const char *value,
762         __unused 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;
788         }
789     } else {
790         /* Calculate space */
792         l->size += strlen(pname->nspace) + strlen(pname->name) + 15;
793         return 0;
794     }
797 static void listxattr_propfind_callback(void *userdata, __unused const ne_uri *u, 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);
828         }
830     } else {
831         l.list = NULL;
832         l.space = 0;
833         l.size = sizeof(MIME_XATTR);
834     }
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;
842     }
844     if (l.list) {
845         assert(l.space > 0);
846         *l.list = 0;
847     }
849     return l.size+1;
852 struct getxattr_info {
853     ne_propname propname;
854     char *value;
855     size_t space, size;
856 };
858 static int getxattr_iterator(
859         void *userdata,
860         const ne_propname *pname,
861         const char *value,
862         __unused 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;
890     }
892     return 0;
895 static void getxattr_propfind_callback(void *userdata, __unused const ne_uri *u, 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;
970     }
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;
980     }
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;
1015     }
1017     if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0) {
1018         r = -ENOATTR;
1019         goto finish;
1020     }
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;
1034     }
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;
1045     }
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;
1051     }
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;
1080     }
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;
1094     }
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;
1100     }
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;
1130     }
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;
1136     }
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,
1165 };
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 [-hDL] [-t SECS] [-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"
1182             "\t-L Locking the repository during mount\n"
1183             "\t-t Set lock timeout\n",
1184             e);
1187 static void exit_handler(__unused int sig) {
1188     static const char m[] = "*** Caught signal ***\n";
1189     if(fuse != NULL)
1190         fuse_exit(fuse);
1191     write(2, m, strlen(m));
1194 static void empty_handler(__unused int sig) {}
1196 static int setup_signal_handlers(void) {
1197     struct sigaction sa;
1198     sigset_t m;
1200     sa.sa_handler = exit_handler;
1201     sigemptyset(&(sa.sa_mask));
1202     sa.sa_flags = 0;
1204     if (sigaction(SIGHUP, &sa, NULL) == -1 ||
1205         sigaction(SIGINT, &sa, NULL) == -1 ||
1206         sigaction(SIGTERM, &sa, NULL) == -1) {
1208         fprintf(stderr, "Cannot set exit signal handlers: %s\n", strerror(errno));
1209         return -1;
1210     }
1212     sa.sa_handler = SIG_IGN;
1214     if (sigaction(SIGPIPE, &sa, NULL) == -1) {
1215         fprintf(stderr, "Cannot set ignored signals: %s\n", strerror(errno));
1216         return -1;
1217     }
1219     /* Used to shut down the locking thread */
1220     sa.sa_handler = empty_handler;
1222     if (sigaction(SIGUSR1, &sa, NULL) == -1) {
1223         fprintf(stderr, "Cannot set user signals: %s\n", strerror(errno));
1224         return -1;
1225     }
1227     sigemptyset(&m);
1228     pthread_sigmask(SIG_BLOCK, &m, &m);
1229     sigdelset(&m, SIGHUP);
1230     sigdelset(&m, SIGINT);
1231     sigdelset(&m, SIGTERM);
1232     sigaddset(&m, SIGPIPE);
1233     sigaddset(&m, SIGUSR1);
1234     pthread_sigmask(SIG_SETMASK, &m, NULL);
1236     return 0;
1239 static int create_lock(void) {
1240     ne_session *session;
1241     char _owner[64], *owner;
1242     int i;
1243     int ret;
1245     lock = ne_lock_create();
1246     assert(lock);
1248     if (!(session = session_get(0)))
1249         return -1;
1251     if (!(owner = username))
1252         if (!(owner = getenv("USER")))
1253             if (!(owner = getenv("LOGNAME"))) {
1254                 snprintf(_owner, sizeof(_owner), "%lu", (unsigned long) getuid());
1255                 owner = owner;
1256             }
1258     ne_fill_server_uri(session, &lock->uri);
1260     lock->uri.path = strdup(base_directory);
1261     lock->depth = NE_DEPTH_INFINITE;
1262     lock->timeout = lock_timeout;
1263     lock->owner = strdup(owner);
1265     if (debug)
1266         fprintf(stderr, "Acquiring lock...\n");
1268     for (i = 0; i < MAX_REDIRECTS; i++) {
1269         const ne_uri *u;
1271         if ((ret = ne_lock(session, lock)) != NE_REDIRECT)
1272             break;
1274         if (!(u = ne_redirect_location(session)))
1275             break;
1277         if (!session_is_local(u))
1278             break;
1280         if (debug)
1281             fprintf(stderr, "REDIRECT FROM '%s' to '%s'\n", lock->uri.path, u->path);
1283         free(lock->uri.path);
1284         lock->uri.path = strdup(u->path);
1285     }
1287     if (ret) {
1288         fprintf(stderr, "LOCK failed: %s\n", ne_get_error(session));
1289         ne_lock_destroy(lock);
1290         lock = NULL;
1291         return -1;
1292     }
1294     lock_store = ne_lockstore_create();
1295     assert(lock_store);
1297     ne_lockstore_add(lock_store, lock);
1299     return 0;
1302 static int remove_lock(void) {
1303     ne_session *session;
1305     assert(lock);
1307     if (!(session = session_get(0)))
1308         return -1;
1310     if (debug)
1311         fprintf(stderr, "Removing lock...\n");
1313     if (ne_unlock(session, lock)) {
1314         fprintf(stderr, "UNLOCK failed: %s\n", ne_get_error(session));
1315         return -1;
1316     }
1318     return 0;
1321 static void *lock_thread_func(__unused void *p) {
1322     ne_session *session;
1323     sigset_t block;
1325     if (debug)
1326         fprintf(stderr, "lock_thread entering\n");
1328     if (!(session = session_get(1)))
1329         return NULL;
1331     sigemptyset(&block);
1332     sigaddset(&block, SIGUSR1);
1334     assert(lock);
1336     while (!lock_thread_exit) {
1337         int r, t;
1339         lock->timeout = lock_timeout;
1341         pthread_sigmask(SIG_BLOCK, &block, NULL);
1342         r = ne_lock_refresh(session, lock);
1343         pthread_sigmask(SIG_UNBLOCK, &block, NULL);
1345         if (r) {
1346             fprintf(stderr, "LOCK refresh failed: %s\n", ne_get_error(session));
1347             break;
1348         }
1350         if (lock_thread_exit)
1351             break;
1353         t = lock_timeout/2;
1354         if (t <= 0)
1355             t = 1;
1356         sleep(t);
1357     }
1359     if (debug)
1360         fprintf(stderr, "lock_thread exiting\n");
1362     return NULL;
1365 int main(int argc, char *argv[]) {
1366     int c;
1367     char *u = NULL, *p = NULL, *o = NULL;
1368     int fuse_fd = -1;
1369     int ret = 1;
1370     char mountpoint[PATH_MAX];
1371     pthread_t lock_thread;
1372     int lock_thread_running = 0;
1373     int enable_locking = 0;
1375     static char *mount_args_strings[] = {
1376         NULL,  /* path*/
1377         NULL,  /* -o */
1378         NULL,
1379         NULL};
1381     struct fuse_args mount_args = {
1382         .argc = 1,
1383         .argv = mount_args_strings,
1384         .allocated = 0
1385     };
1387     if (ne_sock_init()) {
1388         fprintf(stderr, "Failed to initialize libneon.\n");
1389         goto finish;
1390     }
1392     if (!ne_has_support(NE_FEATURE_SSL) ||
1393         !ne_has_support(NE_FEATURE_TS_SSL) ||
1394         !ne_has_support(NE_FEATURE_LFS)) {
1395         fprintf(stderr, "fusedav requires libneon built with SSL, SSL thread safety and LFS enabled.\n");
1396         goto finish;
1397     }
1399     mask = umask(0);
1400     umask(mask);
1402     cache_alloc();
1404     if (setup_signal_handlers() < 0)
1405         goto finish;
1407     while ((c = getopt(argc, argv, "hu:p:Do:Lt:")) != -1) {
1409         switch(c) {
1410             case 'u':
1411                 u = optarg;
1412                 break;
1414             case 'p':
1415                 p = optarg;
1416                 break;
1418             case 'D':
1419                 debug = !debug;
1420                 break;
1422             case 'o':
1423                 o = optarg;
1424                 break;
1426             case 'L':
1427                 enable_locking = 1;
1428                 break;
1430             case 't':
1431                 if ((lock_timeout = atoi(optarg)) < 0) {
1432                     fprintf(stderr, "Invalid lock timeout '%s'\n", optarg);
1433                     goto finish;
1434                 }
1435                 break;
1437             case 'h':
1438                 ret = 0;
1440                 /* fall through */
1441             default:
1442                 usage(argv[0]);
1443                 goto finish;
1444         }
1445     }
1447     if (optind != argc-2) {
1448         usage(argv[0]);
1449         goto finish;
1450     }
1452     if (session_set_uri(argv[optind], u, p) < 0) {
1453         usage(argv[0]);
1454         goto finish;
1455     }
1457     if (argv[optind+1][0] == '/')
1458         snprintf(mountpoint, sizeof(mountpoint), "%s", argv[optind+1]);
1459     else {
1460         char *pwd = get_current_dir_name();
1461         snprintf(mountpoint, sizeof(mountpoint), "%s/%s", pwd, argv[optind+1]);
1462         free(pwd);
1463     }
1465     mount_args_strings[0] = argv[optind];
1467     if (o) {
1468         mount_args_strings[1] = (char*) "-o";
1469         mount_args_strings[2] = o;
1470         mount_args.argc += 2;
1471     }
1473     if ((fuse_fd = fuse_mount(mountpoint, &mount_args)) < 0) {
1474         fprintf(stderr, "Failed to mount FUSE file system.\n");
1475         goto finish;
1476     }
1478     if (!(fuse = fuse_new(fuse_fd, &mount_args, &dav_oper, sizeof(dav_oper)))) {
1479         fprintf(stderr, "Failed to create FUSE object.\n");
1480         goto finish;
1481     }
1483     if (enable_locking && create_lock() >= 0) {
1484         int r;
1485         if ((r = pthread_create(&lock_thread, NULL, lock_thread_func, NULL)) < 0) {
1486             fprintf(stderr, "pthread_create(): %s\n", strerror(r));
1487             goto finish;
1488         }
1490         lock_thread_running = 1;
1491     }
1493     fuse_loop_mt(fuse);
1495     if (debug)
1496         fprintf(stderr, "Exiting cleanly.\n");
1498     ret = 0;
1500 finish:
1502     if (lock_thread_running) {
1503         lock_thread_exit = 1;
1504         pthread_kill(lock_thread, SIGUSR1);
1505         pthread_join(lock_thread, NULL);
1506         remove_lock();
1507         ne_lockstore_destroy(lock_store);
1508     }
1510     if (fuse)
1511         fuse_destroy(fuse);
1513     if (fuse_fd >= 0)
1514         fuse_unmount(mountpoint);
1516     file_cache_close_all();
1517     cache_free();
1518     session_free();
1520     return ret;