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,
 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         vp->v_count--;  /* release our hold from vn_rele */
 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 }