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 55DEFINE_ARRAY(strv_t, char *); 56 57static struct stat vfs; /* stat info of directory */ 58static virtdir_t tree; /* virtual directory tree */ 59static int verbose; /* how chatty are we? */ 60 61 62 63 64 65/********************************************************************/ 66 67/* convert a string to lower case */ 68static char * 69strtolower(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 */ 81static void 82add_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 */ 98static int 99icfs_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 */ 121static int 122icfs_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 */ 149static int 150icfs_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 */ 156static int 157icfs_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 */ 187static int 188icfs_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 */ 218static int 219icfs_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 */ 229static int 230icfs_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 */ 247static int 248icfs_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 */ 264static int 265icfs_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 */ 281static int 282icfs_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 */ 298static int 299icfs_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 */ 322static int 323icfs_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 */ 346static int 347icfs_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 */ 368static int 369icfs_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 */ 390static int 391icfs_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 */ 412static int 413icfs_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 */ 434static int 435icfs_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 */ 451static int 452icfs_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 */ 469static int 470icfs_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 */ 486static int 487icfs_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 */ 503static 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 */ 527static int 528dodir(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 598int 599main(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