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