17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 55a59a8b3Srsb * Common Development and Distribution License (the "License"). 65a59a8b3Srsb * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 21342440ecSPrasad Singamsetty 227c478bd9Sstevel@tonic-gate /* 2369ed0c8eSGarrett D'Amore * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 26d9a54dd1SSebastien Roy /* 27d9a54dd1SSebastien Roy * Copyright (c) 2017 by Delphix. All rights reserved. 28d9a54dd1SSebastien Roy */ 297c478bd9Sstevel@tonic-gate 307c478bd9Sstevel@tonic-gate #include <sys/param.h> 317c478bd9Sstevel@tonic-gate #include <sys/systm.h> 327c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 337c478bd9Sstevel@tonic-gate #include <sys/user.h> 347c478bd9Sstevel@tonic-gate #include <sys/proc.h> 357c478bd9Sstevel@tonic-gate #include <sys/cred.h> 367c478bd9Sstevel@tonic-gate #include <sys/disp.h> 377c478bd9Sstevel@tonic-gate #include <sys/buf.h> 387c478bd9Sstevel@tonic-gate #include <sys/vfs.h> 39aa59c4cbSrsb #include <sys/vfs_opreg.h> 407c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 417c478bd9Sstevel@tonic-gate #include <sys/fdio.h> 427c478bd9Sstevel@tonic-gate #include <sys/file.h> 437c478bd9Sstevel@tonic-gate #include <sys/uio.h> 447c478bd9Sstevel@tonic-gate #include <sys/conf.h> 457c478bd9Sstevel@tonic-gate #include <sys/statvfs.h> 467c478bd9Sstevel@tonic-gate #include <sys/mount.h> 477c478bd9Sstevel@tonic-gate #include <sys/pathname.h> 487c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 497c478bd9Sstevel@tonic-gate #include <sys/debug.h> 507c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 517c478bd9Sstevel@tonic-gate #include <sys/conf.h> 527c478bd9Sstevel@tonic-gate #include <sys/mkdev.h> 537c478bd9Sstevel@tonic-gate #include <sys/swap.h> 547c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 557c478bd9Sstevel@tonic-gate #include <sys/sunldi.h> 567c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h> 577c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h> 587c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h> 597c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h> 607c478bd9Sstevel@tonic-gate #include <sys/fs/pc_node.h> 617c478bd9Sstevel@tonic-gate #include <fs/fs_subr.h> 627c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 637c478bd9Sstevel@tonic-gate #include <sys/dkio.h> 647c478bd9Sstevel@tonic-gate #include <sys/open.h> 657c478bd9Sstevel@tonic-gate #include <sys/mntent.h> 667c478bd9Sstevel@tonic-gate #include <sys/policy.h> 67264a6e74Sfrankho #include <sys/atomic.h> 68f127cb91Sfrankho #include <sys/sdt.h> 697c478bd9Sstevel@tonic-gate 707c478bd9Sstevel@tonic-gate /* 717c478bd9Sstevel@tonic-gate * The majority of PC media use a 512 sector size, but 727c478bd9Sstevel@tonic-gate * occasionally you will run across a 1k sector size. 737c478bd9Sstevel@tonic-gate * For media with a 1k sector size, fd_strategy() requires 747c478bd9Sstevel@tonic-gate * the I/O size to be a 1k multiple; so when the sector size 757c478bd9Sstevel@tonic-gate * is not yet known, always read 1k. 767c478bd9Sstevel@tonic-gate */ 777c478bd9Sstevel@tonic-gate #define PC_SAFESECSIZE (PC_SECSIZE * 2) 787c478bd9Sstevel@tonic-gate 7990c30842Sjmcp static int pcfs_pseudo_floppy(dev_t); 807c478bd9Sstevel@tonic-gate 817c478bd9Sstevel@tonic-gate static int pcfsinit(int, char *); 827c478bd9Sstevel@tonic-gate static int pcfs_mount(struct vfs *, struct vnode *, struct mounta *, 837c478bd9Sstevel@tonic-gate struct cred *); 847c478bd9Sstevel@tonic-gate static int pcfs_unmount(struct vfs *, int, struct cred *); 857c478bd9Sstevel@tonic-gate static int pcfs_root(struct vfs *, struct vnode **); 867c478bd9Sstevel@tonic-gate static int pcfs_statvfs(struct vfs *, struct statvfs64 *); 877c478bd9Sstevel@tonic-gate static int pc_syncfsnodes(struct pcfs *); 887c478bd9Sstevel@tonic-gate static int pcfs_sync(struct vfs *, short, struct cred *); 897c478bd9Sstevel@tonic-gate static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp); 90264a6e74Sfrankho static void pcfs_freevfs(vfs_t *vfsp); 917c478bd9Sstevel@tonic-gate 92f127cb91Sfrankho static int pc_readfat(struct pcfs *fsp, uchar_t *fatp); 937c478bd9Sstevel@tonic-gate static int pc_writefat(struct pcfs *fsp, daddr_t start); 947c478bd9Sstevel@tonic-gate 95f127cb91Sfrankho static int pc_getfattype(struct pcfs *fsp); 969e003ac4Sgd static void pcfs_parse_mntopts(struct pcfs *fsp); 97f127cb91Sfrankho 98f127cb91Sfrankho 997c478bd9Sstevel@tonic-gate /* 1007c478bd9Sstevel@tonic-gate * pcfs mount options table 1017c478bd9Sstevel@tonic-gate */ 1027c478bd9Sstevel@tonic-gate 103264a6e74Sfrankho static char *nohidden_cancel[] = { MNTOPT_PCFS_HIDDEN, NULL }; 104264a6e74Sfrankho static char *hidden_cancel[] = { MNTOPT_PCFS_NOHIDDEN, NULL }; 105264a6e74Sfrankho static char *nofoldcase_cancel[] = { MNTOPT_PCFS_FOLDCASE, NULL }; 106264a6e74Sfrankho static char *foldcase_cancel[] = { MNTOPT_PCFS_NOFOLDCASE, NULL }; 107264a6e74Sfrankho static char *clamptime_cancel[] = { MNTOPT_PCFS_NOCLAMPTIME, NULL }; 108264a6e74Sfrankho static char *noclamptime_cancel[] = { MNTOPT_PCFS_CLAMPTIME, NULL }; 109f127cb91Sfrankho static char *atime_cancel[] = { MNTOPT_NOATIME, NULL }; 110f127cb91Sfrankho static char *noatime_cancel[] = { MNTOPT_ATIME, NULL }; 1117c478bd9Sstevel@tonic-gate 1127c478bd9Sstevel@tonic-gate static mntopt_t mntopts[] = { 1137c478bd9Sstevel@tonic-gate /* 114264a6e74Sfrankho * option name cancel option default arg flags opt data 1157c478bd9Sstevel@tonic-gate */ 116264a6e74Sfrankho { MNTOPT_PCFS_NOHIDDEN, nohidden_cancel, NULL, 0, NULL }, 117264a6e74Sfrankho { MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, MO_DEFAULT, NULL }, 118264a6e74Sfrankho { MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, NULL }, 119264a6e74Sfrankho { MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0, NULL }, 120264a6e74Sfrankho { MNTOPT_PCFS_CLAMPTIME, clamptime_cancel, NULL, MO_DEFAULT, NULL }, 121*80a3d255SToomas Soome { MNTOPT_PCFS_NOCLAMPTIME, noclamptime_cancel, NULL, 0, NULL }, 122*80a3d255SToomas Soome { MNTOPT_NOATIME, noatime_cancel, NULL, 0, NULL }, 123*80a3d255SToomas Soome { MNTOPT_ATIME, atime_cancel, NULL, 0, NULL }, 124f127cb91Sfrankho { MNTOPT_PCFS_TIMEZONE, NULL, "+0", MO_DEFAULT | MO_HASVALUE, NULL }, 125f127cb91Sfrankho { MNTOPT_PCFS_SECSIZE, NULL, NULL, MO_HASVALUE, NULL } 1267c478bd9Sstevel@tonic-gate }; 1277c478bd9Sstevel@tonic-gate 1287c478bd9Sstevel@tonic-gate static mntopts_t pcfs_mntopts = { 1297c478bd9Sstevel@tonic-gate sizeof (mntopts) / sizeof (mntopt_t), 1307c478bd9Sstevel@tonic-gate mntopts 1317c478bd9Sstevel@tonic-gate }; 1327c478bd9Sstevel@tonic-gate 1337c478bd9Sstevel@tonic-gate int pcfsdebuglevel = 0; 1347c478bd9Sstevel@tonic-gate 1357c478bd9Sstevel@tonic-gate /* 1367c478bd9Sstevel@tonic-gate * pcfslock: protects the list of mounted pc filesystems "pc_mounttab. 1377c478bd9Sstevel@tonic-gate * pcfs_lock: (inside per filesystem structure "pcfs") 1387c478bd9Sstevel@tonic-gate * per filesystem lock. Most of the vfsops and vnodeops are 1397c478bd9Sstevel@tonic-gate * protected by this lock. 1407c478bd9Sstevel@tonic-gate * pcnodes_lock: protects the pcnode hash table "pcdhead", "pcfhead". 1417c478bd9Sstevel@tonic-gate * 1427c478bd9Sstevel@tonic-gate * Lock hierarchy: pcfslock > pcfs_lock > pcnodes_lock 143264a6e74Sfrankho * 144264a6e74Sfrankho * pcfs_mountcount: used to prevent module unloads while there is still 145264a6e74Sfrankho * pcfs state from a former mount hanging around. With 146264a6e74Sfrankho * forced umount support, the filesystem module must not 147264a6e74Sfrankho * be allowed to go away before the last VFS_FREEVFS() 148264a6e74Sfrankho * call has been made. 149264a6e74Sfrankho * Since this is just an atomic counter, there's no need 150264a6e74Sfrankho * for locking. 1517c478bd9Sstevel@tonic-gate */ 1527c478bd9Sstevel@tonic-gate kmutex_t pcfslock; 153264a6e74Sfrankho krwlock_t pcnodes_lock; 154264a6e74Sfrankho uint32_t pcfs_mountcount; 1557c478bd9Sstevel@tonic-gate 1567c478bd9Sstevel@tonic-gate static int pcfstype; 1577c478bd9Sstevel@tonic-gate 1587c478bd9Sstevel@tonic-gate static vfsdef_t vfw = { 1597c478bd9Sstevel@tonic-gate VFSDEF_VERSION, 1607c478bd9Sstevel@tonic-gate "pcfs", 1617c478bd9Sstevel@tonic-gate pcfsinit, 162d9a54dd1SSebastien Roy VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS|VSW_CANLOFI|VSW_MOUNTDEV, 1637c478bd9Sstevel@tonic-gate &pcfs_mntopts 1647c478bd9Sstevel@tonic-gate }; 1657c478bd9Sstevel@tonic-gate 1667c478bd9Sstevel@tonic-gate extern struct mod_ops mod_fsops; 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gate static struct modlfs modlfs = { 1697c478bd9Sstevel@tonic-gate &mod_fsops, 170c2aaf90fSgd "PC filesystem", 1717c478bd9Sstevel@tonic-gate &vfw 1727c478bd9Sstevel@tonic-gate }; 1737c478bd9Sstevel@tonic-gate 1747c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = { 1757c478bd9Sstevel@tonic-gate MODREV_1, 1767c478bd9Sstevel@tonic-gate &modlfs, 1777c478bd9Sstevel@tonic-gate NULL 1787c478bd9Sstevel@tonic-gate }; 1797c478bd9Sstevel@tonic-gate 1807c478bd9Sstevel@tonic-gate int 1817c478bd9Sstevel@tonic-gate _init(void) 1827c478bd9Sstevel@tonic-gate { 1837c478bd9Sstevel@tonic-gate int error; 1847c478bd9Sstevel@tonic-gate 1857c478bd9Sstevel@tonic-gate #if !defined(lint) 1867c478bd9Sstevel@tonic-gate /* make sure the on-disk structures are sane */ 1877c478bd9Sstevel@tonic-gate ASSERT(sizeof (struct pcdir) == 32); 1887c478bd9Sstevel@tonic-gate ASSERT(sizeof (struct pcdir_lfn) == 32); 1897c478bd9Sstevel@tonic-gate #endif 1907c478bd9Sstevel@tonic-gate mutex_init(&pcfslock, NULL, MUTEX_DEFAULT, NULL); 1917c478bd9Sstevel@tonic-gate rw_init(&pcnodes_lock, NULL, RW_DEFAULT, NULL); 1927c478bd9Sstevel@tonic-gate error = mod_install(&modlinkage); 1937c478bd9Sstevel@tonic-gate if (error) { 1947c478bd9Sstevel@tonic-gate mutex_destroy(&pcfslock); 1957c478bd9Sstevel@tonic-gate rw_destroy(&pcnodes_lock); 1967c478bd9Sstevel@tonic-gate } 1977c478bd9Sstevel@tonic-gate return (error); 1987c478bd9Sstevel@tonic-gate } 1997c478bd9Sstevel@tonic-gate 2007c478bd9Sstevel@tonic-gate int 2017c478bd9Sstevel@tonic-gate _fini(void) 2027c478bd9Sstevel@tonic-gate { 2037c478bd9Sstevel@tonic-gate int error; 2047c478bd9Sstevel@tonic-gate 205264a6e74Sfrankho /* 206264a6e74Sfrankho * If a forcedly unmounted instance is still hanging around, 207264a6e74Sfrankho * we cannot allow the module to be unloaded because that would 208264a6e74Sfrankho * cause panics once the VFS framework decides it's time to call 209264a6e74Sfrankho * into VFS_FREEVFS(). 210264a6e74Sfrankho */ 211264a6e74Sfrankho if (pcfs_mountcount) 212264a6e74Sfrankho return (EBUSY); 213264a6e74Sfrankho 2147c478bd9Sstevel@tonic-gate error = mod_remove(&modlinkage); 2157c478bd9Sstevel@tonic-gate if (error) 2167c478bd9Sstevel@tonic-gate return (error); 2177c478bd9Sstevel@tonic-gate mutex_destroy(&pcfslock); 2187c478bd9Sstevel@tonic-gate rw_destroy(&pcnodes_lock); 2197c478bd9Sstevel@tonic-gate /* 2207c478bd9Sstevel@tonic-gate * Tear down the operations vectors 2217c478bd9Sstevel@tonic-gate */ 2227c478bd9Sstevel@tonic-gate (void) vfs_freevfsops_by_type(pcfstype); 2237c478bd9Sstevel@tonic-gate vn_freevnodeops(pcfs_fvnodeops); 2247c478bd9Sstevel@tonic-gate vn_freevnodeops(pcfs_dvnodeops); 2257c478bd9Sstevel@tonic-gate return (0); 2267c478bd9Sstevel@tonic-gate } 2277c478bd9Sstevel@tonic-gate 2287c478bd9Sstevel@tonic-gate int 2297c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop) 2307c478bd9Sstevel@tonic-gate { 2317c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 2327c478bd9Sstevel@tonic-gate } 2337c478bd9Sstevel@tonic-gate 2347c478bd9Sstevel@tonic-gate /* ARGSUSED1 */ 2357c478bd9Sstevel@tonic-gate static int 2367c478bd9Sstevel@tonic-gate pcfsinit(int fstype, char *name) 2377c478bd9Sstevel@tonic-gate { 2387c478bd9Sstevel@tonic-gate static const fs_operation_def_t pcfs_vfsops_template[] = { 239aa59c4cbSrsb VFSNAME_MOUNT, { .vfs_mount = pcfs_mount }, 240aa59c4cbSrsb VFSNAME_UNMOUNT, { .vfs_unmount = pcfs_unmount }, 241aa59c4cbSrsb VFSNAME_ROOT, { .vfs_root = pcfs_root }, 242aa59c4cbSrsb VFSNAME_STATVFS, { .vfs_statvfs = pcfs_statvfs }, 243aa59c4cbSrsb VFSNAME_SYNC, { .vfs_sync = pcfs_sync }, 244aa59c4cbSrsb VFSNAME_VGET, { .vfs_vget = pcfs_vget }, 245aa59c4cbSrsb VFSNAME_FREEVFS, { .vfs_freevfs = pcfs_freevfs }, 246aa59c4cbSrsb NULL, NULL 2477c478bd9Sstevel@tonic-gate }; 2487c478bd9Sstevel@tonic-gate int error; 2497c478bd9Sstevel@tonic-gate 2507c478bd9Sstevel@tonic-gate error = vfs_setfsops(fstype, pcfs_vfsops_template, NULL); 2517c478bd9Sstevel@tonic-gate if (error != 0) { 2527c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "pcfsinit: bad vfs ops template"); 2537c478bd9Sstevel@tonic-gate return (error); 2547c478bd9Sstevel@tonic-gate } 2557c478bd9Sstevel@tonic-gate 2567c478bd9Sstevel@tonic-gate error = vn_make_ops("pcfs", pcfs_fvnodeops_template, &pcfs_fvnodeops); 2577c478bd9Sstevel@tonic-gate if (error != 0) { 2587c478bd9Sstevel@tonic-gate (void) vfs_freevfsops_by_type(fstype); 2597c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "pcfsinit: bad file vnode ops template"); 2607c478bd9Sstevel@tonic-gate return (error); 2617c478bd9Sstevel@tonic-gate } 2627c478bd9Sstevel@tonic-gate 2637c478bd9Sstevel@tonic-gate error = vn_make_ops("pcfsd", pcfs_dvnodeops_template, &pcfs_dvnodeops); 2647c478bd9Sstevel@tonic-gate if (error != 0) { 2657c478bd9Sstevel@tonic-gate (void) vfs_freevfsops_by_type(fstype); 2667c478bd9Sstevel@tonic-gate vn_freevnodeops(pcfs_fvnodeops); 2677c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "pcfsinit: bad dir vnode ops template"); 2687c478bd9Sstevel@tonic-gate return (error); 2697c478bd9Sstevel@tonic-gate } 2707c478bd9Sstevel@tonic-gate 2717c478bd9Sstevel@tonic-gate pcfstype = fstype; 2727c478bd9Sstevel@tonic-gate (void) pc_init(); 273264a6e74Sfrankho pcfs_mountcount = 0; 2747c478bd9Sstevel@tonic-gate return (0); 2757c478bd9Sstevel@tonic-gate } 2767c478bd9Sstevel@tonic-gate 2777c478bd9Sstevel@tonic-gate static struct pcfs *pc_mounttab = NULL; 2787c478bd9Sstevel@tonic-gate 2797c478bd9Sstevel@tonic-gate extern struct pcfs_args pc_tz; 2807c478bd9Sstevel@tonic-gate 2817c478bd9Sstevel@tonic-gate /* 2827c478bd9Sstevel@tonic-gate * Define some special logical drives we use internal to this file. 2837c478bd9Sstevel@tonic-gate */ 2847c478bd9Sstevel@tonic-gate #define BOOT_PARTITION_DRIVE 99 2857c478bd9Sstevel@tonic-gate #define PRIMARY_DOS_DRIVE 1 286f127cb91Sfrankho #define UNPARTITIONED_DRIVE 0 2877c478bd9Sstevel@tonic-gate 2887c478bd9Sstevel@tonic-gate static int 289f127cb91Sfrankho pcfs_device_identify( 2907c478bd9Sstevel@tonic-gate struct vfs *vfsp, 2917c478bd9Sstevel@tonic-gate struct mounta *uap, 292f127cb91Sfrankho struct cred *cr, 293f127cb91Sfrankho int *dos_ldrive, 294f127cb91Sfrankho dev_t *xdev) 2957c478bd9Sstevel@tonic-gate { 2967c478bd9Sstevel@tonic-gate struct pathname special; 2979bd42341Sfrankho char *c; 29893239addSjohnlev struct vnode *svp = NULL; 29993239addSjohnlev struct vnode *lvp = NULL; 3007c478bd9Sstevel@tonic-gate int oflag, aflag; 301f127cb91Sfrankho int error; 3027c478bd9Sstevel@tonic-gate 3037c478bd9Sstevel@tonic-gate /* 3047c478bd9Sstevel@tonic-gate * Resolve path name of special file being mounted. 3057c478bd9Sstevel@tonic-gate */ 3067c478bd9Sstevel@tonic-gate if (error = pn_get(uap->spec, UIO_USERSPACE, &special)) { 3077c478bd9Sstevel@tonic-gate return (error); 3087c478bd9Sstevel@tonic-gate } 309f127cb91Sfrankho 310f127cb91Sfrankho *dos_ldrive = -1; 311f127cb91Sfrankho 3127c478bd9Sstevel@tonic-gate if (error = 31393239addSjohnlev lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &svp)) { 314f127cb91Sfrankho /* 315f127cb91Sfrankho * If there's no device node, the name specified most likely 316f127cb91Sfrankho * maps to a PCFS-style "partition specifier" to select a 317f127cb91Sfrankho * harddisk primary/logical partition. Disable floppy-specific 318f127cb91Sfrankho * checks in such cases unless an explicit :A or :B is 319f127cb91Sfrankho * requested. 320f127cb91Sfrankho */ 321f127cb91Sfrankho 3227c478bd9Sstevel@tonic-gate /* 3239bd42341Sfrankho * Split the pathname string at the last ':' separator. 3249bd42341Sfrankho * If there's no ':' in the device name, or the ':' is the 3259bd42341Sfrankho * last character in the string, the name is invalid and 3269bd42341Sfrankho * the error from the previous lookup will be returned. 3277c478bd9Sstevel@tonic-gate */ 3289bd42341Sfrankho c = strrchr(special.pn_path, ':'); 3299bd42341Sfrankho if (c == NULL || strlen(c) == 0) 3309bd42341Sfrankho goto devlookup_done; 3317c478bd9Sstevel@tonic-gate 3329bd42341Sfrankho *c++ = '\0'; 3337c478bd9Sstevel@tonic-gate 3349bd42341Sfrankho /* 3359bd42341Sfrankho * PCFS partition name suffixes can be: 3369bd42341Sfrankho * - "boot" to indicate the X86BOOT partition 3379bd42341Sfrankho * - a drive letter [c-z] for the "DOS logical drive" 3389bd42341Sfrankho * - a drive number 1..24 for the "DOS logical drive" 339f127cb91Sfrankho * - a "floppy name letter", 'a' or 'b' (just strip this) 3409bd42341Sfrankho */ 3419bd42341Sfrankho if (strcasecmp(c, "boot") == 0) { 3429bd42341Sfrankho /* 3439bd42341Sfrankho * The Solaris boot partition is requested. 3449bd42341Sfrankho */ 345f127cb91Sfrankho *dos_ldrive = BOOT_PARTITION_DRIVE; 3469bd42341Sfrankho } else if (strspn(c, "0123456789") == strlen(c)) { 3479bd42341Sfrankho /* 3489bd42341Sfrankho * All digits - parse the partition number. 3499bd42341Sfrankho */ 3509bd42341Sfrankho long drvnum = 0; 3519bd42341Sfrankho 3529bd42341Sfrankho if ((error = ddi_strtol(c, NULL, 10, &drvnum)) == 0) { 3537c478bd9Sstevel@tonic-gate /* 3549bd42341Sfrankho * A number alright - in the allowed range ? 3557c478bd9Sstevel@tonic-gate */ 3569bd42341Sfrankho if (drvnum > 24 || drvnum == 0) 357f127cb91Sfrankho error = ENXIO; 3587c478bd9Sstevel@tonic-gate } 3599bd42341Sfrankho if (error) 3609bd42341Sfrankho goto devlookup_done; 361f127cb91Sfrankho *dos_ldrive = (int)drvnum; 3629bd42341Sfrankho } else if (strlen(c) == 1) { 3639bd42341Sfrankho /* 364f127cb91Sfrankho * A single trailing character was specified. 365f127cb91Sfrankho * - [c-zC-Z] means a harddisk partition, and 366f127cb91Sfrankho * we retrieve the partition number. 367f127cb91Sfrankho * - [abAB] means a floppy drive, so we swallow 368f127cb91Sfrankho * the "drive specifier" and test later 36969ed0c8eSGarrett D'Amore * whether the physical device is a floppy. 3709bd42341Sfrankho */ 3719bd42341Sfrankho *c = tolower(*c); 372f127cb91Sfrankho if (*c == 'a' || *c == 'b') { 373f127cb91Sfrankho *dos_ldrive = UNPARTITIONED_DRIVE; 374f127cb91Sfrankho } else if (*c < 'c' || *c > 'z') { 375f127cb91Sfrankho error = ENXIO; 3769bd42341Sfrankho goto devlookup_done; 377f127cb91Sfrankho } else { 378f127cb91Sfrankho *dos_ldrive = 1 + *c - 'c'; 3799bd42341Sfrankho } 3809bd42341Sfrankho } else { 3819bd42341Sfrankho /* 3829bd42341Sfrankho * Can't parse this - pass through previous error. 3839bd42341Sfrankho */ 3849bd42341Sfrankho goto devlookup_done; 3857c478bd9Sstevel@tonic-gate } 3869bd42341Sfrankho 3879bd42341Sfrankho 3889bd42341Sfrankho error = lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, 38993239addSjohnlev NULLVPP, &svp); 390f127cb91Sfrankho } else { 391f127cb91Sfrankho *dos_ldrive = UNPARTITIONED_DRIVE; 3927c478bd9Sstevel@tonic-gate } 3939bd42341Sfrankho devlookup_done: 3947c478bd9Sstevel@tonic-gate pn_free(&special); 3959bd42341Sfrankho if (error) 3969bd42341Sfrankho return (error); 3979bd42341Sfrankho 398f127cb91Sfrankho ASSERT(*dos_ldrive >= UNPARTITIONED_DRIVE); 399f127cb91Sfrankho 4007c478bd9Sstevel@tonic-gate /* 4017c478bd9Sstevel@tonic-gate * Verify caller's permission to open the device special file. 4027c478bd9Sstevel@tonic-gate */ 4037c478bd9Sstevel@tonic-gate if ((vfsp->vfs_flag & VFS_RDONLY) != 0 || 4047c478bd9Sstevel@tonic-gate ((uap->flags & MS_RDONLY) != 0)) { 4057c478bd9Sstevel@tonic-gate oflag = FREAD; 4067c478bd9Sstevel@tonic-gate aflag = VREAD; 4077c478bd9Sstevel@tonic-gate } else { 4087c478bd9Sstevel@tonic-gate oflag = FREAD | FWRITE; 4097c478bd9Sstevel@tonic-gate aflag = VREAD | VWRITE; 4107c478bd9Sstevel@tonic-gate } 411f127cb91Sfrankho 41293239addSjohnlev error = vfs_get_lofi(vfsp, &lvp); 413f127cb91Sfrankho 41493239addSjohnlev if (error > 0) { 41593239addSjohnlev if (error == ENOENT) 41693239addSjohnlev error = ENODEV; 41793239addSjohnlev goto out; 41893239addSjohnlev } else if (error == 0) { 41993239addSjohnlev *xdev = lvp->v_rdev; 42093239addSjohnlev } else { 42193239addSjohnlev *xdev = svp->v_rdev; 42293239addSjohnlev 423ecffa4d6SJohn Levon if (svp->v_type != VBLK) { 42493239addSjohnlev error = ENOTBLK; 425ecffa4d6SJohn Levon goto out; 426ecffa4d6SJohn Levon } 42793239addSjohnlev 42893239addSjohnlev if ((error = secpolicy_spec_open(cr, svp, oflag)) != 0) 42993239addSjohnlev goto out; 4307c478bd9Sstevel@tonic-gate } 4317c478bd9Sstevel@tonic-gate 43293239addSjohnlev if (getmajor(*xdev) >= devcnt) { 43393239addSjohnlev error = ENXIO; 43493239addSjohnlev goto out; 43593239addSjohnlev } 43693239addSjohnlev 43793239addSjohnlev if ((error = VOP_ACCESS(svp, aflag, 0, cr, NULL)) != 0) 43893239addSjohnlev goto out; 43993239addSjohnlev 44093239addSjohnlev out: 44193239addSjohnlev if (svp != NULL) 44293239addSjohnlev VN_RELE(svp); 44393239addSjohnlev if (lvp != NULL) 44493239addSjohnlev VN_RELE(lvp); 44593239addSjohnlev return (error); 446f127cb91Sfrankho } 447f127cb91Sfrankho 448f127cb91Sfrankho static int 449f127cb91Sfrankho pcfs_device_ismounted( 450f127cb91Sfrankho struct vfs *vfsp, 451f127cb91Sfrankho int dos_ldrive, 452f127cb91Sfrankho dev_t xdev, 453f127cb91Sfrankho int *remounting, 454f127cb91Sfrankho dev_t *pseudodev) 455f127cb91Sfrankho { 456f127cb91Sfrankho struct pcfs *fsp; 457f127cb91Sfrankho int remount = *remounting; 458f127cb91Sfrankho 4597c478bd9Sstevel@tonic-gate /* 4609bd42341Sfrankho * Ensure that this logical drive isn't already mounted, unless 4619bd42341Sfrankho * this is a REMOUNT request. 4629bd42341Sfrankho * Note: The framework will perform this check if the "...:c" 4639bd42341Sfrankho * PCFS-style "logical drive" syntax has not been used and an 4649bd42341Sfrankho * actually existing physical device is backing this filesystem. 465f127cb91Sfrankho * Once all block device drivers support PC-style partitioning, 466f127cb91Sfrankho * this codeblock can be dropped. 4677c478bd9Sstevel@tonic-gate */ 468f127cb91Sfrankho *pseudodev = xdev; 469f127cb91Sfrankho 4707c478bd9Sstevel@tonic-gate if (dos_ldrive) { 4717c478bd9Sstevel@tonic-gate mutex_enter(&pcfslock); 4727c478bd9Sstevel@tonic-gate for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt) 4737c478bd9Sstevel@tonic-gate if (fsp->pcfs_xdev == xdev && 474f127cb91Sfrankho fsp->pcfs_ldrive == dos_ldrive) { 4757c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock); 476f127cb91Sfrankho if (remount) { 4777c478bd9Sstevel@tonic-gate return (0); 4787c478bd9Sstevel@tonic-gate } else { 4797c478bd9Sstevel@tonic-gate return (EBUSY); 4807c478bd9Sstevel@tonic-gate } 4817c478bd9Sstevel@tonic-gate } 4827c478bd9Sstevel@tonic-gate /* 4837c478bd9Sstevel@tonic-gate * Assign a unique device number for the vfs 4847c478bd9Sstevel@tonic-gate * The old way (getudev() + a constantly incrementing 4857c478bd9Sstevel@tonic-gate * major number) was wrong because it changes vfs_dev 4867c478bd9Sstevel@tonic-gate * across mounts and reboots, which breaks nfs file handles. 4877c478bd9Sstevel@tonic-gate * UFS just uses the real dev_t. We can't do that because 4887c478bd9Sstevel@tonic-gate * of the way pcfs opens fdisk partitons (the :c and :d 4897c478bd9Sstevel@tonic-gate * partitions are on the same dev_t). Though that _might_ 4907c478bd9Sstevel@tonic-gate * actually be ok, since the file handle contains an 4917c478bd9Sstevel@tonic-gate * absolute block number, it's probably better to make them 4927c478bd9Sstevel@tonic-gate * different. So I think we should retain the original 4937c478bd9Sstevel@tonic-gate * dev_t, but come up with a different minor number based 4947c478bd9Sstevel@tonic-gate * on the logical drive that will _always_ come up the same. 4957c478bd9Sstevel@tonic-gate * For now, we steal the upper 6 bits. 4967c478bd9Sstevel@tonic-gate */ 4977c478bd9Sstevel@tonic-gate #ifdef notdef 4987c478bd9Sstevel@tonic-gate /* what should we do here? */ 4997c478bd9Sstevel@tonic-gate if (((getminor(xdev) >> 12) & 0x3F) != 0) 5007c478bd9Sstevel@tonic-gate printf("whoops - upper bits used!\n"); 5017c478bd9Sstevel@tonic-gate #endif 502f127cb91Sfrankho *pseudodev = makedevice(getmajor(xdev), 503f127cb91Sfrankho ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32); 504f127cb91Sfrankho if (vfs_devmounting(*pseudodev, vfsp)) { 5057c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock); 5067c478bd9Sstevel@tonic-gate return (EBUSY); 5077c478bd9Sstevel@tonic-gate } 508f127cb91Sfrankho if (vfs_devismounted(*pseudodev)) { 5097c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock); 510f127cb91Sfrankho if (remount) { 5117c478bd9Sstevel@tonic-gate return (0); 5127c478bd9Sstevel@tonic-gate } else { 5137c478bd9Sstevel@tonic-gate return (EBUSY); 5147c478bd9Sstevel@tonic-gate } 5157c478bd9Sstevel@tonic-gate } 5167c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock); 5177c478bd9Sstevel@tonic-gate } else { 518f127cb91Sfrankho *pseudodev = xdev; 519f127cb91Sfrankho if (vfs_devmounting(*pseudodev, vfsp)) { 5207c478bd9Sstevel@tonic-gate return (EBUSY); 5217c478bd9Sstevel@tonic-gate } 522f127cb91Sfrankho if (vfs_devismounted(*pseudodev)) 523f127cb91Sfrankho if (remount) { 5247c478bd9Sstevel@tonic-gate return (0); 5257c478bd9Sstevel@tonic-gate } else { 5267c478bd9Sstevel@tonic-gate return (EBUSY); 5277c478bd9Sstevel@tonic-gate } 5287c478bd9Sstevel@tonic-gate } 5297c478bd9Sstevel@tonic-gate 530f127cb91Sfrankho /* 531f127cb91Sfrankho * This is not a remount. Even if MS_REMOUNT was requested, 532f127cb91Sfrankho * the caller needs to proceed as it would on an ordinary 533f127cb91Sfrankho * mount. 534f127cb91Sfrankho */ 535f127cb91Sfrankho *remounting = 0; 536f127cb91Sfrankho 537f127cb91Sfrankho ASSERT(*pseudodev); 538f127cb91Sfrankho return (0); 539f127cb91Sfrankho } 540f127cb91Sfrankho 541f127cb91Sfrankho /* 542f127cb91Sfrankho * Get the PCFS-specific mount options from the VFS framework. 543f127cb91Sfrankho * For "timezone" and "secsize", we need to parse the number 544f127cb91Sfrankho * ourselves and ensure its validity. 545f127cb91Sfrankho * Note: "secsize" is deliberately undocumented at this time, 546f127cb91Sfrankho * it's a workaround for devices (particularly: lofi image files) 547f127cb91Sfrankho * that don't support the DKIOCGMEDIAINFO ioctl for autodetection. 548f127cb91Sfrankho */ 549f127cb91Sfrankho static void 5509e003ac4Sgd pcfs_parse_mntopts(struct pcfs *fsp) 551f127cb91Sfrankho { 552f127cb91Sfrankho char *c; 553f127cb91Sfrankho char *endptr; 554f127cb91Sfrankho long l; 555f127cb91Sfrankho struct vfs *vfsp = fsp->pcfs_vfs; 556f127cb91Sfrankho 557f127cb91Sfrankho ASSERT(fsp->pcfs_secondswest == 0); 558f127cb91Sfrankho ASSERT(fsp->pcfs_secsize == 0); 559f127cb91Sfrankho 560f127cb91Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL)) 561f127cb91Sfrankho fsp->pcfs_flags |= PCFS_HIDDEN; 562f127cb91Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL)) 563f127cb91Sfrankho fsp->pcfs_flags |= PCFS_FOLDCASE; 564f127cb91Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL)) 565f127cb91Sfrankho fsp->pcfs_flags |= PCFS_NOCLAMPTIME; 566f127cb91Sfrankho if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL)) 567f127cb91Sfrankho fsp->pcfs_flags |= PCFS_NOATIME; 568f127cb91Sfrankho 569f127cb91Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_TIMEZONE, &c)) { 570f127cb91Sfrankho if (ddi_strtol(c, &endptr, 10, &l) == 0 && 571f127cb91Sfrankho endptr == c + strlen(c)) { 572f127cb91Sfrankho /* 573f127cb91Sfrankho * A number alright - in the allowed range ? 574f127cb91Sfrankho */ 575f127cb91Sfrankho if (l <= -12*3600 || l >= 12*3600) { 576f127cb91Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of " 577f127cb91Sfrankho "'timezone' mount option - %ld " 578f127cb91Sfrankho "is out of range. Assuming 0.", l); 579f127cb91Sfrankho l = 0; 580f127cb91Sfrankho } 581f127cb91Sfrankho } else { 582f127cb91Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of " 583f127cb91Sfrankho "'timezone' mount option - argument %s " 584f127cb91Sfrankho "is not a valid number. Assuming 0.", c); 585f127cb91Sfrankho l = 0; 586f127cb91Sfrankho } 587f127cb91Sfrankho fsp->pcfs_secondswest = l; 588f127cb91Sfrankho } 589f127cb91Sfrankho 5907c478bd9Sstevel@tonic-gate /* 591f127cb91Sfrankho * The "secsize=..." mount option is a workaround for the lack of 592f127cb91Sfrankho * lofi(7d) support for DKIOCGMEDIAINFO. If PCFS wants to parse the 593f127cb91Sfrankho * partition table of a disk image and it has been partitioned with 594f127cb91Sfrankho * sector sizes other than 512 bytes, we'd fail on loopback'ed disk 595f127cb91Sfrankho * images. 596f127cb91Sfrankho * That should really be fixed in lofi ... this is a workaround. 5977c478bd9Sstevel@tonic-gate */ 598f127cb91Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_SECSIZE, &c)) { 599f127cb91Sfrankho if (ddi_strtol(c, &endptr, 10, &l) == 0 && 600f127cb91Sfrankho endptr == c + strlen(c)) { 601f127cb91Sfrankho /* 602f127cb91Sfrankho * A number alright - a valid sector size as well ? 603f127cb91Sfrankho */ 604f127cb91Sfrankho if (!VALID_SECSIZE(l)) { 605f127cb91Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of " 606f127cb91Sfrankho "'secsize' mount option - %ld is " 607f127cb91Sfrankho "unsupported. Autodetecting.", l); 608f127cb91Sfrankho l = 0; 609f127cb91Sfrankho } 610f127cb91Sfrankho } else { 611f127cb91Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of " 612f127cb91Sfrankho "'secsize' mount option - argument %s " 613f127cb91Sfrankho "is not a valid number. Autodetecting.", c); 614f127cb91Sfrankho l = 0; 615f127cb91Sfrankho } 616f127cb91Sfrankho fsp->pcfs_secsize = l; 617f127cb91Sfrankho fsp->pcfs_sdshift = ddi_ffs(l / DEV_BSIZE) - 1; 618f127cb91Sfrankho } 619f127cb91Sfrankho } 620f127cb91Sfrankho 621f127cb91Sfrankho /* 622f127cb91Sfrankho * vfs operations 623f127cb91Sfrankho */ 624f127cb91Sfrankho 625f127cb91Sfrankho /* 626f127cb91Sfrankho * pcfs_mount - backend for VFS_MOUNT() on PCFS. 627f127cb91Sfrankho */ 628f127cb91Sfrankho static int 629f127cb91Sfrankho pcfs_mount( 630f127cb91Sfrankho struct vfs *vfsp, 631f127cb91Sfrankho struct vnode *mvp, 632f127cb91Sfrankho struct mounta *uap, 633f127cb91Sfrankho struct cred *cr) 634f127cb91Sfrankho { 635f127cb91Sfrankho struct pcfs *fsp; 636f127cb91Sfrankho struct vnode *devvp; 637f127cb91Sfrankho dev_t pseudodev; 638f127cb91Sfrankho dev_t xdev; 639f127cb91Sfrankho int dos_ldrive = 0; 640f127cb91Sfrankho int error; 641f127cb91Sfrankho int remounting; 642f127cb91Sfrankho 643f127cb91Sfrankho if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0) 644f127cb91Sfrankho return (error); 645f127cb91Sfrankho 646f127cb91Sfrankho if (mvp->v_type != VDIR) 647f127cb91Sfrankho return (ENOTDIR); 648f127cb91Sfrankho 649f127cb91Sfrankho mutex_enter(&mvp->v_lock); 650f127cb91Sfrankho if ((uap->flags & MS_REMOUNT) == 0 && 651f127cb91Sfrankho (uap->flags & MS_OVERLAY) == 0 && 652f127cb91Sfrankho (mvp->v_count != 1 || (mvp->v_flag & VROOT))) { 653f127cb91Sfrankho mutex_exit(&mvp->v_lock); 6547c478bd9Sstevel@tonic-gate return (EBUSY); 6557c478bd9Sstevel@tonic-gate } 656f127cb91Sfrankho mutex_exit(&mvp->v_lock); 6577c478bd9Sstevel@tonic-gate 6587c478bd9Sstevel@tonic-gate /* 659f127cb91Sfrankho * PCFS doesn't do mount arguments anymore - everything's a mount 660f127cb91Sfrankho * option these days. In order not to break existing callers, we 661f127cb91Sfrankho * don't reject it yet, just warn that the data (if any) is ignored. 6627c478bd9Sstevel@tonic-gate */ 663f127cb91Sfrankho if (uap->datalen != 0) 664f127cb91Sfrankho cmn_err(CE_WARN, "!pcfs: deprecated use of mount(2) with " 665f127cb91Sfrankho "mount argument structures instead of mount options. " 666f127cb91Sfrankho "Ignoring mount(2) 'dataptr' argument."); 667f127cb91Sfrankho 668c2aaf90fSgd /* 669c2aaf90fSgd * This is needed early, to make sure the access / open calls 670c2aaf90fSgd * are done using the correct mode. Processing this mount option 671c2aaf90fSgd * only when calling pcfs_parse_mntopts() would lead us to attempt 672c2aaf90fSgd * a read/write access to a possibly writeprotected device, and 673c2aaf90fSgd * a readonly mount attempt might fail because of that. 674c2aaf90fSgd */ 675c2aaf90fSgd if (uap->flags & MS_RDONLY) { 676c2aaf90fSgd vfsp->vfs_flag |= VFS_RDONLY; 677c2aaf90fSgd vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0); 678c2aaf90fSgd } 679c2aaf90fSgd 680f127cb91Sfrankho /* 681f127cb91Sfrankho * For most filesystems, this is just a lookupname() on the 682f127cb91Sfrankho * mount pathname string. PCFS historically has to do its own 683f127cb91Sfrankho * partition table parsing because not all Solaris architectures 684f127cb91Sfrankho * support all styles of partitioning that PC media can have, and 685f127cb91Sfrankho * hence PCFS understands "device names" that don't map to actual 686f127cb91Sfrankho * physical device nodes. Parsing the "PCFS syntax" for device 687f127cb91Sfrankho * names is done in pcfs_device_identify() - see there. 688f127cb91Sfrankho * 689f127cb91Sfrankho * Once all block device drivers that can host FAT filesystems have 690f127cb91Sfrankho * been enhanced to create device nodes for all PC-style partitions, 691f127cb91Sfrankho * this code can go away. 692f127cb91Sfrankho */ 693f127cb91Sfrankho if (error = pcfs_device_identify(vfsp, uap, cr, &dos_ldrive, &xdev)) 694f127cb91Sfrankho return (error); 695f127cb91Sfrankho 696f127cb91Sfrankho /* 697f127cb91Sfrankho * As with looking up the actual device to mount, PCFS cannot rely 698f127cb91Sfrankho * on just the checks done by vfs_ismounted() whether a given device 699f127cb91Sfrankho * is mounted already. The additional check against the "PCFS syntax" 700f127cb91Sfrankho * is done in pcfs_device_ismounted(). 701f127cb91Sfrankho */ 702f127cb91Sfrankho remounting = (uap->flags & MS_REMOUNT); 703f127cb91Sfrankho 704f127cb91Sfrankho if (error = pcfs_device_ismounted(vfsp, dos_ldrive, xdev, &remounting, 705f127cb91Sfrankho &pseudodev)) 706f127cb91Sfrankho return (error); 707f127cb91Sfrankho 708f127cb91Sfrankho if (remounting) 709f127cb91Sfrankho return (0); 710f127cb91Sfrankho 711f127cb91Sfrankho /* 712f127cb91Sfrankho * Mount the filesystem. 713f127cb91Sfrankho * An instance structure is required before the attempt to locate 714f127cb91Sfrankho * and parse the FAT BPB. This is because mount options may change 715f127cb91Sfrankho * the behaviour of the filesystem type matching code. Precreate 716f127cb91Sfrankho * it and fill it in to a degree that allows parsing the mount 717f127cb91Sfrankho * options. 718f127cb91Sfrankho */ 719f127cb91Sfrankho devvp = makespecvp(xdev, VBLK); 720f127cb91Sfrankho if (IS_SWAPVP(devvp)) { 721f127cb91Sfrankho VN_RELE(devvp); 722f127cb91Sfrankho return (EBUSY); 723f127cb91Sfrankho } 724f127cb91Sfrankho error = VOP_OPEN(&devvp, 725da6c28aaSamw (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD | FWRITE, cr, NULL); 726f127cb91Sfrankho if (error) { 727f127cb91Sfrankho VN_RELE(devvp); 728f127cb91Sfrankho return (error); 7297c478bd9Sstevel@tonic-gate } 7307c478bd9Sstevel@tonic-gate 731f127cb91Sfrankho fsp = kmem_zalloc(sizeof (*fsp), KM_SLEEP); 7327c478bd9Sstevel@tonic-gate fsp->pcfs_vfs = vfsp; 7337c478bd9Sstevel@tonic-gate fsp->pcfs_xdev = xdev; 734f127cb91Sfrankho fsp->pcfs_devvp = devvp; 735f127cb91Sfrankho fsp->pcfs_ldrive = dos_ldrive; 7367c478bd9Sstevel@tonic-gate mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL); 7377c478bd9Sstevel@tonic-gate 7389e003ac4Sgd pcfs_parse_mntopts(fsp); 739f127cb91Sfrankho 740f127cb91Sfrankho /* 741f127cb91Sfrankho * This is the actual "mount" - the PCFS superblock check. 742f127cb91Sfrankho * 743f127cb91Sfrankho * Find the requested logical drive and the FAT BPB therein. 744f127cb91Sfrankho * Check device type and flag the instance if media is removeable. 745f127cb91Sfrankho * 746f127cb91Sfrankho * Initializes most members of the filesystem instance structure. 747f127cb91Sfrankho * Returns EINVAL if no valid BPB can be found. Other errors may 748f127cb91Sfrankho * occur after I/O failures, or when invalid / unparseable partition 749f127cb91Sfrankho * tables are encountered. 750f127cb91Sfrankho */ 751f127cb91Sfrankho if (error = pc_getfattype(fsp)) 752f127cb91Sfrankho goto errout; 753f127cb91Sfrankho 754c2aaf90fSgd /* 755c2aaf90fSgd * Now that the BPB has been parsed, this structural information 756c2aaf90fSgd * is available and known to be valid. Initialize the VFS. 757c2aaf90fSgd */ 758c2aaf90fSgd vfsp->vfs_data = fsp; 759c2aaf90fSgd vfsp->vfs_dev = pseudodev; 760c2aaf90fSgd vfsp->vfs_fstype = pcfstype; 761c2aaf90fSgd vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype); 762c2aaf90fSgd vfsp->vfs_bcount = 0; 763c2aaf90fSgd vfsp->vfs_bsize = fsp->pcfs_clsize; 764c2aaf90fSgd 765f127cb91Sfrankho /* 766f127cb91Sfrankho * Validate that we can access the FAT and that it is, to the 767f127cb91Sfrankho * degree we can verify here, self-consistent. 768f127cb91Sfrankho */ 769f127cb91Sfrankho if (error = pc_verify(fsp)) 770f127cb91Sfrankho goto errout; 771f127cb91Sfrankho 772f127cb91Sfrankho /* 773f127cb91Sfrankho * Record the time of the mount, to return as an "approximate" 774f127cb91Sfrankho * timestamp for the FAT root directory. Since FAT roots don't 775f127cb91Sfrankho * have timestamps, this is less confusing to the user than 776f127cb91Sfrankho * claiming "zero" / Jan/01/1970. 777f127cb91Sfrankho */ 778f127cb91Sfrankho gethrestime(&fsp->pcfs_mounttime); 779f127cb91Sfrankho 780f127cb91Sfrankho /* 781f127cb91Sfrankho * Fix up the mount options. Because "noatime" is made default on 782f127cb91Sfrankho * removeable media only, a fixed disk will have neither "atime" 783f127cb91Sfrankho * nor "noatime" set. We set the options explicitly depending on 784f127cb91Sfrankho * the PCFS_NOATIME flag, to inform the user of what applies. 785f127cb91Sfrankho * Mount option cancellation will take care that the mutually 786f127cb91Sfrankho * exclusive 'other' is cleared. 787f127cb91Sfrankho */ 788f127cb91Sfrankho vfs_setmntopt(vfsp, 789f127cb91Sfrankho fsp->pcfs_flags & PCFS_NOATIME ? MNTOPT_NOATIME : MNTOPT_ATIME, 790f127cb91Sfrankho NULL, 0); 791f127cb91Sfrankho 792f127cb91Sfrankho /* 793f127cb91Sfrankho * All clear - insert the FS instance into PCFS' list. 794f127cb91Sfrankho */ 7957c478bd9Sstevel@tonic-gate mutex_enter(&pcfslock); 7967c478bd9Sstevel@tonic-gate fsp->pcfs_nxt = pc_mounttab; 7977c478bd9Sstevel@tonic-gate pc_mounttab = fsp; 7987c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock); 799264a6e74Sfrankho atomic_inc_32(&pcfs_mountcount); 8007c478bd9Sstevel@tonic-gate return (0); 8017c478bd9Sstevel@tonic-gate 802f127cb91Sfrankho errout: 803f127cb91Sfrankho (void) VOP_CLOSE(devvp, 804f127cb91Sfrankho vfsp->vfs_flag & VFS_RDONLY ? FREAD : FREAD | FWRITE, 805da6c28aaSamw 1, (offset_t)0, cr, NULL); 806f127cb91Sfrankho VN_RELE(devvp); 807f127cb91Sfrankho mutex_destroy(&fsp->pcfs_lock); 808f127cb91Sfrankho kmem_free(fsp, sizeof (*fsp)); 809f127cb91Sfrankho return (error); 810f127cb91Sfrankho 811f127cb91Sfrankho } 8127c478bd9Sstevel@tonic-gate 8137c478bd9Sstevel@tonic-gate static int 8147c478bd9Sstevel@tonic-gate pcfs_unmount( 8157c478bd9Sstevel@tonic-gate struct vfs *vfsp, 8167c478bd9Sstevel@tonic-gate int flag, 8177c478bd9Sstevel@tonic-gate struct cred *cr) 8187c478bd9Sstevel@tonic-gate { 8197c478bd9Sstevel@tonic-gate struct pcfs *fsp, *fsp1; 8207c478bd9Sstevel@tonic-gate 8217c478bd9Sstevel@tonic-gate if (secpolicy_fs_unmount(cr, vfsp) != 0) 8227c478bd9Sstevel@tonic-gate return (EPERM); 8237c478bd9Sstevel@tonic-gate 8247c478bd9Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 825264a6e74Sfrankho 8267c478bd9Sstevel@tonic-gate /* 8277c478bd9Sstevel@tonic-gate * We don't have to lock fsp because the VVFSLOCK in vfs layer will 8287c478bd9Sstevel@tonic-gate * prevent lookuppn from crossing the mount point. 829264a6e74Sfrankho * If this is not a forced umount request and there's ongoing I/O, 830264a6e74Sfrankho * don't allow the mount to proceed. 8317c478bd9Sstevel@tonic-gate */ 832264a6e74Sfrankho if (flag & MS_FORCE) 833264a6e74Sfrankho vfsp->vfs_flag |= VFS_UNMOUNTED; 834264a6e74Sfrankho else if (fsp->pcfs_nrefs) 8357c478bd9Sstevel@tonic-gate return (EBUSY); 836264a6e74Sfrankho 837264a6e74Sfrankho mutex_enter(&pcfslock); 8387c478bd9Sstevel@tonic-gate 8397c478bd9Sstevel@tonic-gate /* 840264a6e74Sfrankho * If this is a forced umount request or if the fs instance has 841264a6e74Sfrankho * been marked as beyond recovery, allow the umount to proceed 842264a6e74Sfrankho * regardless of state. pc_diskchanged() forcibly releases all 843264a6e74Sfrankho * inactive vnodes/pcnodes. 8447c478bd9Sstevel@tonic-gate */ 845264a6e74Sfrankho if (flag & MS_FORCE || fsp->pcfs_flags & PCFS_IRRECOV) { 8467c478bd9Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_WRITER); 8477c478bd9Sstevel@tonic-gate pc_diskchanged(fsp); 8487c478bd9Sstevel@tonic-gate rw_exit(&pcnodes_lock); 8497c478bd9Sstevel@tonic-gate } 8507c478bd9Sstevel@tonic-gate 8517c478bd9Sstevel@tonic-gate /* now there should be no pcp node on pcfhead or pcdhead. */ 8527c478bd9Sstevel@tonic-gate 8537c478bd9Sstevel@tonic-gate if (fsp == pc_mounttab) { 8547c478bd9Sstevel@tonic-gate pc_mounttab = fsp->pcfs_nxt; 8557c478bd9Sstevel@tonic-gate } else { 8567c478bd9Sstevel@tonic-gate for (fsp1 = pc_mounttab; fsp1 != NULL; fsp1 = fsp1->pcfs_nxt) 8577c478bd9Sstevel@tonic-gate if (fsp1->pcfs_nxt == fsp) 8587c478bd9Sstevel@tonic-gate fsp1->pcfs_nxt = fsp->pcfs_nxt; 8597c478bd9Sstevel@tonic-gate } 8607c478bd9Sstevel@tonic-gate 8617c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock); 8627c478bd9Sstevel@tonic-gate 863264a6e74Sfrankho /* 864264a6e74Sfrankho * Since we support VFS_FREEVFS(), there's no need to 865264a6e74Sfrankho * free the fsp right now. The framework will tell us 866264a6e74Sfrankho * when the right time to do so has arrived by calling 867264a6e74Sfrankho * into pcfs_freevfs. 868264a6e74Sfrankho */ 8697c478bd9Sstevel@tonic-gate return (0); 8707c478bd9Sstevel@tonic-gate } 8717c478bd9Sstevel@tonic-gate 8727c478bd9Sstevel@tonic-gate /* 8737c478bd9Sstevel@tonic-gate * find root of pcfs 8747c478bd9Sstevel@tonic-gate */ 8757c478bd9Sstevel@tonic-gate static int 8767c478bd9Sstevel@tonic-gate pcfs_root( 8777c478bd9Sstevel@tonic-gate struct vfs *vfsp, 8787c478bd9Sstevel@tonic-gate struct vnode **vpp) 8797c478bd9Sstevel@tonic-gate { 8807c478bd9Sstevel@tonic-gate struct pcfs *fsp; 8817c478bd9Sstevel@tonic-gate struct pcnode *pcp; 8827c478bd9Sstevel@tonic-gate int error; 8837c478bd9Sstevel@tonic-gate 8847c478bd9Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 8857c478bd9Sstevel@tonic-gate if (error = pc_lockfs(fsp, 0, 0)) 8867c478bd9Sstevel@tonic-gate return (error); 887264a6e74Sfrankho 8887c478bd9Sstevel@tonic-gate pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0); 8897c478bd9Sstevel@tonic-gate pc_unlockfs(fsp); 8907c478bd9Sstevel@tonic-gate *vpp = PCTOV(pcp); 8917c478bd9Sstevel@tonic-gate pcp->pc_flags |= PC_EXTERNAL; 8927c478bd9Sstevel@tonic-gate return (0); 8937c478bd9Sstevel@tonic-gate } 8947c478bd9Sstevel@tonic-gate 8957c478bd9Sstevel@tonic-gate /* 8967c478bd9Sstevel@tonic-gate * Get file system statistics. 8977c478bd9Sstevel@tonic-gate */ 8987c478bd9Sstevel@tonic-gate static int 8997c478bd9Sstevel@tonic-gate pcfs_statvfs( 9007c478bd9Sstevel@tonic-gate struct vfs *vfsp, 9017c478bd9Sstevel@tonic-gate struct statvfs64 *sp) 9027c478bd9Sstevel@tonic-gate { 9037c478bd9Sstevel@tonic-gate struct pcfs *fsp; 9047c478bd9Sstevel@tonic-gate int error; 9057c478bd9Sstevel@tonic-gate dev32_t d32; 9067c478bd9Sstevel@tonic-gate 9077c478bd9Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 9087c478bd9Sstevel@tonic-gate error = pc_getfat(fsp); 9097c478bd9Sstevel@tonic-gate if (error) 9107c478bd9Sstevel@tonic-gate return (error); 9117c478bd9Sstevel@tonic-gate bzero(sp, sizeof (*sp)); 9127c478bd9Sstevel@tonic-gate sp->f_bsize = sp->f_frsize = fsp->pcfs_clsize; 9137c478bd9Sstevel@tonic-gate sp->f_blocks = (fsblkcnt64_t)fsp->pcfs_ncluster; 9147c478bd9Sstevel@tonic-gate sp->f_bavail = sp->f_bfree = (fsblkcnt64_t)pc_freeclusters(fsp); 9157c478bd9Sstevel@tonic-gate sp->f_files = (fsfilcnt64_t)-1; 9167c478bd9Sstevel@tonic-gate sp->f_ffree = (fsfilcnt64_t)-1; 9177c478bd9Sstevel@tonic-gate sp->f_favail = (fsfilcnt64_t)-1; 9187c478bd9Sstevel@tonic-gate #ifdef notdef 9197c478bd9Sstevel@tonic-gate (void) cmpldev(&d32, fsp->pcfs_devvp->v_rdev); 9207c478bd9Sstevel@tonic-gate #endif /* notdef */ 9217c478bd9Sstevel@tonic-gate (void) cmpldev(&d32, vfsp->vfs_dev); 9227c478bd9Sstevel@tonic-gate sp->f_fsid = d32; 9237c478bd9Sstevel@tonic-gate (void) strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name); 9247c478bd9Sstevel@tonic-gate sp->f_flag = vf_to_stf(vfsp->vfs_flag); 925e8f766d8Sbatschul sp->f_namemax = PCMAXNAMLEN; 9267c478bd9Sstevel@tonic-gate return (0); 9277c478bd9Sstevel@tonic-gate } 9287c478bd9Sstevel@tonic-gate 9297c478bd9Sstevel@tonic-gate static int 9307c478bd9Sstevel@tonic-gate pc_syncfsnodes(struct pcfs *fsp) 9317c478bd9Sstevel@tonic-gate { 9327c478bd9Sstevel@tonic-gate struct pchead *hp; 9337c478bd9Sstevel@tonic-gate struct pcnode *pcp; 9347c478bd9Sstevel@tonic-gate int error; 9357c478bd9Sstevel@tonic-gate 9367c478bd9Sstevel@tonic-gate if (error = pc_lockfs(fsp, 0, 0)) 9377c478bd9Sstevel@tonic-gate return (error); 9387c478bd9Sstevel@tonic-gate 9397c478bd9Sstevel@tonic-gate if (!(error = pc_syncfat(fsp))) { 9407c478bd9Sstevel@tonic-gate hp = pcfhead; 9417c478bd9Sstevel@tonic-gate while (hp < & pcfhead [ NPCHASH ]) { 9427c478bd9Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_READER); 9437c478bd9Sstevel@tonic-gate pcp = hp->pch_forw; 9447c478bd9Sstevel@tonic-gate while (pcp != (struct pcnode *)hp) { 9457c478bd9Sstevel@tonic-gate if (VFSTOPCFS(PCTOV(pcp) -> v_vfsp) == fsp) 9467c478bd9Sstevel@tonic-gate if (error = pc_nodesync(pcp)) 9477c478bd9Sstevel@tonic-gate break; 9487c478bd9Sstevel@tonic-gate pcp = pcp -> pc_forw; 9497c478bd9Sstevel@tonic-gate } 9507c478bd9Sstevel@tonic-gate rw_exit(&pcnodes_lock); 9517c478bd9Sstevel@tonic-gate if (error) 9527c478bd9Sstevel@tonic-gate break; 9537c478bd9Sstevel@tonic-gate hp++; 9547c478bd9Sstevel@tonic-gate } 9557c478bd9Sstevel@tonic-gate } 9567c478bd9Sstevel@tonic-gate pc_unlockfs(fsp); 9577c478bd9Sstevel@tonic-gate return (error); 9587c478bd9Sstevel@tonic-gate } 9597c478bd9Sstevel@tonic-gate 9607c478bd9Sstevel@tonic-gate /* 9617c478bd9Sstevel@tonic-gate * Flush any pending I/O. 9627c478bd9Sstevel@tonic-gate */ 9637c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 9647c478bd9Sstevel@tonic-gate static int 9657c478bd9Sstevel@tonic-gate pcfs_sync( 9667c478bd9Sstevel@tonic-gate struct vfs *vfsp, 9677c478bd9Sstevel@tonic-gate short flag, 9687c478bd9Sstevel@tonic-gate struct cred *cr) 9697c478bd9Sstevel@tonic-gate { 9707c478bd9Sstevel@tonic-gate struct pcfs *fsp; 9717c478bd9Sstevel@tonic-gate int error = 0; 9727c478bd9Sstevel@tonic-gate 9737c478bd9Sstevel@tonic-gate /* this prevents the filesystem from being umounted. */ 9747c478bd9Sstevel@tonic-gate mutex_enter(&pcfslock); 9757c478bd9Sstevel@tonic-gate if (vfsp != NULL) { 9767c478bd9Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 9777c478bd9Sstevel@tonic-gate if (!(fsp->pcfs_flags & PCFS_IRRECOV)) { 9787c478bd9Sstevel@tonic-gate error = pc_syncfsnodes(fsp); 9797c478bd9Sstevel@tonic-gate } else { 9807c478bd9Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_WRITER); 9817c478bd9Sstevel@tonic-gate pc_diskchanged(fsp); 9827c478bd9Sstevel@tonic-gate rw_exit(&pcnodes_lock); 9837c478bd9Sstevel@tonic-gate error = EIO; 9847c478bd9Sstevel@tonic-gate } 9857c478bd9Sstevel@tonic-gate } else { 9867c478bd9Sstevel@tonic-gate fsp = pc_mounttab; 9877c478bd9Sstevel@tonic-gate while (fsp != NULL) { 9887c478bd9Sstevel@tonic-gate if (fsp->pcfs_flags & PCFS_IRRECOV) { 9897c478bd9Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_WRITER); 9907c478bd9Sstevel@tonic-gate pc_diskchanged(fsp); 9917c478bd9Sstevel@tonic-gate rw_exit(&pcnodes_lock); 9927c478bd9Sstevel@tonic-gate error = EIO; 9937c478bd9Sstevel@tonic-gate break; 9947c478bd9Sstevel@tonic-gate } 9957c478bd9Sstevel@tonic-gate error = pc_syncfsnodes(fsp); 9967c478bd9Sstevel@tonic-gate if (error) break; 9977c478bd9Sstevel@tonic-gate fsp = fsp->pcfs_nxt; 9987c478bd9Sstevel@tonic-gate } 9997c478bd9Sstevel@tonic-gate } 10007c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock); 10017c478bd9Sstevel@tonic-gate return (error); 10027c478bd9Sstevel@tonic-gate } 10037c478bd9Sstevel@tonic-gate 10047c478bd9Sstevel@tonic-gate int 10057c478bd9Sstevel@tonic-gate pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing) 10067c478bd9Sstevel@tonic-gate { 1007264a6e74Sfrankho int err; 1008264a6e74Sfrankho 10097c478bd9Sstevel@tonic-gate if ((fsp->pcfs_flags & PCFS_IRRECOV) && !releasing) 10107c478bd9Sstevel@tonic-gate return (EIO); 10117c478bd9Sstevel@tonic-gate 10127c478bd9Sstevel@tonic-gate if ((fsp->pcfs_flags & PCFS_LOCKED) && (fsp->pcfs_owner == curthread)) { 10137c478bd9Sstevel@tonic-gate fsp->pcfs_count++; 10147c478bd9Sstevel@tonic-gate } else { 10157c478bd9Sstevel@tonic-gate mutex_enter(&fsp->pcfs_lock); 10167c478bd9Sstevel@tonic-gate if (fsp->pcfs_flags & PCFS_LOCKED) 10177c478bd9Sstevel@tonic-gate panic("pc_lockfs"); 10187c478bd9Sstevel@tonic-gate /* 10197c478bd9Sstevel@tonic-gate * We check the IRRECOV bit again just in case somebody 10207c478bd9Sstevel@tonic-gate * snuck past the initial check but then got held up before 10217c478bd9Sstevel@tonic-gate * they could grab the lock. (And in the meantime someone 10227c478bd9Sstevel@tonic-gate * had grabbed the lock and set the bit) 10237c478bd9Sstevel@tonic-gate */ 10240576819eSwyllys if (!diskchanged && !(fsp->pcfs_flags & PCFS_IRRECOV)) { 1025264a6e74Sfrankho if ((err = pc_getfat(fsp))) { 1026264a6e74Sfrankho mutex_exit(&fsp->pcfs_lock); 10270576819eSwyllys return (err); 1028264a6e74Sfrankho } 10290576819eSwyllys } 10307c478bd9Sstevel@tonic-gate fsp->pcfs_flags |= PCFS_LOCKED; 10317c478bd9Sstevel@tonic-gate fsp->pcfs_owner = curthread; 10327c478bd9Sstevel@tonic-gate fsp->pcfs_count++; 10337c478bd9Sstevel@tonic-gate } 10347c478bd9Sstevel@tonic-gate return (0); 10357c478bd9Sstevel@tonic-gate } 10367c478bd9Sstevel@tonic-gate 10377c478bd9Sstevel@tonic-gate void 10387c478bd9Sstevel@tonic-gate pc_unlockfs(struct pcfs *fsp) 10397c478bd9Sstevel@tonic-gate { 10407c478bd9Sstevel@tonic-gate 10417c478bd9Sstevel@tonic-gate if ((fsp->pcfs_flags & PCFS_LOCKED) == 0) 10427c478bd9Sstevel@tonic-gate panic("pc_unlockfs"); 10437c478bd9Sstevel@tonic-gate if (--fsp->pcfs_count < 0) 10447c478bd9Sstevel@tonic-gate panic("pc_unlockfs: count"); 10457c478bd9Sstevel@tonic-gate if (fsp->pcfs_count == 0) { 10467c478bd9Sstevel@tonic-gate fsp->pcfs_flags &= ~PCFS_LOCKED; 10477c478bd9Sstevel@tonic-gate fsp->pcfs_owner = 0; 10487c478bd9Sstevel@tonic-gate mutex_exit(&fsp->pcfs_lock); 10497c478bd9Sstevel@tonic-gate } 10507c478bd9Sstevel@tonic-gate } 10517c478bd9Sstevel@tonic-gate 1052f127cb91Sfrankho int 1053f127cb91Sfrankho pc_syncfat(struct pcfs *fsp) 10547c478bd9Sstevel@tonic-gate { 1055f127cb91Sfrankho struct buf *bp; 1056f127cb91Sfrankho int nfat; 1057f127cb91Sfrankho int error = 0; 1058f127cb91Sfrankho struct fat_od_fsi *fsinfo_disk; 10597c478bd9Sstevel@tonic-gate 1060f127cb91Sfrankho if ((fsp->pcfs_fatp == (uchar_t *)0) || 1061f127cb91Sfrankho !(fsp->pcfs_flags & PCFS_FATMOD)) 1062f127cb91Sfrankho return (0); 1063f127cb91Sfrankho /* 1064f127cb91Sfrankho * write out all copies of FATs 1065f127cb91Sfrankho */ 1066f127cb91Sfrankho fsp->pcfs_flags &= ~PCFS_FATMOD; 1067f127cb91Sfrankho fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT; 1068f127cb91Sfrankho for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) { 1069f127cb91Sfrankho error = pc_writefat(fsp, pc_dbdaddr(fsp, 1070f127cb91Sfrankho fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec)); 1071f127cb91Sfrankho if (error) { 1072f127cb91Sfrankho pc_mark_irrecov(fsp); 1073f127cb91Sfrankho return (EIO); 1074f127cb91Sfrankho } 1075f127cb91Sfrankho } 1076f127cb91Sfrankho pc_clear_fatchanges(fsp); 10777c478bd9Sstevel@tonic-gate 1078f127cb91Sfrankho /* 1079f127cb91Sfrankho * Write out fsinfo sector. 1080f127cb91Sfrankho */ 1081f127cb91Sfrankho if (IS_FAT32(fsp)) { 1082f127cb91Sfrankho bp = bread(fsp->pcfs_xdev, 1083f127cb91Sfrankho pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize); 1084f127cb91Sfrankho if (bp->b_flags & (B_ERROR | B_STALE)) { 1085f127cb91Sfrankho error = geterror(bp); 1086f127cb91Sfrankho } 1087f127cb91Sfrankho fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr); 1088f127cb91Sfrankho if (!error && FSISIG_OK(fsinfo_disk)) { 1089f127cb91Sfrankho fsinfo_disk->fsi_incore.fs_free_clusters = 1090f127cb91Sfrankho LE_32(fsp->pcfs_fsinfo.fs_free_clusters); 1091f127cb91Sfrankho fsinfo_disk->fsi_incore.fs_next_free = 1092f127cb91Sfrankho LE_32(FSINFO_UNKNOWN); 1093f127cb91Sfrankho bwrite2(bp); 1094f127cb91Sfrankho error = geterror(bp); 1095f127cb91Sfrankho } 1096f127cb91Sfrankho brelse(bp); 1097f127cb91Sfrankho if (error) { 1098f127cb91Sfrankho pc_mark_irrecov(fsp); 1099f127cb91Sfrankho return (EIO); 1100f127cb91Sfrankho } 1101f127cb91Sfrankho } 1102f127cb91Sfrankho return (0); 1103f127cb91Sfrankho } 1104f127cb91Sfrankho 1105f127cb91Sfrankho void 1106f127cb91Sfrankho pc_invalfat(struct pcfs *fsp) 1107f127cb91Sfrankho { 1108f127cb91Sfrankho struct pcfs *xfsp; 1109f127cb91Sfrankho int mount_cnt = 0; 1110f127cb91Sfrankho 1111f127cb91Sfrankho if (fsp->pcfs_fatp == (uchar_t *)0) 1112f127cb91Sfrankho panic("pc_invalfat"); 1113f127cb91Sfrankho /* 1114f127cb91Sfrankho * Release FAT 1115f127cb91Sfrankho */ 1116f127cb91Sfrankho kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsec * fsp->pcfs_secsize); 1117f127cb91Sfrankho fsp->pcfs_fatp = NULL; 1118f127cb91Sfrankho kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize); 1119f127cb91Sfrankho fsp->pcfs_fat_changemap = NULL; 1120f127cb91Sfrankho /* 1121f127cb91Sfrankho * Invalidate all the blocks associated with the device. 1122f127cb91Sfrankho * Not needed if stateless. 1123f127cb91Sfrankho */ 1124f127cb91Sfrankho for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt) 1125f127cb91Sfrankho if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev) 1126f127cb91Sfrankho mount_cnt++; 1127f127cb91Sfrankho 1128f127cb91Sfrankho if (!mount_cnt) 1129f127cb91Sfrankho binval(fsp->pcfs_xdev); 1130f127cb91Sfrankho /* 1131f127cb91Sfrankho * close mounted device 1132f127cb91Sfrankho */ 1133f127cb91Sfrankho (void) VOP_CLOSE(fsp->pcfs_devvp, 1134f127cb91Sfrankho (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, 1135da6c28aaSamw 1, (offset_t)0, CRED(), NULL); 1136f127cb91Sfrankho } 1137f127cb91Sfrankho 1138f127cb91Sfrankho void 1139f127cb91Sfrankho pc_badfs(struct pcfs *fsp) 1140f127cb91Sfrankho { 1141f127cb91Sfrankho cmn_err(CE_WARN, "corrupted PC file system on dev (%x.%x):%d\n", 1142f127cb91Sfrankho getmajor(fsp->pcfs_devvp->v_rdev), 1143f127cb91Sfrankho getminor(fsp->pcfs_devvp->v_rdev), fsp->pcfs_ldrive); 1144f127cb91Sfrankho } 1145f127cb91Sfrankho 1146f127cb91Sfrankho /* 1147f127cb91Sfrankho * The problem with supporting NFS on the PCFS filesystem is that there 1148f127cb91Sfrankho * is no good place to keep the generation number. The only possible 1149f127cb91Sfrankho * place is inside a directory entry. There are a few words that we 1150f127cb91Sfrankho * don't use - they store NT & OS/2 attributes, and the creation/last access 1151f127cb91Sfrankho * time of the file - but it seems wrong to use them. In addition, directory 1152f127cb91Sfrankho * entries come and go. If a directory is removed completely, its directory 1153f127cb91Sfrankho * blocks are freed and the generation numbers are lost. Whereas in ufs, 1154f127cb91Sfrankho * inode blocks are dedicated for inodes, so the generation numbers are 1155f127cb91Sfrankho * permanently kept on the disk. 1156f127cb91Sfrankho */ 1157f127cb91Sfrankho static int 1158f127cb91Sfrankho pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp) 1159f127cb91Sfrankho { 1160f127cb91Sfrankho struct pcnode *pcp; 1161f127cb91Sfrankho struct pc_fid *pcfid; 1162f127cb91Sfrankho struct pcfs *fsp; 1163f127cb91Sfrankho struct pcdir *ep; 1164f127cb91Sfrankho daddr_t eblkno; 1165f127cb91Sfrankho int eoffset; 1166f127cb91Sfrankho struct buf *bp; 1167f127cb91Sfrankho int error; 1168f127cb91Sfrankho pc_cluster32_t cn; 1169f127cb91Sfrankho 1170f127cb91Sfrankho pcfid = (struct pc_fid *)fidp; 1171f127cb91Sfrankho fsp = VFSTOPCFS(vfsp); 1172f127cb91Sfrankho 1173f127cb91Sfrankho error = pc_lockfs(fsp, 0, 0); 1174f127cb91Sfrankho if (error) { 1175f127cb91Sfrankho *vpp = NULL; 1176f127cb91Sfrankho return (error); 1177f127cb91Sfrankho } 1178f127cb91Sfrankho 1179f127cb91Sfrankho if (pcfid->pcfid_block == 0) { 1180f127cb91Sfrankho pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0); 1181f127cb91Sfrankho pcp->pc_flags |= PC_EXTERNAL; 1182f127cb91Sfrankho *vpp = PCTOV(pcp); 1183f127cb91Sfrankho pc_unlockfs(fsp); 1184f127cb91Sfrankho return (0); 1185f127cb91Sfrankho } 1186f127cb91Sfrankho eblkno = pcfid->pcfid_block; 1187f127cb91Sfrankho eoffset = pcfid->pcfid_offset; 1188f127cb91Sfrankho 1189f127cb91Sfrankho if ((pc_dbtocl(fsp, 1190f127cb91Sfrankho eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) || 1191f127cb91Sfrankho (eoffset > fsp->pcfs_clsize)) { 1192f127cb91Sfrankho pc_unlockfs(fsp); 1193f127cb91Sfrankho *vpp = NULL; 1194f127cb91Sfrankho return (EINVAL); 1195f127cb91Sfrankho } 1196f127cb91Sfrankho 1197f127cb91Sfrankho if (eblkno >= fsp->pcfs_datastart || (eblkno - fsp->pcfs_rdirstart) 1198f127cb91Sfrankho < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) { 1199f127cb91Sfrankho bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno), 1200f127cb91Sfrankho fsp->pcfs_clsize); 1201f127cb91Sfrankho } else { 1202f127cb91Sfrankho /* 1203f127cb91Sfrankho * This is an access "backwards" into the FAT12/FAT16 1204f127cb91Sfrankho * root directory. A better code structure would 1205f127cb91Sfrankho * significantly improve maintainability here ... 1206f127cb91Sfrankho */ 1207f127cb91Sfrankho bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno), 1208f127cb91Sfrankho (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize); 1209f127cb91Sfrankho } 1210f127cb91Sfrankho if (bp->b_flags & (B_ERROR | B_STALE)) { 1211f127cb91Sfrankho error = geterror(bp); 1212f127cb91Sfrankho brelse(bp); 1213f127cb91Sfrankho if (error) 1214f127cb91Sfrankho pc_mark_irrecov(fsp); 1215f127cb91Sfrankho *vpp = NULL; 1216f127cb91Sfrankho pc_unlockfs(fsp); 1217f127cb91Sfrankho return (error); 1218f127cb91Sfrankho } 1219f127cb91Sfrankho ep = (struct pcdir *)(bp->b_un.b_addr + eoffset); 1220f127cb91Sfrankho /* 1221f127cb91Sfrankho * Ok, if this is a valid file handle that we gave out, 1222f127cb91Sfrankho * then simply ensuring that the creation time matches, 1223f127cb91Sfrankho * the entry has not been deleted, and it has a valid first 1224f127cb91Sfrankho * character should be enough. 1225f127cb91Sfrankho * 1226f127cb91Sfrankho * Unfortunately, verifying that the <blkno, offset> _still_ 1227f127cb91Sfrankho * refers to a directory entry is not easy, since we'd have 1228f127cb91Sfrankho * to search _all_ directories starting from root to find it. 1229f127cb91Sfrankho * That's a high price to pay just in case somebody is forging 1230f127cb91Sfrankho * file handles. So instead we verify that as much of the 1231f127cb91Sfrankho * entry is valid as we can: 1232f127cb91Sfrankho * 1233f127cb91Sfrankho * 1. The starting cluster is 0 (unallocated) or valid 1234f127cb91Sfrankho * 2. It is not an LFN entry 1235f127cb91Sfrankho * 3. It is not hidden (unless mounted as such) 1236f127cb91Sfrankho * 4. It is not the label 1237f127cb91Sfrankho */ 1238f127cb91Sfrankho cn = pc_getstartcluster(fsp, ep); 1239f127cb91Sfrankho /* 1240f127cb91Sfrankho * if the starting cluster is valid, but not valid according 1241f127cb91Sfrankho * to pc_validcl(), force it to be to simplify the following if. 1242f127cb91Sfrankho */ 1243f127cb91Sfrankho if (cn == 0) 1244f127cb91Sfrankho cn = PCF_FIRSTCLUSTER; 1245f127cb91Sfrankho if (IS_FAT32(fsp)) { 1246f127cb91Sfrankho if (cn >= PCF_LASTCLUSTER32) 1247f127cb91Sfrankho cn = PCF_FIRSTCLUSTER; 1248f127cb91Sfrankho } else { 1249f127cb91Sfrankho if (cn >= PCF_LASTCLUSTER) 1250f127cb91Sfrankho cn = PCF_FIRSTCLUSTER; 1251f127cb91Sfrankho } 1252f127cb91Sfrankho if ((!pc_validcl(fsp, cn)) || 1253f127cb91Sfrankho (PCDL_IS_LFN(ep)) || 1254f127cb91Sfrankho (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) || 1255f127cb91Sfrankho ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) { 1256f127cb91Sfrankho bp->b_flags |= B_STALE | B_AGE; 1257f127cb91Sfrankho brelse(bp); 1258f127cb91Sfrankho pc_unlockfs(fsp); 1259f127cb91Sfrankho return (EINVAL); 1260f127cb91Sfrankho } 1261f127cb91Sfrankho if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) && 1262f127cb91Sfrankho (ep->pcd_filename[0] != PCD_ERASED) && 1263f127cb91Sfrankho (pc_validchar(ep->pcd_filename[0]) || 1264f127cb91Sfrankho (ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) { 1265f127cb91Sfrankho pcp = pc_getnode(fsp, eblkno, eoffset, ep); 1266f127cb91Sfrankho pcp->pc_flags |= PC_EXTERNAL; 1267f127cb91Sfrankho *vpp = PCTOV(pcp); 1268f127cb91Sfrankho } else { 1269f127cb91Sfrankho *vpp = NULL; 1270f127cb91Sfrankho } 1271f127cb91Sfrankho bp->b_flags |= B_STALE | B_AGE; 1272f127cb91Sfrankho brelse(bp); 1273f127cb91Sfrankho pc_unlockfs(fsp); 1274f127cb91Sfrankho return (0); 1275f127cb91Sfrankho } 1276f127cb91Sfrankho 1277f127cb91Sfrankho /* 1278f127cb91Sfrankho * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about 1279f127cb91Sfrankho * a meg), so we can't bread() it all in at once. This routine reads a 1280f127cb91Sfrankho * fat a chunk at a time. 1281f127cb91Sfrankho */ 1282f127cb91Sfrankho static int 1283f127cb91Sfrankho pc_readfat(struct pcfs *fsp, uchar_t *fatp) 1284f127cb91Sfrankho { 1285f127cb91Sfrankho struct buf *bp; 1286f127cb91Sfrankho size_t off; 1287f127cb91Sfrankho size_t readsize; 1288f127cb91Sfrankho daddr_t diskblk; 1289f127cb91Sfrankho size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize; 1290f127cb91Sfrankho daddr_t start = fsp->pcfs_fatstart; 1291f127cb91Sfrankho 1292f127cb91Sfrankho readsize = fsp->pcfs_clsize; 1293f127cb91Sfrankho for (off = 0; off < fatsize; off += readsize, fatp += readsize) { 1294f127cb91Sfrankho if (readsize > (fatsize - off)) 1295f127cb91Sfrankho readsize = fatsize - off; 1296f127cb91Sfrankho diskblk = pc_dbdaddr(fsp, start + 1297f127cb91Sfrankho pc_cltodb(fsp, pc_lblkno(fsp, off))); 1298f127cb91Sfrankho bp = bread(fsp->pcfs_xdev, diskblk, readsize); 1299f127cb91Sfrankho if (bp->b_flags & (B_ERROR | B_STALE)) { 1300f127cb91Sfrankho brelse(bp); 1301f127cb91Sfrankho return (EIO); 1302f127cb91Sfrankho } 1303f127cb91Sfrankho bp->b_flags |= B_STALE | B_AGE; 1304f127cb91Sfrankho bcopy(bp->b_un.b_addr, fatp, readsize); 1305f127cb91Sfrankho brelse(bp); 1306f127cb91Sfrankho } 1307f127cb91Sfrankho return (0); 1308f127cb91Sfrankho } 1309f127cb91Sfrankho 1310f127cb91Sfrankho /* 1311f127cb91Sfrankho * We write the FAT out a _lot_, in order to make sure that it 1312f127cb91Sfrankho * is up-to-date. But on a FAT32 system (large drive, small clusters) 1313f127cb91Sfrankho * the FAT might be a couple of megabytes, and writing it all out just 1314f127cb91Sfrankho * because we created or deleted a small file is painful (especially 1315f127cb91Sfrankho * since we do it for each alternate FAT too). So instead, for FAT16 and 1316f127cb91Sfrankho * FAT32 we only write out the bit that has changed. We don't clear 1317f127cb91Sfrankho * the 'updated' fields here because the caller might be writing out 1318f127cb91Sfrankho * several FATs, so the caller must use pc_clear_fatchanges() after 1319f127cb91Sfrankho * all FATs have been updated. 1320f127cb91Sfrankho * This function doesn't take "start" from fsp->pcfs_dosstart because 1321f127cb91Sfrankho * callers can use it to write either the primary or any of the alternate 1322f127cb91Sfrankho * FAT tables. 1323f127cb91Sfrankho */ 1324f127cb91Sfrankho static int 1325f127cb91Sfrankho pc_writefat(struct pcfs *fsp, daddr_t start) 1326f127cb91Sfrankho { 1327f127cb91Sfrankho struct buf *bp; 1328f127cb91Sfrankho size_t off; 1329f127cb91Sfrankho size_t writesize; 1330f127cb91Sfrankho int error; 1331f127cb91Sfrankho uchar_t *fatp = fsp->pcfs_fatp; 1332f127cb91Sfrankho size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize; 1333f127cb91Sfrankho 1334f127cb91Sfrankho writesize = fsp->pcfs_clsize; 1335f127cb91Sfrankho for (off = 0; off < fatsize; off += writesize, fatp += writesize) { 1336f127cb91Sfrankho if (writesize > (fatsize - off)) 1337f127cb91Sfrankho writesize = fatsize - off; 1338f127cb91Sfrankho if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) { 1339f127cb91Sfrankho continue; 1340f127cb91Sfrankho } 1341f127cb91Sfrankho bp = ngeteblk(writesize); 1342f127cb91Sfrankho bp->b_edev = fsp->pcfs_xdev; 1343f127cb91Sfrankho bp->b_dev = cmpdev(bp->b_edev); 1344f127cb91Sfrankho bp->b_blkno = pc_dbdaddr(fsp, start + 1345f127cb91Sfrankho pc_cltodb(fsp, pc_lblkno(fsp, off))); 1346f127cb91Sfrankho bcopy(fatp, bp->b_un.b_addr, writesize); 1347f127cb91Sfrankho bwrite2(bp); 1348f127cb91Sfrankho error = geterror(bp); 1349f127cb91Sfrankho brelse(bp); 1350f127cb91Sfrankho if (error) { 1351f127cb91Sfrankho return (error); 1352f127cb91Sfrankho } 1353f127cb91Sfrankho } 1354f127cb91Sfrankho return (0); 1355f127cb91Sfrankho } 1356f127cb91Sfrankho 1357f127cb91Sfrankho /* 1358f127cb91Sfrankho * Mark the FAT cluster that 'cn' is stored in as modified. 1359f127cb91Sfrankho */ 1360f127cb91Sfrankho void 1361f127cb91Sfrankho pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn) 1362f127cb91Sfrankho { 1363f127cb91Sfrankho pc_cluster32_t bn; 1364f127cb91Sfrankho size_t size; 1365f127cb91Sfrankho 1366f127cb91Sfrankho /* which fat block is the cluster number stored in? */ 1367f127cb91Sfrankho if (IS_FAT32(fsp)) { 1368f127cb91Sfrankho size = sizeof (pc_cluster32_t); 1369f127cb91Sfrankho bn = pc_lblkno(fsp, cn * size); 1370f127cb91Sfrankho fsp->pcfs_fat_changemap[bn] = 1; 1371f127cb91Sfrankho } else if (IS_FAT16(fsp)) { 1372f127cb91Sfrankho size = sizeof (pc_cluster16_t); 1373f127cb91Sfrankho bn = pc_lblkno(fsp, cn * size); 1374f127cb91Sfrankho fsp->pcfs_fat_changemap[bn] = 1; 1375f127cb91Sfrankho } else { 1376f127cb91Sfrankho offset_t off; 1377f127cb91Sfrankho pc_cluster32_t nbn; 1378f127cb91Sfrankho 1379f127cb91Sfrankho ASSERT(IS_FAT12(fsp)); 1380f127cb91Sfrankho off = cn + (cn >> 1); 1381f127cb91Sfrankho bn = pc_lblkno(fsp, off); 1382f127cb91Sfrankho fsp->pcfs_fat_changemap[bn] = 1; 1383f127cb91Sfrankho /* does this field wrap into the next fat cluster? */ 1384f127cb91Sfrankho nbn = pc_lblkno(fsp, off + 1); 1385f127cb91Sfrankho if (nbn != bn) { 1386f127cb91Sfrankho fsp->pcfs_fat_changemap[nbn] = 1; 1387f127cb91Sfrankho } 1388f127cb91Sfrankho } 1389f127cb91Sfrankho } 1390f127cb91Sfrankho 1391f127cb91Sfrankho /* 1392f127cb91Sfrankho * return whether the FAT cluster 'bn' is updated and needs to 1393f127cb91Sfrankho * be written out. 1394f127cb91Sfrankho */ 1395f127cb91Sfrankho int 1396f127cb91Sfrankho pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn) 1397f127cb91Sfrankho { 1398f127cb91Sfrankho return (fsp->pcfs_fat_changemap[bn] == 1); 1399f127cb91Sfrankho } 1400f127cb91Sfrankho 1401f127cb91Sfrankho /* 1402f127cb91Sfrankho * Implementation of VFS_FREEVFS() to support forced umounts. 1403f127cb91Sfrankho * This is called by the vfs framework after umount, to trigger 1404f127cb91Sfrankho * the release of any resources still associated with the given 1405f127cb91Sfrankho * vfs_t once the need to keep them has gone away. 1406f127cb91Sfrankho */ 1407f127cb91Sfrankho void 1408f127cb91Sfrankho pcfs_freevfs(vfs_t *vfsp) 1409f127cb91Sfrankho { 1410f127cb91Sfrankho struct pcfs *fsp = VFSTOPCFS(vfsp); 1411f127cb91Sfrankho 1412f127cb91Sfrankho mutex_enter(&pcfslock); 1413f127cb91Sfrankho /* 1414f127cb91Sfrankho * Purging the FAT closes the device - can't do any more 1415f127cb91Sfrankho * I/O after this. 1416f127cb91Sfrankho */ 1417f127cb91Sfrankho if (fsp->pcfs_fatp != (uchar_t *)0) 1418f127cb91Sfrankho pc_invalfat(fsp); 1419f127cb91Sfrankho mutex_exit(&pcfslock); 1420f127cb91Sfrankho 1421f127cb91Sfrankho VN_RELE(fsp->pcfs_devvp); 1422f127cb91Sfrankho mutex_destroy(&fsp->pcfs_lock); 1423f127cb91Sfrankho kmem_free(fsp, sizeof (*fsp)); 1424f127cb91Sfrankho 1425f127cb91Sfrankho /* 1426f127cb91Sfrankho * Allow _fini() to succeed now, if so desired. 1427f127cb91Sfrankho */ 1428f127cb91Sfrankho atomic_dec_32(&pcfs_mountcount); 1429f127cb91Sfrankho } 1430f127cb91Sfrankho 1431f127cb91Sfrankho 1432f127cb91Sfrankho /* 1433f127cb91Sfrankho * PC-style partition parsing and FAT BPB identification/validation code. 1434f127cb91Sfrankho * The partition parsers here assume: 1435f127cb91Sfrankho * - a FAT filesystem will be in a partition that has one of a set of 1436f127cb91Sfrankho * recognized partition IDs 1437f127cb91Sfrankho * - the user wants the 'numbering' (C:, D:, ...) that one would get 1438f127cb91Sfrankho * on MSDOS 6.x. 1439f127cb91Sfrankho * That means any non-FAT partition type (NTFS, HPFS, or any Linux fs) 1440f127cb91Sfrankho * will not factor in the enumeration. 1441f127cb91Sfrankho * These days, such assumptions should be revisited. FAT is no longer the 1442f127cb91Sfrankho * only game in 'PC town'. 1443f127cb91Sfrankho */ 1444f127cb91Sfrankho /* 1445f127cb91Sfrankho * isDosDrive() 1446f127cb91Sfrankho * Boolean function. Give it the systid field for an fdisk partition 1447f127cb91Sfrankho * and it decides if that's a systid that describes a DOS drive. We 1448f127cb91Sfrankho * use systid values defined in sys/dktp/fdisk.h. 1449f127cb91Sfrankho */ 1450f127cb91Sfrankho static int 1451f127cb91Sfrankho isDosDrive(uchar_t checkMe) 1452f127cb91Sfrankho { 1453f127cb91Sfrankho return ((checkMe == DOSOS12) || (checkMe == DOSOS16) || 1454f127cb91Sfrankho (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) || 1455f127cb91Sfrankho (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) || 1456f127cb91Sfrankho (checkMe == DIAGPART)); 1457f127cb91Sfrankho } 1458f127cb91Sfrankho 1459f127cb91Sfrankho 1460f127cb91Sfrankho /* 1461f127cb91Sfrankho * isDosExtended() 1462f127cb91Sfrankho * Boolean function. Give it the systid field for an fdisk partition 1463f127cb91Sfrankho * and it decides if that's a systid that describes an extended DOS 1464f127cb91Sfrankho * partition. 1465f127cb91Sfrankho */ 1466f127cb91Sfrankho static int 1467f127cb91Sfrankho isDosExtended(uchar_t checkMe) 1468f127cb91Sfrankho { 1469f127cb91Sfrankho return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA)); 1470f127cb91Sfrankho } 1471f127cb91Sfrankho 1472f127cb91Sfrankho 1473f127cb91Sfrankho /* 1474f127cb91Sfrankho * isBootPart() 1475f127cb91Sfrankho * Boolean function. Give it the systid field for an fdisk partition 1476f127cb91Sfrankho * and it decides if that's a systid that describes a Solaris boot 1477f127cb91Sfrankho * partition. 1478f127cb91Sfrankho */ 1479f127cb91Sfrankho static int 1480f127cb91Sfrankho isBootPart(uchar_t checkMe) 1481f127cb91Sfrankho { 1482f127cb91Sfrankho return (checkMe == X86BOOT); 1483f127cb91Sfrankho } 1484f127cb91Sfrankho 1485f127cb91Sfrankho 1486f127cb91Sfrankho /* 14877c478bd9Sstevel@tonic-gate * noLogicalDrive() 14887c478bd9Sstevel@tonic-gate * Display error message about not being able to find a logical 14897c478bd9Sstevel@tonic-gate * drive. 14907c478bd9Sstevel@tonic-gate */ 14917c478bd9Sstevel@tonic-gate static void 1492f127cb91Sfrankho noLogicalDrive(int ldrive) 14937c478bd9Sstevel@tonic-gate { 1494f127cb91Sfrankho if (ldrive == BOOT_PARTITION_DRIVE) { 14957c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "!pcfs: no boot partition"); 14967c478bd9Sstevel@tonic-gate } else { 1497f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: %d: no such logical drive", ldrive); 14987c478bd9Sstevel@tonic-gate } 14997c478bd9Sstevel@tonic-gate } 15007c478bd9Sstevel@tonic-gate 1501f127cb91Sfrankho 15027c478bd9Sstevel@tonic-gate /* 15037c478bd9Sstevel@tonic-gate * findTheDrive() 15047c478bd9Sstevel@tonic-gate * Discover offset of the requested logical drive, and return 15057c478bd9Sstevel@tonic-gate * that offset (startSector), the systid of that drive (sysid), 15067c478bd9Sstevel@tonic-gate * and a buffer pointer (bp), with the buffer contents being 15077c478bd9Sstevel@tonic-gate * the first sector of the logical drive (i.e., the sector that 15087c478bd9Sstevel@tonic-gate * contains the BPB for that drive). 1509f127cb91Sfrankho * 1510f127cb91Sfrankho * Note: this code is not capable of addressing >2TB disks, as it uses 1511f127cb91Sfrankho * daddr_t not diskaddr_t, some of the calculations would overflow 15127c478bd9Sstevel@tonic-gate */ 1513f127cb91Sfrankho #define COPY_PTBL(mbr, ptblp) \ 1514f127cb91Sfrankho bcopy(&(((struct mboot *)(mbr))->parts), (ptblp), \ 1515f127cb91Sfrankho FD_NUMPART * sizeof (struct ipart)) 1516f127cb91Sfrankho 15177c478bd9Sstevel@tonic-gate static int 1518f127cb91Sfrankho findTheDrive(struct pcfs *fsp, buf_t **bp) 15197c478bd9Sstevel@tonic-gate { 1520f127cb91Sfrankho int ldrive = fsp->pcfs_ldrive; 1521f127cb91Sfrankho dev_t dev = fsp->pcfs_devvp->v_rdev; 1522f127cb91Sfrankho 15237c478bd9Sstevel@tonic-gate struct ipart dosp[FD_NUMPART]; /* incore fdisk partition structure */ 15247c478bd9Sstevel@tonic-gate daddr_t lastseek = 0; /* Disk block we sought previously */ 15257c478bd9Sstevel@tonic-gate daddr_t diskblk = 0; /* Disk block to get */ 15267c478bd9Sstevel@tonic-gate daddr_t xstartsect; /* base of Extended DOS partition */ 15277c478bd9Sstevel@tonic-gate int logicalDriveCount = 0; /* Count of logical drives seen */ 15287c478bd9Sstevel@tonic-gate int extendedPart = -1; /* index of extended dos partition */ 15297c478bd9Sstevel@tonic-gate int primaryPart = -1; /* index of primary dos partition */ 15307c478bd9Sstevel@tonic-gate int bootPart = -1; /* index of a Solaris boot partition */ 1531342440ecSPrasad Singamsetty uint32_t xnumsect = 0; /* length of extended DOS partition */ 15327c478bd9Sstevel@tonic-gate int driveIndex; /* computed FDISK table index */ 1533f127cb91Sfrankho daddr_t startsec; 1534f127cb91Sfrankho len_t mediasize; 15357c478bd9Sstevel@tonic-gate int i; 15367c478bd9Sstevel@tonic-gate /* 15377c478bd9Sstevel@tonic-gate * Count of drives in the current extended partition's 15387c478bd9Sstevel@tonic-gate * FDISK table, and indexes of the drives themselves. 15397c478bd9Sstevel@tonic-gate */ 15407c478bd9Sstevel@tonic-gate int extndDrives[FD_NUMPART]; 15417c478bd9Sstevel@tonic-gate int numDrives = 0; 15427c478bd9Sstevel@tonic-gate 15437c478bd9Sstevel@tonic-gate /* 15447c478bd9Sstevel@tonic-gate * Count of drives (beyond primary) in master boot record's 15457c478bd9Sstevel@tonic-gate * FDISK table, and indexes of the drives themselves. 15467c478bd9Sstevel@tonic-gate */ 15477c478bd9Sstevel@tonic-gate int extraDrives[FD_NUMPART]; 15487c478bd9Sstevel@tonic-gate int numExtraDrives = 0; 15497c478bd9Sstevel@tonic-gate 15509bd42341Sfrankho /* 15519bd42341Sfrankho * "ldrive == 0" should never happen, as this is a request to 15529bd42341Sfrankho * mount the physical device (and ignore partitioning). The code 15539bd42341Sfrankho * in pcfs_mount() should have made sure that a logical drive number 15549bd42341Sfrankho * is at least 1, meaning we're looking for drive "C:". It is not 15559bd42341Sfrankho * safe (and a bug in the callers of this function) to request logical 15569bd42341Sfrankho * drive number 0; we could ASSERT() but a graceful EIO is a more 15579bd42341Sfrankho * polite way. 15589bd42341Sfrankho */ 15599bd42341Sfrankho if (ldrive == 0) { 15609bd42341Sfrankho cmn_err(CE_NOTE, "!pcfs: request for logical partition zero"); 15619bd42341Sfrankho noLogicalDrive(ldrive); 15629bd42341Sfrankho return (EIO); 15639bd42341Sfrankho } 15649bd42341Sfrankho 15657c478bd9Sstevel@tonic-gate /* 15667c478bd9Sstevel@tonic-gate * Copy from disk block into memory aligned structure for fdisk usage. 15677c478bd9Sstevel@tonic-gate */ 1568f127cb91Sfrankho COPY_PTBL((*bp)->b_un.b_addr, dosp); 15697c478bd9Sstevel@tonic-gate 1570f127cb91Sfrankho /* 1571f127cb91Sfrankho * This check is ok because a FAT BPB and a master boot record (MBB) 1572f127cb91Sfrankho * have the same signature, in the same position within the block. 1573f127cb91Sfrankho */ 1574f127cb91Sfrankho if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) { 1575f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: MBR partition table signature err, " 1576f127cb91Sfrankho "device (%x.%x):%d\n", 1577f127cb91Sfrankho getmajor(dev), getminor(dev), ldrive); 15789bd42341Sfrankho return (EINVAL); 15799bd42341Sfrankho } 15809bd42341Sfrankho 15817c478bd9Sstevel@tonic-gate /* 15827c478bd9Sstevel@tonic-gate * Get a summary of what is in the Master FDISK table. 15837c478bd9Sstevel@tonic-gate * Normally we expect to find one partition marked as a DOS drive. 15847c478bd9Sstevel@tonic-gate * This partition is the one Windows calls the primary dos partition. 15857c478bd9Sstevel@tonic-gate * If the machine has any logical drives then we also expect 15867c478bd9Sstevel@tonic-gate * to find a partition marked as an extended DOS partition. 15877c478bd9Sstevel@tonic-gate * 15887c478bd9Sstevel@tonic-gate * Sometimes we'll find multiple partitions marked as DOS drives. 15897c478bd9Sstevel@tonic-gate * The Solaris fdisk program allows these partitions 15907c478bd9Sstevel@tonic-gate * to be created, but Windows fdisk no longer does. We still need 15917c478bd9Sstevel@tonic-gate * to support these, though, since Windows does. We also need to fix 15927c478bd9Sstevel@tonic-gate * our fdisk to behave like the Windows version. 15937c478bd9Sstevel@tonic-gate * 15947c478bd9Sstevel@tonic-gate * It turns out that some off-the-shelf media have *only* an 15957c478bd9Sstevel@tonic-gate * Extended partition, so we need to deal with that case as well. 15967c478bd9Sstevel@tonic-gate * 15977c478bd9Sstevel@tonic-gate * Only a single (the first) Extended or Boot Partition will 15987c478bd9Sstevel@tonic-gate * be recognized. Any others will be ignored. 15997c478bd9Sstevel@tonic-gate */ 16007c478bd9Sstevel@tonic-gate for (i = 0; i < FD_NUMPART; i++) { 1601f127cb91Sfrankho DTRACE_PROBE4(primarypart, struct pcfs *, fsp, 1602f127cb91Sfrankho uint_t, (uint_t)dosp[i].systid, 1603f127cb91Sfrankho uint_t, LE_32(dosp[i].relsect), 1604f127cb91Sfrankho uint_t, LE_32(dosp[i].numsect)); 16050576819eSwyllys 16067c478bd9Sstevel@tonic-gate if (isDosDrive(dosp[i].systid)) { 16077c478bd9Sstevel@tonic-gate if (primaryPart < 0) { 16087c478bd9Sstevel@tonic-gate logicalDriveCount++; 16097c478bd9Sstevel@tonic-gate primaryPart = i; 16107c478bd9Sstevel@tonic-gate } else { 16117c478bd9Sstevel@tonic-gate extraDrives[numExtraDrives++] = i; 16127c478bd9Sstevel@tonic-gate } 16137c478bd9Sstevel@tonic-gate continue; 16147c478bd9Sstevel@tonic-gate } 16157c478bd9Sstevel@tonic-gate if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) { 16167c478bd9Sstevel@tonic-gate extendedPart = i; 16177c478bd9Sstevel@tonic-gate continue; 16187c478bd9Sstevel@tonic-gate } 16197c478bd9Sstevel@tonic-gate if ((bootPart < 0) && isBootPart(dosp[i].systid)) { 16207c478bd9Sstevel@tonic-gate bootPart = i; 16217c478bd9Sstevel@tonic-gate continue; 16227c478bd9Sstevel@tonic-gate } 16237c478bd9Sstevel@tonic-gate } 16247c478bd9Sstevel@tonic-gate 16259bd42341Sfrankho if (ldrive == BOOT_PARTITION_DRIVE) { 16267c478bd9Sstevel@tonic-gate if (bootPart < 0) { 16279bd42341Sfrankho noLogicalDrive(ldrive); 16289bd42341Sfrankho return (EINVAL); 16297c478bd9Sstevel@tonic-gate } 1630f127cb91Sfrankho startsec = LE_32(dosp[bootPart].relsect); 1631f127cb91Sfrankho mediasize = LE_32(dosp[bootPart].numsect); 1632f127cb91Sfrankho goto found; 16337c478bd9Sstevel@tonic-gate } 16347c478bd9Sstevel@tonic-gate 16359bd42341Sfrankho if (ldrive == PRIMARY_DOS_DRIVE && primaryPart >= 0) { 1636f127cb91Sfrankho startsec = LE_32(dosp[primaryPart].relsect); 1637f127cb91Sfrankho mediasize = LE_32(dosp[primaryPart].numsect); 1638f127cb91Sfrankho goto found; 16397c478bd9Sstevel@tonic-gate } 16407c478bd9Sstevel@tonic-gate 16417c478bd9Sstevel@tonic-gate /* 16427c478bd9Sstevel@tonic-gate * We are not looking for the C: drive (or the primary drive 16437c478bd9Sstevel@tonic-gate * was not found), so we had better have an extended partition 16447c478bd9Sstevel@tonic-gate * or extra drives in the Master FDISK table. 16457c478bd9Sstevel@tonic-gate */ 16467c478bd9Sstevel@tonic-gate if ((extendedPart < 0) && (numExtraDrives == 0)) { 16477c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "!pcfs: no extended dos partition"); 16489bd42341Sfrankho noLogicalDrive(ldrive); 16499bd42341Sfrankho return (EINVAL); 16507c478bd9Sstevel@tonic-gate } 16517c478bd9Sstevel@tonic-gate 16527c478bd9Sstevel@tonic-gate if (extendedPart >= 0) { 1653f127cb91Sfrankho diskblk = xstartsect = LE_32(dosp[extendedPart].relsect); 1654f127cb91Sfrankho xnumsect = LE_32(dosp[extendedPart].numsect); 16557c478bd9Sstevel@tonic-gate do { 16567c478bd9Sstevel@tonic-gate /* 16577c478bd9Sstevel@tonic-gate * If the seek would not cause us to change 16587c478bd9Sstevel@tonic-gate * position on the drive, then we're out of 16597c478bd9Sstevel@tonic-gate * extended partitions to examine. 16607c478bd9Sstevel@tonic-gate */ 16617c478bd9Sstevel@tonic-gate if (diskblk == lastseek) 16627c478bd9Sstevel@tonic-gate break; 16637c478bd9Sstevel@tonic-gate logicalDriveCount += numDrives; 16647c478bd9Sstevel@tonic-gate /* 16657c478bd9Sstevel@tonic-gate * Seek the next extended partition, and find 16667c478bd9Sstevel@tonic-gate * logical drives within it. 16677c478bd9Sstevel@tonic-gate */ 1668f127cb91Sfrankho brelse(*bp); 1669f127cb91Sfrankho /* 1670f127cb91Sfrankho * bread() block numbers are multiples of DEV_BSIZE 1671f127cb91Sfrankho * but the device sector size (the unit of partitioning) 1672f127cb91Sfrankho * might be larger than that; pcfs_get_device_info() 1673f127cb91Sfrankho * has calculated the multiplicator for us. 1674f127cb91Sfrankho */ 1675f127cb91Sfrankho *bp = bread(dev, 1676f127cb91Sfrankho pc_dbdaddr(fsp, diskblk), fsp->pcfs_secsize); 16777c478bd9Sstevel@tonic-gate if ((*bp)->b_flags & B_ERROR) { 16789bd42341Sfrankho return (EIO); 16797c478bd9Sstevel@tonic-gate } 1680f127cb91Sfrankho 16817c478bd9Sstevel@tonic-gate lastseek = diskblk; 1682f127cb91Sfrankho COPY_PTBL((*bp)->b_un.b_addr, dosp); 1683f127cb91Sfrankho if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) { 16847c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "!pcfs: " 1685f127cb91Sfrankho "extended partition table signature err, " 1686f127cb91Sfrankho "device (%x.%x):%d, LBA %u", 1687f127cb91Sfrankho getmajor(dev), getminor(dev), ldrive, 1688f127cb91Sfrankho (uint_t)pc_dbdaddr(fsp, diskblk)); 16899bd42341Sfrankho return (EINVAL); 16907c478bd9Sstevel@tonic-gate } 16917c478bd9Sstevel@tonic-gate /* 16927c478bd9Sstevel@tonic-gate * Count up drives, and track where the next 16937c478bd9Sstevel@tonic-gate * extended partition is in case we need it. We 16947c478bd9Sstevel@tonic-gate * are expecting only one extended partition. If 16957c478bd9Sstevel@tonic-gate * there is more than one we'll only go to the 16967c478bd9Sstevel@tonic-gate * first one we see, but warn about ignoring. 16977c478bd9Sstevel@tonic-gate */ 16987c478bd9Sstevel@tonic-gate numDrives = 0; 16997c478bd9Sstevel@tonic-gate for (i = 0; i < FD_NUMPART; i++) { 1700f127cb91Sfrankho DTRACE_PROBE4(extendedpart, 1701f127cb91Sfrankho struct pcfs *, fsp, 1702f127cb91Sfrankho uint_t, (uint_t)dosp[i].systid, 1703f127cb91Sfrankho uint_t, LE_32(dosp[i].relsect), 1704f127cb91Sfrankho uint_t, LE_32(dosp[i].numsect)); 17057c478bd9Sstevel@tonic-gate if (isDosDrive(dosp[i].systid)) { 17067c478bd9Sstevel@tonic-gate extndDrives[numDrives++] = i; 17077c478bd9Sstevel@tonic-gate } else if (isDosExtended(dosp[i].systid)) { 17087c478bd9Sstevel@tonic-gate if (diskblk != lastseek) { 17097c478bd9Sstevel@tonic-gate /* 17107c478bd9Sstevel@tonic-gate * Already found an extended 17117c478bd9Sstevel@tonic-gate * partition in this table. 17127c478bd9Sstevel@tonic-gate */ 17137c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, 17147c478bd9Sstevel@tonic-gate "!pcfs: ignoring unexpected" 17157c478bd9Sstevel@tonic-gate " additional extended" 17167c478bd9Sstevel@tonic-gate " partition"); 17179bd42341Sfrankho } else { 17189bd42341Sfrankho diskblk = xstartsect + 1719f127cb91Sfrankho LE_32(dosp[i].relsect); 17207c478bd9Sstevel@tonic-gate } 17217c478bd9Sstevel@tonic-gate } 17227c478bd9Sstevel@tonic-gate } 17239bd42341Sfrankho } while (ldrive > logicalDriveCount + numDrives); 17247c478bd9Sstevel@tonic-gate 1725f127cb91Sfrankho ASSERT(numDrives <= FD_NUMPART); 1726f127cb91Sfrankho 17279bd42341Sfrankho if (ldrive <= logicalDriveCount + numDrives) { 17287c478bd9Sstevel@tonic-gate /* 17297c478bd9Sstevel@tonic-gate * The number of logical drives we've found thus 17307c478bd9Sstevel@tonic-gate * far is enough to get us to the one we were 17317c478bd9Sstevel@tonic-gate * searching for. 17327c478bd9Sstevel@tonic-gate */ 17339bd42341Sfrankho driveIndex = logicalDriveCount + numDrives - ldrive; 1734f127cb91Sfrankho mediasize = 1735f127cb91Sfrankho LE_32(dosp[extndDrives[driveIndex]].numsect); 1736f127cb91Sfrankho startsec = 1737f127cb91Sfrankho LE_32(dosp[extndDrives[driveIndex]].relsect) + 17387c478bd9Sstevel@tonic-gate lastseek; 1739f127cb91Sfrankho if (startsec > (xstartsect + xnumsect)) { 17407c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "!pcfs: extended partition " 17417c478bd9Sstevel@tonic-gate "values bad"); 17429bd42341Sfrankho return (EINVAL); 17437c478bd9Sstevel@tonic-gate } 1744f127cb91Sfrankho goto found; 17457c478bd9Sstevel@tonic-gate } else { 17467c478bd9Sstevel@tonic-gate /* 17477c478bd9Sstevel@tonic-gate * We ran out of extended dos partition 17487c478bd9Sstevel@tonic-gate * drives. The only hope now is to go 17497c478bd9Sstevel@tonic-gate * back to extra drives defined in the master 17507c478bd9Sstevel@tonic-gate * fdisk table. But we overwrote that table 17517c478bd9Sstevel@tonic-gate * already, so we must load it in again. 17527c478bd9Sstevel@tonic-gate */ 17537c478bd9Sstevel@tonic-gate logicalDriveCount += numDrives; 17547c478bd9Sstevel@tonic-gate brelse(*bp); 1755f127cb91Sfrankho ASSERT(fsp->pcfs_dosstart == 0); 1756f127cb91Sfrankho *bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), 1757f127cb91Sfrankho fsp->pcfs_secsize); 17587c478bd9Sstevel@tonic-gate if ((*bp)->b_flags & B_ERROR) { 17599bd42341Sfrankho return (EIO); 17607c478bd9Sstevel@tonic-gate } 1761f127cb91Sfrankho COPY_PTBL((*bp)->b_un.b_addr, dosp); 17627c478bd9Sstevel@tonic-gate } 17637c478bd9Sstevel@tonic-gate } 17647c478bd9Sstevel@tonic-gate /* 17657c478bd9Sstevel@tonic-gate * Still haven't found the drive, is it an extra 17667c478bd9Sstevel@tonic-gate * drive defined in the main FDISK table? 17677c478bd9Sstevel@tonic-gate */ 17689bd42341Sfrankho if (ldrive <= logicalDriveCount + numExtraDrives) { 17699bd42341Sfrankho driveIndex = logicalDriveCount + numExtraDrives - ldrive; 17709bd42341Sfrankho ASSERT(driveIndex < MIN(numExtraDrives, FD_NUMPART)); 1771f127cb91Sfrankho mediasize = LE_32(dosp[extraDrives[driveIndex]].numsect); 1772f127cb91Sfrankho startsec = LE_32(dosp[extraDrives[driveIndex]].relsect); 1773f127cb91Sfrankho goto found; 17747c478bd9Sstevel@tonic-gate } 17757c478bd9Sstevel@tonic-gate /* 17767c478bd9Sstevel@tonic-gate * Still haven't found the drive, and there is 17777c478bd9Sstevel@tonic-gate * nowhere else to look. 17787c478bd9Sstevel@tonic-gate */ 17799bd42341Sfrankho noLogicalDrive(ldrive); 17809bd42341Sfrankho return (EINVAL); 17810576819eSwyllys 1782f127cb91Sfrankho found: 17830576819eSwyllys /* 1784f127cb91Sfrankho * We need this value in units of sectorsize, because PCFS' internal 1785f127cb91Sfrankho * offset calculations go haywire for > 512Byte sectors unless all 1786f127cb91Sfrankho * pcfs_.*start values are in units of sectors. 1787f127cb91Sfrankho * So, assign before the capacity check (that's done in DEV_BSIZE) 17880576819eSwyllys */ 1789f127cb91Sfrankho fsp->pcfs_dosstart = startsec; 17900576819eSwyllys 17910576819eSwyllys /* 1792f127cb91Sfrankho * convert from device sectors to proper units: 1793f127cb91Sfrankho * - starting sector: DEV_BSIZE (as argument to bread()) 1794f127cb91Sfrankho * - media size: Bytes 17950576819eSwyllys */ 1796f127cb91Sfrankho startsec = pc_dbdaddr(fsp, startsec); 1797f127cb91Sfrankho mediasize *= fsp->pcfs_secsize; 17980576819eSwyllys 17990576819eSwyllys /* 1800f127cb91Sfrankho * some additional validation / warnings in case the partition table 1801f127cb91Sfrankho * and the actual media capacity are not in accordance ... 18020576819eSwyllys */ 1803f127cb91Sfrankho if (fsp->pcfs_mediasize != 0) { 1804f127cb91Sfrankho diskaddr_t startoff = 1805f127cb91Sfrankho (diskaddr_t)startsec * (diskaddr_t)DEV_BSIZE; 1806f127cb91Sfrankho 1807f127cb91Sfrankho if (startoff >= fsp->pcfs_mediasize || 1808f127cb91Sfrankho startoff + mediasize > fsp->pcfs_mediasize) { 1809f127cb91Sfrankho cmn_err(CE_WARN, 1810f127cb91Sfrankho "!pcfs: partition size (LBA start %u, %lld bytes, " 1811f127cb91Sfrankho "device (%x.%x):%d) smaller than " 1812f127cb91Sfrankho "mediasize (%lld bytes).\n" 1813f127cb91Sfrankho "filesystem may be truncated, access errors " 1814f127cb91Sfrankho "may result.\n", 1815f127cb91Sfrankho (uint_t)startsec, (long long)mediasize, 1816f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 1817f127cb91Sfrankho fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize); 18180576819eSwyllys } 18190576819eSwyllys } else { 1820f127cb91Sfrankho fsp->pcfs_mediasize = mediasize; 18210576819eSwyllys } 18229bd42341Sfrankho 1823f127cb91Sfrankho return (0); 18240576819eSwyllys } 18250576819eSwyllys 18260576819eSwyllys 1827f127cb91Sfrankho static fattype_t 1828f127cb91Sfrankho secondaryBPBChecks(struct pcfs *fsp, uchar_t *bpb, size_t secsize) 18297c478bd9Sstevel@tonic-gate { 1830f127cb91Sfrankho uint32_t ncl = fsp->pcfs_ncluster; 1831f127cb91Sfrankho 1832f127cb91Sfrankho if (ncl <= 4096) { 1833f127cb91Sfrankho if (bpb_get_FatSz16(bpb) == 0) 1834f127cb91Sfrankho return (FAT_UNKNOWN); 1835f127cb91Sfrankho 1836f127cb91Sfrankho if (bpb_get_FatSz16(bpb) * secsize < ncl * 2 && 1837f127cb91Sfrankho bpb_get_FatSz16(bpb) * secsize >= (3 * ncl / 2)) 1838f127cb91Sfrankho return (FAT12); 1839f127cb91Sfrankho if (bcmp(bpb_FilSysType16(bpb), "FAT12", 5) == 0) 1840f127cb91Sfrankho return (FAT12); 1841f127cb91Sfrankho if (bcmp(bpb_FilSysType16(bpb), "FAT16", 5) == 0) 1842f127cb91Sfrankho return (FAT16); 1843f127cb91Sfrankho 1844f127cb91Sfrankho switch (bpb_get_Media(bpb)) { 1845f127cb91Sfrankho case SS8SPT: 1846f127cb91Sfrankho case DS8SPT: 1847f127cb91Sfrankho case SS9SPT: 1848f127cb91Sfrankho case DS9SPT: 1849f127cb91Sfrankho case DS18SPT: 1850f127cb91Sfrankho case DS9_15SPT: 1851f127cb91Sfrankho /* 1852f127cb91Sfrankho * Is this reliable - all floppies are FAT12 ? 1853f127cb91Sfrankho */ 1854f127cb91Sfrankho return (FAT12); 1855f127cb91Sfrankho case MD_FIXED: 1856f127cb91Sfrankho /* 1857f127cb91Sfrankho * Is this reliable - disks are always FAT16 ? 1858f127cb91Sfrankho */ 1859f127cb91Sfrankho return (FAT16); 1860f127cb91Sfrankho default: 1861f127cb91Sfrankho break; 18627c478bd9Sstevel@tonic-gate } 1863f127cb91Sfrankho } else if (ncl <= 65536) { 1864f127cb91Sfrankho if (bpb_get_FatSz16(bpb) == 0 && bpb_get_FatSz32(bpb) > 0) 1865f127cb91Sfrankho return (FAT32); 1866f127cb91Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig32(bpb))) 1867f127cb91Sfrankho return (FAT32); 1868f127cb91Sfrankho if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb))) 1869f127cb91Sfrankho return (FAT32); 18707c478bd9Sstevel@tonic-gate 1871f127cb91Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig16(bpb))) 1872f127cb91Sfrankho return (FAT16); 1873f127cb91Sfrankho if (bpb_get_FatSz16(bpb) * secsize < ncl * 4) 1874f127cb91Sfrankho return (FAT16); 18757c478bd9Sstevel@tonic-gate } 18767c478bd9Sstevel@tonic-gate 18770576819eSwyllys /* 1878f127cb91Sfrankho * We don't know 18790576819eSwyllys */ 1880f127cb91Sfrankho return (FAT_UNKNOWN); 18817c478bd9Sstevel@tonic-gate } 18827c478bd9Sstevel@tonic-gate 18837c478bd9Sstevel@tonic-gate /* 1884f127cb91Sfrankho * Check to see if the BPB we found is correct. 1885f127cb91Sfrankho * 1886f127cb91Sfrankho * This looks far more complicated that it needs to be for pure structural 1887f127cb91Sfrankho * validation. The reason for this is that parseBPB() is also used for 1888f127cb91Sfrankho * debugging purposes (mdb dcmd) and we therefore want a bitmap of which 1889c2aaf90fSgd * BPB fields (do not) have 'known good' values, even if we (do not) reject 1890c2aaf90fSgd * the BPB when attempting to mount the filesystem. 1891c2aaf90fSgd * 1892c2aaf90fSgd * Real-world usage of FAT shows there are a lot of corner-case situations 1893c2aaf90fSgd * and, following the specification strictly, invalid filesystems out there. 1894c2aaf90fSgd * Known are situations such as: 1895c2aaf90fSgd * - FAT12/FAT16 filesystems with garbage in either totsec16/32 1896c2aaf90fSgd * instead of the zero in one of the fields mandated by the spec 1897c2aaf90fSgd * - filesystems that claim to be larger than the partition they're in 1898c2aaf90fSgd * - filesystems without valid media descriptor 1899c2aaf90fSgd * - FAT32 filesystems with RootEntCnt != 0 1900c2aaf90fSgd * - FAT32 filesystems with less than 65526 clusters 1901c2aaf90fSgd * - FAT32 filesystems without valid FSI sector 1902c2aaf90fSgd * - FAT32 filesystems with FAT size in fatsec16 instead of fatsec32 1903c2aaf90fSgd * 1904c2aaf90fSgd * Such filesystems are accessible by PCFS - if it'd know to start with that 1905c2aaf90fSgd * the filesystem should be treated as a specific FAT type. Before S10, it 1906c2aaf90fSgd * relied on the PC/fdisk partition type for the purpose and almost completely 1907c2aaf90fSgd * ignored the BPB; now it ignores the partition type for anything else but 1908c2aaf90fSgd * logical drive enumeration, which can result in rejection of (invalid) 1909c2aaf90fSgd * FAT32 - if the partition ID says FAT32, but the filesystem, for example 1910c2aaf90fSgd * has less than 65526 clusters. 1911c2aaf90fSgd * 1912c2aaf90fSgd * Without a "force this fs as FAT{12,16,32}" tunable or mount option, it's 1913c2aaf90fSgd * not possible to allow all such mostly-compliant filesystems in unless one 1914c2aaf90fSgd * accepts false positives (definitely invalid filesystems that cause problems 1915c2aaf90fSgd * later). This at least allows to pinpoint why the mount failed. 1916c2aaf90fSgd * 1917c2aaf90fSgd * Due to the use of FAT on removeable media, all relaxations of the rules 1918c2aaf90fSgd * here need to be carefully evaluated wrt. to potential effects on PCFS 1919c2aaf90fSgd * resilience. A faulty/"mis-crafted" filesystem must not cause a panic, so 1920c2aaf90fSgd * beware. 19217c478bd9Sstevel@tonic-gate */ 1922f127cb91Sfrankho static int 1923f127cb91Sfrankho parseBPB(struct pcfs *fsp, uchar_t *bpb, int *valid) 19247c478bd9Sstevel@tonic-gate { 1925f127cb91Sfrankho fattype_t type; 1926f127cb91Sfrankho 1927f127cb91Sfrankho uint32_t ncl; /* number of clusters in file area */ 1928f127cb91Sfrankho uint32_t rec; 1929f127cb91Sfrankho uint32_t reserved; 1930f127cb91Sfrankho uint32_t fsisec, bkbootsec; 1931f127cb91Sfrankho blkcnt_t totsec, totsec16, totsec32, datasec; 1932f127cb91Sfrankho size_t fatsec, fatsec16, fatsec32, rdirsec; 1933f127cb91Sfrankho size_t secsize; 1934f127cb91Sfrankho len_t mediasize; 1935f127cb91Sfrankho uint64_t validflags = 0; 1936f127cb91Sfrankho 1937f127cb91Sfrankho if (VALID_BPBSIG(bpb_get_BPBSig(bpb))) 1938f127cb91Sfrankho validflags |= BPB_BPBSIG_OK; 1939f127cb91Sfrankho 1940f127cb91Sfrankho rec = bpb_get_RootEntCnt(bpb); 1941f127cb91Sfrankho reserved = bpb_get_RsvdSecCnt(bpb); 1942f127cb91Sfrankho fsisec = bpb_get_FSInfo32(bpb); 1943f127cb91Sfrankho bkbootsec = bpb_get_BkBootSec32(bpb); 1944f127cb91Sfrankho totsec16 = (blkcnt_t)bpb_get_TotSec16(bpb); 1945f127cb91Sfrankho totsec32 = (blkcnt_t)bpb_get_TotSec32(bpb); 1946f127cb91Sfrankho fatsec16 = bpb_get_FatSz16(bpb); 1947f127cb91Sfrankho fatsec32 = bpb_get_FatSz32(bpb); 1948f127cb91Sfrankho 1949f127cb91Sfrankho totsec = totsec16 ? totsec16 : totsec32; 1950f127cb91Sfrankho fatsec = fatsec16 ? fatsec16 : fatsec32; 1951f127cb91Sfrankho 1952f127cb91Sfrankho secsize = bpb_get_BytesPerSec(bpb); 1953f127cb91Sfrankho if (!VALID_SECSIZE(secsize)) 1954f127cb91Sfrankho secsize = fsp->pcfs_secsize; 1955f127cb91Sfrankho if (secsize != fsp->pcfs_secsize) { 1956f127cb91Sfrankho PC_DPRINTF3(3, "!pcfs: parseBPB, device (%x.%x):%d:\n", 1957f127cb91Sfrankho getmajor(fsp->pcfs_xdev), 1958f127cb91Sfrankho getminor(fsp->pcfs_xdev), fsp->pcfs_ldrive); 1959f127cb91Sfrankho PC_DPRINTF2(3, "!BPB secsize %d != " 1960f127cb91Sfrankho "autodetected media block size %d\n", 1961f127cb91Sfrankho (int)secsize, (int)fsp->pcfs_secsize); 1962f127cb91Sfrankho if (fsp->pcfs_ldrive) { 1963f127cb91Sfrankho /* 1964f127cb91Sfrankho * We've already attempted to parse the partition 1965f127cb91Sfrankho * table. If the block size used for that don't match 1966f127cb91Sfrankho * the PCFS sector size, we're hosed one way or the 1967f127cb91Sfrankho * other. Just try what happens. 1968f127cb91Sfrankho */ 1969f127cb91Sfrankho secsize = fsp->pcfs_secsize; 1970f127cb91Sfrankho PC_DPRINTF1(3, 1971f127cb91Sfrankho "!pcfs: Using autodetected secsize %d\n", 1972f127cb91Sfrankho (int)secsize); 19737c478bd9Sstevel@tonic-gate } else { 1974f127cb91Sfrankho /* 1975f127cb91Sfrankho * This allows mounting lofi images of PCFS partitions 1976f127cb91Sfrankho * with sectorsize != DEV_BSIZE. We can't parse the 1977f127cb91Sfrankho * partition table on whole-disk images unless the 1978f127cb91Sfrankho * (undocumented) "secsize=..." mount option is used, 1979f127cb91Sfrankho * but at least this allows us to mount if we have 1980f127cb91Sfrankho * an image of a partition. 1981f127cb91Sfrankho */ 1982f127cb91Sfrankho PC_DPRINTF1(3, 1983f127cb91Sfrankho "!pcfs: Using BPB secsize %d\n", (int)secsize); 19847c478bd9Sstevel@tonic-gate } 19857c478bd9Sstevel@tonic-gate } 19860576819eSwyllys 1987f127cb91Sfrankho if (fsp->pcfs_mediasize == 0) { 1988f127cb91Sfrankho mediasize = (len_t)totsec * (len_t)secsize; 1989c2aaf90fSgd /* 1990c2aaf90fSgd * This is not an error because not all devices support the 1991c2aaf90fSgd * dkio(7i) mediasize queries, and/or not all devices are 1992c2aaf90fSgd * partitioned. If we have not been able to figure out the 1993c2aaf90fSgd * size of the underlaying medium, we have to trust the BPB. 1994c2aaf90fSgd */ 1995f127cb91Sfrankho PC_DPRINTF4(3, "!pcfs: parseBPB: mediasize autodetect failed " 1996f127cb91Sfrankho "on device (%x.%x):%d, trusting BPB totsec (%lld Bytes)\n", 1997f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 1998f127cb91Sfrankho fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize); 1999f127cb91Sfrankho } else if ((len_t)totsec * (len_t)secsize > fsp->pcfs_mediasize) { 2000f127cb91Sfrankho cmn_err(CE_WARN, 2001f127cb91Sfrankho "!pcfs: autodetected mediasize (%lld Bytes) smaller than " 2002f127cb91Sfrankho "FAT BPB mediasize (%lld Bytes).\n" 2003f127cb91Sfrankho "truncated filesystem on device (%x.%x):%d, access errors " 2004f127cb91Sfrankho "possible.\n", 2005f127cb91Sfrankho (long long)fsp->pcfs_mediasize, 2006f127cb91Sfrankho (long long)(totsec * (blkcnt_t)secsize), 2007f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 2008f127cb91Sfrankho fsp->pcfs_ldrive); 2009f127cb91Sfrankho mediasize = fsp->pcfs_mediasize; 20107c478bd9Sstevel@tonic-gate } else { 20117c478bd9Sstevel@tonic-gate /* 2012f127cb91Sfrankho * This is actually ok. A FAT needs not occupy the maximum 2013f127cb91Sfrankho * space available in its partition, it can be shorter. 20147c478bd9Sstevel@tonic-gate */ 2015f127cb91Sfrankho mediasize = (len_t)totsec * (len_t)secsize; 20167c478bd9Sstevel@tonic-gate } 20177c478bd9Sstevel@tonic-gate 20187c478bd9Sstevel@tonic-gate /* 2019f127cb91Sfrankho * Since we let just about anything pass through this function, 2020f127cb91Sfrankho * fence against divide-by-zero here. 20217c478bd9Sstevel@tonic-gate */ 2022f127cb91Sfrankho if (secsize) 2023f127cb91Sfrankho rdirsec = roundup(rec * 32, secsize) / secsize; 2024f127cb91Sfrankho else 2025f127cb91Sfrankho rdirsec = 0; 20267c478bd9Sstevel@tonic-gate 20270576819eSwyllys /* 2028f127cb91Sfrankho * This assignment is necessary before pc_dbdaddr() can first be 2029f127cb91Sfrankho * used. Must initialize the value here. 20300576819eSwyllys */ 2031f127cb91Sfrankho fsp->pcfs_secsize = secsize; 2032f127cb91Sfrankho fsp->pcfs_sdshift = ddi_ffs(secsize / DEV_BSIZE) - 1; 2033f127cb91Sfrankho 2034f127cb91Sfrankho fsp->pcfs_mediasize = mediasize; 2035f127cb91Sfrankho 2036f127cb91Sfrankho fsp->pcfs_spcl = bpb_get_SecPerClus(bpb); 2037f127cb91Sfrankho fsp->pcfs_numfat = bpb_get_NumFATs(bpb); 2038f127cb91Sfrankho fsp->pcfs_mediadesc = bpb_get_Media(bpb); 2039f127cb91Sfrankho fsp->pcfs_clsize = secsize * fsp->pcfs_spcl; 2040f127cb91Sfrankho fsp->pcfs_rdirsec = rdirsec; 2041f127cb91Sfrankho 20427c478bd9Sstevel@tonic-gate /* 2043f127cb91Sfrankho * Remember: All PCFS offset calculations in sectors. Before I/O 2044f127cb91Sfrankho * is done, convert to DEV_BSIZE units via pc_dbdaddr(). This is 2045f127cb91Sfrankho * necessary so that media with > 512Byte sector sizes work correctly. 20467c478bd9Sstevel@tonic-gate */ 2047f127cb91Sfrankho fsp->pcfs_fatstart = fsp->pcfs_dosstart + reserved; 2048f127cb91Sfrankho fsp->pcfs_rdirstart = fsp->pcfs_fatstart + fsp->pcfs_numfat * fatsec; 2049f127cb91Sfrankho fsp->pcfs_datastart = fsp->pcfs_rdirstart + rdirsec; 2050f127cb91Sfrankho datasec = totsec - 2051f127cb91Sfrankho (blkcnt_t)fatsec * fsp->pcfs_numfat - 2052f127cb91Sfrankho (blkcnt_t)rdirsec - 2053f127cb91Sfrankho (blkcnt_t)reserved; 2054f127cb91Sfrankho 2055f127cb91Sfrankho DTRACE_PROBE4(fatgeometry, 2056f127cb91Sfrankho blkcnt_t, totsec, size_t, fatsec, 2057f127cb91Sfrankho size_t, rdirsec, blkcnt_t, datasec); 2058f127cb91Sfrankho 20597c478bd9Sstevel@tonic-gate /* 2060c2aaf90fSgd * 'totsec' is taken directly from the BPB and guaranteed to fit 2061c2aaf90fSgd * into a 32bit unsigned integer. The calculation of 'datasec', 2062c2aaf90fSgd * on the other hand, could underflow for incorrect values in 2063c2aaf90fSgd * rdirsec/reserved/fatsec. Check for that. 2064c2aaf90fSgd * We also check that the BPB conforms to the FAT specification's 2065c2aaf90fSgd * requirement that either of the 16/32bit total sector counts 2066c2aaf90fSgd * must be zero. 20677c478bd9Sstevel@tonic-gate */ 2068f127cb91Sfrankho if (totsec != 0 && 2069f127cb91Sfrankho (totsec16 == totsec32 || totsec16 == 0 || totsec32 == 0) && 2070f127cb91Sfrankho datasec < totsec && datasec <= UINT32_MAX) 2071f127cb91Sfrankho validflags |= BPB_TOTSEC_OK; 2072f127cb91Sfrankho 2073c2aaf90fSgd if ((len_t)totsec * (len_t)secsize <= mediasize) 2074f127cb91Sfrankho validflags |= BPB_MEDIASZ_OK; 2075f127cb91Sfrankho 2076f127cb91Sfrankho if (VALID_SECSIZE(secsize)) 2077f127cb91Sfrankho validflags |= BPB_SECSIZE_OK; 2078f127cb91Sfrankho if (VALID_SPCL(fsp->pcfs_spcl)) 2079f127cb91Sfrankho validflags |= BPB_SECPERCLUS_OK; 2080f127cb91Sfrankho if (VALID_CLSIZE(fsp->pcfs_clsize)) 2081f127cb91Sfrankho validflags |= BPB_CLSIZE_OK; 2082f127cb91Sfrankho if (VALID_NUMFATS(fsp->pcfs_numfat)) 2083f127cb91Sfrankho validflags |= BPB_NUMFAT_OK; 2084f127cb91Sfrankho if (VALID_RSVDSEC(reserved) && reserved < totsec) 2085f127cb91Sfrankho validflags |= BPB_RSVDSECCNT_OK; 2086f127cb91Sfrankho if (VALID_MEDIA(fsp->pcfs_mediadesc)) 2087f127cb91Sfrankho validflags |= BPB_MEDIADESC_OK; 2088f127cb91Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig16(bpb))) 2089f127cb91Sfrankho validflags |= BPB_BOOTSIG16_OK; 2090f127cb91Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig32(bpb))) 2091f127cb91Sfrankho validflags |= BPB_BOOTSIG32_OK; 2092f127cb91Sfrankho if (VALID_FSTYPSTR16(bpb_FilSysType16(bpb))) 2093f127cb91Sfrankho validflags |= BPB_FSTYPSTR16_OK; 2094f127cb91Sfrankho if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb))) 2095f127cb91Sfrankho validflags |= BPB_FSTYPSTR32_OK; 2096f127cb91Sfrankho if (VALID_OEMNAME(bpb_OEMName(bpb))) 2097f127cb91Sfrankho validflags |= BPB_OEMNAME_OK; 2098f127cb91Sfrankho if (bkbootsec > 0 && bkbootsec <= reserved && fsisec != bkbootsec) 2099f127cb91Sfrankho validflags |= BPB_BKBOOTSEC_OK; 2100f127cb91Sfrankho if (fsisec > 0 && fsisec <= reserved) 2101f127cb91Sfrankho validflags |= BPB_FSISEC_OK; 2102f127cb91Sfrankho if (VALID_JMPBOOT(bpb_jmpBoot(bpb))) 2103f127cb91Sfrankho validflags |= BPB_JMPBOOT_OK; 2104f127cb91Sfrankho if (VALID_FSVER32(bpb_get_FSVer32(bpb))) 2105f127cb91Sfrankho validflags |= BPB_FSVER_OK; 2106f127cb91Sfrankho if (VALID_VOLLAB(bpb_VolLab16(bpb))) 2107f127cb91Sfrankho validflags |= BPB_VOLLAB16_OK; 2108f127cb91Sfrankho if (VALID_VOLLAB(bpb_VolLab32(bpb))) 2109f127cb91Sfrankho validflags |= BPB_VOLLAB32_OK; 2110f127cb91Sfrankho if (VALID_EXTFLAGS(bpb_get_ExtFlags32(bpb))) 2111f127cb91Sfrankho validflags |= BPB_EXTFLAGS_OK; 21127c478bd9Sstevel@tonic-gate 21137c478bd9Sstevel@tonic-gate /* 2114f127cb91Sfrankho * Try to determine which FAT format to use. 2115f127cb91Sfrankho * 2116f127cb91Sfrankho * Calculate the number of clusters in order to determine 2117f127cb91Sfrankho * the type of FAT we are looking at. This is the only 2118f127cb91Sfrankho * recommended way of determining FAT type, though there 2119f127cb91Sfrankho * are other hints in the data, this is the best way. 2120f127cb91Sfrankho * 2121f127cb91Sfrankho * Since we let just about "anything" pass through this function 2122f127cb91Sfrankho * without early exits, fence against divide-by-zero here. 2123f127cb91Sfrankho * 2124f127cb91Sfrankho * datasec was already validated against UINT32_MAX so we know 2125f127cb91Sfrankho * the result will not overflow the 32bit calculation. 21267c478bd9Sstevel@tonic-gate */ 2127f127cb91Sfrankho if (fsp->pcfs_spcl) 2128f127cb91Sfrankho ncl = (uint32_t)datasec / fsp->pcfs_spcl; 2129f127cb91Sfrankho else 2130f127cb91Sfrankho ncl = 0; 21317c478bd9Sstevel@tonic-gate 2132f127cb91Sfrankho fsp->pcfs_ncluster = ncl; 21337c478bd9Sstevel@tonic-gate 2134f127cb91Sfrankho /* 2135f127cb91Sfrankho * From the Microsoft FAT specification: 2136f127cb91Sfrankho * In the following example, when it says <, it does not mean <=. 2137f127cb91Sfrankho * Note also that the numbers are correct. The first number for 2138f127cb91Sfrankho * FAT12 is 4085; the second number for FAT16 is 65525. These numbers 2139f127cb91Sfrankho * and the '<' signs are not wrong. 2140f127cb91Sfrankho * 2141f127cb91Sfrankho * We "specialdetect" the corner cases, and use at least one "extra" 2142f127cb91Sfrankho * criterion to decide whether it's FAT16 or FAT32 if the cluster 2143f127cb91Sfrankho * count is dangerously close to the boundaries. 2144f127cb91Sfrankho */ 2145f127cb91Sfrankho 2146f127cb91Sfrankho if (ncl <= PCF_FIRSTCLUSTER) { 2147f127cb91Sfrankho type = FAT_UNKNOWN; 2148f127cb91Sfrankho } else if (ncl < 4085) { 2149f127cb91Sfrankho type = FAT12; 2150f127cb91Sfrankho } else if (ncl <= 4096) { 2151f127cb91Sfrankho type = FAT_QUESTIONABLE; 2152f127cb91Sfrankho } else if (ncl < 65525) { 2153f127cb91Sfrankho type = FAT16; 2154f127cb91Sfrankho } else if (ncl <= 65536) { 2155f127cb91Sfrankho type = FAT_QUESTIONABLE; 2156f127cb91Sfrankho } else if (ncl < PCF_LASTCLUSTER32) { 2157f127cb91Sfrankho type = FAT32; 2158f127cb91Sfrankho } else { 2159f127cb91Sfrankho type = FAT_UNKNOWN; 21607c478bd9Sstevel@tonic-gate } 21617c478bd9Sstevel@tonic-gate 2162f127cb91Sfrankho DTRACE_PROBE4(parseBPB__initial, 2163f127cb91Sfrankho struct pcfs *, fsp, unsigned char *, bpb, 2164f127cb91Sfrankho int, validflags, fattype_t, type); 21657c478bd9Sstevel@tonic-gate 2166f127cb91Sfrankho recheck: 2167f127cb91Sfrankho fsp->pcfs_fatsec = fatsec; 2168f127cb91Sfrankho 2169f127cb91Sfrankho /* Do some final sanity checks for each specific type of FAT */ 2170f127cb91Sfrankho switch (type) { 2171f127cb91Sfrankho case FAT12: 2172f127cb91Sfrankho if (rec != 0) 2173f127cb91Sfrankho validflags |= BPB_ROOTENTCNT_OK; 2174f127cb91Sfrankho if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec || 2175f127cb91Sfrankho bpb_get_TotSec16(bpb) == 0) 2176f127cb91Sfrankho validflags |= BPB_TOTSEC16_OK; 2177f127cb91Sfrankho if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec || 2178f127cb91Sfrankho bpb_get_TotSec32(bpb) == 0) 2179f127cb91Sfrankho validflags |= BPB_TOTSEC32_OK; 2180f127cb91Sfrankho if (bpb_get_FatSz16(bpb) == fatsec) 2181f127cb91Sfrankho validflags |= BPB_FATSZ16_OK; 2182985bfda7SMichael Bergknoff if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER) 2183985bfda7SMichael Bergknoff * 3 / 2) 2184f127cb91Sfrankho validflags |= BPB_FATSZ_OK; 2185f127cb91Sfrankho if (ncl < 4085) 2186f127cb91Sfrankho validflags |= BPB_NCLUSTERS_OK; 2187f127cb91Sfrankho 2188f127cb91Sfrankho fsp->pcfs_lastclmark = (PCF_LASTCLUSTER & 0xfff); 2189f127cb91Sfrankho fsp->pcfs_rootblksize = 2190f127cb91Sfrankho fsp->pcfs_rdirsec * secsize; 2191f127cb91Sfrankho fsp->pcfs_fsistart = 0; 2192f127cb91Sfrankho 2193f127cb91Sfrankho if ((validflags & FAT12_VALIDMSK) != FAT12_VALIDMSK) 2194f127cb91Sfrankho type = FAT_UNKNOWN; 2195f127cb91Sfrankho break; 2196f127cb91Sfrankho case FAT16: 2197f127cb91Sfrankho if (rec != 0) 2198f127cb91Sfrankho validflags |= BPB_ROOTENTCNT_OK; 2199f127cb91Sfrankho if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec || 2200f127cb91Sfrankho bpb_get_TotSec16(bpb) == 0) 2201f127cb91Sfrankho validflags |= BPB_TOTSEC16_OK; 2202f127cb91Sfrankho if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec || 2203f127cb91Sfrankho bpb_get_TotSec32(bpb) == 0) 2204f127cb91Sfrankho validflags |= BPB_TOTSEC32_OK; 2205f127cb91Sfrankho if (bpb_get_FatSz16(bpb) == fatsec) 2206f127cb91Sfrankho validflags |= BPB_FATSZ16_OK; 2207985bfda7SMichael Bergknoff if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER) * 2) 2208f127cb91Sfrankho validflags |= BPB_FATSZ_OK; 2209f127cb91Sfrankho if (ncl >= 4085 && ncl < 65525) 2210f127cb91Sfrankho validflags |= BPB_NCLUSTERS_OK; 2211f127cb91Sfrankho 2212f127cb91Sfrankho fsp->pcfs_lastclmark = PCF_LASTCLUSTER; 2213f127cb91Sfrankho fsp->pcfs_rootblksize = 2214f127cb91Sfrankho fsp->pcfs_rdirsec * secsize; 2215f127cb91Sfrankho fsp->pcfs_fsistart = 0; 2216f127cb91Sfrankho 2217f127cb91Sfrankho if ((validflags & FAT16_VALIDMSK) != FAT16_VALIDMSK) 2218f127cb91Sfrankho type = FAT_UNKNOWN; 2219f127cb91Sfrankho break; 2220f127cb91Sfrankho case FAT32: 2221f127cb91Sfrankho if (rec == 0) 2222f127cb91Sfrankho validflags |= BPB_ROOTENTCNT_OK; 2223f127cb91Sfrankho if (bpb_get_TotSec16(bpb) == 0) 2224f127cb91Sfrankho validflags |= BPB_TOTSEC16_OK; 2225f127cb91Sfrankho if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec) 2226f127cb91Sfrankho validflags |= BPB_TOTSEC32_OK; 2227f127cb91Sfrankho if (bpb_get_FatSz16(bpb) == 0) 2228f127cb91Sfrankho validflags |= BPB_FATSZ16_OK; 2229f127cb91Sfrankho if (bpb_get_FatSz32(bpb) == fatsec) 2230f127cb91Sfrankho validflags |= BPB_FATSZ32_OK; 2231985bfda7SMichael Bergknoff if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER) * 4) 2232f127cb91Sfrankho validflags |= BPB_FATSZ_OK; 2233f127cb91Sfrankho if (ncl >= 65525 && ncl < PCF_LASTCLUSTER32) 2234f127cb91Sfrankho validflags |= BPB_NCLUSTERS_OK; 2235f127cb91Sfrankho 2236f127cb91Sfrankho fsp->pcfs_lastclmark = PCF_LASTCLUSTER32; 2237f127cb91Sfrankho fsp->pcfs_rootblksize = fsp->pcfs_clsize; 2238f127cb91Sfrankho fsp->pcfs_fsistart = fsp->pcfs_dosstart + fsisec; 2239f127cb91Sfrankho if (validflags & BPB_FSISEC_OK) 2240f127cb91Sfrankho fsp->pcfs_flags |= PCFS_FSINFO_OK; 2241f127cb91Sfrankho fsp->pcfs_rootclnum = bpb_get_RootClus32(bpb); 2242f127cb91Sfrankho if (pc_validcl(fsp, fsp->pcfs_rootclnum)) 2243f127cb91Sfrankho validflags |= BPB_ROOTCLUSTER_OK; 2244f127cb91Sfrankho 2245f127cb91Sfrankho /* 2246f127cb91Sfrankho * Current PCFS code only works if 'pcfs_rdirstart' 2247f127cb91Sfrankho * contains the root cluster number on FAT32. 2248f127cb91Sfrankho * That's a mis-use and would better be changed. 2249f127cb91Sfrankho */ 2250f127cb91Sfrankho fsp->pcfs_rdirstart = (daddr_t)fsp->pcfs_rootclnum; 2251f127cb91Sfrankho 2252f127cb91Sfrankho if ((validflags & FAT32_VALIDMSK) != FAT32_VALIDMSK) 2253f127cb91Sfrankho type = FAT_UNKNOWN; 2254f127cb91Sfrankho break; 2255f127cb91Sfrankho case FAT_QUESTIONABLE: 2256f127cb91Sfrankho type = secondaryBPBChecks(fsp, bpb, secsize); 2257f127cb91Sfrankho goto recheck; 2258f127cb91Sfrankho default: 2259f127cb91Sfrankho ASSERT(type == FAT_UNKNOWN); 2260f127cb91Sfrankho break; 22617c478bd9Sstevel@tonic-gate } 22627c478bd9Sstevel@tonic-gate 2263f127cb91Sfrankho ASSERT(type != FAT_QUESTIONABLE); 22647c478bd9Sstevel@tonic-gate 2265f127cb91Sfrankho fsp->pcfs_fattype = type; 22667c478bd9Sstevel@tonic-gate 2267f127cb91Sfrankho if (valid) 2268f127cb91Sfrankho *valid = validflags; 2269f127cb91Sfrankho 2270f127cb91Sfrankho DTRACE_PROBE4(parseBPB__final, 2271f127cb91Sfrankho struct pcfs *, fsp, unsigned char *, bpb, 2272f127cb91Sfrankho int, validflags, fattype_t, type); 2273f127cb91Sfrankho 2274f127cb91Sfrankho if (type != FAT_UNKNOWN) { 2275f127cb91Sfrankho ASSERT((secsize & (DEV_BSIZE - 1)) == 0); 2276f127cb91Sfrankho ASSERT(ISP2(secsize / DEV_BSIZE)); 2277f127cb91Sfrankho return (1); 22787c478bd9Sstevel@tonic-gate } 2279f127cb91Sfrankho 2280f127cb91Sfrankho return (0); 22817c478bd9Sstevel@tonic-gate } 22827c478bd9Sstevel@tonic-gate 2283f127cb91Sfrankho 2284f127cb91Sfrankho /* 2285f127cb91Sfrankho * Detect the device's native block size (sector size). 2286f127cb91Sfrankho * 2287f127cb91Sfrankho * Test whether the device is: 2288f127cb91Sfrankho * - a floppy device from a known controller type via DKIOCINFO 2289f127cb91Sfrankho * - a real floppy using the fd(7d) driver and capable of fdio(7I) ioctls 2290f127cb91Sfrankho * - a USB floppy drive (identified by drive geometry) 2291f127cb91Sfrankho * 2292f127cb91Sfrankho * Detecting a floppy will make PCFS metadata updates on such media synchronous, 2293f127cb91Sfrankho * to minimize risks due to slow I/O and user hotplugging / device ejection. 2294f127cb91Sfrankho * 2295f127cb91Sfrankho * This might be a bit wasteful on kernel stack space; if anyone's 2296f127cb91Sfrankho * bothered by this, kmem_alloc/kmem_free the ioctl arguments... 2297f127cb91Sfrankho */ 2298f127cb91Sfrankho static void 2299f127cb91Sfrankho pcfs_device_getinfo(struct pcfs *fsp) 23007c478bd9Sstevel@tonic-gate { 2301f127cb91Sfrankho dev_t rdev = fsp->pcfs_xdev; 2302f127cb91Sfrankho int error; 2303f127cb91Sfrankho union { 2304f127cb91Sfrankho struct dk_minfo mi; 2305f127cb91Sfrankho struct dk_cinfo ci; 2306f127cb91Sfrankho struct dk_geom gi; 2307f127cb91Sfrankho struct fd_char fc; 2308f127cb91Sfrankho } arg; /* save stackspace ... */ 2309f127cb91Sfrankho intptr_t argp = (intptr_t)&arg; 2310f127cb91Sfrankho ldi_handle_t lh; 2311f127cb91Sfrankho ldi_ident_t li; 2312f127cb91Sfrankho int isfloppy, isremoveable, ishotpluggable; 2313f127cb91Sfrankho cred_t *cr = CRED(); 2314f127cb91Sfrankho 2315f127cb91Sfrankho if (ldi_ident_from_dev(rdev, &li)) 2316f127cb91Sfrankho goto out; 2317f127cb91Sfrankho 2318f127cb91Sfrankho error = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, cr, &lh, li); 2319f127cb91Sfrankho ldi_ident_release(li); 2320f127cb91Sfrankho if (error) 2321f127cb91Sfrankho goto out; 23227c478bd9Sstevel@tonic-gate 23237c478bd9Sstevel@tonic-gate /* 2324f127cb91Sfrankho * Not sure if this could possibly happen. It'd be a bit like 2325f127cb91Sfrankho * VOP_OPEN() changing the passed-in vnode ptr. We're just not 2326f127cb91Sfrankho * expecting it, needs some thought if triggered ... 23277c478bd9Sstevel@tonic-gate */ 2328f127cb91Sfrankho ASSERT(fsp->pcfs_xdev == rdev); 2329f127cb91Sfrankho 2330f127cb91Sfrankho /* 2331f127cb91Sfrankho * Check for removeable/hotpluggable media. 2332f127cb91Sfrankho */ 2333f127cb91Sfrankho if (ldi_ioctl(lh, DKIOCREMOVABLE, 2334f127cb91Sfrankho (intptr_t)&isremoveable, FKIOCTL, cr, NULL)) { 2335f127cb91Sfrankho isremoveable = 0; 23367c478bd9Sstevel@tonic-gate } 2337f127cb91Sfrankho if (ldi_ioctl(lh, DKIOCHOTPLUGGABLE, 2338f127cb91Sfrankho (intptr_t)&ishotpluggable, FKIOCTL, cr, NULL)) { 2339f127cb91Sfrankho ishotpluggable = 0; 2340f127cb91Sfrankho } 2341f127cb91Sfrankho 2342f127cb91Sfrankho /* 2343f127cb91Sfrankho * Make sure we don't use "half-initialized" values if the ioctls fail. 2344f127cb91Sfrankho */ 2345f127cb91Sfrankho if (ldi_ioctl(lh, DKIOCGMEDIAINFO, argp, FKIOCTL, cr, NULL)) { 2346f127cb91Sfrankho bzero(&arg, sizeof (arg)); 2347f127cb91Sfrankho fsp->pcfs_mediasize = 0; 2348f127cb91Sfrankho } else { 2349f127cb91Sfrankho fsp->pcfs_mediasize = 2350f127cb91Sfrankho (len_t)arg.mi.dki_lbsize * 2351f127cb91Sfrankho (len_t)arg.mi.dki_capacity; 2352f127cb91Sfrankho } 2353f127cb91Sfrankho 2354f127cb91Sfrankho if (VALID_SECSIZE(arg.mi.dki_lbsize)) { 2355f127cb91Sfrankho if (fsp->pcfs_secsize == 0) { 2356f127cb91Sfrankho fsp->pcfs_secsize = arg.mi.dki_lbsize; 2357f127cb91Sfrankho fsp->pcfs_sdshift = 2358f127cb91Sfrankho ddi_ffs(arg.mi.dki_lbsize / DEV_BSIZE) - 1; 2359f127cb91Sfrankho } else { 2360f127cb91Sfrankho PC_DPRINTF4(1, "!pcfs: autodetected media block size " 2361f127cb91Sfrankho "%d, device (%x.%x), different from user-provided " 2362f127cb91Sfrankho "%d. User override - ignoring autodetect result.\n", 2363f127cb91Sfrankho arg.mi.dki_lbsize, 2364f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 2365f127cb91Sfrankho fsp->pcfs_secsize); 23667c478bd9Sstevel@tonic-gate } 2367f127cb91Sfrankho } else if (arg.mi.dki_lbsize) { 2368f127cb91Sfrankho PC_DPRINTF3(1, "!pcfs: autodetected media block size " 2369f127cb91Sfrankho "%d, device (%x.%x), invalid (not 512, 1024, 2048, 4096). " 2370f127cb91Sfrankho "Ignoring autodetect result.\n", 2371f127cb91Sfrankho arg.mi.dki_lbsize, 2372f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev)); 23737c478bd9Sstevel@tonic-gate } 23747c478bd9Sstevel@tonic-gate 2375f127cb91Sfrankho /* 2376f127cb91Sfrankho * We treat the following media types as a floppy by default. 2377f127cb91Sfrankho */ 2378f127cb91Sfrankho isfloppy = 2379f127cb91Sfrankho (arg.mi.dki_media_type == DK_FLOPPY || 2380f127cb91Sfrankho arg.mi.dki_media_type == DK_ZIP || 2381f127cb91Sfrankho arg.mi.dki_media_type == DK_JAZ); 23827c478bd9Sstevel@tonic-gate 23837c478bd9Sstevel@tonic-gate /* 2384f127cb91Sfrankho * if this device understands fdio(7I) requests it's 2385f127cb91Sfrankho * obviously a floppy drive. 23867c478bd9Sstevel@tonic-gate */ 2387f127cb91Sfrankho if (!isfloppy && 2388f127cb91Sfrankho !ldi_ioctl(lh, FDIOGCHAR, argp, FKIOCTL, cr, NULL)) 2389f127cb91Sfrankho isfloppy = 1; 2390f127cb91Sfrankho 23917c478bd9Sstevel@tonic-gate /* 239269ed0c8eSGarrett D'Amore * some devices we like to treat as floppies, but they don't 239369ed0c8eSGarrett D'Amore * understand fdio(7I) requests. 23947c478bd9Sstevel@tonic-gate */ 2395f127cb91Sfrankho if (!isfloppy && 2396f127cb91Sfrankho !ldi_ioctl(lh, DKIOCINFO, argp, FKIOCTL, cr, NULL) && 2397f127cb91Sfrankho (arg.ci.dki_ctype == DKC_WDC2880 || 2398f127cb91Sfrankho arg.ci.dki_ctype == DKC_NCRFLOPPY || 2399f127cb91Sfrankho arg.ci.dki_ctype == DKC_SMSFLOPPY || 240069ed0c8eSGarrett D'Amore arg.ci.dki_ctype == DKC_INTEL82077)) 2401f127cb91Sfrankho isfloppy = 1; 24027c478bd9Sstevel@tonic-gate 24037c478bd9Sstevel@tonic-gate /* 2404f127cb91Sfrankho * This is the "final fallback" test - media with 2405f127cb91Sfrankho * 2 heads and 80 cylinders are assumed to be floppies. 2406f127cb91Sfrankho * This is normally true for USB floppy drives ... 24077c478bd9Sstevel@tonic-gate */ 2408f127cb91Sfrankho if (!isfloppy && 2409f127cb91Sfrankho !ldi_ioctl(lh, DKIOCGGEOM, argp, FKIOCTL, cr, NULL) && 2410f127cb91Sfrankho (arg.gi.dkg_ncyl == 80 && arg.gi.dkg_nhead == 2)) 2411f127cb91Sfrankho isfloppy = 1; 24127c478bd9Sstevel@tonic-gate 2413f127cb91Sfrankho /* 2414f127cb91Sfrankho * This is similar to the "old" PCFS code that sets this flag 2415f127cb91Sfrankho * just based on the media descriptor being 0xf8 (MD_FIXED). 2416f127cb91Sfrankho * Should be re-worked. We really need some specialcasing for 2417f127cb91Sfrankho * removeable media. 2418f127cb91Sfrankho */ 2419f127cb91Sfrankho if (!isfloppy) { 2420f127cb91Sfrankho fsp->pcfs_flags |= PCFS_NOCHK; 2421f127cb91Sfrankho } 2422f127cb91Sfrankho 2423f127cb91Sfrankho /* 2424f127cb91Sfrankho * We automatically disable access time updates if the medium is 2425f127cb91Sfrankho * removeable and/or hotpluggable, and the admin did not explicitly 2426f127cb91Sfrankho * request access time updates (via the "atime" mount option). 2427f127cb91Sfrankho * The majority of flash-based media should fit this category. 2428f127cb91Sfrankho * Minimizing write access extends the lifetime of your memory stick ! 2429f127cb91Sfrankho */ 2430f127cb91Sfrankho if (!vfs_optionisset(fsp->pcfs_vfs, MNTOPT_ATIME, NULL) && 2431f127cb91Sfrankho (isremoveable || ishotpluggable | isfloppy)) { 2432f127cb91Sfrankho fsp->pcfs_flags |= PCFS_NOATIME; 2433f127cb91Sfrankho } 2434f127cb91Sfrankho 2435f127cb91Sfrankho (void) ldi_close(lh, FREAD, cr); 2436f127cb91Sfrankho out: 2437f127cb91Sfrankho if (fsp->pcfs_secsize == 0) { 2438f127cb91Sfrankho PC_DPRINTF3(1, "!pcfs: media block size autodetection " 2439f127cb91Sfrankho "device (%x.%x) failed, no user-provided fallback. " 2440f127cb91Sfrankho "Using %d bytes.\n", 2441f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 2442f127cb91Sfrankho DEV_BSIZE); 2443f127cb91Sfrankho fsp->pcfs_secsize = DEV_BSIZE; 2444f127cb91Sfrankho fsp->pcfs_sdshift = 0; 2445f127cb91Sfrankho } 2446f127cb91Sfrankho ASSERT(fsp->pcfs_secsize % DEV_BSIZE == 0); 2447f127cb91Sfrankho ASSERT(VALID_SECSIZE(fsp->pcfs_secsize)); 24487c478bd9Sstevel@tonic-gate } 24497c478bd9Sstevel@tonic-gate 24507c478bd9Sstevel@tonic-gate /* 2451f127cb91Sfrankho * Get the FAT type for the DOS medium. 2452f127cb91Sfrankho * 2453f127cb91Sfrankho * ------------------------- 2454f127cb91Sfrankho * According to Microsoft: 2455f127cb91Sfrankho * The FAT type one of FAT12, FAT16, or FAT32 is determined by the 2456f127cb91Sfrankho * count of clusters on the volume and nothing else. 2457f127cb91Sfrankho * ------------------------- 2458f127cb91Sfrankho * 24597c478bd9Sstevel@tonic-gate */ 24607c478bd9Sstevel@tonic-gate static int 2461f127cb91Sfrankho pc_getfattype(struct pcfs *fsp) 24627c478bd9Sstevel@tonic-gate { 2463f127cb91Sfrankho int error = 0; 2464f127cb91Sfrankho buf_t *bp = NULL; 2465f127cb91Sfrankho struct vnode *devvp = fsp->pcfs_devvp; 2466f127cb91Sfrankho dev_t dev = devvp->v_rdev; 24677c478bd9Sstevel@tonic-gate 2468f127cb91Sfrankho /* 2469f127cb91Sfrankho * Detect the native block size of the medium, and attempt to 2470f127cb91Sfrankho * detect whether the medium is removeable. 247169ed0c8eSGarrett D'Amore * We do treat removable media (floppies, USB and FireWire disks) 247269ed0c8eSGarrett D'Amore * differently wrt. to the frequency and synchronicity of FAT updates. 2473f127cb91Sfrankho * We need to know the media block size in order to be able to 2474f127cb91Sfrankho * parse the partition table. 2475f127cb91Sfrankho */ 2476f127cb91Sfrankho pcfs_device_getinfo(fsp); 24777c478bd9Sstevel@tonic-gate 24787c478bd9Sstevel@tonic-gate /* 2479f127cb91Sfrankho * Unpartitioned media (floppies and some removeable devices) 2480f127cb91Sfrankho * don't have a partition table, the FAT BPB is at disk block 0. 2481f127cb91Sfrankho * Start out by reading block 0. 24827c478bd9Sstevel@tonic-gate */ 2483f127cb91Sfrankho fsp->pcfs_dosstart = 0; 2484f127cb91Sfrankho bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), fsp->pcfs_secsize); 2485f127cb91Sfrankho 2486f127cb91Sfrankho if (error = geterror(bp)) 2487f127cb91Sfrankho goto out; 2488f127cb91Sfrankho 24897c478bd9Sstevel@tonic-gate /* 2490f127cb91Sfrankho * If a logical drive number is requested, parse the partition table 2491f127cb91Sfrankho * and attempt to locate it. Otherwise, proceed immediately to the 2492f127cb91Sfrankho * BPB check. findTheDrive(), if successful, returns the disk block 2493f127cb91Sfrankho * number where the requested partition starts in "startsec". 24947c478bd9Sstevel@tonic-gate */ 2495f127cb91Sfrankho if (fsp->pcfs_ldrive != 0) { 2496f127cb91Sfrankho PC_DPRINTF3(5, "!pcfs: pc_getfattype: using FDISK table on " 2497f127cb91Sfrankho "device (%x,%x):%d to find BPB\n", 2498f127cb91Sfrankho getmajor(dev), getminor(dev), fsp->pcfs_ldrive); 2499f127cb91Sfrankho 2500f127cb91Sfrankho if (error = findTheDrive(fsp, &bp)) 2501f127cb91Sfrankho goto out; 2502f127cb91Sfrankho 2503f127cb91Sfrankho ASSERT(fsp->pcfs_dosstart != 0); 2504f127cb91Sfrankho 25057c478bd9Sstevel@tonic-gate brelse(bp); 2506f127cb91Sfrankho bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), 2507f127cb91Sfrankho fsp->pcfs_secsize); 2508f127cb91Sfrankho if (error = geterror(bp)) 2509f127cb91Sfrankho goto out; 25107c478bd9Sstevel@tonic-gate } 2511f127cb91Sfrankho 2512f127cb91Sfrankho /* 2513f127cb91Sfrankho * Validate the BPB and fill in the instance structure. 2514f127cb91Sfrankho */ 2515f127cb91Sfrankho if (!parseBPB(fsp, (uchar_t *)bp->b_un.b_addr, NULL)) { 2516f127cb91Sfrankho PC_DPRINTF4(1, "!pcfs: pc_getfattype: No FAT BPB on " 2517f127cb91Sfrankho "device (%x.%x):%d, disk LBA %u\n", 2518f127cb91Sfrankho getmajor(dev), getminor(dev), fsp->pcfs_ldrive, 2519f127cb91Sfrankho (uint_t)pc_dbdaddr(fsp, fsp->pcfs_dosstart)); 2520f127cb91Sfrankho error = EINVAL; 2521f127cb91Sfrankho goto out; 25227c478bd9Sstevel@tonic-gate } 2523f127cb91Sfrankho 2524f127cb91Sfrankho ASSERT(fsp->pcfs_fattype != FAT_UNKNOWN); 2525f127cb91Sfrankho 2526f127cb91Sfrankho out: 2527f127cb91Sfrankho /* 2528f127cb91Sfrankho * Release the buffer used 2529f127cb91Sfrankho */ 2530f127cb91Sfrankho if (bp != NULL) 2531f127cb91Sfrankho brelse(bp); 2532f127cb91Sfrankho return (error); 25337c478bd9Sstevel@tonic-gate } 25347c478bd9Sstevel@tonic-gate 2535f127cb91Sfrankho 25367c478bd9Sstevel@tonic-gate /* 2537f127cb91Sfrankho * Get the file allocation table. 2538f127cb91Sfrankho * If there is an old FAT, invalidate it. 25397c478bd9Sstevel@tonic-gate */ 2540f127cb91Sfrankho int 2541f127cb91Sfrankho pc_getfat(struct pcfs *fsp) 25427c478bd9Sstevel@tonic-gate { 2543f127cb91Sfrankho struct buf *bp = NULL; 2544f127cb91Sfrankho uchar_t *fatp = NULL; 2545f127cb91Sfrankho uchar_t *fat_changemap = NULL; 2546f127cb91Sfrankho int error; 2547f127cb91Sfrankho int fat_changemapsize; 2548f127cb91Sfrankho int flags = 0; 2549f127cb91Sfrankho int nfat; 2550f127cb91Sfrankho int altfat_mustmatch = 0; 2551f127cb91Sfrankho int fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize; 25527c478bd9Sstevel@tonic-gate 2553f127cb91Sfrankho if (fsp->pcfs_fatp) { 2554f127cb91Sfrankho /* 2555f127cb91Sfrankho * There is a FAT in core. 2556f127cb91Sfrankho * If there are open file pcnodes or we have modified it or 2557f127cb91Sfrankho * it hasn't timed out yet use the in core FAT. 2558f127cb91Sfrankho * Otherwise invalidate it and get a new one 2559f127cb91Sfrankho */ 2560f127cb91Sfrankho #ifdef notdef 2561f127cb91Sfrankho if (fsp->pcfs_frefs || 2562f127cb91Sfrankho (fsp->pcfs_flags & PCFS_FATMOD) || 2563f127cb91Sfrankho (gethrestime_sec() < fsp->pcfs_fattime)) { 2564f127cb91Sfrankho return (0); 2565f127cb91Sfrankho } else { 2566f127cb91Sfrankho mutex_enter(&pcfslock); 2567f127cb91Sfrankho pc_invalfat(fsp); 2568f127cb91Sfrankho mutex_exit(&pcfslock); 2569f127cb91Sfrankho } 2570f127cb91Sfrankho #endif /* notdef */ 25717c478bd9Sstevel@tonic-gate return (0); 25727c478bd9Sstevel@tonic-gate } 25737c478bd9Sstevel@tonic-gate 2574f127cb91Sfrankho /* 2575f127cb91Sfrankho * Get FAT and check it for validity 2576f127cb91Sfrankho */ 2577f127cb91Sfrankho fatp = kmem_alloc(fatsize, KM_SLEEP); 2578f127cb91Sfrankho error = pc_readfat(fsp, fatp); 2579f127cb91Sfrankho if (error) { 2580f127cb91Sfrankho flags = B_ERROR; 2581f127cb91Sfrankho goto out; 25827c478bd9Sstevel@tonic-gate } 2583f127cb91Sfrankho fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1; 2584f127cb91Sfrankho fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP); 2585f127cb91Sfrankho fsp->pcfs_fatp = fatp; 2586f127cb91Sfrankho fsp->pcfs_fat_changemapsize = fat_changemapsize; 2587f127cb91Sfrankho fsp->pcfs_fat_changemap = fat_changemap; 25887c478bd9Sstevel@tonic-gate 2589f127cb91Sfrankho /* 2590f127cb91Sfrankho * The only definite signature check is that the 2591f127cb91Sfrankho * media descriptor byte should match the first byte 2592f127cb91Sfrankho * of the FAT block. 2593f127cb91Sfrankho */ 2594f127cb91Sfrankho if (fatp[0] != fsp->pcfs_mediadesc) { 2595f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: FAT signature mismatch, " 2596f127cb91Sfrankho "media descriptor %x, FAT[0] lowbyte %x\n", 2597f127cb91Sfrankho (uint32_t)fsp->pcfs_mediadesc, (uint32_t)fatp[0]); 2598f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: Enforcing alternate FAT validation\n"); 2599f127cb91Sfrankho altfat_mustmatch = 1; 26007c478bd9Sstevel@tonic-gate } 26017c478bd9Sstevel@tonic-gate 2602f127cb91Sfrankho /* 2603f127cb91Sfrankho * Get alternate FATs and check for consistency 2604f127cb91Sfrankho * This is an inlined version of pc_readfat(). 2605f127cb91Sfrankho * Since we're only comparing FAT and alternate FAT, 2606f127cb91Sfrankho * there's no reason to let pc_readfat() copy data out 2607f127cb91Sfrankho * of the buf. Instead, compare in-situ, one cluster 2608f127cb91Sfrankho * at a time. 2609f127cb91Sfrankho */ 2610f127cb91Sfrankho for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) { 2611f127cb91Sfrankho size_t startsec; 2612f127cb91Sfrankho size_t off; 26137c478bd9Sstevel@tonic-gate 2614f127cb91Sfrankho startsec = pc_dbdaddr(fsp, 2615f127cb91Sfrankho fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec); 26167c478bd9Sstevel@tonic-gate 2617f127cb91Sfrankho for (off = 0; off < fatsize; off += fsp->pcfs_clsize) { 2618f127cb91Sfrankho daddr_t fatblk = startsec + pc_dbdaddr(fsp, 2619f127cb91Sfrankho pc_cltodb(fsp, pc_lblkno(fsp, off))); 26207c478bd9Sstevel@tonic-gate 2621f127cb91Sfrankho bp = bread(fsp->pcfs_xdev, fatblk, 2622f127cb91Sfrankho MIN(fsp->pcfs_clsize, fatsize - off)); 2623f127cb91Sfrankho if (bp->b_flags & (B_ERROR | B_STALE)) { 2624f127cb91Sfrankho cmn_err(CE_NOTE, 2625f127cb91Sfrankho "!pcfs: alternate FAT #%d (start LBA %p)" 2626f127cb91Sfrankho " read error at offset %ld on device" 2627f127cb91Sfrankho " (%x.%x):%d", 2628f127cb91Sfrankho nfat, (void *)(uintptr_t)startsec, off, 2629f127cb91Sfrankho getmajor(fsp->pcfs_xdev), 2630f127cb91Sfrankho getminor(fsp->pcfs_xdev), 2631f127cb91Sfrankho fsp->pcfs_ldrive); 2632f127cb91Sfrankho flags = B_ERROR; 2633f127cb91Sfrankho error = EIO; 2634f127cb91Sfrankho goto out; 2635f127cb91Sfrankho } 2636f127cb91Sfrankho bp->b_flags |= B_STALE | B_AGE; 2637f127cb91Sfrankho if (bcmp(bp->b_un.b_addr, fatp + off, 2638f127cb91Sfrankho MIN(fsp->pcfs_clsize, fatsize - off))) { 2639f127cb91Sfrankho cmn_err(CE_NOTE, 2640f127cb91Sfrankho "!pcfs: alternate FAT #%d (start LBA %p)" 2641f127cb91Sfrankho " corrupted at offset %ld on device" 2642f127cb91Sfrankho " (%x.%x):%d", 2643f127cb91Sfrankho nfat, (void *)(uintptr_t)startsec, off, 2644f127cb91Sfrankho getmajor(fsp->pcfs_xdev), 2645f127cb91Sfrankho getminor(fsp->pcfs_xdev), 2646f127cb91Sfrankho fsp->pcfs_ldrive); 2647f127cb91Sfrankho if (altfat_mustmatch) { 2648f127cb91Sfrankho flags = B_ERROR; 2649f127cb91Sfrankho error = EIO; 2650f127cb91Sfrankho goto out; 2651f127cb91Sfrankho } 2652f127cb91Sfrankho } 2653f127cb91Sfrankho brelse(bp); 2654f127cb91Sfrankho bp = NULL; /* prevent double release */ 26557c478bd9Sstevel@tonic-gate } 26567c478bd9Sstevel@tonic-gate } 26577c478bd9Sstevel@tonic-gate 2658f127cb91Sfrankho fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT; 2659f127cb91Sfrankho fsp->pcfs_fatjustread = 1; 26607c478bd9Sstevel@tonic-gate 2661f127cb91Sfrankho /* 2662f127cb91Sfrankho * Retrieve FAT32 fsinfo sector. 2663f127cb91Sfrankho * A failure to read this is not fatal to accessing the volume. 2664f127cb91Sfrankho * It simply means operations that count or search free blocks 2665f127cb91Sfrankho * will have to do a full FAT walk, vs. a possibly quicker lookup 2666f127cb91Sfrankho * of the summary information. 2667f127cb91Sfrankho * Hence, we log a message but return success overall after this point. 2668f127cb91Sfrankho */ 2669f127cb91Sfrankho if (IS_FAT32(fsp) && (fsp->pcfs_flags & PCFS_FSINFO_OK)) { 2670f127cb91Sfrankho struct fat_od_fsi *fsinfo_disk; 26717c478bd9Sstevel@tonic-gate 2672f127cb91Sfrankho bp = bread(fsp->pcfs_xdev, 2673f127cb91Sfrankho pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize); 2674f127cb91Sfrankho fsinfo_disk = (struct fat_od_fsi *)bp->b_un.b_addr; 2675f127cb91Sfrankho if (bp->b_flags & (B_ERROR | B_STALE) || 2676f127cb91Sfrankho !FSISIG_OK(fsinfo_disk)) { 2677f127cb91Sfrankho cmn_err(CE_NOTE, 2678f127cb91Sfrankho "!pcfs: error reading fat32 fsinfo from " 2679f127cb91Sfrankho "device (%x.%x):%d, block %lld", 2680f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 2681f127cb91Sfrankho fsp->pcfs_ldrive, 2682f127cb91Sfrankho (long long)pc_dbdaddr(fsp, fsp->pcfs_fsistart)); 2683f127cb91Sfrankho fsp->pcfs_flags &= ~PCFS_FSINFO_OK; 2684f127cb91Sfrankho fsp->pcfs_fsinfo.fs_free_clusters = FSINFO_UNKNOWN; 2685f127cb91Sfrankho fsp->pcfs_fsinfo.fs_next_free = FSINFO_UNKNOWN; 2686f127cb91Sfrankho } else { 2687f127cb91Sfrankho bp->b_flags |= B_STALE | B_AGE; 2688f127cb91Sfrankho fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr); 2689f127cb91Sfrankho fsp->pcfs_fsinfo.fs_free_clusters = 2690f127cb91Sfrankho LE_32(fsinfo_disk->fsi_incore.fs_free_clusters); 2691f127cb91Sfrankho fsp->pcfs_fsinfo.fs_next_free = 2692f127cb91Sfrankho LE_32(fsinfo_disk->fsi_incore.fs_next_free); 26937c478bd9Sstevel@tonic-gate } 2694f127cb91Sfrankho brelse(bp); 2695f127cb91Sfrankho bp = NULL; 26967c478bd9Sstevel@tonic-gate } 2697264a6e74Sfrankho 2698f127cb91Sfrankho if (pc_validcl(fsp, (pc_cluster32_t)fsp->pcfs_fsinfo.fs_next_free)) 2699f127cb91Sfrankho fsp->pcfs_nxfrecls = fsp->pcfs_fsinfo.fs_next_free; 2700f127cb91Sfrankho else 2701f127cb91Sfrankho fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER; 2702264a6e74Sfrankho 2703f127cb91Sfrankho return (0); 2704264a6e74Sfrankho 2705f127cb91Sfrankho out: 2706f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: illegal disk format"); 2707f127cb91Sfrankho if (bp) 2708f127cb91Sfrankho brelse(bp); 2709f127cb91Sfrankho if (fatp) 2710f127cb91Sfrankho kmem_free(fatp, fatsize); 2711f127cb91Sfrankho if (fat_changemap) 2712f127cb91Sfrankho kmem_free(fat_changemap, fat_changemapsize); 2713264a6e74Sfrankho 2714f127cb91Sfrankho if (flags) { 2715f127cb91Sfrankho pc_mark_irrecov(fsp); 2716f127cb91Sfrankho } 2717f127cb91Sfrankho return (error); 2718264a6e74Sfrankho } 2719