xref: /illumos-gate/usr/src/uts/common/fs/pcfs/pc_vfsops.c (revision 80a3d255)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
55a59a8b3Srsb  * Common Development and Distribution License (the "License").
65a59a8b3Srsb  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21342440ecSPrasad Singamsetty 
227c478bd9Sstevel@tonic-gate /*
2369ed0c8eSGarrett D'Amore  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
26d9a54dd1SSebastien Roy /*
27d9a54dd1SSebastien Roy  * Copyright (c) 2017 by Delphix. All rights reserved.
28d9a54dd1SSebastien Roy  */
307c478bd9Sstevel@tonic-gate #include <sys/param.h>
317c478bd9Sstevel@tonic-gate #include <sys/systm.h>
327c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
337c478bd9Sstevel@tonic-gate #include <sys/user.h>
347c478bd9Sstevel@tonic-gate #include <sys/proc.h>
357c478bd9Sstevel@tonic-gate #include <sys/cred.h>
367c478bd9Sstevel@tonic-gate #include <sys/disp.h>
377c478bd9Sstevel@tonic-gate #include <sys/buf.h>
387c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
39aa59c4cbSrsb #include <sys/vfs_opreg.h>
407c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
417c478bd9Sstevel@tonic-gate #include <sys/fdio.h>
427c478bd9Sstevel@tonic-gate #include <sys/file.h>
437c478bd9Sstevel@tonic-gate #include <sys/uio.h>
447c478bd9Sstevel@tonic-gate #include <sys/conf.h>
457c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
467c478bd9Sstevel@tonic-gate #include <sys/mount.h>
477c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
487c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
497c478bd9Sstevel@tonic-gate #include <sys/debug.h>
507c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
517c478bd9Sstevel@tonic-gate #include <sys/conf.h>
527c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
537c478bd9Sstevel@tonic-gate #include <sys/swap.h>
547c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
557c478bd9Sstevel@tonic-gate #include <sys/sunldi.h>
567c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
577c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h>
587c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
597c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
607c478bd9Sstevel@tonic-gate #include <sys/fs/pc_node.h>
617c478bd9Sstevel@tonic-gate #include <fs/fs_subr.h>
627c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
637c478bd9Sstevel@tonic-gate #include <sys/dkio.h>
647c478bd9Sstevel@tonic-gate #include <sys/open.h>
657c478bd9Sstevel@tonic-gate #include <sys/mntent.h>
667c478bd9Sstevel@tonic-gate #include <sys/policy.h>
67264a6e74Sfrankho #include <sys/atomic.h>
68f127cb91Sfrankho #include <sys/sdt.h>
707c478bd9Sstevel@tonic-gate /*
717c478bd9Sstevel@tonic-gate  * The majority of PC media use a 512 sector size, but
727c478bd9Sstevel@tonic-gate  * occasionally you will run across a 1k sector size.
737c478bd9Sstevel@tonic-gate  * For media with a 1k sector size, fd_strategy() requires
747c478bd9Sstevel@tonic-gate  * the I/O size to be a 1k multiple; so when the sector size
757c478bd9Sstevel@tonic-gate  * is not yet known, always read 1k.
767c478bd9Sstevel@tonic-gate  */
777c478bd9Sstevel@tonic-gate #define	PC_SAFESECSIZE	(PC_SECSIZE * 2)
7990c30842Sjmcp static int pcfs_pseudo_floppy(dev_t);
817c478bd9Sstevel@tonic-gate static int pcfsinit(int, char *);
827c478bd9Sstevel@tonic-gate static int pcfs_mount(struct vfs *, struct vnode *, struct mounta *,
837c478bd9Sstevel@tonic-gate 	struct cred *);
847c478bd9Sstevel@tonic-gate static int pcfs_unmount(struct vfs *, int, struct cred *);
857c478bd9Sstevel@tonic-gate static int pcfs_root(struct vfs *, struct vnode **);
867c478bd9Sstevel@tonic-gate static int pcfs_statvfs(struct vfs *, struct statvfs64 *);
877c478bd9Sstevel@tonic-gate static int pc_syncfsnodes(struct pcfs *);
887c478bd9Sstevel@tonic-gate static int pcfs_sync(struct vfs *, short, struct cred *);
897c478bd9Sstevel@tonic-gate static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp);
90264a6e74Sfrankho static void pcfs_freevfs(vfs_t *vfsp);
92f127cb91Sfrankho static int pc_readfat(struct pcfs *fsp, uchar_t *fatp);
937c478bd9Sstevel@tonic-gate static int pc_writefat(struct pcfs *fsp, daddr_t start);
95f127cb91Sfrankho static int pc_getfattype(struct pcfs *fsp);
969e003ac4Sgd static void pcfs_parse_mntopts(struct pcfs *fsp);
997c478bd9Sstevel@tonic-gate /*
1007c478bd9Sstevel@tonic-gate  * pcfs mount options table
1017c478bd9Sstevel@tonic-gate  */
103264a6e74Sfrankho static char *nohidden_cancel[] = { MNTOPT_PCFS_HIDDEN, NULL };
104264a6e74Sfrankho static char *hidden_cancel[] = { MNTOPT_PCFS_NOHIDDEN, NULL };
105264a6e74Sfrankho static char *nofoldcase_cancel[] = { MNTOPT_PCFS_FOLDCASE, NULL };
106264a6e74Sfrankho static char *foldcase_cancel[] = { MNTOPT_PCFS_NOFOLDCASE, NULL };
107264a6e74Sfrankho static char *clamptime_cancel[] = { MNTOPT_PCFS_NOCLAMPTIME, NULL };
108264a6e74Sfrankho static char *noclamptime_cancel[] = { MNTOPT_PCFS_CLAMPTIME, NULL };
109f127cb91Sfrankho static char *atime_cancel[] = { MNTOPT_NOATIME, NULL };
110f127cb91Sfrankho static char *noatime_cancel[] = { MNTOPT_ATIME, NULL };
1127c478bd9Sstevel@tonic-gate static mntopt_t mntopts[] = {
1137c478bd9Sstevel@tonic-gate /*
114264a6e74Sfrankho  *	option name	cancel option	default arg	flags	opt data
1157c478bd9Sstevel@tonic-gate  */
116264a6e74Sfrankho 	{ MNTOPT_PCFS_NOHIDDEN, nohidden_cancel, NULL, 0, NULL },
117264a6e74Sfrankho 	{ MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, MO_DEFAULT, NULL },
118264a6e74Sfrankho 	{ MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, NULL },
119264a6e74Sfrankho 	{ MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0, NULL },
120264a6e74Sfrankho 	{ MNTOPT_PCFS_CLAMPTIME, clamptime_cancel, NULL, MO_DEFAULT, NULL },
121*80a3d255SToomas Soome 	{ MNTOPT_PCFS_NOCLAMPTIME, noclamptime_cancel, NULL, 0, NULL },
122*80a3d255SToomas Soome 	{ MNTOPT_NOATIME, noatime_cancel, NULL, 0, NULL },
123*80a3d255SToomas Soome 	{ MNTOPT_ATIME, atime_cancel, NULL, 0, NULL },
1267c478bd9Sstevel@tonic-gate };
1287c478bd9Sstevel@tonic-gate static mntopts_t pcfs_mntopts = {
1297c478bd9Sstevel@tonic-gate 	sizeof (mntopts) / sizeof (mntopt_t),
1307c478bd9Sstevel@tonic-gate 	mntopts
1317c478bd9Sstevel@tonic-gate };
1337c478bd9Sstevel@tonic-gate int pcfsdebuglevel = 0;
1357c478bd9Sstevel@tonic-gate /*
1367c478bd9Sstevel@tonic-gate  * pcfslock:	protects the list of mounted pc filesystems "pc_mounttab.
1377c478bd9Sstevel@tonic-gate  * pcfs_lock:	(inside per filesystem structure "pcfs")
1387c478bd9Sstevel@tonic-gate  *		per filesystem lock. Most of the vfsops and vnodeops are
1397c478bd9Sstevel@tonic-gate  *		protected by this lock.
1407c478bd9Sstevel@tonic-gate  * pcnodes_lock: protects the pcnode hash table "pcdhead", "pcfhead".
1417c478bd9Sstevel@tonic-gate  *
1427c478bd9Sstevel@tonic-gate  * Lock hierarchy: pcfslock > pcfs_lock > pcnodes_lock
143264a6e74Sfrankho  *
144264a6e74Sfrankho  * pcfs_mountcount:	used to prevent module unloads while there is still
145264a6e74Sfrankho  *			pcfs state from a former mount hanging around. With
146264a6e74Sfrankho  *			forced umount support, the filesystem module must not
147264a6e74Sfrankho  *			be allowed to go away before the last VFS_FREEVFS()
148264a6e74Sfrankho  *			call has been made.
149264a6e74Sfrankho  *			Since this is just an atomic counter, there's no need
150264a6e74Sfrankho  *			for locking.
1517c478bd9Sstevel@tonic-gate  */
1527c478bd9Sstevel@tonic-gate kmutex_t	pcfslock;
153264a6e74Sfrankho krwlock_t	pcnodes_lock;
154264a6e74Sfrankho uint32_t	pcfs_mountcount;
1567c478bd9Sstevel@tonic-gate static int pcfstype;
1587c478bd9Sstevel@tonic-gate static vfsdef_t vfw = {
1597c478bd9Sstevel@tonic-gate 	VFSDEF_VERSION,
1607c478bd9Sstevel@tonic-gate 	"pcfs",
1617c478bd9Sstevel@tonic-gate 	pcfsinit,
1637c478bd9Sstevel@tonic-gate 	&pcfs_mntopts
1647c478bd9Sstevel@tonic-gate };
1667c478bd9Sstevel@tonic-gate extern struct mod_ops mod_fsops;
1687c478bd9Sstevel@tonic-gate static struct modlfs modlfs = {
1697c478bd9Sstevel@tonic-gate 	&mod_fsops,
170c2aaf90fSgd 	"PC filesystem",
1717c478bd9Sstevel@tonic-gate 	&vfw
1727c478bd9Sstevel@tonic-gate };
1747c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
1757c478bd9Sstevel@tonic-gate 	MODREV_1,
1767c478bd9Sstevel@tonic-gate 	&modlfs,
1777c478bd9Sstevel@tonic-gate 	NULL
1787c478bd9Sstevel@tonic-gate };
1807c478bd9Sstevel@tonic-gate int
1817c478bd9Sstevel@tonic-gate _init(void)
1827c478bd9Sstevel@tonic-gate {
1837c478bd9Sstevel@tonic-gate 	int	error;
1857c478bd9Sstevel@tonic-gate #if !defined(lint)
1867c478bd9Sstevel@tonic-gate 	/* make sure the on-disk structures are sane */
1877c478bd9Sstevel@tonic-gate 	ASSERT(sizeof (struct pcdir) == 32);
1887c478bd9Sstevel@tonic-gate 	ASSERT(sizeof (struct pcdir_lfn) == 32);
1897c478bd9Sstevel@tonic-gate #endif
1907c478bd9Sstevel@tonic-gate 	mutex_init(&pcfslock, NULL, MUTEX_DEFAULT, NULL);
1917c478bd9Sstevel@tonic-gate 	rw_init(&pcnodes_lock, NULL, RW_DEFAULT, NULL);
1927c478bd9Sstevel@tonic-gate 	error = mod_install(&modlinkage);
1937c478bd9Sstevel@tonic-gate 	if (error) {
1947c478bd9Sstevel@tonic-gate 		mutex_destroy(&pcfslock);
1957c478bd9Sstevel@tonic-gate 		rw_destroy(&pcnodes_lock);
1967c478bd9Sstevel@tonic-gate 	}
1977c478bd9Sstevel@tonic-gate 	return (error);
1987c478bd9Sstevel@tonic-gate }
2007c478bd9Sstevel@tonic-gate int
2017c478bd9Sstevel@tonic-gate _fini(void)
2027c478bd9Sstevel@tonic-gate {
2037c478bd9Sstevel@tonic-gate 	int	error;
205264a6e74Sfrankho 	/*
206264a6e74Sfrankho 	 * If a forcedly unmounted instance is still hanging around,
207264a6e74Sfrankho 	 * we cannot allow the module to be unloaded because that would
208264a6e74Sfrankho 	 * cause panics once the VFS framework decides it's time to call
209264a6e74Sfrankho 	 * into VFS_FREEVFS().
210264a6e74Sfrankho 	 */
211264a6e74Sfrankho 	if (pcfs_mountcount)
212264a6e74Sfrankho 		return (EBUSY);
2147c478bd9Sstevel@tonic-gate 	error = mod_remove(&modlinkage);
2157c478bd9Sstevel@tonic-gate 	if (error)
2167c478bd9Sstevel@tonic-gate 		return (error);
2177c478bd9Sstevel@tonic-gate 	mutex_destroy(&pcfslock);
2187c478bd9Sstevel@tonic-gate 	rw_destroy(&pcnodes_lock);
2197c478bd9Sstevel@tonic-gate 	/*
2207c478bd9Sstevel@tonic-gate 	 * Tear down the operations vectors
2217c478bd9Sstevel@tonic-gate 	 */
2227c478bd9Sstevel@tonic-gate 	(void) vfs_freevfsops_by_type(pcfstype);
2237c478bd9Sstevel@tonic-gate 	vn_freevnodeops(pcfs_fvnodeops);
2247c478bd9Sstevel@tonic-gate 	vn_freevnodeops(pcfs_dvnodeops);
2257c478bd9Sstevel@tonic-gate 	return (0);
2267c478bd9Sstevel@tonic-gate }
2287c478bd9Sstevel@tonic-gate int
2297c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
2307c478bd9Sstevel@tonic-gate {
2317c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2327c478bd9Sstevel@tonic-gate }
2347c478bd9Sstevel@tonic-gate /* ARGSUSED1 */
2357c478bd9Sstevel@tonic-gate static int
2367c478bd9Sstevel@tonic-gate pcfsinit(int fstype, char *name)
2377c478bd9Sstevel@tonic-gate {
2387c478bd9Sstevel@tonic-gate 	static const fs_operation_def_t pcfs_vfsops_template[] = {
239aa59c4cbSrsb 		VFSNAME_MOUNT,		{ .vfs_mount = pcfs_mount },
240aa59c4cbSrsb 		VFSNAME_UNMOUNT,	{ .vfs_unmount = pcfs_unmount },
241aa59c4cbSrsb 		VFSNAME_ROOT,		{ .vfs_root = pcfs_root },
242aa59c4cbSrsb 		VFSNAME_STATVFS,	{ .vfs_statvfs = pcfs_statvfs },
243aa59c4cbSrsb 		VFSNAME_SYNC,		{ .vfs_sync = pcfs_sync },
244aa59c4cbSrsb 		VFSNAME_VGET,		{ .vfs_vget = pcfs_vget },
245aa59c4cbSrsb 		VFSNAME_FREEVFS,	{ .vfs_freevfs = pcfs_freevfs },
246aa59c4cbSrsb 		NULL,			NULL
2477c478bd9Sstevel@tonic-gate 	};
2487c478bd9Sstevel@tonic-gate 	int error;
2507c478bd9Sstevel@tonic-gate 	error = vfs_setfsops(fstype, pcfs_vfsops_template, NULL);
2517c478bd9Sstevel@tonic-gate 	if (error != 0) {
2527c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad vfs ops template");
2537c478bd9Sstevel@tonic-gate 		return (error);
2547c478bd9Sstevel@tonic-gate 	}
2567c478bd9Sstevel@tonic-gate 	error = vn_make_ops("pcfs", pcfs_fvnodeops_template, &pcfs_fvnodeops);
2577c478bd9Sstevel@tonic-gate 	if (error != 0) {
2587c478bd9Sstevel@tonic-gate 		(void) vfs_freevfsops_by_type(fstype);
2597c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad file vnode ops template");
2607c478bd9Sstevel@tonic-gate 		return (error);
2617c478bd9Sstevel@tonic-gate 	}
2637c478bd9Sstevel@tonic-gate 	error = vn_make_ops("pcfsd", pcfs_dvnodeops_template, &pcfs_dvnodeops);
2647c478bd9Sstevel@tonic-gate 	if (error != 0) {
2657c478bd9Sstevel@tonic-gate 		(void) vfs_freevfsops_by_type(fstype);
2667c478bd9Sstevel@tonic-gate 		vn_freevnodeops(pcfs_fvnodeops);
2677c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcfsinit: bad dir vnode ops template");
2687c478bd9Sstevel@tonic-gate 		return (error);
2697c478bd9Sstevel@tonic-gate 	}
2717c478bd9Sstevel@tonic-gate 	pcfstype = fstype;
2727c478bd9Sstevel@tonic-gate 	(void) pc_init();
273264a6e74Sfrankho 	pcfs_mountcount = 0;
2747c478bd9Sstevel@tonic-gate 	return (0);
2757c478bd9Sstevel@tonic-gate }
2777c478bd9Sstevel@tonic-gate static struct pcfs *pc_mounttab = NULL;
2797c478bd9Sstevel@tonic-gate extern struct pcfs_args pc_tz;
2817c478bd9Sstevel@tonic-gate /*
2827c478bd9Sstevel@tonic-gate  *  Define some special logical drives we use internal to this file.
2837c478bd9Sstevel@tonic-gate  */
2847c478bd9Sstevel@tonic-gate #define	BOOT_PARTITION_DRIVE	99
2857c478bd9Sstevel@tonic-gate #define	PRIMARY_DOS_DRIVE	1
286f127cb91Sfrankho #define	UNPARTITIONED_DRIVE	0
2887c478bd9Sstevel@tonic-gate static int
289f127cb91Sfrankho pcfs_device_identify(
2907c478bd9Sstevel@tonic-gate 	struct vfs *vfsp,
2917c478bd9Sstevel@tonic-gate 	struct mounta *uap,
292f127cb91Sfrankho 	struct cred *cr,
293f127cb91Sfrankho 	int *dos_ldrive,
294f127cb91Sfrankho 	dev_t *xdev)
2957c478bd9Sstevel@tonic-gate {
2967c478bd9Sstevel@tonic-gate 	struct pathname special;
2979bd42341Sfrankho 	char *c;
29893239addSjohnlev 	struct vnode *svp = NULL;
29993239addSjohnlev 	struct vnode *lvp = NULL;
3007c478bd9Sstevel@tonic-gate 	int oflag, aflag;
301f127cb91Sfrankho 	int error;
3037c478bd9Sstevel@tonic-gate 	/*
3047c478bd9Sstevel@tonic-gate 	 * Resolve path name of special file being mounted.
3057c478bd9Sstevel@tonic-gate 	 */
3067c478bd9Sstevel@tonic-gate 	if (error = pn_get(uap->spec, UIO_USERSPACE, &special)) {
3077c478bd9Sstevel@tonic-gate 		return (error);
3087c478bd9Sstevel@tonic-gate 	}
310f127cb91Sfrankho 	*dos_ldrive = -1;
3127c478bd9Sstevel@tonic-gate 	if (error =
31393239addSjohnlev 	    lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &svp)) {
314f127cb91Sfrankho 		/*
315f127cb91Sfrankho 		 * If there's no device node, the name specified most likely
316f127cb91Sfrankho 		 * maps to a PCFS-style "partition specifier" to select a
317f127cb91Sfrankho 		 * harddisk primary/logical partition. Disable floppy-specific
318f127cb91Sfrankho 		 * checks in such cases unless an explicit :A or :B is
319f127cb91Sfrankho 		 * requested.
320f127cb91Sfrankho 		 */
3227c478bd9Sstevel@tonic-gate 		/*
3239bd42341Sfrankho 		 * Split the pathname string at the last ':' separator.
3249bd42341Sfrankho 		 * If there's no ':' in the device name, or the ':' is the
3259bd42341Sfrankho 		 * last character in the string, the name is invalid and
3269bd42341Sfrankho 		 * the error from the previous lookup will be returned.
3277c478bd9Sstevel@tonic-gate 		 */
3289bd42341Sfrankho 		c = strrchr(special.pn_path, ':');
3299bd42341Sfrankho 		if (c == NULL || strlen(c) == 0)
3309bd42341Sfrankho 			goto devlookup_done;
3329bd42341Sfrankho 		*c++ = '\0';
3349bd42341Sfrankho 		/*
3359bd42341Sfrankho 		 * PCFS partition name suffixes can be:
3369bd42341Sfrankho 		 *	- "boot" to indicate the X86BOOT partition
3379bd42341Sfrankho 		 *	- a drive letter [c-z] for the "DOS logical drive"
3389bd42341Sfrankho 		 *	- a drive number 1..24 for the "DOS logical drive"
339f127cb91Sfrankho 		 *	- a "floppy name letter", 'a' or 'b' (just strip this)
3409bd42341Sfrankho 		 */
3419bd42341Sfrankho 		if (strcasecmp(c, "boot") == 0) {
3429bd42341Sfrankho 			/*
3439bd42341Sfrankho 			 * The Solaris boot partition is requested.
3449bd42341Sfrankho 			 */
345f127cb91Sfrankho 			*dos_ldrive = BOOT_PARTITION_DRIVE;
3469bd42341Sfrankho 		} else if (strspn(c, "0123456789") == strlen(c)) {
3479bd42341Sfrankho 			/*
3489bd42341Sfrankho 			 * All digits - parse the partition number.
3499bd42341Sfrankho 			 */
3509bd42341Sfrankho 			long drvnum = 0;
3529bd42341Sfrankho 			if ((error = ddi_strtol(c, NULL, 10, &drvnum)) == 0) {
3537c478bd9Sstevel@tonic-gate 				/*
3549bd42341Sfrankho 				 * A number alright - in the allowed range ?
3557c478bd9Sstevel@tonic-gate 				 */
3569bd42341Sfrankho 				if (drvnum > 24 || drvnum == 0)
357f127cb91Sfrankho 					error = ENXIO;
3587c478bd9Sstevel@tonic-gate 			}
3599bd42341Sfrankho 			if (error)
3609bd42341Sfrankho 				goto devlookup_done;
361f127cb91Sfrankho 			*dos_ldrive = (int)drvnum;
3629bd42341Sfrankho 		} else if (strlen(c) == 1) {
3639bd42341Sfrankho 			/*
364f127cb91Sfrankho 			 * A single trailing character was specified.
365f127cb91Sfrankho 			 *	- [c-zC-Z] means a harddisk partition, and
366f127cb91Sfrankho 			 *	  we retrieve the partition number.
367f127cb91Sfrankho 			 *	- [abAB] means a floppy drive, so we swallow
368f127cb91Sfrankho 			 *	  the "drive specifier" and test later
36969ed0c8eSGarrett D'Amore 			 *	  whether the physical device is a floppy.
3709bd42341Sfrankho 			 */
3719bd42341Sfrankho 			*c = tolower(*c);
372f127cb91Sfrankho 			if (*c == 'a' || *c == 'b') {
373f127cb91Sfrankho 				*dos_ldrive = UNPARTITIONED_DRIVE;
374f127cb91Sfrankho 			} else if (*c < 'c' || *c > 'z') {
375f127cb91Sfrankho 				error = ENXIO;
3769bd42341Sfrankho 				goto devlookup_done;
377f127cb91Sfrankho 			} else {
378f127cb91Sfrankho 				*dos_ldrive = 1 + *c - 'c';
3799bd42341Sfrankho 			}
3809bd42341Sfrankho 		} else {
3819bd42341Sfrankho 			/*
3829bd42341Sfrankho 			 * Can't parse this - pass through previous error.
3839bd42341Sfrankho 			 */
3849bd42341Sfrankho 			goto devlookup_done;
3857c478bd9Sstevel@tonic-gate 		}
3889bd42341Sfrankho 		error = lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW,
38993239addSjohnlev 		    NULLVPP, &svp);
390f127cb91Sfrankho 	} else {
391f127cb91Sfrankho 		*dos_ldrive = UNPARTITIONED_DRIVE;
3927c478bd9Sstevel@tonic-gate 	}
3939bd42341Sfrankho devlookup_done:
3947c478bd9Sstevel@tonic-gate 	pn_free(&special);
3959bd42341Sfrankho 	if (error)
3969bd42341Sfrankho 		return (error);
398f127cb91Sfrankho 	ASSERT(*dos_ldrive >= UNPARTITIONED_DRIVE);
4007c478bd9Sstevel@tonic-gate 	/*
4017c478bd9Sstevel@tonic-gate 	 * Verify caller's permission to open the device special file.
4027c478bd9Sstevel@tonic-gate 	 */
4037c478bd9Sstevel@tonic-gate 	if ((vfsp->vfs_flag & VFS_RDONLY) != 0 ||
4047c478bd9Sstevel@tonic-gate 	    ((uap->flags & MS_RDONLY) != 0)) {
4057c478bd9Sstevel@tonic-gate 		oflag = FREAD;
4067c478bd9Sstevel@tonic-gate 		aflag = VREAD;
4077c478bd9Sstevel@tonic-gate 	} else {
4087c478bd9Sstevel@tonic-gate 		oflag = FREAD | FWRITE;
4097c478bd9Sstevel@tonic-gate 		aflag = VREAD | VWRITE;
4107c478bd9Sstevel@tonic-gate 	}
41293239addSjohnlev 	error = vfs_get_lofi(vfsp, &lvp);
41493239addSjohnlev 	if (error > 0) {
41593239addSjohnlev 		if (error == ENOENT)
41693239addSjohnlev 			error = ENODEV;
41793239addSjohnlev 		goto out;
41893239addSjohnlev 	} else if (error == 0) {
41993239addSjohnlev 		*xdev = lvp->v_rdev;
42093239addSjohnlev 	} else {
42193239addSjohnlev 		*xdev = svp->v_rdev;
423ecffa4d6SJohn Levon 		if (svp->v_type != VBLK) {
42493239addSjohnlev 			error = ENOTBLK;
425ecffa4d6SJohn Levon 			goto out;
426ecffa4d6SJohn Levon 		}
42893239addSjohnlev 		if ((error = secpolicy_spec_open(cr, svp, oflag)) != 0)
42993239addSjohnlev 			goto out;
4307c478bd9Sstevel@tonic-gate 	}
43293239addSjohnlev 	if (getmajor(*xdev) >= devcnt) {
43393239addSjohnlev 		error = ENXIO;
43493239addSjohnlev 		goto out;
43593239addSjohnlev 	}
43793239addSjohnlev 	if ((error = VOP_ACCESS(svp, aflag, 0, cr, NULL)) != 0)
43893239addSjohnlev 		goto out;
44093239addSjohnlev out:
44193239addSjohnlev 	if (svp != NULL)
44293239addSjohnlev 		VN_RELE(svp);
44393239addSjohnlev 	if (lvp != NULL)
44493239addSjohnlev 		VN_RELE(lvp);
44593239addSjohnlev 	return (error);
446f127cb91Sfrankho }
448f127cb91Sfrankho static int
449f127cb91Sfrankho pcfs_device_ismounted(
450f127cb91Sfrankho 	struct vfs *vfsp,
451f127cb91Sfrankho 	int dos_ldrive,
452f127cb91Sfrankho 	dev_t xdev,
453f127cb91Sfrankho 	int *remounting,
454f127cb91Sfrankho 	dev_t *pseudodev)
455f127cb91Sfrankho {
456f127cb91Sfrankho 	struct pcfs *fsp;
457f127cb91Sfrankho 	int remount = *remounting;
4597c478bd9Sstevel@tonic-gate 	/*
4609bd42341Sfrankho 	 * Ensure that this logical drive isn't already mounted, unless
4619bd42341Sfrankho 	 * this is a REMOUNT request.
4629bd42341Sfrankho 	 * Note: The framework will perform this check if the "...:c"
4639bd42341Sfrankho 	 * PCFS-style "logical drive" syntax has not been used and an
4649bd42341Sfrankho 	 * actually existing physical device is backing this filesystem.
465f127cb91Sfrankho 	 * Once all block device drivers support PC-style partitioning,
466f127cb91Sfrankho 	 * this codeblock can be dropped.
4677c478bd9Sstevel@tonic-gate 	 */
468f127cb91Sfrankho 	*pseudodev = xdev;
4707c478bd9Sstevel@tonic-gate 	if (dos_ldrive) {
4717c478bd9Sstevel@tonic-gate 		mutex_enter(&pcfslock);
4727c478bd9Sstevel@tonic-gate 		for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt)
4737c478bd9Sstevel@tonic-gate 			if (fsp->pcfs_xdev == xdev &&
474f127cb91Sfrankho 			    fsp->pcfs_ldrive == dos_ldrive) {
4757c478bd9Sstevel@tonic-gate 				mutex_exit(&pcfslock);
476f127cb91Sfrankho 				if (remount) {
4777c478bd9Sstevel@tonic-gate 					return (0);
4787c478bd9Sstevel@tonic-gate 				} else {
4797c478bd9Sstevel@tonic-gate 					return (EBUSY);
4807c478bd9Sstevel@tonic-gate 				}
4817c478bd9Sstevel@tonic-gate 			}
4827c478bd9Sstevel@tonic-gate 		/*
4837c478bd9Sstevel@tonic-gate 		 * Assign a unique device number for the vfs
4847c478bd9Sstevel@tonic-gate 		 * The old way (getudev() + a constantly incrementing
4857c478bd9Sstevel@tonic-gate 		 * major number) was wrong because it changes vfs_dev
4867c478bd9Sstevel@tonic-gate 		 * across mounts and reboots, which breaks nfs file handles.
4877c478bd9Sstevel@tonic-gate 		 * UFS just uses the real dev_t. We can't do that because
4887c478bd9Sstevel@tonic-gate 		 * of the way pcfs opens fdisk partitons (the :c and :d
4897c478bd9Sstevel@tonic-gate 		 * partitions are on the same dev_t). Though that _might_
4907c478bd9Sstevel@tonic-gate 		 * actually be ok, since the file handle contains an
4917c478bd9Sstevel@tonic-gate 		 * absolute block number, it's probably better to make them
4927c478bd9Sstevel@tonic-gate 		 * different. So I think we should retain the original
4937c478bd9Sstevel@tonic-gate 		 * dev_t, but come up with a different minor number based
4947c478bd9Sstevel@tonic-gate 		 * on the logical drive that will _always_ come up the same.
4957c478bd9Sstevel@tonic-gate 		 * For now, we steal the upper 6 bits.
4967c478bd9Sstevel@tonic-gate 		 */
4977c478bd9Sstevel@tonic-gate #ifdef notdef
4987c478bd9Sstevel@tonic-gate 		/* what should we do here? */
4997c478bd9Sstevel@tonic-gate 		if (((getminor(xdev) >> 12) & 0x3F) != 0)
5007c478bd9Sstevel@tonic-gate 			printf("whoops - upper bits used!\n");
5017c478bd9Sstevel@tonic-gate #endif
502f127cb91Sfrankho 		*pseudodev = makedevice(getmajor(xdev),
503f127cb91Sfrankho 		    ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32);
504f127cb91Sfrankho 		if (vfs_devmounting(*pseudodev, vfsp)) {
5057c478bd9Sstevel@tonic-gate 			mutex_exit(&pcfslock);
5067c478bd9Sstevel@tonic-gate 			return (EBUSY);
5077c478bd9Sstevel@tonic-gate 		}
508f127cb91Sfrankho 		if (vfs_devismounted(*pseudodev)) {
5097c478bd9Sstevel@tonic-gate 			mutex_exit(&pcfslock);
510f127cb91Sfrankho 			if (remount) {
5117c478bd9Sstevel@tonic-gate 				return (0);
5127c478bd9Sstevel@tonic-gate 			} else {
5137c478bd9Sstevel@tonic-gate 				return (EBUSY);
5147c478bd9Sstevel@tonic-gate 			}
5157c478bd9Sstevel@tonic-gate 		}
5167c478bd9Sstevel@tonic-gate 		mutex_exit(&pcfslock);
5177c478bd9Sstevel@tonic-gate 	} else {
518f127cb91Sfrankho 		*pseudodev = xdev;
519f127cb91Sfrankho 		if (vfs_devmounting(*pseudodev, vfsp)) {
5207c478bd9Sstevel@tonic-gate 			return (EBUSY);
5217c478bd9Sstevel@tonic-gate 		}
522f127cb91Sfrankho 		if (vfs_devismounted(*pseudodev))
523f127cb91Sfrankho 			if (remount) {
5247c478bd9Sstevel@tonic-gate 				return (0);
5257c478bd9Sstevel@tonic-gate 			} else {
5267c478bd9Sstevel@tonic-gate 				return (EBUSY);
5277c478bd9Sstevel@tonic-gate 			}
5287c478bd9Sstevel@tonic-gate 	}
530f127cb91Sfrankho 	/*
531f127cb91Sfrankho 	 * This is not a remount. Even if MS_REMOUNT was requested,
532f127cb91Sfrankho 	 * the caller needs to proceed as it would on an ordinary
533f127cb91Sfrankho 	 * mount.
534f127cb91Sfrankho 	 */
535f127cb91Sfrankho 	*remounting = 0;
537f127cb91Sfrankho 	ASSERT(*pseudodev);
538f127cb91Sfrankho 	return (0);
539f127cb91Sfrankho }
541f127cb91Sfrankho /*
542f127cb91Sfrankho  * Get the PCFS-specific mount options from the VFS framework.
543f127cb91Sfrankho  * For "timezone" and "secsize", we need to parse the number
544f127cb91Sfrankho  * ourselves and ensure its validity.
545f127cb91Sfrankho  * Note: "secsize" is deliberately undocumented at this time,
546f127cb91Sfrankho  * it's a workaround for devices (particularly: lofi image files)
547f127cb91Sfrankho  * that don't support the DKIOCGMEDIAINFO ioctl for autodetection.
548f127cb91Sfrankho  */
549f127cb91Sfrankho static void
5509e003ac4Sgd pcfs_parse_mntopts(struct pcfs *fsp)
551f127cb91Sfrankho {
552f127cb91Sfrankho 	char *c;
553f127cb91Sfrankho 	char *endptr;
554f127cb91Sfrankho 	long l;
555f127cb91Sfrankho 	struct vfs *vfsp = fsp->pcfs_vfs;
557f127cb91Sfrankho 	ASSERT(fsp->pcfs_secondswest == 0);
558f127cb91Sfrankho 	ASSERT(fsp->pcfs_secsize == 0);
560f127cb91Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL))
561f127cb91Sfrankho 		fsp->pcfs_flags |= PCFS_HIDDEN;
562f127cb91Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL))
563f127cb91Sfrankho 		fsp->pcfs_flags |= PCFS_FOLDCASE;
564f127cb91Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL))
565f127cb91Sfrankho 		fsp->pcfs_flags |= PCFS_NOCLAMPTIME;
566f127cb91Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL))
567f127cb91Sfrankho 		fsp->pcfs_flags |= PCFS_NOATIME;
569f127cb91Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_TIMEZONE, &c)) {
570f127cb91Sfrankho 		if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
571f127cb91Sfrankho 		    endptr == c + strlen(c)) {
572f127cb91Sfrankho 			/*
573f127cb91Sfrankho 			 * A number alright - in the allowed range ?
574f127cb91Sfrankho 			 */
575f127cb91Sfrankho 			if (l <= -12*3600 || l >= 12*3600) {
576f127cb91Sfrankho 				cmn_err(CE_WARN, "!pcfs: invalid use of "
577f127cb91Sfrankho 				    "'timezone' mount option - %ld "
578f127cb91Sfrankho 				    "is out of range. Assuming 0.", l);
579f127cb91Sfrankho 				l = 0;
580f127cb91Sfrankho 			}
581f127cb91Sfrankho 		} else {
582f127cb91Sfrankho 			cmn_err(CE_WARN, "!pcfs: invalid use of "
583f127cb91Sfrankho 			    "'timezone' mount option - argument %s "
584f127cb91Sfrankho 			    "is not a valid number. Assuming 0.", c);
585f127cb91Sfrankho 			l = 0;
586f127cb91Sfrankho 		}
587f127cb91Sfrankho 		fsp->pcfs_secondswest = l;
588f127cb91Sfrankho 	}
5907c478bd9Sstevel@tonic-gate 	/*
591f127cb91Sfrankho 	 * The "secsize=..." mount option is a workaround for the lack of
592f127cb91Sfrankho 	 * lofi(7d) support for DKIOCGMEDIAINFO. If PCFS wants to parse the
593f127cb91Sfrankho 	 * partition table of a disk image and it has been partitioned with
594f127cb91Sfrankho 	 * sector sizes other than 512 bytes, we'd fail on loopback'ed disk
595f127cb91Sfrankho 	 * images.
596f127cb91Sfrankho 	 * That should really be fixed in lofi ... this is a workaround.
5977c478bd9Sstevel@tonic-gate 	 */
598f127cb91Sfrankho 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_SECSIZE, &c)) {
599f127cb91Sfrankho 		if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
600f127cb91Sfrankho 		    endptr == c + strlen(c)) {
601f127cb91Sfrankho 			/*
602f127cb91Sfrankho 			 * A number alright - a valid sector size as well ?
603f127cb91Sfrankho 			 */
604f127cb91Sfrankho 			if (!VALID_SECSIZE(l)) {
605f127cb91Sfrankho 				cmn_err(CE_WARN, "!pcfs: invalid use of "
606f127cb91Sfrankho 				    "'secsize' mount option - %ld is "
607f127cb91Sfrankho 				    "unsupported. Autodetecting.", l);
608f127cb91Sfrankho 				l = 0;
609f127cb91Sfrankho 			}
610f127cb91Sfrankho 		} else {
611f127cb91Sfrankho 			cmn_err(CE_WARN, "!pcfs: invalid use of "
612f127cb91Sfrankho 			    "'secsize' mount option - argument %s "
613f127cb91Sfrankho 			    "is not a valid number. Autodetecting.", c);
614f127cb91Sfrankho 			l = 0;
615f127cb91Sfrankho 		}
616f127cb91Sfrankho 		fsp->pcfs_secsize = l;
617f127cb91Sfrankho 		fsp->pcfs_sdshift = ddi_ffs(l / DEV_BSIZE) - 1;
618f127cb91Sfrankho 	}
619f127cb91Sfrankho }
621f127cb91Sfrankho /*
622f127cb91Sfrankho  * vfs operations
623f127cb91Sfrankho  */
625f127cb91Sfrankho /*
626f127cb91Sfrankho  * pcfs_mount - backend for VFS_MOUNT() on PCFS.
627f127cb91Sfrankho  */
628f127cb91Sfrankho static int
629f127cb91Sfrankho pcfs_mount(
630f127cb91Sfrankho 	struct vfs *vfsp,
631f127cb91Sfrankho 	struct vnode *mvp,
632f127cb91Sfrankho 	struct mounta *uap,
633f127cb91Sfrankho 	struct cred *cr)
634f127cb91Sfrankho {
635f127cb91Sfrankho 	struct pcfs *fsp;
636f127cb91Sfrankho 	struct vnode *devvp;
637f127cb91Sfrankho 	dev_t pseudodev;
638f127cb91Sfrankho 	dev_t xdev;
639f127cb91Sfrankho 	int dos_ldrive = 0;
640f127cb91Sfrankho 	int error;
641f127cb91Sfrankho 	int remounting;
643f127cb91Sfrankho 	if ((