]> CyberLeo.Net >> Repos - SourceForge/afuse.git/blob - src/afuse.c
Refactored some data structures into their own source modules for clarity.
[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 char *expand_template(const char *template, const char *mount_point, const char *root_name)
164 {
165         int len = 0;
166         int i;
167         char *expanded_name;
168         char *expanded_name_start;
169
170         // calculate length
171         for(i = 0; template[i]; i++)
172                 if(template[i] == '%') {
173                         switch(template[i + 1])
174                         {
175                                 case 'm':
176                                         len += strlen(mount_point);
177                                         i++;
178                                         break;
179                                 case 'r':
180                                         len += strlen(root_name);
181                                         i++;
182                                         break;
183                         }
184                 } else
185                         len++;
186         
187         expanded_name_start = expanded_name = my_malloc(len + 1);
188
189         for(i = 0; template[i]; i++)
190                 if(template[i] == '%') {
191                         int j = 0;
192                         switch(template[i + 1])
193                         {
194                                 case 'm':
195                                         while(mount_point[j])
196                                                 *expanded_name++ = mount_point[j++];
197                                         i++;
198                                         break;
199                                 case 'r':
200                                         while(root_name[j])
201                                                 *expanded_name++ = root_name[j++];
202                                         i++;
203                                         break;
204                         }
205                 } else
206                         *expanded_name++ = template[i];
207         
208         *expanded_name = '\0';
209         
210         return expanded_name_start;
211 }
212
213 int do_mount(const char *root_name)
214 {
215         char *mount_point;
216         char *mount_command;
217         mount_list_t *mount;
218         int sysret;
219
220         fprintf(stderr, "Mounting: %s\n", root_name);
221
222         if( !make_mount_point(root_name) ) {
223                 fprintf(stderr, "Failed to create mount point directory: %s/%s\n",
224                         mount_point_directory, root_name);
225                 return 0;
226         }
227                 
228         mount_point = alloca(strlen(mount_point_directory) + 2 + strlen(root_name));
229         sprintf(mount_point, "%s/%s", mount_point_directory, root_name);
230         
231         mount_command = expand_template(user_options.mount_command_template,
232                                         mount_point, root_name);
233         sysret = system(mount_command);
234         free(mount_command);
235
236         fprintf(stderr, "sysret: %.8x\n", sysret);
237
238         if(sysret) {
239                 fprintf(stderr, "Failed to invoke mount command: \"%s\" (%s)\n",
240                         mount_command, sysret != -1 ?
241                                        "Error executing mount" :
242                                        strerror(errno));
243
244                 // remove the now unused directory
245                 if( rmdir(mount_point) == -1 )
246                         fprintf(stderr, "Failed to remove mount point dir: %s (%s)",
247                                 mount_point, strerror(errno));
248
249                 return 0;
250         }
251
252         add_mount(root_name);
253
254         return 1;
255 }
256
257 int do_umount(const char *root_name)
258 {
259         char *mount_point;
260         char *unmount_command;
261         mount_list_t *mount;
262         int sysret;
263
264         fprintf(stderr, "Unmounting: %s\n", root_name);
265                 
266         mount = find_mount(root_name);
267         if(!mount) {
268                 fprintf(stderr, "Internal Error: tried to unmount non-existant mount point: %s\n", root_name);
269                 return 1;
270         }
271         
272         mount_point = alloca(strlen(mount_point_directory) + 2 + strlen(root_name));
273         sprintf(mount_point, "%s/%s", mount_point_directory, root_name);
274
275         unmount_command = expand_template(user_options.unmount_command_template,
276                                           mount_point, root_name);
277         sysret = system(unmount_command);
278         free(unmount_command);
279         if(sysret) {
280                 fprintf(stderr, "Failed to invoke unmount command: \"%s\" (%s)\n",
281                         unmount_command, sysret != -1 ?
282                                        "Error executing mount" :
283                                        strerror(errno));
284                 return 0;
285         }
286         
287         // tidy up after succesful unmount
288         remove_mount(root_name);
289         if( rmdir(mount_point) == -1 )
290                 fprintf(stderr, "Failed to remove mount point dir: %s (%s)",
291                         mount_point, strerror(errno));
292
293         return 1;
294 }
295
296 void unmount_all(void)
297 {
298         fprintf(stderr, "Attemping to unmount all filesystems:\n");
299         
300         while(mount_list) {
301                 fprintf(stderr, "\tUnmounting: %s\n", mount_list->root_name);
302                 
303                 // if unmount fails, ditch the mount anyway
304                 if( !do_umount(mount_list->root_name) )
305                         remove_mount(mount_list->root_name);
306         }
307         
308         fprintf(stderr, "done.\n");
309 }
310
311 void shutdown(void)
312 {
313         unmount_all();
314
315         if(rmdir(mount_point_directory) == -1)
316                 fprintf(stderr, "Failed to remove temporary mount point directory: %s (%s)\n",
317                         mount_point_directory, strerror(errno));
318 }
319
320 int max_path_out_len(const char *path_in)
321 {
322         return strlen(mount_point_directory) + strlen(path_in) + 2;
323 }
324
325 // returns true if path is the a child directory of a root node
326 // e.g. /a/b is a child, /a is not.
327 int extract_root_name(const char *path, char *root_name)
328 {
329         int i;
330         int is_child;
331         
332         for(i = 1; path[i] && path[i] != '/'; i++)
333                 root_name[i - 1] = path[i];
334         root_name[i - 1] = '\0';
335
336         return strlen(&path[i]);
337 }
338
339 typedef enum {PROC_PATH_FAILED, PROC_PATH_ROOT_DIR, PROC_PATH_PROXY_DIR} proc_result_t;
340
341 proc_result_t process_path(const char *path_in, char *path_out, char *root_name, int attempt_mount)
342 {
343         int i;
344         char *path_out_base;
345         int is_child;
346
347         fprintf(stderr, "Path in: %s\n", path_in);
348         is_child = extract_root_name(path_in, root_name);
349         fprintf(stderr, "root_name is: %s\n", root_name);
350         
351         // Mount filesystem if neccessary
352         // the combination of is_child and attempt_mount prevent inappropriate
353         // mounting of a filesystem for example if the user tries to mknod
354         // in the afuse root this should cause an error not a mount.
355         // !!FIXME!! this is broken on FUSE < 2.5 (?) because a getattr 
356         // on the root node seems to occur with every single access.
357         if( //(is_child || attempt_mount ) && 
358             strlen(root_name) > 0        &&
359             !is_mount(root_name)         &&
360             !do_mount(root_name))
361                 return PROC_PATH_FAILED;
362
363         // construct path_out (mount_point_directory + '/' + path_in + '\0')
364         path_out_base = path_out;
365         for(i = 0; i < strlen(mount_point_directory); i++)
366                 *path_out++ = mount_point_directory[i];
367         *path_out++ = '/';
368         for(i = 0; i < strlen(path_in) - 1; i++)
369                 *path_out++ = path_in[i + 1];
370         *path_out = '\0';
371         fprintf(stderr, "Path out: %s\n", path_out_base);
372         
373         return strlen(root_name) ? PROC_PATH_PROXY_DIR : PROC_PATH_ROOT_DIR;
374 }
375
376 // Calling this function unmounts a dir if the user has specified to umount FS'
377 // not inuse and if there are no file or directory handles open. The
378 // update_operation flag combined with the mount_inuse_only_all user option
379 // allows unmounting only after destructive operations.
380 void consider_umount(const char *root_name, bool update_operation)
381 {
382         fprintf(stderr, "Considering unmount of: %s\n", root_name);
383         if((user_options.mount_inuse_only && update_operation) ||
384             user_options.mount_inuse_only_all) {
385                 mount_list_t *mount = find_mount(root_name);
386                 if(mount &&
387                    dir_list_empty(mount->dir_list) && 
388                    fd_list_empty(mount->fd_list)) {
389                         fprintf(stderr, "Decided to umount\n", root_name);
390                         do_umount(root_name);
391                 }
392         } else {
393                 fprintf(stderr, "No user option\n", root_name);
394         }
395 }
396
397 static int afuse_getattr(const char *path, struct stat *stbuf)
398 {
399         int res;
400         char *root_name = alloca( strlen(path) );
401         char *real_path = alloca( max_path_out_len(path) );
402
403         fprintf(stderr, "> GetAttr\n");
404
405         switch( process_path(path, real_path, root_name, 0) )
406         {
407                 case PROC_PATH_FAILED:
408                         return -ENXIO;
409
410                 case PROC_PATH_ROOT_DIR:
411                         extract_root_name(path, root_name);
412                         fprintf(stderr, "Getattr on: (%s) - %s\n", path, root_name);
413                         if( is_mount(root_name) || strlen(root_name) == 0) {
414                                 stbuf->st_mode    = S_IFDIR | 0700;
415                                 stbuf->st_nlink   = 1;
416                                 stbuf->st_uid     = getuid();
417                                 stbuf->st_gid     = getgid();
418                                 stbuf->st_size    = 0;
419                                 stbuf->st_blksize = 0;
420                                 stbuf->st_blocks  = 0;
421                                 stbuf->st_atime   = 0;
422                                 stbuf->st_mtime   = 0;
423                                 stbuf->st_ctime   = 0;
424                                                        
425                                 return 0;
426                         } else
427                                 return -ENOENT;
428                         
429                 case PROC_PATH_PROXY_DIR:
430                         res = lstat(real_path, stbuf);
431                         consider_umount(root_name, false);
432                         if (res == -1)
433                                 return -errno;
434
435                         return 0;
436         }
437 }
438
439
440 static int afuse_readlink(const char *path, char *buf, size_t size)
441 {
442         int res;
443         char *root_name = alloca( strlen(path) );
444         char *real_path = alloca( max_path_out_len(path) );
445
446         switch( process_path(path, real_path, root_name, 1) )
447         {
448                 case PROC_PATH_FAILED:
449                         return -ENXIO;
450
451                 case PROC_PATH_ROOT_DIR:
452                         return -ENOENT;
453                         
454                 case PROC_PATH_PROXY_DIR:
455                         res = readlink(real_path, buf, size - 1);
456                         consider_umount(root_name, false);
457                         if (res == -1)
458                                 return -errno;
459
460                         buf[res] = '\0';
461                         return 0;
462         }
463 }
464
465 static int afuse_opendir(const char *path, struct fuse_file_info *fi)
466 {
467         DIR *dp;
468         char *root_name = alloca( strlen(path) );
469         mount_list_t *mount;
470         char *real_path = alloca( max_path_out_len(path) );
471        
472         switch( process_path(path, real_path, root_name, 1) )
473         {
474                 case PROC_PATH_FAILED:
475                         return -ENXIO;
476
477                 case PROC_PATH_ROOT_DIR:
478                         return 0;
479                         
480                 case PROC_PATH_PROXY_DIR:
481                         dp = opendir(real_path);
482
483                         if (dp == NULL) {
484                                 consider_umount(root_name, false);
485                                 return -errno;
486                         }
487
488                         fi->fh = (unsigned long) dp;
489                         mount = find_mount(root_name);
490                         if(mount)
491                                 dir_list_add(&mount->dir_list, dp);
492                         return 0;
493         }
494 }
495
496 static inline DIR *get_dirp(struct fuse_file_info *fi)
497 {
498         return (DIR *) (uintptr_t) fi->fh;
499 }
500
501 static int afuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
502                        off_t offset, struct fuse_file_info *fi)
503 {
504         DIR *dp = get_dirp(fi);
505         struct dirent *de;
506         char *root_name = alloca( strlen(path) );
507         char *real_path = alloca( max_path_out_len(path) );
508         mount_list_t *mount;
509        
510         switch( process_path(path, real_path, root_name, 1) )
511         {
512                 case PROC_PATH_FAILED:
513                         return -ENXIO;
514
515                 case PROC_PATH_ROOT_DIR:
516                         filler(buf, ".", NULL, 0);
517                         filler(buf, "..", NULL, 0);
518                         for(mount = mount_list; mount; mount = mount->next)
519                                 filler(buf, mount->root_name, NULL, 0);
520                         return 0;
521                         
522                 case PROC_PATH_PROXY_DIR:
523                         seekdir(dp, offset);
524                         while ((de = readdir(dp)) != NULL) {
525                                 struct stat st;
526                                 memset(&st, 0, sizeof(st));
527                                 st.st_ino = de->d_ino;
528                                 st.st_mode = de->d_type << 12;
529                                 if (filler(buf, de->d_name, &st, telldir(dp)))
530                                         break;
531                         }
532                         consider_umount(root_name, false);
533
534                         return 0;
535         }
536 }
537
538 static int afuse_releasedir(const char *path, struct fuse_file_info *fi)
539 {
540         DIR *dp = get_dirp(fi);
541         mount_list_t *mount;
542         char *root_name = alloca( strlen(path) );
543         char *real_path = alloca( max_path_out_len(path) );
544        
545         switch( process_path(path, real_path, root_name, 1) )
546         {
547                 case PROC_PATH_FAILED:
548                         return -ENXIO;
549
550                 case PROC_PATH_ROOT_DIR:
551                         return 0;
552                         
553                 case PROC_PATH_PROXY_DIR:
554                         extract_root_name(path, root_name);
555                         mount = find_mount(root_name);
556                         if(mount)
557                                 dir_list_remove(&mount->dir_list, dp);
558                         
559                         closedir(dp);
560         
561                         consider_umount(root_name, false);
562                         
563                         return 0;
564         }
565 }
566
567 static int afuse_mknod(const char *path, mode_t mode, dev_t rdev)
568 {
569         int res;
570         char *root_name = alloca( strlen(path) );
571         char *real_path = alloca( max_path_out_len(path) );
572
573         fprintf(stderr, "> Mknod\n");
574
575         switch( process_path(path, real_path, root_name, 0) )
576         {
577                 case PROC_PATH_FAILED:
578                         return -ENXIO;
579
580                 case PROC_PATH_ROOT_DIR:
581                         return -ENOTSUP;
582                         
583                 case PROC_PATH_PROXY_DIR:
584                         if (S_ISFIFO(mode))
585                                 res = mkfifo(real_path, mode);
586                         else
587                                 res = mknod(real_path, mode, rdev);
588                         consider_umount(root_name, true);
589                         if (res == -1)
590                                 return -errno;
591
592                         return 0;
593         }
594 }
595
596 static int afuse_mkdir(const char *path, mode_t mode)
597 {
598         int res;
599         char *root_name = alloca( strlen(path) );
600         char *real_path = alloca( max_path_out_len(path) );
601         
602         switch( process_path(path, real_path, root_name, 0) )
603         {
604                 case PROC_PATH_FAILED:
605                         return -ENXIO;
606
607                 case PROC_PATH_ROOT_DIR:
608                         return -ENOTSUP;
609                         
610                 case PROC_PATH_PROXY_DIR:
611                         res = mkdir(real_path, mode);
612                         consider_umount(root_name, true);
613                         if (res == -1)
614                                 return -errno;
615
616                         return 0;
617         }
618 }
619
620 static int afuse_unlink(const char *path)
621 {
622         int res;
623         char *root_name = alloca( strlen(path) );
624         char *real_path = alloca( max_path_out_len(path) );
625
626         switch( process_path(path, real_path, root_name, 0) )
627         {
628                 case PROC_PATH_FAILED:
629                         return -ENXIO;
630
631                 case PROC_PATH_ROOT_DIR:
632                         return -ENOTSUP;
633                         
634                 case PROC_PATH_PROXY_DIR:
635                         res = unlink(real_path);
636                         consider_umount(root_name, true);
637                         if (res == -1)
638                                 return -errno;
639
640                         return 0;
641         }
642 }
643
644 static int afuse_rmdir(const char *path)
645 {
646         int res;
647         char *root_name = alloca( strlen(path) );
648         char *real_path = alloca( max_path_out_len(path) );
649         
650         switch( process_path(path, real_path, root_name, 0) )
651         {
652                 case PROC_PATH_FAILED:
653                         return -ENXIO;
654
655                 case PROC_PATH_ROOT_DIR:
656                         return -ENOTSUP;
657                         
658                 case PROC_PATH_PROXY_DIR:
659                         res = rmdir(real_path);
660                         consider_umount(root_name, true);
661                         if (res == -1)
662                                 return -errno;
663
664                         return 0;
665         }
666 }
667
668 static int afuse_symlink(const char *from, const char *to)
669 {
670         int res;
671         char *root_name_to = alloca( strlen(to) );
672         char *real_to_path = alloca( max_path_out_len(to) );
673         
674         switch( process_path(to, real_to_path, root_name_to, 0) )
675         {
676                 case PROC_PATH_FAILED:
677                         return -ENXIO;
678
679                 case PROC_PATH_ROOT_DIR:
680                         return -ENOTSUP;
681                         
682                 case PROC_PATH_PROXY_DIR:
683                         res = symlink(from, real_to_path);
684                         consider_umount(root_name_to, true);
685                         if (res == -1)
686                                 return -errno;
687
688                         return 0;
689         }
690 }
691
692 static int afuse_rename(const char *from, const char *to)
693 {
694         int res;
695         char *root_name_from = alloca( strlen(from) );
696         char *root_name_to = alloca( strlen(to) );
697         char *real_from_path = alloca( max_path_out_len(from) );
698         char *real_to_path = alloca( max_path_out_len(to) );
699         
700         switch( process_path(from, real_from_path, root_name_from, 0) )
701         {
702                 case PROC_PATH_FAILED:
703                         return -ENXIO;
704
705                 case PROC_PATH_ROOT_DIR:
706                         return -ENOTSUP;
707                         
708                 case PROC_PATH_PROXY_DIR:
709                         switch( process_path(to, real_to_path, root_name_to, 0) )
710                         {
711                                 case PROC_PATH_FAILED:
712                                         consider_umount(root_name_from, false);
713                                         return -ENXIO;
714
715                                 case PROC_PATH_ROOT_DIR:
716                                         consider_umount(root_name_from, false);
717                                         return -ENOTSUP;
718                                         
719                                 case PROC_PATH_PROXY_DIR:
720                                         res = rename(real_from_path, real_to_path);
721                                         consider_umount(root_name_from, true);
722                                         consider_umount(root_name_to, true);
723                                         if (res == -1)
724                                                 return -errno;
725
726                                         return 0;
727                         }
728         }
729 }
730
731 static int afuse_link(const char *from, const char *to)
732 {
733         int res;
734         char *root_name_from = alloca( strlen(from) );
735         char *root_name_to = alloca( strlen(to) );
736         char *real_from_path = alloca( max_path_out_len(from) );
737         char *real_to_path = alloca( max_path_out_len(to) );
738         
739         switch( process_path(from, real_from_path, root_name_from, 0) )
740         {
741                 case PROC_PATH_FAILED:
742                         return -ENXIO;
743
744                 case PROC_PATH_ROOT_DIR:
745                         return -ENOTSUP;
746                         
747                 case PROC_PATH_PROXY_DIR:
748                         switch( process_path(to, real_to_path, root_name_to, 0) )
749                         {
750                                 case PROC_PATH_FAILED:
751                                         consider_umount(root_name_from, false);
752                                         return -ENXIO;
753
754                                 case PROC_PATH_ROOT_DIR:
755                                         consider_umount(root_name_from, false);
756                                         return -ENOTSUP;
757                                         
758                                 case PROC_PATH_PROXY_DIR:
759                                         res = link(real_from_path, real_to_path);
760                                         consider_umount(root_name_from, true);
761                                         consider_umount(root_name_to, true);
762                                         if (res == -1)
763                                                 return -errno;
764
765                                         return 0;
766                         }
767         }
768 }
769
770 static int afuse_chmod(const char *path, mode_t mode)
771 {
772         int res;
773         char *root_name = alloca( strlen(path) );
774         char *real_path = alloca( max_path_out_len(path) );
775         
776         switch( process_path(path, real_path, root_name, 0) )
777         {
778                 case PROC_PATH_FAILED:
779                         return -ENXIO;
780
781                 case PROC_PATH_ROOT_DIR:
782                         return -ENOTSUP;
783                         
784                 case PROC_PATH_PROXY_DIR:
785                         res = chmod(real_path, mode);
786                         consider_umount(root_name, true);
787                         if (res == -1)
788                                 return -errno;
789
790                         return 0;
791         }
792 }
793
794 static int afuse_chown(const char *path, uid_t uid, gid_t gid)
795 {
796         int res;
797         char *root_name = alloca( strlen(path) );
798         char *real_path = alloca( max_path_out_len(path) );
799         
800         switch( process_path(path, real_path, root_name, 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 = lchown(real_path, uid, gid);
810                         consider_umount(root_name, true);
811                         if (res == -1)
812                                 return -errno;
813
814                         return 0;
815         }
816 }
817
818 static int afuse_truncate(const char *path, off_t size)
819 {
820         int res;
821         char *root_name = alloca( strlen(path) );
822         char *real_path = alloca( max_path_out_len(path) );
823         
824         switch( process_path(path, real_path, root_name, 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                         res = truncate(real_path, size);
834                         consider_umount(root_name, true);
835                         if (res == -1)
836                                 return -errno;
837
838                         return 0;
839         }
840 }
841
842
843 static int afuse_utime(const char *path, struct utimbuf *buf)
844 {
845         int res;
846         char *root_name = alloca( strlen(path) );
847         char *real_path = alloca( max_path_out_len(path) );
848         
849         switch( process_path(path, real_path, root_name, 0) )
850         {
851                 case PROC_PATH_FAILED:
852                         return -ENXIO;
853
854                 case PROC_PATH_ROOT_DIR:
855                         return -ENOTSUP;
856                         
857                 case PROC_PATH_PROXY_DIR:
858                         res = utime(real_path, buf);
859                         consider_umount(root_name, true);
860                         if (res == -1)
861                                 return -errno;
862
863                         return 0;
864         }
865 }
866
867
868 static int afuse_open(const char *path, struct fuse_file_info *fi)
869 {
870         int fd;
871         char *root_name = alloca( strlen(path) );
872         mount_list_t *mount;
873         char *real_path = alloca( max_path_out_len(path) );
874
875         switch( process_path(path, real_path, root_name, 1) )
876         {
877                 case PROC_PATH_FAILED:
878                         return -ENXIO;
879
880                 case PROC_PATH_ROOT_DIR:
881                         return -ENOENT;
882                         
883                 case PROC_PATH_PROXY_DIR:
884                         fd = open(real_path, fi->flags);
885                         if (fd == -1) {
886                                 consider_umount(root_name, true);
887                                 return -errno;
888                         }
889
890                         fi->fh = fd;
891                         extract_root_name(path, root_name);
892                         mount = find_mount(root_name);
893                         if(mount)
894                                 fd_list_add(&mount->fd_list, fd);
895                         return 0;
896         }
897 }
898
899 static int afuse_read(const char *path, char *buf, size_t size, off_t offset,
900                       struct fuse_file_info *fi)
901 {
902         int res;
903
904         (void)path;
905         res = pread(fi->fh, buf, size, offset);
906         if (res == -1)
907                 res = -errno;
908
909         return res;
910 }
911
912 static int afuse_write(const char *path, const char *buf, size_t size,
913                      off_t offset, struct fuse_file_info *fi)
914 {
915         int res;
916
917         (void) path;
918         res = pwrite(fi->fh, buf, size, offset);
919         if (res == -1)
920                 res = -errno;
921
922         if(user_options.flush_writes)
923                 fsync(fi->fh);
924
925         return res;
926 }
927
928
929 static int afuse_release(const char *path, struct fuse_file_info *fi)
930 {
931         char *root_name = alloca( strlen(path) );
932         mount_list_t *mount;
933         
934         extract_root_name(path, root_name);
935         mount = find_mount(root_name);
936         if(mount)
937                 fd_list_remove(&mount->fd_list, fi->fh);
938     
939         close(fi->fh);
940         consider_umount(root_name, true);
941
942
943         return 0;
944 }
945
946 static int afuse_fsync(const char *path, int isdatasync,
947                        struct fuse_file_info *fi)
948 {
949         int res;
950         (void) path;
951
952         #ifndef HAVE_FDATASYNC
953         (void) isdatasync;
954         #else
955         if (isdatasync)
956                 res = fdatasync(fi->fh);
957         else
958         #endif
959                 res = fsync(fi->fh);
960         if (res == -1)
961                 return -errno;
962
963         return 0;
964 }
965
966 #if FUSE_VERSION >= 25
967 static int afuse_access(const char *path, int mask)
968 {
969         int res;
970         char *root_name = alloca( strlen(path) );
971         char *real_path = alloca( max_path_out_len(path) );
972
973         switch( process_path(path, real_path, root_name, 1) )
974         {
975                 case PROC_PATH_FAILED:
976                         return -ENXIO;
977
978                 case PROC_PATH_ROOT_DIR:
979                 case PROC_PATH_PROXY_DIR:
980                         res = access(real_path, mask);
981                         consider_umount(root_name, false);
982                         if (res == -1)
983                                 return -errno;
984
985                         return 0;
986         }
987 }
988
989 static int afuse_ftruncate(const char *path, off_t size,
990                            struct fuse_file_info *fi)
991 {
992         int res;
993
994         (void) path;
995
996         res = ftruncate(fi->fh, size);
997         if (res == -1)
998                 return -errno;
999
1000         return 0;
1001 }
1002
1003 static int afuse_create(const char *path, mode_t mode, struct fuse_file_info *fi)
1004 {
1005         int fd;
1006         char *root_name = alloca( strlen(path) );
1007         char *real_path = alloca( max_path_out_len(path) );
1008
1009         switch( process_path(path, real_path, root_name, 0) )
1010         {
1011                 case PROC_PATH_FAILED:
1012                         return -ENXIO;
1013
1014                 case PROC_PATH_ROOT_DIR:
1015                         return -ENOTSUP;
1016                         
1017                 case PROC_PATH_PROXY_DIR:
1018                         fd = open(real_path, fi->flags, mode);
1019                         consider_umount(root_name, true);
1020                         if (fd == -1)
1021                                 return -errno;
1022
1023                         fi->fh = fd;
1024                         return 0;
1025         }
1026 }
1027
1028 static int afuse_fgetattr(const char *path, struct stat *stbuf,
1029                           struct fuse_file_info *fi)
1030 {
1031         int res;
1032
1033         (void) path;
1034
1035         res = fstat(fi->fh, stbuf);
1036         if (res == -1)
1037                 return -errno;
1038
1039         return 0;
1040 }
1041 #endif
1042
1043
1044 #if FUSE_VERSION >= 25
1045 static int afuse_statfs(const char *path, struct statvfs *stbuf)
1046 #else
1047 static int afuse_statfs(const char *path, struct statfs *stbuf)
1048 #endif
1049 {
1050         int res;
1051         char *root_name = alloca( strlen(path) );
1052         char *real_path = alloca( max_path_out_len(path) );
1053
1054         switch( process_path(path, real_path, root_name, 1) )
1055         {
1056                 case PROC_PATH_FAILED:
1057                         return -ENXIO;
1058
1059                 case PROC_PATH_ROOT_DIR:
1060 #if FUSE_VERSION >= 25
1061                         stbuf->f_namemax = 0x7fffffff;
1062                         stbuf->f_frsize  = 512;
1063 #else
1064                         stbuf->f_namelen = 0x7fffffff;
1065 #endif
1066                         stbuf->f_bsize   = 1024;
1067                         stbuf->f_blocks  = 0;
1068                         stbuf->f_bfree   = 0;
1069                         stbuf->f_bavail  = 0;
1070                         stbuf->f_files   = 0;
1071                         stbuf->f_ffree   = 0;
1072                         return 0;
1073                 
1074                 case PROC_PATH_PROXY_DIR:
1075                         res = statvfs(real_path, stbuf);
1076                         consider_umount(root_name, false);
1077                         if (res == -1)
1078                                 return -errno;
1079
1080                         return 0;
1081         }
1082 }
1083
1084 void afuse_destroy(void *p)
1085 {
1086         shutdown();
1087 }
1088
1089 #ifdef HAVE_SETXATTR
1090 /* xattr operations are optional and can safely be left unimplemented */
1091 static int afuse_setxattr(const char *path, const char *name, const char *value,
1092                         size_t size, int flags)
1093 {
1094         int res;
1095         char *root_name = alloca( strlen(path) );
1096         char *real_path = alloca( max_path_out_len(path) );
1097         
1098         switch( process_path(path, real_path, root_name, 0) )
1099         {
1100                 case PROC_PATH_FAILED:
1101                         return -ENXIO;
1102
1103                 case PROC_PATH_ROOT_DIR:
1104                         return -ENOENT;
1105                         
1106                 case PROC_PATH_PROXY_DIR:
1107                         res = lsetxattr(real_path, name, value, size, flags);
1108                         consider_umount(root_name, true);
1109                         if (res == -1)
1110                                 return -errno;
1111                         return 0;
1112         }
1113 }
1114
1115 static int afuse_getxattr(const char *path, const char *name, char *value,
1116                     size_t size)
1117 {
1118         int res;
1119         char *root_name = alloca( strlen(path) );
1120         char *real_path = alloca( max_path_out_len(path) );
1121
1122         switch( process_path(path, real_path, root_name, 1) )
1123         {
1124                 case PROC_PATH_FAILED:
1125                         return -ENXIO;
1126
1127                 case PROC_PATH_ROOT_DIR:
1128                         return -ENOTSUP;
1129                         
1130                 case PROC_PATH_PROXY_DIR:
1131                         res = lgetxattr(real_path, name, value, size);
1132                         consider_umount(root_name, false);
1133                         if (res == -1)
1134                                 return -errno;
1135                         return res;
1136         }
1137 }
1138
1139 static int afuse_listxattr(const char *path, char *list, size_t size)
1140 {
1141         int res;
1142         char *root_name = alloca( strlen(path) );
1143         char *real_path = alloca( max_path_out_len(path) );
1144
1145         switch( process_path(path, real_path, root_name, 1) )
1146         {
1147                 case PROC_PATH_FAILED:
1148                         return -ENXIO;
1149
1150                 case PROC_PATH_ROOT_DIR:
1151                         return -ENOTSUP;
1152                         
1153                 case PROC_PATH_PROXY_DIR:
1154                         res = llistxattr(real_path, list, size);
1155                         consider_umount(root_name, false);
1156                         if (res == -1)
1157                                 return -errno;
1158                         return res;
1159         }
1160 }
1161
1162 static int afuse_removexattr(const char *path, const char *name)
1163 {
1164         int res;
1165         char *root_name = alloca( strlen(path) );
1166         char *real_path = alloca( max_path_out_len(path) );
1167         
1168         switch( process_path(path, real_path, root_name, 0) )
1169         {
1170                 case PROC_PATH_FAILED:
1171                         return -ENXIO;
1172
1173                 case PROC_PATH_ROOT_DIR:
1174                         return -ENOTSUP;
1175                         
1176                 case PROC_PATH_PROXY_DIR:
1177                         res = lremovexattr(real_path, name);
1178                         consider_umount(root_name, true);
1179                         if (res == -1)
1180                                 return -errno;
1181                         return 0;
1182         }
1183 }
1184 #endif /* HAVE_SETXATTR */
1185
1186 static struct fuse_operations afuse_oper = {
1187         .getattr     = afuse_getattr,
1188         .readlink    = afuse_readlink,
1189         .opendir     = afuse_opendir,
1190         .readdir     = afuse_readdir,
1191         .releasedir  = afuse_releasedir,
1192         .mknod       = afuse_mknod,
1193         .mkdir       = afuse_mkdir,
1194         .symlink     = afuse_symlink,
1195         .unlink      = afuse_unlink,
1196         .rmdir       = afuse_rmdir,
1197         .rename      = afuse_rename,
1198         .link        = afuse_link,
1199         .chmod       = afuse_chmod,
1200         .chown       = afuse_chown,
1201         .truncate    = afuse_truncate,
1202         .utime       = afuse_utime,
1203         .open        = afuse_open,
1204         .read        = afuse_read,
1205         .write       = afuse_write,
1206         .release     = afuse_release,
1207         .fsync       = afuse_fsync,
1208         .statfs      = afuse_statfs,
1209 #if FUSE_VERSION >= 25
1210         .access      = afuse_access,
1211         .create      = afuse_create,
1212         .ftruncate   = afuse_ftruncate,
1213         .fgetattr    = afuse_fgetattr,
1214 #endif
1215         .destroy     = afuse_destroy,
1216 #ifdef HAVE_SETXATTR
1217         .setxattr    = afuse_setxattr,
1218         .getxattr    = afuse_getxattr,
1219         .listxattr   = afuse_listxattr,
1220         .removexattr = afuse_removexattr,
1221 #endif
1222 };
1223
1224
1225 enum {
1226         KEY_HELP,
1227         KEY_INUSEONLY,
1228         KEY_INUSEONLYALL,
1229         KEY_FLUSHWRITES
1230 };
1231
1232 #define AFUSE_OPT(t, p, v) { t, offsetof(struct user_options_t, p), v }
1233
1234 static struct fuse_opt afuse_opts[] = {
1235         AFUSE_OPT("mount_template=%s", mount_command_template, 0),
1236         AFUSE_OPT("unmount_template=%s", unmount_command_template, 0),
1237
1238         FUSE_OPT_KEY("inuseonly", KEY_INUSEONLY),
1239         FUSE_OPT_KEY("inuseonlyall", KEY_INUSEONLYALL),
1240         FUSE_OPT_KEY("flushwrites", KEY_FLUSHWRITES),
1241
1242         FUSE_OPT_KEY("-h",     KEY_HELP),
1243         FUSE_OPT_KEY("--help", KEY_HELP),
1244     
1245         FUSE_OPT_END
1246 };
1247
1248 static void usage(const char *progname)
1249 {
1250         fprintf(stderr,
1251 "Usage: %s mountpoint [options]\n"
1252 "\n"
1253 "    -o opt,[opt...]        mount options\n"
1254 "    -h   --help            print help\n"
1255 "    -V   --version         print FUSE version information\n"
1256 "\n"
1257 "afuse options:\n"
1258 "    -o mount_template=CMD    template for CMD to execute to mount (*)\n"
1259 "    -o unmount_template=CMD  template for CMD to execute to unmount (*) (**)\n"
1260 "    -o inuseonly             unmount fs after files are closed or dir updates\n"
1261 "    -o inuseonlyall          as inuseonly, but all access end in unmount\n"
1262 "    -o flushwrites           flushes data to disk for all file writes\n"
1263 "\n\n"
1264 " (*) - When executed, %%r and %%m are expanded in templates to the root\n"
1265 "       directory name for the new mount point, and the actual directory to\n"
1266 "       mount onto respectively to mount onto. Both templates are REQUIRED.\n"
1267 "\n"
1268 " (**) - The unmount command must perform a lazy unmount operation. E.g. the\n"
1269 "        -u -z options to fusermount, or -l for regular mount.\n"       
1270 "\n", progname);
1271 }
1272
1273 static int afuse_opt_proc(void *data, const char *arg, int key,
1274                           struct fuse_args *outargs)
1275 {
1276         (void) arg;
1277
1278         switch(key)
1279         {
1280                 case KEY_HELP:
1281                         usage(outargs->argv[0]);
1282                         fuse_opt_add_arg(outargs, "-ho");
1283                         fuse_main(outargs->argc, outargs->argv, &afuse_oper);
1284                         exit(1);
1285
1286                 case KEY_INUSEONLY:
1287                         user_options.mount_inuse_only = true;
1288                         return 0;
1289
1290                 case KEY_INUSEONLYALL:
1291                         user_options.mount_inuse_only_all = true;
1292                         return 0;
1293
1294                 case KEY_FLUSHWRITES:
1295                         user_options.flush_writes = true;
1296                         return 0;
1297
1298                 default:
1299                         return 1;
1300         }
1301 }
1302
1303 int main(int argc, char *argv[])
1304 {
1305         struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1306         char *temp_dir_name = my_malloc(strlen(TMP_DIR_TEMPLATE));
1307         strcpy(temp_dir_name, TMP_DIR_TEMPLATE);
1308         
1309         if(fuse_opt_parse(&args, &user_options, afuse_opts, afuse_opt_proc) == -1)
1310                 return 1;
1311         
1312         // !!FIXME!! force single-threading for now as datastructures are not locked
1313         fuse_opt_add_arg(&args, "-s");
1314
1315         // Check for required parameters
1316         if(!user_options.mount_command_template || !user_options.unmount_command_template) {
1317                 fprintf(stderr, "(Un)Mount command templates missing.\n\n");
1318                 usage(argv[0]);
1319                 fuse_opt_add_arg(&args, "-ho");
1320                 fuse_main(args.argc, args.argv, &afuse_oper);
1321                 
1322                 return 1;
1323         }
1324
1325         if( !(mount_point_directory = mkdtemp(temp_dir_name)) ) {
1326                 fprintf(stderr, "Failed to create temporary mount point dir.\n");
1327                 return 1;
1328         }
1329         
1330         umask(0);
1331         
1332         // Register function to tidy up on exit conditions
1333         if( atexit( shutdown ) ) {
1334                 fprintf(stderr, "Failed to register exit handler.\n");
1335                 return 1;
1336         }
1337
1338         // !!FIXME!! death by signal doesn't unmount fs
1339         return fuse_main(args.argc, args.argv, &afuse_oper);
1340 }