Code

* make lock_timeout configurable
[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 "openssl-thread.h"
56 #include "fusedav.h"
58 const ne_propname query_properties[] = {
59     { "DAV:", "resourcetype" },
60     { "http://apache.org/dav/props/", "executable" },
61     { "DAV:", "getcontentlength" },
62     { "DAV:", "getlastmodified" },
63     { "DAV:", "creationdate" },
64     { NULL, NULL }
65 };
67 mode_t mask = 0;
68 int debug = 0;
69 struct fuse* fuse = NULL;
70 ne_lock_store *lock_store = NULL;
71 struct ne_lock *lock = NULL;
72 int lock_thread_exit = 0;
73 int lock_timeout = 60;
75 #define MIME_XATTR "user.mime_type"
77 #define MAX_REDIRECTS 10
79 struct fill_info {
80     void *buf;
81     fuse_fill_dir_t filler;
82     const char *root;
83 };
85 static int get_stat(const char *path, struct stat *stbuf);
87 static pthread_once_t path_cvt_once = PTHREAD_ONCE_INIT;
88 static pthread_key_t path_cvt_tsd_key;
90 static void path_cvt_tsd_key_init(void) {
91     pthread_key_create(&path_cvt_tsd_key, free);
92 }
94 static const char *path_cvt(const char *path) {
95     char *r, *t;
96     int l;
98     pthread_once(&path_cvt_once, path_cvt_tsd_key_init);
99     
100     if ((r = pthread_getspecific(path_cvt_tsd_key)))
101         free(r);
103     t = malloc((l = strlen(base_directory)+strlen(path))+1);
104     assert(t);
105     sprintf(t, "%s%s", base_directory, path);
107     if (l > 1 && t[l-1] == '/')
108         t[l-1] = 0;
110     r = ne_path_escape(t);
111     free(t);
113     pthread_setspecific(path_cvt_tsd_key, r);
114     
115     return r;
118 static int simple_propfind_with_redirect(
119         ne_session *session,
120         const char *path,
121         int depth,
122         const ne_propname *props,
123         ne_props_result results,
124         void *userdata) {
126     int i, ret;
127     
128     for (i = 0; i < MAX_REDIRECTS; i++) {
129         const ne_uri *u;
131         if ((ret = ne_simple_propfind(session, path, depth, props, results, userdata)) != NE_REDIRECT)
132             return ret;
134         if (!(u = ne_redirect_location(session)))
135             break;
137         if (!session_is_local(u))
138             break;
140         if (debug)
141             fprintf(stderr, "REDIRECT FROM '%s' to '%s'\n", path, u->path);
142         
143         path = u->path;
144     }
146     return ret;
149 static int proppatch_with_redirect(
150         ne_session *session,
151         const char *path,
152         const ne_proppatch_operation *ops) {
153     
154     int i, ret;
155     
156     for (i = 0; i < MAX_REDIRECTS; i++) {
157         const ne_uri *u;
159         if ((ret = ne_proppatch(session, path, ops)) != NE_REDIRECT)
160             return ret;
162         if (!(u = ne_redirect_location(session)))
163             break;
165         if (!session_is_local(u))
166             break;
168         if (debug)
169             fprintf(stderr, "REDIRECT FROM '%s' to '%s'\n", path, u->path);
170         
171         path = u->path;
172     }
174     return ret;
178 static void fill_stat(struct stat* st, const ne_prop_result_set *results, int is_dir) {
179     const char *rt, *e, *gcl, *glm, *cd;
180     const ne_propname resourcetype = { "DAV:", "resourcetype" };
181     const ne_propname executable = { "http://apache.org/dav/props/", "executable" };
182     const ne_propname getcontentlength = { "DAV:", "getcontentlength" };
183     const ne_propname getlastmodified = { "DAV:", "getlastmodified" };
184     const ne_propname creationdate = { "DAV:", "creationdate" };
185         
186     assert(st && results);
188     rt = ne_propset_value(results, &resourcetype);
189     e = ne_propset_value(results, &executable);
190     gcl = ne_propset_value(results, &getcontentlength);
191     glm = ne_propset_value(results, &getlastmodified);
192     cd = ne_propset_value(results, &creationdate);
194     memset(st, 0, sizeof(struct stat));
195     
196     if (is_dir) {
197         st->st_mode = S_IFDIR | 0777;
198         st->st_nlink = 3;            /* find will ignore this directory if nlin <= and st_size == 0 */
199         st->st_size = 4096;
200     } else {
201         st->st_mode = S_IFREG | (e && (*e == 'T' || *e == 't') ? 0777 : 0666);
202         st->st_nlink = 1;
203         st->st_size = gcl ? atoll(gcl) : 0;
204     }
206     st->st_atime = time(NULL);
207     st->st_mtime = glm ? ne_rfc1123_parse(glm) : 0;
208     st->st_ctime = cd ? ne_iso8601_parse(cd) : 0;
209     
210     st->st_blocks = (st->st_size+511)/512;
211     /*fprintf(stderr, "a: %u; m: %u; c: %u\n", st->st_atime, st->st_mtime, st->st_ctime);*/
213     st->st_mode &= ~mask;
214     
215     st->st_uid = getuid();
216     st->st_gid = getgid();
219 static char *strip_trailing_slash(char *fn, int *is_dir) {
220     size_t l = strlen(fn);
221     assert(fn);
222     assert(is_dir);
223     assert(l > 0);
224     
225     if ((*is_dir = (fn[l-1] == '/')))
226         fn[l-1] = 0;
228     return fn;
231 static void getdir_propfind_callback(void *userdata, const char *href, const ne_prop_result_set *results) {
232     struct fill_info *f = userdata;
233     struct stat st;
234     char fn[PATH_MAX], *t;
235     int is_dir = 0;
237     assert(f);
239     strncpy(fn, href, sizeof(fn));
240     fn[sizeof(fn)-1] = 0;
241     strip_trailing_slash(fn, &is_dir);
243     if (strcmp(fn, f->root) && fn[0]) {
244         char *h;
245         
246         if ((t = strrchr(fn, '/')))
247             t++;
248         else
249             t = fn;
251         dir_cache_add(f->root, t);
252         f->filler(f->buf, h = ne_path_unescape(t), NULL, 0);
253         free(h);
254     }
256     fill_stat(&st, results, is_dir);
257     stat_cache_set(fn, &st);
260 static void getdir_cache_callback(
261         const char *root,
262         const char *fn,
263         void *user) {
264     
265     struct fill_info *f = user;
266     char path[PATH_MAX];
267     char *h;
269     assert(f);
270     
271     snprintf(path, sizeof(path), "%s/%s", !strcmp(root, "/") ? "" : root, fn);
272     
273     f->filler(f->buf, h = ne_path_unescape(fn), NULL, 0);
274     free(h);
277 static int dav_readdir(
278         const char *path,
279         void *buf,
280         fuse_fill_dir_t filler,
281         __unused off_t offset,
282         __unused struct fuse_file_info *fi) {
283     
284     struct fill_info f;
285     ne_session *session;
287     path = path_cvt(path);
289     if (debug)
290         fprintf(stderr, "getdir(%s)\n", path);
292     f.buf = buf;
293     f.filler = filler;
294     f.root = path;
296     filler(buf, ".", NULL, 0);
297     filler(buf, "..", NULL, 0);
298     
299     if (dir_cache_enumerate(path, getdir_cache_callback, &f) < 0) {
301         if (debug)
302             fprintf(stderr, "DIR-CACHE-MISS\n");
303         
304         if (!(session = session_get(1))) 
305             return -EIO;
307         dir_cache_begin(path);
308         
309         if (simple_propfind_with_redirect(session, path, NE_DEPTH_ONE, query_properties, getdir_propfind_callback, &f) != NE_OK) {
310             dir_cache_finish(path, 2);
311             fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
312             return -ENOENT;
313         }
315         dir_cache_finish(path, 1);
316     }
318     return 0;
321 static void getattr_propfind_callback(void *userdata, const char *href, const ne_prop_result_set *results) {
322     struct stat *st = (struct stat*) userdata;
323     char fn[PATH_MAX];
324     int is_dir;
326     assert(st);
328     strncpy(fn, href, sizeof(fn));
329     fn[sizeof(fn)-1] = 0;
330     strip_trailing_slash(fn, &is_dir);
331     
332     fill_stat(st, results, is_dir);
333     stat_cache_set(fn, st);
336 static int get_stat(const char *path, struct stat *stbuf) {
337     ne_session *session;
339     if (!(session = session_get(1))) 
340         return -EIO;
342     if (stat_cache_get(path, stbuf) == 0) {
343         return stbuf->st_mode == 0 ? -ENOENT : 0;
344     } else {
345         if (debug)
346             fprintf(stderr, "STAT-CACHE-MISS\n");
348         if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, query_properties, getattr_propfind_callback, stbuf) != NE_OK) {
349             stat_cache_invalidate(path);
350             fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
351             return -ENOENT;
352         }
354         return 0;
355     }
358 static int dav_getattr(const char *path, struct stat *stbuf) {
359     path = path_cvt(path);
360     if (debug)
361         fprintf(stderr, "getattr(%s)\n", path);
362     return get_stat(path, stbuf);
365 static int dav_unlink(const char *path) {
366     int r;
367     struct stat st;
368     ne_session *session;
370     path = path_cvt(path);
372     if (debug)
373         fprintf(stderr, "unlink(%s)\n", path);
375     if (!(session = session_get(1))) 
376         return -EIO;
378     if ((r = get_stat(path, &st)) < 0)
379         return r;
381     if (!S_ISREG(st.st_mode))
382         return -EISDIR;
383     
384     if (ne_delete(session, path)) {
385         fprintf(stderr, "DELETE failed: %s\n", ne_get_error(session));
386         return -ENOENT;
387     }
389     stat_cache_invalidate(path);
390     dir_cache_invalidate_parent(path);
391     
392     return 0;
395 static int dav_rmdir(const char *path) {
396     char fn[PATH_MAX];
397     int r;
398     struct stat st;
399     ne_session *session;
401     path = path_cvt(path);
403     if (debug)
404         fprintf(stderr, "rmdir(%s)\n", path);
406     if (!(session = session_get(1))) 
407         return -EIO;
409     if ((r = get_stat(path, &st)) < 0)
410         return r;
412     if (!S_ISDIR(st.st_mode))
413         return -ENOTDIR;
415     snprintf(fn, sizeof(fn), "%s/", path);
416     
417     if (ne_delete(session, fn)) {
418         fprintf(stderr, "DELETE failed: %s\n", ne_get_error(session));
419         return -ENOENT;
420     }
422     stat_cache_invalidate(path);
423     dir_cache_invalidate_parent(path);
425     return 0;
428 static int dav_mkdir(const char *path, __unused mode_t mode) {
429     char fn[PATH_MAX];
430     ne_session *session;
432     path = path_cvt(path);
434     if (debug)
435         fprintf(stderr, "mkdir(%s)\n", path);
437     if (!(session = session_get(1))) 
438         return -EIO;
440     snprintf(fn, sizeof(fn), "%s/", path);
441     
442     if (ne_mkcol(session, fn)) {
443         fprintf(stderr, "MKCOL failed: %s\n", ne_get_error(session));
444         return -ENOENT;
445     }
447     stat_cache_invalidate(path);
448     dir_cache_invalidate_parent(path);
449     
450     return 0;
453 static int dav_rename(const char *from, const char *to) {
454     ne_session *session;
455     int r = 0;
456     struct stat st;
457     char fn[PATH_MAX], *_from;
459     from = _from = strdup(path_cvt(from));
460     assert(from);
461     to = path_cvt(to);
463     if (debug)
464         fprintf(stderr, "rename(%s, %s)\n", from, to);
466     if (!(session = session_get(1))) {
467         r = -EIO;
468         goto finish;
469     }
471     if ((r = get_stat(from, &st)) < 0)
472         goto finish;
474     if (S_ISDIR(st.st_mode)) {
475         snprintf(fn, sizeof(fn), "%s/", from);
476         from = fn;
477     }
478     
479     if (ne_move(session, 1, from, to)) {
480         fprintf(stderr, "MOVE failed: %s\n", ne_get_error(session));
481         r = -ENOENT;
482         goto finish;
483     }
484     
485     stat_cache_invalidate(from);
486     stat_cache_invalidate(to);
488     dir_cache_invalidate_parent(from);
489     dir_cache_invalidate_parent(to);
491 finish:
493     free(_from);
494     
495     return r;
498 static int dav_release(const char *path, __unused struct fuse_file_info *info) {
499     void *f = NULL;
500     int r = 0;
501     ne_session *session;
503     path = path_cvt(path);
505     if (debug)
506         fprintf(stderr, "release(%s)\n", path);
508     if (!(session = session_get(1))) {
509         r = -EIO;
510         goto finish;
511     }
512     
513     if (!(f = file_cache_get(path))) {
514         fprintf(stderr, "release() called for closed file\n");
515         r = -EFAULT;
516         goto finish;
517     }
519     if (file_cache_close(f) < 0) {
520         r = -errno;
521         goto finish;
522     }
524 finish:
525     if (f)
526         file_cache_unref(f);
527     
528     return r;
531 static int dav_fsync(const char *path, __unused int isdatasync, __unused struct fuse_file_info *info) {
532     void *f = NULL;
533     int r = 0;
534     ne_session *session;
536     path = path_cvt(path);
537     if (debug)
538         fprintf(stderr, "fsync(%s)\n", path);
540     if (!(session = session_get(1))) {
541         r = -EIO;
542         goto finish;
543     }
545     if (!(f = file_cache_get(path))) {
546         fprintf(stderr, "fsync() called for closed file\n");
547         r = -EFAULT;
548         goto finish;
549     }
551     if (file_cache_sync(f) < 0) {
552         r = -errno;
553         goto finish;
554     }
556 finish:
557     
558     if (f)
559         file_cache_unref(f);
561     return r;
564 static int dav_mknod(const char *path, mode_t mode, __unused dev_t rdev) {
565     char tempfile[PATH_MAX];
566     int fd;
567     ne_session *session;
569     path = path_cvt(path);
570     if (debug)
571         fprintf(stderr, "mknod(%s)\n", path);
573     if (!(session = session_get(1))) 
574         return -EIO;
576     if (!S_ISREG(mode))
577         return -ENOTSUP;
579     snprintf(tempfile, sizeof(tempfile), "%s/fusedav-empty-XXXXXX", "/tmp");
580     if ((fd = mkstemp(tempfile)) < 0)
581         return -errno;
582     
583     unlink(tempfile);
584     
585     if (ne_put(session, path, fd)) {
586         fprintf(stderr, "mknod:PUT failed: %s\n", ne_get_error(session));
587         close(fd);
588         return -EACCES;
589     }
591     close(fd);
593     stat_cache_invalidate(path);
594     dir_cache_invalidate_parent(path);
596     return 0;
599 static int dav_open(const char *path, struct fuse_file_info *info) {
600     void *f;
602     if (debug)
603         fprintf(stderr, "open(%s)\n", path);
605     path = path_cvt(path);
607     if (!(f = file_cache_open(path, info->flags)))
608         return -errno;
610     file_cache_unref(f);
612     return 0;
615 static int dav_read(const char *path, char *buf, size_t size, off_t offset, __unused struct fuse_file_info *info) {
616     void *f = NULL;
617     ssize_t r;
618  
619     path = path_cvt(path);
620     
621     if (debug)
622         fprintf(stderr, "read(%s, %lu+%lu)\n", path, (unsigned long) offset, (unsigned long) size);
623     
624     if (!(f = file_cache_get(path))) {
625         fprintf(stderr, "read() called for closed file\n");
626         r = -EFAULT;
627         goto finish;
628     }
630     if ((r = file_cache_read(f, buf, size, offset)) < 0) {
631         r = -errno;
632         goto finish;
633     }
635 finish:
636     if (f)
637         file_cache_unref(f);
638     
639     return r;
642 static int dav_write(const char *path, const char *buf, size_t size, off_t offset, __unused struct fuse_file_info *info) {
643     void *f = NULL;
644     ssize_t r;
646     path = path_cvt(path);
648     if (debug)
649         fprintf(stderr, "write(%s, %lu+%lu)\n", path, (unsigned long) offset, (unsigned long) size);
651     if (!(f = file_cache_get(path))) {
652         fprintf(stderr, "write() called for closed file\n");
653         r = -EFAULT;
654         goto finish;
655     }
657     if ((r = file_cache_write(f, buf, size, offset)) < 0) {
658         r = -errno;
659         goto finish;
660     }
661     
662 finish:
663     if (f)
664         file_cache_unref(f);
665     
666     return r;
670 static int dav_truncate(const char *path, off_t size) {
671     void *f = NULL;
672     int r = 0;
673     ne_session *session;
674     
675     path = path_cvt(path);
676     
677     if (debug)
678         fprintf(stderr, "truncate(%s, %lu)\n", path, (unsigned long) size);
680     if (!(session = session_get(1)))
681         r = -EIO;
682         goto finish;
683     
684     if (!(f = file_cache_get(path))) {
685         fprintf(stderr, "truncate() called for closed file\n");
686         r = -EFAULT;
687         goto finish;
688     }
690     if (file_cache_truncate(f, size) < 0) {
691         r = -errno;
692         goto finish;
693     }
695 finish:
696     if (f)
697         file_cache_unref(f);
698     
699     return r;
702 static int dav_utime(const char *path, struct utimbuf *buf) {
703     ne_session *session;
704     const ne_propname getlastmodified = { "DAV:", "getlastmodified" };
705     ne_proppatch_operation ops[2];
706     int r = 0;
707     char *date;
708     
709     assert(path);
710     assert(buf);
712     path = path_cvt(path);
713     
714     if (debug)
715         fprintf(stderr, "utime(%s, %lu, %lu)\n", path, (unsigned long) buf->actime, (unsigned long) buf->modtime);
716     
717     ops[0].name = &getlastmodified;
718     ops[0].type = ne_propset;
719     ops[0].value = date = ne_rfc1123_date(buf->modtime);
720     ops[1].name = NULL;
722     if (!(session = session_get(1))) {
723         r = -EIO;
724         goto finish;
725     }
727     if (proppatch_with_redirect(session, path, ops)) {
728         fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
729         r = -ENOTSUP;
730         goto finish;
731     }
732     
733     stat_cache_invalidate(path);
735 finish:
736     free(date);
737     
738     return r;
741 static const char *fix_xattr(const char *name) {
742     assert(name);
744     if (!strcmp(name, MIME_XATTR))
745         return "user.webdav(DAV:;getcontenttype)";
747     return name;
750 struct listxattr_info {
751     char *list;
752     size_t space, size;
753 };
755 static int listxattr_iterator(
756         void *userdata,
757         const ne_propname *pname,
758         const char *value,
759         __unused const ne_status *status) {
761     struct listxattr_info *l = userdata;
762     int n;
763     
764     assert(l);
766     if (!value || !pname)
767         return -1;
769     if (l->list) {
770         n = snprintf(l->list, l->space, "user.webdav(%s;%s)", pname->nspace, pname->name) + 1;
771         
772         if (n >= (int) l->space) {
773             l->size += l->space;
774             l->space = 0;
775             return 1;
776             
777         } else {
778             l->size += n;
779             l->space -= n;
780             
781             if (l->list)
782                 l->list += n;
783             
784             return 0;
785         }
786     } else {
787         /* Calculate space */
788         
789         l->size += strlen(pname->nspace) + strlen(pname->name) + 15;
790         return 0;
791     }
794 static void listxattr_propfind_callback(void *userdata, __unused const char *href, const ne_prop_result_set *results) {
795     struct listxattr_info *l = userdata;
796     ne_propset_iterate(results, listxattr_iterator, l);
799 static int dav_listxattr(
800         const char *path,
801         char *list,
802         size_t size) {
803     
804     ne_session *session;
805     struct listxattr_info l;
806     
808     assert(path);
810     path = path_cvt(path);
812     if (debug)
813         fprintf(stderr, "listxattr(%s, .., %lu)\n", path, (unsigned long) size);
815     if (list) {
816         l.list = list;
817         l.space = size-1;
818         l.size = 0;
820         if (l.space >= sizeof(MIME_XATTR)) {
821             memcpy(l.list, MIME_XATTR, sizeof(MIME_XATTR));
822             l.list += sizeof(MIME_XATTR);
823             l.space -= sizeof(MIME_XATTR);
824             l.size += sizeof(MIME_XATTR);
825         }
826         
827     } else {
828         l.list = NULL;
829         l.space = 0;
830         l.size = sizeof(MIME_XATTR);
831     }
832     
833     if (!(session = session_get(1))) 
834         return -EIO;
836     if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, NULL, listxattr_propfind_callback, &l) != NE_OK) {
837         fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
838         return -EIO;
839     }
841     if (l.list) {
842         assert(l.space > 0);
843         *l.list = 0;
844     }
846     return l.size+1;
849 struct getxattr_info {
850     ne_propname propname;
851     char *value;
852     size_t space, size;
853 };
855 static int getxattr_iterator(
856         void *userdata,
857         const ne_propname *pname,
858         const char *value,
859         __unused const ne_status *status) {
861     struct getxattr_info *g = userdata;
862     
863     assert(g);
865     if (!value || !pname)
866         return -1;
868     if (strcmp(pname->nspace, g->propname.nspace) ||
869         strcmp(pname->name, g->propname.name))
870         return 0;
872     if (g->value) {
873         size_t l;
875         l = strlen(value);
877         if (l > g->space)
878             l = g->space;
880         memcpy(g->value, value, l);
881         g->size = l;
882     } else {
883         /* Calculate space */
884         
885         g->size = strlen(value);
886         return 0;
887     }
888     
889     return 0;
892 static void getxattr_propfind_callback(void *userdata, __unused const char *href, const ne_prop_result_set *results) {
893     struct getxattr_info *g = userdata;
894     ne_propset_iterate(results, getxattr_iterator, g);
897 static int parse_xattr(const char *name, char *dnspace, size_t dnspace_length, char *dname, size_t dname_length) {
898     char *e;
899     size_t k;
900     
901     assert(name);
902     assert(dnspace);
903     assert(dnspace_length);
904     assert(dname);
905     assert(dname_length);
906     
907     if (strncmp(name, "user.webdav(", 12) ||
908         name[strlen(name)-1] != ')' ||
909         !(e = strchr(name+12, ';')))
910         return -1;
912     if ((k = strcspn(name+12, ";")) > dnspace_length-1)
913         return -1;
915     memcpy(dnspace, name+12, k);
916     dnspace[k] = 0;
918     e++;
919     
920     if ((k = strlen(e)) > dname_length-1)
921         return -1;
923     assert(k > 0);
924     k--;
926     memcpy(dname, e, k);
927     dname[k] = 0;
929     return 0;
932 static int dav_getxattr(
933         const char *path,
934         const char *name,
935         char *value,
936         size_t size) {
938     ne_session *session;
939     struct getxattr_info g;
940     ne_propname props[2];
941     char dnspace[128], dname[128];
942         
943     assert(path);
945     path = path_cvt(path);
946     name = fix_xattr(name);
948     if (debug)
949         fprintf(stderr, "getxattr(%s, %s, .., %lu)\n", path, name, (unsigned long) size);
951     if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0)
952         return -ENOATTR;
954     props[0].nspace = dnspace;
955     props[0].name = dname;
956     props[1].nspace = NULL;
957     props[1].name = NULL;
959     if (value) {
960         g.value = value;
961         g.space = size;
962         g.size = (size_t) -1;
963     } else {
964         g.value = NULL;
965         g.space = 0;
966         g.size = (size_t) -1;
967     }
969     g.propname = props[0];
970     
971     if (!(session = session_get(1)))
972         return -EIO;
974     if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, props, getxattr_propfind_callback, &g) != NE_OK) {
975         fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
976         return -EIO;
977     }
979     if (g.size == (size_t) -1)
980         return -ENOATTR;
982     return g.size;
985 static int dav_setxattr(
986         const char *path,
987         const char *name,
988         const char *value,
989         size_t size,
990         int flags) {
992     ne_session *session;
993     ne_propname propname;
994     ne_proppatch_operation ops[2];
995     int r = 0;
996     char dnspace[128], dname[128];
997     char *value_fixed = NULL;
999     assert(path);
1000     assert(name);
1001     assert(value);
1003     path = path_cvt(path);
1004     name = fix_xattr(name);
1005     
1006     if (debug)
1007         fprintf(stderr, "setxattr(%s, %s)\n", path, name);
1009     if (flags) {
1010         r = ENOTSUP;
1011         goto finish;
1012     }
1013     
1014     if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0) {
1015         r = -ENOATTR;
1016         goto finish;
1017     }
1019     propname.nspace = dnspace;
1020     propname.name = dname;
1021     
1022     /* Add trailing NUL byte if required */
1023     if (!memchr(value, 0, size)) {
1024         value_fixed = malloc(size+1);
1025         assert(value_fixed);
1026         
1027         memcpy(value_fixed, value, size);
1028         value_fixed[size] = 0;
1030         value = value_fixed;
1031     }
1032     
1033     ops[0].name = &propname;
1034     ops[0].type = ne_propset;
1035     ops[0].value = value;
1036     
1037     ops[1].name = NULL;
1039     if (!(session = session_get(1))) {
1040         r = -EIO;
1041         goto finish;
1042     }
1043                  
1044     if (proppatch_with_redirect(session, path, ops)) {
1045         fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1046         r = -ENOTSUP;
1047         goto finish;
1048     }
1049     
1050     stat_cache_invalidate(path);
1052 finish:
1053     free(value_fixed);
1054     
1055     return r;
1058 static int dav_removexattr(const char *path, const char *name) {
1059     ne_session *session;
1060     ne_propname propname;
1061     ne_proppatch_operation ops[2];
1062     int r = 0;
1063     char dnspace[128], dname[128];
1065     assert(path);
1066     assert(name);
1068     path = path_cvt(path);
1069     name = fix_xattr(name);
1070     
1071     if (debug)
1072         fprintf(stderr, "removexattr(%s, %s)\n", path, name);
1074     if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0) {
1075         r = -ENOATTR;
1076         goto finish;
1077     }
1079     propname.nspace = dnspace;
1080     propname.name = dname;
1081     
1082     ops[0].name = &propname;
1083     ops[0].type = ne_propremove;
1084     ops[0].value = NULL;
1085     
1086     ops[1].name = NULL;
1088     if (!(session = session_get(1))) {
1089         r = -EIO;
1090         goto finish;
1091     }
1092                  
1093     if (proppatch_with_redirect(session, path, ops)) {
1094         fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1095         r = -ENOTSUP;
1096         goto finish;
1097     }
1098     
1099     stat_cache_invalidate(path);
1101 finish:
1102     
1103     return r;
1106 static int dav_chmod(const char *path, mode_t mode) {
1107     ne_session *session;
1108     const ne_propname executable = { "http://apache.org/dav/props/", "executable" };
1109     ne_proppatch_operation ops[2];
1110     int r = 0;
1111     
1112     assert(path);
1114     path = path_cvt(path);
1115     
1116     if (debug)
1117         fprintf(stderr, "chmod(%s, %04o)\n", path, mode);
1118     
1119     ops[0].name = &executable;
1120     ops[0].type = ne_propset;
1121     ops[0].value = mode & 0111 ? "T" : "F";
1122     ops[1].name = NULL;
1124     if (!(session = session_get(1))) {
1125         r = -EIO;
1126         goto finish;
1127     }
1129     if (proppatch_with_redirect(session, path, ops)) {
1130         fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1131         r = -ENOTSUP;
1132         goto finish;
1133     }
1134     
1135     stat_cache_invalidate(path);
1137 finish:
1138     
1139     return r;
1142 static struct fuse_operations dav_oper = {
1143     .getattr     = dav_getattr,
1144     .readdir     = dav_readdir,
1145     .mknod       = dav_mknod,
1146     .mkdir       = dav_mkdir,
1147     .unlink      = dav_unlink,
1148     .rmdir       = dav_rmdir,
1149     .rename      = dav_rename,
1150     .chmod       = dav_chmod,
1151     .truncate    = dav_truncate,
1152     .utime       = dav_utime,
1153     .open        = dav_open,
1154     .read        = dav_read,
1155     .write       = dav_write,
1156     .release     = dav_release,
1157     .fsync       = dav_fsync,
1158     .setxattr    = dav_setxattr,
1159     .getxattr    = dav_getxattr,
1160     .listxattr   = dav_listxattr,
1161     .removexattr = dav_removexattr,
1162 };
1164 static void usage(char *argv0) {
1165     char *e;
1167     if ((e = strrchr(argv0, '/')))
1168         e++;
1169     else
1170         e = argv0;
1171     
1172     fprintf(stderr,
1173             "%s [-hDL] [-t SECS] [-u USERNAME] [-p PASSWORD] [-o OPTIONS] URL MOUNTPOINT\n"
1174             "\t-h Show this help\n"
1175             "\t-D Enable debug mode\n"
1176             "\t-u Username if required\n"
1177             "\t-p Password if required\n"
1178             "\t-o Additional FUSE mount options\n"
1179             "\t-L Locking the repository during mount\n"
1180             "\t-t Set lock timeout\n",
1181             e);
1184 static void exit_handler(__unused int sig) {
1185     static const char m[] = "*** Caught signal ***\n";
1186     if(fuse != NULL)
1187         fuse_exit(fuse);
1188     write(2, m, strlen(m));
1191 static void empty_handler(__unused int sig) {}
1193 static int setup_signal_handlers(void) {
1194     struct sigaction sa;
1195     sigset_t m;
1196                                                                             
1197     sa.sa_handler = exit_handler;
1198     sigemptyset(&(sa.sa_mask));
1199     sa.sa_flags = 0;
1200     
1201     if (sigaction(SIGHUP, &sa, NULL) == -1 ||
1202         sigaction(SIGINT, &sa, NULL) == -1 ||
1203         sigaction(SIGTERM, &sa, NULL) == -1) {
1204                                                                                                         
1205         fprintf(stderr, "Cannot set exit signal handlers: %s\n", strerror(errno));
1206         return -1;
1207     }
1208                                                                                                         
1209     sa.sa_handler = SIG_IGN;
1210                                                                                                         
1211     if (sigaction(SIGPIPE, &sa, NULL) == -1) {
1212         fprintf(stderr, "Cannot set ignored signals: %s\n", strerror(errno));
1213         return -1;
1214     }
1216     /* Used to shut down the locking thread */
1217     sa.sa_handler = empty_handler;
1219     if (sigaction(SIGUSR1, &sa, NULL) == -1) {
1220         fprintf(stderr, "Cannot set user signals: %s\n", strerror(errno));
1221         return -1;
1222     }
1224     sigemptyset(&m);
1225     pthread_sigmask(SIG_BLOCK, &m, &m);
1226     sigdelset(&m, SIGHUP);
1227     sigdelset(&m, SIGINT);
1228     sigdelset(&m, SIGTERM);
1229     sigaddset(&m, SIGPIPE);
1230     sigaddset(&m, SIGUSR1);
1231     pthread_sigmask(SIG_SETMASK, &m, NULL);
1232     
1233     return 0;
1236 static int create_lock(void) {
1237     ne_session *session;
1238     char _owner[64], *owner;
1239     int i;
1240     int ret;
1241     
1242     lock = ne_lock_create();
1243     assert(lock);
1245     if (!(session = session_get(0)))
1246         return -1;
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     openssl_thread_setup();
1390     mask = umask(0);
1391     umask(mask);
1393     cache_alloc();
1395     if (setup_signal_handlers() < 0)
1396         goto finish;
1397     
1398     while ((c = getopt(argc, argv, "hu:p:Do:Lt:")) != -1) {
1400         switch(c) {
1401             case 'u':
1402                 u = optarg;
1403                 break;
1404                 
1405             case 'p':
1406                 p = optarg;
1407                 break;
1408                 
1409             case 'D':
1410                 debug = !debug;
1411                 break;
1413             case 'o':
1414                 o = optarg;
1415                 break;
1417             case 'L':
1418                 enable_locking = 1;
1419                 break;
1421             case 't':
1422                 if ((lock_timeout = atoi(optarg)) < 0) {
1423                     fprintf(stderr, "Invalid lock timeout '%s'\n", optarg);
1424                     goto finish;
1425                 }
1426                 break;
1427                 
1428             case 'h':
1429                 ret = 0;
1431                 /* fall through */
1432             default:
1433                 usage(argv[0]);
1434                 goto finish;
1435         }
1436     }
1438     if (optind != argc-2) {
1439         usage(argv[0]);
1440         goto finish;
1441     }
1443     if (session_set_uri(argv[optind], u, p) < 0) {
1444         usage(argv[0]);
1445         goto finish;
1446     }
1448     if (argv[optind+1][0] == '/')
1449         snprintf(mountpoint, sizeof(mountpoint), "%s", argv[optind+1]);
1450     else {
1451         char *pwd = get_current_dir_name();
1452         snprintf(mountpoint, sizeof(mountpoint), "%s/%s", pwd, argv[optind+1]);
1453         free(pwd);
1454     }
1456     mount_args_strings[0] = argv[optind];
1458     if (o) {
1459         mount_args_strings[1] = (char*) "-o";
1460         mount_args_strings[2] = o;
1461         mount_args.argc += 2;
1462     }
1463     
1464     if ((fuse_fd = fuse_mount(mountpoint, &mount_args)) < 0) {
1465         fprintf(stderr, "Failed to mount FUSE file system.\n");
1466         goto finish;
1467     }
1469     if (!(fuse = fuse_new(fuse_fd, &mount_args, &dav_oper, sizeof(dav_oper)))) {
1470         fprintf(stderr, "Failed to create FUSE object.\n");
1471         goto finish;
1472     }
1473     
1474     if (enable_locking && create_lock() >= 0) {
1475         int r;
1476         if ((r = pthread_create(&lock_thread, NULL, lock_thread_func, NULL)) < 0) {
1477             fprintf(stderr, "pthread_create(): %s\n", strerror(r));
1478             goto finish;
1479         }
1480         
1481         lock_thread_running = 1;
1482     }
1483     
1484     fuse_loop_mt(fuse);
1486     if (debug)
1487         fprintf(stderr, "Exiting cleanly.\n");
1488     
1489     ret = 0;
1490     
1491 finish:
1492     
1493     if (lock_thread_running) {
1494         lock_thread_exit = 1;
1495         pthread_kill(lock_thread, SIGUSR1);
1496         pthread_join(lock_thread, NULL);
1497         remove_lock();
1498         ne_lockstore_destroy(lock_store);
1499     }
1501     if (fuse)
1502         fuse_destroy(fuse);
1503     
1504     if (fuse_fd >= 0)
1505         fuse_unmount(mountpoint);
1506     
1507     file_cache_close_all();
1508     cache_free();
1509     session_free();
1510     openssl_thread_cleanup();
1511     
1512     return ret;