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>
38 #include <sys/xattr.h>
45 #define TMP_DIR_TEMPLATE "/tmp/afuse-XXXXXX"
46 char *mount_point_directory;
48 // Data structure filled in when parsing command line args
49 struct user_options_t {
50 char *mount_command_template;
51 char *unmount_command_template;
52 bool mount_inuse_only;
53 bool mount_inuse_only_all;
55 } user_options = {NULL, NULL, false, false, false};
57 typedef struct _mount_list_t {
58 struct _mount_list_t *next;
59 struct _mount_list_t *prev;
66 mount_list_t *mount_list = NULL;
68 mount_list_t *find_mount(const char *root_name)
70 mount_list_t *current_mount = mount_list;
72 while(current_mount) {
73 if( strcmp(root_name, current_mount->root_name) == 0)
76 current_mount = current_mount->next;
82 int is_mount(const char *root_name)
84 return find_mount(root_name) ? 1 : 0;
87 void add_mount(const char *root_name)
89 mount_list_t *new_mount;
91 new_mount = (mount_list_t *)my_malloc( sizeof(mount_list_t) );
92 new_mount->root_name = my_strdup(root_name);
94 new_mount->next = mount_list;
95 new_mount->prev = NULL;
96 new_mount->fd_list = NULL;
97 new_mount->dir_list = NULL;
99 mount_list->prev = new_mount;
101 mount_list = new_mount;
104 void remove_mount(const char *root_name)
106 mount_list_t *current_mount = mount_list;
108 while(current_mount) {
109 if( strcmp(root_name, current_mount->root_name) == 0) {
110 free(current_mount->root_name);
111 if(current_mount->prev)
112 current_mount->prev->next = current_mount->next;
114 mount_list = current_mount->next;
115 if(current_mount->next)
116 current_mount->next->prev = current_mount->prev;
122 current_mount = current_mount->next;
126 int make_mount_point(const char *root_name)
131 // First create the mount_point_directory
132 dir_tmp = my_strdup(mount_point_directory);
133 for(i = 0; dir_tmp[i]; i++)
134 if(dir_tmp[i] == '/' && i != 0) {
136 if(mkdir(dir_tmp, 0700) == -1 && errno != EEXIST) {
137 fprintf(stderr, "Cannot create directory: %s (%s)\n",
138 dir_tmp, strerror(errno));
145 // Create the mount point
146 dir_tmp = my_malloc(strlen(mount_point_directory) + 2 + strlen(root_name));
147 strcpy(dir_tmp, mount_point_directory);
148 strcat(dir_tmp, "/");
149 strcat(dir_tmp, root_name);
151 if(mkdir(dir_tmp, 0700) == -1 && errno != EEXIST) {
152 fprintf(stderr, "Cannot create directory: %s (%s)\n",
153 dir_tmp, strerror(errno));
162 // !!FIXME!! allow escaping of %'s
163 char *expand_template(const char *template, const char *mount_point, const char *root_name)
168 char *expanded_name_start;
171 for(i = 0; template[i]; i++)
172 if(template[i] == '%') {
173 switch(template[i + 1])
176 len += strlen(mount_point);
180 len += strlen(root_name);
187 expanded_name_start = expanded_name = my_malloc(len + 1);
189 for(i = 0; template[i]; i++)
190 if(template[i] == '%') {
192 switch(template[i + 1])
195 while(mount_point[j])
196 *expanded_name++ = mount_point[j++];
201 *expanded_name++ = root_name[j++];
206 *expanded_name++ = template[i];
208 *expanded_name = '\0';
210 return expanded_name_start;
213 int do_mount(const char *root_name)
220 fprintf(stderr, "Mounting: %s\n", root_name);
222 if( !make_mount_point(root_name) ) {
223 fprintf(stderr, "Failed to create mount point directory: %s/%s\n",
224 mount_point_directory, root_name);
228 mount_point = alloca(strlen(mount_point_directory) + 2 + strlen(root_name));
229 sprintf(mount_point, "%s/%s", mount_point_directory, root_name);
231 mount_command = expand_template(user_options.mount_command_template,
232 mount_point, root_name);
233 sysret = system(mount_command);
236 fprintf(stderr, "sysret: %.8x\n", sysret);
239 fprintf(stderr, "Failed to invoke mount command: \"%s\" (%s)\n",
240 mount_command, sysret != -1 ?
241 "Error executing mount" :
244 // remove the now unused directory
245 if( rmdir(mount_point) == -1 )
246 fprintf(stderr, "Failed to remove mount point dir: %s (%s)",
247 mount_point, strerror(errno));
252 add_mount(root_name);
257 int do_umount(const char *root_name)
260 char *unmount_command;
264 fprintf(stderr, "Unmounting: %s\n", root_name);
266 mount = find_mount(root_name);
268 fprintf(stderr, "Internal Error: tried to unmount non-existant mount point: %s\n", root_name);
272 mount_point = alloca(strlen(mount_point_directory) + 2 + strlen(root_name));
273 sprintf(mount_point, "%s/%s", mount_point_directory, root_name);
275 unmount_command = expand_template(user_options.unmount_command_template,
276 mount_point, root_name);
277 sysret = system(unmount_command);
278 free(unmount_command);
280 fprintf(stderr, "Failed to invoke unmount command: \"%s\" (%s)\n",
281 unmount_command, sysret != -1 ?
282 "Error executing mount" :
287 // tidy up after succesful unmount
288 remove_mount(root_name);
289 if( rmdir(mount_point) == -1 )
290 fprintf(stderr, "Failed to remove mount point dir: %s (%s)",
291 mount_point, strerror(errno));
296 void unmount_all(void)
298 fprintf(stderr, "Attemping to unmount all filesystems:\n");
301 fprintf(stderr, "\tUnmounting: %s\n", mount_list->root_name);
303 // if unmount fails, ditch the mount anyway
304 if( !do_umount(mount_list->root_name) )
305 remove_mount(mount_list->root_name);
308 fprintf(stderr, "done.\n");
315 if(rmdir(mount_point_directory) == -1)
316 fprintf(stderr, "Failed to remove temporary mount point directory: %s (%s)\n",
317 mount_point_directory, strerror(errno));
320 int max_path_out_len(const char *path_in)
322 return strlen(mount_point_directory) + strlen(path_in) + 2;
325 // returns true if path is the a child directory of a root node
326 // e.g. /a/b is a child, /a is not.
327 int extract_root_name(const char *path, char *root_name)
332 for(i = 1; path[i] && path[i] != '/'; i++)
333 root_name[i - 1] = path[i];
334 root_name[i - 1] = '\0';
336 return strlen(&path[i]);
339 typedef enum {PROC_PATH_FAILED, PROC_PATH_ROOT_DIR, PROC_PATH_PROXY_DIR} proc_result_t;
341 proc_result_t process_path(const char *path_in, char *path_out, char *root_name, int attempt_mount)
347 fprintf(stderr, "Path in: %s\n", path_in);
348 is_child = extract_root_name(path_in, root_name);
349 fprintf(stderr, "root_name is: %s\n", root_name);
351 // Mount filesystem if neccessary
352 // the combination of is_child and attempt_mount prevent inappropriate
353 // mounting of a filesystem for example if the user tries to mknod
354 // in the afuse root this should cause an error not a mount.
355 // !!FIXME!! this is broken on FUSE < 2.5 (?) because a getattr
356 // on the root node seems to occur with every single access.
357 if( //(is_child || attempt_mount ) &&
358 strlen(root_name) > 0 &&
359 !is_mount(root_name) &&
360 !do_mount(root_name))
361 return PROC_PATH_FAILED;
363 // construct path_out (mount_point_directory + '/' + path_in + '\0')
364 path_out_base = path_out;
365 for(i = 0; i < strlen(mount_point_directory); i++)
366 *path_out++ = mount_point_directory[i];
368 for(i = 0; i < strlen(path_in) - 1; i++)
369 *path_out++ = path_in[i + 1];
371 fprintf(stderr, "Path out: %s\n", path_out_base);
373 return strlen(root_name) ? PROC_PATH_PROXY_DIR : PROC_PATH_ROOT_DIR;
376 // Calling this function unmounts a dir if the user has specified to umount FS'
377 // not inuse and if there are no file or directory handles open. The
378 // update_operation flag combined with the mount_inuse_only_all user option
379 // allows unmounting only after destructive operations.
380 void consider_umount(const char *root_name, bool update_operation)
382 fprintf(stderr, "Considering unmount of: %s\n", root_name);
383 if((user_options.mount_inuse_only && update_operation) ||
384 user_options.mount_inuse_only_all) {
385 mount_list_t *mount = find_mount(root_name);
387 dir_list_empty(mount->dir_list) &&
388 fd_list_empty(mount->fd_list)) {
389 fprintf(stderr, "Decided to umount\n", root_name);
390 do_umount(root_name);
393 fprintf(stderr, "No user option\n", root_name);
397 static int afuse_getattr(const char *path, struct stat *stbuf)
400 char *root_name = alloca( strlen(path) );
401 char *real_path = alloca( max_path_out_len(path) );
403 fprintf(stderr, "> GetAttr\n");
405 switch( process_path(path, real_path, root_name, 0) )
407 case PROC_PATH_FAILED:
410 case PROC_PATH_ROOT_DIR:
411 extract_root_name(path, root_name);
412 fprintf(stderr, "Getattr on: (%s) - %s\n", path, root_name);
413 if( is_mount(root_name) || strlen(root_name) == 0) {
414 stbuf->st_mode = S_IFDIR | 0700;
416 stbuf->st_uid = getuid();
417 stbuf->st_gid = getgid();
419 stbuf->st_blksize = 0;
420 stbuf->st_blocks = 0;
429 case PROC_PATH_PROXY_DIR:
430 res = lstat(real_path, stbuf);
431 consider_umount(root_name, false);
440 static int afuse_readlink(const char *path, char *buf, size_t size)
443 char *root_name = alloca( strlen(path) );
444 char *real_path = alloca( max_path_out_len(path) );
446 switch( process_path(path, real_path, root_name, 1) )
448 case PROC_PATH_FAILED:
451 case PROC_PATH_ROOT_DIR:
454 case PROC_PATH_PROXY_DIR:
455 res = readlink(real_path, buf, size - 1);
456 consider_umount(root_name, false);
465 static int afuse_opendir(const char *path, struct fuse_file_info *fi)
468 char *root_name = alloca( strlen(path) );
470 char *real_path = alloca( max_path_out_len(path) );
472 switch( process_path(path, real_path, root_name, 1) )
474 case PROC_PATH_FAILED:
477 case PROC_PATH_ROOT_DIR:
480 case PROC_PATH_PROXY_DIR:
481 dp = opendir(real_path);
484 consider_umount(root_name, false);
488 fi->fh = (unsigned long) dp;
489 mount = find_mount(root_name);
491 dir_list_add(&mount->dir_list, dp);
496 static inline DIR *get_dirp(struct fuse_file_info *fi)
498 return (DIR *) (uintptr_t) fi->fh;
501 static int afuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
502 off_t offset, struct fuse_file_info *fi)
504 DIR *dp = get_dirp(fi);
506 char *root_name = alloca( strlen(path) );
507 char *real_path = alloca( max_path_out_len(path) );
510 switch( process_path(path, real_path, root_name, 1) )
512 case PROC_PATH_FAILED:
515 case PROC_PATH_ROOT_DIR:
516 filler(buf, ".", NULL, 0);
517 filler(buf, "..", NULL, 0);
518 for(mount = mount_list; mount; mount = mount->next)
519 filler(buf, mount->root_name, NULL, 0);
522 case PROC_PATH_PROXY_DIR:
524 while ((de = readdir(dp)) != NULL) {
526 memset(&st, 0, sizeof(st));
527 st.st_ino = de->d_ino;
528 st.st_mode = de->d_type << 12;
529 if (filler(buf, de->d_name, &st, telldir(dp)))
532 consider_umount(root_name, false);
538 static int afuse_releasedir(const char *path, struct fuse_file_info *fi)
540 DIR *dp = get_dirp(fi);
542 char *root_name = alloca( strlen(path) );
543 char *real_path = alloca( max_path_out_len(path) );
545 switch( process_path(path, real_path, root_name, 1) )
547 case PROC_PATH_FAILED:
550 case PROC_PATH_ROOT_DIR:
553 case PROC_PATH_PROXY_DIR:
554 extract_root_name(path, root_name);
555 mount = find_mount(root_name);
557 dir_list_remove(&mount->dir_list, dp);
561 consider_umount(root_name, false);
567 static int afuse_mknod(const char *path, mode_t mode, dev_t rdev)
570 char *root_name = alloca( strlen(path) );
571 char *real_path = alloca( max_path_out_len(path) );
573 fprintf(stderr, "> Mknod\n");
575 switch( process_path(path, real_path, root_name, 0) )
577 case PROC_PATH_FAILED:
580 case PROC_PATH_ROOT_DIR:
583 case PROC_PATH_PROXY_DIR:
585 res = mkfifo(real_path, mode);
587 res = mknod(real_path, mode, rdev);
588 consider_umount(root_name, true);
596 static int afuse_mkdir(const char *path, mode_t mode)
599 char *root_name = alloca( strlen(path) );
600 char *real_path = alloca( max_path_out_len(path) );
602 switch( process_path(path, real_path, root_name, 0) )
604 case PROC_PATH_FAILED:
607 case PROC_PATH_ROOT_DIR:
610 case PROC_PATH_PROXY_DIR:
611 res = mkdir(real_path, mode);
612 consider_umount(root_name, true);
620 static int afuse_unlink(const char *path)
623 char *root_name = alloca( strlen(path) );
624 char *real_path = alloca( max_path_out_len(path) );
626 switch( process_path(path, real_path, root_name, 0) )
628 case PROC_PATH_FAILED:
631 case PROC_PATH_ROOT_DIR:
634 case PROC_PATH_PROXY_DIR:
635 res = unlink(real_path);
636 consider_umount(root_name, true);
644 static int afuse_rmdir(const char *path)
647 char *root_name = alloca( strlen(path) );
648 char *real_path = alloca( max_path_out_len(path) );
650 switch( process_path(path, real_path, root_name, 0) )
652 case PROC_PATH_FAILED:
655 case PROC_PATH_ROOT_DIR:
658 case PROC_PATH_PROXY_DIR:
659 res = rmdir(real_path);
660 consider_umount(root_name, true);
668 static int afuse_symlink(const char *from, const char *to)
671 char *root_name_to = alloca( strlen(to) );
672 char *real_to_path = alloca( max_path_out_len(to) );
674 switch( process_path(to, real_to_path, root_name_to, 0) )
676 case PROC_PATH_FAILED:
679 case PROC_PATH_ROOT_DIR:
682 case PROC_PATH_PROXY_DIR:
683 res = symlink(from, real_to_path);
684 consider_umount(root_name_to, true);
692 static int afuse_rename(const char *from, const char *to)
695 char *root_name_from = alloca( strlen(from) );
696 char *root_name_to = alloca( strlen(to) );
697 char *real_from_path = alloca( max_path_out_len(from) );
698 char *real_to_path = alloca( max_path_out_len(to) );
700 switch( process_path(from, real_from_path, root_name_from, 0) )
702 case PROC_PATH_FAILED:
705 case PROC_PATH_ROOT_DIR:
708 case PROC_PATH_PROXY_DIR:
709 switch( process_path(to, real_to_path, root_name_to, 0) )
711 case PROC_PATH_FAILED:
712 consider_umount(root_name_from, false);
715 case PROC_PATH_ROOT_DIR:
716 consider_umount(root_name_from, false);
719 case PROC_PATH_PROXY_DIR:
720 res = rename(real_from_path, real_to_path);
721 consider_umount(root_name_from, true);
722 consider_umount(root_name_to, true);
731 static int afuse_link(const char *from, const char *to)
734 char *root_name_from = alloca( strlen(from) );
735 char *root_name_to = alloca( strlen(to) );
736 char *real_from_path = alloca( max_path_out_len(from) );
737 char *real_to_path = alloca( max_path_out_len(to) );
739 switch( process_path(from, real_from_path, root_name_from, 0) )
741 case PROC_PATH_FAILED:
744 case PROC_PATH_ROOT_DIR:
747 case PROC_PATH_PROXY_DIR:
748 switch( process_path(to, real_to_path, root_name_to, 0) )
750 case PROC_PATH_FAILED:
751 consider_umount(root_name_from, false);
754 case PROC_PATH_ROOT_DIR:
755 consider_umount(root_name_from, false);
758 case PROC_PATH_PROXY_DIR:
759 res = link(real_from_path, real_to_path);
760 consider_umount(root_name_from, true);
761 consider_umount(root_name_to, true);
770 static int afuse_chmod(const char *path, mode_t mode)
773 char *root_name = alloca( strlen(path) );
774 char *real_path = alloca( max_path_out_len(path) );
776 switch( process_path(path, real_path, root_name, 0) )
778 case PROC_PATH_FAILED:
781 case PROC_PATH_ROOT_DIR:
784 case PROC_PATH_PROXY_DIR:
785 res = chmod(real_path, mode);
786 consider_umount(root_name, true);
794 static int afuse_chown(const char *path, uid_t uid, gid_t gid)
797 char *root_name = alloca( strlen(path) );
798 char *real_path = alloca( max_path_out_len(path) );
800 switch( process_path(path, real_path, root_name, 0) )
802 case PROC_PATH_FAILED:
805 case PROC_PATH_ROOT_DIR:
808 case PROC_PATH_PROXY_DIR:
809 res = lchown(real_path, uid, gid);
810 consider_umount(root_name, true);
818 static int afuse_truncate(const char *path, off_t size)
821 char *root_name = alloca( strlen(path) );
822 char *real_path = alloca( max_path_out_len(path) );
824 switch( process_path(path, real_path, root_name, 0) )
826 case PROC_PATH_FAILED:
829 case PROC_PATH_ROOT_DIR:
832 case PROC_PATH_PROXY_DIR:
833 res = truncate(real_path, size);
834 consider_umount(root_name, true);
843 static int afuse_utime(const char *path, struct utimbuf *buf)
846 char *root_name = alloca( strlen(path) );
847 char *real_path = alloca( max_path_out_len(path) );
849 switch( process_path(path, real_path, root_name, 0) )
851 case PROC_PATH_FAILED:
854 case PROC_PATH_ROOT_DIR:
857 case PROC_PATH_PROXY_DIR:
858 res = utime(real_path, buf);
859 consider_umount(root_name, true);
868 static int afuse_open(const char *path, struct fuse_file_info *fi)
871 char *root_name = alloca( strlen(path) );
873 char *real_path = alloca( max_path_out_len(path) );
875 switch( process_path(path, real_path, root_name, 1) )
877 case PROC_PATH_FAILED:
880 case PROC_PATH_ROOT_DIR:
883 case PROC_PATH_PROXY_DIR:
884 fd = open(real_path, fi->flags);
886 consider_umount(root_name, true);
891 extract_root_name(path, root_name);
892 mount = find_mount(root_name);
894 fd_list_add(&mount->fd_list, fd);
899 static int afuse_read(const char *path, char *buf, size_t size, off_t offset,
900 struct fuse_file_info *fi)
905 res = pread(fi->fh, buf, size, offset);
912 static int afuse_write(const char *path, const char *buf, size_t size,
913 off_t offset, struct fuse_file_info *fi)
918 res = pwrite(fi->fh, buf, size, offset);
922 if(user_options.flush_writes)
929 static int afuse_release(const char *path, struct fuse_file_info *fi)
931 char *root_name = alloca( strlen(path) );
934 extract_root_name(path, root_name);
935 mount = find_mount(root_name);
937 fd_list_remove(&mount->fd_list, fi->fh);
940 consider_umount(root_name, true);
946 static int afuse_fsync(const char *path, int isdatasync,
947 struct fuse_file_info *fi)
952 #ifndef HAVE_FDATASYNC
956 res = fdatasync(fi->fh);
966 #if FUSE_VERSION >= 25
967 static int afuse_access(const char *path, int mask)
970 char *root_name = alloca( strlen(path) );
971 char *real_path = alloca( max_path_out_len(path) );
973 switch( process_path(path, real_path, root_name, 1) )
975 case PROC_PATH_FAILED:
978 case PROC_PATH_ROOT_DIR:
979 case PROC_PATH_PROXY_DIR:
980 res = access(real_path, mask);
981 consider_umount(root_name, false);
989 static int afuse_ftruncate(const char *path, off_t size,
990 struct fuse_file_info *fi)
996 res = ftruncate(fi->fh, size);
1003 static int afuse_create(const char *path, mode_t mode, struct fuse_file_info *fi)
1006 char *root_name = alloca( strlen(path) );
1007 char *real_path = alloca( max_path_out_len(path) );
1009 switch( process_path(path, real_path, root_name, 0) )
1011 case PROC_PATH_FAILED:
1014 case PROC_PATH_ROOT_DIR:
1017 case PROC_PATH_PROXY_DIR:
1018 fd = open(real_path, fi->flags, mode);
1019 consider_umount(root_name, true);
1028 static int afuse_fgetattr(const char *path, struct stat *stbuf,
1029 struct fuse_file_info *fi)
1035 res = fstat(fi->fh, stbuf);
1044 #if FUSE_VERSION >= 25
1045 static int afuse_statfs(const char *path, struct statvfs *stbuf)
1047 static int afuse_statfs(const char *path, struct statfs *stbuf)
1051 char *root_name = alloca( strlen(path) );
1052 char *real_path = alloca( max_path_out_len(path) );
1054 switch( process_path(path, real_path, root_name, 1) )
1056 case PROC_PATH_FAILED:
1059 case PROC_PATH_ROOT_DIR:
1060 #if FUSE_VERSION >= 25
1061 stbuf->f_namemax = 0x7fffffff;
1062 stbuf->f_frsize = 512;
1064 stbuf->f_namelen = 0x7fffffff;
1066 stbuf->f_bsize = 1024;
1067 stbuf->f_blocks = 0;
1069 stbuf->f_bavail = 0;
1074 case PROC_PATH_PROXY_DIR:
1075 res = statvfs(real_path, stbuf);
1076 consider_umount(root_name, false);
1084 void afuse_destroy(void *p)
1089 #ifdef HAVE_SETXATTR
1090 /* xattr operations are optional and can safely be left unimplemented */
1091 static int afuse_setxattr(const char *path, const char *name, const char *value,
1092 size_t size, int flags)
1095 char *root_name = alloca( strlen(path) );
1096 char *real_path = alloca( max_path_out_len(path) );
1098 switch( process_path(path, real_path, root_name, 0) )
1100 case PROC_PATH_FAILED:
1103 case PROC_PATH_ROOT_DIR:
1106 case PROC_PATH_PROXY_DIR:
1107 res = lsetxattr(real_path, name, value, size, flags);
1108 consider_umount(root_name, true);
1115 static int afuse_getxattr(const char *path, const char *name, char *value,
1119 char *root_name = alloca( strlen(path) );
1120 char *real_path = alloca( max_path_out_len(path) );
1122 switch( process_path(path, real_path, root_name, 1) )
1124 case PROC_PATH_FAILED:
1127 case PROC_PATH_ROOT_DIR:
1130 case PROC_PATH_PROXY_DIR:
1131 res = lgetxattr(real_path, name, value, size);
1132 consider_umount(root_name, false);
1139 static int afuse_listxattr(const char *path, char *list, size_t size)
1142 char *root_name = alloca( strlen(path) );
1143 char *real_path = alloca( max_path_out_len(path) );
1145 switch( process_path(path, real_path, root_name, 1) )
1147 case PROC_PATH_FAILED:
1150 case PROC_PATH_ROOT_DIR:
1153 case PROC_PATH_PROXY_DIR:
1154 res = llistxattr(real_path, list, size);
1155 consider_umount(root_name, false);
1162 static int afuse_removexattr(const char *path, const char *name)
1165 char *root_name = alloca( strlen(path) );
1166 char *real_path = alloca( max_path_out_len(path) );
1168 switch( process_path(path, real_path, root_name, 0) )
1170 case PROC_PATH_FAILED:
1173 case PROC_PATH_ROOT_DIR:
1176 case PROC_PATH_PROXY_DIR:
1177 res = lremovexattr(real_path, name);
1178 consider_umount(root_name, true);
1184 #endif /* HAVE_SETXATTR */
1186 static struct fuse_operations afuse_oper = {
1187 .getattr = afuse_getattr,
1188 .readlink = afuse_readlink,
1189 .opendir = afuse_opendir,
1190 .readdir = afuse_readdir,
1191 .releasedir = afuse_releasedir,
1192 .mknod = afuse_mknod,
1193 .mkdir = afuse_mkdir,
1194 .symlink = afuse_symlink,
1195 .unlink = afuse_unlink,
1196 .rmdir = afuse_rmdir,
1197 .rename = afuse_rename,
1199 .chmod = afuse_chmod,
1200 .chown = afuse_chown,
1201 .truncate = afuse_truncate,
1202 .utime = afuse_utime,
1205 .write = afuse_write,
1206 .release = afuse_release,
1207 .fsync = afuse_fsync,
1208 .statfs = afuse_statfs,
1209 #if FUSE_VERSION >= 25
1210 .access = afuse_access,
1211 .create = afuse_create,
1212 .ftruncate = afuse_ftruncate,
1213 .fgetattr = afuse_fgetattr,
1215 .destroy = afuse_destroy,
1216 #ifdef HAVE_SETXATTR
1217 .setxattr = afuse_setxattr,
1218 .getxattr = afuse_getxattr,
1219 .listxattr = afuse_listxattr,
1220 .removexattr = afuse_removexattr,
1232 #define AFUSE_OPT(t, p, v) { t, offsetof(struct user_options_t, p), v }
1234 static struct fuse_opt afuse_opts[] = {
1235 AFUSE_OPT("mount_template=%s", mount_command_template, 0),
1236 AFUSE_OPT("unmount_template=%s", unmount_command_template, 0),
1238 FUSE_OPT_KEY("inuseonly", KEY_INUSEONLY),
1239 FUSE_OPT_KEY("inuseonlyall", KEY_INUSEONLYALL),
1240 FUSE_OPT_KEY("flushwrites", KEY_FLUSHWRITES),
1242 FUSE_OPT_KEY("-h", KEY_HELP),
1243 FUSE_OPT_KEY("--help", KEY_HELP),
1248 static void usage(const char *progname)
1251 "Usage: %s mountpoint [options]\n"
1253 " -o opt,[opt...] mount options\n"
1254 " -h --help print help\n"
1255 " -V --version print FUSE version information\n"
1258 " -o mount_template=CMD template for CMD to execute to mount (*)\n"
1259 " -o unmount_template=CMD template for CMD to execute to unmount (*) (**)\n"
1260 " -o inuseonly unmount fs after files are closed or dir updates\n"
1261 " -o inuseonlyall as inuseonly, but all access end in unmount\n"
1262 " -o flushwrites flushes data to disk for all file writes\n"
1264 " (*) - When executed, %%r and %%m are expanded in templates to the root\n"
1265 " directory name for the new mount point, and the actual directory to\n"
1266 " mount onto respectively to mount onto. Both templates are REQUIRED.\n"
1268 " (**) - The unmount command must perform a lazy unmount operation. E.g. the\n"
1269 " -u -z options to fusermount, or -l for regular mount.\n"
1273 static int afuse_opt_proc(void *data, const char *arg, int key,
1274 struct fuse_args *outargs)
1281 usage(outargs->argv[0]);
1282 fuse_opt_add_arg(outargs, "-ho");
1283 fuse_main(outargs->argc, outargs->argv, &afuse_oper);
1287 user_options.mount_inuse_only = true;
1290 case KEY_INUSEONLYALL:
1291 user_options.mount_inuse_only_all = true;
1294 case KEY_FLUSHWRITES:
1295 user_options.flush_writes = true;
1303 int main(int argc, char *argv[])
1305 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1306 char *temp_dir_name = my_malloc(strlen(TMP_DIR_TEMPLATE));
1307 strcpy(temp_dir_name, TMP_DIR_TEMPLATE);
1309 if(fuse_opt_parse(&args, &user_options, afuse_opts, afuse_opt_proc) == -1)
1312 // !!FIXME!! force single-threading for now as datastructures are not locked
1313 fuse_opt_add_arg(&args, "-s");
1315 // Check for required parameters
1316 if(!user_options.mount_command_template || !user_options.unmount_command_template) {
1317 fprintf(stderr, "(Un)Mount command templates missing.\n\n");
1319 fuse_opt_add_arg(&args, "-ho");
1320 fuse_main(args.argc, args.argv, &afuse_oper);
1325 if( !(mount_point_directory = mkdtemp(temp_dir_name)) ) {
1326 fprintf(stderr, "Failed to create temporary mount point dir.\n");
1332 // Register function to tidy up on exit conditions
1333 if( atexit( shutdown ) ) {
1334 fprintf(stderr, "Failed to register exit handler.\n");
1338 // !!FIXME!! death by signal doesn't unmount fs
1339 return fuse_main(args.argc, args.argv, &afuse_oper);