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