Home | History | Annotate | Download | only in pcfs
      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 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include <sys/param.h>
     28 #include <sys/systm.h>
     29 #include <sys/kmem.h>
     30 #include <sys/user.h>
     31 #include <sys/proc.h>
     32 #include <sys/cred.h>
     33 #include <sys/disp.h>
     34 #include <sys/buf.h>
     35 #include <sys/vfs.h>
     36 #include <sys/vfs_opreg.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 #include <sys/statvfs.h>
     43 #include <sys/mount.h>
     44 #include <sys/pathname.h>
     45 #include <sys/cmn_err.h>
     46 #include <sys/debug.h>
     47 #include <sys/sysmacros.h>
     48 #include <sys/conf.h>
     49 #include <sys/mkdev.h>
     50 #include <sys/swap.h>
     51 #include <sys/sunddi.h>
     52 #include <sys/sunldi.h>
     53 #include <sys/dktp/fdisk.h>
     54 #include <sys/fs/pc_label.h>
     55 #include <sys/fs/pc_fs.h>
     56 #include <sys/fs/pc_dir.h>
     57 #include <sys/fs/pc_node.h>
     58 #include <fs/fs_subr.h>
     59 #include <sys/modctl.h>
     60 #include <sys/dkio.h>
     61 #include <sys/open.h>
     62 #include <sys/mntent.h>
     63 #include <sys/policy.h>
     64 #include <sys/atomic.h>
     65 #include <sys/sdt.h>
     66 
     67 /*
     68  * The majority of PC media use a 512 sector size, but
     69  * occasionally you will run across a 1k sector size.
     70  * For media with a 1k sector size, fd_strategy() requires
     71  * the I/O size to be a 1k multiple; so when the sector size
     72  * is not yet known, always read 1k.
     73  */
     74 #define	PC_SAFESECSIZE	(PC_SECSIZE * 2)
     75 
     76 static int pcfs_pseudo_floppy(dev_t);
     77 
     78 static int pcfsinit(int, char *);
     79 static int pcfs_mount(struct vfs *, struct vnode *, struct mounta *,
     80 	struct cred *);
     81 static int pcfs_unmount(struct vfs *, int, struct cred *);
     82 static int pcfs_root(struct vfs *, struct vnode **);
     83 static int pcfs_statvfs(struct vfs *, struct statvfs64 *);
     84 static int pc_syncfsnodes(struct pcfs *);
     85 static int pcfs_sync(struct vfs *, short, struct cred *);
     86 static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp);
     87 static void pcfs_freevfs(vfs_t *vfsp);
     88 
     89 static int pc_readfat(struct pcfs *fsp, uchar_t *fatp);
     90 static int pc_writefat(struct pcfs *fsp, daddr_t start);
     91 
     92 static int pc_getfattype(struct pcfs *fsp);
     93 static void pcfs_parse_mntopts(struct pcfs *fsp);
     94 
     95 
     96 /*
     97  * pcfs mount options table
     98  */
     99 
    100 static char *nohidden_cancel[] = { MNTOPT_PCFS_HIDDEN, NULL };
    101 static char *hidden_cancel[] = { MNTOPT_PCFS_NOHIDDEN, NULL };
    102 static char *nofoldcase_cancel[] = { MNTOPT_PCFS_FOLDCASE, NULL };
    103 static char *foldcase_cancel[] = { MNTOPT_PCFS_NOFOLDCASE, NULL };
    104 static char *clamptime_cancel[] = { MNTOPT_PCFS_NOCLAMPTIME, NULL };
    105 static char *noclamptime_cancel[] = { MNTOPT_PCFS_CLAMPTIME, NULL };
    106 static char *atime_cancel[] = { MNTOPT_NOATIME, NULL };
    107 static char *noatime_cancel[] = { MNTOPT_ATIME, NULL };
    108 
    109 static mntopt_t mntopts[] = {
    110 /*
    111  *	option name	cancel option	default arg	flags	opt data
    112  */
    113 	{ MNTOPT_PCFS_NOHIDDEN, nohidden_cancel, NULL, 0, NULL },
    114 	{ MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, MO_DEFAULT, NULL },
    115 	{ MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, NULL },
    116 	{ MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0, NULL },
    117 	{ MNTOPT_PCFS_CLAMPTIME, clamptime_cancel, NULL, MO_DEFAULT, NULL },
    118 	{ MNTOPT_PCFS_NOCLAMPTIME, noclamptime_cancel, NULL, NULL, NULL },
    119 	{ MNTOPT_NOATIME, noatime_cancel, NULL, NULL, NULL },
    120 	{ MNTOPT_ATIME, atime_cancel, NULL, NULL, NULL },
    121 	{ MNTOPT_PCFS_TIMEZONE, NULL, "+0", MO_DEFAULT | MO_HASVALUE, NULL },
    122 	{ MNTOPT_PCFS_SECSIZE, NULL, NULL, MO_HASVALUE, NULL }
    123 };
    124 
    125 static mntopts_t pcfs_mntopts = {
    126 	sizeof (mntopts) / sizeof (mntopt_t),
    127 	mntopts
    128 };
    129 
    130 int pcfsdebuglevel = 0;
    131 
    132 /*
    133  * pcfslock:	protects the list of mounted pc filesystems "pc_mounttab.
    134  * pcfs_lock:	(inside per filesystem structure "pcfs")
    135  *		per filesystem lock. Most of the vfsops and vnodeops are
    136  *		protected by this lock.
    137  * pcnodes_lock: protects the pcnode hash table "pcdhead", "pcfhead".
    138  *
    139  * Lock hierarchy: pcfslock > pcfs_lock > pcnodes_lock
    140  *
    141  * pcfs_mountcount:	used to prevent module unloads while there is still
    142  *			pcfs state from a former mount hanging around. With
    143  *			forced umount support, the filesystem module must not
    144  *			be allowed to go away before the last VFS_FREEVFS()
    145  *			call has been made.
    146  *			Since this is just an atomic counter, there's no need
    147  *			for locking.
    148  */
    149 kmutex_t	pcfslock;
    150 krwlock_t	pcnodes_lock;
    151 uint32_t	pcfs_mountcount;
    152 
    153 static int pcfstype;
    154 
    155 static vfsdef_t vfw = {
    156 	VFSDEF_VERSION,
    157 	"pcfs",
    158 	pcfsinit,
    159 	VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS|VSW_CANLOFI,
    160 	&pcfs_mntopts
    161 };
    162 
    163 extern struct mod_ops mod_fsops;
    164 
    165 static struct modlfs modlfs = {
    166 	&mod_fsops,
    167 	"PC filesystem",
    168 	&vfw
    169 };
    170 
    171 static struct modlinkage modlinkage = {
    172 	MODREV_1,
    173 	&modlfs,
    174 	NULL
    175 };
    176 
    177 int
    178 _init(void)
    179 {
    180 	int	error;
    181 
    182 #if !defined(lint)
    183 	/* make sure the on-disk structures are sane */
    184 	ASSERT(sizeof (struct pcdir) == 32);
    185 	ASSERT(sizeof (struct pcdir_lfn) == 32);
    186 #endif
    187 	mutex_init(&pcfslock, NULL, MUTEX_DEFAULT, NULL);
    188 	rw_init(&pcnodes_lock, NULL, RW_DEFAULT, NULL);
    189 	error = mod_install(&modlinkage);
    190 	if (error) {
    191 		mutex_destroy(&pcfslock);
    192 		rw_destroy(&pcnodes_lock);
    193 	}
    194 	return (error);
    195 }
    196 
    197 int
    198 _fini(void)
    199 {
    200 	int	error;
    201 
    202 	/*
    203 	 * If a forcedly unmounted instance is still hanging around,
    204 	 * we cannot allow the module to be unloaded because that would
    205 	 * cause panics once the VFS framework decides it's time to call
    206 	 * into VFS_FREEVFS().
    207 	 */
    208 	if (pcfs_mountcount)
    209 		return (EBUSY);
    210 
    211 	error = mod_remove(&modlinkage);
    212 	if (error)
    213 		return (error);
    214 	mutex_destroy(&pcfslock);
    215 	rw_destroy(&pcnodes_lock);
    216 	/*
    217 	 * Tear down the operations vectors
    218 	 */
    219 	(void) vfs_freevfsops_by_type(pcfstype);
    220 	vn_freevnodeops(pcfs_fvnodeops);
    221 	vn_freevnodeops(pcfs_dvnodeops);
    222 	return (0);
    223 }
    224 
    225 int
    226 _info(struct modinfo *modinfop)
    227 {
    228 	return (mod_info(&modlinkage, modinfop));
    229 }
    230 
    231 /* ARGSUSED1 */
    232 static int
    233 pcfsinit(int fstype, char *name)
    234 {
    235 	static const fs_operation_def_t pcfs_vfsops_template[] = {
    236 		VFSNAME_MOUNT,		{ .vfs_mount = pcfs_mount },
    237 		VFSNAME_UNMOUNT,	{ .vfs_unmount = pcfs_unmount },
    238 		VFSNAME_ROOT,		{ .vfs_root = pcfs_root },
    239 		VFSNAME_STATVFS,	{ .vfs_statvfs = pcfs_statvfs },
    240 		VFSNAME_SYNC,		{ .vfs_sync = pcfs_sync },
    241 		VFSNAME_VGET,		{ .vfs_vget = pcfs_vget },
    242 		VFSNAME_FREEVFS,	{ .vfs_freevfs = pcfs_freevfs },
    243 		NULL,			NULL
    244 	};
    245 	int error;
    246 
    247 	error = vfs_setfsops(fstype, pcfs_vfsops_template, NULL);
    248 	if (error != 0) {
    249 		cmn_err(CE_WARN, "pcfsinit: bad vfs ops template");
    250 		return (error);
    251 	}
    252 
    253 	error = vn_make_ops("pcfs", pcfs_fvnodeops_template, &pcfs_fvnodeops);
    254 	if (error != 0) {
    255 		(void) vfs_freevfsops_by_type(fstype);
    256 		cmn_err(CE_WARN, "pcfsinit: bad file vnode ops template");
    257 		return (error);
    258 	}
    259 
    260 	error = vn_make_ops("pcfsd", pcfs_dvnodeops_template, &pcfs_dvnodeops);
    261 	if (error != 0) {
    262 		(void) vfs_freevfsops_by_type(fstype);
    263 		vn_freevnodeops(pcfs_fvnodeops);
    264 		cmn_err(CE_WARN, "pcfsinit: bad dir vnode ops template");
    265 		return (error);
    266 	}
    267 
    268 	pcfstype = fstype;
    269 	(void) pc_init();
    270 	pcfs_mountcount = 0;
    271 	return (0);
    272 }
    273 
    274 static struct pcfs *pc_mounttab = NULL;
    275 
    276 extern struct pcfs_args pc_tz;
    277 
    278 /*
    279  *  Define some special logical drives we use internal to this file.
    280  */
    281 #define	BOOT_PARTITION_DRIVE	99
    282 #define	PRIMARY_DOS_DRIVE	1
    283 #define	UNPARTITIONED_DRIVE	0
    284 
    285 static int
    286 pcfs_device_identify(
    287 	struct vfs *vfsp,
    288 	struct mounta *uap,
    289 	struct cred *cr,
    290 	int *dos_ldrive,
    291 	dev_t *xdev)
    292 {
    293 	struct pathname special;
    294 	char *c;
    295 	struct vnode *svp = NULL;
    296 	struct vnode *lvp = NULL;
    297 	int oflag, aflag;
    298 	int error;
    299 
    300 	/*
    301 	 * Resolve path name of special file being mounted.
    302 	 */
    303 	if (error = pn_get(uap->spec, UIO_USERSPACE, &special)) {
    304 		return (error);
    305 	}
    306 
    307 	*dos_ldrive = -1;
    308 
    309 	if (error =
    310 	    lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &svp)) {
    311 		/*
    312 		 * If there's no device node, the name specified most likely
    313 		 * maps to a PCFS-style "partition specifier" to select a
    314 		 * harddisk primary/logical partition. Disable floppy-specific
    315 		 * checks in such cases unless an explicit :A or :B is
    316 		 * requested.
    317 		 */
    318 
    319 		/*
    320 		 * Split the pathname string at the last ':' separator.
    321 		 * If there's no ':' in the device name, or the ':' is the
    322 		 * last character in the string, the name is invalid and
    323 		 * the error from the previous lookup will be returned.
    324 		 */
    325 		c = strrchr(special.pn_path, ':');
    326 		if (c == NULL || strlen(c) == 0)
    327 			goto devlookup_done;
    328 
    329 		*c++ = '\0';
    330 
    331 		/*
    332 		 * PCFS partition name suffixes can be:
    333 		 *	- "boot" to indicate the X86BOOT partition
    334 		 *	- a drive letter [c-z] for the "DOS logical drive"
    335 		 *	- a drive number 1..24 for the "DOS logical drive"
    336 		 *	- a "floppy name letter", 'a' or 'b' (just strip this)
    337 		 */
    338 		if (strcasecmp(c, "boot") == 0) {
    339 			/*
    340 			 * The Solaris boot partition is requested.
    341 			 */
    342 			*dos_ldrive = BOOT_PARTITION_DRIVE;
    343 		} else if (strspn(c, "0123456789") == strlen(c)) {
    344 			/*
    345 			 * All digits - parse the partition number.
    346 			 */
    347 			long drvnum = 0;
    348 
    349 			if ((error = ddi_strtol(c, NULL, 10, &drvnum)) == 0) {
    350 				/*
    351 				 * A number alright - in the allowed range ?
    352 				 */
    353 				if (drvnum > 24 || drvnum == 0)
    354 					error = ENXIO;
    355 			}
    356 			if (error)
    357 				goto devlookup_done;
    358 			*dos_ldrive = (int)drvnum;
    359 		} else if (strlen(c) == 1) {
    360 			/*
    361 			 * A single trailing character was specified.
    362 			 *	- [c-zC-Z] means a harddisk partition, and
    363 			 *	  we retrieve the partition number.
    364 			 *	- [abAB] means a floppy drive, so we swallow
    365 			 *	  the "drive specifier" and test later
    366 			 *	  whether the physical device is a floppy or
    367 			 *	  PCMCIA pseudofloppy (sram card).
    368 			 */
    369 			*c = tolower(*c);
    370 			if (*c == 'a' || *c == 'b') {
    371 				*dos_ldrive = UNPARTITIONED_DRIVE;
    372 			} else if (*c < 'c' || *c > 'z') {
    373 				error = ENXIO;
    374 				goto devlookup_done;
    375 			} else {
    376 				*dos_ldrive = 1 + *c - 'c';
    377 			}
    378 		} else {
    379 			/*
    380 			 * Can't parse this - pass through previous error.
    381 			 */
    382 			goto devlookup_done;
    383 		}
    384 
    385 
    386 		error = lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW,
    387 		    NULLVPP, &svp);
    388 	} else {
    389 		*dos_ldrive = UNPARTITIONED_DRIVE;
    390 	}
    391 devlookup_done:
    392 	pn_free(&special);
    393 	if (error)
    394 		return (error);
    395 
    396 	ASSERT(*dos_ldrive >= UNPARTITIONED_DRIVE);
    397 
    398 	/*
    399 	 * Verify caller's permission to open the device special file.
    400 	 */
    401 	if ((vfsp->vfs_flag & VFS_RDONLY) != 0 ||
    402 	    ((uap->flags & MS_RDONLY) != 0)) {
    403 		oflag = FREAD;
    404 		aflag = VREAD;
    405 	} else {
    406 		oflag = FREAD | FWRITE;
    407 		aflag = VREAD | VWRITE;
    408 	}
    409 
    410 	error = vfs_get_lofi(vfsp, &lvp);
    411 
    412 	if (error > 0) {
    413 		if (error == ENOENT)
    414 			error = ENODEV;
    415 		goto out;
    416 	} else if (error == 0) {
    417 		*xdev = lvp->v_rdev;
    418 	} else {
    419 		*xdev = svp->v_rdev;
    420 
    421 		if (svp->v_type != VBLK) {
    422 			error = ENOTBLK;
    423 			goto out;
    424 		}
    425 
    426 		if ((error = secpolicy_spec_open(cr, svp, oflag)) != 0)
    427 			goto out;
    428 	}
    429 
    430 	if (getmajor(*xdev) >= devcnt) {
    431 		error = ENXIO;
    432 		goto out;
    433 	}
    434 
    435 	if ((error = VOP_ACCESS(svp, aflag, 0, cr, NULL)) != 0)
    436 		goto out;
    437 
    438 out:
    439 	if (svp != NULL)
    440 		VN_RELE(svp);
    441 	if (lvp != NULL)
    442 		VN_RELE(lvp);
    443 	return (error);
    444 }
    445 
    446 static int
    447 pcfs_device_ismounted(
    448 	struct vfs *vfsp,
    449 	int dos_ldrive,
    450 	dev_t xdev,
    451 	int *remounting,
    452 	dev_t *pseudodev)
    453 {
    454 	struct pcfs *fsp;
    455 	int remount = *remounting;
    456 
    457 	/*
    458 	 * Ensure that this logical drive isn't already mounted, unless
    459 	 * this is a REMOUNT request.
    460 	 * Note: The framework will perform this check if the "...:c"
    461 	 * PCFS-style "logical drive" syntax has not been used and an
    462 	 * actually existing physical device is backing this filesystem.
    463 	 * Once all block device drivers support PC-style partitioning,
    464 	 * this codeblock can be dropped.
    465 	 */
    466 	*pseudodev = xdev;
    467 
    468 	if (dos_ldrive) {
    469 		mutex_enter(&pcfslock);
    470 		for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt)
    471 			if (fsp->pcfs_xdev == xdev &&
    472 			    fsp->pcfs_ldrive == dos_ldrive) {
    473 				mutex_exit(&pcfslock);
    474 				if (remount) {
    475 					return (0);
    476 				} else {
    477 					return (EBUSY);
    478 				}
    479 			}
    480 		/*
    481 		 * Assign a unique device number for the vfs
    482 		 * The old way (getudev() + a constantly incrementing
    483 		 * major number) was wrong because it changes vfs_dev
    484 		 * across mounts and reboots, which breaks nfs file handles.
    485 		 * UFS just uses the real dev_t. We can't do that because
    486 		 * of the way pcfs opens fdisk partitons (the :c and :d
    487 		 * partitions are on the same dev_t). Though that _might_
    488 		 * actually be ok, since the file handle contains an
    489 		 * absolute block number, it's probably better to make them
    490 		 * different. So I think we should retain the original
    491 		 * dev_t, but come up with a different minor number based
    492 		 * on the logical drive that will _always_ come up the same.
    493 		 * For now, we steal the upper 6 bits.
    494 		 */
    495 #ifdef notdef
    496 		/* what should we do here? */
    497 		if (((getminor(xdev) >> 12) & 0x3F) != 0)
    498 			printf("whoops - upper bits used!\n");
    499 #endif
    500 		*pseudodev = makedevice(getmajor(xdev),
    501 		    ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32);
    502 		if (vfs_devmounting(*pseudodev, vfsp)) {
    503 			mutex_exit(&pcfslock);
    504 			return (EBUSY);
    505 		}
    506 		if (vfs_devismounted(*pseudodev)) {
    507 			mutex_exit(&pcfslock);
    508 			if (remount) {
    509 				return (0);
    510 			} else {
    511 				return (EBUSY);
    512 			}
    513 		}
    514 		mutex_exit(&pcfslock);
    515 	} else {
    516 		*pseudodev = xdev;
    517 		if (vfs_devmounting(*pseudodev, vfsp)) {
    518 			return (EBUSY);
    519 		}
    520 		if (vfs_devismounted(*pseudodev))
    521 			if (remount) {
    522 				return (0);
    523 			} else {
    524 				return (EBUSY);
    525 			}
    526 	}
    527 
    528 	/*
    529 	 * This is not a remount. Even if MS_REMOUNT was requested,
    530 	 * the caller needs to proceed as it would on an ordinary
    531 	 * mount.
    532 	 */
    533 	*remounting = 0;
    534 
    535 	ASSERT(*pseudodev);
    536 	return (0);
    537 }
    538 
    539 /*
    540  * Get the PCFS-specific mount options from the VFS framework.
    541  * For "timezone" and "secsize", we need to parse the number
    542  * ourselves and ensure its validity.
    543  * Note: "secsize" is deliberately undocumented at this time,
    544  * it's a workaround for devices (particularly: lofi image files)
    545  * that don't support the DKIOCGMEDIAINFO ioctl for autodetection.
    546  */
    547 static void
    548 pcfs_parse_mntopts(struct pcfs *fsp)
    549 {
    550 	char *c;
    551 	char *endptr;
    552 	long l;
    553 	struct vfs *vfsp = fsp->pcfs_vfs;
    554 
    555 	ASSERT(fsp->pcfs_secondswest == 0);
    556 	ASSERT(fsp->pcfs_secsize == 0);
    557 
    558 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL))
    559 		fsp->pcfs_flags |= PCFS_HIDDEN;
    560 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL))
    561 		fsp->pcfs_flags |= PCFS_FOLDCASE;
    562 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL))
    563 		fsp->pcfs_flags |= PCFS_NOCLAMPTIME;
    564 	if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL))
    565 		fsp->pcfs_flags |= PCFS_NOATIME;
    566 
    567 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_TIMEZONE, &c)) {
    568 		if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
    569 		    endptr == c + strlen(c)) {
    570 			/*
    571 			 * A number alright - in the allowed range ?
    572 			 */
    573 			if (l <= -12*3600 || l >= 12*3600) {
    574 				cmn_err(CE_WARN, "!pcfs: invalid use of "
    575 				    "'timezone' mount option - %ld "
    576 				    "is out of range. Assuming 0.", l);
    577 				l = 0;
    578 			}
    579 		} else {
    580 			cmn_err(CE_WARN, "!pcfs: invalid use of "
    581 			    "'timezone' mount option - argument %s "
    582 			    "is not a valid number. Assuming 0.", c);
    583 			l = 0;
    584 		}
    585 		fsp->pcfs_secondswest = l;
    586 	}
    587 
    588 	/*
    589 	 * The "secsize=..." mount option is a workaround for the lack of
    590 	 * lofi(7d) support for DKIOCGMEDIAINFO. If PCFS wants to parse the
    591 	 * partition table of a disk image and it has been partitioned with
    592 	 * sector sizes other than 512 bytes, we'd fail on loopback'ed disk
    593 	 * images.
    594 	 * That should really be fixed in lofi ... this is a workaround.
    595 	 */
    596 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_SECSIZE, &c)) {
    597 		if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
    598 		    endptr == c + strlen(c)) {
    599 			/*
    600 			 * A number alright - a valid sector size as well ?
    601 			 */
    602 			if (!VALID_SECSIZE(l)) {
    603 				cmn_err(CE_WARN, "!pcfs: invalid use of "
    604 				    "'secsize' mount option - %ld is "
    605 				    "unsupported. Autodetecting.", l);
    606 				l = 0;
    607 			}
    608 		} else {
    609 			cmn_err(CE_WARN, "!pcfs: invalid use of "
    610 			    "'secsize' mount option - argument %s "
    611 			    "is not a valid number. Autodetecting.", c);
    612 			l = 0;
    613 		}
    614 		fsp->pcfs_secsize = l;
    615 		fsp->pcfs_sdshift = ddi_ffs(l / DEV_BSIZE) - 1;
    616 	}
    617 }
    618 
    619 /*
    620  * vfs operations
    621  */
    622 
    623 /*
    624  * pcfs_mount - backend for VFS_MOUNT() on PCFS.
    625  */
    626 static int
    627 pcfs_mount(
    628 	struct vfs *vfsp,
    629 	struct vnode *mvp,
    630 	struct mounta *uap,
    631 	struct cred *cr)
    632 {
    633 	struct pcfs *fsp;
    634 	struct vnode *devvp;
    635 	dev_t pseudodev;
    636 	dev_t xdev;
    637 	int dos_ldrive = 0;
    638 	int error;
    639 	int remounting;
    640 
    641 	if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
    642 		return (error);
    643 
    644 	if (mvp->v_type != VDIR)
    645 		return (ENOTDIR);
    646 
    647 	mutex_enter(&mvp->v_lock);
    648 	if ((uap->flags & MS_REMOUNT) == 0 &&
    649 	    (uap->flags & MS_OVERLAY) == 0 &&
    650 	    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
    651 		mutex_exit(&mvp->v_lock);
    652 		return (EBUSY);
    653 	}
    654 	mutex_exit(&mvp->v_lock);
    655 
    656 	/*
    657 	 * PCFS doesn't do mount arguments anymore - everything's a mount
    658 	 * option these days. In order not to break existing callers, we
    659 	 * don't reject it yet, just warn that the data (if any) is ignored.
    660 	 */
    661 	if (uap->datalen != 0)
    662 		cmn_err(CE_WARN, "!pcfs: deprecated use of mount(2) with "
    663 		    "mount argument structures instead of mount options. "
    664 		    "Ignoring mount(2) 'dataptr' argument.");
    665 
    666 	/*
    667 	 * This is needed early, to make sure the access / open calls
    668 	 * are done using the correct mode. Processing this mount option
    669 	 * only when calling pcfs_parse_mntopts() would lead us to attempt
    670 	 * a read/write access to a possibly writeprotected device, and
    671 	 * a readonly mount attempt might fail because of that.
    672 	 */
    673 	if (uap->flags & MS_RDONLY) {
    674 		vfsp->vfs_flag |= VFS_RDONLY;
    675 		vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
    676 	}
    677 
    678 	/*
    679 	 * For most filesystems, this is just a lookupname() on the
    680 	 * mount pathname string. PCFS historically has to do its own
    681 	 * partition table parsing because not all Solaris architectures
    682 	 * support all styles of partitioning that PC media can have, and
    683 	 * hence PCFS understands "device names" that don't map to actual
    684 	 * physical device nodes. Parsing the "PCFS syntax" for device
    685 	 * names is done in pcfs_device_identify() - see there.
    686 	 *
    687 	 * Once all block device drivers that can host FAT filesystems have
    688 	 * been enhanced to create device nodes for all PC-style partitions,
    689 	 * this code can go away.
    690 	 */
    691 	if (error = pcfs_device_identify(vfsp, uap, cr, &dos_ldrive, &xdev))
    692 		return (error);
    693 
    694 	/*
    695 	 * As with looking up the actual device to mount, PCFS cannot rely
    696 	 * on just the checks done by vfs_ismounted() whether a given device
    697 	 * is mounted already. The additional check against the "PCFS syntax"
    698 	 * is done in  pcfs_device_ismounted().
    699 	 */
    700 	remounting = (uap->flags & MS_REMOUNT);
    701 
    702 	if (error = pcfs_device_ismounted(vfsp, dos_ldrive, xdev, &remounting,
    703 	    &pseudodev))
    704 		return (error);
    705 
    706 	if (remounting)
    707 		return (0);
    708 
    709 	/*
    710 	 * Mount the filesystem.
    711 	 * An instance structure is required before the attempt to locate
    712 	 * and parse the FAT BPB. This is because mount options may change
    713 	 * the behaviour of the filesystem type matching code. Precreate
    714 	 * it and fill it in to a degree that allows parsing the mount
    715 	 * options.
    716 	 */
    717 	devvp = makespecvp(xdev, VBLK);
    718 	if (IS_SWAPVP(devvp)) {
    719 		VN_RELE(devvp);
    720 		return (EBUSY);
    721 	}
    722 	error = VOP_OPEN(&devvp,
    723 	    (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD | FWRITE, cr, NULL);
    724 	if (error) {
    725 		VN_RELE(devvp);
    726 		return (error);
    727 	}
    728 
    729 	fsp = kmem_zalloc(sizeof (*fsp), KM_SLEEP);
    730 	fsp->pcfs_vfs = vfsp;
    731 	fsp->pcfs_xdev = xdev;
    732 	fsp->pcfs_devvp = devvp;
    733 	fsp->pcfs_ldrive = dos_ldrive;
    734 	mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL);
    735 
    736 	pcfs_parse_mntopts(fsp);
    737 
    738 	/*
    739 	 * This is the actual "mount" - the PCFS superblock check.
    740 	 *
    741 	 * Find the requested logical drive and the FAT BPB therein.
    742 	 * Check device type and flag the instance if media is removeable.
    743 	 *
    744 	 * Initializes most members of the filesystem instance structure.
    745 	 * Returns EINVAL if no valid BPB can be found. Other errors may
    746 	 * occur after I/O failures, or when invalid / unparseable partition
    747 	 * tables are encountered.
    748 	 */
    749 	if (error = pc_getfattype(fsp))
    750 		goto errout;
    751 
    752 	/*
    753 	 * Now that the BPB has been parsed, this structural information
    754 	 * is available and known to be valid. Initialize the VFS.
    755 	 */
    756 	vfsp->vfs_data = fsp;
    757 	vfsp->vfs_dev = pseudodev;
    758 	vfsp->vfs_fstype = pcfstype;
    759 	vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype);
    760 	vfsp->vfs_bcount = 0;
    761 	vfsp->vfs_bsize = fsp->pcfs_clsize;
    762 
    763 	/*
    764 	 * Validate that we can access the FAT and that it is, to the
    765 	 * degree we can verify here, self-consistent.
    766 	 */
    767 	if (error = pc_verify(fsp))
    768 		goto errout;
    769 
    770 	/*
    771 	 * Record the time of the mount, to return as an "approximate"
    772 	 * timestamp for the FAT root directory. Since FAT roots don't
    773 	 * have timestamps, this is less confusing to the user than
    774 	 * claiming "zero" / Jan/01/1970.
    775 	 */
    776 	gethrestime(&fsp->pcfs_mounttime);
    777 
    778 	/*
    779 	 * Fix up the mount options. Because "noatime" is made default on
    780 	 * removeable media only, a fixed disk will have neither "atime"
    781 	 * nor "noatime" set. We set the options explicitly depending on
    782 	 * the PCFS_NOATIME flag, to inform the user of what applies.
    783 	 * Mount option cancellation will take care that the mutually
    784 	 * exclusive 'other' is cleared.
    785 	 */
    786 	vfs_setmntopt(vfsp,
    787 	    fsp->pcfs_flags & PCFS_NOATIME ? MNTOPT_NOATIME : MNTOPT_ATIME,
    788 	    NULL, 0);
    789 
    790 	/*
    791 	 * All clear - insert the FS instance into PCFS' list.
    792 	 */
    793 	mutex_enter(&pcfslock);
    794 	fsp->pcfs_nxt = pc_mounttab;
    795 	pc_mounttab = fsp;
    796 	mutex_exit(&pcfslock);
    797 	atomic_inc_32(&pcfs_mountcount);
    798 	return (0);
    799 
    800 errout:
    801 	(void) VOP_CLOSE(devvp,
    802 	    vfsp->vfs_flag & VFS_RDONLY ? FREAD : FREAD | FWRITE,
    803 	    1, (offset_t)0, cr, NULL);
    804 	VN_RELE(devvp);
    805 	mutex_destroy(&fsp->pcfs_lock);
    806 	kmem_free(fsp, sizeof (*fsp));
    807 	return (error);
    808 
    809 }
    810 
    811 static int
    812 pcfs_unmount(
    813 	struct vfs *vfsp,
    814 	int flag,
    815 	struct cred *cr)
    816 {
    817 	struct pcfs *fsp, *fsp1;
    818 
    819 	if (secpolicy_fs_unmount(cr, vfsp) != 0)
    820 		return (EPERM);
    821 
    822 	fsp = VFSTOPCFS(vfsp);
    823 
    824 	/*
    825 	 * We don't have to lock fsp because the VVFSLOCK in vfs layer will
    826 	 * prevent lookuppn from crossing the mount point.
    827 	 * If this is not a forced umount request and there's ongoing I/O,
    828 	 * don't allow the mount to proceed.
    829 	 */
    830 	if (flag & MS_FORCE)
    831 		vfsp->vfs_flag |= VFS_UNMOUNTED;
    832 	else if (fsp->pcfs_nrefs)
    833 		return (EBUSY);
    834 
    835 	mutex_enter(&pcfslock);
    836 
    837 	/*
    838 	 * If this is a forced umount request or if the fs instance has
    839 	 * been marked as beyond recovery, allow the umount to proceed
    840 	 * regardless of state. pc_diskchanged() forcibly releases all
    841 	 * inactive vnodes/pcnodes.
    842 	 */
    843 	if (flag & MS_FORCE || fsp->pcfs_flags & PCFS_IRRECOV) {
    844 		rw_enter(&pcnodes_lock, RW_WRITER);
    845 		pc_diskchanged(fsp);
    846 		rw_exit(&pcnodes_lock);
    847 	}
    848 
    849 	/* now there should be no pcp node on pcfhead or pcdhead. */
    850 
    851 	if (fsp == pc_mounttab) {
    852 		pc_mounttab = fsp->pcfs_nxt;
    853 	} else {
    854 		for (fsp1 = pc_mounttab; fsp1 != NULL; fsp1 = fsp1->pcfs_nxt)
    855 			if (fsp1->pcfs_nxt == fsp)
    856 				fsp1->pcfs_nxt = fsp->pcfs_nxt;
    857 	}
    858 
    859 	mutex_exit(&pcfslock);
    860 
    861 	/*
    862 	 * Since we support VFS_FREEVFS(), there's no need to
    863 	 * free the fsp right now. The framework will tell us
    864 	 * when the right time to do so has arrived by calling
    865 	 * into pcfs_freevfs.
    866 	 */
    867 	return (0);
    868 }
    869 
    870 /*
    871  * find root of pcfs
    872  */
    873 static int
    874 pcfs_root(
    875 	struct vfs *vfsp,
    876 	struct vnode **vpp)
    877 {
    878 	struct pcfs *fsp;
    879 	struct pcnode *pcp;
    880 	int error;
    881 
    882 	fsp = VFSTOPCFS(vfsp);
    883 	if (error = pc_lockfs(fsp, 0, 0))
    884 		return (error);
    885 
    886 	pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
    887 	pc_unlockfs(fsp);
    888 	*vpp = PCTOV(pcp);
    889 	pcp->pc_flags |= PC_EXTERNAL;
    890 	return (0);
    891 }
    892 
    893 /*
    894  * Get file system statistics.
    895  */
    896 static int
    897 pcfs_statvfs(
    898 	struct vfs *vfsp,
    899 	struct statvfs64 *sp)
    900 {
    901 	struct pcfs *fsp;
    902 	int error;
    903 	dev32_t d32;
    904 
    905 	fsp = VFSTOPCFS(vfsp);
    906 	error = pc_getfat(fsp);
    907 	if (error)
    908 		return (error);
    909 	bzero(sp, sizeof (*sp));
    910 	sp->f_bsize = sp->f_frsize = fsp->pcfs_clsize;
    911 	sp->f_blocks = (fsblkcnt64_t)fsp->pcfs_ncluster;
    912 	sp->f_bavail = sp->f_bfree = (fsblkcnt64_t)pc_freeclusters(fsp);
    913 	sp->f_files = (fsfilcnt64_t)-1;
    914 	sp->f_ffree = (fsfilcnt64_t)-1;
    915 	sp->f_favail = (fsfilcnt64_t)-1;
    916 #ifdef notdef
    917 	(void) cmpldev(&d32, fsp->pcfs_devvp->v_rdev);
    918 #endif /* notdef */
    919 	(void) cmpldev(&d32, vfsp->vfs_dev);
    920 	sp->f_fsid = d32;
    921 	(void) strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name);
    922 	sp->f_flag = vf_to_stf(vfsp->vfs_flag);
    923 	sp->f_namemax = PCMAXNAMLEN;
    924 	return (0);
    925 }
    926 
    927 static int
    928 pc_syncfsnodes(struct pcfs *fsp)
    929 {
    930 	struct pchead *hp;
    931 	struct pcnode *pcp;
    932 	int error;
    933 
    934 	if (error = pc_lockfs(fsp, 0, 0))
    935 		return (error);
    936 
    937 	if (!(error = pc_syncfat(fsp))) {
    938 		hp = pcfhead;
    939 		while (hp < & pcfhead [ NPCHASH ]) {
    940 			rw_enter(&pcnodes_lock, RW_READER);
    941 			pcp = hp->pch_forw;
    942 			while (pcp != (struct pcnode *)hp) {
    943 				if (VFSTOPCFS(PCTOV(pcp) -> v_vfsp) == fsp)
    944 					if (error = pc_nodesync(pcp))
    945 						break;
    946 				pcp = pcp -> pc_forw;
    947 			}
    948 			rw_exit(&pcnodes_lock);
    949 			if (error)
    950 				break;
    951 			hp++;
    952 		}
    953 	}
    954 	pc_unlockfs(fsp);
    955 	return (error);
    956 }
    957 
    958 /*
    959  * Flush any pending I/O.
    960  */
    961 /*ARGSUSED*/
    962 static int
    963 pcfs_sync(
    964 	struct vfs *vfsp,
    965 	short flag,
    966 	struct cred *cr)
    967 {
    968 	struct pcfs *fsp;
    969 	int error = 0;
    970 
    971 	/* this prevents the filesystem from being umounted. */
    972 	mutex_enter(&pcfslock);
    973 	if (vfsp != NULL) {
    974 		fsp = VFSTOPCFS(vfsp);
    975 		if (!(fsp->pcfs_flags & PCFS_IRRECOV)) {
    976 			error = pc_syncfsnodes(fsp);
    977 		} else {
    978 			rw_enter(&pcnodes_lock, RW_WRITER);
    979 			pc_diskchanged(fsp);
    980 			rw_exit(&pcnodes_lock);
    981 			error = EIO;
    982 		}
    983 	} else {
    984 		fsp = pc_mounttab;
    985 		while (fsp != NULL) {
    986 			if (fsp->pcfs_flags & PCFS_IRRECOV) {
    987 				rw_enter(&pcnodes_lock, RW_WRITER);
    988 				pc_diskchanged(fsp);
    989 				rw_exit(&pcnodes_lock);
    990 				error = EIO;
    991 				break;
    992 			}
    993 			error = pc_syncfsnodes(fsp);
    994 			if (error) break;
    995 			fsp = fsp->pcfs_nxt;
    996 		}
    997 	}
    998 	mutex_exit(&pcfslock);
    999 	return (error);
   1000 }
   1001 
   1002 int
   1003 pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing)
   1004 {
   1005 	int err;
   1006 
   1007 	if ((fsp->pcfs_flags & PCFS_IRRECOV) && !releasing)
   1008 		return (EIO);
   1009 
   1010 	if ((fsp->pcfs_flags & PCFS_LOCKED) && (fsp->pcfs_owner == curthread)) {
   1011 		fsp->pcfs_count++;
   1012 	} else {
   1013 		mutex_enter(&fsp->pcfs_lock);
   1014 		if (fsp->pcfs_flags & PCFS_LOCKED)
   1015 			panic("pc_lockfs");
   1016 		/*
   1017 		 * We check the IRRECOV bit again just in case somebody
   1018 		 * snuck past the initial check but then got held up before
   1019 		 * they could grab the lock.  (And in the meantime someone
   1020 		 * had grabbed the lock and set the bit)
   1021 		 */
   1022 		if (!diskchanged && !(fsp->pcfs_flags & PCFS_IRRECOV)) {
   1023 			if ((err = pc_getfat(fsp))) {
   1024 				mutex_exit(&fsp->pcfs_lock);
   1025 				return (err);
   1026 			}
   1027 		}
   1028 		fsp->pcfs_flags |= PCFS_LOCKED;
   1029 		fsp->pcfs_owner = curthread;
   1030 		fsp->pcfs_count++;
   1031 	}
   1032 	return (0);
   1033 }
   1034 
   1035 void
   1036 pc_unlockfs(struct pcfs *fsp)
   1037 {
   1038 
   1039 	if ((fsp->pcfs_flags & PCFS_LOCKED) == 0)
   1040 		panic("pc_unlockfs");
   1041 	if (--fsp->pcfs_count < 0)
   1042 		panic("pc_unlockfs: count");
   1043 	if (fsp->pcfs_count == 0) {
   1044 		fsp->pcfs_flags &= ~PCFS_LOCKED;
   1045 		fsp->pcfs_owner = 0;
   1046 		mutex_exit(&fsp->pcfs_lock);
   1047 	}
   1048 }
   1049 
   1050 int
   1051 pc_syncfat(struct pcfs *fsp)
   1052 {
   1053 	struct buf *bp;
   1054 	int nfat;
   1055 	int	error = 0;
   1056 	struct fat_od_fsi *fsinfo_disk;
   1057 
   1058 	if ((fsp->pcfs_fatp == (uchar_t *)0) ||
   1059 	    !(fsp->pcfs_flags & PCFS_FATMOD))
   1060 		return (0);
   1061 	/*
   1062 	 * write out all copies of FATs
   1063 	 */
   1064 	fsp->pcfs_flags &= ~PCFS_FATMOD;
   1065 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
   1066 	for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) {
   1067 		error = pc_writefat(fsp, pc_dbdaddr(fsp,
   1068 		    fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec));
   1069 		if (error) {
   1070 			pc_mark_irrecov(fsp);
   1071 			return (EIO);
   1072 		}
   1073 	}
   1074 	pc_clear_fatchanges(fsp);
   1075 
   1076 	/*
   1077 	 * Write out fsinfo sector.
   1078 	 */
   1079 	if (IS_FAT32(fsp)) {
   1080 		bp = bread(fsp->pcfs_xdev,
   1081 		    pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
   1082 		if (bp->b_flags & (B_ERROR | B_STALE)) {
   1083 			error = geterror(bp);
   1084 		}
   1085 		fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
   1086 		if (!error && FSISIG_OK(fsinfo_disk)) {
   1087 			fsinfo_disk->fsi_incore.fs_free_clusters =
   1088 			    LE_32(fsp->pcfs_fsinfo.fs_free_clusters);
   1089 			fsinfo_disk->fsi_incore.fs_next_free =
   1090 			    LE_32(FSINFO_UNKNOWN);
   1091 			bwrite2(bp);
   1092 			error = geterror(bp);
   1093 		}
   1094 		brelse(bp);
   1095 		if (error) {
   1096 			pc_mark_irrecov(fsp);
   1097 			return (EIO);
   1098 		}
   1099 	}
   1100 	return (0);
   1101 }
   1102 
   1103 void
   1104 pc_invalfat(struct pcfs *fsp)
   1105 {
   1106 	struct pcfs *xfsp;
   1107 	int mount_cnt = 0;
   1108 
   1109 	if (fsp->pcfs_fatp == (uchar_t *)0)
   1110 		panic("pc_invalfat");
   1111 	/*
   1112 	 * Release FAT
   1113 	 */
   1114 	kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsec * fsp->pcfs_secsize);
   1115 	fsp->pcfs_fatp = NULL;
   1116 	kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize);
   1117 	fsp->pcfs_fat_changemap = NULL;
   1118 	/*
   1119 	 * Invalidate all the blocks associated with the device.
   1120 	 * Not needed if stateless.
   1121 	 */
   1122 	for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt)
   1123 		if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev)
   1124 			mount_cnt++;
   1125 
   1126 	if (!mount_cnt)
   1127 		binval(fsp->pcfs_xdev);
   1128 	/*
   1129 	 * close mounted device
   1130 	 */
   1131 	(void) VOP_CLOSE(fsp->pcfs_devvp,
   1132 	    (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
   1133 	    1, (offset_t)0, CRED(), NULL);
   1134 }
   1135 
   1136 void
   1137 pc_badfs(struct pcfs *fsp)
   1138 {
   1139 	cmn_err(CE_WARN, "corrupted PC file system on dev (%x.%x):%d\n",
   1140 	    getmajor(fsp->pcfs_devvp->v_rdev),
   1141 	    getminor(fsp->pcfs_devvp->v_rdev), fsp->pcfs_ldrive);
   1142 }
   1143 
   1144 /*
   1145  * The problem with supporting NFS on the PCFS filesystem is that there
   1146  * is no good place to keep the generation number. The only possible
   1147  * place is inside a directory entry. There are a few words that we
   1148  * don't use - they store NT & OS/2 attributes, and the creation/last access
   1149  * time of the file - but it seems wrong to use them. In addition, directory
   1150  * entries come and go. If a directory is removed completely, its directory
   1151  * blocks are freed and the generation numbers are lost. Whereas in ufs,
   1152  * inode blocks are dedicated for inodes, so the generation numbers are
   1153  * permanently kept on the disk.
   1154  */
   1155 static int
   1156 pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp)
   1157 {
   1158 	struct pcnode *pcp;
   1159 	struct pc_fid *pcfid;
   1160 	struct pcfs *fsp;
   1161 	struct pcdir *ep;
   1162 	daddr_t eblkno;
   1163 	int eoffset;
   1164 	struct buf *bp;
   1165 	int error;
   1166 	pc_cluster32_t	cn;
   1167 
   1168 	pcfid = (struct pc_fid *)fidp;
   1169 	fsp = VFSTOPCFS(vfsp);
   1170 
   1171 	error = pc_lockfs(fsp, 0, 0);
   1172 	if (error) {
   1173 		*vpp = NULL;
   1174 		return (error);
   1175 	}
   1176 
   1177 	if (pcfid->pcfid_block == 0) {
   1178 		pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
   1179 		pcp->pc_flags |= PC_EXTERNAL;
   1180 		*vpp = PCTOV(pcp);
   1181 		pc_unlockfs(fsp);
   1182 		return (0);
   1183 	}
   1184 	eblkno = pcfid->pcfid_block;
   1185 	eoffset = pcfid->pcfid_offset;
   1186 
   1187 	if ((pc_dbtocl(fsp,
   1188 	    eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) ||
   1189 	    (eoffset > fsp->pcfs_clsize)) {
   1190 		pc_unlockfs(fsp);
   1191 		*vpp = NULL;
   1192 		return (EINVAL);
   1193 	}
   1194 
   1195 	if (eblkno >= fsp->pcfs_datastart || (eblkno - fsp->pcfs_rdirstart)
   1196 	    < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
   1197 		bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
   1198 		    fsp->pcfs_clsize);
   1199 	} else {
   1200 		/*
   1201 		 * This is an access "backwards" into the FAT12/FAT16
   1202 		 * root directory. A better code structure would
   1203 		 * significantly improve maintainability here ...
   1204 		 */
   1205 		bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
   1206 		    (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize);
   1207 	}
   1208 	if (bp->b_flags & (B_ERROR | B_STALE)) {
   1209 		error = geterror(bp);
   1210 		brelse(bp);
   1211 		if (error)
   1212 			pc_mark_irrecov(fsp);
   1213 		*vpp = NULL;
   1214 		pc_unlockfs(fsp);
   1215 		return (error);
   1216 	}
   1217 	ep = (struct pcdir *)(bp->b_un.b_addr + eoffset);
   1218 	/*
   1219 	 * Ok, if this is a valid file handle that we gave out,
   1220 	 * then simply ensuring that the creation time matches,
   1221 	 * the entry has not been deleted, and it has a valid first
   1222 	 * character should be enough.
   1223 	 *
   1224 	 * Unfortunately, verifying that the <blkno, offset> _still_
   1225 	 * refers to a directory entry is not easy, since we'd have
   1226 	 * to search _all_ directories starting from root to find it.
   1227 	 * That's a high price to pay just in case somebody is forging
   1228 	 * file handles. So instead we verify that as much of the
   1229 	 * entry is valid as we can:
   1230 	 *
   1231 	 * 1. The starting cluster is 0 (unallocated) or valid
   1232 	 * 2. It is not an LFN entry
   1233 	 * 3. It is not hidden (unless mounted as such)
   1234 	 * 4. It is not the label
   1235 	 */
   1236 	cn = pc_getstartcluster(fsp, ep);
   1237 	/*
   1238 	 * if the starting cluster is valid, but not valid according
   1239 	 * to pc_validcl(), force it to be to simplify the following if.
   1240 	 */
   1241 	if (cn == 0)
   1242 		cn = PCF_FIRSTCLUSTER;
   1243 	if (IS_FAT32(fsp)) {
   1244 		if (cn >= PCF_LASTCLUSTER32)
   1245 			cn = PCF_FIRSTCLUSTER;
   1246 	} else {
   1247 		if (cn >= PCF_LASTCLUSTER)
   1248 			cn = PCF_FIRSTCLUSTER;
   1249 	}
   1250 	if ((!pc_validcl(fsp, cn)) ||
   1251 	    (PCDL_IS_LFN(ep)) ||
   1252 	    (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) ||
   1253 	    ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) {
   1254 		bp->b_flags |= B_STALE | B_AGE;
   1255 		brelse(bp);
   1256 		pc_unlockfs(fsp);
   1257 		return (EINVAL);
   1258 	}
   1259 	if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) &&
   1260 	    (ep->pcd_filename[0] != PCD_ERASED) &&
   1261 	    (pc_validchar(ep->pcd_filename[0]) ||
   1262 	    (ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) {
   1263 		pcp = pc_getnode(fsp, eblkno, eoffset, ep);
   1264 		pcp->pc_flags |= PC_EXTERNAL;
   1265 		*vpp = PCTOV(pcp);
   1266 	} else {
   1267 		*vpp = NULL;
   1268 	}
   1269 	bp->b_flags |= B_STALE | B_AGE;
   1270 	brelse(bp);
   1271 	pc_unlockfs(fsp);
   1272 	return (0);
   1273 }
   1274 
   1275 /*
   1276  * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about
   1277  * a meg), so we can't bread() it all in at once. This routine reads a
   1278  * fat a chunk at a time.
   1279  */
   1280 static int
   1281 pc_readfat(struct pcfs *fsp, uchar_t *fatp)
   1282 {
   1283 	struct buf *bp;
   1284 	size_t off;
   1285 	size_t readsize;
   1286 	daddr_t diskblk;
   1287 	size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
   1288 	daddr_t start = fsp->pcfs_fatstart;
   1289 
   1290 	readsize = fsp->pcfs_clsize;
   1291 	for (off = 0; off < fatsize; off += readsize, fatp += readsize) {
   1292 		if (readsize > (fatsize - off))
   1293 			readsize = fatsize - off;
   1294 		diskblk = pc_dbdaddr(fsp, start +
   1295 		    pc_cltodb(fsp, pc_lblkno(fsp, off)));
   1296 		bp = bread(fsp->pcfs_xdev, diskblk, readsize);
   1297 		if (bp->b_flags & (B_ERROR | B_STALE)) {
   1298 			brelse(bp);
   1299 			return (EIO);
   1300 		}
   1301 		bp->b_flags |= B_STALE | B_AGE;
   1302 		bcopy(bp->b_un.b_addr, fatp, readsize);
   1303 		brelse(bp);
   1304 	}
   1305 	return (0);
   1306 }
   1307 
   1308 /*
   1309  * We write the FAT out a _lot_, in order to make sure that it
   1310  * is up-to-date. But on a FAT32 system (large drive, small clusters)
   1311  * the FAT might be a couple of megabytes, and writing it all out just
   1312  * because we created or deleted a small file is painful (especially
   1313  * since we do it for each alternate FAT too). So instead, for FAT16 and
   1314  * FAT32 we only write out the bit that has changed. We don't clear
   1315  * the 'updated' fields here because the caller might be writing out
   1316  * several FATs, so the caller must use pc_clear_fatchanges() after
   1317  * all FATs have been updated.
   1318  * This function doesn't take "start" from fsp->pcfs_dosstart because
   1319  * callers can use it to write either the primary or any of the alternate
   1320  * FAT tables.
   1321  */
   1322 static int
   1323 pc_writefat(struct pcfs *fsp, daddr_t start)
   1324 {
   1325 	struct buf *bp;
   1326 	size_t off;
   1327 	size_t writesize;
   1328 	int	error;
   1329 	uchar_t *fatp = fsp->pcfs_fatp;
   1330 	size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
   1331 
   1332 	writesize = fsp->pcfs_clsize;
   1333 	for (off = 0; off < fatsize; off += writesize, fatp += writesize) {
   1334 		if (writesize > (fatsize - off))
   1335 			writesize = fatsize - off;
   1336 		if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) {
   1337 			continue;
   1338 		}
   1339 		bp = ngeteblk(writesize);
   1340 		bp->b_edev = fsp->pcfs_xdev;
   1341 		bp->b_dev = cmpdev(bp->b_edev);
   1342 		bp->b_blkno = pc_dbdaddr(fsp, start +
   1343 		    pc_cltodb(fsp, pc_lblkno(fsp, off)));
   1344 		bcopy(fatp, bp->b_un.b_addr, writesize);
   1345 		bwrite2(bp);
   1346 		error = geterror(bp);
   1347 		brelse(bp);
   1348 		if (error) {
   1349 			return (error);
   1350 		}
   1351 	}
   1352 	return (0);
   1353 }
   1354 
   1355 /*
   1356  * Mark the FAT cluster that 'cn' is stored in as modified.
   1357  */
   1358 void
   1359 pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn)
   1360 {
   1361 	pc_cluster32_t	bn;
   1362 	size_t		size;
   1363 
   1364 	/* which fat block is the cluster number stored in? */
   1365 	if (IS_FAT32(fsp)) {
   1366 		size = sizeof (pc_cluster32_t);
   1367 		bn = pc_lblkno(fsp, cn * size);
   1368 		fsp->pcfs_fat_changemap[bn] = 1;
   1369 	} else if (IS_FAT16(fsp)) {
   1370 		size = sizeof (pc_cluster16_t);
   1371 		bn = pc_lblkno(fsp, cn * size);
   1372 		fsp->pcfs_fat_changemap[bn] = 1;
   1373 	} else {
   1374 		offset_t off;
   1375 		pc_cluster32_t nbn;
   1376 
   1377 		ASSERT(IS_FAT12(fsp));
   1378 		off = cn + (cn >> 1);
   1379 		bn = pc_lblkno(fsp, off);
   1380 		fsp->pcfs_fat_changemap[bn] = 1;
   1381 		/* does this field wrap into the next fat cluster? */
   1382 		nbn = pc_lblkno(fsp, off + 1);
   1383 		if (nbn != bn) {
   1384 			fsp->pcfs_fat_changemap[nbn] = 1;
   1385 		}
   1386 	}
   1387 }
   1388 
   1389 /*
   1390  * return whether the FAT cluster 'bn' is updated and needs to
   1391  * be written out.
   1392  */
   1393 int
   1394 pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn)
   1395 {
   1396 	return (fsp->pcfs_fat_changemap[bn] == 1);
   1397 }
   1398 
   1399 /*
   1400  * Implementation of VFS_FREEVFS() to support forced umounts.
   1401  * This is called by the vfs framework after umount, to trigger
   1402  * the release of any resources still associated with the given
   1403  * vfs_t once the need to keep them has gone away.
   1404  */
   1405 void
   1406 pcfs_freevfs(vfs_t *vfsp)
   1407 {
   1408 	struct pcfs *fsp = VFSTOPCFS(vfsp);
   1409 
   1410 	mutex_enter(&pcfslock);
   1411 	/*
   1412 	 * Purging the FAT closes the device - can't do any more
   1413 	 * I/O after this.
   1414 	 */
   1415 	if (fsp->pcfs_fatp != (uchar_t *)0)
   1416 		pc_invalfat(fsp);
   1417 	mutex_exit(&pcfslock);
   1418 
   1419 	VN_RELE(fsp->pcfs_devvp);
   1420 	mutex_destroy(&fsp->pcfs_lock);
   1421 	kmem_free(fsp, sizeof (*fsp));
   1422 
   1423 	/*
   1424 	 * Allow _fini() to succeed now, if so desired.
   1425 	 */
   1426 	atomic_dec_32(&pcfs_mountcount);
   1427 }
   1428 
   1429 
   1430 /*
   1431  * PC-style partition parsing and FAT BPB identification/validation code.
   1432  * The partition parsers here assume:
   1433  *	- a FAT filesystem will be in a partition that has one of a set of
   1434  *	  recognized partition IDs
   1435  *	- the user wants the 'numbering' (C:, D:, ...) that one would get
   1436  *	  on MSDOS 6.x.
   1437  *	  That means any non-FAT partition type (NTFS, HPFS, or any Linux fs)
   1438  *	  will not factor in the enumeration.
   1439  * These days, such assumptions should be revisited. FAT is no longer the
   1440  * only game in 'PC town'.
   1441  */
   1442 /*
   1443  * isDosDrive()
   1444  *	Boolean function.  Give it the systid field for an fdisk partition
   1445  *	and it decides if that's a systid that describes a DOS drive.  We
   1446  *	use systid values defined in sys/dktp/fdisk.h.
   1447  */
   1448 static int
   1449 isDosDrive(uchar_t checkMe)
   1450 {
   1451 	return ((checkMe == DOSOS12) || (checkMe == DOSOS16) ||
   1452 	    (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) ||
   1453 	    (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) ||
   1454 	    (checkMe == DIAGPART));
   1455 }
   1456 
   1457 
   1458 /*
   1459  * isDosExtended()
   1460  *	Boolean function.  Give it the systid field for an fdisk partition
   1461  *	and it decides if that's a systid that describes an extended DOS
   1462  *	partition.
   1463  */
   1464 static int
   1465 isDosExtended(uchar_t checkMe)
   1466 {
   1467 	return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA));
   1468 }
   1469 
   1470 
   1471 /*
   1472  * isBootPart()
   1473  *	Boolean function.  Give it the systid field for an fdisk partition
   1474  *	and it decides if that's a systid that describes a Solaris boot
   1475  *	partition.
   1476  */
   1477 static int
   1478 isBootPart(uchar_t checkMe)
   1479 {
   1480 	return (checkMe == X86BOOT);
   1481 }
   1482 
   1483 
   1484 /*
   1485  * noLogicalDrive()
   1486  *	Display error message about not being able to find a logical
   1487  *	drive.
   1488  */
   1489 static void
   1490 noLogicalDrive(int ldrive)
   1491 {
   1492 	if (ldrive == BOOT_PARTITION_DRIVE) {
   1493 		cmn_err(CE_NOTE, "!pcfs: no boot partition");
   1494 	} else {
   1495 		cmn_err(CE_NOTE, "!pcfs: %d: no such logical drive", ldrive);
   1496 	}
   1497 }
   1498 
   1499 
   1500 /*
   1501  * findTheDrive()
   1502  *	Discover offset of the requested logical drive, and return
   1503  *	that offset (startSector), the systid of that drive (sysid),
   1504  *	and a buffer pointer (bp), with the buffer contents being
   1505  *	the first sector of the logical drive (i.e., the sector that
   1506  *	contains the BPB for that drive).
   1507  *
   1508  * Note: this code is not capable of addressing >2TB disks, as it uses
   1509  *       daddr_t not diskaddr_t, some of the calculations would overflow
   1510  */
   1511 #define	COPY_PTBL(mbr, ptblp)					\
   1512 	bcopy(&(((struct mboot *)(mbr))->parts), (ptblp),	\
   1513 	    FD_NUMPART * sizeof (struct ipart))
   1514 
   1515 static int
   1516 findTheDrive(struct pcfs *fsp, buf_t **bp)
   1517 {
   1518 	int ldrive = fsp->pcfs_ldrive;
   1519 	dev_t dev = fsp->pcfs_devvp->v_rdev;
   1520 
   1521 	struct ipart dosp[FD_NUMPART];	/* incore fdisk partition structure */
   1522 	daddr_t lastseek = 0;		/* Disk block we sought previously */
   1523 	daddr_t diskblk = 0;		/* Disk block to get */
   1524 	daddr_t xstartsect;		/* base of Extended DOS partition */
   1525 	int logicalDriveCount = 0;	/* Count of logical drives seen */
   1526 	int extendedPart = -1;		/* index of extended dos partition */
   1527 	int primaryPart = -1;		/* index of primary dos partition */
   1528 	int bootPart = -1;		/* index of a Solaris boot partition */
   1529 	uint32_t xnumsect = 0;		/* length of extended DOS partition */
   1530 	int driveIndex;			/* computed FDISK table index */
   1531 	daddr_t startsec;
   1532 	len_t mediasize;
   1533 	int i;
   1534 	/*
   1535 	 * Count of drives in the current extended partition's
   1536 	 * FDISK table, and indexes of the drives themselves.
   1537 	 */
   1538 	int extndDrives[FD_NUMPART];
   1539 	int numDrives = 0;
   1540 
   1541 	/*
   1542 	 * Count of drives (beyond primary) in master boot record's
   1543 	 * FDISK table, and indexes of the drives themselves.
   1544 	 */
   1545 	int extraDrives[FD_NUMPART];
   1546 	int numExtraDrives = 0;
   1547 
   1548 	/*
   1549 	 * "ldrive == 0" should never happen, as this is a request to
   1550 	 * mount the physical device (and ignore partitioning). The code
   1551 	 * in pcfs_mount() should have made sure that a logical drive number
   1552 	 * is at least 1, meaning we're looking for drive "C:". It is not
   1553 	 * safe (and a bug in the callers of this function) to request logical
   1554 	 * drive number 0; we could ASSERT() but a graceful EIO is a more
   1555 	 * polite way.
   1556 	 */
   1557 	if (ldrive == 0) {
   1558 		cmn_err(CE_NOTE, "!pcfs: request for logical partition zero");
   1559 		noLogicalDrive(ldrive);
   1560 		return (EIO);
   1561 	}
   1562 
   1563 	/*
   1564 	 *  Copy from disk block into memory aligned structure for fdisk usage.
   1565 	 */
   1566 	COPY_PTBL((*bp)->b_un.b_addr, dosp);
   1567 
   1568 	/*
   1569 	 * This check is ok because a FAT BPB and a master boot record (MBB)
   1570 	 * have the same signature, in the same position within the block.
   1571 	 */
   1572 	if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
   1573 		cmn_err(CE_NOTE, "!pcfs: MBR partition table signature err, "
   1574 		    "device (%x.%x):%d\n",
   1575 		    getmajor(dev), getminor(dev), ldrive);
   1576 		return (EINVAL);
   1577 	}
   1578 
   1579 	/*
   1580 	 * Get a summary of what is in the Master FDISK table.
   1581 	 * Normally we expect to find one partition marked as a DOS drive.
   1582 	 * This partition is the one Windows calls the primary dos partition.
   1583 	 * If the machine has any logical drives then we also expect
   1584 	 * to find a partition marked as an extended DOS partition.
   1585 	 *
   1586 	 * Sometimes we'll find multiple partitions marked as DOS drives.
   1587 	 * The Solaris fdisk program allows these partitions
   1588 	 * to be created, but Windows fdisk no longer does.  We still need
   1589 	 * to support these, though, since Windows does.  We also need to fix
   1590 	 * our fdisk to behave like the Windows version.
   1591 	 *
   1592 	 * It turns out that some off-the-shelf media have *only* an
   1593 	 * Extended partition, so we need to deal with that case as well.
   1594 	 *
   1595 	 * Only a single (the first) Extended or Boot Partition will
   1596 	 * be recognized.  Any others will be ignored.
   1597 	 */
   1598 	for (i = 0; i < FD_NUMPART; i++) {
   1599 		DTRACE_PROBE4(primarypart, struct pcfs *, fsp,
   1600 		    uint_t, (uint_t)dosp[i].systid,
   1601 		    uint_t, LE_32(dosp[i].relsect),
   1602 		    uint_t, LE_32(dosp[i].numsect));
   1603 
   1604 		if (isDosDrive(dosp[i].systid)) {
   1605 			if (primaryPart < 0) {
   1606 				logicalDriveCount++;
   1607 				primaryPart = i;
   1608 			} else {
   1609 				extraDrives[numExtraDrives++] = i;
   1610 			}
   1611 			continue;
   1612 		}
   1613 		if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) {
   1614 			extendedPart = i;
   1615 			continue;
   1616 		}
   1617 		if ((bootPart < 0) && isBootPart(dosp[i].systid)) {
   1618 			bootPart = i;
   1619 			continue;
   1620 		}
   1621 	}
   1622 
   1623 	if (ldrive == BOOT_PARTITION_DRIVE) {
   1624 		if (bootPart < 0) {
   1625 			noLogicalDrive(ldrive);
   1626 			return (EINVAL);
   1627 		}
   1628 		startsec = LE_32(dosp[bootPart].relsect);
   1629 		mediasize = LE_32(dosp[bootPart].numsect);
   1630 		goto found;
   1631 	}
   1632 
   1633 	if (ldrive == PRIMARY_DOS_DRIVE && primaryPart >= 0) {
   1634 		startsec = LE_32(dosp[primaryPart].relsect);
   1635 		mediasize = LE_32(dosp[primaryPart].numsect);
   1636 		goto found;
   1637 	}
   1638 
   1639 	/*
   1640 	 * We are not looking for the C: drive (or the primary drive
   1641 	 * was not found), so we had better have an extended partition
   1642 	 * or extra drives in the Master FDISK table.
   1643 	 */
   1644 	if ((extendedPart < 0) && (numExtraDrives == 0)) {
   1645 		cmn_err(CE_NOTE, "!pcfs: no extended dos partition");
   1646 		noLogicalDrive(ldrive);
   1647 		return (EINVAL);
   1648 	}
   1649 
   1650 	if (extendedPart >= 0) {
   1651 		diskblk = xstartsect = LE_32(dosp[extendedPart].relsect);
   1652 		xnumsect = LE_32(dosp[extendedPart].numsect);
   1653 		do {
   1654 			/*
   1655 			 *  If the seek would not cause us to change
   1656 			 *  position on the drive, then we're out of
   1657 			 *  extended partitions to examine.
   1658 			 */
   1659 			if (diskblk == lastseek)
   1660 				break;
   1661 			logicalDriveCount += numDrives;
   1662 			/*
   1663 			 *  Seek the next extended partition, and find
   1664 			 *  logical drives within it.
   1665 			 */
   1666 			brelse(*bp);
   1667 			/*
   1668 			 * bread() block numbers are multiples of DEV_BSIZE
   1669 			 * but the device sector size (the unit of partitioning)
   1670 			 * might be larger than that; pcfs_get_device_info()
   1671 			 * has calculated the multiplicator for us.
   1672 			 */
   1673 			*bp = bread(dev,
   1674 			    pc_dbdaddr(fsp, diskblk), fsp->pcfs_secsize);
   1675 			if ((*bp)->b_flags & B_ERROR) {
   1676 				return (EIO);
   1677 			}
   1678 
   1679 			lastseek = diskblk;
   1680 			COPY_PTBL((*bp)->b_un.b_addr, dosp);
   1681 			if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
   1682 				cmn_err(CE_NOTE, "!pcfs: "
   1683 				    "extended partition table signature err, "
   1684 				    "device (%x.%x):%d, LBA %u",
   1685 				    getmajor(dev), getminor(dev), ldrive,
   1686 				    (uint_t)pc_dbdaddr(fsp, diskblk));
   1687 				return (EINVAL);
   1688 			}
   1689 			/*
   1690 			 *  Count up drives, and track where the next
   1691 			 *  extended partition is in case we need it.  We
   1692 			 *  are expecting only one extended partition.  If
   1693 			 *  there is more than one we'll only go to the
   1694 			 *  first one we see, but warn about ignoring.
   1695 			 */
   1696 			numDrives = 0;
   1697 			for (i = 0; i < FD_NUMPART; i++) {
   1698 				DTRACE_PROBE4(extendedpart,
   1699 				    struct pcfs *, fsp,
   1700 				    uint_t, (uint_t)dosp[i].systid,
   1701 				    uint_t, LE_32(dosp[i].relsect),
   1702 				    uint_t, LE_32(dosp[i].numsect));
   1703 				if (isDosDrive(dosp[i].systid)) {
   1704 					extndDrives[numDrives++] = i;
   1705 				} else if (isDosExtended(dosp[i].systid)) {
   1706 					if (diskblk != lastseek) {
   1707 						/*
   1708 						 * Already found an extended
   1709 						 * partition in this table.
   1710 						 */
   1711 						cmn_err(CE_NOTE,
   1712 						    "!pcfs: ignoring unexpected"
   1713 						    " additional extended"
   1714 						    " partition");
   1715 					} else {
   1716 						diskblk = xstartsect +
   1717 						    LE_32(dosp[i].relsect);
   1718 					}
   1719 				}
   1720 			}
   1721 		} while (ldrive > logicalDriveCount + numDrives);
   1722 
   1723 		ASSERT(numDrives <= FD_NUMPART);
   1724 
   1725 		if (ldrive <= logicalDriveCount + numDrives) {
   1726 			/*
   1727 			 * The number of logical drives we've found thus
   1728 			 * far is enough to get us to the one we were
   1729 			 * searching for.
   1730 			 */
   1731 			driveIndex = logicalDriveCount + numDrives - ldrive;
   1732 			mediasize =
   1733 			    LE_32(dosp[extndDrives[driveIndex]].numsect);
   1734 			startsec =
   1735 			    LE_32(dosp[extndDrives[driveIndex]].relsect) +
   1736 			    lastseek;
   1737 			if (startsec > (xstartsect + xnumsect)) {
   1738 				cmn_err(CE_NOTE, "!pcfs: extended partition "
   1739 				    "values bad");
   1740 				return (EINVAL);
   1741 			}
   1742 			goto found;
   1743 		} else {
   1744 			/*
   1745 			 * We ran out of extended dos partition
   1746 			 * drives.  The only hope now is to go
   1747 			 * back to extra drives defined in the master
   1748 			 * fdisk table.  But we overwrote that table
   1749 			 * already, so we must load it in again.
   1750 			 */
   1751 			logicalDriveCount += numDrives;
   1752 			brelse(*bp);
   1753 			ASSERT(fsp->pcfs_dosstart == 0);
   1754 			*bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
   1755 			    fsp->pcfs_secsize);
   1756 			if ((*bp)->b_flags & B_ERROR) {
   1757 				return (EIO);
   1758 			}
   1759 			COPY_PTBL((*bp)->b_un.b_addr, dosp);
   1760 		}
   1761 	}
   1762 	/*
   1763 	 *  Still haven't found the drive, is it an extra
   1764 	 *  drive defined in the main FDISK table?
   1765 	 */
   1766 	if (ldrive <= logicalDriveCount + numExtraDrives) {
   1767 		driveIndex = logicalDriveCount + numExtraDrives - ldrive;
   1768 		ASSERT(driveIndex < MIN(numExtraDrives, FD_NUMPART));
   1769 		mediasize = LE_32(dosp[extraDrives[driveIndex]].numsect);
   1770 		startsec = LE_32(dosp[extraDrives[driveIndex]].relsect);
   1771 		goto found;
   1772 	}
   1773 	/*
   1774 	 *  Still haven't found the drive, and there is
   1775 	 *  nowhere else to look.
   1776 	 */
   1777 	noLogicalDrive(ldrive);
   1778 	return (EINVAL);
   1779 
   1780 found:
   1781 	/*
   1782 	 * We need this value in units of sectorsize, because PCFS' internal
   1783 	 * offset calculations go haywire for > 512Byte sectors unless all
   1784 	 * pcfs_.*start values are in units of sectors.
   1785 	 * So, assign before the capacity check (that's done in DEV_BSIZE)
   1786 	 */
   1787 	fsp->pcfs_dosstart = startsec;
   1788 
   1789 	/*
   1790 	 * convert from device sectors to proper units:
   1791 	 *	- starting sector: DEV_BSIZE (as argument to bread())
   1792 	 *	- media size: Bytes
   1793 	 */
   1794 	startsec = pc_dbdaddr(fsp, startsec);
   1795 	mediasize *= fsp->pcfs_secsize;
   1796 
   1797 	/*
   1798 	 * some additional validation / warnings in case the partition table
   1799 	 * and the actual media capacity are not in accordance ...
   1800 	 */
   1801 	if (fsp->pcfs_mediasize != 0) {
   1802 		diskaddr_t startoff =
   1803 		    (diskaddr_t)startsec * (diskaddr_t)DEV_BSIZE;
   1804 
   1805 		if (startoff >= fsp->pcfs_mediasize ||
   1806 		    startoff + mediasize > fsp->pcfs_mediasize) {
   1807 			cmn_err(CE_WARN,
   1808 			    "!pcfs: partition size (LBA start %u, %lld bytes, "
   1809 			    "device (%x.%x):%d) smaller than "
   1810 			    "mediasize (%lld bytes).\n"
   1811 			    "filesystem may be truncated, access errors "
   1812 			    "may result.\n",
   1813 			    (uint_t)startsec, (long long)mediasize,
   1814 			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
   1815 			    fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
   1816 		}
   1817 	} else {
   1818 		fsp->pcfs_mediasize = mediasize;
   1819 	}
   1820 
   1821 	return (0);
   1822 }
   1823 
   1824 
   1825 static fattype_t
   1826 secondaryBPBChecks(struct pcfs *fsp, uchar_t *bpb, size_t secsize)
   1827 {
   1828 	uint32_t ncl = fsp->pcfs_ncluster;
   1829 
   1830 	if (ncl <= 4096) {
   1831 		if (bpb_get_FatSz16(bpb) == 0)
   1832 			return (FAT_UNKNOWN);
   1833 
   1834 		if (bpb_get_FatSz16(bpb) * secsize < ncl * 2 &&
   1835 		    bpb_get_FatSz16(bpb) * secsize >= (3 * ncl / 2))
   1836 			return (FAT12);
   1837 		if (bcmp(bpb_FilSysType16(bpb), "FAT12", 5) == 0)
   1838 			return (FAT12);
   1839 		if (bcmp(bpb_FilSysType16(bpb), "FAT16", 5) == 0)
   1840 			return (FAT16);
   1841 
   1842 		switch (bpb_get_Media(bpb)) {
   1843 			case SS8SPT:
   1844 			case DS8SPT:
   1845 			case SS9SPT:
   1846 			case DS9SPT:
   1847 			case DS18SPT:
   1848 			case DS9_15SPT:
   1849 				/*
   1850 				 * Is this reliable - all floppies are FAT12 ?
   1851 				 */
   1852 				return (FAT12);
   1853 			case MD_FIXED:
   1854 				/*
   1855 				 * Is this reliable - disks are always FAT16 ?
   1856 				 */
   1857 				return (FAT16);
   1858 			default:
   1859 				break;
   1860 		}
   1861 	} else if (ncl <= 65536) {
   1862 		if (bpb_get_FatSz16(bpb) == 0 && bpb_get_FatSz32(bpb) > 0)
   1863 			return (FAT32);
   1864 		if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
   1865 			return (FAT32);
   1866 		if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
   1867 			return (FAT32);
   1868 
   1869 		if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
   1870 			return (FAT16);
   1871 		if (bpb_get_FatSz16(bpb) * secsize < ncl * 4)
   1872 			return (FAT16);
   1873 	}
   1874 
   1875 	/*
   1876 	 * We don't know
   1877 	 */
   1878 	return (FAT_UNKNOWN);
   1879 }
   1880 
   1881 /*
   1882  * Check to see if the BPB we found is correct.
   1883  *
   1884  * This looks far more complicated that it needs to be for pure structural
   1885  * validation. The reason for this is that parseBPB() is also used for
   1886  * debugging purposes (mdb dcmd) and we therefore want a bitmap of which
   1887  * BPB fields (do not) have 'known good' values, even if we (do not) reject
   1888  * the BPB when attempting to mount the filesystem.
   1889  *
   1890  * Real-world usage of FAT shows there are a lot of corner-case situations
   1891  * and, following the specification strictly, invalid filesystems out there.
   1892  * Known are situations such as:
   1893  *	- FAT12/FAT16 filesystems with garbage in either totsec16/32
   1894  *	  instead of the zero in one of the fields mandated by the spec
   1895  *	- filesystems that claim to be larger than the partition they're in
   1896  *	- filesystems without valid media descriptor
   1897  *	- FAT32 filesystems with RootEntCnt != 0
   1898  *	- FAT32 filesystems with less than 65526 clusters
   1899  *	- FAT32 filesystems without valid FSI sector
   1900  *	- FAT32 filesystems with FAT size in fatsec16 instead of fatsec32
   1901  *
   1902  * Such filesystems are accessible by PCFS - if it'd know to start with that
   1903  * the filesystem should be treated as a specific FAT type. Before S10, it
   1904  * relied on the PC/fdisk partition type for the purpose and almost completely
   1905  * ignored the BPB; now it ignores the partition type for anything else but
   1906  * logical drive enumeration, which can result in rejection of (invalid)
   1907  * FAT32 - if the partition ID says FAT32, but the filesystem, for example
   1908  * has less than 65526 clusters.
   1909  *
   1910  * Without a "force this fs as FAT{12,16,32}" tunable or mount option, it's
   1911  * not possible to allow all such mostly-compliant filesystems in unless one
   1912  * accepts false positives (definitely invalid filesystems that cause problems
   1913  * later). This at least allows to pinpoint why the mount failed.
   1914  *
   1915  * Due to the use of FAT on removeable media, all relaxations of the rules
   1916  * here need to be carefully evaluated wrt. to potential effects on PCFS
   1917  * resilience. A faulty/"mis-crafted" filesystem must not cause a panic, so
   1918  * beware.
   1919  */
   1920 static int
   1921 parseBPB(struct pcfs *fsp, uchar_t *bpb, int *valid)
   1922 {
   1923 	fattype_t type;
   1924 
   1925 	uint32_t	ncl;	/* number of clusters in file area */
   1926 	uint32_t	rec;
   1927 	uint32_t	reserved;
   1928 	uint32_t	fsisec, bkbootsec;
   1929 	blkcnt_t	totsec, totsec16, totsec32, datasec;
   1930 	size_t		fatsec, fatsec16, fatsec32, rdirsec;
   1931 	size_t		secsize;
   1932 	len_t		mediasize;
   1933 	uint64_t	validflags = 0;
   1934 
   1935 	if (VALID_BPBSIG(bpb_get_BPBSig(bpb)))
   1936 		validflags |= BPB_BPBSIG_OK;
   1937 
   1938 	rec = bpb_get_RootEntCnt(bpb);
   1939 	reserved = bpb_get_RsvdSecCnt(bpb);
   1940 	fsisec = bpb_get_FSInfo32(bpb);
   1941 	bkbootsec = bpb_get_BkBootSec32(bpb);
   1942 	totsec16 = (blkcnt_t)bpb_get_TotSec16(bpb);
   1943 	totsec32 = (blkcnt_t)bpb_get_TotSec32(bpb);
   1944 	fatsec16 = bpb_get_FatSz16(bpb);
   1945 	fatsec32 = bpb_get_FatSz32(bpb);
   1946 
   1947 	totsec = totsec16 ? totsec16 : totsec32;
   1948 	fatsec = fatsec16 ? fatsec16 : fatsec32;
   1949 
   1950 	secsize = bpb_get_BytesPerSec(bpb);
   1951 	if (!VALID_SECSIZE(secsize))
   1952 		secsize = fsp->pcfs_secsize;
   1953 	if (secsize != fsp->pcfs_secsize) {
   1954 		PC_DPRINTF3(3, "!pcfs: parseBPB, device (%x.%x):%d:\n",
   1955 		    getmajor(fsp->pcfs_xdev),
   1956 		    getminor(fsp->pcfs_xdev), fsp->pcfs_ldrive);
   1957 		PC_DPRINTF2(3, "!BPB secsize %d != "
   1958 		    "autodetected media block size %d\n",
   1959 		    (int)secsize, (int)fsp->pcfs_secsize);
   1960 		if (fsp->pcfs_ldrive) {
   1961 			/*
   1962 			 * We've already attempted to parse the partition
   1963 			 * table. If the block size used for that don't match
   1964 			 * the PCFS sector size, we're hosed one way or the
   1965 			 * other. Just try what happens.
   1966 			 */
   1967 			secsize = fsp->pcfs_secsize;
   1968 			PC_DPRINTF1(3,
   1969 			    "!pcfs: Using autodetected secsize %d\n",
   1970 			    (int)secsize);
   1971 		} else {
   1972 			/*
   1973 			 * This allows mounting lofi images of PCFS partitions
   1974 			 * with sectorsize != DEV_BSIZE. We can't parse the
   1975 			 * partition table on whole-disk images unless the
   1976 			 * (undocumented) "secsize=..." mount option is used,
   1977 			 * but at least this allows us to mount if we have
   1978 			 * an image of a partition.
   1979 			 */
   1980 			PC_DPRINTF1(3,
   1981 			    "!pcfs: Using BPB secsize %d\n", (int)secsize);
   1982 		}
   1983 	}
   1984 
   1985 	if (fsp->pcfs_mediasize == 0) {
   1986 		mediasize = (len_t)totsec * (len_t)secsize;
   1987 		/*
   1988 		 * This is not an error because not all devices support the
   1989 		 * dkio(7i) mediasize queries, and/or not all devices are
   1990 		 * partitioned. If we have not been able to figure out the
   1991 		 * size of the underlaying medium, we have to trust the BPB.
   1992 		 */
   1993 		PC_DPRINTF4(3, "!pcfs: parseBPB: mediasize autodetect failed "
   1994 		    "on device (%x.%x):%d, trusting BPB totsec (%lld Bytes)\n",
   1995 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
   1996 		    fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
   1997 	} else if ((len_t)totsec * (len_t)secsize > fsp->pcfs_mediasize) {
   1998 		cmn_err(CE_WARN,
   1999 		    "!pcfs: autodetected mediasize (%lld Bytes) smaller than "
   2000 		    "FAT BPB mediasize (%lld Bytes).\n"
   2001 		    "truncated filesystem on device (%x.%x):%d, access errors "
   2002 		    "possible.\n",
   2003 		    (long long)fsp->pcfs_mediasize,
   2004 		    (long long)(totsec * (blkcnt_t)secsize),
   2005 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
   2006 		    fsp->pcfs_ldrive);
   2007 		mediasize = fsp->pcfs_mediasize;
   2008 	} else {
   2009 		/*
   2010 		 * This is actually ok. A FAT needs not occupy the maximum
   2011 		 * space available in its partition, it can be shorter.
   2012 		 */
   2013 		mediasize = (len_t)totsec * (len_t)secsize;
   2014 	}
   2015 
   2016 	/*
   2017 	 * Since we let just about anything pass through this function,
   2018 	 * fence against divide-by-zero here.
   2019 	 */
   2020 	if (secsize)
   2021 		rdirsec = roundup(rec * 32, secsize) / secsize;
   2022 	else
   2023 		rdirsec = 0;
   2024 
   2025 	/*
   2026 	 * This assignment is necessary before pc_dbdaddr() can first be
   2027 	 * used. Must initialize the value here.
   2028 	 */
   2029 	fsp->pcfs_secsize = secsize;
   2030 	fsp->pcfs_sdshift = ddi_ffs(secsize / DEV_BSIZE) - 1;
   2031 
   2032 	fsp->pcfs_mediasize = mediasize;
   2033 
   2034 	fsp->pcfs_spcl = bpb_get_SecPerClus(bpb);
   2035 	fsp->pcfs_numfat = bpb_get_NumFATs(bpb);
   2036 	fsp->pcfs_mediadesc = bpb_get_Media(bpb);
   2037 	fsp->pcfs_clsize = secsize * fsp->pcfs_spcl;
   2038 	fsp->pcfs_rdirsec = rdirsec;
   2039 
   2040 	/*
   2041 	 * Remember: All PCFS offset calculations in sectors. Before I/O
   2042 	 * is done, convert to DEV_BSIZE units via pc_dbdaddr(). This is
   2043 	 * necessary so that media with > 512Byte sector sizes work correctly.
   2044 	 */
   2045 	fsp->pcfs_fatstart = fsp->pcfs_dosstart + reserved;
   2046 	fsp->pcfs_rdirstart = fsp->pcfs_fatstart + fsp->pcfs_numfat * fatsec;
   2047 	fsp->pcfs_datastart = fsp->pcfs_rdirstart + rdirsec;
   2048 	datasec = totsec -
   2049 	    (blkcnt_t)fatsec * fsp->pcfs_numfat -
   2050 	    (blkcnt_t)rdirsec -
   2051 	    (blkcnt_t)reserved;
   2052 
   2053 	DTRACE_PROBE4(fatgeometry,
   2054 	    blkcnt_t, totsec, size_t, fatsec,
   2055 	    size_t, rdirsec, blkcnt_t, datasec);
   2056 
   2057 	/*
   2058 	 * 'totsec' is taken directly from the BPB and guaranteed to fit
   2059 	 * into a 32bit unsigned integer. The calculation of 'datasec',
   2060 	 * on the other hand, could underflow for incorrect values in
   2061 	 * rdirsec/reserved/fatsec. Check for that.
   2062 	 * We also check that the BPB conforms to the FAT specification's
   2063 	 * requirement that either of the 16/32bit total sector counts
   2064 	 * must be zero.
   2065 	 */
   2066 	if (totsec != 0 &&
   2067 	    (totsec16 == totsec32 || totsec16 == 0 || totsec32 == 0) &&
   2068 	    datasec < totsec && datasec <= UINT32_MAX)
   2069 		validflags |= BPB_TOTSEC_OK;
   2070 
   2071 	if ((len_t)totsec * (len_t)secsize <= mediasize)
   2072 		validflags |= BPB_MEDIASZ_OK;
   2073 
   2074 	if (VALID_SECSIZE(secsize))
   2075 		validflags |= BPB_SECSIZE_OK;
   2076 	if (VALID_SPCL(fsp->pcfs_spcl))
   2077 		validflags |= BPB_SECPERCLUS_OK;
   2078 	if (VALID_CLSIZE(fsp->pcfs_clsize))
   2079 		validflags |= BPB_CLSIZE_OK;
   2080 	if (VALID_NUMFATS(fsp->pcfs_numfat))
   2081 		validflags |= BPB_NUMFAT_OK;
   2082 	if (VALID_RSVDSEC(reserved) && reserved < totsec)
   2083 		validflags |= BPB_RSVDSECCNT_OK;
   2084 	if (VALID_MEDIA(fsp->pcfs_mediadesc))
   2085 		validflags |= BPB_MEDIADESC_OK;
   2086 	if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
   2087 		validflags |= BPB_BOOTSIG16_OK;
   2088 	if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
   2089 		validflags |= BPB_BOOTSIG32_OK;
   2090 	if (VALID_FSTYPSTR16(bpb_FilSysType16(bpb)))
   2091 		validflags |= BPB_FSTYPSTR16_OK;
   2092 	if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
   2093 		validflags |= BPB_FSTYPSTR32_OK;
   2094 	if (VALID_OEMNAME(bpb_OEMName(bpb)))
   2095 		validflags |= BPB_OEMNAME_OK;
   2096 	if (bkbootsec > 0 && bkbootsec <= reserved && fsisec != bkbootsec)
   2097 		validflags |= BPB_BKBOOTSEC_OK;
   2098 	if (fsisec > 0 && fsisec <= reserved)
   2099 		validflags |= BPB_FSISEC_OK;
   2100 	if (VALID_JMPBOOT(bpb_jmpBoot(bpb)))
   2101 		validflags |= BPB_JMPBOOT_OK;
   2102 	if (VALID_FSVER32(bpb_get_FSVer32(bpb)))
   2103 		validflags |= BPB_FSVER_OK;
   2104 	if (VALID_VOLLAB(bpb_VolLab16(bpb)))
   2105 		validflags |= BPB_VOLLAB16_OK;
   2106 	if (VALID_VOLLAB(bpb_VolLab32(bpb)))
   2107 		validflags |= BPB_VOLLAB32_OK;
   2108 	if (VALID_EXTFLAGS(bpb_get_ExtFlags32(bpb)))
   2109 		validflags |= BPB_EXTFLAGS_OK;
   2110 
   2111 	/*
   2112 	 * Try to determine which FAT format to use.
   2113 	 *
   2114 	 * Calculate the number of clusters in order to determine
   2115 	 * the type of FAT we are looking at.  This is the only
   2116 	 * recommended way of determining FAT type, though there
   2117 	 * are other hints in the data, this is the best way.
   2118 	 *
   2119 	 * Since we let just about "anything" pass through this function
   2120 	 * without early exits, fence against divide-by-zero here.
   2121 	 *
   2122 	 * datasec was already validated against UINT32_MAX so we know
   2123 	 * the result will not overflow the 32bit calculation.
   2124 	 */
   2125 	if (fsp->pcfs_spcl)
   2126 		ncl = (uint32_t)datasec / fsp->pcfs_spcl;
   2127 	else
   2128 		ncl = 0;
   2129 
   2130 	fsp->pcfs_ncluster = ncl;
   2131 
   2132 	/*
   2133 	 * From the Microsoft FAT specification:
   2134 	 * In the following example, when it says <, it does not mean <=.
   2135 	 * Note also that the numbers are correct.  The first number for
   2136 	 * FAT12 is 4085; the second number for FAT16 is 65525. These numbers
   2137 	 * and the '<' signs are not wrong.
   2138 	 *
   2139 	 * We "specialdetect" the corner cases, and use at least one "extra"
   2140 	 * criterion to decide whether it's FAT16 or FAT32 if the cluster
   2141 	 * count is dangerously close to the boundaries.
   2142 	 */
   2143 
   2144 	if (ncl <= PCF_FIRSTCLUSTER) {
   2145 		type = FAT_UNKNOWN;
   2146 	} else if (ncl < 4085) {
   2147 		type = FAT12;
   2148 	} else if (ncl <= 4096) {
   2149 		type = FAT_QUESTIONABLE;
   2150 	} else if (ncl < 65525) {
   2151 		type = FAT16;
   2152 	} else if (ncl <= 65536) {
   2153 		type = FAT_QUESTIONABLE;
   2154 	} else if (ncl < PCF_LASTCLUSTER32) {
   2155 		type = FAT32;
   2156 	} else {
   2157 		type = FAT_UNKNOWN;
   2158 	}
   2159 
   2160 	DTRACE_PROBE4(parseBPB__initial,
   2161 	    struct pcfs *, fsp, unsigned char *, bpb,
   2162 	    int, validflags, fattype_t, type);
   2163 
   2164 recheck:
   2165 	fsp->pcfs_fatsec = fatsec;
   2166 
   2167 	/* Do some final sanity checks for each specific type of FAT */
   2168 	switch (type) {
   2169 		case FAT12:
   2170 			if (rec != 0)
   2171 				validflags |= BPB_ROOTENTCNT_OK;
   2172 			if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
   2173 			    bpb_get_TotSec16(bpb) == 0)
   2174 				validflags |= BPB_TOTSEC16_OK;
   2175 			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
   2176 			    bpb_get_TotSec32(bpb) == 0)
   2177 				validflags |= BPB_TOTSEC32_OK;
   2178 			if (bpb_get_FatSz16(bpb) == fatsec)
   2179 				validflags |= BPB_FATSZ16_OK;
   2180 			if (fatsec * secsize >= ncl * 3 / 2)
   2181 				validflags |= BPB_FATSZ_OK;
   2182 			if (ncl < 4085)
   2183 				validflags |= BPB_NCLUSTERS_OK;
   2184 
   2185 			fsp->pcfs_lastclmark = (PCF_LASTCLUSTER & 0xfff);
   2186 			fsp->pcfs_rootblksize =
   2187 			    fsp->pcfs_rdirsec * secsize;
   2188 			fsp->pcfs_fsistart = 0;
   2189 
   2190 			if ((validflags & FAT12_VALIDMSK) != FAT12_VALIDMSK)
   2191 				type = FAT_UNKNOWN;
   2192 			break;
   2193 		case FAT16:
   2194 			if (rec != 0)
   2195 				validflags |= BPB_ROOTENTCNT_OK;
   2196 			if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
   2197 			    bpb_get_TotSec16(bpb) == 0)
   2198 				validflags |= BPB_TOTSEC16_OK;
   2199 			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
   2200 			    bpb_get_TotSec32(bpb) == 0)
   2201 				validflags |= BPB_TOTSEC32_OK;
   2202 			if (bpb_get_FatSz16(bpb) == fatsec)
   2203 				validflags |= BPB_FATSZ16_OK;
   2204 			if (fatsec * secsize >= ncl * 2)
   2205 				validflags |= BPB_FATSZ_OK;
   2206 			if (ncl >= 4085 && ncl < 65525)
   2207 				validflags |= BPB_NCLUSTERS_OK;
   2208 
   2209 			fsp->pcfs_lastclmark = PCF_LASTCLUSTER;
   2210 			fsp->pcfs_rootblksize =
   2211 			    fsp->pcfs_rdirsec * secsize;
   2212 			fsp->pcfs_fsistart = 0;
   2213 
   2214 			if ((validflags & FAT16_VALIDMSK) != FAT16_VALIDMSK)
   2215 				type = FAT_UNKNOWN;
   2216 			break;
   2217 		case FAT32:
   2218 			if (rec == 0)
   2219 				validflags |= BPB_ROOTENTCNT_OK;
   2220 			if (bpb_get_TotSec16(bpb) == 0)
   2221 				validflags |= BPB_TOTSEC16_OK;
   2222 			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec)
   2223 				validflags |= BPB_TOTSEC32_OK;
   2224 			if (bpb_get_FatSz16(bpb) == 0)
   2225 				validflags |= BPB_FATSZ16_OK;
   2226 			if (bpb_get_FatSz32(bpb) == fatsec)
   2227 				validflags |= BPB_FATSZ32_OK;
   2228 			if (fatsec * secsize >= ncl * 4)
   2229 				validflags |= BPB_FATSZ_OK;
   2230 			if (ncl >= 65525 && ncl < PCF_LASTCLUSTER32)
   2231 				validflags |= BPB_NCLUSTERS_OK;
   2232 
   2233 			fsp->pcfs_lastclmark = PCF_LASTCLUSTER32;
   2234 			fsp->pcfs_rootblksize = fsp->pcfs_clsize;
   2235 			fsp->pcfs_fsistart = fsp->pcfs_dosstart + fsisec;
   2236 			if (validflags & BPB_FSISEC_OK)
   2237 				fsp->pcfs_flags |= PCFS_FSINFO_OK;
   2238 			fsp->pcfs_rootclnum = bpb_get_RootClus32(bpb);
   2239 			if (pc_validcl(fsp, fsp->pcfs_rootclnum))
   2240 				validflags |= BPB_ROOTCLUSTER_OK;
   2241 
   2242 			/*
   2243 			 * Current PCFS code only works if 'pcfs_rdirstart'
   2244 			 * contains the root cluster number on FAT32.
   2245 			 * That's a mis-use and would better be changed.
   2246 			 */
   2247 			fsp->pcfs_rdirstart = (daddr_t)fsp->pcfs_rootclnum;
   2248 
   2249 			if ((validflags & FAT32_VALIDMSK) != FAT32_VALIDMSK)
   2250 				type = FAT_UNKNOWN;
   2251 			break;
   2252 		case FAT_QUESTIONABLE:
   2253 			type = secondaryBPBChecks(fsp, bpb, secsize);
   2254 			goto recheck;
   2255 		default:
   2256 			ASSERT(type == FAT_UNKNOWN);
   2257 			break;
   2258 	}
   2259 
   2260 	ASSERT(type != FAT_QUESTIONABLE);
   2261 
   2262 	fsp->pcfs_fattype = type;
   2263 
   2264 	if (valid)
   2265 		*valid = validflags;
   2266 
   2267 	DTRACE_PROBE4(parseBPB__final,
   2268 	    struct pcfs *, fsp, unsigned char *, bpb,
   2269 	    int, validflags, fattype_t, type);
   2270 
   2271 	if (type != FAT_UNKNOWN) {
   2272 		ASSERT((secsize & (DEV_BSIZE - 1)) == 0);
   2273 		ASSERT(ISP2(secsize / DEV_BSIZE));
   2274 		return (1);
   2275 	}
   2276 
   2277 	return (0);
   2278 }
   2279 
   2280 
   2281 /*
   2282  * Detect the device's native block size (sector size).
   2283  *
   2284  * Test whether the device is:
   2285  *	- a floppy device from a known controller type via DKIOCINFO
   2286  *	- a real floppy using the fd(7d) driver and capable of fdio(7I) ioctls
   2287  *	- a PCMCIA sram memory card (pseudofloppy) using pcram(7d)
   2288  *	- a USB floppy drive (identified by drive geometry)
   2289  *
   2290  * Detecting a floppy will make PCFS metadata updates on such media synchronous,
   2291  * to minimize risks due to slow I/O and user hotplugging / device ejection.
   2292  *
   2293  * This might be a bit wasteful on kernel stack space; if anyone's
   2294  * bothered by this, kmem_alloc/kmem_free the ioctl arguments...
   2295  */
   2296 static void
   2297 pcfs_device_getinfo(struct pcfs *fsp)
   2298 {
   2299 	dev_t			rdev = fsp->pcfs_xdev;
   2300 	int			error;
   2301 	union {
   2302 		struct dk_minfo		mi;
   2303 		struct dk_cinfo		ci;
   2304 		struct dk_geom		gi;
   2305 		struct fd_char		fc;
   2306 	} arg;				/* save stackspace ... */
   2307 	intptr_t argp = (intptr_t)&arg;
   2308 	ldi_handle_t		lh;
   2309 	ldi_ident_t		li;
   2310 	int isfloppy, isremoveable, ishotpluggable;
   2311 	cred_t			*cr = CRED();
   2312 
   2313 	if (ldi_ident_from_dev(rdev, &li))
   2314 		goto out;
   2315 
   2316 	error = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, cr, &lh, li);
   2317 	ldi_ident_release(li);
   2318 	if (error)
   2319 		goto out;
   2320 
   2321 	/*
   2322 	 * Not sure if this could possibly happen. It'd be a bit like
   2323 	 * VOP_OPEN() changing the passed-in vnode ptr. We're just not
   2324 	 * expecting it, needs some thought if triggered ...
   2325 	 */
   2326 	ASSERT(fsp->pcfs_xdev == rdev);
   2327 
   2328 	/*
   2329 	 * Check for removeable/hotpluggable media.
   2330 	 */
   2331 	if (ldi_ioctl(lh, DKIOCREMOVABLE,
   2332 	    (intptr_t)&isremoveable, FKIOCTL, cr, NULL)) {
   2333 		isremoveable = 0;
   2334 	}
   2335 	if (ldi_ioctl(lh, DKIOCHOTPLUGGABLE,
   2336 	    (intptr_t)&ishotpluggable, FKIOCTL, cr, NULL)) {
   2337 		ishotpluggable = 0;
   2338 	}
   2339 
   2340 	/*
   2341 	 * Make sure we don't use "half-initialized" values if the ioctls fail.
   2342 	 */
   2343 	if (ldi_ioctl(lh, DKIOCGMEDIAINFO, argp, FKIOCTL, cr, NULL)) {
   2344 		bzero(&arg, sizeof (arg));
   2345 		fsp->pcfs_mediasize = 0;
   2346 	} else {
   2347 		fsp->pcfs_mediasize =
   2348 		    (len_t)arg.mi.dki_lbsize *
   2349 		    (len_t)arg.mi.dki_capacity;
   2350 	}
   2351 
   2352 	if (VALID_SECSIZE(arg.mi.dki_lbsize)) {
   2353 		if (fsp->pcfs_secsize == 0) {
   2354 			fsp->pcfs_secsize = arg.mi.dki_lbsize;
   2355 			fsp->pcfs_sdshift =
   2356 			    ddi_ffs(arg.mi.dki_lbsize / DEV_BSIZE) - 1;
   2357 		} else {
   2358 			PC_DPRINTF4(1, "!pcfs: autodetected media block size "
   2359 			    "%d, device (%x.%x), different from user-provided "
   2360 			    "%d. User override - ignoring autodetect result.\n",
   2361 			    arg.mi.dki_lbsize,
   2362 			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
   2363 			    fsp->pcfs_secsize);
   2364 		}
   2365 	} else if (arg.mi.dki_lbsize) {
   2366 		PC_DPRINTF3(1, "!pcfs: autodetected media block size "
   2367 		    "%d, device (%x.%x), invalid (not 512, 1024, 2048, 4096). "
   2368 		    "Ignoring autodetect result.\n",
   2369 		    arg.mi.dki_lbsize,
   2370 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev));
   2371 	}
   2372 
   2373 	/*
   2374 	 * We treat the following media types as a floppy by default.
   2375 	 */
   2376 	isfloppy =
   2377 	    (arg.mi.dki_media_type == DK_FLOPPY ||
   2378 	    arg.mi.dki_media_type == DK_ZIP ||
   2379 	    arg.mi.dki_media_type == DK_JAZ);
   2380 
   2381 	/*
   2382 	 * if this device understands fdio(7I) requests it's
   2383 	 * obviously a floppy drive.
   2384 	 */
   2385 	if (!isfloppy &&
   2386 	    !ldi_ioctl(lh, FDIOGCHAR, argp, FKIOCTL, cr, NULL))
   2387 		isfloppy = 1;
   2388 
   2389 	/*
   2390 	 * some devices (PCMCIA pseudofloppies) we like to treat
   2391 	 * as floppies, but they don't understand fdio(7I) requests.
   2392 	 */
   2393 	if (!isfloppy &&
   2394 	    !ldi_ioctl(lh, DKIOCINFO, argp, FKIOCTL, cr, NULL) &&
   2395 	    (arg.ci.dki_ctype == DKC_WDC2880 ||
   2396 	    arg.ci.dki_ctype == DKC_NCRFLOPPY ||
   2397 	    arg.ci.dki_ctype == DKC_SMSFLOPPY ||
   2398 	    arg.ci.dki_ctype == DKC_INTEL82077 ||
   2399 	    (arg.ci.dki_ctype == DKC_PCMCIA_MEM &&
   2400 	    arg.ci.dki_flags & DKI_PCMCIA_PFD)))
   2401 		isfloppy = 1;
   2402 
   2403 	/*
   2404 	 * This is the "final fallback" test - media with
   2405 	 * 2 heads and 80 cylinders are assumed to be floppies.
   2406 	 * This is normally true for USB floppy drives ...
   2407 	 */
   2408 	if (!isfloppy &&
   2409 	    !ldi_ioctl(lh, DKIOCGGEOM, argp, FKIOCTL, cr, NULL) &&
   2410 	    (arg.gi.dkg_ncyl == 80 && arg.gi.dkg_nhead == 2))
   2411 		isfloppy = 1;
   2412 
   2413 	/*
   2414 	 * This is similar to the "old" PCFS code that sets this flag
   2415 	 * just based on the media descriptor being 0xf8 (MD_FIXED).
   2416 	 * Should be re-worked. We really need some specialcasing for
   2417 	 * removeable media.
   2418 	 */
   2419 	if (!isfloppy) {
   2420 		fsp->pcfs_flags |= PCFS_NOCHK;
   2421 	}
   2422 
   2423 	/*
   2424 	 * We automatically disable access time updates if the medium is
   2425 	 * removeable and/or hotpluggable, and the admin did not explicitly
   2426 	 * request access time updates (via the "atime" mount option).
   2427 	 * The majority of flash-based media should fit this category.
   2428 	 * Minimizing write access extends the lifetime of your memory stick !
   2429 	 */
   2430 	if (!vfs_optionisset(fsp->pcfs_vfs, MNTOPT_ATIME, NULL) &&
   2431 	    (isremoveable || ishotpluggable | isfloppy)) {
   2432 		fsp->pcfs_flags |= PCFS_NOATIME;
   2433 	}
   2434 
   2435 	(void) ldi_close(lh, FREAD, cr);
   2436 out:
   2437 	if (fsp->pcfs_secsize == 0) {
   2438 		PC_DPRINTF3(1, "!pcfs: media block size autodetection "
   2439 		    "device (%x.%x) failed, no user-provided fallback. "
   2440 		    "Using %d bytes.\n",
   2441 		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
   2442 		    DEV_BSIZE);
   2443 		fsp->pcfs_secsize = DEV_BSIZE;
   2444 		fsp->pcfs_sdshift = 0;
   2445 	}
   2446 	ASSERT(fsp->pcfs_secsize % DEV_BSIZE == 0);
   2447 	ASSERT(VALID_SECSIZE(fsp->pcfs_secsize));
   2448 }
   2449 
   2450 /*
   2451  * Get the FAT type for the DOS medium.
   2452  *
   2453  * -------------------------
   2454  * According to Microsoft:
   2455  *   The FAT type one of FAT12, FAT16, or FAT32 is determined by the
   2456  * count of clusters on the volume and nothing else.
   2457  * -------------------------
   2458  *
   2459  */
   2460 static int
   2461 pc_getfattype(struct pcfs *fsp)
   2462 {
   2463 	int error = 0;
   2464 	buf_t *bp = NULL;
   2465 	struct vnode *devvp = fsp->pcfs_devvp;
   2466 	dev_t	dev = devvp->v_rdev;
   2467 
   2468 	/*
   2469 	 * Detect the native block size of the medium, and attempt to
   2470 	 * detect whether the medium is removeable.
   2471 	 * We do treat removeable media (floppies, PCMCIA memory cards,
   2472 	 * USB and FireWire disks) differently wrt. to the frequency
   2473 	 * and synchronicity of FAT updates.
   2474 	 * We need to know the media block size in order to be able to
   2475 	 * parse the partition table.
   2476 	 */
   2477 	pcfs_device_getinfo(fsp);
   2478 
   2479 	/*
   2480 	 * Unpartitioned media (floppies and some removeable devices)
   2481 	 * don't have a partition table, the FAT BPB is at disk block 0.
   2482 	 * Start out by reading block 0.
   2483 	 */
   2484 	fsp->pcfs_dosstart = 0;
   2485 	bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), fsp->pcfs_secsize);
   2486 
   2487 	if (error = geterror(bp))
   2488 		goto out;
   2489 
   2490 	/*
   2491 	 * If a logical drive number is requested, parse the partition table
   2492 	 * and attempt to locate it. Otherwise, proceed immediately to the
   2493 	 * BPB check. findTheDrive(), if successful, returns the disk block
   2494 	 * number where the requested partition starts in "startsec".
   2495 	 */
   2496 	if (fsp->pcfs_ldrive != 0) {
   2497 		PC_DPRINTF3(5, "!pcfs: pc_getfattype: using FDISK table on "
   2498 		    "device (%x,%x):%d to find BPB\n",
   2499 		    getmajor(dev), getminor(dev), fsp->pcfs_ldrive);
   2500 
   2501 		if (error = findTheDrive(fsp, &bp))
   2502 			goto out;
   2503 
   2504 		ASSERT(fsp->pcfs_dosstart != 0);
   2505 
   2506 		brelse(bp);
   2507 		bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
   2508 		    fsp->pcfs_secsize);
   2509 		if (error = geterror(bp))
   2510 			goto out;
   2511 	}
   2512 
   2513 	/*
   2514 	 * Validate the BPB and fill in the instance structure.
   2515 	 */
   2516 	if (!parseBPB(fsp, (uchar_t *)bp->b_un.b_addr, NULL)) {
   2517 		PC_DPRINTF4(1, "!pcfs: pc_getfattype: No FAT BPB on "
   2518 		    "device (%x.%x):%d, disk LBA %u\n",
   2519 		    getmajor(dev), getminor(dev), fsp->pcfs_ldrive,
   2520 		    (uint_t)pc_dbdaddr(fsp, fsp->pcfs_dosstart));
   2521 		error = EINVAL;
   2522 		goto out;
   2523 	}
   2524 
   2525 	ASSERT(fsp->pcfs_fattype != FAT_UNKNOWN);
   2526 
   2527 out:
   2528 	/*
   2529 	 * Release the buffer used
   2530 	 */
   2531 	if (bp != NULL)
   2532 		brelse(bp);
   2533 	return (error);
   2534 }
   2535 
   2536 
   2537 /*
   2538  * Get the file allocation table.
   2539  * If there is an old FAT, invalidate it.
   2540  */
   2541 int
   2542 pc_getfat(struct pcfs *fsp)
   2543 {
   2544 	struct buf *bp = NULL;
   2545 	uchar_t *fatp = NULL;
   2546 	uchar_t *fat_changemap = NULL;
   2547 	int error;
   2548 	int fat_changemapsize;
   2549 	int flags = 0;
   2550 	int nfat;
   2551 	int altfat_mustmatch = 0;
   2552 	int fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
   2553 
   2554 	if (fsp->pcfs_fatp) {
   2555 		/*
   2556 		 * There is a FAT in core.
   2557 		 * If there are open file pcnodes or we have modified it or
   2558 		 * it hasn't timed out yet use the in core FAT.
   2559 		 * Otherwise invalidate it and get a new one
   2560 		 */
   2561 #ifdef notdef
   2562 		if (fsp->pcfs_frefs ||
   2563 		    (fsp->pcfs_flags & PCFS_FATMOD) ||
   2564 		    (gethrestime_sec() < fsp->pcfs_fattime)) {
   2565 			return (0);
   2566 		} else {
   2567 			mutex_enter(&pcfslock);
   2568 			pc_invalfat(fsp);
   2569 			mutex_exit(&pcfslock);
   2570 		}
   2571 #endif /* notdef */
   2572 		return (0);
   2573 	}
   2574 
   2575 	/*
   2576 	 * Get FAT and check it for validity
   2577 	 */
   2578 	fatp = kmem_alloc(fatsize, KM_SLEEP);
   2579 	error = pc_readfat(fsp, fatp);
   2580 	if (error) {
   2581 		flags = B_ERROR;
   2582 		goto out;
   2583 	}
   2584 	fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1;
   2585 	fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP);
   2586 	fsp->pcfs_fatp = fatp;
   2587 	fsp->pcfs_fat_changemapsize = fat_changemapsize;
   2588 	fsp->pcfs_fat_changemap = fat_changemap;
   2589 
   2590 	/*
   2591 	 * The only definite signature check is that the
   2592 	 * media descriptor byte should match the first byte
   2593 	 * of the FAT block.
   2594 	 */
   2595 	if (fatp[0] != fsp->pcfs_mediadesc) {
   2596 		cmn_err(CE_NOTE, "!pcfs: FAT signature mismatch, "
   2597 		    "media descriptor %x, FAT[0] lowbyte %x\n",
   2598 		    (uint32_t)fsp->pcfs_mediadesc, (uint32_t)fatp[0]);
   2599 		cmn_err(CE_NOTE, "!pcfs: Enforcing alternate FAT validation\n");
   2600 		altfat_mustmatch = 1;
   2601 	}
   2602 
   2603 	/*
   2604 	 * Get alternate FATs and check for consistency
   2605 	 * This is an inlined version of pc_readfat().
   2606 	 * Since we're only comparing FAT and alternate FAT,
   2607 	 * there's no reason to let pc_readfat() copy data out
   2608 	 * of the buf. Instead, compare in-situ, one cluster
   2609 	 * at a time.
   2610 	 */
   2611 	for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) {
   2612 		size_t startsec;
   2613 		size_t off;
   2614 
   2615 		startsec = pc_dbdaddr(fsp,
   2616 		    fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec);
   2617 
   2618 		for (off = 0; off < fatsize; off += fsp->pcfs_clsize) {
   2619 			daddr_t fatblk = startsec + pc_dbdaddr(fsp,
   2620 			    pc_cltodb(fsp, pc_lblkno(fsp, off)));
   2621 
   2622 			bp = bread(fsp->pcfs_xdev, fatblk,
   2623 			    MIN(fsp->pcfs_clsize, fatsize - off));
   2624 			if (bp->b_flags & (B_ERROR | B_STALE)) {
   2625 				cmn_err(CE_NOTE,
   2626 				    "!pcfs: alternate FAT #%d (start LBA %p)"
   2627 				    " read error at offset %ld on device"
   2628 				    " (%x.%x):%d",
   2629 				    nfat, (void *)(uintptr_t)startsec, off,
   2630 				    getmajor(fsp->pcfs_xdev),
   2631 				    getminor(fsp->pcfs_xdev),
   2632 				    fsp->pcfs_ldrive);
   2633 				flags = B_ERROR;
   2634 				error = EIO;
   2635 				goto out;
   2636 			}
   2637 			bp->b_flags |= B_STALE | B_AGE;
   2638 			if (bcmp(bp->b_un.b_addr, fatp + off,
   2639 			    MIN(fsp->pcfs_clsize, fatsize - off))) {
   2640 				cmn_err(CE_NOTE,
   2641 				    "!pcfs: alternate FAT #%d (start LBA %p)"
   2642 				    " corrupted at offset %ld on device"
   2643 				    " (%x.%x):%d",
   2644 				    nfat, (void *)(uintptr_t)startsec, off,
   2645 				    getmajor(fsp->pcfs_xdev),
   2646 				    getminor(fsp->pcfs_xdev),
   2647 				    fsp->pcfs_ldrive);
   2648 				if (altfat_mustmatch) {
   2649 					flags = B_ERROR;
   2650 					error = EIO;
   2651 					goto out;
   2652 				}
   2653 			}
   2654 			brelse(bp);
   2655 			bp = NULL;	/* prevent double release */
   2656 		}
   2657 	}
   2658 
   2659 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
   2660 	fsp->pcfs_fatjustread = 1;
   2661 
   2662 	/*
   2663 	 * Retrieve FAT32 fsinfo sector.
   2664 	 * A failure to read this is not fatal to accessing the volume.
   2665 	 * It simply means operations that count or search free blocks
   2666 	 * will have to do a full FAT walk, vs. a possibly quicker lookup
   2667 	 * of the summary information.
   2668 	 * Hence, we log a message but return success overall after this point.
   2669 	 */
   2670 	if (IS_FAT32(fsp) && (fsp->pcfs_flags & PCFS_FSINFO_OK)) {
   2671 		struct fat_od_fsi *fsinfo_disk;
   2672 
   2673 		bp = bread(fsp->pcfs_xdev,
   2674 		    pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
   2675 		fsinfo_disk = (struct fat_od_fsi *)bp->b_un.b_addr;
   2676 		if (bp->b_flags & (B_ERROR | B_STALE) ||
   2677 		    !FSISIG_OK(fsinfo_disk)) {
   2678 			cmn_err(CE_NOTE,
   2679 			    "!pcfs: error reading fat32 fsinfo from "
   2680 			    "device (%x.%x):%d, block %lld",
   2681 			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
   2682 			    fsp->pcfs_ldrive,
   2683 			    (long long)pc_dbdaddr(fsp, fsp->pcfs_fsistart));
   2684 			fsp->pcfs_flags &= ~PCFS_FSINFO_OK;
   2685 			fsp->pcfs_fsinfo.fs_free_clusters = FSINFO_UNKNOWN;
   2686 			fsp->pcfs_fsinfo.fs_next_free = FSINFO_UNKNOWN;
   2687 		} else {
   2688 			bp->b_flags |= B_STALE | B_AGE;
   2689 			fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
   2690 			fsp->pcfs_fsinfo.fs_free_clusters =
   2691 			    LE_32(fsinfo_disk->fsi_incore.fs_free_clusters);
   2692 			fsp->pcfs_fsinfo.fs_next_free =
   2693 			    LE_32(fsinfo_disk->fsi_incore.fs_next_free);
   2694 		}
   2695 		brelse(bp);
   2696 		bp = NULL;
   2697 	}
   2698 
   2699 	if (pc_validcl(fsp, (pc_cluster32_t)fsp->pcfs_fsinfo.fs_next_free))
   2700 		fsp->pcfs_nxfrecls = fsp->pcfs_fsinfo.fs_next_free;
   2701 	else
   2702 		fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
   2703 
   2704 	return (0);
   2705 
   2706 out:
   2707 	cmn_err(CE_NOTE, "!pcfs: illegal disk format");
   2708 	if (bp)
   2709 		brelse(bp);
   2710 	if (fatp)
   2711 		kmem_free(fatp, fatsize);
   2712 	if (fat_changemap)
   2713 		kmem_free(fat_changemap, fat_changemapsize);
   2714 
   2715 	if (flags) {
   2716 		pc_mark_irrecov(fsp);
   2717 	}
   2718 	return (error);
   2719 }
   2720