xref: /onnv-gate/usr/src/uts/common/fs/pcfs/pc_node.c (revision 5121:c926e7bdc887)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52720Sfrankho  * Common Development and Distribution License (the "License").
62720Sfrankho  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
224356Scasper  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <sys/param.h>
290Sstevel@tonic-gate #include <sys/t_lock.h>
300Sstevel@tonic-gate #include <sys/errno.h>
310Sstevel@tonic-gate #include <sys/sysmacros.h>
320Sstevel@tonic-gate #include <sys/buf.h>
330Sstevel@tonic-gate #include <sys/systm.h>
340Sstevel@tonic-gate #include <sys/vfs.h>
350Sstevel@tonic-gate #include <sys/vnode.h>
360Sstevel@tonic-gate #include <sys/kmem.h>
370Sstevel@tonic-gate #include <sys/proc.h>
380Sstevel@tonic-gate #include <sys/cred.h>
390Sstevel@tonic-gate #include <sys/cmn_err.h>
400Sstevel@tonic-gate #include <sys/debug.h>
410Sstevel@tonic-gate #include <vm/pvn.h>
420Sstevel@tonic-gate #include <sys/fs/pc_label.h>
430Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
440Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
450Sstevel@tonic-gate #include <sys/fs/pc_node.h>
460Sstevel@tonic-gate #include <sys/dirent.h>
470Sstevel@tonic-gate #include <sys/fdio.h>
480Sstevel@tonic-gate #include <sys/file.h>
490Sstevel@tonic-gate #include <sys/conf.h>
500Sstevel@tonic-gate 
510Sstevel@tonic-gate struct pchead pcfhead[NPCHASH];
520Sstevel@tonic-gate struct pchead pcdhead[NPCHASH];
530Sstevel@tonic-gate 
540Sstevel@tonic-gate extern krwlock_t pcnodes_lock;
550Sstevel@tonic-gate 
560Sstevel@tonic-gate static int	pc_getentryblock(struct pcnode *, struct buf **);
570Sstevel@tonic-gate static int	syncpcp(struct pcnode *, int);
580Sstevel@tonic-gate 
590Sstevel@tonic-gate /*
600Sstevel@tonic-gate  * fake entry for root directory, since this does not have a parent
610Sstevel@tonic-gate  * pointing to it.
620Sstevel@tonic-gate  */
632720Sfrankho struct pcdir pcfs_rootdirentry = {
640Sstevel@tonic-gate 	"",
650Sstevel@tonic-gate 	"",
660Sstevel@tonic-gate 	PCA_DIR
670Sstevel@tonic-gate };
680Sstevel@tonic-gate 
690Sstevel@tonic-gate void
700Sstevel@tonic-gate pc_init(void)
710Sstevel@tonic-gate {
720Sstevel@tonic-gate 	struct pchead *hdp, *hfp;
730Sstevel@tonic-gate 	int i;
740Sstevel@tonic-gate 	for (i = 0; i < NPCHASH; i++) {
750Sstevel@tonic-gate 		hdp = &pcdhead[i];
760Sstevel@tonic-gate 		hfp = &pcfhead[i];
770Sstevel@tonic-gate 		hdp->pch_forw =  (struct pcnode *)hdp;
780Sstevel@tonic-gate 		hdp->pch_back =  (struct pcnode *)hdp;
790Sstevel@tonic-gate 		hfp->pch_forw =  (struct pcnode *)hfp;
800Sstevel@tonic-gate 		hfp->pch_back =  (struct pcnode *)hfp;
810Sstevel@tonic-gate 	}
820Sstevel@tonic-gate }
830Sstevel@tonic-gate 
840Sstevel@tonic-gate struct pcnode *
850Sstevel@tonic-gate pc_getnode(
860Sstevel@tonic-gate 	struct pcfs *fsp,	/* filsystem for node */
870Sstevel@tonic-gate 	daddr_t blkno,		/* phys block no of dir entry */
880Sstevel@tonic-gate 	int offset,		/* offset of dir entry in block */
890Sstevel@tonic-gate 	struct pcdir *ep)	/* node dir entry */
900Sstevel@tonic-gate {
910Sstevel@tonic-gate 	struct pcnode *pcp;
920Sstevel@tonic-gate 	struct pchead *hp;
930Sstevel@tonic-gate 	struct vnode *vp;
940Sstevel@tonic-gate 	pc_cluster32_t scluster;
950Sstevel@tonic-gate 
960Sstevel@tonic-gate 	ASSERT(fsp->pcfs_flags & PCFS_LOCKED);
970Sstevel@tonic-gate 	if (ep == (struct pcdir *)0) {
982720Sfrankho 		ep = &pcfs_rootdirentry;
990Sstevel@tonic-gate 		scluster = 0;
1000Sstevel@tonic-gate 	} else {
1010Sstevel@tonic-gate 		scluster = pc_getstartcluster(fsp, ep);
1020Sstevel@tonic-gate 	}
1030Sstevel@tonic-gate 	/*
1040Sstevel@tonic-gate 	 * First look for active nodes.
1050Sstevel@tonic-gate 	 * File nodes are identified by the location (blkno, offset) of
1060Sstevel@tonic-gate 	 * its directory entry.
1070Sstevel@tonic-gate 	 * Directory nodes are identified by the starting cluster number
1080Sstevel@tonic-gate 	 * for the entries.
1090Sstevel@tonic-gate 	 */
1100Sstevel@tonic-gate 	if (ep->pcd_attr & PCA_DIR) {
1110Sstevel@tonic-gate 		hp = &pcdhead[PCDHASH(fsp, scluster)];
1120Sstevel@tonic-gate 		rw_enter(&pcnodes_lock, RW_READER);
1130Sstevel@tonic-gate 		for (pcp = hp->pch_forw;
1140Sstevel@tonic-gate 		    pcp != (struct pcnode *)hp; pcp = pcp->pc_forw) {
1150Sstevel@tonic-gate 			if ((fsp == VFSTOPCFS(PCTOV(pcp)->v_vfsp)) &&
1160Sstevel@tonic-gate 			    (scluster == pcp->pc_scluster)) {
1170Sstevel@tonic-gate 				VN_HOLD(PCTOV(pcp));
1180Sstevel@tonic-gate 				rw_exit(&pcnodes_lock);
1190Sstevel@tonic-gate 				return (pcp);
1200Sstevel@tonic-gate 			}
1210Sstevel@tonic-gate 		}
1220Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
1230Sstevel@tonic-gate 	} else {
1240Sstevel@tonic-gate 		hp = &pcfhead[PCFHASH(fsp, blkno, offset)];
1250Sstevel@tonic-gate 		rw_enter(&pcnodes_lock, RW_READER);
1260Sstevel@tonic-gate 		for (pcp = hp->pch_forw;
1270Sstevel@tonic-gate 		    pcp != (struct pcnode *)hp; pcp = pcp->pc_forw) {
1280Sstevel@tonic-gate 			if ((fsp == VFSTOPCFS(PCTOV(pcp)->v_vfsp)) &&
1290Sstevel@tonic-gate 			    ((pcp->pc_flags & PC_INVAL) == 0) &&
1300Sstevel@tonic-gate 			    (blkno == pcp->pc_eblkno) &&
1310Sstevel@tonic-gate 			    (offset == pcp->pc_eoffset)) {
1320Sstevel@tonic-gate 				VN_HOLD(PCTOV(pcp));
1330Sstevel@tonic-gate 				rw_exit(&pcnodes_lock);
1340Sstevel@tonic-gate 				return (pcp);
1350Sstevel@tonic-gate 			}
1360Sstevel@tonic-gate 		}
1370Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
1380Sstevel@tonic-gate 	}
1390Sstevel@tonic-gate 	/*
1400Sstevel@tonic-gate 	 * Cannot find node in active list. Allocate memory for a new node
1410Sstevel@tonic-gate 	 * initialize it, and put it on the active list.
1420Sstevel@tonic-gate 	 */
1430Sstevel@tonic-gate 	pcp = kmem_alloc(sizeof (struct pcnode), KM_SLEEP);
1440Sstevel@tonic-gate 	bzero(pcp, sizeof (struct pcnode));
1450Sstevel@tonic-gate 	vp = vn_alloc(KM_SLEEP);
1460Sstevel@tonic-gate 	pcp->pc_vn = vp;
1470Sstevel@tonic-gate 	pcp->pc_entry = *ep;
1480Sstevel@tonic-gate 	pcp->pc_eblkno = blkno;
1490Sstevel@tonic-gate 	pcp->pc_eoffset = offset;
1500Sstevel@tonic-gate 	pcp->pc_scluster = scluster;
1510Sstevel@tonic-gate 	pcp->pc_lcluster = scluster;
1520Sstevel@tonic-gate 	pcp->pc_lindex = 0;
1530Sstevel@tonic-gate 	pcp->pc_flags = 0;
1540Sstevel@tonic-gate 	if (ep->pcd_attr & PCA_DIR) {
1550Sstevel@tonic-gate 		vn_setops(vp, pcfs_dvnodeops);
1560Sstevel@tonic-gate 		vp->v_type = VDIR;
1570Sstevel@tonic-gate 		if (scluster == 0) {
1580Sstevel@tonic-gate 			vp->v_flag = VROOT;
1590Sstevel@tonic-gate 			blkno = offset = 0;
1600Sstevel@tonic-gate 			if (IS_FAT32(fsp)) {
1612972Sfrankho 				pc_cluster32_t ncl = 0;
1622972Sfrankho 
1632972Sfrankho 				scluster = fsp->pcfs_rdirstart;
1642972Sfrankho 				if (pc_fileclsize(fsp, scluster, &ncl)) {
1652972Sfrankho 					PC_DPRINTF1(2, "cluster chain "
1662972Sfrankho 					    "corruption, scluster=%d\n",
1672972Sfrankho 					    scluster);
1682972Sfrankho 					pcp->pc_flags |= PC_INVAL;
1692972Sfrankho 				}
1702972Sfrankho 				pcp->pc_size = fsp->pcfs_clsize * ncl;
1710Sstevel@tonic-gate 			} else {
1720Sstevel@tonic-gate 				pcp->pc_size =
1730Sstevel@tonic-gate 				    fsp->pcfs_rdirsec * fsp->pcfs_secsize;
1740Sstevel@tonic-gate 			}
1752972Sfrankho 		} else {
1762972Sfrankho 			pc_cluster32_t ncl = 0;
1772972Sfrankho 
1782972Sfrankho 			if (pc_fileclsize(fsp, scluster, &ncl)) {
1792972Sfrankho 				PC_DPRINTF1(2, "cluster chain corruption, "
1802972Sfrankho 				    "scluster=%d\n", scluster);
1812972Sfrankho 				pcp->pc_flags |= PC_INVAL;
1822972Sfrankho 			}
1832972Sfrankho 			pcp->pc_size = fsp->pcfs_clsize * ncl;
1842972Sfrankho 		}
1850Sstevel@tonic-gate 	} else {
1860Sstevel@tonic-gate 		vn_setops(vp, pcfs_fvnodeops);
1870Sstevel@tonic-gate 		vp->v_type = VREG;
1880Sstevel@tonic-gate 		vp->v_flag = VNOSWAP;
1890Sstevel@tonic-gate 		fsp->pcfs_frefs++;
1900Sstevel@tonic-gate 		pcp->pc_size = ltohi(ep->pcd_size);
1910Sstevel@tonic-gate 	}
1920Sstevel@tonic-gate 	fsp->pcfs_nrefs++;
1932720Sfrankho 	VFS_HOLD(PCFSTOVFS(fsp));
1940Sstevel@tonic-gate 	vp->v_data = (caddr_t)pcp;
1950Sstevel@tonic-gate 	vp->v_vfsp = PCFSTOVFS(fsp);
1960Sstevel@tonic-gate 	vn_exists(vp);
1970Sstevel@tonic-gate 	rw_enter(&pcnodes_lock, RW_WRITER);
1980Sstevel@tonic-gate 	insque(pcp, hp);
1990Sstevel@tonic-gate 	rw_exit(&pcnodes_lock);
2000Sstevel@tonic-gate 	return (pcp);
2010Sstevel@tonic-gate }
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate int
2040Sstevel@tonic-gate syncpcp(struct pcnode *pcp, int flags)
2050Sstevel@tonic-gate {
2060Sstevel@tonic-gate 	int err;
2070Sstevel@tonic-gate 	if (!vn_has_cached_data(PCTOV(pcp)))
2080Sstevel@tonic-gate 		err = 0;
2090Sstevel@tonic-gate 	else
2104356Scasper 		err = VOP_PUTPAGE(PCTOV(pcp), 0, 0, flags, kcred);
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate 	return (err);
2130Sstevel@tonic-gate }
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate void
2160Sstevel@tonic-gate pc_rele(struct pcnode *pcp)
2170Sstevel@tonic-gate {
2180Sstevel@tonic-gate 	struct pcfs *fsp;
2190Sstevel@tonic-gate 	struct vnode *vp;
2200Sstevel@tonic-gate 	int err;
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 	vp = PCTOV(pcp);
2230Sstevel@tonic-gate 	PC_DPRINTF1(8, "pc_rele vp=0x%p\n", (void *)vp);
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
2260Sstevel@tonic-gate 	ASSERT(fsp->pcfs_flags & PCFS_LOCKED);
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate 	rw_enter(&pcnodes_lock, RW_WRITER);
2290Sstevel@tonic-gate 	pcp->pc_flags |= PC_RELEHOLD;
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate retry:
2320Sstevel@tonic-gate 	if (vp->v_type != VDIR && (pcp->pc_flags & PC_INVAL) == 0) {
2330Sstevel@tonic-gate 		/*
2340Sstevel@tonic-gate 		 * If the file was removed while active it may be safely
2350Sstevel@tonic-gate 		 * truncated now.
2360Sstevel@tonic-gate 		 */
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate 		if (pcp->pc_entry.pcd_filename[0] == PCD_ERASED) {
2390Sstevel@tonic-gate 			(void) pc_truncate(pcp, 0);
2400Sstevel@tonic-gate 		} else if (pcp->pc_flags & PC_CHG) {
2410Sstevel@tonic-gate 			(void) pc_nodeupdate(pcp);
2420Sstevel@tonic-gate 		}
2430Sstevel@tonic-gate 		err = syncpcp(pcp, B_INVAL);
2440Sstevel@tonic-gate 		if (err) {
245*5121Sfrankho 			(void) syncpcp(pcp, B_INVAL | B_FORCE);
2460Sstevel@tonic-gate 		}
2470Sstevel@tonic-gate 	}
2480Sstevel@tonic-gate 	if (vn_has_cached_data(vp)) {
2490Sstevel@tonic-gate 		/*
2500Sstevel@tonic-gate 		 * pvn_vplist_dirty will abort all old pages
2510Sstevel@tonic-gate 		 */
2520Sstevel@tonic-gate 		(void) pvn_vplist_dirty(vp, (u_offset_t)0,
2530Sstevel@tonic-gate 		    pcfs_putapage, B_INVAL, (struct cred *)NULL);
2540Sstevel@tonic-gate 	}
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 	(void) pc_syncfat(fsp);
2570Sstevel@tonic-gate 	mutex_enter(&vp->v_lock);
2580Sstevel@tonic-gate 	if (vn_has_cached_data(vp)) {
2590Sstevel@tonic-gate 		mutex_exit(&vp->v_lock);
2600Sstevel@tonic-gate 		goto retry;
2610Sstevel@tonic-gate 	}
2620Sstevel@tonic-gate 	ASSERT(!vn_has_cached_data(vp));
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate 	vp->v_count--;  /* release our hold from vn_rele */
2650Sstevel@tonic-gate 	if (vp->v_count > 0) { /* Is this check still needed? */
2660Sstevel@tonic-gate 		PC_DPRINTF1(3, "pc_rele: pcp=0x%p HELD AGAIN!\n", (void *)pcp);
2670Sstevel@tonic-gate 		mutex_exit(&vp->v_lock);
2680Sstevel@tonic-gate 		pcp->pc_flags &= ~PC_RELEHOLD;
2690Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
2700Sstevel@tonic-gate 		return;
2710Sstevel@tonic-gate 	}
2720Sstevel@tonic-gate 
2730Sstevel@tonic-gate 	remque(pcp);
2740Sstevel@tonic-gate 	rw_exit(&pcnodes_lock);
2752972Sfrankho 	/*
2762972Sfrankho 	 * XXX - old code had a check for !(pcp->pc_flags & PC_INVAL)
2772972Sfrankho 	 * here. Seems superfluous/incorrect, but then earlier on PC_INVAL
2782972Sfrankho 	 * was never set anywhere in PCFS. Now it is, and we _have_ to drop
2792972Sfrankho 	 * the file reference here. Else, we'd screw up umount/modunload.
2802972Sfrankho 	 */
2812972Sfrankho 	if ((vp->v_type == VREG)) {
2820Sstevel@tonic-gate 		fsp->pcfs_frefs--;
2830Sstevel@tonic-gate 	}
2840Sstevel@tonic-gate 	fsp->pcfs_nrefs--;
2852720Sfrankho 	VFS_RELE(vp->v_vfsp);
2860Sstevel@tonic-gate 
2870Sstevel@tonic-gate 	if (fsp->pcfs_nrefs < 0) {
2880Sstevel@tonic-gate 		panic("pc_rele: nrefs count");
2890Sstevel@tonic-gate 	}
2900Sstevel@tonic-gate 	if (fsp->pcfs_frefs < 0) {
2910Sstevel@tonic-gate 		panic("pc_rele: frefs count");
2920Sstevel@tonic-gate 	}
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 	mutex_exit(&vp->v_lock);
2950Sstevel@tonic-gate 	vn_invalid(vp);
2960Sstevel@tonic-gate 	vn_free(vp);
2970Sstevel@tonic-gate 	kmem_free(pcp, sizeof (struct pcnode));
2980Sstevel@tonic-gate }
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate /*
3010Sstevel@tonic-gate  * Mark a pcnode as modified with the current time.
3020Sstevel@tonic-gate  */
303*5121Sfrankho /* ARGSUSED */
3040Sstevel@tonic-gate void
305*5121Sfrankho pc_mark_mod(struct pcfs *fsp, struct pcnode *pcp)
3060Sstevel@tonic-gate {
3070Sstevel@tonic-gate 	timestruc_t now;
3080Sstevel@tonic-gate 
309*5121Sfrankho 	if (PCTOV(pcp)->v_type == VDIR)
310*5121Sfrankho 		return;
311*5121Sfrankho 
312*5121Sfrankho 	ASSERT(PCTOV(pcp)->v_type == VREG);
313*5121Sfrankho 
314*5121Sfrankho 	gethrestime(&now);
315*5121Sfrankho 	if (pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime))
316*5121Sfrankho 		PC_DPRINTF1(2, "pc_mark_mod failed timestamp "
317*5121Sfrankho 		    "conversion, curtime = %lld\n",
318*5121Sfrankho 		    (long long)now.tv_sec);
319*5121Sfrankho 
320*5121Sfrankho 	pcp->pc_flags |= PC_CHG;
3210Sstevel@tonic-gate }
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate /*
3240Sstevel@tonic-gate  * Mark a pcnode as accessed with the current time.
3250Sstevel@tonic-gate  */
3260Sstevel@tonic-gate void
327*5121Sfrankho pc_mark_acc(struct pcfs *fsp, struct pcnode *pcp)
3280Sstevel@tonic-gate {
3292720Sfrankho 	struct pctime pt = { 0, 0 };
3300Sstevel@tonic-gate 	timestruc_t now;
3310Sstevel@tonic-gate 
332*5121Sfrankho 	if (fsp->pcfs_flags & PCFS_NOATIME || PCTOV(pcp)->v_type == VDIR)
333*5121Sfrankho 		return;
334*5121Sfrankho 
335*5121Sfrankho 	ASSERT(PCTOV(pcp)->v_type == VREG);
336*5121Sfrankho 
337*5121Sfrankho 	gethrestime(&now);
338*5121Sfrankho 	if (pc_tvtopct(&now, &pt)) {
339*5121Sfrankho 		PC_DPRINTF1(2, "pc_mark_acc failed timestamp "
340*5121Sfrankho 		    "conversion, curtime = %lld\n",
341*5121Sfrankho 		    (long long)now.tv_sec);
342*5121Sfrankho 		return;
343*5121Sfrankho 	}
344*5121Sfrankho 
345*5121Sfrankho 	/*
346*5121Sfrankho 	 * We don't really want to write the adate for every access
347*5121Sfrankho 	 * on flash media; make sure it really changed !
348*5121Sfrankho 	 */
349*5121Sfrankho 	if (pcp->pc_entry.pcd_ladate != pt.pct_date) {
3500Sstevel@tonic-gate 		pcp->pc_entry.pcd_ladate = pt.pct_date;
351*5121Sfrankho 		pcp->pc_flags |= (PC_CHG | PC_ACC);
3520Sstevel@tonic-gate 	}
3530Sstevel@tonic-gate }
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate /*
3560Sstevel@tonic-gate  * Truncate a file to a length.
3570Sstevel@tonic-gate  * Node must be locked.
3580Sstevel@tonic-gate  */
3590Sstevel@tonic-gate int
3600Sstevel@tonic-gate pc_truncate(struct pcnode *pcp, uint_t length)
3610Sstevel@tonic-gate {
3620Sstevel@tonic-gate 	struct pcfs *fsp;
3630Sstevel@tonic-gate 	struct vnode *vp;
3640Sstevel@tonic-gate 	int error = 0;
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate 	PC_DPRINTF3(4, "pc_truncate pcp=0x%p, len=%u, size=%u\n",
3670Sstevel@tonic-gate 	    (void *)pcp, length, pcp->pc_size);
3680Sstevel@tonic-gate 	vp = PCTOV(pcp);
3690Sstevel@tonic-gate 	if (pcp->pc_flags & PC_INVAL)
3700Sstevel@tonic-gate 		return (EIO);
3710Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
3720Sstevel@tonic-gate 	/*
3730Sstevel@tonic-gate 	 * directories are always truncated to zero and are not marked
3740Sstevel@tonic-gate 	 */
3750Sstevel@tonic-gate 	if (vp->v_type == VDIR) {
3760Sstevel@tonic-gate 		error = pc_bfree(pcp, 0);
3770Sstevel@tonic-gate 		return (error);
3780Sstevel@tonic-gate 	}
3790Sstevel@tonic-gate 	/*
3800Sstevel@tonic-gate 	 * If length is the same as the current size
3810Sstevel@tonic-gate 	 * just mark the pcnode and return.
3820Sstevel@tonic-gate 	 */
3830Sstevel@tonic-gate 	if (length > pcp->pc_size) {
3840Sstevel@tonic-gate 		daddr_t bno;
385*5121Sfrankho 		uint_t llcn = howmany((offset_t)length, fsp->pcfs_clsize);
3860Sstevel@tonic-gate 
3870Sstevel@tonic-gate 		/*
3880Sstevel@tonic-gate 		 * We are extending a file.
3890Sstevel@tonic-gate 		 * Extend it with _one_ call to pc_balloc (no holes)
3900Sstevel@tonic-gate 		 * since we don't need to use the block number(s).
3910Sstevel@tonic-gate 		 */
3920Sstevel@tonic-gate 		if ((daddr_t)howmany((offset_t)pcp->pc_size, fsp->pcfs_clsize) <
393*5121Sfrankho 		    (daddr_t)llcn) {
3940Sstevel@tonic-gate 			error = pc_balloc(pcp, (daddr_t)(llcn - 1), 1, &bno);
3950Sstevel@tonic-gate 		}
3960Sstevel@tonic-gate 		if (error) {
3972972Sfrankho 			pc_cluster32_t ncl = 0;
3980Sstevel@tonic-gate 			PC_DPRINTF1(2, "pc_truncate: error=%d\n", error);
3990Sstevel@tonic-gate 			/*
4000Sstevel@tonic-gate 			 * probably ran out disk space;
4010Sstevel@tonic-gate 			 * determine current file size
4020Sstevel@tonic-gate 			 */
4032972Sfrankho 			if (pc_fileclsize(fsp, pcp->pc_scluster, &ncl)) {
4042972Sfrankho 				PC_DPRINTF1(2, "cluster chain corruption, "
4052972Sfrankho 				    "scluster=%d\n", pcp->pc_scluster);
4062972Sfrankho 				pcp->pc_flags |= PC_INVAL;
4072972Sfrankho 			}
4082972Sfrankho 			pcp->pc_size = fsp->pcfs_clsize * ncl;
4090Sstevel@tonic-gate 		} else
4100Sstevel@tonic-gate 			pcp->pc_size = length;
4110Sstevel@tonic-gate 
4120Sstevel@tonic-gate 	} else if (length < pcp->pc_size) {
4130Sstevel@tonic-gate 		/*
4140Sstevel@tonic-gate 		 * We are shrinking a file.
4150Sstevel@tonic-gate 		 * Free blocks after the block that length points to.
4160Sstevel@tonic-gate 		 */
4170Sstevel@tonic-gate 		if (pc_blkoff(fsp, length) == 0) {
4180Sstevel@tonic-gate 			/*
4190Sstevel@tonic-gate 			 * Truncation to a block (cluster size) boundary only
4200Sstevel@tonic-gate 			 * requires us to invalidate everything after the new
4210Sstevel@tonic-gate 			 * end of the file.
4220Sstevel@tonic-gate 			 */
4230Sstevel@tonic-gate 			(void) pvn_vplist_dirty(PCTOV(pcp), (u_offset_t)length,
424*5121Sfrankho 			    pcfs_putapage, B_INVAL | B_TRUNC, CRED());
4250Sstevel@tonic-gate 		} else {
4260Sstevel@tonic-gate 			/*
4270Sstevel@tonic-gate 			 * pvn_vpzero() cannot deal with more than MAXBSIZE
4280Sstevel@tonic-gate 			 * chunks. Since the FAT clustersize can get larger
4290Sstevel@tonic-gate 			 * than that, we'll zero from the new length to the
4300Sstevel@tonic-gate 			 * end of the cluster for clustersizes smaller than
4310Sstevel@tonic-gate 			 * MAXBSIZE - or the end of the MAXBSIZE block in
4320Sstevel@tonic-gate 			 * case we've got a large clustersize.
4330Sstevel@tonic-gate 			 */
4340Sstevel@tonic-gate 			size_t nbytes =
4350Sstevel@tonic-gate 			    roundup(length, MIN(fsp->pcfs_clsize, MAXBSIZE)) -
4360Sstevel@tonic-gate 			    length;
4370Sstevel@tonic-gate 
4380Sstevel@tonic-gate 			pvn_vpzero(PCTOV(pcp), (u_offset_t)length, nbytes);
4390Sstevel@tonic-gate 			(void) pvn_vplist_dirty(PCTOV(pcp),
4400Sstevel@tonic-gate 			    (u_offset_t)length + nbytes,
4410Sstevel@tonic-gate 			    pcfs_putapage, B_INVAL | B_TRUNC, CRED());
4420Sstevel@tonic-gate 		}
443*5121Sfrankho 		error = pc_bfree(pcp, (pc_cluster32_t)
444*5121Sfrankho 		    howmany((offset_t)length, fsp->pcfs_clsize));
4450Sstevel@tonic-gate 		pcp->pc_size = length;
4460Sstevel@tonic-gate 	}
447*5121Sfrankho 
448*5121Sfrankho 	/*
449*5121Sfrankho 	 * This is the only place in PCFS code where pc_mark_mod() is called
450*5121Sfrankho 	 * without setting PC_MOD. May be a historical artifact ...
451*5121Sfrankho 	 */
452*5121Sfrankho 	pc_mark_mod(fsp, pcp);
4530Sstevel@tonic-gate 	return (error);
4540Sstevel@tonic-gate }
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate /*
4570Sstevel@tonic-gate  * Get block for entry.
4580Sstevel@tonic-gate  */
4590Sstevel@tonic-gate static int
4600Sstevel@tonic-gate pc_getentryblock(struct pcnode *pcp, struct buf **bpp)
4610Sstevel@tonic-gate {
4620Sstevel@tonic-gate 	struct pcfs *fsp;
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 	fsp = VFSTOPCFS(PCTOV(pcp)->v_vfsp);
4650Sstevel@tonic-gate 	if (pcp->pc_eblkno >= fsp->pcfs_datastart ||
4660Sstevel@tonic-gate 	    (pcp->pc_eblkno - fsp->pcfs_rdirstart) <
4670Sstevel@tonic-gate 	    (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
4680Sstevel@tonic-gate 		*bpp = bread(fsp->pcfs_xdev,
4690Sstevel@tonic-gate 		    pc_dbdaddr(fsp, pcp->pc_eblkno), fsp->pcfs_clsize);
4700Sstevel@tonic-gate 	} else {
4710Sstevel@tonic-gate 		*bpp = bread(fsp->pcfs_xdev,
4720Sstevel@tonic-gate 		    pc_dbdaddr(fsp, pcp->pc_eblkno),
473*5121Sfrankho 		    (int)(fsp->pcfs_datastart - pcp->pc_eblkno) *
4740Sstevel@tonic-gate 		    fsp->pcfs_secsize);
4750Sstevel@tonic-gate 	}
4760Sstevel@tonic-gate 	if ((*bpp)->b_flags & B_ERROR) {
4770Sstevel@tonic-gate 		brelse(*bpp);
4780Sstevel@tonic-gate 		pc_mark_irrecov(fsp);
4790Sstevel@tonic-gate 		return (EIO);
4800Sstevel@tonic-gate 	}
4810Sstevel@tonic-gate 	return (0);
4820Sstevel@tonic-gate }
4830Sstevel@tonic-gate 
4840Sstevel@tonic-gate /*
4850Sstevel@tonic-gate  * Sync all data associated with a file.
4860Sstevel@tonic-gate  * Flush all the blocks in the buffer cache out to disk, sync the FAT and
4870Sstevel@tonic-gate  * update the directory entry.
4880Sstevel@tonic-gate  */
4890Sstevel@tonic-gate int
4900Sstevel@tonic-gate pc_nodesync(struct pcnode *pcp)
4910Sstevel@tonic-gate {
4920Sstevel@tonic-gate 	struct pcfs *fsp;
4930Sstevel@tonic-gate 	int err;
4940Sstevel@tonic-gate 	struct vnode *vp;
4950Sstevel@tonic-gate 
4960Sstevel@tonic-gate 	vp = PCTOV(pcp);
4970Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
4980Sstevel@tonic-gate 	err = 0;
4990Sstevel@tonic-gate 	if (pcp->pc_flags & PC_MOD) {
5000Sstevel@tonic-gate 		/*
5010Sstevel@tonic-gate 		 * Flush all data blocks from buffer cache and
5020Sstevel@tonic-gate 		 * update the FAT which points to the data.
5030Sstevel@tonic-gate 		 */
5040Sstevel@tonic-gate 		if (err = syncpcp(pcp, 0)) { /* %% ?? how to handle error? */
5050Sstevel@tonic-gate 			if (err == ENOMEM)
5060Sstevel@tonic-gate 				return (err);
5070Sstevel@tonic-gate 			else {
5080Sstevel@tonic-gate 				pc_mark_irrecov(fsp);
5090Sstevel@tonic-gate 				return (EIO);
5100Sstevel@tonic-gate 			}
5110Sstevel@tonic-gate 		}
5120Sstevel@tonic-gate 		pcp->pc_flags &= ~PC_MOD;
5130Sstevel@tonic-gate 	}
5140Sstevel@tonic-gate 	/*
5150Sstevel@tonic-gate 	 * update the directory entry
5160Sstevel@tonic-gate 	 */
5170Sstevel@tonic-gate 	if (pcp->pc_flags & PC_CHG)
5180Sstevel@tonic-gate 		(void) pc_nodeupdate(pcp);
5190Sstevel@tonic-gate 	return (err);
5200Sstevel@tonic-gate }
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate /*
5230Sstevel@tonic-gate  * Update the node's directory entry.
5240Sstevel@tonic-gate  */
5250Sstevel@tonic-gate int
5260Sstevel@tonic-gate pc_nodeupdate(struct pcnode *pcp)
5270Sstevel@tonic-gate {
5280Sstevel@tonic-gate 	struct buf *bp;
5290Sstevel@tonic-gate 	int error;
5300Sstevel@tonic-gate 	struct vnode *vp;
5310Sstevel@tonic-gate 	struct pcfs *fsp;
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate 	vp = PCTOV(pcp);
5340Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
5350Sstevel@tonic-gate 	if (IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
5360Sstevel@tonic-gate 		/* no node to update */
5370Sstevel@tonic-gate 		pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC);
5380Sstevel@tonic-gate 		return (0);
5390Sstevel@tonic-gate 	}
5400Sstevel@tonic-gate 	if (vp->v_flag & VROOT) {
5410Sstevel@tonic-gate 		panic("pc_nodeupdate");
5420Sstevel@tonic-gate 	}
5430Sstevel@tonic-gate 	if (pcp->pc_flags & PC_INVAL)
5440Sstevel@tonic-gate 		return (0);
5450Sstevel@tonic-gate 	PC_DPRINTF3(7, "pc_nodeupdate pcp=0x%p, bn=%ld, off=%d\n", (void *)pcp,
5460Sstevel@tonic-gate 	    pcp->pc_eblkno, pcp->pc_eoffset);
5470Sstevel@tonic-gate 
5480Sstevel@tonic-gate 	if (error = pc_getentryblock(pcp, &bp)) {
5490Sstevel@tonic-gate 		return (error);
5500Sstevel@tonic-gate 	}
5510Sstevel@tonic-gate 	if (vp->v_type == VREG) {
5520Sstevel@tonic-gate 		if (pcp->pc_flags & PC_CHG)
5530Sstevel@tonic-gate 			pcp->pc_entry.pcd_attr |= PCA_ARCH;
5540Sstevel@tonic-gate 		pcp->pc_entry.pcd_size = htoli(pcp->pc_size);
5550Sstevel@tonic-gate 	}
5560Sstevel@tonic-gate 	pc_setstartcluster(fsp, &pcp->pc_entry, pcp->pc_scluster);
5570Sstevel@tonic-gate 	*((struct pcdir *)(bp->b_un.b_addr + pcp->pc_eoffset)) = pcp->pc_entry;
5580Sstevel@tonic-gate 	bwrite2(bp);
5590Sstevel@tonic-gate 	error = geterror(bp);
5600Sstevel@tonic-gate 	brelse(bp);
5610Sstevel@tonic-gate 	if (error) {
562*5121Sfrankho 		error = EIO;
5630Sstevel@tonic-gate 		pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
5640Sstevel@tonic-gate 	}
5650Sstevel@tonic-gate 	pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC);
5660Sstevel@tonic-gate 	return (error);
5670Sstevel@tonic-gate }
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate /*
5700Sstevel@tonic-gate  * Verify that the disk in the drive is the same one that we
5710Sstevel@tonic-gate  * got the pcnode from.
5720Sstevel@tonic-gate  * MUST be called with node unlocked.
5730Sstevel@tonic-gate  */
5740Sstevel@tonic-gate int
5750Sstevel@tonic-gate pc_verify(struct pcfs *fsp)
5760Sstevel@tonic-gate {
5770Sstevel@tonic-gate 	int fdstatus = 0;
5780Sstevel@tonic-gate 	int error = 0;
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 	if (!fsp || fsp->pcfs_flags & PCFS_IRRECOV)
5810Sstevel@tonic-gate 		return (EIO);
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 	if (!(fsp->pcfs_flags & PCFS_NOCHK) && fsp->pcfs_fatp) {
584*5121Sfrankho 		/*
585*5121Sfrankho 		 * This "has it been removed" check should better be
586*5121Sfrankho 		 * modified for removeable media that are not floppies.
587*5121Sfrankho 		 * dkio-managed devices such as USB/firewire external
588*5121Sfrankho 		 * disks/memory sticks/floppies (gasp) do not understand
589*5121Sfrankho 		 * this ioctl.
590*5121Sfrankho 		 */
5910Sstevel@tonic-gate 		PC_DPRINTF1(4, "pc_verify fsp=0x%p\n", (void *)fsp);
5920Sstevel@tonic-gate 		error = cdev_ioctl(fsp->pcfs_vfs->vfs_dev,
593*5121Sfrankho 		    FDGETCHANGE, (intptr_t)&fdstatus, FNATIVE | FKIOCTL,
5940Sstevel@tonic-gate 		    NULL, NULL);
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate 		if (error) {
5970Sstevel@tonic-gate 			if (error == ENOTTY || error == ENXIO) {
598*5121Sfrankho 				/*
599*5121Sfrankho 				 * See comment above. This is a workaround
600*5121Sfrankho 				 * for removeable media that don't understand
601*5121Sfrankho 				 * floppy ioctls.
602*5121Sfrankho 				 */
6030Sstevel@tonic-gate 				error = 0;
6040Sstevel@tonic-gate 			} else {
6050Sstevel@tonic-gate 				PC_DPRINTF1(1,
6060Sstevel@tonic-gate 				    "pc_verify: FDGETCHANGE ioctl failed: %d\n",
6070Sstevel@tonic-gate 				    error);
6080Sstevel@tonic-gate 				pc_mark_irrecov(fsp);
6090Sstevel@tonic-gate 			}
6100Sstevel@tonic-gate 		} else if (fsp->pcfs_fatjustread) {
6110Sstevel@tonic-gate 			/*
6120Sstevel@tonic-gate 			 * Ignore the results of the ioctl if we just
6130Sstevel@tonic-gate 			 * read the FAT.  There is a good chance that
6140Sstevel@tonic-gate 			 * the disk changed bit will be on, because
6150Sstevel@tonic-gate 			 * we've just mounted and we don't want to
6160Sstevel@tonic-gate 			 * give a false positive that the sky is falling.
6170Sstevel@tonic-gate 			 */
6180Sstevel@tonic-gate 			fsp->pcfs_fatjustread = 0;
6190Sstevel@tonic-gate 		} else {
6200Sstevel@tonic-gate 			/*
6210Sstevel@tonic-gate 			 * Oddly enough we can't check just one flag here. The
6220Sstevel@tonic-gate 			 * x86 floppy driver sets a different flag
6230Sstevel@tonic-gate 			 * (FDGC_DETECTED) than the sparc driver does.
6240Sstevel@tonic-gate 			 * I think this MAY be a bug, and I filed 4165938
6250Sstevel@tonic-gate 			 * to get someone to look at the behavior
6260Sstevel@tonic-gate 			 * a bit more closely.  In the meantime, my testing and
6270Sstevel@tonic-gate 			 * code examination seem to indicate it is safe to
6280Sstevel@tonic-gate 			 * check for either bit being set.
6290Sstevel@tonic-gate 			 */
6300Sstevel@tonic-gate 			if (fdstatus & (FDGC_HISTORY | FDGC_DETECTED)) {
6310Sstevel@tonic-gate 				PC_DPRINTF0(1, "pc_verify: change detected\n");
6320Sstevel@tonic-gate 				pc_mark_irrecov(fsp);
6330Sstevel@tonic-gate 			}
6340Sstevel@tonic-gate 		}
6350Sstevel@tonic-gate 	}
636*5121Sfrankho 	if (error == 0 && fsp->pcfs_fatp == NULL) {
6370Sstevel@tonic-gate 		error = pc_getfat(fsp);
6380Sstevel@tonic-gate 	}
6390Sstevel@tonic-gate 
6400Sstevel@tonic-gate 	return (error);
6410Sstevel@tonic-gate }
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate /*
6440Sstevel@tonic-gate  * The disk has changed, pulling the rug out from beneath us.
6450Sstevel@tonic-gate  * Mark the FS as being in an irrecoverable state.
6460Sstevel@tonic-gate  * In a short while we'll clean up.
6470Sstevel@tonic-gate  */
6480Sstevel@tonic-gate void
6490Sstevel@tonic-gate pc_mark_irrecov(struct pcfs *fsp)
6500Sstevel@tonic-gate {
6510Sstevel@tonic-gate 	if (!(fsp->pcfs_flags & PCFS_NOCHK)) {
6520Sstevel@tonic-gate 		if (pc_lockfs(fsp, 1, 0)) {
6530Sstevel@tonic-gate 			/*
6540Sstevel@tonic-gate 			 * Locking failed, which currently would
6550Sstevel@tonic-gate 			 * only happen if the FS were already
6560Sstevel@tonic-gate 			 * marked as hosed.  If another reason for
6570Sstevel@tonic-gate 			 * failure were to arise in the future, this
6580Sstevel@tonic-gate 			 * routine would have to change.
6590Sstevel@tonic-gate 			 */
6600Sstevel@tonic-gate 			return;
6610Sstevel@tonic-gate 		}
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_IRRECOV;
6640Sstevel@tonic-gate 		cmn_err(CE_WARN,
665*5121Sfrankho 		    "Disk was changed during an update or\n"
666*5121Sfrankho 		    "an irrecoverable error was encountered.\n"
667*5121Sfrankho 		    "File damage is possible.  To prevent further\n"
668*5121Sfrankho 		    "damage, this pcfs instance will now be frozen.\n"
669*5121Sfrankho 		    "Use umount(1M) to release the instance.\n");
6700Sstevel@tonic-gate 		(void) pc_unlockfs(fsp);
6710Sstevel@tonic-gate 	}
6720Sstevel@tonic-gate }
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate /*
6750Sstevel@tonic-gate  * The disk has been changed!
6760Sstevel@tonic-gate  */
6770Sstevel@tonic-gate void
6780Sstevel@tonic-gate pc_diskchanged(struct pcfs *fsp)
6790Sstevel@tonic-gate {
6802720Sfrankho 	struct pcnode	*pcp, *npcp = NULL;
6812720Sfrankho 	struct pchead	*hp;
6822720Sfrankho 	struct vnode	*vp;
6832720Sfrankho 	extern vfs_t	EIO_vfs;
6842720Sfrankho 	struct vfs	*vfsp;
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate 	/*
6870Sstevel@tonic-gate 	 * Eliminate all pcnodes (dir & file) associated with this fs.
6880Sstevel@tonic-gate 	 * If the node is internal, ie, no references outside of
6890Sstevel@tonic-gate 	 * pcfs itself, then release the associated vnode structure.
6900Sstevel@tonic-gate 	 * Invalidate the in core FAT.
6910Sstevel@tonic-gate 	 * Invalidate cached data blocks and blocks waiting for I/O.
6920Sstevel@tonic-gate 	 */
6930Sstevel@tonic-gate 	PC_DPRINTF1(1, "pc_diskchanged fsp=0x%p\n", (void *)fsp);
6940Sstevel@tonic-gate 
6952720Sfrankho 	vfsp = PCFSTOVFS(fsp);
6962720Sfrankho 
6970Sstevel@tonic-gate 	for (hp = pcdhead; hp < &pcdhead[NPCHASH]; hp++) {
6980Sstevel@tonic-gate 		for (pcp = hp->pch_forw;
6990Sstevel@tonic-gate 		    pcp != (struct pcnode *)hp; pcp = npcp) {
7000Sstevel@tonic-gate 			npcp = pcp -> pc_forw;
7010Sstevel@tonic-gate 			vp = PCTOV(pcp);
7022720Sfrankho 			if ((vp->v_vfsp == vfsp) &&
7030Sstevel@tonic-gate 			    !(pcp->pc_flags & PC_RELEHOLD)) {
7040Sstevel@tonic-gate 				mutex_enter(&(vp)->v_lock);
7050Sstevel@tonic-gate 				if (vp->v_count > 0) {
7060Sstevel@tonic-gate 					mutex_exit(&(vp)->v_lock);
7070Sstevel@tonic-gate 					continue;
7080Sstevel@tonic-gate 				}
7090Sstevel@tonic-gate 				mutex_exit(&(vp)->v_lock);
7100Sstevel@tonic-gate 				VN_HOLD(vp);
7110Sstevel@tonic-gate 				remque(pcp);
7120Sstevel@tonic-gate 				vp->v_data = NULL;
7130Sstevel@tonic-gate 				vp->v_vfsp = &EIO_vfs;
7140Sstevel@tonic-gate 				vp->v_type = VBAD;
7150Sstevel@tonic-gate 				VN_RELE(vp);
7162720Sfrankho 				if (!(pcp->pc_flags & PC_EXTERNAL)) {
7172720Sfrankho 					(void) pvn_vplist_dirty(vp,
7182720Sfrankho 					    (u_offset_t)0, pcfs_putapage,
7192720Sfrankho 					    B_INVAL | B_TRUNC,
7202720Sfrankho 					    (struct cred *)NULL);
7210Sstevel@tonic-gate 					vn_free(vp);
7222720Sfrankho 				}
7230Sstevel@tonic-gate 				kmem_free(pcp, sizeof (struct pcnode));
7240Sstevel@tonic-gate 				fsp->pcfs_nrefs --;
7252720Sfrankho 				VFS_RELE(vfsp);
7260Sstevel@tonic-gate 			}
7270Sstevel@tonic-gate 		}
7280Sstevel@tonic-gate 	}
7290Sstevel@tonic-gate 	for (hp = pcfhead; fsp->pcfs_frefs && hp < &pcfhead[NPCHASH]; hp++) {
7300Sstevel@tonic-gate 		for (pcp = hp->pch_forw; fsp->pcfs_frefs &&
7310Sstevel@tonic-gate 		    pcp != (struct pcnode *)hp; pcp = npcp) {
7320Sstevel@tonic-gate 			npcp = pcp -> pc_forw;
7330Sstevel@tonic-gate 			vp = PCTOV(pcp);
7342720Sfrankho 			if ((vp->v_vfsp == vfsp) &&
7350Sstevel@tonic-gate 			    !(pcp->pc_flags & PC_RELEHOLD)) {
7360Sstevel@tonic-gate 				mutex_enter(&(vp)->v_lock);
7370Sstevel@tonic-gate 				if (vp->v_count > 0) {
7380Sstevel@tonic-gate 					mutex_exit(&(vp)->v_lock);
7390Sstevel@tonic-gate 					continue;
7400Sstevel@tonic-gate 				}
7410Sstevel@tonic-gate 				mutex_exit(&(vp)->v_lock);
7420Sstevel@tonic-gate 				VN_HOLD(vp);
7430Sstevel@tonic-gate 				remque(pcp);
7440Sstevel@tonic-gate 				vp->v_data = NULL;
7450Sstevel@tonic-gate 				vp->v_vfsp = &EIO_vfs;
7460Sstevel@tonic-gate 				vp->v_type = VBAD;
7470Sstevel@tonic-gate 				VN_RELE(vp);
7482720Sfrankho 				if (!(pcp->pc_flags & PC_EXTERNAL)) {
7492720Sfrankho 					(void) pvn_vplist_dirty(vp,
7502720Sfrankho 					    (u_offset_t)0, pcfs_putapage,
7512720Sfrankho 					    B_INVAL | B_TRUNC,
7522720Sfrankho 					    (struct cred *)NULL);
7530Sstevel@tonic-gate 					vn_free(vp);
7542720Sfrankho 				}
7550Sstevel@tonic-gate 				kmem_free(pcp, sizeof (struct pcnode));
7562972Sfrankho 				fsp->pcfs_frefs--;
7572972Sfrankho 				fsp->pcfs_nrefs--;
7582720Sfrankho 				VFS_RELE(vfsp);
7590Sstevel@tonic-gate 			}
7600Sstevel@tonic-gate 		}
7610Sstevel@tonic-gate 	}
7620Sstevel@tonic-gate #ifdef undef
7630Sstevel@tonic-gate 	if (fsp->pcfs_frefs) {
7640Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
7650Sstevel@tonic-gate 		panic("pc_diskchanged: frefs");
7660Sstevel@tonic-gate 	}
7670Sstevel@tonic-gate 	if (fsp->pcfs_nrefs) {
7680Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
7690Sstevel@tonic-gate 		panic("pc_diskchanged: nrefs");
7700Sstevel@tonic-gate 	}
7710Sstevel@tonic-gate #endif
7722720Sfrankho 	if (!(vfsp->vfs_flag & VFS_UNMOUNTED) &&
7732720Sfrankho 	    fsp->pcfs_fatp != (uchar_t *)0) {
7740Sstevel@tonic-gate 		pc_invalfat(fsp);
7750Sstevel@tonic-gate 	} else {
7760Sstevel@tonic-gate 		binval(fsp->pcfs_xdev);
7770Sstevel@tonic-gate 	}
7780Sstevel@tonic-gate }
779