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 *
strtolower(const char * path,char * name,size_t size)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
add_entry(virtdir_t * tp,const char * name,uint8_t type)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
icfs_getattr(const char * path,struct stat * st)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
icfs_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi)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
icfs_open(const char * path,struct fuse_file_info * fi)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
icfs_read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)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
icfs_write(const char * path,const char * buf,size_t size,off_t offset,struct fuse_file_info * fi)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
icfs_statfs(const char * path,struct statvfs * st)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
icfs_unlink(const char * path)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
icfs_access(const char * path,int acc)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
icfs_chmod(const char * path,mode_t mode)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
icfs_chown(const char * path,uid_t uid,gid_t gid)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
icfs_rename(const char * from,const char * to)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
icfs_create(const char * path,mode_t mode,struct fuse_file_info * fi)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
icfs_mknod(const char * path,mode_t mode,dev_t d)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
icfs_mkdir(const char * path,mode_t mode)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
icfs_symlink(const char * path,const char * tgt)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
icfs_link(const char * path,const char * tgt)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
icfs_readlink(const char * path,char * buf,size_t size)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
icfs_rmdir(const char * path)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
icfs_truncate(const char * path,off_t size)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
icfs_utime(const char * path,struct utimbuf * t)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
dodir(virtdir_t * tp,char * rootdir,const char * subdir)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
main(int argc,char ** argv)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