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