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