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 */
267c478bd9Sstevel@tonic-gate
277c478bd9Sstevel@tonic-gate #include <sys/param.h>
287c478bd9Sstevel@tonic-gate #include <sys/systm.h>
297c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
307c478bd9Sstevel@tonic-gate #include <sys/user.h>
317c478bd9Sstevel@tonic-gate #include <sys/proc.h>
327c478bd9Sstevel@tonic-gate #include <sys/cred.h>
337c478bd9Sstevel@tonic-gate #include <sys/disp.h>
347c478bd9Sstevel@tonic-gate #include <sys/buf.h>
357c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
36aa59c4cbSrsb #include <sys/vfs_opreg.h>
377c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
387c478bd9Sstevel@tonic-gate #include <sys/fdio.h>
397c478bd9Sstevel@tonic-gate #include <sys/file.h>
407c478bd9Sstevel@tonic-gate #include <sys/uio.h>
417c478bd9Sstevel@tonic-gate #include <sys/conf.h>
427c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
437c478bd9Sstevel@tonic-gate #include <sys/mount.h>
447c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
457c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
467c478bd9Sstevel@tonic-gate #include <sys/debug.h>
477c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
487c478bd9Sstevel@tonic-gate #include <sys/conf.h>
497c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
507c478bd9Sstevel@tonic-gate #include <sys/swap.h>
517c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
527c478bd9Sstevel@tonic-gate #include <sys/sunldi.h>
537c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
547c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h>
557c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
567c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
577c478bd9Sstevel@tonic-gate #include <sys/fs/pc_node.h>
587c478bd9Sstevel@tonic-gate #include <fs/fs_subr.h>
597c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
607c478bd9Sstevel@tonic-gate #include <sys/dkio.h>
617c478bd9Sstevel@tonic-gate #include <sys/open.h>
627c478bd9Sstevel@tonic-gate #include <sys/mntent.h>
637c478bd9Sstevel@tonic-gate #include <sys/policy.h>
64264a6e74Sfrankho #include <sys/atomic.h>
65f127cb91Sfrankho #include <sys/sdt.h>
667c478bd9Sstevel@tonic-gate
677c478bd9Sstevel@tonic-gate /*
687c478bd9Sstevel@tonic-gate * The majority of PC media use a 512 sector size, but
697c478bd9Sstevel@tonic-gate * occasionally you will run across a 1k sector size.
707c478bd9Sstevel@tonic-gate * For media with a 1k sector size, fd_strategy() requires
717c478bd9Sstevel@tonic-gate * the I/O size to be a 1k multiple; so when the sector size
727c478bd9Sstevel@tonic-gate * is not yet known, always read 1k.
737c478bd9Sstevel@tonic-gate */
747c478bd9Sstevel@tonic-gate #define PC_SAFESECSIZE (PC_SECSIZE * 2)
757c478bd9Sstevel@tonic-gate
7690c30842Sjmcp static int pcfs_pseudo_floppy(dev_t);
777c478bd9Sstevel@tonic-gate
787c478bd9Sstevel@tonic-gate static int pcfsinit(int, char *);
797c478bd9Sstevel@tonic-gate static int pcfs_mount(struct vfs *, struct vnode *, struct mounta *,
807c478bd9Sstevel@tonic-gate struct cred *);
817c478bd9Sstevel@tonic-gate static int pcfs_unmount(struct vfs *, int, struct cred *);
827c478bd9Sstevel@tonic-gate static int pcfs_root(struct vfs *, struct vnode **);
837c478bd9Sstevel@tonic-gate static int pcfs_statvfs(struct vfs *, struct statvfs64 *);
847c478bd9Sstevel@tonic-gate static int pc_syncfsnodes(struct pcfs *);
857c478bd9Sstevel@tonic-gate static int pcfs_sync(struct vfs *, short, struct cred *);
867c478bd9Sstevel@tonic-gate static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp);
87264a6e74Sfrankho static void pcfs_freevfs(vfs_t *vfsp);
887c478bd9Sstevel@tonic-gate
89f127cb91Sfrankho static int pc_readfat(struct pcfs *fsp, uchar_t *fatp);
907c478bd9Sstevel@tonic-gate static int pc_writefat(struct pcfs *fsp, daddr_t start);
917c478bd9Sstevel@tonic-gate
92f127cb91Sfrankho static int pc_getfattype(struct pcfs *fsp);
939e003ac4Sgd78059 static void pcfs_parse_mntopts(struct pcfs *fsp);
94f127cb91Sfrankho
95f127cb91Sfrankho
967c478bd9Sstevel@tonic-gate /*
977c478bd9Sstevel@tonic-gate * pcfs mount options table
987c478bd9Sstevel@tonic-gate */
997c478bd9Sstevel@tonic-gate
1007c478bd9Sstevel@tonic-gate static char *nohidden_cancel[] = { MNTOPT_PCFS_HIDDEN, NULL };
1017c478bd9Sstevel@tonic-gate static char *hidden_cancel[] = { MNTOPT_PCFS_NOHIDDEN, NULL };
1027c478bd9Sstevel@tonic-gate static char *nofoldcase_cancel[] = { MNTOPT_PCFS_FOLDCASE, NULL };
1037c478bd9Sstevel@tonic-gate static char *foldcase_cancel[] = { MNTOPT_PCFS_NOFOLDCASE, NULL };
104264a6e74Sfrankho static char *clamptime_cancel[] = { MNTOPT_PCFS_NOCLAMPTIME, NULL };
105264a6e74Sfrankho static char *noclamptime_cancel[] = { MNTOPT_PCFS_CLAMPTIME, NULL };
106f127cb91Sfrankho static char *atime_cancel[] = { MNTOPT_NOATIME, NULL };
107f127cb91Sfrankho static char *noatime_cancel[] = { MNTOPT_ATIME, NULL };
1087c478bd9Sstevel@tonic-gate
1097c478bd9Sstevel@tonic-gate static mntopt_t mntopts[] = {
1107c478bd9Sstevel@tonic-gate /*
111264a6e74Sfrankho * option name cancel option default arg flags opt data
1127c478bd9Sstevel@tonic-gate */
113264a6e74Sfrankho { MNTOPT_PCFS_NOHIDDEN, nohidden_cancel, NULL, 0, NULL },
114264a6e74Sfrankho { MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, MO_DEFAULT, NULL },
115264a6e74Sfrankho { MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, NULL },
116264a6e74Sfrankho { MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0, NULL },
117264a6e74Sfrankho { MNTOPT_PCFS_CLAMPTIME, clamptime_cancel, NULL, MO_DEFAULT, NULL },
118f127cb91Sfrankho { MNTOPT_PCFS_NOCLAMPTIME, noclamptime_cancel, NULL, NULL, NULL },
119f127cb91Sfrankho { MNTOPT_NOATIME, noatime_cancel, NULL, NULL, NULL },
120f127cb91Sfrankho { MNTOPT_ATIME, atime_cancel, NULL, NULL, NULL },
121f127cb91Sfrankho { MNTOPT_PCFS_TIMEZONE, NULL, "+0", MO_DEFAULT | MO_HASVALUE, NULL },
122f127cb91Sfrankho { MNTOPT_PCFS_SECSIZE, NULL, NULL, MO_HASVALUE, NULL }
1237c478bd9Sstevel@tonic-gate };
1247c478bd9Sstevel@tonic-gate
1257c478bd9Sstevel@tonic-gate static mntopts_t pcfs_mntopts = {
1267c478bd9Sstevel@tonic-gate sizeof (mntopts) / sizeof (mntopt_t),
1277c478bd9Sstevel@tonic-gate mntopts
1287c478bd9Sstevel@tonic-gate };
1297c478bd9Sstevel@tonic-gate
1307c478bd9Sstevel@tonic-gate int pcfsdebuglevel = 0;
1317c478bd9Sstevel@tonic-gate
1327c478bd9Sstevel@tonic-gate /*
1337c478bd9Sstevel@tonic-gate * pcfslock: protects the list of mounted pc filesystems "pc_mounttab.
1347c478bd9Sstevel@tonic-gate * pcfs_lock: (inside per filesystem structure "pcfs")
1357c478bd9Sstevel@tonic-gate * per filesystem lock. Most of the vfsops and vnodeops are
1367c478bd9Sstevel@tonic-gate * protected by this lock.
1377c478bd9Sstevel@tonic-gate * pcnodes_lock: protects the pcnode hash table "pcdhead", "pcfhead".
1387c478bd9Sstevel@tonic-gate *
1397c478bd9Sstevel@tonic-gate * Lock hierarchy: pcfslock > pcfs_lock > pcnodes_lock
140264a6e74Sfrankho *
141264a6e74Sfrankho * pcfs_mountcount: used to prevent module unloads while there is still
142264a6e74Sfrankho * pcfs state from a former mount hanging around. With
143264a6e74Sfrankho * forced umount support, the filesystem module must not
144264a6e74Sfrankho * be allowed to go away before the last VFS_FREEVFS()
145264a6e74Sfrankho * call has been made.
146264a6e74Sfrankho * Since this is just an atomic counter, there's no need
147264a6e74Sfrankho * for locking.
1487c478bd9Sstevel@tonic-gate */
1497c478bd9Sstevel@tonic-gate kmutex_t pcfslock;
150264a6e74Sfrankho krwlock_t pcnodes_lock;
151264a6e74Sfrankho uint32_t pcfs_mountcount;
1527c478bd9Sstevel@tonic-gate
1537c478bd9Sstevel@tonic-gate static int pcfstype;
1547c478bd9Sstevel@tonic-gate
1557c478bd9Sstevel@tonic-gate static vfsdef_t vfw = {
1567c478bd9Sstevel@tonic-gate VFSDEF_VERSION,
1577c478bd9Sstevel@tonic-gate "pcfs",
1587c478bd9Sstevel@tonic-gate pcfsinit,
15943d5cd3dSjohnlev VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS|VSW_CANLOFI,
1607c478bd9Sstevel@tonic-gate &pcfs_mntopts
1617c478bd9Sstevel@tonic-gate };
1627c478bd9Sstevel@tonic-gate
1637c478bd9Sstevel@tonic-gate extern struct mod_ops mod_fsops;
1647c478bd9Sstevel@tonic-gate
1657c478bd9Sstevel@tonic-gate static struct modlfs modlfs = {
1667c478bd9Sstevel@tonic-gate &mod_fsops,
167c2aaf90fSgd78059 "PC filesystem",
1687c478bd9Sstevel@tonic-gate &vfw
1697c478bd9Sstevel@tonic-gate };
1707c478bd9Sstevel@tonic-gate
1717c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
1727c478bd9Sstevel@tonic-gate MODREV_1,
1737c478bd9Sstevel@tonic-gate &modlfs,
1747c478bd9Sstevel@tonic-gate NULL
1757c478bd9Sstevel@tonic-gate };
1767c478bd9Sstevel@tonic-gate
1777c478bd9Sstevel@tonic-gate int
_init(void)1787c478bd9Sstevel@tonic-gate _init(void)
1797c478bd9Sstevel@tonic-gate {
1807c478bd9Sstevel@tonic-gate int error;
1817c478bd9Sstevel@tonic-gate
1827c478bd9Sstevel@tonic-gate #if !defined(lint)
1837c478bd9Sstevel@tonic-gate /* make sure the on-disk structures are sane */
1847c478bd9Sstevel@tonic-gate ASSERT(sizeof (struct pcdir) == 32);
1857c478bd9Sstevel@tonic-gate ASSERT(sizeof (struct pcdir_lfn) == 32);
1867c478bd9Sstevel@tonic-gate #endif
1877c478bd9Sstevel@tonic-gate mutex_init(&pcfslock, NULL, MUTEX_DEFAULT, NULL);
1887c478bd9Sstevel@tonic-gate rw_init(&pcnodes_lock, NULL, RW_DEFAULT, NULL);
1897c478bd9Sstevel@tonic-gate error = mod_install(&modlinkage);
1907c478bd9Sstevel@tonic-gate if (error) {
1917c478bd9Sstevel@tonic-gate mutex_destroy(&pcfslock);
1927c478bd9Sstevel@tonic-gate rw_destroy(&pcnodes_lock);
1937c478bd9Sstevel@tonic-gate }
1947c478bd9Sstevel@tonic-gate return (error);
1957c478bd9Sstevel@tonic-gate }
1967c478bd9Sstevel@tonic-gate
1977c478bd9Sstevel@tonic-gate int
_fini(void)1987c478bd9Sstevel@tonic-gate _fini(void)
1997c478bd9Sstevel@tonic-gate {
2007c478bd9Sstevel@tonic-gate int error;
2017c478bd9Sstevel@tonic-gate
202264a6e74Sfrankho /*
203264a6e74Sfrankho * If a forcedly unmounted instance is still hanging around,
204264a6e74Sfrankho * we cannot allow the module to be unloaded because that would
205264a6e74Sfrankho * cause panics once the VFS framework decides it's time to call
206264a6e74Sfrankho * into VFS_FREEVFS().
207264a6e74Sfrankho */
208264a6e74Sfrankho if (pcfs_mountcount)
209264a6e74Sfrankho return (EBUSY);
210264a6e74Sfrankho
2117c478bd9Sstevel@tonic-gate error = mod_remove(&modlinkage);
2127c478bd9Sstevel@tonic-gate if (error)
2137c478bd9Sstevel@tonic-gate return (error);
2147c478bd9Sstevel@tonic-gate mutex_destroy(&pcfslock);
2157c478bd9Sstevel@tonic-gate rw_destroy(&pcnodes_lock);
2167c478bd9Sstevel@tonic-gate /*
2177c478bd9Sstevel@tonic-gate * Tear down the operations vectors
2187c478bd9Sstevel@tonic-gate */
2197c478bd9Sstevel@tonic-gate (void) vfs_freevfsops_by_type(pcfstype);
2207c478bd9Sstevel@tonic-gate vn_freevnodeops(pcfs_fvnodeops);
2217c478bd9Sstevel@tonic-gate vn_freevnodeops(pcfs_dvnodeops);
2227c478bd9Sstevel@tonic-gate return (0);
2237c478bd9Sstevel@tonic-gate }
2247c478bd9Sstevel@tonic-gate
2257c478bd9Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2267c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
2277c478bd9Sstevel@tonic-gate {
2287c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop));
2297c478bd9Sstevel@tonic-gate }
2307c478bd9Sstevel@tonic-gate
2317c478bd9Sstevel@tonic-gate /* ARGSUSED1 */
2327c478bd9Sstevel@tonic-gate static int
pcfsinit(int fstype,char * name)2337c478bd9Sstevel@tonic-gate pcfsinit(int fstype, char *name)
2347c478bd9Sstevel@tonic-gate {
2357c478bd9Sstevel@tonic-gate static const fs_operation_def_t pcfs_vfsops_template[] = {
236aa59c4cbSrsb VFSNAME_MOUNT, { .vfs_mount = pcfs_mount },
237aa59c4cbSrsb VFSNAME_UNMOUNT, { .vfs_unmount = pcfs_unmount },
238aa59c4cbSrsb VFSNAME_ROOT, { .vfs_root = pcfs_root },
239aa59c4cbSrsb VFSNAME_STATVFS, { .vfs_statvfs = pcfs_statvfs },
240aa59c4cbSrsb VFSNAME_SYNC, { .vfs_sync = pcfs_sync },
241aa59c4cbSrsb VFSNAME_VGET, { .vfs_vget = pcfs_vget },
242aa59c4cbSrsb VFSNAME_FREEVFS, { .vfs_freevfs = pcfs_freevfs },
2437c478bd9Sstevel@tonic-gate NULL, NULL
2447c478bd9Sstevel@tonic-gate };
2457c478bd9Sstevel@tonic-gate int error;
2467c478bd9Sstevel@tonic-gate
2477c478bd9Sstevel@tonic-gate error = vfs_setfsops(fstype, pcfs_vfsops_template, NULL);
2487c478bd9Sstevel@tonic-gate if (error != 0) {
2497c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "pcfsinit: bad vfs ops template");
2507c478bd9Sstevel@tonic-gate return (error);
2517c478bd9Sstevel@tonic-gate }
2527c478bd9Sstevel@tonic-gate
2537c478bd9Sstevel@tonic-gate error = vn_make_ops("pcfs", pcfs_fvnodeops_template, &pcfs_fvnodeops);
2547c478bd9Sstevel@tonic-gate if (error != 0) {
2557c478bd9Sstevel@tonic-gate (void) vfs_freevfsops_by_type(fstype);
2567c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "pcfsinit: bad file vnode ops template");
2577c478bd9Sstevel@tonic-gate return (error);
2587c478bd9Sstevel@tonic-gate }
2597c478bd9Sstevel@tonic-gate
2607c478bd9Sstevel@tonic-gate error = vn_make_ops("pcfsd", pcfs_dvnodeops_template, &pcfs_dvnodeops);
2617c478bd9Sstevel@tonic-gate if (error != 0) {
2627c478bd9Sstevel@tonic-gate (void) vfs_freevfsops_by_type(fstype);
2637c478bd9Sstevel@tonic-gate vn_freevnodeops(pcfs_fvnodeops);
2647c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "pcfsinit: bad dir vnode ops template");
2657c478bd9Sstevel@tonic-gate return (error);
2667c478bd9Sstevel@tonic-gate }
2677c478bd9Sstevel@tonic-gate
2687c478bd9Sstevel@tonic-gate pcfstype = fstype;
2697c478bd9Sstevel@tonic-gate (void) pc_init();
270264a6e74Sfrankho pcfs_mountcount = 0;
2717c478bd9Sstevel@tonic-gate return (0);
2727c478bd9Sstevel@tonic-gate }
2737c478bd9Sstevel@tonic-gate
2747c478bd9Sstevel@tonic-gate static struct pcfs *pc_mounttab = NULL;
2757c478bd9Sstevel@tonic-gate
2767c478bd9Sstevel@tonic-gate extern struct pcfs_args pc_tz;
2777c478bd9Sstevel@tonic-gate
2787c478bd9Sstevel@tonic-gate /*
2797c478bd9Sstevel@tonic-gate * Define some special logical drives we use internal to this file.
2807c478bd9Sstevel@tonic-gate */
2817c478bd9Sstevel@tonic-gate #define BOOT_PARTITION_DRIVE 99
2827c478bd9Sstevel@tonic-gate #define PRIMARY_DOS_DRIVE 1
283f127cb91Sfrankho #define UNPARTITIONED_DRIVE 0
2847c478bd9Sstevel@tonic-gate
2857c478bd9Sstevel@tonic-gate static int
pcfs_device_identify(struct vfs * vfsp,struct mounta * uap,struct cred * cr,int * dos_ldrive,dev_t * xdev)286f127cb91Sfrankho pcfs_device_identify(
2877c478bd9Sstevel@tonic-gate struct vfs *vfsp,
2887c478bd9Sstevel@tonic-gate struct mounta *uap,
289f127cb91Sfrankho struct cred *cr,
290f127cb91Sfrankho int *dos_ldrive,
291f127cb91Sfrankho dev_t *xdev)
2927c478bd9Sstevel@tonic-gate {
2937c478bd9Sstevel@tonic-gate struct pathname special;
2949bd42341Sfrankho char *c;
29593239addSjohnlev struct vnode *svp = NULL;
29693239addSjohnlev struct vnode *lvp = NULL;
2977c478bd9Sstevel@tonic-gate int oflag, aflag;
298f127cb91Sfrankho int error;
2997c478bd9Sstevel@tonic-gate
3007c478bd9Sstevel@tonic-gate /*
3017c478bd9Sstevel@tonic-gate * Resolve path name of special file being mounted.
3027c478bd9Sstevel@tonic-gate */
3037c478bd9Sstevel@tonic-gate if (error = pn_get(uap->spec, UIO_USERSPACE, &special)) {
3047c478bd9Sstevel@tonic-gate return (error);
3057c478bd9Sstevel@tonic-gate }
306f127cb91Sfrankho
307f127cb91Sfrankho *dos_ldrive = -1;
308f127cb91Sfrankho
3097c478bd9Sstevel@tonic-gate if (error =
31093239addSjohnlev lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &svp)) {
3117c478bd9Sstevel@tonic-gate /*
312f127cb91Sfrankho * If there's no device node, the name specified most likely
313f127cb91Sfrankho * maps to a PCFS-style "partition specifier" to select a
314f127cb91Sfrankho * harddisk primary/logical partition. Disable floppy-specific
315f127cb91Sfrankho * checks in such cases unless an explicit :A or :B is
316f127cb91Sfrankho * requested.
317f127cb91Sfrankho */
318f127cb91Sfrankho
319f127cb91Sfrankho /*
3209bd42341Sfrankho * Split the pathname string at the last ':' separator.
3219bd42341Sfrankho * If there's no ':' in the device name, or the ':' is the
3229bd42341Sfrankho * last character in the string, the name is invalid and
3239bd42341Sfrankho * the error from the previous lookup will be returned.
3247c478bd9Sstevel@tonic-gate */
3259bd42341Sfrankho c = strrchr(special.pn_path, ':');
3269bd42341Sfrankho if (c == NULL || strlen(c) == 0)
3279bd42341Sfrankho goto devlookup_done;
3287c478bd9Sstevel@tonic-gate
3299bd42341Sfrankho *c++ = '\0';
3309bd42341Sfrankho
3317c478bd9Sstevel@tonic-gate /*
3329bd42341Sfrankho * PCFS partition name suffixes can be:
3339bd42341Sfrankho * - "boot" to indicate the X86BOOT partition
3349bd42341Sfrankho * - a drive letter [c-z] for the "DOS logical drive"
3359bd42341Sfrankho * - a drive number 1..24 for the "DOS logical drive"
336f127cb91Sfrankho * - a "floppy name letter", 'a' or 'b' (just strip this)
3377c478bd9Sstevel@tonic-gate */
3389bd42341Sfrankho if (strcasecmp(c, "boot") == 0) {
3399bd42341Sfrankho /*
3409bd42341Sfrankho * The Solaris boot partition is requested.
3419bd42341Sfrankho */
342f127cb91Sfrankho *dos_ldrive = BOOT_PARTITION_DRIVE;
3439bd42341Sfrankho } else if (strspn(c, "0123456789") == strlen(c)) {
3449bd42341Sfrankho /*
3459bd42341Sfrankho * All digits - parse the partition number.
3469bd42341Sfrankho */
3479bd42341Sfrankho long drvnum = 0;
3489bd42341Sfrankho
3499bd42341Sfrankho if ((error = ddi_strtol(c, NULL, 10, &drvnum)) == 0) {
3509bd42341Sfrankho /*
3519bd42341Sfrankho * A number alright - in the allowed range ?
3529bd42341Sfrankho */
3539bd42341Sfrankho if (drvnum > 24 || drvnum == 0)
354f127cb91Sfrankho error = ENXIO;
3557c478bd9Sstevel@tonic-gate }
3569bd42341Sfrankho if (error)
3579bd42341Sfrankho goto devlookup_done;
358f127cb91Sfrankho *dos_ldrive = (int)drvnum;
3599bd42341Sfrankho } else if (strlen(c) == 1) {
3609bd42341Sfrankho /*
361f127cb91Sfrankho * A single trailing character was specified.
362f127cb91Sfrankho * - [c-zC-Z] means a harddisk partition, and
363f127cb91Sfrankho * we retrieve the partition number.
364f127cb91Sfrankho * - [abAB] means a floppy drive, so we swallow
365f127cb91Sfrankho * the "drive specifier" and test later
36669ed0c8eSGarrett D'Amore * whether the physical device is a floppy.
3679bd42341Sfrankho */
3689bd42341Sfrankho *c = tolower(*c);
369f127cb91Sfrankho if (*c == 'a' || *c == 'b') {
370f127cb91Sfrankho *dos_ldrive = UNPARTITIONED_DRIVE;
371f127cb91Sfrankho } else if (*c < 'c' || *c > 'z') {
372f127cb91Sfrankho error = ENXIO;
3739bd42341Sfrankho goto devlookup_done;
374f127cb91Sfrankho } else {
375f127cb91Sfrankho *dos_ldrive = 1 + *c - 'c';
3769bd42341Sfrankho }
3779bd42341Sfrankho } else {
3789bd42341Sfrankho /*
3799bd42341Sfrankho * Can't parse this - pass through previous error.
3809bd42341Sfrankho */
3819bd42341Sfrankho goto devlookup_done;
3827c478bd9Sstevel@tonic-gate }
3837c478bd9Sstevel@tonic-gate
3849bd42341Sfrankho
3859bd42341Sfrankho error = lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW,
38693239addSjohnlev NULLVPP, &svp);
387f127cb91Sfrankho } else {
388f127cb91Sfrankho *dos_ldrive = UNPARTITIONED_DRIVE;
3897c478bd9Sstevel@tonic-gate }
3909bd42341Sfrankho devlookup_done:
3917c478bd9Sstevel@tonic-gate pn_free(&special);
3929bd42341Sfrankho if (error)
3937c478bd9Sstevel@tonic-gate return (error);
3949bd42341Sfrankho
395f127cb91Sfrankho ASSERT(*dos_ldrive >= UNPARTITIONED_DRIVE);
396f127cb91Sfrankho
3977c478bd9Sstevel@tonic-gate /*
3987c478bd9Sstevel@tonic-gate * Verify caller's permission to open the device special file.
3997c478bd9Sstevel@tonic-gate */
4007c478bd9Sstevel@tonic-gate if ((vfsp->vfs_flag & VFS_RDONLY) != 0 ||
4017c478bd9Sstevel@tonic-gate ((uap->flags & MS_RDONLY) != 0)) {
4027c478bd9Sstevel@tonic-gate oflag = FREAD;
4037c478bd9Sstevel@tonic-gate aflag = VREAD;
4047c478bd9Sstevel@tonic-gate } else {
4057c478bd9Sstevel@tonic-gate oflag = FREAD | FWRITE;
4067c478bd9Sstevel@tonic-gate aflag = VREAD | VWRITE;
4077c478bd9Sstevel@tonic-gate }
408f127cb91Sfrankho
40993239addSjohnlev error = vfs_get_lofi(vfsp, &lvp);
410f127cb91Sfrankho
41193239addSjohnlev if (error > 0) {
41293239addSjohnlev if (error == ENOENT)
41393239addSjohnlev error = ENODEV;
41493239addSjohnlev goto out;
41593239addSjohnlev } else if (error == 0) {
41693239addSjohnlev *xdev = lvp->v_rdev;
41793239addSjohnlev } else {
41893239addSjohnlev *xdev = svp->v_rdev;
41993239addSjohnlev
420ecffa4d6SJohn Levon if (svp->v_type != VBLK) {
42193239addSjohnlev error = ENOTBLK;
422ecffa4d6SJohn Levon goto out;
423ecffa4d6SJohn Levon }
42493239addSjohnlev
42593239addSjohnlev if ((error = secpolicy_spec_open(cr, svp, oflag)) != 0)
42693239addSjohnlev goto out;
4277c478bd9Sstevel@tonic-gate }
4287c478bd9Sstevel@tonic-gate
42993239addSjohnlev if (getmajor(*xdev) >= devcnt) {
43093239addSjohnlev error = ENXIO;
43193239addSjohnlev goto out;
43293239addSjohnlev }
43393239addSjohnlev
43493239addSjohnlev if ((error = VOP_ACCESS(svp, aflag, 0, cr, NULL)) != 0)
43593239addSjohnlev goto out;
43693239addSjohnlev
43793239addSjohnlev out:
43893239addSjohnlev if (svp != NULL)
43993239addSjohnlev VN_RELE(svp);
44093239addSjohnlev if (lvp != NULL)
44193239addSjohnlev VN_RELE(lvp);
44293239addSjohnlev return (error);
4437c478bd9Sstevel@tonic-gate }
444f127cb91Sfrankho
445f127cb91Sfrankho static int
pcfs_device_ismounted(struct vfs * vfsp,int dos_ldrive,dev_t xdev,int * remounting,dev_t * pseudodev)446f127cb91Sfrankho pcfs_device_ismounted(
447f127cb91Sfrankho struct vfs *vfsp,
448f127cb91Sfrankho int dos_ldrive,
449f127cb91Sfrankho dev_t xdev,
450f127cb91Sfrankho int *remounting,
451f127cb91Sfrankho dev_t *pseudodev)
452f127cb91Sfrankho {
453f127cb91Sfrankho struct pcfs *fsp;
454f127cb91Sfrankho int remount = *remounting;
455f127cb91Sfrankho
4567c478bd9Sstevel@tonic-gate /*
4579bd42341Sfrankho * Ensure that this logical drive isn't already mounted, unless
4589bd42341Sfrankho * this is a REMOUNT request.
4599bd42341Sfrankho * Note: The framework will perform this check if the "...:c"
4609bd42341Sfrankho * PCFS-style "logical drive" syntax has not been used and an
4619bd42341Sfrankho * actually existing physical device is backing this filesystem.
462f127cb91Sfrankho * Once all block device drivers support PC-style partitioning,
463f127cb91Sfrankho * this codeblock can be dropped.
4647c478bd9Sstevel@tonic-gate */
465f127cb91Sfrankho *pseudodev = xdev;
466f127cb91Sfrankho
4677c478bd9Sstevel@tonic-gate if (dos_ldrive) {
4687c478bd9Sstevel@tonic-gate mutex_enter(&pcfslock);
4697c478bd9Sstevel@tonic-gate for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt)
4707c478bd9Sstevel@tonic-gate if (fsp->pcfs_xdev == xdev &&
471f127cb91Sfrankho fsp->pcfs_ldrive == dos_ldrive) {
4727c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock);
473f127cb91Sfrankho if (remount) {
4747c478bd9Sstevel@tonic-gate return (0);
4757c478bd9Sstevel@tonic-gate } else {
4767c478bd9Sstevel@tonic-gate return (EBUSY);
4777c478bd9Sstevel@tonic-gate }
4787c478bd9Sstevel@tonic-gate }
4797c478bd9Sstevel@tonic-gate /*
4807c478bd9Sstevel@tonic-gate * Assign a unique device number for the vfs
4817c478bd9Sstevel@tonic-gate * The old way (getudev() + a constantly incrementing
4827c478bd9Sstevel@tonic-gate * major number) was wrong because it changes vfs_dev
4837c478bd9Sstevel@tonic-gate * across mounts and reboots, which breaks nfs file handles.
4847c478bd9Sstevel@tonic-gate * UFS just uses the real dev_t. We can't do that because
4857c478bd9Sstevel@tonic-gate * of the way pcfs opens fdisk partitons (the :c and :d
4867c478bd9Sstevel@tonic-gate * partitions are on the same dev_t). Though that _might_
4877c478bd9Sstevel@tonic-gate * actually be ok, since the file handle contains an
4887c478bd9Sstevel@tonic-gate * absolute block number, it's probably better to make them
4897c478bd9Sstevel@tonic-gate * different. So I think we should retain the original
4907c478bd9Sstevel@tonic-gate * dev_t, but come up with a different minor number based
4917c478bd9Sstevel@tonic-gate * on the logical drive that will _always_ come up the same.
4927c478bd9Sstevel@tonic-gate * For now, we steal the upper 6 bits.
4937c478bd9Sstevel@tonic-gate */
4947c478bd9Sstevel@tonic-gate #ifdef notdef
4957c478bd9Sstevel@tonic-gate /* what should we do here? */
4967c478bd9Sstevel@tonic-gate if (((getminor(xdev) >> 12) & 0x3F) != 0)
4977c478bd9Sstevel@tonic-gate printf("whoops - upper bits used!\n");
4987c478bd9Sstevel@tonic-gate #endif
499f127cb91Sfrankho *pseudodev = makedevice(getmajor(xdev),
500f127cb91Sfrankho ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32);
501f127cb91Sfrankho if (vfs_devmounting(*pseudodev, vfsp)) {
5027c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock);
5037c478bd9Sstevel@tonic-gate return (EBUSY);
5047c478bd9Sstevel@tonic-gate }
505f127cb91Sfrankho if (vfs_devismounted(*pseudodev)) {
5067c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock);
507f127cb91Sfrankho if (remount) {
5087c478bd9Sstevel@tonic-gate return (0);
5097c478bd9Sstevel@tonic-gate } else {
5107c478bd9Sstevel@tonic-gate return (EBUSY);
5117c478bd9Sstevel@tonic-gate }
5127c478bd9Sstevel@tonic-gate }
5137c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock);
5147c478bd9Sstevel@tonic-gate } else {
515f127cb91Sfrankho *pseudodev = xdev;
516f127cb91Sfrankho if (vfs_devmounting(*pseudodev, vfsp)) {
5177c478bd9Sstevel@tonic-gate return (EBUSY);
5187c478bd9Sstevel@tonic-gate }
519f127cb91Sfrankho if (vfs_devismounted(*pseudodev))
520f127cb91Sfrankho if (remount) {
5217c478bd9Sstevel@tonic-gate return (0);
5227c478bd9Sstevel@tonic-gate } else {
5237c478bd9Sstevel@tonic-gate return (EBUSY);
5247c478bd9Sstevel@tonic-gate }
5257c478bd9Sstevel@tonic-gate }
5267c478bd9Sstevel@tonic-gate
527f127cb91Sfrankho /*
528f127cb91Sfrankho * This is not a remount. Even if MS_REMOUNT was requested,
529f127cb91Sfrankho * the caller needs to proceed as it would on an ordinary
530f127cb91Sfrankho * mount.
531f127cb91Sfrankho */
532f127cb91Sfrankho *remounting = 0;
533f127cb91Sfrankho
534f127cb91Sfrankho ASSERT(*pseudodev);
535f127cb91Sfrankho return (0);
536f127cb91Sfrankho }
537f127cb91Sfrankho
538f127cb91Sfrankho /*
539f127cb91Sfrankho * Get the PCFS-specific mount options from the VFS framework.
540f127cb91Sfrankho * For "timezone" and "secsize", we need to parse the number
541f127cb91Sfrankho * ourselves and ensure its validity.
542f127cb91Sfrankho * Note: "secsize" is deliberately undocumented at this time,
543f127cb91Sfrankho * it's a workaround for devices (particularly: lofi image files)
544f127cb91Sfrankho * that don't support the DKIOCGMEDIAINFO ioctl for autodetection.
545f127cb91Sfrankho */
546f127cb91Sfrankho static void
pcfs_parse_mntopts(struct pcfs * fsp)5479e003ac4Sgd78059 pcfs_parse_mntopts(struct pcfs *fsp)
548f127cb91Sfrankho {
549f127cb91Sfrankho char *c;
550f127cb91Sfrankho char *endptr;
551f127cb91Sfrankho long l;
552f127cb91Sfrankho struct vfs *vfsp = fsp->pcfs_vfs;
553f127cb91Sfrankho
554f127cb91Sfrankho ASSERT(fsp->pcfs_secondswest == 0);
555f127cb91Sfrankho ASSERT(fsp->pcfs_secsize == 0);
556f127cb91Sfrankho
5577c478bd9Sstevel@tonic-gate if (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL))
5587c478bd9Sstevel@tonic-gate fsp->pcfs_flags |= PCFS_HIDDEN;
5597c478bd9Sstevel@tonic-gate if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL))
5607c478bd9Sstevel@tonic-gate fsp->pcfs_flags |= PCFS_FOLDCASE;
561264a6e74Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL))
562264a6e74Sfrankho fsp->pcfs_flags |= PCFS_NOCLAMPTIME;
563f127cb91Sfrankho if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL))
564f127cb91Sfrankho fsp->pcfs_flags |= PCFS_NOATIME;
5657c478bd9Sstevel@tonic-gate
566f127cb91Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_TIMEZONE, &c)) {
567f127cb91Sfrankho if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
568f127cb91Sfrankho endptr == c + strlen(c)) {
569f127cb91Sfrankho /*
570f127cb91Sfrankho * A number alright - in the allowed range ?
571f127cb91Sfrankho */
572f127cb91Sfrankho if (l <= -12*3600 || l >= 12*3600) {
573f127cb91Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of "
574f127cb91Sfrankho "'timezone' mount option - %ld "
575f127cb91Sfrankho "is out of range. Assuming 0.", l);
576f127cb91Sfrankho l = 0;
5777c478bd9Sstevel@tonic-gate }
578f127cb91Sfrankho } else {
579f127cb91Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of "
580f127cb91Sfrankho "'timezone' mount option - argument %s "
581f127cb91Sfrankho "is not a valid number. Assuming 0.", c);
582f127cb91Sfrankho l = 0;
583f127cb91Sfrankho }
584f127cb91Sfrankho fsp->pcfs_secondswest = l;
585f127cb91Sfrankho }
5867c478bd9Sstevel@tonic-gate
587f127cb91Sfrankho /*
588f127cb91Sfrankho * The "secsize=..." mount option is a workaround for the lack of
589f127cb91Sfrankho * lofi(7d) support for DKIOCGMEDIAINFO. If PCFS wants to parse the
590f127cb91Sfrankho * partition table of a disk image and it has been partitioned with
591f127cb91Sfrankho * sector sizes other than 512 bytes, we'd fail on loopback'ed disk
592f127cb91Sfrankho * images.
593f127cb91Sfrankho * That should really be fixed in lofi ... this is a workaround.
594f127cb91Sfrankho */
595f127cb91Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_SECSIZE, &c)) {
596f127cb91Sfrankho if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
597f127cb91Sfrankho endptr == c + strlen(c)) {
598f127cb91Sfrankho /*
599f127cb91Sfrankho * A number alright - a valid sector size as well ?
600f127cb91Sfrankho */
601f127cb91Sfrankho if (!VALID_SECSIZE(l)) {
602f127cb91Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of "
603f127cb91Sfrankho "'secsize' mount option - %ld is "
604f127cb91Sfrankho "unsupported. Autodetecting.", l);
605f127cb91Sfrankho l = 0;
606f127cb91Sfrankho }
607f127cb91Sfrankho } else {
608f127cb91Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of "
609f127cb91Sfrankho "'secsize' mount option - argument %s "
610f127cb91Sfrankho "is not a valid number. Autodetecting.", c);
611f127cb91Sfrankho l = 0;
612f127cb91Sfrankho }
613f127cb91Sfrankho fsp->pcfs_secsize = l;
614f127cb91Sfrankho fsp->pcfs_sdshift = ddi_ffs(l / DEV_BSIZE) - 1;
615f127cb91Sfrankho }
6167c478bd9Sstevel@tonic-gate }
6177c478bd9Sstevel@tonic-gate
6187c478bd9Sstevel@tonic-gate /*
6197c478bd9Sstevel@tonic-gate * vfs operations
6207c478bd9Sstevel@tonic-gate */
6217c478bd9Sstevel@tonic-gate
622f127cb91Sfrankho /*
623f127cb91Sfrankho * pcfs_mount - backend for VFS_MOUNT() on PCFS.
624f127cb91Sfrankho */
625f127cb91Sfrankho static int
pcfs_mount(struct vfs * vfsp,struct vnode * mvp,struct mounta * uap,struct cred * cr)626f127cb91Sfrankho pcfs_mount(
627f127cb91Sfrankho struct vfs *vfsp,
628f127cb91Sfrankho struct vnode *mvp,
629f127cb91Sfrankho struct mounta *uap,
630f127cb91Sfrankho struct cred *cr)
631f127cb91Sfrankho {
632f127cb91Sfrankho struct pcfs *fsp;
633f127cb91Sfrankho struct vnode *devvp;
634f127cb91Sfrankho dev_t pseudodev;
635f127cb91Sfrankho dev_t xdev;
636f127cb91Sfrankho int dos_ldrive = 0;
637f127cb91Sfrankho int error;
638f127cb91Sfrankho int remounting;
639f127cb91Sfrankho
640f127cb91Sfrankho if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
641f127cb91Sfrankho return (error);
642f127cb91Sfrankho
643f127cb91Sfrankho if (mvp->v_type != VDIR)
644f127cb91Sfrankho return (ENOTDIR);
645f127cb91Sfrankho
646f127cb91Sfrankho mutex_enter(&mvp->v_lock);
647f127cb91Sfrankho if ((uap->flags & MS_REMOUNT) == 0 &&
648f127cb91Sfrankho (uap->flags & MS_OVERLAY) == 0 &&
649f127cb91Sfrankho (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
650f127cb91Sfrankho mutex_exit(&mvp->v_lock);
651f127cb91Sfrankho return (EBUSY);
652f127cb91Sfrankho }
653f127cb91Sfrankho mutex_exit(&mvp->v_lock);
654f127cb91Sfrankho
655f127cb91Sfrankho /*
656f127cb91Sfrankho * PCFS doesn't do mount arguments anymore - everything's a mount
657f127cb91Sfrankho * option these days. In order not to break existing callers, we
658f127cb91Sfrankho * don't reject it yet, just warn that the data (if any) is ignored.
659f127cb91Sfrankho */
660f127cb91Sfrankho if (uap->datalen != 0)
661f127cb91Sfrankho cmn_err(CE_WARN, "!pcfs: deprecated use of mount(2) with "
662f127cb91Sfrankho "mount argument structures instead of mount options. "
663f127cb91Sfrankho "Ignoring mount(2) 'dataptr' argument.");
664f127cb91Sfrankho
665f127cb91Sfrankho /*
666c2aaf90fSgd78059 * This is needed early, to make sure the access / open calls
667c2aaf90fSgd78059 * are done using the correct mode. Processing this mount option
668c2aaf90fSgd78059 * only when calling pcfs_parse_mntopts() would lead us to attempt
669c2aaf90fSgd78059 * a read/write access to a possibly writeprotected device, and
670c2aaf90fSgd78059 * a readonly mount attempt might fail because of that.
671c2aaf90fSgd78059 */
672c2aaf90fSgd78059 if (uap->flags & MS_RDONLY) {
673c2aaf90fSgd78059 vfsp->vfs_flag |= VFS_RDONLY;
674c2aaf90fSgd78059 vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
675c2aaf90fSgd78059 }
676c2aaf90fSgd78059
677c2aaf90fSgd78059 /*
678f127cb91Sfrankho * For most filesystems, this is just a lookupname() on the
679f127cb91Sfrankho * mount pathname string. PCFS historically has to do its own
680f127cb91Sfrankho * partition table parsing because not all Solaris architectures
681f127cb91Sfrankho * support all styles of partitioning that PC media can have, and
682f127cb91Sfrankho * hence PCFS understands "device names" that don't map to actual
683f127cb91Sfrankho * physical device nodes. Parsing the "PCFS syntax" for device
684f127cb91Sfrankho * names is done in pcfs_device_identify() - see there.
685f127cb91Sfrankho *
686f127cb91Sfrankho * Once all block device drivers that can host FAT filesystems have
687f127cb91Sfrankho * been enhanced to create device nodes for all PC-style partitions,
688f127cb91Sfrankho * this code can go away.
689f127cb91Sfrankho */
690f127cb91Sfrankho if (error = pcfs_device_identify(vfsp, uap, cr, &dos_ldrive, &xdev))
691f127cb91Sfrankho return (error);
692f127cb91Sfrankho
693f127cb91Sfrankho /*
694f127cb91Sfrankho * As with looking up the actual device to mount, PCFS cannot rely
695f127cb91Sfrankho * on just the checks done by vfs_ismounted() whether a given device
696f127cb91Sfrankho * is mounted already. The additional check against the "PCFS syntax"
697f127cb91Sfrankho * is done in pcfs_device_ismounted().
698f127cb91Sfrankho */
699f127cb91Sfrankho remounting = (uap->flags & MS_REMOUNT);
700f127cb91Sfrankho
701f127cb91Sfrankho if (error = pcfs_device_ismounted(vfsp, dos_ldrive, xdev, &remounting,
702f127cb91Sfrankho &pseudodev))
703f127cb91Sfrankho return (error);
704f127cb91Sfrankho
705f127cb91Sfrankho if (remounting)
706f127cb91Sfrankho return (0);
707f127cb91Sfrankho
708f127cb91Sfrankho /*
709f127cb91Sfrankho * Mount the filesystem.
710f127cb91Sfrankho * An instance structure is required before the attempt to locate
711f127cb91Sfrankho * and parse the FAT BPB. This is because mount options may change
712f127cb91Sfrankho * the behaviour of the filesystem type matching code. Precreate
713f127cb91Sfrankho * it and fill it in to a degree that allows parsing the mount
714f127cb91Sfrankho * options.
715f127cb91Sfrankho */
716f127cb91Sfrankho devvp = makespecvp(xdev, VBLK);
717f127cb91Sfrankho if (IS_SWAPVP(devvp)) {
718f127cb91Sfrankho VN_RELE(devvp);
719f127cb91Sfrankho return (EBUSY);
720f127cb91Sfrankho }
721f127cb91Sfrankho error = VOP_OPEN(&devvp,
722da6c28aaSamw (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD | FWRITE, cr, NULL);
723f127cb91Sfrankho if (error) {
724f127cb91Sfrankho VN_RELE(devvp);
725f127cb91Sfrankho return (error);
726f127cb91Sfrankho }
727f127cb91Sfrankho
728f127cb91Sfrankho fsp = kmem_zalloc(sizeof (*fsp), KM_SLEEP);
729f127cb91Sfrankho fsp->pcfs_vfs = vfsp;
730f127cb91Sfrankho fsp->pcfs_xdev = xdev;
731f127cb91Sfrankho fsp->pcfs_devvp = devvp;
732f127cb91Sfrankho fsp->pcfs_ldrive = dos_ldrive;
733f127cb91Sfrankho mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL);
734f127cb91Sfrankho
7359e003ac4Sgd78059 pcfs_parse_mntopts(fsp);
736f127cb91Sfrankho
737f127cb91Sfrankho /*
738f127cb91Sfrankho * This is the actual "mount" - the PCFS superblock check.
739f127cb91Sfrankho *
740f127cb91Sfrankho * Find the requested logical drive and the FAT BPB therein.
741f127cb91Sfrankho * Check device type and flag the instance if media is removeable.
742f127cb91Sfrankho *
743f127cb91Sfrankho * Initializes most members of the filesystem instance structure.
744f127cb91Sfrankho * Returns EINVAL if no valid BPB can be found. Other errors may
745f127cb91Sfrankho * occur after I/O failures, or when invalid / unparseable partition
746f127cb91Sfrankho * tables are encountered.
747f127cb91Sfrankho */
748f127cb91Sfrankho if (error = pc_getfattype(fsp))
749f127cb91Sfrankho goto errout;
750f127cb91Sfrankho
751f127cb91Sfrankho /*
752c2aaf90fSgd78059 * Now that the BPB has been parsed, this structural information
753c2aaf90fSgd78059 * is available and known to be valid. Initialize the VFS.
754c2aaf90fSgd78059 */
755c2aaf90fSgd78059 vfsp->vfs_data = fsp;
756c2aaf90fSgd78059 vfsp->vfs_dev = pseudodev;
757c2aaf90fSgd78059 vfsp->vfs_fstype = pcfstype;
758c2aaf90fSgd78059 vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype);
759c2aaf90fSgd78059 vfsp->vfs_bcount = 0;
760c2aaf90fSgd78059 vfsp->vfs_bsize = fsp->pcfs_clsize;
761c2aaf90fSgd78059
762c2aaf90fSgd78059 /*
763f127cb91Sfrankho * Validate that we can access the FAT and that it is, to the
764f127cb91Sfrankho * degree we can verify here, self-consistent.
765f127cb91Sfrankho */
766f127cb91Sfrankho if (error = pc_verify(fsp))
767f127cb91Sfrankho goto errout;
768f127cb91Sfrankho
769f127cb91Sfrankho /*
770f127cb91Sfrankho * Record the time of the mount, to return as an "approximate"
771f127cb91Sfrankho * timestamp for the FAT root directory. Since FAT roots don't
772f127cb91Sfrankho * have timestamps, this is less confusing to the user than
773f127cb91Sfrankho * claiming "zero" / Jan/01/1970.
774f127cb91Sfrankho */
775f127cb91Sfrankho gethrestime(&fsp->pcfs_mounttime);
776f127cb91Sfrankho
777f127cb91Sfrankho /*
778f127cb91Sfrankho * Fix up the mount options. Because "noatime" is made default on
779f127cb91Sfrankho * removeable media only, a fixed disk will have neither "atime"
780f127cb91Sfrankho * nor "noatime" set. We set the options explicitly depending on
781f127cb91Sfrankho * the PCFS_NOATIME flag, to inform the user of what applies.
782f127cb91Sfrankho * Mount option cancellation will take care that the mutually
783f127cb91Sfrankho * exclusive 'other' is cleared.
784f127cb91Sfrankho */
785f127cb91Sfrankho vfs_setmntopt(vfsp,
786f127cb91Sfrankho fsp->pcfs_flags & PCFS_NOATIME ? MNTOPT_NOATIME : MNTOPT_ATIME,
787f127cb91Sfrankho NULL, 0);
788f127cb91Sfrankho
789f127cb91Sfrankho /*
790f127cb91Sfrankho * All clear - insert the FS instance into PCFS' list.
791f127cb91Sfrankho */
792f127cb91Sfrankho mutex_enter(&pcfslock);
793f127cb91Sfrankho fsp->pcfs_nxt = pc_mounttab;
794f127cb91Sfrankho pc_mounttab = fsp;
795f127cb91Sfrankho mutex_exit(&pcfslock);
796f127cb91Sfrankho atomic_inc_32(&pcfs_mountcount);
797f127cb91Sfrankho return (0);
798f127cb91Sfrankho
799f127cb91Sfrankho errout:
800f127cb91Sfrankho (void) VOP_CLOSE(devvp,
801f127cb91Sfrankho vfsp->vfs_flag & VFS_RDONLY ? FREAD : FREAD | FWRITE,
802da6c28aaSamw 1, (offset_t)0, cr, NULL);
803f127cb91Sfrankho VN_RELE(devvp);
804f127cb91Sfrankho mutex_destroy(&fsp->pcfs_lock);
805f127cb91Sfrankho kmem_free(fsp, sizeof (*fsp));
806f127cb91Sfrankho return (error);
807f127cb91Sfrankho
808f127cb91Sfrankho }
809f127cb91Sfrankho
8107c478bd9Sstevel@tonic-gate static int
pcfs_unmount(struct vfs * vfsp,int flag,struct cred * cr)8117c478bd9Sstevel@tonic-gate pcfs_unmount(
8127c478bd9Sstevel@tonic-gate struct vfs *vfsp,
8137c478bd9Sstevel@tonic-gate int flag,
8147c478bd9Sstevel@tonic-gate struct cred *cr)
8157c478bd9Sstevel@tonic-gate {
8167c478bd9Sstevel@tonic-gate struct pcfs *fsp, *fsp1;
8177c478bd9Sstevel@tonic-gate
8187c478bd9Sstevel@tonic-gate if (secpolicy_fs_unmount(cr, vfsp) != 0)
8197c478bd9Sstevel@tonic-gate return (EPERM);
8207c478bd9Sstevel@tonic-gate
8217c478bd9Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp);
822264a6e74Sfrankho
8237c478bd9Sstevel@tonic-gate /*
8247c478bd9Sstevel@tonic-gate * We don't have to lock fsp because the VVFSLOCK in vfs layer will
8257c478bd9Sstevel@tonic-gate * prevent lookuppn from crossing the mount point.
826264a6e74Sfrankho * If this is not a forced umount request and there's ongoing I/O,
827264a6e74Sfrankho * don't allow the mount to proceed.
8287c478bd9Sstevel@tonic-gate */
829264a6e74Sfrankho if (flag & MS_FORCE)
830264a6e74Sfrankho vfsp->vfs_flag |= VFS_UNMOUNTED;
831264a6e74Sfrankho else if (fsp->pcfs_nrefs)
8327c478bd9Sstevel@tonic-gate return (EBUSY);
833264a6e74Sfrankho
834264a6e74Sfrankho mutex_enter(&pcfslock);
8357c478bd9Sstevel@tonic-gate
8367c478bd9Sstevel@tonic-gate /*
837264a6e74Sfrankho * If this is a forced umount request or if the fs instance has
838264a6e74Sfrankho * been marked as beyond recovery, allow the umount to proceed
839264a6e74Sfrankho * regardless of state. pc_diskchanged() forcibly releases all
840264a6e74Sfrankho * inactive vnodes/pcnodes.
8417c478bd9Sstevel@tonic-gate */
842264a6e74Sfrankho if (flag & MS_FORCE || fsp->pcfs_flags & PCFS_IRRECOV) {
8437c478bd9Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_WRITER);
8447c478bd9Sstevel@tonic-gate pc_diskchanged(fsp);
8457c478bd9Sstevel@tonic-gate rw_exit(&pcnodes_lock);
8467c478bd9Sstevel@tonic-gate }
8477c478bd9Sstevel@tonic-gate
8487c478bd9Sstevel@tonic-gate /* now there should be no pcp node on pcfhead or pcdhead. */
8497c478bd9Sstevel@tonic-gate
8507c478bd9Sstevel@tonic-gate if (fsp == pc_mounttab) {
8517c478bd9Sstevel@tonic-gate pc_mounttab = fsp->pcfs_nxt;
8527c478bd9Sstevel@tonic-gate } else {
8537c478bd9Sstevel@tonic-gate for (fsp1 = pc_mounttab; fsp1 != NULL; fsp1 = fsp1->pcfs_nxt)
8547c478bd9Sstevel@tonic-gate if (fsp1->pcfs_nxt == fsp)
8557c478bd9Sstevel@tonic-gate fsp1->pcfs_nxt = fsp->pcfs_nxt;
8567c478bd9Sstevel@tonic-gate }
8577c478bd9Sstevel@tonic-gate
8587c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock);
8597c478bd9Sstevel@tonic-gate
860264a6e74Sfrankho /*
861264a6e74Sfrankho * Since we support VFS_FREEVFS(), there's no need to
862264a6e74Sfrankho * free the fsp right now. The framework will tell us
863264a6e74Sfrankho * when the right time to do so has arrived by calling
864264a6e74Sfrankho * into pcfs_freevfs.
865264a6e74Sfrankho */
8667c478bd9Sstevel@tonic-gate return (0);
8677c478bd9Sstevel@tonic-gate }
8687c478bd9Sstevel@tonic-gate
8697c478bd9Sstevel@tonic-gate /*
8707c478bd9Sstevel@tonic-gate * find root of pcfs
8717c478bd9Sstevel@tonic-gate */
8727c478bd9Sstevel@tonic-gate static int
pcfs_root(struct vfs * vfsp,struct vnode ** vpp)8737c478bd9Sstevel@tonic-gate pcfs_root(
8747c478bd9Sstevel@tonic-gate struct vfs *vfsp,
8757c478bd9Sstevel@tonic-gate struct vnode **vpp)
8767c478bd9Sstevel@tonic-gate {
8777c478bd9Sstevel@tonic-gate struct pcfs *fsp;
8787c478bd9Sstevel@tonic-gate struct pcnode *pcp;
8797c478bd9Sstevel@tonic-gate int error;
8807c478bd9Sstevel@tonic-gate
8817c478bd9Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp);
8827c478bd9Sstevel@tonic-gate if (error = pc_lockfs(fsp, 0, 0))
8837c478bd9Sstevel@tonic-gate return (error);
884264a6e74Sfrankho
8857c478bd9Sstevel@tonic-gate pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
8867c478bd9Sstevel@tonic-gate pc_unlockfs(fsp);
8877c478bd9Sstevel@tonic-gate *vpp = PCTOV(pcp);
8887c478bd9Sstevel@tonic-gate pcp->pc_flags |= PC_EXTERNAL;
8897c478bd9Sstevel@tonic-gate return (0);
8907c478bd9Sstevel@tonic-gate }
8917c478bd9Sstevel@tonic-gate
8927c478bd9Sstevel@tonic-gate /*
8937c478bd9Sstevel@tonic-gate * Get file system statistics.
8947c478bd9Sstevel@tonic-gate */
8957c478bd9Sstevel@tonic-gate static int
pcfs_statvfs(struct vfs * vfsp,struct statvfs64 * sp)8967c478bd9Sstevel@tonic-gate pcfs_statvfs(
8977c478bd9Sstevel@tonic-gate struct vfs *vfsp,
8987c478bd9Sstevel@tonic-gate struct statvfs64 *sp)
8997c478bd9Sstevel@tonic-gate {
9007c478bd9Sstevel@tonic-gate struct pcfs *fsp;
9017c478bd9Sstevel@tonic-gate int error;
9027c478bd9Sstevel@tonic-gate dev32_t d32;
9037c478bd9Sstevel@tonic-gate
9047c478bd9Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp);
9057c478bd9Sstevel@tonic-gate error = pc_getfat(fsp);
9067c478bd9Sstevel@tonic-gate if (error)
9077c478bd9Sstevel@tonic-gate return (error);
9087c478bd9Sstevel@tonic-gate bzero(sp, sizeof (*sp));
9097c478bd9Sstevel@tonic-gate sp->f_bsize = sp->f_frsize = fsp->pcfs_clsize;
9107c478bd9Sstevel@tonic-gate sp->f_blocks = (fsblkcnt64_t)fsp->pcfs_ncluster;
9117c478bd9Sstevel@tonic-gate sp->f_bavail = sp->f_bfree = (fsblkcnt64_t)pc_freeclusters(fsp);
9127c478bd9Sstevel@tonic-gate sp->f_files = (fsfilcnt64_t)-1;
9137c478bd9Sstevel@tonic-gate sp->f_ffree = (fsfilcnt64_t)-1;
9147c478bd9Sstevel@tonic-gate sp->f_favail = (fsfilcnt64_t)-1;
9157c478bd9Sstevel@tonic-gate #ifdef notdef
9167c478bd9Sstevel@tonic-gate (void) cmpldev(&d32, fsp->pcfs_devvp->v_rdev);
9177c478bd9Sstevel@tonic-gate #endif /* notdef */
9187c478bd9Sstevel@tonic-gate (void) cmpldev(&d32, vfsp->vfs_dev);
9197c478bd9Sstevel@tonic-gate sp->f_fsid = d32;
9207c478bd9Sstevel@tonic-gate (void) strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name);
9217c478bd9Sstevel@tonic-gate sp->f_flag = vf_to_stf(vfsp->vfs_flag);
922e8f766d8Sbatschul sp->f_namemax = PCMAXNAMLEN;
9237c478bd9Sstevel@tonic-gate return (0);
9247c478bd9Sstevel@tonic-gate }
9257c478bd9Sstevel@tonic-gate
9267c478bd9Sstevel@tonic-gate static int
pc_syncfsnodes(struct pcfs * fsp)9277c478bd9Sstevel@tonic-gate pc_syncfsnodes(struct pcfs *fsp)
9287c478bd9Sstevel@tonic-gate {
9297c478bd9Sstevel@tonic-gate struct pchead *hp;
9307c478bd9Sstevel@tonic-gate struct pcnode *pcp;
9317c478bd9Sstevel@tonic-gate int error;
9327c478bd9Sstevel@tonic-gate
9337c478bd9Sstevel@tonic-gate if (error = pc_lockfs(fsp, 0, 0))
9347c478bd9Sstevel@tonic-gate return (error);
9357c478bd9Sstevel@tonic-gate
9367c478bd9Sstevel@tonic-gate if (!(error = pc_syncfat(fsp))) {
9377c478bd9Sstevel@tonic-gate hp = pcfhead;
9387c478bd9Sstevel@tonic-gate while (hp < & pcfhead [ NPCHASH ]) {
9397c478bd9Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_READER);
9407c478bd9Sstevel@tonic-gate pcp = hp->pch_forw;
9417c478bd9Sstevel@tonic-gate while (pcp != (struct pcnode *)hp) {
9427c478bd9Sstevel@tonic-gate if (VFSTOPCFS(PCTOV(pcp) -> v_vfsp) == fsp)
9437c478bd9Sstevel@tonic-gate if (error = pc_nodesync(pcp))
9447c478bd9Sstevel@tonic-gate break;
9457c478bd9Sstevel@tonic-gate pcp = pcp -> pc_forw;
9467c478bd9Sstevel@tonic-gate }
9477c478bd9Sstevel@tonic-gate rw_exit(&pcnodes_lock);
9487c478bd9Sstevel@tonic-gate if (error)
9497c478bd9Sstevel@tonic-gate break;
9507c478bd9Sstevel@tonic-gate hp++;
9517c478bd9Sstevel@tonic-gate }
9527c478bd9Sstevel@tonic-gate }
9537c478bd9Sstevel@tonic-gate pc_unlockfs(fsp);
9547c478bd9Sstevel@tonic-gate return (error);
9557c478bd9Sstevel@tonic-gate }
9567c478bd9Sstevel@tonic-gate
9577c478bd9Sstevel@tonic-gate /*
9587c478bd9Sstevel@tonic-gate * Flush any pending I/O.
9597c478bd9Sstevel@tonic-gate */
9607c478bd9Sstevel@tonic-gate /*ARGSUSED*/
9617c478bd9Sstevel@tonic-gate static int
pcfs_sync(struct vfs * vfsp,short flag,struct cred * cr)9627c478bd9Sstevel@tonic-gate pcfs_sync(
9637c478bd9Sstevel@tonic-gate struct vfs *vfsp,
9647c478bd9Sstevel@tonic-gate short flag,
9657c478bd9Sstevel@tonic-gate struct cred *cr)
9667c478bd9Sstevel@tonic-gate {
9677c478bd9Sstevel@tonic-gate struct pcfs *fsp;
9687c478bd9Sstevel@tonic-gate int error = 0;
9697c478bd9Sstevel@tonic-gate
9707c478bd9Sstevel@tonic-gate /* this prevents the filesystem from being umounted. */
9717c478bd9Sstevel@tonic-gate mutex_enter(&pcfslock);
9727c478bd9Sstevel@tonic-gate if (vfsp != NULL) {
9737c478bd9Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp);
9747c478bd9Sstevel@tonic-gate if (!(fsp->pcfs_flags & PCFS_IRRECOV)) {
9757c478bd9Sstevel@tonic-gate error = pc_syncfsnodes(fsp);
9767c478bd9Sstevel@tonic-gate } else {
9777c478bd9Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_WRITER);
9787c478bd9Sstevel@tonic-gate pc_diskchanged(fsp);
9797c478bd9Sstevel@tonic-gate rw_exit(&pcnodes_lock);
9807c478bd9Sstevel@tonic-gate error = EIO;
9817c478bd9Sstevel@tonic-gate }
9827c478bd9Sstevel@tonic-gate } else {
9837c478bd9Sstevel@tonic-gate fsp = pc_mounttab;
9847c478bd9Sstevel@tonic-gate while (fsp != NULL) {
9857c478bd9Sstevel@tonic-gate if (fsp->pcfs_flags & PCFS_IRRECOV) {
9867c478bd9Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_WRITER);
9877c478bd9Sstevel@tonic-gate pc_diskchanged(fsp);
9887c478bd9Sstevel@tonic-gate rw_exit(&pcnodes_lock);
9897c478bd9Sstevel@tonic-gate error = EIO;
9907c478bd9Sstevel@tonic-gate break;
9917c478bd9Sstevel@tonic-gate }
9927c478bd9Sstevel@tonic-gate error = pc_syncfsnodes(fsp);
9937c478bd9Sstevel@tonic-gate if (error) break;
9947c478bd9Sstevel@tonic-gate fsp = fsp->pcfs_nxt;
9957c478bd9Sstevel@tonic-gate }
9967c478bd9Sstevel@tonic-gate }
9977c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock);
9987c478bd9Sstevel@tonic-gate return (error);
9997c478bd9Sstevel@tonic-gate }
10007c478bd9Sstevel@tonic-gate
10017c478bd9Sstevel@tonic-gate int
pc_lockfs(struct pcfs * fsp,int diskchanged,int releasing)10027c478bd9Sstevel@tonic-gate pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing)
10037c478bd9Sstevel@tonic-gate {
1004264a6e74Sfrankho int err;
1005264a6e74Sfrankho
10067c478bd9Sstevel@tonic-gate if ((fsp->pcfs_flags & PCFS_IRRECOV) && !releasing)
10077c478bd9Sstevel@tonic-gate return (EIO);
10087c478bd9Sstevel@tonic-gate
10097c478bd9Sstevel@tonic-gate if ((fsp->pcfs_flags & PCFS_LOCKED) && (fsp->pcfs_owner == curthread)) {
10107c478bd9Sstevel@tonic-gate fsp->pcfs_count++;
10117c478bd9Sstevel@tonic-gate } else {
10127c478bd9Sstevel@tonic-gate mutex_enter(&fsp->pcfs_lock);
10137c478bd9Sstevel@tonic-gate if (fsp->pcfs_flags & PCFS_LOCKED)
10147c478bd9Sstevel@tonic-gate panic("pc_lockfs");
10157c478bd9Sstevel@tonic-gate /*
10167c478bd9Sstevel@tonic-gate * We check the IRRECOV bit again just in case somebody
10177c478bd9Sstevel@tonic-gate * snuck past the initial check but then got held up before
10187c478bd9Sstevel@tonic-gate * they could grab the lock. (And in the meantime someone
10197c478bd9Sstevel@tonic-gate * had grabbed the lock and set the bit)
10207c478bd9Sstevel@tonic-gate */
10210576819eSwyllys if (!diskchanged && !(fsp->pcfs_flags & PCFS_IRRECOV)) {
1022264a6e74Sfrankho if ((err = pc_getfat(fsp))) {
1023264a6e74Sfrankho mutex_exit(&fsp->pcfs_lock);
10240576819eSwyllys return (err);
10250576819eSwyllys }
1026264a6e74Sfrankho }
10277c478bd9Sstevel@tonic-gate fsp->pcfs_flags |= PCFS_LOCKED;
10287c478bd9Sstevel@tonic-gate fsp->pcfs_owner = curthread;
10297c478bd9Sstevel@tonic-gate fsp->pcfs_count++;
10307c478bd9Sstevel@tonic-gate }
10317c478bd9Sstevel@tonic-gate return (0);
10327c478bd9Sstevel@tonic-gate }
10337c478bd9Sstevel@tonic-gate
10347c478bd9Sstevel@tonic-gate void
pc_unlockfs(struct pcfs * fsp)10357c478bd9Sstevel@tonic-gate pc_unlockfs(struct pcfs *fsp)
10367c478bd9Sstevel@tonic-gate {
10377c478bd9Sstevel@tonic-gate
10387c478bd9Sstevel@tonic-gate if ((fsp->pcfs_flags & PCFS_LOCKED) == 0)
10397c478bd9Sstevel@tonic-gate panic("pc_unlockfs");
10407c478bd9Sstevel@tonic-gate if (--fsp->pcfs_count < 0)
10417c478bd9Sstevel@tonic-gate panic("pc_unlockfs: count");
10427c478bd9Sstevel@tonic-gate if (fsp->pcfs_count == 0) {
10437c478bd9Sstevel@tonic-gate fsp->pcfs_flags &= ~PCFS_LOCKED;
10447c478bd9Sstevel@tonic-gate fsp->pcfs_owner = 0;
10457c478bd9Sstevel@tonic-gate mutex_exit(&fsp->pcfs_lock);
10467c478bd9Sstevel@tonic-gate }
10477c478bd9Sstevel@tonic-gate }
10487c478bd9Sstevel@tonic-gate
10497c478bd9Sstevel@tonic-gate int
pc_syncfat(struct pcfs * fsp)10507c478bd9Sstevel@tonic-gate pc_syncfat(struct pcfs *fsp)
10517c478bd9Sstevel@tonic-gate {
10527c478bd9Sstevel@tonic-gate struct buf *bp;
10537c478bd9Sstevel@tonic-gate int nfat;
1054f127cb91Sfrankho int error = 0;
1055f127cb91Sfrankho struct fat_od_fsi *fsinfo_disk;
10567c478bd9Sstevel@tonic-gate
10577c478bd9Sstevel@tonic-gate if ((fsp->pcfs_fatp == (uchar_t *)0) ||
10587c478bd9Sstevel@tonic-gate !(fsp->pcfs_flags & PCFS_FATMOD))
10597c478bd9Sstevel@tonic-gate return (0);
10607c478bd9Sstevel@tonic-gate /*
10617c478bd9Sstevel@tonic-gate * write out all copies of FATs
10627c478bd9Sstevel@tonic-gate */
10637c478bd9Sstevel@tonic-gate fsp->pcfs_flags &= ~PCFS_FATMOD;
10647c478bd9Sstevel@tonic-gate fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
10657c478bd9Sstevel@tonic-gate for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) {
1066f127cb91Sfrankho error = pc_writefat(fsp, pc_dbdaddr(fsp,
1067f127cb91Sfrankho fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec));
10687c478bd9Sstevel@tonic-gate if (error) {
10697c478bd9Sstevel@tonic-gate pc_mark_irrecov(fsp);
10707c478bd9Sstevel@tonic-gate return (EIO);
10717c478bd9Sstevel@tonic-gate }
10727c478bd9Sstevel@tonic-gate }
10737c478bd9Sstevel@tonic-gate pc_clear_fatchanges(fsp);
1074f127cb91Sfrankho
1075f127cb91Sfrankho /*
1076f127cb91Sfrankho * Write out fsinfo sector.
1077f127cb91Sfrankho */
10787c478bd9Sstevel@tonic-gate if (IS_FAT32(fsp)) {
10797c478bd9Sstevel@tonic-gate bp = bread(fsp->pcfs_xdev,
1080f127cb91Sfrankho pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
10817c478bd9Sstevel@tonic-gate if (bp->b_flags & (B_ERROR | B_STALE)) {
1082f127cb91Sfrankho error = geterror(bp);
10837c478bd9Sstevel@tonic-gate }
1084f127cb91Sfrankho fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
1085f127cb91Sfrankho if (!error && FSISIG_OK(fsinfo_disk)) {
1086f127cb91Sfrankho fsinfo_disk->fsi_incore.fs_free_clusters =
1087f127cb91Sfrankho LE_32(fsp->pcfs_fsinfo.fs_free_clusters);
1088f127cb91Sfrankho fsinfo_disk->fsi_incore.fs_next_free =
1089f127cb91Sfrankho LE_32(FSINFO_UNKNOWN);
10907c478bd9Sstevel@tonic-gate bwrite2(bp);
10917c478bd9Sstevel@tonic-gate error = geterror(bp);
1092f127cb91Sfrankho }
10937c478bd9Sstevel@tonic-gate brelse(bp);
10947c478bd9Sstevel@tonic-gate if (error) {
10957c478bd9Sstevel@tonic-gate pc_mark_irrecov(fsp);
10967c478bd9Sstevel@tonic-gate return (EIO);
10977c478bd9Sstevel@tonic-gate }
10987c478bd9Sstevel@tonic-gate }
10997c478bd9Sstevel@tonic-gate return (0);
11007c478bd9Sstevel@tonic-gate }
11017c478bd9Sstevel@tonic-gate
11027c478bd9Sstevel@tonic-gate void
pc_invalfat(struct pcfs * fsp)11037c478bd9Sstevel@tonic-gate pc_invalfat(struct pcfs *fsp)
11047c478bd9Sstevel@tonic-gate {
11057c478bd9Sstevel@tonic-gate struct pcfs *xfsp;
11067c478bd9Sstevel@tonic-gate int mount_cnt = 0;
11077c478bd9Sstevel@tonic-gate
11087c478bd9Sstevel@tonic-gate if (fsp->pcfs_fatp == (uchar_t *)0)
11097c478bd9Sstevel@tonic-gate panic("pc_invalfat");
11107c478bd9Sstevel@tonic-gate /*
11117c478bd9Sstevel@tonic-gate * Release FAT
11127c478bd9Sstevel@tonic-gate */
1113f127cb91Sfrankho kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsec * fsp->pcfs_secsize);
11147c478bd9Sstevel@tonic-gate fsp->pcfs_fatp = NULL;
11157c478bd9Sstevel@tonic-gate kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize);
11167c478bd9Sstevel@tonic-gate fsp->pcfs_fat_changemap = NULL;
11177c478bd9Sstevel@tonic-gate /*
11187c478bd9Sstevel@tonic-gate * Invalidate all the blocks associated with the device.
11197c478bd9Sstevel@tonic-gate * Not needed if stateless.
11207c478bd9Sstevel@tonic-gate */
11217c478bd9Sstevel@tonic-gate for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt)
11227c478bd9Sstevel@tonic-gate if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev)
11237c478bd9Sstevel@tonic-gate mount_cnt++;
11247c478bd9Sstevel@tonic-gate
11257c478bd9Sstevel@tonic-gate if (!mount_cnt)
11267c478bd9Sstevel@tonic-gate binval(fsp->pcfs_xdev);
11277c478bd9Sstevel@tonic-gate /*
11287c478bd9Sstevel@tonic-gate * close mounted device
11297c478bd9Sstevel@tonic-gate */
11307c478bd9Sstevel@tonic-gate (void) VOP_CLOSE(fsp->pcfs_devvp,
11317c478bd9Sstevel@tonic-gate (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
1132da6c28aaSamw 1, (offset_t)0, CRED(), NULL);
11337c478bd9Sstevel@tonic-gate }
11347c478bd9Sstevel@tonic-gate
11357c478bd9Sstevel@tonic-gate void
pc_badfs(struct pcfs * fsp)11367c478bd9Sstevel@tonic-gate pc_badfs(struct pcfs *fsp)
11377c478bd9Sstevel@tonic-gate {
1138f127cb91Sfrankho cmn_err(CE_WARN, "corrupted PC file system on dev (%x.%x):%d\n",
11397c478bd9Sstevel@tonic-gate getmajor(fsp->pcfs_devvp->v_rdev),
1140f127cb91Sfrankho getminor(fsp->pcfs_devvp->v_rdev), fsp->pcfs_ldrive);
11417c478bd9Sstevel@tonic-gate }
11427c478bd9Sstevel@tonic-gate
11437c478bd9Sstevel@tonic-gate /*
11447c478bd9Sstevel@tonic-gate * The problem with supporting NFS on the PCFS filesystem is that there
11457c478bd9Sstevel@tonic-gate * is no good place to keep the generation number. The only possible
11467c478bd9Sstevel@tonic-gate * place is inside a directory entry. There are a few words that we
11477c478bd9Sstevel@tonic-gate * don't use - they store NT & OS/2 attributes, and the creation/last access
11487c478bd9Sstevel@tonic-gate * time of the file - but it seems wrong to use them. In addition, directory
11497c478bd9Sstevel@tonic-gate * entries come and go. If a directory is removed completely, its directory
11507c478bd9Sstevel@tonic-gate * blocks are freed and the generation numbers are lost. Whereas in ufs,
11517c478bd9Sstevel@tonic-gate * inode blocks are dedicated for inodes, so the generation numbers are
11527c478bd9Sstevel@tonic-gate * permanently kept on the disk.
11537c478bd9Sstevel@tonic-gate */
11547c478bd9Sstevel@tonic-gate static int
pcfs_vget(struct vfs * vfsp,struct vnode ** vpp,struct fid * fidp)11557c478bd9Sstevel@tonic-gate pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp)
11567c478bd9Sstevel@tonic-gate {
11577c478bd9Sstevel@tonic-gate struct pcnode *pcp;
11587c478bd9Sstevel@tonic-gate struct pc_fid *pcfid;
11597c478bd9Sstevel@tonic-gate struct pcfs *fsp;
11607c478bd9Sstevel@tonic-gate struct pcdir *ep;
11617c478bd9Sstevel@tonic-gate daddr_t eblkno;
11627c478bd9Sstevel@tonic-gate int eoffset;
11637c478bd9Sstevel@tonic-gate struct buf *bp;
11647c478bd9Sstevel@tonic-gate int error;
11657c478bd9Sstevel@tonic-gate pc_cluster32_t cn;
11667c478bd9Sstevel@tonic-gate
11677c478bd9Sstevel@tonic-gate pcfid = (struct pc_fid *)fidp;
11687c478bd9Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp);
11697c478bd9Sstevel@tonic-gate
11707c478bd9Sstevel@tonic-gate error = pc_lockfs(fsp, 0, 0);
11717c478bd9Sstevel@tonic-gate if (error) {
11727c478bd9Sstevel@tonic-gate *vpp = NULL;
11737c478bd9Sstevel@tonic-gate return (error);
11747c478bd9Sstevel@tonic-gate }
11757c478bd9Sstevel@tonic-gate
11767c478bd9Sstevel@tonic-gate if (pcfid->pcfid_block == 0) {
11777c478bd9Sstevel@tonic-gate pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
11787c478bd9Sstevel@tonic-gate pcp->pc_flags |= PC_EXTERNAL;
11797c478bd9Sstevel@tonic-gate *vpp = PCTOV(pcp);
11807c478bd9Sstevel@tonic-gate pc_unlockfs(fsp);
11817c478bd9Sstevel@tonic-gate return (0);
11827c478bd9Sstevel@tonic-gate }
11837c478bd9Sstevel@tonic-gate eblkno = pcfid->pcfid_block;
11847c478bd9Sstevel@tonic-gate eoffset = pcfid->pcfid_offset;
1185f127cb91Sfrankho
11867c478bd9Sstevel@tonic-gate if ((pc_dbtocl(fsp,
11877c478bd9Sstevel@tonic-gate eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) ||
11887c478bd9Sstevel@tonic-gate (eoffset > fsp->pcfs_clsize)) {
11897c478bd9Sstevel@tonic-gate pc_unlockfs(fsp);
11907c478bd9Sstevel@tonic-gate *vpp = NULL;
11917c478bd9Sstevel@tonic-gate return (EINVAL);
11927c478bd9Sstevel@tonic-gate }
11937c478bd9Sstevel@tonic-gate
11947c478bd9Sstevel@tonic-gate if (eblkno >= fsp->pcfs_datastart || (eblkno - fsp->pcfs_rdirstart)
11957c478bd9Sstevel@tonic-gate < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
1196f127cb91Sfrankho bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
1197f127cb91Sfrankho fsp->pcfs_clsize);
11987c478bd9Sstevel@tonic-gate } else {
1199f127cb91Sfrankho /*
1200f127cb91Sfrankho * This is an access "backwards" into the FAT12/FAT16
1201f127cb91Sfrankho * root directory. A better code structure would
1202f127cb91Sfrankho * significantly improve maintainability here ...
1203f127cb91Sfrankho */
1204f127cb91Sfrankho bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
12057c478bd9Sstevel@tonic-gate (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize);
12067c478bd9Sstevel@tonic-gate }
12077c478bd9Sstevel@tonic-gate if (bp->b_flags & (B_ERROR | B_STALE)) {
12087c478bd9Sstevel@tonic-gate error = geterror(bp);
12097c478bd9Sstevel@tonic-gate brelse(bp);
12107c478bd9Sstevel@tonic-gate if (error)
12117c478bd9Sstevel@tonic-gate pc_mark_irrecov(fsp);
12127c478bd9Sstevel@tonic-gate *vpp = NULL;
12137c478bd9Sstevel@tonic-gate pc_unlockfs(fsp);
12147c478bd9Sstevel@tonic-gate return (error);
12157c478bd9Sstevel@tonic-gate }
12167c478bd9Sstevel@tonic-gate ep = (struct pcdir *)(bp->b_un.b_addr + eoffset);
12177c478bd9Sstevel@tonic-gate /*
12187c478bd9Sstevel@tonic-gate * Ok, if this is a valid file handle that we gave out,
12197c478bd9Sstevel@tonic-gate * then simply ensuring that the creation time matches,
12207c478bd9Sstevel@tonic-gate * the entry has not been deleted, and it has a valid first
12217c478bd9Sstevel@tonic-gate * character should be enough.
12227c478bd9Sstevel@tonic-gate *
12237c478bd9Sstevel@tonic-gate * Unfortunately, verifying that the <blkno, offset> _still_
12247c478bd9Sstevel@tonic-gate * refers to a directory entry is not easy, since we'd have
12257c478bd9Sstevel@tonic-gate * to search _all_ directories starting from root to find it.
12267c478bd9Sstevel@tonic-gate * That's a high price to pay just in case somebody is forging
12277c478bd9Sstevel@tonic-gate * file handles. So instead we verify that as much of the
12287c478bd9Sstevel@tonic-gate * entry is valid as we can:
12297c478bd9Sstevel@tonic-gate *
12307c478bd9Sstevel@tonic-gate * 1. The starting cluster is 0 (unallocated) or valid
12317c478bd9Sstevel@tonic-gate * 2. It is not an LFN entry
12327c478bd9Sstevel@tonic-gate * 3. It is not hidden (unless mounted as such)
12337c478bd9Sstevel@tonic-gate * 4. It is not the label
12347c478bd9Sstevel@tonic-gate */
12357c478bd9Sstevel@tonic-gate cn = pc_getstartcluster(fsp, ep);
12367c478bd9Sstevel@tonic-gate /*
12377c478bd9Sstevel@tonic-gate * if the starting cluster is valid, but not valid according
12387c478bd9Sstevel@tonic-gate * to pc_validcl(), force it to be to simplify the following if.
12397c478bd9Sstevel@tonic-gate */
12407c478bd9Sstevel@tonic-gate if (cn == 0)
12417c478bd9Sstevel@tonic-gate cn = PCF_FIRSTCLUSTER;
12427c478bd9Sstevel@tonic-gate if (IS_FAT32(fsp)) {
12437c478bd9Sstevel@tonic-gate if (cn >= PCF_LASTCLUSTER32)
12447c478bd9Sstevel@tonic-gate cn = PCF_FIRSTCLUSTER;
12457c478bd9Sstevel@tonic-gate } else {
12467c478bd9Sstevel@tonic-gate if (cn >= PCF_LASTCLUSTER)
12477c478bd9Sstevel@tonic-gate cn = PCF_FIRSTCLUSTER;
12487c478bd9Sstevel@tonic-gate }
12497c478bd9Sstevel@tonic-gate if ((!pc_validcl(fsp, cn)) ||
12507c478bd9Sstevel@tonic-gate (PCDL_IS_LFN(ep)) ||
12517c478bd9Sstevel@tonic-gate (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) ||
12527c478bd9Sstevel@tonic-gate ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) {
12537c478bd9Sstevel@tonic-gate bp->b_flags |= B_STALE | B_AGE;
12547c478bd9Sstevel@tonic-gate brelse(bp);
12557c478bd9Sstevel@tonic-gate pc_unlockfs(fsp);
12567c478bd9Sstevel@tonic-gate return (EINVAL);
12577c478bd9Sstevel@tonic-gate }
12587c478bd9Sstevel@tonic-gate if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) &&
12597c478bd9Sstevel@tonic-gate (ep->pcd_filename[0] != PCD_ERASED) &&
12607c478bd9Sstevel@tonic-gate (pc_validchar(ep->pcd_filename[0]) ||
12617c478bd9Sstevel@tonic-gate (ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) {
12627c478bd9Sstevel@tonic-gate pcp = pc_getnode(fsp, eblkno, eoffset, ep);
12637c478bd9Sstevel@tonic-gate pcp->pc_flags |= PC_EXTERNAL;
12647c478bd9Sstevel@tonic-gate *vpp = PCTOV(pcp);
12657c478bd9Sstevel@tonic-gate } else {
12667c478bd9Sstevel@tonic-gate *vpp = NULL;
12677c478bd9Sstevel@tonic-gate }
12687c478bd9Sstevel@tonic-gate bp->b_flags |= B_STALE | B_AGE;
12697c478bd9Sstevel@tonic-gate brelse(bp);
12707c478bd9Sstevel@tonic-gate pc_unlockfs(fsp);
12717c478bd9Sstevel@tonic-gate return (0);
12727c478bd9Sstevel@tonic-gate }
12737c478bd9Sstevel@tonic-gate
12747c478bd9Sstevel@tonic-gate /*
12757c478bd9Sstevel@tonic-gate * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about
12767c478bd9Sstevel@tonic-gate * a meg), so we can't bread() it all in at once. This routine reads a
12777c478bd9Sstevel@tonic-gate * fat a chunk at a time.
12787c478bd9Sstevel@tonic-gate */
12797c478bd9Sstevel@tonic-gate static int
pc_readfat(struct pcfs * fsp,uchar_t * fatp)1280f127cb91Sfrankho pc_readfat(struct pcfs *fsp, uchar_t *fatp)
12817c478bd9Sstevel@tonic-gate {
12827c478bd9Sstevel@tonic-gate struct buf *bp;
12837c478bd9Sstevel@tonic-gate size_t off;
12847c478bd9Sstevel@tonic-gate size_t readsize;
1285f127cb91Sfrankho daddr_t diskblk;
1286f127cb91Sfrankho size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
1287f127cb91Sfrankho daddr_t start = fsp->pcfs_fatstart;
12887c478bd9Sstevel@tonic-gate
12897c478bd9Sstevel@tonic-gate readsize = fsp->pcfs_clsize;
12907c478bd9Sstevel@tonic-gate for (off = 0; off < fatsize; off += readsize, fatp += readsize) {
12917c478bd9Sstevel@tonic-gate if (readsize > (fatsize - off))
12927c478bd9Sstevel@tonic-gate readsize = fatsize - off;
1293f127cb91Sfrankho diskblk = pc_dbdaddr(fsp, start +
1294f127cb91Sfrankho pc_cltodb(fsp, pc_lblkno(fsp, off)));
1295f127cb91Sfrankho bp = bread(fsp->pcfs_xdev, diskblk, readsize);
12967c478bd9Sstevel@tonic-gate if (bp->b_flags & (B_ERROR | B_STALE)) {
12977c478bd9Sstevel@tonic-gate brelse(bp);
12987c478bd9Sstevel@tonic-gate return (EIO);
12997c478bd9Sstevel@tonic-gate }
13007c478bd9Sstevel@tonic-gate bp->b_flags |= B_STALE | B_AGE;
13017c478bd9Sstevel@tonic-gate bcopy(bp->b_un.b_addr, fatp, readsize);
13027c478bd9Sstevel@tonic-gate brelse(bp);
13037c478bd9Sstevel@tonic-gate }
13047c478bd9Sstevel@tonic-gate return (0);
13057c478bd9Sstevel@tonic-gate }
13067c478bd9Sstevel@tonic-gate
13077c478bd9Sstevel@tonic-gate /*
13087c478bd9Sstevel@tonic-gate * We write the FAT out a _lot_, in order to make sure that it
13097c478bd9Sstevel@tonic-gate * is up-to-date. But on a FAT32 system (large drive, small clusters)
13107c478bd9Sstevel@tonic-gate * the FAT might be a couple of megabytes, and writing it all out just
13117c478bd9Sstevel@tonic-gate * because we created or deleted a small file is painful (especially
13127c478bd9Sstevel@tonic-gate * since we do it for each alternate FAT too). So instead, for FAT16 and
13137c478bd9Sstevel@tonic-gate * FAT32 we only write out the bit that has changed. We don't clear
13147c478bd9Sstevel@tonic-gate * the 'updated' fields here because the caller might be writing out
13157c478bd9Sstevel@tonic-gate * several FATs, so the caller must use pc_clear_fatchanges() after
13167c478bd9Sstevel@tonic-gate * all FATs have been updated.
1317f127cb91Sfrankho * This function doesn't take "start" from fsp->pcfs_dosstart because
1318f127cb91Sfrankho * callers can use it to write either the primary or any of the alternate
1319f127cb91Sfrankho * FAT tables.
13207c478bd9Sstevel@tonic-gate */
13217c478bd9Sstevel@tonic-gate static int
pc_writefat(struct pcfs * fsp,daddr_t start)13227c478bd9Sstevel@tonic-gate pc_writefat(struct pcfs *fsp, daddr_t start)
13237c478bd9Sstevel@tonic-gate {
13247c478bd9Sstevel@tonic-gate struct buf *bp;
13257c478bd9Sstevel@tonic-gate size_t off;
13267c478bd9Sstevel@tonic-gate size_t writesize;
13277c478bd9Sstevel@tonic-gate int error;
13287c478bd9Sstevel@tonic-gate uchar_t *fatp = fsp->pcfs_fatp;
1329f127cb91Sfrankho size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
13307c478bd9Sstevel@tonic-gate
13317c478bd9Sstevel@tonic-gate writesize = fsp->pcfs_clsize;
13327c478bd9Sstevel@tonic-gate for (off = 0; off < fatsize; off += writesize, fatp += writesize) {
13337c478bd9Sstevel@tonic-gate if (writesize > (fatsize - off))
13347c478bd9Sstevel@tonic-gate writesize = fatsize - off;
13357c478bd9Sstevel@tonic-gate if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) {
13367c478bd9Sstevel@tonic-gate continue;
13377c478bd9Sstevel@tonic-gate }
13387c478bd9Sstevel@tonic-gate bp = ngeteblk(writesize);
13397c478bd9Sstevel@tonic-gate bp->b_edev = fsp->pcfs_xdev;
13407c478bd9Sstevel@tonic-gate bp->b_dev = cmpdev(bp->b_edev);
13417c478bd9Sstevel@tonic-gate bp->b_blkno = pc_dbdaddr(fsp, start +
13427c478bd9Sstevel@tonic-gate pc_cltodb(fsp, pc_lblkno(fsp, off)));
13437c478bd9Sstevel@tonic-gate bcopy(fatp, bp->b_un.b_addr, writesize);
13447c478bd9Sstevel@tonic-gate bwrite2(bp);
13457c478bd9Sstevel@tonic-gate error = geterror(bp);
13467c478bd9Sstevel@tonic-gate brelse(bp);
13477c478bd9Sstevel@tonic-gate if (error) {
13487c478bd9Sstevel@tonic-gate return (error);
13497c478bd9Sstevel@tonic-gate }
13507c478bd9Sstevel@tonic-gate }
13517c478bd9Sstevel@tonic-gate return (0);
13527c478bd9Sstevel@tonic-gate }
13537c478bd9Sstevel@tonic-gate
13547c478bd9Sstevel@tonic-gate /*
13557c478bd9Sstevel@tonic-gate * Mark the FAT cluster that 'cn' is stored in as modified.
13567c478bd9Sstevel@tonic-gate */
13577c478bd9Sstevel@tonic-gate void
pc_mark_fat_updated(struct pcfs * fsp,pc_cluster32_t cn)13587c478bd9Sstevel@tonic-gate pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn)
13597c478bd9Sstevel@tonic-gate {
13607c478bd9Sstevel@tonic-gate pc_cluster32_t bn;
13617c478bd9Sstevel@tonic-gate size_t size;
13627c478bd9Sstevel@tonic-gate
13637c478bd9Sstevel@tonic-gate /* which fat block is the cluster number stored in? */
13647c478bd9Sstevel@tonic-gate if (IS_FAT32(fsp)) {
13657c478bd9Sstevel@tonic-gate size = sizeof (pc_cluster32_t);
13667c478bd9Sstevel@tonic-gate bn = pc_lblkno(fsp, cn * size);
13677c478bd9Sstevel@tonic-gate fsp->pcfs_fat_changemap[bn] = 1;
13687c478bd9Sstevel@tonic-gate } else if (IS_FAT16(fsp)) {
13697c478bd9Sstevel@tonic-gate size = sizeof (pc_cluster16_t);
13707c478bd9Sstevel@tonic-gate bn = pc_lblkno(fsp, cn * size);
13717c478bd9Sstevel@tonic-gate fsp->pcfs_fat_changemap[bn] = 1;
13727c478bd9Sstevel@tonic-gate } else {
13737c478bd9Sstevel@tonic-gate offset_t off;
13747c478bd9Sstevel@tonic-gate pc_cluster32_t nbn;
13757c478bd9Sstevel@tonic-gate
13767c478bd9Sstevel@tonic-gate ASSERT(IS_FAT12(fsp));
13777c478bd9Sstevel@tonic-gate off = cn + (cn >> 1);
13787c478bd9Sstevel@tonic-gate bn = pc_lblkno(fsp, off);
13797c478bd9Sstevel@tonic-gate fsp->pcfs_fat_changemap[bn] = 1;
13807c478bd9Sstevel@tonic-gate /* does this field wrap into the next fat cluster? */
13817c478bd9Sstevel@tonic-gate nbn = pc_lblkno(fsp, off + 1);
13827c478bd9Sstevel@tonic-gate if (nbn != bn) {
13837c478bd9Sstevel@tonic-gate fsp->pcfs_fat_changemap[nbn] = 1;
13847c478bd9Sstevel@tonic-gate }
13857c478bd9Sstevel@tonic-gate }
13867c478bd9Sstevel@tonic-gate }
13877c478bd9Sstevel@tonic-gate
13887c478bd9Sstevel@tonic-gate /*
13897c478bd9Sstevel@tonic-gate * return whether the FAT cluster 'bn' is updated and needs to
13907c478bd9Sstevel@tonic-gate * be written out.
13917c478bd9Sstevel@tonic-gate */
13927c478bd9Sstevel@tonic-gate int
pc_fat_is_changed(struct pcfs * fsp,pc_cluster32_t bn)13937c478bd9Sstevel@tonic-gate pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn)
13947c478bd9Sstevel@tonic-gate {
13957c478bd9Sstevel@tonic-gate return (fsp->pcfs_fat_changemap[bn] == 1);
13967c478bd9Sstevel@tonic-gate }
1397264a6e74Sfrankho
1398264a6e74Sfrankho /*
1399264a6e74Sfrankho * Implementation of VFS_FREEVFS() to support forced umounts.
1400264a6e74Sfrankho * This is called by the vfs framework after umount, to trigger
1401264a6e74Sfrankho * the release of any resources still associated with the given
1402264a6e74Sfrankho * vfs_t once the need to keep them has gone away.
1403264a6e74Sfrankho */
1404264a6e74Sfrankho void
pcfs_freevfs(vfs_t * vfsp)1405264a6e74Sfrankho pcfs_freevfs(vfs_t *vfsp)
1406264a6e74Sfrankho {
1407264a6e74Sfrankho struct pcfs *fsp = VFSTOPCFS(vfsp);
1408264a6e74Sfrankho
1409264a6e74Sfrankho mutex_enter(&pcfslock);
1410f127cb91Sfrankho /*
1411f127cb91Sfrankho * Purging the FAT closes the device - can't do any more
1412f127cb91Sfrankho * I/O after this.
1413f127cb91Sfrankho */
1414264a6e74Sfrankho if (fsp->pcfs_fatp != (uchar_t *)0)
1415264a6e74Sfrankho pc_invalfat(fsp);
1416264a6e74Sfrankho mutex_exit(&pcfslock);
1417264a6e74Sfrankho
1418264a6e74Sfrankho VN_RELE(fsp->pcfs_devvp);
1419264a6e74Sfrankho mutex_destroy(&fsp->pcfs_lock);
1420f127cb91Sfrankho kmem_free(fsp, sizeof (*fsp));
1421264a6e74Sfrankho
1422264a6e74Sfrankho /*
1423264a6e74Sfrankho * Allow _fini() to succeed now, if so desired.
1424264a6e74Sfrankho */
1425264a6e74Sfrankho atomic_dec_32(&pcfs_mountcount);
1426264a6e74Sfrankho }
1427f127cb91Sfrankho
1428f127cb91Sfrankho
1429f127cb91Sfrankho /*
1430f127cb91Sfrankho * PC-style partition parsing and FAT BPB identification/validation code.
1431f127cb91Sfrankho * The partition parsers here assume:
1432f127cb91Sfrankho * - a FAT filesystem will be in a partition that has one of a set of
1433f127cb91Sfrankho * recognized partition IDs
1434f127cb91Sfrankho * - the user wants the 'numbering' (C:, D:, ...) that one would get
1435f127cb91Sfrankho * on MSDOS 6.x.
1436f127cb91Sfrankho * That means any non-FAT partition type (NTFS, HPFS, or any Linux fs)
1437f127cb91Sfrankho * will not factor in the enumeration.
1438f127cb91Sfrankho * These days, such assumptions should be revisited. FAT is no longer the
1439f127cb91Sfrankho * only game in 'PC town'.
1440f127cb91Sfrankho */
1441f127cb91Sfrankho /*
1442f127cb91Sfrankho * isDosDrive()
1443f127cb91Sfrankho * Boolean function. Give it the systid field for an fdisk partition
1444f127cb91Sfrankho * and it decides if that's a systid that describes a DOS drive. We
1445f127cb91Sfrankho * use systid values defined in sys/dktp/fdisk.h.
1446f127cb91Sfrankho */
1447f127cb91Sfrankho static int
isDosDrive(uchar_t checkMe)1448f127cb91Sfrankho isDosDrive(uchar_t checkMe)
1449f127cb91Sfrankho {
1450f127cb91Sfrankho return ((checkMe == DOSOS12) || (checkMe == DOSOS16) ||
1451f127cb91Sfrankho (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) ||
1452f127cb91Sfrankho (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) ||
1453f127cb91Sfrankho (checkMe == DIAGPART));
1454f127cb91Sfrankho }
1455f127cb91Sfrankho
1456f127cb91Sfrankho
1457f127cb91Sfrankho /*
1458f127cb91Sfrankho * isDosExtended()
1459f127cb91Sfrankho * Boolean function. Give it the systid field for an fdisk partition
1460f127cb91Sfrankho * and it decides if that's a systid that describes an extended DOS
1461f127cb91Sfrankho * partition.
1462f127cb91Sfrankho */
1463f127cb91Sfrankho static int
isDosExtended(uchar_t checkMe)1464f127cb91Sfrankho isDosExtended(uchar_t checkMe)
1465f127cb91Sfrankho {
1466f127cb91Sfrankho return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA));
1467f127cb91Sfrankho }
1468f127cb91Sfrankho
1469f127cb91Sfrankho
1470f127cb91Sfrankho /*
1471f127cb91Sfrankho * isBootPart()
1472f127cb91Sfrankho * Boolean function. Give it the systid field for an fdisk partition
1473f127cb91Sfrankho * and it decides if that's a systid that describes a Solaris boot
1474f127cb91Sfrankho * partition.
1475f127cb91Sfrankho */
1476f127cb91Sfrankho static int
isBootPart(uchar_t checkMe)1477f127cb91Sfrankho isBootPart(uchar_t checkMe)
1478f127cb91Sfrankho {
1479f127cb91Sfrankho return (checkMe == X86BOOT);
1480f127cb91Sfrankho }
1481f127cb91Sfrankho
1482f127cb91Sfrankho
1483f127cb91Sfrankho /*
1484f127cb91Sfrankho * noLogicalDrive()
1485f127cb91Sfrankho * Display error message about not being able to find a logical
1486f127cb91Sfrankho * drive.
1487f127cb91Sfrankho */
1488f127cb91Sfrankho static void
noLogicalDrive(int ldrive)1489f127cb91Sfrankho noLogicalDrive(int ldrive)
1490f127cb91Sfrankho {
1491f127cb91Sfrankho if (ldrive == BOOT_PARTITION_DRIVE) {
1492f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: no boot partition");
1493f127cb91Sfrankho } else {
1494f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: %d: no such logical drive", ldrive);
1495f127cb91Sfrankho }
1496f127cb91Sfrankho }
1497f127cb91Sfrankho
1498f127cb91Sfrankho
1499f127cb91Sfrankho /*
1500f127cb91Sfrankho * findTheDrive()
1501f127cb91Sfrankho * Discover offset of the requested logical drive, and return
1502f127cb91Sfrankho * that offset (startSector), the systid of that drive (sysid),
1503f127cb91Sfrankho * and a buffer pointer (bp), with the buffer contents being
1504f127cb91Sfrankho * the first sector of the logical drive (i.e., the sector that
1505f127cb91Sfrankho * contains the BPB for that drive).
1506f127cb91Sfrankho *
1507f127cb91Sfrankho * Note: this code is not capable of addressing >2TB disks, as it uses
1508f127cb91Sfrankho * daddr_t not diskaddr_t, some of the calculations would overflow
1509f127cb91Sfrankho */
1510f127cb91Sfrankho #define COPY_PTBL(mbr, ptblp) \
1511f127cb91Sfrankho bcopy(&(((struct mboot *)(mbr))->parts), (ptblp), \
1512f127cb91Sfrankho FD_NUMPART * sizeof (struct ipart))
1513f127cb91Sfrankho
1514f127cb91Sfrankho static int
findTheDrive(struct pcfs * fsp,buf_t ** bp)1515f127cb91Sfrankho findTheDrive(struct pcfs *fsp, buf_t **bp)
1516f127cb91Sfrankho {
1517f127cb91Sfrankho int ldrive = fsp->pcfs_ldrive;
1518f127cb91Sfrankho dev_t dev = fsp->pcfs_devvp->v_rdev;
1519f127cb91Sfrankho
1520f127cb91Sfrankho struct ipart dosp[FD_NUMPART]; /* incore fdisk partition structure */
1521f127cb91Sfrankho daddr_t lastseek = 0; /* Disk block we sought previously */
1522f127cb91Sfrankho daddr_t diskblk = 0; /* Disk block to get */
1523f127cb91Sfrankho daddr_t xstartsect; /* base of Extended DOS partition */
1524f127cb91Sfrankho int logicalDriveCount = 0; /* Count of logical drives seen */
1525f127cb91Sfrankho int extendedPart = -1; /* index of extended dos partition */
1526f127cb91Sfrankho int primaryPart = -1; /* index of primary dos partition */
1527f127cb91Sfrankho int bootPart = -1; /* index of a Solaris boot partition */
1528342440ecSPrasad Singamsetty uint32_t xnumsect = 0; /* length of extended DOS partition */
1529f127cb91Sfrankho int driveIndex; /* computed FDISK table index */
1530f127cb91Sfrankho daddr_t startsec;
1531f127cb91Sfrankho len_t mediasize;
1532f127cb91Sfrankho int i;
1533f127cb91Sfrankho /*
1534f127cb91Sfrankho * Count of drives in the current extended partition's
1535f127cb91Sfrankho * FDISK table, and indexes of the drives themselves.
1536f127cb91Sfrankho */
1537f127cb91Sfrankho int extndDrives[FD_NUMPART];
1538f127cb91Sfrankho int numDrives = 0;
1539f127cb91Sfrankho
1540f127cb91Sfrankho /*
1541f127cb91Sfrankho * Count of drives (beyond primary) in master boot record's
1542f127cb91Sfrankho * FDISK table, and indexes of the drives themselves.
1543f127cb91Sfrankho */
1544f127cb91Sfrankho int extraDrives[FD_NUMPART];
1545f127cb91Sfrankho int numExtraDrives = 0;
1546f127cb91Sfrankho
1547f127cb91Sfrankho /*
1548f127cb91Sfrankho * "ldrive == 0" should never happen, as this is a request to
1549f127cb91Sfrankho * mount the physical device (and ignore partitioning). The code
1550f127cb91Sfrankho * in pcfs_mount() should have made sure that a logical drive number
1551f127cb91Sfrankho * is at least 1, meaning we're looking for drive "C:". It is not
1552f127cb91Sfrankho * safe (and a bug in the callers of this function) to request logical
1553f127cb91Sfrankho * drive number 0; we could ASSERT() but a graceful EIO is a more
1554f127cb91Sfrankho * polite way.
1555f127cb91Sfrankho */
1556f127cb91Sfrankho if (ldrive == 0) {
1557f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: request for logical partition zero");
1558f127cb91Sfrankho noLogicalDrive(ldrive);
1559f127cb91Sfrankho return (EIO);
1560f127cb91Sfrankho }
1561f127cb91Sfrankho
1562f127cb91Sfrankho /*
1563f127cb91Sfrankho * Copy from disk block into memory aligned structure for fdisk usage.
1564f127cb91Sfrankho */
1565f127cb91Sfrankho COPY_PTBL((*bp)->b_un.b_addr, dosp);
1566f127cb91Sfrankho
1567f127cb91Sfrankho /*
1568f127cb91Sfrankho * This check is ok because a FAT BPB and a master boot record (MBB)
1569f127cb91Sfrankho * have the same signature, in the same position within the block.
1570f127cb91Sfrankho */
1571f127cb91Sfrankho if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
1572f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: MBR partition table signature err, "
1573f127cb91Sfrankho "device (%x.%x):%d\n",
1574f127cb91Sfrankho getmajor(dev), getminor(dev), ldrive);
1575f127cb91Sfrankho return (EINVAL);
1576f127cb91Sfrankho }
1577f127cb91Sfrankho
1578f127cb91Sfrankho /*
1579f127cb91Sfrankho * Get a summary of what is in the Master FDISK table.
1580f127cb91Sfrankho * Normally we expect to find one partition marked as a DOS drive.
1581f127cb91Sfrankho * This partition is the one Windows calls the primary dos partition.
1582f127cb91Sfrankho * If the machine has any logical drives then we also expect
1583f127cb91Sfrankho * to find a partition marked as an extended DOS partition.
1584f127cb91Sfrankho *
1585f127cb91Sfrankho * Sometimes we'll find multiple partitions marked as DOS drives.
1586f127cb91Sfrankho * The Solaris fdisk program allows these partitions
1587f127cb91Sfrankho * to be created, but Windows fdisk no longer does. We still need
1588f127cb91Sfrankho * to support these, though, since Windows does. We also need to fix
1589f127cb91Sfrankho * our fdisk to behave like the Windows version.
1590f127cb91Sfrankho *
1591f127cb91Sfrankho * It turns out that some off-the-shelf media have *only* an
1592f127cb91Sfrankho * Extended partition, so we need to deal with that case as well.
1593f127cb91Sfrankho *
1594f127cb91Sfrankho * Only a single (the first) Extended or Boot Partition will
1595f127cb91Sfrankho * be recognized. Any others will be ignored.
1596f127cb91Sfrankho */
1597f127cb91Sfrankho for (i = 0; i < FD_NUMPART; i++) {
1598f127cb91Sfrankho DTRACE_PROBE4(primarypart, struct pcfs *, fsp,
1599f127cb91Sfrankho uint_t, (uint_t)dosp[i].systid,
1600f127cb91Sfrankho uint_t, LE_32(dosp[i].relsect),
1601f127cb91Sfrankho uint_t, LE_32(dosp[i].numsect));
1602f127cb91Sfrankho
1603f127cb91Sfrankho if (isDosDrive(dosp[i].systid)) {
1604f127cb91Sfrankho if (primaryPart < 0) {
1605f127cb91Sfrankho logicalDriveCount++;
1606f127cb91Sfrankho primaryPart = i;
1607f127cb91Sfrankho } else {
1608f127cb91Sfrankho extraDrives[numExtraDrives++] = i;
1609f127cb91Sfrankho }
1610f127cb91Sfrankho continue;
1611f127cb91Sfrankho }
1612f127cb91Sfrankho if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) {
1613f127cb91Sfrankho extendedPart = i;
1614f127cb91Sfrankho continue;
1615f127cb91Sfrankho }
1616f127cb91Sfrankho if ((bootPart < 0) && isBootPart(dosp[i].systid)) {
1617f127cb91Sfrankho bootPart = i;
1618f127cb91Sfrankho continue;
1619f127cb91Sfrankho }
1620f127cb91Sfrankho }
1621f127cb91Sfrankho
1622f127cb91Sfrankho if (ldrive == BOOT_PARTITION_DRIVE) {
1623f127cb91Sfrankho if (bootPart < 0) {
1624f127cb91Sfrankho noLogicalDrive(ldrive);
1625f127cb91Sfrankho return (EINVAL);
1626f127cb91Sfrankho }
1627f127cb91Sfrankho startsec = LE_32(dosp[bootPart].relsect);
1628f127cb91Sfrankho mediasize = LE_32(dosp[bootPart].numsect);
1629f127cb91Sfrankho goto found;
1630f127cb91Sfrankho }
1631f127cb91Sfrankho
1632f127cb91Sfrankho if (ldrive == PRIMARY_DOS_DRIVE && primaryPart >= 0) {
1633f127cb91Sfrankho startsec = LE_32(dosp[primaryPart].relsect);
1634f127cb91Sfrankho mediasize = LE_32(dosp[primaryPart].numsect);
1635f127cb91Sfrankho goto found;
1636f127cb91Sfrankho }
1637f127cb91Sfrankho
1638f127cb91Sfrankho /*
1639f127cb91Sfrankho * We are not looking for the C: drive (or the primary drive
1640f127cb91Sfrankho * was not found), so we had better have an extended partition
1641f127cb91Sfrankho * or extra drives in the Master FDISK table.
1642f127cb91Sfrankho */
1643f127cb91Sfrankho if ((extendedPart < 0) && (numExtraDrives == 0)) {
1644f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: no extended dos partition");
1645f127cb91Sfrankho noLogicalDrive(ldrive);
1646f127cb91Sfrankho return (EINVAL);
1647f127cb91Sfrankho }
1648f127cb91Sfrankho
1649f127cb91Sfrankho if (extendedPart >= 0) {
1650f127cb91Sfrankho diskblk = xstartsect = LE_32(dosp[extendedPart].relsect);
1651f127cb91Sfrankho xnumsect = LE_32(dosp[extendedPart].numsect);
1652f127cb91Sfrankho do {
1653f127cb91Sfrankho /*
1654f127cb91Sfrankho * If the seek would not cause us to change
1655f127cb91Sfrankho * position on the drive, then we're out of
1656f127cb91Sfrankho * extended partitions to examine.
1657f127cb91Sfrankho */
1658f127cb91Sfrankho if (diskblk == lastseek)
1659f127cb91Sfrankho break;
1660f127cb91Sfrankho logicalDriveCount += numDrives;
1661f127cb91Sfrankho /*
1662f127cb91Sfrankho * Seek the next extended partition, and find
1663f127cb91Sfrankho * logical drives within it.
1664f127cb91Sfrankho */
1665f127cb91Sfrankho brelse(*bp);
1666f127cb91Sfrankho /*
1667f127cb91Sfrankho * bread() block numbers are multiples of DEV_BSIZE
1668f127cb91Sfrankho * but the device sector size (the unit of partitioning)
1669f127cb91Sfrankho * might be larger than that; pcfs_get_device_info()
1670f127cb91Sfrankho * has calculated the multiplicator for us.
1671f127cb91Sfrankho */
1672f127cb91Sfrankho *bp = bread(dev,
1673f127cb91Sfrankho pc_dbdaddr(fsp, diskblk), fsp->pcfs_secsize);
1674f127cb91Sfrankho if ((*bp)->b_flags & B_ERROR) {
1675f127cb91Sfrankho return (EIO);
1676f127cb91Sfrankho }
1677f127cb91Sfrankho
1678f127cb91Sfrankho lastseek = diskblk;
1679f127cb91Sfrankho COPY_PTBL((*bp)->b_un.b_addr, dosp);
1680f127cb91Sfrankho if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
1681f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: "
1682f127cb91Sfrankho "extended partition table signature err, "
1683f127cb91Sfrankho "device (%x.%x):%d, LBA %u",
1684f127cb91Sfrankho getmajor(dev), getminor(dev), ldrive,
1685f127cb91Sfrankho (uint_t)pc_dbdaddr(fsp, diskblk));
1686f127cb91Sfrankho return (EINVAL);
1687f127cb91Sfrankho }
1688f127cb91Sfrankho /*
1689f127cb91Sfrankho * Count up drives, and track where the next
1690f127cb91Sfrankho * extended partition is in case we need it. We
1691f127cb91Sfrankho * are expecting only one extended partition. If
1692f127cb91Sfrankho * there is more than one we'll only go to the
1693f127cb91Sfrankho * first one we see, but warn about ignoring.
1694f127cb91Sfrankho */
1695f127cb91Sfrankho numDrives = 0;
1696f127cb91Sfrankho for (i = 0; i < FD_NUMPART; i++) {
1697f127cb91Sfrankho DTRACE_PROBE4(extendedpart,
1698f127cb91Sfrankho struct pcfs *, fsp,
1699f127cb91Sfrankho uint_t, (uint_t)dosp[i].systid,
1700f127cb91Sfrankho uint_t, LE_32(dosp[i].relsect),
1701f127cb91Sfrankho uint_t, LE_32(dosp[i].numsect));
1702f127cb91Sfrankho if (isDosDrive(dosp[i].systid)) {
1703f127cb91Sfrankho extndDrives[numDrives++] = i;
1704f127cb91Sfrankho } else if (isDosExtended(dosp[i].systid)) {
1705f127cb91Sfrankho if (diskblk != lastseek) {
1706f127cb91Sfrankho /*
1707f127cb91Sfrankho * Already found an extended
1708f127cb91Sfrankho * partition in this table.
1709f127cb91Sfrankho */
1710f127cb91Sfrankho cmn_err(CE_NOTE,
1711f127cb91Sfrankho "!pcfs: ignoring unexpected"
1712f127cb91Sfrankho " additional extended"
1713f127cb91Sfrankho " partition");
1714f127cb91Sfrankho } else {
1715f127cb91Sfrankho diskblk = xstartsect +
1716f127cb91Sfrankho LE_32(dosp[i].relsect);
1717f127cb91Sfrankho }
1718f127cb91Sfrankho }
1719f127cb91Sfrankho }
1720f127cb91Sfrankho } while (ldrive > logicalDriveCount + numDrives);
1721f127cb91Sfrankho
1722f127cb91Sfrankho ASSERT(numDrives <= FD_NUMPART);
1723f127cb91Sfrankho
1724f127cb91Sfrankho if (ldrive <= logicalDriveCount + numDrives) {
1725f127cb91Sfrankho /*
1726f127cb91Sfrankho * The number of logical drives we've found thus
1727f127cb91Sfrankho * far is enough to get us to the one we were
1728f127cb91Sfrankho * searching for.
1729f127cb91Sfrankho */
1730f127cb91Sfrankho driveIndex = logicalDriveCount + numDrives - ldrive;
1731f127cb91Sfrankho mediasize =
1732f127cb91Sfrankho LE_32(dosp[extndDrives[driveIndex]].numsect);
1733f127cb91Sfrankho startsec =
1734f127cb91Sfrankho LE_32(dosp[extndDrives[driveIndex]].relsect) +
1735f127cb91Sfrankho lastseek;
1736f127cb91Sfrankho if (startsec > (xstartsect + xnumsect)) {
1737f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: extended partition "
1738f127cb91Sfrankho "values bad");
1739f127cb91Sfrankho return (EINVAL);
1740f127cb91Sfrankho }
1741f127cb91Sfrankho goto found;
1742f127cb91Sfrankho } else {
1743f127cb91Sfrankho /*
1744f127cb91Sfrankho * We ran out of extended dos partition
1745f127cb91Sfrankho * drives. The only hope now is to go
1746f127cb91Sfrankho * back to extra drives defined in the master
1747f127cb91Sfrankho * fdisk table. But we overwrote that table
1748f127cb91Sfrankho * already, so we must load it in again.
1749f127cb91Sfrankho */
1750f127cb91Sfrankho logicalDriveCount += numDrives;
1751f127cb91Sfrankho brelse(*bp);
1752f127cb91Sfrankho ASSERT(fsp->pcfs_dosstart == 0);
1753f127cb91Sfrankho *bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
1754f127cb91Sfrankho fsp->pcfs_secsize);
1755f127cb91Sfrankho if ((*bp)->b_flags & B_ERROR) {
1756f127cb91Sfrankho return (EIO);
1757f127cb91Sfrankho }
1758f127cb91Sfrankho COPY_PTBL((*bp)->b_un.b_addr, dosp);
1759f127cb91Sfrankho }
1760f127cb91Sfrankho }
1761f127cb91Sfrankho /*
1762f127cb91Sfrankho * Still haven't found the drive, is it an extra
1763f127cb91Sfrankho * drive defined in the main FDISK table?
1764f127cb91Sfrankho */
1765f127cb91Sfrankho if (ldrive <= logicalDriveCount + numExtraDrives) {
1766f127cb91Sfrankho driveIndex = logicalDriveCount + numExtraDrives - ldrive;
1767f127cb91Sfrankho ASSERT(driveIndex < MIN(numExtraDrives, FD_NUMPART));
1768f127cb91Sfrankho mediasize = LE_32(dosp[extraDrives[driveIndex]].numsect);
1769f127cb91Sfrankho startsec = LE_32(dosp[extraDrives[driveIndex]].relsect);
1770f127cb91Sfrankho goto found;
1771f127cb91Sfrankho }
1772f127cb91Sfrankho /*
1773f127cb91Sfrankho * Still haven't found the drive, and there is
1774f127cb91Sfrankho * nowhere else to look.
1775f127cb91Sfrankho */
1776f127cb91Sfrankho noLogicalDrive(ldrive);
1777f127cb91Sfrankho return (EINVAL);
1778f127cb91Sfrankho
1779f127cb91Sfrankho found:
1780f127cb91Sfrankho /*
1781f127cb91Sfrankho * We need this value in units of sectorsize, because PCFS' internal
1782f127cb91Sfrankho * offset calculations go haywire for > 512Byte sectors unless all
1783f127cb91Sfrankho * pcfs_.*start values are in units of sectors.
1784f127cb91Sfrankho * So, assign before the capacity check (that's done in DEV_BSIZE)
1785f127cb91Sfrankho */
1786f127cb91Sfrankho fsp->pcfs_dosstart = startsec;
1787f127cb91Sfrankho
1788f127cb91Sfrankho /*
1789f127cb91Sfrankho * convert from device sectors to proper units:
1790f127cb91Sfrankho * - starting sector: DEV_BSIZE (as argument to bread())
1791f127cb91Sfrankho * - media size: Bytes
1792f127cb91Sfrankho */
1793f127cb91Sfrankho startsec = pc_dbdaddr(fsp, startsec);
1794f127cb91Sfrankho mediasize *= fsp->pcfs_secsize;
1795f127cb91Sfrankho
1796f127cb91Sfrankho /*
1797f127cb91Sfrankho * some additional validation / warnings in case the partition table
1798f127cb91Sfrankho * and the actual media capacity are not in accordance ...
1799f127cb91Sfrankho */
1800f127cb91Sfrankho if (fsp->pcfs_mediasize != 0) {
1801f127cb91Sfrankho diskaddr_t startoff =
1802f127cb91Sfrankho (diskaddr_t)startsec * (diskaddr_t)DEV_BSIZE;
1803f127cb91Sfrankho
1804f127cb91Sfrankho if (startoff >= fsp->pcfs_mediasize ||
1805f127cb91Sfrankho startoff + mediasize > fsp->pcfs_mediasize) {
1806f127cb91Sfrankho cmn_err(CE_WARN,
1807f127cb91Sfrankho "!pcfs: partition size (LBA start %u, %lld bytes, "
1808f127cb91Sfrankho "device (%x.%x):%d) smaller than "
1809f127cb91Sfrankho "mediasize (%lld bytes).\n"
1810f127cb91Sfrankho "filesystem may be truncated, access errors "
1811f127cb91Sfrankho "may result.\n",
1812f127cb91Sfrankho (uint_t)startsec, (long long)mediasize,
1813f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
1814f127cb91Sfrankho fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
1815f127cb91Sfrankho }
1816f127cb91Sfrankho } else {
1817f127cb91Sfrankho fsp->pcfs_mediasize = mediasize;
1818f127cb91Sfrankho }
1819f127cb91Sfrankho
1820f127cb91Sfrankho return (0);
1821f127cb91Sfrankho }
1822f127cb91Sfrankho
1823f127cb91Sfrankho
1824f127cb91Sfrankho static fattype_t
secondaryBPBChecks(struct pcfs * fsp,uchar_t * bpb,size_t secsize)1825f127cb91Sfrankho secondaryBPBChecks(struct pcfs *fsp, uchar_t *bpb, size_t secsize)
1826f127cb91Sfrankho {
1827f127cb91Sfrankho uint32_t ncl = fsp->pcfs_ncluster;
1828f127cb91Sfrankho
1829f127cb91Sfrankho if (ncl <= 4096) {
1830f127cb91Sfrankho if (bpb_get_FatSz16(bpb) == 0)
1831f127cb91Sfrankho return (FAT_UNKNOWN);
1832f127cb91Sfrankho
1833f127cb91Sfrankho if (bpb_get_FatSz16(bpb) * secsize < ncl * 2 &&
1834f127cb91Sfrankho bpb_get_FatSz16(bpb) * secsize >= (3 * ncl / 2))
1835f127cb91Sfrankho return (FAT12);
1836f127cb91Sfrankho if (bcmp(bpb_FilSysType16(bpb), "FAT12", 5) == 0)
1837f127cb91Sfrankho return (FAT12);
1838f127cb91Sfrankho if (bcmp(bpb_FilSysType16(bpb), "FAT16", 5) == 0)
1839f127cb91Sfrankho return (FAT16);
1840f127cb91Sfrankho
1841f127cb91Sfrankho switch (bpb_get_Media(bpb)) {
1842f127cb91Sfrankho case SS8SPT:
1843f127cb91Sfrankho case DS8SPT:
1844f127cb91Sfrankho case SS9SPT:
1845f127cb91Sfrankho case DS9SPT:
1846f127cb91Sfrankho case DS18SPT:
1847f127cb91Sfrankho case DS9_15SPT:
1848f127cb91Sfrankho /*
1849f127cb91Sfrankho * Is this reliable - all floppies are FAT12 ?
1850f127cb91Sfrankho */
1851f127cb91Sfrankho return (FAT12);
1852f127cb91Sfrankho case MD_FIXED:
1853f127cb91Sfrankho /*
1854f127cb91Sfrankho * Is this reliable - disks are always FAT16 ?
1855f127cb91Sfrankho */
1856f127cb91Sfrankho return (FAT16);
1857f127cb91Sfrankho default:
1858f127cb91Sfrankho break;
1859f127cb91Sfrankho }
1860f127cb91Sfrankho } else if (ncl <= 65536) {
1861f127cb91Sfrankho if (bpb_get_FatSz16(bpb) == 0 && bpb_get_FatSz32(bpb) > 0)
1862f127cb91Sfrankho return (FAT32);
1863f127cb91Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
1864f127cb91Sfrankho return (FAT32);
1865f127cb91Sfrankho if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
1866f127cb91Sfrankho return (FAT32);
1867f127cb91Sfrankho
1868f127cb91Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
1869f127cb91Sfrankho return (FAT16);
1870f127cb91Sfrankho if (bpb_get_FatSz16(bpb) * secsize < ncl * 4)
1871f127cb91Sfrankho return (FAT16);
1872f127cb91Sfrankho }
1873f127cb91Sfrankho
1874f127cb91Sfrankho /*
1875f127cb91Sfrankho * We don't know
1876f127cb91Sfrankho */
1877f127cb91Sfrankho return (FAT_UNKNOWN);
1878f127cb91Sfrankho }
1879f127cb91Sfrankho
1880f127cb91Sfrankho /*
1881f127cb91Sfrankho * Check to see if the BPB we found is correct.
1882f127cb91Sfrankho *
1883f127cb91Sfrankho * This looks far more complicated that it needs to be for pure structural
1884f127cb91Sfrankho * validation. The reason for this is that parseBPB() is also used for
1885f127cb91Sfrankho * debugging purposes (mdb dcmd) and we therefore want a bitmap of which
1886c2aaf90fSgd78059 * BPB fields (do not) have 'known good' values, even if we (do not) reject
1887c2aaf90fSgd78059 * the BPB when attempting to mount the filesystem.
1888c2aaf90fSgd78059 *
1889c2aaf90fSgd78059 * Real-world usage of FAT shows there are a lot of corner-case situations
1890c2aaf90fSgd78059 * and, following the specification strictly, invalid filesystems out there.
1891c2aaf90fSgd78059 * Known are situations such as:
1892c2aaf90fSgd78059 * - FAT12/FAT16 filesystems with garbage in either totsec16/32
1893c2aaf90fSgd78059 * instead of the zero in one of the fields mandated by the spec
1894c2aaf90fSgd78059 * - filesystems that claim to be larger than the partition they're in
1895c2aaf90fSgd78059 * - filesystems without valid media descriptor
1896c2aaf90fSgd78059 * - FAT32 filesystems with RootEntCnt != 0
1897c2aaf90fSgd78059 * - FAT32 filesystems with less than 65526 clusters
1898c2aaf90fSgd78059 * - FAT32 filesystems without valid FSI sector
1899c2aaf90fSgd78059 * - FAT32 filesystems with FAT size in fatsec16 instead of fatsec32
1900c2aaf90fSgd78059 *
1901c2aaf90fSgd78059 * Such filesystems are accessible by PCFS - if it'd know to start with that
1902c2aaf90fSgd78059 * the filesystem should be treated as a specific FAT type. Before S10, it
1903c2aaf90fSgd78059 * relied on the PC/fdisk partition type for the purpose and almost completely
1904c2aaf90fSgd78059 * ignored the BPB; now it ignores the partition type for anything else but
1905c2aaf90fSgd78059 * logical drive enumeration, which can result in rejection of (invalid)
1906c2aaf90fSgd78059 * FAT32 - if the partition ID says FAT32, but the filesystem, for example
1907c2aaf90fSgd78059 * has less than 65526 clusters.
1908c2aaf90fSgd78059 *
1909c2aaf90fSgd78059 * Without a "force this fs as FAT{12,16,32}" tunable or mount option, it's
1910c2aaf90fSgd78059 * not possible to allow all such mostly-compliant filesystems in unless one
1911c2aaf90fSgd78059 * accepts false positives (definitely invalid filesystems that cause problems
1912c2aaf90fSgd78059 * later). This at least allows to pinpoint why the mount failed.
1913c2aaf90fSgd78059 *
1914c2aaf90fSgd78059 * Due to the use of FAT on removeable media, all relaxations of the rules
1915c2aaf90fSgd78059 * here need to be carefully evaluated wrt. to potential effects on PCFS
1916c2aaf90fSgd78059 * resilience. A faulty/"mis-crafted" filesystem must not cause a panic, so
1917c2aaf90fSgd78059 * beware.
1918f127cb91Sfrankho */
1919f127cb91Sfrankho static int
parseBPB(struct pcfs * fsp,uchar_t * bpb,int * valid)1920f127cb91Sfrankho parseBPB(struct pcfs *fsp, uchar_t *bpb, int *valid)
1921f127cb91Sfrankho {
1922f127cb91Sfrankho fattype_t type;
1923f127cb91Sfrankho
1924f127cb91Sfrankho uint32_t ncl; /* number of clusters in file area */
1925f127cb91Sfrankho uint32_t rec;
1926f127cb91Sfrankho uint32_t reserved;
1927f127cb91Sfrankho uint32_t fsisec, bkbootsec;
1928f127cb91Sfrankho blkcnt_t totsec, totsec16, totsec32, datasec;
1929f127cb91Sfrankho size_t fatsec, fatsec16, fatsec32, rdirsec;
1930f127cb91Sfrankho size_t secsize;
1931f127cb91Sfrankho len_t mediasize;
1932f127cb91Sfrankho uint64_t validflags = 0;
1933f127cb91Sfrankho
1934f127cb91Sfrankho if (VALID_BPBSIG(bpb_get_BPBSig(bpb)))
1935f127cb91Sfrankho validflags |= BPB_BPBSIG_OK;
1936f127cb91Sfrankho
1937f127cb91Sfrankho rec = bpb_get_RootEntCnt(bpb);
1938f127cb91Sfrankho reserved = bpb_get_RsvdSecCnt(bpb);
1939f127cb91Sfrankho fsisec = bpb_get_FSInfo32(bpb);
1940f127cb91Sfrankho bkbootsec = bpb_get_BkBootSec32(bpb);
1941f127cb91Sfrankho totsec16 = (blkcnt_t)bpb_get_TotSec16(bpb);
1942f127cb91Sfrankho totsec32 = (blkcnt_t)bpb_get_TotSec32(bpb);
1943f127cb91Sfrankho fatsec16 = bpb_get_FatSz16(bpb);
1944f127cb91Sfrankho fatsec32 = bpb_get_FatSz32(bpb);
1945f127cb91Sfrankho
1946f127cb91Sfrankho totsec = totsec16 ? totsec16 : totsec32;
1947f127cb91Sfrankho fatsec = fatsec16 ? fatsec16 : fatsec32;
1948f127cb91Sfrankho
1949f127cb91Sfrankho secsize = bpb_get_BytesPerSec(bpb);
1950f127cb91Sfrankho if (!VALID_SECSIZE(secsize))
1951f127cb91Sfrankho secsize = fsp->pcfs_secsize;
1952f127cb91Sfrankho if (secsize != fsp->pcfs_secsize) {
1953f127cb91Sfrankho PC_DPRINTF3(3, "!pcfs: parseBPB, device (%x.%x):%d:\n",
1954f127cb91Sfrankho getmajor(fsp->pcfs_xdev),
1955f127cb91Sfrankho getminor(fsp->pcfs_xdev), fsp->pcfs_ldrive);
1956f127cb91Sfrankho PC_DPRINTF2(3, "!BPB secsize %d != "
1957f127cb91Sfrankho "autodetected media block size %d\n",
1958f127cb91Sfrankho (int)secsize, (int)fsp->pcfs_secsize);
1959f127cb91Sfrankho if (fsp->pcfs_ldrive) {
1960f127cb91Sfrankho /*
1961f127cb91Sfrankho * We've already attempted to parse the partition
1962f127cb91Sfrankho * table. If the block size used for that don't match
1963f127cb91Sfrankho * the PCFS sector size, we're hosed one way or the
1964f127cb91Sfrankho * other. Just try what happens.
1965f127cb91Sfrankho */
1966f127cb91Sfrankho secsize = fsp->pcfs_secsize;
1967f127cb91Sfrankho PC_DPRINTF1(3,
1968f127cb91Sfrankho "!pcfs: Using autodetected secsize %d\n",
1969f127cb91Sfrankho (int)secsize);
1970f127cb91Sfrankho } else {
1971f127cb91Sfrankho /*
1972f127cb91Sfrankho * This allows mounting lofi images of PCFS partitions
1973f127cb91Sfrankho * with sectorsize != DEV_BSIZE. We can't parse the
1974f127cb91Sfrankho * partition table on whole-disk images unless the
1975f127cb91Sfrankho * (undocumented) "secsize=..." mount option is used,
1976f127cb91Sfrankho * but at least this allows us to mount if we have
1977f127cb91Sfrankho * an image of a partition.
1978f127cb91Sfrankho */
1979f127cb91Sfrankho PC_DPRINTF1(3,
1980f127cb91Sfrankho "!pcfs: Using BPB secsize %d\n", (int)secsize);
1981f127cb91Sfrankho }
1982f127cb91Sfrankho }
1983f127cb91Sfrankho
1984f127cb91Sfrankho if (fsp->pcfs_mediasize == 0) {
1985f127cb91Sfrankho mediasize = (len_t)totsec * (len_t)secsize;
1986c2aaf90fSgd78059 /*
1987c2aaf90fSgd78059 * This is not an error because not all devices support the
1988c2aaf90fSgd78059 * dkio(7i) mediasize queries, and/or not all devices are
1989c2aaf90fSgd78059 * partitioned. If we have not been able to figure out the
1990c2aaf90fSgd78059 * size of the underlaying medium, we have to trust the BPB.
1991c2aaf90fSgd78059 */
1992f127cb91Sfrankho PC_DPRINTF4(3, "!pcfs: parseBPB: mediasize autodetect failed "
1993f127cb91Sfrankho "on device (%x.%x):%d, trusting BPB totsec (%lld Bytes)\n",
1994f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
1995f127cb91Sfrankho fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
1996f127cb91Sfrankho } else if ((len_t)totsec * (len_t)secsize > fsp->pcfs_mediasize) {
1997f127cb91Sfrankho cmn_err(CE_WARN,
1998f127cb91Sfrankho "!pcfs: autodetected mediasize (%lld Bytes) smaller than "
1999f127cb91Sfrankho "FAT BPB mediasize (%lld Bytes).\n"
2000f127cb91Sfrankho "truncated filesystem on device (%x.%x):%d, access errors "
2001f127cb91Sfrankho "possible.\n",
2002f127cb91Sfrankho (long long)fsp->pcfs_mediasize,
2003f127cb91Sfrankho (long long)(totsec * (blkcnt_t)secsize),
2004f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
2005f127cb91Sfrankho fsp->pcfs_ldrive);
2006f127cb91Sfrankho mediasize = fsp->pcfs_mediasize;
2007f127cb91Sfrankho } else {
2008f127cb91Sfrankho /*
2009f127cb91Sfrankho * This is actually ok. A FAT needs not occupy the maximum
2010f127cb91Sfrankho * space available in its partition, it can be shorter.
2011f127cb91Sfrankho */
2012f127cb91Sfrankho mediasize = (len_t)totsec * (len_t)secsize;
2013f127cb91Sfrankho }
2014f127cb91Sfrankho
2015f127cb91Sfrankho /*
2016f127cb91Sfrankho * Since we let just about anything pass through this function,
2017f127cb91Sfrankho * fence against divide-by-zero here.
2018f127cb91Sfrankho */
2019f127cb91Sfrankho if (secsize)
2020f127cb91Sfrankho rdirsec = roundup(rec * 32, secsize) / secsize;
2021f127cb91Sfrankho else
2022f127cb91Sfrankho rdirsec = 0;
2023f127cb91Sfrankho
2024f127cb91Sfrankho /*
2025f127cb91Sfrankho * This assignment is necessary before pc_dbdaddr() can first be
2026f127cb91Sfrankho * used. Must initialize the value here.
2027f127cb91Sfrankho */
2028f127cb91Sfrankho fsp->pcfs_secsize = secsize;
2029f127cb91Sfrankho fsp->pcfs_sdshift = ddi_ffs(secsize / DEV_BSIZE) - 1;
2030f127cb91Sfrankho
2031f127cb91Sfrankho fsp->pcfs_mediasize = mediasize;
2032f127cb91Sfrankho
2033f127cb91Sfrankho fsp->pcfs_spcl = bpb_get_SecPerClus(bpb);
2034f127cb91Sfrankho fsp->pcfs_numfat = bpb_get_NumFATs(bpb);
2035f127cb91Sfrankho fsp->pcfs_mediadesc = bpb_get_Media(bpb);
2036f127cb91Sfrankho fsp->pcfs_clsize = secsize * fsp->pcfs_spcl;
2037f127cb91Sfrankho fsp->pcfs_rdirsec = rdirsec;
2038f127cb91Sfrankho
2039f127cb91Sfrankho /*
2040f127cb91Sfrankho * Remember: All PCFS offset calculations in sectors. Before I/O
2041f127cb91Sfrankho * is done, convert to DEV_BSIZE units via pc_dbdaddr(). This is
2042f127cb91Sfrankho * necessary so that media with > 512Byte sector sizes work correctly.
2043f127cb91Sfrankho */
2044f127cb91Sfrankho fsp->pcfs_fatstart = fsp->pcfs_dosstart + reserved;
2045f127cb91Sfrankho fsp->pcfs_rdirstart = fsp->pcfs_fatstart + fsp->pcfs_numfat * fatsec;
2046f127cb91Sfrankho fsp->pcfs_datastart = fsp->pcfs_rdirstart + rdirsec;
2047f127cb91Sfrankho datasec = totsec -
2048f127cb91Sfrankho (blkcnt_t)fatsec * fsp->pcfs_numfat -
2049f127cb91Sfrankho (blkcnt_t)rdirsec -
2050f127cb91Sfrankho (blkcnt_t)reserved;
2051f127cb91Sfrankho
2052f127cb91Sfrankho DTRACE_PROBE4(fatgeometry,
2053f127cb91Sfrankho blkcnt_t, totsec, size_t, fatsec,
2054f127cb91Sfrankho size_t, rdirsec, blkcnt_t, datasec);
2055f127cb91Sfrankho
2056f127cb91Sfrankho /*
2057c2aaf90fSgd78059 * 'totsec' is taken directly from the BPB and guaranteed to fit
2058c2aaf90fSgd78059 * into a 32bit unsigned integer. The calculation of 'datasec',
2059c2aaf90fSgd78059 * on the other hand, could underflow for incorrect values in
2060c2aaf90fSgd78059 * rdirsec/reserved/fatsec. Check for that.
2061c2aaf90fSgd78059 * We also check that the BPB conforms to the FAT specification's
2062c2aaf90fSgd78059 * requirement that either of the 16/32bit total sector counts
2063c2aaf90fSgd78059 * must be zero.
2064f127cb91Sfrankho */
2065f127cb91Sfrankho if (totsec != 0 &&
2066f127cb91Sfrankho (totsec16 == totsec32 || totsec16 == 0 || totsec32 == 0) &&
2067f127cb91Sfrankho datasec < totsec && datasec <= UINT32_MAX)
2068f127cb91Sfrankho validflags |= BPB_TOTSEC_OK;
2069f127cb91Sfrankho
2070c2aaf90fSgd78059 if ((len_t)totsec * (len_t)secsize <= mediasize)
2071f127cb91Sfrankho validflags |= BPB_MEDIASZ_OK;
2072f127cb91Sfrankho
2073f127cb91Sfrankho if (VALID_SECSIZE(secsize))
2074f127cb91Sfrankho validflags |= BPB_SECSIZE_OK;
2075f127cb91Sfrankho if (VALID_SPCL(fsp->pcfs_spcl))
2076f127cb91Sfrankho validflags |= BPB_SECPERCLUS_OK;
2077f127cb91Sfrankho if (VALID_CLSIZE(fsp->pcfs_clsize))
2078f127cb91Sfrankho validflags |= BPB_CLSIZE_OK;
2079f127cb91Sfrankho if (VALID_NUMFATS(fsp->pcfs_numfat))
2080f127cb91Sfrankho validflags |= BPB_NUMFAT_OK;
2081f127cb91Sfrankho if (VALID_RSVDSEC(reserved) && reserved < totsec)
2082f127cb91Sfrankho validflags |= BPB_RSVDSECCNT_OK;
2083f127cb91Sfrankho if (VALID_MEDIA(fsp->pcfs_mediadesc))
2084f127cb91Sfrankho validflags |= BPB_MEDIADESC_OK;
2085f127cb91Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
2086f127cb91Sfrankho validflags |= BPB_BOOTSIG16_OK;
2087f127cb91Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
2088f127cb91Sfrankho validflags |= BPB_BOOTSIG32_OK;
2089f127cb91Sfrankho if (VALID_FSTYPSTR16(bpb_FilSysType16(bpb)))
2090f127cb91Sfrankho validflags |= BPB_FSTYPSTR16_OK;
2091f127cb91Sfrankho if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
2092f127cb91Sfrankho validflags |= BPB_FSTYPSTR32_OK;
2093f127cb91Sfrankho if (VALID_OEMNAME(bpb_OEMName(bpb)))
2094f127cb91Sfrankho validflags |= BPB_OEMNAME_OK;
2095f127cb91Sfrankho if (bkbootsec > 0 && bkbootsec <= reserved && fsisec != bkbootsec)
2096f127cb91Sfrankho validflags |= BPB_BKBOOTSEC_OK;
2097f127cb91Sfrankho if (fsisec > 0 && fsisec <= reserved)
2098f127cb91Sfrankho validflags |= BPB_FSISEC_OK;
2099f127cb91Sfrankho if (VALID_JMPBOOT(bpb_jmpBoot(bpb)))
2100f127cb91Sfrankho validflags |= BPB_JMPBOOT_OK;
2101f127cb91Sfrankho if (VALID_FSVER32(bpb_get_FSVer32(bpb)))
2102f127cb91Sfrankho validflags |= BPB_FSVER_OK;
2103f127cb91Sfrankho if (VALID_VOLLAB(bpb_VolLab16(bpb)))
2104f127cb91Sfrankho validflags |= BPB_VOLLAB16_OK;
2105f127cb91Sfrankho if (VALID_VOLLAB(bpb_VolLab32(bpb)))
2106f127cb91Sfrankho validflags |= BPB_VOLLAB32_OK;
2107f127cb91Sfrankho if (VALID_EXTFLAGS(bpb_get_ExtFlags32(bpb)))
2108f127cb91Sfrankho validflags |= BPB_EXTFLAGS_OK;
2109f127cb91Sfrankho
2110f127cb91Sfrankho /*
2111f127cb91Sfrankho * Try to determine which FAT format to use.
2112f127cb91Sfrankho *
2113f127cb91Sfrankho * Calculate the number of clusters in order to determine
2114f127cb91Sfrankho * the type of FAT we are looking at. This is the only
2115f127cb91Sfrankho * recommended way of determining FAT type, though there
2116f127cb91Sfrankho * are other hints in the data, this is the best way.
2117f127cb91Sfrankho *
2118f127cb91Sfrankho * Since we let just about "anything" pass through this function
2119f127cb91Sfrankho * without early exits, fence against divide-by-zero here.
2120f127cb91Sfrankho *
2121f127cb91Sfrankho * datasec was already validated against UINT32_MAX so we know
2122f127cb91Sfrankho * the result will not overflow the 32bit calculation.
2123f127cb91Sfrankho */
2124f127cb91Sfrankho if (fsp->pcfs_spcl)
2125f127cb91Sfrankho ncl = (uint32_t)datasec / fsp->pcfs_spcl;
2126f127cb91Sfrankho else
2127f127cb91Sfrankho ncl = 0;
2128f127cb91Sfrankho
2129f127cb91Sfrankho fsp->pcfs_ncluster = ncl;
2130f127cb91Sfrankho
2131f127cb91Sfrankho /*
2132f127cb91Sfrankho * From the Microsoft FAT specification:
2133f127cb91Sfrankho * In the following example, when it says <, it does not mean <=.
2134f127cb91Sfrankho * Note also that the numbers are correct. The first number for
2135f127cb91Sfrankho * FAT12 is 4085; the second number for FAT16 is 65525. These numbers
2136f127cb91Sfrankho * and the '<' signs are not wrong.
2137f127cb91Sfrankho *
2138f127cb91Sfrankho * We "specialdetect" the corner cases, and use at least one "extra"
2139f127cb91Sfrankho * criterion to decide whether it's FAT16 or FAT32 if the cluster
2140f127cb91Sfrankho * count is dangerously close to the boundaries.
2141f127cb91Sfrankho */
2142f127cb91Sfrankho
2143f127cb91Sfrankho if (ncl <= PCF_FIRSTCLUSTER) {
2144f127cb91Sfrankho type = FAT_UNKNOWN;
2145f127cb91Sfrankho } else if (ncl < 4085) {
2146f127cb91Sfrankho type = FAT12;
2147f127cb91Sfrankho } else if (ncl <= 4096) {
2148f127cb91Sfrankho type = FAT_QUESTIONABLE;
2149f127cb91Sfrankho } else if (ncl < 65525) {
2150f127cb91Sfrankho type = FAT16;
2151f127cb91Sfrankho } else if (ncl <= 65536) {
2152f127cb91Sfrankho type = FAT_QUESTIONABLE;
2153f127cb91Sfrankho } else if (ncl < PCF_LASTCLUSTER32) {
2154f127cb91Sfrankho type = FAT32;
2155f127cb91Sfrankho } else {
2156f127cb91Sfrankho type = FAT_UNKNOWN;
2157f127cb91Sfrankho }
2158f127cb91Sfrankho
2159f127cb91Sfrankho DTRACE_PROBE4(parseBPB__initial,
2160f127cb91Sfrankho struct pcfs *, fsp, unsigned char *, bpb,
2161f127cb91Sfrankho int, validflags, fattype_t, type);
2162f127cb91Sfrankho
2163f127cb91Sfrankho recheck:
2164f127cb91Sfrankho fsp->pcfs_fatsec = fatsec;
2165f127cb91Sfrankho
2166f127cb91Sfrankho /* Do some final sanity checks for each specific type of FAT */
2167f127cb91Sfrankho switch (type) {
2168f127cb91Sfrankho case FAT12:
2169f127cb91Sfrankho if (rec != 0)
2170f127cb91Sfrankho validflags |= BPB_ROOTENTCNT_OK;
2171f127cb91Sfrankho if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
2172f127cb91Sfrankho bpb_get_TotSec16(bpb) == 0)
2173f127cb91Sfrankho validflags |= BPB_TOTSEC16_OK;
2174f127cb91Sfrankho if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
2175f127cb91Sfrankho bpb_get_TotSec32(bpb) == 0)
2176f127cb91Sfrankho validflags |= BPB_TOTSEC32_OK;
2177f127cb91Sfrankho if (bpb_get_FatSz16(bpb) == fatsec)
2178f127cb91Sfrankho validflags |= BPB_FATSZ16_OK;
2179*985bfda7SMichael Bergknoff if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER)
2180*985bfda7SMichael Bergknoff * 3 / 2)
2181f127cb91Sfrankho validflags |= BPB_FATSZ_OK;
2182f127cb91Sfrankho if (ncl < 4085)
2183f127cb91Sfrankho validflags |= BPB_NCLUSTERS_OK;
2184f127cb91Sfrankho
2185f127cb91Sfrankho fsp->pcfs_lastclmark = (PCF_LASTCLUSTER & 0xfff);
2186f127cb91Sfrankho fsp->pcfs_rootblksize =
2187f127cb91Sfrankho fsp->pcfs_rdirsec * secsize;
2188f127cb91Sfrankho fsp->pcfs_fsistart = 0;
2189f127cb91Sfrankho
2190f127cb91Sfrankho if ((validflags & FAT12_VALIDMSK) != FAT12_VALIDMSK)
2191f127cb91Sfrankho type = FAT_UNKNOWN;
2192f127cb91Sfrankho break;
2193f127cb91Sfrankho case FAT16:
2194f127cb91Sfrankho if (rec != 0)
2195f127cb91Sfrankho validflags |= BPB_ROOTENTCNT_OK;
2196f127cb91Sfrankho if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
2197f127cb91Sfrankho bpb_get_TotSec16(bpb) == 0)
2198f127cb91Sfrankho validflags |= BPB_TOTSEC16_OK;
2199f127cb91Sfrankho if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
2200f127cb91Sfrankho bpb_get_TotSec32(bpb) == 0)
2201f127cb91Sfrankho validflags |= BPB_TOTSEC32_OK;
2202f127cb91Sfrankho if (bpb_get_FatSz16(bpb) == fatsec)
2203f127cb91Sfrankho validflags |= BPB_FATSZ16_OK;
2204*985bfda7SMichael Bergknoff if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER) * 2)
2205f127cb91Sfrankho validflags |= BPB_FATSZ_OK;
2206f127cb91Sfrankho if (ncl >= 4085 && ncl < 65525)
2207f127cb91Sfrankho validflags |= BPB_NCLUSTERS_OK;
2208f127cb91Sfrankho
2209f127cb91Sfrankho fsp->pcfs_lastclmark = PCF_LASTCLUSTER;
2210f127cb91Sfrankho fsp->pcfs_rootblksize =
2211f127cb91Sfrankho fsp->pcfs_rdirsec * secsize;
2212f127cb91Sfrankho fsp->pcfs_fsistart = 0;
2213f127cb91Sfrankho
2214f127cb91Sfrankho if ((validflags & FAT16_VALIDMSK) != FAT16_VALIDMSK)
2215f127cb91Sfrankho type = FAT_UNKNOWN;
2216f127cb91Sfrankho break;
2217f127cb91Sfrankho case FAT32:
2218f127cb91Sfrankho if (rec == 0)
2219f127cb91Sfrankho validflags |= BPB_ROOTENTCNT_OK;
2220f127cb91Sfrankho if (bpb_get_TotSec16(bpb) == 0)
2221f127cb91Sfrankho validflags |= BPB_TOTSEC16_OK;
2222f127cb91Sfrankho if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec)
2223f127cb91Sfrankho validflags |= BPB_TOTSEC32_OK;
2224f127cb91Sfrankho if (bpb_get_FatSz16(bpb) == 0)
2225f127cb91Sfrankho validflags |= BPB_FATSZ16_OK;
2226f127cb91Sfrankho if (bpb_get_FatSz32(bpb) == fatsec)
2227f127cb91Sfrankho validflags |= BPB_FATSZ32_OK;
2228*985bfda7SMichael Bergknoff if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER) * 4)
2229f127cb91Sfrankho validflags |= BPB_FATSZ_OK;
2230f127cb91Sfrankho if (ncl >= 65525 && ncl < PCF_LASTCLUSTER32)
2231f127cb91Sfrankho validflags |= BPB_NCLUSTERS_OK;
2232f127cb91Sfrankho
2233f127cb91Sfrankho fsp->pcfs_lastclmark = PCF_LASTCLUSTER32;
2234f127cb91Sfrankho fsp->pcfs_rootblksize = fsp->pcfs_clsize;
2235f127cb91Sfrankho fsp->pcfs_fsistart = fsp->pcfs_dosstart + fsisec;
2236f127cb91Sfrankho if (validflags & BPB_FSISEC_OK)
2237f127cb91Sfrankho fsp->pcfs_flags |= PCFS_FSINFO_OK;
2238f127cb91Sfrankho fsp->pcfs_rootclnum = bpb_get_RootClus32(bpb);
2239f127cb91Sfrankho if (pc_validcl(fsp, fsp->pcfs_rootclnum))
2240f127cb91Sfrankho validflags |= BPB_ROOTCLUSTER_OK;
2241f127cb91Sfrankho
2242f127cb91Sfrankho /*
2243f127cb91Sfrankho * Current PCFS code only works if 'pcfs_rdirstart'
2244f127cb91Sfrankho * contains the root cluster number on FAT32.
2245f127cb91Sfrankho * That's a mis-use and would better be changed.
2246f127cb91Sfrankho */
2247f127cb91Sfrankho fsp->pcfs_rdirstart = (daddr_t)fsp->pcfs_rootclnum;
2248f127cb91Sfrankho
2249f127cb91Sfrankho if ((validflags & FAT32_VALIDMSK) != FAT32_VALIDMSK)
2250f127cb91Sfrankho type = FAT_UNKNOWN;
2251f127cb91Sfrankho break;
2252f127cb91Sfrankho case FAT_QUESTIONABLE:
2253f127cb91Sfrankho type = secondaryBPBChecks(fsp, bpb, secsize);
2254f127cb91Sfrankho goto recheck;
2255f127cb91Sfrankho default:
2256f127cb91Sfrankho ASSERT(type == FAT_UNKNOWN);
2257f127cb91Sfrankho break;
2258f127cb91Sfrankho }
2259f127cb91Sfrankho
2260f127cb91Sfrankho ASSERT(type != FAT_QUESTIONABLE);
2261f127cb91Sfrankho
2262f127cb91Sfrankho fsp->pcfs_fattype = type;
2263f127cb91Sfrankho
2264f127cb91Sfrankho if (valid)
2265f127cb91Sfrankho *valid = validflags;
2266f127cb91Sfrankho
2267f127cb91Sfrankho DTRACE_PROBE4(parseBPB__final,
2268f127cb91Sfrankho struct pcfs *, fsp, unsigned char *, bpb,
2269f127cb91Sfrankho int, validflags, fattype_t, type);
2270f127cb91Sfrankho
2271f127cb91Sfrankho if (type != FAT_UNKNOWN) {
2272f127cb91Sfrankho ASSERT((secsize & (DEV_BSIZE - 1)) == 0);
2273f127cb91Sfrankho ASSERT(ISP2(secsize / DEV_BSIZE));
2274f127cb91Sfrankho return (1);
2275f127cb91Sfrankho }
2276f127cb91Sfrankho
2277f127cb91Sfrankho return (0);
2278f127cb91Sfrankho }
2279f127cb91Sfrankho
2280f127cb91Sfrankho
2281f127cb91Sfrankho /*
2282f127cb91Sfrankho * Detect the device's native block size (sector size).
2283f127cb91Sfrankho *
2284f127cb91Sfrankho * Test whether the device is:
2285f127cb91Sfrankho * - a floppy device from a known controller type via DKIOCINFO
2286f127cb91Sfrankho * - a real floppy using the fd(7d) driver and capable of fdio(7I) ioctls
2287f127cb91Sfrankho * - a USB floppy drive (identified by drive geometry)
2288f127cb91Sfrankho *
2289f127cb91Sfrankho * Detecting a floppy will make PCFS metadata updates on such media synchronous,
2290f127cb91Sfrankho * to minimize risks due to slow I/O and user hotplugging / device ejection.
2291f127cb91Sfrankho *
2292f127cb91Sfrankho * This might be a bit wasteful on kernel stack space; if anyone's
2293f127cb91Sfrankho * bothered by this, kmem_alloc/kmem_free the ioctl arguments...
2294f127cb91Sfrankho */
2295f127cb91Sfrankho static void
pcfs_device_getinfo(struct pcfs * fsp)2296f127cb91Sfrankho pcfs_device_getinfo(struct pcfs *fsp)
2297f127cb91Sfrankho {
2298f127cb91Sfrankho dev_t rdev = fsp->pcfs_xdev;
2299f127cb91Sfrankho int error;
2300f127cb91Sfrankho union {
2301f127cb91Sfrankho struct dk_minfo mi;
2302f127cb91Sfrankho struct dk_cinfo ci;
2303f127cb91Sfrankho struct dk_geom gi;
2304f127cb91Sfrankho struct fd_char fc;
2305f127cb91Sfrankho } arg; /* save stackspace ... */
2306f127cb91Sfrankho intptr_t argp = (intptr_t)&arg;
2307f127cb91Sfrankho ldi_handle_t lh;
2308f127cb91Sfrankho ldi_ident_t li;
2309f127cb91Sfrankho int isfloppy, isremoveable, ishotpluggable;
2310f127cb91Sfrankho cred_t *cr = CRED();
2311f127cb91Sfrankho
2312f127cb91Sfrankho if (ldi_ident_from_dev(rdev, &li))
2313f127cb91Sfrankho goto out;
2314f127cb91Sfrankho
2315f127cb91Sfrankho error = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, cr, &lh, li);
2316f127cb91Sfrankho ldi_ident_release(li);
2317f127cb91Sfrankho if (error)
2318f127cb91Sfrankho goto out;
2319f127cb91Sfrankho
2320f127cb91Sfrankho /*
2321f127cb91Sfrankho * Not sure if this could possibly happen. It'd be a bit like
2322f127cb91Sfrankho * VOP_OPEN() changing the passed-in vnode ptr. We're just not
2323f127cb91Sfrankho * expecting it, needs some thought if triggered ...
2324f127cb91Sfrankho */
2325f127cb91Sfrankho ASSERT(fsp->pcfs_xdev == rdev);
2326f127cb91Sfrankho
2327f127cb91Sfrankho /*
2328f127cb91Sfrankho * Check for removeable/hotpluggable media.
2329f127cb91Sfrankho */
2330f127cb91Sfrankho if (ldi_ioctl(lh, DKIOCREMOVABLE,
2331f127cb91Sfrankho (intptr_t)&isremoveable, FKIOCTL, cr, NULL)) {
2332f127cb91Sfrankho isremoveable = 0;
2333f127cb91Sfrankho }
2334f127cb91Sfrankho if (ldi_ioctl(lh, DKIOCHOTPLUGGABLE,
2335f127cb91Sfrankho (intptr_t)&ishotpluggable, FKIOCTL, cr, NULL)) {
2336f127cb91Sfrankho ishotpluggable = 0;
2337f127cb91Sfrankho }
2338f127cb91Sfrankho
2339f127cb91Sfrankho /*
2340f127cb91Sfrankho * Make sure we don't use "half-initialized" values if the ioctls fail.
2341f127cb91Sfrankho */
2342f127cb91Sfrankho if (ldi_ioctl(lh, DKIOCGMEDIAINFO, argp, FKIOCTL, cr, NULL)) {
2343f127cb91Sfrankho bzero(&arg, sizeof (arg));
2344f127cb91Sfrankho fsp->pcfs_mediasize = 0;
2345f127cb91Sfrankho } else {
2346f127cb91Sfrankho fsp->pcfs_mediasize =
2347f127cb91Sfrankho (len_t)arg.mi.dki_lbsize *
2348f127cb91Sfrankho (len_t)arg.mi.dki_capacity;
2349f127cb91Sfrankho }
2350f127cb91Sfrankho
2351f127cb91Sfrankho if (VALID_SECSIZE(arg.mi.dki_lbsize)) {
2352f127cb91Sfrankho if (fsp->pcfs_secsize == 0) {
2353f127cb91Sfrankho fsp->pcfs_secsize = arg.mi.dki_lbsize;
2354f127cb91Sfrankho fsp->pcfs_sdshift =
2355f127cb91Sfrankho ddi_ffs(arg.mi.dki_lbsize / DEV_BSIZE) - 1;
2356f127cb91Sfrankho } else {
2357f127cb91Sfrankho PC_DPRINTF4(1, "!pcfs: autodetected media block size "
2358f127cb91Sfrankho "%d, device (%x.%x), different from user-provided "
2359f127cb91Sfrankho "%d. User override - ignoring autodetect result.\n",
2360f127cb91Sfrankho arg.mi.dki_lbsize,
2361f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
2362f127cb91Sfrankho fsp->pcfs_secsize);
2363f127cb91Sfrankho }
2364f127cb91Sfrankho } else if (arg.mi.dki_lbsize) {
2365f127cb91Sfrankho PC_DPRINTF3(1, "!pcfs: autodetected media block size "
2366f127cb91Sfrankho "%d, device (%x.%x), invalid (not 512, 1024, 2048, 4096). "
2367f127cb91Sfrankho "Ignoring autodetect result.\n",
2368f127cb91Sfrankho arg.mi.dki_lbsize,
2369f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev));
2370f127cb91Sfrankho }
2371f127cb91Sfrankho
2372f127cb91Sfrankho /*
2373f127cb91Sfrankho * We treat the following media types as a floppy by default.
2374f127cb91Sfrankho */
2375f127cb91Sfrankho isfloppy =
2376f127cb91Sfrankho (arg.mi.dki_media_type == DK_FLOPPY ||
2377f127cb91Sfrankho arg.mi.dki_media_type == DK_ZIP ||
2378f127cb91Sfrankho arg.mi.dki_media_type == DK_JAZ);
2379f127cb91Sfrankho
2380f127cb91Sfrankho /*
2381f127cb91Sfrankho * if this device understands fdio(7I) requests it's
2382f127cb91Sfrankho * obviously a floppy drive.
2383f127cb91Sfrankho */
2384f127cb91Sfrankho if (!isfloppy &&
2385f127cb91Sfrankho !ldi_ioctl(lh, FDIOGCHAR, argp, FKIOCTL, cr, NULL))
2386f127cb91Sfrankho isfloppy = 1;
2387f127cb91Sfrankho
2388f127cb91Sfrankho /*
238969ed0c8eSGarrett D'Amore * some devices we like to treat as floppies, but they don't
239069ed0c8eSGarrett D'Amore * understand fdio(7I) requests.
2391f127cb91Sfrankho */
2392f127cb91Sfrankho if (!isfloppy &&
2393f127cb91Sfrankho !ldi_ioctl(lh, DKIOCINFO, argp, FKIOCTL, cr, NULL) &&
2394f127cb91Sfrankho (arg.ci.dki_ctype == DKC_WDC2880 ||
2395f127cb91Sfrankho arg.ci.dki_ctype == DKC_NCRFLOPPY ||
2396f127cb91Sfrankho arg.ci.dki_ctype == DKC_SMSFLOPPY ||
239769ed0c8eSGarrett D'Amore arg.ci.dki_ctype == DKC_INTEL82077))
2398f127cb91Sfrankho isfloppy = 1;
2399f127cb91Sfrankho
2400f127cb91Sfrankho /*
2401f127cb91Sfrankho * This is the "final fallback" test - media with
2402f127cb91Sfrankho * 2 heads and 80 cylinders are assumed to be floppies.
2403f127cb91Sfrankho * This is normally true for USB floppy drives ...
2404f127cb91Sfrankho */
2405f127cb91Sfrankho if (!isfloppy &&
2406f127cb91Sfrankho !ldi_ioctl(lh, DKIOCGGEOM, argp, FKIOCTL, cr, NULL) &&
2407f127cb91Sfrankho (arg.gi.dkg_ncyl == 80 && arg.gi.dkg_nhead == 2))
2408f127cb91Sfrankho isfloppy = 1;
2409f127cb91Sfrankho
2410f127cb91Sfrankho /*
2411f127cb91Sfrankho * This is similar to the "old" PCFS code that sets this flag
2412f127cb91Sfrankho * just based on the media descriptor being 0xf8 (MD_FIXED).
2413f127cb91Sfrankho * Should be re-worked. We really need some specialcasing for
2414f127cb91Sfrankho * removeable media.
2415f127cb91Sfrankho */
2416f127cb91Sfrankho if (!isfloppy) {
2417f127cb91Sfrankho fsp->pcfs_flags |= PCFS_NOCHK;
2418f127cb91Sfrankho }
2419f127cb91Sfrankho
2420f127cb91Sfrankho /*
2421f127cb91Sfrankho * We automatically disable access time updates if the medium is
2422f127cb91Sfrankho * removeable and/or hotpluggable, and the admin did not explicitly
2423f127cb91Sfrankho * request access time updates (via the "atime" mount option).
2424f127cb91Sfrankho * The majority of flash-based media should fit this category.
2425f127cb91Sfrankho * Minimizing write access extends the lifetime of your memory stick !
2426f127cb91Sfrankho */
2427f127cb91Sfrankho if (!vfs_optionisset(fsp->pcfs_vfs, MNTOPT_ATIME, NULL) &&
2428f127cb91Sfrankho (isremoveable || ishotpluggable | isfloppy)) {
2429f127cb91Sfrankho fsp->pcfs_flags |= PCFS_NOATIME;
2430f127cb91Sfrankho }
2431f127cb91Sfrankho
2432f127cb91Sfrankho (void) ldi_close(lh, FREAD, cr);
2433f127cb91Sfrankho out:
2434f127cb91Sfrankho if (fsp->pcfs_secsize == 0) {
2435f127cb91Sfrankho PC_DPRINTF3(1, "!pcfs: media block size autodetection "
2436f127cb91Sfrankho "device (%x.%x) failed, no user-provided fallback. "
2437f127cb91Sfrankho "Using %d bytes.\n",
2438f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
2439f127cb91Sfrankho DEV_BSIZE);
2440f127cb91Sfrankho fsp->pcfs_secsize = DEV_BSIZE;
2441f127cb91Sfrankho fsp->pcfs_sdshift = 0;
2442f127cb91Sfrankho }
2443f127cb91Sfrankho ASSERT(fsp->pcfs_secsize % DEV_BSIZE == 0);
2444f127cb91Sfrankho ASSERT(VALID_SECSIZE(fsp->pcfs_secsize));
2445f127cb91Sfrankho }
2446f127cb91Sfrankho
2447f127cb91Sfrankho /*
2448f127cb91Sfrankho * Get the FAT type for the DOS medium.
2449f127cb91Sfrankho *
2450f127cb91Sfrankho * -------------------------
2451f127cb91Sfrankho * According to Microsoft:
2452f127cb91Sfrankho * The FAT type one of FAT12, FAT16, or FAT32 is determined by the
2453f127cb91Sfrankho * count of clusters on the volume and nothing else.
2454f127cb91Sfrankho * -------------------------
2455f127cb91Sfrankho *
2456f127cb91Sfrankho */
2457f127cb91Sfrankho static int
pc_getfattype(struct pcfs * fsp)2458f127cb91Sfrankho pc_getfattype(struct pcfs *fsp)
2459f127cb91Sfrankho {
2460f127cb91Sfrankho int error = 0;
2461f127cb91Sfrankho buf_t *bp = NULL;
2462f127cb91Sfrankho struct vnode *devvp = fsp->pcfs_devvp;
2463f127cb91Sfrankho dev_t dev = devvp->v_rdev;
2464f127cb91Sfrankho
2465f127cb91Sfrankho /*
2466f127cb91Sfrankho * Detect the native block size of the medium, and attempt to
2467f127cb91Sfrankho * detect whether the medium is removeable.
246869ed0c8eSGarrett D'Amore * We do treat removable media (floppies, USB and FireWire disks)
246969ed0c8eSGarrett D'Amore * differently wrt. to the frequency and synchronicity of FAT updates.
2470f127cb91Sfrankho * We need to know the media block size in order to be able to
2471f127cb91Sfrankho * parse the partition table.
2472f127cb91Sfrankho */
2473f127cb91Sfrankho pcfs_device_getinfo(fsp);
2474f127cb91Sfrankho
2475f127cb91Sfrankho /*
2476f127cb91Sfrankho * Unpartitioned media (floppies and some removeable devices)
2477f127cb91Sfrankho * don't have a partition table, the FAT BPB is at disk block 0.
2478f127cb91Sfrankho * Start out by reading block 0.
2479f127cb91Sfrankho */
2480f127cb91Sfrankho fsp->pcfs_dosstart = 0;
2481f127cb91Sfrankho bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), fsp->pcfs_secsize);
2482f127cb91Sfrankho
2483f127cb91Sfrankho if (error = geterror(bp))
2484f127cb91Sfrankho goto out;
2485f127cb91Sfrankho
2486f127cb91Sfrankho /*
2487f127cb91Sfrankho * If a logical drive number is requested, parse the partition table
2488f127cb91Sfrankho * and attempt to locate it. Otherwise, proceed immediately to the
2489f127cb91Sfrankho * BPB check. findTheDrive(), if successful, returns the disk block
2490f127cb91Sfrankho * number where the requested partition starts in "startsec".
2491f127cb91Sfrankho */
2492f127cb91Sfrankho if (fsp->pcfs_ldrive != 0) {
2493f127cb91Sfrankho PC_DPRINTF3(5, "!pcfs: pc_getfattype: using FDISK table on "
2494f127cb91Sfrankho "device (%x,%x):%d to find BPB\n",
2495f127cb91Sfrankho getmajor(dev), getminor(dev), fsp->pcfs_ldrive);
2496f127cb91Sfrankho
2497f127cb91Sfrankho if (error = findTheDrive(fsp, &bp))
2498f127cb91Sfrankho goto out;
2499f127cb91Sfrankho
2500f127cb91Sfrankho ASSERT(fsp->pcfs_dosstart != 0);
2501f127cb91Sfrankho
2502f127cb91Sfrankho brelse(bp);
2503f127cb91Sfrankho bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
2504f127cb91Sfrankho fsp->pcfs_secsize);
2505f127cb91Sfrankho if (error = geterror(bp))
2506f127cb91Sfrankho goto out;
2507f127cb91Sfrankho }
2508f127cb91Sfrankho
2509f127cb91Sfrankho /*
2510f127cb91Sfrankho * Validate the BPB and fill in the instance structure.
2511f127cb91Sfrankho */
2512f127cb91Sfrankho if (!parseBPB(fsp, (uchar_t *)bp->b_un.b_addr, NULL)) {
2513f127cb91Sfrankho PC_DPRINTF4(1, "!pcfs: pc_getfattype: No FAT BPB on "
2514f127cb91Sfrankho "device (%x.%x):%d, disk LBA %u\n",
2515f127cb91Sfrankho getmajor(dev), getminor(dev), fsp->pcfs_ldrive,
2516f127cb91Sfrankho (uint_t)pc_dbdaddr(fsp, fsp->pcfs_dosstart));
2517f127cb91Sfrankho error = EINVAL;
2518f127cb91Sfrankho goto out;
2519f127cb91Sfrankho }
2520f127cb91Sfrankho
2521f127cb91Sfrankho ASSERT(fsp->pcfs_fattype != FAT_UNKNOWN);
2522f127cb91Sfrankho
2523f127cb91Sfrankho out:
2524f127cb91Sfrankho /*
2525f127cb91Sfrankho * Release the buffer used
2526f127cb91Sfrankho */
2527f127cb91Sfrankho if (bp != NULL)
2528f127cb91Sfrankho brelse(bp);
2529f127cb91Sfrankho return (error);
2530f127cb91Sfrankho }
2531f127cb91Sfrankho
2532f127cb91Sfrankho
2533f127cb91Sfrankho /*
2534f127cb91Sfrankho * Get the file allocation table.
2535f127cb91Sfrankho * If there is an old FAT, invalidate it.
2536f127cb91Sfrankho */
2537f127cb91Sfrankho int
pc_getfat(struct pcfs * fsp)2538f127cb91Sfrankho pc_getfat(struct pcfs *fsp)
2539f127cb91Sfrankho {
2540f127cb91Sfrankho struct buf *bp = NULL;
2541f127cb91Sfrankho uchar_t *fatp = NULL;
2542f127cb91Sfrankho uchar_t *fat_changemap = NULL;
2543f127cb91Sfrankho int error;
2544f127cb91Sfrankho int fat_changemapsize;
2545f127cb91Sfrankho int flags = 0;
2546f127cb91Sfrankho int nfat;
2547f127cb91Sfrankho int altfat_mustmatch = 0;
2548f127cb91Sfrankho int fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
2549f127cb91Sfrankho
2550f127cb91Sfrankho if (fsp->pcfs_fatp) {
2551f127cb91Sfrankho /*
2552f127cb91Sfrankho * There is a FAT in core.
2553f127cb91Sfrankho * If there are open file pcnodes or we have modified it or
2554f127cb91Sfrankho * it hasn't timed out yet use the in core FAT.
2555f127cb91Sfrankho * Otherwise invalidate it and get a new one
2556f127cb91Sfrankho */
2557f127cb91Sfrankho #ifdef notdef
2558f127cb91Sfrankho if (fsp->pcfs_frefs ||
2559f127cb91Sfrankho (fsp->pcfs_flags & PCFS_FATMOD) ||
2560f127cb91Sfrankho (gethrestime_sec() < fsp->pcfs_fattime)) {
2561f127cb91Sfrankho return (0);
2562f127cb91Sfrankho } else {
2563f127cb91Sfrankho mutex_enter(&pcfslock);
2564f127cb91Sfrankho pc_invalfat(fsp);
2565f127cb91Sfrankho mutex_exit(&pcfslock);
2566f127cb91Sfrankho }
2567f127cb91Sfrankho #endif /* notdef */
2568f127cb91Sfrankho return (0);
2569f127cb91Sfrankho }
2570f127cb91Sfrankho
2571f127cb91Sfrankho /*
2572f127cb91Sfrankho * Get FAT and check it for validity
2573f127cb91Sfrankho */
2574f127cb91Sfrankho fatp = kmem_alloc(fatsize, KM_SLEEP);
2575f127cb91Sfrankho error = pc_readfat(fsp, fatp);
2576f127cb91Sfrankho if (error) {
2577f127cb91Sfrankho flags = B_ERROR;
2578f127cb91Sfrankho goto out;
2579f127cb91Sfrankho }
2580f127cb91Sfrankho fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1;
2581f127cb91Sfrankho fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP);
2582f127cb91Sfrankho fsp->pcfs_fatp = fatp;
2583f127cb91Sfrankho fsp->pcfs_fat_changemapsize = fat_changemapsize;
2584f127cb91Sfrankho fsp->pcfs_fat_changemap = fat_changemap;
2585f127cb91Sfrankho
2586f127cb91Sfrankho /*
2587f127cb91Sfrankho * The only definite signature check is that the
2588f127cb91Sfrankho * media descriptor byte should match the first byte
2589f127cb91Sfrankho * of the FAT block.
2590f127cb91Sfrankho */
2591f127cb91Sfrankho if (fatp[0] != fsp->pcfs_mediadesc) {
2592f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: FAT signature mismatch, "
2593f127cb91Sfrankho "media descriptor %x, FAT[0] lowbyte %x\n",
2594f127cb91Sfrankho (uint32_t)fsp->pcfs_mediadesc, (uint32_t)fatp[0]);
2595f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: Enforcing alternate FAT validation\n");
2596f127cb91Sfrankho altfat_mustmatch = 1;
2597f127cb91Sfrankho }
2598f127cb91Sfrankho
2599f127cb91Sfrankho /*
2600f127cb91Sfrankho * Get alternate FATs and check for consistency
2601f127cb91Sfrankho * This is an inlined version of pc_readfat().
2602f127cb91Sfrankho * Since we're only comparing FAT and alternate FAT,
2603f127cb91Sfrankho * there's no reason to let pc_readfat() copy data out
2604f127cb91Sfrankho * of the buf. Instead, compare in-situ, one cluster
2605f127cb91Sfrankho * at a time.
2606f127cb91Sfrankho */
2607f127cb91Sfrankho for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) {
2608f127cb91Sfrankho size_t startsec;
2609f127cb91Sfrankho size_t off;
2610f127cb91Sfrankho
2611f127cb91Sfrankho startsec = pc_dbdaddr(fsp,
2612f127cb91Sfrankho fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec);
2613f127cb91Sfrankho
2614f127cb91Sfrankho for (off = 0; off < fatsize; off += fsp->pcfs_clsize) {
2615f127cb91Sfrankho daddr_t fatblk = startsec + pc_dbdaddr(fsp,
2616f127cb91Sfrankho pc_cltodb(fsp, pc_lblkno(fsp, off)));
2617f127cb91Sfrankho
2618f127cb91Sfrankho bp = bread(fsp->pcfs_xdev, fatblk,
2619f127cb91Sfrankho MIN(fsp->pcfs_clsize, fatsize - off));
2620f127cb91Sfrankho if (bp->b_flags & (B_ERROR | B_STALE)) {
2621f127cb91Sfrankho cmn_err(CE_NOTE,
2622f127cb91Sfrankho "!pcfs: alternate FAT #%d (start LBA %p)"
2623f127cb91Sfrankho " read error at offset %ld on device"
2624f127cb91Sfrankho " (%x.%x):%d",
2625f127cb91Sfrankho nfat, (void *)(uintptr_t)startsec, off,
2626f127cb91Sfrankho getmajor(fsp->pcfs_xdev),
2627f127cb91Sfrankho getminor(fsp->pcfs_xdev),
2628f127cb91Sfrankho fsp->pcfs_ldrive);
2629f127cb91Sfrankho flags = B_ERROR;
2630f127cb91Sfrankho error = EIO;
2631f127cb91Sfrankho goto out;
2632f127cb91Sfrankho }
2633f127cb91Sfrankho bp->b_flags |= B_STALE | B_AGE;
2634f127cb91Sfrankho if (bcmp(bp->b_un.b_addr, fatp + off,
2635f127cb91Sfrankho MIN(fsp->pcfs_clsize, fatsize - off))) {
2636f127cb91Sfrankho cmn_err(CE_NOTE,
2637f127cb91Sfrankho "!pcfs: alternate FAT #%d (start LBA %p)"
2638f127cb91Sfrankho " corrupted at offset %ld on device"
2639f127cb91Sfrankho " (%x.%x):%d",
2640f127cb91Sfrankho nfat, (void *)(uintptr_t)startsec, off,
2641f127cb91Sfrankho getmajor(fsp->pcfs_xdev),
2642f127cb91Sfrankho getminor(fsp->pcfs_xdev),
2643f127cb91Sfrankho fsp->pcfs_ldrive);
2644f127cb91Sfrankho if (altfat_mustmatch) {
2645f127cb91Sfrankho flags = B_ERROR;
2646f127cb91Sfrankho error = EIO;
2647f127cb91Sfrankho goto out;
2648f127cb91Sfrankho }
2649f127cb91Sfrankho }
2650f127cb91Sfrankho brelse(bp);
2651f127cb91Sfrankho bp = NULL; /* prevent double release */
2652f127cb91Sfrankho }
2653f127cb91Sfrankho }
2654f127cb91Sfrankho
2655f127cb91Sfrankho fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
2656f127cb91Sfrankho fsp->pcfs_fatjustread = 1;
2657f127cb91Sfrankho
2658f127cb91Sfrankho /*
2659f127cb91Sfrankho * Retrieve FAT32 fsinfo sector.
2660f127cb91Sfrankho * A failure to read this is not fatal to accessing the volume.
2661f127cb91Sfrankho * It simply means operations that count or search free blocks
2662f127cb91Sfrankho * will have to do a full FAT walk, vs. a possibly quicker lookup
2663f127cb91Sfrankho * of the summary information.
2664f127cb91Sfrankho * Hence, we log a message but return success overall after this point.
2665f127cb91Sfrankho */
2666f127cb91Sfrankho if (IS_FAT32(fsp) && (fsp->pcfs_flags & PCFS_FSINFO_OK)) {
2667f127cb91Sfrankho struct fat_od_fsi *fsinfo_disk;
2668f127cb91Sfrankho
2669f127cb91Sfrankho bp = bread(fsp->pcfs_xdev,
2670f127cb91Sfrankho pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
2671f127cb91Sfrankho fsinfo_disk = (struct fat_od_fsi *)bp->b_un.b_addr;
2672f127cb91Sfrankho if (bp->b_flags & (B_ERROR | B_STALE) ||
2673f127cb91Sfrankho !FSISIG_OK(fsinfo_disk)) {
2674f127cb91Sfrankho cmn_err(CE_NOTE,
2675f127cb91Sfrankho "!pcfs: error reading fat32 fsinfo from "
2676f127cb91Sfrankho "device (%x.%x):%d, block %lld",
2677f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
2678f127cb91Sfrankho fsp->pcfs_ldrive,
2679f127cb91Sfrankho (long long)pc_dbdaddr(fsp, fsp->pcfs_fsistart));
2680f127cb91Sfrankho fsp->pcfs_flags &= ~PCFS_FSINFO_OK;
2681f127cb91Sfrankho fsp->pcfs_fsinfo.fs_free_clusters = FSINFO_UNKNOWN;
2682f127cb91Sfrankho fsp->pcfs_fsinfo.fs_next_free = FSINFO_UNKNOWN;
2683f127cb91Sfrankho } else {
2684f127cb91Sfrankho bp->b_flags |= B_STALE | B_AGE;
2685f127cb91Sfrankho fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
2686f127cb91Sfrankho fsp->pcfs_fsinfo.fs_free_clusters =
2687f127cb91Sfrankho LE_32(fsinfo_disk->fsi_incore.fs_free_clusters);
2688f127cb91Sfrankho fsp->pcfs_fsinfo.fs_next_free =
2689f127cb91Sfrankho LE_32(fsinfo_disk->fsi_incore.fs_next_free);
2690f127cb91Sfrankho }
2691f127cb91Sfrankho brelse(bp);
2692f127cb91Sfrankho bp = NULL;
2693f127cb91Sfrankho }
2694f127cb91Sfrankho
2695f127cb91Sfrankho if (pc_validcl(fsp, (pc_cluster32_t)fsp->pcfs_fsinfo.fs_next_free))
2696f127cb91Sfrankho fsp->pcfs_nxfrecls = fsp->pcfs_fsinfo.fs_next_free;
2697f127cb91Sfrankho else
2698f127cb91Sfrankho fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
2699f127cb91Sfrankho
2700f127cb91Sfrankho return (0);
2701f127cb91Sfrankho
2702f127cb91Sfrankho out:
2703f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: illegal disk format");
2704f127cb91Sfrankho if (bp)
2705f127cb91Sfrankho brelse(bp);
2706f127cb91Sfrankho if (fatp)
2707f127cb91Sfrankho kmem_free(fatp, fatsize);
2708f127cb91Sfrankho if (fat_changemap)
2709f127cb91Sfrankho kmem_free(fat_changemap, fat_changemapsize);
2710f127cb91Sfrankho
2711f127cb91Sfrankho if (flags) {
2712f127cb91Sfrankho pc_mark_irrecov(fsp);
2713f127cb91Sfrankho }
2714f127cb91Sfrankho return (error);
2715f127cb91Sfrankho }
2716