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