xref: /netbsd-src/share/examples/refuse/icfs/icfs.c (revision b5677b36047b601b9addaaa494a58ceae82c2a6c)
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 "virtdir.h"
45 #include "defs.h"
46 
47 #ifndef PREFIX
48 #define PREFIX		""
49 #endif
50 
51 #ifndef DEF_CONF_FILE
52 #define DEF_CONF_FILE	"/etc/icfs.conf"
53 #endif
54 
55 DEFINE_ARRAY(strv_t, char *);
56 
57 static struct stat	 vfs;		/* stat info of directory */
58 static virtdir_t	 tree;		/* virtual directory tree */
59 static int		 verbose; 	/* how chatty are we? */
60 
61 
62 
63 
64 
65 /********************************************************************/
66 
67 /* convert a string to lower case */
68 static char *
69 strtolower(const char *path, char *name, size_t size)
70 {
71 	const char	*cp;
72 	char		*np;
73 
74 	for (cp = path, np = name ; (int)(np - name) < size ; np++, cp++) {
75 		*np = tolower((unsigned)*cp);
76 	}
77 	return name;
78 }
79 
80 /* add a name and its lower case entry */
81 static void
82 add_entry(virtdir_t *tp, const char *name, uint8_t type)
83 {
84 	char	 icname[MAXPATHLEN];
85 	char	*root;
86 	int	 len;
87 
88 	root = virtdir_rootdir(&tree);
89 	len = strlen(root);
90 	strtolower(&name[len], icname, sizeof(icname));
91 	virtdir_add(tp, &name[len], strlen(name) - len, type, icname,
92 				strlen(icname));
93 }
94 
95 /* file system operations start here */
96 
97 /* perform the stat operation */
98 static int
99 icfs_getattr(const char *path, struct stat *st)
100 {
101 	virt_dirent_t	*ep;
102 	char		 name[MAXPATHLEN];
103 
104 	(void) memset(st, 0x0, sizeof(*st));
105 	if (strcmp(path, "/") == 0) {
106 		st->st_mode = S_IFDIR | 0755;
107 		st->st_nlink = 2;
108 		return 0;
109 	}
110 	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
111 		return -ENOENT;
112 	}
113 	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
114 	if (stat(name, st) < 0) {
115 		return -errno;
116 	}
117 	return 0;
118 }
119 
120 /* readdir operation */
121 static int
122 icfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
123 	      off_t offset, struct fuse_file_info *fi)
124 {
125 	virt_dirent_t	*ep;
126 	VIRTDIR		*dirp;
127 	char		 name[MAXPATHLEN];
128 
129 	(void) fi;
130 
131 	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
132 		return -ENOENT;
133 	}
134 	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
135 	if ((dirp = openvirtdir(&tree, ep->name)) == NULL) {
136 		return -ENOENT;
137 	}
138 	filler(buf, ".", NULL, 0);
139 	filler(buf, "..", NULL, 0);
140 	while ((ep = readvirtdir(dirp)) != NULL) {
141 		strtolower(ep->d_name, name, sizeof(name));
142 		(void) filler(buf, name, NULL, 0);
143 	}
144 	(void) closevirtdir(dirp);
145 	return 0;
146 }
147 
148 /* open the file in the file system */
149 static int
150 icfs_open(const char *path, struct fuse_file_info *fi)
151 {
152 	return 0;
153 }
154 
155 /* read the file's contents in the file system */
156 static int
157 icfs_read(const char *path, char *buf, size_t size, off_t offset,
158 	   struct fuse_file_info * fi)
159 {
160 	virt_dirent_t	*ep;
161 	char		 name[MAXPATHLEN];
162 	int		 fd;
163 	int		 cc;
164 
165 	(void) fi;
166 
167 	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
168 		return -ENOENT;
169 	}
170 	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
171 	if ((fd = open(name, O_RDONLY, 0666)) < 0) {
172 		return -ENOENT;
173 	}
174 	if (lseek(fd, offset, SEEK_SET) < 0) {
175 		(void) close(fd);
176 		return -EBADF;
177 	}
178 	if ((cc = read(fd, buf, size)) < 0) {
179 		(void) close(fd);
180 		return -errno;
181 	}
182 	(void) close(fd);
183 	return cc;
184 }
185 
186 /* write the file's contents in the file system */
187 static int
188 icfs_write(const char *path, const char *buf, size_t size, off_t offset,
189 	   struct fuse_file_info * fi)
190 {
191 	virt_dirent_t	*ep;
192 	char		 name[MAXPATHLEN];
193 	int		 fd;
194 	int		 cc;
195 
196 	(void) fi;
197 
198 	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
199 		return -ENOENT;
200 	}
201 	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
202 	if ((fd = open(name, O_WRONLY, 0666)) < 0) {
203 		return -ENOENT;
204 	}
205 	if (lseek(fd, offset, SEEK_SET) < 0) {
206 		(void) close(fd);
207 		return -EBADF;
208 	}
209 	if ((cc = write(fd, buf, size)) < 0) {
210 		(void) close(fd);
211 		return -errno;
212 	}
213 	(void) close(fd);
214 	return cc;
215 }
216 
217 /* fill in the statvfs struct */
218 static int
219 icfs_statfs(const char *path, struct statvfs *st)
220 {
221 	(void) memset(st, 0x0, sizeof(*st));
222 	st->f_bsize = st->f_frsize = st->f_iosize = 512;
223 	st->f_owner = vfs.st_uid;
224 	st->f_files = 1;
225 	return 0;
226 }
227 
228 /* "remove" a file */
229 static int
230 icfs_unlink(const char *path)
231 {
232 	virt_dirent_t	*ep;
233 	char		 name[MAXPATHLEN];
234 
235 	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
236 		return -ENOENT;
237 	}
238 	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
239 	if (unlink(name) < 0) {
240 		return -errno;
241 	}
242 	/* XXX - delete entry */
243 	return 0;
244 }
245 
246 /* check the access on a file */
247 static int
248 icfs_access(const char *path, int acc)
249 {
250 	virt_dirent_t	*ep;
251 	char		 name[MAXPATHLEN];
252 
253 	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
254 		return -ENOENT;
255 	}
256 	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
257 	if (access(name, acc) < 0) {
258 		return -errno;
259 	}
260 	return 0;
261 }
262 
263 /* change the mode of a file */
264 static int
265 icfs_chmod(const char *path, mode_t mode)
266 {
267 	virt_dirent_t	*ep;
268 	char		 name[MAXPATHLEN];
269 
270 	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
271 		return -ENOENT;
272 	}
273 	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
274 	if (chmod(name, mode) < 0) {
275 		return -errno;
276 	}
277 	return 0;
278 }
279 
280 /* change the owner and group of a file */
281 static int
282 icfs_chown(const char *path, uid_t uid, gid_t gid)
283 {
284 	virt_dirent_t	*ep;
285 	char		 name[MAXPATHLEN];
286 
287 	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
288 		return -ENOENT;
289 	}
290 	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
291 	if (lchown(name, uid, gid) < 0) {
292 		return -errno;
293 	}
294 	return 0;
295 }
296 
297 /* "rename" a file */
298 static int
299 icfs_rename(const char *from, const char *to)
300 {
301 #if 0
302 	char	fromname[MAXPATHLEN];
303 	char	toname[MAXPATHLEN];
304 
305 	virt_dirent_t	*ep;
306 
307 	if ((ep = virtdir_find_tgt(&tree, from, strlen(from))) == NULL) {
308 		return -ENOENT;
309 	}
310 	(void) snprintf(toname, sizeof(toname), "%s%s", dirs.v[0], to);
311 	if (!mkdirs(toname)) {
312 		return -ENOENT;
313 	}
314 	if (rename(fromname, toname) < 0) {
315 		return -EPERM;
316 	}
317 #endif
318 	return 0;
319 }
320 
321 /* create a file */
322 static int
323 icfs_create(const char *path, mode_t mode, struct fuse_file_info *fi)
324 {
325 	virt_dirent_t	*ep;
326 	char		*slash;
327 	char		 name[MAXPATHLEN];
328 	int		 fd;
329 
330 	if ((slash = strrchr(path, '/')) == NULL) {
331 		return -ENOENT;
332 	}
333 	if ((ep = virtdir_find_tgt(&tree, path, (int)(slash - path) - 1)) == NULL) {
334 		return -ENOENT;
335 	}
336 	(void) snprintf(name, sizeof(name), "%s/%s%s", virtdir_rootdir(&tree), ep->name, slash);
337 	if ((fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) {
338 		return -EPERM;
339 	}
340 	(void) close(fd);
341 	add_entry(&tree, name, 'f');
342 	return 0;
343 }
344 
345 /* create a special node */
346 static int
347 icfs_mknod(const char *path, mode_t mode, dev_t d)
348 {
349 	virt_dirent_t	*ep;
350 	char		*slash;
351 	char		 name[MAXPATHLEN];
352 
353 	if ((slash = strrchr(path, '/')) == NULL) {
354 		return -ENOENT;
355 	}
356 	if ((ep = virtdir_find_tgt(&tree, path, (int)(slash - path) - 1)) == NULL) {
357 		return -ENOENT;
358 	}
359 	(void) snprintf(name, sizeof(name), "%s/%s/%s", virtdir_rootdir(&tree), ep->name, slash + 1);
360 	if (mknod(name, mode, d) < 0) {
361 		return -EPERM;
362 	}
363 	add_entry(&tree, name, ((mode & S_IFMT) == S_IFCHR) ? 'c' : 'b');
364 	return 0;
365 }
366 
367 /* create a directory */
368 static int
369 icfs_mkdir(const char *path, mode_t mode)
370 {
371 	virt_dirent_t	*ep;
372 	char		*slash;
373 	char		 name[MAXPATHLEN];
374 
375 	if ((slash = strrchr(path, '/')) == NULL) {
376 		return -ENOENT;
377 	}
378 	if ((ep = virtdir_find_tgt(&tree, path, (int)(slash - path) - 1)) == NULL) {
379 		return -EEXIST;
380 	}
381 	(void) snprintf(name, sizeof(name), "%s/%s/%s", virtdir_rootdir(&tree), ep->name, slash + 1);
382 	if (mkdir(name, mode) < 0) {
383 		return -EPERM;
384 	}
385 	add_entry(&tree, name, 'd');
386 	return 0;
387 }
388 
389 /* create a symbolic link */
390 static int
391 icfs_symlink(const char *path, const char *tgt)
392 {
393 	virt_dirent_t	*ep;
394 	char		*slash;
395 	char		 name[MAXPATHLEN];
396 
397 	if ((slash = strrchr(path, '/')) == NULL) {
398 		return -ENOENT;
399 	}
400 	if ((ep = virtdir_find_tgt(&tree, path, (int)(slash - path) - 1)) == NULL) {
401 		return -EEXIST;
402 	}
403 	(void) snprintf(name, sizeof(name), "%s/%s/%s", virtdir_rootdir(&tree), ep->name, slash + 1);
404 	if (symlink(name, tgt) < 0) {
405 		return -EPERM;
406 	}
407 	add_entry(&tree, name, 'l');
408 	return 0;
409 }
410 
411 /* create a link */
412 static int
413 icfs_link(const char *path, const char *tgt)
414 {
415 	virt_dirent_t	*ep;
416 	char		*slash;
417 	char		 name[MAXPATHLEN];
418 
419 	if ((slash = strrchr(path, '/')) == NULL) {
420 		return -ENOENT;
421 	}
422 	if ((ep = virtdir_find_tgt(&tree, path, (int)(slash - path) - 1)) == NULL) {
423 		return -EEXIST;
424 	}
425 	(void) snprintf(name, sizeof(name), "%s/%s/%s", virtdir_rootdir(&tree), ep->name, slash + 1);
426 	if (link(name, tgt) < 0) {
427 		return -errno;
428 	}
429 	add_entry(&tree, name, 'f');	/* XXX */
430 	return 0;
431 }
432 
433 /* read the contents of a symbolic link */
434 static int
435 icfs_readlink(const char *path, char *buf, size_t size)
436 {
437 	virt_dirent_t	*ep;
438 	char		 name[MAXPATHLEN];
439 
440 	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
441 		return -ENOENT;
442 	}
443 	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
444 	if (readlink(name, buf, size) < 0) {
445 		return -errno;
446 	}
447 	return 0;
448 }
449 
450 /* remove a directory */
451 static int
452 icfs_rmdir(const char *path)
453 {
454 	virt_dirent_t	*ep;
455 	char		 name[MAXPATHLEN];
456 
457 	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
458 		return -ENOENT;
459 	}
460 	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
461 	if (rmdir(name) < 0) {
462 		return -errno;
463 	}
464 	/* XXX - delete entry */
465 	return 0;
466 }
467 
468 /* truncate a file */
469 static int
470 icfs_truncate(const char *path, off_t size)
471 {
472 	virt_dirent_t	*ep;
473 	char		 name[MAXPATHLEN];
474 
475 	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
476 		return -ENOENT;
477 	}
478 	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
479 	if (truncate(name, size) < 0) {
480 		return -errno;
481 	}
482 	return 0;
483 }
484 
485 /* set utimes on a file */
486 static int
487 icfs_utime(const char *path, struct utimbuf *t)
488 {
489 	virt_dirent_t	*ep;
490 	char		 name[MAXPATHLEN];
491 
492 	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
493 		return -ENOENT;
494 	}
495 	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
496 	if (utime(name, t) < 0) {
497 		return -errno;
498 	}
499 	return 0;
500 }
501 
502 /* operations struct */
503 static struct fuse_operations icfs_oper = {
504 	.getattr = icfs_getattr,
505 	.readlink = icfs_readlink,
506 	.mknod = icfs_mknod,
507 	.mkdir = icfs_mkdir,
508 	.unlink = icfs_unlink,
509 	.rmdir = icfs_rmdir,
510 	.symlink = icfs_symlink,
511 	.rename = icfs_rename,
512 	.link = icfs_link,
513 	.chmod = icfs_chmod,
514 	.chown = icfs_chown,
515 	.truncate = icfs_truncate,
516 	.utime = icfs_utime,
517 	.open = icfs_open,
518 	.read = icfs_read,
519 	.write = icfs_write,
520 	.statfs = icfs_statfs,
521 	.readdir = icfs_readdir,
522 	.access = icfs_access,
523 	.create = icfs_create
524 };
525 
526 /* build up a virtdir from the information in the file system */
527 static int
528 dodir(virtdir_t *tp, char *rootdir, const char *subdir)
529 {
530 	struct dirent	*dp;
531 	struct stat	 st;
532 	struct stat	 dir;
533 	struct stat	 f;
534 	struct stat	 l;
535 	char		 icname[MAXPATHLEN];
536 	char		 name[MAXPATHLEN];
537 	char		 type;
538 	DIR		*dirp;
539 	int		 len;
540 
541 	if (tp->v == NULL) {
542 		(void) stat(".", &dir);
543 		(void) memcpy(&f, &dir, sizeof(f));
544 		f.st_mode = S_IFREG | 0644;
545 		(void) memcpy(&l, &f, sizeof(l));
546 		l.st_mode = S_IFLNK | 0755;
547 		virtdir_init(tp, rootdir, &dir, &f, &l);
548 		virtdir_add(tp, "/", 1, 'd', "/", 1);
549 	}
550 	(void) snprintf(name, sizeof(name), "%s/%s", rootdir, subdir);
551 	if ((dirp = opendir(name)) == NULL) {
552 		warn("dodir: can't opendir `%s'", name);
553 		return 0;
554 	}
555 	len = strlen(tp->rootdir);
556 	while ((dp = readdir(dirp)) != NULL) {
557 		if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
558 			continue;
559 		}
560 		(void) snprintf(name, sizeof(name), "%s%s%s%s%s", rootdir, (subdir[0] == 0x0) ? "" : "/", subdir,  "/", dp->d_name);
561 		if (stat(name, &st) < 0) {
562 			warnx("can't stat `%s'", name);
563 			continue;
564 		}
565 		switch (st.st_mode & S_IFMT) {
566 		case S_IFDIR:
567 			type = 'd';
568 			break;
569 		case S_IFREG:
570 			type = 'f';
571 			break;
572 		case S_IFLNK:
573 			type = 'l';
574 			break;
575 		case S_IFBLK:
576 			type = 'b';
577 			break;
578 		case S_IFCHR:
579 			type = 'c';
580 			break;
581 		default:
582 			type = '?';
583 			break;
584 		}
585 		if (!virtdir_find(tp, &name[len], strlen(name) - len)) {
586 			strtolower(&name[len], icname, sizeof(icname));
587 			virtdir_add(tp, &name[len], strlen(name) - len, type, icname,
588 				strlen(icname));
589 		}
590 		if (type == 'd') {
591 			dodir(tp, rootdir, &name[len + 1]);
592 		}
593 	}
594 	(void) closedir(dirp);
595 	return 1;
596 }
597 
598 int
599 main(int argc, char **argv)
600 {
601 	int	 i;
602 
603 	while ((i = getopt(argc, argv, "f:v")) != -1) {
604 		switch(i) {
605 		case 'v':
606 			verbose = 1;
607 			break;
608 		}
609 	}
610 #if 0
611 	(void) daemon(1, 1);
612 #endif
613 	dodir(&tree, argv[optind], "");
614 	return fuse_main(argc, argv, &icfs_oper, NULL);
615 }
616