xref: /onnv-gate/usr/src/uts/common/fs/pcfs/pc_vfsops.c (revision 5331:3047ad28a67b)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
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 /*
223898Srsb  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <sys/param.h>
290Sstevel@tonic-gate #include <sys/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>
373898Srsb #include <sys/vfs_opreg.h>
380Sstevel@tonic-gate #include <sys/vnode.h>
390Sstevel@tonic-gate #include <sys/fdio.h>
400Sstevel@tonic-gate #include <sys/file.h>
410Sstevel@tonic-gate #include <sys/uio.h>
420Sstevel@tonic-gate #include <sys/conf.h>
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>
665121Sfrankho #include <sys/sdt.h>
670Sstevel@tonic-gate 
680Sstevel@tonic-gate /*
690Sstevel@tonic-gate  * The majority of PC media use a 512 sector size, but
700Sstevel@tonic-gate  * occasionally you will run across a 1k sector size.
710Sstevel@tonic-gate  * For media with a 1k sector size, fd_strategy() requires
720Sstevel@tonic-gate  * the I/O size to be a 1k multiple; so when the sector size
730Sstevel@tonic-gate  * is not yet known, always read 1k.
740Sstevel@tonic-gate  */
750Sstevel@tonic-gate #define	PC_SAFESECSIZE	(PC_SECSIZE * 2)
760Sstevel@tonic-gate 
77201Sjmcp static int pcfs_pseudo_floppy(dev_t);
780Sstevel@tonic-gate 
790Sstevel@tonic-gate static int pcfsinit(int, char *);
800Sstevel@tonic-gate static int pcfs_mount(struct vfs *, struct vnode *, struct mounta *,
810Sstevel@tonic-gate 	struct cred *);
820Sstevel@tonic-gate static int pcfs_unmount(struct vfs *, int, struct cred *);
830Sstevel@tonic-gate static int pcfs_root(struct vfs *, struct vnode **);
840Sstevel@tonic-gate static int pcfs_statvfs(struct vfs *, struct statvfs64 *);
850Sstevel@tonic-gate static int pc_syncfsnodes(struct pcfs *);
860Sstevel@tonic-gate static int pcfs_sync(struct vfs *, short, struct cred *);
870Sstevel@tonic-gate static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp);
882720Sfrankho static void pcfs_freevfs(vfs_t *vfsp);
890Sstevel@tonic-gate 
905121Sfrankho static int pc_readfat(struct pcfs *fsp, uchar_t *fatp);
910Sstevel@tonic-gate static int pc_writefat(struct pcfs *fsp, daddr_t start);
920Sstevel@tonic-gate 
935121Sfrankho static int pc_getfattype(struct pcfs *fsp);
945121Sfrankho static void pcfs_parse_mntopts(struct pcfs *fsp, struct mounta *uap);
955121Sfrankho 
965121Sfrankho 
970Sstevel@tonic-gate /*
980Sstevel@tonic-gate  * pcfs mount options table
990Sstevel@tonic-gate  */
1000Sstevel@tonic-gate 
1012720Sfrankho static char *nohidden_cancel[] = { MNTOPT_PCFS_HIDDEN, NULL };
1022720Sfrankho static char *hidden_cancel[] = { MNTOPT_PCFS_NOHIDDEN, NULL };
1032720Sfrankho static char *nofoldcase_cancel[] = { MNTOPT_PCFS_FOLDCASE, NULL };
1042720Sfrankho static char *foldcase_cancel[] = { MNTOPT_PCFS_NOFOLDCASE, NULL };
1052720Sfrankho static char *clamptime_cancel[] = { MNTOPT_PCFS_NOCLAMPTIME, NULL };
1062720Sfrankho static char *noclamptime_cancel[] = { MNTOPT_PCFS_CLAMPTIME, NULL };
1075121Sfrankho static char *atime_cancel[] = { MNTOPT_NOATIME, NULL };
1085121Sfrankho static char *noatime_cancel[] = { MNTOPT_ATIME, NULL };
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate static mntopt_t mntopts[] = {
1110Sstevel@tonic-gate /*
1122720Sfrankho  *	option name	cancel option	default arg	flags	opt data
1130Sstevel@tonic-gate  */
1142720Sfrankho 	{ MNTOPT_PCFS_NOHIDDEN, nohidden_cancel, NULL, 0, NULL },
1152720Sfrankho 	{ MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, MO_DEFAULT, NULL },
1162720Sfrankho 	{ MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, NULL },
1172720Sfrankho 	{ MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0, NULL },
1182720Sfrankho 	{ MNTOPT_PCFS_CLAMPTIME, clamptime_cancel, NULL, MO_DEFAULT, NULL },
1195121Sfrankho 	{ MNTOPT_PCFS_NOCLAMPTIME, noclamptime_cancel, NULL, NULL, NULL },
1205121Sfrankho 	{ MNTOPT_NOATIME, noatime_cancel, NULL, NULL, NULL },
1215121Sfrankho 	{ MNTOPT_ATIME, atime_cancel, NULL, NULL, NULL },
1225121Sfrankho 	{ MNTOPT_PCFS_TIMEZONE, NULL, "+0", MO_DEFAULT | MO_HASVALUE, NULL },
1235121Sfrankho 	{ MNTOPT_PCFS_SECSIZE, NULL, NULL, MO_HASVALUE, NULL }
1240Sstevel@tonic-gate };
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate static mntopts_t pcfs_mntopts = {
1270Sstevel@tonic-gate 	sizeof (mntopts) / sizeof (mntopt_t),
1280Sstevel@tonic-gate 	mntopts
1290Sstevel@tonic-gate };
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate int pcfsdebuglevel = 0;
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate /*
1340Sstevel@tonic-gate  * pcfslock:	protects the list of mounted pc filesystems "pc_mounttab.
1350Sstevel@tonic-gate  * pcfs_lock:	(inside per filesystem structure "pcfs")
1360Sstevel@tonic-gate  *		per filesystem lock. Most of the vfsops and vnodeops are
1370Sstevel@tonic-gate  *		protected by this lock.
1380Sstevel@tonic-gate  * pcnodes_lock: protects the pcnode hash table "pcdhead", "pcfhead".
1390Sstevel@tonic-gate  *
1400Sstevel@tonic-gate  * Lock hierarchy: pcfslock > pcfs_lock > pcnodes_lock
1412720Sfrankho  *
1422720Sfrankho  * pcfs_mountcount:	used to prevent module unloads while there is still
1432720Sfrankho  *			pcfs state from a former mount hanging around. With
1442720Sfrankho  *			forced umount support, the filesystem module must not
1452720Sfrankho  *			be allowed to go away before the last VFS_FREEVFS()
1462720Sfrankho  *			call has been made.
1472720Sfrankho  *			Since this is just an atomic counter, there's no need
1482720Sfrankho  *			for locking.
1490Sstevel@tonic-gate  */
1500Sstevel@tonic-gate kmutex_t	pcfslock;
1512720Sfrankho krwlock_t	pcnodes_lock;
1522720Sfrankho uint32_t	pcfs_mountcount;
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate static int pcfstype;
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate static vfsdef_t vfw = {
1570Sstevel@tonic-gate 	VFSDEF_VERSION,
1580Sstevel@tonic-gate 	"pcfs",
1590Sstevel@tonic-gate 	pcfsinit,
1601488Srsb 	VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS,
1610Sstevel@tonic-gate 	&pcfs_mntopts
1620Sstevel@tonic-gate };
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate extern struct mod_ops mod_fsops;
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate static struct modlfs modlfs = {
1670Sstevel@tonic-gate 	&mod_fsops,
1685121Sfrankho 	"PC filesystem v1.2",
1690Sstevel@tonic-gate 	&vfw
1700Sstevel@tonic-gate };
1710Sstevel@tonic-gate 
1720Sstevel@tonic-gate static struct modlinkage modlinkage = {
1730Sstevel@tonic-gate 	MODREV_1,
1740Sstevel@tonic-gate 	&modlfs,
1750Sstevel@tonic-gate 	NULL
1760Sstevel@tonic-gate };
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate int
1790Sstevel@tonic-gate _init(void)
1800Sstevel@tonic-gate {
1810Sstevel@tonic-gate 	int	error;
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate #if !defined(lint)
1840Sstevel@tonic-gate 	/* make sure the on-disk structures are sane */
1850Sstevel@tonic-gate 	ASSERT(sizeof (struct pcdir) == 32);
1860Sstevel@tonic-gate 	ASSERT(sizeof (struct pcdir_lfn) == 32);
1870Sstevel@tonic-gate #endif
1880Sstevel@tonic-gate 	mutex_init(&pcfslock, NULL, MUTEX_DEFAULT, NULL);
1890Sstevel@tonic-gate 	rw_init(&pcnodes_lock, NULL, RW_DEFAULT, NULL);
1900Sstevel@tonic-gate 	error = mod_install(&modlinkage);
1910Sstevel@tonic-gate 	if (error) {
1920Sstevel@tonic-gate 		mutex_destroy(&pcfslock);
1930Sstevel@tonic-gate 		rw_destroy(&pcnodes_lock);
1940Sstevel@tonic-gate 	}
1950Sstevel@tonic-gate 	return (error);
1960Sstevel@tonic-gate }
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate int
1990Sstevel@tonic-gate _fini(void)
2000Sstevel@tonic-gate {
2010Sstevel@tonic-gate 	int	error;
2020Sstevel@tonic-gate 
2032720Sfrankho 	/*
2042720Sfrankho 	 * If a forcedly unmounted instance is still hanging around,
2052720Sfrankho 	 * we cannot allow the module to be unloaded because that would
2062720Sfrankho 	 * cause panics once the VFS framework decides it's time to call
2072720Sfrankho 	 * into VFS_FREEVFS().
2082720Sfrankho 	 */
2092720Sfrankho 	if (pcfs_mountcount)
2102720Sfrankho 		return (EBUSY);
2112720Sfrankho 
2120Sstevel@tonic-gate 	error = mod_remove(&modlinkage);
2130Sstevel@tonic-gate 	if (error)
2140Sstevel@tonic-gate 		return (error);
2150Sstevel@tonic-gate 	mutex_destroy(&pcfslock);
2160Sstevel@tonic-gate 	rw_destroy(&pcnodes_lock);
2170Sstevel@tonic-gate 	/*
2180Sstevel@tonic-gate 	 * Tear down the operations vectors
2190Sstevel@tonic-gate 	 */
2200Sstevel@tonic-gate 	(void) vfs_freevfsops_by_type(pcfstype);
2210Sstevel@tonic-gate 	vn_freevnodeops(pcfs_fvnodeops);
2220Sstevel@tonic-gate 	vn_freevnodeops(pcfs_dvnodeops);
2230Sstevel@tonic-gate 	return (0);
2240Sstevel@tonic-gate }
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate int
2270Sstevel@tonic-gate _info(struct modinfo *modinfop)
2280Sstevel@tonic-gate {
2290Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2300Sstevel@tonic-gate }
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate /* ARGSUSED1 */
2330Sstevel@tonic-gate static int
2340Sstevel@tonic-gate pcfsinit(int fstype, char *name)
2350Sstevel@tonic-gate {
2360Sstevel@tonic-gate 	static const fs_operation_def_t pcfs_vfsops_template[] = {
2373898Srsb 		VFSNAME_MOUNT,		{ .vfs_mount = pcfs_mount },
2383898Srsb 		VFSNAME_UNMOUNT,	{ .vfs_unmount = pcfs_unmount },
2393898Srsb 		VFSNAME_ROOT,		{ .vfs_root = pcfs_root },
2403898Srsb 		VFSNAME_STATVFS,	{ .vfs_statvfs = pcfs_statvfs },
2413898Srsb 		VFSNAME_SYNC,		{ .vfs_sync = pcfs_sync },
2423898Srsb 		VFSNAME_VGET,		{ .vfs_vget = pcfs_vget },
2433898Srsb 		VFSNAME_FREEVFS,	{ .vfs_freevfs = pcfs_freevfs },
2443898Srsb 		NULL,			NULL
2450Sstevel@tonic-gate 	};
2460Sstevel@tonic-gate 	int error;
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate 	error = vfs_setfsops(fstype, pcfs_vfsops_template, NULL);
2490Sstevel@tonic-gate 	if (error != 0) {
2500Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad vfs ops template");
2510Sstevel@tonic-gate 		return (error);
2520Sstevel@tonic-gate 	}
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate 	error = vn_make_ops("pcfs", pcfs_fvnodeops_template, &pcfs_fvnodeops);
2550Sstevel@tonic-gate 	if (error != 0) {
2560Sstevel@tonic-gate 		(void) vfs_freevfsops_by_type(fstype);
2570Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad file vnode ops template");
2580Sstevel@tonic-gate 		return (error);
2590Sstevel@tonic-gate 	}
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate 	error = vn_make_ops("pcfsd", pcfs_dvnodeops_template, &pcfs_dvnodeops);
2620Sstevel@tonic-gate 	if (error != 0) {
2630Sstevel@tonic-gate 		(void) vfs_freevfsops_by_type(fstype);
2640Sstevel@tonic-gate 		vn_freevnodeops(pcfs_fvnodeops);
2650Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad dir vnode ops template");
2660Sstevel@tonic-gate 		return (error);
2670Sstevel@tonic-gate 	}
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate 	pcfstype = fstype;
2700Sstevel@tonic-gate 	(void) pc_init();
2712720Sfrankho 	pcfs_mountcount = 0;
2720Sstevel@tonic-gate 	return (0);
2730Sstevel@tonic-gate }
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate static struct pcfs *pc_mounttab = NULL;
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate extern struct pcfs_args pc_tz;
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate /*
2800Sstevel@tonic-gate  *  Define some special logical drives we use internal to this file.
2810Sstevel@tonic-gate  */
2820Sstevel@tonic-gate #define	BOOT_PARTITION_DRIVE	99
2830Sstevel@tonic-gate #define	PRIMARY_DOS_DRIVE	1
2845121Sfrankho #define	UNPARTITIONED_DRIVE	0
2850Sstevel@tonic-gate 
2865121Sfrankho static int
2875121Sfrankho pcfs_device_identify(
2885121Sfrankho 	struct vfs *vfsp,
2895121Sfrankho 	struct mounta *uap,
2905121Sfrankho 	struct cred *cr,
2915121Sfrankho 	int *dos_ldrive,
2925121Sfrankho 	dev_t *xdev)
2935121Sfrankho {
2945121Sfrankho 	struct pathname special;
2955121Sfrankho 	char *c;
2965121Sfrankho 	struct vnode *bvp;
2975121Sfrankho 	int oflag, aflag;
2985121Sfrankho 	int error;
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate 	/*
3010Sstevel@tonic-gate 	 * Resolve path name of special file being mounted.
3020Sstevel@tonic-gate 	 */
3030Sstevel@tonic-gate 	if (error = pn_get(uap->spec, UIO_USERSPACE, &special)) {
3040Sstevel@tonic-gate 		return (error);
3050Sstevel@tonic-gate 	}
3065121Sfrankho 
3075121Sfrankho 	*dos_ldrive = -1;
3085121Sfrankho 
3090Sstevel@tonic-gate 	if (error =
3100Sstevel@tonic-gate 	    lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &bvp)) {
3110Sstevel@tonic-gate 		/*
3125121Sfrankho 		 * If there's no device node, the name specified most likely
3135121Sfrankho 		 * maps to a PCFS-style "partition specifier" to select a
3145121Sfrankho 		 * harddisk primary/logical partition. Disable floppy-specific
3155121Sfrankho 		 * checks in such cases unless an explicit :A or :B is
3165121Sfrankho 		 * requested.
3175121Sfrankho 		 */
3185121Sfrankho 
3195121Sfrankho 		/*
3202972Sfrankho 		 * Split the pathname string at the last ':' separator.
3212972Sfrankho 		 * If there's no ':' in the device name, or the ':' is the
3222972Sfrankho 		 * last character in the string, the name is invalid and
3232972Sfrankho 		 * the error from the previous lookup will be returned.
3240Sstevel@tonic-gate 		 */
3252972Sfrankho 		c = strrchr(special.pn_path, ':');
3262972Sfrankho 		if (c == NULL || strlen(c) == 0)
3272972Sfrankho 			goto devlookup_done;
3282972Sfrankho 
3292972Sfrankho 		*c++ = '\0';
3300Sstevel@tonic-gate 
3312972Sfrankho 		/*
3322972Sfrankho 		 * PCFS partition name suffixes can be:
3332972Sfrankho 		 *	- "boot" to indicate the X86BOOT partition
3342972Sfrankho 		 *	- a drive letter [c-z] for the "DOS logical drive"
3352972Sfrankho 		 *	- a drive number 1..24 for the "DOS logical drive"
3365121Sfrankho 		 *	- a "floppy name letter", 'a' or 'b' (just strip this)
3372972Sfrankho 		 */
3382972Sfrankho 		if (strcasecmp(c, "boot") == 0) {
3392972Sfrankho 			/*
3402972Sfrankho 			 * The Solaris boot partition is requested.
3412972Sfrankho 			 */
3425121Sfrankho 			*dos_ldrive = BOOT_PARTITION_DRIVE;
3432972Sfrankho 		} else if (strspn(c, "0123456789") == strlen(c)) {
3442972Sfrankho 			/*
3452972Sfrankho 			 * All digits - parse the partition number.
3462972Sfrankho 			 */
3472972Sfrankho 			long drvnum = 0;
3482972Sfrankho 
3492972Sfrankho 			if ((error = ddi_strtol(c, NULL, 10, &drvnum)) == 0) {
3500Sstevel@tonic-gate 				/*
3512972Sfrankho 				 * A number alright - in the allowed range ?
3520Sstevel@tonic-gate 				 */
3532972Sfrankho 				if (drvnum > 24 || drvnum == 0)
3545121Sfrankho 					error = ENXIO;
3550Sstevel@tonic-gate 			}
3562972Sfrankho 			if (error)
3572972Sfrankho 				goto devlookup_done;
3585121Sfrankho 			*dos_ldrive = (int)drvnum;
3592972Sfrankho 		} else if (strlen(c) == 1) {
3602972Sfrankho 			/*
3615121Sfrankho 			 * A single trailing character was specified.
3625121Sfrankho 			 *	- [c-zC-Z] means a harddisk partition, and
3635121Sfrankho 			 *	  we retrieve the partition number.
3645121Sfrankho 			 *	- [abAB] means a floppy drive, so we swallow
3655121Sfrankho 			 *	  the "drive specifier" and test later
3665121Sfrankho 			 *	  whether the physical device is a floppy or
3675121Sfrankho 			 *	  PCMCIA pseudofloppy (sram card).
3682972Sfrankho 			 */
3692972Sfrankho 			*c = tolower(*c);
3705121Sfrankho 			if (*c == 'a' || *c == 'b') {
3715121Sfrankho 				*dos_ldrive = UNPARTITIONED_DRIVE;
3725121Sfrankho 			} else if (*c < 'c' || *c > 'z') {
3735121Sfrankho 				error = ENXIO;
3742972Sfrankho 				goto devlookup_done;
3755121Sfrankho 			} else {
3765121Sfrankho 				*dos_ldrive = 1 + *c - 'c';
3772972Sfrankho 			}
3782972Sfrankho 		} else {
3792972Sfrankho 			/*
3802972Sfrankho 			 * Can't parse this - pass through previous error.
3812972Sfrankho 			 */
3822972Sfrankho 			goto devlookup_done;
3830Sstevel@tonic-gate 		}
3840Sstevel@tonic-gate 
3852972Sfrankho 
3862972Sfrankho 		error = lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW,
3872972Sfrankho 		    NULLVPP, &bvp);
3885121Sfrankho 	} else {
3895121Sfrankho 		*dos_ldrive = UNPARTITIONED_DRIVE;
3900Sstevel@tonic-gate 	}
3912972Sfrankho devlookup_done:
3920Sstevel@tonic-gate 	pn_free(&special);
3932972Sfrankho 	if (error)
3942972Sfrankho 		return (error);
3952972Sfrankho 
3965121Sfrankho 	ASSERT(*dos_ldrive >= UNPARTITIONED_DRIVE);
3975121Sfrankho 
3985121Sfrankho 	*xdev = bvp->v_rdev;
3995121Sfrankho 
4000Sstevel@tonic-gate 	/*
4010Sstevel@tonic-gate 	 * Verify caller's permission to open the device special file.
4020Sstevel@tonic-gate 	 */
4030Sstevel@tonic-gate 	if ((vfsp->vfs_flag & VFS_RDONLY) != 0 ||
4040Sstevel@tonic-gate 	    ((uap->flags & MS_RDONLY) != 0)) {
4050Sstevel@tonic-gate 		oflag = FREAD;
4060Sstevel@tonic-gate 		aflag = VREAD;
4070Sstevel@tonic-gate 	} else {
4080Sstevel@tonic-gate 		oflag = FREAD | FWRITE;
4090Sstevel@tonic-gate 		aflag = VREAD | VWRITE;
4100Sstevel@tonic-gate 	}
4115121Sfrankho 
4125121Sfrankho 	if (bvp->v_type != VBLK)
4135121Sfrankho 		error = ENOTBLK;
4145121Sfrankho 	else if (getmajor(*xdev) >= devcnt)
4155121Sfrankho 		error = ENXIO;
4165121Sfrankho 
4175121Sfrankho 	if ((error != 0) ||
418*5331Samw 	    (error = VOP_ACCESS(bvp, aflag, 0, cr, NULL)) != 0 ||
4190Sstevel@tonic-gate 	    (error = secpolicy_spec_open(cr, bvp, oflag)) != 0) {
4200Sstevel@tonic-gate 		VN_RELE(bvp);
4210Sstevel@tonic-gate 		return (error);
4220Sstevel@tonic-gate 	}
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 	VN_RELE(bvp);
4255121Sfrankho 	return (0);
4265121Sfrankho }
4275121Sfrankho 
4285121Sfrankho static int
4295121Sfrankho pcfs_device_ismounted(
4305121Sfrankho 	struct vfs *vfsp,
4315121Sfrankho 	int dos_ldrive,
4325121Sfrankho 	dev_t xdev,
4335121Sfrankho 	int *remounting,
4345121Sfrankho 	dev_t *pseudodev)
4355121Sfrankho {
4365121Sfrankho 	struct pcfs *fsp;
4375121Sfrankho 	int remount = *remounting;
4385121Sfrankho 
4390Sstevel@tonic-gate 	/*
4402972Sfrankho 	 * Ensure that this logical drive isn't already mounted, unless
4412972Sfrankho 	 * this is a REMOUNT request.
4422972Sfrankho 	 * Note: The framework will perform this check if the "...:c"
4432972Sfrankho 	 * PCFS-style "logical drive" syntax has not been used and an
4442972Sfrankho 	 * actually existing physical device is backing this filesystem.
4455121Sfrankho 	 * Once all block device drivers support PC-style partitioning,
4465121Sfrankho 	 * this codeblock can be dropped.
4470Sstevel@tonic-gate 	 */
4485121Sfrankho 	*pseudodev = xdev;
4495121Sfrankho 
4500Sstevel@tonic-gate 	if (dos_ldrive) {
4510Sstevel@tonic-gate 		mutex_enter(&pcfslock);
4520Sstevel@tonic-gate 		for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt)
4530Sstevel@tonic-gate 			if (fsp->pcfs_xdev == xdev &&
4545121Sfrankho 			    fsp->pcfs_ldrive == dos_ldrive) {
4550Sstevel@tonic-gate 				mutex_exit(&pcfslock);
4565121Sfrankho 				if (remount) {
4570Sstevel@tonic-gate 					return (0);
4580Sstevel@tonic-gate 				} else {
4590Sstevel@tonic-gate 					return (EBUSY);
4600Sstevel@tonic-gate 				}
4610Sstevel@tonic-gate 			}
4620Sstevel@tonic-gate 		/*
4630Sstevel@tonic-gate 		 * Assign a unique device number for the vfs
4640Sstevel@tonic-gate 		 * The old way (getudev() + a constantly incrementing
4650Sstevel@tonic-gate 		 * major number) was wrong because it changes vfs_dev
4660Sstevel@tonic-gate 		 * across mounts and reboots, which breaks nfs file handles.
4670Sstevel@tonic-gate 		 * UFS just uses the real dev_t. We can't do that because
4680Sstevel@tonic-gate 		 * of the way pcfs opens fdisk partitons (the :c and :d
4690Sstevel@tonic-gate 		 * partitions are on the same dev_t). Though that _might_
4700Sstevel@tonic-gate 		 * actually be ok, since the file handle contains an
4710Sstevel@tonic-gate 		 * absolute block number, it's probably better to make them
4720Sstevel@tonic-gate 		 * different. So I think we should retain the original
4730Sstevel@tonic-gate 		 * dev_t, but come up with a different minor number based
4740Sstevel@tonic-gate 		 * on the logical drive that will _always_ come up the same.
4750Sstevel@tonic-gate 		 * For now, we steal the upper 6 bits.
4760Sstevel@tonic-gate 		 */
4770Sstevel@tonic-gate #ifdef notdef
4780Sstevel@tonic-gate 		/* what should we do here? */
4790Sstevel@tonic-gate 		if (((getminor(xdev) >> 12) & 0x3F) != 0)
4800Sstevel@tonic-gate 			printf("whoops - upper bits used!\n");
4810Sstevel@tonic-gate #endif
4825121Sfrankho 		*pseudodev = makedevice(getmajor(xdev),
4835121Sfrankho 		    ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32);
4845121Sfrankho 		if (vfs_devmounting(*pseudodev, vfsp)) {
4850Sstevel@tonic-gate 			mutex_exit(&pcfslock);
4860Sstevel@tonic-gate 			return (EBUSY);
4870Sstevel@tonic-gate 		}
4885121Sfrankho 		if (vfs_devismounted(*pseudodev)) {
4890Sstevel@tonic-gate 			mutex_exit(&pcfslock);
4905121Sfrankho 			if (remount) {
4910Sstevel@tonic-gate 				return (0);
4920Sstevel@tonic-gate 			} else {
4930Sstevel@tonic-gate 				return (EBUSY);
4940Sstevel@tonic-gate 			}
4950Sstevel@tonic-gate 		}
4960Sstevel@tonic-gate 		mutex_exit(&pcfslock);
4970Sstevel@tonic-gate 	} else {
4985121Sfrankho 		*pseudodev = xdev;
4995121Sfrankho 		if (vfs_devmounting(*pseudodev, vfsp)) {
5000Sstevel@tonic-gate 			return (EBUSY);
5010Sstevel@tonic-gate 		}
5025121Sfrankho 		if (vfs_devismounted(*pseudodev))
5035121Sfrankho 			if (remount) {
5040Sstevel@tonic-gate 				return (0);
5050Sstevel@tonic-gate 			} else {
5060Sstevel@tonic-gate 				return (EBUSY);
5070Sstevel@tonic-gate 			}
5080Sstevel@tonic-gate 	}
5090Sstevel@tonic-gate 
5105121Sfrankho 	/*
5115121Sfrankho 	 * This is not a remount. Even if MS_REMOUNT was requested,
5125121Sfrankho 	 * the caller needs to proceed as it would on an ordinary
5135121Sfrankho 	 * mount.
5145121Sfrankho 	 */
5155121Sfrankho 	*remounting = 0;
5165121Sfrankho 
5175121Sfrankho 	ASSERT(*pseudodev);
5185121Sfrankho 	return (0);
5195121Sfrankho }
5205121Sfrankho 
5215121Sfrankho /*
5225121Sfrankho  * Get the PCFS-specific mount options from the VFS framework.
5235121Sfrankho  * For "timezone" and "secsize", we need to parse the number
5245121Sfrankho  * ourselves and ensure its validity.
5255121Sfrankho  * Note: "secsize" is deliberately undocumented at this time,
5265121Sfrankho  * it's a workaround for devices (particularly: lofi image files)
5275121Sfrankho  * that don't support the DKIOCGMEDIAINFO ioctl for autodetection.
5285121Sfrankho  */
5295121Sfrankho static void
5305121Sfrankho pcfs_parse_mntopts(struct pcfs *fsp, struct mounta *uap)
5315121Sfrankho {
5325121Sfrankho 	char *c;
5335121Sfrankho 	char *endptr;
5345121Sfrankho 	long l;
5355121Sfrankho 	struct vfs *vfsp = fsp->pcfs_vfs;
5365121Sfrankho 
5375121Sfrankho 	ASSERT(fsp->pcfs_secondswest == 0);
5385121Sfrankho 	ASSERT(fsp->pcfs_secsize == 0);
5395121Sfrankho 
5400Sstevel@tonic-gate 	if (uap->flags & MS_RDONLY) {
5410Sstevel@tonic-gate 		vfsp->vfs_flag |= VFS_RDONLY;
5420Sstevel@tonic-gate 		vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
5430Sstevel@tonic-gate 	}
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL))
5460Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_HIDDEN;
5470Sstevel@tonic-gate 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL))
5480Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_FOLDCASE;
5492720Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL))
5502720Sfrankho 		fsp->pcfs_flags |= PCFS_NOCLAMPTIME;
5515121Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL))
5525121Sfrankho 		fsp->pcfs_flags |= PCFS_NOATIME;
5535121Sfrankho 
5545121Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_TIMEZONE, &c)) {
5555121Sfrankho 		if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
5565121Sfrankho 		    endptr == c + strlen(c)) {
5575121Sfrankho 			/*
5585121Sfrankho 			 * A number alright - in the allowed range ?
5595121Sfrankho 			 */
5605121Sfrankho 			if (l <= -12*3600 || l >= 12*3600) {
5615121Sfrankho 				cmn_err(CE_WARN, "!pcfs: invalid use of "
5625121Sfrankho 				    "'timezone' mount option - %ld "
5635121Sfrankho 				    "is out of range. Assuming 0.", l);
5645121Sfrankho 				l = 0;
5655121Sfrankho 			}
5665121Sfrankho 		} else {
5675121Sfrankho 			cmn_err(CE_WARN, "!pcfs: invalid use of "
5685121Sfrankho 			    "'timezone' mount option - argument %s "
5695121Sfrankho 			    "is not a valid number. Assuming 0.", c);
5705121Sfrankho 			l = 0;
5715121Sfrankho 		}
5725121Sfrankho 		fsp->pcfs_secondswest = l;
5735121Sfrankho 	}
5745121Sfrankho 
5755121Sfrankho 	/*
5765121Sfrankho 	 * The "secsize=..." mount option is a workaround for the lack of
5775121Sfrankho 	 * lofi(7d) support for DKIOCGMEDIAINFO. If PCFS wants to parse the
5785121Sfrankho 	 * partition table of a disk image and it has been partitioned with
5795121Sfrankho 	 * sector sizes other than 512 bytes, we'd fail on loopback'ed disk
5805121Sfrankho 	 * images.
5815121Sfrankho 	 * That should really be fixed in lofi ... this is a workaround.
5825121Sfrankho 	 */
5835121Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_SECSIZE, &c)) {
5845121Sfrankho 		if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
5855121Sfrankho 		    endptr == c + strlen(c)) {
5865121Sfrankho 			/*
5875121Sfrankho 			 * A number alright - a valid sector size as well ?
5885121Sfrankho 			 */
5895121Sfrankho 			if (!VALID_SECSIZE(l)) {
5905121Sfrankho 				cmn_err(CE_WARN, "!pcfs: invalid use of "
5915121Sfrankho 				    "'secsize' mount option - %ld is "
5925121Sfrankho 				    "unsupported. Autodetecting.", l);
5935121Sfrankho 				l = 0;
5945121Sfrankho 			}
5955121Sfrankho 		} else {
5965121Sfrankho 			cmn_err(CE_WARN, "!pcfs: invalid use of "
5975121Sfrankho 			    "'secsize' mount option - argument %s "
5985121Sfrankho 			    "is not a valid number. Autodetecting.", c);
5995121Sfrankho 			l = 0;
6005121Sfrankho 		}
6015121Sfrankho 		fsp->pcfs_secsize = l;
6025121Sfrankho 		fsp->pcfs_sdshift = ddi_ffs(l / DEV_BSIZE) - 1;
6035121Sfrankho 	}
6045121Sfrankho }
6055121Sfrankho 
6065121Sfrankho /*
6075121Sfrankho  * vfs operations
6085121Sfrankho  */
6095121Sfrankho 
6105121Sfrankho /*
6115121Sfrankho  * pcfs_mount - backend for VFS_MOUNT() on PCFS.
6125121Sfrankho  */
6135121Sfrankho static int
6145121Sfrankho pcfs_mount(
6155121Sfrankho 	struct vfs *vfsp,
6165121Sfrankho 	struct vnode *mvp,
6175121Sfrankho 	struct mounta *uap,
6185121Sfrankho 	struct cred *cr)
6195121Sfrankho {
6205121Sfrankho 	struct pcfs *fsp;
6215121Sfrankho 	struct vnode *devvp;
6225121Sfrankho 	dev_t pseudodev;
6235121Sfrankho 	dev_t xdev;
6245121Sfrankho 	int dos_ldrive = 0;
6255121Sfrankho 	int error;
6265121Sfrankho 	int remounting;
6275121Sfrankho 
6285121Sfrankho 	if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
6295121Sfrankho 		return (error);
6305121Sfrankho 
6315121Sfrankho 	if (mvp->v_type != VDIR)
6325121Sfrankho 		return (ENOTDIR);
6335121Sfrankho 
6345121Sfrankho 	mutex_enter(&mvp->v_lock);
6355121Sfrankho 	if ((uap->flags & MS_REMOUNT) == 0 &&
6365121Sfrankho 	    (uap->flags & MS_OVERLAY) == 0 &&
6375121Sfrankho 	    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
6385121Sfrankho 		mutex_exit(&mvp->v_lock);
6395121Sfrankho 		return (EBUSY);
6405121Sfrankho 	}
6415121Sfrankho 	mutex_exit(&mvp->v_lock);
6425121Sfrankho 
6435121Sfrankho 	/*
6445121Sfrankho 	 * PCFS doesn't do mount arguments anymore - everything's a mount
6455121Sfrankho 	 * option these days. In order not to break existing callers, we
6465121Sfrankho 	 * don't reject it yet, just warn that the data (if any) is ignored.
6475121Sfrankho 	 */
6485121Sfrankho 	if (uap->datalen != 0)
6495121Sfrankho 		cmn_err(CE_WARN, "!pcfs: deprecated use of mount(2) with "
6505121Sfrankho 		    "mount argument structures instead of mount options. "
6515121Sfrankho 		    "Ignoring mount(2) 'dataptr' argument.");
6525121Sfrankho 
6535121Sfrankho 	/*
6545121Sfrankho 	 * For most filesystems, this is just a lookupname() on the
6555121Sfrankho 	 * mount pathname string. PCFS historically has to do its own
6565121Sfrankho 	 * partition table parsing because not all Solaris architectures
6575121Sfrankho 	 * support all styles of partitioning that PC media can have, and
6585121Sfrankho 	 * hence PCFS understands "device names" that don't map to actual
6595121Sfrankho 	 * physical device nodes. Parsing the "PCFS syntax" for device
6605121Sfrankho 	 * names is done in pcfs_device_identify() - see there.
6615121Sfrankho 	 *
6625121Sfrankho 	 * Once all block device drivers that can host FAT filesystems have
6635121Sfrankho 	 * been enhanced to create device nodes for all PC-style partitions,
6645121Sfrankho 	 * this code can go away.
6655121Sfrankho 	 */
6665121Sfrankho 	if (error = pcfs_device_identify(vfsp, uap, cr, &dos_ldrive, &xdev))
6675121Sfrankho 		return (error);
6685121Sfrankho 
6695121Sfrankho 	/*
6705121Sfrankho 	 * As with looking up the actual device to mount, PCFS cannot rely
6715121Sfrankho 	 * on just the checks done by vfs_ismounted() whether a given device
6725121Sfrankho 	 * is mounted already. The additional check against the "PCFS syntax"
6735121Sfrankho 	 * is done in  pcfs_device_ismounted().
6745121Sfrankho 	 */
6755121Sfrankho 	remounting = (uap->flags & MS_REMOUNT);
6765121Sfrankho 
6775121Sfrankho 	if (error = pcfs_device_ismounted(vfsp, dos_ldrive, xdev, &remounting,
6785121Sfrankho 	    &pseudodev))
6795121Sfrankho 		return (error);
6805121Sfrankho 
6815121Sfrankho 	if (remounting)
6825121Sfrankho 		return (0);
6835121Sfrankho 
6845121Sfrankho 	/*
6855121Sfrankho 	 * Mount the filesystem.
6865121Sfrankho 	 * An instance structure is required before the attempt to locate
6875121Sfrankho 	 * and parse the FAT BPB. This is because mount options may change
6885121Sfrankho 	 * the behaviour of the filesystem type matching code. Precreate
6895121Sfrankho 	 * it and fill it in to a degree that allows parsing the mount
6905121Sfrankho 	 * options.
6915121Sfrankho 	 */
6925121Sfrankho 	devvp = makespecvp(xdev, VBLK);
6935121Sfrankho 	if (IS_SWAPVP(devvp)) {
6945121Sfrankho 		VN_RELE(devvp);
6955121Sfrankho 		return (EBUSY);
6965121Sfrankho 	}
6975121Sfrankho 	error = VOP_OPEN(&devvp,
698*5331Samw 	    (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD | FWRITE, cr, NULL);
6995121Sfrankho 	if (error) {
7005121Sfrankho 		VN_RELE(devvp);
7015121Sfrankho 		return (error);
7025121Sfrankho 	}
7035121Sfrankho 
7045121Sfrankho 	fsp = kmem_zalloc(sizeof (*fsp), KM_SLEEP);
7055121Sfrankho 	fsp->pcfs_vfs = vfsp;
7065121Sfrankho 	fsp->pcfs_xdev = xdev;
7075121Sfrankho 	fsp->pcfs_devvp = devvp;
7085121Sfrankho 	fsp->pcfs_ldrive = dos_ldrive;
7095121Sfrankho 	mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL);
7105121Sfrankho 	vfsp->vfs_data = fsp;
7110Sstevel@tonic-gate 	vfsp->vfs_dev = pseudodev;
7120Sstevel@tonic-gate 	vfsp->vfs_fstype = pcfstype;
7130Sstevel@tonic-gate 	vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype);
7140Sstevel@tonic-gate 	vfsp->vfs_bcount = 0;
7150Sstevel@tonic-gate 	vfsp->vfs_bsize = fsp->pcfs_clsize;
7160Sstevel@tonic-gate 
7175121Sfrankho 	pcfs_parse_mntopts(fsp, uap);
7185121Sfrankho 
7195121Sfrankho 	/*
7205121Sfrankho 	 * This is the actual "mount" - the PCFS superblock check.
7215121Sfrankho 	 *
7225121Sfrankho 	 * Find the requested logical drive and the FAT BPB therein.
7235121Sfrankho 	 * Check device type and flag the instance if media is removeable.
7245121Sfrankho 	 *
7255121Sfrankho 	 * Initializes most members of the filesystem instance structure.
7265121Sfrankho 	 * Returns EINVAL if no valid BPB can be found. Other errors may
7275121Sfrankho 	 * occur after I/O failures, or when invalid / unparseable partition
7285121Sfrankho 	 * tables are encountered.
7295121Sfrankho 	 */
7305121Sfrankho 	if (error = pc_getfattype(fsp))
7315121Sfrankho 		goto errout;
7325121Sfrankho 
7335121Sfrankho 	/*
7345121Sfrankho 	 * Validate that we can access the FAT and that it is, to the
7355121Sfrankho 	 * degree we can verify here, self-consistent.
7365121Sfrankho 	 */
7375121Sfrankho 	if (error = pc_verify(fsp))
7385121Sfrankho 		goto errout;
7395121Sfrankho 
7405121Sfrankho 	/*
7415121Sfrankho 	 * Record the time of the mount, to return as an "approximate"
7425121Sfrankho 	 * timestamp for the FAT root directory. Since FAT roots don't
7435121Sfrankho 	 * have timestamps, this is less confusing to the user than
7445121Sfrankho 	 * claiming "zero" / Jan/01/1970.
7455121Sfrankho 	 */
7465121Sfrankho 	gethrestime(&fsp->pcfs_mounttime);
7475121Sfrankho 
7485121Sfrankho 	/*
7495121Sfrankho 	 * Fix up the mount options. Because "noatime" is made default on
7505121Sfrankho 	 * removeable media only, a fixed disk will have neither "atime"
7515121Sfrankho 	 * nor "noatime" set. We set the options explicitly depending on
7525121Sfrankho 	 * the PCFS_NOATIME flag, to inform the user of what applies.
7535121Sfrankho 	 * Mount option cancellation will take care that the mutually
7545121Sfrankho 	 * exclusive 'other' is cleared.
7555121Sfrankho 	 */
7565121Sfrankho 	vfs_setmntopt(vfsp,
7575121Sfrankho 	    fsp->pcfs_flags & PCFS_NOATIME ? MNTOPT_NOATIME : MNTOPT_ATIME,
7585121Sfrankho 	    NULL, 0);
7595121Sfrankho 
7605121Sfrankho 	/*
7615121Sfrankho 	 * All clear - insert the FS instance into PCFS' list.
7625121Sfrankho 	 */
7630Sstevel@tonic-gate 	mutex_enter(&pcfslock);
7640Sstevel@tonic-gate 	fsp->pcfs_nxt = pc_mounttab;
7650Sstevel@tonic-gate 	pc_mounttab = fsp;
7660Sstevel@tonic-gate 	mutex_exit(&pcfslock);
7672720Sfrankho 	atomic_inc_32(&pcfs_mountcount);
7680Sstevel@tonic-gate 	return (0);
7695121Sfrankho 
7705121Sfrankho errout:
7715121Sfrankho 	(void) VOP_CLOSE(devvp,
7725121Sfrankho 	    vfsp->vfs_flag & VFS_RDONLY ? FREAD : FREAD | FWRITE,
773*5331Samw 	    1, (offset_t)0, cr, NULL);
7745121Sfrankho 	VN_RELE(devvp);
7755121Sfrankho 	mutex_destroy(&fsp->pcfs_lock);
7765121Sfrankho 	kmem_free(fsp, sizeof (*fsp));
7775121Sfrankho 	return (error);
7785121Sfrankho 
7790Sstevel@tonic-gate }
7800Sstevel@tonic-gate 
7810Sstevel@tonic-gate static int
7820Sstevel@tonic-gate pcfs_unmount(
7830Sstevel@tonic-gate 	struct vfs *vfsp,
7840Sstevel@tonic-gate 	int flag,
7850Sstevel@tonic-gate 	struct cred *cr)
7860Sstevel@tonic-gate {
7870Sstevel@tonic-gate 	struct pcfs *fsp, *fsp1;
7880Sstevel@tonic-gate 
7890Sstevel@tonic-gate 	if (secpolicy_fs_unmount(cr, vfsp) != 0)
7900Sstevel@tonic-gate 		return (EPERM);
7910Sstevel@tonic-gate 
7920Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
7932720Sfrankho 
7940Sstevel@tonic-gate 	/*
7950Sstevel@tonic-gate 	 * We don't have to lock fsp because the VVFSLOCK in vfs layer will
7960Sstevel@tonic-gate 	 * prevent lookuppn from crossing the mount point.
7972720Sfrankho 	 * If this is not a forced umount request and there's ongoing I/O,
7982720Sfrankho 	 * don't allow the mount to proceed.
7990Sstevel@tonic-gate 	 */
8002720Sfrankho 	if (flag & MS_FORCE)
8012720Sfrankho 		vfsp->vfs_flag |= VFS_UNMOUNTED;
8022720Sfrankho 	else if (fsp->pcfs_nrefs)
8030Sstevel@tonic-gate 		return (EBUSY);
8042720Sfrankho 
8052720Sfrankho 	mutex_enter(&pcfslock);
8060Sstevel@tonic-gate 
8070Sstevel@tonic-gate 	/*
8082720Sfrankho 	 * If this is a forced umount request or if the fs instance has
8092720Sfrankho 	 * been marked as beyond recovery, allow the umount to proceed
8102720Sfrankho 	 * regardless of state. pc_diskchanged() forcibly releases all
8112720Sfrankho 	 * inactive vnodes/pcnodes.
8120Sstevel@tonic-gate 	 */
8132720Sfrankho 	if (flag & MS_FORCE || fsp->pcfs_flags & PCFS_IRRECOV) {
8140Sstevel@tonic-gate 		rw_enter(&pcnodes_lock, RW_WRITER);
8150Sstevel@tonic-gate 		pc_diskchanged(fsp);
8160Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
8170Sstevel@tonic-gate 	}
8180Sstevel@tonic-gate 
8190Sstevel@tonic-gate 	/* now there should be no pcp node on pcfhead or pcdhead. */
8200Sstevel@tonic-gate 
8210Sstevel@tonic-gate 	if (fsp == pc_mounttab) {
8220Sstevel@tonic-gate 		pc_mounttab = fsp->pcfs_nxt;
8230Sstevel@tonic-gate 	} else {
8240Sstevel@tonic-gate 		for (fsp1 = pc_mounttab; fsp1 != NULL; fsp1 = fsp1->pcfs_nxt)
8250Sstevel@tonic-gate 			if (fsp1->pcfs_nxt == fsp)
8260Sstevel@tonic-gate 				fsp1->pcfs_nxt = fsp->pcfs_nxt;
8270Sstevel@tonic-gate 	}
8280Sstevel@tonic-gate 
8290Sstevel@tonic-gate 	mutex_exit(&pcfslock);
8300Sstevel@tonic-gate 
8312720Sfrankho 	/*
8322720Sfrankho 	 * Since we support VFS_FREEVFS(), there's no need to
8332720Sfrankho 	 * free the fsp right now. The framework will tell us
8342720Sfrankho 	 * when the right time to do so has arrived by calling
8352720Sfrankho 	 * into pcfs_freevfs.
8362720Sfrankho 	 */
8370Sstevel@tonic-gate 	return (0);
8380Sstevel@tonic-gate }
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate /*
8410Sstevel@tonic-gate  * find root of pcfs
8420Sstevel@tonic-gate  */
8430Sstevel@tonic-gate static int
8440Sstevel@tonic-gate pcfs_root(
8450Sstevel@tonic-gate 	struct vfs *vfsp,
8460Sstevel@tonic-gate 	struct vnode **vpp)
8470Sstevel@tonic-gate {
8480Sstevel@tonic-gate 	struct pcfs *fsp;
8490Sstevel@tonic-gate 	struct pcnode *pcp;
8500Sstevel@tonic-gate 	int error;
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
8530Sstevel@tonic-gate 	if (error = pc_lockfs(fsp, 0, 0))
8540Sstevel@tonic-gate 		return (error);
8552720Sfrankho 
8560Sstevel@tonic-gate 	pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
8570Sstevel@tonic-gate 	pc_unlockfs(fsp);
8580Sstevel@tonic-gate 	*vpp = PCTOV(pcp);
8590Sstevel@tonic-gate 	pcp->pc_flags |= PC_EXTERNAL;
8600Sstevel@tonic-gate 	return (0);
8610Sstevel@tonic-gate }
8620Sstevel@tonic-gate 
8630Sstevel@tonic-gate /*
8640Sstevel@tonic-gate  * Get file system statistics.
8650Sstevel@tonic-gate  */
8660Sstevel@tonic-gate static int
8670Sstevel@tonic-gate pcfs_statvfs(
8680Sstevel@tonic-gate 	struct vfs *vfsp,
8690Sstevel@tonic-gate 	struct statvfs64 *sp)
8700Sstevel@tonic-gate {
8710Sstevel@tonic-gate 	struct pcfs *fsp;
8720Sstevel@tonic-gate 	int error;
8730Sstevel@tonic-gate 	dev32_t d32;
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
8760Sstevel@tonic-gate 	error = pc_getfat(fsp);
8770Sstevel@tonic-gate 	if (error)
8780Sstevel@tonic-gate 		return (error);
8790Sstevel@tonic-gate 	bzero(sp, sizeof (*sp));
8800Sstevel@tonic-gate 	sp->f_bsize = sp->f_frsize = fsp->pcfs_clsize;
8810Sstevel@tonic-gate 	sp->f_blocks = (fsblkcnt64_t)fsp->pcfs_ncluster;
8820Sstevel@tonic-gate 	sp->f_bavail = sp->f_bfree = (fsblkcnt64_t)pc_freeclusters(fsp);
8830Sstevel@tonic-gate 	sp->f_files = (fsfilcnt64_t)-1;
8840Sstevel@tonic-gate 	sp->f_ffree = (fsfilcnt64_t)-1;
8850Sstevel@tonic-gate 	sp->f_favail = (fsfilcnt64_t)-1;
8860Sstevel@tonic-gate #ifdef notdef
8870Sstevel@tonic-gate 	(void) cmpldev(&d32, fsp->pcfs_devvp->v_rdev);
8880Sstevel@tonic-gate #endif /* notdef */
8890Sstevel@tonic-gate 	(void) cmpldev(&d32, vfsp->vfs_dev);
8900Sstevel@tonic-gate 	sp->f_fsid = d32;
8910Sstevel@tonic-gate 	(void) strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name);
8920Sstevel@tonic-gate 	sp->f_flag = vf_to_stf(vfsp->vfs_flag);
8930Sstevel@tonic-gate 	sp->f_namemax = PCFNAMESIZE;
8940Sstevel@tonic-gate 	return (0);
8950Sstevel@tonic-gate }
8960Sstevel@tonic-gate 
8970Sstevel@tonic-gate static int
8980Sstevel@tonic-gate pc_syncfsnodes(struct pcfs *fsp)
8990Sstevel@tonic-gate {
9000Sstevel@tonic-gate 	struct pchead *hp;
9010Sstevel@tonic-gate 	struct pcnode *pcp;
9020Sstevel@tonic-gate 	int error;
9030Sstevel@tonic-gate 
9040Sstevel@tonic-gate 	if (error = pc_lockfs(fsp, 0, 0))
9050Sstevel@tonic-gate 		return (error);
9060Sstevel@tonic-gate 
9070Sstevel@tonic-gate 	if (!(error = pc_syncfat(fsp))) {
9080Sstevel@tonic-gate 		hp = pcfhead;
9090Sstevel@tonic-gate 		while (hp < & pcfhead [ NPCHASH ]) {
9100Sstevel@tonic-gate 			rw_enter(&pcnodes_lock, RW_READER);
9110Sstevel@tonic-gate 			pcp = hp->pch_forw;
9120Sstevel@tonic-gate 			while (pcp != (struct pcnode *)hp) {
9130Sstevel@tonic-gate 				if (VFSTOPCFS(PCTOV(pcp) -> v_vfsp) == fsp)
9140Sstevel@tonic-gate 					if (error = pc_nodesync(pcp))
9150Sstevel@tonic-gate 						break;
9160Sstevel@tonic-gate 				pcp = pcp -> pc_forw;
9170Sstevel@tonic-gate 			}
9180Sstevel@tonic-gate 			rw_exit(&pcnodes_lock);
9190Sstevel@tonic-gate 			if (error)
9200Sstevel@tonic-gate 				break;
9210Sstevel@tonic-gate 			hp++;
9220Sstevel@tonic-gate 		}
9230Sstevel@tonic-gate 	}
9240Sstevel@tonic-gate 	pc_unlockfs(fsp);
9250Sstevel@tonic-gate 	return (error);
9260Sstevel@tonic-gate }
9270Sstevel@tonic-gate 
9280Sstevel@tonic-gate /*
9290Sstevel@tonic-gate  * Flush any pending I/O.
9300Sstevel@tonic-gate  */
9310Sstevel@tonic-gate /*ARGSUSED*/
9320Sstevel@tonic-gate static int
9330Sstevel@tonic-gate pcfs_sync(
9340Sstevel@tonic-gate 	struct vfs *vfsp,
9350Sstevel@tonic-gate 	short flag,
9360Sstevel@tonic-gate 	struct cred *cr)
9370Sstevel@tonic-gate {
9380Sstevel@tonic-gate 	struct pcfs *fsp;
9390Sstevel@tonic-gate 	int error = 0;
9400Sstevel@tonic-gate 
9410Sstevel@tonic-gate 	/* this prevents the filesystem from being umounted. */
9420Sstevel@tonic-gate 	mutex_enter(&pcfslock);
9430Sstevel@tonic-gate 	if (vfsp != NULL) {
9440Sstevel@tonic-gate 		fsp = VFSTOPCFS(vfsp);
9450Sstevel@tonic-gate 		if (!(fsp->pcfs_flags & PCFS_IRRECOV)) {
9460Sstevel@tonic-gate 			error = pc_syncfsnodes(fsp);
9470Sstevel@tonic-gate 		} else {
9480Sstevel@tonic-gate 			rw_enter(&pcnodes_lock, RW_WRITER);
9490Sstevel@tonic-gate 			pc_diskchanged(fsp);
9500Sstevel@tonic-gate 			rw_exit(&pcnodes_lock);
9510Sstevel@tonic-gate 			error = EIO;
9520Sstevel@tonic-gate 		}
9530Sstevel@tonic-gate 	} else {
9540Sstevel@tonic-gate 		fsp = pc_mounttab;
9550Sstevel@tonic-gate 		while (fsp != NULL) {
9560Sstevel@tonic-gate 			if (fsp->pcfs_flags & PCFS_IRRECOV) {
9570Sstevel@tonic-gate 				rw_enter(&pcnodes_lock, RW_WRITER);
9580Sstevel@tonic-gate 				pc_diskchanged(fsp);
9590Sstevel@tonic-gate 				rw_exit(&pcnodes_lock);
9600Sstevel@tonic-gate 				error = EIO;
9610Sstevel@tonic-gate 				break;
9620Sstevel@tonic-gate 			}
9630Sstevel@tonic-gate 			error = pc_syncfsnodes(fsp);
9640Sstevel@tonic-gate 			if (error) break;
9650Sstevel@tonic-gate 			fsp = fsp->pcfs_nxt;
9660Sstevel@tonic-gate 		}
9670Sstevel@tonic-gate 	}
9680Sstevel@tonic-gate 	mutex_exit(&pcfslock);
9690Sstevel@tonic-gate 	return (error);
9700Sstevel@tonic-gate }
9710Sstevel@tonic-gate 
9720Sstevel@tonic-gate int
9730Sstevel@tonic-gate pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing)
9740Sstevel@tonic-gate {
9752720Sfrankho 	int err;
9762720Sfrankho 
9770Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_IRRECOV) && !releasing)
9780Sstevel@tonic-gate 		return (EIO);
9790Sstevel@tonic-gate 
9800Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_LOCKED) && (fsp->pcfs_owner == curthread)) {
9810Sstevel@tonic-gate 		fsp->pcfs_count++;
9820Sstevel@tonic-gate 	} else {
9830Sstevel@tonic-gate 		mutex_enter(&fsp->pcfs_lock);
9840Sstevel@tonic-gate 		if (fsp->pcfs_flags & PCFS_LOCKED)
9850Sstevel@tonic-gate 			panic("pc_lockfs");
9860Sstevel@tonic-gate 		/*
9870Sstevel@tonic-gate 		 * We check the IRRECOV bit again just in case somebody
9880Sstevel@tonic-gate 		 * snuck past the initial check but then got held up before
9890Sstevel@tonic-gate 		 * they could grab the lock.  (And in the meantime someone
9900Sstevel@tonic-gate 		 * had grabbed the lock and set the bit)
9910Sstevel@tonic-gate 		 */
992607Swyllys 		if (!diskchanged && !(fsp->pcfs_flags & PCFS_IRRECOV)) {
9932720Sfrankho 			if ((err = pc_getfat(fsp))) {
9942720Sfrankho 				mutex_exit(&fsp->pcfs_lock);
995607Swyllys 				return (err);
9962720Sfrankho 			}
997607Swyllys 		}
9980Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_LOCKED;
9990Sstevel@tonic-gate 		fsp->pcfs_owner = curthread;
10000Sstevel@tonic-gate 		fsp->pcfs_count++;
10010Sstevel@tonic-gate 	}
10020Sstevel@tonic-gate 	return (0);
10030Sstevel@tonic-gate }
10040Sstevel@tonic-gate 
10050Sstevel@tonic-gate void
10060Sstevel@tonic-gate pc_unlockfs(struct pcfs *fsp)
10070Sstevel@tonic-gate {
10080Sstevel@tonic-gate 
10090Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_LOCKED) == 0)
10100Sstevel@tonic-gate 		panic("pc_unlockfs");
10110Sstevel@tonic-gate 	if (--fsp->pcfs_count < 0)
10120Sstevel@tonic-gate 		panic("pc_unlockfs: count");
10130Sstevel@tonic-gate 	if (fsp->pcfs_count == 0) {
10140Sstevel@tonic-gate 		fsp->pcfs_flags &= ~PCFS_LOCKED;
10150Sstevel@tonic-gate 		fsp->pcfs_owner = 0;
10160Sstevel@tonic-gate 		mutex_exit(&fsp->pcfs_lock);
10170Sstevel@tonic-gate 	}
10180Sstevel@tonic-gate }
10190Sstevel@tonic-gate 
10200Sstevel@tonic-gate int
10210Sstevel@tonic-gate pc_syncfat(struct pcfs *fsp)
10220Sstevel@tonic-gate {
10230Sstevel@tonic-gate 	struct buf *bp;
10240Sstevel@tonic-gate 	int nfat;
10255121Sfrankho 	int	error = 0;
10265121Sfrankho 	struct fat_od_fsi *fsinfo_disk;
10270Sstevel@tonic-gate 
10280Sstevel@tonic-gate 	if ((fsp->pcfs_fatp == (uchar_t *)0) ||
10290Sstevel@tonic-gate 	    !(fsp->pcfs_flags & PCFS_FATMOD))
10300Sstevel@tonic-gate 		return (0);
10310Sstevel@tonic-gate 	/*
10320Sstevel@tonic-gate 	 * write out all copies of FATs
10330Sstevel@tonic-gate 	 */
10340Sstevel@tonic-gate 	fsp->pcfs_flags &= ~PCFS_FATMOD;
10350Sstevel@tonic-gate 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
10360Sstevel@tonic-gate 	for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) {
10375121Sfrankho 		error = pc_writefat(fsp, pc_dbdaddr(fsp,
10385121Sfrankho 		    fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec));
10390Sstevel@tonic-gate 		if (error) {
10400Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
10410Sstevel@tonic-gate 			return (EIO);
10420Sstevel@tonic-gate 		}
10430Sstevel@tonic-gate 	}
10440Sstevel@tonic-gate 	pc_clear_fatchanges(fsp);
10455121Sfrankho 
10465121Sfrankho 	/*
10475121Sfrankho 	 * Write out fsinfo sector.
10485121Sfrankho 	 */
10490Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
10500Sstevel@tonic-gate 		bp = bread(fsp->pcfs_xdev,
10515121Sfrankho 		    pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
10520Sstevel@tonic-gate 		if (bp->b_flags & (B_ERROR | B_STALE)) {
10535121Sfrankho 			error = geterror(bp);
10540Sstevel@tonic-gate 		}
10555121Sfrankho 		fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
10565121Sfrankho 		if (!error && FSISIG_OK(fsinfo_disk)) {
10575121Sfrankho 			fsinfo_disk->fsi_incore.fs_free_clusters =
10585121Sfrankho 			    LE_32(fsp->pcfs_fsinfo.fs_free_clusters);
10595121Sfrankho 			fsinfo_disk->fsi_incore.fs_next_free =
10605121Sfrankho 			    LE_32(FSINFO_UNKNOWN);
10615121Sfrankho 			bwrite2(bp);
10625121Sfrankho 			error = geterror(bp);
10635121Sfrankho 		}
10640Sstevel@tonic-gate 		brelse(bp);
10650Sstevel@tonic-gate 		if (error) {
10660Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
10670Sstevel@tonic-gate 			return (EIO);
10680Sstevel@tonic-gate 		}
10690Sstevel@tonic-gate 	}
10700Sstevel@tonic-gate 	return (0);
10710Sstevel@tonic-gate }
10720Sstevel@tonic-gate 
10730Sstevel@tonic-gate void
10740Sstevel@tonic-gate pc_invalfat(struct pcfs *fsp)
10750Sstevel@tonic-gate {
10760Sstevel@tonic-gate 	struct pcfs *xfsp;
10770Sstevel@tonic-gate 	int mount_cnt = 0;
10780Sstevel@tonic-gate 
10790Sstevel@tonic-gate 	if (fsp->pcfs_fatp == (uchar_t *)0)
10800Sstevel@tonic-gate 		panic("pc_invalfat");
10810Sstevel@tonic-gate 	/*
10820Sstevel@tonic-gate 	 * Release FAT
10830Sstevel@tonic-gate 	 */
10845121Sfrankho 	kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsec * fsp->pcfs_secsize);
10850Sstevel@tonic-gate 	fsp->pcfs_fatp = NULL;
10860Sstevel@tonic-gate 	kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize);
10870Sstevel@tonic-gate 	fsp->pcfs_fat_changemap = NULL;
10880Sstevel@tonic-gate 	/*
10890Sstevel@tonic-gate 	 * Invalidate all the blocks associated with the device.
10900Sstevel@tonic-gate 	 * Not needed if stateless.
10910Sstevel@tonic-gate 	 */
10920Sstevel@tonic-gate 	for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt)
10930Sstevel@tonic-gate 		if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev)
10940Sstevel@tonic-gate 			mount_cnt++;
10950Sstevel@tonic-gate 
10960Sstevel@tonic-gate 	if (!mount_cnt)
10970Sstevel@tonic-gate 		binval(fsp->pcfs_xdev);
10980Sstevel@tonic-gate 	/*
10990Sstevel@tonic-gate 	 * close mounted device
11000Sstevel@tonic-gate 	 */
11010Sstevel@tonic-gate 	(void) VOP_CLOSE(fsp->pcfs_devvp,
11020Sstevel@tonic-gate 	    (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
1103*5331Samw 	    1, (offset_t)0, CRED(), NULL);
11040Sstevel@tonic-gate }
11050Sstevel@tonic-gate 
11060Sstevel@tonic-gate void
11070Sstevel@tonic-gate pc_badfs(struct pcfs *fsp)
11080Sstevel@tonic-gate {
11095121Sfrankho 	cmn_err(CE_WARN, "corrupted PC file system on dev (%x.%x):%d\n",
11100Sstevel@tonic-gate 	    getmajor(fsp->pcfs_devvp->v_rdev),
11115121Sfrankho 	    getminor(fsp->pcfs_devvp->v_rdev), fsp->pcfs_ldrive);
11120Sstevel@tonic-gate }
11130Sstevel@tonic-gate 
11140Sstevel@tonic-gate /*
11150Sstevel@tonic-gate  * The problem with supporting NFS on the PCFS filesystem is that there
11160Sstevel@tonic-gate  * is no good place to keep the generation number. The only possible
11170Sstevel@tonic-gate  * place is inside a directory entry. There are a few words that we
11180Sstevel@tonic-gate  * don't use - they store NT & OS/2 attributes, and the creation/last access
11190Sstevel@tonic-gate  * time of the file - but it seems wrong to use them. In addition, directory
11200Sstevel@tonic-gate  * entries come and go. If a directory is removed completely, its directory
11210Sstevel@tonic-gate  * blocks are freed and the generation numbers are lost. Whereas in ufs,
11220Sstevel@tonic-gate  * inode blocks are dedicated for inodes, so the generation numbers are
11230Sstevel@tonic-gate  * permanently kept on the disk.
11240Sstevel@tonic-gate  */
11250Sstevel@tonic-gate static int
11260Sstevel@tonic-gate pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp)
11270Sstevel@tonic-gate {
11280Sstevel@tonic-gate 	struct pcnode *pcp;
11290Sstevel@tonic-gate 	struct pc_fid *pcfid;
11300Sstevel@tonic-gate 	struct pcfs *fsp;
11310Sstevel@tonic-gate 	struct pcdir *ep;
11320Sstevel@tonic-gate 	daddr_t eblkno;
11330Sstevel@tonic-gate 	int eoffset;
11340Sstevel@tonic-gate 	struct buf *bp;
11350Sstevel@tonic-gate 	int error;
11360Sstevel@tonic-gate 	pc_cluster32_t	cn;
11370Sstevel@tonic-gate 
11380Sstevel@tonic-gate 	pcfid = (struct pc_fid *)fidp;
11390Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
11400Sstevel@tonic-gate 
11410Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
11420Sstevel@tonic-gate 	if (error) {
11430Sstevel@tonic-gate 		*vpp = NULL;
11440Sstevel@tonic-gate 		return (error);
11450Sstevel@tonic-gate 	}
11460Sstevel@tonic-gate 
11470Sstevel@tonic-gate 	if (pcfid->pcfid_block == 0) {
11480Sstevel@tonic-gate 		pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
11490Sstevel@tonic-gate 		pcp->pc_flags |= PC_EXTERNAL;
11500Sstevel@tonic-gate 		*vpp = PCTOV(pcp);
11510Sstevel@tonic-gate 		pc_unlockfs(fsp);
11520Sstevel@tonic-gate 		return (0);
11530Sstevel@tonic-gate 	}
11540Sstevel@tonic-gate 	eblkno = pcfid->pcfid_block;
11550Sstevel@tonic-gate 	eoffset = pcfid->pcfid_offset;
11565121Sfrankho 
11570Sstevel@tonic-gate 	if ((pc_dbtocl(fsp,
11580Sstevel@tonic-gate 	    eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) ||
11590Sstevel@tonic-gate 	    (eoffset > fsp->pcfs_clsize)) {
11600Sstevel@tonic-gate 		pc_unlockfs(fsp);
11610Sstevel@tonic-gate 		*vpp = NULL;
11620Sstevel@tonic-gate 		return (EINVAL);
11630Sstevel@tonic-gate 	}
11640Sstevel@tonic-gate 
11655121Sfrankho 	if (eblkno >= fsp->pcfs_datastart || (eblkno - fsp->pcfs_rdirstart)
11660Sstevel@tonic-gate 	    < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
11675121Sfrankho 		bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
11685121Sfrankho 		    fsp->pcfs_clsize);
11690Sstevel@tonic-gate 	} else {
11705121Sfrankho 		/*
11715121Sfrankho 		 * This is an access "backwards" into the FAT12/FAT16
11725121Sfrankho 		 * root directory. A better code structure would
11735121Sfrankho 		 * significantly improve maintainability here ...
11745121Sfrankho 		 */
11755121Sfrankho 		bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
11760Sstevel@tonic-gate 		    (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize);
11770Sstevel@tonic-gate 	}
11780Sstevel@tonic-gate 	if (bp->b_flags & (B_ERROR | B_STALE)) {
11790Sstevel@tonic-gate 		error = geterror(bp);
11800Sstevel@tonic-gate 		brelse(bp);
11810Sstevel@tonic-gate 		if (error)
11820Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
11830Sstevel@tonic-gate 		*vpp = NULL;
11840Sstevel@tonic-gate 		pc_unlockfs(fsp);
11850Sstevel@tonic-gate 		return (error);
11860Sstevel@tonic-gate 	}
11870Sstevel@tonic-gate 	ep = (struct pcdir *)(bp->b_un.b_addr + eoffset);
11880Sstevel@tonic-gate 	/*
11890Sstevel@tonic-gate 	 * Ok, if this is a valid file handle that we gave out,
11900Sstevel@tonic-gate 	 * then simply ensuring that the creation time matches,
11910Sstevel@tonic-gate 	 * the entry has not been deleted, and it has a valid first
11920Sstevel@tonic-gate 	 * character should be enough.
11930Sstevel@tonic-gate 	 *
11940Sstevel@tonic-gate 	 * Unfortunately, verifying that the <blkno, offset> _still_
11950Sstevel@tonic-gate 	 * refers to a directory entry is not easy, since we'd have
11960Sstevel@tonic-gate 	 * to search _all_ directories starting from root to find it.
11970Sstevel@tonic-gate 	 * That's a high price to pay just in case somebody is forging
11980Sstevel@tonic-gate 	 * file handles. So instead we verify that as much of the
11990Sstevel@tonic-gate 	 * entry is valid as we can:
12000Sstevel@tonic-gate 	 *
12010Sstevel@tonic-gate 	 * 1. The starting cluster is 0 (unallocated) or valid
12020Sstevel@tonic-gate 	 * 2. It is not an LFN entry
12030Sstevel@tonic-gate 	 * 3. It is not hidden (unless mounted as such)
12040Sstevel@tonic-gate 	 * 4. It is not the label
12050Sstevel@tonic-gate 	 */
12060Sstevel@tonic-gate 	cn = pc_getstartcluster(fsp, ep);
12070Sstevel@tonic-gate 	/*
12080Sstevel@tonic-gate 	 * if the starting cluster is valid, but not valid according
12090Sstevel@tonic-gate 	 * to pc_validcl(), force it to be to simplify the following if.
12100Sstevel@tonic-gate 	 */
12110Sstevel@tonic-gate 	if (cn == 0)
12120Sstevel@tonic-gate 		cn = PCF_FIRSTCLUSTER;
12130Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
12140Sstevel@tonic-gate 		if (cn >= PCF_LASTCLUSTER32)
12150Sstevel@tonic-gate 			cn = PCF_FIRSTCLUSTER;
12160Sstevel@tonic-gate 	} else {
12170Sstevel@tonic-gate 		if (cn >= PCF_LASTCLUSTER)
12180Sstevel@tonic-gate 			cn = PCF_FIRSTCLUSTER;
12190Sstevel@tonic-gate 	}
12200Sstevel@tonic-gate 	if ((!pc_validcl(fsp, cn)) ||
12210Sstevel@tonic-gate 	    (PCDL_IS_LFN(ep)) ||
12220Sstevel@tonic-gate 	    (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) ||
12230Sstevel@tonic-gate 	    ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) {
12240Sstevel@tonic-gate 		bp->b_flags |= B_STALE | B_AGE;
12250Sstevel@tonic-gate 		brelse(bp);
12260Sstevel@tonic-gate 		pc_unlockfs(fsp);
12270Sstevel@tonic-gate 		return (EINVAL);
12280Sstevel@tonic-gate 	}
12290Sstevel@tonic-gate 	if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) &&
12300Sstevel@tonic-gate 	    (ep->pcd_filename[0] != PCD_ERASED) &&
12310Sstevel@tonic-gate 	    (pc_validchar(ep->pcd_filename[0]) ||
12325121Sfrankho 	    (ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) {
12330Sstevel@tonic-gate 		pcp = pc_getnode(fsp, eblkno, eoffset, ep);
12340Sstevel@tonic-gate 		pcp->pc_flags |= PC_EXTERNAL;
12350Sstevel@tonic-gate 		*vpp = PCTOV(pcp);
12360Sstevel@tonic-gate 	} else {
12370Sstevel@tonic-gate 		*vpp = NULL;
12380Sstevel@tonic-gate 	}
12390Sstevel@tonic-gate 	bp->b_flags |= B_STALE | B_AGE;
12400Sstevel@tonic-gate 	brelse(bp);
12410Sstevel@tonic-gate 	pc_unlockfs(fsp);
12420Sstevel@tonic-gate 	return (0);
12430Sstevel@tonic-gate }
12440Sstevel@tonic-gate 
12450Sstevel@tonic-gate /*
12460Sstevel@tonic-gate  * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about
12470Sstevel@tonic-gate  * a meg), so we can't bread() it all in at once. This routine reads a
12480Sstevel@tonic-gate  * fat a chunk at a time.
12490Sstevel@tonic-gate  */
12500Sstevel@tonic-gate static int
12515121Sfrankho pc_readfat(struct pcfs *fsp, uchar_t *fatp)
12520Sstevel@tonic-gate {
12530Sstevel@tonic-gate 	struct buf *bp;
12540Sstevel@tonic-gate 	size_t off;
12550Sstevel@tonic-gate 	size_t readsize;
12565121Sfrankho 	daddr_t diskblk;
12575121Sfrankho 	size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
12585121Sfrankho 	daddr_t start = fsp->pcfs_fatstart;
12590Sstevel@tonic-gate 
12600Sstevel@tonic-gate 	readsize = fsp->pcfs_clsize;
12610Sstevel@tonic-gate 	for (off = 0; off < fatsize; off += readsize, fatp += readsize) {
12620Sstevel@tonic-gate 		if (readsize > (fatsize - off))
12630Sstevel@tonic-gate 			readsize = fatsize - off;
12645121Sfrankho 		diskblk = pc_dbdaddr(fsp, start +
12655121Sfrankho 		    pc_cltodb(fsp, pc_lblkno(fsp, off)));
12665121Sfrankho 		bp = bread(fsp->pcfs_xdev, diskblk, readsize);
12670Sstevel@tonic-gate 		if (bp->b_flags & (B_ERROR | B_STALE)) {
12680Sstevel@tonic-gate 			brelse(bp);
12690Sstevel@tonic-gate 			return (EIO);
12700Sstevel@tonic-gate 		}
12710Sstevel@tonic-gate 		bp->b_flags |= B_STALE | B_AGE;
12720Sstevel@tonic-gate 		bcopy(bp->b_un.b_addr, fatp, readsize);
12730Sstevel@tonic-gate 		brelse(bp);
12740Sstevel@tonic-gate 	}
12750Sstevel@tonic-gate 	return (0);
12760Sstevel@tonic-gate }
12770Sstevel@tonic-gate 
12780Sstevel@tonic-gate /*
12790Sstevel@tonic-gate  * We write the FAT out a _lot_, in order to make sure that it
12800Sstevel@tonic-gate  * is up-to-date. But on a FAT32 system (large drive, small clusters)
12810Sstevel@tonic-gate  * the FAT might be a couple of megabytes, and writing it all out just
12820Sstevel@tonic-gate  * because we created or deleted a small file is painful (especially
12830Sstevel@tonic-gate  * since we do it for each alternate FAT too). So instead, for FAT16 and
12840Sstevel@tonic-gate  * FAT32 we only write out the bit that has changed. We don't clear
12850Sstevel@tonic-gate  * the 'updated' fields here because the caller might be writing out
12860Sstevel@tonic-gate  * several FATs, so the caller must use pc_clear_fatchanges() after
12870Sstevel@tonic-gate  * all FATs have been updated.
12885121Sfrankho  * This function doesn't take "start" from fsp->pcfs_dosstart because
12895121Sfrankho  * callers can use it to write either the primary or any of the alternate
12905121Sfrankho  * FAT tables.
12910Sstevel@tonic-gate  */
12920Sstevel@tonic-gate static int
12930Sstevel@tonic-gate pc_writefat(struct pcfs *fsp, daddr_t start)
12940Sstevel@tonic-gate {
12950Sstevel@tonic-gate 	struct buf *bp;
12960Sstevel@tonic-gate 	size_t off;
12970Sstevel@tonic-gate 	size_t writesize;
12980Sstevel@tonic-gate 	int	error;
12990Sstevel@tonic-gate 	uchar_t *fatp = fsp->pcfs_fatp;
13005121Sfrankho 	size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
13010Sstevel@tonic-gate 
13020Sstevel@tonic-gate 	writesize = fsp->pcfs_clsize;
13030Sstevel@tonic-gate 	for (off = 0; off < fatsize; off += writesize, fatp += writesize) {
13040Sstevel@tonic-gate 		if (writesize > (fatsize - off))
13050Sstevel@tonic-gate 			writesize = fatsize - off;
13060Sstevel@tonic-gate 		if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) {
13070Sstevel@tonic-gate 			continue;
13080Sstevel@tonic-gate 		}
13090Sstevel@tonic-gate 		bp = ngeteblk(writesize);
13100Sstevel@tonic-gate 		bp->b_edev = fsp->pcfs_xdev;
13110Sstevel@tonic-gate 		bp->b_dev = cmpdev(bp->b_edev);
13120Sstevel@tonic-gate 		bp->b_blkno = pc_dbdaddr(fsp, start +
13130Sstevel@tonic-gate 		    pc_cltodb(fsp, pc_lblkno(fsp, off)));
13140Sstevel@tonic-gate 		bcopy(fatp, bp->b_un.b_addr, writesize);
13150Sstevel@tonic-gate 		bwrite2(bp);
13160Sstevel@tonic-gate 		error = geterror(bp);
13170Sstevel@tonic-gate 		brelse(bp);
13180Sstevel@tonic-gate 		if (error) {
13190Sstevel@tonic-gate 			return (error);
13200Sstevel@tonic-gate 		}
13210Sstevel@tonic-gate 	}
13220Sstevel@tonic-gate 	return (0);
13230Sstevel@tonic-gate }
13240Sstevel@tonic-gate 
13250Sstevel@tonic-gate /*
13260Sstevel@tonic-gate  * Mark the FAT cluster that 'cn' is stored in as modified.
13270Sstevel@tonic-gate  */
13280Sstevel@tonic-gate void
13290Sstevel@tonic-gate pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn)
13300Sstevel@tonic-gate {
13310Sstevel@tonic-gate 	pc_cluster32_t	bn;
13320Sstevel@tonic-gate 	size_t		size;
13330Sstevel@tonic-gate 
13340Sstevel@tonic-gate 	/* which fat block is the cluster number stored in? */
13350Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
13360Sstevel@tonic-gate 		size = sizeof (pc_cluster32_t);
13370Sstevel@tonic-gate 		bn = pc_lblkno(fsp, cn * size);
13380Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
13390Sstevel@tonic-gate 	} else if (IS_FAT16(fsp)) {
13400Sstevel@tonic-gate 		size = sizeof (pc_cluster16_t);
13410Sstevel@tonic-gate 		bn = pc_lblkno(fsp, cn * size);
13420Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
13430Sstevel@tonic-gate 	} else {
13440Sstevel@tonic-gate 		offset_t off;
13450Sstevel@tonic-gate 		pc_cluster32_t nbn;
13460Sstevel@tonic-gate 
13470Sstevel@tonic-gate 		ASSERT(IS_FAT12(fsp));
13480Sstevel@tonic-gate 		off = cn + (cn >> 1);
13490Sstevel@tonic-gate 		bn = pc_lblkno(fsp, off);
13500Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
13510Sstevel@tonic-gate 		/* does this field wrap into the next fat cluster? */
13520Sstevel@tonic-gate 		nbn = pc_lblkno(fsp, off + 1);
13530Sstevel@tonic-gate 		if (nbn != bn) {
13540Sstevel@tonic-gate 			fsp->pcfs_fat_changemap[nbn] = 1;
13550Sstevel@tonic-gate 		}
13560Sstevel@tonic-gate 	}
13570Sstevel@tonic-gate }
13580Sstevel@tonic-gate 
13590Sstevel@tonic-gate /*
13600Sstevel@tonic-gate  * return whether the FAT cluster 'bn' is updated and needs to
13610Sstevel@tonic-gate  * be written out.
13620Sstevel@tonic-gate  */
13630Sstevel@tonic-gate int
13640Sstevel@tonic-gate pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn)
13650Sstevel@tonic-gate {
13660Sstevel@tonic-gate 	return (fsp->pcfs_fat_changemap[bn] == 1);
13670Sstevel@tonic-gate }
13682720Sfrankho 
13692720Sfrankho /*
13702720Sfrankho  * Implementation of VFS_FREEVFS() to support forced umounts.
13712720Sfrankho  * This is called by the vfs framework after umount, to trigger
13722720Sfrankho  * the release of any resources still associated with the given
13732720Sfrankho  * vfs_t once the need to keep them has gone away.
13742720Sfrankho  */
13752720Sfrankho void
13762720Sfrankho pcfs_freevfs(vfs_t *vfsp)
13772720Sfrankho {
13782720Sfrankho 	struct pcfs *fsp = VFSTOPCFS(vfsp);
13792720Sfrankho 
13802720Sfrankho 	mutex_enter(&pcfslock);
13815121Sfrankho 	/*
13825121Sfrankho 	 * Purging the FAT closes the device - can't do any more
13835121Sfrankho 	 * I/O after this.
13845121Sfrankho 	 */
13852720Sfrankho 	if (fsp->pcfs_fatp != (uchar_t *)0)
13862720Sfrankho 		pc_invalfat(fsp);
13872720Sfrankho 	mutex_exit(&pcfslock);
13882720Sfrankho 
13892720Sfrankho 	VN_RELE(fsp->pcfs_devvp);
13902720Sfrankho 	mutex_destroy(&fsp->pcfs_lock);
13915121Sfrankho 	kmem_free(fsp, sizeof (*fsp));
13922720Sfrankho 
13932720Sfrankho 	/*
13942720Sfrankho 	 * Allow _fini() to succeed now, if so desired.
13952720Sfrankho 	 */
13962720Sfrankho 	atomic_dec_32(&pcfs_mountcount);
13972720Sfrankho }
13985121Sfrankho 
13995121Sfrankho 
14005121Sfrankho /*
14015121Sfrankho  * PC-style partition parsing and FAT BPB identification/validation code.
14025121Sfrankho  * The partition parsers here assume:
14035121Sfrankho  *	- a FAT filesystem will be in a partition that has one of a set of
14045121Sfrankho  *	  recognized partition IDs
14055121Sfrankho  *	- the user wants the 'numbering' (C:, D:, ...) that one would get
14065121Sfrankho  *	  on MSDOS 6.x.
14075121Sfrankho  *	  That means any non-FAT partition type (NTFS, HPFS, or any Linux fs)
14085121Sfrankho  *	  will not factor in the enumeration.
14095121Sfrankho  * These days, such assumptions should be revisited. FAT is no longer the
14105121Sfrankho  * only game in 'PC town'.
14115121Sfrankho  */
14125121Sfrankho /*
14135121Sfrankho  * isDosDrive()
14145121Sfrankho  *	Boolean function.  Give it the systid field for an fdisk partition
14155121Sfrankho  *	and it decides if that's a systid that describes a DOS drive.  We
14165121Sfrankho  *	use systid values defined in sys/dktp/fdisk.h.
14175121Sfrankho  */
14185121Sfrankho static int
14195121Sfrankho isDosDrive(uchar_t checkMe)
14205121Sfrankho {
14215121Sfrankho 	return ((checkMe == DOSOS12) || (checkMe == DOSOS16) ||
14225121Sfrankho 	    (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) ||
14235121Sfrankho 	    (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) ||
14245121Sfrankho 	    (checkMe == DIAGPART));
14255121Sfrankho }
14265121Sfrankho 
14275121Sfrankho 
14285121Sfrankho /*
14295121Sfrankho  * isDosExtended()
14305121Sfrankho  *	Boolean function.  Give it the systid field for an fdisk partition
14315121Sfrankho  *	and it decides if that's a systid that describes an extended DOS
14325121Sfrankho  *	partition.
14335121Sfrankho  */
14345121Sfrankho static int
14355121Sfrankho isDosExtended(uchar_t checkMe)
14365121Sfrankho {
14375121Sfrankho 	return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA));
14385121Sfrankho }
14395121Sfrankho 
14405121Sfrankho 
14415121Sfrankho /*
14425121Sfrankho  * isBootPart()
14435121Sfrankho  *	Boolean function.  Give it the systid field for an fdisk partition
14445121Sfrankho  *	and it decides if that's a systid that describes a Solaris boot
14455121Sfrankho  *	partition.
14465121Sfrankho  */
14475121Sfrankho static int
14485121Sfrankho isBootPart(uchar_t checkMe)
14495121Sfrankho {
14505121Sfrankho 	return (checkMe == X86BOOT);
14515121Sfrankho }
14525121Sfrankho 
14535121Sfrankho 
14545121Sfrankho /*
14555121Sfrankho  * noLogicalDrive()
14565121Sfrankho  *	Display error message about not being able to find a logical
14575121Sfrankho  *	drive.
14585121Sfrankho  */
14595121Sfrankho static void
14605121Sfrankho noLogicalDrive(int ldrive)
14615121Sfrankho {
14625121Sfrankho 	if (ldrive == BOOT_PARTITION_DRIVE) {
14635121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: no boot partition");
14645121Sfrankho 	} else {
14655121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: %d: no such logical drive", ldrive);
14665121Sfrankho 	}
14675121Sfrankho }
14685121Sfrankho 
14695121Sfrankho 
14705121Sfrankho /*
14715121Sfrankho  * findTheDrive()
14725121Sfrankho  *	Discover offset of the requested logical drive, and return
14735121Sfrankho  *	that offset (startSector), the systid of that drive (sysid),
14745121Sfrankho  *	and a buffer pointer (bp), with the buffer contents being
14755121Sfrankho  *	the first sector of the logical drive (i.e., the sector that
14765121Sfrankho  *	contains the BPB for that drive).
14775121Sfrankho  *
14785121Sfrankho  * Note: this code is not capable of addressing >2TB disks, as it uses
14795121Sfrankho  *       daddr_t not diskaddr_t, some of the calculations would overflow
14805121Sfrankho  */
14815121Sfrankho #define	COPY_PTBL(mbr, ptblp)					\
14825121Sfrankho 	bcopy(&(((struct mboot *)(mbr))->parts), (ptblp),	\
14835121Sfrankho 	    FD_NUMPART * sizeof (struct ipart))
14845121Sfrankho 
14855121Sfrankho static int
14865121Sfrankho findTheDrive(struct pcfs *fsp, buf_t **bp)
14875121Sfrankho {
14885121Sfrankho 	int ldrive = fsp->pcfs_ldrive;
14895121Sfrankho 	dev_t dev = fsp->pcfs_devvp->v_rdev;
14905121Sfrankho 
14915121Sfrankho 	struct ipart dosp[FD_NUMPART];	/* incore fdisk partition structure */
14925121Sfrankho 	daddr_t lastseek = 0;		/* Disk block we sought previously */
14935121Sfrankho 	daddr_t diskblk = 0;		/* Disk block to get */
14945121Sfrankho 	daddr_t xstartsect;		/* base of Extended DOS partition */
14955121Sfrankho 	int logicalDriveCount = 0;	/* Count of logical drives seen */
14965121Sfrankho 	int extendedPart = -1;		/* index of extended dos partition */
14975121Sfrankho 	int primaryPart = -1;		/* index of primary dos partition */
14985121Sfrankho 	int bootPart = -1;		/* index of a Solaris boot partition */
14995121Sfrankho 	int xnumsect = -1;		/* length of extended DOS partition */
15005121Sfrankho 	int driveIndex;			/* computed FDISK table index */
15015121Sfrankho 	daddr_t startsec;
15025121Sfrankho 	len_t mediasize;
15035121Sfrankho 	int i;
15045121Sfrankho 	/*
15055121Sfrankho 	 * Count of drives in the current extended partition's
15065121Sfrankho 	 * FDISK table, and indexes of the drives themselves.
15075121Sfrankho 	 */
15085121Sfrankho 	int extndDrives[FD_NUMPART];
15095121Sfrankho 	int numDrives = 0;
15105121Sfrankho 
15115121Sfrankho 	/*
15125121Sfrankho 	 * Count of drives (beyond primary) in master boot record's
15135121Sfrankho 	 * FDISK table, and indexes of the drives themselves.
15145121Sfrankho 	 */
15155121Sfrankho 	int extraDrives[FD_NUMPART];
15165121Sfrankho 	int numExtraDrives = 0;
15175121Sfrankho 
15185121Sfrankho 	/*
15195121Sfrankho 	 * "ldrive == 0" should never happen, as this is a request to
15205121Sfrankho 	 * mount the physical device (and ignore partitioning). The code
15215121Sfrankho 	 * in pcfs_mount() should have made sure that a logical drive number
15225121Sfrankho 	 * is at least 1, meaning we're looking for drive "C:". It is not
15235121Sfrankho 	 * safe (and a bug in the callers of this function) to request logical
15245121Sfrankho 	 * drive number 0; we could ASSERT() but a graceful EIO is a more
15255121Sfrankho 	 * polite way.
15265121Sfrankho 	 */
15275121Sfrankho 	if (ldrive == 0) {
15285121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: request for logical partition zero");
15295121Sfrankho 		noLogicalDrive(ldrive);
15305121Sfrankho 		return (EIO);
15315121Sfrankho 	}
15325121Sfrankho 
15335121Sfrankho 	/*
15345121Sfrankho 	 *  Copy from disk block into memory aligned structure for fdisk usage.
15355121Sfrankho 	 */
15365121Sfrankho 	COPY_PTBL((*bp)->b_un.b_addr, dosp);
15375121Sfrankho 
15385121Sfrankho 	/*
15395121Sfrankho 	 * This check is ok because a FAT BPB and a master boot record (MBB)
15405121Sfrankho 	 * have the same signature, in the same position within the block.
15415121Sfrankho 	 */
15425121Sfrankho 	if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
15435121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: MBR partition table signature err, "
15445121Sfrankho 		    "device (%x.%x):%d\n",
15455121Sfrankho 		    getmajor(dev), getminor(dev), ldrive);
15465121Sfrankho 		return (EINVAL);
15475121Sfrankho 	}
15485121Sfrankho 
15495121Sfrankho 	/*
15505121Sfrankho 	 * Get a summary of what is in the Master FDISK table.
15515121Sfrankho 	 * Normally we expect to find one partition marked as a DOS drive.
15525121Sfrankho 	 * This partition is the one Windows calls the primary dos partition.
15535121Sfrankho 	 * If the machine has any logical drives then we also expect
15545121Sfrankho 	 * to find a partition marked as an extended DOS partition.
15555121Sfrankho 	 *
15565121Sfrankho 	 * Sometimes we'll find multiple partitions marked as DOS drives.
15575121Sfrankho 	 * The Solaris fdisk program allows these partitions
15585121Sfrankho 	 * to be created, but Windows fdisk no longer does.  We still need
15595121Sfrankho 	 * to support these, though, since Windows does.  We also need to fix
15605121Sfrankho 	 * our fdisk to behave like the Windows version.
15615121Sfrankho 	 *
15625121Sfrankho 	 * It turns out that some off-the-shelf media have *only* an
15635121Sfrankho 	 * Extended partition, so we need to deal with that case as well.
15645121Sfrankho 	 *
15655121Sfrankho 	 * Only a single (the first) Extended or Boot Partition will
15665121Sfrankho 	 * be recognized.  Any others will be ignored.
15675121Sfrankho 	 */
15685121Sfrankho 	for (i = 0; i < FD_NUMPART; i++) {
15695121Sfrankho 		DTRACE_PROBE4(primarypart, struct pcfs *, fsp,
15705121Sfrankho 		    uint_t, (uint_t)dosp[i].systid,
15715121Sfrankho 		    uint_t, LE_32(dosp[i].relsect),
15725121Sfrankho 		    uint_t, LE_32(dosp[i].numsect));
15735121Sfrankho 
15745121Sfrankho 		if (isDosDrive(dosp[i].systid)) {
15755121Sfrankho 			if (primaryPart < 0) {
15765121Sfrankho 				logicalDriveCount++;
15775121Sfrankho 				primaryPart = i;
15785121Sfrankho 			} else {
15795121Sfrankho 				extraDrives[numExtraDrives++] = i;
15805121Sfrankho 			}
15815121Sfrankho 			continue;
15825121Sfrankho 		}
15835121Sfrankho 		if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) {
15845121Sfrankho 			extendedPart = i;
15855121Sfrankho 			continue;
15865121Sfrankho 		}
15875121Sfrankho 		if ((bootPart < 0) && isBootPart(dosp[i].systid)) {
15885121Sfrankho 			bootPart = i;
15895121Sfrankho 			continue;
15905121Sfrankho 		}
15915121Sfrankho 	}
15925121Sfrankho 
15935121Sfrankho 	if (ldrive == BOOT_PARTITION_DRIVE) {
15945121Sfrankho 		if (bootPart < 0) {
15955121Sfrankho 			noLogicalDrive(ldrive);
15965121Sfrankho 			return (EINVAL);
15975121Sfrankho 		}
15985121Sfrankho 		startsec = LE_32(dosp[bootPart].relsect);
15995121Sfrankho 		mediasize = LE_32(dosp[bootPart].numsect);
16005121Sfrankho 		goto found;
16015121Sfrankho 	}
16025121Sfrankho 
16035121Sfrankho 	if (ldrive == PRIMARY_DOS_DRIVE && primaryPart >= 0) {
16045121Sfrankho 		startsec = LE_32(dosp[primaryPart].relsect);
16055121Sfrankho 		mediasize = LE_32(dosp[primaryPart].numsect);
16065121Sfrankho 		goto found;
16075121Sfrankho 	}
16085121Sfrankho 
16095121Sfrankho 	/*
16105121Sfrankho 	 * We are not looking for the C: drive (or the primary drive
16115121Sfrankho 	 * was not found), so we had better have an extended partition
16125121Sfrankho 	 * or extra drives in the Master FDISK table.
16135121Sfrankho 	 */
16145121Sfrankho 	if ((extendedPart < 0) && (numExtraDrives == 0)) {
16155121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: no extended dos partition");
16165121Sfrankho 		noLogicalDrive(ldrive);
16175121Sfrankho 		return (EINVAL);
16185121Sfrankho 	}
16195121Sfrankho 
16205121Sfrankho 	if (extendedPart >= 0) {
16215121Sfrankho 		diskblk = xstartsect = LE_32(dosp[extendedPart].relsect);
16225121Sfrankho 		xnumsect = LE_32(dosp[extendedPart].numsect);
16235121Sfrankho 		do {
16245121Sfrankho 			/*
16255121Sfrankho 			 *  If the seek would not cause us to change
16265121Sfrankho 			 *  position on the drive, then we're out of
16275121Sfrankho 			 *  extended partitions to examine.
16285121Sfrankho 			 */
16295121Sfrankho 			if (diskblk == lastseek)
16305121Sfrankho 				break;
16315121Sfrankho 			logicalDriveCount += numDrives;
16325121Sfrankho 			/*
16335121Sfrankho 			 *  Seek the next extended partition, and find
16345121Sfrankho 			 *  logical drives within it.
16355121Sfrankho 			 */
16365121Sfrankho 			brelse(*bp);
16375121Sfrankho 			/*
16385121Sfrankho 			 * bread() block numbers are multiples of DEV_BSIZE
16395121Sfrankho 			 * but the device sector size (the unit of partitioning)
16405121Sfrankho 			 * might be larger than that; pcfs_get_device_info()
16415121Sfrankho 			 * has calculated the multiplicator for us.
16425121Sfrankho 			 */
16435121Sfrankho 			*bp = bread(dev,
16445121Sfrankho 			    pc_dbdaddr(fsp, diskblk), fsp->pcfs_secsize);
16455121Sfrankho 			if ((*bp)->b_flags & B_ERROR) {
16465121Sfrankho 				return (EIO);
16475121Sfrankho 			}
16485121Sfrankho 
16495121Sfrankho 			lastseek = diskblk;
16505121Sfrankho 			COPY_PTBL((*bp)->b_un.b_addr, dosp);
16515121Sfrankho 			if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
16525121Sfrankho 				cmn_err(CE_NOTE, "!pcfs: "
16535121Sfrankho 				    "extended partition table signature err, "
16545121Sfrankho 				    "device (%x.%x):%d, LBA %u",
16555121Sfrankho 				    getmajor(dev), getminor(dev), ldrive,
16565121Sfrankho 				    (uint_t)pc_dbdaddr(fsp, diskblk));
16575121Sfrankho 				return (EINVAL);
16585121Sfrankho 			}
16595121Sfrankho 			/*
16605121Sfrankho 			 *  Count up drives, and track where the next
16615121Sfrankho 			 *  extended partition is in case we need it.  We
16625121Sfrankho 			 *  are expecting only one extended partition.  If
16635121Sfrankho 			 *  there is more than one we'll only go to the
16645121Sfrankho 			 *  first one we see, but warn about ignoring.
16655121Sfrankho 			 */
16665121Sfrankho 			numDrives = 0;
16675121Sfrankho 			for (i = 0; i < FD_NUMPART; i++) {
16685121Sfrankho 				DTRACE_PROBE4(extendedpart,
16695121Sfrankho 				    struct pcfs *, fsp,
16705121Sfrankho 				    uint_t, (uint_t)dosp[i].systid,
16715121Sfrankho 				    uint_t, LE_32(dosp[i].relsect),
16725121Sfrankho 				    uint_t, LE_32(dosp[i].numsect));
16735121Sfrankho 				if (isDosDrive(dosp[i].systid)) {
16745121Sfrankho 					extndDrives[numDrives++] = i;
16755121Sfrankho 				} else if (isDosExtended(dosp[i].systid)) {
16765121Sfrankho 					if (diskblk != lastseek) {
16775121Sfrankho 						/*
16785121Sfrankho 						 * Already found an extended
16795121Sfrankho 						 * partition in this table.
16805121Sfrankho 						 */
16815121Sfrankho 						cmn_err(CE_NOTE,
16825121Sfrankho 						    "!pcfs: ignoring unexpected"
16835121Sfrankho 						    " additional extended"
16845121Sfrankho 						    " partition");
16855121Sfrankho 					} else {
16865121Sfrankho 						diskblk = xstartsect +
16875121Sfrankho 						    LE_32(dosp[i].relsect);
16885121Sfrankho 					}
16895121Sfrankho 				}
16905121Sfrankho 			}
16915121Sfrankho 		} while (ldrive > logicalDriveCount + numDrives);
16925121Sfrankho 
16935121Sfrankho 		ASSERT(numDrives <= FD_NUMPART);
16945121Sfrankho 
16955121Sfrankho 		if (ldrive <= logicalDriveCount + numDrives) {
16965121Sfrankho 			/*
16975121Sfrankho 			 * The number of logical drives we've found thus
16985121Sfrankho 			 * far is enough to get us to the one we were
16995121Sfrankho 			 * searching for.
17005121Sfrankho 			 */
17015121Sfrankho 			driveIndex = logicalDriveCount + numDrives - ldrive;
17025121Sfrankho 			mediasize =
17035121Sfrankho 			    LE_32(dosp[extndDrives[driveIndex]].numsect);
17045121Sfrankho 			startsec =
17055121Sfrankho 			    LE_32(dosp[extndDrives[driveIndex]].relsect) +
17065121Sfrankho 			    lastseek;
17075121Sfrankho 			if (startsec > (xstartsect + xnumsect)) {
17085121Sfrankho 				cmn_err(CE_NOTE, "!pcfs: extended partition "
17095121Sfrankho 				    "values bad");
17105121Sfrankho 				return (EINVAL);
17115121Sfrankho 			}
17125121Sfrankho 			goto found;
17135121Sfrankho 		} else {
17145121Sfrankho 			/*
17155121Sfrankho 			 * We ran out of extended dos partition
17165121Sfrankho 			 * drives.  The only hope now is to go
17175121Sfrankho 			 * back to extra drives defined in the master
17185121Sfrankho 			 * fdisk table.  But we overwrote that table
17195121Sfrankho 			 * already, so we must load it in again.
17205121Sfrankho 			 */
17215121Sfrankho 			logicalDriveCount += numDrives;
17225121Sfrankho 			brelse(*bp);
17235121Sfrankho 			ASSERT(fsp->pcfs_dosstart == 0);
17245121Sfrankho 			*bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
17255121Sfrankho 			    fsp->pcfs_secsize);
17265121Sfrankho 			if ((*bp)->b_flags & B_ERROR) {
17275121Sfrankho 				return (EIO);
17285121Sfrankho 			}
17295121Sfrankho 			COPY_PTBL((*bp)->b_un.b_addr, dosp);
17305121Sfrankho 		}
17315121Sfrankho 	}
17325121Sfrankho 	/*
17335121Sfrankho 	 *  Still haven't found the drive, is it an extra
17345121Sfrankho 	 *  drive defined in the main FDISK table?
17355121Sfrankho 	 */
17365121Sfrankho 	if (ldrive <= logicalDriveCount + numExtraDrives) {
17375121Sfrankho 		driveIndex = logicalDriveCount + numExtraDrives - ldrive;
17385121Sfrankho 		ASSERT(driveIndex < MIN(numExtraDrives, FD_NUMPART));
17395121Sfrankho 		mediasize = LE_32(dosp[extraDrives[driveIndex]].numsect);
17405121Sfrankho 		startsec = LE_32(dosp[extraDrives[driveIndex]].relsect);
17415121Sfrankho 		goto found;
17425121Sfrankho 	}
17435121Sfrankho 	/*
17445121Sfrankho 	 *  Still haven't found the drive, and there is
17455121Sfrankho 	 *  nowhere else to look.
17465121Sfrankho 	 */
17475121Sfrankho 	noLogicalDrive(ldrive);
17485121Sfrankho 	return (EINVAL);
17495121Sfrankho 
17505121Sfrankho found:
17515121Sfrankho 	/*
17525121Sfrankho 	 * We need this value in units of sectorsize, because PCFS' internal
17535121Sfrankho 	 * offset calculations go haywire for > 512Byte sectors unless all
17545121Sfrankho 	 * pcfs_.*start values are in units of sectors.
17555121Sfrankho 	 * So, assign before the capacity check (that's done in DEV_BSIZE)
17565121Sfrankho 	 */
17575121Sfrankho 	fsp->pcfs_dosstart = startsec;
17585121Sfrankho 
17595121Sfrankho 	/*
17605121Sfrankho 	 * convert from device sectors to proper units:
17615121Sfrankho 	 *	- starting sector: DEV_BSIZE (as argument to bread())
17625121Sfrankho 	 *	- media size: Bytes
17635121Sfrankho 	 */
17645121Sfrankho 	startsec = pc_dbdaddr(fsp, startsec);
17655121Sfrankho 	mediasize *= fsp->pcfs_secsize;
17665121Sfrankho 
17675121Sfrankho 	/*
17685121Sfrankho 	 * some additional validation / warnings in case the partition table
17695121Sfrankho 	 * and the actual media capacity are not in accordance ...
17705121Sfrankho 	 */
17715121Sfrankho 	if (fsp->pcfs_mediasize != 0) {
17725121Sfrankho 		diskaddr_t startoff =
17735121Sfrankho 		    (diskaddr_t)startsec * (diskaddr_t)DEV_BSIZE;
17745121Sfrankho 
17755121Sfrankho 		if (startoff >= fsp->pcfs_mediasize ||
17765121Sfrankho 		    startoff + mediasize > fsp->pcfs_mediasize) {
17775121Sfrankho 			cmn_err(CE_WARN,
17785121Sfrankho 			    "!pcfs: partition size (LBA start %u, %lld bytes, "
17795121Sfrankho 			    "device (%x.%x):%d) smaller than "
17805121Sfrankho 			    "mediasize (%lld bytes).\n"
17815121Sfrankho 			    "filesystem may be truncated, access errors "
17825121Sfrankho 			    "may result.\n",
17835121Sfrankho 			    (uint_t)startsec, (long long)mediasize,
17845121Sfrankho 			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
17855121Sfrankho 			    fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
17865121Sfrankho 		}
17875121Sfrankho 	} else {
17885121Sfrankho 		fsp->pcfs_mediasize = mediasize;
17895121Sfrankho 	}
17905121Sfrankho 
17915121Sfrankho 	return (0);
17925121Sfrankho }
17935121Sfrankho 
17945121Sfrankho 
17955121Sfrankho static fattype_t
17965121Sfrankho secondaryBPBChecks(struct pcfs *fsp, uchar_t *bpb, size_t secsize)
17975121Sfrankho {
17985121Sfrankho 	uint32_t ncl = fsp->pcfs_ncluster;
17995121Sfrankho 
18005121Sfrankho 	if (ncl <= 4096) {
18015121Sfrankho 		if (bpb_get_FatSz16(bpb) == 0)
18025121Sfrankho 			return (FAT_UNKNOWN);
18035121Sfrankho 
18045121Sfrankho 		if (bpb_get_FatSz16(bpb) * secsize < ncl * 2 &&
18055121Sfrankho 		    bpb_get_FatSz16(bpb) * secsize >= (3 * ncl / 2))
18065121Sfrankho 			return (FAT12);
18075121Sfrankho 		if (bcmp(bpb_FilSysType16(bpb), "FAT12", 5) == 0)
18085121Sfrankho 			return (FAT12);
18095121Sfrankho 		if (bcmp(bpb_FilSysType16(bpb), "FAT16", 5) == 0)
18105121Sfrankho 			return (FAT16);
18115121Sfrankho 
18125121Sfrankho 		switch (bpb_get_Media(bpb)) {
18135121Sfrankho 			case SS8SPT:
18145121Sfrankho 			case DS8SPT:
18155121Sfrankho 			case SS9SPT:
18165121Sfrankho 			case DS9SPT:
18175121Sfrankho 			case DS18SPT:
18185121Sfrankho 			case DS9_15SPT:
18195121Sfrankho 				/*
18205121Sfrankho 				 * Is this reliable - all floppies are FAT12 ?
18215121Sfrankho 				 */
18225121Sfrankho 				return (FAT12);
18235121Sfrankho 			case MD_FIXED:
18245121Sfrankho 				/*
18255121Sfrankho 				 * Is this reliable - disks are always FAT16 ?
18265121Sfrankho 				 */
18275121Sfrankho 				return (FAT16);
18285121Sfrankho 			default:
18295121Sfrankho 				break;
18305121Sfrankho 		}
18315121Sfrankho 	} else if (ncl <= 65536) {
18325121Sfrankho 		if (bpb_get_FatSz16(bpb) == 0 && bpb_get_FatSz32(bpb) > 0)
18335121Sfrankho 			return (FAT32);
18345121Sfrankho 		if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
18355121Sfrankho 			return (FAT32);
18365121Sfrankho 		if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
18375121Sfrankho 			return (FAT32);
18385121Sfrankho 
18395121Sfrankho 		if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
18405121Sfrankho 			return (FAT16);
18415121Sfrankho 		if (bpb_get_FatSz16(bpb) * secsize < ncl * 4)
18425121Sfrankho 			return (FAT16);
18435121Sfrankho 	}
18445121Sfrankho 
18455121Sfrankho 	/*
18465121Sfrankho 	 * We don't know
18475121Sfrankho 	 */
18485121Sfrankho 	return (FAT_UNKNOWN);
18495121Sfrankho }
18505121Sfrankho 
18515121Sfrankho /*
18525121Sfrankho  * Check to see if the BPB we found is correct.
18535121Sfrankho  *
18545121Sfrankho  * This looks far more complicated that it needs to be for pure structural
18555121Sfrankho  * validation. The reason for this is that parseBPB() is also used for
18565121Sfrankho  * debugging purposes (mdb dcmd) and we therefore want a bitmap of which
18575121Sfrankho  * BPB fields have 'known good' values, even if we do not reject the BPB
18585121Sfrankho  * when attempting to mount the filesystem.
18595121Sfrankho  */
18605121Sfrankho static int
18615121Sfrankho parseBPB(struct pcfs *fsp, uchar_t *bpb, int *valid)
18625121Sfrankho {
18635121Sfrankho 	fattype_t type;
18645121Sfrankho 
18655121Sfrankho 	uint32_t	ncl;	/* number of clusters in file area */
18665121Sfrankho 	uint32_t	rec;
18675121Sfrankho 	uint32_t	reserved;
18685121Sfrankho 	uint32_t	fsisec, bkbootsec;
18695121Sfrankho 	blkcnt_t	totsec, totsec16, totsec32, datasec;
18705121Sfrankho 	size_t		fatsec, fatsec16, fatsec32, rdirsec;
18715121Sfrankho 	size_t		secsize;
18725121Sfrankho 	len_t		mediasize;
18735121Sfrankho 	uint64_t	validflags = 0;
18745121Sfrankho 
18755121Sfrankho 	if (VALID_BPBSIG(bpb_get_BPBSig(bpb)))
18765121Sfrankho 		validflags |= BPB_BPBSIG_OK;
18775121Sfrankho 
18785121Sfrankho 	rec = bpb_get_RootEntCnt(bpb);
18795121Sfrankho 	reserved = bpb_get_RsvdSecCnt(bpb);
18805121Sfrankho 	fsisec = bpb_get_FSInfo32(bpb);
18815121Sfrankho 	bkbootsec = bpb_get_BkBootSec32(bpb);
18825121Sfrankho 	totsec16 = (blkcnt_t)bpb_get_TotSec16(bpb);
18835121Sfrankho 	totsec32 = (blkcnt_t)bpb_get_TotSec32(bpb);
18845121Sfrankho 	fatsec16 = bpb_get_FatSz16(bpb);
18855121Sfrankho 	fatsec32 = bpb_get_FatSz32(bpb);
18865121Sfrankho 
18875121Sfrankho 	totsec = totsec16 ? totsec16 : totsec32;
18885121Sfrankho 	fatsec = fatsec16 ? fatsec16 : fatsec32;
18895121Sfrankho 
18905121Sfrankho 	secsize = bpb_get_BytesPerSec(bpb);
18915121Sfrankho 	if (!VALID_SECSIZE(secsize))
18925121Sfrankho 		secsize = fsp->pcfs_secsize;
18935121Sfrankho 	if (secsize != fsp->pcfs_secsize) {
18945121Sfrankho 		PC_DPRINTF3(3, "!pcfs: parseBPB, device (%x.%x):%d:\n",
18955121Sfrankho 		    getmajor(fsp->pcfs_xdev),
18965121Sfrankho 		    getminor(fsp->pcfs_xdev), fsp->pcfs_ldrive);
18975121Sfrankho 		PC_DPRINTF2(3, "!BPB secsize %d != "
18985121Sfrankho 		    "autodetected media block size %d\n",
18995121Sfrankho 		    (int)secsize, (int)fsp->pcfs_secsize);
19005121Sfrankho 		if (fsp->pcfs_ldrive) {
19015121Sfrankho 			/*
19025121Sfrankho 			 * We've already attempted to parse the partition
19035121Sfrankho 			 * table. If the block size used for that don't match
19045121Sfrankho 			 * the PCFS sector size, we're hosed one way or the
19055121Sfrankho 			 * other. Just try what happens.
19065121Sfrankho 			 */
19075121Sfrankho 			secsize = fsp->pcfs_secsize;
19085121Sfrankho 			PC_DPRINTF1(3,
19095121Sfrankho 			    "!pcfs: Using autodetected secsize %d\n",
19105121Sfrankho 			    (int)secsize);
19115121Sfrankho 		} else {
19125121Sfrankho 			/*
19135121Sfrankho 			 * This allows mounting lofi images of PCFS partitions
19145121Sfrankho 			 * with sectorsize != DEV_BSIZE. We can't parse the
19155121Sfrankho 			 * partition table on whole-disk images unless the
19165121Sfrankho 			 * (undocumented) "secsize=..." mount option is used,
19175121Sfrankho 			 * but at least this allows us to mount if we have
19185121Sfrankho 			 * an image of a partition.
19195121Sfrankho 			 */
19205121Sfrankho 			PC_DPRINTF1(3,
19215121Sfrankho 			    "!pcfs: Using BPB secsize %d\n", (int)secsize);
19225121Sfrankho 		}
19235121Sfrankho 	}
19245121Sfrankho 
19255121Sfrankho 	if (fsp->pcfs_mediasize == 0) {
19265121Sfrankho 		mediasize = (len_t)totsec * (len_t)secsize;
19275121Sfrankho 		PC_DPRINTF4(3, "!pcfs: parseBPB: mediasize autodetect failed "
19285121Sfrankho 		    "on device (%x.%x):%d, trusting BPB totsec (%lld Bytes)\n",
19295121Sfrankho 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
19305121Sfrankho 		    fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
19315121Sfrankho 	} else if ((len_t)totsec * (len_t)secsize > fsp->pcfs_mediasize) {
19325121Sfrankho 		cmn_err(CE_WARN,
19335121Sfrankho 		    "!pcfs: autodetected mediasize (%lld Bytes) smaller than "
19345121Sfrankho 		    "FAT BPB mediasize (%lld Bytes).\n"
19355121Sfrankho 		    "truncated filesystem on device (%x.%x):%d, access errors "
19365121Sfrankho 		    "possible.\n",
19375121Sfrankho 		    (long long)fsp->pcfs_mediasize,
19385121Sfrankho 		    (long long)(totsec * (blkcnt_t)secsize),
19395121Sfrankho 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
19405121Sfrankho 		    fsp->pcfs_ldrive);
19415121Sfrankho 		mediasize = fsp->pcfs_mediasize;
19425121Sfrankho 	} else {
19435121Sfrankho 		/*
19445121Sfrankho 		 * This is actually ok. A FAT needs not occupy the maximum
19455121Sfrankho 		 * space available in its partition, it can be shorter.
19465121Sfrankho 		 */
19475121Sfrankho 		mediasize = (len_t)totsec * (len_t)secsize;
19485121Sfrankho 	}
19495121Sfrankho 
19505121Sfrankho 	/*
19515121Sfrankho 	 * Since we let just about anything pass through this function,
19525121Sfrankho 	 * fence against divide-by-zero here.
19535121Sfrankho 	 */
19545121Sfrankho 	if (secsize)
19555121Sfrankho 		rdirsec = roundup(rec * 32, secsize) / secsize;
19565121Sfrankho 	else
19575121Sfrankho 		rdirsec = 0;
19585121Sfrankho 
19595121Sfrankho 	/*
19605121Sfrankho 	 * This assignment is necessary before pc_dbdaddr() can first be
19615121Sfrankho 	 * used. Must initialize the value here.
19625121Sfrankho 	 */
19635121Sfrankho 	fsp->pcfs_secsize = secsize;
19645121Sfrankho 	fsp->pcfs_sdshift = ddi_ffs(secsize / DEV_BSIZE) - 1;
19655121Sfrankho 
19665121Sfrankho 	fsp->pcfs_mediasize = mediasize;
19675121Sfrankho 
19685121Sfrankho 	fsp->pcfs_spcl = bpb_get_SecPerClus(bpb);
19695121Sfrankho 	fsp->pcfs_numfat = bpb_get_NumFATs(bpb);
19705121Sfrankho 	fsp->pcfs_mediadesc = bpb_get_Media(bpb);
19715121Sfrankho 	fsp->pcfs_clsize = secsize * fsp->pcfs_spcl;
19725121Sfrankho 	fsp->pcfs_rdirsec = rdirsec;
19735121Sfrankho 
19745121Sfrankho 	/*
19755121Sfrankho 	 * Remember: All PCFS offset calculations in sectors. Before I/O
19765121Sfrankho 	 * is done, convert to DEV_BSIZE units via pc_dbdaddr(). This is
19775121Sfrankho 	 * necessary so that media with > 512Byte sector sizes work correctly.
19785121Sfrankho 	 */
19795121Sfrankho 	fsp->pcfs_fatstart = fsp->pcfs_dosstart + reserved;
19805121Sfrankho 	fsp->pcfs_rdirstart = fsp->pcfs_fatstart + fsp->pcfs_numfat * fatsec;
19815121Sfrankho 	fsp->pcfs_datastart = fsp->pcfs_rdirstart + rdirsec;
19825121Sfrankho 	datasec = totsec -
19835121Sfrankho 	    (blkcnt_t)fatsec * fsp->pcfs_numfat -
19845121Sfrankho 	    (blkcnt_t)rdirsec -
19855121Sfrankho 	    (blkcnt_t)reserved;
19865121Sfrankho 
19875121Sfrankho 	DTRACE_PROBE4(fatgeometry,
19885121Sfrankho 	    blkcnt_t, totsec, size_t, fatsec,
19895121Sfrankho 	    size_t, rdirsec, blkcnt_t, datasec);
19905121Sfrankho 
19915121Sfrankho 	/*
19925121Sfrankho 	 * UINT32_MAX is an underflow check - we calculate in "blkcnt_t" which
19935121Sfrankho 	 * is 64bit in order to be able to catch "impossible" sector counts.
19945121Sfrankho 	 * A sector count in FAT must fit 32bit unsigned int.
19955121Sfrankho 	 */
19965121Sfrankho 	if (totsec != 0 &&
19975121Sfrankho 	    (totsec16 == totsec32 || totsec16 == 0 || totsec32 == 0) &&
19985121Sfrankho 	    (len_t)totsec * (len_t)secsize <= mediasize &&
19995121Sfrankho 	    datasec < totsec && datasec <= UINT32_MAX)
20005121Sfrankho 		validflags |= BPB_TOTSEC_OK;
20015121Sfrankho 
20025121Sfrankho 	if (mediasize >= (len_t)datasec * (len_t)secsize)
20035121Sfrankho 		validflags |= BPB_MEDIASZ_OK;
20045121Sfrankho 
20055121Sfrankho 	if (VALID_SECSIZE(secsize))
20065121Sfrankho 		validflags |= BPB_SECSIZE_OK;
20075121Sfrankho 	if (VALID_SPCL(fsp->pcfs_spcl))
20085121Sfrankho 		validflags |= BPB_SECPERCLUS_OK;
20095121Sfrankho 	if (VALID_CLSIZE(fsp->pcfs_clsize))
20105121Sfrankho 		validflags |= BPB_CLSIZE_OK;
20115121Sfrankho 	if (VALID_NUMFATS(fsp->pcfs_numfat))
20125121Sfrankho 		validflags |= BPB_NUMFAT_OK;
20135121Sfrankho 	if (VALID_RSVDSEC(reserved) && reserved < totsec)
20145121Sfrankho 		validflags |= BPB_RSVDSECCNT_OK;
20155121Sfrankho 	if (VALID_MEDIA(fsp->pcfs_mediadesc))
20165121Sfrankho 		validflags |= BPB_MEDIADESC_OK;
20175121Sfrankho 	if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
20185121Sfrankho 		validflags |= BPB_BOOTSIG16_OK;
20195121Sfrankho 	if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
20205121Sfrankho 		validflags |= BPB_BOOTSIG32_OK;
20215121Sfrankho 	if (VALID_FSTYPSTR16(bpb_FilSysType16(bpb)))
20225121Sfrankho 		validflags |= BPB_FSTYPSTR16_OK;
20235121Sfrankho 	if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
20245121Sfrankho 		validflags |= BPB_FSTYPSTR32_OK;
20255121Sfrankho 	if (VALID_OEMNAME(bpb_OEMName(bpb)))
20265121Sfrankho 		validflags |= BPB_OEMNAME_OK;
20275121Sfrankho 	if (bkbootsec > 0 && bkbootsec <= reserved && fsisec != bkbootsec)
20285121Sfrankho 		validflags |= BPB_BKBOOTSEC_OK;
20295121Sfrankho 	if (fsisec > 0 && fsisec <= reserved)
20305121Sfrankho 		validflags |= BPB_FSISEC_OK;
20315121Sfrankho 	if (VALID_JMPBOOT(bpb_jmpBoot(bpb)))
20325121Sfrankho 		validflags |= BPB_JMPBOOT_OK;
20335121Sfrankho 	if (VALID_FSVER32(bpb_get_FSVer32(bpb)))
20345121Sfrankho 		validflags |= BPB_FSVER_OK;
20355121Sfrankho 	if (VALID_VOLLAB(bpb_VolLab16(bpb)))
20365121Sfrankho 		validflags |= BPB_VOLLAB16_OK;
20375121Sfrankho 	if (VALID_VOLLAB(bpb_VolLab32(bpb)))
20385121Sfrankho 		validflags |= BPB_VOLLAB32_OK;
20395121Sfrankho 	if (VALID_EXTFLAGS(bpb_get_ExtFlags32(bpb)))
20405121Sfrankho 		validflags |= BPB_EXTFLAGS_OK;
20415121Sfrankho 
20425121Sfrankho 	/*
20435121Sfrankho 	 * Try to determine which FAT format to use.
20445121Sfrankho 	 *
20455121Sfrankho 	 * Calculate the number of clusters in order to determine
20465121Sfrankho 	 * the type of FAT we are looking at.  This is the only
20475121Sfrankho 	 * recommended way of determining FAT type, though there
20485121Sfrankho 	 * are other hints in the data, this is the best way.
20495121Sfrankho 	 *
20505121Sfrankho 	 * Since we let just about "anything" pass through this function
20515121Sfrankho 	 * without early exits, fence against divide-by-zero here.
20525121Sfrankho 	 *
20535121Sfrankho 	 * datasec was already validated against UINT32_MAX so we know
20545121Sfrankho 	 * the result will not overflow the 32bit calculation.
20555121Sfrankho 	 */
20565121Sfrankho 	if (fsp->pcfs_spcl)
20575121Sfrankho 		ncl = (uint32_t)datasec / fsp->pcfs_spcl;
20585121Sfrankho 	else
20595121Sfrankho 		ncl = 0;
20605121Sfrankho 
20615121Sfrankho 	fsp->pcfs_ncluster = ncl;
20625121Sfrankho 
20635121Sfrankho 	/*
20645121Sfrankho 	 * From the Microsoft FAT specification:
20655121Sfrankho 	 * In the following example, when it says <, it does not mean <=.
20665121Sfrankho 	 * Note also that the numbers are correct.  The first number for
20675121Sfrankho 	 * FAT12 is 4085; the second number for FAT16 is 65525. These numbers
20685121Sfrankho 	 * and the '<' signs are not wrong.
20695121Sfrankho 	 *
20705121Sfrankho 	 * We "specialdetect" the corner cases, and use at least one "extra"
20715121Sfrankho 	 * criterion to decide whether it's FAT16 or FAT32 if the cluster
20725121Sfrankho 	 * count is dangerously close to the boundaries.
20735121Sfrankho 	 */
20745121Sfrankho 
20755121Sfrankho 	if (ncl <= PCF_FIRSTCLUSTER) {
20765121Sfrankho 		type = FAT_UNKNOWN;
20775121Sfrankho 	} else if (ncl < 4085) {
20785121Sfrankho 		type = FAT12;
20795121Sfrankho 	} else if (ncl <= 4096) {
20805121Sfrankho 		type = FAT_QUESTIONABLE;
20815121Sfrankho 	} else if (ncl < 65525) {
20825121Sfrankho 		type = FAT16;
20835121Sfrankho 	} else if (ncl <= 65536) {
20845121Sfrankho 		type = FAT_QUESTIONABLE;
20855121Sfrankho 	} else if (ncl < PCF_LASTCLUSTER32) {
20865121Sfrankho 		type = FAT32;
20875121Sfrankho 	} else {
20885121Sfrankho 		type = FAT_UNKNOWN;
20895121Sfrankho 	}
20905121Sfrankho 
20915121Sfrankho 	DTRACE_PROBE4(parseBPB__initial,
20925121Sfrankho 	    struct pcfs *, fsp, unsigned char *, bpb,
20935121Sfrankho 	    int, validflags, fattype_t, type);
20945121Sfrankho 
20955121Sfrankho recheck:
20965121Sfrankho 	fsp->pcfs_fatsec = fatsec;
20975121Sfrankho 
20985121Sfrankho 	/* Do some final sanity checks for each specific type of FAT */
20995121Sfrankho 	switch (type) {
21005121Sfrankho 		case FAT12:
21015121Sfrankho 			if (rec != 0)
21025121Sfrankho 				validflags |= BPB_ROOTENTCNT_OK;
21035121Sfrankho 			if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
21045121Sfrankho 			    bpb_get_TotSec16(bpb) == 0)
21055121Sfrankho 				validflags |= BPB_TOTSEC16_OK;
21065121Sfrankho 			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
21075121Sfrankho 			    bpb_get_TotSec32(bpb) == 0)
21085121Sfrankho 				validflags |= BPB_TOTSEC32_OK;
21095121Sfrankho 			if (bpb_get_FatSz16(bpb) == fatsec)
21105121Sfrankho 				validflags |= BPB_FATSZ16_OK;
21115121Sfrankho 			if (fatsec * secsize >= ncl * 3 / 2)
21125121Sfrankho 				validflags |= BPB_FATSZ_OK;
21135121Sfrankho 			if (ncl < 4085)
21145121Sfrankho 				validflags |= BPB_NCLUSTERS_OK;
21155121Sfrankho 
21165121Sfrankho 			fsp->pcfs_lastclmark = (PCF_LASTCLUSTER & 0xfff);
21175121Sfrankho 			fsp->pcfs_rootblksize =
21185121Sfrankho 			    fsp->pcfs_rdirsec * secsize;
21195121Sfrankho 			fsp->pcfs_fsistart = 0;
21205121Sfrankho 
21215121Sfrankho 			if ((validflags & FAT12_VALIDMSK) != FAT12_VALIDMSK)
21225121Sfrankho 				type = FAT_UNKNOWN;
21235121Sfrankho 			break;
21245121Sfrankho 		case FAT16:
21255121Sfrankho 			if (rec != 0)
21265121Sfrankho 				validflags |= BPB_ROOTENTCNT_OK;
21275121Sfrankho 			if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
21285121Sfrankho 			    bpb_get_TotSec16(bpb) == 0)
21295121Sfrankho 				validflags |= BPB_TOTSEC16_OK;
21305121Sfrankho 			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
21315121Sfrankho 			    bpb_get_TotSec32(bpb) == 0)
21325121Sfrankho 				validflags |= BPB_TOTSEC32_OK;
21335121Sfrankho 			if (bpb_get_FatSz16(bpb) == fatsec)
21345121Sfrankho 				validflags |= BPB_FATSZ16_OK;
21355121Sfrankho 			if (fatsec * secsize >= ncl * 2)
21365121Sfrankho 				validflags |= BPB_FATSZ_OK;
21375121Sfrankho 			if (ncl >= 4085 && ncl < 65525)
21385121Sfrankho 				validflags |= BPB_NCLUSTERS_OK;
21395121Sfrankho 
21405121Sfrankho 			fsp->pcfs_lastclmark = PCF_LASTCLUSTER;
21415121Sfrankho 			fsp->pcfs_rootblksize =
21425121Sfrankho 			    fsp->pcfs_rdirsec * secsize;
21435121Sfrankho 			fsp->pcfs_fsistart = 0;
21445121Sfrankho 
21455121Sfrankho 			if ((validflags & FAT16_VALIDMSK) != FAT16_VALIDMSK)
21465121Sfrankho 				type = FAT_UNKNOWN;
21475121Sfrankho 			break;
21485121Sfrankho 		case FAT32:
21495121Sfrankho 			if (rec == 0)
21505121Sfrankho 				validflags |= BPB_ROOTENTCNT_OK;
21515121Sfrankho 			if (bpb_get_TotSec16(bpb) == 0)
21525121Sfrankho 				validflags |= BPB_TOTSEC16_OK;
21535121Sfrankho 			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec)
21545121Sfrankho 				validflags |= BPB_TOTSEC32_OK;
21555121Sfrankho 			if (bpb_get_FatSz16(bpb) == 0)
21565121Sfrankho 				validflags |= BPB_FATSZ16_OK;
21575121Sfrankho 			if (bpb_get_FatSz32(bpb) == fatsec)
21585121Sfrankho 				validflags |= BPB_FATSZ32_OK;
21595121Sfrankho 			if (fatsec * secsize >= ncl * 4)
21605121Sfrankho 				validflags |= BPB_FATSZ_OK;
21615121Sfrankho 			if (ncl >= 65525 && ncl < PCF_LASTCLUSTER32)
21625121Sfrankho 				validflags |= BPB_NCLUSTERS_OK;
21635121Sfrankho 
21645121Sfrankho 			fsp->pcfs_lastclmark = PCF_LASTCLUSTER32;
21655121Sfrankho 			fsp->pcfs_rootblksize = fsp->pcfs_clsize;
21665121Sfrankho 			fsp->pcfs_fsistart = fsp->pcfs_dosstart + fsisec;
21675121Sfrankho 			if (validflags & BPB_FSISEC_OK)
21685121Sfrankho 				fsp->pcfs_flags |= PCFS_FSINFO_OK;
21695121Sfrankho 			fsp->pcfs_rootclnum = bpb_get_RootClus32(bpb);
21705121Sfrankho 			if (pc_validcl(fsp, fsp->pcfs_rootclnum))
21715121Sfrankho 				validflags |= BPB_ROOTCLUSTER_OK;
21725121Sfrankho 
21735121Sfrankho 			/*
21745121Sfrankho 			 * Current PCFS code only works if 'pcfs_rdirstart'
21755121Sfrankho 			 * contains the root cluster number on FAT32.
21765121Sfrankho 			 * That's a mis-use and would better be changed.
21775121Sfrankho 			 */
21785121Sfrankho 			fsp->pcfs_rdirstart = (daddr_t)fsp->pcfs_rootclnum;
21795121Sfrankho 
21805121Sfrankho 			if ((validflags & FAT32_VALIDMSK) != FAT32_VALIDMSK)
21815121Sfrankho 				type = FAT_UNKNOWN;
21825121Sfrankho 			break;
21835121Sfrankho 		case FAT_QUESTIONABLE:
21845121Sfrankho 			type = secondaryBPBChecks(fsp, bpb, secsize);
21855121Sfrankho 			goto recheck;
21865121Sfrankho 		default:
21875121Sfrankho 			ASSERT(type == FAT_UNKNOWN);
21885121Sfrankho 			break;
21895121Sfrankho 	}
21905121Sfrankho 
21915121Sfrankho 	ASSERT(type != FAT_QUESTIONABLE);
21925121Sfrankho 
21935121Sfrankho 	fsp->pcfs_fattype = type;
21945121Sfrankho 
21955121Sfrankho 	if (valid)
21965121Sfrankho 		*valid = validflags;
21975121Sfrankho 
21985121Sfrankho 	DTRACE_PROBE4(parseBPB__final,
21995121Sfrankho 	    struct pcfs *, fsp, unsigned char *, bpb,
22005121Sfrankho 	    int, validflags, fattype_t, type);
22015121Sfrankho 
22025121Sfrankho 	if (type != FAT_UNKNOWN) {
22035121Sfrankho 		ASSERT((secsize & (DEV_BSIZE - 1)) == 0);
22045121Sfrankho 		ASSERT(ISP2(secsize / DEV_BSIZE));
22055121Sfrankho 		return (1);
22065121Sfrankho 	}
22075121Sfrankho 
22085121Sfrankho 	return (0);
22095121Sfrankho }
22105121Sfrankho 
22115121Sfrankho 
22125121Sfrankho /*
22135121Sfrankho  * Detect the device's native block size (sector size).
22145121Sfrankho  *
22155121Sfrankho  * Test whether the device is:
22165121Sfrankho  *	- a floppy device from a known controller type via DKIOCINFO
22175121Sfrankho  *	- a real floppy using the fd(7d) driver and capable of fdio(7I) ioctls
22185121Sfrankho  *	- a PCMCIA sram memory card (pseudofloppy) using pcram(7d)
22195121Sfrankho  *	- a USB floppy drive (identified by drive geometry)
22205121Sfrankho  *
22215121Sfrankho  * Detecting a floppy will make PCFS metadata updates on such media synchronous,
22225121Sfrankho  * to minimize risks due to slow I/O and user hotplugging / device ejection.
22235121Sfrankho  *
22245121Sfrankho  * This might be a bit wasteful on kernel stack space; if anyone's
22255121Sfrankho  * bothered by this, kmem_alloc/kmem_free the ioctl arguments...
22265121Sfrankho  */
22275121Sfrankho static void
22285121Sfrankho pcfs_device_getinfo(struct pcfs *fsp)
22295121Sfrankho {
22305121Sfrankho 	dev_t			rdev = fsp->pcfs_xdev;
22315121Sfrankho 	int			error;
22325121Sfrankho 	union {
22335121Sfrankho 		struct dk_minfo		mi;
22345121Sfrankho 		struct dk_cinfo		ci;
22355121Sfrankho 		struct dk_geom		gi;
22365121Sfrankho 		struct fd_char		fc;
22375121Sfrankho 	} arg;				/* save stackspace ... */
22385121Sfrankho 	intptr_t argp = (intptr_t)&arg;
22395121Sfrankho 	ldi_handle_t		lh;
22405121Sfrankho 	ldi_ident_t		li;
22415121Sfrankho 	int isfloppy, isremoveable, ishotpluggable;
22425121Sfrankho 	cred_t			*cr = CRED();
22435121Sfrankho 
22445121Sfrankho 	if (ldi_ident_from_dev(rdev, &li))
22455121Sfrankho 		goto out;
22465121Sfrankho 
22475121Sfrankho 	error = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, cr, &lh, li);
22485121Sfrankho 	ldi_ident_release(li);
22495121Sfrankho 	if (error)
22505121Sfrankho 		goto out;
22515121Sfrankho 
22525121Sfrankho 	/*
22535121Sfrankho 	 * Not sure if this could possibly happen. It'd be a bit like
22545121Sfrankho 	 * VOP_OPEN() changing the passed-in vnode ptr. We're just not
22555121Sfrankho 	 * expecting it, needs some thought if triggered ...
22565121Sfrankho 	 */
22575121Sfrankho 	ASSERT(fsp->pcfs_xdev == rdev);
22585121Sfrankho 
22595121Sfrankho 	/*
22605121Sfrankho 	 * Check for removeable/hotpluggable media.
22615121Sfrankho 	 */
22625121Sfrankho 	if (ldi_ioctl(lh, DKIOCREMOVABLE,
22635121Sfrankho 	    (intptr_t)&isremoveable, FKIOCTL, cr, NULL)) {
22645121Sfrankho 		isremoveable = 0;
22655121Sfrankho 	}
22665121Sfrankho 	if (ldi_ioctl(lh, DKIOCHOTPLUGGABLE,
22675121Sfrankho 	    (intptr_t)&ishotpluggable, FKIOCTL, cr, NULL)) {
22685121Sfrankho 		ishotpluggable = 0;
22695121Sfrankho 	}
22705121Sfrankho 
22715121Sfrankho 	/*
22725121Sfrankho 	 * Make sure we don't use "half-initialized" values if the ioctls fail.
22735121Sfrankho 	 */
22745121Sfrankho 	if (ldi_ioctl(lh, DKIOCGMEDIAINFO, argp, FKIOCTL, cr, NULL)) {
22755121Sfrankho 		bzero(&arg, sizeof (arg));
22765121Sfrankho 		fsp->pcfs_mediasize = 0;
22775121Sfrankho 	} else {
22785121Sfrankho 		fsp->pcfs_mediasize =
22795121Sfrankho 		    (len_t)arg.mi.dki_lbsize *
22805121Sfrankho 		    (len_t)arg.mi.dki_capacity;
22815121Sfrankho 	}
22825121Sfrankho 
22835121Sfrankho 	if (VALID_SECSIZE(arg.mi.dki_lbsize)) {
22845121Sfrankho 		if (fsp->pcfs_secsize == 0) {
22855121Sfrankho 			fsp->pcfs_secsize = arg.mi.dki_lbsize;
22865121Sfrankho 			fsp->pcfs_sdshift =
22875121Sfrankho 			    ddi_ffs(arg.mi.dki_lbsize / DEV_BSIZE) - 1;
22885121Sfrankho 		} else {
22895121Sfrankho 			PC_DPRINTF4(1, "!pcfs: autodetected media block size "
22905121Sfrankho 			    "%d, device (%x.%x), different from user-provided "
22915121Sfrankho 			    "%d. User override - ignoring autodetect result.\n",
22925121Sfrankho 			    arg.mi.dki_lbsize,
22935121Sfrankho 			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
22945121Sfrankho 			    fsp->pcfs_secsize);
22955121Sfrankho 		}
22965121Sfrankho 	} else if (arg.mi.dki_lbsize) {
22975121Sfrankho 		PC_DPRINTF3(1, "!pcfs: autodetected media block size "
22985121Sfrankho 		    "%d, device (%x.%x), invalid (not 512, 1024, 2048, 4096). "
22995121Sfrankho 		    "Ignoring autodetect result.\n",
23005121Sfrankho 		    arg.mi.dki_lbsize,
23015121Sfrankho 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev));
23025121Sfrankho 	}
23035121Sfrankho 
23045121Sfrankho 	/*
23055121Sfrankho 	 * We treat the following media types as a floppy by default.
23065121Sfrankho 	 */
23075121Sfrankho 	isfloppy =
23085121Sfrankho 	    (arg.mi.dki_media_type == DK_FLOPPY ||
23095121Sfrankho 	    arg.mi.dki_media_type == DK_ZIP ||
23105121Sfrankho 	    arg.mi.dki_media_type == DK_JAZ);
23115121Sfrankho 
23125121Sfrankho 	/*
23135121Sfrankho 	 * if this device understands fdio(7I) requests it's
23145121Sfrankho 	 * obviously a floppy drive.
23155121Sfrankho 	 */
23165121Sfrankho 	if (!isfloppy &&
23175121Sfrankho 	    !ldi_ioctl(lh, FDIOGCHAR, argp, FKIOCTL, cr, NULL))
23185121Sfrankho 		isfloppy = 1;
23195121Sfrankho 
23205121Sfrankho 	/*
23215121Sfrankho 	 * some devices (PCMCIA pseudofloppies) we like to treat
23225121Sfrankho 	 * as floppies, but they don't understand fdio(7I) requests.
23235121Sfrankho 	 */
23245121Sfrankho 	if (!isfloppy &&
23255121Sfrankho 	    !ldi_ioctl(lh, DKIOCINFO, argp, FKIOCTL, cr, NULL) &&
23265121Sfrankho 	    (arg.ci.dki_ctype == DKC_WDC2880 ||
23275121Sfrankho 	    arg.ci.dki_ctype == DKC_NCRFLOPPY ||
23285121Sfrankho 	    arg.ci.dki_ctype == DKC_SMSFLOPPY ||
23295121Sfrankho 	    arg.ci.dki_ctype == DKC_INTEL82077 ||
23305121Sfrankho 	    (arg.ci.dki_ctype == DKC_PCMCIA_MEM &&
23315121Sfrankho 	    arg.ci.dki_flags & DKI_PCMCIA_PFD)))
23325121Sfrankho 		isfloppy = 1;
23335121Sfrankho 
23345121Sfrankho 	/*
23355121Sfrankho 	 * This is the "final fallback" test - media with
23365121Sfrankho 	 * 2 heads and 80 cylinders are assumed to be floppies.
23375121Sfrankho 	 * This is normally true for USB floppy drives ...
23385121Sfrankho 	 */
23395121Sfrankho 	if (!isfloppy &&
23405121Sfrankho 	    !ldi_ioctl(lh, DKIOCGGEOM, argp, FKIOCTL, cr, NULL) &&
23415121Sfrankho 	    (arg.gi.dkg_ncyl == 80 && arg.gi.dkg_nhead == 2))
23425121Sfrankho 		isfloppy = 1;
23435121Sfrankho 
23445121Sfrankho 	/*
23455121Sfrankho 	 * This is similar to the "old" PCFS code that sets this flag
23465121Sfrankho 	 * just based on the media descriptor being 0xf8 (MD_FIXED).
23475121Sfrankho 	 * Should be re-worked. We really need some specialcasing for
23485121Sfrankho 	 * removeable media.
23495121Sfrankho 	 */
23505121Sfrankho 	if (!isfloppy) {
23515121Sfrankho 		fsp->pcfs_flags |= PCFS_NOCHK;
23525121Sfrankho 	}
23535121Sfrankho 
23545121Sfrankho 	/*
23555121Sfrankho 	 * We automatically disable access time updates if the medium is
23565121Sfrankho 	 * removeable and/or hotpluggable, and the admin did not explicitly
23575121Sfrankho 	 * request access time updates (via the "atime" mount option).
23585121Sfrankho 	 * The majority of flash-based media should fit this category.
23595121Sfrankho 	 * Minimizing write access extends the lifetime of your memory stick !
23605121Sfrankho 	 */
23615121Sfrankho 	if (!vfs_optionisset(fsp->pcfs_vfs, MNTOPT_ATIME, NULL) &&
23625121Sfrankho 	    (isremoveable || ishotpluggable | isfloppy)) {
23635121Sfrankho 		fsp->pcfs_flags |= PCFS_NOATIME;
23645121Sfrankho 	}
23655121Sfrankho 
23665121Sfrankho 	(void) ldi_close(lh, FREAD, cr);
23675121Sfrankho out:
23685121Sfrankho 	if (fsp->pcfs_secsize == 0) {
23695121Sfrankho 		PC_DPRINTF3(1, "!pcfs: media block size autodetection "
23705121Sfrankho 		    "device (%x.%x) failed, no user-provided fallback. "
23715121Sfrankho 		    "Using %d bytes.\n",
23725121Sfrankho 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
23735121Sfrankho 		    DEV_BSIZE);
23745121Sfrankho 		fsp->pcfs_secsize = DEV_BSIZE;
23755121Sfrankho 		fsp->pcfs_sdshift = 0;
23765121Sfrankho 	}
23775121Sfrankho 	ASSERT(fsp->pcfs_secsize % DEV_BSIZE == 0);
23785121Sfrankho 	ASSERT(VALID_SECSIZE(fsp->pcfs_secsize));
23795121Sfrankho }
23805121Sfrankho 
23815121Sfrankho /*
23825121Sfrankho  * Get the FAT type for the DOS medium.
23835121Sfrankho  *
23845121Sfrankho  * -------------------------
23855121Sfrankho  * According to Microsoft:
23865121Sfrankho  *   The FAT type one of FAT12, FAT16, or FAT32 is determined by the
23875121Sfrankho  * count of clusters on the volume and nothing else.
23885121Sfrankho  * -------------------------
23895121Sfrankho  *
23905121Sfrankho  */
23915121Sfrankho static int
23925121Sfrankho pc_getfattype(struct pcfs *fsp)
23935121Sfrankho {
23945121Sfrankho 	int error = 0;
23955121Sfrankho 	buf_t *bp = NULL;
23965121Sfrankho 	struct vnode *devvp = fsp->pcfs_devvp;
23975121Sfrankho 	dev_t	dev = devvp->v_rdev;
23985121Sfrankho 
23995121Sfrankho 	/*
24005121Sfrankho 	 * Detect the native block size of the medium, and attempt to
24015121Sfrankho 	 * detect whether the medium is removeable.
24025121Sfrankho 	 * We do treat removeable media (floppies, PCMCIA memory cards,
24035121Sfrankho 	 * USB and FireWire disks) differently wrt. to the frequency
24045121Sfrankho 	 * and synchronicity of FAT updates.
24055121Sfrankho 	 * We need to know the media block size in order to be able to
24065121Sfrankho 	 * parse the partition table.
24075121Sfrankho 	 */
24085121Sfrankho 	pcfs_device_getinfo(fsp);
24095121Sfrankho 
24105121Sfrankho 	/*
24115121Sfrankho 	 * Unpartitioned media (floppies and some removeable devices)
24125121Sfrankho 	 * don't have a partition table, the FAT BPB is at disk block 0.
24135121Sfrankho 	 * Start out by reading block 0.
24145121Sfrankho 	 */
24155121Sfrankho 	fsp->pcfs_dosstart = 0;
24165121Sfrankho 	bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), fsp->pcfs_secsize);
24175121Sfrankho 
24185121Sfrankho 	if (error = geterror(bp))
24195121Sfrankho 		goto out;
24205121Sfrankho 
24215121Sfrankho 	/*
24225121Sfrankho 	 * If a logical drive number is requested, parse the partition table
24235121Sfrankho 	 * and attempt to locate it. Otherwise, proceed immediately to the
24245121Sfrankho 	 * BPB check. findTheDrive(), if successful, returns the disk block
24255121Sfrankho 	 * number where the requested partition starts in "startsec".
24265121Sfrankho 	 */
24275121Sfrankho 	if (fsp->pcfs_ldrive != 0) {
24285121Sfrankho 		PC_DPRINTF3(5, "!pcfs: pc_getfattype: using FDISK table on "
24295121Sfrankho 		    "device (%x,%x):%d to find BPB\n",
24305121Sfrankho 		    getmajor(dev), getminor(dev), fsp->pcfs_ldrive);
24315121Sfrankho 
24325121Sfrankho 		if (error = findTheDrive(fsp, &bp))
24335121Sfrankho 			goto out;
24345121Sfrankho 
24355121Sfrankho 		ASSERT(fsp->pcfs_dosstart != 0);
24365121Sfrankho 
24375121Sfrankho 		brelse(bp);
24385121Sfrankho 		bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
24395121Sfrankho 		    fsp->pcfs_secsize);
24405121Sfrankho 		if (error = geterror(bp))
24415121Sfrankho 			goto out;
24425121Sfrankho 	}
24435121Sfrankho 
24445121Sfrankho 	/*
24455121Sfrankho 	 * Validate the BPB and fill in the instance structure.
24465121Sfrankho 	 */
24475121Sfrankho 	if (!parseBPB(fsp, (uchar_t *)bp->b_un.b_addr, NULL)) {
24485121Sfrankho 		PC_DPRINTF4(1, "!pcfs: pc_getfattype: No FAT BPB on "
24495121Sfrankho 		    "device (%x.%x):%d, disk LBA %u\n",
24505121Sfrankho 		    getmajor(dev), getminor(dev), fsp->pcfs_ldrive,
24515121Sfrankho 		    (uint_t)pc_dbdaddr(fsp, fsp->pcfs_dosstart));
24525121Sfrankho 		error = EINVAL;
24535121Sfrankho 		goto out;
24545121Sfrankho 	}
24555121Sfrankho 
24565121Sfrankho 	ASSERT(fsp->pcfs_fattype != FAT_UNKNOWN);
24575121Sfrankho 
24585121Sfrankho out:
24595121Sfrankho 	/*
24605121Sfrankho 	 * Release the buffer used
24615121Sfrankho 	 */
24625121Sfrankho 	if (bp != NULL)
24635121Sfrankho 		brelse(bp);
24645121Sfrankho 	return (error);
24655121Sfrankho }
24665121Sfrankho 
24675121Sfrankho 
24685121Sfrankho /*
24695121Sfrankho  * Get the file allocation table.
24705121Sfrankho  * If there is an old FAT, invalidate it.
24715121Sfrankho  */
24725121Sfrankho int
24735121Sfrankho pc_getfat(struct pcfs *fsp)
24745121Sfrankho {
24755121Sfrankho 	struct buf *bp = NULL;
24765121Sfrankho 	uchar_t *fatp = NULL;
24775121Sfrankho 	uchar_t *fat_changemap = NULL;
24785121Sfrankho 	int error;
24795121Sfrankho 	int fat_changemapsize;
24805121Sfrankho 	int flags = 0;
24815121Sfrankho 	int nfat;
24825121Sfrankho 	int altfat_mustmatch = 0;
24835121Sfrankho 	int fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
24845121Sfrankho 
24855121Sfrankho 	if (fsp->pcfs_fatp) {
24865121Sfrankho 		/*
24875121Sfrankho 		 * There is a FAT in core.
24885121Sfrankho 		 * If there are open file pcnodes or we have modified it or
24895121Sfrankho 		 * it hasn't timed out yet use the in core FAT.
24905121Sfrankho 		 * Otherwise invalidate it and get a new one
24915121Sfrankho 		 */
24925121Sfrankho #ifdef notdef
24935121Sfrankho 		if (fsp->pcfs_frefs ||
24945121Sfrankho 		    (fsp->pcfs_flags & PCFS_FATMOD) ||
24955121Sfrankho 		    (gethrestime_sec() < fsp->pcfs_fattime)) {
24965121Sfrankho 			return (0);
24975121Sfrankho 		} else {
24985121Sfrankho 			mutex_enter(&pcfslock);
24995121Sfrankho 			pc_invalfat(fsp);
25005121Sfrankho 			mutex_exit(&pcfslock);
25015121Sfrankho 		}
25025121Sfrankho #endif /* notdef */
25035121Sfrankho 		return (0);
25045121Sfrankho 	}
25055121Sfrankho 
25065121Sfrankho 	/*
25075121Sfrankho 	 * Get FAT and check it for validity
25085121Sfrankho 	 */
25095121Sfrankho 	fatp = kmem_alloc(fatsize, KM_SLEEP);
25105121Sfrankho 	error = pc_readfat(fsp, fatp);
25115121Sfrankho 	if (error) {
25125121Sfrankho 		flags = B_ERROR;
25135121Sfrankho 		goto out;
25145121Sfrankho 	}
25155121Sfrankho 	fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1;
25165121Sfrankho 	fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP);
25175121Sfrankho 	fsp->pcfs_fatp = fatp;
25185121Sfrankho 	fsp->pcfs_fat_changemapsize = fat_changemapsize;
25195121Sfrankho 	fsp->pcfs_fat_changemap = fat_changemap;
25205121Sfrankho 
25215121Sfrankho 	/*
25225121Sfrankho 	 * The only definite signature check is that the
25235121Sfrankho 	 * media descriptor byte should match the first byte
25245121Sfrankho 	 * of the FAT block.
25255121Sfrankho 	 */
25265121Sfrankho 	if (fatp[0] != fsp->pcfs_mediadesc) {
25275121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: FAT signature mismatch, "
25285121Sfrankho 		    "media descriptor %x, FAT[0] lowbyte %x\n",
25295121Sfrankho 		    (uint32_t)fsp->pcfs_mediadesc, (uint32_t)fatp[0]);
25305121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: Enforcing alternate FAT validation\n");
25315121Sfrankho 		altfat_mustmatch = 1;
25325121Sfrankho 	}
25335121Sfrankho 
25345121Sfrankho 	/*
25355121Sfrankho 	 * Get alternate FATs and check for consistency
25365121Sfrankho 	 * This is an inlined version of pc_readfat().
25375121Sfrankho 	 * Since we're only comparing FAT and alternate FAT,
25385121Sfrankho 	 * there's no reason to let pc_readfat() copy data out
25395121Sfrankho 	 * of the buf. Instead, compare in-situ, one cluster
25405121Sfrankho 	 * at a time.
25415121Sfrankho 	 */
25425121Sfrankho 	for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) {
25435121Sfrankho 		size_t startsec;
25445121Sfrankho 		size_t off;
25455121Sfrankho 
25465121Sfrankho 		startsec = pc_dbdaddr(fsp,
25475121Sfrankho 		    fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec);
25485121Sfrankho 
25495121Sfrankho 		for (off = 0; off < fatsize; off += fsp->pcfs_clsize) {
25505121Sfrankho 			daddr_t fatblk = startsec + pc_dbdaddr(fsp,
25515121Sfrankho 			    pc_cltodb(fsp, pc_lblkno(fsp, off)));
25525121Sfrankho 
25535121Sfrankho 			bp = bread(fsp->pcfs_xdev, fatblk,
25545121Sfrankho 			    MIN(fsp->pcfs_clsize, fatsize - off));
25555121Sfrankho 			if (bp->b_flags & (B_ERROR | B_STALE)) {
25565121Sfrankho 				cmn_err(CE_NOTE,
25575121Sfrankho 				    "!pcfs: alternate FAT #%d (start LBA %p)"
25585121Sfrankho 				    " read error at offset %ld on device"
25595121Sfrankho 				    " (%x.%x):%d",
25605121Sfrankho 				    nfat, (void *)(uintptr_t)startsec, off,
25615121Sfrankho 				    getmajor(fsp->pcfs_xdev),
25625121Sfrankho 				    getminor(fsp->pcfs_xdev),
25635121Sfrankho 				    fsp->pcfs_ldrive);
25645121Sfrankho 				flags = B_ERROR;
25655121Sfrankho 				error = EIO;
25665121Sfrankho 				goto out;
25675121Sfrankho 			}
25685121Sfrankho 			bp->b_flags |= B_STALE | B_AGE;
25695121Sfrankho 			if (bcmp(bp->b_un.b_addr, fatp + off,
25705121Sfrankho 			    MIN(fsp->pcfs_clsize, fatsize - off))) {
25715121Sfrankho 				cmn_err(CE_NOTE,
25725121Sfrankho 				    "!pcfs: alternate FAT #%d (start LBA %p)"
25735121Sfrankho 				    " corrupted at offset %ld on device"
25745121Sfrankho 				    " (%x.%x):%d",
25755121Sfrankho 				    nfat, (void *)(uintptr_t)startsec, off,
25765121Sfrankho 				    getmajor(fsp->pcfs_xdev),
25775121Sfrankho 				    getminor(fsp->pcfs_xdev),
25785121Sfrankho 				    fsp->pcfs_ldrive);
25795121Sfrankho 				if (altfat_mustmatch) {
25805121Sfrankho 					flags = B_ERROR;
25815121Sfrankho 					error = EIO;
25825121Sfrankho 					goto out;
25835121Sfrankho 				}
25845121Sfrankho 			}
25855121Sfrankho 			brelse(bp);
25865121Sfrankho 			bp = NULL;	/* prevent double release */
25875121Sfrankho 		}
25885121Sfrankho 	}
25895121Sfrankho 
25905121Sfrankho 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
25915121Sfrankho 	fsp->pcfs_fatjustread = 1;
25925121Sfrankho 
25935121Sfrankho 	/*
25945121Sfrankho 	 * Retrieve FAT32 fsinfo sector.
25955121Sfrankho 	 * A failure to read this is not fatal to accessing the volume.
25965121Sfrankho 	 * It simply means operations that count or search free blocks
25975121Sfrankho 	 * will have to do a full FAT walk, vs. a possibly quicker lookup
25985121Sfrankho 	 * of the summary information.
25995121Sfrankho 	 * Hence, we log a message but return success overall after this point.
26005121Sfrankho 	 */
26015121Sfrankho 	if (IS_FAT32(fsp) && (fsp->pcfs_flags & PCFS_FSINFO_OK)) {
26025121Sfrankho 		struct fat_od_fsi *fsinfo_disk;
26035121Sfrankho 
26045121Sfrankho 		bp = bread(fsp->pcfs_xdev,
26055121Sfrankho 		    pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
26065121Sfrankho 		fsinfo_disk = (struct fat_od_fsi *)bp->b_un.b_addr;
26075121Sfrankho 		if (bp->b_flags & (B_ERROR | B_STALE) ||
26085121Sfrankho 		    !FSISIG_OK(fsinfo_disk)) {
26095121Sfrankho 			cmn_err(CE_NOTE,
26105121Sfrankho 			    "!pcfs: error reading fat32 fsinfo from "
26115121Sfrankho 			    "device (%x.%x):%d, block %lld",
26125121Sfrankho 			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
26135121Sfrankho 			    fsp->pcfs_ldrive,
26145121Sfrankho 			    (long long)pc_dbdaddr(fsp, fsp->pcfs_fsistart));
26155121Sfrankho 			fsp->pcfs_flags &= ~PCFS_FSINFO_OK;
26165121Sfrankho 			fsp->pcfs_fsinfo.fs_free_clusters = FSINFO_UNKNOWN;
26175121Sfrankho 			fsp->pcfs_fsinfo.fs_next_free = FSINFO_UNKNOWN;
26185121Sfrankho 		} else {
26195121Sfrankho 			bp->b_flags |= B_STALE | B_AGE;
26205121Sfrankho 			fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
26215121Sfrankho 			fsp->pcfs_fsinfo.fs_free_clusters =
26225121Sfrankho 			    LE_32(fsinfo_disk->fsi_incore.fs_free_clusters);
26235121Sfrankho 			fsp->pcfs_fsinfo.fs_next_free =
26245121Sfrankho 			    LE_32(fsinfo_disk->fsi_incore.fs_next_free);
26255121Sfrankho 		}
26265121Sfrankho 		brelse(bp);
26275121Sfrankho 		bp = NULL;
26285121Sfrankho 	}
26295121Sfrankho 
26305121Sfrankho 	if (pc_validcl(fsp, (pc_cluster32_t)fsp->pcfs_fsinfo.fs_next_free))
26315121Sfrankho 		fsp->pcfs_nxfrecls = fsp->pcfs_fsinfo.fs_next_free;
26325121Sfrankho 	else
26335121Sfrankho 		fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
26345121Sfrankho 
26355121Sfrankho 	return (0);
26365121Sfrankho 
26375121Sfrankho out:
26385121Sfrankho 	cmn_err(CE_NOTE, "!pcfs: illegal disk format");
26395121Sfrankho 	if (bp)
26405121Sfrankho 		brelse(bp);
26415121Sfrankho 	if (fatp)
26425121Sfrankho 		kmem_free(fatp, fatsize);
26435121Sfrankho 	if (fat_changemap)
26445121Sfrankho 		kmem_free(fat_changemap, fat_changemapsize);
26455121Sfrankho 
26465121Sfrankho 	if (flags) {
26475121Sfrankho 		pc_mark_irrecov(fsp);
26485121Sfrankho 	}
26495121Sfrankho 	return (error);
26505121Sfrankho }
2651