10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51488Srsb * Common Development and Distribution License (the "License"). 61488Srsb * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 226644Sgd78059 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate #include <sys/param.h> 290Sstevel@tonic-gate #include <sys/systm.h> 300Sstevel@tonic-gate #include <sys/kmem.h> 310Sstevel@tonic-gate #include <sys/user.h> 320Sstevel@tonic-gate #include <sys/proc.h> 330Sstevel@tonic-gate #include <sys/cred.h> 340Sstevel@tonic-gate #include <sys/disp.h> 350Sstevel@tonic-gate #include <sys/buf.h> 360Sstevel@tonic-gate #include <sys/vfs.h> 373898Srsb #include <sys/vfs_opreg.h> 380Sstevel@tonic-gate #include <sys/vnode.h> 390Sstevel@tonic-gate #include <sys/fdio.h> 400Sstevel@tonic-gate #include <sys/file.h> 410Sstevel@tonic-gate #include <sys/uio.h> 420Sstevel@tonic-gate #include <sys/conf.h> 430Sstevel@tonic-gate #include <sys/statvfs.h> 440Sstevel@tonic-gate #include <sys/mount.h> 450Sstevel@tonic-gate #include <sys/pathname.h> 460Sstevel@tonic-gate #include <sys/cmn_err.h> 470Sstevel@tonic-gate #include <sys/debug.h> 480Sstevel@tonic-gate #include <sys/sysmacros.h> 490Sstevel@tonic-gate #include <sys/conf.h> 500Sstevel@tonic-gate #include <sys/mkdev.h> 510Sstevel@tonic-gate #include <sys/swap.h> 520Sstevel@tonic-gate #include <sys/sunddi.h> 530Sstevel@tonic-gate #include <sys/sunldi.h> 540Sstevel@tonic-gate #include <sys/dktp/fdisk.h> 550Sstevel@tonic-gate #include <sys/fs/pc_label.h> 560Sstevel@tonic-gate #include <sys/fs/pc_fs.h> 570Sstevel@tonic-gate #include <sys/fs/pc_dir.h> 580Sstevel@tonic-gate #include <sys/fs/pc_node.h> 590Sstevel@tonic-gate #include <fs/fs_subr.h> 600Sstevel@tonic-gate #include <sys/modctl.h> 610Sstevel@tonic-gate #include <sys/dkio.h> 620Sstevel@tonic-gate #include <sys/open.h> 630Sstevel@tonic-gate #include <sys/mntent.h> 640Sstevel@tonic-gate #include <sys/policy.h> 652720Sfrankho #include <sys/atomic.h> 665121Sfrankho #include <sys/sdt.h> 670Sstevel@tonic-gate 680Sstevel@tonic-gate /* 690Sstevel@tonic-gate * The majority of PC media use a 512 sector size, but 700Sstevel@tonic-gate * occasionally you will run across a 1k sector size. 710Sstevel@tonic-gate * For media with a 1k sector size, fd_strategy() requires 720Sstevel@tonic-gate * the I/O size to be a 1k multiple; so when the sector size 730Sstevel@tonic-gate * is not yet known, always read 1k. 740Sstevel@tonic-gate */ 750Sstevel@tonic-gate #define PC_SAFESECSIZE (PC_SECSIZE * 2) 760Sstevel@tonic-gate 77201Sjmcp static int pcfs_pseudo_floppy(dev_t); 780Sstevel@tonic-gate 790Sstevel@tonic-gate static int pcfsinit(int, char *); 800Sstevel@tonic-gate static int pcfs_mount(struct vfs *, struct vnode *, struct mounta *, 810Sstevel@tonic-gate struct cred *); 820Sstevel@tonic-gate static int pcfs_unmount(struct vfs *, int, struct cred *); 830Sstevel@tonic-gate static int pcfs_root(struct vfs *, struct vnode **); 840Sstevel@tonic-gate static int pcfs_statvfs(struct vfs *, struct statvfs64 *); 850Sstevel@tonic-gate static int pc_syncfsnodes(struct pcfs *); 860Sstevel@tonic-gate static int pcfs_sync(struct vfs *, short, struct cred *); 870Sstevel@tonic-gate static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp); 882720Sfrankho static void pcfs_freevfs(vfs_t *vfsp); 890Sstevel@tonic-gate 905121Sfrankho static int pc_readfat(struct pcfs *fsp, uchar_t *fatp); 910Sstevel@tonic-gate static int pc_writefat(struct pcfs *fsp, daddr_t start); 920Sstevel@tonic-gate 935121Sfrankho static int pc_getfattype(struct pcfs *fsp); 94*6645Sgd78059 static void pcfs_parse_mntopts(struct pcfs *fsp); 955121Sfrankho 965121Sfrankho 970Sstevel@tonic-gate /* 980Sstevel@tonic-gate * pcfs mount options table 990Sstevel@tonic-gate */ 1000Sstevel@tonic-gate 1012720Sfrankho static char *nohidden_cancel[] = { MNTOPT_PCFS_HIDDEN, NULL }; 1022720Sfrankho static char *hidden_cancel[] = { MNTOPT_PCFS_NOHIDDEN, NULL }; 1032720Sfrankho static char *nofoldcase_cancel[] = { MNTOPT_PCFS_FOLDCASE, NULL }; 1042720Sfrankho static char *foldcase_cancel[] = { MNTOPT_PCFS_NOFOLDCASE, NULL }; 1052720Sfrankho static char *clamptime_cancel[] = { MNTOPT_PCFS_NOCLAMPTIME, NULL }; 1062720Sfrankho static char *noclamptime_cancel[] = { MNTOPT_PCFS_CLAMPTIME, NULL }; 1075121Sfrankho static char *atime_cancel[] = { MNTOPT_NOATIME, NULL }; 1085121Sfrankho static char *noatime_cancel[] = { MNTOPT_ATIME, NULL }; 1090Sstevel@tonic-gate 1100Sstevel@tonic-gate static mntopt_t mntopts[] = { 1110Sstevel@tonic-gate /* 1122720Sfrankho * option name cancel option default arg flags opt data 1130Sstevel@tonic-gate */ 1142720Sfrankho { MNTOPT_PCFS_NOHIDDEN, nohidden_cancel, NULL, 0, NULL }, 1152720Sfrankho { MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, MO_DEFAULT, NULL }, 1162720Sfrankho { MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, NULL }, 1172720Sfrankho { MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0, NULL }, 1182720Sfrankho { MNTOPT_PCFS_CLAMPTIME, clamptime_cancel, NULL, MO_DEFAULT, NULL }, 1195121Sfrankho { MNTOPT_PCFS_NOCLAMPTIME, noclamptime_cancel, NULL, NULL, NULL }, 1205121Sfrankho { MNTOPT_NOATIME, noatime_cancel, NULL, NULL, NULL }, 1215121Sfrankho { MNTOPT_ATIME, atime_cancel, NULL, NULL, NULL }, 1225121Sfrankho { MNTOPT_PCFS_TIMEZONE, NULL, "+0", MO_DEFAULT | MO_HASVALUE, NULL }, 1235121Sfrankho { MNTOPT_PCFS_SECSIZE, NULL, NULL, MO_HASVALUE, NULL } 1240Sstevel@tonic-gate }; 1250Sstevel@tonic-gate 1260Sstevel@tonic-gate static mntopts_t pcfs_mntopts = { 1270Sstevel@tonic-gate sizeof (mntopts) / sizeof (mntopt_t), 1280Sstevel@tonic-gate mntopts 1290Sstevel@tonic-gate }; 1300Sstevel@tonic-gate 1310Sstevel@tonic-gate int pcfsdebuglevel = 0; 1320Sstevel@tonic-gate 1330Sstevel@tonic-gate /* 1340Sstevel@tonic-gate * pcfslock: protects the list of mounted pc filesystems "pc_mounttab. 1350Sstevel@tonic-gate * pcfs_lock: (inside per filesystem structure "pcfs") 1360Sstevel@tonic-gate * per filesystem lock. Most of the vfsops and vnodeops are 1370Sstevel@tonic-gate * protected by this lock. 1380Sstevel@tonic-gate * pcnodes_lock: protects the pcnode hash table "pcdhead", "pcfhead". 1390Sstevel@tonic-gate * 1400Sstevel@tonic-gate * Lock hierarchy: pcfslock > pcfs_lock > pcnodes_lock 1412720Sfrankho * 1422720Sfrankho * pcfs_mountcount: used to prevent module unloads while there is still 1432720Sfrankho * pcfs state from a former mount hanging around. With 1442720Sfrankho * forced umount support, the filesystem module must not 1452720Sfrankho * be allowed to go away before the last VFS_FREEVFS() 1462720Sfrankho * call has been made. 1472720Sfrankho * Since this is just an atomic counter, there's no need 1482720Sfrankho * for locking. 1490Sstevel@tonic-gate */ 1500Sstevel@tonic-gate kmutex_t pcfslock; 1512720Sfrankho krwlock_t pcnodes_lock; 1522720Sfrankho uint32_t pcfs_mountcount; 1530Sstevel@tonic-gate 1540Sstevel@tonic-gate static int pcfstype; 1550Sstevel@tonic-gate 1560Sstevel@tonic-gate static vfsdef_t vfw = { 1570Sstevel@tonic-gate VFSDEF_VERSION, 1580Sstevel@tonic-gate "pcfs", 1590Sstevel@tonic-gate pcfsinit, 1601488Srsb VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS, 1610Sstevel@tonic-gate &pcfs_mntopts 1620Sstevel@tonic-gate }; 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate extern struct mod_ops mod_fsops; 1650Sstevel@tonic-gate 1660Sstevel@tonic-gate static struct modlfs modlfs = { 1670Sstevel@tonic-gate &mod_fsops, 1686644Sgd78059 "PC filesystem", 1690Sstevel@tonic-gate &vfw 1700Sstevel@tonic-gate }; 1710Sstevel@tonic-gate 1720Sstevel@tonic-gate static struct modlinkage modlinkage = { 1730Sstevel@tonic-gate MODREV_1, 1740Sstevel@tonic-gate &modlfs, 1750Sstevel@tonic-gate NULL 1760Sstevel@tonic-gate }; 1770Sstevel@tonic-gate 1780Sstevel@tonic-gate int 1790Sstevel@tonic-gate _init(void) 1800Sstevel@tonic-gate { 1810Sstevel@tonic-gate int error; 1820Sstevel@tonic-gate 1830Sstevel@tonic-gate #if !defined(lint) 1840Sstevel@tonic-gate /* make sure the on-disk structures are sane */ 1850Sstevel@tonic-gate ASSERT(sizeof (struct pcdir) == 32); 1860Sstevel@tonic-gate ASSERT(sizeof (struct pcdir_lfn) == 32); 1870Sstevel@tonic-gate #endif 1880Sstevel@tonic-gate mutex_init(&pcfslock, NULL, MUTEX_DEFAULT, NULL); 1890Sstevel@tonic-gate rw_init(&pcnodes_lock, NULL, RW_DEFAULT, NULL); 1900Sstevel@tonic-gate error = mod_install(&modlinkage); 1910Sstevel@tonic-gate if (error) { 1920Sstevel@tonic-gate mutex_destroy(&pcfslock); 1930Sstevel@tonic-gate rw_destroy(&pcnodes_lock); 1940Sstevel@tonic-gate } 1950Sstevel@tonic-gate return (error); 1960Sstevel@tonic-gate } 1970Sstevel@tonic-gate 1980Sstevel@tonic-gate int 1990Sstevel@tonic-gate _fini(void) 2000Sstevel@tonic-gate { 2010Sstevel@tonic-gate int error; 2020Sstevel@tonic-gate 2032720Sfrankho /* 2042720Sfrankho * If a forcedly unmounted instance is still hanging around, 2052720Sfrankho * we cannot allow the module to be unloaded because that would 2062720Sfrankho * cause panics once the VFS framework decides it's time to call 2072720Sfrankho * into VFS_FREEVFS(). 2082720Sfrankho */ 2092720Sfrankho if (pcfs_mountcount) 2102720Sfrankho return (EBUSY); 2112720Sfrankho 2120Sstevel@tonic-gate error = mod_remove(&modlinkage); 2130Sstevel@tonic-gate if (error) 2140Sstevel@tonic-gate return (error); 2150Sstevel@tonic-gate mutex_destroy(&pcfslock); 2160Sstevel@tonic-gate rw_destroy(&pcnodes_lock); 2170Sstevel@tonic-gate /* 2180Sstevel@tonic-gate * Tear down the operations vectors 2190Sstevel@tonic-gate */ 2200Sstevel@tonic-gate (void) vfs_freevfsops_by_type(pcfstype); 2210Sstevel@tonic-gate vn_freevnodeops(pcfs_fvnodeops); 2220Sstevel@tonic-gate vn_freevnodeops(pcfs_dvnodeops); 2230Sstevel@tonic-gate return (0); 2240Sstevel@tonic-gate } 2250Sstevel@tonic-gate 2260Sstevel@tonic-gate int 2270Sstevel@tonic-gate _info(struct modinfo *modinfop) 2280Sstevel@tonic-gate { 2290Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 2300Sstevel@tonic-gate } 2310Sstevel@tonic-gate 2320Sstevel@tonic-gate /* ARGSUSED1 */ 2330Sstevel@tonic-gate static int 2340Sstevel@tonic-gate pcfsinit(int fstype, char *name) 2350Sstevel@tonic-gate { 2360Sstevel@tonic-gate static const fs_operation_def_t pcfs_vfsops_template[] = { 2373898Srsb VFSNAME_MOUNT, { .vfs_mount = pcfs_mount }, 2383898Srsb VFSNAME_UNMOUNT, { .vfs_unmount = pcfs_unmount }, 2393898Srsb VFSNAME_ROOT, { .vfs_root = pcfs_root }, 2403898Srsb VFSNAME_STATVFS, { .vfs_statvfs = pcfs_statvfs }, 2413898Srsb VFSNAME_SYNC, { .vfs_sync = pcfs_sync }, 2423898Srsb VFSNAME_VGET, { .vfs_vget = pcfs_vget }, 2433898Srsb VFSNAME_FREEVFS, { .vfs_freevfs = pcfs_freevfs }, 2443898Srsb NULL, NULL 2450Sstevel@tonic-gate }; 2460Sstevel@tonic-gate int error; 2470Sstevel@tonic-gate 2480Sstevel@tonic-gate error = vfs_setfsops(fstype, pcfs_vfsops_template, NULL); 2490Sstevel@tonic-gate if (error != 0) { 2500Sstevel@tonic-gate cmn_err(CE_WARN, "pcfsinit: bad vfs ops template"); 2510Sstevel@tonic-gate return (error); 2520Sstevel@tonic-gate } 2530Sstevel@tonic-gate 2540Sstevel@tonic-gate error = vn_make_ops("pcfs", pcfs_fvnodeops_template, &pcfs_fvnodeops); 2550Sstevel@tonic-gate if (error != 0) { 2560Sstevel@tonic-gate (void) vfs_freevfsops_by_type(fstype); 2570Sstevel@tonic-gate cmn_err(CE_WARN, "pcfsinit: bad file vnode ops template"); 2580Sstevel@tonic-gate return (error); 2590Sstevel@tonic-gate } 2600Sstevel@tonic-gate 2610Sstevel@tonic-gate error = vn_make_ops("pcfsd", pcfs_dvnodeops_template, &pcfs_dvnodeops); 2620Sstevel@tonic-gate if (error != 0) { 2630Sstevel@tonic-gate (void) vfs_freevfsops_by_type(fstype); 2640Sstevel@tonic-gate vn_freevnodeops(pcfs_fvnodeops); 2650Sstevel@tonic-gate cmn_err(CE_WARN, "pcfsinit: bad dir vnode ops template"); 2660Sstevel@tonic-gate return (error); 2670Sstevel@tonic-gate } 2680Sstevel@tonic-gate 2690Sstevel@tonic-gate pcfstype = fstype; 2700Sstevel@tonic-gate (void) pc_init(); 2712720Sfrankho pcfs_mountcount = 0; 2720Sstevel@tonic-gate return (0); 2730Sstevel@tonic-gate } 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate static struct pcfs *pc_mounttab = NULL; 2760Sstevel@tonic-gate 2770Sstevel@tonic-gate extern struct pcfs_args pc_tz; 2780Sstevel@tonic-gate 2790Sstevel@tonic-gate /* 2800Sstevel@tonic-gate * Define some special logical drives we use internal to this file. 2810Sstevel@tonic-gate */ 2820Sstevel@tonic-gate #define BOOT_PARTITION_DRIVE 99 2830Sstevel@tonic-gate #define PRIMARY_DOS_DRIVE 1 2845121Sfrankho #define UNPARTITIONED_DRIVE 0 2850Sstevel@tonic-gate 2865121Sfrankho static int 2875121Sfrankho pcfs_device_identify( 2885121Sfrankho struct vfs *vfsp, 2895121Sfrankho struct mounta *uap, 2905121Sfrankho struct cred *cr, 2915121Sfrankho int *dos_ldrive, 2925121Sfrankho dev_t *xdev) 2935121Sfrankho { 2945121Sfrankho struct pathname special; 2955121Sfrankho char *c; 2965121Sfrankho struct vnode *bvp; 2975121Sfrankho int oflag, aflag; 2985121Sfrankho int error; 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate /* 3010Sstevel@tonic-gate * Resolve path name of special file being mounted. 3020Sstevel@tonic-gate */ 3030Sstevel@tonic-gate if (error = pn_get(uap->spec, UIO_USERSPACE, &special)) { 3040Sstevel@tonic-gate return (error); 3050Sstevel@tonic-gate } 3065121Sfrankho 3075121Sfrankho *dos_ldrive = -1; 3085121Sfrankho 3090Sstevel@tonic-gate if (error = 3100Sstevel@tonic-gate lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &bvp)) { 3110Sstevel@tonic-gate /* 3125121Sfrankho * If there's no device node, the name specified most likely 3135121Sfrankho * maps to a PCFS-style "partition specifier" to select a 3145121Sfrankho * harddisk primary/logical partition. Disable floppy-specific 3155121Sfrankho * checks in such cases unless an explicit :A or :B is 3165121Sfrankho * requested. 3175121Sfrankho */ 3185121Sfrankho 3195121Sfrankho /* 3202972Sfrankho * Split the pathname string at the last ':' separator. 3212972Sfrankho * If there's no ':' in the device name, or the ':' is the 3222972Sfrankho * last character in the string, the name is invalid and 3232972Sfrankho * the error from the previous lookup will be returned. 3240Sstevel@tonic-gate */ 3252972Sfrankho c = strrchr(special.pn_path, ':'); 3262972Sfrankho if (c == NULL || strlen(c) == 0) 3272972Sfrankho goto devlookup_done; 3282972Sfrankho 3292972Sfrankho *c++ = '\0'; 3300Sstevel@tonic-gate 3312972Sfrankho /* 3322972Sfrankho * PCFS partition name suffixes can be: 3332972Sfrankho * - "boot" to indicate the X86BOOT partition 3342972Sfrankho * - a drive letter [c-z] for the "DOS logical drive" 3352972Sfrankho * - a drive number 1..24 for the "DOS logical drive" 3365121Sfrankho * - a "floppy name letter", 'a' or 'b' (just strip this) 3372972Sfrankho */ 3382972Sfrankho if (strcasecmp(c, "boot") == 0) { 3392972Sfrankho /* 3402972Sfrankho * The Solaris boot partition is requested. 3412972Sfrankho */ 3425121Sfrankho *dos_ldrive = BOOT_PARTITION_DRIVE; 3432972Sfrankho } else if (strspn(c, "0123456789") == strlen(c)) { 3442972Sfrankho /* 3452972Sfrankho * All digits - parse the partition number. 3462972Sfrankho */ 3472972Sfrankho long drvnum = 0; 3482972Sfrankho 3492972Sfrankho if ((error = ddi_strtol(c, NULL, 10, &drvnum)) == 0) { 3500Sstevel@tonic-gate /* 3512972Sfrankho * A number alright - in the allowed range ? 3520Sstevel@tonic-gate */ 3532972Sfrankho if (drvnum > 24 || drvnum == 0) 3545121Sfrankho error = ENXIO; 3550Sstevel@tonic-gate } 3562972Sfrankho if (error) 3572972Sfrankho goto devlookup_done; 3585121Sfrankho *dos_ldrive = (int)drvnum; 3592972Sfrankho } else if (strlen(c) == 1) { 3602972Sfrankho /* 3615121Sfrankho * A single trailing character was specified. 3625121Sfrankho * - [c-zC-Z] means a harddisk partition, and 3635121Sfrankho * we retrieve the partition number. 3645121Sfrankho * - [abAB] means a floppy drive, so we swallow 3655121Sfrankho * the "drive specifier" and test later 3665121Sfrankho * whether the physical device is a floppy or 3675121Sfrankho * PCMCIA pseudofloppy (sram card). 3682972Sfrankho */ 3692972Sfrankho *c = tolower(*c); 3705121Sfrankho if (*c == 'a' || *c == 'b') { 3715121Sfrankho *dos_ldrive = UNPARTITIONED_DRIVE; 3725121Sfrankho } else if (*c < 'c' || *c > 'z') { 3735121Sfrankho error = ENXIO; 3742972Sfrankho goto devlookup_done; 3755121Sfrankho } else { 3765121Sfrankho *dos_ldrive = 1 + *c - 'c'; 3772972Sfrankho } 3782972Sfrankho } else { 3792972Sfrankho /* 3802972Sfrankho * Can't parse this - pass through previous error. 3812972Sfrankho */ 3822972Sfrankho goto devlookup_done; 3830Sstevel@tonic-gate } 3840Sstevel@tonic-gate 3852972Sfrankho 3862972Sfrankho error = lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, 3872972Sfrankho NULLVPP, &bvp); 3885121Sfrankho } else { 3895121Sfrankho *dos_ldrive = UNPARTITIONED_DRIVE; 3900Sstevel@tonic-gate } 3912972Sfrankho devlookup_done: 3920Sstevel@tonic-gate pn_free(&special); 3932972Sfrankho if (error) 3942972Sfrankho return (error); 3952972Sfrankho 3965121Sfrankho ASSERT(*dos_ldrive >= UNPARTITIONED_DRIVE); 3975121Sfrankho 3985121Sfrankho *xdev = bvp->v_rdev; 3995121Sfrankho 4000Sstevel@tonic-gate /* 4010Sstevel@tonic-gate * Verify caller's permission to open the device special file. 4020Sstevel@tonic-gate */ 4030Sstevel@tonic-gate if ((vfsp->vfs_flag & VFS_RDONLY) != 0 || 4040Sstevel@tonic-gate ((uap->flags & MS_RDONLY) != 0)) { 4050Sstevel@tonic-gate oflag = FREAD; 4060Sstevel@tonic-gate aflag = VREAD; 4070Sstevel@tonic-gate } else { 4080Sstevel@tonic-gate oflag = FREAD | FWRITE; 4090Sstevel@tonic-gate aflag = VREAD | VWRITE; 4100Sstevel@tonic-gate } 4115121Sfrankho 4125121Sfrankho if (bvp->v_type != VBLK) 4135121Sfrankho error = ENOTBLK; 4145121Sfrankho else if (getmajor(*xdev) >= devcnt) 4155121Sfrankho error = ENXIO; 4165121Sfrankho 4175121Sfrankho if ((error != 0) || 4185331Samw (error = VOP_ACCESS(bvp, aflag, 0, cr, NULL)) != 0 || 4190Sstevel@tonic-gate (error = secpolicy_spec_open(cr, bvp, oflag)) != 0) { 4200Sstevel@tonic-gate VN_RELE(bvp); 4210Sstevel@tonic-gate return (error); 4220Sstevel@tonic-gate } 4230Sstevel@tonic-gate 4240Sstevel@tonic-gate VN_RELE(bvp); 4255121Sfrankho return (0); 4265121Sfrankho } 4275121Sfrankho 4285121Sfrankho static int 4295121Sfrankho pcfs_device_ismounted( 4305121Sfrankho struct vfs *vfsp, 4315121Sfrankho int dos_ldrive, 4325121Sfrankho dev_t xdev, 4335121Sfrankho int *remounting, 4345121Sfrankho dev_t *pseudodev) 4355121Sfrankho { 4365121Sfrankho struct pcfs *fsp; 4375121Sfrankho int remount = *remounting; 4385121Sfrankho 4390Sstevel@tonic-gate /* 4402972Sfrankho * Ensure that this logical drive isn't already mounted, unless 4412972Sfrankho * this is a REMOUNT request. 4422972Sfrankho * Note: The framework will perform this check if the "...:c" 4432972Sfrankho * PCFS-style "logical drive" syntax has not been used and an 4442972Sfrankho * actually existing physical device is backing this filesystem. 4455121Sfrankho * Once all block device drivers support PC-style partitioning, 4465121Sfrankho * this codeblock can be dropped. 4470Sstevel@tonic-gate */ 4485121Sfrankho *pseudodev = xdev; 4495121Sfrankho 4500Sstevel@tonic-gate if (dos_ldrive) { 4510Sstevel@tonic-gate mutex_enter(&pcfslock); 4520Sstevel@tonic-gate for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt) 4530Sstevel@tonic-gate if (fsp->pcfs_xdev == xdev && 4545121Sfrankho fsp->pcfs_ldrive == dos_ldrive) { 4550Sstevel@tonic-gate mutex_exit(&pcfslock); 4565121Sfrankho if (remount) { 4570Sstevel@tonic-gate return (0); 4580Sstevel@tonic-gate } else { 4590Sstevel@tonic-gate return (EBUSY); 4600Sstevel@tonic-gate } 4610Sstevel@tonic-gate } 4620Sstevel@tonic-gate /* 4630Sstevel@tonic-gate * Assign a unique device number for the vfs 4640Sstevel@tonic-gate * The old way (getudev() + a constantly incrementing 4650Sstevel@tonic-gate * major number) was wrong because it changes vfs_dev 4660Sstevel@tonic-gate * across mounts and reboots, which breaks nfs file handles. 4670Sstevel@tonic-gate * UFS just uses the real dev_t. We can't do that because 4680Sstevel@tonic-gate * of the way pcfs opens fdisk partitons (the :c and :d 4690Sstevel@tonic-gate * partitions are on the same dev_t). Though that _might_ 4700Sstevel@tonic-gate * actually be ok, since the file handle contains an 4710Sstevel@tonic-gate * absolute block number, it's probably better to make them 4720Sstevel@tonic-gate * different. So I think we should retain the original 4730Sstevel@tonic-gate * dev_t, but come up with a different minor number based 4740Sstevel@tonic-gate * on the logical drive that will _always_ come up the same. 4750Sstevel@tonic-gate * For now, we steal the upper 6 bits. 4760Sstevel@tonic-gate */ 4770Sstevel@tonic-gate #ifdef notdef 4780Sstevel@tonic-gate /* what should we do here? */ 4790Sstevel@tonic-gate if (((getminor(xdev) >> 12) & 0x3F) != 0) 4800Sstevel@tonic-gate printf("whoops - upper bits used!\n"); 4810Sstevel@tonic-gate #endif 4825121Sfrankho *pseudodev = makedevice(getmajor(xdev), 4835121Sfrankho ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32); 4845121Sfrankho if (vfs_devmounting(*pseudodev, vfsp)) { 4850Sstevel@tonic-gate mutex_exit(&pcfslock); 4860Sstevel@tonic-gate return (EBUSY); 4870Sstevel@tonic-gate } 4885121Sfrankho if (vfs_devismounted(*pseudodev)) { 4890Sstevel@tonic-gate mutex_exit(&pcfslock); 4905121Sfrankho if (remount) { 4910Sstevel@tonic-gate return (0); 4920Sstevel@tonic-gate } else { 4930Sstevel@tonic-gate return (EBUSY); 4940Sstevel@tonic-gate } 4950Sstevel@tonic-gate } 4960Sstevel@tonic-gate mutex_exit(&pcfslock); 4970Sstevel@tonic-gate } else { 4985121Sfrankho *pseudodev = xdev; 4995121Sfrankho if (vfs_devmounting(*pseudodev, vfsp)) { 5000Sstevel@tonic-gate return (EBUSY); 5010Sstevel@tonic-gate } 5025121Sfrankho if (vfs_devismounted(*pseudodev)) 5035121Sfrankho if (remount) { 5040Sstevel@tonic-gate return (0); 5050Sstevel@tonic-gate } else { 5060Sstevel@tonic-gate return (EBUSY); 5070Sstevel@tonic-gate } 5080Sstevel@tonic-gate } 5090Sstevel@tonic-gate 5105121Sfrankho /* 5115121Sfrankho * This is not a remount. Even if MS_REMOUNT was requested, 5125121Sfrankho * the caller needs to proceed as it would on an ordinary 5135121Sfrankho * mount. 5145121Sfrankho */ 5155121Sfrankho *remounting = 0; 5165121Sfrankho 5175121Sfrankho ASSERT(*pseudodev); 5185121Sfrankho return (0); 5195121Sfrankho } 5205121Sfrankho 5215121Sfrankho /* 5225121Sfrankho * Get the PCFS-specific mount options from the VFS framework. 5235121Sfrankho * For "timezone" and "secsize", we need to parse the number 5245121Sfrankho * ourselves and ensure its validity. 5255121Sfrankho * Note: "secsize" is deliberately undocumented at this time, 5265121Sfrankho * it's a workaround for devices (particularly: lofi image files) 5275121Sfrankho * that don't support the DKIOCGMEDIAINFO ioctl for autodetection. 5285121Sfrankho */ 5295121Sfrankho static void 530*6645Sgd78059 pcfs_parse_mntopts(struct pcfs *fsp) 5315121Sfrankho { 5325121Sfrankho char *c; 5335121Sfrankho char *endptr; 5345121Sfrankho long l; 5355121Sfrankho struct vfs *vfsp = fsp->pcfs_vfs; 5365121Sfrankho 5375121Sfrankho ASSERT(fsp->pcfs_secondswest == 0); 5385121Sfrankho ASSERT(fsp->pcfs_secsize == 0); 5395121Sfrankho 5400Sstevel@tonic-gate if (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL)) 5410Sstevel@tonic-gate fsp->pcfs_flags |= PCFS_HIDDEN; 5420Sstevel@tonic-gate if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL)) 5430Sstevel@tonic-gate fsp->pcfs_flags |= PCFS_FOLDCASE; 5442720Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL)) 5452720Sfrankho fsp->pcfs_flags |= PCFS_NOCLAMPTIME; 5465121Sfrankho if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL)) 5475121Sfrankho fsp->pcfs_flags |= PCFS_NOATIME; 5485121Sfrankho 5495121Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_TIMEZONE, &c)) { 5505121Sfrankho if (ddi_strtol(c, &endptr, 10, &l) == 0 && 5515121Sfrankho endptr == c + strlen(c)) { 5525121Sfrankho /* 5535121Sfrankho * A number alright - in the allowed range ? 5545121Sfrankho */ 5555121Sfrankho if (l <= -12*3600 || l >= 12*3600) { 5565121Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of " 5575121Sfrankho "'timezone' mount option - %ld " 5585121Sfrankho "is out of range. Assuming 0.", l); 5595121Sfrankho l = 0; 5605121Sfrankho } 5615121Sfrankho } else { 5625121Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of " 5635121Sfrankho "'timezone' mount option - argument %s " 5645121Sfrankho "is not a valid number. Assuming 0.", c); 5655121Sfrankho l = 0; 5665121Sfrankho } 5675121Sfrankho fsp->pcfs_secondswest = l; 5685121Sfrankho } 5695121Sfrankho 5705121Sfrankho /* 5715121Sfrankho * The "secsize=..." mount option is a workaround for the lack of 5725121Sfrankho * lofi(7d) support for DKIOCGMEDIAINFO. If PCFS wants to parse the 5735121Sfrankho * partition table of a disk image and it has been partitioned with 5745121Sfrankho * sector sizes other than 512 bytes, we'd fail on loopback'ed disk 5755121Sfrankho * images. 5765121Sfrankho * That should really be fixed in lofi ... this is a workaround. 5775121Sfrankho */ 5785121Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_SECSIZE, &c)) { 5795121Sfrankho if (ddi_strtol(c, &endptr, 10, &l) == 0 && 5805121Sfrankho endptr == c + strlen(c)) { 5815121Sfrankho /* 5825121Sfrankho * A number alright - a valid sector size as well ? 5835121Sfrankho */ 5845121Sfrankho if (!VALID_SECSIZE(l)) { 5855121Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of " 5865121Sfrankho "'secsize' mount option - %ld is " 5875121Sfrankho "unsupported. Autodetecting.", l); 5885121Sfrankho l = 0; 5895121Sfrankho } 5905121Sfrankho } else { 5915121Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of " 5925121Sfrankho "'secsize' mount option - argument %s " 5935121Sfrankho "is not a valid number. Autodetecting.", c); 5945121Sfrankho l = 0; 5955121Sfrankho } 5965121Sfrankho fsp->pcfs_secsize = l; 5975121Sfrankho fsp->pcfs_sdshift = ddi_ffs(l / DEV_BSIZE) - 1; 5985121Sfrankho } 5995121Sfrankho } 6005121Sfrankho 6015121Sfrankho /* 6025121Sfrankho * vfs operations 6035121Sfrankho */ 6045121Sfrankho 6055121Sfrankho /* 6065121Sfrankho * pcfs_mount - backend for VFS_MOUNT() on PCFS. 6075121Sfrankho */ 6085121Sfrankho static int 6095121Sfrankho pcfs_mount( 6105121Sfrankho struct vfs *vfsp, 6115121Sfrankho struct vnode *mvp, 6125121Sfrankho struct mounta *uap, 6135121Sfrankho struct cred *cr) 6145121Sfrankho { 6155121Sfrankho struct pcfs *fsp; 6165121Sfrankho struct vnode *devvp; 6175121Sfrankho dev_t pseudodev; 6185121Sfrankho dev_t xdev; 6195121Sfrankho int dos_ldrive = 0; 6205121Sfrankho int error; 6215121Sfrankho int remounting; 6225121Sfrankho 6235121Sfrankho if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0) 6245121Sfrankho return (error); 6255121Sfrankho 6265121Sfrankho if (mvp->v_type != VDIR) 6275121Sfrankho return (ENOTDIR); 6285121Sfrankho 6295121Sfrankho mutex_enter(&mvp->v_lock); 6305121Sfrankho if ((uap->flags & MS_REMOUNT) == 0 && 6315121Sfrankho (uap->flags & MS_OVERLAY) == 0 && 6325121Sfrankho (mvp->v_count != 1 || (mvp->v_flag & VROOT))) { 6335121Sfrankho mutex_exit(&mvp->v_lock); 6345121Sfrankho return (EBUSY); 6355121Sfrankho } 6365121Sfrankho mutex_exit(&mvp->v_lock); 6375121Sfrankho 6385121Sfrankho /* 6395121Sfrankho * PCFS doesn't do mount arguments anymore - everything's a mount 6405121Sfrankho * option these days. In order not to break existing callers, we 6415121Sfrankho * don't reject it yet, just warn that the data (if any) is ignored. 6425121Sfrankho */ 6435121Sfrankho if (uap->datalen != 0) 6445121Sfrankho cmn_err(CE_WARN, "!pcfs: deprecated use of mount(2) with " 6455121Sfrankho "mount argument structures instead of mount options. " 6465121Sfrankho "Ignoring mount(2) 'dataptr' argument."); 6475121Sfrankho 6485121Sfrankho /* 6496644Sgd78059 * This is needed early, to make sure the access / open calls 6506644Sgd78059 * are done using the correct mode. Processing this mount option 6516644Sgd78059 * only when calling pcfs_parse_mntopts() would lead us to attempt 6526644Sgd78059 * a read/write access to a possibly writeprotected device, and 6536644Sgd78059 * a readonly mount attempt might fail because of that. 6546644Sgd78059 */ 6556644Sgd78059 if (uap->flags & MS_RDONLY) { 6566644Sgd78059 vfsp->vfs_flag |= VFS_RDONLY; 6576644Sgd78059 vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0); 6586644Sgd78059 } 6596644Sgd78059 6606644Sgd78059 /* 6615121Sfrankho * For most filesystems, this is just a lookupname() on the 6625121Sfrankho * mount pathname string. PCFS historically has to do its own 6635121Sfrankho * partition table parsing because not all Solaris architectures 6645121Sfrankho * support all styles of partitioning that PC media can have, and 6655121Sfrankho * hence PCFS understands "device names" that don't map to actual 6665121Sfrankho * physical device nodes. Parsing the "PCFS syntax" for device 6675121Sfrankho * names is done in pcfs_device_identify() - see there. 6685121Sfrankho * 6695121Sfrankho * Once all block device drivers that can host FAT filesystems have 6705121Sfrankho * been enhanced to create device nodes for all PC-style partitions, 6715121Sfrankho * this code can go away. 6725121Sfrankho */ 6735121Sfrankho if (error = pcfs_device_identify(vfsp, uap, cr, &dos_ldrive, &xdev)) 6745121Sfrankho return (error); 6755121Sfrankho 6765121Sfrankho /* 6775121Sfrankho * As with looking up the actual device to mount, PCFS cannot rely 6785121Sfrankho * on just the checks done by vfs_ismounted() whether a given device 6795121Sfrankho * is mounted already. The additional check against the "PCFS syntax" 6805121Sfrankho * is done in pcfs_device_ismounted(). 6815121Sfrankho */ 6825121Sfrankho remounting = (uap->flags & MS_REMOUNT); 6835121Sfrankho 6845121Sfrankho if (error = pcfs_device_ismounted(vfsp, dos_ldrive, xdev, &remounting, 6855121Sfrankho &pseudodev)) 6865121Sfrankho return (error); 6875121Sfrankho 6885121Sfrankho if (remounting) 6895121Sfrankho return (0); 6905121Sfrankho 6915121Sfrankho /* 6925121Sfrankho * Mount the filesystem. 6935121Sfrankho * An instance structure is required before the attempt to locate 6945121Sfrankho * and parse the FAT BPB. This is because mount options may change 6955121Sfrankho * the behaviour of the filesystem type matching code. Precreate 6965121Sfrankho * it and fill it in to a degree that allows parsing the mount 6975121Sfrankho * options. 6985121Sfrankho */ 6995121Sfrankho devvp = makespecvp(xdev, VBLK); 7005121Sfrankho if (IS_SWAPVP(devvp)) { 7015121Sfrankho VN_RELE(devvp); 7025121Sfrankho return (EBUSY); 7035121Sfrankho } 7045121Sfrankho error = VOP_OPEN(&devvp, 7055331Samw (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD | FWRITE, cr, NULL); 7065121Sfrankho if (error) { 7075121Sfrankho VN_RELE(devvp); 7085121Sfrankho return (error); 7095121Sfrankho } 7105121Sfrankho 7115121Sfrankho fsp = kmem_zalloc(sizeof (*fsp), KM_SLEEP); 7125121Sfrankho fsp->pcfs_vfs = vfsp; 7135121Sfrankho fsp->pcfs_xdev = xdev; 7145121Sfrankho fsp->pcfs_devvp = devvp; 7155121Sfrankho fsp->pcfs_ldrive = dos_ldrive; 7165121Sfrankho mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL); 7170Sstevel@tonic-gate 718*6645Sgd78059 pcfs_parse_mntopts(fsp); 7195121Sfrankho 7205121Sfrankho /* 7215121Sfrankho * This is the actual "mount" - the PCFS superblock check. 7225121Sfrankho * 7235121Sfrankho * Find the requested logical drive and the FAT BPB therein. 7245121Sfrankho * Check device type and flag the instance if media is removeable. 7255121Sfrankho * 7265121Sfrankho * Initializes most members of the filesystem instance structure. 7275121Sfrankho * Returns EINVAL if no valid BPB can be found. Other errors may 7285121Sfrankho * occur after I/O failures, or when invalid / unparseable partition 7295121Sfrankho * tables are encountered. 7305121Sfrankho */ 7315121Sfrankho if (error = pc_getfattype(fsp)) 7325121Sfrankho goto errout; 7335121Sfrankho 7345121Sfrankho /* 7356644Sgd78059 * Now that the BPB has been parsed, this structural information 7366644Sgd78059 * is available and known to be valid. Initialize the VFS. 7376644Sgd78059 */ 7386644Sgd78059 vfsp->vfs_data = fsp; 7396644Sgd78059 vfsp->vfs_dev = pseudodev; 7406644Sgd78059 vfsp->vfs_fstype = pcfstype; 7416644Sgd78059 vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype); 7426644Sgd78059 vfsp->vfs_bcount = 0; 7436644Sgd78059 vfsp->vfs_bsize = fsp->pcfs_clsize; 7446644Sgd78059 7456644Sgd78059 /* 7465121Sfrankho * Validate that we can access the FAT and that it is, to the 7475121Sfrankho * degree we can verify here, self-consistent. 7485121Sfrankho */ 7495121Sfrankho if (error = pc_verify(fsp)) 7505121Sfrankho goto errout; 7515121Sfrankho 7525121Sfrankho /* 7535121Sfrankho * Record the time of the mount, to return as an "approximate" 7545121Sfrankho * timestamp for the FAT root directory. Since FAT roots don't 7555121Sfrankho * have timestamps, this is less confusing to the user than 7565121Sfrankho * claiming "zero" / Jan/01/1970. 7575121Sfrankho */ 7585121Sfrankho gethrestime(&fsp->pcfs_mounttime); 7595121Sfrankho 7605121Sfrankho /* 7615121Sfrankho * Fix up the mount options. Because "noatime" is made default on 7625121Sfrankho * removeable media only, a fixed disk will have neither "atime" 7635121Sfrankho * nor "noatime" set. We set the options explicitly depending on 7645121Sfrankho * the PCFS_NOATIME flag, to inform the user of what applies. 7655121Sfrankho * Mount option cancellation will take care that the mutually 7665121Sfrankho * exclusive 'other' is cleared. 7675121Sfrankho */ 7685121Sfrankho vfs_setmntopt(vfsp, 7695121Sfrankho fsp->pcfs_flags & PCFS_NOATIME ? MNTOPT_NOATIME : MNTOPT_ATIME, 7705121Sfrankho NULL, 0); 7715121Sfrankho 7725121Sfrankho /* 7735121Sfrankho * All clear - insert the FS instance into PCFS' list. 7745121Sfrankho */ 7750Sstevel@tonic-gate mutex_enter(&pcfslock); 7760Sstevel@tonic-gate fsp->pcfs_nxt = pc_mounttab; 7770Sstevel@tonic-gate pc_mounttab = fsp; 7780Sstevel@tonic-gate mutex_exit(&pcfslock); 7792720Sfrankho atomic_inc_32(&pcfs_mountcount); 7800Sstevel@tonic-gate return (0); 7815121Sfrankho 7825121Sfrankho errout: 7835121Sfrankho (void) VOP_CLOSE(devvp, 7845121Sfrankho vfsp->vfs_flag & VFS_RDONLY ? FREAD : FREAD | FWRITE, 7855331Samw 1, (offset_t)0, cr, NULL); 7865121Sfrankho VN_RELE(devvp); 7875121Sfrankho mutex_destroy(&fsp->pcfs_lock); 7885121Sfrankho kmem_free(fsp, sizeof (*fsp)); 7895121Sfrankho return (error); 7905121Sfrankho 7910Sstevel@tonic-gate } 7920Sstevel@tonic-gate 7930Sstevel@tonic-gate static int 7940Sstevel@tonic-gate pcfs_unmount( 7950Sstevel@tonic-gate struct vfs *vfsp, 7960Sstevel@tonic-gate int flag, 7970Sstevel@tonic-gate struct cred *cr) 7980Sstevel@tonic-gate { 7990Sstevel@tonic-gate struct pcfs *fsp, *fsp1; 8000Sstevel@tonic-gate 8010Sstevel@tonic-gate if (secpolicy_fs_unmount(cr, vfsp) != 0) 8020Sstevel@tonic-gate return (EPERM); 8030Sstevel@tonic-gate 8040Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 8052720Sfrankho 8060Sstevel@tonic-gate /* 8070Sstevel@tonic-gate * We don't have to lock fsp because the VVFSLOCK in vfs layer will 8080Sstevel@tonic-gate * prevent lookuppn from crossing the mount point. 8092720Sfrankho * If this is not a forced umount request and there's ongoing I/O, 8102720Sfrankho * don't allow the mount to proceed. 8110Sstevel@tonic-gate */ 8122720Sfrankho if (flag & MS_FORCE) 8132720Sfrankho vfsp->vfs_flag |= VFS_UNMOUNTED; 8142720Sfrankho else if (fsp->pcfs_nrefs) 8150Sstevel@tonic-gate return (EBUSY); 8162720Sfrankho 8172720Sfrankho mutex_enter(&pcfslock); 8180Sstevel@tonic-gate 8190Sstevel@tonic-gate /* 8202720Sfrankho * If this is a forced umount request or if the fs instance has 8212720Sfrankho * been marked as beyond recovery, allow the umount to proceed 8222720Sfrankho * regardless of state. pc_diskchanged() forcibly releases all 8232720Sfrankho * inactive vnodes/pcnodes. 8240Sstevel@tonic-gate */ 8252720Sfrankho if (flag & MS_FORCE || fsp->pcfs_flags & PCFS_IRRECOV) { 8260Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_WRITER); 8270Sstevel@tonic-gate pc_diskchanged(fsp); 8280Sstevel@tonic-gate rw_exit(&pcnodes_lock); 8290Sstevel@tonic-gate } 8300Sstevel@tonic-gate 8310Sstevel@tonic-gate /* now there should be no pcp node on pcfhead or pcdhead. */ 8320Sstevel@tonic-gate 8330Sstevel@tonic-gate if (fsp == pc_mounttab) { 8340Sstevel@tonic-gate pc_mounttab = fsp->pcfs_nxt; 8350Sstevel@tonic-gate } else { 8360Sstevel@tonic-gate for (fsp1 = pc_mounttab; fsp1 != NULL; fsp1 = fsp1->pcfs_nxt) 8370Sstevel@tonic-gate if (fsp1->pcfs_nxt == fsp) 8380Sstevel@tonic-gate fsp1->pcfs_nxt = fsp->pcfs_nxt; 8390Sstevel@tonic-gate } 8400Sstevel@tonic-gate 8410Sstevel@tonic-gate mutex_exit(&pcfslock); 8420Sstevel@tonic-gate 8432720Sfrankho /* 8442720Sfrankho * Since we support VFS_FREEVFS(), there's no need to 8452720Sfrankho * free the fsp right now. The framework will tell us 8462720Sfrankho * when the right time to do so has arrived by calling 8472720Sfrankho * into pcfs_freevfs. 8482720Sfrankho */ 8490Sstevel@tonic-gate return (0); 8500Sstevel@tonic-gate } 8510Sstevel@tonic-gate 8520Sstevel@tonic-gate /* 8530Sstevel@tonic-gate * find root of pcfs 8540Sstevel@tonic-gate */ 8550Sstevel@tonic-gate static int 8560Sstevel@tonic-gate pcfs_root( 8570Sstevel@tonic-gate struct vfs *vfsp, 8580Sstevel@tonic-gate struct vnode **vpp) 8590Sstevel@tonic-gate { 8600Sstevel@tonic-gate struct pcfs *fsp; 8610Sstevel@tonic-gate struct pcnode *pcp; 8620Sstevel@tonic-gate int error; 8630Sstevel@tonic-gate 8640Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 8650Sstevel@tonic-gate if (error = pc_lockfs(fsp, 0, 0)) 8660Sstevel@tonic-gate return (error); 8672720Sfrankho 8680Sstevel@tonic-gate pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0); 8690Sstevel@tonic-gate pc_unlockfs(fsp); 8700Sstevel@tonic-gate *vpp = PCTOV(pcp); 8710Sstevel@tonic-gate pcp->pc_flags |= PC_EXTERNAL; 8720Sstevel@tonic-gate return (0); 8730Sstevel@tonic-gate } 8740Sstevel@tonic-gate 8750Sstevel@tonic-gate /* 8760Sstevel@tonic-gate * Get file system statistics. 8770Sstevel@tonic-gate */ 8780Sstevel@tonic-gate static int 8790Sstevel@tonic-gate pcfs_statvfs( 8800Sstevel@tonic-gate struct vfs *vfsp, 8810Sstevel@tonic-gate struct statvfs64 *sp) 8820Sstevel@tonic-gate { 8830Sstevel@tonic-gate struct pcfs *fsp; 8840Sstevel@tonic-gate int error; 8850Sstevel@tonic-gate dev32_t d32; 8860Sstevel@tonic-gate 8870Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 8880Sstevel@tonic-gate error = pc_getfat(fsp); 8890Sstevel@tonic-gate if (error) 8900Sstevel@tonic-gate return (error); 8910Sstevel@tonic-gate bzero(sp, sizeof (*sp)); 8920Sstevel@tonic-gate sp->f_bsize = sp->f_frsize = fsp->pcfs_clsize; 8930Sstevel@tonic-gate sp->f_blocks = (fsblkcnt64_t)fsp->pcfs_ncluster; 8940Sstevel@tonic-gate sp->f_bavail = sp->f_bfree = (fsblkcnt64_t)pc_freeclusters(fsp); 8950Sstevel@tonic-gate sp->f_files = (fsfilcnt64_t)-1; 8960Sstevel@tonic-gate sp->f_ffree = (fsfilcnt64_t)-1; 8970Sstevel@tonic-gate sp->f_favail = (fsfilcnt64_t)-1; 8980Sstevel@tonic-gate #ifdef notdef 8990Sstevel@tonic-gate (void) cmpldev(&d32, fsp->pcfs_devvp->v_rdev); 9000Sstevel@tonic-gate #endif /* notdef */ 9010Sstevel@tonic-gate (void) cmpldev(&d32, vfsp->vfs_dev); 9020Sstevel@tonic-gate sp->f_fsid = d32; 9030Sstevel@tonic-gate (void) strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name); 9040Sstevel@tonic-gate sp->f_flag = vf_to_stf(vfsp->vfs_flag); 9050Sstevel@tonic-gate sp->f_namemax = PCFNAMESIZE; 9060Sstevel@tonic-gate return (0); 9070Sstevel@tonic-gate } 9080Sstevel@tonic-gate 9090Sstevel@tonic-gate static int 9100Sstevel@tonic-gate pc_syncfsnodes(struct pcfs *fsp) 9110Sstevel@tonic-gate { 9120Sstevel@tonic-gate struct pchead *hp; 9130Sstevel@tonic-gate struct pcnode *pcp; 9140Sstevel@tonic-gate int error; 9150Sstevel@tonic-gate 9160Sstevel@tonic-gate if (error = pc_lockfs(fsp, 0, 0)) 9170Sstevel@tonic-gate return (error); 9180Sstevel@tonic-gate 9190Sstevel@tonic-gate if (!(error = pc_syncfat(fsp))) { 9200Sstevel@tonic-gate hp = pcfhead; 9210Sstevel@tonic-gate while (hp < & pcfhead [ NPCHASH ]) { 9220Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_READER); 9230Sstevel@tonic-gate pcp = hp->pch_forw; 9240Sstevel@tonic-gate while (pcp != (struct pcnode *)hp) { 9250Sstevel@tonic-gate if (VFSTOPCFS(PCTOV(pcp) -> v_vfsp) == fsp) 9260Sstevel@tonic-gate if (error = pc_nodesync(pcp)) 9270Sstevel@tonic-gate break; 9280Sstevel@tonic-gate pcp = pcp -> pc_forw; 9290Sstevel@tonic-gate } 9300Sstevel@tonic-gate rw_exit(&pcnodes_lock); 9310Sstevel@tonic-gate if (error) 9320Sstevel@tonic-gate break; 9330Sstevel@tonic-gate hp++; 9340Sstevel@tonic-gate } 9350Sstevel@tonic-gate } 9360Sstevel@tonic-gate pc_unlockfs(fsp); 9370Sstevel@tonic-gate return (error); 9380Sstevel@tonic-gate } 9390Sstevel@tonic-gate 9400Sstevel@tonic-gate /* 9410Sstevel@tonic-gate * Flush any pending I/O. 9420Sstevel@tonic-gate */ 9430Sstevel@tonic-gate /*ARGSUSED*/ 9440Sstevel@tonic-gate static int 9450Sstevel@tonic-gate pcfs_sync( 9460Sstevel@tonic-gate struct vfs *vfsp, 9470Sstevel@tonic-gate short flag, 9480Sstevel@tonic-gate struct cred *cr) 9490Sstevel@tonic-gate { 9500Sstevel@tonic-gate struct pcfs *fsp; 9510Sstevel@tonic-gate int error = 0; 9520Sstevel@tonic-gate 9530Sstevel@tonic-gate /* this prevents the filesystem from being umounted. */ 9540Sstevel@tonic-gate mutex_enter(&pcfslock); 9550Sstevel@tonic-gate if (vfsp != NULL) { 9560Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 9570Sstevel@tonic-gate if (!(fsp->pcfs_flags & PCFS_IRRECOV)) { 9580Sstevel@tonic-gate error = pc_syncfsnodes(fsp); 9590Sstevel@tonic-gate } else { 9600Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_WRITER); 9610Sstevel@tonic-gate pc_diskchanged(fsp); 9620Sstevel@tonic-gate rw_exit(&pcnodes_lock); 9630Sstevel@tonic-gate error = EIO; 9640Sstevel@tonic-gate } 9650Sstevel@tonic-gate } else { 9660Sstevel@tonic-gate fsp = pc_mounttab; 9670Sstevel@tonic-gate while (fsp != NULL) { 9680Sstevel@tonic-gate if (fsp->pcfs_flags & PCFS_IRRECOV) { 9690Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_WRITER); 9700Sstevel@tonic-gate pc_diskchanged(fsp); 9710Sstevel@tonic-gate rw_exit(&pcnodes_lock); 9720Sstevel@tonic-gate error = EIO; 9730Sstevel@tonic-gate break; 9740Sstevel@tonic-gate } 9750Sstevel@tonic-gate error = pc_syncfsnodes(fsp); 9760Sstevel@tonic-gate if (error) break; 9770Sstevel@tonic-gate fsp = fsp->pcfs_nxt; 9780Sstevel@tonic-gate } 9790Sstevel@tonic-gate } 9800Sstevel@tonic-gate mutex_exit(&pcfslock); 9810Sstevel@tonic-gate return (error); 9820Sstevel@tonic-gate } 9830Sstevel@tonic-gate 9840Sstevel@tonic-gate int 9850Sstevel@tonic-gate pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing) 9860Sstevel@tonic-gate { 9872720Sfrankho int err; 9882720Sfrankho 9890Sstevel@tonic-gate if ((fsp->pcfs_flags & PCFS_IRRECOV) && !releasing) 9900Sstevel@tonic-gate return (EIO); 9910Sstevel@tonic-gate 9920Sstevel@tonic-gate if ((fsp->pcfs_flags & PCFS_LOCKED) && (fsp->pcfs_owner == curthread)) { 9930Sstevel@tonic-gate fsp->pcfs_count++; 9940Sstevel@tonic-gate } else { 9950Sstevel@tonic-gate mutex_enter(&fsp->pcfs_lock); 9960Sstevel@tonic-gate if (fsp->pcfs_flags & PCFS_LOCKED) 9970Sstevel@tonic-gate panic("pc_lockfs"); 9980Sstevel@tonic-gate /* 9990Sstevel@tonic-gate * We check the IRRECOV bit again just in case somebody 10000Sstevel@tonic-gate * snuck past the initial check but then got held up before 10010Sstevel@tonic-gate * they could grab the lock. (And in the meantime someone 10020Sstevel@tonic-gate * had grabbed the lock and set the bit) 10030Sstevel@tonic-gate */ 1004607Swyllys if (!diskchanged && !(fsp->pcfs_flags & PCFS_IRRECOV)) { 10052720Sfrankho if ((err = pc_getfat(fsp))) { 10062720Sfrankho mutex_exit(&fsp->pcfs_lock); 1007607Swyllys return (err); 10082720Sfrankho } 1009607Swyllys } 10100Sstevel@tonic-gate fsp->pcfs_flags |= PCFS_LOCKED; 10110Sstevel@tonic-gate fsp->pcfs_owner = curthread; 10120Sstevel@tonic-gate fsp->pcfs_count++; 10130Sstevel@tonic-gate } 10140Sstevel@tonic-gate return (0); 10150Sstevel@tonic-gate } 10160Sstevel@tonic-gate 10170Sstevel@tonic-gate void 10180Sstevel@tonic-gate pc_unlockfs(struct pcfs *fsp) 10190Sstevel@tonic-gate { 10200Sstevel@tonic-gate 10210Sstevel@tonic-gate if ((fsp->pcfs_flags & PCFS_LOCKED) == 0) 10220Sstevel@tonic-gate panic("pc_unlockfs"); 10230Sstevel@tonic-gate if (--fsp->pcfs_count < 0) 10240Sstevel@tonic-gate panic("pc_unlockfs: count"); 10250Sstevel@tonic-gate if (fsp->pcfs_count == 0) { 10260Sstevel@tonic-gate fsp->pcfs_flags &= ~PCFS_LOCKED; 10270Sstevel@tonic-gate fsp->pcfs_owner = 0; 10280Sstevel@tonic-gate mutex_exit(&fsp->pcfs_lock); 10290Sstevel@tonic-gate } 10300Sstevel@tonic-gate } 10310Sstevel@tonic-gate 10320Sstevel@tonic-gate int 10330Sstevel@tonic-gate pc_syncfat(struct pcfs *fsp) 10340Sstevel@tonic-gate { 10350Sstevel@tonic-gate struct buf *bp; 10360Sstevel@tonic-gate int nfat; 10375121Sfrankho int error = 0; 10385121Sfrankho struct fat_od_fsi *fsinfo_disk; 10390Sstevel@tonic-gate 10400Sstevel@tonic-gate if ((fsp->pcfs_fatp == (uchar_t *)0) || 10410Sstevel@tonic-gate !(fsp->pcfs_flags & PCFS_FATMOD)) 10420Sstevel@tonic-gate return (0); 10430Sstevel@tonic-gate /* 10440Sstevel@tonic-gate * write out all copies of FATs 10450Sstevel@tonic-gate */ 10460Sstevel@tonic-gate fsp->pcfs_flags &= ~PCFS_FATMOD; 10470Sstevel@tonic-gate fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT; 10480Sstevel@tonic-gate for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) { 10495121Sfrankho error = pc_writefat(fsp, pc_dbdaddr(fsp, 10505121Sfrankho fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec)); 10510Sstevel@tonic-gate if (error) { 10520Sstevel@tonic-gate pc_mark_irrecov(fsp); 10530Sstevel@tonic-gate return (EIO); 10540Sstevel@tonic-gate } 10550Sstevel@tonic-gate } 10560Sstevel@tonic-gate pc_clear_fatchanges(fsp); 10575121Sfrankho 10585121Sfrankho /* 10595121Sfrankho * Write out fsinfo sector. 10605121Sfrankho */ 10610Sstevel@tonic-gate if (IS_FAT32(fsp)) { 10620Sstevel@tonic-gate bp = bread(fsp->pcfs_xdev, 10635121Sfrankho pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize); 10640Sstevel@tonic-gate if (bp->b_flags & (B_ERROR | B_STALE)) { 10655121Sfrankho error = geterror(bp); 10660Sstevel@tonic-gate } 10675121Sfrankho fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr); 10685121Sfrankho if (!error && FSISIG_OK(fsinfo_disk)) { 10695121Sfrankho fsinfo_disk->fsi_incore.fs_free_clusters = 10705121Sfrankho LE_32(fsp->pcfs_fsinfo.fs_free_clusters); 10715121Sfrankho fsinfo_disk->fsi_incore.fs_next_free = 10725121Sfrankho LE_32(FSINFO_UNKNOWN); 10735121Sfrankho bwrite2(bp); 10745121Sfrankho error = geterror(bp); 10755121Sfrankho } 10760Sstevel@tonic-gate brelse(bp); 10770Sstevel@tonic-gate if (error) { 10780Sstevel@tonic-gate pc_mark_irrecov(fsp); 10790Sstevel@tonic-gate return (EIO); 10800Sstevel@tonic-gate } 10810Sstevel@tonic-gate } 10820Sstevel@tonic-gate return (0); 10830Sstevel@tonic-gate } 10840Sstevel@tonic-gate 10850Sstevel@tonic-gate void 10860Sstevel@tonic-gate pc_invalfat(struct pcfs *fsp) 10870Sstevel@tonic-gate { 10880Sstevel@tonic-gate struct pcfs *xfsp; 10890Sstevel@tonic-gate int mount_cnt = 0; 10900Sstevel@tonic-gate 10910Sstevel@tonic-gate if (fsp->pcfs_fatp == (uchar_t *)0) 10920Sstevel@tonic-gate panic("pc_invalfat"); 10930Sstevel@tonic-gate /* 10940Sstevel@tonic-gate * Release FAT 10950Sstevel@tonic-gate */ 10965121Sfrankho kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsec * fsp->pcfs_secsize); 10970Sstevel@tonic-gate fsp->pcfs_fatp = NULL; 10980Sstevel@tonic-gate kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize); 10990Sstevel@tonic-gate fsp->pcfs_fat_changemap = NULL; 11000Sstevel@tonic-gate /* 11010Sstevel@tonic-gate * Invalidate all the blocks associated with the device. 11020Sstevel@tonic-gate * Not needed if stateless. 11030Sstevel@tonic-gate */ 11040Sstevel@tonic-gate for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt) 11050Sstevel@tonic-gate if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev) 11060Sstevel@tonic-gate mount_cnt++; 11070Sstevel@tonic-gate 11080Sstevel@tonic-gate if (!mount_cnt) 11090Sstevel@tonic-gate binval(fsp->pcfs_xdev); 11100Sstevel@tonic-gate /* 11110Sstevel@tonic-gate * close mounted device 11120Sstevel@tonic-gate */ 11130Sstevel@tonic-gate (void) VOP_CLOSE(fsp->pcfs_devvp, 11140Sstevel@tonic-gate (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, 11155331Samw 1, (offset_t)0, CRED(), NULL); 11160Sstevel@tonic-gate } 11170Sstevel@tonic-gate 11180Sstevel@tonic-gate void 11190Sstevel@tonic-gate pc_badfs(struct pcfs *fsp) 11200Sstevel@tonic-gate { 11215121Sfrankho cmn_err(CE_WARN, "corrupted PC file system on dev (%x.%x):%d\n", 11220Sstevel@tonic-gate getmajor(fsp->pcfs_devvp->v_rdev), 11235121Sfrankho getminor(fsp->pcfs_devvp->v_rdev), fsp->pcfs_ldrive); 11240Sstevel@tonic-gate } 11250Sstevel@tonic-gate 11260Sstevel@tonic-gate /* 11270Sstevel@tonic-gate * The problem with supporting NFS on the PCFS filesystem is that there 11280Sstevel@tonic-gate * is no good place to keep the generation number. The only possible 11290Sstevel@tonic-gate * place is inside a directory entry. There are a few words that we 11300Sstevel@tonic-gate * don't use - they store NT & OS/2 attributes, and the creation/last access 11310Sstevel@tonic-gate * time of the file - but it seems wrong to use them. In addition, directory 11320Sstevel@tonic-gate * entries come and go. If a directory is removed completely, its directory 11330Sstevel@tonic-gate * blocks are freed and the generation numbers are lost. Whereas in ufs, 11340Sstevel@tonic-gate * inode blocks are dedicated for inodes, so the generation numbers are 11350Sstevel@tonic-gate * permanently kept on the disk. 11360Sstevel@tonic-gate */ 11370Sstevel@tonic-gate static int 11380Sstevel@tonic-gate pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp) 11390Sstevel@tonic-gate { 11400Sstevel@tonic-gate struct pcnode *pcp; 11410Sstevel@tonic-gate struct pc_fid *pcfid; 11420Sstevel@tonic-gate struct pcfs *fsp; 11430Sstevel@tonic-gate struct pcdir *ep; 11440Sstevel@tonic-gate daddr_t eblkno; 11450Sstevel@tonic-gate int eoffset; 11460Sstevel@tonic-gate struct buf *bp; 11470Sstevel@tonic-gate int error; 11480Sstevel@tonic-gate pc_cluster32_t cn; 11490Sstevel@tonic-gate 11500Sstevel@tonic-gate pcfid = (struct pc_fid *)fidp; 11510Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 11520Sstevel@tonic-gate 11530Sstevel@tonic-gate error = pc_lockfs(fsp, 0, 0); 11540Sstevel@tonic-gate if (error) { 11550Sstevel@tonic-gate *vpp = NULL; 11560Sstevel@tonic-gate return (error); 11570Sstevel@tonic-gate } 11580Sstevel@tonic-gate 11590Sstevel@tonic-gate if (pcfid->pcfid_block == 0) { 11600Sstevel@tonic-gate pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0); 11610Sstevel@tonic-gate pcp->pc_flags |= PC_EXTERNAL; 11620Sstevel@tonic-gate *vpp = PCTOV(pcp); 11630Sstevel@tonic-gate pc_unlockfs(fsp); 11640Sstevel@tonic-gate return (0); 11650Sstevel@tonic-gate } 11660Sstevel@tonic-gate eblkno = pcfid->pcfid_block; 11670Sstevel@tonic-gate eoffset = pcfid->pcfid_offset; 11685121Sfrankho 11690Sstevel@tonic-gate if ((pc_dbtocl(fsp, 11700Sstevel@tonic-gate eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) || 11710Sstevel@tonic-gate (eoffset > fsp->pcfs_clsize)) { 11720Sstevel@tonic-gate pc_unlockfs(fsp); 11730Sstevel@tonic-gate *vpp = NULL; 11740Sstevel@tonic-gate return (EINVAL); 11750Sstevel@tonic-gate } 11760Sstevel@tonic-gate 11775121Sfrankho if (eblkno >= fsp->pcfs_datastart || (eblkno - fsp->pcfs_rdirstart) 11780Sstevel@tonic-gate < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) { 11795121Sfrankho bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno), 11805121Sfrankho fsp->pcfs_clsize); 11810Sstevel@tonic-gate } else { 11825121Sfrankho /* 11835121Sfrankho * This is an access "backwards" into the FAT12/FAT16 11845121Sfrankho * root directory. A better code structure would 11855121Sfrankho * significantly improve maintainability here ... 11865121Sfrankho */ 11875121Sfrankho bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno), 11880Sstevel@tonic-gate (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize); 11890Sstevel@tonic-gate } 11900Sstevel@tonic-gate if (bp->b_flags & (B_ERROR | B_STALE)) { 11910Sstevel@tonic-gate error = geterror(bp); 11920Sstevel@tonic-gate brelse(bp); 11930Sstevel@tonic-gate if (error) 11940Sstevel@tonic-gate pc_mark_irrecov(fsp); 11950Sstevel@tonic-gate *vpp = NULL; 11960Sstevel@tonic-gate pc_unlockfs(fsp); 11970Sstevel@tonic-gate return (error); 11980Sstevel@tonic-gate } 11990Sstevel@tonic-gate ep = (struct pcdir *)(bp->b_un.b_addr + eoffset); 12000Sstevel@tonic-gate /* 12010Sstevel@tonic-gate * Ok, if this is a valid file handle that we gave out, 12020Sstevel@tonic-gate * then simply ensuring that the creation time matches, 12030Sstevel@tonic-gate * the entry has not been deleted, and it has a valid first 12040Sstevel@tonic-gate * character should be enough. 12050Sstevel@tonic-gate * 12060Sstevel@tonic-gate * Unfortunately, verifying that the <blkno, offset> _still_ 12070Sstevel@tonic-gate * refers to a directory entry is not easy, since we'd have 12080Sstevel@tonic-gate * to search _all_ directories starting from root to find it. 12090Sstevel@tonic-gate * That's a high price to pay just in case somebody is forging 12100Sstevel@tonic-gate * file handles. So instead we verify that as much of the 12110Sstevel@tonic-gate * entry is valid as we can: 12120Sstevel@tonic-gate * 12130Sstevel@tonic-gate * 1. The starting cluster is 0 (unallocated) or valid 12140Sstevel@tonic-gate * 2. It is not an LFN entry 12150Sstevel@tonic-gate * 3. It is not hidden (unless mounted as such) 12160Sstevel@tonic-gate * 4. It is not the label 12170Sstevel@tonic-gate */ 12180Sstevel@tonic-gate cn = pc_getstartcluster(fsp, ep); 12190Sstevel@tonic-gate /* 12200Sstevel@tonic-gate * if the starting cluster is valid, but not valid according 12210Sstevel@tonic-gate * to pc_validcl(), force it to be to simplify the following if. 12220Sstevel@tonic-gate */ 12230Sstevel@tonic-gate if (cn == 0) 12240Sstevel@tonic-gate cn = PCF_FIRSTCLUSTER; 12250Sstevel@tonic-gate if (IS_FAT32(fsp)) { 12260Sstevel@tonic-gate if (cn >= PCF_LASTCLUSTER32) 12270Sstevel@tonic-gate cn = PCF_FIRSTCLUSTER; 12280Sstevel@tonic-gate } else { 12290Sstevel@tonic-gate if (cn >= PCF_LASTCLUSTER) 12300Sstevel@tonic-gate cn = PCF_FIRSTCLUSTER; 12310Sstevel@tonic-gate } 12320Sstevel@tonic-gate if ((!pc_validcl(fsp, cn)) || 12330Sstevel@tonic-gate (PCDL_IS_LFN(ep)) || 12340Sstevel@tonic-gate (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) || 12350Sstevel@tonic-gate ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) { 12360Sstevel@tonic-gate bp->b_flags |= B_STALE | B_AGE; 12370Sstevel@tonic-gate brelse(bp); 12380Sstevel@tonic-gate pc_unlockfs(fsp); 12390Sstevel@tonic-gate return (EINVAL); 12400Sstevel@tonic-gate } 12410Sstevel@tonic-gate if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) && 12420Sstevel@tonic-gate (ep->pcd_filename[0] != PCD_ERASED) && 12430Sstevel@tonic-gate (pc_validchar(ep->pcd_filename[0]) || 12445121Sfrankho (ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) { 12450Sstevel@tonic-gate pcp = pc_getnode(fsp, eblkno, eoffset, ep); 12460Sstevel@tonic-gate pcp->pc_flags |= PC_EXTERNAL; 12470Sstevel@tonic-gate *vpp = PCTOV(pcp); 12480Sstevel@tonic-gate } else { 12490Sstevel@tonic-gate *vpp = NULL; 12500Sstevel@tonic-gate } 12510Sstevel@tonic-gate bp->b_flags |= B_STALE | B_AGE; 12520Sstevel@tonic-gate brelse(bp); 12530Sstevel@tonic-gate pc_unlockfs(fsp); 12540Sstevel@tonic-gate return (0); 12550Sstevel@tonic-gate } 12560Sstevel@tonic-gate 12570Sstevel@tonic-gate /* 12580Sstevel@tonic-gate * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about 12590Sstevel@tonic-gate * a meg), so we can't bread() it all in at once. This routine reads a 12600Sstevel@tonic-gate * fat a chunk at a time. 12610Sstevel@tonic-gate */ 12620Sstevel@tonic-gate static int 12635121Sfrankho pc_readfat(struct pcfs *fsp, uchar_t *fatp) 12640Sstevel@tonic-gate { 12650Sstevel@tonic-gate struct buf *bp; 12660Sstevel@tonic-gate size_t off; 12670Sstevel@tonic-gate size_t readsize; 12685121Sfrankho daddr_t diskblk; 12695121Sfrankho size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize; 12705121Sfrankho daddr_t start = fsp->pcfs_fatstart; 12710Sstevel@tonic-gate 12720Sstevel@tonic-gate readsize = fsp->pcfs_clsize; 12730Sstevel@tonic-gate for (off = 0; off < fatsize; off += readsize, fatp += readsize) { 12740Sstevel@tonic-gate if (readsize > (fatsize - off)) 12750Sstevel@tonic-gate readsize = fatsize - off; 12765121Sfrankho diskblk = pc_dbdaddr(fsp, start + 12775121Sfrankho pc_cltodb(fsp, pc_lblkno(fsp, off))); 12785121Sfrankho bp = bread(fsp->pcfs_xdev, diskblk, readsize); 12790Sstevel@tonic-gate if (bp->b_flags & (B_ERROR | B_STALE)) { 12800Sstevel@tonic-gate brelse(bp); 12810Sstevel@tonic-gate return (EIO); 12820Sstevel@tonic-gate } 12830Sstevel@tonic-gate bp->b_flags |= B_STALE | B_AGE; 12840Sstevel@tonic-gate bcopy(bp->b_un.b_addr, fatp, readsize); 12850Sstevel@tonic-gate brelse(bp); 12860Sstevel@tonic-gate } 12870Sstevel@tonic-gate return (0); 12880Sstevel@tonic-gate } 12890Sstevel@tonic-gate 12900Sstevel@tonic-gate /* 12910Sstevel@tonic-gate * We write the FAT out a _lot_, in order to make sure that it 12920Sstevel@tonic-gate * is up-to-date. But on a FAT32 system (large drive, small clusters) 12930Sstevel@tonic-gate * the FAT might be a couple of megabytes, and writing it all out just 12940Sstevel@tonic-gate * because we created or deleted a small file is painful (especially 12950Sstevel@tonic-gate * since we do it for each alternate FAT too). So instead, for FAT16 and 12960Sstevel@tonic-gate * FAT32 we only write out the bit that has changed. We don't clear 12970Sstevel@tonic-gate * the 'updated' fields here because the caller might be writing out 12980Sstevel@tonic-gate * several FATs, so the caller must use pc_clear_fatchanges() after 12990Sstevel@tonic-gate * all FATs have been updated. 13005121Sfrankho * This function doesn't take "start" from fsp->pcfs_dosstart because 13015121Sfrankho * callers can use it to write either the primary or any of the alternate 13025121Sfrankho * FAT tables. 13030Sstevel@tonic-gate */ 13040Sstevel@tonic-gate static int 13050Sstevel@tonic-gate pc_writefat(struct pcfs *fsp, daddr_t start) 13060Sstevel@tonic-gate { 13070Sstevel@tonic-gate struct buf *bp; 13080Sstevel@tonic-gate size_t off; 13090Sstevel@tonic-gate size_t writesize; 13100Sstevel@tonic-gate int error; 13110Sstevel@tonic-gate uchar_t *fatp = fsp->pcfs_fatp; 13125121Sfrankho size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize; 13130Sstevel@tonic-gate 13140Sstevel@tonic-gate writesize = fsp->pcfs_clsize; 13150Sstevel@tonic-gate for (off = 0; off < fatsize; off += writesize, fatp += writesize) { 13160Sstevel@tonic-gate if (writesize > (fatsize - off)) 13170Sstevel@tonic-gate writesize = fatsize - off; 13180Sstevel@tonic-gate if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) { 13190Sstevel@tonic-gate continue; 13200Sstevel@tonic-gate } 13210Sstevel@tonic-gate bp = ngeteblk(writesize); 13220Sstevel@tonic-gate bp->b_edev = fsp->pcfs_xdev; 13230Sstevel@tonic-gate bp->b_dev = cmpdev(bp->b_edev); 13240Sstevel@tonic-gate bp->b_blkno = pc_dbdaddr(fsp, start + 13250Sstevel@tonic-gate pc_cltodb(fsp, pc_lblkno(fsp, off))); 13260Sstevel@tonic-gate bcopy(fatp, bp->b_un.b_addr, writesize); 13270Sstevel@tonic-gate bwrite2(bp); 13280Sstevel@tonic-gate error = geterror(bp); 13290Sstevel@tonic-gate brelse(bp); 13300Sstevel@tonic-gate if (error) { 13310Sstevel@tonic-gate return (error); 13320Sstevel@tonic-gate } 13330Sstevel@tonic-gate } 13340Sstevel@tonic-gate return (0); 13350Sstevel@tonic-gate } 13360Sstevel@tonic-gate 13370Sstevel@tonic-gate /* 13380Sstevel@tonic-gate * Mark the FAT cluster that 'cn' is stored in as modified. 13390Sstevel@tonic-gate */ 13400Sstevel@tonic-gate void 13410Sstevel@tonic-gate pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn) 13420Sstevel@tonic-gate { 13430Sstevel@tonic-gate pc_cluster32_t bn; 13440Sstevel@tonic-gate size_t size; 13450Sstevel@tonic-gate 13460Sstevel@tonic-gate /* which fat block is the cluster number stored in? */ 13470Sstevel@tonic-gate if (IS_FAT32(fsp)) { 13480Sstevel@tonic-gate size = sizeof (pc_cluster32_t); 13490Sstevel@tonic-gate bn = pc_lblkno(fsp, cn * size); 13500Sstevel@tonic-gate fsp->pcfs_fat_changemap[bn] = 1; 13510Sstevel@tonic-gate } else if (IS_FAT16(fsp)) { 13520Sstevel@tonic-gate size = sizeof (pc_cluster16_t); 13530Sstevel@tonic-gate bn = pc_lblkno(fsp, cn * size); 13540Sstevel@tonic-gate fsp->pcfs_fat_changemap[bn] = 1; 13550Sstevel@tonic-gate } else { 13560Sstevel@tonic-gate offset_t off; 13570Sstevel@tonic-gate pc_cluster32_t nbn; 13580Sstevel@tonic-gate 13590Sstevel@tonic-gate ASSERT(IS_FAT12(fsp)); 13600Sstevel@tonic-gate off = cn + (cn >> 1); 13610Sstevel@tonic-gate bn = pc_lblkno(fsp, off); 13620Sstevel@tonic-gate fsp->pcfs_fat_changemap[bn] = 1; 13630Sstevel@tonic-gate /* does this field wrap into the next fat cluster? */ 13640Sstevel@tonic-gate nbn = pc_lblkno(fsp, off + 1); 13650Sstevel@tonic-gate if (nbn != bn) { 13660Sstevel@tonic-gate fsp->pcfs_fat_changemap[nbn] = 1; 13670Sstevel@tonic-gate } 13680Sstevel@tonic-gate } 13690Sstevel@tonic-gate } 13700Sstevel@tonic-gate 13710Sstevel@tonic-gate /* 13720Sstevel@tonic-gate * return whether the FAT cluster 'bn' is updated and needs to 13730Sstevel@tonic-gate * be written out. 13740Sstevel@tonic-gate */ 13750Sstevel@tonic-gate int 13760Sstevel@tonic-gate pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn) 13770Sstevel@tonic-gate { 13780Sstevel@tonic-gate return (fsp->pcfs_fat_changemap[bn] == 1); 13790Sstevel@tonic-gate } 13802720Sfrankho 13812720Sfrankho /* 13822720Sfrankho * Implementation of VFS_FREEVFS() to support forced umounts. 13832720Sfrankho * This is called by the vfs framework after umount, to trigger 13842720Sfrankho * the release of any resources still associated with the given 13852720Sfrankho * vfs_t once the need to keep them has gone away. 13862720Sfrankho */ 13872720Sfrankho void 13882720Sfrankho pcfs_freevfs(vfs_t *vfsp) 13892720Sfrankho { 13902720Sfrankho struct pcfs *fsp = VFSTOPCFS(vfsp); 13912720Sfrankho 13922720Sfrankho mutex_enter(&pcfslock); 13935121Sfrankho /* 13945121Sfrankho * Purging the FAT closes the device - can't do any more 13955121Sfrankho * I/O after this. 13965121Sfrankho */ 13972720Sfrankho if (fsp->pcfs_fatp != (uchar_t *)0) 13982720Sfrankho pc_invalfat(fsp); 13992720Sfrankho mutex_exit(&pcfslock); 14002720Sfrankho 14012720Sfrankho VN_RELE(fsp->pcfs_devvp); 14022720Sfrankho mutex_destroy(&fsp->pcfs_lock); 14035121Sfrankho kmem_free(fsp, sizeof (*fsp)); 14042720Sfrankho 14052720Sfrankho /* 14062720Sfrankho * Allow _fini() to succeed now, if so desired. 14072720Sfrankho */ 14082720Sfrankho atomic_dec_32(&pcfs_mountcount); 14092720Sfrankho } 14105121Sfrankho 14115121Sfrankho 14125121Sfrankho /* 14135121Sfrankho * PC-style partition parsing and FAT BPB identification/validation code. 14145121Sfrankho * The partition parsers here assume: 14155121Sfrankho * - a FAT filesystem will be in a partition that has one of a set of 14165121Sfrankho * recognized partition IDs 14175121Sfrankho * - the user wants the 'numbering' (C:, D:, ...) that one would get 14185121Sfrankho * on MSDOS 6.x. 14195121Sfrankho * That means any non-FAT partition type (NTFS, HPFS, or any Linux fs) 14205121Sfrankho * will not factor in the enumeration. 14215121Sfrankho * These days, such assumptions should be revisited. FAT is no longer the 14225121Sfrankho * only game in 'PC town'. 14235121Sfrankho */ 14245121Sfrankho /* 14255121Sfrankho * isDosDrive() 14265121Sfrankho * Boolean function. Give it the systid field for an fdisk partition 14275121Sfrankho * and it decides if that's a systid that describes a DOS drive. We 14285121Sfrankho * use systid values defined in sys/dktp/fdisk.h. 14295121Sfrankho */ 14305121Sfrankho static int 14315121Sfrankho isDosDrive(uchar_t checkMe) 14325121Sfrankho { 14335121Sfrankho return ((checkMe == DOSOS12) || (checkMe == DOSOS16) || 14345121Sfrankho (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) || 14355121Sfrankho (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) || 14365121Sfrankho (checkMe == DIAGPART)); 14375121Sfrankho } 14385121Sfrankho 14395121Sfrankho 14405121Sfrankho /* 14415121Sfrankho * isDosExtended() 14425121Sfrankho * Boolean function. Give it the systid field for an fdisk partition 14435121Sfrankho * and it decides if that's a systid that describes an extended DOS 14445121Sfrankho * partition. 14455121Sfrankho */ 14465121Sfrankho static int 14475121Sfrankho isDosExtended(uchar_t checkMe) 14485121Sfrankho { 14495121Sfrankho return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA)); 14505121Sfrankho } 14515121Sfrankho 14525121Sfrankho 14535121Sfrankho /* 14545121Sfrankho * isBootPart() 14555121Sfrankho * Boolean function. Give it the systid field for an fdisk partition 14565121Sfrankho * and it decides if that's a systid that describes a Solaris boot 14575121Sfrankho * partition. 14585121Sfrankho */ 14595121Sfrankho static int 14605121Sfrankho isBootPart(uchar_t checkMe) 14615121Sfrankho { 14625121Sfrankho return (checkMe == X86BOOT); 14635121Sfrankho } 14645121Sfrankho 14655121Sfrankho 14665121Sfrankho /* 14675121Sfrankho * noLogicalDrive() 14685121Sfrankho * Display error message about not being able to find a logical 14695121Sfrankho * drive. 14705121Sfrankho */ 14715121Sfrankho static void 14725121Sfrankho noLogicalDrive(int ldrive) 14735121Sfrankho { 14745121Sfrankho if (ldrive == BOOT_PARTITION_DRIVE) { 14755121Sfrankho cmn_err(CE_NOTE, "!pcfs: no boot partition"); 14765121Sfrankho } else { 14775121Sfrankho cmn_err(CE_NOTE, "!pcfs: %d: no such logical drive", ldrive); 14785121Sfrankho } 14795121Sfrankho } 14805121Sfrankho 14815121Sfrankho 14825121Sfrankho /* 14835121Sfrankho * findTheDrive() 14845121Sfrankho * Discover offset of the requested logical drive, and return 14855121Sfrankho * that offset (startSector), the systid of that drive (sysid), 14865121Sfrankho * and a buffer pointer (bp), with the buffer contents being 14875121Sfrankho * the first sector of the logical drive (i.e., the sector that 14885121Sfrankho * contains the BPB for that drive). 14895121Sfrankho * 14905121Sfrankho * Note: this code is not capable of addressing >2TB disks, as it uses 14915121Sfrankho * daddr_t not diskaddr_t, some of the calculations would overflow 14925121Sfrankho */ 14935121Sfrankho #define COPY_PTBL(mbr, ptblp) \ 14945121Sfrankho bcopy(&(((struct mboot *)(mbr))->parts), (ptblp), \ 14955121Sfrankho FD_NUMPART * sizeof (struct ipart)) 14965121Sfrankho 14975121Sfrankho static int 14985121Sfrankho findTheDrive(struct pcfs *fsp, buf_t **bp) 14995121Sfrankho { 15005121Sfrankho int ldrive = fsp->pcfs_ldrive; 15015121Sfrankho dev_t dev = fsp->pcfs_devvp->v_rdev; 15025121Sfrankho 15035121Sfrankho struct ipart dosp[FD_NUMPART]; /* incore fdisk partition structure */ 15045121Sfrankho daddr_t lastseek = 0; /* Disk block we sought previously */ 15055121Sfrankho daddr_t diskblk = 0; /* Disk block to get */ 15065121Sfrankho daddr_t xstartsect; /* base of Extended DOS partition */ 15075121Sfrankho int logicalDriveCount = 0; /* Count of logical drives seen */ 15085121Sfrankho int extendedPart = -1; /* index of extended dos partition */ 15095121Sfrankho int primaryPart = -1; /* index of primary dos partition */ 15105121Sfrankho int bootPart = -1; /* index of a Solaris boot partition */ 15115121Sfrankho int xnumsect = -1; /* length of extended DOS partition */ 15125121Sfrankho int driveIndex; /* computed FDISK table index */ 15135121Sfrankho daddr_t startsec; 15145121Sfrankho len_t mediasize; 15155121Sfrankho int i; 15165121Sfrankho /* 15175121Sfrankho * Count of drives in the current extended partition's 15185121Sfrankho * FDISK table, and indexes of the drives themselves. 15195121Sfrankho */ 15205121Sfrankho int extndDrives[FD_NUMPART]; 15215121Sfrankho int numDrives = 0; 15225121Sfrankho 15235121Sfrankho /* 15245121Sfrankho * Count of drives (beyond primary) in master boot record's 15255121Sfrankho * FDISK table, and indexes of the drives themselves. 15265121Sfrankho */ 15275121Sfrankho int extraDrives[FD_NUMPART]; 15285121Sfrankho int numExtraDrives = 0; 15295121Sfrankho 15305121Sfrankho /* 15315121Sfrankho * "ldrive == 0" should never happen, as this is a request to 15325121Sfrankho * mount the physical device (and ignore partitioning). The code 15335121Sfrankho * in pcfs_mount() should have made sure that a logical drive number 15345121Sfrankho * is at least 1, meaning we're looking for drive "C:". It is not 15355121Sfrankho * safe (and a bug in the callers of this function) to request logical 15365121Sfrankho * drive number 0; we could ASSERT() but a graceful EIO is a more 15375121Sfrankho * polite way. 15385121Sfrankho */ 15395121Sfrankho if (ldrive == 0) { 15405121Sfrankho cmn_err(CE_NOTE, "!pcfs: request for logical partition zero"); 15415121Sfrankho noLogicalDrive(ldrive); 15425121Sfrankho return (EIO); 15435121Sfrankho } 15445121Sfrankho 15455121Sfrankho /* 15465121Sfrankho * Copy from disk block into memory aligned structure for fdisk usage. 15475121Sfrankho */ 15485121Sfrankho COPY_PTBL((*bp)->b_un.b_addr, dosp); 15495121Sfrankho 15505121Sfrankho /* 15515121Sfrankho * This check is ok because a FAT BPB and a master boot record (MBB) 15525121Sfrankho * have the same signature, in the same position within the block. 15535121Sfrankho */ 15545121Sfrankho if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) { 15555121Sfrankho cmn_err(CE_NOTE, "!pcfs: MBR partition table signature err, " 15565121Sfrankho "device (%x.%x):%d\n", 15575121Sfrankho getmajor(dev), getminor(dev), ldrive); 15585121Sfrankho return (EINVAL); 15595121Sfrankho } 15605121Sfrankho 15615121Sfrankho /* 15625121Sfrankho * Get a summary of what is in the Master FDISK table. 15635121Sfrankho * Normally we expect to find one partition marked as a DOS drive. 15645121Sfrankho * This partition is the one Windows calls the primary dos partition. 15655121Sfrankho * If the machine has any logical drives then we also expect 15665121Sfrankho * to find a partition marked as an extended DOS partition. 15675121Sfrankho * 15685121Sfrankho * Sometimes we'll find multiple partitions marked as DOS drives. 15695121Sfrankho * The Solaris fdisk program allows these partitions 15705121Sfrankho * to be created, but Windows fdisk no longer does. We still need 15715121Sfrankho * to support these, though, since Windows does. We also need to fix 15725121Sfrankho * our fdisk to behave like the Windows version. 15735121Sfrankho * 15745121Sfrankho * It turns out that some off-the-shelf media have *only* an 15755121Sfrankho * Extended partition, so we need to deal with that case as well. 15765121Sfrankho * 15775121Sfrankho * Only a single (the first) Extended or Boot Partition will 15785121Sfrankho * be recognized. Any others will be ignored. 15795121Sfrankho */ 15805121Sfrankho for (i = 0; i < FD_NUMPART; i++) { 15815121Sfrankho DTRACE_PROBE4(primarypart, struct pcfs *, fsp, 15825121Sfrankho uint_t, (uint_t)dosp[i].systid, 15835121Sfrankho uint_t, LE_32(dosp[i].relsect), 15845121Sfrankho uint_t, LE_32(dosp[i].numsect)); 15855121Sfrankho 15865121Sfrankho if (isDosDrive(dosp[i].systid)) { 15875121Sfrankho if (primaryPart < 0) { 15885121Sfrankho logicalDriveCount++; 15895121Sfrankho primaryPart = i; 15905121Sfrankho } else { 15915121Sfrankho extraDrives[numExtraDrives++] = i; 15925121Sfrankho } 15935121Sfrankho continue; 15945121Sfrankho } 15955121Sfrankho if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) { 15965121Sfrankho extendedPart = i; 15975121Sfrankho continue; 15985121Sfrankho } 15995121Sfrankho if ((bootPart < 0) && isBootPart(dosp[i].systid)) { 16005121Sfrankho bootPart = i; 16015121Sfrankho continue; 16025121Sfrankho } 16035121Sfrankho } 16045121Sfrankho 16055121Sfrankho if (ldrive == BOOT_PARTITION_DRIVE) { 16065121Sfrankho if (bootPart < 0) { 16075121Sfrankho noLogicalDrive(ldrive); 16085121Sfrankho return (EINVAL); 16095121Sfrankho } 16105121Sfrankho startsec = LE_32(dosp[bootPart].relsect); 16115121Sfrankho mediasize = LE_32(dosp[bootPart].numsect); 16125121Sfrankho goto found; 16135121Sfrankho } 16145121Sfrankho 16155121Sfrankho if (ldrive == PRIMARY_DOS_DRIVE && primaryPart >= 0) { 16165121Sfrankho startsec = LE_32(dosp[primaryPart].relsect); 16175121Sfrankho mediasize = LE_32(dosp[primaryPart].numsect); 16185121Sfrankho goto found; 16195121Sfrankho } 16205121Sfrankho 16215121Sfrankho /* 16225121Sfrankho * We are not looking for the C: drive (or the primary drive 16235121Sfrankho * was not found), so we had better have an extended partition 16245121Sfrankho * or extra drives in the Master FDISK table. 16255121Sfrankho */ 16265121Sfrankho if ((extendedPart < 0) && (numExtraDrives == 0)) { 16275121Sfrankho cmn_err(CE_NOTE, "!pcfs: no extended dos partition"); 16285121Sfrankho noLogicalDrive(ldrive); 16295121Sfrankho return (EINVAL); 16305121Sfrankho } 16315121Sfrankho 16325121Sfrankho if (extendedPart >= 0) { 16335121Sfrankho diskblk = xstartsect = LE_32(dosp[extendedPart].relsect); 16345121Sfrankho xnumsect = LE_32(dosp[extendedPart].numsect); 16355121Sfrankho do { 16365121Sfrankho /* 16375121Sfrankho * If the seek would not cause us to change 16385121Sfrankho * position on the drive, then we're out of 16395121Sfrankho * extended partitions to examine. 16405121Sfrankho */ 16415121Sfrankho if (diskblk == lastseek) 16425121Sfrankho break; 16435121Sfrankho logicalDriveCount += numDrives; 16445121Sfrankho /* 16455121Sfrankho * Seek the next extended partition, and find 16465121Sfrankho * logical drives within it. 16475121Sfrankho */ 16485121Sfrankho brelse(*bp); 16495121Sfrankho /* 16505121Sfrankho * bread() block numbers are multiples of DEV_BSIZE 16515121Sfrankho * but the device sector size (the unit of partitioning) 16525121Sfrankho * might be larger than that; pcfs_get_device_info() 16535121Sfrankho * has calculated the multiplicator for us. 16545121Sfrankho */ 16555121Sfrankho *bp = bread(dev, 16565121Sfrankho pc_dbdaddr(fsp, diskblk), fsp->pcfs_secsize); 16575121Sfrankho if ((*bp)->b_flags & B_ERROR) { 16585121Sfrankho return (EIO); 16595121Sfrankho } 16605121Sfrankho 16615121Sfrankho lastseek = diskblk; 16625121Sfrankho COPY_PTBL((*bp)->b_un.b_addr, dosp); 16635121Sfrankho if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) { 16645121Sfrankho cmn_err(CE_NOTE, "!pcfs: " 16655121Sfrankho "extended partition table signature err, " 16665121Sfrankho "device (%x.%x):%d, LBA %u", 16675121Sfrankho getmajor(dev), getminor(dev), ldrive, 16685121Sfrankho (uint_t)pc_dbdaddr(fsp, diskblk)); 16695121Sfrankho return (EINVAL); 16705121Sfrankho } 16715121Sfrankho /* 16725121Sfrankho * Count up drives, and track where the next 16735121Sfrankho * extended partition is in case we need it. We 16745121Sfrankho * are expecting only one extended partition. If 16755121Sfrankho * there is more than one we'll only go to the 16765121Sfrankho * first one we see, but warn about ignoring. 16775121Sfrankho */ 16785121Sfrankho numDrives = 0; 16795121Sfrankho for (i = 0; i < FD_NUMPART; i++) { 16805121Sfrankho DTRACE_PROBE4(extendedpart, 16815121Sfrankho struct pcfs *, fsp, 16825121Sfrankho uint_t, (uint_t)dosp[i].systid, 16835121Sfrankho uint_t, LE_32(dosp[i].relsect), 16845121Sfrankho uint_t, LE_32(dosp[i].numsect)); 16855121Sfrankho if (isDosDrive(dosp[i].systid)) { 16865121Sfrankho extndDrives[numDrives++] = i; 16875121Sfrankho } else if (isDosExtended(dosp[i].systid)) { 16885121Sfrankho if (diskblk != lastseek) { 16895121Sfrankho /* 16905121Sfrankho * Already found an extended 16915121Sfrankho * partition in this table. 16925121Sfrankho */ 16935121Sfrankho cmn_err(CE_NOTE, 16945121Sfrankho "!pcfs: ignoring unexpected" 16955121Sfrankho " additional extended" 16965121Sfrankho " partition"); 16975121Sfrankho } else { 16985121Sfrankho diskblk = xstartsect + 16995121Sfrankho LE_32(dosp[i].relsect); 17005121Sfrankho } 17015121Sfrankho } 17025121Sfrankho } 17035121Sfrankho } while (ldrive > logicalDriveCount + numDrives); 17045121Sfrankho 17055121Sfrankho ASSERT(numDrives <= FD_NUMPART); 17065121Sfrankho 17075121Sfrankho if (ldrive <= logicalDriveCount + numDrives) { 17085121Sfrankho /* 17095121Sfrankho * The number of logical drives we've found thus 17105121Sfrankho * far is enough to get us to the one we were 17115121Sfrankho * searching for. 17125121Sfrankho */ 17135121Sfrankho driveIndex = logicalDriveCount + numDrives - ldrive; 17145121Sfrankho mediasize = 17155121Sfrankho LE_32(dosp[extndDrives[driveIndex]].numsect); 17165121Sfrankho startsec = 17175121Sfrankho LE_32(dosp[extndDrives[driveIndex]].relsect) + 17185121Sfrankho lastseek; 17195121Sfrankho if (startsec > (xstartsect + xnumsect)) { 17205121Sfrankho cmn_err(CE_NOTE, "!pcfs: extended partition " 17215121Sfrankho "values bad"); 17225121Sfrankho return (EINVAL); 17235121Sfrankho } 17245121Sfrankho goto found; 17255121Sfrankho } else { 17265121Sfrankho /* 17275121Sfrankho * We ran out of extended dos partition 17285121Sfrankho * drives. The only hope now is to go 17295121Sfrankho * back to extra drives defined in the master 17305121Sfrankho * fdisk table. But we overwrote that table 17315121Sfrankho * already, so we must load it in again. 17325121Sfrankho */ 17335121Sfrankho logicalDriveCount += numDrives; 17345121Sfrankho brelse(*bp); 17355121Sfrankho ASSERT(fsp->pcfs_dosstart == 0); 17365121Sfrankho *bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), 17375121Sfrankho fsp->pcfs_secsize); 17385121Sfrankho if ((*bp)->b_flags & B_ERROR) { 17395121Sfrankho return (EIO); 17405121Sfrankho } 17415121Sfrankho COPY_PTBL((*bp)->b_un.b_addr, dosp); 17425121Sfrankho } 17435121Sfrankho } 17445121Sfrankho /* 17455121Sfrankho * Still haven't found the drive, is it an extra 17465121Sfrankho * drive defined in the main FDISK table? 17475121Sfrankho */ 17485121Sfrankho if (ldrive <= logicalDriveCount + numExtraDrives) { 17495121Sfrankho driveIndex = logicalDriveCount + numExtraDrives - ldrive; 17505121Sfrankho ASSERT(driveIndex < MIN(numExtraDrives, FD_NUMPART)); 17515121Sfrankho mediasize = LE_32(dosp[extraDrives[driveIndex]].numsect); 17525121Sfrankho startsec = LE_32(dosp[extraDrives[driveIndex]].relsect); 17535121Sfrankho goto found; 17545121Sfrankho } 17555121Sfrankho /* 17565121Sfrankho * Still haven't found the drive, and there is 17575121Sfrankho * nowhere else to look. 17585121Sfrankho */ 17595121Sfrankho noLogicalDrive(ldrive); 17605121Sfrankho return (EINVAL); 17615121Sfrankho 17625121Sfrankho found: 17635121Sfrankho /* 17645121Sfrankho * We need this value in units of sectorsize, because PCFS' internal 17655121Sfrankho * offset calculations go haywire for > 512Byte sectors unless all 17665121Sfrankho * pcfs_.*start values are in units of sectors. 17675121Sfrankho * So, assign before the capacity check (that's done in DEV_BSIZE) 17685121Sfrankho */ 17695121Sfrankho fsp->pcfs_dosstart = startsec; 17705121Sfrankho 17715121Sfrankho /* 17725121Sfrankho * convert from device sectors to proper units: 17735121Sfrankho * - starting sector: DEV_BSIZE (as argument to bread()) 17745121Sfrankho * - media size: Bytes 17755121Sfrankho */ 17765121Sfrankho startsec = pc_dbdaddr(fsp, startsec); 17775121Sfrankho mediasize *= fsp->pcfs_secsize; 17785121Sfrankho 17795121Sfrankho /* 17805121Sfrankho * some additional validation / warnings in case the partition table 17815121Sfrankho * and the actual media capacity are not in accordance ... 17825121Sfrankho */ 17835121Sfrankho if (fsp->pcfs_mediasize != 0) { 17845121Sfrankho diskaddr_t startoff = 17855121Sfrankho (diskaddr_t)startsec * (diskaddr_t)DEV_BSIZE; 17865121Sfrankho 17875121Sfrankho if (startoff >= fsp->pcfs_mediasize || 17885121Sfrankho startoff + mediasize > fsp->pcfs_mediasize) { 17895121Sfrankho cmn_err(CE_WARN, 17905121Sfrankho "!pcfs: partition size (LBA start %u, %lld bytes, " 17915121Sfrankho "device (%x.%x):%d) smaller than " 17925121Sfrankho "mediasize (%lld bytes).\n" 17935121Sfrankho "filesystem may be truncated, access errors " 17945121Sfrankho "may result.\n", 17955121Sfrankho (uint_t)startsec, (long long)mediasize, 17965121Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 17975121Sfrankho fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize); 17985121Sfrankho } 17995121Sfrankho } else { 18005121Sfrankho fsp->pcfs_mediasize = mediasize; 18015121Sfrankho } 18025121Sfrankho 18035121Sfrankho return (0); 18045121Sfrankho } 18055121Sfrankho 18065121Sfrankho 18075121Sfrankho static fattype_t 18085121Sfrankho secondaryBPBChecks(struct pcfs *fsp, uchar_t *bpb, size_t secsize) 18095121Sfrankho { 18105121Sfrankho uint32_t ncl = fsp->pcfs_ncluster; 18115121Sfrankho 18125121Sfrankho if (ncl <= 4096) { 18135121Sfrankho if (bpb_get_FatSz16(bpb) == 0) 18145121Sfrankho return (FAT_UNKNOWN); 18155121Sfrankho 18165121Sfrankho if (bpb_get_FatSz16(bpb) * secsize < ncl * 2 && 18175121Sfrankho bpb_get_FatSz16(bpb) * secsize >= (3 * ncl / 2)) 18185121Sfrankho return (FAT12); 18195121Sfrankho if (bcmp(bpb_FilSysType16(bpb), "FAT12", 5) == 0) 18205121Sfrankho return (FAT12); 18215121Sfrankho if (bcmp(bpb_FilSysType16(bpb), "FAT16", 5) == 0) 18225121Sfrankho return (FAT16); 18235121Sfrankho 18245121Sfrankho switch (bpb_get_Media(bpb)) { 18255121Sfrankho case SS8SPT: 18265121Sfrankho case DS8SPT: 18275121Sfrankho case SS9SPT: 18285121Sfrankho case DS9SPT: 18295121Sfrankho case DS18SPT: 18305121Sfrankho case DS9_15SPT: 18315121Sfrankho /* 18325121Sfrankho * Is this reliable - all floppies are FAT12 ? 18335121Sfrankho */ 18345121Sfrankho return (FAT12); 18355121Sfrankho case MD_FIXED: 18365121Sfrankho /* 18375121Sfrankho * Is this reliable - disks are always FAT16 ? 18385121Sfrankho */ 18395121Sfrankho return (FAT16); 18405121Sfrankho default: 18415121Sfrankho break; 18425121Sfrankho } 18435121Sfrankho } else if (ncl <= 65536) { 18445121Sfrankho if (bpb_get_FatSz16(bpb) == 0 && bpb_get_FatSz32(bpb) > 0) 18455121Sfrankho return (FAT32); 18465121Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig32(bpb))) 18475121Sfrankho return (FAT32); 18485121Sfrankho if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb))) 18495121Sfrankho return (FAT32); 18505121Sfrankho 18515121Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig16(bpb))) 18525121Sfrankho return (FAT16); 18535121Sfrankho if (bpb_get_FatSz16(bpb) * secsize < ncl * 4) 18545121Sfrankho return (FAT16); 18555121Sfrankho } 18565121Sfrankho 18575121Sfrankho /* 18585121Sfrankho * We don't know 18595121Sfrankho */ 18605121Sfrankho return (FAT_UNKNOWN); 18615121Sfrankho } 18625121Sfrankho 18635121Sfrankho /* 18645121Sfrankho * Check to see if the BPB we found is correct. 18655121Sfrankho * 18665121Sfrankho * This looks far more complicated that it needs to be for pure structural 18675121Sfrankho * validation. The reason for this is that parseBPB() is also used for 18685121Sfrankho * debugging purposes (mdb dcmd) and we therefore want a bitmap of which 18696644Sgd78059 * BPB fields (do not) have 'known good' values, even if we (do not) reject 18706644Sgd78059 * the BPB when attempting to mount the filesystem. 18716644Sgd78059 * 18726644Sgd78059 * Real-world usage of FAT shows there are a lot of corner-case situations 18736644Sgd78059 * and, following the specification strictly, invalid filesystems out there. 18746644Sgd78059 * Known are situations such as: 18756644Sgd78059 * - FAT12/FAT16 filesystems with garbage in either totsec16/32 18766644Sgd78059 * instead of the zero in one of the fields mandated by the spec 18776644Sgd78059 * - filesystems that claim to be larger than the partition they're in 18786644Sgd78059 * - filesystems without valid media descriptor 18796644Sgd78059 * - FAT32 filesystems with RootEntCnt != 0 18806644Sgd78059 * - FAT32 filesystems with less than 65526 clusters 18816644Sgd78059 * - FAT32 filesystems without valid FSI sector 18826644Sgd78059 * - FAT32 filesystems with FAT size in fatsec16 instead of fatsec32 18836644Sgd78059 * 18846644Sgd78059 * Such filesystems are accessible by PCFS - if it'd know to start with that 18856644Sgd78059 * the filesystem should be treated as a specific FAT type. Before S10, it 18866644Sgd78059 * relied on the PC/fdisk partition type for the purpose and almost completely 18876644Sgd78059 * ignored the BPB; now it ignores the partition type for anything else but 18886644Sgd78059 * logical drive enumeration, which can result in rejection of (invalid) 18896644Sgd78059 * FAT32 - if the partition ID says FAT32, but the filesystem, for example 18906644Sgd78059 * has less than 65526 clusters. 18916644Sgd78059 * 18926644Sgd78059 * Without a "force this fs as FAT{12,16,32}" tunable or mount option, it's 18936644Sgd78059 * not possible to allow all such mostly-compliant filesystems in unless one 18946644Sgd78059 * accepts false positives (definitely invalid filesystems that cause problems 18956644Sgd78059 * later). This at least allows to pinpoint why the mount failed. 18966644Sgd78059 * 18976644Sgd78059 * Due to the use of FAT on removeable media, all relaxations of the rules 18986644Sgd78059 * here need to be carefully evaluated wrt. to potential effects on PCFS 18996644Sgd78059 * resilience. A faulty/"mis-crafted" filesystem must not cause a panic, so 19006644Sgd78059 * beware. 19015121Sfrankho */ 19025121Sfrankho static int 19035121Sfrankho parseBPB(struct pcfs *fsp, uchar_t *bpb, int *valid) 19045121Sfrankho { 19055121Sfrankho fattype_t type; 19065121Sfrankho 19075121Sfrankho uint32_t ncl; /* number of clusters in file area */ 19085121Sfrankho uint32_t rec; 19095121Sfrankho uint32_t reserved; 19105121Sfrankho uint32_t fsisec, bkbootsec; 19115121Sfrankho blkcnt_t totsec, totsec16, totsec32, datasec; 19125121Sfrankho size_t fatsec, fatsec16, fatsec32, rdirsec; 19135121Sfrankho size_t secsize; 19145121Sfrankho len_t mediasize; 19155121Sfrankho uint64_t validflags = 0; 19165121Sfrankho 19175121Sfrankho if (VALID_BPBSIG(bpb_get_BPBSig(bpb))) 19185121Sfrankho validflags |= BPB_BPBSIG_OK; 19195121Sfrankho 19205121Sfrankho rec = bpb_get_RootEntCnt(bpb); 19215121Sfrankho reserved = bpb_get_RsvdSecCnt(bpb); 19225121Sfrankho fsisec = bpb_get_FSInfo32(bpb); 19235121Sfrankho bkbootsec = bpb_get_BkBootSec32(bpb); 19245121Sfrankho totsec16 = (blkcnt_t)bpb_get_TotSec16(bpb); 19255121Sfrankho totsec32 = (blkcnt_t)bpb_get_TotSec32(bpb); 19265121Sfrankho fatsec16 = bpb_get_FatSz16(bpb); 19275121Sfrankho fatsec32 = bpb_get_FatSz32(bpb); 19285121Sfrankho 19295121Sfrankho totsec = totsec16 ? totsec16 : totsec32; 19305121Sfrankho fatsec = fatsec16 ? fatsec16 : fatsec32; 19315121Sfrankho 19325121Sfrankho secsize = bpb_get_BytesPerSec(bpb); 19335121Sfrankho if (!VALID_SECSIZE(secsize)) 19345121Sfrankho secsize = fsp->pcfs_secsize; 19355121Sfrankho if (secsize != fsp->pcfs_secsize) { 19365121Sfrankho PC_DPRINTF3(3, "!pcfs: parseBPB, device (%x.%x):%d:\n", 19375121Sfrankho getmajor(fsp->pcfs_xdev), 19385121Sfrankho getminor(fsp->pcfs_xdev), fsp->pcfs_ldrive); 19395121Sfrankho PC_DPRINTF2(3, "!BPB secsize %d != " 19405121Sfrankho "autodetected media block size %d\n", 19415121Sfrankho (int)secsize, (int)fsp->pcfs_secsize); 19425121Sfrankho if (fsp->pcfs_ldrive) { 19435121Sfrankho /* 19445121Sfrankho * We've already attempted to parse the partition 19455121Sfrankho * table. If the block size used for that don't match 19465121Sfrankho * the PCFS sector size, we're hosed one way or the 19475121Sfrankho * other. Just try what happens. 19485121Sfrankho */ 19495121Sfrankho secsize = fsp->pcfs_secsize; 19505121Sfrankho PC_DPRINTF1(3, 19515121Sfrankho "!pcfs: Using autodetected secsize %d\n", 19525121Sfrankho (int)secsize); 19535121Sfrankho } else { 19545121Sfrankho /* 19555121Sfrankho * This allows mounting lofi images of PCFS partitions 19565121Sfrankho * with sectorsize != DEV_BSIZE. We can't parse the 19575121Sfrankho * partition table on whole-disk images unless the 19585121Sfrankho * (undocumented) "secsize=..." mount option is used, 19595121Sfrankho * but at least this allows us to mount if we have 19605121Sfrankho * an image of a partition. 19615121Sfrankho */ 19625121Sfrankho PC_DPRINTF1(3, 19635121Sfrankho "!pcfs: Using BPB secsize %d\n", (int)secsize); 19645121Sfrankho } 19655121Sfrankho } 19665121Sfrankho 19675121Sfrankho if (fsp->pcfs_mediasize == 0) { 19685121Sfrankho mediasize = (len_t)totsec * (len_t)secsize; 19696644Sgd78059 /* 19706644Sgd78059 * This is not an error because not all devices support the 19716644Sgd78059 * dkio(7i) mediasize queries, and/or not all devices are 19726644Sgd78059 * partitioned. If we have not been able to figure out the 19736644Sgd78059 * size of the underlaying medium, we have to trust the BPB. 19746644Sgd78059 */ 19755121Sfrankho PC_DPRINTF4(3, "!pcfs: parseBPB: mediasize autodetect failed " 19765121Sfrankho "on device (%x.%x):%d, trusting BPB totsec (%lld Bytes)\n", 19775121Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 19785121Sfrankho fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize); 19795121Sfrankho } else if ((len_t)totsec * (len_t)secsize > fsp->pcfs_mediasize) { 19805121Sfrankho cmn_err(CE_WARN, 19815121Sfrankho "!pcfs: autodetected mediasize (%lld Bytes) smaller than " 19825121Sfrankho "FAT BPB mediasize (%lld Bytes).\n" 19835121Sfrankho "truncated filesystem on device (%x.%x):%d, access errors " 19845121Sfrankho "possible.\n", 19855121Sfrankho (long long)fsp->pcfs_mediasize, 19865121Sfrankho (long long)(totsec * (blkcnt_t)secsize), 19875121Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 19885121Sfrankho fsp->pcfs_ldrive); 19895121Sfrankho mediasize = fsp->pcfs_mediasize; 19905121Sfrankho } else { 19915121Sfrankho /* 19925121Sfrankho * This is actually ok. A FAT needs not occupy the maximum 19935121Sfrankho * space available in its partition, it can be shorter. 19945121Sfrankho */ 19955121Sfrankho mediasize = (len_t)totsec * (len_t)secsize; 19965121Sfrankho } 19975121Sfrankho 19985121Sfrankho /* 19995121Sfrankho * Since we let just about anything pass through this function, 20005121Sfrankho * fence against divide-by-zero here. 20015121Sfrankho */ 20025121Sfrankho if (secsize) 20035121Sfrankho rdirsec = roundup(rec * 32, secsize) / secsize; 20045121Sfrankho else 20055121Sfrankho rdirsec = 0; 20065121Sfrankho 20075121Sfrankho /* 20085121Sfrankho * This assignment is necessary before pc_dbdaddr() can first be 20095121Sfrankho * used. Must initialize the value here. 20105121Sfrankho */ 20115121Sfrankho fsp->pcfs_secsize = secsize; 20125121Sfrankho fsp->pcfs_sdshift = ddi_ffs(secsize / DEV_BSIZE) - 1; 20135121Sfrankho 20145121Sfrankho fsp->pcfs_mediasize = mediasize; 20155121Sfrankho 20165121Sfrankho fsp->pcfs_spcl = bpb_get_SecPerClus(bpb); 20175121Sfrankho fsp->pcfs_numfat = bpb_get_NumFATs(bpb); 20185121Sfrankho fsp->pcfs_mediadesc = bpb_get_Media(bpb); 20195121Sfrankho fsp->pcfs_clsize = secsize * fsp->pcfs_spcl; 20205121Sfrankho fsp->pcfs_rdirsec = rdirsec; 20215121Sfrankho 20225121Sfrankho /* 20235121Sfrankho * Remember: All PCFS offset calculations in sectors. Before I/O 20245121Sfrankho * is done, convert to DEV_BSIZE units via pc_dbdaddr(). This is 20255121Sfrankho * necessary so that media with > 512Byte sector sizes work correctly. 20265121Sfrankho */ 20275121Sfrankho fsp->pcfs_fatstart = fsp->pcfs_dosstart + reserved; 20285121Sfrankho fsp->pcfs_rdirstart = fsp->pcfs_fatstart + fsp->pcfs_numfat * fatsec; 20295121Sfrankho fsp->pcfs_datastart = fsp->pcfs_rdirstart + rdirsec; 20305121Sfrankho datasec = totsec - 20315121Sfrankho (blkcnt_t)fatsec * fsp->pcfs_numfat - 20325121Sfrankho (blkcnt_t)rdirsec - 20335121Sfrankho (blkcnt_t)reserved; 20345121Sfrankho 20355121Sfrankho DTRACE_PROBE4(fatgeometry, 20365121Sfrankho blkcnt_t, totsec, size_t, fatsec, 20375121Sfrankho size_t, rdirsec, blkcnt_t, datasec); 20385121Sfrankho 20395121Sfrankho /* 20406644Sgd78059 * 'totsec' is taken directly from the BPB and guaranteed to fit 20416644Sgd78059 * into a 32bit unsigned integer. The calculation of 'datasec', 20426644Sgd78059 * on the other hand, could underflow for incorrect values in 20436644Sgd78059 * rdirsec/reserved/fatsec. Check for that. 20446644Sgd78059 * We also check that the BPB conforms to the FAT specification's 20456644Sgd78059 * requirement that either of the 16/32bit total sector counts 20466644Sgd78059 * must be zero. 20475121Sfrankho */ 20485121Sfrankho if (totsec != 0 && 20495121Sfrankho (totsec16 == totsec32 || totsec16 == 0 || totsec32 == 0) && 20505121Sfrankho datasec < totsec && datasec <= UINT32_MAX) 20515121Sfrankho validflags |= BPB_TOTSEC_OK; 20525121Sfrankho 20536644Sgd78059 if ((len_t)totsec * (len_t)secsize <= mediasize) 20545121Sfrankho validflags |= BPB_MEDIASZ_OK; 20555121Sfrankho 20565121Sfrankho if (VALID_SECSIZE(secsize)) 20575121Sfrankho validflags |= BPB_SECSIZE_OK; 20585121Sfrankho if (VALID_SPCL(fsp->pcfs_spcl)) 20595121Sfrankho validflags |= BPB_SECPERCLUS_OK; 20605121Sfrankho if (VALID_CLSIZE(fsp->pcfs_clsize)) 20615121Sfrankho validflags |= BPB_CLSIZE_OK; 20625121Sfrankho if (VALID_NUMFATS(fsp->pcfs_numfat)) 20635121Sfrankho validflags |= BPB_NUMFAT_OK; 20645121Sfrankho if (VALID_RSVDSEC(reserved) && reserved < totsec) 20655121Sfrankho validflags |= BPB_RSVDSECCNT_OK; 20665121Sfrankho if (VALID_MEDIA(fsp->pcfs_mediadesc)) 20675121Sfrankho validflags |= BPB_MEDIADESC_OK; 20685121Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig16(bpb))) 20695121Sfrankho validflags |= BPB_BOOTSIG16_OK; 20705121Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig32(bpb))) 20715121Sfrankho validflags |= BPB_BOOTSIG32_OK; 20725121Sfrankho if (VALID_FSTYPSTR16(bpb_FilSysType16(bpb))) 20735121Sfrankho validflags |= BPB_FSTYPSTR16_OK; 20745121Sfrankho if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb))) 20755121Sfrankho validflags |= BPB_FSTYPSTR32_OK; 20765121Sfrankho if (VALID_OEMNAME(bpb_OEMName(bpb))) 20775121Sfrankho validflags |= BPB_OEMNAME_OK; 20785121Sfrankho if (bkbootsec > 0 && bkbootsec <= reserved && fsisec != bkbootsec) 20795121Sfrankho validflags |= BPB_BKBOOTSEC_OK; 20805121Sfrankho if (fsisec > 0 && fsisec <= reserved) 20815121Sfrankho validflags |= BPB_FSISEC_OK; 20825121Sfrankho if (VALID_JMPBOOT(bpb_jmpBoot(bpb))) 20835121Sfrankho validflags |= BPB_JMPBOOT_OK; 20845121Sfrankho if (VALID_FSVER32(bpb_get_FSVer32(bpb))) 20855121Sfrankho validflags |= BPB_FSVER_OK; 20865121Sfrankho if (VALID_VOLLAB(bpb_VolLab16(bpb))) 20875121Sfrankho validflags |= BPB_VOLLAB16_OK; 20885121Sfrankho if (VALID_VOLLAB(bpb_VolLab32(bpb))) 20895121Sfrankho validflags |= BPB_VOLLAB32_OK; 20905121Sfrankho if (VALID_EXTFLAGS(bpb_get_ExtFlags32(bpb))) 20915121Sfrankho validflags |= BPB_EXTFLAGS_OK; 20925121Sfrankho 20935121Sfrankho /* 20945121Sfrankho * Try to determine which FAT format to use. 20955121Sfrankho * 20965121Sfrankho * Calculate the number of clusters in order to determine 20975121Sfrankho * the type of FAT we are looking at. This is the only 20985121Sfrankho * recommended way of determining FAT type, though there 20995121Sfrankho * are other hints in the data, this is the best way. 21005121Sfrankho * 21015121Sfrankho * Since we let just about "anything" pass through this function 21025121Sfrankho * without early exits, fence against divide-by-zero here. 21035121Sfrankho * 21045121Sfrankho * datasec was already validated against UINT32_MAX so we know 21055121Sfrankho * the result will not overflow the 32bit calculation. 21065121Sfrankho */ 21075121Sfrankho if (fsp->pcfs_spcl) 21085121Sfrankho ncl = (uint32_t)datasec / fsp->pcfs_spcl; 21095121Sfrankho else 21105121Sfrankho ncl = 0; 21115121Sfrankho 21125121Sfrankho fsp->pcfs_ncluster = ncl; 21135121Sfrankho 21145121Sfrankho /* 21155121Sfrankho * From the Microsoft FAT specification: 21165121Sfrankho * In the following example, when it says <, it does not mean <=. 21175121Sfrankho * Note also that the numbers are correct. The first number for 21185121Sfrankho * FAT12 is 4085; the second number for FAT16 is 65525. These numbers 21195121Sfrankho * and the '<' signs are not wrong. 21205121Sfrankho * 21215121Sfrankho * We "specialdetect" the corner cases, and use at least one "extra" 21225121Sfrankho * criterion to decide whether it's FAT16 or FAT32 if the cluster 21235121Sfrankho * count is dangerously close to the boundaries. 21245121Sfrankho */ 21255121Sfrankho 21265121Sfrankho if (ncl <= PCF_FIRSTCLUSTER) { 21275121Sfrankho type = FAT_UNKNOWN; 21285121Sfrankho } else if (ncl < 4085) { 21295121Sfrankho type = FAT12; 21305121Sfrankho } else if (ncl <= 4096) { 21315121Sfrankho type = FAT_QUESTIONABLE; 21325121Sfrankho } else if (ncl < 65525) { 21335121Sfrankho type = FAT16; 21345121Sfrankho } else if (ncl <= 65536) { 21355121Sfrankho type = FAT_QUESTIONABLE; 21365121Sfrankho } else if (ncl < PCF_LASTCLUSTER32) { 21375121Sfrankho type = FAT32; 21385121Sfrankho } else { 21395121Sfrankho type = FAT_UNKNOWN; 21405121Sfrankho } 21415121Sfrankho 21425121Sfrankho DTRACE_PROBE4(parseBPB__initial, 21435121Sfrankho struct pcfs *, fsp, unsigned char *, bpb, 21445121Sfrankho int, validflags, fattype_t, type); 21455121Sfrankho 21465121Sfrankho recheck: 21475121Sfrankho fsp->pcfs_fatsec = fatsec; 21485121Sfrankho 21495121Sfrankho /* Do some final sanity checks for each specific type of FAT */ 21505121Sfrankho switch (type) { 21515121Sfrankho case FAT12: 21525121Sfrankho if (rec != 0) 21535121Sfrankho validflags |= BPB_ROOTENTCNT_OK; 21545121Sfrankho if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec || 21555121Sfrankho bpb_get_TotSec16(bpb) == 0) 21565121Sfrankho validflags |= BPB_TOTSEC16_OK; 21575121Sfrankho if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec || 21585121Sfrankho bpb_get_TotSec32(bpb) == 0) 21595121Sfrankho validflags |= BPB_TOTSEC32_OK; 21605121Sfrankho if (bpb_get_FatSz16(bpb) == fatsec) 21615121Sfrankho validflags |= BPB_FATSZ16_OK; 21625121Sfrankho if (fatsec * secsize >= ncl * 3 / 2) 21635121Sfrankho validflags |= BPB_FATSZ_OK; 21645121Sfrankho if (ncl < 4085) 21655121Sfrankho validflags |= BPB_NCLUSTERS_OK; 21665121Sfrankho 21675121Sfrankho fsp->pcfs_lastclmark = (PCF_LASTCLUSTER & 0xfff); 21685121Sfrankho fsp->pcfs_rootblksize = 21695121Sfrankho fsp->pcfs_rdirsec * secsize; 21705121Sfrankho fsp->pcfs_fsistart = 0; 21715121Sfrankho 21725121Sfrankho if ((validflags & FAT12_VALIDMSK) != FAT12_VALIDMSK) 21735121Sfrankho type = FAT_UNKNOWN; 21745121Sfrankho break; 21755121Sfrankho case FAT16: 21765121Sfrankho if (rec != 0) 21775121Sfrankho validflags |= BPB_ROOTENTCNT_OK; 21785121Sfrankho if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec || 21795121Sfrankho bpb_get_TotSec16(bpb) == 0) 21805121Sfrankho validflags |= BPB_TOTSEC16_OK; 21815121Sfrankho if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec || 21825121Sfrankho bpb_get_TotSec32(bpb) == 0) 21835121Sfrankho validflags |= BPB_TOTSEC32_OK; 21845121Sfrankho if (bpb_get_FatSz16(bpb) == fatsec) 21855121Sfrankho validflags |= BPB_FATSZ16_OK; 21865121Sfrankho if (fatsec * secsize >= ncl * 2) 21875121Sfrankho validflags |= BPB_FATSZ_OK; 21885121Sfrankho if (ncl >= 4085 && ncl < 65525) 21895121Sfrankho validflags |= BPB_NCLUSTERS_OK; 21905121Sfrankho 21915121Sfrankho fsp->pcfs_lastclmark = PCF_LASTCLUSTER; 21925121Sfrankho fsp->pcfs_rootblksize = 21935121Sfrankho fsp->pcfs_rdirsec * secsize; 21945121Sfrankho fsp->pcfs_fsistart = 0; 21955121Sfrankho 21965121Sfrankho if ((validflags & FAT16_VALIDMSK) != FAT16_VALIDMSK) 21975121Sfrankho type = FAT_UNKNOWN; 21985121Sfrankho break; 21995121Sfrankho case FAT32: 22005121Sfrankho if (rec == 0) 22015121Sfrankho validflags |= BPB_ROOTENTCNT_OK; 22025121Sfrankho if (bpb_get_TotSec16(bpb) == 0) 22035121Sfrankho validflags |= BPB_TOTSEC16_OK; 22045121Sfrankho if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec) 22055121Sfrankho validflags |= BPB_TOTSEC32_OK; 22065121Sfrankho if (bpb_get_FatSz16(bpb) == 0) 22075121Sfrankho validflags |= BPB_FATSZ16_OK; 22085121Sfrankho if (bpb_get_FatSz32(bpb) == fatsec) 22095121Sfrankho validflags |= BPB_FATSZ32_OK; 22105121Sfrankho if (fatsec * secsize >= ncl * 4) 22115121Sfrankho validflags |= BPB_FATSZ_OK; 22125121Sfrankho if (ncl >= 65525 && ncl < PCF_LASTCLUSTER32) 22135121Sfrankho validflags |= BPB_NCLUSTERS_OK; 22145121Sfrankho 22155121Sfrankho fsp->pcfs_lastclmark = PCF_LASTCLUSTER32; 22165121Sfrankho fsp->pcfs_rootblksize = fsp->pcfs_clsize; 22175121Sfrankho fsp->pcfs_fsistart = fsp->pcfs_dosstart + fsisec; 22185121Sfrankho if (validflags & BPB_FSISEC_OK) 22195121Sfrankho fsp->pcfs_flags |= PCFS_FSINFO_OK; 22205121Sfrankho fsp->pcfs_rootclnum = bpb_get_RootClus32(bpb); 22215121Sfrankho if (pc_validcl(fsp, fsp->pcfs_rootclnum)) 22225121Sfrankho validflags |= BPB_ROOTCLUSTER_OK; 22235121Sfrankho 22245121Sfrankho /* 22255121Sfrankho * Current PCFS code only works if 'pcfs_rdirstart' 22265121Sfrankho * contains the root cluster number on FAT32. 22275121Sfrankho * That's a mis-use and would better be changed. 22285121Sfrankho */ 22295121Sfrankho fsp->pcfs_rdirstart = (daddr_t)fsp->pcfs_rootclnum; 22305121Sfrankho 22315121Sfrankho if ((validflags & FAT32_VALIDMSK) != FAT32_VALIDMSK) 22325121Sfrankho type = FAT_UNKNOWN; 22335121Sfrankho break; 22345121Sfrankho case FAT_QUESTIONABLE: 22355121Sfrankho type = secondaryBPBChecks(fsp, bpb, secsize); 22365121Sfrankho goto recheck; 22375121Sfrankho default: 22385121Sfrankho ASSERT(type == FAT_UNKNOWN); 22395121Sfrankho break; 22405121Sfrankho } 22415121Sfrankho 22425121Sfrankho ASSERT(type != FAT_QUESTIONABLE); 22435121Sfrankho 22445121Sfrankho fsp->pcfs_fattype = type; 22455121Sfrankho 22465121Sfrankho if (valid) 22475121Sfrankho *valid = validflags; 22485121Sfrankho 22495121Sfrankho DTRACE_PROBE4(parseBPB__final, 22505121Sfrankho struct pcfs *, fsp, unsigned char *, bpb, 22515121Sfrankho int, validflags, fattype_t, type); 22525121Sfrankho 22535121Sfrankho if (type != FAT_UNKNOWN) { 22545121Sfrankho ASSERT((secsize & (DEV_BSIZE - 1)) == 0); 22555121Sfrankho ASSERT(ISP2(secsize / DEV_BSIZE)); 22565121Sfrankho return (1); 22575121Sfrankho } 22585121Sfrankho 22595121Sfrankho return (0); 22605121Sfrankho } 22615121Sfrankho 22625121Sfrankho 22635121Sfrankho /* 22645121Sfrankho * Detect the device's native block size (sector size). 22655121Sfrankho * 22665121Sfrankho * Test whether the device is: 22675121Sfrankho * - a floppy device from a known controller type via DKIOCINFO 22685121Sfrankho * - a real floppy using the fd(7d) driver and capable of fdio(7I) ioctls 22695121Sfrankho * - a PCMCIA sram memory card (pseudofloppy) using pcram(7d) 22705121Sfrankho * - a USB floppy drive (identified by drive geometry) 22715121Sfrankho * 22725121Sfrankho * Detecting a floppy will make PCFS metadata updates on such media synchronous, 22735121Sfrankho * to minimize risks due to slow I/O and user hotplugging / device ejection. 22745121Sfrankho * 22755121Sfrankho * This might be a bit wasteful on kernel stack space; if anyone's 22765121Sfrankho * bothered by this, kmem_alloc/kmem_free the ioctl arguments... 22775121Sfrankho */ 22785121Sfrankho static void 22795121Sfrankho pcfs_device_getinfo(struct pcfs *fsp) 22805121Sfrankho { 22815121Sfrankho dev_t rdev = fsp->pcfs_xdev; 22825121Sfrankho int error; 22835121Sfrankho union { 22845121Sfrankho struct dk_minfo mi; 22855121Sfrankho struct dk_cinfo ci; 22865121Sfrankho struct dk_geom gi; 22875121Sfrankho struct fd_char fc; 22885121Sfrankho } arg; /* save stackspace ... */ 22895121Sfrankho intptr_t argp = (intptr_t)&arg; 22905121Sfrankho ldi_handle_t lh; 22915121Sfrankho ldi_ident_t li; 22925121Sfrankho int isfloppy, isremoveable, ishotpluggable; 22935121Sfrankho cred_t *cr = CRED(); 22945121Sfrankho 22955121Sfrankho if (ldi_ident_from_dev(rdev, &li)) 22965121Sfrankho goto out; 22975121Sfrankho 22985121Sfrankho error = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, cr, &lh, li); 22995121Sfrankho ldi_ident_release(li); 23005121Sfrankho if (error) 23015121Sfrankho goto out; 23025121Sfrankho 23035121Sfrankho /* 23045121Sfrankho * Not sure if this could possibly happen. It'd be a bit like 23055121Sfrankho * VOP_OPEN() changing the passed-in vnode ptr. We're just not 23065121Sfrankho * expecting it, needs some thought if triggered ... 23075121Sfrankho */ 23085121Sfrankho ASSERT(fsp->pcfs_xdev == rdev); 23095121Sfrankho 23105121Sfrankho /* 23115121Sfrankho * Check for removeable/hotpluggable media. 23125121Sfrankho */ 23135121Sfrankho if (ldi_ioctl(lh, DKIOCREMOVABLE, 23145121Sfrankho (intptr_t)&isremoveable, FKIOCTL, cr, NULL)) { 23155121Sfrankho isremoveable = 0; 23165121Sfrankho } 23175121Sfrankho if (ldi_ioctl(lh, DKIOCHOTPLUGGABLE, 23185121Sfrankho (intptr_t)&ishotpluggable, FKIOCTL, cr, NULL)) { 23195121Sfrankho ishotpluggable = 0; 23205121Sfrankho } 23215121Sfrankho 23225121Sfrankho /* 23235121Sfrankho * Make sure we don't use "half-initialized" values if the ioctls fail. 23245121Sfrankho */ 23255121Sfrankho if (ldi_ioctl(lh, DKIOCGMEDIAINFO, argp, FKIOCTL, cr, NULL)) { 23265121Sfrankho bzero(&arg, sizeof (arg)); 23275121Sfrankho fsp->pcfs_mediasize = 0; 23285121Sfrankho } else { 23295121Sfrankho fsp->pcfs_mediasize = 23305121Sfrankho (len_t)arg.mi.dki_lbsize * 23315121Sfrankho (len_t)arg.mi.dki_capacity; 23325121Sfrankho } 23335121Sfrankho 23345121Sfrankho if (VALID_SECSIZE(arg.mi.dki_lbsize)) { 23355121Sfrankho if (fsp->pcfs_secsize == 0) { 23365121Sfrankho fsp->pcfs_secsize = arg.mi.dki_lbsize; 23375121Sfrankho fsp->pcfs_sdshift = 23385121Sfrankho ddi_ffs(arg.mi.dki_lbsize / DEV_BSIZE) - 1; 23395121Sfrankho } else { 23405121Sfrankho PC_DPRINTF4(1, "!pcfs: autodetected media block size " 23415121Sfrankho "%d, device (%x.%x), different from user-provided " 23425121Sfrankho "%d. User override - ignoring autodetect result.\n", 23435121Sfrankho arg.mi.dki_lbsize, 23445121Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 23455121Sfrankho fsp->pcfs_secsize); 23465121Sfrankho } 23475121Sfrankho } else if (arg.mi.dki_lbsize) { 23485121Sfrankho PC_DPRINTF3(1, "!pcfs: autodetected media block size " 23495121Sfrankho "%d, device (%x.%x), invalid (not 512, 1024, 2048, 4096). " 23505121Sfrankho "Ignoring autodetect result.\n", 23515121Sfrankho arg.mi.dki_lbsize, 23525121Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev)); 23535121Sfrankho } 23545121Sfrankho 23555121Sfrankho /* 23565121Sfrankho * We treat the following media types as a floppy by default. 23575121Sfrankho */ 23585121Sfrankho isfloppy = 23595121Sfrankho (arg.mi.dki_media_type == DK_FLOPPY || 23605121Sfrankho arg.mi.dki_media_type == DK_ZIP || 23615121Sfrankho arg.mi.dki_media_type == DK_JAZ); 23625121Sfrankho 23635121Sfrankho /* 23645121Sfrankho * if this device understands fdio(7I) requests it's 23655121Sfrankho * obviously a floppy drive. 23665121Sfrankho */ 23675121Sfrankho if (!isfloppy && 23685121Sfrankho !ldi_ioctl(lh, FDIOGCHAR, argp, FKIOCTL, cr, NULL)) 23695121Sfrankho isfloppy = 1; 23705121Sfrankho 23715121Sfrankho /* 23725121Sfrankho * some devices (PCMCIA pseudofloppies) we like to treat 23735121Sfrankho * as floppies, but they don't understand fdio(7I) requests. 23745121Sfrankho */ 23755121Sfrankho if (!isfloppy && 23765121Sfrankho !ldi_ioctl(lh, DKIOCINFO, argp, FKIOCTL, cr, NULL) && 23775121Sfrankho (arg.ci.dki_ctype == DKC_WDC2880 || 23785121Sfrankho arg.ci.dki_ctype == DKC_NCRFLOPPY || 23795121Sfrankho arg.ci.dki_ctype == DKC_SMSFLOPPY || 23805121Sfrankho arg.ci.dki_ctype == DKC_INTEL82077 || 23815121Sfrankho (arg.ci.dki_ctype == DKC_PCMCIA_MEM && 23825121Sfrankho arg.ci.dki_flags & DKI_PCMCIA_PFD))) 23835121Sfrankho isfloppy = 1; 23845121Sfrankho 23855121Sfrankho /* 23865121Sfrankho * This is the "final fallback" test - media with 23875121Sfrankho * 2 heads and 80 cylinders are assumed to be floppies. 23885121Sfrankho * This is normally true for USB floppy drives ... 23895121Sfrankho */ 23905121Sfrankho if (!isfloppy && 23915121Sfrankho !ldi_ioctl(lh, DKIOCGGEOM, argp, FKIOCTL, cr, NULL) && 23925121Sfrankho (arg.gi.dkg_ncyl == 80 && arg.gi.dkg_nhead == 2)) 23935121Sfrankho isfloppy = 1; 23945121Sfrankho 23955121Sfrankho /* 23965121Sfrankho * This is similar to the "old" PCFS code that sets this flag 23975121Sfrankho * just based on the media descriptor being 0xf8 (MD_FIXED). 23985121Sfrankho * Should be re-worked. We really need some specialcasing for 23995121Sfrankho * removeable media. 24005121Sfrankho */ 24015121Sfrankho if (!isfloppy) { 24025121Sfrankho fsp->pcfs_flags |= PCFS_NOCHK; 24035121Sfrankho } 24045121Sfrankho 24055121Sfrankho /* 24065121Sfrankho * We automatically disable access time updates if the medium is 24075121Sfrankho * removeable and/or hotpluggable, and the admin did not explicitly 24085121Sfrankho * request access time updates (via the "atime" mount option). 24095121Sfrankho * The majority of flash-based media should fit this category. 24105121Sfrankho * Minimizing write access extends the lifetime of your memory stick ! 24115121Sfrankho */ 24125121Sfrankho if (!vfs_optionisset(fsp->pcfs_vfs, MNTOPT_ATIME, NULL) && 24135121Sfrankho (isremoveable || ishotpluggable | isfloppy)) { 24145121Sfrankho fsp->pcfs_flags |= PCFS_NOATIME; 24155121Sfrankho } 24165121Sfrankho 24175121Sfrankho (void) ldi_close(lh, FREAD, cr); 24185121Sfrankho out: 24195121Sfrankho if (fsp->pcfs_secsize == 0) { 24205121Sfrankho PC_DPRINTF3(1, "!pcfs: media block size autodetection " 24215121Sfrankho "device (%x.%x) failed, no user-provided fallback. " 24225121Sfrankho "Using %d bytes.\n", 24235121Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 24245121Sfrankho DEV_BSIZE); 24255121Sfrankho fsp->pcfs_secsize = DEV_BSIZE; 24265121Sfrankho fsp->pcfs_sdshift = 0; 24275121Sfrankho } 24285121Sfrankho ASSERT(fsp->pcfs_secsize % DEV_BSIZE == 0); 24295121Sfrankho ASSERT(VALID_SECSIZE(fsp->pcfs_secsize)); 24305121Sfrankho } 24315121Sfrankho 24325121Sfrankho /* 24335121Sfrankho * Get the FAT type for the DOS medium. 24345121Sfrankho * 24355121Sfrankho * ------------------------- 24365121Sfrankho * According to Microsoft: 24375121Sfrankho * The FAT type one of FAT12, FAT16, or FAT32 is determined by the 24385121Sfrankho * count of clusters on the volume and nothing else. 24395121Sfrankho * ------------------------- 24405121Sfrankho * 24415121Sfrankho */ 24425121Sfrankho static int 24435121Sfrankho pc_getfattype(struct pcfs *fsp) 24445121Sfrankho { 24455121Sfrankho int error = 0; 24465121Sfrankho buf_t *bp = NULL; 24475121Sfrankho struct vnode *devvp = fsp->pcfs_devvp; 24485121Sfrankho dev_t dev = devvp->v_rdev; 24495121Sfrankho 24505121Sfrankho /* 24515121Sfrankho * Detect the native block size of the medium, and attempt to 24525121Sfrankho * detect whether the medium is removeable. 24535121Sfrankho * We do treat removeable media (floppies, PCMCIA memory cards, 24545121Sfrankho * USB and FireWire disks) differently wrt. to the frequency 24555121Sfrankho * and synchronicity of FAT updates. 24565121Sfrankho * We need to know the media block size in order to be able to 24575121Sfrankho * parse the partition table. 24585121Sfrankho */ 24595121Sfrankho pcfs_device_getinfo(fsp); 24605121Sfrankho 24615121Sfrankho /* 24625121Sfrankho * Unpartitioned media (floppies and some removeable devices) 24635121Sfrankho * don't have a partition table, the FAT BPB is at disk block 0. 24645121Sfrankho * Start out by reading block 0. 24655121Sfrankho */ 24665121Sfrankho fsp->pcfs_dosstart = 0; 24675121Sfrankho bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), fsp->pcfs_secsize); 24685121Sfrankho 24695121Sfrankho if (error = geterror(bp)) 24705121Sfrankho goto out; 24715121Sfrankho 24725121Sfrankho /* 24735121Sfrankho * If a logical drive number is requested, parse the partition table 24745121Sfrankho * and attempt to locate it. Otherwise, proceed immediately to the 24755121Sfrankho * BPB check. findTheDrive(), if successful, returns the disk block 24765121Sfrankho * number where the requested partition starts in "startsec". 24775121Sfrankho */ 24785121Sfrankho if (fsp->pcfs_ldrive != 0) { 24795121Sfrankho PC_DPRINTF3(5, "!pcfs: pc_getfattype: using FDISK table on " 24805121Sfrankho "device (%x,%x):%d to find BPB\n", 24815121Sfrankho getmajor(dev), getminor(dev), fsp->pcfs_ldrive); 24825121Sfrankho 24835121Sfrankho if (error = findTheDrive(fsp, &bp)) 24845121Sfrankho goto out; 24855121Sfrankho 24865121Sfrankho ASSERT(fsp->pcfs_dosstart != 0); 24875121Sfrankho 24885121Sfrankho brelse(bp); 24895121Sfrankho bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), 24905121Sfrankho fsp->pcfs_secsize); 24915121Sfrankho if (error = geterror(bp)) 24925121Sfrankho goto out; 24935121Sfrankho } 24945121Sfrankho 24955121Sfrankho /* 24965121Sfrankho * Validate the BPB and fill in the instance structure. 24975121Sfrankho */ 24985121Sfrankho if (!parseBPB(fsp, (uchar_t *)bp->b_un.b_addr, NULL)) { 24995121Sfrankho PC_DPRINTF4(1, "!pcfs: pc_getfattype: No FAT BPB on " 25005121Sfrankho "device (%x.%x):%d, disk LBA %u\n", 25015121Sfrankho getmajor(dev), getminor(dev), fsp->pcfs_ldrive, 25025121Sfrankho (uint_t)pc_dbdaddr(fsp, fsp->pcfs_dosstart)); 25035121Sfrankho error = EINVAL; 25045121Sfrankho goto out; 25055121Sfrankho } 25065121Sfrankho 25075121Sfrankho ASSERT(fsp->pcfs_fattype != FAT_UNKNOWN); 25085121Sfrankho 25095121Sfrankho out: 25105121Sfrankho /* 25115121Sfrankho * Release the buffer used 25125121Sfrankho */ 25135121Sfrankho if (bp != NULL) 25145121Sfrankho brelse(bp); 25155121Sfrankho return (error); 25165121Sfrankho } 25175121Sfrankho 25185121Sfrankho 25195121Sfrankho /* 25205121Sfrankho * Get the file allocation table. 25215121Sfrankho * If there is an old FAT, invalidate it. 25225121Sfrankho */ 25235121Sfrankho int 25245121Sfrankho pc_getfat(struct pcfs *fsp) 25255121Sfrankho { 25265121Sfrankho struct buf *bp = NULL; 25275121Sfrankho uchar_t *fatp = NULL; 25285121Sfrankho uchar_t *fat_changemap = NULL; 25295121Sfrankho int error; 25305121Sfrankho int fat_changemapsize; 25315121Sfrankho int flags = 0; 25325121Sfrankho int nfat; 25335121Sfrankho int altfat_mustmatch = 0; 25345121Sfrankho int fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize; 25355121Sfrankho 25365121Sfrankho if (fsp->pcfs_fatp) { 25375121Sfrankho /* 25385121Sfrankho * There is a FAT in core. 25395121Sfrankho * If there are open file pcnodes or we have modified it or 25405121Sfrankho * it hasn't timed out yet use the in core FAT. 25415121Sfrankho * Otherwise invalidate it and get a new one 25425121Sfrankho */ 25435121Sfrankho #ifdef notdef 25445121Sfrankho if (fsp->pcfs_frefs || 25455121Sfrankho (fsp->pcfs_flags & PCFS_FATMOD) || 25465121Sfrankho (gethrestime_sec() < fsp->pcfs_fattime)) { 25475121Sfrankho return (0); 25485121Sfrankho } else { 25495121Sfrankho mutex_enter(&pcfslock); 25505121Sfrankho pc_invalfat(fsp); 25515121Sfrankho mutex_exit(&pcfslock); 25525121Sfrankho } 25535121Sfrankho #endif /* notdef */ 25545121Sfrankho return (0); 25555121Sfrankho } 25565121Sfrankho 25575121Sfrankho /* 25585121Sfrankho * Get FAT and check it for validity 25595121Sfrankho */ 25605121Sfrankho fatp = kmem_alloc(fatsize, KM_SLEEP); 25615121Sfrankho error = pc_readfat(fsp, fatp); 25625121Sfrankho if (error) { 25635121Sfrankho flags = B_ERROR; 25645121Sfrankho goto out; 25655121Sfrankho } 25665121Sfrankho fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1; 25675121Sfrankho fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP); 25685121Sfrankho fsp->pcfs_fatp = fatp; 25695121Sfrankho fsp->pcfs_fat_changemapsize = fat_changemapsize; 25705121Sfrankho fsp->pcfs_fat_changemap = fat_changemap; 25715121Sfrankho 25725121Sfrankho /* 25735121Sfrankho * The only definite signature check is that the 25745121Sfrankho * media descriptor byte should match the first byte 25755121Sfrankho * of the FAT block. 25765121Sfrankho */ 25775121Sfrankho if (fatp[0] != fsp->pcfs_mediadesc) { 25785121Sfrankho cmn_err(CE_NOTE, "!pcfs: FAT signature mismatch, " 25795121Sfrankho "media descriptor %x, FAT[0] lowbyte %x\n", 25805121Sfrankho (uint32_t)fsp->pcfs_mediadesc, (uint32_t)fatp[0]); 25815121Sfrankho cmn_err(CE_NOTE, "!pcfs: Enforcing alternate FAT validation\n"); 25825121Sfrankho altfat_mustmatch = 1; 25835121Sfrankho } 25845121Sfrankho 25855121Sfrankho /* 25865121Sfrankho * Get alternate FATs and check for consistency 25875121Sfrankho * This is an inlined version of pc_readfat(). 25885121Sfrankho * Since we're only comparing FAT and alternate FAT, 25895121Sfrankho * there's no reason to let pc_readfat() copy data out 25905121Sfrankho * of the buf. Instead, compare in-situ, one cluster 25915121Sfrankho * at a time. 25925121Sfrankho */ 25935121Sfrankho for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) { 25945121Sfrankho size_t startsec; 25955121Sfrankho size_t off; 25965121Sfrankho 25975121Sfrankho startsec = pc_dbdaddr(fsp, 25985121Sfrankho fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec); 25995121Sfrankho 26005121Sfrankho for (off = 0; off < fatsize; off += fsp->pcfs_clsize) { 26015121Sfrankho daddr_t fatblk = startsec + pc_dbdaddr(fsp, 26025121Sfrankho pc_cltodb(fsp, pc_lblkno(fsp, off))); 26035121Sfrankho 26045121Sfrankho bp = bread(fsp->pcfs_xdev, fatblk, 26055121Sfrankho MIN(fsp->pcfs_clsize, fatsize - off)); 26065121Sfrankho if (bp->b_flags & (B_ERROR | B_STALE)) { 26075121Sfrankho cmn_err(CE_NOTE, 26085121Sfrankho "!pcfs: alternate FAT #%d (start LBA %p)" 26095121Sfrankho " read error at offset %ld on device" 26105121Sfrankho " (%x.%x):%d", 26115121Sfrankho nfat, (void *)(uintptr_t)startsec, off, 26125121Sfrankho getmajor(fsp->pcfs_xdev), 26135121Sfrankho getminor(fsp->pcfs_xdev), 26145121Sfrankho fsp->pcfs_ldrive); 26155121Sfrankho flags = B_ERROR; 26165121Sfrankho error = EIO; 26175121Sfrankho goto out; 26185121Sfrankho } 26195121Sfrankho bp->b_flags |= B_STALE | B_AGE; 26205121Sfrankho if (bcmp(bp->b_un.b_addr, fatp + off, 26215121Sfrankho MIN(fsp->pcfs_clsize, fatsize - off))) { 26225121Sfrankho cmn_err(CE_NOTE, 26235121Sfrankho "!pcfs: alternate FAT #%d (start LBA %p)" 26245121Sfrankho " corrupted at offset %ld on device" 26255121Sfrankho " (%x.%x):%d", 26265121Sfrankho nfat, (void *)(uintptr_t)startsec, off, 26275121Sfrankho getmajor(fsp->pcfs_xdev), 26285121Sfrankho getminor(fsp->pcfs_xdev), 26295121Sfrankho fsp->pcfs_ldrive); 26305121Sfrankho if (altfat_mustmatch) { 26315121Sfrankho flags = B_ERROR; 26325121Sfrankho error = EIO; 26335121Sfrankho goto out; 26345121Sfrankho } 26355121Sfrankho } 26365121Sfrankho brelse(bp); 26375121Sfrankho bp = NULL; /* prevent double release */ 26385121Sfrankho } 26395121Sfrankho } 26405121Sfrankho 26415121Sfrankho fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT; 26425121Sfrankho fsp->pcfs_fatjustread = 1; 26435121Sfrankho 26445121Sfrankho /* 26455121Sfrankho * Retrieve FAT32 fsinfo sector. 26465121Sfrankho * A failure to read this is not fatal to accessing the volume. 26475121Sfrankho * It simply means operations that count or search free blocks 26485121Sfrankho * will have to do a full FAT walk, vs. a possibly quicker lookup 26495121Sfrankho * of the summary information. 26505121Sfrankho * Hence, we log a message but return success overall after this point. 26515121Sfrankho */ 26525121Sfrankho if (IS_FAT32(fsp) && (fsp->pcfs_flags & PCFS_FSINFO_OK)) { 26535121Sfrankho struct fat_od_fsi *fsinfo_disk; 26545121Sfrankho 26555121Sfrankho bp = bread(fsp->pcfs_xdev, 26565121Sfrankho pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize); 26575121Sfrankho fsinfo_disk = (struct fat_od_fsi *)bp->b_un.b_addr; 26585121Sfrankho if (bp->b_flags & (B_ERROR | B_STALE) || 26595121Sfrankho !FSISIG_OK(fsinfo_disk)) { 26605121Sfrankho cmn_err(CE_NOTE, 26615121Sfrankho "!pcfs: error reading fat32 fsinfo from " 26625121Sfrankho "device (%x.%x):%d, block %lld", 26635121Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 26645121Sfrankho fsp->pcfs_ldrive, 26655121Sfrankho (long long)pc_dbdaddr(fsp, fsp->pcfs_fsistart)); 26665121Sfrankho fsp->pcfs_flags &= ~PCFS_FSINFO_OK; 26675121Sfrankho fsp->pcfs_fsinfo.fs_free_clusters = FSINFO_UNKNOWN; 26685121Sfrankho fsp->pcfs_fsinfo.fs_next_free = FSINFO_UNKNOWN; 26695121Sfrankho } else { 26705121Sfrankho bp->b_flags |= B_STALE | B_AGE; 26715121Sfrankho fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr); 26725121Sfrankho fsp->pcfs_fsinfo.fs_free_clusters = 26735121Sfrankho LE_32(fsinfo_disk->fsi_incore.fs_free_clusters); 26745121Sfrankho fsp->pcfs_fsinfo.fs_next_free = 26755121Sfrankho LE_32(fsinfo_disk->fsi_incore.fs_next_free); 26765121Sfrankho } 26775121Sfrankho brelse(bp); 26785121Sfrankho bp = NULL; 26795121Sfrankho } 26805121Sfrankho 26815121Sfrankho if (pc_validcl(fsp, (pc_cluster32_t)fsp->pcfs_fsinfo.fs_next_free)) 26825121Sfrankho fsp->pcfs_nxfrecls = fsp->pcfs_fsinfo.fs_next_free; 26835121Sfrankho else 26845121Sfrankho fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER; 26855121Sfrankho 26865121Sfrankho return (0); 26875121Sfrankho 26885121Sfrankho out: 26895121Sfrankho cmn_err(CE_NOTE, "!pcfs: illegal disk format"); 26905121Sfrankho if (bp) 26915121Sfrankho brelse(bp); 26925121Sfrankho if (fatp) 26935121Sfrankho kmem_free(fatp, fatsize); 26945121Sfrankho if (fat_changemap) 26955121Sfrankho kmem_free(fat_changemap, fat_changemapsize); 26965121Sfrankho 26975121Sfrankho if (flags) { 26985121Sfrankho pc_mark_irrecov(fsp); 26995121Sfrankho } 27005121Sfrankho return (error); 27015121Sfrankho } 2702