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