Code

use neon's native thread safe SSL support
[fusedav.git] / src / fusedav.c
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.
10   
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.
15   
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 "fusedav.h"
57 const ne_propname query_properties[] = {
58     { "DAV:", "resourcetype" },
59     { "http://apache.org/dav/props/", "executable" },
60     { "DAV:", "getcontentlength" },
61     { "DAV:", "getlastmodified" },
62     { "DAV:", "creationdate" },
63     { NULL, NULL }
64 };
66 mode_t mask = 0;
67 int debug = 0;
68 struct fuse* fuse = NULL;
69 ne_lock_store *lock_store = NULL;
70 struct ne_lock *lock = NULL;
71 int lock_thread_exit = 0;
72 int lock_timeout = 60;
74 #define MIME_XATTR "user.mime_type"
76 #define MAX_REDIRECTS 10
78 struct fill_info {
79     void *buf;
80     fuse_fill_dir_t filler;
81     const char *root;
82 };
84 static int get_stat(const char *path, struct stat *stbuf);
86 static pthread_once_t path_cvt_once = PTHREAD_ONCE_INIT;
87 static pthread_key_t path_cvt_tsd_key;
89 static void path_cvt_tsd_key_init(void) {
90     pthread_key_create(&path_cvt_tsd_key, free);
91 }
93 static const char *path_cvt(const char *path) {
94     char *r, *t;
95     int l;
97     pthread_once(&path_cvt_once, path_cvt_tsd_key_init);
98     
99     if ((r = pthread_getspecific(path_cvt_tsd_key)))
100         free(r);
102     t = malloc((l = strlen(base_directory)+strlen(path))+1);
103     assert(t);
104     sprintf(t, "%s%s", base_directory, path);
106     if (l > 1 && t[l-1] == '/')
107         t[l-1] = 0;
109     r = ne_path_escape(t);
110     free(t);
112     pthread_setspecific(path_cvt_tsd_key, r);
113     
114     return r;
117 static int simple_propfind_with_redirect(
118         ne_session *session,
119         const char *path,
120         int depth,
121         const ne_propname *props,
122         ne_props_result results,
123         void *userdata) {
125     int i, ret;
126     
127     for (i = 0; i < MAX_REDIRECTS; i++) {
128         const ne_uri *u;
130         if ((ret = ne_simple_propfind(session, path, depth, props, results, userdata)) != NE_REDIRECT)
131             return ret;
133         if (!(u = ne_redirect_location(session)))
134             break;
136         if (!session_is_local(u))
137             break;
139         if (debug)
140             fprintf(stderr, "REDIRECT FROM '%s' to '%s'\n", path, u->path);
141         
142         path = u->path;
143     }
145     return ret;
148 static int proppatch_with_redirect(
149         ne_session *session,
150         const char *path,
151         const ne_proppatch_operation *ops) {
152     
153     int i, ret;
154     
155     for (i = 0; i < MAX_REDIRECTS; i++) {
156         const ne_uri *u;
158         if ((ret = ne_proppatch(session, path, ops)) != NE_REDIRECT)
159             return ret;
161         if (!(u = ne_redirect_location(session)))
162             break;
164         if (!session_is_local(u))
165             break;
167         if (debug)
168             fprintf(stderr, "REDIRECT FROM '%s' to '%s'\n", path, u->path);
169         
170         path = u->path;
171     }
173     return ret;
177 static void fill_stat(struct stat* st, const ne_prop_result_set *results, int is_dir) {
178     const char *rt, *e, *gcl, *glm, *cd;
179     const ne_propname resourcetype = { "DAV:", "resourcetype" };
180     const ne_propname executable = { "http://apache.org/dav/props/", "executable" };
181     const ne_propname getcontentlength = { "DAV:", "getcontentlength" };
182     const ne_propname getlastmodified = { "DAV:", "getlastmodified" };
183     const ne_propname creationdate = { "DAV:", "creationdate" };
184         
185     assert(st && results);
187     rt = ne_propset_value(results, &resourcetype);
188     e = ne_propset_value(results, &executable);
189     gcl = ne_propset_value(results, &getcontentlength);
190     glm = ne_propset_value(results, &getlastmodified);
191     cd = ne_propset_value(results, &creationdate);
193     memset(st, 0, sizeof(struct stat));
194     
195     if (is_dir) {
196         st->st_mode = S_IFDIR | 0777;
197         st->st_nlink = 3;            /* find will ignore this directory if nlin <= and st_size == 0 */
198         st->st_size = 4096;
199     } else {
200         st->st_mode = S_IFREG | (e && (*e == 'T' || *e == 't') ? 0777 : 0666);
201         st->st_nlink = 1;
202         st->st_size = gcl ? atoll(gcl) : 0;
203     }
205     st->st_atime = time(NULL);
206     st->st_mtime = glm ? ne_rfc1123_parse(glm) : 0;
207     st->st_ctime = cd ? ne_iso8601_parse(cd) : 0;
208     
209     st->st_blocks = (st->st_size+511)/512;
210     /*fprintf(stderr, "a: %u; m: %u; c: %u\n", st->st_atime, st->st_mtime, st->st_ctime);*/
212     st->st_mode &= ~mask;
213     
214     st->st_uid = getuid();
215     st->st_gid = getgid();
218 static char *strip_trailing_slash(char *fn, int *is_dir) {
219     size_t l = strlen(fn);
220     assert(fn);
221     assert(is_dir);
222     assert(l > 0);
223     
224     if ((*is_dir = (fn[l-1] == '/')))
225         fn[l-1] = 0;
227     return fn;
230 static void getdir_propfind_callback(void *userdata, const ne_uri *u, const ne_prop_result_set *results) {
231     struct fill_info *f = userdata;
232     struct stat st;
233     char fn[PATH_MAX], *t;
234     int is_dir = 0;
236     assert(f);
238     strncpy(fn, u->path, sizeof(fn));
239     fn[sizeof(fn)-1] = 0;
240     strip_trailing_slash(fn, &is_dir);
242     if (strcmp(fn, f->root) && fn[0]) {
243         char *h;
244         
245         if ((t = strrchr(fn, '/')))
246             t++;
247         else
248             t = fn;
250         dir_cache_add(f->root, t);
251         f->filler(f->buf, h = ne_path_unescape(t), NULL, 0);
252         free(h);
253     }
255     fill_stat(&st, results, is_dir);
256     stat_cache_set(fn, &st);
259 static void getdir_cache_callback(
260         const char *root,
261         const char *fn,
262         void *user) {
263     
264     struct fill_info *f = user;
265     char path[PATH_MAX];
266     char *h;
268     assert(f);
269     
270     snprintf(path, sizeof(path), "%s/%s", !strcmp(root, "/") ? "" : root, fn);
271     
272     f->filler(f->buf, h = ne_path_unescape(fn), NULL, 0);
273     free(h);
276 static int dav_readdir(
277         const char *path,
278         void *buf,
279         fuse_fill_dir_t filler,
280         __unused off_t offset,
281         __unused struct fuse_file_info *fi) {
282     
283     struct fill_info f;
284     ne_session *session;
286     path = path_cvt(path);
288     if (debug)
289         fprintf(stderr, "getdir(%s)\n", path);
291     f.buf = buf;
292     f.filler = filler;
293     f.root = path;
295     filler(buf, ".", NULL, 0);
296     filler(buf, "..", NULL, 0);
297     
298     if (dir_cache_enumerate(path, getdir_cache_callback, &f) < 0) {
300         if (debug)
301             fprintf(stderr, "DIR-CACHE-MISS\n");
302         
303         if (!(session = session_get(1))) 
304             return -EIO;
306         dir_cache_begin(path);
307         
308         if (simple_propfind_with_redirect(session, path, NE_DEPTH_ONE, query_properties, getdir_propfind_callback, &f) != NE_OK) {
309             dir_cache_finish(path, 2);
310             fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
311             return -ENOENT;
312         }
314         dir_cache_finish(path, 1);
315     }
317     return 0;
320 static void getattr_propfind_callback(void *userdata, const ne_uri *u, const ne_prop_result_set *results) {
321     struct stat *st = (struct stat*) userdata;
322     char fn[PATH_MAX];
323     int is_dir;
325     assert(st);
327     strncpy(fn, u->path, sizeof(fn));
328     fn[sizeof(fn)-1] = 0;
329     strip_trailing_slash(fn, &is_dir);
330     
331     fill_stat(st, results, is_dir);
332     stat_cache_set(fn, st);
335 static int get_stat(const char *path, struct stat *stbuf) {
336     ne_session *session;
338     if (!(session = session_get(1))) 
339         return -EIO;
341     if (stat_cache_get(path, stbuf) == 0) {
342         return stbuf->st_mode == 0 ? -ENOENT : 0;
343     } else {
344         if (debug)
345             fprintf(stderr, "STAT-CACHE-MISS\n");
347         if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, query_properties, getattr_propfind_callback, stbuf) != NE_OK) {
348             stat_cache_invalidate(path);
349             fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
350             return -ENOENT;
351         }
353         return 0;
354     }
357 static int dav_getattr(const char *path, struct stat *stbuf) {
358     path = path_cvt(path);
359     if (debug)
360         fprintf(stderr, "getattr(%s)\n", path);
361     return get_stat(path, stbuf);
364 static int dav_unlink(const char *path) {
365     int r;
366     struct stat st;
367     ne_session *session;
369     path = path_cvt(path);
371     if (debug)
372         fprintf(stderr, "unlink(%s)\n", path);
374     if (!(session = session_get(1))) 
375         return -EIO;
377     if ((r = get_stat(path, &st)) < 0)
378         return r;
380     if (!S_ISREG(st.st_mode))
381         return -EISDIR;
382     
383     if (ne_delete(session, path)) {
384         fprintf(stderr, "DELETE failed: %s\n", ne_get_error(session));
385         return -ENOENT;
386     }
388     stat_cache_invalidate(path);
389     dir_cache_invalidate_parent(path);
390     
391     return 0;
394 static int dav_rmdir(const char *path) {
395     char fn[PATH_MAX];
396     int r;
397     struct stat st;
398     ne_session *session;
400     path = path_cvt(path);
402     if (debug)
403         fprintf(stderr, "rmdir(%s)\n", path);
405     if (!(session = session_get(1))) 
406         return -EIO;
408     if ((r = get_stat(path, &st)) < 0)
409         return r;
411     if (!S_ISDIR(st.st_mode))
412         return -ENOTDIR;
414     snprintf(fn, sizeof(fn), "%s/", path);
415     
416     if (ne_delete(session, fn)) {
417         fprintf(stderr, "DELETE failed: %s\n", ne_get_error(session));
418         return -ENOENT;
419     }
421     stat_cache_invalidate(path);
422     dir_cache_invalidate_parent(path);
424     return 0;
427 static int dav_mkdir(const char *path, __unused mode_t mode) {
428     char fn[PATH_MAX];
429     ne_session *session;
431     path = path_cvt(path);
433     if (debug)
434         fprintf(stderr, "mkdir(%s)\n", path);
436     if (!(session = session_get(1))) 
437         return -EIO;
439     snprintf(fn, sizeof(fn), "%s/", path);
440     
441     if (ne_mkcol(session, fn)) {
442         fprintf(stderr, "MKCOL failed: %s\n", ne_get_error(session));
443         return -ENOENT;
444     }
446     stat_cache_invalidate(path);
447     dir_cache_invalidate_parent(path);
448     
449     return 0;
452 static int dav_rename(const char *from, const char *to) {
453     ne_session *session;
454     int r = 0;
455     struct stat st;
456     char fn[PATH_MAX], *_from;
458     from = _from = strdup(path_cvt(from));
459     assert(from);
460     to = path_cvt(to);
462     if (debug)
463         fprintf(stderr, "rename(%s, %s)\n", from, to);
465     if (!(session = session_get(1))) {
466         r = -EIO;
467         goto finish;
468     }
470     if ((r = get_stat(from, &st)) < 0)
471         goto finish;
473     if (S_ISDIR(st.st_mode)) {
474         snprintf(fn, sizeof(fn), "%s/", from);
475         from = fn;
476     }
477     
478     if (ne_move(session, 1, from, to)) {
479         fprintf(stderr, "MOVE failed: %s\n", ne_get_error(session));
480         r = -ENOENT;
481         goto finish;
482     }
483     
484     stat_cache_invalidate(from);
485     stat_cache_invalidate(to);
487     dir_cache_invalidate_parent(from);
488     dir_cache_invalidate_parent(to);
490 finish:
492     free(_from);
493     
494     return r;
497 static int dav_release(const char *path, __unused struct fuse_file_info *info) {
498     void *f = NULL;
499     int r = 0;
500     ne_session *session;
502     path = path_cvt(path);
504     if (debug)
505         fprintf(stderr, "release(%s)\n", path);
507     if (!(session = session_get(1))) {
508         r = -EIO;
509         goto finish;
510     }
511     
512     if (!(f = file_cache_get(path))) {
513         fprintf(stderr, "release() called for closed file\n");
514         r = -EFAULT;
515         goto finish;
516     }
518     if (file_cache_close(f) < 0) {
519         r = -errno;
520         goto finish;
521     }
523 finish:
524     if (f)
525         file_cache_unref(f);
526     
527     return r;
530 static int dav_fsync(const char *path, __unused int isdatasync, __unused struct fuse_file_info *info) {
531     void *f = NULL;
532     int r = 0;
533     ne_session *session;
535     path = path_cvt(path);
536     if (debug)
537         fprintf(stderr, "fsync(%s)\n", path);
539     if (!(session = session_get(1))) {
540         r = -EIO;
541         goto finish;
542     }
544     if (!(f = file_cache_get(path))) {
545         fprintf(stderr, "fsync() called for closed file\n");
546         r = -EFAULT;
547         goto finish;
548     }
550     if (file_cache_sync(f) < 0) {
551         r = -errno;
552         goto finish;
553     }
555 finish:
556     
557     if (f)
558         file_cache_unref(f);
560     return r;
563 static int dav_mknod(const char *path, mode_t mode, __unused dev_t rdev) {
564     char tempfile[PATH_MAX];
565     int fd;
566     ne_session *session;
568     path = path_cvt(path);
569     if (debug)
570         fprintf(stderr, "mknod(%s)\n", path);
572     if (!(session = session_get(1))) 
573         return -EIO;
575     if (!S_ISREG(mode))
576         return -ENOTSUP;
578     snprintf(tempfile, sizeof(tempfile), "%s/fusedav-empty-XXXXXX", "/tmp");
579     if ((fd = mkstemp(tempfile)) < 0)
580         return -errno;
581     
582     unlink(tempfile);
583     
584     if (ne_put(session, path, fd)) {
585         fprintf(stderr, "mknod:PUT failed: %s\n", ne_get_error(session));
586         close(fd);
587         return -EACCES;
588     }
590     close(fd);
592     stat_cache_invalidate(path);
593     dir_cache_invalidate_parent(path);
595     return 0;
598 static int dav_open(const char *path, struct fuse_file_info *info) {
599     void *f;
601     if (debug)
602         fprintf(stderr, "open(%s)\n", path);
604     path = path_cvt(path);
606     if (!(f = file_cache_open(path, info->flags)))
607         return -errno;
609     file_cache_unref(f);
611     return 0;
614 static int dav_read(const char *path, char *buf, size_t size, off_t offset, __unused struct fuse_file_info *info) {
615     void *f = NULL;
616     ssize_t r;
617  
618     path = path_cvt(path);
619     
620     if (debug)
621         fprintf(stderr, "read(%s, %lu+%lu)\n", path, (unsigned long) offset, (unsigned long) size);
622     
623     if (!(f = file_cache_get(path))) {
624         fprintf(stderr, "read() called for closed file\n");
625         r = -EFAULT;
626         goto finish;
627     }
629     if ((r = file_cache_read(f, buf, size, offset)) < 0) {
630         r = -errno;
631         goto finish;
632     }
634 finish:
635     if (f)
636         file_cache_unref(f);
637     
638     return r;
641 static int dav_write(const char *path, const char *buf, size_t size, off_t offset, __unused struct fuse_file_info *info) {
642     void *f = NULL;
643     ssize_t r;
645     path = path_cvt(path);
647     if (debug)
648         fprintf(stderr, "write(%s, %lu+%lu)\n", path, (unsigned long) offset, (unsigned long) size);
650     if (!(f = file_cache_get(path))) {
651         fprintf(stderr, "write() called for closed file\n");
652         r = -EFAULT;
653         goto finish;
654     }
656     if ((r = file_cache_write(f, buf, size, offset)) < 0) {
657         r = -errno;
658         goto finish;
659     }
660     
661 finish:
662     if (f)
663         file_cache_unref(f);
664     
665     return r;
669 static int dav_truncate(const char *path, off_t size) {
670     void *f = NULL;
671     int r = 0;
672     ne_session *session;
673     
674     path = path_cvt(path);
675     
676     if (debug)
677         fprintf(stderr, "truncate(%s, %lu)\n", path, (unsigned long) size);
679     if (!(session = session_get(1)))
680         r = -EIO;
681         goto finish;
682     
683     if (!(f = file_cache_get(path))) {
684         fprintf(stderr, "truncate() called for closed file\n");
685         r = -EFAULT;
686         goto finish;
687     }
689     if (file_cache_truncate(f, size) < 0) {
690         r = -errno;
691         goto finish;
692     }
694 finish:
695     if (f)
696         file_cache_unref(f);
697     
698     return r;
701 static int dav_utime(const char *path, struct utimbuf *buf) {
702     ne_session *session;
703     const ne_propname getlastmodified = { "DAV:", "getlastmodified" };
704     ne_proppatch_operation ops[2];
705     int r = 0;
706     char *date;
707     
708     assert(path);
709     assert(buf);
711     path = path_cvt(path);
712     
713     if (debug)
714         fprintf(stderr, "utime(%s, %lu, %lu)\n", path, (unsigned long) buf->actime, (unsigned long) buf->modtime);
715     
716     ops[0].name = &getlastmodified;
717     ops[0].type = ne_propset;
718     ops[0].value = date = ne_rfc1123_date(buf->modtime);
719     ops[1].name = NULL;
721     if (!(session = session_get(1))) {
722         r = -EIO;
723         goto finish;
724     }
726     if (proppatch_with_redirect(session, path, ops)) {
727         fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
728         r = -ENOTSUP;
729         goto finish;
730     }
731     
732     stat_cache_invalidate(path);
734 finish:
735     free(date);
736     
737     return r;
740 static const char *fix_xattr(const char *name) {
741     assert(name);
743     if (!strcmp(name, MIME_XATTR))
744         return "user.webdav(DAV:;getcontenttype)";
746     return name;
749 struct listxattr_info {
750     char *list;
751     size_t space, size;
752 };
754 static int listxattr_iterator(
755         void *userdata,
756         const ne_propname *pname,
757         const char *value,
758         __unused const ne_status *status) {
760     struct listxattr_info *l = userdata;
761     int n;
762     
763     assert(l);
765     if (!value || !pname)
766         return -1;
768     if (l->list) {
769         n = snprintf(l->list, l->space, "user.webdav(%s;%s)", pname->nspace, pname->name) + 1;
770         
771         if (n >= (int) l->space) {
772             l->size += l->space;
773             l->space = 0;
774             return 1;
775             
776         } else {
777             l->size += n;
778             l->space -= n;
779             
780             if (l->list)
781                 l->list += n;
782             
783             return 0;
784         }
785     } else {
786         /* Calculate space */
787         
788         l->size += strlen(pname->nspace) + strlen(pname->name) + 15;
789         return 0;
790     }
793 static void listxattr_propfind_callback(void *userdata, __unused const ne_uri *u, const ne_prop_result_set *results) {
794     struct listxattr_info *l = userdata;
795     ne_propset_iterate(results, listxattr_iterator, l);
798 static int dav_listxattr(
799         const char *path,
800         char *list,
801         size_t size) {
802     
803     ne_session *session;
804     struct listxattr_info l;
805     
807     assert(path);
809     path = path_cvt(path);
811     if (debug)
812         fprintf(stderr, "listxattr(%s, .., %lu)\n", path, (unsigned long) size);
814     if (list) {
815         l.list = list;
816         l.space = size-1;
817         l.size = 0;
819         if (l.space >= sizeof(MIME_XATTR)) {
820             memcpy(l.list, MIME_XATTR, sizeof(MIME_XATTR));
821             l.list += sizeof(MIME_XATTR);
822             l.space -= sizeof(MIME_XATTR);
823             l.size += sizeof(MIME_XATTR);
824         }
825         
826     } else {
827         l.list = NULL;
828         l.space = 0;
829         l.size = sizeof(MIME_XATTR);
830     }
831     
832     if (!(session = session_get(1))) 
833         return -EIO;
835     if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, NULL, listxattr_propfind_callback, &l) != NE_OK) {
836         fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
837         return -EIO;
838     }
840     if (l.list) {
841         assert(l.space > 0);
842         *l.list = 0;
843     }
845     return l.size+1;
848 struct getxattr_info {
849     ne_propname propname;
850     char *value;
851     size_t space, size;
852 };
854 static int getxattr_iterator(
855         void *userdata,
856         const ne_propname *pname,
857         const char *value,
858         __unused const ne_status *status) {
860     struct getxattr_info *g = userdata;
861     
862     assert(g);
864     if (!value || !pname)
865         return -1;
867     if (strcmp(pname->nspace, g->propname.nspace) ||
868         strcmp(pname->name, g->propname.name))
869         return 0;
871     if (g->value) {
872         size_t l;
874         l = strlen(value);
876         if (l > g->space)
877             l = g->space;
879         memcpy(g->value, value, l);
880         g->size = l;
881     } else {
882         /* Calculate space */
883         
884         g->size = strlen(value);
885         return 0;
886     }
887     
888     return 0;
891 static void getxattr_propfind_callback(void *userdata, __unused const ne_uri *u, const ne_prop_result_set *results) {
892     struct getxattr_info *g = userdata;
893     ne_propset_iterate(results, getxattr_iterator, g);
896 static int parse_xattr(const char *name, char *dnspace, size_t dnspace_length, char *dname, size_t dname_length) {
897     char *e;
898     size_t k;
899     
900     assert(name);
901     assert(dnspace);
902     assert(dnspace_length);
903     assert(dname);
904     assert(dname_length);
905     
906     if (strncmp(name, "user.webdav(", 12) ||
907         name[strlen(name)-1] != ')' ||
908         !(e = strchr(name+12, ';')))
909         return -1;
911     if ((k = strcspn(name+12, ";")) > dnspace_length-1)
912         return -1;
914     memcpy(dnspace, name+12, k);
915     dnspace[k] = 0;
917     e++;
918     
919     if ((k = strlen(e)) > dname_length-1)
920         return -1;
922     assert(k > 0);
923     k--;
925     memcpy(dname, e, k);
926     dname[k] = 0;
928     return 0;
931 static int dav_getxattr(
932         const char *path,
933         const char *name,
934         char *value,
935         size_t size) {
937     ne_session *session;
938     struct getxattr_info g;
939     ne_propname props[2];
940     char dnspace[128], dname[128];
941         
942     assert(path);
944     path = path_cvt(path);
945     name = fix_xattr(name);
947     if (debug)
948         fprintf(stderr, "getxattr(%s, %s, .., %lu)\n", path, name, (unsigned long) size);
950     if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0)
951         return -ENOATTR;
953     props[0].nspace = dnspace;
954     props[0].name = dname;
955     props[1].nspace = NULL;
956     props[1].name = NULL;
958     if (value) {
959         g.value = value;
960         g.space = size;
961         g.size = (size_t) -1;
962     } else {
963         g.value = NULL;
964         g.space = 0;
965         g.size = (size_t) -1;
966     }
968     g.propname = props[0];
969     
970     if (!(session = session_get(1)))
971         return -EIO;
973     if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, props, getxattr_propfind_callback, &g) != NE_OK) {
974         fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
975         return -EIO;
976     }
978     if (g.size == (size_t) -1)
979         return -ENOATTR;
981     return g.size;
984 static int dav_setxattr(
985         const char *path,
986         const char *name,
987         const char *value,
988         size_t size,
989         int flags) {
991     ne_session *session;
992     ne_propname propname;
993     ne_proppatch_operation ops[2];
994     int r = 0;
995     char dnspace[128], dname[128];
996     char *value_fixed = NULL;
998     assert(path);
999     assert(name);
1000     assert(value);
1002     path = path_cvt(path);
1003     name = fix_xattr(name);
1004     
1005     if (debug)
1006         fprintf(stderr, "setxattr(%s, %s)\n", path, name);
1008     if (flags) {
1009         r = ENOTSUP;
1010         goto finish;
1011     }
1012     
1013     if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0) {
1014         r = -ENOATTR;
1015         goto finish;
1016     }
1018     propname.nspace = dnspace;
1019     propname.name = dname;
1020     
1021     /* Add trailing NUL byte if required */
1022     if (!memchr(value, 0, size)) {
1023         value_fixed = malloc(size+1);
1024         assert(value_fixed);
1025         
1026         memcpy(value_fixed, value, size);
1027         value_fixed[size] = 0;
1029         value = value_fixed;
1030     }
1031     
1032     ops[0].name = &propname;
1033     ops[0].type = ne_propset;
1034     ops[0].value = value;
1035     
1036     ops[1].name = NULL;
1038     if (!(session = session_get(1))) {
1039         r = -EIO;
1040         goto finish;
1041     }
1042                  
1043     if (proppatch_with_redirect(session, path, ops)) {
1044         fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1045         r = -ENOTSUP;
1046         goto finish;
1047     }
1048     
1049     stat_cache_invalidate(path);
1051 finish:
1052     free(value_fixed);
1053     
1054     return r;
1057 static int dav_removexattr(const char *path, const char *name) {
1058     ne_session *session;
1059     ne_propname propname;
1060     ne_proppatch_operation ops[2];
1061     int r = 0;
1062     char dnspace[128], dname[128];
1064     assert(path);
1065     assert(name);
1067     path = path_cvt(path);
1068     name = fix_xattr(name);
1069     
1070     if (debug)
1071         fprintf(stderr, "removexattr(%s, %s)\n", path, name);
1073     if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0) {
1074         r = -ENOATTR;
1075         goto finish;
1076     }
1078     propname.nspace = dnspace;
1079     propname.name = dname;
1080     
1081     ops[0].name = &propname;
1082     ops[0].type = ne_propremove;
1083     ops[0].value = NULL;
1084     
1085     ops[1].name = NULL;
1087     if (!(session = session_get(1))) {
1088         r = -EIO;
1089         goto finish;
1090     }
1091                  
1092     if (proppatch_with_redirect(session, path, ops)) {
1093         fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1094         r = -ENOTSUP;
1095         goto finish;
1096     }
1097     
1098     stat_cache_invalidate(path);
1100 finish:
1101     
1102     return r;
1105 static int dav_chmod(const char *path, mode_t mode) {
1106     ne_session *session;
1107     const ne_propname executable = { "http://apache.org/dav/props/", "executable" };
1108     ne_proppatch_operation ops[2];
1109     int r = 0;
1110     
1111     assert(path);
1113     path = path_cvt(path);
1114     
1115     if (debug)
1116         fprintf(stderr, "chmod(%s, %04o)\n", path, mode);
1117     
1118     ops[0].name = &executable;
1119     ops[0].type = ne_propset;
1120     ops[0].value = mode & 0111 ? "T" : "F";
1121     ops[1].name = NULL;
1123     if (!(session = session_get(1))) {
1124         r = -EIO;
1125         goto finish;
1126     }
1128     if (proppatch_with_redirect(session, path, ops)) {
1129         fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1130         r = -ENOTSUP;
1131         goto finish;
1132     }
1133     
1134     stat_cache_invalidate(path);
1136 finish:
1137     
1138     return r;
1141 static struct fuse_operations dav_oper = {
1142     .getattr     = dav_getattr,
1143     .readdir     = dav_readdir,
1144     .mknod       = dav_mknod,
1145     .mkdir       = dav_mkdir,
1146     .unlink      = dav_unlink,
1147     .rmdir       = dav_rmdir,
1148     .rename      = dav_rename,
1149     .chmod       = dav_chmod,
1150     .truncate    = dav_truncate,
1151     .utime       = dav_utime,
1152     .open        = dav_open,
1153     .read        = dav_read,
1154     .write       = dav_write,
1155     .release     = dav_release,
1156     .fsync       = dav_fsync,
1157     .setxattr    = dav_setxattr,
1158     .getxattr    = dav_getxattr,
1159     .listxattr   = dav_listxattr,
1160     .removexattr = dav_removexattr,
1161 };
1163 static void usage(char *argv0) {
1164     char *e;
1166     if ((e = strrchr(argv0, '/')))
1167         e++;
1168     else
1169         e = argv0;
1170     
1171     fprintf(stderr,
1172             "%s [-hDL] [-t SECS] [-u USERNAME] [-p PASSWORD] [-o OPTIONS] URL MOUNTPOINT\n"
1173             "\t-h Show this help\n"
1174             "\t-D Enable debug mode\n"
1175             "\t-u Username if required\n"
1176             "\t-p Password if required\n"
1177             "\t-o Additional FUSE mount options\n"
1178             "\t-L Locking the repository during mount\n"
1179             "\t-t Set lock timeout\n",
1180             e);
1183 static void exit_handler(__unused int sig) {
1184     static const char m[] = "*** Caught signal ***\n";
1185     if(fuse != NULL)
1186         fuse_exit(fuse);
1187     write(2, m, strlen(m));
1190 static void empty_handler(__unused int sig) {}
1192 static int setup_signal_handlers(void) {
1193     struct sigaction sa;
1194     sigset_t m;
1195                                                                             
1196     sa.sa_handler = exit_handler;
1197     sigemptyset(&(sa.sa_mask));
1198     sa.sa_flags = 0;
1199     
1200     if (sigaction(SIGHUP, &sa, NULL) == -1 ||
1201         sigaction(SIGINT, &sa, NULL) == -1 ||
1202         sigaction(SIGTERM, &sa, NULL) == -1) {
1203                                                                                                         
1204         fprintf(stderr, "Cannot set exit signal handlers: %s\n", strerror(errno));
1205         return -1;
1206     }
1207                                                                                                         
1208     sa.sa_handler = SIG_IGN;
1209                                                                                                         
1210     if (sigaction(SIGPIPE, &sa, NULL) == -1) {
1211         fprintf(stderr, "Cannot set ignored signals: %s\n", strerror(errno));
1212         return -1;
1213     }
1215     /* Used to shut down the locking thread */
1216     sa.sa_handler = empty_handler;
1218     if (sigaction(SIGUSR1, &sa, NULL) == -1) {
1219         fprintf(stderr, "Cannot set user signals: %s\n", strerror(errno));
1220         return -1;
1221     }
1223     sigemptyset(&m);
1224     pthread_sigmask(SIG_BLOCK, &m, &m);
1225     sigdelset(&m, SIGHUP);
1226     sigdelset(&m, SIGINT);
1227     sigdelset(&m, SIGTERM);
1228     sigaddset(&m, SIGPIPE);
1229     sigaddset(&m, SIGUSR1);
1230     pthread_sigmask(SIG_SETMASK, &m, NULL);
1231     
1232     return 0;
1235 static int create_lock(void) {
1236     ne_session *session;
1237     char _owner[64], *owner;
1238     int i;
1239     int ret;
1240     
1241     lock = ne_lock_create();
1242     assert(lock);
1244     if (!(session = session_get(0)))
1245         return -1;
1247     if (!(owner = username))
1248         if (!(owner = getenv("USER")))
1249             if (!(owner = getenv("LOGNAME"))) {
1250                 snprintf(_owner, sizeof(_owner), "%lu", (unsigned long) getuid());
1251                 owner = owner;
1252             }
1254     ne_fill_server_uri(session, &lock->uri);
1255     
1256     lock->uri.path = strdup(base_directory);
1257     lock->depth = NE_DEPTH_INFINITE;
1258     lock->timeout = lock_timeout;
1259     lock->owner = strdup(owner);
1261     if (debug)
1262         fprintf(stderr, "Acquiring lock...\n");
1263     
1264     for (i = 0; i < MAX_REDIRECTS; i++) {
1265         const ne_uri *u;
1267         if ((ret = ne_lock(session, lock)) != NE_REDIRECT)
1268             break;
1270         if (!(u = ne_redirect_location(session)))
1271             break;
1273         if (!session_is_local(u))
1274             break;
1276         if (debug)
1277             fprintf(stderr, "REDIRECT FROM '%s' to '%s'\n", lock->uri.path, u->path);
1279         free(lock->uri.path);
1280         lock->uri.path = strdup(u->path);
1281     }
1283     if (ret) {
1284         fprintf(stderr, "LOCK failed: %s\n", ne_get_error(session));
1285         ne_lock_destroy(lock);
1286         lock = NULL;
1287         return -1;
1288     }
1290     lock_store = ne_lockstore_create();
1291     assert(lock_store);
1292     
1293     ne_lockstore_add(lock_store, lock);
1294     
1295     return 0;
1298 static int remove_lock(void) {
1299     ne_session *session;
1301     assert(lock);
1303     if (!(session = session_get(0)))
1304         return -1;
1306     if (debug)
1307         fprintf(stderr, "Removing lock...\n");
1309     if (ne_unlock(session, lock)) {
1310         fprintf(stderr, "UNLOCK failed: %s\n", ne_get_error(session));
1311         return -1;
1312     }
1314     return 0;
1317 static void *lock_thread_func(__unused void *p) {
1318     ne_session *session;
1319     sigset_t block;
1321     if (debug)
1322         fprintf(stderr, "lock_thread entering\n");
1324     if (!(session = session_get(1)))
1325         return NULL;
1327     sigemptyset(&block);
1328     sigaddset(&block, SIGUSR1);
1330     assert(lock);
1332     while (!lock_thread_exit) {
1333         int r, t;
1334         
1335         lock->timeout = lock_timeout;
1337         pthread_sigmask(SIG_BLOCK, &block, NULL);
1338         r = ne_lock_refresh(session, lock);
1339         pthread_sigmask(SIG_UNBLOCK, &block, NULL);
1341         if (r) {
1342             fprintf(stderr, "LOCK refresh failed: %s\n", ne_get_error(session));
1343             break;
1344         }
1346         if (lock_thread_exit)
1347             break;
1349         t = lock_timeout/2;
1350         if (t <= 0)
1351             t = 1;
1352         sleep(t);
1353     }
1354     
1355     if (debug)
1356         fprintf(stderr, "lock_thread exiting\n");
1358     return NULL;
1361 int main(int argc, char *argv[]) {
1362     int c;
1363     char *u = NULL, *p = NULL, *o = NULL;
1364     int fuse_fd = -1;
1365     int ret = 1;
1366     char mountpoint[PATH_MAX];
1367     pthread_t lock_thread;
1368     int lock_thread_running = 0;
1369     int enable_locking = 0;
1370     
1371     static char *mount_args_strings[] = {
1372         NULL,  /* path*/
1373         NULL,  /* -o */
1374         NULL,
1375         NULL};
1376     
1377     struct fuse_args mount_args = {
1378         .argc = 1,
1379         .argv = mount_args_strings,
1380         .allocated = 0
1381     };
1382     
1383     if (ne_sock_init()) {
1384         fprintf(stderr, "Failed to initialize libneon.\n");
1385         goto finish;
1386     }
1388     if (!ne_has_support(NE_FEATURE_SSL) ||
1389         !ne_has_support(NE_FEATURE_TS_SSL) ||
1390         !ne_has_support(NE_FEATURE_LFS)) {
1391         fprintf(stderr, "fusedav requires libneon built with SSL, SSL thread safety and LFS enabled.\n");
1392         goto finish;
1393     }
1394         
1395     mask = umask(0);
1396     umask(mask);
1398     cache_alloc();
1400     if (setup_signal_handlers() < 0)
1401         goto finish;
1402     
1403     while ((c = getopt(argc, argv, "hu:p:Do:Lt:")) != -1) {
1405         switch(c) {
1406             case 'u':
1407                 u = optarg;
1408                 break;
1409                 
1410             case 'p':
1411                 p = optarg;
1412                 break;
1413                 
1414             case 'D':
1415                 debug = !debug;
1416                 break;
1418             case 'o':
1419                 o = optarg;
1420                 break;
1422             case 'L':
1423                 enable_locking = 1;
1424                 break;
1426             case 't':
1427                 if ((lock_timeout = atoi(optarg)) < 0) {
1428                     fprintf(stderr, "Invalid lock timeout '%s'\n", optarg);
1429                     goto finish;
1430                 }
1431                 break;
1432                 
1433             case 'h':
1434                 ret = 0;
1436                 /* fall through */
1437             default:
1438                 usage(argv[0]);
1439                 goto finish;
1440         }
1441     }
1443     if (optind != argc-2) {
1444         usage(argv[0]);
1445         goto finish;
1446     }
1448     if (session_set_uri(argv[optind], u, p) < 0) {
1449         usage(argv[0]);
1450         goto finish;
1451     }
1453     if (argv[optind+1][0] == '/')
1454         snprintf(mountpoint, sizeof(mountpoint), "%s", argv[optind+1]);
1455     else {
1456         char *pwd = get_current_dir_name();
1457         snprintf(mountpoint, sizeof(mountpoint), "%s/%s", pwd, argv[optind+1]);
1458         free(pwd);
1459     }
1461     mount_args_strings[0] = argv[optind];
1463     if (o) {
1464         mount_args_strings[1] = (char*) "-o";
1465         mount_args_strings[2] = o;
1466         mount_args.argc += 2;
1467     }
1468     
1469     if ((fuse_fd = fuse_mount(mountpoint, &mount_args)) < 0) {
1470         fprintf(stderr, "Failed to mount FUSE file system.\n");
1471         goto finish;
1472     }
1474     if (!(fuse = fuse_new(fuse_fd, &mount_args, &dav_oper, sizeof(dav_oper)))) {
1475         fprintf(stderr, "Failed to create FUSE object.\n");
1476         goto finish;
1477     }
1478     
1479     if (enable_locking && create_lock() >= 0) {
1480         int r;
1481         if ((r = pthread_create(&lock_thread, NULL, lock_thread_func, NULL)) < 0) {
1482             fprintf(stderr, "pthread_create(): %s\n", strerror(r));
1483             goto finish;
1484         }
1485         
1486         lock_thread_running = 1;
1487     }
1488     
1489     fuse_loop_mt(fuse);
1491     if (debug)
1492         fprintf(stderr, "Exiting cleanly.\n");
1493     
1494     ret = 0;
1495     
1496 finish:
1497     
1498     if (lock_thread_running) {
1499         lock_thread_exit = 1;
1500         pthread_kill(lock_thread, SIGUSR1);
1501         pthread_join(lock_thread, NULL);
1502         remove_lock();
1503         ne_lockstore_destroy(lock_store);
1504     }
1506     if (fuse)
1507         fuse_destroy(fuse);
1508     
1509     if (fuse_fd >= 0)
1510         fuse_unmount(mountpoint);
1511     
1512     file_cache_close_all();
1513     cache_free();
1514     session_free();
1515     
1516     return ret;