/filesystems/loopback/loopback.c
C | 756 lines | 576 code | 158 blank | 22 comment | 110 complexity | 017ce370291472d6f69b51ececc259da MD5 | raw file
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 GPL. 6 See the file COPYING. 7 8*/ 9 10/* 11 * Loopback MacFUSE file system in C. Uses the high-level FUSE API. 12 * Based on the fusexmp_fh.c example from the Linux FUSE distribution. 13 * Amit Singh <http://osxbook.com> 14 */ 15 16#include <AvailabilityMacros.h> 17 18#if !defined(AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER) 19#error "This file system requires Leopard and above." 20#endif 21 22#define FUSE_USE_VERSION 26 23 24#define _GNU_SOURCE 25 26#include <fuse.h> 27#include <stdio.h> 28#include <stdlib.h> 29#include <string.h> 30#include <unistd.h> 31#include <fcntl.h> 32#include <dirent.h> 33#include <errno.h> 34#include <sys/time.h> 35#include <sys/xattr.h> 36#include <sys/attr.h> 37#include <sys/param.h> 38 39#if defined(_POSIX_C_SOURCE) 40typedef unsigned char u_char; 41typedef unsigned short u_short; 42typedef unsigned int u_int; 43typedef unsigned long u_long; 44#endif 45 46#define G_PREFIX "org" 47#define G_KAUTH_FILESEC_XATTR G_PREFIX ".apple.system.Security" 48#define A_PREFIX "com" 49#define A_KAUTH_FILESEC_XATTR A_PREFIX ".apple.system.Security" 50#define XATTR_APPLE_PREFIX "com.apple." 51 52static int 53loopback_getattr(const char *path, struct stat *stbuf) 54{ 55 int res; 56 57 res = lstat(path, stbuf); 58 if (res == -1) { 59 return -errno; 60 } 61 62 return 0; 63} 64 65static int 66loopback_fgetattr(const char *path, struct stat *stbuf, 67 struct fuse_file_info *fi) 68{ 69 int res; 70 71 (void)path; 72 73 res = fstat(fi->fh, stbuf); 74 if (res == -1) { 75 return -errno; 76 } 77 78 return 0; 79} 80 81static int 82loopback_readlink(const char *path, char *buf, size_t size) 83{ 84 int res; 85 86 res = readlink(path, buf, size - 1); 87 if (res == -1) { 88 return -errno; 89 } 90 91 buf[res] = '\0'; 92 93 return 0; 94} 95 96struct loopback_dirp { 97 DIR *dp; 98 struct dirent *entry; 99 off_t offset; 100}; 101 102static int 103loopback_opendir(const char *path, struct fuse_file_info *fi) 104{ 105 int res; 106 107 struct loopback_dirp *d = malloc(sizeof(struct loopback_dirp)); 108 if (d == NULL) { 109 return -ENOMEM; 110 } 111 112 d->dp = opendir(path); 113 if (d->dp == NULL) { 114 res = -errno; 115 free(d); 116 return res; 117 } 118 119 d->offset = 0; 120 d->entry = NULL; 121 122 fi->fh = (unsigned long)d; 123 124 return 0; 125} 126 127static inline struct loopback_dirp * 128get_dirp(struct fuse_file_info *fi) 129{ 130 return (struct loopback_dirp *)(uintptr_t)fi->fh; 131} 132 133static int 134loopback_readdir(const char *path, void *buf, fuse_fill_dir_t filler, 135 off_t offset, struct fuse_file_info *fi) 136{ 137 struct loopback_dirp *d = get_dirp(fi); 138 139 (void)path; 140 141 if (offset != d->offset) { 142 seekdir(d->dp, offset); 143 d->entry = NULL; 144 d->offset = offset; 145 } 146 147 while (1) { 148 struct stat st; 149 off_t nextoff; 150 151 if (!d->entry) { 152 d->entry = readdir(d->dp); 153 if (!d->entry) { 154 break; 155 } 156 } 157 158 memset(&st, 0, sizeof(st)); 159 st.st_ino = d->entry->d_ino; 160 st.st_mode = d->entry->d_type << 12; 161 nextoff = telldir(d->dp); 162 if (filler(buf, d->entry->d_name, &st, nextoff)) { 163 break; 164 } 165 166 d->entry = NULL; 167 d->offset = nextoff; 168 } 169 170 return 0; 171} 172 173static int 174loopback_releasedir(const char *path, struct fuse_file_info *fi) 175{ 176 struct loopback_dirp *d = get_dirp(fi); 177 178 (void)path; 179 180 closedir(d->dp); 181 free(d); 182 183 return 0; 184} 185 186static int 187loopback_mknod(const char *path, mode_t mode, dev_t rdev) 188{ 189 int res; 190 191 if (S_ISFIFO(mode)) { 192 res = mkfifo(path, mode); 193 } else { 194 res = mknod(path, mode, rdev); 195 } 196 197 if (res == -1) { 198 return -errno; 199 } 200 201 return 0; 202} 203 204static int 205loopback_mkdir(const char *path, mode_t mode) 206{ 207 int res; 208 209 res = mkdir(path, mode); 210 if (res == -1) { 211 return -errno; 212 } 213 214 return 0; 215} 216 217static int 218loopback_unlink(const char *path) 219{ 220 int res; 221 222 res = unlink(path); 223 if (res == -1) { 224 return -errno; 225 } 226 227 return 0; 228} 229 230static int 231loopback_rmdir(const char *path) 232{ 233 int res; 234 235 res = rmdir(path); 236 if (res == -1) { 237 return -errno; 238 } 239 240 return 0; 241} 242 243static int 244loopback_symlink(const char *from, const char *to) 245{ 246 int res; 247 248 res = symlink(from, to); 249 if (res == -1) { 250 return -errno; 251 } 252 253 return 0; 254} 255 256static int 257loopback_rename(const char *from, const char *to) 258{ 259 int res; 260 261 res = rename(from, to); 262 if (res == -1) { 263 return -errno; 264 } 265 266 return 0; 267} 268 269static int 270loopback_exchange(const char *path1, const char *path2, unsigned long options) 271{ 272 int res; 273 274 res = exchangedata(path1, path2, options); 275 if (res == -1) { 276 return -errno; 277 } 278 279 return 0; 280} 281 282static int 283loopback_link(const char *from, const char *to) 284{ 285 int res; 286 287 res = link(from, to); 288 if (res == -1) { 289 return -errno; 290 } 291 292 return 0; 293} 294 295static int 296loopback_fsetattr_x(const char *path, struct setattr_x *attr, 297 struct fuse_file_info *fi) 298{ 299 int res; 300 uid_t uid = -1; 301 gid_t gid = -1; 302 303 if (SETATTR_WANTS_MODE(attr)) { 304 res = lchmod(path, attr->mode); 305 if (res == -1) { 306 return -errno; 307 } 308 } 309 310 if (SETATTR_WANTS_UID(attr)) { 311 uid = attr->uid; 312 } 313 314 if (SETATTR_WANTS_GID(attr)) { 315 gid = attr->gid; 316 } 317 318 if ((uid != -1) || (gid != -1)) { 319 res = lchown(path, uid, gid); 320 if (res == -1) { 321 return -errno; 322 } 323 } 324 325 if (SETATTR_WANTS_SIZE(attr)) { 326 if (fi) { 327 res = ftruncate(fi->fh, attr->size); 328 } else { 329 res = truncate(path, attr->size); 330 } 331 if (res == -1) { 332 return -errno; 333 } 334 } 335 336 if (SETATTR_WANTS_MODTIME(attr)) { 337 struct timeval tv[2]; 338 if (!SETATTR_WANTS_ACCTIME(attr)) { 339 gettimeofday(&tv[0], NULL); 340 } else { 341 tv[0].tv_sec = attr->acctime.tv_sec; 342 tv[0].tv_usec = attr->acctime.tv_nsec / 1000; 343 } 344 tv[1].tv_sec = attr->modtime.tv_sec; 345 tv[1].tv_usec = attr->modtime.tv_nsec / 1000; 346 res = utimes(path, tv); 347 if (res == -1) { 348 return -errno; 349 } 350 } 351 352 if (SETATTR_WANTS_CRTIME(attr)) { 353 struct attrlist attributes; 354 355 attributes.bitmapcount = ATTR_BIT_MAP_COUNT; 356 attributes.reserved = 0; 357 attributes.commonattr = ATTR_CMN_CRTIME; 358 attributes.dirattr = 0; 359 attributes.fileattr = 0; 360 attributes.forkattr = 0; 361 attributes.volattr = 0; 362 363 res = setattrlist(path, &attributes, &attr->crtime, 364 sizeof(struct timespec), FSOPT_NOFOLLOW); 365 366 if (res == -1) { 367 return -errno; 368 } 369 } 370 371 if (SETATTR_WANTS_CHGTIME(attr)) { 372 struct attrlist attributes; 373 374 attributes.bitmapcount = ATTR_BIT_MAP_COUNT; 375 attributes.reserved = 0; 376 attributes.commonattr = ATTR_CMN_CHGTIME; 377 attributes.dirattr = 0; 378 attributes.fileattr = 0; 379 attributes.forkattr = 0; 380 attributes.volattr = 0; 381 382 res = setattrlist(path, &attributes, &attr->chgtime, 383 sizeof(struct timespec), FSOPT_NOFOLLOW); 384 385 if (res == -1) { 386 return -errno; 387 } 388 } 389 390 if (SETATTR_WANTS_BKUPTIME(attr)) { 391 struct attrlist attributes; 392 393 attributes.bitmapcount = ATTR_BIT_MAP_COUNT; 394 attributes.reserved = 0; 395 attributes.commonattr = ATTR_CMN_BKUPTIME; 396 attributes.dirattr = 0; 397 attributes.fileattr = 0; 398 attributes.forkattr = 0; 399 attributes.volattr = 0; 400 401 res = setattrlist(path, &attributes, &attr->bkuptime, 402 sizeof(struct timespec), FSOPT_NOFOLLOW); 403 404 if (res == -1) { 405 return -errno; 406 } 407 } 408 409 if (SETATTR_WANTS_FLAGS(attr)) { 410 res = lchflags(path, attr->flags); 411 if (res == -1) { 412 return -errno; 413 } 414 } 415 416 return 0; 417} 418 419static int 420loopback_setattr_x(const char *path, struct setattr_x *attr) 421{ 422 return loopback_fsetattr_x(path, attr, (struct fuse_file_info *)0); 423} 424 425static int 426loopback_getxtimes(const char *path, struct timespec *bkuptime, 427 struct timespec *crtime) 428{ 429 int res = 0; 430 struct attrlist attributes; 431 432 attributes.bitmapcount = ATTR_BIT_MAP_COUNT; 433 attributes.reserved = 0; 434 attributes.commonattr = 0; 435 attributes.dirattr = 0; 436 attributes.fileattr = 0; 437 attributes.forkattr = 0; 438 attributes.volattr = 0; 439 440 441 442 struct xtimeattrbuf { 443 uint32_t size; 444 struct timespec xtime; 445 } __attribute__ ((packed)); 446 447 448 struct xtimeattrbuf buf; 449 450 attributes.commonattr = ATTR_CMN_BKUPTIME; 451 res = getattrlist(path, &attributes, &buf, sizeof(buf), FSOPT_NOFOLLOW); 452 if (res == 0) { 453 (void)memcpy(bkuptime, &(buf.xtime), sizeof(struct timespec)); 454 } else { 455 (void)memset(bkuptime, 0, sizeof(struct timespec)); 456 } 457 458 attributes.commonattr = ATTR_CMN_CRTIME; 459 res = getattrlist(path, &attributes, &buf, sizeof(buf), FSOPT_NOFOLLOW); 460 if (res == 0) { 461 (void)memcpy(crtime, &(buf.xtime), sizeof(struct timespec)); 462 } else { 463 (void)memset(crtime, 0, sizeof(struct timespec)); 464 } 465 466 return 0; 467} 468 469static int 470loopback_create(const char *path, mode_t mode, struct fuse_file_info *fi) 471{ 472 int fd; 473 474 fd = open(path, fi->flags, mode); 475 if (fd == -1) { 476 return -errno; 477 } 478 479 fi->fh = fd; 480 return 0; 481} 482 483static int 484loopback_open(const char *path, struct fuse_file_info *fi) 485{ 486 int fd; 487 488 fd = open(path, fi->flags); 489 if (fd == -1) { 490 return -errno; 491 } 492 493 fi->fh = fd; 494 return 0; 495} 496 497static int 498loopback_read(const char *path, char *buf, size_t size, off_t offset, 499 struct fuse_file_info *fi) 500{ 501 int res; 502 503 (void)path; 504 res = pread(fi->fh, buf, size, offset); 505 if (res == -1) { 506 res = -errno; 507 } 508 509 return res; 510} 511 512static int 513loopback_write(const char *path, const char *buf, size_t size, 514 off_t offset, struct fuse_file_info *fi) 515{ 516 int res; 517 518 (void)path; 519 520 res = pwrite(fi->fh, buf, size, offset); 521 if (res == -1) { 522 res = -errno; 523 } 524 525 return res; 526} 527 528static int 529loopback_statfs(const char *path, struct statvfs *stbuf) 530{ 531 int res; 532 533 res = statvfs(path, stbuf); 534 if (res == -1) { 535 return -errno; 536 } 537 538 return 0; 539} 540 541static int 542loopback_flush(const char *path, struct fuse_file_info *fi) 543{ 544 int res; 545 546 (void)path; 547 548 res = close(dup(fi->fh)); 549 if (res == -1) { 550 return -errno; 551 } 552 553 return 0; 554} 555 556static int 557loopback_release(const char *path, struct fuse_file_info *fi) 558{ 559 (void)path; 560 561 close(fi->fh); 562 563 return 0; 564} 565 566static int 567loopback_fsync(const char *path, int isdatasync, struct fuse_file_info *fi) 568{ 569 int res; 570 571 (void)path; 572 573 (void)isdatasync; 574 575 res = fsync(fi->fh); 576 if (res == -1) { 577 return -errno; 578 } 579 580 return 0; 581} 582 583static int 584loopback_setxattr(const char *path, const char *name, const char *value, 585 size_t size, int flags, uint32_t position) 586{ 587 int res; 588 589 if (!strncmp(name, XATTR_APPLE_PREFIX, sizeof(XATTR_APPLE_PREFIX) - 1)) { 590 flags &= ~(XATTR_NOSECURITY); 591 } 592 593 if (!strcmp(name, A_KAUTH_FILESEC_XATTR)) { 594 595 char new_name[MAXPATHLEN]; 596 597 memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR)); 598 memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1); 599 600 res = setxattr(path, new_name, value, size, position, flags); 601 602 } else { 603 res = setxattr(path, name, value, size, position, flags); 604 } 605 606 if (res == -1) { 607 return -errno; 608 } 609 610 return 0; 611} 612 613static int 614loopback_getxattr(const char *path, const char *name, char *value, size_t size, 615 uint32_t position) 616{ 617 int res; 618 619 if (strcmp(name, A_KAUTH_FILESEC_XATTR) == 0) { 620 621 char new_name[MAXPATHLEN]; 622 623 memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR)); 624 memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1); 625 626 res = getxattr(path, new_name, value, size, position, XATTR_NOFOLLOW); 627 628 } else { 629 res = getxattr(path, name, value, size, position, XATTR_NOFOLLOW); 630 } 631 632 if (res == -1) { 633 return -errno; 634 } 635 636 return res; 637} 638 639static int 640loopback_listxattr(const char *path, char *list, size_t size) 641{ 642 ssize_t res = listxattr(path, list, size, XATTR_NOFOLLOW); 643 if (res > 0) { 644 if (list) { 645 size_t len = 0; 646 char *curr = list; 647 do { 648 size_t thislen = strlen(curr) + 1; 649 if (strcmp(curr, G_KAUTH_FILESEC_XATTR) == 0) { 650 memmove(curr, curr + thislen, res - len - thislen); 651 res -= thislen; 652 break; 653 } 654 curr += thislen; 655 len += thislen; 656 } while (len < res); 657 } else { 658 /* 659 ssize_t res2 = getxattr(path, G_KAUTH_FILESEC_XATTR, NULL, 0, 0, 660 XATTR_NOFOLLOW); 661 if (res2 >= 0) { 662 res -= sizeof(G_KAUTH_FILESEC_XATTR); 663 } 664 */ 665 } 666 } 667 668 if (res == -1) { 669 return -errno; 670 } 671 672 return res; 673} 674 675static int 676loopback_removexattr(const char *path, const char *name) 677{ 678 int res; 679 680 if (strcmp(name, A_KAUTH_FILESEC_XATTR) == 0) { 681 682 char new_name[MAXPATHLEN]; 683 684 memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR)); 685 memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1); 686 687 res = removexattr(path, new_name, XATTR_NOFOLLOW); 688 689 } else { 690 res = removexattr(path, name, XATTR_NOFOLLOW); 691 } 692 693 if (res == -1) { 694 return -errno; 695 } 696 697 return 0; 698} 699 700void * 701loopback_init(struct fuse_conn_info *conn) 702{ 703 FUSE_ENABLE_SETVOLNAME(conn); 704 FUSE_ENABLE_XTIMES(conn); 705 706 return NULL; 707} 708 709void 710loopback_destroy(void *userdata) 711{ 712 /* nothing */ 713} 714 715static struct fuse_operations loopback_oper = { 716 .init = loopback_init, 717 .destroy = loopback_destroy, 718 .getattr = loopback_getattr, 719 .fgetattr = loopback_fgetattr, 720/* .access = loopback_access, */ 721 .readlink = loopback_readlink, 722 .opendir = loopback_opendir, 723 .readdir = loopback_readdir, 724 .releasedir = loopback_releasedir, 725 .mknod = loopback_mknod, 726 .mkdir = loopback_mkdir, 727 .symlink = loopback_symlink, 728 .unlink = loopback_unlink, 729 .rmdir = loopback_rmdir, 730 .rename = loopback_rename, 731 .link = loopback_link, 732 .create = loopback_create, 733 .open = loopback_open, 734 .read = loopback_read, 735 .write = loopback_write, 736 .statfs = loopback_statfs, 737 .flush = loopback_flush, 738 .release = loopback_release, 739 .fsync = loopback_fsync, 740 .setxattr = loopback_setxattr, 741 .getxattr = loopback_getxattr, 742 .listxattr = loopback_listxattr, 743 .removexattr = loopback_removexattr, 744 .exchange = loopback_exchange, 745 .getxtimes = loopback_getxtimes, 746 .setattr_x = loopback_setattr_x, 747 .fsetattr_x = loopback_fsetattr_x, 748}; 749 750int 751main(int argc, char *argv[]) 752{ 753 umask(0); 754 755 return fuse_main(argc, argv, &loopback_oper, NULL); 756}