1 /*
2  * Copyright � 2007 Alistair Crooks.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  * 3. The name of the author may not be used to endorse or promote
13  *    products derived from this software without specific prior written
14  *    permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
17  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
22  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 
31 #include <ctype.h>
32 #include <dirent.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <fuse.h>
37 #include <signal.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <time.h>
42 #include <unistd.h>
43 
44 #include "defs.h"
45 
46 #ifndef PREFIX
47 #define PREFIX                ""
48 #endif
49 
50 #ifndef DEF_CONF_FILE
51 #define DEF_CONF_FILE         "/etc/fanoutfs.conf"
52 #endif
53 
54 DEFINE_ARRAY(strv_t, char *);
55 
56 static struct stat   vfs;               /* stat info of directory */
57 static strv_t                  dirs;              /* the directories, in order */
58 static char                   *conffile;          /* configuration file name */
59 static int                     verbose;           /* how chatty are we? */
60 
61 
62 
63 
64 
65 /********************************************************************/
66 
67 static int
readconf(const char * f)68 readconf(const char *f)
69 {
70           char       buf[BUFSIZ];
71           char      *cp;
72           FILE      *fp;
73           int        line;
74 
75           if ((fp = fopen((f) ? f : PREFIX DEF_CONF_FILE, "r")) == NULL) {
76                     warn("can't read configuration file `%s'\n", f);
77                     return 0;
78           }
79           for (line = 1 ;  fgets(buf, sizeof(buf), fp) != NULL ; line += 1) {
80                     buf[strlen(buf) - 1] = 0x0;
81                     for (cp = buf ; *cp && isspace((unsigned)*cp) ; cp++) {
82                     }
83                     if (*cp == '\n' || *cp == 0x0 || *cp == '#') {
84                               continue;
85                     }
86                     ALLOC(char *, dirs.v, dirs.size, dirs.c, 10, 10,
87                               "readconf", exit(EXIT_FAILURE));
88                     dirs.v[dirs.c++] = strdup(cp);
89           }
90           (void) fclose(fp);
91           return 1;
92 }
93 
94 /* yes, this does too much work */
95 static void
sighup(int n)96 sighup(int n)
97 {
98           int       i;
99 
100           printf("Reading configuration file `%s'\n", conffile);
101           for (i = 0 ; i < dirs.c ; i++) {
102                     FREE(dirs.v[i]);
103           }
104           dirs.c = 0;
105           readconf(conffile);
106 }
107 
108 /* find the correct entry in the list of directories */
109 static int
findentry(const char * path,char * name,size_t namesize,struct stat * sp)110 findentry(const char *path, char *name, size_t namesize, struct stat *sp)
111 {
112           struct stat         st;
113           int                 i;
114 
115           if (sp == NULL) {
116                     sp = &st;
117           }
118           for (i = 0 ; i < dirs.c ; i++) {
119                     (void) snprintf(name, namesize, "%s%s", dirs.v[i], path);
120                     if (stat(name, sp) == 0) {
121                               return i;
122                     }
123           }
124           return -1;
125 }
126 
127 /* return 1 if the string `s' is present in the array */
128 static int
present(char * s,strv_t * sp)129 present(char *s, strv_t *sp)
130 {
131           int       i;
132 
133           for (i = 0 ; i < sp->c && strcmp(s, sp->v[i]) != 0 ; i++) {
134           }
135           return (i < sp->c);
136 }
137 
138 /* make sure the directory hierarchy exists */
139 static int
mkdirs(char * path)140 mkdirs(char *path)
141 {
142           char       name[MAXPATHLEN];
143           char      *slash;
144 
145           (void) snprintf(name, sizeof(name), "%s%s", dirs.v[0], path);
146           slash = &name[strlen(path) + 1];
147           while ((slash = strchr(slash, '/')) != NULL) {
148                     *slash = 0x0;
149 printf("mkdirs: dir `%s'\n", name);
150                     if (mkdir(name, 0777) < 0) {
151                               return 0;
152                     }
153                     *slash = '/';
154           }
155           return 1;
156 }
157 
158 /* copy a file, preserving mode, to make it writable */
159 static int
copyfile(char * from,char * to)160 copyfile(char *from, char *to)
161 {
162           struct stat         st;
163           char                buf[BUFSIZ * 10];
164           int                 fdfrom;
165           int                 fdto;
166           int                 ret;
167           int                 cc;
168 
169           if ((fdfrom = open(from, O_RDONLY, 0666)) < 0) {
170                     warn("can't open file `%s' for reading", from);
171                     return 0;
172           }
173           (void) fstat(fdfrom, &st);
174           if ((fdto = open(to, O_WRONLY | O_CREAT | O_EXCL, st.st_mode & 07777)) < 0) {
175                     warn("can't open file `%s' for reading", from);
176                     close(fdfrom);
177                     return 0;
178           }
179           for (ret = 1 ; ret && (cc = read(fdfrom, buf, sizeof(buf))) > 0 ; ) {
180                     if (write(fdto, buf, cc) != cc) {
181                               warn("short write");
182                               ret = 0;
183                     }
184           }
185           if (fchown(fdto, st.st_uid, st.st_gid) < 0) {
186                     warn("bad fchown");
187                     ret = 0;
188           }
189           (void) close(fdfrom);
190           (void) close(fdto);
191           return ret;
192 }
193 
194 /* file system operations start here */
195 
196 /* perform the stat operation */
197 static int
fanoutfs_getattr(const char * path,struct stat * st)198 fanoutfs_getattr(const char *path, struct stat *st)
199 {
200           char      name[MAXPATHLEN];
201 
202           (void) memset(st, 0x0, sizeof(*st));
203           if (strcmp(path, "/") == 0) {
204                     st->st_mode = S_IFDIR | 0755;
205                     st->st_nlink = 2;
206                     return 0;
207           }
208           if (findentry(path, name, sizeof(name), st) < 0) {
209                     return -ENOENT;
210           }
211           return 0;
212 }
213 
214 /* readdir operation */
215 static int
fanoutfs_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi)216 fanoutfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
217                 off_t offset, struct fuse_file_info *fi)
218 {
219           struct dirent       *dp;
220           strv_t               names;
221           char                 name[MAXPATHLEN];
222           DIR                 *dirp;
223           int                  i;
224 
225           (void) fi;
226 
227           (void) memset(&names, 0x0, sizeof(names));
228           for (i = 0 ; i < dirs.c ; i++) {
229                     (void) snprintf(name, sizeof(name), "%s%s", dirs.v[i], path);
230                     if ((dirp = opendir(name)) == NULL) {
231                               continue;
232                     }
233                     while ((dp = readdir(dirp)) != NULL) {
234                               if (!present(dp->d_name, &names)) {
235                                         ALLOC(char *, names.v, names.size, names.c,
236                                                   10, 10, "readdir", exit(EXIT_FAILURE));
237                                         names.v[names.c++] = strdup(dp->d_name);
238                               }
239                     }
240                     (void) closedir(dirp);
241           }
242           for (i = 0 ; i < names.c ; i++) {
243                     (void) filler(buf, names.v[i], NULL, 0);
244                     FREE(names.v[i]);
245           }
246           if (i > 0) {
247                     FREE(names.v);
248           }
249           return 0;
250 }
251 
252 /* open the file in the file system */
253 static int
fanoutfs_open(const char * path,struct fuse_file_info * fi)254 fanoutfs_open(const char *path, struct fuse_file_info *fi)
255 {
256           char      newname[MAXPATHLEN];
257           char      name[MAXPATHLEN];
258           int       d;
259 
260           if ((d = findentry(path, name, sizeof(name), NULL)) < 0) {
261                     return -ENOENT;
262           }
263           if (d > 0 && (fi->flags & 0x3) != O_RDONLY) {
264                     /* need to copy file to writable dir */
265                     (void) snprintf(newname, sizeof(newname), "%s%s", dirs.v[0], path);
266                     if (!mkdirs(newname)) {
267                               return -ENOENT;
268                     }
269                     if (!copyfile(name, newname)) {
270                               return -EPERM;
271                     }
272           }
273           return 0;
274 }
275 
276 /* read the file's contents in the file system */
277 static int
fanoutfs_read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)278 fanoutfs_read(const char *path, char *buf, size_t size, off_t offset,
279              struct fuse_file_info * fi)
280 {
281           char      name[MAXPATHLEN];
282           int       fd;
283           int       cc;
284 
285           (void) fi;
286 
287           if (findentry(path, name, sizeof(name), NULL) < 0) {
288                     return -ENOENT;
289           }
290           if ((fd = open(name, O_RDONLY, 0666)) < 0) {
291                     return -ENOENT;
292           }
293           if (lseek(fd, offset, SEEK_SET) < 0) {
294                     (void) close(fd);
295                     return -EBADF;
296           }
297           if ((cc = read(fd, buf, size)) < 0) {
298                     (void) close(fd);
299                     return -errno;
300           }
301           (void) close(fd);
302           return cc;
303 }
304 
305 /* write the file's contents in the file system */
306 static int
fanoutfs_write(const char * path,const char * buf,size_t size,off_t offset,struct fuse_file_info * fi)307 fanoutfs_write(const char *path, const char *buf, size_t size, off_t offset,
308              struct fuse_file_info * fi)
309 {
310           char      name[MAXPATHLEN];
311           int       fd;
312           int       cc;
313 
314           (void) fi;
315 
316           if (findentry(path, name, sizeof(name), NULL) < 0) {
317                     return -ENOENT;
318           }
319           if ((fd = open(name, O_WRONLY, 0666)) < 0) {
320                     return -ENOENT;
321           }
322           if (lseek(fd, offset, SEEK_SET) < 0) {
323                     (void) close(fd);
324                     return -EBADF;
325           }
326           if ((cc = write(fd, buf, size)) < 0) {
327                     (void) close(fd);
328                     return -errno;
329           }
330           (void) close(fd);
331           return cc;
332 }
333 
334 /* fill in the statvfs struct */
335 static int
fanoutfs_statfs(const char * path,struct statvfs * st)336 fanoutfs_statfs(const char *path, struct statvfs *st)
337 {
338           (void) memset(st, 0x0, sizeof(*st));
339           st->f_bsize = st->f_frsize = st->f_iosize = 512;
340           st->f_owner = vfs.st_uid;
341           st->f_files = 1;
342           return 0;
343 }
344 
345 /* "remove" a file */
346 static int
fanoutfs_unlink(const char * path)347 fanoutfs_unlink(const char *path)
348 {
349           char      name[MAXPATHLEN];
350 
351           if (findentry(path, name, sizeof(name), NULL) < 0) {
352                     return -ENOENT;
353           }
354           if (unlink(name) < 0) {
355                     return -errno;
356           }
357           return 0;
358 }
359 
360 /* check the access on a file */
361 static int
fanoutfs_access(const char * path,int acc)362 fanoutfs_access(const char *path, int acc)
363 {
364           char      name[MAXPATHLEN];
365 
366           if (findentry(path, name, sizeof(name), NULL) < 0) {
367                     return -ENOENT;
368           }
369           if (access(name, acc) < 0) {
370                     return -errno;
371           }
372           return 0;
373 }
374 
375 /* change the mode of a file */
376 static int
fanoutfs_chmod(const char * path,mode_t mode)377 fanoutfs_chmod(const char *path, mode_t mode)
378 {
379           char      name[MAXPATHLEN];
380 
381           if (findentry(path, name, sizeof(name), NULL) < 0) {
382                     return -ENOENT;
383           }
384           if (chmod(name, mode) < 0) {
385                     return -errno;
386           }
387           return 0;
388 }
389 
390 /* change the owner and group of a file */
391 static int
fanoutfs_chown(const char * path,uid_t uid,gid_t gid)392 fanoutfs_chown(const char *path, uid_t uid, gid_t gid)
393 {
394           char      name[MAXPATHLEN];
395 
396           if (findentry(path, name, sizeof(name), NULL) < 0) {
397                     return -ENOENT;
398           }
399           if (lchown(name, uid, gid) < 0) {
400                     return -errno;
401           }
402           return 0;
403 }
404 
405 /* "rename" a file */
406 static int
fanoutfs_rename(const char * from,const char * to)407 fanoutfs_rename(const char *from, const char *to)
408 {
409           char      fromname[MAXPATHLEN];
410           char      toname[MAXPATHLEN];
411 
412           if (findentry(from, fromname, sizeof(fromname), NULL) < 0) {
413                     return -ENOENT;
414           }
415           (void) snprintf(toname, sizeof(toname), "%s%s", dirs.v[0], to);
416           if (!mkdirs(toname)) {
417                     return -ENOENT;
418           }
419           if (rename(fromname, toname) < 0) {
420                     return -EPERM;
421           }
422           return 0;
423 }
424 
425 /* create a file */
426 static int
fanoutfs_create(const char * path,mode_t mode,struct fuse_file_info * fi)427 fanoutfs_create(const char *path, mode_t mode, struct fuse_file_info *fi)
428 {
429           struct stat         st;
430           char                name[MAXPATHLEN];
431           int                 fd;
432 
433           if (findentry(path, name, sizeof(name), &st) >= 0) {
434                     return -EEXIST;
435           }
436           if ((fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) {
437                     return -EPERM;
438           }
439           (void) close(fd);
440           return 0;
441 }
442 
443 /* create a special node */
444 static int
fanoutfs_mknod(const char * path,mode_t mode,dev_t d)445 fanoutfs_mknod(const char *path, mode_t mode, dev_t d)
446 {
447           struct stat         st;
448           char                name[MAXPATHLEN];
449 
450           if (findentry(path, name, sizeof(name), &st) >= 0) {
451                     return -EEXIST;
452           }
453           if (mknod(name, mode, d) < 0) {
454                     return -EPERM;
455           }
456           return 0;
457 }
458 
459 /* create a directory */
460 static int
fanoutfs_mkdir(const char * path,mode_t mode)461 fanoutfs_mkdir(const char *path, mode_t mode)
462 {
463           char      name[MAXPATHLEN];
464 
465           (void) snprintf(name, sizeof(name), "%s%s", dirs.v[0], path);
466           if (!mkdirs(name)) {
467                     return -ENOENT;
468           }
469           if (mkdir(name, mode) < 0) {
470                     return -EPERM;
471           }
472           return 0;
473 }
474 
475 /* create a symbolic link */
476 static int
fanoutfs_symlink(const char * path,const char * tgt)477 fanoutfs_symlink(const char *path, const char *tgt)
478 {
479           char      name[MAXPATHLEN];
480 
481           (void) snprintf(name, sizeof(name), "%s%s", dirs.v[0], path);
482           if (!mkdirs(name)) {
483                     return -ENOENT;
484           }
485           if (symlink(name, tgt) < 0) {
486                     return -EPERM;
487           }
488           return 0;
489 }
490 
491 /* create a link */
492 static int
fanoutfs_link(const char * path,const char * tgt)493 fanoutfs_link(const char *path, const char *tgt)
494 {
495           char      name[MAXPATHLEN];
496 
497           (void) snprintf(name, sizeof(name), "%s%s", dirs.v[0], path);
498           if (!mkdirs(name)) {
499                     return -ENOENT;
500           }
501           if (link(name, tgt) < 0) {
502                     return -errno;
503           }
504           return 0;
505 }
506 
507 /* read the contents of a symbolic link */
508 static int
fanoutfs_readlink(const char * path,char * buf,size_t size)509 fanoutfs_readlink(const char *path, char *buf, size_t size)
510 {
511           char      name[MAXPATHLEN];
512 
513           if (findentry(path, name, sizeof(name), NULL) < 0) {
514                     return -ENOENT;
515           }
516           if (readlink(name, buf, size) < 0) {
517                     return -errno;
518           }
519           return 0;
520 }
521 
522 /* remove a directory */
523 static int
fanoutfs_rmdir(const char * path)524 fanoutfs_rmdir(const char *path)
525 {
526           char      name[MAXPATHLEN];
527 
528           if (findentry(path, name, sizeof(name), NULL) < 0) {
529                     return -ENOENT;
530           }
531           if (rmdir(name) < 0) {
532                     return -errno;
533           }
534           return 0;
535 }
536 
537 /* truncate a file */
538 static int
fanoutfs_truncate(const char * path,off_t size)539 fanoutfs_truncate(const char *path, off_t size)
540 {
541           char      name[MAXPATHLEN];
542 
543           if (findentry(path, name, sizeof(name), NULL) < 0) {
544                     return -ENOENT;
545           }
546           if (truncate(name, size) < 0) {
547                     return -errno;
548           }
549           return 0;
550 }
551 
552 /* set utimes on a file */
553 static int
fanoutfs_utime(const char * path,struct utimbuf * t)554 fanoutfs_utime(const char *path, struct utimbuf *t)
555 {
556           char      name[MAXPATHLEN];
557 
558           if (findentry(path, name, sizeof(name), NULL) < 0) {
559                     return -ENOENT;
560           }
561           if (utime(name, t) < 0) {
562                     return -errno;
563           }
564           return 0;
565 }
566 
567 /* operations struct */
568 static struct fuse_operations fanoutfs_oper = {
569           .getattr = fanoutfs_getattr,
570           .readlink = fanoutfs_readlink,
571           .mknod = fanoutfs_mknod,
572           .mkdir = fanoutfs_mkdir,
573           .unlink = fanoutfs_unlink,
574           .rmdir = fanoutfs_rmdir,
575           .symlink = fanoutfs_symlink,
576           .rename = fanoutfs_rename,
577           .link = fanoutfs_link,
578           .chmod = fanoutfs_chmod,
579           .chown = fanoutfs_chown,
580           .truncate = fanoutfs_truncate,
581           .utime = fanoutfs_utime,
582           .open = fanoutfs_open,
583           .read = fanoutfs_read,
584           .write = fanoutfs_write,
585           .statfs = fanoutfs_statfs,
586           .readdir = fanoutfs_readdir,
587           .access = fanoutfs_access,
588           .create = fanoutfs_create
589 };
590 
591 int
main(int argc,char ** argv)592 main(int argc, char **argv)
593 {
594           int        i;
595 
596           while ((i = getopt(argc, argv, "f:v")) != -1) {
597                     switch(i) {
598                     case 'f':
599                               conffile = optarg;
600                               break;
601                     case 'v':
602                               verbose = 1;
603                               break;
604                     }
605           }
606           (void) signal(SIGHUP, sighup);
607           readconf(conffile);
608           (void) daemon(1, 1);
609           return fuse_main(argc, argv, &fanoutfs_oper, NULL);
610 }
611