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