xref: /onnv-gate/usr/src/uts/common/fs/pcfs/pc_vfsops.c (revision 2972:10096047b1c0)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51488Srsb  * Common Development and Distribution License (the "License").
61488Srsb  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
221488Srsb  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <sys/param.h>
290Sstevel@tonic-gate #include <sys/systm.h>
300Sstevel@tonic-gate #include <sys/kmem.h>
310Sstevel@tonic-gate #include <sys/user.h>
320Sstevel@tonic-gate #include <sys/proc.h>
330Sstevel@tonic-gate #include <sys/cred.h>
340Sstevel@tonic-gate #include <sys/disp.h>
350Sstevel@tonic-gate #include <sys/buf.h>
360Sstevel@tonic-gate #include <sys/vfs.h>
370Sstevel@tonic-gate #include <sys/vnode.h>
380Sstevel@tonic-gate #include <sys/fdio.h>
390Sstevel@tonic-gate #include <sys/file.h>
400Sstevel@tonic-gate #include <sys/uio.h>
410Sstevel@tonic-gate #include <sys/conf.h>
420Sstevel@tonic-gate #undef NFSCLIENT
430Sstevel@tonic-gate #include <sys/statvfs.h>
440Sstevel@tonic-gate #include <sys/mount.h>
450Sstevel@tonic-gate #include <sys/pathname.h>
460Sstevel@tonic-gate #include <sys/cmn_err.h>
470Sstevel@tonic-gate #include <sys/debug.h>
480Sstevel@tonic-gate #include <sys/sysmacros.h>
490Sstevel@tonic-gate #include <sys/conf.h>
500Sstevel@tonic-gate #include <sys/mkdev.h>
510Sstevel@tonic-gate #include <sys/swap.h>
520Sstevel@tonic-gate #include <sys/sunddi.h>
530Sstevel@tonic-gate #include <sys/sunldi.h>
540Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
550Sstevel@tonic-gate #include <sys/fs/pc_label.h>
560Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
570Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
580Sstevel@tonic-gate #include <sys/fs/pc_node.h>
590Sstevel@tonic-gate #include <fs/fs_subr.h>
600Sstevel@tonic-gate #include <sys/modctl.h>
610Sstevel@tonic-gate #include <sys/dkio.h>
620Sstevel@tonic-gate #include <sys/open.h>
630Sstevel@tonic-gate #include <sys/mntent.h>
640Sstevel@tonic-gate #include <sys/policy.h>
652720Sfrankho #include <sys/atomic.h>
660Sstevel@tonic-gate 
670Sstevel@tonic-gate /*
680Sstevel@tonic-gate  * The majority of PC media use a 512 sector size, but
690Sstevel@tonic-gate  * occasionally you will run across a 1k sector size.
700Sstevel@tonic-gate  * For media with a 1k sector size, fd_strategy() requires
710Sstevel@tonic-gate  * the I/O size to be a 1k multiple; so when the sector size
720Sstevel@tonic-gate  * is not yet known, always read 1k.
730Sstevel@tonic-gate  */
740Sstevel@tonic-gate #define	PC_SAFESECSIZE	(PC_SECSIZE * 2)
750Sstevel@tonic-gate 
76201Sjmcp static int pcfs_pseudo_floppy(dev_t);
770Sstevel@tonic-gate 
780Sstevel@tonic-gate static int pcfsinit(int, char *);
790Sstevel@tonic-gate static int pcfs_mount(struct vfs *, struct vnode *, struct mounta *,
800Sstevel@tonic-gate 	struct cred *);
810Sstevel@tonic-gate static int pcfs_unmount(struct vfs *, int, struct cred *);
820Sstevel@tonic-gate static int pcfs_root(struct vfs *, struct vnode **);
830Sstevel@tonic-gate static int pcfs_statvfs(struct vfs *, struct statvfs64 *);
840Sstevel@tonic-gate static int pc_syncfsnodes(struct pcfs *);
850Sstevel@tonic-gate static int pcfs_sync(struct vfs *, short, struct cred *);
860Sstevel@tonic-gate static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp);
872720Sfrankho static void pcfs_freevfs(vfs_t *vfsp);
880Sstevel@tonic-gate 
890Sstevel@tonic-gate static int pc_getfattype(struct vnode *, int, daddr_t *, int *);
900Sstevel@tonic-gate static int pc_readfat(struct pcfs *fsp, uchar_t *fatp, daddr_t start,
910Sstevel@tonic-gate     size_t fatsize);
920Sstevel@tonic-gate static int pc_writefat(struct pcfs *fsp, daddr_t start);
930Sstevel@tonic-gate 
940Sstevel@tonic-gate /*
950Sstevel@tonic-gate  * pcfs mount options table
960Sstevel@tonic-gate  */
970Sstevel@tonic-gate 
982720Sfrankho static char *nohidden_cancel[] = { MNTOPT_PCFS_HIDDEN, NULL };
992720Sfrankho static char *hidden_cancel[] = { MNTOPT_PCFS_NOHIDDEN, NULL };
1002720Sfrankho static char *nofoldcase_cancel[] = { MNTOPT_PCFS_FOLDCASE, NULL };
1012720Sfrankho static char *foldcase_cancel[] = { MNTOPT_PCFS_NOFOLDCASE, NULL };
1022720Sfrankho static char *clamptime_cancel[] = { MNTOPT_PCFS_NOCLAMPTIME, NULL };
1032720Sfrankho static char *noclamptime_cancel[] = { MNTOPT_PCFS_CLAMPTIME, NULL };
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate static mntopt_t mntopts[] = {
1060Sstevel@tonic-gate /*
1072720Sfrankho  *	option name	cancel option	default arg	flags	opt data
1080Sstevel@tonic-gate  */
1092720Sfrankho 	{ MNTOPT_PCFS_NOHIDDEN, nohidden_cancel, NULL, 0, NULL },
1102720Sfrankho 	{ MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, MO_DEFAULT, NULL },
1112720Sfrankho 	{ MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, NULL },
1122720Sfrankho 	{ MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0, NULL },
1132720Sfrankho 	{ MNTOPT_PCFS_CLAMPTIME, clamptime_cancel, NULL, MO_DEFAULT, NULL },
1142720Sfrankho 	{ MNTOPT_PCFS_NOCLAMPTIME, noclamptime_cancel, NULL, NULL, NULL }
1150Sstevel@tonic-gate };
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate static mntopts_t pcfs_mntopts = {
1180Sstevel@tonic-gate 	sizeof (mntopts) / sizeof (mntopt_t),
1190Sstevel@tonic-gate 	mntopts
1200Sstevel@tonic-gate };
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate int pcfsdebuglevel = 0;
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate /*
1250Sstevel@tonic-gate  * pcfslock:	protects the list of mounted pc filesystems "pc_mounttab.
1260Sstevel@tonic-gate  * pcfs_lock:	(inside per filesystem structure "pcfs")
1270Sstevel@tonic-gate  *		per filesystem lock. Most of the vfsops and vnodeops are
1280Sstevel@tonic-gate  *		protected by this lock.
1290Sstevel@tonic-gate  * pcnodes_lock: protects the pcnode hash table "pcdhead", "pcfhead".
1300Sstevel@tonic-gate  *
1310Sstevel@tonic-gate  * Lock hierarchy: pcfslock > pcfs_lock > pcnodes_lock
1322720Sfrankho  *
1332720Sfrankho  * pcfs_mountcount:	used to prevent module unloads while there is still
1342720Sfrankho  *			pcfs state from a former mount hanging around. With
1352720Sfrankho  *			forced umount support, the filesystem module must not
1362720Sfrankho  *			be allowed to go away before the last VFS_FREEVFS()
1372720Sfrankho  *			call has been made.
1382720Sfrankho  *			Since this is just an atomic counter, there's no need
1392720Sfrankho  *			for locking.
1400Sstevel@tonic-gate  */
1410Sstevel@tonic-gate kmutex_t	pcfslock;
1422720Sfrankho krwlock_t	pcnodes_lock;
1432720Sfrankho uint32_t	pcfs_mountcount;
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate static int pcfstype;
1460Sstevel@tonic-gate 
1470Sstevel@tonic-gate static vfsdef_t vfw = {
1480Sstevel@tonic-gate 	VFSDEF_VERSION,
1490Sstevel@tonic-gate 	"pcfs",
1500Sstevel@tonic-gate 	pcfsinit,
1511488Srsb 	VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS,
1520Sstevel@tonic-gate 	&pcfs_mntopts
1530Sstevel@tonic-gate };
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate extern struct mod_ops mod_fsops;
1560Sstevel@tonic-gate 
1570Sstevel@tonic-gate static struct modlfs modlfs = {
1580Sstevel@tonic-gate 	&mod_fsops,
159*2972Sfrankho 	"PC filesystem v1.100",
1600Sstevel@tonic-gate 	&vfw
1610Sstevel@tonic-gate };
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate static struct modlinkage modlinkage = {
1640Sstevel@tonic-gate 	MODREV_1,
1650Sstevel@tonic-gate 	&modlfs,
1660Sstevel@tonic-gate 	NULL
1670Sstevel@tonic-gate };
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate int
1700Sstevel@tonic-gate _init(void)
1710Sstevel@tonic-gate {
1720Sstevel@tonic-gate 	int	error;
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate #if !defined(lint)
1750Sstevel@tonic-gate 	/* make sure the on-disk structures are sane */
1760Sstevel@tonic-gate 	ASSERT(sizeof (struct pcdir) == 32);
1770Sstevel@tonic-gate 	ASSERT(sizeof (struct pcdir_lfn) == 32);
1780Sstevel@tonic-gate #endif
1790Sstevel@tonic-gate 	mutex_init(&pcfslock, NULL, MUTEX_DEFAULT, NULL);
1800Sstevel@tonic-gate 	rw_init(&pcnodes_lock, NULL, RW_DEFAULT, NULL);
1810Sstevel@tonic-gate 	error = mod_install(&modlinkage);
1820Sstevel@tonic-gate 	if (error) {
1830Sstevel@tonic-gate 		mutex_destroy(&pcfslock);
1840Sstevel@tonic-gate 		rw_destroy(&pcnodes_lock);
1850Sstevel@tonic-gate 	}
1860Sstevel@tonic-gate 	return (error);
1870Sstevel@tonic-gate }
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate int
1900Sstevel@tonic-gate _fini(void)
1910Sstevel@tonic-gate {
1920Sstevel@tonic-gate 	int	error;
1930Sstevel@tonic-gate 
1942720Sfrankho 	/*
1952720Sfrankho 	 * If a forcedly unmounted instance is still hanging around,
1962720Sfrankho 	 * we cannot allow the module to be unloaded because that would
1972720Sfrankho 	 * cause panics once the VFS framework decides it's time to call
1982720Sfrankho 	 * into VFS_FREEVFS().
1992720Sfrankho 	 */
2002720Sfrankho 	if (pcfs_mountcount)
2012720Sfrankho 		return (EBUSY);
2022720Sfrankho 
2030Sstevel@tonic-gate 	error = mod_remove(&modlinkage);
2040Sstevel@tonic-gate 	if (error)
2050Sstevel@tonic-gate 		return (error);
2060Sstevel@tonic-gate 	mutex_destroy(&pcfslock);
2070Sstevel@tonic-gate 	rw_destroy(&pcnodes_lock);
2080Sstevel@tonic-gate 	/*
2090Sstevel@tonic-gate 	 * Tear down the operations vectors
2100Sstevel@tonic-gate 	 */
2110Sstevel@tonic-gate 	(void) vfs_freevfsops_by_type(pcfstype);
2120Sstevel@tonic-gate 	vn_freevnodeops(pcfs_fvnodeops);
2130Sstevel@tonic-gate 	vn_freevnodeops(pcfs_dvnodeops);
2140Sstevel@tonic-gate 	return (0);
2150Sstevel@tonic-gate }
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate int
2180Sstevel@tonic-gate _info(struct modinfo *modinfop)
2190Sstevel@tonic-gate {
2200Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2210Sstevel@tonic-gate }
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate /* ARGSUSED1 */
2240Sstevel@tonic-gate static int
2250Sstevel@tonic-gate pcfsinit(int fstype, char *name)
2260Sstevel@tonic-gate {
2270Sstevel@tonic-gate 	static const fs_operation_def_t pcfs_vfsops_template[] = {
2280Sstevel@tonic-gate 		VFSNAME_MOUNT, pcfs_mount,
2290Sstevel@tonic-gate 		VFSNAME_UNMOUNT, pcfs_unmount,
2300Sstevel@tonic-gate 		VFSNAME_ROOT, pcfs_root,
2310Sstevel@tonic-gate 		VFSNAME_STATVFS, pcfs_statvfs,
2320Sstevel@tonic-gate 		VFSNAME_SYNC, (fs_generic_func_p) pcfs_sync,
2330Sstevel@tonic-gate 		VFSNAME_VGET, pcfs_vget,
2342720Sfrankho 		VFSNAME_FREEVFS, (fs_generic_func_p) pcfs_freevfs,
2350Sstevel@tonic-gate 		NULL, NULL
2360Sstevel@tonic-gate 	};
2370Sstevel@tonic-gate 	int error;
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 	error = vfs_setfsops(fstype, pcfs_vfsops_template, NULL);
2400Sstevel@tonic-gate 	if (error != 0) {
2410Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad vfs ops template");
2420Sstevel@tonic-gate 		return (error);
2430Sstevel@tonic-gate 	}
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 	error = vn_make_ops("pcfs", pcfs_fvnodeops_template, &pcfs_fvnodeops);
2460Sstevel@tonic-gate 	if (error != 0) {
2470Sstevel@tonic-gate 		(void) vfs_freevfsops_by_type(fstype);
2480Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad file vnode ops template");
2490Sstevel@tonic-gate 		return (error);
2500Sstevel@tonic-gate 	}
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate 	error = vn_make_ops("pcfsd", pcfs_dvnodeops_template, &pcfs_dvnodeops);
2530Sstevel@tonic-gate 	if (error != 0) {
2540Sstevel@tonic-gate 		(void) vfs_freevfsops_by_type(fstype);
2550Sstevel@tonic-gate 		vn_freevnodeops(pcfs_fvnodeops);
2560Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad dir vnode ops template");
2570Sstevel@tonic-gate 		return (error);
2580Sstevel@tonic-gate 	}
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate 	pcfstype = fstype;
2610Sstevel@tonic-gate 	(void) pc_init();
2622720Sfrankho 	pcfs_mountcount = 0;
2630Sstevel@tonic-gate 	return (0);
2640Sstevel@tonic-gate }
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate static struct pcfs *pc_mounttab = NULL;
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate extern struct pcfs_args pc_tz;
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate /*
2710Sstevel@tonic-gate  *  Define some special logical drives we use internal to this file.
2720Sstevel@tonic-gate  */
2730Sstevel@tonic-gate #define	BOOT_PARTITION_DRIVE	99
2740Sstevel@tonic-gate #define	PRIMARY_DOS_DRIVE	1
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate /*
2770Sstevel@tonic-gate  * pc_mount system call
2780Sstevel@tonic-gate  */
2790Sstevel@tonic-gate static int
2800Sstevel@tonic-gate pcfs_mount(
2810Sstevel@tonic-gate 	struct vfs *vfsp,
2820Sstevel@tonic-gate 	struct vnode *mvp,
2830Sstevel@tonic-gate 	struct mounta *uap,
2840Sstevel@tonic-gate 	struct cred *cr)
2850Sstevel@tonic-gate {
2860Sstevel@tonic-gate 	struct pcfs *fsp;
2870Sstevel@tonic-gate 	struct vnode *bvp;
2880Sstevel@tonic-gate 	struct vnode *devvp;
2890Sstevel@tonic-gate 	struct pathname special;
2900Sstevel@tonic-gate 	daddr_t dosstart;
2910Sstevel@tonic-gate 	dev_t pseudodev;
2920Sstevel@tonic-gate 	dev_t xdev;
293*2972Sfrankho 	char *c;
2940Sstevel@tonic-gate 	char *data = uap->dataptr;
2950Sstevel@tonic-gate 	int datalen = uap->datalen;
2960Sstevel@tonic-gate 	int dos_ldrive = 0;
2970Sstevel@tonic-gate 	int error;
2980Sstevel@tonic-gate 	int fattype;
2990Sstevel@tonic-gate 	minor_t	minor;
3000Sstevel@tonic-gate 	int oflag, aflag;
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
3030Sstevel@tonic-gate 		return (error);
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate 	PC_DPRINTF0(4, "pcfs_mount\n");
3060Sstevel@tonic-gate 	if (mvp->v_type != VDIR) {
3070Sstevel@tonic-gate 		return (ENOTDIR);
3080Sstevel@tonic-gate 	}
3090Sstevel@tonic-gate 	mutex_enter(&mvp->v_lock);
3100Sstevel@tonic-gate 	if ((uap->flags & MS_REMOUNT) == 0 &&
3110Sstevel@tonic-gate 	    (uap->flags & MS_OVERLAY) == 0 &&
3120Sstevel@tonic-gate 	    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
3130Sstevel@tonic-gate 		mutex_exit(&mvp->v_lock);
3140Sstevel@tonic-gate 		return (EBUSY);
3150Sstevel@tonic-gate 	}
3160Sstevel@tonic-gate 	mutex_exit(&mvp->v_lock);
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 	/*
3190Sstevel@tonic-gate 	 * The caller is responsible for making sure to always
3200Sstevel@tonic-gate 	 * pass in sizeof(struct pcfs_args) (or the old one).
3210Sstevel@tonic-gate 	 * Doing this is the only way to know an EINVAL return
3220Sstevel@tonic-gate 	 * from mount(2) is due to the "not a DOS filesystem"
3230Sstevel@tonic-gate 	 * EINVAL that pc_verify/pc_getfattype could return.
3240Sstevel@tonic-gate 	 */
3250Sstevel@tonic-gate 	if ((datalen != sizeof (struct pcfs_args)) &&
3260Sstevel@tonic-gate 	    (datalen != sizeof (struct old_pcfs_args))) {
3270Sstevel@tonic-gate 		return (EINVAL);
3280Sstevel@tonic-gate 	} else {
3290Sstevel@tonic-gate 		struct pcfs_args tmp_tz;
3300Sstevel@tonic-gate 		int hidden = 0;
3310Sstevel@tonic-gate 		int foldcase = 0;
3322720Sfrankho 		int noclamptime = 0;
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate 		tmp_tz.flags = 0;
3350Sstevel@tonic-gate 		if (copyin(data, &tmp_tz, datalen)) {
3360Sstevel@tonic-gate 			return (EFAULT);
3370Sstevel@tonic-gate 		}
3380Sstevel@tonic-gate 		if (datalen == sizeof (struct pcfs_args)) {
3390Sstevel@tonic-gate 			hidden = tmp_tz.flags & PCFS_MNT_HIDDEN;
3400Sstevel@tonic-gate 			foldcase = tmp_tz.flags & PCFS_MNT_FOLDCASE;
3412720Sfrankho 			noclamptime = tmp_tz.flags & PCFS_MNT_NOCLAMPTIME;
3420Sstevel@tonic-gate 		}
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 		if (hidden)
3450Sstevel@tonic-gate 			vfs_setmntopt(vfsp, MNTOPT_PCFS_HIDDEN,	NULL, 0);
3460Sstevel@tonic-gate 		if (foldcase)
3470Sstevel@tonic-gate 			vfs_setmntopt(vfsp, MNTOPT_PCFS_FOLDCASE, NULL, 0);
3482720Sfrankho 		if (noclamptime)
3492720Sfrankho 			vfs_setmntopt(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL, 0);
3500Sstevel@tonic-gate 		/*
3510Sstevel@tonic-gate 		 * more than one pc filesystem can be mounted on x86
3520Sstevel@tonic-gate 		 * so the pc_tz structure is now a critical region
3530Sstevel@tonic-gate 		 */
3540Sstevel@tonic-gate 		mutex_enter(&pcfslock);
3550Sstevel@tonic-gate 		if (pc_mounttab == NULL)
3560Sstevel@tonic-gate 			bcopy(&tmp_tz, &pc_tz, sizeof (struct pcfs_args));
3570Sstevel@tonic-gate 		mutex_exit(&pcfslock);
3580Sstevel@tonic-gate 	}
3590Sstevel@tonic-gate 	/*
3600Sstevel@tonic-gate 	 * Resolve path name of special file being mounted.
3610Sstevel@tonic-gate 	 */
3620Sstevel@tonic-gate 	if (error = pn_get(uap->spec, UIO_USERSPACE, &special)) {
3630Sstevel@tonic-gate 		return (error);
3640Sstevel@tonic-gate 	}
3650Sstevel@tonic-gate 	if (error =
3660Sstevel@tonic-gate 	    lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &bvp)) {
3670Sstevel@tonic-gate 		/*
368*2972Sfrankho 		 * Split the pathname string at the last ':' separator.
369*2972Sfrankho 		 * If there's no ':' in the device name, or the ':' is the
370*2972Sfrankho 		 * last character in the string, the name is invalid and
371*2972Sfrankho 		 * the error from the previous lookup will be returned.
3720Sstevel@tonic-gate 		 */
373*2972Sfrankho 		c = strrchr(special.pn_path, ':');
374*2972Sfrankho 		if (c == NULL || strlen(c) == 0)
375*2972Sfrankho 			goto devlookup_done;
376*2972Sfrankho 
377*2972Sfrankho 		*c++ = '\0';
3780Sstevel@tonic-gate 
379*2972Sfrankho 		/*
380*2972Sfrankho 		 * PCFS partition name suffixes can be:
381*2972Sfrankho 		 *	- "boot" to indicate the X86BOOT partition
382*2972Sfrankho 		 *	- a drive letter [c-z] for the "DOS logical drive"
383*2972Sfrankho 		 *	- a drive number 1..24 for the "DOS logical drive"
384*2972Sfrankho 		 */
385*2972Sfrankho 		if (strcasecmp(c, "boot") == 0) {
386*2972Sfrankho 			/*
387*2972Sfrankho 			 * The Solaris boot partition is requested.
388*2972Sfrankho 			 */
389*2972Sfrankho 			dos_ldrive = BOOT_PARTITION_DRIVE;
390*2972Sfrankho 		} else if (strspn(c, "0123456789") == strlen(c)) {
391*2972Sfrankho 			/*
392*2972Sfrankho 			 * All digits - parse the partition number.
393*2972Sfrankho 			 */
394*2972Sfrankho 			long drvnum = 0;
395*2972Sfrankho 
396*2972Sfrankho 			if ((error = ddi_strtol(c, NULL, 10, &drvnum)) == 0) {
3970Sstevel@tonic-gate 				/*
398*2972Sfrankho 				 * A number alright - in the allowed range ?
3990Sstevel@tonic-gate 				 */
400*2972Sfrankho 				if (drvnum > 24 || drvnum == 0)
401*2972Sfrankho 					error = EINVAL;
4020Sstevel@tonic-gate 			}
403*2972Sfrankho 			if (error)
404*2972Sfrankho 				goto devlookup_done;
405*2972Sfrankho 			dos_ldrive = (int)drvnum;
406*2972Sfrankho 		} else if (strlen(c) == 1) {
407*2972Sfrankho 			/*
408*2972Sfrankho 			 * A single trailing character - is it [c-zC-Z] ?
409*2972Sfrankho 			 */
410*2972Sfrankho 			*c = tolower(*c);
411*2972Sfrankho 			if (*c < 'c' || *c > 'z') {
412*2972Sfrankho 				error = EINVAL;
413*2972Sfrankho 				goto devlookup_done;
414*2972Sfrankho 			}
415*2972Sfrankho 			dos_ldrive = 1 + *c - 'c';
416*2972Sfrankho 		} else {
417*2972Sfrankho 			/*
418*2972Sfrankho 			 * Can't parse this - pass through previous error.
419*2972Sfrankho 			 */
420*2972Sfrankho 			goto devlookup_done;
4210Sstevel@tonic-gate 		}
4220Sstevel@tonic-gate 
423*2972Sfrankho 		ASSERT(dos_ldrive > 0);
424*2972Sfrankho 
425*2972Sfrankho 		error = lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW,
426*2972Sfrankho 		    NULLVPP, &bvp);
4270Sstevel@tonic-gate 	}
428*2972Sfrankho devlookup_done:
4290Sstevel@tonic-gate 	pn_free(&special);
430*2972Sfrankho 	if (error)
431*2972Sfrankho 		return (error);
432*2972Sfrankho 
4330Sstevel@tonic-gate 	if (bvp->v_type != VBLK) {
4340Sstevel@tonic-gate 		VN_RELE(bvp);
4350Sstevel@tonic-gate 		return (ENOTBLK);
4360Sstevel@tonic-gate 	}
4370Sstevel@tonic-gate 	xdev = bvp->v_rdev;
4380Sstevel@tonic-gate 	/*
4390Sstevel@tonic-gate 	 * Verify caller's permission to open the device special file.
4400Sstevel@tonic-gate 	 */
4410Sstevel@tonic-gate 	if ((vfsp->vfs_flag & VFS_RDONLY) != 0 ||
4420Sstevel@tonic-gate 	    ((uap->flags & MS_RDONLY) != 0)) {
4430Sstevel@tonic-gate 		oflag = FREAD;
4440Sstevel@tonic-gate 		aflag = VREAD;
4450Sstevel@tonic-gate 	} else {
4460Sstevel@tonic-gate 		oflag = FREAD | FWRITE;
4470Sstevel@tonic-gate 		aflag = VREAD | VWRITE;
4480Sstevel@tonic-gate 	}
4490Sstevel@tonic-gate 	if ((error = VOP_ACCESS(bvp, aflag, 0, cr)) != 0 ||
4500Sstevel@tonic-gate 	    (error = secpolicy_spec_open(cr, bvp, oflag)) != 0) {
4510Sstevel@tonic-gate 		VN_RELE(bvp);
4520Sstevel@tonic-gate 		return (error);
4530Sstevel@tonic-gate 	}
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 	VN_RELE(bvp);
4560Sstevel@tonic-gate 	if (getmajor(xdev) >= devcnt) {
4570Sstevel@tonic-gate 		return (ENXIO);
4580Sstevel@tonic-gate 	}
4590Sstevel@tonic-gate 	/*
460*2972Sfrankho 	 * Ensure that this logical drive isn't already mounted, unless
461*2972Sfrankho 	 * this is a REMOUNT request.
462*2972Sfrankho 	 * Note: The framework will perform this check if the "...:c"
463*2972Sfrankho 	 * PCFS-style "logical drive" syntax has not been used and an
464*2972Sfrankho 	 * actually existing physical device is backing this filesystem.
4650Sstevel@tonic-gate 	 */
4660Sstevel@tonic-gate 	if (dos_ldrive) {
4670Sstevel@tonic-gate 		mutex_enter(&pcfslock);
4680Sstevel@tonic-gate 		for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt)
4690Sstevel@tonic-gate 			if (fsp->pcfs_xdev == xdev &&
4700Sstevel@tonic-gate 			    fsp->pcfs_ldrv == dos_ldrive) {
4710Sstevel@tonic-gate 				mutex_exit(&pcfslock);
4720Sstevel@tonic-gate 				if (uap->flags & MS_REMOUNT) {
4730Sstevel@tonic-gate 					return (0);
4740Sstevel@tonic-gate 				} else {
4750Sstevel@tonic-gate 					return (EBUSY);
4760Sstevel@tonic-gate 				}
4770Sstevel@tonic-gate 			}
4780Sstevel@tonic-gate 		/*
4790Sstevel@tonic-gate 		 * Assign a unique device number for the vfs
4800Sstevel@tonic-gate 		 * The old way (getudev() + a constantly incrementing
4810Sstevel@tonic-gate 		 * major number) was wrong because it changes vfs_dev
4820Sstevel@tonic-gate 		 * across mounts and reboots, which breaks nfs file handles.
4830Sstevel@tonic-gate 		 * UFS just uses the real dev_t. We can't do that because
4840Sstevel@tonic-gate 		 * of the way pcfs opens fdisk partitons (the :c and :d
4850Sstevel@tonic-gate 		 * partitions are on the same dev_t). Though that _might_
4860Sstevel@tonic-gate 		 * actually be ok, since the file handle contains an
4870Sstevel@tonic-gate 		 * absolute block number, it's probably better to make them
4880Sstevel@tonic-gate 		 * different. So I think we should retain the original
4890Sstevel@tonic-gate 		 * dev_t, but come up with a different minor number based
4900Sstevel@tonic-gate 		 * on the logical drive that will _always_ come up the same.
4910Sstevel@tonic-gate 		 * For now, we steal the upper 6 bits.
4920Sstevel@tonic-gate 		 */
4930Sstevel@tonic-gate #ifdef notdef
4940Sstevel@tonic-gate 		/* what should we do here? */
4950Sstevel@tonic-gate 		if (((getminor(xdev) >> 12) & 0x3F) != 0)
4960Sstevel@tonic-gate 			printf("whoops - upper bits used!\n");
4970Sstevel@tonic-gate #endif
4980Sstevel@tonic-gate 		minor = ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32;
4990Sstevel@tonic-gate 		pseudodev = makedevice(getmajor(xdev), minor);
5000Sstevel@tonic-gate 		if (vfs_devmounting(pseudodev, vfsp)) {
5010Sstevel@tonic-gate 			mutex_exit(&pcfslock);
5020Sstevel@tonic-gate 			return (EBUSY);
5030Sstevel@tonic-gate 		}
5040Sstevel@tonic-gate 		if (vfs_devismounted(pseudodev)) {
5050Sstevel@tonic-gate 			mutex_exit(&pcfslock);
5060Sstevel@tonic-gate 			if (uap->flags & MS_REMOUNT) {
5070Sstevel@tonic-gate 				return (0);
5080Sstevel@tonic-gate 			} else {
5090Sstevel@tonic-gate 				return (EBUSY);
5100Sstevel@tonic-gate 			}
5110Sstevel@tonic-gate 		}
5120Sstevel@tonic-gate 		mutex_exit(&pcfslock);
5130Sstevel@tonic-gate 	} else {
5140Sstevel@tonic-gate 		if (vfs_devmounting(xdev, vfsp)) {
5150Sstevel@tonic-gate 			return (EBUSY);
5160Sstevel@tonic-gate 		}
5170Sstevel@tonic-gate 		if (vfs_devismounted(xdev))
5180Sstevel@tonic-gate 			if (uap->flags & MS_REMOUNT) {
5190Sstevel@tonic-gate 				return (0);
5200Sstevel@tonic-gate 			} else {
5210Sstevel@tonic-gate 				return (EBUSY);
5220Sstevel@tonic-gate 			}
5230Sstevel@tonic-gate 		pseudodev = xdev;
5240Sstevel@tonic-gate 	}
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 	if (uap->flags & MS_RDONLY) {
5270Sstevel@tonic-gate 		vfsp->vfs_flag |= VFS_RDONLY;
5280Sstevel@tonic-gate 		vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
5290Sstevel@tonic-gate 	}
5300Sstevel@tonic-gate 
5310Sstevel@tonic-gate 	/*
5320Sstevel@tonic-gate 	 * Mount the filesystem
5330Sstevel@tonic-gate 	 */
5340Sstevel@tonic-gate 	devvp = makespecvp(xdev, VBLK);
5350Sstevel@tonic-gate 	if (IS_SWAPVP(devvp)) {
5360Sstevel@tonic-gate 		VN_RELE(devvp);
5370Sstevel@tonic-gate 		return (EBUSY);
5380Sstevel@tonic-gate 	}
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 	/*
5410Sstevel@tonic-gate 	 * special handling for PCMCIA memory card
542201Sjmcp 	 * with pseudo floppies organization
5430Sstevel@tonic-gate 	 */
544201Sjmcp 	if (dos_ldrive == 0 && pcfs_pseudo_floppy(xdev)) {
5450Sstevel@tonic-gate 		dosstart = (daddr_t)0;
5460Sstevel@tonic-gate 		fattype = PCFS_PCMCIA_NO_CIS;
5470Sstevel@tonic-gate 	} else {
5480Sstevel@tonic-gate 		if (error = pc_getfattype(devvp, dos_ldrive, &dosstart,
5490Sstevel@tonic-gate 		    &fattype)) {
5500Sstevel@tonic-gate 			VN_RELE(devvp);
5510Sstevel@tonic-gate 			return (error);
5520Sstevel@tonic-gate 		}
5530Sstevel@tonic-gate 	}
5540Sstevel@tonic-gate 
5550Sstevel@tonic-gate 	(void) VOP_PUTPAGE(devvp, (offset_t)0, (uint_t)0, B_INVAL, cr);
5560Sstevel@tonic-gate 	fsp = kmem_zalloc((uint_t)sizeof (struct pcfs), KM_SLEEP);
5570Sstevel@tonic-gate 	fsp->pcfs_vfs = vfsp;
5580Sstevel@tonic-gate 	fsp->pcfs_flags = fattype;
5590Sstevel@tonic-gate 	fsp->pcfs_devvp = devvp;
5600Sstevel@tonic-gate 	fsp->pcfs_xdev = xdev;
5610Sstevel@tonic-gate 	fsp->pcfs_ldrv = dos_ldrive;
5620Sstevel@tonic-gate 	fsp->pcfs_dosstart = dosstart;
5630Sstevel@tonic-gate 	mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL);
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL))
5660Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_HIDDEN;
5670Sstevel@tonic-gate 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL))
5680Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_FOLDCASE;
5692720Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL))
5702720Sfrankho 		fsp->pcfs_flags |= PCFS_NOCLAMPTIME;
5710Sstevel@tonic-gate 	vfsp->vfs_dev = pseudodev;
5720Sstevel@tonic-gate 	vfsp->vfs_fstype = pcfstype;
5730Sstevel@tonic-gate 	vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype);
5740Sstevel@tonic-gate 	vfsp->vfs_data = (caddr_t)fsp;
5750Sstevel@tonic-gate 	vfsp->vfs_bcount = 0;
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 	error = pc_verify(fsp);
5780Sstevel@tonic-gate 	if (error) {
5790Sstevel@tonic-gate 		VN_RELE(devvp);
5800Sstevel@tonic-gate 		mutex_destroy(&fsp->pcfs_lock);
5810Sstevel@tonic-gate 		kmem_free(fsp, (uint_t)sizeof (struct pcfs));
5820Sstevel@tonic-gate 		return (error);
5830Sstevel@tonic-gate 	}
5840Sstevel@tonic-gate 	vfsp->vfs_bsize = fsp->pcfs_clsize;
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate 	mutex_enter(&pcfslock);
5870Sstevel@tonic-gate 	fsp->pcfs_nxt = pc_mounttab;
5880Sstevel@tonic-gate 	pc_mounttab = fsp;
5890Sstevel@tonic-gate 	mutex_exit(&pcfslock);
5902720Sfrankho 	atomic_inc_32(&pcfs_mountcount);
5910Sstevel@tonic-gate 	return (0);
5920Sstevel@tonic-gate }
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate /*
5950Sstevel@tonic-gate  * vfs operations
5960Sstevel@tonic-gate  */
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate /* ARGSUSED */
5990Sstevel@tonic-gate static int
6000Sstevel@tonic-gate pcfs_unmount(
6010Sstevel@tonic-gate 	struct vfs *vfsp,
6020Sstevel@tonic-gate 	int flag,
6030Sstevel@tonic-gate 	struct cred *cr)
6040Sstevel@tonic-gate {
6050Sstevel@tonic-gate 	struct pcfs *fsp, *fsp1;
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate 	if (secpolicy_fs_unmount(cr, vfsp) != 0)
6080Sstevel@tonic-gate 		return (EPERM);
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate 	PC_DPRINTF0(4, "pcfs_unmount\n");
6110Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
6122720Sfrankho 
6130Sstevel@tonic-gate 	/*
6140Sstevel@tonic-gate 	 * We don't have to lock fsp because the VVFSLOCK in vfs layer will
6150Sstevel@tonic-gate 	 * prevent lookuppn from crossing the mount point.
6162720Sfrankho 	 * If this is not a forced umount request and there's ongoing I/O,
6172720Sfrankho 	 * don't allow the mount to proceed.
6180Sstevel@tonic-gate 	 */
6192720Sfrankho 	if (flag & MS_FORCE)
6202720Sfrankho 		vfsp->vfs_flag |= VFS_UNMOUNTED;
6212720Sfrankho 	else if (fsp->pcfs_nrefs)
6220Sstevel@tonic-gate 		return (EBUSY);
6232720Sfrankho 
6242720Sfrankho 	mutex_enter(&pcfslock);
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 	/*
6272720Sfrankho 	 * If this is a forced umount request or if the fs instance has
6282720Sfrankho 	 * been marked as beyond recovery, allow the umount to proceed
6292720Sfrankho 	 * regardless of state. pc_diskchanged() forcibly releases all
6302720Sfrankho 	 * inactive vnodes/pcnodes.
6310Sstevel@tonic-gate 	 */
6322720Sfrankho 	if (flag & MS_FORCE || fsp->pcfs_flags & PCFS_IRRECOV) {
6330Sstevel@tonic-gate 		rw_enter(&pcnodes_lock, RW_WRITER);
6340Sstevel@tonic-gate 		pc_diskchanged(fsp);
6350Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
6360Sstevel@tonic-gate 	}
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 	/* now there should be no pcp node on pcfhead or pcdhead. */
6390Sstevel@tonic-gate 
6400Sstevel@tonic-gate 	if (fsp == pc_mounttab) {
6410Sstevel@tonic-gate 		pc_mounttab = fsp->pcfs_nxt;
6420Sstevel@tonic-gate 	} else {
6430Sstevel@tonic-gate 		for (fsp1 = pc_mounttab; fsp1 != NULL; fsp1 = fsp1->pcfs_nxt)
6440Sstevel@tonic-gate 			if (fsp1->pcfs_nxt == fsp)
6450Sstevel@tonic-gate 				fsp1->pcfs_nxt = fsp->pcfs_nxt;
6460Sstevel@tonic-gate 	}
6470Sstevel@tonic-gate 
6480Sstevel@tonic-gate 	mutex_exit(&pcfslock);
6490Sstevel@tonic-gate 
6502720Sfrankho 	/*
6512720Sfrankho 	 * Since we support VFS_FREEVFS(), there's no need to
6522720Sfrankho 	 * free the fsp right now. The framework will tell us
6532720Sfrankho 	 * when the right time to do so has arrived by calling
6542720Sfrankho 	 * into pcfs_freevfs.
6552720Sfrankho 	 */
6560Sstevel@tonic-gate 	return (0);
6570Sstevel@tonic-gate }
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate /*
6600Sstevel@tonic-gate  * find root of pcfs
6610Sstevel@tonic-gate  */
6620Sstevel@tonic-gate static int
6630Sstevel@tonic-gate pcfs_root(
6640Sstevel@tonic-gate 	struct vfs *vfsp,
6650Sstevel@tonic-gate 	struct vnode **vpp)
6660Sstevel@tonic-gate {
6670Sstevel@tonic-gate 	struct pcfs *fsp;
6680Sstevel@tonic-gate 	struct pcnode *pcp;
6690Sstevel@tonic-gate 	int error;
6700Sstevel@tonic-gate 
6710Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
6720Sstevel@tonic-gate 	if (error = pc_lockfs(fsp, 0, 0))
6730Sstevel@tonic-gate 		return (error);
6742720Sfrankho 
6750Sstevel@tonic-gate 	pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
6760Sstevel@tonic-gate 	PC_DPRINTF2(9, "pcfs_root(0x%p) pcp= 0x%p\n",
6770Sstevel@tonic-gate 	    (void *)vfsp, (void *)pcp);
6780Sstevel@tonic-gate 	pc_unlockfs(fsp);
6790Sstevel@tonic-gate 	*vpp = PCTOV(pcp);
6800Sstevel@tonic-gate 	pcp->pc_flags |= PC_EXTERNAL;
6810Sstevel@tonic-gate 	return (0);
6820Sstevel@tonic-gate }
6830Sstevel@tonic-gate 
6840Sstevel@tonic-gate /*
6850Sstevel@tonic-gate  * Get file system statistics.
6860Sstevel@tonic-gate  */
6870Sstevel@tonic-gate static int
6880Sstevel@tonic-gate pcfs_statvfs(
6890Sstevel@tonic-gate 	struct vfs *vfsp,
6900Sstevel@tonic-gate 	struct statvfs64 *sp)
6910Sstevel@tonic-gate {
6920Sstevel@tonic-gate 	struct pcfs *fsp;
6930Sstevel@tonic-gate 	int error;
6940Sstevel@tonic-gate 	dev32_t d32;
6950Sstevel@tonic-gate 
6960Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
6970Sstevel@tonic-gate 	error = pc_getfat(fsp);
6980Sstevel@tonic-gate 	if (error)
6990Sstevel@tonic-gate 		return (error);
7000Sstevel@tonic-gate 	bzero(sp, sizeof (*sp));
7010Sstevel@tonic-gate 	sp->f_bsize = sp->f_frsize = fsp->pcfs_clsize;
7020Sstevel@tonic-gate 	sp->f_blocks = (fsblkcnt64_t)fsp->pcfs_ncluster;
7030Sstevel@tonic-gate 	sp->f_bavail = sp->f_bfree = (fsblkcnt64_t)pc_freeclusters(fsp);
7040Sstevel@tonic-gate 	sp->f_files = (fsfilcnt64_t)-1;
7050Sstevel@tonic-gate 	sp->f_ffree = (fsfilcnt64_t)-1;
7060Sstevel@tonic-gate 	sp->f_favail = (fsfilcnt64_t)-1;
7070Sstevel@tonic-gate #ifdef notdef
7080Sstevel@tonic-gate 	(void) cmpldev(&d32, fsp->pcfs_devvp->v_rdev);
7090Sstevel@tonic-gate #endif /* notdef */
7100Sstevel@tonic-gate 	(void) cmpldev(&d32, vfsp->vfs_dev);
7110Sstevel@tonic-gate 	sp->f_fsid = d32;
7120Sstevel@tonic-gate 	(void) strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name);
7130Sstevel@tonic-gate 	sp->f_flag = vf_to_stf(vfsp->vfs_flag);
7140Sstevel@tonic-gate 	sp->f_namemax = PCFNAMESIZE;
7150Sstevel@tonic-gate 	return (0);
7160Sstevel@tonic-gate }
7170Sstevel@tonic-gate 
7180Sstevel@tonic-gate static int
7190Sstevel@tonic-gate pc_syncfsnodes(struct pcfs *fsp)
7200Sstevel@tonic-gate {
7210Sstevel@tonic-gate 	struct pchead *hp;
7220Sstevel@tonic-gate 	struct pcnode *pcp;
7230Sstevel@tonic-gate 	int error;
7240Sstevel@tonic-gate 
7250Sstevel@tonic-gate 	PC_DPRINTF0(7, "pcfs_syncfsnodes\n");
7260Sstevel@tonic-gate 	if (error = pc_lockfs(fsp, 0, 0))
7270Sstevel@tonic-gate 		return (error);
7280Sstevel@tonic-gate 
7290Sstevel@tonic-gate 	if (!(error = pc_syncfat(fsp))) {
7300Sstevel@tonic-gate 		hp = pcfhead;
7310Sstevel@tonic-gate 		while (hp < & pcfhead [ NPCHASH ]) {
7320Sstevel@tonic-gate 			rw_enter(&pcnodes_lock, RW_READER);
7330Sstevel@tonic-gate 			pcp = hp->pch_forw;
7340Sstevel@tonic-gate 			while (pcp != (struct pcnode *)hp) {
7350Sstevel@tonic-gate 				if (VFSTOPCFS(PCTOV(pcp) -> v_vfsp) == fsp)
7360Sstevel@tonic-gate 					if (error = pc_nodesync(pcp))
7370Sstevel@tonic-gate 						break;
7380Sstevel@tonic-gate 				pcp = pcp -> pc_forw;
7390Sstevel@tonic-gate 			}
7400Sstevel@tonic-gate 			rw_exit(&pcnodes_lock);
7410Sstevel@tonic-gate 			if (error)
7420Sstevel@tonic-gate 				break;
7430Sstevel@tonic-gate 			hp++;
7440Sstevel@tonic-gate 		}
7450Sstevel@tonic-gate 	}
7460Sstevel@tonic-gate 	pc_unlockfs(fsp);
7470Sstevel@tonic-gate 	return (error);
7480Sstevel@tonic-gate }
7490Sstevel@tonic-gate 
7500Sstevel@tonic-gate /*
7510Sstevel@tonic-gate  * Flush any pending I/O.
7520Sstevel@tonic-gate  */
7530Sstevel@tonic-gate /*ARGSUSED*/
7540Sstevel@tonic-gate static int
7550Sstevel@tonic-gate pcfs_sync(
7560Sstevel@tonic-gate 	struct vfs *vfsp,
7570Sstevel@tonic-gate 	short flag,
7580Sstevel@tonic-gate 	struct cred *cr)
7590Sstevel@tonic-gate {
7600Sstevel@tonic-gate 	struct pcfs *fsp;
7610Sstevel@tonic-gate 	int error = 0;
7620Sstevel@tonic-gate 
7630Sstevel@tonic-gate 	/* this prevents the filesystem from being umounted. */
7640Sstevel@tonic-gate 	mutex_enter(&pcfslock);
7650Sstevel@tonic-gate 	if (vfsp != NULL) {
7660Sstevel@tonic-gate 		fsp = VFSTOPCFS(vfsp);
7670Sstevel@tonic-gate 		if (!(fsp->pcfs_flags & PCFS_IRRECOV)) {
7680Sstevel@tonic-gate 			error = pc_syncfsnodes(fsp);
7690Sstevel@tonic-gate 		} else {
7700Sstevel@tonic-gate 			rw_enter(&pcnodes_lock, RW_WRITER);
7710Sstevel@tonic-gate 			pc_diskchanged(fsp);
7720Sstevel@tonic-gate 			rw_exit(&pcnodes_lock);
7730Sstevel@tonic-gate 			error = EIO;
7740Sstevel@tonic-gate 		}
7750Sstevel@tonic-gate 	} else {
7760Sstevel@tonic-gate 		fsp = pc_mounttab;
7770Sstevel@tonic-gate 		while (fsp != NULL) {
7780Sstevel@tonic-gate 			if (fsp->pcfs_flags & PCFS_IRRECOV) {
7790Sstevel@tonic-gate 				rw_enter(&pcnodes_lock, RW_WRITER);
7800Sstevel@tonic-gate 				pc_diskchanged(fsp);
7810Sstevel@tonic-gate 				rw_exit(&pcnodes_lock);
7820Sstevel@tonic-gate 				error = EIO;
7830Sstevel@tonic-gate 				break;
7840Sstevel@tonic-gate 			}
7850Sstevel@tonic-gate 			error = pc_syncfsnodes(fsp);
7860Sstevel@tonic-gate 			if (error) break;
7870Sstevel@tonic-gate 			fsp = fsp->pcfs_nxt;
7880Sstevel@tonic-gate 		}
7890Sstevel@tonic-gate 	}
7900Sstevel@tonic-gate 	mutex_exit(&pcfslock);
7910Sstevel@tonic-gate 	return (error);
7920Sstevel@tonic-gate }
7930Sstevel@tonic-gate 
7940Sstevel@tonic-gate int
7950Sstevel@tonic-gate pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing)
7960Sstevel@tonic-gate {
7972720Sfrankho 	int err;
7982720Sfrankho 
7990Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_IRRECOV) && !releasing)
8000Sstevel@tonic-gate 		return (EIO);
8010Sstevel@tonic-gate 
8020Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_LOCKED) && (fsp->pcfs_owner == curthread)) {
8030Sstevel@tonic-gate 		fsp->pcfs_count++;
8040Sstevel@tonic-gate 	} else {
8050Sstevel@tonic-gate 		mutex_enter(&fsp->pcfs_lock);
8060Sstevel@tonic-gate 		if (fsp->pcfs_flags & PCFS_LOCKED)
8070Sstevel@tonic-gate 			panic("pc_lockfs");
8080Sstevel@tonic-gate 		/*
8090Sstevel@tonic-gate 		 * We check the IRRECOV bit again just in case somebody
8100Sstevel@tonic-gate 		 * snuck past the initial check but then got held up before
8110Sstevel@tonic-gate 		 * they could grab the lock.  (And in the meantime someone
8120Sstevel@tonic-gate 		 * had grabbed the lock and set the bit)
8130Sstevel@tonic-gate 		 */
814607Swyllys 		if (!diskchanged && !(fsp->pcfs_flags & PCFS_IRRECOV)) {
8152720Sfrankho 			if ((err = pc_getfat(fsp))) {
8162720Sfrankho 				mutex_exit(&fsp->pcfs_lock);
817607Swyllys 				return (err);
8182720Sfrankho 			}
819607Swyllys 		}
8200Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_LOCKED;
8210Sstevel@tonic-gate 		fsp->pcfs_owner = curthread;
8220Sstevel@tonic-gate 		fsp->pcfs_count++;
8230Sstevel@tonic-gate 	}
8240Sstevel@tonic-gate 	return (0);
8250Sstevel@tonic-gate }
8260Sstevel@tonic-gate 
8270Sstevel@tonic-gate void
8280Sstevel@tonic-gate pc_unlockfs(struct pcfs *fsp)
8290Sstevel@tonic-gate {
8300Sstevel@tonic-gate 
8310Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_LOCKED) == 0)
8320Sstevel@tonic-gate 		panic("pc_unlockfs");
8330Sstevel@tonic-gate 	if (--fsp->pcfs_count < 0)
8340Sstevel@tonic-gate 		panic("pc_unlockfs: count");
8350Sstevel@tonic-gate 	if (fsp->pcfs_count == 0) {
8360Sstevel@tonic-gate 		fsp->pcfs_flags &= ~PCFS_LOCKED;
8370Sstevel@tonic-gate 		fsp->pcfs_owner = 0;
8380Sstevel@tonic-gate 		mutex_exit(&fsp->pcfs_lock);
8390Sstevel@tonic-gate 	}
8400Sstevel@tonic-gate }
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate /*
8430Sstevel@tonic-gate  * isDosDrive()
8440Sstevel@tonic-gate  *	Boolean function.  Give it the systid field for an fdisk partition
8450Sstevel@tonic-gate  *	and it decides if that's a systid that describes a DOS drive.  We
8460Sstevel@tonic-gate  *	use systid values defined in sys/dktp/fdisk.h.
8470Sstevel@tonic-gate  */
8480Sstevel@tonic-gate static int
8490Sstevel@tonic-gate isDosDrive(uchar_t checkMe)
8500Sstevel@tonic-gate {
8510Sstevel@tonic-gate 	return ((checkMe == DOSOS12) || (checkMe == DOSOS16) ||
8520Sstevel@tonic-gate 	    (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) ||
8530Sstevel@tonic-gate 	    (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) ||
8540Sstevel@tonic-gate 	    (checkMe == DIAGPART));
8550Sstevel@tonic-gate }
8560Sstevel@tonic-gate 
8570Sstevel@tonic-gate /*
8580Sstevel@tonic-gate  * isDosExtended()
8590Sstevel@tonic-gate  *	Boolean function.  Give it the systid field for an fdisk partition
8600Sstevel@tonic-gate  *	and it decides if that's a systid that describes an extended DOS
8610Sstevel@tonic-gate  *	partition.
8620Sstevel@tonic-gate  */
8630Sstevel@tonic-gate static int
8640Sstevel@tonic-gate isDosExtended(uchar_t checkMe)
8650Sstevel@tonic-gate {
8660Sstevel@tonic-gate 	return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA));
8670Sstevel@tonic-gate }
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate /*
8700Sstevel@tonic-gate  * isBootPart()
8710Sstevel@tonic-gate  *	Boolean function.  Give it the systid field for an fdisk partition
8720Sstevel@tonic-gate  *	and it decides if that's a systid that describes a Solaris boot
8730Sstevel@tonic-gate  *	partition.
8740Sstevel@tonic-gate  */
8750Sstevel@tonic-gate static int
8760Sstevel@tonic-gate isBootPart(uchar_t checkMe)
8770Sstevel@tonic-gate {
8780Sstevel@tonic-gate 	return (checkMe == X86BOOT);
8790Sstevel@tonic-gate }
8800Sstevel@tonic-gate 
8810Sstevel@tonic-gate /*
8820Sstevel@tonic-gate  * noLogicalDrive()
8830Sstevel@tonic-gate  *	Display error message about not being able to find a logical
8840Sstevel@tonic-gate  *	drive.
8850Sstevel@tonic-gate  */
8860Sstevel@tonic-gate static void
8870Sstevel@tonic-gate noLogicalDrive(int requested)
8880Sstevel@tonic-gate {
8890Sstevel@tonic-gate 	if (requested == BOOT_PARTITION_DRIVE) {
8900Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: no boot partition");
8910Sstevel@tonic-gate 	} else {
8920Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: no such logical drive");
8930Sstevel@tonic-gate 	}
8940Sstevel@tonic-gate }
8950Sstevel@tonic-gate 
8960Sstevel@tonic-gate /*
8970Sstevel@tonic-gate  * findTheDrive()
8980Sstevel@tonic-gate  *	Discover offset of the requested logical drive, and return
8990Sstevel@tonic-gate  *	that offset (startSector), the systid of that drive (sysid),
9000Sstevel@tonic-gate  *	and a buffer pointer (bp), with the buffer contents being
9010Sstevel@tonic-gate  *	the first sector of the logical drive (i.e., the sector that
9020Sstevel@tonic-gate  *	contains the BPB for that drive).
9030Sstevel@tonic-gate  */
9040Sstevel@tonic-gate static int
905*2972Sfrankho findTheDrive(dev_t dev, int ldrive, buf_t **bp,
9060Sstevel@tonic-gate     daddr_t *startSector, uchar_t *sysid)
9070Sstevel@tonic-gate {
9080Sstevel@tonic-gate 	struct ipart dosp[FD_NUMPART];	/* incore fdisk partition structure */
9090Sstevel@tonic-gate 	struct mboot *dosp_ptr;		/* boot structure pointer */
9100Sstevel@tonic-gate 	daddr_t lastseek = 0;		/* Disk block we sought previously */
9110Sstevel@tonic-gate 	daddr_t diskblk = 0;		/* Disk block to get */
9120Sstevel@tonic-gate 	daddr_t xstartsect;		/* base of Extended DOS partition */
9130Sstevel@tonic-gate 	int logicalDriveCount = 0;	/* Count of logical drives seen */
9140Sstevel@tonic-gate 	int extendedPart = -1;		/* index of extended dos partition */
9150Sstevel@tonic-gate 	int primaryPart = -1;		/* index of primary dos partition */
9160Sstevel@tonic-gate 	int bootPart = -1;		/* index of a Solaris boot partition */
9170Sstevel@tonic-gate 	int xnumsect = -1;		/* length of extended DOS partition */
9180Sstevel@tonic-gate 	int driveIndex;			/* computed FDISK table index */
9190Sstevel@tonic-gate 	int i;
9200Sstevel@tonic-gate 	/*
9210Sstevel@tonic-gate 	 * Count of drives in the current extended partition's
9220Sstevel@tonic-gate 	 * FDISK table, and indexes of the drives themselves.
9230Sstevel@tonic-gate 	 */
9240Sstevel@tonic-gate 	int extndDrives[FD_NUMPART];
9250Sstevel@tonic-gate 	int numDrives = 0;
9260Sstevel@tonic-gate 
9270Sstevel@tonic-gate 	/*
9280Sstevel@tonic-gate 	 * Count of drives (beyond primary) in master boot record's
9290Sstevel@tonic-gate 	 * FDISK table, and indexes of the drives themselves.
9300Sstevel@tonic-gate 	 */
9310Sstevel@tonic-gate 	int extraDrives[FD_NUMPART];
9320Sstevel@tonic-gate 	int numExtraDrives = 0;
9330Sstevel@tonic-gate 
9340Sstevel@tonic-gate 	/*
935*2972Sfrankho 	 * "ldrive == 0" should never happen, as this is a request to
936*2972Sfrankho 	 * mount the physical device (and ignore partitioning). The code
937*2972Sfrankho 	 * in pcfs_mount() should have made sure that a logical drive number
938*2972Sfrankho 	 * is at least 1, meaning we're looking for drive "C:". It is not
939*2972Sfrankho 	 * safe (and a bug in the callers of this function) to request logical
940*2972Sfrankho 	 * drive number 0; we could ASSERT() but a graceful EIO is a more
941*2972Sfrankho 	 * polite way.
942*2972Sfrankho 	 */
943*2972Sfrankho 	if (ldrive == 0) {
944*2972Sfrankho 		cmn_err(CE_NOTE, "!pcfs: request for logical partition zero");
945*2972Sfrankho 		noLogicalDrive(ldrive);
946*2972Sfrankho 		return (EIO);
947*2972Sfrankho 	}
948*2972Sfrankho 
949*2972Sfrankho 	/*
9500Sstevel@tonic-gate 	 *  Copy from disk block into memory aligned structure for fdisk usage.
9510Sstevel@tonic-gate 	 */
9520Sstevel@tonic-gate 	dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr;
9530Sstevel@tonic-gate 	bcopy(dosp_ptr->parts, dosp, sizeof (struct ipart) * FD_NUMPART);
9540Sstevel@tonic-gate 
955*2972Sfrankho 	if (ltohs(dosp_ptr->signature) != MBB_MAGIC) {
956*2972Sfrankho 		cmn_err(CE_NOTE, "!pcfs: MBR partition table signature err");
957*2972Sfrankho 		return (EINVAL);
958*2972Sfrankho 	}
959*2972Sfrankho 
9600Sstevel@tonic-gate 	/*
9610Sstevel@tonic-gate 	 * Get a summary of what is in the Master FDISK table.
9620Sstevel@tonic-gate 	 * Normally we expect to find one partition marked as a DOS drive.
9630Sstevel@tonic-gate 	 * This partition is the one Windows calls the primary dos partition.
9640Sstevel@tonic-gate 	 * If the machine has any logical drives then we also expect
9650Sstevel@tonic-gate 	 * to find a partition marked as an extended DOS partition.
9660Sstevel@tonic-gate 	 *
9670Sstevel@tonic-gate 	 * Sometimes we'll find multiple partitions marked as DOS drives.
9680Sstevel@tonic-gate 	 * The Solaris fdisk program allows these partitions
9690Sstevel@tonic-gate 	 * to be created, but Windows fdisk no longer does.  We still need
9700Sstevel@tonic-gate 	 * to support these, though, since Windows does.  We also need to fix
9710Sstevel@tonic-gate 	 * our fdisk to behave like the Windows version.
9720Sstevel@tonic-gate 	 *
9730Sstevel@tonic-gate 	 * It turns out that some off-the-shelf media have *only* an
9740Sstevel@tonic-gate 	 * Extended partition, so we need to deal with that case as well.
9750Sstevel@tonic-gate 	 *
9760Sstevel@tonic-gate 	 * Only a single (the first) Extended or Boot Partition will
9770Sstevel@tonic-gate 	 * be recognized.  Any others will be ignored.
9780Sstevel@tonic-gate 	 */
9790Sstevel@tonic-gate 	for (i = 0; i < FD_NUMPART; i++) {
980607Swyllys 		PC_DPRINTF1(2, "findTheDrive: found partition type %02x",
981607Swyllys 			dosp[i].systid);
982607Swyllys 
9830Sstevel@tonic-gate 		if (isDosDrive(dosp[i].systid)) {
9840Sstevel@tonic-gate 			if (primaryPart < 0) {
9850Sstevel@tonic-gate 				logicalDriveCount++;
9860Sstevel@tonic-gate 				primaryPart = i;
9870Sstevel@tonic-gate 			} else {
9880Sstevel@tonic-gate 				extraDrives[numExtraDrives++] = i;
9890Sstevel@tonic-gate 			}
9900Sstevel@tonic-gate 			continue;
9910Sstevel@tonic-gate 		}
9920Sstevel@tonic-gate 		if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) {
9930Sstevel@tonic-gate 			extendedPart = i;
9940Sstevel@tonic-gate 			continue;
9950Sstevel@tonic-gate 		}
9960Sstevel@tonic-gate 		if ((bootPart < 0) && isBootPart(dosp[i].systid)) {
9970Sstevel@tonic-gate 			bootPart = i;
9980Sstevel@tonic-gate 			continue;
9990Sstevel@tonic-gate 		}
10000Sstevel@tonic-gate 	}
10010Sstevel@tonic-gate 
1002*2972Sfrankho 	if (ldrive == BOOT_PARTITION_DRIVE) {
10030Sstevel@tonic-gate 		if (bootPart < 0) {
1004*2972Sfrankho 			noLogicalDrive(ldrive);
1005*2972Sfrankho 			return (EINVAL);
10060Sstevel@tonic-gate 		}
10070Sstevel@tonic-gate 		*sysid = dosp[bootPart].systid;
10080Sstevel@tonic-gate 		*startSector = ltohi(dosp[bootPart].relsect);
1009*2972Sfrankho 		return (0);
10100Sstevel@tonic-gate 	}
10110Sstevel@tonic-gate 
1012*2972Sfrankho 	if (ldrive == PRIMARY_DOS_DRIVE && primaryPart >= 0) {
10130Sstevel@tonic-gate 		*sysid = dosp[primaryPart].systid;
10140Sstevel@tonic-gate 		*startSector = ltohi(dosp[primaryPart].relsect);
1015*2972Sfrankho 		return (0);
10160Sstevel@tonic-gate 	}
10170Sstevel@tonic-gate 
10180Sstevel@tonic-gate 	/*
10190Sstevel@tonic-gate 	 * We are not looking for the C: drive (or the primary drive
10200Sstevel@tonic-gate 	 * was not found), so we had better have an extended partition
10210Sstevel@tonic-gate 	 * or extra drives in the Master FDISK table.
10220Sstevel@tonic-gate 	 */
10230Sstevel@tonic-gate 	if ((extendedPart < 0) && (numExtraDrives == 0)) {
10240Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: no extended dos partition");
1025*2972Sfrankho 		noLogicalDrive(ldrive);
1026*2972Sfrankho 		return (EINVAL);
10270Sstevel@tonic-gate 	}
10280Sstevel@tonic-gate 
10290Sstevel@tonic-gate 	if (extendedPart >= 0) {
10300Sstevel@tonic-gate 		diskblk = xstartsect = ltohi(dosp[extendedPart].relsect);
10310Sstevel@tonic-gate 		xnumsect = ltohi(dosp[extendedPart].numsect);
10320Sstevel@tonic-gate 		do {
10330Sstevel@tonic-gate 			/*
10340Sstevel@tonic-gate 			 *  If the seek would not cause us to change
10350Sstevel@tonic-gate 			 *  position on the drive, then we're out of
10360Sstevel@tonic-gate 			 *  extended partitions to examine.
10370Sstevel@tonic-gate 			 */
10380Sstevel@tonic-gate 			if (diskblk == lastseek)
10390Sstevel@tonic-gate 				break;
10400Sstevel@tonic-gate 			logicalDriveCount += numDrives;
10410Sstevel@tonic-gate 			/*
10420Sstevel@tonic-gate 			 *  Seek the next extended partition, and find
10430Sstevel@tonic-gate 			 *  logical drives within it.
10440Sstevel@tonic-gate 			 */
10450Sstevel@tonic-gate 			brelse(*bp);
10460Sstevel@tonic-gate 			*bp = bread(dev, diskblk, PC_SAFESECSIZE);
10470Sstevel@tonic-gate 			if ((*bp)->b_flags & B_ERROR) {
10480Sstevel@tonic-gate 				PC_DPRINTF0(1, "pc_getfattype: read error\n");
1049*2972Sfrankho 				return (EIO);
10500Sstevel@tonic-gate 			}
10510Sstevel@tonic-gate 			lastseek = diskblk;
10520Sstevel@tonic-gate 			dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr;
10530Sstevel@tonic-gate 			if (ltohs(dosp_ptr->signature) != MBB_MAGIC) {
10540Sstevel@tonic-gate 				cmn_err(CE_NOTE, "!pcfs: "
10550Sstevel@tonic-gate 				    "extended partition signature err");
1056*2972Sfrankho 				return (EINVAL);
10570Sstevel@tonic-gate 			}
10580Sstevel@tonic-gate 			bcopy(dosp_ptr->parts, dosp,
10590Sstevel@tonic-gate 			    sizeof (struct ipart) * FD_NUMPART);
10600Sstevel@tonic-gate 			/*
10610Sstevel@tonic-gate 			 *  Count up drives, and track where the next
10620Sstevel@tonic-gate 			 *  extended partition is in case we need it.  We
10630Sstevel@tonic-gate 			 *  are expecting only one extended partition.  If
10640Sstevel@tonic-gate 			 *  there is more than one we'll only go to the
10650Sstevel@tonic-gate 			 *  first one we see, but warn about ignoring.
10660Sstevel@tonic-gate 			 */
10670Sstevel@tonic-gate 			numDrives = 0;
10680Sstevel@tonic-gate 			for (i = 0; i < FD_NUMPART; i++) {
10690Sstevel@tonic-gate 				if (isDosDrive(dosp[i].systid)) {
10700Sstevel@tonic-gate 					extndDrives[numDrives++] = i;
10710Sstevel@tonic-gate 				} else if (isDosExtended(dosp[i].systid)) {
10720Sstevel@tonic-gate 					if (diskblk != lastseek) {
10730Sstevel@tonic-gate 						/*
10740Sstevel@tonic-gate 						 * Already found an extended
10750Sstevel@tonic-gate 						 * partition in this table.
10760Sstevel@tonic-gate 						 */
10770Sstevel@tonic-gate 						cmn_err(CE_NOTE,
10780Sstevel@tonic-gate 						    "!pcfs: ignoring unexpected"
10790Sstevel@tonic-gate 						    " additional extended"
10800Sstevel@tonic-gate 						    " partition");
1081*2972Sfrankho 					} else {
1082*2972Sfrankho 						diskblk = xstartsect +
1083*2972Sfrankho 						    ltohi(dosp[i].relsect);
10840Sstevel@tonic-gate 					}
10850Sstevel@tonic-gate 				}
10860Sstevel@tonic-gate 			}
1087*2972Sfrankho 		} while (ldrive > logicalDriveCount + numDrives);
10880Sstevel@tonic-gate 
1089*2972Sfrankho 		if (ldrive <= logicalDriveCount + numDrives) {
10900Sstevel@tonic-gate 			/*
10910Sstevel@tonic-gate 			 * The number of logical drives we've found thus
10920Sstevel@tonic-gate 			 * far is enough to get us to the one we were
10930Sstevel@tonic-gate 			 * searching for.
10940Sstevel@tonic-gate 			 */
1095*2972Sfrankho 			driveIndex = logicalDriveCount + numDrives - ldrive;
10960Sstevel@tonic-gate 			*sysid = dosp[extndDrives[driveIndex]].systid;
10970Sstevel@tonic-gate 			*startSector =
10980Sstevel@tonic-gate 			    ltohi(dosp[extndDrives[driveIndex]].relsect) +
10990Sstevel@tonic-gate 			    lastseek;
11000Sstevel@tonic-gate 			if (*startSector > (xstartsect + xnumsect)) {
11010Sstevel@tonic-gate 				cmn_err(CE_NOTE, "!pcfs: extended partition "
11020Sstevel@tonic-gate 				    "values bad");
1103*2972Sfrankho 				return (EINVAL);
11040Sstevel@tonic-gate 			}
1105*2972Sfrankho 			return (0);
11060Sstevel@tonic-gate 		} else {
11070Sstevel@tonic-gate 			/*
11080Sstevel@tonic-gate 			 * We ran out of extended dos partition
11090Sstevel@tonic-gate 			 * drives.  The only hope now is to go
11100Sstevel@tonic-gate 			 * back to extra drives defined in the master
11110Sstevel@tonic-gate 			 * fdisk table.  But we overwrote that table
11120Sstevel@tonic-gate 			 * already, so we must load it in again.
11130Sstevel@tonic-gate 			 */
11140Sstevel@tonic-gate 			logicalDriveCount += numDrives;
11150Sstevel@tonic-gate 			brelse(*bp);
11160Sstevel@tonic-gate 			*bp = bread(dev, (daddr_t)0, PC_SAFESECSIZE);
11170Sstevel@tonic-gate 			if ((*bp)->b_flags & B_ERROR) {
11180Sstevel@tonic-gate 				PC_DPRINTF0(1, "pc_getfattype: read error\n");
1119*2972Sfrankho 				return (EIO);
11200Sstevel@tonic-gate 			}
11210Sstevel@tonic-gate 			dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr;
11220Sstevel@tonic-gate 			bcopy(dosp_ptr->parts, dosp,
11230Sstevel@tonic-gate 			    sizeof (struct ipart) * FD_NUMPART);
11240Sstevel@tonic-gate 		}
11250Sstevel@tonic-gate 	}
11260Sstevel@tonic-gate 	/*
11270Sstevel@tonic-gate 	 *  Still haven't found the drive, is it an extra
11280Sstevel@tonic-gate 	 *  drive defined in the main FDISK table?
11290Sstevel@tonic-gate 	 */
1130*2972Sfrankho 	if (ldrive <= logicalDriveCount + numExtraDrives) {
1131*2972Sfrankho 		driveIndex = logicalDriveCount + numExtraDrives - ldrive;
1132*2972Sfrankho 		ASSERT(driveIndex < MIN(numExtraDrives, FD_NUMPART));
11330Sstevel@tonic-gate 		*sysid = dosp[extraDrives[driveIndex]].systid;
11340Sstevel@tonic-gate 		*startSector = ltohi(dosp[extraDrives[driveIndex]].relsect);
1135*2972Sfrankho 		return (0);
11360Sstevel@tonic-gate 	}
11370Sstevel@tonic-gate 	/*
11380Sstevel@tonic-gate 	 *  Still haven't found the drive, and there is
11390Sstevel@tonic-gate 	 *  nowhere else to look.
11400Sstevel@tonic-gate 	 */
1141*2972Sfrankho 	noLogicalDrive(ldrive);
1142*2972Sfrankho 	return (EINVAL);
11430Sstevel@tonic-gate }
11440Sstevel@tonic-gate 
11450Sstevel@tonic-gate /*
1146607Swyllys  * FAT12/FAT16 specific consistency checks.
1147607Swyllys  */
1148607Swyllys static int
1149607Swyllys check_bpb_fat16(struct bootsec *bpb)
1150607Swyllys {
1151607Swyllys 	if (pcfsdebuglevel >= 5) {
1152607Swyllys 		PC_DPRINTF1(5, "check_bpb_fat: RootEntCount = %d",
1153607Swyllys 			ltohs(bpb->rdirents[0]));
1154607Swyllys 		PC_DPRINTF1(5, "check_bpb_fat16: TotSec16 = %d",
1155607Swyllys 			ltohs(bpb->numsect[0]));
1156607Swyllys 		PC_DPRINTF1(5, "check_bpb_fat16: FATSz16 = %d",
1157607Swyllys 			ltohs(bpb->fatsec));
1158607Swyllys 		PC_DPRINTF1(5, "check_bpb_fat16: TotSec32 = %d",
1159607Swyllys 			ltohi(bpb->totalsec));
1160607Swyllys 	}
1161607Swyllys 	return (ltohs(bpb->rdirents[0]) > 0 &&	 /* RootEntCnt > 0 */
1162607Swyllys 		((ltohs(bpb->numsect[0]) == 0 && /* TotSec16 == 0 */
1163607Swyllys 		ltohi(bpb->totalsec) > 0) ||	 /* TotSec32 > 0 */
1164607Swyllys 		ltohs(bpb->numsect[0]) > 0) && /* TotSec16 > 0 */
1165607Swyllys 		ltohs(bpb->fatsec) > 0);	 /* FatSz16 > 0 */
1166607Swyllys }
1167607Swyllys 
1168607Swyllys /*
1169607Swyllys  * FAT32 specific consistency checks.
1170607Swyllys  */
1171607Swyllys static int
1172607Swyllys check_bpb_fat32(struct fat32_bootsec *bpb)
1173607Swyllys {
1174607Swyllys 	if (pcfsdebuglevel >= 5) {
1175607Swyllys 		PC_DPRINTF1(5, "check_bpb_fat32: RootEntCount = %d",
1176607Swyllys 			ltohs(bpb->f_bs.rdirents[0]));
1177607Swyllys 		PC_DPRINTF1(5, "check_bpb_fat32: TotSec16 = %d",
1178607Swyllys 			ltohs(bpb->f_bs.numsect[0]));
1179607Swyllys 		PC_DPRINTF1(5, "check_bpb_fat32: FATSz16 = %d",
1180607Swyllys 			ltohs(bpb->f_bs.fatsec));
1181607Swyllys 		PC_DPRINTF1(5, "check_bpb_fat32: TotSec32 = %d",
1182607Swyllys 			ltohi(bpb->f_bs.totalsec));
1183607Swyllys 		PC_DPRINTF1(5, "check_bpb_fat32: FATSz32 = %d",
1184607Swyllys 			ltohi(bpb->f_fatlength));
1185607Swyllys 	}
1186607Swyllys 	return (ltohs(bpb->f_bs.rdirents[0]) == 0 &&
1187607Swyllys 		ltohs(bpb->f_bs.numsect[0]) == 0 &&
1188607Swyllys 		ltohs(bpb->f_bs.fatsec) == 0 &&
1189607Swyllys 		ltohi(bpb->f_bs.totalsec) > 0 &&
1190607Swyllys 		ltohi(bpb->f_fatlength) > 0);
1191607Swyllys }
1192607Swyllys 
1193607Swyllys /*
1194607Swyllys  * Calculate the number of clusters in order to determine
1195607Swyllys  * the type of FAT we are looking at.  This is the only
1196607Swyllys  * recommended way of determining FAT type, though there
1197607Swyllys  * are other hints in the data, this is the best way.
1198607Swyllys  */
1199607Swyllys static ulong_t
1200607Swyllys bpb_to_numclusters(uchar_t *cp)
1201607Swyllys {
1202607Swyllys 	struct fat32_bootsec *bpb;
1203607Swyllys 
1204607Swyllys 	ulong_t rootdirsectors;
1205607Swyllys 	ulong_t FATsz;
1206607Swyllys 	ulong_t TotSec;
1207607Swyllys 	ulong_t DataSec;
1208607Swyllys 	ulong_t CountOfClusters;
1209607Swyllys 	char FileSysType[9];
1210607Swyllys 
1211607Swyllys 	/*
1212607Swyllys 	 * Cast it to FAT32 bpb. If it turns out to be FAT12/16, its
1213607Swyllys 	 * OK, we won't try accessing the data beyond the FAT16 header
1214607Swyllys 	 * boundary.
1215607Swyllys 	 */
1216607Swyllys 	bpb = (struct fat32_bootsec *)cp;
1217607Swyllys 
1218607Swyllys 	if (pcfsdebuglevel >= 5) {
1219607Swyllys 		if (ltohs(bpb->f_bs.rdirents[0]) != 0) {
1220608Swyllys 			(void) memcpy(FileSysType, &cp[54], 8);
1221607Swyllys 			FileSysType[8] = 0;
1222607Swyllys 			PC_DPRINTF1(5, "debug_bpb: FAT12/FAT16 FileSysType = "
1223607Swyllys 				"%s", FileSysType);
1224607Swyllys 		}
1225607Swyllys 	}
1226607Swyllys 
1227607Swyllys 	rootdirsectors = ((ltohs(bpb->f_bs.rdirents[0]) * 32) +
1228607Swyllys 		(ltohs(bpb->f_bs.bps[0]) - 1)) / ltohs(bpb->f_bs.bps[0]);
1229607Swyllys 
1230607Swyllys 	if (ltohs(bpb->f_bs.fatsec) != 0)
1231607Swyllys 		FATsz = ltohs(bpb->f_bs.fatsec);
1232607Swyllys 	else
1233607Swyllys 		FATsz = ltohi(bpb->f_fatlength);
1234607Swyllys 
1235607Swyllys 	if (ltohs(bpb->f_bs.numsect[0]) != 0)
1236607Swyllys 		TotSec = ltohs(bpb->f_bs.numsect[0]);
1237607Swyllys 	else
1238607Swyllys 		TotSec = ltohi(bpb->f_bs.totalsec);
1239607Swyllys 
1240607Swyllys 	DataSec = TotSec - (ltohs(bpb->f_bs.res_sec[0]) +
1241607Swyllys 			(bpb->f_bs.nfat * FATsz) + rootdirsectors);
1242607Swyllys 
1243607Swyllys 	CountOfClusters = DataSec / bpb->f_bs.spcl;
1244607Swyllys 
1245607Swyllys 	PC_DPRINTF1(5, "debug_bpb: CountOfClusters = %ld", CountOfClusters);
1246607Swyllys 
1247607Swyllys 	return (CountOfClusters);
1248607Swyllys 
1249607Swyllys }
1250607Swyllys 
1251607Swyllys static int
1252607Swyllys fattype(ulong_t CountOfClusters)
1253607Swyllys {
1254607Swyllys 	/*
1255607Swyllys 	 * From Microsoft:
1256607Swyllys 	 * In the following example, when it says <, it does not mean <=.
1257607Swyllys 	 * Note also that the numbers are correct.  The first number for
1258607Swyllys 	 * FAT12 is 4085; the second number for FAT16 is 65525. These numbers
1259607Swyllys 	 * and the '<' signs are not wrong.
1260607Swyllys 	 */
1261607Swyllys 
1262607Swyllys 	/* Watch for edge cases */
1263607Swyllys 	if ((CountOfClusters >= 4085 && CountOfClusters <= 4095) ||
1264607Swyllys 	    (CountOfClusters >= 65525 && CountOfClusters <= 65535)) {
1265607Swyllys 		PC_DPRINTF1(5, "debug_bpb: Cannot determine FAT yet - %ld",
1266607Swyllys 			CountOfClusters);
1267607Swyllys 		return (-1); /* Cannot be determined yet */
1268607Swyllys 	} else if (CountOfClusters < 4085) {
1269607Swyllys 		/* Volume is FAT12 */
1270607Swyllys 		PC_DPRINTF0(5, "debug_bpb: This must be FAT12");
1271607Swyllys 		return (0);
1272607Swyllys 	} else if (CountOfClusters < 65525) {
1273607Swyllys 		/* Volume is FAT16 */
1274607Swyllys 		PC_DPRINTF0(5, "debug_bpb: This must be FAT16");
1275607Swyllys 		return (PCFS_FAT16);
1276607Swyllys 	} else {
1277607Swyllys 		/* Volume is FAT32 */
1278607Swyllys 		PC_DPRINTF0(5, "debug_bpb: This must be FAT32");
1279607Swyllys 		return (PCFS_FAT32);
1280607Swyllys 	}
1281607Swyllys }
1282607Swyllys 
1283607Swyllys #define	VALID_SECSIZE(s) (s == 512 || s == 1024 || s == 2048 || s == 4096)
1284607Swyllys 
1285607Swyllys #define	VALID_SPCL(s) (s == 1 || s == 2 || s == 4 || s == 8 || s == 16 ||\
1286607Swyllys 	s == 32 || s == 64 || s == 128)
1287607Swyllys 
1288607Swyllys static int
1289607Swyllys secondaryBPBChecks(uchar_t *cp)
1290607Swyllys {
1291607Swyllys 	struct bootsec *bpb = (struct bootsec *)cp;
1292607Swyllys 	struct fat32_bootsec *f32bpb = (struct fat32_bootsec *)cp;
1293607Swyllys 
1294607Swyllys 	/*
1295607Swyllys 	 * Perform secondary checks to try and determine what sort
1296607Swyllys 	 * of FAT partition we have based on other, less reliable,
1297607Swyllys 	 * data in the BPB header.
1298607Swyllys 	 */
1299607Swyllys 	if (ltohs(bpb->fatsec) != 0) {
1300607Swyllys 		/*
1301607Swyllys 		 * Must be FAT12 or FAT16, check the
1302607Swyllys 		 * FilSysType string (not 100% reliable).
1303607Swyllys 		 */
1304607Swyllys 		if (!memcmp((cp + PCFS_TYPESTRING_OFFSET16), "FAT12", 5)) {
1305607Swyllys 			PC_DPRINTF0(5, "secondaryBPBCheck says: FAT12");
1306607Swyllys 			return (0); /* FAT12 */
1307607Swyllys 		} else if (!memcmp((cp + PCFS_TYPESTRING_OFFSET16), "FAT16",
1308607Swyllys 			5)) {
1309607Swyllys 			PC_DPRINTF0(5, "secondaryBPBCheck says: FAT16");
1310607Swyllys 			return (PCFS_FAT16);
1311607Swyllys 		} else {
1312607Swyllys 			/*
1313607Swyllys 			 * Try to use the BPB_Media byte
1314607Swyllys 			 *
1315607Swyllys 			 *  If the media byte indicates a floppy we'll
1316607Swyllys 			 *  assume FAT12, otherwise we'll assume FAT16.
1317607Swyllys 			 */
1318607Swyllys 			switch (bpb->mediadesriptor) {
1319607Swyllys 				case SS8SPT:
1320607Swyllys 				case DS8SPT:
1321607Swyllys 				case SS9SPT:
1322607Swyllys 				case DS9SPT:
1323607Swyllys 				case DS18SPT:
1324607Swyllys 				case DS9_15SPT:
1325607Swyllys 					PC_DPRINTF0(5,
1326607Swyllys 					"secondaryBPBCheck says: FAT12");
1327607Swyllys 					return (0); /* FAT12 */
1328607Swyllys 				case MD_FIXED:
1329607Swyllys 					PC_DPRINTF0(5,
1330607Swyllys 					"secondaryBPBCheck says: FAT16");
1331607Swyllys 					return (PCFS_FAT16);
1332607Swyllys 				default:
1333607Swyllys 					cmn_err(CE_NOTE,
1334607Swyllys 						"!pcfs: unknown FAT type");
1335607Swyllys 					return (-1);
1336607Swyllys 			}
1337607Swyllys 		}
1338607Swyllys 	} else if (ltohi(f32bpb->f_fatlength) > 0) {
1339607Swyllys 		PC_DPRINTF0(5, "secondaryBPBCheck says: FAT32");
1340607Swyllys 		return (PCFS_FAT32);
1341607Swyllys 	} else {
1342607Swyllys 		/* We don't know */
1343607Swyllys 		PC_DPRINTF0(5, "secondaryBPBCheck says: unknown!!");
1344607Swyllys 		return (-1);
1345607Swyllys 	}
1346607Swyllys }
1347607Swyllys 
1348607Swyllys /*
1349607Swyllys  * Check to see if the BPB we found is correct.
1350607Swyllys  *
1351607Swyllys  * First, look for obvious, tell-tale signs of trouble:
1352607Swyllys  * The NumFATs value should always be 2.  Sometimes it can be a '1'
1353607Swyllys  * on FLASH memory cards and other non-disk-based media, so we
1354607Swyllys  * will allow that as well.
1355607Swyllys  *
1356607Swyllys  * We also look at the Media byte, the valid range is 0xF0, or
1357607Swyllys  * 0xF8 thru 0xFF, anything else means this is probably not a good
1358607Swyllys  * BPB.
1359607Swyllys  *
1360607Swyllys  * Finally, check the BPB Magic number at the end of the 512 byte
1361607Swyllys  * block, it must be 0xAA55.
1362607Swyllys  *
1363607Swyllys  * If that all is good, calculate the number of clusters and
1364607Swyllys  * do some final verification steps.
1365607Swyllys  *
1366*2972Sfrankho  * If all is well, return success (1) and set the fattypep value to the
1367*2972Sfrankho  * correct FAT value if the caller provided a pointer to store it in.
1368607Swyllys  */
1369607Swyllys static int
1370607Swyllys isBPB(uchar_t *cp, int *fattypep)
1371607Swyllys {
1372607Swyllys 	struct bootsec *bpb = (struct bootsec *)cp;
1373*2972Sfrankho 	int type;
1374607Swyllys 
1375607Swyllys 	uint_t numclusters;		/* number of clusters in file area */
1376607Swyllys 	ushort_t secsize = (int)ltohs(bpb->bps[0]);
1377607Swyllys 
1378607Swyllys 	if (pcfsdebuglevel >= 3) {
1379607Swyllys 		if (!VALID_SECSIZE(secsize))
1380607Swyllys 			PC_DPRINTF1(3, "check_bpb: invalid bps value %d",
1381607Swyllys 				secsize);
1382607Swyllys 
1383607Swyllys 		if (!VALID_SPCL(bpb->spcl))
1384607Swyllys 			PC_DPRINTF1(3, "check_bpb: invalid spcl value %d",
1385607Swyllys 				bpb->spcl);
1386607Swyllys 
1387607Swyllys 		if ((secsize * bpb->spcl) >= (32 * 1024))
1388607Swyllys 			PC_DPRINTF3(3, "check_bpb: BPC > 32K  %d x %d = %d",
1389607Swyllys 				secsize,
1390607Swyllys 				bpb->spcl,
1391607Swyllys 				secsize * bpb->spcl);
1392607Swyllys 
1393607Swyllys 		if (bpb->nfat == 0)
1394607Swyllys 			PC_DPRINTF1(3, "check_bpb: bad NumFATs value %d",
1395607Swyllys 				bpb->nfat);
1396607Swyllys 
1397607Swyllys 		if (ltohs(bpb->res_sec[0]) == 0)
1398607Swyllys 			PC_DPRINTF1(3, "check_bpb: bad RsvdSecCnt value %d",
1399607Swyllys 				ltohs(bpb->res_sec[0]));
1400607Swyllys 
1401607Swyllys 		PC_DPRINTF1(5, "check_bpb: Media byte = %02x",
1402607Swyllys 			bpb->mediadesriptor);
1403607Swyllys 
1404607Swyllys 	}
1405607Swyllys 	if ((bpb->nfat == 0) ||
1406607Swyllys 		(bpb->mediadesriptor != 0xF0 && bpb->mediadesriptor < 0xF8) ||
1407607Swyllys 		(ltohs(cp[510]) != MBB_MAGIC) ||
1408607Swyllys 		!VALID_SECSIZE(secsize) ||
1409607Swyllys 		!VALID_SPCL(bpb->spcl) ||
14102720Sfrankho 		(secsize * bpb->spcl > (64 * 1024)) ||
1411607Swyllys 		!(ltohs(bpb->res_sec[0])))
1412607Swyllys 		return (0);
1413607Swyllys 
1414607Swyllys 	/*
1415607Swyllys 	 * Basic sanity checks passed so far, now try to determine which
1416607Swyllys 	 * FAT format to use.
1417607Swyllys 	 */
1418607Swyllys 	numclusters = bpb_to_numclusters(cp);
1419607Swyllys 
1420*2972Sfrankho 	type = fattype(numclusters);
1421607Swyllys 
1422607Swyllys 	/* Do some final sanity checks for each specific type of FAT */
1423*2972Sfrankho 	switch (type) {
1424607Swyllys 		case 0: /* FAT12 */
1425607Swyllys 		case PCFS_FAT16:
1426607Swyllys 			if (!check_bpb_fat16((struct bootsec *)cp))
1427607Swyllys 				return (0);
1428607Swyllys 			break;
1429607Swyllys 		case PCFS_FAT32:
1430607Swyllys 			if (!check_bpb_fat32((struct fat32_bootsec *)cp))
1431607Swyllys 				return (0);
1432607Swyllys 			break;
1433607Swyllys 		default: /* not sure yet */
1434*2972Sfrankho 			type = secondaryBPBChecks(cp);
1435*2972Sfrankho 			if (type == -1) {
1436607Swyllys 				/* Still nothing, give it up. */
1437607Swyllys 				return (0);
1438607Swyllys 			}
1439607Swyllys 			break;
1440607Swyllys 	}
1441*2972Sfrankho 
1442*2972Sfrankho 	if (fattypep)
1443*2972Sfrankho 		*fattypep = type;
1444*2972Sfrankho 
1445607Swyllys 	PC_DPRINTF0(5, "isBPB: BPB passes verification tests");
1446607Swyllys 	return (1);
1447607Swyllys }
1448607Swyllys 
1449607Swyllys 
1450607Swyllys /*
14510Sstevel@tonic-gate  * Get the FAT type for the DOS medium.
14520Sstevel@tonic-gate  *
1453607Swyllys  * -------------------------
1454607Swyllys  * According to Microsoft:
1455607Swyllys  *   The FAT type one of FAT12, FAT16, or FAT32 is determined by the
1456607Swyllys  * count of clusters on the volume and nothing else.
1457607Swyllys  * -------------------------
14580Sstevel@tonic-gate  *
14590Sstevel@tonic-gate  */
14600Sstevel@tonic-gate static int
14610Sstevel@tonic-gate pc_getfattype(
14620Sstevel@tonic-gate 	struct vnode *devvp,
14630Sstevel@tonic-gate 	int ldrive,
14640Sstevel@tonic-gate 	daddr_t *strtsectp,
14650Sstevel@tonic-gate 	int *fattypep)
14660Sstevel@tonic-gate {
14670Sstevel@tonic-gate 	buf_t *bp = NULL;		/* Disk buffer pointer */
1468*2972Sfrankho 	int err = 0;
14690Sstevel@tonic-gate 	uchar_t sysid = 0;		/* System ID character */
14700Sstevel@tonic-gate 	dev_t	dev = devvp->v_rdev;
14710Sstevel@tonic-gate 
14720Sstevel@tonic-gate 
14730Sstevel@tonic-gate 	/*
14740Sstevel@tonic-gate 	 * Open the device so we can check out the BPB or FDISK table,
14750Sstevel@tonic-gate 	 * then read in the sector.
14760Sstevel@tonic-gate 	 */
14770Sstevel@tonic-gate 	PC_DPRINTF2(5, "pc_getfattype: dev=%x  ldrive=%x  ", (int)dev, ldrive);
1478*2972Sfrankho 	if (err = VOP_OPEN(&devvp, FREAD, CRED())) {
1479*2972Sfrankho 		PC_DPRINTF1(1, "pc_getfattype: open error=%d\n", err);
1480*2972Sfrankho 		return (err);
14810Sstevel@tonic-gate 	}
14820Sstevel@tonic-gate 
14830Sstevel@tonic-gate 	/*
1484*2972Sfrankho 	 * Unpartitioned media (floppies and some removeable devices)
1485*2972Sfrankho 	 * don't have a partition table, the FAT BPB is at disk block 0.
1486*2972Sfrankho 	 * Start out by reading block 0.
14870Sstevel@tonic-gate 	 */
1488*2972Sfrankho 	*strtsectp = (daddr_t)0;
1489*2972Sfrankho 	bp = bread(dev, *strtsectp, PC_SAFESECSIZE);
1490*2972Sfrankho 
14910Sstevel@tonic-gate 	if (bp->b_flags & B_ERROR) {
1492*2972Sfrankho 		PC_DPRINTF2(1, "pc_getfattype: read error on "
1493*2972Sfrankho 		    "device %d, disk LBA %d\n", (int)dev, (int)*strtsectp);
1494*2972Sfrankho 		err = EIO;
14950Sstevel@tonic-gate 		goto out;
14960Sstevel@tonic-gate 	}
14970Sstevel@tonic-gate 
14980Sstevel@tonic-gate 	/*
1499*2972Sfrankho 	 * If a logical drive number is requested, parse the partition table
1500*2972Sfrankho 	 * and attempt to locate it. Otherwise, proceed immediately to the
1501*2972Sfrankho 	 * BPB check. findTheDrive(), if successful, returns the disk block
1502*2972Sfrankho 	 * number where the requested partition starts in "strtsecp".
15030Sstevel@tonic-gate 	 */
1504*2972Sfrankho 	if (ldrive != 0) {
1505607Swyllys 		PC_DPRINTF0(5, "pc_getfattype: using FDISK table to find BPB");
15060Sstevel@tonic-gate 
1507*2972Sfrankho 		if (err = findTheDrive(dev, ldrive, &bp, strtsectp, &sysid))
15080Sstevel@tonic-gate 			goto out;
15090Sstevel@tonic-gate 
15100Sstevel@tonic-gate 		brelse(bp);
15110Sstevel@tonic-gate 		bp = bread(dev, *strtsectp, PC_SAFESECSIZE);
15120Sstevel@tonic-gate 		if (bp->b_flags & B_ERROR) {
1513*2972Sfrankho 			PC_DPRINTF2(1, "pc_getfattype: read error on "
1514*2972Sfrankho 			    "device %d, disk LBA %d\n",
1515*2972Sfrankho 			    (int)dev, (int)*strtsectp);
1516*2972Sfrankho 			err = EIO;
15170Sstevel@tonic-gate 			goto out;
15180Sstevel@tonic-gate 		}
1519*2972Sfrankho 	}
15200Sstevel@tonic-gate 
1521*2972Sfrankho 	if (!isBPB((uchar_t *)bp->b_un.b_addr, fattypep)) {
1522*2972Sfrankho 		PC_DPRINTF2(1, "pc_getfattype: No FAT BPB on device %d, "
1523*2972Sfrankho 		    "disk LBA %d\n", (int)dev, (int)*strtsectp);
1524*2972Sfrankho 		err = EIO;
1525*2972Sfrankho 		goto out;
15260Sstevel@tonic-gate 	}
15270Sstevel@tonic-gate 
15280Sstevel@tonic-gate out:
1529607Swyllys 	/*
1530607Swyllys 	 * Release the buffer used
1531607Swyllys 	 */
15320Sstevel@tonic-gate 	if (bp != NULL)
15330Sstevel@tonic-gate 		brelse(bp);
15340Sstevel@tonic-gate 	(void) VOP_CLOSE(devvp, FREAD, 1, (offset_t)0, CRED());
1535*2972Sfrankho 	return (err);
15360Sstevel@tonic-gate }
15370Sstevel@tonic-gate 
15380Sstevel@tonic-gate 
15390Sstevel@tonic-gate /*
15400Sstevel@tonic-gate  * Get the boot parameter block and file allocation table.
15410Sstevel@tonic-gate  * If there is an old FAT, invalidate it.
15420Sstevel@tonic-gate  */
15430Sstevel@tonic-gate int
15440Sstevel@tonic-gate pc_getfat(struct pcfs *fsp)
15450Sstevel@tonic-gate {
15460Sstevel@tonic-gate 	struct vfs *vfsp = PCFSTOVFS(fsp);
15470Sstevel@tonic-gate 	struct buf *tp = 0;
15480Sstevel@tonic-gate 	struct buf *bp = 0;
15490Sstevel@tonic-gate 	uchar_t *fatp = NULL;
15500Sstevel@tonic-gate 	uchar_t *fat_changemap = NULL;
15510Sstevel@tonic-gate 	struct bootsec *bootp;
15520Sstevel@tonic-gate 	struct fat32_bootsec *f32b;
15530Sstevel@tonic-gate 	struct vnode *devvp;
15540Sstevel@tonic-gate 	int error;
15550Sstevel@tonic-gate 	int fatsize;
15560Sstevel@tonic-gate 	int fat_changemapsize;
15570Sstevel@tonic-gate 	int flags = 0;
15580Sstevel@tonic-gate 	int nfat;
15590Sstevel@tonic-gate 	int secsize;
15600Sstevel@tonic-gate 	int fatsec;
15610Sstevel@tonic-gate 
15620Sstevel@tonic-gate 	PC_DPRINTF0(5, "pc_getfat\n");
15630Sstevel@tonic-gate 	devvp = fsp->pcfs_devvp;
15640Sstevel@tonic-gate 	if (fsp->pcfs_fatp) {
15650Sstevel@tonic-gate 		/*
15660Sstevel@tonic-gate 		 * There is a FAT in core.
15670Sstevel@tonic-gate 		 * If there are open file pcnodes or we have modified it or
15680Sstevel@tonic-gate 		 * it hasn't timed out yet use the in core FAT.
15690Sstevel@tonic-gate 		 * Otherwise invalidate it and get a new one
15700Sstevel@tonic-gate 		 */
15710Sstevel@tonic-gate #ifdef notdef
15720Sstevel@tonic-gate 		if (fsp->pcfs_frefs ||
15730Sstevel@tonic-gate 		    (fsp->pcfs_flags & PCFS_FATMOD) ||
15740Sstevel@tonic-gate 		    (gethrestime_sec() < fsp->pcfs_fattime)) {
15750Sstevel@tonic-gate 			return (0);
15760Sstevel@tonic-gate 		} else {
15770Sstevel@tonic-gate 			mutex_enter(&pcfslock);
15780Sstevel@tonic-gate 			pc_invalfat(fsp);
15790Sstevel@tonic-gate 			mutex_exit(&pcfslock);
15800Sstevel@tonic-gate 		}
15810Sstevel@tonic-gate #endif /* notdef */
15820Sstevel@tonic-gate 		return (0);
15830Sstevel@tonic-gate 	}
15840Sstevel@tonic-gate 	/*
15850Sstevel@tonic-gate 	 * Open block device mounted on.
15860Sstevel@tonic-gate 	 */
15870Sstevel@tonic-gate 	error = VOP_OPEN(&devvp,
15880Sstevel@tonic-gate 	    (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
15890Sstevel@tonic-gate 	    CRED());
15900Sstevel@tonic-gate 	if (error) {
15910Sstevel@tonic-gate 		PC_DPRINTF1(1, "pc_getfat: open error=%d\n", error);
15920Sstevel@tonic-gate 		return (error);
15930Sstevel@tonic-gate 	}
15940Sstevel@tonic-gate 	/*
15950Sstevel@tonic-gate 	 * Get boot parameter block and check it for validity
15960Sstevel@tonic-gate 	 */
15970Sstevel@tonic-gate 	tp = bread(fsp->pcfs_xdev, fsp->pcfs_dosstart, PC_SAFESECSIZE);
1598*2972Sfrankho 	if ((tp->b_flags & (B_ERROR | B_STALE)) ||
1599*2972Sfrankho 	    !isBPB((uchar_t *)tp->b_un.b_addr, NULL)) {
1600*2972Sfrankho 		PC_DPRINTF2(1, "pc_getfat: boot block error on device %d, "
1601*2972Sfrankho 		    "disk LBA %d\n",
1602*2972Sfrankho 		    (int)fsp->pcfs_xdev, (int)fsp->pcfs_dosstart);
16030Sstevel@tonic-gate 		flags = tp->b_flags & B_ERROR;
16040Sstevel@tonic-gate 		error = EIO;
16050Sstevel@tonic-gate 		goto out;
16060Sstevel@tonic-gate 	}
16070Sstevel@tonic-gate 	tp->b_flags |= B_STALE | B_AGE;
16080Sstevel@tonic-gate 	bootp = (struct bootsec *)tp->b_un.b_addr;
16090Sstevel@tonic-gate 
1610607Swyllys 
16110Sstevel@tonic-gate 	/* get the sector size - may be more than 512 bytes */
16120Sstevel@tonic-gate 	secsize = (int)ltohs(bootp->bps[0]);
16130Sstevel@tonic-gate 	/* check for bogus sector size - fat should be at least 1 sector */
16140Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
16150Sstevel@tonic-gate 		f32b = (struct fat32_bootsec *)bootp;
16160Sstevel@tonic-gate 		fatsec = ltohi(f32b->f_fatlength);
16170Sstevel@tonic-gate 	} else {
16180Sstevel@tonic-gate 		fatsec = ltohs(bootp->fatsec);
16190Sstevel@tonic-gate 	}
16200Sstevel@tonic-gate 	if (secsize < 512 || fatsec < 1 || bootp->nfat < 1) {
16210Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: FAT size error");
16220Sstevel@tonic-gate 		error = EINVAL;
16230Sstevel@tonic-gate 		goto out;
16240Sstevel@tonic-gate 	}
16250Sstevel@tonic-gate 
16260Sstevel@tonic-gate 	switch (bootp->mediadesriptor) {
16270Sstevel@tonic-gate 	default:
16280Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: media-descriptor error, 0x%x",
16290Sstevel@tonic-gate 		    bootp->mediadesriptor);
16300Sstevel@tonic-gate 		error = EINVAL;
16310Sstevel@tonic-gate 		goto out;
16320Sstevel@tonic-gate 
16330Sstevel@tonic-gate 	case MD_FIXED:
16340Sstevel@tonic-gate 		/*
1635201Sjmcp 		 * PCMCIA pseudo floppy is type MD_FIXED,
16360Sstevel@tonic-gate 		 * but is accessed like a floppy
16370Sstevel@tonic-gate 		 */
16380Sstevel@tonic-gate 		if (!(fsp->pcfs_flags & PCFS_PCMCIA_NO_CIS)) {
16390Sstevel@tonic-gate 			fsp->pcfs_flags |= PCFS_NOCHK;
16400Sstevel@tonic-gate 		}
16410Sstevel@tonic-gate 		/* FALLTHRU */
16420Sstevel@tonic-gate 	case SS8SPT:
16430Sstevel@tonic-gate 	case DS8SPT:
16440Sstevel@tonic-gate 	case SS9SPT:
16450Sstevel@tonic-gate 	case DS9SPT:
16460Sstevel@tonic-gate 	case DS18SPT:
16470Sstevel@tonic-gate 	case DS9_15SPT:
16480Sstevel@tonic-gate 		fsp->pcfs_secsize = secsize;
16490Sstevel@tonic-gate 		fsp->pcfs_sdshift = secsize / DEV_BSIZE - 1;
16500Sstevel@tonic-gate 		fsp->pcfs_entps = secsize / sizeof (struct pcdir);
16510Sstevel@tonic-gate 		fsp->pcfs_spcl = (int)bootp->spcl;
16520Sstevel@tonic-gate 		fsp->pcfs_fatsec = fatsec;
16530Sstevel@tonic-gate 		fsp->pcfs_spt = (int)ltohs(bootp->spt);
16542720Sfrankho 		fsp->pcfs_rdirsec = ((int)ltohs(bootp->rdirents[0])
16552720Sfrankho 		    * sizeof (struct pcdir) + (secsize - 1)) / secsize;
16560Sstevel@tonic-gate 		fsp->pcfs_clsize = fsp->pcfs_spcl * secsize;
16570Sstevel@tonic-gate 		fsp->pcfs_fatstart = fsp->pcfs_dosstart +
16580Sstevel@tonic-gate 		    (daddr_t)ltohs(bootp->res_sec[0]);
16590Sstevel@tonic-gate 		fsp->pcfs_rdirstart = fsp->pcfs_fatstart +
16600Sstevel@tonic-gate 		    (bootp->nfat * fsp->pcfs_fatsec);
16610Sstevel@tonic-gate 		fsp->pcfs_datastart = fsp->pcfs_rdirstart + fsp->pcfs_rdirsec;
16620Sstevel@tonic-gate 		if (IS_FAT32(fsp))
16630Sstevel@tonic-gate 			fsp->pcfs_rdirstart = ltohi(f32b->f_rootcluster);
16640Sstevel@tonic-gate 		fsp->pcfs_ncluster = (((int)(ltohs(bootp->numsect[0]) ?
16650Sstevel@tonic-gate 		    ltohs(bootp->numsect[0]) : ltohi(bootp->totalsec))) -
16660Sstevel@tonic-gate 		    fsp->pcfs_datastart + fsp->pcfs_dosstart) / fsp->pcfs_spcl;
16670Sstevel@tonic-gate 		fsp->pcfs_numfat = (int)bootp->nfat;
16680Sstevel@tonic-gate 		fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
16690Sstevel@tonic-gate 		break;
16700Sstevel@tonic-gate 	}
16710Sstevel@tonic-gate 
16720Sstevel@tonic-gate 	/*
16730Sstevel@tonic-gate 	 * Get FAT and check it for validity
16740Sstevel@tonic-gate 	 */
16750Sstevel@tonic-gate 	fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
16760Sstevel@tonic-gate 	fatp = kmem_alloc(fatsize, KM_SLEEP);
16770Sstevel@tonic-gate 	error = pc_readfat(fsp, fatp, fsp->pcfs_fatstart, fatsize);
16780Sstevel@tonic-gate 	if (error) {
16790Sstevel@tonic-gate 		flags = B_ERROR;
16800Sstevel@tonic-gate 		goto out;
16810Sstevel@tonic-gate 	}
16820Sstevel@tonic-gate 	fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1;
16830Sstevel@tonic-gate 	fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP);
16840Sstevel@tonic-gate 
1685607Swyllys 	/*
1686607Swyllys 	 * The only definite signature check is that the
1687607Swyllys 	 * media descriptor byte should match the first byte
1688607Swyllys 	 * of the FAT block.
1689607Swyllys 	 */
1690607Swyllys 	if (fatp[0] != bootp->mediadesriptor) {
16910Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: FAT signature error");
16920Sstevel@tonic-gate 		error = EINVAL;
16930Sstevel@tonic-gate 		goto out;
16940Sstevel@tonic-gate 	}
16950Sstevel@tonic-gate 	/*
16960Sstevel@tonic-gate 	 * Checking for fatsec and number of supported clusters, should
1697607Swyllys 	 * actually determine a FAT12/FAT media.
16980Sstevel@tonic-gate 	 */
16990Sstevel@tonic-gate 	if (fsp->pcfs_flags & PCFS_FAT16) {
17000Sstevel@tonic-gate 		if ((fsp->pcfs_fatsec <= 12) &&
17010Sstevel@tonic-gate 		    ((fatsize * 2 / 3) >= fsp->pcfs_ncluster)) {
17020Sstevel@tonic-gate 			/*
17030Sstevel@tonic-gate 			 * We have a 12-bit FAT, rather than a 16-bit FAT.
17040Sstevel@tonic-gate 			 * Ignore what the fdisk table says.
17050Sstevel@tonic-gate 			 */
17060Sstevel@tonic-gate 			PC_DPRINTF0(2, "pc_getfattype: forcing 12-bit FAT\n");
17070Sstevel@tonic-gate 			fsp->pcfs_flags &= ~PCFS_FAT16;
17080Sstevel@tonic-gate 		}
17090Sstevel@tonic-gate 	}
17100Sstevel@tonic-gate 	/*
17110Sstevel@tonic-gate 	 * Sanity check our FAT is large enough for the
17120Sstevel@tonic-gate 	 * clusters we think we have.
17130Sstevel@tonic-gate 	 */
17140Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_FAT16) &&
17150Sstevel@tonic-gate 	    ((fatsize / 2) < fsp->pcfs_ncluster)) {
17160Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: FAT too small for number of clusters");
17170Sstevel@tonic-gate 		error = EINVAL;
17180Sstevel@tonic-gate 		goto out;
17190Sstevel@tonic-gate 	}
17200Sstevel@tonic-gate 
17210Sstevel@tonic-gate 	/*
17220Sstevel@tonic-gate 	 * Get alternate FATs and check for consistency
17230Sstevel@tonic-gate 	 * This is an inlined version of pc_readfat().
17240Sstevel@tonic-gate 	 * Since we're only comparing FAT and alternate FAT,
17250Sstevel@tonic-gate 	 * there's no reason to let pc_readfat() copy data out
17260Sstevel@tonic-gate 	 * of the buf. Instead, compare in-situ, one cluster
17270Sstevel@tonic-gate 	 * at a time.
17280Sstevel@tonic-gate 	 */
17290Sstevel@tonic-gate 	for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) {
17300Sstevel@tonic-gate 		size_t startsec;
17310Sstevel@tonic-gate 		size_t off;
17320Sstevel@tonic-gate 
17330Sstevel@tonic-gate 		startsec = fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec;
17340Sstevel@tonic-gate 
17350Sstevel@tonic-gate 		for (off = 0; off < fatsize; off += fsp->pcfs_clsize) {
17360Sstevel@tonic-gate 			bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp,
17370Sstevel@tonic-gate 				startsec +
17380Sstevel@tonic-gate 				pc_cltodb(fsp, pc_lblkno(fsp, off))),
17390Sstevel@tonic-gate 				MIN(fsp->pcfs_clsize, fatsize - off));
17400Sstevel@tonic-gate 			if (bp->b_flags & (B_ERROR | B_STALE)) {
17410Sstevel@tonic-gate 				cmn_err(CE_NOTE,
17420Sstevel@tonic-gate 					"!pcfs: alternate FAT #%d read error"
17430Sstevel@tonic-gate 					" at byte %ld", nfat, off);
17440Sstevel@tonic-gate 				flags = B_ERROR;
17450Sstevel@tonic-gate 				error = EIO;
17460Sstevel@tonic-gate 				goto out;
17470Sstevel@tonic-gate 			}
17480Sstevel@tonic-gate 			bp->b_flags |= B_STALE | B_AGE;
17490Sstevel@tonic-gate 			if (bcmp(bp->b_un.b_addr,
17500Sstevel@tonic-gate 			    fatp + off,
17510Sstevel@tonic-gate 			    MIN(fsp->pcfs_clsize, fatsize - off))) {
17520Sstevel@tonic-gate 				cmn_err(CE_NOTE,
17530Sstevel@tonic-gate 					"!pcfs: alternate FAT #%d corrupted"
17540Sstevel@tonic-gate 					" at byte %ld", nfat, off);
17550Sstevel@tonic-gate 				flags = B_ERROR;
17560Sstevel@tonic-gate 			}
17570Sstevel@tonic-gate 			brelse(bp);
17580Sstevel@tonic-gate 			bp = NULL;	/* prevent double release */
17590Sstevel@tonic-gate 		}
17600Sstevel@tonic-gate 	}
17610Sstevel@tonic-gate 
17620Sstevel@tonic-gate 	fsp->pcfs_fatsize = fatsize;
17630Sstevel@tonic-gate 	fsp->pcfs_fatp = fatp;
17640Sstevel@tonic-gate 	fsp->pcfs_fat_changemapsize = fat_changemapsize;
17650Sstevel@tonic-gate 	fsp->pcfs_fat_changemap = fat_changemap;
17660Sstevel@tonic-gate 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
17670Sstevel@tonic-gate 	fsp->pcfs_fatjustread = 1;
17680Sstevel@tonic-gate 
17690Sstevel@tonic-gate 	brelse(tp);
17700Sstevel@tonic-gate 	tp = NULL;
17710Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
17720Sstevel@tonic-gate 		/* get fsinfo */
17730Sstevel@tonic-gate 		struct fat32_boot_fsinfo fsinfo_disk;
17740Sstevel@tonic-gate 
17750Sstevel@tonic-gate 		fsp->f32fsinfo_sector = ltohs(f32b->f_infosector);
17760Sstevel@tonic-gate 		tp = bread(fsp->pcfs_xdev,
17770Sstevel@tonic-gate 		    fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector),
17780Sstevel@tonic-gate 		    PC_SAFESECSIZE);
17790Sstevel@tonic-gate 		if (tp->b_flags & (B_ERROR | B_STALE)) {
17800Sstevel@tonic-gate 			cmn_err(CE_NOTE, "!pcfs: error reading fat32 fsinfo");
17810Sstevel@tonic-gate 			flags = tp->b_flags & B_ERROR;
17820Sstevel@tonic-gate 			brelse(tp);
17830Sstevel@tonic-gate 			tp = NULL;
17840Sstevel@tonic-gate 			error = EIO;
17850Sstevel@tonic-gate 			goto out;
17860Sstevel@tonic-gate 		}
17870Sstevel@tonic-gate 		tp->b_flags |= B_STALE | B_AGE;
17880Sstevel@tonic-gate 		bcopy((void *)(tp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
17890Sstevel@tonic-gate 		    &fsinfo_disk, sizeof (struct fat32_boot_fsinfo));
17900Sstevel@tonic-gate 		brelse(tp);
17910Sstevel@tonic-gate 		tp = NULL;
17920Sstevel@tonic-gate 
17930Sstevel@tonic-gate 		/* translated fields */
17940Sstevel@tonic-gate 		fsp->fsinfo_native.fs_signature =
17950Sstevel@tonic-gate 		    ltohi(fsinfo_disk.fs_signature);
17960Sstevel@tonic-gate 		fsp->fsinfo_native.fs_free_clusters =
17970Sstevel@tonic-gate 		    ltohi(fsinfo_disk.fs_free_clusters);
17980Sstevel@tonic-gate 		if (fsp->fsinfo_native.fs_signature != FAT32_FS_SIGN) {
17990Sstevel@tonic-gate 			cmn_err(CE_NOTE,
18000Sstevel@tonic-gate 			    "!pcfs: fat32 fsinfo signature mismatch.");
18010Sstevel@tonic-gate 			error = EINVAL;
18020Sstevel@tonic-gate 			goto out;
18030Sstevel@tonic-gate 		}
18040Sstevel@tonic-gate 	}
18050Sstevel@tonic-gate 
18060Sstevel@tonic-gate 	return (0);
18070Sstevel@tonic-gate 
18080Sstevel@tonic-gate out:
18090Sstevel@tonic-gate 	cmn_err(CE_NOTE, "!pcfs: illegal disk format");
18100Sstevel@tonic-gate 	if (tp)
18110Sstevel@tonic-gate 		brelse(tp);
18120Sstevel@tonic-gate 	if (bp)
18130Sstevel@tonic-gate 		brelse(bp);
18140Sstevel@tonic-gate 	if (fatp)
18150Sstevel@tonic-gate 		kmem_free(fatp, fatsize);
18160Sstevel@tonic-gate 	if (fat_changemap)
18170Sstevel@tonic-gate 		kmem_free(fat_changemap, fat_changemapsize);
18180Sstevel@tonic-gate 
18190Sstevel@tonic-gate 	if (flags) {
18200Sstevel@tonic-gate 		pc_mark_irrecov(fsp);
18210Sstevel@tonic-gate 	}
18220Sstevel@tonic-gate 	(void) VOP_CLOSE(devvp, (vfsp->vfs_flag & VFS_RDONLY) ?
18230Sstevel@tonic-gate 	    FREAD : FREAD|FWRITE, 1, (offset_t)0, CRED());
18240Sstevel@tonic-gate 	return (error);
18250Sstevel@tonic-gate }
18260Sstevel@tonic-gate 
18270Sstevel@tonic-gate int
18280Sstevel@tonic-gate pc_syncfat(struct pcfs *fsp)
18290Sstevel@tonic-gate {
18300Sstevel@tonic-gate 	struct buf *bp;
18310Sstevel@tonic-gate 	int nfat;
18320Sstevel@tonic-gate 	int	error;
18330Sstevel@tonic-gate 	struct fat32_boot_fsinfo fsinfo_disk;
18340Sstevel@tonic-gate 
18350Sstevel@tonic-gate 	PC_DPRINTF0(7, "pcfs_syncfat\n");
18360Sstevel@tonic-gate 	if ((fsp->pcfs_fatp == (uchar_t *)0) ||
18370Sstevel@tonic-gate 	    !(fsp->pcfs_flags & PCFS_FATMOD))
18380Sstevel@tonic-gate 		return (0);
18390Sstevel@tonic-gate 	/*
18400Sstevel@tonic-gate 	 * write out all copies of FATs
18410Sstevel@tonic-gate 	 */
18420Sstevel@tonic-gate 	fsp->pcfs_flags &= ~PCFS_FATMOD;
18430Sstevel@tonic-gate 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
18440Sstevel@tonic-gate 	for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) {
18450Sstevel@tonic-gate 		error = pc_writefat(fsp,
18460Sstevel@tonic-gate 		    fsp->pcfs_fatstart + nfat*fsp->pcfs_fatsec);
18470Sstevel@tonic-gate 		if (error) {
18480Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
18490Sstevel@tonic-gate 			return (EIO);
18500Sstevel@tonic-gate 		}
18510Sstevel@tonic-gate 	}
18520Sstevel@tonic-gate 	pc_clear_fatchanges(fsp);
18530Sstevel@tonic-gate 	PC_DPRINTF0(6, "pcfs_syncfat: wrote out FAT\n");
18540Sstevel@tonic-gate 	/* write out fsinfo */
18550Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
18560Sstevel@tonic-gate 		bp = bread(fsp->pcfs_xdev,
18570Sstevel@tonic-gate 		    fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector),
18580Sstevel@tonic-gate 		    PC_SAFESECSIZE);
18590Sstevel@tonic-gate 		if (bp->b_flags & (B_ERROR | B_STALE)) {
18600Sstevel@tonic-gate 			brelse(bp);
18610Sstevel@tonic-gate 			return (EIO);
18620Sstevel@tonic-gate 		}
18630Sstevel@tonic-gate 		bcopy((void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
18640Sstevel@tonic-gate 		    &fsinfo_disk, sizeof (struct fat32_boot_fsinfo));
18650Sstevel@tonic-gate 		/* translate fields */
18660Sstevel@tonic-gate 		fsinfo_disk.fs_free_clusters =
18670Sstevel@tonic-gate 		    htoli(fsp->fsinfo_native.fs_free_clusters);
18680Sstevel@tonic-gate 		fsinfo_disk.fs_next_cluster = (uint32_t)FSINFO_UNKNOWN;
18690Sstevel@tonic-gate 		bcopy(&fsinfo_disk,
18700Sstevel@tonic-gate 		    (void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
18710Sstevel@tonic-gate 		    sizeof (struct fat32_boot_fsinfo));
18720Sstevel@tonic-gate 		bwrite2(bp);
18730Sstevel@tonic-gate 		error = geterror(bp);
18740Sstevel@tonic-gate 		brelse(bp);
18750Sstevel@tonic-gate 		if (error) {
18760Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
18770Sstevel@tonic-gate 			return (EIO);
18780Sstevel@tonic-gate 		}
18790Sstevel@tonic-gate 	}
18800Sstevel@tonic-gate 	return (0);
18810Sstevel@tonic-gate }
18820Sstevel@tonic-gate 
18830Sstevel@tonic-gate void
18840Sstevel@tonic-gate pc_invalfat(struct pcfs *fsp)
18850Sstevel@tonic-gate {
18860Sstevel@tonic-gate 	struct pcfs *xfsp;
18870Sstevel@tonic-gate 	int mount_cnt = 0;
18880Sstevel@tonic-gate 
18890Sstevel@tonic-gate 	PC_DPRINTF0(7, "pc_invalfat\n");
18900Sstevel@tonic-gate 	if (fsp->pcfs_fatp == (uchar_t *)0)
18910Sstevel@tonic-gate 		panic("pc_invalfat");
18920Sstevel@tonic-gate 	/*
18930Sstevel@tonic-gate 	 * Release FAT
18940Sstevel@tonic-gate 	 */
18950Sstevel@tonic-gate 	kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsize);
18960Sstevel@tonic-gate 	fsp->pcfs_fatp = NULL;
18970Sstevel@tonic-gate 	kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize);
18980Sstevel@tonic-gate 	fsp->pcfs_fat_changemap = NULL;
18990Sstevel@tonic-gate 	/*
19000Sstevel@tonic-gate 	 * Invalidate all the blocks associated with the device.
19010Sstevel@tonic-gate 	 * Not needed if stateless.
19020Sstevel@tonic-gate 	 */
19030Sstevel@tonic-gate 	for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt)
19040Sstevel@tonic-gate 		if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev)
19050Sstevel@tonic-gate 			mount_cnt++;
19060Sstevel@tonic-gate 
19070Sstevel@tonic-gate 	if (!mount_cnt)
19080Sstevel@tonic-gate 		binval(fsp->pcfs_xdev);
19090Sstevel@tonic-gate 	/*
19100Sstevel@tonic-gate 	 * close mounted device
19110Sstevel@tonic-gate 	 */
19120Sstevel@tonic-gate 	(void) VOP_CLOSE(fsp->pcfs_devvp,
19130Sstevel@tonic-gate 	    (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
19140Sstevel@tonic-gate 	    1, (offset_t)0, CRED());
19150Sstevel@tonic-gate }
19160Sstevel@tonic-gate 
19170Sstevel@tonic-gate void
19180Sstevel@tonic-gate pc_badfs(struct pcfs *fsp)
19190Sstevel@tonic-gate {
19200Sstevel@tonic-gate 	cmn_err(CE_WARN, "corrupted PC file system on dev %x.%x\n",
19210Sstevel@tonic-gate 	    getmajor(fsp->pcfs_devvp->v_rdev),
19220Sstevel@tonic-gate 	    getminor(fsp->pcfs_devvp->v_rdev));
19230Sstevel@tonic-gate }
19240Sstevel@tonic-gate 
19250Sstevel@tonic-gate /*
19260Sstevel@tonic-gate  * The problem with supporting NFS on the PCFS filesystem is that there
19270Sstevel@tonic-gate  * is no good place to keep the generation number. The only possible
19280Sstevel@tonic-gate  * place is inside a directory entry. There are a few words that we
19290Sstevel@tonic-gate  * don't use - they store NT & OS/2 attributes, and the creation/last access
19300Sstevel@tonic-gate  * time of the file - but it seems wrong to use them. In addition, directory
19310Sstevel@tonic-gate  * entries come and go. If a directory is removed completely, its directory
19320Sstevel@tonic-gate  * blocks are freed and the generation numbers are lost. Whereas in ufs,
19330Sstevel@tonic-gate  * inode blocks are dedicated for inodes, so the generation numbers are
19340Sstevel@tonic-gate  * permanently kept on the disk.
19350Sstevel@tonic-gate  */
19360Sstevel@tonic-gate static int
19370Sstevel@tonic-gate pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp)
19380Sstevel@tonic-gate {
19390Sstevel@tonic-gate 	struct pcnode *pcp;
19400Sstevel@tonic-gate 	struct pc_fid *pcfid;
19410Sstevel@tonic-gate 	struct pcfs *fsp;
19420Sstevel@tonic-gate 	struct pcdir *ep;
19430Sstevel@tonic-gate 	daddr_t eblkno;
19440Sstevel@tonic-gate 	int eoffset;
19450Sstevel@tonic-gate 	struct buf *bp;
19460Sstevel@tonic-gate 	int error;
19470Sstevel@tonic-gate 	pc_cluster32_t	cn;
19480Sstevel@tonic-gate 
19490Sstevel@tonic-gate 	pcfid = (struct pc_fid *)fidp;
19500Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
19510Sstevel@tonic-gate 
19520Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
19530Sstevel@tonic-gate 	if (error) {
19540Sstevel@tonic-gate 		*vpp = NULL;
19550Sstevel@tonic-gate 		return (error);
19560Sstevel@tonic-gate 	}
19570Sstevel@tonic-gate 
19580Sstevel@tonic-gate 	if (pcfid->pcfid_block == 0) {
19590Sstevel@tonic-gate 		pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
19600Sstevel@tonic-gate 		pcp->pc_flags |= PC_EXTERNAL;
19610Sstevel@tonic-gate 		*vpp = PCTOV(pcp);
19620Sstevel@tonic-gate 		pc_unlockfs(fsp);
19630Sstevel@tonic-gate 		return (0);
19640Sstevel@tonic-gate 	}
19650Sstevel@tonic-gate 	eblkno = pcfid->pcfid_block;
19660Sstevel@tonic-gate 	eoffset = pcfid->pcfid_offset;
19670Sstevel@tonic-gate 	if ((pc_dbtocl(fsp,
19680Sstevel@tonic-gate 	    eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) ||
19690Sstevel@tonic-gate 	    (eoffset > fsp->pcfs_clsize)) {
19700Sstevel@tonic-gate 		pc_unlockfs(fsp);
19710Sstevel@tonic-gate 		*vpp = NULL;
19720Sstevel@tonic-gate 		return (EINVAL);
19730Sstevel@tonic-gate 	}
19740Sstevel@tonic-gate 
19750Sstevel@tonic-gate 	if (eblkno >= fsp->pcfs_datastart || (eblkno-fsp->pcfs_rdirstart)
19760Sstevel@tonic-gate 	    < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
19770Sstevel@tonic-gate 		bp = bread(fsp->pcfs_xdev, eblkno, fsp->pcfs_clsize);
19780Sstevel@tonic-gate 	} else {
19790Sstevel@tonic-gate 		bp = bread(fsp->pcfs_xdev, eblkno,
19800Sstevel@tonic-gate 		    (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize);
19810Sstevel@tonic-gate 	}
19820Sstevel@tonic-gate 	if (bp->b_flags & (B_ERROR | B_STALE)) {
19830Sstevel@tonic-gate 		error = geterror(bp);
19840Sstevel@tonic-gate 		brelse(bp);
19850Sstevel@tonic-gate 		if (error)
19860Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
19870Sstevel@tonic-gate 		*vpp = NULL;
19880Sstevel@tonic-gate 		pc_unlockfs(fsp);
19890Sstevel@tonic-gate 		return (error);
19900Sstevel@tonic-gate 	}
19910Sstevel@tonic-gate 	ep = (struct pcdir *)(bp->b_un.b_addr + eoffset);
19920Sstevel@tonic-gate 	/*
19930Sstevel@tonic-gate 	 * Ok, if this is a valid file handle that we gave out,
19940Sstevel@tonic-gate 	 * then simply ensuring that the creation time matches,
19950Sstevel@tonic-gate 	 * the entry has not been deleted, and it has a valid first
19960Sstevel@tonic-gate 	 * character should be enough.
19970Sstevel@tonic-gate 	 *
19980Sstevel@tonic-gate 	 * Unfortunately, verifying that the <blkno, offset> _still_
19990Sstevel@tonic-gate 	 * refers to a directory entry is not easy, since we'd have
20000Sstevel@tonic-gate 	 * to search _all_ directories starting from root to find it.
20010Sstevel@tonic-gate 	 * That's a high price to pay just in case somebody is forging
20020Sstevel@tonic-gate 	 * file handles. So instead we verify that as much of the
20030Sstevel@tonic-gate 	 * entry is valid as we can:
20040Sstevel@tonic-gate 	 *
20050Sstevel@tonic-gate 	 * 1. The starting cluster is 0 (unallocated) or valid
20060Sstevel@tonic-gate 	 * 2. It is not an LFN entry
20070Sstevel@tonic-gate 	 * 3. It is not hidden (unless mounted as such)
20080Sstevel@tonic-gate 	 * 4. It is not the label
20090Sstevel@tonic-gate 	 */
20100Sstevel@tonic-gate 	cn = pc_getstartcluster(fsp, ep);
20110Sstevel@tonic-gate 	/*
20120Sstevel@tonic-gate 	 * if the starting cluster is valid, but not valid according
20130Sstevel@tonic-gate 	 * to pc_validcl(), force it to be to simplify the following if.
20140Sstevel@tonic-gate 	 */
20150Sstevel@tonic-gate 	if (cn == 0)
20160Sstevel@tonic-gate 		cn = PCF_FIRSTCLUSTER;
20170Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
20180Sstevel@tonic-gate 		if (cn >= PCF_LASTCLUSTER32)
20190Sstevel@tonic-gate 			cn = PCF_FIRSTCLUSTER;
20200Sstevel@tonic-gate 	} else {
20210Sstevel@tonic-gate 		if (cn >= PCF_LASTCLUSTER)
20220Sstevel@tonic-gate 			cn = PCF_FIRSTCLUSTER;
20230Sstevel@tonic-gate 	}
20240Sstevel@tonic-gate 	if ((!pc_validcl(fsp, cn)) ||
20250Sstevel@tonic-gate 	    (PCDL_IS_LFN(ep)) ||
20260Sstevel@tonic-gate 	    (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) ||
20270Sstevel@tonic-gate 	    ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) {
20280Sstevel@tonic-gate 		bp->b_flags |= B_STALE | B_AGE;
20290Sstevel@tonic-gate 		brelse(bp);
20300Sstevel@tonic-gate 		pc_unlockfs(fsp);
20310Sstevel@tonic-gate 		return (EINVAL);
20320Sstevel@tonic-gate 	}
20330Sstevel@tonic-gate 	if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) &&
20340Sstevel@tonic-gate 	    (ep->pcd_filename[0] != PCD_ERASED) &&
20350Sstevel@tonic-gate 	    (pc_validchar(ep->pcd_filename[0]) ||
20360Sstevel@tonic-gate 		(ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) {
20370Sstevel@tonic-gate 		pcp = pc_getnode(fsp, eblkno, eoffset, ep);
20380Sstevel@tonic-gate 		pcp->pc_flags |= PC_EXTERNAL;
20390Sstevel@tonic-gate 		*vpp = PCTOV(pcp);
20400Sstevel@tonic-gate 	} else {
20410Sstevel@tonic-gate 		*vpp = NULL;
20420Sstevel@tonic-gate 	}
20430Sstevel@tonic-gate 	bp->b_flags |= B_STALE | B_AGE;
20440Sstevel@tonic-gate 	brelse(bp);
20450Sstevel@tonic-gate 	pc_unlockfs(fsp);
20460Sstevel@tonic-gate 	return (0);
20470Sstevel@tonic-gate }
20480Sstevel@tonic-gate 
20490Sstevel@tonic-gate /*
2050201Sjmcp  * if device is a PCMCIA pseudo floppy, return 1
20510Sstevel@tonic-gate  * otherwise, return 0
20520Sstevel@tonic-gate  */
20530Sstevel@tonic-gate static int
2054201Sjmcp pcfs_pseudo_floppy(dev_t rdev)
20550Sstevel@tonic-gate {
20560Sstevel@tonic-gate 	int			error, err;
20570Sstevel@tonic-gate 	struct dk_cinfo		info;
20580Sstevel@tonic-gate 	ldi_handle_t		lh;
20590Sstevel@tonic-gate 	ldi_ident_t		li;
20600Sstevel@tonic-gate 
20610Sstevel@tonic-gate 	err = ldi_ident_from_mod(&modlinkage, &li);
20620Sstevel@tonic-gate 	if (err) {
20630Sstevel@tonic-gate 		PC_DPRINTF1(1,
2064201Sjmcp 		    "pcfs_pseudo_floppy: ldi_ident_from_mod err=%d\n", err);
20650Sstevel@tonic-gate 		return (0);
20660Sstevel@tonic-gate 	}
20670Sstevel@tonic-gate 
20680Sstevel@tonic-gate 	err = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, CRED(), &lh, li);
20690Sstevel@tonic-gate 	ldi_ident_release(li);
20700Sstevel@tonic-gate 	if (err) {
20710Sstevel@tonic-gate 		PC_DPRINTF1(1,
2072201Sjmcp 		    "pcfs_pseudo_floppy: ldi_open err=%d\n", err);
20730Sstevel@tonic-gate 		return (0);
20740Sstevel@tonic-gate 	}
20750Sstevel@tonic-gate 
20760Sstevel@tonic-gate 	/* return value stored in err is purposfully ignored */
20770Sstevel@tonic-gate 	error = ldi_ioctl(lh, DKIOCINFO, (intptr_t)&info, FKIOCTL,
20780Sstevel@tonic-gate 	    CRED(), &err);
20790Sstevel@tonic-gate 
20800Sstevel@tonic-gate 	err = ldi_close(lh, FREAD, CRED());
20810Sstevel@tonic-gate 	if (err != 0) {
20820Sstevel@tonic-gate 		PC_DPRINTF1(1,
2083201Sjmcp 		    "pcfs_pseudo_floppy: ldi_close err=%d\n", err);
20840Sstevel@tonic-gate 		return (0);
20850Sstevel@tonic-gate 	}
20860Sstevel@tonic-gate 
20870Sstevel@tonic-gate 	if ((error == 0) && (info.dki_ctype == DKC_PCMCIA_MEM) &&
20880Sstevel@tonic-gate 		(info.dki_flags & DKI_PCMCIA_PFD))
20890Sstevel@tonic-gate 		return (1);
20900Sstevel@tonic-gate 	else
20910Sstevel@tonic-gate 		return (0);
20920Sstevel@tonic-gate }
20930Sstevel@tonic-gate 
20940Sstevel@tonic-gate /*
20950Sstevel@tonic-gate  * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about
20960Sstevel@tonic-gate  * a meg), so we can't bread() it all in at once. This routine reads a
20970Sstevel@tonic-gate  * fat a chunk at a time.
20980Sstevel@tonic-gate  */
20990Sstevel@tonic-gate static int
21000Sstevel@tonic-gate pc_readfat(struct pcfs *fsp, uchar_t *fatp, daddr_t start, size_t fatsize)
21010Sstevel@tonic-gate {
21020Sstevel@tonic-gate 	struct buf *bp;
21030Sstevel@tonic-gate 	size_t off;
21040Sstevel@tonic-gate 	size_t readsize;
21050Sstevel@tonic-gate 
21060Sstevel@tonic-gate 	readsize = fsp->pcfs_clsize;
21070Sstevel@tonic-gate 	for (off = 0; off < fatsize; off += readsize, fatp += readsize) {
21080Sstevel@tonic-gate 		if (readsize > (fatsize - off))
21090Sstevel@tonic-gate 			readsize = fatsize - off;
21100Sstevel@tonic-gate 		bp = bread(fsp->pcfs_xdev,
21110Sstevel@tonic-gate 		    pc_dbdaddr(fsp, start +
21120Sstevel@tonic-gate 			pc_cltodb(fsp, pc_lblkno(fsp, off))),
21130Sstevel@tonic-gate 		    readsize);
21140Sstevel@tonic-gate 		if (bp->b_flags & (B_ERROR | B_STALE)) {
21150Sstevel@tonic-gate 			brelse(bp);
21160Sstevel@tonic-gate 			return (EIO);
21170Sstevel@tonic-gate 		}
21180Sstevel@tonic-gate 		bp->b_flags |= B_STALE | B_AGE;
21190Sstevel@tonic-gate 		bcopy(bp->b_un.b_addr, fatp, readsize);
21200Sstevel@tonic-gate 		brelse(bp);
21210Sstevel@tonic-gate 	}
21220Sstevel@tonic-gate 	return (0);
21230Sstevel@tonic-gate }
21240Sstevel@tonic-gate 
21250Sstevel@tonic-gate /*
21260Sstevel@tonic-gate  * We write the FAT out a _lot_, in order to make sure that it
21270Sstevel@tonic-gate  * is up-to-date. But on a FAT32 system (large drive, small clusters)
21280Sstevel@tonic-gate  * the FAT might be a couple of megabytes, and writing it all out just
21290Sstevel@tonic-gate  * because we created or deleted a small file is painful (especially
21300Sstevel@tonic-gate  * since we do it for each alternate FAT too). So instead, for FAT16 and
21310Sstevel@tonic-gate  * FAT32 we only write out the bit that has changed. We don't clear
21320Sstevel@tonic-gate  * the 'updated' fields here because the caller might be writing out
21330Sstevel@tonic-gate  * several FATs, so the caller must use pc_clear_fatchanges() after
21340Sstevel@tonic-gate  * all FATs have been updated.
21350Sstevel@tonic-gate  */
21360Sstevel@tonic-gate static int
21370Sstevel@tonic-gate pc_writefat(struct pcfs *fsp, daddr_t start)
21380Sstevel@tonic-gate {
21390Sstevel@tonic-gate 	struct buf *bp;
21400Sstevel@tonic-gate 	size_t off;
21410Sstevel@tonic-gate 	size_t writesize;
21420Sstevel@tonic-gate 	int	error;
21430Sstevel@tonic-gate 	uchar_t *fatp = fsp->pcfs_fatp;
21440Sstevel@tonic-gate 	size_t fatsize = fsp->pcfs_fatsize;
21450Sstevel@tonic-gate 
21460Sstevel@tonic-gate 	writesize = fsp->pcfs_clsize;
21470Sstevel@tonic-gate 	for (off = 0; off < fatsize; off += writesize, fatp += writesize) {
21480Sstevel@tonic-gate 		if (writesize > (fatsize - off))
21490Sstevel@tonic-gate 			writesize = fatsize - off;
21500Sstevel@tonic-gate 		if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) {
21510Sstevel@tonic-gate 			continue;
21520Sstevel@tonic-gate 		}
21530Sstevel@tonic-gate 		bp = ngeteblk(writesize);
21540Sstevel@tonic-gate 		bp->b_edev = fsp->pcfs_xdev;
21550Sstevel@tonic-gate 		bp->b_dev = cmpdev(bp->b_edev);
21560Sstevel@tonic-gate 		bp->b_blkno = pc_dbdaddr(fsp, start +
21570Sstevel@tonic-gate 		    pc_cltodb(fsp, pc_lblkno(fsp, off)));
21580Sstevel@tonic-gate 		bcopy(fatp, bp->b_un.b_addr, writesize);
21590Sstevel@tonic-gate 		bwrite2(bp);
21600Sstevel@tonic-gate 		error = geterror(bp);
21610Sstevel@tonic-gate 		brelse(bp);
21620Sstevel@tonic-gate 		if (error) {
21630Sstevel@tonic-gate 			return (error);
21640Sstevel@tonic-gate 		}
21650Sstevel@tonic-gate 	}
21660Sstevel@tonic-gate 	return (0);
21670Sstevel@tonic-gate }
21680Sstevel@tonic-gate 
21690Sstevel@tonic-gate /*
21700Sstevel@tonic-gate  * Mark the FAT cluster that 'cn' is stored in as modified.
21710Sstevel@tonic-gate  */
21720Sstevel@tonic-gate void
21730Sstevel@tonic-gate pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn)
21740Sstevel@tonic-gate {
21750Sstevel@tonic-gate 	pc_cluster32_t	bn;
21760Sstevel@tonic-gate 	size_t		size;
21770Sstevel@tonic-gate 
21780Sstevel@tonic-gate 	/* which fat block is the cluster number stored in? */
21790Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
21800Sstevel@tonic-gate 		size = sizeof (pc_cluster32_t);
21810Sstevel@tonic-gate 		bn = pc_lblkno(fsp, cn * size);
21820Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
21830Sstevel@tonic-gate 	} else if (IS_FAT16(fsp)) {
21840Sstevel@tonic-gate 		size = sizeof (pc_cluster16_t);
21850Sstevel@tonic-gate 		bn = pc_lblkno(fsp, cn * size);
21860Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
21870Sstevel@tonic-gate 	} else {
21880Sstevel@tonic-gate 		offset_t off;
21890Sstevel@tonic-gate 		pc_cluster32_t nbn;
21900Sstevel@tonic-gate 
21910Sstevel@tonic-gate 		ASSERT(IS_FAT12(fsp));
21920Sstevel@tonic-gate 		off = cn + (cn >> 1);
21930Sstevel@tonic-gate 		bn = pc_lblkno(fsp, off);
21940Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
21950Sstevel@tonic-gate 		/* does this field wrap into the next fat cluster? */
21960Sstevel@tonic-gate 		nbn = pc_lblkno(fsp, off + 1);
21970Sstevel@tonic-gate 		if (nbn != bn) {
21980Sstevel@tonic-gate 			fsp->pcfs_fat_changemap[nbn] = 1;
21990Sstevel@tonic-gate 		}
22000Sstevel@tonic-gate 	}
22010Sstevel@tonic-gate }
22020Sstevel@tonic-gate 
22030Sstevel@tonic-gate /*
22040Sstevel@tonic-gate  * return whether the FAT cluster 'bn' is updated and needs to
22050Sstevel@tonic-gate  * be written out.
22060Sstevel@tonic-gate  */
22070Sstevel@tonic-gate int
22080Sstevel@tonic-gate pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn)
22090Sstevel@tonic-gate {
22100Sstevel@tonic-gate 	return (fsp->pcfs_fat_changemap[bn] == 1);
22110Sstevel@tonic-gate }
22122720Sfrankho 
22132720Sfrankho /*
22142720Sfrankho  * Implementation of VFS_FREEVFS() to support forced umounts.
22152720Sfrankho  * This is called by the vfs framework after umount, to trigger
22162720Sfrankho  * the release of any resources still associated with the given
22172720Sfrankho  * vfs_t once the need to keep them has gone away.
22182720Sfrankho  */
22192720Sfrankho void
22202720Sfrankho pcfs_freevfs(vfs_t *vfsp)
22212720Sfrankho {
22222720Sfrankho 	struct pcfs *fsp = VFSTOPCFS(vfsp);
22232720Sfrankho 
22242720Sfrankho 	mutex_enter(&pcfslock);
22252720Sfrankho 	if (fsp->pcfs_fatp != (uchar_t *)0)
22262720Sfrankho 		pc_invalfat(fsp);
22272720Sfrankho 	mutex_exit(&pcfslock);
22282720Sfrankho 
22292720Sfrankho 	VN_RELE(fsp->pcfs_devvp);
22302720Sfrankho 	mutex_destroy(&fsp->pcfs_lock);
22312720Sfrankho 	kmem_free(fsp, (uint_t)sizeof (struct pcfs));
22322720Sfrankho 
22332720Sfrankho 	/*
22342720Sfrankho 	 * Allow _fini() to succeed now, if so desired.
22352720Sfrankho 	 */
22362720Sfrankho 	atomic_dec_32(&pcfs_mountcount);
22372720Sfrankho }
2238