xref: /titanic_44/usr/src/uts/common/fs/pcfs/pc_node.c (revision da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5264a6e74Sfrankho  * Common Development and Distribution License (the "License").
6264a6e74Sfrankho  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22a43c4dd1Scasper  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include <sys/param.h>
297c478bd9Sstevel@tonic-gate #include <sys/t_lock.h>
307c478bd9Sstevel@tonic-gate #include <sys/errno.h>
317c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
327c478bd9Sstevel@tonic-gate #include <sys/buf.h>
337c478bd9Sstevel@tonic-gate #include <sys/systm.h>
347c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
357c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
367c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
377c478bd9Sstevel@tonic-gate #include <sys/proc.h>
387c478bd9Sstevel@tonic-gate #include <sys/cred.h>
397c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
407c478bd9Sstevel@tonic-gate #include <sys/debug.h>
417c478bd9Sstevel@tonic-gate #include <vm/pvn.h>
427c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h>
437c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
447c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
457c478bd9Sstevel@tonic-gate #include <sys/fs/pc_node.h>
467c478bd9Sstevel@tonic-gate #include <sys/dirent.h>
477c478bd9Sstevel@tonic-gate #include <sys/fdio.h>
487c478bd9Sstevel@tonic-gate #include <sys/file.h>
497c478bd9Sstevel@tonic-gate #include <sys/conf.h>
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate struct pchead pcfhead[NPCHASH];
527c478bd9Sstevel@tonic-gate struct pchead pcdhead[NPCHASH];
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate extern krwlock_t pcnodes_lock;
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate static int	pc_getentryblock(struct pcnode *, struct buf **);
577c478bd9Sstevel@tonic-gate static int	syncpcp(struct pcnode *, int);
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate /*
607c478bd9Sstevel@tonic-gate  * fake entry for root directory, since this does not have a parent
617c478bd9Sstevel@tonic-gate  * pointing to it.
627c478bd9Sstevel@tonic-gate  */
63264a6e74Sfrankho struct pcdir pcfs_rootdirentry = {
647c478bd9Sstevel@tonic-gate 	"",
657c478bd9Sstevel@tonic-gate 	"",
667c478bd9Sstevel@tonic-gate 	PCA_DIR
677c478bd9Sstevel@tonic-gate };
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate void
pc_init(void)707c478bd9Sstevel@tonic-gate pc_init(void)
717c478bd9Sstevel@tonic-gate {
727c478bd9Sstevel@tonic-gate 	struct pchead *hdp, *hfp;
737c478bd9Sstevel@tonic-gate 	int i;
747c478bd9Sstevel@tonic-gate 	for (i = 0; i < NPCHASH; i++) {
757c478bd9Sstevel@tonic-gate 		hdp = &pcdhead[i];
767c478bd9Sstevel@tonic-gate 		hfp = &pcfhead[i];
777c478bd9Sstevel@tonic-gate 		hdp->pch_forw =  (struct pcnode *)hdp;
787c478bd9Sstevel@tonic-gate 		hdp->pch_back =  (struct pcnode *)hdp;
797c478bd9Sstevel@tonic-gate 		hfp->pch_forw =  (struct pcnode *)hfp;
807c478bd9Sstevel@tonic-gate 		hfp->pch_back =  (struct pcnode *)hfp;
817c478bd9Sstevel@tonic-gate 	}
827c478bd9Sstevel@tonic-gate }
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate struct pcnode *
pc_getnode(struct pcfs * fsp,daddr_t blkno,int offset,struct pcdir * ep)857c478bd9Sstevel@tonic-gate pc_getnode(
867c478bd9Sstevel@tonic-gate 	struct pcfs *fsp,	/* filsystem for node */
877c478bd9Sstevel@tonic-gate 	daddr_t blkno,		/* phys block no of dir entry */
887c478bd9Sstevel@tonic-gate 	int offset,		/* offset of dir entry in block */
897c478bd9Sstevel@tonic-gate 	struct pcdir *ep)	/* node dir entry */
907c478bd9Sstevel@tonic-gate {
917c478bd9Sstevel@tonic-gate 	struct pcnode *pcp;
927c478bd9Sstevel@tonic-gate 	struct pchead *hp;
937c478bd9Sstevel@tonic-gate 	struct vnode *vp;
947c478bd9Sstevel@tonic-gate 	pc_cluster32_t scluster;
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate 	ASSERT(fsp->pcfs_flags & PCFS_LOCKED);
977c478bd9Sstevel@tonic-gate 	if (ep == (struct pcdir *)0) {
98264a6e74Sfrankho 		ep = &pcfs_rootdirentry;
997c478bd9Sstevel@tonic-gate 		scluster = 0;
1007c478bd9Sstevel@tonic-gate 	} else {
1017c478bd9Sstevel@tonic-gate 		scluster = pc_getstartcluster(fsp, ep);
1027c478bd9Sstevel@tonic-gate 	}
1037c478bd9Sstevel@tonic-gate 	/*
1047c478bd9Sstevel@tonic-gate 	 * First look for active nodes.
1057c478bd9Sstevel@tonic-gate 	 * File nodes are identified by the location (blkno, offset) of
1067c478bd9Sstevel@tonic-gate 	 * its directory entry.
1077c478bd9Sstevel@tonic-gate 	 * Directory nodes are identified by the starting cluster number
1087c478bd9Sstevel@tonic-gate 	 * for the entries.
1097c478bd9Sstevel@tonic-gate 	 */
1107c478bd9Sstevel@tonic-gate 	if (ep->pcd_attr & PCA_DIR) {
1117c478bd9Sstevel@tonic-gate 		hp = &pcdhead[PCDHASH(fsp, scluster)];
1127c478bd9Sstevel@tonic-gate 		rw_enter(&pcnodes_lock, RW_READER);
1137c478bd9Sstevel@tonic-gate 		for (pcp = hp->pch_forw;
1147c478bd9Sstevel@tonic-gate 		    pcp != (struct pcnode *)hp; pcp = pcp->pc_forw) {
1157c478bd9Sstevel@tonic-gate 			if ((fsp == VFSTOPCFS(PCTOV(pcp)->v_vfsp)) &&
1167c478bd9Sstevel@tonic-gate 			    (scluster == pcp->pc_scluster)) {
1177c478bd9Sstevel@tonic-gate 				VN_HOLD(PCTOV(pcp));
1187c478bd9Sstevel@tonic-gate 				rw_exit(&pcnodes_lock);
1197c478bd9Sstevel@tonic-gate 				return (pcp);
1207c478bd9Sstevel@tonic-gate 			}
1217c478bd9Sstevel@tonic-gate 		}
1227c478bd9Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
1237c478bd9Sstevel@tonic-gate 	} else {
1247c478bd9Sstevel@tonic-gate 		hp = &pcfhead[PCFHASH(fsp, blkno, offset)];
1257c478bd9Sstevel@tonic-gate 		rw_enter(&pcnodes_lock, RW_READER);
1267c478bd9Sstevel@tonic-gate 		for (pcp = hp->pch_forw;
1277c478bd9Sstevel@tonic-gate 		    pcp != (struct pcnode *)hp; pcp = pcp->pc_forw) {
1287c478bd9Sstevel@tonic-gate 			if ((fsp == VFSTOPCFS(PCTOV(pcp)->v_vfsp)) &&
1297c478bd9Sstevel@tonic-gate 			    ((pcp->pc_flags & PC_INVAL) == 0) &&
1307c478bd9Sstevel@tonic-gate 			    (blkno == pcp->pc_eblkno) &&
1317c478bd9Sstevel@tonic-gate 			    (offset == pcp->pc_eoffset)) {
1327c478bd9Sstevel@tonic-gate 				VN_HOLD(PCTOV(pcp));
1337c478bd9Sstevel@tonic-gate 				rw_exit(&pcnodes_lock);
1347c478bd9Sstevel@tonic-gate 				return (pcp);
1357c478bd9Sstevel@tonic-gate 			}
1367c478bd9Sstevel@tonic-gate 		}
1377c478bd9Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
1387c478bd9Sstevel@tonic-gate 	}
1397c478bd9Sstevel@tonic-gate 	/*
1407c478bd9Sstevel@tonic-gate 	 * Cannot find node in active list. Allocate memory for a new node
1417c478bd9Sstevel@tonic-gate 	 * initialize it, and put it on the active list.
1427c478bd9Sstevel@tonic-gate 	 */
1437c478bd9Sstevel@tonic-gate 	pcp = kmem_alloc(sizeof (struct pcnode), KM_SLEEP);
1447c478bd9Sstevel@tonic-gate 	bzero(pcp, sizeof (struct pcnode));
1457c478bd9Sstevel@tonic-gate 	vp = vn_alloc(KM_SLEEP);
1467c478bd9Sstevel@tonic-gate 	pcp->pc_vn = vp;
1477c478bd9Sstevel@tonic-gate 	pcp->pc_entry = *ep;
1487c478bd9Sstevel@tonic-gate 	pcp->pc_eblkno = blkno;
1497c478bd9Sstevel@tonic-gate 	pcp->pc_eoffset = offset;
1507c478bd9Sstevel@tonic-gate 	pcp->pc_scluster = scluster;
1517c478bd9Sstevel@tonic-gate 	pcp->pc_lcluster = scluster;
1527c478bd9Sstevel@tonic-gate 	pcp->pc_lindex = 0;
1537c478bd9Sstevel@tonic-gate 	pcp->pc_flags = 0;
1547c478bd9Sstevel@tonic-gate 	if (ep->pcd_attr & PCA_DIR) {
1557c478bd9Sstevel@tonic-gate 		vn_setops(vp, pcfs_dvnodeops);
1567c478bd9Sstevel@tonic-gate 		vp->v_type = VDIR;
1577c478bd9Sstevel@tonic-gate 		if (scluster == 0) {
1587c478bd9Sstevel@tonic-gate 			vp->v_flag = VROOT;
1597c478bd9Sstevel@tonic-gate 			blkno = offset = 0;
1607c478bd9Sstevel@tonic-gate 			if (IS_FAT32(fsp)) {
1619bd42341Sfrankho 				pc_cluster32_t ncl = 0;
1629bd42341Sfrankho 
1639bd42341Sfrankho 				scluster = fsp->pcfs_rdirstart;
1649bd42341Sfrankho 				if (pc_fileclsize(fsp, scluster, &ncl)) {
1659bd42341Sfrankho 					PC_DPRINTF1(2, "cluster chain "
1669bd42341Sfrankho 					    "corruption, scluster=%d\n",
1679bd42341Sfrankho 					    scluster);
1689bd42341Sfrankho 					pcp->pc_flags |= PC_INVAL;
1699bd42341Sfrankho 				}
1709bd42341Sfrankho 				pcp->pc_size = fsp->pcfs_clsize * ncl;
1717c478bd9Sstevel@tonic-gate 			} else {
1727c478bd9Sstevel@tonic-gate 				pcp->pc_size =
1737c478bd9Sstevel@tonic-gate 				    fsp->pcfs_rdirsec * fsp->pcfs_secsize;
1747c478bd9Sstevel@tonic-gate 			}
1759bd42341Sfrankho 		} else {
1769bd42341Sfrankho 			pc_cluster32_t ncl = 0;
1779bd42341Sfrankho 
1789bd42341Sfrankho 			if (pc_fileclsize(fsp, scluster, &ncl)) {
1799bd42341Sfrankho 				PC_DPRINTF1(2, "cluster chain corruption, "
1809bd42341Sfrankho 				    "scluster=%d\n", scluster);
1819bd42341Sfrankho 				pcp->pc_flags |= PC_INVAL;
1829bd42341Sfrankho 			}
1839bd42341Sfrankho 			pcp->pc_size = fsp->pcfs_clsize * ncl;
1849bd42341Sfrankho 		}
1857c478bd9Sstevel@tonic-gate 	} else {
1867c478bd9Sstevel@tonic-gate 		vn_setops(vp, pcfs_fvnodeops);
1877c478bd9Sstevel@tonic-gate 		vp->v_type = VREG;
1887c478bd9Sstevel@tonic-gate 		vp->v_flag = VNOSWAP;
1897c478bd9Sstevel@tonic-gate 		fsp->pcfs_frefs++;
1907c478bd9Sstevel@tonic-gate 		pcp->pc_size = ltohi(ep->pcd_size);
1917c478bd9Sstevel@tonic-gate 	}
1927c478bd9Sstevel@tonic-gate 	fsp->pcfs_nrefs++;
193264a6e74Sfrankho 	VFS_HOLD(PCFSTOVFS(fsp));
1947c478bd9Sstevel@tonic-gate 	vp->v_data = (caddr_t)pcp;
1957c478bd9Sstevel@tonic-gate 	vp->v_vfsp = PCFSTOVFS(fsp);
1967c478bd9Sstevel@tonic-gate 	vn_exists(vp);
1977c478bd9Sstevel@tonic-gate 	rw_enter(&pcnodes_lock, RW_WRITER);
1987c478bd9Sstevel@tonic-gate 	insque(pcp, hp);
1997c478bd9Sstevel@tonic-gate 	rw_exit(&pcnodes_lock);
2007c478bd9Sstevel@tonic-gate 	return (pcp);
2017c478bd9Sstevel@tonic-gate }
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate int
syncpcp(struct pcnode * pcp,int flags)2047c478bd9Sstevel@tonic-gate syncpcp(struct pcnode *pcp, int flags)
2057c478bd9Sstevel@tonic-gate {
2067c478bd9Sstevel@tonic-gate 	int err;
2077c478bd9Sstevel@tonic-gate 	if (!vn_has_cached_data(PCTOV(pcp)))
2087c478bd9Sstevel@tonic-gate 		err = 0;
2097c478bd9Sstevel@tonic-gate 	else
210*da6c28aaSamw 		err = VOP_PUTPAGE(PCTOV(pcp), 0, 0, flags,
211*da6c28aaSamw 		    kcred, NULL);
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 	return (err);
2147c478bd9Sstevel@tonic-gate }
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate void
pc_rele(struct pcnode * pcp)2177c478bd9Sstevel@tonic-gate pc_rele(struct pcnode *pcp)
2187c478bd9Sstevel@tonic-gate {
2197c478bd9Sstevel@tonic-gate 	struct pcfs *fsp;
2207c478bd9Sstevel@tonic-gate 	struct vnode *vp;
2217c478bd9Sstevel@tonic-gate 	int err;
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 	vp = PCTOV(pcp);
2247c478bd9Sstevel@tonic-gate 	PC_DPRINTF1(8, "pc_rele vp=0x%p\n", (void *)vp);
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
2277c478bd9Sstevel@tonic-gate 	ASSERT(fsp->pcfs_flags & PCFS_LOCKED);
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate 	rw_enter(&pcnodes_lock, RW_WRITER);
2307c478bd9Sstevel@tonic-gate 	pcp->pc_flags |= PC_RELEHOLD;
2317c478bd9Sstevel@tonic-gate 
2327c478bd9Sstevel@tonic-gate retry:
2337c478bd9Sstevel@tonic-gate 	if (vp->v_type != VDIR && (pcp->pc_flags & PC_INVAL) == 0) {
2347c478bd9Sstevel@tonic-gate 		/*
2357c478bd9Sstevel@tonic-gate 		 * If the file was removed while active it may be safely
2367c478bd9Sstevel@tonic-gate 		 * truncated now.
2377c478bd9Sstevel@tonic-gate 		 */
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate 		if (pcp->pc_entry.pcd_filename[0] == PCD_ERASED) {
2407c478bd9Sstevel@tonic-gate 			(void) pc_truncate(pcp, 0);
2417c478bd9Sstevel@tonic-gate 		} else if (pcp->pc_flags & PC_CHG) {
2427c478bd9Sstevel@tonic-gate 			(void) pc_nodeupdate(pcp);
2437c478bd9Sstevel@tonic-gate 		}
2447c478bd9Sstevel@tonic-gate 		err = syncpcp(pcp, B_INVAL);
2457c478bd9Sstevel@tonic-gate 		if (err) {
2467c478bd9Sstevel@tonic-gate 			(void) syncpcp(pcp, B_INVAL | B_FORCE);
2477c478bd9Sstevel@tonic-gate 		}
2487c478bd9Sstevel@tonic-gate 	}
2497c478bd9Sstevel@tonic-gate 	if (vn_has_cached_data(vp)) {
2507c478bd9Sstevel@tonic-gate 		/*
2517c478bd9Sstevel@tonic-gate 		 * pvn_vplist_dirty will abort all old pages
2527c478bd9Sstevel@tonic-gate 		 */
2537c478bd9Sstevel@tonic-gate 		(void) pvn_vplist_dirty(vp, (u_offset_t)0,
2547c478bd9Sstevel@tonic-gate 		    pcfs_putapage, B_INVAL, (struct cred *)NULL);
2557c478bd9Sstevel@tonic-gate 	}
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 	(void) pc_syncfat(fsp);
2587c478bd9Sstevel@tonic-gate 	mutex_enter(&vp->v_lock);
2597c478bd9Sstevel@tonic-gate 	if (vn_has_cached_data(vp)) {
2607c478bd9Sstevel@tonic-gate 		mutex_exit(&vp->v_lock);
2617c478bd9Sstevel@tonic-gate 		goto retry;
2627c478bd9Sstevel@tonic-gate 	}
2637c478bd9Sstevel@tonic-gate 	ASSERT(!vn_has_cached_data(vp));
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate 	vp->v_count--;  /* release our hold from vn_rele */
2667c478bd9Sstevel@tonic-gate 	if (vp->v_count > 0) { /* Is this check still needed? */
2677c478bd9Sstevel@tonic-gate 		PC_DPRINTF1(3, "pc_rele: pcp=0x%p HELD AGAIN!\n", (void *)pcp);
2687c478bd9Sstevel@tonic-gate 		mutex_exit(&vp->v_lock);
2697c478bd9Sstevel@tonic-gate 		pcp->pc_flags &= ~PC_RELEHOLD;
2707c478bd9Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
2717c478bd9Sstevel@tonic-gate 		return;
2727c478bd9Sstevel@tonic-gate 	}
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 	remque(pcp);
2757c478bd9Sstevel@tonic-gate 	rw_exit(&pcnodes_lock);
2769bd42341Sfrankho 	/*
2779bd42341Sfrankho 	 * XXX - old code had a check for !(pcp->pc_flags & PC_INVAL)
2789bd42341Sfrankho 	 * here. Seems superfluous/incorrect, but then earlier on PC_INVAL
2799bd42341Sfrankho 	 * was never set anywhere in PCFS. Now it is, and we _have_ to drop
2809bd42341Sfrankho 	 * the file reference here. Else, we'd screw up umount/modunload.
2819bd42341Sfrankho 	 */
2829bd42341Sfrankho 	if ((vp->v_type == VREG)) {
2837c478bd9Sstevel@tonic-gate 		fsp->pcfs_frefs--;
2847c478bd9Sstevel@tonic-gate 	}
2857c478bd9Sstevel@tonic-gate 	fsp->pcfs_nrefs--;
286264a6e74Sfrankho 	VFS_RELE(vp->v_vfsp);
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate 	if (fsp->pcfs_nrefs < 0) {
2897c478bd9Sstevel@tonic-gate 		panic("pc_rele: nrefs count");
2907c478bd9Sstevel@tonic-gate 	}
2917c478bd9Sstevel@tonic-gate 	if (fsp->pcfs_frefs < 0) {
2927c478bd9Sstevel@tonic-gate 		panic("pc_rele: frefs count");
2937c478bd9Sstevel@tonic-gate 	}
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate 	mutex_exit(&vp->v_lock);
2967c478bd9Sstevel@tonic-gate 	vn_invalid(vp);
2977c478bd9Sstevel@tonic-gate 	vn_free(vp);
2987c478bd9Sstevel@tonic-gate 	kmem_free(pcp, sizeof (struct pcnode));
2997c478bd9Sstevel@tonic-gate }
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate /*
3027c478bd9Sstevel@tonic-gate  * Mark a pcnode as modified with the current time.
3037c478bd9Sstevel@tonic-gate  */
304f127cb91Sfrankho /* ARGSUSED */
3057c478bd9Sstevel@tonic-gate void
pc_mark_mod(struct pcfs * fsp,struct pcnode * pcp)306f127cb91Sfrankho pc_mark_mod(struct pcfs *fsp, struct pcnode *pcp)
3077c478bd9Sstevel@tonic-gate {
3087c478bd9Sstevel@tonic-gate 	timestruc_t now;
3097c478bd9Sstevel@tonic-gate 
310f127cb91Sfrankho 	if (PCTOV(pcp)->v_type == VDIR)
311f127cb91Sfrankho 		return;
312f127cb91Sfrankho 
313f127cb91Sfrankho 	ASSERT(PCTOV(pcp)->v_type == VREG);
314f127cb91Sfrankho 
3157c478bd9Sstevel@tonic-gate 	gethrestime(&now);
316264a6e74Sfrankho 	if (pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime))
317264a6e74Sfrankho 		PC_DPRINTF1(2, "pc_mark_mod failed timestamp "
318264a6e74Sfrankho 		    "conversion, curtime = %lld\n",
319264a6e74Sfrankho 		    (long long)now.tv_sec);
320f127cb91Sfrankho 
3217c478bd9Sstevel@tonic-gate 	pcp->pc_flags |= PC_CHG;
3227c478bd9Sstevel@tonic-gate }
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate /*
3257c478bd9Sstevel@tonic-gate  * Mark a pcnode as accessed with the current time.
3267c478bd9Sstevel@tonic-gate  */
3277c478bd9Sstevel@tonic-gate void
pc_mark_acc(struct pcfs * fsp,struct pcnode * pcp)328f127cb91Sfrankho pc_mark_acc(struct pcfs *fsp, struct pcnode *pcp)
3297c478bd9Sstevel@tonic-gate {
330264a6e74Sfrankho 	struct pctime pt = { 0, 0 };
3317c478bd9Sstevel@tonic-gate 	timestruc_t now;
3327c478bd9Sstevel@tonic-gate 
333f127cb91Sfrankho 	if (fsp->pcfs_flags & PCFS_NOATIME || PCTOV(pcp)->v_type == VDIR)
334f127cb91Sfrankho 		return;
335f127cb91Sfrankho 
336f127cb91Sfrankho 	ASSERT(PCTOV(pcp)->v_type == VREG);
337f127cb91Sfrankho 
3387c478bd9Sstevel@tonic-gate 	gethrestime(&now);
339f127cb91Sfrankho 	if (pc_tvtopct(&now, &pt)) {
340264a6e74Sfrankho 		PC_DPRINTF1(2, "pc_mark_acc failed timestamp "
341264a6e74Sfrankho 		    "conversion, curtime = %lld\n",
342264a6e74Sfrankho 		    (long long)now.tv_sec);
343f127cb91Sfrankho 		return;
344f127cb91Sfrankho 	}
345f127cb91Sfrankho 
346f127cb91Sfrankho 	/*
347f127cb91Sfrankho 	 * We don't really want to write the adate for every access
348f127cb91Sfrankho 	 * on flash media; make sure it really changed !
349f127cb91Sfrankho 	 */
350f127cb91Sfrankho 	if (pcp->pc_entry.pcd_ladate != pt.pct_date) {
3517c478bd9Sstevel@tonic-gate 		pcp->pc_entry.pcd_ladate = pt.pct_date;
352f127cb91Sfrankho 		pcp->pc_flags |= (PC_CHG | PC_ACC);
3537c478bd9Sstevel@tonic-gate 	}
3547c478bd9Sstevel@tonic-gate }
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate /*
3577c478bd9Sstevel@tonic-gate  * Truncate a file to a length.
3587c478bd9Sstevel@tonic-gate  * Node must be locked.
3597c478bd9Sstevel@tonic-gate  */
3607c478bd9Sstevel@tonic-gate int
pc_truncate(struct pcnode * pcp,uint_t length)3617c478bd9Sstevel@tonic-gate pc_truncate(struct pcnode *pcp, uint_t length)
3627c478bd9Sstevel@tonic-gate {
3637c478bd9Sstevel@tonic-gate 	struct pcfs *fsp;
3647c478bd9Sstevel@tonic-gate 	struct vnode *vp;
3657c478bd9Sstevel@tonic-gate 	int error = 0;
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 	PC_DPRINTF3(4, "pc_truncate pcp=0x%p, len=%u, size=%u\n",
3687c478bd9Sstevel@tonic-gate 	    (void *)pcp, length, pcp->pc_size);
3697c478bd9Sstevel@tonic-gate 	vp = PCTOV(pcp);
3707c478bd9Sstevel@tonic-gate 	if (pcp->pc_flags & PC_INVAL)
3717c478bd9Sstevel@tonic-gate 		return (EIO);
3727c478bd9Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
3737c478bd9Sstevel@tonic-gate 	/*
3747c478bd9Sstevel@tonic-gate 	 * directories are always truncated to zero and are not marked
3757c478bd9Sstevel@tonic-gate 	 */
3767c478bd9Sstevel@tonic-gate 	if (vp->v_type == VDIR) {
3777c478bd9Sstevel@tonic-gate 		error = pc_bfree(pcp, 0);
3787c478bd9Sstevel@tonic-gate 		return (error);
3797c478bd9Sstevel@tonic-gate 	}
3807c478bd9Sstevel@tonic-gate 	/*
3817c478bd9Sstevel@tonic-gate 	 * If length is the same as the current size
3827c478bd9Sstevel@tonic-gate 	 * just mark the pcnode and return.
3837c478bd9Sstevel@tonic-gate 	 */
3847c478bd9Sstevel@tonic-gate 	if (length > pcp->pc_size) {
3857c478bd9Sstevel@tonic-gate 		daddr_t bno;
386f127cb91Sfrankho 		uint_t llcn = howmany((offset_t)length, fsp->pcfs_clsize);
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate 		/*
3897c478bd9Sstevel@tonic-gate 		 * We are extending a file.
3907c478bd9Sstevel@tonic-gate 		 * Extend it with _one_ call to pc_balloc (no holes)
3917c478bd9Sstevel@tonic-gate 		 * since we don't need to use the block number(s).
3927c478bd9Sstevel@tonic-gate 		 */
3937c478bd9Sstevel@tonic-gate 		if ((daddr_t)howmany((offset_t)pcp->pc_size, fsp->pcfs_clsize) <
394f127cb91Sfrankho 		    (daddr_t)llcn) {
3957c478bd9Sstevel@tonic-gate 			error = pc_balloc(pcp, (daddr_t)(llcn - 1), 1, &bno);
3967c478bd9Sstevel@tonic-gate 		}
3977c478bd9Sstevel@tonic-gate 		if (error) {
3989bd42341Sfrankho 			pc_cluster32_t ncl = 0;
3997c478bd9Sstevel@tonic-gate 			PC_DPRINTF1(2, "pc_truncate: error=%d\n", error);
4007c478bd9Sstevel@tonic-gate 			/*
4017c478bd9Sstevel@tonic-gate 			 * probably ran out disk space;
4027c478bd9Sstevel@tonic-gate 			 * determine current file size
4037c478bd9Sstevel@tonic-gate 			 */
4049bd42341Sfrankho 			if (pc_fileclsize(fsp, pcp->pc_scluster, &ncl)) {
4059bd42341Sfrankho 				PC_DPRINTF1(2, "cluster chain corruption, "
4069bd42341Sfrankho 				    "scluster=%d\n", pcp->pc_scluster);
4079bd42341Sfrankho 				pcp->pc_flags |= PC_INVAL;
4089bd42341Sfrankho 			}
4099bd42341Sfrankho 			pcp->pc_size = fsp->pcfs_clsize * ncl;
4107c478bd9Sstevel@tonic-gate 		} else
4117c478bd9Sstevel@tonic-gate 			pcp->pc_size = length;
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 	} else if (length < pcp->pc_size) {
4147c478bd9Sstevel@tonic-gate 		/*
4157c478bd9Sstevel@tonic-gate 		 * We are shrinking a file.
4167c478bd9Sstevel@tonic-gate 		 * Free blocks after the block that length points to.
4177c478bd9Sstevel@tonic-gate 		 */
4187c478bd9Sstevel@tonic-gate 		if (pc_blkoff(fsp, length) == 0) {
4197c478bd9Sstevel@tonic-gate 			/*
4207c478bd9Sstevel@tonic-gate 			 * Truncation to a block (cluster size) boundary only
4217c478bd9Sstevel@tonic-gate 			 * requires us to invalidate everything after the new
4227c478bd9Sstevel@tonic-gate 			 * end of the file.
4237c478bd9Sstevel@tonic-gate 			 */
4247c478bd9Sstevel@tonic-gate 			(void) pvn_vplist_dirty(PCTOV(pcp), (u_offset_t)length,
4257c478bd9Sstevel@tonic-gate 			    pcfs_putapage, B_INVAL | B_TRUNC, CRED());
4267c478bd9Sstevel@tonic-gate 		} else {
4277c478bd9Sstevel@tonic-gate 			/*
4287c478bd9Sstevel@tonic-gate 			 * pvn_vpzero() cannot deal with more than MAXBSIZE
4297c478bd9Sstevel@tonic-gate 			 * chunks. Since the FAT clustersize can get larger
4307c478bd9Sstevel@tonic-gate 			 * than that, we'll zero from the new length to the
4317c478bd9Sstevel@tonic-gate 			 * end of the cluster for clustersizes smaller than
4327c478bd9Sstevel@tonic-gate 			 * MAXBSIZE - or the end of the MAXBSIZE block in
4337c478bd9Sstevel@tonic-gate 			 * case we've got a large clustersize.
4347c478bd9Sstevel@tonic-gate 			 */
4357c478bd9Sstevel@tonic-gate 			size_t nbytes =
4367c478bd9Sstevel@tonic-gate 			    roundup(length, MIN(fsp->pcfs_clsize, MAXBSIZE)) -
4377c478bd9Sstevel@tonic-gate 			    length;
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 			pvn_vpzero(PCTOV(pcp), (u_offset_t)length, nbytes);
4407c478bd9Sstevel@tonic-gate 			(void) pvn_vplist_dirty(PCTOV(pcp),
4417c478bd9Sstevel@tonic-gate 			    (u_offset_t)length + nbytes,
4427c478bd9Sstevel@tonic-gate 			    pcfs_putapage, B_INVAL | B_TRUNC, CRED());
4437c478bd9Sstevel@tonic-gate 		}
444f127cb91Sfrankho 		error = pc_bfree(pcp, (pc_cluster32_t)
445f127cb91Sfrankho 		    howmany((offset_t)length, fsp->pcfs_clsize));
4467c478bd9Sstevel@tonic-gate 		pcp->pc_size = length;
4477c478bd9Sstevel@tonic-gate 	}
448f127cb91Sfrankho 
449f127cb91Sfrankho 	/*
450f127cb91Sfrankho 	 * This is the only place in PCFS code where pc_mark_mod() is called
451f127cb91Sfrankho 	 * without setting PC_MOD. May be a historical artifact ...
452f127cb91Sfrankho 	 */
453f127cb91Sfrankho 	pc_mark_mod(fsp, pcp);
4547c478bd9Sstevel@tonic-gate 	return (error);
4557c478bd9Sstevel@tonic-gate }
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate /*
4587c478bd9Sstevel@tonic-gate  * Get block for entry.
4597c478bd9Sstevel@tonic-gate  */
4607c478bd9Sstevel@tonic-gate static int
pc_getentryblock(struct pcnode * pcp,struct buf ** bpp)4617c478bd9Sstevel@tonic-gate pc_getentryblock(struct pcnode *pcp, struct buf **bpp)
4627c478bd9Sstevel@tonic-gate {
4637c478bd9Sstevel@tonic-gate 	struct pcfs *fsp;
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 	fsp = VFSTOPCFS(PCTOV(pcp)->v_vfsp);
4667c478bd9Sstevel@tonic-gate 	if (pcp->pc_eblkno >= fsp->pcfs_datastart ||
4677c478bd9Sstevel@tonic-gate 	    (pcp->pc_eblkno - fsp->pcfs_rdirstart) <
4687c478bd9Sstevel@tonic-gate 	    (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
4697c478bd9Sstevel@tonic-gate 		*bpp = bread(fsp->pcfs_xdev,
4707c478bd9Sstevel@tonic-gate 		    pc_dbdaddr(fsp, pcp->pc_eblkno), fsp->pcfs_clsize);
4717c478bd9Sstevel@tonic-gate 	} else {
4727c478bd9Sstevel@tonic-gate 		*bpp = bread(fsp->pcfs_xdev,
4737c478bd9Sstevel@tonic-gate 		    pc_dbdaddr(fsp, pcp->pc_eblkno),
4747c478bd9Sstevel@tonic-gate 		    (int)(fsp->pcfs_datastart - pcp->pc_eblkno) *
4757c478bd9Sstevel@tonic-gate 		    fsp->pcfs_secsize);
4767c478bd9Sstevel@tonic-gate 	}
4777c478bd9Sstevel@tonic-gate 	if ((*bpp)->b_flags & B_ERROR) {
4787c478bd9Sstevel@tonic-gate 		brelse(*bpp);
4797c478bd9Sstevel@tonic-gate 		pc_mark_irrecov(fsp);
4807c478bd9Sstevel@tonic-gate 		return (EIO);
4817c478bd9Sstevel@tonic-gate 	}
4827c478bd9Sstevel@tonic-gate 	return (0);
4837c478bd9Sstevel@tonic-gate }
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate /*
4867c478bd9Sstevel@tonic-gate  * Sync all data associated with a file.
4877c478bd9Sstevel@tonic-gate  * Flush all the blocks in the buffer cache out to disk, sync the FAT and
4887c478bd9Sstevel@tonic-gate  * update the directory entry.
4897c478bd9Sstevel@tonic-gate  */
4907c478bd9Sstevel@tonic-gate int
pc_nodesync(struct pcnode * pcp)4917c478bd9Sstevel@tonic-gate pc_nodesync(struct pcnode *pcp)
4927c478bd9Sstevel@tonic-gate {
4937c478bd9Sstevel@tonic-gate 	struct pcfs *fsp;
4947c478bd9Sstevel@tonic-gate 	int err;
4957c478bd9Sstevel@tonic-gate 	struct vnode *vp;
4967c478bd9Sstevel@tonic-gate 
4977c478bd9Sstevel@tonic-gate 	vp = PCTOV(pcp);
4987c478bd9Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
4997c478bd9Sstevel@tonic-gate 	err = 0;
5007c478bd9Sstevel@tonic-gate 	if (pcp->pc_flags & PC_MOD) {
5017c478bd9Sstevel@tonic-gate 		/*
5027c478bd9Sstevel@tonic-gate 		 * Flush all data blocks from buffer cache and
5037c478bd9Sstevel@tonic-gate 		 * update the FAT which points to the data.
5047c478bd9Sstevel@tonic-gate 		 */
5057c478bd9Sstevel@tonic-gate 		if (err = syncpcp(pcp, 0)) { /* %% ?? how to handle error? */
5067c478bd9Sstevel@tonic-gate 			if (err == ENOMEM)
5077c478bd9Sstevel@tonic-gate 				return (err);
5087c478bd9Sstevel@tonic-gate 			else {
5097c478bd9Sstevel@tonic-gate 				pc_mark_irrecov(fsp);
5107c478bd9Sstevel@tonic-gate 				return (EIO);
5117c478bd9Sstevel@tonic-gate 			}
5127c478bd9Sstevel@tonic-gate 		}
5137c478bd9Sstevel@tonic-gate 		pcp->pc_flags &= ~PC_MOD;
5147c478bd9Sstevel@tonic-gate 	}
5157c478bd9Sstevel@tonic-gate 	/*
5167c478bd9Sstevel@tonic-gate 	 * update the directory entry
5177c478bd9Sstevel@tonic-gate 	 */
5187c478bd9Sstevel@tonic-gate 	if (pcp->pc_flags & PC_CHG)
5197c478bd9Sstevel@tonic-gate 		(void) pc_nodeupdate(pcp);
5207c478bd9Sstevel@tonic-gate 	return (err);
5217c478bd9Sstevel@tonic-gate }
5227c478bd9Sstevel@tonic-gate 
5237c478bd9Sstevel@tonic-gate /*
5247c478bd9Sstevel@tonic-gate  * Update the node's directory entry.
5257c478bd9Sstevel@tonic-gate  */
5267c478bd9Sstevel@tonic-gate int
pc_nodeupdate(struct pcnode * pcp)5277c478bd9Sstevel@tonic-gate pc_nodeupdate(struct pcnode *pcp)
5287c478bd9Sstevel@tonic-gate {
5297c478bd9Sstevel@tonic-gate 	struct buf *bp;
5307c478bd9Sstevel@tonic-gate 	int error;
5317c478bd9Sstevel@tonic-gate 	struct vnode *vp;
5327c478bd9Sstevel@tonic-gate 	struct pcfs *fsp;
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate 	vp = PCTOV(pcp);
5357c478bd9Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
5367c478bd9Sstevel@tonic-gate 	if (IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
5377c478bd9Sstevel@tonic-gate 		/* no node to update */
5387c478bd9Sstevel@tonic-gate 		pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC);
5397c478bd9Sstevel@tonic-gate 		return (0);
5407c478bd9Sstevel@tonic-gate 	}
5417c478bd9Sstevel@tonic-gate 	if (vp->v_flag & VROOT) {
5427c478bd9Sstevel@tonic-gate 		panic("pc_nodeupdate");
5437c478bd9Sstevel@tonic-gate 	}
5447c478bd9Sstevel@tonic-gate 	if (pcp->pc_flags & PC_INVAL)
5457c478bd9Sstevel@tonic-gate 		return (0);
5467c478bd9Sstevel@tonic-gate 	PC_DPRINTF3(7, "pc_nodeupdate pcp=0x%p, bn=%ld, off=%d\n", (void *)pcp,
5477c478bd9Sstevel@tonic-gate 	    pcp->pc_eblkno, pcp->pc_eoffset);
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 	if (error = pc_getentryblock(pcp, &bp)) {
5507c478bd9Sstevel@tonic-gate 		return (error);
5517c478bd9Sstevel@tonic-gate 	}
5527c478bd9Sstevel@tonic-gate 	if (vp->v_type == VREG) {
5537c478bd9Sstevel@tonic-gate 		if (pcp->pc_flags & PC_CHG)
5547c478bd9Sstevel@tonic-gate 			pcp->pc_entry.pcd_attr |= PCA_ARCH;
5557c478bd9Sstevel@tonic-gate 		pcp->pc_entry.pcd_size = htoli(pcp->pc_size);
5567c478bd9Sstevel@tonic-gate 	}
5577c478bd9Sstevel@tonic-gate 	pc_setstartcluster(fsp, &pcp->pc_entry, pcp->pc_scluster);
5587c478bd9Sstevel@tonic-gate 	*((struct pcdir *)(bp->b_un.b_addr + pcp->pc_eoffset)) = pcp->pc_entry;
5597c478bd9Sstevel@tonic-gate 	bwrite2(bp);
5607c478bd9Sstevel@tonic-gate 	error = geterror(bp);
5617c478bd9Sstevel@tonic-gate 	brelse(bp);
5627c478bd9Sstevel@tonic-gate 	if (error) {
563f127cb91Sfrankho 		error = EIO;
5647c478bd9Sstevel@tonic-gate 		pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
5657c478bd9Sstevel@tonic-gate 	}
5667c478bd9Sstevel@tonic-gate 	pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC);
5677c478bd9Sstevel@tonic-gate 	return (error);
5687c478bd9Sstevel@tonic-gate }
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate /*
5717c478bd9Sstevel@tonic-gate  * Verify that the disk in the drive is the same one that we
5727c478bd9Sstevel@tonic-gate  * got the pcnode from.
5737c478bd9Sstevel@tonic-gate  * MUST be called with node unlocked.
5747c478bd9Sstevel@tonic-gate  */
5757c478bd9Sstevel@tonic-gate int
pc_verify(struct pcfs * fsp)5767c478bd9Sstevel@tonic-gate pc_verify(struct pcfs *fsp)
5777c478bd9Sstevel@tonic-gate {
5787c478bd9Sstevel@tonic-gate 	int fdstatus = 0;
5797c478bd9Sstevel@tonic-gate 	int error = 0;
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate 	if (!fsp || fsp->pcfs_flags & PCFS_IRRECOV)
5827c478bd9Sstevel@tonic-gate 		return (EIO);
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate 	if (!(fsp->pcfs_flags & PCFS_NOCHK) && fsp->pcfs_fatp) {
585f127cb91Sfrankho 		/*
586f127cb91Sfrankho 		 * This "has it been removed" check should better be
587f127cb91Sfrankho 		 * modified for removeable media that are not floppies.
588f127cb91Sfrankho 		 * dkio-managed devices such as USB/firewire external
589f127cb91Sfrankho 		 * disks/memory sticks/floppies (gasp) do not understand
590f127cb91Sfrankho 		 * this ioctl.
591f127cb91Sfrankho 		 */
5927c478bd9Sstevel@tonic-gate 		PC_DPRINTF1(4, "pc_verify fsp=0x%p\n", (void *)fsp);
5937c478bd9Sstevel@tonic-gate 		error = cdev_ioctl(fsp->pcfs_vfs->vfs_dev,
5947c478bd9Sstevel@tonic-gate 		    FDGETCHANGE, (intptr_t)&fdstatus, FNATIVE | FKIOCTL,
5957c478bd9Sstevel@tonic-gate 		    NULL, NULL);
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate 		if (error) {
5987c478bd9Sstevel@tonic-gate 			if (error == ENOTTY || error == ENXIO) {
599f127cb91Sfrankho 				/*
600f127cb91Sfrankho 				 * See comment above. This is a workaround
601f127cb91Sfrankho 				 * for removeable media that don't understand
602f127cb91Sfrankho 				 * floppy ioctls.
603f127cb91Sfrankho 				 */
6047c478bd9Sstevel@tonic-gate 				error = 0;
6057c478bd9Sstevel@tonic-gate 			} else {
6067c478bd9Sstevel@tonic-gate 				PC_DPRINTF1(1,
6077c478bd9Sstevel@tonic-gate 				    "pc_verify: FDGETCHANGE ioctl failed: %d\n",
6087c478bd9Sstevel@tonic-gate 				    error);
6097c478bd9Sstevel@tonic-gate 				pc_mark_irrecov(fsp);
6107c478bd9Sstevel@tonic-gate 			}
6117c478bd9Sstevel@tonic-gate 		} else if (fsp->pcfs_fatjustread) {
6127c478bd9Sstevel@tonic-gate 			/*
6137c478bd9Sstevel@tonic-gate 			 * Ignore the results of the ioctl if we just
6147c478bd9Sstevel@tonic-gate 			 * read the FAT.  There is a good chance that
6157c478bd9Sstevel@tonic-gate 			 * the disk changed bit will be on, because
6167c478bd9Sstevel@tonic-gate 			 * we've just mounted and we don't want to
6177c478bd9Sstevel@tonic-gate 			 * give a false positive that the sky is falling.
6187c478bd9Sstevel@tonic-gate 			 */
6197c478bd9Sstevel@tonic-gate 			fsp->pcfs_fatjustread = 0;
6207c478bd9Sstevel@tonic-gate 		} else {
6217c478bd9Sstevel@tonic-gate 			/*
6227c478bd9Sstevel@tonic-gate 			 * Oddly enough we can't check just one flag here. The
6237c478bd9Sstevel@tonic-gate 			 * x86 floppy driver sets a different flag
6247c478bd9Sstevel@tonic-gate 			 * (FDGC_DETECTED) than the sparc driver does.
6257c478bd9Sstevel@tonic-gate 			 * I think this MAY be a bug, and I filed 4165938
6267c478bd9Sstevel@tonic-gate 			 * to get someone to look at the behavior
6277c478bd9Sstevel@tonic-gate 			 * a bit more closely.  In the meantime, my testing and
6287c478bd9Sstevel@tonic-gate 			 * code examination seem to indicate it is safe to
6297c478bd9Sstevel@tonic-gate 			 * check for either bit being set.
6307c478bd9Sstevel@tonic-gate 			 */
6317c478bd9Sstevel@tonic-gate 			if (fdstatus & (FDGC_HISTORY | FDGC_DETECTED)) {
6327c478bd9Sstevel@tonic-gate 				PC_DPRINTF0(1, "pc_verify: change detected\n");
6337c478bd9Sstevel@tonic-gate 				pc_mark_irrecov(fsp);
6347c478bd9Sstevel@tonic-gate 			}
6357c478bd9Sstevel@tonic-gate 		}
6367c478bd9Sstevel@tonic-gate 	}
637f127cb91Sfrankho 	if (error == 0 && fsp->pcfs_fatp == NULL) {
6387c478bd9Sstevel@tonic-gate 		error = pc_getfat(fsp);
6397c478bd9Sstevel@tonic-gate 	}
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate 	return (error);
6427c478bd9Sstevel@tonic-gate }
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate /*
6457c478bd9Sstevel@tonic-gate  * The disk has changed, pulling the rug out from beneath us.
6467c478bd9Sstevel@tonic-gate  * Mark the FS as being in an irrecoverable state.
6477c478bd9Sstevel@tonic-gate  * In a short while we'll clean up.
6487c478bd9Sstevel@tonic-gate  */
6497c478bd9Sstevel@tonic-gate void
pc_mark_irrecov(struct pcfs * fsp)6507c478bd9Sstevel@tonic-gate pc_mark_irrecov(struct pcfs *fsp)
6517c478bd9Sstevel@tonic-gate {
6527c478bd9Sstevel@tonic-gate 	if (!(fsp->pcfs_flags & PCFS_NOCHK)) {
6537c478bd9Sstevel@tonic-gate 		if (pc_lockfs(fsp, 1, 0)) {
6547c478bd9Sstevel@tonic-gate 			/*
6557c478bd9Sstevel@tonic-gate 			 * Locking failed, which currently would
6567c478bd9Sstevel@tonic-gate 			 * only happen if the FS were already
6577c478bd9Sstevel@tonic-gate 			 * marked as hosed.  If another reason for
6587c478bd9Sstevel@tonic-gate 			 * failure were to arise in the future, this
6597c478bd9Sstevel@tonic-gate 			 * routine would have to change.
6607c478bd9Sstevel@tonic-gate 			 */
6617c478bd9Sstevel@tonic-gate 			return;
6627c478bd9Sstevel@tonic-gate 		}
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_IRRECOV;
6657c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN,
6667c478bd9Sstevel@tonic-gate 		    "Disk was changed during an update or\n"
6677c478bd9Sstevel@tonic-gate 		    "an irrecoverable error was encountered.\n"
6687c478bd9Sstevel@tonic-gate 		    "File damage is possible.  To prevent further\n"
6697c478bd9Sstevel@tonic-gate 		    "damage, this pcfs instance will now be frozen.\n"
6707c478bd9Sstevel@tonic-gate 		    "Use umount(1M) to release the instance.\n");
6717c478bd9Sstevel@tonic-gate 		(void) pc_unlockfs(fsp);
6727c478bd9Sstevel@tonic-gate 	}
6737c478bd9Sstevel@tonic-gate }
6747c478bd9Sstevel@tonic-gate 
6757c478bd9Sstevel@tonic-gate /*
6767c478bd9Sstevel@tonic-gate  * The disk has been changed!
6777c478bd9Sstevel@tonic-gate  */
6787c478bd9Sstevel@tonic-gate void
pc_diskchanged(struct pcfs * fsp)6797c478bd9Sstevel@tonic-gate pc_diskchanged(struct pcfs *fsp)
6807c478bd9Sstevel@tonic-gate {
6817c478bd9Sstevel@tonic-gate 	struct pcnode	*pcp, *npcp = NULL;
6827c478bd9Sstevel@tonic-gate 	struct pchead	*hp;
6837c478bd9Sstevel@tonic-gate 	struct vnode	*vp;
6847c478bd9Sstevel@tonic-gate 	extern vfs_t	EIO_vfs;
685264a6e74Sfrankho 	struct vfs	*vfsp;
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate 	/*
6887c478bd9Sstevel@tonic-gate 	 * Eliminate all pcnodes (dir & file) associated with this fs.
6897c478bd9Sstevel@tonic-gate 	 * If the node is internal, ie, no references outside of
6907c478bd9Sstevel@tonic-gate 	 * pcfs itself, then release the associated vnode structure.
6917c478bd9Sstevel@tonic-gate 	 * Invalidate the in core FAT.
6927c478bd9Sstevel@tonic-gate 	 * Invalidate cached data blocks and blocks waiting for I/O.
6937c478bd9Sstevel@tonic-gate 	 */
6947c478bd9Sstevel@tonic-gate 	PC_DPRINTF1(1, "pc_diskchanged fsp=0x%p\n", (void *)fsp);
6957c478bd9Sstevel@tonic-gate 
696264a6e74Sfrankho 	vfsp = PCFSTOVFS(fsp);
697264a6e74Sfrankho 
6987c478bd9Sstevel@tonic-gate 	for (hp = pcdhead; hp < &pcdhead[NPCHASH]; hp++) {
6997c478bd9Sstevel@tonic-gate 		for (pcp = hp->pch_forw;
7007c478bd9Sstevel@tonic-gate 		    pcp != (struct pcnode *)hp; pcp = npcp) {
7017c478bd9Sstevel@tonic-gate 			npcp = pcp -> pc_forw;
7027c478bd9Sstevel@tonic-gate 			vp = PCTOV(pcp);
703264a6e74Sfrankho 			if ((vp->v_vfsp == vfsp) &&
7047c478bd9Sstevel@tonic-gate 			    !(pcp->pc_flags & PC_RELEHOLD)) {
7057c478bd9Sstevel@tonic-gate 				mutex_enter(&(vp)->v_lock);
7067c478bd9Sstevel@tonic-gate 				if (vp->v_count > 0) {
7077c478bd9Sstevel@tonic-gate 					mutex_exit(&(vp)->v_lock);
7087c478bd9Sstevel@tonic-gate 					continue;
7097c478bd9Sstevel@tonic-gate 				}
7107c478bd9Sstevel@tonic-gate 				mutex_exit(&(vp)->v_lock);
7117c478bd9Sstevel@tonic-gate 				VN_HOLD(vp);
7127c478bd9Sstevel@tonic-gate 				remque(pcp);
7137c478bd9Sstevel@tonic-gate 				vp->v_data = NULL;
7147c478bd9Sstevel@tonic-gate 				vp->v_vfsp = &EIO_vfs;
7157c478bd9Sstevel@tonic-gate 				vp->v_type = VBAD;
7167c478bd9Sstevel@tonic-gate 				VN_RELE(vp);
717264a6e74Sfrankho 				if (!(pcp->pc_flags & PC_EXTERNAL)) {
718264a6e74Sfrankho 					(void) pvn_vplist_dirty(vp,
719264a6e74Sfrankho 					    (u_offset_t)0, pcfs_putapage,
720264a6e74Sfrankho 					    B_INVAL | B_TRUNC,
721264a6e74Sfrankho 					    (struct cred *)NULL);
7227c478bd9Sstevel@tonic-gate 					vn_free(vp);
723264a6e74Sfrankho 				}
7247c478bd9Sstevel@tonic-gate 				kmem_free(pcp, sizeof (struct pcnode));
7257c478bd9Sstevel@tonic-gate 				fsp->pcfs_nrefs --;
726264a6e74Sfrankho 				VFS_RELE(vfsp);
7277c478bd9Sstevel@tonic-gate 			}
7287c478bd9Sstevel@tonic-gate 		}
7297c478bd9Sstevel@tonic-gate 	}
7307c478bd9Sstevel@tonic-gate 	for (hp = pcfhead; fsp->pcfs_frefs && hp < &pcfhead[NPCHASH]; hp++) {
7317c478bd9Sstevel@tonic-gate 		for (pcp = hp->pch_forw; fsp->pcfs_frefs &&
7327c478bd9Sstevel@tonic-gate 		    pcp != (struct pcnode *)hp; pcp = npcp) {
7337c478bd9Sstevel@tonic-gate 			npcp = pcp -> pc_forw;
7347c478bd9Sstevel@tonic-gate 			vp = PCTOV(pcp);
735264a6e74Sfrankho 			if ((vp->v_vfsp == vfsp) &&
7367c478bd9Sstevel@tonic-gate 			    !(pcp->pc_flags & PC_RELEHOLD)) {
7377c478bd9Sstevel@tonic-gate 				mutex_enter(&(vp)->v_lock);
7387c478bd9Sstevel@tonic-gate 				if (vp->v_count > 0) {
7397c478bd9Sstevel@tonic-gate 					mutex_exit(&(vp)->v_lock);
7407c478bd9Sstevel@tonic-gate 					continue;
7417c478bd9Sstevel@tonic-gate 				}
7427c478bd9Sstevel@tonic-gate 				mutex_exit(&(vp)->v_lock);
7437c478bd9Sstevel@tonic-gate 				VN_HOLD(vp);
7447c478bd9Sstevel@tonic-gate 				remque(pcp);
7457c478bd9Sstevel@tonic-gate 				vp->v_data = NULL;
7467c478bd9Sstevel@tonic-gate 				vp->v_vfsp = &EIO_vfs;
7477c478bd9Sstevel@tonic-gate 				vp->v_type = VBAD;
7487c478bd9Sstevel@tonic-gate 				VN_RELE(vp);
749264a6e74Sfrankho 				if (!(pcp->pc_flags & PC_EXTERNAL)) {
750264a6e74Sfrankho 					(void) pvn_vplist_dirty(vp,
751264a6e74Sfrankho 					    (u_offset_t)0, pcfs_putapage,
752264a6e74Sfrankho 					    B_INVAL | B_TRUNC,
753264a6e74Sfrankho 					    (struct cred *)NULL);
7547c478bd9Sstevel@tonic-gate 					vn_free(vp);
755264a6e74Sfrankho 				}
7567c478bd9Sstevel@tonic-gate 				kmem_free(pcp, sizeof (struct pcnode));
7577c478bd9Sstevel@tonic-gate 				fsp->pcfs_frefs--;
7587c478bd9Sstevel@tonic-gate 				fsp->pcfs_nrefs--;
759264a6e74Sfrankho 				VFS_RELE(vfsp);
7607c478bd9Sstevel@tonic-gate 			}
7617c478bd9Sstevel@tonic-gate 		}
7627c478bd9Sstevel@tonic-gate 	}
7637c478bd9Sstevel@tonic-gate #ifdef undef
7647c478bd9Sstevel@tonic-gate 	if (fsp->pcfs_frefs) {
7657c478bd9Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
7667c478bd9Sstevel@tonic-gate 		panic("pc_diskchanged: frefs");
7677c478bd9Sstevel@tonic-gate 	}
7687c478bd9Sstevel@tonic-gate 	if (fsp->pcfs_nrefs) {
7697c478bd9Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
7707c478bd9Sstevel@tonic-gate 		panic("pc_diskchanged: nrefs");
7717c478bd9Sstevel@tonic-gate 	}
7727c478bd9Sstevel@tonic-gate #endif
773264a6e74Sfrankho 	if (!(vfsp->vfs_flag & VFS_UNMOUNTED) &&
774264a6e74Sfrankho 	    fsp->pcfs_fatp != (uchar_t *)0) {
7757c478bd9Sstevel@tonic-gate 		pc_invalfat(fsp);
7767c478bd9Sstevel@tonic-gate 	} else {
7777c478bd9Sstevel@tonic-gate 		binval(fsp->pcfs_xdev);
7787c478bd9Sstevel@tonic-gate 	}
7797c478bd9Sstevel@tonic-gate }
780