1/* 2 FUSE: Filesystem in Userspace 3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> 4 Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org> 5 6 This program can be distributed under the terms of the GNU GPLv2. 7 See the file COPYING. 8*/ 9 10/** @file 11 * 12 * This file system mirrors the existing file system hierarchy of the 13 * system, starting at the root file system. This is implemented by 14 * just "passing through" all requests to the corresponding user-space 15 * libc functions. Its performance is terrible. 16 * 17 * Compile with 18 * 19 * gcc -Wall passthrough.c `pkg-config fuse3 --cflags --libs` -o passthrough 20 * 21 * ## Source code ## 22 * \include passthrough.c 23 */ 24 25 26#define FUSE_USE_VERSION 31 27 28#define _GNU_SOURCE 29 30#ifdef linux 31/* For pread()/pwrite()/utimensat() */ 32#define _XOPEN_SOURCE 700 33#endif 34 35#include <fuse.h> 36#include <stdio.h> 37#include <string.h> 38#include <unistd.h> 39#include <fcntl.h> 40#include <sys/stat.h> 41#include <dirent.h> 42#include <errno.h> 43#ifdef __FreeBSD__ 44#include <sys/socket.h> 45#include <sys/un.h> 46#endif 47#include <sys/time.h> 48#ifdef HAVE_SETXATTR 49#include <sys/xattr.h> 50#endif 51 52#include "passthrough_helpers.h" 53 54static int fill_dir_plus = 0; 55 56static void *xmp_init(struct fuse_conn_info *conn, 57 struct fuse_config *cfg) 58{ 59 (void) conn; 60 cfg->use_ino = 1; 61 62 /* Pick up changes from lower filesystem right away. This is 63 also necessary for better hardlink support. When the kernel 64 calls the unlink() handler, it does not know the inode of 65 the to-be-removed entry and can therefore not invalidate 66 the cache of the associated inode - resulting in an 67 incorrect st_nlink value being reported for any remaining 68 hardlinks to this inode. */ 69 cfg->entry_timeout = 0; 70 cfg->attr_timeout = 0; 71 cfg->negative_timeout = 0; 72 73 return NULL; 74} 75 76static int xmp_getattr(const char *path, struct stat *stbuf, 77 struct fuse_file_info *fi) 78{ 79 (void) fi; 80 int res; 81 82 res = lstat(path, stbuf); 83 if (res == -1) 84 return -errno; 85 86 return 0; 87} 88 89static int xmp_access(const char *path, int mask) 90{ 91 int res; 92 93 res = access(path, mask); 94 if (res == -1) 95 return -errno; 96 97 return 0; 98} 99 100static int xmp_readlink(const char *path, char *buf, size_t size) 101{ 102 int res; 103 104 res = readlink(path, buf, size - 1); 105 if (res == -1) 106 return -errno; 107 108 buf[res] = '\0'; 109 return 0; 110} 111 112 113static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, 114 off_t offset, struct fuse_file_info *fi, 115 enum fuse_readdir_flags flags) 116{ 117 DIR *dp; 118 struct dirent *de; 119 120 (void) offset; 121 (void) fi; 122 (void) flags; 123 124 dp = opendir(path); 125 if (dp == NULL) 126 return -errno; 127 128 while ((de = readdir(dp)) != NULL) { 129 struct stat st; 130 memset(&st, 0, sizeof(st)); 131 st.st_ino = de->d_ino; 132 st.st_mode = de->d_type << 12; 133 if (filler(buf, de->d_name, &st, 0, fill_dir_plus)) 134 break; 135 } 136 137 closedir(dp); 138 return 0; 139} 140 141static int xmp_mknod(const char *path, mode_t mode, dev_t rdev) 142{ 143 int res; 144 145 res = mknod_wrapper(AT_FDCWD, path, NULL, mode, rdev); 146 if (res == -1) 147 return -errno; 148 149 return 0; 150} 151 152static int xmp_mkdir(const char *path, mode_t mode) 153{ 154 int res; 155 156 res = mkdir(path, mode); 157 if (res == -1) 158 return -errno; 159 160 return 0; 161} 162 163static int xmp_unlink(const char *path) 164{ 165 int res; 166 167 res = unlink(path); 168 if (res == -1) 169 return -errno; 170 171 return 0; 172} 173 174static int xmp_rmdir(const char *path) 175{ 176 int res; 177 178 res = rmdir(path); 179 if (res == -1) 180 return -errno; 181 182 return 0; 183} 184 185static int xmp_symlink(const char *from, const char *to) 186{ 187 int res; 188 189 res = symlink(from, to); 190 if (res == -1) 191 return -errno; 192 193 return 0; 194} 195 196static int xmp_rename(const char *from, const char *to, unsigned int flags) 197{ 198 int res; 199 200 if (flags) 201 return -EINVAL; 202 203 res = rename(from, to); 204 if (res == -1) 205 return -errno; 206 207 return 0; 208} 209 210static int xmp_link(const char *from, const char *to) 211{ 212 int res; 213 214 res = link(from, to); 215 if (res == -1) 216 return -errno; 217 218 return 0; 219} 220 221static int xmp_chmod(const char *path, mode_t mode, 222 struct fuse_file_info *fi) 223{ 224 (void) fi; 225 int res; 226 227 res = chmod(path, mode); 228 if (res == -1) 229 return -errno; 230 231 return 0; 232} 233 234static int xmp_chown(const char *path, uid_t uid, gid_t gid, 235 struct fuse_file_info *fi) 236{ 237 (void) fi; 238 int res; 239 240 res = lchown(path, uid, gid); 241 if (res == -1) 242 return -errno; 243 244 return 0; 245} 246 247static int xmp_truncate(const char *path, off_t size, 248 struct fuse_file_info *fi) 249{ 250 int res; 251 252 if (fi != NULL) 253 res = ftruncate(fi->fh, size); 254 else 255 res = truncate(path, size); 256 if (res == -1) 257 return -errno; 258 259 return 0; 260} 261 262#ifdef HAVE_UTIMENSAT 263static int xmp_utimens(const char *path, const struct timespec ts[2], 264 struct fuse_file_info *fi) 265{ 266 (void) fi; 267 int res; 268 269 /* don't use utime/utimes since they follow symlinks */ 270 res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW); 271 if (res == -1) 272 return -errno; 273 274 return 0; 275} 276#endif 277 278static int xmp_create(const char *path, mode_t mode, 279 struct fuse_file_info *fi) 280{ 281 int res; 282 283 res = open(path, fi->flags, mode); 284 if (res == -1) 285 return -errno; 286 287 fi->fh = res; 288 return 0; 289} 290 291static int xmp_open(const char *path, struct fuse_file_info *fi) 292{ 293 int res; 294 295 res = open(path, fi->flags); 296 if (res == -1) 297 return -errno; 298 299 fi->fh = res; 300 return 0; 301} 302 303static int xmp_read(const char *path, char *buf, size_t size, off_t offset, 304 struct fuse_file_info *fi) 305{ 306 int fd; 307 int res; 308 309 if(fi == NULL) 310 fd = open(path, O_RDONLY); 311 else 312 fd = fi->fh; 313 314 if (fd == -1) 315 return -errno; 316 317 res = pread(fd, buf, size, offset); 318 if (res == -1) 319 res = -errno; 320 321 if(fi == NULL) 322 close(fd); 323 return res; 324} 325 326static int xmp_write(const char *path, const char *buf, size_t size, 327 off_t offset, struct fuse_file_info *fi) 328{ 329 int fd; 330 int res; 331 332 (void) fi; 333 if(fi == NULL) 334 fd = open(path, O_WRONLY); 335 else 336 fd = fi->fh; 337 338 if (fd == -1) 339 return -errno; 340 341 res = pwrite(fd, buf, size, offset); 342 if (res == -1) 343 res = -errno; 344 345 if(fi == NULL) 346 close(fd); 347 return res; 348} 349 350static int xmp_statfs(const char *path, struct statvfs *stbuf) 351{ 352 int res; 353 354 res = statvfs(path, stbuf); 355 if (res == -1) 356 return -errno; 357 358 return 0; 359} 360 361static int xmp_release(const char *path, struct fuse_file_info *fi) 362{ 363 (void) path; 364 close(fi->fh); 365 return 0; 366} 367 368static int xmp_fsync(const char *path, int isdatasync, 369 struct fuse_file_info *fi) 370{ 371 /* Just a stub. This method is optional and can safely be left 372 unimplemented */ 373 374 (void) path; 375 (void) isdatasync; 376 (void) fi; 377 return 0; 378} 379 380#ifdef HAVE_POSIX_FALLOCATE 381static int xmp_fallocate(const char *path, int mode, 382 off_t offset, off_t length, struct fuse_file_info *fi) 383{ 384 int fd; 385 int res; 386 387 (void) fi; 388 389 if (mode) 390 return -EOPNOTSUPP; 391 392 if(fi == NULL) 393 fd = open(path, O_WRONLY); 394 else 395 fd = fi->fh; 396 397 if (fd == -1) 398 return -errno; 399 400 res = -posix_fallocate(fd, offset, length); 401 402 if(fi == NULL) 403 close(fd); 404 return res; 405} 406#endif 407 408#ifdef HAVE_SETXATTR 409/* xattr operations are optional and can safely be left unimplemented */ 410static int xmp_setxattr(const char *path, const char *name, const char *value, 411 size_t size, int flags) 412{ 413 int res = lsetxattr(path, name, value, size, flags); 414 if (res == -1) 415 return -errno; 416 return 0; 417} 418 419static int xmp_getxattr(const char *path, const char *name, char *value, 420 size_t size) 421{ 422 int res = lgetxattr(path, name, value, size); 423 if (res == -1) 424 return -errno; 425 return res; 426} 427 428static int xmp_listxattr(const char *path, char *list, size_t size) 429{ 430 int res = llistxattr(path, list, size); 431 if (res == -1) 432 return -errno; 433 return res; 434} 435 436static int xmp_removexattr(const char *path, const char *name) 437{ 438 int res = lremovexattr(path, name); 439 if (res == -1) 440 return -errno; 441 return 0; 442} 443#endif /* HAVE_SETXATTR */ 444 445#ifdef HAVE_COPY_FILE_RANGE 446static ssize_t xmp_copy_file_range(const char *path_in, 447 struct fuse_file_info *fi_in, 448 off_t offset_in, const char *path_out, 449 struct fuse_file_info *fi_out, 450 off_t offset_out, size_t len, int flags) 451{ 452 int fd_in, fd_out; 453 ssize_t res; 454 455 if(fi_in == NULL) 456 fd_in = open(path_in, O_RDONLY); 457 else 458 fd_in = fi_in->fh; 459 460 if (fd_in == -1) 461 return -errno; 462 463 if(fi_out == NULL) 464 fd_out = open(path_out, O_WRONLY); 465 else 466 fd_out = fi_out->fh; 467 468 if (fd_out == -1) { 469 close(fd_in); 470 return -errno; 471 } 472 473 res = copy_file_range(fd_in, &offset_in, fd_out, &offset_out, len, 474 flags); 475 if (res == -1) 476 res = -errno; 477 478 if (fi_out == NULL) 479 close(fd_out); 480 if (fi_in == NULL) 481 close(fd_in); 482 483 return res; 484} 485#endif 486 487static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi) 488{ 489 int fd; 490 off_t res; 491 492 if (fi == NULL) 493 fd = open(path, O_RDONLY); 494 else 495 fd = fi->fh; 496 497 if (fd == -1) 498 return -errno; 499 500 res = lseek(fd, off, whence); 501 if (res == -1) 502 res = -errno; 503 504 if (fi == NULL) 505 close(fd); 506 return res; 507} 508 509static const struct fuse_operations xmp_oper = { 510 .init = xmp_init, 511 .getattr = xmp_getattr, 512 .access = xmp_access, 513 .readlink = xmp_readlink, 514 .readdir = xmp_readdir, 515 .mknod = xmp_mknod, 516 .mkdir = xmp_mkdir, 517 .symlink = xmp_symlink, 518 .unlink = xmp_unlink, 519 .rmdir = xmp_rmdir, 520 .rename = xmp_rename, 521 .link = xmp_link, 522 .chmod = xmp_chmod, 523 .chown = xmp_chown, 524 .truncate = xmp_truncate, 525#ifdef HAVE_UTIMENSAT 526 .utimens = xmp_utimens, 527#endif 528 .open = xmp_open, 529 .create = xmp_create, 530 .read = xmp_read, 531 .write = xmp_write, 532 .statfs = xmp_statfs, 533 .release = xmp_release, 534 .fsync = xmp_fsync, 535#ifdef HAVE_POSIX_FALLOCATE 536 .fallocate = xmp_fallocate, 537#endif 538#ifdef HAVE_SETXATTR 539 .setxattr = xmp_setxattr, 540 .getxattr = xmp_getxattr, 541 .listxattr = xmp_listxattr, 542 .removexattr = xmp_removexattr, 543#endif 544#ifdef HAVE_COPY_FILE_RANGE 545 .copy_file_range = xmp_copy_file_range, 546#endif 547 .lseek = xmp_lseek, 548}; 549 550int main(int argc, char *argv[]) 551{ 552 enum { MAX_ARGS = 10 }; 553 int i,new_argc; 554 char *new_argv[MAX_ARGS]; 555 556 umask(0); 557 /* Process the "--plus" option apart */ 558 for (i=0, new_argc=0; (i<argc) && (new_argc<MAX_ARGS); i++) { 559 if (!strcmp(argv[i], "--plus")) { 560 fill_dir_plus = FUSE_FILL_DIR_PLUS; 561 } else { 562 new_argv[new_argc++] = argv[i]; 563 } 564 } 565 return fuse_main(new_argc, new_argv, &xmp_oper, NULL); 566} 567