xref: /titanic_52/usr/src/uts/common/fs/pcfs/pc_vfsops.c (revision 7950274e5799b56695a76f974ac361411ec2e515)
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,		0,
109 		NULL },
110 	{ MNTOPT_PCFS_HIDDEN,	hidden_cancel, NULL,		MO_DEFAULT,
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 			(void) 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 	struct bootsec *bpb = (struct bootsec *)cp;
1333 
1334 	uint_t numclusters;		/* number of clusters in file area */
1335 	ushort_t secsize = (int)ltohs(bpb->bps[0]);
1336 
1337 	if (pcfsdebuglevel >= 3) {
1338 		if (!VALID_SECSIZE(secsize))
1339 			PC_DPRINTF1(3, "check_bpb: invalid bps value %d",
1340 				secsize);
1341 
1342 		if (!VALID_SPCL(bpb->spcl))
1343 			PC_DPRINTF1(3, "check_bpb: invalid spcl value %d",
1344 				bpb->spcl);
1345 
1346 		if ((secsize * bpb->spcl) >= (32 * 1024))
1347 			PC_DPRINTF3(3, "check_bpb: BPC > 32K  %d x %d = %d",
1348 				secsize,
1349 				bpb->spcl,
1350 				secsize * bpb->spcl);
1351 
1352 		if (bpb->nfat == 0)
1353 			PC_DPRINTF1(3, "check_bpb: bad NumFATs value %d",
1354 				bpb->nfat);
1355 
1356 		if (ltohs(bpb->res_sec[0]) == 0)
1357 			PC_DPRINTF1(3, "check_bpb: bad RsvdSecCnt value %d",
1358 				ltohs(bpb->res_sec[0]));
1359 
1360 		PC_DPRINTF1(5, "check_bpb: Media byte = %02x",
1361 			bpb->mediadesriptor);
1362 
1363 	}
1364 	if ((bpb->nfat == 0) ||
1365 		(bpb->mediadesriptor != 0xF0 && bpb->mediadesriptor < 0xF8) ||
1366 		(ltohs(cp[510]) != MBB_MAGIC) ||
1367 		!VALID_SECSIZE(secsize) ||
1368 		!VALID_SPCL(bpb->spcl) ||
1369 		(secsize * bpb->spcl >= (64 * 1024)) ||
1370 		!(ltohs(bpb->res_sec[0])))
1371 		return (0);
1372 
1373 	/*
1374 	 * Basic sanity checks passed so far, now try to determine which
1375 	 * FAT format to use.
1376 	 */
1377 	numclusters = bpb_to_numclusters(cp);
1378 
1379 	*fattypep = fattype(numclusters);
1380 
1381 	/* Do some final sanity checks for each specific type of FAT */
1382 	switch (*fattypep) {
1383 		case 0: /* FAT12 */
1384 		case PCFS_FAT16:
1385 			if (!check_bpb_fat16((struct bootsec *)cp))
1386 				return (0);
1387 			break;
1388 		case PCFS_FAT32:
1389 			if (!check_bpb_fat32((struct fat32_bootsec *)cp))
1390 				return (0);
1391 			break;
1392 		default: /* not sure yet */
1393 			*fattypep = secondaryBPBChecks(cp);
1394 			if (*fattypep == -1) {
1395 				/* Still nothing, give it up. */
1396 				return (0);
1397 			}
1398 			break;
1399 	}
1400 	PC_DPRINTF0(5, "isBPB: BPB passes verification tests");
1401 	return (1);
1402 }
1403 
1404 
1405 /*
1406  * Get the FAT type for the DOS medium.
1407  *
1408  * -------------------------
1409  * According to Microsoft:
1410  *   The FAT type one of FAT12, FAT16, or FAT32 is determined by the
1411  * count of clusters on the volume and nothing else.
1412  * -------------------------
1413  *
1414  */
1415 static int
1416 pc_getfattype(
1417 	struct vnode *devvp,
1418 	int ldrive,
1419 	daddr_t *strtsectp,
1420 	int *fattypep)
1421 {
1422 	uchar_t *cp;			/* for searching out FAT string */
1423 	buf_t *bp = NULL;		/* Disk buffer pointer */
1424 	int rval = 0;
1425 	uchar_t sysid = 0;		/* System ID character */
1426 	dev_t	dev = devvp->v_rdev;
1427 
1428 	*strtsectp = (daddr_t)0;
1429 
1430 	/*
1431 	 * Open the device so we can check out the BPB or FDISK table,
1432 	 * then read in the sector.
1433 	 */
1434 	PC_DPRINTF2(5, "pc_getfattype: dev=%x  ldrive=%x  ", (int)dev, ldrive);
1435 	if (rval = VOP_OPEN(&devvp, FREAD, CRED())) {
1436 		PC_DPRINTF1(1, "pc_getfattype: open error=%d\n", rval);
1437 		return (rval);
1438 	}
1439 
1440 	/*
1441 	 *  Read block 0 from device
1442 	 */
1443 	bp = bread(dev, (daddr_t)0, PC_SAFESECSIZE);
1444 	if (bp->b_flags & B_ERROR) {
1445 		PC_DPRINTF0(1, "pc_getfattype: read error\n");
1446 		rval = EIO;
1447 		goto out;
1448 	}
1449 
1450 	cp = (uchar_t *)bp->b_un.b_addr;
1451 
1452 	/*
1453 	 * If the first block is not a valid BPB, look for the
1454 	 * through the FDISK table.
1455 	 */
1456 	if (!isBPB(cp, fattypep)) {
1457 		/* find the partition table and get 512 bytes from it. */
1458 		PC_DPRINTF0(5, "pc_getfattype: using FDISK table to find BPB");
1459 
1460 		if (findTheDrive(dev, ldrive, &rval, &bp,
1461 			strtsectp, &sysid) == 0)
1462 			goto out;
1463 
1464 		brelse(bp);
1465 		bp = bread(dev, *strtsectp, PC_SAFESECSIZE);
1466 		if (bp->b_flags & B_ERROR) {
1467 			PC_DPRINTF0(1, "pc_getfattype: read error\n");
1468 			rval = EIO;
1469 			goto out;
1470 		}
1471 		cp = (uchar_t *)bp->b_un.b_addr;
1472 
1473 		/* If this one is still no good, give it up. */
1474 		if (!isBPB(cp, fattypep)) {
1475 			rval = EIO;
1476 			goto out;
1477 		}
1478 	}
1479 
1480 out:
1481 	/*
1482 	 * Release the buffer used
1483 	 */
1484 	if (bp != NULL)
1485 		brelse(bp);
1486 	(void) VOP_CLOSE(devvp, FREAD, 1, (offset_t)0, CRED());
1487 	return (rval);
1488 }
1489 
1490 
1491 /*
1492  * Get the boot parameter block and file allocation table.
1493  * If there is an old FAT, invalidate it.
1494  */
1495 int
1496 pc_getfat(struct pcfs *fsp)
1497 {
1498 	struct vfs *vfsp = PCFSTOVFS(fsp);
1499 	struct buf *tp = 0;
1500 	struct buf *bp = 0;
1501 	uchar_t *fatp = NULL;
1502 	uchar_t *fat_changemap = NULL;
1503 	struct bootsec *bootp;
1504 	struct fat32_bootsec *f32b;
1505 	struct vnode *devvp;
1506 	int error;
1507 	int fatsize;
1508 	int fat_changemapsize;
1509 	int flags = 0;
1510 	int nfat;
1511 	int secsize;
1512 	int fatsec;
1513 
1514 	PC_DPRINTF0(5, "pc_getfat\n");
1515 	devvp = fsp->pcfs_devvp;
1516 	if (fsp->pcfs_fatp) {
1517 		/*
1518 		 * There is a FAT in core.
1519 		 * If there are open file pcnodes or we have modified it or
1520 		 * it hasn't timed out yet use the in core FAT.
1521 		 * Otherwise invalidate it and get a new one
1522 		 */
1523 #ifdef notdef
1524 		if (fsp->pcfs_frefs ||
1525 		    (fsp->pcfs_flags & PCFS_FATMOD) ||
1526 		    (gethrestime_sec() < fsp->pcfs_fattime)) {
1527 			return (0);
1528 		} else {
1529 			mutex_enter(&pcfslock);
1530 			pc_invalfat(fsp);
1531 			mutex_exit(&pcfslock);
1532 		}
1533 #endif /* notdef */
1534 		return (0);
1535 	}
1536 	/*
1537 	 * Open block device mounted on.
1538 	 */
1539 	error = VOP_OPEN(&devvp,
1540 	    (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
1541 	    CRED());
1542 	if (error) {
1543 		PC_DPRINTF1(1, "pc_getfat: open error=%d\n", error);
1544 		return (error);
1545 	}
1546 	/*
1547 	 * Get boot parameter block and check it for validity
1548 	 */
1549 	tp = bread(fsp->pcfs_xdev, fsp->pcfs_dosstart, PC_SAFESECSIZE);
1550 	if (tp->b_flags & (B_ERROR | B_STALE)) {
1551 		PC_DPRINTF0(1, "pc_getfat: boot block error\n");
1552 		flags = tp->b_flags & B_ERROR;
1553 		error = EIO;
1554 		goto out;
1555 	}
1556 	tp->b_flags |= B_STALE | B_AGE;
1557 	bootp = (struct bootsec *)tp->b_un.b_addr;
1558 
1559 
1560 	/* get the sector size - may be more than 512 bytes */
1561 	secsize = (int)ltohs(bootp->bps[0]);
1562 	/* check for bogus sector size - fat should be at least 1 sector */
1563 	if (IS_FAT32(fsp)) {
1564 		f32b = (struct fat32_bootsec *)bootp;
1565 		fatsec = ltohi(f32b->f_fatlength);
1566 	} else {
1567 		fatsec = ltohs(bootp->fatsec);
1568 	}
1569 	if (secsize < 512 || fatsec < 1 || bootp->nfat < 1) {
1570 		cmn_err(CE_NOTE, "!pcfs: FAT size error");
1571 		error = EINVAL;
1572 		goto out;
1573 	}
1574 
1575 	switch (bootp->mediadesriptor) {
1576 	default:
1577 		cmn_err(CE_NOTE, "!pcfs: media-descriptor error, 0x%x",
1578 		    bootp->mediadesriptor);
1579 		error = EINVAL;
1580 		goto out;
1581 
1582 	case MD_FIXED:
1583 		/*
1584 		 * PCMCIA pseudo floppy is type MD_FIXED,
1585 		 * but is accessed like a floppy
1586 		 */
1587 		if (!(fsp->pcfs_flags & PCFS_PCMCIA_NO_CIS)) {
1588 			fsp->pcfs_flags |= PCFS_NOCHK;
1589 		}
1590 		/* FALLTHRU */
1591 	case SS8SPT:
1592 	case DS8SPT:
1593 	case SS9SPT:
1594 	case DS9SPT:
1595 	case DS18SPT:
1596 	case DS9_15SPT:
1597 		fsp->pcfs_secsize = secsize;
1598 		fsp->pcfs_sdshift = secsize / DEV_BSIZE - 1;
1599 		fsp->pcfs_entps = secsize / sizeof (struct pcdir);
1600 		fsp->pcfs_spcl = (int)bootp->spcl;
1601 		fsp->pcfs_fatsec = fatsec;
1602 		fsp->pcfs_spt = (int)ltohs(bootp->spt);
1603 		fsp->pcfs_rdirsec = (int)ltohs(bootp->rdirents[0])
1604 		    * sizeof (struct pcdir) / secsize;
1605 		fsp->pcfs_clsize = fsp->pcfs_spcl * secsize;
1606 		fsp->pcfs_fatstart = fsp->pcfs_dosstart +
1607 		    (daddr_t)ltohs(bootp->res_sec[0]);
1608 		fsp->pcfs_rdirstart = fsp->pcfs_fatstart +
1609 		    (bootp->nfat * fsp->pcfs_fatsec);
1610 		fsp->pcfs_datastart = fsp->pcfs_rdirstart + fsp->pcfs_rdirsec;
1611 		if (IS_FAT32(fsp))
1612 			fsp->pcfs_rdirstart = ltohi(f32b->f_rootcluster);
1613 		fsp->pcfs_ncluster = (((int)(ltohs(bootp->numsect[0]) ?
1614 		    ltohs(bootp->numsect[0]) : ltohi(bootp->totalsec))) -
1615 		    fsp->pcfs_datastart + fsp->pcfs_dosstart) / fsp->pcfs_spcl;
1616 		fsp->pcfs_numfat = (int)bootp->nfat;
1617 		fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
1618 		break;
1619 	}
1620 
1621 	/*
1622 	 * Get FAT and check it for validity
1623 	 */
1624 	fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
1625 	fatp = kmem_alloc(fatsize, KM_SLEEP);
1626 	error = pc_readfat(fsp, fatp, fsp->pcfs_fatstart, fatsize);
1627 	if (error) {
1628 		flags = B_ERROR;
1629 		goto out;
1630 	}
1631 	fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1;
1632 	fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP);
1633 
1634 	/*
1635 	 * The only definite signature check is that the
1636 	 * media descriptor byte should match the first byte
1637 	 * of the FAT block.
1638 	 */
1639 	if (fatp[0] != bootp->mediadesriptor) {
1640 		cmn_err(CE_NOTE, "!pcfs: FAT signature error");
1641 		error = EINVAL;
1642 		goto out;
1643 	}
1644 	/*
1645 	 * Checking for fatsec and number of supported clusters, should
1646 	 * actually determine a FAT12/FAT media.
1647 	 */
1648 	if (fsp->pcfs_flags & PCFS_FAT16) {
1649 		if ((fsp->pcfs_fatsec <= 12) &&
1650 		    ((fatsize * 2 / 3) >= fsp->pcfs_ncluster)) {
1651 			/*
1652 			 * We have a 12-bit FAT, rather than a 16-bit FAT.
1653 			 * Ignore what the fdisk table says.
1654 			 */
1655 			PC_DPRINTF0(2, "pc_getfattype: forcing 12-bit FAT\n");
1656 			fsp->pcfs_flags &= ~PCFS_FAT16;
1657 		}
1658 	}
1659 	/*
1660 	 * Sanity check our FAT is large enough for the
1661 	 * clusters we think we have.
1662 	 */
1663 	if ((fsp->pcfs_flags & PCFS_FAT16) &&
1664 	    ((fatsize / 2) < fsp->pcfs_ncluster)) {
1665 		cmn_err(CE_NOTE, "!pcfs: FAT too small for number of clusters");
1666 		error = EINVAL;
1667 		goto out;
1668 	}
1669 
1670 	/*
1671 	 * Get alternate FATs and check for consistency
1672 	 * This is an inlined version of pc_readfat().
1673 	 * Since we're only comparing FAT and alternate FAT,
1674 	 * there's no reason to let pc_readfat() copy data out
1675 	 * of the buf. Instead, compare in-situ, one cluster
1676 	 * at a time.
1677 	 */
1678 	for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) {
1679 		size_t startsec;
1680 		size_t off;
1681 
1682 		startsec = fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec;
1683 
1684 		for (off = 0; off < fatsize; off += fsp->pcfs_clsize) {
1685 			bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp,
1686 				startsec +
1687 				pc_cltodb(fsp, pc_lblkno(fsp, off))),
1688 				MIN(fsp->pcfs_clsize, fatsize - off));
1689 			if (bp->b_flags & (B_ERROR | B_STALE)) {
1690 				cmn_err(CE_NOTE,
1691 					"!pcfs: alternate FAT #%d read error"
1692 					" at byte %ld", nfat, off);
1693 				flags = B_ERROR;
1694 				error = EIO;
1695 				goto out;
1696 			}
1697 			bp->b_flags |= B_STALE | B_AGE;
1698 			if (bcmp(bp->b_un.b_addr,
1699 			    fatp + off,
1700 			    MIN(fsp->pcfs_clsize, fatsize - off))) {
1701 				cmn_err(CE_NOTE,
1702 					"!pcfs: alternate FAT #%d corrupted"
1703 					" at byte %ld", nfat, off);
1704 				flags = B_ERROR;
1705 			}
1706 			brelse(bp);
1707 			bp = NULL;	/* prevent double release */
1708 		}
1709 	}
1710 
1711 	fsp->pcfs_fatsize = fatsize;
1712 	fsp->pcfs_fatp = fatp;
1713 	fsp->pcfs_fat_changemapsize = fat_changemapsize;
1714 	fsp->pcfs_fat_changemap = fat_changemap;
1715 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
1716 	fsp->pcfs_fatjustread = 1;
1717 
1718 	brelse(tp);
1719 	tp = NULL;
1720 	if (IS_FAT32(fsp)) {
1721 		/* get fsinfo */
1722 		struct fat32_boot_fsinfo fsinfo_disk;
1723 
1724 		fsp->f32fsinfo_sector = ltohs(f32b->f_infosector);
1725 		tp = bread(fsp->pcfs_xdev,
1726 		    fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector),
1727 		    PC_SAFESECSIZE);
1728 		if (tp->b_flags & (B_ERROR | B_STALE)) {
1729 			cmn_err(CE_NOTE, "!pcfs: error reading fat32 fsinfo");
1730 			flags = tp->b_flags & B_ERROR;
1731 			brelse(tp);
1732 			tp = NULL;
1733 			error = EIO;
1734 			goto out;
1735 		}
1736 		tp->b_flags |= B_STALE | B_AGE;
1737 		bcopy((void *)(tp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
1738 		    &fsinfo_disk, sizeof (struct fat32_boot_fsinfo));
1739 		brelse(tp);
1740 		tp = NULL;
1741 
1742 		/* translated fields */
1743 		fsp->fsinfo_native.fs_signature =
1744 		    ltohi(fsinfo_disk.fs_signature);
1745 		fsp->fsinfo_native.fs_free_clusters =
1746 		    ltohi(fsinfo_disk.fs_free_clusters);
1747 		if (fsp->fsinfo_native.fs_signature != FAT32_FS_SIGN) {
1748 			cmn_err(CE_NOTE,
1749 			    "!pcfs: fat32 fsinfo signature mismatch.");
1750 			error = EINVAL;
1751 			goto out;
1752 		}
1753 	}
1754 
1755 	return (0);
1756 
1757 out:
1758 	cmn_err(CE_NOTE, "!pcfs: illegal disk format");
1759 	if (tp)
1760 		brelse(tp);
1761 	if (bp)
1762 		brelse(bp);
1763 	if (fatp)
1764 		kmem_free(fatp, fatsize);
1765 	if (fat_changemap)
1766 		kmem_free(fat_changemap, fat_changemapsize);
1767 
1768 	if (flags) {
1769 		pc_mark_irrecov(fsp);
1770 	}
1771 	(void) VOP_CLOSE(devvp, (vfsp->vfs_flag & VFS_RDONLY) ?
1772 	    FREAD : FREAD|FWRITE, 1, (offset_t)0, CRED());
1773 	return (error);
1774 }
1775 
1776 int
1777 pc_syncfat(struct pcfs *fsp)
1778 {
1779 	struct buf *bp;
1780 	int nfat;
1781 	int	error;
1782 	struct fat32_boot_fsinfo fsinfo_disk;
1783 
1784 	PC_DPRINTF0(7, "pcfs_syncfat\n");
1785 	if ((fsp->pcfs_fatp == (uchar_t *)0) ||
1786 	    !(fsp->pcfs_flags & PCFS_FATMOD))
1787 		return (0);
1788 	/*
1789 	 * write out all copies of FATs
1790 	 */
1791 	fsp->pcfs_flags &= ~PCFS_FATMOD;
1792 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
1793 	for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) {
1794 		error = pc_writefat(fsp,
1795 		    fsp->pcfs_fatstart + nfat*fsp->pcfs_fatsec);
1796 		if (error) {
1797 			pc_mark_irrecov(fsp);
1798 			return (EIO);
1799 		}
1800 	}
1801 	pc_clear_fatchanges(fsp);
1802 	PC_DPRINTF0(6, "pcfs_syncfat: wrote out FAT\n");
1803 	/* write out fsinfo */
1804 	if (IS_FAT32(fsp)) {
1805 		bp = bread(fsp->pcfs_xdev,
1806 		    fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector),
1807 		    PC_SAFESECSIZE);
1808 		if (bp->b_flags & (B_ERROR | B_STALE)) {
1809 			brelse(bp);
1810 			return (EIO);
1811 		}
1812 		bcopy((void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
1813 		    &fsinfo_disk, sizeof (struct fat32_boot_fsinfo));
1814 		/* translate fields */
1815 		fsinfo_disk.fs_free_clusters =
1816 		    htoli(fsp->fsinfo_native.fs_free_clusters);
1817 		fsinfo_disk.fs_next_cluster = (uint32_t)FSINFO_UNKNOWN;
1818 		bcopy(&fsinfo_disk,
1819 		    (void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
1820 		    sizeof (struct fat32_boot_fsinfo));
1821 		bwrite2(bp);
1822 		error = geterror(bp);
1823 		brelse(bp);
1824 		if (error) {
1825 			pc_mark_irrecov(fsp);
1826 			return (EIO);
1827 		}
1828 	}
1829 	return (0);
1830 }
1831 
1832 void
1833 pc_invalfat(struct pcfs *fsp)
1834 {
1835 	struct pcfs *xfsp;
1836 	int mount_cnt = 0;
1837 
1838 	PC_DPRINTF0(7, "pc_invalfat\n");
1839 	if (fsp->pcfs_fatp == (uchar_t *)0)
1840 		panic("pc_invalfat");
1841 	/*
1842 	 * Release FAT
1843 	 */
1844 	kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsize);
1845 	fsp->pcfs_fatp = NULL;
1846 	kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize);
1847 	fsp->pcfs_fat_changemap = NULL;
1848 	/*
1849 	 * Invalidate all the blocks associated with the device.
1850 	 * Not needed if stateless.
1851 	 */
1852 	for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt)
1853 		if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev)
1854 			mount_cnt++;
1855 
1856 	if (!mount_cnt)
1857 		binval(fsp->pcfs_xdev);
1858 	/*
1859 	 * close mounted device
1860 	 */
1861 	(void) VOP_CLOSE(fsp->pcfs_devvp,
1862 	    (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
1863 	    1, (offset_t)0, CRED());
1864 }
1865 
1866 void
1867 pc_badfs(struct pcfs *fsp)
1868 {
1869 	cmn_err(CE_WARN, "corrupted PC file system on dev %x.%x\n",
1870 	    getmajor(fsp->pcfs_devvp->v_rdev),
1871 	    getminor(fsp->pcfs_devvp->v_rdev));
1872 }
1873 
1874 /*
1875  * The problem with supporting NFS on the PCFS filesystem is that there
1876  * is no good place to keep the generation number. The only possible
1877  * place is inside a directory entry. There are a few words that we
1878  * don't use - they store NT & OS/2 attributes, and the creation/last access
1879  * time of the file - but it seems wrong to use them. In addition, directory
1880  * entries come and go. If a directory is removed completely, its directory
1881  * blocks are freed and the generation numbers are lost. Whereas in ufs,
1882  * inode blocks are dedicated for inodes, so the generation numbers are
1883  * permanently kept on the disk.
1884  */
1885 static int
1886 pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp)
1887 {
1888 	struct pcnode *pcp;
1889 	struct pc_fid *pcfid;
1890 	struct pcfs *fsp;
1891 	struct pcdir *ep;
1892 	daddr_t eblkno;
1893 	int eoffset;
1894 	struct buf *bp;
1895 	int error;
1896 	pc_cluster32_t	cn;
1897 
1898 	pcfid = (struct pc_fid *)fidp;
1899 	fsp = VFSTOPCFS(vfsp);
1900 
1901 	error = pc_lockfs(fsp, 0, 0);
1902 	if (error) {
1903 		*vpp = NULL;
1904 		return (error);
1905 	}
1906 
1907 	if (pcfid->pcfid_block == 0) {
1908 		pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
1909 		pcp->pc_flags |= PC_EXTERNAL;
1910 		*vpp = PCTOV(pcp);
1911 		pc_unlockfs(fsp);
1912 		return (0);
1913 	}
1914 	eblkno = pcfid->pcfid_block;
1915 	eoffset = pcfid->pcfid_offset;
1916 	if ((pc_dbtocl(fsp,
1917 	    eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) ||
1918 	    (eoffset > fsp->pcfs_clsize)) {
1919 		pc_unlockfs(fsp);
1920 		*vpp = NULL;
1921 		return (EINVAL);
1922 	}
1923 
1924 	if (eblkno >= fsp->pcfs_datastart || (eblkno-fsp->pcfs_rdirstart)
1925 	    < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
1926 		bp = bread(fsp->pcfs_xdev, eblkno, fsp->pcfs_clsize);
1927 	} else {
1928 		bp = bread(fsp->pcfs_xdev, eblkno,
1929 		    (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize);
1930 	}
1931 	if (bp->b_flags & (B_ERROR | B_STALE)) {
1932 		error = geterror(bp);
1933 		brelse(bp);
1934 		if (error)
1935 			pc_mark_irrecov(fsp);
1936 		*vpp = NULL;
1937 		pc_unlockfs(fsp);
1938 		return (error);
1939 	}
1940 	ep = (struct pcdir *)(bp->b_un.b_addr + eoffset);
1941 	/*
1942 	 * Ok, if this is a valid file handle that we gave out,
1943 	 * then simply ensuring that the creation time matches,
1944 	 * the entry has not been deleted, and it has a valid first
1945 	 * character should be enough.
1946 	 *
1947 	 * Unfortunately, verifying that the <blkno, offset> _still_
1948 	 * refers to a directory entry is not easy, since we'd have
1949 	 * to search _all_ directories starting from root to find it.
1950 	 * That's a high price to pay just in case somebody is forging
1951 	 * file handles. So instead we verify that as much of the
1952 	 * entry is valid as we can:
1953 	 *
1954 	 * 1. The starting cluster is 0 (unallocated) or valid
1955 	 * 2. It is not an LFN entry
1956 	 * 3. It is not hidden (unless mounted as such)
1957 	 * 4. It is not the label
1958 	 */
1959 	cn = pc_getstartcluster(fsp, ep);
1960 	/*
1961 	 * if the starting cluster is valid, but not valid according
1962 	 * to pc_validcl(), force it to be to simplify the following if.
1963 	 */
1964 	if (cn == 0)
1965 		cn = PCF_FIRSTCLUSTER;
1966 	if (IS_FAT32(fsp)) {
1967 		if (cn >= PCF_LASTCLUSTER32)
1968 			cn = PCF_FIRSTCLUSTER;
1969 	} else {
1970 		if (cn >= PCF_LASTCLUSTER)
1971 			cn = PCF_FIRSTCLUSTER;
1972 	}
1973 	if ((!pc_validcl(fsp, cn)) ||
1974 	    (PCDL_IS_LFN(ep)) ||
1975 	    (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) ||
1976 	    ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) {
1977 		bp->b_flags |= B_STALE | B_AGE;
1978 		brelse(bp);
1979 		pc_unlockfs(fsp);
1980 		return (EINVAL);
1981 	}
1982 	if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) &&
1983 	    (ep->pcd_filename[0] != PCD_ERASED) &&
1984 	    (pc_validchar(ep->pcd_filename[0]) ||
1985 		(ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) {
1986 		pcp = pc_getnode(fsp, eblkno, eoffset, ep);
1987 		pcp->pc_flags |= PC_EXTERNAL;
1988 		*vpp = PCTOV(pcp);
1989 	} else {
1990 		*vpp = NULL;
1991 	}
1992 	bp->b_flags |= B_STALE | B_AGE;
1993 	brelse(bp);
1994 	pc_unlockfs(fsp);
1995 	return (0);
1996 }
1997 
1998 /*
1999  * if device is a PCMCIA pseudo floppy, return 1
2000  * otherwise, return 0
2001  */
2002 static int
2003 pcfs_pseudo_floppy(dev_t rdev)
2004 {
2005 	int			error, err;
2006 	struct dk_cinfo		info;
2007 	ldi_handle_t		lh;
2008 	ldi_ident_t		li;
2009 
2010 	err = ldi_ident_from_mod(&modlinkage, &li);
2011 	if (err) {
2012 		PC_DPRINTF1(1,
2013 		    "pcfs_pseudo_floppy: ldi_ident_from_mod err=%d\n", err);
2014 		return (0);
2015 	}
2016 
2017 	err = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, CRED(), &lh, li);
2018 	ldi_ident_release(li);
2019 	if (err) {
2020 		PC_DPRINTF1(1,
2021 		    "pcfs_pseudo_floppy: ldi_open err=%d\n", err);
2022 		return (0);
2023 	}
2024 
2025 	/* return value stored in err is purposfully ignored */
2026 	error = ldi_ioctl(lh, DKIOCINFO, (intptr_t)&info, FKIOCTL,
2027 	    CRED(), &err);
2028 
2029 	err = ldi_close(lh, FREAD, CRED());
2030 	if (err != 0) {
2031 		PC_DPRINTF1(1,
2032 		    "pcfs_pseudo_floppy: ldi_close err=%d\n", err);
2033 		return (0);
2034 	}
2035 
2036 	if ((error == 0) && (info.dki_ctype == DKC_PCMCIA_MEM) &&
2037 		(info.dki_flags & DKI_PCMCIA_PFD))
2038 		return (1);
2039 	else
2040 		return (0);
2041 }
2042 
2043 /*
2044  * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about
2045  * a meg), so we can't bread() it all in at once. This routine reads a
2046  * fat a chunk at a time.
2047  */
2048 static int
2049 pc_readfat(struct pcfs *fsp, uchar_t *fatp, daddr_t start, size_t fatsize)
2050 {
2051 	struct buf *bp;
2052 	size_t off;
2053 	size_t readsize;
2054 
2055 	readsize = fsp->pcfs_clsize;
2056 	for (off = 0; off < fatsize; off += readsize, fatp += readsize) {
2057 		if (readsize > (fatsize - off))
2058 			readsize = fatsize - off;
2059 		bp = bread(fsp->pcfs_xdev,
2060 		    pc_dbdaddr(fsp, start +
2061 			pc_cltodb(fsp, pc_lblkno(fsp, off))),
2062 		    readsize);
2063 		if (bp->b_flags & (B_ERROR | B_STALE)) {
2064 			brelse(bp);
2065 			return (EIO);
2066 		}
2067 		bp->b_flags |= B_STALE | B_AGE;
2068 		bcopy(bp->b_un.b_addr, fatp, readsize);
2069 		brelse(bp);
2070 	}
2071 	return (0);
2072 }
2073 
2074 /*
2075  * We write the FAT out a _lot_, in order to make sure that it
2076  * is up-to-date. But on a FAT32 system (large drive, small clusters)
2077  * the FAT might be a couple of megabytes, and writing it all out just
2078  * because we created or deleted a small file is painful (especially
2079  * since we do it for each alternate FAT too). So instead, for FAT16 and
2080  * FAT32 we only write out the bit that has changed. We don't clear
2081  * the 'updated' fields here because the caller might be writing out
2082  * several FATs, so the caller must use pc_clear_fatchanges() after
2083  * all FATs have been updated.
2084  */
2085 static int
2086 pc_writefat(struct pcfs *fsp, daddr_t start)
2087 {
2088 	struct buf *bp;
2089 	size_t off;
2090 	size_t writesize;
2091 	int	error;
2092 	uchar_t *fatp = fsp->pcfs_fatp;
2093 	size_t fatsize = fsp->pcfs_fatsize;
2094 
2095 	writesize = fsp->pcfs_clsize;
2096 	for (off = 0; off < fatsize; off += writesize, fatp += writesize) {
2097 		if (writesize > (fatsize - off))
2098 			writesize = fatsize - off;
2099 		if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) {
2100 			continue;
2101 		}
2102 		bp = ngeteblk(writesize);
2103 		bp->b_edev = fsp->pcfs_xdev;
2104 		bp->b_dev = cmpdev(bp->b_edev);
2105 		bp->b_blkno = pc_dbdaddr(fsp, start +
2106 		    pc_cltodb(fsp, pc_lblkno(fsp, off)));
2107 		bcopy(fatp, bp->b_un.b_addr, writesize);
2108 		bwrite2(bp);
2109 		error = geterror(bp);
2110 		brelse(bp);
2111 		if (error) {
2112 			return (error);
2113 		}
2114 	}
2115 	return (0);
2116 }
2117 
2118 /*
2119  * Mark the FAT cluster that 'cn' is stored in as modified.
2120  */
2121 void
2122 pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn)
2123 {
2124 	pc_cluster32_t	bn;
2125 	size_t		size;
2126 
2127 	/* which fat block is the cluster number stored in? */
2128 	if (IS_FAT32(fsp)) {
2129 		size = sizeof (pc_cluster32_t);
2130 		bn = pc_lblkno(fsp, cn * size);
2131 		fsp->pcfs_fat_changemap[bn] = 1;
2132 	} else if (IS_FAT16(fsp)) {
2133 		size = sizeof (pc_cluster16_t);
2134 		bn = pc_lblkno(fsp, cn * size);
2135 		fsp->pcfs_fat_changemap[bn] = 1;
2136 	} else {
2137 		offset_t off;
2138 		pc_cluster32_t nbn;
2139 
2140 		ASSERT(IS_FAT12(fsp));
2141 		off = cn + (cn >> 1);
2142 		bn = pc_lblkno(fsp, off);
2143 		fsp->pcfs_fat_changemap[bn] = 1;
2144 		/* does this field wrap into the next fat cluster? */
2145 		nbn = pc_lblkno(fsp, off + 1);
2146 		if (nbn != bn) {
2147 			fsp->pcfs_fat_changemap[nbn] = 1;
2148 		}
2149 	}
2150 }
2151 
2152 /*
2153  * return whether the FAT cluster 'bn' is updated and needs to
2154  * be written out.
2155  */
2156 int
2157 pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn)
2158 {
2159 	return (fsp->pcfs_fat_changemap[bn] == 1);
2160 }
2161