xref: /illumos-gate/usr/src/uts/common/fs/pcfs/pc_vfsops.c (revision 80a3d255)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
55a59a8b3Srsb  * Common Development and Distribution License (the "License").
65a59a8b3Srsb  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21342440ecSPrasad Singamsetty 
227c478bd9Sstevel@tonic-gate /*
2369ed0c8eSGarrett D'Amore  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
26d9a54dd1SSebastien Roy /*
27d9a54dd1SSebastien Roy  * Copyright (c) 2017 by Delphix. All rights reserved.
28d9a54dd1SSebastien Roy  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate #include <sys/param.h>
317c478bd9Sstevel@tonic-gate #include <sys/systm.h>
327c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
337c478bd9Sstevel@tonic-gate #include <sys/user.h>
347c478bd9Sstevel@tonic-gate #include <sys/proc.h>
357c478bd9Sstevel@tonic-gate #include <sys/cred.h>
367c478bd9Sstevel@tonic-gate #include <sys/disp.h>
377c478bd9Sstevel@tonic-gate #include <sys/buf.h>
387c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
39aa59c4cbSrsb #include <sys/vfs_opreg.h>
407c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
417c478bd9Sstevel@tonic-gate #include <sys/fdio.h>
427c478bd9Sstevel@tonic-gate #include <sys/file.h>
437c478bd9Sstevel@tonic-gate #include <sys/uio.h>
447c478bd9Sstevel@tonic-gate #include <sys/conf.h>
457c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
467c478bd9Sstevel@tonic-gate #include <sys/mount.h>
477c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
487c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
497c478bd9Sstevel@tonic-gate #include <sys/debug.h>
507c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
517c478bd9Sstevel@tonic-gate #include <sys/conf.h>
527c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
537c478bd9Sstevel@tonic-gate #include <sys/swap.h>
547c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
557c478bd9Sstevel@tonic-gate #include <sys/sunldi.h>
567c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
577c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h>
587c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
597c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
607c478bd9Sstevel@tonic-gate #include <sys/fs/pc_node.h>
617c478bd9Sstevel@tonic-gate #include <fs/fs_subr.h>
627c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
637c478bd9Sstevel@tonic-gate #include <sys/dkio.h>
647c478bd9Sstevel@tonic-gate #include <sys/open.h>
657c478bd9Sstevel@tonic-gate #include <sys/mntent.h>
667c478bd9Sstevel@tonic-gate #include <sys/policy.h>
67264a6e74Sfrankho #include <sys/atomic.h>
68f127cb91Sfrankho #include <sys/sdt.h>
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate /*
717c478bd9Sstevel@tonic-gate  * The majority of PC media use a 512 sector size, but
727c478bd9Sstevel@tonic-gate  * occasionally you will run across a 1k sector size.
737c478bd9Sstevel@tonic-gate  * For media with a 1k sector size, fd_strategy() requires
747c478bd9Sstevel@tonic-gate  * the I/O size to be a 1k multiple; so when the sector size
757c478bd9Sstevel@tonic-gate  * is not yet known, always read 1k.
767c478bd9Sstevel@tonic-gate  */
777c478bd9Sstevel@tonic-gate #define	PC_SAFESECSIZE	(PC_SECSIZE * 2)
787c478bd9Sstevel@tonic-gate 
7990c30842Sjmcp static int pcfs_pseudo_floppy(dev_t);
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate static int pcfsinit(int, char *);
827c478bd9Sstevel@tonic-gate static int pcfs_mount(struct vfs *, struct vnode *, struct mounta *,
837c478bd9Sstevel@tonic-gate 	struct cred *);
847c478bd9Sstevel@tonic-gate static int pcfs_unmount(struct vfs *, int, struct cred *);
857c478bd9Sstevel@tonic-gate static int pcfs_root(struct vfs *, struct vnode **);
867c478bd9Sstevel@tonic-gate static int pcfs_statvfs(struct vfs *, struct statvfs64 *);
877c478bd9Sstevel@tonic-gate static int pc_syncfsnodes(struct pcfs *);
887c478bd9Sstevel@tonic-gate static int pcfs_sync(struct vfs *, short, struct cred *);
897c478bd9Sstevel@tonic-gate static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp);
90264a6e74Sfrankho static void pcfs_freevfs(vfs_t *vfsp);
917c478bd9Sstevel@tonic-gate 
92f127cb91Sfrankho static int pc_readfat(struct pcfs *fsp, uchar_t *fatp);
937c478bd9Sstevel@tonic-gate static int pc_writefat(struct pcfs *fsp, daddr_t start);
947c478bd9Sstevel@tonic-gate 
95f127cb91Sfrankho static int pc_getfattype(struct pcfs *fsp);
969e003ac4Sgd static void pcfs_parse_mntopts(struct pcfs *fsp);
97f127cb91Sfrankho 
98f127cb91Sfrankho 
997c478bd9Sstevel@tonic-gate /*
1007c478bd9Sstevel@tonic-gate  * pcfs mount options table
1017c478bd9Sstevel@tonic-gate  */
1027c478bd9Sstevel@tonic-gate 
103264a6e74Sfrankho static char *nohidden_cancel[] = { MNTOPT_PCFS_HIDDEN, NULL };
104264a6e74Sfrankho static char *hidden_cancel[] = { MNTOPT_PCFS_NOHIDDEN, NULL };
105264a6e74Sfrankho static char *nofoldcase_cancel[] = { MNTOPT_PCFS_FOLDCASE, NULL };
106264a6e74Sfrankho static char *foldcase_cancel[] = { MNTOPT_PCFS_NOFOLDCASE, NULL };
107264a6e74Sfrankho static char *clamptime_cancel[] = { MNTOPT_PCFS_NOCLAMPTIME, NULL };
108264a6e74Sfrankho static char *noclamptime_cancel[] = { MNTOPT_PCFS_CLAMPTIME, NULL };
109f127cb91Sfrankho static char *atime_cancel[] = { MNTOPT_NOATIME, NULL };
110f127cb91Sfrankho static char *noatime_cancel[] = { MNTOPT_ATIME, NULL };
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate static mntopt_t mntopts[] = {
1137c478bd9Sstevel@tonic-gate /*
114264a6e74Sfrankho  *	option name	cancel option	default arg	flags	opt data
1157c478bd9Sstevel@tonic-gate  */
116264a6e74Sfrankho 	{ MNTOPT_PCFS_NOHIDDEN, nohidden_cancel, NULL, 0, NULL },
117264a6e74Sfrankho 	{ MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, MO_DEFAULT, NULL },
118264a6e74Sfrankho 	{ MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, NULL },
119264a6e74Sfrankho 	{ MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0, NULL },
120264a6e74Sfrankho 	{ MNTOPT_PCFS_CLAMPTIME, clamptime_cancel, NULL, MO_DEFAULT, NULL },
121*80a3d255SToomas Soome 	{ MNTOPT_PCFS_NOCLAMPTIME, noclamptime_cancel, NULL, 0, NULL },
122*80a3d255SToomas Soome 	{ MNTOPT_NOATIME, noatime_cancel, NULL, 0, NULL },
123*80a3d255SToomas Soome 	{ MNTOPT_ATIME, atime_cancel, NULL, 0, NULL },
124f127cb91Sfrankho 	{ MNTOPT_PCFS_TIMEZONE, NULL, "+0", MO_DEFAULT | MO_HASVALUE, NULL },
125f127cb91Sfrankho 	{ MNTOPT_PCFS_SECSIZE, NULL, NULL, MO_HASVALUE, NULL }
1267c478bd9Sstevel@tonic-gate };
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate static mntopts_t pcfs_mntopts = {
1297c478bd9Sstevel@tonic-gate 	sizeof (mntopts) / sizeof (mntopt_t),
1307c478bd9Sstevel@tonic-gate 	mntopts
1317c478bd9Sstevel@tonic-gate };
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate int pcfsdebuglevel = 0;
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate /*
1367c478bd9Sstevel@tonic-gate  * pcfslock:	protects the list of mounted pc filesystems "pc_mounttab.
1377c478bd9Sstevel@tonic-gate  * pcfs_lock:	(inside per filesystem structure "pcfs")
1387c478bd9Sstevel@tonic-gate  *		per filesystem lock. Most of the vfsops and vnodeops are
1397c478bd9Sstevel@tonic-gate  *		protected by this lock.
1407c478bd9Sstevel@tonic-gate  * pcnodes_lock: protects the pcnode hash table "pcdhead", "pcfhead".
1417c478bd9Sstevel@tonic-gate  *
1427c478bd9Sstevel@tonic-gate  * Lock hierarchy: pcfslock > pcfs_lock > pcnodes_lock
143264a6e74Sfrankho  *
144264a6e74Sfrankho  * pcfs_mountcount:	used to prevent module unloads while there is still
145264a6e74Sfrankho  *			pcfs state from a former mount hanging around. With
146264a6e74Sfrankho  *			forced umount support, the filesystem module must not
147264a6e74Sfrankho  *			be allowed to go away before the last VFS_FREEVFS()
148264a6e74Sfrankho  *			call has been made.
149264a6e74Sfrankho  *			Since this is just an atomic counter, there's no need
150264a6e74Sfrankho  *			for locking.
1517c478bd9Sstevel@tonic-gate  */
1527c478bd9Sstevel@tonic-gate kmutex_t	pcfslock;
153264a6e74Sfrankho krwlock_t	pcnodes_lock;
154264a6e74Sfrankho uint32_t	pcfs_mountcount;
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate static int pcfstype;
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate static vfsdef_t vfw = {
1597c478bd9Sstevel@tonic-gate 	VFSDEF_VERSION,
1607c478bd9Sstevel@tonic-gate 	"pcfs",
1617c478bd9Sstevel@tonic-gate 	pcfsinit,
162d9a54dd1SSebastien Roy 	VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS|VSW_CANLOFI|VSW_MOUNTDEV,
1637c478bd9Sstevel@tonic-gate 	&pcfs_mntopts
1647c478bd9Sstevel@tonic-gate };
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate extern struct mod_ops mod_fsops;
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate static struct modlfs modlfs = {
1697c478bd9Sstevel@tonic-gate 	&mod_fsops,
170c2aaf90fSgd 	"PC filesystem",
1717c478bd9Sstevel@tonic-gate 	&vfw
1727c478bd9Sstevel@tonic-gate };
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
1757c478bd9Sstevel@tonic-gate 	MODREV_1,
1767c478bd9Sstevel@tonic-gate 	&modlfs,
1777c478bd9Sstevel@tonic-gate 	NULL
1787c478bd9Sstevel@tonic-gate };
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate int
1817c478bd9Sstevel@tonic-gate _init(void)
1827c478bd9Sstevel@tonic-gate {
1837c478bd9Sstevel@tonic-gate 	int	error;
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate #if !defined(lint)
1867c478bd9Sstevel@tonic-gate 	/* make sure the on-disk structures are sane */
1877c478bd9Sstevel@tonic-gate 	ASSERT(sizeof (struct pcdir) == 32);
1887c478bd9Sstevel@tonic-gate 	ASSERT(sizeof (struct pcdir_lfn) == 32);
1897c478bd9Sstevel@tonic-gate #endif
1907c478bd9Sstevel@tonic-gate 	mutex_init(&pcfslock, NULL, MUTEX_DEFAULT, NULL);
1917c478bd9Sstevel@tonic-gate 	rw_init(&pcnodes_lock, NULL, RW_DEFAULT, NULL);
1927c478bd9Sstevel@tonic-gate 	error = mod_install(&modlinkage);
1937c478bd9Sstevel@tonic-gate 	if (error) {
1947c478bd9Sstevel@tonic-gate 		mutex_destroy(&pcfslock);
1957c478bd9Sstevel@tonic-gate 		rw_destroy(&pcnodes_lock);
1967c478bd9Sstevel@tonic-gate 	}
1977c478bd9Sstevel@tonic-gate 	return (error);
1987c478bd9Sstevel@tonic-gate }
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate int
2017c478bd9Sstevel@tonic-gate _fini(void)
2027c478bd9Sstevel@tonic-gate {
2037c478bd9Sstevel@tonic-gate 	int	error;
2047c478bd9Sstevel@tonic-gate 
205264a6e74Sfrankho 	/*
206264a6e74Sfrankho 	 * If a forcedly unmounted instance is still hanging around,
207264a6e74Sfrankho 	 * we cannot allow the module to be unloaded because that would
208264a6e74Sfrankho 	 * cause panics once the VFS framework decides it's time to call
209264a6e74Sfrankho 	 * into VFS_FREEVFS().
210264a6e74Sfrankho 	 */
211264a6e74Sfrankho 	if (pcfs_mountcount)
212264a6e74Sfrankho 		return (EBUSY);
213264a6e74Sfrankho 
2147c478bd9Sstevel@tonic-gate 	error = mod_remove(&modlinkage);
2157c478bd9Sstevel@tonic-gate 	if (error)
2167c478bd9Sstevel@tonic-gate 		return (error);
2177c478bd9Sstevel@tonic-gate 	mutex_destroy(&pcfslock);
2187c478bd9Sstevel@tonic-gate 	rw_destroy(&pcnodes_lock);
2197c478bd9Sstevel@tonic-gate 	/*
2207c478bd9Sstevel@tonic-gate 	 * Tear down the operations vectors
2217c478bd9Sstevel@tonic-gate 	 */
2227c478bd9Sstevel@tonic-gate 	(void) vfs_freevfsops_by_type(pcfstype);
2237c478bd9Sstevel@tonic-gate 	vn_freevnodeops(pcfs_fvnodeops);
2247c478bd9Sstevel@tonic-gate 	vn_freevnodeops(pcfs_dvnodeops);
2257c478bd9Sstevel@tonic-gate 	return (0);
2267c478bd9Sstevel@tonic-gate }
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate int
2297c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
2307c478bd9Sstevel@tonic-gate {
2317c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2327c478bd9Sstevel@tonic-gate }
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate /* ARGSUSED1 */
2357c478bd9Sstevel@tonic-gate static int
2367c478bd9Sstevel@tonic-gate pcfsinit(int fstype, char *name)
2377c478bd9Sstevel@tonic-gate {
2387c478bd9Sstevel@tonic-gate 	static const fs_operation_def_t pcfs_vfsops_template[] = {
239aa59c4cbSrsb 		VFSNAME_MOUNT,		{ .vfs_mount = pcfs_mount },
240aa59c4cbSrsb 		VFSNAME_UNMOUNT,	{ .vfs_unmount = pcfs_unmount },
241aa59c4cbSrsb 		VFSNAME_ROOT,		{ .vfs_root = pcfs_root },
242aa59c4cbSrsb 		VFSNAME_STATVFS,	{ .vfs_statvfs = pcfs_statvfs },
243aa59c4cbSrsb 		VFSNAME_SYNC,		{ .vfs_sync = pcfs_sync },
244aa59c4cbSrsb 		VFSNAME_VGET,		{ .vfs_vget = pcfs_vget },
245aa59c4cbSrsb 		VFSNAME_FREEVFS,	{ .vfs_freevfs = pcfs_freevfs },
246aa59c4cbSrsb 		NULL,			NULL
2477c478bd9Sstevel@tonic-gate 	};
2487c478bd9Sstevel@tonic-gate 	int error;
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate 	error = vfs_setfsops(fstype, pcfs_vfsops_template, NULL);
2517c478bd9Sstevel@tonic-gate 	if (error != 0) {
2527c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad vfs ops template");
2537c478bd9Sstevel@tonic-gate 		return (error);
2547c478bd9Sstevel@tonic-gate 	}
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate 	error = vn_make_ops("pcfs", pcfs_fvnodeops_template, &pcfs_fvnodeops);
2577c478bd9Sstevel@tonic-gate 	if (error != 0) {
2587c478bd9Sstevel@tonic-gate 		(void) vfs_freevfsops_by_type(fstype);
2597c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad file vnode ops template");
2607c478bd9Sstevel@tonic-gate 		return (error);
2617c478bd9Sstevel@tonic-gate 	}
2627c478bd9Sstevel@tonic-gate 
2637c478bd9Sstevel@tonic-gate 	error = vn_make_ops("pcfsd", pcfs_dvnodeops_template, &pcfs_dvnodeops);
2647c478bd9Sstevel@tonic-gate 	if (error != 0) {
2657c478bd9Sstevel@tonic-gate 		(void) vfs_freevfsops_by_type(fstype);
2667c478bd9Sstevel@tonic-gate 		vn_freevnodeops(pcfs_fvnodeops);
2677c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad dir vnode ops template");
2687c478bd9Sstevel@tonic-gate 		return (error);
2697c478bd9Sstevel@tonic-gate 	}
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 	pcfstype = fstype;
2727c478bd9Sstevel@tonic-gate 	(void) pc_init();
273264a6e74Sfrankho 	pcfs_mountcount = 0;
2747c478bd9Sstevel@tonic-gate 	return (0);
2757c478bd9Sstevel@tonic-gate }
2767c478bd9Sstevel@tonic-gate 
2777c478bd9Sstevel@tonic-gate static struct pcfs *pc_mounttab = NULL;
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate extern struct pcfs_args pc_tz;
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate /*
2827c478bd9Sstevel@tonic-gate  *  Define some special logical drives we use internal to this file.
2837c478bd9Sstevel@tonic-gate  */
2847c478bd9Sstevel@tonic-gate #define	BOOT_PARTITION_DRIVE	99
2857c478bd9Sstevel@tonic-gate #define	PRIMARY_DOS_DRIVE	1
286f127cb91Sfrankho #define	UNPARTITIONED_DRIVE	0
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate static int
289f127cb91Sfrankho pcfs_device_identify(
2907c478bd9Sstevel@tonic-gate 	struct vfs *vfsp,
2917c478bd9Sstevel@tonic-gate 	struct mounta *uap,
292f127cb91Sfrankho 	struct cred *cr,
293f127cb91Sfrankho 	int *dos_ldrive,
294f127cb91Sfrankho 	dev_t *xdev)
2957c478bd9Sstevel@tonic-gate {
2967c478bd9Sstevel@tonic-gate 	struct pathname special;
2979bd42341Sfrankho 	char *c;
29893239addSjohnlev 	struct vnode *svp = NULL;
29993239addSjohnlev 	struct vnode *lvp = NULL;
3007c478bd9Sstevel@tonic-gate 	int oflag, aflag;
301f127cb91Sfrankho 	int error;
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 	/*
3047c478bd9Sstevel@tonic-gate 	 * Resolve path name of special file being mounted.
3057c478bd9Sstevel@tonic-gate 	 */
3067c478bd9Sstevel@tonic-gate 	if (error = pn_get(uap->spec, UIO_USERSPACE, &special)) {
3077c478bd9Sstevel@tonic-gate 		return (error);
3087c478bd9Sstevel@tonic-gate 	}
309f127cb91Sfrankho 
310f127cb91Sfrankho 	*dos_ldrive = -1;
311f127cb91Sfrankho 
3127c478bd9Sstevel@tonic-gate 	if (error =
31393239addSjohnlev 	    lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &svp)) {
314f127cb91Sfrankho 		/*
315f127cb91Sfrankho 		 * If there's no device node, the name specified most likely
316f127cb91Sfrankho 		 * maps to a PCFS-style "partition specifier" to select a
317f127cb91Sfrankho 		 * harddisk primary/logical partition. Disable floppy-specific
318f127cb91Sfrankho 		 * checks in such cases unless an explicit :A or :B is
319f127cb91Sfrankho 		 * requested.
320f127cb91Sfrankho 		 */
321f127cb91Sfrankho 
3227c478bd9Sstevel@tonic-gate 		/*
3239bd42341Sfrankho 		 * Split the pathname string at the last ':' separator.
3249bd42341Sfrankho 		 * If there's no ':' in the device name, or the ':' is the
3259bd42341Sfrankho 		 * last character in the string, the name is invalid and
3269bd42341Sfrankho 		 * the error from the previous lookup will be returned.
3277c478bd9Sstevel@tonic-gate 		 */
3289bd42341Sfrankho 		c = strrchr(special.pn_path, ':');
3299bd42341Sfrankho 		if (c == NULL || strlen(c) == 0)
3309bd42341Sfrankho 			goto devlookup_done;
3317c478bd9Sstevel@tonic-gate 
3329bd42341Sfrankho 		*c++ = '\0';
3337c478bd9Sstevel@tonic-gate 
3349bd42341Sfrankho 		/*
3359bd42341Sfrankho 		 * PCFS partition name suffixes can be:
3369bd42341Sfrankho 		 *	- "boot" to indicate the X86BOOT partition
3379bd42341Sfrankho 		 *	- a drive letter [c-z] for the "DOS logical drive"
3389bd42341Sfrankho 		 *	- a drive number 1..24 for the "DOS logical drive"
339f127cb91Sfrankho 		 *	- a "floppy name letter", 'a' or 'b' (just strip this)
3409bd42341Sfrankho 		 */
3419bd42341Sfrankho 		if (strcasecmp(c, "boot") == 0) {
3429bd42341Sfrankho 			/*
3439bd42341Sfrankho 			 * The Solaris boot partition is requested.
3449bd42341Sfrankho 			 */
345f127cb91Sfrankho 			*dos_ldrive = BOOT_PARTITION_DRIVE;
3469bd42341Sfrankho 		} else if (strspn(c, "0123456789") == strlen(c)) {
3479bd42341Sfrankho 			/*
3489bd42341Sfrankho 			 * All digits - parse the partition number.
3499bd42341Sfrankho 			 */
3509bd42341Sfrankho 			long drvnum = 0;
3519bd42341Sfrankho 
3529bd42341Sfrankho 			if ((error = ddi_strtol(c, NULL, 10, &drvnum)) == 0) {
3537c478bd9Sstevel@tonic-gate 				/*
3549bd42341Sfrankho 				 * A number alright - in the allowed range ?
3557c478bd9Sstevel@tonic-gate 				 */
3569bd42341Sfrankho 				if (drvnum > 24 || drvnum == 0)
357f127cb91Sfrankho 					error = ENXIO;
3587c478bd9Sstevel@tonic-gate 			}
3599bd42341Sfrankho 			if (error)
3609bd42341Sfrankho 				goto devlookup_done;
361f127cb91Sfrankho 			*dos_ldrive = (int)drvnum;
3629bd42341Sfrankho 		} else if (strlen(c) == 1) {
3639bd42341Sfrankho 			/*
364f127cb91Sfrankho 			 * A single trailing character was specified.
365f127cb91Sfrankho 			 *	- [c-zC-Z] means a harddisk partition, and
366f127cb91Sfrankho 			 *	  we retrieve the partition number.
367f127cb91Sfrankho 			 *	- [abAB] means a floppy drive, so we swallow
368f127cb91Sfrankho 			 *	  the "drive specifier" and test later
36969ed0c8eSGarrett D'Amore 			 *	  whether the physical device is a floppy.
3709bd42341Sfrankho 			 */
3719bd42341Sfrankho 			*c = tolower(*c);
372f127cb91Sfrankho 			if (*c == 'a' || *c == 'b') {
373f127cb91Sfrankho 				*dos_ldrive = UNPARTITIONED_DRIVE;
374f127cb91Sfrankho 			} else if (*c < 'c' || *c > 'z') {
375f127cb91Sfrankho 				error = ENXIO;
3769bd42341Sfrankho 				goto devlookup_done;
377f127cb91Sfrankho 			} else {
378f127cb91Sfrankho 				*dos_ldrive = 1 + *c - 'c';
3799bd42341Sfrankho 			}
3809bd42341Sfrankho 		} else {
3819bd42341Sfrankho 			/*
3829bd42341Sfrankho 			 * Can't parse this - pass through previous error.
3839bd42341Sfrankho 			 */
3849bd42341Sfrankho 			goto devlookup_done;
3857c478bd9Sstevel@tonic-gate 		}
3869bd42341Sfrankho 
3879bd42341Sfrankho 
3889bd42341Sfrankho 		error = lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW,
38993239addSjohnlev 		    NULLVPP, &svp);
390f127cb91Sfrankho 	} else {
391f127cb91Sfrankho 		*dos_ldrive = UNPARTITIONED_DRIVE;
3927c478bd9Sstevel@tonic-gate 	}
3939bd42341Sfrankho devlookup_done:
3947c478bd9Sstevel@tonic-gate 	pn_free(&special);
3959bd42341Sfrankho 	if (error)
3969bd42341Sfrankho 		return (error);
3979bd42341Sfrankho 
398f127cb91Sfrankho 	ASSERT(*dos_ldrive >= UNPARTITIONED_DRIVE);
399f127cb91Sfrankho 
4007c478bd9Sstevel@tonic-gate 	/*
4017c478bd9Sstevel@tonic-gate 	 * Verify caller's permission to open the device special file.
4027c478bd9Sstevel@tonic-gate 	 */
4037c478bd9Sstevel@tonic-gate 	if ((vfsp->vfs_flag & VFS_RDONLY) != 0 ||
4047c478bd9Sstevel@tonic-gate 	    ((uap->flags & MS_RDONLY) != 0)) {
4057c478bd9Sstevel@tonic-gate 		oflag = FREAD;
4067c478bd9Sstevel@tonic-gate 		aflag = VREAD;
4077c478bd9Sstevel@tonic-gate 	} else {
4087c478bd9Sstevel@tonic-gate 		oflag = FREAD | FWRITE;
4097c478bd9Sstevel@tonic-gate 		aflag = VREAD | VWRITE;
4107c478bd9Sstevel@tonic-gate 	}
411f127cb91Sfrankho 
41293239addSjohnlev 	error = vfs_get_lofi(vfsp, &lvp);
413f127cb91Sfrankho 
41493239addSjohnlev 	if (error > 0) {
41593239addSjohnlev 		if (error == ENOENT)
41693239addSjohnlev 			error = ENODEV;
41793239addSjohnlev 		goto out;
41893239addSjohnlev 	} else if (error == 0) {
41993239addSjohnlev 		*xdev = lvp->v_rdev;
42093239addSjohnlev 	} else {
42193239addSjohnlev 		*xdev = svp->v_rdev;
42293239addSjohnlev 
423ecffa4d6SJohn Levon 		if (svp->v_type != VBLK) {
42493239addSjohnlev 			error = ENOTBLK;
425ecffa4d6SJohn Levon 			goto out;
426ecffa4d6SJohn Levon 		}
42793239addSjohnlev 
42893239addSjohnlev 		if ((error = secpolicy_spec_open(cr, svp, oflag)) != 0)
42993239addSjohnlev 			goto out;
4307c478bd9Sstevel@tonic-gate 	}
4317c478bd9Sstevel@tonic-gate 
43293239addSjohnlev 	if (getmajor(*xdev) >= devcnt) {
43393239addSjohnlev 		error = ENXIO;
43493239addSjohnlev 		goto out;
43593239addSjohnlev 	}
43693239addSjohnlev 
43793239addSjohnlev 	if ((error = VOP_ACCESS(svp, aflag, 0, cr, NULL)) != 0)
43893239addSjohnlev 		goto out;
43993239addSjohnlev 
44093239addSjohnlev out:
44193239addSjohnlev 	if (svp != NULL)
44293239addSjohnlev 		VN_RELE(svp);
44393239addSjohnlev 	if (lvp != NULL)
44493239addSjohnlev 		VN_RELE(lvp);
44593239addSjohnlev 	return (error);
446f127cb91Sfrankho }
447f127cb91Sfrankho 
448f127cb91Sfrankho static int
449f127cb91Sfrankho pcfs_device_ismounted(
450f127cb91Sfrankho 	struct vfs *vfsp,
451f127cb91Sfrankho 	int dos_ldrive,
452f127cb91Sfrankho 	dev_t xdev,
453f127cb91Sfrankho 	int *remounting,
454f127cb91Sfrankho 	dev_t *pseudodev)
455f127cb91Sfrankho {
456f127cb91Sfrankho 	struct pcfs *fsp;
457f127cb91Sfrankho 	int remount = *remounting;
458f127cb91Sfrankho 
4597c478bd9Sstevel@tonic-gate 	/*
4609bd42341Sfrankho 	 * Ensure that this logical drive isn't already mounted, unless
4619bd42341Sfrankho 	 * this is a REMOUNT request.
4629bd42341Sfrankho 	 * Note: The framework will perform this check if the "...:c"
4639bd42341Sfrankho 	 * PCFS-style "logical drive" syntax has not been used and an
4649bd42341Sfrankho 	 * actually existing physical device is backing this filesystem.
465f127cb91Sfrankho 	 * Once all block device drivers support PC-style partitioning,
466f127cb91Sfrankho 	 * this codeblock can be dropped.
4677c478bd9Sstevel@tonic-gate 	 */
468f127cb91Sfrankho 	*pseudodev = xdev;
469f127cb91Sfrankho 
4707c478bd9Sstevel@tonic-gate 	if (dos_ldrive) {
4717c478bd9Sstevel@tonic-gate 		mutex_enter(&pcfslock);
4727c478bd9Sstevel@tonic-gate 		for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt)
4737c478bd9Sstevel@tonic-gate 			if (fsp->pcfs_xdev == xdev &&
474f127cb91Sfrankho 			    fsp->pcfs_ldrive == dos_ldrive) {
4757c478bd9Sstevel@tonic-gate 				mutex_exit(&pcfslock);
476f127cb91Sfrankho 				if (remount) {
4777c478bd9Sstevel@tonic-gate 					return (0);
4787c478bd9Sstevel@tonic-gate 				} else {
4797c478bd9Sstevel@tonic-gate 					return (EBUSY);
4807c478bd9Sstevel@tonic-gate 				}
4817c478bd9Sstevel@tonic-gate 			}
4827c478bd9Sstevel@tonic-gate 		/*
4837c478bd9Sstevel@tonic-gate 		 * Assign a unique device number for the vfs
4847c478bd9Sstevel@tonic-gate 		 * The old way (getudev() + a constantly incrementing
4857c478bd9Sstevel@tonic-gate 		 * major number) was wrong because it changes vfs_dev
4867c478bd9Sstevel@tonic-gate 		 * across mounts and reboots, which breaks nfs file handles.
4877c478bd9Sstevel@tonic-gate 		 * UFS just uses the real dev_t. We can't do that because
4887c478bd9Sstevel@tonic-gate 		 * of the way pcfs opens fdisk partitons (the :c and :d
4897c478bd9Sstevel@tonic-gate 		 * partitions are on the same dev_t). Though that _might_
4907c478bd9Sstevel@tonic-gate 		 * actually be ok, since the file handle contains an
4917c478bd9Sstevel@tonic-gate 		 * absolute block number, it's probably better to make them
4927c478bd9Sstevel@tonic-gate 		 * different. So I think we should retain the original
4937c478bd9Sstevel@tonic-gate 		 * dev_t, but come up with a different minor number based
4947c478bd9Sstevel@tonic-gate 		 * on the logical drive that will _always_ come up the same.
4957c478bd9Sstevel@tonic-gate 		 * For now, we steal the upper 6 bits.
4967c478bd9Sstevel@tonic-gate 		 */
4977c478bd9Sstevel@tonic-gate #ifdef notdef
4987c478bd9Sstevel@tonic-gate 		/* what should we do here? */
4997c478bd9Sstevel@tonic-gate 		if (((getminor(xdev) >> 12) & 0x3F) != 0)
5007c478bd9Sstevel@tonic-gate 			printf("whoops - upper bits used!\n");
5017c478bd9Sstevel@tonic-gate #endif
502f127cb91Sfrankho 		*pseudodev = makedevice(getmajor(xdev),
503f127cb91Sfrankho 		    ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32);
504f127cb91Sfrankho 		if (vfs_devmounting(*pseudodev, vfsp)) {
5057c478bd9Sstevel@tonic-gate 			mutex_exit(&pcfslock);
5067c478bd9Sstevel@tonic-gate 			return (EBUSY);
5077c478bd9Sstevel@tonic-gate 		}
508f127cb91Sfrankho 		if (vfs_devismounted(*pseudodev)) {
5097c478bd9Sstevel@tonic-gate 			mutex_exit(&pcfslock);
510f127cb91Sfrankho 			if (remount) {
5117c478bd9Sstevel@tonic-gate 				return (0);
5127c478bd9Sstevel@tonic-gate 			} else {
5137c478bd9Sstevel@tonic-gate 				return (EBUSY);
5147c478bd9Sstevel@tonic-gate 			}
5157c478bd9Sstevel@tonic-gate 		}
5167c478bd9Sstevel@tonic-gate 		mutex_exit(&pcfslock);
5177c478bd9Sstevel@tonic-gate 	} else {
518f127cb91Sfrankho 		*pseudodev = xdev;
519f127cb91Sfrankho 		if (vfs_devmounting(*pseudodev, vfsp)) {
5207c478bd9Sstevel@tonic-gate 			return (EBUSY);
5217c478bd9Sstevel@tonic-gate 		}
522f127cb91Sfrankho 		if (vfs_devismounted(*pseudodev))
523f127cb91Sfrankho 			if (remount) {
5247c478bd9Sstevel@tonic-gate 				return (0);
5257c478bd9Sstevel@tonic-gate 			} else {
5267c478bd9Sstevel@tonic-gate 				return (EBUSY);
5277c478bd9Sstevel@tonic-gate 			}
5287c478bd9Sstevel@tonic-gate 	}
5297c478bd9Sstevel@tonic-gate 
530f127cb91Sfrankho 	/*
531f127cb91Sfrankho 	 * This is not a remount. Even if MS_REMOUNT was requested,
532f127cb91Sfrankho 	 * the caller needs to proceed as it would on an ordinary
533f127cb91Sfrankho 	 * mount.
534f127cb91Sfrankho 	 */
535f127cb91Sfrankho 	*remounting = 0;
536f127cb91Sfrankho 
537f127cb91Sfrankho 	ASSERT(*pseudodev);
538f127cb91Sfrankho 	return (0);
539f127cb91Sfrankho }
540f127cb91Sfrankho 
541f127cb91Sfrankho /*
542f127cb91Sfrankho  * Get the PCFS-specific mount options from the VFS framework.
543f127cb91Sfrankho  * For "timezone" and "secsize", we need to parse the number
544f127cb91Sfrankho  * ourselves and ensure its validity.
545f127cb91Sfrankho  * Note: "secsize" is deliberately undocumented at this time,
546f127cb91Sfrankho  * it's a workaround for devices (particularly: lofi image files)
547f127cb91Sfrankho  * that don't support the DKIOCGMEDIAINFO ioctl for autodetection.
548f127cb91Sfrankho  */
549f127cb91Sfrankho static void
5509e003ac4Sgd pcfs_parse_mntopts(struct pcfs *fsp)
551f127cb91Sfrankho {
552f127cb91Sfrankho 	char *c;
553f127cb91Sfrankho 	char *endptr;
554f127cb91Sfrankho 	long l;
555f127cb91Sfrankho 	struct vfs *vfsp = fsp->pcfs_vfs;
556f127cb91Sfrankho 
557f127cb91Sfrankho 	ASSERT(fsp->pcfs_secondswest == 0);
558f127cb91Sfrankho 	ASSERT(fsp->pcfs_secsize == 0);
559f127cb91Sfrankho 
560f127cb91Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL))
561f127cb91Sfrankho 		fsp->pcfs_flags |= PCFS_HIDDEN;
562f127cb91Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL))
563f127cb91Sfrankho 		fsp->pcfs_flags |= PCFS_FOLDCASE;
564f127cb91Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL))
565f127cb91Sfrankho 		fsp->pcfs_flags |= PCFS_NOCLAMPTIME;
566f127cb91Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL))
567f127cb91Sfrankho 		fsp->pcfs_flags |= PCFS_NOATIME;
568f127cb91Sfrankho 
569f127cb91Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_TIMEZONE, &c)) {
570f127cb91Sfrankho 		if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
571f127cb91Sfrankho 		    endptr == c + strlen(c)) {
572f127cb91Sfrankho 			/*
573f127cb91Sfrankho 			 * A number alright - in the allowed range ?
574f127cb91Sfrankho 			 */
575f127cb91Sfrankho 			if (l <= -12*3600 || l >= 12*3600) {
576f127cb91Sfrankho 				cmn_err(CE_WARN, "!pcfs: invalid use of "
577f127cb91Sfrankho 				    "'timezone' mount option - %ld "
578f127cb91Sfrankho 				    "is out of range. Assuming 0.", l);
579f127cb91Sfrankho 				l = 0;
580f127cb91Sfrankho 			}
581f127cb91Sfrankho 		} else {
582f127cb91Sfrankho 			cmn_err(CE_WARN, "!pcfs: invalid use of "
583f127cb91Sfrankho 			    "'timezone' mount option - argument %s "
584f127cb91Sfrankho 			    "is not a valid number. Assuming 0.", c);
585f127cb91Sfrankho 			l = 0;
586f127cb91Sfrankho 		}
587f127cb91Sfrankho 		fsp->pcfs_secondswest = l;
588f127cb91Sfrankho 	}
589f127cb91Sfrankho 
5907c478bd9Sstevel@tonic-gate 	/*
591f127cb91Sfrankho 	 * The "secsize=..." mount option is a workaround for the lack of
592f127cb91Sfrankho 	 * lofi(7d) support for DKIOCGMEDIAINFO. If PCFS wants to parse the
593f127cb91Sfrankho 	 * partition table of a disk image and it has been partitioned with
594f127cb91Sfrankho 	 * sector sizes other than 512 bytes, we'd fail on loopback'ed disk
595f127cb91Sfrankho 	 * images.
596f127cb91Sfrankho 	 * That should really be fixed in lofi ... this is a workaround.
5977c478bd9Sstevel@tonic-gate 	 */
598f127cb91Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_SECSIZE, &c)) {
599f127cb91Sfrankho 		if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
600f127cb91Sfrankho 		    endptr == c + strlen(c)) {
601f127cb91Sfrankho 			/*
602f127cb91Sfrankho 			 * A number alright - a valid sector size as well ?
603f127cb91Sfrankho 			 */
604f127cb91Sfrankho 			if (!VALID_SECSIZE(l)) {
605f127cb91Sfrankho 				cmn_err(CE_WARN, "!pcfs: invalid use of "
606f127cb91Sfrankho 				    "'secsize' mount option - %ld is "
607f127cb91Sfrankho 				    "unsupported. Autodetecting.", l);
608f127cb91Sfrankho 				l = 0;
609f127cb91Sfrankho 			}
610f127cb91Sfrankho 		} else {
611f127cb91Sfrankho 			cmn_err(CE_WARN, "!pcfs: invalid use of "
612f127cb91Sfrankho 			    "'secsize' mount option - argument %s "
613f127cb91Sfrankho 			    "is not a valid number. Autodetecting.", c);
614f127cb91Sfrankho 			l = 0;
615f127cb91Sfrankho 		}
616f127cb91Sfrankho 		fsp->pcfs_secsize = l;
617f127cb91Sfrankho 		fsp->pcfs_sdshift = ddi_ffs(l / DEV_BSIZE) - 1;
618f127cb91Sfrankho 	}
619f127cb91Sfrankho }
620f127cb91Sfrankho 
621f127cb91Sfrankho /*
622f127cb91Sfrankho  * vfs operations
623f127cb91Sfrankho  */
624f127cb91Sfrankho 
625f127cb91Sfrankho /*
626f127cb91Sfrankho  * pcfs_mount - backend for VFS_MOUNT() on PCFS.
627f127cb91Sfrankho  */
628f127cb91Sfrankho static int
629f127cb91Sfrankho pcfs_mount(
630f127cb91Sfrankho 	struct vfs *vfsp,
631f127cb91Sfrankho 	struct vnode *mvp,
632f127cb91Sfrankho 	struct mounta *uap,
633f127cb91Sfrankho 	struct cred *cr)
634f127cb91Sfrankho {
635f127cb91Sfrankho 	struct pcfs *fsp;
636f127cb91Sfrankho 	struct vnode *devvp;
637f127cb91Sfrankho 	dev_t pseudodev;
638f127cb91Sfrankho 	dev_t xdev;
639f127cb91Sfrankho 	int dos_ldrive = 0;
640f127cb91Sfrankho 	int error;
641f127cb91Sfrankho 	int remounting;
642f127cb91Sfrankho 
643f127cb91Sfrankho 	if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
644f127cb91Sfrankho 		return (error);
645f127cb91Sfrankho 
646f127cb91Sfrankho 	if (mvp->v_type != VDIR)
647f127cb91Sfrankho 		return (ENOTDIR);
648f127cb91Sfrankho 
649f127cb91Sfrankho 	mutex_enter(&mvp->v_lock);
650f127cb91Sfrankho 	if ((uap->flags & MS_REMOUNT) == 0 &&
651f127cb91Sfrankho 	    (uap->flags & MS_OVERLAY) == 0 &&
652f127cb91Sfrankho 	    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
653f127cb91Sfrankho 		mutex_exit(&mvp->v_lock);
6547c478bd9Sstevel@tonic-gate 		return (EBUSY);
6557c478bd9Sstevel@tonic-gate 	}
656f127cb91Sfrankho 	mutex_exit(&mvp->v_lock);
6577c478bd9Sstevel@tonic-gate 
6587c478bd9Sstevel@tonic-gate 	/*
659f127cb91Sfrankho 	 * PCFS doesn't do mount arguments anymore - everything's a mount
660f127cb91Sfrankho 	 * option these days. In order not to break existing callers, we
661f127cb91Sfrankho 	 * don't reject it yet, just warn that the data (if any) is ignored.
6627c478bd9Sstevel@tonic-gate 	 */
663f127cb91Sfrankho 	if (uap->datalen != 0)
664f127cb91Sfrankho 		cmn_err(CE_WARN, "!pcfs: deprecated use of mount(2) with "
665f127cb91Sfrankho 		    "mount argument structures instead of mount options. "
666f127cb91Sfrankho 		    "Ignoring mount(2) 'dataptr' argument.");
667f127cb91Sfrankho 
668c2aaf90fSgd 	/*
669c2aaf90fSgd 	 * This is needed early, to make sure the access / open calls
670c2aaf90fSgd 	 * are done using the correct mode. Processing this mount option
671c2aaf90fSgd 	 * only when calling pcfs_parse_mntopts() would lead us to attempt
672c2aaf90fSgd 	 * a read/write access to a possibly writeprotected device, and
673c2aaf90fSgd 	 * a readonly mount attempt might fail because of that.
674c2aaf90fSgd 	 */
675c2aaf90fSgd 	if (uap->flags & MS_RDONLY) {
676c2aaf90fSgd 		vfsp->vfs_flag |= VFS_RDONLY;
677c2aaf90fSgd 		vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
678c2aaf90fSgd 	}
679c2aaf90fSgd 
680f127cb91Sfrankho 	/*
681f127cb91Sfrankho 	 * For most filesystems, this is just a lookupname() on the
682f127cb91Sfrankho 	 * mount pathname string. PCFS historically has to do its own
683f127cb91Sfrankho 	 * partition table parsing because not all Solaris architectures
684f127cb91Sfrankho 	 * support all styles of partitioning that PC media can have, and
685f127cb91Sfrankho 	 * hence PCFS understands "device names" that don't map to actual
686f127cb91Sfrankho 	 * physical device nodes. Parsing the "PCFS syntax" for device
687f127cb91Sfrankho 	 * names is done in pcfs_device_identify() - see there.
688f127cb91Sfrankho 	 *
689f127cb91Sfrankho 	 * Once all block device drivers that can host FAT filesystems have
690f127cb91Sfrankho 	 * been enhanced to create device nodes for all PC-style partitions,
691f127cb91Sfrankho 	 * this code can go away.
692f127cb91Sfrankho 	 */
693f127cb91Sfrankho 	if (error = pcfs_device_identify(vfsp, uap, cr, &dos_ldrive, &xdev))
694f127cb91Sfrankho 		return (error);
695f127cb91Sfrankho 
696f127cb91Sfrankho 	/*
697f127cb91Sfrankho 	 * As with looking up the actual device to mount, PCFS cannot rely
698f127cb91Sfrankho 	 * on just the checks done by vfs_ismounted() whether a given device
699f127cb91Sfrankho 	 * is mounted already. The additional check against the "PCFS syntax"
700f127cb91Sfrankho 	 * is done in  pcfs_device_ismounted().
701f127cb91Sfrankho 	 */
702f127cb91Sfrankho 	remounting = (uap->flags & MS_REMOUNT);
703f127cb91Sfrankho 
704f127cb91Sfrankho 	if (error = pcfs_device_ismounted(vfsp, dos_ldrive, xdev, &remounting,
705f127cb91Sfrankho 	    &pseudodev))
706f127cb91Sfrankho 		return (error);
707f127cb91Sfrankho 
708f127cb91Sfrankho 	if (remounting)
709f127cb91Sfrankho 		return (0);
710f127cb91Sfrankho 
711f127cb91Sfrankho 	/*
712f127cb91Sfrankho 	 * Mount the filesystem.
713f127cb91Sfrankho 	 * An instance structure is required before the attempt to locate
714f127cb91Sfrankho 	 * and parse the FAT BPB. This is because mount options may change
715f127cb91Sfrankho 	 * the behaviour of the filesystem type matching code. Precreate
716f127cb91Sfrankho 	 * it and fill it in to a degree that allows parsing the mount
717f127cb91Sfrankho 	 * options.
718f127cb91Sfrankho 	 */
719f127cb91Sfrankho 	devvp = makespecvp(xdev, VBLK);
720f127cb91Sfrankho 	if (IS_SWAPVP(devvp)) {
721f127cb91Sfrankho 		VN_RELE(devvp);
722f127cb91Sfrankho 		return (EBUSY);
723f127cb91Sfrankho 	}
724f127cb91Sfrankho 	error = VOP_OPEN(&devvp,
725da6c28aaSamw 	    (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD | FWRITE, cr, NULL);
726f127cb91Sfrankho 	if (error) {
727f127cb91Sfrankho 		VN_RELE(devvp);
728f127cb91Sfrankho 		return (error);
7297c478bd9Sstevel@tonic-gate 	}
7307c478bd9Sstevel@tonic-gate 
731f127cb91Sfrankho 	fsp = kmem_zalloc(sizeof (*fsp), KM_SLEEP);
7327c478bd9Sstevel@tonic-gate 	fsp->pcfs_vfs = vfsp;
7337c478bd9Sstevel@tonic-gate 	fsp->pcfs_xdev = xdev;
734f127cb91Sfrankho 	fsp->pcfs_devvp = devvp;
735f127cb91Sfrankho 	fsp->pcfs_ldrive = dos_ldrive;
7367c478bd9Sstevel@tonic-gate 	mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL);
7377c478bd9Sstevel@tonic-gate 
7389e003ac4Sgd 	pcfs_parse_mntopts(fsp);
739f127cb91Sfrankho 
740f127cb91Sfrankho 	/*
741f127cb91Sfrankho 	 * This is the actual "mount" - the PCFS superblock check.
742f127cb91Sfrankho 	 *
743f127cb91Sfrankho 	 * Find the requested logical drive and the FAT BPB therein.
744f127cb91Sfrankho 	 * Check device type and flag the instance if media is removeable.
745f127cb91Sfrankho 	 *
746f127cb91Sfrankho 	 * Initializes most members of the filesystem instance structure.
747f127cb91Sfrankho 	 * Returns EINVAL if no valid BPB can be found. Other errors may
748f127cb91Sfrankho 	 * occur after I/O failures, or when invalid / unparseable partition
749f127cb91Sfrankho 	 * tables are encountered.
750f127cb91Sfrankho 	 */
751f127cb91Sfrankho 	if (error = pc_getfattype(fsp))
752f127cb91Sfrankho 		goto errout;
753f127cb91Sfrankho 
754c2aaf90fSgd 	/*
755c2aaf90fSgd 	 * Now that the BPB has been parsed, this structural information
756c2aaf90fSgd 	 * is available and known to be valid. Initialize the VFS.
757c2aaf90fSgd 	 */
758c2aaf90fSgd 	vfsp->vfs_data = fsp;
759c2aaf90fSgd 	vfsp->vfs_dev = pseudodev;
760c2aaf90fSgd 	vfsp->vfs_fstype = pcfstype;
761c2aaf90fSgd 	vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype);
762c2aaf90fSgd 	vfsp->vfs_bcount = 0;
763c2aaf90fSgd 	vfsp->vfs_bsize = fsp->pcfs_clsize;
764c2aaf90fSgd 
765f127cb91Sfrankho 	/*
766f127cb91Sfrankho 	 * Validate that we can access the FAT and that it is, to the
767f127cb91Sfrankho 	 * degree we can verify here, self-consistent.
768f127cb91Sfrankho 	 */
769f127cb91Sfrankho 	if (error = pc_verify(fsp))
770f127cb91Sfrankho 		goto errout;
771f127cb91Sfrankho 
772f127cb91Sfrankho 	/*
773f127cb91Sfrankho 	 * Record the time of the mount, to return as an "approximate"
774f127cb91Sfrankho 	 * timestamp for the FAT root directory. Since FAT roots don't
775f127cb91Sfrankho 	 * have timestamps, this is less confusing to the user than
776f127cb91Sfrankho 	 * claiming "zero" / Jan/01/1970.
777f127cb91Sfrankho 	 */
778f127cb91Sfrankho 	gethrestime(&fsp->pcfs_mounttime);
779f127cb91Sfrankho 
780f127cb91Sfrankho 	/*
781f127cb91Sfrankho 	 * Fix up the mount options. Because "noatime" is made default on
782f127cb91Sfrankho 	 * removeable media only, a fixed disk will have neither "atime"
783f127cb91Sfrankho 	 * nor "noatime" set. We set the options explicitly depending on
784f127cb91Sfrankho 	 * the PCFS_NOATIME flag, to inform the user of what applies.
785f127cb91Sfrankho 	 * Mount option cancellation will take care that the mutually
786f127cb91Sfrankho 	 * exclusive 'other' is cleared.
787f127cb91Sfrankho 	 */
788f127cb91Sfrankho 	vfs_setmntopt(vfsp,
789f127cb91Sfrankho 	    fsp->pcfs_flags & PCFS_NOATIME ? MNTOPT_NOATIME : MNTOPT_ATIME,
790f127cb91Sfrankho 	    NULL, 0);
791f127cb91Sfrankho 
792f127cb91Sfrankho 	/*
793f127cb91Sfrankho 	 * All clear - insert the FS instance into PCFS' list.
794f127cb91Sfrankho 	 */
7957c478bd9Sstevel@tonic-gate 	mutex_enter(&pcfslock);
7967c478bd9Sstevel@tonic-gate 	fsp->pcfs_nxt = pc_mounttab;
7977c478bd9Sstevel@tonic-gate 	pc_mounttab = fsp;
7987c478bd9Sstevel@tonic-gate 	mutex_exit(&pcfslock);
799264a6e74Sfrankho 	atomic_inc_32(&pcfs_mountcount);
8007c478bd9Sstevel@tonic-gate 	return (0);
8017c478bd9Sstevel@tonic-gate 
802f127cb91Sfrankho errout:
803f127cb91Sfrankho 	(void) VOP_CLOSE(devvp,
804f127cb91Sfrankho 	    vfsp->vfs_flag & VFS_RDONLY ? FREAD : FREAD | FWRITE,
805da6c28aaSamw 	    1, (offset_t)0, cr, NULL);
806f127cb91Sfrankho 	VN_RELE(devvp);
807f127cb91Sfrankho 	mutex_destroy(&fsp->pcfs_lock);
808f127cb91Sfrankho 	kmem_free(fsp, sizeof (*fsp));
809f127cb91Sfrankho 	return (error);
810f127cb91Sfrankho 
811f127cb91Sfrankho }
8127c478bd9Sstevel@tonic-gate 
8137c478bd9Sstevel@tonic-gate static int
8147c478bd9Sstevel@tonic-gate pcfs_unmount(
8157c478bd9Sstevel@tonic-gate 	struct vfs *vfsp,
8167c478bd9Sstevel@tonic-gate 	int flag,
8177c478bd9Sstevel@tonic-gate 	struct cred *cr)
8187c478bd9Sstevel@tonic-gate {
8197c478bd9Sstevel@tonic-gate 	struct pcfs *fsp, *fsp1;
8207c478bd9Sstevel@tonic-gate 
8217c478bd9Sstevel@tonic-gate 	if (secpolicy_fs_unmount(cr, vfsp) != 0)
8227c478bd9Sstevel@tonic-gate 		return (EPERM);
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
825264a6e74Sfrankho 
8267c478bd9Sstevel@tonic-gate 	/*
8277c478bd9Sstevel@tonic-gate 	 * We don't have to lock fsp because the VVFSLOCK in vfs layer will
8287c478bd9Sstevel@tonic-gate 	 * prevent lookuppn from crossing the mount point.
829264a6e74Sfrankho 	 * If this is not a forced umount request and there's ongoing I/O,
830264a6e74Sfrankho 	 * don't allow the mount to proceed.
8317c478bd9Sstevel@tonic-gate 	 */
832264a6e74Sfrankho 	if (flag & MS_FORCE)
833264a6e74Sfrankho 		vfsp->vfs_flag |= VFS_UNMOUNTED;
834264a6e74Sfrankho 	else if (fsp->pcfs_nrefs)
8357c478bd9Sstevel@tonic-gate 		return (EBUSY);
836264a6e74Sfrankho 
837264a6e74Sfrankho 	mutex_enter(&pcfslock);
8387c478bd9Sstevel@tonic-gate 
8397c478bd9Sstevel@tonic-gate 	/*
840264a6e74Sfrankho 	 * If this is a forced umount request or if the fs instance has
841264a6e74Sfrankho 	 * been marked as beyond recovery, allow the umount to proceed
842264a6e74Sfrankho 	 * regardless of state. pc_diskchanged() forcibly releases all
843264a6e74Sfrankho 	 * inactive vnodes/pcnodes.
8447c478bd9Sstevel@tonic-gate 	 */
845264a6e74Sfrankho 	if (flag & MS_FORCE || fsp->pcfs_flags & PCFS_IRRECOV) {
8467c478bd9Sstevel@tonic-gate 		rw_enter(&pcnodes_lock, RW_WRITER);
8477c478bd9Sstevel@tonic-gate 		pc_diskchanged(fsp);
8487c478bd9Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
8497c478bd9Sstevel@tonic-gate 	}
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 	/* now there should be no pcp node on pcfhead or pcdhead. */
8527c478bd9Sstevel@tonic-gate 
8537c478bd9Sstevel@tonic-gate 	if (fsp == pc_mounttab) {
8547c478bd9Sstevel@tonic-gate 		pc_mounttab = fsp->pcfs_nxt;
8557c478bd9Sstevel@tonic-gate 	} else {
8567c478bd9Sstevel@tonic-gate 		for (fsp1 = pc_mounttab; fsp1 != NULL; fsp1 = fsp1->pcfs_nxt)
8577c478bd9Sstevel@tonic-gate 			if (fsp1->pcfs_nxt == fsp)
8587c478bd9Sstevel@tonic-gate 				fsp1->pcfs_nxt = fsp->pcfs_nxt;
8597c478bd9Sstevel@tonic-gate 	}
8607c478bd9Sstevel@tonic-gate 
8617c478bd9Sstevel@tonic-gate 	mutex_exit(&pcfslock);
8627c478bd9Sstevel@tonic-gate 
863264a6e74Sfrankho 	/*
864264a6e74Sfrankho 	 * Since we support VFS_FREEVFS(), there's no need to
865264a6e74Sfrankho 	 * free the fsp right now. The framework will tell us
866264a6e74Sfrankho 	 * when the right time to do so has arrived by calling
867264a6e74Sfrankho 	 * into pcfs_freevfs.
868264a6e74Sfrankho 	 */
8697c478bd9Sstevel@tonic-gate 	return (0);
8707c478bd9Sstevel@tonic-gate }
8717c478bd9Sstevel@tonic-gate 
8727c478bd9Sstevel@tonic-gate /*
8737c478bd9Sstevel@tonic-gate  * find root of pcfs
8747c478bd9Sstevel@tonic-gate  */
8757c478bd9Sstevel@tonic-gate static int
8767c478bd9Sstevel@tonic-gate pcfs_root(
8777c478bd9Sstevel@tonic-gate 	struct vfs *vfsp,
8787c478bd9Sstevel@tonic-gate 	struct vnode **vpp)
8797c478bd9Sstevel@tonic-gate {
8807c478bd9Sstevel@tonic-gate 	struct pcfs *fsp;
8817c478bd9Sstevel@tonic-gate 	struct pcnode *pcp;
8827c478bd9Sstevel@tonic-gate 	int error;
8837c478bd9Sstevel@tonic-gate 
8847c478bd9Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
8857c478bd9Sstevel@tonic-gate 	if (error = pc_lockfs(fsp, 0, 0))
8867c478bd9Sstevel@tonic-gate 		return (error);
887264a6e74Sfrankho 
8887c478bd9Sstevel@tonic-gate 	pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
8897c478bd9Sstevel@tonic-gate 	pc_unlockfs(fsp);
8907c478bd9Sstevel@tonic-gate 	*vpp = PCTOV(pcp);
8917c478bd9Sstevel@tonic-gate 	pcp->pc_flags |= PC_EXTERNAL;
8927c478bd9Sstevel@tonic-gate 	return (0);
8937c478bd9Sstevel@tonic-gate }
8947c478bd9Sstevel@tonic-gate 
8957c478bd9Sstevel@tonic-gate /*
8967c478bd9Sstevel@tonic-gate  * Get file system statistics.
8977c478bd9Sstevel@tonic-gate  */
8987c478bd9Sstevel@tonic-gate static int
8997c478bd9Sstevel@tonic-gate pcfs_statvfs(
9007c478bd9Sstevel@tonic-gate 	struct vfs *vfsp,
9017c478bd9Sstevel@tonic-gate 	struct statvfs64 *sp)
9027c478bd9Sstevel@tonic-gate {
9037c478bd9Sstevel@tonic-gate 	struct pcfs *fsp;
9047c478bd9Sstevel@tonic-gate 	int error;
9057c478bd9Sstevel@tonic-gate 	dev32_t d32;
9067c478bd9Sstevel@tonic-gate 
9077c478bd9Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
9087c478bd9Sstevel@tonic-gate 	error = pc_getfat(fsp);
9097c478bd9Sstevel@tonic-gate 	if (error)
9107c478bd9Sstevel@tonic-gate 		return (error);
9117c478bd9Sstevel@tonic-gate 	bzero(sp, sizeof (*sp));
9127c478bd9Sstevel@tonic-gate 	sp->f_bsize = sp->f_frsize = fsp->pcfs_clsize;
9137c478bd9Sstevel@tonic-gate 	sp->f_blocks = (fsblkcnt64_t)fsp->pcfs_ncluster;
9147c478bd9Sstevel@tonic-gate 	sp->f_bavail = sp->f_bfree = (fsblkcnt64_t)pc_freeclusters(fsp);
9157c478bd9Sstevel@tonic-gate 	sp->f_files = (fsfilcnt64_t)-1;
9167c478bd9Sstevel@tonic-gate 	sp->f_ffree = (fsfilcnt64_t)-1;
9177c478bd9Sstevel@tonic-gate 	sp->f_favail = (fsfilcnt64_t)-1;
9187c478bd9Sstevel@tonic-gate #ifdef notdef
9197c478bd9Sstevel@tonic-gate 	(void) cmpldev(&d32, fsp->pcfs_devvp->v_rdev);
9207c478bd9Sstevel@tonic-gate #endif /* notdef */
9217c478bd9Sstevel@tonic-gate 	(void) cmpldev(&d32, vfsp->vfs_dev);
9227c478bd9Sstevel@tonic-gate 	sp->f_fsid = d32;
9237c478bd9Sstevel@tonic-gate 	(void) strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name);
9247c478bd9Sstevel@tonic-gate 	sp->f_flag = vf_to_stf(vfsp->vfs_flag);
925e8f766d8Sbatschul 	sp->f_namemax = PCMAXNAMLEN;
9267c478bd9Sstevel@tonic-gate 	return (0);
9277c478bd9Sstevel@tonic-gate }
9287c478bd9Sstevel@tonic-gate 
9297c478bd9Sstevel@tonic-gate static int
9307c478bd9Sstevel@tonic-gate pc_syncfsnodes(struct pcfs *fsp)
9317c478bd9Sstevel@tonic-gate {
9327c478bd9Sstevel@tonic-gate 	struct pchead *hp;
9337c478bd9Sstevel@tonic-gate 	struct pcnode *pcp;
9347c478bd9Sstevel@tonic-gate 	int error;
9357c478bd9Sstevel@tonic-gate 
9367c478bd9Sstevel@tonic-gate 	if (error = pc_lockfs(fsp, 0, 0))
9377c478bd9Sstevel@tonic-gate 		return (error);
9387c478bd9Sstevel@tonic-gate 
9397c478bd9Sstevel@tonic-gate 	if (!(error = pc_syncfat(fsp))) {
9407c478bd9Sstevel@tonic-gate 		hp = pcfhead;
9417c478bd9Sstevel@tonic-gate 		while (hp < & pcfhead [ NPCHASH ]) {
9427c478bd9Sstevel@tonic-gate 			rw_enter(&pcnodes_lock, RW_READER);
9437c478bd9Sstevel@tonic-gate 			pcp = hp->pch_forw;
9447c478bd9Sstevel@tonic-gate 			while (pcp != (struct pcnode *)hp) {
9457c478bd9Sstevel@tonic-gate 				if (VFSTOPCFS(PCTOV(pcp) -> v_vfsp) == fsp)
9467c478bd9Sstevel@tonic-gate 					if (error = pc_nodesync(pcp))
9477c478bd9Sstevel@tonic-gate 						break;
9487c478bd9Sstevel@tonic-gate 				pcp = pcp -> pc_forw;
9497c478bd9Sstevel@tonic-gate 			}
9507c478bd9Sstevel@tonic-gate 			rw_exit(&pcnodes_lock);
9517c478bd9Sstevel@tonic-gate 			if (error)
9527c478bd9Sstevel@tonic-gate 				break;
9537c478bd9Sstevel@tonic-gate 			hp++;
9547c478bd9Sstevel@tonic-gate 		}
9557c478bd9Sstevel@tonic-gate 	}
9567c478bd9Sstevel@tonic-gate 	pc_unlockfs(fsp);
9577c478bd9Sstevel@tonic-gate 	return (error);
9587c478bd9Sstevel@tonic-gate }
9597c478bd9Sstevel@tonic-gate 
9607c478bd9Sstevel@tonic-gate /*
9617c478bd9Sstevel@tonic-gate  * Flush any pending I/O.
9627c478bd9Sstevel@tonic-gate  */
9637c478bd9Sstevel@tonic-gate /*ARGSUSED*/
9647c478bd9Sstevel@tonic-gate static int
9657c478bd9Sstevel@tonic-gate pcfs_sync(
9667c478bd9Sstevel@tonic-gate 	struct vfs *vfsp,
9677c478bd9Sstevel@tonic-gate 	short flag,
9687c478bd9Sstevel@tonic-gate 	struct cred *cr)
9697c478bd9Sstevel@tonic-gate {
9707c478bd9Sstevel@tonic-gate 	struct pcfs *fsp;
9717c478bd9Sstevel@tonic-gate 	int error = 0;
9727c478bd9Sstevel@tonic-gate 
9737c478bd9Sstevel@tonic-gate 	/* this prevents the filesystem from being umounted. */
9747c478bd9Sstevel@tonic-gate 	mutex_enter(&pcfslock);
9757c478bd9Sstevel@tonic-gate 	if (vfsp != NULL) {
9767c478bd9Sstevel@tonic-gate 		fsp = VFSTOPCFS(vfsp);
9777c478bd9Sstevel@tonic-gate 		if (!(fsp->pcfs_flags & PCFS_IRRECOV)) {
9787c478bd9Sstevel@tonic-gate 			error = pc_syncfsnodes(fsp);
9797c478bd9Sstevel@tonic-gate 		} else {
9807c478bd9Sstevel@tonic-gate 			rw_enter(&pcnodes_lock, RW_WRITER);
9817c478bd9Sstevel@tonic-gate 			pc_diskchanged(fsp);
9827c478bd9Sstevel@tonic-gate 			rw_exit(&pcnodes_lock);
9837c478bd9Sstevel@tonic-gate 			error = EIO;
9847c478bd9Sstevel@tonic-gate 		}
9857c478bd9Sstevel@tonic-gate 	} else {
9867c478bd9Sstevel@tonic-gate 		fsp = pc_mounttab;
9877c478bd9Sstevel@tonic-gate 		while (fsp != NULL) {
9887c478bd9Sstevel@tonic-gate 			if (fsp->pcfs_flags & PCFS_IRRECOV) {
9897c478bd9Sstevel@tonic-gate 				rw_enter(&pcnodes_lock, RW_WRITER);
9907c478bd9Sstevel@tonic-gate 				pc_diskchanged(fsp);
9917c478bd9Sstevel@tonic-gate 				rw_exit(&pcnodes_lock);
9927c478bd9Sstevel@tonic-gate 				error = EIO;
9937c478bd9Sstevel@tonic-gate 				break;
9947c478bd9Sstevel@tonic-gate 			}
9957c478bd9Sstevel@tonic-gate 			error = pc_syncfsnodes(fsp);
9967c478bd9Sstevel@tonic-gate 			if (error) break;
9977c478bd9Sstevel@tonic-gate 			fsp = fsp->pcfs_nxt;
9987c478bd9Sstevel@tonic-gate 		}
9997c478bd9Sstevel@tonic-gate 	}
10007c478bd9Sstevel@tonic-gate 	mutex_exit(&pcfslock);
10017c478bd9Sstevel@tonic-gate 	return (error);
10027c478bd9Sstevel@tonic-gate }
10037c478bd9Sstevel@tonic-gate 
10047c478bd9Sstevel@tonic-gate int
10057c478bd9Sstevel@tonic-gate pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing)
10067c478bd9Sstevel@tonic-gate {
1007264a6e74Sfrankho 	int err;
1008264a6e74Sfrankho 
10097c478bd9Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_IRRECOV) && !releasing)
10107c478bd9Sstevel@tonic-gate 		return (EIO);
10117c478bd9Sstevel@tonic-gate 
10127c478bd9Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_LOCKED) && (fsp->pcfs_owner == curthread)) {
10137c478bd9Sstevel@tonic-gate 		fsp->pcfs_count++;
10147c478bd9Sstevel@tonic-gate 	} else {
10157c478bd9Sstevel@tonic-gate 		mutex_enter(&fsp->pcfs_lock);
10167c478bd9Sstevel@tonic-gate 		if (fsp->pcfs_flags & PCFS_LOCKED)
10177c478bd9Sstevel@tonic-gate 			panic("pc_lockfs");
10187c478bd9Sstevel@tonic-gate 		/*
10197c478bd9Sstevel@tonic-gate 		 * We check the IRRECOV bit again just in case somebody
10207c478bd9Sstevel@tonic-gate 		 * snuck past the initial check but then got held up before
10217c478bd9Sstevel@tonic-gate 		 * they could grab the lock.  (And in the meantime someone
10227c478bd9Sstevel@tonic-gate 		 * had grabbed the lock and set the bit)
10237c478bd9Sstevel@tonic-gate 		 */
10240576819eSwyllys 		if (!diskchanged && !(fsp->pcfs_flags & PCFS_IRRECOV)) {
1025264a6e74Sfrankho 			if ((err = pc_getfat(fsp))) {
1026264a6e74Sfrankho 				mutex_exit(&fsp->pcfs_lock);
10270576819eSwyllys 				return (err);
1028264a6e74Sfrankho 			}
10290576819eSwyllys 		}
10307c478bd9Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_LOCKED;
10317c478bd9Sstevel@tonic-gate 		fsp->pcfs_owner = curthread;
10327c478bd9Sstevel@tonic-gate 		fsp->pcfs_count++;
10337c478bd9Sstevel@tonic-gate 	}
10347c478bd9Sstevel@tonic-gate 	return (0);
10357c478bd9Sstevel@tonic-gate }
10367c478bd9Sstevel@tonic-gate 
10377c478bd9Sstevel@tonic-gate void
10387c478bd9Sstevel@tonic-gate pc_unlockfs(struct pcfs *fsp)
10397c478bd9Sstevel@tonic-gate {
10407c478bd9Sstevel@tonic-gate 
10417c478bd9Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_LOCKED) == 0)
10427c478bd9Sstevel@tonic-gate 		panic("pc_unlockfs");
10437c478bd9Sstevel@tonic-gate 	if (--fsp->pcfs_count < 0)
10447c478bd9Sstevel@tonic-gate 		panic("pc_unlockfs: count");
10457c478bd9Sstevel@tonic-gate 	if (fsp->pcfs_count == 0) {
10467c478bd9Sstevel@tonic-gate 		fsp->pcfs_flags &= ~PCFS_LOCKED;
10477c478bd9Sstevel@tonic-gate 		fsp->pcfs_owner = 0;
10487c478bd9Sstevel@tonic-gate 		mutex_exit(&fsp->pcfs_lock);
10497c478bd9Sstevel@tonic-gate 	}
10507c478bd9Sstevel@tonic-gate }
10517c478bd9Sstevel@tonic-gate 
1052f127cb91Sfrankho int
1053f127cb91Sfrankho pc_syncfat(struct pcfs *fsp)
10547c478bd9Sstevel@tonic-gate {
1055f127cb91Sfrankho 	struct buf *bp;
1056f127cb91Sfrankho 	int nfat;
1057f127cb91Sfrankho 	int	error = 0;
1058f127cb91Sfrankho 	struct fat_od_fsi *fsinfo_disk;
10597c478bd9Sstevel@tonic-gate 
1060f127cb91Sfrankho 	if ((fsp->pcfs_fatp == (uchar_t *)0) ||
1061f127cb91Sfrankho 	    !(fsp->pcfs_flags & PCFS_FATMOD))
1062f127cb91Sfrankho 		return (0);
1063f127cb91Sfrankho 	/*
1064f127cb91Sfrankho 	 * write out all copies of FATs
1065f127cb91Sfrankho 	 */
1066f127cb91Sfrankho 	fsp->pcfs_flags &= ~PCFS_FATMOD;
1067f127cb91Sfrankho 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
1068f127cb91Sfrankho 	for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) {
1069f127cb91Sfrankho 		error = pc_writefat(fsp, pc_dbdaddr(fsp,
1070f127cb91Sfrankho 		    fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec));
1071f127cb91Sfrankho 		if (error) {
1072f127cb91Sfrankho 			pc_mark_irrecov(fsp);
1073f127cb91Sfrankho 			return (EIO);
1074f127cb91Sfrankho 		}
1075f127cb91Sfrankho 	}
1076f127cb91Sfrankho 	pc_clear_fatchanges(fsp);
10777c478bd9Sstevel@tonic-gate 
1078f127cb91Sfrankho 	/*
1079f127cb91Sfrankho 	 * Write out fsinfo sector.
1080f127cb91Sfrankho 	 */
1081f127cb91Sfrankho 	if (IS_FAT32(fsp)) {
1082f127cb91Sfrankho 		bp = bread(fsp->pcfs_xdev,
1083f127cb91Sfrankho 		    pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
1084f127cb91Sfrankho 		if (bp->b_flags & (B_ERROR | B_STALE)) {
1085f127cb91Sfrankho 			error = geterror(bp);
1086f127cb91Sfrankho 		}
1087f127cb91Sfrankho 		fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
1088f127cb91Sfrankho 		if (!error && FSISIG_OK(fsinfo_disk)) {
1089f127cb91Sfrankho 			fsinfo_disk->fsi_incore.fs_free_clusters =
1090f127cb91Sfrankho 			    LE_32(fsp->pcfs_fsinfo.fs_free_clusters);
1091f127cb91Sfrankho 			fsinfo_disk->fsi_incore.fs_next_free =
1092f127cb91Sfrankho 			    LE_32(FSINFO_UNKNOWN);
1093f127cb91Sfrankho 			bwrite2(bp);
1094f127cb91Sfrankho 			error = geterror(bp);
1095f127cb91Sfrankho 		}
1096f127cb91Sfrankho 		brelse(bp);
1097f127cb91Sfrankho 		if (error) {
1098f127cb91Sfrankho 			pc_mark_irrecov(fsp);
1099f127cb91Sfrankho 			return (EIO);
1100f127cb91Sfrankho 		}
1101f127cb91Sfrankho 	}
1102f127cb91Sfrankho 	return (0);
1103f127cb91Sfrankho }
1104f127cb91Sfrankho 
1105f127cb91Sfrankho void
1106f127cb91Sfrankho pc_invalfat(struct pcfs *fsp)
1107f127cb91Sfrankho {
1108f127cb91Sfrankho 	struct pcfs *xfsp;
1109f127cb91Sfrankho 	int mount_cnt = 0;
1110f127cb91Sfrankho 
1111f127cb91Sfrankho 	if (fsp->pcfs_fatp == (uchar_t *)0)
1112f127cb91Sfrankho 		panic("pc_invalfat");
1113f127cb91Sfrankho 	/*
1114f127cb91Sfrankho 	 * Release FAT
1115f127cb91Sfrankho 	 */
1116f127cb91Sfrankho 	kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsec * fsp->pcfs_secsize);
1117f127cb91Sfrankho 	fsp->pcfs_fatp = NULL;
1118f127cb91Sfrankho 	kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize);
1119f127cb91Sfrankho 	fsp->pcfs_fat_changemap = NULL;
1120f127cb91Sfrankho 	/*
1121f127cb91Sfrankho 	 * Invalidate all the blocks associated with the device.
1122f127cb91Sfrankho 	 * Not needed if stateless.
1123f127cb91Sfrankho 	 */
1124f127cb91Sfrankho 	for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt)
1125f127cb91Sfrankho 		if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev)
1126f127cb91Sfrankho 			mount_cnt++;
1127f127cb91Sfrankho 
1128f127cb91Sfrankho 	if (!mount_cnt)
1129f127cb91Sfrankho 		binval(fsp->pcfs_xdev);
1130f127cb91Sfrankho 	/*
1131f127cb91Sfrankho 	 * close mounted device
1132f127cb91Sfrankho 	 */
1133f127cb91Sfrankho 	(void) VOP_CLOSE(fsp->pcfs_devvp,
1134f127cb91Sfrankho 	    (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
1135da6c28aaSamw 	    1, (offset_t)0, CRED(), NULL);
1136f127cb91Sfrankho }
1137f127cb91Sfrankho 
1138f127cb91Sfrankho void
1139f127cb91Sfrankho pc_badfs(struct pcfs *fsp)
1140f127cb91Sfrankho {
1141f127cb91Sfrankho 	cmn_err(CE_WARN, "corrupted PC file system on dev (%x.%x):%d\n",
1142f127cb91Sfrankho 	    getmajor(fsp->pcfs_devvp->v_rdev),
1143f127cb91Sfrankho 	    getminor(fsp->pcfs_devvp->v_rdev), fsp->pcfs_ldrive);
1144f127cb91Sfrankho }
1145f127cb91Sfrankho 
1146f127cb91Sfrankho /*
1147f127cb91Sfrankho  * The problem with supporting NFS on the PCFS filesystem is that there
1148f127cb91Sfrankho  * is no good place to keep the generation number. The only possible
1149f127cb91Sfrankho  * place is inside a directory entry. There are a few words that we
1150f127cb91Sfrankho  * don't use - they store NT & OS/2 attributes, and the creation/last access
1151f127cb91Sfrankho  * time of the file - but it seems wrong to use them. In addition, directory
1152f127cb91Sfrankho  * entries come and go. If a directory is removed completely, its directory
1153f127cb91Sfrankho  * blocks are freed and the generation numbers are lost. Whereas in ufs,
1154f127cb91Sfrankho  * inode blocks are dedicated for inodes, so the generation numbers are
1155f127cb91Sfrankho  * permanently kept on the disk.
1156f127cb91Sfrankho  */
1157f127cb91Sfrankho static int
1158f127cb91Sfrankho pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp)
1159f127cb91Sfrankho {
1160f127cb91Sfrankho 	struct pcnode *pcp;
1161f127cb91Sfrankho 	struct pc_fid *pcfid;
1162f127cb91Sfrankho 	struct pcfs *fsp;
1163f127cb91Sfrankho 	struct pcdir *ep;
1164f127cb91Sfrankho 	daddr_t eblkno;
1165f127cb91Sfrankho 	int eoffset;
1166f127cb91Sfrankho 	struct buf *bp;
1167f127cb91Sfrankho 	int error;
1168f127cb91Sfrankho 	pc_cluster32_t	cn;
1169f127cb91Sfrankho 
1170f127cb91Sfrankho 	pcfid = (struct pc_fid *)fidp;
1171f127cb91Sfrankho 	fsp = VFSTOPCFS(vfsp);
1172f127cb91Sfrankho 
1173f127cb91Sfrankho 	error = pc_lockfs(fsp, 0, 0);
1174f127cb91Sfrankho 	if (error) {
1175f127cb91Sfrankho 		*vpp = NULL;
1176f127cb91Sfrankho 		return (error);
1177f127cb91Sfrankho 	}
1178f127cb91Sfrankho 
1179f127cb91Sfrankho 	if (pcfid->pcfid_block == 0) {
1180f127cb91Sfrankho 		pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
1181f127cb91Sfrankho 		pcp->pc_flags |= PC_EXTERNAL;
1182f127cb91Sfrankho 		*vpp = PCTOV(pcp);
1183f127cb91Sfrankho 		pc_unlockfs(fsp);
1184f127cb91Sfrankho 		return (0);
1185f127cb91Sfrankho 	}
1186f127cb91Sfrankho 	eblkno = pcfid->pcfid_block;
1187f127cb91Sfrankho 	eoffset = pcfid->pcfid_offset;
1188f127cb91Sfrankho 
1189f127cb91Sfrankho 	if ((pc_dbtocl(fsp,
1190f127cb91Sfrankho 	    eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) ||
1191f127cb91Sfrankho 	    (eoffset > fsp->pcfs_clsize)) {
1192f127cb91Sfrankho 		pc_unlockfs(fsp);
1193f127cb91Sfrankho 		*vpp = NULL;
1194f127cb91Sfrankho 		return (EINVAL);
1195f127cb91Sfrankho 	}
1196f127cb91Sfrankho 
1197f127cb91Sfrankho 	if (eblkno >= fsp->pcfs_datastart || (eblkno - fsp->pcfs_rdirstart)
1198f127cb91Sfrankho 	    < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
1199f127cb91Sfrankho 		bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
1200f127cb91Sfrankho 		    fsp->pcfs_clsize);
1201f127cb91Sfrankho 	} else {
1202f127cb91Sfrankho 		/*
1203f127cb91Sfrankho 		 * This is an access "backwards" into the FAT12/FAT16
1204f127cb91Sfrankho 		 * root directory. A better code structure would
1205f127cb91Sfrankho 		 * significantly improve maintainability here ...
1206f127cb91Sfrankho 		 */
1207f127cb91Sfrankho 		bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
1208f127cb91Sfrankho 		    (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize);
1209f127cb91Sfrankho 	}
1210f127cb91Sfrankho 	if (bp->b_flags & (B_ERROR | B_STALE)) {
1211f127cb91Sfrankho 		error = geterror(bp);
1212f127cb91Sfrankho 		brelse(bp);
1213f127cb91Sfrankho 		if (error)
1214f127cb91Sfrankho 			pc_mark_irrecov(fsp);
1215f127cb91Sfrankho 		*vpp = NULL;
1216f127cb91Sfrankho 		pc_unlockfs(fsp);
1217f127cb91Sfrankho 		return (error);
1218f127cb91Sfrankho 	}
1219f127cb91Sfrankho 	ep = (struct pcdir *)(bp->b_un.b_addr + eoffset);
1220f127cb91Sfrankho 	/*
1221f127cb91Sfrankho 	 * Ok, if this is a valid file handle that we gave out,
1222f127cb91Sfrankho 	 * then simply ensuring that the creation time matches,
1223f127cb91Sfrankho 	 * the entry has not been deleted, and it has a valid first
1224f127cb91Sfrankho 	 * character should be enough.
1225f127cb91Sfrankho 	 *
1226f127cb91Sfrankho 	 * Unfortunately, verifying that the <blkno, offset> _still_
1227f127cb91Sfrankho 	 * refers to a directory entry is not easy, since we'd have
1228f127cb91Sfrankho 	 * to search _all_ directories starting from root to find it.
1229f127cb91Sfrankho 	 * That's a high price to pay just in case somebody is forging
1230f127cb91Sfrankho 	 * file handles. So instead we verify that as much of the
1231f127cb91Sfrankho 	 * entry is valid as we can:
1232f127cb91Sfrankho 	 *
1233f127cb91Sfrankho 	 * 1. The starting cluster is 0 (unallocated) or valid
1234f127cb91Sfrankho 	 * 2. It is not an LFN entry
1235f127cb91Sfrankho 	 * 3. It is not hidden (unless mounted as such)
1236f127cb91Sfrankho 	 * 4. It is not the label
1237f127cb91Sfrankho 	 */
1238f127cb91Sfrankho 	cn = pc_getstartcluster(fsp, ep);
1239f127cb91Sfrankho 	/*
1240f127cb91Sfrankho 	 * if the starting cluster is valid, but not valid according
1241f127cb91Sfrankho 	 * to pc_validcl(), force it to be to simplify the following if.
1242f127cb91Sfrankho 	 */
1243f127cb91Sfrankho 	if (cn == 0)
1244f127cb91Sfrankho 		cn = PCF_FIRSTCLUSTER;
1245f127cb91Sfrankho 	if (IS_FAT32(fsp)) {
1246f127cb91Sfrankho 		if (cn >= PCF_LASTCLUSTER32)
1247f127cb91Sfrankho 			cn = PCF_FIRSTCLUSTER;
1248f127cb91Sfrankho 	} else {
1249f127cb91Sfrankho 		if (cn >= PCF_LASTCLUSTER)
1250f127cb91Sfrankho 			cn = PCF_FIRSTCLUSTER;
1251f127cb91Sfrankho 	}
1252f127cb91Sfrankho 	if ((!pc_validcl(fsp, cn)) ||
1253f127cb91Sfrankho 	    (PCDL_IS_LFN(ep)) ||
1254f127cb91Sfrankho 	    (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) ||
1255f127cb91Sfrankho 	    ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) {
1256f127cb91Sfrankho 		bp->b_flags |= B_STALE | B_AGE;
1257f127cb91Sfrankho 		brelse(bp);
1258f127cb91Sfrankho 		pc_unlockfs(fsp);
1259f127cb91Sfrankho 		return (EINVAL);
1260f127cb91Sfrankho 	}
1261f127cb91Sfrankho 	if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) &&
1262f127cb91Sfrankho 	    (ep->pcd_filename[0] != PCD_ERASED) &&
1263f127cb91Sfrankho 	    (pc_validchar(ep->pcd_filename[0]) ||
1264f127cb91Sfrankho 	    (ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) {
1265f127cb91Sfrankho 		pcp = pc_getnode(fsp, eblkno, eoffset, ep);
1266f127cb91Sfrankho 		pcp->pc_flags |= PC_EXTERNAL;
1267f127cb91Sfrankho 		*vpp = PCTOV(pcp);
1268f127cb91Sfrankho 	} else {
1269f127cb91Sfrankho 		*vpp = NULL;
1270f127cb91Sfrankho 	}
1271f127cb91Sfrankho 	bp->b_flags |= B_STALE | B_AGE;
1272f127cb91Sfrankho 	brelse(bp);
1273f127cb91Sfrankho 	pc_unlockfs(fsp);
1274f127cb91Sfrankho 	return (0);
1275f127cb91Sfrankho }
1276f127cb91Sfrankho 
1277f127cb91Sfrankho /*
1278f127cb91Sfrankho  * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about
1279f127cb91Sfrankho  * a meg), so we can't bread() it all in at once. This routine reads a
1280f127cb91Sfrankho  * fat a chunk at a time.
1281f127cb91Sfrankho  */
1282f127cb91Sfrankho static int
1283f127cb91Sfrankho pc_readfat(struct pcfs *fsp, uchar_t *fatp)
1284f127cb91Sfrankho {
1285f127cb91Sfrankho 	struct buf *bp;
1286f127cb91Sfrankho 	size_t off;
1287f127cb91Sfrankho 	size_t readsize;
1288f127cb91Sfrankho 	daddr_t diskblk;
1289f127cb91Sfrankho 	size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
1290f127cb91Sfrankho 	daddr_t start = fsp->pcfs_fatstart;
1291f127cb91Sfrankho 
1292f127cb91Sfrankho 	readsize = fsp->pcfs_clsize;
1293f127cb91Sfrankho 	for (off = 0; off < fatsize; off += readsize, fatp += readsize) {
1294f127cb91Sfrankho 		if (readsize > (fatsize - off))
1295f127cb91Sfrankho 			readsize = fatsize - off;
1296f127cb91Sfrankho 		diskblk = pc_dbdaddr(fsp, start +
1297f127cb91Sfrankho 		    pc_cltodb(fsp, pc_lblkno(fsp, off)));
1298f127cb91Sfrankho 		bp = bread(fsp->pcfs_xdev, diskblk, readsize);
1299f127cb91Sfrankho 		if (bp->b_flags & (B_ERROR | B_STALE)) {
1300f127cb91Sfrankho 			brelse(bp);
1301f127cb91Sfrankho 			return (EIO);
1302f127cb91Sfrankho 		}
1303f127cb91Sfrankho 		bp->b_flags |= B_STALE | B_AGE;
1304f127cb91Sfrankho 		bcopy(bp->b_un.b_addr, fatp, readsize);
1305f127cb91Sfrankho 		brelse(bp);
1306f127cb91Sfrankho 	}
1307f127cb91Sfrankho 	return (0);
1308f127cb91Sfrankho }
1309f127cb91Sfrankho 
1310f127cb91Sfrankho /*
1311f127cb91Sfrankho  * We write the FAT out a _lot_, in order to make sure that it
1312f127cb91Sfrankho  * is up-to-date. But on a FAT32 system (large drive, small clusters)
1313f127cb91Sfrankho  * the FAT might be a couple of megabytes, and writing it all out just
1314f127cb91Sfrankho  * because we created or deleted a small file is painful (especially
1315f127cb91Sfrankho  * since we do it for each alternate FAT too). So instead, for FAT16 and
1316f127cb91Sfrankho  * FAT32 we only write out the bit that has changed. We don't clear
1317f127cb91Sfrankho  * the 'updated' fields here because the caller might be writing out
1318f127cb91Sfrankho  * several FATs, so the caller must use pc_clear_fatchanges() after
1319f127cb91Sfrankho  * all FATs have been updated.
1320f127cb91Sfrankho  * This function doesn't take "start" from fsp->pcfs_dosstart because
1321f127cb91Sfrankho  * callers can use it to write either the primary or any of the alternate
1322f127cb91Sfrankho  * FAT tables.
1323f127cb91Sfrankho  */
1324f127cb91Sfrankho static int
1325f127cb91Sfrankho pc_writefat(struct pcfs *fsp, daddr_t start)
1326f127cb91Sfrankho {
1327f127cb91Sfrankho 	struct buf *bp;
1328f127cb91Sfrankho 	size_t off;
1329f127cb91Sfrankho 	size_t writesize;
1330f127cb91Sfrankho 	int	error;
1331f127cb91Sfrankho 	uchar_t *fatp = fsp->pcfs_fatp;
1332f127cb91Sfrankho 	size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
1333f127cb91Sfrankho 
1334f127cb91Sfrankho 	writesize = fsp->pcfs_clsize;
1335f127cb91Sfrankho 	for (off = 0; off < fatsize; off += writesize, fatp += writesize) {
1336f127cb91Sfrankho 		if (writesize > (fatsize - off))
1337f127cb91Sfrankho 			writesize = fatsize - off;
1338f127cb91Sfrankho 		if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) {
1339f127cb91Sfrankho 			continue;
1340f127cb91Sfrankho 		}
1341f127cb91Sfrankho 		bp = ngeteblk(writesize);
1342f127cb91Sfrankho 		bp->b_edev = fsp->pcfs_xdev;
1343f127cb91Sfrankho 		bp->b_dev = cmpdev(bp->b_edev);
1344f127cb91Sfrankho 		bp->b_blkno = pc_dbdaddr(fsp, start +
1345f127cb91Sfrankho 		    pc_cltodb(fsp, pc_lblkno(fsp, off)));
1346f127cb91Sfrankho 		bcopy(fatp, bp->b_un.b_addr, writesize);
1347f127cb91Sfrankho 		bwrite2(bp);
1348f127cb91Sfrankho 		error = geterror(bp);
1349f127cb91Sfrankho 		brelse(bp);
1350f127cb91Sfrankho 		if (error) {
1351f127cb91Sfrankho 			return (error);
1352f127cb91Sfrankho 		}
1353f127cb91Sfrankho 	}
1354f127cb91Sfrankho 	return (0);
1355f127cb91Sfrankho }
1356f127cb91Sfrankho 
1357f127cb91Sfrankho /*
1358f127cb91Sfrankho  * Mark the FAT cluster that 'cn' is stored in as modified.
1359f127cb91Sfrankho  */
1360f127cb91Sfrankho void
1361f127cb91Sfrankho pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn)
1362f127cb91Sfrankho {
1363f127cb91Sfrankho 	pc_cluster32_t	bn;
1364f127cb91Sfrankho 	size_t		size;
1365f127cb91Sfrankho 
1366f127cb91Sfrankho 	/* which fat block is the cluster number stored in? */
1367f127cb91Sfrankho 	if (IS_FAT32(fsp)) {
1368f127cb91Sfrankho 		size = sizeof (pc_cluster32_t);
1369f127cb91Sfrankho 		bn = pc_lblkno(fsp, cn * size);
1370f127cb91Sfrankho 		fsp->pcfs_fat_changemap[bn] = 1;
1371f127cb91Sfrankho 	} else if (IS_FAT16(fsp)) {
1372f127cb91Sfrankho 		size = sizeof (pc_cluster16_t);
1373f127cb91Sfrankho 		bn = pc_lblkno(fsp, cn * size);
1374f127cb91Sfrankho 		fsp->pcfs_fat_changemap[bn] = 1;
1375f127cb91Sfrankho 	} else {
1376f127cb91Sfrankho 		offset_t off;
1377f127cb91Sfrankho 		pc_cluster32_t nbn;
1378f127cb91Sfrankho 
1379f127cb91Sfrankho 		ASSERT(IS_FAT12(fsp));
1380f127cb91Sfrankho 		off = cn + (cn >> 1);
1381f127cb91Sfrankho 		bn = pc_lblkno(fsp, off);
1382f127cb91Sfrankho 		fsp->pcfs_fat_changemap[bn] = 1;
1383f127cb91Sfrankho 		/* does this field wrap into the next fat cluster? */
1384f127cb91Sfrankho 		nbn = pc_lblkno(fsp, off + 1);
1385f127cb91Sfrankho 		if (nbn != bn) {
1386f127cb91Sfrankho 			fsp->pcfs_fat_changemap[nbn] = 1;
1387f127cb91Sfrankho 		}
1388f127cb91Sfrankho 	}
1389f127cb91Sfrankho }
1390f127cb91Sfrankho 
1391f127cb91Sfrankho /*
1392f127cb91Sfrankho  * return whether the FAT cluster 'bn' is updated and needs to
1393f127cb91Sfrankho  * be written out.
1394f127cb91Sfrankho  */
1395f127cb91Sfrankho int
1396f127cb91Sfrankho pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn)
1397f127cb91Sfrankho {
1398f127cb91Sfrankho 	return (fsp->pcfs_fat_changemap[bn] == 1);
1399f127cb91Sfrankho }
1400f127cb91Sfrankho 
1401f127cb91Sfrankho /*
1402f127cb91Sfrankho  * Implementation of VFS_FREEVFS() to support forced umounts.
1403f127cb91Sfrankho  * This is called by the vfs framework after umount, to trigger
1404f127cb91Sfrankho  * the release of any resources still associated with the given
1405f127cb91Sfrankho  * vfs_t once the need to keep them has gone away.
1406f127cb91Sfrankho  */
1407f127cb91Sfrankho void
1408f127cb91Sfrankho pcfs_freevfs(vfs_t *vfsp)
1409f127cb91Sfrankho {
1410f127cb91Sfrankho 	struct pcfs *fsp = VFSTOPCFS(vfsp);
1411f127cb91Sfrankho 
1412f127cb91Sfrankho 	mutex_enter(&pcfslock);
1413f127cb91Sfrankho 	/*
1414f127cb91Sfrankho 	 * Purging the FAT closes the device - can't do any more
1415f127cb91Sfrankho 	 * I/O after this.
1416f127cb91Sfrankho 	 */
1417f127cb91Sfrankho 	if (fsp->pcfs_fatp != (uchar_t *)0)
1418f127cb91Sfrankho 		pc_invalfat(fsp);
1419f127cb91Sfrankho 	mutex_exit(&pcfslock);
1420f127cb91Sfrankho 
1421f127cb91Sfrankho 	VN_RELE(fsp->pcfs_devvp);
1422f127cb91Sfrankho 	mutex_destroy(&fsp->pcfs_lock);
1423f127cb91Sfrankho 	kmem_free(fsp, sizeof (*fsp));
1424f127cb91Sfrankho 
1425f127cb91Sfrankho 	/*
1426f127cb91Sfrankho 	 * Allow _fini() to succeed now, if so desired.
1427f127cb91Sfrankho 	 */
1428f127cb91Sfrankho 	atomic_dec_32(&pcfs_mountcount);
1429f127cb91Sfrankho }
1430f127cb91Sfrankho 
1431f127cb91Sfrankho 
1432f127cb91Sfrankho /*
1433f127cb91Sfrankho  * PC-style partition parsing and FAT BPB identification/validation code.
1434f127cb91Sfrankho  * The partition parsers here assume:
1435f127cb91Sfrankho  *	- a FAT filesystem will be in a partition that has one of a set of
1436f127cb91Sfrankho  *	  recognized partition IDs
1437f127cb91Sfrankho  *	- the user wants the 'numbering' (C:, D:, ...) that one would get
1438f127cb91Sfrankho  *	  on MSDOS 6.x.
1439f127cb91Sfrankho  *	  That means any non-FAT partition type (NTFS, HPFS, or any Linux fs)
1440f127cb91Sfrankho  *	  will not factor in the enumeration.
1441f127cb91Sfrankho  * These days, such assumptions should be revisited. FAT is no longer the
1442f127cb91Sfrankho  * only game in 'PC town'.
1443f127cb91Sfrankho  */
1444f127cb91Sfrankho /*
1445f127cb91Sfrankho  * isDosDrive()
1446f127cb91Sfrankho  *	Boolean function.  Give it the systid field for an fdisk partition
1447f127cb91Sfrankho  *	and it decides if that's a systid that describes a DOS drive.  We
1448f127cb91Sfrankho  *	use systid values defined in sys/dktp/fdisk.h.
1449f127cb91Sfrankho  */
1450f127cb91Sfrankho static int
1451f127cb91Sfrankho isDosDrive(uchar_t checkMe)
1452f127cb91Sfrankho {
1453f127cb91Sfrankho 	return ((checkMe == DOSOS12) || (checkMe == DOSOS16) ||
1454f127cb91Sfrankho 	    (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) ||
1455f127cb91Sfrankho 	    (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) ||
1456f127cb91Sfrankho 	    (checkMe == DIAGPART));
1457f127cb91Sfrankho }
1458f127cb91Sfrankho 
1459f127cb91Sfrankho 
1460f127cb91Sfrankho /*
1461f127cb91Sfrankho  * isDosExtended()
1462f127cb91Sfrankho  *	Boolean function.  Give it the systid field for an fdisk partition
1463f127cb91Sfrankho  *	and it decides if that's a systid that describes an extended DOS
1464f127cb91Sfrankho  *	partition.
1465f127cb91Sfrankho  */
1466f127cb91Sfrankho static int
1467f127cb91Sfrankho isDosExtended(uchar_t checkMe)
1468f127cb91Sfrankho {
1469f127cb91Sfrankho 	return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA));
1470f127cb91Sfrankho }
1471f127cb91Sfrankho 
1472f127cb91Sfrankho 
1473f127cb91Sfrankho /*
1474f127cb91Sfrankho  * isBootPart()
1475f127cb91Sfrankho  *	Boolean function.  Give it the systid field for an fdisk partition
1476f127cb91Sfrankho  *	and it decides if that's a systid that describes a Solaris boot
1477f127cb91Sfrankho  *	partition.
1478f127cb91Sfrankho  */
1479f127cb91Sfrankho static int
1480f127cb91Sfrankho isBootPart(uchar_t checkMe)
1481f127cb91Sfrankho {
1482f127cb91Sfrankho 	return (checkMe == X86BOOT);
1483f127cb91Sfrankho }
1484f127cb91Sfrankho 
1485f127cb91Sfrankho 
1486f127cb91Sfrankho /*
14877c478bd9Sstevel@tonic-gate  * noLogicalDrive()
14887c478bd9Sstevel@tonic-gate  *	Display error message about not being able to find a logical
14897c478bd9Sstevel@tonic-gate  *	drive.
14907c478bd9Sstevel@tonic-gate  */
14917c478bd9Sstevel@tonic-gate static void
1492f127cb91Sfrankho noLogicalDrive(int ldrive)
14937c478bd9Sstevel@tonic-gate {
1494f127cb91Sfrankho 	if (ldrive == BOOT_PARTITION_DRIVE) {
14957c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: no boot partition");
14967c478bd9Sstevel@tonic-gate 	} else {
1497f127cb91Sfrankho 		cmn_err(CE_NOTE, "!pcfs: %d: no such logical drive", ldrive);
14987c478bd9Sstevel@tonic-gate 	}
14997c478bd9Sstevel@tonic-gate }
15007c478bd9Sstevel@tonic-gate 
1501f127cb91Sfrankho 
15027c478bd9Sstevel@tonic-gate /*
15037c478bd9Sstevel@tonic-gate  * findTheDrive()
15047c478bd9Sstevel@tonic-gate  *	Discover offset of the requested logical drive, and return
15057c478bd9Sstevel@tonic-gate  *	that offset (startSector), the systid of that drive (sysid),
15067c478bd9Sstevel@tonic-gate  *	and a buffer pointer (bp), with the buffer contents being
15077c478bd9Sstevel@tonic-gate  *	the first sector of the logical drive (i.e., the sector that
15087c478bd9Sstevel@tonic-gate  *	contains the BPB for that drive).
1509f127cb91Sfrankho  *
1510f127cb91Sfrankho  * Note: this code is not capable of addressing >2TB disks, as it uses
1511f127cb91Sfrankho  *       daddr_t not diskaddr_t, some of the calculations would overflow
15127c478bd9Sstevel@tonic-gate  */
1513f127cb91Sfrankho #define	COPY_PTBL(mbr, ptblp)					\
1514f127cb91Sfrankho 	bcopy(&(((struct mboot *)(mbr))->parts), (ptblp),	\
1515f127cb91Sfrankho 	    FD_NUMPART * sizeof (struct ipart))
1516f127cb91Sfrankho 
15177c478bd9Sstevel@tonic-gate static int
1518f127cb91Sfrankho findTheDrive(struct pcfs *fsp, buf_t **bp)
15197c478bd9Sstevel@tonic-gate {
1520f127cb91Sfrankho 	int ldrive = fsp->pcfs_ldrive;
1521f127cb91Sfrankho 	dev_t dev = fsp->pcfs_devvp->v_rdev;
1522f127cb91Sfrankho 
15237c478bd9Sstevel@tonic-gate 	struct ipart dosp[FD_NUMPART];	/* incore fdisk partition structure */
15247c478bd9Sstevel@tonic-gate 	daddr_t lastseek = 0;		/* Disk block we sought previously */
15257c478bd9Sstevel@tonic-gate 	daddr_t diskblk = 0;		/* Disk block to get */
15267c478bd9Sstevel@tonic-gate 	daddr_t xstartsect;		/* base of Extended DOS partition */
15277c478bd9Sstevel@tonic-gate 	int logicalDriveCount = 0;	/* Count of logical drives seen */
15287c478bd9Sstevel@tonic-gate 	int extendedPart = -1;		/* index of extended dos partition */
15297c478bd9Sstevel@tonic-gate 	int primaryPart = -1;		/* index of primary dos partition */
15307c478bd9Sstevel@tonic-gate 	int bootPart = -1;		/* index of a Solaris boot partition */
1531342440ecSPrasad Singamsetty 	uint32_t xnumsect = 0;		/* length of extended DOS partition */
15327c478bd9Sstevel@tonic-gate 	int driveIndex;			/* computed FDISK table index */
1533f127cb91Sfrankho 	daddr_t startsec;
1534f127cb91Sfrankho 	len_t mediasize;
15357c478bd9Sstevel@tonic-gate 	int i;
15367c478bd9Sstevel@tonic-gate 	/*
15377c478bd9Sstevel@tonic-gate 	 * Count of drives in the current extended partition's
15387c478bd9Sstevel@tonic-gate 	 * FDISK table, and indexes of the drives themselves.
15397c478bd9Sstevel@tonic-gate 	 */
15407c478bd9Sstevel@tonic-gate 	int extndDrives[FD_NUMPART];
15417c478bd9Sstevel@tonic-gate 	int numDrives = 0;
15427c478bd9Sstevel@tonic-gate 
15437c478bd9Sstevel@tonic-gate 	/*
15447c478bd9Sstevel@tonic-gate 	 * Count of drives (beyond primary) in master boot record's
15457c478bd9Sstevel@tonic-gate 	 * FDISK table, and indexes of the drives themselves.
15467c478bd9Sstevel@tonic-gate 	 */
15477c478bd9Sstevel@tonic-gate 	int extraDrives[FD_NUMPART];
15487c478bd9Sstevel@tonic-gate 	int numExtraDrives = 0;
15497c478bd9Sstevel@tonic-gate 
15509bd42341Sfrankho 	/*
15519bd42341Sfrankho 	 * "ldrive == 0" should never happen, as this is a request to
15529bd42341Sfrankho 	 * mount the physical device (and ignore partitioning). The code
15539bd42341Sfrankho 	 * in pcfs_mount() should have made sure that a logical drive number
15549bd42341Sfrankho 	 * is at least 1, meaning we're looking for drive "C:". It is not
15559bd42341Sfrankho 	 * safe (and a bug in the callers of this function) to request logical
15569bd42341Sfrankho 	 * drive number 0; we could ASSERT() but a graceful EIO is a more
15579bd42341Sfrankho 	 * polite way.
15589bd42341Sfrankho 	 */
15599bd42341Sfrankho 	if (ldrive == 0) {
15609bd42341Sfrankho 		cmn_err(CE_NOTE, "!pcfs: request for logical partition zero");
15619bd42341Sfrankho 		noLogicalDrive(ldrive);
15629bd42341Sfrankho 		return (EIO);
15639bd42341Sfrankho 	}
15649bd42341Sfrankho 
15657c478bd9Sstevel@tonic-gate 	/*
15667c478bd9Sstevel@tonic-gate 	 *  Copy from disk block into memory aligned structure for fdisk usage.
15677c478bd9Sstevel@tonic-gate 	 */
1568f127cb91Sfrankho 	COPY_PTBL((*bp)->b_un.b_addr, dosp);
15697c478bd9Sstevel@tonic-gate 
1570f127cb91Sfrankho 	/*
1571f127cb91Sfrankho 	 * This check is ok because a FAT BPB and a master boot record (MBB)
1572f127cb91Sfrankho 	 * have the same signature, in the same position within the block.
1573f127cb91Sfrankho 	 */
1574f127cb91Sfrankho 	if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
1575f127cb91Sfrankho 		cmn_err(CE_NOTE, "!pcfs: MBR partition table signature err, "
1576f127cb91Sfrankho 		    "device (%x.%x):%d\n",
1577f127cb91Sfrankho 		    getmajor(dev), getminor(dev), ldrive);
15789bd42341Sfrankho 		return (EINVAL);
15799bd42341Sfrankho 	}
15809bd42341Sfrankho 
15817c478bd9Sstevel@tonic-gate 	/*
15827c478bd9Sstevel@tonic-gate 	 * Get a summary of what is in the Master FDISK table.
15837c478bd9Sstevel@tonic-gate 	 * Normally we expect to find one partition marked as a DOS drive.
15847c478bd9Sstevel@tonic-gate 	 * This partition is the one Windows calls the primary dos partition.
15857c478bd9Sstevel@tonic-gate 	 * If the machine has any logical drives then we also expect
15867c478bd9Sstevel@tonic-gate 	 * to find a partition marked as an extended DOS partition.
15877c478bd9Sstevel@tonic-gate 	 *
15887c478bd9Sstevel@tonic-gate 	 * Sometimes we'll find multiple partitions marked as DOS drives.
15897c478bd9Sstevel@tonic-gate 	 * The Solaris fdisk program allows these partitions
15907c478bd9Sstevel@tonic-gate 	 * to be created, but Windows fdisk no longer does.  We still need
15917c478bd9Sstevel@tonic-gate 	 * to support these, though, since Windows does.  We also need to fix
15927c478bd9Sstevel@tonic-gate 	 * our fdisk to behave like the Windows version.
15937c478bd9Sstevel@tonic-gate 	 *
15947c478bd9Sstevel@tonic-gate 	 * It turns out that some off-the-shelf media have *only* an
15957c478bd9Sstevel@tonic-gate 	 * Extended partition, so we need to deal with that case as well.
15967c478bd9Sstevel@tonic-gate 	 *
15977c478bd9Sstevel@tonic-gate 	 * Only a single (the first) Extended or Boot Partition will
15987c478bd9Sstevel@tonic-gate 	 * be recognized.  Any others will be ignored.
15997c478bd9Sstevel@tonic-gate 	 */
16007c478bd9Sstevel@tonic-gate 	for (i = 0; i < FD_NUMPART; i++) {
1601f127cb91Sfrankho 		DTRACE_PROBE4(primarypart, struct pcfs *, fsp,
1602f127cb91Sfrankho 		    uint_t, (uint_t)dosp[i].systid,
1603f127cb91Sfrankho 		    uint_t, LE_32(dosp[i].relsect),
1604f127cb91Sfrankho 		    uint_t, LE_32(dosp[i].numsect));
16050576819eSwyllys 
16067c478bd9Sstevel@tonic-gate 		if (isDosDrive(dosp[i].systid)) {
16077c478bd9Sstevel@tonic-gate 			if (primaryPart < 0) {
16087c478bd9Sstevel@tonic-gate 				logicalDriveCount++;
16097c478bd9Sstevel@tonic-gate 				primaryPart = i;
16107c478bd9Sstevel@tonic-gate 			} else {
16117c478bd9Sstevel@tonic-gate 				extraDrives[numExtraDrives++] = i;
16127c478bd9Sstevel@tonic-gate 			}
16137c478bd9Sstevel@tonic-gate 			continue;
16147c478bd9Sstevel@tonic-gate 		}
16157c478bd9Sstevel@tonic-gate 		if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) {
16167c478bd9Sstevel@tonic-gate 			extendedPart = i;
16177c478bd9Sstevel@tonic-gate 			continue;
16187c478bd9Sstevel@tonic-gate 		}
16197c478bd9Sstevel@tonic-gate 		if ((bootPart < 0) && isBootPart(dosp[i].systid)) {
16207c478bd9Sstevel@tonic-gate 			bootPart = i;
16217c478bd9Sstevel@tonic-gate 			continue;
16227c478bd9Sstevel@tonic-gate 		}
16237c478bd9Sstevel@tonic-gate 	}
16247c478bd9Sstevel@tonic-gate 
16259bd42341Sfrankho 	if (ldrive == BOOT_PARTITION_DRIVE) {
16267c478bd9Sstevel@tonic-gate 		if (bootPart < 0) {
16279bd42341Sfrankho 			noLogicalDrive(ldrive);
16289bd42341Sfrankho 			return (EINVAL);
16297c478bd9Sstevel@tonic-gate 		}
1630f127cb91Sfrankho 		startsec = LE_32(dosp[bootPart].relsect);
1631f127cb91Sfrankho 		mediasize = LE_32(dosp[bootPart].numsect);
1632f127cb91Sfrankho 		goto found;
16337c478bd9Sstevel@tonic-gate 	}
16347c478bd9Sstevel@tonic-gate 
16359bd42341Sfrankho 	if (ldrive == PRIMARY_DOS_DRIVE && primaryPart >= 0) {
1636f127cb91Sfrankho 		startsec = LE_32(dosp[primaryPart].relsect);
1637f127cb91Sfrankho 		mediasize = LE_32(dosp[primaryPart].numsect);
1638f127cb91Sfrankho 		goto found;
16397c478bd9Sstevel@tonic-gate 	}
16407c478bd9Sstevel@tonic-gate 
16417c478bd9Sstevel@tonic-gate 	/*
16427c478bd9Sstevel@tonic-gate 	 * We are not looking for the C: drive (or the primary drive
16437c478bd9Sstevel@tonic-gate 	 * was not found), so we had better have an extended partition
16447c478bd9Sstevel@tonic-gate 	 * or extra drives in the Master FDISK table.
16457c478bd9Sstevel@tonic-gate 	 */
16467c478bd9Sstevel@tonic-gate 	if ((extendedPart < 0) && (numExtraDrives == 0)) {
16477c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: no extended dos partition");
16489bd42341Sfrankho 		noLogicalDrive(ldrive);
16499bd42341Sfrankho 		return (EINVAL);
16507c478bd9Sstevel@tonic-gate 	}
16517c478bd9Sstevel@tonic-gate 
16527c478bd9Sstevel@tonic-gate 	if (extendedPart >= 0) {
1653f127cb91Sfrankho 		diskblk = xstartsect = LE_32(dosp[extendedPart].relsect);
1654f127cb91Sfrankho 		xnumsect = LE_32(dosp[extendedPart].numsect);
16557c478bd9Sstevel@tonic-gate 		do {
16567c478bd9Sstevel@tonic-gate 			/*
16577c478bd9Sstevel@tonic-gate 			 *  If the seek would not cause us to change
16587c478bd9Sstevel@tonic-gate 			 *  position on the drive, then we're out of
16597c478bd9Sstevel@tonic-gate 			 *  extended partitions to examine.
16607c478bd9Sstevel@tonic-gate 			 */
16617c478bd9Sstevel@tonic-gate 			if (diskblk == lastseek)
16627c478bd9Sstevel@tonic-gate 				break;
16637c478bd9Sstevel@tonic-gate 			logicalDriveCount += numDrives;
16647c478bd9Sstevel@tonic-gate 			/*
16657c478bd9Sstevel@tonic-gate 			 *  Seek the next extended partition, and find
16667c478bd9Sstevel@tonic-gate 			 *  logical drives within it.
16677c478bd9Sstevel@tonic-gate 			 */
1668f127cb91Sfrankho 			brelse(*bp);
1669f127cb91Sfrankho 			/*
1670f127cb91Sfrankho 			 * bread() block numbers are multiples of DEV_BSIZE
1671f127cb91Sfrankho 			 * but the device sector size (the unit of partitioning)
1672f127cb91Sfrankho 			 * might be larger than that; pcfs_get_device_info()
1673f127cb91Sfrankho 			 * has calculated the multiplicator for us.
1674f127cb91Sfrankho 			 */
1675f127cb91Sfrankho 			*bp = bread(dev,
1676f127cb91Sfrankho 			    pc_dbdaddr(fsp, diskblk), fsp->pcfs_secsize);
16777c478bd9Sstevel@tonic-gate 			if ((*bp)->b_flags & B_ERROR) {
16789bd42341Sfrankho 				return (EIO);
16797c478bd9Sstevel@tonic-gate 			}
1680f127cb91Sfrankho 
16817c478bd9Sstevel@tonic-gate 			lastseek = diskblk;
1682f127cb91Sfrankho 			COPY_PTBL((*bp)->b_un.b_addr, dosp);
1683f127cb91Sfrankho 			if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
16847c478bd9Sstevel@tonic-gate 				cmn_err(CE_NOTE, "!pcfs: "
1685f127cb91Sfrankho 				    "extended partition table signature err, "
1686f127cb91Sfrankho 				    "device (%x.%x):%d, LBA %u",
1687f127cb91Sfrankho 				    getmajor(dev), getminor(dev), ldrive,
1688f127cb91Sfrankho 				    (uint_t)pc_dbdaddr(fsp, diskblk));
16899bd42341Sfrankho 				return (EINVAL);
16907c478bd9Sstevel@tonic-gate 			}
16917c478bd9Sstevel@tonic-gate 			/*
16927c478bd9Sstevel@tonic-gate 			 *  Count up drives, and track where the next
16937c478bd9Sstevel@tonic-gate 			 *  extended partition is in case we need it.  We
16947c478bd9Sstevel@tonic-gate 			 *  are expecting only one extended partition.  If
16957c478bd9Sstevel@tonic-gate 			 *  there is more than one we'll only go to the
16967c478bd9Sstevel@tonic-gate 			 *  first one we see, but warn about ignoring.
16977c478bd9Sstevel@tonic-gate 			 */
16987c478bd9Sstevel@tonic-gate 			numDrives = 0;
16997c478bd9Sstevel@tonic-gate 			for (i = 0; i < FD_NUMPART; i++) {
1700f127cb91Sfrankho 				DTRACE_PROBE4(extendedpart,
1701f127cb91Sfrankho 				    struct pcfs *, fsp,
1702f127cb91Sfrankho 				    uint_t, (uint_t)dosp[i].systid,
1703f127cb91Sfrankho 				    uint_t, LE_32(dosp[i].relsect),
1704f127cb91Sfrankho 				    uint_t, LE_32(dosp[i].numsect));
17057c478bd9Sstevel@tonic-gate 				if (isDosDrive(dosp[i].systid)) {
17067c478bd9Sstevel@tonic-gate 					extndDrives[numDrives++] = i;
17077c478bd9Sstevel@tonic-gate 				} else if (isDosExtended(dosp[i].systid)) {
17087c478bd9Sstevel@tonic-gate 					if (diskblk != lastseek) {
17097c478bd9Sstevel@tonic-gate 						/*
17107c478bd9Sstevel@tonic-gate 						 * Already found an extended
17117c478bd9Sstevel@tonic-gate 						 * partition in this table.
17127c478bd9Sstevel@tonic-gate 						 */
17137c478bd9Sstevel@tonic-gate 						cmn_err(CE_NOTE,
17147c478bd9Sstevel@tonic-gate 						    "!pcfs: ignoring unexpected"
17157c478bd9Sstevel@tonic-gate 						    " additional extended"
17167c478bd9Sstevel@tonic-gate 						    " partition");
17179bd42341Sfrankho 					} else {
17189bd42341Sfrankho 						diskblk = xstartsect +
1719f127cb91Sfrankho 						    LE_32(dosp[i].relsect);
17207c478bd9Sstevel@tonic-gate 					}
17217c478bd9Sstevel@tonic-gate 				}
17227c478bd9Sstevel@tonic-gate 			}
17239bd42341Sfrankho 		} while (ldrive > logicalDriveCount + numDrives);
17247c478bd9Sstevel@tonic-gate 
1725f127cb91Sfrankho 		ASSERT(numDrives <= FD_NUMPART);
1726f127cb91Sfrankho 
17279bd42341Sfrankho 		if (ldrive <= logicalDriveCount + numDrives) {
17287c478bd9Sstevel@tonic-gate 			/*
17297c478bd9Sstevel@tonic-gate 			 * The number of logical drives we've found thus
17307c478bd9Sstevel@tonic-gate 			 * far is enough to get us to the one we were
17317c478bd9Sstevel@tonic-gate 			 * searching for.
17327c478bd9Sstevel@tonic-gate 			 */
17339bd42341Sfrankho 			driveIndex = logicalDriveCount + numDrives - ldrive;
1734f127cb91Sfrankho 			mediasize =
1735f127cb91Sfrankho 			    LE_32(dosp[extndDrives[driveIndex]].numsect);
1736f127cb91Sfrankho 			startsec =
1737f127cb91Sfrankho 			    LE_32(dosp[extndDrives[driveIndex]].relsect) +
17387c478bd9Sstevel@tonic-gate 			    lastseek;
1739f127cb91Sfrankho 			if (startsec > (xstartsect + xnumsect)) {
17407c478bd9Sstevel@tonic-gate 				cmn_err(CE_NOTE, "!pcfs: extended partition "
17417c478bd9Sstevel@tonic-gate 				    "values bad");
17429bd42341Sfrankho 				return (EINVAL);
17437c478bd9Sstevel@tonic-gate 			}
1744f127cb91Sfrankho 			goto found;
17457c478bd9Sstevel@tonic-gate 		} else {
17467c478bd9Sstevel@tonic-gate 			/*
17477c478bd9Sstevel@tonic-gate 			 * We ran out of extended dos partition
17487c478bd9Sstevel@tonic-gate 			 * drives.  The only hope now is to go
17497c478bd9Sstevel@tonic-gate 			 * back to extra drives defined in the master
17507c478bd9Sstevel@tonic-gate 			 * fdisk table.  But we overwrote that table
17517c478bd9Sstevel@tonic-gate 			 * already, so we must load it in again.
17527c478bd9Sstevel@tonic-gate 			 */
17537c478bd9Sstevel@tonic-gate 			logicalDriveCount += numDrives;
17547c478bd9Sstevel@tonic-gate 			brelse(*bp);
1755f127cb91Sfrankho 			ASSERT(fsp->pcfs_dosstart == 0);
1756f127cb91Sfrankho 			*bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
1757f127cb91Sfrankho 			    fsp->pcfs_secsize);
17587c478bd9Sstevel@tonic-gate 			if ((*bp)->b_flags & B_ERROR) {
17599bd42341Sfrankho 				return (EIO);
17607c478bd9Sstevel@tonic-gate 			}
1761f127cb91Sfrankho 			COPY_PTBL((*bp)->b_un.b_addr, dosp);
17627c478bd9Sstevel@tonic-gate 		}
17637c478bd9Sstevel@tonic-gate 	}
17647c478bd9Sstevel@tonic-gate 	/*
17657c478bd9Sstevel@tonic-gate 	 *  Still haven't found the drive, is it an extra
17667c478bd9Sstevel@tonic-gate 	 *  drive defined in the main FDISK table?
17677c478bd9Sstevel@tonic-gate 	 */
17689bd42341Sfrankho 	if (ldrive <= logicalDriveCount + numExtraDrives) {
17699bd42341Sfrankho 		driveIndex = logicalDriveCount + numExtraDrives - ldrive;
17709bd42341Sfrankho 		ASSERT(driveIndex < MIN(numExtraDrives, FD_NUMPART));
1771f127cb91Sfrankho 		mediasize = LE_32(dosp[extraDrives[driveIndex]].numsect);
1772f127cb91Sfrankho 		startsec = LE_32(dosp[extraDrives[driveIndex]].relsect);
1773f127cb91Sfrankho 		goto found;
17747c478bd9Sstevel@tonic-gate 	}
17757c478bd9Sstevel@tonic-gate 	/*
17767c478bd9Sstevel@tonic-gate 	 *  Still haven't found the drive, and there is
17777c478bd9Sstevel@tonic-gate 	 *  nowhere else to look.
17787c478bd9Sstevel@tonic-gate 	 */
17799bd42341Sfrankho 	noLogicalDrive(ldrive);
17809bd42341Sfrankho 	return (EINVAL);
17810576819eSwyllys 
1782f127cb91Sfrankho found:
17830576819eSwyllys 	/*
1784f127cb91Sfrankho 	 * We need this value in units of sectorsize, because PCFS' internal
1785f127cb91Sfrankho 	 * offset calculations go haywire for > 512Byte sectors unless all
1786f127cb91Sfrankho 	 * pcfs_.*start values are in units of sectors.
1787f127cb91Sfrankho 	 * So, assign before the capacity check (that's done in DEV_BSIZE)
17880576819eSwyllys 	 */
1789f127cb91Sfrankho 	fsp->pcfs_dosstart = startsec;
17900576819eSwyllys 
17910576819eSwyllys 	/*
1792f127cb91Sfrankho 	 * convert from device sectors to proper units:
1793f127cb91Sfrankho 	 *	- starting sector: DEV_BSIZE (as argument to bread())
1794f127cb91Sfrankho 	 *	- media size: Bytes
17950576819eSwyllys 	 */
1796f127cb91Sfrankho 	startsec = pc_dbdaddr(fsp, startsec);
1797f127cb91Sfrankho 	mediasize *= fsp->pcfs_secsize;
17980576819eSwyllys 
17990576819eSwyllys 	/*
1800f127cb91Sfrankho 	 * some additional validation / warnings in case the partition table
1801f127cb91Sfrankho 	 * and the actual media capacity are not in accordance ...
18020576819eSwyllys 	 */
1803f127cb91Sfrankho 	if (fsp->pcfs_mediasize != 0) {
1804f127cb91Sfrankho 		diskaddr_t startoff =
1805f127cb91Sfrankho 		    (diskaddr_t)startsec * (diskaddr_t)DEV_BSIZE;
1806f127cb91Sfrankho 
1807f127cb91Sfrankho 		if (startoff >= fsp->pcfs_mediasize ||
1808f127cb91Sfrankho 		    startoff + mediasize > fsp->pcfs_mediasize) {
1809f127cb91Sfrankho 			cmn_err(CE_WARN,
1810f127cb91Sfrankho 			    "!pcfs: partition size (LBA start %u, %lld bytes, "
1811f127cb91Sfrankho 			    "device (%x.%x):%d) smaller than "
1812f127cb91Sfrankho 			    "mediasize (%lld bytes).\n"
1813f127cb91Sfrankho 			    "filesystem may be truncated, access errors "
1814f127cb91Sfrankho 			    "may result.\n",
1815f127cb91Sfrankho 			    (uint_t)startsec, (long long)mediasize,
1816f127cb91Sfrankho 			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
1817f127cb91Sfrankho 			    fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
18180576819eSwyllys 		}
18190576819eSwyllys 	} else {
1820f127cb91Sfrankho 		fsp->pcfs_mediasize = mediasize;
18210576819eSwyllys 	}
18229bd42341Sfrankho 
1823f127cb91Sfrankho 	return (0);
18240576819eSwyllys }
18250576819eSwyllys 
18260576819eSwyllys 
1827f127cb91Sfrankho static fattype_t
1828f127cb91Sfrankho secondaryBPBChecks(struct pcfs *fsp, uchar_t *bpb, size_t secsize)
18297c478bd9Sstevel@tonic-gate {
1830f127cb91Sfrankho 	uint32_t ncl = fsp->pcfs_ncluster;
1831f127cb91Sfrankho 
1832f127cb91Sfrankho 	if (ncl <= 4096) {
1833f127cb91Sfrankho 		if (bpb_get_FatSz16(bpb) == 0)
1834f127cb91Sfrankho 			return (FAT_UNKNOWN);
1835f127cb91Sfrankho 
1836f127cb91Sfrankho 		if (bpb_get_FatSz16(bpb) * secsize < ncl * 2 &&
1837f127cb91Sfrankho 		    bpb_get_FatSz16(bpb) * secsize >= (3 * ncl / 2))
1838f127cb91Sfrankho 			return (FAT12);
1839f127cb91Sfrankho 		if (bcmp(bpb_FilSysType16(bpb), "FAT12", 5) == 0)
1840f127cb91Sfrankho 			return (FAT12);
1841f127cb91Sfrankho 		if (bcmp(bpb_FilSysType16(bpb), "FAT16", 5) == 0)
1842f127cb91Sfrankho 			return (FAT16);
1843f127cb91Sfrankho 
1844f127cb91Sfrankho 		switch (bpb_get_Media(bpb)) {
1845f127cb91Sfrankho 			case SS8SPT:
1846f127cb91Sfrankho 			case DS8SPT:
1847f127cb91Sfrankho 			case SS9SPT:
1848f127cb91Sfrankho 			case DS9SPT:
1849f127cb91Sfrankho 			case DS18SPT:
1850f127cb91Sfrankho 			case DS9_15SPT:
1851f127cb91Sfrankho 				/*
1852f127cb91Sfrankho 				 * Is this reliable - all floppies are FAT12 ?
1853f127cb91Sfrankho 				 */
1854f127cb91Sfrankho 				return (FAT12);
1855f127cb91Sfrankho 			case MD_FIXED:
1856f127cb91Sfrankho 				/*
1857f127cb91Sfrankho 				 * Is this reliable - disks are always FAT16 ?
1858f127cb91Sfrankho 				 */
1859f127cb91Sfrankho 				return (FAT16);
1860f127cb91Sfrankho 			default:
1861f127cb91Sfrankho 				break;
18627c478bd9Sstevel@tonic-gate 		}
1863f127cb91Sfrankho 	} else if (ncl <= 65536) {
1864f127cb91Sfrankho 		if (bpb_get_FatSz16(bpb) == 0 && bpb_get_FatSz32(bpb) > 0)
1865f127cb91Sfrankho 			return (FAT32);
1866f127cb91Sfrankho 		if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
1867f127cb91Sfrankho 			return (FAT32);
1868f127cb91Sfrankho 		if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
1869f127cb91Sfrankho 			return (FAT32);
18707c478bd9Sstevel@tonic-gate 
1871f127cb91Sfrankho 		if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
1872f127cb91Sfrankho 			return (FAT16);
1873f127cb91Sfrankho 		if (bpb_get_FatSz16(bpb) * secsize < ncl * 4)
1874f127cb91Sfrankho 			return (FAT16);
18757c478bd9Sstevel@tonic-gate 	}
18767c478bd9Sstevel@tonic-gate 
18770576819eSwyllys 	/*
1878f127cb91Sfrankho 	 * We don't know
18790576819eSwyllys 	 */
1880f127cb91Sfrankho 	return (FAT_UNKNOWN);
18817c478bd9Sstevel@tonic-gate }
18827c478bd9Sstevel@tonic-gate 
18837c478bd9Sstevel@tonic-gate /*
1884f127cb91Sfrankho  * Check to see if the BPB we found is correct.
1885f127cb91Sfrankho  *
1886f127cb91Sfrankho  * This looks far more complicated that it needs to be for pure structural
1887f127cb91Sfrankho  * validation. The reason for this is that parseBPB() is also used for
1888f127cb91Sfrankho  * debugging purposes (mdb dcmd) and we therefore want a bitmap of which
1889c2aaf90fSgd  * BPB fields (do not) have 'known good' values, even if we (do not) reject
1890c2aaf90fSgd  * the BPB when attempting to mount the filesystem.
1891c2aaf90fSgd  *
1892c2aaf90fSgd  * Real-world usage of FAT shows there are a lot of corner-case situations
1893c2aaf90fSgd  * and, following the specification strictly, invalid filesystems out there.
1894c2aaf90fSgd  * Known are situations such as:
1895c2aaf90fSgd  *	- FAT12/FAT16 filesystems with garbage in either totsec16/32
1896c2aaf90fSgd  *	  instead of the zero in one of the fields mandated by the spec
1897c2aaf90fSgd  *	- filesystems that claim to be larger than the partition they're in
1898c2aaf90fSgd  *	- filesystems without valid media descriptor
1899c2aaf90fSgd  *	- FAT32 filesystems with RootEntCnt != 0
1900c2aaf90fSgd  *	- FAT32 filesystems with less than 65526 clusters
1901c2aaf90fSgd  *	- FAT32 filesystems without valid FSI sector
1902c2aaf90fSgd  *	- FAT32 filesystems with FAT size in fatsec16 instead of fatsec32
1903c2aaf90fSgd  *
1904c2aaf90fSgd  * Such filesystems are accessible by PCFS - if it'd know to start with that
1905c2aaf90fSgd  * the filesystem should be treated as a specific FAT type. Before S10, it
1906c2aaf90fSgd  * relied on the PC/fdisk partition type for the purpose and almost completely
1907c2aaf90fSgd  * ignored the BPB; now it ignores the partition type for anything else but
1908c2aaf90fSgd  * logical drive enumeration, which can result in rejection of (invalid)
1909c2aaf90fSgd  * FAT32 - if the partition ID says FAT32, but the filesystem, for example
1910c2aaf90fSgd  * has less than 65526 clusters.
1911c2aaf90fSgd  *
1912c2aaf90fSgd  * Without a "force this fs as FAT{12,16,32}" tunable or mount option, it's
1913c2aaf90fSgd  * not possible to allow all such mostly-compliant filesystems in unless one
1914c2aaf90fSgd  * accepts false positives (definitely invalid filesystems that cause problems
1915c2aaf90fSgd  * later). This at least allows to pinpoint why the mount failed.
1916c2aaf90fSgd  *
1917c2aaf90fSgd  * Due to the use of FAT on removeable media, all relaxations of the rules
1918c2aaf90fSgd  * here need to be carefully evaluated wrt. to potential effects on PCFS
1919c2aaf90fSgd  * resilience. A faulty/"mis-crafted" filesystem must not cause a panic, so
1920c2aaf90fSgd  * beware.
19217c478bd9Sstevel@tonic-gate  */
1922f127cb91Sfrankho static int
1923f127cb91Sfrankho parseBPB(struct pcfs *fsp, uchar_t *bpb, int *valid)
19247c478bd9Sstevel@tonic-gate {
1925f127cb91Sfrankho 	fattype_t type;
1926f127cb91Sfrankho 
1927f127cb91Sfrankho 	uint32_t	ncl;	/* number of clusters in file area */
1928f127cb91Sfrankho 	uint32_t	rec;
1929f127cb91Sfrankho 	uint32_t	reserved;
1930f127cb91Sfrankho 	uint32_t	fsisec, bkbootsec;
1931f127cb91Sfrankho 	blkcnt_t	totsec, totsec16, totsec32, datasec;
1932f127cb91Sfrankho 	size_t		fatsec, fatsec16, fatsec32, rdirsec;
1933f127cb91Sfrankho 	size_t		secsize;
1934f127cb91Sfrankho 	len_t		mediasize;
1935f127cb91Sfrankho 	uint64_t	validflags = 0;
1936f127cb91Sfrankho 
1937f127cb91Sfrankho 	if (VALID_BPBSIG(bpb_get_BPBSig(bpb)))
1938f127cb91Sfrankho 		validflags |= BPB_BPBSIG_OK;
1939f127cb91Sfrankho 
1940f127cb91Sfrankho 	rec = bpb_get_RootEntCnt(bpb);
1941f127cb91Sfrankho 	reserved = bpb_get_RsvdSecCnt(bpb);
1942f127cb91Sfrankho 	fsisec = bpb_get_FSInfo32(bpb);
1943f127cb91Sfrankho 	bkbootsec = bpb_get_BkBootSec32(bpb);
1944f127cb91Sfrankho 	totsec16 = (blkcnt_t)bpb_get_TotSec16(bpb);
1945f127cb91Sfrankho 	totsec32 = (blkcnt_t)bpb_get_TotSec32(bpb);
1946f127cb91Sfrankho 	fatsec16 = bpb_get_FatSz16(bpb);
1947f127cb91Sfrankho 	fatsec32 = bpb_get_FatSz32(bpb);
1948f127cb91Sfrankho 
1949f127cb91Sfrankho 	totsec = totsec16 ? totsec16 : totsec32;
1950f127cb91Sfrankho 	fatsec = fatsec16 ? fatsec16 : fatsec32;
1951f127cb91Sfrankho 
1952f127cb91Sfrankho 	secsize = bpb_get_BytesPerSec(bpb);
1953f127cb91Sfrankho 	if (!VALID_SECSIZE(secsize))
1954f127cb91Sfrankho 		secsize = fsp->pcfs_secsize;
1955f127cb91Sfrankho 	if (secsize != fsp->pcfs_secsize) {
1956f127cb91Sfrankho 		PC_DPRINTF3(3, "!pcfs: parseBPB, device (%x.%x):%d:\n",
1957f127cb91Sfrankho 		    getmajor(fsp->pcfs_xdev),
1958f127cb91Sfrankho 		    getminor(fsp->pcfs_xdev), fsp->pcfs_ldrive);
1959f127cb91Sfrankho 		PC_DPRINTF2(3, "!BPB secsize %d != "
1960f127cb91Sfrankho 		    "autodetected media block size %d\n",
1961f127cb91Sfrankho 		    (int)secsize, (int)fsp->pcfs_secsize);
1962f127cb91Sfrankho 		if (fsp->pcfs_ldrive) {
1963f127cb91Sfrankho 			/*
1964f127cb91Sfrankho 			 * We've already attempted to parse the partition
1965f127cb91Sfrankho 			 * table. If the block size used for that don't match
1966f127cb91Sfrankho 			 * the PCFS sector size, we're hosed one way or the
1967f127cb91Sfrankho 			 * other. Just try what happens.
1968f127cb91Sfrankho 			 */
1969f127cb91Sfrankho 			secsize = fsp->pcfs_secsize;
1970f127cb91Sfrankho 			PC_DPRINTF1(3,
1971f127cb91Sfrankho 			    "!pcfs: Using autodetected secsize %d\n",
1972f127cb91Sfrankho 			    (int)secsize);
19737c478bd9Sstevel@tonic-gate 		} else {
1974f127cb91Sfrankho 			/*
1975f127cb91Sfrankho 			 * This allows mounting lofi images of PCFS partitions
1976f127cb91Sfrankho 			 * with sectorsize != DEV_BSIZE. We can't parse the
1977f127cb91Sfrankho 			 * partition table on whole-disk images unless the
1978f127cb91Sfrankho 			 * (undocumented) "secsize=..." mount option is used,
1979f127cb91Sfrankho 			 * but at least this allows us to mount if we have
1980f127cb91Sfrankho 			 * an image of a partition.
1981f127cb91Sfrankho 			 */
1982f127cb91Sfrankho 			PC_DPRINTF1(3,
1983f127cb91Sfrankho 			    "!pcfs: Using BPB secsize %d\n", (int)secsize);
19847c478bd9Sstevel@tonic-gate 		}
19857c478bd9Sstevel@tonic-gate 	}
19860576819eSwyllys 
1987f127cb91Sfrankho 	if (fsp->pcfs_mediasize == 0) {
1988f127cb91Sfrankho 		mediasize = (len_t)totsec * (len_t)secsize;
1989c2aaf90fSgd 		/*
1990c2aaf90fSgd 		 * This is not an error because not all devices support the
1991c2aaf90fSgd 		 * dkio(7i) mediasize queries, and/or not all devices are
1992c2aaf90fSgd 		 * partitioned. If we have not been able to figure out the
1993c2aaf90fSgd 		 * size of the underlaying medium, we have to trust the BPB.
1994c2aaf90fSgd 		 */
1995f127cb91Sfrankho 		PC_DPRINTF4(3, "!pcfs: parseBPB: mediasize autodetect failed "
1996f127cb91Sfrankho 		    "on device (%x.%x):%d, trusting BPB totsec (%lld Bytes)\n",
1997f127cb91Sfrankho 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
1998f127cb91Sfrankho 		    fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
1999f127cb91Sfrankho 	} else if ((len_t)totsec * (len_t)secsize > fsp->pcfs_mediasize) {
2000f127cb91Sfrankho 		cmn_err(CE_WARN,
2001f127cb91Sfrankho 		    "!pcfs: autodetected mediasize (%lld Bytes) smaller than "
2002f127cb91Sfrankho 		    "FAT BPB mediasize (%lld Bytes).\n"
2003f127cb91Sfrankho 		    "truncated filesystem on device (%x.%x):%d, access errors "
2004f127cb91Sfrankho 		    "possible.\n",
2005f127cb91Sfrankho 		    (long long)fsp->pcfs_mediasize,
2006f127cb91Sfrankho 		    (long long)(totsec * (blkcnt_t)secsize),
2007f127cb91Sfrankho 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
2008f127cb91Sfrankho 		    fsp->pcfs_ldrive);
2009f127cb91Sfrankho 		mediasize = fsp->pcfs_mediasize;
20107c478bd9Sstevel@tonic-gate 	} else {
20117c478bd9Sstevel@tonic-gate 		/*
2012f127cb91Sfrankho 		 * This is actually ok. A FAT needs not occupy the maximum
2013f127cb91Sfrankho 		 * space available in its partition, it can be shorter.
20147c478bd9Sstevel@tonic-gate 		 */
2015f127cb91Sfrankho 		mediasize = (len_t)totsec * (len_t)secsize;
20167c478bd9Sstevel@tonic-gate 	}
20177c478bd9Sstevel@tonic-gate 
20187c478bd9Sstevel@tonic-gate 	/*
2019f127cb91Sfrankho 	 * Since we let just about anything pass through this function,
2020f127cb91Sfrankho 	 * fence against divide-by-zero here.
20217c478bd9Sstevel@tonic-gate 	 */
2022f127cb91Sfrankho 	if (secsize)
2023f127cb91Sfrankho 		rdirsec = roundup(rec * 32, secsize) / secsize;
2024f127cb91Sfrankho 	else
2025f127cb91Sfrankho 		rdirsec = 0;
20267c478bd9Sstevel@tonic-gate 
20270576819eSwyllys 	/*
2028f127cb91Sfrankho 	 * This assignment is necessary before pc_dbdaddr() can first be
2029f127cb91Sfrankho 	 * used. Must initialize the value here.
20300576819eSwyllys 	 */
2031f127cb91Sfrankho 	fsp->pcfs_secsize = secsize;
2032f127cb91Sfrankho 	fsp->pcfs_sdshift = ddi_ffs(secsize / DEV_BSIZE) - 1;
2033f127cb91Sfrankho 
2034f127cb91Sfrankho 	fsp->pcfs_mediasize = mediasize;
2035f127cb91Sfrankho 
2036f127cb91Sfrankho 	fsp->pcfs_spcl = bpb_get_SecPerClus(bpb);
2037f127cb91Sfrankho 	fsp->pcfs_numfat = bpb_get_NumFATs(bpb);
2038f127cb91Sfrankho 	fsp->pcfs_mediadesc = bpb_get_Media(bpb);
2039f127cb91Sfrankho 	fsp->pcfs_clsize = secsize * fsp->pcfs_spcl;
2040f127cb91Sfrankho 	fsp->pcfs_rdirsec = rdirsec;
2041f127cb91Sfrankho 
20427c478bd9Sstevel@tonic-gate 	/*
2043f127cb91Sfrankho 	 * Remember: All PCFS offset calculations in sectors. Before I/O
2044f127cb91Sfrankho 	 * is done, convert to DEV_BSIZE units via pc_dbdaddr(). This is
2045f127cb91Sfrankho 	 * necessary so that media with > 512Byte sector sizes work correctly.
20467c478bd9Sstevel@tonic-gate 	 */
2047f127cb91Sfrankho 	fsp->pcfs_fatstart = fsp->pcfs_dosstart + reserved;
2048f127cb91Sfrankho 	fsp->pcfs_rdirstart = fsp->pcfs_fatstart + fsp->pcfs_numfat * fatsec;
2049f127cb91Sfrankho 	fsp->pcfs_datastart = fsp->pcfs_rdirstart + rdirsec;
2050f127cb91Sfrankho 	datasec = totsec -
2051f127cb91Sfrankho 	    (blkcnt_t)fatsec * fsp->pcfs_numfat -
2052f127cb91Sfrankho 	    (blkcnt_t)rdirsec -
2053f127cb91Sfrankho 	    (blkcnt_t)reserved;
2054f127cb91Sfrankho 
2055f127cb91Sfrankho 	DTRACE_PROBE4(fatgeometry,
2056f127cb91Sfrankho 	    blkcnt_t, totsec, size_t, fatsec,
2057f127cb91Sfrankho 	    size_t, rdirsec, blkcnt_t, datasec);
2058f127cb91Sfrankho 
20597c478bd9Sstevel@tonic-gate 	/*
2060c2aaf90fSgd 	 * 'totsec' is taken directly from the BPB and guaranteed to fit
2061c2aaf90fSgd 	 * into a 32bit unsigned integer. The calculation of 'datasec',
2062c2aaf90fSgd 	 * on the other hand, could underflow for incorrect values in
2063c2aaf90fSgd 	 * rdirsec/reserved/fatsec. Check for that.
2064c2aaf90fSgd 	 * We also check that the BPB conforms to the FAT specification's
2065c2aaf90fSgd 	 * requirement that either of the 16/32bit total sector counts
2066c2aaf90fSgd 	 * must be zero.
20677c478bd9Sstevel@tonic-gate 	 */
2068f127cb91Sfrankho 	if (totsec != 0 &&
2069f127cb91Sfrankho 	    (totsec16 == totsec32 || totsec16 == 0 || totsec32 == 0) &&
2070f127cb91Sfrankho 	    datasec < totsec && datasec <= UINT32_MAX)
2071f127cb91Sfrankho 		validflags |= BPB_TOTSEC_OK;
2072f127cb91Sfrankho 
2073c2aaf90fSgd 	if ((len_t)totsec * (len_t)secsize <= mediasize)
2074f127cb91Sfrankho 		validflags |= BPB_MEDIASZ_OK;
2075f127cb91Sfrankho 
2076f127cb91Sfrankho 	if (VALID_SECSIZE(secsize))
2077f127cb91Sfrankho 		validflags |= BPB_SECSIZE_OK;
2078f127cb91Sfrankho 	if (VALID_SPCL(fsp->pcfs_spcl))
2079f127cb91Sfrankho 		validflags |= BPB_SECPERCLUS_OK;
2080f127cb91Sfrankho 	if (VALID_CLSIZE(fsp->pcfs_clsize))
2081f127cb91Sfrankho 		validflags |= BPB_CLSIZE_OK;
2082f127cb91Sfrankho 	if (VALID_NUMFATS(fsp->pcfs_numfat))
2083f127cb91Sfrankho 		validflags |= BPB_NUMFAT_OK;
2084f127cb91Sfrankho 	if (VALID_RSVDSEC(reserved) && reserved < totsec)
2085f127cb91Sfrankho 		validflags |= BPB_RSVDSECCNT_OK;
2086f127cb91Sfrankho 	if (VALID_MEDIA(fsp->pcfs_mediadesc))
2087f127cb91Sfrankho 		validflags |= BPB_MEDIADESC_OK;
2088f127cb91Sfrankho 	if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
2089f127cb91Sfrankho 		validflags |= BPB_BOOTSIG16_OK;
2090f127cb91Sfrankho 	if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
2091f127cb91Sfrankho 		validflags |= BPB_BOOTSIG32_OK;
2092f127cb91Sfrankho 	if (VALID_FSTYPSTR16(bpb_FilSysType16(bpb)))
2093f127cb91Sfrankho 		validflags |= BPB_FSTYPSTR16_OK;
2094f127cb91Sfrankho 	if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
2095f127cb91Sfrankho 		validflags |= BPB_FSTYPSTR32_OK;
2096f127cb91Sfrankho 	if (VALID_OEMNAME(bpb_OEMName(bpb)))
2097f127cb91Sfrankho 		validflags |= BPB_OEMNAME_OK;
2098f127cb91Sfrankho 	if (bkbootsec > 0 && bkbootsec <= reserved && fsisec != bkbootsec)
2099f127cb91Sfrankho 		validflags |= BPB_BKBOOTSEC_OK;
2100f127cb91Sfrankho 	if (fsisec > 0 && fsisec <= reserved)
2101f127cb91Sfrankho 		validflags |= BPB_FSISEC_OK;
2102f127cb91Sfrankho 	if (VALID_JMPBOOT(bpb_jmpBoot(bpb)))
2103f127cb91Sfrankho 		validflags |= BPB_JMPBOOT_OK;
2104f127cb91Sfrankho 	if (VALID_FSVER32(bpb_get_FSVer32(bpb)))
2105f127cb91Sfrankho 		validflags |= BPB_FSVER_OK;
2106f127cb91Sfrankho 	if (VALID_VOLLAB(bpb_VolLab16(bpb)))
2107f127cb91Sfrankho 		validflags |= BPB_VOLLAB16_OK;
2108f127cb91Sfrankho 	if (VALID_VOLLAB(bpb_VolLab32(bpb)))
2109f127cb91Sfrankho 		validflags |= BPB_VOLLAB32_OK;
2110f127cb91Sfrankho 	if (VALID_EXTFLAGS(bpb_get_ExtFlags32(bpb)))
2111f127cb91Sfrankho 		validflags |= BPB_EXTFLAGS_OK;
21127c478bd9Sstevel@tonic-gate 
21137c478bd9Sstevel@tonic-gate 	/*
2114f127cb91Sfrankho 	 * Try to determine which FAT format to use.
2115f127cb91Sfrankho 	 *
2116f127cb91Sfrankho 	 * Calculate the number of clusters in order to determine
2117f127cb91Sfrankho 	 * the type of FAT we are looking at.  This is the only
2118f127cb91Sfrankho 	 * recommended way of determining FAT type, though there
2119f127cb91Sfrankho 	 * are other hints in the data, this is the best way.
2120f127cb91Sfrankho 	 *
2121f127cb91Sfrankho 	 * Since we let just about "anything" pass through this function
2122f127cb91Sfrankho 	 * without early exits, fence against divide-by-zero here.
2123f127cb91Sfrankho 	 *
2124f127cb91Sfrankho 	 * datasec was already validated against UINT32_MAX so we know
2125f127cb91Sfrankho 	 * the result will not overflow the 32bit calculation.
21267c478bd9Sstevel@tonic-gate 	 */
2127f127cb91Sfrankho 	if (fsp->pcfs_spcl)
2128f127cb91Sfrankho 		ncl = (uint32_t)datasec / fsp->pcfs_spcl;
2129f127cb91Sfrankho 	else
2130f127cb91Sfrankho 		ncl = 0;
21317c478bd9Sstevel@tonic-gate 
2132f127cb91Sfrankho 	fsp->pcfs_ncluster = ncl;
21337c478bd9Sstevel@tonic-gate 
2134f127cb91Sfrankho 	/*
2135f127cb91Sfrankho 	 * From the Microsoft FAT specification:
2136f127cb91Sfrankho 	 * In the following example, when it says <, it does not mean <=.
2137f127cb91Sfrankho 	 * Note also that the numbers are correct.  The first number for
2138f127cb91Sfrankho 	 * FAT12 is 4085; the second number for FAT16 is 65525. These numbers
2139f127cb91Sfrankho 	 * and the '<' signs are not wrong.
2140f127cb91Sfrankho 	 *
2141f127cb91Sfrankho 	 * We "specialdetect" the corner cases, and use at least one "extra"
2142f127cb91Sfrankho 	 * criterion to decide whether it's FAT16 or FAT32 if the cluster
2143f127cb91Sfrankho 	 * count is dangerously close to the boundaries.
2144f127cb91Sfrankho 	 */
2145f127cb91Sfrankho 
2146f127cb91Sfrankho 	if (ncl <= PCF_FIRSTCLUSTER) {
2147f127cb91Sfrankho 		type = FAT_UNKNOWN;
2148f127cb91Sfrankho 	} else if (ncl < 4085) {
2149f127cb91Sfrankho 		type = FAT12;
2150f127cb91Sfrankho 	} else if (ncl <= 4096) {
2151f127cb91Sfrankho 		type = FAT_QUESTIONABLE;
2152f127cb91Sfrankho 	} else if (ncl < 65525) {
2153f127cb91Sfrankho 		type = FAT16;
2154f127cb91Sfrankho 	} else if (ncl <= 65536) {
2155f127cb91Sfrankho 		type = FAT_QUESTIONABLE;
2156f127cb91Sfrankho 	} else if (ncl < PCF_LASTCLUSTER32) {
2157f127cb91Sfrankho 		type = FAT32;
2158f127cb91Sfrankho 	} else {
2159f127cb91Sfrankho 		type = FAT_UNKNOWN;
21607c478bd9Sstevel@tonic-gate 	}
21617c478bd9Sstevel@tonic-gate 
2162f127cb91Sfrankho 	DTRACE_PROBE4(parseBPB__initial,
2163f127cb91Sfrankho 	    struct pcfs *, fsp, unsigned char *, bpb,
2164f127cb91Sfrankho 	    int, validflags, fattype_t, type);
21657c478bd9Sstevel@tonic-gate 
2166f127cb91Sfrankho recheck:
2167f127cb91Sfrankho 	fsp->pcfs_fatsec = fatsec;
2168f127cb91Sfrankho 
2169f127cb91Sfrankho 	/* Do some final sanity checks for each specific type of FAT */
2170f127cb91Sfrankho 	switch (type) {
2171f127cb91Sfrankho 		case FAT12:
2172f127cb91Sfrankho 			if (rec != 0)
2173f127cb91Sfrankho 				validflags |= BPB_ROOTENTCNT_OK;
2174f127cb91Sfrankho 			if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
2175f127cb91Sfrankho 			    bpb_get_TotSec16(bpb) == 0)
2176f127cb91Sfrankho 				validflags |= BPB_TOTSEC16_OK;
2177f127cb91Sfrankho 			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
2178f127cb91Sfrankho 			    bpb_get_TotSec32(bpb) == 0)
2179f127cb91Sfrankho 				validflags |= BPB_TOTSEC32_OK;
2180f127cb91Sfrankho 			if (bpb_get_FatSz16(bpb) == fatsec)
2181f127cb91Sfrankho 				validflags |= BPB_FATSZ16_OK;
2182985bfda7SMichael Bergknoff 			if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER)
2183985bfda7SMichael Bergknoff 			    * 3 / 2)
2184f127cb91Sfrankho 				validflags |= BPB_FATSZ_OK;
2185f127cb91Sfrankho 			if (ncl < 4085)
2186f127cb91Sfrankho 				validflags |= BPB_NCLUSTERS_OK;
2187f127cb91Sfrankho 
2188f127cb91Sfrankho 			fsp->pcfs_lastclmark = (PCF_LASTCLUSTER & 0xfff);
2189f127cb91Sfrankho 			fsp->pcfs_rootblksize =
2190f127cb91Sfrankho 			    fsp->pcfs_rdirsec * secsize;
2191f127cb91Sfrankho 			fsp->pcfs_fsistart = 0;
2192f127cb91Sfrankho 
2193f127cb91Sfrankho 			if ((validflags & FAT12_VALIDMSK) != FAT12_VALIDMSK)
2194f127cb91Sfrankho 				type = FAT_UNKNOWN;
2195f127cb91Sfrankho 			break;
2196f127cb91Sfrankho 		case FAT16:
2197f127cb91Sfrankho 			if (rec != 0)
2198f127cb91Sfrankho 				validflags |= BPB_ROOTENTCNT_OK;
2199f127cb91Sfrankho 			if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
2200f127cb91Sfrankho 			    bpb_get_TotSec16(bpb) == 0)
2201f127cb91Sfrankho 				validflags |= BPB_TOTSEC16_OK;
2202f127cb91Sfrankho 			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
2203f127cb91Sfrankho 			    bpb_get_TotSec32(bpb) == 0)
2204f127cb91Sfrankho 				validflags |= BPB_TOTSEC32_OK;
2205f127cb91Sfrankho 			if (bpb_get_FatSz16(bpb) == fatsec)
2206f127cb91Sfrankho 				validflags |= BPB_FATSZ16_OK;
2207985bfda7SMichael Bergknoff 			if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER) * 2)
2208f127cb91Sfrankho 				validflags |= BPB_FATSZ_OK;
2209f127cb91Sfrankho 			if (ncl >= 4085 && ncl < 65525)
2210f127cb91Sfrankho 				validflags |= BPB_NCLUSTERS_OK;
2211f127cb91Sfrankho 
2212f127cb91Sfrankho 			fsp->pcfs_lastclmark = PCF_LASTCLUSTER;
2213f127cb91Sfrankho 			fsp->pcfs_rootblksize =
2214f127cb91Sfrankho 			    fsp->pcfs_rdirsec * secsize;
2215f127cb91Sfrankho 			fsp->pcfs_fsistart = 0;
2216f127cb91Sfrankho 
2217f127cb91Sfrankho 			if ((validflags & FAT16_VALIDMSK) != FAT16_VALIDMSK)
2218f127cb91Sfrankho 				type = FAT_UNKNOWN;
2219f127cb91Sfrankho 			break;
2220f127cb91Sfrankho 		case FAT32:
2221f127cb91Sfrankho 			if (rec == 0)
2222f127cb91Sfrankho 				validflags |= BPB_ROOTENTCNT_OK;
2223f127cb91Sfrankho 			if (bpb_get_TotSec16(bpb) == 0)
2224f127cb91Sfrankho 				validflags |= BPB_TOTSEC16_OK;
2225f127cb91Sfrankho 			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec)
2226f127cb91Sfrankho 				validflags |= BPB_TOTSEC32_OK;
2227f127cb91Sfrankho 			if (bpb_get_FatSz16(bpb) == 0)
2228f127cb91Sfrankho 				validflags |= BPB_FATSZ16_OK;
2229f127cb91Sfrankho 			if (bpb_get_FatSz32(bpb) == fatsec)
2230f127cb91Sfrankho 				validflags |= BPB_FATSZ32_OK;
2231985bfda7SMichael Bergknoff 			if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER) * 4)
2232f127cb91Sfrankho 				validflags |= BPB_FATSZ_OK;
2233f127cb91Sfrankho 			if (ncl >= 65525 && ncl < PCF_LASTCLUSTER32)
2234f127cb91Sfrankho 				validflags |= BPB_NCLUSTERS_OK;
2235f127cb91Sfrankho 
2236f127cb91Sfrankho 			fsp->pcfs_lastclmark = PCF_LASTCLUSTER32;
2237f127cb91Sfrankho 			fsp->pcfs_rootblksize = fsp->pcfs_clsize;
2238f127cb91Sfrankho 			fsp->pcfs_fsistart = fsp->pcfs_dosstart + fsisec;
2239f127cb91Sfrankho 			if (validflags & BPB_FSISEC_OK)
2240f127cb91Sfrankho 				fsp->pcfs_flags |= PCFS_FSINFO_OK;
2241f127cb91Sfrankho 			fsp->pcfs_rootclnum = bpb_get_RootClus32(bpb);
2242f127cb91Sfrankho 			if (pc_validcl(fsp, fsp->pcfs_rootclnum))
2243f127cb91Sfrankho 				validflags |= BPB_ROOTCLUSTER_OK;
2244f127cb91Sfrankho 
2245f127cb91Sfrankho 			/*
2246f127cb91Sfrankho 			 * Current PCFS code only works if 'pcfs_rdirstart'
2247f127cb91Sfrankho 			 * contains the root cluster number on FAT32.
2248f127cb91Sfrankho 			 * That's a mis-use and would better be changed.
2249f127cb91Sfrankho 			 */
2250f127cb91Sfrankho 			fsp->pcfs_rdirstart = (daddr_t)fsp->pcfs_rootclnum;
2251f127cb91Sfrankho 
2252f127cb91Sfrankho 			if ((validflags & FAT32_VALIDMSK) != FAT32_VALIDMSK)
2253f127cb91Sfrankho 				type = FAT_UNKNOWN;
2254f127cb91Sfrankho 			break;
2255f127cb91Sfrankho 		case FAT_QUESTIONABLE:
2256f127cb91Sfrankho 			type = secondaryBPBChecks(fsp, bpb, secsize);
2257f127cb91Sfrankho 			goto recheck;
2258f127cb91Sfrankho 		default:
2259f127cb91Sfrankho 			ASSERT(type == FAT_UNKNOWN);
2260f127cb91Sfrankho 			break;
22617c478bd9Sstevel@tonic-gate 	}
22627c478bd9Sstevel@tonic-gate 
2263f127cb91Sfrankho 	ASSERT(type != FAT_QUESTIONABLE);
22647c478bd9Sstevel@tonic-gate 
2265f127cb91Sfrankho 	fsp->pcfs_fattype = type;
22667c478bd9Sstevel@tonic-gate 
2267f127cb91Sfrankho 	if (valid)
2268f127cb91Sfrankho 		*valid = validflags;
2269f127cb91Sfrankho 
2270f127cb91Sfrankho 	DTRACE_PROBE4(parseBPB__final,
2271f127cb91Sfrankho 	    struct pcfs *, fsp, unsigned char *, bpb,
2272f127cb91Sfrankho 	    int, validflags, fattype_t, type);
2273f127cb91Sfrankho 
2274f127cb91Sfrankho 	if (type != FAT_UNKNOWN) {
2275f127cb91Sfrankho 		ASSERT((secsize & (DEV_BSIZE - 1)) == 0);
2276f127cb91Sfrankho 		ASSERT(ISP2(secsize / DEV_BSIZE));
2277f127cb91Sfrankho 		return (1);
22787c478bd9Sstevel@tonic-gate 	}
2279f127cb91Sfrankho 
2280f127cb91Sfrankho 	return (0);
22817c478bd9Sstevel@tonic-gate }
22827c478bd9Sstevel@tonic-gate 
2283f127cb91Sfrankho 
2284f127cb91Sfrankho /*
2285f127cb91Sfrankho  * Detect the device's native block size (sector size).
2286f127cb91Sfrankho  *
2287f127cb91Sfrankho  * Test whether the device is:
2288f127cb91Sfrankho  *	- a floppy device from a known controller type via DKIOCINFO
2289f127cb91Sfrankho  *	- a real floppy using the fd(7d) driver and capable of fdio(7I) ioctls
2290f127cb91Sfrankho  *	- a USB floppy drive (identified by drive geometry)
2291f127cb91Sfrankho  *
2292f127cb91Sfrankho  * Detecting a floppy will make PCFS metadata updates on such media synchronous,
2293f127cb91Sfrankho  * to minimize risks due to slow I/O and user hotplugging / device ejection.
2294f127cb91Sfrankho  *
2295f127cb91Sfrankho  * This might be a bit wasteful on kernel stack space; if anyone's
2296f127cb91Sfrankho  * bothered by this, kmem_alloc/kmem_free the ioctl arguments...
2297f127cb91Sfrankho  */
2298f127cb91Sfrankho static void
2299f127cb91Sfrankho pcfs_device_getinfo(struct pcfs *fsp)
23007c478bd9Sstevel@tonic-gate {
2301f127cb91Sfrankho 	dev_t			rdev = fsp->pcfs_xdev;
2302f127cb91Sfrankho 	int			error;
2303f127cb91Sfrankho 	union {
2304f127cb91Sfrankho 		struct dk_minfo		mi;
2305f127cb91Sfrankho 		struct dk_cinfo		ci;
2306f127cb91Sfrankho 		struct dk_geom		gi;
2307f127cb91Sfrankho 		struct fd_char		fc;
2308f127cb91Sfrankho 	} arg;				/* save stackspace ... */
2309f127cb91Sfrankho 	intptr_t argp = (intptr_t)&arg;
2310f127cb91Sfrankho 	ldi_handle_t		lh;
2311f127cb91Sfrankho 	ldi_ident_t		li;
2312f127cb91Sfrankho 	int isfloppy, isremoveable, ishotpluggable;
2313f127cb91Sfrankho 	cred_t			*cr = CRED();
2314f127cb91Sfrankho 
2315f127cb91Sfrankho 	if (ldi_ident_from_dev(rdev, &li))
2316f127cb91Sfrankho 		goto out;
2317f127cb91Sfrankho 
2318f127cb91Sfrankho 	error = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, cr, &lh, li);
2319f127cb91Sfrankho 	ldi_ident_release(li);
2320f127cb91Sfrankho 	if (error)
2321f127cb91Sfrankho 		goto out;
23227c478bd9Sstevel@tonic-gate 
23237c478bd9Sstevel@tonic-gate 	/*
2324f127cb91Sfrankho 	 * Not sure if this could possibly happen. It'd be a bit like
2325f127cb91Sfrankho 	 * VOP_OPEN() changing the passed-in vnode ptr. We're just not
2326f127cb91Sfrankho 	 * expecting it, needs some thought if triggered ...
23277c478bd9Sstevel@tonic-gate 	 */
2328f127cb91Sfrankho 	ASSERT(fsp->pcfs_xdev == rdev);
2329f127cb91Sfrankho 
2330f127cb91Sfrankho 	/*
2331f127cb91Sfrankho 	 * Check for removeable/hotpluggable media.
2332f127cb91Sfrankho 	 */
2333f127cb91Sfrankho 	if (ldi_ioctl(lh, DKIOCREMOVABLE,
2334f127cb91Sfrankho 	    (intptr_t)&isremoveable, FKIOCTL, cr, NULL)) {
2335f127cb91Sfrankho 		isremoveable = 0;
23367c478bd9Sstevel@tonic-gate 	}
2337f127cb91Sfrankho 	if (ldi_ioctl(lh, DKIOCHOTPLUGGABLE,
2338f127cb91Sfrankho 	    (intptr_t)&ishotpluggable, FKIOCTL, cr, NULL)) {
2339f127cb91Sfrankho 		ishotpluggable = 0;
2340f127cb91Sfrankho 	}
2341f127cb91Sfrankho 
2342f127cb91Sfrankho 	/*
2343f127cb91Sfrankho 	 * Make sure we don't use "half-initialized" values if the ioctls fail.
2344f127cb91Sfrankho 	 */
2345f127cb91Sfrankho 	if (ldi_ioctl(lh, DKIOCGMEDIAINFO, argp, FKIOCTL, cr, NULL)) {
2346f127cb91Sfrankho 		bzero(&arg, sizeof (arg));
2347f127cb91Sfrankho 		fsp->pcfs_mediasize = 0;
2348f127cb91Sfrankho 	} else {
2349f127cb91Sfrankho 		fsp->pcfs_mediasize =
2350f127cb91Sfrankho 		    (len_t)arg.mi.dki_lbsize *
2351f127cb91Sfrankho 		    (len_t)arg.mi.dki_capacity;
2352f127cb91Sfrankho 	}
2353f127cb91Sfrankho 
2354f127cb91Sfrankho 	if (VALID_SECSIZE(arg.mi.dki_lbsize)) {
2355f127cb91Sfrankho 		if (fsp->pcfs_secsize == 0) {
2356f127cb91Sfrankho 			fsp->pcfs_secsize = arg.mi.dki_lbsize;
2357f127cb91Sfrankho 			fsp->pcfs_sdshift =
2358f127cb91Sfrankho 			    ddi_ffs(arg.mi.dki_lbsize / DEV_BSIZE) - 1;
2359f127cb91Sfrankho 		} else {
2360f127cb91Sfrankho 			PC_DPRINTF4(1, "!pcfs: autodetected media block size "
2361f127cb91Sfrankho 			    "%d, device (%x.%x), different from user-provided "
2362f127cb91Sfrankho 			    "%d. User override - ignoring autodetect result.\n",
2363f127cb91Sfrankho 			    arg.mi.dki_lbsize,
2364f127cb91Sfrankho 			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
2365f127cb91Sfrankho 			    fsp->pcfs_secsize);
23667c478bd9Sstevel@tonic-gate 		}
2367f127cb91Sfrankho 	} else if (arg.mi.dki_lbsize) {
2368f127cb91Sfrankho 		PC_DPRINTF3(1, "!pcfs: autodetected media block size "
2369f127cb91Sfrankho 		    "%d, device (%x.%x), invalid (not 512, 1024, 2048, 4096). "
2370f127cb91Sfrankho 		    "Ignoring autodetect result.\n",
2371f127cb91Sfrankho 		    arg.mi.dki_lbsize,
2372f127cb91Sfrankho 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev));
23737c478bd9Sstevel@tonic-gate 	}
23747c478bd9Sstevel@tonic-gate 
2375f127cb91Sfrankho 	/*
2376f127cb91Sfrankho 	 * We treat the following media types as a floppy by default.
2377f127cb91Sfrankho 	 */
2378f127cb91Sfrankho 	isfloppy =
2379f127cb91Sfrankho 	    (arg.mi.dki_media_type == DK_FLOPPY ||
2380f127cb91Sfrankho 	    arg.mi.dki_media_type == DK_ZIP ||
2381f127cb91Sfrankho 	    arg.mi.dki_media_type == DK_JAZ);
23827c478bd9Sstevel@tonic-gate 
23837c478bd9Sstevel@tonic-gate 	/*
2384f127cb91Sfrankho 	 * if this device understands fdio(7I) requests it's
2385f127cb91Sfrankho 	 * obviously a floppy drive.
23867c478bd9Sstevel@tonic-gate 	 */
2387f127cb91Sfrankho 	if (!isfloppy &&
2388f127cb91Sfrankho 	    !ldi_ioctl(lh, FDIOGCHAR, argp, FKIOCTL, cr, NULL))
2389f127cb91Sfrankho 		isfloppy = 1;
2390f127cb91Sfrankho 
23917c478bd9Sstevel@tonic-gate 	/*
239269ed0c8eSGarrett D'Amore 	 * some devices we like to treat as floppies, but they don't
239369ed0c8eSGarrett D'Amore 	 * understand fdio(7I) requests.
23947c478bd9Sstevel@tonic-gate 	 */
2395f127cb91Sfrankho 	if (!isfloppy &&
2396f127cb91Sfrankho 	    !ldi_ioctl(lh, DKIOCINFO, argp, FKIOCTL, cr, NULL) &&
2397f127cb91Sfrankho 	    (arg.ci.dki_ctype == DKC_WDC2880 ||
2398f127cb91Sfrankho 	    arg.ci.dki_ctype == DKC_NCRFLOPPY ||
2399f127cb91Sfrankho 	    arg.ci.dki_ctype == DKC_SMSFLOPPY ||
240069ed0c8eSGarrett D'Amore 	    arg.ci.dki_ctype == DKC_INTEL82077))
2401f127cb91Sfrankho 		isfloppy = 1;
24027c478bd9Sstevel@tonic-gate 
24037c478bd9Sstevel@tonic-gate 	/*
2404f127cb91Sfrankho 	 * This is the "final fallback" test - media with
2405f127cb91Sfrankho 	 * 2 heads and 80 cylinders are assumed to be floppies.
2406f127cb91Sfrankho 	 * This is normally true for USB floppy drives ...
24077c478bd9Sstevel@tonic-gate 	 */
2408f127cb91Sfrankho 	if (!isfloppy &&
2409f127cb91Sfrankho 	    !ldi_ioctl(lh, DKIOCGGEOM, argp, FKIOCTL, cr, NULL) &&
2410f127cb91Sfrankho 	    (arg.gi.dkg_ncyl == 80 && arg.gi.dkg_nhead == 2))
2411f127cb91Sfrankho 		isfloppy = 1;
24127c478bd9Sstevel@tonic-gate 
2413f127cb91Sfrankho 	/*
2414f127cb91Sfrankho 	 * This is similar to the "old" PCFS code that sets this flag
2415f127cb91Sfrankho 	 * just based on the media descriptor being 0xf8 (MD_FIXED).
2416f127cb91Sfrankho 	 * Should be re-worked. We really need some specialcasing for
2417f127cb91Sfrankho 	 * removeable media.
2418f127cb91Sfrankho 	 */
2419f127cb91Sfrankho 	if (!isfloppy) {
2420f127cb91Sfrankho 		fsp->pcfs_flags |= PCFS_NOCHK;
2421f127cb91Sfrankho 	}
2422f127cb91Sfrankho 
2423f127cb91Sfrankho 	/*
2424f127cb91Sfrankho 	 * We automatically disable access time updates if the medium is
2425f127cb91Sfrankho 	 * removeable and/or hotpluggable, and the admin did not explicitly
2426f127cb91Sfrankho 	 * request access time updates (via the "atime" mount option).
2427f127cb91Sfrankho 	 * The majority of flash-based media should fit this category.
2428f127cb91Sfrankho 	 * Minimizing write access extends the lifetime of your memory stick !
2429f127cb91Sfrankho 	 */
2430f127cb91Sfrankho 	if (!vfs_optionisset(fsp->pcfs_vfs, MNTOPT_ATIME, NULL) &&
2431f127cb91Sfrankho 	    (isremoveable || ishotpluggable | isfloppy)) {
2432f127cb91Sfrankho 		fsp->pcfs_flags |= PCFS_NOATIME;
2433f127cb91Sfrankho 	}
2434f127cb91Sfrankho 
2435f127cb91Sfrankho 	(void) ldi_close(lh, FREAD, cr);
2436f127cb91Sfrankho out:
2437f127cb91Sfrankho 	if (fsp->pcfs_secsize == 0) {
2438f127cb91Sfrankho 		PC_DPRINTF3(1, "!pcfs: media block size autodetection "
2439f127cb91Sfrankho 		    "device (%x.%x) failed, no user-provided fallback. "
2440f127cb91Sfrankho 		    "Using %d bytes.\n",
2441f127cb91Sfrankho 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
2442f127cb91Sfrankho 		    DEV_BSIZE);
2443f127cb91Sfrankho 		fsp->pcfs_secsize = DEV_BSIZE;
2444f127cb91Sfrankho 		fsp->pcfs_sdshift = 0;
2445f127cb91Sfrankho 	}
2446f127cb91Sfrankho 	ASSERT(fsp->pcfs_secsize % DEV_BSIZE == 0);
2447f127cb91Sfrankho 	ASSERT(VALID_SECSIZE(fsp->pcfs_secsize));
24487c478bd9Sstevel@tonic-gate }
24497c478bd9Sstevel@tonic-gate 
24507c478bd9Sstevel@tonic-gate /*
2451f127cb91Sfrankho  * Get the FAT type for the DOS medium.
2452f127cb91Sfrankho  *
2453f127cb91Sfrankho  * -------------------------
2454f127cb91Sfrankho  * According to Microsoft:
2455f127cb91Sfrankho  *   The FAT type one of FAT12, FAT16, or FAT32 is determined by the
2456f127cb91Sfrankho  * count of clusters on the volume and nothing else.
2457f127cb91Sfrankho  * -------------------------
2458f127cb91Sfrankho  *
24597c478bd9Sstevel@tonic-gate  */
24607c478bd9Sstevel@tonic-gate static int
2461f127cb91Sfrankho pc_getfattype(struct pcfs *fsp)
24627c478bd9Sstevel@tonic-gate {
2463f127cb91Sfrankho 	int error = 0;
2464f127cb91Sfrankho 	buf_t *bp = NULL;
2465f127cb91Sfrankho 	struct vnode *devvp = fsp->pcfs_devvp;
2466f127cb91Sfrankho 	dev_t	dev = devvp->v_rdev;
24677c478bd9Sstevel@tonic-gate 
2468f127cb91Sfrankho 	/*
2469f127cb91Sfrankho 	 * Detect the native block size of the medium, and attempt to
2470f127cb91Sfrankho 	 * detect whether the medium is removeable.
247169ed0c8eSGarrett D'Amore 	 * We do treat removable media (floppies, USB and FireWire disks)
247269ed0c8eSGarrett D'Amore 	 * differently wrt. to the frequency and synchronicity of FAT updates.
2473f127cb91Sfrankho 	 * We need to know the media block size in order to be able to
2474f127cb91Sfrankho 	 * parse the partition table.
2475f127cb91Sfrankho 	 */
2476f127cb91Sfrankho 	pcfs_device_getinfo(fsp);
24777c478bd9Sstevel@tonic-gate 
24787c478bd9Sstevel@tonic-gate 	/*
2479f127cb91Sfrankho 	 * Unpartitioned media (floppies and some removeable devices)
2480f127cb91Sfrankho 	 * don't have a partition table, the FAT BPB is at disk block 0.
2481f127cb91Sfrankho 	 * Start out by reading block 0.
24827c478bd9Sstevel@tonic-gate 	 */
2483f127cb91Sfrankho 	fsp->pcfs_dosstart = 0;
2484f127cb91Sfrankho 	bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), fsp->pcfs_secsize);
2485f127cb91Sfrankho 
2486f127cb91Sfrankho 	if (error = geterror(bp))
2487f127cb91Sfrankho 		goto out;
2488f127cb91Sfrankho 
24897c478bd9Sstevel@tonic-gate 	/*
2490f127cb91Sfrankho 	 * If a logical drive number is requested, parse the partition table
2491f127cb91Sfrankho 	 * and attempt to locate it. Otherwise, proceed immediately to the
2492f127cb91Sfrankho 	 * BPB check. findTheDrive(), if successful, returns the disk block
2493f127cb91Sfrankho 	 * number where the requested partition starts in "startsec".
24947c478bd9Sstevel@tonic-gate 	 */
2495f127cb91Sfrankho 	if (fsp->pcfs_ldrive != 0) {
2496f127cb91Sfrankho 		PC_DPRINTF3(5, "!pcfs: pc_getfattype: using FDISK table on "
2497f127cb91Sfrankho 		    "device (%x,%x):%d to find BPB\n",
2498f127cb91Sfrankho 		    getmajor(dev), getminor(dev), fsp->pcfs_ldrive);
2499f127cb91Sfrankho 
2500f127cb91Sfrankho 		if (error = findTheDrive(fsp, &bp))
2501f127cb91Sfrankho 			goto out;
2502f127cb91Sfrankho 
2503f127cb91Sfrankho 		ASSERT(fsp->pcfs_dosstart != 0);
2504f127cb91Sfrankho 
25057c478bd9Sstevel@tonic-gate 		brelse(bp);
2506f127cb91Sfrankho 		bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
2507f127cb91Sfrankho 		    fsp->pcfs_secsize);
2508f127cb91Sfrankho 		if (error = geterror(bp))
2509f127cb91Sfrankho 			goto out;
25107c478bd9Sstevel@tonic-gate 	}
2511f127cb91Sfrankho 
2512f127cb91Sfrankho 	/*
2513f127cb91Sfrankho 	 * Validate the BPB and fill in the instance structure.
2514f127cb91Sfrankho 	 */
2515f127cb91Sfrankho 	if (!parseBPB(fsp, (uchar_t *)bp->b_un.b_addr, NULL)) {
2516f127cb91Sfrankho 		PC_DPRINTF4(1, "!pcfs: pc_getfattype: No FAT BPB on "
2517f127cb91Sfrankho 		    "device (%x.%x):%d, disk LBA %u\n",
2518f127cb91Sfrankho 		    getmajor(dev), getminor(dev), fsp->pcfs_ldrive,
2519f127cb91Sfrankho 		    (uint_t)pc_dbdaddr(fsp, fsp->pcfs_dosstart));
2520f127cb91Sfrankho 		error = EINVAL;
2521f127cb91Sfrankho 		goto out;
25227c478bd9Sstevel@tonic-gate 	}
2523f127cb91Sfrankho 
2524f127cb91Sfrankho 	ASSERT(fsp->pcfs_fattype != FAT_UNKNOWN);
2525f127cb91Sfrankho 
2526f127cb91Sfrankho out:
2527f127cb91Sfrankho 	/*
2528f127cb91Sfrankho 	 * Release the buffer used
2529f127cb91Sfrankho 	 */
2530f127cb91Sfrankho 	if (bp != NULL)
2531f127cb91Sfrankho 		brelse(bp);
2532f127cb91Sfrankho 	return (error);
25337c478bd9Sstevel@tonic-gate }
25347c478bd9Sstevel@tonic-gate 
2535f127cb91Sfrankho 
25367c478bd9Sstevel@tonic-gate /*
2537f127cb91Sfrankho  * Get the file allocation table.
2538f127cb91Sfrankho  * If there is an old FAT, invalidate it.
25397c478bd9Sstevel@tonic-gate  */
2540f127cb91Sfrankho int
2541f127cb91Sfrankho pc_getfat(struct pcfs *fsp)
25427c478bd9Sstevel@tonic-gate {
2543f127cb91Sfrankho 	struct buf *bp = NULL;
2544f127cb91Sfrankho 	uchar_t *fatp = NULL;
2545f127cb91Sfrankho 	uchar_t *fat_changemap = NULL;
2546f127cb91Sfrankho 	int error;
2547f127cb91Sfrankho 	int fat_changemapsize;
2548f127cb91Sfrankho 	int flags = 0;
2549f127cb91Sfrankho 	int nfat;
2550f127cb91Sfrankho 	int altfat_mustmatch = 0;
2551f127cb91Sfrankho 	int fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
25527c478bd9Sstevel@tonic-gate 
2553f127cb91Sfrankho 	if (fsp->pcfs_fatp) {
2554f127cb91Sfrankho 		/*
2555f127cb91Sfrankho 		 * There is a FAT in core.
2556f127cb91Sfrankho 		 * If there are open file pcnodes or we have modified it or
2557f127cb91Sfrankho 		 * it hasn't timed out yet use the in core FAT.
2558f127cb91Sfrankho 		 * Otherwise invalidate it and get a new one
2559f127cb91Sfrankho 		 */
2560f127cb91Sfrankho #ifdef notdef
2561f127cb91Sfrankho 		if (fsp->pcfs_frefs ||
2562f127cb91Sfrankho 		    (fsp->pcfs_flags & PCFS_FATMOD) ||
2563f127cb91Sfrankho 		    (gethrestime_sec() < fsp->pcfs_fattime)) {
2564f127cb91Sfrankho 			return (0);
2565f127cb91Sfrankho 		} else {
2566f127cb91Sfrankho 			mutex_enter(&pcfslock);
2567f127cb91Sfrankho 			pc_invalfat(fsp);
2568f127cb91Sfrankho 			mutex_exit(&pcfslock);
2569f127cb91Sfrankho 		}
2570f127cb91Sfrankho #endif /* notdef */
25717c478bd9Sstevel@tonic-gate 		return (0);
25727c478bd9Sstevel@tonic-gate 	}
25737c478bd9Sstevel@tonic-gate 
2574f127cb91Sfrankho 	/*
2575f127cb91Sfrankho 	 * Get FAT and check it for validity
2576f127cb91Sfrankho 	 */
2577f127cb91Sfrankho 	fatp = kmem_alloc(fatsize, KM_SLEEP);
2578f127cb91Sfrankho 	error = pc_readfat(fsp, fatp);
2579f127cb91Sfrankho 	if (error) {
2580f127cb91Sfrankho 		flags = B_ERROR;
2581f127cb91Sfrankho 		goto out;
25827c478bd9Sstevel@tonic-gate 	}
2583f127cb91Sfrankho 	fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1;
2584f127cb91Sfrankho 	fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP);
2585f127cb91Sfrankho 	fsp->pcfs_fatp = fatp;
2586f127cb91Sfrankho 	fsp->pcfs_fat_changemapsize = fat_changemapsize;
2587f127cb91Sfrankho 	fsp->pcfs_fat_changemap = fat_changemap;
25887c478bd9Sstevel@tonic-gate 
2589f127cb91Sfrankho 	/*
2590f127cb91Sfrankho 	 * The only definite signature check is that the
2591f127cb91Sfrankho 	 * media descriptor byte should match the first byte
2592f127cb91Sfrankho 	 * of the FAT block.
2593f127cb91Sfrankho 	 */
2594f127cb91Sfrankho 	if (fatp[0] != fsp->pcfs_mediadesc) {
2595f127cb91Sfrankho 		cmn_err(CE_NOTE, "!pcfs: FAT signature mismatch, "
2596f127cb91Sfrankho 		    "media descriptor %x, FAT[0] lowbyte %x\n",
2597f127cb91Sfrankho 		    (uint32_t)fsp->pcfs_mediadesc, (uint32_t)fatp[0]);
2598f127cb91Sfrankho 		cmn_err(CE_NOTE, "!pcfs: Enforcing alternate FAT validation\n");
2599f127cb91Sfrankho 		altfat_mustmatch = 1;
26007c478bd9Sstevel@tonic-gate 	}
26017c478bd9Sstevel@tonic-gate 
2602f127cb91Sfrankho 	/*
2603f127cb91Sfrankho 	 * Get alternate FATs and check for consistency
2604f127cb91Sfrankho 	 * This is an inlined version of pc_readfat().
2605f127cb91Sfrankho 	 * Since we're only comparing FAT and alternate FAT,
2606f127cb91Sfrankho 	 * there's no reason to let pc_readfat() copy data out
2607f127cb91Sfrankho 	 * of the buf. Instead, compare in-situ, one cluster
2608f127cb91Sfrankho 	 * at a time.
2609f127cb91Sfrankho 	 */
2610f127cb91Sfrankho 	for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) {
2611f127cb91Sfrankho 		size_t startsec;
2612f127cb91Sfrankho 		size_t off;
26137c478bd9Sstevel@tonic-gate 
2614f127cb91Sfrankho 		startsec = pc_dbdaddr(fsp,
2615f127cb91Sfrankho 		    fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec);
26167c478bd9Sstevel@tonic-gate 
2617f127cb91Sfrankho 		for (off = 0; off < fatsize; off += fsp->pcfs_clsize) {
2618f127cb91Sfrankho 			daddr_t fatblk = startsec + pc_dbdaddr(fsp,
2619f127cb91Sfrankho 			    pc_cltodb(fsp, pc_lblkno(fsp, off)));
26207c478bd9Sstevel@tonic-gate 
2621f127cb91Sfrankho 			bp = bread(fsp->pcfs_xdev, fatblk,
2622f127cb91Sfrankho 			    MIN(fsp->pcfs_clsize, fatsize - off));
2623f127cb91Sfrankho 			if (bp->b_flags & (B_ERROR | B_STALE)) {
2624f127cb91Sfrankho 				cmn_err(CE_NOTE,
2625f127cb91Sfrankho 				    "!pcfs: alternate FAT #%d (start LBA %p)"
2626f127cb91Sfrankho 				    " read error at offset %ld on device"
2627f127cb91Sfrankho 				    " (%x.%x):%d",
2628f127cb91Sfrankho 				    nfat, (void *)(uintptr_t)startsec, off,
2629f127cb91Sfrankho 				    getmajor(fsp->pcfs_xdev),
2630f127cb91Sfrankho 				    getminor(fsp->pcfs_xdev),
2631f127cb91Sfrankho 				    fsp->pcfs_ldrive);
2632f127cb91Sfrankho 				flags = B_ERROR;
2633f127cb91Sfrankho 				error = EIO;
2634f127cb91Sfrankho 				goto out;
2635f127cb91Sfrankho 			}
2636f127cb91Sfrankho 			bp->b_flags |= B_STALE | B_AGE;
2637f127cb91Sfrankho 			if (bcmp(bp->b_un.b_addr, fatp + off,
2638f127cb91Sfrankho 			    MIN(fsp->pcfs_clsize, fatsize - off))) {
2639f127cb91Sfrankho 				cmn_err(CE_NOTE,
2640f127cb91Sfrankho 				    "!pcfs: alternate FAT #%d (start LBA %p)"
2641f127cb91Sfrankho 				    " corrupted at offset %ld on device"
2642f127cb91Sfrankho 				    " (%x.%x):%d",
2643f127cb91Sfrankho 				    nfat, (void *)(uintptr_t)startsec, off,
2644f127cb91Sfrankho 				    getmajor(fsp->pcfs_xdev),
2645f127cb91Sfrankho 				    getminor(fsp->pcfs_xdev),
2646f127cb91Sfrankho 				    fsp->pcfs_ldrive);
2647f127cb91Sfrankho 				if (altfat_mustmatch) {
2648f127cb91Sfrankho 					flags = B_ERROR;
2649f127cb91Sfrankho 					error = EIO;
2650f127cb91Sfrankho 					goto out;
2651f127cb91Sfrankho 				}
2652f127cb91Sfrankho 			}
2653f127cb91Sfrankho 			brelse(bp);
2654f127cb91Sfrankho 			bp = NULL;	/* prevent double release */
26557c478bd9Sstevel@tonic-gate 		}
26567c478bd9Sstevel@tonic-gate 	}
26577c478bd9Sstevel@tonic-gate 
2658f127cb91Sfrankho 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
2659f127cb91Sfrankho 	fsp->pcfs_fatjustread = 1;
26607c478bd9Sstevel@tonic-gate 
2661f127cb91Sfrankho 	/*
2662f127cb91Sfrankho 	 * Retrieve FAT32 fsinfo sector.
2663f127cb91Sfrankho 	 * A failure to read this is not fatal to accessing the volume.
2664f127cb91Sfrankho 	 * It simply means operations that count or search free blocks
2665f127cb91Sfrankho 	 * will have to do a full FAT walk, vs. a possibly quicker lookup
2666f127cb91Sfrankho 	 * of the summary information.
2667f127cb91Sfrankho 	 * Hence, we log a message but return success overall after this point.
2668f127cb91Sfrankho 	 */
2669f127cb91Sfrankho 	if (IS_FAT32(fsp) && (fsp->pcfs_flags & PCFS_FSINFO_OK)) {
2670f127cb91Sfrankho 		struct fat_od_fsi *fsinfo_disk;
26717c478bd9Sstevel@tonic-gate 
2672f127cb91Sfrankho 		bp = bread(fsp->pcfs_xdev,
2673f127cb91Sfrankho 		    pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
2674f127cb91Sfrankho 		fsinfo_disk = (struct fat_od_fsi *)bp->b_un.b_addr;
2675f127cb91Sfrankho 		if (bp->b_flags & (B_ERROR | B_STALE) ||
2676f127cb91Sfrankho 		    !FSISIG_OK(fsinfo_disk)) {
2677f127cb91Sfrankho 			cmn_err(CE_NOTE,
2678f127cb91Sfrankho 			    "!pcfs: error reading fat32 fsinfo from "
2679f127cb91Sfrankho 			    "device (%x.%x):%d, block %lld",
2680f127cb91Sfrankho 			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
2681f127cb91Sfrankho 			    fsp->pcfs_ldrive,
2682f127cb91Sfrankho 			    (long long)pc_dbdaddr(fsp, fsp->pcfs_fsistart));
2683f127cb91Sfrankho 			fsp->pcfs_flags &= ~PCFS_FSINFO_OK;
2684f127cb91Sfrankho 			fsp->pcfs_fsinfo.fs_free_clusters = FSINFO_UNKNOWN;
2685f127cb91Sfrankho 			fsp->pcfs_fsinfo.fs_next_free = FSINFO_UNKNOWN;
2686f127cb91Sfrankho 		} else {
2687f127cb91Sfrankho 			bp->b_flags |= B_STALE | B_AGE;
2688f127cb91Sfrankho 			fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
2689f127cb91Sfrankho 			fsp->pcfs_fsinfo.fs_free_clusters =
2690f127cb91Sfrankho 			    LE_32(fsinfo_disk->fsi_incore.fs_free_clusters);
2691f127cb91Sfrankho 			fsp->pcfs_fsinfo.fs_next_free =
2692f127cb91Sfrankho 			    LE_32(fsinfo_disk->fsi_incore.fs_next_free);
26937c478bd9Sstevel@tonic-gate 		}
2694f127cb91Sfrankho 		brelse(bp);
2695f127cb91Sfrankho 		bp = NULL;
26967c478bd9Sstevel@tonic-gate 	}
2697264a6e74Sfrankho 
2698f127cb91Sfrankho 	if (pc_validcl(fsp, (pc_cluster32_t)fsp->pcfs_fsinfo.fs_next_free))
2699f127cb91Sfrankho 		fsp->pcfs_nxfrecls = fsp->pcfs_fsinfo.fs_next_free;
2700f127cb91Sfrankho 	else
2701f127cb91Sfrankho 		fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
2702264a6e74Sfrankho 
2703f127cb91Sfrankho 	return (0);
2704264a6e74Sfrankho 
2705f127cb91Sfrankho out:
2706f127cb91Sfrankho 	cmn_err(CE_NOTE, "!pcfs: illegal disk format");
2707f127cb91Sfrankho 	if (bp)
2708f127cb91Sfrankho 		brelse(bp);
2709f127cb91Sfrankho 	if (fatp)
2710f127cb91Sfrankho 		kmem_free(fatp, fatsize);
2711f127cb91Sfrankho 	if (fat_changemap)
2712f127cb91Sfrankho 		kmem_free(fat_changemap, fat_changemapsize);
2713264a6e74Sfrankho 
2714f127cb91Sfrankho 	if (flags) {
2715f127cb91Sfrankho 		pc_mark_irrecov(fsp);
2716f127cb91Sfrankho 	}
2717f127cb91Sfrankho 	return (error);
2718264a6e74Sfrankho }
2719