1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/param.h> 30 #include <sys/t_lock.h> 31 #include <sys/errno.h> 32 #include <sys/sysmacros.h> 33 #include <sys/buf.h> 34 #include <sys/systm.h> 35 #include <sys/vfs.h> 36 #include <sys/vnode.h> 37 #include <sys/kmem.h> 38 #include <sys/proc.h> 39 #include <sys/cred.h> 40 #include <sys/cmn_err.h> 41 #include <sys/debug.h> 42 #include <vm/pvn.h> 43 #include <sys/fs/pc_label.h> 44 #include <sys/fs/pc_fs.h> 45 #include <sys/fs/pc_dir.h> 46 #include <sys/fs/pc_node.h> 47 #include <sys/dirent.h> 48 #include <sys/fdio.h> 49 #include <sys/file.h> 50 #include <sys/conf.h> 51 52 struct pchead pcfhead[NPCHASH]; 53 struct pchead pcdhead[NPCHASH]; 54 55 extern krwlock_t pcnodes_lock; 56 57 static int pc_getentryblock(struct pcnode *, struct buf **); 58 static int syncpcp(struct pcnode *, int); 59 60 /* 61 * fake entry for root directory, since this does not have a parent 62 * pointing to it. 63 */ 64 static struct pcdir rootentry = { 65 "", 66 "", 67 PCA_DIR 68 }; 69 70 void 71 pc_init(void) 72 { 73 struct pchead *hdp, *hfp; 74 int i; 75 for (i = 0; i < NPCHASH; i++) { 76 hdp = &pcdhead[i]; 77 hfp = &pcfhead[i]; 78 hdp->pch_forw = (struct pcnode *)hdp; 79 hdp->pch_back = (struct pcnode *)hdp; 80 hfp->pch_forw = (struct pcnode *)hfp; 81 hfp->pch_back = (struct pcnode *)hfp; 82 } 83 } 84 85 struct pcnode * 86 pc_getnode( 87 struct pcfs *fsp, /* filsystem for node */ 88 daddr_t blkno, /* phys block no of dir entry */ 89 int offset, /* offset of dir entry in block */ 90 struct pcdir *ep) /* node dir entry */ 91 { 92 struct pcnode *pcp; 93 struct pchead *hp; 94 struct vnode *vp; 95 pc_cluster32_t scluster; 96 97 ASSERT(fsp->pcfs_flags & PCFS_LOCKED); 98 if (ep == (struct pcdir *)0) { 99 ep = &rootentry; 100 scluster = 0; 101 } else { 102 scluster = pc_getstartcluster(fsp, ep); 103 } 104 /* 105 * First look for active nodes. 106 * File nodes are identified by the location (blkno, offset) of 107 * its directory entry. 108 * Directory nodes are identified by the starting cluster number 109 * for the entries. 110 */ 111 if (ep->pcd_attr & PCA_DIR) { 112 hp = &pcdhead[PCDHASH(fsp, scluster)]; 113 rw_enter(&pcnodes_lock, RW_READER); 114 for (pcp = hp->pch_forw; 115 pcp != (struct pcnode *)hp; pcp = pcp->pc_forw) { 116 if ((fsp == VFSTOPCFS(PCTOV(pcp)->v_vfsp)) && 117 (scluster == pcp->pc_scluster)) { 118 VN_HOLD(PCTOV(pcp)); 119 rw_exit(&pcnodes_lock); 120 return (pcp); 121 } 122 } 123 rw_exit(&pcnodes_lock); 124 } else { 125 hp = &pcfhead[PCFHASH(fsp, blkno, offset)]; 126 rw_enter(&pcnodes_lock, RW_READER); 127 for (pcp = hp->pch_forw; 128 pcp != (struct pcnode *)hp; pcp = pcp->pc_forw) { 129 if ((fsp == VFSTOPCFS(PCTOV(pcp)->v_vfsp)) && 130 ((pcp->pc_flags & PC_INVAL) == 0) && 131 (blkno == pcp->pc_eblkno) && 132 (offset == pcp->pc_eoffset)) { 133 VN_HOLD(PCTOV(pcp)); 134 rw_exit(&pcnodes_lock); 135 return (pcp); 136 } 137 } 138 rw_exit(&pcnodes_lock); 139 } 140 /* 141 * Cannot find node in active list. Allocate memory for a new node 142 * initialize it, and put it on the active list. 143 */ 144 pcp = kmem_alloc(sizeof (struct pcnode), KM_SLEEP); 145 bzero(pcp, sizeof (struct pcnode)); 146 vp = vn_alloc(KM_SLEEP); 147 pcp->pc_vn = vp; 148 pcp->pc_entry = *ep; 149 pcp->pc_eblkno = blkno; 150 pcp->pc_eoffset = offset; 151 pcp->pc_scluster = scluster; 152 pcp->pc_lcluster = scluster; 153 pcp->pc_lindex = 0; 154 pcp->pc_flags = 0; 155 if (ep->pcd_attr & PCA_DIR) { 156 vn_setops(vp, pcfs_dvnodeops); 157 vp->v_type = VDIR; 158 if (scluster == 0) { 159 vp->v_flag = VROOT; 160 blkno = offset = 0; 161 if (IS_FAT32(fsp)) { 162 pcp->pc_size = pc_fileclsize(fsp, 163 fsp->pcfs_rdirstart) * fsp->pcfs_clsize; 164 } else { 165 pcp->pc_size = 166 fsp->pcfs_rdirsec * fsp->pcfs_secsize; 167 } 168 } else 169 pcp->pc_size = pc_fileclsize(fsp, scluster) * 170 fsp->pcfs_clsize; 171 } else { 172 vn_setops(vp, pcfs_fvnodeops); 173 vp->v_type = VREG; 174 vp->v_flag = VNOSWAP; 175 fsp->pcfs_frefs++; 176 pcp->pc_size = ltohi(ep->pcd_size); 177 } 178 fsp->pcfs_nrefs++; 179 vp->v_data = (caddr_t)pcp; 180 vp->v_vfsp = PCFSTOVFS(fsp); 181 vn_exists(vp); 182 rw_enter(&pcnodes_lock, RW_WRITER); 183 insque(pcp, hp); 184 rw_exit(&pcnodes_lock); 185 return (pcp); 186 } 187 188 int 189 syncpcp(struct pcnode *pcp, int flags) 190 { 191 int err; 192 if (!vn_has_cached_data(PCTOV(pcp))) 193 err = 0; 194 else 195 err = VOP_PUTPAGE(PCTOV(pcp), (offset_t)0, (uint_t)0, 196 flags, (struct cred *)0); 197 198 return (err); 199 } 200 201 void 202 pc_rele(struct pcnode *pcp) 203 { 204 struct pcfs *fsp; 205 struct vnode *vp; 206 int err; 207 208 vp = PCTOV(pcp); 209 PC_DPRINTF1(8, "pc_rele vp=0x%p\n", (void *)vp); 210 211 fsp = VFSTOPCFS(vp->v_vfsp); 212 ASSERT(fsp->pcfs_flags & PCFS_LOCKED); 213 214 rw_enter(&pcnodes_lock, RW_WRITER); 215 pcp->pc_flags |= PC_RELEHOLD; 216 217 retry: 218 if (vp->v_type != VDIR && (pcp->pc_flags & PC_INVAL) == 0) { 219 /* 220 * If the file was removed while active it may be safely 221 * truncated now. 222 */ 223 224 if (pcp->pc_entry.pcd_filename[0] == PCD_ERASED) { 225 (void) pc_truncate(pcp, 0); 226 } else if (pcp->pc_flags & PC_CHG) { 227 (void) pc_nodeupdate(pcp); 228 } 229 err = syncpcp(pcp, B_INVAL); 230 if (err) { 231 (void) syncpcp(pcp, B_INVAL|B_FORCE); 232 } 233 } 234 if (vn_has_cached_data(vp)) { 235 /* 236 * pvn_vplist_dirty will abort all old pages 237 */ 238 (void) pvn_vplist_dirty(vp, (u_offset_t)0, 239 pcfs_putapage, B_INVAL, (struct cred *)NULL); 240 } 241 242 (void) pc_syncfat(fsp); 243 mutex_enter(&vp->v_lock); 244 if (vn_has_cached_data(vp)) { 245 mutex_exit(&vp->v_lock); 246 goto retry; 247 } 248 ASSERT(!vn_has_cached_data(vp)); 249 250 vp->v_count--; /* release our hold from vn_rele */ 251 if (vp->v_count > 0) { /* Is this check still needed? */ 252 PC_DPRINTF1(3, "pc_rele: pcp=0x%p HELD AGAIN!\n", (void *)pcp); 253 mutex_exit(&vp->v_lock); 254 pcp->pc_flags &= ~PC_RELEHOLD; 255 rw_exit(&pcnodes_lock); 256 return; 257 } 258 259 remque(pcp); 260 rw_exit(&pcnodes_lock); 261 if ((vp->v_type == VREG) && !(pcp->pc_flags & PC_INVAL)) { 262 fsp->pcfs_frefs--; 263 } 264 fsp->pcfs_nrefs--; 265 266 if (fsp->pcfs_nrefs < 0) { 267 panic("pc_rele: nrefs count"); 268 } 269 if (fsp->pcfs_frefs < 0) { 270 panic("pc_rele: frefs count"); 271 } 272 273 mutex_exit(&vp->v_lock); 274 vn_invalid(vp); 275 vn_free(vp); 276 kmem_free(pcp, sizeof (struct pcnode)); 277 } 278 279 /* 280 * Mark a pcnode as modified with the current time. 281 */ 282 void 283 pc_mark_mod(struct pcnode *pcp) 284 { 285 timestruc_t now; 286 287 if (PCTOV(pcp)->v_type == VREG) { 288 gethrestime(&now); 289 pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime); 290 pcp->pc_flags |= PC_CHG; 291 } 292 } 293 294 /* 295 * Mark a pcnode as accessed with the current time. 296 */ 297 void 298 pc_mark_acc(struct pcnode *pcp) 299 { 300 struct pctime pt; 301 timestruc_t now; 302 303 if (PCTOV(pcp)->v_type == VREG) { 304 gethrestime(&now); 305 pc_tvtopct(&now, &pt); 306 pcp->pc_entry.pcd_ladate = pt.pct_date; 307 pcp->pc_flags |= PC_CHG; 308 } 309 } 310 311 /* 312 * Truncate a file to a length. 313 * Node must be locked. 314 */ 315 int 316 pc_truncate(struct pcnode *pcp, uint_t length) 317 { 318 struct pcfs *fsp; 319 struct vnode *vp; 320 int error = 0; 321 322 PC_DPRINTF3(4, "pc_truncate pcp=0x%p, len=%u, size=%u\n", 323 (void *)pcp, length, pcp->pc_size); 324 vp = PCTOV(pcp); 325 if (pcp->pc_flags & PC_INVAL) 326 return (EIO); 327 fsp = VFSTOPCFS(vp->v_vfsp); 328 /* 329 * directories are always truncated to zero and are not marked 330 */ 331 if (vp->v_type == VDIR) { 332 error = pc_bfree(pcp, 0); 333 return (error); 334 } 335 /* 336 * If length is the same as the current size 337 * just mark the pcnode and return. 338 */ 339 if (length > pcp->pc_size) { 340 daddr_t bno; 341 uint_t llcn; 342 343 /* 344 * We are extending a file. 345 * Extend it with _one_ call to pc_balloc (no holes) 346 * since we don't need to use the block number(s). 347 */ 348 if ((daddr_t)howmany((offset_t)pcp->pc_size, fsp->pcfs_clsize) < 349 (llcn = (daddr_t)howmany((offset_t)length, 350 fsp->pcfs_clsize))) { 351 error = pc_balloc(pcp, (daddr_t)(llcn - 1), 1, &bno); 352 } 353 if (error) { 354 PC_DPRINTF1(2, "pc_truncate: error=%d\n", error); 355 /* 356 * probably ran out disk space; 357 * determine current file size 358 */ 359 pcp->pc_size = fsp->pcfs_clsize * 360 pc_fileclsize(fsp, pcp->pc_scluster); 361 } else 362 pcp->pc_size = length; 363 364 } else if (length < pcp->pc_size) { 365 /* 366 * We are shrinking a file. 367 * Free blocks after the block that length points to. 368 */ 369 if (pc_blkoff(fsp, length) == 0) { 370 /* 371 * Truncation to a block (cluster size) boundary only 372 * requires us to invalidate everything after the new 373 * end of the file. 374 */ 375 (void) pvn_vplist_dirty(PCTOV(pcp), (u_offset_t)length, 376 pcfs_putapage, B_INVAL | B_TRUNC, CRED()); 377 } else { 378 /* 379 * pvn_vpzero() cannot deal with more than MAXBSIZE 380 * chunks. Since the FAT clustersize can get larger 381 * than that, we'll zero from the new length to the 382 * end of the cluster for clustersizes smaller than 383 * MAXBSIZE - or the end of the MAXBSIZE block in 384 * case we've got a large clustersize. 385 */ 386 size_t nbytes = 387 roundup(length, MIN(fsp->pcfs_clsize, MAXBSIZE)) - 388 length; 389 390 pvn_vpzero(PCTOV(pcp), (u_offset_t)length, nbytes); 391 (void) pvn_vplist_dirty(PCTOV(pcp), 392 (u_offset_t)length + nbytes, 393 pcfs_putapage, B_INVAL | B_TRUNC, CRED()); 394 } 395 error = pc_bfree(pcp, 396 (pc_cluster32_t)howmany((offset_t)length, 397 fsp->pcfs_clsize)); 398 pcp->pc_size = length; 399 } 400 pc_mark_mod(pcp); 401 return (error); 402 } 403 404 /* 405 * Get block for entry. 406 */ 407 static int 408 pc_getentryblock(struct pcnode *pcp, struct buf **bpp) 409 { 410 struct pcfs *fsp; 411 412 PC_DPRINTF0(7, "pc_getentryblock "); 413 fsp = VFSTOPCFS(PCTOV(pcp)->v_vfsp); 414 if (pcp->pc_eblkno >= fsp->pcfs_datastart || 415 (pcp->pc_eblkno - fsp->pcfs_rdirstart) < 416 (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) { 417 *bpp = bread(fsp->pcfs_xdev, 418 pc_dbdaddr(fsp, pcp->pc_eblkno), fsp->pcfs_clsize); 419 } else { 420 *bpp = bread(fsp->pcfs_xdev, 421 pc_dbdaddr(fsp, pcp->pc_eblkno), 422 (int)(fsp->pcfs_datastart-pcp->pc_eblkno) * 423 fsp->pcfs_secsize); 424 } 425 if ((*bpp)->b_flags & B_ERROR) { 426 PC_DPRINTF0(1, "pc_getentryblock: error "); 427 brelse(*bpp); 428 pc_mark_irrecov(fsp); 429 return (EIO); 430 } 431 return (0); 432 } 433 434 /* 435 * Sync all data associated with a file. 436 * Flush all the blocks in the buffer cache out to disk, sync the FAT and 437 * update the directory entry. 438 */ 439 int 440 pc_nodesync(struct pcnode *pcp) 441 { 442 struct pcfs *fsp; 443 int err; 444 struct vnode *vp; 445 446 PC_DPRINTF1(7, "pc_nodesync pcp=0x%p\n", (void *)pcp); 447 vp = PCTOV(pcp); 448 fsp = VFSTOPCFS(vp->v_vfsp); 449 err = 0; 450 if (pcp->pc_flags & PC_MOD) { 451 /* 452 * Flush all data blocks from buffer cache and 453 * update the FAT which points to the data. 454 */ 455 if (err = syncpcp(pcp, 0)) { /* %% ?? how to handle error? */ 456 if (err == ENOMEM) 457 return (err); 458 else { 459 pc_mark_irrecov(fsp); 460 return (EIO); 461 } 462 } 463 pcp->pc_flags &= ~PC_MOD; 464 } 465 /* 466 * update the directory entry 467 */ 468 if (pcp->pc_flags & PC_CHG) 469 (void) pc_nodeupdate(pcp); 470 return (err); 471 } 472 473 /* 474 * Update the node's directory entry. 475 */ 476 int 477 pc_nodeupdate(struct pcnode *pcp) 478 { 479 struct buf *bp; 480 int error; 481 struct vnode *vp; 482 struct pcfs *fsp; 483 484 vp = PCTOV(pcp); 485 fsp = VFSTOPCFS(vp->v_vfsp); 486 if (IS_FAT32(fsp) && (vp->v_flag & VROOT)) { 487 /* no node to update */ 488 pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC); 489 return (0); 490 } 491 if (vp->v_flag & VROOT) { 492 panic("pc_nodeupdate"); 493 } 494 if (pcp->pc_flags & PC_INVAL) 495 return (0); 496 PC_DPRINTF3(7, "pc_nodeupdate pcp=0x%p, bn=%ld, off=%d\n", (void *)pcp, 497 pcp->pc_eblkno, pcp->pc_eoffset); 498 499 if (error = pc_getentryblock(pcp, &bp)) { 500 return (error); 501 } 502 if (vp->v_type == VREG) { 503 if (pcp->pc_flags & PC_CHG) 504 pcp->pc_entry.pcd_attr |= PCA_ARCH; 505 pcp->pc_entry.pcd_size = htoli(pcp->pc_size); 506 } 507 pc_setstartcluster(fsp, &pcp->pc_entry, pcp->pc_scluster); 508 *((struct pcdir *)(bp->b_un.b_addr + pcp->pc_eoffset)) = pcp->pc_entry; 509 bwrite2(bp); 510 error = geterror(bp); 511 if (error) 512 error = EIO; 513 brelse(bp); 514 if (error) { 515 PC_DPRINTF0(1, "pc_nodeupdate ERROR\n"); 516 pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp)); 517 } 518 pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC); 519 return (error); 520 } 521 522 /* 523 * Verify that the disk in the drive is the same one that we 524 * got the pcnode from. 525 * MUST be called with node unlocked. 526 */ 527 /* ARGSUSED */ 528 int 529 pc_verify(struct pcfs *fsp) 530 { 531 int fdstatus = 0; 532 int error = 0; 533 534 if (!fsp || fsp->pcfs_flags & PCFS_IRRECOV) 535 return (EIO); 536 537 if (!(fsp->pcfs_flags & PCFS_NOCHK) && fsp->pcfs_fatp) { 538 PC_DPRINTF1(4, "pc_verify fsp=0x%p\n", (void *)fsp); 539 error = cdev_ioctl(fsp->pcfs_vfs->vfs_dev, 540 FDGETCHANGE, (intptr_t)&fdstatus, FNATIVE|FKIOCTL, 541 NULL, NULL); 542 543 if (error) { 544 if (error == ENOTTY || error == ENXIO) { 545 error = 0; 546 } else { 547 PC_DPRINTF1(1, 548 "pc_verify: FDGETCHANGE ioctl failed: %d\n", 549 error); 550 pc_mark_irrecov(fsp); 551 } 552 } else if (fsp->pcfs_fatjustread) { 553 /* 554 * Ignore the results of the ioctl if we just 555 * read the FAT. There is a good chance that 556 * the disk changed bit will be on, because 557 * we've just mounted and we don't want to 558 * give a false positive that the sky is falling. 559 */ 560 fsp->pcfs_fatjustread = 0; 561 } else { 562 /* 563 * Oddly enough we can't check just one flag here. The 564 * x86 floppy driver sets a different flag 565 * (FDGC_DETECTED) than the sparc driver does. 566 * I think this MAY be a bug, and I filed 4165938 567 * to get someone to look at the behavior 568 * a bit more closely. In the meantime, my testing and 569 * code examination seem to indicate it is safe to 570 * check for either bit being set. 571 */ 572 if (fdstatus & (FDGC_HISTORY | FDGC_DETECTED)) { 573 PC_DPRINTF0(1, "pc_verify: change detected\n"); 574 pc_mark_irrecov(fsp); 575 } 576 } 577 } 578 if (!(error || fsp->pcfs_fatp)) { 579 error = pc_getfat(fsp); 580 } 581 582 return (error); 583 } 584 585 /* 586 * The disk has changed, pulling the rug out from beneath us. 587 * Mark the FS as being in an irrecoverable state. 588 * In a short while we'll clean up. 589 */ 590 void 591 pc_mark_irrecov(struct pcfs *fsp) 592 { 593 if (!(fsp->pcfs_flags & PCFS_NOCHK)) { 594 if (pc_lockfs(fsp, 1, 0)) { 595 /* 596 * Locking failed, which currently would 597 * only happen if the FS were already 598 * marked as hosed. If another reason for 599 * failure were to arise in the future, this 600 * routine would have to change. 601 */ 602 return; 603 } 604 605 fsp->pcfs_flags |= PCFS_IRRECOV; 606 cmn_err(CE_WARN, 607 "Disk was changed during an update or\n" 608 "an irrecoverable error was encountered.\n" 609 "File damage is possible. To prevent further\n" 610 "damage, this pcfs instance will now be frozen.\n" 611 "Use umount(1M) to release the instance.\n"); 612 (void) pc_unlockfs(fsp); 613 } 614 } 615 616 /* 617 * The disk has been changed! 618 */ 619 void 620 pc_diskchanged(struct pcfs *fsp) 621 { 622 struct pcnode *pcp, *npcp = NULL; 623 struct pchead *hp; 624 struct vnode *vp; 625 extern vfs_t EIO_vfs; 626 627 /* 628 * Eliminate all pcnodes (dir & file) associated with this fs. 629 * If the node is internal, ie, no references outside of 630 * pcfs itself, then release the associated vnode structure. 631 * Invalidate the in core FAT. 632 * Invalidate cached data blocks and blocks waiting for I/O. 633 */ 634 PC_DPRINTF1(1, "pc_diskchanged fsp=0x%p\n", (void *)fsp); 635 636 for (hp = pcdhead; hp < &pcdhead[NPCHASH]; hp++) { 637 for (pcp = hp->pch_forw; 638 pcp != (struct pcnode *)hp; pcp = npcp) { 639 npcp = pcp -> pc_forw; 640 vp = PCTOV(pcp); 641 if (VFSTOPCFS(vp->v_vfsp) == fsp && 642 !(pcp->pc_flags & PC_RELEHOLD)) { 643 mutex_enter(&(vp)->v_lock); 644 if (vp->v_count > 0) { 645 mutex_exit(&(vp)->v_lock); 646 continue; 647 } 648 mutex_exit(&(vp)->v_lock); 649 VN_HOLD(vp); 650 remque(pcp); 651 vp->v_data = NULL; 652 vp->v_vfsp = &EIO_vfs; 653 vp->v_type = VBAD; 654 VN_RELE(vp); 655 if (!(pcp->pc_flags & PC_EXTERNAL)) 656 vn_free(vp); 657 kmem_free(pcp, sizeof (struct pcnode)); 658 fsp->pcfs_nrefs --; 659 } 660 } 661 } 662 for (hp = pcfhead; fsp->pcfs_frefs && hp < &pcfhead[NPCHASH]; hp++) { 663 for (pcp = hp->pch_forw; fsp->pcfs_frefs && 664 pcp != (struct pcnode *)hp; pcp = npcp) { 665 npcp = pcp -> pc_forw; 666 vp = PCTOV(pcp); 667 if (VFSTOPCFS(vp->v_vfsp) == fsp && 668 !(pcp->pc_flags & PC_RELEHOLD)) { 669 mutex_enter(&(vp)->v_lock); 670 if (vp->v_count > 0) { 671 mutex_exit(&(vp)->v_lock); 672 continue; 673 } 674 mutex_exit(&(vp)->v_lock); 675 VN_HOLD(vp); 676 remque(pcp); 677 vp->v_data = NULL; 678 vp->v_vfsp = &EIO_vfs; 679 vp->v_type = VBAD; 680 VN_RELE(vp); 681 if (!(pcp->pc_flags & PC_EXTERNAL)) 682 vn_free(vp); 683 kmem_free(pcp, sizeof (struct pcnode)); 684 fsp->pcfs_frefs --; 685 fsp->pcfs_nrefs --; 686 } 687 } 688 } 689 #ifdef undef 690 if (fsp->pcfs_frefs) { 691 rw_exit(&pcnodes_lock); 692 panic("pc_diskchanged: frefs"); 693 } 694 if (fsp->pcfs_nrefs) { 695 rw_exit(&pcnodes_lock); 696 panic("pc_diskchanged: nrefs"); 697 } 698 #endif 699 if (fsp->pcfs_fatp != (uchar_t *)0) { 700 pc_invalfat(fsp); 701 } else { 702 binval(fsp->pcfs_xdev); 703 } 704 } 705