xref: /netbsd-src/share/examples/refuse/fanoutfs/fanoutfs.c (revision b143e0b001b5dc4c4ab2a3ac3b69169be612eb66)
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