1 /* $NetBSD: p2k.c,v 1.29 2009/11/20 14:24:58 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 int partition, const char *mountpath, int mntflags, 368 void *arg, size_t alen) 369 { 370 char partpath[UKFS_PARTITION_MAXPATHLEN]; 371 char typebuf[PUFFS_TYPELEN]; 372 struct puffs_usermount *pu = p2m->p2m_pu; 373 struct p2k_node *p2n_root; 374 struct ukfs *ukfs = NULL; 375 extern int puffs_fakecc; 376 int rv = -1, sverrno; 377 378 strcpy(typebuf, "p2k|"); 379 if (strcmp(vfsname, "puffs") == 0) { /* XXX */ 380 struct puffs_kargs *args = arg; 381 strlcat(typebuf, args->pa_typename, sizeof(typebuf)); 382 } else { 383 strlcat(typebuf, vfsname, sizeof(typebuf)); 384 } 385 386 if (UKFS_USEPARTITION(partition)) { 387 char partbuf[UKFS_PARTITION_MAGICLEN+1]; 388 389 strlcpy(partpath, devpath, sizeof(partpath)); 390 snprintf(partbuf, sizeof(partbuf), "%s%c%%", 391 UKFS_PARTITION_SCANMAGIC, partition + 'a'); 392 strlcat(partpath, partbuf, sizeof(partpath)); 393 } else { 394 strlcpy(partpath, devpath, sizeof(partpath)); 395 } 396 puffs_setmntinfo(pu, partpath, typebuf); 397 398 if (ukfs_init() == -1) 399 goto out; 400 if (partition != UKFS_PARTITION_NA) 401 ukfs = ukfs_mount_disk(vfsname, devpath, partition, 402 mountpath, mntflags, arg, alen); 403 else 404 ukfs = ukfs_mount(vfsname, devpath, mountpath, mntflags, 405 arg, alen); 406 if (ukfs == NULL) 407 goto out; 408 ukfs_setspecific(ukfs, p2m); 409 p2m->p2m_ukfs = ukfs; 410 p2m->p2m_pu = pu; 411 412 p2m->p2m_rvp = ukfs_getrvp(ukfs); 413 p2n_root = getp2n(p2m, p2m->p2m_rvp, true, NULL); 414 puffs_setfhsize(pu, 0, PUFFS_FHFLAG_PASSTHROUGH); 415 puffs_setstacksize(pu, PUFFS_STACKSIZE_MIN); 416 puffs_fakecc = 1; 417 puffs_set_prepost(pu, makelwp, clearlwp); 418 puffs_set_errnotify(pu, p2k_errcatcher); 419 420 puffs_setspecific(pu, ukfs); 421 rv = puffs_mount(pu, mountpath, mntflags, p2n_root); 422 423 out: 424 if (rv == -1) { 425 sverrno = errno; 426 puffs_cancel(pu, sverrno); 427 if (ukfs) 428 ukfs_release(p2m->p2m_ukfs, UKFS_RELFLAG_FORCE); 429 free(p2m); 430 errno = sverrno; 431 } 432 433 return rv; 434 } 435 436 int 437 p2k_mainloop(struct p2k_mount *p2m) 438 { 439 int rv, sverrno; 440 441 rv = puffs_mainloop(p2m->p2m_pu); 442 sverrno = errno; 443 puffs_exit(p2m->p2m_pu, 1); 444 if (p2m->p2m_ukfs) 445 ukfs_release(p2m->p2m_ukfs, UKFS_RELFLAG_FORCE); 446 free(p2m); 447 448 if (rv == -1) 449 errno = sverrno; 450 return rv; 451 } 452 453 int 454 p2k_run_fs(const char *vfsname, const char *devpath, const char *mountpath, 455 int mntflags, void *arg, size_t alen, uint32_t puffs_flags) 456 { 457 struct p2k_mount *p2m; 458 int rv; 459 460 p2m = p2k_init(puffs_flags); 461 if (p2m == NULL) 462 return -1; 463 rv = setupfs(p2m, vfsname, devpath, UKFS_PARTITION_NA, mountpath, 464 mntflags, arg, alen); 465 if (rv == -1) 466 return rv; 467 return p2k_mainloop(p2m); 468 } 469 470 int 471 p2k_run_diskfs(const char *vfsname, const char *devpath, int partition, 472 const char *mountpath, int mntflags, void *arg, size_t alen, 473 uint32_t puffs_flags) 474 { 475 struct p2k_mount *p2m; 476 int rv; 477 478 p2m = p2k_init(puffs_flags); 479 if (p2m == NULL) 480 return -1; 481 rv = setupfs(p2m, vfsname, devpath, partition, mountpath, mntflags, 482 arg, alen); 483 if (rv == -1) 484 return rv; 485 return p2k_mainloop(p2m); 486 } 487 488 int 489 p2k_setup_fs(struct p2k_mount *p2m, const char *vfsname, const char *devpath, 490 const char *mountpath, int mntflags, void *arg, size_t alen) 491 { 492 493 return setupfs(p2m, vfsname, devpath, UKFS_PARTITION_NA, mountpath, 494 mntflags, arg, alen); 495 } 496 497 int 498 p2k_setup_diskfs(struct p2k_mount *p2m, const char *vfsname, 499 const char *devpath, int partition, const char *mountpath, 500 int mntflags, void *arg, size_t alen) 501 { 502 503 return setupfs(p2m, vfsname, devpath, partition, mountpath, mntflags, 504 arg, alen); 505 } 506 507 int 508 p2k_fs_statvfs(struct puffs_usermount *pu, struct statvfs *sbp) 509 { 510 struct mount *mp = ukfs_getmp(puffs_getspecific(pu)); 511 512 return rump_pub_vfs_statvfs(mp, sbp); 513 } 514 515 /*ARGSUSED*/ 516 int 517 p2k_fs_unmount(struct puffs_usermount *pu, int flags) 518 { 519 struct ukfs *fs = puffs_getspecific(pu); 520 struct p2k_mount *p2m = ukfs_getspecific(fs); 521 int error = 0; 522 523 rump_pub_lwp_release(rump_pub_lwp_curlwp()); /* ukfs & curlwp tricks */ 524 525 rump_pub_vp_rele(p2m->p2m_rvp); 526 if (ukfs_release(fs, 0) != 0) { 527 ukfs_release(fs, UKFS_RELFLAG_FORCE); 528 error = 0; 529 } 530 p2m->p2m_ukfs = NULL; 531 532 rump_pub_lwp_alloc_and_switch(0, 0); 533 return error; 534 } 535 536 int 537 p2k_fs_sync(struct puffs_usermount *pu, int waitfor, 538 const struct puffs_cred *pcr) 539 { 540 struct mount *mp = ukfs_getmp(puffs_getspecific(pu)); 541 kauth_cred_t cred; 542 int rv; 543 544 cred = cred_create(pcr); 545 rv = rump_pub_vfs_sync(mp, waitfor, (kauth_cred_t)cred); 546 cred_destroy(cred); 547 548 return rv; 549 } 550 551 /*ARGSUSED*/ 552 int 553 p2k_fs_fhtonode(struct puffs_usermount *pu, void *fid, size_t fidsize, 554 struct puffs_newinfo *pni) 555 { 556 struct mount *mp = ukfs_getmp(puffs_getspecific(pu)); 557 struct p2k_mount *p2m = ukfs_getspecific(puffs_getspecific(pu)); 558 struct p2k_node *p2n; 559 struct vnode *vp; 560 enum vtype vtype; 561 voff_t vsize; 562 uint64_t rdev; /* XXX: allows running this on NetBSD 5.0 */ 563 int rv; 564 565 rv = rump_pub_vfs_fhtovp(mp, fid, &vp); 566 if (rv) 567 return rv; 568 RUMP_VOP_UNLOCK(vp, 0); 569 570 p2n = getp2n(p2m, vp, false, NULL); 571 if (p2n == NULL) 572 return ENOMEM; 573 574 puffs_newinfo_setcookie(pni, p2n); 575 rump_pub_getvninfo(vp, &vtype, &vsize, (void *)&rdev); 576 puffs_newinfo_setvtype(pni, vtype); 577 puffs_newinfo_setsize(pni, vsize); 578 /* LINTED: yea, it'll lose accuracy, but that's life */ 579 puffs_newinfo_setrdev(pni, rdev); 580 581 return 0; 582 } 583 584 /*ARGSUSED*/ 585 int 586 p2k_fs_nodetofh(struct puffs_usermount *pu, puffs_cookie_t cookie, void *fid, 587 size_t *fidsize) 588 { 589 struct vnode *vp = cookie; 590 591 return rump_pub_vfs_vptofh(vp, fid, fidsize); 592 } 593 594 /*ARGSUSED*/ 595 int 596 p2k_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc, 597 struct puffs_newinfo *pni, const struct puffs_cn *pcn) 598 { 599 struct p2k_mount *p2m = ukfs_getspecific(puffs_getspecific(pu)); 600 struct p2k_node *p2n_dir = opc, *p2n; 601 struct componentname *cn; 602 struct vnode *dvp = p2n_dir->p2n_vp, *vp; 603 enum vtype vtype; 604 voff_t vsize; 605 uint64_t rdev; /* XXX: uint64_t because of stack overwrite in compat */ 606 int rv; 607 608 cn = makecn(pcn, 0); 609 RUMP_VOP_LOCK(dvp, LK_EXCLUSIVE); 610 rv = RUMP_VOP_LOOKUP(dvp, &vp, cn); 611 RUMP_VOP_UNLOCK(dvp, 0); 612 if (rump_pub_checksavecn(cn)) { 613 /* 614 * XXX: detect RENAME by SAVESTART, both src and targ lookups 615 * 616 * XXX part deux: rename syscall actually does two lookups 617 * for the source, the second without SAVESTART. So detect 618 * this also and compensate. 619 */ 620 if (pcn->pcn_flags & NAMEI_SAVESTART) { 621 if (pcn->pcn_nameiop == NAMEI_DELETE) { 622 assert(p2n_dir->p2n_cn_ren_src == NULL); 623 p2n_dir->p2n_cn_ren_src = cn; 624 } else { 625 assert(pcn->pcn_nameiop == NAMEI_RENAME); 626 assert(p2n_dir->p2n_cn_ren_targ == NULL); 627 p2n_dir->p2n_cn_ren_targ = cn; 628 } 629 } else { 630 if (pcn->pcn_nameiop == NAMEI_DELETE 631 && p2n_dir->p2n_cn_ren_src) { 632 freecn(cn, RUMPCN_FORCEFREE); 633 cn = NULL; 634 } else { 635 assert(p2n_dir->p2n_cn == NULL); 636 p2n_dir->p2n_cn = cn; 637 } 638 } 639 } else { 640 freecn(cn, 0); 641 cn = NULL; 642 } 643 if (rv) { 644 if (rv == EJUSTRETURN) { 645 rv = ENOENT; 646 } 647 return rv; 648 } 649 RUMP_VOP_UNLOCK(vp, 0); 650 651 p2n = getp2n(p2m, vp, false, NULL); 652 if (p2n == NULL) { 653 if (pcn->pcn_flags & NAMEI_SAVESTART) { 654 if (pcn->pcn_nameiop == NAMEI_DELETE) { 655 p2n_dir->p2n_cn_ren_src = NULL; 656 } else { 657 p2n_dir->p2n_cn_ren_targ = NULL; 658 } 659 } else { 660 p2n_dir->p2n_cn = NULL; 661 } 662 /* XXX: what in the world should happen with SAVESTART? */ 663 RUMP_VOP_ABORTOP(dvp, cn); 664 return ENOMEM; 665 } 666 667 puffs_newinfo_setcookie(pni, p2n); 668 rump_pub_getvninfo(vp, &vtype, &vsize, (void *)&rdev); 669 puffs_newinfo_setvtype(pni, vtype); 670 puffs_newinfo_setsize(pni, vsize); 671 /* LINTED: yea, it'll lose accuracy, but that's life */ 672 puffs_newinfo_setrdev(pni, rdev); 673 674 return 0; 675 } 676 677 #define VERS_TIMECHANGE 599000700 678 static int 679 needcompat(void) 680 { 681 682 /*LINTED*/ 683 return __NetBSD_Version__ < VERS_TIMECHANGE 684 && rump_pub_getversion() >= VERS_TIMECHANGE; 685 } 686 687 #define DOCOMPAT(va, va_compat) \ 688 do { \ 689 if (needcompat()) { \ 690 va_compat = rump_pub_vattr_init(); \ 691 rump_pub_vattr50_to_vattr(va, va_compat); \ 692 } else { \ 693 va_compat = __UNCONST(va); \ 694 } \ 695 } while (/*CONSTCOND*/0) 696 697 #define UNDOCOMPAT(va_compat) \ 698 do { \ 699 if (needcompat()) \ 700 rump_pub_vattr_free(va_compat); \ 701 } while (/*CONSTCOND*/0) 702 703 static int 704 do_makenode(struct puffs_usermount *pu, struct p2k_node *p2n_dir, 705 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 706 const struct vattr *vap, char *link_target, 707 int (*makefn)(struct vnode *, struct vnode **, struct componentname *, 708 struct vattr *), 709 int (*symfn)(struct vnode *, struct vnode **, struct componentname *, 710 struct vattr *, char *)) 711 { 712 struct p2k_mount *p2m = ukfs_getspecific(puffs_getspecific(pu)); 713 struct vnode *dvp = p2n_dir->p2n_vp; 714 struct p2k_node *p2n; 715 struct componentname *cn; 716 struct vattr *va_x; 717 struct vnode *vp; 718 int rv; 719 720 p2n = malloc(sizeof(*p2n)); 721 if (p2n == NULL) 722 return ENOMEM; 723 DOCOMPAT(vap, va_x); 724 725 if (p2n_dir->p2n_cn) { 726 cn = p2n_dir->p2n_cn; 727 p2n_dir->p2n_cn = NULL; 728 } else { 729 cn = makecn(pcn, RUMP_NAMEI_HASBUF); 730 } 731 732 RUMP_VOP_LOCK(dvp, LK_EXCLUSIVE); 733 rump_pub_vp_incref(dvp); 734 if (makefn) { 735 rv = makefn(dvp, &vp, cn, va_x); 736 } else { 737 rv = symfn(dvp, &vp, cn, va_x, link_target); 738 } 739 assert(RUMP_VOP_ISLOCKED(dvp) == 0); 740 freecn(cn, 0); 741 742 if (rv == 0) { 743 RUMP_VOP_UNLOCK(vp, 0); 744 p2n = getp2n(p2m, vp, true, p2n); 745 puffs_newinfo_setcookie(pni, p2n); 746 } else { 747 free(p2n); 748 } 749 750 UNDOCOMPAT(va_x); 751 752 return rv; 753 754 } 755 756 /*ARGSUSED*/ 757 int 758 p2k_node_create(struct puffs_usermount *pu, puffs_cookie_t opc, 759 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 760 const struct vattr *vap) 761 { 762 763 return do_makenode(pu, opc, pni, pcn, vap, NULL, RUMP_VOP_CREATE, NULL); 764 } 765 766 /*ARGSUSED*/ 767 int 768 p2k_node_mknod(struct puffs_usermount *pu, puffs_cookie_t opc, 769 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 770 const struct vattr *vap) 771 { 772 773 return do_makenode(pu, opc, pni, pcn, vap, NULL, RUMP_VOP_MKNOD, NULL); 774 } 775 776 /*ARGSUSED*/ 777 int 778 p2k_node_open(struct puffs_usermount *pu, puffs_cookie_t opc, int mode, 779 const struct puffs_cred *pcr) 780 { 781 struct vnode *vp = OPC2VP(opc); 782 kauth_cred_t cred; 783 int rv; 784 785 cred = cred_create(pcr); 786 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 787 rv = RUMP_VOP_OPEN(vp, mode, cred); 788 RUMP_VOP_UNLOCK(vp, 0); 789 cred_destroy(cred); 790 791 return rv; 792 } 793 794 /*ARGSUSED*/ 795 int 796 p2k_node_close(struct puffs_usermount *pu, puffs_cookie_t opc, int flags, 797 const struct puffs_cred *pcr) 798 { 799 struct vnode *vp = OPC2VP(opc); 800 kauth_cred_t cred; 801 802 cred = cred_create(pcr); 803 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 804 RUMP_VOP_CLOSE(vp, flags, cred); 805 RUMP_VOP_UNLOCK(vp, 0); 806 cred_destroy(cred); 807 808 return 0; 809 } 810 811 /*ARGSUSED*/ 812 int 813 p2k_node_access(struct puffs_usermount *pu, puffs_cookie_t opc, int mode, 814 const struct puffs_cred *pcr) 815 { 816 struct vnode *vp = OPC2VP(opc); 817 kauth_cred_t cred; 818 int rv; 819 820 cred = cred_create(pcr); 821 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 822 rv = RUMP_VOP_ACCESS(vp, mode, cred); 823 RUMP_VOP_UNLOCK(vp, 0); 824 cred_destroy(cred); 825 826 return rv; 827 } 828 829 /*ARGSUSED*/ 830 int 831 p2k_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc, 832 struct vattr *vap, const struct puffs_cred *pcr) 833 { 834 struct vnode *vp = OPC2VP(opc); 835 kauth_cred_t cred; 836 struct vattr *va_x; 837 int rv; 838 839 /* "deadfs" */ 840 if (!vp) 841 return 0; 842 843 if (needcompat()) { 844 va_x = rump_pub_vattr_init(); 845 } else { 846 va_x = vap; 847 } 848 849 cred = cred_create(pcr); 850 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 851 rv = RUMP_VOP_GETATTR(vp, va_x, cred); 852 RUMP_VOP_UNLOCK(vp, 0); 853 cred_destroy(cred); 854 855 if (needcompat()) { 856 rump_pub_vattr_to_vattr50(va_x, vap); 857 rump_pub_vattr_free(va_x); 858 } 859 860 return rv; 861 } 862 863 /*ARGSUSED*/ 864 int 865 p2k_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc, 866 const struct vattr *vap, const struct puffs_cred *pcr) 867 { 868 struct vnode *vp = OPC2VP(opc); 869 kauth_cred_t cred; 870 struct vattr *va_x; 871 int rv; 872 873 /* "deadfs" */ 874 if (!vp) 875 return 0; 876 877 DOCOMPAT(vap, va_x); 878 879 cred = cred_create(pcr); 880 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 881 rv = RUMP_VOP_SETATTR(vp, va_x, cred); 882 RUMP_VOP_UNLOCK(vp, 0); 883 cred_destroy(cred); 884 885 UNDOCOMPAT(va_x); 886 887 return rv; 888 } 889 890 /*ARGSUSED*/ 891 int 892 p2k_node_fsync(struct puffs_usermount *pu, puffs_cookie_t opc, 893 const struct puffs_cred *pcr, int flags, off_t offlo, off_t offhi) 894 { 895 struct vnode *vp = OPC2VP(opc); 896 kauth_cred_t cred; 897 int rv; 898 899 /* "deadfs" */ 900 if (!vp) 901 return 0; 902 903 cred = cred_create(pcr); 904 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 905 rv = RUMP_VOP_FSYNC(vp, cred, flags, offlo, offhi); 906 RUMP_VOP_UNLOCK(vp, 0); 907 cred_destroy(cred); 908 909 return rv; 910 } 911 912 /*ARGSUSED*/ 913 int 914 p2k_node_mmap(struct puffs_usermount *pu, puffs_cookie_t opc, vm_prot_t flags, 915 const struct puffs_cred *pcr) 916 { 917 kauth_cred_t cred; 918 int rv; 919 920 cred = cred_create(pcr); 921 rv = RUMP_VOP_MMAP(OPC2VP(opc), flags, cred); 922 cred_destroy(cred); 923 924 return rv; 925 } 926 927 /*ARGSUSED*/ 928 int 929 p2k_node_seek(struct puffs_usermount *pu, puffs_cookie_t opc, 930 off_t oldoff, off_t newoff, const struct puffs_cred *pcr) 931 { 932 struct vnode *vp = OPC2VP(opc); 933 kauth_cred_t cred; 934 int rv; 935 936 cred = cred_create(pcr); 937 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 938 rv = RUMP_VOP_SEEK(vp, oldoff, newoff, cred); 939 RUMP_VOP_UNLOCK(vp, 0); 940 cred_destroy(cred); 941 942 return rv; 943 } 944 945 /*ARGSUSED*/ 946 int 947 p2k_node_abortop(struct puffs_usermount *pu, puffs_cookie_t opc, 948 const struct puffs_cn *pcn) 949 { 950 struct p2k_node *p2n_dir = opc; 951 struct componentname *cnp; 952 953 if ((cnp = p2n_dir->p2n_cn) != NULL) { 954 freecn(cnp, 0); 955 p2n_dir->p2n_cn = NULL; 956 } 957 if ((cnp = p2n_dir->p2n_cn_ren_src) != NULL) { 958 freecn(cnp, RUMPCN_FORCEFREE); 959 p2n_dir->p2n_cn_ren_src = NULL; 960 } 961 if ((cnp = p2n_dir->p2n_cn_ren_targ) != NULL) { 962 freecn(cnp, RUMPCN_FORCEFREE); 963 p2n_dir->p2n_cn_ren_targ = NULL; 964 } 965 966 return 0; 967 } 968 969 static int 970 do_nukenode(struct p2k_node *p2n_dir, struct p2k_node *p2n, 971 const struct puffs_cn *pcn, 972 int (*nukefn)(struct vnode *, struct vnode *, struct componentname *)) 973 { 974 struct vnode *dvp = p2n_dir->p2n_vp, *vp = p2n->p2n_vp; 975 struct componentname *cn; 976 int rv; 977 978 if (p2n_dir->p2n_cn) { 979 cn = p2n_dir->p2n_cn; 980 p2n_dir->p2n_cn = NULL; 981 } else { 982 cn = makecn(pcn, RUMP_NAMEI_HASBUF); 983 } 984 985 RUMP_VOP_LOCK(dvp, LK_EXCLUSIVE); 986 rump_pub_vp_incref(dvp); 987 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 988 rump_pub_vp_incref(vp); 989 rv = nukefn(dvp, vp, cn); 990 assert(RUMP_VOP_ISLOCKED(dvp) == 0); 991 assert(RUMP_VOP_ISLOCKED(vp) == 0); 992 freecn(cn, 0); 993 994 return rv; 995 996 } 997 998 /*ARGSUSED*/ 999 int 1000 p2k_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc, 1001 puffs_cookie_t targ, const struct puffs_cn *pcn) 1002 { 1003 1004 return do_nukenode(opc, targ, pcn, RUMP_VOP_REMOVE); 1005 } 1006 1007 /*ARGSUSED*/ 1008 int 1009 p2k_node_link(struct puffs_usermount *pu, puffs_cookie_t opc, 1010 puffs_cookie_t targ, const struct puffs_cn *pcn) 1011 { 1012 struct vnode *dvp = OPC2VP(opc); 1013 struct p2k_node *p2n_dir = opc; 1014 struct componentname *cn; 1015 int rv; 1016 1017 if (p2n_dir->p2n_cn) { 1018 cn = p2n_dir->p2n_cn; 1019 p2n_dir->p2n_cn = NULL; 1020 } else { 1021 cn = makecn(pcn, RUMP_NAMEI_HASBUF); 1022 } 1023 1024 RUMP_VOP_LOCK(dvp, LK_EXCLUSIVE); 1025 rump_pub_vp_incref(dvp); 1026 rv = RUMP_VOP_LINK(dvp, OPC2VP(targ), cn); 1027 freecn(cn, 0); 1028 1029 return rv; 1030 } 1031 1032 /*ARGSUSED*/ 1033 int 1034 p2k_node_rename(struct puffs_usermount *pu, 1035 puffs_cookie_t src_dir, puffs_cookie_t src, 1036 const struct puffs_cn *pcn_src, 1037 puffs_cookie_t targ_dir, puffs_cookie_t targ, 1038 const struct puffs_cn *pcn_targ) 1039 { 1040 struct p2k_node *p2n_srcdir = src_dir, *p2n_targdir = targ_dir; 1041 struct vnode *dvp, *vp, *tdvp, *tvp = NULL; 1042 struct componentname *cn_src, *cn_targ; 1043 int rv; 1044 1045 if (p2n_srcdir->p2n_cn_ren_src) { 1046 cn_src = p2n_srcdir->p2n_cn_ren_src; 1047 p2n_srcdir->p2n_cn_ren_src = NULL; 1048 } else { 1049 cn_src = makecn(pcn_src, RUMP_NAMEI_HASBUF); 1050 } 1051 1052 if (p2n_targdir->p2n_cn_ren_targ) { 1053 cn_targ = p2n_targdir->p2n_cn_ren_targ; 1054 p2n_targdir->p2n_cn_ren_targ = NULL; 1055 } else { 1056 cn_targ = makecn(pcn_targ, RUMP_NAMEI_HASBUF); 1057 } 1058 1059 dvp = OPC2VP(src_dir); 1060 vp = OPC2VP(src); 1061 tdvp = OPC2VP(targ_dir); 1062 if (targ) { 1063 tvp = OPC2VP(targ); 1064 } 1065 1066 rump_pub_vp_incref(dvp); 1067 rump_pub_vp_incref(vp); 1068 RUMP_VOP_LOCK(tdvp, LK_EXCLUSIVE); 1069 rump_pub_vp_incref(tdvp); 1070 if (tvp) { 1071 RUMP_VOP_LOCK(tvp, LK_EXCLUSIVE); 1072 rump_pub_vp_incref(tvp); 1073 } 1074 rv = RUMP_VOP_RENAME(dvp, vp, cn_src, tdvp, tvp, cn_targ); 1075 assert(RUMP_VOP_ISLOCKED(tdvp) == 0); 1076 if (tvp) { 1077 assert(RUMP_VOP_ISLOCKED(tvp) == 0); 1078 } 1079 freecn(cn_src, RUMPCN_FORCEFREE); 1080 freecn(cn_targ, RUMPCN_FORCEFREE); 1081 1082 return rv; 1083 } 1084 1085 /*ARGSUSED*/ 1086 int 1087 p2k_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc, 1088 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 1089 const struct vattr *vap) 1090 { 1091 1092 return do_makenode(pu, opc, pni, pcn, vap, NULL, RUMP_VOP_MKDIR, NULL); 1093 } 1094 1095 /*ARGSUSED*/ 1096 int 1097 p2k_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc, 1098 puffs_cookie_t targ, const struct puffs_cn *pcn) 1099 { 1100 1101 return do_nukenode(opc, targ, pcn, RUMP_VOP_RMDIR); 1102 } 1103 1104 /*ARGSUSED*/ 1105 int 1106 p2k_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc, 1107 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 1108 const struct vattr *vap, const char *link_target) 1109 { 1110 1111 return do_makenode(pu, opc, pni, pcn, vap, 1112 __UNCONST(link_target), NULL, RUMP_VOP_SYMLINK); 1113 } 1114 1115 /*ARGSUSED*/ 1116 int 1117 p2k_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc, 1118 struct dirent *dent, off_t *readoff, size_t *reslen, 1119 const struct puffs_cred *pcr, int *eofflag, 1120 off_t *cookies, size_t *ncookies) 1121 { 1122 struct vnode *vp = OPC2VP(opc); 1123 kauth_cred_t cred; 1124 struct uio *uio; 1125 off_t *vop_cookies; 1126 int vop_ncookies; 1127 int rv; 1128 1129 cred = cred_create(pcr); 1130 uio = rump_pub_uio_setup(dent, *reslen, *readoff, RUMPUIO_READ); 1131 RUMP_VOP_LOCK(vp, LK_SHARED); 1132 if (cookies) { 1133 rv = RUMP_VOP_READDIR(vp, uio, cred, eofflag, 1134 &vop_cookies, &vop_ncookies); 1135 memcpy(cookies, vop_cookies, vop_ncookies * sizeof(*cookies)); 1136 *ncookies = vop_ncookies; 1137 free(vop_cookies); 1138 } else { 1139 rv = RUMP_VOP_READDIR(vp, uio, cred, eofflag, NULL, NULL); 1140 } 1141 RUMP_VOP_UNLOCK(vp, 0); 1142 if (rv == 0) { 1143 *reslen = rump_pub_uio_getresid(uio); 1144 *readoff = rump_pub_uio_getoff(uio); 1145 } 1146 rump_pub_uio_free(uio); 1147 cred_destroy(cred); 1148 1149 return rv; 1150 } 1151 1152 /*ARGSUSED*/ 1153 int 1154 p2k_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc, 1155 const struct puffs_cred *pcr, char *linkname, size_t *linklen) 1156 { 1157 struct vnode *vp = OPC2VP(opc); 1158 kauth_cred_t cred; 1159 struct uio *uio; 1160 int rv; 1161 1162 cred = cred_create(pcr); 1163 uio = rump_pub_uio_setup(linkname, *linklen, 0, RUMPUIO_READ); 1164 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 1165 rv = RUMP_VOP_READLINK(vp, uio, cred); 1166 RUMP_VOP_UNLOCK(vp, 0); 1167 *linklen -= rump_pub_uio_free(uio); 1168 cred_destroy(cred); 1169 1170 return rv; 1171 } 1172 1173 /*ARGSUSED*/ 1174 int 1175 p2k_node_read(struct puffs_usermount *pu, puffs_cookie_t opc, 1176 uint8_t *buf, off_t offset, size_t *resid, 1177 const struct puffs_cred *pcr, int ioflag) 1178 { 1179 struct vnode *vp = OPC2VP(opc); 1180 kauth_cred_t cred; 1181 struct uio *uio; 1182 int rv; 1183 1184 cred = cred_create(pcr); 1185 uio = rump_pub_uio_setup(buf, *resid, offset, RUMPUIO_READ); 1186 RUMP_VOP_LOCK(vp, LK_SHARED); 1187 rv = RUMP_VOP_READ(vp, uio, ioflag, cred); 1188 RUMP_VOP_UNLOCK(vp, 0); 1189 *resid = rump_pub_uio_free(uio); 1190 cred_destroy(cred); 1191 1192 return rv; 1193 } 1194 1195 /*ARGSUSED*/ 1196 int 1197 p2k_node_write(struct puffs_usermount *pu, puffs_cookie_t opc, 1198 uint8_t *buf, off_t offset, size_t *resid, 1199 const struct puffs_cred *pcr, int ioflag) 1200 { 1201 struct vnode *vp = OPC2VP(opc); 1202 kauth_cred_t cred; 1203 struct uio *uio; 1204 int rv; 1205 1206 /* "deadfs" */ 1207 if (!vp) 1208 return 0; 1209 1210 cred = cred_create(pcr); 1211 uio = rump_pub_uio_setup(buf, *resid, offset, RUMPUIO_WRITE); 1212 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 1213 rv = RUMP_VOP_WRITE(vp, uio, ioflag, cred); 1214 RUMP_VOP_UNLOCK(vp, 0); 1215 *resid = rump_pub_uio_free(uio); 1216 cred_destroy(cred); 1217 1218 return rv; 1219 } 1220 1221 /* the kernel releases its last reference here */ 1222 int 1223 p2k_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc) 1224 { 1225 struct p2k_node *p2n = opc; 1226 struct vnode *vp = OPC2VP(opc); 1227 bool recycle = false; 1228 int rv; 1229 1230 /* deadfs */ 1231 if (!vp) 1232 return 0; 1233 1234 /* 1235 * Flush all cached vnode pages from the rump kernel -- they 1236 * are kept in puffs for all things that matter. 1237 */ 1238 rump_pub_vp_interlock(vp); 1239 (void) RUMP_VOP_PUTPAGES(vp, 0, 0, PGO_ALLPAGES|PGO_CLEANIT|PGO_FREE); 1240 1241 /* 1242 * Ok, this is where we get nasty. We pretend the vnode is 1243 * inactive and already tell the file system that. However, 1244 * we are allowed to pretend it also grows a reference immediately 1245 * after per vget(), so this does not do harm. Cheap trick, but ... 1246 * 1247 * If the file system thinks the inode is done for, we release 1248 * our reference and clear all knowledge of the vnode. If, 1249 * however, the inode is still active, we retain our reference 1250 * until reclaim, since puffs might be flushing out some data 1251 * later. 1252 */ 1253 RUMP_VOP_LOCK(vp, LK_EXCLUSIVE); 1254 rv = RUMP_VOP_INACTIVE(vp, &recycle); 1255 if (recycle) { 1256 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N1); 1257 rump_pub_vp_rele(p2n->p2n_vp); 1258 p2n->p2n_vp = NULL; 1259 } 1260 1261 return rv; 1262 } 1263 1264 /*ARGSUSED*/ 1265 int 1266 p2k_node_reclaim(struct puffs_usermount *pu, puffs_croissant_t opc) 1267 { 1268 struct p2k_node *p2n = opc; 1269 1270 if (p2n->p2n_vp) { 1271 rump_pub_vp_rele(p2n->p2n_vp); 1272 p2n->p2n_vp = NULL; 1273 } 1274 1275 freep2n(p2n); 1276 return 0; 1277 } 1278