xref: /illumos-gate/usr/src/uts/common/fs/pcfs/pc_vfsops.c (revision 24da5b34f49324ed742a340010ed5bd3d4e06625)
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 2007 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/vfs_opreg.h>
38 #include <sys/vnode.h>
39 #include <sys/fdio.h>
40 #include <sys/file.h>
41 #include <sys/uio.h>
42 #include <sys/conf.h>
43 #undef NFSCLIENT
44 #include <sys/statvfs.h>
45 #include <sys/mount.h>
46 #include <sys/pathname.h>
47 #include <sys/cmn_err.h>
48 #include <sys/debug.h>
49 #include <sys/sysmacros.h>
50 #include <sys/conf.h>
51 #include <sys/mkdev.h>
52 #include <sys/swap.h>
53 #include <sys/sunddi.h>
54 #include <sys/sunldi.h>
55 #include <sys/dktp/fdisk.h>
56 #include <sys/fs/pc_label.h>
57 #include <sys/fs/pc_fs.h>
58 #include <sys/fs/pc_dir.h>
59 #include <sys/fs/pc_node.h>
60 #include <fs/fs_subr.h>
61 #include <sys/modctl.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 v1.100",
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,		{ .vfs_mount = pcfs_mount },
230 		VFSNAME_UNMOUNT,	{ .vfs_unmount = pcfs_unmount },
231 		VFSNAME_ROOT,		{ .vfs_root = pcfs_root },
232 		VFSNAME_STATVFS,	{ .vfs_statvfs = pcfs_statvfs },
233 		VFSNAME_SYNC,		{ .vfs_sync = pcfs_sync },
234 		VFSNAME_VGET,		{ .vfs_vget = pcfs_vget },
235 		VFSNAME_FREEVFS,	{ .vfs_freevfs = 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 *c;
295 	char *data = uap->dataptr;
296 	int datalen = uap->datalen;
297 	int dos_ldrive = 0;
298 	int error;
299 	int fattype;
300 	minor_t	minor;
301 	int oflag, aflag;
302 
303 	if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
304 		return (error);
305 
306 	PC_DPRINTF0(4, "pcfs_mount\n");
307 	if (mvp->v_type != VDIR) {
308 		return (ENOTDIR);
309 	}
310 	mutex_enter(&mvp->v_lock);
311 	if ((uap->flags & MS_REMOUNT) == 0 &&
312 	    (uap->flags & MS_OVERLAY) == 0 &&
313 	    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
314 		mutex_exit(&mvp->v_lock);
315 		return (EBUSY);
316 	}
317 	mutex_exit(&mvp->v_lock);
318 
319 	/*
320 	 * The caller is responsible for making sure to always
321 	 * pass in sizeof(struct pcfs_args) (or the old one).
322 	 * Doing this is the only way to know an EINVAL return
323 	 * from mount(2) is due to the "not a DOS filesystem"
324 	 * EINVAL that pc_verify/pc_getfattype could return.
325 	 */
326 	if ((datalen != sizeof (struct pcfs_args)) &&
327 	    (datalen != sizeof (struct old_pcfs_args))) {
328 		return (EINVAL);
329 	} else {
330 		struct pcfs_args tmp_tz;
331 		int hidden = 0;
332 		int foldcase = 0;
333 		int noclamptime = 0;
334 
335 		tmp_tz.flags = 0;
336 		if (copyin(data, &tmp_tz, datalen)) {
337 			return (EFAULT);
338 		}
339 		if (datalen == sizeof (struct pcfs_args)) {
340 			hidden = tmp_tz.flags & PCFS_MNT_HIDDEN;
341 			foldcase = tmp_tz.flags & PCFS_MNT_FOLDCASE;
342 			noclamptime = tmp_tz.flags & PCFS_MNT_NOCLAMPTIME;
343 		}
344 
345 		if (hidden)
346 			vfs_setmntopt(vfsp, MNTOPT_PCFS_HIDDEN,	NULL, 0);
347 		if (foldcase)
348 			vfs_setmntopt(vfsp, MNTOPT_PCFS_FOLDCASE, NULL, 0);
349 		if (noclamptime)
350 			vfs_setmntopt(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL, 0);
351 		/*
352 		 * more than one pc filesystem can be mounted on x86
353 		 * so the pc_tz structure is now a critical region
354 		 */
355 		mutex_enter(&pcfslock);
356 		if (pc_mounttab == NULL)
357 			bcopy(&tmp_tz, &pc_tz, sizeof (struct pcfs_args));
358 		mutex_exit(&pcfslock);
359 	}
360 	/*
361 	 * Resolve path name of special file being mounted.
362 	 */
363 	if (error = pn_get(uap->spec, UIO_USERSPACE, &special)) {
364 		return (error);
365 	}
366 	if (error =
367 	    lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &bvp)) {
368 		/*
369 		 * Split the pathname string at the last ':' separator.
370 		 * If there's no ':' in the device name, or the ':' is the
371 		 * last character in the string, the name is invalid and
372 		 * the error from the previous lookup will be returned.
373 		 */
374 		c = strrchr(special.pn_path, ':');
375 		if (c == NULL || strlen(c) == 0)
376 			goto devlookup_done;
377 
378 		*c++ = '\0';
379 
380 		/*
381 		 * PCFS partition name suffixes can be:
382 		 *	- "boot" to indicate the X86BOOT partition
383 		 *	- a drive letter [c-z] for the "DOS logical drive"
384 		 *	- a drive number 1..24 for the "DOS logical drive"
385 		 */
386 		if (strcasecmp(c, "boot") == 0) {
387 			/*
388 			 * The Solaris boot partition is requested.
389 			 */
390 			dos_ldrive = BOOT_PARTITION_DRIVE;
391 		} else if (strspn(c, "0123456789") == strlen(c)) {
392 			/*
393 			 * All digits - parse the partition number.
394 			 */
395 			long drvnum = 0;
396 
397 			if ((error = ddi_strtol(c, NULL, 10, &drvnum)) == 0) {
398 				/*
399 				 * A number alright - in the allowed range ?
400 				 */
401 				if (drvnum > 24 || drvnum == 0)
402 					error = EINVAL;
403 			}
404 			if (error)
405 				goto devlookup_done;
406 			dos_ldrive = (int)drvnum;
407 		} else if (strlen(c) == 1) {
408 			/*
409 			 * A single trailing character - is it [c-zC-Z] ?
410 			 */
411 			*c = tolower(*c);
412 			if (*c < 'c' || *c > 'z') {
413 				error = EINVAL;
414 				goto devlookup_done;
415 			}
416 			dos_ldrive = 1 + *c - 'c';
417 		} else {
418 			/*
419 			 * Can't parse this - pass through previous error.
420 			 */
421 			goto devlookup_done;
422 		}
423 
424 		ASSERT(dos_ldrive > 0);
425 
426 		error = lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW,
427 		    NULLVPP, &bvp);
428 	}
429 devlookup_done:
430 	pn_free(&special);
431 	if (error)
432 		return (error);
433 
434 	if (bvp->v_type != VBLK) {
435 		VN_RELE(bvp);
436 		return (ENOTBLK);
437 	}
438 	xdev = bvp->v_rdev;
439 	/*
440 	 * Verify caller's permission to open the device special file.
441 	 */
442 	if ((vfsp->vfs_flag & VFS_RDONLY) != 0 ||
443 	    ((uap->flags & MS_RDONLY) != 0)) {
444 		oflag = FREAD;
445 		aflag = VREAD;
446 	} else {
447 		oflag = FREAD | FWRITE;
448 		aflag = VREAD | VWRITE;
449 	}
450 	if ((error = VOP_ACCESS(bvp, aflag, 0, cr)) != 0 ||
451 	    (error = secpolicy_spec_open(cr, bvp, oflag)) != 0) {
452 		VN_RELE(bvp);
453 		return (error);
454 	}
455 
456 	VN_RELE(bvp);
457 	if (getmajor(xdev) >= devcnt) {
458 		return (ENXIO);
459 	}
460 	/*
461 	 * Ensure that this logical drive isn't already mounted, unless
462 	 * this is a REMOUNT request.
463 	 * Note: The framework will perform this check if the "...:c"
464 	 * PCFS-style "logical drive" syntax has not been used and an
465 	 * actually existing physical device is backing this filesystem.
466 	 */
467 	if (dos_ldrive) {
468 		mutex_enter(&pcfslock);
469 		for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt)
470 			if (fsp->pcfs_xdev == xdev &&
471 			    fsp->pcfs_ldrv == dos_ldrive) {
472 				mutex_exit(&pcfslock);
473 				if (uap->flags & MS_REMOUNT) {
474 					return (0);
475 				} else {
476 					return (EBUSY);
477 				}
478 			}
479 		/*
480 		 * Assign a unique device number for the vfs
481 		 * The old way (getudev() + a constantly incrementing
482 		 * major number) was wrong because it changes vfs_dev
483 		 * across mounts and reboots, which breaks nfs file handles.
484 		 * UFS just uses the real dev_t. We can't do that because
485 		 * of the way pcfs opens fdisk partitons (the :c and :d
486 		 * partitions are on the same dev_t). Though that _might_
487 		 * actually be ok, since the file handle contains an
488 		 * absolute block number, it's probably better to make them
489 		 * different. So I think we should retain the original
490 		 * dev_t, but come up with a different minor number based
491 		 * on the logical drive that will _always_ come up the same.
492 		 * For now, we steal the upper 6 bits.
493 		 */
494 #ifdef notdef
495 		/* what should we do here? */
496 		if (((getminor(xdev) >> 12) & 0x3F) != 0)
497 			printf("whoops - upper bits used!\n");
498 #endif
499 		minor = ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32;
500 		pseudodev = makedevice(getmajor(xdev), minor);
501 		if (vfs_devmounting(pseudodev, vfsp)) {
502 			mutex_exit(&pcfslock);
503 			return (EBUSY);
504 		}
505 		if (vfs_devismounted(pseudodev)) {
506 			mutex_exit(&pcfslock);
507 			if (uap->flags & MS_REMOUNT) {
508 				return (0);
509 			} else {
510 				return (EBUSY);
511 			}
512 		}
513 		mutex_exit(&pcfslock);
514 	} else {
515 		if (vfs_devmounting(xdev, vfsp)) {
516 			return (EBUSY);
517 		}
518 		if (vfs_devismounted(xdev))
519 			if (uap->flags & MS_REMOUNT) {
520 				return (0);
521 			} else {
522 				return (EBUSY);
523 			}
524 		pseudodev = xdev;
525 	}
526 
527 	if (uap->flags & MS_RDONLY) {
528 		vfsp->vfs_flag |= VFS_RDONLY;
529 		vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
530 	}
531 
532 	/*
533 	 * Mount the filesystem
534 	 */
535 	devvp = makespecvp(xdev, VBLK);
536 	if (IS_SWAPVP(devvp)) {
537 		VN_RELE(devvp);
538 		return (EBUSY);
539 	}
540 
541 	/*
542 	 * special handling for PCMCIA memory card
543 	 * with pseudo floppies organization
544 	 */
545 	if (dos_ldrive == 0 && pcfs_pseudo_floppy(xdev)) {
546 		dosstart = (daddr_t)0;
547 		fattype = PCFS_PCMCIA_NO_CIS;
548 	} else {
549 		if (error = pc_getfattype(devvp, dos_ldrive, &dosstart,
550 		    &fattype)) {
551 			VN_RELE(devvp);
552 			return (error);
553 		}
554 	}
555 
556 	(void) VOP_PUTPAGE(devvp, (offset_t)0, (uint_t)0, B_INVAL, cr);
557 	fsp = kmem_zalloc((uint_t)sizeof (struct pcfs), KM_SLEEP);
558 	fsp->pcfs_vfs = vfsp;
559 	fsp->pcfs_flags = fattype;
560 	fsp->pcfs_devvp = devvp;
561 	fsp->pcfs_xdev = xdev;
562 	fsp->pcfs_ldrv = dos_ldrive;
563 	fsp->pcfs_dosstart = dosstart;
564 	mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL);
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 ldrive, 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 	 * "ldrive == 0" should never happen, as this is a request to
937 	 * mount the physical device (and ignore partitioning). The code
938 	 * in pcfs_mount() should have made sure that a logical drive number
939 	 * is at least 1, meaning we're looking for drive "C:". It is not
940 	 * safe (and a bug in the callers of this function) to request logical
941 	 * drive number 0; we could ASSERT() but a graceful EIO is a more
942 	 * polite way.
943 	 */
944 	if (ldrive == 0) {
945 		cmn_err(CE_NOTE, "!pcfs: request for logical partition zero");
946 		noLogicalDrive(ldrive);
947 		return (EIO);
948 	}
949 
950 	/*
951 	 *  Copy from disk block into memory aligned structure for fdisk usage.
952 	 */
953 	dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr;
954 	bcopy(dosp_ptr->parts, dosp, sizeof (struct ipart) * FD_NUMPART);
955 
956 	if (ltohs(dosp_ptr->signature) != MBB_MAGIC) {
957 		cmn_err(CE_NOTE, "!pcfs: MBR partition table signature err");
958 		return (EINVAL);
959 	}
960 
961 	/*
962 	 * Get a summary of what is in the Master FDISK table.
963 	 * Normally we expect to find one partition marked as a DOS drive.
964 	 * This partition is the one Windows calls the primary dos partition.
965 	 * If the machine has any logical drives then we also expect
966 	 * to find a partition marked as an extended DOS partition.
967 	 *
968 	 * Sometimes we'll find multiple partitions marked as DOS drives.
969 	 * The Solaris fdisk program allows these partitions
970 	 * to be created, but Windows fdisk no longer does.  We still need
971 	 * to support these, though, since Windows does.  We also need to fix
972 	 * our fdisk to behave like the Windows version.
973 	 *
974 	 * It turns out that some off-the-shelf media have *only* an
975 	 * Extended partition, so we need to deal with that case as well.
976 	 *
977 	 * Only a single (the first) Extended or Boot Partition will
978 	 * be recognized.  Any others will be ignored.
979 	 */
980 	for (i = 0; i < FD_NUMPART; i++) {
981 		PC_DPRINTF1(2, "findTheDrive: found partition type %02x",
982 			dosp[i].systid);
983 
984 		if (isDosDrive(dosp[i].systid)) {
985 			if (primaryPart < 0) {
986 				logicalDriveCount++;
987 				primaryPart = i;
988 			} else {
989 				extraDrives[numExtraDrives++] = i;
990 			}
991 			continue;
992 		}
993 		if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) {
994 			extendedPart = i;
995 			continue;
996 		}
997 		if ((bootPart < 0) && isBootPart(dosp[i].systid)) {
998 			bootPart = i;
999 			continue;
1000 		}
1001 	}
1002 
1003 	if (ldrive == BOOT_PARTITION_DRIVE) {
1004 		if (bootPart < 0) {
1005 			noLogicalDrive(ldrive);
1006 			return (EINVAL);
1007 		}
1008 		*sysid = dosp[bootPart].systid;
1009 		*startSector = ltohi(dosp[bootPart].relsect);
1010 		return (0);
1011 	}
1012 
1013 	if (ldrive == PRIMARY_DOS_DRIVE && primaryPart >= 0) {
1014 		*sysid = dosp[primaryPart].systid;
1015 		*startSector = ltohi(dosp[primaryPart].relsect);
1016 		return (0);
1017 	}
1018 
1019 	/*
1020 	 * We are not looking for the C: drive (or the primary drive
1021 	 * was not found), so we had better have an extended partition
1022 	 * or extra drives in the Master FDISK table.
1023 	 */
1024 	if ((extendedPart < 0) && (numExtraDrives == 0)) {
1025 		cmn_err(CE_NOTE, "!pcfs: no extended dos partition");
1026 		noLogicalDrive(ldrive);
1027 		return (EINVAL);
1028 	}
1029 
1030 	if (extendedPart >= 0) {
1031 		diskblk = xstartsect = ltohi(dosp[extendedPart].relsect);
1032 		xnumsect = ltohi(dosp[extendedPart].numsect);
1033 		do {
1034 			/*
1035 			 *  If the seek would not cause us to change
1036 			 *  position on the drive, then we're out of
1037 			 *  extended partitions to examine.
1038 			 */
1039 			if (diskblk == lastseek)
1040 				break;
1041 			logicalDriveCount += numDrives;
1042 			/*
1043 			 *  Seek the next extended partition, and find
1044 			 *  logical drives within it.
1045 			 */
1046 			brelse(*bp);
1047 			*bp = bread(dev, diskblk, PC_SAFESECSIZE);
1048 			if ((*bp)->b_flags & B_ERROR) {
1049 				PC_DPRINTF0(1, "pc_getfattype: read error\n");
1050 				return (EIO);
1051 			}
1052 			lastseek = diskblk;
1053 			dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr;
1054 			if (ltohs(dosp_ptr->signature) != MBB_MAGIC) {
1055 				cmn_err(CE_NOTE, "!pcfs: "
1056 				    "extended partition signature err");
1057 				return (EINVAL);
1058 			}
1059 			bcopy(dosp_ptr->parts, dosp,
1060 			    sizeof (struct ipart) * FD_NUMPART);
1061 			/*
1062 			 *  Count up drives, and track where the next
1063 			 *  extended partition is in case we need it.  We
1064 			 *  are expecting only one extended partition.  If
1065 			 *  there is more than one we'll only go to the
1066 			 *  first one we see, but warn about ignoring.
1067 			 */
1068 			numDrives = 0;
1069 			for (i = 0; i < FD_NUMPART; i++) {
1070 				if (isDosDrive(dosp[i].systid)) {
1071 					extndDrives[numDrives++] = i;
1072 				} else if (isDosExtended(dosp[i].systid)) {
1073 					if (diskblk != lastseek) {
1074 						/*
1075 						 * Already found an extended
1076 						 * partition in this table.
1077 						 */
1078 						cmn_err(CE_NOTE,
1079 						    "!pcfs: ignoring unexpected"
1080 						    " additional extended"
1081 						    " partition");
1082 					} else {
1083 						diskblk = xstartsect +
1084 						    ltohi(dosp[i].relsect);
1085 					}
1086 				}
1087 			}
1088 		} while (ldrive > logicalDriveCount + numDrives);
1089 
1090 		if (ldrive <= logicalDriveCount + numDrives) {
1091 			/*
1092 			 * The number of logical drives we've found thus
1093 			 * far is enough to get us to the one we were
1094 			 * searching for.
1095 			 */
1096 			driveIndex = logicalDriveCount + numDrives - ldrive;
1097 			*sysid = dosp[extndDrives[driveIndex]].systid;
1098 			*startSector =
1099 			    ltohi(dosp[extndDrives[driveIndex]].relsect) +
1100 			    lastseek;
1101 			if (*startSector > (xstartsect + xnumsect)) {
1102 				cmn_err(CE_NOTE, "!pcfs: extended partition "
1103 				    "values bad");
1104 				return (EINVAL);
1105 			}
1106 			return (0);
1107 		} else {
1108 			/*
1109 			 * We ran out of extended dos partition
1110 			 * drives.  The only hope now is to go
1111 			 * back to extra drives defined in the master
1112 			 * fdisk table.  But we overwrote that table
1113 			 * already, so we must load it in again.
1114 			 */
1115 			logicalDriveCount += numDrives;
1116 			brelse(*bp);
1117 			*bp = bread(dev, (daddr_t)0, PC_SAFESECSIZE);
1118 			if ((*bp)->b_flags & B_ERROR) {
1119 				PC_DPRINTF0(1, "pc_getfattype: read error\n");
1120 				return (EIO);
1121 			}
1122 			dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr;
1123 			bcopy(dosp_ptr->parts, dosp,
1124 			    sizeof (struct ipart) * FD_NUMPART);
1125 		}
1126 	}
1127 	/*
1128 	 *  Still haven't found the drive, is it an extra
1129 	 *  drive defined in the main FDISK table?
1130 	 */
1131 	if (ldrive <= logicalDriveCount + numExtraDrives) {
1132 		driveIndex = logicalDriveCount + numExtraDrives - ldrive;
1133 		ASSERT(driveIndex < MIN(numExtraDrives, FD_NUMPART));
1134 		*sysid = dosp[extraDrives[driveIndex]].systid;
1135 		*startSector = ltohi(dosp[extraDrives[driveIndex]].relsect);
1136 		return (0);
1137 	}
1138 	/*
1139 	 *  Still haven't found the drive, and there is
1140 	 *  nowhere else to look.
1141 	 */
1142 	noLogicalDrive(ldrive);
1143 	return (EINVAL);
1144 }
1145 
1146 /*
1147  * FAT12/FAT16 specific consistency checks.
1148  */
1149 static int
1150 check_bpb_fat16(struct bootsec *bpb)
1151 {
1152 	if (pcfsdebuglevel >= 5) {
1153 		PC_DPRINTF1(5, "check_bpb_fat: RootEntCount = %d",
1154 			ltohs(bpb->rdirents[0]));
1155 		PC_DPRINTF1(5, "check_bpb_fat16: TotSec16 = %d",
1156 			ltohs(bpb->numsect[0]));
1157 		PC_DPRINTF1(5, "check_bpb_fat16: FATSz16 = %d",
1158 			ltohs(bpb->fatsec));
1159 		PC_DPRINTF1(5, "check_bpb_fat16: TotSec32 = %d",
1160 			ltohi(bpb->totalsec));
1161 	}
1162 	return (ltohs(bpb->rdirents[0]) > 0 &&	 /* RootEntCnt > 0 */
1163 		((ltohs(bpb->numsect[0]) == 0 && /* TotSec16 == 0 */
1164 		ltohi(bpb->totalsec) > 0) ||	 /* TotSec32 > 0 */
1165 		ltohs(bpb->numsect[0]) > 0) && /* TotSec16 > 0 */
1166 		ltohs(bpb->fatsec) > 0);	 /* FatSz16 > 0 */
1167 }
1168 
1169 /*
1170  * FAT32 specific consistency checks.
1171  */
1172 static int
1173 check_bpb_fat32(struct fat32_bootsec *bpb)
1174 {
1175 	if (pcfsdebuglevel >= 5) {
1176 		PC_DPRINTF1(5, "check_bpb_fat32: RootEntCount = %d",
1177 			ltohs(bpb->f_bs.rdirents[0]));
1178 		PC_DPRINTF1(5, "check_bpb_fat32: TotSec16 = %d",
1179 			ltohs(bpb->f_bs.numsect[0]));
1180 		PC_DPRINTF1(5, "check_bpb_fat32: FATSz16 = %d",
1181 			ltohs(bpb->f_bs.fatsec));
1182 		PC_DPRINTF1(5, "check_bpb_fat32: TotSec32 = %d",
1183 			ltohi(bpb->f_bs.totalsec));
1184 		PC_DPRINTF1(5, "check_bpb_fat32: FATSz32 = %d",
1185 			ltohi(bpb->f_fatlength));
1186 	}
1187 	return (ltohs(bpb->f_bs.rdirents[0]) == 0 &&
1188 		ltohs(bpb->f_bs.numsect[0]) == 0 &&
1189 		ltohs(bpb->f_bs.fatsec) == 0 &&
1190 		ltohi(bpb->f_bs.totalsec) > 0 &&
1191 		ltohi(bpb->f_fatlength) > 0);
1192 }
1193 
1194 /*
1195  * Calculate the number of clusters in order to determine
1196  * the type of FAT we are looking at.  This is the only
1197  * recommended way of determining FAT type, though there
1198  * are other hints in the data, this is the best way.
1199  */
1200 static ulong_t
1201 bpb_to_numclusters(uchar_t *cp)
1202 {
1203 	struct fat32_bootsec *bpb;
1204 
1205 	ulong_t rootdirsectors;
1206 	ulong_t FATsz;
1207 	ulong_t TotSec;
1208 	ulong_t DataSec;
1209 	ulong_t CountOfClusters;
1210 	char FileSysType[9];
1211 
1212 	/*
1213 	 * Cast it to FAT32 bpb. If it turns out to be FAT12/16, its
1214 	 * OK, we won't try accessing the data beyond the FAT16 header
1215 	 * boundary.
1216 	 */
1217 	bpb = (struct fat32_bootsec *)cp;
1218 
1219 	if (pcfsdebuglevel >= 5) {
1220 		if (ltohs(bpb->f_bs.rdirents[0]) != 0) {
1221 			(void) memcpy(FileSysType, &cp[54], 8);
1222 			FileSysType[8] = 0;
1223 			PC_DPRINTF1(5, "debug_bpb: FAT12/FAT16 FileSysType = "
1224 				"%s", FileSysType);
1225 		}
1226 	}
1227 
1228 	rootdirsectors = ((ltohs(bpb->f_bs.rdirents[0]) * 32) +
1229 		(ltohs(bpb->f_bs.bps[0]) - 1)) / ltohs(bpb->f_bs.bps[0]);
1230 
1231 	if (ltohs(bpb->f_bs.fatsec) != 0)
1232 		FATsz = ltohs(bpb->f_bs.fatsec);
1233 	else
1234 		FATsz = ltohi(bpb->f_fatlength);
1235 
1236 	if (ltohs(bpb->f_bs.numsect[0]) != 0)
1237 		TotSec = ltohs(bpb->f_bs.numsect[0]);
1238 	else
1239 		TotSec = ltohi(bpb->f_bs.totalsec);
1240 
1241 	DataSec = TotSec - (ltohs(bpb->f_bs.res_sec[0]) +
1242 			(bpb->f_bs.nfat * FATsz) + rootdirsectors);
1243 
1244 	CountOfClusters = DataSec / bpb->f_bs.spcl;
1245 
1246 	PC_DPRINTF1(5, "debug_bpb: CountOfClusters = %ld", CountOfClusters);
1247 
1248 	return (CountOfClusters);
1249 
1250 }
1251 
1252 static int
1253 fattype(ulong_t CountOfClusters)
1254 {
1255 	/*
1256 	 * From Microsoft:
1257 	 * In the following example, when it says <, it does not mean <=.
1258 	 * Note also that the numbers are correct.  The first number for
1259 	 * FAT12 is 4085; the second number for FAT16 is 65525. These numbers
1260 	 * and the '<' signs are not wrong.
1261 	 */
1262 
1263 	/* Watch for edge cases */
1264 	if ((CountOfClusters >= 4085 && CountOfClusters <= 4095) ||
1265 	    (CountOfClusters >= 65525 && CountOfClusters <= 65535)) {
1266 		PC_DPRINTF1(5, "debug_bpb: Cannot determine FAT yet - %ld",
1267 			CountOfClusters);
1268 		return (-1); /* Cannot be determined yet */
1269 	} else if (CountOfClusters < 4085) {
1270 		/* Volume is FAT12 */
1271 		PC_DPRINTF0(5, "debug_bpb: This must be FAT12");
1272 		return (0);
1273 	} else if (CountOfClusters < 65525) {
1274 		/* Volume is FAT16 */
1275 		PC_DPRINTF0(5, "debug_bpb: This must be FAT16");
1276 		return (PCFS_FAT16);
1277 	} else {
1278 		/* Volume is FAT32 */
1279 		PC_DPRINTF0(5, "debug_bpb: This must be FAT32");
1280 		return (PCFS_FAT32);
1281 	}
1282 }
1283 
1284 #define	VALID_SECSIZE(s) (s == 512 || s == 1024 || s == 2048 || s == 4096)
1285 
1286 #define	VALID_SPCL(s) (s == 1 || s == 2 || s == 4 || s == 8 || s == 16 ||\
1287 	s == 32 || s == 64 || s == 128)
1288 
1289 static int
1290 secondaryBPBChecks(uchar_t *cp)
1291 {
1292 	struct bootsec *bpb = (struct bootsec *)cp;
1293 	struct fat32_bootsec *f32bpb = (struct fat32_bootsec *)cp;
1294 
1295 	/*
1296 	 * Perform secondary checks to try and determine what sort
1297 	 * of FAT partition we have based on other, less reliable,
1298 	 * data in the BPB header.
1299 	 */
1300 	if (ltohs(bpb->fatsec) != 0) {
1301 		/*
1302 		 * Must be FAT12 or FAT16, check the
1303 		 * FilSysType string (not 100% reliable).
1304 		 */
1305 		if (!memcmp((cp + PCFS_TYPESTRING_OFFSET16), "FAT12", 5)) {
1306 			PC_DPRINTF0(5, "secondaryBPBCheck says: FAT12");
1307 			return (0); /* FAT12 */
1308 		} else if (!memcmp((cp + PCFS_TYPESTRING_OFFSET16), "FAT16",
1309 			5)) {
1310 			PC_DPRINTF0(5, "secondaryBPBCheck says: FAT16");
1311 			return (PCFS_FAT16);
1312 		} else {
1313 			/*
1314 			 * Try to use the BPB_Media byte
1315 			 *
1316 			 *  If the media byte indicates a floppy we'll
1317 			 *  assume FAT12, otherwise we'll assume FAT16.
1318 			 */
1319 			switch (bpb->mediadesriptor) {
1320 				case SS8SPT:
1321 				case DS8SPT:
1322 				case SS9SPT:
1323 				case DS9SPT:
1324 				case DS18SPT:
1325 				case DS9_15SPT:
1326 					PC_DPRINTF0(5,
1327 					"secondaryBPBCheck says: FAT12");
1328 					return (0); /* FAT12 */
1329 				case MD_FIXED:
1330 					PC_DPRINTF0(5,
1331 					"secondaryBPBCheck says: FAT16");
1332 					return (PCFS_FAT16);
1333 				default:
1334 					cmn_err(CE_NOTE,
1335 						"!pcfs: unknown FAT type");
1336 					return (-1);
1337 			}
1338 		}
1339 	} else if (ltohi(f32bpb->f_fatlength) > 0) {
1340 		PC_DPRINTF0(5, "secondaryBPBCheck says: FAT32");
1341 		return (PCFS_FAT32);
1342 	} else {
1343 		/* We don't know */
1344 		PC_DPRINTF0(5, "secondaryBPBCheck says: unknown!!");
1345 		return (-1);
1346 	}
1347 }
1348 
1349 /*
1350  * Check to see if the BPB we found is correct.
1351  *
1352  * First, look for obvious, tell-tale signs of trouble:
1353  * The NumFATs value should always be 2.  Sometimes it can be a '1'
1354  * on FLASH memory cards and other non-disk-based media, so we
1355  * will allow that as well.
1356  *
1357  * We also look at the Media byte, the valid range is 0xF0, or
1358  * 0xF8 thru 0xFF, anything else means this is probably not a good
1359  * BPB.
1360  *
1361  * Finally, check the BPB Magic number at the end of the 512 byte
1362  * block, it must be 0xAA55.
1363  *
1364  * If that all is good, calculate the number of clusters and
1365  * do some final verification steps.
1366  *
1367  * If all is well, return success (1) and set the fattypep value to the
1368  * correct FAT value if the caller provided a pointer to store it in.
1369  */
1370 static int
1371 isBPB(uchar_t *cp, int *fattypep)
1372 {
1373 	struct bootsec *bpb = (struct bootsec *)cp;
1374 	int type;
1375 
1376 	uint_t numclusters;		/* number of clusters in file area */
1377 	ushort_t secsize = (int)ltohs(bpb->bps[0]);
1378 
1379 	if (pcfsdebuglevel >= 3) {
1380 		if (!VALID_SECSIZE(secsize))
1381 			PC_DPRINTF1(3, "check_bpb: invalid bps value %d",
1382 				secsize);
1383 
1384 		if (!VALID_SPCL(bpb->spcl))
1385 			PC_DPRINTF1(3, "check_bpb: invalid spcl value %d",
1386 				bpb->spcl);
1387 
1388 		if ((secsize * bpb->spcl) >= (32 * 1024))
1389 			PC_DPRINTF3(3, "check_bpb: BPC > 32K  %d x %d = %d",
1390 				secsize,
1391 				bpb->spcl,
1392 				secsize * bpb->spcl);
1393 
1394 		if (bpb->nfat == 0)
1395 			PC_DPRINTF1(3, "check_bpb: bad NumFATs value %d",
1396 				bpb->nfat);
1397 
1398 		if (ltohs(bpb->res_sec[0]) == 0)
1399 			PC_DPRINTF1(3, "check_bpb: bad RsvdSecCnt value %d",
1400 				ltohs(bpb->res_sec[0]));
1401 
1402 		PC_DPRINTF1(5, "check_bpb: Media byte = %02x",
1403 			bpb->mediadesriptor);
1404 
1405 	}
1406 	if ((bpb->nfat == 0) ||
1407 		(bpb->mediadesriptor != 0xF0 && bpb->mediadesriptor < 0xF8) ||
1408 		(ltohs(cp[510]) != MBB_MAGIC) ||
1409 		!VALID_SECSIZE(secsize) ||
1410 		!VALID_SPCL(bpb->spcl) ||
1411 		(secsize * bpb->spcl > (64 * 1024)) ||
1412 		!(ltohs(bpb->res_sec[0])))
1413 		return (0);
1414 
1415 	/*
1416 	 * Basic sanity checks passed so far, now try to determine which
1417 	 * FAT format to use.
1418 	 */
1419 	numclusters = bpb_to_numclusters(cp);
1420 
1421 	type = fattype(numclusters);
1422 
1423 	/* Do some final sanity checks for each specific type of FAT */
1424 	switch (type) {
1425 		case 0: /* FAT12 */
1426 		case PCFS_FAT16:
1427 			if (!check_bpb_fat16((struct bootsec *)cp))
1428 				return (0);
1429 			break;
1430 		case PCFS_FAT32:
1431 			if (!check_bpb_fat32((struct fat32_bootsec *)cp))
1432 				return (0);
1433 			break;
1434 		default: /* not sure yet */
1435 			type = secondaryBPBChecks(cp);
1436 			if (type == -1) {
1437 				/* Still nothing, give it up. */
1438 				return (0);
1439 			}
1440 			break;
1441 	}
1442 
1443 	if (fattypep)
1444 		*fattypep = type;
1445 
1446 	PC_DPRINTF0(5, "isBPB: BPB passes verification tests");
1447 	return (1);
1448 }
1449 
1450 
1451 /*
1452  * Get the FAT type for the DOS medium.
1453  *
1454  * -------------------------
1455  * According to Microsoft:
1456  *   The FAT type one of FAT12, FAT16, or FAT32 is determined by the
1457  * count of clusters on the volume and nothing else.
1458  * -------------------------
1459  *
1460  */
1461 static int
1462 pc_getfattype(
1463 	struct vnode *devvp,
1464 	int ldrive,
1465 	daddr_t *strtsectp,
1466 	int *fattypep)
1467 {
1468 	buf_t *bp = NULL;		/* Disk buffer pointer */
1469 	int err = 0;
1470 	uchar_t sysid = 0;		/* System ID character */
1471 	dev_t	dev = devvp->v_rdev;
1472 
1473 
1474 	/*
1475 	 * Open the device so we can check out the BPB or FDISK table,
1476 	 * then read in the sector.
1477 	 */
1478 	PC_DPRINTF2(5, "pc_getfattype: dev=%x  ldrive=%x  ", (int)dev, ldrive);
1479 	if (err = VOP_OPEN(&devvp, FREAD, CRED())) {
1480 		PC_DPRINTF1(1, "pc_getfattype: open error=%d\n", err);
1481 		return (err);
1482 	}
1483 
1484 	/*
1485 	 * Unpartitioned media (floppies and some removeable devices)
1486 	 * don't have a partition table, the FAT BPB is at disk block 0.
1487 	 * Start out by reading block 0.
1488 	 */
1489 	*strtsectp = (daddr_t)0;
1490 	bp = bread(dev, *strtsectp, PC_SAFESECSIZE);
1491 
1492 	if (bp->b_flags & B_ERROR) {
1493 		PC_DPRINTF2(1, "pc_getfattype: read error on "
1494 		    "device %d, disk LBA %d\n", (int)dev, (int)*strtsectp);
1495 		err = EIO;
1496 		goto out;
1497 	}
1498 
1499 	/*
1500 	 * If a logical drive number is requested, parse the partition table
1501 	 * and attempt to locate it. Otherwise, proceed immediately to the
1502 	 * BPB check. findTheDrive(), if successful, returns the disk block
1503 	 * number where the requested partition starts in "strtsecp".
1504 	 */
1505 	if (ldrive != 0) {
1506 		PC_DPRINTF0(5, "pc_getfattype: using FDISK table to find BPB");
1507 
1508 		if (err = findTheDrive(dev, ldrive, &bp, strtsectp, &sysid))
1509 			goto out;
1510 
1511 		brelse(bp);
1512 		bp = bread(dev, *strtsectp, PC_SAFESECSIZE);
1513 		if (bp->b_flags & B_ERROR) {
1514 			PC_DPRINTF2(1, "pc_getfattype: read error on "
1515 			    "device %d, disk LBA %d\n",
1516 			    (int)dev, (int)*strtsectp);
1517 			err = EIO;
1518 			goto out;
1519 		}
1520 	}
1521 
1522 	if (!isBPB((uchar_t *)bp->b_un.b_addr, fattypep)) {
1523 		PC_DPRINTF2(1, "pc_getfattype: No FAT BPB on device %d, "
1524 		    "disk LBA %d\n", (int)dev, (int)*strtsectp);
1525 		err = EIO;
1526 		goto out;
1527 	}
1528 
1529 out:
1530 	/*
1531 	 * Release the buffer used
1532 	 */
1533 	if (bp != NULL)
1534 		brelse(bp);
1535 	(void) VOP_CLOSE(devvp, FREAD, 1, (offset_t)0, CRED());
1536 	return (err);
1537 }
1538 
1539 
1540 /*
1541  * Get the boot parameter block and file allocation table.
1542  * If there is an old FAT, invalidate it.
1543  */
1544 int
1545 pc_getfat(struct pcfs *fsp)
1546 {
1547 	struct vfs *vfsp = PCFSTOVFS(fsp);
1548 	struct buf *tp = 0;
1549 	struct buf *bp = 0;
1550 	uchar_t *fatp = NULL;
1551 	uchar_t *fat_changemap = NULL;
1552 	struct bootsec *bootp;
1553 	struct fat32_bootsec *f32b;
1554 	struct vnode *devvp;
1555 	int error;
1556 	int fatsize;
1557 	int fat_changemapsize;
1558 	int flags = 0;
1559 	int nfat;
1560 	int secsize;
1561 	int fatsec;
1562 
1563 	PC_DPRINTF0(5, "pc_getfat\n");
1564 	devvp = fsp->pcfs_devvp;
1565 	if (fsp->pcfs_fatp) {
1566 		/*
1567 		 * There is a FAT in core.
1568 		 * If there are open file pcnodes or we have modified it or
1569 		 * it hasn't timed out yet use the in core FAT.
1570 		 * Otherwise invalidate it and get a new one
1571 		 */
1572 #ifdef notdef
1573 		if (fsp->pcfs_frefs ||
1574 		    (fsp->pcfs_flags & PCFS_FATMOD) ||
1575 		    (gethrestime_sec() < fsp->pcfs_fattime)) {
1576 			return (0);
1577 		} else {
1578 			mutex_enter(&pcfslock);
1579 			pc_invalfat(fsp);
1580 			mutex_exit(&pcfslock);
1581 		}
1582 #endif /* notdef */
1583 		return (0);
1584 	}
1585 	/*
1586 	 * Open block device mounted on.
1587 	 */
1588 	error = VOP_OPEN(&devvp,
1589 	    (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
1590 	    CRED());
1591 	if (error) {
1592 		PC_DPRINTF1(1, "pc_getfat: open error=%d\n", error);
1593 		return (error);
1594 	}
1595 	/*
1596 	 * Get boot parameter block and check it for validity
1597 	 */
1598 	tp = bread(fsp->pcfs_xdev, fsp->pcfs_dosstart, PC_SAFESECSIZE);
1599 	if ((tp->b_flags & (B_ERROR | B_STALE)) ||
1600 	    !isBPB((uchar_t *)tp->b_un.b_addr, NULL)) {
1601 		PC_DPRINTF2(1, "pc_getfat: boot block error on device %d, "
1602 		    "disk LBA %d\n",
1603 		    (int)fsp->pcfs_xdev, (int)fsp->pcfs_dosstart);
1604 		flags = tp->b_flags & B_ERROR;
1605 		error = EIO;
1606 		goto out;
1607 	}
1608 	tp->b_flags |= B_STALE | B_AGE;
1609 	bootp = (struct bootsec *)tp->b_un.b_addr;
1610 
1611 
1612 	/* get the sector size - may be more than 512 bytes */
1613 	secsize = (int)ltohs(bootp->bps[0]);
1614 	/* check for bogus sector size - fat should be at least 1 sector */
1615 	if (IS_FAT32(fsp)) {
1616 		f32b = (struct fat32_bootsec *)bootp;
1617 		fatsec = ltohi(f32b->f_fatlength);
1618 	} else {
1619 		fatsec = ltohs(bootp->fatsec);
1620 	}
1621 	if (secsize < 512 || fatsec < 1 || bootp->nfat < 1) {
1622 		cmn_err(CE_NOTE, "!pcfs: FAT size error");
1623 		error = EINVAL;
1624 		goto out;
1625 	}
1626 
1627 	switch (bootp->mediadesriptor) {
1628 	default:
1629 		cmn_err(CE_NOTE, "!pcfs: media-descriptor error, 0x%x",
1630 		    bootp->mediadesriptor);
1631 		error = EINVAL;
1632 		goto out;
1633 
1634 	case MD_FIXED:
1635 		/*
1636 		 * PCMCIA pseudo floppy is type MD_FIXED,
1637 		 * but is accessed like a floppy
1638 		 */
1639 		if (!(fsp->pcfs_flags & PCFS_PCMCIA_NO_CIS)) {
1640 			fsp->pcfs_flags |= PCFS_NOCHK;
1641 		}
1642 		/* FALLTHRU */
1643 	case SS8SPT:
1644 	case DS8SPT:
1645 	case SS9SPT:
1646 	case DS9SPT:
1647 	case DS18SPT:
1648 	case DS9_15SPT:
1649 		fsp->pcfs_secsize = secsize;
1650 		fsp->pcfs_sdshift = secsize / DEV_BSIZE - 1;
1651 		fsp->pcfs_entps = secsize / sizeof (struct pcdir);
1652 		fsp->pcfs_spcl = (int)bootp->spcl;
1653 		fsp->pcfs_fatsec = fatsec;
1654 		fsp->pcfs_spt = (int)ltohs(bootp->spt);
1655 		fsp->pcfs_rdirsec = ((int)ltohs(bootp->rdirents[0])
1656 		    * sizeof (struct pcdir) + (secsize - 1)) / secsize;
1657 		fsp->pcfs_clsize = fsp->pcfs_spcl * secsize;
1658 		fsp->pcfs_fatstart = fsp->pcfs_dosstart +
1659 		    (daddr_t)ltohs(bootp->res_sec[0]);
1660 		fsp->pcfs_rdirstart = fsp->pcfs_fatstart +
1661 		    (bootp->nfat * fsp->pcfs_fatsec);
1662 		fsp->pcfs_datastart = fsp->pcfs_rdirstart + fsp->pcfs_rdirsec;
1663 		if (IS_FAT32(fsp))
1664 			fsp->pcfs_rdirstart = ltohi(f32b->f_rootcluster);
1665 		fsp->pcfs_ncluster = (((int)(ltohs(bootp->numsect[0]) ?
1666 		    ltohs(bootp->numsect[0]) : ltohi(bootp->totalsec))) -
1667 		    fsp->pcfs_datastart + fsp->pcfs_dosstart) / fsp->pcfs_spcl;
1668 		fsp->pcfs_numfat = (int)bootp->nfat;
1669 		fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
1670 		break;
1671 	}
1672 
1673 	/*
1674 	 * Get FAT and check it for validity
1675 	 */
1676 	fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
1677 	fatp = kmem_alloc(fatsize, KM_SLEEP);
1678 	error = pc_readfat(fsp, fatp, fsp->pcfs_fatstart, fatsize);
1679 	if (error) {
1680 		flags = B_ERROR;
1681 		goto out;
1682 	}
1683 	fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1;
1684 	fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP);
1685 
1686 	/*
1687 	 * The only definite signature check is that the
1688 	 * media descriptor byte should match the first byte
1689 	 * of the FAT block.
1690 	 */
1691 	if (fatp[0] != bootp->mediadesriptor) {
1692 		cmn_err(CE_NOTE, "!pcfs: FAT signature error");
1693 		error = EINVAL;
1694 		goto out;
1695 	}
1696 	/*
1697 	 * Checking for fatsec and number of supported clusters, should
1698 	 * actually determine a FAT12/FAT media.
1699 	 */
1700 	if (fsp->pcfs_flags & PCFS_FAT16) {
1701 		if ((fsp->pcfs_fatsec <= 12) &&
1702 		    ((fatsize * 2 / 3) >= fsp->pcfs_ncluster)) {
1703 			/*
1704 			 * We have a 12-bit FAT, rather than a 16-bit FAT.
1705 			 * Ignore what the fdisk table says.
1706 			 */
1707 			PC_DPRINTF0(2, "pc_getfattype: forcing 12-bit FAT\n");
1708 			fsp->pcfs_flags &= ~PCFS_FAT16;
1709 		}
1710 	}
1711 	/*
1712 	 * Sanity check our FAT is large enough for the
1713 	 * clusters we think we have.
1714 	 */
1715 	if ((fsp->pcfs_flags & PCFS_FAT16) &&
1716 	    ((fatsize / 2) < fsp->pcfs_ncluster)) {
1717 		cmn_err(CE_NOTE, "!pcfs: FAT too small for number of clusters");
1718 		error = EINVAL;
1719 		goto out;
1720 	}
1721 
1722 	/*
1723 	 * Get alternate FATs and check for consistency
1724 	 * This is an inlined version of pc_readfat().
1725 	 * Since we're only comparing FAT and alternate FAT,
1726 	 * there's no reason to let pc_readfat() copy data out
1727 	 * of the buf. Instead, compare in-situ, one cluster
1728 	 * at a time.
1729 	 */
1730 	for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) {
1731 		size_t startsec;
1732 		size_t off;
1733 
1734 		startsec = fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec;
1735 
1736 		for (off = 0; off < fatsize; off += fsp->pcfs_clsize) {
1737 			bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp,
1738 				startsec +
1739 				pc_cltodb(fsp, pc_lblkno(fsp, off))),
1740 				MIN(fsp->pcfs_clsize, fatsize - off));
1741 			if (bp->b_flags & (B_ERROR | B_STALE)) {
1742 				cmn_err(CE_NOTE,
1743 					"!pcfs: alternate FAT #%d read error"
1744 					" at byte %ld", nfat, off);
1745 				flags = B_ERROR;
1746 				error = EIO;
1747 				goto out;
1748 			}
1749 			bp->b_flags |= B_STALE | B_AGE;
1750 			if (bcmp(bp->b_un.b_addr,
1751 			    fatp + off,
1752 			    MIN(fsp->pcfs_clsize, fatsize - off))) {
1753 				cmn_err(CE_NOTE,
1754 					"!pcfs: alternate FAT #%d corrupted"
1755 					" at byte %ld", nfat, off);
1756 				flags = B_ERROR;
1757 			}
1758 			brelse(bp);
1759 			bp = NULL;	/* prevent double release */
1760 		}
1761 	}
1762 
1763 	fsp->pcfs_fatsize = fatsize;
1764 	fsp->pcfs_fatp = fatp;
1765 	fsp->pcfs_fat_changemapsize = fat_changemapsize;
1766 	fsp->pcfs_fat_changemap = fat_changemap;
1767 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
1768 	fsp->pcfs_fatjustread = 1;
1769 
1770 	brelse(tp);
1771 	tp = NULL;
1772 	if (IS_FAT32(fsp)) {
1773 		/* get fsinfo */
1774 		struct fat32_boot_fsinfo fsinfo_disk;
1775 
1776 		fsp->f32fsinfo_sector = ltohs(f32b->f_infosector);
1777 		tp = bread(fsp->pcfs_xdev,
1778 		    fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector),
1779 		    PC_SAFESECSIZE);
1780 		if (tp->b_flags & (B_ERROR | B_STALE)) {
1781 			cmn_err(CE_NOTE, "!pcfs: error reading fat32 fsinfo");
1782 			flags = tp->b_flags & B_ERROR;
1783 			brelse(tp);
1784 			tp = NULL;
1785 			error = EIO;
1786 			goto out;
1787 		}
1788 		tp->b_flags |= B_STALE | B_AGE;
1789 		bcopy((void *)(tp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
1790 		    &fsinfo_disk, sizeof (struct fat32_boot_fsinfo));
1791 		brelse(tp);
1792 		tp = NULL;
1793 
1794 		/* translated fields */
1795 		fsp->fsinfo_native.fs_signature =
1796 		    ltohi(fsinfo_disk.fs_signature);
1797 		fsp->fsinfo_native.fs_free_clusters =
1798 		    ltohi(fsinfo_disk.fs_free_clusters);
1799 		if (fsp->fsinfo_native.fs_signature != FAT32_FS_SIGN) {
1800 			cmn_err(CE_NOTE,
1801 			    "!pcfs: fat32 fsinfo signature mismatch.");
1802 			error = EINVAL;
1803 			goto out;
1804 		}
1805 	}
1806 
1807 	return (0);
1808 
1809 out:
1810 	cmn_err(CE_NOTE, "!pcfs: illegal disk format");
1811 	if (tp)
1812 		brelse(tp);
1813 	if (bp)
1814 		brelse(bp);
1815 	if (fatp)
1816 		kmem_free(fatp, fatsize);
1817 	if (fat_changemap)
1818 		kmem_free(fat_changemap, fat_changemapsize);
1819 
1820 	if (flags) {
1821 		pc_mark_irrecov(fsp);
1822 	}
1823 	(void) VOP_CLOSE(devvp, (vfsp->vfs_flag & VFS_RDONLY) ?
1824 	    FREAD : FREAD|FWRITE, 1, (offset_t)0, CRED());
1825 	return (error);
1826 }
1827 
1828 int
1829 pc_syncfat(struct pcfs *fsp)
1830 {
1831 	struct buf *bp;
1832 	int nfat;
1833 	int	error;
1834 	struct fat32_boot_fsinfo fsinfo_disk;
1835 
1836 	PC_DPRINTF0(7, "pcfs_syncfat\n");
1837 	if ((fsp->pcfs_fatp == (uchar_t *)0) ||
1838 	    !(fsp->pcfs_flags & PCFS_FATMOD))
1839 		return (0);
1840 	/*
1841 	 * write out all copies of FATs
1842 	 */
1843 	fsp->pcfs_flags &= ~PCFS_FATMOD;
1844 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
1845 	for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) {
1846 		error = pc_writefat(fsp,
1847 		    fsp->pcfs_fatstart + nfat*fsp->pcfs_fatsec);
1848 		if (error) {
1849 			pc_mark_irrecov(fsp);
1850 			return (EIO);
1851 		}
1852 	}
1853 	pc_clear_fatchanges(fsp);
1854 	PC_DPRINTF0(6, "pcfs_syncfat: wrote out FAT\n");
1855 	/* write out fsinfo */
1856 	if (IS_FAT32(fsp)) {
1857 		bp = bread(fsp->pcfs_xdev,
1858 		    fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector),
1859 		    PC_SAFESECSIZE);
1860 		if (bp->b_flags & (B_ERROR | B_STALE)) {
1861 			brelse(bp);
1862 			return (EIO);
1863 		}
1864 		bcopy((void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
1865 		    &fsinfo_disk, sizeof (struct fat32_boot_fsinfo));
1866 		/* translate fields */
1867 		fsinfo_disk.fs_free_clusters =
1868 		    htoli(fsp->fsinfo_native.fs_free_clusters);
1869 		fsinfo_disk.fs_next_cluster = (uint32_t)FSINFO_UNKNOWN;
1870 		bcopy(&fsinfo_disk,
1871 		    (void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
1872 		    sizeof (struct fat32_boot_fsinfo));
1873 		bwrite2(bp);
1874 		error = geterror(bp);
1875 		brelse(bp);
1876 		if (error) {
1877 			pc_mark_irrecov(fsp);
1878 			return (EIO);
1879 		}
1880 	}
1881 	return (0);
1882 }
1883 
1884 void
1885 pc_invalfat(struct pcfs *fsp)
1886 {
1887 	struct pcfs *xfsp;
1888 	int mount_cnt = 0;
1889 
1890 	PC_DPRINTF0(7, "pc_invalfat\n");
1891 	if (fsp->pcfs_fatp == (uchar_t *)0)
1892 		panic("pc_invalfat");
1893 	/*
1894 	 * Release FAT
1895 	 */
1896 	kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsize);
1897 	fsp->pcfs_fatp = NULL;
1898 	kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize);
1899 	fsp->pcfs_fat_changemap = NULL;
1900 	/*
1901 	 * Invalidate all the blocks associated with the device.
1902 	 * Not needed if stateless.
1903 	 */
1904 	for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt)
1905 		if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev)
1906 			mount_cnt++;
1907 
1908 	if (!mount_cnt)
1909 		binval(fsp->pcfs_xdev);
1910 	/*
1911 	 * close mounted device
1912 	 */
1913 	(void) VOP_CLOSE(fsp->pcfs_devvp,
1914 	    (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
1915 	    1, (offset_t)0, CRED());
1916 }
1917 
1918 void
1919 pc_badfs(struct pcfs *fsp)
1920 {
1921 	cmn_err(CE_WARN, "corrupted PC file system on dev %x.%x\n",
1922 	    getmajor(fsp->pcfs_devvp->v_rdev),
1923 	    getminor(fsp->pcfs_devvp->v_rdev));
1924 }
1925 
1926 /*
1927  * The problem with supporting NFS on the PCFS filesystem is that there
1928  * is no good place to keep the generation number. The only possible
1929  * place is inside a directory entry. There are a few words that we
1930  * don't use - they store NT & OS/2 attributes, and the creation/last access
1931  * time of the file - but it seems wrong to use them. In addition, directory
1932  * entries come and go. If a directory is removed completely, its directory
1933  * blocks are freed and the generation numbers are lost. Whereas in ufs,
1934  * inode blocks are dedicated for inodes, so the generation numbers are
1935  * permanently kept on the disk.
1936  */
1937 static int
1938 pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp)
1939 {
1940 	struct pcnode *pcp;
1941 	struct pc_fid *pcfid;
1942 	struct pcfs *fsp;
1943 	struct pcdir *ep;
1944 	daddr_t eblkno;
1945 	int eoffset;
1946 	struct buf *bp;
1947 	int error;
1948 	pc_cluster32_t	cn;
1949 
1950 	pcfid = (struct pc_fid *)fidp;
1951 	fsp = VFSTOPCFS(vfsp);
1952 
1953 	error = pc_lockfs(fsp, 0, 0);
1954 	if (error) {
1955 		*vpp = NULL;
1956 		return (error);
1957 	}
1958 
1959 	if (pcfid->pcfid_block == 0) {
1960 		pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
1961 		pcp->pc_flags |= PC_EXTERNAL;
1962 		*vpp = PCTOV(pcp);
1963 		pc_unlockfs(fsp);
1964 		return (0);
1965 	}
1966 	eblkno = pcfid->pcfid_block;
1967 	eoffset = pcfid->pcfid_offset;
1968 	if ((pc_dbtocl(fsp,
1969 	    eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) ||
1970 	    (eoffset > fsp->pcfs_clsize)) {
1971 		pc_unlockfs(fsp);
1972 		*vpp = NULL;
1973 		return (EINVAL);
1974 	}
1975 
1976 	if (eblkno >= fsp->pcfs_datastart || (eblkno-fsp->pcfs_rdirstart)
1977 	    < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
1978 		bp = bread(fsp->pcfs_xdev, eblkno, fsp->pcfs_clsize);
1979 	} else {
1980 		bp = bread(fsp->pcfs_xdev, eblkno,
1981 		    (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize);
1982 	}
1983 	if (bp->b_flags & (B_ERROR | B_STALE)) {
1984 		error = geterror(bp);
1985 		brelse(bp);
1986 		if (error)
1987 			pc_mark_irrecov(fsp);
1988 		*vpp = NULL;
1989 		pc_unlockfs(fsp);
1990 		return (error);
1991 	}
1992 	ep = (struct pcdir *)(bp->b_un.b_addr + eoffset);
1993 	/*
1994 	 * Ok, if this is a valid file handle that we gave out,
1995 	 * then simply ensuring that the creation time matches,
1996 	 * the entry has not been deleted, and it has a valid first
1997 	 * character should be enough.
1998 	 *
1999 	 * Unfortunately, verifying that the <blkno, offset> _still_
2000 	 * refers to a directory entry is not easy, since we'd have
2001 	 * to search _all_ directories starting from root to find it.
2002 	 * That's a high price to pay just in case somebody is forging
2003 	 * file handles. So instead we verify that as much of the
2004 	 * entry is valid as we can:
2005 	 *
2006 	 * 1. The starting cluster is 0 (unallocated) or valid
2007 	 * 2. It is not an LFN entry
2008 	 * 3. It is not hidden (unless mounted as such)
2009 	 * 4. It is not the label
2010 	 */
2011 	cn = pc_getstartcluster(fsp, ep);
2012 	/*
2013 	 * if the starting cluster is valid, but not valid according
2014 	 * to pc_validcl(), force it to be to simplify the following if.
2015 	 */
2016 	if (cn == 0)
2017 		cn = PCF_FIRSTCLUSTER;
2018 	if (IS_FAT32(fsp)) {
2019 		if (cn >= PCF_LASTCLUSTER32)
2020 			cn = PCF_FIRSTCLUSTER;
2021 	} else {
2022 		if (cn >= PCF_LASTCLUSTER)
2023 			cn = PCF_FIRSTCLUSTER;
2024 	}
2025 	if ((!pc_validcl(fsp, cn)) ||
2026 	    (PCDL_IS_LFN(ep)) ||
2027 	    (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) ||
2028 	    ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) {
2029 		bp->b_flags |= B_STALE | B_AGE;
2030 		brelse(bp);
2031 		pc_unlockfs(fsp);
2032 		return (EINVAL);
2033 	}
2034 	if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) &&
2035 	    (ep->pcd_filename[0] != PCD_ERASED) &&
2036 	    (pc_validchar(ep->pcd_filename[0]) ||
2037 		(ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) {
2038 		pcp = pc_getnode(fsp, eblkno, eoffset, ep);
2039 		pcp->pc_flags |= PC_EXTERNAL;
2040 		*vpp = PCTOV(pcp);
2041 	} else {
2042 		*vpp = NULL;
2043 	}
2044 	bp->b_flags |= B_STALE | B_AGE;
2045 	brelse(bp);
2046 	pc_unlockfs(fsp);
2047 	return (0);
2048 }
2049 
2050 /*
2051  * if device is a PCMCIA pseudo floppy, return 1
2052  * otherwise, return 0
2053  */
2054 static int
2055 pcfs_pseudo_floppy(dev_t rdev)
2056 {
2057 	int			error, err;
2058 	struct dk_cinfo		info;
2059 	ldi_handle_t		lh;
2060 	ldi_ident_t		li;
2061 
2062 	err = ldi_ident_from_mod(&modlinkage, &li);
2063 	if (err) {
2064 		PC_DPRINTF1(1,
2065 		    "pcfs_pseudo_floppy: ldi_ident_from_mod err=%d\n", err);
2066 		return (0);
2067 	}
2068 
2069 	err = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, CRED(), &lh, li);
2070 	ldi_ident_release(li);
2071 	if (err) {
2072 		PC_DPRINTF1(1,
2073 		    "pcfs_pseudo_floppy: ldi_open err=%d\n", err);
2074 		return (0);
2075 	}
2076 
2077 	/* return value stored in err is purposfully ignored */
2078 	error = ldi_ioctl(lh, DKIOCINFO, (intptr_t)&info, FKIOCTL,
2079 	    CRED(), &err);
2080 
2081 	err = ldi_close(lh, FREAD, CRED());
2082 	if (err != 0) {
2083 		PC_DPRINTF1(1,
2084 		    "pcfs_pseudo_floppy: ldi_close err=%d\n", err);
2085 		return (0);
2086 	}
2087 
2088 	if ((error == 0) && (info.dki_ctype == DKC_PCMCIA_MEM) &&
2089 		(info.dki_flags & DKI_PCMCIA_PFD))
2090 		return (1);
2091 	else
2092 		return (0);
2093 }
2094 
2095 /*
2096  * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about
2097  * a meg), so we can't bread() it all in at once. This routine reads a
2098  * fat a chunk at a time.
2099  */
2100 static int
2101 pc_readfat(struct pcfs *fsp, uchar_t *fatp, daddr_t start, size_t fatsize)
2102 {
2103 	struct buf *bp;
2104 	size_t off;
2105 	size_t readsize;
2106 
2107 	readsize = fsp->pcfs_clsize;
2108 	for (off = 0; off < fatsize; off += readsize, fatp += readsize) {
2109 		if (readsize > (fatsize - off))
2110 			readsize = fatsize - off;
2111 		bp = bread(fsp->pcfs_xdev,
2112 		    pc_dbdaddr(fsp, start +
2113 			pc_cltodb(fsp, pc_lblkno(fsp, off))),
2114 		    readsize);
2115 		if (bp->b_flags & (B_ERROR | B_STALE)) {
2116 			brelse(bp);
2117 			return (EIO);
2118 		}
2119 		bp->b_flags |= B_STALE | B_AGE;
2120 		bcopy(bp->b_un.b_addr, fatp, readsize);
2121 		brelse(bp);
2122 	}
2123 	return (0);
2124 }
2125 
2126 /*
2127  * We write the FAT out a _lot_, in order to make sure that it
2128  * is up-to-date. But on a FAT32 system (large drive, small clusters)
2129  * the FAT might be a couple of megabytes, and writing it all out just
2130  * because we created or deleted a small file is painful (especially
2131  * since we do it for each alternate FAT too). So instead, for FAT16 and
2132  * FAT32 we only write out the bit that has changed. We don't clear
2133  * the 'updated' fields here because the caller might be writing out
2134  * several FATs, so the caller must use pc_clear_fatchanges() after
2135  * all FATs have been updated.
2136  */
2137 static int
2138 pc_writefat(struct pcfs *fsp, daddr_t start)
2139 {
2140 	struct buf *bp;
2141 	size_t off;
2142 	size_t writesize;
2143 	int	error;
2144 	uchar_t *fatp = fsp->pcfs_fatp;
2145 	size_t fatsize = fsp->pcfs_fatsize;
2146 
2147 	writesize = fsp->pcfs_clsize;
2148 	for (off = 0; off < fatsize; off += writesize, fatp += writesize) {
2149 		if (writesize > (fatsize - off))
2150 			writesize = fatsize - off;
2151 		if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) {
2152 			continue;
2153 		}
2154 		bp = ngeteblk(writesize);
2155 		bp->b_edev = fsp->pcfs_xdev;
2156 		bp->b_dev = cmpdev(bp->b_edev);
2157 		bp->b_blkno = pc_dbdaddr(fsp, start +
2158 		    pc_cltodb(fsp, pc_lblkno(fsp, off)));
2159 		bcopy(fatp, bp->b_un.b_addr, writesize);
2160 		bwrite2(bp);
2161 		error = geterror(bp);
2162 		brelse(bp);
2163 		if (error) {
2164 			return (error);
2165 		}
2166 	}
2167 	return (0);
2168 }
2169 
2170 /*
2171  * Mark the FAT cluster that 'cn' is stored in as modified.
2172  */
2173 void
2174 pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn)
2175 {
2176 	pc_cluster32_t	bn;
2177 	size_t		size;
2178 
2179 	/* which fat block is the cluster number stored in? */
2180 	if (IS_FAT32(fsp)) {
2181 		size = sizeof (pc_cluster32_t);
2182 		bn = pc_lblkno(fsp, cn * size);
2183 		fsp->pcfs_fat_changemap[bn] = 1;
2184 	} else if (IS_FAT16(fsp)) {
2185 		size = sizeof (pc_cluster16_t);
2186 		bn = pc_lblkno(fsp, cn * size);
2187 		fsp->pcfs_fat_changemap[bn] = 1;
2188 	} else {
2189 		offset_t off;
2190 		pc_cluster32_t nbn;
2191 
2192 		ASSERT(IS_FAT12(fsp));
2193 		off = cn + (cn >> 1);
2194 		bn = pc_lblkno(fsp, off);
2195 		fsp->pcfs_fat_changemap[bn] = 1;
2196 		/* does this field wrap into the next fat cluster? */
2197 		nbn = pc_lblkno(fsp, off + 1);
2198 		if (nbn != bn) {
2199 			fsp->pcfs_fat_changemap[nbn] = 1;
2200 		}
2201 	}
2202 }
2203 
2204 /*
2205  * return whether the FAT cluster 'bn' is updated and needs to
2206  * be written out.
2207  */
2208 int
2209 pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn)
2210 {
2211 	return (fsp->pcfs_fat_changemap[bn] == 1);
2212 }
2213 
2214 /*
2215  * Implementation of VFS_FREEVFS() to support forced umounts.
2216  * This is called by the vfs framework after umount, to trigger
2217  * the release of any resources still associated with the given
2218  * vfs_t once the need to keep them has gone away.
2219  */
2220 void
2221 pcfs_freevfs(vfs_t *vfsp)
2222 {
2223 	struct pcfs *fsp = VFSTOPCFS(vfsp);
2224 
2225 	mutex_enter(&pcfslock);
2226 	if (fsp->pcfs_fatp != (uchar_t *)0)
2227 		pc_invalfat(fsp);
2228 	mutex_exit(&pcfslock);
2229 
2230 	VN_RELE(fsp->pcfs_devvp);
2231 	mutex_destroy(&fsp->pcfs_lock);
2232 	kmem_free(fsp, (uint_t)sizeof (struct pcfs));
2233 
2234 	/*
2235 	 * Allow _fini() to succeed now, if so desired.
2236 	 */
2237 	atomic_dec_32(&pcfs_mountcount);
2238 }
2239