Fix mdoc(7)/man(7) mix up.
[netbsd-mini2440.git] / share / examples / refuse / fanoutfs / fanoutfs.c
blob09ecdba15d3c1dd78a28b98f6494537f83b6d467
1 /*
2 * Copyright © 2007 Alistair Crooks. All rights reserved.
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.
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.
28 #include <sys/types.h>
29 #include <sys/stat.h>
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>
44 #include "defs.h"
46 #ifndef PREFIX
47 #define PREFIX ""
48 #endif
50 #ifndef DEF_CONF_FILE
51 #define DEF_CONF_FILE "/etc/fanoutfs.conf"
52 #endif
54 DEFINE_ARRAY(strv_t, char *);
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? */
65 /********************************************************************/
67 static int
68 readconf(const char *f)
70 char buf[BUFSIZ];
71 char *cp;
72 FILE *fp;
73 int line;
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;
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++) {
83 if (*cp == '\n' || *cp == 0x0 || *cp == '#') {
84 continue;
86 ALLOC(char *, dirs.v, dirs.size, dirs.c, 10, 10,
87 "readconf", exit(EXIT_FAILURE));
88 dirs.v[dirs.c++] = strdup(cp);
90 (void) fclose(fp);
91 return 1;
94 /* yes, this does too much work */
95 static void
96 sighup(int n)
98 int i;
100 printf("Reading configuration file `%s'\n", conffile);
101 for (i = 0 ; i < dirs.c ; i++) {
102 FREE(dirs.v[i]);
104 dirs.c = 0;
105 readconf(conffile);
108 /* find the correct entry in the list of directories */
109 static int
110 findentry(const char *path, char *name, size_t namesize, struct stat *sp)
112 struct stat st;
113 int i;
115 if (sp == NULL) {
116 sp = &st;
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;
124 return -1;
127 /* return 1 if the string `s' is present in the array */
128 static int
129 present(char *s, strv_t *sp)
131 int i;
133 for (i = 0 ; i < sp->c && strcmp(s, sp->v[i]) != 0 ; i++) {
135 return (i < sp->c);
138 /* make sure the directory hierarchy exists */
139 static int
140 mkdirs(char *path)
142 char name[MAXPATHLEN];
143 char *slash;
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;
153 *slash = '/';
155 return 1;
158 /* copy a file, preserving mode, to make it writable */
159 static int
160 copyfile(char *from, char *to)
162 struct stat st;
163 char buf[BUFSIZ * 10];
164 int fdfrom;
165 int fdto;
166 int ret;
167 int cc;
169 if ((fdfrom = open(from, O_RDONLY, 0666)) < 0) {
170 warn("can't open file `%s' for reading", from);
171 return 0;
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;
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;
185 if (fchown(fdto, st.st_uid, st.st_gid) < 0) {
186 warn("bad fchown");
187 ret = 0;
189 (void) close(fdfrom);
190 (void) close(fdto);
191 return ret;
194 /* file system operations start here */
196 /* perform the stat operation */
197 static int
198 fanoutfs_getattr(const char *path, struct stat *st)
200 char name[MAXPATHLEN];
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;
208 if (findentry(path, name, sizeof(name), st) < 0) {
209 return -ENOENT;
211 return 0;
214 /* readdir operation */
215 static int
216 fanoutfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
217 off_t offset, struct fuse_file_info *fi)
219 struct dirent *dp;
220 strv_t names;
221 char name[MAXPATHLEN];
222 DIR *dirp;
223 int i;
225 (void) fi;
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;
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);
240 (void) closedir(dirp);
242 for (i = 0 ; i < names.c ; i++) {
243 (void) filler(buf, names.v[i], NULL, 0);
244 FREE(names.v[i]);
246 if (i > 0) {
247 FREE(names.v);
249 return 0;
252 /* open the file in the file system */
253 static int
254 fanoutfs_open(const char *path, struct fuse_file_info *fi)
256 char newname[MAXPATHLEN];
257 char name[MAXPATHLEN];
258 int d;
260 if ((d = findentry(path, name, sizeof(name), NULL)) < 0) {
261 return -ENOENT;
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;
269 if (!copyfile(name, newname)) {
270 return -EPERM;
273 return 0;
276 /* read the file's contents in the file system */
277 static int
278 fanoutfs_read(const char *path, char *buf, size_t size, off_t offset,
279 struct fuse_file_info * fi)
281 char name[MAXPATHLEN];
282 int fd;
283 int cc;
285 (void) fi;
287 if (findentry(path, name, sizeof(name), NULL) < 0) {
288 return -ENOENT;
290 if ((fd = open(name, O_RDONLY, 0666)) < 0) {
291 return -ENOENT;
293 if (lseek(fd, offset, SEEK_SET) < 0) {
294 (void) close(fd);
295 return -EBADF;
297 if ((cc = read(fd, buf, size)) < 0) {
298 (void) close(fd);
299 return -errno;
301 (void) close(fd);
302 return cc;
305 /* write the file's contents in the file system */
306 static int
307 fanoutfs_write(const char *path, const char *buf, size_t size, off_t offset,
308 struct fuse_file_info * fi)
310 char name[MAXPATHLEN];
311 int fd;
312 int cc;
314 (void) fi;
316 if (findentry(path, name, sizeof(name), NULL) < 0) {
317 return -ENOENT;
319 if ((fd = open(name, O_WRONLY, 0666)) < 0) {
320 return -ENOENT;
322 if (lseek(fd, offset, SEEK_SET) < 0) {
323 (void) close(fd);
324 return -EBADF;
326 if ((cc = write(fd, buf, size)) < 0) {
327 (void) close(fd);
328 return -errno;
330 (void) close(fd);
331 return cc;
334 /* fill in the statvfs struct */
335 static int
336 fanoutfs_statfs(const char *path, struct statvfs *st)
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;
345 /* "remove" a file */
346 static int
347 fanoutfs_unlink(const char *path)
349 char name[MAXPATHLEN];
351 if (findentry(path, name, sizeof(name), NULL) < 0) {
352 return -ENOENT;
354 if (unlink(name) < 0) {
355 return -errno;
357 return 0;
360 /* check the access on a file */
361 static int
362 fanoutfs_access(const char *path, int acc)
364 char name[MAXPATHLEN];
366 if (findentry(path, name, sizeof(name), NULL) < 0) {
367 return -ENOENT;
369 if (access(name, acc) < 0) {
370 return -errno;
372 return 0;
375 /* change the mode of a file */
376 static int
377 fanoutfs_chmod(const char *path, mode_t mode)
379 char name[MAXPATHLEN];
381 if (findentry(path, name, sizeof(name), NULL) < 0) {
382 return -ENOENT;
384 if (chmod(name, mode) < 0) {
385 return -errno;
387 return 0;
390 /* change the owner and group of a file */
391 static int
392 fanoutfs_chown(const char *path, uid_t uid, gid_t gid)
394 char name[MAXPATHLEN];
396 if (findentry(path, name, sizeof(name), NULL) < 0) {
397 return -ENOENT;
399 if (lchown(name, uid, gid) < 0) {
400 return -errno;
402 return 0;
405 /* "rename" a file */
406 static int
407 fanoutfs_rename(const char *from, const char *to)
409 char fromname[MAXPATHLEN];
410 char toname[MAXPATHLEN];
412 if (findentry(from, fromname, sizeof(fromname), NULL) < 0) {
413 return -ENOENT;
415 (void) snprintf(toname, sizeof(toname), "%s%s", dirs.v[0], to);
416 if (!mkdirs(toname)) {
417 return -ENOENT;
419 if (rename(fromname, toname) < 0) {
420 return -EPERM;
422 return 0;
425 /* create a file */
426 static int
427 fanoutfs_create(const char *path, mode_t mode, struct fuse_file_info *fi)
429 struct stat st;
430 char name[MAXPATHLEN];
431 int fd;
433 if (findentry(path, name, sizeof(name), &st) >= 0) {
434 return -EEXIST;
436 if ((fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) {
437 return -EPERM;
439 (void) close(fd);
440 return 0;
443 /* create a special node */
444 static int
445 fanoutfs_mknod(const char *path, mode_t mode, dev_t d)
447 struct stat st;
448 char name[MAXPATHLEN];
450 if (findentry(path, name, sizeof(name), &st) >= 0) {
451 return -EEXIST;
453 if (mknod(name, mode, d) < 0) {
454 return -EPERM;
456 return 0;
459 /* create a directory */
460 static int
461 fanoutfs_mkdir(const char *path, mode_t mode)
463 char name[MAXPATHLEN];
465 (void) snprintf(name, sizeof(name), "%s%s", dirs.v[0], path);
466 if (!mkdirs(name)) {
467 return -ENOENT;
469 if (mkdir(name, mode) < 0) {
470 return -EPERM;
472 return 0;
475 /* create a symbolic link */
476 static int
477 fanoutfs_symlink(const char *path, const char *tgt)
479 char name[MAXPATHLEN];
481 (void) snprintf(name, sizeof(name), "%s%s", dirs.v[0], path);
482 if (!mkdirs(name)) {
483 return -ENOENT;
485 if (symlink(name, tgt) < 0) {
486 return -EPERM;
488 return 0;
491 /* create a link */
492 static int
493 fanoutfs_link(const char *path, const char *tgt)
495 char name[MAXPATHLEN];
497 (void) snprintf(name, sizeof(name), "%s%s", dirs.v[0], path);
498 if (!mkdirs(name)) {
499 return -ENOENT;
501 if (link(name, tgt) < 0) {
502 return -errno;
504 return 0;
507 /* read the contents of a symbolic link */
508 static int
509 fanoutfs_readlink(const char *path, char *buf, size_t size)
511 char name[MAXPATHLEN];
513 if (findentry(path, name, sizeof(name), NULL) < 0) {
514 return -ENOENT;
516 if (readlink(name, buf, size) < 0) {
517 return -errno;
519 return 0;
522 /* remove a directory */
523 static int
524 fanoutfs_rmdir(const char *path)
526 char name[MAXPATHLEN];
528 if (findentry(path, name, sizeof(name), NULL) < 0) {
529 return -ENOENT;
531 if (rmdir(name) < 0) {
532 return -errno;
534 return 0;
537 /* truncate a file */
538 static int
539 fanoutfs_truncate(const char *path, off_t size)
541 char name[MAXPATHLEN];
543 if (findentry(path, name, sizeof(name), NULL) < 0) {
544 return -ENOENT;
546 if (truncate(name, size) < 0) {
547 return -errno;
549 return 0;
552 /* set utimes on a file */
553 static int
554 fanoutfs_utime(const char *path, struct utimbuf *t)
556 char name[MAXPATHLEN];
558 if (findentry(path, name, sizeof(name), NULL) < 0) {
559 return -ENOENT;
561 if (utime(name, t) < 0) {
562 return -errno;
564 return 0;
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
591 int
592 main(int argc, char **argv)
594 int i;
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;
606 (void) signal(SIGHUP, sighup);
607 readconf(conffile);
608 (void) daemon(1, 1);
609 return fuse_main(argc, argv, &fanoutfs_oper, NULL);