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