1 /* $NetBSD: ops.c,v 1.14 2010/09/09 09:12:35 manu Exp $ */ 2 3 /*- 4 * Copyright (c) 2010 Emmanuel Dreyfus. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 19 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <stdio.h> 29 #include <unistd.h> 30 #include <stdlib.h> 31 #include <libgen.h> 32 #include <errno.h> 33 #include <err.h> 34 #include <sysexits.h> 35 #include <syslog.h> 36 #include <puffs.h> 37 #include <sys/vnode.h> 38 #include <sys/socket.h> 39 #include <machine/vmparam.h> 40 41 #include "perfuse_priv.h" 42 #include "fuse.h" 43 44 extern int perfuse_diagflags; 45 46 static int node_close_common(struct puffs_usermount *, puffs_cookie_t, int); 47 static int no_access(puffs_cookie_t, const struct puffs_cred *, mode_t); 48 static void fuse_attr_to_vap(struct perfuse_state *, 49 struct vattr *, struct fuse_attr *); 50 static int node_lookup_dir_nodot(struct puffs_usermount *, 51 puffs_cookie_t, char *, size_t, struct puffs_node **); 52 static int node_lookup_common(struct puffs_usermount *, puffs_cookie_t, 53 const char*, struct puffs_node **); 54 static int node_mk_common(struct puffs_usermount *, puffs_cookie_t, 55 struct puffs_newinfo *, const struct puffs_cn *pcn, perfuse_msg_t *); 56 static const char *basename_r(const char *); 57 static ssize_t fuse_to_dirent(struct puffs_usermount *, puffs_cookie_t, 58 struct fuse_dirent *, size_t); 59 static int readdir_buffered(struct perfuse_state *, puffs_cookie_t, 60 struct dirent *, off_t *, size_t *, const struct puffs_cred *, 61 int *, off_t *, size_t *); 62 static void requeue_request(struct puffs_usermount *, 63 puffs_cookie_t opc, enum perfuse_qtype); 64 static int dequeue_requests(struct perfuse_state *, 65 puffs_cookie_t opc, enum perfuse_qtype, int); 66 #define DEQUEUE_ALL 0 67 68 /* 69 * From <sys/vnode>, inside #ifdef _KERNEL section 70 */ 71 #define IO_SYNC (0x40|IO_DSYNC) 72 #define IO_DSYNC 0x00200 73 #define IO_DIRECT 0x02000 74 75 /* 76 * From <fcntl>, inside #ifdef _KERNEL section 77 */ 78 #define F_WAIT 0x010 79 #define F_FLOCK 0x020 80 #define OFLAGS(fflags) ((fflags) - 1) 81 82 /* 83 * Borrowed from src/sys/kern/vfs_subr.c and src/sys/sys/vnode.h 84 */ 85 const enum vtype iftovt_tab[16] = { 86 VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON, 87 VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VBAD, 88 }; 89 const int vttoif_tab[9] = { 90 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 91 S_IFSOCK, S_IFIFO, S_IFMT, 92 }; 93 94 #define IFTOVT(mode) (iftovt_tab[((mode) & S_IFMT) >> 12]) 95 #define VTTOIF(indx) (vttoif_tab[(int)(indx)]) 96 97 static int 98 node_close_common(pu, opc, mode) 99 struct puffs_usermount *pu; 100 puffs_cookie_t opc; 101 int mode; 102 { 103 struct perfuse_state *ps; 104 perfuse_msg_t *pm; 105 int op; 106 uint64_t fh; 107 struct fuse_release_in *fri; 108 struct perfuse_node_data *pnd; 109 struct puffs_node *pn; 110 int error; 111 112 ps = puffs_getspecific(pu); 113 pn = (struct puffs_node *)opc; 114 pnd = PERFUSE_NODE_DATA(pn); 115 116 if (puffs_pn_getvap(pn)->va_type == VDIR) { 117 op = FUSE_RELEASEDIR; 118 mode = FREAD; 119 } else { 120 op = FUSE_RELEASE; 121 } 122 123 /* 124 * Destroy the filehandle before sending the 125 * request to the FUSE filesystem, otherwise 126 * we may get a second close() while we wait 127 * for the reply, and we would end up closing 128 * the same fh twice instead of closng both. 129 */ 130 fh = perfuse_get_fh(opc, mode); 131 perfuse_destroy_fh(pn, fh); 132 133 /* 134 * release_flags may be set to FUSE_RELEASE_FLUSH 135 * to flush locks. lock_owner must be set in that case 136 */ 137 pm = ps->ps_new_msg(pu, opc, op, sizeof(*fri), NULL); 138 fri = GET_INPAYLOAD(ps, pm, fuse_release_in); 139 fri->fh = fh; 140 fri->flags = 0; 141 fri->release_flags = 0; 142 fri->lock_owner = pnd->pnd_lock_owner; 143 fri->flags = (fri->lock_owner != 0) ? FUSE_RELEASE_FLUSH : 0; 144 145 #ifdef PERFUSE_DEBUG 146 if (perfuse_diagflags & PDF_FH) 147 DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n", 148 __func__, (void *)opc, pnd->pnd_ino, fri->fh); 149 #endif 150 151 if ((error = XCHG_MSG(ps, pu, pm, NO_PAYLOAD_REPLY_LEN)) != 0) 152 goto out; 153 154 ps->ps_destroy_msg(pm); 155 156 error = 0; 157 158 out: 159 if (error != 0) 160 DERRX(EX_SOFTWARE, "%s: freed fh = 0x%"PRIx64" but filesystem " 161 "returned error = %d", __func__, fh, error); 162 163 return error; 164 } 165 166 static int 167 no_access(opc, pcr, mode) 168 puffs_cookie_t opc; 169 const struct puffs_cred *pcr; 170 mode_t mode; 171 { 172 struct puffs_node *pn; 173 struct vattr *va; 174 175 pn = (struct puffs_node *)opc; 176 va = puffs_pn_getvap(pn); 177 178 return puffs_access(va->va_type, va->va_mode, 179 va->va_uid, va->va_gid, 180 mode, pcr); 181 } 182 183 static void 184 fuse_attr_to_vap(ps, vap, fa) 185 struct perfuse_state *ps; 186 struct vattr *vap; 187 struct fuse_attr *fa; 188 { 189 vap->va_type = IFTOVT(fa->mode); 190 vap->va_mode = fa->mode; 191 vap->va_nlink = fa->nlink; 192 vap->va_uid = fa->uid; 193 vap->va_gid = fa->gid; 194 vap->va_fsid = ps->ps_fsid; 195 vap->va_fileid = fa->ino; 196 vap->va_size = fa->size; 197 vap->va_blocksize = fa->blksize; 198 vap->va_atime.tv_sec = (time_t)fa->atime; 199 vap->va_atime.tv_nsec = (long) fa->atimensec; 200 vap->va_mtime.tv_sec = (time_t)fa->mtime; 201 vap->va_mtime.tv_nsec = (long)fa->mtimensec; 202 vap->va_ctime.tv_sec = (time_t)fa->ctime; 203 vap->va_ctime.tv_nsec = (long)fa->ctimensec; 204 vap->va_birthtime.tv_sec = 0; 205 vap->va_birthtime.tv_nsec = 0; 206 vap->va_gen = 0; 207 vap->va_flags = 0; 208 vap->va_rdev = fa->rdev; 209 vap->va_bytes = fa->size; 210 vap->va_filerev = 0; 211 vap->va_vaflags = 0; 212 213 if (vap->va_blocksize == 0) 214 vap->va_blocksize = DEV_BSIZE; 215 216 if (vap->va_size == (size_t)-1) /* XXX */ 217 vap->va_size = 0; 218 219 return; 220 } 221 222 223 /* 224 * Lookup name in directory opc 225 * We take special care of name being . or .. 226 * These are returned by readdir and deserve tweaks. 227 */ 228 static int 229 node_lookup_dir_nodot(pu, opc, name, namelen, pnp) 230 struct puffs_usermount *pu; 231 puffs_cookie_t opc; 232 char *name; 233 size_t namelen; 234 struct puffs_node **pnp; 235 { 236 char *path; 237 struct puffs_node *dpn = (struct puffs_node *)opc; 238 int error; 239 240 /* 241 * is easy as we already know it 242 */ 243 if (strncmp(name, ".", namelen) == 0) { 244 *pnp = (struct puffs_node *)opc; 245 return 0; 246 } 247 248 /* 249 * For .. we just forget the name part 250 */ 251 if (strncmp(name, "..", namelen) == 0) 252 namelen = 0; 253 254 namelen = PNPLEN(dpn) + 1 + namelen + 1; 255 if ((path = malloc(namelen)) == NULL) 256 DERR(EX_OSERR, "malloc failed"); 257 (void)snprintf(path, namelen, "%s/%s", (char *)PNPATH(dpn), name); 258 259 error = node_lookup_common(pu, opc, path, pnp); 260 261 free(path); 262 263 return error; 264 } 265 266 static int 267 node_lookup_common(pu, opc, path, pnp) 268 struct puffs_usermount *pu; 269 puffs_cookie_t opc; 270 const char *path; 271 struct puffs_node **pnp; 272 { 273 struct perfuse_state *ps; 274 perfuse_msg_t *pm; 275 struct fuse_entry_out *feo; 276 struct puffs_node *pn; 277 size_t len; 278 int error; 279 280 ps = puffs_getspecific(pu); 281 282 path = basename_r(path); 283 len = strlen(path) + 1; 284 285 pm = ps->ps_new_msg(pu, opc, FUSE_LOOKUP, len, NULL); 286 (void)strlcpy(_GET_INPAYLOAD(ps, pm, char *), path, len); 287 288 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*feo))) != 0) 289 goto out; 290 291 feo = GET_OUTPAYLOAD(ps, pm, fuse_entry_out); 292 293 pn = perfuse_new_pn(pu, opc); 294 PERFUSE_NODE_DATA(pn)->pnd_ino = feo->nodeid; 295 296 fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr); 297 298 if (pnp != NULL) 299 *pnp = pn; 300 301 out: 302 ps->ps_destroy_msg(pm); 303 304 return error; 305 } 306 307 308 /* 309 * Common final code for methods that create objects: 310 * perfuse_node_mkdir 311 * perfuse_node_mknod 312 * perfuse_node_symlink 313 */ 314 static int 315 node_mk_common(pu, opc, pni, pcn, pm) 316 struct puffs_usermount *pu; 317 puffs_cookie_t opc; 318 struct puffs_newinfo *pni; 319 const struct puffs_cn *pcn; 320 perfuse_msg_t *pm; 321 { 322 struct perfuse_state *ps; 323 struct puffs_node *pn; 324 struct fuse_entry_out *feo; 325 struct fuse_setattr_in *fsi; 326 int error; 327 328 ps = puffs_getspecific(pu); 329 330 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*feo))) != 0) 331 goto out; 332 333 feo = GET_OUTPAYLOAD(ps, pm, fuse_entry_out); 334 if (feo->nodeid == PERFUSE_UNKNOWN_INO) 335 DERRX(EX_SOFTWARE, "%s: no ino", __func__); 336 337 pn = perfuse_new_pn(pu, opc); 338 PERFUSE_NODE_DATA(pn)->pnd_ino = feo->nodeid; 339 340 fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr); 341 puffs_newinfo_setcookie(pni, pn); 342 ps->ps_destroy_msg(pm); 343 344 /* 345 * Set owner and group 346 */ 347 (void)puffs_cred_getuid(pcn->pcn_cred, &pn->pn_va.va_uid); 348 (void)puffs_cred_getgid(pcn->pcn_cred, &pn->pn_va.va_gid); 349 350 pm = ps->ps_new_msg(pu, (puffs_cookie_t)pn, 351 FUSE_SETATTR, sizeof(*fsi), NULL); 352 fsi = GET_INPAYLOAD(ps, pm, fuse_setattr_in); 353 fsi->uid = pn->pn_va.va_uid; 354 fsi->gid = pn->pn_va.va_gid; 355 fsi->valid = FUSE_FATTR_UID|FUSE_FATTR_GID; 356 357 /* 358 * A fuse_attr_out is returned, but we ignore it. 359 */ 360 error = XCHG_MSG(ps, pu, pm, sizeof(struct fuse_attr_out)); 361 362 /* 363 * The parent directory needs a sync 364 */ 365 PERFUSE_NODE_DATA(opc)->pnd_flags |= PND_DIRTY; 366 out: 367 ps->ps_destroy_msg(pm); 368 369 return error; 370 } 371 372 static const char * 373 basename_r(string) 374 const char *string; 375 { 376 char *result; 377 378 if ((result = rindex(string, '/')) == NULL) 379 return string; 380 381 /* 382 * We are finished if this is not a trailing / 383 */ 384 if (result[1] != '\0') 385 return result + 1; 386 387 388 /* 389 * Go back until we found something else than a / 390 */ 391 while (result != string) { 392 result--; 393 if (result[0] != '/') 394 break; 395 } 396 397 if (result == string) 398 return string; 399 400 if ((result = rindex(string, '/')) == NULL) 401 return string; 402 403 return result + 1; 404 405 } 406 407 static ssize_t 408 fuse_to_dirent(pu, opc, fd, fd_len) 409 struct puffs_usermount *pu; 410 puffs_cookie_t opc; 411 struct fuse_dirent *fd; 412 size_t fd_len; 413 { 414 struct dirent *dents; 415 size_t dents_len; 416 ssize_t written; 417 uint64_t fd_offset; 418 struct fuse_dirent *fd_base; 419 size_t len; 420 421 fd_base = fd; 422 fd_offset = 0; 423 written = 0; 424 dents = PERFUSE_NODE_DATA(opc)->pnd_dirent; 425 dents_len = (size_t)PERFUSE_NODE_DATA(opc)->pnd_dirent_len; 426 427 do { 428 char *ndp; 429 size_t reclen; 430 431 reclen = _DIRENT_RECLEN(dents, fd->namelen); 432 433 /* 434 * Check we do not overflow the output buffer 435 * struct fuse_dirent is bigger than struct dirent, 436 * so we should always use fd_len and never reallocate 437 * later. 438 * If we have to reallocate,try to double the buffer 439 * each time so that we do not have to do it too often. 440 */ 441 if (written + reclen > dents_len) { 442 if (dents_len == 0) 443 dents_len = fd_len; 444 else 445 dents_len = 446 MAX(2 * dents_len, written + reclen); 447 448 dents = PERFUSE_NODE_DATA(opc)->pnd_dirent; 449 if ((dents = realloc(dents, dents_len)) == NULL) 450 DERR(EX_OSERR, "malloc failed"); 451 452 PERFUSE_NODE_DATA(opc)->pnd_dirent = dents; 453 PERFUSE_NODE_DATA(opc)->pnd_dirent_len = dents_len; 454 455 /* 456 * (void *) for delint 457 */ 458 ndp = (char *)(void *)dents + written; 459 dents = (struct dirent *)(void *)ndp; 460 } 461 462 463 464 /* 465 * Filesystem was mounted without -o use_ino 466 * Perform a lookup to find it. 467 * XXX still broken 468 */ 469 if (fd->ino == PERFUSE_UNKNOWN_INO) { 470 struct puffs_node *pn; 471 472 if (node_lookup_dir_nodot(pu, opc, fd->name, 473 fd->namelen, &pn) != 0) 474 DERRX(EX_SOFTWARE, 475 "node_lookup_dir_nodot failed"); 476 477 fd->ino = PERFUSE_NODE_DATA(pn)->pnd_ino; 478 } 479 480 dents->d_fileno = fd->ino; 481 dents->d_reclen = (unsigned short)reclen; 482 dents->d_namlen = fd->namelen; 483 dents->d_type = fd->type; 484 strlcpy(dents->d_name, fd->name, fd->namelen + 1); 485 486 #ifdef PERFUSE_DEBUG 487 if (perfuse_diagflags & PDF_READDIR) 488 DPRINTF("%s: translated \"%s\" ino = %"PRId64"\n", 489 __func__, dents->d_name, dents->d_fileno); 490 #endif 491 492 dents = _DIRENT_NEXT(dents); 493 written += reclen; 494 495 /* 496 * Move to the next record. 497 * fd->off seems unreliable, for instance, flusterfs 498 * does not clear the unused bits, and we get 499 * 0xffffffffb9b95040 instead of just 0x40. Use 500 * record alignement instead. 501 */ 502 len = FUSE_DIRENT_ALIGN(sizeof(*fd) + fd->namelen); 503 #ifdef PERFUSE_DEBUG 504 if (perfuse_diagflags & PDF_READDIR) 505 DPRINTF("%s: record at %"PRId64"/0x%"PRIx64" " 506 "length = %zd/0x%zx. " 507 "next record at %"PRId64"/0x%"PRIx64" " 508 "max %zd/0x%zx\n", 509 __func__, fd_offset, fd_offset, len, len, 510 fd_offset + len, fd_offset + len, 511 fd_len, fd_len); 512 #endif 513 fd_offset += len; 514 515 /* 516 * Check if next record is still within the packet 517 * If it is not, we reached the end of the buffer. 518 */ 519 if (fd_offset >= fd_len) 520 break; 521 522 /* 523 * (void *) for delint 524 */ 525 ndp = (char *)(void *)fd_base + (size_t)fd_offset; 526 fd = (struct fuse_dirent *)(void *)ndp; 527 528 } while (1 /* CONSTCOND */); 529 530 /* 531 * Adjust the dirent output length 532 */ 533 if (written != -1) 534 PERFUSE_NODE_DATA(opc)->pnd_dirent_len = written; 535 536 return written; 537 } 538 539 /* ARGSUSED0 */ 540 static int 541 readdir_buffered(ps, opc, dent, readoff, 542 reslen, pcr, eofflag, cookies, ncookies) 543 struct perfuse_state *ps; 544 puffs_cookie_t opc; 545 struct dirent *dent; 546 off_t *readoff; 547 size_t *reslen; 548 const struct puffs_cred *pcr; 549 int *eofflag; 550 off_t *cookies; 551 size_t *ncookies; 552 { 553 struct dirent *fromdent; 554 struct perfuse_node_data *pnd; 555 char *ndp; 556 557 pnd = PERFUSE_NODE_DATA(opc); 558 559 while (*readoff < pnd->pnd_dirent_len) { 560 /* 561 * (void *) for delint 562 */ 563 ndp = (char *)(void *)pnd->pnd_dirent + (size_t)*readoff; 564 fromdent = (struct dirent *)(void *)ndp; 565 566 if (*reslen < _DIRENT_SIZE(fromdent)) 567 break; 568 569 memcpy(dent, fromdent, _DIRENT_SIZE(fromdent)); 570 *readoff += _DIRENT_SIZE(fromdent); 571 *reslen -= _DIRENT_SIZE(fromdent); 572 573 dent = _DIRENT_NEXT(dent); 574 } 575 576 #ifdef PERFUSE_DEBUG 577 if (perfuse_diagflags & PDF_READDIR) 578 DPRINTF("%s: readoff = %"PRId64", " 579 "pnd->pnd_dirent_len = %"PRId64"\n", 580 __func__, *readoff, pnd->pnd_dirent_len); 581 #endif 582 if (*readoff >= pnd->pnd_dirent_len) { 583 free(pnd->pnd_dirent); 584 pnd->pnd_dirent = NULL; 585 pnd->pnd_dirent_len = 0; 586 *eofflag = 1; 587 } 588 589 return 0; 590 } 591 592 /* ARGSUSED0 */ 593 static void 594 requeue_request(pu, opc, type) 595 struct puffs_usermount *pu; 596 puffs_cookie_t opc; 597 enum perfuse_qtype type; 598 { 599 struct perfuse_cc_queue pcq; 600 struct perfuse_node_data *pnd; 601 #ifdef PERFUSE_DEBUG 602 struct perfuse_state *ps; 603 604 ps = perfuse_getspecific(pu); 605 #endif 606 607 /* 608 * XXX Add a lock he day we go multithreaded 609 */ 610 pnd = PERFUSE_NODE_DATA(opc); 611 pcq.pcq_type = type; 612 pcq.pcq_cc = puffs_cc_getcc(pu); 613 TAILQ_INSERT_TAIL(&pnd->pnd_pcq, &pcq, pcq_next); 614 615 #ifdef PERFUSE_DEBUG 616 617 if (perfuse_diagflags & PDF_REQUEUE) 618 DPRINTF("%s: REQUEUE opc = %p, pcc = %p\n", 619 __func__, (void *)opc, pcq.pcq_cc); 620 #endif 621 622 puffs_cc_yield(pcq.pcq_cc); 623 TAILQ_REMOVE(&pnd->pnd_pcq, &pcq, pcq_next); 624 625 #ifdef PERFUSE_DEBUG 626 if (perfuse_diagflags & PDF_REQUEUE) 627 DPRINTF("%s: RESUME opc = %p, pcc = %p\n", 628 __func__, (void *)opc, pcq.pcq_cc); 629 #endif 630 631 return; 632 } 633 634 /* ARGSUSED0 */ 635 static int 636 dequeue_requests(ps, opc, type, max) 637 struct perfuse_state *ps; 638 puffs_cookie_t opc; 639 enum perfuse_qtype type; 640 int max; 641 { 642 struct perfuse_cc_queue *pcq; 643 struct perfuse_node_data *pnd; 644 int dequeued; 645 646 /* 647 * XXX Add a lock he day we go multithreaded 648 */ 649 pnd = PERFUSE_NODE_DATA(opc); 650 dequeued = 0; 651 TAILQ_FOREACH(pcq, &pnd->pnd_pcq, pcq_next) { 652 if (pcq->pcq_type != type) 653 continue; 654 655 #ifdef PERFUSE_DEBUG 656 if (perfuse_diagflags & PDF_REQUEUE) 657 DPRINTF("%s: SCHEDULE opc = %p, pcc = %p\n", 658 __func__, (void *)opc, pcq->pcq_cc); 659 #endif 660 puffs_cc_schedule(pcq->pcq_cc); 661 662 if (++dequeued == max) 663 break; 664 } 665 666 #ifdef PERFUSE_DEBUG 667 if (perfuse_diagflags & PDF_REQUEUE) 668 DPRINTF("%s: DONE opc = %p\n", __func__, (void *)opc); 669 #endif 670 671 return dequeued; 672 } 673 674 void 675 perfuse_fs_init(pu) 676 struct puffs_usermount *pu; 677 { 678 struct perfuse_state *ps; 679 perfuse_msg_t *pm; 680 struct fuse_init_in *fii; 681 struct fuse_init_out *fio; 682 int error; 683 684 ps = puffs_getspecific(pu); 685 686 if (puffs_mount(pu, ps->ps_target, ps->ps_mountflags, ps->ps_root) != 0) 687 DERR(EX_OSERR, "puffs_mount failed"); 688 689 /* 690 * Linux 2.6.34.1 sends theses flags: 691 * FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC 692 * FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK 693 * 694 * Linux also sets max_readahead at 32 pages (128 kB) 695 */ 696 pm = ps->ps_new_msg(pu, 0, FUSE_INIT, sizeof(*fii), NULL); 697 fii = GET_INPAYLOAD(ps, pm, fuse_init_in); 698 fii->major = FUSE_KERNEL_VERSION; 699 fii->minor = FUSE_KERNEL_MINOR_VERSION; 700 fii->max_readahead = 32 * PAGE_SIZE; 701 fii->flags = (FUSE_ASYNC_READ|FUSE_POSIX_LOCKS|FUSE_ATOMIC_O_TRUNC); 702 703 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fio))) != 0) 704 DERRX(EX_SOFTWARE, "init message exchange failed (%d)", error); 705 706 fio = GET_OUTPAYLOAD(ps, pm, fuse_init_out); 707 ps->ps_max_readahead = fio->max_readahead; 708 ps->ps_max_write = fio->max_write; 709 710 ps->ps_destroy_msg(pm); 711 712 return; 713 } 714 715 int 716 perfuse_fs_unmount(pu, flags) 717 struct puffs_usermount *pu; 718 int flags; 719 { 720 perfuse_msg_t *pm; 721 struct perfuse_state *ps; 722 puffs_cookie_t opc; 723 int error; 724 725 ps = puffs_getspecific(pu); 726 727 opc = (puffs_cookie_t)puffs_getroot(pu); 728 pm = ps->ps_new_msg(pu, opc, FUSE_DESTROY, 0, NULL); 729 730 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0) { 731 DWARN("unmount %s", ps->ps_target); 732 if (!(flags & MNT_FORCE)) 733 goto out; 734 } 735 736 DPRINTF("%s unmounted, exit\n", ps->ps_target); 737 738 exit(0); 739 out: 740 ps->ps_destroy_msg(pm); 741 742 return error; 743 } 744 745 int 746 perfuse_fs_statvfs(pu, svfsb) 747 struct puffs_usermount *pu; 748 struct statvfs *svfsb; 749 { 750 struct perfuse_state *ps; 751 perfuse_msg_t *pm; 752 puffs_cookie_t opc; 753 struct fuse_statfs_out *fso; 754 int error; 755 756 ps = puffs_getspecific(pu); 757 opc = (puffs_cookie_t)puffs_getroot(pu); 758 pm = ps->ps_new_msg(pu, opc, FUSE_STATFS, 0, NULL); 759 760 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fso))) != 0) 761 goto out; 762 763 fso = GET_OUTPAYLOAD(ps, pm, fuse_statfs_out); 764 svfsb->f_flag = ps->ps_mountflags; 765 svfsb->f_bsize = fso->st.bsize; 766 svfsb->f_frsize = fso->st.frsize; 767 svfsb->f_iosize = ((struct puffs_node *)opc)->pn_va.va_blocksize; 768 svfsb->f_blocks = fso->st.blocks; 769 svfsb->f_bfree = fso->st.bfree; 770 svfsb->f_bavail = fso->st.bavail; 771 svfsb->f_bresvd = fso->st.bfree - fso->st.bavail; 772 svfsb->f_files = fso->st.files; 773 svfsb->f_ffree = fso->st.ffree; 774 svfsb->f_favail = fso->st.ffree;/* files not reserved for root */ 775 svfsb->f_fresvd = 0; /* files reserved for root */ 776 777 svfsb->f_syncreads = ps->ps_syncreads; 778 svfsb->f_syncwrites = ps->ps_syncwrites; 779 780 svfsb->f_asyncreads = ps->ps_asyncreads; 781 svfsb->f_asyncwrites = ps->ps_asyncwrites; 782 783 svfsb->f_fsidx.__fsid_val[0] = (int32_t)ps->ps_fsid; 784 svfsb->f_fsidx.__fsid_val[1] = 0; 785 svfsb->f_fsid = ps->ps_fsid; 786 svfsb->f_namemax = MAXPATHLEN; /* XXX */ 787 svfsb->f_owner = ps->ps_owner_uid; 788 789 (void)strlcpy(svfsb->f_mntonname, ps->ps_target, _VFS_NAMELEN); 790 791 if (ps->ps_filesystemtype != NULL) 792 (void)strlcpy(svfsb->f_fstypename, 793 ps->ps_filesystemtype, _VFS_NAMELEN); 794 else 795 (void)strlcpy(svfsb->f_fstypename, "fuse", _VFS_NAMELEN); 796 797 if (ps->ps_source != NULL) 798 strlcpy(svfsb->f_mntfromname, ps->ps_source, _VFS_NAMELEN); 799 else 800 strlcpy(svfsb->f_mntfromname, _PATH_FUSE, _VFS_NAMELEN); 801 out: 802 ps->ps_destroy_msg(pm); 803 804 return error; 805 } 806 807 int 808 perfuse_fs_sync(pu, waitfor, pcr) 809 struct puffs_usermount *pu; 810 int waitfor; 811 const struct puffs_cred *pcr; 812 { 813 /* 814 * FUSE does not seem to have a FS sync callback. 815 * Maybe do not even register this callback 816 */ 817 return puffs_fsnop_sync(pu, waitfor, pcr); 818 } 819 820 /* ARGSUSED0 */ 821 int 822 perfuse_fs_fhtonode(pu, fid, fidsize, pni) 823 struct puffs_usermount *pu; 824 void *fid; 825 size_t fidsize; 826 struct puffs_newinfo *pni; 827 { 828 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__); 829 return 0; 830 } 831 832 /* ARGSUSED0 */ 833 int 834 perfuse_fs_nodetofh(pu, cookie, fid, fidsize) 835 struct puffs_usermount *pu; 836 puffs_cookie_t cookie; 837 void *fid; 838 size_t *fidsize; 839 { 840 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__); 841 return 0; 842 } 843 844 #if 0 845 /* ARGSUSED0 */ 846 void 847 perfuse_fs_extattrctl(pu, cmd, cookie, flags, namespace, attrname) 848 struct puffs_usermount *pu; 849 int cmd, 850 puffs_cookie_t *cookie; 851 int flags; 852 int namespace; 853 const char *attrname; 854 { 855 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__); 856 return 0; 857 } 858 #endif /* 0 */ 859 860 /* ARGSUSED0 */ 861 void 862 perfuse_fs_suspend(pu, status) 863 struct puffs_usermount *pu; 864 int status; 865 { 866 return; 867 } 868 869 870 871 int 872 perfuse_node_lookup(pu, opc, pni, pcn) 873 struct puffs_usermount *pu; 874 puffs_cookie_t opc; 875 struct puffs_newinfo *pni; 876 const struct puffs_cn *pcn; 877 { 878 struct puffs_node *pn; 879 int error; 880 881 /* 882 * Special case for .. 883 */ 884 if (PCNISDOTDOT(pcn)) { 885 pn = PERFUSE_NODE_DATA(opc)->pnd_parent; 886 PERFUSE_NODE_DATA(pn)->pnd_flags &= ~PND_RECLAIMED; 887 888 puffs_newinfo_setcookie(pni, pn); 889 puffs_newinfo_setvtype(pni, VDIR); 890 891 return 0; 892 } 893 894 /* 895 * XXX This is borrowed from librefuse, 896 * and __UNCONST is said to be fixed. 897 */ 898 pn = puffs_pn_nodewalk(pu, puffs_path_walkcmp, 899 __UNCONST(&pcn->pcn_po_full)); 900 901 if (pn == NULL) { 902 error = node_lookup_common(pu, opc, (char *)PCNPATH(pcn), &pn); 903 if (error != 0) 904 return error; 905 } 906 907 /* 908 * If that node had a pending reclaim, wipe it out. 909 */ 910 PERFUSE_NODE_DATA(pn)->pnd_flags &= ~PND_RECLAIMED; 911 912 puffs_newinfo_setcookie(pni, pn); 913 puffs_newinfo_setvtype(pni, pn->pn_va.va_type); 914 puffs_newinfo_setsize(pni, (voff_t)pn->pn_va.va_size); 915 puffs_newinfo_setrdev(pni, pn->pn_va.va_rdev); 916 917 return 0; 918 } 919 920 int 921 perfuse_node_create(pu, opc, pni, pcn, vap) 922 struct puffs_usermount *pu; 923 puffs_cookie_t opc; 924 struct puffs_newinfo *pni; 925 const struct puffs_cn *pcn; 926 const struct vattr *vap; 927 { 928 perfuse_msg_t *pm; 929 struct perfuse_state *ps; 930 struct fuse_create_in *fci; 931 struct fuse_entry_out *feo; 932 struct fuse_open_out *foo; 933 struct puffs_node *pn; 934 const char *name; 935 size_t namelen; 936 size_t len; 937 int error; 938 939 /* 940 * Create an object require -WX permission in the parent directory 941 */ 942 if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC)) 943 return EACCES; 944 945 /* 946 * If create is unimplemented: Check that it does not 947 * already exists, and if not, do mknod and open 948 */ 949 ps = puffs_getspecific(pu); 950 if (ps->ps_flags & PS_NO_CREAT) { 951 error = node_lookup_common(pu, opc, (char*)PCNPATH(pcn), &pn); 952 if (error == 0) 953 return EEXIST; 954 955 error = perfuse_node_mknod(pu, opc, pni, pcn, vap); 956 if (error != 0) 957 return error; 958 959 error = node_lookup_common(pu, opc, (char*)PCNPATH(pcn), &pn); 960 if (error != 0) 961 return error; 962 963 opc = (puffs_cookie_t)pn; 964 965 error = perfuse_node_open(pu, opc, FREAD|FWRITE, pcn->pcn_cred); 966 if (error != 0) 967 return error; 968 969 return 0; 970 } 971 972 name = basename_r((char *)PCNPATH(pcn)); 973 namelen = strlen(name) + 1; 974 len = sizeof(*fci) + namelen; 975 976 /* 977 * flags should use O_WRONLY instead of O_RDWR, but it 978 * breaks when the caller tries to read from file. 979 * 980 * mode must contain file type (ie: S_IFREG), use VTTOIF(vap->va_type) 981 */ 982 pm = ps->ps_new_msg(pu, opc, FUSE_CREATE, len, pcn->pcn_cred); 983 fci = GET_INPAYLOAD(ps, pm, fuse_create_in); 984 fci->flags = O_CREAT | O_TRUNC | O_RDWR; 985 fci->mode = vap->va_mode | VTTOIF(vap->va_type); 986 fci->umask = 0; /* Seems unused by libfuse */ 987 (void)strlcpy((char*)(void *)(fci + 1), name, namelen); 988 989 len = sizeof(*feo) + sizeof(*foo); 990 if ((error = XCHG_MSG(ps, pu, pm, len)) != 0) 991 goto out; 992 993 feo = GET_OUTPAYLOAD(ps, pm, fuse_entry_out); 994 foo = (struct fuse_open_out *)(void *)(feo + 1); 995 if (feo->nodeid == PERFUSE_UNKNOWN_INO) 996 DERRX(EX_SOFTWARE, "%s: no ino", __func__); 997 998 /* 999 * Save the file handle and inode in node private data 1000 * so that we can reuse it later 1001 */ 1002 pn = perfuse_new_pn(pu, opc); 1003 perfuse_new_fh((puffs_cookie_t)pn, foo->fh, FWRITE); 1004 PERFUSE_NODE_DATA(pn)->pnd_ino = feo->nodeid; 1005 1006 #ifdef PERFUSE_DEBUG 1007 if (perfuse_diagflags & PDF_FH) 1008 DPRINTF("%s: opc = %p, file = \"%s\", " 1009 "ino = %"PRId64", rfh = 0x%"PRIx64"\n", 1010 __func__, (void *)pn, (char *)PCNPATH(pcn), 1011 feo->nodeid, foo->fh); 1012 #endif 1013 1014 fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr); 1015 puffs_newinfo_setcookie(pni, pn); 1016 1017 /* 1018 * The parent directory needs a sync 1019 */ 1020 PERFUSE_NODE_DATA(opc)->pnd_flags |= PND_DIRTY; 1021 out: 1022 ps->ps_destroy_msg(pm); 1023 1024 /* 1025 * create is unimplmented, remember it for later, 1026 * and start over using mknod and open instead. 1027 */ 1028 if (error == ENOSYS) { 1029 ps->ps_flags |= PS_NO_CREAT; 1030 return perfuse_node_create(pu, opc, pni, pcn, vap); 1031 } 1032 1033 return error; 1034 } 1035 1036 1037 int 1038 perfuse_node_mknod(pu, opc, pni, pcn, vap) 1039 struct puffs_usermount *pu; 1040 puffs_cookie_t opc; 1041 struct puffs_newinfo *pni; 1042 const struct puffs_cn *pcn; 1043 const struct vattr *vap; 1044 { 1045 struct perfuse_state *ps; 1046 perfuse_msg_t *pm; 1047 struct fuse_mknod_in *fmi; 1048 const char* path; 1049 size_t len; 1050 1051 /* 1052 * Only superuser can mknod objects other than 1053 * directories, files, socks, fifo and links. 1054 * 1055 * Create an object require -WX permission in the parent directory 1056 */ 1057 switch (vap->va_type) { 1058 case VDIR: /* FALLTHROUGH */ 1059 case VREG: /* FALLTHROUGH */ 1060 case VFIFO: /* FALLTHROUGH */ 1061 case VSOCK: /* FALLTHROUGH */ 1062 case VLNK: 1063 if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC)) 1064 return EACCES; 1065 break; 1066 default: /* VNON, VBLK, VCHR, VBAD */ 1067 if (!puffs_cred_isjuggernaut(pcn->pcn_cred)) 1068 return EACCES; 1069 break; 1070 } 1071 1072 1073 ps = puffs_getspecific(pu); 1074 path = basename_r((char *)PCNPATH(pcn)); 1075 len = sizeof(*fmi) + strlen(path) + 1; 1076 1077 /* 1078 * mode must contain file type (ie: S_IFREG), use VTTOIF(vap->va_type) 1079 */ 1080 pm = ps->ps_new_msg(pu, opc, FUSE_MKNOD, len, pcn->pcn_cred); 1081 fmi = GET_INPAYLOAD(ps, pm, fuse_mknod_in); 1082 fmi->mode = vap->va_mode | VTTOIF(vap->va_type); 1083 fmi->rdev = (uint32_t)vap->va_rdev; 1084 fmi->umask = 0; /* Seems unused bu libfuse */ 1085 (void)strlcpy((char *)(void *)(fmi + 1), path, len - sizeof(*fmi)); 1086 1087 return node_mk_common(pu, opc, pni, pcn, pm); 1088 } 1089 1090 1091 int 1092 perfuse_node_open(pu, opc, mode, pcr) 1093 struct puffs_usermount *pu; 1094 puffs_cookie_t opc; 1095 int mode; 1096 const struct puffs_cred *pcr; 1097 { 1098 struct perfuse_state *ps; 1099 struct perfuse_node_data *pnd; 1100 perfuse_msg_t *pm; 1101 mode_t pmode; 1102 mode_t fmode; 1103 int op; 1104 struct fuse_open_in *foi; 1105 struct fuse_open_out *foo; 1106 struct puffs_node *pn; 1107 int error; 1108 1109 ps = puffs_getspecific(pu); 1110 pnd = PERFUSE_NODE_DATA(opc); 1111 1112 pn = (struct puffs_node *)opc; 1113 if (puffs_pn_getvap(pn)->va_type == VDIR) { 1114 op = FUSE_OPENDIR; 1115 pmode = PUFFS_VREAD|PUFFS_VEXEC; 1116 } else { 1117 op = FUSE_OPEN; 1118 if (mode & FWRITE) 1119 pmode = PUFFS_VWRITE|PUFFS_VREAD; 1120 else 1121 pmode = PUFFS_VREAD; 1122 } 1123 1124 /* 1125 * Opening a directory require R-X on the directory 1126 * Opening a file requires R-- for reading, -W- for writing 1127 * In both cases, --X is required on the parent. 1128 */ 1129 if (no_access((puffs_cookie_t)pnd->pnd_parent, pcr, PUFFS_VEXEC)) 1130 return EACCES; 1131 1132 if (no_access(opc, pcr, pmode)) 1133 return EACCES; 1134 1135 /* 1136 * libfuse docs say O_CREAT should not be set. 1137 */ 1138 mode &= ~O_CREAT; 1139 1140 /* 1141 * Do not open twice, and do not reopen for reading 1142 * if we already have write handle. 1143 */ 1144 if ((mode & FREAD) && (pnd->pnd_flags & PND_RFH)) 1145 return 0; 1146 if ((mode & FWRITE) && (pnd->pnd_flags & PND_WFH)) 1147 return 0; 1148 1149 /* 1150 * Convert PUFFS mode to FUSE mode: convert FREAD/FWRITE 1151 * to O_RDONLY/O_WRONLY while perserving the other options. 1152 */ 1153 fmode = mode & ~(FREAD|FWRITE); 1154 fmode |= (mode & FWRITE) ? O_RDWR : O_RDONLY; 1155 1156 pm = ps->ps_new_msg(pu, opc, op, sizeof(*foi), pcr); 1157 foi = GET_INPAYLOAD(ps, pm, fuse_open_in); 1158 foi->flags = fmode; 1159 foi->unused = 0; 1160 1161 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*foo))) != 0) 1162 goto out; 1163 1164 foo = GET_OUTPAYLOAD(ps, pm, fuse_open_out); 1165 1166 /* 1167 * Save the file handle in node private data 1168 * so that we can reuse it later 1169 */ 1170 perfuse_new_fh((puffs_cookie_t)pn, foo->fh, mode); 1171 1172 #ifdef PERFUSE_DEBUG 1173 if (perfuse_diagflags & PDF_FH) 1174 DPRINTF("%s: opc = %p, file = \"%s\", " 1175 "ino = %"PRId64", %s%sfh = 0x%"PRIx64"\n", 1176 __func__, (void *)opc, 1177 (char *)PNPATH((struct puffs_node *)opc), 1178 pnd->pnd_ino, mode & FREAD ? "r" : "", 1179 mode & FWRITE ? "w" : "", foo->fh); 1180 #endif 1181 out: 1182 ps->ps_destroy_msg(pm); 1183 1184 return error; 1185 } 1186 1187 /* ARGSUSED0 */ 1188 int 1189 perfuse_node_close(pu, opc, flags, pcr) 1190 struct puffs_usermount *pu; 1191 puffs_cookie_t opc; 1192 int flags; 1193 const struct puffs_cred *pcr; 1194 { 1195 struct puffs_node *pn; 1196 struct perfuse_node_data *pnd; 1197 1198 pn = (struct puffs_node *)opc; 1199 pnd = PERFUSE_NODE_DATA(opc); 1200 1201 if (!(pnd->pnd_flags & PND_OPEN)) 1202 return EBADF; 1203 1204 /* 1205 * The NetBSD kernel will send sync and setattr(mtime, ctime) 1206 * afer a close on a regular file. Some FUSE filesystem will 1207 * assume theses operations are performed on open files. We 1208 * therefore postpone the close operation at reclaim time. 1209 */ 1210 if (puffs_pn_getvap(pn)->va_type != VREG) 1211 return node_close_common(pu, opc, flags); 1212 1213 return 0; 1214 } 1215 1216 int 1217 perfuse_node_access(pu, opc, mode, pcr) 1218 struct puffs_usermount *pu; 1219 puffs_cookie_t opc; 1220 int mode; 1221 const struct puffs_cred *pcr; 1222 { 1223 perfuse_msg_t *pm; 1224 struct perfuse_state *ps; 1225 struct fuse_access_in *fai; 1226 int error; 1227 1228 if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_REMOVED) 1229 return ENOENT; 1230 1231 /* 1232 * If we previously detected the filesystem does not 1233 * implement access(), short-circuit the call and skip 1234 * to libpffs access() emulation. 1235 */ 1236 ps = puffs_getspecific(pu); 1237 if (ps->ps_flags & PS_NO_ACCESS) { 1238 error = ENOSYS; 1239 } else { 1240 pm = ps->ps_new_msg(pu, opc, FUSE_ACCESS, sizeof(*fai), pcr); 1241 fai = GET_INPAYLOAD(ps, pm, fuse_access_in); 1242 fai->mask = mode; 1243 1244 error = XCHG_MSG(ps, pu, pm, NO_PAYLOAD_REPLY_LEN); 1245 ps->ps_destroy_msg(pm); 1246 } 1247 1248 if (error == ENOSYS) { 1249 struct fuse_getattr_in *fgi; 1250 struct fuse_attr_out *fao; 1251 1252 ps->ps_flags |= PS_NO_ACCESS; 1253 1254 pm = ps->ps_new_msg(pu, opc, FUSE_GETATTR, 1255 sizeof(*fgi), NULL); 1256 fgi = GET_INPAYLOAD(ps, pm, fuse_getattr_in); 1257 fgi->getattr_flags = 0; 1258 fgi->dummy = 0; 1259 fgi->fh = perfuse_get_fh(opc, FREAD); 1260 1261 #ifdef PERFUSE_DEBUG 1262 if (perfuse_diagflags & PDF_FH) 1263 DPRINTF("%s: opc = %p, ino = %"PRId64", " 1264 "fh = 0x%"PRIx64"\n", __func__, (void *)opc, 1265 PERFUSE_NODE_DATA(opc)->pnd_ino, fgi->fh); 1266 #endif 1267 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fao))) != 0) { 1268 ps->ps_destroy_msg(pm); 1269 goto out; 1270 } 1271 1272 fao = GET_OUTPAYLOAD(ps, pm, fuse_attr_out); 1273 1274 error = puffs_access(VREG, fao->attr.mode, fao->attr.uid, 1275 fao->attr.gid, (mode_t)mode, pcr); 1276 1277 ps->ps_destroy_msg(pm); 1278 } 1279 1280 out: 1281 return error; 1282 } 1283 1284 int 1285 perfuse_node_getattr(pu, opc, vap, pcr) 1286 struct puffs_usermount *pu; 1287 puffs_cookie_t opc; 1288 struct vattr *vap; 1289 const struct puffs_cred *pcr; 1290 { 1291 perfuse_msg_t *pm; 1292 struct perfuse_state *ps; 1293 struct fuse_getattr_in *fgi; 1294 struct fuse_attr_out *fao; 1295 int error; 1296 1297 if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_REMOVED) 1298 return ENOENT; 1299 1300 /* 1301 * getattr requires --X on the parent directory 1302 */ 1303 if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent, 1304 pcr, PUFFS_VEXEC)) 1305 return EACCES; 1306 1307 ps = puffs_getspecific(pu); 1308 1309 /* 1310 * FUSE_GETATTR_FH must be set in fgi->flags 1311 * if we use for fgi->fh, but we do not. 1312 */ 1313 pm = ps->ps_new_msg(pu, opc, FUSE_GETATTR, sizeof(*fgi), pcr); 1314 fgi = GET_INPAYLOAD(ps, pm, fuse_getattr_in); 1315 fgi->getattr_flags = 0; 1316 fgi->dummy = 0; 1317 fgi->fh = 0; 1318 1319 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fao))) != 0) 1320 goto out; 1321 1322 fao = GET_OUTPAYLOAD(ps, pm, fuse_attr_out); 1323 1324 /* 1325 * The message from filesystem has a cache timeout 1326 * XXX this is ignored yet, is that right? 1327 * 1328 * We also set birthtime, flags, filerev,vaflags to 0. 1329 * This seems the best bet, since the information is 1330 * not available from filesystem. 1331 */ 1332 fuse_attr_to_vap(ps, vap, &fao->attr); 1333 1334 out: 1335 ps->ps_destroy_msg(pm); 1336 1337 return error; 1338 } 1339 1340 int 1341 perfuse_node_setattr(pu, opc, vap, pcr) 1342 struct puffs_usermount *pu; 1343 puffs_cookie_t opc; 1344 const struct vattr *vap; 1345 const struct puffs_cred *pcr; 1346 { 1347 perfuse_msg_t *pm; 1348 uint64_t fh; 1349 struct perfuse_state *ps; 1350 struct perfuse_node_data *pnd; 1351 struct fuse_setattr_in *fsi; 1352 int error; 1353 struct vattr *old_va; 1354 1355 ps = puffs_getspecific(pu); 1356 pnd = PERFUSE_NODE_DATA(opc); 1357 1358 /* 1359 * The only operation we can do once the file is removed 1360 * is to resize it, and we can do it only if it is open. 1361 */ 1362 if (pnd->pnd_flags & PND_REMOVED) { 1363 if (!(pnd->pnd_flags & PND_OPEN)) 1364 return ENOENT; 1365 1366 if (vap->va_size == (u_quad_t)PUFFS_VNOVAL) 1367 return 0; 1368 } 1369 1370 /* 1371 * setattr requires --X on the parent directory 1372 */ 1373 if (no_access((puffs_cookie_t)pnd->pnd_parent, pcr, PUFFS_VEXEC)) 1374 return EACCES; 1375 1376 old_va = puffs_pn_getvap((struct puffs_node *)opc); 1377 1378 /* 1379 * Check for permission to change size 1380 */ 1381 if ((vap->va_size != (u_quad_t)PUFFS_VNOVAL) && 1382 no_access(opc, pcr, PUFFS_VWRITE)) 1383 return EACCES; 1384 1385 /* 1386 * Check for permission to change dates 1387 */ 1388 if (((vap->va_atime.tv_sec != (time_t)PUFFS_VNOVAL) || 1389 (vap->va_mtime.tv_sec != (time_t)PUFFS_VNOVAL)) && 1390 (puffs_access_times(old_va->va_uid, old_va->va_gid, 1391 old_va->va_mode, 0, pcr) != 0)) 1392 return EACCES; 1393 1394 /* 1395 * Check for permission to change owner and group 1396 */ 1397 if (((vap->va_uid != (uid_t)PUFFS_VNOVAL) || 1398 (vap->va_gid != (gid_t)PUFFS_VNOVAL)) && 1399 (puffs_access_chown(old_va->va_uid, old_va->va_gid, 1400 vap->va_uid, vap->va_gid, pcr)) != 0) 1401 return EACCES; 1402 1403 /* 1404 * Check for permission to change permissions 1405 */ 1406 if ((vap->va_mode != (mode_t)PUFFS_VNOVAL) && 1407 (puffs_access_chmod(old_va->va_uid, old_va->va_gid, 1408 old_va->va_type, vap->va_mode, pcr)) != 0) 1409 return EACCES; 1410 1411 /* 1412 * It seems troublesome to resize a file while 1413 * a write is just beeing done. Wait for 1414 * it to finish. 1415 */ 1416 if (vap->va_size != (u_quad_t)PUFFS_VNOVAL) 1417 while (pnd->pnd_flags & PND_INWRITE) 1418 requeue_request(pu, opc, PCQ_AFTERWRITE); 1419 1420 1421 pm = ps->ps_new_msg(pu, opc, FUSE_SETATTR, sizeof(*fsi), pcr); 1422 fsi = GET_INPAYLOAD(ps, pm, fuse_setattr_in); 1423 fsi->valid = 0; 1424 1425 if (pnd->pnd_flags & PND_WFH) { 1426 fh = perfuse_get_fh(opc, FWRITE); 1427 fsi->fh = fh; 1428 fsi->valid |= FUSE_FATTR_FH; 1429 } 1430 1431 if (vap->va_size != (u_quad_t)PUFFS_VNOVAL) { 1432 fsi->size = vap->va_size; 1433 fsi->valid |= FUSE_FATTR_SIZE; 1434 } 1435 1436 if (vap->va_atime.tv_sec != (time_t)PUFFS_VNOVAL) { 1437 fsi->atime = vap->va_atime.tv_sec;; 1438 fsi->atimensec = (uint32_t)vap->va_atime.tv_nsec;; 1439 fsi->valid |= (FUSE_FATTR_ATIME|FUSE_FATTR_ATIME_NOW); 1440 } 1441 1442 if (vap->va_mtime.tv_sec != (time_t)PUFFS_VNOVAL) { 1443 fsi->mtime = vap->va_mtime.tv_sec;; 1444 fsi->mtimensec = (uint32_t)vap->va_mtime.tv_nsec;; 1445 fsi->valid |= (FUSE_FATTR_MTIME|FUSE_FATTR_MTIME_NOW); 1446 } 1447 1448 if (vap->va_mode != (mode_t)PUFFS_VNOVAL) { 1449 fsi->mode = vap->va_mode; 1450 fsi->valid |= FUSE_FATTR_MODE; 1451 } 1452 1453 if (vap->va_uid != (uid_t)PUFFS_VNOVAL) { 1454 fsi->uid = vap->va_uid; 1455 fsi->valid |= FUSE_FATTR_UID; 1456 } 1457 1458 if (vap->va_gid != (gid_t)PUFFS_VNOVAL) { 1459 fsi->gid = vap->va_gid; 1460 fsi->valid |= FUSE_FATTR_GID; 1461 } 1462 1463 if (pnd->pnd_lock_owner != 0) { 1464 fsi->lock_owner = pnd->pnd_lock_owner; 1465 fsi->valid |= FUSE_FATTR_LOCKOWNER; 1466 } 1467 1468 /* 1469 * If node was removed, ignore anything but resize 1470 * This works around glusterfs' 1471 * "SETATTR (null) (fuse_loc_fill() failed), ret = -2" 1472 */ 1473 if (pnd->pnd_flags & PND_REMOVED) 1474 fsi->valid &= 1475 (FUSE_FATTR_SIZE | FUSE_FATTR_FH | FUSE_FATTR_LOCKOWNER); 1476 1477 /* 1478 * A fuse_attr_out is returned, but we ignore it. 1479 */ 1480 error = XCHG_MSG(ps, pu, pm, sizeof(struct fuse_attr_out)); 1481 1482 ps->ps_destroy_msg(pm); 1483 1484 return error; 1485 } 1486 1487 int 1488 perfuse_node_poll(pu, opc, events) 1489 struct puffs_usermount *pu; 1490 puffs_cookie_t opc; 1491 int *events; 1492 { 1493 struct perfuse_state *ps; 1494 perfuse_msg_t *pm; 1495 struct fuse_poll_in *fpi; 1496 struct fuse_poll_out *fpo; 1497 int error; 1498 1499 ps = puffs_getspecific(pu); 1500 /* 1501 * kh is set if FUSE_POLL_SCHEDULE_NOTIFY is set. 1502 */ 1503 pm = ps->ps_new_msg(pu, opc, FUSE_POLL, sizeof(*fpi), NULL); 1504 fpi = GET_INPAYLOAD(ps, pm, fuse_poll_in); 1505 fpi->fh = perfuse_get_fh(opc, FREAD); 1506 fpi->kh = 0; 1507 fpi->flags = 0; 1508 1509 #ifdef PERFUSE_DEBUG 1510 if (perfuse_diagflags & PDF_FH) 1511 DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n", 1512 __func__, (void *)opc, 1513 PERFUSE_NODE_DATA(opc)->pnd_ino, fpi->fh); 1514 #endif 1515 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fpo))) != 0) 1516 goto out; 1517 1518 fpo = GET_OUTPAYLOAD(ps, pm, fuse_poll_out); 1519 *events = fpo->revents; 1520 out: 1521 ps->ps_destroy_msg(pm); 1522 1523 return error; 1524 } 1525 1526 /* ARGSUSED0 */ 1527 int 1528 perfuse_node_mmap(pu, opc, flags, pcr) 1529 struct puffs_usermount *pu; 1530 puffs_cookie_t opc; 1531 int flags; 1532 const struct puffs_cred *pcr; 1533 { 1534 /* 1535 * Not implemented anymore in libfuse 1536 */ 1537 return ENOSYS; 1538 } 1539 1540 /* ARGSUSED2 */ 1541 int 1542 perfuse_node_fsync(pu, opc, pcr, flags, offlo, offhi) 1543 struct puffs_usermount *pu; 1544 puffs_cookie_t opc; 1545 const struct puffs_cred *pcr; 1546 int flags; 1547 off_t offlo; 1548 off_t offhi; 1549 { 1550 int op; 1551 perfuse_msg_t *pm; 1552 struct perfuse_state *ps; 1553 struct perfuse_node_data *pnd; 1554 struct fuse_fsync_in *ffi; 1555 uint64_t fh; 1556 int open_self; 1557 int error; 1558 1559 pm = NULL; 1560 open_self = 0; 1561 ps = puffs_getspecific(pu); 1562 1563 if (puffs_pn_getvap((struct puffs_node *)opc)->va_type == VDIR) 1564 op = FUSE_FSYNCDIR; 1565 else /* VREG but also other types such as VLNK */ 1566 op = FUSE_FSYNC; 1567 1568 /* 1569 * Do not sync if there are no change to sync 1570 * XXX remove that test on files if we implement mmap 1571 */ 1572 pnd = PERFUSE_NODE_DATA(opc); 1573 #ifdef PERFUSE_DEBUG 1574 if (perfuse_diagflags & PDF_SYNC) 1575 DPRINTF("%s: TEST opc = %p, file = \"%s\" is %sdirty\n", 1576 __func__, (void*)opc, 1577 (char *)PNPATH((struct puffs_node *)opc), 1578 pnd->pnd_flags & PND_DIRTY ? "" : "not "); 1579 #endif 1580 if (!(pnd->pnd_flags & PND_DIRTY)) 1581 return 0; 1582 1583 /* 1584 * It seems NetBSD can call fsync without open first 1585 * glusterfs complain in such a situation: 1586 * "FSYNC() ERR => -1 (Invalid argument)" 1587 */ 1588 if (!(pnd->pnd_flags & PND_WFH)) { 1589 if ((error = perfuse_node_open(pu, opc, FWRITE, pcr)) != 0) 1590 goto out; 1591 open_self = 1; 1592 } 1593 1594 fh = perfuse_get_fh(opc, FWRITE); 1595 1596 /* 1597 * If fsync_flags is set, meta data should not be flushed. 1598 */ 1599 pm = ps->ps_new_msg(pu, opc, op, sizeof(*ffi), NULL); 1600 ffi = GET_INPAYLOAD(ps, pm, fuse_fsync_in); 1601 ffi->fh = fh; 1602 ffi->fsync_flags = (flags & FFILESYNC) ? 0 : 1; 1603 1604 #ifdef PERFUSE_DEBUG 1605 if (perfuse_diagflags & PDF_FH) 1606 DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n", 1607 __func__, (void *)opc, 1608 PERFUSE_NODE_DATA(opc)->pnd_ino, ffi->fh); 1609 #endif 1610 1611 if ((error = XCHG_MSG(ps, pu, pm, NO_PAYLOAD_REPLY_LEN)) != 0) 1612 goto out; 1613 1614 /* 1615 * No reply beyond fuse_out_header: nothing to do on success 1616 * just clear the dirty flag 1617 */ 1618 pnd->pnd_flags &= ~PND_DIRTY; 1619 1620 #ifdef PERFUSE_DEBUG 1621 if (perfuse_diagflags & PDF_SYNC) 1622 DPRINTF("%s: CLEAR opc = %p, file = \"%s\"\n", 1623 __func__, (void*)opc, 1624 (char *)PNPATH((struct puffs_node *)opc)); 1625 #endif 1626 1627 out: 1628 /* 1629 * ENOSYS is not returned to kernel, 1630 */ 1631 if (error == ENOSYS) 1632 error = 0; 1633 1634 if (pm != NULL) 1635 ps->ps_destroy_msg(pm); 1636 1637 if (open_self) 1638 (void)node_close_common(pu, opc, FWRITE); 1639 1640 return error; 1641 } 1642 1643 /* ARGSUSED0 */ 1644 int 1645 perfuse_node_seek(pu, opc, oldoff, newoff, pcr) 1646 struct puffs_usermount *pu; 1647 puffs_cookie_t opc; 1648 off_t oldoff; 1649 off_t newoff; 1650 const struct puffs_cred *pcr; 1651 { 1652 /* 1653 * XXX what should I do with oldoff? 1654 * XXX where is the newoffset returned? 1655 * XXX the held seek pointer seems just unused 1656 */ 1657 PERFUSE_NODE_DATA(opc)->pnd_offset = newoff; 1658 1659 return 0; 1660 } 1661 1662 int 1663 perfuse_node_remove(pu, opc, targ, pcn) 1664 struct puffs_usermount *pu; 1665 puffs_cookie_t opc; 1666 puffs_cookie_t targ; 1667 const struct puffs_cn *pcn; 1668 { 1669 struct perfuse_state *ps; 1670 struct puffs_node *pn; 1671 struct perfuse_node_data *pnd; 1672 perfuse_msg_t *pm; 1673 char *path; 1674 const char *name; 1675 size_t len; 1676 int error; 1677 1678 pnd = PERFUSE_NODE_DATA(opc); 1679 1680 /* 1681 * remove requires -WX on the parent directory 1682 * no right required on the object. 1683 */ 1684 if (no_access((puffs_cookie_t)pnd->pnd_parent, 1685 pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC)) 1686 return EACCES; 1687 1688 if (targ == NULL) 1689 DERRX(EX_SOFTWARE, "%s: targ is NULL", __func__); 1690 1691 ps = puffs_getspecific(pu); 1692 pnd = PERFUSE_NODE_DATA(opc); 1693 pn = (struct puffs_node *)targ; 1694 name = basename_r((char *)PNPATH(pn)); 1695 len = strlen(name) + 1; 1696 1697 pm = ps->ps_new_msg(pu, opc, FUSE_UNLINK, len, pcn->pcn_cred); 1698 path = _GET_INPAYLOAD(ps, pm, char *); 1699 (void)strlcpy(path, name, len); 1700 1701 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0) 1702 goto out; 1703 1704 if (puffs_inval_namecache_dir(pu, opc) != 0) 1705 DERR(EX_OSERR, "puffs_inval_namecache_dir failed"); 1706 1707 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); 1708 1709 PERFUSE_NODE_DATA(targ)->pnd_flags |= PND_REMOVED; 1710 1711 /* 1712 * Reclaim should take care of decreasing pnd_childcount 1713 */ 1714 1715 /* 1716 * The parent directory needs a sync 1717 */ 1718 PERFUSE_NODE_DATA(opc)->pnd_flags |= PND_DIRTY; 1719 out: 1720 ps->ps_destroy_msg(pm); 1721 1722 return error; 1723 } 1724 1725 int 1726 perfuse_node_link(pu, opc, targ, pcn) 1727 struct puffs_usermount *pu; 1728 puffs_cookie_t opc; 1729 puffs_cookie_t targ; 1730 const struct puffs_cn *pcn; 1731 { 1732 struct perfuse_state *ps; 1733 perfuse_msg_t *pm; 1734 const char *name; 1735 size_t len; 1736 struct puffs_node *pn; 1737 struct fuse_link_in *fli; 1738 int error; 1739 1740 /* 1741 * Create an object require -WX permission in the parent directory 1742 */ 1743 if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC)) 1744 return EACCES; 1745 1746 1747 ps = puffs_getspecific(pu); 1748 pn = (struct puffs_node *)targ; 1749 name = basename_r((char *)PCNPATH(pcn)); 1750 len = sizeof(*fli) + strlen(name) + 1; 1751 1752 pm = ps->ps_new_msg(pu, opc, FUSE_LINK, len, pcn->pcn_cred); 1753 fli = GET_INPAYLOAD(ps, pm, fuse_link_in); 1754 fli->oldnodeid = PERFUSE_NODE_DATA(pn)->pnd_ino; 1755 (void)strlcpy((char *)(void *)(fli + 1), name, len - sizeof(*fli)); 1756 1757 error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN); 1758 1759 ps->ps_destroy_msg(pm); 1760 1761 return error; 1762 } 1763 1764 /* targ is unused since the name is in pcn_targ */ 1765 /* ARGSUSED5 */ 1766 int 1767 perfuse_node_rename(pu, opc, src, pcn_src, targ_dir, targ, pcn_targ) 1768 struct puffs_usermount *pu; 1769 puffs_cookie_t opc; 1770 puffs_cookie_t src; 1771 const struct puffs_cn *pcn_src; 1772 puffs_cookie_t targ_dir; 1773 puffs_cookie_t targ; 1774 const struct puffs_cn *pcn_targ; 1775 { 1776 struct perfuse_state *ps; 1777 perfuse_msg_t *pm; 1778 struct fuse_rename_in *fri; 1779 const char *newname; 1780 const char *oldname; 1781 char *np; 1782 int error; 1783 size_t len; 1784 size_t newname_len; 1785 size_t oldname_len; 1786 1787 /* 1788 * move requires -WX on source and destination directory 1789 */ 1790 if (no_access(opc, pcn_src->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC) || 1791 no_access(targ_dir, pcn_targ->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC)) 1792 return EACCES; 1793 1794 ps = puffs_getspecific(pu); 1795 newname = basename_r((char *)PCNPATH(pcn_targ)); 1796 newname_len = strlen(newname) + 1; 1797 oldname = basename_r((char *)PCNPATH(pcn_src)); 1798 oldname_len = strlen(oldname) + 1; 1799 1800 len = sizeof(*fri) + oldname_len + newname_len; 1801 pm = ps->ps_new_msg(pu, opc, FUSE_RENAME, len, pcn_src->pcn_cred); 1802 fri = GET_INPAYLOAD(ps, pm, fuse_rename_in); 1803 fri->newdir = PERFUSE_NODE_DATA(targ_dir)->pnd_ino; 1804 np = (char *)(void *)(fri + 1); 1805 (void)strlcpy(np, oldname, oldname_len); 1806 np += oldname_len; 1807 (void)strlcpy(np, newname, newname_len); 1808 1809 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0) 1810 goto out; 1811 1812 /* 1813 * Update source and destination directories child count 1814 * Update moved object parent directory 1815 */ 1816 PERFUSE_NODE_DATA(opc)->pnd_childcount--; 1817 PERFUSE_NODE_DATA(targ_dir)->pnd_childcount++; 1818 PERFUSE_NODE_DATA(src)->pnd_parent = targ_dir; 1819 1820 out: 1821 ps->ps_destroy_msg(pm); 1822 1823 return error; 1824 } 1825 1826 int 1827 perfuse_node_mkdir(pu, opc, pni, pcn, vap) 1828 struct puffs_usermount *pu; 1829 puffs_cookie_t opc; 1830 struct puffs_newinfo *pni; 1831 const struct puffs_cn *pcn; 1832 const struct vattr *vap; 1833 { 1834 struct perfuse_state *ps; 1835 perfuse_msg_t *pm; 1836 struct fuse_mkdir_in *fmi; 1837 const char *path; 1838 size_t len; 1839 1840 /* 1841 * Create an object require -WX permission in the parent directory 1842 */ 1843 if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC)) 1844 return EACCES; 1845 1846 ps = puffs_getspecific(pu); 1847 path = basename_r((char *)PCNPATH(pcn)); 1848 len = sizeof(*fmi) + strlen(path) + 1; 1849 1850 pm = ps->ps_new_msg(pu, opc, FUSE_MKDIR, len, pcn->pcn_cred); 1851 fmi = GET_INPAYLOAD(ps, pm, fuse_mkdir_in); 1852 fmi->mode = vap->va_mode; 1853 fmi->umask = 0; /* Seems unused by libfuse? */ 1854 (void)strlcpy((char *)(void *)(fmi + 1), path, len - sizeof(*fmi)); 1855 1856 return node_mk_common(pu, opc, pni, pcn, pm); 1857 } 1858 1859 1860 int 1861 perfuse_node_rmdir(pu, opc, targ, pcn) 1862 struct puffs_usermount *pu; 1863 puffs_cookie_t opc; 1864 puffs_cookie_t targ; 1865 const struct puffs_cn *pcn; 1866 { 1867 struct perfuse_state *ps; 1868 struct perfuse_node_data *pnd; 1869 perfuse_msg_t *pm; 1870 struct puffs_node *pn; 1871 char *path; 1872 const char *name; 1873 size_t len; 1874 int error; 1875 1876 pnd = PERFUSE_NODE_DATA(opc); 1877 1878 /* 1879 * remove requires -WX on the parent directory 1880 * no right required on the object. 1881 */ 1882 if (no_access((puffs_cookie_t)pnd->pnd_parent, 1883 pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC)) 1884 return EACCES; 1885 1886 ps = puffs_getspecific(pu); 1887 pn = (struct puffs_node *)targ; 1888 name = basename_r((char *)PNPATH(pn)); 1889 len = strlen(name) + 1; 1890 1891 pm = ps->ps_new_msg(pu, opc, FUSE_RMDIR, len, pcn->pcn_cred); 1892 path = _GET_INPAYLOAD(ps, pm, char *); 1893 (void)strlcpy(path, name, len); 1894 1895 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0) 1896 goto out; 1897 1898 if (puffs_inval_namecache_dir(pu, opc) != 0) 1899 DERR(EX_OSERR, "puffs_inval_namecache_dir failed"); 1900 1901 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); 1902 1903 PERFUSE_NODE_DATA(targ)->pnd_flags |= PND_REMOVED; 1904 1905 /* 1906 * The parent directory needs a sync 1907 */ 1908 PERFUSE_NODE_DATA(opc)->pnd_flags |= PND_DIRTY; 1909 out: 1910 ps->ps_destroy_msg(pm); 1911 1912 return error; 1913 } 1914 1915 /* vap is unused */ 1916 /* ARGSUSED4 */ 1917 int 1918 perfuse_node_symlink(pu, opc, pni, pcn_src, vap, link_target) 1919 struct puffs_usermount *pu; 1920 puffs_cookie_t opc; 1921 struct puffs_newinfo *pni; 1922 const struct puffs_cn *pcn_src; 1923 const struct vattr *vap; 1924 const char *link_target; 1925 { 1926 struct perfuse_state *ps; 1927 perfuse_msg_t *pm; 1928 char *np; 1929 const char *path; 1930 size_t path_len; 1931 size_t linkname_len; 1932 size_t len; 1933 1934 /* 1935 * Create an object require -WX permission in the parent directory 1936 */ 1937 if (no_access(opc, pcn_src->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC)) 1938 return EACCES; 1939 1940 ps = puffs_getspecific(pu); 1941 path = basename_r((char *)PCNPATH(pcn_src)); 1942 path_len = strlen(path) + 1; 1943 linkname_len = strlen(link_target) + 1; 1944 len = path_len + linkname_len; 1945 1946 pm = ps->ps_new_msg(pu, opc, FUSE_SYMLINK, len, pcn_src->pcn_cred); 1947 np = _GET_INPAYLOAD(ps, pm, char *); 1948 (void)strlcpy(np, path, path_len); 1949 np += path_len; 1950 (void)strlcpy(np, link_target, linkname_len); 1951 1952 return node_mk_common(pu, opc, pni, pcn_src, pm); 1953 } 1954 1955 int 1956 perfuse_node_readdir(pu, opc, dent, readoff, 1957 reslen, pcr, eofflag, cookies, ncookies) 1958 struct puffs_usermount *pu; 1959 puffs_cookie_t opc; 1960 struct dirent *dent; 1961 off_t *readoff; 1962 size_t *reslen; 1963 const struct puffs_cred *pcr; 1964 int *eofflag; 1965 off_t *cookies; 1966 size_t *ncookies; 1967 { 1968 perfuse_msg_t *pm; 1969 uint64_t fh; 1970 struct perfuse_state *ps; 1971 struct perfuse_node_data *pnd; 1972 struct fuse_read_in *fri; 1973 struct fuse_out_header *foh; 1974 struct fuse_dirent *fd; 1975 size_t foh_len; 1976 int error; 1977 int open_self; 1978 uint64_t fd_offset; 1979 1980 pm = NULL; 1981 error = 0; 1982 open_self = 0; 1983 ps = puffs_getspecific(pu); 1984 1985 /* 1986 * readdir state is kept at node level, and several readdir 1987 * requests can be issued at the same time on the same node. 1988 * We need to queue requests so that only one is in readdir 1989 * code at the same time. 1990 */ 1991 pnd = PERFUSE_NODE_DATA(opc); 1992 while (pnd->pnd_flags & PND_INREADDIR) 1993 requeue_request(pu, opc, PCQ_READDIR); 1994 pnd->pnd_flags |= PND_INREADDIR; 1995 1996 #ifdef PERFUSE_DEBUG 1997 if (perfuse_diagflags & PDF_READDIR) 1998 DPRINTF("%s: READDIR opc = %p enter critical section\n", 1999 __func__, (void *)opc); 2000 #endif 2001 /* 2002 * Do we already have the data bufered? 2003 */ 2004 if (pnd->pnd_dirent != NULL) 2005 goto out; 2006 pnd->pnd_dirent_len = 0; 2007 2008 /* 2009 * It seems NetBSD can call readdir without open first 2010 * libfuse will crash if it is done that way, hence open first. 2011 */ 2012 if (!(pnd->pnd_flags & PND_OPEN)) { 2013 if ((error = perfuse_node_open(pu, opc, FREAD, pcr)) != 0) 2014 goto out; 2015 open_self = 1; 2016 } 2017 2018 fh = perfuse_get_fh(opc, FREAD); 2019 2020 #ifdef PERFUSE_DEBUG 2021 if (perfuse_diagflags & PDF_FH) 2022 DPRINTF("%s: opc = %p, ino = %"PRId64", rfh = 0x%"PRIx64"\n", 2023 __func__, (void *)opc, 2024 PERFUSE_NODE_DATA(opc)->pnd_ino, fh); 2025 #endif 2026 2027 pnd->pnd_all_fd = NULL; 2028 pnd->pnd_all_fd_len = 0; 2029 fd_offset = 0; 2030 2031 do { 2032 size_t fd_len; 2033 char *afdp; 2034 2035 pm = ps->ps_new_msg(pu, opc, FUSE_READDIR, sizeof(*fri), pcr); 2036 2037 /* 2038 * read_flags, lock_owner and flags are unused in libfuse 2039 * 2040 * XXX if fri->size is too big (bigger than PAGE_SIZE?), * we get strange bugs. ktrace shows 16 bytes or garbage 2041 * at the end of sent frames, but perfused does not receive 2042 * that data. The data length is hoverver the same, which 2043 * cause perfused to use the last 16 bytes of the frame 2044 * as the frame header of the next frame. 2045 * 2046 * This may be a kernel bug. 2047 */ 2048 fri = GET_INPAYLOAD(ps, pm, fuse_read_in); 2049 fri->fh = fh; 2050 fri->offset = fd_offset; 2051 fri->size = PAGE_SIZE - sizeof(struct fuse_out_header); 2052 fri->read_flags = 0; 2053 fri->lock_owner = 0; 2054 fri->flags = 0; 2055 2056 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0) 2057 goto out; 2058 2059 /* 2060 * There are many puffs_framebufs calls later, 2061 * therefore foh will not be valid for a long time. 2062 * Just get the length and forget it. 2063 */ 2064 foh = GET_OUTHDR(ps, pm); 2065 foh_len = foh->len; 2066 2067 /* 2068 * It seems that the only way to discover the end 2069 * of the buffer is to get an empty read 2070 */ 2071 if (foh_len == sizeof(*foh)) 2072 break; 2073 2074 /* 2075 * Corrupted message. 2076 */ 2077 if (foh_len < sizeof(*foh) + sizeof(*fd)) { 2078 DWARNX("readdir reply too short"); 2079 error = EIO; 2080 goto out; 2081 } 2082 2083 2084 fd = GET_OUTPAYLOAD(ps, pm, fuse_dirent); 2085 fd_len = foh_len - sizeof(*foh); 2086 2087 pnd->pnd_all_fd = realloc(pnd->pnd_all_fd, 2088 pnd->pnd_all_fd_len + fd_len); 2089 if (pnd->pnd_all_fd == NULL) 2090 DERR(EX_OSERR, "malloc failed"); 2091 2092 afdp = (char *)(void *)pnd->pnd_all_fd + pnd->pnd_all_fd_len; 2093 (void)memcpy(afdp, fd, fd_len); 2094 2095 pnd->pnd_all_fd_len += fd_len; 2096 fd_offset += fd_len; 2097 2098 ps->ps_destroy_msg(pm); 2099 pm = NULL; 2100 } while (1 /* CONSTCOND */); 2101 2102 if (fuse_to_dirent(pu, opc, pnd->pnd_all_fd, pnd->pnd_all_fd_len) == -1) 2103 error = EIO; 2104 2105 out: 2106 if (pnd->pnd_all_fd != NULL) { 2107 free(pnd->pnd_all_fd); 2108 pnd->pnd_all_fd = NULL; 2109 pnd->pnd_all_fd_len = 0; 2110 } 2111 2112 if (pm != NULL) 2113 ps->ps_destroy_msg(pm); 2114 2115 /* 2116 * If we opened the directory ourselves, close now 2117 * errors are ignored. 2118 */ 2119 if (open_self) 2120 (void)perfuse_node_close(pu, opc, FWRITE, pcr); 2121 2122 if (error == 0) 2123 error = readdir_buffered(ps, opc, dent, readoff, 2124 reslen, pcr, eofflag, cookies, ncookies); 2125 2126 /* 2127 * Schedule queued readdir requests 2128 */ 2129 pnd->pnd_flags &= ~PND_INREADDIR; 2130 (void)dequeue_requests(ps, opc, PCQ_READDIR, DEQUEUE_ALL); 2131 2132 #ifdef PERFUSE_DEBUG 2133 if (perfuse_diagflags & PDF_READDIR) 2134 DPRINTF("%s: READDIR opc = %p exit critical section\n", 2135 __func__, (void *)opc); 2136 #endif 2137 2138 return error; 2139 } 2140 2141 int 2142 perfuse_node_readlink(pu, opc, pcr, linkname, linklen) 2143 struct puffs_usermount *pu; 2144 puffs_cookie_t opc; 2145 const struct puffs_cred *pcr; 2146 char *linkname; 2147 size_t *linklen; 2148 { 2149 struct perfuse_state *ps; 2150 perfuse_msg_t *pm; 2151 int error; 2152 size_t len; 2153 struct fuse_out_header *foh; 2154 2155 /* 2156 * --X required on parent, R-- required on link 2157 */ 2158 if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent, 2159 pcr, PUFFS_VEXEC) || 2160 no_access(opc, pcr, PUFFS_VREAD)) 2161 return EACCES; 2162 2163 ps = puffs_getspecific(pu); 2164 2165 pm = ps->ps_new_msg(pu, opc, FUSE_READLINK, 0, pcr); 2166 2167 if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0) 2168 goto out; 2169 2170 foh = GET_OUTHDR(ps, pm); 2171 len = foh->len - sizeof(*foh) + 1; 2172 if (len > *linklen) 2173 DERRX(EX_PROTOCOL, "path len = %zd too long", len); 2174 2175 *linklen = len; 2176 (void)strlcpy(linkname, _GET_OUTPAYLOAD(ps, pm, char *), len); 2177 out: 2178 ps->ps_destroy_msg(pm); 2179 2180 return error; 2181 } 2182 2183 int 2184 perfuse_node_reclaim(pu, opc) 2185 struct puffs_usermount *pu; 2186 puffs_cookie_t opc; 2187 { 2188 struct perfuse_state *ps; 2189 perfuse_msg_t *pm; 2190 struct perfuse_node_data *pnd; 2191 struct fuse_forget_in *ffi; 2192 struct puffs_node *pn; 2193 struct puffs_node *pn_root; 2194 2195 ps = puffs_getspecific(pu); 2196 pnd = PERFUSE_NODE_DATA(opc); 2197 2198 /* 2199 * Never forget the root. 2200 */ 2201 if (pnd->pnd_ino == FUSE_ROOT_ID) 2202 return 0; 2203 2204 pnd->pnd_flags |= PND_RECLAIMED; 2205 2206 #ifdef PERFUSE_DEBUG 2207 if (perfuse_diagflags & PDF_RECLAIM) 2208 DPRINTF("%s (nodeid %"PRId64") reclaimed\n", 2209 (char *)PNPATH((struct puffs_node *)opc), pnd->pnd_ino); 2210 #endif 2211 2212 pn_root = puffs_getroot(pu); 2213 pn = (struct puffs_node *)opc; 2214 while (pn != pn_root) { 2215 struct puffs_node *parent_pn; 2216 2217 pnd = PERFUSE_NODE_DATA(pn); 2218 2219 #ifdef PERFUSE_DEBUG 2220 if (perfuse_diagflags & PDF_RECLAIM) 2221 DPRINTF("%s (nodeid %"PRId64") is %sreclaimed, " 2222 "has childcount %d %s%s%s, pending ops:%s%s\n", 2223 (char *)PNPATH(pn), pnd->pnd_ino, 2224 pnd->pnd_flags & PND_RECLAIMED ? "" : "not ", 2225 pnd->pnd_childcount, 2226 pnd->pnd_flags & PND_OPEN ? "open " : "not open", 2227 pnd->pnd_flags & PND_RFH ? "r" : "", 2228 pnd->pnd_flags & PND_WFH ? "w" : "", 2229 pnd->pnd_flags & PND_INREADDIR ? " readdir" : "", 2230 pnd->pnd_flags & PND_INWRITE ? " write" : ""); 2231 #endif 2232 2233 if (!(pnd->pnd_flags & PND_RECLAIMED) || 2234 (pnd->pnd_childcount != 0)) 2235 return 0; 2236 2237 /* 2238 * Make sure all operation are finished 2239 * There can be an ongoing write, or queued operations 2240 */ 2241 while (pnd->pnd_flags & PND_INWRITE) { 2242 requeue_request(pu, opc, PCQ_AFTERWRITE); 2243 2244 /* 2245 * reclaim may have been cancelled in the meantime 2246 * if the file as been look'ed up again. 2247 */ 2248 if (!(pnd->pnd_flags & PND_RECLAIMED)) 2249 return 0; 2250 } 2251 2252 #ifdef PERFUSE_DEBUG 2253 if ((pnd->pnd_flags & (PND_INREADDIR|PND_INWRITE)) || 2254 !TAILQ_EMPTY(&pnd->pnd_pcq)) 2255 DERRX(EX_SOFTWARE, "%s: opc = %p: ongoing operations", 2256 __func__, (void *)opc); 2257 #endif 2258 2259 /* 2260 * Close open files 2261 */ 2262 if (pnd->pnd_flags & PND_WFH) 2263 (void)node_close_common(pu, opc, FWRITE); 2264 2265 if (pnd->pnd_flags & PND_RFH) 2266 (void)node_close_common(pu, opc, FREAD); 2267 2268 /* 2269 * And send the FORGET message 2270 */ 2271 pm = ps->ps_new_msg(pu, (puffs_cookie_t)pn, FUSE_FORGET, 2272 sizeof(*ffi), NULL); 2273 ffi = GET_INPAYLOAD(ps, pm, fuse_forget_in); 2274 ffi->nlookup = pnd->pnd_nlookup; 2275 2276 /* 2277 * No reply is expected, pm is freed in XCHG_MSG 2278 */ 2279 (void)XCHG_MSG_NOREPLY(ps, pu, pm, UNSPEC_REPLY_LEN); 2280 2281 parent_pn = pnd->pnd_parent; 2282 2283 perfuse_destroy_pn(pn); 2284 puffs_pn_put(pn); 2285 2286 pn = parent_pn; 2287 } 2288 2289 return 0; 2290 } 2291 2292 /* ARGSUSED0 */ 2293 int 2294 perfuse_node_inactive(pu, opc) 2295 struct puffs_usermount *pu; 2296 puffs_cookie_t opc; 2297 { 2298 return 0; 2299 } 2300 2301 2302 /* ARGSUSED0 */ 2303 int 2304 perfuse_node_print(pu, opc) 2305 struct puffs_usermount *pu; 2306 puffs_cookie_t opc; 2307 { 2308 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__); 2309 return 0; 2310 } 2311 2312 /* ARGSUSED0 */ 2313 int 2314 perfuse_node_pathconf(pu, opc, name, retval) 2315 struct puffs_usermount *pu; 2316 puffs_cookie_t opc; 2317 int name; 2318 int *retval; 2319 { 2320 DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__); 2321 return 0; 2322 } 2323 2324 /* id is unused */ 2325 /* ARGSUSED2 */ 2326 int 2327 perfuse_node_advlock(pu, opc, id, op, fl, flags) 2328 struct puffs_usermount *pu; 2329 puffs_cookie_t opc; 2330 void *id; 2331 int op; 2332 struct flock *fl; 2333 int flags; 2334 { 2335 struct perfuse_state *ps; 2336 int fop; 2337 perfuse_msg_t *pm; 2338 struct fuse_lk_in *fli; 2339 struct fuse_lk_out *flo; 2340 int error; 2341 2342 ps = puffs_getspecific(pu); 2343 2344 if (op == F_GETLK) 2345 fop = FUSE_GETLK; 2346 else 2347 fop = (flags & F_WAIT) ? FUSE_SETLKW : FUSE_SETLK; 2348 2349 pm = ps->ps_new_msg(pu, opc, fop, sizeof(*fli), NULL); 2350 fli = GET_INPAYLOAD(ps, pm, fuse_lk_in); 2351 fli->fh = perfuse_get_fh(opc, FWRITE); 2352 fli->owner = fl->l_pid; 2353 fli->lk.start = fl->l_start; 2354 fli->lk.end = fl->l_start + fl->l_len; 2355 fli->lk.type = fl->l_type; 2356 fli->lk.pid = fl->l_pid; 2357 fli->lk_flags = (flags & F_FLOCK) ? FUSE_LK_FLOCK : 0; 2358 2359 #ifdef PERFUSE_DEBUG 2360 if (perfuse_diagflags & PDF_FH) 2361 DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n", 2362 __func__, (void *)opc, 2363 PERFUSE_NODE_DATA(opc)->pnd_ino, fli->fh); 2364 #endif 2365 2366 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*flo))) != 0) 2367 goto out; 2368 2369 flo = GET_OUTPAYLOAD(ps, pm, fuse_lk_out); 2370 fl->l_start = flo->lk.start; 2371 fl->l_len = flo->lk.end - flo->lk.start; 2372 fl->l_pid = flo->lk.pid; 2373 fl->l_type = flo->lk.type; 2374 fl->l_whence = SEEK_SET; /* libfuse hardcodes it */ 2375 2376 /* 2377 * Save or clear the lock 2378 */ 2379 switch (op) { 2380 case F_SETLK: 2381 PERFUSE_NODE_DATA(opc)->pnd_lock_owner = flo->lk.pid; 2382 break; 2383 case F_UNLCK: 2384 PERFUSE_NODE_DATA(opc)->pnd_lock_owner = 0; 2385 break; 2386 default: 2387 break; 2388 } 2389 2390 out: 2391 ps->ps_destroy_msg(pm); 2392 2393 return error; 2394 } 2395 2396 int 2397 perfuse_node_read(pu, opc, buf, offset, resid, pcr, ioflag) 2398 struct puffs_usermount *pu; 2399 puffs_cookie_t opc; 2400 uint8_t *buf; 2401 off_t offset; 2402 size_t *resid; 2403 const struct puffs_cred *pcr; 2404 int ioflag; 2405 { 2406 struct perfuse_state *ps; 2407 struct perfuse_node_data *pnd; 2408 perfuse_msg_t *pm; 2409 struct fuse_read_in *fri; 2410 struct fuse_out_header *foh; 2411 size_t readen; 2412 size_t requested; 2413 int error; 2414 2415 ps = puffs_getspecific(pu); 2416 pnd = PERFUSE_NODE_DATA(opc); 2417 pm = NULL; 2418 2419 if (puffs_pn_getvap((struct puffs_node *)opc)->va_type == VDIR) 2420 return EBADF; 2421 2422 requested = *resid; 2423 if ((ps->ps_readahead + requested) > ps->ps_max_readahead) { 2424 if (perfuse_diagflags & PDF_REQUEUE) 2425 DPRINTF("readahead = %zd\n", ps->ps_readahead); 2426 requeue_request(pu, opc, PCQ_READ); 2427 } 2428 ps->ps_readahead += requested; 2429 2430 do { 2431 /* 2432 * flags may be set to FUSE_READ_LOCKOWNER 2433 * if lock_owner is provided. 2434 * 2435 * XXX See comment about fri->size in perfuse_node_readdir 2436 * We encounter the same bug here. 2437 */ 2438 pm = ps->ps_new_msg(pu, opc, FUSE_READ, sizeof(*fri), pcr); 2439 fri = GET_INPAYLOAD(ps, pm, fuse_read_in); 2440 fri->fh = perfuse_get_fh(opc, FREAD); 2441 fri->offset = offset; 2442 fri->size = (uint32_t)MIN(*resid, PAGE_SIZE - sizeof(*foh)); 2443 fri->read_flags = 0; /* XXX Unused by libfuse? */ 2444 fri->lock_owner = pnd->pnd_lock_owner; 2445 fri->flags = 0; 2446 fri->flags |= (fri->lock_owner != 0) ? FUSE_READ_LOCKOWNER : 0; 2447 2448 #ifdef PERFUSE_DEBUG 2449 if (perfuse_diagflags & PDF_FH) 2450 DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n", 2451 __func__, (void *)opc, pnd->pnd_ino, fri->fh); 2452 #endif 2453 error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN); 2454 2455 if (error != 0) 2456 goto out; 2457 2458 foh = GET_OUTHDR(ps, pm); 2459 readen = foh->len - sizeof(*foh); 2460 2461 (void)memcpy(buf, _GET_OUTPAYLOAD(ps, pm, char *), readen); 2462 2463 buf += readen; 2464 offset += readen; 2465 *resid -= readen; 2466 2467 ps->ps_destroy_msg(pm); 2468 pm = NULL; 2469 } while ((*resid != 0) && (readen != 0)); 2470 2471 if (ioflag & (IO_SYNC|IO_DSYNC)) 2472 ps->ps_syncreads++; 2473 else 2474 ps->ps_asyncreads++; 2475 2476 out: 2477 if (pm != NULL) 2478 ps->ps_destroy_msg(pm); 2479 2480 ps->ps_readahead -= requested; 2481 2482 (void)dequeue_requests(ps, opc, PCQ_READ, 1); 2483 2484 return error; 2485 } 2486 2487 int 2488 perfuse_node_write(pu, opc, buf, offset, resid, pcr, ioflag) 2489 struct puffs_usermount *pu; 2490 puffs_cookie_t opc; 2491 uint8_t *buf; 2492 off_t offset; 2493 size_t *resid; 2494 const struct puffs_cred *pcr; 2495 int ioflag; 2496 { 2497 struct perfuse_state *ps; 2498 struct perfuse_node_data *pnd; 2499 perfuse_msg_t *pm; 2500 struct fuse_write_in *fwi; 2501 struct fuse_write_out *fwo; 2502 size_t data_len; 2503 size_t payload_len; 2504 size_t written; 2505 size_t requested; 2506 int error; 2507 2508 ps = puffs_getspecific(pu); 2509 pnd = PERFUSE_NODE_DATA(opc); 2510 pm = NULL; 2511 written = 0; 2512 2513 if (puffs_pn_getvap((struct puffs_node *)opc)->va_type == VDIR) 2514 return EBADF; 2515 2516 while (pnd->pnd_flags & PND_INWRITE) 2517 requeue_request(pu, opc, PCQ_WRITE); 2518 pnd->pnd_flags |= PND_INWRITE; 2519 2520 2521 requested = *resid; 2522 if ((ps->ps_write + requested) > ps->ps_max_write) { 2523 if (perfuse_diagflags & PDF_REQUEUE) 2524 DPRINTF("write = %zd\n", ps->ps_write); 2525 requeue_request(pu, opc, PCQ_WRITE); 2526 } 2527 ps->ps_write += requested; 2528 2529 do { 2530 /* 2531 * It seems libfuse does not expects big chunks, so 2532 * send it page per page. The writepage feature is 2533 * probably there to minmize data movement. 2534 * XXX use ps->ps_maxwrite? 2535 */ 2536 data_len = MIN(*resid, PAGE_SIZE); 2537 payload_len = data_len + sizeof(*fwi); 2538 2539 /* 2540 * flags may be set to FUSE_WRITE_CACHE (XXX usage?) 2541 * or FUSE_WRITE_LOCKOWNER, if lock_owner is provided. 2542 * write_flags is set to 1 for writepage. 2543 */ 2544 pm = ps->ps_new_msg(pu, opc, FUSE_WRITE, payload_len, pcr); 2545 fwi = GET_INPAYLOAD(ps, pm, fuse_write_in); 2546 fwi->fh = perfuse_get_fh(opc, FWRITE); 2547 fwi->offset = offset; 2548 fwi->size = (uint32_t)data_len; 2549 fwi->write_flags = (fwi->size % PAGE_SIZE) ? 0 : 1; 2550 fwi->lock_owner = pnd->pnd_lock_owner; 2551 fwi->flags = 0; 2552 fwi->flags |= (fwi->lock_owner != 0) ? FUSE_WRITE_LOCKOWNER : 0; 2553 fwi->flags |= (ioflag & IO_DIRECT) ? 0 : FUSE_WRITE_CACHE; 2554 (void)memcpy((fwi + 1), buf + written, data_len); 2555 2556 #ifdef PERFUSE_DEBUG 2557 if (perfuse_diagflags & PDF_FH) 2558 DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n", 2559 __func__, (void *)opc, pnd->pnd_ino, fwi->fh); 2560 #endif 2561 if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fwo))) != 0) 2562 goto out; 2563 2564 fwo = GET_OUTPAYLOAD(ps, pm, fuse_write_out); 2565 written = fwo->size; 2566 *resid -= written; 2567 offset += written; 2568 buf += written; 2569 2570 ps->ps_destroy_msg(pm); 2571 pm = NULL; 2572 } while (*resid != 0); 2573 2574 /* 2575 * puffs_ops(3) says 2576 * "everything must be written or an error will be generated" 2577 */ 2578 if (*resid != 0) 2579 error = EFBIG; 2580 2581 if (ioflag & (IO_SYNC|IO_DSYNC)) 2582 ps->ps_syncwrites++; 2583 else 2584 ps->ps_asyncwrites++; 2585 2586 /* 2587 * Remember to sync the file 2588 */ 2589 pnd->pnd_flags |= PND_DIRTY; 2590 2591 #ifdef PERFUSE_DEBUG 2592 if (perfuse_diagflags & PDF_SYNC) 2593 DPRINTF("%s: DIRTY opc = %p, file = \"%s\"\n", 2594 __func__, (void*)opc, 2595 (char *)PNPATH((struct puffs_node *)opc)); 2596 #endif 2597 out: 2598 if (pm != NULL) 2599 ps->ps_destroy_msg(pm); 2600 2601 ps->ps_write -= requested; 2602 2603 2604 /* 2605 * If there are no more queued write, we can resume 2606 * an operation awaiting write completion. 2607 */ 2608 pnd->pnd_flags &= ~PND_INWRITE; 2609 if (dequeue_requests(ps, opc, PCQ_WRITE, 1) == 0) 2610 (void)dequeue_requests(ps, opc, PCQ_AFTERWRITE, DEQUEUE_ALL); 2611 2612 return error; 2613 } 2614 2615 /* ARGSUSED0 */ 2616 void 2617 perfuse_cache_write(pu, opc, size, runs) 2618 struct puffs_usermount *pu; 2619 puffs_cookie_t opc; 2620 size_t size; 2621 struct puffs_cacherun *runs; 2622 { 2623 return; 2624 } 2625 2626