1/* 2 FUSE: Filesystem in Userspace 3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> 4 5 This program can be distributed under the terms of the GNU GPLv2. 6 See the file COPYING. 7*/ 8 9/** @file 10 * 11 * This file system mirrors the existing file system hierarchy of the 12 * system, starting at the root file system. This is implemented by 13 * just "passing through" all requests to the corresponding user-space 14 * libc functions. In contrast to passthrough.c and passthrough_fh.c, 15 * this implementation uses the low-level API. Its performance should 16 * be the least bad among the three, but many operations are not 17 * implemented. In particular, it is not possible to remove files (or 18 * directories) because the code necessary to defer actual removal 19 * until the file is not opened anymore would make the example much 20 * more complicated. 21 * 22 * When writeback caching is enabled (-o writeback mount option), it 23 * is only possible to write to files for which the mounting user has 24 * read permissions. This is because the writeback cache requires the 25 * kernel to be able to issue read requests for all files (which the 26 * passthrough filesystem cannot satisfy if it can't read the file in 27 * the underlying filesystem). 28 * 29 * Compile with: 30 * 31 * gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o passthrough_ll 32 * 33 * ## Source code ## 34 * \include passthrough_ll.c 35 */ 36 37#define _GNU_SOURCE 38#define FUSE_USE_VERSION 34 39 40#include <fuse_lowlevel.h> 41#include <unistd.h> 42#include <stdlib.h> 43#include <stdio.h> 44#include <stddef.h> 45#include <stdbool.h> 46#include <string.h> 47#include <limits.h> 48#include <dirent.h> 49#include <assert.h> 50#include <errno.h> 51#include <inttypes.h> 52#include <pthread.h> 53#include <sys/file.h> 54#include <sys/xattr.h> 55 56#include "passthrough_helpers.h" 57 58/* We are re-using pointers to our `struct lo_inode` and `struct 59 lo_dirp` elements as inodes. This means that we must be able to 60 store uintptr_t values in a fuse_ino_t variable. The following 61 incantation checks this condition at compile time. */ 62#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus 63_Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t), 64 "fuse_ino_t too small to hold uintptr_t values!"); 65#else 66struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \ 67 { unsigned _uintptr_to_must_hold_fuse_ino_t: 68 ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); }; 69#endif 70 71struct lo_inode { 72 struct lo_inode *next; /* protected by lo->mutex */ 73 struct lo_inode *prev; /* protected by lo->mutex */ 74 int fd; 75 ino_t ino; 76 dev_t dev; 77 uint64_t refcount; /* protected by lo->mutex */ 78}; 79 80enum { 81 CACHE_NEVER, 82 CACHE_NORMAL, 83 CACHE_ALWAYS, 84}; 85 86struct lo_data { 87 pthread_mutex_t mutex; 88 int debug; 89 int writeback; 90 int flock; 91 int xattr; 92 char *source; 93 double timeout; 94 int cache; 95 int timeout_set; 96 struct lo_inode root; /* protected by lo->mutex */ 97}; 98 99static const struct fuse_opt lo_opts[] = { 100 { "writeback", 101 offsetof(struct lo_data, writeback), 1 }, 102 { "no_writeback", 103 offsetof(struct lo_data, writeback), 0 }, 104 { "source=%s", 105 offsetof(struct lo_data, source), 0 }, 106 { "flock", 107 offsetof(struct lo_data, flock), 1 }, 108 { "no_flock", 109 offsetof(struct lo_data, flock), 0 }, 110 { "xattr", 111 offsetof(struct lo_data, xattr), 1 }, 112 { "no_xattr", 113 offsetof(struct lo_data, xattr), 0 }, 114 { "timeout=%lf", 115 offsetof(struct lo_data, timeout), 0 }, 116 { "timeout=", 117 offsetof(struct lo_data, timeout_set), 1 }, 118 { "cache=never", 119 offsetof(struct lo_data, cache), CACHE_NEVER }, 120 { "cache=auto", 121 offsetof(struct lo_data, cache), CACHE_NORMAL }, 122 { "cache=always", 123 offsetof(struct lo_data, cache), CACHE_ALWAYS }, 124 125 FUSE_OPT_END 126}; 127 128static void passthrough_ll_help(void) 129{ 130 printf( 131" -o writeback Enable writeback\n" 132" -o no_writeback Disable write back\n" 133" -o source=/home/dir Source directory to be mounted\n" 134" -o flock Enable flock\n" 135" -o no_flock Disable flock\n" 136" -o xattr Enable xattr\n" 137" -o no_xattr Disable xattr\n" 138" -o timeout=1.0 Caching timeout\n" 139" -o timeout=0/1 Timeout is set\n" 140" -o cache=never Disable cache\n" 141" -o cache=auto Auto enable cache\n" 142" -o cache=always Cache always\n"); 143} 144 145static struct lo_data *lo_data(fuse_req_t req) 146{ 147 return (struct lo_data *) fuse_req_userdata(req); 148} 149 150static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) 151{ 152 if (ino == FUSE_ROOT_ID) 153 return &lo_data(req)->root; 154 else 155 return (struct lo_inode *) (uintptr_t) ino; 156} 157 158static int lo_fd(fuse_req_t req, fuse_ino_t ino) 159{ 160 return lo_inode(req, ino)->fd; 161} 162 163static bool lo_debug(fuse_req_t req) 164{ 165 return lo_data(req)->debug != 0; 166} 167 168static void lo_init(void *userdata, 169 struct fuse_conn_info *conn) 170{ 171 struct lo_data *lo = (struct lo_data*) userdata; 172 173 if(conn->capable & FUSE_CAP_EXPORT_SUPPORT) 174 conn->want |= FUSE_CAP_EXPORT_SUPPORT; 175 176 if (lo->writeback && 177 conn->capable & FUSE_CAP_WRITEBACK_CACHE) { 178 if (lo->debug) 179 fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); 180 conn->want |= FUSE_CAP_WRITEBACK_CACHE; 181 } 182 if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { 183 if (lo->debug) 184 fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); 185 conn->want |= FUSE_CAP_FLOCK_LOCKS; 186 } 187} 188 189static void lo_destroy(void *userdata) 190{ 191 struct lo_data *lo = (struct lo_data*) userdata; 192 193 while (lo->root.next != &lo->root) { 194 struct lo_inode* next = lo->root.next; 195 lo->root.next = next->next; 196 free(next); 197 } 198} 199 200static void lo_getattr(fuse_req_t req, fuse_ino_t ino, 201 struct fuse_file_info *fi) 202{ 203 int res; 204 struct stat buf; 205 struct lo_data *lo = lo_data(req); 206 207 (void) fi; 208 209 res = fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); 210 if (res == -1) 211 return (void) fuse_reply_err(req, errno); 212 213 fuse_reply_attr(req, &buf, lo->timeout); 214} 215 216static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, 217 int valid, struct fuse_file_info *fi) 218{ 219 int saverr; 220 char procname[64]; 221 struct lo_inode *inode = lo_inode(req, ino); 222 int ifd = inode->fd; 223 int res; 224 225 if (valid & FUSE_SET_ATTR_MODE) { 226 if (fi) { 227 res = fchmod(fi->fh, attr->st_mode); 228 } else { 229 sprintf(procname, "/proc/self/fd/%i", ifd); 230 res = chmod(procname, attr->st_mode); 231 } 232 if (res == -1) 233 goto out_err; 234 } 235 if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) { 236 uid_t uid = (valid & FUSE_SET_ATTR_UID) ? 237 attr->st_uid : (uid_t) -1; 238 gid_t gid = (valid & FUSE_SET_ATTR_GID) ? 239 attr->st_gid : (gid_t) -1; 240 241 res = fchownat(ifd, "", uid, gid, 242 AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); 243 if (res == -1) 244 goto out_err; 245 } 246 if (valid & FUSE_SET_ATTR_SIZE) { 247 if (fi) { 248 res = ftruncate(fi->fh, attr->st_size); 249 } else { 250 sprintf(procname, "/proc/self/fd/%i", ifd); 251 res = truncate(procname, attr->st_size); 252 } 253 if (res == -1) 254 goto out_err; 255 } 256 if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) { 257 struct timespec tv[2]; 258 259 tv[0].tv_sec = 0; 260 tv[1].tv_sec = 0; 261 tv[0].tv_nsec = UTIME_OMIT; 262 tv[1].tv_nsec = UTIME_OMIT; 263 264 if (valid & FUSE_SET_ATTR_ATIME_NOW) 265 tv[0].tv_nsec = UTIME_NOW; 266 else if (valid & FUSE_SET_ATTR_ATIME) 267 tv[0] = attr->st_atim; 268 269 if (valid & FUSE_SET_ATTR_MTIME_NOW) 270 tv[1].tv_nsec = UTIME_NOW; 271 else if (valid & FUSE_SET_ATTR_MTIME) 272 tv[1] = attr->st_mtim; 273 274 if (fi) 275 res = futimens(fi->fh, tv); 276 else { 277 sprintf(procname, "/proc/self/fd/%i", ifd); 278 res = utimensat(AT_FDCWD, procname, tv, 0); 279 } 280 if (res == -1) 281 goto out_err; 282 } 283 284 return lo_getattr(req, ino, fi); 285 286out_err: 287 saverr = errno; 288 fuse_reply_err(req, saverr); 289} 290 291static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st) 292{ 293 struct lo_inode *p; 294 struct lo_inode *ret = NULL; 295 296 pthread_mutex_lock(&lo->mutex); 297 for (p = lo->root.next; p != &lo->root; p = p->next) { 298 if (p->ino == st->st_ino && p->dev == st->st_dev) { 299 assert(p->refcount > 0); 300 ret = p; 301 ret->refcount++; 302 break; 303 } 304 } 305 pthread_mutex_unlock(&lo->mutex); 306 return ret; 307} 308 309static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, 310 struct fuse_entry_param *e) 311{ 312 int newfd; 313 int res; 314 int saverr; 315 struct lo_data *lo = lo_data(req); 316 struct lo_inode *inode; 317 318 memset(e, 0, sizeof(*e)); 319 e->attr_timeout = lo->timeout; 320 e->entry_timeout = lo->timeout; 321 322 newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW); 323 if (newfd == -1) 324 goto out_err; 325 326 res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); 327 if (res == -1) 328 goto out_err; 329 330 inode = lo_find(lo_data(req), &e->attr); 331 if (inode) { 332 close(newfd); 333 newfd = -1; 334 } else { 335 struct lo_inode *prev, *next; 336 337 saverr = ENOMEM; 338 inode = calloc(1, sizeof(struct lo_inode)); 339 if (!inode) 340 goto out_err; 341 342 inode->refcount = 1; 343 inode->fd = newfd; 344 inode->ino = e->attr.st_ino; 345 inode->dev = e->attr.st_dev; 346 347 pthread_mutex_lock(&lo->mutex); 348 prev = &lo->root; 349 next = prev->next; 350 next->prev = inode; 351 inode->next = next; 352 inode->prev = prev; 353 prev->next = inode; 354 pthread_mutex_unlock(&lo->mutex); 355 } 356 e->ino = (uintptr_t) inode; 357 358 if (lo_debug(req)) 359 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", 360 (unsigned long long) parent, name, (unsigned long long) e->ino); 361 362 return 0; 363 364out_err: 365 saverr = errno; 366 if (newfd != -1) 367 close(newfd); 368 return saverr; 369} 370 371static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) 372{ 373 struct fuse_entry_param e; 374 int err; 375 376 if (lo_debug(req)) 377 fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", 378 parent, name); 379 380 err = lo_do_lookup(req, parent, name, &e); 381 if (err) 382 fuse_reply_err(req, err); 383 else 384 fuse_reply_entry(req, &e); 385} 386 387static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, 388 const char *name, mode_t mode, dev_t rdev, 389 const char *link) 390{ 391 int res; 392 int saverr; 393 struct lo_inode *dir = lo_inode(req, parent); 394 struct fuse_entry_param e; 395 396 res = mknod_wrapper(dir->fd, name, link, mode, rdev); 397 398 saverr = errno; 399 if (res == -1) 400 goto out; 401 402 saverr = lo_do_lookup(req, parent, name, &e); 403 if (saverr) 404 goto out; 405 406 if (lo_debug(req)) 407 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", 408 (unsigned long long) parent, name, (unsigned long long) e.ino); 409 410 fuse_reply_entry(req, &e); 411 return; 412 413out: 414 fuse_reply_err(req, saverr); 415} 416 417static void lo_mknod(fuse_req_t req, fuse_ino_t parent, 418 const char *name, mode_t mode, dev_t rdev) 419{ 420 lo_mknod_symlink(req, parent, name, mode, rdev, NULL); 421} 422 423static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, 424 mode_t mode) 425{ 426 lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL); 427} 428 429static void lo_symlink(fuse_req_t req, const char *link, 430 fuse_ino_t parent, const char *name) 431{ 432 lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link); 433} 434 435static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, 436 const char *name) 437{ 438 int res; 439 struct lo_data *lo = lo_data(req); 440 struct lo_inode *inode = lo_inode(req, ino); 441 struct fuse_entry_param e; 442 char procname[64]; 443 int saverr; 444 445 memset(&e, 0, sizeof(struct fuse_entry_param)); 446 e.attr_timeout = lo->timeout; 447 e.entry_timeout = lo->timeout; 448 449 sprintf(procname, "/proc/self/fd/%i", inode->fd); 450 res = linkat(AT_FDCWD, procname, lo_fd(req, parent), name, 451 AT_SYMLINK_FOLLOW); 452 if (res == -1) 453 goto out_err; 454 455 res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); 456 if (res == -1) 457 goto out_err; 458 459 pthread_mutex_lock(&lo->mutex); 460 inode->refcount++; 461 pthread_mutex_unlock(&lo->mutex); 462 e.ino = (uintptr_t) inode; 463 464 if (lo_debug(req)) 465 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", 466 (unsigned long long) parent, name, 467 (unsigned long long) e.ino); 468 469 fuse_reply_entry(req, &e); 470 return; 471 472out_err: 473 saverr = errno; 474 fuse_reply_err(req, saverr); 475} 476 477static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) 478{ 479 int res; 480 481 res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR); 482 483 fuse_reply_err(req, res == -1 ? errno : 0); 484} 485 486static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, 487 fuse_ino_t newparent, const char *newname, 488 unsigned int flags) 489{ 490 int res; 491 492 if (flags) { 493 fuse_reply_err(req, EINVAL); 494 return; 495 } 496 497 res = renameat(lo_fd(req, parent), name, 498 lo_fd(req, newparent), newname); 499 500 fuse_reply_err(req, res == -1 ? errno : 0); 501} 502 503static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) 504{ 505 int res; 506 507 res = unlinkat(lo_fd(req, parent), name, 0); 508 509 fuse_reply_err(req, res == -1 ? errno : 0); 510} 511 512static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n) 513{ 514 if (!inode) 515 return; 516 517 pthread_mutex_lock(&lo->mutex); 518 assert(inode->refcount >= n); 519 inode->refcount -= n; 520 if (!inode->refcount) { 521 struct lo_inode *prev, *next; 522 523 prev = inode->prev; 524 next = inode->next; 525 next->prev = prev; 526 prev->next = next; 527 528 pthread_mutex_unlock(&lo->mutex); 529 close(inode->fd); 530 free(inode); 531 532 } else { 533 pthread_mutex_unlock(&lo->mutex); 534 } 535} 536 537static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) 538{ 539 struct lo_data *lo = lo_data(req); 540 struct lo_inode *inode = lo_inode(req, ino); 541 542 if (lo_debug(req)) { 543 fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", 544 (unsigned long long) ino, 545 (unsigned long long) inode->refcount, 546 (unsigned long long) nlookup); 547 } 548 549 unref_inode(lo, inode, nlookup); 550} 551 552static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) 553{ 554 lo_forget_one(req, ino, nlookup); 555 fuse_reply_none(req); 556} 557 558static void lo_forget_multi(fuse_req_t req, size_t count, 559 struct fuse_forget_data *forgets) 560{ 561 int i; 562 563 for (i = 0; i < count; i++) 564 lo_forget_one(req, forgets[i].ino, forgets[i].nlookup); 565 fuse_reply_none(req); 566} 567 568static void lo_readlink(fuse_req_t req, fuse_ino_t ino) 569{ 570 char buf[PATH_MAX + 1]; 571 int res; 572 573 res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf)); 574 if (res == -1) 575 return (void) fuse_reply_err(req, errno); 576 577 if (res == sizeof(buf)) 578 return (void) fuse_reply_err(req, ENAMETOOLONG); 579 580 buf[res] = '\0'; 581 582 fuse_reply_readlink(req, buf); 583} 584 585struct lo_dirp { 586 DIR *dp; 587 struct dirent *entry; 588 off_t offset; 589}; 590 591static struct lo_dirp *lo_dirp(struct fuse_file_info *fi) 592{ 593 return (struct lo_dirp *) (uintptr_t) fi->fh; 594} 595 596static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) 597{ 598 int error = ENOMEM; 599 struct lo_data *lo = lo_data(req); 600 struct lo_dirp *d; 601 int fd; 602 603 d = calloc(1, sizeof(struct lo_dirp)); 604 if (d == NULL) 605 goto out_err; 606 607 fd = openat(lo_fd(req, ino), ".", O_RDONLY); 608 if (fd == -1) 609 goto out_errno; 610 611 d->dp = fdopendir(fd); 612 if (d->dp == NULL) 613 goto out_errno; 614 615 d->offset = 0; 616 d->entry = NULL; 617 618 fi->fh = (uintptr_t) d; 619 if (lo->cache == CACHE_ALWAYS) 620 fi->cache_readdir = 1; 621 fuse_reply_open(req, fi); 622 return; 623 624out_errno: 625 error = errno; 626out_err: 627 if (d) { 628 if (fd != -1) 629 close(fd); 630 free(d); 631 } 632 fuse_reply_err(req, error); 633} 634 635static int is_dot_or_dotdot(const char *name) 636{ 637 return name[0] == '.' && (name[1] == '\0' || 638 (name[1] == '.' && name[2] == '\0')); 639} 640 641static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, 642 off_t offset, struct fuse_file_info *fi, int plus) 643{ 644 struct lo_dirp *d = lo_dirp(fi); 645 char *buf; 646 char *p; 647 size_t rem = size; 648 int err; 649 650 (void) ino; 651 652 buf = calloc(1, size); 653 if (!buf) { 654 err = ENOMEM; 655 goto error; 656 } 657 p = buf; 658 659 if (offset != d->offset) { 660 seekdir(d->dp, offset); 661 d->entry = NULL; 662 d->offset = offset; 663 } 664 while (1) { 665 size_t entsize; 666 off_t nextoff; 667 const char *name; 668 669 if (!d->entry) { 670 errno = 0; 671 d->entry = readdir(d->dp); 672 if (!d->entry) { 673 if (errno) { // Error 674 err = errno; 675 goto error; 676 } else { // End of stream 677 break; 678 } 679 } 680 } 681 nextoff = d->entry->d_off; 682 name = d->entry->d_name; 683 fuse_ino_t entry_ino = 0; 684 if (plus) { 685 struct fuse_entry_param e; 686 if (is_dot_or_dotdot(name)) { 687 e = (struct fuse_entry_param) { 688 .attr.st_ino = d->entry->d_ino, 689 .attr.st_mode = d->entry->d_type << 12, 690 }; 691 } else { 692 err = lo_do_lookup(req, ino, name, &e); 693 if (err) 694 goto error; 695 entry_ino = e.ino; 696 } 697 698 entsize = fuse_add_direntry_plus(req, p, rem, name, 699 &e, nextoff); 700 } else { 701 struct stat st = { 702 .st_ino = d->entry->d_ino, 703 .st_mode = d->entry->d_type << 12, 704 }; 705 entsize = fuse_add_direntry(req, p, rem, name, 706 &st, nextoff); 707 } 708 if (entsize > rem) { 709 if (entry_ino != 0) 710 lo_forget_one(req, entry_ino, 1); 711 break; 712 } 713 714 p += entsize; 715 rem -= entsize; 716 717 d->entry = NULL; 718 d->offset = nextoff; 719 } 720 721 err = 0; 722error: 723 // If there's an error, we can only signal it if we haven't stored 724 // any entries yet - otherwise we'd end up with wrong lookup 725 // counts for the entries that are already in the buffer. So we 726 // return what we've collected until that point. 727 if (err && rem == size) 728 fuse_reply_err(req, err); 729 else 730 fuse_reply_buf(req, buf, size - rem); 731 free(buf); 732} 733 734static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, 735 off_t offset, struct fuse_file_info *fi) 736{ 737 lo_do_readdir(req, ino, size, offset, fi, 0); 738} 739 740static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, 741 off_t offset, struct fuse_file_info *fi) 742{ 743 lo_do_readdir(req, ino, size, offset, fi, 1); 744} 745 746static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) 747{ 748 struct lo_dirp *d = lo_dirp(fi); 749 (void) ino; 750 closedir(d->dp); 751 free(d); 752 fuse_reply_err(req, 0); 753} 754 755static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, 756 mode_t mode, struct fuse_file_info *fi) 757{ 758 int fd; 759 struct lo_data *lo = lo_data(req); 760 struct fuse_entry_param e; 761 int err; 762 763 if (lo_debug(req)) 764 fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", 765 parent, name); 766 767 fd = openat(lo_fd(req, parent), name, 768 (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode); 769 if (fd == -1) 770 return (void) fuse_reply_err(req, errno); 771 772 fi->fh = fd; 773 if (lo->cache == CACHE_NEVER) 774 fi->direct_io = 1; 775 else if (lo->cache == CACHE_ALWAYS) 776 fi->keep_cache = 1; 777 778 err = lo_do_lookup(req, parent, name, &e); 779 if (err) 780 fuse_reply_err(req, err); 781 else 782 fuse_reply_create(req, &e, fi); 783} 784 785static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, 786 struct fuse_file_info *fi) 787{ 788 int res; 789 int fd = dirfd(lo_dirp(fi)->dp); 790 (void) ino; 791 if (datasync) 792 res = fdatasync(fd); 793 else 794 res = fsync(fd); 795 fuse_reply_err(req, res == -1 ? errno : 0); 796} 797 798static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) 799{ 800 int fd; 801 char buf[64]; 802 struct lo_data *lo = lo_data(req); 803 804 if (lo_debug(req)) 805 fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", 806 ino, fi->flags); 807 808 /* With writeback cache, kernel may send read requests even 809 when userspace opened write-only */ 810 if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) { 811 fi->flags &= ~O_ACCMODE; 812 fi->flags |= O_RDWR; 813 } 814 815 /* With writeback cache, O_APPEND is handled by the kernel. 816 This breaks atomicity (since the file may change in the 817 underlying filesystem, so that the kernel's idea of the 818 end of the file isn't accurate anymore). In this example, 819 we just accept that. A more rigorous filesystem may want 820 to return an error here */ 821 if (lo->writeback && (fi->flags & O_APPEND)) 822 fi->flags &= ~O_APPEND; 823 824 sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino)); 825 fd = open(buf, fi->flags & ~O_NOFOLLOW); 826 if (fd == -1) 827 return (void) fuse_reply_err(req, errno); 828 829 fi->fh = fd; 830 if (lo->cache == CACHE_NEVER) 831 fi->direct_io = 1; 832 else if (lo->cache == CACHE_ALWAYS) 833 fi->keep_cache = 1; 834 fuse_reply_open(req, fi); 835} 836 837static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) 838{ 839 (void) ino; 840 841 close(fi->fh); 842 fuse_reply_err(req, 0); 843} 844 845static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) 846{ 847 int res; 848 (void) ino; 849 res = close(dup(fi->fh)); 850 fuse_reply_err(req, res == -1 ? errno : 0); 851} 852 853static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, 854 struct fuse_file_info *fi) 855{ 856 int res; 857 (void) ino; 858 if (datasync) 859 res = fdatasync(fi->fh); 860 else 861 res = fsync(fi->fh); 862 fuse_reply_err(req, res == -1 ? errno : 0); 863} 864 865static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, 866 off_t offset, struct fuse_file_info *fi) 867{ 868 struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size); 869 870 if (lo_debug(req)) 871 fuse_log(FUSE_LOG_DEBUG, "lo_read(ino=%" PRIu64 ", size=%zd, " 872 "off=%lu)\n", ino, size, (unsigned long) offset); 873 874 buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; 875 buf.buf[0].fd = fi->fh; 876 buf.buf[0].pos = offset; 877 878 fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE); 879} 880 881static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, 882 struct fuse_bufvec *in_buf, off_t off, 883 struct fuse_file_info *fi) 884{ 885 (void) ino; 886 ssize_t res; 887 struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf)); 888 889 out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; 890 out_buf.buf[0].fd = fi->fh; 891 out_buf.buf[0].pos = off; 892 893 if (lo_debug(req)) 894 fuse_log(FUSE_LOG_DEBUG, "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n", 895 ino, out_buf.buf[0].size, (unsigned long) off); 896 897 res = fuse_buf_copy(&out_buf, in_buf, 0); 898 if(res < 0) 899 fuse_reply_err(req, -res); 900 else 901 fuse_reply_write(req, (size_t) res); 902} 903 904static void lo_statfs(fuse_req_t req, fuse_ino_t ino) 905{ 906 int res; 907 struct statvfs stbuf; 908 909 res = fstatvfs(lo_fd(req, ino), &stbuf); 910 if (res == -1) 911 fuse_reply_err(req, errno); 912 else 913 fuse_reply_statfs(req, &stbuf); 914} 915 916static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, 917 off_t offset, off_t length, struct fuse_file_info *fi) 918{ 919 int err = EOPNOTSUPP; 920 (void) ino; 921 922#ifdef HAVE_FALLOCATE 923 err = fallocate(fi->fh, mode, offset, length); 924 if (err < 0) 925 err = errno; 926 927#elif defined(HAVE_POSIX_FALLOCATE) 928 if (mode) { 929 fuse_reply_err(req, EOPNOTSUPP); 930 return; 931 } 932 933 err = posix_fallocate(fi->fh, offset, length); 934#endif 935 936 fuse_reply_err(req, err); 937} 938 939static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, 940 int op) 941{ 942 int res; 943 (void) ino; 944 945 res = flock(fi->fh, op); 946 947 fuse_reply_err(req, res == -1 ? errno : 0); 948} 949 950static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, 951 size_t size) 952{ 953 char *value = NULL; 954 char procname[64]; 955 struct lo_inode *inode = lo_inode(req, ino); 956 ssize_t ret; 957 int saverr; 958 959 saverr = ENOSYS; 960 if (!lo_data(req)->xattr) 961 goto out; 962 963 if (lo_debug(req)) { 964 fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n", 965 ino, name, size); 966 } 967 968 sprintf(procname, "/proc/self/fd/%i", inode->fd); 969 970 if (size) { 971 value = malloc(size); 972 if (!value) 973 goto out_err; 974 975 ret = getxattr(procname, name, value, size); 976 if (ret == -1) 977 goto out_err; 978 saverr = 0; 979 if (ret == 0) 980 goto out; 981 982 fuse_reply_buf(req, value, ret); 983 } else { 984 ret = getxattr(procname, name, NULL, 0); 985 if (ret == -1) 986 goto out_err; 987 988 fuse_reply_xattr(req, ret); 989 } 990out_free: 991 free(value); 992 return; 993 994out_err: 995 saverr = errno; 996out: 997 fuse_reply_err(req, saverr); 998 goto out_free; 999} 1000 1001static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) 1002{ 1003 char *value = NULL; 1004 char procname[64]; 1005 struct lo_inode *inode = lo_inode(req, ino); 1006 ssize_t ret; 1007 int saverr; 1008 1009 saverr = ENOSYS; 1010 if (!lo_data(req)->xattr) 1011 goto out; 1012 1013 if (lo_debug(req)) { 1014 fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n", 1015 ino, size); 1016 } 1017 1018 sprintf(procname, "/proc/self/fd/%i", inode->fd); 1019 1020 if (size) { 1021 value = malloc(size); 1022 if (!value) 1023 goto out_err; 1024 1025 ret = listxattr(procname, value, size); 1026 if (ret == -1) 1027 goto out_err; 1028 saverr = 0; 1029 if (ret == 0) 1030 goto out; 1031 1032 fuse_reply_buf(req, value, ret); 1033 } else { 1034 ret = listxattr(procname, NULL, 0); 1035 if (ret == -1) 1036 goto out_err; 1037 1038 fuse_reply_xattr(req, ret); 1039 } 1040out_free: 1041 free(value); 1042 return; 1043 1044out_err: 1045 saverr = errno; 1046out: 1047 fuse_reply_err(req, saverr); 1048 goto out_free; 1049} 1050 1051static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, 1052 const char *value, size_t size, int flags) 1053{ 1054 char procname[64]; 1055 struct lo_inode *inode = lo_inode(req, ino); 1056 ssize_t ret; 1057 int saverr; 1058 1059 saverr = ENOSYS; 1060 if (!lo_data(req)->xattr) 1061 goto out; 1062 1063 if (lo_debug(req)) { 1064 fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n", 1065 ino, name, value, size); 1066 } 1067 1068 sprintf(procname, "/proc/self/fd/%i", inode->fd); 1069 1070 ret = setxattr(procname, name, value, size, flags); 1071 saverr = ret == -1 ? errno : 0; 1072 1073out: 1074 fuse_reply_err(req, saverr); 1075} 1076 1077static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) 1078{ 1079 char procname[64]; 1080 struct lo_inode *inode = lo_inode(req, ino); 1081 ssize_t ret; 1082 int saverr; 1083 1084 saverr = ENOSYS; 1085 if (!lo_data(req)->xattr) 1086 goto out; 1087 1088 if (lo_debug(req)) { 1089 fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n", 1090 ino, name); 1091 } 1092 1093 sprintf(procname, "/proc/self/fd/%i", inode->fd); 1094 1095 ret = removexattr(procname, name); 1096 saverr = ret == -1 ? errno : 0; 1097 1098out: 1099 fuse_reply_err(req, saverr); 1100} 1101 1102#ifdef HAVE_COPY_FILE_RANGE 1103static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, 1104 struct fuse_file_info *fi_in, 1105 fuse_ino_t ino_out, off_t off_out, 1106 struct fuse_file_info *fi_out, size_t len, 1107 int flags) 1108{ 1109 ssize_t res; 1110 1111 if (lo_debug(req)) 1112 fuse_log(FUSE_LOG_DEBUG, "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, " 1113 "off=%lu, ino=%" PRIu64 "/fd=%lu, " 1114 "off=%lu, size=%zd, flags=0x%x)\n", 1115 ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out, 1116 len, flags); 1117 1118 res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len, 1119 flags); 1120 if (res < 0) 1121 fuse_reply_err(req, errno); 1122 else 1123 fuse_reply_write(req, res); 1124} 1125#endif 1126 1127static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, 1128 struct fuse_file_info *fi) 1129{ 1130 off_t res; 1131 1132 (void)ino; 1133 res = lseek(fi->fh, off, whence); 1134 if (res != -1) 1135 fuse_reply_lseek(req, res); 1136 else 1137 fuse_reply_err(req, errno); 1138} 1139 1140static const struct fuse_lowlevel_ops lo_oper = { 1141 .init = lo_init, 1142 .destroy = lo_destroy, 1143 .lookup = lo_lookup, 1144 .mkdir = lo_mkdir, 1145 .mknod = lo_mknod, 1146 .symlink = lo_symlink, 1147 .link = lo_link, 1148 .unlink = lo_unlink, 1149 .rmdir = lo_rmdir, 1150 .rename = lo_rename, 1151 .forget = lo_forget, 1152 .forget_multi = lo_forget_multi, 1153 .getattr = lo_getattr, 1154 .setattr = lo_setattr, 1155 .readlink = lo_readlink, 1156 .opendir = lo_opendir, 1157 .readdir = lo_readdir, 1158 .readdirplus = lo_readdirplus, 1159 .releasedir = lo_releasedir, 1160 .fsyncdir = lo_fsyncdir, 1161 .create = lo_create, 1162 .open = lo_open, 1163 .release = lo_release, 1164 .flush = lo_flush, 1165 .fsync = lo_fsync, 1166 .read = lo_read, 1167 .write_buf = lo_write_buf, 1168 .statfs = lo_statfs, 1169 .fallocate = lo_fallocate, 1170 .flock = lo_flock, 1171 .getxattr = lo_getxattr, 1172 .listxattr = lo_listxattr, 1173 .setxattr = lo_setxattr, 1174 .removexattr = lo_removexattr, 1175#ifdef HAVE_COPY_FILE_RANGE 1176 .copy_file_range = lo_copy_file_range, 1177#endif 1178 .lseek = lo_lseek, 1179}; 1180 1181int main(int argc, char *argv[]) 1182{ 1183 struct fuse_args args = FUSE_ARGS_INIT(argc, argv); 1184 struct fuse_session *se; 1185 struct fuse_cmdline_opts opts; 1186 struct fuse_loop_config config; 1187 struct lo_data lo = { .debug = 0, 1188 .writeback = 0 }; 1189 int ret = -1; 1190 1191 /* Don't mask creation mode, kernel already did that */ 1192 umask(0); 1193 1194 pthread_mutex_init(&lo.mutex, NULL); 1195 lo.root.next = lo.root.prev = &lo.root; 1196 lo.root.fd = -1; 1197 lo.cache = CACHE_NORMAL; 1198 1199 if (fuse_parse_cmdline(&args, &opts) != 0) 1200 return 1; 1201 if (opts.show_help) { 1202 printf("usage: %s [options] <mountpoint>\n\n", argv[0]); 1203 fuse_cmdline_help(); 1204 fuse_lowlevel_help(); 1205 passthrough_ll_help(); 1206 ret = 0; 1207 goto err_out1; 1208 } else if (opts.show_version) { 1209 printf("FUSE library version %s\n", fuse_pkgversion()); 1210 fuse_lowlevel_version(); 1211 ret = 0; 1212 goto err_out1; 1213 } 1214 1215 if(opts.mountpoint == NULL) { 1216 printf("usage: %s [options] <mountpoint>\n", argv[0]); 1217 printf(" %s --help\n", argv[0]); 1218 ret = 1; 1219 goto err_out1; 1220 } 1221 1222 if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1) 1223 return 1; 1224 1225 lo.debug = opts.debug; 1226 lo.root.refcount = 2; 1227 if (lo.source) { 1228 struct stat stat; 1229 int res; 1230 1231 res = lstat(lo.source, &stat); 1232 if (res == -1) { 1233 fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n", 1234 lo.source); 1235 exit(1); 1236 } 1237 if (!S_ISDIR(stat.st_mode)) { 1238 fuse_log(FUSE_LOG_ERR, "source is not a directory\n"); 1239 exit(1); 1240 } 1241 1242 } else { 1243 lo.source = strdup("/"); 1244 if(!lo.source) { 1245 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); 1246 exit(1); 1247 } 1248 } 1249 if (!lo.timeout_set) { 1250 switch (lo.cache) { 1251 case CACHE_NEVER: 1252 lo.timeout = 0.0; 1253 break; 1254 1255 case CACHE_NORMAL: 1256 lo.timeout = 1.0; 1257 break; 1258 1259 case CACHE_ALWAYS: 1260 lo.timeout = 86400.0; 1261 break; 1262 } 1263 } else if (lo.timeout < 0) { 1264 fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n", 1265 lo.timeout); 1266 exit(1); 1267 } 1268 1269 lo.root.fd = open(lo.source, O_PATH); 1270 if (lo.root.fd == -1) { 1271 fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", 1272 lo.source); 1273 exit(1); 1274 } 1275 1276 se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); 1277 if (se == NULL) 1278 goto err_out1; 1279 1280 if (fuse_set_signal_handlers(se) != 0) 1281 goto err_out2; 1282 1283 if (fuse_session_mount(se, opts.mountpoint) != 0) 1284 goto err_out3; 1285 1286 fuse_daemonize(opts.foreground); 1287 1288 /* Block until ctrl+c or fusermount -u */ 1289 if (opts.singlethread) 1290 ret = fuse_session_loop(se); 1291 else { 1292 config.clone_fd = opts.clone_fd; 1293 config.max_idle_threads = opts.max_idle_threads; 1294 ret = fuse_session_loop_mt(se, &config); 1295 } 1296 1297 fuse_session_unmount(se); 1298err_out3: 1299 fuse_remove_signal_handlers(se); 1300err_out2: 1301 fuse_session_destroy(se); 1302err_out1: 1303 free(opts.mountpoint); 1304 fuse_opt_free_args(&args); 1305 1306 if (lo.root.fd >= 0) 1307 close(lo.root.fd); 1308 1309 free(lo.source); 1310 return ret ? 1 : 0; 1311} 1312