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