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