xref: /illumos-gate/usr/src/uts/common/fs/pcfs/pc_node.c (revision bbf21555)
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  */
25ade42b55SSebastien Roy /*
26ade42b55SSebastien Roy  * Copyright (c) 2017 by Delphix. All rights reserved.
27ade42b55SSebastien Roy  */
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #include <sys/param.h>
307c478bd9Sstevel@tonic-gate #include <sys/t_lock.h>
317c478bd9Sstevel@tonic-gate #include <sys/errno.h>
327c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
337c478bd9Sstevel@tonic-gate #include <sys/buf.h>
347c478bd9Sstevel@tonic-gate #include <sys/systm.h>
357c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
367c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
377c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
387c478bd9Sstevel@tonic-gate #include <sys/proc.h>
397c478bd9Sstevel@tonic-gate #include <sys/cred.h>
407c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
417c478bd9Sstevel@tonic-gate #include <sys/debug.h>
427c478bd9Sstevel@tonic-gate #include <vm/pvn.h>
437c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h>
447c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
457c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
467c478bd9Sstevel@tonic-gate #include <sys/fs/pc_node.h>
477c478bd9Sstevel@tonic-gate #include <sys/dirent.h>
487c478bd9Sstevel@tonic-gate #include <sys/fdio.h>
497c478bd9Sstevel@tonic-gate #include <sys/file.h>
507c478bd9Sstevel@tonic-gate #include <sys/conf.h>
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate struct pchead pcfhead[NPCHASH];
537c478bd9Sstevel@tonic-gate struct pchead pcdhead[NPCHASH];
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate extern krwlock_t pcnodes_lock;
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate static int	pc_getentryblock(struct pcnode *, struct buf **);
587c478bd9Sstevel@tonic-gate static int	syncpcp(struct pcnode *, int);
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate /*
617c478bd9Sstevel@tonic-gate  * fake entry for root directory, since this does not have a parent
627c478bd9Sstevel@tonic-gate  * pointing to it.
637c478bd9Sstevel@tonic-gate  */
64264a6e74Sfrankho struct pcdir pcfs_rootdirentry = {
657c478bd9Sstevel@tonic-gate 	"",
667c478bd9Sstevel@tonic-gate 	"",
677c478bd9Sstevel@tonic-gate 	PCA_DIR
687c478bd9Sstevel@tonic-gate };
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate void
pc_init(void)717c478bd9Sstevel@tonic-gate pc_init(void)
727c478bd9Sstevel@tonic-gate {
737c478bd9Sstevel@tonic-gate 	struct pchead *hdp, *hfp;
747c478bd9Sstevel@tonic-gate 	int i;
757c478bd9Sstevel@tonic-gate 	for (i = 0; i < NPCHASH; i++) {
767c478bd9Sstevel@tonic-gate 		hdp = &pcdhead[i];
777c478bd9Sstevel@tonic-gate 		hfp = &pcfhead[i];
787c478bd9Sstevel@tonic-gate 		hdp->pch_forw =  (struct pcnode *)hdp;
797c478bd9Sstevel@tonic-gate 		hdp->pch_back =  (struct pcnode *)hdp;
807c478bd9Sstevel@tonic-gate 		hfp->pch_forw =  (struct pcnode *)hfp;
817c478bd9Sstevel@tonic-gate 		hfp->pch_back =  (struct pcnode *)hfp;
827c478bd9Sstevel@tonic-gate 	}
837c478bd9Sstevel@tonic-gate }
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate struct pcnode *
pc_getnode(struct pcfs * fsp,daddr_t blkno,int offset,struct pcdir * ep)867c478bd9Sstevel@tonic-gate pc_getnode(
877c478bd9Sstevel@tonic-gate 	struct pcfs *fsp,	/* filsystem for node */
887c478bd9Sstevel@tonic-gate 	daddr_t blkno,		/* phys block no of dir entry */
897c478bd9Sstevel@tonic-gate 	int offset,		/* offset of dir entry in block */
907c478bd9Sstevel@tonic-gate 	struct pcdir *ep)	/* node dir entry */
917c478bd9Sstevel@tonic-gate {
927c478bd9Sstevel@tonic-gate 	struct pcnode *pcp;
937c478bd9Sstevel@tonic-gate 	struct pchead *hp;
947c478bd9Sstevel@tonic-gate 	struct vnode *vp;
957c478bd9Sstevel@tonic-gate 	pc_cluster32_t scluster;
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate 	ASSERT(fsp->pcfs_flags & PCFS_LOCKED);
987c478bd9Sstevel@tonic-gate 	if (ep == (struct pcdir *)0) {
99264a6e74Sfrankho 		ep = &pcfs_rootdirentry;
1007c478bd9Sstevel@tonic-gate 		scluster = 0;
1017c478bd9Sstevel@tonic-gate 	} else {
1027c478bd9Sstevel@tonic-gate 		scluster = pc_getstartcluster(fsp, ep);
1037c478bd9Sstevel@tonic-gate 	}
1047c478bd9Sstevel@tonic-gate 	/*
1057c478bd9Sstevel@tonic-gate 	 * First look for active nodes.
1067c478bd9Sstevel@tonic-gate 	 * File nodes are identified by the location (blkno, offset) of
1077c478bd9Sstevel@tonic-gate 	 * its directory entry.
1087c478bd9Sstevel@tonic-gate 	 * Directory nodes are identified by the starting cluster number
1097c478bd9Sstevel@tonic-gate 	 * for the entries.
1107c478bd9Sstevel@tonic-gate 	 */
1117c478bd9Sstevel@tonic-gate 	if (ep->pcd_attr & PCA_DIR) {
1127c478bd9Sstevel@tonic-gate 		hp = &pcdhead[PCDHASH(fsp, scluster)];
1137c478bd9Sstevel@tonic-gate 		rw_enter(&pcnodes_lock, RW_READER);
1147c478bd9Sstevel@tonic-gate 		for (pcp = hp->pch_forw;
1157c478bd9Sstevel@tonic-gate 		    pcp != (struct pcnode *)hp; pcp = pcp->pc_forw) {
1167c478bd9Sstevel@tonic-gate 			if ((fsp == VFSTOPCFS(PCTOV(pcp)->v_vfsp)) &&
1177c478bd9Sstevel@tonic-gate 			    (scluster == pcp->pc_scluster)) {
1187c478bd9Sstevel@tonic-gate 				VN_HOLD(PCTOV(pcp));
1197c478bd9Sstevel@tonic-gate 				rw_exit(&pcnodes_lock);
1207c478bd9Sstevel@tonic-gate 				return (pcp);
1217c478bd9Sstevel@tonic-gate 			}
1227c478bd9Sstevel@tonic-gate 		}
1237c478bd9Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
1247c478bd9Sstevel@tonic-gate 	} else {
1257c478bd9Sstevel@tonic-gate 		hp = &pcfhead[PCFHASH(fsp, blkno, offset)];
1267c478bd9Sstevel@tonic-gate 		rw_enter(&pcnodes_lock, RW_READER);
1277c478bd9Sstevel@tonic-gate 		for (pcp = hp->pch_forw;
1287c478bd9Sstevel@tonic-gate 		    pcp != (struct pcnode *)hp; pcp = pcp->pc_forw) {
1297c478bd9Sstevel@tonic-gate 			if ((fsp == VFSTOPCFS(PCTOV(pcp)->v_vfsp)) &&
1307c478bd9Sstevel@tonic-gate 			    ((pcp->pc_flags & PC_INVAL) == 0) &&
1317c478bd9Sstevel@tonic-gate 			    (blkno == pcp->pc_eblkno) &&
1327c478bd9Sstevel@tonic-gate 			    (offset == pcp->pc_eoffset)) {
1337c478bd9Sstevel@tonic-gate 				VN_HOLD(PCTOV(pcp));
1347c478bd9Sstevel@tonic-gate 				rw_exit(&pcnodes_lock);
1357c478bd9Sstevel@tonic-gate 				return (pcp);
1367c478bd9Sstevel@tonic-gate 			}
1377c478bd9Sstevel@tonic-gate 		}
1387c478bd9Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
1397c478bd9Sstevel@tonic-gate 	}
1407c478bd9Sstevel@tonic-gate 	/*
1417c478bd9Sstevel@tonic-gate 	 * Cannot find node in active list. Allocate memory for a new node
1427c478bd9Sstevel@tonic-gate 	 * initialize it, and put it on the active list.
1437c478bd9Sstevel@tonic-gate 	 */
144a0f9c00cSJosef 'Jeff' Sipek 	pcp = kmem_zalloc(sizeof (struct pcnode), KM_SLEEP);
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
210da6c28aaSamw 		err = VOP_PUTPAGE(PCTOV(pcp), 0, 0, flags,
211da6c28aaSamw 		    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) {
246f127cb91Sfrankho 			(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 
265ade42b55SSebastien Roy 	VN_RELE_LOCKED(vp);
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 
315f127cb91Sfrankho 	gethrestime(&now);
316f127cb91Sfrankho 	if (pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime))
317f127cb91Sfrankho 		PC_DPRINTF1(2, "pc_mark_mod failed timestamp "
318f127cb91Sfrankho 		    "conversion, curtime = %lld\n",
319f127cb91Sfrankho 		    (long long)now.tv_sec);
320f127cb91Sfrankho 
321f127cb91Sfrankho 	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 
338f127cb91Sfrankho 	gethrestime(&now);
339f127cb91Sfrankho 	if (pc_tvtopct(&now, &pt)) {
340f127cb91Sfrankho 		PC_DPRINTF1(2, "pc_mark_acc failed timestamp "
341f127cb91Sfrankho 		    "conversion, curtime = %lld\n",
342f127cb91Sfrankho 		    (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,
425f127cb91Sfrankho 			    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),
474f127cb91Sfrankho 		    (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,
594f127cb91Sfrankho 		    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,
666f127cb91Sfrankho 		    "Disk was changed during an update or\n"
667f127cb91Sfrankho 		    "an irrecoverable error was encountered.\n"
668f127cb91Sfrankho 		    "File damage is possible.  To prevent further\n"
669f127cb91Sfrankho 		    "damage, this pcfs instance will now be frozen.\n"
670*bbf21555SRichard Lowe 		    "Use umount(8) 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 {
681264a6e74Sfrankho 	struct pcnode	*pcp, *npcp = NULL;
682264a6e74Sfrankho 	struct pchead	*hp;
683264a6e74Sfrankho 	struct vnode	*vp;
684264a6e74Sfrankho 	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));
7579bd42341Sfrankho 				fsp->pcfs_frefs--;
7589bd42341Sfrankho 				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