1 /* $NetBSD: refuse.c,v 1.96 2012/12/30 10:04:22 tron Exp $ */ 2 3 /* 4 * Copyright � 2007 Alistair Crooks. All rights reserved. 5 * Copyright � 2007 Antti Kantee. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote 16 * products derived from this software without specific prior written 17 * permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 25 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #if !defined(lint) 34 __RCSID("$NetBSD: refuse.c,v 1.96 2012/12/30 10:04:22 tron Exp $"); 35 #endif /* !lint */ 36 37 #include <sys/types.h> 38 39 #include <assert.h> 40 #include <err.h> 41 #include <errno.h> 42 #include <fuse.h> 43 #include <paths.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <unistd.h> 48 #ifdef MULTITHREADED_REFUSE 49 #include <pthread.h> 50 #endif 51 52 typedef uint64_t fuse_ino_t; 53 54 struct fuse_config { 55 uid_t uid; 56 gid_t gid; 57 mode_t umask; 58 double entry_timeout; 59 double negative_timeout; 60 double attr_timeout; 61 double ac_attr_timeout; 62 int ac_attr_timeout_set; 63 int debug; 64 int hard_remove; 65 int use_ino; 66 int readdir_ino; 67 int set_mode; 68 int set_uid; 69 int set_gid; 70 int direct_io; 71 int kernel_cache; 72 int auto_cache; 73 int intr; 74 int intr_signal; 75 }; 76 77 struct fuse_chan { 78 const char *dir; 79 struct fuse_args *args; 80 struct puffs_usermount *pu; 81 int dead; 82 }; 83 84 /* this is the private fuse structure */ 85 struct fuse { 86 struct fuse_chan *fc; /* fuse channel pointer */ 87 struct fuse_operations op; /* switch table of operations */ 88 int compat; /* compat level - 89 * not used in puffs_fuse */ 90 struct node **name_table; 91 size_t name_table_size; 92 struct node **id_table; 93 size_t id_table_size; 94 fuse_ino_t ctr; 95 unsigned int generation; 96 unsigned int hidectr; 97 #if !defined(__minix) 98 pthread_mutex_t lock; 99 pthread_rwlock_t tree_lock; 100 #endif /* !defined(__minix) */ 101 void *user_data; 102 struct fuse_config conf; 103 int intr_installed; 104 }; 105 106 struct puffs_fuse_dirh { 107 void *dbuf; 108 struct dirent *d; 109 110 size_t reslen; 111 size_t bufsize; 112 }; 113 114 struct refusenode { 115 struct fuse_file_info file_info; 116 struct puffs_fuse_dirh dirh; 117 int opencount; 118 int flags; 119 }; 120 #define RN_ROOT 0x01 121 #define RN_OPEN 0x02 /* XXX: could just use opencount */ 122 123 static int fuse_setattr(struct fuse *, struct puffs_node *, 124 const char *, const struct vattr *); 125 126 static struct puffs_node * 127 newrn(struct puffs_usermount *pu) 128 { 129 struct puffs_node *pn; 130 struct refusenode *rn; 131 132 if ((rn = calloc(1, sizeof(*rn))) == NULL) { 133 err(EXIT_FAILURE, "newrn"); 134 } 135 pn = puffs_pn_new(pu, rn); 136 137 return pn; 138 } 139 140 static void 141 nukern(struct puffs_node *pn) 142 { 143 struct refusenode *rn = pn->pn_data; 144 145 free(rn->dirh.dbuf); 146 free(rn); 147 puffs_pn_put(pn); 148 } 149 150 /* XXX - not threadsafe */ 151 static ino_t fakeino = 3; 152 153 /***************** start of pthread context routines ************************/ 154 155 /* 156 * Notes on fuse_context: 157 * we follow fuse's lead and use the pthread specific information to hold 158 * a reference to the fuse_context structure for this thread. 159 */ 160 #ifdef MULTITHREADED_REFUSE 161 static pthread_mutex_t context_mutex = PTHREAD_MUTEX_INITIALIZER; 162 static pthread_key_t context_key; 163 static unsigned long context_refc; 164 #endif 165 166 /* return the fuse_context struct related to this thread */ 167 struct fuse_context * 168 fuse_get_context(void) 169 { 170 #ifdef MULTITHREADED_REFUSE 171 struct fuse_context *ctxt; 172 173 if ((ctxt = pthread_getspecific(context_key)) == NULL) { 174 if ((ctxt = calloc(1, sizeof(struct fuse_context))) == NULL) { 175 abort(); 176 } 177 pthread_setspecific(context_key, ctxt); 178 } 179 return ctxt; 180 #else 181 static struct fuse_context fcon; 182 183 return &fcon; 184 #endif 185 } 186 187 /* used as a callback function */ 188 #ifdef MULTITHREADED_REFUSE 189 static void 190 free_context(void *ctxt) 191 { 192 free(ctxt); 193 } 194 #endif 195 196 /* 197 * Create the pthread key. The reason for the complexity is to 198 * enable use of multiple fuse instances within a single process. 199 */ 200 static int 201 create_context_key(void) 202 { 203 #ifdef MULTITHREADED_REFUSE 204 int rv; 205 206 rv = pthread_mutex_lock(&context_mutex); 207 assert(rv == 0); 208 209 if (context_refc == 0) { 210 if (pthread_key_create(&context_key, free_context) != 0) { 211 warnx("create_context_key: pthread_key_create failed"); 212 pthread_mutex_unlock(&context_mutex); 213 return 0; 214 } 215 } 216 context_refc += 1; 217 pthread_mutex_unlock(&context_mutex); 218 return 1; 219 #else 220 return 1; 221 #endif 222 } 223 224 static void 225 delete_context_key(void) 226 { 227 #ifdef MULTITHREADED_REFUSE 228 pthread_mutex_lock(&context_mutex); 229 /* If we are the last fuse instances using the key, delete it */ 230 if (--context_refc == 0) { 231 free(pthread_getspecific(context_key)); 232 pthread_key_delete(context_key); 233 } 234 pthread_mutex_unlock(&context_mutex); 235 #endif 236 } 237 238 /* set the uid and gid of the calling process in the current fuse context */ 239 static void 240 set_fuse_context_uid_gid(const struct puffs_cred *cred) 241 { 242 struct fuse_context *fusectx; 243 uid_t uid; 244 gid_t gid; 245 246 fusectx = fuse_get_context(); 247 if (puffs_cred_getuid(cred, &uid) == 0) { 248 fusectx->uid = uid; 249 } 250 if (puffs_cred_getgid(cred, &gid) == 0) { 251 fusectx->gid = gid; 252 } 253 } 254 255 /* set the pid of the calling process in the current fuse context */ 256 static void 257 set_fuse_context_pid(struct puffs_usermount *pu) 258 { 259 struct puffs_cc *pcc = puffs_cc_getcc(pu); 260 struct fuse_context *fusectx; 261 262 fusectx = fuse_get_context(); 263 puffs_cc_getcaller(pcc, &fusectx->pid, NULL); 264 } 265 266 /***************** end of pthread context routines ************************/ 267 268 #define DIR_CHUNKSIZE 4096 269 static int 270 fill_dirbuf(struct puffs_fuse_dirh *dh, const char *name, ino_t dino, 271 uint8_t dtype) 272 { 273 274 /* initial? */ 275 if (dh->bufsize == 0) { 276 if ((dh->dbuf = calloc(1, DIR_CHUNKSIZE)) == NULL) { 277 abort(); 278 } 279 dh->d = dh->dbuf; 280 dh->reslen = dh->bufsize = DIR_CHUNKSIZE; 281 } 282 283 if (puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen)) { 284 return 0; 285 } 286 287 /* try to increase buffer space */ 288 dh->dbuf = realloc(dh->dbuf, dh->bufsize + DIR_CHUNKSIZE); 289 if (dh->dbuf == NULL) { 290 abort(); 291 } 292 dh->d = (void *)((uint8_t *)dh->dbuf + (dh->bufsize - dh->reslen)); 293 dh->reslen += DIR_CHUNKSIZE; 294 dh->bufsize += DIR_CHUNKSIZE; 295 296 return !puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen); 297 } 298 299 /* ARGSUSED3 */ 300 /* XXX: I have no idea how "off" is supposed to be used */ 301 static int 302 puffs_fuse_fill_dir(void *buf, const char *name, 303 const struct stat *stbuf, off_t off) 304 { 305 struct puffs_fuse_dirh *deh = buf; 306 ino_t dino; 307 uint8_t dtype; 308 309 if (stbuf == NULL) { 310 dtype = DT_UNKNOWN; 311 dino = fakeino++; 312 } else { 313 dtype = puffs_vtype2dt(puffs_mode2vt(stbuf->st_mode)); 314 dino = stbuf->st_ino; 315 316 /* 317 * Some FUSE file systems like to always use 0 as the 318 * inode number. Our readdir() doesn't like to show 319 * directory entries with inode number 0 ==> workaround. 320 */ 321 if (dino == 0) { 322 dino = fakeino++; 323 } 324 } 325 326 return fill_dirbuf(deh, name, dino, dtype); 327 } 328 329 static int 330 puffs_fuse_dirfil(fuse_dirh_t h, const char *name, int type, ino_t ino) 331 { 332 ino_t dino; 333 int dtype; 334 335 if ((dtype = type) == 0) { 336 dtype = DT_UNKNOWN; 337 } 338 339 dino = (ino) ? ino : fakeino++; 340 341 return fill_dirbuf(h, name, dino, dtype); 342 } 343 344 /* place the refuse file system name into `name' */ 345 static void 346 set_refuse_mount_name(char **argv, char *name, size_t size) 347 { 348 char *slash; 349 350 if (argv == NULL || *argv == NULL) { 351 (void) strlcpy(name, "refuse", size); 352 } else { 353 if ((slash = strrchr(*argv, '/')) == NULL) { 354 slash = *argv; 355 } else { 356 slash += 1; 357 } 358 if (strncmp(*argv, "refuse:", 7) == 0) { 359 /* we've already done this */ 360 (void) strlcpy(name, *argv, size); 361 } else { 362 (void) snprintf(name, size, "refuse:%s", slash); 363 } 364 } 365 } 366 367 368 /* this function exposes struct fuse to userland */ 369 static struct fuse * 370 fuse_setup_real(int argc, char **argv, const struct fuse_operations *ops, 371 size_t size, char **mountpoint, int *multithreaded, int *fd, 372 void *user_data) 373 { 374 struct fuse_chan *fc; 375 struct fuse_args *args; 376 struct fuse *fuse; 377 char name[64]; 378 int i; 379 380 /* set up the proper name */ 381 set_refuse_mount_name(argv, name, sizeof(name)); 382 383 /* grab the pthread context key */ 384 if (!create_context_key()) { 385 return NULL; 386 } 387 388 /* stuff name into fuse_args */ 389 args = fuse_opt_deep_copy_args(argc, argv); 390 if (args->argc > 0) { 391 free(args->argv[0]); 392 } 393 if ((args->argv[0] = strdup(name)) == NULL) { 394 fuse_opt_free_args(args); 395 return NULL; 396 } 397 398 /* count back from the end over arguments starting with '-' */ 399 for (i = argc - 1 ; i > 0 && *argv[i] == '-' ; --i) { 400 } 401 402 fc = fuse_mount(*mountpoint = argv[i], args); 403 fuse = fuse_new(fc, args, ops, size, user_data); 404 405 fuse_opt_free_args(args); 406 free(args); 407 408 /* XXX - wait for puffs to become multi-threaded */ 409 if (multithreaded) { 410 *multithreaded = 0; 411 } 412 413 /* XXX - this is unused */ 414 if (fd) { 415 *fd = 0; 416 } 417 418 return fuse; 419 } 420 421 #ifdef fuse_setup 422 #undef fuse_setup 423 #endif 424 425 struct fuse * 426 fuse_setup(int argc, char **argv, const struct fuse_operations *ops, 427 size_t size, char **mountpoint, int *multithreaded, int *fd) 428 { 429 return fuse_setup_real(argc, argv, ops, size, mountpoint, 430 multithreaded, fd, NULL); 431 } 432 433 struct fuse * 434 fuse_setup26(int argc, char **argv, const struct fuse_operations *ops, 435 size_t size, char **mountpoint, int *multithreaded, void *user_data) 436 { 437 return fuse_setup_real(argc, argv, ops, size, mountpoint, 438 multithreaded, NULL, user_data); 439 } 440 441 #define FUSE_ERR_UNLINK(fuse, file) if (fuse->op.unlink) fuse->op.unlink(file) 442 #define FUSE_ERR_RMDIR(fuse, dir) if (fuse->op.rmdir) fuse->op.rmdir(dir) 443 444 /* ARGSUSED1 */ 445 static int 446 fuse_getattr(struct fuse *fuse, struct puffs_node *pn, const char *path, 447 struct vattr *va) 448 { 449 struct stat st; 450 int ret; 451 452 if (fuse->op.getattr == NULL) { 453 return ENOSYS; 454 } 455 456 /* wrap up return code */ 457 memset(&st, 0, sizeof(st)); 458 ret = (*fuse->op.getattr)(path, &st); 459 460 if (ret == 0) { 461 if (st.st_blksize == 0) 462 st.st_blksize = DEV_BSIZE; 463 puffs_stat2vattr(va, &st); 464 } 465 466 return -ret; 467 } 468 469 /* utility function to set various elements of the attribute */ 470 static int 471 fuse_setattr(struct fuse *fuse, struct puffs_node *pn, const char *path, 472 const struct vattr *va) 473 { 474 struct refusenode *rn = pn->pn_data; 475 mode_t mode; 476 uid_t uid; 477 gid_t gid; 478 int error, ret; 479 480 error = 0; 481 482 mode = va->va_mode; 483 uid = va->va_uid; 484 gid = va->va_gid; 485 486 if (mode != (mode_t)PUFFS_VNOVAL) { 487 ret = 0; 488 489 if (fuse->op.chmod == NULL) { 490 error = -ENOSYS; 491 } else { 492 ret = fuse->op.chmod(path, mode); 493 if (ret) 494 error = ret; 495 } 496 } 497 if (uid != (uid_t)PUFFS_VNOVAL || gid != (gid_t)PUFFS_VNOVAL) { 498 ret = 0; 499 500 if (fuse->op.chown == NULL) { 501 error = -ENOSYS; 502 } else { 503 ret = fuse->op.chown(path, uid, gid); 504 if (ret) 505 error = ret; 506 } 507 } 508 if (va->va_atime.tv_sec != (time_t)PUFFS_VNOVAL 509 || va->va_mtime.tv_sec != (long)PUFFS_VNOVAL) { 510 ret = 0; 511 512 if (fuse->op.utimens) { 513 struct timespec tv[2]; 514 515 tv[0].tv_sec = va->va_atime.tv_sec; 516 tv[0].tv_nsec = va->va_atime.tv_nsec; 517 tv[1].tv_sec = va->va_mtime.tv_sec; 518 tv[1].tv_nsec = va->va_mtime.tv_nsec; 519 520 ret = fuse->op.utimens(path, tv); 521 } else if (fuse->op.utime) { 522 struct utimbuf timbuf; 523 524 timbuf.actime = va->va_atime.tv_sec; 525 timbuf.modtime = va->va_mtime.tv_sec; 526 527 ret = fuse->op.utime(path, &timbuf); 528 } else { 529 error = -ENOSYS; 530 } 531 532 if (ret) 533 error = ret; 534 } 535 if (va->va_size != (u_quad_t)PUFFS_VNOVAL) { 536 ret = 0; 537 538 if (fuse->op.truncate) { 539 ret = fuse->op.truncate(path, (off_t)va->va_size); 540 } else if (fuse->op.ftruncate) { 541 ret = fuse->op.ftruncate(path, (off_t)va->va_size, 542 &rn->file_info); 543 } else { 544 error = -ENOSYS; 545 } 546 547 if (ret) 548 error = ret; 549 } 550 /* XXX: no reflection with reality */ 551 puffs_setvattr(&pn->pn_va, va); 552 553 return -error; 554 555 } 556 557 static int 558 fuse_newnode(struct puffs_usermount *pu, const char *path, 559 const struct vattr *va, struct fuse_file_info *fi, 560 struct puffs_newinfo *pni, struct puffs_node **pn_new) 561 { 562 struct puffs_node *pn; 563 struct refusenode *rn; 564 struct vattr newva; 565 struct fuse *fuse; 566 567 fuse = puffs_getspecific(pu); 568 569 /* fix up nodes */ 570 pn = newrn(pu); 571 if (pn == NULL) { 572 if (va->va_type == VDIR) { 573 FUSE_ERR_RMDIR(fuse, path); 574 } else { 575 FUSE_ERR_UNLINK(fuse, path); 576 } 577 return ENOMEM; 578 } 579 fuse_setattr(fuse, pn, path, va); 580 if (fuse_getattr(fuse, pn, path, &newva) == 0) 581 puffs_setvattr(&pn->pn_va, &newva); 582 583 rn = pn->pn_data; 584 if (fi) 585 memcpy(&rn->file_info, fi, sizeof(struct fuse_file_info)); 586 587 puffs_newinfo_setcookie(pni, pn); 588 if (pn_new) 589 *pn_new = pn; 590 591 return 0; 592 } 593 594 595 /* operation wrappers start here */ 596 597 /* lookup the path */ 598 /* ARGSUSED1 */ 599 static int 600 puffs_fuse_node_lookup(struct puffs_usermount *pu, void *opc, 601 struct puffs_newinfo *pni, const struct puffs_cn *pcn) 602 { 603 struct puffs_node *pn_res; 604 struct stat st; 605 struct fuse *fuse; 606 const char *path = PCNPATH(pcn); 607 int ret; 608 609 fuse = puffs_getspecific(pu); 610 611 set_fuse_context_uid_gid(pcn->pcn_cred); 612 613 ret = fuse->op.getattr(path, &st); 614 615 if (ret != 0) { 616 return -ret; 617 } 618 619 /* XXX: fiXXXme unconst */ 620 pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp, 621 __UNCONST(&pcn->pcn_po_full)); 622 if (pn_res == NULL) { 623 pn_res = newrn(pu); 624 if (pn_res == NULL) 625 return errno; 626 puffs_stat2vattr(&pn_res->pn_va, &st); 627 } 628 629 puffs_newinfo_setcookie(pni, pn_res); 630 puffs_newinfo_setvtype(pni, pn_res->pn_va.va_type); 631 puffs_newinfo_setsize(pni, (voff_t)pn_res->pn_va.va_size); 632 puffs_newinfo_setrdev(pni, pn_res->pn_va.va_rdev); 633 634 return 0; 635 } 636 637 /* get attributes for the path name */ 638 /* ARGSUSED3 */ 639 static int 640 puffs_fuse_node_getattr(struct puffs_usermount *pu, void *opc, struct vattr *va, 641 const struct puffs_cred *pcr) 642 { 643 struct puffs_node *pn = opc; 644 struct fuse *fuse; 645 const char *path = PNPATH(pn); 646 647 fuse = puffs_getspecific(pu); 648 649 set_fuse_context_uid_gid(pcr); 650 651 return fuse_getattr(fuse, pn, path, va); 652 } 653 654 /* read the contents of the symbolic link */ 655 /* ARGSUSED2 */ 656 static int 657 puffs_fuse_node_readlink(struct puffs_usermount *pu, void *opc, 658 const struct puffs_cred *cred, char *linkname, size_t *linklen) 659 { 660 struct puffs_node *pn = opc; 661 struct fuse *fuse; 662 const char *path = PNPATH(pn), *p; 663 int ret; 664 665 fuse = puffs_getspecific(pu); 666 if (fuse->op.readlink == NULL) { 667 return ENOSYS; 668 } 669 670 set_fuse_context_uid_gid(cred); 671 672 /* wrap up return code */ 673 ret = (*fuse->op.readlink)(path, linkname, *linklen); 674 675 if (ret == 0) { 676 p = memchr(linkname, '\0', *linklen); 677 if (!p) 678 return EINVAL; 679 680 *linklen = p - linkname; 681 } 682 683 return -ret; 684 } 685 686 /* make the special node */ 687 /* ARGSUSED1 */ 688 static int 689 puffs_fuse_node_mknod(struct puffs_usermount *pu, void *opc, 690 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 691 const struct vattr *va) 692 { 693 struct fuse *fuse; 694 mode_t mode; 695 const char *path = PCNPATH(pcn); 696 int ret; 697 698 fuse = puffs_getspecific(pu); 699 if (fuse->op.mknod == NULL) { 700 return ENOSYS; 701 } 702 703 set_fuse_context_uid_gid(pcn->pcn_cred); 704 705 /* wrap up return code */ 706 mode = puffs_addvtype2mode(va->va_mode, va->va_type); 707 ret = (*fuse->op.mknod)(path, mode, va->va_rdev); 708 709 if (ret == 0) { 710 ret = fuse_newnode(pu, path, va, NULL, pni, NULL); 711 } 712 713 return -ret; 714 } 715 716 /* make a directory */ 717 /* ARGSUSED1 */ 718 static int 719 puffs_fuse_node_mkdir(struct puffs_usermount *pu, void *opc, 720 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 721 const struct vattr *va) 722 { 723 struct fuse *fuse; 724 mode_t mode = va->va_mode; 725 const char *path = PCNPATH(pcn); 726 int ret; 727 728 fuse = puffs_getspecific(pu); 729 730 set_fuse_context_uid_gid(pcn->pcn_cred); 731 732 if (fuse->op.mkdir == NULL) { 733 return ENOSYS; 734 } 735 736 /* wrap up return code */ 737 ret = (*fuse->op.mkdir)(path, mode); 738 739 if (ret == 0) { 740 ret = fuse_newnode(pu, path, va, NULL, pni, NULL); 741 } 742 743 return -ret; 744 } 745 746 /* 747 * create a regular file 748 * 749 * since linux/fuse sports using mknod for creating regular files 750 * instead of having a separate call for it in some versions, if 751 * we don't have create, just jump to op->mknod. 752 */ 753 /*ARGSUSED1*/ 754 static int 755 puffs_fuse_node_create(struct puffs_usermount *pu, void *opc, 756 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 757 const struct vattr *va) 758 { 759 struct fuse *fuse; 760 struct fuse_file_info fi; 761 struct puffs_node *pn; 762 mode_t mode = va->va_mode; 763 const char *path = PCNPATH(pcn); 764 int ret, created; 765 766 fuse = puffs_getspecific(pu); 767 768 set_fuse_context_uid_gid(pcn->pcn_cred); 769 770 created = 0; 771 if (fuse->op.create) { 772 ret = fuse->op.create(path, mode | S_IFREG, &fi); 773 if (ret == 0) 774 created = 1; 775 776 } else if (fuse->op.mknod) { 777 ret = fuse->op.mknod(path, mode | S_IFREG, 0); 778 779 } else { 780 ret = -ENOSYS; 781 } 782 783 if (ret == 0) { 784 ret = fuse_newnode(pu, path, va, &fi, pni, &pn); 785 786 /* sweet.. create also open the file */ 787 if (created) { 788 struct refusenode *rn; 789 790 rn = pn->pn_data; 791 rn->flags |= RN_OPEN; 792 rn->opencount++; 793 } 794 } 795 796 return -ret; 797 } 798 799 /* remove the directory entry */ 800 /* ARGSUSED1 */ 801 static int 802 puffs_fuse_node_remove(struct puffs_usermount *pu, void *opc, void *targ, 803 const struct puffs_cn *pcn) 804 { 805 struct puffs_node *pn_targ = targ; 806 struct fuse *fuse; 807 const char *path = PNPATH(pn_targ); 808 int ret; 809 810 fuse = puffs_getspecific(pu); 811 812 set_fuse_context_uid_gid(pcn->pcn_cred); 813 814 if (fuse->op.unlink == NULL) { 815 return ENOSYS; 816 } 817 818 /* wrap up return code */ 819 ret = (*fuse->op.unlink)(path); 820 821 return -ret; 822 } 823 824 /* remove the directory */ 825 /* ARGSUSED1 */ 826 static int 827 puffs_fuse_node_rmdir(struct puffs_usermount *pu, void *opc, void *targ, 828 const struct puffs_cn *pcn) 829 { 830 struct puffs_node *pn_targ = targ; 831 struct fuse *fuse; 832 const char *path = PNPATH(pn_targ); 833 int ret; 834 835 fuse = puffs_getspecific(pu); 836 837 set_fuse_context_uid_gid(pcn->pcn_cred); 838 839 if (fuse->op.rmdir == NULL) { 840 return ENOSYS; 841 } 842 843 /* wrap up return code */ 844 ret = (*fuse->op.rmdir)(path); 845 846 return -ret; 847 } 848 849 /* create a symbolic link */ 850 /* ARGSUSED1 */ 851 static int 852 puffs_fuse_node_symlink(struct puffs_usermount *pu, void *opc, 853 struct puffs_newinfo *pni, const struct puffs_cn *pcn_src, 854 const struct vattr *va, const char *link_target) 855 { 856 struct fuse *fuse; 857 const char *path = PCNPATH(pcn_src); 858 int ret; 859 860 fuse = puffs_getspecific(pu); 861 862 set_fuse_context_uid_gid(pcn_src->pcn_cred); 863 864 if (fuse->op.symlink == NULL) { 865 return ENOSYS; 866 } 867 868 /* wrap up return code */ 869 ret = fuse->op.symlink(link_target, path); 870 871 if (ret == 0) { 872 ret = fuse_newnode(pu, path, va, NULL, pni, NULL); 873 } 874 875 return -ret; 876 } 877 878 /* rename a directory entry */ 879 /* ARGSUSED1 */ 880 static int 881 puffs_fuse_node_rename(struct puffs_usermount *pu, void *opc, void *src, 882 const struct puffs_cn *pcn_src, void *targ_dir, void *targ, 883 const struct puffs_cn *pcn_targ) 884 { 885 struct fuse *fuse; 886 const char *path_src = PCNPATH(pcn_src); 887 const char *path_dest = PCNPATH(pcn_targ); 888 int ret; 889 890 fuse = puffs_getspecific(pu); 891 892 set_fuse_context_uid_gid(pcn_targ->pcn_cred); 893 894 if (fuse->op.rename == NULL) { 895 return ENOSYS; 896 } 897 898 ret = fuse->op.rename(path_src, path_dest); 899 900 if (ret == 0) { 901 } 902 903 return -ret; 904 } 905 906 /* create a link in the file system */ 907 /* ARGSUSED1 */ 908 static int 909 puffs_fuse_node_link(struct puffs_usermount *pu, void *opc, void *targ, 910 const struct puffs_cn *pcn) 911 { 912 struct puffs_node *pn = targ; 913 struct fuse *fuse; 914 int ret; 915 916 fuse = puffs_getspecific(pu); 917 918 set_fuse_context_uid_gid(pcn->pcn_cred); 919 920 if (fuse->op.link == NULL) { 921 return ENOSYS; 922 } 923 924 /* wrap up return code */ 925 ret = (*fuse->op.link)(PNPATH(pn), PCNPATH(pcn)); 926 927 return -ret; 928 } 929 930 /* 931 * fuse's regular interface provides chmod(), chown(), utimes() 932 * and truncate() + some variations, so try to fit the square block 933 * in the circle hole and the circle block .... something like that 934 */ 935 /* ARGSUSED3 */ 936 static int 937 puffs_fuse_node_setattr(struct puffs_usermount *pu, void *opc, 938 const struct vattr *va, const struct puffs_cred *pcr) 939 { 940 struct puffs_node *pn = opc; 941 struct fuse *fuse; 942 const char *path = PNPATH(pn); 943 944 fuse = puffs_getspecific(pu); 945 946 set_fuse_context_uid_gid(pcr); 947 948 return fuse_setattr(fuse, pn, path, va); 949 } 950 951 /* ARGSUSED2 */ 952 static int 953 puffs_fuse_node_open(struct puffs_usermount *pu, void *opc, int mode, 954 const struct puffs_cred *cred) 955 { 956 struct puffs_node *pn = opc; 957 struct refusenode *rn = pn->pn_data; 958 struct fuse_file_info *fi = &rn->file_info; 959 struct fuse *fuse; 960 const char *path = PNPATH(pn); 961 962 fuse = puffs_getspecific(pu); 963 964 set_fuse_context_uid_gid(cred); 965 966 /* if open, don't open again, lest risk nuking file private info */ 967 if (rn->flags & RN_OPEN) { 968 rn->opencount++; 969 return 0; 970 } 971 972 /* OFLAGS(), need to convert FREAD/FWRITE to O_RD/WR */ 973 fi->flags = (mode & ~(O_CREAT | O_EXCL | O_TRUNC)) - 1; 974 975 if (pn->pn_va.va_type == VDIR) { 976 if (fuse->op.opendir) 977 fuse->op.opendir(path, fi); 978 } else { 979 if (fuse->op.open) 980 fuse->op.open(path, fi); 981 } 982 983 rn->flags |= RN_OPEN; 984 rn->opencount++; 985 986 return 0; 987 } 988 989 /* ARGSUSED2 */ 990 static int 991 puffs_fuse_node_close(struct puffs_usermount *pu, void *opc, int fflag, 992 const struct puffs_cred *pcr) 993 { 994 struct puffs_node *pn = opc; 995 struct refusenode *rn = pn->pn_data; 996 struct fuse *fuse; 997 struct fuse_file_info *fi; 998 const char *path = PNPATH(pn); 999 int ret; 1000 1001 fuse = puffs_getspecific(pu); 1002 fi = &rn->file_info; 1003 ret = 0; 1004 1005 set_fuse_context_uid_gid(pcr); 1006 1007 if (rn->flags & RN_OPEN) { 1008 if (pn->pn_va.va_type == VDIR) { 1009 if (fuse->op.releasedir) 1010 ret = fuse->op.releasedir(path, fi); 1011 } else { 1012 if (fuse->op.release) 1013 ret = fuse->op.release(path, fi); 1014 } 1015 } 1016 rn->flags &= ~RN_OPEN; 1017 rn->opencount--; 1018 1019 return ret; 1020 } 1021 1022 /* read some more from the file */ 1023 /* ARGSUSED5 */ 1024 static int 1025 puffs_fuse_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf, 1026 off_t offset, size_t *resid, const struct puffs_cred *pcr, 1027 int ioflag) 1028 { 1029 struct puffs_node *pn = opc; 1030 struct refusenode *rn = pn->pn_data; 1031 struct fuse *fuse; 1032 const char *path = PNPATH(pn); 1033 size_t maxread; 1034 int ret; 1035 1036 fuse = puffs_getspecific(pu); 1037 if (fuse->op.read == NULL) { 1038 return ENOSYS; 1039 } 1040 1041 set_fuse_context_uid_gid(pcr); 1042 1043 maxread = *resid; 1044 if (maxread > pn->pn_va.va_size - offset) { 1045 /*LINTED*/ 1046 maxread = pn->pn_va.va_size - offset; 1047 } 1048 if (maxread == 0) 1049 return 0; 1050 1051 ret = (*fuse->op.read)(path, (char *)buf, maxread, offset, 1052 &rn->file_info); 1053 1054 if (ret > 0) { 1055 *resid -= ret; 1056 ret = 0; 1057 } 1058 1059 return -ret; 1060 } 1061 1062 /* write to the file */ 1063 /* ARGSUSED0 */ 1064 static int 1065 puffs_fuse_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf, 1066 off_t offset, size_t *resid, const struct puffs_cred *pcr, 1067 int ioflag) 1068 { 1069 struct puffs_node *pn = opc; 1070 struct refusenode *rn = pn->pn_data; 1071 struct fuse *fuse; 1072 const char *path = PNPATH(pn); 1073 int ret; 1074 1075 fuse = puffs_getspecific(pu); 1076 if (fuse->op.write == NULL) { 1077 return ENOSYS; 1078 } 1079 1080 set_fuse_context_uid_gid(pcr); 1081 1082 if (ioflag & PUFFS_IO_APPEND) 1083 offset = pn->pn_va.va_size; 1084 1085 ret = (*fuse->op.write)(path, (char *)buf, *resid, offset, 1086 &rn->file_info); 1087 1088 if (ret >= 0) { 1089 if ((uint64_t)(offset + ret) > pn->pn_va.va_size) 1090 pn->pn_va.va_size = offset + ret; 1091 *resid -= ret; 1092 ret = (*resid == 0) ? 0 : ENOSPC; 1093 } else { 1094 ret = -ret; 1095 } 1096 1097 return ret; 1098 } 1099 1100 1101 /* ARGSUSED3 */ 1102 static int 1103 puffs_fuse_node_readdir(struct puffs_usermount *pu, void *opc, 1104 struct dirent *dent, off_t *readoff, size_t *reslen, 1105 const struct puffs_cred *pcr, int *eofflag, 1106 off_t *cookies, size_t *ncookies) 1107 { 1108 struct puffs_node *pn = opc; 1109 struct refusenode *rn = pn->pn_data; 1110 struct puffs_fuse_dirh *dirh; 1111 struct fuse *fuse; 1112 struct dirent *fromdent; 1113 const char *path = PNPATH(pn); 1114 int ret; 1115 1116 fuse = puffs_getspecific(pu); 1117 if (fuse->op.readdir == NULL && fuse->op.getdir == NULL) { 1118 return ENOSYS; 1119 } 1120 1121 set_fuse_context_uid_gid(pcr); 1122 1123 if (pn->pn_va.va_type != VDIR) 1124 return ENOTDIR; 1125 1126 dirh = &rn->dirh; 1127 1128 /* 1129 * if we are starting from the beginning, slurp entire directory 1130 * into our buffers 1131 */ 1132 if (*readoff == 0) { 1133 /* free old buffers */ 1134 free(dirh->dbuf); 1135 memset(dirh, 0, sizeof(struct puffs_fuse_dirh)); 1136 1137 if (fuse->op.readdir) 1138 ret = fuse->op.readdir(path, dirh, puffs_fuse_fill_dir, 1139 0, &rn->file_info); 1140 else 1141 ret = fuse->op.getdir(path, dirh, puffs_fuse_dirfil); 1142 if (ret) 1143 return -ret; 1144 } 1145 1146 /* Both op.readdir and op.getdir read full directory */ 1147 *eofflag = 1; 1148 1149 /* now, stuff results into the kernel buffers */ 1150 while (*readoff < (off_t)(dirh->bufsize - dirh->reslen)) { 1151 /*LINTED*/ 1152 fromdent = (struct dirent *)((uint8_t *)dirh->dbuf + *readoff); 1153 1154 if (*reslen < _DIRENT_SIZE(fromdent)) 1155 break; 1156 1157 memcpy(dent, fromdent, _DIRENT_SIZE(fromdent)); 1158 *readoff += _DIRENT_SIZE(fromdent); 1159 *reslen -= _DIRENT_SIZE(fromdent); 1160 1161 dent = _DIRENT_NEXT(dent); 1162 } 1163 1164 return 0; 1165 } 1166 1167 /* ARGSUSED */ 1168 static int 1169 puffs_fuse_node_reclaim(struct puffs_usermount *pu, void *opc) 1170 { 1171 struct puffs_node *pn = opc; 1172 1173 nukern(pn); 1174 return 0; 1175 } 1176 1177 /* ARGSUSED1 */ 1178 static int 1179 puffs_fuse_fs_unmount(struct puffs_usermount *pu, int flags) 1180 { 1181 struct fuse *fuse; 1182 1183 fuse = puffs_getspecific(pu); 1184 if (fuse->op.destroy == NULL) { 1185 return 0; 1186 } 1187 (*fuse->op.destroy)(fuse); 1188 return 0; 1189 } 1190 1191 /* ARGSUSED0 */ 1192 static int 1193 puffs_fuse_fs_sync(struct puffs_usermount *pu, int flags, 1194 const struct puffs_cred *cr) 1195 { 1196 set_fuse_context_uid_gid(cr); 1197 return 0; 1198 } 1199 1200 /* ARGSUSED2 */ 1201 static int 1202 puffs_fuse_fs_statvfs(struct puffs_usermount *pu, struct statvfs *svfsb) 1203 { 1204 struct fuse *fuse; 1205 int ret; 1206 1207 fuse = puffs_getspecific(pu); 1208 if (fuse->op.statfs == NULL) { 1209 if ((ret = statvfs(PNPATH(puffs_getroot(pu)), svfsb)) == -1) { 1210 return errno; 1211 } 1212 } else { 1213 ret = fuse->op.statfs(PNPATH(puffs_getroot(pu)), svfsb); 1214 } 1215 1216 return -ret; 1217 } 1218 1219 1220 /* End of puffs_fuse operations */ 1221 /* ARGSUSED3 */ 1222 int 1223 fuse_main_real(int argc, char **argv, const struct fuse_operations *ops, 1224 size_t size, void *userdata) 1225 { 1226 struct fuse *fuse; 1227 char *mountpoint; 1228 int multithreaded; 1229 int fd; 1230 1231 fuse = fuse_setup_real(argc, argv, ops, size, &mountpoint, 1232 &multithreaded, &fd, userdata); 1233 1234 return fuse_loop(fuse); 1235 } 1236 1237 /* 1238 * XXX: just defer the operation until fuse_new() when we have more 1239 * info on our hands. The real beef is why's this separate in fuse in 1240 * the first place? 1241 */ 1242 /* ARGSUSED1 */ 1243 struct fuse_chan * 1244 fuse_mount(const char *dir, struct fuse_args *args) 1245 { 1246 struct fuse_chan *fc; 1247 char name[64]; 1248 1249 if ((fc = calloc(1, sizeof(*fc))) == NULL) { 1250 err(EXIT_FAILURE, "fuse_mount"); 1251 } 1252 fc->dead = 0; 1253 1254 if ((fc->dir = strdup(dir)) == NULL) { 1255 err(EXIT_FAILURE, "fuse_mount"); 1256 } 1257 1258 /* 1259 * we need to deep copy the args struct - some fuse file 1260 * systems "clean up" the argument vector for "security 1261 * reasons" 1262 */ 1263 fc->args = fuse_opt_deep_copy_args(args->argc, args->argv); 1264 1265 if (args->argc > 0) { 1266 set_refuse_mount_name(args->argv, name, sizeof(name)); 1267 if ((args->argv[0] = strdup(name)) == NULL) 1268 err(1, "fuse_mount"); 1269 } 1270 1271 return fc; 1272 } 1273 1274 /* ARGSUSED1 */ 1275 struct fuse * 1276 fuse_new(struct fuse_chan *fc, struct fuse_args *args, 1277 const struct fuse_operations *ops, size_t size, void *userdata) 1278 { 1279 struct puffs_usermount *pu; 1280 struct fuse_context *fusectx; 1281 struct puffs_pathobj *po_root; 1282 struct puffs_node *pn_root; 1283 struct puffs_ops *pops; 1284 struct refusenode *rn_root; 1285 struct statvfs svfsb; 1286 struct stat st; 1287 struct fuse *fuse; 1288 extern int puffs_fakecc; 1289 char name[64]; 1290 char *argv0; 1291 1292 if ((fuse = calloc(1, sizeof(*fuse))) == NULL) { 1293 err(EXIT_FAILURE, "fuse_new"); 1294 } 1295 1296 /* copy fuse ops to their own structure */ 1297 (void) memcpy(&fuse->op, ops, sizeof(fuse->op)); 1298 1299 fusectx = fuse_get_context(); 1300 fusectx->fuse = fuse; 1301 fusectx->uid = 0; 1302 fusectx->gid = 0; 1303 fusectx->pid = 0; 1304 fusectx->private_data = userdata; 1305 1306 fuse->fc = fc; 1307 1308 if (fuse->op.init != NULL) 1309 fusectx->private_data = fuse->op.init(NULL); /* XXX */ 1310 1311 /* initialise the puffs operations structure */ 1312 PUFFSOP_INIT(pops); 1313 1314 PUFFSOP_SET(pops, puffs_fuse, fs, sync); 1315 PUFFSOP_SET(pops, puffs_fuse, fs, statvfs); 1316 PUFFSOP_SET(pops, puffs_fuse, fs, unmount); 1317 1318 /* 1319 * XXX: all of these don't possibly need to be 1320 * unconditionally set 1321 */ 1322 PUFFSOP_SET(pops, puffs_fuse, node, lookup); 1323 PUFFSOP_SET(pops, puffs_fuse, node, getattr); 1324 PUFFSOP_SET(pops, puffs_fuse, node, setattr); 1325 PUFFSOP_SET(pops, puffs_fuse, node, readdir); 1326 PUFFSOP_SET(pops, puffs_fuse, node, readlink); 1327 PUFFSOP_SET(pops, puffs_fuse, node, mknod); 1328 PUFFSOP_SET(pops, puffs_fuse, node, create); 1329 PUFFSOP_SET(pops, puffs_fuse, node, remove); 1330 PUFFSOP_SET(pops, puffs_fuse, node, mkdir); 1331 PUFFSOP_SET(pops, puffs_fuse, node, rmdir); 1332 PUFFSOP_SET(pops, puffs_fuse, node, symlink); 1333 PUFFSOP_SET(pops, puffs_fuse, node, rename); 1334 PUFFSOP_SET(pops, puffs_fuse, node, link); 1335 PUFFSOP_SET(pops, puffs_fuse, node, open); 1336 PUFFSOP_SET(pops, puffs_fuse, node, close); 1337 PUFFSOP_SET(pops, puffs_fuse, node, read); 1338 PUFFSOP_SET(pops, puffs_fuse, node, write); 1339 PUFFSOP_SET(pops, puffs_fuse, node, reclaim); 1340 1341 argv0 = (*args->argv[0] == 0x0) ? fc->args->argv[0] : args->argv[0]; 1342 set_refuse_mount_name(&argv0, name, sizeof(name)); 1343 1344 puffs_fakecc = 1; /* XXX */ 1345 pu = puffs_init(pops, _PATH_PUFFS, name, fuse, 1346 PUFFS_FLAG_BUILDPATH 1347 | PUFFS_FLAG_HASHPATH 1348 | PUFFS_KFLAG_NOCACHE); 1349 if (pu == NULL) { 1350 err(EXIT_FAILURE, "puffs_init"); 1351 } 1352 fc->pu = pu; 1353 1354 pn_root = newrn(pu); 1355 puffs_setroot(pu, pn_root); 1356 rn_root = pn_root->pn_data; 1357 rn_root->flags |= RN_ROOT; 1358 1359 po_root = puffs_getrootpathobj(pu); 1360 if ((po_root->po_path = strdup("/")) == NULL) 1361 err(1, "fuse_new"); 1362 po_root->po_len = 1; 1363 puffs_path_buildhash(pu, po_root); 1364 1365 /* sane defaults */ 1366 puffs_vattr_null(&pn_root->pn_va); 1367 pn_root->pn_va.va_type = VDIR; 1368 pn_root->pn_va.va_mode = 0755; 1369 if (fuse->op.getattr) 1370 if (fuse->op.getattr(po_root->po_path, &st) == 0) 1371 puffs_stat2vattr(&pn_root->pn_va, &st); 1372 assert(pn_root->pn_va.va_type == VDIR); 1373 1374 puffs_set_prepost(pu, set_fuse_context_pid, NULL); 1375 1376 puffs_zerostatvfs(&svfsb); 1377 if (puffs_mount(pu, fc->dir, MNT_NODEV | MNT_NOSUID, pn_root) == -1) { 1378 err(EXIT_FAILURE, "puffs_mount: directory \"%s\"", fc->dir); 1379 } 1380 1381 return fuse; 1382 } 1383 1384 int 1385 fuse_loop(struct fuse *fuse) 1386 { 1387 1388 return puffs_mainloop(fuse->fc->pu); 1389 } 1390 1391 void 1392 fuse_destroy(struct fuse *fuse) 1393 { 1394 1395 /* 1396 * TODO: needs to assert the fs is quiescent, i.e. no other 1397 * threads exist 1398 */ 1399 1400 delete_context_key(); 1401 /* XXXXXX: missing stuff */ 1402 free(fuse); 1403 } 1404 1405 void 1406 fuse_exit(struct fuse *fuse) 1407 { 1408 1409 /* XXX: puffs_exit() is WRONG */ 1410 if (fuse->fc->dead == 0) 1411 puffs_exit(fuse->fc->pu, 1); 1412 fuse->fc->dead = 1; 1413 } 1414 1415 /* 1416 * XXX: obviously not the most perfect of functions, but needs some 1417 * puffs tweaking for a better tomorrow 1418 */ 1419 /*ARGSUSED*/ 1420 void 1421 fuse_unmount(const char *mp, struct fuse_chan *fc) 1422 { 1423 1424 /* XXX: puffs_exit() is WRONG */ 1425 if (fc->dead == 0) 1426 puffs_exit(fc->pu, 1); 1427 fc->dead = 1; 1428 } 1429 1430 /*ARGSUSED*/ 1431 void 1432 fuse_unmount_compat22(const char *mp) 1433 { 1434 1435 return; 1436 } 1437 1438 /* The next function "exposes" struct fuse to userland. Not much 1439 * that we can do about this, as we're conforming to a defined 1440 * interface. */ 1441 1442 void 1443 fuse_teardown(struct fuse *fuse, char *mountpoint) 1444 { 1445 fuse_unmount(mountpoint, fuse->fc); 1446 fuse_destroy(fuse); 1447 } 1448