Code

d145e836b2a668a50b9652cf6a8d765b31d307d7
[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;
74 #define MIME_XATTR "user.mime_type"
76 #define MAX_REDIRECTS 10
77 #define LOCK_TIMEOUT 8
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, is_dir);
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         int is_dir,
264         void *user) {
265     
266     struct fill_info *f = user;
267     char path[PATH_MAX];
268     char *h;
270     assert(f);
271     
272     snprintf(path, sizeof(path), "%s/%s", !strcmp(root, "/") ? "" : root, fn);
273     
274     f->filler(f->buf, h = ne_path_unescape(fn), NULL, 0);
275     free(h);
278 static int dav_readdir(
279         const char *path,
280         void *buf,
281         fuse_fill_dir_t filler,
282         off_t offset,
283         struct fuse_file_info *fi) {
284     
285     struct fill_info f;
286     ne_session *session;
288     path = path_cvt(path);
290     if (debug)
291         fprintf(stderr, "getdir(%s)\n", path);
293     f.buf = buf;
294     f.filler = filler;
295     f.root = path;
297     filler(buf, ".", NULL, 0);
298     filler(buf, "..", NULL, 0);
299     
300     if (dir_cache_enumerate(path, getdir_cache_callback, &f) < 0) {
302         if (debug)
303             fprintf(stderr, "DIR-CACHE-MISS\n");
304         
305         if (!(session = session_get(1))) 
306             return -EIO;
308         dir_cache_begin(path);
309         
310         if (simple_propfind_with_redirect(session, path, NE_DEPTH_ONE, query_properties, getdir_propfind_callback, &f) != NE_OK) {
311             dir_cache_finish(path, 2);
312             fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
313             return -ENOENT;
314         }
316         dir_cache_finish(path, 1);
317     }
319     return 0;
322 static void getattr_propfind_callback(void *userdata, const char *href, const ne_prop_result_set *results) {
323     struct stat *st = (struct stat*) userdata;
324     char fn[PATH_MAX];
325     int is_dir;
327     assert(st);
329     strncpy(fn, href, sizeof(fn));
330     fn[sizeof(fn)-1] = 0;
331     strip_trailing_slash(fn, &is_dir);
332     
333     fill_stat(st, results, is_dir);
334     stat_cache_set(fn, st);
337 static int get_stat(const char *path, struct stat *stbuf) {
338     ne_session *session;
340     if (!(session = session_get(1))) 
341         return -EIO;
343     if (stat_cache_get(path, stbuf) == 0) {
344         return stbuf->st_mode == 0 ? -ENOENT : 0;
345     } else {
346         if (debug)
347             fprintf(stderr, "STAT-CACHE-MISS\n");
349         if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, query_properties, getattr_propfind_callback, stbuf) != NE_OK) {
350             stat_cache_invalidate(path);
351             fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
352             return -ENOENT;
353         }
355         return 0;
356     }
359 static int dav_getattr(const char *path, struct stat *stbuf) {
360     path = path_cvt(path);
361     if (debug)
362         fprintf(stderr, "getattr(%s)\n", path);
363     return get_stat(path, stbuf);
366 static int dav_unlink(const char *path) {
367     int r;
368     struct stat st;
369     ne_session *session;
371     path = path_cvt(path);
373     if (debug)
374         fprintf(stderr, "unlink(%s)\n", path);
376     if (!(session = session_get(1))) 
377         return -EIO;
379     if ((r = get_stat(path, &st)) < 0)
380         return r;
382     if (!S_ISREG(st.st_mode))
383         return -EISDIR;
384     
385     if (ne_delete(session, path)) {
386         fprintf(stderr, "DELETE failed: %s\n", ne_get_error(session));
387         return -ENOENT;
388     }
390     stat_cache_invalidate(path);
391     dir_cache_invalidate_parent(path);
392     
393     return 0;
396 static int dav_rmdir(const char *path) {
397     char fn[PATH_MAX];
398     int r;
399     struct stat st;
400     ne_session *session;
402     path = path_cvt(path);
404     if (debug)
405         fprintf(stderr, "rmdir(%s)\n", path);
407     if (!(session = session_get(1))) 
408         return -EIO;
410     if ((r = get_stat(path, &st)) < 0)
411         return r;
413     if (!S_ISDIR(st.st_mode))
414         return -ENOTDIR;
416     snprintf(fn, sizeof(fn), "%s/", path);
417     
418     if (ne_delete(session, fn)) {
419         fprintf(stderr, "DELETE failed: %s\n", ne_get_error(session));
420         return -ENOENT;
421     }
423     stat_cache_invalidate(path);
424     dir_cache_invalidate_parent(path);
426     return 0;
429 static int dav_mkdir(const char *path, mode_t mode) {
430     char fn[PATH_MAX];
431     ne_session *session;
433     path = path_cvt(path);
435     if (debug)
436         fprintf(stderr, "mkdir(%s)\n", path);
438     if (!(session = session_get(1))) 
439         return -EIO;
441     snprintf(fn, sizeof(fn), "%s/", path);
442     
443     if (ne_mkcol(session, fn)) {
444         fprintf(stderr, "MKCOL failed: %s\n", ne_get_error(session));
445         return -ENOENT;
446     }
448     stat_cache_invalidate(path);
449     dir_cache_invalidate_parent(path);
450     
451     return 0;
454 static int dav_rename(const char *from, const char *to) {
455     ne_session *session;
456     int r = 0;
457     struct stat st;
458     char fn[PATH_MAX], *_from;
460     from = _from = strdup(path_cvt(from));
461     assert(from);
462     to = path_cvt(to);
464     if (debug)
465         fprintf(stderr, "rename(%s, %s)\n", from, to);
467     if (!(session = session_get(1))) {
468         r = -EIO;
469         goto finish;
470     }
472     if ((r = get_stat(from, &st)) < 0)
473         goto finish;
475     if (S_ISDIR(st.st_mode)) {
476         snprintf(fn, sizeof(fn), "%s/", from);
477         from = fn;
478     }
479     
480     if (ne_move(session, 1, from, to)) {
481         fprintf(stderr, "MOVE failed: %s\n", ne_get_error(session));
482         r = -ENOENT;
483         goto finish;
484     }
485     
486     stat_cache_invalidate(from);
487     stat_cache_invalidate(to);
489     dir_cache_invalidate_parent(from);
490     dir_cache_invalidate_parent(to);
492 finish:
494     free(_from);
495     
496     return r;
499 static int dav_release(const char *path, struct fuse_file_info *info) {
500     void *f = NULL;
501     int r = 0;
502     ne_session *session;
504     path = path_cvt(path);
506     if (debug)
507         fprintf(stderr, "release(%s)\n", path);
509     if (!(session = session_get(1))) {
510         r = -EIO;
511         goto finish;
512     }
513     
514     if (!(f = file_cache_get(path))) {
515         fprintf(stderr, "release() called for closed file\n");
516         r = -EFAULT;
517         goto finish;
518     }
520     if (file_cache_close(f) < 0) {
521         r = -errno;
522         goto finish;
523     }
525 finish:
526     if (f)
527         file_cache_unref(f);
528     
529     return r;
532 static int dav_fsync(const char *path, __unused int isdatasync, struct fuse_file_info *info) {
533     void *f = NULL;
534     int r = 0;
535     ne_session *session;
537     path = path_cvt(path);
538     if (debug)
539         fprintf(stderr, "fsync(%s)\n", path);
541     if (!(session = session_get(1))) {
542         r = -EIO;
543         goto finish;
544     }
546     if (!(f = file_cache_get(path))) {
547         fprintf(stderr, "fsync() called for closed file\n");
548         r = -EFAULT;
549         goto finish;
550     }
552     if (file_cache_sync(f) < 0) {
553         r = -errno;
554         goto finish;
555     }
557 finish:
558     
559     if (f)
560         file_cache_unref(f);
562     return r;
565 static int dav_mknod(const char *path, mode_t mode, dev_t rdev) {
566     char tempfile[PATH_MAX];
567     int fd;
568     ne_session *session;
570     path = path_cvt(path);
571     if (debug)
572         fprintf(stderr, "mknod(%s)\n", path);
574     if (!(session = session_get(1))) 
575         return -EIO;
577     if (!S_ISREG(mode))
578         return -ENOTSUP;
580     snprintf(tempfile, sizeof(tempfile), "%s/fusedav-empty-XXXXXX", "/tmp");
581     if ((fd = mkstemp(tempfile)) < 0)
582         return -errno;
583     
584     unlink(tempfile);
585     
586     if (ne_put(session, path, fd)) {
587         fprintf(stderr, "mknod:PUT failed: %s\n", ne_get_error(session));
588         close(fd);
589         return -EACCES;
590     }
592     close(fd);
594     stat_cache_invalidate(path);
595     dir_cache_invalidate_parent(path);
597     return 0;
600 static int dav_open(const char *path, struct fuse_file_info *info) {
601     void *f;
603     if (debug)
604         fprintf(stderr, "open(%s)\n", path);
606     path = path_cvt(path);
608     if (!(f = file_cache_open(path, info->flags)))
609         return -errno;
611     file_cache_unref(f);
613     return 0;
616 static int dav_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *info) {
617     void *f = NULL;
618     ssize_t r;
619  
620     path = path_cvt(path);
621     
622     if (debug)
623         fprintf(stderr, "read(%s, %lu+%lu)\n", path, (unsigned long) offset, (unsigned long) size);
624     
625     if (!(f = file_cache_get(path))) {
626         fprintf(stderr, "read() called for closed file\n");
627         r = -EFAULT;
628         goto finish;
629     }
631     if ((r = file_cache_read(f, buf, size, offset)) < 0) {
632         r = -errno;
633         goto finish;
634     }
636     fprintf(stderr, "read: %i\n", r);
638 finish:
639     if (f)
640         file_cache_unref(f);
641     
642     return r;
645 static int dav_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *info) {
646     void *f = NULL;
647     ssize_t r;
649     path = path_cvt(path);
651     if (debug)
652         fprintf(stderr, "write(%s, %lu+%lu)\n", path, (unsigned long) offset, (unsigned long) size);
654     if (!(f = file_cache_get(path))) {
655         fprintf(stderr, "write() called for closed file\n");
656         r = -EFAULT;
657         goto finish;
658     }
660     if ((r = file_cache_write(f, buf, size, offset)) < 0) {
661         r = -errno;
662         goto finish;
663     }
664     
665 finish:
666     if (f)
667         file_cache_unref(f);
668     
669     return r;
673 static int dav_truncate(const char *path, off_t size) {
674     void *f = NULL;
675     int r = 0;
676     ne_session *session;
677     
678     path = path_cvt(path);
679     
680     if (debug)
681         fprintf(stderr, "truncate(%s, %lu)\n", path, (unsigned long) size);
683     if (!(session = session_get(1)))
684         r = -EIO;
685         goto finish;
686     
687     if (!(f = file_cache_get(path))) {
688         fprintf(stderr, "truncate() called for closed file\n");
689         r = -EFAULT;
690         goto finish;
691     }
693     if (file_cache_truncate(f, size) < 0) {
694         r = -errno;
695         goto finish;
696     }
698 finish:
699     if (f)
700         file_cache_unref(f);
701     
702     return r;
705 static int dav_utime(const char *path, struct utimbuf *buf) {
706     ne_session *session;
707     const ne_propname getlastmodified = { "DAV:", "getlastmodified" };
708     ne_proppatch_operation ops[2];
709     int r = 0;
710     char *date;
711     
712     assert(path);
713     assert(buf);
715     path = path_cvt(path);
716     
717     if (debug)
718         fprintf(stderr, "utime(%s, %lu, %lu)\n", path, (unsigned long) buf->actime, (unsigned long) buf->modtime);
719     
720     ops[0].name = &getlastmodified;
721     ops[0].type = ne_propset;
722     ops[0].value = date = ne_rfc1123_date(buf->modtime);
723     ops[1].name = NULL;
725     if (!(session = session_get(1))) {
726         r = -EIO;
727         goto finish;
728     }
730     if (proppatch_with_redirect(session, path, ops)) {
731         fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
732         r = -ENOTSUP;
733         goto finish;
734     }
735     
736     stat_cache_invalidate(path);
738 finish:
739     free(date);
740     
741     return r;
744 static const char *fix_xattr(const char *name) {
745     assert(name);
747     if (!strcmp(name, MIME_XATTR))
748         return "user.webdav(DAV:;getcontenttype)";
750     return name;
753 struct listxattr_info {
754     char *list;
755     size_t space, size;
756 };
758 static int listxattr_iterator(
759         void *userdata,
760         const ne_propname *pname,
761         const char *value,
762         const ne_status *status) {
764     struct listxattr_info *l = userdata;
765     int n;
766     
767     assert(l);
769     if (!value || !pname)
770         return -1;
772     if (l->list) {
773         n = snprintf(l->list, l->space, "user.webdav(%s;%s)", pname->nspace, pname->name) + 1;
774         
775         if (n >= (int) l->space) {
776             l->size += l->space;
777             l->space = 0;
778             return 1;
779             
780         } else {
781             l->size += n;
782             l->space -= n;
783             
784             if (l->list)
785                 l->list += n;
786             
787             return 0;
788         }
789     } else {
790         /* Calculate space */
791         
792         l->size += strlen(pname->nspace) + strlen(pname->name) + 15;
793         return 0;
794     }
797 static void listxattr_propfind_callback(void *userdata, const char *href, const ne_prop_result_set *results) {
798     struct listxattr_info *l = userdata;
799     ne_propset_iterate(results, listxattr_iterator, l);
802 static int dav_listxattr(
803         const char *path,
804         char *list,
805         size_t size) {
806     
807     ne_session *session;
808     struct listxattr_info l;
809     
811     assert(path);
813     path = path_cvt(path);
815     if (debug)
816         fprintf(stderr, "listxattr(%s, .., %lu)\n", path, (unsigned long) size);
818     if (list) {
819         l.list = list;
820         l.space = size-1;
821         l.size = 0;
823         if (l.space >= sizeof(MIME_XATTR)) {
824             memcpy(l.list, MIME_XATTR, sizeof(MIME_XATTR));
825             l.list += sizeof(MIME_XATTR);
826             l.space -= sizeof(MIME_XATTR);
827             l.size += sizeof(MIME_XATTR);
828         }
829         
830     } else {
831         l.list = NULL;
832         l.space = 0;
833         l.size = sizeof(MIME_XATTR);
834     }
835     
836     if (!(session = session_get(1))) 
837         return -EIO;
839     if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, NULL, listxattr_propfind_callback, &l) != NE_OK) {
840         fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
841         return -EIO;
842     }
844     if (l.list) {
845         assert(l.space > 0);
846         *l.list = 0;
847     }
849     return l.size+1;
852 struct getxattr_info {
853     ne_propname propname;
854     char *value;
855     size_t space, size;
856 };
858 static int getxattr_iterator(
859         void *userdata,
860         const ne_propname *pname,
861         const char *value,
862         const ne_status *status) {
864     struct getxattr_info *g = userdata;
865     
866     assert(g);
868     if (!value || !pname)
869         return -1;
871     if (strcmp(pname->nspace, g->propname.nspace) ||
872         strcmp(pname->name, g->propname.name))
873         return 0;
875     if (g->value) {
876         size_t l;
878         l = strlen(value);
880         if (l > g->space)
881             l = g->space;
883         memcpy(g->value, value, l);
884         g->size = l;
885     } else {
886         /* Calculate space */
887         
888         g->size = strlen(value);
889         return 0;
890     }
891     
892     return 0;
895 static void getxattr_propfind_callback(void *userdata, const char *href, const ne_prop_result_set *results) {
896     struct getxattr_info *g = userdata;
897     ne_propset_iterate(results, getxattr_iterator, g);
900 static int parse_xattr(const char *name, char *dnspace, size_t dnspace_length, char *dname, size_t dname_length) {
901     char *e;
902     size_t k;
903     
904     assert(name);
905     assert(dnspace);
906     assert(dnspace_length);
907     assert(dname);
908     assert(dname_length);
909     
910     if (strncmp(name, "user.webdav(", 12) ||
911         name[strlen(name)-1] != ')' ||
912         !(e = strchr(name+12, ';')))
913         return -1;
915     if ((k = strcspn(name+12, ";")) > dnspace_length-1)
916         return -1;
918     memcpy(dnspace, name+12, k);
919     dnspace[k] = 0;
921     e++;
922     
923     if ((k = strlen(e)) > dname_length-1)
924         return -1;
926     assert(k > 0);
927     k--;
929     memcpy(dname, e, k);
930     dname[k] = 0;
932     return 0;
935 static int dav_getxattr(
936         const char *path,
937         const char *name,
938         char *value,
939         size_t size) {
941     ne_session *session;
942     struct getxattr_info g;
943     ne_propname props[2];
944     char dnspace[128], dname[128];
945         
946     assert(path);
948     path = path_cvt(path);
949     name = fix_xattr(name);
951     if (debug)
952         fprintf(stderr, "getxattr(%s, %s, .., %lu)\n", path, name, (unsigned long) size);
954     if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0)
955         return -ENOATTR;
957     props[0].nspace = dnspace;
958     props[0].name = dname;
959     props[1].nspace = NULL;
960     props[1].name = NULL;
962     if (value) {
963         g.value = value;
964         g.space = size;
965         g.size = (size_t) -1;
966     } else {
967         g.value = NULL;
968         g.space = 0;
969         g.size = (size_t) -1;
970     }
972     g.propname = props[0];
973     
974     if (!(session = session_get(1)))
975         return -EIO;
977     if (simple_propfind_with_redirect(session, path, NE_DEPTH_ZERO, props, getxattr_propfind_callback, &g) != NE_OK) {
978         fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
979         return -EIO;
980     }
982     if (g.size == (size_t) -1)
983         return -ENOATTR;
985     return g.size;
988 static int dav_setxattr(
989         const char *path,
990         const char *name,
991         const char *value,
992         size_t size,
993         int flags) {
995     ne_session *session;
996     ne_propname propname;
997     ne_proppatch_operation ops[2];
998     int r = 0;
999     char dnspace[128], dname[128];
1000     char *value_fixed = NULL;
1002     assert(path);
1003     assert(name);
1004     assert(value);
1006     path = path_cvt(path);
1007     name = fix_xattr(name);
1008     
1009     if (debug)
1010         fprintf(stderr, "setxattr(%s, %s)\n", path, name);
1012     if (flags) {
1013         r = ENOTSUP;
1014         goto finish;
1015     }
1016     
1017     if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0) {
1018         r = -ENOATTR;
1019         goto finish;
1020     }
1022     propname.nspace = dnspace;
1023     propname.name = dname;
1024     
1025     /* Add trailing NUL byte if required */
1026     if (!memchr(value, 0, size)) {
1027         value_fixed = malloc(size+1);
1028         assert(value_fixed);
1029         
1030         memcpy(value_fixed, value, size);
1031         value_fixed[size] = 0;
1033         value = value_fixed;
1034     }
1035     
1036     ops[0].name = &propname;
1037     ops[0].type = ne_propset;
1038     ops[0].value = value;
1039     
1040     ops[1].name = NULL;
1042     if (!(session = session_get(1))) {
1043         r = -EIO;
1044         goto finish;
1045     }
1046                  
1047     if (proppatch_with_redirect(session, path, ops)) {
1048         fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1049         r = -ENOTSUP;
1050         goto finish;
1051     }
1052     
1053     stat_cache_invalidate(path);
1055 finish:
1056     free(value_fixed);
1057     
1058     return r;
1061 static int dav_removexattr(const char *path, const char *name) {
1062     ne_session *session;
1063     ne_propname propname;
1064     ne_proppatch_operation ops[2];
1065     int r = 0;
1066     char dnspace[128], dname[128];
1068     assert(path);
1069     assert(name);
1071     path = path_cvt(path);
1072     name = fix_xattr(name);
1073     
1074     if (debug)
1075         fprintf(stderr, "removexattr(%s, %s)\n", path, name);
1077     if (parse_xattr(name, dnspace, sizeof(dnspace), dname, sizeof(dname)) < 0) {
1078         r = -ENOATTR;
1079         goto finish;
1080     }
1082     propname.nspace = dnspace;
1083     propname.name = dname;
1084     
1085     ops[0].name = &propname;
1086     ops[0].type = ne_propremove;
1087     ops[0].value = NULL;
1088     
1089     ops[1].name = NULL;
1091     if (!(session = session_get(1))) {
1092         r = -EIO;
1093         goto finish;
1094     }
1095                  
1096     if (proppatch_with_redirect(session, path, ops)) {
1097         fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1098         r = -ENOTSUP;
1099         goto finish;
1100     }
1101     
1102     stat_cache_invalidate(path);
1104 finish:
1105     
1106     return r;
1109 static int dav_chmod(const char *path, mode_t mode) {
1110     ne_session *session;
1111     const ne_propname executable = { "http://apache.org/dav/props/", "executable" };
1112     ne_proppatch_operation ops[2];
1113     int r = 0;
1114     
1115     assert(path);
1117     path = path_cvt(path);
1118     
1119     if (debug)
1120         fprintf(stderr, "chmod(%s, %04o)\n", path, mode);
1121     
1122     ops[0].name = &executable;
1123     ops[0].type = ne_propset;
1124     ops[0].value = mode & 0111 ? "T" : "F";
1125     ops[1].name = NULL;
1127     if (!(session = session_get(1))) {
1128         r = -EIO;
1129         goto finish;
1130     }
1132     if (proppatch_with_redirect(session, path, ops)) {
1133         fprintf(stderr, "PROPPATCH failed: %s\n", ne_get_error(session));
1134         r = -ENOTSUP;
1135         goto finish;
1136     }
1137     
1138     stat_cache_invalidate(path);
1140 finish:
1141     
1142     return r;
1145 static struct fuse_operations dav_oper = {
1146     .getattr     = dav_getattr,
1147     .readdir     = dav_readdir,
1148     .mknod       = dav_mknod,
1149     .mkdir       = dav_mkdir,
1150     .unlink      = dav_unlink,
1151     .rmdir       = dav_rmdir,
1152     .rename      = dav_rename,
1153     .chmod       = dav_chmod,
1154     .truncate    = dav_truncate,
1155     .utime       = dav_utime,
1156     .open        = dav_open,
1157     .read        = dav_read,
1158     .write       = dav_write,
1159     .release     = dav_release,
1160     .fsync       = dav_fsync,
1161     .setxattr    = dav_setxattr,
1162     .getxattr    = dav_getxattr,
1163     .listxattr   = dav_listxattr,
1164     .removexattr = dav_removexattr,
1165 };
1167 static void usage(char *argv0) {
1168     char *e;
1170     if ((e = strrchr(argv0, '/')))
1171         e++;
1172     else
1173         e = argv0;
1174     
1175     fprintf(stderr,
1176             "%s [-h] [-D] [-u USERNAME] [-p PASSWORD] [-o OPTIONS] URL MOUNTPOINT\n"
1177             "\t-h Show this help\n"
1178             "\t-D Enable debug mode\n"
1179             "\t-u Username if required\n"
1180             "\t-p Password if required\n"
1181             "\t-o Additional FUSE mount options\n",
1182             e);
1185 static void exit_handler(int s) {
1186     static const char m[] = "*** Caught signal ***\n";
1187     if(fuse != NULL)
1188         fuse_exit(fuse);
1189     write(2, m, strlen(m));
1192 static void empty_handler(int sig) {}
1194 static int setup_signal_handlers(void) {
1195     struct sigaction sa;
1196     sigset_t m;
1197                                                                             
1198     sa.sa_handler = exit_handler;
1199     sigemptyset(&(sa.sa_mask));
1200     sa.sa_flags = 0;
1201     
1202     if (sigaction(SIGHUP, &sa, NULL) == -1 ||
1203         sigaction(SIGINT, &sa, NULL) == -1 ||
1204         sigaction(SIGTERM, &sa, NULL) == -1) {
1205                                                                                                         
1206         fprintf(stderr, "Cannot set exit signal handlers: %s\n", strerror(errno));
1207         return -1;
1208     }
1209                                                                                                         
1210     sa.sa_handler = SIG_IGN;
1211                                                                                                         
1212     if (sigaction(SIGPIPE, &sa, NULL) == -1) {
1213         fprintf(stderr, "Cannot set ignored signals: %s\n", strerror(errno));
1214         return -1;
1215     }
1217     /* Used to shut down the locking thread */
1218     sa.sa_handler = empty_handler;
1220     if (sigaction(SIGUSR1, &sa, NULL) == -1) {
1221         fprintf(stderr, "Cannot set user signals: %s\n", strerror(errno));
1222         return -1;
1223     }
1225     sigemptyset(&m);
1226     pthread_sigmask(SIG_BLOCK, &m, &m);
1227     sigdelset(&m, SIGHUP);
1228     sigdelset(&m, SIGINT);
1229     sigdelset(&m, SIGTERM);
1230     sigaddset(&m, SIGPIPE);
1231     sigaddset(&m, SIGUSR1);
1232     pthread_sigmask(SIG_SETMASK, &m, NULL);
1233     
1234     return 0;
1237 static int create_lock(void) {
1238     ne_session *session;
1239     char _owner[64], *owner;
1240     int i;
1241     int ret;
1242     
1243     lock = ne_lock_create();
1244     assert(lock);
1246     if (!(session = session_get(0)))
1247         return -1;
1249     if (!(owner = getenv("USER")))
1250         if (!(owner = getenv("LOGNAME"))) {
1251             snprintf(_owner, sizeof(_owner), "%lu", (unsigned long) getuid());
1252             owner = owner;
1253         }
1255     ne_fill_server_uri(session, &lock->uri);
1256     
1257     lock->uri.path = strdup(base_directory);
1258     lock->depth = NE_DEPTH_INFINITE;
1259     lock->timeout = LOCK_TIMEOUT+2;
1260     lock->owner = strdup(owner);
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;
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         sleep(LOCK_TIMEOUT);
1350     }
1351     
1352     if (debug)
1353         fprintf(stderr, "lock_thread exiting\n");
1355     return NULL;
1358 int main(int argc, char *argv[]) {
1359     int c;
1360     char *u = NULL, *p = NULL, *o = NULL;
1361     int fuse_fd = -1;
1362     int ret = 1;
1363     char mountpoint[PATH_MAX];
1364     pthread_t lock_thread;
1365     int lock_thread_running = 0;
1366     int enable_locking = 0;
1367     
1368     static char *mount_args_strings[] = {
1369         NULL,  /* path*/
1370         NULL,  /* -o */
1371         NULL,
1372         NULL};
1373     
1374     struct fuse_args mount_args = {
1375         .argc = 1,
1376         .argv = mount_args_strings,
1377         .allocated = 0
1378     };
1379     
1380     if (ne_sock_init()) {
1381         fprintf(stderr, "Failed to initialize libneon.\n");
1382         goto finish;
1383     }
1385     openssl_thread_setup();
1387     mask = umask(0);
1388     umask(mask);
1390     cache_alloc();
1392     if (setup_signal_handlers() < 0)
1393         goto finish;
1394     
1395     while ((c = getopt(argc, argv, "hu:p:Do:Lt:")) != -1) {
1397         switch(c) {
1398             case 'u':
1399                 u = optarg;
1400                 break;
1401                 
1402             case 'p':
1403                 p = optarg;
1404                 break;
1405                 
1406             case 'D':
1407                 debug = !debug;
1408                 break;
1410             case 'o':
1411                 o = optarg;
1412                 break;
1414             case 'L':
1415                 enable_locking = 1;
1416                 break;
1417                 
1418             case 'h':
1419             default:
1420                 usage(argv[0]);
1421                 goto finish;
1422         }
1423     }
1425     if (optind != argc-2) {
1426         usage(argv[0]);
1427         goto finish;
1428     }
1430     if (session_set_uri(argv[optind], u, p) < 0) {
1431         usage(argv[0]);
1432         goto finish;
1433     }
1435     if (argv[optind+1][0] == '/')
1436         snprintf(mountpoint, sizeof(mountpoint), "%s", argv[optind+1]);
1437     else {
1438         char *pwd = get_current_dir_name();
1439         snprintf(mountpoint, sizeof(mountpoint), "%s/%s", pwd, argv[optind+1]);
1440         free(pwd);
1441     }
1443     mount_args_strings[0] = argv[optind];
1445     if (o) {
1446         mount_args_strings[1] = "-o";
1447         mount_args_strings[2] = o;
1448         mount_args.argc += 2;
1449     }
1450     
1451     if ((fuse_fd = fuse_mount(mountpoint, &mount_args)) < 0) {
1452         fprintf(stderr, "Failed to mount FUSE file system.\n");
1453         goto finish;
1454     }
1456     if (!(fuse = fuse_new(fuse_fd, &mount_args, &dav_oper, sizeof(dav_oper)))) {
1457         fprintf(stderr, "Failed to create FUSE object.\n");
1458         goto finish;
1459     }
1460     
1461     if (enable_locking && create_lock() >= 0) {
1462         int r;
1463         if ((r = pthread_create(&lock_thread, NULL, lock_thread_func, NULL)) < 0) {
1464             fprintf(stderr, "pthread_create(): %s\n", strerror(r));
1465             goto finish;
1466         }
1467         
1468         lock_thread_running = 1;
1469     }
1470     
1471     fuse_loop_mt(fuse);
1473     if (debug)
1474         fprintf(stderr, "Exiting cleanly.\n");
1475     
1476     ret = 0;
1477     
1478 finish:
1479     
1480     if (lock_thread_running) {
1481         lock_thread_exit = 1;
1482         pthread_kill(lock_thread, SIGUSR1);
1483         pthread_join(lock_thread, NULL);
1484         remove_lock();
1485         ne_lockstore_destroy(lock_store);
1486     }
1488     if (fuse)
1489         fuse_destroy(fuse);
1490     
1491     if (fuse_fd >= 0)
1492         fuse_unmount(mountpoint);
1493     
1494     file_cache_close_all();
1495     cache_free();
1496     session_free();
1497     openssl_thread_cleanup();
1498     
1499     return ret;