xref: /onnv-gate/usr/src/uts/common/fs/pcfs/pc_vfsops.c (revision 6645:3e77f5977908)
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 /*
226644Sgd78059  * Copyright 2008 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);
94*6645Sgd78059 static void pcfs_parse_mntopts(struct pcfs *fsp);
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,
1686644Sgd78059 	"PC filesystem",
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) ||
4185331Samw 	    (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
530*6645Sgd78059 pcfs_parse_mntopts(struct pcfs *fsp)
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 (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL))
5410Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_HIDDEN;
5420Sstevel@tonic-gate 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL))
5430Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_FOLDCASE;
5442720Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL))
5452720Sfrankho 		fsp->pcfs_flags |= PCFS_NOCLAMPTIME;
5465121Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL))
5475121Sfrankho 		fsp->pcfs_flags |= PCFS_NOATIME;
5485121Sfrankho 
5495121Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_TIMEZONE, &c)) {
5505121Sfrankho 		if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
5515121Sfrankho 		    endptr == c + strlen(c)) {
5525121Sfrankho 			/*
5535121Sfrankho 			 * A number alright - in the allowed range ?
5545121Sfrankho 			 */
5555121Sfrankho 			if (l <= -12*3600 || l >= 12*3600) {
5565121Sfrankho 				cmn_err(CE_WARN, "!pcfs: invalid use of "
5575121Sfrankho 				    "'timezone' mount option - %ld "
5585121Sfrankho 				    "is out of range. Assuming 0.", l);
5595121Sfrankho 				l = 0;
5605121Sfrankho 			}
5615121Sfrankho 		} else {
5625121Sfrankho 			cmn_err(CE_WARN, "!pcfs: invalid use of "
5635121Sfrankho 			    "'timezone' mount option - argument %s "
5645121Sfrankho 			    "is not a valid number. Assuming 0.", c);
5655121Sfrankho 			l = 0;
5665121Sfrankho 		}
5675121Sfrankho 		fsp->pcfs_secondswest = l;
5685121Sfrankho 	}
5695121Sfrankho 
5705121Sfrankho 	/*
5715121Sfrankho 	 * The "secsize=..." mount option is a workaround for the lack of
5725121Sfrankho 	 * lofi(7d) support for DKIOCGMEDIAINFO. If PCFS wants to parse the
5735121Sfrankho 	 * partition table of a disk image and it has been partitioned with
5745121Sfrankho 	 * sector sizes other than 512 bytes, we'd fail on loopback'ed disk
5755121Sfrankho 	 * images.
5765121Sfrankho 	 * That should really be fixed in lofi ... this is a workaround.
5775121Sfrankho 	 */
5785121Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_SECSIZE, &c)) {
5795121Sfrankho 		if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
5805121Sfrankho 		    endptr == c + strlen(c)) {
5815121Sfrankho 			/*
5825121Sfrankho 			 * A number alright - a valid sector size as well ?
5835121Sfrankho 			 */
5845121Sfrankho 			if (!VALID_SECSIZE(l)) {
5855121Sfrankho 				cmn_err(CE_WARN, "!pcfs: invalid use of "
5865121Sfrankho 				    "'secsize' mount option - %ld is "
5875121Sfrankho 				    "unsupported. Autodetecting.", l);
5885121Sfrankho 				l = 0;
5895121Sfrankho 			}
5905121Sfrankho 		} else {
5915121Sfrankho 			cmn_err(CE_WARN, "!pcfs: invalid use of "
5925121Sfrankho 			    "'secsize' mount option - argument %s "
5935121Sfrankho 			    "is not a valid number. Autodetecting.", c);
5945121Sfrankho 			l = 0;
5955121Sfrankho 		}
5965121Sfrankho 		fsp->pcfs_secsize = l;
5975121Sfrankho 		fsp->pcfs_sdshift = ddi_ffs(l / DEV_BSIZE) - 1;
5985121Sfrankho 	}
5995121Sfrankho }
6005121Sfrankho 
6015121Sfrankho /*
6025121Sfrankho  * vfs operations
6035121Sfrankho  */
6045121Sfrankho 
6055121Sfrankho /*
6065121Sfrankho  * pcfs_mount - backend for VFS_MOUNT() on PCFS.
6075121Sfrankho  */
6085121Sfrankho static int
6095121Sfrankho pcfs_mount(
6105121Sfrankho 	struct vfs *vfsp,
6115121Sfrankho 	struct vnode *mvp,
6125121Sfrankho 	struct mounta *uap,
6135121Sfrankho 	struct cred *cr)
6145121Sfrankho {
6155121Sfrankho 	struct pcfs *fsp;
6165121Sfrankho 	struct vnode *devvp;
6175121Sfrankho 	dev_t pseudodev;
6185121Sfrankho 	dev_t xdev;
6195121Sfrankho 	int dos_ldrive = 0;
6205121Sfrankho 	int error;
6215121Sfrankho 	int remounting;
6225121Sfrankho 
6235121Sfrankho 	if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
6245121Sfrankho 		return (error);
6255121Sfrankho 
6265121Sfrankho 	if (mvp->v_type != VDIR)
6275121Sfrankho 		return (ENOTDIR);
6285121Sfrankho 
6295121Sfrankho 	mutex_enter(&mvp->v_lock);
6305121Sfrankho 	if ((uap->flags & MS_REMOUNT) == 0 &&
6315121Sfrankho 	    (uap->flags & MS_OVERLAY) == 0 &&
6325121Sfrankho 	    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
6335121Sfrankho 		mutex_exit(&mvp->v_lock);
6345121Sfrankho 		return (EBUSY);
6355121Sfrankho 	}
6365121Sfrankho 	mutex_exit(&mvp->v_lock);
6375121Sfrankho 
6385121Sfrankho 	/*
6395121Sfrankho 	 * PCFS doesn't do mount arguments anymore - everything's a mount
6405121Sfrankho 	 * option these days. In order not to break existing callers, we
6415121Sfrankho 	 * don't reject it yet, just warn that the data (if any) is ignored.
6425121Sfrankho 	 */
6435121Sfrankho 	if (uap->datalen != 0)
6445121Sfrankho 		cmn_err(CE_WARN, "!pcfs: deprecated use of mount(2) with "
6455121Sfrankho 		    "mount argument structures instead of mount options. "
6465121Sfrankho 		    "Ignoring mount(2) 'dataptr' argument.");
6475121Sfrankho 
6485121Sfrankho 	/*
6496644Sgd78059 	 * This is needed early, to make sure the access / open calls
6506644Sgd78059 	 * are done using the correct mode. Processing this mount option
6516644Sgd78059 	 * only when calling pcfs_parse_mntopts() would lead us to attempt
6526644Sgd78059 	 * a read/write access to a possibly writeprotected device, and
6536644Sgd78059 	 * a readonly mount attempt might fail because of that.
6546644Sgd78059 	 */
6556644Sgd78059 	if (uap->flags & MS_RDONLY) {
6566644Sgd78059 		vfsp->vfs_flag |= VFS_RDONLY;
6576644Sgd78059 		vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
6586644Sgd78059 	}
6596644Sgd78059 
6606644Sgd78059 	/*
6615121Sfrankho 	 * For most filesystems, this is just a lookupname() on the
6625121Sfrankho 	 * mount pathname string. PCFS historically has to do its own
6635121Sfrankho 	 * partition table parsing because not all Solaris architectures
6645121Sfrankho 	 * support all styles of partitioning that PC media can have, and
6655121Sfrankho 	 * hence PCFS understands "device names" that don't map to actual
6665121Sfrankho 	 * physical device nodes. Parsing the "PCFS syntax" for device
6675121Sfrankho 	 * names is done in pcfs_device_identify() - see there.
6685121Sfrankho 	 *
6695121Sfrankho 	 * Once all block device drivers that can host FAT filesystems have
6705121Sfrankho 	 * been enhanced to create device nodes for all PC-style partitions,
6715121Sfrankho 	 * this code can go away.
6725121Sfrankho 	 */
6735121Sfrankho 	if (error = pcfs_device_identify(vfsp, uap, cr, &dos_ldrive, &xdev))
6745121Sfrankho 		return (error);
6755121Sfrankho 
6765121Sfrankho 	/*
6775121Sfrankho 	 * As with looking up the actual device to mount, PCFS cannot rely
6785121Sfrankho 	 * on just the checks done by vfs_ismounted() whether a given device
6795121Sfrankho 	 * is mounted already. The additional check against the "PCFS syntax"
6805121Sfrankho 	 * is done in  pcfs_device_ismounted().
6815121Sfrankho 	 */
6825121Sfrankho 	remounting = (uap->flags & MS_REMOUNT);
6835121Sfrankho 
6845121Sfrankho 	if (error = pcfs_device_ismounted(vfsp, dos_ldrive, xdev, &remounting,
6855121Sfrankho 	    &pseudodev))
6865121Sfrankho 		return (error);
6875121Sfrankho 
6885121Sfrankho 	if (remounting)
6895121Sfrankho 		return (0);
6905121Sfrankho 
6915121Sfrankho 	/*
6925121Sfrankho 	 * Mount the filesystem.
6935121Sfrankho 	 * An instance structure is required before the attempt to locate
6945121Sfrankho 	 * and parse the FAT BPB. This is because mount options may change
6955121Sfrankho 	 * the behaviour of the filesystem type matching code. Precreate
6965121Sfrankho 	 * it and fill it in to a degree that allows parsing the mount
6975121Sfrankho 	 * options.
6985121Sfrankho 	 */
6995121Sfrankho 	devvp = makespecvp(xdev, VBLK);
7005121Sfrankho 	if (IS_SWAPVP(devvp)) {
7015121Sfrankho 		VN_RELE(devvp);
7025121Sfrankho 		return (EBUSY);
7035121Sfrankho 	}
7045121Sfrankho 	error = VOP_OPEN(&devvp,
7055331Samw 	    (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD | FWRITE, cr, NULL);
7065121Sfrankho 	if (error) {
7075121Sfrankho 		VN_RELE(devvp);
7085121Sfrankho 		return (error);
7095121Sfrankho 	}
7105121Sfrankho 
7115121Sfrankho 	fsp = kmem_zalloc(sizeof (*fsp), KM_SLEEP);
7125121Sfrankho 	fsp->pcfs_vfs = vfsp;
7135121Sfrankho 	fsp->pcfs_xdev = xdev;
7145121Sfrankho 	fsp->pcfs_devvp = devvp;
7155121Sfrankho 	fsp->pcfs_ldrive = dos_ldrive;
7165121Sfrankho 	mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL);
7170Sstevel@tonic-gate 
718*6645Sgd78059 	pcfs_parse_mntopts(fsp);
7195121Sfrankho 
7205121Sfrankho 	/*
7215121Sfrankho 	 * This is the actual "mount" - the PCFS superblock check.
7225121Sfrankho 	 *
7235121Sfrankho 	 * Find the requested logical drive and the FAT BPB therein.
7245121Sfrankho 	 * Check device type and flag the instance if media is removeable.
7255121Sfrankho 	 *
7265121Sfrankho 	 * Initializes most members of the filesystem instance structure.
7275121Sfrankho 	 * Returns EINVAL if no valid BPB can be found. Other errors may
7285121Sfrankho 	 * occur after I/O failures, or when invalid / unparseable partition
7295121Sfrankho 	 * tables are encountered.
7305121Sfrankho 	 */
7315121Sfrankho 	if (error = pc_getfattype(fsp))
7325121Sfrankho 		goto errout;
7335121Sfrankho 
7345121Sfrankho 	/*
7356644Sgd78059 	 * Now that the BPB has been parsed, this structural information
7366644Sgd78059 	 * is available and known to be valid. Initialize the VFS.
7376644Sgd78059 	 */
7386644Sgd78059 	vfsp->vfs_data = fsp;
7396644Sgd78059 	vfsp->vfs_dev = pseudodev;
7406644Sgd78059 	vfsp->vfs_fstype = pcfstype;
7416644Sgd78059 	vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype);
7426644Sgd78059 	vfsp->vfs_bcount = 0;
7436644Sgd78059 	vfsp->vfs_bsize = fsp->pcfs_clsize;
7446644Sgd78059 
7456644Sgd78059 	/*
7465121Sfrankho 	 * Validate that we can access the FAT and that it is, to the
7475121Sfrankho 	 * degree we can verify here, self-consistent.
7485121Sfrankho 	 */
7495121Sfrankho 	if (error = pc_verify(fsp))
7505121Sfrankho 		goto errout;
7515121Sfrankho 
7525121Sfrankho 	/*
7535121Sfrankho 	 * Record the time of the mount, to return as an "approximate"
7545121Sfrankho 	 * timestamp for the FAT root directory. Since FAT roots don't
7555121Sfrankho 	 * have timestamps, this is less confusing to the user than
7565121Sfrankho 	 * claiming "zero" / Jan/01/1970.
7575121Sfrankho 	 */
7585121Sfrankho 	gethrestime(&fsp->pcfs_mounttime);
7595121Sfrankho 
7605121Sfrankho 	/*
7615121Sfrankho 	 * Fix up the mount options. Because "noatime" is made default on
7625121Sfrankho 	 * removeable media only, a fixed disk will have neither "atime"
7635121Sfrankho 	 * nor "noatime" set. We set the options explicitly depending on
7645121Sfrankho 	 * the PCFS_NOATIME flag, to inform the user of what applies.
7655121Sfrankho 	 * Mount option cancellation will take care that the mutually
7665121Sfrankho 	 * exclusive 'other' is cleared.
7675121Sfrankho 	 */
7685121Sfrankho 	vfs_setmntopt(vfsp,
7695121Sfrankho 	    fsp->pcfs_flags & PCFS_NOATIME ? MNTOPT_NOATIME : MNTOPT_ATIME,
7705121Sfrankho 	    NULL, 0);
7715121Sfrankho 
7725121Sfrankho 	/*
7735121Sfrankho 	 * All clear - insert the FS instance into PCFS' list.
7745121Sfrankho 	 */
7750Sstevel@tonic-gate 	mutex_enter(&pcfslock);
7760Sstevel@tonic-gate 	fsp->pcfs_nxt = pc_mounttab;
7770Sstevel@tonic-gate 	pc_mounttab = fsp;
7780Sstevel@tonic-gate 	mutex_exit(&pcfslock);
7792720Sfrankho 	atomic_inc_32(&pcfs_mountcount);
7800Sstevel@tonic-gate 	return (0);
7815121Sfrankho 
7825121Sfrankho errout:
7835121Sfrankho 	(void) VOP_CLOSE(devvp,
7845121Sfrankho 	    vfsp->vfs_flag & VFS_RDONLY ? FREAD : FREAD | FWRITE,
7855331Samw 	    1, (offset_t)0, cr, NULL);
7865121Sfrankho 	VN_RELE(devvp);
7875121Sfrankho 	mutex_destroy(&fsp->pcfs_lock);
7885121Sfrankho 	kmem_free(fsp, sizeof (*fsp));
7895121Sfrankho 	return (error);
7905121Sfrankho 
7910Sstevel@tonic-gate }
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate static int
7940Sstevel@tonic-gate pcfs_unmount(
7950Sstevel@tonic-gate 	struct vfs *vfsp,
7960Sstevel@tonic-gate 	int flag,
7970Sstevel@tonic-gate 	struct cred *cr)
7980Sstevel@tonic-gate {
7990Sstevel@tonic-gate 	struct pcfs *fsp, *fsp1;
8000Sstevel@tonic-gate 
8010Sstevel@tonic-gate 	if (secpolicy_fs_unmount(cr, vfsp) != 0)
8020Sstevel@tonic-gate 		return (EPERM);
8030Sstevel@tonic-gate 
8040Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
8052720Sfrankho 
8060Sstevel@tonic-gate 	/*
8070Sstevel@tonic-gate 	 * We don't have to lock fsp because the VVFSLOCK in vfs layer will
8080Sstevel@tonic-gate 	 * prevent lookuppn from crossing the mount point.
8092720Sfrankho 	 * If this is not a forced umount request and there's ongoing I/O,
8102720Sfrankho 	 * don't allow the mount to proceed.
8110Sstevel@tonic-gate 	 */
8122720Sfrankho 	if (flag & MS_FORCE)
8132720Sfrankho 		vfsp->vfs_flag |= VFS_UNMOUNTED;
8142720Sfrankho 	else if (fsp->pcfs_nrefs)
8150Sstevel@tonic-gate 		return (EBUSY);
8162720Sfrankho 
8172720Sfrankho 	mutex_enter(&pcfslock);
8180Sstevel@tonic-gate 
8190Sstevel@tonic-gate 	/*
8202720Sfrankho 	 * If this is a forced umount request or if the fs instance has
8212720Sfrankho 	 * been marked as beyond recovery, allow the umount to proceed
8222720Sfrankho 	 * regardless of state. pc_diskchanged() forcibly releases all
8232720Sfrankho 	 * inactive vnodes/pcnodes.
8240Sstevel@tonic-gate 	 */
8252720Sfrankho 	if (flag & MS_FORCE || fsp->pcfs_flags & PCFS_IRRECOV) {
8260Sstevel@tonic-gate 		rw_enter(&pcnodes_lock, RW_WRITER);
8270Sstevel@tonic-gate 		pc_diskchanged(fsp);
8280Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
8290Sstevel@tonic-gate 	}
8300Sstevel@tonic-gate 
8310Sstevel@tonic-gate 	/* now there should be no pcp node on pcfhead or pcdhead. */
8320Sstevel@tonic-gate 
8330Sstevel@tonic-gate 	if (fsp == pc_mounttab) {
8340Sstevel@tonic-gate 		pc_mounttab = fsp->pcfs_nxt;
8350Sstevel@tonic-gate 	} else {
8360Sstevel@tonic-gate 		for (fsp1 = pc_mounttab; fsp1 != NULL; fsp1 = fsp1->pcfs_nxt)
8370Sstevel@tonic-gate 			if (fsp1->pcfs_nxt == fsp)
8380Sstevel@tonic-gate 				fsp1->pcfs_nxt = fsp->pcfs_nxt;
8390Sstevel@tonic-gate 	}
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate 	mutex_exit(&pcfslock);
8420Sstevel@tonic-gate 
8432720Sfrankho 	/*
8442720Sfrankho 	 * Since we support VFS_FREEVFS(), there's no need to
8452720Sfrankho 	 * free the fsp right now. The framework will tell us
8462720Sfrankho 	 * when the right time to do so has arrived by calling
8472720Sfrankho 	 * into pcfs_freevfs.
8482720Sfrankho 	 */
8490Sstevel@tonic-gate 	return (0);
8500Sstevel@tonic-gate }
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate /*
8530Sstevel@tonic-gate  * find root of pcfs
8540Sstevel@tonic-gate  */
8550Sstevel@tonic-gate static int
8560Sstevel@tonic-gate pcfs_root(
8570Sstevel@tonic-gate 	struct vfs *vfsp,
8580Sstevel@tonic-gate 	struct vnode **vpp)
8590Sstevel@tonic-gate {
8600Sstevel@tonic-gate 	struct pcfs *fsp;
8610Sstevel@tonic-gate 	struct pcnode *pcp;
8620Sstevel@tonic-gate 	int error;
8630Sstevel@tonic-gate 
8640Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
8650Sstevel@tonic-gate 	if (error = pc_lockfs(fsp, 0, 0))
8660Sstevel@tonic-gate 		return (error);
8672720Sfrankho 
8680Sstevel@tonic-gate 	pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
8690Sstevel@tonic-gate 	pc_unlockfs(fsp);
8700Sstevel@tonic-gate 	*vpp = PCTOV(pcp);
8710Sstevel@tonic-gate 	pcp->pc_flags |= PC_EXTERNAL;
8720Sstevel@tonic-gate 	return (0);
8730Sstevel@tonic-gate }
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate /*
8760Sstevel@tonic-gate  * Get file system statistics.
8770Sstevel@tonic-gate  */
8780Sstevel@tonic-gate static int
8790Sstevel@tonic-gate pcfs_statvfs(
8800Sstevel@tonic-gate 	struct vfs *vfsp,
8810Sstevel@tonic-gate 	struct statvfs64 *sp)
8820Sstevel@tonic-gate {
8830Sstevel@tonic-gate 	struct pcfs *fsp;
8840Sstevel@tonic-gate 	int error;
8850Sstevel@tonic-gate 	dev32_t d32;
8860Sstevel@tonic-gate 
8870Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
8880Sstevel@tonic-gate 	error = pc_getfat(fsp);
8890Sstevel@tonic-gate 	if (error)
8900Sstevel@tonic-gate 		return (error);
8910Sstevel@tonic-gate 	bzero(sp, sizeof (*sp));
8920Sstevel@tonic-gate 	sp->f_bsize = sp->f_frsize = fsp->pcfs_clsize;
8930Sstevel@tonic-gate 	sp->f_blocks = (fsblkcnt64_t)fsp->pcfs_ncluster;
8940Sstevel@tonic-gate 	sp->f_bavail = sp->f_bfree = (fsblkcnt64_t)pc_freeclusters(fsp);
8950Sstevel@tonic-gate 	sp->f_files = (fsfilcnt64_t)-1;
8960Sstevel@tonic-gate 	sp->f_ffree = (fsfilcnt64_t)-1;
8970Sstevel@tonic-gate 	sp->f_favail = (fsfilcnt64_t)-1;
8980Sstevel@tonic-gate #ifdef notdef
8990Sstevel@tonic-gate 	(void) cmpldev(&d32, fsp->pcfs_devvp->v_rdev);
9000Sstevel@tonic-gate #endif /* notdef */
9010Sstevel@tonic-gate 	(void) cmpldev(&d32, vfsp->vfs_dev);
9020Sstevel@tonic-gate 	sp->f_fsid = d32;
9030Sstevel@tonic-gate 	(void) strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name);
9040Sstevel@tonic-gate 	sp->f_flag = vf_to_stf(vfsp->vfs_flag);
9050Sstevel@tonic-gate 	sp->f_namemax = PCFNAMESIZE;
9060Sstevel@tonic-gate 	return (0);
9070Sstevel@tonic-gate }
9080Sstevel@tonic-gate 
9090Sstevel@tonic-gate static int
9100Sstevel@tonic-gate pc_syncfsnodes(struct pcfs *fsp)
9110Sstevel@tonic-gate {
9120Sstevel@tonic-gate 	struct pchead *hp;
9130Sstevel@tonic-gate 	struct pcnode *pcp;
9140Sstevel@tonic-gate 	int error;
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate 	if (error = pc_lockfs(fsp, 0, 0))
9170Sstevel@tonic-gate 		return (error);
9180Sstevel@tonic-gate 
9190Sstevel@tonic-gate 	if (!(error = pc_syncfat(fsp))) {
9200Sstevel@tonic-gate 		hp = pcfhead;
9210Sstevel@tonic-gate 		while (hp < & pcfhead [ NPCHASH ]) {
9220Sstevel@tonic-gate 			rw_enter(&pcnodes_lock, RW_READER);
9230Sstevel@tonic-gate 			pcp = hp->pch_forw;
9240Sstevel@tonic-gate 			while (pcp != (struct pcnode *)hp) {
9250Sstevel@tonic-gate 				if (VFSTOPCFS(PCTOV(pcp) -> v_vfsp) == fsp)
9260Sstevel@tonic-gate 					if (error = pc_nodesync(pcp))
9270Sstevel@tonic-gate 						break;
9280Sstevel@tonic-gate 				pcp = pcp -> pc_forw;
9290Sstevel@tonic-gate 			}
9300Sstevel@tonic-gate 			rw_exit(&pcnodes_lock);
9310Sstevel@tonic-gate 			if (error)
9320Sstevel@tonic-gate 				break;
9330Sstevel@tonic-gate 			hp++;
9340Sstevel@tonic-gate 		}
9350Sstevel@tonic-gate 	}
9360Sstevel@tonic-gate 	pc_unlockfs(fsp);
9370Sstevel@tonic-gate 	return (error);
9380Sstevel@tonic-gate }
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate /*
9410Sstevel@tonic-gate  * Flush any pending I/O.
9420Sstevel@tonic-gate  */
9430Sstevel@tonic-gate /*ARGSUSED*/
9440Sstevel@tonic-gate static int
9450Sstevel@tonic-gate pcfs_sync(
9460Sstevel@tonic-gate 	struct vfs *vfsp,
9470Sstevel@tonic-gate 	short flag,
9480Sstevel@tonic-gate 	struct cred *cr)
9490Sstevel@tonic-gate {
9500Sstevel@tonic-gate 	struct pcfs *fsp;
9510Sstevel@tonic-gate 	int error = 0;
9520Sstevel@tonic-gate 
9530Sstevel@tonic-gate 	/* this prevents the filesystem from being umounted. */
9540Sstevel@tonic-gate 	mutex_enter(&pcfslock);
9550Sstevel@tonic-gate 	if (vfsp != NULL) {
9560Sstevel@tonic-gate 		fsp = VFSTOPCFS(vfsp);
9570Sstevel@tonic-gate 		if (!(fsp->pcfs_flags & PCFS_IRRECOV)) {
9580Sstevel@tonic-gate 			error = pc_syncfsnodes(fsp);
9590Sstevel@tonic-gate 		} else {
9600Sstevel@tonic-gate 			rw_enter(&pcnodes_lock, RW_WRITER);
9610Sstevel@tonic-gate 			pc_diskchanged(fsp);
9620Sstevel@tonic-gate 			rw_exit(&pcnodes_lock);
9630Sstevel@tonic-gate 			error = EIO;
9640Sstevel@tonic-gate 		}
9650Sstevel@tonic-gate 	} else {
9660Sstevel@tonic-gate 		fsp = pc_mounttab;
9670Sstevel@tonic-gate 		while (fsp != NULL) {
9680Sstevel@tonic-gate 			if (fsp->pcfs_flags & PCFS_IRRECOV) {
9690Sstevel@tonic-gate 				rw_enter(&pcnodes_lock, RW_WRITER);
9700Sstevel@tonic-gate 				pc_diskchanged(fsp);
9710Sstevel@tonic-gate 				rw_exit(&pcnodes_lock);
9720Sstevel@tonic-gate 				error = EIO;
9730Sstevel@tonic-gate 				break;
9740Sstevel@tonic-gate 			}
9750Sstevel@tonic-gate 			error = pc_syncfsnodes(fsp);
9760Sstevel@tonic-gate 			if (error) break;
9770Sstevel@tonic-gate 			fsp = fsp->pcfs_nxt;
9780Sstevel@tonic-gate 		}
9790Sstevel@tonic-gate 	}
9800Sstevel@tonic-gate 	mutex_exit(&pcfslock);
9810Sstevel@tonic-gate 	return (error);
9820Sstevel@tonic-gate }
9830Sstevel@tonic-gate 
9840Sstevel@tonic-gate int
9850Sstevel@tonic-gate pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing)
9860Sstevel@tonic-gate {
9872720Sfrankho 	int err;
9882720Sfrankho 
9890Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_IRRECOV) && !releasing)
9900Sstevel@tonic-gate 		return (EIO);
9910Sstevel@tonic-gate 
9920Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_LOCKED) && (fsp->pcfs_owner == curthread)) {
9930Sstevel@tonic-gate 		fsp->pcfs_count++;
9940Sstevel@tonic-gate 	} else {
9950Sstevel@tonic-gate 		mutex_enter(&fsp->pcfs_lock);
9960Sstevel@tonic-gate 		if (fsp->pcfs_flags & PCFS_LOCKED)
9970Sstevel@tonic-gate 			panic("pc_lockfs");
9980Sstevel@tonic-gate 		/*
9990Sstevel@tonic-gate 		 * We check the IRRECOV bit again just in case somebody
10000Sstevel@tonic-gate 		 * snuck past the initial check but then got held up before
10010Sstevel@tonic-gate 		 * they could grab the lock.  (And in the meantime someone
10020Sstevel@tonic-gate 		 * had grabbed the lock and set the bit)
10030Sstevel@tonic-gate 		 */
1004607Swyllys 		if (!diskchanged && !(fsp->pcfs_flags & PCFS_IRRECOV)) {
10052720Sfrankho 			if ((err = pc_getfat(fsp))) {
10062720Sfrankho 				mutex_exit(&fsp->pcfs_lock);
1007607Swyllys 				return (err);
10082720Sfrankho 			}
1009607Swyllys 		}
10100Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_LOCKED;
10110Sstevel@tonic-gate 		fsp->pcfs_owner = curthread;
10120Sstevel@tonic-gate 		fsp->pcfs_count++;
10130Sstevel@tonic-gate 	}
10140Sstevel@tonic-gate 	return (0);
10150Sstevel@tonic-gate }
10160Sstevel@tonic-gate 
10170Sstevel@tonic-gate void
10180Sstevel@tonic-gate pc_unlockfs(struct pcfs *fsp)
10190Sstevel@tonic-gate {
10200Sstevel@tonic-gate 
10210Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_LOCKED) == 0)
10220Sstevel@tonic-gate 		panic("pc_unlockfs");
10230Sstevel@tonic-gate 	if (--fsp->pcfs_count < 0)
10240Sstevel@tonic-gate 		panic("pc_unlockfs: count");
10250Sstevel@tonic-gate 	if (fsp->pcfs_count == 0) {
10260Sstevel@tonic-gate 		fsp->pcfs_flags &= ~PCFS_LOCKED;
10270Sstevel@tonic-gate 		fsp->pcfs_owner = 0;
10280Sstevel@tonic-gate 		mutex_exit(&fsp->pcfs_lock);
10290Sstevel@tonic-gate 	}
10300Sstevel@tonic-gate }
10310Sstevel@tonic-gate 
10320Sstevel@tonic-gate int
10330Sstevel@tonic-gate pc_syncfat(struct pcfs *fsp)
10340Sstevel@tonic-gate {
10350Sstevel@tonic-gate 	struct buf *bp;
10360Sstevel@tonic-gate 	int nfat;
10375121Sfrankho 	int	error = 0;
10385121Sfrankho 	struct fat_od_fsi *fsinfo_disk;
10390Sstevel@tonic-gate 
10400Sstevel@tonic-gate 	if ((fsp->pcfs_fatp == (uchar_t *)0) ||
10410Sstevel@tonic-gate 	    !(fsp->pcfs_flags & PCFS_FATMOD))
10420Sstevel@tonic-gate 		return (0);
10430Sstevel@tonic-gate 	/*
10440Sstevel@tonic-gate 	 * write out all copies of FATs
10450Sstevel@tonic-gate 	 */
10460Sstevel@tonic-gate 	fsp->pcfs_flags &= ~PCFS_FATMOD;
10470Sstevel@tonic-gate 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
10480Sstevel@tonic-gate 	for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) {
10495121Sfrankho 		error = pc_writefat(fsp, pc_dbdaddr(fsp,
10505121Sfrankho 		    fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec));
10510Sstevel@tonic-gate 		if (error) {
10520Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
10530Sstevel@tonic-gate 			return (EIO);
10540Sstevel@tonic-gate 		}
10550Sstevel@tonic-gate 	}
10560Sstevel@tonic-gate 	pc_clear_fatchanges(fsp);
10575121Sfrankho 
10585121Sfrankho 	/*
10595121Sfrankho 	 * Write out fsinfo sector.
10605121Sfrankho 	 */
10610Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
10620Sstevel@tonic-gate 		bp = bread(fsp->pcfs_xdev,
10635121Sfrankho 		    pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
10640Sstevel@tonic-gate 		if (bp->b_flags & (B_ERROR | B_STALE)) {
10655121Sfrankho 			error = geterror(bp);
10660Sstevel@tonic-gate 		}
10675121Sfrankho 		fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
10685121Sfrankho 		if (!error && FSISIG_OK(fsinfo_disk)) {
10695121Sfrankho 			fsinfo_disk->fsi_incore.fs_free_clusters =
10705121Sfrankho 			    LE_32(fsp->pcfs_fsinfo.fs_free_clusters);
10715121Sfrankho 			fsinfo_disk->fsi_incore.fs_next_free =
10725121Sfrankho 			    LE_32(FSINFO_UNKNOWN);
10735121Sfrankho 			bwrite2(bp);
10745121Sfrankho 			error = geterror(bp);
10755121Sfrankho 		}
10760Sstevel@tonic-gate 		brelse(bp);
10770Sstevel@tonic-gate 		if (error) {
10780Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
10790Sstevel@tonic-gate 			return (EIO);
10800Sstevel@tonic-gate 		}
10810Sstevel@tonic-gate 	}
10820Sstevel@tonic-gate 	return (0);
10830Sstevel@tonic-gate }
10840Sstevel@tonic-gate 
10850Sstevel@tonic-gate void
10860Sstevel@tonic-gate pc_invalfat(struct pcfs *fsp)
10870Sstevel@tonic-gate {
10880Sstevel@tonic-gate 	struct pcfs *xfsp;
10890Sstevel@tonic-gate 	int mount_cnt = 0;
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate 	if (fsp->pcfs_fatp == (uchar_t *)0)
10920Sstevel@tonic-gate 		panic("pc_invalfat");
10930Sstevel@tonic-gate 	/*
10940Sstevel@tonic-gate 	 * Release FAT
10950Sstevel@tonic-gate 	 */
10965121Sfrankho 	kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsec * fsp->pcfs_secsize);
10970Sstevel@tonic-gate 	fsp->pcfs_fatp = NULL;
10980Sstevel@tonic-gate 	kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize);
10990Sstevel@tonic-gate 	fsp->pcfs_fat_changemap = NULL;
11000Sstevel@tonic-gate 	/*
11010Sstevel@tonic-gate 	 * Invalidate all the blocks associated with the device.
11020Sstevel@tonic-gate 	 * Not needed if stateless.
11030Sstevel@tonic-gate 	 */
11040Sstevel@tonic-gate 	for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt)
11050Sstevel@tonic-gate 		if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev)
11060Sstevel@tonic-gate 			mount_cnt++;
11070Sstevel@tonic-gate 
11080Sstevel@tonic-gate 	if (!mount_cnt)
11090Sstevel@tonic-gate 		binval(fsp->pcfs_xdev);
11100Sstevel@tonic-gate 	/*
11110Sstevel@tonic-gate 	 * close mounted device
11120Sstevel@tonic-gate 	 */
11130Sstevel@tonic-gate 	(void) VOP_CLOSE(fsp->pcfs_devvp,
11140Sstevel@tonic-gate 	    (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
11155331Samw 	    1, (offset_t)0, CRED(), NULL);
11160Sstevel@tonic-gate }
11170Sstevel@tonic-gate 
11180Sstevel@tonic-gate void
11190Sstevel@tonic-gate pc_badfs(struct pcfs *fsp)
11200Sstevel@tonic-gate {
11215121Sfrankho 	cmn_err(CE_WARN, "corrupted PC file system on dev (%x.%x):%d\n",
11220Sstevel@tonic-gate 	    getmajor(fsp->pcfs_devvp->v_rdev),
11235121Sfrankho 	    getminor(fsp->pcfs_devvp->v_rdev), fsp->pcfs_ldrive);
11240Sstevel@tonic-gate }
11250Sstevel@tonic-gate 
11260Sstevel@tonic-gate /*
11270Sstevel@tonic-gate  * The problem with supporting NFS on the PCFS filesystem is that there
11280Sstevel@tonic-gate  * is no good place to keep the generation number. The only possible
11290Sstevel@tonic-gate  * place is inside a directory entry. There are a few words that we
11300Sstevel@tonic-gate  * don't use - they store NT & OS/2 attributes, and the creation/last access
11310Sstevel@tonic-gate  * time of the file - but it seems wrong to use them. In addition, directory
11320Sstevel@tonic-gate  * entries come and go. If a directory is removed completely, its directory
11330Sstevel@tonic-gate  * blocks are freed and the generation numbers are lost. Whereas in ufs,
11340Sstevel@tonic-gate  * inode blocks are dedicated for inodes, so the generation numbers are
11350Sstevel@tonic-gate  * permanently kept on the disk.
11360Sstevel@tonic-gate  */
11370Sstevel@tonic-gate static int
11380Sstevel@tonic-gate pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp)
11390Sstevel@tonic-gate {
11400Sstevel@tonic-gate 	struct pcnode *pcp;
11410Sstevel@tonic-gate 	struct pc_fid *pcfid;
11420Sstevel@tonic-gate 	struct pcfs *fsp;
11430Sstevel@tonic-gate 	struct pcdir *ep;
11440Sstevel@tonic-gate 	daddr_t eblkno;
11450Sstevel@tonic-gate 	int eoffset;
11460Sstevel@tonic-gate 	struct buf *bp;
11470Sstevel@tonic-gate 	int error;
11480Sstevel@tonic-gate 	pc_cluster32_t	cn;
11490Sstevel@tonic-gate 
11500Sstevel@tonic-gate 	pcfid = (struct pc_fid *)fidp;
11510Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
11520Sstevel@tonic-gate 
11530Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
11540Sstevel@tonic-gate 	if (error) {
11550Sstevel@tonic-gate 		*vpp = NULL;
11560Sstevel@tonic-gate 		return (error);
11570Sstevel@tonic-gate 	}
11580Sstevel@tonic-gate 
11590Sstevel@tonic-gate 	if (pcfid->pcfid_block == 0) {
11600Sstevel@tonic-gate 		pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
11610Sstevel@tonic-gate 		pcp->pc_flags |= PC_EXTERNAL;
11620Sstevel@tonic-gate 		*vpp = PCTOV(pcp);
11630Sstevel@tonic-gate 		pc_unlockfs(fsp);
11640Sstevel@tonic-gate 		return (0);
11650Sstevel@tonic-gate 	}
11660Sstevel@tonic-gate 	eblkno = pcfid->pcfid_block;
11670Sstevel@tonic-gate 	eoffset = pcfid->pcfid_offset;
11685121Sfrankho 
11690Sstevel@tonic-gate 	if ((pc_dbtocl(fsp,
11700Sstevel@tonic-gate 	    eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) ||
11710Sstevel@tonic-gate 	    (eoffset > fsp->pcfs_clsize)) {
11720Sstevel@tonic-gate 		pc_unlockfs(fsp);
11730Sstevel@tonic-gate 		*vpp = NULL;
11740Sstevel@tonic-gate 		return (EINVAL);
11750Sstevel@tonic-gate 	}
11760Sstevel@tonic-gate 
11775121Sfrankho 	if (eblkno >= fsp->pcfs_datastart || (eblkno - fsp->pcfs_rdirstart)
11780Sstevel@tonic-gate 	    < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
11795121Sfrankho 		bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
11805121Sfrankho 		    fsp->pcfs_clsize);
11810Sstevel@tonic-gate 	} else {
11825121Sfrankho 		/*
11835121Sfrankho 		 * This is an access "backwards" into the FAT12/FAT16
11845121Sfrankho 		 * root directory. A better code structure would
11855121Sfrankho 		 * significantly improve maintainability here ...
11865121Sfrankho 		 */
11875121Sfrankho 		bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
11880Sstevel@tonic-gate 		    (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize);
11890Sstevel@tonic-gate 	}
11900Sstevel@tonic-gate 	if (bp->b_flags & (B_ERROR | B_STALE)) {
11910Sstevel@tonic-gate 		error = geterror(bp);
11920Sstevel@tonic-gate 		brelse(bp);
11930Sstevel@tonic-gate 		if (error)
11940Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
11950Sstevel@tonic-gate 		*vpp = NULL;
11960Sstevel@tonic-gate 		pc_unlockfs(fsp);
11970Sstevel@tonic-gate 		return (error);
11980Sstevel@tonic-gate 	}
11990Sstevel@tonic-gate 	ep = (struct pcdir *)(bp->b_un.b_addr + eoffset);
12000Sstevel@tonic-gate 	/*
12010Sstevel@tonic-gate 	 * Ok, if this is a valid file handle that we gave out,
12020Sstevel@tonic-gate 	 * then simply ensuring that the creation time matches,
12030Sstevel@tonic-gate 	 * the entry has not been deleted, and it has a valid first
12040Sstevel@tonic-gate 	 * character should be enough.
12050Sstevel@tonic-gate 	 *
12060Sstevel@tonic-gate 	 * Unfortunately, verifying that the <blkno, offset> _still_
12070Sstevel@tonic-gate 	 * refers to a directory entry is not easy, since we'd have
12080Sstevel@tonic-gate 	 * to search _all_ directories starting from root to find it.
12090Sstevel@tonic-gate 	 * That's a high price to pay just in case somebody is forging
12100Sstevel@tonic-gate 	 * file handles. So instead we verify that as much of the
12110Sstevel@tonic-gate 	 * entry is valid as we can:
12120Sstevel@tonic-gate 	 *
12130Sstevel@tonic-gate 	 * 1. The starting cluster is 0 (unallocated) or valid
12140Sstevel@tonic-gate 	 * 2. It is not an LFN entry
12150Sstevel@tonic-gate 	 * 3. It is not hidden (unless mounted as such)
12160Sstevel@tonic-gate 	 * 4. It is not the label
12170Sstevel@tonic-gate 	 */
12180Sstevel@tonic-gate 	cn = pc_getstartcluster(fsp, ep);
12190Sstevel@tonic-gate 	/*
12200Sstevel@tonic-gate 	 * if the starting cluster is valid, but not valid according
12210Sstevel@tonic-gate 	 * to pc_validcl(), force it to be to simplify the following if.
12220Sstevel@tonic-gate 	 */
12230Sstevel@tonic-gate 	if (cn == 0)
12240Sstevel@tonic-gate 		cn = PCF_FIRSTCLUSTER;
12250Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
12260Sstevel@tonic-gate 		if (cn >= PCF_LASTCLUSTER32)
12270Sstevel@tonic-gate 			cn = PCF_FIRSTCLUSTER;
12280Sstevel@tonic-gate 	} else {
12290Sstevel@tonic-gate 		if (cn >= PCF_LASTCLUSTER)
12300Sstevel@tonic-gate 			cn = PCF_FIRSTCLUSTER;
12310Sstevel@tonic-gate 	}
12320Sstevel@tonic-gate 	if ((!pc_validcl(fsp, cn)) ||
12330Sstevel@tonic-gate 	    (PCDL_IS_LFN(ep)) ||
12340Sstevel@tonic-gate 	    (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) ||
12350Sstevel@tonic-gate 	    ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) {
12360Sstevel@tonic-gate 		bp->b_flags |= B_STALE | B_AGE;
12370Sstevel@tonic-gate 		brelse(bp);
12380Sstevel@tonic-gate 		pc_unlockfs(fsp);
12390Sstevel@tonic-gate 		return (EINVAL);
12400Sstevel@tonic-gate 	}
12410Sstevel@tonic-gate 	if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) &&
12420Sstevel@tonic-gate 	    (ep->pcd_filename[0] != PCD_ERASED) &&
12430Sstevel@tonic-gate 	    (pc_validchar(ep->pcd_filename[0]) ||
12445121Sfrankho 	    (ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) {
12450Sstevel@tonic-gate 		pcp = pc_getnode(fsp, eblkno, eoffset, ep);
12460Sstevel@tonic-gate 		pcp->pc_flags |= PC_EXTERNAL;
12470Sstevel@tonic-gate 		*vpp = PCTOV(pcp);
12480Sstevel@tonic-gate 	} else {
12490Sstevel@tonic-gate 		*vpp = NULL;
12500Sstevel@tonic-gate 	}
12510Sstevel@tonic-gate 	bp->b_flags |= B_STALE | B_AGE;
12520Sstevel@tonic-gate 	brelse(bp);
12530Sstevel@tonic-gate 	pc_unlockfs(fsp);
12540Sstevel@tonic-gate 	return (0);
12550Sstevel@tonic-gate }
12560Sstevel@tonic-gate 
12570Sstevel@tonic-gate /*
12580Sstevel@tonic-gate  * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about
12590Sstevel@tonic-gate  * a meg), so we can't bread() it all in at once. This routine reads a
12600Sstevel@tonic-gate  * fat a chunk at a time.
12610Sstevel@tonic-gate  */
12620Sstevel@tonic-gate static int
12635121Sfrankho pc_readfat(struct pcfs *fsp, uchar_t *fatp)
12640Sstevel@tonic-gate {
12650Sstevel@tonic-gate 	struct buf *bp;
12660Sstevel@tonic-gate 	size_t off;
12670Sstevel@tonic-gate 	size_t readsize;
12685121Sfrankho 	daddr_t diskblk;
12695121Sfrankho 	size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
12705121Sfrankho 	daddr_t start = fsp->pcfs_fatstart;
12710Sstevel@tonic-gate 
12720Sstevel@tonic-gate 	readsize = fsp->pcfs_clsize;
12730Sstevel@tonic-gate 	for (off = 0; off < fatsize; off += readsize, fatp += readsize) {
12740Sstevel@tonic-gate 		if (readsize > (fatsize - off))
12750Sstevel@tonic-gate 			readsize = fatsize - off;
12765121Sfrankho 		diskblk = pc_dbdaddr(fsp, start +
12775121Sfrankho 		    pc_cltodb(fsp, pc_lblkno(fsp, off)));
12785121Sfrankho 		bp = bread(fsp->pcfs_xdev, diskblk, readsize);
12790Sstevel@tonic-gate 		if (bp->b_flags & (B_ERROR | B_STALE)) {
12800Sstevel@tonic-gate 			brelse(bp);
12810Sstevel@tonic-gate 			return (EIO);
12820Sstevel@tonic-gate 		}
12830Sstevel@tonic-gate 		bp->b_flags |= B_STALE | B_AGE;
12840Sstevel@tonic-gate 		bcopy(bp->b_un.b_addr, fatp, readsize);
12850Sstevel@tonic-gate 		brelse(bp);
12860Sstevel@tonic-gate 	}
12870Sstevel@tonic-gate 	return (0);
12880Sstevel@tonic-gate }
12890Sstevel@tonic-gate 
12900Sstevel@tonic-gate /*
12910Sstevel@tonic-gate  * We write the FAT out a _lot_, in order to make sure that it
12920Sstevel@tonic-gate  * is up-to-date. But on a FAT32 system (large drive, small clusters)
12930Sstevel@tonic-gate  * the FAT might be a couple of megabytes, and writing it all out just
12940Sstevel@tonic-gate  * because we created or deleted a small file is painful (especially
12950Sstevel@tonic-gate  * since we do it for each alternate FAT too). So instead, for FAT16 and
12960Sstevel@tonic-gate  * FAT32 we only write out the bit that has changed. We don't clear
12970Sstevel@tonic-gate  * the 'updated' fields here because the caller might be writing out
12980Sstevel@tonic-gate  * several FATs, so the caller must use pc_clear_fatchanges() after
12990Sstevel@tonic-gate  * all FATs have been updated.
13005121Sfrankho  * This function doesn't take "start" from fsp->pcfs_dosstart because
13015121Sfrankho  * callers can use it to write either the primary or any of the alternate
13025121Sfrankho  * FAT tables.
13030Sstevel@tonic-gate  */
13040Sstevel@tonic-gate static int
13050Sstevel@tonic-gate pc_writefat(struct pcfs *fsp, daddr_t start)
13060Sstevel@tonic-gate {
13070Sstevel@tonic-gate 	struct buf *bp;
13080Sstevel@tonic-gate 	size_t off;
13090Sstevel@tonic-gate 	size_t writesize;
13100Sstevel@tonic-gate 	int	error;
13110Sstevel@tonic-gate 	uchar_t *fatp = fsp->pcfs_fatp;
13125121Sfrankho 	size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
13130Sstevel@tonic-gate 
13140Sstevel@tonic-gate 	writesize = fsp->pcfs_clsize;
13150Sstevel@tonic-gate 	for (off = 0; off < fatsize; off += writesize, fatp += writesize) {
13160Sstevel@tonic-gate 		if (writesize > (fatsize - off))
13170Sstevel@tonic-gate 			writesize = fatsize - off;
13180Sstevel@tonic-gate 		if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) {
13190Sstevel@tonic-gate 			continue;
13200Sstevel@tonic-gate 		}
13210Sstevel@tonic-gate 		bp = ngeteblk(writesize);
13220Sstevel@tonic-gate 		bp->b_edev = fsp->pcfs_xdev;
13230Sstevel@tonic-gate 		bp->b_dev = cmpdev(bp->b_edev);
13240Sstevel@tonic-gate 		bp->b_blkno = pc_dbdaddr(fsp, start +
13250Sstevel@tonic-gate 		    pc_cltodb(fsp, pc_lblkno(fsp, off)));
13260Sstevel@tonic-gate 		bcopy(fatp, bp->b_un.b_addr, writesize);
13270Sstevel@tonic-gate 		bwrite2(bp);
13280Sstevel@tonic-gate 		error = geterror(bp);
13290Sstevel@tonic-gate 		brelse(bp);
13300Sstevel@tonic-gate 		if (error) {
13310Sstevel@tonic-gate 			return (error);
13320Sstevel@tonic-gate 		}
13330Sstevel@tonic-gate 	}
13340Sstevel@tonic-gate 	return (0);
13350Sstevel@tonic-gate }
13360Sstevel@tonic-gate 
13370Sstevel@tonic-gate /*
13380Sstevel@tonic-gate  * Mark the FAT cluster that 'cn' is stored in as modified.
13390Sstevel@tonic-gate  */
13400Sstevel@tonic-gate void
13410Sstevel@tonic-gate pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn)
13420Sstevel@tonic-gate {
13430Sstevel@tonic-gate 	pc_cluster32_t	bn;
13440Sstevel@tonic-gate 	size_t		size;
13450Sstevel@tonic-gate 
13460Sstevel@tonic-gate 	/* which fat block is the cluster number stored in? */
13470Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
13480Sstevel@tonic-gate 		size = sizeof (pc_cluster32_t);
13490Sstevel@tonic-gate 		bn = pc_lblkno(fsp, cn * size);
13500Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
13510Sstevel@tonic-gate 	} else if (IS_FAT16(fsp)) {
13520Sstevel@tonic-gate 		size = sizeof (pc_cluster16_t);
13530Sstevel@tonic-gate 		bn = pc_lblkno(fsp, cn * size);
13540Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
13550Sstevel@tonic-gate 	} else {
13560Sstevel@tonic-gate 		offset_t off;
13570Sstevel@tonic-gate 		pc_cluster32_t nbn;
13580Sstevel@tonic-gate 
13590Sstevel@tonic-gate 		ASSERT(IS_FAT12(fsp));
13600Sstevel@tonic-gate 		off = cn + (cn >> 1);
13610Sstevel@tonic-gate 		bn = pc_lblkno(fsp, off);
13620Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
13630Sstevel@tonic-gate 		/* does this field wrap into the next fat cluster? */
13640Sstevel@tonic-gate 		nbn = pc_lblkno(fsp, off + 1);
13650Sstevel@tonic-gate 		if (nbn != bn) {
13660Sstevel@tonic-gate 			fsp->pcfs_fat_changemap[nbn] = 1;
13670Sstevel@tonic-gate 		}
13680Sstevel@tonic-gate 	}
13690Sstevel@tonic-gate }
13700Sstevel@tonic-gate 
13710Sstevel@tonic-gate /*
13720Sstevel@tonic-gate  * return whether the FAT cluster 'bn' is updated and needs to
13730Sstevel@tonic-gate  * be written out.
13740Sstevel@tonic-gate  */
13750Sstevel@tonic-gate int
13760Sstevel@tonic-gate pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn)
13770Sstevel@tonic-gate {
13780Sstevel@tonic-gate 	return (fsp->pcfs_fat_changemap[bn] == 1);
13790Sstevel@tonic-gate }
13802720Sfrankho 
13812720Sfrankho /*
13822720Sfrankho  * Implementation of VFS_FREEVFS() to support forced umounts.
13832720Sfrankho  * This is called by the vfs framework after umount, to trigger
13842720Sfrankho  * the release of any resources still associated with the given
13852720Sfrankho  * vfs_t once the need to keep them has gone away.
13862720Sfrankho  */
13872720Sfrankho void
13882720Sfrankho pcfs_freevfs(vfs_t *vfsp)
13892720Sfrankho {
13902720Sfrankho 	struct pcfs *fsp = VFSTOPCFS(vfsp);
13912720Sfrankho 
13922720Sfrankho 	mutex_enter(&pcfslock);
13935121Sfrankho 	/*
13945121Sfrankho 	 * Purging the FAT closes the device - can't do any more
13955121Sfrankho 	 * I/O after this.
13965121Sfrankho 	 */
13972720Sfrankho 	if (fsp->pcfs_fatp != (uchar_t *)0)
13982720Sfrankho 		pc_invalfat(fsp);
13992720Sfrankho 	mutex_exit(&pcfslock);
14002720Sfrankho 
14012720Sfrankho 	VN_RELE(fsp->pcfs_devvp);
14022720Sfrankho 	mutex_destroy(&fsp->pcfs_lock);
14035121Sfrankho 	kmem_free(fsp, sizeof (*fsp));
14042720Sfrankho 
14052720Sfrankho 	/*
14062720Sfrankho 	 * Allow _fini() to succeed now, if so desired.
14072720Sfrankho 	 */
14082720Sfrankho 	atomic_dec_32(&pcfs_mountcount);
14092720Sfrankho }
14105121Sfrankho 
14115121Sfrankho 
14125121Sfrankho /*
14135121Sfrankho  * PC-style partition parsing and FAT BPB identification/validation code.
14145121Sfrankho  * The partition parsers here assume:
14155121Sfrankho  *	- a FAT filesystem will be in a partition that has one of a set of
14165121Sfrankho  *	  recognized partition IDs
14175121Sfrankho  *	- the user wants the 'numbering' (C:, D:, ...) that one would get
14185121Sfrankho  *	  on MSDOS 6.x.
14195121Sfrankho  *	  That means any non-FAT partition type (NTFS, HPFS, or any Linux fs)
14205121Sfrankho  *	  will not factor in the enumeration.
14215121Sfrankho  * These days, such assumptions should be revisited. FAT is no longer the
14225121Sfrankho  * only game in 'PC town'.
14235121Sfrankho  */
14245121Sfrankho /*
14255121Sfrankho  * isDosDrive()
14265121Sfrankho  *	Boolean function.  Give it the systid field for an fdisk partition
14275121Sfrankho  *	and it decides if that's a systid that describes a DOS drive.  We
14285121Sfrankho  *	use systid values defined in sys/dktp/fdisk.h.
14295121Sfrankho  */
14305121Sfrankho static int
14315121Sfrankho isDosDrive(uchar_t checkMe)
14325121Sfrankho {
14335121Sfrankho 	return ((checkMe == DOSOS12) || (checkMe == DOSOS16) ||
14345121Sfrankho 	    (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) ||
14355121Sfrankho 	    (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) ||
14365121Sfrankho 	    (checkMe == DIAGPART));
14375121Sfrankho }
14385121Sfrankho 
14395121Sfrankho 
14405121Sfrankho /*
14415121Sfrankho  * isDosExtended()
14425121Sfrankho  *	Boolean function.  Give it the systid field for an fdisk partition
14435121Sfrankho  *	and it decides if that's a systid that describes an extended DOS
14445121Sfrankho  *	partition.
14455121Sfrankho  */
14465121Sfrankho static int
14475121Sfrankho isDosExtended(uchar_t checkMe)
14485121Sfrankho {
14495121Sfrankho 	return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA));
14505121Sfrankho }
14515121Sfrankho 
14525121Sfrankho 
14535121Sfrankho /*
14545121Sfrankho  * isBootPart()
14555121Sfrankho  *	Boolean function.  Give it the systid field for an fdisk partition
14565121Sfrankho  *	and it decides if that's a systid that describes a Solaris boot
14575121Sfrankho  *	partition.
14585121Sfrankho  */
14595121Sfrankho static int
14605121Sfrankho isBootPart(uchar_t checkMe)
14615121Sfrankho {
14625121Sfrankho 	return (checkMe == X86BOOT);
14635121Sfrankho }
14645121Sfrankho 
14655121Sfrankho 
14665121Sfrankho /*
14675121Sfrankho  * noLogicalDrive()
14685121Sfrankho  *	Display error message about not being able to find a logical
14695121Sfrankho  *	drive.
14705121Sfrankho  */
14715121Sfrankho static void
14725121Sfrankho noLogicalDrive(int ldrive)
14735121Sfrankho {
14745121Sfrankho 	if (ldrive == BOOT_PARTITION_DRIVE) {
14755121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: no boot partition");
14765121Sfrankho 	} else {
14775121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: %d: no such logical drive", ldrive);
14785121Sfrankho 	}
14795121Sfrankho }
14805121Sfrankho 
14815121Sfrankho 
14825121Sfrankho /*
14835121Sfrankho  * findTheDrive()
14845121Sfrankho  *	Discover offset of the requested logical drive, and return
14855121Sfrankho  *	that offset (startSector), the systid of that drive (sysid),
14865121Sfrankho  *	and a buffer pointer (bp), with the buffer contents being
14875121Sfrankho  *	the first sector of the logical drive (i.e., the sector that
14885121Sfrankho  *	contains the BPB for that drive).
14895121Sfrankho  *
14905121Sfrankho  * Note: this code is not capable of addressing >2TB disks, as it uses
14915121Sfrankho  *       daddr_t not diskaddr_t, some of the calculations would overflow
14925121Sfrankho  */
14935121Sfrankho #define	COPY_PTBL(mbr, ptblp)					\
14945121Sfrankho 	bcopy(&(((struct mboot *)(mbr))->parts), (ptblp),	\
14955121Sfrankho 	    FD_NUMPART * sizeof (struct ipart))
14965121Sfrankho 
14975121Sfrankho static int
14985121Sfrankho findTheDrive(struct pcfs *fsp, buf_t **bp)
14995121Sfrankho {
15005121Sfrankho 	int ldrive = fsp->pcfs_ldrive;
15015121Sfrankho 	dev_t dev = fsp->pcfs_devvp->v_rdev;
15025121Sfrankho 
15035121Sfrankho 	struct ipart dosp[FD_NUMPART];	/* incore fdisk partition structure */
15045121Sfrankho 	daddr_t lastseek = 0;		/* Disk block we sought previously */
15055121Sfrankho 	daddr_t diskblk = 0;		/* Disk block to get */
15065121Sfrankho 	daddr_t xstartsect;		/* base of Extended DOS partition */
15075121Sfrankho 	int logicalDriveCount = 0;	/* Count of logical drives seen */
15085121Sfrankho 	int extendedPart = -1;		/* index of extended dos partition */
15095121Sfrankho 	int primaryPart = -1;		/* index of primary dos partition */
15105121Sfrankho 	int bootPart = -1;		/* index of a Solaris boot partition */
15115121Sfrankho 	int xnumsect = -1;		/* length of extended DOS partition */
15125121Sfrankho 	int driveIndex;			/* computed FDISK table index */
15135121Sfrankho 	daddr_t startsec;
15145121Sfrankho 	len_t mediasize;
15155121Sfrankho 	int i;
15165121Sfrankho 	/*
15175121Sfrankho 	 * Count of drives in the current extended partition's
15185121Sfrankho 	 * FDISK table, and indexes of the drives themselves.
15195121Sfrankho 	 */
15205121Sfrankho 	int extndDrives[FD_NUMPART];
15215121Sfrankho 	int numDrives = 0;
15225121Sfrankho 
15235121Sfrankho 	/*
15245121Sfrankho 	 * Count of drives (beyond primary) in master boot record's
15255121Sfrankho 	 * FDISK table, and indexes of the drives themselves.
15265121Sfrankho 	 */
15275121Sfrankho 	int extraDrives[FD_NUMPART];
15285121Sfrankho 	int numExtraDrives = 0;
15295121Sfrankho 
15305121Sfrankho 	/*
15315121Sfrankho 	 * "ldrive == 0" should never happen, as this is a request to
15325121Sfrankho 	 * mount the physical device (and ignore partitioning). The code
15335121Sfrankho 	 * in pcfs_mount() should have made sure that a logical drive number
15345121Sfrankho 	 * is at least 1, meaning we're looking for drive "C:". It is not
15355121Sfrankho 	 * safe (and a bug in the callers of this function) to request logical
15365121Sfrankho 	 * drive number 0; we could ASSERT() but a graceful EIO is a more
15375121Sfrankho 	 * polite way.
15385121Sfrankho 	 */
15395121Sfrankho 	if (ldrive == 0) {
15405121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: request for logical partition zero");
15415121Sfrankho 		noLogicalDrive(ldrive);
15425121Sfrankho 		return (EIO);
15435121Sfrankho 	}
15445121Sfrankho 
15455121Sfrankho 	/*
15465121Sfrankho 	 *  Copy from disk block into memory aligned structure for fdisk usage.
15475121Sfrankho 	 */
15485121Sfrankho 	COPY_PTBL((*bp)->b_un.b_addr, dosp);
15495121Sfrankho 
15505121Sfrankho 	/*
15515121Sfrankho 	 * This check is ok because a FAT BPB and a master boot record (MBB)
15525121Sfrankho 	 * have the same signature, in the same position within the block.
15535121Sfrankho 	 */
15545121Sfrankho 	if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
15555121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: MBR partition table signature err, "
15565121Sfrankho 		    "device (%x.%x):%d\n",
15575121Sfrankho 		    getmajor(dev), getminor(dev), ldrive);
15585121Sfrankho 		return (EINVAL);
15595121Sfrankho 	}
15605121Sfrankho 
15615121Sfrankho 	/*
15625121Sfrankho 	 * Get a summary of what is in the Master FDISK table.
15635121Sfrankho 	 * Normally we expect to find one partition marked as a DOS drive.
15645121Sfrankho 	 * This partition is the one Windows calls the primary dos partition.
15655121Sfrankho 	 * If the machine has any logical drives then we also expect
15665121Sfrankho 	 * to find a partition marked as an extended DOS partition.
15675121Sfrankho 	 *
15685121Sfrankho 	 * Sometimes we'll find multiple partitions marked as DOS drives.
15695121Sfrankho 	 * The Solaris fdisk program allows these partitions
15705121Sfrankho 	 * to be created, but Windows fdisk no longer does.  We still need
15715121Sfrankho 	 * to support these, though, since Windows does.  We also need to fix
15725121Sfrankho 	 * our fdisk to behave like the Windows version.
15735121Sfrankho 	 *
15745121Sfrankho 	 * It turns out that some off-the-shelf media have *only* an
15755121Sfrankho 	 * Extended partition, so we need to deal with that case as well.
15765121Sfrankho 	 *
15775121Sfrankho 	 * Only a single (the first) Extended or Boot Partition will
15785121Sfrankho 	 * be recognized.  Any others will be ignored.
15795121Sfrankho 	 */
15805121Sfrankho 	for (i = 0; i < FD_NUMPART; i++) {
15815121Sfrankho 		DTRACE_PROBE4(primarypart, struct pcfs *, fsp,
15825121Sfrankho 		    uint_t, (uint_t)dosp[i].systid,
15835121Sfrankho 		    uint_t, LE_32(dosp[i].relsect),
15845121Sfrankho 		    uint_t, LE_32(dosp[i].numsect));
15855121Sfrankho 
15865121Sfrankho 		if (isDosDrive(dosp[i].systid)) {
15875121Sfrankho 			if (primaryPart < 0) {
15885121Sfrankho 				logicalDriveCount++;
15895121Sfrankho 				primaryPart = i;
15905121Sfrankho 			} else {
15915121Sfrankho 				extraDrives[numExtraDrives++] = i;
15925121Sfrankho 			}
15935121Sfrankho 			continue;
15945121Sfrankho 		}
15955121Sfrankho 		if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) {
15965121Sfrankho 			extendedPart = i;
15975121Sfrankho 			continue;
15985121Sfrankho 		}
15995121Sfrankho 		if ((bootPart < 0) && isBootPart(dosp[i].systid)) {
16005121Sfrankho 			bootPart = i;
16015121Sfrankho 			continue;
16025121Sfrankho 		}
16035121Sfrankho 	}
16045121Sfrankho 
16055121Sfrankho 	if (ldrive == BOOT_PARTITION_DRIVE) {
16065121Sfrankho 		if (bootPart < 0) {
16075121Sfrankho 			noLogicalDrive(ldrive);
16085121Sfrankho 			return (EINVAL);
16095121Sfrankho 		}
16105121Sfrankho 		startsec = LE_32(dosp[bootPart].relsect);
16115121Sfrankho 		mediasize = LE_32(dosp[bootPart].numsect);
16125121Sfrankho 		goto found;
16135121Sfrankho 	}
16145121Sfrankho 
16155121Sfrankho 	if (ldrive == PRIMARY_DOS_DRIVE && primaryPart >= 0) {
16165121Sfrankho 		startsec = LE_32(dosp[primaryPart].relsect);
16175121Sfrankho 		mediasize = LE_32(dosp[primaryPart].numsect);
16185121Sfrankho 		goto found;
16195121Sfrankho 	}
16205121Sfrankho 
16215121Sfrankho 	/*
16225121Sfrankho 	 * We are not looking for the C: drive (or the primary drive
16235121Sfrankho 	 * was not found), so we had better have an extended partition
16245121Sfrankho 	 * or extra drives in the Master FDISK table.
16255121Sfrankho 	 */
16265121Sfrankho 	if ((extendedPart < 0) && (numExtraDrives == 0)) {
16275121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: no extended dos partition");
16285121Sfrankho 		noLogicalDrive(ldrive);
16295121Sfrankho 		return (EINVAL);
16305121Sfrankho 	}
16315121Sfrankho 
16325121Sfrankho 	if (extendedPart >= 0) {
16335121Sfrankho 		diskblk = xstartsect = LE_32(dosp[extendedPart].relsect);
16345121Sfrankho 		xnumsect = LE_32(dosp[extendedPart].numsect);
16355121Sfrankho 		do {
16365121Sfrankho 			/*
16375121Sfrankho 			 *  If the seek would not cause us to change
16385121Sfrankho 			 *  position on the drive, then we're out of
16395121Sfrankho 			 *  extended partitions to examine.
16405121Sfrankho 			 */
16415121Sfrankho 			if (diskblk == lastseek)
16425121Sfrankho 				break;
16435121Sfrankho 			logicalDriveCount += numDrives;
16445121Sfrankho 			/*
16455121Sfrankho 			 *  Seek the next extended partition, and find
16465121Sfrankho 			 *  logical drives within it.
16475121Sfrankho 			 */
16485121Sfrankho 			brelse(*bp);
16495121Sfrankho 			/*
16505121Sfrankho 			 * bread() block numbers are multiples of DEV_BSIZE
16515121Sfrankho 			 * but the device sector size (the unit of partitioning)
16525121Sfrankho 			 * might be larger than that; pcfs_get_device_info()
16535121Sfrankho 			 * has calculated the multiplicator for us.
16545121Sfrankho 			 */
16555121Sfrankho 			*bp = bread(dev,
16565121Sfrankho 			    pc_dbdaddr(fsp, diskblk), fsp->pcfs_secsize);
16575121Sfrankho 			if ((*bp)->b_flags & B_ERROR) {
16585121Sfrankho 				return (EIO);
16595121Sfrankho 			}
16605121Sfrankho 
16615121Sfrankho 			lastseek = diskblk;
16625121Sfrankho 			COPY_PTBL((*bp)->b_un.b_addr, dosp);
16635121Sfrankho 			if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
16645121Sfrankho 				cmn_err(CE_NOTE, "!pcfs: "
16655121Sfrankho 				    "extended partition table signature err, "
16665121Sfrankho 				    "device (%x.%x):%d, LBA %u",
16675121Sfrankho 				    getmajor(dev), getminor(dev), ldrive,
16685121Sfrankho 				    (uint_t)pc_dbdaddr(fsp, diskblk));
16695121Sfrankho 				return (EINVAL);
16705121Sfrankho 			}
16715121Sfrankho 			/*
16725121Sfrankho 			 *  Count up drives, and track where the next
16735121Sfrankho 			 *  extended partition is in case we need it.  We
16745121Sfrankho 			 *  are expecting only one extended partition.  If
16755121Sfrankho 			 *  there is more than one we'll only go to the
16765121Sfrankho 			 *  first one we see, but warn about ignoring.
16775121Sfrankho 			 */
16785121Sfrankho 			numDrives = 0;
16795121Sfrankho 			for (i = 0; i < FD_NUMPART; i++) {
16805121Sfrankho 				DTRACE_PROBE4(extendedpart,
16815121Sfrankho 				    struct pcfs *, fsp,
16825121Sfrankho 				    uint_t, (uint_t)dosp[i].systid,
16835121Sfrankho 				    uint_t, LE_32(dosp[i].relsect),
16845121Sfrankho 				    uint_t, LE_32(dosp[i].numsect));
16855121Sfrankho 				if (isDosDrive(dosp[i].systid)) {
16865121Sfrankho 					extndDrives[numDrives++] = i;
16875121Sfrankho 				} else if (isDosExtended(dosp[i].systid)) {
16885121Sfrankho 					if (diskblk != lastseek) {
16895121Sfrankho 						/*
16905121Sfrankho 						 * Already found an extended
16915121Sfrankho 						 * partition in this table.
16925121Sfrankho 						 */
16935121Sfrankho 						cmn_err(CE_NOTE,
16945121Sfrankho 						    "!pcfs: ignoring unexpected"
16955121Sfrankho 						    " additional extended"
16965121Sfrankho 						    " partition");
16975121Sfrankho 					} else {
16985121Sfrankho 						diskblk = xstartsect +
16995121Sfrankho 						    LE_32(dosp[i].relsect);
17005121Sfrankho 					}
17015121Sfrankho 				}
17025121Sfrankho 			}
17035121Sfrankho 		} while (ldrive > logicalDriveCount + numDrives);
17045121Sfrankho 
17055121Sfrankho 		ASSERT(numDrives <= FD_NUMPART);
17065121Sfrankho 
17075121Sfrankho 		if (ldrive <= logicalDriveCount + numDrives) {
17085121Sfrankho 			/*
17095121Sfrankho 			 * The number of logical drives we've found thus
17105121Sfrankho 			 * far is enough to get us to the one we were
17115121Sfrankho 			 * searching for.
17125121Sfrankho 			 */
17135121Sfrankho 			driveIndex = logicalDriveCount + numDrives - ldrive;
17145121Sfrankho 			mediasize =
17155121Sfrankho 			    LE_32(dosp[extndDrives[driveIndex]].numsect);
17165121Sfrankho 			startsec =
17175121Sfrankho 			    LE_32(dosp[extndDrives[driveIndex]].relsect) +
17185121Sfrankho 			    lastseek;
17195121Sfrankho 			if (startsec > (xstartsect + xnumsect)) {
17205121Sfrankho 				cmn_err(CE_NOTE, "!pcfs: extended partition "
17215121Sfrankho 				    "values bad");
17225121Sfrankho 				return (EINVAL);
17235121Sfrankho 			}
17245121Sfrankho 			goto found;
17255121Sfrankho 		} else {
17265121Sfrankho 			/*
17275121Sfrankho 			 * We ran out of extended dos partition
17285121Sfrankho 			 * drives.  The only hope now is to go
17295121Sfrankho 			 * back to extra drives defined in the master
17305121Sfrankho 			 * fdisk table.  But we overwrote that table
17315121Sfrankho 			 * already, so we must load it in again.
17325121Sfrankho 			 */
17335121Sfrankho 			logicalDriveCount += numDrives;
17345121Sfrankho 			brelse(*bp);
17355121Sfrankho 			ASSERT(fsp->pcfs_dosstart == 0);
17365121Sfrankho 			*bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
17375121Sfrankho 			    fsp->pcfs_secsize);
17385121Sfrankho 			if ((*bp)->b_flags & B_ERROR) {
17395121Sfrankho 				return (EIO);
17405121Sfrankho 			}
17415121Sfrankho 			COPY_PTBL((*bp)->b_un.b_addr, dosp);
17425121Sfrankho 		}
17435121Sfrankho 	}
17445121Sfrankho 	/*
17455121Sfrankho 	 *  Still haven't found the drive, is it an extra
17465121Sfrankho 	 *  drive defined in the main FDISK table?
17475121Sfrankho 	 */
17485121Sfrankho 	if (ldrive <= logicalDriveCount + numExtraDrives) {
17495121Sfrankho 		driveIndex = logicalDriveCount + numExtraDrives - ldrive;
17505121Sfrankho 		ASSERT(driveIndex < MIN(numExtraDrives, FD_NUMPART));
17515121Sfrankho 		mediasize = LE_32(dosp[extraDrives[driveIndex]].numsect);
17525121Sfrankho 		startsec = LE_32(dosp[extraDrives[driveIndex]].relsect);
17535121Sfrankho 		goto found;
17545121Sfrankho 	}
17555121Sfrankho 	/*
17565121Sfrankho 	 *  Still haven't found the drive, and there is
17575121Sfrankho 	 *  nowhere else to look.
17585121Sfrankho 	 */
17595121Sfrankho 	noLogicalDrive(ldrive);
17605121Sfrankho 	return (EINVAL);
17615121Sfrankho 
17625121Sfrankho found:
17635121Sfrankho 	/*
17645121Sfrankho 	 * We need this value in units of sectorsize, because PCFS' internal
17655121Sfrankho 	 * offset calculations go haywire for > 512Byte sectors unless all
17665121Sfrankho 	 * pcfs_.*start values are in units of sectors.
17675121Sfrankho 	 * So, assign before the capacity check (that's done in DEV_BSIZE)
17685121Sfrankho 	 */
17695121Sfrankho 	fsp->pcfs_dosstart = startsec;
17705121Sfrankho 
17715121Sfrankho 	/*
17725121Sfrankho 	 * convert from device sectors to proper units:
17735121Sfrankho 	 *	- starting sector: DEV_BSIZE (as argument to bread())
17745121Sfrankho 	 *	- media size: Bytes
17755121Sfrankho 	 */
17765121Sfrankho 	startsec = pc_dbdaddr(fsp, startsec);
17775121Sfrankho 	mediasize *= fsp->pcfs_secsize;
17785121Sfrankho 
17795121Sfrankho 	/*
17805121Sfrankho 	 * some additional validation / warnings in case the partition table
17815121Sfrankho 	 * and the actual media capacity are not in accordance ...
17825121Sfrankho 	 */
17835121Sfrankho 	if (fsp->pcfs_mediasize != 0) {
17845121Sfrankho 		diskaddr_t startoff =
17855121Sfrankho 		    (diskaddr_t)startsec * (diskaddr_t)DEV_BSIZE;
17865121Sfrankho 
17875121Sfrankho 		if (startoff >= fsp->pcfs_mediasize ||
17885121Sfrankho 		    startoff + mediasize > fsp->pcfs_mediasize) {
17895121Sfrankho 			cmn_err(CE_WARN,
17905121Sfrankho 			    "!pcfs: partition size (LBA start %u, %lld bytes, "
17915121Sfrankho 			    "device (%x.%x):%d) smaller than "
17925121Sfrankho 			    "mediasize (%lld bytes).\n"
17935121Sfrankho 			    "filesystem may be truncated, access errors "
17945121Sfrankho 			    "may result.\n",
17955121Sfrankho 			    (uint_t)startsec, (long long)mediasize,
17965121Sfrankho 			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
17975121Sfrankho 			    fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
17985121Sfrankho 		}
17995121Sfrankho 	} else {
18005121Sfrankho 		fsp->pcfs_mediasize = mediasize;
18015121Sfrankho 	}
18025121Sfrankho 
18035121Sfrankho 	return (0);
18045121Sfrankho }
18055121Sfrankho 
18065121Sfrankho 
18075121Sfrankho static fattype_t
18085121Sfrankho secondaryBPBChecks(struct pcfs *fsp, uchar_t *bpb, size_t secsize)
18095121Sfrankho {
18105121Sfrankho 	uint32_t ncl = fsp->pcfs_ncluster;
18115121Sfrankho 
18125121Sfrankho 	if (ncl <= 4096) {
18135121Sfrankho 		if (bpb_get_FatSz16(bpb) == 0)
18145121Sfrankho 			return (FAT_UNKNOWN);
18155121Sfrankho 
18165121Sfrankho 		if (bpb_get_FatSz16(bpb) * secsize < ncl * 2 &&
18175121Sfrankho 		    bpb_get_FatSz16(bpb) * secsize >= (3 * ncl / 2))
18185121Sfrankho 			return (FAT12);
18195121Sfrankho 		if (bcmp(bpb_FilSysType16(bpb), "FAT12", 5) == 0)
18205121Sfrankho 			return (FAT12);
18215121Sfrankho 		if (bcmp(bpb_FilSysType16(bpb), "FAT16", 5) == 0)
18225121Sfrankho 			return (FAT16);
18235121Sfrankho 
18245121Sfrankho 		switch (bpb_get_Media(bpb)) {
18255121Sfrankho 			case SS8SPT:
18265121Sfrankho 			case DS8SPT:
18275121Sfrankho 			case SS9SPT:
18285121Sfrankho 			case DS9SPT:
18295121Sfrankho 			case DS18SPT:
18305121Sfrankho 			case DS9_15SPT:
18315121Sfrankho 				/*
18325121Sfrankho 				 * Is this reliable - all floppies are FAT12 ?
18335121Sfrankho 				 */
18345121Sfrankho 				return (FAT12);
18355121Sfrankho 			case MD_FIXED:
18365121Sfrankho 				/*
18375121Sfrankho 				 * Is this reliable - disks are always FAT16 ?
18385121Sfrankho 				 */
18395121Sfrankho 				return (FAT16);
18405121Sfrankho 			default:
18415121Sfrankho 				break;
18425121Sfrankho 		}
18435121Sfrankho 	} else if (ncl <= 65536) {
18445121Sfrankho 		if (bpb_get_FatSz16(bpb) == 0 && bpb_get_FatSz32(bpb) > 0)
18455121Sfrankho 			return (FAT32);
18465121Sfrankho 		if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
18475121Sfrankho 			return (FAT32);
18485121Sfrankho 		if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
18495121Sfrankho 			return (FAT32);
18505121Sfrankho 
18515121Sfrankho 		if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
18525121Sfrankho 			return (FAT16);
18535121Sfrankho 		if (bpb_get_FatSz16(bpb) * secsize < ncl * 4)
18545121Sfrankho 			return (FAT16);
18555121Sfrankho 	}
18565121Sfrankho 
18575121Sfrankho 	/*
18585121Sfrankho 	 * We don't know
18595121Sfrankho 	 */
18605121Sfrankho 	return (FAT_UNKNOWN);
18615121Sfrankho }
18625121Sfrankho 
18635121Sfrankho /*
18645121Sfrankho  * Check to see if the BPB we found is correct.
18655121Sfrankho  *
18665121Sfrankho  * This looks far more complicated that it needs to be for pure structural
18675121Sfrankho  * validation. The reason for this is that parseBPB() is also used for
18685121Sfrankho  * debugging purposes (mdb dcmd) and we therefore want a bitmap of which
18696644Sgd78059  * BPB fields (do not) have 'known good' values, even if we (do not) reject
18706644Sgd78059  * the BPB when attempting to mount the filesystem.
18716644Sgd78059  *
18726644Sgd78059  * Real-world usage of FAT shows there are a lot of corner-case situations
18736644Sgd78059  * and, following the specification strictly, invalid filesystems out there.
18746644Sgd78059  * Known are situations such as:
18756644Sgd78059  *	- FAT12/FAT16 filesystems with garbage in either totsec16/32
18766644Sgd78059  *	  instead of the zero in one of the fields mandated by the spec
18776644Sgd78059  *	- filesystems that claim to be larger than the partition they're in
18786644Sgd78059  *	- filesystems without valid media descriptor
18796644Sgd78059  *	- FAT32 filesystems with RootEntCnt != 0
18806644Sgd78059  *	- FAT32 filesystems with less than 65526 clusters
18816644Sgd78059  *	- FAT32 filesystems without valid FSI sector
18826644Sgd78059  *	- FAT32 filesystems with FAT size in fatsec16 instead of fatsec32
18836644Sgd78059  *
18846644Sgd78059  * Such filesystems are accessible by PCFS - if it'd know to start with that
18856644Sgd78059  * the filesystem should be treated as a specific FAT type. Before S10, it
18866644Sgd78059  * relied on the PC/fdisk partition type for the purpose and almost completely
18876644Sgd78059  * ignored the BPB; now it ignores the partition type for anything else but
18886644Sgd78059  * logical drive enumeration, which can result in rejection of (invalid)
18896644Sgd78059  * FAT32 - if the partition ID says FAT32, but the filesystem, for example
18906644Sgd78059  * has less than 65526 clusters.
18916644Sgd78059  *
18926644Sgd78059  * Without a "force this fs as FAT{12,16,32}" tunable or mount option, it's
18936644Sgd78059  * not possible to allow all such mostly-compliant filesystems in unless one
18946644Sgd78059  * accepts false positives (definitely invalid filesystems that cause problems
18956644Sgd78059  * later). This at least allows to pinpoint why the mount failed.
18966644Sgd78059  *
18976644Sgd78059  * Due to the use of FAT on removeable media, all relaxations of the rules
18986644Sgd78059  * here need to be carefully evaluated wrt. to potential effects on PCFS
18996644Sgd78059  * resilience. A faulty/"mis-crafted" filesystem must not cause a panic, so
19006644Sgd78059  * beware.
19015121Sfrankho  */
19025121Sfrankho static int
19035121Sfrankho parseBPB(struct pcfs *fsp, uchar_t *bpb, int *valid)
19045121Sfrankho {
19055121Sfrankho 	fattype_t type;
19065121Sfrankho 
19075121Sfrankho 	uint32_t	ncl;	/* number of clusters in file area */
19085121Sfrankho 	uint32_t	rec;
19095121Sfrankho 	uint32_t	reserved;
19105121Sfrankho 	uint32_t	fsisec, bkbootsec;
19115121Sfrankho 	blkcnt_t	totsec, totsec16, totsec32, datasec;
19125121Sfrankho 	size_t		fatsec, fatsec16, fatsec32, rdirsec;
19135121Sfrankho 	size_t		secsize;
19145121Sfrankho 	len_t		mediasize;
19155121Sfrankho 	uint64_t	validflags = 0;
19165121Sfrankho 
19175121Sfrankho 	if (VALID_BPBSIG(bpb_get_BPBSig(bpb)))
19185121Sfrankho 		validflags |= BPB_BPBSIG_OK;
19195121Sfrankho 
19205121Sfrankho 	rec = bpb_get_RootEntCnt(bpb);
19215121Sfrankho 	reserved = bpb_get_RsvdSecCnt(bpb);
19225121Sfrankho 	fsisec = bpb_get_FSInfo32(bpb);
19235121Sfrankho 	bkbootsec = bpb_get_BkBootSec32(bpb);
19245121Sfrankho 	totsec16 = (blkcnt_t)bpb_get_TotSec16(bpb);
19255121Sfrankho 	totsec32 = (blkcnt_t)bpb_get_TotSec32(bpb);
19265121Sfrankho 	fatsec16 = bpb_get_FatSz16(bpb);
19275121Sfrankho 	fatsec32 = bpb_get_FatSz32(bpb);
19285121Sfrankho 
19295121Sfrankho 	totsec = totsec16 ? totsec16 : totsec32;
19305121Sfrankho 	fatsec = fatsec16 ? fatsec16 : fatsec32;
19315121Sfrankho 
19325121Sfrankho 	secsize = bpb_get_BytesPerSec(bpb);
19335121Sfrankho 	if (!VALID_SECSIZE(secsize))
19345121Sfrankho 		secsize = fsp->pcfs_secsize;
19355121Sfrankho 	if (secsize != fsp->pcfs_secsize) {
19365121Sfrankho 		PC_DPRINTF3(3, "!pcfs: parseBPB, device (%x.%x):%d:\n",
19375121Sfrankho 		    getmajor(fsp->pcfs_xdev),
19385121Sfrankho 		    getminor(fsp->pcfs_xdev), fsp->pcfs_ldrive);
19395121Sfrankho 		PC_DPRINTF2(3, "!BPB secsize %d != "
19405121Sfrankho 		    "autodetected media block size %d\n",
19415121Sfrankho 		    (int)secsize, (int)fsp->pcfs_secsize);
19425121Sfrankho 		if (fsp->pcfs_ldrive) {
19435121Sfrankho 			/*
19445121Sfrankho 			 * We've already attempted to parse the partition
19455121Sfrankho 			 * table. If the block size used for that don't match
19465121Sfrankho 			 * the PCFS sector size, we're hosed one way or the
19475121Sfrankho 			 * other. Just try what happens.
19485121Sfrankho 			 */
19495121Sfrankho 			secsize = fsp->pcfs_secsize;
19505121Sfrankho 			PC_DPRINTF1(3,
19515121Sfrankho 			    "!pcfs: Using autodetected secsize %d\n",
19525121Sfrankho 			    (int)secsize);
19535121Sfrankho 		} else {
19545121Sfrankho 			/*
19555121Sfrankho 			 * This allows mounting lofi images of PCFS partitions
19565121Sfrankho 			 * with sectorsize != DEV_BSIZE. We can't parse the
19575121Sfrankho 			 * partition table on whole-disk images unless the
19585121Sfrankho 			 * (undocumented) "secsize=..." mount option is used,
19595121Sfrankho 			 * but at least this allows us to mount if we have
19605121Sfrankho 			 * an image of a partition.
19615121Sfrankho 			 */
19625121Sfrankho 			PC_DPRINTF1(3,
19635121Sfrankho 			    "!pcfs: Using BPB secsize %d\n", (int)secsize);
19645121Sfrankho 		}
19655121Sfrankho 	}
19665121Sfrankho 
19675121Sfrankho 	if (fsp->pcfs_mediasize == 0) {
19685121Sfrankho 		mediasize = (len_t)totsec * (len_t)secsize;
19696644Sgd78059 		/*
19706644Sgd78059 		 * This is not an error because not all devices support the
19716644Sgd78059 		 * dkio(7i) mediasize queries, and/or not all devices are
19726644Sgd78059 		 * partitioned. If we have not been able to figure out the
19736644Sgd78059 		 * size of the underlaying medium, we have to trust the BPB.
19746644Sgd78059 		 */
19755121Sfrankho 		PC_DPRINTF4(3, "!pcfs: parseBPB: mediasize autodetect failed "
19765121Sfrankho 		    "on device (%x.%x):%d, trusting BPB totsec (%lld Bytes)\n",
19775121Sfrankho 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
19785121Sfrankho 		    fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
19795121Sfrankho 	} else if ((len_t)totsec * (len_t)secsize > fsp->pcfs_mediasize) {
19805121Sfrankho 		cmn_err(CE_WARN,
19815121Sfrankho 		    "!pcfs: autodetected mediasize (%lld Bytes) smaller than "
19825121Sfrankho 		    "FAT BPB mediasize (%lld Bytes).\n"
19835121Sfrankho 		    "truncated filesystem on device (%x.%x):%d, access errors "
19845121Sfrankho 		    "possible.\n",
19855121Sfrankho 		    (long long)fsp->pcfs_mediasize,
19865121Sfrankho 		    (long long)(totsec * (blkcnt_t)secsize),
19875121Sfrankho 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
19885121Sfrankho 		    fsp->pcfs_ldrive);
19895121Sfrankho 		mediasize = fsp->pcfs_mediasize;
19905121Sfrankho 	} else {
19915121Sfrankho 		/*
19925121Sfrankho 		 * This is actually ok. A FAT needs not occupy the maximum
19935121Sfrankho 		 * space available in its partition, it can be shorter.
19945121Sfrankho 		 */
19955121Sfrankho 		mediasize = (len_t)totsec * (len_t)secsize;
19965121Sfrankho 	}
19975121Sfrankho 
19985121Sfrankho 	/*
19995121Sfrankho 	 * Since we let just about anything pass through this function,
20005121Sfrankho 	 * fence against divide-by-zero here.
20015121Sfrankho 	 */
20025121Sfrankho 	if (secsize)
20035121Sfrankho 		rdirsec = roundup(rec * 32, secsize) / secsize;
20045121Sfrankho 	else
20055121Sfrankho 		rdirsec = 0;
20065121Sfrankho 
20075121Sfrankho 	/*
20085121Sfrankho 	 * This assignment is necessary before pc_dbdaddr() can first be
20095121Sfrankho 	 * used. Must initialize the value here.
20105121Sfrankho 	 */
20115121Sfrankho 	fsp->pcfs_secsize = secsize;
20125121Sfrankho 	fsp->pcfs_sdshift = ddi_ffs(secsize / DEV_BSIZE) - 1;
20135121Sfrankho 
20145121Sfrankho 	fsp->pcfs_mediasize = mediasize;
20155121Sfrankho 
20165121Sfrankho 	fsp->pcfs_spcl = bpb_get_SecPerClus(bpb);
20175121Sfrankho 	fsp->pcfs_numfat = bpb_get_NumFATs(bpb);
20185121Sfrankho 	fsp->pcfs_mediadesc = bpb_get_Media(bpb);
20195121Sfrankho 	fsp->pcfs_clsize = secsize * fsp->pcfs_spcl;
20205121Sfrankho 	fsp->pcfs_rdirsec = rdirsec;
20215121Sfrankho 
20225121Sfrankho 	/*
20235121Sfrankho 	 * Remember: All PCFS offset calculations in sectors. Before I/O
20245121Sfrankho 	 * is done, convert to DEV_BSIZE units via pc_dbdaddr(). This is
20255121Sfrankho 	 * necessary so that media with > 512Byte sector sizes work correctly.
20265121Sfrankho 	 */
20275121Sfrankho 	fsp->pcfs_fatstart = fsp->pcfs_dosstart + reserved;
20285121Sfrankho 	fsp->pcfs_rdirstart = fsp->pcfs_fatstart + fsp->pcfs_numfat * fatsec;
20295121Sfrankho 	fsp->pcfs_datastart = fsp->pcfs_rdirstart + rdirsec;
20305121Sfrankho 	datasec = totsec -
20315121Sfrankho 	    (blkcnt_t)fatsec * fsp->pcfs_numfat -
20325121Sfrankho 	    (blkcnt_t)rdirsec -
20335121Sfrankho 	    (blkcnt_t)reserved;
20345121Sfrankho 
20355121Sfrankho 	DTRACE_PROBE4(fatgeometry,
20365121Sfrankho 	    blkcnt_t, totsec, size_t, fatsec,
20375121Sfrankho 	    size_t, rdirsec, blkcnt_t, datasec);
20385121Sfrankho 
20395121Sfrankho 	/*
20406644Sgd78059 	 * 'totsec' is taken directly from the BPB and guaranteed to fit
20416644Sgd78059 	 * into a 32bit unsigned integer. The calculation of 'datasec',
20426644Sgd78059 	 * on the other hand, could underflow for incorrect values in
20436644Sgd78059 	 * rdirsec/reserved/fatsec. Check for that.
20446644Sgd78059 	 * We also check that the BPB conforms to the FAT specification's
20456644Sgd78059 	 * requirement that either of the 16/32bit total sector counts
20466644Sgd78059 	 * must be zero.
20475121Sfrankho 	 */
20485121Sfrankho 	if (totsec != 0 &&
20495121Sfrankho 	    (totsec16 == totsec32 || totsec16 == 0 || totsec32 == 0) &&
20505121Sfrankho 	    datasec < totsec && datasec <= UINT32_MAX)
20515121Sfrankho 		validflags |= BPB_TOTSEC_OK;
20525121Sfrankho 
20536644Sgd78059 	if ((len_t)totsec * (len_t)secsize <= mediasize)
20545121Sfrankho 		validflags |= BPB_MEDIASZ_OK;
20555121Sfrankho 
20565121Sfrankho 	if (VALID_SECSIZE(secsize))
20575121Sfrankho 		validflags |= BPB_SECSIZE_OK;
20585121Sfrankho 	if (VALID_SPCL(fsp->pcfs_spcl))
20595121Sfrankho 		validflags |= BPB_SECPERCLUS_OK;
20605121Sfrankho 	if (VALID_CLSIZE(fsp->pcfs_clsize))
20615121Sfrankho 		validflags |= BPB_CLSIZE_OK;
20625121Sfrankho 	if (VALID_NUMFATS(fsp->pcfs_numfat))
20635121Sfrankho 		validflags |= BPB_NUMFAT_OK;
20645121Sfrankho 	if (VALID_RSVDSEC(reserved) && reserved < totsec)
20655121Sfrankho 		validflags |= BPB_RSVDSECCNT_OK;
20665121Sfrankho 	if (VALID_MEDIA(fsp->pcfs_mediadesc))
20675121Sfrankho 		validflags |= BPB_MEDIADESC_OK;
20685121Sfrankho 	if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
20695121Sfrankho 		validflags |= BPB_BOOTSIG16_OK;
20705121Sfrankho 	if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
20715121Sfrankho 		validflags |= BPB_BOOTSIG32_OK;
20725121Sfrankho 	if (VALID_FSTYPSTR16(bpb_FilSysType16(bpb)))
20735121Sfrankho 		validflags |= BPB_FSTYPSTR16_OK;
20745121Sfrankho 	if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
20755121Sfrankho 		validflags |= BPB_FSTYPSTR32_OK;
20765121Sfrankho 	if (VALID_OEMNAME(bpb_OEMName(bpb)))
20775121Sfrankho 		validflags |= BPB_OEMNAME_OK;
20785121Sfrankho 	if (bkbootsec > 0 && bkbootsec <= reserved && fsisec != bkbootsec)
20795121Sfrankho 		validflags |= BPB_BKBOOTSEC_OK;
20805121Sfrankho 	if (fsisec > 0 && fsisec <= reserved)
20815121Sfrankho 		validflags |= BPB_FSISEC_OK;
20825121Sfrankho 	if (VALID_JMPBOOT(bpb_jmpBoot(bpb)))
20835121Sfrankho 		validflags |= BPB_JMPBOOT_OK;
20845121Sfrankho 	if (VALID_FSVER32(bpb_get_FSVer32(bpb)))
20855121Sfrankho 		validflags |= BPB_FSVER_OK;
20865121Sfrankho 	if (VALID_VOLLAB(bpb_VolLab16(bpb)))
20875121Sfrankho 		validflags |= BPB_VOLLAB16_OK;
20885121Sfrankho 	if (VALID_VOLLAB(bpb_VolLab32(bpb)))
20895121Sfrankho 		validflags |= BPB_VOLLAB32_OK;
20905121Sfrankho 	if (VALID_EXTFLAGS(bpb_get_ExtFlags32(bpb)))
20915121Sfrankho 		validflags |= BPB_EXTFLAGS_OK;
20925121Sfrankho 
20935121Sfrankho 	/*
20945121Sfrankho 	 * Try to determine which FAT format to use.
20955121Sfrankho 	 *
20965121Sfrankho 	 * Calculate the number of clusters in order to determine
20975121Sfrankho 	 * the type of FAT we are looking at.  This is the only
20985121Sfrankho 	 * recommended way of determining FAT type, though there
20995121Sfrankho 	 * are other hints in the data, this is the best way.
21005121Sfrankho 	 *
21015121Sfrankho 	 * Since we let just about "anything" pass through this function
21025121Sfrankho 	 * without early exits, fence against divide-by-zero here.
21035121Sfrankho 	 *
21045121Sfrankho 	 * datasec was already validated against UINT32_MAX so we know
21055121Sfrankho 	 * the result will not overflow the 32bit calculation.
21065121Sfrankho 	 */
21075121Sfrankho 	if (fsp->pcfs_spcl)
21085121Sfrankho 		ncl = (uint32_t)datasec / fsp->pcfs_spcl;
21095121Sfrankho 	else
21105121Sfrankho 		ncl = 0;
21115121Sfrankho 
21125121Sfrankho 	fsp->pcfs_ncluster = ncl;
21135121Sfrankho 
21145121Sfrankho 	/*
21155121Sfrankho 	 * From the Microsoft FAT specification:
21165121Sfrankho 	 * In the following example, when it says <, it does not mean <=.
21175121Sfrankho 	 * Note also that the numbers are correct.  The first number for
21185121Sfrankho 	 * FAT12 is 4085; the second number for FAT16 is 65525. These numbers
21195121Sfrankho 	 * and the '<' signs are not wrong.
21205121Sfrankho 	 *
21215121Sfrankho 	 * We "specialdetect" the corner cases, and use at least one "extra"
21225121Sfrankho 	 * criterion to decide whether it's FAT16 or FAT32 if the cluster
21235121Sfrankho 	 * count is dangerously close to the boundaries.
21245121Sfrankho 	 */
21255121Sfrankho 
21265121Sfrankho 	if (ncl <= PCF_FIRSTCLUSTER) {
21275121Sfrankho 		type = FAT_UNKNOWN;
21285121Sfrankho 	} else if (ncl < 4085) {
21295121Sfrankho 		type = FAT12;
21305121Sfrankho 	} else if (ncl <= 4096) {
21315121Sfrankho 		type = FAT_QUESTIONABLE;
21325121Sfrankho 	} else if (ncl < 65525) {
21335121Sfrankho 		type = FAT16;
21345121Sfrankho 	} else if (ncl <= 65536) {
21355121Sfrankho 		type = FAT_QUESTIONABLE;
21365121Sfrankho 	} else if (ncl < PCF_LASTCLUSTER32) {
21375121Sfrankho 		type = FAT32;
21385121Sfrankho 	} else {
21395121Sfrankho 		type = FAT_UNKNOWN;
21405121Sfrankho 	}
21415121Sfrankho 
21425121Sfrankho 	DTRACE_PROBE4(parseBPB__initial,
21435121Sfrankho 	    struct pcfs *, fsp, unsigned char *, bpb,
21445121Sfrankho 	    int, validflags, fattype_t, type);
21455121Sfrankho 
21465121Sfrankho recheck:
21475121Sfrankho 	fsp->pcfs_fatsec = fatsec;
21485121Sfrankho 
21495121Sfrankho 	/* Do some final sanity checks for each specific type of FAT */
21505121Sfrankho 	switch (type) {
21515121Sfrankho 		case FAT12:
21525121Sfrankho 			if (rec != 0)
21535121Sfrankho 				validflags |= BPB_ROOTENTCNT_OK;
21545121Sfrankho 			if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
21555121Sfrankho 			    bpb_get_TotSec16(bpb) == 0)
21565121Sfrankho 				validflags |= BPB_TOTSEC16_OK;
21575121Sfrankho 			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
21585121Sfrankho 			    bpb_get_TotSec32(bpb) == 0)
21595121Sfrankho 				validflags |= BPB_TOTSEC32_OK;
21605121Sfrankho 			if (bpb_get_FatSz16(bpb) == fatsec)
21615121Sfrankho 				validflags |= BPB_FATSZ16_OK;
21625121Sfrankho 			if (fatsec * secsize >= ncl * 3 / 2)
21635121Sfrankho 				validflags |= BPB_FATSZ_OK;
21645121Sfrankho 			if (ncl < 4085)
21655121Sfrankho 				validflags |= BPB_NCLUSTERS_OK;
21665121Sfrankho 
21675121Sfrankho 			fsp->pcfs_lastclmark = (PCF_LASTCLUSTER & 0xfff);
21685121Sfrankho 			fsp->pcfs_rootblksize =
21695121Sfrankho 			    fsp->pcfs_rdirsec * secsize;
21705121Sfrankho 			fsp->pcfs_fsistart = 0;
21715121Sfrankho 
21725121Sfrankho 			if ((validflags & FAT12_VALIDMSK) != FAT12_VALIDMSK)
21735121Sfrankho 				type = FAT_UNKNOWN;
21745121Sfrankho 			break;
21755121Sfrankho 		case FAT16:
21765121Sfrankho 			if (rec != 0)
21775121Sfrankho 				validflags |= BPB_ROOTENTCNT_OK;
21785121Sfrankho 			if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
21795121Sfrankho 			    bpb_get_TotSec16(bpb) == 0)
21805121Sfrankho 				validflags |= BPB_TOTSEC16_OK;
21815121Sfrankho 			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
21825121Sfrankho 			    bpb_get_TotSec32(bpb) == 0)
21835121Sfrankho 				validflags |= BPB_TOTSEC32_OK;
21845121Sfrankho 			if (bpb_get_FatSz16(bpb) == fatsec)
21855121Sfrankho 				validflags |= BPB_FATSZ16_OK;
21865121Sfrankho 			if (fatsec * secsize >= ncl * 2)
21875121Sfrankho 				validflags |= BPB_FATSZ_OK;
21885121Sfrankho 			if (ncl >= 4085 && ncl < 65525)
21895121Sfrankho 				validflags |= BPB_NCLUSTERS_OK;
21905121Sfrankho 
21915121Sfrankho 			fsp->pcfs_lastclmark = PCF_LASTCLUSTER;
21925121Sfrankho 			fsp->pcfs_rootblksize =
21935121Sfrankho 			    fsp->pcfs_rdirsec * secsize;
21945121Sfrankho 			fsp->pcfs_fsistart = 0;
21955121Sfrankho 
21965121Sfrankho 			if ((validflags & FAT16_VALIDMSK) != FAT16_VALIDMSK)
21975121Sfrankho 				type = FAT_UNKNOWN;
21985121Sfrankho 			break;
21995121Sfrankho 		case FAT32:
22005121Sfrankho 			if (rec == 0)
22015121Sfrankho 				validflags |= BPB_ROOTENTCNT_OK;
22025121Sfrankho 			if (bpb_get_TotSec16(bpb) == 0)
22035121Sfrankho 				validflags |= BPB_TOTSEC16_OK;
22045121Sfrankho 			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec)
22055121Sfrankho 				validflags |= BPB_TOTSEC32_OK;
22065121Sfrankho 			if (bpb_get_FatSz16(bpb) == 0)
22075121Sfrankho 				validflags |= BPB_FATSZ16_OK;
22085121Sfrankho 			if (bpb_get_FatSz32(bpb) == fatsec)
22095121Sfrankho 				validflags |= BPB_FATSZ32_OK;
22105121Sfrankho 			if (fatsec * secsize >= ncl * 4)
22115121Sfrankho 				validflags |= BPB_FATSZ_OK;
22125121Sfrankho 			if (ncl >= 65525 && ncl < PCF_LASTCLUSTER32)
22135121Sfrankho 				validflags |= BPB_NCLUSTERS_OK;
22145121Sfrankho 
22155121Sfrankho 			fsp->pcfs_lastclmark = PCF_LASTCLUSTER32;
22165121Sfrankho 			fsp->pcfs_rootblksize = fsp->pcfs_clsize;
22175121Sfrankho 			fsp->pcfs_fsistart = fsp->pcfs_dosstart + fsisec;
22185121Sfrankho 			if (validflags & BPB_FSISEC_OK)
22195121Sfrankho 				fsp->pcfs_flags |= PCFS_FSINFO_OK;
22205121Sfrankho 			fsp->pcfs_rootclnum = bpb_get_RootClus32(bpb);
22215121Sfrankho 			if (pc_validcl(fsp, fsp->pcfs_rootclnum))
22225121Sfrankho 				validflags |= BPB_ROOTCLUSTER_OK;
22235121Sfrankho 
22245121Sfrankho 			/*
22255121Sfrankho 			 * Current PCFS code only works if 'pcfs_rdirstart'
22265121Sfrankho 			 * contains the root cluster number on FAT32.
22275121Sfrankho 			 * That's a mis-use and would better be changed.
22285121Sfrankho 			 */
22295121Sfrankho 			fsp->pcfs_rdirstart = (daddr_t)fsp->pcfs_rootclnum;
22305121Sfrankho 
22315121Sfrankho 			if ((validflags & FAT32_VALIDMSK) != FAT32_VALIDMSK)
22325121Sfrankho 				type = FAT_UNKNOWN;
22335121Sfrankho 			break;
22345121Sfrankho 		case FAT_QUESTIONABLE:
22355121Sfrankho 			type = secondaryBPBChecks(fsp, bpb, secsize);
22365121Sfrankho 			goto recheck;
22375121Sfrankho 		default:
22385121Sfrankho 			ASSERT(type == FAT_UNKNOWN);
22395121Sfrankho 			break;
22405121Sfrankho 	}
22415121Sfrankho 
22425121Sfrankho 	ASSERT(type != FAT_QUESTIONABLE);
22435121Sfrankho 
22445121Sfrankho 	fsp->pcfs_fattype = type;
22455121Sfrankho 
22465121Sfrankho 	if (valid)
22475121Sfrankho 		*valid = validflags;
22485121Sfrankho 
22495121Sfrankho 	DTRACE_PROBE4(parseBPB__final,
22505121Sfrankho 	    struct pcfs *, fsp, unsigned char *, bpb,
22515121Sfrankho 	    int, validflags, fattype_t, type);
22525121Sfrankho 
22535121Sfrankho 	if (type != FAT_UNKNOWN) {
22545121Sfrankho 		ASSERT((secsize & (DEV_BSIZE - 1)) == 0);
22555121Sfrankho 		ASSERT(ISP2(secsize / DEV_BSIZE));
22565121Sfrankho 		return (1);
22575121Sfrankho 	}
22585121Sfrankho 
22595121Sfrankho 	return (0);
22605121Sfrankho }
22615121Sfrankho 
22625121Sfrankho 
22635121Sfrankho /*
22645121Sfrankho  * Detect the device's native block size (sector size).
22655121Sfrankho  *
22665121Sfrankho  * Test whether the device is:
22675121Sfrankho  *	- a floppy device from a known controller type via DKIOCINFO
22685121Sfrankho  *	- a real floppy using the fd(7d) driver and capable of fdio(7I) ioctls
22695121Sfrankho  *	- a PCMCIA sram memory card (pseudofloppy) using pcram(7d)
22705121Sfrankho  *	- a USB floppy drive (identified by drive geometry)
22715121Sfrankho  *
22725121Sfrankho  * Detecting a floppy will make PCFS metadata updates on such media synchronous,
22735121Sfrankho  * to minimize risks due to slow I/O and user hotplugging / device ejection.
22745121Sfrankho  *
22755121Sfrankho  * This might be a bit wasteful on kernel stack space; if anyone's
22765121Sfrankho  * bothered by this, kmem_alloc/kmem_free the ioctl arguments...
22775121Sfrankho  */
22785121Sfrankho static void
22795121Sfrankho pcfs_device_getinfo(struct pcfs *fsp)
22805121Sfrankho {
22815121Sfrankho 	dev_t			rdev = fsp->pcfs_xdev;
22825121Sfrankho 	int			error;
22835121Sfrankho 	union {
22845121Sfrankho 		struct dk_minfo		mi;
22855121Sfrankho 		struct dk_cinfo		ci;
22865121Sfrankho 		struct dk_geom		gi;
22875121Sfrankho 		struct fd_char		fc;
22885121Sfrankho 	} arg;				/* save stackspace ... */
22895121Sfrankho 	intptr_t argp = (intptr_t)&arg;
22905121Sfrankho 	ldi_handle_t		lh;
22915121Sfrankho 	ldi_ident_t		li;
22925121Sfrankho 	int isfloppy, isremoveable, ishotpluggable;
22935121Sfrankho 	cred_t			*cr = CRED();
22945121Sfrankho 
22955121Sfrankho 	if (ldi_ident_from_dev(rdev, &li))
22965121Sfrankho 		goto out;
22975121Sfrankho 
22985121Sfrankho 	error = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, cr, &lh, li);
22995121Sfrankho 	ldi_ident_release(li);
23005121Sfrankho 	if (error)
23015121Sfrankho 		goto out;
23025121Sfrankho 
23035121Sfrankho 	/*
23045121Sfrankho 	 * Not sure if this could possibly happen. It'd be a bit like
23055121Sfrankho 	 * VOP_OPEN() changing the passed-in vnode ptr. We're just not
23065121Sfrankho 	 * expecting it, needs some thought if triggered ...
23075121Sfrankho 	 */
23085121Sfrankho 	ASSERT(fsp->pcfs_xdev == rdev);
23095121Sfrankho 
23105121Sfrankho 	/*
23115121Sfrankho 	 * Check for removeable/hotpluggable media.
23125121Sfrankho 	 */
23135121Sfrankho 	if (ldi_ioctl(lh, DKIOCREMOVABLE,
23145121Sfrankho 	    (intptr_t)&isremoveable, FKIOCTL, cr, NULL)) {
23155121Sfrankho 		isremoveable = 0;
23165121Sfrankho 	}
23175121Sfrankho 	if (ldi_ioctl(lh, DKIOCHOTPLUGGABLE,
23185121Sfrankho 	    (intptr_t)&ishotpluggable, FKIOCTL, cr, NULL)) {
23195121Sfrankho 		ishotpluggable = 0;
23205121Sfrankho 	}
23215121Sfrankho 
23225121Sfrankho 	/*
23235121Sfrankho 	 * Make sure we don't use "half-initialized" values if the ioctls fail.
23245121Sfrankho 	 */
23255121Sfrankho 	if (ldi_ioctl(lh, DKIOCGMEDIAINFO, argp, FKIOCTL, cr, NULL)) {
23265121Sfrankho 		bzero(&arg, sizeof (arg));
23275121Sfrankho 		fsp->pcfs_mediasize = 0;
23285121Sfrankho 	} else {
23295121Sfrankho 		fsp->pcfs_mediasize =
23305121Sfrankho 		    (len_t)arg.mi.dki_lbsize *
23315121Sfrankho 		    (len_t)arg.mi.dki_capacity;
23325121Sfrankho 	}
23335121Sfrankho 
23345121Sfrankho 	if (VALID_SECSIZE(arg.mi.dki_lbsize)) {
23355121Sfrankho 		if (fsp->pcfs_secsize == 0) {
23365121Sfrankho 			fsp->pcfs_secsize = arg.mi.dki_lbsize;
23375121Sfrankho 			fsp->pcfs_sdshift =
23385121Sfrankho 			    ddi_ffs(arg.mi.dki_lbsize / DEV_BSIZE) - 1;
23395121Sfrankho 		} else {
23405121Sfrankho 			PC_DPRINTF4(1, "!pcfs: autodetected media block size "
23415121Sfrankho 			    "%d, device (%x.%x), different from user-provided "
23425121Sfrankho 			    "%d. User override - ignoring autodetect result.\n",
23435121Sfrankho 			    arg.mi.dki_lbsize,
23445121Sfrankho 			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
23455121Sfrankho 			    fsp->pcfs_secsize);
23465121Sfrankho 		}
23475121Sfrankho 	} else if (arg.mi.dki_lbsize) {
23485121Sfrankho 		PC_DPRINTF3(1, "!pcfs: autodetected media block size "
23495121Sfrankho 		    "%d, device (%x.%x), invalid (not 512, 1024, 2048, 4096). "
23505121Sfrankho 		    "Ignoring autodetect result.\n",
23515121Sfrankho 		    arg.mi.dki_lbsize,
23525121Sfrankho 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev));
23535121Sfrankho 	}
23545121Sfrankho 
23555121Sfrankho 	/*
23565121Sfrankho 	 * We treat the following media types as a floppy by default.
23575121Sfrankho 	 */
23585121Sfrankho 	isfloppy =
23595121Sfrankho 	    (arg.mi.dki_media_type == DK_FLOPPY ||
23605121Sfrankho 	    arg.mi.dki_media_type == DK_ZIP ||
23615121Sfrankho 	    arg.mi.dki_media_type == DK_JAZ);
23625121Sfrankho 
23635121Sfrankho 	/*
23645121Sfrankho 	 * if this device understands fdio(7I) requests it's
23655121Sfrankho 	 * obviously a floppy drive.
23665121Sfrankho 	 */
23675121Sfrankho 	if (!isfloppy &&
23685121Sfrankho 	    !ldi_ioctl(lh, FDIOGCHAR, argp, FKIOCTL, cr, NULL))
23695121Sfrankho 		isfloppy = 1;
23705121Sfrankho 
23715121Sfrankho 	/*
23725121Sfrankho 	 * some devices (PCMCIA pseudofloppies) we like to treat
23735121Sfrankho 	 * as floppies, but they don't understand fdio(7I) requests.
23745121Sfrankho 	 */
23755121Sfrankho 	if (!isfloppy &&
23765121Sfrankho 	    !ldi_ioctl(lh, DKIOCINFO, argp, FKIOCTL, cr, NULL) &&
23775121Sfrankho 	    (arg.ci.dki_ctype == DKC_WDC2880 ||
23785121Sfrankho 	    arg.ci.dki_ctype == DKC_NCRFLOPPY ||
23795121Sfrankho 	    arg.ci.dki_ctype == DKC_SMSFLOPPY ||
23805121Sfrankho 	    arg.ci.dki_ctype == DKC_INTEL82077 ||
23815121Sfrankho 	    (arg.ci.dki_ctype == DKC_PCMCIA_MEM &&
23825121Sfrankho 	    arg.ci.dki_flags & DKI_PCMCIA_PFD)))
23835121Sfrankho 		isfloppy = 1;
23845121Sfrankho 
23855121Sfrankho 	/*
23865121Sfrankho 	 * This is the "final fallback" test - media with
23875121Sfrankho 	 * 2 heads and 80 cylinders are assumed to be floppies.
23885121Sfrankho 	 * This is normally true for USB floppy drives ...
23895121Sfrankho 	 */
23905121Sfrankho 	if (!isfloppy &&
23915121Sfrankho 	    !ldi_ioctl(lh, DKIOCGGEOM, argp, FKIOCTL, cr, NULL) &&
23925121Sfrankho 	    (arg.gi.dkg_ncyl == 80 && arg.gi.dkg_nhead == 2))
23935121Sfrankho 		isfloppy = 1;
23945121Sfrankho 
23955121Sfrankho 	/*
23965121Sfrankho 	 * This is similar to the "old" PCFS code that sets this flag
23975121Sfrankho 	 * just based on the media descriptor being 0xf8 (MD_FIXED).
23985121Sfrankho 	 * Should be re-worked. We really need some specialcasing for
23995121Sfrankho 	 * removeable media.
24005121Sfrankho 	 */
24015121Sfrankho 	if (!isfloppy) {
24025121Sfrankho 		fsp->pcfs_flags |= PCFS_NOCHK;
24035121Sfrankho 	}
24045121Sfrankho 
24055121Sfrankho 	/*
24065121Sfrankho 	 * We automatically disable access time updates if the medium is
24075121Sfrankho 	 * removeable and/or hotpluggable, and the admin did not explicitly
24085121Sfrankho 	 * request access time updates (via the "atime" mount option).
24095121Sfrankho 	 * The majority of flash-based media should fit this category.
24105121Sfrankho 	 * Minimizing write access extends the lifetime of your memory stick !
24115121Sfrankho 	 */
24125121Sfrankho 	if (!vfs_optionisset(fsp->pcfs_vfs, MNTOPT_ATIME, NULL) &&
24135121Sfrankho 	    (isremoveable || ishotpluggable | isfloppy)) {
24145121Sfrankho 		fsp->pcfs_flags |= PCFS_NOATIME;
24155121Sfrankho 	}
24165121Sfrankho 
24175121Sfrankho 	(void) ldi_close(lh, FREAD, cr);
24185121Sfrankho out:
24195121Sfrankho 	if (fsp->pcfs_secsize == 0) {
24205121Sfrankho 		PC_DPRINTF3(1, "!pcfs: media block size autodetection "
24215121Sfrankho 		    "device (%x.%x) failed, no user-provided fallback. "
24225121Sfrankho 		    "Using %d bytes.\n",
24235121Sfrankho 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
24245121Sfrankho 		    DEV_BSIZE);
24255121Sfrankho 		fsp->pcfs_secsize = DEV_BSIZE;
24265121Sfrankho 		fsp->pcfs_sdshift = 0;
24275121Sfrankho 	}
24285121Sfrankho 	ASSERT(fsp->pcfs_secsize % DEV_BSIZE == 0);
24295121Sfrankho 	ASSERT(VALID_SECSIZE(fsp->pcfs_secsize));
24305121Sfrankho }
24315121Sfrankho 
24325121Sfrankho /*
24335121Sfrankho  * Get the FAT type for the DOS medium.
24345121Sfrankho  *
24355121Sfrankho  * -------------------------
24365121Sfrankho  * According to Microsoft:
24375121Sfrankho  *   The FAT type one of FAT12, FAT16, or FAT32 is determined by the
24385121Sfrankho  * count of clusters on the volume and nothing else.
24395121Sfrankho  * -------------------------
24405121Sfrankho  *
24415121Sfrankho  */
24425121Sfrankho static int
24435121Sfrankho pc_getfattype(struct pcfs *fsp)
24445121Sfrankho {
24455121Sfrankho 	int error = 0;
24465121Sfrankho 	buf_t *bp = NULL;
24475121Sfrankho 	struct vnode *devvp = fsp->pcfs_devvp;
24485121Sfrankho 	dev_t	dev = devvp->v_rdev;
24495121Sfrankho 
24505121Sfrankho 	/*
24515121Sfrankho 	 * Detect the native block size of the medium, and attempt to
24525121Sfrankho 	 * detect whether the medium is removeable.
24535121Sfrankho 	 * We do treat removeable media (floppies, PCMCIA memory cards,
24545121Sfrankho 	 * USB and FireWire disks) differently wrt. to the frequency
24555121Sfrankho 	 * and synchronicity of FAT updates.
24565121Sfrankho 	 * We need to know the media block size in order to be able to
24575121Sfrankho 	 * parse the partition table.
24585121Sfrankho 	 */
24595121Sfrankho 	pcfs_device_getinfo(fsp);
24605121Sfrankho 
24615121Sfrankho 	/*
24625121Sfrankho 	 * Unpartitioned media (floppies and some removeable devices)
24635121Sfrankho 	 * don't have a partition table, the FAT BPB is at disk block 0.
24645121Sfrankho 	 * Start out by reading block 0.
24655121Sfrankho 	 */
24665121Sfrankho 	fsp->pcfs_dosstart = 0;
24675121Sfrankho 	bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), fsp->pcfs_secsize);
24685121Sfrankho 
24695121Sfrankho 	if (error = geterror(bp))
24705121Sfrankho 		goto out;
24715121Sfrankho 
24725121Sfrankho 	/*
24735121Sfrankho 	 * If a logical drive number is requested, parse the partition table
24745121Sfrankho 	 * and attempt to locate it. Otherwise, proceed immediately to the
24755121Sfrankho 	 * BPB check. findTheDrive(), if successful, returns the disk block
24765121Sfrankho 	 * number where the requested partition starts in "startsec".
24775121Sfrankho 	 */
24785121Sfrankho 	if (fsp->pcfs_ldrive != 0) {
24795121Sfrankho 		PC_DPRINTF3(5, "!pcfs: pc_getfattype: using FDISK table on "
24805121Sfrankho 		    "device (%x,%x):%d to find BPB\n",
24815121Sfrankho 		    getmajor(dev), getminor(dev), fsp->pcfs_ldrive);
24825121Sfrankho 
24835121Sfrankho 		if (error = findTheDrive(fsp, &bp))
24845121Sfrankho 			goto out;
24855121Sfrankho 
24865121Sfrankho 		ASSERT(fsp->pcfs_dosstart != 0);
24875121Sfrankho 
24885121Sfrankho 		brelse(bp);
24895121Sfrankho 		bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
24905121Sfrankho 		    fsp->pcfs_secsize);
24915121Sfrankho 		if (error = geterror(bp))
24925121Sfrankho 			goto out;
24935121Sfrankho 	}
24945121Sfrankho 
24955121Sfrankho 	/*
24965121Sfrankho 	 * Validate the BPB and fill in the instance structure.
24975121Sfrankho 	 */
24985121Sfrankho 	if (!parseBPB(fsp, (uchar_t *)bp->b_un.b_addr, NULL)) {
24995121Sfrankho 		PC_DPRINTF4(1, "!pcfs: pc_getfattype: No FAT BPB on "
25005121Sfrankho 		    "device (%x.%x):%d, disk LBA %u\n",
25015121Sfrankho 		    getmajor(dev), getminor(dev), fsp->pcfs_ldrive,
25025121Sfrankho 		    (uint_t)pc_dbdaddr(fsp, fsp->pcfs_dosstart));
25035121Sfrankho 		error = EINVAL;
25045121Sfrankho 		goto out;
25055121Sfrankho 	}
25065121Sfrankho 
25075121Sfrankho 	ASSERT(fsp->pcfs_fattype != FAT_UNKNOWN);
25085121Sfrankho 
25095121Sfrankho out:
25105121Sfrankho 	/*
25115121Sfrankho 	 * Release the buffer used
25125121Sfrankho 	 */
25135121Sfrankho 	if (bp != NULL)
25145121Sfrankho 		brelse(bp);
25155121Sfrankho 	return (error);
25165121Sfrankho }
25175121Sfrankho 
25185121Sfrankho 
25195121Sfrankho /*
25205121Sfrankho  * Get the file allocation table.
25215121Sfrankho  * If there is an old FAT, invalidate it.
25225121Sfrankho  */
25235121Sfrankho int
25245121Sfrankho pc_getfat(struct pcfs *fsp)
25255121Sfrankho {
25265121Sfrankho 	struct buf *bp = NULL;
25275121Sfrankho 	uchar_t *fatp = NULL;
25285121Sfrankho 	uchar_t *fat_changemap = NULL;
25295121Sfrankho 	int error;
25305121Sfrankho 	int fat_changemapsize;
25315121Sfrankho 	int flags = 0;
25325121Sfrankho 	int nfat;
25335121Sfrankho 	int altfat_mustmatch = 0;
25345121Sfrankho 	int fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
25355121Sfrankho 
25365121Sfrankho 	if (fsp->pcfs_fatp) {
25375121Sfrankho 		/*
25385121Sfrankho 		 * There is a FAT in core.
25395121Sfrankho 		 * If there are open file pcnodes or we have modified it or
25405121Sfrankho 		 * it hasn't timed out yet use the in core FAT.
25415121Sfrankho 		 * Otherwise invalidate it and get a new one
25425121Sfrankho 		 */
25435121Sfrankho #ifdef notdef
25445121Sfrankho 		if (fsp->pcfs_frefs ||
25455121Sfrankho 		    (fsp->pcfs_flags & PCFS_FATMOD) ||
25465121Sfrankho 		    (gethrestime_sec() < fsp->pcfs_fattime)) {
25475121Sfrankho 			return (0);
25485121Sfrankho 		} else {
25495121Sfrankho 			mutex_enter(&pcfslock);
25505121Sfrankho 			pc_invalfat(fsp);
25515121Sfrankho 			mutex_exit(&pcfslock);
25525121Sfrankho 		}
25535121Sfrankho #endif /* notdef */
25545121Sfrankho 		return (0);
25555121Sfrankho 	}
25565121Sfrankho 
25575121Sfrankho 	/*
25585121Sfrankho 	 * Get FAT and check it for validity
25595121Sfrankho 	 */
25605121Sfrankho 	fatp = kmem_alloc(fatsize, KM_SLEEP);
25615121Sfrankho 	error = pc_readfat(fsp, fatp);
25625121Sfrankho 	if (error) {
25635121Sfrankho 		flags = B_ERROR;
25645121Sfrankho 		goto out;
25655121Sfrankho 	}
25665121Sfrankho 	fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1;
25675121Sfrankho 	fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP);
25685121Sfrankho 	fsp->pcfs_fatp = fatp;
25695121Sfrankho 	fsp->pcfs_fat_changemapsize = fat_changemapsize;
25705121Sfrankho 	fsp->pcfs_fat_changemap = fat_changemap;
25715121Sfrankho 
25725121Sfrankho 	/*
25735121Sfrankho 	 * The only definite signature check is that the
25745121Sfrankho 	 * media descriptor byte should match the first byte
25755121Sfrankho 	 * of the FAT block.
25765121Sfrankho 	 */
25775121Sfrankho 	if (fatp[0] != fsp->pcfs_mediadesc) {
25785121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: FAT signature mismatch, "
25795121Sfrankho 		    "media descriptor %x, FAT[0] lowbyte %x\n",
25805121Sfrankho 		    (uint32_t)fsp->pcfs_mediadesc, (uint32_t)fatp[0]);
25815121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: Enforcing alternate FAT validation\n");
25825121Sfrankho 		altfat_mustmatch = 1;
25835121Sfrankho 	}
25845121Sfrankho 
25855121Sfrankho 	/*
25865121Sfrankho 	 * Get alternate FATs and check for consistency
25875121Sfrankho 	 * This is an inlined version of pc_readfat().
25885121Sfrankho 	 * Since we're only comparing FAT and alternate FAT,
25895121Sfrankho 	 * there's no reason to let pc_readfat() copy data out
25905121Sfrankho 	 * of the buf. Instead, compare in-situ, one cluster
25915121Sfrankho 	 * at a time.
25925121Sfrankho 	 */
25935121Sfrankho 	for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) {
25945121Sfrankho 		size_t startsec;
25955121Sfrankho 		size_t off;
25965121Sfrankho 
25975121Sfrankho 		startsec = pc_dbdaddr(fsp,
25985121Sfrankho 		    fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec);
25995121Sfrankho 
26005121Sfrankho 		for (off = 0; off < fatsize; off += fsp->pcfs_clsize) {
26015121Sfrankho 			daddr_t fatblk = startsec + pc_dbdaddr(fsp,
26025121Sfrankho 			    pc_cltodb(fsp, pc_lblkno(fsp, off)));
26035121Sfrankho 
26045121Sfrankho 			bp = bread(fsp->pcfs_xdev, fatblk,
26055121Sfrankho 			    MIN(fsp->pcfs_clsize, fatsize - off));
26065121Sfrankho 			if (bp->b_flags & (B_ERROR | B_STALE)) {
26075121Sfrankho 				cmn_err(CE_NOTE,
26085121Sfrankho 				    "!pcfs: alternate FAT #%d (start LBA %p)"
26095121Sfrankho 				    " read error at offset %ld on device"
26105121Sfrankho 				    " (%x.%x):%d",
26115121Sfrankho 				    nfat, (void *)(uintptr_t)startsec, off,
26125121Sfrankho 				    getmajor(fsp->pcfs_xdev),
26135121Sfrankho 				    getminor(fsp->pcfs_xdev),
26145121Sfrankho 				    fsp->pcfs_ldrive);
26155121Sfrankho 				flags = B_ERROR;
26165121Sfrankho 				error = EIO;
26175121Sfrankho 				goto out;
26185121Sfrankho 			}
26195121Sfrankho 			bp->b_flags |= B_STALE | B_AGE;
26205121Sfrankho 			if (bcmp(bp->b_un.b_addr, fatp + off,
26215121Sfrankho 			    MIN(fsp->pcfs_clsize, fatsize - off))) {
26225121Sfrankho 				cmn_err(CE_NOTE,
26235121Sfrankho 				    "!pcfs: alternate FAT #%d (start LBA %p)"
26245121Sfrankho 				    " corrupted at offset %ld on device"
26255121Sfrankho 				    " (%x.%x):%d",
26265121Sfrankho 				    nfat, (void *)(uintptr_t)startsec, off,
26275121Sfrankho 				    getmajor(fsp->pcfs_xdev),
26285121Sfrankho 				    getminor(fsp->pcfs_xdev),
26295121Sfrankho 				    fsp->pcfs_ldrive);
26305121Sfrankho 				if (altfat_mustmatch) {
26315121Sfrankho 					flags = B_ERROR;
26325121Sfrankho 					error = EIO;
26335121Sfrankho 					goto out;
26345121Sfrankho 				}
26355121Sfrankho 			}
26365121Sfrankho 			brelse(bp);
26375121Sfrankho 			bp = NULL;	/* prevent double release */
26385121Sfrankho 		}
26395121Sfrankho 	}
26405121Sfrankho 
26415121Sfrankho 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
26425121Sfrankho 	fsp->pcfs_fatjustread = 1;
26435121Sfrankho 
26445121Sfrankho 	/*
26455121Sfrankho 	 * Retrieve FAT32 fsinfo sector.
26465121Sfrankho 	 * A failure to read this is not fatal to accessing the volume.
26475121Sfrankho 	 * It simply means operations that count or search free blocks
26485121Sfrankho 	 * will have to do a full FAT walk, vs. a possibly quicker lookup
26495121Sfrankho 	 * of the summary information.
26505121Sfrankho 	 * Hence, we log a message but return success overall after this point.
26515121Sfrankho 	 */
26525121Sfrankho 	if (IS_FAT32(fsp) && (fsp->pcfs_flags & PCFS_FSINFO_OK)) {
26535121Sfrankho 		struct fat_od_fsi *fsinfo_disk;
26545121Sfrankho 
26555121Sfrankho 		bp = bread(fsp->pcfs_xdev,
26565121Sfrankho 		    pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
26575121Sfrankho 		fsinfo_disk = (struct fat_od_fsi *)bp->b_un.b_addr;
26585121Sfrankho 		if (bp->b_flags & (B_ERROR | B_STALE) ||
26595121Sfrankho 		    !FSISIG_OK(fsinfo_disk)) {
26605121Sfrankho 			cmn_err(CE_NOTE,
26615121Sfrankho 			    "!pcfs: error reading fat32 fsinfo from "
26625121Sfrankho 			    "device (%x.%x):%d, block %lld",
26635121Sfrankho 			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
26645121Sfrankho 			    fsp->pcfs_ldrive,
26655121Sfrankho 			    (long long)pc_dbdaddr(fsp, fsp->pcfs_fsistart));
26665121Sfrankho 			fsp->pcfs_flags &= ~PCFS_FSINFO_OK;
26675121Sfrankho 			fsp->pcfs_fsinfo.fs_free_clusters = FSINFO_UNKNOWN;
26685121Sfrankho 			fsp->pcfs_fsinfo.fs_next_free = FSINFO_UNKNOWN;
26695121Sfrankho 		} else {
26705121Sfrankho 			bp->b_flags |= B_STALE | B_AGE;
26715121Sfrankho 			fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
26725121Sfrankho 			fsp->pcfs_fsinfo.fs_free_clusters =
26735121Sfrankho 			    LE_32(fsinfo_disk->fsi_incore.fs_free_clusters);
26745121Sfrankho 			fsp->pcfs_fsinfo.fs_next_free =
26755121Sfrankho 			    LE_32(fsinfo_disk->fsi_incore.fs_next_free);
26765121Sfrankho 		}
26775121Sfrankho 		brelse(bp);
26785121Sfrankho 		bp = NULL;
26795121Sfrankho 	}
26805121Sfrankho 
26815121Sfrankho 	if (pc_validcl(fsp, (pc_cluster32_t)fsp->pcfs_fsinfo.fs_next_free))
26825121Sfrankho 		fsp->pcfs_nxfrecls = fsp->pcfs_fsinfo.fs_next_free;
26835121Sfrankho 	else
26845121Sfrankho 		fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
26855121Sfrankho 
26865121Sfrankho 	return (0);
26875121Sfrankho 
26885121Sfrankho out:
26895121Sfrankho 	cmn_err(CE_NOTE, "!pcfs: illegal disk format");
26905121Sfrankho 	if (bp)
26915121Sfrankho 		brelse(bp);
26925121Sfrankho 	if (fatp)
26935121Sfrankho 		kmem_free(fatp, fatsize);
26945121Sfrankho 	if (fat_changemap)
26955121Sfrankho 		kmem_free(fat_changemap, fat_changemapsize);
26965121Sfrankho 
26975121Sfrankho 	if (flags) {
26985121Sfrankho 		pc_mark_irrecov(fsp);
26995121Sfrankho 	}
27005121Sfrankho 	return (error);
27015121Sfrankho }
2702