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.
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.
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
36 #include <sys/statfs.h>
38 #include <attr/xattr.h>
40 #include <ne_request.h>
44 #include <ne_socket.h>
47 #include <ne_redirect.h>
52 #include "statcache.h"
53 #include "filecache.h"
55 #include "openssl-thread.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" },
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
81 fuse_fill_dir_t filler
;
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
);
94 static const char *path_cvt(const char *path
) {
98 pthread_once(&path_cvt_once
, path_cvt_tsd_key_init
);
100 if ((r
= pthread_getspecific(path_cvt_tsd_key
)))
103 t
= malloc((l
= strlen(base_directory
)+strlen(path
))+1);
105 sprintf(t
, "%s%s", base_directory
, path
);
107 if (l
> 1 && t
[l
-1] == '/')
110 r
= ne_path_escape(t
);
113 pthread_setspecific(path_cvt_tsd_key
, r
);
118 static int simple_propfind_with_redirect(
122 const ne_propname
*props
,
123 ne_props_result results
,
128 for (i
= 0; i
< MAX_REDIRECTS
; i
++) {
131 if ((ret
= ne_simple_propfind(session
, path
, depth
, props
, results
, userdata
)) != NE_REDIRECT
)
134 if (!(u
= ne_redirect_location(session
)))
137 if (!session_is_local(u
))
141 fprintf(stderr
, "REDIRECT FROM '%s' to '%s'\n", path
, u
->path
);
149 static int proppatch_with_redirect(
152 const ne_proppatch_operation
*ops
) {
156 for (i
= 0; i
< MAX_REDIRECTS
; i
++) {
159 if ((ret
= ne_proppatch(session
, path
, ops
)) != NE_REDIRECT
)
162 if (!(u
= ne_redirect_location(session
)))
165 if (!session_is_local(u
))
169 fprintf(stderr
, "REDIRECT FROM '%s' to '%s'\n", path
, u
->path
);
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" };
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
));
197 st
->st_mode
= S_IFDIR
| 0777;
198 st
->st_nlink
= 3; /* find will ignore this directory if nlin <= and st_size == 0 */
201 st
->st_mode
= S_IFREG
| (e
&& (*e
== 'T' || *e
== 't') ? 0777 : 0666);
203 st
->st_size
= gcl
? atoll(gcl
) : 0;
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;
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
;
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
);
225 if ((*is_dir
= (fn
[l
-1] == '/')))
231 static void getdir_propfind_callback(void *userdata
, const char *href
, const ne_prop_result_set
*results
) {
232 struct fill_info
*f
= userdata
;
234 char fn
[PATH_MAX
], *t
;
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]) {
246 if ((t
= strrchr(fn
, '/')))
251 dir_cache_add(f
->root
, t
, is_dir
);
252 f
->filler(f
->buf
, h
= ne_path_unescape(t
), NULL
, 0);
256 fill_stat(&st
, results
, is_dir
);
257 stat_cache_set(fn
, &st
);
260 static void getdir_cache_callback(
266 struct fill_info
*f
= user
;
272 snprintf(path
, sizeof(path
), "%s/%s", !strcmp(root
, "/") ? "" : root
, fn
);
274 f
->filler(f
->buf
, h
= ne_path_unescape(fn
), NULL
, 0);
278 static int dav_readdir(
281 fuse_fill_dir_t filler
,
283 struct fuse_file_info
*fi
) {
288 path
= path_cvt(path
);
291 fprintf(stderr
, "getdir(%s)\n", path
);
297 filler(buf
, ".", NULL
, 0);
298 filler(buf
, "..", NULL
, 0);
300 if (dir_cache_enumerate(path
, getdir_cache_callback
, &f
) < 0) {
303 fprintf(stderr
, "DIR-CACHE-MISS\n");
305 if (!(session
= session_get(1)))
308 dir_cache_begin(path
);
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
));
316 dir_cache_finish(path
, 1);
322 static void getattr_propfind_callback(void *userdata
, const char *href
, const ne_prop_result_set
*results
) {
323 struct stat
*st
= (struct stat
*) userdata
;
329 strncpy(fn
, href
, sizeof(fn
));
330 fn
[sizeof(fn
)-1] = 0;
331 strip_trailing_slash(fn
, &is_dir
);
333 fill_stat(st
, results
, is_dir
);
334 stat_cache_set(fn
, st
);
337 static int get_stat(const char *path
, struct stat
*stbuf
) {
340 if (!(session
= session_get(1)))
343 if (stat_cache_get(path
, stbuf
) == 0) {
344 return stbuf
->st_mode
== 0 ? -ENOENT
: 0;
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
));
359 static int dav_getattr(const char *path
, struct stat
*stbuf
) {
360 path
= path_cvt(path
);
362 fprintf(stderr
, "getattr(%s)\n", path
);
363 return get_stat(path
, stbuf
);
366 static int dav_unlink(const char *path
) {
371 path
= path_cvt(path
);
374 fprintf(stderr
, "unlink(%s)\n", path
);
376 if (!(session
= session_get(1)))
379 if ((r
= get_stat(path
, &st
)) < 0)
382 if (!S_ISREG(st
.st_mode
))
385 if (ne_delete(session
, path
)) {
386 fprintf(stderr
, "DELETE failed: %s\n", ne_get_error(session
));
390 stat_cache_invalidate(path
);
391 dir_cache_invalidate_parent(path
);
396 static int dav_rmdir(const char *path
) {
402 path
= path_cvt(path
);
405 fprintf(stderr
, "rmdir(%s)\n", path
);
407 if (!(session
= session_get(1)))
410 if ((r
= get_stat(path
, &st
)) < 0)
413 if (!S_ISDIR(st
.st_mode
))
416 snprintf(fn
, sizeof(fn
), "%s/", path
);
418 if (ne_delete(session
, fn
)) {
419 fprintf(stderr
, "DELETE failed: %s\n", ne_get_error(session
));
423 stat_cache_invalidate(path
);
424 dir_cache_invalidate_parent(path
);
429 static int dav_mkdir(const char *path
, mode_t mode
) {
433 path
= path_cvt(path
);
436 fprintf(stderr
, "mkdir(%s)\n", path
);
438 if (!(session
= session_get(1)))
441 snprintf(fn
, sizeof(fn
), "%s/", path
);
443 if (ne_mkcol(session
, fn
)) {
444 fprintf(stderr
, "MKCOL failed: %s\n", ne_get_error(session
));
448 stat_cache_invalidate(path
);
449 dir_cache_invalidate_parent(path
);
454 static int dav_rename(const char *from
, const char *to
) {
458 char fn
[PATH_MAX
], *_from
;
460 from
= _from
= strdup(path_cvt(from
));
465 fprintf(stderr
, "rename(%s, %s)\n", from
, to
);
467 if (!(session
= session_get(1))) {
472 if ((r
= get_stat(from
, &st
)) < 0)
475 if (S_ISDIR(st
.st_mode
)) {
476 snprintf(fn
, sizeof(fn
), "%s/", from
);
480 if (ne_move(session
, 1, from
, to
)) {
481 fprintf(stderr
, "MOVE failed: %s\n", ne_get_error(session
));
486 stat_cache_invalidate(from
);
487 stat_cache_invalidate(to
);
489 dir_cache_invalidate_parent(from
);
490 dir_cache_invalidate_parent(to
);
499 static int dav_release(const char *path
, struct fuse_file_info
*info
) {
504 path
= path_cvt(path
);
507 fprintf(stderr
, "release(%s)\n", path
);
509 if (!(session
= session_get(1))) {
514 if (!(f
= file_cache_get(path
))) {
515 fprintf(stderr
, "release() called for closed file\n");
520 if (file_cache_close(f
) < 0) {
532 static int dav_fsync(const char *path
, __unused
int isdatasync
, struct fuse_file_info
*info
) {
537 path
= path_cvt(path
);
539 fprintf(stderr
, "fsync(%s)\n", path
);
541 if (!(session
= session_get(1))) {
546 if (!(f
= file_cache_get(path
))) {
547 fprintf(stderr
, "fsync() called for closed file\n");
552 if (file_cache_sync(f
) < 0) {
565 static int dav_mknod(const char *path
, mode_t mode
, dev_t rdev
) {
566 char tempfile
[PATH_MAX
];
570 path
= path_cvt(path
);
572 fprintf(stderr
, "mknod(%s)\n", path
);
574 if (!(session
= session_get(1)))
580 snprintf(tempfile
, sizeof(tempfile
), "%s/fusedav-empty-XXXXXX", "/tmp");
581 if ((fd
= mkstemp(tempfile
)) < 0)
586 if (ne_put(session
, path
, fd
)) {
587 fprintf(stderr
, "mknod:PUT failed: %s\n", ne_get_error(session
));
594 stat_cache_invalidate(path
);
595 dir_cache_invalidate_parent(path
);
600 static int dav_open(const char *path
, struct fuse_file_info
*info
) {
604 fprintf(stderr
, "open(%s)\n", path
);
606 path
= path_cvt(path
);
608 if (!(f
= file_cache_open(path
, info
->flags
)))
616 static int dav_read(const char *path
, char *buf
, size_t size
, off_t offset
, struct fuse_file_info
*info
) {
620 path
= path_cvt(path
);
623 fprintf(stderr
, "read(%s, %lu+%lu)\n", path
, (unsigned long) offset
, (unsigned long) size
);
625 if (!(f
= file_cache_get(path
))) {
626 fprintf(stderr
, "read() called for closed file\n");
631 if ((r
= file_cache_read(f
, buf
, size
, offset
)) < 0) {
636 fprintf(stderr
, "read: %i\n", r
);
645 static int dav_write(const char *path
, const char *buf
, size_t size
, off_t offset
, struct fuse_file_info
*info
) {
649 path
= path_cvt(path
);
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");
660 if ((r
= file_cache_write(f
, buf
, size
, offset
)) < 0) {
673 static int dav_truncate(const char *path
, off_t size
) {
678 path
= path_cvt(path
);
681 fprintf(stderr
, "truncate(%s, %lu)\n", path
, (unsigned long) size
);
683 if (!(session
= session_get(1)))
687 if (!(f
= file_cache_get(path
))) {
688 fprintf(stderr
, "truncate() called for closed file\n");
693 if (file_cache_truncate(f
, size
) < 0) {
705 static int dav_utime(const char *path
, struct utimbuf
*buf
) {
707 const ne_propname getlastmodified
= { "DAV:", "getlastmodified" };
708 ne_proppatch_operation ops
[2];
715 path
= path_cvt(path
);
718 fprintf(stderr
, "utime(%s, %lu, %lu)\n", path
, (unsigned long) buf
->actime
, (unsigned long) buf
->modtime
);
720 ops
[0].name
= &getlastmodified
;
721 ops
[0].type
= ne_propset
;
722 ops
[0].value
= date
= ne_rfc1123_date(buf
->modtime
);
725 if (!(session
= session_get(1))) {
730 if (proppatch_with_redirect(session
, path
, ops
)) {
731 fprintf(stderr
, "PROPPATCH failed: %s\n", ne_get_error(session
));
736 stat_cache_invalidate(path
);
744 static const char *fix_xattr(const char *name
) {
747 if (!strcmp(name
, MIME_XATTR
))
748 return "user.webdav(DAV:;getcontenttype)";
753 struct listxattr_info
{
758 static int listxattr_iterator(
760 const ne_propname
*pname
,
762 const ne_status
*status
) {
764 struct listxattr_info
*l
= userdata
;
769 if (!value
|| !pname
)
773 n
= snprintf(l
->list
, l
->space
, "user.webdav(%s;%s)", pname
->nspace
, pname
->name
) + 1;
775 if (n
>= (int) l
->space
) {
790 /* Calculate space */
792 l
->size
+= strlen(pname
->nspace
) + strlen(pname
->name
) + 15;
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(
808 struct listxattr_info l
;
813 path
= path_cvt(path
);
816 fprintf(stderr
, "listxattr(%s, .., %lu)\n", path
, (unsigned long) size
);
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
);
833 l
.size
= sizeof(MIME_XATTR
);
836 if (!(session
= session_get(1)))
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
));
852 struct getxattr_info
{
853 ne_propname propname
;
858 static int getxattr_iterator(
860 const ne_propname
*pname
,
862 const ne_status
*status
) {
864 struct getxattr_info
*g
= userdata
;
868 if (!value
|| !pname
)
871 if (strcmp(pname
->nspace
, g
->propname
.nspace
) ||
872 strcmp(pname
->name
, g
->propname
.name
))
883 memcpy(g
->value
, value
, l
);
886 /* Calculate space */
888 g
->size
= strlen(value
);
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
) {
906 assert(dnspace_length
);
908 assert(dname_length
);
910 if (strncmp(name
, "user.webdav(", 12) ||
911 name
[strlen(name
)-1] != ')' ||
912 !(e
= strchr(name
+12, ';')))
915 if ((k
= strcspn(name
+12, ";")) > dnspace_length
-1)
918 memcpy(dnspace
, name
+12, k
);
923 if ((k
= strlen(e
)) > dname_length
-1)
935 static int dav_getxattr(
942 struct getxattr_info g
;
943 ne_propname props
[2];
944 char dnspace
[128], dname
[128];
948 path
= path_cvt(path
);
949 name
= fix_xattr(name
);
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)
957 props
[0].nspace
= dnspace
;
958 props
[0].name
= dname
;
959 props
[1].nspace
= NULL
;
960 props
[1].name
= NULL
;
965 g
.size
= (size_t) -1;
969 g
.size
= (size_t) -1;
972 g
.propname
= props
[0];
974 if (!(session
= session_get(1)))
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
));
982 if (g
.size
== (size_t) -1)
988 static int dav_setxattr(
996 ne_propname propname
;
997 ne_proppatch_operation ops
[2];
999 char dnspace
[128], dname
[128];
1000 char *value_fixed
= NULL
;
1006 path
= path_cvt(path
);
1007 name
= fix_xattr(name
);
1010 fprintf(stderr
, "setxattr(%s, %s)\n", path
, name
);
1017 if (parse_xattr(name
, dnspace
, sizeof(dnspace
), dname
, sizeof(dname
)) < 0) {
1022 propname
.nspace
= dnspace
;
1023 propname
.name
= dname
;
1025 /* Add trailing NUL byte if required */
1026 if (!memchr(value
, 0, size
)) {
1027 value_fixed
= malloc(size
+1);
1028 assert(value_fixed
);
1030 memcpy(value_fixed
, value
, size
);
1031 value_fixed
[size
] = 0;
1033 value
= value_fixed
;
1036 ops
[0].name
= &propname
;
1037 ops
[0].type
= ne_propset
;
1038 ops
[0].value
= value
;
1042 if (!(session
= session_get(1))) {
1047 if (proppatch_with_redirect(session
, path
, ops
)) {
1048 fprintf(stderr
, "PROPPATCH failed: %s\n", ne_get_error(session
));
1053 stat_cache_invalidate(path
);
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];
1066 char dnspace
[128], dname
[128];
1071 path
= path_cvt(path
);
1072 name
= fix_xattr(name
);
1075 fprintf(stderr
, "removexattr(%s, %s)\n", path
, name
);
1077 if (parse_xattr(name
, dnspace
, sizeof(dnspace
), dname
, sizeof(dname
)) < 0) {
1082 propname
.nspace
= dnspace
;
1083 propname
.name
= dname
;
1085 ops
[0].name
= &propname
;
1086 ops
[0].type
= ne_propremove
;
1087 ops
[0].value
= NULL
;
1091 if (!(session
= session_get(1))) {
1096 if (proppatch_with_redirect(session
, path
, ops
)) {
1097 fprintf(stderr
, "PROPPATCH failed: %s\n", ne_get_error(session
));
1102 stat_cache_invalidate(path
);
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];
1117 path
= path_cvt(path
);
1120 fprintf(stderr
, "chmod(%s, %04o)\n", path
, mode
);
1122 ops
[0].name
= &executable
;
1123 ops
[0].type
= ne_propset
;
1124 ops
[0].value
= mode
& 0111 ? "T" : "F";
1127 if (!(session
= session_get(1))) {
1132 if (proppatch_with_redirect(session
, path
, ops
)) {
1133 fprintf(stderr
, "PROPPATCH failed: %s\n", ne_get_error(session
));
1138 stat_cache_invalidate(path
);
1145 static struct fuse_operations dav_oper
= {
1146 .getattr
= dav_getattr
,
1147 .readdir
= dav_readdir
,
1150 .unlink
= dav_unlink
,
1152 .rename
= dav_rename
,
1154 .truncate
= dav_truncate
,
1159 .release
= dav_release
,
1161 .setxattr
= dav_setxattr
,
1162 .getxattr
= dav_getxattr
,
1163 .listxattr
= dav_listxattr
,
1164 .removexattr
= dav_removexattr
,
1167 static void usage(char *argv0
) {
1170 if ((e
= strrchr(argv0
, '/')))
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",
1185 static void exit_handler(int s
) {
1186 static const char m
[] = "*** Caught signal ***\n";
1189 write(2, m
, strlen(m
));
1192 static void empty_handler(int sig
) {}
1194 static int setup_signal_handlers(void) {
1195 struct sigaction sa
;
1198 sa
.sa_handler
= exit_handler
;
1199 sigemptyset(&(sa
.sa_mask
));
1202 if (sigaction(SIGHUP
, &sa
, NULL
) == -1 ||
1203 sigaction(SIGINT
, &sa
, NULL
) == -1 ||
1204 sigaction(SIGTERM
, &sa
, NULL
) == -1) {
1206 fprintf(stderr
, "Cannot set exit signal handlers: %s\n", strerror(errno
));
1210 sa
.sa_handler
= SIG_IGN
;
1212 if (sigaction(SIGPIPE
, &sa
, NULL
) == -1) {
1213 fprintf(stderr
, "Cannot set ignored signals: %s\n", strerror(errno
));
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
));
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
);
1237 static int create_lock(void) {
1238 ne_session
*session
;
1239 char _owner
[64], *owner
;
1243 lock
= ne_lock_create();
1246 if (!(session
= session_get(0)))
1249 if (!(owner
= getenv("USER")))
1250 if (!(owner
= getenv("LOGNAME"))) {
1251 snprintf(_owner
, sizeof(_owner
), "%lu", (unsigned long) getuid());
1255 ne_fill_server_uri(session
, &lock
->uri
);
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");
1264 for (i
= 0; i
< MAX_REDIRECTS
; i
++) {
1267 if ((ret
= ne_lock(session
, lock
)) != NE_REDIRECT
)
1270 if (!(u
= ne_redirect_location(session
)))
1273 if (!session_is_local(u
))
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
);
1284 fprintf(stderr
, "LOCK failed: %s\n", ne_get_error(session
));
1285 ne_lock_destroy(lock
);
1290 lock_store
= ne_lockstore_create();
1293 ne_lockstore_add(lock_store
, lock
);
1298 static int remove_lock(void) {
1299 ne_session
*session
;
1303 if (!(session
= session_get(0)))
1307 fprintf(stderr
, "Removing lock...\n");
1309 if (ne_unlock(session
, lock
)) {
1310 fprintf(stderr
, "UNLOCK failed: %s\n", ne_get_error(session
));
1317 static void *lock_thread_func(__unused
void *p
) {
1318 ne_session
*session
;
1322 fprintf(stderr
, "lock_thread entering\n");
1324 if (!(session
= session_get(1)))
1327 sigemptyset(&block
);
1328 sigaddset(&block
, SIGUSR1
);
1332 while (!lock_thread_exit
) {
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
);
1342 fprintf(stderr
, "LOCK refresh failed: %s\n", ne_get_error(session
));
1346 if (lock_thread_exit
)
1349 sleep(LOCK_TIMEOUT
);
1353 fprintf(stderr
, "lock_thread exiting\n");
1358 int main(int argc
, char *argv
[]) {
1360 char *u
= NULL
, *p
= NULL
, *o
= NULL
;
1363 char mountpoint
[PATH_MAX
];
1364 pthread_t lock_thread
;
1365 int lock_thread_running
= 0;
1366 int enable_locking
= 0;
1368 static char *mount_args_strings
[] = {
1374 struct fuse_args mount_args
= {
1376 .argv
= mount_args_strings
,
1380 if (ne_sock_init()) {
1381 fprintf(stderr
, "Failed to initialize libneon.\n");
1385 openssl_thread_setup();
1392 if (setup_signal_handlers() < 0)
1395 while ((c
= getopt(argc
, argv
, "hu:p:Do:Lt:")) != -1) {
1425 if (optind
!= argc
-2) {
1430 if (session_set_uri(argv
[optind
], u
, p
) < 0) {
1435 if (argv
[optind
+1][0] == '/')
1436 snprintf(mountpoint
, sizeof(mountpoint
), "%s", argv
[optind
+1]);
1438 char *pwd
= get_current_dir_name();
1439 snprintf(mountpoint
, sizeof(mountpoint
), "%s/%s", pwd
, argv
[optind
+1]);
1443 mount_args_strings
[0] = argv
[optind
];
1446 mount_args_strings
[1] = "-o";
1447 mount_args_strings
[2] = o
;
1448 mount_args
.argc
+= 2;
1451 if ((fuse_fd
= fuse_mount(mountpoint
, &mount_args
)) < 0) {
1452 fprintf(stderr
, "Failed to mount FUSE file system.\n");
1456 if (!(fuse
= fuse_new(fuse_fd
, &mount_args
, &dav_oper
, sizeof(dav_oper
)))) {
1457 fprintf(stderr
, "Failed to create FUSE object.\n");
1461 if (enable_locking
&& create_lock() >= 0) {
1463 if ((r
= pthread_create(&lock_thread
, NULL
, lock_thread_func
, NULL
)) < 0) {
1464 fprintf(stderr
, "pthread_create(): %s\n", strerror(r
));
1468 lock_thread_running
= 1;
1474 fprintf(stderr
, "Exiting cleanly.\n");
1480 if (lock_thread_running
) {
1481 lock_thread_exit
= 1;
1482 pthread_kill(lock_thread
, SIGUSR1
);
1483 pthread_join(lock_thread
, NULL
);
1485 ne_lockstore_destroy(lock_store
);
1492 fuse_unmount(mountpoint
);
1494 file_cache_close_all();
1497 openssl_thread_cleanup();