1/* 2 fuse subdir module: offset paths with a base directory 3 Copyright (C) 2007 Miklos Szeredi <miklos@szeredi.hu> 4 5 This program can be distributed under the terms of the GNU LGPLv2. 6 See the file COPYING.LIB 7*/ 8 9#include <fuse_config.h> 10 11#include <fuse.h> 12#include <stdio.h> 13#include <stdlib.h> 14#include <stddef.h> 15#include <string.h> 16#include <errno.h> 17 18struct subdir { 19 char *base; 20 size_t baselen; 21 int rellinks; 22 struct fuse_fs *next; 23}; 24 25static struct subdir *subdir_get(void) 26{ 27 return fuse_get_context()->private_data; 28} 29 30static int subdir_addpath(struct subdir *d, const char *path, char **newpathp) 31{ 32 char *newpath = NULL; 33 34 if (path != NULL) { 35 unsigned newlen = d->baselen + strlen(path); 36 37 newpath = malloc(newlen + 2); 38 if (!newpath) 39 return -ENOMEM; 40 41 if (path[0] == '/') 42 path++; 43 strcpy(newpath, d->base); 44 strcpy(newpath + d->baselen, path); 45 if (!newpath[0]) 46 strcpy(newpath, "."); 47 } 48 *newpathp = newpath; 49 50 return 0; 51} 52 53static int subdir_getattr(const char *path, struct stat *stbuf, 54 struct fuse_file_info *fi) 55{ 56 struct subdir *d = subdir_get(); 57 char *newpath; 58 int err = subdir_addpath(d, path, &newpath); 59 if (!err) { 60 err = fuse_fs_getattr(d->next, newpath, stbuf, fi); 61 free(newpath); 62 } 63 return err; 64} 65 66static int subdir_access(const char *path, int mask) 67{ 68 struct subdir *d = subdir_get(); 69 char *newpath; 70 int err = subdir_addpath(d, path, &newpath); 71 if (!err) { 72 err = fuse_fs_access(d->next, newpath, mask); 73 free(newpath); 74 } 75 return err; 76} 77 78 79static int count_components(const char *p) 80{ 81 int ctr; 82 83 for (; *p == '/'; p++); 84 for (ctr = 0; *p; ctr++) { 85 for (; *p && *p != '/'; p++); 86 for (; *p == '/'; p++); 87 } 88 return ctr; 89} 90 91static void strip_common(const char **sp, const char **tp) 92{ 93 const char *s = *sp; 94 const char *t = *tp; 95 do { 96 for (; *s == '/'; s++); 97 for (; *t == '/'; t++); 98 *tp = t; 99 *sp = s; 100 for (; *s == *t && *s && *s != '/'; s++, t++); 101 } while ((*s == *t && *s) || (!*s && *t == '/') || (*s == '/' && !*t)); 102} 103 104static void transform_symlink(struct subdir *d, const char *path, 105 char *buf, size_t size) 106{ 107 const char *l = buf; 108 size_t llen; 109 char *s; 110 int dotdots; 111 int i; 112 113 if (l[0] != '/' || d->base[0] != '/') 114 return; 115 116 strip_common(&l, &path); 117 if (l - buf < (long) d->baselen) 118 return; 119 120 dotdots = count_components(path); 121 if (!dotdots) 122 return; 123 dotdots--; 124 125 llen = strlen(l); 126 if (dotdots * 3 + llen + 2 > size) 127 return; 128 129 s = buf + dotdots * 3; 130 if (llen) 131 memmove(s, l, llen + 1); 132 else if (!dotdots) 133 strcpy(s, "."); 134 else 135 *s = '\0'; 136 137 for (s = buf, i = 0; i < dotdots; i++, s += 3) 138 memcpy(s, "../", 3); 139} 140 141 142static int subdir_readlink(const char *path, char *buf, size_t size) 143{ 144 struct subdir *d = subdir_get(); 145 char *newpath; 146 int err = subdir_addpath(d, path, &newpath); 147 if (!err) { 148 err = fuse_fs_readlink(d->next, newpath, buf, size); 149 if (!err && d->rellinks) 150 transform_symlink(d, newpath, buf, size); 151 free(newpath); 152 } 153 return err; 154} 155 156static int subdir_opendir(const char *path, struct fuse_file_info *fi) 157{ 158 struct subdir *d = subdir_get(); 159 char *newpath; 160 int err = subdir_addpath(d, path, &newpath); 161 if (!err) { 162 err = fuse_fs_opendir(d->next, newpath, fi); 163 free(newpath); 164 } 165 return err; 166} 167 168static int subdir_readdir(const char *path, void *buf, 169 fuse_fill_dir_t filler, off_t offset, 170 struct fuse_file_info *fi, 171 enum fuse_readdir_flags flags) 172{ 173 struct subdir *d = subdir_get(); 174 char *newpath; 175 int err = subdir_addpath(d, path, &newpath); 176 if (!err) { 177 err = fuse_fs_readdir(d->next, newpath, buf, filler, offset, 178 fi, flags); 179 free(newpath); 180 } 181 return err; 182} 183 184static int subdir_releasedir(const char *path, struct fuse_file_info *fi) 185{ 186 struct subdir *d = subdir_get(); 187 char *newpath; 188 int err = subdir_addpath(d, path, &newpath); 189 if (!err) { 190 err = fuse_fs_releasedir(d->next, newpath, fi); 191 free(newpath); 192 } 193 return err; 194} 195 196static int subdir_mknod(const char *path, mode_t mode, dev_t rdev) 197{ 198 struct subdir *d = subdir_get(); 199 char *newpath; 200 int err = subdir_addpath(d, path, &newpath); 201 if (!err) { 202 err = fuse_fs_mknod(d->next, newpath, mode, rdev); 203 free(newpath); 204 } 205 return err; 206} 207 208static int subdir_mkdir(const char *path, mode_t mode) 209{ 210 struct subdir *d = subdir_get(); 211 char *newpath; 212 int err = subdir_addpath(d, path, &newpath); 213 if (!err) { 214 err = fuse_fs_mkdir(d->next, newpath, mode); 215 free(newpath); 216 } 217 return err; 218} 219 220static int subdir_unlink(const char *path) 221{ 222 struct subdir *d = subdir_get(); 223 char *newpath; 224 int err = subdir_addpath(d, path, &newpath); 225 if (!err) { 226 err = fuse_fs_unlink(d->next, newpath); 227 free(newpath); 228 } 229 return err; 230} 231 232static int subdir_rmdir(const char *path) 233{ 234 struct subdir *d = subdir_get(); 235 char *newpath; 236 int err = subdir_addpath(d, path, &newpath); 237 if (!err) { 238 err = fuse_fs_rmdir(d->next, newpath); 239 free(newpath); 240 } 241 return err; 242} 243 244static int subdir_symlink(const char *from, const char *path) 245{ 246 struct subdir *d = subdir_get(); 247 char *newpath; 248 int err = subdir_addpath(d, path, &newpath); 249 if (!err) { 250 err = fuse_fs_symlink(d->next, from, newpath); 251 free(newpath); 252 } 253 return err; 254} 255 256static int subdir_rename(const char *from, const char *to, unsigned int flags) 257{ 258 struct subdir *d = subdir_get(); 259 char *newfrom; 260 char *newto; 261 int err = subdir_addpath(d, from, &newfrom); 262 if (!err) { 263 err = subdir_addpath(d, to, &newto); 264 if (!err) { 265 err = fuse_fs_rename(d->next, newfrom, newto, flags); 266 free(newto); 267 } 268 free(newfrom); 269 } 270 return err; 271} 272 273static int subdir_link(const char *from, const char *to) 274{ 275 struct subdir *d = subdir_get(); 276 char *newfrom; 277 char *newto; 278 int err = subdir_addpath(d, from, &newfrom); 279 if (!err) { 280 err = subdir_addpath(d, to, &newto); 281 if (!err) { 282 err = fuse_fs_link(d->next, newfrom, newto); 283 free(newto); 284 } 285 free(newfrom); 286 } 287 return err; 288} 289 290static int subdir_chmod(const char *path, mode_t mode, 291 struct fuse_file_info *fi) 292{ 293 struct subdir *d = subdir_get(); 294 char *newpath; 295 int err = subdir_addpath(d, path, &newpath); 296 if (!err) { 297 err = fuse_fs_chmod(d->next, newpath, mode, fi); 298 free(newpath); 299 } 300 return err; 301} 302 303static int subdir_chown(const char *path, uid_t uid, gid_t gid, 304 struct fuse_file_info *fi) 305{ 306 struct subdir *d = subdir_get(); 307 char *newpath; 308 int err = subdir_addpath(d, path, &newpath); 309 if (!err) { 310 err = fuse_fs_chown(d->next, newpath, uid, gid, fi); 311 free(newpath); 312 } 313 return err; 314} 315 316static int subdir_truncate(const char *path, off_t size, 317 struct fuse_file_info *fi) 318{ 319 struct subdir *d = subdir_get(); 320 char *newpath; 321 int err = subdir_addpath(d, path, &newpath); 322 if (!err) { 323 err = fuse_fs_truncate(d->next, newpath, size, fi); 324 free(newpath); 325 } 326 return err; 327} 328 329static int subdir_utimens(const char *path, const struct timespec ts[2], 330 struct fuse_file_info *fi) 331{ 332 struct subdir *d = subdir_get(); 333 char *newpath; 334 int err = subdir_addpath(d, path, &newpath); 335 if (!err) { 336 err = fuse_fs_utimens(d->next, newpath, ts, fi); 337 free(newpath); 338 } 339 return err; 340} 341 342static int subdir_create(const char *path, mode_t mode, 343 struct fuse_file_info *fi) 344{ 345 struct subdir *d = subdir_get(); 346 char *newpath; 347 int err = subdir_addpath(d, path, &newpath); 348 if (!err) { 349 err = fuse_fs_create(d->next, newpath, mode, fi); 350 free(newpath); 351 } 352 return err; 353} 354 355static int subdir_open(const char *path, struct fuse_file_info *fi) 356{ 357 struct subdir *d = subdir_get(); 358 char *newpath; 359 int err = subdir_addpath(d, path, &newpath); 360 if (!err) { 361 err = fuse_fs_open(d->next, newpath, fi); 362 free(newpath); 363 } 364 return err; 365} 366 367static int subdir_read_buf(const char *path, struct fuse_bufvec **bufp, 368 size_t size, off_t offset, struct fuse_file_info *fi) 369{ 370 struct subdir *d = subdir_get(); 371 char *newpath; 372 int err = subdir_addpath(d, path, &newpath); 373 if (!err) { 374 err = fuse_fs_read_buf(d->next, newpath, bufp, size, offset, fi); 375 free(newpath); 376 } 377 return err; 378} 379 380static int subdir_write_buf(const char *path, struct fuse_bufvec *buf, 381 off_t offset, struct fuse_file_info *fi) 382{ 383 struct subdir *d = subdir_get(); 384 char *newpath; 385 int err = subdir_addpath(d, path, &newpath); 386 if (!err) { 387 err = fuse_fs_write_buf(d->next, newpath, buf, offset, fi); 388 free(newpath); 389 } 390 return err; 391} 392 393static int subdir_statfs(const char *path, struct statvfs *stbuf) 394{ 395 struct subdir *d = subdir_get(); 396 char *newpath; 397 int err = subdir_addpath(d, path, &newpath); 398 if (!err) { 399 err = fuse_fs_statfs(d->next, newpath, stbuf); 400 free(newpath); 401 } 402 return err; 403} 404 405static int subdir_flush(const char *path, struct fuse_file_info *fi) 406{ 407 struct subdir *d = subdir_get(); 408 char *newpath; 409 int err = subdir_addpath(d, path, &newpath); 410 if (!err) { 411 err = fuse_fs_flush(d->next, newpath, fi); 412 free(newpath); 413 } 414 return err; 415} 416 417static int subdir_release(const char *path, struct fuse_file_info *fi) 418{ 419 struct subdir *d = subdir_get(); 420 char *newpath; 421 int err = subdir_addpath(d, path, &newpath); 422 if (!err) { 423 err = fuse_fs_release(d->next, newpath, fi); 424 free(newpath); 425 } 426 return err; 427} 428 429static int subdir_fsync(const char *path, int isdatasync, 430 struct fuse_file_info *fi) 431{ 432 struct subdir *d = subdir_get(); 433 char *newpath; 434 int err = subdir_addpath(d, path, &newpath); 435 if (!err) { 436 err = fuse_fs_fsync(d->next, newpath, isdatasync, fi); 437 free(newpath); 438 } 439 return err; 440} 441 442static int subdir_fsyncdir(const char *path, int isdatasync, 443 struct fuse_file_info *fi) 444{ 445 struct subdir *d = subdir_get(); 446 char *newpath; 447 int err = subdir_addpath(d, path, &newpath); 448 if (!err) { 449 err = fuse_fs_fsyncdir(d->next, newpath, isdatasync, fi); 450 free(newpath); 451 } 452 return err; 453} 454 455static int subdir_setxattr(const char *path, const char *name, 456 const char *value, size_t size, int flags) 457{ 458 struct subdir *d = subdir_get(); 459 char *newpath; 460 int err = subdir_addpath(d, path, &newpath); 461 if (!err) { 462 err = fuse_fs_setxattr(d->next, newpath, name, value, size, 463 flags); 464 free(newpath); 465 } 466 return err; 467} 468 469static int subdir_getxattr(const char *path, const char *name, char *value, 470 size_t size) 471{ 472 struct subdir *d = subdir_get(); 473 char *newpath; 474 int err = subdir_addpath(d, path, &newpath); 475 if (!err) { 476 err = fuse_fs_getxattr(d->next, newpath, name, value, size); 477 free(newpath); 478 } 479 return err; 480} 481 482static int subdir_listxattr(const char *path, char *list, size_t size) 483{ 484 struct subdir *d = subdir_get(); 485 char *newpath; 486 int err = subdir_addpath(d, path, &newpath); 487 if (!err) { 488 err = fuse_fs_listxattr(d->next, newpath, list, size); 489 free(newpath); 490 } 491 return err; 492} 493 494static int subdir_removexattr(const char *path, const char *name) 495{ 496 struct subdir *d = subdir_get(); 497 char *newpath; 498 int err = subdir_addpath(d, path, &newpath); 499 if (!err) { 500 err = fuse_fs_removexattr(d->next, newpath, name); 501 free(newpath); 502 } 503 return err; 504} 505 506static int subdir_lock(const char *path, struct fuse_file_info *fi, int cmd, 507 struct flock *lock) 508{ 509 struct subdir *d = subdir_get(); 510 char *newpath; 511 int err = subdir_addpath(d, path, &newpath); 512 if (!err) { 513 err = fuse_fs_lock(d->next, newpath, fi, cmd, lock); 514 free(newpath); 515 } 516 return err; 517} 518 519static int subdir_flock(const char *path, struct fuse_file_info *fi, int op) 520{ 521 struct subdir *d = subdir_get(); 522 char *newpath; 523 int err = subdir_addpath(d, path, &newpath); 524 if (!err) { 525 err = fuse_fs_flock(d->next, newpath, fi, op); 526 free(newpath); 527 } 528 return err; 529} 530 531static int subdir_bmap(const char *path, size_t blocksize, uint64_t *idx) 532{ 533 struct subdir *d = subdir_get(); 534 char *newpath; 535 int err = subdir_addpath(d, path, &newpath); 536 if (!err) { 537 err = fuse_fs_bmap(d->next, newpath, blocksize, idx); 538 free(newpath); 539 } 540 return err; 541} 542 543static off_t subdir_lseek(const char *path, off_t off, int whence, 544 struct fuse_file_info *fi) 545{ 546 struct subdir *ic = subdir_get(); 547 char *newpath; 548 int res = subdir_addpath(ic, path, &newpath); 549 if (!res) { 550 res = fuse_fs_lseek(ic->next, newpath, off, whence, fi); 551 free(newpath); 552 } 553 return res; 554} 555 556static void *subdir_init(struct fuse_conn_info *conn, 557 struct fuse_config *cfg) 558{ 559 struct subdir *d = subdir_get(); 560 fuse_fs_init(d->next, conn, cfg); 561 /* Don't touch cfg->nullpath_ok, we can work with 562 either */ 563 return d; 564} 565 566static void subdir_destroy(void *data) 567{ 568 struct subdir *d = data; 569 fuse_fs_destroy(d->next); 570 free(d->base); 571 free(d); 572} 573 574static const struct fuse_operations subdir_oper = { 575 .destroy = subdir_destroy, 576 .init = subdir_init, 577 .getattr = subdir_getattr, 578 .access = subdir_access, 579 .readlink = subdir_readlink, 580 .opendir = subdir_opendir, 581 .readdir = subdir_readdir, 582 .releasedir = subdir_releasedir, 583 .mknod = subdir_mknod, 584 .mkdir = subdir_mkdir, 585 .symlink = subdir_symlink, 586 .unlink = subdir_unlink, 587 .rmdir = subdir_rmdir, 588 .rename = subdir_rename, 589 .link = subdir_link, 590 .chmod = subdir_chmod, 591 .chown = subdir_chown, 592 .truncate = subdir_truncate, 593 .utimens = subdir_utimens, 594 .create = subdir_create, 595 .open = subdir_open, 596 .read_buf = subdir_read_buf, 597 .write_buf = subdir_write_buf, 598 .statfs = subdir_statfs, 599 .flush = subdir_flush, 600 .release = subdir_release, 601 .fsync = subdir_fsync, 602 .fsyncdir = subdir_fsyncdir, 603 .setxattr = subdir_setxattr, 604 .getxattr = subdir_getxattr, 605 .listxattr = subdir_listxattr, 606 .removexattr = subdir_removexattr, 607 .lock = subdir_lock, 608 .flock = subdir_flock, 609 .bmap = subdir_bmap, 610 .lseek = subdir_lseek, 611}; 612 613static const struct fuse_opt subdir_opts[] = { 614 FUSE_OPT_KEY("-h", 0), 615 FUSE_OPT_KEY("--help", 0), 616 { "subdir=%s", offsetof(struct subdir, base), 0 }, 617 { "rellinks", offsetof(struct subdir, rellinks), 1 }, 618 { "norellinks", offsetof(struct subdir, rellinks), 0 }, 619 FUSE_OPT_END 620}; 621 622static void subdir_help(void) 623{ 624 printf( 625" -o subdir=DIR prepend this directory to all paths (mandatory)\n" 626" -o [no]rellinks transform absolute symlinks to relative\n"); 627} 628 629static int subdir_opt_proc(void *data, const char *arg, int key, 630 struct fuse_args *outargs) 631{ 632 (void) data; (void) arg; (void) outargs; 633 634 if (!key) { 635 subdir_help(); 636 return -1; 637 } 638 639 return 1; 640} 641 642static struct fuse_fs *subdir_new(struct fuse_args *args, 643 struct fuse_fs *next[]) 644{ 645 struct fuse_fs *fs; 646 struct subdir *d; 647 648 d = calloc(1, sizeof(struct subdir)); 649 if (d == NULL) { 650 fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n"); 651 return NULL; 652 } 653 654 if (fuse_opt_parse(args, d, subdir_opts, subdir_opt_proc) == -1) 655 goto out_free; 656 657 if (!next[0] || next[1]) { 658 fuse_log(FUSE_LOG_ERR, "fuse-subdir: exactly one next filesystem required\n"); 659 goto out_free; 660 } 661 662 if (!d->base) { 663 fuse_log(FUSE_LOG_ERR, "fuse-subdir: missing 'subdir' option\n"); 664 goto out_free; 665 } 666 667 if (d->base[0] && d->base[strlen(d->base)-1] != '/') { 668 char *tmp = realloc(d->base, strlen(d->base) + 2); 669 if (!tmp) { 670 fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n"); 671 goto out_free; 672 } 673 d->base = tmp; 674 strcat(d->base, "/"); 675 } 676 d->baselen = strlen(d->base); 677 d->next = next[0]; 678 fs = fuse_fs_new(&subdir_oper, sizeof(subdir_oper), d); 679 if (!fs) 680 goto out_free; 681 return fs; 682 683out_free: 684 free(d->base); 685 free(d); 686 return NULL; 687} 688 689FUSE_REGISTER_MODULE(subdir, subdir_new); 690