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 // Note: this method strips out quotes and applies them itself as should be appropriate
164 char *expand_template(const char *template, const char *mount_point, const char *root_name)
169 char *expanded_name_start;
172 for(i = 0; template[i]; i++)
173 if(template[i] == '%') {
174 switch(template[i + 1])
177 len += strlen(mount_point) + 2;
181 len += strlen(root_name) + 2;
185 } else if(template[i] != '"')
188 expanded_name_start = expanded_name = my_malloc(len + 1);
190 for(i = 0; template[i]; i++)
191 if(template[i] == '%') {
193 switch(template[i + 1])
196 *expanded_name++ = '"';
197 while(mount_point[j])
198 *expanded_name++ = mount_point[j++];
199 *expanded_name++ = '"';
203 *expanded_name++ = '"';
205 *expanded_name++ = root_name[j++];
206 *expanded_name++ = '"';
210 } else if(template[i] != '"')
211 *expanded_name++ = template[i];
213 *expanded_name = '\0';
215 return expanded_name_start;
218 int do_mount(const char *root_name)
225 fprintf(stderr, "Mounting: %s\n", root_name);
227 if( !make_mount_point(root_name) ) {
228 fprintf(stderr, "Failed to create mount point directory: %s/%s\n",
229 mount_point_directory, root_name);
233 mount_point = alloca(strlen(mount_point_directory) + 2 + strlen(root_name));
234 sprintf(mount_point, "%s/%s", mount_point_directory, root_name);
236 mount_command = expand_template(user_options.mount_command_template,
237 mount_point, root_name);
238 sysret = system(mount_command);
240 fprintf(stderr, "sysret: %.8x\n", sysret);
243 fprintf(stderr, "Failed to invoke mount command: '%s' (%s)\n",
244 mount_command, sysret != -1 ?
245 "Error executing mount" :
248 // remove the now unused directory
249 if( rmdir(mount_point) == -1 )
250 fprintf(stderr, "Failed to remove mount point dir: %s (%s)",
251 mount_point, strerror(errno));
257 add_mount(root_name);
263 int do_umount(const char *root_name)
266 char *unmount_command;
270 fprintf(stderr, "Unmounting: %s\n", root_name);
272 mount = find_mount(root_name);
274 fprintf(stderr, "Internal Error: tried to unmount non-existant mount point: %s\n", root_name);
278 mount_point = alloca(strlen(mount_point_directory) + 2 + strlen(root_name));
279 sprintf(mount_point, "%s/%s", mount_point_directory, root_name);
281 unmount_command = expand_template(user_options.unmount_command_template,
282 mount_point, root_name);
283 sysret = system(unmount_command);
285 fprintf(stderr, "Failed to invoke unmount command: '%s' (%s)\n",
286 unmount_command, sysret != -1 ?
287 "Error executing mount" :
289 free(unmount_command);
293 // tidy up after succesful unmount
294 remove_mount(root_name);
295 if( rmdir(mount_point) == -1 )
296 fprintf(stderr, "Failed to remove mount point dir: %s (%s)",
297 mount_point, strerror(errno));
299 free(unmount_command);
303 void unmount_all(void)
305 fprintf(stderr, "Attemping to unmount all filesystems:\n");
308 fprintf(stderr, "\tUnmounting: %s\n", mount_list->root_name);
310 // if unmount fails, ditch the mount anyway
311 if( !do_umount(mount_list->root_name) )
312 remove_mount(mount_list->root_name);
315 fprintf(stderr, "done.\n");
322 if(rmdir(mount_point_directory) == -1)
323 fprintf(stderr, "Failed to remove temporary mount point directory: %s (%s)\n",
324 mount_point_directory, strerror(errno));
327 int max_path_out_len(const char *path_in)
329 return strlen(mount_point_directory) + strlen(path_in) + 2;
332 // returns true if path is the a child directory of a root node
333 // e.g. /a/b is a child, /a is not.
334 int extract_root_name(const char *path, char *root_name)
339 for(i = 1; path[i] && path[i] != '/'; i++)
340 root_name[i - 1] = path[i];
341 root_name[i - 1] = '\0';
343 return strlen(&path[i]);
346 typedef enum {PROC_PATH_FAILED, PROC_PATH_ROOT_DIR, PROC_PATH_PROXY_DIR} proc_result_t;
348 proc_result_t process_path(const char *path_in, char *path_out, char *root_name, int attempt_mount)
354 fprintf(stderr, "Path in: %s\n", path_in);
355 is_child = extract_root_name(path_in, root_name);
356 fprintf(stderr, "root_name is: %s\n", root_name);
358 // Mount filesystem if neccessary
359 // the combination of is_child and attempt_mount prevent inappropriate
360 // mounting of a filesystem for example if the user tries to mknod
361 // in the afuse root this should cause an error not a mount.
362 // !!FIXME!! this is broken on FUSE < 2.5 (?) because a getattr
363 // on the root node seems to occur with every single access.
364 if( //(is_child || attempt_mount ) &&
365 strlen(root_name) > 0 &&
366 !is_mount(root_name) &&
367 !do_mount(root_name))
368 return PROC_PATH_FAILED;
370 // construct path_out (mount_point_directory + '/' + path_in + '\0')
371 path_out_base = path_out;
372 for(i = 0; i < strlen(mount_point_directory); i++)
373 *path_out++ = mount_point_directory[i];
375 for(i = 0; i < strlen(path_in) - 1; i++)
376 *path_out++ = path_in[i + 1];
378 fprintf(stderr, "Path out: %s\n", path_out_base);
380 return strlen(root_name) ? PROC_PATH_PROXY_DIR : PROC_PATH_ROOT_DIR;
383 // Calling this function unmounts a dir if the user has specified to umount FS'
384 // not inuse and if there are no file or directory handles open. The
385 // update_operation flag combined with the mount_inuse_only_all user option
386 // allows unmounting only after destructive operations.
387 void consider_umount(const char *root_name, bool update_operation)
389 fprintf(stderr, "Considering unmount of: %s\n", root_name);
390 if((user_options.mount_inuse_only && update_operation) ||
391 user_options.mount_inuse_only_all) {
392 mount_list_t *mount = find_mount(root_name);
394 dir_list_empty(mount->dir_list) &&
395 fd_list_empty(mount->fd_list)) {
396 fprintf(stderr, "Decided to umount\n", root_name);
397 do_umount(root_name);
400 fprintf(stderr, "No user option\n", root_name);
404 static int afuse_getattr(const char *path, struct stat *stbuf)
407 char *root_name = alloca( strlen(path) );
408 char *real_path = alloca( max_path_out_len(path) );
410 fprintf(stderr, "> GetAttr\n");
412 switch( process_path(path, real_path, root_name, 0) )
414 case PROC_PATH_FAILED:
417 case PROC_PATH_ROOT_DIR:
418 extract_root_name(path, root_name);
419 fprintf(stderr, "Getattr on: (%s) - %s\n", path, root_name);
420 if( is_mount(root_name) || strlen(root_name) == 0) {
421 stbuf->st_mode = S_IFDIR | 0700;
423 stbuf->st_uid = getuid();
424 stbuf->st_gid = getgid();
426 stbuf->st_blksize = 0;
427 stbuf->st_blocks = 0;
436 case PROC_PATH_PROXY_DIR:
437 res = lstat(real_path, stbuf);
438 consider_umount(root_name, false);
447 static int afuse_readlink(const char *path, char *buf, size_t size)
450 char *root_name = alloca( strlen(path) );
451 char *real_path = alloca( max_path_out_len(path) );
453 switch( process_path(path, real_path, root_name, 1) )
455 case PROC_PATH_FAILED:
458 case PROC_PATH_ROOT_DIR:
461 case PROC_PATH_PROXY_DIR:
462 res = readlink(real_path, buf, size - 1);
463 consider_umount(root_name, false);
472 static int afuse_opendir(const char *path, struct fuse_file_info *fi)
475 char *root_name = alloca( strlen(path) );
477 char *real_path = alloca( max_path_out_len(path) );
479 switch( process_path(path, real_path, root_name, 1) )
481 case PROC_PATH_FAILED:
484 case PROC_PATH_ROOT_DIR:
487 case PROC_PATH_PROXY_DIR:
488 dp = opendir(real_path);
491 consider_umount(root_name, false);
495 fi->fh = (unsigned long) dp;
496 mount = find_mount(root_name);
498 dir_list_add(&mount->dir_list, dp);
503 static inline DIR *get_dirp(struct fuse_file_info *fi)
505 return (DIR *) (uintptr_t) fi->fh;
508 static int afuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
509 off_t offset, struct fuse_file_info *fi)
511 DIR *dp = get_dirp(fi);
513 char *root_name = alloca( strlen(path) );
514 char *real_path = alloca( max_path_out_len(path) );
517 switch( process_path(path, real_path, root_name, 1) )
519 case PROC_PATH_FAILED:
522 case PROC_PATH_ROOT_DIR:
523 filler(buf, ".", NULL, 0);
524 filler(buf, "..", NULL, 0);
525 for(mount = mount_list; mount; mount = mount->next)
526 filler(buf, mount->root_name, NULL, 0);
529 case PROC_PATH_PROXY_DIR:
531 while ((de = readdir(dp)) != NULL) {
533 memset(&st, 0, sizeof(st));
534 st.st_ino = de->d_ino;
535 st.st_mode = de->d_type << 12;
536 if (filler(buf, de->d_name, &st, telldir(dp)))
539 consider_umount(root_name, false);
545 static int afuse_releasedir(const char *path, struct fuse_file_info *fi)
547 DIR *dp = get_dirp(fi);
549 char *root_name = alloca( strlen(path) );
550 char *real_path = alloca( max_path_out_len(path) );
552 switch( process_path(path, real_path, root_name, 1) )
554 case PROC_PATH_FAILED:
557 case PROC_PATH_ROOT_DIR:
560 case PROC_PATH_PROXY_DIR:
561 extract_root_name(path, root_name);
562 mount = find_mount(root_name);
564 dir_list_remove(&mount->dir_list, dp);
568 consider_umount(root_name, false);
574 static int afuse_mknod(const char *path, mode_t mode, dev_t rdev)
577 char *root_name = alloca( strlen(path) );
578 char *real_path = alloca( max_path_out_len(path) );
580 fprintf(stderr, "> Mknod\n");
582 switch( process_path(path, real_path, root_name, 0) )
584 case PROC_PATH_FAILED:
587 case PROC_PATH_ROOT_DIR:
590 case PROC_PATH_PROXY_DIR:
592 res = mkfifo(real_path, mode);
594 res = mknod(real_path, mode, rdev);
595 consider_umount(root_name, true);
603 static int afuse_mkdir(const char *path, mode_t mode)
606 char *root_name = alloca( strlen(path) );
607 char *real_path = alloca( max_path_out_len(path) );
609 switch( process_path(path, real_path, root_name, 0) )
611 case PROC_PATH_FAILED:
614 case PROC_PATH_ROOT_DIR:
617 case PROC_PATH_PROXY_DIR:
618 res = mkdir(real_path, mode);
619 consider_umount(root_name, true);
627 static int afuse_unlink(const char *path)
630 char *root_name = alloca( strlen(path) );
631 char *real_path = alloca( max_path_out_len(path) );
633 switch( process_path(path, real_path, root_name, 0) )
635 case PROC_PATH_FAILED:
638 case PROC_PATH_ROOT_DIR:
641 case PROC_PATH_PROXY_DIR:
642 res = unlink(real_path);
643 consider_umount(root_name, true);
651 static int afuse_rmdir(const char *path)
654 char *root_name = alloca( strlen(path) );
655 char *real_path = alloca( max_path_out_len(path) );
657 switch( process_path(path, real_path, root_name, 0) )
659 case PROC_PATH_FAILED:
662 case PROC_PATH_ROOT_DIR:
665 case PROC_PATH_PROXY_DIR:
666 res = rmdir(real_path);
667 consider_umount(root_name, true);
675 static int afuse_symlink(const char *from, const char *to)
678 char *root_name_to = alloca( strlen(to) );
679 char *real_to_path = alloca( max_path_out_len(to) );
681 switch( process_path(to, real_to_path, root_name_to, 0) )
683 case PROC_PATH_FAILED:
686 case PROC_PATH_ROOT_DIR:
689 case PROC_PATH_PROXY_DIR:
690 res = symlink(from, real_to_path);
691 consider_umount(root_name_to, true);
699 static int afuse_rename(const char *from, const char *to)
702 char *root_name_from = alloca( strlen(from) );
703 char *root_name_to = alloca( strlen(to) );
704 char *real_from_path = alloca( max_path_out_len(from) );
705 char *real_to_path = alloca( max_path_out_len(to) );
707 switch( process_path(from, real_from_path, root_name_from, 0) )
709 case PROC_PATH_FAILED:
712 case PROC_PATH_ROOT_DIR:
715 case PROC_PATH_PROXY_DIR:
716 switch( process_path(to, real_to_path, root_name_to, 0) )
718 case PROC_PATH_FAILED:
719 consider_umount(root_name_from, false);
722 case PROC_PATH_ROOT_DIR:
723 consider_umount(root_name_from, false);
726 case PROC_PATH_PROXY_DIR:
727 res = rename(real_from_path, real_to_path);
728 consider_umount(root_name_from, true);
729 consider_umount(root_name_to, true);
738 static int afuse_link(const char *from, const char *to)
741 char *root_name_from = alloca( strlen(from) );
742 char *root_name_to = alloca( strlen(to) );
743 char *real_from_path = alloca( max_path_out_len(from) );
744 char *real_to_path = alloca( max_path_out_len(to) );
746 switch( process_path(from, real_from_path, root_name_from, 0) )
748 case PROC_PATH_FAILED:
751 case PROC_PATH_ROOT_DIR:
754 case PROC_PATH_PROXY_DIR:
755 switch( process_path(to, real_to_path, root_name_to, 0) )
757 case PROC_PATH_FAILED:
758 consider_umount(root_name_from, false);
761 case PROC_PATH_ROOT_DIR:
762 consider_umount(root_name_from, false);
765 case PROC_PATH_PROXY_DIR:
766 res = link(real_from_path, real_to_path);
767 consider_umount(root_name_from, true);
768 consider_umount(root_name_to, true);
777 static int afuse_chmod(const char *path, mode_t mode)
780 char *root_name = alloca( strlen(path) );
781 char *real_path = alloca( max_path_out_len(path) );
783 switch( process_path(path, real_path, root_name, 0) )
785 case PROC_PATH_FAILED:
788 case PROC_PATH_ROOT_DIR:
791 case PROC_PATH_PROXY_DIR:
792 res = chmod(real_path, mode);
793 consider_umount(root_name, true);
801 static int afuse_chown(const char *path, uid_t uid, gid_t gid)
804 char *root_name = alloca( strlen(path) );
805 char *real_path = alloca( max_path_out_len(path) );
807 switch( process_path(path, real_path, root_name, 0) )
809 case PROC_PATH_FAILED:
812 case PROC_PATH_ROOT_DIR:
815 case PROC_PATH_PROXY_DIR:
816 res = lchown(real_path, uid, gid);
817 consider_umount(root_name, true);
825 static int afuse_truncate(const char *path, off_t size)
828 char *root_name = alloca( strlen(path) );
829 char *real_path = alloca( max_path_out_len(path) );
831 switch( process_path(path, real_path, root_name, 0) )
833 case PROC_PATH_FAILED:
836 case PROC_PATH_ROOT_DIR:
839 case PROC_PATH_PROXY_DIR:
840 res = truncate(real_path, size);
841 consider_umount(root_name, true);
850 static int afuse_utime(const char *path, struct utimbuf *buf)
853 char *root_name = alloca( strlen(path) );
854 char *real_path = alloca( max_path_out_len(path) );
856 switch( process_path(path, real_path, root_name, 0) )
858 case PROC_PATH_FAILED:
861 case PROC_PATH_ROOT_DIR:
864 case PROC_PATH_PROXY_DIR:
865 res = utime(real_path, buf);
866 consider_umount(root_name, true);
875 static int afuse_open(const char *path, struct fuse_file_info *fi)
878 char *root_name = alloca( strlen(path) );
880 char *real_path = alloca( max_path_out_len(path) );
882 switch( process_path(path, real_path, root_name, 1) )
884 case PROC_PATH_FAILED:
887 case PROC_PATH_ROOT_DIR:
890 case PROC_PATH_PROXY_DIR:
891 fd = open(real_path, fi->flags);
893 consider_umount(root_name, true);
898 extract_root_name(path, root_name);
899 mount = find_mount(root_name);
901 fd_list_add(&mount->fd_list, fd);
906 static int afuse_read(const char *path, char *buf, size_t size, off_t offset,
907 struct fuse_file_info *fi)
912 res = pread(fi->fh, buf, size, offset);
919 static int afuse_write(const char *path, const char *buf, size_t size,
920 off_t offset, struct fuse_file_info *fi)
925 res = pwrite(fi->fh, buf, size, offset);
929 if(user_options.flush_writes)
936 static int afuse_release(const char *path, struct fuse_file_info *fi)
938 char *root_name = alloca( strlen(path) );
941 extract_root_name(path, root_name);
942 mount = find_mount(root_name);
944 fd_list_remove(&mount->fd_list, fi->fh);
947 consider_umount(root_name, true);
953 static int afuse_fsync(const char *path, int isdatasync,
954 struct fuse_file_info *fi)
959 #ifndef HAVE_FDATASYNC
963 res = fdatasync(fi->fh);
973 #if FUSE_VERSION >= 25
974 static int afuse_access(const char *path, int mask)
977 char *root_name = alloca( strlen(path) );
978 char *real_path = alloca( max_path_out_len(path) );
980 switch( process_path(path, real_path, root_name, 1) )
982 case PROC_PATH_FAILED:
985 case PROC_PATH_ROOT_DIR:
986 case PROC_PATH_PROXY_DIR:
987 res = access(real_path, mask);
988 consider_umount(root_name, false);
996 static int afuse_ftruncate(const char *path, off_t size,
997 struct fuse_file_info *fi)
1003 res = ftruncate(fi->fh, size);
1010 static int afuse_create(const char *path, mode_t mode, struct fuse_file_info *fi)
1013 char *root_name = alloca( strlen(path) );
1014 char *real_path = alloca( max_path_out_len(path) );
1016 switch( process_path(path, real_path, root_name, 0) )
1018 case PROC_PATH_FAILED:
1021 case PROC_PATH_ROOT_DIR:
1024 case PROC_PATH_PROXY_DIR:
1025 fd = open(real_path, fi->flags, mode);
1026 consider_umount(root_name, true);
1035 static int afuse_fgetattr(const char *path, struct stat *stbuf,
1036 struct fuse_file_info *fi)
1042 res = fstat(fi->fh, stbuf);
1051 #if FUSE_VERSION >= 25
1052 static int afuse_statfs(const char *path, struct statvfs *stbuf)
1054 static int afuse_statfs(const char *path, struct statfs *stbuf)
1058 char *root_name = alloca( strlen(path) );
1059 char *real_path = alloca( max_path_out_len(path) );
1061 switch( process_path(path, real_path, root_name, 1) )
1063 case PROC_PATH_FAILED:
1066 case PROC_PATH_ROOT_DIR:
1067 #if FUSE_VERSION >= 25
1068 stbuf->f_namemax = 0x7fffffff;
1069 stbuf->f_frsize = 512;
1071 stbuf->f_namelen = 0x7fffffff;
1073 stbuf->f_bsize = 1024;
1074 stbuf->f_blocks = 0;
1076 stbuf->f_bavail = 0;
1081 case PROC_PATH_PROXY_DIR:
1082 res = statvfs(real_path, stbuf);
1083 consider_umount(root_name, false);
1091 void afuse_destroy(void *p)
1096 #ifdef HAVE_SETXATTR
1097 /* xattr operations are optional and can safely be left unimplemented */
1098 static int afuse_setxattr(const char *path, const char *name, const char *value,
1099 size_t size, int flags)
1102 char *root_name = alloca( strlen(path) );
1103 char *real_path = alloca( max_path_out_len(path) );
1105 switch( process_path(path, real_path, root_name, 0) )
1107 case PROC_PATH_FAILED:
1110 case PROC_PATH_ROOT_DIR:
1113 case PROC_PATH_PROXY_DIR:
1114 res = lsetxattr(real_path, name, value, size, flags);
1115 consider_umount(root_name, true);
1122 static int afuse_getxattr(const char *path, const char *name, char *value,
1126 char *root_name = alloca( strlen(path) );
1127 char *real_path = alloca( max_path_out_len(path) );
1129 switch( process_path(path, real_path, root_name, 1) )
1131 case PROC_PATH_FAILED:
1134 case PROC_PATH_ROOT_DIR:
1137 case PROC_PATH_PROXY_DIR:
1138 res = lgetxattr(real_path, name, value, size);
1139 consider_umount(root_name, false);
1146 static int afuse_listxattr(const char *path, char *list, size_t size)
1149 char *root_name = alloca( strlen(path) );
1150 char *real_path = alloca( max_path_out_len(path) );
1152 switch( process_path(path, real_path, root_name, 1) )
1154 case PROC_PATH_FAILED:
1157 case PROC_PATH_ROOT_DIR:
1160 case PROC_PATH_PROXY_DIR:
1161 res = llistxattr(real_path, list, size);
1162 consider_umount(root_name, false);
1169 static int afuse_removexattr(const char *path, const char *name)
1172 char *root_name = alloca( strlen(path) );
1173 char *real_path = alloca( max_path_out_len(path) );
1175 switch( process_path(path, real_path, root_name, 0) )
1177 case PROC_PATH_FAILED:
1180 case PROC_PATH_ROOT_DIR:
1183 case PROC_PATH_PROXY_DIR:
1184 res = lremovexattr(real_path, name);
1185 consider_umount(root_name, true);
1191 #endif /* HAVE_SETXATTR */
1193 static struct fuse_operations afuse_oper = {
1194 .getattr = afuse_getattr,
1195 .readlink = afuse_readlink,
1196 .opendir = afuse_opendir,
1197 .readdir = afuse_readdir,
1198 .releasedir = afuse_releasedir,
1199 .mknod = afuse_mknod,
1200 .mkdir = afuse_mkdir,
1201 .symlink = afuse_symlink,
1202 .unlink = afuse_unlink,
1203 .rmdir = afuse_rmdir,
1204 .rename = afuse_rename,
1206 .chmod = afuse_chmod,
1207 .chown = afuse_chown,
1208 .truncate = afuse_truncate,
1209 .utime = afuse_utime,
1212 .write = afuse_write,
1213 .release = afuse_release,
1214 .fsync = afuse_fsync,
1215 .statfs = afuse_statfs,
1216 #if FUSE_VERSION >= 25
1217 .access = afuse_access,
1218 .create = afuse_create,
1219 .ftruncate = afuse_ftruncate,
1220 .fgetattr = afuse_fgetattr,
1222 .destroy = afuse_destroy,
1223 #ifdef HAVE_SETXATTR
1224 .setxattr = afuse_setxattr,
1225 .getxattr = afuse_getxattr,
1226 .listxattr = afuse_listxattr,
1227 .removexattr = afuse_removexattr,
1239 #define AFUSE_OPT(t, p, v) { t, offsetof(struct user_options_t, p), v }
1241 static struct fuse_opt afuse_opts[] = {
1242 AFUSE_OPT("mount_template=%s", mount_command_template, 0),
1243 AFUSE_OPT("unmount_template=%s", unmount_command_template, 0),
1245 FUSE_OPT_KEY("inuseonly", KEY_INUSEONLY),
1246 FUSE_OPT_KEY("inuseonlyall", KEY_INUSEONLYALL),
1247 FUSE_OPT_KEY("flushwrites", KEY_FLUSHWRITES),
1249 FUSE_OPT_KEY("-h", KEY_HELP),
1250 FUSE_OPT_KEY("--help", KEY_HELP),
1255 static void usage(const char *progname)
1258 "Usage: %s mountpoint [options]\n"
1260 " -o opt,[opt...] mount options\n"
1261 " -h --help print help\n"
1262 " -V --version print FUSE version information\n"
1265 " -o mount_template=CMD template for CMD to execute to mount (*)\n"
1266 " -o unmount_template=CMD template for CMD to execute to unmount (*) (**)\n"
1267 " -o inuseonly unmount fs after files are closed or dir updates\n"
1268 " -o inuseonlyall as inuseonly, but all access end in unmount\n"
1269 " -o flushwrites flushes data to disk for all file writes\n"
1271 " (*) - When executed, %%r and %%m are expanded in templates to the root\n"
1272 " directory name for the new mount point, and the actual directory to\n"
1273 " mount onto respectively to mount onto. Both templates are REQUIRED.\n"
1275 " (**) - The unmount command must perform a lazy unmount operation. E.g. the\n"
1276 " -u -z options to fusermount, or -l for regular mount.\n"
1280 static int afuse_opt_proc(void *data, const char *arg, int key,
1281 struct fuse_args *outargs)
1288 usage(outargs->argv[0]);
1289 fuse_opt_add_arg(outargs, "-ho");
1290 fuse_main(outargs->argc, outargs->argv, &afuse_oper);
1294 user_options.mount_inuse_only = true;
1297 case KEY_INUSEONLYALL:
1298 user_options.mount_inuse_only_all = true;
1301 case KEY_FLUSHWRITES:
1302 user_options.flush_writes = true;
1310 int main(int argc, char *argv[])
1312 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1313 char *temp_dir_name = my_malloc(strlen(TMP_DIR_TEMPLATE));
1314 strcpy(temp_dir_name, TMP_DIR_TEMPLATE);
1316 if(fuse_opt_parse(&args, &user_options, afuse_opts, afuse_opt_proc) == -1)
1319 // !!FIXME!! force single-threading for now as datastructures are not locked
1320 fuse_opt_add_arg(&args, "-s");
1322 // Check for required parameters
1323 if(!user_options.mount_command_template || !user_options.unmount_command_template) {
1324 fprintf(stderr, "(Un)Mount command templates missing.\n\n");
1326 fuse_opt_add_arg(&args, "-ho");
1327 fuse_main(args.argc, args.argv, &afuse_oper);
1332 if( !(mount_point_directory = mkdtemp(temp_dir_name)) ) {
1333 fprintf(stderr, "Failed to create temporary mount point dir.\n");
1339 // Register function to tidy up on exit conditions
1340 if( atexit( shutdown ) ) {
1341 fprintf(stderr, "Failed to register exit handler.\n");
1345 // !!FIXME!! death by signal doesn't unmount fs
1346 return fuse_main(args.argc, args.argv, &afuse_oper);