Home | History | Annotate | Line # | Download | only in icfs
      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