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