xref: /onnv-gate/usr/src/uts/common/fs/pcfs/pc_vfsops.c (revision 11342:2310541b09ca)
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  */
217563SPrasad.Singamsetty@Sun.COM 
220Sstevel@tonic-gate /*
2311215Sgdamore@opensolaris.org  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #include <sys/param.h>
280Sstevel@tonic-gate #include <sys/systm.h>
290Sstevel@tonic-gate #include <sys/kmem.h>
300Sstevel@tonic-gate #include <sys/user.h>
310Sstevel@tonic-gate #include <sys/proc.h>
320Sstevel@tonic-gate #include <sys/cred.h>
330Sstevel@tonic-gate #include <sys/disp.h>
340Sstevel@tonic-gate #include <sys/buf.h>
350Sstevel@tonic-gate #include <sys/vfs.h>
363898Srsb #include <sys/vfs_opreg.h>
370Sstevel@tonic-gate #include <sys/vnode.h>
380Sstevel@tonic-gate #include <sys/fdio.h>
390Sstevel@tonic-gate #include <sys/file.h>
400Sstevel@tonic-gate #include <sys/uio.h>
410Sstevel@tonic-gate #include <sys/conf.h>
420Sstevel@tonic-gate #include <sys/statvfs.h>
430Sstevel@tonic-gate #include <sys/mount.h>
440Sstevel@tonic-gate #include <sys/pathname.h>
450Sstevel@tonic-gate #include <sys/cmn_err.h>
460Sstevel@tonic-gate #include <sys/debug.h>
470Sstevel@tonic-gate #include <sys/sysmacros.h>
480Sstevel@tonic-gate #include <sys/conf.h>
490Sstevel@tonic-gate #include <sys/mkdev.h>
500Sstevel@tonic-gate #include <sys/swap.h>
510Sstevel@tonic-gate #include <sys/sunddi.h>
520Sstevel@tonic-gate #include <sys/sunldi.h>
530Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
540Sstevel@tonic-gate #include <sys/fs/pc_label.h>
550Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
560Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
570Sstevel@tonic-gate #include <sys/fs/pc_node.h>
580Sstevel@tonic-gate #include <fs/fs_subr.h>
590Sstevel@tonic-gate #include <sys/modctl.h>
600Sstevel@tonic-gate #include <sys/dkio.h>
610Sstevel@tonic-gate #include <sys/open.h>
620Sstevel@tonic-gate #include <sys/mntent.h>
630Sstevel@tonic-gate #include <sys/policy.h>
642720Sfrankho #include <sys/atomic.h>
655121Sfrankho #include <sys/sdt.h>
660Sstevel@tonic-gate 
670Sstevel@tonic-gate /*
680Sstevel@tonic-gate  * The majority of PC media use a 512 sector size, but
690Sstevel@tonic-gate  * occasionally you will run across a 1k sector size.
700Sstevel@tonic-gate  * For media with a 1k sector size, fd_strategy() requires
710Sstevel@tonic-gate  * the I/O size to be a 1k multiple; so when the sector size
720Sstevel@tonic-gate  * is not yet known, always read 1k.
730Sstevel@tonic-gate  */
740Sstevel@tonic-gate #define	PC_SAFESECSIZE	(PC_SECSIZE * 2)
750Sstevel@tonic-gate 
76201Sjmcp static int pcfs_pseudo_floppy(dev_t);
770Sstevel@tonic-gate 
780Sstevel@tonic-gate static int pcfsinit(int, char *);
790Sstevel@tonic-gate static int pcfs_mount(struct vfs *, struct vnode *, struct mounta *,
800Sstevel@tonic-gate 	struct cred *);
810Sstevel@tonic-gate static int pcfs_unmount(struct vfs *, int, struct cred *);
820Sstevel@tonic-gate static int pcfs_root(struct vfs *, struct vnode **);
830Sstevel@tonic-gate static int pcfs_statvfs(struct vfs *, struct statvfs64 *);
840Sstevel@tonic-gate static int pc_syncfsnodes(struct pcfs *);
850Sstevel@tonic-gate static int pcfs_sync(struct vfs *, short, struct cred *);
860Sstevel@tonic-gate static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp);
872720Sfrankho static void pcfs_freevfs(vfs_t *vfsp);
880Sstevel@tonic-gate 
895121Sfrankho static int pc_readfat(struct pcfs *fsp, uchar_t *fatp);
900Sstevel@tonic-gate static int pc_writefat(struct pcfs *fsp, daddr_t start);
910Sstevel@tonic-gate 
925121Sfrankho static int pc_getfattype(struct pcfs *fsp);
936645Sgd78059 static void pcfs_parse_mntopts(struct pcfs *fsp);
945121Sfrankho 
955121Sfrankho 
960Sstevel@tonic-gate /*
970Sstevel@tonic-gate  * pcfs mount options table
980Sstevel@tonic-gate  */
990Sstevel@tonic-gate 
1002720Sfrankho static char *nohidden_cancel[] = { MNTOPT_PCFS_HIDDEN, NULL };
1012720Sfrankho static char *hidden_cancel[] = { MNTOPT_PCFS_NOHIDDEN, NULL };
1022720Sfrankho static char *nofoldcase_cancel[] = { MNTOPT_PCFS_FOLDCASE, NULL };
1032720Sfrankho static char *foldcase_cancel[] = { MNTOPT_PCFS_NOFOLDCASE, NULL };
1042720Sfrankho static char *clamptime_cancel[] = { MNTOPT_PCFS_NOCLAMPTIME, NULL };
1052720Sfrankho static char *noclamptime_cancel[] = { MNTOPT_PCFS_CLAMPTIME, NULL };
1065121Sfrankho static char *atime_cancel[] = { MNTOPT_NOATIME, NULL };
1075121Sfrankho static char *noatime_cancel[] = { MNTOPT_ATIME, NULL };
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate static mntopt_t mntopts[] = {
1100Sstevel@tonic-gate /*
1112720Sfrankho  *	option name	cancel option	default arg	flags	opt data
1120Sstevel@tonic-gate  */
1132720Sfrankho 	{ MNTOPT_PCFS_NOHIDDEN, nohidden_cancel, NULL, 0, NULL },
1142720Sfrankho 	{ MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, MO_DEFAULT, NULL },
1152720Sfrankho 	{ MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, NULL },
1162720Sfrankho 	{ MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0, NULL },
1172720Sfrankho 	{ MNTOPT_PCFS_CLAMPTIME, clamptime_cancel, NULL, MO_DEFAULT, NULL },
1185121Sfrankho 	{ MNTOPT_PCFS_NOCLAMPTIME, noclamptime_cancel, NULL, NULL, NULL },
1195121Sfrankho 	{ MNTOPT_NOATIME, noatime_cancel, NULL, NULL, NULL },
1205121Sfrankho 	{ MNTOPT_ATIME, atime_cancel, NULL, NULL, NULL },
1215121Sfrankho 	{ MNTOPT_PCFS_TIMEZONE, NULL, "+0", MO_DEFAULT | MO_HASVALUE, NULL },
1225121Sfrankho 	{ MNTOPT_PCFS_SECSIZE, NULL, NULL, MO_HASVALUE, NULL }
1230Sstevel@tonic-gate };
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate static mntopts_t pcfs_mntopts = {
1260Sstevel@tonic-gate 	sizeof (mntopts) / sizeof (mntopt_t),
1270Sstevel@tonic-gate 	mntopts
1280Sstevel@tonic-gate };
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate int pcfsdebuglevel = 0;
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate /*
1330Sstevel@tonic-gate  * pcfslock:	protects the list of mounted pc filesystems "pc_mounttab.
1340Sstevel@tonic-gate  * pcfs_lock:	(inside per filesystem structure "pcfs")
1350Sstevel@tonic-gate  *		per filesystem lock. Most of the vfsops and vnodeops are
1360Sstevel@tonic-gate  *		protected by this lock.
1370Sstevel@tonic-gate  * pcnodes_lock: protects the pcnode hash table "pcdhead", "pcfhead".
1380Sstevel@tonic-gate  *
1390Sstevel@tonic-gate  * Lock hierarchy: pcfslock > pcfs_lock > pcnodes_lock
1402720Sfrankho  *
1412720Sfrankho  * pcfs_mountcount:	used to prevent module unloads while there is still
1422720Sfrankho  *			pcfs state from a former mount hanging around. With
1432720Sfrankho  *			forced umount support, the filesystem module must not
1442720Sfrankho  *			be allowed to go away before the last VFS_FREEVFS()
1452720Sfrankho  *			call has been made.
1462720Sfrankho  *			Since this is just an atomic counter, there's no need
1472720Sfrankho  *			for locking.
1480Sstevel@tonic-gate  */
1490Sstevel@tonic-gate kmutex_t	pcfslock;
1502720Sfrankho krwlock_t	pcnodes_lock;
1512720Sfrankho uint32_t	pcfs_mountcount;
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate static int pcfstype;
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate static vfsdef_t vfw = {
1560Sstevel@tonic-gate 	VFSDEF_VERSION,
1570Sstevel@tonic-gate 	"pcfs",
1580Sstevel@tonic-gate 	pcfsinit,
1596855Sjohnlev 	VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS|VSW_CANLOFI,
1600Sstevel@tonic-gate 	&pcfs_mntopts
1610Sstevel@tonic-gate };
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate extern struct mod_ops mod_fsops;
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate static struct modlfs modlfs = {
1660Sstevel@tonic-gate 	&mod_fsops,
1676644Sgd78059 	"PC filesystem",
1680Sstevel@tonic-gate 	&vfw
1690Sstevel@tonic-gate };
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate static struct modlinkage modlinkage = {
1720Sstevel@tonic-gate 	MODREV_1,
1730Sstevel@tonic-gate 	&modlfs,
1740Sstevel@tonic-gate 	NULL
1750Sstevel@tonic-gate };
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate int
_init(void)1780Sstevel@tonic-gate _init(void)
1790Sstevel@tonic-gate {
1800Sstevel@tonic-gate 	int	error;
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate #if !defined(lint)
1830Sstevel@tonic-gate 	/* make sure the on-disk structures are sane */
1840Sstevel@tonic-gate 	ASSERT(sizeof (struct pcdir) == 32);
1850Sstevel@tonic-gate 	ASSERT(sizeof (struct pcdir_lfn) == 32);
1860Sstevel@tonic-gate #endif
1870Sstevel@tonic-gate 	mutex_init(&pcfslock, NULL, MUTEX_DEFAULT, NULL);
1880Sstevel@tonic-gate 	rw_init(&pcnodes_lock, NULL, RW_DEFAULT, NULL);
1890Sstevel@tonic-gate 	error = mod_install(&modlinkage);
1900Sstevel@tonic-gate 	if (error) {
1910Sstevel@tonic-gate 		mutex_destroy(&pcfslock);
1920Sstevel@tonic-gate 		rw_destroy(&pcnodes_lock);
1930Sstevel@tonic-gate 	}
1940Sstevel@tonic-gate 	return (error);
1950Sstevel@tonic-gate }
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate int
_fini(void)1980Sstevel@tonic-gate _fini(void)
1990Sstevel@tonic-gate {
2000Sstevel@tonic-gate 	int	error;
2010Sstevel@tonic-gate 
2022720Sfrankho 	/*
2032720Sfrankho 	 * If a forcedly unmounted instance is still hanging around,
2042720Sfrankho 	 * we cannot allow the module to be unloaded because that would
2052720Sfrankho 	 * cause panics once the VFS framework decides it's time to call
2062720Sfrankho 	 * into VFS_FREEVFS().
2072720Sfrankho 	 */
2082720Sfrankho 	if (pcfs_mountcount)
2092720Sfrankho 		return (EBUSY);
2102720Sfrankho 
2110Sstevel@tonic-gate 	error = mod_remove(&modlinkage);
2120Sstevel@tonic-gate 	if (error)
2130Sstevel@tonic-gate 		return (error);
2140Sstevel@tonic-gate 	mutex_destroy(&pcfslock);
2150Sstevel@tonic-gate 	rw_destroy(&pcnodes_lock);
2160Sstevel@tonic-gate 	/*
2170Sstevel@tonic-gate 	 * Tear down the operations vectors
2180Sstevel@tonic-gate 	 */
2190Sstevel@tonic-gate 	(void) vfs_freevfsops_by_type(pcfstype);
2200Sstevel@tonic-gate 	vn_freevnodeops(pcfs_fvnodeops);
2210Sstevel@tonic-gate 	vn_freevnodeops(pcfs_dvnodeops);
2220Sstevel@tonic-gate 	return (0);
2230Sstevel@tonic-gate }
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2260Sstevel@tonic-gate _info(struct modinfo *modinfop)
2270Sstevel@tonic-gate {
2280Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2290Sstevel@tonic-gate }
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate /* ARGSUSED1 */
2320Sstevel@tonic-gate static int
pcfsinit(int fstype,char * name)2330Sstevel@tonic-gate pcfsinit(int fstype, char *name)
2340Sstevel@tonic-gate {
2350Sstevel@tonic-gate 	static const fs_operation_def_t pcfs_vfsops_template[] = {
2363898Srsb 		VFSNAME_MOUNT,		{ .vfs_mount = pcfs_mount },
2373898Srsb 		VFSNAME_UNMOUNT,	{ .vfs_unmount = pcfs_unmount },
2383898Srsb 		VFSNAME_ROOT,		{ .vfs_root = pcfs_root },
2393898Srsb 		VFSNAME_STATVFS,	{ .vfs_statvfs = pcfs_statvfs },
2403898Srsb 		VFSNAME_SYNC,		{ .vfs_sync = pcfs_sync },
2413898Srsb 		VFSNAME_VGET,		{ .vfs_vget = pcfs_vget },
2423898Srsb 		VFSNAME_FREEVFS,	{ .vfs_freevfs = pcfs_freevfs },
2433898Srsb 		NULL,			NULL
2440Sstevel@tonic-gate 	};
2450Sstevel@tonic-gate 	int error;
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 	error = vfs_setfsops(fstype, pcfs_vfsops_template, NULL);
2480Sstevel@tonic-gate 	if (error != 0) {
2490Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad vfs ops template");
2500Sstevel@tonic-gate 		return (error);
2510Sstevel@tonic-gate 	}
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate 	error = vn_make_ops("pcfs", pcfs_fvnodeops_template, &pcfs_fvnodeops);
2540Sstevel@tonic-gate 	if (error != 0) {
2550Sstevel@tonic-gate 		(void) vfs_freevfsops_by_type(fstype);
2560Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad file vnode ops template");
2570Sstevel@tonic-gate 		return (error);
2580Sstevel@tonic-gate 	}
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate 	error = vn_make_ops("pcfsd", pcfs_dvnodeops_template, &pcfs_dvnodeops);
2610Sstevel@tonic-gate 	if (error != 0) {
2620Sstevel@tonic-gate 		(void) vfs_freevfsops_by_type(fstype);
2630Sstevel@tonic-gate 		vn_freevnodeops(pcfs_fvnodeops);
2640Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad dir vnode ops template");
2650Sstevel@tonic-gate 		return (error);
2660Sstevel@tonic-gate 	}
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate 	pcfstype = fstype;
2690Sstevel@tonic-gate 	(void) pc_init();
2702720Sfrankho 	pcfs_mountcount = 0;
2710Sstevel@tonic-gate 	return (0);
2720Sstevel@tonic-gate }
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate static struct pcfs *pc_mounttab = NULL;
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate extern struct pcfs_args pc_tz;
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate /*
2790Sstevel@tonic-gate  *  Define some special logical drives we use internal to this file.
2800Sstevel@tonic-gate  */
2810Sstevel@tonic-gate #define	BOOT_PARTITION_DRIVE	99
2820Sstevel@tonic-gate #define	PRIMARY_DOS_DRIVE	1
2835121Sfrankho #define	UNPARTITIONED_DRIVE	0
2840Sstevel@tonic-gate 
2855121Sfrankho static int
pcfs_device_identify(struct vfs * vfsp,struct mounta * uap,struct cred * cr,int * dos_ldrive,dev_t * xdev)2865121Sfrankho pcfs_device_identify(
2875121Sfrankho 	struct vfs *vfsp,
2885121Sfrankho 	struct mounta *uap,
2895121Sfrankho 	struct cred *cr,
2905121Sfrankho 	int *dos_ldrive,
2915121Sfrankho 	dev_t *xdev)
2925121Sfrankho {
2935121Sfrankho 	struct pathname special;
2945121Sfrankho 	char *c;
2956734Sjohnlev 	struct vnode *svp = NULL;
2966734Sjohnlev 	struct vnode *lvp = NULL;
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 =
3106734Sjohnlev 	    lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &svp)) {
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
36611215Sgdamore@opensolaris.org 			 *	  whether the physical device is a floppy.
3672972Sfrankho 			 */
3682972Sfrankho 			*c = tolower(*c);
3695121Sfrankho 			if (*c == 'a' || *c == 'b') {
3705121Sfrankho 				*dos_ldrive = UNPARTITIONED_DRIVE;
3715121Sfrankho 			} else if (*c < 'c' || *c > 'z') {
3725121Sfrankho 				error = ENXIO;
3732972Sfrankho 				goto devlookup_done;
3745121Sfrankho 			} else {
3755121Sfrankho 				*dos_ldrive = 1 + *c - 'c';
3762972Sfrankho 			}
3772972Sfrankho 		} else {
3782972Sfrankho 			/*
3792972Sfrankho 			 * Can't parse this - pass through previous error.
3802972Sfrankho 			 */
3812972Sfrankho 			goto devlookup_done;
3820Sstevel@tonic-gate 		}
3830Sstevel@tonic-gate 
3842972Sfrankho 
3852972Sfrankho 		error = lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW,
3866734Sjohnlev 		    NULLVPP, &svp);
3875121Sfrankho 	} else {
3885121Sfrankho 		*dos_ldrive = UNPARTITIONED_DRIVE;
3890Sstevel@tonic-gate 	}
3902972Sfrankho devlookup_done:
3910Sstevel@tonic-gate 	pn_free(&special);
3922972Sfrankho 	if (error)
3932972Sfrankho 		return (error);
3942972Sfrankho 
3955121Sfrankho 	ASSERT(*dos_ldrive >= UNPARTITIONED_DRIVE);
3965121Sfrankho 
3970Sstevel@tonic-gate 	/*
3980Sstevel@tonic-gate 	 * Verify caller's permission to open the device special file.
3990Sstevel@tonic-gate 	 */
4000Sstevel@tonic-gate 	if ((vfsp->vfs_flag & VFS_RDONLY) != 0 ||
4010Sstevel@tonic-gate 	    ((uap->flags & MS_RDONLY) != 0)) {
4020Sstevel@tonic-gate 		oflag = FREAD;
4030Sstevel@tonic-gate 		aflag = VREAD;
4040Sstevel@tonic-gate 	} else {
4050Sstevel@tonic-gate 		oflag = FREAD | FWRITE;
4060Sstevel@tonic-gate 		aflag = VREAD | VWRITE;
4070Sstevel@tonic-gate 	}
4085121Sfrankho 
4096734Sjohnlev 	error = vfs_get_lofi(vfsp, &lvp);
4105121Sfrankho 
4116734Sjohnlev 	if (error > 0) {
4126734Sjohnlev 		if (error == ENOENT)
4136734Sjohnlev 			error = ENODEV;
4146734Sjohnlev 		goto out;
4156734Sjohnlev 	} else if (error == 0) {
4166734Sjohnlev 		*xdev = lvp->v_rdev;
4176734Sjohnlev 	} else {
4186734Sjohnlev 		*xdev = svp->v_rdev;
4196734Sjohnlev 
4208424Sjohn.levon@sun.com 		if (svp->v_type != VBLK) {
4216734Sjohnlev 			error = ENOTBLK;
4228424Sjohn.levon@sun.com 			goto out;
4238424Sjohn.levon@sun.com 		}
4246734Sjohnlev 
4256734Sjohnlev 		if ((error = secpolicy_spec_open(cr, svp, oflag)) != 0)
4266734Sjohnlev 			goto out;
4270Sstevel@tonic-gate 	}
4280Sstevel@tonic-gate 
4296734Sjohnlev 	if (getmajor(*xdev) >= devcnt) {
4306734Sjohnlev 		error = ENXIO;
4316734Sjohnlev 		goto out;
4326734Sjohnlev 	}
4336734Sjohnlev 
4346734Sjohnlev 	if ((error = VOP_ACCESS(svp, aflag, 0, cr, NULL)) != 0)
4356734Sjohnlev 		goto out;
4366734Sjohnlev 
4376734Sjohnlev out:
4386734Sjohnlev 	if (svp != NULL)
4396734Sjohnlev 		VN_RELE(svp);
4406734Sjohnlev 	if (lvp != NULL)
4416734Sjohnlev 		VN_RELE(lvp);
4426734Sjohnlev 	return (error);
4435121Sfrankho }
4445121Sfrankho 
4455121Sfrankho static int
pcfs_device_ismounted(struct vfs * vfsp,int dos_ldrive,dev_t xdev,int * remounting,dev_t * pseudodev)4465121Sfrankho pcfs_device_ismounted(
4475121Sfrankho 	struct vfs *vfsp,
4485121Sfrankho 	int dos_ldrive,
4495121Sfrankho 	dev_t xdev,
4505121Sfrankho 	int *remounting,
4515121Sfrankho 	dev_t *pseudodev)
4525121Sfrankho {
4535121Sfrankho 	struct pcfs *fsp;
4545121Sfrankho 	int remount = *remounting;
4555121Sfrankho 
4560Sstevel@tonic-gate 	/*
4572972Sfrankho 	 * Ensure that this logical drive isn't already mounted, unless
4582972Sfrankho 	 * this is a REMOUNT request.
4592972Sfrankho 	 * Note: The framework will perform this check if the "...:c"
4602972Sfrankho 	 * PCFS-style "logical drive" syntax has not been used and an
4612972Sfrankho 	 * actually existing physical device is backing this filesystem.
4625121Sfrankho 	 * Once all block device drivers support PC-style partitioning,
4635121Sfrankho 	 * this codeblock can be dropped.
4640Sstevel@tonic-gate 	 */
4655121Sfrankho 	*pseudodev = xdev;
4665121Sfrankho 
4670Sstevel@tonic-gate 	if (dos_ldrive) {
4680Sstevel@tonic-gate 		mutex_enter(&pcfslock);
4690Sstevel@tonic-gate 		for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt)
4700Sstevel@tonic-gate 			if (fsp->pcfs_xdev == xdev &&
4715121Sfrankho 			    fsp->pcfs_ldrive == dos_ldrive) {
4720Sstevel@tonic-gate 				mutex_exit(&pcfslock);
4735121Sfrankho 				if (remount) {
4740Sstevel@tonic-gate 					return (0);
4750Sstevel@tonic-gate 				} else {
4760Sstevel@tonic-gate 					return (EBUSY);
4770Sstevel@tonic-gate 				}
4780Sstevel@tonic-gate 			}
4790Sstevel@tonic-gate 		/*
4800Sstevel@tonic-gate 		 * Assign a unique device number for the vfs
4810Sstevel@tonic-gate 		 * The old way (getudev() + a constantly incrementing
4820Sstevel@tonic-gate 		 * major number) was wrong because it changes vfs_dev
4830Sstevel@tonic-gate 		 * across mounts and reboots, which breaks nfs file handles.
4840Sstevel@tonic-gate 		 * UFS just uses the real dev_t. We can't do that because
4850Sstevel@tonic-gate 		 * of the way pcfs opens fdisk partitons (the :c and :d
4860Sstevel@tonic-gate 		 * partitions are on the same dev_t). Though that _might_
4870Sstevel@tonic-gate 		 * actually be ok, since the file handle contains an
4880Sstevel@tonic-gate 		 * absolute block number, it's probably better to make them
4890Sstevel@tonic-gate 		 * different. So I think we should retain the original
4900Sstevel@tonic-gate 		 * dev_t, but come up with a different minor number based
4910Sstevel@tonic-gate 		 * on the logical drive that will _always_ come up the same.
4920Sstevel@tonic-gate 		 * For now, we steal the upper 6 bits.
4930Sstevel@tonic-gate 		 */
4940Sstevel@tonic-gate #ifdef notdef
4950Sstevel@tonic-gate 		/* what should we do here? */
4960Sstevel@tonic-gate 		if (((getminor(xdev) >> 12) & 0x3F) != 0)
4970Sstevel@tonic-gate 			printf("whoops - upper bits used!\n");
4980Sstevel@tonic-gate #endif
4995121Sfrankho 		*pseudodev = makedevice(getmajor(xdev),
5005121Sfrankho 		    ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32);
5015121Sfrankho 		if (vfs_devmounting(*pseudodev, vfsp)) {
5020Sstevel@tonic-gate 			mutex_exit(&pcfslock);
5030Sstevel@tonic-gate 			return (EBUSY);
5040Sstevel@tonic-gate 		}
5055121Sfrankho 		if (vfs_devismounted(*pseudodev)) {
5060Sstevel@tonic-gate 			mutex_exit(&pcfslock);
5075121Sfrankho 			if (remount) {
5080Sstevel@tonic-gate 				return (0);
5090Sstevel@tonic-gate 			} else {
5100Sstevel@tonic-gate 				return (EBUSY);
5110Sstevel@tonic-gate 			}
5120Sstevel@tonic-gate 		}
5130Sstevel@tonic-gate 		mutex_exit(&pcfslock);
5140Sstevel@tonic-gate 	} else {
5155121Sfrankho 		*pseudodev = xdev;
5165121Sfrankho 		if (vfs_devmounting(*pseudodev, vfsp)) {
5170Sstevel@tonic-gate 			return (EBUSY);
5180Sstevel@tonic-gate 		}
5195121Sfrankho 		if (vfs_devismounted(*pseudodev))
5205121Sfrankho 			if (remount) {
5210Sstevel@tonic-gate 				return (0);
5220Sstevel@tonic-gate 			} else {
5230Sstevel@tonic-gate 				return (EBUSY);
5240Sstevel@tonic-gate 			}
5250Sstevel@tonic-gate 	}
5260Sstevel@tonic-gate 
5275121Sfrankho 	/*
5285121Sfrankho 	 * This is not a remount. Even if MS_REMOUNT was requested,
5295121Sfrankho 	 * the caller needs to proceed as it would on an ordinary
5305121Sfrankho 	 * mount.
5315121Sfrankho 	 */
5325121Sfrankho 	*remounting = 0;
5335121Sfrankho 
5345121Sfrankho 	ASSERT(*pseudodev);
5355121Sfrankho 	return (0);
5365121Sfrankho }
5375121Sfrankho 
5385121Sfrankho /*
5395121Sfrankho  * Get the PCFS-specific mount options from the VFS framework.
5405121Sfrankho  * For "timezone" and "secsize", we need to parse the number
5415121Sfrankho  * ourselves and ensure its validity.
5425121Sfrankho  * Note: "secsize" is deliberately undocumented at this time,
5435121Sfrankho  * it's a workaround for devices (particularly: lofi image files)
5445121Sfrankho  * that don't support the DKIOCGMEDIAINFO ioctl for autodetection.
5455121Sfrankho  */
5465121Sfrankho static void
pcfs_parse_mntopts(struct pcfs * fsp)5476645Sgd78059 pcfs_parse_mntopts(struct pcfs *fsp)
5485121Sfrankho {
5495121Sfrankho 	char *c;
5505121Sfrankho 	char *endptr;
5515121Sfrankho 	long l;
5525121Sfrankho 	struct vfs *vfsp = fsp->pcfs_vfs;
5535121Sfrankho 
5545121Sfrankho 	ASSERT(fsp->pcfs_secondswest == 0);
5555121Sfrankho 	ASSERT(fsp->pcfs_secsize == 0);
5565121Sfrankho 
5570Sstevel@tonic-gate 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL))
5580Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_HIDDEN;
5590Sstevel@tonic-gate 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL))
5600Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_FOLDCASE;
5612720Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL))
5622720Sfrankho 		fsp->pcfs_flags |= PCFS_NOCLAMPTIME;
5635121Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL))
5645121Sfrankho 		fsp->pcfs_flags |= PCFS_NOATIME;
5655121Sfrankho 
5665121Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_TIMEZONE, &c)) {
5675121Sfrankho 		if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
5685121Sfrankho 		    endptr == c + strlen(c)) {
5695121Sfrankho 			/*
5705121Sfrankho 			 * A number alright - in the allowed range ?
5715121Sfrankho 			 */
5725121Sfrankho 			if (l <= -12*3600 || l >= 12*3600) {
5735121Sfrankho 				cmn_err(CE_WARN, "!pcfs: invalid use of "
5745121Sfrankho 				    "'timezone' mount option - %ld "
5755121Sfrankho 				    "is out of range. Assuming 0.", l);
5765121Sfrankho 				l = 0;
5775121Sfrankho 			}
5785121Sfrankho 		} else {
5795121Sfrankho 			cmn_err(CE_WARN, "!pcfs: invalid use of "
5805121Sfrankho 			    "'timezone' mount option - argument %s "
5815121Sfrankho 			    "is not a valid number. Assuming 0.", c);
5825121Sfrankho 			l = 0;
5835121Sfrankho 		}
5845121Sfrankho 		fsp->pcfs_secondswest = l;
5855121Sfrankho 	}
5865121Sfrankho 
5875121Sfrankho 	/*
5885121Sfrankho 	 * The "secsize=..." mount option is a workaround for the lack of
5895121Sfrankho 	 * lofi(7d) support for DKIOCGMEDIAINFO. If PCFS wants to parse the
5905121Sfrankho 	 * partition table of a disk image and it has been partitioned with
5915121Sfrankho 	 * sector sizes other than 512 bytes, we'd fail on loopback'ed disk
5925121Sfrankho 	 * images.
5935121Sfrankho 	 * That should really be fixed in lofi ... this is a workaround.
5945121Sfrankho 	 */
5955121Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_SECSIZE, &c)) {
5965121Sfrankho 		if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
5975121Sfrankho 		    endptr == c + strlen(c)) {
5985121Sfrankho 			/*
5995121Sfrankho 			 * A number alright - a valid sector size as well ?
6005121Sfrankho 			 */
6015121Sfrankho 			if (!VALID_SECSIZE(l)) {
6025121Sfrankho 				cmn_err(CE_WARN, "!pcfs: invalid use of "
6035121Sfrankho 				    "'secsize' mount option - %ld is "
6045121Sfrankho 				    "unsupported. Autodetecting.", l);
6055121Sfrankho 				l = 0;
6065121Sfrankho 			}
6075121Sfrankho 		} else {
6085121Sfrankho 			cmn_err(CE_WARN, "!pcfs: invalid use of "
6095121Sfrankho 			    "'secsize' mount option - argument %s "
6105121Sfrankho 			    "is not a valid number. Autodetecting.", c);
6115121Sfrankho 			l = 0;
6125121Sfrankho 		}
6135121Sfrankho 		fsp->pcfs_secsize = l;
6145121Sfrankho 		fsp->pcfs_sdshift = ddi_ffs(l / DEV_BSIZE) - 1;
6155121Sfrankho 	}
6165121Sfrankho }
6175121Sfrankho 
6185121Sfrankho /*
6195121Sfrankho  * vfs operations
6205121Sfrankho  */
6215121Sfrankho 
6225121Sfrankho /*
6235121Sfrankho  * pcfs_mount - backend for VFS_MOUNT() on PCFS.
6245121Sfrankho  */
6255121Sfrankho static int
pcfs_mount(struct vfs * vfsp,struct vnode * mvp,struct mounta * uap,struct cred * cr)6265121Sfrankho pcfs_mount(
6275121Sfrankho 	struct vfs *vfsp,
6285121Sfrankho 	struct vnode *mvp,
6295121Sfrankho 	struct mounta *uap,
6305121Sfrankho 	struct cred *cr)
6315121Sfrankho {
6325121Sfrankho 	struct pcfs *fsp;
6335121Sfrankho 	struct vnode *devvp;
6345121Sfrankho 	dev_t pseudodev;
6355121Sfrankho 	dev_t xdev;
6365121Sfrankho 	int dos_ldrive = 0;
6375121Sfrankho 	int error;
6385121Sfrankho 	int remounting;
6395121Sfrankho 
6405121Sfrankho 	if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
6415121Sfrankho 		return (error);
6425121Sfrankho 
6435121Sfrankho 	if (mvp->v_type != VDIR)
6445121Sfrankho 		return (ENOTDIR);
6455121Sfrankho 
6465121Sfrankho 	mutex_enter(&mvp->v_lock);
6475121Sfrankho 	if ((uap->flags & MS_REMOUNT) == 0 &&
6485121Sfrankho 	    (uap->flags & MS_OVERLAY) == 0 &&
6495121Sfrankho 	    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
6505121Sfrankho 		mutex_exit(&mvp->v_lock);
6515121Sfrankho 		return (EBUSY);
6525121Sfrankho 	}
6535121Sfrankho 	mutex_exit(&mvp->v_lock);
6545121Sfrankho 
6555121Sfrankho 	/*
6565121Sfrankho 	 * PCFS doesn't do mount arguments anymore - everything's a mount
6575121Sfrankho 	 * option these days. In order not to break existing callers, we
6585121Sfrankho 	 * don't reject it yet, just warn that the data (if any) is ignored.
6595121Sfrankho 	 */
6605121Sfrankho 	if (uap->datalen != 0)
6615121Sfrankho 		cmn_err(CE_WARN, "!pcfs: deprecated use of mount(2) with "
6625121Sfrankho 		    "mount argument structures instead of mount options. "
6635121Sfrankho 		    "Ignoring mount(2) 'dataptr' argument.");
6645121Sfrankho 
6655121Sfrankho 	/*
6666644Sgd78059 	 * This is needed early, to make sure the access / open calls
6676644Sgd78059 	 * are done using the correct mode. Processing this mount option
6686644Sgd78059 	 * only when calling pcfs_parse_mntopts() would lead us to attempt
6696644Sgd78059 	 * a read/write access to a possibly writeprotected device, and
6706644Sgd78059 	 * a readonly mount attempt might fail because of that.
6716644Sgd78059 	 */
6726644Sgd78059 	if (uap->flags & MS_RDONLY) {
6736644Sgd78059 		vfsp->vfs_flag |= VFS_RDONLY;
6746644Sgd78059 		vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
6756644Sgd78059 	}
6766644Sgd78059 
6776644Sgd78059 	/*
6785121Sfrankho 	 * For most filesystems, this is just a lookupname() on the
6795121Sfrankho 	 * mount pathname string. PCFS historically has to do its own
6805121Sfrankho 	 * partition table parsing because not all Solaris architectures
6815121Sfrankho 	 * support all styles of partitioning that PC media can have, and
6825121Sfrankho 	 * hence PCFS understands "device names" that don't map to actual
6835121Sfrankho 	 * physical device nodes. Parsing the "PCFS syntax" for device
6845121Sfrankho 	 * names is done in pcfs_device_identify() - see there.
6855121Sfrankho 	 *
6865121Sfrankho 	 * Once all block device drivers that can host FAT filesystems have
6875121Sfrankho 	 * been enhanced to create device nodes for all PC-style partitions,
6885121Sfrankho 	 * this code can go away.
6895121Sfrankho 	 */
6905121Sfrankho 	if (error = pcfs_device_identify(vfsp, uap, cr, &dos_ldrive, &xdev))
6915121Sfrankho 		return (error);
6925121Sfrankho 
6935121Sfrankho 	/*
6945121Sfrankho 	 * As with looking up the actual device to mount, PCFS cannot rely
6955121Sfrankho 	 * on just the checks done by vfs_ismounted() whether a given device
6965121Sfrankho 	 * is mounted already. The additional check against the "PCFS syntax"
6975121Sfrankho 	 * is done in  pcfs_device_ismounted().
6985121Sfrankho 	 */
6995121Sfrankho 	remounting = (uap->flags & MS_REMOUNT);
7005121Sfrankho 
7015121Sfrankho 	if (error = pcfs_device_ismounted(vfsp, dos_ldrive, xdev, &remounting,
7025121Sfrankho 	    &pseudodev))
7035121Sfrankho 		return (error);
7045121Sfrankho 
7055121Sfrankho 	if (remounting)
7065121Sfrankho 		return (0);
7075121Sfrankho 
7085121Sfrankho 	/*
7095121Sfrankho 	 * Mount the filesystem.
7105121Sfrankho 	 * An instance structure is required before the attempt to locate
7115121Sfrankho 	 * and parse the FAT BPB. This is because mount options may change
7125121Sfrankho 	 * the behaviour of the filesystem type matching code. Precreate
7135121Sfrankho 	 * it and fill it in to a degree that allows parsing the mount
7145121Sfrankho 	 * options.
7155121Sfrankho 	 */
7165121Sfrankho 	devvp = makespecvp(xdev, VBLK);
7175121Sfrankho 	if (IS_SWAPVP(devvp)) {
7185121Sfrankho 		VN_RELE(devvp);
7195121Sfrankho 		return (EBUSY);
7205121Sfrankho 	}
7215121Sfrankho 	error = VOP_OPEN(&devvp,
7225331Samw 	    (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD | FWRITE, cr, NULL);
7235121Sfrankho 	if (error) {
7245121Sfrankho 		VN_RELE(devvp);
7255121Sfrankho 		return (error);
7265121Sfrankho 	}
7275121Sfrankho 
7285121Sfrankho 	fsp = kmem_zalloc(sizeof (*fsp), KM_SLEEP);
7295121Sfrankho 	fsp->pcfs_vfs = vfsp;
7305121Sfrankho 	fsp->pcfs_xdev = xdev;
7315121Sfrankho 	fsp->pcfs_devvp = devvp;
7325121Sfrankho 	fsp->pcfs_ldrive = dos_ldrive;
7335121Sfrankho 	mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL);
7340Sstevel@tonic-gate 
7356645Sgd78059 	pcfs_parse_mntopts(fsp);
7365121Sfrankho 
7375121Sfrankho 	/*
7385121Sfrankho 	 * This is the actual "mount" - the PCFS superblock check.
7395121Sfrankho 	 *
7405121Sfrankho 	 * Find the requested logical drive and the FAT BPB therein.
7415121Sfrankho 	 * Check device type and flag the instance if media is removeable.
7425121Sfrankho 	 *
7435121Sfrankho 	 * Initializes most members of the filesystem instance structure.
7445121Sfrankho 	 * Returns EINVAL if no valid BPB can be found. Other errors may
7455121Sfrankho 	 * occur after I/O failures, or when invalid / unparseable partition
7465121Sfrankho 	 * tables are encountered.
7475121Sfrankho 	 */
7485121Sfrankho 	if (error = pc_getfattype(fsp))
7495121Sfrankho 		goto errout;
7505121Sfrankho 
7515121Sfrankho 	/*
7526644Sgd78059 	 * Now that the BPB has been parsed, this structural information
7536644Sgd78059 	 * is available and known to be valid. Initialize the VFS.
7546644Sgd78059 	 */
7556644Sgd78059 	vfsp->vfs_data = fsp;
7566644Sgd78059 	vfsp->vfs_dev = pseudodev;
7576644Sgd78059 	vfsp->vfs_fstype = pcfstype;
7586644Sgd78059 	vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype);
7596644Sgd78059 	vfsp->vfs_bcount = 0;
7606644Sgd78059 	vfsp->vfs_bsize = fsp->pcfs_clsize;
7616644Sgd78059 
7626644Sgd78059 	/*
7635121Sfrankho 	 * Validate that we can access the FAT and that it is, to the
7645121Sfrankho 	 * degree we can verify here, self-consistent.
7655121Sfrankho 	 */
7665121Sfrankho 	if (error = pc_verify(fsp))
7675121Sfrankho 		goto errout;
7685121Sfrankho 
7695121Sfrankho 	/*
7705121Sfrankho 	 * Record the time of the mount, to return as an "approximate"
7715121Sfrankho 	 * timestamp for the FAT root directory. Since FAT roots don't
7725121Sfrankho 	 * have timestamps, this is less confusing to the user than
7735121Sfrankho 	 * claiming "zero" / Jan/01/1970.
7745121Sfrankho 	 */
7755121Sfrankho 	gethrestime(&fsp->pcfs_mounttime);
7765121Sfrankho 
7775121Sfrankho 	/*
7785121Sfrankho 	 * Fix up the mount options. Because "noatime" is made default on
7795121Sfrankho 	 * removeable media only, a fixed disk will have neither "atime"
7805121Sfrankho 	 * nor "noatime" set. We set the options explicitly depending on
7815121Sfrankho 	 * the PCFS_NOATIME flag, to inform the user of what applies.
7825121Sfrankho 	 * Mount option cancellation will take care that the mutually
7835121Sfrankho 	 * exclusive 'other' is cleared.
7845121Sfrankho 	 */
7855121Sfrankho 	vfs_setmntopt(vfsp,
7865121Sfrankho 	    fsp->pcfs_flags & PCFS_NOATIME ? MNTOPT_NOATIME : MNTOPT_ATIME,
7875121Sfrankho 	    NULL, 0);
7885121Sfrankho 
7895121Sfrankho 	/*
7905121Sfrankho 	 * All clear - insert the FS instance into PCFS' list.
7915121Sfrankho 	 */
7920Sstevel@tonic-gate 	mutex_enter(&pcfslock);
7930Sstevel@tonic-gate 	fsp->pcfs_nxt = pc_mounttab;
7940Sstevel@tonic-gate 	pc_mounttab = fsp;
7950Sstevel@tonic-gate 	mutex_exit(&pcfslock);
7962720Sfrankho 	atomic_inc_32(&pcfs_mountcount);
7970Sstevel@tonic-gate 	return (0);
7985121Sfrankho 
7995121Sfrankho errout:
8005121Sfrankho 	(void) VOP_CLOSE(devvp,
8015121Sfrankho 	    vfsp->vfs_flag & VFS_RDONLY ? FREAD : FREAD | FWRITE,
8025331Samw 	    1, (offset_t)0, cr, NULL);
8035121Sfrankho 	VN_RELE(devvp);
8045121Sfrankho 	mutex_destroy(&fsp->pcfs_lock);
8055121Sfrankho 	kmem_free(fsp, sizeof (*fsp));
8065121Sfrankho 	return (error);
8075121Sfrankho 
8080Sstevel@tonic-gate }
8090Sstevel@tonic-gate 
8100Sstevel@tonic-gate static int
pcfs_unmount(struct vfs * vfsp,int flag,struct cred * cr)8110Sstevel@tonic-gate pcfs_unmount(
8120Sstevel@tonic-gate 	struct vfs *vfsp,
8130Sstevel@tonic-gate 	int flag,
8140Sstevel@tonic-gate 	struct cred *cr)
8150Sstevel@tonic-gate {
8160Sstevel@tonic-gate 	struct pcfs *fsp, *fsp1;
8170Sstevel@tonic-gate 
8180Sstevel@tonic-gate 	if (secpolicy_fs_unmount(cr, vfsp) != 0)
8190Sstevel@tonic-gate 		return (EPERM);
8200Sstevel@tonic-gate 
8210Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
8222720Sfrankho 
8230Sstevel@tonic-gate 	/*
8240Sstevel@tonic-gate 	 * We don't have to lock fsp because the VVFSLOCK in vfs layer will
8250Sstevel@tonic-gate 	 * prevent lookuppn from crossing the mount point.
8262720Sfrankho 	 * If this is not a forced umount request and there's ongoing I/O,
8272720Sfrankho 	 * don't allow the mount to proceed.
8280Sstevel@tonic-gate 	 */
8292720Sfrankho 	if (flag & MS_FORCE)
8302720Sfrankho 		vfsp->vfs_flag |= VFS_UNMOUNTED;
8312720Sfrankho 	else if (fsp->pcfs_nrefs)
8320Sstevel@tonic-gate 		return (EBUSY);
8332720Sfrankho 
8342720Sfrankho 	mutex_enter(&pcfslock);
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate 	/*
8372720Sfrankho 	 * If this is a forced umount request or if the fs instance has
8382720Sfrankho 	 * been marked as beyond recovery, allow the umount to proceed
8392720Sfrankho 	 * regardless of state. pc_diskchanged() forcibly releases all
8402720Sfrankho 	 * inactive vnodes/pcnodes.
8410Sstevel@tonic-gate 	 */
8422720Sfrankho 	if (flag & MS_FORCE || fsp->pcfs_flags & PCFS_IRRECOV) {
8430Sstevel@tonic-gate 		rw_enter(&pcnodes_lock, RW_WRITER);
8440Sstevel@tonic-gate 		pc_diskchanged(fsp);
8450Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
8460Sstevel@tonic-gate 	}
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate 	/* now there should be no pcp node on pcfhead or pcdhead. */
8490Sstevel@tonic-gate 
8500Sstevel@tonic-gate 	if (fsp == pc_mounttab) {
8510Sstevel@tonic-gate 		pc_mounttab = fsp->pcfs_nxt;
8520Sstevel@tonic-gate 	} else {
8530Sstevel@tonic-gate 		for (fsp1 = pc_mounttab; fsp1 != NULL; fsp1 = fsp1->pcfs_nxt)
8540Sstevel@tonic-gate 			if (fsp1->pcfs_nxt == fsp)
8550Sstevel@tonic-gate 				fsp1->pcfs_nxt = fsp->pcfs_nxt;
8560Sstevel@tonic-gate 	}
8570Sstevel@tonic-gate 
8580Sstevel@tonic-gate 	mutex_exit(&pcfslock);
8590Sstevel@tonic-gate 
8602720Sfrankho 	/*
8612720Sfrankho 	 * Since we support VFS_FREEVFS(), there's no need to
8622720Sfrankho 	 * free the fsp right now. The framework will tell us
8632720Sfrankho 	 * when the right time to do so has arrived by calling
8642720Sfrankho 	 * into pcfs_freevfs.
8652720Sfrankho 	 */
8660Sstevel@tonic-gate 	return (0);
8670Sstevel@tonic-gate }
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate /*
8700Sstevel@tonic-gate  * find root of pcfs
8710Sstevel@tonic-gate  */
8720Sstevel@tonic-gate static int
pcfs_root(struct vfs * vfsp,struct vnode ** vpp)8730Sstevel@tonic-gate pcfs_root(
8740Sstevel@tonic-gate 	struct vfs *vfsp,
8750Sstevel@tonic-gate 	struct vnode **vpp)
8760Sstevel@tonic-gate {
8770Sstevel@tonic-gate 	struct pcfs *fsp;
8780Sstevel@tonic-gate 	struct pcnode *pcp;
8790Sstevel@tonic-gate 	int error;
8800Sstevel@tonic-gate 
8810Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
8820Sstevel@tonic-gate 	if (error = pc_lockfs(fsp, 0, 0))
8830Sstevel@tonic-gate 		return (error);
8842720Sfrankho 
8850Sstevel@tonic-gate 	pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
8860Sstevel@tonic-gate 	pc_unlockfs(fsp);
8870Sstevel@tonic-gate 	*vpp = PCTOV(pcp);
8880Sstevel@tonic-gate 	pcp->pc_flags |= PC_EXTERNAL;
8890Sstevel@tonic-gate 	return (0);
8900Sstevel@tonic-gate }
8910Sstevel@tonic-gate 
8920Sstevel@tonic-gate /*
8930Sstevel@tonic-gate  * Get file system statistics.
8940Sstevel@tonic-gate  */
8950Sstevel@tonic-gate static int
pcfs_statvfs(struct vfs * vfsp,struct statvfs64 * sp)8960Sstevel@tonic-gate pcfs_statvfs(
8970Sstevel@tonic-gate 	struct vfs *vfsp,
8980Sstevel@tonic-gate 	struct statvfs64 *sp)
8990Sstevel@tonic-gate {
9000Sstevel@tonic-gate 	struct pcfs *fsp;
9010Sstevel@tonic-gate 	int error;
9020Sstevel@tonic-gate 	dev32_t d32;
9030Sstevel@tonic-gate 
9040Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
9050Sstevel@tonic-gate 	error = pc_getfat(fsp);
9060Sstevel@tonic-gate 	if (error)
9070Sstevel@tonic-gate 		return (error);
9080Sstevel@tonic-gate 	bzero(sp, sizeof (*sp));
9090Sstevel@tonic-gate 	sp->f_bsize = sp->f_frsize = fsp->pcfs_clsize;
9100Sstevel@tonic-gate 	sp->f_blocks = (fsblkcnt64_t)fsp->pcfs_ncluster;
9110Sstevel@tonic-gate 	sp->f_bavail = sp->f_bfree = (fsblkcnt64_t)pc_freeclusters(fsp);
9120Sstevel@tonic-gate 	sp->f_files = (fsfilcnt64_t)-1;
9130Sstevel@tonic-gate 	sp->f_ffree = (fsfilcnt64_t)-1;
9140Sstevel@tonic-gate 	sp->f_favail = (fsfilcnt64_t)-1;
9150Sstevel@tonic-gate #ifdef notdef
9160Sstevel@tonic-gate 	(void) cmpldev(&d32, fsp->pcfs_devvp->v_rdev);
9170Sstevel@tonic-gate #endif /* notdef */
9180Sstevel@tonic-gate 	(void) cmpldev(&d32, vfsp->vfs_dev);
9190Sstevel@tonic-gate 	sp->f_fsid = d32;
9200Sstevel@tonic-gate 	(void) strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name);
9210Sstevel@tonic-gate 	sp->f_flag = vf_to_stf(vfsp->vfs_flag);
9226971Sbatschul 	sp->f_namemax = PCMAXNAMLEN;
9230Sstevel@tonic-gate 	return (0);
9240Sstevel@tonic-gate }
9250Sstevel@tonic-gate 
9260Sstevel@tonic-gate static int
pc_syncfsnodes(struct pcfs * fsp)9270Sstevel@tonic-gate pc_syncfsnodes(struct pcfs *fsp)
9280Sstevel@tonic-gate {
9290Sstevel@tonic-gate 	struct pchead *hp;
9300Sstevel@tonic-gate 	struct pcnode *pcp;
9310Sstevel@tonic-gate 	int error;
9320Sstevel@tonic-gate 
9330Sstevel@tonic-gate 	if (error = pc_lockfs(fsp, 0, 0))
9340Sstevel@tonic-gate 		return (error);
9350Sstevel@tonic-gate 
9360Sstevel@tonic-gate 	if (!(error = pc_syncfat(fsp))) {
9370Sstevel@tonic-gate 		hp = pcfhead;
9380Sstevel@tonic-gate 		while (hp < & pcfhead [ NPCHASH ]) {
9390Sstevel@tonic-gate 			rw_enter(&pcnodes_lock, RW_READER);
9400Sstevel@tonic-gate 			pcp = hp->pch_forw;
9410Sstevel@tonic-gate 			while (pcp != (struct pcnode *)hp) {
9420Sstevel@tonic-gate 				if (VFSTOPCFS(PCTOV(pcp) -> v_vfsp) == fsp)
9430Sstevel@tonic-gate 					if (error = pc_nodesync(pcp))
9440Sstevel@tonic-gate 						break;
9450Sstevel@tonic-gate 				pcp = pcp -> pc_forw;
9460Sstevel@tonic-gate 			}
9470Sstevel@tonic-gate 			rw_exit(&pcnodes_lock);
9480Sstevel@tonic-gate 			if (error)
9490Sstevel@tonic-gate 				break;
9500Sstevel@tonic-gate 			hp++;
9510Sstevel@tonic-gate 		}
9520Sstevel@tonic-gate 	}
9530Sstevel@tonic-gate 	pc_unlockfs(fsp);
9540Sstevel@tonic-gate 	return (error);
9550Sstevel@tonic-gate }
9560Sstevel@tonic-gate 
9570Sstevel@tonic-gate /*
9580Sstevel@tonic-gate  * Flush any pending I/O.
9590Sstevel@tonic-gate  */
9600Sstevel@tonic-gate /*ARGSUSED*/
9610Sstevel@tonic-gate static int
pcfs_sync(struct vfs * vfsp,short flag,struct cred * cr)9620Sstevel@tonic-gate pcfs_sync(
9630Sstevel@tonic-gate 	struct vfs *vfsp,
9640Sstevel@tonic-gate 	short flag,
9650Sstevel@tonic-gate 	struct cred *cr)
9660Sstevel@tonic-gate {
9670Sstevel@tonic-gate 	struct pcfs *fsp;
9680Sstevel@tonic-gate 	int error = 0;
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 	/* this prevents the filesystem from being umounted. */
9710Sstevel@tonic-gate 	mutex_enter(&pcfslock);
9720Sstevel@tonic-gate 	if (vfsp != NULL) {
9730Sstevel@tonic-gate 		fsp = VFSTOPCFS(vfsp);
9740Sstevel@tonic-gate 		if (!(fsp->pcfs_flags & PCFS_IRRECOV)) {
9750Sstevel@tonic-gate 			error = pc_syncfsnodes(fsp);
9760Sstevel@tonic-gate 		} else {
9770Sstevel@tonic-gate 			rw_enter(&pcnodes_lock, RW_WRITER);
9780Sstevel@tonic-gate 			pc_diskchanged(fsp);
9790Sstevel@tonic-gate 			rw_exit(&pcnodes_lock);
9800Sstevel@tonic-gate 			error = EIO;
9810Sstevel@tonic-gate 		}
9820Sstevel@tonic-gate 	} else {
9830Sstevel@tonic-gate 		fsp = pc_mounttab;
9840Sstevel@tonic-gate 		while (fsp != NULL) {
9850Sstevel@tonic-gate 			if (fsp->pcfs_flags & PCFS_IRRECOV) {
9860Sstevel@tonic-gate 				rw_enter(&pcnodes_lock, RW_WRITER);
9870Sstevel@tonic-gate 				pc_diskchanged(fsp);
9880Sstevel@tonic-gate 				rw_exit(&pcnodes_lock);
9890Sstevel@tonic-gate 				error = EIO;
9900Sstevel@tonic-gate 				break;
9910Sstevel@tonic-gate 			}
9920Sstevel@tonic-gate 			error = pc_syncfsnodes(fsp);
9930Sstevel@tonic-gate 			if (error) break;
9940Sstevel@tonic-gate 			fsp = fsp->pcfs_nxt;
9950Sstevel@tonic-gate 		}
9960Sstevel@tonic-gate 	}
9970Sstevel@tonic-gate 	mutex_exit(&pcfslock);
9980Sstevel@tonic-gate 	return (error);
9990Sstevel@tonic-gate }
10000Sstevel@tonic-gate 
10010Sstevel@tonic-gate int
pc_lockfs(struct pcfs * fsp,int diskchanged,int releasing)10020Sstevel@tonic-gate pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing)
10030Sstevel@tonic-gate {
10042720Sfrankho 	int err;
10052720Sfrankho 
10060Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_IRRECOV) && !releasing)
10070Sstevel@tonic-gate 		return (EIO);
10080Sstevel@tonic-gate 
10090Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_LOCKED) && (fsp->pcfs_owner == curthread)) {
10100Sstevel@tonic-gate 		fsp->pcfs_count++;
10110Sstevel@tonic-gate 	} else {
10120Sstevel@tonic-gate 		mutex_enter(&fsp->pcfs_lock);
10130Sstevel@tonic-gate 		if (fsp->pcfs_flags & PCFS_LOCKED)
10140Sstevel@tonic-gate 			panic("pc_lockfs");
10150Sstevel@tonic-gate 		/*
10160Sstevel@tonic-gate 		 * We check the IRRECOV bit again just in case somebody
10170Sstevel@tonic-gate 		 * snuck past the initial check but then got held up before
10180Sstevel@tonic-gate 		 * they could grab the lock.  (And in the meantime someone
10190Sstevel@tonic-gate 		 * had grabbed the lock and set the bit)
10200Sstevel@tonic-gate 		 */
1021607Swyllys 		if (!diskchanged && !(fsp->pcfs_flags & PCFS_IRRECOV)) {
10222720Sfrankho 			if ((err = pc_getfat(fsp))) {
10232720Sfrankho 				mutex_exit(&fsp->pcfs_lock);
1024607Swyllys 				return (err);
10252720Sfrankho 			}
1026607Swyllys 		}
10270Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_LOCKED;
10280Sstevel@tonic-gate 		fsp->pcfs_owner = curthread;
10290Sstevel@tonic-gate 		fsp->pcfs_count++;
10300Sstevel@tonic-gate 	}
10310Sstevel@tonic-gate 	return (0);
10320Sstevel@tonic-gate }
10330Sstevel@tonic-gate 
10340Sstevel@tonic-gate void
pc_unlockfs(struct pcfs * fsp)10350Sstevel@tonic-gate pc_unlockfs(struct pcfs *fsp)
10360Sstevel@tonic-gate {
10370Sstevel@tonic-gate 
10380Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_LOCKED) == 0)
10390Sstevel@tonic-gate 		panic("pc_unlockfs");
10400Sstevel@tonic-gate 	if (--fsp->pcfs_count < 0)
10410Sstevel@tonic-gate 		panic("pc_unlockfs: count");
10420Sstevel@tonic-gate 	if (fsp->pcfs_count == 0) {
10430Sstevel@tonic-gate 		fsp->pcfs_flags &= ~PCFS_LOCKED;
10440Sstevel@tonic-gate 		fsp->pcfs_owner = 0;
10450Sstevel@tonic-gate 		mutex_exit(&fsp->pcfs_lock);
10460Sstevel@tonic-gate 	}
10470Sstevel@tonic-gate }
10480Sstevel@tonic-gate 
10490Sstevel@tonic-gate int
pc_syncfat(struct pcfs * fsp)10500Sstevel@tonic-gate pc_syncfat(struct pcfs *fsp)
10510Sstevel@tonic-gate {
10520Sstevel@tonic-gate 	struct buf *bp;
10530Sstevel@tonic-gate 	int nfat;
10545121Sfrankho 	int	error = 0;
10555121Sfrankho 	struct fat_od_fsi *fsinfo_disk;
10560Sstevel@tonic-gate 
10570Sstevel@tonic-gate 	if ((fsp->pcfs_fatp == (uchar_t *)0) ||
10580Sstevel@tonic-gate 	    !(fsp->pcfs_flags & PCFS_FATMOD))
10590Sstevel@tonic-gate 		return (0);
10600Sstevel@tonic-gate 	/*
10610Sstevel@tonic-gate 	 * write out all copies of FATs
10620Sstevel@tonic-gate 	 */
10630Sstevel@tonic-gate 	fsp->pcfs_flags &= ~PCFS_FATMOD;
10640Sstevel@tonic-gate 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
10650Sstevel@tonic-gate 	for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) {
10665121Sfrankho 		error = pc_writefat(fsp, pc_dbdaddr(fsp,
10675121Sfrankho 		    fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec));
10680Sstevel@tonic-gate 		if (error) {
10690Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
10700Sstevel@tonic-gate 			return (EIO);
10710Sstevel@tonic-gate 		}
10720Sstevel@tonic-gate 	}
10730Sstevel@tonic-gate 	pc_clear_fatchanges(fsp);
10745121Sfrankho 
10755121Sfrankho 	/*
10765121Sfrankho 	 * Write out fsinfo sector.
10775121Sfrankho 	 */
10780Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
10790Sstevel@tonic-gate 		bp = bread(fsp->pcfs_xdev,
10805121Sfrankho 		    pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
10810Sstevel@tonic-gate 		if (bp->b_flags & (B_ERROR | B_STALE)) {
10825121Sfrankho 			error = geterror(bp);
10830Sstevel@tonic-gate 		}
10845121Sfrankho 		fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
10855121Sfrankho 		if (!error && FSISIG_OK(fsinfo_disk)) {
10865121Sfrankho 			fsinfo_disk->fsi_incore.fs_free_clusters =
10875121Sfrankho 			    LE_32(fsp->pcfs_fsinfo.fs_free_clusters);
10885121Sfrankho 			fsinfo_disk->fsi_incore.fs_next_free =
10895121Sfrankho 			    LE_32(FSINFO_UNKNOWN);
10905121Sfrankho 			bwrite2(bp);
10915121Sfrankho 			error = geterror(bp);
10925121Sfrankho 		}
10930Sstevel@tonic-gate 		brelse(bp);
10940Sstevel@tonic-gate 		if (error) {
10950Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
10960Sstevel@tonic-gate 			return (EIO);
10970Sstevel@tonic-gate 		}
10980Sstevel@tonic-gate 	}
10990Sstevel@tonic-gate 	return (0);
11000Sstevel@tonic-gate }
11010Sstevel@tonic-gate 
11020Sstevel@tonic-gate void
pc_invalfat(struct pcfs * fsp)11030Sstevel@tonic-gate pc_invalfat(struct pcfs *fsp)
11040Sstevel@tonic-gate {
11050Sstevel@tonic-gate 	struct pcfs *xfsp;
11060Sstevel@tonic-gate 	int mount_cnt = 0;
11070Sstevel@tonic-gate 
11080Sstevel@tonic-gate 	if (fsp->pcfs_fatp == (uchar_t *)0)
11090Sstevel@tonic-gate 		panic("pc_invalfat");
11100Sstevel@tonic-gate 	/*
11110Sstevel@tonic-gate 	 * Release FAT
11120Sstevel@tonic-gate 	 */
11135121Sfrankho 	kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsec * fsp->pcfs_secsize);
11140Sstevel@tonic-gate 	fsp->pcfs_fatp = NULL;
11150Sstevel@tonic-gate 	kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize);
11160Sstevel@tonic-gate 	fsp->pcfs_fat_changemap = NULL;
11170Sstevel@tonic-gate 	/*
11180Sstevel@tonic-gate 	 * Invalidate all the blocks associated with the device.
11190Sstevel@tonic-gate 	 * Not needed if stateless.
11200Sstevel@tonic-gate 	 */
11210Sstevel@tonic-gate 	for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt)
11220Sstevel@tonic-gate 		if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev)
11230Sstevel@tonic-gate 			mount_cnt++;
11240Sstevel@tonic-gate 
11250Sstevel@tonic-gate 	if (!mount_cnt)
11260Sstevel@tonic-gate 		binval(fsp->pcfs_xdev);
11270Sstevel@tonic-gate 	/*
11280Sstevel@tonic-gate 	 * close mounted device
11290Sstevel@tonic-gate 	 */
11300Sstevel@tonic-gate 	(void) VOP_CLOSE(fsp->pcfs_devvp,
11310Sstevel@tonic-gate 	    (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
11325331Samw 	    1, (offset_t)0, CRED(), NULL);
11330Sstevel@tonic-gate }
11340Sstevel@tonic-gate 
11350Sstevel@tonic-gate void
pc_badfs(struct pcfs * fsp)11360Sstevel@tonic-gate pc_badfs(struct pcfs *fsp)
11370Sstevel@tonic-gate {
11385121Sfrankho 	cmn_err(CE_WARN, "corrupted PC file system on dev (%x.%x):%d\n",
11390Sstevel@tonic-gate 	    getmajor(fsp->pcfs_devvp->v_rdev),
11405121Sfrankho 	    getminor(fsp->pcfs_devvp->v_rdev), fsp->pcfs_ldrive);
11410Sstevel@tonic-gate }
11420Sstevel@tonic-gate 
11430Sstevel@tonic-gate /*
11440Sstevel@tonic-gate  * The problem with supporting NFS on the PCFS filesystem is that there
11450Sstevel@tonic-gate  * is no good place to keep the generation number. The only possible
11460Sstevel@tonic-gate  * place is inside a directory entry. There are a few words that we
11470Sstevel@tonic-gate  * don't use - they store NT & OS/2 attributes, and the creation/last access
11480Sstevel@tonic-gate  * time of the file - but it seems wrong to use them. In addition, directory
11490Sstevel@tonic-gate  * entries come and go. If a directory is removed completely, its directory
11500Sstevel@tonic-gate  * blocks are freed and the generation numbers are lost. Whereas in ufs,
11510Sstevel@tonic-gate  * inode blocks are dedicated for inodes, so the generation numbers are
11520Sstevel@tonic-gate  * permanently kept on the disk.
11530Sstevel@tonic-gate  */
11540Sstevel@tonic-gate static int
pcfs_vget(struct vfs * vfsp,struct vnode ** vpp,struct fid * fidp)11550Sstevel@tonic-gate pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp)
11560Sstevel@tonic-gate {
11570Sstevel@tonic-gate 	struct pcnode *pcp;
11580Sstevel@tonic-gate 	struct pc_fid *pcfid;
11590Sstevel@tonic-gate 	struct pcfs *fsp;
11600Sstevel@tonic-gate 	struct pcdir *ep;
11610Sstevel@tonic-gate 	daddr_t eblkno;
11620Sstevel@tonic-gate 	int eoffset;
11630Sstevel@tonic-gate 	struct buf *bp;
11640Sstevel@tonic-gate 	int error;
11650Sstevel@tonic-gate 	pc_cluster32_t	cn;
11660Sstevel@tonic-gate 
11670Sstevel@tonic-gate 	pcfid = (struct pc_fid *)fidp;
11680Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
11690Sstevel@tonic-gate 
11700Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
11710Sstevel@tonic-gate 	if (error) {
11720Sstevel@tonic-gate 		*vpp = NULL;
11730Sstevel@tonic-gate 		return (error);
11740Sstevel@tonic-gate 	}
11750Sstevel@tonic-gate 
11760Sstevel@tonic-gate 	if (pcfid->pcfid_block == 0) {
11770Sstevel@tonic-gate 		pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
11780Sstevel@tonic-gate 		pcp->pc_flags |= PC_EXTERNAL;
11790Sstevel@tonic-gate 		*vpp = PCTOV(pcp);
11800Sstevel@tonic-gate 		pc_unlockfs(fsp);
11810Sstevel@tonic-gate 		return (0);
11820Sstevel@tonic-gate 	}
11830Sstevel@tonic-gate 	eblkno = pcfid->pcfid_block;
11840Sstevel@tonic-gate 	eoffset = pcfid->pcfid_offset;
11855121Sfrankho 
11860Sstevel@tonic-gate 	if ((pc_dbtocl(fsp,
11870Sstevel@tonic-gate 	    eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) ||
11880Sstevel@tonic-gate 	    (eoffset > fsp->pcfs_clsize)) {
11890Sstevel@tonic-gate 		pc_unlockfs(fsp);
11900Sstevel@tonic-gate 		*vpp = NULL;
11910Sstevel@tonic-gate 		return (EINVAL);
11920Sstevel@tonic-gate 	}
11930Sstevel@tonic-gate 
11945121Sfrankho 	if (eblkno >= fsp->pcfs_datastart || (eblkno - fsp->pcfs_rdirstart)
11950Sstevel@tonic-gate 	    < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
11965121Sfrankho 		bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
11975121Sfrankho 		    fsp->pcfs_clsize);
11980Sstevel@tonic-gate 	} else {
11995121Sfrankho 		/*
12005121Sfrankho 		 * This is an access "backwards" into the FAT12/FAT16
12015121Sfrankho 		 * root directory. A better code structure would
12025121Sfrankho 		 * significantly improve maintainability here ...
12035121Sfrankho 		 */
12045121Sfrankho 		bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
12050Sstevel@tonic-gate 		    (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize);
12060Sstevel@tonic-gate 	}
12070Sstevel@tonic-gate 	if (bp->b_flags & (B_ERROR | B_STALE)) {
12080Sstevel@tonic-gate 		error = geterror(bp);
12090Sstevel@tonic-gate 		brelse(bp);
12100Sstevel@tonic-gate 		if (error)
12110Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
12120Sstevel@tonic-gate 		*vpp = NULL;
12130Sstevel@tonic-gate 		pc_unlockfs(fsp);
12140Sstevel@tonic-gate 		return (error);
12150Sstevel@tonic-gate 	}
12160Sstevel@tonic-gate 	ep = (struct pcdir *)(bp->b_un.b_addr + eoffset);
12170Sstevel@tonic-gate 	/*
12180Sstevel@tonic-gate 	 * Ok, if this is a valid file handle that we gave out,
12190Sstevel@tonic-gate 	 * then simply ensuring that the creation time matches,
12200Sstevel@tonic-gate 	 * the entry has not been deleted, and it has a valid first
12210Sstevel@tonic-gate 	 * character should be enough.
12220Sstevel@tonic-gate 	 *
12230Sstevel@tonic-gate 	 * Unfortunately, verifying that the <blkno, offset> _still_
12240Sstevel@tonic-gate 	 * refers to a directory entry is not easy, since we'd have
12250Sstevel@tonic-gate 	 * to search _all_ directories starting from root to find it.
12260Sstevel@tonic-gate 	 * That's a high price to pay just in case somebody is forging
12270Sstevel@tonic-gate 	 * file handles. So instead we verify that as much of the
12280Sstevel@tonic-gate 	 * entry is valid as we can:
12290Sstevel@tonic-gate 	 *
12300Sstevel@tonic-gate 	 * 1. The starting cluster is 0 (unallocated) or valid
12310Sstevel@tonic-gate 	 * 2. It is not an LFN entry
12320Sstevel@tonic-gate 	 * 3. It is not hidden (unless mounted as such)
12330Sstevel@tonic-gate 	 * 4. It is not the label
12340Sstevel@tonic-gate 	 */
12350Sstevel@tonic-gate 	cn = pc_getstartcluster(fsp, ep);
12360Sstevel@tonic-gate 	/*
12370Sstevel@tonic-gate 	 * if the starting cluster is valid, but not valid according
12380Sstevel@tonic-gate 	 * to pc_validcl(), force it to be to simplify the following if.
12390Sstevel@tonic-gate 	 */
12400Sstevel@tonic-gate 	if (cn == 0)
12410Sstevel@tonic-gate 		cn = PCF_FIRSTCLUSTER;
12420Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
12430Sstevel@tonic-gate 		if (cn >= PCF_LASTCLUSTER32)
12440Sstevel@tonic-gate 			cn = PCF_FIRSTCLUSTER;
12450Sstevel@tonic-gate 	} else {
12460Sstevel@tonic-gate 		if (cn >= PCF_LASTCLUSTER)
12470Sstevel@tonic-gate 			cn = PCF_FIRSTCLUSTER;
12480Sstevel@tonic-gate 	}
12490Sstevel@tonic-gate 	if ((!pc_validcl(fsp, cn)) ||
12500Sstevel@tonic-gate 	    (PCDL_IS_LFN(ep)) ||
12510Sstevel@tonic-gate 	    (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) ||
12520Sstevel@tonic-gate 	    ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) {
12530Sstevel@tonic-gate 		bp->b_flags |= B_STALE | B_AGE;
12540Sstevel@tonic-gate 		brelse(bp);
12550Sstevel@tonic-gate 		pc_unlockfs(fsp);
12560Sstevel@tonic-gate 		return (EINVAL);
12570Sstevel@tonic-gate 	}
12580Sstevel@tonic-gate 	if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) &&
12590Sstevel@tonic-gate 	    (ep->pcd_filename[0] != PCD_ERASED) &&
12600Sstevel@tonic-gate 	    (pc_validchar(ep->pcd_filename[0]) ||
12615121Sfrankho 	    (ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) {
12620Sstevel@tonic-gate 		pcp = pc_getnode(fsp, eblkno, eoffset, ep);
12630Sstevel@tonic-gate 		pcp->pc_flags |= PC_EXTERNAL;
12640Sstevel@tonic-gate 		*vpp = PCTOV(pcp);
12650Sstevel@tonic-gate 	} else {
12660Sstevel@tonic-gate 		*vpp = NULL;
12670Sstevel@tonic-gate 	}
12680Sstevel@tonic-gate 	bp->b_flags |= B_STALE | B_AGE;
12690Sstevel@tonic-gate 	brelse(bp);
12700Sstevel@tonic-gate 	pc_unlockfs(fsp);
12710Sstevel@tonic-gate 	return (0);
12720Sstevel@tonic-gate }
12730Sstevel@tonic-gate 
12740Sstevel@tonic-gate /*
12750Sstevel@tonic-gate  * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about
12760Sstevel@tonic-gate  * a meg), so we can't bread() it all in at once. This routine reads a
12770Sstevel@tonic-gate  * fat a chunk at a time.
12780Sstevel@tonic-gate  */
12790Sstevel@tonic-gate static int
pc_readfat(struct pcfs * fsp,uchar_t * fatp)12805121Sfrankho pc_readfat(struct pcfs *fsp, uchar_t *fatp)
12810Sstevel@tonic-gate {
12820Sstevel@tonic-gate 	struct buf *bp;
12830Sstevel@tonic-gate 	size_t off;
12840Sstevel@tonic-gate 	size_t readsize;
12855121Sfrankho 	daddr_t diskblk;
12865121Sfrankho 	size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
12875121Sfrankho 	daddr_t start = fsp->pcfs_fatstart;
12880Sstevel@tonic-gate 
12890Sstevel@tonic-gate 	readsize = fsp->pcfs_clsize;
12900Sstevel@tonic-gate 	for (off = 0; off < fatsize; off += readsize, fatp += readsize) {
12910Sstevel@tonic-gate 		if (readsize > (fatsize - off))
12920Sstevel@tonic-gate 			readsize = fatsize - off;
12935121Sfrankho 		diskblk = pc_dbdaddr(fsp, start +
12945121Sfrankho 		    pc_cltodb(fsp, pc_lblkno(fsp, off)));
12955121Sfrankho 		bp = bread(fsp->pcfs_xdev, diskblk, readsize);
12960Sstevel@tonic-gate 		if (bp->b_flags & (B_ERROR | B_STALE)) {
12970Sstevel@tonic-gate 			brelse(bp);
12980Sstevel@tonic-gate 			return (EIO);
12990Sstevel@tonic-gate 		}
13000Sstevel@tonic-gate 		bp->b_flags |= B_STALE | B_AGE;
13010Sstevel@tonic-gate 		bcopy(bp->b_un.b_addr, fatp, readsize);
13020Sstevel@tonic-gate 		brelse(bp);
13030Sstevel@tonic-gate 	}
13040Sstevel@tonic-gate 	return (0);
13050Sstevel@tonic-gate }
13060Sstevel@tonic-gate 
13070Sstevel@tonic-gate /*
13080Sstevel@tonic-gate  * We write the FAT out a _lot_, in order to make sure that it
13090Sstevel@tonic-gate  * is up-to-date. But on a FAT32 system (large drive, small clusters)
13100Sstevel@tonic-gate  * the FAT might be a couple of megabytes, and writing it all out just
13110Sstevel@tonic-gate  * because we created or deleted a small file is painful (especially
13120Sstevel@tonic-gate  * since we do it for each alternate FAT too). So instead, for FAT16 and
13130Sstevel@tonic-gate  * FAT32 we only write out the bit that has changed. We don't clear
13140Sstevel@tonic-gate  * the 'updated' fields here because the caller might be writing out
13150Sstevel@tonic-gate  * several FATs, so the caller must use pc_clear_fatchanges() after
13160Sstevel@tonic-gate  * all FATs have been updated.
13175121Sfrankho  * This function doesn't take "start" from fsp->pcfs_dosstart because
13185121Sfrankho  * callers can use it to write either the primary or any of the alternate
13195121Sfrankho  * FAT tables.
13200Sstevel@tonic-gate  */
13210Sstevel@tonic-gate static int
pc_writefat(struct pcfs * fsp,daddr_t start)13220Sstevel@tonic-gate pc_writefat(struct pcfs *fsp, daddr_t start)
13230Sstevel@tonic-gate {
13240Sstevel@tonic-gate 	struct buf *bp;
13250Sstevel@tonic-gate 	size_t off;
13260Sstevel@tonic-gate 	size_t writesize;
13270Sstevel@tonic-gate 	int	error;
13280Sstevel@tonic-gate 	uchar_t *fatp = fsp->pcfs_fatp;
13295121Sfrankho 	size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
13300Sstevel@tonic-gate 
13310Sstevel@tonic-gate 	writesize = fsp->pcfs_clsize;
13320Sstevel@tonic-gate 	for (off = 0; off < fatsize; off += writesize, fatp += writesize) {
13330Sstevel@tonic-gate 		if (writesize > (fatsize - off))
13340Sstevel@tonic-gate 			writesize = fatsize - off;
13350Sstevel@tonic-gate 		if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) {
13360Sstevel@tonic-gate 			continue;
13370Sstevel@tonic-gate 		}
13380Sstevel@tonic-gate 		bp = ngeteblk(writesize);
13390Sstevel@tonic-gate 		bp->b_edev = fsp->pcfs_xdev;
13400Sstevel@tonic-gate 		bp->b_dev = cmpdev(bp->b_edev);
13410Sstevel@tonic-gate 		bp->b_blkno = pc_dbdaddr(fsp, start +
13420Sstevel@tonic-gate 		    pc_cltodb(fsp, pc_lblkno(fsp, off)));
13430Sstevel@tonic-gate 		bcopy(fatp, bp->b_un.b_addr, writesize);
13440Sstevel@tonic-gate 		bwrite2(bp);
13450Sstevel@tonic-gate 		error = geterror(bp);
13460Sstevel@tonic-gate 		brelse(bp);
13470Sstevel@tonic-gate 		if (error) {
13480Sstevel@tonic-gate 			return (error);
13490Sstevel@tonic-gate 		}
13500Sstevel@tonic-gate 	}
13510Sstevel@tonic-gate 	return (0);
13520Sstevel@tonic-gate }
13530Sstevel@tonic-gate 
13540Sstevel@tonic-gate /*
13550Sstevel@tonic-gate  * Mark the FAT cluster that 'cn' is stored in as modified.
13560Sstevel@tonic-gate  */
13570Sstevel@tonic-gate void
pc_mark_fat_updated(struct pcfs * fsp,pc_cluster32_t cn)13580Sstevel@tonic-gate pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn)
13590Sstevel@tonic-gate {
13600Sstevel@tonic-gate 	pc_cluster32_t	bn;
13610Sstevel@tonic-gate 	size_t		size;
13620Sstevel@tonic-gate 
13630Sstevel@tonic-gate 	/* which fat block is the cluster number stored in? */
13640Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
13650Sstevel@tonic-gate 		size = sizeof (pc_cluster32_t);
13660Sstevel@tonic-gate 		bn = pc_lblkno(fsp, cn * size);
13670Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
13680Sstevel@tonic-gate 	} else if (IS_FAT16(fsp)) {
13690Sstevel@tonic-gate 		size = sizeof (pc_cluster16_t);
13700Sstevel@tonic-gate 		bn = pc_lblkno(fsp, cn * size);
13710Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
13720Sstevel@tonic-gate 	} else {
13730Sstevel@tonic-gate 		offset_t off;
13740Sstevel@tonic-gate 		pc_cluster32_t nbn;
13750Sstevel@tonic-gate 
13760Sstevel@tonic-gate 		ASSERT(IS_FAT12(fsp));
13770Sstevel@tonic-gate 		off = cn + (cn >> 1);
13780Sstevel@tonic-gate 		bn = pc_lblkno(fsp, off);
13790Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
13800Sstevel@tonic-gate 		/* does this field wrap into the next fat cluster? */
13810Sstevel@tonic-gate 		nbn = pc_lblkno(fsp, off + 1);
13820Sstevel@tonic-gate 		if (nbn != bn) {
13830Sstevel@tonic-gate 			fsp->pcfs_fat_changemap[nbn] = 1;
13840Sstevel@tonic-gate 		}
13850Sstevel@tonic-gate 	}
13860Sstevel@tonic-gate }
13870Sstevel@tonic-gate 
13880Sstevel@tonic-gate /*
13890Sstevel@tonic-gate  * return whether the FAT cluster 'bn' is updated and needs to
13900Sstevel@tonic-gate  * be written out.
13910Sstevel@tonic-gate  */
13920Sstevel@tonic-gate int
pc_fat_is_changed(struct pcfs * fsp,pc_cluster32_t bn)13930Sstevel@tonic-gate pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn)
13940Sstevel@tonic-gate {
13950Sstevel@tonic-gate 	return (fsp->pcfs_fat_changemap[bn] == 1);
13960Sstevel@tonic-gate }
13972720Sfrankho 
13982720Sfrankho /*
13992720Sfrankho  * Implementation of VFS_FREEVFS() to support forced umounts.
14002720Sfrankho  * This is called by the vfs framework after umount, to trigger
14012720Sfrankho  * the release of any resources still associated with the given
14022720Sfrankho  * vfs_t once the need to keep them has gone away.
14032720Sfrankho  */
14042720Sfrankho void
pcfs_freevfs(vfs_t * vfsp)14052720Sfrankho pcfs_freevfs(vfs_t *vfsp)
14062720Sfrankho {
14072720Sfrankho 	struct pcfs *fsp = VFSTOPCFS(vfsp);
14082720Sfrankho 
14092720Sfrankho 	mutex_enter(&pcfslock);
14105121Sfrankho 	/*
14115121Sfrankho 	 * Purging the FAT closes the device - can't do any more
14125121Sfrankho 	 * I/O after this.
14135121Sfrankho 	 */
14142720Sfrankho 	if (fsp->pcfs_fatp != (uchar_t *)0)
14152720Sfrankho 		pc_invalfat(fsp);
14162720Sfrankho 	mutex_exit(&pcfslock);
14172720Sfrankho 
14182720Sfrankho 	VN_RELE(fsp->pcfs_devvp);
14192720Sfrankho 	mutex_destroy(&fsp->pcfs_lock);
14205121Sfrankho 	kmem_free(fsp, sizeof (*fsp));
14212720Sfrankho 
14222720Sfrankho 	/*
14232720Sfrankho 	 * Allow _fini() to succeed now, if so desired.
14242720Sfrankho 	 */
14252720Sfrankho 	atomic_dec_32(&pcfs_mountcount);
14262720Sfrankho }
14275121Sfrankho 
14285121Sfrankho 
14295121Sfrankho /*
14305121Sfrankho  * PC-style partition parsing and FAT BPB identification/validation code.
14315121Sfrankho  * The partition parsers here assume:
14325121Sfrankho  *	- a FAT filesystem will be in a partition that has one of a set of
14335121Sfrankho  *	  recognized partition IDs
14345121Sfrankho  *	- the user wants the 'numbering' (C:, D:, ...) that one would get
14355121Sfrankho  *	  on MSDOS 6.x.
14365121Sfrankho  *	  That means any non-FAT partition type (NTFS, HPFS, or any Linux fs)
14375121Sfrankho  *	  will not factor in the enumeration.
14385121Sfrankho  * These days, such assumptions should be revisited. FAT is no longer the
14395121Sfrankho  * only game in 'PC town'.
14405121Sfrankho  */
14415121Sfrankho /*
14425121Sfrankho  * isDosDrive()
14435121Sfrankho  *	Boolean function.  Give it the systid field for an fdisk partition
14445121Sfrankho  *	and it decides if that's a systid that describes a DOS drive.  We
14455121Sfrankho  *	use systid values defined in sys/dktp/fdisk.h.
14465121Sfrankho  */
14475121Sfrankho static int
isDosDrive(uchar_t checkMe)14485121Sfrankho isDosDrive(uchar_t checkMe)
14495121Sfrankho {
14505121Sfrankho 	return ((checkMe == DOSOS12) || (checkMe == DOSOS16) ||
14515121Sfrankho 	    (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) ||
14525121Sfrankho 	    (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) ||
14535121Sfrankho 	    (checkMe == DIAGPART));
14545121Sfrankho }
14555121Sfrankho 
14565121Sfrankho 
14575121Sfrankho /*
14585121Sfrankho  * isDosExtended()
14595121Sfrankho  *	Boolean function.  Give it the systid field for an fdisk partition
14605121Sfrankho  *	and it decides if that's a systid that describes an extended DOS
14615121Sfrankho  *	partition.
14625121Sfrankho  */
14635121Sfrankho static int
isDosExtended(uchar_t checkMe)14645121Sfrankho isDosExtended(uchar_t checkMe)
14655121Sfrankho {
14665121Sfrankho 	return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA));
14675121Sfrankho }
14685121Sfrankho 
14695121Sfrankho 
14705121Sfrankho /*
14715121Sfrankho  * isBootPart()
14725121Sfrankho  *	Boolean function.  Give it the systid field for an fdisk partition
14735121Sfrankho  *	and it decides if that's a systid that describes a Solaris boot
14745121Sfrankho  *	partition.
14755121Sfrankho  */
14765121Sfrankho static int
isBootPart(uchar_t checkMe)14775121Sfrankho isBootPart(uchar_t checkMe)
14785121Sfrankho {
14795121Sfrankho 	return (checkMe == X86BOOT);
14805121Sfrankho }
14815121Sfrankho 
14825121Sfrankho 
14835121Sfrankho /*
14845121Sfrankho  * noLogicalDrive()
14855121Sfrankho  *	Display error message about not being able to find a logical
14865121Sfrankho  *	drive.
14875121Sfrankho  */
14885121Sfrankho static void
noLogicalDrive(int ldrive)14895121Sfrankho noLogicalDrive(int ldrive)
14905121Sfrankho {
14915121Sfrankho 	if (ldrive == BOOT_PARTITION_DRIVE) {
14925121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: no boot partition");
14935121Sfrankho 	} else {
14945121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: %d: no such logical drive", ldrive);
14955121Sfrankho 	}
14965121Sfrankho }
14975121Sfrankho 
14985121Sfrankho 
14995121Sfrankho /*
15005121Sfrankho  * findTheDrive()
15015121Sfrankho  *	Discover offset of the requested logical drive, and return
15025121Sfrankho  *	that offset (startSector), the systid of that drive (sysid),
15035121Sfrankho  *	and a buffer pointer (bp), with the buffer contents being
15045121Sfrankho  *	the first sector of the logical drive (i.e., the sector that
15055121Sfrankho  *	contains the BPB for that drive).
15065121Sfrankho  *
15075121Sfrankho  * Note: this code is not capable of addressing >2TB disks, as it uses
15085121Sfrankho  *       daddr_t not diskaddr_t, some of the calculations would overflow
15095121Sfrankho  */
15105121Sfrankho #define	COPY_PTBL(mbr, ptblp)					\
15115121Sfrankho 	bcopy(&(((struct mboot *)(mbr))->parts), (ptblp),	\
15125121Sfrankho 	    FD_NUMPART * sizeof (struct ipart))
15135121Sfrankho 
15145121Sfrankho static int
findTheDrive(struct pcfs * fsp,buf_t ** bp)15155121Sfrankho findTheDrive(struct pcfs *fsp, buf_t **bp)
15165121Sfrankho {
15175121Sfrankho 	int ldrive = fsp->pcfs_ldrive;
15185121Sfrankho 	dev_t dev = fsp->pcfs_devvp->v_rdev;
15195121Sfrankho 
15205121Sfrankho 	struct ipart dosp[FD_NUMPART];	/* incore fdisk partition structure */
15215121Sfrankho 	daddr_t lastseek = 0;		/* Disk block we sought previously */
15225121Sfrankho 	daddr_t diskblk = 0;		/* Disk block to get */
15235121Sfrankho 	daddr_t xstartsect;		/* base of Extended DOS partition */
15245121Sfrankho 	int logicalDriveCount = 0;	/* Count of logical drives seen */
15255121Sfrankho 	int extendedPart = -1;		/* index of extended dos partition */
15265121Sfrankho 	int primaryPart = -1;		/* index of primary dos partition */
15275121Sfrankho 	int bootPart = -1;		/* index of a Solaris boot partition */
15287563SPrasad.Singamsetty@Sun.COM 	uint32_t xnumsect = 0;		/* length of extended DOS partition */
15295121Sfrankho 	int driveIndex;			/* computed FDISK table index */
15305121Sfrankho 	daddr_t startsec;
15315121Sfrankho 	len_t mediasize;
15325121Sfrankho 	int i;
15335121Sfrankho 	/*
15345121Sfrankho 	 * Count of drives in the current extended partition's
15355121Sfrankho 	 * FDISK table, and indexes of the drives themselves.
15365121Sfrankho 	 */
15375121Sfrankho 	int extndDrives[FD_NUMPART];
15385121Sfrankho 	int numDrives = 0;
15395121Sfrankho 
15405121Sfrankho 	/*
15415121Sfrankho 	 * Count of drives (beyond primary) in master boot record's
15425121Sfrankho 	 * FDISK table, and indexes of the drives themselves.
15435121Sfrankho 	 */
15445121Sfrankho 	int extraDrives[FD_NUMPART];
15455121Sfrankho 	int numExtraDrives = 0;
15465121Sfrankho 
15475121Sfrankho 	/*
15485121Sfrankho 	 * "ldrive == 0" should never happen, as this is a request to
15495121Sfrankho 	 * mount the physical device (and ignore partitioning). The code
15505121Sfrankho 	 * in pcfs_mount() should have made sure that a logical drive number
15515121Sfrankho 	 * is at least 1, meaning we're looking for drive "C:". It is not
15525121Sfrankho 	 * safe (and a bug in the callers of this function) to request logical
15535121Sfrankho 	 * drive number 0; we could ASSERT() but a graceful EIO is a more
15545121Sfrankho 	 * polite way.
15555121Sfrankho 	 */
15565121Sfrankho 	if (ldrive == 0) {
15575121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: request for logical partition zero");
15585121Sfrankho 		noLogicalDrive(ldrive);
15595121Sfrankho 		return (EIO);
15605121Sfrankho 	}
15615121Sfrankho 
15625121Sfrankho 	/*
15635121Sfrankho 	 *  Copy from disk block into memory aligned structure for fdisk usage.
15645121Sfrankho 	 */
15655121Sfrankho 	COPY_PTBL((*bp)->b_un.b_addr, dosp);
15665121Sfrankho 
15675121Sfrankho 	/*
15685121Sfrankho 	 * This check is ok because a FAT BPB and a master boot record (MBB)
15695121Sfrankho 	 * have the same signature, in the same position within the block.
15705121Sfrankho 	 */
15715121Sfrankho 	if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
15725121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: MBR partition table signature err, "
15735121Sfrankho 		    "device (%x.%x):%d\n",
15745121Sfrankho 		    getmajor(dev), getminor(dev), ldrive);
15755121Sfrankho 		return (EINVAL);
15765121Sfrankho 	}
15775121Sfrankho 
15785121Sfrankho 	/*
15795121Sfrankho 	 * Get a summary of what is in the Master FDISK table.
15805121Sfrankho 	 * Normally we expect to find one partition marked as a DOS drive.
15815121Sfrankho 	 * This partition is the one Windows calls the primary dos partition.
15825121Sfrankho 	 * If the machine has any logical drives then we also expect
15835121Sfrankho 	 * to find a partition marked as an extended DOS partition.
15845121Sfrankho 	 *
15855121Sfrankho 	 * Sometimes we'll find multiple partitions marked as DOS drives.
15865121Sfrankho 	 * The Solaris fdisk program allows these partitions
15875121Sfrankho 	 * to be created, but Windows fdisk no longer does.  We still need
15885121Sfrankho 	 * to support these, though, since Windows does.  We also need to fix
15895121Sfrankho 	 * our fdisk to behave like the Windows version.
15905121Sfrankho 	 *
15915121Sfrankho 	 * It turns out that some off-the-shelf media have *only* an
15925121Sfrankho 	 * Extended partition, so we need to deal with that case as well.
15935121Sfrankho 	 *
15945121Sfrankho 	 * Only a single (the first) Extended or Boot Partition will
15955121Sfrankho 	 * be recognized.  Any others will be ignored.
15965121Sfrankho 	 */
15975121Sfrankho 	for (i = 0; i < FD_NUMPART; i++) {
15985121Sfrankho 		DTRACE_PROBE4(primarypart, struct pcfs *, fsp,
15995121Sfrankho 		    uint_t, (uint_t)dosp[i].systid,
16005121Sfrankho 		    uint_t, LE_32(dosp[i].relsect),
16015121Sfrankho 		    uint_t, LE_32(dosp[i].numsect));
16025121Sfrankho 
16035121Sfrankho 		if (isDosDrive(dosp[i].systid)) {
16045121Sfrankho 			if (primaryPart < 0) {
16055121Sfrankho 				logicalDriveCount++;
16065121Sfrankho 				primaryPart = i;
16075121Sfrankho 			} else {
16085121Sfrankho 				extraDrives[numExtraDrives++] = i;
16095121Sfrankho 			}
16105121Sfrankho 			continue;
16115121Sfrankho 		}
16125121Sfrankho 		if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) {
16135121Sfrankho 			extendedPart = i;
16145121Sfrankho 			continue;
16155121Sfrankho 		}
16165121Sfrankho 		if ((bootPart < 0) && isBootPart(dosp[i].systid)) {
16175121Sfrankho 			bootPart = i;
16185121Sfrankho 			continue;
16195121Sfrankho 		}
16205121Sfrankho 	}
16215121Sfrankho 
16225121Sfrankho 	if (ldrive == BOOT_PARTITION_DRIVE) {
16235121Sfrankho 		if (bootPart < 0) {
16245121Sfrankho 			noLogicalDrive(ldrive);
16255121Sfrankho 			return (EINVAL);
16265121Sfrankho 		}
16275121Sfrankho 		startsec = LE_32(dosp[bootPart].relsect);
16285121Sfrankho 		mediasize = LE_32(dosp[bootPart].numsect);
16295121Sfrankho 		goto found;
16305121Sfrankho 	}
16315121Sfrankho 
16325121Sfrankho 	if (ldrive == PRIMARY_DOS_DRIVE && primaryPart >= 0) {
16335121Sfrankho 		startsec = LE_32(dosp[primaryPart].relsect);
16345121Sfrankho 		mediasize = LE_32(dosp[primaryPart].numsect);
16355121Sfrankho 		goto found;
16365121Sfrankho 	}
16375121Sfrankho 
16385121Sfrankho 	/*
16395121Sfrankho 	 * We are not looking for the C: drive (or the primary drive
16405121Sfrankho 	 * was not found), so we had better have an extended partition
16415121Sfrankho 	 * or extra drives in the Master FDISK table.
16425121Sfrankho 	 */
16435121Sfrankho 	if ((extendedPart < 0) && (numExtraDrives == 0)) {
16445121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: no extended dos partition");
16455121Sfrankho 		noLogicalDrive(ldrive);
16465121Sfrankho 		return (EINVAL);
16475121Sfrankho 	}
16485121Sfrankho 
16495121Sfrankho 	if (extendedPart >= 0) {
16505121Sfrankho 		diskblk = xstartsect = LE_32(dosp[extendedPart].relsect);
16515121Sfrankho 		xnumsect = LE_32(dosp[extendedPart].numsect);
16525121Sfrankho 		do {
16535121Sfrankho 			/*
16545121Sfrankho 			 *  If the seek would not cause us to change
16555121Sfrankho 			 *  position on the drive, then we're out of
16565121Sfrankho 			 *  extended partitions to examine.
16575121Sfrankho 			 */
16585121Sfrankho 			if (diskblk == lastseek)
16595121Sfrankho 				break;
16605121Sfrankho 			logicalDriveCount += numDrives;
16615121Sfrankho 			/*
16625121Sfrankho 			 *  Seek the next extended partition, and find
16635121Sfrankho 			 *  logical drives within it.
16645121Sfrankho 			 */
16655121Sfrankho 			brelse(*bp);
16665121Sfrankho 			/*
16675121Sfrankho 			 * bread() block numbers are multiples of DEV_BSIZE
16685121Sfrankho 			 * but the device sector size (the unit of partitioning)
16695121Sfrankho 			 * might be larger than that; pcfs_get_device_info()
16705121Sfrankho 			 * has calculated the multiplicator for us.
16715121Sfrankho 			 */
16725121Sfrankho 			*bp = bread(dev,
16735121Sfrankho 			    pc_dbdaddr(fsp, diskblk), fsp->pcfs_secsize);
16745121Sfrankho 			if ((*bp)->b_flags & B_ERROR) {
16755121Sfrankho 				return (EIO);
16765121Sfrankho 			}
16775121Sfrankho 
16785121Sfrankho 			lastseek = diskblk;
16795121Sfrankho 			COPY_PTBL((*bp)->b_un.b_addr, dosp);
16805121Sfrankho 			if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
16815121Sfrankho 				cmn_err(CE_NOTE, "!pcfs: "
16825121Sfrankho 				    "extended partition table signature err, "
16835121Sfrankho 				    "device (%x.%x):%d, LBA %u",
16845121Sfrankho 				    getmajor(dev), getminor(dev), ldrive,
16855121Sfrankho 				    (uint_t)pc_dbdaddr(fsp, diskblk));
16865121Sfrankho 				return (EINVAL);
16875121Sfrankho 			}
16885121Sfrankho 			/*
16895121Sfrankho 			 *  Count up drives, and track where the next
16905121Sfrankho 			 *  extended partition is in case we need it.  We
16915121Sfrankho 			 *  are expecting only one extended partition.  If
16925121Sfrankho 			 *  there is more than one we'll only go to the
16935121Sfrankho 			 *  first one we see, but warn about ignoring.
16945121Sfrankho 			 */
16955121Sfrankho 			numDrives = 0;
16965121Sfrankho 			for (i = 0; i < FD_NUMPART; i++) {
16975121Sfrankho 				DTRACE_PROBE4(extendedpart,
16985121Sfrankho 				    struct pcfs *, fsp,
16995121Sfrankho 				    uint_t, (uint_t)dosp[i].systid,
17005121Sfrankho 				    uint_t, LE_32(dosp[i].relsect),
17015121Sfrankho 				    uint_t, LE_32(dosp[i].numsect));
17025121Sfrankho 				if (isDosDrive(dosp[i].systid)) {
17035121Sfrankho 					extndDrives[numDrives++] = i;
17045121Sfrankho 				} else if (isDosExtended(dosp[i].systid)) {
17055121Sfrankho 					if (diskblk != lastseek) {
17065121Sfrankho 						/*
17075121Sfrankho 						 * Already found an extended
17085121Sfrankho 						 * partition in this table.
17095121Sfrankho 						 */
17105121Sfrankho 						cmn_err(CE_NOTE,
17115121Sfrankho 						    "!pcfs: ignoring unexpected"
17125121Sfrankho 						    " additional extended"
17135121Sfrankho 						    " partition");
17145121Sfrankho 					} else {
17155121Sfrankho 						diskblk = xstartsect +
17165121Sfrankho 						    LE_32(dosp[i].relsect);
17175121Sfrankho 					}
17185121Sfrankho 				}
17195121Sfrankho 			}
17205121Sfrankho 		} while (ldrive > logicalDriveCount + numDrives);
17215121Sfrankho 
17225121Sfrankho 		ASSERT(numDrives <= FD_NUMPART);
17235121Sfrankho 
17245121Sfrankho 		if (ldrive <= logicalDriveCount + numDrives) {
17255121Sfrankho 			/*
17265121Sfrankho 			 * The number of logical drives we've found thus
17275121Sfrankho 			 * far is enough to get us to the one we were
17285121Sfrankho 			 * searching for.
17295121Sfrankho 			 */
17305121Sfrankho 			driveIndex = logicalDriveCount + numDrives - ldrive;
17315121Sfrankho 			mediasize =
17325121Sfrankho 			    LE_32(dosp[extndDrives[driveIndex]].numsect);
17335121Sfrankho 			startsec =
17345121Sfrankho 			    LE_32(dosp[extndDrives[driveIndex]].relsect) +
17355121Sfrankho 			    lastseek;
17365121Sfrankho 			if (startsec > (xstartsect + xnumsect)) {
17375121Sfrankho 				cmn_err(CE_NOTE, "!pcfs: extended partition "
17385121Sfrankho 				    "values bad");
17395121Sfrankho 				return (EINVAL);
17405121Sfrankho 			}
17415121Sfrankho 			goto found;
17425121Sfrankho 		} else {
17435121Sfrankho 			/*
17445121Sfrankho 			 * We ran out of extended dos partition
17455121Sfrankho 			 * drives.  The only hope now is to go
17465121Sfrankho 			 * back to extra drives defined in the master
17475121Sfrankho 			 * fdisk table.  But we overwrote that table
17485121Sfrankho 			 * already, so we must load it in again.
17495121Sfrankho 			 */
17505121Sfrankho 			logicalDriveCount += numDrives;
17515121Sfrankho 			brelse(*bp);
17525121Sfrankho 			ASSERT(fsp->pcfs_dosstart == 0);
17535121Sfrankho 			*bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
17545121Sfrankho 			    fsp->pcfs_secsize);
17555121Sfrankho 			if ((*bp)->b_flags & B_ERROR) {
17565121Sfrankho 				return (EIO);
17575121Sfrankho 			}
17585121Sfrankho 			COPY_PTBL((*bp)->b_un.b_addr, dosp);
17595121Sfrankho 		}
17605121Sfrankho 	}
17615121Sfrankho 	/*
17625121Sfrankho 	 *  Still haven't found the drive, is it an extra
17635121Sfrankho 	 *  drive defined in the main FDISK table?
17645121Sfrankho 	 */
17655121Sfrankho 	if (ldrive <= logicalDriveCount + numExtraDrives) {
17665121Sfrankho 		driveIndex = logicalDriveCount + numExtraDrives - ldrive;
17675121Sfrankho 		ASSERT(driveIndex < MIN(numExtraDrives, FD_NUMPART));
17685121Sfrankho 		mediasize = LE_32(dosp[extraDrives[driveIndex]].numsect);
17695121Sfrankho 		startsec = LE_32(dosp[extraDrives[driveIndex]].relsect);
17705121Sfrankho 		goto found;
17715121Sfrankho 	}
17725121Sfrankho 	/*
17735121Sfrankho 	 *  Still haven't found the drive, and there is
17745121Sfrankho 	 *  nowhere else to look.
17755121Sfrankho 	 */
17765121Sfrankho 	noLogicalDrive(ldrive);
17775121Sfrankho 	return (EINVAL);
17785121Sfrankho 
17795121Sfrankho found:
17805121Sfrankho 	/*
17815121Sfrankho 	 * We need this value in units of sectorsize, because PCFS' internal
17825121Sfrankho 	 * offset calculations go haywire for > 512Byte sectors unless all
17835121Sfrankho 	 * pcfs_.*start values are in units of sectors.
17845121Sfrankho 	 * So, assign before the capacity check (that's done in DEV_BSIZE)
17855121Sfrankho 	 */
17865121Sfrankho 	fsp->pcfs_dosstart = startsec;
17875121Sfrankho 
17885121Sfrankho 	/*
17895121Sfrankho 	 * convert from device sectors to proper units:
17905121Sfrankho 	 *	- starting sector: DEV_BSIZE (as argument to bread())
17915121Sfrankho 	 *	- media size: Bytes
17925121Sfrankho 	 */
17935121Sfrankho 	startsec = pc_dbdaddr(fsp, startsec);
17945121Sfrankho 	mediasize *= fsp->pcfs_secsize;
17955121Sfrankho 
17965121Sfrankho 	/*
17975121Sfrankho 	 * some additional validation / warnings in case the partition table
17985121Sfrankho 	 * and the actual media capacity are not in accordance ...
17995121Sfrankho 	 */
18005121Sfrankho 	if (fsp->pcfs_mediasize != 0) {
18015121Sfrankho 		diskaddr_t startoff =
18025121Sfrankho 		    (diskaddr_t)startsec * (diskaddr_t)DEV_BSIZE;
18035121Sfrankho 
18045121Sfrankho 		if (startoff >= fsp->pcfs_mediasize ||
18055121Sfrankho 		    startoff + mediasize > fsp->pcfs_mediasize) {
18065121Sfrankho 			cmn_err(CE_WARN,
18075121Sfrankho 			    "!pcfs: partition size (LBA start %u, %lld bytes, "
18085121Sfrankho 			    "device (%x.%x):%d) smaller than "
18095121Sfrankho 			    "mediasize (%lld bytes).\n"
18105121Sfrankho 			    "filesystem may be truncated, access errors "
18115121Sfrankho 			    "may result.\n",
18125121Sfrankho 			    (uint_t)startsec, (long long)mediasize,
18135121Sfrankho 			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
18145121Sfrankho 			    fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
18155121Sfrankho 		}
18165121Sfrankho 	} else {
18175121Sfrankho 		fsp->pcfs_mediasize = mediasize;
18185121Sfrankho 	}
18195121Sfrankho 
18205121Sfrankho 	return (0);
18215121Sfrankho }
18225121Sfrankho 
18235121Sfrankho 
18245121Sfrankho static fattype_t
secondaryBPBChecks(struct pcfs * fsp,uchar_t * bpb,size_t secsize)18255121Sfrankho secondaryBPBChecks(struct pcfs *fsp, uchar_t *bpb, size_t secsize)
18265121Sfrankho {
18275121Sfrankho 	uint32_t ncl = fsp->pcfs_ncluster;
18285121Sfrankho 
18295121Sfrankho 	if (ncl <= 4096) {
18305121Sfrankho 		if (bpb_get_FatSz16(bpb) == 0)
18315121Sfrankho 			return (FAT_UNKNOWN);
18325121Sfrankho 
18335121Sfrankho 		if (bpb_get_FatSz16(bpb) * secsize < ncl * 2 &&
18345121Sfrankho 		    bpb_get_FatSz16(bpb) * secsize >= (3 * ncl / 2))
18355121Sfrankho 			return (FAT12);
18365121Sfrankho 		if (bcmp(bpb_FilSysType16(bpb), "FAT12", 5) == 0)
18375121Sfrankho 			return (FAT12);
18385121Sfrankho 		if (bcmp(bpb_FilSysType16(bpb), "FAT16", 5) == 0)
18395121Sfrankho 			return (FAT16);
18405121Sfrankho 
18415121Sfrankho 		switch (bpb_get_Media(bpb)) {
18425121Sfrankho 			case SS8SPT:
18435121Sfrankho 			case DS8SPT:
18445121Sfrankho 			case SS9SPT:
18455121Sfrankho 			case DS9SPT:
18465121Sfrankho 			case DS18SPT:
18475121Sfrankho 			case DS9_15SPT:
18485121Sfrankho 				/*
18495121Sfrankho 				 * Is this reliable - all floppies are FAT12 ?
18505121Sfrankho 				 */
18515121Sfrankho 				return (FAT12);
18525121Sfrankho 			case MD_FIXED:
18535121Sfrankho 				/*
18545121Sfrankho 				 * Is this reliable - disks are always FAT16 ?
18555121Sfrankho 				 */
18565121Sfrankho 				return (FAT16);
18575121Sfrankho 			default:
18585121Sfrankho 				break;
18595121Sfrankho 		}
18605121Sfrankho 	} else if (ncl <= 65536) {
18615121Sfrankho 		if (bpb_get_FatSz16(bpb) == 0 && bpb_get_FatSz32(bpb) > 0)
18625121Sfrankho 			return (FAT32);
18635121Sfrankho 		if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
18645121Sfrankho 			return (FAT32);
18655121Sfrankho 		if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
18665121Sfrankho 			return (FAT32);
18675121Sfrankho 
18685121Sfrankho 		if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
18695121Sfrankho 			return (FAT16);
18705121Sfrankho 		if (bpb_get_FatSz16(bpb) * secsize < ncl * 4)
18715121Sfrankho 			return (FAT16);
18725121Sfrankho 	}
18735121Sfrankho 
18745121Sfrankho 	/*
18755121Sfrankho 	 * We don't know
18765121Sfrankho 	 */
18775121Sfrankho 	return (FAT_UNKNOWN);
18785121Sfrankho }
18795121Sfrankho 
18805121Sfrankho /*
18815121Sfrankho  * Check to see if the BPB we found is correct.
18825121Sfrankho  *
18835121Sfrankho  * This looks far more complicated that it needs to be for pure structural
18845121Sfrankho  * validation. The reason for this is that parseBPB() is also used for
18855121Sfrankho  * debugging purposes (mdb dcmd) and we therefore want a bitmap of which
18866644Sgd78059  * BPB fields (do not) have 'known good' values, even if we (do not) reject
18876644Sgd78059  * the BPB when attempting to mount the filesystem.
18886644Sgd78059  *
18896644Sgd78059  * Real-world usage of FAT shows there are a lot of corner-case situations
18906644Sgd78059  * and, following the specification strictly, invalid filesystems out there.
18916644Sgd78059  * Known are situations such as:
18926644Sgd78059  *	- FAT12/FAT16 filesystems with garbage in either totsec16/32
18936644Sgd78059  *	  instead of the zero in one of the fields mandated by the spec
18946644Sgd78059  *	- filesystems that claim to be larger than the partition they're in
18956644Sgd78059  *	- filesystems without valid media descriptor
18966644Sgd78059  *	- FAT32 filesystems with RootEntCnt != 0
18976644Sgd78059  *	- FAT32 filesystems with less than 65526 clusters
18986644Sgd78059  *	- FAT32 filesystems without valid FSI sector
18996644Sgd78059  *	- FAT32 filesystems with FAT size in fatsec16 instead of fatsec32
19006644Sgd78059  *
19016644Sgd78059  * Such filesystems are accessible by PCFS - if it'd know to start with that
19026644Sgd78059  * the filesystem should be treated as a specific FAT type. Before S10, it
19036644Sgd78059  * relied on the PC/fdisk partition type for the purpose and almost completely
19046644Sgd78059  * ignored the BPB; now it ignores the partition type for anything else but
19056644Sgd78059  * logical drive enumeration, which can result in rejection of (invalid)
19066644Sgd78059  * FAT32 - if the partition ID says FAT32, but the filesystem, for example
19076644Sgd78059  * has less than 65526 clusters.
19086644Sgd78059  *
19096644Sgd78059  * Without a "force this fs as FAT{12,16,32}" tunable or mount option, it's
19106644Sgd78059  * not possible to allow all such mostly-compliant filesystems in unless one
19116644Sgd78059  * accepts false positives (definitely invalid filesystems that cause problems
19126644Sgd78059  * later). This at least allows to pinpoint why the mount failed.
19136644Sgd78059  *
19146644Sgd78059  * Due to the use of FAT on removeable media, all relaxations of the rules
19156644Sgd78059  * here need to be carefully evaluated wrt. to potential effects on PCFS
19166644Sgd78059  * resilience. A faulty/"mis-crafted" filesystem must not cause a panic, so
19176644Sgd78059  * beware.
19185121Sfrankho  */
19195121Sfrankho static int
parseBPB(struct pcfs * fsp,uchar_t * bpb,int * valid)19205121Sfrankho parseBPB(struct pcfs *fsp, uchar_t *bpb, int *valid)
19215121Sfrankho {
19225121Sfrankho 	fattype_t type;
19235121Sfrankho 
19245121Sfrankho 	uint32_t	ncl;	/* number of clusters in file area */
19255121Sfrankho 	uint32_t	rec;
19265121Sfrankho 	uint32_t	reserved;
19275121Sfrankho 	uint32_t	fsisec, bkbootsec;
19285121Sfrankho 	blkcnt_t	totsec, totsec16, totsec32, datasec;
19295121Sfrankho 	size_t		fatsec, fatsec16, fatsec32, rdirsec;
19305121Sfrankho 	size_t		secsize;
19315121Sfrankho 	len_t		mediasize;
19325121Sfrankho 	uint64_t	validflags = 0;
19335121Sfrankho 
19345121Sfrankho 	if (VALID_BPBSIG(bpb_get_BPBSig(bpb)))
19355121Sfrankho 		validflags |= BPB_BPBSIG_OK;
19365121Sfrankho 
19375121Sfrankho 	rec = bpb_get_RootEntCnt(bpb);
19385121Sfrankho 	reserved = bpb_get_RsvdSecCnt(bpb);
19395121Sfrankho 	fsisec = bpb_get_FSInfo32(bpb);
19405121Sfrankho 	bkbootsec = bpb_get_BkBootSec32(bpb);
19415121Sfrankho 	totsec16 = (blkcnt_t)bpb_get_TotSec16(bpb);
19425121Sfrankho 	totsec32 = (blkcnt_t)bpb_get_TotSec32(bpb);
19435121Sfrankho 	fatsec16 = bpb_get_FatSz16(bpb);
19445121Sfrankho 	fatsec32 = bpb_get_FatSz32(bpb);
19455121Sfrankho 
19465121Sfrankho 	totsec = totsec16 ? totsec16 : totsec32;
19475121Sfrankho 	fatsec = fatsec16 ? fatsec16 : fatsec32;
19485121Sfrankho 
19495121Sfrankho 	secsize = bpb_get_BytesPerSec(bpb);
19505121Sfrankho 	if (!VALID_SECSIZE(secsize))
19515121Sfrankho 		secsize = fsp->pcfs_secsize;
19525121Sfrankho 	if (secsize != fsp->pcfs_secsize) {
19535121Sfrankho 		PC_DPRINTF3(3, "!pcfs: parseBPB, device (%x.%x):%d:\n",
19545121Sfrankho 		    getmajor(fsp->pcfs_xdev),
19555121Sfrankho 		    getminor(fsp->pcfs_xdev), fsp->pcfs_ldrive);
19565121Sfrankho 		PC_DPRINTF2(3, "!BPB secsize %d != "
19575121Sfrankho 		    "autodetected media block size %d\n",
19585121Sfrankho 		    (int)secsize, (int)fsp->pcfs_secsize);
19595121Sfrankho 		if (fsp->pcfs_ldrive) {
19605121Sfrankho 			/*
19615121Sfrankho 			 * We've already attempted to parse the partition
19625121Sfrankho 			 * table. If the block size used for that don't match
19635121Sfrankho 			 * the PCFS sector size, we're hosed one way or the
19645121Sfrankho 			 * other. Just try what happens.
19655121Sfrankho 			 */
19665121Sfrankho 			secsize = fsp->pcfs_secsize;
19675121Sfrankho 			PC_DPRINTF1(3,
19685121Sfrankho 			    "!pcfs: Using autodetected secsize %d\n",
19695121Sfrankho 			    (int)secsize);
19705121Sfrankho 		} else {
19715121Sfrankho 			/*
19725121Sfrankho 			 * This allows mounting lofi images of PCFS partitions
19735121Sfrankho 			 * with sectorsize != DEV_BSIZE. We can't parse the
19745121Sfrankho 			 * partition table on whole-disk images unless the
19755121Sfrankho 			 * (undocumented) "secsize=..." mount option is used,
19765121Sfrankho 			 * but at least this allows us to mount if we have
19775121Sfrankho 			 * an image of a partition.
19785121Sfrankho 			 */
19795121Sfrankho 			PC_DPRINTF1(3,
19805121Sfrankho 			    "!pcfs: Using BPB secsize %d\n", (int)secsize);
19815121Sfrankho 		}
19825121Sfrankho 	}
19835121Sfrankho 
19845121Sfrankho 	if (fsp->pcfs_mediasize == 0) {
19855121Sfrankho 		mediasize = (len_t)totsec * (len_t)secsize;
19866644Sgd78059 		/*
19876644Sgd78059 		 * This is not an error because not all devices support the
19886644Sgd78059 		 * dkio(7i) mediasize queries, and/or not all devices are
19896644Sgd78059 		 * partitioned. If we have not been able to figure out the
19906644Sgd78059 		 * size of the underlaying medium, we have to trust the BPB.
19916644Sgd78059 		 */
19925121Sfrankho 		PC_DPRINTF4(3, "!pcfs: parseBPB: mediasize autodetect failed "
19935121Sfrankho 		    "on device (%x.%x):%d, trusting BPB totsec (%lld Bytes)\n",
19945121Sfrankho 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
19955121Sfrankho 		    fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
19965121Sfrankho 	} else if ((len_t)totsec * (len_t)secsize > fsp->pcfs_mediasize) {
19975121Sfrankho 		cmn_err(CE_WARN,
19985121Sfrankho 		    "!pcfs: autodetected mediasize (%lld Bytes) smaller than "
19995121Sfrankho 		    "FAT BPB mediasize (%lld Bytes).\n"
20005121Sfrankho 		    "truncated filesystem on device (%x.%x):%d, access errors "
20015121Sfrankho 		    "possible.\n",
20025121Sfrankho 		    (long long)fsp->pcfs_mediasize,
20035121Sfrankho 		    (long long)(totsec * (blkcnt_t)secsize),
20045121Sfrankho 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
20055121Sfrankho 		    fsp->pcfs_ldrive);
20065121Sfrankho 		mediasize = fsp->pcfs_mediasize;
20075121Sfrankho 	} else {
20085121Sfrankho 		/*
20095121Sfrankho 		 * This is actually ok. A FAT needs not occupy the maximum
20105121Sfrankho 		 * space available in its partition, it can be shorter.
20115121Sfrankho 		 */
20125121Sfrankho 		mediasize = (len_t)totsec * (len_t)secsize;
20135121Sfrankho 	}
20145121Sfrankho 
20155121Sfrankho 	/*
20165121Sfrankho 	 * Since we let just about anything pass through this function,
20175121Sfrankho 	 * fence against divide-by-zero here.
20185121Sfrankho 	 */
20195121Sfrankho 	if (secsize)
20205121Sfrankho 		rdirsec = roundup(rec * 32, secsize) / secsize;
20215121Sfrankho 	else
20225121Sfrankho 		rdirsec = 0;
20235121Sfrankho 
20245121Sfrankho 	/*
20255121Sfrankho 	 * This assignment is necessary before pc_dbdaddr() can first be
20265121Sfrankho 	 * used. Must initialize the value here.
20275121Sfrankho 	 */
20285121Sfrankho 	fsp->pcfs_secsize = secsize;
20295121Sfrankho 	fsp->pcfs_sdshift = ddi_ffs(secsize / DEV_BSIZE) - 1;
20305121Sfrankho 
20315121Sfrankho 	fsp->pcfs_mediasize = mediasize;
20325121Sfrankho 
20335121Sfrankho 	fsp->pcfs_spcl = bpb_get_SecPerClus(bpb);
20345121Sfrankho 	fsp->pcfs_numfat = bpb_get_NumFATs(bpb);
20355121Sfrankho 	fsp->pcfs_mediadesc = bpb_get_Media(bpb);
20365121Sfrankho 	fsp->pcfs_clsize = secsize * fsp->pcfs_spcl;
20375121Sfrankho 	fsp->pcfs_rdirsec = rdirsec;
20385121Sfrankho 
20395121Sfrankho 	/*
20405121Sfrankho 	 * Remember: All PCFS offset calculations in sectors. Before I/O
20415121Sfrankho 	 * is done, convert to DEV_BSIZE units via pc_dbdaddr(). This is
20425121Sfrankho 	 * necessary so that media with > 512Byte sector sizes work correctly.
20435121Sfrankho 	 */
20445121Sfrankho 	fsp->pcfs_fatstart = fsp->pcfs_dosstart + reserved;
20455121Sfrankho 	fsp->pcfs_rdirstart = fsp->pcfs_fatstart + fsp->pcfs_numfat * fatsec;
20465121Sfrankho 	fsp->pcfs_datastart = fsp->pcfs_rdirstart + rdirsec;
20475121Sfrankho 	datasec = totsec -
20485121Sfrankho 	    (blkcnt_t)fatsec * fsp->pcfs_numfat -
20495121Sfrankho 	    (blkcnt_t)rdirsec -
20505121Sfrankho 	    (blkcnt_t)reserved;
20515121Sfrankho 
20525121Sfrankho 	DTRACE_PROBE4(fatgeometry,
20535121Sfrankho 	    blkcnt_t, totsec, size_t, fatsec,
20545121Sfrankho 	    size_t, rdirsec, blkcnt_t, datasec);
20555121Sfrankho 
20565121Sfrankho 	/*
20576644Sgd78059 	 * 'totsec' is taken directly from the BPB and guaranteed to fit
20586644Sgd78059 	 * into a 32bit unsigned integer. The calculation of 'datasec',
20596644Sgd78059 	 * on the other hand, could underflow for incorrect values in
20606644Sgd78059 	 * rdirsec/reserved/fatsec. Check for that.
20616644Sgd78059 	 * We also check that the BPB conforms to the FAT specification's
20626644Sgd78059 	 * requirement that either of the 16/32bit total sector counts
20636644Sgd78059 	 * must be zero.
20645121Sfrankho 	 */
20655121Sfrankho 	if (totsec != 0 &&
20665121Sfrankho 	    (totsec16 == totsec32 || totsec16 == 0 || totsec32 == 0) &&
20675121Sfrankho 	    datasec < totsec && datasec <= UINT32_MAX)
20685121Sfrankho 		validflags |= BPB_TOTSEC_OK;
20695121Sfrankho 
20706644Sgd78059 	if ((len_t)totsec * (len_t)secsize <= mediasize)
20715121Sfrankho 		validflags |= BPB_MEDIASZ_OK;
20725121Sfrankho 
20735121Sfrankho 	if (VALID_SECSIZE(secsize))
20745121Sfrankho 		validflags |= BPB_SECSIZE_OK;
20755121Sfrankho 	if (VALID_SPCL(fsp->pcfs_spcl))
20765121Sfrankho 		validflags |= BPB_SECPERCLUS_OK;
20775121Sfrankho 	if (VALID_CLSIZE(fsp->pcfs_clsize))
20785121Sfrankho 		validflags |= BPB_CLSIZE_OK;
20795121Sfrankho 	if (VALID_NUMFATS(fsp->pcfs_numfat))
20805121Sfrankho 		validflags |= BPB_NUMFAT_OK;
20815121Sfrankho 	if (VALID_RSVDSEC(reserved) && reserved < totsec)
20825121Sfrankho 		validflags |= BPB_RSVDSECCNT_OK;
20835121Sfrankho 	if (VALID_MEDIA(fsp->pcfs_mediadesc))
20845121Sfrankho 		validflags |= BPB_MEDIADESC_OK;
20855121Sfrankho 	if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
20865121Sfrankho 		validflags |= BPB_BOOTSIG16_OK;
20875121Sfrankho 	if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
20885121Sfrankho 		validflags |= BPB_BOOTSIG32_OK;
20895121Sfrankho 	if (VALID_FSTYPSTR16(bpb_FilSysType16(bpb)))
20905121Sfrankho 		validflags |= BPB_FSTYPSTR16_OK;
20915121Sfrankho 	if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
20925121Sfrankho 		validflags |= BPB_FSTYPSTR32_OK;
20935121Sfrankho 	if (VALID_OEMNAME(bpb_OEMName(bpb)))
20945121Sfrankho 		validflags |= BPB_OEMNAME_OK;
20955121Sfrankho 	if (bkbootsec > 0 && bkbootsec <= reserved && fsisec != bkbootsec)
20965121Sfrankho 		validflags |= BPB_BKBOOTSEC_OK;
20975121Sfrankho 	if (fsisec > 0 && fsisec <= reserved)
20985121Sfrankho 		validflags |= BPB_FSISEC_OK;
20995121Sfrankho 	if (VALID_JMPBOOT(bpb_jmpBoot(bpb)))
21005121Sfrankho 		validflags |= BPB_JMPBOOT_OK;
21015121Sfrankho 	if (VALID_FSVER32(bpb_get_FSVer32(bpb)))
21025121Sfrankho 		validflags |= BPB_FSVER_OK;
21035121Sfrankho 	if (VALID_VOLLAB(bpb_VolLab16(bpb)))
21045121Sfrankho 		validflags |= BPB_VOLLAB16_OK;
21055121Sfrankho 	if (VALID_VOLLAB(bpb_VolLab32(bpb)))
21065121Sfrankho 		validflags |= BPB_VOLLAB32_OK;
21075121Sfrankho 	if (VALID_EXTFLAGS(bpb_get_ExtFlags32(bpb)))
21085121Sfrankho 		validflags |= BPB_EXTFLAGS_OK;
21095121Sfrankho 
21105121Sfrankho 	/*
21115121Sfrankho 	 * Try to determine which FAT format to use.
21125121Sfrankho 	 *
21135121Sfrankho 	 * Calculate the number of clusters in order to determine
21145121Sfrankho 	 * the type of FAT we are looking at.  This is the only
21155121Sfrankho 	 * recommended way of determining FAT type, though there
21165121Sfrankho 	 * are other hints in the data, this is the best way.
21175121Sfrankho 	 *
21185121Sfrankho 	 * Since we let just about "anything" pass through this function
21195121Sfrankho 	 * without early exits, fence against divide-by-zero here.
21205121Sfrankho 	 *
21215121Sfrankho 	 * datasec was already validated against UINT32_MAX so we know
21225121Sfrankho 	 * the result will not overflow the 32bit calculation.
21235121Sfrankho 	 */
21245121Sfrankho 	if (fsp->pcfs_spcl)
21255121Sfrankho 		ncl = (uint32_t)datasec / fsp->pcfs_spcl;
21265121Sfrankho 	else
21275121Sfrankho 		ncl = 0;
21285121Sfrankho 
21295121Sfrankho 	fsp->pcfs_ncluster = ncl;
21305121Sfrankho 
21315121Sfrankho 	/*
21325121Sfrankho 	 * From the Microsoft FAT specification:
21335121Sfrankho 	 * In the following example, when it says <, it does not mean <=.
21345121Sfrankho 	 * Note also that the numbers are correct.  The first number for
21355121Sfrankho 	 * FAT12 is 4085; the second number for FAT16 is 65525. These numbers
21365121Sfrankho 	 * and the '<' signs are not wrong.
21375121Sfrankho 	 *
21385121Sfrankho 	 * We "specialdetect" the corner cases, and use at least one "extra"
21395121Sfrankho 	 * criterion to decide whether it's FAT16 or FAT32 if the cluster
21405121Sfrankho 	 * count is dangerously close to the boundaries.
21415121Sfrankho 	 */
21425121Sfrankho 
21435121Sfrankho 	if (ncl <= PCF_FIRSTCLUSTER) {
21445121Sfrankho 		type = FAT_UNKNOWN;
21455121Sfrankho 	} else if (ncl < 4085) {
21465121Sfrankho 		type = FAT12;
21475121Sfrankho 	} else if (ncl <= 4096) {
21485121Sfrankho 		type = FAT_QUESTIONABLE;
21495121Sfrankho 	} else if (ncl < 65525) {
21505121Sfrankho 		type = FAT16;
21515121Sfrankho 	} else if (ncl <= 65536) {
21525121Sfrankho 		type = FAT_QUESTIONABLE;
21535121Sfrankho 	} else if (ncl < PCF_LASTCLUSTER32) {
21545121Sfrankho 		type = FAT32;
21555121Sfrankho 	} else {
21565121Sfrankho 		type = FAT_UNKNOWN;
21575121Sfrankho 	}
21585121Sfrankho 
21595121Sfrankho 	DTRACE_PROBE4(parseBPB__initial,
21605121Sfrankho 	    struct pcfs *, fsp, unsigned char *, bpb,
21615121Sfrankho 	    int, validflags, fattype_t, type);
21625121Sfrankho 
21635121Sfrankho recheck:
21645121Sfrankho 	fsp->pcfs_fatsec = fatsec;
21655121Sfrankho 
21665121Sfrankho 	/* Do some final sanity checks for each specific type of FAT */
21675121Sfrankho 	switch (type) {
21685121Sfrankho 		case FAT12:
21695121Sfrankho 			if (rec != 0)
21705121Sfrankho 				validflags |= BPB_ROOTENTCNT_OK;
21715121Sfrankho 			if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
21725121Sfrankho 			    bpb_get_TotSec16(bpb) == 0)
21735121Sfrankho 				validflags |= BPB_TOTSEC16_OK;
21745121Sfrankho 			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
21755121Sfrankho 			    bpb_get_TotSec32(bpb) == 0)
21765121Sfrankho 				validflags |= BPB_TOTSEC32_OK;
21775121Sfrankho 			if (bpb_get_FatSz16(bpb) == fatsec)
21785121Sfrankho 				validflags |= BPB_FATSZ16_OK;
2179*11342SMichael.Bergknoff@Sun.COM 			if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER)
2180*11342SMichael.Bergknoff@Sun.COM 			    * 3 / 2)
21815121Sfrankho 				validflags |= BPB_FATSZ_OK;
21825121Sfrankho 			if (ncl < 4085)
21835121Sfrankho 				validflags |= BPB_NCLUSTERS_OK;
21845121Sfrankho 
21855121Sfrankho 			fsp->pcfs_lastclmark = (PCF_LASTCLUSTER & 0xfff);
21865121Sfrankho 			fsp->pcfs_rootblksize =
21875121Sfrankho 			    fsp->pcfs_rdirsec * secsize;
21885121Sfrankho 			fsp->pcfs_fsistart = 0;
21895121Sfrankho 
21905121Sfrankho 			if ((validflags & FAT12_VALIDMSK) != FAT12_VALIDMSK)
21915121Sfrankho 				type = FAT_UNKNOWN;
21925121Sfrankho 			break;
21935121Sfrankho 		case FAT16:
21945121Sfrankho 			if (rec != 0)
21955121Sfrankho 				validflags |= BPB_ROOTENTCNT_OK;
21965121Sfrankho 			if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
21975121Sfrankho 			    bpb_get_TotSec16(bpb) == 0)
21985121Sfrankho 				validflags |= BPB_TOTSEC16_OK;
21995121Sfrankho 			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
22005121Sfrankho 			    bpb_get_TotSec32(bpb) == 0)
22015121Sfrankho 				validflags |= BPB_TOTSEC32_OK;
22025121Sfrankho 			if (bpb_get_FatSz16(bpb) == fatsec)
22035121Sfrankho 				validflags |= BPB_FATSZ16_OK;
2204*11342SMichael.Bergknoff@Sun.COM 			if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER) * 2)
22055121Sfrankho 				validflags |= BPB_FATSZ_OK;
22065121Sfrankho 			if (ncl >= 4085 && ncl < 65525)
22075121Sfrankho 				validflags |= BPB_NCLUSTERS_OK;
22085121Sfrankho 
22095121Sfrankho 			fsp->pcfs_lastclmark = PCF_LASTCLUSTER;
22105121Sfrankho 			fsp->pcfs_rootblksize =
22115121Sfrankho 			    fsp->pcfs_rdirsec * secsize;
22125121Sfrankho 			fsp->pcfs_fsistart = 0;
22135121Sfrankho 
22145121Sfrankho 			if ((validflags & FAT16_VALIDMSK) != FAT16_VALIDMSK)
22155121Sfrankho 				type = FAT_UNKNOWN;
22165121Sfrankho 			break;
22175121Sfrankho 		case FAT32:
22185121Sfrankho 			if (rec == 0)
22195121Sfrankho 				validflags |= BPB_ROOTENTCNT_OK;
22205121Sfrankho 			if (bpb_get_TotSec16(bpb) == 0)
22215121Sfrankho 				validflags |= BPB_TOTSEC16_OK;
22225121Sfrankho 			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec)
22235121Sfrankho 				validflags |= BPB_TOTSEC32_OK;
22245121Sfrankho 			if (bpb_get_FatSz16(bpb) == 0)
22255121Sfrankho 				validflags |= BPB_FATSZ16_OK;
22265121Sfrankho 			if (bpb_get_FatSz32(bpb) == fatsec)
22275121Sfrankho 				validflags |= BPB_FATSZ32_OK;
2228*11342SMichael.Bergknoff@Sun.COM 			if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER) * 4)
22295121Sfrankho 				validflags |= BPB_FATSZ_OK;
22305121Sfrankho 			if (ncl >= 65525 && ncl < PCF_LASTCLUSTER32)
22315121Sfrankho 				validflags |= BPB_NCLUSTERS_OK;
22325121Sfrankho 
22335121Sfrankho 			fsp->pcfs_lastclmark = PCF_LASTCLUSTER32;
22345121Sfrankho 			fsp->pcfs_rootblksize = fsp->pcfs_clsize;
22355121Sfrankho 			fsp->pcfs_fsistart = fsp->pcfs_dosstart + fsisec;
22365121Sfrankho 			if (validflags & BPB_FSISEC_OK)
22375121Sfrankho 				fsp->pcfs_flags |= PCFS_FSINFO_OK;
22385121Sfrankho 			fsp->pcfs_rootclnum = bpb_get_RootClus32(bpb);
22395121Sfrankho 			if (pc_validcl(fsp, fsp->pcfs_rootclnum))
22405121Sfrankho 				validflags |= BPB_ROOTCLUSTER_OK;
22415121Sfrankho 
22425121Sfrankho 			/*
22435121Sfrankho 			 * Current PCFS code only works if 'pcfs_rdirstart'
22445121Sfrankho 			 * contains the root cluster number on FAT32.
22455121Sfrankho 			 * That's a mis-use and would better be changed.
22465121Sfrankho 			 */
22475121Sfrankho 			fsp->pcfs_rdirstart = (daddr_t)fsp->pcfs_rootclnum;
22485121Sfrankho 
22495121Sfrankho 			if ((validflags & FAT32_VALIDMSK) != FAT32_VALIDMSK)
22505121Sfrankho 				type = FAT_UNKNOWN;
22515121Sfrankho 			break;
22525121Sfrankho 		case FAT_QUESTIONABLE:
22535121Sfrankho 			type = secondaryBPBChecks(fsp, bpb, secsize);
22545121Sfrankho 			goto recheck;
22555121Sfrankho 		default:
22565121Sfrankho 			ASSERT(type == FAT_UNKNOWN);
22575121Sfrankho 			break;
22585121Sfrankho 	}
22595121Sfrankho 
22605121Sfrankho 	ASSERT(type != FAT_QUESTIONABLE);
22615121Sfrankho 
22625121Sfrankho 	fsp->pcfs_fattype = type;
22635121Sfrankho 
22645121Sfrankho 	if (valid)
22655121Sfrankho 		*valid = validflags;
22665121Sfrankho 
22675121Sfrankho 	DTRACE_PROBE4(parseBPB__final,
22685121Sfrankho 	    struct pcfs *, fsp, unsigned char *, bpb,
22695121Sfrankho 	    int, validflags, fattype_t, type);
22705121Sfrankho 
22715121Sfrankho 	if (type != FAT_UNKNOWN) {
22725121Sfrankho 		ASSERT((secsize & (DEV_BSIZE - 1)) == 0);
22735121Sfrankho 		ASSERT(ISP2(secsize / DEV_BSIZE));
22745121Sfrankho 		return (1);
22755121Sfrankho 	}
22765121Sfrankho 
22775121Sfrankho 	return (0);
22785121Sfrankho }
22795121Sfrankho 
22805121Sfrankho 
22815121Sfrankho /*
22825121Sfrankho  * Detect the device's native block size (sector size).
22835121Sfrankho  *
22845121Sfrankho  * Test whether the device is:
22855121Sfrankho  *	- a floppy device from a known controller type via DKIOCINFO
22865121Sfrankho  *	- a real floppy using the fd(7d) driver and capable of fdio(7I) ioctls
22875121Sfrankho  *	- a USB floppy drive (identified by drive geometry)
22885121Sfrankho  *
22895121Sfrankho  * Detecting a floppy will make PCFS metadata updates on such media synchronous,
22905121Sfrankho  * to minimize risks due to slow I/O and user hotplugging / device ejection.
22915121Sfrankho  *
22925121Sfrankho  * This might be a bit wasteful on kernel stack space; if anyone's
22935121Sfrankho  * bothered by this, kmem_alloc/kmem_free the ioctl arguments...
22945121Sfrankho  */
22955121Sfrankho static void
pcfs_device_getinfo(struct pcfs * fsp)22965121Sfrankho pcfs_device_getinfo(struct pcfs *fsp)
22975121Sfrankho {
22985121Sfrankho 	dev_t			rdev = fsp->pcfs_xdev;
22995121Sfrankho 	int			error;
23005121Sfrankho 	union {
23015121Sfrankho 		struct dk_minfo		mi;
23025121Sfrankho 		struct dk_cinfo		ci;
23035121Sfrankho 		struct dk_geom		gi;
23045121Sfrankho 		struct fd_char		fc;
23055121Sfrankho 	} arg;				/* save stackspace ... */
23065121Sfrankho 	intptr_t argp = (intptr_t)&arg;
23075121Sfrankho 	ldi_handle_t		lh;
23085121Sfrankho 	ldi_ident_t		li;
23095121Sfrankho 	int isfloppy, isremoveable, ishotpluggable;
23105121Sfrankho 	cred_t			*cr = CRED();
23115121Sfrankho 
23125121Sfrankho 	if (ldi_ident_from_dev(rdev, &li))
23135121Sfrankho 		goto out;
23145121Sfrankho 
23155121Sfrankho 	error = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, cr, &lh, li);
23165121Sfrankho 	ldi_ident_release(li);
23175121Sfrankho 	if (error)
23185121Sfrankho 		goto out;
23195121Sfrankho 
23205121Sfrankho 	/*
23215121Sfrankho 	 * Not sure if this could possibly happen. It'd be a bit like
23225121Sfrankho 	 * VOP_OPEN() changing the passed-in vnode ptr. We're just not
23235121Sfrankho 	 * expecting it, needs some thought if triggered ...
23245121Sfrankho 	 */
23255121Sfrankho 	ASSERT(fsp->pcfs_xdev == rdev);
23265121Sfrankho 
23275121Sfrankho 	/*
23285121Sfrankho 	 * Check for removeable/hotpluggable media.
23295121Sfrankho 	 */
23305121Sfrankho 	if (ldi_ioctl(lh, DKIOCREMOVABLE,
23315121Sfrankho 	    (intptr_t)&isremoveable, FKIOCTL, cr, NULL)) {
23325121Sfrankho 		isremoveable = 0;
23335121Sfrankho 	}
23345121Sfrankho 	if (ldi_ioctl(lh, DKIOCHOTPLUGGABLE,
23355121Sfrankho 	    (intptr_t)&ishotpluggable, FKIOCTL, cr, NULL)) {
23365121Sfrankho 		ishotpluggable = 0;
23375121Sfrankho 	}
23385121Sfrankho 
23395121Sfrankho 	/*
23405121Sfrankho 	 * Make sure we don't use "half-initialized" values if the ioctls fail.
23415121Sfrankho 	 */
23425121Sfrankho 	if (ldi_ioctl(lh, DKIOCGMEDIAINFO, argp, FKIOCTL, cr, NULL)) {
23435121Sfrankho 		bzero(&arg, sizeof (arg));
23445121Sfrankho 		fsp->pcfs_mediasize = 0;
23455121Sfrankho 	} else {
23465121Sfrankho 		fsp->pcfs_mediasize =
23475121Sfrankho 		    (len_t)arg.mi.dki_lbsize *
23485121Sfrankho 		    (len_t)arg.mi.dki_capacity;
23495121Sfrankho 	}
23505121Sfrankho 
23515121Sfrankho 	if (VALID_SECSIZE(arg.mi.dki_lbsize)) {
23525121Sfrankho 		if (fsp->pcfs_secsize == 0) {
23535121Sfrankho 			fsp->pcfs_secsize = arg.mi.dki_lbsize;
23545121Sfrankho 			fsp->pcfs_sdshift =
23555121Sfrankho 			    ddi_ffs(arg.mi.dki_lbsize / DEV_BSIZE) - 1;
23565121Sfrankho 		} else {
23575121Sfrankho 			PC_DPRINTF4(1, "!pcfs: autodetected media block size "
23585121Sfrankho 			    "%d, device (%x.%x), different from user-provided "
23595121Sfrankho 			    "%d. User override - ignoring autodetect result.\n",
23605121Sfrankho 			    arg.mi.dki_lbsize,
23615121Sfrankho 			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
23625121Sfrankho 			    fsp->pcfs_secsize);
23635121Sfrankho 		}
23645121Sfrankho 	} else if (arg.mi.dki_lbsize) {
23655121Sfrankho 		PC_DPRINTF3(1, "!pcfs: autodetected media block size "
23665121Sfrankho 		    "%d, device (%x.%x), invalid (not 512, 1024, 2048, 4096). "
23675121Sfrankho 		    "Ignoring autodetect result.\n",
23685121Sfrankho 		    arg.mi.dki_lbsize,
23695121Sfrankho 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev));
23705121Sfrankho 	}
23715121Sfrankho 
23725121Sfrankho 	/*
23735121Sfrankho 	 * We treat the following media types as a floppy by default.
23745121Sfrankho 	 */
23755121Sfrankho 	isfloppy =
23765121Sfrankho 	    (arg.mi.dki_media_type == DK_FLOPPY ||
23775121Sfrankho 	    arg.mi.dki_media_type == DK_ZIP ||
23785121Sfrankho 	    arg.mi.dki_media_type == DK_JAZ);
23795121Sfrankho 
23805121Sfrankho 	/*
23815121Sfrankho 	 * if this device understands fdio(7I) requests it's
23825121Sfrankho 	 * obviously a floppy drive.
23835121Sfrankho 	 */
23845121Sfrankho 	if (!isfloppy &&
23855121Sfrankho 	    !ldi_ioctl(lh, FDIOGCHAR, argp, FKIOCTL, cr, NULL))
23865121Sfrankho 		isfloppy = 1;
23875121Sfrankho 
23885121Sfrankho 	/*
238911215Sgdamore@opensolaris.org 	 * some devices we like to treat as floppies, but they don't
239011215Sgdamore@opensolaris.org 	 * understand fdio(7I) requests.
23915121Sfrankho 	 */
23925121Sfrankho 	if (!isfloppy &&
23935121Sfrankho 	    !ldi_ioctl(lh, DKIOCINFO, argp, FKIOCTL, cr, NULL) &&
23945121Sfrankho 	    (arg.ci.dki_ctype == DKC_WDC2880 ||
23955121Sfrankho 	    arg.ci.dki_ctype == DKC_NCRFLOPPY ||
23965121Sfrankho 	    arg.ci.dki_ctype == DKC_SMSFLOPPY ||
239711215Sgdamore@opensolaris.org 	    arg.ci.dki_ctype == DKC_INTEL82077))
23985121Sfrankho 		isfloppy = 1;
23995121Sfrankho 
24005121Sfrankho 	/*
24015121Sfrankho 	 * This is the "final fallback" test - media with
24025121Sfrankho 	 * 2 heads and 80 cylinders are assumed to be floppies.
24035121Sfrankho 	 * This is normally true for USB floppy drives ...
24045121Sfrankho 	 */
24055121Sfrankho 	if (!isfloppy &&
24065121Sfrankho 	    !ldi_ioctl(lh, DKIOCGGEOM, argp, FKIOCTL, cr, NULL) &&
24075121Sfrankho 	    (arg.gi.dkg_ncyl == 80 && arg.gi.dkg_nhead == 2))
24085121Sfrankho 		isfloppy = 1;
24095121Sfrankho 
24105121Sfrankho 	/*
24115121Sfrankho 	 * This is similar to the "old" PCFS code that sets this flag
24125121Sfrankho 	 * just based on the media descriptor being 0xf8 (MD_FIXED).
24135121Sfrankho 	 * Should be re-worked. We really need some specialcasing for
24145121Sfrankho 	 * removeable media.
24155121Sfrankho 	 */
24165121Sfrankho 	if (!isfloppy) {
24175121Sfrankho 		fsp->pcfs_flags |= PCFS_NOCHK;
24185121Sfrankho 	}
24195121Sfrankho 
24205121Sfrankho 	/*
24215121Sfrankho 	 * We automatically disable access time updates if the medium is
24225121Sfrankho 	 * removeable and/or hotpluggable, and the admin did not explicitly
24235121Sfrankho 	 * request access time updates (via the "atime" mount option).
24245121Sfrankho 	 * The majority of flash-based media should fit this category.
24255121Sfrankho 	 * Minimizing write access extends the lifetime of your memory stick !
24265121Sfrankho 	 */
24275121Sfrankho 	if (!vfs_optionisset(fsp->pcfs_vfs, MNTOPT_ATIME, NULL) &&
24285121Sfrankho 	    (isremoveable || ishotpluggable | isfloppy)) {
24295121Sfrankho 		fsp->pcfs_flags |= PCFS_NOATIME;
24305121Sfrankho 	}
24315121Sfrankho 
24325121Sfrankho 	(void) ldi_close(lh, FREAD, cr);
24335121Sfrankho out:
24345121Sfrankho 	if (fsp->pcfs_secsize == 0) {
24355121Sfrankho 		PC_DPRINTF3(1, "!pcfs: media block size autodetection "
24365121Sfrankho 		    "device (%x.%x) failed, no user-provided fallback. "
24375121Sfrankho 		    "Using %d bytes.\n",
24385121Sfrankho 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
24395121Sfrankho 		    DEV_BSIZE);
24405121Sfrankho 		fsp->pcfs_secsize = DEV_BSIZE;
24415121Sfrankho 		fsp->pcfs_sdshift = 0;
24425121Sfrankho 	}
24435121Sfrankho 	ASSERT(fsp->pcfs_secsize % DEV_BSIZE == 0);
24445121Sfrankho 	ASSERT(VALID_SECSIZE(fsp->pcfs_secsize));
24455121Sfrankho }
24465121Sfrankho 
24475121Sfrankho /*
24485121Sfrankho  * Get the FAT type for the DOS medium.
24495121Sfrankho  *
24505121Sfrankho  * -------------------------
24515121Sfrankho  * According to Microsoft:
24525121Sfrankho  *   The FAT type one of FAT12, FAT16, or FAT32 is determined by the
24535121Sfrankho  * count of clusters on the volume and nothing else.
24545121Sfrankho  * -------------------------
24555121Sfrankho  *
24565121Sfrankho  */
24575121Sfrankho static int
pc_getfattype(struct pcfs * fsp)24585121Sfrankho pc_getfattype(struct pcfs *fsp)
24595121Sfrankho {
24605121Sfrankho 	int error = 0;
24615121Sfrankho 	buf_t *bp = NULL;
24625121Sfrankho 	struct vnode *devvp = fsp->pcfs_devvp;
24635121Sfrankho 	dev_t	dev = devvp->v_rdev;
24645121Sfrankho 
24655121Sfrankho 	/*
24665121Sfrankho 	 * Detect the native block size of the medium, and attempt to
24675121Sfrankho 	 * detect whether the medium is removeable.
246811215Sgdamore@opensolaris.org 	 * We do treat removable media (floppies, USB and FireWire disks)
246911215Sgdamore@opensolaris.org 	 * differently wrt. to the frequency and synchronicity of FAT updates.
24705121Sfrankho 	 * We need to know the media block size in order to be able to
24715121Sfrankho 	 * parse the partition table.
24725121Sfrankho 	 */
24735121Sfrankho 	pcfs_device_getinfo(fsp);
24745121Sfrankho 
24755121Sfrankho 	/*
24765121Sfrankho 	 * Unpartitioned media (floppies and some removeable devices)
24775121Sfrankho 	 * don't have a partition table, the FAT BPB is at disk block 0.
24785121Sfrankho 	 * Start out by reading block 0.
24795121Sfrankho 	 */
24805121Sfrankho 	fsp->pcfs_dosstart = 0;
24815121Sfrankho 	bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), fsp->pcfs_secsize);
24825121Sfrankho 
24835121Sfrankho 	if (error = geterror(bp))
24845121Sfrankho 		goto out;
24855121Sfrankho 
24865121Sfrankho 	/*
24875121Sfrankho 	 * If a logical drive number is requested, parse the partition table
24885121Sfrankho 	 * and attempt to locate it. Otherwise, proceed immediately to the
24895121Sfrankho 	 * BPB check. findTheDrive(), if successful, returns the disk block
24905121Sfrankho 	 * number where the requested partition starts in "startsec".
24915121Sfrankho 	 */
24925121Sfrankho 	if (fsp->pcfs_ldrive != 0) {
24935121Sfrankho 		PC_DPRINTF3(5, "!pcfs: pc_getfattype: using FDISK table on "
24945121Sfrankho 		    "device (%x,%x):%d to find BPB\n",
24955121Sfrankho 		    getmajor(dev), getminor(dev), fsp->pcfs_ldrive);
24965121Sfrankho 
24975121Sfrankho 		if (error = findTheDrive(fsp, &bp))
24985121Sfrankho 			goto out;
24995121Sfrankho 
25005121Sfrankho 		ASSERT(fsp->pcfs_dosstart != 0);
25015121Sfrankho 
25025121Sfrankho 		brelse(bp);
25035121Sfrankho 		bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
25045121Sfrankho 		    fsp->pcfs_secsize);
25055121Sfrankho 		if (error = geterror(bp))
25065121Sfrankho 			goto out;
25075121Sfrankho 	}
25085121Sfrankho 
25095121Sfrankho 	/*
25105121Sfrankho 	 * Validate the BPB and fill in the instance structure.
25115121Sfrankho 	 */
25125121Sfrankho 	if (!parseBPB(fsp, (uchar_t *)bp->b_un.b_addr, NULL)) {
25135121Sfrankho 		PC_DPRINTF4(1, "!pcfs: pc_getfattype: No FAT BPB on "
25145121Sfrankho 		    "device (%x.%x):%d, disk LBA %u\n",
25155121Sfrankho 		    getmajor(dev), getminor(dev), fsp->pcfs_ldrive,
25165121Sfrankho 		    (uint_t)pc_dbdaddr(fsp, fsp->pcfs_dosstart));
25175121Sfrankho 		error = EINVAL;
25185121Sfrankho 		goto out;
25195121Sfrankho 	}
25205121Sfrankho 
25215121Sfrankho 	ASSERT(fsp->pcfs_fattype != FAT_UNKNOWN);
25225121Sfrankho 
25235121Sfrankho out:
25245121Sfrankho 	/*
25255121Sfrankho 	 * Release the buffer used
25265121Sfrankho 	 */
25275121Sfrankho 	if (bp != NULL)
25285121Sfrankho 		brelse(bp);
25295121Sfrankho 	return (error);
25305121Sfrankho }
25315121Sfrankho 
25325121Sfrankho 
25335121Sfrankho /*
25345121Sfrankho  * Get the file allocation table.
25355121Sfrankho  * If there is an old FAT, invalidate it.
25365121Sfrankho  */
25375121Sfrankho int
pc_getfat(struct pcfs * fsp)25385121Sfrankho pc_getfat(struct pcfs *fsp)
25395121Sfrankho {
25405121Sfrankho 	struct buf *bp = NULL;
25415121Sfrankho 	uchar_t *fatp = NULL;
25425121Sfrankho 	uchar_t *fat_changemap = NULL;
25435121Sfrankho 	int error;
25445121Sfrankho 	int fat_changemapsize;
25455121Sfrankho 	int flags = 0;
25465121Sfrankho 	int nfat;
25475121Sfrankho 	int altfat_mustmatch = 0;
25485121Sfrankho 	int fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
25495121Sfrankho 
25505121Sfrankho 	if (fsp->pcfs_fatp) {
25515121Sfrankho 		/*
25525121Sfrankho 		 * There is a FAT in core.
25535121Sfrankho 		 * If there are open file pcnodes or we have modified it or
25545121Sfrankho 		 * it hasn't timed out yet use the in core FAT.
25555121Sfrankho 		 * Otherwise invalidate it and get a new one
25565121Sfrankho 		 */
25575121Sfrankho #ifdef notdef
25585121Sfrankho 		if (fsp->pcfs_frefs ||
25595121Sfrankho 		    (fsp->pcfs_flags & PCFS_FATMOD) ||
25605121Sfrankho 		    (gethrestime_sec() < fsp->pcfs_fattime)) {
25615121Sfrankho 			return (0);
25625121Sfrankho 		} else {
25635121Sfrankho 			mutex_enter(&pcfslock);
25645121Sfrankho 			pc_invalfat(fsp);
25655121Sfrankho 			mutex_exit(&pcfslock);
25665121Sfrankho 		}
25675121Sfrankho #endif /* notdef */
25685121Sfrankho 		return (0);
25695121Sfrankho 	}
25705121Sfrankho 
25715121Sfrankho 	/*
25725121Sfrankho 	 * Get FAT and check it for validity
25735121Sfrankho 	 */
25745121Sfrankho 	fatp = kmem_alloc(fatsize, KM_SLEEP);
25755121Sfrankho 	error = pc_readfat(fsp, fatp);
25765121Sfrankho 	if (error) {
25775121Sfrankho 		flags = B_ERROR;
25785121Sfrankho 		goto out;
25795121Sfrankho 	}
25805121Sfrankho 	fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1;
25815121Sfrankho 	fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP);
25825121Sfrankho 	fsp->pcfs_fatp = fatp;
25835121Sfrankho 	fsp->pcfs_fat_changemapsize = fat_changemapsize;
25845121Sfrankho 	fsp->pcfs_fat_changemap = fat_changemap;
25855121Sfrankho 
25865121Sfrankho 	/*
25875121Sfrankho 	 * The only definite signature check is that the
25885121Sfrankho 	 * media descriptor byte should match the first byte
25895121Sfrankho 	 * of the FAT block.
25905121Sfrankho 	 */
25915121Sfrankho 	if (fatp[0] != fsp->pcfs_mediadesc) {
25925121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: FAT signature mismatch, "
25935121Sfrankho 		    "media descriptor %x, FAT[0] lowbyte %x\n",
25945121Sfrankho 		    (uint32_t)fsp->pcfs_mediadesc, (uint32_t)fatp[0]);
25955121Sfrankho 		cmn_err(CE_NOTE, "!pcfs: Enforcing alternate FAT validation\n");
25965121Sfrankho 		altfat_mustmatch = 1;
25975121Sfrankho 	}
25985121Sfrankho 
25995121Sfrankho 	/*
26005121Sfrankho 	 * Get alternate FATs and check for consistency
26015121Sfrankho 	 * This is an inlined version of pc_readfat().
26025121Sfrankho 	 * Since we're only comparing FAT and alternate FAT,
26035121Sfrankho 	 * there's no reason to let pc_readfat() copy data out
26045121Sfrankho 	 * of the buf. Instead, compare in-situ, one cluster
26055121Sfrankho 	 * at a time.
26065121Sfrankho 	 */
26075121Sfrankho 	for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) {
26085121Sfrankho 		size_t startsec;
26095121Sfrankho 		size_t off;
26105121Sfrankho 
26115121Sfrankho 		startsec = pc_dbdaddr(fsp,
26125121Sfrankho 		    fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec);
26135121Sfrankho 
26145121Sfrankho 		for (off = 0; off < fatsize; off += fsp->pcfs_clsize) {
26155121Sfrankho 			daddr_t fatblk = startsec + pc_dbdaddr(fsp,
26165121Sfrankho 			    pc_cltodb(fsp, pc_lblkno(fsp, off)));
26175121Sfrankho 
26185121Sfrankho 			bp = bread(fsp->pcfs_xdev, fatblk,
26195121Sfrankho 			    MIN(fsp->pcfs_clsize, fatsize - off));
26205121Sfrankho 			if (bp->b_flags & (B_ERROR | B_STALE)) {
26215121Sfrankho 				cmn_err(CE_NOTE,
26225121Sfrankho 				    "!pcfs: alternate FAT #%d (start LBA %p)"
26235121Sfrankho 				    " read error at offset %ld on device"
26245121Sfrankho 				    " (%x.%x):%d",
26255121Sfrankho 				    nfat, (void *)(uintptr_t)startsec, off,
26265121Sfrankho 				    getmajor(fsp->pcfs_xdev),
26275121Sfrankho 				    getminor(fsp->pcfs_xdev),
26285121Sfrankho 				    fsp->pcfs_ldrive);
26295121Sfrankho 				flags = B_ERROR;
26305121Sfrankho 				error = EIO;
26315121Sfrankho 				goto out;
26325121Sfrankho 			}
26335121Sfrankho 			bp->b_flags |= B_STALE | B_AGE;
26345121Sfrankho 			if (bcmp(bp->b_un.b_addr, fatp + off,
26355121Sfrankho 			    MIN(fsp->pcfs_clsize, fatsize - off))) {
26365121Sfrankho 				cmn_err(CE_NOTE,
26375121Sfrankho 				    "!pcfs: alternate FAT #%d (start LBA %p)"
26385121Sfrankho 				    " corrupted at offset %ld on device"
26395121Sfrankho 				    " (%x.%x):%d",
26405121Sfrankho 				    nfat, (void *)(uintptr_t)startsec, off,
26415121Sfrankho 				    getmajor(fsp->pcfs_xdev),
26425121Sfrankho 				    getminor(fsp->pcfs_xdev),
26435121Sfrankho 				    fsp->pcfs_ldrive);
26445121Sfrankho 				if (altfat_mustmatch) {
26455121Sfrankho 					flags = B_ERROR;
26465121Sfrankho 					error = EIO;
26475121Sfrankho 					goto out;
26485121Sfrankho 				}
26495121Sfrankho 			}
26505121Sfrankho 			brelse(bp);
26515121Sfrankho 			bp = NULL;	/* prevent double release */
26525121Sfrankho 		}
26535121Sfrankho 	}
26545121Sfrankho 
26555121Sfrankho 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
26565121Sfrankho 	fsp->pcfs_fatjustread = 1;
26575121Sfrankho 
26585121Sfrankho 	/*
26595121Sfrankho 	 * Retrieve FAT32 fsinfo sector.
26605121Sfrankho 	 * A failure to read this is not fatal to accessing the volume.
26615121Sfrankho 	 * It simply means operations that count or search free blocks
26625121Sfrankho 	 * will have to do a full FAT walk, vs. a possibly quicker lookup
26635121Sfrankho 	 * of the summary information.
26645121Sfrankho 	 * Hence, we log a message but return success overall after this point.
26655121Sfrankho 	 */
26665121Sfrankho 	if (IS_FAT32(fsp) && (fsp->pcfs_flags & PCFS_FSINFO_OK)) {
26675121Sfrankho 		struct fat_od_fsi *fsinfo_disk;
26685121Sfrankho 
26695121Sfrankho 		bp = bread(fsp->pcfs_xdev,
26705121Sfrankho 		    pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
26715121Sfrankho 		fsinfo_disk = (struct fat_od_fsi *)bp->b_un.b_addr;
26725121Sfrankho 		if (bp->b_flags & (B_ERROR | B_STALE) ||
26735121Sfrankho 		    !FSISIG_OK(fsinfo_disk)) {
26745121Sfrankho 			cmn_err(CE_NOTE,
26755121Sfrankho 			    "!pcfs: error reading fat32 fsinfo from "
26765121Sfrankho 			    "device (%x.%x):%d, block %lld",
26775121Sfrankho 			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
26785121Sfrankho 			    fsp->pcfs_ldrive,
26795121Sfrankho 			    (long long)pc_dbdaddr(fsp, fsp->pcfs_fsistart));
26805121Sfrankho 			fsp->pcfs_flags &= ~PCFS_FSINFO_OK;
26815121Sfrankho 			fsp->pcfs_fsinfo.fs_free_clusters = FSINFO_UNKNOWN;
26825121Sfrankho 			fsp->pcfs_fsinfo.fs_next_free = FSINFO_UNKNOWN;
26835121Sfrankho 		} else {
26845121Sfrankho 			bp->b_flags |= B_STALE | B_AGE;
26855121Sfrankho 			fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
26865121Sfrankho 			fsp->pcfs_fsinfo.fs_free_clusters =
26875121Sfrankho 			    LE_32(fsinfo_disk->fsi_incore.fs_free_clusters);
26885121Sfrankho 			fsp->pcfs_fsinfo.fs_next_free =
26895121Sfrankho 			    LE_32(fsinfo_disk->fsi_incore.fs_next_free);
26905121Sfrankho 		}
26915121Sfrankho 		brelse(bp);
26925121Sfrankho 		bp = NULL;
26935121Sfrankho 	}
26945121Sfrankho 
26955121Sfrankho 	if (pc_validcl(fsp, (pc_cluster32_t)fsp->pcfs_fsinfo.fs_next_free))
26965121Sfrankho 		fsp->pcfs_nxfrecls = fsp->pcfs_fsinfo.fs_next_free;
26975121Sfrankho 	else
26985121Sfrankho 		fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
26995121Sfrankho 
27005121Sfrankho 	return (0);
27015121Sfrankho 
27025121Sfrankho out:
27035121Sfrankho 	cmn_err(CE_NOTE, "!pcfs: illegal disk format");
27045121Sfrankho 	if (bp)
27055121Sfrankho 		brelse(bp);
27065121Sfrankho 	if (fatp)
27075121Sfrankho 		kmem_free(fatp, fatsize);
27085121Sfrankho 	if (fat_changemap)
27095121Sfrankho 		kmem_free(fat_changemap, fat_changemapsize);
27105121Sfrankho 
27115121Sfrankho 	if (flags) {
27125121Sfrankho 		pc_mark_irrecov(fsp);
27135121Sfrankho 	}
27145121Sfrankho 	return (error);
27155121Sfrankho }
2716