]> CyberLeo.Net >> Repos - SourceForge/afuse.git/blob - src/afuse.c
Better handling of quotes in command template expansion.
[SourceForge/afuse.git] / src / afuse.c
1 /*
2         afuse -  An automounter using FUSE
3         Copyright (C) 2006  Jacob Bower <jacob.bower@ic.ac.uk>
4
5         Portions of this program derive from examples provided with
6         FUSE-2.5.2.
7
8         This program can be distributed under the terms of the GNU GPL.
9         See the file COPYING.
10 */
11
12 #include <config.h>
13
14 #ifdef linux
15 // For pread()/pwrite()
16 #define _XOPEN_SOURCE 500
17 #endif
18
19 #include <fuse.h>
20 #include <fuse_opt.h>
21 // for mkdtemp
22 #define __USE_BSD
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <stddef.h>
27 #include <unistd.h>
28 #include <alloca.h>
29 #include <fcntl.h>
30 #include <dirent.h>
31 #include <errno.h>
32 #include <sys/wait.h>
33 #include <sys/types.h>
34 #include <unistd.h>
35 #include <stdint.h>
36 #include <signal.h>
37 #ifdef HAVE_SETXATTR
38 #include <sys/xattr.h>
39 #endif
40
41 #include "fd_list.h"
42 #include "dir_list.h"
43 #include "utils.h"
44
45 #define TMP_DIR_TEMPLATE "/tmp/afuse-XXXXXX"
46 char *mount_point_directory;
47
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;
54         bool flush_writes;
55 } user_options = {NULL, NULL, false, false, false};
56
57 typedef struct _mount_list_t {
58         struct _mount_list_t *next;
59         struct _mount_list_t *prev;
60
61         char *root_name;
62         fd_list_t *fd_list;
63         dir_list_t *dir_list;
64 } mount_list_t;
65
66 mount_list_t *mount_list = NULL;
67
68 mount_list_t *find_mount(const char *root_name)
69 {
70         mount_list_t *current_mount = mount_list;
71
72         while(current_mount) {
73                 if( strcmp(root_name, current_mount->root_name) == 0)
74                         return current_mount;
75                         
76                 current_mount = current_mount->next;
77         }
78
79         return NULL;
80 }
81        
82 int is_mount(const char *root_name)
83 {
84         return find_mount(root_name) ? 1 : 0;
85 }
86
87 void add_mount(const char *root_name)
88 {
89         mount_list_t *new_mount;
90         
91         new_mount = (mount_list_t *)my_malloc( sizeof(mount_list_t) );
92         new_mount->root_name = my_strdup(root_name);
93
94         new_mount->next = mount_list;
95         new_mount->prev = NULL;
96         new_mount->fd_list = NULL;
97         new_mount->dir_list = NULL;
98         if(mount_list)
99                 mount_list->prev = new_mount;
100         
101         mount_list = new_mount;
102 }
103
104 void remove_mount(const char *root_name)
105 {
106         mount_list_t *current_mount = mount_list;
107
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;
113                         else
114                                 mount_list = current_mount->next;
115                         if(current_mount->next)
116                                 current_mount->next->prev = current_mount->prev;
117                         free(current_mount);
118
119                         return;
120                 }
121
122                 current_mount = current_mount->next;
123         }
124 }
125
126 int make_mount_point(const char *root_name)
127 {
128         char *dir_tmp;
129         int i;
130         
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) {
135                         dir_tmp[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));
139                                 return 0;
140                         }
141                         dir_tmp[i] = '/';
142                 }
143         free(dir_tmp);
144         
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);
150         
151         if(mkdir(dir_tmp, 0700) == -1 && errno != EEXIST) {
152                 fprintf(stderr, "Cannot create directory: %s (%s)\n",
153                         dir_tmp, strerror(errno));
154                 return 0;
155         }
156         free(dir_tmp);
157
158         return 1;
159 }
160
161
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)
165 {
166         int len = 0;
167         int i;
168         char *expanded_name;
169         char *expanded_name_start;
170
171         // calculate length
172         for(i = 0; template[i]; i++)
173                 if(template[i] == '%') {
174                         switch(template[i + 1])
175                         {
176                                 case 'm':
177                                         len += strlen(mount_point) + 2;
178                                         i++;
179                                         break;
180                                 case 'r':
181                                         len += strlen(root_name) + 2;
182                                         i++;
183                                         break;
184                         }
185                 } else if(template[i] != '"')
186                         len++;
187         
188         expanded_name_start = expanded_name = my_malloc(len + 1);
189
190         for(i = 0; template[i]; i++)
191                 if(template[i] == '%') {
192                         int j = 0;
193                         switch(template[i + 1])
194                         {
195                                 case 'm':
196                                         *expanded_name++ = '"';
197                                         while(mount_point[j])
198                                                 *expanded_name++ = mount_point[j++];
199                                         *expanded_name++ = '"';
200                                         i++;
201                                         break;
202                                 case 'r':
203                                         *expanded_name++ = '"';
204                                         while(root_name[j])
205                                                 *expanded_name++ = root_name[j++];
206                                         *expanded_name++ = '"';
207                                         i++;
208                                         break;
209                         }
210                 } else if(template[i] != '"')
211                         *expanded_name++ = template[i];
212         
213         *expanded_name = '\0';
214         
215         return expanded_name_start;
216 }
217
218 int do_mount(const char *root_name)
219 {
220         char *mount_point;
221         char *mount_command;
222         mount_list_t *mount;
223         int sysret;
224
225         fprintf(stderr, "Mounting: %s\n", root_name);
226
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);
230                 return 0;
231         }
232                 
233         mount_point = alloca(strlen(mount_point_directory) + 2 + strlen(root_name));
234         sprintf(mount_point, "%s/%s", mount_point_directory, root_name);
235         
236         mount_command = expand_template(user_options.mount_command_template,
237                                         mount_point, root_name);
238         sysret = system(mount_command);
239
240         fprintf(stderr, "sysret: %.8x\n", sysret);
241
242         if(sysret) {
243                 fprintf(stderr, "Failed to invoke mount command: '%s' (%s)\n",
244                         mount_command, sysret != -1 ?
245                                        "Error executing mount" :
246                                        strerror(errno));
247
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));
252
253                 free(mount_command);
254                 return 0;
255         }
256
257         add_mount(root_name);
258
259         free(mount_command);
260         return 1;
261 }
262
263 int do_umount(const char *root_name)
264 {
265         char *mount_point;
266         char *unmount_command;
267         mount_list_t *mount;
268         int sysret;
269
270         fprintf(stderr, "Unmounting: %s\n", root_name);
271                 
272         mount = find_mount(root_name);
273         if(!mount) {
274                 fprintf(stderr, "Internal Error: tried to unmount non-existant mount point: %s\n", root_name);
275                 return 1;
276         }
277         
278         mount_point = alloca(strlen(mount_point_directory) + 2 + strlen(root_name));
279         sprintf(mount_point, "%s/%s", mount_point_directory, root_name);
280
281         unmount_command = expand_template(user_options.unmount_command_template,
282                                           mount_point, root_name);
283         sysret = system(unmount_command);
284         if(sysret) {
285                 fprintf(stderr, "Failed to invoke unmount command: '%s' (%s)\n",
286                         unmount_command, sysret != -1 ?
287                                        "Error executing mount" :
288                                        strerror(errno));
289                 free(unmount_command);
290                 return 0;
291         }
292         
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));
298
299         free(unmount_command);
300         return 1;
301 }
302
303 void unmount_all(void)
304 {
305         fprintf(stderr, "Attemping to unmount all filesystems:\n");
306         
307         while(mount_list) {
308                 fprintf(stderr, "\tUnmounting: %s\n", mount_list->root_name);
309                 
310                 // if unmount fails, ditch the mount anyway
311                 if( !do_umount(mount_list->root_name) )
312                         remove_mount(mount_list->root_name);
313         }
314         
315         fprintf(stderr, "done.\n");
316 }
317
318 void shutdown(void)
319 {
320         unmount_all();
321
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));
325 }
326
327 int max_path_out_len(const char *path_in)
328 {
329         return strlen(mount_point_directory) + strlen(path_in) + 2;
330 }
331
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)
335 {
336         int i;
337         int is_child;
338         
339         for(i = 1; path[i] && path[i] != '/'; i++)
340                 root_name[i - 1] = path[i];
341         root_name[i - 1] = '\0';
342
343         return strlen(&path[i]);
344 }
345
346 typedef enum {PROC_PATH_FAILED, PROC_PATH_ROOT_DIR, PROC_PATH_PROXY_DIR} proc_result_t;
347
348 proc_result_t process_path(const char *path_in, char *path_out, char *root_name, int attempt_mount)
349 {
350         int i;
351         char *path_out_base;
352         int is_child;
353
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);
357         
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;
369
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];
374         *path_out++ = '/';
375         for(i = 0; i < strlen(path_in) - 1; i++)
376                 *path_out++ = path_in[i + 1];
377         *path_out = '\0';
378         fprintf(stderr, "Path out: %s\n", path_out_base);
379         
380         return strlen(root_name) ? PROC_PATH_PROXY_DIR : PROC_PATH_ROOT_DIR;
381 }
382
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)
388 {
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);
393                 if(mount &&
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);
398                 }
399         } else {
400                 fprintf(stderr, "No user option\n", root_name);
401         }
402 }
403
404 static int afuse_getattr(const char *path, struct stat *stbuf)
405 {
406         int res;
407         char *root_name = alloca( strlen(path) );
408         char *real_path = alloca( max_path_out_len(path) );
409
410         fprintf(stderr, "> GetAttr\n");
411
412         switch( process_path(path, real_path, root_name, 0) )
413         {
414                 case PROC_PATH_FAILED:
415                         return -ENXIO;
416
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;
422                                 stbuf->st_nlink   = 1;
423                                 stbuf->st_uid     = getuid();
424                                 stbuf->st_gid     = getgid();
425                                 stbuf->st_size    = 0;
426                                 stbuf->st_blksize = 0;
427                                 stbuf->st_blocks  = 0;
428                                 stbuf->st_atime   = 0;
429                                 stbuf->st_mtime   = 0;
430                                 stbuf->st_ctime   = 0;
431                                                        
432                                 return 0;
433                         } else
434                                 return -ENOENT;
435                         
436                 case PROC_PATH_PROXY_DIR:
437                         res = lstat(real_path, stbuf);
438                         consider_umount(root_name, false);
439                         if (res == -1)
440                                 return -errno;
441
442                         return 0;
443         }
444 }
445
446
447 static int afuse_readlink(const char *path, char *buf, size_t size)
448 {
449         int res;
450         char *root_name = alloca( strlen(path) );
451         char *real_path = alloca( max_path_out_len(path) );
452
453         switch( process_path(path, real_path, root_name, 1) )
454         {
455                 case PROC_PATH_FAILED:
456                         return -ENXIO;
457
458                 case PROC_PATH_ROOT_DIR:
459                         return -ENOENT;
460                         
461                 case PROC_PATH_PROXY_DIR:
462                         res = readlink(real_path, buf, size - 1);
463                         consider_umount(root_name, false);
464                         if (res == -1)
465                                 return -errno;
466
467                         buf[res] = '\0';
468                         return 0;
469         }
470 }
471
472 static int afuse_opendir(const char *path, struct fuse_file_info *fi)
473 {
474         DIR *dp;
475         char *root_name = alloca( strlen(path) );
476         mount_list_t *mount;
477         char *real_path = alloca( max_path_out_len(path) );
478        
479         switch( process_path(path, real_path, root_name, 1) )
480         {
481                 case PROC_PATH_FAILED:
482                         return -ENXIO;
483
484                 case PROC_PATH_ROOT_DIR:
485                         return 0;
486                         
487                 case PROC_PATH_PROXY_DIR:
488                         dp = opendir(real_path);
489
490                         if (dp == NULL) {
491                                 consider_umount(root_name, false);
492                                 return -errno;
493                         }
494
495                         fi->fh = (unsigned long) dp;
496                         mount = find_mount(root_name);
497                         if(mount)
498                                 dir_list_add(&mount->dir_list, dp);
499                         return 0;
500         }
501 }
502
503 static inline DIR *get_dirp(struct fuse_file_info *fi)
504 {
505         return (DIR *) (uintptr_t) fi->fh;
506 }
507
508 static int afuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
509                        off_t offset, struct fuse_file_info *fi)
510 {
511         DIR *dp = get_dirp(fi);
512         struct dirent *de;
513         char *root_name = alloca( strlen(path) );
514         char *real_path = alloca( max_path_out_len(path) );
515         mount_list_t *mount;
516        
517         switch( process_path(path, real_path, root_name, 1) )
518         {
519                 case PROC_PATH_FAILED:
520                         return -ENXIO;
521
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);
527                         return 0;
528                         
529                 case PROC_PATH_PROXY_DIR:
530                         seekdir(dp, offset);
531                         while ((de = readdir(dp)) != NULL) {
532                                 struct stat st;
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)))
537                                         break;
538                         }
539                         consider_umount(root_name, false);
540
541                         return 0;
542         }
543 }
544
545 static int afuse_releasedir(const char *path, struct fuse_file_info *fi)
546 {
547         DIR *dp = get_dirp(fi);
548         mount_list_t *mount;
549         char *root_name = alloca( strlen(path) );
550         char *real_path = alloca( max_path_out_len(path) );
551        
552         switch( process_path(path, real_path, root_name, 1) )
553         {
554                 case PROC_PATH_FAILED:
555                         return -ENXIO;
556
557                 case PROC_PATH_ROOT_DIR:
558                         return 0;
559                         
560                 case PROC_PATH_PROXY_DIR:
561                         extract_root_name(path, root_name);
562                         mount = find_mount(root_name);
563                         if(mount)
564                                 dir_list_remove(&mount->dir_list, dp);
565                         
566                         closedir(dp);
567         
568                         consider_umount(root_name, false);
569                         
570                         return 0;
571         }
572 }
573
574 static int afuse_mknod(const char *path, mode_t mode, dev_t rdev)
575 {
576         int res;
577         char *root_name = alloca( strlen(path) );
578         char *real_path = alloca( max_path_out_len(path) );
579
580         fprintf(stderr, "> Mknod\n");
581
582         switch( process_path(path, real_path, root_name, 0) )
583         {
584                 case PROC_PATH_FAILED:
585                         return -ENXIO;
586
587                 case PROC_PATH_ROOT_DIR:
588                         return -ENOTSUP;
589                         
590                 case PROC_PATH_PROXY_DIR:
591                         if (S_ISFIFO(mode))
592                                 res = mkfifo(real_path, mode);
593                         else
594                                 res = mknod(real_path, mode, rdev);
595                         consider_umount(root_name, true);
596                         if (res == -1)
597                                 return -errno;
598
599                         return 0;
600         }
601 }
602
603 static int afuse_mkdir(const char *path, mode_t mode)
604 {
605         int res;
606         char *root_name = alloca( strlen(path) );
607         char *real_path = alloca( max_path_out_len(path) );
608         
609         switch( process_path(path, real_path, root_name, 0) )
610         {
611                 case PROC_PATH_FAILED:
612                         return -ENXIO;
613
614                 case PROC_PATH_ROOT_DIR:
615                         return -ENOTSUP;
616                         
617                 case PROC_PATH_PROXY_DIR:
618                         res = mkdir(real_path, mode);
619                         consider_umount(root_name, true);
620                         if (res == -1)
621                                 return -errno;
622
623                         return 0;
624         }
625 }
626
627 static int afuse_unlink(const char *path)
628 {
629         int res;
630         char *root_name = alloca( strlen(path) );
631         char *real_path = alloca( max_path_out_len(path) );
632
633         switch( process_path(path, real_path, root_name, 0) )
634         {
635                 case PROC_PATH_FAILED:
636                         return -ENXIO;
637
638                 case PROC_PATH_ROOT_DIR:
639                         return -ENOTSUP;
640                         
641                 case PROC_PATH_PROXY_DIR:
642                         res = unlink(real_path);
643                         consider_umount(root_name, true);
644                         if (res == -1)
645                                 return -errno;
646
647                         return 0;
648         }
649 }
650
651 static int afuse_rmdir(const char *path)
652 {
653         int res;
654         char *root_name = alloca( strlen(path) );
655         char *real_path = alloca( max_path_out_len(path) );
656         
657         switch( process_path(path, real_path, root_name, 0) )
658         {
659                 case PROC_PATH_FAILED:
660                         return -ENXIO;
661
662                 case PROC_PATH_ROOT_DIR:
663                         return -ENOTSUP;
664                         
665                 case PROC_PATH_PROXY_DIR:
666                         res = rmdir(real_path);
667                         consider_umount(root_name, true);
668                         if (res == -1)
669                                 return -errno;
670
671                         return 0;
672         }
673 }
674
675 static int afuse_symlink(const char *from, const char *to)
676 {
677         int res;
678         char *root_name_to = alloca( strlen(to) );
679         char *real_to_path = alloca( max_path_out_len(to) );
680         
681         switch( process_path(to, real_to_path, root_name_to, 0) )
682         {
683                 case PROC_PATH_FAILED:
684                         return -ENXIO;
685
686                 case PROC_PATH_ROOT_DIR:
687                         return -ENOTSUP;
688                         
689                 case PROC_PATH_PROXY_DIR:
690                         res = symlink(from, real_to_path);
691                         consider_umount(root_name_to, true);
692                         if (res == -1)
693                                 return -errno;
694
695                         return 0;
696         }
697 }
698
699 static int afuse_rename(const char *from, const char *to)
700 {
701         int res;
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) );
706         
707         switch( process_path(from, real_from_path, root_name_from, 0) )
708         {
709                 case PROC_PATH_FAILED:
710                         return -ENXIO;
711
712                 case PROC_PATH_ROOT_DIR:
713                         return -ENOTSUP;
714                         
715                 case PROC_PATH_PROXY_DIR:
716                         switch( process_path(to, real_to_path, root_name_to, 0) )
717                         {
718                                 case PROC_PATH_FAILED:
719                                         consider_umount(root_name_from, false);
720                                         return -ENXIO;
721
722                                 case PROC_PATH_ROOT_DIR:
723                                         consider_umount(root_name_from, false);
724                                         return -ENOTSUP;
725                                         
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);
730                                         if (res == -1)
731                                                 return -errno;
732
733                                         return 0;
734                         }
735         }
736 }
737
738 static int afuse_link(const char *from, const char *to)
739 {
740         int res;
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) );
745         
746         switch( process_path(from, real_from_path, root_name_from, 0) )
747         {
748                 case PROC_PATH_FAILED:
749                         return -ENXIO;
750
751                 case PROC_PATH_ROOT_DIR:
752                         return -ENOTSUP;
753                         
754                 case PROC_PATH_PROXY_DIR:
755                         switch( process_path(to, real_to_path, root_name_to, 0) )
756                         {
757                                 case PROC_PATH_FAILED:
758                                         consider_umount(root_name_from, false);
759                                         return -ENXIO;
760
761                                 case PROC_PATH_ROOT_DIR:
762                                         consider_umount(root_name_from, false);
763                                         return -ENOTSUP;
764                                         
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);
769                                         if (res == -1)
770                                                 return -errno;
771
772                                         return 0;
773                         }
774         }
775 }
776
777 static int afuse_chmod(const char *path, mode_t mode)
778 {
779         int res;
780         char *root_name = alloca( strlen(path) );
781         char *real_path = alloca( max_path_out_len(path) );
782         
783         switch( process_path(path, real_path, root_name, 0) )
784         {
785                 case PROC_PATH_FAILED:
786                         return -ENXIO;
787
788                 case PROC_PATH_ROOT_DIR:
789                         return -ENOTSUP;
790                         
791                 case PROC_PATH_PROXY_DIR:
792                         res = chmod(real_path, mode);
793                         consider_umount(root_name, true);
794                         if (res == -1)
795                                 return -errno;
796
797                         return 0;
798         }
799 }
800
801 static int afuse_chown(const char *path, uid_t uid, gid_t gid)
802 {
803         int res;
804         char *root_name = alloca( strlen(path) );
805         char *real_path = alloca( max_path_out_len(path) );
806         
807         switch( process_path(path, real_path, root_name, 0) )
808         {
809                 case PROC_PATH_FAILED:
810                         return -ENXIO;
811
812                 case PROC_PATH_ROOT_DIR:
813                         return -ENOTSUP;
814                         
815                 case PROC_PATH_PROXY_DIR:
816                         res = lchown(real_path, uid, gid);
817                         consider_umount(root_name, true);
818                         if (res == -1)
819                                 return -errno;
820
821                         return 0;
822         }
823 }
824
825 static int afuse_truncate(const char *path, off_t size)
826 {
827         int res;
828         char *root_name = alloca( strlen(path) );
829         char *real_path = alloca( max_path_out_len(path) );
830         
831         switch( process_path(path, real_path, root_name, 0) )
832         {
833                 case PROC_PATH_FAILED:
834                         return -ENXIO;
835
836                 case PROC_PATH_ROOT_DIR:
837                         return -ENOTSUP;
838                         
839                 case PROC_PATH_PROXY_DIR:
840                         res = truncate(real_path, size);
841                         consider_umount(root_name, true);
842                         if (res == -1)
843                                 return -errno;
844
845                         return 0;
846         }
847 }
848
849
850 static int afuse_utime(const char *path, struct utimbuf *buf)
851 {
852         int res;
853         char *root_name = alloca( strlen(path) );
854         char *real_path = alloca( max_path_out_len(path) );
855         
856         switch( process_path(path, real_path, root_name, 0) )
857         {
858                 case PROC_PATH_FAILED:
859                         return -ENXIO;
860
861                 case PROC_PATH_ROOT_DIR:
862                         return -ENOTSUP;
863                         
864                 case PROC_PATH_PROXY_DIR:
865                         res = utime(real_path, buf);
866                         consider_umount(root_name, true);
867                         if (res == -1)
868                                 return -errno;
869
870                         return 0;
871         }
872 }
873
874
875 static int afuse_open(const char *path, struct fuse_file_info *fi)
876 {
877         int fd;
878         char *root_name = alloca( strlen(path) );
879         mount_list_t *mount;
880         char *real_path = alloca( max_path_out_len(path) );
881
882         switch( process_path(path, real_path, root_name, 1) )
883         {
884                 case PROC_PATH_FAILED:
885                         return -ENXIO;
886
887                 case PROC_PATH_ROOT_DIR:
888                         return -ENOENT;
889                         
890                 case PROC_PATH_PROXY_DIR:
891                         fd = open(real_path, fi->flags);
892                         if (fd == -1) {
893                                 consider_umount(root_name, true);
894                                 return -errno;
895                         }
896
897                         fi->fh = fd;
898                         extract_root_name(path, root_name);
899                         mount = find_mount(root_name);
900                         if(mount)
901                                 fd_list_add(&mount->fd_list, fd);
902                         return 0;
903         }
904 }
905
906 static int afuse_read(const char *path, char *buf, size_t size, off_t offset,
907                       struct fuse_file_info *fi)
908 {
909         int res;
910
911         (void)path;
912         res = pread(fi->fh, buf, size, offset);
913         if (res == -1)
914                 res = -errno;
915
916         return res;
917 }
918
919 static int afuse_write(const char *path, const char *buf, size_t size,
920                      off_t offset, struct fuse_file_info *fi)
921 {
922         int res;
923
924         (void) path;
925         res = pwrite(fi->fh, buf, size, offset);
926         if (res == -1)
927                 res = -errno;
928
929         if(user_options.flush_writes)
930                 fsync(fi->fh);
931
932         return res;
933 }
934
935
936 static int afuse_release(const char *path, struct fuse_file_info *fi)
937 {
938         char *root_name = alloca( strlen(path) );
939         mount_list_t *mount;
940         
941         extract_root_name(path, root_name);
942         mount = find_mount(root_name);
943         if(mount)
944                 fd_list_remove(&mount->fd_list, fi->fh);
945     
946         close(fi->fh);
947         consider_umount(root_name, true);
948
949
950         return 0;
951 }
952
953 static int afuse_fsync(const char *path, int isdatasync,
954                        struct fuse_file_info *fi)
955 {
956         int res;
957         (void) path;
958
959         #ifndef HAVE_FDATASYNC
960         (void) isdatasync;
961         #else
962         if (isdatasync)
963                 res = fdatasync(fi->fh);
964         else
965         #endif
966                 res = fsync(fi->fh);
967         if (res == -1)
968                 return -errno;
969
970         return 0;
971 }
972
973 #if FUSE_VERSION >= 25
974 static int afuse_access(const char *path, int mask)
975 {
976         int res;
977         char *root_name = alloca( strlen(path) );
978         char *real_path = alloca( max_path_out_len(path) );
979
980         switch( process_path(path, real_path, root_name, 1) )
981         {
982                 case PROC_PATH_FAILED:
983                         return -ENXIO;
984
985                 case PROC_PATH_ROOT_DIR:
986                 case PROC_PATH_PROXY_DIR:
987                         res = access(real_path, mask);
988                         consider_umount(root_name, false);
989                         if (res == -1)
990                                 return -errno;
991
992                         return 0;
993         }
994 }
995
996 static int afuse_ftruncate(const char *path, off_t size,
997                            struct fuse_file_info *fi)
998 {
999         int res;
1000
1001         (void) path;
1002
1003         res = ftruncate(fi->fh, size);
1004         if (res == -1)
1005                 return -errno;
1006
1007         return 0;
1008 }
1009
1010 static int afuse_create(const char *path, mode_t mode, struct fuse_file_info *fi)
1011 {
1012         int fd;
1013         char *root_name = alloca( strlen(path) );
1014         char *real_path = alloca( max_path_out_len(path) );
1015
1016         switch( process_path(path, real_path, root_name, 0) )
1017         {
1018                 case PROC_PATH_FAILED:
1019                         return -ENXIO;
1020
1021                 case PROC_PATH_ROOT_DIR:
1022                         return -ENOTSUP;
1023                         
1024                 case PROC_PATH_PROXY_DIR:
1025                         fd = open(real_path, fi->flags, mode);
1026                         consider_umount(root_name, true);
1027                         if (fd == -1)
1028                                 return -errno;
1029
1030                         fi->fh = fd;
1031                         return 0;
1032         }
1033 }
1034
1035 static int afuse_fgetattr(const char *path, struct stat *stbuf,
1036                           struct fuse_file_info *fi)
1037 {
1038         int res;
1039
1040         (void) path;
1041
1042         res = fstat(fi->fh, stbuf);
1043         if (res == -1)
1044                 return -errno;
1045
1046         return 0;
1047 }
1048 #endif
1049
1050
1051 #if FUSE_VERSION >= 25
1052 static int afuse_statfs(const char *path, struct statvfs *stbuf)
1053 #else
1054 static int afuse_statfs(const char *path, struct statfs *stbuf)
1055 #endif
1056 {
1057         int res;
1058         char *root_name = alloca( strlen(path) );
1059         char *real_path = alloca( max_path_out_len(path) );
1060
1061         switch( process_path(path, real_path, root_name, 1) )
1062         {
1063                 case PROC_PATH_FAILED:
1064                         return -ENXIO;
1065
1066                 case PROC_PATH_ROOT_DIR:
1067 #if FUSE_VERSION >= 25
1068                         stbuf->f_namemax = 0x7fffffff;
1069                         stbuf->f_frsize  = 512;
1070 #else
1071                         stbuf->f_namelen = 0x7fffffff;
1072 #endif
1073                         stbuf->f_bsize   = 1024;
1074                         stbuf->f_blocks  = 0;
1075                         stbuf->f_bfree   = 0;
1076                         stbuf->f_bavail  = 0;
1077                         stbuf->f_files   = 0;
1078                         stbuf->f_ffree   = 0;
1079                         return 0;
1080                 
1081                 case PROC_PATH_PROXY_DIR:
1082                         res = statvfs(real_path, stbuf);
1083                         consider_umount(root_name, false);
1084                         if (res == -1)
1085                                 return -errno;
1086
1087                         return 0;
1088         }
1089 }
1090
1091 void afuse_destroy(void *p)
1092 {
1093         shutdown();
1094 }
1095
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)
1100 {
1101         int res;
1102         char *root_name = alloca( strlen(path) );
1103         char *real_path = alloca( max_path_out_len(path) );
1104         
1105         switch( process_path(path, real_path, root_name, 0) )
1106         {
1107                 case PROC_PATH_FAILED:
1108                         return -ENXIO;
1109
1110                 case PROC_PATH_ROOT_DIR:
1111                         return -ENOENT;
1112                         
1113                 case PROC_PATH_PROXY_DIR:
1114                         res = lsetxattr(real_path, name, value, size, flags);
1115                         consider_umount(root_name, true);
1116                         if (res == -1)
1117                                 return -errno;
1118                         return 0;
1119         }
1120 }
1121
1122 static int afuse_getxattr(const char *path, const char *name, char *value,
1123                     size_t size)
1124 {
1125         int res;
1126         char *root_name = alloca( strlen(path) );
1127         char *real_path = alloca( max_path_out_len(path) );
1128
1129         switch( process_path(path, real_path, root_name, 1) )
1130         {
1131                 case PROC_PATH_FAILED:
1132                         return -ENXIO;
1133
1134                 case PROC_PATH_ROOT_DIR:
1135                         return -ENOTSUP;
1136                         
1137                 case PROC_PATH_PROXY_DIR:
1138                         res = lgetxattr(real_path, name, value, size);
1139                         consider_umount(root_name, false);
1140                         if (res == -1)
1141                                 return -errno;
1142                         return res;
1143         }
1144 }
1145
1146 static int afuse_listxattr(const char *path, char *list, size_t size)
1147 {
1148         int res;
1149         char *root_name = alloca( strlen(path) );
1150         char *real_path = alloca( max_path_out_len(path) );
1151
1152         switch( process_path(path, real_path, root_name, 1) )
1153         {
1154                 case PROC_PATH_FAILED:
1155                         return -ENXIO;
1156
1157                 case PROC_PATH_ROOT_DIR:
1158                         return -ENOTSUP;
1159                         
1160                 case PROC_PATH_PROXY_DIR:
1161                         res = llistxattr(real_path, list, size);
1162                         consider_umount(root_name, false);
1163                         if (res == -1)
1164                                 return -errno;
1165                         return res;
1166         }
1167 }
1168
1169 static int afuse_removexattr(const char *path, const char *name)
1170 {
1171         int res;
1172         char *root_name = alloca( strlen(path) );
1173         char *real_path = alloca( max_path_out_len(path) );
1174         
1175         switch( process_path(path, real_path, root_name, 0) )
1176         {
1177                 case PROC_PATH_FAILED:
1178                         return -ENXIO;
1179
1180                 case PROC_PATH_ROOT_DIR:
1181                         return -ENOTSUP;
1182                         
1183                 case PROC_PATH_PROXY_DIR:
1184                         res = lremovexattr(real_path, name);
1185                         consider_umount(root_name, true);
1186                         if (res == -1)
1187                                 return -errno;
1188                         return 0;
1189         }
1190 }
1191 #endif /* HAVE_SETXATTR */
1192
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,
1205         .link        = afuse_link,
1206         .chmod       = afuse_chmod,
1207         .chown       = afuse_chown,
1208         .truncate    = afuse_truncate,
1209         .utime       = afuse_utime,
1210         .open        = afuse_open,
1211         .read        = afuse_read,
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,
1221 #endif
1222         .destroy     = afuse_destroy,
1223 #ifdef HAVE_SETXATTR
1224         .setxattr    = afuse_setxattr,
1225         .getxattr    = afuse_getxattr,
1226         .listxattr   = afuse_listxattr,
1227         .removexattr = afuse_removexattr,
1228 #endif
1229 };
1230
1231
1232 enum {
1233         KEY_HELP,
1234         KEY_INUSEONLY,
1235         KEY_INUSEONLYALL,
1236         KEY_FLUSHWRITES
1237 };
1238
1239 #define AFUSE_OPT(t, p, v) { t, offsetof(struct user_options_t, p), v }
1240
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),
1244
1245         FUSE_OPT_KEY("inuseonly", KEY_INUSEONLY),
1246         FUSE_OPT_KEY("inuseonlyall", KEY_INUSEONLYALL),
1247         FUSE_OPT_KEY("flushwrites", KEY_FLUSHWRITES),
1248
1249         FUSE_OPT_KEY("-h",     KEY_HELP),
1250         FUSE_OPT_KEY("--help", KEY_HELP),
1251     
1252         FUSE_OPT_END
1253 };
1254
1255 static void usage(const char *progname)
1256 {
1257         fprintf(stderr,
1258 "Usage: %s mountpoint [options]\n"
1259 "\n"
1260 "    -o opt,[opt...]        mount options\n"
1261 "    -h   --help            print help\n"
1262 "    -V   --version         print FUSE version information\n"
1263 "\n"
1264 "afuse options:\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"
1270 "\n\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"
1274 "\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"       
1277 "\n", progname);
1278 }
1279
1280 static int afuse_opt_proc(void *data, const char *arg, int key,
1281                           struct fuse_args *outargs)
1282 {
1283         (void) arg;
1284
1285         switch(key)
1286         {
1287                 case KEY_HELP:
1288                         usage(outargs->argv[0]);
1289                         fuse_opt_add_arg(outargs, "-ho");
1290                         fuse_main(outargs->argc, outargs->argv, &afuse_oper);
1291                         exit(1);
1292
1293                 case KEY_INUSEONLY:
1294                         user_options.mount_inuse_only = true;
1295                         return 0;
1296
1297                 case KEY_INUSEONLYALL:
1298                         user_options.mount_inuse_only_all = true;
1299                         return 0;
1300
1301                 case KEY_FLUSHWRITES:
1302                         user_options.flush_writes = true;
1303                         return 0;
1304
1305                 default:
1306                         return 1;
1307         }
1308 }
1309
1310 int main(int argc, char *argv[])
1311 {
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);
1315         
1316         if(fuse_opt_parse(&args, &user_options, afuse_opts, afuse_opt_proc) == -1)
1317                 return 1;
1318         
1319         // !!FIXME!! force single-threading for now as datastructures are not locked
1320         fuse_opt_add_arg(&args, "-s");
1321
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");
1325                 usage(argv[0]);
1326                 fuse_opt_add_arg(&args, "-ho");
1327                 fuse_main(args.argc, args.argv, &afuse_oper);
1328                 
1329                 return 1;
1330         }
1331
1332         if( !(mount_point_directory = mkdtemp(temp_dir_name)) ) {
1333                 fprintf(stderr, "Failed to create temporary mount point dir.\n");
1334                 return 1;
1335         }
1336         
1337         umask(0);
1338         
1339         // Register function to tidy up on exit conditions
1340         if( atexit( shutdown ) ) {
1341                 fprintf(stderr, "Failed to register exit handler.\n");
1342                 return 1;
1343         }
1344
1345         // !!FIXME!! death by signal doesn't unmount fs
1346         return fuse_main(args.argc, args.argv, &afuse_oper);
1347 }