1 /* $NetBSD: p2k.c,v 1.30 2009/12/03 14:27:16 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2007, 2008, 2009 Antti Kantee. All Rights Reserved. 5 * 6 * Development of this software was supported by the 7 * Finnish Cultural Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * puffs 2k, i.e. puffs 2 kernel. Converts the puffs protocol to 33 * the kernel vfs protocol and vice versa. 34 * 35 * A word about reference counting: puffs in the kernel is the king of 36 * reference counting. We must maintain a vnode alive and kicking 37 * until the kernel tells us to reclaim it. Therefore we make sure 38 * we never accidentally lose a vnode. Before calling operations which 39 * decrease the refcount we always bump the refcount up to compensate. 40 * Come inactive, if the file system thinks that the vnode should be 41 * put out of its misery, it will set the recycle flag. We use this 42 * to tell the kernel to reclaim the vnode. Only in reclaim do we 43 * really nuke the last reference. 44 */ 45 46 #include <sys/cdefs.h> 47 #include <sys/mount.h> 48 #include <sys/param.h> 49 #include <sys/vnode.h> 50 #include <sys/lock.h> 51 #include <sys/namei.h> 52 #include <sys/dirent.h> 53 #include <sys/hash.h> 54 55 #include <assert.h> 56 #include <errno.h> 57 #include <puffs.h> 58 #include <stdlib.h> 59 #include <stdio.h> 60 61 #include <rump/rump.h> 62 #include <rump/p2k.h> 63 #include <rump/ukfs.h> 64 65 PUFFSOP_PROTOS(p2k) 66 67 LIST_HEAD(p2k_vp_hash, p2k_node); 68 #define NHASHBUCK (1<<16) 69 struct p2k_mount { 70 struct vnode *p2m_rvp; 71 struct puffs_usermount *p2m_pu; 72 struct ukfs *p2m_ukfs; 73 struct p2k_vp_hash p2m_vphash[NHASHBUCK]; 74 int p2m_nvnodes; 75 }; 76 77 struct p2k_node { 78 struct vnode *p2n_vp; 79 struct componentname *p2n_cn; 80 81 /* 82 * Ok, then, uhm, we need .. *drumroll*.. two componentname 83 * storages for rename. This is because the source dir is 84 * unlocked after the first lookup, and someone else might 85 * race in here. However, we know it's not another rename 86 * because of the kernel rename lock. And we need two since 87 * srcdir and targdir might be the same. It's a wonderful world. 88 */ 89 struct componentname *p2n_cn_ren_src, *p2n_cn_ren_targ; 90 91 LIST_ENTRY(p2k_node) p2n_entries; 92 }; 93 94 #define OPC2VP(opc) (((struct p2k_node *)opc)->p2n_vp) 95 96 static int haswizard; 97 static uid_t wizarduid; 98 99 static kauth_cred_t 100 cred_create(const struct puffs_cred *pcr) 101 { 102 gid_t groups[NGROUPS]; 103 uid_t uid; 104 gid_t gid; 105 short ngroups = __arraycount(groups); 106 107 if (haswizard) { 108 uid = wizarduid; 109 } else { 110 if (puffs_cred_getuid(pcr, &uid) == -1) 111 uid = 0; 112 } 113 if (puffs_cred_getgid(pcr, &gid) == -1) 114 gid = 0; 115 puffs_cred_getgroups(pcr, groups, &ngroups); 116 117 /* LINTED: ngroups is ok */ 118 return rump_pub_cred_create(uid, gid, ngroups, groups); 119 } 120 121 static __inline void 122 cred_destroy(kauth_cred_t cred) 123 { 124 125 rump_pub_cred_put(cred); 126 } 127 128 static struct componentname * 129 makecn(const struct puffs_cn *pcn, int myflags) 130 { 131 kauth_cred_t cred; 132 133 cred = cred_create(pcn->pcn_cred); 134 /* LINTED: prehistoric types in first two args */ 135 return rump_pub_makecn(pcn->pcn_nameiop, pcn->pcn_flags | myflags, 136 pcn->pcn_name, pcn->pcn_namelen, cred, rump_pub_lwp_curlwp()); 137 } 138 139 static __inline void 140 freecn(struct componentname *cnp, int flags) 141 { 142 143 rump_pub_freecn(cnp, flags | RUMPCN_FREECRED); 144 } 145 146 static void 147 makelwp(struct puffs_usermount *pu) 148 { 149 pid_t pid; 150 lwpid_t lid; 151 152 puffs_cc_getcaller(puffs_cc_getcc(pu), &pid, &lid); 153 rump_pub_lwp_alloc_and_switch(pid, lid); 154 } 155 156 /*ARGSUSED*/ 157 static void 158 clearlwp(struct puffs_usermount *pu) 159 { 160 161 rump_pub_lwp_release(rump_pub_lwp_curlwp()); 162 } 163 164 static __inline struct p2k_vp_hash * 165 gethash(struct p2k_mount *p2m, struct vnode *vp) 166 { 167 uint32_t hash; 168 169 hash = hash32_buf(&vp, sizeof(vp), HASH32_BUF_INIT); 170 return &p2m->p2m_vphash[hash % NHASHBUCK]; 171 } 172 173 /* 174 * Find node based on hash of vnode pointer. If vnode is found, 175 * releases one reference to vnode based on the fact that we just 176 * performed a lookup for it. 177 * 178 * If the optinal p2n_storage parameter is passed, it is used instead 179 * of allocating more memory. This allows for easier error recovery. 180 */ 181 static struct p2k_node * 182 getp2n(struct p2k_mount *p2m, struct vnode *vp, bool initial, 183 struct p2k_node *p2n_storage) 184 { 185 struct p2k_vp_hash *hl; 186 struct p2k_node *p2n = NULL; 187 188 /* p2n_storage => initial */ 189 assert(!p2n_storage || initial); 190 191 hl = gethash(p2m, vp); 192 if (!initial) 193 LIST_FOREACH(p2n, hl, p2n_entries) 194 if (p2n->p2n_vp == vp) 195 break; 196 197 hl = gethash(p2m, vp); 198 if (p2n) { 199 rump_pub_vp_rele(vp); 200 } else { 201 if (p2n_storage) 202 p2n = p2n_storage; 203 else 204 p2n = malloc(sizeof(*p2n)); 205 if (!p2n) { 206 rump_pub_vp_rele(vp); 207 return NULL; 208 } 209 memset(p2n, 0, sizeof(*p2n)); 210 LIST_INSERT_HEAD(hl, p2n, p2n_entries); 211 p2n->p2n_vp = vp; 212 } 213 return p2n; 214 } 215 216 static void 217 freep2n(struct p2k_node *p2n) 218 { 219 220 assert(p2n->p2n_vp == NULL); 221 assert(p2n->p2n_cn == NULL); 222 LIST_REMOVE(p2n, p2n_entries); 223 free(p2n); 224 } 225 226 /*ARGSUSED*/ 227 static void 228 p2k_errcatcher(struct puffs_usermount *pu, uint8_t type, int error, 229 const char *str, puffs_cookie_t cook) 230 { 231 232 fprintf(stderr, "type %d, error %d, cookie %p (%s)\n", 233 type, error, cook, str); 234 235 /* 236 * Trap all EINVAL responses to lookup. It most likely means 237 * that we supplied VNON/VBAD as the type. The real kernel 238 * doesn't panic from this either, but just handles it. 239 */ 240 if (type != PUFFS_VN_LOOKUP && error == EINVAL) 241 abort(); 242 } 243 244 /* just to avoid annoying loop when singlestepping */ 245 static struct p2k_mount * 246 allocp2m(void) 247 { 248 struct p2k_mount *p2m; 249 int i; 250 251 p2m = malloc(sizeof(*p2m)); 252 if (p2m == NULL) 253 return NULL; 254 memset(p2m, 0, sizeof(*p2m)); 255 256 for (i = 0; i < NHASHBUCK; i++) 257 LIST_INIT(&p2m->p2m_vphash[i]); 258 259 return p2m; 260 } 261 262 struct p2k_mount * 263 p2k_init(uint32_t puffs_flags) 264 { 265 struct puffs_ops *pops; 266 struct p2k_mount *p2m; 267 char *envbuf; 268 bool dodaemon; 269 270 PUFFSOP_INIT(pops); 271 272 PUFFSOP_SET(pops, p2k, fs, statvfs); 273 PUFFSOP_SET(pops, p2k, fs, unmount); 274 PUFFSOP_SET(pops, p2k, fs, sync); 275 PUFFSOP_SET(pops, p2k, fs, fhtonode); 276 PUFFSOP_SET(pops, p2k, fs, nodetofh); 277 278 PUFFSOP_SET(pops, p2k, node, lookup); 279 PUFFSOP_SET(pops, p2k, node, create); 280 PUFFSOP_SET(pops, p2k, node, mknod); 281 PUFFSOP_SET(pops, p2k, node, open); 282 PUFFSOP_SET(pops, p2k, node, close); 283 PUFFSOP_SET(pops, p2k, node, access); 284 PUFFSOP_SET(pops, p2k, node, getattr); 285 PUFFSOP_SET(pops, p2k, node, setattr); 286 #if 0 287 PUFFSOP_SET(pops, p2k, node, poll); 288 #endif 289 PUFFSOP_SET(pops, p2k, node, mmap); 290 PUFFSOP_SET(pops, p2k, node, fsync); 291 PUFFSOP_SET(pops, p2k, node, seek); 292 PUFFSOP_SET(pops, p2k, node, remove); 293 PUFFSOP_SET(pops, p2k, node, link); 294 PUFFSOP_SET(pops, p2k, node, rename); 295 PUFFSOP_SET(pops, p2k, node, mkdir); 296 PUFFSOP_SET(pops, p2k, node, rmdir); 297 PUFFSOP_SET(pops, p2k, node, symlink); 298 PUFFSOP_SET(pops, p2k, node, readdir); 299 PUFFSOP_SET(pops, p2k, node, readlink); 300 PUFFSOP_SET(pops, p2k, node, read); 301 PUFFSOP_SET(pops, p2k, node, write); 302 303 PUFFSOP_SET(pops, p2k, node, inactive); 304 PUFFSOP_SET(pops, p2k, node, reclaim); 305 PUFFSOP_SET(pops, p2k, node, abortop); 306 307 dodaemon = true; 308 if (getenv("P2K_DEBUG") != NULL) { 309 puffs_flags |= PUFFS_FLAG_OPDUMP; 310 dodaemon = false; 311 } 312 if (getenv("P2K_NODETACH") != NULL) { 313 dodaemon = false; 314 } 315 if (getenv("P2K_NOCACHE_PAGE") != NULL) { 316 puffs_flags |= PUFFS_KFLAG_NOCACHE_PAGE; 317 } 318 if (getenv("P2K_NOCACHE_NAME") != NULL) { 319 puffs_flags |= PUFFS_KFLAG_NOCACHE_NAME; 320 } 321 if (getenv("P2K_NOCACHE") != NULL) { 322 puffs_flags |= PUFFS_KFLAG_NOCACHE; 323 } 324 if ((envbuf = getenv("P2K_WIZARDUID")) != NULL) { 325 /* default to 0 in error cases */ 326 wizarduid = atoi(envbuf); 327 haswizard = 1; 328 printf("P2K WIZARD MODE: using uid %d\n", wizarduid); 329 } 330 331 p2m = allocp2m(); 332 if (p2m == NULL) 333 return NULL; 334 p2m->p2m_pu = puffs_init(pops, PUFFS_DEFER, PUFFS_DEFER, 335 PUFFS_DEFER, puffs_flags); 336 if (p2m->p2m_pu == NULL) { 337 int sverrno = errno; 338 free(p2m); 339 errno = sverrno; 340 return NULL; 341 } 342 343 if (dodaemon) { 344 if (puffs_daemon(p2m->p2m_pu, 1, 1) == -1) { 345 int sverrno = errno; 346 p2k_cancel(p2m, sverrno); 347 errno = sverrno; 348 p2m = NULL; 349 } 350 } 351 if (p2m) 352 rump_init(); 353 354 return p2m; 355 } 356 357 void 358 p2k_cancel(struct p2k_mount *p2m, int error) 359 { 360 361 puffs_cancel(p2m->p2m_pu, error); 362 free(p2m); 363 } 364 365 static int 366 setupfs(struct p2k_mount *p2m, const char *vfsname, const char *devpath, 367 struct ukfs_part *part, const char *mountpath, int mntflags, 368 void *arg, size_t alen) 369 { 370 char partpath[UKFS_DEVICE_MAXPATHLEN]; 371 char partbuf[UKFS_DEVICE_MAXSTR]; 372 char typebuf[PUFFS_TYPELEN]; 373 struct puffs_usermount *pu = p2m->p2m_pu; 374 struct p2k_node *p2n_root; 375 struct ukfs *ukfs = NULL; 376 extern int puffs_fakecc; 377 int rv = -1, sverrno; 378 379 strcpy(typebuf, "p2k|"); 380 if (strcmp(vfsname, "puffs") == 0) { /* XXX */ 381 struct puffs_kargs *args = arg; 382 strlcat(typebuf, args->pa_typename, sizeof(typebuf)); 383 } else { 384 strlcat(typebuf, vfsname, sizeof(typebuf)); 385 } 386 387 strlcpy(partpath, devpath, sizeof(partpath)); 388 if (ukfs_part_tostring(part, partbuf, sizeof(partbuf))) { 389 strlcat(partpath, partbuf, sizeof(partpath)); 390 } 391 puffs_setmntinfo(pu, partpath, typebuf); 392 393 if (ukfs_init() == -1) 394 goto out; 395 if (part != ukfs_part_na) 396 ukfs = ukfs_mount_disk(vfsname, devpath, part, 397 mountpath, mntflags, arg, alen); 398 else 399 ukfs = ukfs_mount(vfsname, devpath, mountpath, mntflags, 400 arg, alen); 401 if (ukfs == NULL) 402 goto out; 403 ukfs_setspecific(ukfs, p2m); 404 p2m->p2m_ukfs = ukfs; 405 p2m->p2m_pu = pu; 406 407 p2m->p2m_rvp = ukfs_getrvp(ukfs); 408 p2n_root = getp2n(p2m, p2m->p2m_rvp, true, NULL); 409 puffs_setfhsize(pu, 0, PUFFS_FHFLAG_PASSTHROUGH); 410 puffs_setstacksize(pu, PUFFS_STACKSIZE_MIN); 411 puffs_fakecc = 1; 412 puffs_set_prepost(pu, makelwp, clearlwp); 413 puffs_set_errnotify(pu, p2k_errcatcher); 414 415 puffs_setspecific(pu, ukfs); 416 rv = puffs_mount(pu, mountpath, mntflags, p2n_root); 417 418 out: 419 if (rv == -1) { 420 sverrno = errno; 421 puffs_cancel(pu, sverrno); 422 if (ukfs) 423 ukfs_release(p2m->p2m_ukfs, UKFS_RELFLAG_FORCE); 424 free(p2m); 425 errno = sverrno; 426 } 427 428 return rv; 429 } 430 431 int 432 p2k_mainloop(struct p2k_mount *p2m) 433 { 434 int rv, sverrno; 435 436 rv = puffs_mainloop(p2m->p2m_pu); 437 sverrno = errno; 438 puffs_exit(p2m->p2m_pu, 1); 439 if (p2m->p2m_ukfs) 440 ukfs_release(p2m->p2m_ukfs, UKFS_RELFLAG_FORCE); 441 free(p2m); 442 443 if (rv == -1) 444 errno = sverrno; 445 return rv; 446 } 447 448 int 449 p2k_run_fs(const char *vfsname, const char *devpath, const char *mountpath, 450 int mntflags, void *arg, size_t alen, uint32_t puffs_flags) 451 { 452 struct p2k_mount *p2m; 453 int rv; 454 455 p2m = p2k_init(puffs_flags); 456 if (p2m == NULL) 457 return -1; 458 rv = setupfs(p2m, vfsname, devpath, ukfs_part_na, mountpath, 459 mntflags, arg, alen); 460 if (rv == -1) 461 return rv; 462 return p2k_mainloop(p2m); 463 } 464 465 int 466 p2k_run_diskfs(const char *vfsname, const char *devpath, struct ukfs_part *part, 467 const char *mountpath, int mntflags, void *arg, size_t alen, 468 uint32_t puffs_flags) 469 { 470 struct p2k_mount *p2m; 471 int rv; 472 473 p2m = p2k_init(puffs_flags); 474 if (p2m == NULL) 475 return -1; 476 rv = setupfs(p2m, vfsname, devpath, part, mountpath, mntflags, 477 arg, alen); 478 if (rv == -1) 479 return rv; 480 return p2k_mainloop(p2m); 481 } 482 483 int 484 p2k_setup_fs(struct p2k_mount *p2m, const char *vfsname, const char *devpath, 485 const char *mountpath, int mntflags, void *arg, size_t alen) 486 { 487 488 return setupfs(p2m, vfsname, devpath, ukfs_part_na, mountpath, 489 mntflags, arg, alen); 490 } 491 492 int 493 p2k_setup_diskfs(struct p2k_mount *p2m, const char *vfsname, 494 const char *devpath, struct ukfs_part *part, const char *mountpath, 495 int mntflags, void *arg, size_t alen) 496 { 497 498 return setupfs(p2m, vfsname, devpath, part, mountpath, mntflags, 499 arg, alen); 500 } 501 502 int 503 p2k_fs_statvfs(struct puffs_usermount *pu, struct statvfs *sbp) 504 { 505 struct mount *mp = ukfs_getmp(puffs_getspecific(pu)); 506 507 return rump_pub_vfs_statvfs(mp, sbp); 508 } 509 510 /*ARGSUSED*/ 511 int 512 p2k_fs_unmount(struct puffs_usermount *pu, int flags) 513 { 514 struct ukfs *fs = puffs_getspecific(pu); 515 struct p2k_mount *p2m = ukfs_getspecific(fs); 516 int error = 0; 517 518 rump_pub_lwp_release(rump_pub_lwp_curlwp()); /* ukfs & curlwp tricks */ 519 520 rump_pub_vp_rele(p2m->p2m_rvp); 521 if (ukfs_release(fs, 0) != 0) { 522 ukfs_release(fs, UKFS_RELFLAG_FORCE); 523 error = 0; 524 } 525 p2m->p2m_ukfs = NULL; 526 527 rump_pub_lwp_alloc_and_switch(0, 0); 528 return error; 529 } 530 531 int 532 p2k_fs_sync(struct puffs_usermount *pu, int waitfor, 533 const struct puffs_cred *pcr) 534 { 535 struct mount *mp = ukfs_getmp(puffs_getspecific(pu)); 536 kauth_cred_t cred; 537 int rv; 538 539 cred = cred_create(pcr); 540 rv = rump_pub_vfs_sync(mp, waitfor, (kauth_cred_t)cred); 541 cred_destroy(cred); 542 543 return rv; 544 } 545 546 /*ARGSUSED*/ 547 int 548 p2k_fs_fhtonode(struct puffs_usermount *pu, void *fid, size_t fidsize, 549 struct puffs_newinfo *pni) 550 { 551 struct mount *mp = ukfs_getmp(puffs_getspecific(pu)); 552 struct p2k_mount *p2m = ukfs_getspecific(puffs_getspecific(pu)); 553 struct p2k_node *p2n; 554 struct vnode *vp; 555 enum vtype vtype; 556 voff_t vsize; 557 uint64_t rdev; /* XXX: allows running this on NetBSD 5.0 */ 558 int rv; 559 560 rv = rump_pub_vfs_fhtovp(mp, fid, &vp); 561 if (rv) 562 return rv; 563 RUMP_VOP_UNLOCK(vp, 0); 564 565 p2n = getp2n(p2m, vp, false, NULL); 566 if (p2n == NULL) 567 return ENOMEM; 568 569 puffs_newinfo_setcookie(pni, p2n); 570 rump_pub_getvninfo(vp, &vtype, &vsize, (void *)&rdev); 571 puffs_newinfo_setvtype(pni, vtype); 572 puffs_newinfo_setsize(pni, vsize); 573 /* LINTED: yea, it'll lose accuracy, but that's life */ 574 puffs_newinfo_setrdev(pni, rdev); 575 576 return 0; 577 } 578 579 /*ARGSUSED*/ 580 int 581 p2k_fs_nodetofh(struct puffs_usermount *pu, puffs_cookie_t cookie, void *fid, 582 size_t *fidsize) 583 { 584 struct vnode *vp = cookie; 585 586 return rump_pub_vfs_vptofh(vp, fid, fidsize); 587 } 588 589 /*ARGSUSED*/ 590 int 591 p2k_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc, 592 struct puffs_newinfo *pni, const struct puffs_cn *pcn) 593 { 594 struct p2k_mount *p2m = ukfs_getspecific(puffs_getspecific(pu)); 595 struct p2k_node *p2n_dir = opc, *p2n; 596 struct componentname *cn; 597 struct vnode *dvp = p2n_dir->p2n_vp, *vp; 598 enum vtype vtype; 599 voff_t vsize; 600 uint64_t rdev; /* XXX: uint64_t because of stack overwrite in compat */ 601 int rv; 602 603 cn = makecn(pcn, 0); 604 RUMP_VOP_LOCK(dvp, LK_EXCLUSIVE); 605 rv = RUMP_VOP_LOOKUP(dvp, &vp, cn); 606 RUMP_VOP_UNLOCK(dvp, 0); 607 if (rump_pub_checksavecn(cn)) { 608 /* 609 * XXX: detect RENAME by SAVESTART, both src and targ lookups 610 * 611 * XXX part deux: rename syscall actually does two lookups 612 * for the source, the second without SAVESTART. So detect 613 * this also and compensate. 614 */ 615 if (pcn->pcn_flags & NAMEI_SAVESTART) { 616 if (pcn->pcn_nameiop == NAMEI_DELETE) { 617 assert(p2n_dir->p2n_cn_ren_src == NULL); 618 p2n_dir->p2n_cn_ren_src = cn; 619 } else { 620 assert(pcn->pcn_nameiop == NAMEI_RENAME); 621 assert(p2n_dir->p2n_cn_ren_targ == NULL); 622 p2n_dir->p2n_cn_ren_targ = cn; 623 } 624 } else { 625 if (pcn->pcn_nameiop == NAMEI_DELETE 626 && p2n_dir->p2n_cn_ren_src) { 627 freecn(cn, RUMPCN_FORCEFREE); 628 cn = NULL; 629 } else { 630 assert(p2n_dir->p2n_cn == NULL); 631 p2n_dir->p2n_cn = cn; 632 } 633 } 634 } else { 635 freecn(cn, 0); 636 cn = NULL; 637 } 638 if (rv) { 639 if (rv == EJUSTRETURN) { 640 rv = ENOENT; 641 } 642 return rv; 643 } 644 RUMP_VOP_UNLOCK(vp, 0); 645 646 p2n = getp2n(p2m, vp, false, NULL); 647 if (p2n == NULL) { 648 if (pcn->pcn_flags & NAMEI_SAVESTART) { 649 if (pcn->pcn_nameiop == NAMEI_DELETE) { 650 p2n_dir->p2n_cn_ren_src = NULL; 651 } else { 652 p2n_dir->p2n_cn_ren_targ = NULL; 653 } 654 } else { 655 p2n_dir->p2n_cn = NULL; 656 } 657 /* XXX: what in the world should happen with SAVESTART? */ 658 RUMP_VOP_ABORTOP(dvp, cn); 659 return ENOMEM; 660 } 661 662 puffs_newinfo_setcookie(pni, p2n); 663 rump_pub_getvninfo(vp, &vtype, &vsize, (void *)&rdev); 664 puffs_newinfo_setvtype(pni, vtype); 665 puffs_newinfo_setsize(pni, vsize); 666 /* LINTED: yea, it'll lose accuracy, but that's life */ 667 puffs_newinfo_setrdev(pni, rdev); 668 669 return 0; 670 } 671 672 #define VERS_TIMECHANGE 599000700 673 static int 674 needcompat(void) 675 { 676 677 /*LINTED*/ 678 return __NetBSD_Version__ < VERS_TIMECHANGE 679 && rump_pub_getversion() >= VERS_TIMECHANGE; 680 } 681 682 #define DOCOMPAT(va, va_compat) \ 683 do { \ 684 if (needcompat()) { \ 685 va_compat = rump_pub_vattr_init(); \ 686 rump_pub_vattr50_to_vattr(va, va_compat); \ 687 } else { \ 688 va_compat = __UNCONST(va); \ 689 } \ 690 } while (/*CONSTCOND*/0) 691 692 #define UNDOCOMPAT(va_compat) \ 693 do { \ 694 if (needcompat()) \ 695 rump_pub_vattr_free(va_compat); \ 696 } while (/*CONSTCOND*/0) 697 698 static int 699 do_makenode(struct puffs_usermount *pu, struct p2k_node *p2n_dir, 700 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 701 const struct vattr *vap, char *link_target, 702 int (*makefn)(struct vnode *, struct vnode **, struct componentname *, 703 struct vattr *), 704 int (*symfn)(struct vnode *, struct vnode **, struct componentname *, 705 struct vattr *, char *)) 706 { 707 struct p2k_mount *p2m = ukfs_getspecific(puffs_getspecific(pu)); 708 struct vnode *dvp = p2n_dir->p2n_vp; 709 struct p2k_node *p2n; 710 struct componentname *cn; 711 struct vattr *va_x; 712 struct vnode *vp; 713 int rv; 714 715 p2n = malloc(sizeof(*p2n)); 716 if (p2n == NULL) 717 return ENOMEM; 718 DOCOMPAT(vap, va_x); 719 720 if (p2n_dir->p2n_cn) { 721 cn = p2n_dir->p2n_cn; 722 p2n_dir->p2n_cn = NULL; 723 } else { 724 cn = makecn(pcn, RUMP_NAMEI_HASBUF); 725 } 726 727 RUMP_VOP_LOCK(dvp, LK_EXCLUSIVE); 728 rump_pub_vp_incref(dvp); 729 if (makefn) { 730 rv = makefn(dvp, &vp, cn, va_x); 731 } else { 732 rv = symfn(dvp, &vp, cn, va_x, link_target); 733 } 734 assert(RUMP_VOP_ISLOCKED(dvp) == 0); 735 freecn(cn, 0); 736 737 if (rv == 0) { 738 RUMP_VOP_UNLOCK(vp, 0); 739 p2n = getp2n(p2m, vp, true, p2n); 740 puffs_newinfo_setcookie(pni, p2n); 741 } else { 742 free(p2n); 743 } 744 745 UNDOCOMPAT(va_x); 746 747 return rv; 748 749 } 750 751 /*ARGSUSED*/ 752 int 753 p2k_node_create(struct puffs_usermount *pu, puffs_cookie_t opc, 754 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 755 const struct vattr *vap) 756 { 757 758 return do_makenode(pu, opc, pni, pcn, vap, NULL, RUMP_VOP_CREATE, NULL); 759 } 760 761 /*ARGSUSED*/ 762 int 763 p2k_node_mknod(struct puffs_usermount *pu, puffs_cookie_t opc, 764 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 765 const struct vattr *vap) 766 { 767 768 return do_makenode(pu, opc, pni, pcn, vap, NULL, RUMP_VOP_MKNOD, NULL); 769 } 770 771 /*ARGSUSED*/ 772 int 773 p2k_node_open(struct puffs_usermount *pu, puffs_cookie_t opc, int mode, 774 const struct puffs_cred *pcr) 775 { 776 struct vnode *vp = OPC2VP(opc); 777 kauth_cred_t cred; 778 int rv; 779 780 cred = cred_create(pcr); 781 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 782 rv = RUMP_VOP_OPEN(vp, mode, cred); 783 RUMP_VOP_UNLOCK(vp, 0); 784 cred_destroy(cred); 785 786 return rv; 787 } 788 789 /*ARGSUSED*/ 790 int 791 p2k_node_close(struct puffs_usermount *pu, puffs_cookie_t opc, int flags, 792 const struct puffs_cred *pcr) 793 { 794 struct vnode *vp = OPC2VP(opc); 795 kauth_cred_t cred; 796 797 cred = cred_create(pcr); 798 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 799 RUMP_VOP_CLOSE(vp, flags, cred); 800 RUMP_VOP_UNLOCK(vp, 0); 801 cred_destroy(cred); 802 803 return 0; 804 } 805 806 /*ARGSUSED*/ 807 int 808 p2k_node_access(struct puffs_usermount *pu, puffs_cookie_t opc, int mode, 809 const struct puffs_cred *pcr) 810 { 811 struct vnode *vp = OPC2VP(opc); 812 kauth_cred_t cred; 813 int rv; 814 815 cred = cred_create(pcr); 816 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 817 rv = RUMP_VOP_ACCESS(vp, mode, cred); 818 RUMP_VOP_UNLOCK(vp, 0); 819 cred_destroy(cred); 820 821 return rv; 822 } 823 824 /*ARGSUSED*/ 825 int 826 p2k_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc, 827 struct vattr *vap, const struct puffs_cred *pcr) 828 { 829 struct vnode *vp = OPC2VP(opc); 830 kauth_cred_t cred; 831 struct vattr *va_x; 832 int rv; 833 834 /* "deadfs" */ 835 if (!vp) 836 return 0; 837 838 if (needcompat()) { 839 va_x = rump_pub_vattr_init(); 840 } else { 841 va_x = vap; 842 } 843 844 cred = cred_create(pcr); 845 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 846 rv = RUMP_VOP_GETATTR(vp, va_x, cred); 847 RUMP_VOP_UNLOCK(vp, 0); 848 cred_destroy(cred); 849 850 if (needcompat()) { 851 rump_pub_vattr_to_vattr50(va_x, vap); 852 rump_pub_vattr_free(va_x); 853 } 854 855 return rv; 856 } 857 858 /*ARGSUSED*/ 859 int 860 p2k_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc, 861 const struct vattr *vap, const struct puffs_cred *pcr) 862 { 863 struct vnode *vp = OPC2VP(opc); 864 kauth_cred_t cred; 865 struct vattr *va_x; 866 int rv; 867 868 /* "deadfs" */ 869 if (!vp) 870 return 0; 871 872 DOCOMPAT(vap, va_x); 873 874 cred = cred_create(pcr); 875 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 876 rv = RUMP_VOP_SETATTR(vp, va_x, cred); 877 RUMP_VOP_UNLOCK(vp, 0); 878 cred_destroy(cred); 879 880 UNDOCOMPAT(va_x); 881 882 return rv; 883 } 884 885 /*ARGSUSED*/ 886 int 887 p2k_node_fsync(struct puffs_usermount *pu, puffs_cookie_t opc, 888 const struct puffs_cred *pcr, int flags, off_t offlo, off_t offhi) 889 { 890 struct vnode *vp = OPC2VP(opc); 891 kauth_cred_t cred; 892 int rv; 893 894 /* "deadfs" */ 895 if (!vp) 896 return 0; 897 898 cred = cred_create(pcr); 899 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 900 rv = RUMP_VOP_FSYNC(vp, cred, flags, offlo, offhi); 901 RUMP_VOP_UNLOCK(vp, 0); 902 cred_destroy(cred); 903 904 return rv; 905 } 906 907 /*ARGSUSED*/ 908 int 909 p2k_node_mmap(struct puffs_usermount *pu, puffs_cookie_t opc, vm_prot_t flags, 910 const struct puffs_cred *pcr) 911 { 912 kauth_cred_t cred; 913 int rv; 914 915 cred = cred_create(pcr); 916 rv = RUMP_VOP_MMAP(OPC2VP(opc), flags, cred); 917 cred_destroy(cred); 918 919 return rv; 920 } 921 922 /*ARGSUSED*/ 923 int 924 p2k_node_seek(struct puffs_usermount *pu, puffs_cookie_t opc, 925 off_t oldoff, off_t newoff, const struct puffs_cred *pcr) 926 { 927 struct vnode *vp = OPC2VP(opc); 928 kauth_cred_t cred; 929 int rv; 930 931 cred = cred_create(pcr); 932 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 933 rv = RUMP_VOP_SEEK(vp, oldoff, newoff, cred); 934 RUMP_VOP_UNLOCK(vp, 0); 935 cred_destroy(cred); 936 937 return rv; 938 } 939 940 /*ARGSUSED*/ 941 int 942 p2k_node_abortop(struct puffs_usermount *pu, puffs_cookie_t opc, 943 const struct puffs_cn *pcn) 944 { 945 struct p2k_node *p2n_dir = opc; 946 struct componentname *cnp; 947 948 if ((cnp = p2n_dir->p2n_cn) != NULL) { 949 freecn(cnp, 0); 950 p2n_dir->p2n_cn = NULL; 951 } 952 if ((cnp = p2n_dir->p2n_cn_ren_src) != NULL) { 953 freecn(cnp, RUMPCN_FORCEFREE); 954 p2n_dir->p2n_cn_ren_src = NULL; 955 } 956 if ((cnp = p2n_dir->p2n_cn_ren_targ) != NULL) { 957 freecn(cnp, RUMPCN_FORCEFREE); 958 p2n_dir->p2n_cn_ren_targ = NULL; 959 } 960 961 return 0; 962 } 963 964 static int 965 do_nukenode(struct p2k_node *p2n_dir, struct p2k_node *p2n, 966 const struct puffs_cn *pcn, 967 int (*nukefn)(struct vnode *, struct vnode *, struct componentname *)) 968 { 969 struct vnode *dvp = p2n_dir->p2n_vp, *vp = p2n->p2n_vp; 970 struct componentname *cn; 971 int rv; 972 973 if (p2n_dir->p2n_cn) { 974 cn = p2n_dir->p2n_cn; 975 p2n_dir->p2n_cn = NULL; 976 } else { 977 cn = makecn(pcn, RUMP_NAMEI_HASBUF); 978 } 979 980 RUMP_VOP_LOCK(dvp, LK_EXCLUSIVE); 981 rump_pub_vp_incref(dvp); 982 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 983 rump_pub_vp_incref(vp); 984 rv = nukefn(dvp, vp, cn); 985 assert(RUMP_VOP_ISLOCKED(dvp) == 0); 986 assert(RUMP_VOP_ISLOCKED(vp) == 0); 987 freecn(cn, 0); 988 989 return rv; 990 991 } 992 993 /*ARGSUSED*/ 994 int 995 p2k_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc, 996 puffs_cookie_t targ, const struct puffs_cn *pcn) 997 { 998 999 return do_nukenode(opc, targ, pcn, RUMP_VOP_REMOVE); 1000 } 1001 1002 /*ARGSUSED*/ 1003 int 1004 p2k_node_link(struct puffs_usermount *pu, puffs_cookie_t opc, 1005 puffs_cookie_t targ, const struct puffs_cn *pcn) 1006 { 1007 struct vnode *dvp = OPC2VP(opc); 1008 struct p2k_node *p2n_dir = opc; 1009 struct componentname *cn; 1010 int rv; 1011 1012 if (p2n_dir->p2n_cn) { 1013 cn = p2n_dir->p2n_cn; 1014 p2n_dir->p2n_cn = NULL; 1015 } else { 1016 cn = makecn(pcn, RUMP_NAMEI_HASBUF); 1017 } 1018 1019 RUMP_VOP_LOCK(dvp, LK_EXCLUSIVE); 1020 rump_pub_vp_incref(dvp); 1021 rv = RUMP_VOP_LINK(dvp, OPC2VP(targ), cn); 1022 freecn(cn, 0); 1023 1024 return rv; 1025 } 1026 1027 /*ARGSUSED*/ 1028 int 1029 p2k_node_rename(struct puffs_usermount *pu, 1030 puffs_cookie_t src_dir, puffs_cookie_t src, 1031 const struct puffs_cn *pcn_src, 1032 puffs_cookie_t targ_dir, puffs_cookie_t targ, 1033 const struct puffs_cn *pcn_targ) 1034 { 1035 struct p2k_node *p2n_srcdir = src_dir, *p2n_targdir = targ_dir; 1036 struct vnode *dvp, *vp, *tdvp, *tvp = NULL; 1037 struct componentname *cn_src, *cn_targ; 1038 int rv; 1039 1040 if (p2n_srcdir->p2n_cn_ren_src) { 1041 cn_src = p2n_srcdir->p2n_cn_ren_src; 1042 p2n_srcdir->p2n_cn_ren_src = NULL; 1043 } else { 1044 cn_src = makecn(pcn_src, RUMP_NAMEI_HASBUF); 1045 } 1046 1047 if (p2n_targdir->p2n_cn_ren_targ) { 1048 cn_targ = p2n_targdir->p2n_cn_ren_targ; 1049 p2n_targdir->p2n_cn_ren_targ = NULL; 1050 } else { 1051 cn_targ = makecn(pcn_targ, RUMP_NAMEI_HASBUF); 1052 } 1053 1054 dvp = OPC2VP(src_dir); 1055 vp = OPC2VP(src); 1056 tdvp = OPC2VP(targ_dir); 1057 if (targ) { 1058 tvp = OPC2VP(targ); 1059 } 1060 1061 rump_pub_vp_incref(dvp); 1062 rump_pub_vp_incref(vp); 1063 RUMP_VOP_LOCK(tdvp, LK_EXCLUSIVE); 1064 rump_pub_vp_incref(tdvp); 1065 if (tvp) { 1066 RUMP_VOP_LOCK(tvp, LK_EXCLUSIVE); 1067 rump_pub_vp_incref(tvp); 1068 } 1069 rv = RUMP_VOP_RENAME(dvp, vp, cn_src, tdvp, tvp, cn_targ); 1070 assert(RUMP_VOP_ISLOCKED(tdvp) == 0); 1071 if (tvp) { 1072 assert(RUMP_VOP_ISLOCKED(tvp) == 0); 1073 } 1074 freecn(cn_src, RUMPCN_FORCEFREE); 1075 freecn(cn_targ, RUMPCN_FORCEFREE); 1076 1077 return rv; 1078 } 1079 1080 /*ARGSUSED*/ 1081 int 1082 p2k_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc, 1083 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 1084 const struct vattr *vap) 1085 { 1086 1087 return do_makenode(pu, opc, pni, pcn, vap, NULL, RUMP_VOP_MKDIR, NULL); 1088 } 1089 1090 /*ARGSUSED*/ 1091 int 1092 p2k_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc, 1093 puffs_cookie_t targ, const struct puffs_cn *pcn) 1094 { 1095 1096 return do_nukenode(opc, targ, pcn, RUMP_VOP_RMDIR); 1097 } 1098 1099 /*ARGSUSED*/ 1100 int 1101 p2k_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc, 1102 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 1103 const struct vattr *vap, const char *link_target) 1104 { 1105 1106 return do_makenode(pu, opc, pni, pcn, vap, 1107 __UNCONST(link_target), NULL, RUMP_VOP_SYMLINK); 1108 } 1109 1110 /*ARGSUSED*/ 1111 int 1112 p2k_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc, 1113 struct dirent *dent, off_t *readoff, size_t *reslen, 1114 const struct puffs_cred *pcr, int *eofflag, 1115 off_t *cookies, size_t *ncookies) 1116 { 1117 struct vnode *vp = OPC2VP(opc); 1118 kauth_cred_t cred; 1119 struct uio *uio; 1120 off_t *vop_cookies; 1121 int vop_ncookies; 1122 int rv; 1123 1124 cred = cred_create(pcr); 1125 uio = rump_pub_uio_setup(dent, *reslen, *readoff, RUMPUIO_READ); 1126 RUMP_VOP_LOCK(vp, LK_SHARED); 1127 if (cookies) { 1128 rv = RUMP_VOP_READDIR(vp, uio, cred, eofflag, 1129 &vop_cookies, &vop_ncookies); 1130 memcpy(cookies, vop_cookies, vop_ncookies * sizeof(*cookies)); 1131 *ncookies = vop_ncookies; 1132 free(vop_cookies); 1133 } else { 1134 rv = RUMP_VOP_READDIR(vp, uio, cred, eofflag, NULL, NULL); 1135 } 1136 RUMP_VOP_UNLOCK(vp, 0); 1137 if (rv == 0) { 1138 *reslen = rump_pub_uio_getresid(uio); 1139 *readoff = rump_pub_uio_getoff(uio); 1140 } 1141 rump_pub_uio_free(uio); 1142 cred_destroy(cred); 1143 1144 return rv; 1145 } 1146 1147 /*ARGSUSED*/ 1148 int 1149 p2k_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc, 1150 const struct puffs_cred *pcr, char *linkname, size_t *linklen) 1151 { 1152 struct vnode *vp = OPC2VP(opc); 1153 kauth_cred_t cred; 1154 struct uio *uio; 1155 int rv; 1156 1157 cred = cred_create(pcr); 1158 uio = rump_pub_uio_setup(linkname, *linklen, 0, RUMPUIO_READ); 1159 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 1160 rv = RUMP_VOP_READLINK(vp, uio, cred); 1161 RUMP_VOP_UNLOCK(vp, 0); 1162 *linklen -= rump_pub_uio_free(uio); 1163 cred_destroy(cred); 1164 1165 return rv; 1166 } 1167 1168 /*ARGSUSED*/ 1169 int 1170 p2k_node_read(struct puffs_usermount *pu, puffs_cookie_t opc, 1171 uint8_t *buf, off_t offset, size_t *resid, 1172 const struct puffs_cred *pcr, int ioflag) 1173 { 1174 struct vnode *vp = OPC2VP(opc); 1175 kauth_cred_t cred; 1176 struct uio *uio; 1177 int rv; 1178 1179 cred = cred_create(pcr); 1180 uio = rump_pub_uio_setup(buf, *resid, offset, RUMPUIO_READ); 1181 RUMP_VOP_LOCK(vp, LK_SHARED); 1182 rv = RUMP_VOP_READ(vp, uio, ioflag, cred); 1183 RUMP_VOP_UNLOCK(vp, 0); 1184 *resid = rump_pub_uio_free(uio); 1185 cred_destroy(cred); 1186 1187 return rv; 1188 } 1189 1190 /*ARGSUSED*/ 1191 int 1192 p2k_node_write(struct puffs_usermount *pu, puffs_cookie_t opc, 1193 uint8_t *buf, off_t offset, size_t *resid, 1194 const struct puffs_cred *pcr, int ioflag) 1195 { 1196 struct vnode *vp = OPC2VP(opc); 1197 kauth_cred_t cred; 1198 struct uio *uio; 1199 int rv; 1200 1201 /* "deadfs" */ 1202 if (!vp) 1203 return 0; 1204 1205 cred = cred_create(pcr); 1206 uio = rump_pub_uio_setup(buf, *resid, offset, RUMPUIO_WRITE); 1207 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 1208 rv = RUMP_VOP_WRITE(vp, uio, ioflag, cred); 1209 RUMP_VOP_UNLOCK(vp, 0); 1210 *resid = rump_pub_uio_free(uio); 1211 cred_destroy(cred); 1212 1213 return rv; 1214 } 1215 1216 /* the kernel releases its last reference here */ 1217 int 1218 p2k_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc) 1219 { 1220 struct p2k_node *p2n = opc; 1221 struct vnode *vp = OPC2VP(opc); 1222 bool recycle = false; 1223 int rv; 1224 1225 /* deadfs */ 1226 if (!vp) 1227 return 0; 1228 1229 /* 1230 * Flush all cached vnode pages from the rump kernel -- they 1231 * are kept in puffs for all things that matter. 1232 */ 1233 rump_pub_vp_interlock(vp); 1234 (void) RUMP_VOP_PUTPAGES(vp, 0, 0, PGO_ALLPAGES|PGO_CLEANIT|PGO_FREE); 1235 1236 /* 1237 * Ok, this is where we get nasty. We pretend the vnode is 1238 * inactive and already tell the file system that. However, 1239 * we are allowed to pretend it also grows a reference immediately 1240 * after per vget(), so this does not do harm. Cheap trick, but ... 1241 * 1242 * If the file system thinks the inode is done for, we release 1243 * our reference and clear all knowledge of the vnode. If, 1244 * however, the inode is still active, we retain our reference 1245 * until reclaim, since puffs might be flushing out some data 1246 * later. 1247 */ 1248 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 1249 rv = RUMP_VOP_INACTIVE(vp, &recycle); 1250 if (recycle) { 1251 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N1); 1252 rump_pub_vp_rele(p2n->p2n_vp); 1253 p2n->p2n_vp = NULL; 1254 } 1255 1256 return rv; 1257 } 1258 1259 /*ARGSUSED*/ 1260 int 1261 p2k_node_reclaim(struct puffs_usermount *pu, puffs_croissant_t opc) 1262 { 1263 struct p2k_node *p2n = opc; 1264 1265 if (p2n->p2n_vp) { 1266 rump_pub_vp_rele(p2n->p2n_vp); 1267 p2n->p2n_vp = NULL; 1268 } 1269 1270 freep2n(p2n); 1271 return 0; 1272 } 1273