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 */
297c478bd9Sstevel@tonic-gate
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>
697c478bd9Sstevel@tonic-gate
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)
787c478bd9Sstevel@tonic-gate
7990c30842Sjmcp static int pcfs_pseudo_floppy(dev_t);
807c478bd9Sstevel@tonic-gate
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);
917c478bd9Sstevel@tonic-gate
92f127cb91Sfrankho static int pc_readfat(struct pcfs *fsp, uchar_t *fatp);
937c478bd9Sstevel@tonic-gate static int pc_writefat(struct pcfs *fsp, daddr_t start);
947c478bd9Sstevel@tonic-gate
95f127cb91Sfrankho static int pc_getfattype(struct pcfs *fsp);
969e003ac4Sgd static void pcfs_parse_mntopts(struct pcfs *fsp);
97f127cb91Sfrankho
98f127cb91Sfrankho
997c478bd9Sstevel@tonic-gate /*
1007c478bd9Sstevel@tonic-gate * pcfs mount options table
1017c478bd9Sstevel@tonic-gate */
1027c478bd9Sstevel@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 };
1117c478bd9Sstevel@tonic-gate
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 },
124f127cb91Sfrankho { MNTOPT_PCFS_TIMEZONE, NULL, "+0", MO_DEFAULT | MO_HASVALUE, NULL },
125f127cb91Sfrankho { MNTOPT_PCFS_SECSIZE, NULL, NULL, MO_HASVALUE, NULL }
1267c478bd9Sstevel@tonic-gate };
1277c478bd9Sstevel@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 };
1327c478bd9Sstevel@tonic-gate
1337c478bd9Sstevel@tonic-gate int pcfsdebuglevel = 0;
1347c478bd9Sstevel@tonic-gate
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;
1557c478bd9Sstevel@tonic-gate
1567c478bd9Sstevel@tonic-gate static int pcfstype;
1577c478bd9Sstevel@tonic-gate
1587c478bd9Sstevel@tonic-gate static vfsdef_t vfw = {
1597c478bd9Sstevel@tonic-gate VFSDEF_VERSION,
1607c478bd9Sstevel@tonic-gate "pcfs",
1617c478bd9Sstevel@tonic-gate pcfsinit,
162d9a54dd1SSebastien Roy VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS|VSW_CANLOFI|VSW_MOUNTDEV,
1637c478bd9Sstevel@tonic-gate &pcfs_mntopts
1647c478bd9Sstevel@tonic-gate };
1657c478bd9Sstevel@tonic-gate
1667c478bd9Sstevel@tonic-gate extern struct mod_ops mod_fsops;
1677c478bd9Sstevel@tonic-gate
1687c478bd9Sstevel@tonic-gate static struct modlfs modlfs = {
1697c478bd9Sstevel@tonic-gate &mod_fsops,
170c2aaf90fSgd "PC filesystem",
1717c478bd9Sstevel@tonic-gate &vfw
1727c478bd9Sstevel@tonic-gate };
1737c478bd9Sstevel@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 };
1797c478bd9Sstevel@tonic-gate
1807c478bd9Sstevel@tonic-gate int
_init(void)1817c478bd9Sstevel@tonic-gate _init(void)
1827c478bd9Sstevel@tonic-gate {
1837c478bd9Sstevel@tonic-gate int error;
1847c478bd9Sstevel@tonic-gate
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 }
1997c478bd9Sstevel@tonic-gate
2007c478bd9Sstevel@tonic-gate int
_fini(void)2017c478bd9Sstevel@tonic-gate _fini(void)
2027c478bd9Sstevel@tonic-gate {
2037c478bd9Sstevel@tonic-gate int error;
2047c478bd9Sstevel@tonic-gate
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);
213264a6e74Sfrankho
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 }
2277c478bd9Sstevel@tonic-gate
2287c478bd9Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2297c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
2307c478bd9Sstevel@tonic-gate {
2317c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop));
2327c478bd9Sstevel@tonic-gate }
2337c478bd9Sstevel@tonic-gate
2347c478bd9Sstevel@tonic-gate /* ARGSUSED1 */
2357c478bd9Sstevel@tonic-gate static int
pcfsinit(int fstype,char * name)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;
2497c478bd9Sstevel@tonic-gate
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 }
2557c478bd9Sstevel@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 }
2627c478bd9Sstevel@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 }
2707c478bd9Sstevel@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 }
2767c478bd9Sstevel@tonic-gate
2777c478bd9Sstevel@tonic-gate static struct pcfs *pc_mounttab = NULL;
2787c478bd9Sstevel@tonic-gate
2797c478bd9Sstevel@tonic-gate extern struct pcfs_args pc_tz;
2807c478bd9Sstevel@tonic-gate
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
2877c478bd9Sstevel@tonic-gate
2887c478bd9Sstevel@tonic-gate static int
pcfs_device_identify(struct vfs * vfsp,struct mounta * uap,struct cred * cr,int * dos_ldrive,dev_t * xdev)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;
3027c478bd9Sstevel@tonic-gate
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 }
309f127cb91Sfrankho
310f127cb91Sfrankho *dos_ldrive = -1;
311f127cb91Sfrankho
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 */
321f127cb91Sfrankho
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;
3317c478bd9Sstevel@tonic-gate
3329bd42341Sfrankho *c++ = '\0';
3337c478bd9Sstevel@tonic-gate
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;
3519bd42341Sfrankho
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 }
3869bd42341Sfrankho
3879bd42341Sfrankho
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);
3979bd42341Sfrankho
398f127cb91Sfrankho ASSERT(*dos_ldrive >= UNPARTITIONED_DRIVE);
399f127cb91Sfrankho
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 }
411f127cb91Sfrankho
41293239addSjohnlev error = vfs_get_lofi(vfsp, &lvp);
413f127cb91Sfrankho
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;
42293239addSjohnlev
423ecffa4d6SJohn Levon if (svp->v_type != VBLK) {
42493239addSjohnlev error = ENOTBLK;
425ecffa4d6SJohn Levon goto out;
426ecffa4d6SJohn Levon }
42793239addSjohnlev
42893239addSjohnlev if ((error = secpolicy_spec_open(cr, svp, oflag)) != 0)
42993239addSjohnlev goto out;
4307c478bd9Sstevel@tonic-gate }
4317c478bd9Sstevel@tonic-gate
43293239addSjohnlev if (getmajor(*xdev) >= devcnt) {
43393239addSjohnlev error = ENXIO;
43493239addSjohnlev goto out;
43593239addSjohnlev }
43693239addSjohnlev
43793239addSjohnlev if ((error = VOP_ACCESS(svp, aflag, 0, cr, NULL)) != 0)
43893239addSjohnlev goto out;
43993239addSjohnlev
44093239addSjohnlev out:
44193239addSjohnlev if (svp != NULL)
44293239addSjohnlev VN_RELE(svp);
44393239addSjohnlev if (lvp != NULL)
44493239addSjohnlev VN_RELE(lvp);
44593239addSjohnlev return (error);
446f127cb91Sfrankho }
447f127cb91Sfrankho
448f127cb91Sfrankho static int
pcfs_device_ismounted(struct vfs * vfsp,int dos_ldrive,dev_t xdev,int * remounting,dev_t * pseudodev)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;
458f127cb91Sfrankho
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;
469f127cb91Sfrankho
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 }
5297c478bd9Sstevel@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;
536f127cb91Sfrankho
537f127cb91Sfrankho ASSERT(*pseudodev);
538f127cb91Sfrankho return (0);
539f127cb91Sfrankho }
540f127cb91Sfrankho
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
pcfs_parse_mntopts(struct pcfs * fsp)5509e003ac4Sgd pcfs_parse_mntopts(struct pcfs *fsp)
551f127cb91Sfrankho {
552f127cb91Sfrankho char *c;
553f127cb91Sfrankho char *endptr;
554f127cb91Sfrankho long l;
555f127cb91Sfrankho struct vfs *vfsp = fsp->pcfs_vfs;
556f127cb91Sfrankho
557f127cb91Sfrankho ASSERT(fsp->pcfs_secondswest == 0);
558f127cb91Sfrankho ASSERT(fsp->pcfs_secsize == 0);
559f127cb91Sfrankho
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;
568f127cb91Sfrankho
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 }
589f127cb91Sfrankho
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 }
620f127cb91Sfrankho
621f127cb91Sfrankho /*
622f127cb91Sfrankho * vfs operations
623f127cb91Sfrankho */
624f127cb91Sfrankho
625f127cb91Sfrankho /*
626f127cb91Sfrankho * pcfs_mount - backend for VFS_MOUNT() on PCFS.
627f127cb91Sfrankho */
628f127cb91Sfrankho static int
pcfs_mount(struct vfs * vfsp,struct vnode * mvp,struct mounta * uap,struct cred * cr)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;
642f127cb91Sfrankho
643