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