xref: /onnv-gate/usr/src/uts/common/fs/pcfs/pc_vfsops.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <sys/param.h>
30*0Sstevel@tonic-gate #include <sys/systm.h>
31*0Sstevel@tonic-gate #include <sys/kmem.h>
32*0Sstevel@tonic-gate #include <sys/user.h>
33*0Sstevel@tonic-gate #include <sys/proc.h>
34*0Sstevel@tonic-gate #include <sys/cred.h>
35*0Sstevel@tonic-gate #include <sys/disp.h>
36*0Sstevel@tonic-gate #include <sys/buf.h>
37*0Sstevel@tonic-gate #include <sys/vfs.h>
38*0Sstevel@tonic-gate #include <sys/vnode.h>
39*0Sstevel@tonic-gate #include <sys/fdio.h>
40*0Sstevel@tonic-gate #include <sys/file.h>
41*0Sstevel@tonic-gate #include <sys/uio.h>
42*0Sstevel@tonic-gate #include <sys/conf.h>
43*0Sstevel@tonic-gate #undef NFSCLIENT
44*0Sstevel@tonic-gate #include <sys/statvfs.h>
45*0Sstevel@tonic-gate #include <sys/mount.h>
46*0Sstevel@tonic-gate #include <sys/pathname.h>
47*0Sstevel@tonic-gate #include <sys/cmn_err.h>
48*0Sstevel@tonic-gate #include <sys/debug.h>
49*0Sstevel@tonic-gate #include <sys/sysmacros.h>
50*0Sstevel@tonic-gate #include <sys/conf.h>
51*0Sstevel@tonic-gate #include <sys/mkdev.h>
52*0Sstevel@tonic-gate #include <sys/swap.h>
53*0Sstevel@tonic-gate #include <sys/sunddi.h>
54*0Sstevel@tonic-gate #include <sys/sunldi.h>
55*0Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
56*0Sstevel@tonic-gate #include <sys/fs/pc_label.h>
57*0Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
58*0Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
59*0Sstevel@tonic-gate #include <sys/fs/pc_node.h>
60*0Sstevel@tonic-gate #include <fs/fs_subr.h>
61*0Sstevel@tonic-gate #include <sys/modctl.h>
62*0Sstevel@tonic-gate #include <sys/vol.h>
63*0Sstevel@tonic-gate #include <sys/dkio.h>
64*0Sstevel@tonic-gate #include <sys/open.h>
65*0Sstevel@tonic-gate #include <sys/mntent.h>
66*0Sstevel@tonic-gate #include <sys/policy.h>
67*0Sstevel@tonic-gate 
68*0Sstevel@tonic-gate /*
69*0Sstevel@tonic-gate  * The majority of PC media use a 512 sector size, but
70*0Sstevel@tonic-gate  * occasionally you will run across a 1k sector size.
71*0Sstevel@tonic-gate  * For media with a 1k sector size, fd_strategy() requires
72*0Sstevel@tonic-gate  * the I/O size to be a 1k multiple; so when the sector size
73*0Sstevel@tonic-gate  * is not yet known, always read 1k.
74*0Sstevel@tonic-gate  */
75*0Sstevel@tonic-gate #define	PC_SAFESECSIZE	(PC_SECSIZE * 2)
76*0Sstevel@tonic-gate 
77*0Sstevel@tonic-gate static int pcfs_psuedo_floppy(dev_t);
78*0Sstevel@tonic-gate 
79*0Sstevel@tonic-gate static int pcfsinit(int, char *);
80*0Sstevel@tonic-gate static int pcfs_mount(struct vfs *, struct vnode *, struct mounta *,
81*0Sstevel@tonic-gate 	struct cred *);
82*0Sstevel@tonic-gate static int pcfs_unmount(struct vfs *, int, struct cred *);
83*0Sstevel@tonic-gate static int pcfs_root(struct vfs *, struct vnode **);
84*0Sstevel@tonic-gate static int pcfs_statvfs(struct vfs *, struct statvfs64 *);
85*0Sstevel@tonic-gate static int pc_syncfsnodes(struct pcfs *);
86*0Sstevel@tonic-gate static int pcfs_sync(struct vfs *, short, struct cred *);
87*0Sstevel@tonic-gate static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp);
88*0Sstevel@tonic-gate 
89*0Sstevel@tonic-gate static int pc_getfattype(struct vnode *, int, daddr_t *, int *);
90*0Sstevel@tonic-gate static int pc_readfat(struct pcfs *fsp, uchar_t *fatp, daddr_t start,
91*0Sstevel@tonic-gate     size_t fatsize);
92*0Sstevel@tonic-gate static int pc_writefat(struct pcfs *fsp, daddr_t start);
93*0Sstevel@tonic-gate 
94*0Sstevel@tonic-gate /*
95*0Sstevel@tonic-gate  * pcfs mount options table
96*0Sstevel@tonic-gate  */
97*0Sstevel@tonic-gate 
98*0Sstevel@tonic-gate static char *nohidden_cancel[] = {MNTOPT_PCFS_HIDDEN, NULL};
99*0Sstevel@tonic-gate static char *hidden_cancel[] = {MNTOPT_PCFS_NOHIDDEN, NULL};
100*0Sstevel@tonic-gate static char *nofoldcase_cancel[] = {MNTOPT_PCFS_FOLDCASE, NULL};
101*0Sstevel@tonic-gate static char *foldcase_cancel[] = {MNTOPT_PCFS_NOFOLDCASE, NULL};
102*0Sstevel@tonic-gate 
103*0Sstevel@tonic-gate static mntopt_t mntopts[] = {
104*0Sstevel@tonic-gate /*
105*0Sstevel@tonic-gate  *	option name		cancel option	default arg	flags
106*0Sstevel@tonic-gate  *		opt data
107*0Sstevel@tonic-gate  */
108*0Sstevel@tonic-gate 	{ MNTOPT_PCFS_NOHIDDEN,	nohidden_cancel, NULL,		MO_DEFAULT,
109*0Sstevel@tonic-gate 		NULL },
110*0Sstevel@tonic-gate 	{ MNTOPT_PCFS_HIDDEN,	hidden_cancel, NULL,		0,
111*0Sstevel@tonic-gate 		NULL },
112*0Sstevel@tonic-gate 	{ MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL,	MO_DEFAULT,
113*0Sstevel@tonic-gate 		NULL },
114*0Sstevel@tonic-gate 	{ MNTOPT_PCFS_FOLDCASE,	foldcase_cancel, NULL,		0,
115*0Sstevel@tonic-gate 		NULL }
116*0Sstevel@tonic-gate };
117*0Sstevel@tonic-gate 
118*0Sstevel@tonic-gate static mntopts_t pcfs_mntopts = {
119*0Sstevel@tonic-gate 	sizeof (mntopts) / sizeof (mntopt_t),
120*0Sstevel@tonic-gate 	mntopts
121*0Sstevel@tonic-gate };
122*0Sstevel@tonic-gate 
123*0Sstevel@tonic-gate int pcfsdebuglevel = 0;
124*0Sstevel@tonic-gate 
125*0Sstevel@tonic-gate /*
126*0Sstevel@tonic-gate  * pcfslock:	protects the list of mounted pc filesystems "pc_mounttab.
127*0Sstevel@tonic-gate  * pcfs_lock:	(inside per filesystem structure "pcfs")
128*0Sstevel@tonic-gate  *		per filesystem lock. Most of the vfsops and vnodeops are
129*0Sstevel@tonic-gate  *		protected by this lock.
130*0Sstevel@tonic-gate  * pcnodes_lock: protects the pcnode hash table "pcdhead", "pcfhead".
131*0Sstevel@tonic-gate  *
132*0Sstevel@tonic-gate  * Lock hierarchy: pcfslock > pcfs_lock > pcnodes_lock
133*0Sstevel@tonic-gate  */
134*0Sstevel@tonic-gate kmutex_t	pcfslock;
135*0Sstevel@tonic-gate krwlock_t pcnodes_lock; /* protect the pcnode hash table "pcdhead", "pcfhead" */
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate static int pcfstype;
138*0Sstevel@tonic-gate 
139*0Sstevel@tonic-gate static vfsdef_t vfw = {
140*0Sstevel@tonic-gate 	VFSDEF_VERSION,
141*0Sstevel@tonic-gate 	"pcfs",
142*0Sstevel@tonic-gate 	pcfsinit,
143*0Sstevel@tonic-gate 	VSW_HASPROTO|VSW_CANREMOUNT,
144*0Sstevel@tonic-gate 	&pcfs_mntopts
145*0Sstevel@tonic-gate };
146*0Sstevel@tonic-gate 
147*0Sstevel@tonic-gate extern struct mod_ops mod_fsops;
148*0Sstevel@tonic-gate 
149*0Sstevel@tonic-gate static struct modlfs modlfs = {
150*0Sstevel@tonic-gate 	&mod_fsops,
151*0Sstevel@tonic-gate 	"filesystem for PC",
152*0Sstevel@tonic-gate 	&vfw
153*0Sstevel@tonic-gate };
154*0Sstevel@tonic-gate 
155*0Sstevel@tonic-gate static struct modlinkage modlinkage = {
156*0Sstevel@tonic-gate 	MODREV_1,
157*0Sstevel@tonic-gate 	&modlfs,
158*0Sstevel@tonic-gate 	NULL
159*0Sstevel@tonic-gate };
160*0Sstevel@tonic-gate 
161*0Sstevel@tonic-gate int
162*0Sstevel@tonic-gate _init(void)
163*0Sstevel@tonic-gate {
164*0Sstevel@tonic-gate 	int	error;
165*0Sstevel@tonic-gate 
166*0Sstevel@tonic-gate #if !defined(lint)
167*0Sstevel@tonic-gate 	/* make sure the on-disk structures are sane */
168*0Sstevel@tonic-gate 	ASSERT(sizeof (struct pcdir) == 32);
169*0Sstevel@tonic-gate 	ASSERT(sizeof (struct pcdir_lfn) == 32);
170*0Sstevel@tonic-gate #endif
171*0Sstevel@tonic-gate 	mutex_init(&pcfslock, NULL, MUTEX_DEFAULT, NULL);
172*0Sstevel@tonic-gate 	rw_init(&pcnodes_lock, NULL, RW_DEFAULT, NULL);
173*0Sstevel@tonic-gate 	error = mod_install(&modlinkage);
174*0Sstevel@tonic-gate 	if (error) {
175*0Sstevel@tonic-gate 		mutex_destroy(&pcfslock);
176*0Sstevel@tonic-gate 		rw_destroy(&pcnodes_lock);
177*0Sstevel@tonic-gate 	}
178*0Sstevel@tonic-gate 	return (error);
179*0Sstevel@tonic-gate }
180*0Sstevel@tonic-gate 
181*0Sstevel@tonic-gate int
182*0Sstevel@tonic-gate _fini(void)
183*0Sstevel@tonic-gate {
184*0Sstevel@tonic-gate 	int	error;
185*0Sstevel@tonic-gate 
186*0Sstevel@tonic-gate 	error = mod_remove(&modlinkage);
187*0Sstevel@tonic-gate 	if (error)
188*0Sstevel@tonic-gate 		return (error);
189*0Sstevel@tonic-gate 	mutex_destroy(&pcfslock);
190*0Sstevel@tonic-gate 	rw_destroy(&pcnodes_lock);
191*0Sstevel@tonic-gate 	/*
192*0Sstevel@tonic-gate 	 * Tear down the operations vectors
193*0Sstevel@tonic-gate 	 */
194*0Sstevel@tonic-gate 	(void) vfs_freevfsops_by_type(pcfstype);
195*0Sstevel@tonic-gate 	vn_freevnodeops(pcfs_fvnodeops);
196*0Sstevel@tonic-gate 	vn_freevnodeops(pcfs_dvnodeops);
197*0Sstevel@tonic-gate 	return (0);
198*0Sstevel@tonic-gate }
199*0Sstevel@tonic-gate 
200*0Sstevel@tonic-gate int
201*0Sstevel@tonic-gate _info(struct modinfo *modinfop)
202*0Sstevel@tonic-gate {
203*0Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
204*0Sstevel@tonic-gate }
205*0Sstevel@tonic-gate 
206*0Sstevel@tonic-gate /* ARGSUSED1 */
207*0Sstevel@tonic-gate static int
208*0Sstevel@tonic-gate pcfsinit(int fstype, char *name)
209*0Sstevel@tonic-gate {
210*0Sstevel@tonic-gate 	static const fs_operation_def_t pcfs_vfsops_template[] = {
211*0Sstevel@tonic-gate 		VFSNAME_MOUNT, pcfs_mount,
212*0Sstevel@tonic-gate 		VFSNAME_UNMOUNT, pcfs_unmount,
213*0Sstevel@tonic-gate 		VFSNAME_ROOT, pcfs_root,
214*0Sstevel@tonic-gate 		VFSNAME_STATVFS, pcfs_statvfs,
215*0Sstevel@tonic-gate 		VFSNAME_SYNC, (fs_generic_func_p) pcfs_sync,
216*0Sstevel@tonic-gate 		VFSNAME_VGET, pcfs_vget,
217*0Sstevel@tonic-gate 		NULL, NULL
218*0Sstevel@tonic-gate 	};
219*0Sstevel@tonic-gate 	int error;
220*0Sstevel@tonic-gate 
221*0Sstevel@tonic-gate 	error = vfs_setfsops(fstype, pcfs_vfsops_template, NULL);
222*0Sstevel@tonic-gate 	if (error != 0) {
223*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad vfs ops template");
224*0Sstevel@tonic-gate 		return (error);
225*0Sstevel@tonic-gate 	}
226*0Sstevel@tonic-gate 
227*0Sstevel@tonic-gate 	error = vn_make_ops("pcfs", pcfs_fvnodeops_template, &pcfs_fvnodeops);
228*0Sstevel@tonic-gate 	if (error != 0) {
229*0Sstevel@tonic-gate 		(void) vfs_freevfsops_by_type(fstype);
230*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad file vnode ops template");
231*0Sstevel@tonic-gate 		return (error);
232*0Sstevel@tonic-gate 	}
233*0Sstevel@tonic-gate 
234*0Sstevel@tonic-gate 	error = vn_make_ops("pcfsd", pcfs_dvnodeops_template, &pcfs_dvnodeops);
235*0Sstevel@tonic-gate 	if (error != 0) {
236*0Sstevel@tonic-gate 		(void) vfs_freevfsops_by_type(fstype);
237*0Sstevel@tonic-gate 		vn_freevnodeops(pcfs_fvnodeops);
238*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad dir vnode ops template");
239*0Sstevel@tonic-gate 		return (error);
240*0Sstevel@tonic-gate 	}
241*0Sstevel@tonic-gate 
242*0Sstevel@tonic-gate 	pcfstype = fstype;
243*0Sstevel@tonic-gate 	(void) pc_init();
244*0Sstevel@tonic-gate 	return (0);
245*0Sstevel@tonic-gate }
246*0Sstevel@tonic-gate 
247*0Sstevel@tonic-gate static struct pcfs *pc_mounttab = NULL;
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate extern struct pcfs_args pc_tz;
250*0Sstevel@tonic-gate 
251*0Sstevel@tonic-gate /*
252*0Sstevel@tonic-gate  *  Define some special logical drives we use internal to this file.
253*0Sstevel@tonic-gate  */
254*0Sstevel@tonic-gate #define	BOOT_PARTITION_DRIVE	99
255*0Sstevel@tonic-gate #define	PRIMARY_DOS_DRIVE	1
256*0Sstevel@tonic-gate 
257*0Sstevel@tonic-gate /*
258*0Sstevel@tonic-gate  * pc_mount system call
259*0Sstevel@tonic-gate  */
260*0Sstevel@tonic-gate static int
261*0Sstevel@tonic-gate pcfs_mount(
262*0Sstevel@tonic-gate 	struct vfs *vfsp,
263*0Sstevel@tonic-gate 	struct vnode *mvp,
264*0Sstevel@tonic-gate 	struct mounta *uap,
265*0Sstevel@tonic-gate 	struct cred *cr)
266*0Sstevel@tonic-gate {
267*0Sstevel@tonic-gate 	struct pcfs *fsp;
268*0Sstevel@tonic-gate 	struct vnode *bvp;
269*0Sstevel@tonic-gate 	struct vnode *devvp;
270*0Sstevel@tonic-gate 	struct pathname special;
271*0Sstevel@tonic-gate 	daddr_t dosstart;
272*0Sstevel@tonic-gate 	dev_t pseudodev;
273*0Sstevel@tonic-gate 	dev_t xdev;
274*0Sstevel@tonic-gate 	char *spnp;
275*0Sstevel@tonic-gate 	char *data = uap->dataptr;
276*0Sstevel@tonic-gate 	int datalen = uap->datalen;
277*0Sstevel@tonic-gate 	int dos_ldrive = 0;
278*0Sstevel@tonic-gate 	int error;
279*0Sstevel@tonic-gate 	int fattype;
280*0Sstevel@tonic-gate 	int spnlen;
281*0Sstevel@tonic-gate 	int wantbootpart = 0;
282*0Sstevel@tonic-gate 	struct vioc_info info;
283*0Sstevel@tonic-gate 	int rval;		/* set but not used */
284*0Sstevel@tonic-gate 	minor_t	minor;
285*0Sstevel@tonic-gate 	int oflag, aflag;
286*0Sstevel@tonic-gate 
287*0Sstevel@tonic-gate 	if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
288*0Sstevel@tonic-gate 		return (error);
289*0Sstevel@tonic-gate 
290*0Sstevel@tonic-gate 	PC_DPRINTF0(4, "pcfs_mount\n");
291*0Sstevel@tonic-gate 	if (mvp->v_type != VDIR) {
292*0Sstevel@tonic-gate 		return (ENOTDIR);
293*0Sstevel@tonic-gate 	}
294*0Sstevel@tonic-gate 	mutex_enter(&mvp->v_lock);
295*0Sstevel@tonic-gate 	if ((uap->flags & MS_REMOUNT) == 0 &&
296*0Sstevel@tonic-gate 	    (uap->flags & MS_OVERLAY) == 0 &&
297*0Sstevel@tonic-gate 	    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
298*0Sstevel@tonic-gate 		mutex_exit(&mvp->v_lock);
299*0Sstevel@tonic-gate 		return (EBUSY);
300*0Sstevel@tonic-gate 	}
301*0Sstevel@tonic-gate 	mutex_exit(&mvp->v_lock);
302*0Sstevel@tonic-gate 
303*0Sstevel@tonic-gate 	/*
304*0Sstevel@tonic-gate 	 * The caller is responsible for making sure to always
305*0Sstevel@tonic-gate 	 * pass in sizeof(struct pcfs_args) (or the old one).
306*0Sstevel@tonic-gate 	 * Doing this is the only way to know an EINVAL return
307*0Sstevel@tonic-gate 	 * from mount(2) is due to the "not a DOS filesystem"
308*0Sstevel@tonic-gate 	 * EINVAL that pc_verify/pc_getfattype could return.
309*0Sstevel@tonic-gate 	 */
310*0Sstevel@tonic-gate 	if ((datalen != sizeof (struct pcfs_args)) &&
311*0Sstevel@tonic-gate 	    (datalen != sizeof (struct old_pcfs_args))) {
312*0Sstevel@tonic-gate 		return (EINVAL);
313*0Sstevel@tonic-gate 	} else {
314*0Sstevel@tonic-gate 		struct pcfs_args tmp_tz;
315*0Sstevel@tonic-gate 		int hidden = 0;
316*0Sstevel@tonic-gate 		int foldcase = 0;
317*0Sstevel@tonic-gate 
318*0Sstevel@tonic-gate 		tmp_tz.flags = 0;
319*0Sstevel@tonic-gate 		if (copyin(data, &tmp_tz, datalen)) {
320*0Sstevel@tonic-gate 			return (EFAULT);
321*0Sstevel@tonic-gate 		}
322*0Sstevel@tonic-gate 		if (datalen == sizeof (struct pcfs_args)) {
323*0Sstevel@tonic-gate 			hidden = tmp_tz.flags & PCFS_MNT_HIDDEN;
324*0Sstevel@tonic-gate 			foldcase = tmp_tz.flags & PCFS_MNT_FOLDCASE;
325*0Sstevel@tonic-gate 		}
326*0Sstevel@tonic-gate 
327*0Sstevel@tonic-gate 		if (hidden)
328*0Sstevel@tonic-gate 			vfs_setmntopt(vfsp, MNTOPT_PCFS_HIDDEN,	NULL, 0);
329*0Sstevel@tonic-gate 		if (foldcase)
330*0Sstevel@tonic-gate 			vfs_setmntopt(vfsp, MNTOPT_PCFS_FOLDCASE, NULL, 0);
331*0Sstevel@tonic-gate 		/*
332*0Sstevel@tonic-gate 		 * more than one pc filesystem can be mounted on x86
333*0Sstevel@tonic-gate 		 * so the pc_tz structure is now a critical region
334*0Sstevel@tonic-gate 		 */
335*0Sstevel@tonic-gate 		mutex_enter(&pcfslock);
336*0Sstevel@tonic-gate 		if (pc_mounttab == NULL)
337*0Sstevel@tonic-gate 			bcopy(&tmp_tz, &pc_tz, sizeof (struct pcfs_args));
338*0Sstevel@tonic-gate 		mutex_exit(&pcfslock);
339*0Sstevel@tonic-gate 	}
340*0Sstevel@tonic-gate 	/*
341*0Sstevel@tonic-gate 	 * Resolve path name of special file being mounted.
342*0Sstevel@tonic-gate 	 */
343*0Sstevel@tonic-gate 	if (error = pn_get(uap->spec, UIO_USERSPACE, &special)) {
344*0Sstevel@tonic-gate 		return (error);
345*0Sstevel@tonic-gate 	}
346*0Sstevel@tonic-gate 	if (error =
347*0Sstevel@tonic-gate 	    lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &bvp)) {
348*0Sstevel@tonic-gate 		/*
349*0Sstevel@tonic-gate 		 * look for suffix to special
350*0Sstevel@tonic-gate 		 * which indicates a request to mount the solaris boot
351*0Sstevel@tonic-gate 		 * partition, or a DOS logical drive on the hard disk
352*0Sstevel@tonic-gate 		 */
353*0Sstevel@tonic-gate 		spnlen = special.pn_pathlen;
354*0Sstevel@tonic-gate 
355*0Sstevel@tonic-gate 		if (spnlen > 5) {
356*0Sstevel@tonic-gate 			spnp = special.pn_path + spnlen - 5;
357*0Sstevel@tonic-gate 			if (*spnp++ == ':' && *spnp++ == 'b' &&
358*0Sstevel@tonic-gate 			    *spnp++ == 'o' && *spnp++ == 'o' &&
359*0Sstevel@tonic-gate 			    *spnp++ == 't') {
360*0Sstevel@tonic-gate 				/*
361*0Sstevel@tonic-gate 				 * Looks as if they want to mount
362*0Sstevel@tonic-gate 				 * the Solaris boot partition
363*0Sstevel@tonic-gate 				 */
364*0Sstevel@tonic-gate 				wantbootpart = 1;
365*0Sstevel@tonic-gate 				dos_ldrive = BOOT_PARTITION_DRIVE;
366*0Sstevel@tonic-gate 				spnp = special.pn_path + spnlen - 5;
367*0Sstevel@tonic-gate 				*spnp = '\0';
368*0Sstevel@tonic-gate 				error = lookupname(special.pn_path,
369*0Sstevel@tonic-gate 				    UIO_SYSSPACE, FOLLOW, NULLVPP, &bvp);
370*0Sstevel@tonic-gate 			}
371*0Sstevel@tonic-gate 		}
372*0Sstevel@tonic-gate 
373*0Sstevel@tonic-gate 		if (!wantbootpart) {
374*0Sstevel@tonic-gate 			spnp = special.pn_path + spnlen - 1;
375*0Sstevel@tonic-gate 			if (spnlen > 2 && *spnp >= 'c' && *spnp <= 'z') {
376*0Sstevel@tonic-gate 				spnlen--;
377*0Sstevel@tonic-gate 				dos_ldrive = *spnp-- - 'c' + 1;
378*0Sstevel@tonic-gate 			} else if (spnlen > 2 && *spnp >= '0' && *spnp <= '9') {
379*0Sstevel@tonic-gate 				spnlen--;
380*0Sstevel@tonic-gate 				dos_ldrive = *spnp-- - '0';
381*0Sstevel@tonic-gate 				if (spnlen > 2 && *spnp >= '0' &&
382*0Sstevel@tonic-gate 				    *spnp <= '9') {
383*0Sstevel@tonic-gate 					spnlen--;
384*0Sstevel@tonic-gate 					dos_ldrive += 10 * (*spnp-- - '0');
385*0Sstevel@tonic-gate 				}
386*0Sstevel@tonic-gate 			}
387*0Sstevel@tonic-gate 			if (spnlen > 1 && dos_ldrive && dos_ldrive <= 24 &&
388*0Sstevel@tonic-gate 			    *spnp == ':') {
389*0Sstevel@tonic-gate 				/*
390*0Sstevel@tonic-gate 				 * remove suffix so that we have a real
391*0Sstevel@tonic-gate 				 * device name
392*0Sstevel@tonic-gate 				 */
393*0Sstevel@tonic-gate 				*spnp = '\0';
394*0Sstevel@tonic-gate 				error = lookupname(special.pn_path,
395*0Sstevel@tonic-gate 				    UIO_SYSSPACE, FOLLOW, NULLVPP, &bvp);
396*0Sstevel@tonic-gate 			}
397*0Sstevel@tonic-gate 		}
398*0Sstevel@tonic-gate 		if (error) {
399*0Sstevel@tonic-gate 			pn_free(&special);
400*0Sstevel@tonic-gate 			return (error);
401*0Sstevel@tonic-gate 		}
402*0Sstevel@tonic-gate 	}
403*0Sstevel@tonic-gate 	pn_free(&special);
404*0Sstevel@tonic-gate 	if (bvp->v_type != VBLK) {
405*0Sstevel@tonic-gate 		VN_RELE(bvp);
406*0Sstevel@tonic-gate 		return (ENOTBLK);
407*0Sstevel@tonic-gate 	}
408*0Sstevel@tonic-gate 	xdev = bvp->v_rdev;
409*0Sstevel@tonic-gate 	/*
410*0Sstevel@tonic-gate 	 * Verify caller's permission to open the device special file.
411*0Sstevel@tonic-gate 	 */
412*0Sstevel@tonic-gate 	if ((vfsp->vfs_flag & VFS_RDONLY) != 0 ||
413*0Sstevel@tonic-gate 	    ((uap->flags & MS_RDONLY) != 0)) {
414*0Sstevel@tonic-gate 		oflag = FREAD;
415*0Sstevel@tonic-gate 		aflag = VREAD;
416*0Sstevel@tonic-gate 	} else {
417*0Sstevel@tonic-gate 		oflag = FREAD | FWRITE;
418*0Sstevel@tonic-gate 		aflag = VREAD | VWRITE;
419*0Sstevel@tonic-gate 	}
420*0Sstevel@tonic-gate 	if ((error = VOP_ACCESS(bvp, aflag, 0, cr)) != 0 ||
421*0Sstevel@tonic-gate 	    (error = secpolicy_spec_open(cr, bvp, oflag)) != 0) {
422*0Sstevel@tonic-gate 		VN_RELE(bvp);
423*0Sstevel@tonic-gate 		return (error);
424*0Sstevel@tonic-gate 	}
425*0Sstevel@tonic-gate 
426*0Sstevel@tonic-gate 	VN_RELE(bvp);
427*0Sstevel@tonic-gate 	if (getmajor(xdev) >= devcnt) {
428*0Sstevel@tonic-gate 		return (ENXIO);
429*0Sstevel@tonic-gate 	}
430*0Sstevel@tonic-gate 	/*
431*0Sstevel@tonic-gate 	 * Ensure that this device (or logical drive) isn't already mounted,
432*0Sstevel@tonic-gate 	 * unless this is a REMOUNT request
433*0Sstevel@tonic-gate 	 */
434*0Sstevel@tonic-gate 	if (dos_ldrive) {
435*0Sstevel@tonic-gate 		mutex_enter(&pcfslock);
436*0Sstevel@tonic-gate 		for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt)
437*0Sstevel@tonic-gate 			if (fsp->pcfs_xdev == xdev &&
438*0Sstevel@tonic-gate 			    fsp->pcfs_ldrv == dos_ldrive) {
439*0Sstevel@tonic-gate 				mutex_exit(&pcfslock);
440*0Sstevel@tonic-gate 				if (uap->flags & MS_REMOUNT) {
441*0Sstevel@tonic-gate 					return (0);
442*0Sstevel@tonic-gate 				} else {
443*0Sstevel@tonic-gate 					return (EBUSY);
444*0Sstevel@tonic-gate 				}
445*0Sstevel@tonic-gate 			}
446*0Sstevel@tonic-gate 		/*
447*0Sstevel@tonic-gate 		 * Assign a unique device number for the vfs
448*0Sstevel@tonic-gate 		 * The old way (getudev() + a constantly incrementing
449*0Sstevel@tonic-gate 		 * major number) was wrong because it changes vfs_dev
450*0Sstevel@tonic-gate 		 * across mounts and reboots, which breaks nfs file handles.
451*0Sstevel@tonic-gate 		 * UFS just uses the real dev_t. We can't do that because
452*0Sstevel@tonic-gate 		 * of the way pcfs opens fdisk partitons (the :c and :d
453*0Sstevel@tonic-gate 		 * partitions are on the same dev_t). Though that _might_
454*0Sstevel@tonic-gate 		 * actually be ok, since the file handle contains an
455*0Sstevel@tonic-gate 		 * absolute block number, it's probably better to make them
456*0Sstevel@tonic-gate 		 * different. So I think we should retain the original
457*0Sstevel@tonic-gate 		 * dev_t, but come up with a different minor number based
458*0Sstevel@tonic-gate 		 * on the logical drive that will _always_ come up the same.
459*0Sstevel@tonic-gate 		 * For now, we steal the upper 6 bits.
460*0Sstevel@tonic-gate 		 */
461*0Sstevel@tonic-gate #ifdef notdef
462*0Sstevel@tonic-gate 		/* what should we do here? */
463*0Sstevel@tonic-gate 		if (((getminor(xdev) >> 12) & 0x3F) != 0)
464*0Sstevel@tonic-gate 			printf("whoops - upper bits used!\n");
465*0Sstevel@tonic-gate #endif
466*0Sstevel@tonic-gate 		minor = ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32;
467*0Sstevel@tonic-gate 		pseudodev = makedevice(getmajor(xdev), minor);
468*0Sstevel@tonic-gate 		if (vfs_devmounting(pseudodev, vfsp)) {
469*0Sstevel@tonic-gate 			mutex_exit(&pcfslock);
470*0Sstevel@tonic-gate 			return (EBUSY);
471*0Sstevel@tonic-gate 		}
472*0Sstevel@tonic-gate 		if (vfs_devismounted(pseudodev)) {
473*0Sstevel@tonic-gate 			mutex_exit(&pcfslock);
474*0Sstevel@tonic-gate 			if (uap->flags & MS_REMOUNT) {
475*0Sstevel@tonic-gate 				return (0);
476*0Sstevel@tonic-gate 			} else {
477*0Sstevel@tonic-gate 				return (EBUSY);
478*0Sstevel@tonic-gate 			}
479*0Sstevel@tonic-gate 		}
480*0Sstevel@tonic-gate 		mutex_exit(&pcfslock);
481*0Sstevel@tonic-gate 	} else {
482*0Sstevel@tonic-gate 		if (vfs_devmounting(xdev, vfsp)) {
483*0Sstevel@tonic-gate 			return (EBUSY);
484*0Sstevel@tonic-gate 		}
485*0Sstevel@tonic-gate 		if (vfs_devismounted(xdev))
486*0Sstevel@tonic-gate 			if (uap->flags & MS_REMOUNT) {
487*0Sstevel@tonic-gate 				return (0);
488*0Sstevel@tonic-gate 			} else {
489*0Sstevel@tonic-gate 				return (EBUSY);
490*0Sstevel@tonic-gate 			}
491*0Sstevel@tonic-gate 		pseudodev = xdev;
492*0Sstevel@tonic-gate 	}
493*0Sstevel@tonic-gate 
494*0Sstevel@tonic-gate 	if (uap->flags & MS_RDONLY) {
495*0Sstevel@tonic-gate 		vfsp->vfs_flag |= VFS_RDONLY;
496*0Sstevel@tonic-gate 		vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
497*0Sstevel@tonic-gate 	}
498*0Sstevel@tonic-gate 
499*0Sstevel@tonic-gate 	/*
500*0Sstevel@tonic-gate 	 * Mount the filesystem
501*0Sstevel@tonic-gate 	 */
502*0Sstevel@tonic-gate 	devvp = makespecvp(xdev, VBLK);
503*0Sstevel@tonic-gate 	if (IS_SWAPVP(devvp)) {
504*0Sstevel@tonic-gate 		VN_RELE(devvp);
505*0Sstevel@tonic-gate 		return (EBUSY);
506*0Sstevel@tonic-gate 	}
507*0Sstevel@tonic-gate 
508*0Sstevel@tonic-gate 	/*
509*0Sstevel@tonic-gate 	 * special handling for PCMCIA memory card
510*0Sstevel@tonic-gate 	 * with psuedo floppies organization
511*0Sstevel@tonic-gate 	 */
512*0Sstevel@tonic-gate 	if (dos_ldrive == 0 && pcfs_psuedo_floppy(xdev)) {
513*0Sstevel@tonic-gate 		dosstart = (daddr_t)0;
514*0Sstevel@tonic-gate 		fattype = PCFS_PCMCIA_NO_CIS;
515*0Sstevel@tonic-gate 	} else {
516*0Sstevel@tonic-gate 		if (error = pc_getfattype(devvp, dos_ldrive, &dosstart,
517*0Sstevel@tonic-gate 		    &fattype)) {
518*0Sstevel@tonic-gate 			VN_RELE(devvp);
519*0Sstevel@tonic-gate 			return (error);
520*0Sstevel@tonic-gate 		}
521*0Sstevel@tonic-gate 	}
522*0Sstevel@tonic-gate 
523*0Sstevel@tonic-gate 	(void) VOP_PUTPAGE(devvp, (offset_t)0, (uint_t)0, B_INVAL, cr);
524*0Sstevel@tonic-gate 	fsp = kmem_zalloc((uint_t)sizeof (struct pcfs), KM_SLEEP);
525*0Sstevel@tonic-gate 	fsp->pcfs_vfs = vfsp;
526*0Sstevel@tonic-gate 	fsp->pcfs_flags = fattype;
527*0Sstevel@tonic-gate 	fsp->pcfs_devvp = devvp;
528*0Sstevel@tonic-gate 	fsp->pcfs_xdev = xdev;
529*0Sstevel@tonic-gate 	fsp->pcfs_ldrv = dos_ldrive;
530*0Sstevel@tonic-gate 	fsp->pcfs_dosstart = dosstart;
531*0Sstevel@tonic-gate 	mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL);
532*0Sstevel@tonic-gate 
533*0Sstevel@tonic-gate 	/* set the "nocheck" flag if volmgt is managing this volume */
534*0Sstevel@tonic-gate 	info.vii_pathlen = 0;
535*0Sstevel@tonic-gate 	info.vii_devpath = 0;
536*0Sstevel@tonic-gate 	error = cdev_ioctl(fsp->pcfs_xdev, VOLIOCINFO, (intptr_t)&info,
537*0Sstevel@tonic-gate 	    FKIOCTL|FREAD, kcred, &rval);
538*0Sstevel@tonic-gate 	if (error == 0) {
539*0Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_NOCHK;
540*0Sstevel@tonic-gate 	}
541*0Sstevel@tonic-gate 
542*0Sstevel@tonic-gate 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL))
543*0Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_HIDDEN;
544*0Sstevel@tonic-gate 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL))
545*0Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_FOLDCASE;
546*0Sstevel@tonic-gate 	vfsp->vfs_dev = pseudodev;
547*0Sstevel@tonic-gate 	vfsp->vfs_fstype = pcfstype;
548*0Sstevel@tonic-gate 	vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype);
549*0Sstevel@tonic-gate 	vfsp->vfs_data = (caddr_t)fsp;
550*0Sstevel@tonic-gate 	vfsp->vfs_bcount = 0;
551*0Sstevel@tonic-gate 
552*0Sstevel@tonic-gate 	error = pc_verify(fsp);
553*0Sstevel@tonic-gate 	if (error) {
554*0Sstevel@tonic-gate 		VN_RELE(devvp);
555*0Sstevel@tonic-gate 		mutex_destroy(&fsp->pcfs_lock);
556*0Sstevel@tonic-gate 		kmem_free(fsp, (uint_t)sizeof (struct pcfs));
557*0Sstevel@tonic-gate 		return (error);
558*0Sstevel@tonic-gate 	}
559*0Sstevel@tonic-gate 	vfsp->vfs_bsize = fsp->pcfs_clsize;
560*0Sstevel@tonic-gate 
561*0Sstevel@tonic-gate 	mutex_enter(&pcfslock);
562*0Sstevel@tonic-gate 	fsp->pcfs_nxt = pc_mounttab;
563*0Sstevel@tonic-gate 	pc_mounttab = fsp;
564*0Sstevel@tonic-gate 	mutex_exit(&pcfslock);
565*0Sstevel@tonic-gate 	return (0);
566*0Sstevel@tonic-gate }
567*0Sstevel@tonic-gate 
568*0Sstevel@tonic-gate /*
569*0Sstevel@tonic-gate  * vfs operations
570*0Sstevel@tonic-gate  */
571*0Sstevel@tonic-gate 
572*0Sstevel@tonic-gate /* ARGSUSED */
573*0Sstevel@tonic-gate static int
574*0Sstevel@tonic-gate pcfs_unmount(
575*0Sstevel@tonic-gate 	struct vfs *vfsp,
576*0Sstevel@tonic-gate 	int flag,
577*0Sstevel@tonic-gate 	struct cred *cr)
578*0Sstevel@tonic-gate {
579*0Sstevel@tonic-gate 	struct pcfs *fsp, *fsp1;
580*0Sstevel@tonic-gate 
581*0Sstevel@tonic-gate 	if (secpolicy_fs_unmount(cr, vfsp) != 0)
582*0Sstevel@tonic-gate 		return (EPERM);
583*0Sstevel@tonic-gate 
584*0Sstevel@tonic-gate 	/*
585*0Sstevel@tonic-gate 	 * forced unmount is not supported by this file system
586*0Sstevel@tonic-gate 	 * and thus, ENOTSUP, is being returned.
587*0Sstevel@tonic-gate 	 */
588*0Sstevel@tonic-gate 	if (flag & MS_FORCE)
589*0Sstevel@tonic-gate 		return (ENOTSUP);
590*0Sstevel@tonic-gate 
591*0Sstevel@tonic-gate 	PC_DPRINTF0(4, "pcfs_unmount\n");
592*0Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
593*0Sstevel@tonic-gate 	/*
594*0Sstevel@tonic-gate 	 * We don't have to lock fsp because the VVFSLOCK in vfs layer will
595*0Sstevel@tonic-gate 	 * prevent lookuppn from crossing the mount point.
596*0Sstevel@tonic-gate 	 */
597*0Sstevel@tonic-gate 	if (fsp->pcfs_nrefs) {
598*0Sstevel@tonic-gate 		return (EBUSY);
599*0Sstevel@tonic-gate 	}
600*0Sstevel@tonic-gate 
601*0Sstevel@tonic-gate 	/*
602*0Sstevel@tonic-gate 	 * Allow an unmount (regardless of state) if the fs instance has
603*0Sstevel@tonic-gate 	 * been marked as beyond recovery.
604*0Sstevel@tonic-gate 	 */
605*0Sstevel@tonic-gate 	if (fsp->pcfs_flags & PCFS_IRRECOV) {
606*0Sstevel@tonic-gate 		mutex_enter(&pcfslock);
607*0Sstevel@tonic-gate 		rw_enter(&pcnodes_lock, RW_WRITER);
608*0Sstevel@tonic-gate 		pc_diskchanged(fsp);
609*0Sstevel@tonic-gate 		rw_exit(&pcnodes_lock);
610*0Sstevel@tonic-gate 		mutex_exit(&pcfslock);
611*0Sstevel@tonic-gate 	}
612*0Sstevel@tonic-gate 
613*0Sstevel@tonic-gate 	/* now there should be no pcp node on pcfhead or pcdhead. */
614*0Sstevel@tonic-gate 
615*0Sstevel@tonic-gate 	mutex_enter(&pcfslock);
616*0Sstevel@tonic-gate 	if (fsp == pc_mounttab) {
617*0Sstevel@tonic-gate 		pc_mounttab = fsp->pcfs_nxt;
618*0Sstevel@tonic-gate 	} else {
619*0Sstevel@tonic-gate 		for (fsp1 = pc_mounttab; fsp1 != NULL; fsp1 = fsp1->pcfs_nxt)
620*0Sstevel@tonic-gate 			if (fsp1->pcfs_nxt == fsp)
621*0Sstevel@tonic-gate 				fsp1->pcfs_nxt = fsp->pcfs_nxt;
622*0Sstevel@tonic-gate 	}
623*0Sstevel@tonic-gate 
624*0Sstevel@tonic-gate 	if (fsp->pcfs_fatp != (uchar_t *)0) {
625*0Sstevel@tonic-gate 		pc_invalfat(fsp);
626*0Sstevel@tonic-gate 	}
627*0Sstevel@tonic-gate 	mutex_exit(&pcfslock);
628*0Sstevel@tonic-gate 
629*0Sstevel@tonic-gate 	VN_RELE(fsp->pcfs_devvp);
630*0Sstevel@tonic-gate 	mutex_destroy(&fsp->pcfs_lock);
631*0Sstevel@tonic-gate 	kmem_free(fsp, (uint_t)sizeof (struct pcfs));
632*0Sstevel@tonic-gate 	return (0);
633*0Sstevel@tonic-gate }
634*0Sstevel@tonic-gate 
635*0Sstevel@tonic-gate /*
636*0Sstevel@tonic-gate  * find root of pcfs
637*0Sstevel@tonic-gate  */
638*0Sstevel@tonic-gate static int
639*0Sstevel@tonic-gate pcfs_root(
640*0Sstevel@tonic-gate 	struct vfs *vfsp,
641*0Sstevel@tonic-gate 	struct vnode **vpp)
642*0Sstevel@tonic-gate {
643*0Sstevel@tonic-gate 	struct pcfs *fsp;
644*0Sstevel@tonic-gate 	struct pcnode *pcp;
645*0Sstevel@tonic-gate 	int error;
646*0Sstevel@tonic-gate 
647*0Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
648*0Sstevel@tonic-gate 	if (error = pc_lockfs(fsp, 0, 0))
649*0Sstevel@tonic-gate 		return (error);
650*0Sstevel@tonic-gate 	pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
651*0Sstevel@tonic-gate 	PC_DPRINTF2(9, "pcfs_root(0x%p) pcp= 0x%p\n",
652*0Sstevel@tonic-gate 	    (void *)vfsp, (void *)pcp);
653*0Sstevel@tonic-gate 	pc_unlockfs(fsp);
654*0Sstevel@tonic-gate 	*vpp = PCTOV(pcp);
655*0Sstevel@tonic-gate 	pcp->pc_flags |= PC_EXTERNAL;
656*0Sstevel@tonic-gate 	return (0);
657*0Sstevel@tonic-gate }
658*0Sstevel@tonic-gate 
659*0Sstevel@tonic-gate /*
660*0Sstevel@tonic-gate  * Get file system statistics.
661*0Sstevel@tonic-gate  */
662*0Sstevel@tonic-gate static int
663*0Sstevel@tonic-gate pcfs_statvfs(
664*0Sstevel@tonic-gate 	struct vfs *vfsp,
665*0Sstevel@tonic-gate 	struct statvfs64 *sp)
666*0Sstevel@tonic-gate {
667*0Sstevel@tonic-gate 	struct pcfs *fsp;
668*0Sstevel@tonic-gate 	int error;
669*0Sstevel@tonic-gate 	dev32_t d32;
670*0Sstevel@tonic-gate 
671*0Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
672*0Sstevel@tonic-gate 	error = pc_getfat(fsp);
673*0Sstevel@tonic-gate 	if (error)
674*0Sstevel@tonic-gate 		return (error);
675*0Sstevel@tonic-gate 	bzero(sp, sizeof (*sp));
676*0Sstevel@tonic-gate 	sp->f_bsize = sp->f_frsize = fsp->pcfs_clsize;
677*0Sstevel@tonic-gate 	sp->f_blocks = (fsblkcnt64_t)fsp->pcfs_ncluster;
678*0Sstevel@tonic-gate 	sp->f_bavail = sp->f_bfree = (fsblkcnt64_t)pc_freeclusters(fsp);
679*0Sstevel@tonic-gate 	sp->f_files = (fsfilcnt64_t)-1;
680*0Sstevel@tonic-gate 	sp->f_ffree = (fsfilcnt64_t)-1;
681*0Sstevel@tonic-gate 	sp->f_favail = (fsfilcnt64_t)-1;
682*0Sstevel@tonic-gate #ifdef notdef
683*0Sstevel@tonic-gate 	(void) cmpldev(&d32, fsp->pcfs_devvp->v_rdev);
684*0Sstevel@tonic-gate #endif /* notdef */
685*0Sstevel@tonic-gate 	(void) cmpldev(&d32, vfsp->vfs_dev);
686*0Sstevel@tonic-gate 	sp->f_fsid = d32;
687*0Sstevel@tonic-gate 	(void) strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name);
688*0Sstevel@tonic-gate 	sp->f_flag = vf_to_stf(vfsp->vfs_flag);
689*0Sstevel@tonic-gate 	sp->f_namemax = PCFNAMESIZE;
690*0Sstevel@tonic-gate 	return (0);
691*0Sstevel@tonic-gate }
692*0Sstevel@tonic-gate 
693*0Sstevel@tonic-gate static int
694*0Sstevel@tonic-gate pc_syncfsnodes(struct pcfs *fsp)
695*0Sstevel@tonic-gate {
696*0Sstevel@tonic-gate 	struct pchead *hp;
697*0Sstevel@tonic-gate 	struct pcnode *pcp;
698*0Sstevel@tonic-gate 	int error;
699*0Sstevel@tonic-gate 
700*0Sstevel@tonic-gate 	PC_DPRINTF0(7, "pcfs_syncfsnodes\n");
701*0Sstevel@tonic-gate 	if (error = pc_lockfs(fsp, 0, 0))
702*0Sstevel@tonic-gate 		return (error);
703*0Sstevel@tonic-gate 
704*0Sstevel@tonic-gate 	if (!(error = pc_syncfat(fsp))) {
705*0Sstevel@tonic-gate 		hp = pcfhead;
706*0Sstevel@tonic-gate 		while (hp < & pcfhead [ NPCHASH ]) {
707*0Sstevel@tonic-gate 			rw_enter(&pcnodes_lock, RW_READER);
708*0Sstevel@tonic-gate 			pcp = hp->pch_forw;
709*0Sstevel@tonic-gate 			while (pcp != (struct pcnode *)hp) {
710*0Sstevel@tonic-gate 				if (VFSTOPCFS(PCTOV(pcp) -> v_vfsp) == fsp)
711*0Sstevel@tonic-gate 					if (error = pc_nodesync(pcp))
712*0Sstevel@tonic-gate 						break;
713*0Sstevel@tonic-gate 				pcp = pcp -> pc_forw;
714*0Sstevel@tonic-gate 			}
715*0Sstevel@tonic-gate 			rw_exit(&pcnodes_lock);
716*0Sstevel@tonic-gate 			if (error)
717*0Sstevel@tonic-gate 				break;
718*0Sstevel@tonic-gate 			hp++;
719*0Sstevel@tonic-gate 		}
720*0Sstevel@tonic-gate 	}
721*0Sstevel@tonic-gate 	pc_unlockfs(fsp);
722*0Sstevel@tonic-gate 	return (error);
723*0Sstevel@tonic-gate }
724*0Sstevel@tonic-gate 
725*0Sstevel@tonic-gate /*
726*0Sstevel@tonic-gate  * Flush any pending I/O.
727*0Sstevel@tonic-gate  */
728*0Sstevel@tonic-gate /*ARGSUSED*/
729*0Sstevel@tonic-gate static int
730*0Sstevel@tonic-gate pcfs_sync(
731*0Sstevel@tonic-gate 	struct vfs *vfsp,
732*0Sstevel@tonic-gate 	short flag,
733*0Sstevel@tonic-gate 	struct cred *cr)
734*0Sstevel@tonic-gate {
735*0Sstevel@tonic-gate 	struct pcfs *fsp;
736*0Sstevel@tonic-gate 	int error = 0;
737*0Sstevel@tonic-gate 
738*0Sstevel@tonic-gate 	/* this prevents the filesystem from being umounted. */
739*0Sstevel@tonic-gate 	mutex_enter(&pcfslock);
740*0Sstevel@tonic-gate 	if (vfsp != NULL) {
741*0Sstevel@tonic-gate 		fsp = VFSTOPCFS(vfsp);
742*0Sstevel@tonic-gate 		if (!(fsp->pcfs_flags & PCFS_IRRECOV)) {
743*0Sstevel@tonic-gate 			error = pc_syncfsnodes(fsp);
744*0Sstevel@tonic-gate 		} else {
745*0Sstevel@tonic-gate 			rw_enter(&pcnodes_lock, RW_WRITER);
746*0Sstevel@tonic-gate 			pc_diskchanged(fsp);
747*0Sstevel@tonic-gate 			rw_exit(&pcnodes_lock);
748*0Sstevel@tonic-gate 			error = EIO;
749*0Sstevel@tonic-gate 		}
750*0Sstevel@tonic-gate 	} else {
751*0Sstevel@tonic-gate 		fsp = pc_mounttab;
752*0Sstevel@tonic-gate 		while (fsp != NULL) {
753*0Sstevel@tonic-gate 			if (fsp->pcfs_flags & PCFS_IRRECOV) {
754*0Sstevel@tonic-gate 				rw_enter(&pcnodes_lock, RW_WRITER);
755*0Sstevel@tonic-gate 				pc_diskchanged(fsp);
756*0Sstevel@tonic-gate 				rw_exit(&pcnodes_lock);
757*0Sstevel@tonic-gate 				error = EIO;
758*0Sstevel@tonic-gate 				break;
759*0Sstevel@tonic-gate 			}
760*0Sstevel@tonic-gate 			error = pc_syncfsnodes(fsp);
761*0Sstevel@tonic-gate 			if (error) break;
762*0Sstevel@tonic-gate 			fsp = fsp->pcfs_nxt;
763*0Sstevel@tonic-gate 		}
764*0Sstevel@tonic-gate 	}
765*0Sstevel@tonic-gate 	mutex_exit(&pcfslock);
766*0Sstevel@tonic-gate 	return (error);
767*0Sstevel@tonic-gate }
768*0Sstevel@tonic-gate 
769*0Sstevel@tonic-gate int
770*0Sstevel@tonic-gate pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing)
771*0Sstevel@tonic-gate {
772*0Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_IRRECOV) && !releasing)
773*0Sstevel@tonic-gate 		return (EIO);
774*0Sstevel@tonic-gate 
775*0Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_LOCKED) && (fsp->pcfs_owner == curthread)) {
776*0Sstevel@tonic-gate 		fsp->pcfs_count++;
777*0Sstevel@tonic-gate 	} else {
778*0Sstevel@tonic-gate 		mutex_enter(&fsp->pcfs_lock);
779*0Sstevel@tonic-gate 		if (fsp->pcfs_flags & PCFS_LOCKED)
780*0Sstevel@tonic-gate 			panic("pc_lockfs");
781*0Sstevel@tonic-gate 		/*
782*0Sstevel@tonic-gate 		 * We check the IRRECOV bit again just in case somebody
783*0Sstevel@tonic-gate 		 * snuck past the initial check but then got held up before
784*0Sstevel@tonic-gate 		 * they could grab the lock.  (And in the meantime someone
785*0Sstevel@tonic-gate 		 * had grabbed the lock and set the bit)
786*0Sstevel@tonic-gate 		 */
787*0Sstevel@tonic-gate 		if (!diskchanged && !(fsp->pcfs_flags & PCFS_IRRECOV))
788*0Sstevel@tonic-gate 			(void) pc_getfat(fsp);
789*0Sstevel@tonic-gate 		fsp->pcfs_flags |= PCFS_LOCKED;
790*0Sstevel@tonic-gate 		fsp->pcfs_owner = curthread;
791*0Sstevel@tonic-gate 		fsp->pcfs_count++;
792*0Sstevel@tonic-gate 	}
793*0Sstevel@tonic-gate 	return (0);
794*0Sstevel@tonic-gate }
795*0Sstevel@tonic-gate 
796*0Sstevel@tonic-gate void
797*0Sstevel@tonic-gate pc_unlockfs(struct pcfs *fsp)
798*0Sstevel@tonic-gate {
799*0Sstevel@tonic-gate 
800*0Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_LOCKED) == 0)
801*0Sstevel@tonic-gate 		panic("pc_unlockfs");
802*0Sstevel@tonic-gate 	if (--fsp->pcfs_count < 0)
803*0Sstevel@tonic-gate 		panic("pc_unlockfs: count");
804*0Sstevel@tonic-gate 	if (fsp->pcfs_count == 0) {
805*0Sstevel@tonic-gate 		fsp->pcfs_flags &= ~PCFS_LOCKED;
806*0Sstevel@tonic-gate 		fsp->pcfs_owner = 0;
807*0Sstevel@tonic-gate 		mutex_exit(&fsp->pcfs_lock);
808*0Sstevel@tonic-gate 	}
809*0Sstevel@tonic-gate }
810*0Sstevel@tonic-gate 
811*0Sstevel@tonic-gate /*
812*0Sstevel@tonic-gate  * isDosDrive()
813*0Sstevel@tonic-gate  *	Boolean function.  Give it the systid field for an fdisk partition
814*0Sstevel@tonic-gate  *	and it decides if that's a systid that describes a DOS drive.  We
815*0Sstevel@tonic-gate  *	use systid values defined in sys/dktp/fdisk.h.
816*0Sstevel@tonic-gate  */
817*0Sstevel@tonic-gate static int
818*0Sstevel@tonic-gate isDosDrive(uchar_t checkMe)
819*0Sstevel@tonic-gate {
820*0Sstevel@tonic-gate 	return ((checkMe == DOSOS12) || (checkMe == DOSOS16) ||
821*0Sstevel@tonic-gate 	    (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) ||
822*0Sstevel@tonic-gate 	    (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) ||
823*0Sstevel@tonic-gate 	    (checkMe == DIAGPART));
824*0Sstevel@tonic-gate }
825*0Sstevel@tonic-gate 
826*0Sstevel@tonic-gate /*
827*0Sstevel@tonic-gate  * isDosExtended()
828*0Sstevel@tonic-gate  *	Boolean function.  Give it the systid field for an fdisk partition
829*0Sstevel@tonic-gate  *	and it decides if that's a systid that describes an extended DOS
830*0Sstevel@tonic-gate  *	partition.
831*0Sstevel@tonic-gate  */
832*0Sstevel@tonic-gate static int
833*0Sstevel@tonic-gate isDosExtended(uchar_t checkMe)
834*0Sstevel@tonic-gate {
835*0Sstevel@tonic-gate 	return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA));
836*0Sstevel@tonic-gate }
837*0Sstevel@tonic-gate 
838*0Sstevel@tonic-gate /*
839*0Sstevel@tonic-gate  * isBootPart()
840*0Sstevel@tonic-gate  *	Boolean function.  Give it the systid field for an fdisk partition
841*0Sstevel@tonic-gate  *	and it decides if that's a systid that describes a Solaris boot
842*0Sstevel@tonic-gate  *	partition.
843*0Sstevel@tonic-gate  */
844*0Sstevel@tonic-gate static int
845*0Sstevel@tonic-gate isBootPart(uchar_t checkMe)
846*0Sstevel@tonic-gate {
847*0Sstevel@tonic-gate 	return (checkMe == X86BOOT);
848*0Sstevel@tonic-gate }
849*0Sstevel@tonic-gate 
850*0Sstevel@tonic-gate /*
851*0Sstevel@tonic-gate  * noLogicalDrive()
852*0Sstevel@tonic-gate  *	Display error message about not being able to find a logical
853*0Sstevel@tonic-gate  *	drive.
854*0Sstevel@tonic-gate  */
855*0Sstevel@tonic-gate static void
856*0Sstevel@tonic-gate noLogicalDrive(int requested)
857*0Sstevel@tonic-gate {
858*0Sstevel@tonic-gate 	if (requested == BOOT_PARTITION_DRIVE) {
859*0Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: no boot partition");
860*0Sstevel@tonic-gate 	} else {
861*0Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: no such logical drive");
862*0Sstevel@tonic-gate 	}
863*0Sstevel@tonic-gate }
864*0Sstevel@tonic-gate 
865*0Sstevel@tonic-gate /*
866*0Sstevel@tonic-gate  * findTheDrive()
867*0Sstevel@tonic-gate  *	Discover offset of the requested logical drive, and return
868*0Sstevel@tonic-gate  *	that offset (startSector), the systid of that drive (sysid),
869*0Sstevel@tonic-gate  *	and a buffer pointer (bp), with the buffer contents being
870*0Sstevel@tonic-gate  *	the first sector of the logical drive (i.e., the sector that
871*0Sstevel@tonic-gate  *	contains the BPB for that drive).
872*0Sstevel@tonic-gate  */
873*0Sstevel@tonic-gate static int
874*0Sstevel@tonic-gate findTheDrive(dev_t dev, int askedFor, int *error, buf_t **bp,
875*0Sstevel@tonic-gate     daddr_t *startSector, uchar_t *sysid)
876*0Sstevel@tonic-gate {
877*0Sstevel@tonic-gate 	struct ipart dosp[FD_NUMPART];	/* incore fdisk partition structure */
878*0Sstevel@tonic-gate 	struct mboot *dosp_ptr;		/* boot structure pointer */
879*0Sstevel@tonic-gate 	daddr_t lastseek = 0;		/* Disk block we sought previously */
880*0Sstevel@tonic-gate 	daddr_t diskblk = 0;		/* Disk block to get */
881*0Sstevel@tonic-gate 	daddr_t xstartsect;		/* base of Extended DOS partition */
882*0Sstevel@tonic-gate 	int logicalDriveCount = 0;	/* Count of logical drives seen */
883*0Sstevel@tonic-gate 	int extendedPart = -1;		/* index of extended dos partition */
884*0Sstevel@tonic-gate 	int primaryPart = -1;		/* index of primary dos partition */
885*0Sstevel@tonic-gate 	int bootPart = -1;		/* index of a Solaris boot partition */
886*0Sstevel@tonic-gate 	int xnumsect = -1;		/* length of extended DOS partition */
887*0Sstevel@tonic-gate 	int driveIndex;			/* computed FDISK table index */
888*0Sstevel@tonic-gate 	int i;
889*0Sstevel@tonic-gate 	/*
890*0Sstevel@tonic-gate 	 * Count of drives in the current extended partition's
891*0Sstevel@tonic-gate 	 * FDISK table, and indexes of the drives themselves.
892*0Sstevel@tonic-gate 	 */
893*0Sstevel@tonic-gate 	int extndDrives[FD_NUMPART];
894*0Sstevel@tonic-gate 	int numDrives = 0;
895*0Sstevel@tonic-gate 
896*0Sstevel@tonic-gate 	/*
897*0Sstevel@tonic-gate 	 * Count of drives (beyond primary) in master boot record's
898*0Sstevel@tonic-gate 	 * FDISK table, and indexes of the drives themselves.
899*0Sstevel@tonic-gate 	 */
900*0Sstevel@tonic-gate 	int extraDrives[FD_NUMPART];
901*0Sstevel@tonic-gate 	int numExtraDrives = 0;
902*0Sstevel@tonic-gate 
903*0Sstevel@tonic-gate 	/*
904*0Sstevel@tonic-gate 	 *  Copy from disk block into memory aligned structure for fdisk usage.
905*0Sstevel@tonic-gate 	 */
906*0Sstevel@tonic-gate 	dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr;
907*0Sstevel@tonic-gate 	bcopy(dosp_ptr->parts, dosp, sizeof (struct ipart) * FD_NUMPART);
908*0Sstevel@tonic-gate 
909*0Sstevel@tonic-gate 	/*
910*0Sstevel@tonic-gate 	 * Get a summary of what is in the Master FDISK table.
911*0Sstevel@tonic-gate 	 * Normally we expect to find one partition marked as a DOS drive.
912*0Sstevel@tonic-gate 	 * This partition is the one Windows calls the primary dos partition.
913*0Sstevel@tonic-gate 	 * If the machine has any logical drives then we also expect
914*0Sstevel@tonic-gate 	 * to find a partition marked as an extended DOS partition.
915*0Sstevel@tonic-gate 	 *
916*0Sstevel@tonic-gate 	 * Sometimes we'll find multiple partitions marked as DOS drives.
917*0Sstevel@tonic-gate 	 * The Solaris fdisk program allows these partitions
918*0Sstevel@tonic-gate 	 * to be created, but Windows fdisk no longer does.  We still need
919*0Sstevel@tonic-gate 	 * to support these, though, since Windows does.  We also need to fix
920*0Sstevel@tonic-gate 	 * our fdisk to behave like the Windows version.
921*0Sstevel@tonic-gate 	 *
922*0Sstevel@tonic-gate 	 * It turns out that some off-the-shelf media have *only* an
923*0Sstevel@tonic-gate 	 * Extended partition, so we need to deal with that case as well.
924*0Sstevel@tonic-gate 	 *
925*0Sstevel@tonic-gate 	 * Only a single (the first) Extended or Boot Partition will
926*0Sstevel@tonic-gate 	 * be recognized.  Any others will be ignored.
927*0Sstevel@tonic-gate 	 */
928*0Sstevel@tonic-gate 	for (i = 0; i < FD_NUMPART; i++) {
929*0Sstevel@tonic-gate 		if (isDosDrive(dosp[i].systid)) {
930*0Sstevel@tonic-gate 			if (primaryPart < 0) {
931*0Sstevel@tonic-gate 				logicalDriveCount++;
932*0Sstevel@tonic-gate 				primaryPart = i;
933*0Sstevel@tonic-gate 			} else {
934*0Sstevel@tonic-gate 				extraDrives[numExtraDrives++] = i;
935*0Sstevel@tonic-gate 			}
936*0Sstevel@tonic-gate 			continue;
937*0Sstevel@tonic-gate 		}
938*0Sstevel@tonic-gate 		if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) {
939*0Sstevel@tonic-gate 			extendedPart = i;
940*0Sstevel@tonic-gate 			continue;
941*0Sstevel@tonic-gate 		}
942*0Sstevel@tonic-gate 		if ((bootPart < 0) && isBootPart(dosp[i].systid)) {
943*0Sstevel@tonic-gate 			bootPart = i;
944*0Sstevel@tonic-gate 			continue;
945*0Sstevel@tonic-gate 		}
946*0Sstevel@tonic-gate 	}
947*0Sstevel@tonic-gate 
948*0Sstevel@tonic-gate 	if (askedFor == BOOT_PARTITION_DRIVE) {
949*0Sstevel@tonic-gate 		if (bootPart < 0) {
950*0Sstevel@tonic-gate 			noLogicalDrive(askedFor);
951*0Sstevel@tonic-gate 			*error = EINVAL;
952*0Sstevel@tonic-gate 			return (0);
953*0Sstevel@tonic-gate 		}
954*0Sstevel@tonic-gate 		*sysid = dosp[bootPart].systid;
955*0Sstevel@tonic-gate 		*startSector = ltohi(dosp[bootPart].relsect);
956*0Sstevel@tonic-gate 		return (1);
957*0Sstevel@tonic-gate 	}
958*0Sstevel@tonic-gate 
959*0Sstevel@tonic-gate 	if (askedFor == PRIMARY_DOS_DRIVE && primaryPart >= 0) {
960*0Sstevel@tonic-gate 		*sysid = dosp[primaryPart].systid;
961*0Sstevel@tonic-gate 		*startSector = ltohi(dosp[primaryPart].relsect);
962*0Sstevel@tonic-gate 		return (1);
963*0Sstevel@tonic-gate 	}
964*0Sstevel@tonic-gate 
965*0Sstevel@tonic-gate 	/*
966*0Sstevel@tonic-gate 	 * We are not looking for the C: drive (or the primary drive
967*0Sstevel@tonic-gate 	 * was not found), so we had better have an extended partition
968*0Sstevel@tonic-gate 	 * or extra drives in the Master FDISK table.
969*0Sstevel@tonic-gate 	 */
970*0Sstevel@tonic-gate 	if ((extendedPart < 0) && (numExtraDrives == 0)) {
971*0Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: no extended dos partition");
972*0Sstevel@tonic-gate 		noLogicalDrive(askedFor);
973*0Sstevel@tonic-gate 		*error = EINVAL;
974*0Sstevel@tonic-gate 		return (0);
975*0Sstevel@tonic-gate 	}
976*0Sstevel@tonic-gate 
977*0Sstevel@tonic-gate 	if (extendedPart >= 0) {
978*0Sstevel@tonic-gate 		diskblk = xstartsect = ltohi(dosp[extendedPart].relsect);
979*0Sstevel@tonic-gate 		xnumsect = ltohi(dosp[extendedPart].numsect);
980*0Sstevel@tonic-gate 		do {
981*0Sstevel@tonic-gate 			/*
982*0Sstevel@tonic-gate 			 *  If the seek would not cause us to change
983*0Sstevel@tonic-gate 			 *  position on the drive, then we're out of
984*0Sstevel@tonic-gate 			 *  extended partitions to examine.
985*0Sstevel@tonic-gate 			 */
986*0Sstevel@tonic-gate 			if (diskblk == lastseek)
987*0Sstevel@tonic-gate 				break;
988*0Sstevel@tonic-gate 			logicalDriveCount += numDrives;
989*0Sstevel@tonic-gate 			/*
990*0Sstevel@tonic-gate 			 *  Seek the next extended partition, and find
991*0Sstevel@tonic-gate 			 *  logical drives within it.
992*0Sstevel@tonic-gate 			 */
993*0Sstevel@tonic-gate 			brelse(*bp);
994*0Sstevel@tonic-gate 			*bp = bread(dev, diskblk, PC_SAFESECSIZE);
995*0Sstevel@tonic-gate 			if ((*bp)->b_flags & B_ERROR) {
996*0Sstevel@tonic-gate 				PC_DPRINTF0(1, "pc_getfattype: read error\n");
997*0Sstevel@tonic-gate 				*error = EIO;
998*0Sstevel@tonic-gate 				return (0);
999*0Sstevel@tonic-gate 			}
1000*0Sstevel@tonic-gate 			lastseek = diskblk;
1001*0Sstevel@tonic-gate 			dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr;
1002*0Sstevel@tonic-gate 			if (ltohs(dosp_ptr->signature) != MBB_MAGIC) {
1003*0Sstevel@tonic-gate 				cmn_err(CE_NOTE, "!pcfs: "
1004*0Sstevel@tonic-gate 				    "extended partition signature err");
1005*0Sstevel@tonic-gate 				*error = EINVAL;
1006*0Sstevel@tonic-gate 				return (0);
1007*0Sstevel@tonic-gate 			}
1008*0Sstevel@tonic-gate 			bcopy(dosp_ptr->parts, dosp,
1009*0Sstevel@tonic-gate 			    sizeof (struct ipart) * FD_NUMPART);
1010*0Sstevel@tonic-gate 			/*
1011*0Sstevel@tonic-gate 			 *  Count up drives, and track where the next
1012*0Sstevel@tonic-gate 			 *  extended partition is in case we need it.  We
1013*0Sstevel@tonic-gate 			 *  are expecting only one extended partition.  If
1014*0Sstevel@tonic-gate 			 *  there is more than one we'll only go to the
1015*0Sstevel@tonic-gate 			 *  first one we see, but warn about ignoring.
1016*0Sstevel@tonic-gate 			 */
1017*0Sstevel@tonic-gate 			numDrives = 0;
1018*0Sstevel@tonic-gate 			for (i = 0; i < FD_NUMPART; i++) {
1019*0Sstevel@tonic-gate 				if (isDosDrive(dosp[i].systid)) {
1020*0Sstevel@tonic-gate 					extndDrives[numDrives++] = i;
1021*0Sstevel@tonic-gate 					continue;
1022*0Sstevel@tonic-gate 				} else if (isDosExtended(dosp[i].systid)) {
1023*0Sstevel@tonic-gate 					if (diskblk != lastseek) {
1024*0Sstevel@tonic-gate 						/*
1025*0Sstevel@tonic-gate 						 * Already found an extended
1026*0Sstevel@tonic-gate 						 * partition in this table.
1027*0Sstevel@tonic-gate 						 */
1028*0Sstevel@tonic-gate 						cmn_err(CE_NOTE,
1029*0Sstevel@tonic-gate 						    "!pcfs: ignoring unexpected"
1030*0Sstevel@tonic-gate 						    " additional extended"
1031*0Sstevel@tonic-gate 						    " partition");
1032*0Sstevel@tonic-gate 						continue;
1033*0Sstevel@tonic-gate 					}
1034*0Sstevel@tonic-gate 					diskblk = xstartsect +
1035*0Sstevel@tonic-gate 					    ltohi(dosp[i].relsect);
1036*0Sstevel@tonic-gate 					continue;
1037*0Sstevel@tonic-gate 				}
1038*0Sstevel@tonic-gate 			}
1039*0Sstevel@tonic-gate 		} while (askedFor > logicalDriveCount + numDrives);
1040*0Sstevel@tonic-gate 
1041*0Sstevel@tonic-gate 		if (askedFor <= logicalDriveCount + numDrives) {
1042*0Sstevel@tonic-gate 			/*
1043*0Sstevel@tonic-gate 			 * The number of logical drives we've found thus
1044*0Sstevel@tonic-gate 			 * far is enough to get us to the one we were
1045*0Sstevel@tonic-gate 			 * searching for.
1046*0Sstevel@tonic-gate 			 */
1047*0Sstevel@tonic-gate 			driveIndex = logicalDriveCount + numDrives - askedFor;
1048*0Sstevel@tonic-gate 			*sysid = dosp[extndDrives[driveIndex]].systid;
1049*0Sstevel@tonic-gate 			*startSector =
1050*0Sstevel@tonic-gate 			    ltohi(dosp[extndDrives[driveIndex]].relsect) +
1051*0Sstevel@tonic-gate 			    lastseek;
1052*0Sstevel@tonic-gate 			if (*startSector > (xstartsect + xnumsect)) {
1053*0Sstevel@tonic-gate 				cmn_err(CE_NOTE, "!pcfs: extended partition "
1054*0Sstevel@tonic-gate 				    "values bad");
1055*0Sstevel@tonic-gate 				*error = EINVAL;
1056*0Sstevel@tonic-gate 				return (0);
1057*0Sstevel@tonic-gate 			}
1058*0Sstevel@tonic-gate 			return (1);
1059*0Sstevel@tonic-gate 		} else {
1060*0Sstevel@tonic-gate 			/*
1061*0Sstevel@tonic-gate 			 * We ran out of extended dos partition
1062*0Sstevel@tonic-gate 			 * drives.  The only hope now is to go
1063*0Sstevel@tonic-gate 			 * back to extra drives defined in the master
1064*0Sstevel@tonic-gate 			 * fdisk table.  But we overwrote that table
1065*0Sstevel@tonic-gate 			 * already, so we must load it in again.
1066*0Sstevel@tonic-gate 			 */
1067*0Sstevel@tonic-gate 			logicalDriveCount += numDrives;
1068*0Sstevel@tonic-gate 			brelse(*bp);
1069*0Sstevel@tonic-gate 			*bp = bread(dev, (daddr_t)0, PC_SAFESECSIZE);
1070*0Sstevel@tonic-gate 			if ((*bp)->b_flags & B_ERROR) {
1071*0Sstevel@tonic-gate 				PC_DPRINTF0(1, "pc_getfattype: read error\n");
1072*0Sstevel@tonic-gate 				*error = EIO;
1073*0Sstevel@tonic-gate 				return (0);
1074*0Sstevel@tonic-gate 			}
1075*0Sstevel@tonic-gate 			dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr;
1076*0Sstevel@tonic-gate 			bcopy(dosp_ptr->parts, dosp,
1077*0Sstevel@tonic-gate 			    sizeof (struct ipart) * FD_NUMPART);
1078*0Sstevel@tonic-gate 		}
1079*0Sstevel@tonic-gate 	}
1080*0Sstevel@tonic-gate 	/*
1081*0Sstevel@tonic-gate 	 *  Still haven't found the drive, is it an extra
1082*0Sstevel@tonic-gate 	 *  drive defined in the main FDISK table?
1083*0Sstevel@tonic-gate 	 */
1084*0Sstevel@tonic-gate 	if (askedFor <= logicalDriveCount + numExtraDrives) {
1085*0Sstevel@tonic-gate 		driveIndex = logicalDriveCount + numExtraDrives - askedFor;
1086*0Sstevel@tonic-gate 		*sysid = dosp[extraDrives[driveIndex]].systid;
1087*0Sstevel@tonic-gate 		*startSector = ltohi(dosp[extraDrives[driveIndex]].relsect);
1088*0Sstevel@tonic-gate 		return (1);
1089*0Sstevel@tonic-gate 	}
1090*0Sstevel@tonic-gate 	/*
1091*0Sstevel@tonic-gate 	 *  Still haven't found the drive, and there is
1092*0Sstevel@tonic-gate 	 *  nowhere else to look.
1093*0Sstevel@tonic-gate 	 */
1094*0Sstevel@tonic-gate 	noLogicalDrive(askedFor);
1095*0Sstevel@tonic-gate 	*error = EINVAL;
1096*0Sstevel@tonic-gate 	return (0);
1097*0Sstevel@tonic-gate }
1098*0Sstevel@tonic-gate 
1099*0Sstevel@tonic-gate /*
1100*0Sstevel@tonic-gate  * Get the FAT type for the DOS medium.
1101*0Sstevel@tonic-gate  *
1102*0Sstevel@tonic-gate  * We look at sector 0 and determine if we are looking at an FDISK table
1103*0Sstevel@tonic-gate  * or a BIOS Parameter Block (BPB).  Most of the time, if its a floppy
1104*0Sstevel@tonic-gate  * we'll be looking at a BPB and if we're looking at a hard drive we'll be
1105*0Sstevel@tonic-gate  * examining an FDISK table.  Those fun exceptions do happen, though.
1106*0Sstevel@tonic-gate  *
1107*0Sstevel@tonic-gate  * If we are looking at a BPB, we can calculate and verify the FAT size.
1108*0Sstevel@tonic-gate  * If we are looking at an FDISK partition table, we scan the partition
1109*0Sstevel@tonic-gate  * table for the requested logical volume.
1110*0Sstevel@tonic-gate  */
1111*0Sstevel@tonic-gate static int
1112*0Sstevel@tonic-gate pc_getfattype(
1113*0Sstevel@tonic-gate 	struct vnode *devvp,
1114*0Sstevel@tonic-gate 	int ldrive,
1115*0Sstevel@tonic-gate 	daddr_t *strtsectp,
1116*0Sstevel@tonic-gate 	int *fattypep)
1117*0Sstevel@tonic-gate {
1118*0Sstevel@tonic-gate 	struct mboot *dosp_ptr;		/* boot structure pointer */
1119*0Sstevel@tonic-gate 	struct bootsec *bootp, *bpbp;	/* for detailed sector examination */
1120*0Sstevel@tonic-gate 	uchar_t *cp;			/* for searching out FAT string */
1121*0Sstevel@tonic-gate 	uint_t overhead;		/* sectors not part of file area */
1122*0Sstevel@tonic-gate 	uint_t numclusters;		/* number of clusters in file area */
1123*0Sstevel@tonic-gate 	uint_t bytesoffat;		/* computed number of bytes in a FAT */
1124*0Sstevel@tonic-gate 	int secsize;			/* Sector size in bytes/sec */
1125*0Sstevel@tonic-gate 	buf_t *bp = NULL;		/* Disk buffer pointer */
1126*0Sstevel@tonic-gate 	int rval = 0;
1127*0Sstevel@tonic-gate 	uchar_t sysid = 0;		/* System ID character */
1128*0Sstevel@tonic-gate 	dev_t	dev = devvp->v_rdev;
1129*0Sstevel@tonic-gate 
1130*0Sstevel@tonic-gate 	*strtsectp = (daddr_t)0;
1131*0Sstevel@tonic-gate 
1132*0Sstevel@tonic-gate 	/*
1133*0Sstevel@tonic-gate 	 * Open the device so we can check out the BPB or FDISK table,
1134*0Sstevel@tonic-gate 	 * then read in the sector.
1135*0Sstevel@tonic-gate 	 */
1136*0Sstevel@tonic-gate 	PC_DPRINTF2(5, "pc_getfattype: dev=%x  ldrive=%x  ", (int)dev, ldrive);
1137*0Sstevel@tonic-gate 	if (rval = VOP_OPEN(&devvp, FREAD, CRED())) {
1138*0Sstevel@tonic-gate 		PC_DPRINTF1(1, "pc_getfattype: open error=%d\n", rval);
1139*0Sstevel@tonic-gate 		return (rval);
1140*0Sstevel@tonic-gate 	}
1141*0Sstevel@tonic-gate 
1142*0Sstevel@tonic-gate 	/*
1143*0Sstevel@tonic-gate 	 *  Read block 0 from device
1144*0Sstevel@tonic-gate 	 */
1145*0Sstevel@tonic-gate 	bp = bread(dev, (daddr_t)0, PC_SAFESECSIZE);
1146*0Sstevel@tonic-gate 	if (bp->b_flags & B_ERROR) {
1147*0Sstevel@tonic-gate 		PC_DPRINTF0(1, "pc_getfattype: read error\n");
1148*0Sstevel@tonic-gate 		rval = EIO;
1149*0Sstevel@tonic-gate 		goto out;
1150*0Sstevel@tonic-gate 	}
1151*0Sstevel@tonic-gate 
1152*0Sstevel@tonic-gate 	/*
1153*0Sstevel@tonic-gate 	 * If this is a logical volume with a FAT file system,
1154*0Sstevel@tonic-gate 	 * then we expect to find a "file system boot sector"
1155*0Sstevel@tonic-gate 	 * (also called a BIOS Perameter Block -- or BPB) in
1156*0Sstevel@tonic-gate 	 * the first physical sector.
1157*0Sstevel@tonic-gate 	 */
1158*0Sstevel@tonic-gate 
1159*0Sstevel@tonic-gate 	/*
1160*0Sstevel@tonic-gate 	 * The word "FAT" is encoded into the BPB beginning at
1161*0Sstevel@tonic-gate 	 * PCFS_TYPESTRING_OFFSET16 in 12 and 16 bit FATs.  It is
1162*0Sstevel@tonic-gate 	 * encoded at FS_TYPESTRING_OFFSET32 in 32 bit FATs.
1163*0Sstevel@tonic-gate 	 */
1164*0Sstevel@tonic-gate 	cp = (uchar_t *)bp->b_un.b_addr;
1165*0Sstevel@tonic-gate 	if (*(cp + PCFS_TYPESTRING_OFFSET16) == 'F' &&
1166*0Sstevel@tonic-gate 	    *(cp + PCFS_TYPESTRING_OFFSET16 + 1) == 'A' &&
1167*0Sstevel@tonic-gate 	    *(cp + PCFS_TYPESTRING_OFFSET16 + 2) == 'T') {
1168*0Sstevel@tonic-gate 		PC_DPRINTF0(5, "Found the FAT string at 12/16 location\n");
1169*0Sstevel@tonic-gate 		/*
1170*0Sstevel@tonic-gate 		 * Compute a bits/fat value
1171*0Sstevel@tonic-gate 		 */
1172*0Sstevel@tonic-gate 		bpbp = (struct bootsec *)bp->b_un.b_addr;
1173*0Sstevel@tonic-gate 		secsize = (int)ltohs(bpbp->bps[0]);
1174*0Sstevel@tonic-gate 		/*
1175*0Sstevel@tonic-gate 		 * Check for bogus sector size -
1176*0Sstevel@tonic-gate 		 *	fat should be at least 1 sector
1177*0Sstevel@tonic-gate 		 * If anything looks weird, we have to bail and try looking
1178*0Sstevel@tonic-gate 		 * for an FDISK table instead.
1179*0Sstevel@tonic-gate 		 */
1180*0Sstevel@tonic-gate 		if (secsize < 512 || (int)ltohs(bpbp->fatsec) < 1 ||
1181*0Sstevel@tonic-gate 		    bpbp->nfat < 1 || bpbp->spcl < 1) {
1182*0Sstevel@tonic-gate 			PC_DPRINTF4(5, "One or more BPB fields bad\n"
1183*0Sstevel@tonic-gate 			    "bytes/sec = %d, sec/fat = %d, numfats = %d, "
1184*0Sstevel@tonic-gate 			    "sec/clust = %d\n", secsize,
1185*0Sstevel@tonic-gate 			    (int)ltohs(bpbp->fatsec), bpbp->nfat, bpbp->spcl);
1186*0Sstevel@tonic-gate 			goto lookforfdisk;
1187*0Sstevel@tonic-gate 		}
1188*0Sstevel@tonic-gate 
1189*0Sstevel@tonic-gate 		overhead = bpbp->nfat * ltohs(bpbp->fatsec);
1190*0Sstevel@tonic-gate 		overhead += ltohs(bpbp->res_sec[0]);
1191*0Sstevel@tonic-gate 		overhead += (ltohs(bpbp->rdirents[0]) *
1192*0Sstevel@tonic-gate 		    sizeof (struct pcdir)) / secsize;
1193*0Sstevel@tonic-gate 
1194*0Sstevel@tonic-gate 		numclusters = ((ltohs(bpbp->numsect[0]) ?
1195*0Sstevel@tonic-gate 		    ltohs(bpbp->numsect[0]) : ltohi(bpbp->totalsec)) -
1196*0Sstevel@tonic-gate 		    overhead) / bpbp->spcl;
1197*0Sstevel@tonic-gate 
1198*0Sstevel@tonic-gate 		/*
1199*0Sstevel@tonic-gate 		 * If the number of clusters looks bad, go look for an
1200*0Sstevel@tonic-gate 		 * FDISK table.
1201*0Sstevel@tonic-gate 		 */
1202*0Sstevel@tonic-gate 		if (numclusters < 1) {
1203*0Sstevel@tonic-gate 			PC_DPRINTF1(5, "num clusters is bad ( = %d )\n",
1204*0Sstevel@tonic-gate 			    numclusters);
1205*0Sstevel@tonic-gate 			goto lookforfdisk;
1206*0Sstevel@tonic-gate 		}
1207*0Sstevel@tonic-gate 
1208*0Sstevel@tonic-gate 		/*
1209*0Sstevel@tonic-gate 		 * The number of bytes of FAT determines the maximum number
1210*0Sstevel@tonic-gate 		 * of entries of a given size that FAT can contain.
1211*0Sstevel@tonic-gate 		 * The FAT can only contain (bytes of FAT)*8/12 12-bit entries
1212*0Sstevel@tonic-gate 		 * and (bytes of FAT)*8/16 16-bit entries.
1213*0Sstevel@tonic-gate 		 */
1214*0Sstevel@tonic-gate 		bytesoffat = ltohs(bpbp->fatsec) * secsize;
1215*0Sstevel@tonic-gate 		PC_DPRINTF1(5, "Computed bytes of fat = %u\n", bytesoffat);
1216*0Sstevel@tonic-gate 		if ((bytesoffat * 2 / 3) >= numclusters &&
1217*0Sstevel@tonic-gate 		    *(cp + PCFS_TYPESTRING_OFFSET16 + 4) == '2') {
1218*0Sstevel@tonic-gate 			PC_DPRINTF0(4, "pc_getfattype: 12-bit FAT\n");
1219*0Sstevel@tonic-gate 			*fattypep = 0;
1220*0Sstevel@tonic-gate 			rval = 0;
1221*0Sstevel@tonic-gate 			goto out;
1222*0Sstevel@tonic-gate 		} else if (*(cp + PCFS_TYPESTRING_OFFSET16 + 4) == '6') {
1223*0Sstevel@tonic-gate 			/*
1224*0Sstevel@tonic-gate 			 * this check can result in a false positive, where
1225*0Sstevel@tonic-gate 			 * we believe a FAT12 filesystem to be a FAT16 one
1226*0Sstevel@tonic-gate 			 * (if the type recorded in the header block lies).
1227*0Sstevel@tonic-gate 			 * we recover from being lied to, in 'pc_getfat' by
1228*0Sstevel@tonic-gate 			 * Forcing fat12 over fat16, if
1229*0Sstevel@tonic-gate 			 *    'pcfs_fatsec <= 12' and
1230*0Sstevel@tonic-gate 			 *    '(byteoffat * 2 / 3) >= numclusters'
1231*0Sstevel@tonic-gate 			 * the obvious check would have been:
1232*0Sstevel@tonic-gate 			 * 	else if ((bytesoffat / 2) >= numclusters &&
1233*0Sstevel@tonic-gate 			 * 	*(cp + PCFS_TYPESTRING_OFFSET16 + 4) == '6')
1234*0Sstevel@tonic-gate 			 */
1235*0Sstevel@tonic-gate 			PC_DPRINTF0(4, "pc_getfattype: 16-bit FAT\n");
1236*0Sstevel@tonic-gate 			*fattypep = PCFS_FAT16;
1237*0Sstevel@tonic-gate 			rval = 0;
1238*0Sstevel@tonic-gate 			goto out;
1239*0Sstevel@tonic-gate 		}
1240*0Sstevel@tonic-gate 		goto lookforfdisk;
1241*0Sstevel@tonic-gate 	} else if (*(cp + PCFS_TYPESTRING_OFFSET32) == 'F' &&
1242*0Sstevel@tonic-gate 	    *(cp + PCFS_TYPESTRING_OFFSET32 + 1) == 'A' &&
1243*0Sstevel@tonic-gate 	    *(cp + PCFS_TYPESTRING_OFFSET32 + 2) == 'T') {
1244*0Sstevel@tonic-gate 		PC_DPRINTF0(5, "Found the FAT string at 32 location\n");
1245*0Sstevel@tonic-gate 		bpbp = (struct bootsec *)bp->b_un.b_addr;
1246*0Sstevel@tonic-gate 		if ((int)ltohs(bpbp->fatsec) != 0) {
1247*0Sstevel@tonic-gate 			/*
1248*0Sstevel@tonic-gate 			 * Not a good sign, we expect it to be zero if
1249*0Sstevel@tonic-gate 			 * this is really a FAT32 BPB.  All we can do
1250*0Sstevel@tonic-gate 			 * is consider the string an anomaly, and go look
1251*0Sstevel@tonic-gate 			 * for an FDISK table.
1252*0Sstevel@tonic-gate 			 */
1253*0Sstevel@tonic-gate 			PC_DPRINTF0(5, "But secs/fat non-zero\n");
1254*0Sstevel@tonic-gate 			goto lookforfdisk;
1255*0Sstevel@tonic-gate 		}
1256*0Sstevel@tonic-gate 		PC_DPRINTF0(4, "pc_getfattype: 32-bit FAT\n");
1257*0Sstevel@tonic-gate 		*fattypep = PCFS_FAT32;
1258*0Sstevel@tonic-gate 		rval = 0;
1259*0Sstevel@tonic-gate 		goto out;
1260*0Sstevel@tonic-gate 	} else {
1261*0Sstevel@tonic-gate 		/*
1262*0Sstevel@tonic-gate 		 *  We've got some legacy cases (like the stuff fdformat
1263*0Sstevel@tonic-gate 		 *  produces!).  Basically this is pre-MSDOS4.0 FATs.
1264*0Sstevel@tonic-gate 		 *
1265*0Sstevel@tonic-gate 		 *  We'll declare a match if:
1266*0Sstevel@tonic-gate 		 *	1. Bytes/Sector and other fields seem reasonable
1267*0Sstevel@tonic-gate 		 *	2. The media byte matches a known one.
1268*0Sstevel@tonic-gate 		 *
1269*0Sstevel@tonic-gate 		 *  If the media byte indicates a floppy we'll
1270*0Sstevel@tonic-gate 		 *  assume FAT12, otherwise we'll assume FAT16.
1271*0Sstevel@tonic-gate 		 */
1272*0Sstevel@tonic-gate 		bpbp = (struct bootsec *)bp->b_un.b_addr;
1273*0Sstevel@tonic-gate 		secsize = (int)ltohs(bpbp->bps[0]);
1274*0Sstevel@tonic-gate 		if (secsize && secsize % 512 == 0 &&
1275*0Sstevel@tonic-gate 		    ltohs(bpbp->fatsec) > 0 && bpbp->nfat > 0 &&
1276*0Sstevel@tonic-gate 		    bpbp->spcl > 0 && ltohs(bpbp->res_sec[0]) >= 1 &&
1277*0Sstevel@tonic-gate 		    ltohs(bpbp->numsect[0]) > 0) {
1278*0Sstevel@tonic-gate 			switch (bpbp->mediadesriptor) {
1279*0Sstevel@tonic-gate 			case SS8SPT:
1280*0Sstevel@tonic-gate 			case DS8SPT:
1281*0Sstevel@tonic-gate 			case SS9SPT:
1282*0Sstevel@tonic-gate 			case DS9SPT:
1283*0Sstevel@tonic-gate 			case DS18SPT:
1284*0Sstevel@tonic-gate 			case DS9_15SPT:
1285*0Sstevel@tonic-gate 				*fattypep = 0;
1286*0Sstevel@tonic-gate 				rval = 0;
1287*0Sstevel@tonic-gate 				goto out;
1288*0Sstevel@tonic-gate 			case MD_FIXED:
1289*0Sstevel@tonic-gate 				*fattypep = PCFS_FAT16;
1290*0Sstevel@tonic-gate 				rval = 0;
1291*0Sstevel@tonic-gate 				goto out;
1292*0Sstevel@tonic-gate 			default:
1293*0Sstevel@tonic-gate 				goto lookforfdisk;
1294*0Sstevel@tonic-gate 			}
1295*0Sstevel@tonic-gate 		}
1296*0Sstevel@tonic-gate 	}
1297*0Sstevel@tonic-gate 
1298*0Sstevel@tonic-gate lookforfdisk:
1299*0Sstevel@tonic-gate 	/*
1300*0Sstevel@tonic-gate 	 *  If we got here then we didn't find a BPB.
1301*0Sstevel@tonic-gate 	 *  We now assume we are looking at the start of a hard drive,
1302*0Sstevel@tonic-gate 	 *  where the first sector will be a Master Boot Record (MBR).
1303*0Sstevel@tonic-gate 	 *  The MBR contains a partition table (also called an FDISK
1304*0Sstevel@tonic-gate 	 *  table) and should end with a signature word (MBB_MAGIC).
1305*0Sstevel@tonic-gate 	 *
1306*0Sstevel@tonic-gate 	 *  Check signature at end of boot block for good value.
1307*0Sstevel@tonic-gate 	 *  If not then error with invalid request.
1308*0Sstevel@tonic-gate 	 */
1309*0Sstevel@tonic-gate 	dosp_ptr = (struct mboot *)bp->b_un.b_addr;
1310*0Sstevel@tonic-gate 	if (ltohs(dosp_ptr->signature) != MBB_MAGIC) {
1311*0Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: MBR signature error");
1312*0Sstevel@tonic-gate 		rval = EINVAL;
1313*0Sstevel@tonic-gate 		goto out;
1314*0Sstevel@tonic-gate 	}
1315*0Sstevel@tonic-gate 	if (ldrive <= 0) {
1316*0Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: no logical drive specified");
1317*0Sstevel@tonic-gate 		rval = EINVAL;
1318*0Sstevel@tonic-gate 		goto out;
1319*0Sstevel@tonic-gate 	}
1320*0Sstevel@tonic-gate 
1321*0Sstevel@tonic-gate 	if (findTheDrive(dev, ldrive, &rval, &bp, strtsectp, &sysid) == 0)
1322*0Sstevel@tonic-gate 		goto out;
1323*0Sstevel@tonic-gate 
1324*0Sstevel@tonic-gate 	/*
1325*0Sstevel@tonic-gate 	 * Check the sysid value of the logical drive.
1326*0Sstevel@tonic-gate 	 * Return the correct value for the type of FAT found.
1327*0Sstevel@tonic-gate 	 * Else return a value of -1 for unknown FAT type.
1328*0Sstevel@tonic-gate 	 */
1329*0Sstevel@tonic-gate 	if ((sysid == DOS_FAT32) || (sysid == DOS_FAT32_LBA)) {
1330*0Sstevel@tonic-gate 		*fattypep = PCFS_FAT32 | PCFS_NOCHK;
1331*0Sstevel@tonic-gate 		PC_DPRINTF0(4, "pc_getfattype: 32-bit FAT\n");
1332*0Sstevel@tonic-gate 	} else if ((sysid == DOS_SYSFAT16) || (sysid == DOS_SYSHUGE) ||
1333*0Sstevel@tonic-gate 	    (sysid == DIAGPART) ||
1334*0Sstevel@tonic-gate 	    (sysid == DOS_FAT16P_LBA) || (sysid == DOS_FAT16_LBA)) {
1335*0Sstevel@tonic-gate 		*fattypep = PCFS_FAT16 | PCFS_NOCHK;
1336*0Sstevel@tonic-gate 		PC_DPRINTF0(4, "pc_getfattype: 16-bit FAT\n");
1337*0Sstevel@tonic-gate 	} else if (sysid == DOS_SYSFAT12) {
1338*0Sstevel@tonic-gate 		*fattypep = PCFS_NOCHK;
1339*0Sstevel@tonic-gate 		PC_DPRINTF0(4, "pc_getfattype: 12-bit FAT\n");
1340*0Sstevel@tonic-gate 	} else if (sysid == X86BOOT) {
1341*0Sstevel@tonic-gate 		brelse(bp);
1342*0Sstevel@tonic-gate 		bp = bread(dev, *strtsectp, PC_SAFESECSIZE);
1343*0Sstevel@tonic-gate 		if (bp->b_flags & B_ERROR) {
1344*0Sstevel@tonic-gate 			PC_DPRINTF0(1, "pc_getfattype: read error\n");
1345*0Sstevel@tonic-gate 			rval = EIO;
1346*0Sstevel@tonic-gate 			goto out;
1347*0Sstevel@tonic-gate 		}
1348*0Sstevel@tonic-gate 		bootp = (struct bootsec *)bp->b_un.b_addr;
1349*0Sstevel@tonic-gate 
1350*0Sstevel@tonic-gate 		/* get the sector size - may be more than 512 bytes */
1351*0Sstevel@tonic-gate 		secsize = (int)ltohs(bootp->bps[0]);
1352*0Sstevel@tonic-gate 		/*
1353*0Sstevel@tonic-gate 		 * Check for bogus sector size -
1354*0Sstevel@tonic-gate 		 *	fat should be at least 1 sector
1355*0Sstevel@tonic-gate 		 */
1356*0Sstevel@tonic-gate 		if (secsize < 512 || (int)ltohs(bootp->fatsec) < 1 ||
1357*0Sstevel@tonic-gate 		    bootp->nfat < 1 || bootp->spcl < 1) {
1358*0Sstevel@tonic-gate 			cmn_err(CE_NOTE, "!pcfs: FAT size error");
1359*0Sstevel@tonic-gate 			rval = EINVAL;
1360*0Sstevel@tonic-gate 			goto out;
1361*0Sstevel@tonic-gate 		}
1362*0Sstevel@tonic-gate 
1363*0Sstevel@tonic-gate 		overhead = bootp->nfat * ltohs(bootp->fatsec);
1364*0Sstevel@tonic-gate 		overhead += ltohs(bootp->res_sec[0]);
1365*0Sstevel@tonic-gate 		overhead += (ltohs(bootp->rdirents[0]) *
1366*0Sstevel@tonic-gate 		    sizeof (struct pcdir)) / secsize;
1367*0Sstevel@tonic-gate 
1368*0Sstevel@tonic-gate 		numclusters = ((ltohs(bootp->numsect[0]) ?
1369*0Sstevel@tonic-gate 		    ltohs(bootp->numsect[0]) : ltohi(bootp->totalsec)) -
1370*0Sstevel@tonic-gate 		    overhead) / bootp->spcl;
1371*0Sstevel@tonic-gate 
1372*0Sstevel@tonic-gate 		if (numclusters > DOS_F12MAXC) {
1373*0Sstevel@tonic-gate 			PC_DPRINTF0(4, "pc_getfattype: 16-bit FAT BOOTPART\n");
1374*0Sstevel@tonic-gate 			*fattypep = PCFS_FAT16 | PCFS_NOCHK | PCFS_BOOTPART;
1375*0Sstevel@tonic-gate 		} else {
1376*0Sstevel@tonic-gate 			PC_DPRINTF0(4, "pc_getfattype: 12-bit FAT BOOTPART\n");
1377*0Sstevel@tonic-gate 			*fattypep = PCFS_NOCHK | PCFS_BOOTPART;
1378*0Sstevel@tonic-gate 		}
1379*0Sstevel@tonic-gate 	} else {
1380*0Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: unknown FAT type");
1381*0Sstevel@tonic-gate 		rval = EINVAL;
1382*0Sstevel@tonic-gate 	}
1383*0Sstevel@tonic-gate 
1384*0Sstevel@tonic-gate /*
1385*0Sstevel@tonic-gate  *   Release the buffer used
1386*0Sstevel@tonic-gate  */
1387*0Sstevel@tonic-gate out:
1388*0Sstevel@tonic-gate 	if (bp != NULL)
1389*0Sstevel@tonic-gate 		brelse(bp);
1390*0Sstevel@tonic-gate 	(void) VOP_CLOSE(devvp, FREAD, 1, (offset_t)0, CRED());
1391*0Sstevel@tonic-gate 	return (rval);
1392*0Sstevel@tonic-gate }
1393*0Sstevel@tonic-gate 
1394*0Sstevel@tonic-gate 
1395*0Sstevel@tonic-gate /*
1396*0Sstevel@tonic-gate  * Get the boot parameter block and file allocation table.
1397*0Sstevel@tonic-gate  * If there is an old FAT, invalidate it.
1398*0Sstevel@tonic-gate  */
1399*0Sstevel@tonic-gate int
1400*0Sstevel@tonic-gate pc_getfat(struct pcfs *fsp)
1401*0Sstevel@tonic-gate {
1402*0Sstevel@tonic-gate 	struct vfs *vfsp = PCFSTOVFS(fsp);
1403*0Sstevel@tonic-gate 	struct buf *tp = 0;
1404*0Sstevel@tonic-gate 	struct buf *bp = 0;
1405*0Sstevel@tonic-gate 	uchar_t *fatp = NULL;
1406*0Sstevel@tonic-gate 	uchar_t *fat_changemap = NULL;
1407*0Sstevel@tonic-gate 	struct bootsec *bootp;
1408*0Sstevel@tonic-gate 	struct fat32_bootsec *f32b;
1409*0Sstevel@tonic-gate 	struct vnode *devvp;
1410*0Sstevel@tonic-gate 	int error;
1411*0Sstevel@tonic-gate 	int fatsize;
1412*0Sstevel@tonic-gate 	int fat_changemapsize;
1413*0Sstevel@tonic-gate 	int flags = 0;
1414*0Sstevel@tonic-gate 	int nfat;
1415*0Sstevel@tonic-gate 	int secsize;
1416*0Sstevel@tonic-gate 	int fatsec;
1417*0Sstevel@tonic-gate 
1418*0Sstevel@tonic-gate 	PC_DPRINTF0(5, "pc_getfat\n");
1419*0Sstevel@tonic-gate 	devvp = fsp->pcfs_devvp;
1420*0Sstevel@tonic-gate 	if (fsp->pcfs_fatp) {
1421*0Sstevel@tonic-gate 		/*
1422*0Sstevel@tonic-gate 		 * There is a FAT in core.
1423*0Sstevel@tonic-gate 		 * If there are open file pcnodes or we have modified it or
1424*0Sstevel@tonic-gate 		 * it hasn't timed out yet use the in core FAT.
1425*0Sstevel@tonic-gate 		 * Otherwise invalidate it and get a new one
1426*0Sstevel@tonic-gate 		 */
1427*0Sstevel@tonic-gate #ifdef notdef
1428*0Sstevel@tonic-gate 		if (fsp->pcfs_frefs ||
1429*0Sstevel@tonic-gate 		    (fsp->pcfs_flags & PCFS_FATMOD) ||
1430*0Sstevel@tonic-gate 		    (gethrestime_sec() < fsp->pcfs_fattime)) {
1431*0Sstevel@tonic-gate 			return (0);
1432*0Sstevel@tonic-gate 		} else {
1433*0Sstevel@tonic-gate 			mutex_enter(&pcfslock);
1434*0Sstevel@tonic-gate 			pc_invalfat(fsp);
1435*0Sstevel@tonic-gate 			mutex_exit(&pcfslock);
1436*0Sstevel@tonic-gate 		}
1437*0Sstevel@tonic-gate #endif /* notdef */
1438*0Sstevel@tonic-gate 		return (0);
1439*0Sstevel@tonic-gate 	}
1440*0Sstevel@tonic-gate 	/*
1441*0Sstevel@tonic-gate 	 * Open block device mounted on.
1442*0Sstevel@tonic-gate 	 */
1443*0Sstevel@tonic-gate 	error = VOP_OPEN(&devvp,
1444*0Sstevel@tonic-gate 	    (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
1445*0Sstevel@tonic-gate 	    CRED());
1446*0Sstevel@tonic-gate 	if (error) {
1447*0Sstevel@tonic-gate 		PC_DPRINTF1(1, "pc_getfat: open error=%d\n", error);
1448*0Sstevel@tonic-gate 		return (error);
1449*0Sstevel@tonic-gate 	}
1450*0Sstevel@tonic-gate 	/*
1451*0Sstevel@tonic-gate 	 * Get boot parameter block and check it for validity
1452*0Sstevel@tonic-gate 	 */
1453*0Sstevel@tonic-gate 	tp = bread(fsp->pcfs_xdev, fsp->pcfs_dosstart, PC_SAFESECSIZE);
1454*0Sstevel@tonic-gate 	if (tp->b_flags & (B_ERROR | B_STALE)) {
1455*0Sstevel@tonic-gate 		PC_DPRINTF0(1, "pc_getfat: boot block error\n");
1456*0Sstevel@tonic-gate 		flags = tp->b_flags & B_ERROR;
1457*0Sstevel@tonic-gate 		error = EIO;
1458*0Sstevel@tonic-gate 		goto out;
1459*0Sstevel@tonic-gate 	}
1460*0Sstevel@tonic-gate 	tp->b_flags |= B_STALE | B_AGE;
1461*0Sstevel@tonic-gate 	bootp = (struct bootsec *)tp->b_un.b_addr;
1462*0Sstevel@tonic-gate 
1463*0Sstevel@tonic-gate 	/* get the sector size - may be more than 512 bytes */
1464*0Sstevel@tonic-gate 	secsize = (int)ltohs(bootp->bps[0]);
1465*0Sstevel@tonic-gate 	/* check for bogus sector size - fat should be at least 1 sector */
1466*0Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
1467*0Sstevel@tonic-gate 		f32b = (struct fat32_bootsec *)bootp;
1468*0Sstevel@tonic-gate 		fatsec = ltohi(f32b->f_fatlength);
1469*0Sstevel@tonic-gate 	} else {
1470*0Sstevel@tonic-gate 		fatsec = ltohs(bootp->fatsec);
1471*0Sstevel@tonic-gate 	}
1472*0Sstevel@tonic-gate 	if (secsize < 512 || fatsec < 1 || bootp->nfat < 1) {
1473*0Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: FAT size error");
1474*0Sstevel@tonic-gate 		error = EINVAL;
1475*0Sstevel@tonic-gate 		goto out;
1476*0Sstevel@tonic-gate 	}
1477*0Sstevel@tonic-gate 
1478*0Sstevel@tonic-gate 	switch (bootp->mediadesriptor) {
1479*0Sstevel@tonic-gate 	default:
1480*0Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: media-descriptor error, 0x%x",
1481*0Sstevel@tonic-gate 		    bootp->mediadesriptor);
1482*0Sstevel@tonic-gate 		error = EINVAL;
1483*0Sstevel@tonic-gate 		goto out;
1484*0Sstevel@tonic-gate 
1485*0Sstevel@tonic-gate 	case MD_FIXED:
1486*0Sstevel@tonic-gate 		/*
1487*0Sstevel@tonic-gate 		 * PCMCIA psuedo floppy is type MD_FIXED,
1488*0Sstevel@tonic-gate 		 * but is accessed like a floppy
1489*0Sstevel@tonic-gate 		 */
1490*0Sstevel@tonic-gate 		if (!(fsp->pcfs_flags & PCFS_PCMCIA_NO_CIS)) {
1491*0Sstevel@tonic-gate 			fsp->pcfs_flags |= PCFS_NOCHK;
1492*0Sstevel@tonic-gate 		}
1493*0Sstevel@tonic-gate 		/* FALLTHRU */
1494*0Sstevel@tonic-gate 	case SS8SPT:
1495*0Sstevel@tonic-gate 	case DS8SPT:
1496*0Sstevel@tonic-gate 	case SS9SPT:
1497*0Sstevel@tonic-gate 	case DS9SPT:
1498*0Sstevel@tonic-gate 	case DS18SPT:
1499*0Sstevel@tonic-gate 	case DS9_15SPT:
1500*0Sstevel@tonic-gate 		fsp->pcfs_secsize = secsize;
1501*0Sstevel@tonic-gate 		fsp->pcfs_sdshift = secsize / DEV_BSIZE - 1;
1502*0Sstevel@tonic-gate 		fsp->pcfs_entps = secsize / sizeof (struct pcdir);
1503*0Sstevel@tonic-gate 		fsp->pcfs_spcl = (int)bootp->spcl;
1504*0Sstevel@tonic-gate 		fsp->pcfs_fatsec = fatsec;
1505*0Sstevel@tonic-gate 		fsp->pcfs_spt = (int)ltohs(bootp->spt);
1506*0Sstevel@tonic-gate 		fsp->pcfs_rdirsec = (int)ltohs(bootp->rdirents[0])
1507*0Sstevel@tonic-gate 		    * sizeof (struct pcdir) / secsize;
1508*0Sstevel@tonic-gate 		fsp->pcfs_clsize = fsp->pcfs_spcl * secsize;
1509*0Sstevel@tonic-gate 		fsp->pcfs_fatstart = fsp->pcfs_dosstart +
1510*0Sstevel@tonic-gate 		    (daddr_t)ltohs(bootp->res_sec[0]);
1511*0Sstevel@tonic-gate 		fsp->pcfs_rdirstart = fsp->pcfs_fatstart +
1512*0Sstevel@tonic-gate 		    (bootp->nfat * fsp->pcfs_fatsec);
1513*0Sstevel@tonic-gate 		fsp->pcfs_datastart = fsp->pcfs_rdirstart + fsp->pcfs_rdirsec;
1514*0Sstevel@tonic-gate 		if (IS_FAT32(fsp))
1515*0Sstevel@tonic-gate 			fsp->pcfs_rdirstart = ltohi(f32b->f_rootcluster);
1516*0Sstevel@tonic-gate 		fsp->pcfs_ncluster = (((int)(ltohs(bootp->numsect[0]) ?
1517*0Sstevel@tonic-gate 		    ltohs(bootp->numsect[0]) : ltohi(bootp->totalsec))) -
1518*0Sstevel@tonic-gate 		    fsp->pcfs_datastart + fsp->pcfs_dosstart) / fsp->pcfs_spcl;
1519*0Sstevel@tonic-gate 		fsp->pcfs_numfat = (int)bootp->nfat;
1520*0Sstevel@tonic-gate 		fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
1521*0Sstevel@tonic-gate 		break;
1522*0Sstevel@tonic-gate 	}
1523*0Sstevel@tonic-gate 
1524*0Sstevel@tonic-gate 	/*
1525*0Sstevel@tonic-gate 	 * Get FAT and check it for validity
1526*0Sstevel@tonic-gate 	 */
1527*0Sstevel@tonic-gate 	fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
1528*0Sstevel@tonic-gate 	fatp = kmem_alloc(fatsize, KM_SLEEP);
1529*0Sstevel@tonic-gate 	error = pc_readfat(fsp, fatp, fsp->pcfs_fatstart, fatsize);
1530*0Sstevel@tonic-gate 	if (error) {
1531*0Sstevel@tonic-gate 		flags = B_ERROR;
1532*0Sstevel@tonic-gate 		goto out;
1533*0Sstevel@tonic-gate 	}
1534*0Sstevel@tonic-gate 	fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1;
1535*0Sstevel@tonic-gate 	fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP);
1536*0Sstevel@tonic-gate 
1537*0Sstevel@tonic-gate 	if (fatp[0] != bootp->mediadesriptor ||
1538*0Sstevel@tonic-gate 	    fatp[1] != 0xFF || fatp[2] != 0xFF) {
1539*0Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: FAT signature error");
1540*0Sstevel@tonic-gate 		error = EINVAL;
1541*0Sstevel@tonic-gate 		goto out;
1542*0Sstevel@tonic-gate 	}
1543*0Sstevel@tonic-gate 	/*
1544*0Sstevel@tonic-gate 	 * Checking for fatsec and number of supported clusters, should
1545*0Sstevel@tonic-gate 	 * actually determine a FAT12/FAT media. See pc_getfattype().
1546*0Sstevel@tonic-gate 	 * fatp[3] != 0xFF is necessary for FAT validity.
1547*0Sstevel@tonic-gate 	 */
1548*0Sstevel@tonic-gate 	if (fsp->pcfs_flags & PCFS_FAT16) {
1549*0Sstevel@tonic-gate 		if ((fsp->pcfs_fatsec <= 12) &&
1550*0Sstevel@tonic-gate 		    ((fatsize * 2 / 3) >= fsp->pcfs_ncluster)) {
1551*0Sstevel@tonic-gate 			/*
1552*0Sstevel@tonic-gate 			 * We have a 12-bit FAT, rather than a 16-bit FAT.
1553*0Sstevel@tonic-gate 			 * Ignore what the fdisk table says.
1554*0Sstevel@tonic-gate 			 */
1555*0Sstevel@tonic-gate 			PC_DPRINTF0(2, "pc_getfattype: forcing 12-bit FAT\n");
1556*0Sstevel@tonic-gate 			fsp->pcfs_flags &= ~PCFS_FAT16;
1557*0Sstevel@tonic-gate 		} else if (fatp[3] != 0xFF) {
1558*0Sstevel@tonic-gate 			cmn_err(CE_NOTE, "!pcfs: FAT signature error");
1559*0Sstevel@tonic-gate 			error = EINVAL;
1560*0Sstevel@tonic-gate 			goto out;
1561*0Sstevel@tonic-gate 		}
1562*0Sstevel@tonic-gate 	}
1563*0Sstevel@tonic-gate 	/*
1564*0Sstevel@tonic-gate 	 * Sanity check our FAT is large enough for the
1565*0Sstevel@tonic-gate 	 * clusters we think we have.
1566*0Sstevel@tonic-gate 	 */
1567*0Sstevel@tonic-gate 	if ((fsp->pcfs_flags & PCFS_FAT16) &&
1568*0Sstevel@tonic-gate 	    ((fatsize / 2) < fsp->pcfs_ncluster)) {
1569*0Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pcfs: FAT too small for number of clusters");
1570*0Sstevel@tonic-gate 		error = EINVAL;
1571*0Sstevel@tonic-gate 		goto out;
1572*0Sstevel@tonic-gate 	}
1573*0Sstevel@tonic-gate 
1574*0Sstevel@tonic-gate 	/*
1575*0Sstevel@tonic-gate 	 * Get alternate FATs and check for consistency
1576*0Sstevel@tonic-gate 	 * This is an inlined version of pc_readfat().
1577*0Sstevel@tonic-gate 	 * Since we're only comparing FAT and alternate FAT,
1578*0Sstevel@tonic-gate 	 * there's no reason to let pc_readfat() copy data out
1579*0Sstevel@tonic-gate 	 * of the buf. Instead, compare in-situ, one cluster
1580*0Sstevel@tonic-gate 	 * at a time.
1581*0Sstevel@tonic-gate 	 */
1582*0Sstevel@tonic-gate 	for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) {
1583*0Sstevel@tonic-gate 		size_t startsec;
1584*0Sstevel@tonic-gate 		size_t off;
1585*0Sstevel@tonic-gate 
1586*0Sstevel@tonic-gate 		startsec = fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec;
1587*0Sstevel@tonic-gate 
1588*0Sstevel@tonic-gate 		for (off = 0; off < fatsize; off += fsp->pcfs_clsize) {
1589*0Sstevel@tonic-gate 			bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp,
1590*0Sstevel@tonic-gate 				startsec +
1591*0Sstevel@tonic-gate 				pc_cltodb(fsp, pc_lblkno(fsp, off))),
1592*0Sstevel@tonic-gate 				MIN(fsp->pcfs_clsize, fatsize - off));
1593*0Sstevel@tonic-gate 			if (bp->b_flags & (B_ERROR | B_STALE)) {
1594*0Sstevel@tonic-gate 				cmn_err(CE_NOTE,
1595*0Sstevel@tonic-gate 					"!pcfs: alternate FAT #%d read error"
1596*0Sstevel@tonic-gate 					" at byte %ld", nfat, off);
1597*0Sstevel@tonic-gate 				flags = B_ERROR;
1598*0Sstevel@tonic-gate 				error = EIO;
1599*0Sstevel@tonic-gate 				goto out;
1600*0Sstevel@tonic-gate 			}
1601*0Sstevel@tonic-gate 			bp->b_flags |= B_STALE | B_AGE;
1602*0Sstevel@tonic-gate 			if (bcmp(bp->b_un.b_addr,
1603*0Sstevel@tonic-gate 			    fatp + off,
1604*0Sstevel@tonic-gate 			    MIN(fsp->pcfs_clsize, fatsize - off))) {
1605*0Sstevel@tonic-gate 				cmn_err(CE_NOTE,
1606*0Sstevel@tonic-gate 					"!pcfs: alternate FAT #%d corrupted"
1607*0Sstevel@tonic-gate 					" at byte %ld", nfat, off);
1608*0Sstevel@tonic-gate 				flags = B_ERROR;
1609*0Sstevel@tonic-gate 			}
1610*0Sstevel@tonic-gate 			brelse(bp);
1611*0Sstevel@tonic-gate 			bp = NULL;	/* prevent double release */
1612*0Sstevel@tonic-gate 		}
1613*0Sstevel@tonic-gate 	}
1614*0Sstevel@tonic-gate 
1615*0Sstevel@tonic-gate 	fsp->pcfs_fatsize = fatsize;
1616*0Sstevel@tonic-gate 	fsp->pcfs_fatp = fatp;
1617*0Sstevel@tonic-gate 	fsp->pcfs_fat_changemapsize = fat_changemapsize;
1618*0Sstevel@tonic-gate 	fsp->pcfs_fat_changemap = fat_changemap;
1619*0Sstevel@tonic-gate 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
1620*0Sstevel@tonic-gate 	fsp->pcfs_fatjustread = 1;
1621*0Sstevel@tonic-gate 
1622*0Sstevel@tonic-gate 	brelse(tp);
1623*0Sstevel@tonic-gate 	tp = NULL;
1624*0Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
1625*0Sstevel@tonic-gate 		/* get fsinfo */
1626*0Sstevel@tonic-gate 		struct fat32_boot_fsinfo fsinfo_disk;
1627*0Sstevel@tonic-gate 
1628*0Sstevel@tonic-gate 		fsp->f32fsinfo_sector = ltohs(f32b->f_infosector);
1629*0Sstevel@tonic-gate 		tp = bread(fsp->pcfs_xdev,
1630*0Sstevel@tonic-gate 		    fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector),
1631*0Sstevel@tonic-gate 		    PC_SAFESECSIZE);
1632*0Sstevel@tonic-gate 		if (tp->b_flags & (B_ERROR | B_STALE)) {
1633*0Sstevel@tonic-gate 			cmn_err(CE_NOTE, "!pcfs: error reading fat32 fsinfo");
1634*0Sstevel@tonic-gate 			flags = tp->b_flags & B_ERROR;
1635*0Sstevel@tonic-gate 			brelse(tp);
1636*0Sstevel@tonic-gate 			tp = NULL;
1637*0Sstevel@tonic-gate 			error = EIO;
1638*0Sstevel@tonic-gate 			goto out;
1639*0Sstevel@tonic-gate 		}
1640*0Sstevel@tonic-gate 		tp->b_flags |= B_STALE | B_AGE;
1641*0Sstevel@tonic-gate 		bcopy((void *)(tp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
1642*0Sstevel@tonic-gate 		    &fsinfo_disk, sizeof (struct fat32_boot_fsinfo));
1643*0Sstevel@tonic-gate 		brelse(tp);
1644*0Sstevel@tonic-gate 		tp = NULL;
1645*0Sstevel@tonic-gate 
1646*0Sstevel@tonic-gate 		/* translated fields */
1647*0Sstevel@tonic-gate 		fsp->fsinfo_native.fs_signature =
1648*0Sstevel@tonic-gate 		    ltohi(fsinfo_disk.fs_signature);
1649*0Sstevel@tonic-gate 		fsp->fsinfo_native.fs_free_clusters =
1650*0Sstevel@tonic-gate 		    ltohi(fsinfo_disk.fs_free_clusters);
1651*0Sstevel@tonic-gate 		if (fsp->fsinfo_native.fs_signature != FAT32_FS_SIGN) {
1652*0Sstevel@tonic-gate 			cmn_err(CE_NOTE,
1653*0Sstevel@tonic-gate 			    "!pcfs: fat32 fsinfo signature mismatch.");
1654*0Sstevel@tonic-gate 			error = EINVAL;
1655*0Sstevel@tonic-gate 			goto out;
1656*0Sstevel@tonic-gate 		}
1657*0Sstevel@tonic-gate 	}
1658*0Sstevel@tonic-gate 
1659*0Sstevel@tonic-gate 	return (0);
1660*0Sstevel@tonic-gate 
1661*0Sstevel@tonic-gate out:
1662*0Sstevel@tonic-gate 	cmn_err(CE_NOTE, "!pcfs: illegal disk format");
1663*0Sstevel@tonic-gate 	if (tp)
1664*0Sstevel@tonic-gate 		brelse(tp);
1665*0Sstevel@tonic-gate 	if (bp)
1666*0Sstevel@tonic-gate 		brelse(bp);
1667*0Sstevel@tonic-gate 	if (fatp)
1668*0Sstevel@tonic-gate 		kmem_free(fatp, fatsize);
1669*0Sstevel@tonic-gate 	if (fat_changemap)
1670*0Sstevel@tonic-gate 		kmem_free(fat_changemap, fat_changemapsize);
1671*0Sstevel@tonic-gate 
1672*0Sstevel@tonic-gate 	if (flags) {
1673*0Sstevel@tonic-gate 		pc_mark_irrecov(fsp);
1674*0Sstevel@tonic-gate 	}
1675*0Sstevel@tonic-gate 	(void) VOP_CLOSE(devvp, (vfsp->vfs_flag & VFS_RDONLY) ?
1676*0Sstevel@tonic-gate 	    FREAD : FREAD|FWRITE, 1, (offset_t)0, CRED());
1677*0Sstevel@tonic-gate 	return (error);
1678*0Sstevel@tonic-gate }
1679*0Sstevel@tonic-gate 
1680*0Sstevel@tonic-gate int
1681*0Sstevel@tonic-gate pc_syncfat(struct pcfs *fsp)
1682*0Sstevel@tonic-gate {
1683*0Sstevel@tonic-gate 	struct buf *bp;
1684*0Sstevel@tonic-gate 	int nfat;
1685*0Sstevel@tonic-gate 	int	error;
1686*0Sstevel@tonic-gate 	struct fat32_boot_fsinfo fsinfo_disk;
1687*0Sstevel@tonic-gate 
1688*0Sstevel@tonic-gate 	PC_DPRINTF0(7, "pcfs_syncfat\n");
1689*0Sstevel@tonic-gate 	if ((fsp->pcfs_fatp == (uchar_t *)0) ||
1690*0Sstevel@tonic-gate 	    !(fsp->pcfs_flags & PCFS_FATMOD))
1691*0Sstevel@tonic-gate 		return (0);
1692*0Sstevel@tonic-gate 	/*
1693*0Sstevel@tonic-gate 	 * write out all copies of FATs
1694*0Sstevel@tonic-gate 	 */
1695*0Sstevel@tonic-gate 	fsp->pcfs_flags &= ~PCFS_FATMOD;
1696*0Sstevel@tonic-gate 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
1697*0Sstevel@tonic-gate 	for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) {
1698*0Sstevel@tonic-gate 		error = pc_writefat(fsp,
1699*0Sstevel@tonic-gate 		    fsp->pcfs_fatstart + nfat*fsp->pcfs_fatsec);
1700*0Sstevel@tonic-gate 		if (error) {
1701*0Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
1702*0Sstevel@tonic-gate 			return (EIO);
1703*0Sstevel@tonic-gate 		}
1704*0Sstevel@tonic-gate 	}
1705*0Sstevel@tonic-gate 	pc_clear_fatchanges(fsp);
1706*0Sstevel@tonic-gate 	PC_DPRINTF0(6, "pcfs_syncfat: wrote out FAT\n");
1707*0Sstevel@tonic-gate 	/* write out fsinfo */
1708*0Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
1709*0Sstevel@tonic-gate 		bp = bread(fsp->pcfs_xdev,
1710*0Sstevel@tonic-gate 		    fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector),
1711*0Sstevel@tonic-gate 		    PC_SAFESECSIZE);
1712*0Sstevel@tonic-gate 		if (bp->b_flags & (B_ERROR | B_STALE)) {
1713*0Sstevel@tonic-gate 			brelse(bp);
1714*0Sstevel@tonic-gate 			return (EIO);
1715*0Sstevel@tonic-gate 		}
1716*0Sstevel@tonic-gate 		bcopy((void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
1717*0Sstevel@tonic-gate 		    &fsinfo_disk, sizeof (struct fat32_boot_fsinfo));
1718*0Sstevel@tonic-gate 		/* translate fields */
1719*0Sstevel@tonic-gate 		fsinfo_disk.fs_free_clusters =
1720*0Sstevel@tonic-gate 		    htoli(fsp->fsinfo_native.fs_free_clusters);
1721*0Sstevel@tonic-gate 		fsinfo_disk.fs_next_cluster = (uint32_t)FSINFO_UNKNOWN;
1722*0Sstevel@tonic-gate 		bcopy(&fsinfo_disk,
1723*0Sstevel@tonic-gate 		    (void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
1724*0Sstevel@tonic-gate 		    sizeof (struct fat32_boot_fsinfo));
1725*0Sstevel@tonic-gate 		bwrite2(bp);
1726*0Sstevel@tonic-gate 		error = geterror(bp);
1727*0Sstevel@tonic-gate 		brelse(bp);
1728*0Sstevel@tonic-gate 		if (error) {
1729*0Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
1730*0Sstevel@tonic-gate 			return (EIO);
1731*0Sstevel@tonic-gate 		}
1732*0Sstevel@tonic-gate 	}
1733*0Sstevel@tonic-gate 	return (0);
1734*0Sstevel@tonic-gate }
1735*0Sstevel@tonic-gate 
1736*0Sstevel@tonic-gate void
1737*0Sstevel@tonic-gate pc_invalfat(struct pcfs *fsp)
1738*0Sstevel@tonic-gate {
1739*0Sstevel@tonic-gate 	struct pcfs *xfsp;
1740*0Sstevel@tonic-gate 	int mount_cnt = 0;
1741*0Sstevel@tonic-gate 
1742*0Sstevel@tonic-gate 	PC_DPRINTF0(7, "pc_invalfat\n");
1743*0Sstevel@tonic-gate 	if (fsp->pcfs_fatp == (uchar_t *)0)
1744*0Sstevel@tonic-gate 		panic("pc_invalfat");
1745*0Sstevel@tonic-gate 	/*
1746*0Sstevel@tonic-gate 	 * Release FAT
1747*0Sstevel@tonic-gate 	 */
1748*0Sstevel@tonic-gate 	kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsize);
1749*0Sstevel@tonic-gate 	fsp->pcfs_fatp = NULL;
1750*0Sstevel@tonic-gate 	kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize);
1751*0Sstevel@tonic-gate 	fsp->pcfs_fat_changemap = NULL;
1752*0Sstevel@tonic-gate 	/*
1753*0Sstevel@tonic-gate 	 * Invalidate all the blocks associated with the device.
1754*0Sstevel@tonic-gate 	 * Not needed if stateless.
1755*0Sstevel@tonic-gate 	 */
1756*0Sstevel@tonic-gate 	for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt)
1757*0Sstevel@tonic-gate 		if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev)
1758*0Sstevel@tonic-gate 			mount_cnt++;
1759*0Sstevel@tonic-gate 
1760*0Sstevel@tonic-gate 	if (!mount_cnt)
1761*0Sstevel@tonic-gate 		binval(fsp->pcfs_xdev);
1762*0Sstevel@tonic-gate 	/*
1763*0Sstevel@tonic-gate 	 * close mounted device
1764*0Sstevel@tonic-gate 	 */
1765*0Sstevel@tonic-gate 	(void) VOP_CLOSE(fsp->pcfs_devvp,
1766*0Sstevel@tonic-gate 	    (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
1767*0Sstevel@tonic-gate 	    1, (offset_t)0, CRED());
1768*0Sstevel@tonic-gate }
1769*0Sstevel@tonic-gate 
1770*0Sstevel@tonic-gate void
1771*0Sstevel@tonic-gate pc_badfs(struct pcfs *fsp)
1772*0Sstevel@tonic-gate {
1773*0Sstevel@tonic-gate 	cmn_err(CE_WARN, "corrupted PC file system on dev %x.%x\n",
1774*0Sstevel@tonic-gate 	    getmajor(fsp->pcfs_devvp->v_rdev),
1775*0Sstevel@tonic-gate 	    getminor(fsp->pcfs_devvp->v_rdev));
1776*0Sstevel@tonic-gate }
1777*0Sstevel@tonic-gate 
1778*0Sstevel@tonic-gate /*
1779*0Sstevel@tonic-gate  * The problem with supporting NFS on the PCFS filesystem is that there
1780*0Sstevel@tonic-gate  * is no good place to keep the generation number. The only possible
1781*0Sstevel@tonic-gate  * place is inside a directory entry. There are a few words that we
1782*0Sstevel@tonic-gate  * don't use - they store NT & OS/2 attributes, and the creation/last access
1783*0Sstevel@tonic-gate  * time of the file - but it seems wrong to use them. In addition, directory
1784*0Sstevel@tonic-gate  * entries come and go. If a directory is removed completely, its directory
1785*0Sstevel@tonic-gate  * blocks are freed and the generation numbers are lost. Whereas in ufs,
1786*0Sstevel@tonic-gate  * inode blocks are dedicated for inodes, so the generation numbers are
1787*0Sstevel@tonic-gate  * permanently kept on the disk.
1788*0Sstevel@tonic-gate  */
1789*0Sstevel@tonic-gate static int
1790*0Sstevel@tonic-gate pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp)
1791*0Sstevel@tonic-gate {
1792*0Sstevel@tonic-gate 	struct pcnode *pcp;
1793*0Sstevel@tonic-gate 	struct pc_fid *pcfid;
1794*0Sstevel@tonic-gate 	struct pcfs *fsp;
1795*0Sstevel@tonic-gate 	struct pcdir *ep;
1796*0Sstevel@tonic-gate 	daddr_t eblkno;
1797*0Sstevel@tonic-gate 	int eoffset;
1798*0Sstevel@tonic-gate 	struct buf *bp;
1799*0Sstevel@tonic-gate 	int error;
1800*0Sstevel@tonic-gate 	pc_cluster32_t	cn;
1801*0Sstevel@tonic-gate 
1802*0Sstevel@tonic-gate 	pcfid = (struct pc_fid *)fidp;
1803*0Sstevel@tonic-gate 	fsp = VFSTOPCFS(vfsp);
1804*0Sstevel@tonic-gate 
1805*0Sstevel@tonic-gate 	error = pc_lockfs(fsp, 0, 0);
1806*0Sstevel@tonic-gate 	if (error) {
1807*0Sstevel@tonic-gate 		*vpp = NULL;
1808*0Sstevel@tonic-gate 		return (error);
1809*0Sstevel@tonic-gate 	}
1810*0Sstevel@tonic-gate 
1811*0Sstevel@tonic-gate 	if (pcfid->pcfid_block == 0) {
1812*0Sstevel@tonic-gate 		pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
1813*0Sstevel@tonic-gate 		pcp->pc_flags |= PC_EXTERNAL;
1814*0Sstevel@tonic-gate 		*vpp = PCTOV(pcp);
1815*0Sstevel@tonic-gate 		pc_unlockfs(fsp);
1816*0Sstevel@tonic-gate 		return (0);
1817*0Sstevel@tonic-gate 	}
1818*0Sstevel@tonic-gate 	eblkno = pcfid->pcfid_block;
1819*0Sstevel@tonic-gate 	eoffset = pcfid->pcfid_offset;
1820*0Sstevel@tonic-gate 	if ((pc_dbtocl(fsp,
1821*0Sstevel@tonic-gate 	    eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) ||
1822*0Sstevel@tonic-gate 	    (eoffset > fsp->pcfs_clsize)) {
1823*0Sstevel@tonic-gate 		pc_unlockfs(fsp);
1824*0Sstevel@tonic-gate 		*vpp = NULL;
1825*0Sstevel@tonic-gate 		return (EINVAL);
1826*0Sstevel@tonic-gate 	}
1827*0Sstevel@tonic-gate 
1828*0Sstevel@tonic-gate 	if (eblkno >= fsp->pcfs_datastart || (eblkno-fsp->pcfs_rdirstart)
1829*0Sstevel@tonic-gate 	    < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
1830*0Sstevel@tonic-gate 		bp = bread(fsp->pcfs_xdev, eblkno, fsp->pcfs_clsize);
1831*0Sstevel@tonic-gate 	} else {
1832*0Sstevel@tonic-gate 		bp = bread(fsp->pcfs_xdev, eblkno,
1833*0Sstevel@tonic-gate 		    (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize);
1834*0Sstevel@tonic-gate 	}
1835*0Sstevel@tonic-gate 	if (bp->b_flags & (B_ERROR | B_STALE)) {
1836*0Sstevel@tonic-gate 		error = geterror(bp);
1837*0Sstevel@tonic-gate 		brelse(bp);
1838*0Sstevel@tonic-gate 		if (error)
1839*0Sstevel@tonic-gate 			pc_mark_irrecov(fsp);
1840*0Sstevel@tonic-gate 		*vpp = NULL;
1841*0Sstevel@tonic-gate 		pc_unlockfs(fsp);
1842*0Sstevel@tonic-gate 		return (error);
1843*0Sstevel@tonic-gate 	}
1844*0Sstevel@tonic-gate 	ep = (struct pcdir *)(bp->b_un.b_addr + eoffset);
1845*0Sstevel@tonic-gate 	/*
1846*0Sstevel@tonic-gate 	 * Ok, if this is a valid file handle that we gave out,
1847*0Sstevel@tonic-gate 	 * then simply ensuring that the creation time matches,
1848*0Sstevel@tonic-gate 	 * the entry has not been deleted, and it has a valid first
1849*0Sstevel@tonic-gate 	 * character should be enough.
1850*0Sstevel@tonic-gate 	 *
1851*0Sstevel@tonic-gate 	 * Unfortunately, verifying that the <blkno, offset> _still_
1852*0Sstevel@tonic-gate 	 * refers to a directory entry is not easy, since we'd have
1853*0Sstevel@tonic-gate 	 * to search _all_ directories starting from root to find it.
1854*0Sstevel@tonic-gate 	 * That's a high price to pay just in case somebody is forging
1855*0Sstevel@tonic-gate 	 * file handles. So instead we verify that as much of the
1856*0Sstevel@tonic-gate 	 * entry is valid as we can:
1857*0Sstevel@tonic-gate 	 *
1858*0Sstevel@tonic-gate 	 * 1. The starting cluster is 0 (unallocated) or valid
1859*0Sstevel@tonic-gate 	 * 2. It is not an LFN entry
1860*0Sstevel@tonic-gate 	 * 3. It is not hidden (unless mounted as such)
1861*0Sstevel@tonic-gate 	 * 4. It is not the label
1862*0Sstevel@tonic-gate 	 */
1863*0Sstevel@tonic-gate 	cn = pc_getstartcluster(fsp, ep);
1864*0Sstevel@tonic-gate 	/*
1865*0Sstevel@tonic-gate 	 * if the starting cluster is valid, but not valid according
1866*0Sstevel@tonic-gate 	 * to pc_validcl(), force it to be to simplify the following if.
1867*0Sstevel@tonic-gate 	 */
1868*0Sstevel@tonic-gate 	if (cn == 0)
1869*0Sstevel@tonic-gate 		cn = PCF_FIRSTCLUSTER;
1870*0Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
1871*0Sstevel@tonic-gate 		if (cn >= PCF_LASTCLUSTER32)
1872*0Sstevel@tonic-gate 			cn = PCF_FIRSTCLUSTER;
1873*0Sstevel@tonic-gate 	} else {
1874*0Sstevel@tonic-gate 		if (cn >= PCF_LASTCLUSTER)
1875*0Sstevel@tonic-gate 			cn = PCF_FIRSTCLUSTER;
1876*0Sstevel@tonic-gate 	}
1877*0Sstevel@tonic-gate 	if ((!pc_validcl(fsp, cn)) ||
1878*0Sstevel@tonic-gate 	    (PCDL_IS_LFN(ep)) ||
1879*0Sstevel@tonic-gate 	    (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) ||
1880*0Sstevel@tonic-gate 	    ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) {
1881*0Sstevel@tonic-gate 		bp->b_flags |= B_STALE | B_AGE;
1882*0Sstevel@tonic-gate 		brelse(bp);
1883*0Sstevel@tonic-gate 		pc_unlockfs(fsp);
1884*0Sstevel@tonic-gate 		return (EINVAL);
1885*0Sstevel@tonic-gate 	}
1886*0Sstevel@tonic-gate 	if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) &&
1887*0Sstevel@tonic-gate 	    (ep->pcd_filename[0] != PCD_ERASED) &&
1888*0Sstevel@tonic-gate 	    (pc_validchar(ep->pcd_filename[0]) ||
1889*0Sstevel@tonic-gate 		(ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) {
1890*0Sstevel@tonic-gate 		pcp = pc_getnode(fsp, eblkno, eoffset, ep);
1891*0Sstevel@tonic-gate 		pcp->pc_flags |= PC_EXTERNAL;
1892*0Sstevel@tonic-gate 		*vpp = PCTOV(pcp);
1893*0Sstevel@tonic-gate 	} else {
1894*0Sstevel@tonic-gate 		*vpp = NULL;
1895*0Sstevel@tonic-gate 	}
1896*0Sstevel@tonic-gate 	bp->b_flags |= B_STALE | B_AGE;
1897*0Sstevel@tonic-gate 	brelse(bp);
1898*0Sstevel@tonic-gate 	pc_unlockfs(fsp);
1899*0Sstevel@tonic-gate 	return (0);
1900*0Sstevel@tonic-gate }
1901*0Sstevel@tonic-gate 
1902*0Sstevel@tonic-gate /*
1903*0Sstevel@tonic-gate  * if device is a PCMCIA psuedo floppy, return 1
1904*0Sstevel@tonic-gate  * otherwise, return 0
1905*0Sstevel@tonic-gate  */
1906*0Sstevel@tonic-gate static int
1907*0Sstevel@tonic-gate pcfs_psuedo_floppy(dev_t rdev)
1908*0Sstevel@tonic-gate {
1909*0Sstevel@tonic-gate 	int			error, err;
1910*0Sstevel@tonic-gate 	struct dk_cinfo		info;
1911*0Sstevel@tonic-gate 	ldi_handle_t		lh;
1912*0Sstevel@tonic-gate 	ldi_ident_t		li;
1913*0Sstevel@tonic-gate 
1914*0Sstevel@tonic-gate 	err = ldi_ident_from_mod(&modlinkage, &li);
1915*0Sstevel@tonic-gate 	if (err) {
1916*0Sstevel@tonic-gate 		PC_DPRINTF1(1,
1917*0Sstevel@tonic-gate 		    "pcfs_psuedo_floppy: ldi_ident_from_mod err=%d\n", err);
1918*0Sstevel@tonic-gate 		return (0);
1919*0Sstevel@tonic-gate 	}
1920*0Sstevel@tonic-gate 
1921*0Sstevel@tonic-gate 	err = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, CRED(), &lh, li);
1922*0Sstevel@tonic-gate 	ldi_ident_release(li);
1923*0Sstevel@tonic-gate 	if (err) {
1924*0Sstevel@tonic-gate 		PC_DPRINTF1(1,
1925*0Sstevel@tonic-gate 		    "pcfs_psuedo_floppy: ldi_open err=%d\n", err);
1926*0Sstevel@tonic-gate 		return (0);
1927*0Sstevel@tonic-gate 	}
1928*0Sstevel@tonic-gate 
1929*0Sstevel@tonic-gate 	/* return value stored in err is purposfully ignored */
1930*0Sstevel@tonic-gate 	error = ldi_ioctl(lh, DKIOCINFO, (intptr_t)&info, FKIOCTL,
1931*0Sstevel@tonic-gate 	    CRED(), &err);
1932*0Sstevel@tonic-gate 
1933*0Sstevel@tonic-gate 	err = ldi_close(lh, FREAD, CRED());
1934*0Sstevel@tonic-gate 	if (err != 0) {
1935*0Sstevel@tonic-gate 		PC_DPRINTF1(1,
1936*0Sstevel@tonic-gate 		    "pcfs_psuedo_floppy: ldi_close err=%d\n", err);
1937*0Sstevel@tonic-gate 		return (0);
1938*0Sstevel@tonic-gate 	}
1939*0Sstevel@tonic-gate 
1940*0Sstevel@tonic-gate 	if ((error == 0) && (info.dki_ctype == DKC_PCMCIA_MEM) &&
1941*0Sstevel@tonic-gate 		(info.dki_flags & DKI_PCMCIA_PFD))
1942*0Sstevel@tonic-gate 		return (1);
1943*0Sstevel@tonic-gate 	else
1944*0Sstevel@tonic-gate 		return (0);
1945*0Sstevel@tonic-gate }
1946*0Sstevel@tonic-gate 
1947*0Sstevel@tonic-gate /*
1948*0Sstevel@tonic-gate  * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about
1949*0Sstevel@tonic-gate  * a meg), so we can't bread() it all in at once. This routine reads a
1950*0Sstevel@tonic-gate  * fat a chunk at a time.
1951*0Sstevel@tonic-gate  */
1952*0Sstevel@tonic-gate static int
1953*0Sstevel@tonic-gate pc_readfat(struct pcfs *fsp, uchar_t *fatp, daddr_t start, size_t fatsize)
1954*0Sstevel@tonic-gate {
1955*0Sstevel@tonic-gate 	struct buf *bp;
1956*0Sstevel@tonic-gate 	size_t off;
1957*0Sstevel@tonic-gate 	size_t readsize;
1958*0Sstevel@tonic-gate 
1959*0Sstevel@tonic-gate 	readsize = fsp->pcfs_clsize;
1960*0Sstevel@tonic-gate 	for (off = 0; off < fatsize; off += readsize, fatp += readsize) {
1961*0Sstevel@tonic-gate 		if (readsize > (fatsize - off))
1962*0Sstevel@tonic-gate 			readsize = fatsize - off;
1963*0Sstevel@tonic-gate 		bp = bread(fsp->pcfs_xdev,
1964*0Sstevel@tonic-gate 		    pc_dbdaddr(fsp, start +
1965*0Sstevel@tonic-gate 			pc_cltodb(fsp, pc_lblkno(fsp, off))),
1966*0Sstevel@tonic-gate 		    readsize);
1967*0Sstevel@tonic-gate 		if (bp->b_flags & (B_ERROR | B_STALE)) {
1968*0Sstevel@tonic-gate 			brelse(bp);
1969*0Sstevel@tonic-gate 			return (EIO);
1970*0Sstevel@tonic-gate 		}
1971*0Sstevel@tonic-gate 		bp->b_flags |= B_STALE | B_AGE;
1972*0Sstevel@tonic-gate 		bcopy(bp->b_un.b_addr, fatp, readsize);
1973*0Sstevel@tonic-gate 		brelse(bp);
1974*0Sstevel@tonic-gate 	}
1975*0Sstevel@tonic-gate 	return (0);
1976*0Sstevel@tonic-gate }
1977*0Sstevel@tonic-gate 
1978*0Sstevel@tonic-gate /*
1979*0Sstevel@tonic-gate  * We write the FAT out a _lot_, in order to make sure that it
1980*0Sstevel@tonic-gate  * is up-to-date. But on a FAT32 system (large drive, small clusters)
1981*0Sstevel@tonic-gate  * the FAT might be a couple of megabytes, and writing it all out just
1982*0Sstevel@tonic-gate  * because we created or deleted a small file is painful (especially
1983*0Sstevel@tonic-gate  * since we do it for each alternate FAT too). So instead, for FAT16 and
1984*0Sstevel@tonic-gate  * FAT32 we only write out the bit that has changed. We don't clear
1985*0Sstevel@tonic-gate  * the 'updated' fields here because the caller might be writing out
1986*0Sstevel@tonic-gate  * several FATs, so the caller must use pc_clear_fatchanges() after
1987*0Sstevel@tonic-gate  * all FATs have been updated.
1988*0Sstevel@tonic-gate  */
1989*0Sstevel@tonic-gate static int
1990*0Sstevel@tonic-gate pc_writefat(struct pcfs *fsp, daddr_t start)
1991*0Sstevel@tonic-gate {
1992*0Sstevel@tonic-gate 	struct buf *bp;
1993*0Sstevel@tonic-gate 	size_t off;
1994*0Sstevel@tonic-gate 	size_t writesize;
1995*0Sstevel@tonic-gate 	int	error;
1996*0Sstevel@tonic-gate 	uchar_t *fatp = fsp->pcfs_fatp;
1997*0Sstevel@tonic-gate 	size_t fatsize = fsp->pcfs_fatsize;
1998*0Sstevel@tonic-gate 
1999*0Sstevel@tonic-gate 	writesize = fsp->pcfs_clsize;
2000*0Sstevel@tonic-gate 	for (off = 0; off < fatsize; off += writesize, fatp += writesize) {
2001*0Sstevel@tonic-gate 		if (writesize > (fatsize - off))
2002*0Sstevel@tonic-gate 			writesize = fatsize - off;
2003*0Sstevel@tonic-gate 		if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) {
2004*0Sstevel@tonic-gate 			continue;
2005*0Sstevel@tonic-gate 		}
2006*0Sstevel@tonic-gate 		bp = ngeteblk(writesize);
2007*0Sstevel@tonic-gate 		bp->b_edev = fsp->pcfs_xdev;
2008*0Sstevel@tonic-gate 		bp->b_dev = cmpdev(bp->b_edev);
2009*0Sstevel@tonic-gate 		bp->b_blkno = pc_dbdaddr(fsp, start +
2010*0Sstevel@tonic-gate 		    pc_cltodb(fsp, pc_lblkno(fsp, off)));
2011*0Sstevel@tonic-gate 		bcopy(fatp, bp->b_un.b_addr, writesize);
2012*0Sstevel@tonic-gate 		bwrite2(bp);
2013*0Sstevel@tonic-gate 		error = geterror(bp);
2014*0Sstevel@tonic-gate 		brelse(bp);
2015*0Sstevel@tonic-gate 		if (error) {
2016*0Sstevel@tonic-gate 			return (error);
2017*0Sstevel@tonic-gate 		}
2018*0Sstevel@tonic-gate 	}
2019*0Sstevel@tonic-gate 	return (0);
2020*0Sstevel@tonic-gate }
2021*0Sstevel@tonic-gate 
2022*0Sstevel@tonic-gate /*
2023*0Sstevel@tonic-gate  * Mark the FAT cluster that 'cn' is stored in as modified.
2024*0Sstevel@tonic-gate  */
2025*0Sstevel@tonic-gate void
2026*0Sstevel@tonic-gate pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn)
2027*0Sstevel@tonic-gate {
2028*0Sstevel@tonic-gate 	pc_cluster32_t	bn;
2029*0Sstevel@tonic-gate 	size_t		size;
2030*0Sstevel@tonic-gate 
2031*0Sstevel@tonic-gate 	/* which fat block is the cluster number stored in? */
2032*0Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {
2033*0Sstevel@tonic-gate 		size = sizeof (pc_cluster32_t);
2034*0Sstevel@tonic-gate 		bn = pc_lblkno(fsp, cn * size);
2035*0Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
2036*0Sstevel@tonic-gate 	} else if (IS_FAT16(fsp)) {
2037*0Sstevel@tonic-gate 		size = sizeof (pc_cluster16_t);
2038*0Sstevel@tonic-gate 		bn = pc_lblkno(fsp, cn * size);
2039*0Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
2040*0Sstevel@tonic-gate 	} else {
2041*0Sstevel@tonic-gate 		offset_t off;
2042*0Sstevel@tonic-gate 		pc_cluster32_t nbn;
2043*0Sstevel@tonic-gate 
2044*0Sstevel@tonic-gate 		ASSERT(IS_FAT12(fsp));
2045*0Sstevel@tonic-gate 		off = cn + (cn >> 1);
2046*0Sstevel@tonic-gate 		bn = pc_lblkno(fsp, off);
2047*0Sstevel@tonic-gate 		fsp->pcfs_fat_changemap[bn] = 1;
2048*0Sstevel@tonic-gate 		/* does this field wrap into the next fat cluster? */
2049*0Sstevel@tonic-gate 		nbn = pc_lblkno(fsp, off + 1);
2050*0Sstevel@tonic-gate 		if (nbn != bn) {
2051*0Sstevel@tonic-gate 			fsp->pcfs_fat_changemap[nbn] = 1;
2052*0Sstevel@tonic-gate 		}
2053*0Sstevel@tonic-gate 	}
2054*0Sstevel@tonic-gate }
2055*0Sstevel@tonic-gate 
2056*0Sstevel@tonic-gate /*
2057*0Sstevel@tonic-gate  * return whether the FAT cluster 'bn' is updated and needs to
2058*0Sstevel@tonic-gate  * be written out.
2059*0Sstevel@tonic-gate  */
2060*0Sstevel@tonic-gate int
2061*0Sstevel@tonic-gate pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn)
2062*0Sstevel@tonic-gate {
2063*0Sstevel@tonic-gate 	return (fsp->pcfs_fat_changemap[bn] == 1);
2064*0Sstevel@tonic-gate }
2065