Code

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