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