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 */ 217c478bd9Sstevel@tonic-gate /* 22c2aaf90fSgd * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate #include <sys/param.h> 297c478bd9Sstevel@tonic-gate #include <sys/systm.h> 307c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 317c478bd9Sstevel@tonic-gate #include <sys/user.h> 327c478bd9Sstevel@tonic-gate #include <sys/proc.h> 337c478bd9Sstevel@tonic-gate #include <sys/cred.h> 347c478bd9Sstevel@tonic-gate #include <sys/disp.h> 357c478bd9Sstevel@tonic-gate #include <sys/buf.h> 367c478bd9Sstevel@tonic-gate #include <sys/vfs.h> 37aa59c4cbSrsb #include <sys/vfs_opreg.h> 387c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 397c478bd9Sstevel@tonic-gate #include <sys/fdio.h> 407c478bd9Sstevel@tonic-gate #include <sys/file.h> 417c478bd9Sstevel@tonic-gate #include <sys/uio.h> 427c478bd9Sstevel@tonic-gate #include <sys/conf.h> 437c478bd9Sstevel@tonic-gate #include <sys/statvfs.h> 447c478bd9Sstevel@tonic-gate #include <sys/mount.h> 457c478bd9Sstevel@tonic-gate #include <sys/pathname.h> 467c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 477c478bd9Sstevel@tonic-gate #include <sys/debug.h> 487c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 497c478bd9Sstevel@tonic-gate #include <sys/conf.h> 507c478bd9Sstevel@tonic-gate #include <sys/mkdev.h> 517c478bd9Sstevel@tonic-gate #include <sys/swap.h> 527c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 537c478bd9Sstevel@tonic-gate #include <sys/sunldi.h> 547c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h> 557c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h> 567c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h> 577c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h> 587c478bd9Sstevel@tonic-gate #include <sys/fs/pc_node.h> 597c478bd9Sstevel@tonic-gate #include <fs/fs_subr.h> 607c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 617c478bd9Sstevel@tonic-gate #include <sys/dkio.h> 627c478bd9Sstevel@tonic-gate #include <sys/open.h> 637c478bd9Sstevel@tonic-gate #include <sys/mntent.h> 647c478bd9Sstevel@tonic-gate #include <sys/policy.h> 65264a6e74Sfrankho #include <sys/atomic.h> 66f127cb91Sfrankho #include <sys/sdt.h> 677c478bd9Sstevel@tonic-gate 687c478bd9Sstevel@tonic-gate /* 697c478bd9Sstevel@tonic-gate * The majority of PC media use a 512 sector size, but 707c478bd9Sstevel@tonic-gate * occasionally you will run across a 1k sector size. 717c478bd9Sstevel@tonic-gate * For media with a 1k sector size, fd_strategy() requires 727c478bd9Sstevel@tonic-gate * the I/O size to be a 1k multiple; so when the sector size 737c478bd9Sstevel@tonic-gate * is not yet known, always read 1k. 747c478bd9Sstevel@tonic-gate */ 757c478bd9Sstevel@tonic-gate #define PC_SAFESECSIZE (PC_SECSIZE * 2) 767c478bd9Sstevel@tonic-gate 7790c30842Sjmcp static int pcfs_pseudo_floppy(dev_t); 787c478bd9Sstevel@tonic-gate 797c478bd9Sstevel@tonic-gate static int pcfsinit(int, char *); 807c478bd9Sstevel@tonic-gate static int pcfs_mount(struct vfs *, struct vnode *, struct mounta *, 817c478bd9Sstevel@tonic-gate struct cred *); 827c478bd9Sstevel@tonic-gate static int pcfs_unmount(struct vfs *, int, struct cred *); 837c478bd9Sstevel@tonic-gate static int pcfs_root(struct vfs *, struct vnode **); 847c478bd9Sstevel@tonic-gate static int pcfs_statvfs(struct vfs *, struct statvfs64 *); 857c478bd9Sstevel@tonic-gate static int pc_syncfsnodes(struct pcfs *); 867c478bd9Sstevel@tonic-gate static int pcfs_sync(struct vfs *, short, struct cred *); 877c478bd9Sstevel@tonic-gate static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp); 88264a6e74Sfrankho static void pcfs_freevfs(vfs_t *vfsp); 897c478bd9Sstevel@tonic-gate 90f127cb91Sfrankho static int pc_readfat(struct pcfs *fsp, uchar_t *fatp); 917c478bd9Sstevel@tonic-gate static int pc_writefat(struct pcfs *fsp, daddr_t start); 927c478bd9Sstevel@tonic-gate 93f127cb91Sfrankho static int pc_getfattype(struct pcfs *fsp); 94*9e003ac4Sgd static void pcfs_parse_mntopts(struct pcfs *fsp); 95f127cb91Sfrankho 96f127cb91Sfrankho 977c478bd9Sstevel@tonic-gate /* 987c478bd9Sstevel@tonic-gate * pcfs mount options table 997c478bd9Sstevel@tonic-gate */ 1007c478bd9Sstevel@tonic-gate 101264a6e74Sfrankho static char *nohidden_cancel[] = { MNTOPT_PCFS_HIDDEN, NULL }; 102264a6e74Sfrankho static char *hidden_cancel[] = { MNTOPT_PCFS_NOHIDDEN, NULL }; 103264a6e74Sfrankho static char *nofoldcase_cancel[] = { MNTOPT_PCFS_FOLDCASE, NULL }; 104264a6e74Sfrankho static char *foldcase_cancel[] = { MNTOPT_PCFS_NOFOLDCASE, NULL }; 105264a6e74Sfrankho static char *clamptime_cancel[] = { MNTOPT_PCFS_NOCLAMPTIME, NULL }; 106264a6e74Sfrankho static char *noclamptime_cancel[] = { MNTOPT_PCFS_CLAMPTIME, NULL }; 107f127cb91Sfrankho static char *atime_cancel[] = { MNTOPT_NOATIME, NULL }; 108f127cb91Sfrankho static char *noatime_cancel[] = { MNTOPT_ATIME, NULL }; 1097c478bd9Sstevel@tonic-gate 1107c478bd9Sstevel@tonic-gate static mntopt_t mntopts[] = { 1117c478bd9Sstevel@tonic-gate /* 112264a6e74Sfrankho * option name cancel option default arg flags opt data 1137c478bd9Sstevel@tonic-gate */ 114264a6e74Sfrankho { MNTOPT_PCFS_NOHIDDEN, nohidden_cancel, NULL, 0, NULL }, 115264a6e74Sfrankho { MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, MO_DEFAULT, NULL }, 116264a6e74Sfrankho { MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, NULL }, 117264a6e74Sfrankho { MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0, NULL }, 118264a6e74Sfrankho { MNTOPT_PCFS_CLAMPTIME, clamptime_cancel, NULL, MO_DEFAULT, NULL }, 119f127cb91Sfrankho { MNTOPT_PCFS_NOCLAMPTIME, noclamptime_cancel, NULL, NULL, NULL }, 120f127cb91Sfrankho { MNTOPT_NOATIME, noatime_cancel, NULL, NULL, NULL }, 121f127cb91Sfrankho { MNTOPT_ATIME, atime_cancel, NULL, NULL, NULL }, 122f127cb91Sfrankho { MNTOPT_PCFS_TIMEZONE, NULL, "+0", MO_DEFAULT | MO_HASVALUE, NULL }, 123f127cb91Sfrankho { MNTOPT_PCFS_SECSIZE, NULL, NULL, MO_HASVALUE, NULL } 1247c478bd9Sstevel@tonic-gate }; 1257c478bd9Sstevel@tonic-gate 1267c478bd9Sstevel@tonic-gate static mntopts_t pcfs_mntopts = { 1277c478bd9Sstevel@tonic-gate sizeof (mntopts) / sizeof (mntopt_t), 1287c478bd9Sstevel@tonic-gate mntopts 1297c478bd9Sstevel@tonic-gate }; 1307c478bd9Sstevel@tonic-gate 1317c478bd9Sstevel@tonic-gate int pcfsdebuglevel = 0; 1327c478bd9Sstevel@tonic-gate 1337c478bd9Sstevel@tonic-gate /* 1347c478bd9Sstevel@tonic-gate * pcfslock: protects the list of mounted pc filesystems "pc_mounttab. 1357c478bd9Sstevel@tonic-gate * pcfs_lock: (inside per filesystem structure "pcfs") 1367c478bd9Sstevel@tonic-gate * per filesystem lock. Most of the vfsops and vnodeops are 1377c478bd9Sstevel@tonic-gate * protected by this lock. 1387c478bd9Sstevel@tonic-gate * pcnodes_lock: protects the pcnode hash table "pcdhead", "pcfhead". 1397c478bd9Sstevel@tonic-gate * 1407c478bd9Sstevel@tonic-gate * Lock hierarchy: pcfslock > pcfs_lock > pcnodes_lock 141264a6e74Sfrankho * 142264a6e74Sfrankho * pcfs_mountcount: used to prevent module unloads while there is still 143264a6e74Sfrankho * pcfs state from a former mount hanging around. With 144264a6e74Sfrankho * forced umount support, the filesystem module must not 145264a6e74Sfrankho * be allowed to go away before the last VFS_FREEVFS() 146264a6e74Sfrankho * call has been made. 147264a6e74Sfrankho * Since this is just an atomic counter, there's no need 148264a6e74Sfrankho * for locking. 1497c478bd9Sstevel@tonic-gate */ 1507c478bd9Sstevel@tonic-gate kmutex_t pcfslock; 151264a6e74Sfrankho krwlock_t pcnodes_lock; 152264a6e74Sfrankho uint32_t pcfs_mountcount; 1537c478bd9Sstevel@tonic-gate 1547c478bd9Sstevel@tonic-gate static int pcfstype; 1557c478bd9Sstevel@tonic-gate 1567c478bd9Sstevel@tonic-gate static vfsdef_t vfw = { 1577c478bd9Sstevel@tonic-gate VFSDEF_VERSION, 1587c478bd9Sstevel@tonic-gate "pcfs", 1597c478bd9Sstevel@tonic-gate pcfsinit, 1605a59a8b3Srsb VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS, 1617c478bd9Sstevel@tonic-gate &pcfs_mntopts 1627c478bd9Sstevel@tonic-gate }; 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate extern struct mod_ops mod_fsops; 1657c478bd9Sstevel@tonic-gate 1667c478bd9Sstevel@tonic-gate static struct modlfs modlfs = { 1677c478bd9Sstevel@tonic-gate &mod_fsops, 168c2aaf90fSgd "PC filesystem", 1697c478bd9Sstevel@tonic-gate &vfw 1707c478bd9Sstevel@tonic-gate }; 1717c478bd9Sstevel@tonic-gate 1727c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = { 1737c478bd9Sstevel@tonic-gate MODREV_1, 1747c478bd9Sstevel@tonic-gate &modlfs, 1757c478bd9Sstevel@tonic-gate NULL 1767c478bd9Sstevel@tonic-gate }; 1777c478bd9Sstevel@tonic-gate 1787c478bd9Sstevel@tonic-gate int 1797c478bd9Sstevel@tonic-gate _init(void) 1807c478bd9Sstevel@tonic-gate { 1817c478bd9Sstevel@tonic-gate int error; 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate #if !defined(lint) 1847c478bd9Sstevel@tonic-gate /* make sure the on-disk structures are sane */ 1857c478bd9Sstevel@tonic-gate ASSERT(sizeof (struct pcdir) == 32); 1867c478bd9Sstevel@tonic-gate ASSERT(sizeof (struct pcdir_lfn) == 32); 1877c478bd9Sstevel@tonic-gate #endif 1887c478bd9Sstevel@tonic-gate mutex_init(&pcfslock, NULL, MUTEX_DEFAULT, NULL); 1897c478bd9Sstevel@tonic-gate rw_init(&pcnodes_lock, NULL, RW_DEFAULT, NULL); 1907c478bd9Sstevel@tonic-gate error = mod_install(&modlinkage); 1917c478bd9Sstevel@tonic-gate if (error) { 1927c478bd9Sstevel@tonic-gate mutex_destroy(&pcfslock); 1937c478bd9Sstevel@tonic-gate rw_destroy(&pcnodes_lock); 1947c478bd9Sstevel@tonic-gate } 1957c478bd9Sstevel@tonic-gate return (error); 1967c478bd9Sstevel@tonic-gate } 1977c478bd9Sstevel@tonic-gate 1987c478bd9Sstevel@tonic-gate int 1997c478bd9Sstevel@tonic-gate _fini(void) 2007c478bd9Sstevel@tonic-gate { 2017c478bd9Sstevel@tonic-gate int error; 2027c478bd9Sstevel@tonic-gate 203264a6e74Sfrankho /* 204264a6e74Sfrankho * If a forcedly unmounted instance is still hanging around, 205264a6e74Sfrankho * we cannot allow the module to be unloaded because that would 206264a6e74Sfrankho * cause panics once the VFS framework decides it's time to call 207264a6e74Sfrankho * into VFS_FREEVFS(). 208264a6e74Sfrankho */ 209264a6e74Sfrankho if (pcfs_mountcount) 210264a6e74Sfrankho return (EBUSY); 211264a6e74Sfrankho 2127c478bd9Sstevel@tonic-gate error = mod_remove(&modlinkage); 2137c478bd9Sstevel@tonic-gate if (error) 2147c478bd9Sstevel@tonic-gate return (error); 2157c478bd9Sstevel@tonic-gate mutex_destroy(&pcfslock); 2167c478bd9Sstevel@tonic-gate rw_destroy(&pcnodes_lock); 2177c478bd9Sstevel@tonic-gate /* 2187c478bd9Sstevel@tonic-gate * Tear down the operations vectors 2197c478bd9Sstevel@tonic-gate */ 2207c478bd9Sstevel@tonic-gate (void) vfs_freevfsops_by_type(pcfstype); 2217c478bd9Sstevel@tonic-gate vn_freevnodeops(pcfs_fvnodeops); 2227c478bd9Sstevel@tonic-gate vn_freevnodeops(pcfs_dvnodeops); 2237c478bd9Sstevel@tonic-gate return (0); 2247c478bd9Sstevel@tonic-gate } 2257c478bd9Sstevel@tonic-gate 2267c478bd9Sstevel@tonic-gate int 2277c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop) 2287c478bd9Sstevel@tonic-gate { 2297c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 2307c478bd9Sstevel@tonic-gate } 2317c478bd9Sstevel@tonic-gate 2327c478bd9Sstevel@tonic-gate /* ARGSUSED1 */ 2337c478bd9Sstevel@tonic-gate static int 2347c478bd9Sstevel@tonic-gate pcfsinit(int fstype, char *name) 2357c478bd9Sstevel@tonic-gate { 2367c478bd9Sstevel@tonic-gate static const fs_operation_def_t pcfs_vfsops_template[] = { 237aa59c4cbSrsb VFSNAME_MOUNT, { .vfs_mount = pcfs_mount }, 238aa59c4cbSrsb VFSNAME_UNMOUNT, { .vfs_unmount = pcfs_unmount }, 239aa59c4cbSrsb VFSNAME_ROOT, { .vfs_root = pcfs_root }, 240aa59c4cbSrsb VFSNAME_STATVFS, { .vfs_statvfs = pcfs_statvfs }, 241aa59c4cbSrsb VFSNAME_SYNC, { .vfs_sync = pcfs_sync }, 242aa59c4cbSrsb VFSNAME_VGET, { .vfs_vget = pcfs_vget }, 243aa59c4cbSrsb VFSNAME_FREEVFS, { .vfs_freevfs = pcfs_freevfs }, 244aa59c4cbSrsb NULL, NULL 2457c478bd9Sstevel@tonic-gate }; 2467c478bd9Sstevel@tonic-gate int error; 2477c478bd9Sstevel@tonic-gate 2487c478bd9Sstevel@tonic-gate error = vfs_setfsops(fstype, pcfs_vfsops_template, NULL); 2497c478bd9Sstevel@tonic-gate if (error != 0) { 2507c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "pcfsinit: bad vfs ops template"); 2517c478bd9Sstevel@tonic-gate return (error); 2527c478bd9Sstevel@tonic-gate } 2537c478bd9Sstevel@tonic-gate 2547c478bd9Sstevel@tonic-gate error = vn_make_ops("pcfs", pcfs_fvnodeops_template, &pcfs_fvnodeops); 2557c478bd9Sstevel@tonic-gate if (error != 0) { 2567c478bd9Sstevel@tonic-gate (void) vfs_freevfsops_by_type(fstype); 2577c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "pcfsinit: bad file vnode ops template"); 2587c478bd9Sstevel@tonic-gate return (error); 2597c478bd9Sstevel@tonic-gate } 2607c478bd9Sstevel@tonic-gate 2617c478bd9Sstevel@tonic-gate error = vn_make_ops("pcfsd", pcfs_dvnodeops_template, &pcfs_dvnodeops); 2627c478bd9Sstevel@tonic-gate if (error != 0) { 2637c478bd9Sstevel@tonic-gate (void) vfs_freevfsops_by_type(fstype); 2647c478bd9Sstevel@tonic-gate vn_freevnodeops(pcfs_fvnodeops); 2657c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "pcfsinit: bad dir vnode ops template"); 2667c478bd9Sstevel@tonic-gate return (error); 2677c478bd9Sstevel@tonic-gate } 2687c478bd9Sstevel@tonic-gate 2697c478bd9Sstevel@tonic-gate pcfstype = fstype; 2707c478bd9Sstevel@tonic-gate (void) pc_init(); 271264a6e74Sfrankho pcfs_mountcount = 0; 2727c478bd9Sstevel@tonic-gate return (0); 2737c478bd9Sstevel@tonic-gate } 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate static struct pcfs *pc_mounttab = NULL; 2767c478bd9Sstevel@tonic-gate 2777c478bd9Sstevel@tonic-gate extern struct pcfs_args pc_tz; 2787c478bd9Sstevel@tonic-gate 2797c478bd9Sstevel@tonic-gate /* 2807c478bd9Sstevel@tonic-gate * Define some special logical drives we use internal to this file. 2817c478bd9Sstevel@tonic-gate */ 2827c478bd9Sstevel@tonic-gate #define BOOT_PARTITION_DRIVE 99 2837c478bd9Sstevel@tonic-gate #define PRIMARY_DOS_DRIVE 1 284f127cb91Sfrankho #define UNPARTITIONED_DRIVE 0 2857c478bd9Sstevel@tonic-gate 2867c478bd9Sstevel@tonic-gate static int 287f127cb91Sfrankho pcfs_device_identify( 2887c478bd9Sstevel@tonic-gate struct vfs *vfsp, 2897c478bd9Sstevel@tonic-gate struct mounta *uap, 290f127cb91Sfrankho struct cred *cr, 291f127cb91Sfrankho int *dos_ldrive, 292f127cb91Sfrankho dev_t *xdev) 2937c478bd9Sstevel@tonic-gate { 2947c478bd9Sstevel@tonic-gate struct pathname special; 2959bd42341Sfrankho char *c; 296f127cb91Sfrankho struct vnode *bvp; 2977c478bd9Sstevel@tonic-gate int oflag, aflag; 298f127cb91Sfrankho int error; 2997c478bd9Sstevel@tonic-gate 3007c478bd9Sstevel@tonic-gate /* 3017c478bd9Sstevel@tonic-gate * Resolve path name of special file being mounted. 3027c478bd9Sstevel@tonic-gate */ 3037c478bd9Sstevel@tonic-gate if (error = pn_get(uap->spec, UIO_USERSPACE, &special)) { 3047c478bd9Sstevel@tonic-gate return (error); 3057c478bd9Sstevel@tonic-gate } 306f127cb91Sfrankho 307f127cb91Sfrankho *dos_ldrive = -1; 308f127cb91Sfrankho 3097c478bd9Sstevel@tonic-gate if (error = 3107c478bd9Sstevel@tonic-gate lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &bvp)) { 311f127cb91Sfrankho /* 312f127cb91Sfrankho * If there's no device node, the name specified most likely 313f127cb91Sfrankho * maps to a PCFS-style "partition specifier" to select a 314f127cb91Sfrankho * harddisk primary/logical partition. Disable floppy-specific 315f127cb91Sfrankho * checks in such cases unless an explicit :A or :B is 316f127cb91Sfrankho * requested. 317f127cb91Sfrankho */ 318f127cb91Sfrankho 3197c478bd9Sstevel@tonic-gate /* 3209bd42341Sfrankho * Split the pathname string at the last ':' separator. 3219bd42341Sfrankho * If there's no ':' in the device name, or the ':' is the 3229bd42341Sfrankho * last character in the string, the name is invalid and 3239bd42341Sfrankho * the error from the previous lookup will be returned. 3247c478bd9Sstevel@tonic-gate */ 3259bd42341Sfrankho c = strrchr(special.pn_path, ':'); 3269bd42341Sfrankho if (c == NULL || strlen(c) == 0) 3279bd42341Sfrankho goto devlookup_done; 3287c478bd9Sstevel@tonic-gate 3299bd42341Sfrankho *c++ = '\0'; 3307c478bd9Sstevel@tonic-gate 3319bd42341Sfrankho /* 3329bd42341Sfrankho * PCFS partition name suffixes can be: 3339bd42341Sfrankho * - "boot" to indicate the X86BOOT partition 3349bd42341Sfrankho * - a drive letter [c-z] for the "DOS logical drive" 3359bd42341Sfrankho * - a drive number 1..24 for the "DOS logical drive" 336f127cb91Sfrankho * - a "floppy name letter", 'a' or 'b' (just strip this) 3379bd42341Sfrankho */ 3389bd42341Sfrankho if (strcasecmp(c, "boot") == 0) { 3399bd42341Sfrankho /* 3409bd42341Sfrankho * The Solaris boot partition is requested. 3419bd42341Sfrankho */ 342f127cb91Sfrankho *dos_ldrive = BOOT_PARTITION_DRIVE; 3439bd42341Sfrankho } else if (strspn(c, "0123456789") == strlen(c)) { 3449bd42341Sfrankho /* 3459bd42341Sfrankho * All digits - parse the partition number. 3469bd42341Sfrankho */ 3479bd42341Sfrankho long drvnum = 0; 3489bd42341Sfrankho 3499bd42341Sfrankho if ((error = ddi_strtol(c, NULL, 10, &drvnum)) == 0) { 3507c478bd9Sstevel@tonic-gate /* 3519bd42341Sfrankho * A number alright - in the allowed range ? 3527c478bd9Sstevel@tonic-gate */ 3539bd42341Sfrankho if (drvnum > 24 || drvnum == 0) 354f127cb91Sfrankho error = ENXIO; 3557c478bd9Sstevel@tonic-gate } 3569bd42341Sfrankho if (error) 3579bd42341Sfrankho goto devlookup_done; 358f127cb91Sfrankho *dos_ldrive = (int)drvnum; 3599bd42341Sfrankho } else if (strlen(c) == 1) { 3609bd42341Sfrankho /* 361f127cb91Sfrankho * A single trailing character was specified. 362f127cb91Sfrankho * - [c-zC-Z] means a harddisk partition, and 363f127cb91Sfrankho * we retrieve the partition number. 364f127cb91Sfrankho * - [abAB] means a floppy drive, so we swallow 365f127cb91Sfrankho * the "drive specifier" and test later 366f127cb91Sfrankho * whether the physical device is a floppy or 367f127cb91Sfrankho * PCMCIA pseudofloppy (sram card). 3689bd42341Sfrankho */ 3699bd42341Sfrankho *c = tolower(*c); 370f127cb91Sfrankho if (*c == 'a' || *c == 'b') { 371f127cb91Sfrankho *dos_ldrive = UNPARTITIONED_DRIVE; 372f127cb91Sfrankho } else if (*c < 'c' || *c > 'z') { 373f127cb91Sfrankho error = ENXIO; 3749bd42341Sfrankho goto devlookup_done; 375f127cb91Sfrankho } else { 376f127cb91Sfrankho *dos_ldrive = 1 + *c - 'c'; 3779bd42341Sfrankho } 3789bd42341Sfrankho } else { 3799bd42341Sfrankho /* 3809bd42341Sfrankho * Can't parse this - pass through previous error. 3819bd42341Sfrankho */ 3829bd42341Sfrankho goto devlookup_done; 3837c478bd9Sstevel@tonic-gate } 3849bd42341Sfrankho 3859bd42341Sfrankho 3869bd42341Sfrankho error = lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, 3879bd42341Sfrankho NULLVPP, &bvp); 388f127cb91Sfrankho } else { 389f127cb91Sfrankho *dos_ldrive = UNPARTITIONED_DRIVE; 3907c478bd9Sstevel@tonic-gate } 3919bd42341Sfrankho devlookup_done: 3927c478bd9Sstevel@tonic-gate pn_free(&special); 3939bd42341Sfrankho if (error) 3949bd42341Sfrankho return (error); 3959bd42341Sfrankho 396f127cb91Sfrankho ASSERT(*dos_ldrive >= UNPARTITIONED_DRIVE); 397f127cb91Sfrankho 398f127cb91Sfrankho *xdev = bvp->v_rdev; 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 412f127cb91Sfrankho if (bvp->v_type != VBLK) 413f127cb91Sfrankho error = ENOTBLK; 414f127cb91Sfrankho else if (getmajor(*xdev) >= devcnt) 415f127cb91Sfrankho error = ENXIO; 416f127cb91Sfrankho 417f127cb91Sfrankho if ((error != 0) || 418da6c28aaSamw (error = VOP_ACCESS(bvp, aflag, 0, cr, NULL)) != 0 || 4197c478bd9Sstevel@tonic-gate (error = secpolicy_spec_open(cr, bvp, oflag)) != 0) { 4207c478bd9Sstevel@tonic-gate VN_RELE(bvp); 4217c478bd9Sstevel@tonic-gate return (error); 4227c478bd9Sstevel@tonic-gate } 4237c478bd9Sstevel@tonic-gate 4247c478bd9Sstevel@tonic-gate VN_RELE(bvp); 425f127cb91Sfrankho return (0); 426f127cb91Sfrankho } 427f127cb91Sfrankho 428f127cb91Sfrankho static int 429f127cb91Sfrankho pcfs_device_ismounted( 430f127cb91Sfrankho struct vfs *vfsp, 431f127cb91Sfrankho int dos_ldrive, 432f127cb91Sfrankho dev_t xdev, 433f127cb91Sfrankho int *remounting, 434f127cb91Sfrankho dev_t *pseudodev) 435f127cb91Sfrankho { 436f127cb91Sfrankho struct pcfs *fsp; 437f127cb91Sfrankho int remount = *remounting; 438f127cb91Sfrankho 4397c478bd9Sstevel@tonic-gate /* 4409bd42341Sfrankho * Ensure that this logical drive isn't already mounted, unless 4419bd42341Sfrankho * this is a REMOUNT request. 4429bd42341Sfrankho * Note: The framework will perform this check if the "...:c" 4439bd42341Sfrankho * PCFS-style "logical drive" syntax has not been used and an 4449bd42341Sfrankho * actually existing physical device is backing this filesystem. 445f127cb91Sfrankho * Once all block device drivers support PC-style partitioning, 446f127cb91Sfrankho * this codeblock can be dropped. 4477c478bd9Sstevel@tonic-gate */ 448f127cb91Sfrankho *pseudodev = xdev; 449f127cb91Sfrankho 4507c478bd9Sstevel@tonic-gate if (dos_ldrive) { 4517c478bd9Sstevel@tonic-gate mutex_enter(&pcfslock); 4527c478bd9Sstevel@tonic-gate for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt) 4537c478bd9Sstevel@tonic-gate if (fsp->pcfs_xdev == xdev && 454f127cb91Sfrankho fsp->pcfs_ldrive == dos_ldrive) { 4557c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock); 456f127cb91Sfrankho if (remount) { 4577c478bd9Sstevel@tonic-gate return (0); 4587c478bd9Sstevel@tonic-gate } else { 4597c478bd9Sstevel@tonic-gate return (EBUSY); 4607c478bd9Sstevel@tonic-gate } 4617c478bd9Sstevel@tonic-gate } 4627c478bd9Sstevel@tonic-gate /* 4637c478bd9Sstevel@tonic-gate * Assign a unique device number for the vfs 4647c478bd9Sstevel@tonic-gate * The old way (getudev() + a constantly incrementing 4657c478bd9Sstevel@tonic-gate * major number) was wrong because it changes vfs_dev 4667c478bd9Sstevel@tonic-gate * across mounts and reboots, which breaks nfs file handles. 4677c478bd9Sstevel@tonic-gate * UFS just uses the real dev_t. We can't do that because 4687c478bd9Sstevel@tonic-gate * of the way pcfs opens fdisk partitons (the :c and :d 4697c478bd9Sstevel@tonic-gate * partitions are on the same dev_t). Though that _might_ 4707c478bd9Sstevel@tonic-gate * actually be ok, since the file handle contains an 4717c478bd9Sstevel@tonic-gate * absolute block number, it's probably better to make them 4727c478bd9Sstevel@tonic-gate * different. So I think we should retain the original 4737c478bd9Sstevel@tonic-gate * dev_t, but come up with a different minor number based 4747c478bd9Sstevel@tonic-gate * on the logical drive that will _always_ come up the same. 4757c478bd9Sstevel@tonic-gate * For now, we steal the upper 6 bits. 4767c478bd9Sstevel@tonic-gate */ 4777c478bd9Sstevel@tonic-gate #ifdef notdef 4787c478bd9Sstevel@tonic-gate /* what should we do here? */ 4797c478bd9Sstevel@tonic-gate if (((getminor(xdev) >> 12) & 0x3F) != 0) 4807c478bd9Sstevel@tonic-gate printf("whoops - upper bits used!\n"); 4817c478bd9Sstevel@tonic-gate #endif 482f127cb91Sfrankho *pseudodev = makedevice(getmajor(xdev), 483f127cb91Sfrankho ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32); 484f127cb91Sfrankho if (vfs_devmounting(*pseudodev, vfsp)) { 4857c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock); 4867c478bd9Sstevel@tonic-gate return (EBUSY); 4877c478bd9Sstevel@tonic-gate } 488f127cb91Sfrankho if (vfs_devismounted(*pseudodev)) { 4897c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock); 490f127cb91Sfrankho if (remount) { 4917c478bd9Sstevel@tonic-gate return (0); 4927c478bd9Sstevel@tonic-gate } else { 4937c478bd9Sstevel@tonic-gate return (EBUSY); 4947c478bd9Sstevel@tonic-gate } 4957c478bd9Sstevel@tonic-gate } 4967c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock); 4977c478bd9Sstevel@tonic-gate } else { 498f127cb91Sfrankho *pseudodev = xdev; 499f127cb91Sfrankho if (vfs_devmounting(*pseudodev, vfsp)) { 5007c478bd9Sstevel@tonic-gate return (EBUSY); 5017c478bd9Sstevel@tonic-gate } 502f127cb91Sfrankho if (vfs_devismounted(*pseudodev)) 503f127cb91Sfrankho if (remount) { 5047c478bd9Sstevel@tonic-gate return (0); 5057c478bd9Sstevel@tonic-gate } else { 5067c478bd9Sstevel@tonic-gate return (EBUSY); 5077c478bd9Sstevel@tonic-gate } 5087c478bd9Sstevel@tonic-gate } 5097c478bd9Sstevel@tonic-gate 510f127cb91Sfrankho /* 511f127cb91Sfrankho * This is not a remount. Even if MS_REMOUNT was requested, 512f127cb91Sfrankho * the caller needs to proceed as it would on an ordinary 513f127cb91Sfrankho * mount. 514f127cb91Sfrankho */ 515f127cb91Sfrankho *remounting = 0; 516f127cb91Sfrankho 517f127cb91Sfrankho ASSERT(*pseudodev); 518f127cb91Sfrankho return (0); 519f127cb91Sfrankho } 520f127cb91Sfrankho 521f127cb91Sfrankho /* 522f127cb91Sfrankho * Get the PCFS-specific mount options from the VFS framework. 523f127cb91Sfrankho * For "timezone" and "secsize", we need to parse the number 524f127cb91Sfrankho * ourselves and ensure its validity. 525f127cb91Sfrankho * Note: "secsize" is deliberately undocumented at this time, 526f127cb91Sfrankho * it's a workaround for devices (particularly: lofi image files) 527f127cb91Sfrankho * that don't support the DKIOCGMEDIAINFO ioctl for autodetection. 528f127cb91Sfrankho */ 529f127cb91Sfrankho static void 530*9e003ac4Sgd pcfs_parse_mntopts(struct pcfs *fsp) 531f127cb91Sfrankho { 532f127cb91Sfrankho char *c; 533f127cb91Sfrankho char *endptr; 534f127cb91Sfrankho long l; 535f127cb91Sfrankho struct vfs *vfsp = fsp->pcfs_vfs; 536f127cb91Sfrankho 537f127cb91Sfrankho ASSERT(fsp->pcfs_secondswest == 0); 538f127cb91Sfrankho ASSERT(fsp->pcfs_secsize == 0); 539f127cb91Sfrankho 540f127cb91Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL)) 541f127cb91Sfrankho fsp->pcfs_flags |= PCFS_HIDDEN; 542f127cb91Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL)) 543f127cb91Sfrankho fsp->pcfs_flags |= PCFS_FOLDCASE; 544f127cb91Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL)) 545f127cb91Sfrankho fsp->pcfs_flags |= PCFS_NOCLAMPTIME; 546f127cb91Sfrankho if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL)) 547f127cb91Sfrankho fsp->pcfs_flags |= PCFS_NOATIME; 548f127cb91Sfrankho 549f127cb91Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_TIMEZONE, &c)) { 550f127cb91Sfrankho if (ddi_strtol(c, &endptr, 10, &l) == 0 && 551f127cb91Sfrankho endptr == c + strlen(c)) { 552f127cb91Sfrankho /* 553f127cb91Sfrankho * A number alright - in the allowed range ? 554f127cb91Sfrankho */ 555f127cb91Sfrankho if (l <= -12*3600 || l >= 12*3600) { 556f127cb91Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of " 557f127cb91Sfrankho "'timezone' mount option - %ld " 558f127cb91Sfrankho "is out of range. Assuming 0.", l); 559f127cb91Sfrankho l = 0; 560f127cb91Sfrankho } 561f127cb91Sfrankho } else { 562f127cb91Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of " 563f127cb91Sfrankho "'timezone' mount option - argument %s " 564f127cb91Sfrankho "is not a valid number. Assuming 0.", c); 565f127cb91Sfrankho l = 0; 566f127cb91Sfrankho } 567f127cb91Sfrankho fsp->pcfs_secondswest = l; 568f127cb91Sfrankho } 569f127cb91Sfrankho 5707c478bd9Sstevel@tonic-gate /* 571f127cb91Sfrankho * The "secsize=..." mount option is a workaround for the lack of 572f127cb91Sfrankho * lofi(7d) support for DKIOCGMEDIAINFO. If PCFS wants to parse the 573f127cb91Sfrankho * partition table of a disk image and it has been partitioned with 574f127cb91Sfrankho * sector sizes other than 512 bytes, we'd fail on loopback'ed disk 575f127cb91Sfrankho * images. 576f127cb91Sfrankho * That should really be fixed in lofi ... this is a workaround. 5777c478bd9Sstevel@tonic-gate */ 578f127cb91Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_SECSIZE, &c)) { 579f127cb91Sfrankho if (ddi_strtol(c, &endptr, 10, &l) == 0 && 580f127cb91Sfrankho endptr == c + strlen(c)) { 581f127cb91Sfrankho /* 582f127cb91Sfrankho * A number alright - a valid sector size as well ? 583f127cb91Sfrankho */ 584f127cb91Sfrankho if (!VALID_SECSIZE(l)) { 585f127cb91Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of " 586f127cb91Sfrankho "'secsize' mount option - %ld is " 587f127cb91Sfrankho "unsupported. Autodetecting.", l); 588f127cb91Sfrankho l = 0; 589f127cb91Sfrankho } 590f127cb91Sfrankho } else { 591f127cb91Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of " 592f127cb91Sfrankho "'secsize' mount option - argument %s " 593f127cb91Sfrankho "is not a valid number. Autodetecting.", c); 594f127cb91Sfrankho l = 0; 595f127cb91Sfrankho } 596f127cb91Sfrankho fsp->pcfs_secsize = l; 597f127cb91Sfrankho fsp->pcfs_sdshift = ddi_ffs(l / DEV_BSIZE) - 1; 598f127cb91Sfrankho } 599f127cb91Sfrankho } 600f127cb91Sfrankho 601f127cb91Sfrankho /* 602f127cb91Sfrankho * vfs operations 603f127cb91Sfrankho */ 604f127cb91Sfrankho 605f127cb91Sfrankho /* 606f127cb91Sfrankho * pcfs_mount - backend for VFS_MOUNT() on PCFS. 607f127cb91Sfrankho */ 608f127cb91Sfrankho static int 609f127cb91Sfrankho pcfs_mount( 610f127cb91Sfrankho struct vfs *vfsp, 611f127cb91Sfrankho struct vnode *mvp, 612f127cb91Sfrankho struct mounta *uap, 613f127cb91Sfrankho struct cred *cr) 614f127cb91Sfrankho { 615f127cb91Sfrankho struct pcfs *fsp; 616f127cb91Sfrankho struct vnode *devvp; 617f127cb91Sfrankho dev_t pseudodev; 618f127cb91Sfrankho dev_t xdev; 619f127cb91Sfrankho int dos_ldrive = 0; 620f127cb91Sfrankho int error; 621f127cb91Sfrankho int remounting; 622f127cb91Sfrankho 623f127cb91Sfrankho if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0) 624f127cb91Sfrankho return (error); 625f127cb91Sfrankho 626f127cb91Sfrankho if (mvp->v_type != VDIR) 627f127cb91Sfrankho return (ENOTDIR); 628f127cb91Sfrankho 629f127cb91Sfrankho mutex_enter(&mvp->v_lock); 630f127cb91Sfrankho if ((uap->flags & MS_REMOUNT) == 0 && 631f127cb91Sfrankho (uap->flags & MS_OVERLAY) == 0 && 632f127cb91Sfrankho (mvp->v_count != 1 || (mvp->v_flag & VROOT))) { 633f127cb91Sfrankho mutex_exit(&mvp->v_lock); 6347c478bd9Sstevel@tonic-gate return (EBUSY); 6357c478bd9Sstevel@tonic-gate } 636f127cb91Sfrankho mutex_exit(&mvp->v_lock); 6377c478bd9Sstevel@tonic-gate 6387c478bd9Sstevel@tonic-gate /* 639f127cb91Sfrankho * PCFS doesn't do mount arguments anymore - everything's a mount 640f127cb91Sfrankho * option these days. In order not to break existing callers, we 641f127cb91Sfrankho * don't reject it yet, just warn that the data (if any) is ignored. 6427c478bd9Sstevel@tonic-gate */ 643f127cb91Sfrankho if (uap->datalen != 0) 644f127cb91Sfrankho cmn_err(CE_WARN, "!pcfs: deprecated use of mount(2) with " 645f127cb91Sfrankho "mount argument structures instead of mount options. " 646f127cb91Sfrankho "Ignoring mount(2) 'dataptr' argument."); 647f127cb91Sfrankho 648c2aaf90fSgd /* 649c2aaf90fSgd * This is needed early, to make sure the access / open calls 650c2aaf90fSgd * are done using the correct mode. Processing this mount option 651c2aaf90fSgd * only when calling pcfs_parse_mntopts() would lead us to attempt 652c2aaf90fSgd * a read/write access to a possibly writeprotected device, and 653c2aaf90fSgd * a readonly mount attempt might fail because of that. 654c2aaf90fSgd */ 655c2aaf90fSgd if (uap->flags & MS_RDONLY) { 656c2aaf90fSgd vfsp->vfs_flag |= VFS_RDONLY; 657c2aaf90fSgd vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0); 658c2aaf90fSgd } 659c2aaf90fSgd 660f127cb91Sfrankho /* 661f127cb91Sfrankho * For most filesystems, this is just a lookupname() on the 662f127cb91Sfrankho * mount pathname string. PCFS historically has to do its own 663f127cb91Sfrankho * partition table parsing because not all Solaris architectures 664f127cb91Sfrankho * support all styles of partitioning that PC media can have, and 665f127cb91Sfrankho * hence PCFS understands "device names" that don't map to actual 666f127cb91Sfrankho * physical device nodes. Parsing the "PCFS syntax" for device 667f127cb91Sfrankho * names is done in pcfs_device_identify() - see there. 668f127cb91Sfrankho * 669f127cb91Sfrankho * Once all block device drivers that can host FAT filesystems have 670f127cb91Sfrankho * been enhanced to create device nodes for all PC-style partitions, 671f127cb91Sfrankho * this code can go away. 672f127cb91Sfrankho */ 673f127cb91Sfrankho if (error = pcfs_device_identify(vfsp, uap, cr, &dos_ldrive, &xdev)) 674f127cb91Sfrankho return (error); 675f127cb91Sfrankho 676f127cb91Sfrankho /* 677f127cb91Sfrankho * As with looking up the actual device to mount, PCFS cannot rely 678f127cb91Sfrankho * on just the checks done by vfs_ismounted() whether a given device 679f127cb91Sfrankho * is mounted already. The additional check against the "PCFS syntax" 680f127cb91Sfrankho * is done in pcfs_device_ismounted(). 681f127cb91Sfrankho */ 682f127cb91Sfrankho remounting = (uap->flags & MS_REMOUNT); 683f127cb91Sfrankho 684f127cb91Sfrankho if (error = pcfs_device_ismounted(vfsp, dos_ldrive, xdev, &remounting, 685f127cb91Sfrankho &pseudodev)) 686f127cb91Sfrankho return (error); 687f127cb91Sfrankho 688f127cb91Sfrankho if (remounting) 689f127cb91Sfrankho return (0); 690f127cb91Sfrankho 691f127cb91Sfrankho /* 692f127cb91Sfrankho * Mount the filesystem. 693f127cb91Sfrankho * An instance structure is required before the attempt to locate 694f127cb91Sfrankho * and parse the FAT BPB. This is because mount options may change 695f127cb91Sfrankho * the behaviour of the filesystem type matching code. Precreate 696f127cb91Sfrankho * it and fill it in to a degree that allows parsing the mount 697f127cb91Sfrankho * options. 698f127cb91Sfrankho */ 699f127cb91Sfrankho devvp = makespecvp(xdev, VBLK); 700f127cb91Sfrankho if (IS_SWAPVP(devvp)) { 701f127cb91Sfrankho VN_RELE(devvp); 702f127cb91Sfrankho return (EBUSY); 703f127cb91Sfrankho } 704f127cb91Sfrankho error = VOP_OPEN(&devvp, 705da6c28aaSamw (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD | FWRITE, cr, NULL); 706f127cb91Sfrankho if (error) { 707f127cb91Sfrankho VN_RELE(devvp); 708f127cb91Sfrankho return (error); 7097c478bd9Sstevel@tonic-gate } 7107c478bd9Sstevel@tonic-gate 711f127cb91Sfrankho fsp = kmem_zalloc(sizeof (*fsp), KM_SLEEP); 7127c478bd9Sstevel@tonic-gate fsp->pcfs_vfs = vfsp; 7137c478bd9Sstevel@tonic-gate fsp->pcfs_xdev = xdev; 714f127cb91Sfrankho fsp->pcfs_devvp = devvp; 715f127cb91Sfrankho fsp->pcfs_ldrive = dos_ldrive; 7167c478bd9Sstevel@tonic-gate mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL); 7177c478bd9Sstevel@tonic-gate 718*9e003ac4Sgd pcfs_parse_mntopts(fsp); 719f127cb91Sfrankho 720f127cb91Sfrankho /* 721f127cb91Sfrankho * This is the actual "mount" - the PCFS superblock check. 722f127cb91Sfrankho * 723f127cb91Sfrankho * Find the requested logical drive and the FAT BPB therein. 724f127cb91Sfrankho * Check device type and flag the instance if media is removeable. 725f127cb91Sfrankho * 726f127cb91Sfrankho * Initializes most members of the filesystem instance structure. 727f127cb91Sfrankho * Returns EINVAL if no valid BPB can be found. Other errors may 728f127cb91Sfrankho * occur after I/O failures, or when invalid / unparseable partition 729f127cb91Sfrankho * tables are encountered. 730f127cb91Sfrankho */ 731f127cb91Sfrankho if (error = pc_getfattype(fsp)) 732f127cb91Sfrankho goto errout; 733f127cb91Sfrankho 734c2aaf90fSgd /* 735c2aaf90fSgd * Now that the BPB has been parsed, this structural information 736c2aaf90fSgd * is available and known to be valid. Initialize the VFS. 737c2aaf90fSgd */ 738c2aaf90fSgd vfsp->vfs_data = fsp; 739c2aaf90fSgd vfsp->vfs_dev = pseudodev; 740c2aaf90fSgd vfsp->vfs_fstype = pcfstype; 741c2aaf90fSgd vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype); 742c2aaf90fSgd vfsp->vfs_bcount = 0; 743c2aaf90fSgd vfsp->vfs_bsize = fsp->pcfs_clsize; 744c2aaf90fSgd 745f127cb91Sfrankho /* 746f127cb91Sfrankho * Validate that we can access the FAT and that it is, to the 747f127cb91Sfrankho * degree we can verify here, self-consistent. 748f127cb91Sfrankho */ 749f127cb91Sfrankho if (error = pc_verify(fsp)) 750f127cb91Sfrankho goto errout; 751f127cb91Sfrankho 752f127cb91Sfrankho /* 753f127cb91Sfrankho * Record the time of the mount, to return as an "approximate" 754f127cb91Sfrankho * timestamp for the FAT root directory. Since FAT roots don't 755f127cb91Sfrankho * have timestamps, this is less confusing to the user than 756f127cb91Sfrankho * claiming "zero" / Jan/01/1970. 757f127cb91Sfrankho */ 758f127cb91Sfrankho gethrestime(&fsp->pcfs_mounttime); 759f127cb91Sfrankho 760f127cb91Sfrankho /* 761f127cb91Sfrankho * Fix up the mount options. Because "noatime" is made default on 762f127cb91Sfrankho * removeable media only, a fixed disk will have neither "atime" 763f127cb91Sfrankho * nor "noatime" set. We set the options explicitly depending on 764f127cb91Sfrankho * the PCFS_NOATIME flag, to inform the user of what applies. 765f127cb91Sfrankho * Mount option cancellation will take care that the mutually 766f127cb91Sfrankho * exclusive 'other' is cleared. 767f127cb91Sfrankho */ 768f127cb91Sfrankho vfs_setmntopt(vfsp, 769f127cb91Sfrankho fsp->pcfs_flags & PCFS_NOATIME ? MNTOPT_NOATIME : MNTOPT_ATIME, 770f127cb91Sfrankho NULL, 0); 771f127cb91Sfrankho 772f127cb91Sfrankho /* 773f127cb91Sfrankho * All clear - insert the FS instance into PCFS' list. 774f127cb91Sfrankho */ 7757c478bd9Sstevel@tonic-gate mutex_enter(&pcfslock); 7767c478bd9Sstevel@tonic-gate fsp->pcfs_nxt = pc_mounttab; 7777c478bd9Sstevel@tonic-gate pc_mounttab = fsp; 7787c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock); 779264a6e74Sfrankho atomic_inc_32(&pcfs_mountcount); 7807c478bd9Sstevel@tonic-gate return (0); 7817c478bd9Sstevel@tonic-gate 782f127cb91Sfrankho errout: 783f127cb91Sfrankho (void) VOP_CLOSE(devvp, 784f127cb91Sfrankho vfsp->vfs_flag & VFS_RDONLY ? FREAD : FREAD | FWRITE, 785da6c28aaSamw 1, (offset_t)0, cr, NULL); 786f127cb91Sfrankho VN_RELE(devvp); 787f127cb91Sfrankho mutex_destroy(&fsp->pcfs_lock); 788f127cb91Sfrankho kmem_free(fsp, sizeof (*fsp)); 789f127cb91Sfrankho return (error); 790f127cb91Sfrankho 791f127cb91Sfrankho } 7927c478bd9Sstevel@tonic-gate 7937c478bd9Sstevel@tonic-gate static int 7947c478bd9Sstevel@tonic-gate pcfs_unmount( 7957c478bd9Sstevel@tonic-gate struct vfs *vfsp, 7967c478bd9Sstevel@tonic-gate int flag, 7977c478bd9Sstevel@tonic-gate struct cred *cr) 7987c478bd9Sstevel@tonic-gate { 7997c478bd9Sstevel@tonic-gate struct pcfs *fsp, *fsp1; 8007c478bd9Sstevel@tonic-gate 8017c478bd9Sstevel@tonic-gate if (secpolicy_fs_unmount(cr, vfsp) != 0) 8027c478bd9Sstevel@tonic-gate return (EPERM); 8037c478bd9Sstevel@tonic-gate 8047c478bd9Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 805264a6e74Sfrankho 8067c478bd9Sstevel@tonic-gate /* 8077c478bd9Sstevel@tonic-gate * We don't have to lock fsp because the VVFSLOCK in vfs layer will 8087c478bd9Sstevel@tonic-gate * prevent lookuppn from crossing the mount point. 809264a6e74Sfrankho * If this is not a forced umount request and there's ongoing I/O, 810264a6e74Sfrankho * don't allow the mount to proceed. 8117c478bd9Sstevel@tonic-gate */ 812264a6e74Sfrankho if (flag & MS_FORCE) 813264a6e74Sfrankho vfsp->vfs_flag |= VFS_UNMOUNTED; 814264a6e74Sfrankho else if (fsp->pcfs_nrefs) 8157c478bd9Sstevel@tonic-gate return (EBUSY); 816264a6e74Sfrankho 817264a6e74Sfrankho mutex_enter(&pcfslock); 8187c478bd9Sstevel@tonic-gate 8197c478bd9Sstevel@tonic-gate /* 820264a6e74Sfrankho * If this is a forced umount request or if the fs instance has 821264a6e74Sfrankho * been marked as beyond recovery, allow the umount to proceed 822264a6e74Sfrankho * regardless of state. pc_diskchanged() forcibly releases all 823264a6e74Sfrankho * inactive vnodes/pcnodes. 8247c478bd9Sstevel@tonic-gate */ 825264a6e74Sfrankho if (flag & MS_FORCE || fsp->pcfs_flags & PCFS_IRRECOV) { 8267c478bd9Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_WRITER); 8277c478bd9Sstevel@tonic-gate pc_diskchanged(fsp); 8287c478bd9Sstevel@tonic-gate rw_exit(&pcnodes_lock); 8297c478bd9Sstevel@tonic-gate } 8307c478bd9Sstevel@tonic-gate 8317c478bd9Sstevel@tonic-gate /* now there should be no pcp node on pcfhead or pcdhead. */ 8327c478bd9Sstevel@tonic-gate 8337c478bd9Sstevel@tonic-gate if (fsp == pc_mounttab) { 8347c478bd9Sstevel@tonic-gate pc_mounttab = fsp->pcfs_nxt; 8357c478bd9Sstevel@tonic-gate } else { 8367c478bd9Sstevel@tonic-gate for (fsp1 = pc_mounttab; fsp1 != NULL; fsp1 = fsp1->pcfs_nxt) 8377c478bd9Sstevel@tonic-gate if (fsp1->pcfs_nxt == fsp) 8387c478bd9Sstevel@tonic-gate fsp1->pcfs_nxt = fsp->pcfs_nxt; 8397c478bd9Sstevel@tonic-gate } 8407c478bd9Sstevel@tonic-gate 8417c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock); 8427c478bd9Sstevel@tonic-gate 843264a6e74Sfrankho /* 844264a6e74Sfrankho * Since we support VFS_FREEVFS(), there's no need to 845264a6e74Sfrankho * free the fsp right now. The framework will tell us 846264a6e74Sfrankho * when the right time to do so has arrived by calling 847264a6e74Sfrankho * into pcfs_freevfs. 848264a6e74Sfrankho */ 8497c478bd9Sstevel@tonic-gate return (0); 8507c478bd9Sstevel@tonic-gate } 8517c478bd9Sstevel@tonic-gate 8527c478bd9Sstevel@tonic-gate /* 8537c478bd9Sstevel@tonic-gate * find root of pcfs 8547c478bd9Sstevel@tonic-gate */ 8557c478bd9Sstevel@tonic-gate static int 8567c478bd9Sstevel@tonic-gate pcfs_root( 8577c478bd9Sstevel@tonic-gate struct vfs *vfsp, 8587c478bd9Sstevel@tonic-gate struct vnode **vpp) 8597c478bd9Sstevel@tonic-gate { 8607c478bd9Sstevel@tonic-gate struct pcfs *fsp; 8617c478bd9Sstevel@tonic-gate struct pcnode *pcp; 8627c478bd9Sstevel@tonic-gate int error; 8637c478bd9Sstevel@tonic-gate 8647c478bd9Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 8657c478bd9Sstevel@tonic-gate if (error = pc_lockfs(fsp, 0, 0)) 8667c478bd9Sstevel@tonic-gate return (error); 867264a6e74Sfrankho 8687c478bd9Sstevel@tonic-gate pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0); 8697c478bd9Sstevel@tonic-gate pc_unlockfs(fsp); 8707c478bd9Sstevel@tonic-gate *vpp = PCTOV(pcp); 8717c478bd9Sstevel@tonic-gate pcp->pc_flags |= PC_EXTERNAL; 8727c478bd9Sstevel@tonic-gate return (0); 8737c478bd9Sstevel@tonic-gate } 8747c478bd9Sstevel@tonic-gate 8757c478bd9Sstevel@tonic-gate /* 8767c478bd9Sstevel@tonic-gate * Get file system statistics. 8777c478bd9Sstevel@tonic-gate */ 8787c478bd9Sstevel@tonic-gate static int 8797c478bd9Sstevel@tonic-gate pcfs_statvfs( 8807c478bd9Sstevel@tonic-gate struct vfs *vfsp, 8817c478bd9Sstevel@tonic-gate struct statvfs64 *sp) 8827c478bd9Sstevel@tonic-gate { 8837c478bd9Sstevel@tonic-gate struct pcfs *fsp; 8847c478bd9Sstevel@tonic-gate int error; 8857c478bd9Sstevel@tonic-gate dev32_t d32; 8867c478bd9Sstevel@tonic-gate 8877c478bd9Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 8887c478bd9Sstevel@tonic-gate error = pc_getfat(fsp); 8897c478bd9Sstevel@tonic-gate if (error) 8907c478bd9Sstevel@tonic-gate return (error); 8917c478bd9Sstevel@tonic-gate bzero(sp, sizeof (*sp)); 8927c478bd9Sstevel@tonic-gate sp->f_bsize = sp->f_frsize = fsp->pcfs_clsize; 8937c478bd9Sstevel@tonic-gate sp->f_blocks = (fsblkcnt64_t)fsp->pcfs_ncluster; 8947c478bd9Sstevel@tonic-gate sp->f_bavail = sp->f_bfree = (fsblkcnt64_t)pc_freeclusters(fsp); 8957c478bd9Sstevel@tonic-gate sp->f_files = (fsfilcnt64_t)-1; 8967c478bd9Sstevel@tonic-gate sp->f_ffree = (fsfilcnt64_t)-1; 8977c478bd9Sstevel@tonic-gate sp->f_favail = (fsfilcnt64_t)-1; 8987c478bd9Sstevel@tonic-gate #ifdef notdef 8997c478bd9Sstevel@tonic-gate (void) cmpldev(&d32, fsp->pcfs_devvp->v_rdev); 9007c478bd9Sstevel@tonic-gate #endif /* notdef */ 9017c478bd9Sstevel@tonic-gate (void) cmpldev(&d32, vfsp->vfs_dev); 9027c478bd9Sstevel@tonic-gate sp->f_fsid = d32; 9037c478bd9Sstevel@tonic-gate (void) strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name); 9047c478bd9Sstevel@tonic-gate sp->f_flag = vf_to_stf(vfsp->vfs_flag); 9057c478bd9Sstevel@tonic-gate sp->f_namemax = PCFNAMESIZE; 9067c478bd9Sstevel@tonic-gate return (0); 9077c478bd9Sstevel@tonic-gate } 9087c478bd9Sstevel@tonic-gate 9097c478bd9Sstevel@tonic-gate static int 9107c478bd9Sstevel@tonic-gate pc_syncfsnodes(struct pcfs *fsp) 9117c478bd9Sstevel@tonic-gate { 9127c478bd9Sstevel@tonic-gate struct pchead *hp; 9137c478bd9Sstevel@tonic-gate struct pcnode *pcp; 9147c478bd9Sstevel@tonic-gate int error; 9157c478bd9Sstevel@tonic-gate 9167c478bd9Sstevel@tonic-gate if (error = pc_lockfs(fsp, 0, 0)) 9177c478bd9Sstevel@tonic-gate return (error); 9187c478bd9Sstevel@tonic-gate 9197c478bd9Sstevel@tonic-gate if (!(error = pc_syncfat(fsp))) { 9207c478bd9Sstevel@tonic-gate hp = pcfhead; 9217c478bd9Sstevel@tonic-gate while (hp < & pcfhead [ NPCHASH ]) { 9227c478bd9Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_READER); 9237c478bd9Sstevel@tonic-gate pcp = hp->pch_forw; 9247c478bd9Sstevel@tonic-gate while (pcp != (struct pcnode *)hp) { 9257c478bd9Sstevel@tonic-gate if (VFSTOPCFS(PCTOV(pcp) -> v_vfsp) == fsp) 9267c478bd9Sstevel@tonic-gate if (error = pc_nodesync(pcp)) 9277c478bd9Sstevel@tonic-gate break; 9287c478bd9Sstevel@tonic-gate pcp = pcp -> pc_forw; 9297c478bd9Sstevel@tonic-gate } 9307c478bd9Sstevel@tonic-gate rw_exit(&pcnodes_lock); 9317c478bd9Sstevel@tonic-gate if (error) 9327c478bd9Sstevel@tonic-gate break; 9337c478bd9Sstevel@tonic-gate hp++; 9347c478bd9Sstevel@tonic-gate } 9357c478bd9Sstevel@tonic-gate } 9367c478bd9Sstevel@tonic-gate pc_unlockfs(fsp); 9377c478bd9Sstevel@tonic-gate return (error); 9387c478bd9Sstevel@tonic-gate } 9397c478bd9Sstevel@tonic-gate 9407c478bd9Sstevel@tonic-gate /* 9417c478bd9Sstevel@tonic-gate * Flush any pending I/O. 9427c478bd9Sstevel@tonic-gate */ 9437c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 9447c478bd9Sstevel@tonic-gate static int 9457c478bd9Sstevel@tonic-gate pcfs_sync( 9467c478bd9Sstevel@tonic-gate struct vfs *vfsp, 9477c478bd9Sstevel@tonic-gate short flag, 9487c478bd9Sstevel@tonic-gate struct cred *cr) 9497c478bd9Sstevel@tonic-gate { 9507c478bd9Sstevel@tonic-gate struct pcfs *fsp; 9517c478bd9Sstevel@tonic-gate int error = 0; 9527c478bd9Sstevel@tonic-gate 9537c478bd9Sstevel@tonic-gate /* this prevents the filesystem from being umounted. */ 9547c478bd9Sstevel@tonic-gate mutex_enter(&pcfslock); 9557c478bd9Sstevel@tonic-gate if (vfsp != NULL) { 9567c478bd9Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 9577c478bd9Sstevel@tonic-gate if (!(fsp->pcfs_flags & PCFS_IRRECOV)) { 9587c478bd9Sstevel@tonic-gate error = pc_syncfsnodes(fsp); 9597c478bd9Sstevel@tonic-gate } else { 9607c478bd9Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_WRITER); 9617c478bd9Sstevel@tonic-gate pc_diskchanged(fsp); 9627c478bd9Sstevel@tonic-gate rw_exit(&pcnodes_lock); 9637c478bd9Sstevel@tonic-gate error = EIO; 9647c478bd9Sstevel@tonic-gate } 9657c478bd9Sstevel@tonic-gate } else { 9667c478bd9Sstevel@tonic-gate fsp = pc_mounttab; 9677c478bd9Sstevel@tonic-gate while (fsp != NULL) { 9687c478bd9Sstevel@tonic-gate if (fsp->pcfs_flags & PCFS_IRRECOV) { 9697c478bd9Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_WRITER); 9707c478bd9Sstevel@tonic-gate pc_diskchanged(fsp); 9717c478bd9Sstevel@tonic-gate rw_exit(&pcnodes_lock); 9727c478bd9Sstevel@tonic-gate error = EIO; 9737c478bd9Sstevel@tonic-gate break; 9747c478bd9Sstevel@tonic-gate } 9757c478bd9Sstevel@tonic-gate error = pc_syncfsnodes(fsp); 9767c478bd9Sstevel@tonic-gate if (error) break; 9777c478bd9Sstevel@tonic-gate fsp = fsp->pcfs_nxt; 9787c478bd9Sstevel@tonic-gate } 9797c478bd9Sstevel@tonic-gate } 9807c478bd9Sstevel@tonic-gate mutex_exit(&pcfslock); 9817c478bd9Sstevel@tonic-gate return (error); 9827c478bd9Sstevel@tonic-gate } 9837c478bd9Sstevel@tonic-gate 9847c478bd9Sstevel@tonic-gate int 9857c478bd9Sstevel@tonic-gate pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing) 9867c478bd9Sstevel@tonic-gate { 987264a6e74Sfrankho int err; 988264a6e74Sfrankho 9897c478bd9Sstevel@tonic-gate if ((fsp->pcfs_flags & PCFS_IRRECOV) && !releasing) 9907c478bd9Sstevel@tonic-gate return (EIO); 9917c478bd9Sstevel@tonic-gate 9927c478bd9Sstevel@tonic-gate if ((fsp->pcfs_flags & PCFS_LOCKED) && (fsp->pcfs_owner == curthread)) { 9937c478bd9Sstevel@tonic-gate fsp->pcfs_count++; 9947c478bd9Sstevel@tonic-gate } else { 9957c478bd9Sstevel@tonic-gate mutex_enter(&fsp->pcfs_lock); 9967c478bd9Sstevel@tonic-gate if (fsp->pcfs_flags & PCFS_LOCKED) 9977c478bd9Sstevel@tonic-gate panic("pc_lockfs"); 9987c478bd9Sstevel@tonic-gate /* 9997c478bd9Sstevel@tonic-gate * We check the IRRECOV bit again just in case somebody 10007c478bd9Sstevel@tonic-gate * snuck past the initial check but then got held up before 10017c478bd9Sstevel@tonic-gate * they could grab the lock. (And in the meantime someone 10027c478bd9Sstevel@tonic-gate * had grabbed the lock and set the bit) 10037c478bd9Sstevel@tonic-gate */ 10040576819eSwyllys if (!diskchanged && !(fsp->pcfs_flags & PCFS_IRRECOV)) { 1005264a6e74Sfrankho if ((err = pc_getfat(fsp))) { 1006264a6e74Sfrankho mutex_exit(&fsp->pcfs_lock); 10070576819eSwyllys return (err); 1008264a6e74Sfrankho } 10090576819eSwyllys } 10107c478bd9Sstevel@tonic-gate fsp->pcfs_flags |= PCFS_LOCKED; 10117c478bd9Sstevel@tonic-gate fsp->pcfs_owner = curthread; 10127c478bd9Sstevel@tonic-gate fsp->pcfs_count++; 10137c478bd9Sstevel@tonic-gate } 10147c478bd9Sstevel@tonic-gate return (0); 10157c478bd9Sstevel@tonic-gate } 10167c478bd9Sstevel@tonic-gate 10177c478bd9Sstevel@tonic-gate void 10187c478bd9Sstevel@tonic-gate pc_unlockfs(struct pcfs *fsp) 10197c478bd9Sstevel@tonic-gate { 10207c478bd9Sstevel@tonic-gate 10217c478bd9Sstevel@tonic-gate if ((fsp->pcfs_flags & PCFS_LOCKED) == 0) 10227c478bd9Sstevel@tonic-gate panic("pc_unlockfs"); 10237c478bd9Sstevel@tonic-gate if (--fsp->pcfs_count < 0) 10247c478bd9Sstevel@tonic-gate panic("pc_unlockfs: count"); 10257c478bd9Sstevel@tonic-gate if (fsp->pcfs_count == 0) { 10267c478bd9Sstevel@tonic-gate fsp->pcfs_flags &= ~PCFS_LOCKED; 10277c478bd9Sstevel@tonic-gate fsp->pcfs_owner = 0; 10287c478bd9Sstevel@tonic-gate mutex_exit(&fsp->pcfs_lock); 10297c478bd9Sstevel@tonic-gate } 10307c478bd9Sstevel@tonic-gate } 10317c478bd9Sstevel@tonic-gate 1032f127cb91Sfrankho int 1033f127cb91Sfrankho pc_syncfat(struct pcfs *fsp) 10347c478bd9Sstevel@tonic-gate { 1035f127cb91Sfrankho struct buf *bp; 1036f127cb91Sfrankho int nfat; 1037f127cb91Sfrankho int error = 0; 1038f127cb91Sfrankho struct fat_od_fsi *fsinfo_disk; 10397c478bd9Sstevel@tonic-gate 1040f127cb91Sfrankho if ((fsp->pcfs_fatp == (uchar_t *)0) || 1041f127cb91Sfrankho !(fsp->pcfs_flags & PCFS_FATMOD)) 1042f127cb91Sfrankho return (0); 1043f127cb91Sfrankho /* 1044f127cb91Sfrankho * write out all copies of FATs 1045f127cb91Sfrankho */ 1046f127cb91Sfrankho fsp->pcfs_flags &= ~PCFS_FATMOD; 1047f127cb91Sfrankho fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT; 1048f127cb91Sfrankho for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) { 1049f127cb91Sfrankho error = pc_writefat(fsp, pc_dbdaddr(fsp, 1050f127cb91Sfrankho fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec)); 1051f127cb91Sfrankho if (error) { 1052f127cb91Sfrankho pc_mark_irrecov(fsp); 1053f127cb91Sfrankho return (EIO); 1054f127cb91Sfrankho } 1055f127cb91Sfrankho } 1056f127cb91Sfrankho pc_clear_fatchanges(fsp); 10577c478bd9Sstevel@tonic-gate 1058f127cb91Sfrankho /* 1059f127cb91Sfrankho * Write out fsinfo sector. 1060f127cb91Sfrankho */ 1061f127cb91Sfrankho if (IS_FAT32(fsp)) { 1062f127cb91Sfrankho bp = bread(fsp->pcfs_xdev, 1063f127cb91Sfrankho pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize); 1064f127cb91Sfrankho if (bp->b_flags & (B_ERROR | B_STALE)) { 1065f127cb91Sfrankho error = geterror(bp); 1066f127cb91Sfrankho } 1067f127cb91Sfrankho fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr); 1068f127cb91Sfrankho if (!error && FSISIG_OK(fsinfo_disk)) { 1069f127cb91Sfrankho fsinfo_disk->fsi_incore.fs_free_clusters = 1070f127cb91Sfrankho LE_32(fsp->pcfs_fsinfo.fs_free_clusters); 1071f127cb91Sfrankho fsinfo_disk->fsi_incore.fs_next_free = 1072f127cb91Sfrankho LE_32(FSINFO_UNKNOWN); 1073f127cb91Sfrankho bwrite2(bp); 1074f127cb91Sfrankho error = geterror(bp); 1075f127cb91Sfrankho } 1076f127cb91Sfrankho brelse(bp); 1077f127cb91Sfrankho if (error) { 1078f127cb91Sfrankho pc_mark_irrecov(fsp); 1079f127cb91Sfrankho return (EIO); 1080f127cb91Sfrankho } 1081f127cb91Sfrankho } 1082f127cb91Sfrankho return (0); 1083f127cb91Sfrankho } 1084f127cb91Sfrankho 1085f127cb91Sfrankho void 1086f127cb91Sfrankho pc_invalfat(struct pcfs *fsp) 1087f127cb91Sfrankho { 1088f127cb91Sfrankho struct pcfs *xfsp; 1089f127cb91Sfrankho int mount_cnt = 0; 1090f127cb91Sfrankho 1091f127cb91Sfrankho if (fsp->pcfs_fatp == (uchar_t *)0) 1092f127cb91Sfrankho panic("pc_invalfat"); 1093f127cb91Sfrankho /* 1094f127cb91Sfrankho * Release FAT 1095f127cb91Sfrankho */ 1096f127cb91Sfrankho kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsec * fsp->pcfs_secsize); 1097f127cb91Sfrankho fsp->pcfs_fatp = NULL; 1098f127cb91Sfrankho kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize); 1099f127cb91Sfrankho fsp->pcfs_fat_changemap = NULL; 1100f127cb91Sfrankho /* 1101f127cb91Sfrankho * Invalidate all the blocks associated with the device. 1102f127cb91Sfrankho * Not needed if stateless. 1103f127cb91Sfrankho */ 1104f127cb91Sfrankho for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt) 1105f127cb91Sfrankho if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev) 1106f127cb91Sfrankho mount_cnt++; 1107f127cb91Sfrankho 1108f127cb91Sfrankho if (!mount_cnt) 1109f127cb91Sfrankho binval(fsp->pcfs_xdev); 1110f127cb91Sfrankho /* 1111f127cb91Sfrankho * close mounted device 1112f127cb91Sfrankho */ 1113f127cb91Sfrankho (void) VOP_CLOSE(fsp->pcfs_devvp, 1114f127cb91Sfrankho (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, 1115da6c28aaSamw 1, (offset_t)0, CRED(), NULL); 1116f127cb91Sfrankho } 1117f127cb91Sfrankho 1118f127cb91Sfrankho void 1119f127cb91Sfrankho pc_badfs(struct pcfs *fsp) 1120f127cb91Sfrankho { 1121f127cb91Sfrankho cmn_err(CE_WARN, "corrupted PC file system on dev (%x.%x):%d\n", 1122f127cb91Sfrankho getmajor(fsp->pcfs_devvp->v_rdev), 1123f127cb91Sfrankho getminor(fsp->pcfs_devvp->v_rdev), fsp->pcfs_ldrive); 1124f127cb91Sfrankho } 1125f127cb91Sfrankho 1126f127cb91Sfrankho /* 1127f127cb91Sfrankho * The problem with supporting NFS on the PCFS filesystem is that there 1128f127cb91Sfrankho * is no good place to keep the generation number. The only possible 1129f127cb91Sfrankho * place is inside a directory entry. There are a few words that we 1130f127cb91Sfrankho * don't use - they store NT & OS/2 attributes, and the creation/last access 1131f127cb91Sfrankho * time of the file - but it seems wrong to use them. In addition, directory 1132f127cb91Sfrankho * entries come and go. If a directory is removed completely, its directory 1133f127cb91Sfrankho * blocks are freed and the generation numbers are lost. Whereas in ufs, 1134f127cb91Sfrankho * inode blocks are dedicated for inodes, so the generation numbers are 1135f127cb91Sfrankho * permanently kept on the disk. 1136f127cb91Sfrankho */ 1137f127cb91Sfrankho static int 1138f127cb91Sfrankho pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp) 1139f127cb91Sfrankho { 1140f127cb91Sfrankho struct pcnode *pcp; 1141f127cb91Sfrankho struct pc_fid *pcfid; 1142f127cb91Sfrankho struct pcfs *fsp; 1143f127cb91Sfrankho struct pcdir *ep; 1144f127cb91Sfrankho daddr_t eblkno; 1145f127cb91Sfrankho int eoffset; 1146f127cb91Sfrankho struct buf *bp; 1147f127cb91Sfrankho int error; 1148f127cb91Sfrankho pc_cluster32_t cn; 1149f127cb91Sfrankho 1150f127cb91Sfrankho pcfid = (struct pc_fid *)fidp; 1151f127cb91Sfrankho fsp = VFSTOPCFS(vfsp); 1152f127cb91Sfrankho 1153f127cb91Sfrankho error = pc_lockfs(fsp, 0, 0); 1154f127cb91Sfrankho if (error) { 1155f127cb91Sfrankho *vpp = NULL; 1156f127cb91Sfrankho return (error); 1157f127cb91Sfrankho } 1158f127cb91Sfrankho 1159f127cb91Sfrankho if (pcfid->pcfid_block == 0) { 1160f127cb91Sfrankho pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0); 1161f127cb91Sfrankho pcp->pc_flags |= PC_EXTERNAL; 1162f127cb91Sfrankho *vpp = PCTOV(pcp); 1163f127cb91Sfrankho pc_unlockfs(fsp); 1164f127cb91Sfrankho return (0); 1165f127cb91Sfrankho } 1166f127cb91Sfrankho eblkno = pcfid->pcfid_block; 1167f127cb91Sfrankho eoffset = pcfid->pcfid_offset; 1168f127cb91Sfrankho 1169f127cb91Sfrankho if ((pc_dbtocl(fsp, 1170f127cb91Sfrankho eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) || 1171f127cb91Sfrankho (eoffset > fsp->pcfs_clsize)) { 1172f127cb91Sfrankho pc_unlockfs(fsp); 1173f127cb91Sfrankho *vpp = NULL; 1174f127cb91Sfrankho return (EINVAL); 1175f127cb91Sfrankho } 1176f127cb91Sfrankho 1177f127cb91Sfrankho if (eblkno >= fsp->pcfs_datastart || (eblkno - fsp->pcfs_rdirstart) 1178f127cb91Sfrankho < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) { 1179f127cb91Sfrankho bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno), 1180f127cb91Sfrankho fsp->pcfs_clsize); 1181f127cb91Sfrankho } else { 1182f127cb91Sfrankho /* 1183f127cb91Sfrankho * This is an access "backwards" into the FAT12/FAT16 1184f127cb91Sfrankho * root directory. A better code structure would 1185f127cb91Sfrankho * significantly improve maintainability here ... 1186f127cb91Sfrankho */ 1187f127cb91Sfrankho bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno), 1188f127cb91Sfrankho (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize); 1189f127cb91Sfrankho } 1190f127cb91Sfrankho if (bp->b_flags & (B_ERROR | B_STALE)) { 1191f127cb91Sfrankho error = geterror(bp); 1192f127cb91Sfrankho brelse(bp); 1193f127cb91Sfrankho if (error) 1194f127cb91Sfrankho pc_mark_irrecov(fsp); 1195f127cb91Sfrankho *vpp = NULL; 1196f127cb91Sfrankho pc_unlockfs(fsp); 1197f127cb91Sfrankho return (error); 1198f127cb91Sfrankho } 1199f127cb91Sfrankho ep = (struct pcdir *)(bp->b_un.b_addr + eoffset); 1200f127cb91Sfrankho /* 1201f127cb91Sfrankho * Ok, if this is a valid file handle that we gave out, 1202f127cb91Sfrankho * then simply ensuring that the creation time matches, 1203f127cb91Sfrankho * the entry has not been deleted, and it has a valid first 1204f127cb91Sfrankho * character should be enough. 1205f127cb91Sfrankho * 1206f127cb91Sfrankho * Unfortunately, verifying that the <blkno, offset> _still_ 1207f127cb91Sfrankho * refers to a directory entry is not easy, since we'd have 1208f127cb91Sfrankho * to search _all_ directories starting from root to find it. 1209f127cb91Sfrankho * That's a high price to pay just in case somebody is forging 1210f127cb91Sfrankho * file handles. So instead we verify that as much of the 1211f127cb91Sfrankho * entry is valid as we can: 1212f127cb91Sfrankho * 1213f127cb91Sfrankho * 1. The starting cluster is 0 (unallocated) or valid 1214f127cb91Sfrankho * 2. It is not an LFN entry 1215f127cb91Sfrankho * 3. It is not hidden (unless mounted as such) 1216f127cb91Sfrankho * 4. It is not the label 1217f127cb91Sfrankho */ 1218f127cb91Sfrankho cn = pc_getstartcluster(fsp, ep); 1219f127cb91Sfrankho /* 1220f127cb91Sfrankho * if the starting cluster is valid, but not valid according 1221f127cb91Sfrankho * to pc_validcl(), force it to be to simplify the following if. 1222f127cb91Sfrankho */ 1223f127cb91Sfrankho if (cn == 0) 1224f127cb91Sfrankho cn = PCF_FIRSTCLUSTER; 1225f127cb91Sfrankho if (IS_FAT32(fsp)) { 1226f127cb91Sfrankho if (cn >= PCF_LASTCLUSTER32) 1227f127cb91Sfrankho cn = PCF_FIRSTCLUSTER; 1228f127cb91Sfrankho } else { 1229f127cb91Sfrankho if (cn >= PCF_LASTCLUSTER) 1230f127cb91Sfrankho cn = PCF_FIRSTCLUSTER; 1231f127cb91Sfrankho } 1232f127cb91Sfrankho if ((!pc_validcl(fsp, cn)) || 1233f127cb91Sfrankho (PCDL_IS_LFN(ep)) || 1234f127cb91Sfrankho (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) || 1235f127cb91Sfrankho ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) { 1236f127cb91Sfrankho bp->b_flags |= B_STALE | B_AGE; 1237f127cb91Sfrankho brelse(bp); 1238f127cb91Sfrankho pc_unlockfs(fsp); 1239f127cb91Sfrankho return (EINVAL); 1240f127cb91Sfrankho } 1241f127cb91Sfrankho if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) && 1242f127cb91Sfrankho (ep->pcd_filename[0] != PCD_ERASED) && 1243f127cb91Sfrankho (pc_validchar(ep->pcd_filename[0]) || 1244f127cb91Sfrankho (ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) { 1245f127cb91Sfrankho pcp = pc_getnode(fsp, eblkno, eoffset, ep); 1246f127cb91Sfrankho pcp->pc_flags |= PC_EXTERNAL; 1247f127cb91Sfrankho *vpp = PCTOV(pcp); 1248f127cb91Sfrankho } else { 1249f127cb91Sfrankho *vpp = NULL; 1250f127cb91Sfrankho } 1251f127cb91Sfrankho bp->b_flags |= B_STALE | B_AGE; 1252f127cb91Sfrankho brelse(bp); 1253f127cb91Sfrankho pc_unlockfs(fsp); 1254f127cb91Sfrankho return (0); 1255f127cb91Sfrankho } 1256f127cb91Sfrankho 1257f127cb91Sfrankho /* 1258f127cb91Sfrankho * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about 1259f127cb91Sfrankho * a meg), so we can't bread() it all in at once. This routine reads a 1260f127cb91Sfrankho * fat a chunk at a time. 1261f127cb91Sfrankho */ 1262f127cb91Sfrankho static int 1263f127cb91Sfrankho pc_readfat(struct pcfs *fsp, uchar_t *fatp) 1264f127cb91Sfrankho { 1265f127cb91Sfrankho struct buf *bp; 1266f127cb91Sfrankho size_t off; 1267f127cb91Sfrankho size_t readsize; 1268f127cb91Sfrankho daddr_t diskblk; 1269f127cb91Sfrankho size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize; 1270f127cb91Sfrankho daddr_t start = fsp->pcfs_fatstart; 1271f127cb91Sfrankho 1272f127cb91Sfrankho readsize = fsp->pcfs_clsize; 1273f127cb91Sfrankho for (off = 0; off < fatsize; off += readsize, fatp += readsize) { 1274f127cb91Sfrankho if (readsize > (fatsize - off)) 1275f127cb91Sfrankho readsize = fatsize - off; 1276f127cb91Sfrankho diskblk = pc_dbdaddr(fsp, start + 1277f127cb91Sfrankho pc_cltodb(fsp, pc_lblkno(fsp, off))); 1278f127cb91Sfrankho bp = bread(fsp->pcfs_xdev, diskblk, readsize); 1279f127cb91Sfrankho if (bp->b_flags & (B_ERROR | B_STALE)) { 1280f127cb91Sfrankho brelse(bp); 1281f127cb91Sfrankho return (EIO); 1282f127cb91Sfrankho } 1283f127cb91Sfrankho bp->b_flags |= B_STALE | B_AGE; 1284f127cb91Sfrankho bcopy(bp->b_un.b_addr, fatp, readsize); 1285f127cb91Sfrankho brelse(bp); 1286f127cb91Sfrankho } 1287f127cb91Sfrankho return (0); 1288f127cb91Sfrankho } 1289f127cb91Sfrankho 1290f127cb91Sfrankho /* 1291f127cb91Sfrankho * We write the FAT out a _lot_, in order to make sure that it 1292f127cb91Sfrankho * is up-to-date. But on a FAT32 system (large drive, small clusters) 1293f127cb91Sfrankho * the FAT might be a couple of megabytes, and writing it all out just 1294f127cb91Sfrankho * because we created or deleted a small file is painful (especially 1295f127cb91Sfrankho * since we do it for each alternate FAT too). So instead, for FAT16 and 1296f127cb91Sfrankho * FAT32 we only write out the bit that has changed. We don't clear 1297f127cb91Sfrankho * the 'updated' fields here because the caller might be writing out 1298f127cb91Sfrankho * several FATs, so the caller must use pc_clear_fatchanges() after 1299f127cb91Sfrankho * all FATs have been updated. 1300f127cb91Sfrankho * This function doesn't take "start" from fsp->pcfs_dosstart because 1301f127cb91Sfrankho * callers can use it to write either the primary or any of the alternate 1302f127cb91Sfrankho * FAT tables. 1303f127cb91Sfrankho */ 1304f127cb91Sfrankho static int 1305f127cb91Sfrankho pc_writefat(struct pcfs *fsp, daddr_t start) 1306f127cb91Sfrankho { 1307f127cb91Sfrankho struct buf *bp; 1308f127cb91Sfrankho size_t off; 1309f127cb91Sfrankho size_t writesize; 1310f127cb91Sfrankho int error; 1311f127cb91Sfrankho uchar_t *fatp = fsp->pcfs_fatp; 1312f127cb91Sfrankho size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize; 1313f127cb91Sfrankho 1314f127cb91Sfrankho writesize = fsp->pcfs_clsize; 1315f127cb91Sfrankho for (off = 0; off < fatsize; off += writesize, fatp += writesize) { 1316f127cb91Sfrankho if (writesize > (fatsize - off)) 1317f127cb91Sfrankho writesize = fatsize - off; 1318f127cb91Sfrankho if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) { 1319f127cb91Sfrankho continue; 1320f127cb91Sfrankho } 1321f127cb91Sfrankho bp = ngeteblk(writesize); 1322f127cb91Sfrankho bp->b_edev = fsp->pcfs_xdev; 1323f127cb91Sfrankho bp->b_dev = cmpdev(bp->b_edev); 1324f127cb91Sfrankho bp->b_blkno = pc_dbdaddr(fsp, start + 1325f127cb91Sfrankho pc_cltodb(fsp, pc_lblkno(fsp, off))); 1326f127cb91Sfrankho bcopy(fatp, bp->b_un.b_addr, writesize); 1327f127cb91Sfrankho bwrite2(bp); 1328f127cb91Sfrankho error = geterror(bp); 1329f127cb91Sfrankho brelse(bp); 1330f127cb91Sfrankho if (error) { 1331f127cb91Sfrankho return (error); 1332f127cb91Sfrankho } 1333f127cb91Sfrankho } 1334f127cb91Sfrankho return (0); 1335f127cb91Sfrankho } 1336f127cb91Sfrankho 1337f127cb91Sfrankho /* 1338f127cb91Sfrankho * Mark the FAT cluster that 'cn' is stored in as modified. 1339f127cb91Sfrankho */ 1340f127cb91Sfrankho void 1341f127cb91Sfrankho pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn) 1342f127cb91Sfrankho { 1343f127cb91Sfrankho pc_cluster32_t bn; 1344f127cb91Sfrankho size_t size; 1345f127cb91Sfrankho 1346f127cb91Sfrankho /* which fat block is the cluster number stored in? */ 1347f127cb91Sfrankho if (IS_FAT32(fsp)) { 1348f127cb91Sfrankho size = sizeof (pc_cluster32_t); 1349f127cb91Sfrankho bn = pc_lblkno(fsp, cn * size); 1350f127cb91Sfrankho fsp->pcfs_fat_changemap[bn] = 1; 1351f127cb91Sfrankho } else if (IS_FAT16(fsp)) { 1352f127cb91Sfrankho size = sizeof (pc_cluster16_t); 1353f127cb91Sfrankho bn = pc_lblkno(fsp, cn * size); 1354f127cb91Sfrankho fsp->pcfs_fat_changemap[bn] = 1; 1355f127cb91Sfrankho } else { 1356f127cb91Sfrankho offset_t off; 1357f127cb91Sfrankho pc_cluster32_t nbn; 1358f127cb91Sfrankho 1359f127cb91Sfrankho ASSERT(IS_FAT12(fsp)); 1360f127cb91Sfrankho off = cn + (cn >> 1); 1361f127cb91Sfrankho bn = pc_lblkno(fsp, off); 1362f127cb91Sfrankho fsp->pcfs_fat_changemap[bn] = 1; 1363f127cb91Sfrankho /* does this field wrap into the next fat cluster? */ 1364f127cb91Sfrankho nbn = pc_lblkno(fsp, off + 1); 1365f127cb91Sfrankho if (nbn != bn) { 1366f127cb91Sfrankho fsp->pcfs_fat_changemap[nbn] = 1; 1367f127cb91Sfrankho } 1368f127cb91Sfrankho } 1369f127cb91Sfrankho } 1370f127cb91Sfrankho 1371f127cb91Sfrankho /* 1372f127cb91Sfrankho * return whether the FAT cluster 'bn' is updated and needs to 1373f127cb91Sfrankho * be written out. 1374f127cb91Sfrankho */ 1375f127cb91Sfrankho int 1376f127cb91Sfrankho pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn) 1377f127cb91Sfrankho { 1378f127cb91Sfrankho return (fsp->pcfs_fat_changemap[bn] == 1); 1379f127cb91Sfrankho } 1380f127cb91Sfrankho 1381f127cb91Sfrankho /* 1382f127cb91Sfrankho * Implementation of VFS_FREEVFS() to support forced umounts. 1383f127cb91Sfrankho * This is called by the vfs framework after umount, to trigger 1384f127cb91Sfrankho * the release of any resources still associated with the given 1385f127cb91Sfrankho * vfs_t once the need to keep them has gone away. 1386f127cb91Sfrankho */ 1387f127cb91Sfrankho void 1388f127cb91Sfrankho pcfs_freevfs(vfs_t *vfsp) 1389f127cb91Sfrankho { 1390f127cb91Sfrankho struct pcfs *fsp = VFSTOPCFS(vfsp); 1391f127cb91Sfrankho 1392f127cb91Sfrankho mutex_enter(&pcfslock); 1393f127cb91Sfrankho /* 1394f127cb91Sfrankho * Purging the FAT closes the device - can't do any more 1395f127cb91Sfrankho * I/O after this. 1396f127cb91Sfrankho */ 1397f127cb91Sfrankho if (fsp->pcfs_fatp != (uchar_t *)0) 1398f127cb91Sfrankho pc_invalfat(fsp); 1399f127cb91Sfrankho mutex_exit(&pcfslock); 1400f127cb91Sfrankho 1401f127cb91Sfrankho VN_RELE(fsp->pcfs_devvp); 1402f127cb91Sfrankho mutex_destroy(&fsp->pcfs_lock); 1403f127cb91Sfrankho kmem_free(fsp, sizeof (*fsp)); 1404f127cb91Sfrankho 1405f127cb91Sfrankho /* 1406f127cb91Sfrankho * Allow _fini() to succeed now, if so desired. 1407f127cb91Sfrankho */ 1408f127cb91Sfrankho atomic_dec_32(&pcfs_mountcount); 1409f127cb91Sfrankho } 1410f127cb91Sfrankho 1411f127cb91Sfrankho 1412f127cb91Sfrankho /* 1413f127cb91Sfrankho * PC-style partition parsing and FAT BPB identification/validation code. 1414f127cb91Sfrankho * The partition parsers here assume: 1415f127cb91Sfrankho * - a FAT filesystem will be in a partition that has one of a set of 1416f127cb91Sfrankho * recognized partition IDs 1417f127cb91Sfrankho * - the user wants the 'numbering' (C:, D:, ...) that one would get 1418f127cb91Sfrankho * on MSDOS 6.x. 1419f127cb91Sfrankho * That means any non-FAT partition type (NTFS, HPFS, or any Linux fs) 1420f127cb91Sfrankho * will not factor in the enumeration. 1421f127cb91Sfrankho * These days, such assumptions should be revisited. FAT is no longer the 1422f127cb91Sfrankho * only game in 'PC town'. 1423f127cb91Sfrankho */ 1424f127cb91Sfrankho /* 1425f127cb91Sfrankho * isDosDrive() 1426f127cb91Sfrankho * Boolean function. Give it the systid field for an fdisk partition 1427f127cb91Sfrankho * and it decides if that's a systid that describes a DOS drive. We 1428f127cb91Sfrankho * use systid values defined in sys/dktp/fdisk.h. 1429f127cb91Sfrankho */ 1430f127cb91Sfrankho static int 1431f127cb91Sfrankho isDosDrive(uchar_t checkMe) 1432f127cb91Sfrankho { 1433f127cb91Sfrankho return ((checkMe == DOSOS12) || (checkMe == DOSOS16) || 1434f127cb91Sfrankho (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) || 1435f127cb91Sfrankho (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) || 1436f127cb91Sfrankho (checkMe == DIAGPART)); 1437f127cb91Sfrankho } 1438f127cb91Sfrankho 1439f127cb91Sfrankho 1440f127cb91Sfrankho /* 1441f127cb91Sfrankho * isDosExtended() 1442f127cb91Sfrankho * Boolean function. Give it the systid field for an fdisk partition 1443f127cb91Sfrankho * and it decides if that's a systid that describes an extended DOS 1444f127cb91Sfrankho * partition. 1445f127cb91Sfrankho */ 1446f127cb91Sfrankho static int 1447f127cb91Sfrankho isDosExtended(uchar_t checkMe) 1448f127cb91Sfrankho { 1449f127cb91Sfrankho return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA)); 1450f127cb91Sfrankho } 1451f127cb91Sfrankho 1452f127cb91Sfrankho 1453f127cb91Sfrankho /* 1454f127cb91Sfrankho * isBootPart() 1455f127cb91Sfrankho * Boolean function. Give it the systid field for an fdisk partition 1456f127cb91Sfrankho * and it decides if that's a systid that describes a Solaris boot 1457f127cb91Sfrankho * partition. 1458f127cb91Sfrankho */ 1459f127cb91Sfrankho static int 1460f127cb91Sfrankho isBootPart(uchar_t checkMe) 1461f127cb91Sfrankho { 1462f127cb91Sfrankho return (checkMe == X86BOOT); 1463f127cb91Sfrankho } 1464f127cb91Sfrankho 1465f127cb91Sfrankho 1466f127cb91Sfrankho /* 14677c478bd9Sstevel@tonic-gate * noLogicalDrive() 14687c478bd9Sstevel@tonic-gate * Display error message about not being able to find a logical 14697c478bd9Sstevel@tonic-gate * drive. 14707c478bd9Sstevel@tonic-gate */ 14717c478bd9Sstevel@tonic-gate static void 1472f127cb91Sfrankho noLogicalDrive(int ldrive) 14737c478bd9Sstevel@tonic-gate { 1474f127cb91Sfrankho if (ldrive == BOOT_PARTITION_DRIVE) { 14757c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "!pcfs: no boot partition"); 14767c478bd9Sstevel@tonic-gate } else { 1477f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: %d: no such logical drive", ldrive); 14787c478bd9Sstevel@tonic-gate } 14797c478bd9Sstevel@tonic-gate } 14807c478bd9Sstevel@tonic-gate 1481f127cb91Sfrankho 14827c478bd9Sstevel@tonic-gate /* 14837c478bd9Sstevel@tonic-gate * findTheDrive() 14847c478bd9Sstevel@tonic-gate * Discover offset of the requested logical drive, and return 14857c478bd9Sstevel@tonic-gate * that offset (startSector), the systid of that drive (sysid), 14867c478bd9Sstevel@tonic-gate * and a buffer pointer (bp), with the buffer contents being 14877c478bd9Sstevel@tonic-gate * the first sector of the logical drive (i.e., the sector that 14887c478bd9Sstevel@tonic-gate * contains the BPB for that drive). 1489f127cb91Sfrankho * 1490f127cb91Sfrankho * Note: this code is not capable of addressing >2TB disks, as it uses 1491f127cb91Sfrankho * daddr_t not diskaddr_t, some of the calculations would overflow 14927c478bd9Sstevel@tonic-gate */ 1493f127cb91Sfrankho #define COPY_PTBL(mbr, ptblp) \ 1494f127cb91Sfrankho bcopy(&(((struct mboot *)(mbr))->parts), (ptblp), \ 1495f127cb91Sfrankho FD_NUMPART * sizeof (struct ipart)) 1496f127cb91Sfrankho 14977c478bd9Sstevel@tonic-gate static int 1498f127cb91Sfrankho findTheDrive(struct pcfs *fsp, buf_t **bp) 14997c478bd9Sstevel@tonic-gate { 1500f127cb91Sfrankho int ldrive = fsp->pcfs_ldrive; 1501f127cb91Sfrankho dev_t dev = fsp->pcfs_devvp->v_rdev; 1502f127cb91Sfrankho 15037c478bd9Sstevel@tonic-gate struct ipart dosp[FD_NUMPART]; /* incore fdisk partition structure */ 15047c478bd9Sstevel@tonic-gate daddr_t lastseek = 0; /* Disk block we sought previously */ 15057c478bd9Sstevel@tonic-gate daddr_t diskblk = 0; /* Disk block to get */ 15067c478bd9Sstevel@tonic-gate daddr_t xstartsect; /* base of Extended DOS partition */ 15077c478bd9Sstevel@tonic-gate int logicalDriveCount = 0; /* Count of logical drives seen */ 15087c478bd9Sstevel@tonic-gate int extendedPart = -1; /* index of extended dos partition */ 15097c478bd9Sstevel@tonic-gate int primaryPart = -1; /* index of primary dos partition */ 15107c478bd9Sstevel@tonic-gate int bootPart = -1; /* index of a Solaris boot partition */ 15117c478bd9Sstevel@tonic-gate int xnumsect = -1; /* length of extended DOS partition */ 15127c478bd9Sstevel@tonic-gate int driveIndex; /* computed FDISK table index */ 1513f127cb91Sfrankho daddr_t startsec; 1514f127cb91Sfrankho len_t mediasize; 15157c478bd9Sstevel@tonic-gate int i; 15167c478bd9Sstevel@tonic-gate /* 15177c478bd9Sstevel@tonic-gate * Count of drives in the current extended partition's 15187c478bd9Sstevel@tonic-gate * FDISK table, and indexes of the drives themselves. 15197c478bd9Sstevel@tonic-gate */ 15207c478bd9Sstevel@tonic-gate int extndDrives[FD_NUMPART]; 15217c478bd9Sstevel@tonic-gate int numDrives = 0; 15227c478bd9Sstevel@tonic-gate 15237c478bd9Sstevel@tonic-gate /* 15247c478bd9Sstevel@tonic-gate * Count of drives (beyond primary) in master boot record's 15257c478bd9Sstevel@tonic-gate * FDISK table, and indexes of the drives themselves. 15267c478bd9Sstevel@tonic-gate */ 15277c478bd9Sstevel@tonic-gate int extraDrives[FD_NUMPART]; 15287c478bd9Sstevel@tonic-gate int numExtraDrives = 0; 15297c478bd9Sstevel@tonic-gate 15309bd42341Sfrankho /* 15319bd42341Sfrankho * "ldrive == 0" should never happen, as this is a request to 15329bd42341Sfrankho * mount the physical device (and ignore partitioning). The code 15339bd42341Sfrankho * in pcfs_mount() should have made sure that a logical drive number 15349bd42341Sfrankho * is at least 1, meaning we're looking for drive "C:". It is not 15359bd42341Sfrankho * safe (and a bug in the callers of this function) to request logical 15369bd42341Sfrankho * drive number 0; we could ASSERT() but a graceful EIO is a more 15379bd42341Sfrankho * polite way. 15389bd42341Sfrankho */ 15399bd42341Sfrankho if (ldrive == 0) { 15409bd42341Sfrankho cmn_err(CE_NOTE, "!pcfs: request for logical partition zero"); 15419bd42341Sfrankho noLogicalDrive(ldrive); 15429bd42341Sfrankho return (EIO); 15439bd42341Sfrankho } 15449bd42341Sfrankho 15457c478bd9Sstevel@tonic-gate /* 15467c478bd9Sstevel@tonic-gate * Copy from disk block into memory aligned structure for fdisk usage. 15477c478bd9Sstevel@tonic-gate */ 1548f127cb91Sfrankho COPY_PTBL((*bp)->b_un.b_addr, dosp); 15497c478bd9Sstevel@tonic-gate 1550f127cb91Sfrankho /* 1551f127cb91Sfrankho * This check is ok because a FAT BPB and a master boot record (MBB) 1552f127cb91Sfrankho * have the same signature, in the same position within the block. 1553f127cb91Sfrankho */ 1554f127cb91Sfrankho if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) { 1555f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: MBR partition table signature err, " 1556f127cb91Sfrankho "device (%x.%x):%d\n", 1557f127cb91Sfrankho getmajor(dev), getminor(dev), ldrive); 15589bd42341Sfrankho return (EINVAL); 15599bd42341Sfrankho } 15609bd42341Sfrankho 15617c478bd9Sstevel@tonic-gate /* 15627c478bd9Sstevel@tonic-gate * Get a summary of what is in the Master FDISK table. 15637c478bd9Sstevel@tonic-gate * Normally we expect to find one partition marked as a DOS drive. 15647c478bd9Sstevel@tonic-gate * This partition is the one Windows calls the primary dos partition. 15657c478bd9Sstevel@tonic-gate * If the machine has any logical drives then we also expect 15667c478bd9Sstevel@tonic-gate * to find a partition marked as an extended DOS partition. 15677c478bd9Sstevel@tonic-gate * 15687c478bd9Sstevel@tonic-gate * Sometimes we'll find multiple partitions marked as DOS drives. 15697c478bd9Sstevel@tonic-gate * The Solaris fdisk program allows these partitions 15707c478bd9Sstevel@tonic-gate * to be created, but Windows fdisk no longer does. We still need 15717c478bd9Sstevel@tonic-gate * to support these, though, since Windows does. We also need to fix 15727c478bd9Sstevel@tonic-gate * our fdisk to behave like the Windows version. 15737c478bd9Sstevel@tonic-gate * 15747c478bd9Sstevel@tonic-gate * It turns out that some off-the-shelf media have *only* an 15757c478bd9Sstevel@tonic-gate * Extended partition, so we need to deal with that case as well. 15767c478bd9Sstevel@tonic-gate * 15777c478bd9Sstevel@tonic-gate * Only a single (the first) Extended or Boot Partition will 15787c478bd9Sstevel@tonic-gate * be recognized. Any others will be ignored. 15797c478bd9Sstevel@tonic-gate */ 15807c478bd9Sstevel@tonic-gate for (i = 0; i < FD_NUMPART; i++) { 1581f127cb91Sfrankho DTRACE_PROBE4(primarypart, struct pcfs *, fsp, 1582f127cb91Sfrankho uint_t, (uint_t)dosp[i].systid, 1583f127cb91Sfrankho uint_t, LE_32(dosp[i].relsect), 1584f127cb91Sfrankho uint_t, LE_32(dosp[i].numsect)); 15850576819eSwyllys 15867c478bd9Sstevel@tonic-gate if (isDosDrive(dosp[i].systid)) { 15877c478bd9Sstevel@tonic-gate if (primaryPart < 0) { 15887c478bd9Sstevel@tonic-gate logicalDriveCount++; 15897c478bd9Sstevel@tonic-gate primaryPart = i; 15907c478bd9Sstevel@tonic-gate } else { 15917c478bd9Sstevel@tonic-gate extraDrives[numExtraDrives++] = i; 15927c478bd9Sstevel@tonic-gate } 15937c478bd9Sstevel@tonic-gate continue; 15947c478bd9Sstevel@tonic-gate } 15957c478bd9Sstevel@tonic-gate if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) { 15967c478bd9Sstevel@tonic-gate extendedPart = i; 15977c478bd9Sstevel@tonic-gate continue; 15987c478bd9Sstevel@tonic-gate } 15997c478bd9Sstevel@tonic-gate if ((bootPart < 0) && isBootPart(dosp[i].systid)) { 16007c478bd9Sstevel@tonic-gate bootPart = i; 16017c478bd9Sstevel@tonic-gate continue; 16027c478bd9Sstevel@tonic-gate } 16037c478bd9Sstevel@tonic-gate } 16047c478bd9Sstevel@tonic-gate 16059bd42341Sfrankho if (ldrive == BOOT_PARTITION_DRIVE) { 16067c478bd9Sstevel@tonic-gate if (bootPart < 0) { 16079bd42341Sfrankho noLogicalDrive(ldrive); 16089bd42341Sfrankho return (EINVAL); 16097c478bd9Sstevel@tonic-gate } 1610f127cb91Sfrankho startsec = LE_32(dosp[bootPart].relsect); 1611f127cb91Sfrankho mediasize = LE_32(dosp[bootPart].numsect); 1612f127cb91Sfrankho goto found; 16137c478bd9Sstevel@tonic-gate } 16147c478bd9Sstevel@tonic-gate 16159bd42341Sfrankho if (ldrive == PRIMARY_DOS_DRIVE && primaryPart >= 0) { 1616f127cb91Sfrankho startsec = LE_32(dosp[primaryPart].relsect); 1617f127cb91Sfrankho mediasize = LE_32(dosp[primaryPart].numsect); 1618f127cb91Sfrankho goto found; 16197c478bd9Sstevel@tonic-gate } 16207c478bd9Sstevel@tonic-gate 16217c478bd9Sstevel@tonic-gate /* 16227c478bd9Sstevel@tonic-gate * We are not looking for the C: drive (or the primary drive 16237c478bd9Sstevel@tonic-gate * was not found), so we had better have an extended partition 16247c478bd9Sstevel@tonic-gate * or extra drives in the Master FDISK table. 16257c478bd9Sstevel@tonic-gate */ 16267c478bd9Sstevel@tonic-gate if ((extendedPart < 0) && (numExtraDrives == 0)) { 16277c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "!pcfs: no extended dos partition"); 16289bd42341Sfrankho noLogicalDrive(ldrive); 16299bd42341Sfrankho return (EINVAL); 16307c478bd9Sstevel@tonic-gate } 16317c478bd9Sstevel@tonic-gate 16327c478bd9Sstevel@tonic-gate if (extendedPart >= 0) { 1633f127cb91Sfrankho diskblk = xstartsect = LE_32(dosp[extendedPart].relsect); 1634f127cb91Sfrankho xnumsect = LE_32(dosp[extendedPart].numsect); 16357c478bd9Sstevel@tonic-gate do { 16367c478bd9Sstevel@tonic-gate /* 16377c478bd9Sstevel@tonic-gate * If the seek would not cause us to change 16387c478bd9Sstevel@tonic-gate * position on the drive, then we're out of 16397c478bd9Sstevel@tonic-gate * extended partitions to examine. 16407c478bd9Sstevel@tonic-gate */ 16417c478bd9Sstevel@tonic-gate if (diskblk == lastseek) 16427c478bd9Sstevel@tonic-gate break; 16437c478bd9Sstevel@tonic-gate logicalDriveCount += numDrives; 16447c478bd9Sstevel@tonic-gate /* 16457c478bd9Sstevel@tonic-gate * Seek the next extended partition, and find 16467c478bd9Sstevel@tonic-gate * logical drives within it. 16477c478bd9Sstevel@tonic-gate */ 1648f127cb91Sfrankho brelse(*bp); 1649f127cb91Sfrankho /* 1650f127cb91Sfrankho * bread() block numbers are multiples of DEV_BSIZE 1651f127cb91Sfrankho * but the device sector size (the unit of partitioning) 1652f127cb91Sfrankho * might be larger than that; pcfs_get_device_info() 1653f127cb91Sfrankho * has calculated the multiplicator for us. 1654f127cb91Sfrankho */ 1655f127cb91Sfrankho *bp = bread(dev, 1656f127cb91Sfrankho pc_dbdaddr(fsp, diskblk), fsp->pcfs_secsize); 16577c478bd9Sstevel@tonic-gate if ((*bp)->b_flags & B_ERROR) { 16589bd42341Sfrankho return (EIO); 16597c478bd9Sstevel@tonic-gate } 1660f127cb91Sfrankho 16617c478bd9Sstevel@tonic-gate lastseek = diskblk; 1662f127cb91Sfrankho COPY_PTBL((*bp)->b_un.b_addr, dosp); 1663f127cb91Sfrankho if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) { 16647c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "!pcfs: " 1665f127cb91Sfrankho "extended partition table signature err, " 1666f127cb91Sfrankho "device (%x.%x):%d, LBA %u", 1667f127cb91Sfrankho getmajor(dev), getminor(dev), ldrive, 1668f127cb91Sfrankho (uint_t)pc_dbdaddr(fsp, diskblk)); 16699bd42341Sfrankho return (EINVAL); 16707c478bd9Sstevel@tonic-gate } 16717c478bd9Sstevel@tonic-gate /* 16727c478bd9Sstevel@tonic-gate * Count up drives, and track where the next 16737c478bd9Sstevel@tonic-gate * extended partition is in case we need it. We 16747c478bd9Sstevel@tonic-gate * are expecting only one extended partition. If 16757c478bd9Sstevel@tonic-gate * there is more than one we'll only go to the 16767c478bd9Sstevel@tonic-gate * first one we see, but warn about ignoring. 16777c478bd9Sstevel@tonic-gate */ 16787c478bd9Sstevel@tonic-gate numDrives = 0; 16797c478bd9Sstevel@tonic-gate for (i = 0; i < FD_NUMPART; i++) { 1680f127cb91Sfrankho DTRACE_PROBE4(extendedpart, 1681f127cb91Sfrankho struct pcfs *, fsp, 1682f127cb91Sfrankho uint_t, (uint_t)dosp[i].systid, 1683f127cb91Sfrankho uint_t, LE_32(dosp[i].relsect), 1684f127cb91Sfrankho uint_t, LE_32(dosp[i].numsect)); 16857c478bd9Sstevel@tonic-gate if (isDosDrive(dosp[i].systid)) { 16867c478bd9Sstevel@tonic-gate extndDrives[numDrives++] = i; 16877c478bd9Sstevel@tonic-gate } else if (isDosExtended(dosp[i].systid)) { 16887c478bd9Sstevel@tonic-gate if (diskblk != lastseek) { 16897c478bd9Sstevel@tonic-gate /* 16907c478bd9Sstevel@tonic-gate * Already found an extended 16917c478bd9Sstevel@tonic-gate * partition in this table. 16927c478bd9Sstevel@tonic-gate */ 16937c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, 16947c478bd9Sstevel@tonic-gate "!pcfs: ignoring unexpected" 16957c478bd9Sstevel@tonic-gate " additional extended" 16967c478bd9Sstevel@tonic-gate " partition"); 16979bd42341Sfrankho } else { 16989bd42341Sfrankho diskblk = xstartsect + 1699f127cb91Sfrankho LE_32(dosp[i].relsect); 17007c478bd9Sstevel@tonic-gate } 17017c478bd9Sstevel@tonic-gate } 17027c478bd9Sstevel@tonic-gate } 17039bd42341Sfrankho } while (ldrive > logicalDriveCount + numDrives); 17047c478bd9Sstevel@tonic-gate 1705f127cb91Sfrankho ASSERT(numDrives <= FD_NUMPART); 1706f127cb91Sfrankho 17079bd42341Sfrankho if (ldrive <= logicalDriveCount + numDrives) { 17087c478bd9Sstevel@tonic-gate /* 17097c478bd9Sstevel@tonic-gate * The number of logical drives we've found thus 17107c478bd9Sstevel@tonic-gate * far is enough to get us to the one we were 17117c478bd9Sstevel@tonic-gate * searching for. 17127c478bd9Sstevel@tonic-gate */ 17139bd42341Sfrankho driveIndex = logicalDriveCount + numDrives - ldrive; 1714f127cb91Sfrankho mediasize = 1715f127cb91Sfrankho LE_32(dosp[extndDrives[driveIndex]].numsect); 1716f127cb91Sfrankho startsec = 1717f127cb91Sfrankho LE_32(dosp[extndDrives[driveIndex]].relsect) + 17187c478bd9Sstevel@tonic-gate lastseek; 1719f127cb91Sfrankho if (startsec > (xstartsect + xnumsect)) { 17207c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "!pcfs: extended partition " 17217c478bd9Sstevel@tonic-gate "values bad"); 17229bd42341Sfrankho return (EINVAL); 17237c478bd9Sstevel@tonic-gate } 1724f127cb91Sfrankho goto found; 17257c478bd9Sstevel@tonic-gate } else { 17267c478bd9Sstevel@tonic-gate /* 17277c478bd9Sstevel@tonic-gate * We ran out of extended dos partition 17287c478bd9Sstevel@tonic-gate * drives. The only hope now is to go 17297c478bd9Sstevel@tonic-gate * back to extra drives defined in the master 17307c478bd9Sstevel@tonic-gate * fdisk table. But we overwrote that table 17317c478bd9Sstevel@tonic-gate * already, so we must load it in again. 17327c478bd9Sstevel@tonic-gate */ 17337c478bd9Sstevel@tonic-gate logicalDriveCount += numDrives; 17347c478bd9Sstevel@tonic-gate brelse(*bp); 1735f127cb91Sfrankho ASSERT(fsp->pcfs_dosstart == 0); 1736f127cb91Sfrankho *bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), 1737f127cb91Sfrankho fsp->pcfs_secsize); 17387c478bd9Sstevel@tonic-gate if ((*bp)->b_flags & B_ERROR) { 17399bd42341Sfrankho return (EIO); 17407c478bd9Sstevel@tonic-gate } 1741f127cb91Sfrankho COPY_PTBL((*bp)->b_un.b_addr, dosp); 17427c478bd9Sstevel@tonic-gate } 17437c478bd9Sstevel@tonic-gate } 17447c478bd9Sstevel@tonic-gate /* 17457c478bd9Sstevel@tonic-gate * Still haven't found the drive, is it an extra 17467c478bd9Sstevel@tonic-gate * drive defined in the main FDISK table? 17477c478bd9Sstevel@tonic-gate */ 17489bd42341Sfrankho if (ldrive <= logicalDriveCount + numExtraDrives) { 17499bd42341Sfrankho driveIndex = logicalDriveCount + numExtraDrives - ldrive; 17509bd42341Sfrankho ASSERT(driveIndex < MIN(numExtraDrives, FD_NUMPART)); 1751f127cb91Sfrankho mediasize = LE_32(dosp[extraDrives[driveIndex]].numsect); 1752f127cb91Sfrankho startsec = LE_32(dosp[extraDrives[driveIndex]].relsect); 1753f127cb91Sfrankho goto found; 17547c478bd9Sstevel@tonic-gate } 17557c478bd9Sstevel@tonic-gate /* 17567c478bd9Sstevel@tonic-gate * Still haven't found the drive, and there is 17577c478bd9Sstevel@tonic-gate * nowhere else to look. 17587c478bd9Sstevel@tonic-gate */ 17599bd42341Sfrankho noLogicalDrive(ldrive); 17609bd42341Sfrankho return (EINVAL); 17610576819eSwyllys 1762f127cb91Sfrankho found: 17630576819eSwyllys /* 1764f127cb91Sfrankho * We need this value in units of sectorsize, because PCFS' internal 1765f127cb91Sfrankho * offset calculations go haywire for > 512Byte sectors unless all 1766f127cb91Sfrankho * pcfs_.*start values are in units of sectors. 1767f127cb91Sfrankho * So, assign before the capacity check (that's done in DEV_BSIZE) 17680576819eSwyllys */ 1769f127cb91Sfrankho fsp->pcfs_dosstart = startsec; 17700576819eSwyllys 17710576819eSwyllys /* 1772f127cb91Sfrankho * convert from device sectors to proper units: 1773f127cb91Sfrankho * - starting sector: DEV_BSIZE (as argument to bread()) 1774f127cb91Sfrankho * - media size: Bytes 17750576819eSwyllys */ 1776f127cb91Sfrankho startsec = pc_dbdaddr(fsp, startsec); 1777f127cb91Sfrankho mediasize *= fsp->pcfs_secsize; 17780576819eSwyllys 17790576819eSwyllys /* 1780f127cb91Sfrankho * some additional validation / warnings in case the partition table 1781f127cb91Sfrankho * and the actual media capacity are not in accordance ... 17820576819eSwyllys */ 1783f127cb91Sfrankho if (fsp->pcfs_mediasize != 0) { 1784f127cb91Sfrankho diskaddr_t startoff = 1785f127cb91Sfrankho (diskaddr_t)startsec * (diskaddr_t)DEV_BSIZE; 1786f127cb91Sfrankho 1787f127cb91Sfrankho if (startoff >= fsp->pcfs_mediasize || 1788f127cb91Sfrankho startoff + mediasize > fsp->pcfs_mediasize) { 1789f127cb91Sfrankho cmn_err(CE_WARN, 1790f127cb91Sfrankho "!pcfs: partition size (LBA start %u, %lld bytes, " 1791f127cb91Sfrankho "device (%x.%x):%d) smaller than " 1792f127cb91Sfrankho "mediasize (%lld bytes).\n" 1793f127cb91Sfrankho "filesystem may be truncated, access errors " 1794f127cb91Sfrankho "may result.\n", 1795f127cb91Sfrankho (uint_t)startsec, (long long)mediasize, 1796f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 1797f127cb91Sfrankho fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize); 17980576819eSwyllys } 17990576819eSwyllys } else { 1800f127cb91Sfrankho fsp->pcfs_mediasize = mediasize; 18010576819eSwyllys } 18029bd42341Sfrankho 1803f127cb91Sfrankho return (0); 18040576819eSwyllys } 18050576819eSwyllys 18060576819eSwyllys 1807f127cb91Sfrankho static fattype_t 1808f127cb91Sfrankho secondaryBPBChecks(struct pcfs *fsp, uchar_t *bpb, size_t secsize) 18097c478bd9Sstevel@tonic-gate { 1810f127cb91Sfrankho uint32_t ncl = fsp->pcfs_ncluster; 1811f127cb91Sfrankho 1812f127cb91Sfrankho if (ncl <= 4096) { 1813f127cb91Sfrankho if (bpb_get_FatSz16(bpb) == 0) 1814f127cb91Sfrankho return (FAT_UNKNOWN); 1815f127cb91Sfrankho 1816f127cb91Sfrankho if (bpb_get_FatSz16(bpb) * secsize < ncl * 2 && 1817f127cb91Sfrankho bpb_get_FatSz16(bpb) * secsize >= (3 * ncl / 2)) 1818f127cb91Sfrankho return (FAT12); 1819f127cb91Sfrankho if (bcmp(bpb_FilSysType16(bpb), "FAT12", 5) == 0) 1820f127cb91Sfrankho return (FAT12); 1821f127cb91Sfrankho if (bcmp(bpb_FilSysType16(bpb), "FAT16", 5) == 0) 1822f127cb91Sfrankho return (FAT16); 1823f127cb91Sfrankho 1824f127cb91Sfrankho switch (bpb_get_Media(bpb)) { 1825f127cb91Sfrankho case SS8SPT: 1826f127cb91Sfrankho case DS8SPT: 1827f127cb91Sfrankho case SS9SPT: 1828f127cb91Sfrankho case DS9SPT: 1829f127cb91Sfrankho case DS18SPT: 1830f127cb91Sfrankho case DS9_15SPT: 1831f127cb91Sfrankho /* 1832f127cb91Sfrankho * Is this reliable - all floppies are FAT12 ? 1833f127cb91Sfrankho */ 1834f127cb91Sfrankho return (FAT12); 1835f127cb91Sfrankho case MD_FIXED: 1836f127cb91Sfrankho /* 1837f127cb91Sfrankho * Is this reliable - disks are always FAT16 ? 1838f127cb91Sfrankho */ 1839f127cb91Sfrankho return (FAT16); 1840f127cb91Sfrankho default: 1841f127cb91Sfrankho break; 18427c478bd9Sstevel@tonic-gate } 1843f127cb91Sfrankho } else if (ncl <= 65536) { 1844f127cb91Sfrankho if (bpb_get_FatSz16(bpb) == 0 && bpb_get_FatSz32(bpb) > 0) 1845f127cb91Sfrankho return (FAT32); 1846f127cb91Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig32(bpb))) 1847f127cb91Sfrankho return (FAT32); 1848f127cb91Sfrankho if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb))) 1849f127cb91Sfrankho return (FAT32); 18507c478bd9Sstevel@tonic-gate 1851f127cb91Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig16(bpb))) 1852f127cb91Sfrankho return (FAT16); 1853f127cb91Sfrankho if (bpb_get_FatSz16(bpb) * secsize < ncl * 4) 1854f127cb91Sfrankho return (FAT16); 18557c478bd9Sstevel@tonic-gate } 18567c478bd9Sstevel@tonic-gate 18570576819eSwyllys /* 1858f127cb91Sfrankho * We don't know 18590576819eSwyllys */ 1860f127cb91Sfrankho return (FAT_UNKNOWN); 18617c478bd9Sstevel@tonic-gate } 18627c478bd9Sstevel@tonic-gate 18637c478bd9Sstevel@tonic-gate /* 1864f127cb91Sfrankho * Check to see if the BPB we found is correct. 1865f127cb91Sfrankho * 1866f127cb91Sfrankho * This looks far more complicated that it needs to be for pure structural 1867f127cb91Sfrankho * validation. The reason for this is that parseBPB() is also used for 1868f127cb91Sfrankho * debugging purposes (mdb dcmd) and we therefore want a bitmap of which 1869c2aaf90fSgd * BPB fields (do not) have 'known good' values, even if we (do not) reject 1870c2aaf90fSgd * the BPB when attempting to mount the filesystem. 1871c2aaf90fSgd * 1872c2aaf90fSgd * Real-world usage of FAT shows there are a lot of corner-case situations 1873c2aaf90fSgd * and, following the specification strictly, invalid filesystems out there. 1874c2aaf90fSgd * Known are situations such as: 1875c2aaf90fSgd * - FAT12/FAT16 filesystems with garbage in either totsec16/32 1876c2aaf90fSgd * instead of the zero in one of the fields mandated by the spec 1877c2aaf90fSgd * - filesystems that claim to be larger than the partition they're in 1878c2aaf90fSgd * - filesystems without valid media descriptor 1879c2aaf90fSgd * - FAT32 filesystems with RootEntCnt != 0 1880c2aaf90fSgd * - FAT32 filesystems with less than 65526 clusters 1881c2aaf90fSgd * - FAT32 filesystems without valid FSI sector 1882c2aaf90fSgd * - FAT32 filesystems with FAT size in fatsec16 instead of fatsec32 1883c2aaf90fSgd * 1884c2aaf90fSgd * Such filesystems are accessible by PCFS - if it'd know to start with that 1885c2aaf90fSgd * the filesystem should be treated as a specific FAT type. Before S10, it 1886c2aaf90fSgd * relied on the PC/fdisk partition type for the purpose and almost completely 1887c2aaf90fSgd * ignored the BPB; now it ignores the partition type for anything else but 1888c2aaf90fSgd * logical drive enumeration, which can result in rejection of (invalid) 1889c2aaf90fSgd * FAT32 - if the partition ID says FAT32, but the filesystem, for example 1890c2aaf90fSgd * has less than 65526 clusters. 1891c2aaf90fSgd * 1892c2aaf90fSgd * Without a "force this fs as FAT{12,16,32}" tunable or mount option, it's 1893c2aaf90fSgd * not possible to allow all such mostly-compliant filesystems in unless one 1894c2aaf90fSgd * accepts false positives (definitely invalid filesystems that cause problems 1895c2aaf90fSgd * later). This at least allows to pinpoint why the mount failed. 1896c2aaf90fSgd * 1897c2aaf90fSgd * Due to the use of FAT on removeable media, all relaxations of the rules 1898c2aaf90fSgd * here need to be carefully evaluated wrt. to potential effects on PCFS 1899c2aaf90fSgd * resilience. A faulty/"mis-crafted" filesystem must not cause a panic, so 1900c2aaf90fSgd * beware. 19017c478bd9Sstevel@tonic-gate */ 1902f127cb91Sfrankho static int 1903f127cb91Sfrankho parseBPB(struct pcfs *fsp, uchar_t *bpb, int *valid) 19047c478bd9Sstevel@tonic-gate { 1905f127cb91Sfrankho fattype_t type; 1906f127cb91Sfrankho 1907f127cb91Sfrankho uint32_t ncl; /* number of clusters in file area */ 1908f127cb91Sfrankho uint32_t rec; 1909f127cb91Sfrankho uint32_t reserved; 1910f127cb91Sfrankho uint32_t fsisec, bkbootsec; 1911f127cb91Sfrankho blkcnt_t totsec, totsec16, totsec32, datasec; 1912f127cb91Sfrankho size_t fatsec, fatsec16, fatsec32, rdirsec; 1913f127cb91Sfrankho size_t secsize; 1914f127cb91Sfrankho len_t mediasize; 1915f127cb91Sfrankho uint64_t validflags = 0; 1916f127cb91Sfrankho 1917f127cb91Sfrankho if (VALID_BPBSIG(bpb_get_BPBSig(bpb))) 1918f127cb91Sfrankho validflags |= BPB_BPBSIG_OK; 1919f127cb91Sfrankho 1920f127cb91Sfrankho rec = bpb_get_RootEntCnt(bpb); 1921f127cb91Sfrankho reserved = bpb_get_RsvdSecCnt(bpb); 1922f127cb91Sfrankho fsisec = bpb_get_FSInfo32(bpb); 1923f127cb91Sfrankho bkbootsec = bpb_get_BkBootSec32(bpb); 1924f127cb91Sfrankho totsec16 = (blkcnt_t)bpb_get_TotSec16(bpb); 1925f127cb91Sfrankho totsec32 = (blkcnt_t)bpb_get_TotSec32(bpb); 1926f127cb91Sfrankho fatsec16 = bpb_get_FatSz16(bpb); 1927f127cb91Sfrankho fatsec32 = bpb_get_FatSz32(bpb); 1928f127cb91Sfrankho 1929f127cb91Sfrankho totsec = totsec16 ? totsec16 : totsec32; 1930f127cb91Sfrankho fatsec = fatsec16 ? fatsec16 : fatsec32; 1931f127cb91Sfrankho 1932f127cb91Sfrankho secsize = bpb_get_BytesPerSec(bpb); 1933f127cb91Sfrankho if (!VALID_SECSIZE(secsize)) 1934f127cb91Sfrankho secsize = fsp->pcfs_secsize; 1935f127cb91Sfrankho if (secsize != fsp->pcfs_secsize) { 1936f127cb91Sfrankho PC_DPRINTF3(3, "!pcfs: parseBPB, device (%x.%x):%d:\n", 1937f127cb91Sfrankho getmajor(fsp->pcfs_xdev), 1938f127cb91Sfrankho getminor(fsp->pcfs_xdev), fsp->pcfs_ldrive); 1939f127cb91Sfrankho PC_DPRINTF2(3, "!BPB secsize %d != " 1940f127cb91Sfrankho "autodetected media block size %d\n", 1941f127cb91Sfrankho (int)secsize, (int)fsp->pcfs_secsize); 1942f127cb91Sfrankho if (fsp->pcfs_ldrive) { 1943f127cb91Sfrankho /* 1944f127cb91Sfrankho * We've already attempted to parse the partition 1945f127cb91Sfrankho * table. If the block size used for that don't match 1946f127cb91Sfrankho * the PCFS sector size, we're hosed one way or the 1947f127cb91Sfrankho * other. Just try what happens. 1948f127cb91Sfrankho */ 1949f127cb91Sfrankho secsize = fsp->pcfs_secsize; 1950f127cb91Sfrankho PC_DPRINTF1(3, 1951f127cb91Sfrankho "!pcfs: Using autodetected secsize %d\n", 1952f127cb91Sfrankho (int)secsize); 19537c478bd9Sstevel@tonic-gate } else { 1954f127cb91Sfrankho /* 1955f127cb91Sfrankho * This allows mounting lofi images of PCFS partitions 1956f127cb91Sfrankho * with sectorsize != DEV_BSIZE. We can't parse the 1957f127cb91Sfrankho * partition table on whole-disk images unless the 1958f127cb91Sfrankho * (undocumented) "secsize=..." mount option is used, 1959f127cb91Sfrankho * but at least this allows us to mount if we have 1960f127cb91Sfrankho * an image of a partition. 1961f127cb91Sfrankho */ 1962f127cb91Sfrankho PC_DPRINTF1(3, 1963f127cb91Sfrankho "!pcfs: Using BPB secsize %d\n", (int)secsize); 19647c478bd9Sstevel@tonic-gate } 19657c478bd9Sstevel@tonic-gate } 19660576819eSwyllys 1967f127cb91Sfrankho if (fsp->pcfs_mediasize == 0) { 1968f127cb91Sfrankho mediasize = (len_t)totsec * (len_t)secsize; 1969c2aaf90fSgd /* 1970c2aaf90fSgd * This is not an error because not all devices support the 1971c2aaf90fSgd * dkio(7i) mediasize queries, and/or not all devices are 1972c2aaf90fSgd * partitioned. If we have not been able to figure out the 1973c2aaf90fSgd * size of the underlaying medium, we have to trust the BPB. 1974c2aaf90fSgd */ 1975f127cb91Sfrankho PC_DPRINTF4(3, "!pcfs: parseBPB: mediasize autodetect failed " 1976f127cb91Sfrankho "on device (%x.%x):%d, trusting BPB totsec (%lld Bytes)\n", 1977f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 1978f127cb91Sfrankho fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize); 1979f127cb91Sfrankho } else if ((len_t)totsec * (len_t)secsize > fsp->pcfs_mediasize) { 1980f127cb91Sfrankho cmn_err(CE_WARN, 1981f127cb91Sfrankho "!pcfs: autodetected mediasize (%lld Bytes) smaller than " 1982f127cb91Sfrankho "FAT BPB mediasize (%lld Bytes).\n" 1983f127cb91Sfrankho "truncated filesystem on device (%x.%x):%d, access errors " 1984f127cb91Sfrankho "possible.\n", 1985f127cb91Sfrankho (long long)fsp->pcfs_mediasize, 1986f127cb91Sfrankho (long long)(totsec * (blkcnt_t)secsize), 1987f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 1988f127cb91Sfrankho fsp->pcfs_ldrive); 1989f127cb91Sfrankho mediasize = fsp->pcfs_mediasize; 19907c478bd9Sstevel@tonic-gate } else { 19917c478bd9Sstevel@tonic-gate /* 1992f127cb91Sfrankho * This is actually ok. A FAT needs not occupy the maximum 1993f127cb91Sfrankho * space available in its partition, it can be shorter. 19947c478bd9Sstevel@tonic-gate */ 1995f127cb91Sfrankho mediasize = (len_t)totsec * (len_t)secsize; 19967c478bd9Sstevel@tonic-gate } 19977c478bd9Sstevel@tonic-gate 19987c478bd9Sstevel@tonic-gate /* 1999f127cb91Sfrankho * Since we let just about anything pass through this function, 2000f127cb91Sfrankho * fence against divide-by-zero here. 20017c478bd9Sstevel@tonic-gate */ 2002f127cb91Sfrankho if (secsize) 2003f127cb91Sfrankho rdirsec = roundup(rec * 32, secsize) / secsize; 2004f127cb91Sfrankho else 2005f127cb91Sfrankho rdirsec = 0; 20067c478bd9Sstevel@tonic-gate 20070576819eSwyllys /* 2008f127cb91Sfrankho * This assignment is necessary before pc_dbdaddr() can first be 2009f127cb91Sfrankho * used. Must initialize the value here. 20100576819eSwyllys */ 2011f127cb91Sfrankho fsp->pcfs_secsize = secsize; 2012f127cb91Sfrankho fsp->pcfs_sdshift = ddi_ffs(secsize / DEV_BSIZE) - 1; 2013f127cb91Sfrankho 2014f127cb91Sfrankho fsp->pcfs_mediasize = mediasize; 2015f127cb91Sfrankho 2016f127cb91Sfrankho fsp->pcfs_spcl = bpb_get_SecPerClus(bpb); 2017f127cb91Sfrankho fsp->pcfs_numfat = bpb_get_NumFATs(bpb); 2018f127cb91Sfrankho fsp->pcfs_mediadesc = bpb_get_Media(bpb); 2019f127cb91Sfrankho fsp->pcfs_clsize = secsize * fsp->pcfs_spcl; 2020f127cb91Sfrankho fsp->pcfs_rdirsec = rdirsec; 2021f127cb91Sfrankho 20227c478bd9Sstevel@tonic-gate /* 2023f127cb91Sfrankho * Remember: All PCFS offset calculations in sectors. Before I/O 2024f127cb91Sfrankho * is done, convert to DEV_BSIZE units via pc_dbdaddr(). This is 2025f127cb91Sfrankho * necessary so that media with > 512Byte sector sizes work correctly. 20267c478bd9Sstevel@tonic-gate */ 2027f127cb91Sfrankho fsp->pcfs_fatstart = fsp->pcfs_dosstart + reserved; 2028f127cb91Sfrankho fsp->pcfs_rdirstart = fsp->pcfs_fatstart + fsp->pcfs_numfat * fatsec; 2029f127cb91Sfrankho fsp->pcfs_datastart = fsp->pcfs_rdirstart + rdirsec; 2030f127cb91Sfrankho datasec = totsec - 2031f127cb91Sfrankho (blkcnt_t)fatsec * fsp->pcfs_numfat - 2032f127cb91Sfrankho (blkcnt_t)rdirsec - 2033f127cb91Sfrankho (blkcnt_t)reserved; 2034f127cb91Sfrankho 2035f127cb91Sfrankho DTRACE_PROBE4(fatgeometry, 2036f127cb91Sfrankho blkcnt_t, totsec, size_t, fatsec, 2037f127cb91Sfrankho size_t, rdirsec, blkcnt_t, datasec); 2038f127cb91Sfrankho 20397c478bd9Sstevel@tonic-gate /* 2040c2aaf90fSgd * 'totsec' is taken directly from the BPB and guaranteed to fit 2041c2aaf90fSgd * into a 32bit unsigned integer. The calculation of 'datasec', 2042c2aaf90fSgd * on the other hand, could underflow for incorrect values in 2043c2aaf90fSgd * rdirsec/reserved/fatsec. Check for that. 2044c2aaf90fSgd * We also check that the BPB conforms to the FAT specification's 2045c2aaf90fSgd * requirement that either of the 16/32bit total sector counts 2046c2aaf90fSgd * must be zero. 20477c478bd9Sstevel@tonic-gate */ 2048f127cb91Sfrankho if (totsec != 0 && 2049f127cb91Sfrankho (totsec16 == totsec32 || totsec16 == 0 || totsec32 == 0) && 2050f127cb91Sfrankho datasec < totsec && datasec <= UINT32_MAX) 2051f127cb91Sfrankho validflags |= BPB_TOTSEC_OK; 2052f127cb91Sfrankho 2053c2aaf90fSgd if ((len_t)totsec * (len_t)secsize <= mediasize) 2054f127cb91Sfrankho validflags |= BPB_MEDIASZ_OK; 2055f127cb91Sfrankho 2056f127cb91Sfrankho if (VALID_SECSIZE(secsize)) 2057f127cb91Sfrankho validflags |= BPB_SECSIZE_OK; 2058f127cb91Sfrankho if (VALID_SPCL(fsp->pcfs_spcl)) 2059f127cb91Sfrankho validflags |= BPB_SECPERCLUS_OK; 2060f127cb91Sfrankho if (VALID_CLSIZE(fsp->pcfs_clsize)) 2061f127cb91Sfrankho validflags |= BPB_CLSIZE_OK; 2062f127cb91Sfrankho if (VALID_NUMFATS(fsp->pcfs_numfat)) 2063f127cb91Sfrankho validflags |= BPB_NUMFAT_OK; 2064f127cb91Sfrankho if (VALID_RSVDSEC(reserved) && reserved < totsec) 2065f127cb91Sfrankho validflags |= BPB_RSVDSECCNT_OK; 2066f127cb91Sfrankho if (VALID_MEDIA(fsp->pcfs_mediadesc)) 2067f127cb91Sfrankho validflags |= BPB_MEDIADESC_OK; 2068f127cb91Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig16(bpb))) 2069f127cb91Sfrankho validflags |= BPB_BOOTSIG16_OK; 2070f127cb91Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig32(bpb))) 2071f127cb91Sfrankho validflags |= BPB_BOOTSIG32_OK; 2072f127cb91Sfrankho if (VALID_FSTYPSTR16(bpb_FilSysType16(bpb))) 2073f127cb91Sfrankho validflags |= BPB_FSTYPSTR16_OK; 2074f127cb91Sfrankho if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb))) 2075f127cb91Sfrankho validflags |= BPB_FSTYPSTR32_OK; 2076f127cb91Sfrankho if (VALID_OEMNAME(bpb_OEMName(bpb))) 2077f127cb91Sfrankho validflags |= BPB_OEMNAME_OK; 2078f127cb91Sfrankho if (bkbootsec > 0 && bkbootsec <= reserved && fsisec != bkbootsec) 2079f127cb91Sfrankho validflags |= BPB_BKBOOTSEC_OK; 2080f127cb91Sfrankho if (fsisec > 0 && fsisec <= reserved) 2081f127cb91Sfrankho validflags |= BPB_FSISEC_OK; 2082f127cb91Sfrankho if (VALID_JMPBOOT(bpb_jmpBoot(bpb))) 2083f127cb91Sfrankho validflags |= BPB_JMPBOOT_OK; 2084f127cb91Sfrankho if (VALID_FSVER32(bpb_get_FSVer32(bpb))) 2085f127cb91Sfrankho validflags |= BPB_FSVER_OK; 2086f127cb91Sfrankho if (VALID_VOLLAB(bpb_VolLab16(bpb))) 2087f127cb91Sfrankho validflags |= BPB_VOLLAB16_OK; 2088f127cb91Sfrankho if (VALID_VOLLAB(bpb_VolLab32(bpb))) 2089f127cb91Sfrankho validflags |= BPB_VOLLAB32_OK; 2090f127cb91Sfrankho if (VALID_EXTFLAGS(bpb_get_ExtFlags32(bpb))) 2091f127cb91Sfrankho validflags |= BPB_EXTFLAGS_OK; 20927c478bd9Sstevel@tonic-gate 20937c478bd9Sstevel@tonic-gate /* 2094f127cb91Sfrankho * Try to determine which FAT format to use. 2095f127cb91Sfrankho * 2096f127cb91Sfrankho * Calculate the number of clusters in order to determine 2097f127cb91Sfrankho * the type of FAT we are looking at. This is the only 2098f127cb91Sfrankho * recommended way of determining FAT type, though there 2099f127cb91Sfrankho * are other hints in the data, this is the best way. 2100f127cb91Sfrankho * 2101f127cb91Sfrankho * Since we let just about "anything" pass through this function 2102f127cb91Sfrankho * without early exits, fence against divide-by-zero here. 2103f127cb91Sfrankho * 2104f127cb91Sfrankho * datasec was already validated against UINT32_MAX so we know 2105f127cb91Sfrankho * the result will not overflow the 32bit calculation. 21067c478bd9Sstevel@tonic-gate */ 2107f127cb91Sfrankho if (fsp->pcfs_spcl) 2108f127cb91Sfrankho ncl = (uint32_t)datasec / fsp->pcfs_spcl; 2109f127cb91Sfrankho else 2110f127cb91Sfrankho ncl = 0; 21117c478bd9Sstevel@tonic-gate 2112f127cb91Sfrankho fsp->pcfs_ncluster = ncl; 21137c478bd9Sstevel@tonic-gate 2114f127cb91Sfrankho /* 2115f127cb91Sfrankho * From the Microsoft FAT specification: 2116f127cb91Sfrankho * In the following example, when it says <, it does not mean <=. 2117f127cb91Sfrankho * Note also that the numbers are correct. The first number for 2118f127cb91Sfrankho * FAT12 is 4085; the second number for FAT16 is 65525. These numbers 2119f127cb91Sfrankho * and the '<' signs are not wrong. 2120f127cb91Sfrankho * 2121f127cb91Sfrankho * We "specialdetect" the corner cases, and use at least one "extra" 2122f127cb91Sfrankho * criterion to decide whether it's FAT16 or FAT32 if the cluster 2123f127cb91Sfrankho * count is dangerously close to the boundaries. 2124f127cb91Sfrankho */ 2125f127cb91Sfrankho 2126f127cb91Sfrankho if (ncl <= PCF_FIRSTCLUSTER) { 2127f127cb91Sfrankho type = FAT_UNKNOWN; 2128f127cb91Sfrankho } else if (ncl < 4085) { 2129f127cb91Sfrankho type = FAT12; 2130f127cb91Sfrankho } else if (ncl <= 4096) { 2131f127cb91Sfrankho type = FAT_QUESTIONABLE; 2132f127cb91Sfrankho } else if (ncl < 65525) { 2133f127cb91Sfrankho type = FAT16; 2134f127cb91Sfrankho } else if (ncl <= 65536) { 2135f127cb91Sfrankho type = FAT_QUESTIONABLE; 2136f127cb91Sfrankho } else if (ncl < PCF_LASTCLUSTER32) { 2137f127cb91Sfrankho type = FAT32; 2138f127cb91Sfrankho } else { 2139f127cb91Sfrankho type = FAT_UNKNOWN; 21407c478bd9Sstevel@tonic-gate } 21417c478bd9Sstevel@tonic-gate 2142f127cb91Sfrankho DTRACE_PROBE4(parseBPB__initial, 2143f127cb91Sfrankho struct pcfs *, fsp, unsigned char *, bpb, 2144f127cb91Sfrankho int, validflags, fattype_t, type); 21457c478bd9Sstevel@tonic-gate 2146f127cb91Sfrankho recheck: 2147f127cb91Sfrankho fsp->pcfs_fatsec = fatsec; 2148f127cb91Sfrankho 2149f127cb91Sfrankho /* Do some final sanity checks for each specific type of FAT */ 2150f127cb91Sfrankho switch (type) { 2151f127cb91Sfrankho case FAT12: 2152f127cb91Sfrankho if (rec != 0) 2153f127cb91Sfrankho validflags |= BPB_ROOTENTCNT_OK; 2154f127cb91Sfrankho if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec || 2155f127cb91Sfrankho bpb_get_TotSec16(bpb) == 0) 2156f127cb91Sfrankho validflags |= BPB_TOTSEC16_OK; 2157f127cb91Sfrankho if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec || 2158f127cb91Sfrankho bpb_get_TotSec32(bpb) == 0) 2159f127cb91Sfrankho validflags |= BPB_TOTSEC32_OK; 2160f127cb91Sfrankho if (bpb_get_FatSz16(bpb) == fatsec) 2161f127cb91Sfrankho validflags |= BPB_FATSZ16_OK; 2162f127cb91Sfrankho if (fatsec * secsize >= ncl * 3 / 2) 2163f127cb91Sfrankho validflags |= BPB_FATSZ_OK; 2164f127cb91Sfrankho if (ncl < 4085) 2165f127cb91Sfrankho validflags |= BPB_NCLUSTERS_OK; 2166f127cb91Sfrankho 2167f127cb91Sfrankho fsp->pcfs_lastclmark = (PCF_LASTCLUSTER & 0xfff); 2168f127cb91Sfrankho fsp->pcfs_rootblksize = 2169f127cb91Sfrankho fsp->pcfs_rdirsec * secsize; 2170f127cb91Sfrankho fsp->pcfs_fsistart = 0; 2171f127cb91Sfrankho 2172f127cb91Sfrankho if ((validflags & FAT12_VALIDMSK) != FAT12_VALIDMSK) 2173f127cb91Sfrankho type = FAT_UNKNOWN; 2174f127cb91Sfrankho break; 2175f127cb91Sfrankho case FAT16: 2176f127cb91Sfrankho if (rec != 0) 2177f127cb91Sfrankho validflags |= BPB_ROOTENTCNT_OK; 2178f127cb91Sfrankho if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec || 2179f127cb91Sfrankho bpb_get_TotSec16(bpb) == 0) 2180f127cb91Sfrankho validflags |= BPB_TOTSEC16_OK; 2181f127cb91Sfrankho if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec || 2182f127cb91Sfrankho bpb_get_TotSec32(bpb) == 0) 2183f127cb91Sfrankho validflags |= BPB_TOTSEC32_OK; 2184f127cb91Sfrankho if (bpb_get_FatSz16(bpb) == fatsec) 2185f127cb91Sfrankho validflags |= BPB_FATSZ16_OK; 2186f127cb91Sfrankho if (fatsec * secsize >= ncl * 2) 2187f127cb91Sfrankho validflags |= BPB_FATSZ_OK; 2188f127cb91Sfrankho if (ncl >= 4085 && ncl < 65525) 2189f127cb91Sfrankho validflags |= BPB_NCLUSTERS_OK; 2190f127cb91Sfrankho 2191f127cb91Sfrankho fsp->pcfs_lastclmark = PCF_LASTCLUSTER; 2192f127cb91Sfrankho fsp->pcfs_rootblksize = 2193f127cb91Sfrankho fsp->pcfs_rdirsec * secsize; 2194f127cb91Sfrankho fsp->pcfs_fsistart = 0; 2195f127cb91Sfrankho 2196f127cb91Sfrankho if ((validflags & FAT16_VALIDMSK) != FAT16_VALIDMSK) 2197f127cb91Sfrankho type = FAT_UNKNOWN; 2198f127cb91Sfrankho break; 2199f127cb91Sfrankho case FAT32: 2200f127cb91Sfrankho if (rec == 0) 2201f127cb91Sfrankho validflags |= BPB_ROOTENTCNT_OK; 2202f127cb91Sfrankho if (bpb_get_TotSec16(bpb) == 0) 2203f127cb91Sfrankho validflags |= BPB_TOTSEC16_OK; 2204f127cb91Sfrankho if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec) 2205f127cb91Sfrankho validflags |= BPB_TOTSEC32_OK; 2206f127cb91Sfrankho if (bpb_get_FatSz16(bpb) == 0) 2207f127cb91Sfrankho validflags |= BPB_FATSZ16_OK; 2208f127cb91Sfrankho if (bpb_get_FatSz32(bpb) == fatsec) 2209f127cb91Sfrankho validflags |= BPB_FATSZ32_OK; 2210f127cb91Sfrankho if (fatsec * secsize >= ncl * 4) 2211f127cb91Sfrankho validflags |= BPB_FATSZ_OK; 2212f127cb91Sfrankho if (ncl >= 65525 && ncl < PCF_LASTCLUSTER32) 2213f127cb91Sfrankho validflags |= BPB_NCLUSTERS_OK; 2214f127cb91Sfrankho 2215f127cb91Sfrankho fsp->pcfs_lastclmark = PCF_LASTCLUSTER32; 2216f127cb91Sfrankho fsp->pcfs_rootblksize = fsp->pcfs_clsize; 2217f127cb91Sfrankho fsp->pcfs_fsistart = fsp->pcfs_dosstart + fsisec; 2218f127cb91Sfrankho if (validflags & BPB_FSISEC_OK) 2219f127cb91Sfrankho fsp->pcfs_flags |= PCFS_FSINFO_OK; 2220f127cb91Sfrankho fsp->pcfs_rootclnum = bpb_get_RootClus32(bpb); 2221f127cb91Sfrankho if (pc_validcl(fsp, fsp->pcfs_rootclnum)) 2222f127cb91Sfrankho validflags |= BPB_ROOTCLUSTER_OK; 2223f127cb91Sfrankho 2224f127cb91Sfrankho /* 2225f127cb91Sfrankho * Current PCFS code only works if 'pcfs_rdirstart' 2226f127cb91Sfrankho * contains the root cluster number on FAT32. 2227f127cb91Sfrankho * That's a mis-use and would better be changed. 2228f127cb91Sfrankho */ 2229f127cb91Sfrankho fsp->pcfs_rdirstart = (daddr_t)fsp->pcfs_rootclnum; 2230f127cb91Sfrankho 2231f127cb91Sfrankho if ((validflags & FAT32_VALIDMSK) != FAT32_VALIDMSK) 2232f127cb91Sfrankho type = FAT_UNKNOWN; 2233f127cb91Sfrankho break; 2234f127cb91Sfrankho case FAT_QUESTIONABLE: 2235f127cb91Sfrankho type = secondaryBPBChecks(fsp, bpb, secsize); 2236f127cb91Sfrankho goto recheck; 2237f127cb91Sfrankho default: 2238f127cb91Sfrankho ASSERT(type == FAT_UNKNOWN); 2239f127cb91Sfrankho break; 22407c478bd9Sstevel@tonic-gate } 22417c478bd9Sstevel@tonic-gate 2242f127cb91Sfrankho ASSERT(type != FAT_QUESTIONABLE); 22437c478bd9Sstevel@tonic-gate 2244f127cb91Sfrankho fsp->pcfs_fattype = type; 22457c478bd9Sstevel@tonic-gate 2246f127cb91Sfrankho if (valid) 2247f127cb91Sfrankho *valid = validflags; 2248f127cb91Sfrankho 2249f127cb91Sfrankho DTRACE_PROBE4(parseBPB__final, 2250f127cb91Sfrankho struct pcfs *, fsp, unsigned char *, bpb, 2251f127cb91Sfrankho int, validflags, fattype_t, type); 2252f127cb91Sfrankho 2253f127cb91Sfrankho if (type != FAT_UNKNOWN) { 2254f127cb91Sfrankho ASSERT((secsize & (DEV_BSIZE - 1)) == 0); 2255f127cb91Sfrankho ASSERT(ISP2(secsize / DEV_BSIZE)); 2256f127cb91Sfrankho return (1); 22577c478bd9Sstevel@tonic-gate } 2258f127cb91Sfrankho 2259f127cb91Sfrankho return (0); 22607c478bd9Sstevel@tonic-gate } 22617c478bd9Sstevel@tonic-gate 2262f127cb91Sfrankho 2263f127cb91Sfrankho /* 2264f127cb91Sfrankho * Detect the device's native block size (sector size). 2265f127cb91Sfrankho * 2266f127cb91Sfrankho * Test whether the device is: 2267f127cb91Sfrankho * - a floppy device from a known controller type via DKIOCINFO 2268f127cb91Sfrankho * - a real floppy using the fd(7d) driver and capable of fdio(7I) ioctls 2269f127cb91Sfrankho * - a PCMCIA sram memory card (pseudofloppy) using pcram(7d) 2270f127cb91Sfrankho * - a USB floppy drive (identified by drive geometry) 2271f127cb91Sfrankho * 2272f127cb91Sfrankho * Detecting a floppy will make PCFS metadata updates on such media synchronous, 2273f127cb91Sfrankho * to minimize risks due to slow I/O and user hotplugging / device ejection. 2274f127cb91Sfrankho * 2275f127cb91Sfrankho * This might be a bit wasteful on kernel stack space; if anyone's 2276f127cb91Sfrankho * bothered by this, kmem_alloc/kmem_free the ioctl arguments... 2277f127cb91Sfrankho */ 2278f127cb91Sfrankho static void 2279f127cb91Sfrankho pcfs_device_getinfo(struct pcfs *fsp) 22807c478bd9Sstevel@tonic-gate { 2281f127cb91Sfrankho dev_t rdev = fsp->pcfs_xdev; 2282f127cb91Sfrankho int error; 2283f127cb91Sfrankho union { 2284f127cb91Sfrankho struct dk_minfo mi; 2285f127cb91Sfrankho struct dk_cinfo ci; 2286f127cb91Sfrankho struct dk_geom gi; 2287f127cb91Sfrankho struct fd_char fc; 2288f127cb91Sfrankho } arg; /* save stackspace ... */ 2289f127cb91Sfrankho intptr_t argp = (intptr_t)&arg; 2290f127cb91Sfrankho ldi_handle_t lh; 2291f127cb91Sfrankho ldi_ident_t li; 2292f127cb91Sfrankho int isfloppy, isremoveable, ishotpluggable; 2293f127cb91Sfrankho cred_t *cr = CRED(); 2294f127cb91Sfrankho 2295f127cb91Sfrankho if (ldi_ident_from_dev(rdev, &li)) 2296f127cb91Sfrankho goto out; 2297f127cb91Sfrankho 2298f127cb91Sfrankho error = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, cr, &lh, li); 2299f127cb91Sfrankho ldi_ident_release(li); 2300f127cb91Sfrankho if (error) 2301f127cb91Sfrankho goto out; 23027c478bd9Sstevel@tonic-gate 23037c478bd9Sstevel@tonic-gate /* 2304f127cb91Sfrankho * Not sure if this could possibly happen. It'd be a bit like 2305f127cb91Sfrankho * VOP_OPEN() changing the passed-in vnode ptr. We're just not 2306f127cb91Sfrankho * expecting it, needs some thought if triggered ... 23077c478bd9Sstevel@tonic-gate */ 2308f127cb91Sfrankho ASSERT(fsp->pcfs_xdev == rdev); 2309f127cb91Sfrankho 2310f127cb91Sfrankho /* 2311f127cb91Sfrankho * Check for removeable/hotpluggable media. 2312f127cb91Sfrankho */ 2313f127cb91Sfrankho if (ldi_ioctl(lh, DKIOCREMOVABLE, 2314f127cb91Sfrankho (intptr_t)&isremoveable, FKIOCTL, cr, NULL)) { 2315f127cb91Sfrankho isremoveable = 0; 23167c478bd9Sstevel@tonic-gate } 2317f127cb91Sfrankho if (ldi_ioctl(lh, DKIOCHOTPLUGGABLE, 2318f127cb91Sfrankho (intptr_t)&ishotpluggable, FKIOCTL, cr, NULL)) { 2319f127cb91Sfrankho ishotpluggable = 0; 2320f127cb91Sfrankho } 2321f127cb91Sfrankho 2322f127cb91Sfrankho /* 2323f127cb91Sfrankho * Make sure we don't use "half-initialized" values if the ioctls fail. 2324f127cb91Sfrankho */ 2325f127cb91Sfrankho if (ldi_ioctl(lh, DKIOCGMEDIAINFO, argp, FKIOCTL, cr, NULL)) { 2326f127cb91Sfrankho bzero(&arg, sizeof (arg)); 2327f127cb91Sfrankho fsp->pcfs_mediasize = 0; 2328f127cb91Sfrankho } else { 2329f127cb91Sfrankho fsp->pcfs_mediasize = 2330f127cb91Sfrankho (len_t)arg.mi.dki_lbsize * 2331f127cb91Sfrankho (len_t)arg.mi.dki_capacity; 2332f127cb91Sfrankho } 2333f127cb91Sfrankho 2334f127cb91Sfrankho if (VALID_SECSIZE(arg.mi.dki_lbsize)) { 2335f127cb91Sfrankho if (fsp->pcfs_secsize == 0) { 2336f127cb91Sfrankho fsp->pcfs_secsize = arg.mi.dki_lbsize; 2337f127cb91Sfrankho fsp->pcfs_sdshift = 2338f127cb91Sfrankho ddi_ffs(arg.mi.dki_lbsize / DEV_BSIZE) - 1; 2339f127cb91Sfrankho } else { 2340f127cb91Sfrankho PC_DPRINTF4(1, "!pcfs: autodetected media block size " 2341f127cb91Sfrankho "%d, device (%x.%x), different from user-provided " 2342f127cb91Sfrankho "%d. User override - ignoring autodetect result.\n", 2343f127cb91Sfrankho arg.mi.dki_lbsize, 2344f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 2345f127cb91Sfrankho fsp->pcfs_secsize); 23467c478bd9Sstevel@tonic-gate } 2347f127cb91Sfrankho } else if (arg.mi.dki_lbsize) { 2348f127cb91Sfrankho PC_DPRINTF3(1, "!pcfs: autodetected media block size " 2349f127cb91Sfrankho "%d, device (%x.%x), invalid (not 512, 1024, 2048, 4096). " 2350f127cb91Sfrankho "Ignoring autodetect result.\n", 2351f127cb91Sfrankho arg.mi.dki_lbsize, 2352f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev)); 23537c478bd9Sstevel@tonic-gate } 23547c478bd9Sstevel@tonic-gate 2355f127cb91Sfrankho /* 2356f127cb91Sfrankho * We treat the following media types as a floppy by default. 2357f127cb91Sfrankho */ 2358f127cb91Sfrankho isfloppy = 2359f127cb91Sfrankho (arg.mi.dki_media_type == DK_FLOPPY || 2360f127cb91Sfrankho arg.mi.dki_media_type == DK_ZIP || 2361f127cb91Sfrankho arg.mi.dki_media_type == DK_JAZ); 23627c478bd9Sstevel@tonic-gate 23637c478bd9Sstevel@tonic-gate /* 2364f127cb91Sfrankho * if this device understands fdio(7I) requests it's 2365f127cb91Sfrankho * obviously a floppy drive. 23667c478bd9Sstevel@tonic-gate */ 2367f127cb91Sfrankho if (!isfloppy && 2368f127cb91Sfrankho !ldi_ioctl(lh, FDIOGCHAR, argp, FKIOCTL, cr, NULL)) 2369f127cb91Sfrankho isfloppy = 1; 2370f127cb91Sfrankho 23717c478bd9Sstevel@tonic-gate /* 2372f127cb91Sfrankho * some devices (PCMCIA pseudofloppies) we like to treat 2373f127cb91Sfrankho * as floppies, but they don't understand fdio(7I) requests. 23747c478bd9Sstevel@tonic-gate */ 2375f127cb91Sfrankho if (!isfloppy && 2376f127cb91Sfrankho !ldi_ioctl(lh, DKIOCINFO, argp, FKIOCTL, cr, NULL) && 2377f127cb91Sfrankho (arg.ci.dki_ctype == DKC_WDC2880 || 2378f127cb91Sfrankho arg.ci.dki_ctype == DKC_NCRFLOPPY || 2379f127cb91Sfrankho arg.ci.dki_ctype == DKC_SMSFLOPPY || 2380f127cb91Sfrankho arg.ci.dki_ctype == DKC_INTEL82077 || 2381f127cb91Sfrankho (arg.ci.dki_ctype == DKC_PCMCIA_MEM && 2382f127cb91Sfrankho arg.ci.dki_flags & DKI_PCMCIA_PFD))) 2383f127cb91Sfrankho isfloppy = 1; 23847c478bd9Sstevel@tonic-gate 23857c478bd9Sstevel@tonic-gate /* 2386f127cb91Sfrankho * This is the "final fallback" test - media with 2387f127cb91Sfrankho * 2 heads and 80 cylinders are assumed to be floppies. 2388f127cb91Sfrankho * This is normally true for USB floppy drives ... 23897c478bd9Sstevel@tonic-gate */ 2390f127cb91Sfrankho if (!isfloppy && 2391f127cb91Sfrankho !ldi_ioctl(lh, DKIOCGGEOM, argp, FKIOCTL, cr, NULL) && 2392f127cb91Sfrankho (arg.gi.dkg_ncyl == 80 && arg.gi.dkg_nhead == 2)) 2393f127cb91Sfrankho isfloppy = 1; 23947c478bd9Sstevel@tonic-gate 2395f127cb91Sfrankho /* 2396f127cb91Sfrankho * This is similar to the "old" PCFS code that sets this flag 2397f127cb91Sfrankho * just based on the media descriptor being 0xf8 (MD_FIXED). 2398f127cb91Sfrankho * Should be re-worked. We really need some specialcasing for 2399f127cb91Sfrankho * removeable media. 2400f127cb91Sfrankho */ 2401f127cb91Sfrankho if (!isfloppy) { 2402f127cb91Sfrankho fsp->pcfs_flags |= PCFS_NOCHK; 2403f127cb91Sfrankho } 2404f127cb91Sfrankho 2405f127cb91Sfrankho /* 2406f127cb91Sfrankho * We automatically disable access time updates if the medium is 2407f127cb91Sfrankho * removeable and/or hotpluggable, and the admin did not explicitly 2408f127cb91Sfrankho * request access time updates (via the "atime" mount option). 2409f127cb91Sfrankho * The majority of flash-based media should fit this category. 2410f127cb91Sfrankho * Minimizing write access extends the lifetime of your memory stick ! 2411f127cb91Sfrankho */ 2412f127cb91Sfrankho if (!vfs_optionisset(fsp->pcfs_vfs, MNTOPT_ATIME, NULL) && 2413f127cb91Sfrankho (isremoveable || ishotpluggable | isfloppy)) { 2414f127cb91Sfrankho fsp->pcfs_flags |= PCFS_NOATIME; 2415f127cb91Sfrankho } 2416f127cb91Sfrankho 2417f127cb91Sfrankho (void) ldi_close(lh, FREAD, cr); 2418f127cb91Sfrankho out: 2419f127cb91Sfrankho if (fsp->pcfs_secsize == 0) { 2420f127cb91Sfrankho PC_DPRINTF3(1, "!pcfs: media block size autodetection " 2421f127cb91Sfrankho "device (%x.%x) failed, no user-provided fallback. " 2422f127cb91Sfrankho "Using %d bytes.\n", 2423f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 2424f127cb91Sfrankho DEV_BSIZE); 2425f127cb91Sfrankho fsp->pcfs_secsize = DEV_BSIZE; 2426f127cb91Sfrankho fsp->pcfs_sdshift = 0; 2427f127cb91Sfrankho } 2428f127cb91Sfrankho ASSERT(fsp->pcfs_secsize % DEV_BSIZE == 0); 2429f127cb91Sfrankho ASSERT(VALID_SECSIZE(fsp->pcfs_secsize)); 24307c478bd9Sstevel@tonic-gate } 24317c478bd9Sstevel@tonic-gate 24327c478bd9Sstevel@tonic-gate /* 2433f127cb91Sfrankho * Get the FAT type for the DOS medium. 2434f127cb91Sfrankho * 2435f127cb91Sfrankho * ------------------------- 2436f127cb91Sfrankho * According to Microsoft: 2437f127cb91Sfrankho * The FAT type one of FAT12, FAT16, or FAT32 is determined by the 2438f127cb91Sfrankho * count of clusters on the volume and nothing else. 2439f127cb91Sfrankho * ------------------------- 2440f127cb91Sfrankho * 24417c478bd9Sstevel@tonic-gate */ 24427c478bd9Sstevel@tonic-gate static int 2443f127cb91Sfrankho pc_getfattype(struct pcfs *fsp) 24447c478bd9Sstevel@tonic-gate { 2445f127cb91Sfrankho int error = 0; 2446f127cb91Sfrankho buf_t *bp = NULL; 2447f127cb91Sfrankho struct vnode *devvp = fsp->pcfs_devvp; 2448f127cb91Sfrankho dev_t dev = devvp->v_rdev; 24497c478bd9Sstevel@tonic-gate 2450f127cb91Sfrankho /* 2451f127cb91Sfrankho * Detect the native block size of the medium, and attempt to 2452f127cb91Sfrankho * detect whether the medium is removeable. 2453f127cb91Sfrankho * We do treat removeable media (floppies, PCMCIA memory cards, 2454f127cb91Sfrankho * USB and FireWire disks) differently wrt. to the frequency 2455f127cb91Sfrankho * and synchronicity of FAT updates. 2456f127cb91Sfrankho * We need to know the media block size in order to be able to 2457f127cb91Sfrankho * parse the partition table. 2458f127cb91Sfrankho */ 2459f127cb91Sfrankho pcfs_device_getinfo(fsp); 24607c478bd9Sstevel@tonic-gate 24617c478bd9Sstevel@tonic-gate /* 2462f127cb91Sfrankho * Unpartitioned media (floppies and some removeable devices) 2463f127cb91Sfrankho * don't have a partition table, the FAT BPB is at disk block 0. 2464f127cb91Sfrankho * Start out by reading block 0. 24657c478bd9Sstevel@tonic-gate */ 2466f127cb91Sfrankho fsp->pcfs_dosstart = 0; 2467f127cb91Sfrankho bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), fsp->pcfs_secsize); 2468f127cb91Sfrankho 2469f127cb91Sfrankho if (error = geterror(bp)) 2470f127cb91Sfrankho goto out; 2471f127cb91Sfrankho 24727c478bd9Sstevel@tonic-gate /* 2473f127cb91Sfrankho * If a logical drive number is requested, parse the partition table 2474f127cb91Sfrankho * and attempt to locate it. Otherwise, proceed immediately to the 2475f127cb91Sfrankho * BPB check. findTheDrive(), if successful, returns the disk block 2476f127cb91Sfrankho * number where the requested partition starts in "startsec". 24777c478bd9Sstevel@tonic-gate */ 2478f127cb91Sfrankho if (fsp->pcfs_ldrive != 0) { 2479f127cb91Sfrankho PC_DPRINTF3(5, "!pcfs: pc_getfattype: using FDISK table on " 2480f127cb91Sfrankho "device (%x,%x):%d to find BPB\n", 2481f127cb91Sfrankho getmajor(dev), getminor(dev), fsp->pcfs_ldrive); 2482f127cb91Sfrankho 2483f127cb91Sfrankho if (error = findTheDrive(fsp, &bp)) 2484f127cb91Sfrankho goto out; 2485f127cb91Sfrankho 2486f127cb91Sfrankho ASSERT(fsp->pcfs_dosstart != 0); 2487f127cb91Sfrankho 24887c478bd9Sstevel@tonic-gate brelse(bp); 2489f127cb91Sfrankho bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), 2490f127cb91Sfrankho fsp->pcfs_secsize); 2491f127cb91Sfrankho if (error = geterror(bp)) 2492f127cb91Sfrankho goto out; 24937c478bd9Sstevel@tonic-gate } 2494f127cb91Sfrankho 2495f127cb91Sfrankho /* 2496f127cb91Sfrankho * Validate the BPB and fill in the instance structure. 2497f127cb91Sfrankho */ 2498f127cb91Sfrankho if (!parseBPB(fsp, (uchar_t *)bp->b_un.b_addr, NULL)) { 2499f127cb91Sfrankho PC_DPRINTF4(1, "!pcfs: pc_getfattype: No FAT BPB on " 2500f127cb91Sfrankho "device (%x.%x):%d, disk LBA %u\n", 2501f127cb91Sfrankho getmajor(dev), getminor(dev), fsp->pcfs_ldrive, 2502f127cb91Sfrankho (uint_t)pc_dbdaddr(fsp, fsp->pcfs_dosstart)); 2503f127cb91Sfrankho error = EINVAL; 2504f127cb91Sfrankho goto out; 25057c478bd9Sstevel@tonic-gate } 2506f127cb91Sfrankho 2507f127cb91Sfrankho ASSERT(fsp->pcfs_fattype != FAT_UNKNOWN); 2508f127cb91Sfrankho 2509f127cb91Sfrankho out: 2510f127cb91Sfrankho /* 2511f127cb91Sfrankho * Release the buffer used 2512f127cb91Sfrankho */ 2513f127cb91Sfrankho if (bp != NULL) 2514f127cb91Sfrankho brelse(bp); 2515f127cb91Sfrankho return (error); 25167c478bd9Sstevel@tonic-gate } 25177c478bd9Sstevel@tonic-gate 2518f127cb91Sfrankho 25197c478bd9Sstevel@tonic-gate /* 2520f127cb91Sfrankho * Get the file allocation table. 2521f127cb91Sfrankho * If there is an old FAT, invalidate it. 25227c478bd9Sstevel@tonic-gate */ 2523f127cb91Sfrankho int 2524f127cb91Sfrankho pc_getfat(struct pcfs *fsp) 25257c478bd9Sstevel@tonic-gate { 2526f127cb91Sfrankho struct buf *bp = NULL; 2527f127cb91Sfrankho uchar_t *fatp = NULL; 2528f127cb91Sfrankho uchar_t *fat_changemap = NULL; 2529f127cb91Sfrankho int error; 2530f127cb91Sfrankho int fat_changemapsize; 2531f127cb91Sfrankho int flags = 0; 2532f127cb91Sfrankho int nfat; 2533f127cb91Sfrankho int altfat_mustmatch = 0; 2534f127cb91Sfrankho int fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize; 25357c478bd9Sstevel@tonic-gate 2536f127cb91Sfrankho if (fsp->pcfs_fatp) { 2537f127cb91Sfrankho /* 2538f127cb91Sfrankho * There is a FAT in core. 2539f127cb91Sfrankho * If there are open file pcnodes or we have modified it or 2540f127cb91Sfrankho * it hasn't timed out yet use the in core FAT. 2541f127cb91Sfrankho * Otherwise invalidate it and get a new one 2542f127cb91Sfrankho */ 2543f127cb91Sfrankho #ifdef notdef 2544f127cb91Sfrankho if (fsp->pcfs_frefs || 2545f127cb91Sfrankho (fsp->pcfs_flags & PCFS_FATMOD) || 2546f127cb91Sfrankho (gethrestime_sec() < fsp->pcfs_fattime)) { 2547f127cb91Sfrankho return (0); 2548f127cb91Sfrankho } else { 2549f127cb91Sfrankho mutex_enter(&pcfslock); 2550f127cb91Sfrankho pc_invalfat(fsp); 2551f127cb91Sfrankho mutex_exit(&pcfslock); 2552f127cb91Sfrankho } 2553f127cb91Sfrankho #endif /* notdef */ 25547c478bd9Sstevel@tonic-gate return (0); 25557c478bd9Sstevel@tonic-gate } 25567c478bd9Sstevel@tonic-gate 2557f127cb91Sfrankho /* 2558f127cb91Sfrankho * Get FAT and check it for validity 2559f127cb91Sfrankho */ 2560f127cb91Sfrankho fatp = kmem_alloc(fatsize, KM_SLEEP); 2561f127cb91Sfrankho error = pc_readfat(fsp, fatp); 2562f127cb91Sfrankho if (error) { 2563f127cb91Sfrankho flags = B_ERROR; 2564f127cb91Sfrankho goto out; 25657c478bd9Sstevel@tonic-gate } 2566f127cb91Sfrankho fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1; 2567f127cb91Sfrankho fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP); 2568f127cb91Sfrankho fsp->pcfs_fatp = fatp; 2569f127cb91Sfrankho fsp->pcfs_fat_changemapsize = fat_changemapsize; 2570f127cb91Sfrankho fsp->pcfs_fat_changemap = fat_changemap; 25717c478bd9Sstevel@tonic-gate 2572f127cb91Sfrankho /* 2573f127cb91Sfrankho * The only definite signature check is that the 2574f127cb91Sfrankho * media descriptor byte should match the first byte 2575f127cb91Sfrankho * of the FAT block. 2576f127cb91Sfrankho */ 2577f127cb91Sfrankho if (fatp[0] != fsp->pcfs_mediadesc) { 2578f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: FAT signature mismatch, " 2579f127cb91Sfrankho "media descriptor %x, FAT[0] lowbyte %x\n", 2580f127cb91Sfrankho (uint32_t)fsp->pcfs_mediadesc, (uint32_t)fatp[0]); 2581f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: Enforcing alternate FAT validation\n"); 2582f127cb91Sfrankho altfat_mustmatch = 1; 25837c478bd9Sstevel@tonic-gate } 25847c478bd9Sstevel@tonic-gate 2585f127cb91Sfrankho /* 2586f127cb91Sfrankho * Get alternate FATs and check for consistency 2587f127cb91Sfrankho * This is an inlined version of pc_readfat(). 2588f127cb91Sfrankho * Since we're only comparing FAT and alternate FAT, 2589f127cb91Sfrankho * there's no reason to let pc_readfat() copy data out 2590f127cb91Sfrankho * of the buf. Instead, compare in-situ, one cluster 2591f127cb91Sfrankho * at a time. 2592f127cb91Sfrankho */ 2593f127cb91Sfrankho for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) { 2594f127cb91Sfrankho size_t startsec; 2595f127cb91Sfrankho size_t off; 25967c478bd9Sstevel@tonic-gate 2597f127cb91Sfrankho startsec = pc_dbdaddr(fsp, 2598f127cb91Sfrankho fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec); 25997c478bd9Sstevel@tonic-gate 2600f127cb91Sfrankho for (off = 0; off < fatsize; off += fsp->pcfs_clsize) { 2601f127cb91Sfrankho daddr_t fatblk = startsec + pc_dbdaddr(fsp, 2602f127cb91Sfrankho pc_cltodb(fsp, pc_lblkno(fsp, off))); 26037c478bd9Sstevel@tonic-gate 2604f127cb91Sfrankho bp = bread(fsp->pcfs_xdev, fatblk, 2605f127cb91Sfrankho MIN(fsp->pcfs_clsize, fatsize - off)); 2606f127cb91Sfrankho if (bp->b_flags & (B_ERROR | B_STALE)) { 2607f127cb91Sfrankho cmn_err(CE_NOTE, 2608f127cb91Sfrankho "!pcfs: alternate FAT #%d (start LBA %p)" 2609f127cb91Sfrankho " read error at offset %ld on device" 2610f127cb91Sfrankho " (%x.%x):%d", 2611f127cb91Sfrankho nfat, (void *)(uintptr_t)startsec, off, 2612f127cb91Sfrankho getmajor(fsp->pcfs_xdev), 2613f127cb91Sfrankho getminor(fsp->pcfs_xdev), 2614f127cb91Sfrankho fsp->pcfs_ldrive); 2615f127cb91Sfrankho flags = B_ERROR; 2616f127cb91Sfrankho error = EIO; 2617f127cb91Sfrankho goto out; 2618f127cb91Sfrankho } 2619f127cb91Sfrankho bp->b_flags |= B_STALE | B_AGE; 2620f127cb91Sfrankho if (bcmp(bp->b_un.b_addr, fatp + off, 2621f127cb91Sfrankho MIN(fsp->pcfs_clsize, fatsize - off))) { 2622f127cb91Sfrankho cmn_err(CE_NOTE, 2623f127cb91Sfrankho "!pcfs: alternate FAT #%d (start LBA %p)" 2624f127cb91Sfrankho " corrupted at offset %ld on device" 2625f127cb91Sfrankho " (%x.%x):%d", 2626f127cb91Sfrankho nfat, (void *)(uintptr_t)startsec, off, 2627f127cb91Sfrankho getmajor(fsp->pcfs_xdev), 2628f127cb91Sfrankho getminor(fsp->pcfs_xdev), 2629f127cb91Sfrankho fsp->pcfs_ldrive); 2630f127cb91Sfrankho if (altfat_mustmatch) { 2631f127cb91Sfrankho flags = B_ERROR; 2632f127cb91Sfrankho error = EIO; 2633f127cb91Sfrankho goto out; 2634f127cb91Sfrankho } 2635f127cb91Sfrankho } 2636f127cb91Sfrankho brelse(bp); 2637f127cb91Sfrankho bp = NULL; /* prevent double release */ 26387c478bd9Sstevel@tonic-gate } 26397c478bd9Sstevel@tonic-gate } 26407c478bd9Sstevel@tonic-gate 2641f127cb91Sfrankho fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT; 2642f127cb91Sfrankho fsp->pcfs_fatjustread = 1; 26437c478bd9Sstevel@tonic-gate 2644f127cb91Sfrankho /* 2645f127cb91Sfrankho * Retrieve FAT32 fsinfo sector. 2646f127cb91Sfrankho * A failure to read this is not fatal to accessing the volume. 2647f127cb91Sfrankho * It simply means operations that count or search free blocks 2648f127cb91Sfrankho * will have to do a full FAT walk, vs. a possibly quicker lookup 2649f127cb91Sfrankho * of the summary information. 2650f127cb91Sfrankho * Hence, we log a message but return success overall after this point. 2651f127cb91Sfrankho */ 2652f127cb91Sfrankho if (IS_FAT32(fsp) && (fsp->pcfs_flags & PCFS_FSINFO_OK)) { 2653f127cb91Sfrankho struct fat_od_fsi *fsinfo_disk; 26547c478bd9Sstevel@tonic-gate 2655f127cb91Sfrankho bp = bread(fsp->pcfs_xdev, 2656f127cb91Sfrankho pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize); 2657f127cb91Sfrankho fsinfo_disk = (struct fat_od_fsi *)bp->b_un.b_addr; 2658f127cb91Sfrankho if (bp->b_flags & (B_ERROR | B_STALE) || 2659f127cb91Sfrankho !FSISIG_OK(fsinfo_disk)) { 2660f127cb91Sfrankho cmn_err(CE_NOTE, 2661f127cb91Sfrankho "!pcfs: error reading fat32 fsinfo from " 2662f127cb91Sfrankho "device (%x.%x):%d, block %lld", 2663f127cb91Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 2664f127cb91Sfrankho fsp->pcfs_ldrive, 2665f127cb91Sfrankho (long long)pc_dbdaddr(fsp, fsp->pcfs_fsistart)); 2666f127cb91Sfrankho fsp->pcfs_flags &= ~PCFS_FSINFO_OK; 2667f127cb91Sfrankho fsp->pcfs_fsinfo.fs_free_clusters = FSINFO_UNKNOWN; 2668f127cb91Sfrankho fsp->pcfs_fsinfo.fs_next_free = FSINFO_UNKNOWN; 2669f127cb91Sfrankho } else { 2670f127cb91Sfrankho bp->b_flags |= B_STALE | B_AGE; 2671f127cb91Sfrankho fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr); 2672f127cb91Sfrankho fsp->pcfs_fsinfo.fs_free_clusters = 2673f127cb91Sfrankho LE_32(fsinfo_disk->fsi_incore.fs_free_clusters); 2674f127cb91Sfrankho fsp->pcfs_fsinfo.fs_next_free = 2675f127cb91Sfrankho LE_32(fsinfo_disk->fsi_incore.fs_next_free); 26767c478bd9Sstevel@tonic-gate } 2677f127cb91Sfrankho brelse(bp); 2678f127cb91Sfrankho bp = NULL; 26797c478bd9Sstevel@tonic-gate } 2680264a6e74Sfrankho 2681f127cb91Sfrankho if (pc_validcl(fsp, (pc_cluster32_t)fsp->pcfs_fsinfo.fs_next_free)) 2682f127cb91Sfrankho fsp->pcfs_nxfrecls = fsp->pcfs_fsinfo.fs_next_free; 2683f127cb91Sfrankho else 2684f127cb91Sfrankho fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER; 2685264a6e74Sfrankho 2686f127cb91Sfrankho return (0); 2687264a6e74Sfrankho 2688f127cb91Sfrankho out: 2689f127cb91Sfrankho cmn_err(CE_NOTE, "!pcfs: illegal disk format"); 2690f127cb91Sfrankho if (bp) 2691f127cb91Sfrankho brelse(bp); 2692f127cb91Sfrankho if (fatp) 2693f127cb91Sfrankho kmem_free(fatp, fatsize); 2694f127cb91Sfrankho if (fat_changemap) 2695f127cb91Sfrankho kmem_free(fat_changemap, fat_changemapsize); 2696264a6e74Sfrankho 2697f127cb91Sfrankho if (flags) { 2698f127cb91Sfrankho pc_mark_irrecov(fsp); 2699f127cb91Sfrankho } 2700f127cb91Sfrankho return (error); 2701264a6e74Sfrankho } 2702