]> CyberLeo.Net >> Repos - SourceForge/afuse.git/blob - src/afuse.c
Moved to SVN due to CVS crapness.
[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 // 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
45 // succeed.
46 #define CLOSE_MAX_RETRIES 5
47
48 #define TMP_DIR_TEMPLATE "/tmp/afuse-XXXXXX"
49 char *mount_point_directory;
50
51 struct user_options_t {
52         char *mount_command_template;
53         char *unmount_command_template;
54 } user_options = {NULL, NULL};
55
56 typedef struct _fd_list_t {
57         struct _fd_list_t *next;
58         struct _fd_list_t *prev;
59
60         int fd;
61 } fd_list_t;
62
63 typedef struct _dir_list_t {
64         struct _dir_list_t *next;
65         struct _dir_list_t *prev;
66
67         DIR *dir;
68 } dir_list_t;
69
70 typedef struct _mount_list_t {
71         struct _mount_list_t *next;
72         struct _mount_list_t *prev;
73
74         char *root_name;
75         fd_list_t *fd_list;
76         dir_list_t *dir_list;
77 } mount_list_t;
78
79 mount_list_t *mount_list = NULL;
80
81 void *my_malloc(size_t size)
82 {
83         void *p;
84
85         p = malloc(size);
86
87         if(!p) {
88                 fprintf(stderr, "Failed to allocate: %d bytes of memory.\n");
89                 exit(1);
90         }
91
92         return p;
93 }
94
95 char *my_strdup(char *str)
96 {
97         char *new_str;
98
99         new_str = my_malloc(strlen(str) + 1);
100         strcpy(new_str, str);
101
102         return new_str;
103 }
104
105 mount_list_t *find_mount(char *root_name)
106 {
107         mount_list_t *current_mount = mount_list;
108
109         while(current_mount) {
110                 if( strcmp(root_name, current_mount->root_name) == 0)
111                         return current_mount;
112                         
113                 current_mount = current_mount->next;
114         }
115
116         return NULL;
117 }
118        
119 int is_mount(char *root_name)
120 {
121         return find_mount(root_name) ? 1 : 0;
122 }
123
124 void add_fd(fd_list_t **fd_list, int fd)
125 {
126         fd_list_t *new_fd;
127
128         new_fd = my_malloc( sizeof(fd_list_t) );
129         new_fd->fd = fd;
130         new_fd->next = *fd_list;
131         new_fd->prev = NULL;
132
133         *fd_list = new_fd;
134 }
135
136 void remove_fd(fd_list_t **fd_list, int fd)
137 {
138         fd_list_t *current_fd = *fd_list;
139         
140         while(current_fd) {
141                 if(current_fd->fd == fd) {
142                         if(current_fd->prev)
143                                 current_fd->prev->next = current_fd->next;
144                         else
145                                 *fd_list = current_fd->next;
146                         if(current_fd->next)
147                                 current_fd->next->prev = current_fd->prev;
148                         free(current_fd);
149
150                         return;
151                 }
152
153                 current_fd = current_fd->next;
154         }
155 }
156
157 void add_dir(dir_list_t **dir_list, DIR *dir)
158 {
159         dir_list_t *new_dir;
160
161         new_dir = my_malloc( sizeof(dir_list_t) );
162         new_dir->dir = dir;
163         new_dir->next = *dir_list;
164         new_dir->prev = NULL;
165
166         *dir_list = new_dir;
167 }
168
169 void remove_dir(dir_list_t **dir_list, DIR *dir)
170 {
171         dir_list_t *current_dir = *dir_list;
172
173         while(current_dir) {
174                 if(current_dir->dir == dir) {
175                         if(current_dir->prev)
176                                 current_dir->prev->next = current_dir->next;
177                         else
178                                 *dir_list = current_dir->next;
179                         if(current_dir->next)
180                                 current_dir->next->prev = current_dir->prev;
181                         free(current_dir);
182
183                         return;
184                 }
185
186                 current_dir = current_dir->next;
187         }
188 }
189
190 void close_all_fds(fd_list_t **fd_list)
191 {
192         while(*fd_list) {
193                 int retries;
194                 
195                 for(retries = 0; retries < CLOSE_MAX_RETRIES &&
196                                  close((*fd_list)->fd) == -1   &&
197                                  errno == EINTR;
198                     retries++);
199                 remove_fd(fd_list, (*fd_list)->fd);
200         }
201 }
202
203 void close_all_dirs(dir_list_t **dir_list)
204 {
205         while(*dir_list) {
206                 int retries;
207                 
208                 for(retries = 0; retries < CLOSE_MAX_RETRIES &&
209                                  closedir((*dir_list)->dir) == -1   &&
210                                  errno == EINTR;
211                     retries++);
212                 remove_dir(dir_list, (*dir_list)->dir);
213         }
214 }
215
216 void add_mount(char *root_name)
217 {
218         mount_list_t *new_mount;
219         
220         new_mount = (mount_list_t *)my_malloc( sizeof(mount_list_t) );
221         new_mount->root_name = my_strdup(root_name);
222
223         new_mount->next = mount_list;
224         new_mount->prev = NULL;
225         new_mount->fd_list = NULL;
226         new_mount->dir_list = NULL;
227         if(mount_list)
228                 mount_list->prev = new_mount;
229         
230         mount_list = new_mount;
231 }
232
233
234 void remove_mount(char *root_name)
235 {
236         mount_list_t *current_mount = mount_list;
237
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;
243                         else
244                                 mount_list = current_mount->next;
245                         if(current_mount->next)
246                                 current_mount->next->prev = current_mount->prev;
247                         free(current_mount);
248
249                         return;
250                 }
251
252                 current_mount = current_mount->next;
253         }
254 }
255
256 int make_mount_point(char *root_name)
257 {
258         char *dir_tmp;
259         int i;
260         
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) {
265                         dir_tmp[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));
269                                 return 0;
270                         }
271                         dir_tmp[i] = '/';
272                 }
273         free(dir_tmp);
274         
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);
280         
281         if(mkdir(dir_tmp, 0700) == -1 && errno != EEXIST) {
282                 fprintf(stderr, "Cannot create directory: %s (%s)\n",
283                         dir_tmp, strerror(errno));
284                 return 0;
285         }
286         free(dir_tmp);
287
288         return 1;
289 }
290
291
292 // !!FIXME!! allow escaping of %'s
293 char *expand_template(char *template, char *mount_point, char *root_name)
294 {
295         int len = 0;
296         int i;
297         char *expanded_name;
298         char *expanded_name_start;
299
300         // calculate length
301         for(i = 0; template[i]; i++)
302                 if(template[i] == '%') {
303                         switch(template[i + 1])
304                         {
305                                 case 'm':
306                                         len += strlen(mount_point);
307                                         i++;
308                                         break;
309                                 case 'r':
310                                         len += strlen(root_name);
311                                         i++;
312                                         break;
313                         }
314                 } else
315                         len++;
316         
317         expanded_name_start = expanded_name = my_malloc(len + 1);
318
319         for(i = 0; template[i]; i++)
320                 if(template[i] == '%') {
321                         int j = 0;
322                         switch(template[i + 1])
323                         {
324                                 case 'm':
325                                         while(mount_point[j])
326                                                 *expanded_name++ = mount_point[j++];
327                                         i++;
328                                         break;
329                                 case 'r':
330                                         while(root_name[j])
331                                                 *expanded_name++ = root_name[j++];
332                                         i++;
333                                         break;
334                         }
335                 } else
336                         *expanded_name++ = template[i];
337         
338         *expanded_name = '\0';
339         
340         return expanded_name_start;
341 }
342
343 int do_mount(char *root_name)
344 {
345         char *mount_point;
346         char *mount_command;
347         mount_list_t *mount;
348         int sysret;
349
350         fprintf(stderr, "Mounting: %s\n", root_name);
351
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);
355                 return 0;
356         }
357                 
358         mount_point = alloca(strlen(mount_point_directory) + 2 + strlen(root_name));
359         sprintf(mount_point, "%s/%s", mount_point_directory, root_name);
360         
361         mount_command = expand_template(user_options.mount_command_template,
362                                         mount_point, root_name);
363         sysret = system(mount_command);
364         free(mount_command);
365
366         fprintf(stderr, "sysret: %.8x\n", sysret);
367
368         if(sysret) {
369                 fprintf(stderr, "Failed to invoke mount command: \"%s\" (%s)\n",
370                         mount_command, sysret != -1 ?
371                                        "Error executing mount" :
372                                        strerror(errno));
373
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));
378
379                 return 0;
380         }
381
382         add_mount(root_name);
383
384         return 1;
385 }
386
387 int do_umount(char *root_name)
388 {
389         char *mount_point;
390         char *unmount_command;
391         mount_list_t *mount;
392         int sysret;
393
394         fprintf(stderr, "Unmounting: %s\n", root_name);
395                 
396         mount = find_mount(root_name);
397         if(!mount) {
398                 fprintf(stderr, "Internal Error: tried to unmount non-existant mount point: %s\n", root_name);
399                 return 1;
400         }
401         
402         mount_point = alloca(strlen(mount_point_directory) + 2 + strlen(root_name));
403         sprintf(mount_point, "%s/%s", mount_point_directory, root_name);
404
405         unmount_command = expand_template(user_options.unmount_command_template,
406                                           mount_point, root_name);
407         sysret = system(unmount_command);
408         free(unmount_command);
409         if(sysret) {
410                 fprintf(stderr, "Failed to invoke unmount command: \"%s\" (%s)\n",
411                         unmount_command, sysret != -1 ?
412                                        "Error executing mount" :
413                                        strerror(errno));
414                 return 0;
415         }
416         
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));
422
423         return 1;
424 }
425
426 void unmount_all(void)
427 {
428         fprintf(stderr, "Attemping to unmount all filesystems:\n");
429         
430         while(mount_list) {
431                 fprintf(stderr, "\tUnmounting: %s\n", mount_list->root_name);
432                 
433                 // if unmount fails, ditch the mount anyway
434                 if( !do_umount(mount_list->root_name) )
435                         remove_mount(mount_list->root_name);
436         }
437         
438         fprintf(stderr, "done.\n");
439 }
440
441 void shutdown(void)
442 {
443         unmount_all();
444
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));
448 }
449
450 int max_path_out_len(const char *path_in)
451 {
452         return strlen(mount_point_directory) + strlen(path_in) + 2;
453 }
454
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)
458 {
459         int i;
460         int is_child;
461         
462         for(i = 1; path[i] && path[i] != '/'; i++)
463                 root_name[i - 1] = path[i];
464         root_name[i - 1] = '\0';
465
466         return strlen(&path[i]);
467 }
468
469 typedef enum {PROC_PATH_FAILED, PROC_PATH_ROOT_DIR, PROC_PATH_PROXY_DIR} proc_result_t;
470
471 proc_result_t process_path(const char *path_in, char *path_out, int attempt_mount)
472 {
473         int i;
474         char *root_name = alloca(strlen(path_in));
475         char *path_out_base;
476         int is_child;
477
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);
481         
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;
493
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];
498         *path_out++ = '/';
499         for(i = 0; i < strlen(path_in) - 1; i++)
500                 *path_out++ = path_in[i + 1];
501         *path_out = '\0';
502         fprintf(stderr, "Path out: %s\n", path_out_base);
503         
504         return strlen(root_name) ? PROC_PATH_PROXY_DIR : PROC_PATH_ROOT_DIR;
505 }
506
507 static int afuse_getattr(const char *path, struct stat *stbuf)
508 {
509         int res;
510         char *root_name = alloca( strlen(path) );
511         char *real_path = alloca( max_path_out_len(path) );
512
513         fprintf(stderr, "> GetAttr\n");
514
515         switch( process_path(path, real_path, 0) )
516         {
517                 case PROC_PATH_FAILED:
518                         return -ENXIO;
519
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;
525                                 stbuf->st_nlink   = 1;
526                                 stbuf->st_uid     = getuid();
527                                 stbuf->st_gid     = getgid();
528                                 stbuf->st_size    = 0;
529                                 stbuf->st_blksize = 0;
530                                 stbuf->st_blocks  = 0;
531                                 stbuf->st_atime   = 0;
532                                 stbuf->st_mtime   = 0;
533                                 stbuf->st_ctime   = 0;
534                                                        
535                                 return 0;
536                         } else
537                                 return -ENOENT;
538                         
539                 case PROC_PATH_PROXY_DIR:
540                         res = lstat(real_path, stbuf);
541                         if (res == -1)
542                                 return -errno;
543
544                         return 0;
545         }
546 }
547
548
549 static int afuse_readlink(const char *path, char *buf, size_t size)
550 {
551         int res;
552         char *real_path = alloca( max_path_out_len(path) );
553
554         switch( process_path(path, real_path, 1) )
555         {
556                 case PROC_PATH_FAILED:
557                         return -ENXIO;
558
559                 case PROC_PATH_ROOT_DIR:
560                         return -ENOENT;
561                         
562                 case PROC_PATH_PROXY_DIR:
563                         res = readlink(real_path, buf, size - 1);
564                         if (res == -1)
565                                 return -errno;
566
567                         buf[res] = '\0';
568                         return 0;
569         }
570 }
571
572 static int afuse_opendir(const char *path, struct fuse_file_info *fi)
573 {
574         DIR *dp;
575         char *root_name = alloca( strlen(path) );
576         mount_list_t *mount;
577         char *real_path = alloca( max_path_out_len(path) );
578        
579         switch( process_path(path, real_path, 1) )
580         {
581                 case PROC_PATH_FAILED:
582                         return -ENXIO;
583
584                 case PROC_PATH_ROOT_DIR:
585                         return 0;
586                         
587                 case PROC_PATH_PROXY_DIR:
588                         dp = opendir(real_path);
589
590                         if (dp == NULL)
591                                 return -errno;
592
593                         fi->fh = (unsigned long) dp;
594                         extract_root_name(path, root_name);
595                         mount = find_mount(root_name);
596                         if(mount)
597                                 add_dir(&mount->dir_list, dp);
598                         return 0;
599         }
600 }
601
602 static inline DIR *get_dirp(struct fuse_file_info *fi)
603 {
604         return (DIR *) (uintptr_t) fi->fh;
605 }
606
607 static int afuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
608                        off_t offset, struct fuse_file_info *fi)
609 {
610         DIR *dp = get_dirp(fi);
611         struct dirent *de;
612         char *real_path = alloca( max_path_out_len(path) );
613         mount_list_t *mount;
614        
615         switch( process_path(path, real_path, 1) )
616         {
617                 case PROC_PATH_FAILED:
618                         return -ENXIO;
619
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);
625                         return 0;
626                         
627                 case PROC_PATH_PROXY_DIR:
628                         seekdir(dp, offset);
629                         while ((de = readdir(dp)) != NULL) {
630                                 struct stat st;
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)))
635                                         break;
636                         }
637
638                         return 0;
639         }
640 }
641
642 static int afuse_releasedir(const char *path, struct fuse_file_info *fi)
643 {
644         DIR *dp = get_dirp(fi);
645         mount_list_t *mount;
646         char *root_name = alloca( strlen(path) );
647
648         char *real_path = alloca( max_path_out_len(path) );
649        
650         switch( process_path(path, real_path, 1) )
651         {
652                 case PROC_PATH_FAILED:
653                         return -ENXIO;
654
655                 case PROC_PATH_ROOT_DIR:
656                         return 0;
657                         
658                 case PROC_PATH_PROXY_DIR:
659                         extract_root_name(path, root_name);
660                         mount = find_mount(root_name);
661                         if(mount)
662                                 remove_dir(&mount->dir_list, dp);
663                         
664                         closedir(dp);
665         
666                         return 0;
667         }
668 }
669
670 static int afuse_mknod(const char *path, mode_t mode, dev_t rdev)
671 {
672         int res;
673         char *real_path = alloca( max_path_out_len(path) );
674
675         fprintf(stderr, "> Mknod\n");
676
677         switch( process_path(path, real_path, 0) )
678         {
679                 case PROC_PATH_FAILED:
680                         return -ENXIO;
681
682                 case PROC_PATH_ROOT_DIR:
683                         return -ENOTSUP;
684                         
685                 case PROC_PATH_PROXY_DIR:
686                         if (S_ISFIFO(mode))
687                                 res = mkfifo(real_path, mode);
688                         else
689                                 res = mknod(real_path, mode, rdev);
690                         if (res == -1)
691                                 return -errno;
692
693                         return 0;
694         }
695 }
696
697 static int afuse_mkdir(const char *path, mode_t mode)
698 {
699         int res;
700         char *real_path = alloca( max_path_out_len(path) );
701         
702         switch( process_path(path, real_path, 0) )
703         {
704                 case PROC_PATH_FAILED:
705                         return -ENXIO;
706
707                 case PROC_PATH_ROOT_DIR:
708                         return -ENOTSUP;
709                         
710                 case PROC_PATH_PROXY_DIR:
711                         res = mkdir(real_path, mode);
712                         if (res == -1)
713                                 return -errno;
714
715                         return 0;
716         }
717 }
718
719 static int afuse_unlink(const char *path)
720 {
721         int res;
722         char *real_path = alloca( max_path_out_len(path) );
723
724         switch( process_path(path, real_path, 0) )
725         {
726                 case PROC_PATH_FAILED:
727                         return -ENXIO;
728
729                 case PROC_PATH_ROOT_DIR:
730                         return -ENOTSUP;
731                         
732                 case PROC_PATH_PROXY_DIR:
733                         res = unlink(real_path);
734                         if (res == -1)
735                                 return -errno;
736
737                         return 0;
738         }
739 }
740
741 static int afuse_rmdir(const char *path)
742 {
743         int res;
744         char *real_path = alloca( max_path_out_len(path) );
745         
746         switch( process_path(path, real_path, 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                         res = rmdir(real_path);
756                         if (res == -1)
757                                 return -errno;
758
759                         return 0;
760         }
761 }
762
763 static int afuse_symlink(const char *from, const char *to)
764 {
765         int res;
766         char *real_to_path = alloca( max_path_out_len(to) );
767         
768         switch( process_path(to, real_to_path, 0) )
769         {
770                 case PROC_PATH_FAILED:
771                         return -ENXIO;
772
773                 case PROC_PATH_ROOT_DIR:
774                         return -ENOTSUP;
775                         
776                 case PROC_PATH_PROXY_DIR:
777                         res = symlink(from, real_to_path);
778                         if (res == -1)
779                                 return -errno;
780
781                         return 0;
782         }
783 }
784
785 static int afuse_rename(const char *from, const char *to)
786 {
787         int res;
788         char *real_from_path = alloca( max_path_out_len(from) );
789         char *real_to_path = alloca( max_path_out_len(to) );
790         
791         switch( process_path(from, real_from_path, 0) )
792         {
793                 case PROC_PATH_FAILED:
794                         return -ENXIO;
795
796                 case PROC_PATH_ROOT_DIR:
797                         return -ENOTSUP;
798                         
799                 case PROC_PATH_PROXY_DIR:
800                         switch( process_path(to, real_to_path, 0) )
801                         {
802                                 case PROC_PATH_FAILED:
803                                         return -ENXIO;
804
805                                 case PROC_PATH_ROOT_DIR:
806                                         return -ENOTSUP;
807                                         
808                                 case PROC_PATH_PROXY_DIR:
809                                         res = rename(real_from_path, real_to_path);
810                                         if (res == -1)
811                                                 return -errno;
812
813                                         return 0;
814                         }
815         }
816 }
817
818 static int afuse_link(const char *from, const char *to)
819 {
820         int res;
821         char *real_from_path = alloca( max_path_out_len(from) );
822         char *real_to_path = alloca( max_path_out_len(to) );
823         
824         switch( process_path(from, real_from_path, 0) )
825         {
826                 case PROC_PATH_FAILED:
827                         return -ENXIO;
828
829                 case PROC_PATH_ROOT_DIR:
830                         return -ENOTSUP;
831                         
832                 case PROC_PATH_PROXY_DIR:
833                         switch( process_path(to, real_to_path, 0) )
834                         {
835                                 case PROC_PATH_FAILED:
836                                         return -ENXIO;
837
838                                 case PROC_PATH_ROOT_DIR:
839                                         return -ENOTSUP;
840                                         
841                                 case PROC_PATH_PROXY_DIR:
842                                         res = link(real_from_path, real_to_path);
843                                         if (res == -1)
844                                                 return -errno;
845
846                                         return 0;
847                         }
848         }
849 }
850
851 static int afuse_chmod(const char *path, mode_t mode)
852 {
853         int res;
854         char *real_path = alloca( max_path_out_len(path) );
855         
856         switch( process_path(path, real_path, 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 = chmod(real_path, mode);
866                         if (res == -1)
867                                 return -errno;
868
869                         return 0;
870         }
871 }
872
873 static int afuse_chown(const char *path, uid_t uid, gid_t gid)
874 {
875         int res;
876         char *real_path = alloca( max_path_out_len(path) );
877         
878         switch( process_path(path, real_path, 0) )
879         {
880                 case PROC_PATH_FAILED:
881                         return -ENXIO;
882
883                 case PROC_PATH_ROOT_DIR:
884                         return -ENOTSUP;
885                         
886                 case PROC_PATH_PROXY_DIR:
887                         res = lchown(real_path, uid, gid);
888                         if (res == -1)
889                                 return -errno;
890
891                         return 0;
892         }
893 }
894
895 static int afuse_truncate(const char *path, off_t size)
896 {
897         int res;
898         char *real_path = alloca( max_path_out_len(path) );
899         
900         switch( process_path(path, real_path, 0) )
901         {
902                 case PROC_PATH_FAILED:
903                         return -ENXIO;
904
905                 case PROC_PATH_ROOT_DIR:
906                         return -ENOTSUP;
907                         
908                 case PROC_PATH_PROXY_DIR:
909                         res = truncate(real_path, size);
910                         if (res == -1)
911                                 return -errno;
912
913                         return 0;
914         }
915 }
916
917
918 static int afuse_utime(const char *path, struct utimbuf *buf)
919 {
920         int res;
921         char *real_path = alloca( max_path_out_len(path) );
922         
923         switch( process_path(path, real_path, 0) )
924         {
925                 case PROC_PATH_FAILED:
926                         return -ENXIO;
927
928                 case PROC_PATH_ROOT_DIR:
929                         return -ENOTSUP;
930                         
931                 case PROC_PATH_PROXY_DIR:
932                         res = utime(real_path, buf);
933                         if (res == -1)
934                                 return -errno;
935
936                         return 0;
937         }
938 }
939
940
941 static int afuse_open(const char *path, struct fuse_file_info *fi)
942 {
943         int fd;
944         char *root_name = alloca( strlen(path) );
945         mount_list_t *mount;
946         char *real_path = alloca( max_path_out_len(path) );
947
948         switch( process_path(path, real_path, 1) )
949         {
950                 case PROC_PATH_FAILED:
951                         return -ENXIO;
952
953                 case PROC_PATH_ROOT_DIR:
954                         return -ENOENT;
955                         
956                 case PROC_PATH_PROXY_DIR:
957                         fd = open(real_path, fi->flags);
958                         if (fd == -1)
959                                 return -errno;
960
961                         fi->fh = fd;
962                         extract_root_name(path, root_name);
963                         mount = find_mount(root_name);
964                         if(mount)
965                                 add_fd(&mount->fd_list, fd);
966                         return 0;
967         }
968 }
969
970 static int afuse_read(const char *path, char *buf, size_t size, off_t offset,
971                       struct fuse_file_info *fi)
972 {
973         int res;
974
975         (void)path;
976         res = pread(fi->fh, buf, size, offset);
977         if (res == -1)
978                 res = -errno;
979
980         return res;
981 }
982
983 static int afuse_write(const char *path, const char *buf, size_t size,
984                      off_t offset, struct fuse_file_info *fi)
985 {
986         int res;
987
988         (void) path;
989         res = pwrite(fi->fh, buf, size, offset);
990         if (res == -1)
991                 res = -errno;
992
993         return res;
994 }
995
996
997 static int afuse_release(const char *path, struct fuse_file_info *fi)
998 {
999         char *root_name = alloca( strlen(path) );
1000         mount_list_t *mount;
1001         
1002         extract_root_name(path, root_name);
1003         mount = find_mount(root_name);
1004         if(mount)
1005                 remove_fd(&mount->fd_list, fi->fh);
1006     
1007         close(fi->fh);
1008
1009         return 0;
1010 }
1011
1012 static int afuse_fsync(const char *path, int isdatasync,
1013                        struct fuse_file_info *fi)
1014 {
1015         int res;
1016         (void) path;
1017
1018         #ifndef HAVE_FDATASYNC
1019         (void) isdatasync;
1020         #else
1021         if (isdatasync)
1022                 res = fdatasync(fi->fh);
1023         else
1024         #endif
1025                 res = fsync(fi->fh);
1026         if (res == -1)
1027                 return -errno;
1028
1029         return 0;
1030 }
1031
1032 #if FUSE_VERSION >= 25
1033 static int afuse_access(const char *path, int mask)
1034 {
1035         int res;
1036         char *real_path = alloca( max_path_out_len(path) );
1037
1038         switch( process_path(path, real_path, 1) )
1039         {
1040                 case PROC_PATH_FAILED:
1041                         return -ENXIO;
1042
1043                 case PROC_PATH_ROOT_DIR:
1044                 case PROC_PATH_PROXY_DIR:
1045                         res = access(real_path, mask);
1046                         if (res == -1)
1047                                 return -errno;
1048
1049                         return 0;
1050         }
1051 }
1052
1053 static int afuse_ftruncate(const char *path, off_t size,
1054                            struct fuse_file_info *fi)
1055 {
1056         int res;
1057
1058         (void) path;
1059
1060         res = ftruncate(fi->fh, size);
1061         if (res == -1)
1062                 return -errno;
1063
1064         return 0;
1065 }
1066
1067 static int afuse_create(const char *path, mode_t mode, struct fuse_file_info *fi)
1068 {
1069         int fd;
1070         char *real_path = alloca( max_path_out_len(path) );
1071
1072         switch( process_path(path, real_path, 0) )
1073         {
1074                 case PROC_PATH_FAILED:
1075                         return -ENXIO;
1076
1077                 case PROC_PATH_ROOT_DIR:
1078                         return -ENOTSUP;
1079                         
1080                 case PROC_PATH_PROXY_DIR:
1081                         fd = open(real_path, fi->flags, mode);
1082                         if (fd == -1)
1083                                 return -errno;
1084
1085                         fi->fh = fd;
1086                         return 0;
1087         }
1088 }
1089
1090 static int afuse_fgetattr(const char *path, struct stat *stbuf,
1091                           struct fuse_file_info *fi)
1092 {
1093         int res;
1094
1095         (void) path;
1096
1097         res = fstat(fi->fh, stbuf);
1098         if (res == -1)
1099                 return -errno;
1100
1101         return 0;
1102 }
1103 #endif
1104
1105
1106 #if FUSE_VERSION >= 25
1107 static int afuse_statfs(const char *path, struct statvfs *stbuf)
1108 #else
1109 static int afuse_statfs(const char *path, struct statfs *stbuf)
1110 #endif
1111 {
1112         int res;
1113         char *real_path = alloca( max_path_out_len(path) );
1114
1115         switch( process_path(path, real_path, 1) )
1116         {
1117                 case PROC_PATH_FAILED:
1118                         return -ENXIO;
1119
1120                 case PROC_PATH_ROOT_DIR:
1121 #if FUSE_VERSION >= 25
1122                         stbuf->f_namemax = 0x7fffffff;
1123                         stbuf->f_frsize  = 512;
1124 #else
1125                         stbuf->f_namelen = 0x7fffffff;
1126 #endif
1127                         stbuf->f_bsize   = 1024;
1128                         stbuf->f_blocks  = 0;
1129                         stbuf->f_bfree   = 0;
1130                         stbuf->f_bavail  = 0;
1131                         stbuf->f_files   = 0;
1132                         stbuf->f_ffree   = 0;
1133                         return 0;
1134                 
1135                 case PROC_PATH_PROXY_DIR:
1136                         res = statvfs(real_path, stbuf);
1137                         if (res == -1)
1138                                 return -errno;
1139
1140                         return 0;
1141         }
1142 }
1143
1144 void afuse_destroy(void *p)
1145 {
1146         shutdown();
1147 }
1148
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)
1153 {
1154         int res;
1155         char *real_path = alloca( max_path_out_len(path) );
1156         
1157         switch( process_path(path, real_path, 0) )
1158         {
1159                 case PROC_PATH_FAILED:
1160                         return -ENXIO;
1161
1162                 case PROC_PATH_ROOT_DIR:
1163                         return -ENOENT;
1164                         
1165                 case PROC_PATH_PROXY_DIR:
1166                         res = lsetxattr(real_path, name, value, size, flags);
1167                         if (res == -1)
1168                                 return -errno;
1169                         return 0;
1170         }
1171 }
1172
1173 static int afuse_getxattr(const char *path, const char *name, char *value,
1174                     size_t size)
1175 {
1176         int res;
1177         char *real_path = alloca( max_path_out_len(path) );
1178
1179         switch( process_path(path, real_path, 1) )
1180         {
1181                 case PROC_PATH_FAILED:
1182                         return -ENXIO;
1183
1184                 case PROC_PATH_ROOT_DIR:
1185                         return -ENOTSUP;
1186                         
1187                 case PROC_PATH_PROXY_DIR:
1188                         res = lgetxattr(real_path, name, value, size);
1189                         if (res == -1)
1190                                 return -errno;
1191                         return res;
1192         }
1193 }
1194
1195 static int afuse_listxattr(const char *path, char *list, size_t size)
1196 {
1197         int res;
1198         char *real_path = alloca( max_path_out_len(path) );
1199
1200         switch( process_path(path, real_path, 1) )
1201         {
1202                 case PROC_PATH_FAILED:
1203                         return -ENXIO;
1204
1205                 case PROC_PATH_ROOT_DIR:
1206                         return -ENOTSUP;
1207                         
1208                 case PROC_PATH_PROXY_DIR:
1209                         res = llistxattr(real_path, list, size);
1210                         if (res == -1)
1211                                 return -errno;
1212                         return res;
1213         }
1214 }
1215
1216 static int afuse_removexattr(const char *path, const char *name)
1217 {
1218         int res;
1219         char *real_path = alloca( max_path_out_len(path) );
1220         
1221         switch( process_path(path, real_path, 0) )
1222         {
1223                 case PROC_PATH_FAILED:
1224                         return -ENXIO;
1225
1226                 case PROC_PATH_ROOT_DIR:
1227                         return -ENOTSUP;
1228                         
1229                 case PROC_PATH_PROXY_DIR:
1230                         res = lremovexattr(real_path, name);
1231                         if (res == -1)
1232                                 return -errno;
1233                         return 0;
1234         }
1235 }
1236 #endif /* HAVE_SETXATTR */
1237
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,
1250         .link        = afuse_link,
1251         .chmod       = afuse_chmod,
1252         .chown       = afuse_chown,
1253         .truncate    = afuse_truncate,
1254         .utime       = afuse_utime,
1255         .open        = afuse_open,
1256         .read        = afuse_read,
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,
1266 #endif
1267         .destroy     = afuse_destroy,
1268 #ifdef HAVE_SETXATTR
1269         .setxattr    = afuse_setxattr,
1270         .getxattr    = afuse_getxattr,
1271         .listxattr   = afuse_listxattr,
1272         .removexattr = afuse_removexattr,
1273 #endif
1274 };
1275
1276
1277 enum {
1278         KEY_HELP
1279 };
1280
1281 #define AFUSE_OPT(t, p, v) { t, offsetof(struct user_options_t, p), v }
1282
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),
1286
1287         FUSE_OPT_KEY("-h",     KEY_HELP),
1288         FUSE_OPT_KEY("--help", KEY_HELP),
1289     
1290         FUSE_OPT_END
1291 };
1292
1293 static void usage(const char *progname)
1294 {
1295         fprintf(stderr,
1296 "Usage: %s mountpoint [options]\n"
1297 "\n"
1298 "    -o opt,[opt...]        mount options\n"
1299 "    -h   --help            print help\n"
1300 "    -V   --version         print FUSE version information\n"
1301 "\n"
1302 "afuse options:\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"
1305 "\n\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"
1309 "\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"       
1312 "\n", progname);
1313 }
1314
1315 static int afuse_opt_proc(void *data, const char *arg, int key,
1316                           struct fuse_args *outargs)
1317 {
1318         struct user_options_t *user_options = (struct user_options_t *)user_options;
1319         
1320         (void) user_options;
1321         (void) arg;
1322
1323         switch(key)
1324         {
1325                 case KEY_HELP:
1326                         usage(outargs->argv[0]);
1327                         fuse_opt_add_arg(outargs, "-ho");
1328                         fuse_main(outargs->argc, outargs->argv, &afuse_oper);
1329                         exit(1);
1330
1331                 default:
1332                         return 1;
1333         }
1334 }
1335
1336 int main(int argc, char *argv[])
1337 {
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);
1341         
1342         if(fuse_opt_parse(&args, &user_options, afuse_opts, afuse_opt_proc) == -1)
1343                 return 1;
1344         
1345         // !!FIXME!! force single-threading for now as datastructures are not locked
1346         fuse_opt_add_arg(&args, "-s");
1347
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");
1351                 usage(argv[0]);
1352                 fuse_opt_add_arg(&args, "-ho");
1353                 fuse_main(args.argc, args.argv, &afuse_oper);
1354                 
1355                 return 1;
1356         }
1357
1358         if( !(mount_point_directory = mkdtemp(temp_dir_name)) ) {
1359                 fprintf(stderr, "Failed to create temporary mount point dir.\n");
1360                 return 1;
1361         }
1362         
1363         umask(0);
1364         
1365         // Register function to tidy up on exit conditions
1366         if( atexit( shutdown ) ) {
1367                 fprintf(stderr, "Failed to register exit handler.\n");
1368                 return 1;
1369         }
1370
1371         // !!FIXME!! death by signal doesn't unmount fs
1372         return fuse_main(args.argc, args.argv, &afuse_oper);
1373 }