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>
41 // When closing an fd/dir, the close may fail due to a signal
42 // this value defines how many times we retry in this case.
43 // It's useful to try and close as many fd's as possible
44 // for the proxied fs to increase the chance an umount will
46 #define CLOSE_MAX_RETRIES 5
48 #define TMP_DIR_TEMPLATE "/tmp/afuse-XXXXXX"
49 char *mount_point_directory;
51 struct user_options_t {
52 char *mount_command_template;
53 char *unmount_command_template;
54 } user_options = {NULL, NULL};
56 typedef struct _fd_list_t {
57 struct _fd_list_t *next;
58 struct _fd_list_t *prev;
63 typedef struct _dir_list_t {
64 struct _dir_list_t *next;
65 struct _dir_list_t *prev;
70 typedef struct _mount_list_t {
71 struct _mount_list_t *next;
72 struct _mount_list_t *prev;
79 mount_list_t *mount_list = NULL;
81 void *my_malloc(size_t size)
88 fprintf(stderr, "Failed to allocate: %d bytes of memory.\n");
95 char *my_strdup(char *str)
99 new_str = my_malloc(strlen(str) + 1);
100 strcpy(new_str, str);
105 mount_list_t *find_mount(char *root_name)
107 mount_list_t *current_mount = mount_list;
109 while(current_mount) {
110 if( strcmp(root_name, current_mount->root_name) == 0)
111 return current_mount;
113 current_mount = current_mount->next;
119 int is_mount(char *root_name)
121 return find_mount(root_name) ? 1 : 0;
124 void add_fd(fd_list_t **fd_list, int fd)
128 new_fd = my_malloc( sizeof(fd_list_t) );
130 new_fd->next = *fd_list;
136 void remove_fd(fd_list_t **fd_list, int fd)
138 fd_list_t *current_fd = *fd_list;
141 if(current_fd->fd == fd) {
143 current_fd->prev->next = current_fd->next;
145 *fd_list = current_fd->next;
147 current_fd->next->prev = current_fd->prev;
153 current_fd = current_fd->next;
157 void add_dir(dir_list_t **dir_list, DIR *dir)
161 new_dir = my_malloc( sizeof(dir_list_t) );
163 new_dir->next = *dir_list;
164 new_dir->prev = NULL;
169 void remove_dir(dir_list_t **dir_list, DIR *dir)
171 dir_list_t *current_dir = *dir_list;
174 if(current_dir->dir == dir) {
175 if(current_dir->prev)
176 current_dir->prev->next = current_dir->next;
178 *dir_list = current_dir->next;
179 if(current_dir->next)
180 current_dir->next->prev = current_dir->prev;
186 current_dir = current_dir->next;
190 void close_all_fds(fd_list_t **fd_list)
195 for(retries = 0; retries < CLOSE_MAX_RETRIES &&
196 close((*fd_list)->fd) == -1 &&
199 remove_fd(fd_list, (*fd_list)->fd);
203 void close_all_dirs(dir_list_t **dir_list)
208 for(retries = 0; retries < CLOSE_MAX_RETRIES &&
209 closedir((*dir_list)->dir) == -1 &&
212 remove_dir(dir_list, (*dir_list)->dir);
216 void add_mount(char *root_name)
218 mount_list_t *new_mount;
220 new_mount = (mount_list_t *)my_malloc( sizeof(mount_list_t) );
221 new_mount->root_name = my_strdup(root_name);
223 new_mount->next = mount_list;
224 new_mount->prev = NULL;
225 new_mount->fd_list = NULL;
226 new_mount->dir_list = NULL;
228 mount_list->prev = new_mount;
230 mount_list = new_mount;
234 void remove_mount(char *root_name)
236 mount_list_t *current_mount = mount_list;
238 while(current_mount) {
239 if( strcmp(root_name, current_mount->root_name) == 0) {
240 free(current_mount->root_name);
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;
252 current_mount = current_mount->next;
256 int make_mount_point(char *root_name)
261 // First create the mount_point_directory
262 dir_tmp = my_strdup(mount_point_directory);
263 for(i = 0; dir_tmp[i]; i++)
264 if(dir_tmp[i] == '/' && i != 0) {
266 if(mkdir(dir_tmp, 0700) == -1 && errno != EEXIST) {
267 fprintf(stderr, "Cannot create directory: %s (%s)\n",
268 dir_tmp, strerror(errno));
275 // Create the mount point
276 dir_tmp = my_malloc(strlen(mount_point_directory) + 2 + strlen(root_name));
277 strcpy(dir_tmp, mount_point_directory);
278 strcat(dir_tmp, "/");
279 strcat(dir_tmp, root_name);
281 if(mkdir(dir_tmp, 0700) == -1 && errno != EEXIST) {
282 fprintf(stderr, "Cannot create directory: %s (%s)\n",
283 dir_tmp, strerror(errno));
292 // !!FIXME!! allow escaping of %'s
293 char *expand_template(char *template, char *mount_point, char *root_name)
298 char *expanded_name_start;
301 for(i = 0; template[i]; i++)
302 if(template[i] == '%') {
303 switch(template[i + 1])
306 len += strlen(mount_point);
310 len += strlen(root_name);
317 expanded_name_start = expanded_name = my_malloc(len + 1);
319 for(i = 0; template[i]; i++)
320 if(template[i] == '%') {
322 switch(template[i + 1])
325 while(mount_point[j])
326 *expanded_name++ = mount_point[j++];
331 *expanded_name++ = root_name[j++];
336 *expanded_name++ = template[i];
338 *expanded_name = '\0';
340 return expanded_name_start;
343 int do_mount(char *root_name)
350 fprintf(stderr, "Mounting: %s\n", root_name);
352 if( !make_mount_point(root_name) ) {
353 fprintf(stderr, "Failed to create mount point directory: %s/%s\n",
354 mount_point_directory, root_name);
358 mount_point = alloca(strlen(mount_point_directory) + 2 + strlen(root_name));
359 sprintf(mount_point, "%s/%s", mount_point_directory, root_name);
361 mount_command = expand_template(user_options.mount_command_template,
362 mount_point, root_name);
363 sysret = system(mount_command);
366 fprintf(stderr, "sysret: %.8x\n", sysret);
369 fprintf(stderr, "Failed to invoke mount command: \"%s\" (%s)\n",
370 mount_command, sysret != -1 ?
371 "Error executing mount" :
374 // remove the now unused directory
375 if( rmdir(mount_point) == -1 )
376 fprintf(stderr, "Failed to remove mount point dir: %s (%s)",
377 mount_point, strerror(errno));
382 add_mount(root_name);
387 int do_umount(char *root_name)
390 char *unmount_command;
394 fprintf(stderr, "Unmounting: %s\n", root_name);
396 mount = find_mount(root_name);
398 fprintf(stderr, "Internal Error: tried to unmount non-existant mount point: %s\n", root_name);
402 mount_point = alloca(strlen(mount_point_directory) + 2 + strlen(root_name));
403 sprintf(mount_point, "%s/%s", mount_point_directory, root_name);
405 unmount_command = expand_template(user_options.unmount_command_template,
406 mount_point, root_name);
407 sysret = system(unmount_command);
408 free(unmount_command);
410 fprintf(stderr, "Failed to invoke unmount command: \"%s\" (%s)\n",
411 unmount_command, sysret != -1 ?
412 "Error executing mount" :
417 // tidy up after succesful unmount
418 remove_mount(root_name);
419 if( rmdir(mount_point) == -1 )
420 fprintf(stderr, "Failed to remove mount point dir: %s (%s)",
421 mount_point, strerror(errno));
426 void unmount_all(void)
428 fprintf(stderr, "Attemping to unmount all filesystems:\n");
431 fprintf(stderr, "\tUnmounting: %s\n", mount_list->root_name);
433 // if unmount fails, ditch the mount anyway
434 if( !do_umount(mount_list->root_name) )
435 remove_mount(mount_list->root_name);
438 fprintf(stderr, "done.\n");
445 if(rmdir(mount_point_directory) == -1)
446 fprintf(stderr, "Failed to remove temporary mount point directory: %s (%s)\n",
447 mount_point_directory, strerror(errno));
450 int max_path_out_len(const char *path_in)
452 return strlen(mount_point_directory) + strlen(path_in) + 2;
455 // returns true if path is the a child directory of a root node
456 // e.g. /a/b is a child, /a is not.
457 int extract_root_name(const char *path, char *root_name)
462 for(i = 1; path[i] && path[i] != '/'; i++)
463 root_name[i - 1] = path[i];
464 root_name[i - 1] = '\0';
466 return strlen(&path[i]);
469 typedef enum {PROC_PATH_FAILED, PROC_PATH_ROOT_DIR, PROC_PATH_PROXY_DIR} proc_result_t;
471 proc_result_t process_path(const char *path_in, char *path_out, int attempt_mount)
474 char *root_name = alloca(strlen(path_in));
478 fprintf(stderr, "Path in: %s\n", path_in);
479 is_child = extract_root_name(path_in, root_name);
480 fprintf(stderr, "root_name is: %s\n", root_name);
482 // Mount filesystem if neccessary
483 // the combination of is_child and attempt_mount prevent inappropriate
484 // mounting of a filesystem for example if the user tries to mknod
485 // in the afuse root this should cause an error not a mount.
486 // !!FIXME!! this is broken on FUSE < 2.5 (?) because a getattr
487 // on the root node seems to occur with every single access.
488 if( //(is_child || attempt_mount ) &&
489 strlen(root_name) > 0 &&
490 !is_mount(root_name) &&
491 !do_mount(root_name))
492 return PROC_PATH_FAILED;
494 // construct path_out (mount_point_directory + '/' + path_in + '\0')
495 path_out_base = path_out;
496 for(i = 0; i < strlen(mount_point_directory); i++)
497 *path_out++ = mount_point_directory[i];
499 for(i = 0; i < strlen(path_in) - 1; i++)
500 *path_out++ = path_in[i + 1];
502 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) );
513 fprintf(stderr, "> GetAttr\n");
515 switch( process_path(path, real_path, 0) )
517 case PROC_PATH_FAILED:
520 case PROC_PATH_ROOT_DIR:
521 extract_root_name(path, root_name);
522 fprintf(stderr, "Getattr on: (%s) - %s\n", path, root_name);
523 if( is_mount(root_name) || strlen(root_name) == 0) {
524 stbuf->st_mode = S_IFDIR | 0700;
526 stbuf->st_uid = getuid();
527 stbuf->st_gid = getgid();
529 stbuf->st_blksize = 0;
530 stbuf->st_blocks = 0;
539 case PROC_PATH_PROXY_DIR:
540 res = lstat(real_path, stbuf);
549 static int afuse_readlink(const char *path, char *buf, size_t size)
552 char *real_path = alloca( max_path_out_len(path) );
554 switch( process_path(path, real_path, 1) )
556 case PROC_PATH_FAILED:
559 case PROC_PATH_ROOT_DIR:
562 case PROC_PATH_PROXY_DIR:
563 res = readlink(real_path, buf, size - 1);
572 static int afuse_opendir(const char *path, struct fuse_file_info *fi)
575 char *root_name = alloca( strlen(path) );
577 char *real_path = alloca( max_path_out_len(path) );
579 switch( process_path(path, real_path, 1) )
581 case PROC_PATH_FAILED:
584 case PROC_PATH_ROOT_DIR:
587 case PROC_PATH_PROXY_DIR:
588 dp = opendir(real_path);
593 fi->fh = (unsigned long) dp;
594 extract_root_name(path, root_name);
595 mount = find_mount(root_name);
597 add_dir(&mount->dir_list, dp);
602 static inline DIR *get_dirp(struct fuse_file_info *fi)
604 return (DIR *) (uintptr_t) fi->fh;
607 static int afuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
608 off_t offset, struct fuse_file_info *fi)
610 DIR *dp = get_dirp(fi);
612 char *real_path = alloca( max_path_out_len(path) );
615 switch( process_path(path, real_path, 1) )
617 case PROC_PATH_FAILED:
620 case PROC_PATH_ROOT_DIR:
621 filler(buf, ".", NULL, 0);
622 filler(buf, "..", NULL, 0);
623 for(mount = mount_list; mount; mount = mount->next)
624 filler(buf, mount->root_name, NULL, 0);
627 case PROC_PATH_PROXY_DIR:
629 while ((de = readdir(dp)) != NULL) {
631 memset(&st, 0, sizeof(st));
632 st.st_ino = de->d_ino;
633 st.st_mode = de->d_type << 12;
634 if (filler(buf, de->d_name, &st, telldir(dp)))
642 static int afuse_releasedir(const char *path, struct fuse_file_info *fi)
644 DIR *dp = get_dirp(fi);
646 char *root_name = alloca( strlen(path) );
648 char *real_path = alloca( max_path_out_len(path) );
650 switch( process_path(path, real_path, 1) )
652 case PROC_PATH_FAILED:
655 case PROC_PATH_ROOT_DIR:
658 case PROC_PATH_PROXY_DIR:
659 extract_root_name(path, root_name);
660 mount = find_mount(root_name);
662 remove_dir(&mount->dir_list, dp);
670 static int afuse_mknod(const char *path, mode_t mode, dev_t rdev)
673 char *real_path = alloca( max_path_out_len(path) );
675 fprintf(stderr, "> Mknod\n");
677 switch( process_path(path, real_path, 0) )
679 case PROC_PATH_FAILED:
682 case PROC_PATH_ROOT_DIR:
685 case PROC_PATH_PROXY_DIR:
687 res = mkfifo(real_path, mode);
689 res = mknod(real_path, mode, rdev);
697 static int afuse_mkdir(const char *path, mode_t mode)
700 char *real_path = alloca( max_path_out_len(path) );
702 switch( process_path(path, real_path, 0) )
704 case PROC_PATH_FAILED:
707 case PROC_PATH_ROOT_DIR:
710 case PROC_PATH_PROXY_DIR:
711 res = mkdir(real_path, mode);
719 static int afuse_unlink(const char *path)
722 char *real_path = alloca( max_path_out_len(path) );
724 switch( process_path(path, real_path, 0) )
726 case PROC_PATH_FAILED:
729 case PROC_PATH_ROOT_DIR:
732 case PROC_PATH_PROXY_DIR:
733 res = unlink(real_path);
741 static int afuse_rmdir(const char *path)
744 char *real_path = alloca( max_path_out_len(path) );
746 switch( process_path(path, real_path, 0) )
748 case PROC_PATH_FAILED:
751 case PROC_PATH_ROOT_DIR:
754 case PROC_PATH_PROXY_DIR:
755 res = rmdir(real_path);
763 static int afuse_symlink(const char *from, const char *to)
766 char *real_to_path = alloca( max_path_out_len(to) );
768 switch( process_path(to, real_to_path, 0) )
770 case PROC_PATH_FAILED:
773 case PROC_PATH_ROOT_DIR:
776 case PROC_PATH_PROXY_DIR:
777 res = symlink(from, real_to_path);
785 static int afuse_rename(const char *from, const char *to)
788 char *real_from_path = alloca( max_path_out_len(from) );
789 char *real_to_path = alloca( max_path_out_len(to) );
791 switch( process_path(from, real_from_path, 0) )
793 case PROC_PATH_FAILED:
796 case PROC_PATH_ROOT_DIR:
799 case PROC_PATH_PROXY_DIR:
800 switch( process_path(to, real_to_path, 0) )
802 case PROC_PATH_FAILED:
805 case PROC_PATH_ROOT_DIR:
808 case PROC_PATH_PROXY_DIR:
809 res = rename(real_from_path, real_to_path);
818 static int afuse_link(const char *from, const char *to)
821 char *real_from_path = alloca( max_path_out_len(from) );
822 char *real_to_path = alloca( max_path_out_len(to) );
824 switch( process_path(from, real_from_path, 0) )
826 case PROC_PATH_FAILED:
829 case PROC_PATH_ROOT_DIR:
832 case PROC_PATH_PROXY_DIR:
833 switch( process_path(to, real_to_path, 0) )
835 case PROC_PATH_FAILED:
838 case PROC_PATH_ROOT_DIR:
841 case PROC_PATH_PROXY_DIR:
842 res = link(real_from_path, real_to_path);
851 static int afuse_chmod(const char *path, mode_t mode)
854 char *real_path = alloca( max_path_out_len(path) );
856 switch( process_path(path, real_path, 0) )
858 case PROC_PATH_FAILED:
861 case PROC_PATH_ROOT_DIR:
864 case PROC_PATH_PROXY_DIR:
865 res = chmod(real_path, mode);
873 static int afuse_chown(const char *path, uid_t uid, gid_t gid)
876 char *real_path = alloca( max_path_out_len(path) );
878 switch( process_path(path, real_path, 0) )
880 case PROC_PATH_FAILED:
883 case PROC_PATH_ROOT_DIR:
886 case PROC_PATH_PROXY_DIR:
887 res = lchown(real_path, uid, gid);
895 static int afuse_truncate(const char *path, off_t size)
898 char *real_path = alloca( max_path_out_len(path) );
900 switch( process_path(path, real_path, 0) )
902 case PROC_PATH_FAILED:
905 case PROC_PATH_ROOT_DIR:
908 case PROC_PATH_PROXY_DIR:
909 res = truncate(real_path, size);
918 static int afuse_utime(const char *path, struct utimbuf *buf)
921 char *real_path = alloca( max_path_out_len(path) );
923 switch( process_path(path, real_path, 0) )
925 case PROC_PATH_FAILED:
928 case PROC_PATH_ROOT_DIR:
931 case PROC_PATH_PROXY_DIR:
932 res = utime(real_path, buf);
941 static int afuse_open(const char *path, struct fuse_file_info *fi)
944 char *root_name = alloca( strlen(path) );
946 char *real_path = alloca( max_path_out_len(path) );
948 switch( process_path(path, real_path, 1) )
950 case PROC_PATH_FAILED:
953 case PROC_PATH_ROOT_DIR:
956 case PROC_PATH_PROXY_DIR:
957 fd = open(real_path, fi->flags);
962 extract_root_name(path, root_name);
963 mount = find_mount(root_name);
965 add_fd(&mount->fd_list, fd);
970 static int afuse_read(const char *path, char *buf, size_t size, off_t offset,
971 struct fuse_file_info *fi)
976 res = pread(fi->fh, buf, size, offset);
983 static int afuse_write(const char *path, const char *buf, size_t size,
984 off_t offset, struct fuse_file_info *fi)
989 res = pwrite(fi->fh, buf, size, offset);
997 static int afuse_release(const char *path, struct fuse_file_info *fi)
999 char *root_name = alloca( strlen(path) );
1000 mount_list_t *mount;
1002 extract_root_name(path, root_name);
1003 mount = find_mount(root_name);
1005 remove_fd(&mount->fd_list, fi->fh);
1012 static int afuse_fsync(const char *path, int isdatasync,
1013 struct fuse_file_info *fi)
1018 #ifndef HAVE_FDATASYNC
1022 res = fdatasync(fi->fh);
1025 res = fsync(fi->fh);
1032 #if FUSE_VERSION >= 25
1033 static int afuse_access(const char *path, int mask)
1036 char *real_path = alloca( max_path_out_len(path) );
1038 switch( process_path(path, real_path, 1) )
1040 case PROC_PATH_FAILED:
1043 case PROC_PATH_ROOT_DIR:
1044 case PROC_PATH_PROXY_DIR:
1045 res = access(real_path, mask);
1053 static int afuse_ftruncate(const char *path, off_t size,
1054 struct fuse_file_info *fi)
1060 res = ftruncate(fi->fh, size);
1067 static int afuse_create(const char *path, mode_t mode, struct fuse_file_info *fi)
1070 char *real_path = alloca( max_path_out_len(path) );
1072 switch( process_path(path, real_path, 0) )
1074 case PROC_PATH_FAILED:
1077 case PROC_PATH_ROOT_DIR:
1080 case PROC_PATH_PROXY_DIR:
1081 fd = open(real_path, fi->flags, mode);
1090 static int afuse_fgetattr(const char *path, struct stat *stbuf,
1091 struct fuse_file_info *fi)
1097 res = fstat(fi->fh, stbuf);
1106 #if FUSE_VERSION >= 25
1107 static int afuse_statfs(const char *path, struct statvfs *stbuf)
1109 static int afuse_statfs(const char *path, struct statfs *stbuf)
1113 char *real_path = alloca( max_path_out_len(path) );
1115 switch( process_path(path, real_path, 1) )
1117 case PROC_PATH_FAILED:
1120 case PROC_PATH_ROOT_DIR:
1121 #if FUSE_VERSION >= 25
1122 stbuf->f_namemax = 0x7fffffff;
1123 stbuf->f_frsize = 512;
1125 stbuf->f_namelen = 0x7fffffff;
1127 stbuf->f_bsize = 1024;
1128 stbuf->f_blocks = 0;
1130 stbuf->f_bavail = 0;
1135 case PROC_PATH_PROXY_DIR:
1136 res = statvfs(real_path, stbuf);
1144 void afuse_destroy(void *p)
1149 #ifdef HAVE_SETXATTR
1150 /* xattr operations are optional and can safely be left unimplemented */
1151 static int afuse_setxattr(const char *path, const char *name, const char *value,
1152 size_t size, int flags)
1155 char *real_path = alloca( max_path_out_len(path) );
1157 switch( process_path(path, real_path, 0) )
1159 case PROC_PATH_FAILED:
1162 case PROC_PATH_ROOT_DIR:
1165 case PROC_PATH_PROXY_DIR:
1166 res = lsetxattr(real_path, name, value, size, flags);
1173 static int afuse_getxattr(const char *path, const char *name, char *value,
1177 char *real_path = alloca( max_path_out_len(path) );
1179 switch( process_path(path, real_path, 1) )
1181 case PROC_PATH_FAILED:
1184 case PROC_PATH_ROOT_DIR:
1187 case PROC_PATH_PROXY_DIR:
1188 res = lgetxattr(real_path, name, value, size);
1195 static int afuse_listxattr(const char *path, char *list, size_t size)
1198 char *real_path = alloca( max_path_out_len(path) );
1200 switch( process_path(path, real_path, 1) )
1202 case PROC_PATH_FAILED:
1205 case PROC_PATH_ROOT_DIR:
1208 case PROC_PATH_PROXY_DIR:
1209 res = llistxattr(real_path, list, size);
1216 static int afuse_removexattr(const char *path, const char *name)
1219 char *real_path = alloca( max_path_out_len(path) );
1221 switch( process_path(path, real_path, 0) )
1223 case PROC_PATH_FAILED:
1226 case PROC_PATH_ROOT_DIR:
1229 case PROC_PATH_PROXY_DIR:
1230 res = lremovexattr(real_path, name);
1236 #endif /* HAVE_SETXATTR */
1238 static struct fuse_operations afuse_oper = {
1239 .getattr = afuse_getattr,
1240 .readlink = afuse_readlink,
1241 .opendir = afuse_opendir,
1242 .readdir = afuse_readdir,
1243 .releasedir = afuse_releasedir,
1244 .mknod = afuse_mknod,
1245 .mkdir = afuse_mkdir,
1246 .symlink = afuse_symlink,
1247 .unlink = afuse_unlink,
1248 .rmdir = afuse_rmdir,
1249 .rename = afuse_rename,
1251 .chmod = afuse_chmod,
1252 .chown = afuse_chown,
1253 .truncate = afuse_truncate,
1254 .utime = afuse_utime,
1257 .write = afuse_write,
1258 .release = afuse_release,
1259 .fsync = afuse_fsync,
1260 .statfs = afuse_statfs,
1261 #if FUSE_VERSION >= 25
1262 .access = afuse_access,
1263 .create = afuse_create,
1264 .ftruncate = afuse_ftruncate,
1265 .fgetattr = afuse_fgetattr,
1267 .destroy = afuse_destroy,
1268 #ifdef HAVE_SETXATTR
1269 .setxattr = afuse_setxattr,
1270 .getxattr = afuse_getxattr,
1271 .listxattr = afuse_listxattr,
1272 .removexattr = afuse_removexattr,
1281 #define AFUSE_OPT(t, p, v) { t, offsetof(struct user_options_t, p), v }
1283 static struct fuse_opt afuse_opts[] = {
1284 AFUSE_OPT("mount_template=%s", mount_command_template, 0),
1285 AFUSE_OPT("unmount_template=%s", unmount_command_template, 0),
1287 FUSE_OPT_KEY("-h", KEY_HELP),
1288 FUSE_OPT_KEY("--help", KEY_HELP),
1293 static void usage(const char *progname)
1296 "Usage: %s mountpoint [options]\n"
1298 " -o opt,[opt...] mount options\n"
1299 " -h --help print help\n"
1300 " -V --version print FUSE version information\n"
1303 " -o mount_template=CMD template for CMD to execute to mount (*)\n"
1304 " -o unmount_template=CMD template for CMD to execute to unmount (*) (**)\n"
1306 " (*) - When executed, %%r and %%m are expanded in templates to the root\n"
1307 " directory name for the new mount point, and the actual directory to\n"
1308 " mount onto respectively to mount onto. Both templates are REQUIRED.\n"
1310 " (**) - The unmount command must perform a lazy unmount operation. E.g. the\n"
1311 " -u -z options to fusermount, or -l for regular mount.\n"
1315 static int afuse_opt_proc(void *data, const char *arg, int key,
1316 struct fuse_args *outargs)
1318 struct user_options_t *user_options = (struct user_options_t *)user_options;
1320 (void) user_options;
1326 usage(outargs->argv[0]);
1327 fuse_opt_add_arg(outargs, "-ho");
1328 fuse_main(outargs->argc, outargs->argv, &afuse_oper);
1336 int main(int argc, char *argv[])
1338 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1339 char *temp_dir_name = my_malloc(strlen(TMP_DIR_TEMPLATE));
1340 strcpy(temp_dir_name, TMP_DIR_TEMPLATE);
1342 if(fuse_opt_parse(&args, &user_options, afuse_opts, afuse_opt_proc) == -1)
1345 // !!FIXME!! force single-threading for now as datastructures are not locked
1346 fuse_opt_add_arg(&args, "-s");
1348 // Check for required parameters
1349 if(!user_options.mount_command_template || !user_options.unmount_command_template) {
1350 fprintf(stderr, "(Un)Mount command templates missing.\n\n");
1352 fuse_opt_add_arg(&args, "-ho");
1353 fuse_main(args.argc, args.argv, &afuse_oper);
1358 if( !(mount_point_directory = mkdtemp(temp_dir_name)) ) {
1359 fprintf(stderr, "Failed to create temporary mount point dir.\n");
1365 // Register function to tidy up on exit conditions
1366 if( atexit( shutdown ) ) {
1367 fprintf(stderr, "Failed to register exit handler.\n");
1371 // !!FIXME!! death by signal doesn't unmount fs
1372 return fuse_main(args.argc, args.argv, &afuse_oper);