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