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