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