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 * Copyright (c) 2017 by Delphix. All rights reserved. 27 */ 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 struct pcdir pcfs_rootdirentry = { 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 = &pcfs_rootdirentry; 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_zalloc(sizeof (struct pcnode), KM_SLEEP); 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, 211 kcred, NULL); 212 213 return (err); 214 } 215 216 void 217 pc_rele(struct pcnode *pcp) 218 { 219 struct pcfs *fsp; 220 struct vnode *vp; 221 int err; 222 223 vp = PCTOV(pcp); 224 PC_DPRINTF1(8, "pc_rele vp=0x%p\n", (void *)vp); 225 226 fsp = VFSTOPCFS(vp->v_vfsp); 227 ASSERT(fsp->pcfs_flags & PCFS_LOCKED); 228 229 rw_enter(&pcnodes_lock, RW_WRITER); 230 pcp->pc_flags |= PC_RELEHOLD; 231 232 retry: 233 if (vp->v_type != VDIR && (pcp->pc_flags & PC_INVAL) == 0) { 234 /* 235 * If the file was removed while active it may be safely 236 * truncated now. 237 */ 238 239 if (pcp->pc_entry.pcd_filename[0] == PCD_ERASED) { 240 (void) pc_truncate(pcp, 0); 241 } else if (pcp->pc_flags & PC_CHG) { 242 (void) pc_nodeupdate(pcp); 243 } 244 err = syncpcp(pcp, B_INVAL); 245 if (err) { 246 (void) syncpcp(pcp, B_INVAL | B_FORCE); 247 } 248 } 249 if (vn_has_cached_data(vp)) { 250 /* 251 * pvn_vplist_dirty will abort all old pages 252 */ 253 (void) pvn_vplist_dirty(vp, (u_offset_t)0, 254 pcfs_putapage, B_INVAL, (struct cred *)NULL); 255 } 256 257 (void) pc_syncfat(fsp); 258 mutex_enter(&vp->v_lock); 259 if (vn_has_cached_data(vp)) { 260 mutex_exit(&vp->v_lock); 261 goto retry; 262 } 263 ASSERT(!vn_has_cached_data(vp)); 264 265 VN_RELE_LOCKED(vp); 266 if (vp->v_count > 0) { /* Is this check still needed? */ 267 PC_DPRINTF1(3, "pc_rele: pcp=0x%p HELD AGAIN!\n", (void *)pcp); 268 mutex_exit(&vp->v_lock); 269 pcp->pc_flags &= ~PC_RELEHOLD; 270 rw_exit(&pcnodes_lock); 271 return; 272 } 273 274 remque(pcp); 275 rw_exit(&pcnodes_lock); 276 /* 277 * XXX - old code had a check for !(pcp->pc_flags & PC_INVAL) 278 * here. Seems superfluous/incorrect, but then earlier on PC_INVAL 279 * was never set anywhere in PCFS. Now it is, and we _have_ to drop 280 * the file reference here. Else, we'd screw up umount/modunload. 281 */ 282 if ((vp->v_type == VREG)) { 283 fsp->pcfs_frefs--; 284 } 285 fsp->pcfs_nrefs--; 286 VFS_RELE(vp->v_vfsp); 287 288 if (fsp->pcfs_nrefs < 0) { 289 panic("pc_rele: nrefs count"); 290 } 291 if (fsp->pcfs_frefs < 0) { 292 panic("pc_rele: frefs count"); 293 } 294 295 mutex_exit(&vp->v_lock); 296 vn_invalid(vp); 297 vn_free(vp); 298 kmem_free(pcp, sizeof (struct pcnode)); 299 } 300 301 /* 302 * Mark a pcnode as modified with the current time. 303 */ 304 /* ARGSUSED */ 305 void 306 pc_mark_mod(struct pcfs *fsp, struct pcnode *pcp) 307 { 308 timestruc_t now; 309 310 if (PCTOV(pcp)->v_type == VDIR) 311 return; 312 313 ASSERT(PCTOV(pcp)->v_type == VREG); 314 315 gethrestime(&now); 316 if (pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime)) 317 PC_DPRINTF1(2, "pc_mark_mod failed timestamp " 318 "conversion, curtime = %lld\n", 319 (long long)now.tv_sec); 320 321 pcp->pc_flags |= PC_CHG; 322 } 323 324 /* 325 * Mark a pcnode as accessed with the current time. 326 */ 327 void 328 pc_mark_acc(struct pcfs *fsp, struct pcnode *pcp) 329 { 330 struct pctime pt = { 0, 0 }; 331 timestruc_t now; 332 333 if (fsp->pcfs_flags & PCFS_NOATIME || PCTOV(pcp)->v_type == VDIR) 334 return; 335 336 ASSERT(PCTOV(pcp)->v_type == VREG); 337 338 gethrestime(&now); 339 if (pc_tvtopct(&now, &pt)) { 340 PC_DPRINTF1(2, "pc_mark_acc failed timestamp " 341 "conversion, curtime = %lld\n", 342 (long long)now.tv_sec); 343 return; 344 } 345 346 /* 347 * We don't really want to write the adate for every access 348 * on flash media; make sure it really changed ! 349 */ 350 if (pcp->pc_entry.pcd_ladate != pt.pct_date) { 351 pcp->pc_entry.pcd_ladate = pt.pct_date; 352 pcp->pc_flags |= (PC_CHG | PC_ACC); 353 } 354 } 355 356 /* 357 * Truncate a file to a length. 358 * Node must be locked. 359 */ 360 int 361 pc_truncate(struct pcnode *pcp, uint_t length) 362 { 363 struct pcfs *fsp; 364 struct vnode *vp; 365 int error = 0; 366 367 PC_DPRINTF3(4, "pc_truncate pcp=0x%p, len=%u, size=%u\n", 368 (void *)pcp, length, pcp->pc_size); 369 vp = PCTOV(pcp); 370 if (pcp->pc_flags & PC_INVAL) 371 return (EIO); 372 fsp = VFSTOPCFS(vp->v_vfsp); 373 /* 374 * directories are always truncated to zero and are not marked 375 */ 376 if (vp->v_type == VDIR) { 377 error = pc_bfree(pcp, 0); 378 return (error); 379 } 380 /* 381 * If length is the same as the current size 382 * just mark the pcnode and return. 383 */ 384 if (length > pcp->pc_size) { 385 daddr_t bno; 386 uint_t llcn = howmany((offset_t)length, fsp->pcfs_clsize); 387 388 /* 389 * We are extending a file. 390 * Extend it with _one_ call to pc_balloc (no holes) 391 * since we don't need to use the block number(s). 392 */ 393 if ((daddr_t)howmany((offset_t)pcp->pc_size, fsp->pcfs_clsize) < 394 (daddr_t)llcn) { 395 error = pc_balloc(pcp, (daddr_t)(llcn - 1), 1, &bno); 396 } 397 if (error) { 398 pc_cluster32_t ncl = 0; 399 PC_DPRINTF1(2, "pc_truncate: error=%d\n", error); 400 /* 401 * probably ran out disk space; 402 * determine current file size 403 */ 404 if (pc_fileclsize(fsp, pcp->pc_scluster, &ncl)) { 405 PC_DPRINTF1(2, "cluster chain corruption, " 406 "scluster=%d\n", pcp->pc_scluster); 407 pcp->pc_flags |= PC_INVAL; 408 } 409 pcp->pc_size = fsp->pcfs_clsize * ncl; 410 } else 411 pcp->pc_size = length; 412 413 } else if (length < pcp->pc_size) { 414 /* 415 * We are shrinking a file. 416 * Free blocks after the block that length points to. 417 */ 418 if (pc_blkoff(fsp, length) == 0) { 419 /* 420 * Truncation to a block (cluster size) boundary only 421 * requires us to invalidate everything after the new 422 * end of the file. 423 */ 424 (void) pvn_vplist_dirty(PCTOV(pcp), (u_offset_t)length, 425 pcfs_putapage, B_INVAL | B_TRUNC, CRED()); 426 } else { 427 /* 428 * pvn_vpzero() cannot deal with more than MAXBSIZE 429 * chunks. Since the FAT clustersize can get larger 430 * than that, we'll zero from the new length to the 431 * end of the cluster for clustersizes smaller than 432 * MAXBSIZE - or the end of the MAXBSIZE block in 433 * case we've got a large clustersize. 434 */ 435 size_t nbytes = 436 roundup(length, MIN(fsp->pcfs_clsize, MAXBSIZE)) - 437 length; 438 439 pvn_vpzero(PCTOV(pcp), (u_offset_t)length, nbytes); 440 (void) pvn_vplist_dirty(PCTOV(pcp), 441 (u_offset_t)length + nbytes, 442 pcfs_putapage, B_INVAL | B_TRUNC, CRED()); 443 } 444 error = pc_bfree(pcp, (pc_cluster32_t) 445 howmany((offset_t)length, fsp->pcfs_clsize)); 446 pcp->pc_size = length; 447 } 448 449 /* 450 * This is the only place in PCFS code where pc_mark_mod() is called 451 * without setting PC_MOD. May be a historical artifact ... 452 */ 453 pc_mark_mod(fsp, pcp); 454 return (error); 455 } 456 457 /* 458 * Get block for entry. 459 */ 460 static int 461 pc_getentryblock(struct pcnode *pcp, struct buf **bpp) 462 { 463 struct pcfs *fsp; 464 465 fsp = VFSTOPCFS(PCTOV(pcp)->v_vfsp); 466 if (pcp->pc_eblkno >= fsp->pcfs_datastart || 467 (pcp->pc_eblkno - fsp->pcfs_rdirstart) < 468 (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) { 469 *bpp = bread(fsp->pcfs_xdev, 470 pc_dbdaddr(fsp, pcp->pc_eblkno), fsp->pcfs_clsize); 471 } else { 472 *bpp = bread(fsp->pcfs_xdev, 473 pc_dbdaddr(fsp, pcp->pc_eblkno), 474 (int)(fsp->pcfs_datastart - pcp->pc_eblkno) * 475 fsp->pcfs_secsize); 476 } 477 if ((*bpp)->b_flags & B_ERROR) { 478 brelse(*bpp); 479 pc_mark_irrecov(fsp); 480 return (EIO); 481 } 482 return (0); 483 } 484 485 /* 486 * Sync all data associated with a file. 487 * Flush all the blocks in the buffer cache out to disk, sync the FAT and 488 * update the directory entry. 489 */ 490 int 491 pc_nodesync(struct pcnode *pcp) 492 { 493 struct pcfs *fsp; 494 int err; 495 struct vnode *vp; 496 497 vp = PCTOV(pcp); 498 fsp = VFSTOPCFS(vp->v_vfsp); 499 err = 0; 500 if (pcp->pc_flags & PC_MOD) { 501 /* 502 * Flush all data blocks from buffer cache and 503 * update the FAT which points to the data. 504 */ 505 if (err = syncpcp(pcp, 0)) { /* %% ?? how to handle error? */ 506 if (err == ENOMEM) 507 return (err); 508 else { 509 pc_mark_irrecov(fsp); 510 return (EIO); 511 } 512 } 513 pcp->pc_flags &= ~PC_MOD; 514 } 515 /* 516 * update the directory entry 517 */ 518 if (pcp->pc_flags & PC_CHG) 519 (void) pc_nodeupdate(pcp); 520 return (err); 521 } 522 523 /* 524 * Update the node's directory entry. 525 */ 526 int 527 pc_nodeupdate(struct pcnode *pcp) 528 { 529 struct buf *bp; 530 int error; 531 struct vnode *vp; 532 struct pcfs *fsp; 533 534 vp = PCTOV(pcp); 535 fsp = VFSTOPCFS(vp->v_vfsp); 536 if (IS_FAT32(fsp) && (vp->v_flag & VROOT)) { 537 /* no node to update */ 538 pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC); 539 return (0); 540 } 541 if (vp->v_flag & VROOT) { 542 panic("pc_nodeupdate"); 543 } 544 if (pcp->pc_flags & PC_INVAL) 545 return (0); 546 PC_DPRINTF3(7, "pc_nodeupdate pcp=0x%p, bn=%ld, off=%d\n", (void *)pcp, 547 pcp->pc_eblkno, pcp->pc_eoffset); 548 549 if (error = pc_getentryblock(pcp, &bp)) { 550 return (error); 551 } 552 if (vp->v_type == VREG) { 553 if (pcp->pc_flags & PC_CHG) 554 pcp->pc_entry.pcd_attr |= PCA_ARCH; 555 pcp->pc_entry.pcd_size = htoli(pcp->pc_size); 556 } 557 pc_setstartcluster(fsp, &pcp->pc_entry, pcp->pc_scluster); 558 *((struct pcdir *)(bp->b_un.b_addr + pcp->pc_eoffset)) = pcp->pc_entry; 559 bwrite2(bp); 560 error = geterror(bp); 561 brelse(bp); 562 if (error) { 563 error = EIO; 564 pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp)); 565 } 566 pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC); 567 return (error); 568 } 569 570 /* 571 * Verify that the disk in the drive is the same one that we 572 * got the pcnode from. 573 * MUST be called with node unlocked. 574 */ 575 int 576 pc_verify(struct pcfs *fsp) 577 { 578 int fdstatus = 0; 579 int error = 0; 580 581 if (!fsp || fsp->pcfs_flags & PCFS_IRRECOV) 582 return (EIO); 583 584 if (!(fsp->pcfs_flags & PCFS_NOCHK) && fsp->pcfs_fatp) { 585 /* 586 * This "has it been removed" check should better be 587 * modified for removeable media that are not floppies. 588 * dkio-managed devices such as USB/firewire external 589 * disks/memory sticks/floppies (gasp) do not understand 590 * this ioctl. 591 */ 592 PC_DPRINTF1(4, "pc_verify fsp=0x%p\n", (void *)fsp); 593 error = cdev_ioctl(fsp->pcfs_vfs->vfs_dev, 594 FDGETCHANGE, (intptr_t)&fdstatus, FNATIVE | FKIOCTL, 595 NULL, NULL); 596 597 if (error) { 598 if (error == ENOTTY || error == ENXIO) { 599 /* 600 * See comment above. This is a workaround 601 * for removeable media that don't understand 602 * floppy ioctls. 603 */ 604 error = 0; 605 } else { 606 PC_DPRINTF1(1, 607 "pc_verify: FDGETCHANGE ioctl failed: %d\n", 608 error); 609 pc_mark_irrecov(fsp); 610 } 611 } else if (fsp->pcfs_fatjustread) { 612 /* 613 * Ignore the results of the ioctl if we just 614 * read the FAT. There is a good chance that 615 * the disk changed bit will be on, because 616 * we've just mounted and we don't want to 617 * give a false positive that the sky is falling. 618 */ 619 fsp->pcfs_fatjustread = 0; 620 } else { 621 /* 622 * Oddly enough we can't check just one flag here. The 623 * x86 floppy driver sets a different flag 624 * (FDGC_DETECTED) than the sparc driver does. 625 * I think this MAY be a bug, and I filed 4165938 626 * to get someone to look at the behavior 627 * a bit more closely. In the meantime, my testing and 628 * code examination seem to indicate it is safe to 629 * check for either bit being set. 630 */ 631 if (fdstatus & (FDGC_HISTORY | FDGC_DETECTED)) { 632 PC_DPRINTF0(1, "pc_verify: change detected\n"); 633 pc_mark_irrecov(fsp); 634 } 635 } 636 } 637 if (error == 0 && fsp->pcfs_fatp == NULL) { 638 error = pc_getfat(fsp); 639 } 640 641 return (error); 642 } 643 644 /* 645 * The disk has changed, pulling the rug out from beneath us. 646 * Mark the FS as being in an irrecoverable state. 647 * In a short while we'll clean up. 648 */ 649 void 650 pc_mark_irrecov(struct pcfs *fsp) 651 { 652 if (!(fsp->pcfs_flags & PCFS_NOCHK)) { 653 if (pc_lockfs(fsp, 1, 0)) { 654 /* 655 * Locking failed, which currently would 656 * only happen if the FS were already 657 * marked as hosed. If another reason for 658 * failure were to arise in the future, this 659 * routine would have to change. 660 */ 661 return; 662 } 663 664 fsp->pcfs_flags |= PCFS_IRRECOV; 665 cmn_err(CE_WARN, 666 "Disk was changed during an update or\n" 667 "an irrecoverable error was encountered.\n" 668 "File damage is possible. To prevent further\n" 669 "damage, this pcfs instance will now be frozen.\n" 670 "Use umount(1M) to release the instance.\n"); 671 (void) pc_unlockfs(fsp); 672 } 673 } 674 675 /* 676 * The disk has been changed! 677 */ 678 void 679 pc_diskchanged(struct pcfs *fsp) 680 { 681 struct pcnode *pcp, *npcp = NULL; 682 struct pchead *hp; 683 struct vnode *vp; 684 extern vfs_t EIO_vfs; 685 struct vfs *vfsp; 686 687 /* 688 * Eliminate all pcnodes (dir & file) associated with this fs. 689 * If the node is internal, ie, no references outside of 690 * pcfs itself, then release the associated vnode structure. 691 * Invalidate the in core FAT. 692 * Invalidate cached data blocks and blocks waiting for I/O. 693 */ 694 PC_DPRINTF1(1, "pc_diskchanged fsp=0x%p\n", (void *)fsp); 695 696 vfsp = PCFSTOVFS(fsp); 697 698 for (hp = pcdhead; hp < &pcdhead[NPCHASH]; hp++) { 699 for (pcp = hp->pch_forw; 700 pcp != (struct pcnode *)hp; pcp = npcp) { 701 npcp = pcp -> pc_forw; 702 vp = PCTOV(pcp); 703 if ((vp->v_vfsp == vfsp) && 704 !(pcp->pc_flags & PC_RELEHOLD)) { 705 mutex_enter(&(vp)->v_lock); 706 if (vp->v_count > 0) { 707 mutex_exit(&(vp)->v_lock); 708 continue; 709 } 710 mutex_exit(&(vp)->v_lock); 711 VN_HOLD(vp); 712 remque(pcp); 713 vp->v_data = NULL; 714 vp->v_vfsp = &EIO_vfs; 715 vp->v_type = VBAD; 716 VN_RELE(vp); 717 if (!(pcp->pc_flags & PC_EXTERNAL)) { 718 (void) pvn_vplist_dirty(vp, 719 (u_offset_t)0, pcfs_putapage, 720 B_INVAL | B_TRUNC, 721 (struct cred *)NULL); 722 vn_free(vp); 723 } 724 kmem_free(pcp, sizeof (struct pcnode)); 725 fsp->pcfs_nrefs --; 726 VFS_RELE(vfsp); 727 } 728 } 729 } 730 for (hp = pcfhead; fsp->pcfs_frefs && hp < &pcfhead[NPCHASH]; hp++) { 731 for (pcp = hp->pch_forw; fsp->pcfs_frefs && 732 pcp != (struct pcnode *)hp; pcp = npcp) { 733 npcp = pcp -> pc_forw; 734 vp = PCTOV(pcp); 735 if ((vp->v_vfsp == vfsp) && 736 !(pcp->pc_flags & PC_RELEHOLD)) { 737 mutex_enter(&(vp)->v_lock); 738 if (vp->v_count > 0) { 739 mutex_exit(&(vp)->v_lock); 740 continue; 741 } 742 mutex_exit(&(vp)->v_lock); 743 VN_HOLD(vp); 744 remque(pcp); 745 vp->v_data = NULL; 746 vp->v_vfsp = &EIO_vfs; 747 vp->v_type = VBAD; 748 VN_RELE(vp); 749 if (!(pcp->pc_flags & PC_EXTERNAL)) { 750 (void) pvn_vplist_dirty(vp, 751 (u_offset_t)0, pcfs_putapage, 752 B_INVAL | B_TRUNC, 753 (struct cred *)NULL); 754 vn_free(vp); 755 } 756 kmem_free(pcp, sizeof (struct pcnode)); 757 fsp->pcfs_frefs--; 758 fsp->pcfs_nrefs--; 759 VFS_RELE(vfsp); 760 } 761 } 762 } 763 #ifdef undef 764 if (fsp->pcfs_frefs) { 765 rw_exit(&pcnodes_lock); 766 panic("pc_diskchanged: frefs"); 767 } 768 if (fsp->pcfs_nrefs) { 769 rw_exit(&pcnodes_lock); 770 panic("pc_diskchanged: nrefs"); 771 } 772 #endif 773 if (!(vfsp->vfs_flag & VFS_UNMOUNTED) && 774 fsp->pcfs_fatp != (uchar_t *)0) { 775 pc_invalfat(fsp); 776 } else { 777 binval(fsp->pcfs_xdev); 778 } 779 } 780