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