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