Code

add boilerplate licenses in source files
[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>
39 #include <ne_request.h>
40 #include <ne_basic.h>
41 #include <ne_props.h>
42 #include <ne_utils.h>
43 #include <ne_socket.h>
44 #include <ne_auth.h>
45 #include <ne_dates.h>
47 #include <fuse.h>
49 #include "statcache.h"
50 #include "filecache.h"
51 #include "session.h"
52 #include "openssl-thread.h"
54 const ne_propname query_properties[] = {
55     { "DAV:", "resourcetype" },
56     { "http://apache.org/dav/props/", "executable" },
57     { "DAV:", "getcontentlength" },
58     { "DAV:", "getlastmodified" },
59     { "DAV:", "creationdate" },
60     { NULL, NULL }
61 };
63 mode_t mask = 0;
64 int debug = 0;
65 struct fuse* fuse = NULL;
67 struct fill_info {
68     fuse_dirh_t h;
69     fuse_dirfil_t filler;
70     const char *root;
71 };
73 static int get_stat(const char *path, struct stat *stbuf);
75 static pthread_once_t path_cvt_once = PTHREAD_ONCE_INIT;
76 static pthread_key_t path_cvt_tsd_key;
78 static void path_cvt_tsd_key_init(void) {
79     pthread_key_create(&path_cvt_tsd_key, free);
80 }
82 static const char *path_cvt(const char *path) {
83     char *r, *t;
84     int l;
86     pthread_once(&path_cvt_once, path_cvt_tsd_key_init);
87     
88     if ((r = pthread_getspecific(path_cvt_tsd_key)))
89         free(r);
91     t = malloc((l = strlen(base_directory)+strlen(path))+1);
92     assert(t);
93     sprintf(t, "%s%s", base_directory, path);
95     if (l > 1 && t[l-1] == '/')
96         t[l-1] = 0;
98     r = ne_path_escape(t);
99     free(t);
101     pthread_setspecific(path_cvt_tsd_key, r);
102     
103     return r;
106 static void fill_stat(struct stat* st, const ne_prop_result_set *results, int is_dir) {
107     const char *rt, *e, *gcl, *glm, *cd;
108     const ne_propname resourcetype = { "DAV:", "resourcetype" };
109     const ne_propname executable = { "http://apache.org/dav/props/", "executable" };
110     const ne_propname getcontentlength = { "DAV:", "getcontentlength" };
111     const ne_propname getlastmodified = { "DAV:", "getlastmodified" };
112     const ne_propname creationdate = { "DAV:", "creationdate" };
113         
114     assert(st && results);
116     rt = ne_propset_value(results, &resourcetype);
117     e = ne_propset_value(results, &executable);
118     gcl = ne_propset_value(results, &getcontentlength);
119     glm = ne_propset_value(results, &getlastmodified);
120     cd = ne_propset_value(results, &creationdate);
122     memset(st, 0, sizeof(struct stat));
123     
124     if (is_dir) {
125         st->st_mode = S_IFDIR | 0777;
126         st->st_nlink = 3;            /* find will ignore this directory if nlin <= and st_size == 0 */
127         st->st_size = 4096;
128     } else {
129         st->st_mode = S_IFREG | (e && (*e == 'T' || *e == 't') ? 0777 : 0666);
130         st->st_nlink = 1;
131         st->st_size = gcl ? atoll(gcl) : 0;
132     }
134     st->st_atime = time(NULL);
135     st->st_mtime = glm ? ne_rfc1123_parse(glm) : 0;
136     st->st_ctime = cd ? ne_iso8601_parse(cd) : 0;
139     st->st_blocks = (st->st_size+511)/512;
140     /*fprintf(stderr, "a: %u; m: %u; c: %u\n", st->st_atime, st->st_mtime, st->st_ctime);*/
142     st->st_mode &= ~mask;
143     
144     st->st_uid = getuid();
145     st->st_gid = getgid();
148 static char *strip_trailing_slash(char *fn, int *is_dir) {
149     size_t l = strlen(fn);
150     assert(fn && is_dir);
151     
152     if ((*is_dir = (fn[l-1] == '/')))
153         fn[l-1] = 0;
155     return fn;
158 static void getdir_propfind_callback(void *userdata, const char *href, const ne_prop_result_set *results) {
159     struct fill_info *f = userdata;
160     struct stat st;
161     char fn[PATH_MAX], *t;
162     int is_dir = 0;
164     assert(f);
166     strncpy(fn, href, sizeof(fn));
167     fn[sizeof(fn)-1] = 0;
168     strip_trailing_slash(fn, &is_dir);
170     if (strcmp(fn, f->root) && fn[0]) {
171         char *h;
172         
173         if ((t = strrchr(fn, '/')))
174             t++;
175         else
176             t = fn;
178         dir_cache_add(f->root, t, is_dir);
179         f->filler(f->h, h = ne_path_unescape(t), is_dir ? DT_DIR : DT_REG);
180         free(h);
181     }
183     fill_stat(&st, results, is_dir);
184     stat_cache_set(fn, &st);
187 static void getdir_cache_callback(const char *root, const char *fn, int is_dir, void *user) {
188     struct fill_info *f = user;
189     assert(f);
190     char path[PATH_MAX];
191     struct stat st;
192     char *h;
194     snprintf(path, sizeof(path), "%s/%s", !strcmp(root, "/") ? "" : root, fn);
195     
196     if (get_stat(path, &st) < 0)
197         return;
198     
199     f->filler(f->h, h = ne_path_unescape(fn), is_dir ? DT_DIR : DT_REG);
200     free(h);
203 static int dav_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler) {
204     struct fill_info f;
205     ne_session *session;
207     path = path_cvt(path);
209     if (debug)
210         fprintf(stderr, "getdir(%s)\n", path);
212     f.h = h;
213     f.filler = filler;
214     f.root = path;
216     if (dir_cache_enumerate(path, getdir_cache_callback, &f) < 0) {
218         if (debug)
219             fprintf(stderr, "DIR-CACHE-MISS\n");
220         
221         if (!(session = session_get())) 
222             return -EIO;
224         dir_cache_begin(path);
225         
226         if (ne_simple_propfind(session, path, NE_DEPTH_ONE, query_properties, getdir_propfind_callback, &f) != NE_OK) {
227             dir_cache_finish(path, 2);
228             fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
229             return -ENOENT;
230         }
232         dir_cache_finish(path, 1);
233     }
235     filler(h, ".", DT_DIR);
236     filler(h, "..", DT_DIR);
238     return 0;
241 static void getattr_propfind_callback(void *userdata, const char *href, const ne_prop_result_set *results) {
242     struct stat *st = (struct stat*) userdata;
243     char fn[PATH_MAX];
244     int is_dir;
246     assert(st);
248     strncpy(fn, href, sizeof(fn));
249     fn[sizeof(fn)-1] = 0;
250     strip_trailing_slash(fn, &is_dir);
251     
252     fill_stat(st, results, is_dir);
253     stat_cache_set(fn, st);
256 static int get_stat(const char *path, struct stat *stbuf) {
257     ne_session *session;
259     if (!(session = session_get())) 
260         return -EIO;
262     if (stat_cache_get(path, stbuf) == 0) {
263         return stbuf->st_mode == 0 ? -ENOENT : 0;
264     } else {
265         if (debug)
266             fprintf(stderr, "STAT-CACHE-MISS\n");
267         
268         if (ne_simple_propfind(session, path, NE_DEPTH_ZERO, query_properties, getattr_propfind_callback, stbuf) != NE_OK) {
269             stat_cache_invalidate(path);
270             fprintf(stderr, "PROPFIND failed: %s\n", ne_get_error(session));
271             return -ENOENT;
272         }
274         return 0;
275     }
278 static int dav_getattr(const char *path, struct stat *stbuf) {
279     path = path_cvt(path);
280     if (debug)
281         fprintf(stderr, "getattr(%s)\n", path);
282     return get_stat(path, stbuf);
285 static int dav_unlink(const char *path) {
286     int r;
287     struct stat st;
288     ne_session *session;
290     path = path_cvt(path);
292     if (debug)
293         fprintf(stderr, "unlink(%s)\n", path);
295     if (!(session = session_get())) 
296         return -EIO;
298     if ((r = get_stat(path, &st)) < 0)
299         return r;
301     if (!S_ISREG(st.st_mode))
302         return -EISDIR;
303     
304     if (ne_delete(session, path)) {
305         fprintf(stderr, "DELETE failed: %s\n", ne_get_error(session));
306         return -ENOENT;
307     }
309     stat_cache_invalidate(path);
310     dir_cache_invalidate_parent(path);
311     
312     return 0;
315 static int dav_rmdir(const char *path) {
316     int r;
317     struct stat st;
318     ne_session *session;
320     path = path_cvt(path);
322     if (debug)
323         fprintf(stderr, "rmdir(%s)\n", path);
325     if (!(session = session_get())) 
326         return -EIO;
328     if ((r = get_stat(path, &st)) < 0)
329         return r;
331     if (!S_ISDIR(st.st_mode))
332         return -ENOTDIR;
333     
334     if (ne_delete(session, path)) {
335         fprintf(stderr, "DELETE failed: %s\n", ne_get_error(session));
336         return -ENOENT;
337     }
339     stat_cache_invalidate(path);
340     dir_cache_invalidate_parent(path);
342     return 0;
345 static int dav_mkdir(const char *path, mode_t mode) {
346     char fn[PATH_MAX];
347     ne_session *session;
349     path = path_cvt(path);
351     if (debug)
352         fprintf(stderr, "mkdir(%s)\n", path);
354     if (!(session = session_get())) 
355         return -EIO;
357     snprintf(fn, sizeof(fn), "%s/", path);
358     
359     if (ne_mkcol(session, fn)) {
360         fprintf(stderr, "MKCOL failed: %s\n", ne_get_error(session));
361         return -ENOENT;
362     }
364     stat_cache_invalidate(path);
365     dir_cache_invalidate_parent(path);
366     
367     return 0;
370 static int dav_rename(const char *from, const char *to) {
371     ne_session *session;
372     int r = 0;
374     from = strdup(path_cvt(from));
375     to = path_cvt(to);
377     if (debug)
378         fprintf(stderr, "rename(%s, %s)\n", from, to);
380     if (!(session = session_get())) {
381         r = -EIO;
382         goto finish;
383     }
385     if (ne_move(session, 1, from, to)) {
386         fprintf(stderr, "MOVE failed: %s\n", ne_get_error(session));
387         r = -ENOENT;
388         goto finish;
389     }
390     
391     stat_cache_invalidate(from);
392     stat_cache_invalidate(to);
394     dir_cache_invalidate_parent(from);
395     dir_cache_invalidate_parent(to);
397 finish:
399     free((char*) from);
400     
401     return r;
404 static int dav_release(const char *path, int flags) {
405     void *f = NULL;
406     int r = 0;
407     ne_session *session;
409     path = path_cvt(path);
411     if (debug)
412         fprintf(stderr, "release(%s)\n", path);
414     if (!(session = session_get())) {
415         r = -EIO;
416         goto finish;
417     }
418     
419     if (!(f = file_cache_get(path))) {
420         fprintf(stderr, "release() called for closed file\n");
421         r = -EFAULT;
422         goto finish;
423     }
425     if (file_cache_close(f) < 0) {
426         r = -errno;
427         goto finish;
428     }
430 finish:
431     if (f)
432         file_cache_unref(f);
433     
434     return r;
437 static int dav_fsync(const char *path, int isdatasync) {
438     void *f = NULL;
439     int r = 0;
440     ne_session *session;
442     path = path_cvt(path);
443     if (debug)
444         fprintf(stderr, "fsync(%s)\n", path);
446     if (!(session = session_get())) {
447         r = -EIO;
448         goto finish;
449     }
451     if (!(f = file_cache_get(path))) {
452         fprintf(stderr, "fsync() called for closed file\n");
453         r = -EFAULT;
454         goto finish;
455     }
457     if (file_cache_sync(f) < 0) {
458         r = -errno;
459         goto finish;
460     }
462 finish:
463     
464     if (f)
465         file_cache_unref(f);
467     return r;
470 static int dav_mknod(const char *path, mode_t mode, dev_t rdev) {
471     char tempfile[PATH_MAX];
472     int fd;
473     ne_session *session;
474     
475     path = path_cvt(path);
476     if (debug)
477         fprintf(stderr, "mknod(%s)\n", path);
479     if (!(session = session_get())) 
480         return -EIO;
482     if (!S_ISREG(mode))
483         return -ENOTSUP;
485     snprintf(tempfile, sizeof(tempfile), "%s/fusedav-empty-XXXXXX", "/tmp");
486     if ((fd = mkstemp(tempfile)) < 0)
487         return -errno;
488     
489     unlink(tempfile);
490     
491     if (ne_put(session, path, fd)) {
492         fprintf(stderr, "mknod:PUT failed: %s\n", ne_get_error(session));
493         close(fd);
494         return -EACCES;
495     }
497     close(fd);
499     stat_cache_invalidate(path);
500     dir_cache_invalidate_parent(path);
502     return 0;
505 static int dav_open(const char *path, int flags) {
506     void *f;
508     if (debug)
509         fprintf(stderr, "open(%s)\n", path);
511     path = path_cvt(path);
512     if (!(f = file_cache_open(path, flags)))
513         return -errno;
515     file_cache_unref(f);
517     return 0;
520 static int dav_read(const char *path, char *buf, size_t size, off_t offset) {
521     void *f = NULL;
522     ssize_t r;
523  
524     path = path_cvt(path);
525     if (debug)
526         fprintf(stderr, "read(%s, %lu+%lu)\n", path, (unsigned long) offset, (unsigned long) size);
527     
528     if (!(f = file_cache_get(path))) {
529         fprintf(stderr, "read() called for closed file\n");
530         r = -EFAULT;
531         goto finish;
532     }
534     if ((r = file_cache_read(f, buf, size, offset)) < 0) {
535         r = -errno;
536         goto finish;
537     }
539 finish:
540     if (f)
541         file_cache_unref(f);
542     
543     return r;
546 static int dav_write(const char *path, const char *buf, size_t size, off_t offset) {
547     void *f = NULL;
548     ssize_t r;
550     path = path_cvt(path);
551     if (debug)
552         fprintf(stderr, "write(%s, %lu+%lu)\n", path, (unsigned long) offset, (unsigned long) size);
554     if (!(f = file_cache_get(path))) {
555         fprintf(stderr, "write() called for closed file\n");
556         r = -EFAULT;
557         goto finish;
558     }
560     if ((r = file_cache_write(f, buf, size, offset)) < 0) {
561         r = -errno;
562         goto finish;
563     }
564     
565 finish:
566     if (f)
567         file_cache_unref(f);
568     
569     return r;
573 static int dav_truncate(const char *path, off_t size) {
574     void *f = NULL;
575     int r = 0;
576     ne_session *session;
577     
578     path = path_cvt(path);
579     if (debug)
580         fprintf(stderr, "truncate(%s, %lu)\n", path, (unsigned long) size);
582     if (!(session = session_get()))
583         r = -EIO;
584         goto finish;
585     
586     if (!(f = file_cache_get(path))) {
587         fprintf(stderr, "truncate() called for closed file\n");
588         r = -EFAULT;
589         goto finish;
590     }
592     if (file_cache_truncate(f, size) < 0) {
593         r = -errno;
594         goto finish;
595     }
597 finish:
598     if (f)
599         file_cache_unref(f);
600     
601     return r;
605 static struct fuse_operations dav_oper = {
606     .getattr    = dav_getattr,
607     .getdir     = dav_getdir,
608     .mknod      = dav_mknod,
609     .mkdir      = dav_mkdir,
610     .unlink     = dav_unlink,
611     .rmdir      = dav_rmdir,
612     .rename     = dav_rename,
613 /*    .chmod    = dav_chmod,*/
614     .truncate   = dav_truncate,
615 /*    .utime    = dav_utime,*/
616     .open       = dav_open,
617     .read       = dav_read,
618     .write      = dav_write,
619     .release    = dav_release,
620     .fsync      = dav_fsync
621 };
623 static void usage(char *argv0) {
624     char *e;
626     if ((e = strrchr(argv0, '/')))
627         e++;
628     else
629         e = argv0;
630     
631     fprintf(stderr,
632             "%s [-h] [-D] [-u USERNAME] [-p PASSWORD] URL MOUNTPOINT\n"
633             "\t-h Show this help\n"
634             "\t-D Enable debug mode\n"
635             "\t-u Username if required\n"
636             "\t-p Password if required\n",
637             e);
640 static void exit_handler(int s) {
641     static const char m[] = "*** Caught signal ***\n";
642     write(2, m, strlen(m));
643     if(fuse != NULL)
644         fuse_exit(fuse);
647 static int setup_signal_handlers(void) {
648     struct sigaction sa;
649                                                                                                         
650     sa.sa_handler = exit_handler;
651     sigemptyset(&(sa.sa_mask));
652     sa.sa_flags = 0;
653     
654     if (sigaction(SIGHUP, &sa, NULL) == -1 ||
655         sigaction(SIGINT, &sa, NULL) == -1 ||
656         sigaction(SIGTERM, &sa, NULL) == -1) {
657                                                                                                         
658         fprintf(stderr, "Cannot set exit signal handlers: %s\n", strerror(errno));
659         return -1;
660     }
661                                                                                                         
662     sa.sa_handler = SIG_IGN;
663                                                                                                         
664     if (sigaction(SIGPIPE, &sa, NULL) == -1) {
665         fprintf(stderr, "Cannot set ignored signals: %s\n", strerror(errno));
666         return -1;
667     }
669     return 0;
672 int main(int argc, char *argv[]) {
673     int c;
674     char *u=NULL, *p = NULL;
675     int fuse_fd = -1;
676     int ret = 1;
677     char mountpoint[PATH_MAX];
678     static const char *mount_args[] = { "-n",  NULL, "-l", "-c", NULL };
680     if (ne_sock_init()) {
681         fprintf(stderr, "Failed to initialize libneon.\n");
682         goto finish;
683     }
685     openssl_thread_setup();
687     mask = umask(0);
688     umask(mask);
690     cache_alloc();
692     if (setup_signal_handlers() < 0)
693         goto finish;
694     
695     while ((c = getopt(argc, argv, "hu:p:D")) != -1) {
697         switch(c) {
698             case 'u':
699                 u = optarg;
700                 break;
701                 
702             case 'p':
703                 p = optarg;
704                 break;
705                 
706             case 'D':
707                 debug = !debug;
708                 break;
709                     
710             case 'h':
711             default:
712                 usage(argv[0]);
713                 goto finish;
714         }
715     }
717     if (optind != argc-2) {
718         usage(argv[0]);
719         goto finish;
720     }
722     if (session_set_uri(argv[optind], u, p) < 0) {
723         usage(argv[0]);
724         goto finish;
725     }
727     if (argv[optind+1][0] == '/')
728         snprintf(mountpoint, sizeof(mountpoint), "%s", argv[optind+1]);
729     else {
730         char *pwd = get_current_dir_name();
731         snprintf(mountpoint, sizeof(mountpoint), "%s/%s", pwd, argv[optind+1]);
732         free(pwd);
733     }
735     mount_args[1] = argv[optind];
736     
737     if ((fuse_fd = fuse_mount(mountpoint, mount_args)) < 0) {
738         fprintf(stderr, "Failed to mount FUSE file system.\n");
739         goto finish;
740     }
742     if (!(fuse = fuse_new(fuse_fd, 0, &dav_oper))) {
743         fprintf(stderr, "Failed to create FUSE object.\n");
744         goto finish;
745     }
746     
747     fuse_loop_mt(fuse);
748     
749     ret = 0;
750     
751 finish:
753     if (fuse)
754         fuse_destroy(fuse);
755     
756     if (fuse_fd >= 0)
757         fuse_unmount(mountpoint);
758     
759     file_cache_close_all();
760     cache_free();
761     session_free();
762     openssl_thread_cleanup();
763     
764     return ret;