xref: /illumos-gate/usr/src/uts/common/fs/pcfs/pc_vfsops.c (revision 9bd42341)
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
55a59a8b3Srsb  * Common Development and Distribution License (the "License").
65a59a8b3Srsb  * 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 /*
225a59a8b3Srsb  * Copyright 2006 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/systm.h>
307c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
317c478bd9Sstevel@tonic-gate #include <sys/user.h>
327c478bd9Sstevel@tonic-gate #include <sys/proc.h>
337c478bd9Sstevel@tonic-gate #include <sys/cred.h>
347c478bd9Sstevel@tonic-gate #include <sys/disp.h>
357c478bd9Sstevel@tonic-gate #include <sys/buf.h>
367c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
377c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
387c478bd9Sstevel@tonic-gate #include <sys/fdio.h>
397c478bd9Sstevel@tonic-gate #include <sys/file.h>
407c478bd9Sstevel@tonic-gate #include <sys/uio.h>
417c478bd9Sstevel@tonic-gate #include <sys/conf.h>
427c478bd9Sstevel@tonic-gate #undef NFSCLIENT
437c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
447c478bd9Sstevel@tonic-gate #include <sys/mount.h>
457c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
467c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
477c478bd9Sstevel@tonic-gate #include <sys/debug.h>
487c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
497c478bd9Sstevel@tonic-gate #include <sys/conf.h>
507c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
517c478bd9Sstevel@tonic-gate #include <sys/swap.h>
527c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
537c478bd9Sstevel@tonic-gate #include <sys/sunldi.h>
547c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
557c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h>
567c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
577c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
587c478bd9Sstevel@tonic-gate #include <sys/fs/pc_node.h>
597c478bd9Sstevel@tonic-gate #include <fs/fs_subr.h>
607c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
617c478bd9Sstevel@tonic-gate #include <sys/dkio.h>
627c478bd9Sstevel@tonic-gate #include <sys/open.h>
637c478bd9Sstevel@tonic-gate #include <sys/mntent.h>
647c478bd9Sstevel@tonic-gate #include <sys/policy.h>
65264a6e74Sfrankho #include <sys/atomic.h>
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate /*
687c478bd9Sstevel@tonic-gate  * The majority of PC media use a 512 sector size, but
697c478bd9Sstevel@tonic-gate  * occasionally you will run across a 1k sector size.
707c478bd9Sstevel@tonic-gate  * For media with a 1k sector size, fd_strategy() requires
717c478bd9Sstevel@tonic-gate  * the I/O size to be a 1k multiple; so when the sector size
727c478bd9Sstevel@tonic-gate  * is not yet known, always read 1k.
737c478bd9Sstevel@tonic-gate  */
747c478bd9Sstevel@tonic-gate #define	PC_SAFESECSIZE	(PC_SECSIZE * 2)
757c478bd9Sstevel@tonic-gate 
7690c30842Sjmcp static int pcfs_pseudo_floppy(dev_t);
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate static int pcfsinit(int, char *);
797c478bd9Sstevel@tonic-gate static int pcfs_mount(struct vfs *, struct vnode *, struct mounta *,
807c478bd9Sstevel@tonic-gate 	struct cred *);
817c478bd9Sstevel@tonic-gate static int pcfs_unmount(struct vfs *, int, struct cred *);
827c478bd9Sstevel@tonic-gate static int pcfs_root(struct vfs *, struct vnode **);
837c478bd9Sstevel@tonic-gate static int pcfs_statvfs(struct vfs *, struct statvfs64 *);
847c478bd9Sstevel@tonic-gate static int pc_syncfsnodes(struct pcfs *);
857c478bd9Sstevel@tonic-gate static int pcfs_sync(struct vfs *, short, struct cred *);
867c478bd9Sstevel@tonic-gate static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp);
87264a6e74Sfrankho static void pcfs_freevfs(vfs_t *vfsp);
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate static int pc_getfattype(struct vnode *, int, daddr_t *, int *);
907c478bd9Sstevel@tonic-gate static int pc_readfat(struct pcfs *fsp, uchar_t *fatp, daddr_t start,
917c478bd9Sstevel@tonic-gate     size_t fatsize);
927c478bd9Sstevel@tonic-gate static int pc_writefat(struct pcfs *fsp, daddr_t start);
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate /*
957c478bd9Sstevel@tonic-gate  * pcfs mount options table
967c478bd9Sstevel@tonic-gate  */
977c478bd9Sstevel@tonic-gate 
98264a6e74Sfrankho static char *nohidden_cancel[] = { MNTOPT_PCFS_HIDDEN, NULL };
99264a6e74Sfrankho static char *hidden_cancel[] = { MNTOPT_PCFS_NOHIDDEN, NULL };
100264a6e74Sfrankho static char *nofoldcase_cancel[] = { MNTOPT_PCFS_FOLDCASE, NULL };
101264a6e74Sfrankho static char *foldcase_cancel[] = { MNTOPT_PCFS_NOFOLDCASE, NULL };
102264a6e74Sfrankho static char *clamptime_cancel[] = { MNTOPT_PCFS_NOCLAMPTIME, NULL };
103264a6e74Sfrankho static char *noclamptime_cancel[] = { MNTOPT_PCFS_CLAMPTIME, NULL };
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate static mntopt_t mntopts[] = {
1067c478bd9Sstevel@tonic-gate /*
107264a6e74Sfrankho  *	option name	cancel option	default arg	flags	opt data
1087c478bd9Sstevel@tonic-gate  */
109264a6e74Sfrankho 	{ MNTOPT_PCFS_NOHIDDEN, nohidden_cancel, NULL, 0, NULL },
110264a6e74Sfrankho 	{ MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, MO_DEFAULT, NULL },
111264a6e74Sfrankho 	{ MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, NULL },
112264a6e74Sfrankho 	{ MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0, NULL },
113264a6e74Sfrankho 	{ MNTOPT_PCFS_CLAMPTIME, clamptime_cancel, NULL, MO_DEFAULT, NULL },
114264a6e74Sfrankho 	{ MNTOPT_PCFS_NOCLAMPTIME, noclamptime_cancel, NULL, NULL, NULL }
1157c478bd9Sstevel@tonic-gate };
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate static mntopts_t pcfs_mntopts = {
1187c478bd9Sstevel@tonic-gate 	sizeof (mntopts) / sizeof (mntopt_t),
1197c478bd9Sstevel@tonic-gate 	mntopts
1207c478bd9Sstevel@tonic-gate };
1217c478bd9Sstevel@tonic-gate 
1227c478bd9Sstevel@tonic-gate int pcfsdebuglevel = 0;
1237c478bd9Sstevel@tonic-gate 
1247c478bd9Sstevel@tonic-gate /*
1257c478bd9Sstevel@tonic-gate  * pcfslock:	protects the list of mounted pc filesystems "pc_mounttab.
1267c478bd9Sstevel@tonic-gate  * pcfs_lock:	(inside per filesystem structure "pcfs")
1277c478bd9Sstevel@tonic-gate  *		per filesystem lock. Most of the vfsops and vnodeops are
1287c478bd9Sstevel@tonic-gate  *		protected by this lock.
1297c478bd9Sstevel@tonic-gate  * pcnodes_lock: protects the pcnode hash table "pcdhead", "pcfhead".
1307c478bd9Sstevel@tonic-gate  *
1317c478bd9Sstevel@tonic-gate  * Lock hierarchy: pcfslock > pcfs_lock > pcnodes_lock
132264a6e74Sfrankho  *
133264a6e74Sfrankho  * pcfs_mountcount:	used to prevent module unloads while there is still
134264a6e74Sfrankho  *			pcfs state from a former mount hanging around. With
135264a6e74Sfrankho  *			forced umount support, the filesystem module must not
136264a6e74Sfrankho  *			be allowed to go away before the last VFS_FREEVFS()
137264a6e74Sfrankho  *			call has been made.
138264a6e74Sfrankho  *			Since this is just an atomic counter, there's no need
139264a6e74Sfrankho  *			for locking.
1407c478bd9Sstevel@tonic-gate  */
1417c478bd9Sstevel@tonic-gate kmutex_t	pcfslock;
142264a6e74Sfrankho krwlock_t	pcnodes_lock;
143264a6e74Sfrankho uint32_t	pcfs_mountcount;
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate static int pcfstype;
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate static vfsdef_t vfw = {
1487c478bd9Sstevel@tonic-gate 	VFSDEF_VERSION,
1497c478bd9Sstevel@tonic-gate 	"pcfs",
1507c478bd9Sstevel@tonic-gate 	pcfsinit,
1515a59a8b3Srsb 	VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS,
1527c478bd9Sstevel@tonic-gate 	&pcfs_mntopts
1537c478bd9Sstevel@tonic-gate };
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate extern struct mod_ops mod_fsops;
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate static struct modlfs modlfs = {
1587c478bd9Sstevel@tonic-gate 	&mod_fsops,
159*9bd42341Sfrankho 	"PC filesystem v1.100",
1607c478bd9Sstevel@tonic-gate 	&vfw
1617c478bd9Sstevel@tonic-gate };
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
1647c478bd9Sstevel@tonic-gate 	MODREV_1,
1657c478bd9Sstevel@tonic-gate 	&modlfs,
1667c478bd9Sstevel@tonic-gate 	NULL
1677c478bd9Sstevel@tonic-gate };
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate int
1707c478bd9Sstevel@tonic-gate _init(void)
1717c478bd9Sstevel@tonic-gate {
1727c478bd9Sstevel@tonic-gate 	int	error;
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate #if !defined(lint)
1757c478bd9Sstevel@tonic-gate 	/* make sure the on-disk structures are sane */
1767c478bd9Sstevel@tonic-gate 	ASSERT(sizeof (struct pcdir) == 32);
1777c478bd9Sstevel@tonic-gate 	ASSERT(sizeof (struct pcdir_lfn) == 32);
1787c478bd9Sstevel@tonic-gate #endif
1797c478bd9Sstevel@tonic-gate 	mutex_init(&pcfslock, NULL, MUTEX_DEFAULT, NULL);
1807c478bd9Sstevel@tonic-gate 	rw_init(&pcnodes_lock, NULL, RW_DEFAULT, NULL);
1817c478bd9Sstevel@tonic-gate 	error = mod_install(&modlinkage);
1827c478bd9Sstevel@tonic-gate 	if (error) {
1837c478bd9Sstevel@tonic-gate 		mutex_destroy(&pcfslock);
1847c478bd9Sstevel@tonic-gate 		rw_destroy(&pcnodes_lock);
1857c478bd9Sstevel@tonic-gate 	}
1867c478bd9Sstevel@tonic-gate 	return (error);
1877c478bd9Sstevel@tonic-gate }
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate int
1907c478bd9Sstevel@tonic-gate _fini(void)
1917c478bd9Sstevel@tonic-gate {
1927c478bd9Sstevel@tonic-gate 	int	error;
1937c478bd9Sstevel@tonic-gate 
194264a6e74Sfrankho 	/*
195264a6e74Sfrankho 	 * If a forcedly unmounted instance is still hanging around,
196264a6e74Sfrankho 	 * we cannot allow the module to be unloaded because that would
197264a6e74Sfrankho 	 * cause panics once the VFS framework decides it's time to call
198264a6e74Sfrankho 	 * into VFS_FREEVFS().
199264a6e74Sfrankho 	 */
200264a6e74Sfrankho 	if (pcfs_mountcount)
201264a6e74Sfrankho 		return (EBUSY);
202264a6e74Sfrankho 
2037c478bd9Sstevel@tonic-gate 	error = mod_remove(&modlinkage);
2047c478bd9Sstevel@tonic-gate 	if (error)
2057c478bd9Sstevel@tonic-gate 		return (error);
2067c478bd9Sstevel@tonic-gate 	mutex_destroy(&pcfslock);
2077c478bd9Sstevel@tonic-gate 	rw_destroy(&pcnodes_lock);
2087c478bd9Sstevel@tonic-gate 	/*
2097c478bd9Sstevel@tonic-gate 	 * Tear down the operations vectors
2107c478bd9Sstevel@tonic-gate 	 */
2117c478bd9Sstevel@tonic-gate 	(void) vfs_freevfsops_by_type(pcfstype);
2127c478bd9Sstevel@tonic-gate 	vn_freevnodeops(pcfs_fvnodeops);
2137c478bd9Sstevel@tonic-gate 	vn_freevnodeops(pcfs_dvnodeops);
2147c478bd9Sstevel@tonic-gate 	return (0);
2157c478bd9Sstevel@tonic-gate }
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate int
2187c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
2197c478bd9Sstevel@tonic-gate {
2207c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2217c478bd9Sstevel@tonic-gate }
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate /* ARGSUSED1 */
2247c478bd9Sstevel@tonic-gate static int
2257c478bd9Sstevel@tonic-gate pcfsinit(int fstype, char *name)
2267c478bd9Sstevel@tonic-gate {
2277c478bd9Sstevel@tonic-gate 	static const fs_operation_def_t pcfs_vfsops_template[] = {
2287c478bd9Sstevel@tonic-gate 		VFSNAME_MOUNT, pcfs_mount,
2297c478bd9Sstevel@tonic-gate 		VFSNAME_UNMOUNT, pcfs_unmount,
2307c478bd9Sstevel@tonic-gate 		VFSNAME_ROOT, pcfs_root,
2317c478bd9Sstevel@tonic-gate 		VFSNAME_STATVFS, pcfs_statvfs,
2327c478bd9Sstevel@tonic-gate 		VFSNAME_SYNC, (fs_generic_func_p) pcfs_sync,
2337c478bd9Sstevel@tonic-gate 		VFSNAME_VGET, pcfs_vget,
234264a6e74Sfrankho 		VFSNAME_FREEVFS, (fs_generic_func_p) pcfs_freevfs,
2357c478bd9Sstevel@tonic-gate 		NULL, NULL
2367c478bd9Sstevel@tonic-gate 	};
2377c478bd9Sstevel@tonic-gate 	int error;
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate 	error = vfs_setfsops(fstype, pcfs_vfsops_template, NULL);
2407c478bd9Sstevel@tonic-gate 	if (error != 0) {
2417c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad vfs ops template");
2427c478bd9Sstevel@tonic-gate 		return (error);
2437c478bd9Sstevel@tonic-gate 	}
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 	error = vn_make_ops("pcfs", pcfs_fvnodeops_template, &pcfs_fvnodeops);
2467c478bd9Sstevel@tonic-gate 	if (error != 0) {
2477c478bd9Sstevel@tonic-gate 		(void) vfs_freevfsops_by_type(fstype);
2487c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad file vnode ops template");
2497c478bd9Sstevel@tonic-gate 		return (error);
2507c478bd9Sstevel@tonic-gate 	}
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 	error = vn_make_ops("pcfsd", pcfs_dvnodeops_template, &pcfs_dvnodeops);
2537c478bd9Sstevel@tonic-gate 	if (error != 0) {
2547c478bd9Sstevel@tonic-gate 		(void) vfs_freevfsops_by_type(fstype);
2557c478bd9Sstevel@tonic-gate 		vn_freevnodeops(pcfs_fvnodeops);
2567c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad dir vnode ops template");
2577c478bd9Sstevel@tonic-gate 		return (error);
2587c478bd9Sstevel@tonic-gate 	}
2597c478bd9Sstevel@tonic-gate 
2607c478bd9Sstevel@tonic-gate 	pcfstype = fstype;
2617c478bd9Sstevel@tonic-gate 	(void) pc_init();
262264a6e74Sfrankho 	pcfs_mountcount = 0;
2637c478bd9Sstevel@tonic-gate 	return (0);
2647c478bd9Sstevel@tonic-gate }
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate static struct pcfs *pc_mounttab = NULL;
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate extern struct pcfs_args pc_tz;
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate /*
2717c478bd9Sstevel@tonic-gate  *  Define some special logical drives we use internal to this file.
2727c478bd9Sstevel@tonic-gate  */
2737c478bd9Sstevel@tonic-gate #define	BOOT_PARTITION_DRIVE	99
2747c478bd9Sstevel@tonic-gate #define	PRIMARY_DOS_DRIVE	1
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate /*
2777c478bd9Sstevel@tonic-gate  * pc_mount system call
2787c478bd9Sstevel@tonic-gate  */
2797c478bd9Sstevel@tonic-gate static int
2807c478bd9Sstevel@tonic-gate pcfs_mount(
2817c478bd9Sstevel@tonic-gate 	struct vfs *vfsp,
2827c478bd9Sstevel@tonic-gate 	struct vnode *mvp,
2837c478bd9Sstevel@tonic-gate 	struct mounta *uap,
2847c478bd9Sstevel@tonic-gate 	struct cred *cr)
2857c478bd9Sstevel@tonic-gate {
2867c478bd9Sstevel@tonic-gate 	struct pcfs *fsp;
2877c478bd9Sstevel@tonic-gate 	struct vnode *bvp;
2887c478bd9Sstevel@tonic-gate 	struct vnode *devvp;
2897c478bd9Sstevel@tonic-gate 	struct pathname special;
2907c478bd9Sstevel@tonic-gate 	daddr_t dosstart;
2917c478bd9Sstevel@tonic-gate 	dev_t pseudodev;
2927c478bd9Sstevel@tonic-gate 	dev_t xdev;
293*9bd42341Sfrankho 	char *c;
2947c478bd9Sstevel@tonic-gate 	char *data = uap->dataptr;
2957c478bd9Sstevel@tonic-gate 	int datalen = uap->datalen;
2967c478bd9Sstevel@tonic-gate 	int dos_ldrive = 0;
2977c478bd9Sstevel@tonic-gate 	int error;
2987c478bd9Sstevel@tonic-gate 	int fattype;
2997c478bd9Sstevel@tonic-gate 	minor_t	minor;
3007c478bd9Sstevel@tonic-gate 	int oflag, aflag;
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 	if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
3037c478bd9Sstevel@tonic-gate 		return (error);
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 	PC_DPRINTF0(4, "pcfs_mount\n");
3067c478bd9Sstevel@tonic-gate 	if (mvp->v_type != VDIR) {
3077c478bd9Sstevel@tonic-gate 		return (ENOTDIR);
3087c478bd9Sstevel@tonic-gate 	}
3097c478bd9Sstevel@tonic-gate 	mutex_enter(&mvp->v_lock);
3107c478bd9Sstevel@tonic-gate 	if ((uap->flags & MS_REMOUNT) == 0 &&
3117c478bd9Sstevel@tonic-gate 	    (uap->flags & MS_OVERLAY) == 0 &&
3127c478bd9Sstevel@tonic-gate 	    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
3137c478bd9Sstevel@tonic-gate 		mutex_exit(&mvp->v_lock);
3147c478bd9Sstevel@tonic-gate 		return (EBUSY);
3157c478bd9Sstevel@tonic-gate 	}
3167c478bd9Sstevel@tonic-gate 	mutex_exit(&mvp->v_lock);
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 	/*
3197c478bd9Sstevel@tonic-gate 	 * The caller is responsible for making sure to always
3207c478bd9Sstevel@tonic-gate 	 * pass in sizeof(struct pcfs_args) (or the old one).
3217c478bd9Sstevel@tonic-gate 	 * Doing this is the only way to know an EINVAL return
3227c478bd9Sstevel@tonic-gate 	 * from mount(2) is due to the "not a DOS filesystem"
3237c478bd9Sstevel@tonic-gate 	 * EINVAL that pc_verify/pc_getfattype could return.
3247c478bd9Sstevel@tonic-gate 	 */
3257c478bd9Sstevel@tonic-gate 	if ((datalen != sizeof (struct pcfs_args)) &&
3267c478bd9Sstevel@tonic-gate 	    (datalen != sizeof (struct old_pcfs_args))) {
3277c478bd9Sstevel@tonic-gate 		return (EINVAL);
3287c478bd9Sstevel@tonic-gate 	} else {
3297c478bd9Sstevel@tonic-gate 		struct pcfs_args tmp_tz;
3307c478bd9Sstevel@tonic-gate 		int hidden = 0;
3317c478bd9Sstevel@tonic-gate 		int foldcase = 0;
332264a6e74Sfrankho 		int noclamptime = 0;
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate 		tmp_tz.flags = 0;
3357c478bd9Sstevel@tonic-gate 		if (copyin(data, &tmp_tz, datalen)) {
3367c478bd9Sstevel@tonic-gate 			return (EFAULT);
3377c478bd9Sstevel@tonic-gate 		}
3387c478bd9Sstevel@tonic-gate 		if (datalen == sizeof (struct pcfs_args)) {
3397c478bd9Sstevel@tonic-gate 			hidden = tmp_tz.flags & PCFS_MNT_HIDDEN;
3407c478bd9Sstevel@tonic-gate 			foldcase = tmp_tz.flags & PCFS_MNT_FOLDCASE;
341264a6e74Sfrankho 			noclamptime = tmp_tz.flags & PCFS_MNT_NOCLAMPTIME;
3427c478bd9Sstevel@tonic-gate 		}
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 		if (hidden)
3457c478bd9Sstevel@tonic-gate 			vfs_setmntopt(vfsp, MNTOPT_PCFS_HIDDEN,	NULL, 0);
3467c478bd9Sstevel@tonic-gate 		if (foldcase)
3477c478bd9Sstevel@tonic-gate 			vfs_setmntopt(vfsp, MNTOPT_PCFS_FOLDCASE, NULL, 0);
348264a6e74Sfrankho 		if (noclamptime)
349264a6e74Sfrankho 			vfs_setmntopt(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL, 0);
3507c478bd9Sstevel@tonic-gate 		/*
3517c478bd9Sstevel@tonic-gate 		 * more than one pc filesystem can be mounted on x86
3527c478bd9Sstevel@tonic-gate 		 * so the pc_tz structure is now a critical region
3537c478bd9Sstevel@tonic-gate 		 */
3547c478bd9Sstevel@tonic-gate 		mutex_enter(&pcfslock);
3557c478bd9Sstevel@tonic-gate 		if (pc_mounttab == NULL)
3567c478bd9Sstevel@tonic-gate 			bcopy(&tmp_tz, &pc_tz, sizeof (struct pcfs_args));
3577c478bd9Sstevel@tonic-gate 		mutex_exit(&pcfslock);
3587c478bd9Sstevel@tonic-gate 	}
3597c478bd9Sstevel@tonic-gate 	/*
3607c478bd9Sstevel@tonic-gate 	 * Resolve path name of special file being mounted.
3617c478bd9Sstevel@tonic-gate 	 */
3627c478bd9Sstevel@tonic-gate 	if (error = pn_get(uap->spec, UIO_USERSPACE, &special)) {
3637c478bd9Sstevel@tonic-gate 		return (error);
3647c478bd9Sstevel@tonic-gate 	}
3657c478bd9Sstevel@tonic-gate 	if (error =
3667c478bd9Sstevel@tonic-gate 	    lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &bvp)) {
3677c478bd9Sstevel@tonic-gate 		/*
368*9bd42341Sfrankho 		 * Split the pathname string at the last ':' separator.
369*9bd42341Sfrankho 		 * If there's no ':' in the device name, or the ':' is the
370*9bd42341Sfrankho 		 * last character in the string, the name is invalid and
371*9bd42341Sfrankho 		 * the error from the previous lookup will be returned.
3727c478bd9Sstevel@tonic-gate 		 */
373*9bd42341Sfrankho 		c = strrchr(special.pn_path, ':');
374*9bd42341Sfrankho 		if (c == NULL || strlen(c) == 0)
375*9bd42341Sfrankho 			goto devlookup_done;
3767c478bd9Sstevel@tonic-gate 
377*9bd42341Sfrankho 		*c++ = '\0';
3787c478bd9Sstevel@tonic-gate 
379*9bd42341Sfrankho 		/*
380*9bd42341Sfrankho 		 * PCFS partition name suffixes can be:
381*9bd42341Sfrankho 		 *	- "boot" to indicate the X86BOOT partition
382*9bd42341Sfrankho 		 *	- a drive letter [c-z] for the "DOS logical drive"
383*9bd42341Sfrankho 		 *	- a drive number 1..24 for the "DOS logical drive"
384*9bd42341Sfrankho 		 */
385*9bd42341Sfrankho 		if (strcasecmp(c, "boot") == 0) {
386*9bd42341Sfrankho 			/*
387*9bd42341Sfrankho 			 * The Solaris boot partition is requested.
388*9bd42341Sfrankho 			 */
389*9bd42341Sfrankho 			dos_ldrive = BOOT_PARTITION_DRIVE;
390*9bd42341Sfrankho 		} else if (strspn(c, "0123456789") == strlen(c)) {
391*9bd42341Sfrankho 			/*
392*9bd42341Sfrankho 			 * All digits - parse the partition number.
393*9bd42341Sfrankho 			 */
394*9bd42341Sfrankho 			long drvnum = 0;
395*9bd42341Sfrankho 
396*9bd42341Sfrankho 			if ((error = ddi_strtol(c, NULL, 10, &drvnum)) == 0) {
3977c478bd9Sstevel@tonic-gate 				/*
398*9bd42341Sfrankho 				 * A number alright - in the allowed range ?
3997c478bd9Sstevel@tonic-gate 				 */
400*9bd42341Sfrankho 				if (drvnum > 24 || drvnum == 0)
401*9bd42341Sfrankho 					error = EINVAL;
4027c478bd9Sstevel@tonic-gate 			}
403*9bd42341Sfrankho 			if (error)
404*9bd42341Sfrankho 				goto devlookup_done;
405*9bd42341Sfrankho 			dos_ldrive = (int)drvnum;
406*9bd42341Sfrankho 		} else if (strlen(c) == 1) {
407*9bd42341Sfrankho 			/*
408*9bd42341Sfrankho 			 * A single trailing character - is it [c-zC-Z] ?
409*9bd42341Sfrankho 			 */
410*9bd42341Sfrankho 			*c = tolower(*c);
411*9bd42341Sfrankho 			if (*c < 'c' || *c > 'z') {
412*9bd42341Sfrankho 				error = EINVAL;
413*9bd42341Sfrankho 				goto devlookup_done;
414*9bd42341Sfrankho 			}
415*9bd42341Sfrankho 			dos_ldrive = 1 + *c - 'c';
416*9bd42341Sfrankho 		} else {
417*9bd42341Sfrankho 			/*
418*9bd42341Sfrankho 			 * Can't parse this - pass through previous error.
419*9bd42341Sfrankho 			 */
420*9bd42341Sfrankho 			goto devlookup_done;
4217c478bd9Sstevel@tonic-gate 		}
422*9bd42341Sfrankho 
423*9bd42341Sfrankho 		ASSERT(dos_ldrive > 0);
424*9bd42341Sfrankho 
425*9bd42341Sfrankho 		error = lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW,
426*9bd42341Sfrankho 		    NULLVPP, &bvp);
4277c478bd9Sstevel@tonic-gate 	}
428*9bd42341Sfrankho devlookup_done:
4297c478bd9Sstevel@tonic-gate 	pn_free(&special);
430*9bd42341Sfrankho 	if (error)
431*9bd42341Sfrankho 		return (error);
432*9bd42341Sfrankho 
4337c478bd9Sstevel@tonic-gate 	if (bvp->v_type != VBLK) {
4347c478bd9Sstevel@tonic-gate 		VN_RELE(bvp);
4357c478bd9Sstevel@tonic-gate 		return (ENOTBLK);
4367c478bd9Sstevel@tonic-gate 	}
4377c478bd9Sstevel@tonic-gate 	xdev = bvp->v_rdev;
4387c478bd9Sstevel@tonic-gate 	/*
4397c478bd9Sstevel@tonic-gate 	 * Verify caller's permission to open the device special file.
4407c478bd9Sstevel@tonic-gate 	 */
4417c478bd9Sstevel@tonic-gate 	if ((vfsp->vfs_flag & VFS_RDONLY) != 0 ||
4427c478bd9Sstevel@tonic-gate 	    ((uap->flags & MS_RDONLY) != 0)) {
4437c478bd9Sstevel@tonic-gate 		oflag = FREAD;
4447c478bd9Sstevel@tonic-gate 		aflag = VREAD;
4457c478bd9Sstevel@tonic-gate 	} else {
4467c478bd9Sstevel@tonic-gate 		oflag = FREAD | FWRITE;
4477c478bd9Sstevel@tonic-gate 		aflag = VREAD | VWRITE;
4487c478bd9Sstevel@tonic-gate 	}
4497c478bd9Sstevel@tonic-gate 	if ((error = VOP_ACCESS(bvp, aflag, 0, cr)) != 0 ||
4507c478bd9Sstevel@tonic-gate 	    (error = secpolicy_spec_open(cr, bvp, oflag)) != 0) {
4517c478bd9Sstevel@tonic-gate 		VN_RELE(bvp);
4527c478bd9Sstevel@tonic-gate 		return (error);
4537c478bd9Sstevel@tonic-gate 	}
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 	VN_RELE(bvp);
4567c478bd9Sstevel@tonic-gate 	if (getmajor(xdev) >= devcnt) {
4577c478bd9Sstevel@tonic-gate 		return (ENXIO);
4587c478bd9Sstevel@tonic-gate 	}
4597c478bd9Sstevel@tonic-gate 	/*
460*9bd42341Sfrankho 	 * Ensure that this logical drive isn't already mounted, unless
461*9bd42341Sfrankho 	 * this is a REMOUNT request.
462*9bd42341Sfrankho 	 * Note: The framework will perform this check if the "...:c"
463*9bd42341Sfrankho 	 * PCFS-style "logical drive" syntax has not been used and an
464*9bd42341Sfrankho 	 * actually existing physical device is backing this filesystem.
4657c478bd9Sstevel@tonic-gate 	 */
4667c478bd9Sstevel@tonic-gate 	if (dos_ldrive) {
4677c478bd9Sstevel@tonic-gate 		mutex_enter(&pcfslock);
4687c478bd9Sstevel@tonic-gate 		for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt)
4697c478bd9Sstevel@tonic-gate 			if (fsp->pcfs_xdev == xdev &&
4707c478bd9Sstevel@tonic-gate 			    fsp->pcfs_ldrv == dos_ldrive) {
4717c478bd9Sstevel@tonic-gate 				mutex_exit(&pcfslock);
4727c478bd9Sstevel@tonic-gate 				if (uap->flags & MS_REMOUNT) {
4737c478bd9Sstevel@tonic-gate 					return (0);
4747c478bd9Sstevel@tonic-gate 				} else {
4757c478bd9Sstevel@tonic-gate 					return (EBUSY);
4767c478bd9Sstevel@tonic-gate 				}
4777c478bd9Sstevel@tonic-gate 			}
4787c478bd9Sstevel@tonic-gate 		/*
4797c478bd9Sstevel@tonic-gate 		 * Assign a unique device number for the vfs
4807c478bd9Sstevel@tonic-gate 		 * The old way (getudev() + a constantly incrementing
4817c478bd9Sstevel@tonic-gate 		 * major number) was wrong because it changes vfs_dev
4827c478bd9Sstevel@tonic-gate 		 * across mounts and reboots, which breaks nfs file handles.
4837c478bd9Sstevel@tonic-gate 		 * UFS just uses the real dev_t. We can't do that because
4847c478bd9Sstevel@tonic-gate 		 * of the way pcfs opens fdisk partitons (the :c and :d
4857c478bd9Sstevel@tonic-gate 		 * partitions are on the same dev_t). Though that _might_
4867c478bd9Sstevel@tonic-gate 		 * actually be ok, since the file handle contains an
4877c478bd9Sstevel@tonic-gate 		 * absolute block number, it's probably better to make them
4887c478bd9Sstevel@tonic-gate 		 * different. So I think we should retain the original
4897c478bd9Sstevel@tonic-gate 		 * dev_t, but come up with a different minor number based
4907c478bd9Sstevel@tonic-gate 		 * on the logical drive that will _always_ come up the same.
4917c478bd9Sstevel@tonic-gate 		 * For now, we steal the upper 6 bits.
4927c478bd9Sstevel@tonic-gate 		 */
4937c478bd9Sstevel@tonic-gate #ifdef notdef
4947c478bd9Sstevel@tonic-gate 		/* what should we do here? */
4957c478bd9Sstevel@tonic-gate 		if (((getminor(xdev) >> 12) & 0x3F) != 0)
4967c478bd9Sstevel@tonic-gate 			printf("whoops - upper bits used!\n");
4977c478bd9Sstevel@tonic-gate #endif
4987c478bd9Sstevel@tonic-gate 		minor = ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32;
4997c478bd9Sstevel@tonic-gate 		pseudodev = makedevice(getmajor(xdev), minor);
5007c478bd9Sstevel@tonic-gate 		if (vfs_devmounting(pseudodev, vfsp)) {
5017c478bd9Sstevel@tonic-gate 			mutex_exit(&pcfslock);
5027c478bd9Sstevel@tonic-gate 			return (EBUSY);
5037c478bd9Sstevel@tonic-gate 		}
5047c478bd9Sstevel@tonic-gate 		if (vfs_devismounted(pseudodev)) {
5057c478bd9Sstevel@tonic-gate 			mutex_exit(&pcfslock);
5067c478bd9Sstevel@tonic-gate 			if (uap->flags & MS_REMOUNT) {
5077c478bd9Sstevel@tonic-gate 				return (0);
5087c478bd9Sstevel@tonic-gate 			} else {
5097c478bd9Sstevel@tonic-gate 				return (EBUSY);
5107c478bd9Sstevel@tonic-gate 			}
5117c478bd9Sstevel@tonic-gate 		}
5127c478bd9Sstevel@tonic-gate 		mutex_exit(&pcfslock);
5137c478bd9Sstevel@tonic-gate 	} else {
5147c478bd9Sstevel@tonic-gate 		if (vfs_devmounting(xdev, vfsp)) {
5157c478bd9Sstevel@tonic-gate 			return (EBUSY);
5167c478bd9Sstevel@tonic-gate 		}
5177c478bd9Sstevel@tonic-gate 		if (vfs_devismounted(xdev))
5187c478bd9Sstevel@tonic-gate 			if (uap->flags & MS_REMOUNT) {
5197c478bd9Sstevel@tonic-gate 				return (0);
5207c478bd9Sstevel@tonic-gate 			} else {
5217c478bd9Sstevel@tonic-gate 				return (EBUSY);
5227c478bd9Sstevel@tonic-gate 			}
5237c478bd9Sstevel@tonic-gate 		pseudodev = xdev;
5247c478bd9Sstevel@tonic-gate 	}
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate 	if (uap->flags & MS_RDONLY) {
5277c478bd9Sstevel@tonic-gate 		vfsp->vfs_flag |= VFS_RDONLY;
5287c478bd9Sstevel@tonic-gate 		vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
5297c478bd9Sstevel@tonic-gate 	}
5307c478bd9Sstevel@tonic-gate 
5317c478bd9Sstevel@tonic-gate 	/*
5327c478bd9Sstevel@tonic-gate 	 * Mount the filesystem
5337c478bd9Sstevel@tonic-gate 	 */
5347c478bd9Sstevel@tonic-gate 	devvp = makespecvp(xdev, VBLK);
5357c478bd9Sstevel@tonic-gate 	if (IS_SWAPVP(devvp)) {
5367c478bd9Sstevel@tonic-gate 		VN_RELE(devvp);
5377c478bd9Sstevel@tonic-gate 		return (EBUSY);
5387c478bd9Sstevel@tonic-gate 	}
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate 	/*
5417c478bd9Sstevel@tonic-gate 	 * special handling for PCMCIA memory card
54290c30842Sjmcp 	 * with pseudo floppies organization
5437c478bd9Sstevel@tonic-gate 	 */
54490c30842Sjmcp 	if (dos_ldrive == 0 && pcfs_pseudo_floppy(xdev)) {
5457c478bd9Sstevel@tonic-gate 		dosstart = (daddr_t)0;
5467c478bd9Sstevel@tonic-gate 		fattype = PCFS_PCMCIA_NO_CIS;
5477c478bd9Sstevel@tonic-gate 	} else {
5487c478bd9Sstevel@tonic-gate 		if (error = pc_getfattype(devvp, dos_ldrive, &dosstart,
5497c478bd9Sstevel@tonic-gate 		    &fattype)) {
5507c478bd9Sstevel@tonic-gate 			VN_RELE(devvp);
5517c478bd9Sstevel@tonic-gate 			return (error);
5527c478bd9Sstevel@tonic-gate 		}
5537c478bd9Sstevel@tonic-gate 	}
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate 	(void) VOP_PUTPAGE(devvp, (offset_t)0, (uint_t)0, B_INVAL, cr);
5567c478bd9Sstevel@tonic-gate 	fsp = kmem_zalloc((uint_t)sizeof (struct pcfs), KM_SLEEP);
5577c478bd9Sstevel@tonic-gate 	fsp->pcfs_vfs = vfsp;
5587c478bd9Sstevel@tonic-gate 	fsp->pcfs_flags = fattype;
5597c478bd9Sstevel@tonic-gate 	fsp->pcfs_devvp = devvp;
5607c478bd9Sstevel@tonic-gate 	fsp->pcfs_xdev = xdev;
5617c478bd9Sstevel@tonic-gate 	fsp->pcfs_ldrv = dos_ldrive;
5627c478bd9Sstevel@tonic-gate 	fsp->pcfs_dosstart = dosstart;
5637c478bd9Sstevel@tonic-gate 	mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL);
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL))
5667c478bd9Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_HIDDEN;
5677c478bd9Sstevel@tonic-gate 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL))
5687c478bd9Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_FOLDCASE;
569264a6e74Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL))
570264a6e74Sfrankho 		fsp->pcfs_flags |= PCFS_NOCLAMPTIME;
5717c478bd9Sstevel@tonic-gate 	vfsp->vfs_dev = pseudodev;
5727c478bd9Sstevel@tonic-gate 	vfsp->vfs_fstype = pcfstype;
5737c478bd9Sstevel@tonic-gate 	vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype);
5747c478bd9Sstevel@tonic-gate 	vfsp->vfs_data = (caddr_t)fsp;
5757c478bd9Sstevel@tonic-gate 	vfsp->vfs_bcount = 0;
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 	error = pc_verify(fsp);
5787c478bd9Sstevel@tonic-gate 	if (error) {
5797c478bd9Sstevel@tonic-gate 		VN_RELE(devvp);
5807c478bd9Sstevel@tonic-gate 		mutex_destroy(&fsp->pcfs_lock);
5817c478bd9Sstevel@tonic-gate 		kmem_free(fsp, (uint_t)sizeof (struct pcfs));
5827c478bd9Sstevel@tonic-gate 		return (error);
5837c478bd9Sstevel@tonic-gate 	}
5847c478bd9Sstevel@tonic-gate 	vfsp->vfs_bsize = fsp->pcfs_clsize;
5857c478bd9Sstevel@tonic-gate 
5867c478bd9Sstevel@tonic-gate 	mutex_enter(&pcfslock);
5877c478bd9Sstevel@tonic-gate 	fsp->pcfs_nxt = pc_mounttab;
5887c478bd9Sstevel@tonic-gate 	pc_mounttab = fsp;
5897c478bd9Sstevel@tonic-gate 	mutex_exit(&pcfslock);
590264a6e74Sfrankho 	atomic_inc_32(&pcfs_mountcount);
5917c478bd9Sstevel@tonic-gate 	return (0);
5927c478bd9Sstevel@tonic-gate }
5937c478bd9Sstevel@tonic-gate 
5947c478bd9Sstevel@tonic-gate /*
5957c478bd9Sstevel@tonic-gate  * vfs operations
5967c478bd9Sstevel@tonic-gate  */
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate /* ARGSUSED */
5997c478bd9Sstevel@tonic-gate static int
6007c478bd9Sstevel@tonic-gate pcfs_unmount(
6017c478bd9Sstevel@tonic-gate 	struct vfs *vfsp,
6027c478bd9Sstevel@tonic-gate 	int flag,
6037c478bd9Sstevel@tonic-gate 	struct cred *cr)
6047c478bd9Sstevel@tonic-gate {
6057c478bd9Sstevel@tonic-gate 	struct pcfs *fsp, *fsp1;
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate 	if (secpolicy_fs_unmount(cr, vfsp) != 0)
6087c478bd9Sstevel@tonic-gate 		return (EPERM);
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 	PC_DPRINTF0(4, "pcfs_unmount\n");
6117c478bd9Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
612264a6e74Sfrankho 
6137c478bd9Sstevel@tonic-gate 	/*
6147c478bd9Sstevel@tonic-gate 	 * We don't have to lock fsp because the VVFSLOCK in vfs layer will
6157c478bd9Sstevel@tonic-gate 	 * prevent lookuppn from crossing the mount point.
616264a6e74Sfrankho 	 * If this is not a forced umount request and there's ongoing I/O,
617264a6e74Sfrankho 	 * don't allow the mount to proceed.
6187c478bd9Sstevel@tonic-gate 	 */
619264a6e74Sfrankho 	if (flag & MS_FORCE)
620264a6e74Sfrankho 		vfsp->vfs_flag |= VFS_UNMOUNTED;
621264a6e74Sfrankho 	else if (fsp->pcfs_nrefs)
6227c478bd9Sstevel@tonic-gate 		return (EBUSY);
623264a6e74Sfrankho 
624264a6e74Sfrankho 	mutex_enter(&pcfslock);
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 	/*
627264a6e74Sfrankho 	 * If this is a forced umount request or if the fs instance has
628264a6e74Sfrankho 	 * been marked as beyond recovery, allow the umount to proceed
629264a6e74Sfrankho 	 * regardless of state. pc_diskchanged() forcibly releases all
630264a6e74Sfrankho 	 * inactive vnodes/pcnodes.
6317c478bd9Sstevel@tonic-gate 	 */
632264a6e74Sfrankho 	if (flag & MS_FORCE || fsp->pcfs_flags & PCFS_IRRECOV) {
6337c478bd9Sstevel@tonic-gate 		rw_enter(&pcnodes_lock, RW_WRITER);
6347c478bd9Sstevel@tonic-gate 		pc_diskchanged(fsp);
6357c478bd9Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
6367c478bd9Sstevel@tonic-gate 	}
6377c478bd9Sstevel@tonic-gate 
6387c478bd9Sstevel@tonic-gate 	/* now there should be no pcp node on pcfhead or pcdhead. */
6397c478bd9Sstevel@tonic-gate 
6407c478bd9Sstevel@tonic-gate 	if (fsp == pc_mounttab) {
6417c478bd9Sstevel@tonic-gate 		pc_mounttab = fsp->pcfs_nxt;
6427c478bd9Sstevel@tonic-gate 	} else {
6437c478bd9Sstevel@tonic-gate 		for (fsp1 = pc_mounttab; fsp1 != NULL; fsp1 = fsp1->pcfs_nxt)
6447c478bd9Sstevel@tonic-gate 			if (fsp1->pcfs_nxt == fsp)
6457c478bd9Sstevel@tonic-gate 				fsp1->pcfs_nxt = fsp->pcfs_nxt;
6467c478bd9Sstevel@tonic-gate 	}
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate 	mutex_exit(&pcfslock);
6497c478bd9Sstevel@tonic-gate 
650264a6e74Sfrankho 	/*
651264a6e74Sfrankho 	 * Since we support VFS_FREEVFS(), there's no need to
652264a6e74Sfrankho 	 * free the fsp right now. The framework will tell us
653264a6e74Sfrankho 	 * when the right time to do so has arrived by calling
654264a6e74Sfrankho 	 * into pcfs_freevfs.
655264a6e74Sfrankho 	 */
6567c478bd9Sstevel@tonic-gate 	return (0);
6577c478bd9Sstevel@tonic-gate }
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate /*
6607c478bd9Sstevel@tonic-gate  * find root of pcfs
6617c478bd9Sstevel@tonic-gate  */
6627c478bd9Sstevel@tonic-gate static int
6637c478bd9Sstevel@tonic-gate pcfs_root(
6647c478bd9Sstevel@tonic-gate 	struct vfs *vfsp,
6657c478bd9Sstevel@tonic-gate 	struct vnode **vpp)
6667c478bd9Sstevel@tonic-gate {
6677c478bd9Sstevel@tonic-gate 	struct pcfs *fsp;
6687c478bd9Sstevel@tonic-gate 	struct pcnode *pcp;
6697c478bd9Sstevel@tonic-gate 	int error;
6707c478bd9Sstevel@tonic-gate 
6717c478bd9Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
6727c478bd9Sstevel@tonic-gate 	if (error = pc_lockfs(fsp, 0, 0))
6737c478bd9Sstevel@tonic-gate 		return (error);
674264a6e74Sfrankho 
6757c478bd9Sstevel@tonic-gate 	pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
6767c478bd9Sstevel@tonic-gate 	PC_DPRINTF2(9, "pcfs_root(0x%p) pcp= 0x%p\n",
6777c478bd9Sstevel@tonic-gate 	    (void *)vfsp, (void *)pcp);
6787c478bd9Sstevel@tonic-gate 	pc_unlockfs(fsp);
6797c478bd9Sstevel@tonic-gate 	*vpp = PCTOV(pcp);
6807c478bd9Sstevel@tonic-gate 	pcp->pc_flags |= PC_EXTERNAL;
6817c478bd9Sstevel@tonic-gate 	return (0);
6827c478bd9Sstevel@tonic-gate }
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate /*
6857c478bd9Sstevel@tonic-gate  * Get file system statistics.
6867c478bd9Sstevel@tonic-gate  */
6877c478bd9Sstevel@tonic-gate static int
6887c478bd9Sstevel@tonic-gate pcfs_statvfs(
6897c478bd9Sstevel@tonic-gate 	struct vfs *vfsp,
6907c478bd9Sstevel@tonic-gate 	struct statvfs64 *sp)
6917c478bd9Sstevel@tonic-gate {
6927c478bd9Sstevel@tonic-gate 	struct pcfs *fsp;
6937c478bd9Sstevel@tonic-gate 	int error;
6947c478bd9Sstevel@tonic-gate 	dev32_t d32;
6957c478bd9Sstevel@tonic-gate 
6967c478bd9Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
6977c478bd9Sstevel@tonic-gate 	error = pc_getfat(fsp);
6987c478bd9Sstevel@tonic-gate 	if (error)
6997c478bd9Sstevel@tonic-gate 		return (error);
7007c478bd9Sstevel@tonic-gate 	bzero(sp, sizeof (*sp));
7017c478bd9Sstevel@tonic-gate 	sp->f_bsize = sp->f_frsize = fsp->pcfs_clsize;
7027c478bd9Sstevel@tonic-gate 	sp->f_blocks = (fsblkcnt64_t)fsp->pcfs_ncluster;
7037c478bd9Sstevel@tonic-gate 	sp->f_bavail = sp->f_bfree = (fsblkcnt64_t)pc_freeclusters(fsp);
7047c478bd9Sstevel@tonic-gate 	sp->f_files = (fsfilcnt64_t)-1;
7057c478bd9Sstevel@tonic-gate 	sp->f_ffree = (fsfilcnt64_t)-1;
7067c478bd9Sstevel@tonic-gate 	sp->f_favail = (fsfilcnt64_t)-1;
7077c478bd9Sstevel@tonic-gate #ifdef notdef
7087c478bd9Sstevel@tonic-gate 	(void) cmpldev(&d32, fsp->pcfs_devvp->v_rdev);
7097c478bd9Sstevel@tonic-gate #endif /* notdef */
7107c478bd9Sstevel@tonic-gate 	(void) cmpldev(&d32, vfsp->vfs_dev);
7117c478bd9Sstevel@tonic-gate 	sp->f_fsid = d32;
7127c478bd9Sstevel@tonic-gate 	(void) strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name);
7137c478bd9Sstevel@tonic-gate 	sp->f_flag = vf_to_stf(vfsp->vfs_flag);
7147c478bd9Sstevel@tonic-gate 	sp->f_namemax = PCFNAMESIZE;
7157c478bd9Sstevel@tonic-gate 	return (0);
7167c478bd9Sstevel@tonic-gate }
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate static int
7197c478bd9Sstevel@tonic-gate pc_syncfsnodes(struct pcfs *fsp)
7207c478bd9Sstevel@tonic-gate {
7217c478bd9Sstevel@tonic-gate 	struct pchead *hp;
7227c478bd9Sstevel@tonic-gate 	struct pcnode *pcp;
7237c478bd9Sstevel@tonic-gate 	int error;
7247c478bd9Sstevel@tonic-gate 
7257c478bd9Sstevel@tonic-gate 	PC_DPRINTF0(7, "pcfs_syncfsnodes\n");
7267c478bd9Sstevel@tonic-gate 	if (error = pc_lockfs(fsp, 0, 0))
7277c478bd9Sstevel@tonic-gate 		return (error);
7287c478bd9Sstevel@tonic-gate 
7297c478bd9Sstevel@tonic-gate 	if (!(error = pc_syncfat(fsp))) {
7307c478bd9Sstevel@tonic-gate 		hp = pcfhead;
7317c478bd9Sstevel@tonic-gate 		while (hp < & pcfhead [ NPCHASH ]) {
7327c478bd9Sstevel@tonic-gate 			rw_enter(&pcnodes_lock, RW_READER);
7337c478bd9Sstevel@tonic-gate 			pcp = hp->pch_forw;
7347c478bd9Sstevel@tonic-gate 			while (pcp != (struct pcnode *)hp) {
7357c478bd9Sstevel@tonic-gate 				if (VFSTOPCFS(PCTOV(pcp) -> v_vfsp) == fsp)
7367c478bd9Sstevel@tonic-gate 					if (error = pc_nodesync(pcp))
7377c478bd9Sstevel@tonic-gate 						break;
7387c478bd9Sstevel@tonic-gate 				pcp = pcp -> pc_forw;
7397c478bd9Sstevel@tonic-gate 			}
7407c478bd9Sstevel@tonic-gate 			rw_exit(&pcnodes_lock);
7417c478bd9Sstevel@tonic-gate 			if (error)
7427c478bd9Sstevel@tonic-gate 				break;
7437c478bd9Sstevel@tonic-gate 			hp++;
7447c478bd9Sstevel@tonic-gate 		}
7457c478bd9Sstevel@tonic-gate 	}
7467c478bd9Sstevel@tonic-gate 	pc_unlockfs(fsp);
7477c478bd9Sstevel@tonic-gate 	return (error);
7487c478bd9Sstevel@tonic-gate }
7497c478bd9Sstevel@tonic-gate 
7507c478bd9Sstevel@tonic-gate /*
7517c478bd9Sstevel@tonic-gate  * Flush any pending I/O.
7527c478bd9Sstevel@tonic-gate  */
7537c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7547c478bd9Sstevel@tonic-gate static int
7557c478bd9Sstevel@tonic-gate pcfs_sync(
7567c478bd9Sstevel@tonic-gate 	struct vfs *vfsp,
7577c478bd9Sstevel@tonic-gate 	short flag,
7587c478bd9Sstevel@tonic-gate 	struct cred *cr)
7597c478bd9Sstevel@tonic-gate {
7607c478bd9Sstevel@tonic-gate 	struct pcfs *fsp;
7617c478bd9Sstevel@tonic-gate 	int error = 0;
7627c478bd9Sstevel@tonic-gate 
7637c478bd9Sstevel@tonic-gate 	/* this prevents the filesystem from being umounted. */
7647c478bd9Sstevel@tonic-gate 	mutex_enter(&pcfslock);
7657c478bd9Sstevel@tonic-gate 	if (vfsp != NULL) {
7667c478bd9Sstevel@tonic-gate 		fsp = VFSTOPCFS(vfsp);
7677c478bd9Sstevel@tonic-gate 		if (!(fsp->pcfs_flags & PCFS_IRRECOV)) {
7687c478bd9Sstevel@tonic-gate 			error = pc_syncfsnodes(fsp);
7697c478bd9Sstevel@tonic-gate 		} else {
7707c478bd9Sstevel@tonic-gate 			rw_enter(&pcnodes_lock, RW_WRITER);
7717c478bd9Sstevel@tonic-gate 			pc_diskchanged(fsp);
7727c478bd9Sstevel@tonic-gate 			rw_exit(&pcnodes_lock);
7737c478bd9Sstevel@tonic-gate 			error = EIO;
7747c478bd9Sstevel@tonic-gate 		}
7757c478bd9Sstevel@tonic-gate 	} else {
7767c478bd9Sstevel@tonic-gate 		fsp = pc_mounttab;
7777c478bd9Sstevel@tonic-gate 		while (fsp != NULL) {
7787c478bd9Sstevel@tonic-gate 			if (fsp->pcfs_flags & PCFS_IRRECOV) {
7797c478bd9Sstevel@tonic-gate 				rw_enter(&pcnodes_lock, RW_WRITER);
7807c478bd9Sstevel@tonic-gate 				pc_diskchanged(fsp);
7817c478bd9Sstevel@tonic-gate 				rw_exit(&pcnodes_lock);
7827c478bd9Sstevel@tonic-gate 				error = EIO;
7837c478bd9Sstevel@tonic-gate 				break;
7847c478bd9Sstevel@tonic-gate 			}
7857c478bd9Sstevel@tonic-gate 			error = pc_syncfsnodes(fsp);
7867c478bd9Sstevel@tonic-gate 			if (error) break;
7877c478bd9Sstevel@tonic-gate 			fsp = fsp->pcfs_nxt;
7887c478bd9Sstevel@tonic-gate 		}
7897c478bd9Sstevel@tonic-gate 	}
7907c478bd9Sstevel@tonic-gate 	mutex_exit(&pcfslock);
7917c478bd9Sstevel@tonic-gate 	return (error);
7927c478bd9Sstevel@tonic-gate }
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate int
7957c478bd9Sstevel@tonic-gate pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing)
7967c478bd9Sstevel@tonic-gate {
797264a6e74Sfrankho 	int err;
798264a6e74Sfrankho 
7997c478bd9Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_IRRECOV) && !releasing)
8007c478bd9Sstevel@tonic-gate 		return (EIO);
8017c478bd9Sstevel@tonic-gate 
8027c478bd9Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_LOCKED) && (fsp->pcfs_owner == curthread)) {
8037c478bd9Sstevel@tonic-gate 		fsp->pcfs_count++;
8047c478bd9Sstevel@tonic-gate 	} else {
8057c478bd9Sstevel@tonic-gate 		mutex_enter(&fsp->pcfs_lock);
8067c478bd9Sstevel@tonic-gate 		if (fsp->pcfs_flags & PCFS_LOCKED)
8077c478bd9Sstevel@tonic-gate 			panic("pc_lockfs");
8087c478bd9Sstevel@tonic-gate 		/*
8097c478bd9Sstevel@tonic-gate 		 * We check the IRRECOV bit again just in case somebody
8107c478bd9Sstevel@tonic-gate 		 * snuck past the initial check but then got held up before
8117c478bd9Sstevel@tonic-gate 		 * they could grab the lock.  (And in the meantime someone
8127c478bd9Sstevel@tonic-gate 		 * had grabbed the lock and set the bit)
8137c478bd9Sstevel@tonic-gate 		 */
8140576819eSwyllys 		if (!diskchanged && !(fsp->pcfs_flags & PCFS_IRRECOV)) {
815264a6e74Sfrankho 			if ((err = pc_getfat(fsp))) {
816264a6e74Sfrankho 				mutex_exit(&fsp->pcfs_lock);
8170576819eSwyllys 				return (err);
818264a6e74Sfrankho 			}
8190576819eSwyllys 		}
8207c478bd9Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_LOCKED;
8217c478bd9Sstevel@tonic-gate 		fsp->pcfs_owner = curthread;
8227c478bd9Sstevel@tonic-gate 		fsp->pcfs_count++;
8237c478bd9Sstevel@tonic-gate 	}
8247c478bd9Sstevel@tonic-gate 	return (0);
8257c478bd9Sstevel@tonic-gate }
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate void
8287c478bd9Sstevel@tonic-gate pc_unlockfs(struct pcfs *fsp)
8297c478bd9Sstevel@tonic-gate {
8307c478bd9Sstevel@tonic-gate 
8317c478bd9Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_LOCKED) == 0)
8327c478bd9Sstevel@tonic-gate 		panic("pc_unlockfs");
8337c478bd9Sstevel@tonic-gate 	if (--fsp->pcfs_count < 0)
8347c478bd9Sstevel@tonic-gate 		panic("pc_unlockfs: count");
8357c478bd9Sstevel@tonic-gate 	if (fsp->pcfs_count == 0) {
8367c478bd9Sstevel@tonic-gate 		fsp->pcfs_flags &= ~PCFS_LOCKED;
8377c478bd9Sstevel@tonic-gate 		fsp->pcfs_owner = 0;
8387c478bd9Sstevel@tonic-gate 		mutex_exit(&fsp->pcfs_lock);
8397c478bd9Sstevel@tonic-gate 	}
8407c478bd9Sstevel@tonic-gate }
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate /*
8437c478bd9Sstevel@tonic-gate  * isDosDrive()
8447c478bd9Sstevel@tonic-gate  *	Boolean function.  Give it the systid field for an fdisk partition
8457c478bd9Sstevel@tonic-gate  *	and it decides if that's a systid that describes a DOS drive.  We
8467c478bd9Sstevel@tonic-gate  *	use systid values defined in sys/dktp/fdisk.h.
8477c478bd9Sstevel@tonic-gate  */
8487c478bd9Sstevel@tonic-gate static int
8497c478bd9Sstevel@tonic-gate isDosDrive(uchar_t checkMe)
8507c478bd9Sstevel@tonic-gate {
8517c478bd9Sstevel@tonic-gate 	return ((checkMe == DOSOS12) || (checkMe == DOSOS16) ||
8527c478bd9Sstevel@tonic-gate 	    (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) ||
8537c478bd9Sstevel@tonic-gate 	    (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) ||
8547c478bd9Sstevel@tonic-gate 	    (checkMe == DIAGPART));
8557c478bd9Sstevel@tonic-gate }
8567c478bd9Sstevel@tonic-gate 
8577c478bd9Sstevel@tonic-gate /*
8587c478bd9Sstevel@tonic-gate  * isDosExtended()
8597c478bd9Sstevel@tonic-gate  *	Boolean function.  Give it the systid field for an fdisk partition
8607c478bd9Sstevel@tonic-gate  *	and it decides if that's a systid that describes an extended DOS
8617c478bd9Sstevel@tonic-gate  *	partition.
8627c478bd9Sstevel@tonic-gate  */
8637c478bd9Sstevel@tonic-gate static int
8647c478bd9Sstevel@tonic-gate isDosExtended(uchar_t checkMe)
8657c478bd9Sstevel@tonic-gate {
8667c478bd9Sstevel@tonic-gate 	return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA));
8677c478bd9Sstevel@tonic-gate }
8687c478bd9Sstevel@tonic-gate 
8697c478bd9Sstevel@tonic-gate /*
8707c478bd9Sstevel@tonic-gate  * isBootPart()
8717c478bd9Sstevel@tonic-gate  *	Boolean function.  Give it the systid field for an fdisk partition
8727c478bd9Sstevel@tonic-gate  *	and it decides if that's a systid that describes a Solaris boot
8737c478bd9Sstevel@tonic-gate  *	partition.
8747c478bd9Sstevel@tonic-gate  */
8757c478bd9Sstevel@tonic-gate static int
8767c478bd9Sstevel@tonic-gate isBootPart(uchar_t checkMe)
8777c478bd9Sstevel@tonic-gate {
8787c478bd9Sstevel@tonic-gate 	return (checkMe == X86BOOT);
8797c478bd9Sstevel@tonic-gate }
8807c478bd9Sstevel@tonic-gate 
8817c478bd9Sstevel@tonic-gate /*
8827c478bd9Sstevel@tonic-gate  * noLogicalDrive()
8837c478bd9Sstevel@tonic-gate  *	Display error message about not being able to find a logical
8847c478bd9Sstevel@tonic-gate  *	drive.
8857c478bd9Sstevel@tonic-gate  */
8867c478bd9Sstevel@tonic-gate static void
8877c478bd9Sstevel@tonic-gate noLogicalDrive(int requested)
8887c478bd9Sstevel@tonic-gate {
8897c478bd9Sstevel@tonic-gate 	if (requested == BOOT_PARTITION_DRIVE) {
8907c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: no boot partition");
8917c478bd9Sstevel@tonic-gate 	} else {
8927c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: no such logical drive");
8937c478bd9Sstevel@tonic-gate 	}
8947c478bd9Sstevel@tonic-gate }
8957c478bd9Sstevel@tonic-gate 
8967c478bd9Sstevel@tonic-gate /*
8977c478bd9Sstevel@tonic-gate  * findTheDrive()
8987c478bd9Sstevel@tonic-gate  *	Discover offset of the requested logical drive, and return
8997c478bd9Sstevel@tonic-gate  *	that offset (startSector), the systid of that drive (sysid),
9007c478bd9Sstevel@tonic-gate  *	and a buffer pointer (bp), with the buffer contents being
9017c478bd9Sstevel@tonic-gate  *	the first sector of the logical drive (i.e., the sector that
9027c478bd9Sstevel@tonic-gate  *	contains the BPB for that drive).
9037c478bd9Sstevel@tonic-gate  */
9047c478bd9Sstevel@tonic-gate static int
905*9bd42341Sfrankho findTheDrive(dev_t dev, int ldrive, buf_t **bp,
9067c478bd9Sstevel@tonic-gate     daddr_t *startSector, uchar_t *sysid)
9077c478bd9Sstevel@tonic-gate {
9087c478bd9Sstevel@tonic-gate 	struct ipart dosp[FD_NUMPART];	/* incore fdisk partition structure */
9097c478bd9Sstevel@tonic-gate 	struct mboot *dosp_ptr;		/* boot structure pointer */
9107c478bd9Sstevel@tonic-gate 	daddr_t lastseek = 0;		/* Disk block we sought previously */
9117c478bd9Sstevel@tonic-gate 	daddr_t diskblk = 0;		/* Disk block to get */
9127c478bd9Sstevel@tonic-gate 	daddr_t xstartsect;		/* base of Extended DOS partition */
9137c478bd9Sstevel@tonic-gate 	int logicalDriveCount = 0;	/* Count of logical drives seen */
9147c478bd9Sstevel@tonic-gate 	int extendedPart = -1;		/* index of extended dos partition */
9157c478bd9Sstevel@tonic-gate 	int primaryPart = -1;		/* index of primary dos partition */
9167c478bd9Sstevel@tonic-gate 	int bootPart = -1;		/* index of a Solaris boot partition */
9177c478bd9Sstevel@tonic-gate 	int xnumsect = -1;		/* length of extended DOS partition */
9187c478bd9Sstevel@tonic-gate 	int driveIndex;			/* computed FDISK table index */
9197c478bd9Sstevel@tonic-gate 	int i;
9207c478bd9Sstevel@tonic-gate 	/*
9217c478bd9Sstevel@tonic-gate 	 * Count of drives in the current extended partition's
9227c478bd9Sstevel@tonic-gate 	 * FDISK table, and indexes of the drives themselves.
9237c478bd9Sstevel@tonic-gate 	 */
9247c478bd9Sstevel@tonic-gate 	int extndDrives[FD_NUMPART];
9257c478bd9Sstevel@tonic-gate 	int numDrives = 0;
9267c478bd9Sstevel@tonic-gate 
9277c478bd9Sstevel@tonic-gate 	/*
9287c478bd9Sstevel@tonic-gate 	 * Count of drives (beyond primary) in master boot record's
9297c478bd9Sstevel@tonic-gate 	 * FDISK table, and indexes of the drives themselves.
9307c478bd9Sstevel@tonic-gate 	 */
9317c478bd9Sstevel@tonic-gate 	int extraDrives[FD_NUMPART];
9327c478bd9Sstevel@tonic-gate 	int numExtraDrives = 0;
9337c478bd9Sstevel@tonic-gate 
934*9bd42341Sfrankho 	/*
935*9bd42341Sfrankho 	 * "ldrive == 0" should never happen, as this is a request to
936*9bd42341Sfrankho 	 * mount the physical device (and ignore partitioning). The code
937*9bd42341Sfrankho 	 * in pcfs_mount() should have made sure that a logical drive number
938*9bd42341Sfrankho 	 * is at least 1, meaning we're looking for drive "C:". It is not
939*9bd42341Sfrankho 	 * safe (and a bug in the callers of this function) to request logical
940*9bd42341Sfrankho 	 * drive number 0; we could ASSERT() but a graceful EIO is a more
941*9bd42341Sfrankho 	 * polite way.
942*9bd42341Sfrankho 	 */
943*9bd42341Sfrankho 	if (ldrive == 0) {
944*9bd42341Sfrankho 		cmn_err(CE_NOTE, "!pcfs: request for logical partition zero");
945*9bd42341Sfrankho 		noLogicalDrive(ldrive);
946*9bd42341Sfrankho 		return (EIO);
947*9bd42341Sfrankho 	}
948*9bd42341Sfrankho 
9497c478bd9Sstevel@tonic-gate 	/*
9507c478bd9Sstevel@tonic-gate 	 *  Copy from disk block into memory aligned structure for fdisk usage.
9517c478bd9Sstevel@tonic-gate 	 */
9527c478bd9Sstevel@tonic-gate 	dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr;
9537c478bd9Sstevel@tonic-gate 	bcopy(dosp_ptr->parts, dosp, sizeof (struct ipart) * FD_NUMPART);
9547c478bd9Sstevel@tonic-gate 
955*9bd42341Sfrankho 	if (ltohs(dosp_ptr->signature) != MBB_MAGIC) {
956*9bd42341Sfrankho 		cmn_err(CE_NOTE, "!pcfs: MBR partition table signature err");
957*9bd42341Sfrankho 		return (EINVAL);
958*9bd42341Sfrankho 	}
959*9bd42341Sfrankho 
9607c478bd9Sstevel@tonic-gate 	/*
9617c478bd9Sstevel@tonic-gate 	 * Get a summary of what is in the Master FDISK table.
9627c478bd9Sstevel@tonic-gate 	 * Normally we expect to find one partition marked as a DOS drive.
9637c478bd9Sstevel@tonic-gate 	 * This partition is the one Windows calls the primary dos partition.
9647c478bd9Sstevel@tonic-gate 	 * If the machine has any logical drives then we also expect
9657c478bd9Sstevel@tonic-gate 	 * to find a partition marked as an extended DOS partition.
9667c478bd9Sstevel@tonic-gate 	 *
9677c478bd9Sstevel@tonic-gate 	 * Sometimes we'll find multiple partitions marked as DOS drives.
9687c478bd9Sstevel@tonic-gate 	 * The Solaris fdisk program allows these partitions
9697c478bd9Sstevel@tonic-gate 	 * to be created, but Windows fdisk no longer does.  We still need
9707c478bd9Sstevel@tonic-gate 	 * to support these, though, since Windows does.  We also need to fix
9717c478bd9Sstevel@tonic-gate 	 * our fdisk to behave like the Windows version.
9727c478bd9Sstevel@tonic-gate 	 *
9737c478bd9Sstevel@tonic-gate 	 * It turns out that some off-the-shelf media have *only* an
9747c478bd9Sstevel@tonic-gate 	 * Extended partition, so we need to deal with that case as well.
9757c478bd9Sstevel@tonic-gate 	 *
9767c478bd9Sstevel@tonic-gate 	 * Only a single (the first) Extended or Boot Partition will
9777c478bd9Sstevel@tonic-gate 	 * be recognized.  Any others will be ignored.
9787c478bd9Sstevel@tonic-gate 	 */
9797c478bd9Sstevel@tonic-gate 	for (i = 0; i < FD_NUMPART; i++) {
9800576819eSwyllys 		PC_DPRINTF1(2, "findTheDrive: found partition type %02x",
9810576819eSwyllys 			dosp[i].systid);
9820576819eSwyllys 
9837c478bd9Sstevel@tonic-gate 		if (isDosDrive(dosp[i].systid)) {
9847c478bd9Sstevel@tonic-gate 			if (primaryPart < 0) {
9857c478bd9Sstevel@tonic-gate 				logicalDriveCount++;
9867c478bd9Sstevel@tonic-gate 				primaryPart = i;
9877c478bd9Sstevel@tonic-gate 			} else {
9887c478bd9Sstevel@tonic-gate 				extraDrives[numExtraDrives++] = i;
9897c478bd9Sstevel@tonic-gate 			}
9907c478bd9Sstevel@tonic-gate 			continue;
9917c478bd9Sstevel@tonic-gate 		}
9927c478bd9Sstevel@tonic-gate 		if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) {
9937c478bd9Sstevel@tonic-gate 			extendedPart = i;
9947c478bd9Sstevel@tonic-gate 			continue;
9957c478bd9Sstevel@tonic-gate 		}
9967c478bd9Sstevel@tonic-gate 		if ((bootPart < 0) && isBootPart(dosp[i].systid)) {
9977c478bd9Sstevel@tonic-gate 			bootPart = i;
9987c478bd9Sstevel@tonic-gate 			continue;
9997c478bd9Sstevel@tonic-gate 		}
10007c478bd9Sstevel@tonic-gate 	}
10017c478bd9Sstevel@tonic-gate 
1002*9bd42341Sfrankho 	if (ldrive == BOOT_PARTITION_DRIVE) {
10037c478bd9Sstevel@tonic-gate 		if (bootPart < 0) {
1004*9bd42341Sfrankho 			noLogicalDrive(ldrive);
1005*9bd42341Sfrankho 			return (EINVAL);
10067c478bd9Sstevel@tonic-gate 		}
10077c478bd9Sstevel@tonic-gate 		*sysid = dosp[bootPart].systid;
10087c478bd9Sstevel@tonic-gate 		*startSector = ltohi(dosp[bootPart].relsect);
1009*9bd42341Sfrankho 		return (0);
10107c478bd9Sstevel@tonic-gate 	}
10117c478bd9Sstevel@tonic-gate 
1012*9bd42341Sfrankho 	if (ldrive == PRIMARY_DOS_DRIVE && primaryPart >= 0) {
10137c478bd9Sstevel@tonic-gate 		*sysid = dosp[primaryPart].systid;
10147c478bd9Sstevel@tonic-gate 		*startSector = ltohi(dosp[primaryPart].relsect);
1015*9bd42341Sfrankho 		return (0);
10167c478bd9Sstevel@tonic-gate 	}
10177c478bd9Sstevel@tonic-gate 
10187c478bd9Sstevel@tonic-gate 	/*
10197c478bd9Sstevel@tonic-gate 	 * We are not looking for the C: drive (or the primary drive
10207c478bd9Sstevel@tonic-gate 	 * was not found), so we had better have an extended partition
10217c478bd9Sstevel@tonic-gate 	 * or extra drives in the Master FDISK table.
10227c478bd9Sstevel@tonic-gate 	 */
10237c478bd9Sstevel@tonic-gate 	if ((extendedPart < 0) && (numExtraDrives == 0)) {
10247c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: no extended dos partition");
1025*9bd42341Sfrankho 		noLogicalDrive(ldrive);
1026*9bd42341Sfrankho 		return (EINVAL);
10277c478bd9Sstevel@tonic-gate 	}
10287c478bd9Sstevel@tonic-gate 
10297c478bd9Sstevel@tonic-gate 	if (extendedPart >= 0) {
10307c478bd9Sstevel@tonic-gate 		diskblk = xstartsect = ltohi(dosp[extendedPart].relsect);
10317c478bd9Sstevel@tonic-gate 		xnumsect = ltohi(dosp[extendedPart].numsect);
10327c478bd9Sstevel@tonic-gate 		do {
10337c478bd9Sstevel@tonic-gate 			/*
10347c478bd9Sstevel@tonic-gate 			 *  If the seek would not cause us to change
10357c478bd9Sstevel@tonic-gate 			 *  position on the drive, then we're out of
10367c478bd9Sstevel@tonic-gate 			 *  extended partitions to examine.
10377c478bd9Sstevel@tonic-gate 			 */
10387c478bd9Sstevel@tonic-gate 			if (diskblk == lastseek)
10397c478bd9Sstevel@tonic-gate 				break;
10407c478bd9Sstevel@tonic-gate 			logicalDriveCount += numDrives;
10417c478bd9Sstevel@tonic-gate 			/*
10427c478bd9Sstevel@tonic-gate 			 *  Seek the next extended partition, and find
10437c478bd9Sstevel@tonic-gate 			 *  logical drives within it.
10447c478bd9Sstevel@tonic-gate 			 */
10457c478bd9Sstevel@tonic-gate 			brelse(*bp);
10467c478bd9Sstevel@tonic-gate 			*bp = bread(dev, diskblk, PC_SAFESECSIZE);
10477c478bd9Sstevel@tonic-gate 			if ((*bp)->b_flags & B_ERROR) {
10487c478bd9Sstevel@tonic-gate 				PC_DPRINTF0(1, "pc_getfattype: read error\n");
1049*9bd42341Sfrankho 				return (EIO);
10507c478bd9Sstevel@tonic-gate 			}
10517c478bd9Sstevel@tonic-gate 			lastseek = diskblk;
10527c478bd9Sstevel@tonic-gate 			dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr;
10537c478bd9Sstevel@tonic-gate 			if (ltohs(dosp_ptr->signature) != MBB_MAGIC) {
10547c478bd9Sstevel@tonic-gate 				cmn_err(CE_NOTE, "!pcfs: "
10557c478bd9Sstevel@tonic-gate 				    "extended partition signature err");
1056*9bd42341Sfrankho 				return (EINVAL);
10577c478bd9Sstevel@tonic-gate 			}
10587c478bd9Sstevel@tonic-gate 			bcopy(dosp_ptr->parts, dosp,
10597c478bd9Sstevel@tonic-gate 			    sizeof (struct ipart) * FD_NUMPART);
10607c478bd9Sstevel@tonic-gate 			/*
10617c478bd9Sstevel@tonic-gate 			 *  Count up drives, and track where the next
10627c478bd9Sstevel@tonic-gate 			 *  extended partition is in case we need it.  We
10637c478bd9Sstevel@tonic-gate 			 *  are expecting only one extended partition.  If
10647c478bd9Sstevel@tonic-gate 			 *  there is more than one we'll only go to the
10657c478bd9Sstevel@tonic-gate 			 *  first one we see, but warn about ignoring.
10667c478bd9Sstevel@tonic-gate 			 */
10677c478bd9Sstevel@tonic-gate 			numDrives = 0;
10687c478bd9Sstevel@tonic-gate 			for (i = 0; i < FD_NUMPART; i++) {
10697c478bd9Sstevel@tonic-gate 				if (isDosDrive(dosp[i].systid)) {
10707c478bd9Sstevel@tonic-gate 					extndDrives[numDrives++] = i;
10717c478bd9Sstevel@tonic-gate 				} else if (isDosExtended(dosp[i].systid)) {
10727c478bd9Sstevel@tonic-gate 					if (diskblk != lastseek) {
10737c478bd9Sstevel@tonic-gate 						/*
10747c478bd9Sstevel@tonic-gate 						 * Already found an extended
10757c478bd9Sstevel@tonic-gate 						 * partition in this table.
10767c478bd9Sstevel@tonic-gate 						 */
10777c478bd9Sstevel@tonic-gate 						cmn_err(CE_NOTE,
10787c478bd9Sstevel@tonic-gate 						    "!pcfs: ignoring unexpected"
10797c478bd9Sstevel@tonic-gate 						    " additional extended"
10807c478bd9Sstevel@tonic-gate 						    " partition");
1081*9bd42341Sfrankho 					} else {
1082*9bd42341Sfrankho 						diskblk = xstartsect +
1083*9bd42341Sfrankho 						    ltohi(dosp[i].relsect);
10847c478bd9Sstevel@tonic-gate 					}
10857c478bd9Sstevel@tonic-gate 				}
10867c478bd9Sstevel@tonic-gate 			}
1087*9bd42341Sfrankho 		} while (ldrive > logicalDriveCount + numDrives);
10887c478bd9Sstevel@tonic-gate 
1089*9bd42341Sfrankho 		if (ldrive <= logicalDriveCount + numDrives) {
10907c478bd9Sstevel@tonic-gate 			/*
10917c478bd9Sstevel@tonic-gate 			 * The number of logical drives we've found thus
10927c478bd9Sstevel@tonic-gate 			 * far is enough to get us to the one we were
10937c478bd9Sstevel@tonic-gate 			 * searching for.
10947c478bd9Sstevel@tonic-gate 			 */
1095*9bd42341Sfrankho 			driveIndex = logicalDriveCount + numDrives - ldrive;
10967c478bd9Sstevel@tonic-gate 			*sysid = dosp[extndDrives[driveIndex]].systid;
10977c478bd9Sstevel@tonic-gate 			*startSector =
10987c478bd9Sstevel@tonic-gate 			    ltohi(dosp[extndDrives[driveIndex]].relsect) +
10997c478bd9Sstevel@tonic-gate 			    lastseek;
11007c478bd9Sstevel@tonic-gate 			if (*startSector > (xstartsect + xnumsect)) {
11017c478bd9Sstevel@tonic-gate 				cmn_err(CE_NOTE, "!pcfs: extended partition "
11027c478bd9Sstevel@tonic-gate 				    "values bad");
1103*9bd42341Sfrankho 				return (EINVAL);
11047c478bd9Sstevel@tonic-gate 			}
1105*9bd42341Sfrankho 			return (0);
11067c478bd9Sstevel@tonic-gate 		} else {
11077c478bd9Sstevel@tonic-gate 			/*
11087c478bd9Sstevel@tonic-gate 			 * We ran out of extended dos partition
11097c478bd9Sstevel@tonic-gate 			 * drives.  The only hope now is to go
11107c478bd9Sstevel@tonic-gate 			 * back to extra drives defined in the master
11117c478bd9Sstevel@tonic-gate 			 * fdisk table.  But we overwrote that table
11127c478bd9Sstevel@tonic-gate 			 * already, so we must load it in again.
11137c478bd9Sstevel@tonic-gate 			 */
11147c478bd9Sstevel@tonic-gate 			logicalDriveCount += numDrives;
11157c478bd9Sstevel@tonic-gate 			brelse(*bp);
11167c478bd9Sstevel@tonic-gate 			*bp = bread(dev, (daddr_t)0, PC_SAFESECSIZE);
11177c478bd9Sstevel@tonic-gate 			if ((*bp)->b_flags & B_ERROR) {
11187c478bd9Sstevel@tonic-gate 				PC_DPRINTF0(1, "pc_getfattype: read error\n");
1119*9bd42341Sfrankho 				return (EIO);
11207c478bd9Sstevel@tonic-gate 			}
11217c478bd9Sstevel@tonic-gate 			dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr;
11227c478bd9Sstevel@tonic-gate 			bcopy(dosp_ptr->parts, dosp,
11237c478bd9Sstevel@tonic-gate 			    sizeof (struct ipart) * FD_NUMPART);
11247c478bd9Sstevel@tonic-gate 		}
11257c478bd9Sstevel@tonic-gate 	}
11267c478bd9Sstevel@tonic-gate 	/*
11277c478bd9Sstevel@tonic-gate 	 *  Still haven't found the drive, is it an extra
11287c478bd9Sstevel@tonic-gate 	 *  drive defined in the main FDISK table?
11297c478bd9Sstevel@tonic-gate 	 */
1130*9bd42341Sfrankho 	if (ldrive <= logicalDriveCount + numExtraDrives) {
1131*9bd42341Sfrankho 		driveIndex = logicalDriveCount + numExtraDrives - ldrive;
1132*9bd42341Sfrankho 		ASSERT(driveIndex < MIN(numExtraDrives, FD_NUMPART));
11337c478bd9Sstevel@tonic-gate 		*sysid = dosp[extraDrives[driveIndex]].systid;
11347c478bd9Sstevel@tonic-gate 		*startSector = ltohi(dosp[extraDrives[driveIndex]].relsect);
1135*9bd42341Sfrankho 		return (0);
11367c478bd9Sstevel@tonic-gate 	}
11377c478bd9Sstevel@tonic-gate 	/*
11387c478bd9Sstevel@tonic-gate 	 *  Still haven't found the drive, and there is
11397c478bd9Sstevel@tonic-gate 	 *  nowhere else to look.
11407c478bd9Sstevel@tonic-gate 	 */
1141*9bd42341Sfrankho 	noLogicalDrive(ldrive);
1142*9bd42341Sfrankho 	return (EINVAL);
11437c478bd9Sstevel@tonic-gate }
11447c478bd9Sstevel@tonic-gate 
11450576819eSwyllys /*
11460576819eSwyllys  * FAT12/FAT16 specific consistency checks.
11470576819eSwyllys  */
11480576819eSwyllys static int
11490576819eSwyllys check_bpb_fat16(struct bootsec *bpb)
11500576819eSwyllys {
11510576819eSwyllys 	if (pcfsdebuglevel >= 5) {
11520576819eSwyllys 		PC_DPRINTF1(5, "check_bpb_fat: RootEntCount = %d",
11530576819eSwyllys 			ltohs(bpb->rdirents[0]));
11540576819eSwyllys 		PC_DPRINTF1(5, "check_bpb_fat16: TotSec16 = %d",
11550576819eSwyllys 			ltohs(bpb->numsect[0]));
11560576819eSwyllys 		PC_DPRINTF1(5, "check_bpb_fat16: FATSz16 = %d",
11570576819eSwyllys 			ltohs(bpb->fatsec));
11580576819eSwyllys 		PC_DPRINTF1(5, "check_bpb_fat16: TotSec32 = %d",
11590576819eSwyllys 			ltohi(bpb->totalsec));
11600576819eSwyllys 	}
11610576819eSwyllys 	return (ltohs(bpb->rdirents[0]) > 0 &&	 /* RootEntCnt > 0 */
11620576819eSwyllys 		((ltohs(bpb->numsect[0]) == 0 && /* TotSec16 == 0 */
11630576819eSwyllys 		ltohi(bpb->totalsec) > 0) ||	 /* TotSec32 > 0 */
11640576819eSwyllys 		ltohs(bpb->numsect[0]) > 0) && /* TotSec16 > 0 */
11650576819eSwyllys 		ltohs(bpb->fatsec) > 0);	 /* FatSz16 > 0 */
11660576819eSwyllys }
11670576819eSwyllys 
11680576819eSwyllys /*
11690576819eSwyllys  * FAT32 specific consistency checks.
11700576819eSwyllys  */
11710576819eSwyllys static int
11720576819eSwyllys check_bpb_fat32(struct fat32_bootsec *bpb)
11730576819eSwyllys {
11740576819eSwyllys 	if (pcfsdebuglevel >= 5) {
11750576819eSwyllys 		PC_DPRINTF1(5, "check_bpb_fat32: RootEntCount = %d",
11760576819eSwyllys 			ltohs(bpb->f_bs.rdirents[0]));
11770576819eSwyllys 		PC_DPRINTF1(5, "check_bpb_fat32: TotSec16 = %d",
11780576819eSwyllys 			ltohs(bpb->f_bs.numsect[0]));
11790576819eSwyllys 		PC_DPRINTF1(5, "check_bpb_fat32: FATSz16 = %d",
11800576819eSwyllys 			ltohs(bpb->f_bs.fatsec));
11810576819eSwyllys 		PC_DPRINTF1(5, "check_bpb_fat32: TotSec32 = %d",
11820576819eSwyllys 			ltohi(bpb->f_bs.totalsec));
11830576819eSwyllys 		PC_DPRINTF1(5, "check_bpb_fat32: FATSz32 = %d",
11840576819eSwyllys 			ltohi(bpb->f_fatlength));
11850576819eSwyllys 	}
11860576819eSwyllys 	return (ltohs(bpb->f_bs.rdirents[0]) == 0 &&
11870576819eSwyllys 		ltohs(bpb->f_bs.numsect[0]) == 0 &&
11880576819eSwyllys 		ltohs(bpb->f_bs.fatsec) == 0 &&
11890576819eSwyllys 		ltohi(bpb->f_bs.totalsec) > 0 &&
11900576819eSwyllys 		ltohi(bpb->f_fatlength) > 0);
11910576819eSwyllys }
11920576819eSwyllys 
11930576819eSwyllys /*
11940576819eSwyllys  * Calculate the number of clusters in order to determine
11950576819eSwyllys  * the type of FAT we are looking at.  This is the only
11960576819eSwyllys  * recommended way of determining FAT type, though there
11970576819eSwyllys  * are other hints in the data, this is the best way.
11980576819eSwyllys  */
11990576819eSwyllys static ulong_t
12000576819eSwyllys bpb_to_numclusters(uchar_t *cp)
12010576819eSwyllys {
12020576819eSwyllys 	struct fat32_bootsec *bpb;
12030576819eSwyllys 
12040576819eSwyllys 	ulong_t rootdirsectors;
12050576819eSwyllys 	ulong_t FATsz;
12060576819eSwyllys 	ulong_t TotSec;
12070576819eSwyllys 	ulong_t DataSec;
12080576819eSwyllys 	ulong_t CountOfClusters;
12090576819eSwyllys 	char FileSysType[9];
12100576819eSwyllys 
12110576819eSwyllys 	/*
12120576819eSwyllys 	 * Cast it to FAT32 bpb. If it turns out to be FAT12/16, its
12130576819eSwyllys 	 * OK, we won't try accessing the data beyond the FAT16 header
12140576819eSwyllys 	 * boundary.
12150576819eSwyllys 	 */
12160576819eSwyllys 	bpb = (struct fat32_bootsec *)cp;
12170576819eSwyllys 
12180576819eSwyllys 	if (pcfsdebuglevel >= 5) {
12190576819eSwyllys 		if (ltohs(bpb->f_bs.rdirents[0]) != 0) {
122057e22c6cSwyllys 			(void) memcpy(FileSysType, &cp[54], 8);
12210576819eSwyllys 			FileSysType[8] = 0;
12220576819eSwyllys 			PC_DPRINTF1(5, "debug_bpb: FAT12/FAT16 FileSysType = "
12230576819eSwyllys 				"%s", FileSysType);
12240576819eSwyllys 		}
12250576819eSwyllys 	}
12260576819eSwyllys 
12270576819eSwyllys 	rootdirsectors = ((ltohs(bpb->f_bs.rdirents[0]) * 32) +
12280576819eSwyllys 		(ltohs(bpb->f_bs.bps[0]) - 1)) / ltohs(bpb->f_bs.bps[0]);
12290576819eSwyllys 
12300576819eSwyllys 	if (ltohs(bpb->f_bs.fatsec) != 0)
12310576819eSwyllys 		FATsz = ltohs(bpb->f_bs.fatsec);
12320576819eSwyllys 	else
12330576819eSwyllys 		FATsz = ltohi(bpb->f_fatlength);
12340576819eSwyllys 
12350576819eSwyllys 	if (ltohs(bpb->f_bs.numsect[0]) != 0)
12360576819eSwyllys 		TotSec = ltohs(bpb->f_bs.numsect[0]);
12370576819eSwyllys 	else
12380576819eSwyllys 		TotSec = ltohi(bpb->f_bs.totalsec);
12390576819eSwyllys 
12400576819eSwyllys 	DataSec = TotSec - (ltohs(bpb->f_bs.res_sec[0]) +
12410576819eSwyllys 			(bpb->f_bs.nfat * FATsz) + rootdirsectors);
12420576819eSwyllys 
12430576819eSwyllys 	CountOfClusters = DataSec / bpb->f_bs.spcl;
12440576819eSwyllys 
12450576819eSwyllys 	PC_DPRINTF1(5, "debug_bpb: CountOfClusters = %ld", CountOfClusters);
12460576819eSwyllys 
12470576819eSwyllys 	return (CountOfClusters);
12480576819eSwyllys 
12490576819eSwyllys }
12500576819eSwyllys 
12510576819eSwyllys static int
12520576819eSwyllys fattype(ulong_t CountOfClusters)
12530576819eSwyllys {
12540576819eSwyllys 	/*
12550576819eSwyllys 	 * From Microsoft:
12560576819eSwyllys 	 * In the following example, when it says <, it does not mean <=.
12570576819eSwyllys 	 * Note also that the numbers are correct.  The first number for
12580576819eSwyllys 	 * FAT12 is 4085; the second number for FAT16 is 65525. These numbers
12590576819eSwyllys 	 * and the '<' signs are not wrong.
12600576819eSwyllys 	 */
12610576819eSwyllys 
12620576819eSwyllys 	/* Watch for edge cases */
12630576819eSwyllys 	if ((CountOfClusters >= 4085 && CountOfClusters <= 4095) ||
12640576819eSwyllys 	    (CountOfClusters >= 65525 && CountOfClusters <= 65535)) {
12650576819eSwyllys 		PC_DPRINTF1(5, "debug_bpb: Cannot determine FAT yet - %ld",
12660576819eSwyllys 			CountOfClusters);
12670576819eSwyllys 		return (-1); /* Cannot be determined yet */
12680576819eSwyllys 	} else if (CountOfClusters < 4085) {
12690576819eSwyllys 		/* Volume is FAT12 */
12700576819eSwyllys 		PC_DPRINTF0(5, "debug_bpb: This must be FAT12");
12710576819eSwyllys 		return (0);
12720576819eSwyllys 	} else if (CountOfClusters < 65525) {
12730576819eSwyllys 		/* Volume is FAT16 */
12740576819eSwyllys 		PC_DPRINTF0(5, "debug_bpb: This must be FAT16");
12750576819eSwyllys 		return (PCFS_FAT16);
12760576819eSwyllys 	} else {
12770576819eSwyllys 		/* Volume is FAT32 */
12780576819eSwyllys 		PC_DPRINTF0(5, "debug_bpb: This must be FAT32");
12790576819eSwyllys 		return (PCFS_FAT32);
12800576819eSwyllys 	}
12810576819eSwyllys }
12820576819eSwyllys 
12830576819eSwyllys #define	VALID_SECSIZE(s) (s == 512 || s == 1024 || s == 2048 || s == 4096)
12840576819eSwyllys 
12850576819eSwyllys #define	VALID_SPCL(s) (s == 1 || s == 2 || s == 4 || s == 8 || s == 16 ||\
12860576819eSwyllys 	s == 32 || s == 64 || s == 128)
12870576819eSwyllys 
12880576819eSwyllys static int
12890576819eSwyllys secondaryBPBChecks(uchar_t *cp)
12900576819eSwyllys {
12910576819eSwyllys 	struct bootsec *bpb = (struct bootsec *)cp;
12920576819eSwyllys 	struct fat32_bootsec *f32bpb = (struct fat32_bootsec *)cp;
12930576819eSwyllys 
12940576819eSwyllys 	/*
12950576819eSwyllys 	 * Perform secondary checks to try and determine what sort
12960576819eSwyllys 	 * of FAT partition we have based on other, less reliable,
12970576819eSwyllys 	 * data in the BPB header.
12980576819eSwyllys 	 */
12990576819eSwyllys 	if (ltohs(bpb->fatsec) != 0) {
13000576819eSwyllys 		/*
13010576819eSwyllys 		 * Must be FAT12 or FAT16, check the
13020576819eSwyllys 		 * FilSysType string (not 100% reliable).
13030576819eSwyllys 		 */
13040576819eSwyllys 		if (!memcmp((cp + PCFS_TYPESTRING_OFFSET16), "FAT12", 5)) {
13050576819eSwyllys 			PC_DPRINTF0(5, "secondaryBPBCheck says: FAT12");
13060576819eSwyllys 			return (0); /* FAT12 */
13070576819eSwyllys 		} else if (!memcmp((cp + PCFS_TYPESTRING_OFFSET16), "FAT16",
13080576819eSwyllys 			5)) {
13090576819eSwyllys 			PC_DPRINTF0(5, "secondaryBPBCheck says: FAT16");
13100576819eSwyllys 			return (PCFS_FAT16);
13110576819eSwyllys 		} else {
13120576819eSwyllys 			/*
13130576819eSwyllys 			 * Try to use the BPB_Media byte
13140576819eSwyllys 			 *
13150576819eSwyllys 			 *  If the media byte indicates a floppy we'll
13160576819eSwyllys 			 *  assume FAT12, otherwise we'll assume FAT16.
13170576819eSwyllys 			 */
13180576819eSwyllys 			switch (bpb->mediadesriptor) {
13190576819eSwyllys 				case SS8SPT:
13200576819eSwyllys 				case DS8SPT:
13210576819eSwyllys 				case SS9SPT:
13220576819eSwyllys 				case DS9SPT:
13230576819eSwyllys 				case DS18SPT:
13240576819eSwyllys 				case DS9_15SPT:
13250576819eSwyllys 					PC_DPRINTF0(5,
13260576819eSwyllys 					"secondaryBPBCheck says: FAT12");
13270576819eSwyllys 					return (0); /* FAT12 */
13280576819eSwyllys 				case MD_FIXED:
13290576819eSwyllys 					PC_DPRINTF0(5,
13300576819eSwyllys 					"secondaryBPBCheck says: FAT16");
13310576819eSwyllys 					return (PCFS_FAT16);
13320576819eSwyllys 				default:
13330576819eSwyllys 					cmn_err(CE_NOTE,
13340576819eSwyllys 						"!pcfs: unknown FAT type");
13350576819eSwyllys 					return (-1);
13360576819eSwyllys 			}
13370576819eSwyllys 		}
13380576819eSwyllys 	} else if (ltohi(f32bpb->f_fatlength) > 0) {
13390576819eSwyllys 		PC_DPRINTF0(5, "secondaryBPBCheck says: FAT32");
13400576819eSwyllys 		return (PCFS_FAT32);
13410576819eSwyllys 	} else {
13420576819eSwyllys 		/* We don't know */
13430576819eSwyllys 		PC_DPRINTF0(5, "secondaryBPBCheck says: unknown!!");
13440576819eSwyllys 		return (-1);
13450576819eSwyllys 	}
13460576819eSwyllys }
13470576819eSwyllys 
13480576819eSwyllys /*
13490576819eSwyllys  * Check to see if the BPB we found is correct.
13500576819eSwyllys  *
13510576819eSwyllys  * First, look for obvious, tell-tale signs of trouble:
13520576819eSwyllys  * The NumFATs value should always be 2.  Sometimes it can be a '1'
13530576819eSwyllys  * on FLASH memory cards and other non-disk-based media, so we
13540576819eSwyllys  * will allow that as well.
13550576819eSwyllys  *
13560576819eSwyllys  * We also look at the Media byte, the valid range is 0xF0, or
13570576819eSwyllys  * 0xF8 thru 0xFF, anything else means this is probably not a good
13580576819eSwyllys  * BPB.
13590576819eSwyllys  *
13600576819eSwyllys  * Finally, check the BPB Magic number at the end of the 512 byte
13610576819eSwyllys  * block, it must be 0xAA55.
13620576819eSwyllys  *
13630576819eSwyllys  * If that all is good, calculate the number of clusters and
13640576819eSwyllys  * do some final verification steps.
13650576819eSwyllys  *
1366*9bd42341Sfrankho  * If all is well, return success (1) and set the fattypep value to the
1367*9bd42341Sfrankho  * correct FAT value if the caller provided a pointer to store it in.
13680576819eSwyllys  */
13690576819eSwyllys static int
13700576819eSwyllys isBPB(uchar_t *cp, int *fattypep)
13710576819eSwyllys {
13720576819eSwyllys 	struct bootsec *bpb = (struct bootsec *)cp;
1373*9bd42341Sfrankho 	int type;
13740576819eSwyllys 
13750576819eSwyllys 	uint_t numclusters;		/* number of clusters in file area */
13760576819eSwyllys 	ushort_t secsize = (int)ltohs(bpb->bps[0]);
13770576819eSwyllys 
13780576819eSwyllys 	if (pcfsdebuglevel >= 3) {
13790576819eSwyllys 		if (!VALID_SECSIZE(secsize))
13800576819eSwyllys 			PC_DPRINTF1(3, "check_bpb: invalid bps value %d",
13810576819eSwyllys 				secsize);
13820576819eSwyllys 
13830576819eSwyllys 		if (!VALID_SPCL(bpb->spcl))
13840576819eSwyllys 			PC_DPRINTF1(3, "check_bpb: invalid spcl value %d",
13850576819eSwyllys 				bpb->spcl);
13860576819eSwyllys 
13870576819eSwyllys 		if ((secsize * bpb->spcl) >= (32 * 1024))
13880576819eSwyllys 			PC_DPRINTF3(3, "check_bpb: BPC > 32K  %d x %d = %d",
13890576819eSwyllys 				secsize,
13900576819eSwyllys 				bpb->spcl,
13910576819eSwyllys 				secsize * bpb->spcl);
13920576819eSwyllys 
13930576819eSwyllys 		if (bpb->nfat == 0)
13940576819eSwyllys 			PC_DPRINTF1(3, "check_bpb: bad NumFATs value %d",
13950576819eSwyllys 				bpb->nfat);
13960576819eSwyllys 
13970576819eSwyllys 		if (ltohs(bpb->res_sec[0]) == 0)
13980576819eSwyllys 			PC_DPRINTF1(3, "check_bpb: bad RsvdSecCnt value %d",
13990576819eSwyllys 				ltohs(bpb->res_sec[0]));
14000576819eSwyllys 
14010576819eSwyllys 		PC_DPRINTF1(5, "check_bpb: Media byte = %02x",
14020576819eSwyllys 			bpb->mediadesriptor);
14030576819eSwyllys 
14040576819eSwyllys 	}
14050576819eSwyllys 	if ((bpb->nfat == 0) ||
14060576819eSwyllys 		(bpb->mediadesriptor != 0xF0 && bpb->mediadesriptor < 0xF8) ||
14070576819eSwyllys 		(ltohs(cp[510]) != MBB_MAGIC) ||
14080576819eSwyllys 		!VALID_SECSIZE(secsize) ||
14090576819eSwyllys 		!VALID_SPCL(bpb->spcl) ||
1410264a6e74Sfrankho 		(secsize * bpb->spcl > (64 * 1024)) ||
14110576819eSwyllys 		!(ltohs(bpb->res_sec[0])))
14120576819eSwyllys 		return (0);
14130576819eSwyllys 
14140576819eSwyllys 	/*
14150576819eSwyllys 	 * Basic sanity checks passed so far, now try to determine which
14160576819eSwyllys 	 * FAT format to use.
14170576819eSwyllys 	 */
14180576819eSwyllys 	numclusters = bpb_to_numclusters(cp);
14190576819eSwyllys 
1420*9bd42341Sfrankho 	type = fattype(numclusters);
14210576819eSwyllys 
14220576819eSwyllys 	/* Do some final sanity checks for each specific type of FAT */
1423*9bd42341Sfrankho 	switch (type) {
14240576819eSwyllys 		case 0: /* FAT12 */
14250576819eSwyllys 		case PCFS_FAT16:
14260576819eSwyllys 			if (!check_bpb_fat16((struct bootsec *)cp))
14270576819eSwyllys 				return (0);
14280576819eSwyllys 			break;
14290576819eSwyllys 		case PCFS_FAT32:
14300576819eSwyllys 			if (!check_bpb_fat32((struct fat32_bootsec *)cp))
14310576819eSwyllys 				return (0);
14320576819eSwyllys 			break;
14330576819eSwyllys 		default: /* not sure yet */
1434*9bd42341Sfrankho 			type = secondaryBPBChecks(cp);
1435*9bd42341Sfrankho 			if (type == -1) {
14360576819eSwyllys 				/* Still nothing, give it up. */
14370576819eSwyllys 				return (0);
14380576819eSwyllys 			}
14390576819eSwyllys 			break;
14400576819eSwyllys 	}
1441*9bd42341Sfrankho 
1442*9bd42341Sfrankho 	if (fattypep)
1443*9bd42341Sfrankho 		*fattypep = type;
1444*9bd42341Sfrankho 
14450576819eSwyllys 	PC_DPRINTF0(5, "isBPB: BPB passes verification tests");
14460576819eSwyllys 	return (1);
14470576819eSwyllys }
14480576819eSwyllys 
14490576819eSwyllys 
14507c478bd9Sstevel@tonic-gate /*
14517c478bd9Sstevel@tonic-gate  * Get the FAT type for the DOS medium.
14527c478bd9Sstevel@tonic-gate  *
14530576819eSwyllys  * -------------------------
14540576819eSwyllys  * According to Microsoft:
14550576819eSwyllys  *   The FAT type one of FAT12, FAT16, or FAT32 is determined by the
14560576819eSwyllys  * count of clusters on the volume and nothing else.
14570576819eSwyllys  * -------------------------
14587c478bd9Sstevel@tonic-gate  *
14597c478bd9Sstevel@tonic-gate  */
14607c478bd9Sstevel@tonic-gate static int
14617c478bd9Sstevel@tonic-gate pc_getfattype(
14627c478bd9Sstevel@tonic-gate 	struct vnode *devvp,
14637c478bd9Sstevel@tonic-gate 	int ldrive,
14647c478bd9Sstevel@tonic-gate 	daddr_t *strtsectp,
14657c478bd9Sstevel@tonic-gate 	int *fattypep)
14667c478bd9Sstevel@tonic-gate {
14677c478bd9Sstevel@tonic-gate 	buf_t *bp = NULL;		/* Disk buffer pointer */
1468*9bd42341Sfrankho 	int err = 0;
14697c478bd9Sstevel@tonic-gate 	uchar_t sysid = 0;		/* System ID character */
14707c478bd9Sstevel@tonic-gate 	dev_t	dev = devvp->v_rdev;
14717c478bd9Sstevel@tonic-gate 
14727c478bd9Sstevel@tonic-gate 
14737c478bd9Sstevel@tonic-gate 	/*
14747c478bd9Sstevel@tonic-gate 	 * Open the device so we can check out the BPB or FDISK table,
14757c478bd9Sstevel@tonic-gate 	 * then read in the sector.
14767c478bd9Sstevel@tonic-gate 	 */
14777c478bd9Sstevel@tonic-gate 	PC_DPRINTF2(5, "pc_getfattype: dev=%x  ldrive=%x  ", (int)dev, ldrive);
1478*9bd42341Sfrankho 	if (err = VOP_OPEN(&devvp, FREAD, CRED())) {
1479*9bd42341Sfrankho 		PC_DPRINTF1(1, "pc_getfattype: open error=%d\n", err);
1480*9bd42341Sfrankho 		return (err);
14817c478bd9Sstevel@tonic-gate 	}
14827c478bd9Sstevel@tonic-gate 
14837c478bd9Sstevel@tonic-gate 	/*
1484*9bd42341Sfrankho 	 * Unpartitioned media (floppies and some removeable devices)
1485*9bd42341Sfrankho 	 * don't have a partition table, the FAT BPB is at disk block 0.
1486*9bd42341Sfrankho 	 * Start out by reading block 0.
14877c478bd9Sstevel@tonic-gate 	 */
1488*9bd42341Sfrankho 	*strtsectp = (daddr_t)0;
1489*9bd42341Sfrankho 	bp = bread(dev, *strtsectp, PC_SAFESECSIZE);
1490*9bd42341Sfrankho 
14917c478bd9Sstevel@tonic-gate 	if (bp->b_flags & B_ERROR) {
1492*9bd42341Sfrankho 		PC_DPRINTF2(1, "pc_getfattype: read error on "
1493*9bd42341Sfrankho 		    "device %d, disk LBA %d\n", (int)dev, (int)*strtsectp);
1494*9bd42341Sfrankho 		err = EIO;
14957c478bd9Sstevel@tonic-gate 		goto out;
14967c478bd9Sstevel@tonic-gate 	}
14977c478bd9Sstevel@tonic-gate 
14987c478bd9Sstevel@tonic-gate 	/*
1499*9bd42341Sfrankho 	 * If a logical drive number is requested, parse the partition table
1500*9bd42341Sfrankho 	 * and attempt to locate it. Otherwise, proceed immediately to the
1501*9bd42341Sfrankho 	 * BPB check. findTheDrive(), if successful, returns the disk block
1502*9bd42341Sfrankho 	 * number where the requested partition starts in "strtsecp".
15037c478bd9Sstevel@tonic-gate 	 */
1504*9bd42341Sfrankho 	if (ldrive != 0) {
15050576819eSwyllys 		PC_DPRINTF0(5, "pc_getfattype: using FDISK table to find BPB");
15067c478bd9Sstevel@tonic-gate 
1507*9bd42341Sfrankho 		if (err = findTheDrive(dev, ldrive, &bp, strtsectp, &sysid))
15080576819eSwyllys 			goto out;
15097c478bd9Sstevel@tonic-gate 
15107c478bd9Sstevel@tonic-gate 		brelse(bp);
15117c478bd9Sstevel@tonic-gate 		bp = bread(dev, *strtsectp, PC_SAFESECSIZE);
15127c478bd9Sstevel@tonic-gate 		if (bp->b_flags & B_ERROR) {
1513*9bd42341Sfrankho 			PC_DPRINTF2(1, "pc_getfattype: read error on "
1514*9bd42341Sfrankho 			    "device %d, disk LBA %d\n",
1515*9bd42341Sfrankho 			    (int)dev, (int)*strtsectp);
1516*9bd42341Sfrankho 			err = EIO;
15177c478bd9Sstevel@tonic-gate 			goto out;
15187c478bd9Sstevel@tonic-gate 		}
1519*9bd42341Sfrankho 	}
15207c478bd9Sstevel@tonic-gate 
1521*9bd42341Sfrankho 	if (!isBPB((uchar_t *)bp->b_un.b_addr, fattypep)) {
1522*9bd42341Sfrankho 		PC_DPRINTF2(1, "pc_getfattype: No FAT BPB on device %d, "
1523*9bd42341Sfrankho 		    "disk LBA %d\n", (int)dev, (int)*strtsectp);
1524*9bd42341Sfrankho 		err = EIO;
1525*9bd42341Sfrankho 		goto out;
15267c478bd9Sstevel@tonic-gate 	}
15277c478bd9Sstevel@tonic-gate 
15287c478bd9Sstevel@tonic-gate out:
15290576819eSwyllys 	/*
15300576819eSwyllys 	 * Release the buffer used
15310576819eSwyllys 	 */
15327c478bd9Sstevel@tonic-gate 	if (bp != NULL)
15337c478bd9Sstevel@tonic-gate 		brelse(bp);
15347c478bd9Sstevel@tonic-gate 	(void) VOP_CLOSE(devvp, FREAD, 1, (offset_t)0, CRED());
1535*9bd42341Sfrankho 	return (err);
15367c478bd9Sstevel@tonic-gate }
15377c478bd9Sstevel@tonic-gate 
15387c478bd9Sstevel@tonic-gate 
15397c478bd9Sstevel@tonic-gate /*
15407c478bd9Sstevel@tonic-gate  * Get the boot parameter block and file allocation table.
15417c478bd9Sstevel@tonic-gate  * If there is an old FAT, invalidate it.
15427c478bd9Sstevel@tonic-gate  */
15437c478bd9Sstevel@tonic-gate int
15447c478bd9Sstevel@tonic-gate pc_getfat(struct pcfs *fsp)
15457c478bd9Sstevel@tonic-gate {
15467c478bd9Sstevel@tonic-gate 	struct vfs *vfsp = PCFSTOVFS(fsp);
15477c478bd9Sstevel@tonic-gate 	struct buf *tp = 0;
15487c478bd9Sstevel@tonic-gate 	struct buf *bp = 0;
15497c478bd9Sstevel@tonic-gate 	uchar_t *fatp = NULL;
15507c478bd9Sstevel@tonic-gate 	uchar_t *fat_changemap = NULL;
15517c478bd9Sstevel@tonic-gate 	struct bootsec *bootp;
15527c478bd9Sstevel@tonic-gate 	struct fat32_bootsec *f32b;
15537c478bd9Sstevel@tonic-gate 	struct vnode *devvp;
15547c478bd9Sstevel@tonic-gate 	int error;
15557c478bd9Sstevel@tonic-gate 	int fatsize;
15567c478bd9Sstevel@tonic-gate 	int fat_changemapsize;
15577c478bd9Sstevel@tonic-gate 	int flags = 0;
15587c478bd9Sstevel@tonic-gate 	int nfat;
15597c478bd9Sstevel@tonic-gate 	int secsize;
15607c478bd9Sstevel@tonic-gate 	int fatsec;
15617c478bd9Sstevel@tonic-gate 
15627c478bd9Sstevel@tonic-gate 	PC_DPRINTF0(5, "pc_getfat\n");
15637c478bd9Sstevel@tonic-gate 	devvp = fsp->pcfs_devvp;
15647c478bd9Sstevel@tonic-gate 	if (fsp->pcfs_fatp) {
15657c478bd9Sstevel@tonic-gate 		/*
15667c478bd9Sstevel@tonic-gate 		 * There is a FAT in core.
15677c478bd9Sstevel@tonic-gate 		 * If there are open file pcnodes or we have modified it or
15687c478bd9Sstevel@tonic-gate 		 * it hasn't timed out yet use the in core FAT.
15697c478bd9Sstevel@tonic-gate 		 * Otherwise invalidate it and get a new one
15707c478bd9Sstevel@tonic-gate 		 */
15717c478bd9Sstevel@tonic-gate #ifdef notdef
15727c478bd9Sstevel@tonic-gate 		if (fsp->pcfs_frefs ||
15737c478bd9Sstevel@tonic-gate 		    (fsp->pcfs_flags & PCFS_FATMOD) ||
15747c478bd9Sstevel@tonic-gate 		    (gethrestime_sec() < fsp->pcfs_fattime)) {
15757c478bd9Sstevel@tonic-gate 			return (0);
15767c478bd9Sstevel@tonic-gate 		} else {
15777c478bd9Sstevel@tonic-gate 			mutex_enter(&pcfslock);
15787c478bd9Sstevel@tonic-gate 			pc_invalfat(fsp);
15797c478bd9Sstevel@tonic-gate 			mutex_exit(&pcfslock);
15807c478bd9Sstevel@tonic-gate 		}
15817c478bd9Sstevel@tonic-gate #endif /* notdef */
15827c478bd9Sstevel@tonic-gate 		return (0);
15837c478bd9Sstevel@tonic-gate 	}
15847c478bd9Sstevel@tonic-gate 	/*
15857c478bd9Sstevel@tonic-gate 	 * Open block device mounted on.
15867c478bd9Sstevel@tonic-gate 	 */
15877c478bd9Sstevel@tonic-gate 	error = VOP_OPEN(&devvp,
15887c478bd9Sstevel@tonic-gate 	    (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
15897c478bd9Sstevel@tonic-gate 	    CRED());
15907c478bd9Sstevel@tonic-gate 	if (error) {
15917c478bd9Sstevel@tonic-gate 		PC_DPRINTF1(1, "pc_getfat: open error=%d\n", error);
15927c478bd9Sstevel@tonic-gate 		return (error);
15937c478bd9Sstevel@tonic-gate 	}
15947c478bd9Sstevel@tonic-gate 	/*
15957c478bd9Sstevel@tonic-gate 	 * Get boot parameter block and check it for validity
15967c478bd9Sstevel@tonic-gate 	 */
15977c478bd9Sstevel@tonic-gate 	tp = bread(fsp->pcfs_xdev, fsp->pcfs_dosstart, PC_SAFESECSIZE);
1598*9bd42341Sfrankho 	if ((tp->b_flags & (B_ERROR | B_STALE)) ||
1599*9bd42341Sfrankho 	    !isBPB((uchar_t *)tp->b_un.b_addr, NULL)) {
1600*9bd42341Sfrankho 		PC_DPRINTF2(1, "pc_getfat: boot block error on device %d, "
1601*9bd42341Sfrankho 		    "disk LBA %d\n",
1602*9bd42341Sfrankho 		    (int)fsp->pcfs_xdev, (int)fsp->pcfs_dosstart);
16037c478bd9Sstevel@tonic-gate 		flags = tp->b_flags & B_ERROR;
16047c478bd9Sstevel@tonic-gate 		error = EIO;
16057c478bd9Sstevel@tonic-gate 		goto out;
16067c478bd9Sstevel@tonic-gate 	}
16077c478bd9Sstevel@tonic-gate 	tp->b_flags |= B_STALE | B_AGE;
16087c478bd9Sstevel@tonic-gate 	bootp = (struct bootsec *)tp->b_un.b_addr;
16097c478bd9Sstevel@tonic-gate 
16100576819eSwyllys 
16117c478bd9Sstevel@tonic-gate 	/* get the sector size - may be more than 512 bytes */
16127c478bd9Sstevel@tonic-gate 	secsize = (int)ltohs(bootp->bps[0]);
16137c478bd9Sstevel@tonic-gate 	/* check for bogus sector size - fat should be at least 1 sector */
16147c478bd9Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
16157c478bd9Sstevel@tonic-gate 		f32b = (struct fat32_bootsec *)bootp;
16167c478bd9Sstevel@tonic-gate 		fatsec = ltohi(f32b->f_fatlength);
16177c478bd9Sstevel@tonic-gate 	} else {
16187c478bd9Sstevel@tonic-gate 		fatsec = ltohs(bootp->fatsec);
16197c478bd9Sstevel@tonic-gate 	}
16207c478bd9Sstevel@tonic-gate 	if (secsize < 512 || fatsec < 1 || bootp->nfat < 1) {
16217c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: FAT size error");
16227c478bd9Sstevel@tonic-gate 		error = EINVAL;
16237c478bd9Sstevel@tonic-gate 		goto out;
16247c478bd9Sstevel@tonic-gate 	}
16257c478bd9Sstevel@tonic-gate 
16267c478bd9Sstevel@tonic-gate 	switch (bootp->mediadesriptor) {
16277c478bd9Sstevel@tonic-gate 	default:
16287c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: media-descriptor error, 0x%x",
16297c478bd9Sstevel@tonic-gate 		    bootp->mediadesriptor);
16307c478bd9Sstevel@tonic-gate 		error = EINVAL;
16317c478bd9Sstevel@tonic-gate 		goto out;
16327c478bd9Sstevel@tonic-gate 
16337c478bd9Sstevel@tonic-gate 	case MD_FIXED:
16347c478bd9Sstevel@tonic-gate 		/*
163590c30842Sjmcp 		 * PCMCIA pseudo floppy is type MD_FIXED,
16367c478bd9Sstevel@tonic-gate 		 * but is accessed like a floppy
16377c478bd9Sstevel@tonic-gate 		 */
16387c478bd9Sstevel@tonic-gate 		if (!(fsp->pcfs_flags & PCFS_PCMCIA_NO_CIS)) {
16397c478bd9Sstevel@tonic-gate 			fsp->pcfs_flags |= PCFS_NOCHK;
16407c478bd9Sstevel@tonic-gate 		}
16417c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
16427c478bd9Sstevel@tonic-gate 	case SS8SPT:
16437c478bd9Sstevel@tonic-gate 	case DS8SPT:
16447c478bd9Sstevel@tonic-gate 	case SS9SPT:
16457c478bd9Sstevel@tonic-gate 	case DS9SPT:
16467c478bd9Sstevel@tonic-gate 	case DS18SPT:
16477c478bd9Sstevel@tonic-gate 	case DS9_15SPT:
16487c478bd9Sstevel@tonic-gate 		fsp->pcfs_secsize = secsize;
16497c478bd9Sstevel@tonic-gate 		fsp->pcfs_sdshift = secsize / DEV_BSIZE - 1;
16507c478bd9Sstevel@tonic-gate 		fsp->pcfs_entps = secsize / sizeof (struct pcdir);
16517c478bd9Sstevel@tonic-gate 		fsp->pcfs_spcl = (int)bootp->spcl;
16527c478bd9Sstevel@tonic-gate 		fsp->pcfs_fatsec = fatsec;
16537c478bd9Sstevel@tonic-gate 		fsp->pcfs_spt = (int)ltohs(bootp->spt);
1654264a6e74Sfrankho 		fsp->pcfs_rdirsec = ((int)ltohs(bootp->rdirents[0])
1655264a6e74Sfrankho 		    * sizeof (struct pcdir) + (secsize - 1)) / secsize;
16567c478bd9Sstevel@tonic-gate 		fsp->pcfs_clsize = fsp->pcfs_spcl * secsize;
16577c478bd9Sstevel@tonic-gate 		fsp->pcfs_fatstart = fsp->pcfs_dosstart +
16587c478bd9Sstevel@tonic-gate 		    (daddr_t)ltohs(bootp->res_sec[0]);
16597c478bd9Sstevel@tonic-gate 		fsp->pcfs_rdirstart = fsp->pcfs_fatstart +
16607c478bd9Sstevel@tonic-gate 		    (bootp->nfat * fsp->pcfs_fatsec);
16617c478bd9Sstevel@tonic-gate 		fsp->pcfs_datastart = fsp->pcfs_rdirstart + fsp->pcfs_rdirsec;
16627c478bd9Sstevel@tonic-gate 		if (IS_FAT32(fsp))
16637c478bd9Sstevel@tonic-gate 			fsp->pcfs_rdirstart = ltohi(f32b->f_rootcluster);
16647c478bd9Sstevel@tonic-gate 		fsp->pcfs_ncluster = (((int)(ltohs(bootp->numsect[0]) ?
16657c478bd9Sstevel@tonic-gate 		    ltohs(bootp->numsect[0]) : ltohi(bootp->totalsec))) -
16667c478bd9Sstevel@tonic-gate 		    fsp->pcfs_datastart + fsp->pcfs_dosstart) / fsp->pcfs_spcl;
16677c478bd9Sstevel@tonic-gate 		fsp->pcfs_numfat = (int)bootp->nfat;
16687c478bd9Sstevel@tonic-gate 		fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
16697c478bd9Sstevel@tonic-gate 		break;
16707c478bd9Sstevel@tonic-gate 	}
16717c478bd9Sstevel@tonic-gate 
16727c478bd9Sstevel@tonic-gate 	/*
16737c478bd9Sstevel@tonic-gate 	 * Get FAT and check it for validity
16747c478bd9Sstevel@tonic-gate 	 */
16757c478bd9Sstevel@tonic-gate 	fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
16767c478bd9Sstevel@tonic-gate 	fatp = kmem_alloc(fatsize, KM_SLEEP);
16777c478bd9Sstevel@tonic-gate 	error = pc_readfat(fsp, fatp, fsp->pcfs_fatstart, fatsize);
16787c478bd9Sstevel@tonic-gate 	if (error) {
16797c478bd9Sstevel@tonic-gate 		flags = B_ERROR;
16807c478bd9Sstevel@tonic-gate 		goto out;
16817c478bd9Sstevel@tonic-gate 	}
16827c478bd9Sstevel@tonic-gate 	fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1;
16837c478bd9Sstevel@tonic-gate 	fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP);
16847c478bd9Sstevel@tonic-gate 
16850576819eSwyllys 	/*
16860576819eSwyllys 	 * The only definite signature check is that the
16870576819eSwyllys 	 * media descriptor byte should match the first byte
16880576819eSwyllys 	 * of the FAT block.
16890576819eSwyllys 	 */
16900576819eSwyllys 	if (fatp[0] != bootp->mediadesriptor) {
16917c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: FAT signature error");
16927c478bd9Sstevel@tonic-gate 		error = EINVAL;
16937c478bd9Sstevel@tonic-gate 		goto out;
16947c478bd9Sstevel@tonic-gate 	}
16957c478bd9Sstevel@tonic-gate 	/*
16967c478bd9Sstevel@tonic-gate 	 * Checking for fatsec and number of supported clusters, should
16970576819eSwyllys 	 * actually determine a FAT12/FAT media.
16987c478bd9Sstevel@tonic-gate 	 */
16997c478bd9Sstevel@tonic-gate 	if (fsp->pcfs_flags & PCFS_FAT16) {
17007c478bd9Sstevel@tonic-gate 		if ((fsp->pcfs_fatsec <= 12) &&
17017c478bd9Sstevel@tonic-gate 		    ((fatsize * 2 / 3) >= fsp->pcfs_ncluster)) {
17027c478bd9Sstevel@tonic-gate 			/*
17037c478bd9Sstevel@tonic-gate 			 * We have a 12-bit FAT, rather than a 16-bit FAT.
17047c478bd9Sstevel@tonic-gate 			 * Ignore what the fdisk table says.
17057c478bd9Sstevel@tonic-gate 			 */
17067c478bd9Sstevel@tonic-gate 			PC_DPRINTF0(2, "pc_getfattype: forcing 12-bit FAT\n");
17077c478bd9Sstevel@tonic-gate 			fsp->pcfs_flags &= ~PCFS_FAT16;
17087c478bd9Sstevel@tonic-gate 		}
17097c478bd9Sstevel@tonic-gate 	}
17107c478bd9Sstevel@tonic-gate 	/*
17117c478bd9Sstevel@tonic-gate 	 * Sanity check our FAT is large enough for the
17127c478bd9Sstevel@tonic-gate 	 * clusters we think we have.
17137c478bd9Sstevel@tonic-gate 	 */
17147c478bd9Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_FAT16) &&
17157c478bd9Sstevel@tonic-gate 	    ((fatsize / 2) < fsp->pcfs_ncluster)) {
17167c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: FAT too small for number of clusters");
17177c478bd9Sstevel@tonic-gate 		error = EINVAL;
17187c478bd9Sstevel@tonic-gate 		goto out;
17197c478bd9Sstevel@tonic-gate 	}
17207c478bd9Sstevel@tonic-gate 
17217c478bd9Sstevel@tonic-gate 	/*
17227c478bd9Sstevel@tonic-gate 	 * Get alternate FATs and check for consistency
17237c478bd9Sstevel@tonic-gate 	 * This is an inlined version of pc_readfat().
17247c478bd9Sstevel@tonic-gate 	 * Since we're only comparing FAT and alternate FAT,
17257c478bd9Sstevel@tonic-gate 	 * there's no reason to let pc_readfat() copy data out
17267c478bd9Sstevel@tonic-gate 	 * of the buf. Instead, compare in-situ, one cluster
17277c478bd9Sstevel@tonic-gate 	 * at a time.
17287c478bd9Sstevel@tonic-gate 	 */
17297c478bd9Sstevel@tonic-gate 	for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) {
17307c478bd9Sstevel@tonic-gate 		size_t startsec;
17317c478bd9Sstevel@tonic-gate 		size_t off;
17327c478bd9Sstevel@tonic-gate 
17337c478bd9Sstevel@tonic-gate 		startsec = fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec;
17347c478bd9Sstevel@tonic-gate 
17357c478bd9Sstevel@tonic-gate 		for (off = 0; off < fatsize; off += fsp->pcfs_clsize) {
17367c478bd9Sstevel@tonic-gate 			bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp,
17377c478bd9Sstevel@tonic-gate 				startsec +
17387c478bd9Sstevel@tonic-gate 				pc_cltodb(fsp, pc_lblkno(fsp, off))),
17397c478bd9Sstevel@tonic-gate 				MIN(fsp->pcfs_clsize, fatsize - off));
17407c478bd9Sstevel@tonic-gate 			if (bp->b_flags & (B_ERROR | B_STALE)) {
17417c478bd9Sstevel@tonic-gate 				cmn_err(CE_NOTE,
17427c478bd9Sstevel@tonic-gate 					"!pcfs: alternate FAT #%d read error"
17437c478bd9Sstevel@tonic-gate 					" at byte %ld", nfat, off);
17447c478bd9Sstevel@tonic-gate 				flags = B_ERROR;
17457c478bd9Sstevel@tonic-gate 				error = EIO;
17467c478bd9Sstevel@tonic-gate 				goto out;
17477c478bd9Sstevel@tonic-gate 			}
17487c478bd9Sstevel@tonic-gate 			bp->b_flags |= B_STALE | B_AGE;
17497c478bd9Sstevel@tonic-gate 			if (bcmp(bp->b_un.b_addr,
17507c478bd9Sstevel@tonic-gate 			    fatp + off,
17517c478bd9Sstevel@tonic-gate 			    MIN(fsp->pcfs_clsize, fatsize - off))) {
17527c478bd9Sstevel@tonic-gate 				cmn_err(CE_NOTE,
17537c478bd9Sstevel@tonic-gate 					"!pcfs: alternate FAT #%d corrupted"
17547c478bd9Sstevel@tonic-gate 					" at byte %ld", nfat, off);
17557c478bd9Sstevel@tonic-gate 				flags = B_ERROR;
17567c478bd9Sstevel@tonic-gate 			}
17577c478bd9Sstevel@tonic-gate 			brelse(bp);
17587c478bd9Sstevel@tonic-gate 			bp = NULL;	/* prevent double release */
17597c478bd9Sstevel@tonic-gate 		}
17607c478bd9Sstevel@tonic-gate 	}
17617c478bd9Sstevel@tonic-gate 
17627c478bd9Sstevel@tonic-gate 	fsp->pcfs_fatsize = fatsize;
17637c478bd9Sstevel@tonic-gate 	fsp->pcfs_fatp = fatp;
17647c478bd9Sstevel@tonic-gate 	fsp->pcfs_fat_changemapsize = fat_changemapsize;
17657c478bd9Sstevel@tonic-gate 	fsp->pcfs_fat_changemap = fat_changemap;
17667c478bd9Sstevel@tonic-gate 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
17677c478bd9Sstevel@tonic-gate 	fsp->pcfs_fatjustread = 1;
17687c478bd9Sstevel@tonic-gate 
17697c478bd9Sstevel@tonic-gate 	brelse(tp);
17707c478bd9Sstevel@tonic-gate 	tp = NULL;
17717c478bd9Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
17727c478bd9Sstevel@tonic-gate 		/* get fsinfo */
17737c478bd9Sstevel@tonic-gate 		struct fat32_boot_fsinfo fsinfo_disk;
17747c478bd9Sstevel@tonic-gate 
17757c478bd9Sstevel@tonic-gate 		fsp->f32fsinfo_sector = ltohs(f32b->f_infosector);
17767c478bd9Sstevel@tonic-gate 		tp = bread(fsp->pcfs_xdev,
17777c478bd9Sstevel@tonic-gate 		    fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector),
17787c478bd9Sstevel@tonic-gate 		    PC_SAFESECSIZE);
17797c478bd9Sstevel@tonic-gate 		if (tp->b_flags & (B_ERROR | B_STALE)) {
17807c478bd9Sstevel@tonic-gate 			cmn_err(CE_NOTE, "!pcfs: error reading fat32 fsinfo");
17817c478bd9Sstevel@tonic-gate 			flags = tp->b_flags & B_ERROR;
17827c478bd9Sstevel@tonic-gate 			brelse(tp);
17837c478bd9Sstevel@tonic-gate 			tp = NULL;
17847c478bd9Sstevel@tonic-gate 			error = EIO;
17857c478bd9Sstevel@tonic-gate 			goto out;
17867c478bd9Sstevel@tonic-gate 		}
17877c478bd9Sstevel@tonic-gate 		tp->b_flags |= B_STALE | B_AGE;
17887c478bd9Sstevel@tonic-gate 		bcopy((void *)(tp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
17897c478bd9Sstevel@tonic-gate 		    &fsinfo_disk, sizeof (struct fat32_boot_fsinfo));
17907c478bd9Sstevel@tonic-gate 		brelse(tp);
17917c478bd9Sstevel@tonic-gate 		tp = NULL;
17927c478bd9Sstevel@tonic-gate 
17937c478bd9Sstevel@tonic-gate 		/* translated fields */
17947c478bd9Sstevel@tonic-gate 		fsp->fsinfo_native.fs_signature =
17957c478bd9Sstevel@tonic-gate 		    ltohi(fsinfo_disk.fs_signature);
17967c478bd9Sstevel@tonic-gate 		fsp->fsinfo_native.fs_free_clusters =
17977c478bd9Sstevel@tonic-gate 		    ltohi(fsinfo_disk.fs_free_clusters);
17987c478bd9Sstevel@tonic-gate 		if (fsp->fsinfo_native.fs_signature != FAT32_FS_SIGN) {
17997c478bd9Sstevel@tonic-gate 			cmn_err(CE_NOTE,
18007c478bd9Sstevel@tonic-gate 			    "!pcfs: fat32 fsinfo signature mismatch.");
18017c478bd9Sstevel@tonic-gate 			error = EINVAL;
18027c478bd9Sstevel@tonic-gate 			goto out;
18037c478bd9Sstevel@tonic-gate 		}
18047c478bd9Sstevel@tonic-gate 	}
18057c478bd9Sstevel@tonic-gate 
18067c478bd9Sstevel@tonic-gate 	return (0);
18077c478bd9Sstevel@tonic-gate 
18087c478bd9Sstevel@tonic-gate out:
18097c478bd9Sstevel@tonic-gate 	cmn_err(CE_NOTE, "!pcfs: illegal disk format");
18107c478bd9Sstevel@tonic-gate 	if (tp)
18117c478bd9Sstevel@tonic-gate 		brelse(tp);
18127c478bd9Sstevel@tonic-gate 	if (bp)
18137c478bd9Sstevel@tonic-gate 		brelse(bp);
18147c478bd9Sstevel@tonic-gate 	if (fatp)
18157c478bd9Sstevel@tonic-gate 		kmem_free(fatp, fatsize);
18167c478bd9Sstevel@tonic-gate 	if (fat_changemap)
18177c478bd9Sstevel@tonic-gate 		kmem_free(fat_changemap, fat_changemapsize);
18187c478bd9Sstevel@tonic-gate 
18197c478bd9Sstevel@tonic-gate 	if (flags) {
18207c478bd9Sstevel@tonic-gate 		pc_mark_irrecov(fsp);
18217c478bd9Sstevel@tonic-gate 	}
18227c478bd9Sstevel@tonic-gate 	(void) VOP_CLOSE(devvp, (vfsp->vfs_flag & VFS_RDONLY) ?
18237c478bd9Sstevel@tonic-gate 	    FREAD : FREAD|FWRITE, 1, (offset_t)0, CRED());
18247c478bd9Sstevel@tonic-gate 	return (error);
18257c478bd9Sstevel@tonic-gate }
18267c478bd9Sstevel@tonic-gate 
18277c478bd9Sstevel@tonic-gate int
18287c478bd9Sstevel@tonic-gate pc_syncfat(struct pcfs *fsp)
18297c478bd9Sstevel@tonic-gate {
18307c478bd9Sstevel@tonic-gate 	struct buf *bp;
18317c478bd9Sstevel@tonic-gate 	int nfat;
18327c478bd9Sstevel@tonic-gate 	int	error;
18337c478bd9Sstevel@tonic-gate 	struct fat32_boot_fsinfo fsinfo_disk;
18347c478bd9Sstevel@tonic-gate 
18357c478bd9Sstevel@tonic-gate 	PC_DPRINTF0(7, "pcfs_syncfat\n");
18367c478bd9Sstevel@tonic-gate 	if ((fsp->pcfs_fatp == (uchar_t *)0) ||
18377c478bd9Sstevel@tonic-gate 	    !(fsp->pcfs_flags & PCFS_FATMOD))
18387c478bd9Sstevel@tonic-gate 		return (0);
18397c478bd9Sstevel@tonic-gate 	/*
18407c478bd9Sstevel@tonic-gate 	 * write out all copies of FATs
18417c478bd9Sstevel@tonic-gate 	 */
18427c478bd9Sstevel@tonic-gate 	fsp->pcfs_flags &= ~PCFS_FATMOD;
18437c478bd9Sstevel@tonic-gate 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
18447c478bd9Sstevel@tonic-gate 	for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) {
18457c478bd9Sstevel@tonic-gate 		error = pc_writefat(fsp,
18467c478bd9Sstevel@tonic-gate 		    fsp->pcfs_fatstart + nfat*fsp->pcfs_fatsec);
18477c478bd9Sstevel@tonic-gate 		if (error) {
18487c478bd9Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
18497c478bd9Sstevel@tonic-gate 			return (EIO);
18507c478bd9Sstevel@tonic-gate 		}
18517c478bd9Sstevel@tonic-gate 	}
18527c478bd9Sstevel@tonic-gate 	pc_clear_fatchanges(fsp);
18537c478bd9Sstevel@tonic-gate 	PC_DPRINTF0(6, "pcfs_syncfat: wrote out FAT\n");
18547c478bd9Sstevel@tonic-gate 	/* write out fsinfo */
18557c478bd9Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
18567c478bd9Sstevel@tonic-gate 		bp = bread(fsp->pcfs_xdev,
18577c478bd9Sstevel@tonic-gate 		    fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector),
18587c478bd9Sstevel@tonic-gate 		    PC_SAFESECSIZE);
18597c478bd9Sstevel@tonic-gate 		if (bp->b_flags & (B_ERROR | B_STALE)) {
18607c478bd9Sstevel@tonic-gate 			brelse(bp);
18617c478bd9Sstevel@tonic-gate 			return (EIO);
18627c478bd9Sstevel@tonic-gate 		}
18637c478bd9Sstevel@tonic-gate 		bcopy((void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
18647c478bd9Sstevel@tonic-gate 		    &fsinfo_disk, sizeof (struct fat32_boot_fsinfo));
18657c478bd9Sstevel@tonic-gate 		/* translate fields */
18667c478bd9Sstevel@tonic-gate 		fsinfo_disk.fs_free_clusters =
18677c478bd9Sstevel@tonic-gate 		    htoli(fsp->fsinfo_native.fs_free_clusters);
18687c478bd9Sstevel@tonic-gate 		fsinfo_disk.fs_next_cluster = (uint32_t)FSINFO_UNKNOWN;
18697c478bd9Sstevel@tonic-gate 		bcopy(&fsinfo_disk,
18707c478bd9Sstevel@tonic-gate 		    (void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
18717c478bd9Sstevel@tonic-gate 		    sizeof (struct fat32_boot_fsinfo));
18727c478bd9Sstevel@tonic-gate 		bwrite2(bp);
18737c478bd9Sstevel@tonic-gate 		error = geterror(bp);
18747c478bd9Sstevel@tonic-gate 		brelse(bp);
18757c478bd9Sstevel@tonic-gate 		if (error) {
18767c478bd9Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
18777c478bd9Sstevel@tonic-gate 			return (EIO);
18787c478bd9Sstevel@tonic-gate 		}
18797c478bd9Sstevel@tonic-gate 	}
18807c478bd9Sstevel@tonic-gate 	return (0);
18817c478bd9Sstevel@tonic-gate }
18827c478bd9Sstevel@tonic-gate 
18837c478bd9Sstevel@tonic-gate void
18847c478bd9Sstevel@tonic-gate pc_invalfat(struct pcfs *fsp)
18857c478bd9Sstevel@tonic-gate {
18867c478bd9Sstevel@tonic-gate 	struct pcfs *xfsp;
18877c478bd9Sstevel@tonic-gate 	int mount_cnt = 0;
18887c478bd9Sstevel@tonic-gate 
18897c478bd9Sstevel@tonic-gate 	PC_DPRINTF0(7, "pc_invalfat\n");
18907c478bd9Sstevel@tonic-gate 	if (fsp->pcfs_fatp == (uchar_t *)0)
18917c478bd9Sstevel@tonic-gate 		panic("pc_invalfat");
18927c478bd9Sstevel@tonic-gate 	/*
18937c478bd9Sstevel@tonic-gate 	 * Release FAT
18947c478bd9Sstevel@tonic-gate 	 */
18957c478bd9Sstevel@tonic-gate 	kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsize);
18967c478bd9Sstevel@tonic-gate 	fsp->pcfs_fatp = NULL;
18977c478bd9Sstevel@tonic-gate 	kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize);
18987c478bd9Sstevel@tonic-gate 	fsp->pcfs_fat_changemap = NULL;
18997c478bd9Sstevel@tonic-gate 	/*
19007c478bd9Sstevel@tonic-gate 	 * Invalidate all the blocks associated with the device.
19017c478bd9Sstevel@tonic-gate 	 * Not needed if stateless.
19027c478bd9Sstevel@tonic-gate 	 */
19037c478bd9Sstevel@tonic-gate 	for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt)
19047c478bd9Sstevel@tonic-gate 		if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev)
19057c478bd9Sstevel@tonic-gate 			mount_cnt++;
19067c478bd9Sstevel@tonic-gate 
19077c478bd9Sstevel@tonic-gate 	if (!mount_cnt)
19087c478bd9Sstevel@tonic-gate 		binval(fsp->pcfs_xdev);
19097c478bd9Sstevel@tonic-gate 	/*
19107c478bd9Sstevel@tonic-gate 	 * close mounted device
19117c478bd9Sstevel@tonic-gate 	 */
19127c478bd9Sstevel@tonic-gate 	(void) VOP_CLOSE(fsp->pcfs_devvp,
19137c478bd9Sstevel@tonic-gate 	    (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
19147c478bd9Sstevel@tonic-gate 	    1, (offset_t)0, CRED());
19157c478bd9Sstevel@tonic-gate }
19167c478bd9Sstevel@tonic-gate 
19177c478bd9Sstevel@tonic-gate void
19187c478bd9Sstevel@tonic-gate pc_badfs(struct pcfs *fsp)
19197c478bd9Sstevel@tonic-gate {
19207c478bd9Sstevel@tonic-gate 	cmn_err(CE_WARN, "corrupted PC file system on dev %x.%x\n",
19217c478bd9Sstevel@tonic-gate 	    getmajor(fsp->pcfs_devvp->v_rdev),
19227c478bd9Sstevel@tonic-gate 	    getminor(fsp->pcfs_devvp->v_rdev));
19237c478bd9Sstevel@tonic-gate }
19247c478bd9Sstevel@tonic-gate 
19257c478bd9Sstevel@tonic-gate /*
19267c478bd9Sstevel@tonic-gate  * The problem with supporting NFS on the PCFS filesystem is that there
19277c478bd9Sstevel@tonic-gate  * is no good place to keep the generation number. The only possible
19287c478bd9Sstevel@tonic-gate  * place is inside a directory entry. There are a few words that we
19297c478bd9Sstevel@tonic-gate  * don't use - they store NT & OS/2 attributes, and the creation/last access
19307c478bd9Sstevel@tonic-gate  * time of the file - but it seems wrong to use them. In addition, directory
19317c478bd9Sstevel@tonic-gate  * entries come and go. If a directory is removed completely, its directory
19327c478bd9Sstevel@tonic-gate  * blocks are freed and the generation numbers are lost. Whereas in ufs,
19337c478bd9Sstevel@tonic-gate  * inode blocks are dedicated for inodes, so the generation numbers are
19347c478bd9Sstevel@tonic-gate  * permanently kept on the disk.
19357c478bd9Sstevel@tonic-gate  */
19367c478bd9Sstevel@tonic-gate static int
19377c478bd9Sstevel@tonic-gate pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp)
19387c478bd9Sstevel@tonic-gate {
19397c478bd9Sstevel@tonic-gate 	struct pcnode *pcp;
19407c478bd9Sstevel@tonic-gate 	struct pc_fid *pcfid;
19417c478bd9Sstevel@tonic-gate 	struct pcfs *fsp;
19427c478bd9Sstevel@tonic-gate 	struct pcdir *ep;
19437c478bd9Sstevel@tonic-gate 	daddr_t eblkno;
19447c478bd9Sstevel@tonic-gate 	int eoffset;
19457c478bd9Sstevel@tonic-gate 	struct buf *bp;
19467c478bd9Sstevel@tonic-gate 	int error;
19477c478bd9Sstevel@tonic-gate 	pc_cluster32_t	cn;
19487c478bd9Sstevel@tonic-gate 
19497c478bd9Sstevel@tonic-gate 	pcfid = (struct pc_fid *)fidp;
19507c478bd9Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
19517c478bd9Sstevel@tonic-gate 
19527c478bd9Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
19537c478bd9Sstevel@tonic-gate 	if (error) {
19547c478bd9Sstevel@tonic-gate 		*vpp = NULL;
19557c478bd9Sstevel@tonic-gate 		return (error);
19567c478bd9Sstevel@tonic-gate 	}
19577c478bd9Sstevel@tonic-gate 
19587c478bd9Sstevel@tonic-gate 	if (pcfid->pcfid_block == 0) {
19597c478bd9Sstevel@tonic-gate 		pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
19607c478bd9Sstevel@tonic-gate 		pcp->pc_flags |= PC_EXTERNAL;
19617c478bd9Sstevel@tonic-gate 		*vpp = PCTOV(pcp);
19627c478bd9Sstevel@tonic-gate 		pc_unlockfs(fsp);
19637c478bd9Sstevel@tonic-gate 		return (0);
19647c478bd9Sstevel@tonic-gate 	}
19657c478bd9Sstevel@tonic-gate 	eblkno = pcfid->pcfid_block;
19667c478bd9Sstevel@tonic-gate 	eoffset = pcfid->pcfid_offset;
19677c478bd9Sstevel@tonic-gate 	if ((pc_dbtocl(fsp,
19687c478bd9Sstevel@tonic-gate 	    eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) ||
19697c478bd9Sstevel@tonic-gate 	    (eoffset > fsp->pcfs_clsize)) {
19707c478bd9Sstevel@tonic-gate 		pc_unlockfs(fsp);
19717c478bd9Sstevel@tonic-gate 		*vpp = NULL;
19727c478bd9Sstevel@tonic-gate 		return (EINVAL);
19737c478bd9Sstevel@tonic-gate 	}
19747c478bd9Sstevel@tonic-gate 
19757c478bd9Sstevel@tonic-gate 	if (eblkno >= fsp->pcfs_datastart || (eblkno-fsp->pcfs_rdirstart)
19767c478bd9Sstevel@tonic-gate 	    < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
19777c478bd9Sstevel@tonic-gate 		bp = bread(fsp->pcfs_xdev, eblkno, fsp->pcfs_clsize);
19787c478bd9Sstevel@tonic-gate 	} else {
19797c478bd9Sstevel@tonic-gate 		bp = bread(fsp->pcfs_xdev, eblkno,
19807c478bd9Sstevel@tonic-gate 		    (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize);
19817c478bd9Sstevel@tonic-gate 	}
19827c478bd9Sstevel@tonic-gate 	if (bp->b_flags & (B_ERROR | B_STALE)) {
19837c478bd9Sstevel@tonic-gate 		error = geterror(bp);
19847c478bd9Sstevel@tonic-gate 		brelse(bp);
19857c478bd9Sstevel@tonic-gate 		if (error)
19867c478bd9Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
19877c478bd9Sstevel@tonic-gate 		*vpp = NULL;
19887c478bd9Sstevel@tonic-gate 		pc_unlockfs(fsp);
19897c478bd9Sstevel@tonic-gate 		return (error);
19907c478bd9Sstevel@tonic-gate 	}
19917c478bd9Sstevel@tonic-gate 	ep = (struct pcdir *)(bp->b_un.b_addr + eoffset);
19927c478bd9Sstevel@tonic-gate 	/*
19937c478bd9Sstevel@tonic-gate 	 * Ok, if this is a valid file handle that we gave out,
19947c478bd9Sstevel@tonic-gate 	 * then simply ensuring that the creation time matches,
19957c478bd9Sstevel@tonic-gate 	 * the entry has not been deleted, and it has a valid first
19967c478bd9Sstevel@tonic-gate 	 * character should be enough.
19977c478bd9Sstevel@tonic-gate 	 *
19987c478bd9Sstevel@tonic-gate 	 * Unfortunately, verifying that the <blkno, offset> _still_
19997c478bd9Sstevel@tonic-gate 	 * refers to a directory entry is not easy, since we'd have
20007c478bd9Sstevel@tonic-gate 	 * to search _all_ directories starting from root to find it.
20017c478bd9Sstevel@tonic-gate 	 * That's a high price to pay just in case somebody is forging
20027c478bd9Sstevel@tonic-gate 	 * file handles. So instead we verify that as much of the
20037c478bd9Sstevel@tonic-gate 	 * entry is valid as we can:
20047c478bd9Sstevel@tonic-gate 	 *
20057c478bd9Sstevel@tonic-gate 	 * 1. The starting cluster is 0 (unallocated) or valid
20067c478bd9Sstevel@tonic-gate 	 * 2. It is not an LFN entry
20077c478bd9Sstevel@tonic-gate 	 * 3. It is not hidden (unless mounted as such)
20087c478bd9Sstevel@tonic-gate 	 * 4. It is not the label
20097c478bd9Sstevel@tonic-gate 	 */
20107c478bd9Sstevel@tonic-gate 	cn = pc_getstartcluster(fsp, ep);
20117c478bd9Sstevel@tonic-gate 	/*
20127c478bd9Sstevel@tonic-gate 	 * if the starting cluster is valid, but not valid according
20137c478bd9Sstevel@tonic-gate 	 * to pc_validcl(), force it to be to simplify the following if.
20147c478bd9Sstevel@tonic-gate 	 */
20157c478bd9Sstevel@tonic-gate 	if (cn == 0)
20167c478bd9Sstevel@tonic-gate 		cn = PCF_FIRSTCLUSTER;
20177c478bd9Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
20187c478bd9Sstevel@tonic-gate 		if (cn >= PCF_LASTCLUSTER32)
20197c478bd9Sstevel@tonic-gate 			cn = PCF_FIRSTCLUSTER;
20207c478bd9Sstevel@tonic-gate 	} else {
20217c478bd9Sstevel@tonic-gate 		if (cn >= PCF_LASTCLUSTER)
20227c478bd9Sstevel@tonic-gate 			cn = PCF_FIRSTCLUSTER;
20237c478bd9Sstevel@tonic-gate 	}
20247c478bd9Sstevel@tonic-gate 	if ((!pc_validcl(fsp, cn)) ||
20257c478bd9Sstevel@tonic-gate 	    (PCDL_IS_LFN(ep)) ||
20267c478bd9Sstevel@tonic-gate 	    (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) ||
20277c478bd9Sstevel@tonic-gate 	    ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) {
20287c478bd9Sstevel@tonic-gate 		bp->b_flags |= B_STALE | B_AGE;
20297c478bd9Sstevel@tonic-gate 		brelse(bp);
20307c478bd9Sstevel@tonic-gate 		pc_unlockfs(fsp);
20317c478bd9Sstevel@tonic-gate 		return (EINVAL);
20327c478bd9Sstevel@tonic-gate 	}
20337c478bd9Sstevel@tonic-gate 	if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) &&
20347c478bd9Sstevel@tonic-gate 	    (ep->pcd_filename[0] != PCD_ERASED) &&
20357c478bd9Sstevel@tonic-gate 	    (pc_validchar(ep->pcd_filename[0]) ||
20367c478bd9Sstevel@tonic-gate 		(ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) {
20377c478bd9Sstevel@tonic-gate 		pcp = pc_getnode(fsp, eblkno, eoffset, ep);
20387c478bd9Sstevel@tonic-gate 		pcp->pc_flags |= PC_EXTERNAL;
20397c478bd9Sstevel@tonic-gate 		*vpp = PCTOV(pcp);
20407c478bd9Sstevel@tonic-gate 	} else {
20417c478bd9Sstevel@tonic-gate 		*vpp = NULL;
20427c478bd9Sstevel@tonic-gate 	}
20437c478bd9Sstevel@tonic-gate 	bp->b_flags |= B_STALE | B_AGE;
20447c478bd9Sstevel@tonic-gate 	brelse(bp);
20457c478bd9Sstevel@tonic-gate 	pc_unlockfs(fsp);
20467c478bd9Sstevel@tonic-gate 	return (0);
20477c478bd9Sstevel@tonic-gate }
20487c478bd9Sstevel@tonic-gate 
20497c478bd9Sstevel@tonic-gate /*
205090c30842Sjmcp  * if device is a PCMCIA pseudo floppy, return 1
20517c478bd9Sstevel@tonic-gate  * otherwise, return 0
20527c478bd9Sstevel@tonic-gate  */
20537c478bd9Sstevel@tonic-gate static int
205490c30842Sjmcp pcfs_pseudo_floppy(dev_t rdev)
20557c478bd9Sstevel@tonic-gate {
20567c478bd9Sstevel@tonic-gate 	int			error, err;
20577c478bd9Sstevel@tonic-gate 	struct dk_cinfo		info;
20587c478bd9Sstevel@tonic-gate 	ldi_handle_t		lh;
20597c478bd9Sstevel@tonic-gate 	ldi_ident_t		li;
20607c478bd9Sstevel@tonic-gate 
20617c478bd9Sstevel@tonic-gate 	err = ldi_ident_from_mod(&modlinkage, &li);
20627c478bd9Sstevel@tonic-gate 	if (err) {
20637c478bd9Sstevel@tonic-gate 		PC_DPRINTF1(1,
206490c30842Sjmcp 		    "pcfs_pseudo_floppy: ldi_ident_from_mod err=%d\n", err);
20657c478bd9Sstevel@tonic-gate 		return (0);
20667c478bd9Sstevel@tonic-gate 	}
20677c478bd9Sstevel@tonic-gate 
20687c478bd9Sstevel@tonic-gate 	err = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, CRED(), &lh, li);
20697c478bd9Sstevel@tonic-gate 	ldi_ident_release(li);
20707c478bd9Sstevel@tonic-gate 	if (err) {
20717c478bd9Sstevel@tonic-gate 		PC_DPRINTF1(1,
207290c30842Sjmcp 		    "pcfs_pseudo_floppy: ldi_open err=%d\n", err);
20737c478bd9Sstevel@tonic-gate 		return (0);
20747c478bd9Sstevel@tonic-gate 	}
20757c478bd9Sstevel@tonic-gate 
20767c478bd9Sstevel@tonic-gate 	/* return value stored in err is purposfully ignored */
20777c478bd9Sstevel@tonic-gate 	error = ldi_ioctl(lh, DKIOCINFO, (intptr_t)&info, FKIOCTL,
20787c478bd9Sstevel@tonic-gate 	    CRED(), &err);
20797c478bd9Sstevel@tonic-gate 
20807c478bd9Sstevel@tonic-gate 	err = ldi_close(lh, FREAD, CRED());
20817c478bd9Sstevel@tonic-gate 	if (err != 0) {
20827c478bd9Sstevel@tonic-gate 		PC_DPRINTF1(1,
208390c30842Sjmcp 		    "pcfs_pseudo_floppy: ldi_close err=%d\n", err);
20847c478bd9Sstevel@tonic-gate 		return (0);
20857c478bd9Sstevel@tonic-gate 	}
20867c478bd9Sstevel@tonic-gate 
20877c478bd9Sstevel@tonic-gate 	if ((error == 0) && (info.dki_ctype == DKC_PCMCIA_MEM) &&
20887c478bd9Sstevel@tonic-gate 		(info.dki_flags & DKI_PCMCIA_PFD))
20897c478bd9Sstevel@tonic-gate 		return (1);
20907c478bd9Sstevel@tonic-gate 	else
20917c478bd9Sstevel@tonic-gate 		return (0);
20927c478bd9Sstevel@tonic-gate }
20937c478bd9Sstevel@tonic-gate 
20947c478bd9Sstevel@tonic-gate /*
20957c478bd9Sstevel@tonic-gate  * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about
20967c478bd9Sstevel@tonic-gate  * a meg), so we can't bread() it all in at once. This routine reads a
20977c478bd9Sstevel@tonic-gate  * fat a chunk at a time.
20987c478bd9Sstevel@tonic-gate  */
20997c478bd9Sstevel@tonic-gate static int
21007c478bd9Sstevel@tonic-gate pc_readfat(struct pcfs *fsp, uchar_t *fatp, daddr_t start, size_t fatsize)
21017c478bd9Sstevel@tonic-gate {
21027c478bd9Sstevel@tonic-gate 	struct buf *bp;
21037c478bd9Sstevel@tonic-gate 	size_t off;
21047c478bd9Sstevel@tonic-gate 	size_t readsize;
21057c478bd9Sstevel@tonic-gate 
21067c478bd9Sstevel@tonic-gate 	readsize = fsp->pcfs_clsize;
21077c478bd9Sstevel@tonic-gate 	for (off = 0; off < fatsize; off += readsize, fatp += readsize) {
21087c478bd9Sstevel@tonic-gate 		if (readsize > (fatsize - off))
21097c478bd9Sstevel@tonic-gate 			readsize = fatsize - off;
21107c478bd9Sstevel@tonic-gate 		bp = bread(fsp->pcfs_xdev,
21117c478bd9Sstevel@tonic-gate 		    pc_dbdaddr(fsp, start +
21127c478bd9Sstevel@tonic-gate 			pc_cltodb(fsp, pc_lblkno(fsp, off))),
21137c478bd9Sstevel@tonic-gate 		    readsize);
21147c478bd9Sstevel@tonic-gate 		if (bp->b_flags & (B_ERROR | B_STALE)) {
21157c478bd9Sstevel@tonic-gate 			brelse(bp);
21167c478bd9Sstevel@tonic-gate 			return (EIO);
21177c478bd9Sstevel@tonic-gate 		}
21187c478bd9Sstevel@tonic-gate 		bp->b_flags |= B_STALE | B_AGE;
21197c478bd9Sstevel@tonic-gate 		bcopy(bp->b_un.b_addr, fatp, readsize);
21207c478bd9Sstevel@tonic-gate 		brelse(bp);
21217c478bd9Sstevel@tonic-gate 	}
21227c478bd9Sstevel@tonic-gate 	return (0);
21237c478bd9Sstevel@tonic-gate }
21247c478bd9Sstevel@tonic-gate 
21257c478bd9Sstevel@tonic-gate /*
21267c478bd9Sstevel@tonic-gate  * We write the FAT out a _lot_, in order to make sure that it
21277c478bd9Sstevel@tonic-gate  * is up-to-date. But on a FAT32 system (large drive, small clusters)
21287c478bd9Sstevel@tonic-gate  * the FAT might be a couple of megabytes, and writing it all out just
21297c478bd9Sstevel@tonic-gate  * because we created or deleted a small file is painful (especially
21307c478bd9Sstevel@tonic-gate  * since we do it for each alternate FAT too). So instead, for FAT16 and
21317c478bd9Sstevel@tonic-gate  * FAT32 we only write out the bit that has changed. We don't clear
21327c478bd9Sstevel@tonic-gate  * the 'updated' fields here because the caller might be writing out
21337c478bd9Sstevel@tonic-gate  * several FATs, so the caller must use pc_clear_fatchanges() after
21347c478bd9Sstevel@tonic-gate  * all FATs have been updated.
21357c478bd9Sstevel@tonic-gate  */
21367c478bd9Sstevel@tonic-gate static int
21377c478bd9Sstevel@tonic-gate pc_writefat(struct pcfs *fsp, daddr_t start)
21387c478bd9Sstevel@tonic-gate {
21397c478bd9Sstevel@tonic-gate 	struct buf *bp;
21407c478bd9Sstevel@tonic-gate 	size_t off;
21417c478bd9Sstevel@tonic-gate 	size_t writesize;
21427c478bd9Sstevel@tonic-gate 	int	error;
21437c478bd9Sstevel@tonic-gate 	uchar_t *fatp = fsp->pcfs_fatp;
21447c478bd9Sstevel@tonic-gate 	size_t fatsize = fsp->pcfs_fatsize;
21457c478bd9Sstevel@tonic-gate 
21467c478bd9Sstevel@tonic-gate 	writesize = fsp->pcfs_clsize;
21477c478bd9Sstevel@tonic-gate 	for (off = 0; off < fatsize; off += writesize, fatp += writesize) {
21487c478bd9Sstevel@tonic-gate 		if (writesize > (fatsize - off))
21497c478bd9Sstevel@tonic-gate 			writesize = fatsize - off;
21507c478bd9Sstevel@tonic-gate 		if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) {
21517c478bd9Sstevel@tonic-gate 			continue;
21527c478bd9Sstevel@tonic-gate 		}
21537c478bd9Sstevel@tonic-gate 		bp = ngeteblk(writesize);
21547c478bd9Sstevel@tonic-gate 		bp->b_edev = fsp->pcfs_xdev;
21557c478bd9Sstevel@tonic-gate 		bp->b_dev = cmpdev(bp->b_edev);
21567c478bd9Sstevel@tonic-gate 		bp->b_blkno = pc_dbdaddr(fsp, start +
21577c478bd9Sstevel@tonic-gate 		    pc_cltodb(fsp, pc_lblkno(fsp, off)));
21587c478bd9Sstevel@tonic-gate 		bcopy(fatp, bp->b_un.b_addr, writesize);
21597c478bd9Sstevel@tonic-gate 		bwrite2(bp);
21607c478bd9Sstevel@tonic-gate 		error = geterror(bp);
21617c478bd9Sstevel@tonic-gate 		brelse(bp);
21627c478bd9Sstevel@tonic-gate 		if (error) {
21637c478bd9Sstevel@tonic-gate 			return (error);
21647c478bd9Sstevel@tonic-gate 		}
21657c478bd9Sstevel@tonic-gate 	}
21667c478bd9Sstevel@tonic-gate 	return (0);
21677c478bd9Sstevel@tonic-gate }
21687c478bd9Sstevel@tonic-gate 
21697c478bd9Sstevel@tonic-gate /*
21707c478bd9Sstevel@tonic-gate  * Mark the FAT cluster that 'cn' is stored in as modified.
21717c478bd9Sstevel@tonic-gate  */
21727c478bd9Sstevel@tonic-gate void
21737c478bd9Sstevel@tonic-gate pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn)
21747c478bd9Sstevel@tonic-gate {
21757c478bd9Sstevel@tonic-gate 	pc_cluster32_t	bn;
21767c478bd9Sstevel@tonic-gate 	size_t		size;
21777c478bd9Sstevel@tonic-gate 
21787c478bd9Sstevel@tonic-gate 	/* which fat block is the cluster number stored in? */
21797c478bd9Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
21807c478bd9Sstevel@tonic-gate 		size = sizeof (pc_cluster32_t);
21817c478bd9Sstevel@tonic-gate 		bn = pc_lblkno(fsp, cn * size);
21827c478bd9Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
21837c478bd9Sstevel@tonic-gate 	} else if (IS_FAT16(fsp)) {
21847c478bd9Sstevel@tonic-gate 		size = sizeof (pc_cluster16_t);
21857c478bd9Sstevel@tonic-gate 		bn = pc_lblkno(fsp, cn * size);
21867c478bd9Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
21877c478bd9Sstevel@tonic-gate 	} else {
21887c478bd9Sstevel@tonic-gate 		offset_t off;
21897c478bd9Sstevel@tonic-gate 		pc_cluster32_t nbn;
21907c478bd9Sstevel@tonic-gate 
21917c478bd9Sstevel@tonic-gate 		ASSERT(IS_FAT12(fsp));
21927c478bd9Sstevel@tonic-gate 		off = cn + (cn >> 1);
21937c478bd9Sstevel@tonic-gate 		bn = pc_lblkno(fsp, off);
21947c478bd9Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
21957c478bd9Sstevel@tonic-gate 		/* does this field wrap into the next fat cluster? */
21967c478bd9Sstevel@tonic-gate 		nbn = pc_lblkno(fsp, off + 1);
21977c478bd9Sstevel@tonic-gate 		if (nbn != bn) {
21987c478bd9Sstevel@tonic-gate 			fsp->pcfs_fat_changemap[nbn] = 1;
21997c478bd9Sstevel@tonic-gate 		}
22007c478bd9Sstevel@tonic-gate 	}
22017c478bd9Sstevel@tonic-gate }
22027c478bd9Sstevel@tonic-gate 
22037c478bd9Sstevel@tonic-gate /*
22047c478bd9Sstevel@tonic-gate  * return whether the FAT cluster 'bn' is updated and needs to
22057c478bd9Sstevel@tonic-gate  * be written out.
22067c478bd9Sstevel@tonic-gate  */
22077c478bd9Sstevel@tonic-gate int
22087c478bd9Sstevel@tonic-gate pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn)
22097c478bd9Sstevel@tonic-gate {
22107c478bd9Sstevel@tonic-gate 	return (fsp->pcfs_fat_changemap[bn] == 1);
22117c478bd9Sstevel@tonic-gate }
2212264a6e74Sfrankho 
2213264a6e74Sfrankho /*
2214264a6e74Sfrankho  * Implementation of VFS_FREEVFS() to support forced umounts.
2215264a6e74Sfrankho  * This is called by the vfs framework after umount, to trigger
2216264a6e74Sfrankho  * the release of any resources still associated with the given
2217264a6e74Sfrankho  * vfs_t once the need to keep them has gone away.
2218264a6e74Sfrankho  */
2219264a6e74Sfrankho void
2220264a6e74Sfrankho pcfs_freevfs(vfs_t *vfsp)
2221264a6e74Sfrankho {
2222264a6e74Sfrankho 	struct pcfs *fsp = VFSTOPCFS(vfsp);
2223264a6e74Sfrankho 
2224264a6e74Sfrankho 	mutex_enter(&pcfslock);
2225264a6e74Sfrankho 	if (fsp->pcfs_fatp != (uchar_t *)0)
2226264a6e74Sfrankho 		pc_invalfat(fsp);
2227264a6e74Sfrankho 	mutex_exit(&pcfslock);
2228264a6e74Sfrankho 
2229264a6e74Sfrankho 	VN_RELE(fsp->pcfs_devvp);
2230264a6e74Sfrankho 	mutex_destroy(&fsp->pcfs_lock);
2231264a6e74Sfrankho 	kmem_free(fsp, (uint_t)sizeof (struct pcfs));
2232264a6e74Sfrankho 
2233264a6e74Sfrankho 	/*
2234264a6e74Sfrankho 	 * Allow _fini() to succeed now, if so desired.
2235264a6e74Sfrankho 	 */
2236264a6e74Sfrankho 	atomic_dec_32(&pcfs_mountcount);
2237264a6e74Sfrankho }
2238