xref: /onnv-gate/usr/src/uts/common/fs/pcfs/pc_node.c (revision 5331:3047ad28a67b)
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
pc_init(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 *
pc_getnode(struct pcfs * fsp,daddr_t blkno,int offset,struct pcdir * ep)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
syncpcp(struct pcnode * pcp,int flags)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
210*5331Samw 		err = VOP_PUTPAGE(PCTOV(pcp), 0, 0, flags,
211*5331Samw 		    kcred, NULL);
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate 	return (err);
2140Sstevel@tonic-gate }
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate void
pc_rele(struct pcnode * pcp)2170Sstevel@tonic-gate pc_rele(struct pcnode *pcp)
2180Sstevel@tonic-gate {
2190Sstevel@tonic-gate 	struct pcfs *fsp;
2200Sstevel@tonic-gate 	struct vnode *vp;
2210Sstevel@tonic-gate 	int err;
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate 	vp = PCTOV(pcp);
2240Sstevel@tonic-gate 	PC_DPRINTF1(8, "pc_rele vp=0x%p\n", (void *)vp);
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
2270Sstevel@tonic-gate 	ASSERT(fsp->pcfs_flags & PCFS_LOCKED);
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 	rw_enter(&pcnodes_lock, RW_WRITER);
2300Sstevel@tonic-gate 	pcp->pc_flags |= PC_RELEHOLD;
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate retry:
2330Sstevel@tonic-gate 	if (vp->v_type != VDIR && (pcp->pc_flags & PC_INVAL) == 0) {
2340Sstevel@tonic-gate 		/*
2350Sstevel@tonic-gate 		 * If the file was removed while active it may be safely
2360Sstevel@tonic-gate 		 * truncated now.
2370Sstevel@tonic-gate 		 */
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 		if (pcp->pc_entry.pcd_filename[0] == PCD_ERASED) {
2400Sstevel@tonic-gate 			(void) pc_truncate(pcp, 0);
2410Sstevel@tonic-gate 		} else if (pcp->pc_flags & PC_CHG) {
2420Sstevel@tonic-gate 			(void) pc_nodeupdate(pcp);
2430Sstevel@tonic-gate 		}
2440Sstevel@tonic-gate 		err = syncpcp(pcp, B_INVAL);
2450Sstevel@tonic-gate 		if (err) {
2465121Sfrankho 			(void) syncpcp(pcp, B_INVAL | B_FORCE);
2470Sstevel@tonic-gate 		}
2480Sstevel@tonic-gate 	}
2490Sstevel@tonic-gate 	if (vn_has_cached_data(vp)) {
2500Sstevel@tonic-gate 		/*
2510Sstevel@tonic-gate 		 * pvn_vplist_dirty will abort all old pages
2520Sstevel@tonic-gate 		 */
2530Sstevel@tonic-gate 		(void) pvn_vplist_dirty(vp, (u_offset_t)0,
2540Sstevel@tonic-gate 		    pcfs_putapage, B_INVAL, (struct cred *)NULL);
2550Sstevel@tonic-gate 	}
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	(void) pc_syncfat(fsp);
2580Sstevel@tonic-gate 	mutex_enter(&vp->v_lock);
2590Sstevel@tonic-gate 	if (vn_has_cached_data(vp)) {
2600Sstevel@tonic-gate 		mutex_exit(&vp->v_lock);
2610Sstevel@tonic-gate 		goto retry;
2620Sstevel@tonic-gate 	}
2630Sstevel@tonic-gate 	ASSERT(!vn_has_cached_data(vp));
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	vp->v_count--;  /* release our hold from vn_rele */
2660Sstevel@tonic-gate 	if (vp->v_count > 0) { /* Is this check still needed? */
2670Sstevel@tonic-gate 		PC_DPRINTF1(3, "pc_rele: pcp=0x%p HELD AGAIN!\n", (void *)pcp);
2680Sstevel@tonic-gate 		mutex_exit(&vp->v_lock);
2690Sstevel@tonic-gate 		pcp->pc_flags &= ~PC_RELEHOLD;
2700Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
2710Sstevel@tonic-gate 		return;
2720Sstevel@tonic-gate 	}
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 	remque(pcp);
2750Sstevel@tonic-gate 	rw_exit(&pcnodes_lock);
2762972Sfrankho 	/*
2772972Sfrankho 	 * XXX - old code had a check for !(pcp->pc_flags & PC_INVAL)
2782972Sfrankho 	 * here. Seems superfluous/incorrect, but then earlier on PC_INVAL
2792972Sfrankho 	 * was never set anywhere in PCFS. Now it is, and we _have_ to drop
2802972Sfrankho 	 * the file reference here. Else, we'd screw up umount/modunload.
2812972Sfrankho 	 */
2822972Sfrankho 	if ((vp->v_type == VREG)) {
2830Sstevel@tonic-gate 		fsp->pcfs_frefs--;
2840Sstevel@tonic-gate 	}
2850Sstevel@tonic-gate 	fsp->pcfs_nrefs--;
2862720Sfrankho 	VFS_RELE(vp->v_vfsp);
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	if (fsp->pcfs_nrefs < 0) {
2890Sstevel@tonic-gate 		panic("pc_rele: nrefs count");
2900Sstevel@tonic-gate 	}
2910Sstevel@tonic-gate 	if (fsp->pcfs_frefs < 0) {
2920Sstevel@tonic-gate 		panic("pc_rele: frefs count");
2930Sstevel@tonic-gate 	}
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate 	mutex_exit(&vp->v_lock);
2960Sstevel@tonic-gate 	vn_invalid(vp);
2970Sstevel@tonic-gate 	vn_free(vp);
2980Sstevel@tonic-gate 	kmem_free(pcp, sizeof (struct pcnode));
2990Sstevel@tonic-gate }
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate /*
3020Sstevel@tonic-gate  * Mark a pcnode as modified with the current time.
3030Sstevel@tonic-gate  */
3045121Sfrankho /* ARGSUSED */
3050Sstevel@tonic-gate void
pc_mark_mod(struct pcfs * fsp,struct pcnode * pcp)3065121Sfrankho pc_mark_mod(struct pcfs *fsp, struct pcnode *pcp)
3070Sstevel@tonic-gate {
3080Sstevel@tonic-gate 	timestruc_t now;
3090Sstevel@tonic-gate 
3105121Sfrankho 	if (PCTOV(pcp)->v_type == VDIR)
3115121Sfrankho 		return;
3125121Sfrankho 
3135121Sfrankho 	ASSERT(PCTOV(pcp)->v_type == VREG);
3145121Sfrankho 
3155121Sfrankho 	gethrestime(&now);
3165121Sfrankho 	if (pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime))
3175121Sfrankho 		PC_DPRINTF1(2, "pc_mark_mod failed timestamp "
3185121Sfrankho 		    "conversion, curtime = %lld\n",
3195121Sfrankho 		    (long long)now.tv_sec);
3205121Sfrankho 
3215121Sfrankho 	pcp->pc_flags |= PC_CHG;
3220Sstevel@tonic-gate }
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate /*
3250Sstevel@tonic-gate  * Mark a pcnode as accessed with the current time.
3260Sstevel@tonic-gate  */
3270Sstevel@tonic-gate void
pc_mark_acc(struct pcfs * fsp,struct pcnode * pcp)3285121Sfrankho pc_mark_acc(struct pcfs *fsp, struct pcnode *pcp)
3290Sstevel@tonic-gate {
3302720Sfrankho 	struct pctime pt = { 0, 0 };
3310Sstevel@tonic-gate 	timestruc_t now;
3320Sstevel@tonic-gate 
3335121Sfrankho 	if (fsp->pcfs_flags & PCFS_NOATIME || PCTOV(pcp)->v_type == VDIR)
3345121Sfrankho 		return;
3355121Sfrankho 
3365121Sfrankho 	ASSERT(PCTOV(pcp)->v_type == VREG);
3375121Sfrankho 
3385121Sfrankho 	gethrestime(&now);
3395121Sfrankho 	if (pc_tvtopct(&now, &pt)) {
3405121Sfrankho 		PC_DPRINTF1(2, "pc_mark_acc failed timestamp "
3415121Sfrankho 		    "conversion, curtime = %lld\n",
3425121Sfrankho 		    (long long)now.tv_sec);
3435121Sfrankho 		return;
3445121Sfrankho 	}
3455121Sfrankho 
3465121Sfrankho 	/*
3475121Sfrankho 	 * We don't really want to write the adate for every access
3485121Sfrankho 	 * on flash media; make sure it really changed !
3495121Sfrankho 	 */
3505121Sfrankho 	if (pcp->pc_entry.pcd_ladate != pt.pct_date) {
3510Sstevel@tonic-gate 		pcp->pc_entry.pcd_ladate = pt.pct_date;
3525121Sfrankho 		pcp->pc_flags |= (PC_CHG | PC_ACC);
3530Sstevel@tonic-gate 	}
3540Sstevel@tonic-gate }
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate /*
3570Sstevel@tonic-gate  * Truncate a file to a length.
3580Sstevel@tonic-gate  * Node must be locked.
3590Sstevel@tonic-gate  */
3600Sstevel@tonic-gate int
pc_truncate(struct pcnode * pcp,uint_t length)3610Sstevel@tonic-gate pc_truncate(struct pcnode *pcp, uint_t length)
3620Sstevel@tonic-gate {
3630Sstevel@tonic-gate 	struct pcfs *fsp;
3640Sstevel@tonic-gate 	struct vnode *vp;
3650Sstevel@tonic-gate 	int error = 0;
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate 	PC_DPRINTF3(4, "pc_truncate pcp=0x%p, len=%u, size=%u\n",
3680Sstevel@tonic-gate 	    (void *)pcp, length, pcp->pc_size);
3690Sstevel@tonic-gate 	vp = PCTOV(pcp);
3700Sstevel@tonic-gate 	if (pcp->pc_flags & PC_INVAL)
3710Sstevel@tonic-gate 		return (EIO);
3720Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
3730Sstevel@tonic-gate 	/*
3740Sstevel@tonic-gate 	 * directories are always truncated to zero and are not marked
3750Sstevel@tonic-gate 	 */
3760Sstevel@tonic-gate 	if (vp->v_type == VDIR) {
3770Sstevel@tonic-gate 		error = pc_bfree(pcp, 0);
3780Sstevel@tonic-gate 		return (error);
3790Sstevel@tonic-gate 	}
3800Sstevel@tonic-gate 	/*
3810Sstevel@tonic-gate 	 * If length is the same as the current size
3820Sstevel@tonic-gate 	 * just mark the pcnode and return.
3830Sstevel@tonic-gate 	 */
3840Sstevel@tonic-gate 	if (length > pcp->pc_size) {
3850Sstevel@tonic-gate 		daddr_t bno;
3865121Sfrankho 		uint_t llcn = howmany((offset_t)length, fsp->pcfs_clsize);
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 		/*
3890Sstevel@tonic-gate 		 * We are extending a file.
3900Sstevel@tonic-gate 		 * Extend it with _one_ call to pc_balloc (no holes)
3910Sstevel@tonic-gate 		 * since we don't need to use the block number(s).
3920Sstevel@tonic-gate 		 */
3930Sstevel@tonic-gate 		if ((daddr_t)howmany((offset_t)pcp->pc_size, fsp->pcfs_clsize) <
3945121Sfrankho 		    (daddr_t)llcn) {
3950Sstevel@tonic-gate 			error = pc_balloc(pcp, (daddr_t)(llcn - 1), 1, &bno);
3960Sstevel@tonic-gate 		}
3970Sstevel@tonic-gate 		if (error) {
3982972Sfrankho 			pc_cluster32_t ncl = 0;
3990Sstevel@tonic-gate 			PC_DPRINTF1(2, "pc_truncate: error=%d\n", error);
4000Sstevel@tonic-gate 			/*
4010Sstevel@tonic-gate 			 * probably ran out disk space;
4020Sstevel@tonic-gate 			 * determine current file size
4030Sstevel@tonic-gate 			 */
4042972Sfrankho 			if (pc_fileclsize(fsp, pcp->pc_scluster, &ncl)) {
4052972Sfrankho 				PC_DPRINTF1(2, "cluster chain corruption, "
4062972Sfrankho 				    "scluster=%d\n", pcp->pc_scluster);
4072972Sfrankho 				pcp->pc_flags |= PC_INVAL;
4082972Sfrankho 			}
4092972Sfrankho 			pcp->pc_size = fsp->pcfs_clsize * ncl;
4100Sstevel@tonic-gate 		} else
4110Sstevel@tonic-gate 			pcp->pc_size = length;
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate 	} else if (length < pcp->pc_size) {
4140Sstevel@tonic-gate 		/*
4150Sstevel@tonic-gate 		 * We are shrinking a file.
4160Sstevel@tonic-gate 		 * Free blocks after the block that length points to.
4170Sstevel@tonic-gate 		 */
4180Sstevel@tonic-gate 		if (pc_blkoff(fsp, length) == 0) {
4190Sstevel@tonic-gate 			/*
4200Sstevel@tonic-gate 			 * Truncation to a block (cluster size) boundary only
4210Sstevel@tonic-gate 			 * requires us to invalidate everything after the new
4220Sstevel@tonic-gate 			 * end of the file.
4230Sstevel@tonic-gate 			 */
4240Sstevel@tonic-gate 			(void) pvn_vplist_dirty(PCTOV(pcp), (u_offset_t)length,
4255121Sfrankho 			    pcfs_putapage, B_INVAL | B_TRUNC, CRED());
4260Sstevel@tonic-gate 		} else {
4270Sstevel@tonic-gate 			/*
4280Sstevel@tonic-gate 			 * pvn_vpzero() cannot deal with more than MAXBSIZE
4290Sstevel@tonic-gate 			 * chunks. Since the FAT clustersize can get larger
4300Sstevel@tonic-gate 			 * than that, we'll zero from the new length to the
4310Sstevel@tonic-gate 			 * end of the cluster for clustersizes smaller than
4320Sstevel@tonic-gate 			 * MAXBSIZE - or the end of the MAXBSIZE block in
4330Sstevel@tonic-gate 			 * case we've got a large clustersize.
4340Sstevel@tonic-gate 			 */
4350Sstevel@tonic-gate 			size_t nbytes =
4360Sstevel@tonic-gate 			    roundup(length, MIN(fsp->pcfs_clsize, MAXBSIZE)) -
4370Sstevel@tonic-gate 			    length;
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate 			pvn_vpzero(PCTOV(pcp), (u_offset_t)length, nbytes);
4400Sstevel@tonic-gate 			(void) pvn_vplist_dirty(PCTOV(pcp),
4410Sstevel@tonic-gate 			    (u_offset_t)length + nbytes,
4420Sstevel@tonic-gate 			    pcfs_putapage, B_INVAL | B_TRUNC, CRED());
4430Sstevel@tonic-gate 		}
4445121Sfrankho 		error = pc_bfree(pcp, (pc_cluster32_t)
4455121Sfrankho 		    howmany((offset_t)length, fsp->pcfs_clsize));
4460Sstevel@tonic-gate 		pcp->pc_size = length;
4470Sstevel@tonic-gate 	}
4485121Sfrankho 
4495121Sfrankho 	/*
4505121Sfrankho 	 * This is the only place in PCFS code where pc_mark_mod() is called
4515121Sfrankho 	 * without setting PC_MOD. May be a historical artifact ...
4525121Sfrankho 	 */
4535121Sfrankho 	pc_mark_mod(fsp, pcp);
4540Sstevel@tonic-gate 	return (error);
4550Sstevel@tonic-gate }
4560Sstevel@tonic-gate 
4570Sstevel@tonic-gate /*
4580Sstevel@tonic-gate  * Get block for entry.
4590Sstevel@tonic-gate  */
4600Sstevel@tonic-gate static int
pc_getentryblock(struct pcnode * pcp,struct buf ** bpp)4610Sstevel@tonic-gate pc_getentryblock(struct pcnode *pcp, struct buf **bpp)
4620Sstevel@tonic-gate {
4630Sstevel@tonic-gate 	struct pcfs *fsp;
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate 	fsp = VFSTOPCFS(PCTOV(pcp)->v_vfsp);
4660Sstevel@tonic-gate 	if (pcp->pc_eblkno >= fsp->pcfs_datastart ||
4670Sstevel@tonic-gate 	    (pcp->pc_eblkno - fsp->pcfs_rdirstart) <
4680Sstevel@tonic-gate 	    (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
4690Sstevel@tonic-gate 		*bpp = bread(fsp->pcfs_xdev,
4700Sstevel@tonic-gate 		    pc_dbdaddr(fsp, pcp->pc_eblkno), fsp->pcfs_clsize);
4710Sstevel@tonic-gate 	} else {
4720Sstevel@tonic-gate 		*bpp = bread(fsp->pcfs_xdev,
4730Sstevel@tonic-gate 		    pc_dbdaddr(fsp, pcp->pc_eblkno),
4745121Sfrankho 		    (int)(fsp->pcfs_datastart - pcp->pc_eblkno) *
4750Sstevel@tonic-gate 		    fsp->pcfs_secsize);
4760Sstevel@tonic-gate 	}
4770Sstevel@tonic-gate 	if ((*bpp)->b_flags & B_ERROR) {
4780Sstevel@tonic-gate 		brelse(*bpp);
4790Sstevel@tonic-gate 		pc_mark_irrecov(fsp);
4800Sstevel@tonic-gate 		return (EIO);
4810Sstevel@tonic-gate 	}
4820Sstevel@tonic-gate 	return (0);
4830Sstevel@tonic-gate }
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate /*
4860Sstevel@tonic-gate  * Sync all data associated with a file.
4870Sstevel@tonic-gate  * Flush all the blocks in the buffer cache out to disk, sync the FAT and
4880Sstevel@tonic-gate  * update the directory entry.
4890Sstevel@tonic-gate  */
4900Sstevel@tonic-gate int
pc_nodesync(struct pcnode * pcp)4910Sstevel@tonic-gate pc_nodesync(struct pcnode *pcp)
4920Sstevel@tonic-gate {
4930Sstevel@tonic-gate 	struct pcfs *fsp;
4940Sstevel@tonic-gate 	int err;
4950Sstevel@tonic-gate 	struct vnode *vp;
4960Sstevel@tonic-gate 
4970Sstevel@tonic-gate 	vp = PCTOV(pcp);
4980Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
4990Sstevel@tonic-gate 	err = 0;
5000Sstevel@tonic-gate 	if (pcp->pc_flags & PC_MOD) {
5010Sstevel@tonic-gate 		/*
5020Sstevel@tonic-gate 		 * Flush all data blocks from buffer cache and
5030Sstevel@tonic-gate 		 * update the FAT which points to the data.
5040Sstevel@tonic-gate 		 */
5050Sstevel@tonic-gate 		if (err = syncpcp(pcp, 0)) { /* %% ?? how to handle error? */
5060Sstevel@tonic-gate 			if (err == ENOMEM)
5070Sstevel@tonic-gate 				return (err);
5080Sstevel@tonic-gate 			else {
5090Sstevel@tonic-gate 				pc_mark_irrecov(fsp);
5100Sstevel@tonic-gate 				return (EIO);
5110Sstevel@tonic-gate 			}
5120Sstevel@tonic-gate 		}
5130Sstevel@tonic-gate 		pcp->pc_flags &= ~PC_MOD;
5140Sstevel@tonic-gate 	}
5150Sstevel@tonic-gate 	/*
5160Sstevel@tonic-gate 	 * update the directory entry
5170Sstevel@tonic-gate 	 */
5180Sstevel@tonic-gate 	if (pcp->pc_flags & PC_CHG)
5190Sstevel@tonic-gate 		(void) pc_nodeupdate(pcp);
5200Sstevel@tonic-gate 	return (err);
5210Sstevel@tonic-gate }
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate /*
5240Sstevel@tonic-gate  * Update the node's directory entry.
5250Sstevel@tonic-gate  */
5260Sstevel@tonic-gate int
pc_nodeupdate(struct pcnode * pcp)5270Sstevel@tonic-gate pc_nodeupdate(struct pcnode *pcp)
5280Sstevel@tonic-gate {
5290Sstevel@tonic-gate 	struct buf *bp;
5300Sstevel@tonic-gate 	int error;
5310Sstevel@tonic-gate 	struct vnode *vp;
5320Sstevel@tonic-gate 	struct pcfs *fsp;
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate 	vp = PCTOV(pcp);
5350Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
5360Sstevel@tonic-gate 	if (IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
5370Sstevel@tonic-gate 		/* no node to update */
5380Sstevel@tonic-gate 		pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC);
5390Sstevel@tonic-gate 		return (0);
5400Sstevel@tonic-gate 	}
5410Sstevel@tonic-gate 	if (vp->v_flag & VROOT) {
5420Sstevel@tonic-gate 		panic("pc_nodeupdate");
5430Sstevel@tonic-gate 	}
5440Sstevel@tonic-gate 	if (pcp->pc_flags & PC_INVAL)
5450Sstevel@tonic-gate 		return (0);
5460Sstevel@tonic-gate 	PC_DPRINTF3(7, "pc_nodeupdate pcp=0x%p, bn=%ld, off=%d\n", (void *)pcp,
5470Sstevel@tonic-gate 	    pcp->pc_eblkno, pcp->pc_eoffset);
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate 	if (error = pc_getentryblock(pcp, &bp)) {
5500Sstevel@tonic-gate 		return (error);
5510Sstevel@tonic-gate 	}
5520Sstevel@tonic-gate 	if (vp->v_type == VREG) {
5530Sstevel@tonic-gate 		if (pcp->pc_flags & PC_CHG)
5540Sstevel@tonic-gate 			pcp->pc_entry.pcd_attr |= PCA_ARCH;
5550Sstevel@tonic-gate 		pcp->pc_entry.pcd_size = htoli(pcp->pc_size);
5560Sstevel@tonic-gate 	}
5570Sstevel@tonic-gate 	pc_setstartcluster(fsp, &pcp->pc_entry, pcp->pc_scluster);
5580Sstevel@tonic-gate 	*((struct pcdir *)(bp->b_un.b_addr + pcp->pc_eoffset)) = pcp->pc_entry;
5590Sstevel@tonic-gate 	bwrite2(bp);
5600Sstevel@tonic-gate 	error = geterror(bp);
5610Sstevel@tonic-gate 	brelse(bp);
5620Sstevel@tonic-gate 	if (error) {
5635121Sfrankho 		error = EIO;
5640Sstevel@tonic-gate 		pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
5650Sstevel@tonic-gate 	}
5660Sstevel@tonic-gate 	pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC);
5670Sstevel@tonic-gate 	return (error);
5680Sstevel@tonic-gate }
5690Sstevel@tonic-gate 
5700Sstevel@tonic-gate /*
5710Sstevel@tonic-gate  * Verify that the disk in the drive is the same one that we
5720Sstevel@tonic-gate  * got the pcnode from.
5730Sstevel@tonic-gate  * MUST be called with node unlocked.
5740Sstevel@tonic-gate  */
5750Sstevel@tonic-gate int
pc_verify(struct pcfs * fsp)5760Sstevel@tonic-gate pc_verify(struct pcfs *fsp)
5770Sstevel@tonic-gate {
5780Sstevel@tonic-gate 	int fdstatus = 0;
5790Sstevel@tonic-gate 	int error = 0;
5800Sstevel@tonic-gate 
5810Sstevel@tonic-gate 	if (!fsp || fsp->pcfs_flags & PCFS_IRRECOV)
5820Sstevel@tonic-gate 		return (EIO);
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate 	if (!(fsp->pcfs_flags & PCFS_NOCHK) && fsp->pcfs_fatp) {
5855121Sfrankho 		/*
5865121Sfrankho 		 * This "has it been removed" check should better be
5875121Sfrankho 		 * modified for removeable media that are not floppies.
5885121Sfrankho 		 * dkio-managed devices such as USB/firewire external
5895121Sfrankho 		 * disks/memory sticks/floppies (gasp) do not understand
5905121Sfrankho 		 * this ioctl.
5915121Sfrankho 		 */
5920Sstevel@tonic-gate 		PC_DPRINTF1(4, "pc_verify fsp=0x%p\n", (void *)fsp);
5930Sstevel@tonic-gate 		error = cdev_ioctl(fsp->pcfs_vfs->vfs_dev,
5945121Sfrankho 		    FDGETCHANGE, (intptr_t)&fdstatus, FNATIVE | FKIOCTL,
5950Sstevel@tonic-gate 		    NULL, NULL);
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate 		if (error) {
5980Sstevel@tonic-gate 			if (error == ENOTTY || error == ENXIO) {
5995121Sfrankho 				/*
6005121Sfrankho 				 * See comment above. This is a workaround
6015121Sfrankho 				 * for removeable media that don't understand
6025121Sfrankho 				 * floppy ioctls.
6035121Sfrankho 				 */
6040Sstevel@tonic-gate 				error = 0;
6050Sstevel@tonic-gate 			} else {
6060Sstevel@tonic-gate 				PC_DPRINTF1(1,
6070Sstevel@tonic-gate 				    "pc_verify: FDGETCHANGE ioctl failed: %d\n",
6080Sstevel@tonic-gate 				    error);
6090Sstevel@tonic-gate 				pc_mark_irrecov(fsp);
6100Sstevel@tonic-gate 			}
6110Sstevel@tonic-gate 		} else if (fsp->pcfs_fatjustread) {
6120Sstevel@tonic-gate 			/*
6130Sstevel@tonic-gate 			 * Ignore the results of the ioctl if we just
6140Sstevel@tonic-gate 			 * read the FAT.  There is a good chance that
6150Sstevel@tonic-gate 			 * the disk changed bit will be on, because
6160Sstevel@tonic-gate 			 * we've just mounted and we don't want to
6170Sstevel@tonic-gate 			 * give a false positive that the sky is falling.
6180Sstevel@tonic-gate 			 */
6190Sstevel@tonic-gate 			fsp->pcfs_fatjustread = 0;
6200Sstevel@tonic-gate 		} else {
6210Sstevel@tonic-gate 			/*
6220Sstevel@tonic-gate 			 * Oddly enough we can't check just one flag here. The
6230Sstevel@tonic-gate 			 * x86 floppy driver sets a different flag
6240Sstevel@tonic-gate 			 * (FDGC_DETECTED) than the sparc driver does.
6250Sstevel@tonic-gate 			 * I think this MAY be a bug, and I filed 4165938
6260Sstevel@tonic-gate 			 * to get someone to look at the behavior
6270Sstevel@tonic-gate 			 * a bit more closely.  In the meantime, my testing and
6280Sstevel@tonic-gate 			 * code examination seem to indicate it is safe to
6290Sstevel@tonic-gate 			 * check for either bit being set.
6300Sstevel@tonic-gate 			 */
6310Sstevel@tonic-gate 			if (fdstatus & (FDGC_HISTORY | FDGC_DETECTED)) {
6320Sstevel@tonic-gate 				PC_DPRINTF0(1, "pc_verify: change detected\n");
6330Sstevel@tonic-gate 				pc_mark_irrecov(fsp);
6340Sstevel@tonic-gate 			}
6350Sstevel@tonic-gate 		}
6360Sstevel@tonic-gate 	}
6375121Sfrankho 	if (error == 0 && fsp->pcfs_fatp == NULL) {
6380Sstevel@tonic-gate 		error = pc_getfat(fsp);
6390Sstevel@tonic-gate 	}
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 	return (error);
6420Sstevel@tonic-gate }
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate /*
6450Sstevel@tonic-gate  * The disk has changed, pulling the rug out from beneath us.
6460Sstevel@tonic-gate  * Mark the FS as being in an irrecoverable state.
6470Sstevel@tonic-gate  * In a short while we'll clean up.
6480Sstevel@tonic-gate  */
6490Sstevel@tonic-gate void
pc_mark_irrecov(struct pcfs * fsp)6500Sstevel@tonic-gate pc_mark_irrecov(struct pcfs *fsp)
6510Sstevel@tonic-gate {
6520Sstevel@tonic-gate 	if (!(fsp->pcfs_flags & PCFS_NOCHK)) {
6530Sstevel@tonic-gate 		if (pc_lockfs(fsp, 1, 0)) {
6540Sstevel@tonic-gate 			/*
6550Sstevel@tonic-gate 			 * Locking failed, which currently would
6560Sstevel@tonic-gate 			 * only happen if the FS were already
6570Sstevel@tonic-gate 			 * marked as hosed.  If another reason for
6580Sstevel@tonic-gate 			 * failure were to arise in the future, this
6590Sstevel@tonic-gate 			 * routine would have to change.
6600Sstevel@tonic-gate 			 */
6610Sstevel@tonic-gate 			return;
6620Sstevel@tonic-gate 		}
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_IRRECOV;
6650Sstevel@tonic-gate 		cmn_err(CE_WARN,
6665121Sfrankho 		    "Disk was changed during an update or\n"
6675121Sfrankho 		    "an irrecoverable error was encountered.\n"
6685121Sfrankho 		    "File damage is possible.  To prevent further\n"
6695121Sfrankho 		    "damage, this pcfs instance will now be frozen.\n"
6705121Sfrankho 		    "Use umount(1M) to release the instance.\n");
6710Sstevel@tonic-gate 		(void) pc_unlockfs(fsp);
6720Sstevel@tonic-gate 	}
6730Sstevel@tonic-gate }
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate /*
6760Sstevel@tonic-gate  * The disk has been changed!
6770Sstevel@tonic-gate  */
6780Sstevel@tonic-gate void
pc_diskchanged(struct pcfs * fsp)6790Sstevel@tonic-gate pc_diskchanged(struct pcfs *fsp)
6800Sstevel@tonic-gate {
6812720Sfrankho 	struct pcnode	*pcp, *npcp = NULL;
6822720Sfrankho 	struct pchead	*hp;
6832720Sfrankho 	struct vnode	*vp;
6842720Sfrankho 	extern vfs_t	EIO_vfs;
6852720Sfrankho 	struct vfs	*vfsp;
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate 	/*
6880Sstevel@tonic-gate 	 * Eliminate all pcnodes (dir & file) associated with this fs.
6890Sstevel@tonic-gate 	 * If the node is internal, ie, no references outside of
6900Sstevel@tonic-gate 	 * pcfs itself, then release the associated vnode structure.
6910Sstevel@tonic-gate 	 * Invalidate the in core FAT.
6920Sstevel@tonic-gate 	 * Invalidate cached data blocks and blocks waiting for I/O.
6930Sstevel@tonic-gate 	 */
6940Sstevel@tonic-gate 	PC_DPRINTF1(1, "pc_diskchanged fsp=0x%p\n", (void *)fsp);
6950Sstevel@tonic-gate 
6962720Sfrankho 	vfsp = PCFSTOVFS(fsp);
6972720Sfrankho 
6980Sstevel@tonic-gate 	for (hp = pcdhead; hp < &pcdhead[NPCHASH]; hp++) {
6990Sstevel@tonic-gate 		for (pcp = hp->pch_forw;
7000Sstevel@tonic-gate 		    pcp != (struct pcnode *)hp; pcp = npcp) {
7010Sstevel@tonic-gate 			npcp = pcp -> pc_forw;
7020Sstevel@tonic-gate 			vp = PCTOV(pcp);
7032720Sfrankho 			if ((vp->v_vfsp == vfsp) &&
7040Sstevel@tonic-gate 			    !(pcp->pc_flags & PC_RELEHOLD)) {
7050Sstevel@tonic-gate 				mutex_enter(&(vp)->v_lock);
7060Sstevel@tonic-gate 				if (vp->v_count > 0) {
7070Sstevel@tonic-gate 					mutex_exit(&(vp)->v_lock);
7080Sstevel@tonic-gate 					continue;
7090Sstevel@tonic-gate 				}
7100Sstevel@tonic-gate 				mutex_exit(&(vp)->v_lock);
7110Sstevel@tonic-gate 				VN_HOLD(vp);
7120Sstevel@tonic-gate 				remque(pcp);
7130Sstevel@tonic-gate 				vp->v_data = NULL;
7140Sstevel@tonic-gate 				vp->v_vfsp = &EIO_vfs;
7150Sstevel@tonic-gate 				vp->v_type = VBAD;
7160Sstevel@tonic-gate 				VN_RELE(vp);
7172720Sfrankho 				if (!(pcp->pc_flags & PC_EXTERNAL)) {
7182720Sfrankho 					(void) pvn_vplist_dirty(vp,
7192720Sfrankho 					    (u_offset_t)0, pcfs_putapage,
7202720Sfrankho 					    B_INVAL | B_TRUNC,
7212720Sfrankho 					    (struct cred *)NULL);
7220Sstevel@tonic-gate 					vn_free(vp);
7232720Sfrankho 				}
7240Sstevel@tonic-gate 				kmem_free(pcp, sizeof (struct pcnode));
7250Sstevel@tonic-gate 				fsp->pcfs_nrefs --;
7262720Sfrankho 				VFS_RELE(vfsp);
7270Sstevel@tonic-gate 			}
7280Sstevel@tonic-gate 		}
7290Sstevel@tonic-gate 	}
7300Sstevel@tonic-gate 	for (hp = pcfhead; fsp->pcfs_frefs && hp < &pcfhead[NPCHASH]; hp++) {
7310Sstevel@tonic-gate 		for (pcp = hp->pch_forw; fsp->pcfs_frefs &&
7320Sstevel@tonic-gate 		    pcp != (struct pcnode *)hp; pcp = npcp) {
7330Sstevel@tonic-gate 			npcp = pcp -> pc_forw;
7340Sstevel@tonic-gate 			vp = PCTOV(pcp);
7352720Sfrankho 			if ((vp->v_vfsp == vfsp) &&
7360Sstevel@tonic-gate 			    !(pcp->pc_flags & PC_RELEHOLD)) {
7370Sstevel@tonic-gate 				mutex_enter(&(vp)->v_lock);
7380Sstevel@tonic-gate 				if (vp->v_count > 0) {
7390Sstevel@tonic-gate 					mutex_exit(&(vp)->v_lock);
7400Sstevel@tonic-gate 					continue;
7410Sstevel@tonic-gate 				}
7420Sstevel@tonic-gate 				mutex_exit(&(vp)->v_lock);
7430Sstevel@tonic-gate 				VN_HOLD(vp);
7440Sstevel@tonic-gate 				remque(pcp);
7450Sstevel@tonic-gate 				vp->v_data = NULL;
7460Sstevel@tonic-gate 				vp->v_vfsp = &EIO_vfs;
7470Sstevel@tonic-gate 				vp->v_type = VBAD;
7480Sstevel@tonic-gate 				VN_RELE(vp);
7492720Sfrankho 				if (!(pcp->pc_flags & PC_EXTERNAL)) {
7502720Sfrankho 					(void) pvn_vplist_dirty(vp,
7512720Sfrankho 					    (u_offset_t)0, pcfs_putapage,
7522720Sfrankho 					    B_INVAL | B_TRUNC,
7532720Sfrankho 					    (struct cred *)NULL);
7540Sstevel@tonic-gate 					vn_free(vp);
7552720Sfrankho 				}
7560Sstevel@tonic-gate 				kmem_free(pcp, sizeof (struct pcnode));
7572972Sfrankho 				fsp->pcfs_frefs--;
7582972Sfrankho 				fsp->pcfs_nrefs--;
7592720Sfrankho 				VFS_RELE(vfsp);
7600Sstevel@tonic-gate 			}
7610Sstevel@tonic-gate 		}
7620Sstevel@tonic-gate 	}
7630Sstevel@tonic-gate #ifdef undef
7640Sstevel@tonic-gate 	if (fsp->pcfs_frefs) {
7650Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
7660Sstevel@tonic-gate 		panic("pc_diskchanged: frefs");
7670Sstevel@tonic-gate 	}
7680Sstevel@tonic-gate 	if (fsp->pcfs_nrefs) {
7690Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
7700Sstevel@tonic-gate 		panic("pc_diskchanged: nrefs");
7710Sstevel@tonic-gate 	}
7720Sstevel@tonic-gate #endif
7732720Sfrankho 	if (!(vfsp->vfs_flag & VFS_UNMOUNTED) &&
7742720Sfrankho 	    fsp->pcfs_fatp != (uchar_t *)0) {
7750Sstevel@tonic-gate 		pc_invalfat(fsp);
7760Sstevel@tonic-gate 	} else {
7770Sstevel@tonic-gate 		binval(fsp->pcfs_xdev);
7780Sstevel@tonic-gate 	}
7790Sstevel@tonic-gate }
780