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