2 afuse - An automounter using FUSE
3 Copyright (C) 2006 Jacob Bower <jacob.bower@ic.ac.uk>
5 Portions of this program derive from examples provided with
8 This program can be distributed under the terms of the GNU GPL.
15 // For pread()/pwrite()
16 #define _XOPEN_SOURCE 500
33 #include <sys/types.h>
39 #include <sys/xattr.h>
46 #include "variable_pairing_heap.h"
48 #define TMP_DIR_TEMPLATE "/tmp/afuse-XXXXXX"
49 char *mount_point_directory;
50 dev_t mount_point_dev;
52 // Data structure filled in when parsing command line args
53 struct user_options_t {
54 char *mount_command_template;
55 char *unmount_command_template;
57 uint64_t auto_unmount_delay;
58 } user_options = {NULL, NULL, false, UINT64_MAX};
60 typedef struct _mount_list_t {
61 struct _mount_list_t *next;
62 struct _mount_list_t *prev;
69 PH_NEW_LINK(struct _mount_list_t) auto_unmount_ph_node;
70 /* This is the sort key for the auto_unmount_ph heap. It will
71 equal UINT64_MAX if this node is not in the heap. */
72 int64_t auto_unmount_time;
75 PH_DECLARE_TYPE(auto_unmount_ph, mount_list_t)
76 PH_DEFINE_TYPE(auto_unmount_ph, mount_list_t, auto_unmount_ph_node, auto_unmount_time)
78 #define BLOCK_SIGALRM \
79 sigset_t block_sigalrm_oldset, block_sigalrm_set; \
80 sigemptyset(&block_sigalrm_set); \
81 sigaddset(&block_sigalrm_set, SIGALRM); \
82 sigprocmask(SIG_BLOCK, &block_sigalrm_set, &block_sigalrm_oldset)
84 #define UNBLOCK_SIGALRM \
85 sigprocmask(SIG_SETMASK, &block_sigalrm_oldset, NULL)
87 auto_unmount_ph_t auto_unmount_ph;
88 int64_t auto_unmount_next_timeout = INT64_MAX;
90 static int64_t from_timeval(const struct timeval *tv)
92 return (int64_t)tv->tv_sec * 1000000 + ((int64_t)tv->tv_usec);
95 static void to_timeval(struct timeval *tv, int64_t usec)
97 tv->tv_sec = usec / 1000000;
98 tv->tv_usec = usec % 1000000;
101 static int get_retval(int res)
108 static int check_mount(mount_list_t *mount)
112 if (lstat(mount->mount_point, &buf) == -1)
114 if (buf.st_dev == mount_point_dev)
119 static void update_auto_unmount(mount_list_t *mount)
121 if (user_options.auto_unmount_delay == UINT64_MAX)
124 /* Get the current time */
126 mount_list_t *min_mount;
127 int64_t cur_time, next_time;
128 gettimeofday(&tv, NULL);
129 cur_time = from_timeval(&tv);
133 /* Always remove first */
134 if (mount->auto_unmount_time != INT64_MAX)
135 auto_unmount_ph_remove(&auto_unmount_ph, mount);
137 if (!mount->fd_list && !mount->dir_list)
139 mount->auto_unmount_time = cur_time + user_options.auto_unmount_delay;
140 auto_unmount_ph_insert(&auto_unmount_ph, mount);
143 mount->auto_unmount_time = INT64_MAX;
146 min_mount = auto_unmount_ph_min(&auto_unmount_ph);
147 next_time = min_mount ? min_mount->auto_unmount_time : INT64_MAX;
149 if (next_time != auto_unmount_next_timeout)
151 struct itimerval itv;
152 auto_unmount_next_timeout = next_time;
154 if (next_time != INT64_MAX)
156 if (next_time > cur_time)
157 to_timeval(&itv.it_value, next_time - cur_time);
158 else /* Timer is set to expire immediately --- set it to 1 instead*/
159 to_timeval(&itv.it_value, 1);
162 /* Disable the timer */
163 to_timeval(&itv.it_value, 0);
165 to_timeval(&itv.it_interval, 0);
166 if (setitimer(ITIMER_REAL, &itv, NULL) != 0) {
167 perror("Error setting timer");
172 static void handle_auto_unmount_timer(int x)
174 /* Get the current time */
178 gettimeofday(&tv, NULL);
179 cur_time = from_timeval(&tv);
181 while ((mount = auto_unmount_ph_min(&auto_unmount_ph)) != NULL &&
182 mount->auto_unmount_time <= cur_time)
187 update_auto_unmount(NULL);
190 mount_list_t *mount_list = NULL;
192 mount_list_t *find_mount(const char *root_name)
194 mount_list_t *current_mount = mount_list;
196 while(current_mount) {
197 if( strcmp(root_name, current_mount->root_name) == 0)
198 return current_mount;
200 current_mount = current_mount->next;
206 int is_mount(const char *root_name)
208 return find_mount(root_name) ? 1 : 0;
211 mount_list_t *add_mount(const char *root_name, char *mount_point)
213 mount_list_t *new_mount;
215 new_mount = (mount_list_t *)my_malloc( sizeof(mount_list_t) );
216 new_mount->root_name = my_strdup(root_name);
217 new_mount->mount_point = mount_point;
219 new_mount->next = mount_list;
220 new_mount->prev = NULL;
221 new_mount->fd_list = NULL;
222 new_mount->dir_list = NULL;
223 new_mount->auto_unmount_time = INT64_MAX;
225 mount_list->prev = new_mount;
227 mount_list = new_mount;
229 update_auto_unmount(new_mount);
234 void remove_mount(mount_list_t *current_mount)
236 if (current_mount->auto_unmount_time != INT64_MAX)
237 auto_unmount_ph_remove(&auto_unmount_ph, current_mount);
239 free(current_mount->root_name);
240 free(current_mount->mount_point);
241 if(current_mount->prev)
242 current_mount->prev->next = current_mount->next;
244 mount_list = current_mount->next;
245 if(current_mount->next)
246 current_mount->next->prev = current_mount->prev;
248 update_auto_unmount(NULL);
251 char *make_mount_point(const char *root_name)
256 // Create the mount point
257 dir_tmp = my_malloc(strlen(mount_point_directory) + 2 + strlen(root_name));
258 strcpy(dir_tmp, mount_point_directory);
259 strcat(dir_tmp, "/");
260 strcat(dir_tmp, root_name);
262 if(mkdir(dir_tmp, 0700) == -1 && errno != EEXIST) {
263 fprintf(stderr, "Cannot create directory: %s (%s)\n",
264 dir_tmp, strerror(errno));
272 // !!FIXME!! allow escaping of %'s
273 // Note: this method strips out quotes and applies them itself as should be appropriate
274 char *expand_template(const char *template, const char *mount_point, const char *root_name)
279 char *expanded_name_start;
282 for(i = 0; template[i]; i++)
283 if(template[i] == '%') {
284 switch(template[i + 1])
287 len += strlen(mount_point) + 2;
291 len += strlen(root_name) + 2;
295 } else if(template[i] != '"')
298 expanded_name_start = expanded_name = my_malloc(len + 1);
300 for(i = 0; template[i]; i++)
301 if(template[i] == '%') {
303 switch(template[i + 1])
306 *expanded_name++ = '"';
307 while(mount_point[j])
308 *expanded_name++ = mount_point[j++];
309 *expanded_name++ = '"';
313 *expanded_name++ = '"';
315 *expanded_name++ = root_name[j++];
316 *expanded_name++ = '"';
320 } else if(template[i] != '"')
321 *expanded_name++ = template[i];
323 *expanded_name = '\0';
325 return expanded_name_start;
328 mount_list_t *do_mount(const char *root_name)
335 fprintf(stderr, "Mounting: %s\n", root_name);
337 if( !(mount_point = make_mount_point(root_name)) ) {
338 fprintf(stderr, "Failed to create mount point directory: %s/%s\n",
339 mount_point_directory, root_name);
343 mount_command = expand_template(user_options.mount_command_template,
344 mount_point, root_name);
345 sysret = system(mount_command);
347 fprintf(stderr, "sysret: %.8x\n", sysret);
350 fprintf(stderr, "Failed to invoke mount command: '%s' (%s)\n",
351 mount_command, sysret != -1 ?
352 "Error executing mount" :
355 // remove the now unused directory
356 if( rmdir(mount_point) == -1 )
357 fprintf(stderr, "Failed to remove mount point dir: %s (%s)",
358 mount_point, strerror(errno));
365 mount = add_mount(root_name, mount_point);
371 int do_umount(mount_list_t *mount)
373 char *unmount_command;
376 fprintf(stderr, "Unmounting: %s\n", mount->root_name);
378 unmount_command = expand_template(user_options.unmount_command_template,
379 mount->mount_point, mount->root_name);
380 sysret = system(unmount_command);
382 fprintf(stderr, "Failed to invoke unmount command: '%s' (%s)\n",
383 unmount_command, sysret != -1 ?
384 "Error executing mount" :
386 /* Still unmount anyway */
389 if( rmdir(mount->mount_point) == -1 )
390 fprintf(stderr, "Failed to remove mount point dir: %s (%s)",
391 mount->mount_point, strerror(errno));
393 free(unmount_command);
397 void unmount_all(void)
399 fprintf(stderr, "Attemping to unmount all filesystems:\n");
402 fprintf(stderr, "\tUnmounting: %s\n", mount_list->root_name);
404 do_umount(mount_list);
407 fprintf(stderr, "done.\n");
418 if(rmdir(mount_point_directory) == -1)
419 fprintf(stderr, "Failed to remove temporary mount point directory: %s (%s)\n",
420 mount_point_directory, strerror(errno));
423 int max_path_out_len(const char *path_in)
425 return strlen(mount_point_directory) + strlen(path_in) + 2;
428 // returns true if path is the a child directory of a root node
429 // e.g. /a/b is a child, /a is not.
430 int extract_root_name(const char *path, char *root_name)
435 for(i = 1; path[i] && path[i] != '/'; i++)
436 root_name[i - 1] = path[i];
437 root_name[i - 1] = '\0';
439 return strlen(&path[i]);
442 typedef enum {PROC_PATH_FAILED, PROC_PATH_ROOT_DIR, PROC_PATH_PROXY_DIR} proc_result_t;
444 proc_result_t process_path(const char *path_in, char *path_out, char *root_name, int attempt_mount, mount_list_t **out_mount)
450 mount_list_t *mount = NULL;
454 fprintf(stderr, "Path in: %s\n", path_in);
455 is_child = extract_root_name(path_in, root_name);
456 fprintf(stderr, "root_name is: %s\n", root_name);
458 // Mount filesystem if neccessary
459 // the combination of is_child and attempt_mount prevent inappropriate
460 // mounting of a filesystem for example if the user tries to mknod
461 // in the afuse root this should cause an error not a mount.
462 // !!FIXME!! this is broken on FUSE < 2.5 (?) because a getattr
463 // on the root node seems to occur with every single access.
464 if( /*(is_child || attempt_mount ) && */
465 strlen(root_name) > 0 &&
466 !(mount = find_mount(root_name)) &&
467 !(mount = do_mount(root_name)))
468 return PROC_PATH_FAILED;
470 if (mount && !check_mount(mount))
473 mount = do_mount(root_name);
475 return PROC_PATH_FAILED;
478 // construct path_out (mount_point_directory + '/' + path_in + '\0')
479 path_out_base = path_out;
480 len = strlen(mount_point_directory);
481 memcpy(path_out, mount_point_directory, len);
484 len = strlen(path_in) - 1;
485 memcpy(path_out, path_in + 1, len);
488 fprintf(stderr, "Path out: %s\n", path_out_base);
492 return strlen(root_name) ? PROC_PATH_PROXY_DIR : PROC_PATH_ROOT_DIR;
495 static int afuse_getattr(const char *path, struct stat *stbuf)
498 char *root_name = alloca( strlen(path) );
499 char *real_path = alloca( max_path_out_len(path) );
504 fprintf(stderr, "> GetAttr\n");
506 switch( process_path(path, real_path, root_name, 0, &mount) )
508 case PROC_PATH_FAILED:
512 case PROC_PATH_ROOT_DIR:
513 fprintf(stderr, "Getattr on: (%s) - %s\n", path, root_name);
514 if(mount || strlen(root_name) == 0) {
515 stbuf->st_mode = S_IFDIR | 0700;
517 stbuf->st_uid = getuid();
518 stbuf->st_gid = getgid();
520 stbuf->st_blksize = 0;
521 stbuf->st_blocks = 0;
530 case PROC_PATH_PROXY_DIR:
531 retval = get_retval(lstat(real_path, stbuf));
535 update_auto_unmount(mount);
540 static int afuse_readlink(const char *path, char *buf, size_t size)
543 char *root_name = alloca( strlen(path) );
544 char *real_path = alloca( max_path_out_len(path) );
549 switch( process_path(path, real_path, root_name, 1, &mount) )
551 case PROC_PATH_FAILED:
554 case PROC_PATH_ROOT_DIR:
557 case PROC_PATH_PROXY_DIR:
558 res = readlink(real_path, buf, size - 1);
569 update_auto_unmount(mount);
574 static int afuse_opendir(const char *path, struct fuse_file_info *fi)
577 char *root_name = alloca( strlen(path) );
579 char *real_path = alloca( max_path_out_len(path) );
583 switch( process_path(path, real_path, root_name, 1, &mount) )
585 case PROC_PATH_FAILED:
588 case PROC_PATH_ROOT_DIR:
591 case PROC_PATH_PROXY_DIR:
592 dp = opendir(real_path);
597 fi->fh = (unsigned long) dp;
599 dir_list_add(&mount->dir_list, dp);
604 update_auto_unmount(mount);
609 static inline DIR *get_dirp(struct fuse_file_info *fi)
611 return (DIR *) (uintptr_t) fi->fh;
614 static int afuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
615 off_t offset, struct fuse_file_info *fi)
617 DIR *dp = get_dirp(fi);
619 char *root_name = alloca( strlen(path) );
620 char *real_path = alloca( max_path_out_len(path) );
621 mount_list_t *mount, *next;
625 switch( process_path(path, real_path, root_name, 1, &mount) )
627 case PROC_PATH_FAILED:
631 case PROC_PATH_ROOT_DIR:
632 filler(buf, ".", NULL, 0);
633 filler(buf, "..", NULL, 0);
634 for(mount = mount_list; mount; mount = next)
637 /* Check for dead mounts. */
638 if (!check_mount(mount))
641 filler(buf, mount->root_name, NULL, 0);
647 case PROC_PATH_PROXY_DIR:
649 while ((de = readdir(dp)) != NULL) {
651 memset(&st, 0, sizeof(st));
652 st.st_ino = de->d_ino;
653 st.st_mode = de->d_type << 12;
654 if (filler(buf, de->d_name, &st, telldir(dp)))
661 update_auto_unmount(mount);
666 static int afuse_releasedir(const char *path, struct fuse_file_info *fi)
668 DIR *dp = get_dirp(fi);
670 char *root_name = alloca( strlen(path) );
671 char *real_path = alloca( max_path_out_len(path) );
676 switch( process_path(path, real_path, root_name, 1, &mount) )
678 case PROC_PATH_FAILED:
682 case PROC_PATH_ROOT_DIR:
686 case PROC_PATH_PROXY_DIR:
688 dir_list_remove(&mount->dir_list, dp);
693 update_auto_unmount(mount);
698 static int afuse_mknod(const char *path, mode_t mode, dev_t rdev)
700 char *root_name = alloca( strlen(path) );
701 char *real_path = alloca( max_path_out_len(path) );
705 fprintf(stderr, "> Mknod\n");
707 switch( process_path(path, real_path, root_name, 0, &mount) )
709 case PROC_PATH_FAILED:
713 case PROC_PATH_ROOT_DIR:
717 case PROC_PATH_PROXY_DIR:
719 retval = get_retval(mkfifo(real_path, mode));
721 retval = get_retval(mknod(real_path, mode, rdev));
725 update_auto_unmount(mount);
730 static int afuse_mkdir(const char *path, mode_t mode)
732 char *root_name = alloca( strlen(path) );
733 char *real_path = alloca( max_path_out_len(path) );
738 switch( process_path(path, real_path, root_name, 0, &mount) )
740 case PROC_PATH_FAILED:
743 case PROC_PATH_ROOT_DIR:
746 case PROC_PATH_PROXY_DIR:
747 retval = get_retval(mkdir(real_path, mode));
751 update_auto_unmount(mount);
756 static int afuse_unlink(const char *path)
758 char *root_name = alloca( strlen(path) );
759 char *real_path = alloca( max_path_out_len(path) );
764 switch( process_path(path, real_path, root_name, 0, &mount) )
766 case PROC_PATH_FAILED:
769 case PROC_PATH_ROOT_DIR:
772 case PROC_PATH_PROXY_DIR:
773 retval = get_retval(unlink(real_path));
777 update_auto_unmount(mount);
782 static int afuse_rmdir(const char *path)
784 char *root_name = alloca( strlen(path) );
785 char *real_path = alloca( max_path_out_len(path) );
790 switch( process_path(path, real_path, root_name, 0, &mount) )
792 case PROC_PATH_FAILED:
795 case PROC_PATH_ROOT_DIR:
798 case PROC_PATH_PROXY_DIR:
799 if (!extract_root_name(path, root_name))
802 if (mount->dir_list || mount->fd_list)
811 retval = get_retval(rmdir(real_path));
815 update_auto_unmount(mount);
820 static int afuse_symlink(const char *from, const char *to)
822 char *root_name_to = alloca( strlen(to) );
823 char *real_to_path = alloca( max_path_out_len(to) );
828 switch( process_path(to, real_to_path, root_name_to, 0, &mount) )
830 case PROC_PATH_FAILED:
833 case PROC_PATH_ROOT_DIR:
836 case PROC_PATH_PROXY_DIR:
837 retval = get_retval(symlink(from, real_to_path));
841 update_auto_unmount(mount);
846 static int afuse_rename(const char *from, const char *to)
848 char *root_name_from = alloca( strlen(from) );
849 char *root_name_to = alloca( strlen(to) );
850 char *real_from_path = alloca( max_path_out_len(from) );
851 char *real_to_path = alloca( max_path_out_len(to) );
852 mount_list_t *mount_from, *mount_to = NULL;
856 switch( process_path(from, real_from_path, root_name_from, 0, &mount_from) )
858 case PROC_PATH_FAILED:
862 case PROC_PATH_ROOT_DIR:
866 case PROC_PATH_PROXY_DIR:
867 switch( process_path(to, real_to_path, root_name_to, 0, &mount_to) )
869 case PROC_PATH_FAILED:
873 case PROC_PATH_ROOT_DIR:
877 case PROC_PATH_PROXY_DIR:
878 retval = get_retval(rename(real_from_path, real_to_path));
884 update_auto_unmount(mount_to);
885 if (mount_from && mount_from != mount_to)
886 update_auto_unmount(mount_from);
891 static int afuse_link(const char *from, const char *to)
893 char *root_name_from = alloca( strlen(from) );
894 char *root_name_to = alloca( strlen(to) );
895 char *real_from_path = alloca( max_path_out_len(from) );
896 char *real_to_path = alloca( max_path_out_len(to) );
897 mount_list_t *mount_to = NULL, *mount_from;
901 switch( process_path(from, real_from_path, root_name_from, 0, &mount_from) )
903 case PROC_PATH_FAILED:
906 case PROC_PATH_ROOT_DIR:
909 case PROC_PATH_PROXY_DIR:
910 switch( process_path(to, real_to_path, root_name_to, 0, &mount_to) )
912 case PROC_PATH_FAILED:
915 case PROC_PATH_ROOT_DIR:
918 case PROC_PATH_PROXY_DIR:
919 retval = get_retval(link(real_from_path, real_to_path));
925 update_auto_unmount(mount_to);
926 if (mount_from && mount_from != mount_to)
927 update_auto_unmount(mount_from);
932 static int afuse_chmod(const char *path, mode_t mode)
934 char *root_name = alloca( strlen(path) );
935 char *real_path = alloca( max_path_out_len(path) );
940 switch( process_path(path, real_path, root_name, 0, &mount) )
942 case PROC_PATH_FAILED:
945 case PROC_PATH_ROOT_DIR:
948 case PROC_PATH_PROXY_DIR:
949 retval = get_retval(chmod(real_path, mode));
953 update_auto_unmount(mount);
958 static int afuse_chown(const char *path, uid_t uid, gid_t gid)
960 char *root_name = alloca( strlen(path) );
961 char *real_path = alloca( max_path_out_len(path) );
966 switch( process_path(path, real_path, root_name, 0, &mount) )
968 case PROC_PATH_FAILED:
971 case PROC_PATH_ROOT_DIR:
974 case PROC_PATH_PROXY_DIR:
975 retval = get_retval(lchown(real_path, uid, gid));
979 update_auto_unmount(mount);
984 static int afuse_truncate(const char *path, off_t size)
986 char *root_name = alloca( strlen(path) );
987 char *real_path = alloca( max_path_out_len(path) );
992 switch( process_path(path, real_path, root_name, 0, &mount) )
994 case PROC_PATH_FAILED:
997 case PROC_PATH_ROOT_DIR:
1000 case PROC_PATH_PROXY_DIR:
1001 retval = get_retval(truncate(real_path, size));
1005 update_auto_unmount(mount);
1011 static int afuse_utime(const char *path, struct utimbuf *buf)
1014 char *root_name = alloca( strlen(path) );
1015 char *real_path = alloca( max_path_out_len(path) );
1016 mount_list_t *mount;
1020 switch( process_path(path, real_path, root_name, 0, &mount) )
1022 case PROC_PATH_FAILED:
1025 case PROC_PATH_ROOT_DIR:
1028 case PROC_PATH_PROXY_DIR:
1029 retval = get_retval(utime(real_path, buf));
1033 update_auto_unmount(mount);
1039 static int afuse_open(const char *path, struct fuse_file_info *fi)
1042 char *root_name = alloca( strlen(path) );
1043 mount_list_t *mount;
1044 char *real_path = alloca( max_path_out_len(path) );
1048 switch( process_path(path, real_path, root_name, 1, &mount) )
1050 case PROC_PATH_FAILED:
1053 case PROC_PATH_ROOT_DIR:
1056 case PROC_PATH_PROXY_DIR:
1057 fd = open(real_path, fi->flags);
1065 fd_list_add(&mount->fd_list, fd);
1070 update_auto_unmount(mount);
1075 static int afuse_read(const char *path, char *buf, size_t size, off_t offset,
1076 struct fuse_file_info *fi)
1081 res = pread(fi->fh, buf, size, offset);
1088 static int afuse_write(const char *path, const char *buf, size_t size,
1089 off_t offset, struct fuse_file_info *fi)
1094 res = pwrite(fi->fh, buf, size, offset);
1098 if(user_options.flush_writes)
1105 static int afuse_release(const char *path, struct fuse_file_info *fi)
1107 char *root_name = alloca( strlen(path) );
1108 mount_list_t *mount;
1112 extract_root_name(path, root_name);
1113 mount = find_mount(root_name);
1114 retval = get_retval(close(fi->fh));
1118 fd_list_remove(&mount->fd_list, fi->fh);
1119 update_auto_unmount(mount);
1126 static int afuse_fsync(const char *path, int isdatasync,
1127 struct fuse_file_info *fi)
1132 #ifndef HAVE_FDATASYNC
1136 res = fdatasync(fi->fh);
1139 res = fsync(fi->fh);
1140 return get_retval(res);
1143 #if FUSE_VERSION >= 25
1144 static int afuse_access(const char *path, int mask)
1146 char *root_name = alloca( strlen(path) );
1147 char *real_path = alloca( max_path_out_len(path) );
1148 mount_list_t *mount;
1152 switch( process_path(path, real_path, root_name, 1, &mount) )
1154 case PROC_PATH_FAILED:
1157 case PROC_PATH_ROOT_DIR:
1158 case PROC_PATH_PROXY_DIR:
1159 retval = get_retval(access(real_path, mask));
1163 update_auto_unmount(mount);
1168 static int afuse_ftruncate(const char *path, off_t size,
1169 struct fuse_file_info *fi)
1172 return get_retval(ftruncate(fi->fh, size));
1175 static int afuse_create(const char *path, mode_t mode, struct fuse_file_info *fi)
1178 char *root_name = alloca( strlen(path) );
1179 char *real_path = alloca( max_path_out_len(path) );
1180 mount_list_t *mount;
1184 switch( process_path(path, real_path, root_name, 0, &mount) )
1186 case PROC_PATH_FAILED:
1189 case PROC_PATH_ROOT_DIR:
1192 case PROC_PATH_PROXY_DIR:
1193 fd = open(real_path, fi->flags, mode);
1204 update_auto_unmount(mount);
1209 static int afuse_fgetattr(const char *path, struct stat *stbuf,
1210 struct fuse_file_info *fi)
1214 return get_retval(fstat(fi->fh, stbuf));
1219 #if FUSE_VERSION >= 25
1220 static int afuse_statfs(const char *path, struct statvfs *stbuf)
1222 static int afuse_statfs(const char *path, struct statfs *stbuf)
1225 char *root_name = alloca( strlen(path) );
1226 char *real_path = alloca( max_path_out_len(path) );
1227 mount_list_t *mount;
1231 switch( process_path(path, real_path, root_name, 1, &mount) )
1233 case PROC_PATH_FAILED:
1237 case PROC_PATH_ROOT_DIR:
1238 #if FUSE_VERSION >= 25
1239 stbuf->f_namemax = 0x7fffffff;
1240 stbuf->f_frsize = 512;
1242 stbuf->f_namelen = 0x7fffffff;
1244 stbuf->f_bsize = 1024;
1245 stbuf->f_blocks = 0;
1247 stbuf->f_bavail = 0;
1253 case PROC_PATH_PROXY_DIR:
1254 retval = get_retval(statvfs(real_path, stbuf));
1258 update_auto_unmount(mount);
1263 void afuse_destroy(void *p)
1268 #ifdef HAVE_SETXATTR
1269 /* xattr operations are optional and can safely be left unimplemented */
1270 static int afuse_setxattr(const char *path, const char *name, const char *value,
1271 size_t size, int flags)
1273 char *root_name = alloca( strlen(path) );
1274 char *real_path = alloca( max_path_out_len(path) );
1275 mount_list_t *mount;
1279 switch( process_path(path, real_path, root_name, 0, &mount) )
1281 case PROC_PATH_FAILED:
1284 case PROC_PATH_ROOT_DIR:
1287 case PROC_PATH_PROXY_DIR:
1288 retval = get_retval(lsetxattr(real_path, name, value, size, flags));
1292 update_auto_unmount(mount);
1297 static int afuse_getxattr(const char *path, const char *name, char *value,
1300 char *root_name = alloca( strlen(path) );
1301 char *real_path = alloca( max_path_out_len(path) );
1302 mount_list_t *mount;
1306 switch( process_path(path, real_path, root_name, 1, &mount) )
1308 case PROC_PATH_FAILED:
1311 case PROC_PATH_ROOT_DIR:
1314 case PROC_PATH_PROXY_DIR:
1315 retval = get_retval(lgetxattr(real_path, name, value, size));
1319 update_auto_unmount(mount);
1324 static int afuse_listxattr(const char *path, char *list, size_t size)
1326 char *root_name = alloca( strlen(path) );
1327 char *real_path = alloca( max_path_out_len(path) );
1328 mount_list_t *mount;
1332 switch( process_path(path, real_path, root_name, 1, &mount) )
1334 case PROC_PATH_FAILED:
1337 case PROC_PATH_ROOT_DIR:
1340 case PROC_PATH_PROXY_DIR:
1341 retval = get_retval(llistxattr(real_path, list, size));
1345 update_auto_unmount(mount);
1350 static int afuse_removexattr(const char *path, const char *name)
1352 char *root_name = alloca( strlen(path) );
1353 char *real_path = alloca( max_path_out_len(path) );
1354 mount_list_t *mount;
1358 switch( process_path(path, real_path, root_name, 0, &mount) )
1360 case PROC_PATH_FAILED:
1363 case PROC_PATH_ROOT_DIR:
1366 case PROC_PATH_PROXY_DIR:
1367 retval = get_retval(lremovexattr(real_path, name));
1371 update_auto_unmount(mount);
1375 #endif /* HAVE_SETXATTR */
1377 static struct fuse_operations afuse_oper = {
1378 .getattr = afuse_getattr,
1379 .readlink = afuse_readlink,
1380 .opendir = afuse_opendir,
1381 .readdir = afuse_readdir,
1382 .releasedir = afuse_releasedir,
1383 .mknod = afuse_mknod,
1384 .mkdir = afuse_mkdir,
1385 .symlink = afuse_symlink,
1386 .unlink = afuse_unlink,
1387 .rmdir = afuse_rmdir,
1388 .rename = afuse_rename,
1390 .chmod = afuse_chmod,
1391 .chown = afuse_chown,
1392 .truncate = afuse_truncate,
1393 .utime = afuse_utime,
1396 .write = afuse_write,
1397 .release = afuse_release,
1398 .fsync = afuse_fsync,
1399 .statfs = afuse_statfs,
1400 #if FUSE_VERSION >= 25
1401 .access = afuse_access,
1402 .create = afuse_create,
1403 .ftruncate = afuse_ftruncate,
1404 .fgetattr = afuse_fgetattr,
1406 .destroy = afuse_destroy,
1407 #ifdef HAVE_SETXATTR
1408 .setxattr = afuse_setxattr,
1409 .getxattr = afuse_getxattr,
1410 .listxattr = afuse_listxattr,
1411 .removexattr = afuse_removexattr,
1421 #define AFUSE_OPT(t, p, v) { t, offsetof(struct user_options_t, p), v }
1423 static struct fuse_opt afuse_opts[] = {
1424 AFUSE_OPT("mount_template=%s", mount_command_template, 0),
1425 AFUSE_OPT("unmount_template=%s", unmount_command_template, 0),
1427 AFUSE_OPT("timeout=%Lu", auto_unmount_delay, 0),
1429 FUSE_OPT_KEY("flushwrites", KEY_FLUSHWRITES),
1430 FUSE_OPT_KEY("-h", KEY_HELP),
1431 FUSE_OPT_KEY("--help", KEY_HELP),
1436 static void usage(const char *progname)
1439 "Usage: %s mountpoint [options]\n"
1441 " -o opt,[opt...] mount options\n"
1442 " -h --help print help\n"
1443 " -V --version print FUSE version information\n"
1446 " -o mount_template=CMD template for CMD to execute to mount (*)\n"
1447 " -o unmount_template=CMD template for CMD to execute to unmount (*) (**)\n"
1448 " -o timeout=TIMEOUT automatically unmount after TIMEOUT microseconds\n"
1449 " -o flushwrites flushes data to disk for all file writes\n"
1451 " (*) - When executed, %%r and %%m are expanded in templates to the root\n"
1452 " directory name for the new mount point, and the actual directory to\n"
1453 " mount onto respectively to mount onto. Both templates are REQUIRED.\n"
1455 " (**) - The unmount command must perform a lazy unmount operation. E.g. the\n"
1456 " -u -z options to fusermount, or -l for regular mount.\n"
1460 static int afuse_opt_proc(void *data, const char *arg, int key,
1461 struct fuse_args *outargs)
1468 usage(outargs->argv[0]);
1469 fuse_opt_add_arg(outargs, "-ho");
1470 fuse_main(outargs->argc, outargs->argv, &afuse_oper);
1473 case KEY_FLUSHWRITES:
1474 user_options.flush_writes = true;
1482 int main(int argc, char *argv[])
1484 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1485 char *temp_dir_name = my_malloc(strlen(TMP_DIR_TEMPLATE));
1486 strcpy(temp_dir_name, TMP_DIR_TEMPLATE);
1488 if(fuse_opt_parse(&args, &user_options, afuse_opts, afuse_opt_proc) == -1)
1491 // !!FIXME!! force single-threading for now as datastructures are not locked
1492 fuse_opt_add_arg(&args, "-s");
1494 auto_unmount_ph_init(&auto_unmount_ph);
1497 * Install the SIGALRM signal handler.
1500 struct sigaction act;
1501 act.sa_handler = &handle_auto_unmount_timer;
1502 sigemptyset(&act.sa_mask);
1504 while (sigaction(SIGALRM, &act, NULL) == -1 && errno == EINTR)
1508 // Check for required parameters
1509 if(!user_options.mount_command_template || !user_options.unmount_command_template) {
1510 fprintf(stderr, "(Un)Mount command templates missing.\n\n");
1512 fuse_opt_add_arg(&args, "-ho");
1513 fuse_main(args.argc, args.argv, &afuse_oper);
1518 if( !(mount_point_directory = mkdtemp(temp_dir_name)) ) {
1519 fprintf(stderr, "Failed to create temporary mount point dir.\n");
1525 if (lstat(mount_point_directory, &buf) == -1) {
1526 fprintf(stderr, "Failed to stat temporary mount point dir.\n");
1529 mount_point_dev = buf.st_dev;
1535 // !!FIXME!! death by signal doesn't unmount fs
1536 return fuse_main(args.argc, args.argv, &afuse_oper);