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