2 afuse - An automounter using FUSE
3 Copyright (C) 2008 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.
13 Feb '08, Jeremy Maitin-Shepard <jbms@cmu.edu>
14 * Added timeout-based unmounting.
15 * Forced immediate unmounting by removing root directory of
17 * Minor performance tweaks.
18 * Better handling of filesystems which are unexpectedly
26 // For pread()/pwrite()
27 #define _XOPEN_SOURCE 500
44 #include <sys/types.h>
50 #include <sys/xattr.h>
57 #include "variable_pairing_heap.h"
59 #define TMP_DIR_TEMPLATE "/tmp/afuse-XXXXXX"
60 char *mount_point_directory;
61 dev_t mount_point_dev;
63 // Data structure filled in when parsing command line args
64 struct user_options_t {
65 char *mount_command_template;
66 char *unmount_command_template;
68 uint64_t auto_unmount_delay;
69 } user_options = {NULL, NULL, false, UINT64_MAX};
71 typedef struct _mount_list_t {
72 struct _mount_list_t *next;
73 struct _mount_list_t *prev;
80 PH_NEW_LINK(struct _mount_list_t) auto_unmount_ph_node;
81 /* This is the sort key for the auto_unmount_ph heap. It will
82 equal UINT64_MAX if this node is not in the heap. */
83 int64_t auto_unmount_time;
86 PH_DECLARE_TYPE(auto_unmount_ph, mount_list_t)
87 PH_DEFINE_TYPE(auto_unmount_ph, mount_list_t, auto_unmount_ph_node, auto_unmount_time)
89 #define BLOCK_SIGALRM \
90 sigset_t block_sigalrm_oldset, block_sigalrm_set; \
91 sigemptyset(&block_sigalrm_set); \
92 sigaddset(&block_sigalrm_set, SIGALRM); \
93 sigprocmask(SIG_BLOCK, &block_sigalrm_set, &block_sigalrm_oldset)
95 #define UNBLOCK_SIGALRM \
96 sigprocmask(SIG_SETMASK, &block_sigalrm_oldset, NULL)
98 auto_unmount_ph_t auto_unmount_ph;
99 int64_t auto_unmount_next_timeout = INT64_MAX;
101 static int64_t from_timeval(const struct timeval *tv)
103 return (int64_t)tv->tv_sec * 1000000 + ((int64_t)tv->tv_usec);
106 static void to_timeval(struct timeval *tv, int64_t usec)
108 tv->tv_sec = usec / 1000000;
109 tv->tv_usec = usec % 1000000;
112 static int get_retval(int res)
119 static int check_mount(mount_list_t *mount)
123 if (lstat(mount->mount_point, &buf) == -1)
125 if (buf.st_dev == mount_point_dev)
130 static void update_auto_unmount(mount_list_t *mount)
132 if (user_options.auto_unmount_delay == UINT64_MAX)
135 /* Get the current time */
137 mount_list_t *min_mount;
138 int64_t cur_time, next_time;
139 gettimeofday(&tv, NULL);
140 cur_time = from_timeval(&tv);
144 /* Always remove first */
145 if (mount->auto_unmount_time != INT64_MAX)
146 auto_unmount_ph_remove(&auto_unmount_ph, mount);
148 if (!mount->fd_list && !mount->dir_list)
150 mount->auto_unmount_time = cur_time + user_options.auto_unmount_delay;
151 auto_unmount_ph_insert(&auto_unmount_ph, mount);
154 mount->auto_unmount_time = INT64_MAX;
157 min_mount = auto_unmount_ph_min(&auto_unmount_ph);
158 next_time = min_mount ? min_mount->auto_unmount_time : INT64_MAX;
160 if (next_time != auto_unmount_next_timeout)
162 struct itimerval itv;
163 auto_unmount_next_timeout = next_time;
165 if (next_time != INT64_MAX)
167 if (next_time > cur_time)
168 to_timeval(&itv.it_value, next_time - cur_time);
169 else /* Timer is set to expire immediately --- set it to 1 instead*/
170 to_timeval(&itv.it_value, 1);
173 /* Disable the timer */
174 to_timeval(&itv.it_value, 0);
176 to_timeval(&itv.it_interval, 0);
177 if (setitimer(ITIMER_REAL, &itv, NULL) != 0) {
178 perror("Error setting timer");
183 static void handle_auto_unmount_timer(int x)
185 /* Get the current time */
189 gettimeofday(&tv, NULL);
190 cur_time = from_timeval(&tv);
192 while ((mount = auto_unmount_ph_min(&auto_unmount_ph)) != NULL &&
193 mount->auto_unmount_time <= cur_time)
198 update_auto_unmount(NULL);
201 mount_list_t *mount_list = NULL;
203 mount_list_t *find_mount(const char *root_name)
205 mount_list_t *current_mount = mount_list;
207 while(current_mount) {
208 if( strcmp(root_name, current_mount->root_name) == 0)
209 return current_mount;
211 current_mount = current_mount->next;
217 int is_mount(const char *root_name)
219 return find_mount(root_name) ? 1 : 0;
222 mount_list_t *add_mount(const char *root_name, char *mount_point)
224 mount_list_t *new_mount;
226 new_mount = (mount_list_t *)my_malloc( sizeof(mount_list_t) );
227 new_mount->root_name = my_strdup(root_name);
228 new_mount->mount_point = mount_point;
230 new_mount->next = mount_list;
231 new_mount->prev = NULL;
232 new_mount->fd_list = NULL;
233 new_mount->dir_list = NULL;
234 new_mount->auto_unmount_time = INT64_MAX;
236 mount_list->prev = new_mount;
238 mount_list = new_mount;
240 update_auto_unmount(new_mount);
245 void remove_mount(mount_list_t *current_mount)
247 if (current_mount->auto_unmount_time != INT64_MAX)
248 auto_unmount_ph_remove(&auto_unmount_ph, current_mount);
250 free(current_mount->root_name);
251 free(current_mount->mount_point);
252 if(current_mount->prev)
253 current_mount->prev->next = current_mount->next;
255 mount_list = current_mount->next;
256 if(current_mount->next)
257 current_mount->next->prev = current_mount->prev;
259 update_auto_unmount(NULL);
262 char *make_mount_point(const char *root_name)
267 // Create the mount point
268 dir_tmp = my_malloc(strlen(mount_point_directory) + 2 + strlen(root_name));
269 strcpy(dir_tmp, mount_point_directory);
270 strcat(dir_tmp, "/");
271 strcat(dir_tmp, root_name);
273 if(mkdir(dir_tmp, 0700) == -1 && errno != EEXIST) {
274 fprintf(stderr, "Cannot create directory: %s (%s)\n",
275 dir_tmp, strerror(errno));
283 // !!FIXME!! allow escaping of %'s
284 // Note: this method strips out quotes and applies them itself as should be appropriate
285 char *expand_template(const char *template, const char *mount_point, const char *root_name)
290 char *expanded_name_start;
293 for(i = 0; template[i]; i++)
294 if(template[i] == '%') {
295 switch(template[i + 1])
298 len += strlen(mount_point) + 2;
302 len += strlen(root_name) + 2;
306 } else if(template[i] != '"')
309 expanded_name_start = expanded_name = my_malloc(len + 1);
311 for(i = 0; template[i]; i++)
312 if(template[i] == '%') {
314 switch(template[i + 1])
317 *expanded_name++ = '"';
318 while(mount_point[j])
319 *expanded_name++ = mount_point[j++];
320 *expanded_name++ = '"';
324 *expanded_name++ = '"';
326 *expanded_name++ = root_name[j++];
327 *expanded_name++ = '"';
331 } else if(template[i] != '"')
332 *expanded_name++ = template[i];
334 *expanded_name = '\0';
336 return expanded_name_start;
339 mount_list_t *do_mount(const char *root_name)
346 fprintf(stderr, "Mounting: %s\n", root_name);
348 if( !(mount_point = make_mount_point(root_name)) ) {
349 fprintf(stderr, "Failed to create mount point directory: %s/%s\n",
350 mount_point_directory, root_name);
354 mount_command = expand_template(user_options.mount_command_template,
355 mount_point, root_name);
356 sysret = system(mount_command);
358 fprintf(stderr, "sysret: %.8x\n", sysret);
361 fprintf(stderr, "Failed to invoke mount command: '%s' (%s)\n",
362 mount_command, sysret != -1 ?
363 "Error executing mount" :
366 // remove the now unused directory
367 if( rmdir(mount_point) == -1 )
368 fprintf(stderr, "Failed to remove mount point dir: %s (%s)",
369 mount_point, strerror(errno));
376 mount = add_mount(root_name, mount_point);
382 int do_umount(mount_list_t *mount)
384 char *unmount_command;
387 fprintf(stderr, "Unmounting: %s\n", mount->root_name);
389 unmount_command = expand_template(user_options.unmount_command_template,
390 mount->mount_point, mount->root_name);
391 sysret = system(unmount_command);
393 fprintf(stderr, "Failed to invoke unmount command: '%s' (%s)\n",
394 unmount_command, sysret != -1 ?
395 "Error executing mount" :
397 /* Still unmount anyway */
400 if( rmdir(mount->mount_point) == -1 )
401 fprintf(stderr, "Failed to remove mount point dir: %s (%s)",
402 mount->mount_point, strerror(errno));
404 free(unmount_command);
408 void unmount_all(void)
410 fprintf(stderr, "Attempting to unmount all filesystems:\n");
413 fprintf(stderr, "\tUnmounting: %s\n", mount_list->root_name);
415 do_umount(mount_list);
418 fprintf(stderr, "done.\n");
429 if(rmdir(mount_point_directory) == -1)
430 fprintf(stderr, "Failed to remove temporary mount point directory: %s (%s)\n",
431 mount_point_directory, strerror(errno));
434 int max_path_out_len(const char *path_in)
436 return strlen(mount_point_directory) + strlen(path_in) + 2;
439 // returns true if path is the a child directory of a root node
440 // e.g. /a/b is a child, /a is not.
441 int extract_root_name(const char *path, char *root_name)
446 for(i = 1; path[i] && path[i] != '/'; i++)
447 root_name[i - 1] = path[i];
448 root_name[i - 1] = '\0';
450 return strlen(&path[i]);
453 typedef enum {PROC_PATH_FAILED, PROC_PATH_ROOT_DIR, PROC_PATH_PROXY_DIR} proc_result_t;
455 proc_result_t process_path(const char *path_in, char *path_out, char *root_name,
456 int attempt_mount, mount_list_t **out_mount)
462 mount_list_t *mount = NULL;
466 fprintf(stderr, "Path in: %s\n", path_in);
467 is_child = extract_root_name(path_in, root_name);
468 fprintf(stderr, "root_name is: %s\n", root_name);
470 // Mount filesystem if necessary
471 // the combination of is_child and attempt_mount prevent inappropriate
472 // mounting of a filesystem for example if the user tries to mknod
473 // in the afuse root this should cause an error not a mount.
474 // !!FIXME!! this is broken on FUSE < 2.5 (?) because a getattr
475 // on the root node seems to occur with every single access.
476 if( /*(is_child || attempt_mount ) && */
477 strlen(root_name) > 0 &&
478 !(mount = find_mount(root_name)) &&
479 !(mount = do_mount(root_name)))
480 return PROC_PATH_FAILED;
482 if (mount && !check_mount(mount))
485 mount = do_mount(root_name);
487 return PROC_PATH_FAILED;
490 // construct path_out (mount_point_directory + '/' + path_in + '\0')
491 path_out_base = path_out;
492 len = strlen(mount_point_directory);
493 memcpy(path_out, mount_point_directory, len);
496 len = strlen(path_in) - 1;
497 memcpy(path_out, path_in + 1, len);
500 fprintf(stderr, "Path out: %s\n", path_out_base);
504 return strlen(root_name) ? PROC_PATH_PROXY_DIR : PROC_PATH_ROOT_DIR;
507 static int afuse_getattr(const char *path, struct stat *stbuf)
510 char *root_name = alloca( strlen(path) );
511 char *real_path = alloca( max_path_out_len(path) );
516 fprintf(stderr, "> GetAttr\n");
518 switch( process_path(path, real_path, root_name, 0, &mount) )
520 case PROC_PATH_FAILED:
524 case PROC_PATH_ROOT_DIR:
525 fprintf(stderr, "Getattr on: (%s) - %s\n", path, root_name);
526 if(mount || strlen(root_name) == 0) {
527 stbuf->st_mode = S_IFDIR | 0700;
529 stbuf->st_uid = getuid();
530 stbuf->st_gid = getgid();
532 stbuf->st_blksize = 0;
533 stbuf->st_blocks = 0;
542 case PROC_PATH_PROXY_DIR:
543 retval = get_retval(lstat(real_path, stbuf));
547 update_auto_unmount(mount);
552 static int afuse_readlink(const char *path, char *buf, size_t size)
555 char *root_name = alloca( strlen(path) );
556 char *real_path = alloca( max_path_out_len(path) );
561 switch( process_path(path, real_path, root_name, 1, &mount) )
563 case PROC_PATH_FAILED:
566 case PROC_PATH_ROOT_DIR:
569 case PROC_PATH_PROXY_DIR:
570 res = readlink(real_path, buf, size - 1);
581 update_auto_unmount(mount);
586 static int afuse_opendir(const char *path, struct fuse_file_info *fi)
589 char *root_name = alloca( strlen(path) );
591 char *real_path = alloca( max_path_out_len(path) );
595 switch( process_path(path, real_path, root_name, 1, &mount) )
597 case PROC_PATH_FAILED:
600 case PROC_PATH_ROOT_DIR:
603 case PROC_PATH_PROXY_DIR:
604 dp = opendir(real_path);
609 fi->fh = (unsigned long) dp;
611 dir_list_add(&mount->dir_list, dp);
616 update_auto_unmount(mount);
621 static inline DIR *get_dirp(struct fuse_file_info *fi)
623 return (DIR *) (uintptr_t) fi->fh;
626 static int afuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
627 off_t offset, struct fuse_file_info *fi)
629 DIR *dp = get_dirp(fi);
631 char *root_name = alloca( strlen(path) );
632 char *real_path = alloca( max_path_out_len(path) );
633 mount_list_t *mount, *next;
637 switch( process_path(path, real_path, root_name, 1, &mount) )
639 case PROC_PATH_FAILED:
643 case PROC_PATH_ROOT_DIR:
644 filler(buf, ".", NULL, 0);
645 filler(buf, "..", NULL, 0);
646 for(mount = mount_list; mount; mount = next)
649 /* Check for dead mounts. */
650 if (!check_mount(mount))
653 filler(buf, mount->root_name, NULL, 0);
659 case PROC_PATH_PROXY_DIR:
661 while ((de = readdir(dp)) != NULL) {
663 memset(&st, 0, sizeof(st));
664 st.st_ino = de->d_ino;
665 st.st_mode = de->d_type << 12;
666 if (filler(buf, de->d_name, &st, telldir(dp)))
673 update_auto_unmount(mount);
678 static int afuse_releasedir(const char *path, struct fuse_file_info *fi)
680 DIR *dp = get_dirp(fi);
682 char *root_name = alloca( strlen(path) );
683 char *real_path = alloca( max_path_out_len(path) );
688 switch( process_path(path, real_path, root_name, 1, &mount) )
690 case PROC_PATH_FAILED:
694 case PROC_PATH_ROOT_DIR:
698 case PROC_PATH_PROXY_DIR:
700 dir_list_remove(&mount->dir_list, dp);
705 update_auto_unmount(mount);
710 static int afuse_mknod(const char *path, mode_t mode, dev_t rdev)
712 char *root_name = alloca( strlen(path) );
713 char *real_path = alloca( max_path_out_len(path) );
717 fprintf(stderr, "> Mknod\n");
719 switch( process_path(path, real_path, root_name, 0, &mount) )
721 case PROC_PATH_FAILED:
725 case PROC_PATH_ROOT_DIR:
729 case PROC_PATH_PROXY_DIR:
731 retval = get_retval(mkfifo(real_path, mode));
733 retval = get_retval(mknod(real_path, mode, rdev));
737 update_auto_unmount(mount);
742 static int afuse_mkdir(const char *path, mode_t mode)
744 char *root_name = alloca( strlen(path) );
745 char *real_path = alloca( max_path_out_len(path) );
750 switch( process_path(path, real_path, root_name, 0, &mount) )
752 case PROC_PATH_FAILED:
755 case PROC_PATH_ROOT_DIR:
758 case PROC_PATH_PROXY_DIR:
759 retval = get_retval(mkdir(real_path, mode));
763 update_auto_unmount(mount);
768 static int afuse_unlink(const char *path)
770 char *root_name = alloca( strlen(path) );
771 char *real_path = alloca( max_path_out_len(path) );
776 switch( process_path(path, real_path, root_name, 0, &mount) )
778 case PROC_PATH_FAILED:
781 case PROC_PATH_ROOT_DIR:
784 case PROC_PATH_PROXY_DIR:
785 retval = get_retval(unlink(real_path));
789 update_auto_unmount(mount);
794 static int afuse_rmdir(const char *path)
796 char *root_name = alloca( strlen(path) );
797 char *real_path = alloca( max_path_out_len(path) );
802 switch( process_path(path, real_path, root_name, 0, &mount) )
804 case PROC_PATH_FAILED:
807 case PROC_PATH_ROOT_DIR:
810 case PROC_PATH_PROXY_DIR:
811 if (!extract_root_name(path, root_name))
814 if (mount->dir_list || mount->fd_list)
823 retval = get_retval(rmdir(real_path));
827 update_auto_unmount(mount);
832 static int afuse_symlink(const char *from, const char *to)
834 char *root_name_to = alloca( strlen(to) );
835 char *real_to_path = alloca( max_path_out_len(to) );
840 switch( process_path(to, real_to_path, root_name_to, 0, &mount) )
842 case PROC_PATH_FAILED:
845 case PROC_PATH_ROOT_DIR:
848 case PROC_PATH_PROXY_DIR:
849 retval = get_retval(symlink(from, real_to_path));
853 update_auto_unmount(mount);
858 static int afuse_rename(const char *from, const char *to)
860 char *root_name_from = alloca( strlen(from) );
861 char *root_name_to = alloca( strlen(to) );
862 char *real_from_path = alloca( max_path_out_len(from) );
863 char *real_to_path = alloca( max_path_out_len(to) );
864 mount_list_t *mount_from, *mount_to = NULL;
868 switch( process_path(from, real_from_path, root_name_from, 0, &mount_from) )
870 case PROC_PATH_FAILED:
874 case PROC_PATH_ROOT_DIR:
878 case PROC_PATH_PROXY_DIR:
879 switch( process_path(to, real_to_path, root_name_to, 0, &mount_to) )
881 case PROC_PATH_FAILED:
885 case PROC_PATH_ROOT_DIR:
889 case PROC_PATH_PROXY_DIR:
890 retval = get_retval(rename(real_from_path, real_to_path));
896 update_auto_unmount(mount_to);
897 if (mount_from && mount_from != mount_to)
898 update_auto_unmount(mount_from);
903 static int afuse_link(const char *from, const char *to)
905 char *root_name_from = alloca( strlen(from) );
906 char *root_name_to = alloca( strlen(to) );
907 char *real_from_path = alloca( max_path_out_len(from) );
908 char *real_to_path = alloca( max_path_out_len(to) );
909 mount_list_t *mount_to = NULL, *mount_from;
913 switch( process_path(from, real_from_path, root_name_from, 0, &mount_from) )
915 case PROC_PATH_FAILED:
918 case PROC_PATH_ROOT_DIR:
921 case PROC_PATH_PROXY_DIR:
922 switch( process_path(to, real_to_path, root_name_to, 0, &mount_to) )
924 case PROC_PATH_FAILED:
927 case PROC_PATH_ROOT_DIR:
930 case PROC_PATH_PROXY_DIR:
931 retval = get_retval(link(real_from_path, real_to_path));
937 update_auto_unmount(mount_to);
938 if (mount_from && mount_from != mount_to)
939 update_auto_unmount(mount_from);
944 static int afuse_chmod(const char *path, mode_t mode)
946 char *root_name = alloca( strlen(path) );
947 char *real_path = alloca( max_path_out_len(path) );
952 switch( process_path(path, real_path, root_name, 0, &mount) )
954 case PROC_PATH_FAILED:
957 case PROC_PATH_ROOT_DIR:
960 case PROC_PATH_PROXY_DIR:
961 retval = get_retval(chmod(real_path, mode));
965 update_auto_unmount(mount);
970 static int afuse_chown(const char *path, uid_t uid, gid_t gid)
972 char *root_name = alloca( strlen(path) );
973 char *real_path = alloca( max_path_out_len(path) );
978 switch( process_path(path, real_path, root_name, 0, &mount) )
980 case PROC_PATH_FAILED:
983 case PROC_PATH_ROOT_DIR:
986 case PROC_PATH_PROXY_DIR:
987 retval = get_retval(lchown(real_path, uid, gid));
991 update_auto_unmount(mount);
996 static int afuse_truncate(const char *path, off_t size)
998 char *root_name = alloca( strlen(path) );
999 char *real_path = alloca( max_path_out_len(path) );
1000 mount_list_t *mount;
1004 switch( process_path(path, real_path, root_name, 0, &mount) )
1006 case PROC_PATH_FAILED:
1009 case PROC_PATH_ROOT_DIR:
1012 case PROC_PATH_PROXY_DIR:
1013 retval = get_retval(truncate(real_path, size));
1017 update_auto_unmount(mount);
1023 static int afuse_utime(const char *path, struct utimbuf *buf)
1026 char *root_name = alloca( strlen(path) );
1027 char *real_path = alloca( max_path_out_len(path) );
1028 mount_list_t *mount;
1032 switch( process_path(path, real_path, root_name, 0, &mount) )
1034 case PROC_PATH_FAILED:
1037 case PROC_PATH_ROOT_DIR:
1040 case PROC_PATH_PROXY_DIR:
1041 retval = get_retval(utime(real_path, buf));
1045 update_auto_unmount(mount);
1051 static int afuse_open(const char *path, struct fuse_file_info *fi)
1054 char *root_name = alloca( strlen(path) );
1055 mount_list_t *mount;
1056 char *real_path = alloca( max_path_out_len(path) );
1060 switch( process_path(path, real_path, root_name, 1, &mount) )
1062 case PROC_PATH_FAILED:
1065 case PROC_PATH_ROOT_DIR:
1068 case PROC_PATH_PROXY_DIR:
1069 fd = open(real_path, fi->flags);
1077 fd_list_add(&mount->fd_list, fd);
1082 update_auto_unmount(mount);
1087 static int afuse_read(const char *path, char *buf, size_t size, off_t offset,
1088 struct fuse_file_info *fi)
1093 res = pread(fi->fh, buf, size, offset);
1100 static int afuse_write(const char *path, const char *buf, size_t size,
1101 off_t offset, struct fuse_file_info *fi)
1106 res = pwrite(fi->fh, buf, size, offset);
1110 if(user_options.flush_writes)
1117 static int afuse_release(const char *path, struct fuse_file_info *fi)
1119 char *root_name = alloca( strlen(path) );
1120 mount_list_t *mount;
1124 extract_root_name(path, root_name);
1125 mount = find_mount(root_name);
1126 retval = get_retval(close(fi->fh));
1130 fd_list_remove(&mount->fd_list, fi->fh);
1131 update_auto_unmount(mount);
1138 static int afuse_fsync(const char *path, int isdatasync,
1139 struct fuse_file_info *fi)
1144 #ifndef HAVE_FDATASYNC
1148 res = fdatasync(fi->fh);
1151 res = fsync(fi->fh);
1152 return get_retval(res);
1155 #if FUSE_VERSION >= 25
1156 static int afuse_access(const char *path, int mask)
1158 char *root_name = alloca( strlen(path) );
1159 char *real_path = alloca( max_path_out_len(path) );
1160 mount_list_t *mount;
1164 switch( process_path(path, real_path, root_name, 1, &mount) )
1166 case PROC_PATH_FAILED:
1169 case PROC_PATH_ROOT_DIR:
1170 case PROC_PATH_PROXY_DIR:
1171 retval = get_retval(access(real_path, mask));
1175 update_auto_unmount(mount);
1180 static int afuse_ftruncate(const char *path, off_t size,
1181 struct fuse_file_info *fi)
1184 return get_retval(ftruncate(fi->fh, size));
1187 static int afuse_create(const char *path, mode_t mode, struct fuse_file_info *fi)
1190 char *root_name = alloca( strlen(path) );
1191 char *real_path = alloca( max_path_out_len(path) );
1192 mount_list_t *mount;
1196 switch( process_path(path, real_path, root_name, 0, &mount) )
1198 case PROC_PATH_FAILED:
1201 case PROC_PATH_ROOT_DIR:
1204 case PROC_PATH_PROXY_DIR:
1205 fd = open(real_path, fi->flags, mode);
1216 update_auto_unmount(mount);
1221 static int afuse_fgetattr(const char *path, struct stat *stbuf,
1222 struct fuse_file_info *fi)
1226 return get_retval(fstat(fi->fh, stbuf));
1231 #if FUSE_VERSION >= 25
1232 static int afuse_statfs(const char *path, struct statvfs *stbuf)
1234 static int afuse_statfs(const char *path, struct statfs *stbuf)
1237 char *root_name = alloca( strlen(path) );
1238 char *real_path = alloca( max_path_out_len(path) );
1239 mount_list_t *mount;
1243 switch( process_path(path, real_path, root_name, 1, &mount) )
1245 case PROC_PATH_FAILED:
1249 case PROC_PATH_ROOT_DIR:
1250 #if FUSE_VERSION >= 25
1251 stbuf->f_namemax = 0x7fffffff;
1252 stbuf->f_frsize = 512;
1254 stbuf->f_namelen = 0x7fffffff;
1256 stbuf->f_bsize = 1024;
1257 stbuf->f_blocks = 0;
1259 stbuf->f_bavail = 0;
1265 case PROC_PATH_PROXY_DIR:
1266 retval = get_retval(statvfs(real_path, stbuf));
1270 update_auto_unmount(mount);
1275 void afuse_destroy(void *p)
1280 #ifdef HAVE_SETXATTR
1281 /* xattr operations are optional and can safely be left unimplemented */
1282 static int afuse_setxattr(const char *path, const char *name, const char *value,
1283 size_t size, int flags)
1285 char *root_name = alloca( strlen(path) );
1286 char *real_path = alloca( max_path_out_len(path) );
1287 mount_list_t *mount;
1291 switch( process_path(path, real_path, root_name, 0, &mount) )
1293 case PROC_PATH_FAILED:
1296 case PROC_PATH_ROOT_DIR:
1299 case PROC_PATH_PROXY_DIR:
1300 retval = get_retval(lsetxattr(real_path, name, value, size, flags));
1304 update_auto_unmount(mount);
1309 static int afuse_getxattr(const char *path, const char *name, char *value,
1312 char *root_name = alloca( strlen(path) );
1313 char *real_path = alloca( max_path_out_len(path) );
1314 mount_list_t *mount;
1318 switch( process_path(path, real_path, root_name, 1, &mount) )
1320 case PROC_PATH_FAILED:
1323 case PROC_PATH_ROOT_DIR:
1326 case PROC_PATH_PROXY_DIR:
1327 retval = get_retval(lgetxattr(real_path, name, value, size));
1331 update_auto_unmount(mount);
1336 static int afuse_listxattr(const char *path, char *list, size_t size)
1338 char *root_name = alloca( strlen(path) );
1339 char *real_path = alloca( max_path_out_len(path) );
1340 mount_list_t *mount;
1344 switch( process_path(path, real_path, root_name, 1, &mount) )
1346 case PROC_PATH_FAILED:
1349 case PROC_PATH_ROOT_DIR:
1352 case PROC_PATH_PROXY_DIR:
1353 retval = get_retval(llistxattr(real_path, list, size));
1357 update_auto_unmount(mount);
1362 static int afuse_removexattr(const char *path, const char *name)
1364 char *root_name = alloca( strlen(path) );
1365 char *real_path = alloca( max_path_out_len(path) );
1366 mount_list_t *mount;
1370 switch( process_path(path, real_path, root_name, 0, &mount) )
1372 case PROC_PATH_FAILED:
1375 case PROC_PATH_ROOT_DIR:
1378 case PROC_PATH_PROXY_DIR:
1379 retval = get_retval(lremovexattr(real_path, name));
1383 update_auto_unmount(mount);
1387 #endif /* HAVE_SETXATTR */
1389 static struct fuse_operations afuse_oper = {
1390 .getattr = afuse_getattr,
1391 .readlink = afuse_readlink,
1392 .opendir = afuse_opendir,
1393 .readdir = afuse_readdir,
1394 .releasedir = afuse_releasedir,
1395 .mknod = afuse_mknod,
1396 .mkdir = afuse_mkdir,
1397 .symlink = afuse_symlink,
1398 .unlink = afuse_unlink,
1399 .rmdir = afuse_rmdir,
1400 .rename = afuse_rename,
1402 .chmod = afuse_chmod,
1403 .chown = afuse_chown,
1404 .truncate = afuse_truncate,
1405 .utime = afuse_utime,
1408 .write = afuse_write,
1409 .release = afuse_release,
1410 .fsync = afuse_fsync,
1411 .statfs = afuse_statfs,
1412 #if FUSE_VERSION >= 25
1413 .access = afuse_access,
1414 .create = afuse_create,
1415 .ftruncate = afuse_ftruncate,
1416 .fgetattr = afuse_fgetattr,
1418 .destroy = afuse_destroy,
1419 #ifdef HAVE_SETXATTR
1420 .setxattr = afuse_setxattr,
1421 .getxattr = afuse_getxattr,
1422 .listxattr = afuse_listxattr,
1423 .removexattr = afuse_removexattr,
1433 #define AFUSE_OPT(t, p, v) { t, offsetof(struct user_options_t, p), v }
1435 static struct fuse_opt afuse_opts[] = {
1436 AFUSE_OPT("mount_template=%s", mount_command_template, 0),
1437 AFUSE_OPT("unmount_template=%s", unmount_command_template, 0),
1439 AFUSE_OPT("timeout=%Lu", auto_unmount_delay, 0),
1441 FUSE_OPT_KEY("flushwrites", KEY_FLUSHWRITES),
1442 FUSE_OPT_KEY("-h", KEY_HELP),
1443 FUSE_OPT_KEY("--help", KEY_HELP),
1448 static void usage(const char *progname)
1451 "Usage: %s mountpoint [options]\n"
1453 " -o opt,[opt...] mount options\n"
1454 " -h --help print help\n"
1455 " -V --version print FUSE version information\n"
1458 " -o mount_template=CMD template for CMD to execute to mount (*)\n"
1459 " -o unmount_template=CMD template for CMD to execute to unmount (*) (**)\n"
1460 " -o timeout=TIMEOUT automatically unmount after TIMEOUT seconds\n"
1461 " -o flushwrites flushes data to disk for all file writes\n"
1463 " (*) - When executed, %%r and %%m are expanded in templates to the root\n"
1464 " directory name for the new mount point, and the actual directory to\n"
1465 " mount onto respectively to mount onto. Both templates are REQUIRED.\n"
1467 " (**) - The unmount command must perform a lazy unmount operation. E.g. the\n"
1468 " -u -z options to fusermount, or -l for regular mount.\n"
1472 static int afuse_opt_proc(void *data, const char *arg, int key,
1473 struct fuse_args *outargs)
1480 usage(outargs->argv[0]);
1481 fuse_opt_add_arg(outargs, "-ho");
1482 fuse_main(outargs->argc, outargs->argv, &afuse_oper);
1485 case KEY_FLUSHWRITES:
1486 user_options.flush_writes = true;
1494 int main(int argc, char *argv[])
1496 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1497 char *temp_dir_name = my_malloc(strlen(TMP_DIR_TEMPLATE));
1498 strcpy(temp_dir_name, TMP_DIR_TEMPLATE);
1500 if(fuse_opt_parse(&args, &user_options, afuse_opts, afuse_opt_proc) == -1)
1503 // !!FIXME!! force single-threading for now as data structures are not locked
1504 fuse_opt_add_arg(&args, "-s");
1506 // Adjust user specified timeout from seconds to microseconds as required
1507 user_options.auto_unmount_delay *= 1000000;
1509 auto_unmount_ph_init(&auto_unmount_ph);
1512 * Install the SIGALRM signal handler.
1515 struct sigaction act;
1516 act.sa_handler = &handle_auto_unmount_timer;
1517 sigemptyset(&act.sa_mask);
1519 while (sigaction(SIGALRM, &act, NULL) == -1 && errno == EINTR)
1523 // Check for required parameters
1524 if(!user_options.mount_command_template || !user_options.unmount_command_template) {
1525 fprintf(stderr, "(Un)Mount command templates missing.\n\n");
1527 fuse_opt_add_arg(&args, "-ho");
1528 fuse_main(args.argc, args.argv, &afuse_oper);
1533 if( !(mount_point_directory = mkdtemp(temp_dir_name)) ) {
1534 fprintf(stderr, "Failed to create temporary mount point dir.\n");
1540 if (lstat(mount_point_directory, &buf) == -1) {
1541 fprintf(stderr, "Failed to stat temporary mount point dir.\n");
1544 mount_point_dev = buf.st_dev;
1550 // !!FIXME!! death by signal doesn't unmount fs
1551 return fuse_main(args.argc, args.argv, &afuse_oper);