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 */ 217563SPrasad.Singamsetty@Sun.COM 220Sstevel@tonic-gate /* 23*11215Sgdamore@opensolaris.org * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #include <sys/param.h> 280Sstevel@tonic-gate #include <sys/systm.h> 290Sstevel@tonic-gate #include <sys/kmem.h> 300Sstevel@tonic-gate #include <sys/user.h> 310Sstevel@tonic-gate #include <sys/proc.h> 320Sstevel@tonic-gate #include <sys/cred.h> 330Sstevel@tonic-gate #include <sys/disp.h> 340Sstevel@tonic-gate #include <sys/buf.h> 350Sstevel@tonic-gate #include <sys/vfs.h> 363898Srsb #include <sys/vfs_opreg.h> 370Sstevel@tonic-gate #include <sys/vnode.h> 380Sstevel@tonic-gate #include <sys/fdio.h> 390Sstevel@tonic-gate #include <sys/file.h> 400Sstevel@tonic-gate #include <sys/uio.h> 410Sstevel@tonic-gate #include <sys/conf.h> 420Sstevel@tonic-gate #include <sys/statvfs.h> 430Sstevel@tonic-gate #include <sys/mount.h> 440Sstevel@tonic-gate #include <sys/pathname.h> 450Sstevel@tonic-gate #include <sys/cmn_err.h> 460Sstevel@tonic-gate #include <sys/debug.h> 470Sstevel@tonic-gate #include <sys/sysmacros.h> 480Sstevel@tonic-gate #include <sys/conf.h> 490Sstevel@tonic-gate #include <sys/mkdev.h> 500Sstevel@tonic-gate #include <sys/swap.h> 510Sstevel@tonic-gate #include <sys/sunddi.h> 520Sstevel@tonic-gate #include <sys/sunldi.h> 530Sstevel@tonic-gate #include <sys/dktp/fdisk.h> 540Sstevel@tonic-gate #include <sys/fs/pc_label.h> 550Sstevel@tonic-gate #include <sys/fs/pc_fs.h> 560Sstevel@tonic-gate #include <sys/fs/pc_dir.h> 570Sstevel@tonic-gate #include <sys/fs/pc_node.h> 580Sstevel@tonic-gate #include <fs/fs_subr.h> 590Sstevel@tonic-gate #include <sys/modctl.h> 600Sstevel@tonic-gate #include <sys/dkio.h> 610Sstevel@tonic-gate #include <sys/open.h> 620Sstevel@tonic-gate #include <sys/mntent.h> 630Sstevel@tonic-gate #include <sys/policy.h> 642720Sfrankho #include <sys/atomic.h> 655121Sfrankho #include <sys/sdt.h> 660Sstevel@tonic-gate 670Sstevel@tonic-gate /* 680Sstevel@tonic-gate * The majority of PC media use a 512 sector size, but 690Sstevel@tonic-gate * occasionally you will run across a 1k sector size. 700Sstevel@tonic-gate * For media with a 1k sector size, fd_strategy() requires 710Sstevel@tonic-gate * the I/O size to be a 1k multiple; so when the sector size 720Sstevel@tonic-gate * is not yet known, always read 1k. 730Sstevel@tonic-gate */ 740Sstevel@tonic-gate #define PC_SAFESECSIZE (PC_SECSIZE * 2) 750Sstevel@tonic-gate 76201Sjmcp static int pcfs_pseudo_floppy(dev_t); 770Sstevel@tonic-gate 780Sstevel@tonic-gate static int pcfsinit(int, char *); 790Sstevel@tonic-gate static int pcfs_mount(struct vfs *, struct vnode *, struct mounta *, 800Sstevel@tonic-gate struct cred *); 810Sstevel@tonic-gate static int pcfs_unmount(struct vfs *, int, struct cred *); 820Sstevel@tonic-gate static int pcfs_root(struct vfs *, struct vnode **); 830Sstevel@tonic-gate static int pcfs_statvfs(struct vfs *, struct statvfs64 *); 840Sstevel@tonic-gate static int pc_syncfsnodes(struct pcfs *); 850Sstevel@tonic-gate static int pcfs_sync(struct vfs *, short, struct cred *); 860Sstevel@tonic-gate static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp); 872720Sfrankho static void pcfs_freevfs(vfs_t *vfsp); 880Sstevel@tonic-gate 895121Sfrankho static int pc_readfat(struct pcfs *fsp, uchar_t *fatp); 900Sstevel@tonic-gate static int pc_writefat(struct pcfs *fsp, daddr_t start); 910Sstevel@tonic-gate 925121Sfrankho static int pc_getfattype(struct pcfs *fsp); 936645Sgd78059 static void pcfs_parse_mntopts(struct pcfs *fsp); 945121Sfrankho 955121Sfrankho 960Sstevel@tonic-gate /* 970Sstevel@tonic-gate * pcfs mount options table 980Sstevel@tonic-gate */ 990Sstevel@tonic-gate 1002720Sfrankho static char *nohidden_cancel[] = { MNTOPT_PCFS_HIDDEN, NULL }; 1012720Sfrankho static char *hidden_cancel[] = { MNTOPT_PCFS_NOHIDDEN, NULL }; 1022720Sfrankho static char *nofoldcase_cancel[] = { MNTOPT_PCFS_FOLDCASE, NULL }; 1032720Sfrankho static char *foldcase_cancel[] = { MNTOPT_PCFS_NOFOLDCASE, NULL }; 1042720Sfrankho static char *clamptime_cancel[] = { MNTOPT_PCFS_NOCLAMPTIME, NULL }; 1052720Sfrankho static char *noclamptime_cancel[] = { MNTOPT_PCFS_CLAMPTIME, NULL }; 1065121Sfrankho static char *atime_cancel[] = { MNTOPT_NOATIME, NULL }; 1075121Sfrankho static char *noatime_cancel[] = { MNTOPT_ATIME, NULL }; 1080Sstevel@tonic-gate 1090Sstevel@tonic-gate static mntopt_t mntopts[] = { 1100Sstevel@tonic-gate /* 1112720Sfrankho * option name cancel option default arg flags opt data 1120Sstevel@tonic-gate */ 1132720Sfrankho { MNTOPT_PCFS_NOHIDDEN, nohidden_cancel, NULL, 0, NULL }, 1142720Sfrankho { MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, MO_DEFAULT, NULL }, 1152720Sfrankho { MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, NULL }, 1162720Sfrankho { MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0, NULL }, 1172720Sfrankho { MNTOPT_PCFS_CLAMPTIME, clamptime_cancel, NULL, MO_DEFAULT, NULL }, 1185121Sfrankho { MNTOPT_PCFS_NOCLAMPTIME, noclamptime_cancel, NULL, NULL, NULL }, 1195121Sfrankho { MNTOPT_NOATIME, noatime_cancel, NULL, NULL, NULL }, 1205121Sfrankho { MNTOPT_ATIME, atime_cancel, NULL, NULL, NULL }, 1215121Sfrankho { MNTOPT_PCFS_TIMEZONE, NULL, "+0", MO_DEFAULT | MO_HASVALUE, NULL }, 1225121Sfrankho { MNTOPT_PCFS_SECSIZE, NULL, NULL, MO_HASVALUE, NULL } 1230Sstevel@tonic-gate }; 1240Sstevel@tonic-gate 1250Sstevel@tonic-gate static mntopts_t pcfs_mntopts = { 1260Sstevel@tonic-gate sizeof (mntopts) / sizeof (mntopt_t), 1270Sstevel@tonic-gate mntopts 1280Sstevel@tonic-gate }; 1290Sstevel@tonic-gate 1300Sstevel@tonic-gate int pcfsdebuglevel = 0; 1310Sstevel@tonic-gate 1320Sstevel@tonic-gate /* 1330Sstevel@tonic-gate * pcfslock: protects the list of mounted pc filesystems "pc_mounttab. 1340Sstevel@tonic-gate * pcfs_lock: (inside per filesystem structure "pcfs") 1350Sstevel@tonic-gate * per filesystem lock. Most of the vfsops and vnodeops are 1360Sstevel@tonic-gate * protected by this lock. 1370Sstevel@tonic-gate * pcnodes_lock: protects the pcnode hash table "pcdhead", "pcfhead". 1380Sstevel@tonic-gate * 1390Sstevel@tonic-gate * Lock hierarchy: pcfslock > pcfs_lock > pcnodes_lock 1402720Sfrankho * 1412720Sfrankho * pcfs_mountcount: used to prevent module unloads while there is still 1422720Sfrankho * pcfs state from a former mount hanging around. With 1432720Sfrankho * forced umount support, the filesystem module must not 1442720Sfrankho * be allowed to go away before the last VFS_FREEVFS() 1452720Sfrankho * call has been made. 1462720Sfrankho * Since this is just an atomic counter, there's no need 1472720Sfrankho * for locking. 1480Sstevel@tonic-gate */ 1490Sstevel@tonic-gate kmutex_t pcfslock; 1502720Sfrankho krwlock_t pcnodes_lock; 1512720Sfrankho uint32_t pcfs_mountcount; 1520Sstevel@tonic-gate 1530Sstevel@tonic-gate static int pcfstype; 1540Sstevel@tonic-gate 1550Sstevel@tonic-gate static vfsdef_t vfw = { 1560Sstevel@tonic-gate VFSDEF_VERSION, 1570Sstevel@tonic-gate "pcfs", 1580Sstevel@tonic-gate pcfsinit, 1596855Sjohnlev VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS|VSW_CANLOFI, 1600Sstevel@tonic-gate &pcfs_mntopts 1610Sstevel@tonic-gate }; 1620Sstevel@tonic-gate 1630Sstevel@tonic-gate extern struct mod_ops mod_fsops; 1640Sstevel@tonic-gate 1650Sstevel@tonic-gate static struct modlfs modlfs = { 1660Sstevel@tonic-gate &mod_fsops, 1676644Sgd78059 "PC filesystem", 1680Sstevel@tonic-gate &vfw 1690Sstevel@tonic-gate }; 1700Sstevel@tonic-gate 1710Sstevel@tonic-gate static struct modlinkage modlinkage = { 1720Sstevel@tonic-gate MODREV_1, 1730Sstevel@tonic-gate &modlfs, 1740Sstevel@tonic-gate NULL 1750Sstevel@tonic-gate }; 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate int 1780Sstevel@tonic-gate _init(void) 1790Sstevel@tonic-gate { 1800Sstevel@tonic-gate int error; 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate #if !defined(lint) 1830Sstevel@tonic-gate /* make sure the on-disk structures are sane */ 1840Sstevel@tonic-gate ASSERT(sizeof (struct pcdir) == 32); 1850Sstevel@tonic-gate ASSERT(sizeof (struct pcdir_lfn) == 32); 1860Sstevel@tonic-gate #endif 1870Sstevel@tonic-gate mutex_init(&pcfslock, NULL, MUTEX_DEFAULT, NULL); 1880Sstevel@tonic-gate rw_init(&pcnodes_lock, NULL, RW_DEFAULT, NULL); 1890Sstevel@tonic-gate error = mod_install(&modlinkage); 1900Sstevel@tonic-gate if (error) { 1910Sstevel@tonic-gate mutex_destroy(&pcfslock); 1920Sstevel@tonic-gate rw_destroy(&pcnodes_lock); 1930Sstevel@tonic-gate } 1940Sstevel@tonic-gate return (error); 1950Sstevel@tonic-gate } 1960Sstevel@tonic-gate 1970Sstevel@tonic-gate int 1980Sstevel@tonic-gate _fini(void) 1990Sstevel@tonic-gate { 2000Sstevel@tonic-gate int error; 2010Sstevel@tonic-gate 2022720Sfrankho /* 2032720Sfrankho * If a forcedly unmounted instance is still hanging around, 2042720Sfrankho * we cannot allow the module to be unloaded because that would 2052720Sfrankho * cause panics once the VFS framework decides it's time to call 2062720Sfrankho * into VFS_FREEVFS(). 2072720Sfrankho */ 2082720Sfrankho if (pcfs_mountcount) 2092720Sfrankho return (EBUSY); 2102720Sfrankho 2110Sstevel@tonic-gate error = mod_remove(&modlinkage); 2120Sstevel@tonic-gate if (error) 2130Sstevel@tonic-gate return (error); 2140Sstevel@tonic-gate mutex_destroy(&pcfslock); 2150Sstevel@tonic-gate rw_destroy(&pcnodes_lock); 2160Sstevel@tonic-gate /* 2170Sstevel@tonic-gate * Tear down the operations vectors 2180Sstevel@tonic-gate */ 2190Sstevel@tonic-gate (void) vfs_freevfsops_by_type(pcfstype); 2200Sstevel@tonic-gate vn_freevnodeops(pcfs_fvnodeops); 2210Sstevel@tonic-gate vn_freevnodeops(pcfs_dvnodeops); 2220Sstevel@tonic-gate return (0); 2230Sstevel@tonic-gate } 2240Sstevel@tonic-gate 2250Sstevel@tonic-gate int 2260Sstevel@tonic-gate _info(struct modinfo *modinfop) 2270Sstevel@tonic-gate { 2280Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 2290Sstevel@tonic-gate } 2300Sstevel@tonic-gate 2310Sstevel@tonic-gate /* ARGSUSED1 */ 2320Sstevel@tonic-gate static int 2330Sstevel@tonic-gate pcfsinit(int fstype, char *name) 2340Sstevel@tonic-gate { 2350Sstevel@tonic-gate static const fs_operation_def_t pcfs_vfsops_template[] = { 2363898Srsb VFSNAME_MOUNT, { .vfs_mount = pcfs_mount }, 2373898Srsb VFSNAME_UNMOUNT, { .vfs_unmount = pcfs_unmount }, 2383898Srsb VFSNAME_ROOT, { .vfs_root = pcfs_root }, 2393898Srsb VFSNAME_STATVFS, { .vfs_statvfs = pcfs_statvfs }, 2403898Srsb VFSNAME_SYNC, { .vfs_sync = pcfs_sync }, 2413898Srsb VFSNAME_VGET, { .vfs_vget = pcfs_vget }, 2423898Srsb VFSNAME_FREEVFS, { .vfs_freevfs = pcfs_freevfs }, 2433898Srsb NULL, NULL 2440Sstevel@tonic-gate }; 2450Sstevel@tonic-gate int error; 2460Sstevel@tonic-gate 2470Sstevel@tonic-gate error = vfs_setfsops(fstype, pcfs_vfsops_template, NULL); 2480Sstevel@tonic-gate if (error != 0) { 2490Sstevel@tonic-gate cmn_err(CE_WARN, "pcfsinit: bad vfs ops template"); 2500Sstevel@tonic-gate return (error); 2510Sstevel@tonic-gate } 2520Sstevel@tonic-gate 2530Sstevel@tonic-gate error = vn_make_ops("pcfs", pcfs_fvnodeops_template, &pcfs_fvnodeops); 2540Sstevel@tonic-gate if (error != 0) { 2550Sstevel@tonic-gate (void) vfs_freevfsops_by_type(fstype); 2560Sstevel@tonic-gate cmn_err(CE_WARN, "pcfsinit: bad file vnode ops template"); 2570Sstevel@tonic-gate return (error); 2580Sstevel@tonic-gate } 2590Sstevel@tonic-gate 2600Sstevel@tonic-gate error = vn_make_ops("pcfsd", pcfs_dvnodeops_template, &pcfs_dvnodeops); 2610Sstevel@tonic-gate if (error != 0) { 2620Sstevel@tonic-gate (void) vfs_freevfsops_by_type(fstype); 2630Sstevel@tonic-gate vn_freevnodeops(pcfs_fvnodeops); 2640Sstevel@tonic-gate cmn_err(CE_WARN, "pcfsinit: bad dir vnode ops template"); 2650Sstevel@tonic-gate return (error); 2660Sstevel@tonic-gate } 2670Sstevel@tonic-gate 2680Sstevel@tonic-gate pcfstype = fstype; 2690Sstevel@tonic-gate (void) pc_init(); 2702720Sfrankho pcfs_mountcount = 0; 2710Sstevel@tonic-gate return (0); 2720Sstevel@tonic-gate } 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate static struct pcfs *pc_mounttab = NULL; 2750Sstevel@tonic-gate 2760Sstevel@tonic-gate extern struct pcfs_args pc_tz; 2770Sstevel@tonic-gate 2780Sstevel@tonic-gate /* 2790Sstevel@tonic-gate * Define some special logical drives we use internal to this file. 2800Sstevel@tonic-gate */ 2810Sstevel@tonic-gate #define BOOT_PARTITION_DRIVE 99 2820Sstevel@tonic-gate #define PRIMARY_DOS_DRIVE 1 2835121Sfrankho #define UNPARTITIONED_DRIVE 0 2840Sstevel@tonic-gate 2855121Sfrankho static int 2865121Sfrankho pcfs_device_identify( 2875121Sfrankho struct vfs *vfsp, 2885121Sfrankho struct mounta *uap, 2895121Sfrankho struct cred *cr, 2905121Sfrankho int *dos_ldrive, 2915121Sfrankho dev_t *xdev) 2925121Sfrankho { 2935121Sfrankho struct pathname special; 2945121Sfrankho char *c; 2956734Sjohnlev struct vnode *svp = NULL; 2966734Sjohnlev struct vnode *lvp = NULL; 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 = 3106734Sjohnlev lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &svp)) { 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 366*11215Sgdamore@opensolaris.org * whether the physical device is a floppy. 3672972Sfrankho */ 3682972Sfrankho *c = tolower(*c); 3695121Sfrankho if (*c == 'a' || *c == 'b') { 3705121Sfrankho *dos_ldrive = UNPARTITIONED_DRIVE; 3715121Sfrankho } else if (*c < 'c' || *c > 'z') { 3725121Sfrankho error = ENXIO; 3732972Sfrankho goto devlookup_done; 3745121Sfrankho } else { 3755121Sfrankho *dos_ldrive = 1 + *c - 'c'; 3762972Sfrankho } 3772972Sfrankho } else { 3782972Sfrankho /* 3792972Sfrankho * Can't parse this - pass through previous error. 3802972Sfrankho */ 3812972Sfrankho goto devlookup_done; 3820Sstevel@tonic-gate } 3830Sstevel@tonic-gate 3842972Sfrankho 3852972Sfrankho error = lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, 3866734Sjohnlev NULLVPP, &svp); 3875121Sfrankho } else { 3885121Sfrankho *dos_ldrive = UNPARTITIONED_DRIVE; 3890Sstevel@tonic-gate } 3902972Sfrankho devlookup_done: 3910Sstevel@tonic-gate pn_free(&special); 3922972Sfrankho if (error) 3932972Sfrankho return (error); 3942972Sfrankho 3955121Sfrankho ASSERT(*dos_ldrive >= UNPARTITIONED_DRIVE); 3965121Sfrankho 3970Sstevel@tonic-gate /* 3980Sstevel@tonic-gate * Verify caller's permission to open the device special file. 3990Sstevel@tonic-gate */ 4000Sstevel@tonic-gate if ((vfsp->vfs_flag & VFS_RDONLY) != 0 || 4010Sstevel@tonic-gate ((uap->flags & MS_RDONLY) != 0)) { 4020Sstevel@tonic-gate oflag = FREAD; 4030Sstevel@tonic-gate aflag = VREAD; 4040Sstevel@tonic-gate } else { 4050Sstevel@tonic-gate oflag = FREAD | FWRITE; 4060Sstevel@tonic-gate aflag = VREAD | VWRITE; 4070Sstevel@tonic-gate } 4085121Sfrankho 4096734Sjohnlev error = vfs_get_lofi(vfsp, &lvp); 4105121Sfrankho 4116734Sjohnlev if (error > 0) { 4126734Sjohnlev if (error == ENOENT) 4136734Sjohnlev error = ENODEV; 4146734Sjohnlev goto out; 4156734Sjohnlev } else if (error == 0) { 4166734Sjohnlev *xdev = lvp->v_rdev; 4176734Sjohnlev } else { 4186734Sjohnlev *xdev = svp->v_rdev; 4196734Sjohnlev 4208424Sjohn.levon@sun.com if (svp->v_type != VBLK) { 4216734Sjohnlev error = ENOTBLK; 4228424Sjohn.levon@sun.com goto out; 4238424Sjohn.levon@sun.com } 4246734Sjohnlev 4256734Sjohnlev if ((error = secpolicy_spec_open(cr, svp, oflag)) != 0) 4266734Sjohnlev goto out; 4270Sstevel@tonic-gate } 4280Sstevel@tonic-gate 4296734Sjohnlev if (getmajor(*xdev) >= devcnt) { 4306734Sjohnlev error = ENXIO; 4316734Sjohnlev goto out; 4326734Sjohnlev } 4336734Sjohnlev 4346734Sjohnlev if ((error = VOP_ACCESS(svp, aflag, 0, cr, NULL)) != 0) 4356734Sjohnlev goto out; 4366734Sjohnlev 4376734Sjohnlev out: 4386734Sjohnlev if (svp != NULL) 4396734Sjohnlev VN_RELE(svp); 4406734Sjohnlev if (lvp != NULL) 4416734Sjohnlev VN_RELE(lvp); 4426734Sjohnlev return (error); 4435121Sfrankho } 4445121Sfrankho 4455121Sfrankho static int 4465121Sfrankho pcfs_device_ismounted( 4475121Sfrankho struct vfs *vfsp, 4485121Sfrankho int dos_ldrive, 4495121Sfrankho dev_t xdev, 4505121Sfrankho int *remounting, 4515121Sfrankho dev_t *pseudodev) 4525121Sfrankho { 4535121Sfrankho struct pcfs *fsp; 4545121Sfrankho int remount = *remounting; 4555121Sfrankho 4560Sstevel@tonic-gate /* 4572972Sfrankho * Ensure that this logical drive isn't already mounted, unless 4582972Sfrankho * this is a REMOUNT request. 4592972Sfrankho * Note: The framework will perform this check if the "...:c" 4602972Sfrankho * PCFS-style "logical drive" syntax has not been used and an 4612972Sfrankho * actually existing physical device is backing this filesystem. 4625121Sfrankho * Once all block device drivers support PC-style partitioning, 4635121Sfrankho * this codeblock can be dropped. 4640Sstevel@tonic-gate */ 4655121Sfrankho *pseudodev = xdev; 4665121Sfrankho 4670Sstevel@tonic-gate if (dos_ldrive) { 4680Sstevel@tonic-gate mutex_enter(&pcfslock); 4690Sstevel@tonic-gate for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt) 4700Sstevel@tonic-gate if (fsp->pcfs_xdev == xdev && 4715121Sfrankho fsp->pcfs_ldrive == dos_ldrive) { 4720Sstevel@tonic-gate mutex_exit(&pcfslock); 4735121Sfrankho if (remount) { 4740Sstevel@tonic-gate return (0); 4750Sstevel@tonic-gate } else { 4760Sstevel@tonic-gate return (EBUSY); 4770Sstevel@tonic-gate } 4780Sstevel@tonic-gate } 4790Sstevel@tonic-gate /* 4800Sstevel@tonic-gate * Assign a unique device number for the vfs 4810Sstevel@tonic-gate * The old way (getudev() + a constantly incrementing 4820Sstevel@tonic-gate * major number) was wrong because it changes vfs_dev 4830Sstevel@tonic-gate * across mounts and reboots, which breaks nfs file handles. 4840Sstevel@tonic-gate * UFS just uses the real dev_t. We can't do that because 4850Sstevel@tonic-gate * of the way pcfs opens fdisk partitons (the :c and :d 4860Sstevel@tonic-gate * partitions are on the same dev_t). Though that _might_ 4870Sstevel@tonic-gate * actually be ok, since the file handle contains an 4880Sstevel@tonic-gate * absolute block number, it's probably better to make them 4890Sstevel@tonic-gate * different. So I think we should retain the original 4900Sstevel@tonic-gate * dev_t, but come up with a different minor number based 4910Sstevel@tonic-gate * on the logical drive that will _always_ come up the same. 4920Sstevel@tonic-gate * For now, we steal the upper 6 bits. 4930Sstevel@tonic-gate */ 4940Sstevel@tonic-gate #ifdef notdef 4950Sstevel@tonic-gate /* what should we do here? */ 4960Sstevel@tonic-gate if (((getminor(xdev) >> 12) & 0x3F) != 0) 4970Sstevel@tonic-gate printf("whoops - upper bits used!\n"); 4980Sstevel@tonic-gate #endif 4995121Sfrankho *pseudodev = makedevice(getmajor(xdev), 5005121Sfrankho ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32); 5015121Sfrankho if (vfs_devmounting(*pseudodev, vfsp)) { 5020Sstevel@tonic-gate mutex_exit(&pcfslock); 5030Sstevel@tonic-gate return (EBUSY); 5040Sstevel@tonic-gate } 5055121Sfrankho if (vfs_devismounted(*pseudodev)) { 5060Sstevel@tonic-gate mutex_exit(&pcfslock); 5075121Sfrankho if (remount) { 5080Sstevel@tonic-gate return (0); 5090Sstevel@tonic-gate } else { 5100Sstevel@tonic-gate return (EBUSY); 5110Sstevel@tonic-gate } 5120Sstevel@tonic-gate } 5130Sstevel@tonic-gate mutex_exit(&pcfslock); 5140Sstevel@tonic-gate } else { 5155121Sfrankho *pseudodev = xdev; 5165121Sfrankho if (vfs_devmounting(*pseudodev, vfsp)) { 5170Sstevel@tonic-gate return (EBUSY); 5180Sstevel@tonic-gate } 5195121Sfrankho if (vfs_devismounted(*pseudodev)) 5205121Sfrankho if (remount) { 5210Sstevel@tonic-gate return (0); 5220Sstevel@tonic-gate } else { 5230Sstevel@tonic-gate return (EBUSY); 5240Sstevel@tonic-gate } 5250Sstevel@tonic-gate } 5260Sstevel@tonic-gate 5275121Sfrankho /* 5285121Sfrankho * This is not a remount. Even if MS_REMOUNT was requested, 5295121Sfrankho * the caller needs to proceed as it would on an ordinary 5305121Sfrankho * mount. 5315121Sfrankho */ 5325121Sfrankho *remounting = 0; 5335121Sfrankho 5345121Sfrankho ASSERT(*pseudodev); 5355121Sfrankho return (0); 5365121Sfrankho } 5375121Sfrankho 5385121Sfrankho /* 5395121Sfrankho * Get the PCFS-specific mount options from the VFS framework. 5405121Sfrankho * For "timezone" and "secsize", we need to parse the number 5415121Sfrankho * ourselves and ensure its validity. 5425121Sfrankho * Note: "secsize" is deliberately undocumented at this time, 5435121Sfrankho * it's a workaround for devices (particularly: lofi image files) 5445121Sfrankho * that don't support the DKIOCGMEDIAINFO ioctl for autodetection. 5455121Sfrankho */ 5465121Sfrankho static void 5476645Sgd78059 pcfs_parse_mntopts(struct pcfs *fsp) 5485121Sfrankho { 5495121Sfrankho char *c; 5505121Sfrankho char *endptr; 5515121Sfrankho long l; 5525121Sfrankho struct vfs *vfsp = fsp->pcfs_vfs; 5535121Sfrankho 5545121Sfrankho ASSERT(fsp->pcfs_secondswest == 0); 5555121Sfrankho ASSERT(fsp->pcfs_secsize == 0); 5565121Sfrankho 5570Sstevel@tonic-gate if (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL)) 5580Sstevel@tonic-gate fsp->pcfs_flags |= PCFS_HIDDEN; 5590Sstevel@tonic-gate if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL)) 5600Sstevel@tonic-gate fsp->pcfs_flags |= PCFS_FOLDCASE; 5612720Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL)) 5622720Sfrankho fsp->pcfs_flags |= PCFS_NOCLAMPTIME; 5635121Sfrankho if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL)) 5645121Sfrankho fsp->pcfs_flags |= PCFS_NOATIME; 5655121Sfrankho 5665121Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_TIMEZONE, &c)) { 5675121Sfrankho if (ddi_strtol(c, &endptr, 10, &l) == 0 && 5685121Sfrankho endptr == c + strlen(c)) { 5695121Sfrankho /* 5705121Sfrankho * A number alright - in the allowed range ? 5715121Sfrankho */ 5725121Sfrankho if (l <= -12*3600 || l >= 12*3600) { 5735121Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of " 5745121Sfrankho "'timezone' mount option - %ld " 5755121Sfrankho "is out of range. Assuming 0.", l); 5765121Sfrankho l = 0; 5775121Sfrankho } 5785121Sfrankho } else { 5795121Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of " 5805121Sfrankho "'timezone' mount option - argument %s " 5815121Sfrankho "is not a valid number. Assuming 0.", c); 5825121Sfrankho l = 0; 5835121Sfrankho } 5845121Sfrankho fsp->pcfs_secondswest = l; 5855121Sfrankho } 5865121Sfrankho 5875121Sfrankho /* 5885121Sfrankho * The "secsize=..." mount option is a workaround for the lack of 5895121Sfrankho * lofi(7d) support for DKIOCGMEDIAINFO. If PCFS wants to parse the 5905121Sfrankho * partition table of a disk image and it has been partitioned with 5915121Sfrankho * sector sizes other than 512 bytes, we'd fail on loopback'ed disk 5925121Sfrankho * images. 5935121Sfrankho * That should really be fixed in lofi ... this is a workaround. 5945121Sfrankho */ 5955121Sfrankho if (vfs_optionisset(vfsp, MNTOPT_PCFS_SECSIZE, &c)) { 5965121Sfrankho if (ddi_strtol(c, &endptr, 10, &l) == 0 && 5975121Sfrankho endptr == c + strlen(c)) { 5985121Sfrankho /* 5995121Sfrankho * A number alright - a valid sector size as well ? 6005121Sfrankho */ 6015121Sfrankho if (!VALID_SECSIZE(l)) { 6025121Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of " 6035121Sfrankho "'secsize' mount option - %ld is " 6045121Sfrankho "unsupported. Autodetecting.", l); 6055121Sfrankho l = 0; 6065121Sfrankho } 6075121Sfrankho } else { 6085121Sfrankho cmn_err(CE_WARN, "!pcfs: invalid use of " 6095121Sfrankho "'secsize' mount option - argument %s " 6105121Sfrankho "is not a valid number. Autodetecting.", c); 6115121Sfrankho l = 0; 6125121Sfrankho } 6135121Sfrankho fsp->pcfs_secsize = l; 6145121Sfrankho fsp->pcfs_sdshift = ddi_ffs(l / DEV_BSIZE) - 1; 6155121Sfrankho } 6165121Sfrankho } 6175121Sfrankho 6185121Sfrankho /* 6195121Sfrankho * vfs operations 6205121Sfrankho */ 6215121Sfrankho 6225121Sfrankho /* 6235121Sfrankho * pcfs_mount - backend for VFS_MOUNT() on PCFS. 6245121Sfrankho */ 6255121Sfrankho static int 6265121Sfrankho pcfs_mount( 6275121Sfrankho struct vfs *vfsp, 6285121Sfrankho struct vnode *mvp, 6295121Sfrankho struct mounta *uap, 6305121Sfrankho struct cred *cr) 6315121Sfrankho { 6325121Sfrankho struct pcfs *fsp; 6335121Sfrankho struct vnode *devvp; 6345121Sfrankho dev_t pseudodev; 6355121Sfrankho dev_t xdev; 6365121Sfrankho int dos_ldrive = 0; 6375121Sfrankho int error; 6385121Sfrankho int remounting; 6395121Sfrankho 6405121Sfrankho if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0) 6415121Sfrankho return (error); 6425121Sfrankho 6435121Sfrankho if (mvp->v_type != VDIR) 6445121Sfrankho return (ENOTDIR); 6455121Sfrankho 6465121Sfrankho mutex_enter(&mvp->v_lock); 6475121Sfrankho if ((uap->flags & MS_REMOUNT) == 0 && 6485121Sfrankho (uap->flags & MS_OVERLAY) == 0 && 6495121Sfrankho (mvp->v_count != 1 || (mvp->v_flag & VROOT))) { 6505121Sfrankho mutex_exit(&mvp->v_lock); 6515121Sfrankho return (EBUSY); 6525121Sfrankho } 6535121Sfrankho mutex_exit(&mvp->v_lock); 6545121Sfrankho 6555121Sfrankho /* 6565121Sfrankho * PCFS doesn't do mount arguments anymore - everything's a mount 6575121Sfrankho * option these days. In order not to break existing callers, we 6585121Sfrankho * don't reject it yet, just warn that the data (if any) is ignored. 6595121Sfrankho */ 6605121Sfrankho if (uap->datalen != 0) 6615121Sfrankho cmn_err(CE_WARN, "!pcfs: deprecated use of mount(2) with " 6625121Sfrankho "mount argument structures instead of mount options. " 6635121Sfrankho "Ignoring mount(2) 'dataptr' argument."); 6645121Sfrankho 6655121Sfrankho /* 6666644Sgd78059 * This is needed early, to make sure the access / open calls 6676644Sgd78059 * are done using the correct mode. Processing this mount option 6686644Sgd78059 * only when calling pcfs_parse_mntopts() would lead us to attempt 6696644Sgd78059 * a read/write access to a possibly writeprotected device, and 6706644Sgd78059 * a readonly mount attempt might fail because of that. 6716644Sgd78059 */ 6726644Sgd78059 if (uap->flags & MS_RDONLY) { 6736644Sgd78059 vfsp->vfs_flag |= VFS_RDONLY; 6746644Sgd78059 vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0); 6756644Sgd78059 } 6766644Sgd78059 6776644Sgd78059 /* 6785121Sfrankho * For most filesystems, this is just a lookupname() on the 6795121Sfrankho * mount pathname string. PCFS historically has to do its own 6805121Sfrankho * partition table parsing because not all Solaris architectures 6815121Sfrankho * support all styles of partitioning that PC media can have, and 6825121Sfrankho * hence PCFS understands "device names" that don't map to actual 6835121Sfrankho * physical device nodes. Parsing the "PCFS syntax" for device 6845121Sfrankho * names is done in pcfs_device_identify() - see there. 6855121Sfrankho * 6865121Sfrankho * Once all block device drivers that can host FAT filesystems have 6875121Sfrankho * been enhanced to create device nodes for all PC-style partitions, 6885121Sfrankho * this code can go away. 6895121Sfrankho */ 6905121Sfrankho if (error = pcfs_device_identify(vfsp, uap, cr, &dos_ldrive, &xdev)) 6915121Sfrankho return (error); 6925121Sfrankho 6935121Sfrankho /* 6945121Sfrankho * As with looking up the actual device to mount, PCFS cannot rely 6955121Sfrankho * on just the checks done by vfs_ismounted() whether a given device 6965121Sfrankho * is mounted already. The additional check against the "PCFS syntax" 6975121Sfrankho * is done in pcfs_device_ismounted(). 6985121Sfrankho */ 6995121Sfrankho remounting = (uap->flags & MS_REMOUNT); 7005121Sfrankho 7015121Sfrankho if (error = pcfs_device_ismounted(vfsp, dos_ldrive, xdev, &remounting, 7025121Sfrankho &pseudodev)) 7035121Sfrankho return (error); 7045121Sfrankho 7055121Sfrankho if (remounting) 7065121Sfrankho return (0); 7075121Sfrankho 7085121Sfrankho /* 7095121Sfrankho * Mount the filesystem. 7105121Sfrankho * An instance structure is required before the attempt to locate 7115121Sfrankho * and parse the FAT BPB. This is because mount options may change 7125121Sfrankho * the behaviour of the filesystem type matching code. Precreate 7135121Sfrankho * it and fill it in to a degree that allows parsing the mount 7145121Sfrankho * options. 7155121Sfrankho */ 7165121Sfrankho devvp = makespecvp(xdev, VBLK); 7175121Sfrankho if (IS_SWAPVP(devvp)) { 7185121Sfrankho VN_RELE(devvp); 7195121Sfrankho return (EBUSY); 7205121Sfrankho } 7215121Sfrankho error = VOP_OPEN(&devvp, 7225331Samw (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD | FWRITE, cr, NULL); 7235121Sfrankho if (error) { 7245121Sfrankho VN_RELE(devvp); 7255121Sfrankho return (error); 7265121Sfrankho } 7275121Sfrankho 7285121Sfrankho fsp = kmem_zalloc(sizeof (*fsp), KM_SLEEP); 7295121Sfrankho fsp->pcfs_vfs = vfsp; 7305121Sfrankho fsp->pcfs_xdev = xdev; 7315121Sfrankho fsp->pcfs_devvp = devvp; 7325121Sfrankho fsp->pcfs_ldrive = dos_ldrive; 7335121Sfrankho mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL); 7340Sstevel@tonic-gate 7356645Sgd78059 pcfs_parse_mntopts(fsp); 7365121Sfrankho 7375121Sfrankho /* 7385121Sfrankho * This is the actual "mount" - the PCFS superblock check. 7395121Sfrankho * 7405121Sfrankho * Find the requested logical drive and the FAT BPB therein. 7415121Sfrankho * Check device type and flag the instance if media is removeable. 7425121Sfrankho * 7435121Sfrankho * Initializes most members of the filesystem instance structure. 7445121Sfrankho * Returns EINVAL if no valid BPB can be found. Other errors may 7455121Sfrankho * occur after I/O failures, or when invalid / unparseable partition 7465121Sfrankho * tables are encountered. 7475121Sfrankho */ 7485121Sfrankho if (error = pc_getfattype(fsp)) 7495121Sfrankho goto errout; 7505121Sfrankho 7515121Sfrankho /* 7526644Sgd78059 * Now that the BPB has been parsed, this structural information 7536644Sgd78059 * is available and known to be valid. Initialize the VFS. 7546644Sgd78059 */ 7556644Sgd78059 vfsp->vfs_data = fsp; 7566644Sgd78059 vfsp->vfs_dev = pseudodev; 7576644Sgd78059 vfsp->vfs_fstype = pcfstype; 7586644Sgd78059 vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype); 7596644Sgd78059 vfsp->vfs_bcount = 0; 7606644Sgd78059 vfsp->vfs_bsize = fsp->pcfs_clsize; 7616644Sgd78059 7626644Sgd78059 /* 7635121Sfrankho * Validate that we can access the FAT and that it is, to the 7645121Sfrankho * degree we can verify here, self-consistent. 7655121Sfrankho */ 7665121Sfrankho if (error = pc_verify(fsp)) 7675121Sfrankho goto errout; 7685121Sfrankho 7695121Sfrankho /* 7705121Sfrankho * Record the time of the mount, to return as an "approximate" 7715121Sfrankho * timestamp for the FAT root directory. Since FAT roots don't 7725121Sfrankho * have timestamps, this is less confusing to the user than 7735121Sfrankho * claiming "zero" / Jan/01/1970. 7745121Sfrankho */ 7755121Sfrankho gethrestime(&fsp->pcfs_mounttime); 7765121Sfrankho 7775121Sfrankho /* 7785121Sfrankho * Fix up the mount options. Because "noatime" is made default on 7795121Sfrankho * removeable media only, a fixed disk will have neither "atime" 7805121Sfrankho * nor "noatime" set. We set the options explicitly depending on 7815121Sfrankho * the PCFS_NOATIME flag, to inform the user of what applies. 7825121Sfrankho * Mount option cancellation will take care that the mutually 7835121Sfrankho * exclusive 'other' is cleared. 7845121Sfrankho */ 7855121Sfrankho vfs_setmntopt(vfsp, 7865121Sfrankho fsp->pcfs_flags & PCFS_NOATIME ? MNTOPT_NOATIME : MNTOPT_ATIME, 7875121Sfrankho NULL, 0); 7885121Sfrankho 7895121Sfrankho /* 7905121Sfrankho * All clear - insert the FS instance into PCFS' list. 7915121Sfrankho */ 7920Sstevel@tonic-gate mutex_enter(&pcfslock); 7930Sstevel@tonic-gate fsp->pcfs_nxt = pc_mounttab; 7940Sstevel@tonic-gate pc_mounttab = fsp; 7950Sstevel@tonic-gate mutex_exit(&pcfslock); 7962720Sfrankho atomic_inc_32(&pcfs_mountcount); 7970Sstevel@tonic-gate return (0); 7985121Sfrankho 7995121Sfrankho errout: 8005121Sfrankho (void) VOP_CLOSE(devvp, 8015121Sfrankho vfsp->vfs_flag & VFS_RDONLY ? FREAD : FREAD | FWRITE, 8025331Samw 1, (offset_t)0, cr, NULL); 8035121Sfrankho VN_RELE(devvp); 8045121Sfrankho mutex_destroy(&fsp->pcfs_lock); 8055121Sfrankho kmem_free(fsp, sizeof (*fsp)); 8065121Sfrankho return (error); 8075121Sfrankho 8080Sstevel@tonic-gate } 8090Sstevel@tonic-gate 8100Sstevel@tonic-gate static int 8110Sstevel@tonic-gate pcfs_unmount( 8120Sstevel@tonic-gate struct vfs *vfsp, 8130Sstevel@tonic-gate int flag, 8140Sstevel@tonic-gate struct cred *cr) 8150Sstevel@tonic-gate { 8160Sstevel@tonic-gate struct pcfs *fsp, *fsp1; 8170Sstevel@tonic-gate 8180Sstevel@tonic-gate if (secpolicy_fs_unmount(cr, vfsp) != 0) 8190Sstevel@tonic-gate return (EPERM); 8200Sstevel@tonic-gate 8210Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 8222720Sfrankho 8230Sstevel@tonic-gate /* 8240Sstevel@tonic-gate * We don't have to lock fsp because the VVFSLOCK in vfs layer will 8250Sstevel@tonic-gate * prevent lookuppn from crossing the mount point. 8262720Sfrankho * If this is not a forced umount request and there's ongoing I/O, 8272720Sfrankho * don't allow the mount to proceed. 8280Sstevel@tonic-gate */ 8292720Sfrankho if (flag & MS_FORCE) 8302720Sfrankho vfsp->vfs_flag |= VFS_UNMOUNTED; 8312720Sfrankho else if (fsp->pcfs_nrefs) 8320Sstevel@tonic-gate return (EBUSY); 8332720Sfrankho 8342720Sfrankho mutex_enter(&pcfslock); 8350Sstevel@tonic-gate 8360Sstevel@tonic-gate /* 8372720Sfrankho * If this is a forced umount request or if the fs instance has 8382720Sfrankho * been marked as beyond recovery, allow the umount to proceed 8392720Sfrankho * regardless of state. pc_diskchanged() forcibly releases all 8402720Sfrankho * inactive vnodes/pcnodes. 8410Sstevel@tonic-gate */ 8422720Sfrankho if (flag & MS_FORCE || fsp->pcfs_flags & PCFS_IRRECOV) { 8430Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_WRITER); 8440Sstevel@tonic-gate pc_diskchanged(fsp); 8450Sstevel@tonic-gate rw_exit(&pcnodes_lock); 8460Sstevel@tonic-gate } 8470Sstevel@tonic-gate 8480Sstevel@tonic-gate /* now there should be no pcp node on pcfhead or pcdhead. */ 8490Sstevel@tonic-gate 8500Sstevel@tonic-gate if (fsp == pc_mounttab) { 8510Sstevel@tonic-gate pc_mounttab = fsp->pcfs_nxt; 8520Sstevel@tonic-gate } else { 8530Sstevel@tonic-gate for (fsp1 = pc_mounttab; fsp1 != NULL; fsp1 = fsp1->pcfs_nxt) 8540Sstevel@tonic-gate if (fsp1->pcfs_nxt == fsp) 8550Sstevel@tonic-gate fsp1->pcfs_nxt = fsp->pcfs_nxt; 8560Sstevel@tonic-gate } 8570Sstevel@tonic-gate 8580Sstevel@tonic-gate mutex_exit(&pcfslock); 8590Sstevel@tonic-gate 8602720Sfrankho /* 8612720Sfrankho * Since we support VFS_FREEVFS(), there's no need to 8622720Sfrankho * free the fsp right now. The framework will tell us 8632720Sfrankho * when the right time to do so has arrived by calling 8642720Sfrankho * into pcfs_freevfs. 8652720Sfrankho */ 8660Sstevel@tonic-gate return (0); 8670Sstevel@tonic-gate } 8680Sstevel@tonic-gate 8690Sstevel@tonic-gate /* 8700Sstevel@tonic-gate * find root of pcfs 8710Sstevel@tonic-gate */ 8720Sstevel@tonic-gate static int 8730Sstevel@tonic-gate pcfs_root( 8740Sstevel@tonic-gate struct vfs *vfsp, 8750Sstevel@tonic-gate struct vnode **vpp) 8760Sstevel@tonic-gate { 8770Sstevel@tonic-gate struct pcfs *fsp; 8780Sstevel@tonic-gate struct pcnode *pcp; 8790Sstevel@tonic-gate int error; 8800Sstevel@tonic-gate 8810Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 8820Sstevel@tonic-gate if (error = pc_lockfs(fsp, 0, 0)) 8830Sstevel@tonic-gate return (error); 8842720Sfrankho 8850Sstevel@tonic-gate pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0); 8860Sstevel@tonic-gate pc_unlockfs(fsp); 8870Sstevel@tonic-gate *vpp = PCTOV(pcp); 8880Sstevel@tonic-gate pcp->pc_flags |= PC_EXTERNAL; 8890Sstevel@tonic-gate return (0); 8900Sstevel@tonic-gate } 8910Sstevel@tonic-gate 8920Sstevel@tonic-gate /* 8930Sstevel@tonic-gate * Get file system statistics. 8940Sstevel@tonic-gate */ 8950Sstevel@tonic-gate static int 8960Sstevel@tonic-gate pcfs_statvfs( 8970Sstevel@tonic-gate struct vfs *vfsp, 8980Sstevel@tonic-gate struct statvfs64 *sp) 8990Sstevel@tonic-gate { 9000Sstevel@tonic-gate struct pcfs *fsp; 9010Sstevel@tonic-gate int error; 9020Sstevel@tonic-gate dev32_t d32; 9030Sstevel@tonic-gate 9040Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 9050Sstevel@tonic-gate error = pc_getfat(fsp); 9060Sstevel@tonic-gate if (error) 9070Sstevel@tonic-gate return (error); 9080Sstevel@tonic-gate bzero(sp, sizeof (*sp)); 9090Sstevel@tonic-gate sp->f_bsize = sp->f_frsize = fsp->pcfs_clsize; 9100Sstevel@tonic-gate sp->f_blocks = (fsblkcnt64_t)fsp->pcfs_ncluster; 9110Sstevel@tonic-gate sp->f_bavail = sp->f_bfree = (fsblkcnt64_t)pc_freeclusters(fsp); 9120Sstevel@tonic-gate sp->f_files = (fsfilcnt64_t)-1; 9130Sstevel@tonic-gate sp->f_ffree = (fsfilcnt64_t)-1; 9140Sstevel@tonic-gate sp->f_favail = (fsfilcnt64_t)-1; 9150Sstevel@tonic-gate #ifdef notdef 9160Sstevel@tonic-gate (void) cmpldev(&d32, fsp->pcfs_devvp->v_rdev); 9170Sstevel@tonic-gate #endif /* notdef */ 9180Sstevel@tonic-gate (void) cmpldev(&d32, vfsp->vfs_dev); 9190Sstevel@tonic-gate sp->f_fsid = d32; 9200Sstevel@tonic-gate (void) strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name); 9210Sstevel@tonic-gate sp->f_flag = vf_to_stf(vfsp->vfs_flag); 9226971Sbatschul sp->f_namemax = PCMAXNAMLEN; 9230Sstevel@tonic-gate return (0); 9240Sstevel@tonic-gate } 9250Sstevel@tonic-gate 9260Sstevel@tonic-gate static int 9270Sstevel@tonic-gate pc_syncfsnodes(struct pcfs *fsp) 9280Sstevel@tonic-gate { 9290Sstevel@tonic-gate struct pchead *hp; 9300Sstevel@tonic-gate struct pcnode *pcp; 9310Sstevel@tonic-gate int error; 9320Sstevel@tonic-gate 9330Sstevel@tonic-gate if (error = pc_lockfs(fsp, 0, 0)) 9340Sstevel@tonic-gate return (error); 9350Sstevel@tonic-gate 9360Sstevel@tonic-gate if (!(error = pc_syncfat(fsp))) { 9370Sstevel@tonic-gate hp = pcfhead; 9380Sstevel@tonic-gate while (hp < & pcfhead [ NPCHASH ]) { 9390Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_READER); 9400Sstevel@tonic-gate pcp = hp->pch_forw; 9410Sstevel@tonic-gate while (pcp != (struct pcnode *)hp) { 9420Sstevel@tonic-gate if (VFSTOPCFS(PCTOV(pcp) -> v_vfsp) == fsp) 9430Sstevel@tonic-gate if (error = pc_nodesync(pcp)) 9440Sstevel@tonic-gate break; 9450Sstevel@tonic-gate pcp = pcp -> pc_forw; 9460Sstevel@tonic-gate } 9470Sstevel@tonic-gate rw_exit(&pcnodes_lock); 9480Sstevel@tonic-gate if (error) 9490Sstevel@tonic-gate break; 9500Sstevel@tonic-gate hp++; 9510Sstevel@tonic-gate } 9520Sstevel@tonic-gate } 9530Sstevel@tonic-gate pc_unlockfs(fsp); 9540Sstevel@tonic-gate return (error); 9550Sstevel@tonic-gate } 9560Sstevel@tonic-gate 9570Sstevel@tonic-gate /* 9580Sstevel@tonic-gate * Flush any pending I/O. 9590Sstevel@tonic-gate */ 9600Sstevel@tonic-gate /*ARGSUSED*/ 9610Sstevel@tonic-gate static int 9620Sstevel@tonic-gate pcfs_sync( 9630Sstevel@tonic-gate struct vfs *vfsp, 9640Sstevel@tonic-gate short flag, 9650Sstevel@tonic-gate struct cred *cr) 9660Sstevel@tonic-gate { 9670Sstevel@tonic-gate struct pcfs *fsp; 9680Sstevel@tonic-gate int error = 0; 9690Sstevel@tonic-gate 9700Sstevel@tonic-gate /* this prevents the filesystem from being umounted. */ 9710Sstevel@tonic-gate mutex_enter(&pcfslock); 9720Sstevel@tonic-gate if (vfsp != NULL) { 9730Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 9740Sstevel@tonic-gate if (!(fsp->pcfs_flags & PCFS_IRRECOV)) { 9750Sstevel@tonic-gate error = pc_syncfsnodes(fsp); 9760Sstevel@tonic-gate } else { 9770Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_WRITER); 9780Sstevel@tonic-gate pc_diskchanged(fsp); 9790Sstevel@tonic-gate rw_exit(&pcnodes_lock); 9800Sstevel@tonic-gate error = EIO; 9810Sstevel@tonic-gate } 9820Sstevel@tonic-gate } else { 9830Sstevel@tonic-gate fsp = pc_mounttab; 9840Sstevel@tonic-gate while (fsp != NULL) { 9850Sstevel@tonic-gate if (fsp->pcfs_flags & PCFS_IRRECOV) { 9860Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_WRITER); 9870Sstevel@tonic-gate pc_diskchanged(fsp); 9880Sstevel@tonic-gate rw_exit(&pcnodes_lock); 9890Sstevel@tonic-gate error = EIO; 9900Sstevel@tonic-gate break; 9910Sstevel@tonic-gate } 9920Sstevel@tonic-gate error = pc_syncfsnodes(fsp); 9930Sstevel@tonic-gate if (error) break; 9940Sstevel@tonic-gate fsp = fsp->pcfs_nxt; 9950Sstevel@tonic-gate } 9960Sstevel@tonic-gate } 9970Sstevel@tonic-gate mutex_exit(&pcfslock); 9980Sstevel@tonic-gate return (error); 9990Sstevel@tonic-gate } 10000Sstevel@tonic-gate 10010Sstevel@tonic-gate int 10020Sstevel@tonic-gate pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing) 10030Sstevel@tonic-gate { 10042720Sfrankho int err; 10052720Sfrankho 10060Sstevel@tonic-gate if ((fsp->pcfs_flags & PCFS_IRRECOV) && !releasing) 10070Sstevel@tonic-gate return (EIO); 10080Sstevel@tonic-gate 10090Sstevel@tonic-gate if ((fsp->pcfs_flags & PCFS_LOCKED) && (fsp->pcfs_owner == curthread)) { 10100Sstevel@tonic-gate fsp->pcfs_count++; 10110Sstevel@tonic-gate } else { 10120Sstevel@tonic-gate mutex_enter(&fsp->pcfs_lock); 10130Sstevel@tonic-gate if (fsp->pcfs_flags & PCFS_LOCKED) 10140Sstevel@tonic-gate panic("pc_lockfs"); 10150Sstevel@tonic-gate /* 10160Sstevel@tonic-gate * We check the IRRECOV bit again just in case somebody 10170Sstevel@tonic-gate * snuck past the initial check but then got held up before 10180Sstevel@tonic-gate * they could grab the lock. (And in the meantime someone 10190Sstevel@tonic-gate * had grabbed the lock and set the bit) 10200Sstevel@tonic-gate */ 1021607Swyllys if (!diskchanged && !(fsp->pcfs_flags & PCFS_IRRECOV)) { 10222720Sfrankho if ((err = pc_getfat(fsp))) { 10232720Sfrankho mutex_exit(&fsp->pcfs_lock); 1024607Swyllys return (err); 10252720Sfrankho } 1026607Swyllys } 10270Sstevel@tonic-gate fsp->pcfs_flags |= PCFS_LOCKED; 10280Sstevel@tonic-gate fsp->pcfs_owner = curthread; 10290Sstevel@tonic-gate fsp->pcfs_count++; 10300Sstevel@tonic-gate } 10310Sstevel@tonic-gate return (0); 10320Sstevel@tonic-gate } 10330Sstevel@tonic-gate 10340Sstevel@tonic-gate void 10350Sstevel@tonic-gate pc_unlockfs(struct pcfs *fsp) 10360Sstevel@tonic-gate { 10370Sstevel@tonic-gate 10380Sstevel@tonic-gate if ((fsp->pcfs_flags & PCFS_LOCKED) == 0) 10390Sstevel@tonic-gate panic("pc_unlockfs"); 10400Sstevel@tonic-gate if (--fsp->pcfs_count < 0) 10410Sstevel@tonic-gate panic("pc_unlockfs: count"); 10420Sstevel@tonic-gate if (fsp->pcfs_count == 0) { 10430Sstevel@tonic-gate fsp->pcfs_flags &= ~PCFS_LOCKED; 10440Sstevel@tonic-gate fsp->pcfs_owner = 0; 10450Sstevel@tonic-gate mutex_exit(&fsp->pcfs_lock); 10460Sstevel@tonic-gate } 10470Sstevel@tonic-gate } 10480Sstevel@tonic-gate 10490Sstevel@tonic-gate int 10500Sstevel@tonic-gate pc_syncfat(struct pcfs *fsp) 10510Sstevel@tonic-gate { 10520Sstevel@tonic-gate struct buf *bp; 10530Sstevel@tonic-gate int nfat; 10545121Sfrankho int error = 0; 10555121Sfrankho struct fat_od_fsi *fsinfo_disk; 10560Sstevel@tonic-gate 10570Sstevel@tonic-gate if ((fsp->pcfs_fatp == (uchar_t *)0) || 10580Sstevel@tonic-gate !(fsp->pcfs_flags & PCFS_FATMOD)) 10590Sstevel@tonic-gate return (0); 10600Sstevel@tonic-gate /* 10610Sstevel@tonic-gate * write out all copies of FATs 10620Sstevel@tonic-gate */ 10630Sstevel@tonic-gate fsp->pcfs_flags &= ~PCFS_FATMOD; 10640Sstevel@tonic-gate fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT; 10650Sstevel@tonic-gate for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) { 10665121Sfrankho error = pc_writefat(fsp, pc_dbdaddr(fsp, 10675121Sfrankho fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec)); 10680Sstevel@tonic-gate if (error) { 10690Sstevel@tonic-gate pc_mark_irrecov(fsp); 10700Sstevel@tonic-gate return (EIO); 10710Sstevel@tonic-gate } 10720Sstevel@tonic-gate } 10730Sstevel@tonic-gate pc_clear_fatchanges(fsp); 10745121Sfrankho 10755121Sfrankho /* 10765121Sfrankho * Write out fsinfo sector. 10775121Sfrankho */ 10780Sstevel@tonic-gate if (IS_FAT32(fsp)) { 10790Sstevel@tonic-gate bp = bread(fsp->pcfs_xdev, 10805121Sfrankho pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize); 10810Sstevel@tonic-gate if (bp->b_flags & (B_ERROR | B_STALE)) { 10825121Sfrankho error = geterror(bp); 10830Sstevel@tonic-gate } 10845121Sfrankho fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr); 10855121Sfrankho if (!error && FSISIG_OK(fsinfo_disk)) { 10865121Sfrankho fsinfo_disk->fsi_incore.fs_free_clusters = 10875121Sfrankho LE_32(fsp->pcfs_fsinfo.fs_free_clusters); 10885121Sfrankho fsinfo_disk->fsi_incore.fs_next_free = 10895121Sfrankho LE_32(FSINFO_UNKNOWN); 10905121Sfrankho bwrite2(bp); 10915121Sfrankho error = geterror(bp); 10925121Sfrankho } 10930Sstevel@tonic-gate brelse(bp); 10940Sstevel@tonic-gate if (error) { 10950Sstevel@tonic-gate pc_mark_irrecov(fsp); 10960Sstevel@tonic-gate return (EIO); 10970Sstevel@tonic-gate } 10980Sstevel@tonic-gate } 10990Sstevel@tonic-gate return (0); 11000Sstevel@tonic-gate } 11010Sstevel@tonic-gate 11020Sstevel@tonic-gate void 11030Sstevel@tonic-gate pc_invalfat(struct pcfs *fsp) 11040Sstevel@tonic-gate { 11050Sstevel@tonic-gate struct pcfs *xfsp; 11060Sstevel@tonic-gate int mount_cnt = 0; 11070Sstevel@tonic-gate 11080Sstevel@tonic-gate if (fsp->pcfs_fatp == (uchar_t *)0) 11090Sstevel@tonic-gate panic("pc_invalfat"); 11100Sstevel@tonic-gate /* 11110Sstevel@tonic-gate * Release FAT 11120Sstevel@tonic-gate */ 11135121Sfrankho kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsec * fsp->pcfs_secsize); 11140Sstevel@tonic-gate fsp->pcfs_fatp = NULL; 11150Sstevel@tonic-gate kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize); 11160Sstevel@tonic-gate fsp->pcfs_fat_changemap = NULL; 11170Sstevel@tonic-gate /* 11180Sstevel@tonic-gate * Invalidate all the blocks associated with the device. 11190Sstevel@tonic-gate * Not needed if stateless. 11200Sstevel@tonic-gate */ 11210Sstevel@tonic-gate for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt) 11220Sstevel@tonic-gate if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev) 11230Sstevel@tonic-gate mount_cnt++; 11240Sstevel@tonic-gate 11250Sstevel@tonic-gate if (!mount_cnt) 11260Sstevel@tonic-gate binval(fsp->pcfs_xdev); 11270Sstevel@tonic-gate /* 11280Sstevel@tonic-gate * close mounted device 11290Sstevel@tonic-gate */ 11300Sstevel@tonic-gate (void) VOP_CLOSE(fsp->pcfs_devvp, 11310Sstevel@tonic-gate (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, 11325331Samw 1, (offset_t)0, CRED(), NULL); 11330Sstevel@tonic-gate } 11340Sstevel@tonic-gate 11350Sstevel@tonic-gate void 11360Sstevel@tonic-gate pc_badfs(struct pcfs *fsp) 11370Sstevel@tonic-gate { 11385121Sfrankho cmn_err(CE_WARN, "corrupted PC file system on dev (%x.%x):%d\n", 11390Sstevel@tonic-gate getmajor(fsp->pcfs_devvp->v_rdev), 11405121Sfrankho getminor(fsp->pcfs_devvp->v_rdev), fsp->pcfs_ldrive); 11410Sstevel@tonic-gate } 11420Sstevel@tonic-gate 11430Sstevel@tonic-gate /* 11440Sstevel@tonic-gate * The problem with supporting NFS on the PCFS filesystem is that there 11450Sstevel@tonic-gate * is no good place to keep the generation number. The only possible 11460Sstevel@tonic-gate * place is inside a directory entry. There are a few words that we 11470Sstevel@tonic-gate * don't use - they store NT & OS/2 attributes, and the creation/last access 11480Sstevel@tonic-gate * time of the file - but it seems wrong to use them. In addition, directory 11490Sstevel@tonic-gate * entries come and go. If a directory is removed completely, its directory 11500Sstevel@tonic-gate * blocks are freed and the generation numbers are lost. Whereas in ufs, 11510Sstevel@tonic-gate * inode blocks are dedicated for inodes, so the generation numbers are 11520Sstevel@tonic-gate * permanently kept on the disk. 11530Sstevel@tonic-gate */ 11540Sstevel@tonic-gate static int 11550Sstevel@tonic-gate pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp) 11560Sstevel@tonic-gate { 11570Sstevel@tonic-gate struct pcnode *pcp; 11580Sstevel@tonic-gate struct pc_fid *pcfid; 11590Sstevel@tonic-gate struct pcfs *fsp; 11600Sstevel@tonic-gate struct pcdir *ep; 11610Sstevel@tonic-gate daddr_t eblkno; 11620Sstevel@tonic-gate int eoffset; 11630Sstevel@tonic-gate struct buf *bp; 11640Sstevel@tonic-gate int error; 11650Sstevel@tonic-gate pc_cluster32_t cn; 11660Sstevel@tonic-gate 11670Sstevel@tonic-gate pcfid = (struct pc_fid *)fidp; 11680Sstevel@tonic-gate fsp = VFSTOPCFS(vfsp); 11690Sstevel@tonic-gate 11700Sstevel@tonic-gate error = pc_lockfs(fsp, 0, 0); 11710Sstevel@tonic-gate if (error) { 11720Sstevel@tonic-gate *vpp = NULL; 11730Sstevel@tonic-gate return (error); 11740Sstevel@tonic-gate } 11750Sstevel@tonic-gate 11760Sstevel@tonic-gate if (pcfid->pcfid_block == 0) { 11770Sstevel@tonic-gate pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0); 11780Sstevel@tonic-gate pcp->pc_flags |= PC_EXTERNAL; 11790Sstevel@tonic-gate *vpp = PCTOV(pcp); 11800Sstevel@tonic-gate pc_unlockfs(fsp); 11810Sstevel@tonic-gate return (0); 11820Sstevel@tonic-gate } 11830Sstevel@tonic-gate eblkno = pcfid->pcfid_block; 11840Sstevel@tonic-gate eoffset = pcfid->pcfid_offset; 11855121Sfrankho 11860Sstevel@tonic-gate if ((pc_dbtocl(fsp, 11870Sstevel@tonic-gate eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) || 11880Sstevel@tonic-gate (eoffset > fsp->pcfs_clsize)) { 11890Sstevel@tonic-gate pc_unlockfs(fsp); 11900Sstevel@tonic-gate *vpp = NULL; 11910Sstevel@tonic-gate return (EINVAL); 11920Sstevel@tonic-gate } 11930Sstevel@tonic-gate 11945121Sfrankho if (eblkno >= fsp->pcfs_datastart || (eblkno - fsp->pcfs_rdirstart) 11950Sstevel@tonic-gate < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) { 11965121Sfrankho bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno), 11975121Sfrankho fsp->pcfs_clsize); 11980Sstevel@tonic-gate } else { 11995121Sfrankho /* 12005121Sfrankho * This is an access "backwards" into the FAT12/FAT16 12015121Sfrankho * root directory. A better code structure would 12025121Sfrankho * significantly improve maintainability here ... 12035121Sfrankho */ 12045121Sfrankho bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno), 12050Sstevel@tonic-gate (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize); 12060Sstevel@tonic-gate } 12070Sstevel@tonic-gate if (bp->b_flags & (B_ERROR | B_STALE)) { 12080Sstevel@tonic-gate error = geterror(bp); 12090Sstevel@tonic-gate brelse(bp); 12100Sstevel@tonic-gate if (error) 12110Sstevel@tonic-gate pc_mark_irrecov(fsp); 12120Sstevel@tonic-gate *vpp = NULL; 12130Sstevel@tonic-gate pc_unlockfs(fsp); 12140Sstevel@tonic-gate return (error); 12150Sstevel@tonic-gate } 12160Sstevel@tonic-gate ep = (struct pcdir *)(bp->b_un.b_addr + eoffset); 12170Sstevel@tonic-gate /* 12180Sstevel@tonic-gate * Ok, if this is a valid file handle that we gave out, 12190Sstevel@tonic-gate * then simply ensuring that the creation time matches, 12200Sstevel@tonic-gate * the entry has not been deleted, and it has a valid first 12210Sstevel@tonic-gate * character should be enough. 12220Sstevel@tonic-gate * 12230Sstevel@tonic-gate * Unfortunately, verifying that the <blkno, offset> _still_ 12240Sstevel@tonic-gate * refers to a directory entry is not easy, since we'd have 12250Sstevel@tonic-gate * to search _all_ directories starting from root to find it. 12260Sstevel@tonic-gate * That's a high price to pay just in case somebody is forging 12270Sstevel@tonic-gate * file handles. So instead we verify that as much of the 12280Sstevel@tonic-gate * entry is valid as we can: 12290Sstevel@tonic-gate * 12300Sstevel@tonic-gate * 1. The starting cluster is 0 (unallocated) or valid 12310Sstevel@tonic-gate * 2. It is not an LFN entry 12320Sstevel@tonic-gate * 3. It is not hidden (unless mounted as such) 12330Sstevel@tonic-gate * 4. It is not the label 12340Sstevel@tonic-gate */ 12350Sstevel@tonic-gate cn = pc_getstartcluster(fsp, ep); 12360Sstevel@tonic-gate /* 12370Sstevel@tonic-gate * if the starting cluster is valid, but not valid according 12380Sstevel@tonic-gate * to pc_validcl(), force it to be to simplify the following if. 12390Sstevel@tonic-gate */ 12400Sstevel@tonic-gate if (cn == 0) 12410Sstevel@tonic-gate cn = PCF_FIRSTCLUSTER; 12420Sstevel@tonic-gate if (IS_FAT32(fsp)) { 12430Sstevel@tonic-gate if (cn >= PCF_LASTCLUSTER32) 12440Sstevel@tonic-gate cn = PCF_FIRSTCLUSTER; 12450Sstevel@tonic-gate } else { 12460Sstevel@tonic-gate if (cn >= PCF_LASTCLUSTER) 12470Sstevel@tonic-gate cn = PCF_FIRSTCLUSTER; 12480Sstevel@tonic-gate } 12490Sstevel@tonic-gate if ((!pc_validcl(fsp, cn)) || 12500Sstevel@tonic-gate (PCDL_IS_LFN(ep)) || 12510Sstevel@tonic-gate (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) || 12520Sstevel@tonic-gate ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) { 12530Sstevel@tonic-gate bp->b_flags |= B_STALE | B_AGE; 12540Sstevel@tonic-gate brelse(bp); 12550Sstevel@tonic-gate pc_unlockfs(fsp); 12560Sstevel@tonic-gate return (EINVAL); 12570Sstevel@tonic-gate } 12580Sstevel@tonic-gate if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) && 12590Sstevel@tonic-gate (ep->pcd_filename[0] != PCD_ERASED) && 12600Sstevel@tonic-gate (pc_validchar(ep->pcd_filename[0]) || 12615121Sfrankho (ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) { 12620Sstevel@tonic-gate pcp = pc_getnode(fsp, eblkno, eoffset, ep); 12630Sstevel@tonic-gate pcp->pc_flags |= PC_EXTERNAL; 12640Sstevel@tonic-gate *vpp = PCTOV(pcp); 12650Sstevel@tonic-gate } else { 12660Sstevel@tonic-gate *vpp = NULL; 12670Sstevel@tonic-gate } 12680Sstevel@tonic-gate bp->b_flags |= B_STALE | B_AGE; 12690Sstevel@tonic-gate brelse(bp); 12700Sstevel@tonic-gate pc_unlockfs(fsp); 12710Sstevel@tonic-gate return (0); 12720Sstevel@tonic-gate } 12730Sstevel@tonic-gate 12740Sstevel@tonic-gate /* 12750Sstevel@tonic-gate * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about 12760Sstevel@tonic-gate * a meg), so we can't bread() it all in at once. This routine reads a 12770Sstevel@tonic-gate * fat a chunk at a time. 12780Sstevel@tonic-gate */ 12790Sstevel@tonic-gate static int 12805121Sfrankho pc_readfat(struct pcfs *fsp, uchar_t *fatp) 12810Sstevel@tonic-gate { 12820Sstevel@tonic-gate struct buf *bp; 12830Sstevel@tonic-gate size_t off; 12840Sstevel@tonic-gate size_t readsize; 12855121Sfrankho daddr_t diskblk; 12865121Sfrankho size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize; 12875121Sfrankho daddr_t start = fsp->pcfs_fatstart; 12880Sstevel@tonic-gate 12890Sstevel@tonic-gate readsize = fsp->pcfs_clsize; 12900Sstevel@tonic-gate for (off = 0; off < fatsize; off += readsize, fatp += readsize) { 12910Sstevel@tonic-gate if (readsize > (fatsize - off)) 12920Sstevel@tonic-gate readsize = fatsize - off; 12935121Sfrankho diskblk = pc_dbdaddr(fsp, start + 12945121Sfrankho pc_cltodb(fsp, pc_lblkno(fsp, off))); 12955121Sfrankho bp = bread(fsp->pcfs_xdev, diskblk, readsize); 12960Sstevel@tonic-gate if (bp->b_flags & (B_ERROR | B_STALE)) { 12970Sstevel@tonic-gate brelse(bp); 12980Sstevel@tonic-gate return (EIO); 12990Sstevel@tonic-gate } 13000Sstevel@tonic-gate bp->b_flags |= B_STALE | B_AGE; 13010Sstevel@tonic-gate bcopy(bp->b_un.b_addr, fatp, readsize); 13020Sstevel@tonic-gate brelse(bp); 13030Sstevel@tonic-gate } 13040Sstevel@tonic-gate return (0); 13050Sstevel@tonic-gate } 13060Sstevel@tonic-gate 13070Sstevel@tonic-gate /* 13080Sstevel@tonic-gate * We write the FAT out a _lot_, in order to make sure that it 13090Sstevel@tonic-gate * is up-to-date. But on a FAT32 system (large drive, small clusters) 13100Sstevel@tonic-gate * the FAT might be a couple of megabytes, and writing it all out just 13110Sstevel@tonic-gate * because we created or deleted a small file is painful (especially 13120Sstevel@tonic-gate * since we do it for each alternate FAT too). So instead, for FAT16 and 13130Sstevel@tonic-gate * FAT32 we only write out the bit that has changed. We don't clear 13140Sstevel@tonic-gate * the 'updated' fields here because the caller might be writing out 13150Sstevel@tonic-gate * several FATs, so the caller must use pc_clear_fatchanges() after 13160Sstevel@tonic-gate * all FATs have been updated. 13175121Sfrankho * This function doesn't take "start" from fsp->pcfs_dosstart because 13185121Sfrankho * callers can use it to write either the primary or any of the alternate 13195121Sfrankho * FAT tables. 13200Sstevel@tonic-gate */ 13210Sstevel@tonic-gate static int 13220Sstevel@tonic-gate pc_writefat(struct pcfs *fsp, daddr_t start) 13230Sstevel@tonic-gate { 13240Sstevel@tonic-gate struct buf *bp; 13250Sstevel@tonic-gate size_t off; 13260Sstevel@tonic-gate size_t writesize; 13270Sstevel@tonic-gate int error; 13280Sstevel@tonic-gate uchar_t *fatp = fsp->pcfs_fatp; 13295121Sfrankho size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize; 13300Sstevel@tonic-gate 13310Sstevel@tonic-gate writesize = fsp->pcfs_clsize; 13320Sstevel@tonic-gate for (off = 0; off < fatsize; off += writesize, fatp += writesize) { 13330Sstevel@tonic-gate if (writesize > (fatsize - off)) 13340Sstevel@tonic-gate writesize = fatsize - off; 13350Sstevel@tonic-gate if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) { 13360Sstevel@tonic-gate continue; 13370Sstevel@tonic-gate } 13380Sstevel@tonic-gate bp = ngeteblk(writesize); 13390Sstevel@tonic-gate bp->b_edev = fsp->pcfs_xdev; 13400Sstevel@tonic-gate bp->b_dev = cmpdev(bp->b_edev); 13410Sstevel@tonic-gate bp->b_blkno = pc_dbdaddr(fsp, start + 13420Sstevel@tonic-gate pc_cltodb(fsp, pc_lblkno(fsp, off))); 13430Sstevel@tonic-gate bcopy(fatp, bp->b_un.b_addr, writesize); 13440Sstevel@tonic-gate bwrite2(bp); 13450Sstevel@tonic-gate error = geterror(bp); 13460Sstevel@tonic-gate brelse(bp); 13470Sstevel@tonic-gate if (error) { 13480Sstevel@tonic-gate return (error); 13490Sstevel@tonic-gate } 13500Sstevel@tonic-gate } 13510Sstevel@tonic-gate return (0); 13520Sstevel@tonic-gate } 13530Sstevel@tonic-gate 13540Sstevel@tonic-gate /* 13550Sstevel@tonic-gate * Mark the FAT cluster that 'cn' is stored in as modified. 13560Sstevel@tonic-gate */ 13570Sstevel@tonic-gate void 13580Sstevel@tonic-gate pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn) 13590Sstevel@tonic-gate { 13600Sstevel@tonic-gate pc_cluster32_t bn; 13610Sstevel@tonic-gate size_t size; 13620Sstevel@tonic-gate 13630Sstevel@tonic-gate /* which fat block is the cluster number stored in? */ 13640Sstevel@tonic-gate if (IS_FAT32(fsp)) { 13650Sstevel@tonic-gate size = sizeof (pc_cluster32_t); 13660Sstevel@tonic-gate bn = pc_lblkno(fsp, cn * size); 13670Sstevel@tonic-gate fsp->pcfs_fat_changemap[bn] = 1; 13680Sstevel@tonic-gate } else if (IS_FAT16(fsp)) { 13690Sstevel@tonic-gate size = sizeof (pc_cluster16_t); 13700Sstevel@tonic-gate bn = pc_lblkno(fsp, cn * size); 13710Sstevel@tonic-gate fsp->pcfs_fat_changemap[bn] = 1; 13720Sstevel@tonic-gate } else { 13730Sstevel@tonic-gate offset_t off; 13740Sstevel@tonic-gate pc_cluster32_t nbn; 13750Sstevel@tonic-gate 13760Sstevel@tonic-gate ASSERT(IS_FAT12(fsp)); 13770Sstevel@tonic-gate off = cn + (cn >> 1); 13780Sstevel@tonic-gate bn = pc_lblkno(fsp, off); 13790Sstevel@tonic-gate fsp->pcfs_fat_changemap[bn] = 1; 13800Sstevel@tonic-gate /* does this field wrap into the next fat cluster? */ 13810Sstevel@tonic-gate nbn = pc_lblkno(fsp, off + 1); 13820Sstevel@tonic-gate if (nbn != bn) { 13830Sstevel@tonic-gate fsp->pcfs_fat_changemap[nbn] = 1; 13840Sstevel@tonic-gate } 13850Sstevel@tonic-gate } 13860Sstevel@tonic-gate } 13870Sstevel@tonic-gate 13880Sstevel@tonic-gate /* 13890Sstevel@tonic-gate * return whether the FAT cluster 'bn' is updated and needs to 13900Sstevel@tonic-gate * be written out. 13910Sstevel@tonic-gate */ 13920Sstevel@tonic-gate int 13930Sstevel@tonic-gate pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn) 13940Sstevel@tonic-gate { 13950Sstevel@tonic-gate return (fsp->pcfs_fat_changemap[bn] == 1); 13960Sstevel@tonic-gate } 13972720Sfrankho 13982720Sfrankho /* 13992720Sfrankho * Implementation of VFS_FREEVFS() to support forced umounts. 14002720Sfrankho * This is called by the vfs framework after umount, to trigger 14012720Sfrankho * the release of any resources still associated with the given 14022720Sfrankho * vfs_t once the need to keep them has gone away. 14032720Sfrankho */ 14042720Sfrankho void 14052720Sfrankho pcfs_freevfs(vfs_t *vfsp) 14062720Sfrankho { 14072720Sfrankho struct pcfs *fsp = VFSTOPCFS(vfsp); 14082720Sfrankho 14092720Sfrankho mutex_enter(&pcfslock); 14105121Sfrankho /* 14115121Sfrankho * Purging the FAT closes the device - can't do any more 14125121Sfrankho * I/O after this. 14135121Sfrankho */ 14142720Sfrankho if (fsp->pcfs_fatp != (uchar_t *)0) 14152720Sfrankho pc_invalfat(fsp); 14162720Sfrankho mutex_exit(&pcfslock); 14172720Sfrankho 14182720Sfrankho VN_RELE(fsp->pcfs_devvp); 14192720Sfrankho mutex_destroy(&fsp->pcfs_lock); 14205121Sfrankho kmem_free(fsp, sizeof (*fsp)); 14212720Sfrankho 14222720Sfrankho /* 14232720Sfrankho * Allow _fini() to succeed now, if so desired. 14242720Sfrankho */ 14252720Sfrankho atomic_dec_32(&pcfs_mountcount); 14262720Sfrankho } 14275121Sfrankho 14285121Sfrankho 14295121Sfrankho /* 14305121Sfrankho * PC-style partition parsing and FAT BPB identification/validation code. 14315121Sfrankho * The partition parsers here assume: 14325121Sfrankho * - a FAT filesystem will be in a partition that has one of a set of 14335121Sfrankho * recognized partition IDs 14345121Sfrankho * - the user wants the 'numbering' (C:, D:, ...) that one would get 14355121Sfrankho * on MSDOS 6.x. 14365121Sfrankho * That means any non-FAT partition type (NTFS, HPFS, or any Linux fs) 14375121Sfrankho * will not factor in the enumeration. 14385121Sfrankho * These days, such assumptions should be revisited. FAT is no longer the 14395121Sfrankho * only game in 'PC town'. 14405121Sfrankho */ 14415121Sfrankho /* 14425121Sfrankho * isDosDrive() 14435121Sfrankho * Boolean function. Give it the systid field for an fdisk partition 14445121Sfrankho * and it decides if that's a systid that describes a DOS drive. We 14455121Sfrankho * use systid values defined in sys/dktp/fdisk.h. 14465121Sfrankho */ 14475121Sfrankho static int 14485121Sfrankho isDosDrive(uchar_t checkMe) 14495121Sfrankho { 14505121Sfrankho return ((checkMe == DOSOS12) || (checkMe == DOSOS16) || 14515121Sfrankho (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) || 14525121Sfrankho (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) || 14535121Sfrankho (checkMe == DIAGPART)); 14545121Sfrankho } 14555121Sfrankho 14565121Sfrankho 14575121Sfrankho /* 14585121Sfrankho * isDosExtended() 14595121Sfrankho * Boolean function. Give it the systid field for an fdisk partition 14605121Sfrankho * and it decides if that's a systid that describes an extended DOS 14615121Sfrankho * partition. 14625121Sfrankho */ 14635121Sfrankho static int 14645121Sfrankho isDosExtended(uchar_t checkMe) 14655121Sfrankho { 14665121Sfrankho return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA)); 14675121Sfrankho } 14685121Sfrankho 14695121Sfrankho 14705121Sfrankho /* 14715121Sfrankho * isBootPart() 14725121Sfrankho * Boolean function. Give it the systid field for an fdisk partition 14735121Sfrankho * and it decides if that's a systid that describes a Solaris boot 14745121Sfrankho * partition. 14755121Sfrankho */ 14765121Sfrankho static int 14775121Sfrankho isBootPart(uchar_t checkMe) 14785121Sfrankho { 14795121Sfrankho return (checkMe == X86BOOT); 14805121Sfrankho } 14815121Sfrankho 14825121Sfrankho 14835121Sfrankho /* 14845121Sfrankho * noLogicalDrive() 14855121Sfrankho * Display error message about not being able to find a logical 14865121Sfrankho * drive. 14875121Sfrankho */ 14885121Sfrankho static void 14895121Sfrankho noLogicalDrive(int ldrive) 14905121Sfrankho { 14915121Sfrankho if (ldrive == BOOT_PARTITION_DRIVE) { 14925121Sfrankho cmn_err(CE_NOTE, "!pcfs: no boot partition"); 14935121Sfrankho } else { 14945121Sfrankho cmn_err(CE_NOTE, "!pcfs: %d: no such logical drive", ldrive); 14955121Sfrankho } 14965121Sfrankho } 14975121Sfrankho 14985121Sfrankho 14995121Sfrankho /* 15005121Sfrankho * findTheDrive() 15015121Sfrankho * Discover offset of the requested logical drive, and return 15025121Sfrankho * that offset (startSector), the systid of that drive (sysid), 15035121Sfrankho * and a buffer pointer (bp), with the buffer contents being 15045121Sfrankho * the first sector of the logical drive (i.e., the sector that 15055121Sfrankho * contains the BPB for that drive). 15065121Sfrankho * 15075121Sfrankho * Note: this code is not capable of addressing >2TB disks, as it uses 15085121Sfrankho * daddr_t not diskaddr_t, some of the calculations would overflow 15095121Sfrankho */ 15105121Sfrankho #define COPY_PTBL(mbr, ptblp) \ 15115121Sfrankho bcopy(&(((struct mboot *)(mbr))->parts), (ptblp), \ 15125121Sfrankho FD_NUMPART * sizeof (struct ipart)) 15135121Sfrankho 15145121Sfrankho static int 15155121Sfrankho findTheDrive(struct pcfs *fsp, buf_t **bp) 15165121Sfrankho { 15175121Sfrankho int ldrive = fsp->pcfs_ldrive; 15185121Sfrankho dev_t dev = fsp->pcfs_devvp->v_rdev; 15195121Sfrankho 15205121Sfrankho struct ipart dosp[FD_NUMPART]; /* incore fdisk partition structure */ 15215121Sfrankho daddr_t lastseek = 0; /* Disk block we sought previously */ 15225121Sfrankho daddr_t diskblk = 0; /* Disk block to get */ 15235121Sfrankho daddr_t xstartsect; /* base of Extended DOS partition */ 15245121Sfrankho int logicalDriveCount = 0; /* Count of logical drives seen */ 15255121Sfrankho int extendedPart = -1; /* index of extended dos partition */ 15265121Sfrankho int primaryPart = -1; /* index of primary dos partition */ 15275121Sfrankho int bootPart = -1; /* index of a Solaris boot partition */ 15287563SPrasad.Singamsetty@Sun.COM uint32_t xnumsect = 0; /* length of extended DOS partition */ 15295121Sfrankho int driveIndex; /* computed FDISK table index */ 15305121Sfrankho daddr_t startsec; 15315121Sfrankho len_t mediasize; 15325121Sfrankho int i; 15335121Sfrankho /* 15345121Sfrankho * Count of drives in the current extended partition's 15355121Sfrankho * FDISK table, and indexes of the drives themselves. 15365121Sfrankho */ 15375121Sfrankho int extndDrives[FD_NUMPART]; 15385121Sfrankho int numDrives = 0; 15395121Sfrankho 15405121Sfrankho /* 15415121Sfrankho * Count of drives (beyond primary) in master boot record's 15425121Sfrankho * FDISK table, and indexes of the drives themselves. 15435121Sfrankho */ 15445121Sfrankho int extraDrives[FD_NUMPART]; 15455121Sfrankho int numExtraDrives = 0; 15465121Sfrankho 15475121Sfrankho /* 15485121Sfrankho * "ldrive == 0" should never happen, as this is a request to 15495121Sfrankho * mount the physical device (and ignore partitioning). The code 15505121Sfrankho * in pcfs_mount() should have made sure that a logical drive number 15515121Sfrankho * is at least 1, meaning we're looking for drive "C:". It is not 15525121Sfrankho * safe (and a bug in the callers of this function) to request logical 15535121Sfrankho * drive number 0; we could ASSERT() but a graceful EIO is a more 15545121Sfrankho * polite way. 15555121Sfrankho */ 15565121Sfrankho if (ldrive == 0) { 15575121Sfrankho cmn_err(CE_NOTE, "!pcfs: request for logical partition zero"); 15585121Sfrankho noLogicalDrive(ldrive); 15595121Sfrankho return (EIO); 15605121Sfrankho } 15615121Sfrankho 15625121Sfrankho /* 15635121Sfrankho * Copy from disk block into memory aligned structure for fdisk usage. 15645121Sfrankho */ 15655121Sfrankho COPY_PTBL((*bp)->b_un.b_addr, dosp); 15665121Sfrankho 15675121Sfrankho /* 15685121Sfrankho * This check is ok because a FAT BPB and a master boot record (MBB) 15695121Sfrankho * have the same signature, in the same position within the block. 15705121Sfrankho */ 15715121Sfrankho if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) { 15725121Sfrankho cmn_err(CE_NOTE, "!pcfs: MBR partition table signature err, " 15735121Sfrankho "device (%x.%x):%d\n", 15745121Sfrankho getmajor(dev), getminor(dev), ldrive); 15755121Sfrankho return (EINVAL); 15765121Sfrankho } 15775121Sfrankho 15785121Sfrankho /* 15795121Sfrankho * Get a summary of what is in the Master FDISK table. 15805121Sfrankho * Normally we expect to find one partition marked as a DOS drive. 15815121Sfrankho * This partition is the one Windows calls the primary dos partition. 15825121Sfrankho * If the machine has any logical drives then we also expect 15835121Sfrankho * to find a partition marked as an extended DOS partition. 15845121Sfrankho * 15855121Sfrankho * Sometimes we'll find multiple partitions marked as DOS drives. 15865121Sfrankho * The Solaris fdisk program allows these partitions 15875121Sfrankho * to be created, but Windows fdisk no longer does. We still need 15885121Sfrankho * to support these, though, since Windows does. We also need to fix 15895121Sfrankho * our fdisk to behave like the Windows version. 15905121Sfrankho * 15915121Sfrankho * It turns out that some off-the-shelf media have *only* an 15925121Sfrankho * Extended partition, so we need to deal with that case as well. 15935121Sfrankho * 15945121Sfrankho * Only a single (the first) Extended or Boot Partition will 15955121Sfrankho * be recognized. Any others will be ignored. 15965121Sfrankho */ 15975121Sfrankho for (i = 0; i < FD_NUMPART; i++) { 15985121Sfrankho DTRACE_PROBE4(primarypart, struct pcfs *, fsp, 15995121Sfrankho uint_t, (uint_t)dosp[i].systid, 16005121Sfrankho uint_t, LE_32(dosp[i].relsect), 16015121Sfrankho uint_t, LE_32(dosp[i].numsect)); 16025121Sfrankho 16035121Sfrankho if (isDosDrive(dosp[i].systid)) { 16045121Sfrankho if (primaryPart < 0) { 16055121Sfrankho logicalDriveCount++; 16065121Sfrankho primaryPart = i; 16075121Sfrankho } else { 16085121Sfrankho extraDrives[numExtraDrives++] = i; 16095121Sfrankho } 16105121Sfrankho continue; 16115121Sfrankho } 16125121Sfrankho if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) { 16135121Sfrankho extendedPart = i; 16145121Sfrankho continue; 16155121Sfrankho } 16165121Sfrankho if ((bootPart < 0) && isBootPart(dosp[i].systid)) { 16175121Sfrankho bootPart = i; 16185121Sfrankho continue; 16195121Sfrankho } 16205121Sfrankho } 16215121Sfrankho 16225121Sfrankho if (ldrive == BOOT_PARTITION_DRIVE) { 16235121Sfrankho if (bootPart < 0) { 16245121Sfrankho noLogicalDrive(ldrive); 16255121Sfrankho return (EINVAL); 16265121Sfrankho } 16275121Sfrankho startsec = LE_32(dosp[bootPart].relsect); 16285121Sfrankho mediasize = LE_32(dosp[bootPart].numsect); 16295121Sfrankho goto found; 16305121Sfrankho } 16315121Sfrankho 16325121Sfrankho if (ldrive == PRIMARY_DOS_DRIVE && primaryPart >= 0) { 16335121Sfrankho startsec = LE_32(dosp[primaryPart].relsect); 16345121Sfrankho mediasize = LE_32(dosp[primaryPart].numsect); 16355121Sfrankho goto found; 16365121Sfrankho } 16375121Sfrankho 16385121Sfrankho /* 16395121Sfrankho * We are not looking for the C: drive (or the primary drive 16405121Sfrankho * was not found), so we had better have an extended partition 16415121Sfrankho * or extra drives in the Master FDISK table. 16425121Sfrankho */ 16435121Sfrankho if ((extendedPart < 0) && (numExtraDrives == 0)) { 16445121Sfrankho cmn_err(CE_NOTE, "!pcfs: no extended dos partition"); 16455121Sfrankho noLogicalDrive(ldrive); 16465121Sfrankho return (EINVAL); 16475121Sfrankho } 16485121Sfrankho 16495121Sfrankho if (extendedPart >= 0) { 16505121Sfrankho diskblk = xstartsect = LE_32(dosp[extendedPart].relsect); 16515121Sfrankho xnumsect = LE_32(dosp[extendedPart].numsect); 16525121Sfrankho do { 16535121Sfrankho /* 16545121Sfrankho * If the seek would not cause us to change 16555121Sfrankho * position on the drive, then we're out of 16565121Sfrankho * extended partitions to examine. 16575121Sfrankho */ 16585121Sfrankho if (diskblk == lastseek) 16595121Sfrankho break; 16605121Sfrankho logicalDriveCount += numDrives; 16615121Sfrankho /* 16625121Sfrankho * Seek the next extended partition, and find 16635121Sfrankho * logical drives within it. 16645121Sfrankho */ 16655121Sfrankho brelse(*bp); 16665121Sfrankho /* 16675121Sfrankho * bread() block numbers are multiples of DEV_BSIZE 16685121Sfrankho * but the device sector size (the unit of partitioning) 16695121Sfrankho * might be larger than that; pcfs_get_device_info() 16705121Sfrankho * has calculated the multiplicator for us. 16715121Sfrankho */ 16725121Sfrankho *bp = bread(dev, 16735121Sfrankho pc_dbdaddr(fsp, diskblk), fsp->pcfs_secsize); 16745121Sfrankho if ((*bp)->b_flags & B_ERROR) { 16755121Sfrankho return (EIO); 16765121Sfrankho } 16775121Sfrankho 16785121Sfrankho lastseek = diskblk; 16795121Sfrankho COPY_PTBL((*bp)->b_un.b_addr, dosp); 16805121Sfrankho if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) { 16815121Sfrankho cmn_err(CE_NOTE, "!pcfs: " 16825121Sfrankho "extended partition table signature err, " 16835121Sfrankho "device (%x.%x):%d, LBA %u", 16845121Sfrankho getmajor(dev), getminor(dev), ldrive, 16855121Sfrankho (uint_t)pc_dbdaddr(fsp, diskblk)); 16865121Sfrankho return (EINVAL); 16875121Sfrankho } 16885121Sfrankho /* 16895121Sfrankho * Count up drives, and track where the next 16905121Sfrankho * extended partition is in case we need it. We 16915121Sfrankho * are expecting only one extended partition. If 16925121Sfrankho * there is more than one we'll only go to the 16935121Sfrankho * first one we see, but warn about ignoring. 16945121Sfrankho */ 16955121Sfrankho numDrives = 0; 16965121Sfrankho for (i = 0; i < FD_NUMPART; i++) { 16975121Sfrankho DTRACE_PROBE4(extendedpart, 16985121Sfrankho struct pcfs *, fsp, 16995121Sfrankho uint_t, (uint_t)dosp[i].systid, 17005121Sfrankho uint_t, LE_32(dosp[i].relsect), 17015121Sfrankho uint_t, LE_32(dosp[i].numsect)); 17025121Sfrankho if (isDosDrive(dosp[i].systid)) { 17035121Sfrankho extndDrives[numDrives++] = i; 17045121Sfrankho } else if (isDosExtended(dosp[i].systid)) { 17055121Sfrankho if (diskblk != lastseek) { 17065121Sfrankho /* 17075121Sfrankho * Already found an extended 17085121Sfrankho * partition in this table. 17095121Sfrankho */ 17105121Sfrankho cmn_err(CE_NOTE, 17115121Sfrankho "!pcfs: ignoring unexpected" 17125121Sfrankho " additional extended" 17135121Sfrankho " partition"); 17145121Sfrankho } else { 17155121Sfrankho diskblk = xstartsect + 17165121Sfrankho LE_32(dosp[i].relsect); 17175121Sfrankho } 17185121Sfrankho } 17195121Sfrankho } 17205121Sfrankho } while (ldrive > logicalDriveCount + numDrives); 17215121Sfrankho 17225121Sfrankho ASSERT(numDrives <= FD_NUMPART); 17235121Sfrankho 17245121Sfrankho if (ldrive <= logicalDriveCount + numDrives) { 17255121Sfrankho /* 17265121Sfrankho * The number of logical drives we've found thus 17275121Sfrankho * far is enough to get us to the one we were 17285121Sfrankho * searching for. 17295121Sfrankho */ 17305121Sfrankho driveIndex = logicalDriveCount + numDrives - ldrive; 17315121Sfrankho mediasize = 17325121Sfrankho LE_32(dosp[extndDrives[driveIndex]].numsect); 17335121Sfrankho startsec = 17345121Sfrankho LE_32(dosp[extndDrives[driveIndex]].relsect) + 17355121Sfrankho lastseek; 17365121Sfrankho if (startsec > (xstartsect + xnumsect)) { 17375121Sfrankho cmn_err(CE_NOTE, "!pcfs: extended partition " 17385121Sfrankho "values bad"); 17395121Sfrankho return (EINVAL); 17405121Sfrankho } 17415121Sfrankho goto found; 17425121Sfrankho } else { 17435121Sfrankho /* 17445121Sfrankho * We ran out of extended dos partition 17455121Sfrankho * drives. The only hope now is to go 17465121Sfrankho * back to extra drives defined in the master 17475121Sfrankho * fdisk table. But we overwrote that table 17485121Sfrankho * already, so we must load it in again. 17495121Sfrankho */ 17505121Sfrankho logicalDriveCount += numDrives; 17515121Sfrankho brelse(*bp); 17525121Sfrankho ASSERT(fsp->pcfs_dosstart == 0); 17535121Sfrankho *bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), 17545121Sfrankho fsp->pcfs_secsize); 17555121Sfrankho if ((*bp)->b_flags & B_ERROR) { 17565121Sfrankho return (EIO); 17575121Sfrankho } 17585121Sfrankho COPY_PTBL((*bp)->b_un.b_addr, dosp); 17595121Sfrankho } 17605121Sfrankho } 17615121Sfrankho /* 17625121Sfrankho * Still haven't found the drive, is it an extra 17635121Sfrankho * drive defined in the main FDISK table? 17645121Sfrankho */ 17655121Sfrankho if (ldrive <= logicalDriveCount + numExtraDrives) { 17665121Sfrankho driveIndex = logicalDriveCount + numExtraDrives - ldrive; 17675121Sfrankho ASSERT(driveIndex < MIN(numExtraDrives, FD_NUMPART)); 17685121Sfrankho mediasize = LE_32(dosp[extraDrives[driveIndex]].numsect); 17695121Sfrankho startsec = LE_32(dosp[extraDrives[driveIndex]].relsect); 17705121Sfrankho goto found; 17715121Sfrankho } 17725121Sfrankho /* 17735121Sfrankho * Still haven't found the drive, and there is 17745121Sfrankho * nowhere else to look. 17755121Sfrankho */ 17765121Sfrankho noLogicalDrive(ldrive); 17775121Sfrankho return (EINVAL); 17785121Sfrankho 17795121Sfrankho found: 17805121Sfrankho /* 17815121Sfrankho * We need this value in units of sectorsize, because PCFS' internal 17825121Sfrankho * offset calculations go haywire for > 512Byte sectors unless all 17835121Sfrankho * pcfs_.*start values are in units of sectors. 17845121Sfrankho * So, assign before the capacity check (that's done in DEV_BSIZE) 17855121Sfrankho */ 17865121Sfrankho fsp->pcfs_dosstart = startsec; 17875121Sfrankho 17885121Sfrankho /* 17895121Sfrankho * convert from device sectors to proper units: 17905121Sfrankho * - starting sector: DEV_BSIZE (as argument to bread()) 17915121Sfrankho * - media size: Bytes 17925121Sfrankho */ 17935121Sfrankho startsec = pc_dbdaddr(fsp, startsec); 17945121Sfrankho mediasize *= fsp->pcfs_secsize; 17955121Sfrankho 17965121Sfrankho /* 17975121Sfrankho * some additional validation / warnings in case the partition table 17985121Sfrankho * and the actual media capacity are not in accordance ... 17995121Sfrankho */ 18005121Sfrankho if (fsp->pcfs_mediasize != 0) { 18015121Sfrankho diskaddr_t startoff = 18025121Sfrankho (diskaddr_t)startsec * (diskaddr_t)DEV_BSIZE; 18035121Sfrankho 18045121Sfrankho if (startoff >= fsp->pcfs_mediasize || 18055121Sfrankho startoff + mediasize > fsp->pcfs_mediasize) { 18065121Sfrankho cmn_err(CE_WARN, 18075121Sfrankho "!pcfs: partition size (LBA start %u, %lld bytes, " 18085121Sfrankho "device (%x.%x):%d) smaller than " 18095121Sfrankho "mediasize (%lld bytes).\n" 18105121Sfrankho "filesystem may be truncated, access errors " 18115121Sfrankho "may result.\n", 18125121Sfrankho (uint_t)startsec, (long long)mediasize, 18135121Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 18145121Sfrankho fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize); 18155121Sfrankho } 18165121Sfrankho } else { 18175121Sfrankho fsp->pcfs_mediasize = mediasize; 18185121Sfrankho } 18195121Sfrankho 18205121Sfrankho return (0); 18215121Sfrankho } 18225121Sfrankho 18235121Sfrankho 18245121Sfrankho static fattype_t 18255121Sfrankho secondaryBPBChecks(struct pcfs *fsp, uchar_t *bpb, size_t secsize) 18265121Sfrankho { 18275121Sfrankho uint32_t ncl = fsp->pcfs_ncluster; 18285121Sfrankho 18295121Sfrankho if (ncl <= 4096) { 18305121Sfrankho if (bpb_get_FatSz16(bpb) == 0) 18315121Sfrankho return (FAT_UNKNOWN); 18325121Sfrankho 18335121Sfrankho if (bpb_get_FatSz16(bpb) * secsize < ncl * 2 && 18345121Sfrankho bpb_get_FatSz16(bpb) * secsize >= (3 * ncl / 2)) 18355121Sfrankho return (FAT12); 18365121Sfrankho if (bcmp(bpb_FilSysType16(bpb), "FAT12", 5) == 0) 18375121Sfrankho return (FAT12); 18385121Sfrankho if (bcmp(bpb_FilSysType16(bpb), "FAT16", 5) == 0) 18395121Sfrankho return (FAT16); 18405121Sfrankho 18415121Sfrankho switch (bpb_get_Media(bpb)) { 18425121Sfrankho case SS8SPT: 18435121Sfrankho case DS8SPT: 18445121Sfrankho case SS9SPT: 18455121Sfrankho case DS9SPT: 18465121Sfrankho case DS18SPT: 18475121Sfrankho case DS9_15SPT: 18485121Sfrankho /* 18495121Sfrankho * Is this reliable - all floppies are FAT12 ? 18505121Sfrankho */ 18515121Sfrankho return (FAT12); 18525121Sfrankho case MD_FIXED: 18535121Sfrankho /* 18545121Sfrankho * Is this reliable - disks are always FAT16 ? 18555121Sfrankho */ 18565121Sfrankho return (FAT16); 18575121Sfrankho default: 18585121Sfrankho break; 18595121Sfrankho } 18605121Sfrankho } else if (ncl <= 65536) { 18615121Sfrankho if (bpb_get_FatSz16(bpb) == 0 && bpb_get_FatSz32(bpb) > 0) 18625121Sfrankho return (FAT32); 18635121Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig32(bpb))) 18645121Sfrankho return (FAT32); 18655121Sfrankho if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb))) 18665121Sfrankho return (FAT32); 18675121Sfrankho 18685121Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig16(bpb))) 18695121Sfrankho return (FAT16); 18705121Sfrankho if (bpb_get_FatSz16(bpb) * secsize < ncl * 4) 18715121Sfrankho return (FAT16); 18725121Sfrankho } 18735121Sfrankho 18745121Sfrankho /* 18755121Sfrankho * We don't know 18765121Sfrankho */ 18775121Sfrankho return (FAT_UNKNOWN); 18785121Sfrankho } 18795121Sfrankho 18805121Sfrankho /* 18815121Sfrankho * Check to see if the BPB we found is correct. 18825121Sfrankho * 18835121Sfrankho * This looks far more complicated that it needs to be for pure structural 18845121Sfrankho * validation. The reason for this is that parseBPB() is also used for 18855121Sfrankho * debugging purposes (mdb dcmd) and we therefore want a bitmap of which 18866644Sgd78059 * BPB fields (do not) have 'known good' values, even if we (do not) reject 18876644Sgd78059 * the BPB when attempting to mount the filesystem. 18886644Sgd78059 * 18896644Sgd78059 * Real-world usage of FAT shows there are a lot of corner-case situations 18906644Sgd78059 * and, following the specification strictly, invalid filesystems out there. 18916644Sgd78059 * Known are situations such as: 18926644Sgd78059 * - FAT12/FAT16 filesystems with garbage in either totsec16/32 18936644Sgd78059 * instead of the zero in one of the fields mandated by the spec 18946644Sgd78059 * - filesystems that claim to be larger than the partition they're in 18956644Sgd78059 * - filesystems without valid media descriptor 18966644Sgd78059 * - FAT32 filesystems with RootEntCnt != 0 18976644Sgd78059 * - FAT32 filesystems with less than 65526 clusters 18986644Sgd78059 * - FAT32 filesystems without valid FSI sector 18996644Sgd78059 * - FAT32 filesystems with FAT size in fatsec16 instead of fatsec32 19006644Sgd78059 * 19016644Sgd78059 * Such filesystems are accessible by PCFS - if it'd know to start with that 19026644Sgd78059 * the filesystem should be treated as a specific FAT type. Before S10, it 19036644Sgd78059 * relied on the PC/fdisk partition type for the purpose and almost completely 19046644Sgd78059 * ignored the BPB; now it ignores the partition type for anything else but 19056644Sgd78059 * logical drive enumeration, which can result in rejection of (invalid) 19066644Sgd78059 * FAT32 - if the partition ID says FAT32, but the filesystem, for example 19076644Sgd78059 * has less than 65526 clusters. 19086644Sgd78059 * 19096644Sgd78059 * Without a "force this fs as FAT{12,16,32}" tunable or mount option, it's 19106644Sgd78059 * not possible to allow all such mostly-compliant filesystems in unless one 19116644Sgd78059 * accepts false positives (definitely invalid filesystems that cause problems 19126644Sgd78059 * later). This at least allows to pinpoint why the mount failed. 19136644Sgd78059 * 19146644Sgd78059 * Due to the use of FAT on removeable media, all relaxations of the rules 19156644Sgd78059 * here need to be carefully evaluated wrt. to potential effects on PCFS 19166644Sgd78059 * resilience. A faulty/"mis-crafted" filesystem must not cause a panic, so 19176644Sgd78059 * beware. 19185121Sfrankho */ 19195121Sfrankho static int 19205121Sfrankho parseBPB(struct pcfs *fsp, uchar_t *bpb, int *valid) 19215121Sfrankho { 19225121Sfrankho fattype_t type; 19235121Sfrankho 19245121Sfrankho uint32_t ncl; /* number of clusters in file area */ 19255121Sfrankho uint32_t rec; 19265121Sfrankho uint32_t reserved; 19275121Sfrankho uint32_t fsisec, bkbootsec; 19285121Sfrankho blkcnt_t totsec, totsec16, totsec32, datasec; 19295121Sfrankho size_t fatsec, fatsec16, fatsec32, rdirsec; 19305121Sfrankho size_t secsize; 19315121Sfrankho len_t mediasize; 19325121Sfrankho uint64_t validflags = 0; 19335121Sfrankho 19345121Sfrankho if (VALID_BPBSIG(bpb_get_BPBSig(bpb))) 19355121Sfrankho validflags |= BPB_BPBSIG_OK; 19365121Sfrankho 19375121Sfrankho rec = bpb_get_RootEntCnt(bpb); 19385121Sfrankho reserved = bpb_get_RsvdSecCnt(bpb); 19395121Sfrankho fsisec = bpb_get_FSInfo32(bpb); 19405121Sfrankho bkbootsec = bpb_get_BkBootSec32(bpb); 19415121Sfrankho totsec16 = (blkcnt_t)bpb_get_TotSec16(bpb); 19425121Sfrankho totsec32 = (blkcnt_t)bpb_get_TotSec32(bpb); 19435121Sfrankho fatsec16 = bpb_get_FatSz16(bpb); 19445121Sfrankho fatsec32 = bpb_get_FatSz32(bpb); 19455121Sfrankho 19465121Sfrankho totsec = totsec16 ? totsec16 : totsec32; 19475121Sfrankho fatsec = fatsec16 ? fatsec16 : fatsec32; 19485121Sfrankho 19495121Sfrankho secsize = bpb_get_BytesPerSec(bpb); 19505121Sfrankho if (!VALID_SECSIZE(secsize)) 19515121Sfrankho secsize = fsp->pcfs_secsize; 19525121Sfrankho if (secsize != fsp->pcfs_secsize) { 19535121Sfrankho PC_DPRINTF3(3, "!pcfs: parseBPB, device (%x.%x):%d:\n", 19545121Sfrankho getmajor(fsp->pcfs_xdev), 19555121Sfrankho getminor(fsp->pcfs_xdev), fsp->pcfs_ldrive); 19565121Sfrankho PC_DPRINTF2(3, "!BPB secsize %d != " 19575121Sfrankho "autodetected media block size %d\n", 19585121Sfrankho (int)secsize, (int)fsp->pcfs_secsize); 19595121Sfrankho if (fsp->pcfs_ldrive) { 19605121Sfrankho /* 19615121Sfrankho * We've already attempted to parse the partition 19625121Sfrankho * table. If the block size used for that don't match 19635121Sfrankho * the PCFS sector size, we're hosed one way or the 19645121Sfrankho * other. Just try what happens. 19655121Sfrankho */ 19665121Sfrankho secsize = fsp->pcfs_secsize; 19675121Sfrankho PC_DPRINTF1(3, 19685121Sfrankho "!pcfs: Using autodetected secsize %d\n", 19695121Sfrankho (int)secsize); 19705121Sfrankho } else { 19715121Sfrankho /* 19725121Sfrankho * This allows mounting lofi images of PCFS partitions 19735121Sfrankho * with sectorsize != DEV_BSIZE. We can't parse the 19745121Sfrankho * partition table on whole-disk images unless the 19755121Sfrankho * (undocumented) "secsize=..." mount option is used, 19765121Sfrankho * but at least this allows us to mount if we have 19775121Sfrankho * an image of a partition. 19785121Sfrankho */ 19795121Sfrankho PC_DPRINTF1(3, 19805121Sfrankho "!pcfs: Using BPB secsize %d\n", (int)secsize); 19815121Sfrankho } 19825121Sfrankho } 19835121Sfrankho 19845121Sfrankho if (fsp->pcfs_mediasize == 0) { 19855121Sfrankho mediasize = (len_t)totsec * (len_t)secsize; 19866644Sgd78059 /* 19876644Sgd78059 * This is not an error because not all devices support the 19886644Sgd78059 * dkio(7i) mediasize queries, and/or not all devices are 19896644Sgd78059 * partitioned. If we have not been able to figure out the 19906644Sgd78059 * size of the underlaying medium, we have to trust the BPB. 19916644Sgd78059 */ 19925121Sfrankho PC_DPRINTF4(3, "!pcfs: parseBPB: mediasize autodetect failed " 19935121Sfrankho "on device (%x.%x):%d, trusting BPB totsec (%lld Bytes)\n", 19945121Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 19955121Sfrankho fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize); 19965121Sfrankho } else if ((len_t)totsec * (len_t)secsize > fsp->pcfs_mediasize) { 19975121Sfrankho cmn_err(CE_WARN, 19985121Sfrankho "!pcfs: autodetected mediasize (%lld Bytes) smaller than " 19995121Sfrankho "FAT BPB mediasize (%lld Bytes).\n" 20005121Sfrankho "truncated filesystem on device (%x.%x):%d, access errors " 20015121Sfrankho "possible.\n", 20025121Sfrankho (long long)fsp->pcfs_mediasize, 20035121Sfrankho (long long)(totsec * (blkcnt_t)secsize), 20045121Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 20055121Sfrankho fsp->pcfs_ldrive); 20065121Sfrankho mediasize = fsp->pcfs_mediasize; 20075121Sfrankho } else { 20085121Sfrankho /* 20095121Sfrankho * This is actually ok. A FAT needs not occupy the maximum 20105121Sfrankho * space available in its partition, it can be shorter. 20115121Sfrankho */ 20125121Sfrankho mediasize = (len_t)totsec * (len_t)secsize; 20135121Sfrankho } 20145121Sfrankho 20155121Sfrankho /* 20165121Sfrankho * Since we let just about anything pass through this function, 20175121Sfrankho * fence against divide-by-zero here. 20185121Sfrankho */ 20195121Sfrankho if (secsize) 20205121Sfrankho rdirsec = roundup(rec * 32, secsize) / secsize; 20215121Sfrankho else 20225121Sfrankho rdirsec = 0; 20235121Sfrankho 20245121Sfrankho /* 20255121Sfrankho * This assignment is necessary before pc_dbdaddr() can first be 20265121Sfrankho * used. Must initialize the value here. 20275121Sfrankho */ 20285121Sfrankho fsp->pcfs_secsize = secsize; 20295121Sfrankho fsp->pcfs_sdshift = ddi_ffs(secsize / DEV_BSIZE) - 1; 20305121Sfrankho 20315121Sfrankho fsp->pcfs_mediasize = mediasize; 20325121Sfrankho 20335121Sfrankho fsp->pcfs_spcl = bpb_get_SecPerClus(bpb); 20345121Sfrankho fsp->pcfs_numfat = bpb_get_NumFATs(bpb); 20355121Sfrankho fsp->pcfs_mediadesc = bpb_get_Media(bpb); 20365121Sfrankho fsp->pcfs_clsize = secsize * fsp->pcfs_spcl; 20375121Sfrankho fsp->pcfs_rdirsec = rdirsec; 20385121Sfrankho 20395121Sfrankho /* 20405121Sfrankho * Remember: All PCFS offset calculations in sectors. Before I/O 20415121Sfrankho * is done, convert to DEV_BSIZE units via pc_dbdaddr(). This is 20425121Sfrankho * necessary so that media with > 512Byte sector sizes work correctly. 20435121Sfrankho */ 20445121Sfrankho fsp->pcfs_fatstart = fsp->pcfs_dosstart + reserved; 20455121Sfrankho fsp->pcfs_rdirstart = fsp->pcfs_fatstart + fsp->pcfs_numfat * fatsec; 20465121Sfrankho fsp->pcfs_datastart = fsp->pcfs_rdirstart + rdirsec; 20475121Sfrankho datasec = totsec - 20485121Sfrankho (blkcnt_t)fatsec * fsp->pcfs_numfat - 20495121Sfrankho (blkcnt_t)rdirsec - 20505121Sfrankho (blkcnt_t)reserved; 20515121Sfrankho 20525121Sfrankho DTRACE_PROBE4(fatgeometry, 20535121Sfrankho blkcnt_t, totsec, size_t, fatsec, 20545121Sfrankho size_t, rdirsec, blkcnt_t, datasec); 20555121Sfrankho 20565121Sfrankho /* 20576644Sgd78059 * 'totsec' is taken directly from the BPB and guaranteed to fit 20586644Sgd78059 * into a 32bit unsigned integer. The calculation of 'datasec', 20596644Sgd78059 * on the other hand, could underflow for incorrect values in 20606644Sgd78059 * rdirsec/reserved/fatsec. Check for that. 20616644Sgd78059 * We also check that the BPB conforms to the FAT specification's 20626644Sgd78059 * requirement that either of the 16/32bit total sector counts 20636644Sgd78059 * must be zero. 20645121Sfrankho */ 20655121Sfrankho if (totsec != 0 && 20665121Sfrankho (totsec16 == totsec32 || totsec16 == 0 || totsec32 == 0) && 20675121Sfrankho datasec < totsec && datasec <= UINT32_MAX) 20685121Sfrankho validflags |= BPB_TOTSEC_OK; 20695121Sfrankho 20706644Sgd78059 if ((len_t)totsec * (len_t)secsize <= mediasize) 20715121Sfrankho validflags |= BPB_MEDIASZ_OK; 20725121Sfrankho 20735121Sfrankho if (VALID_SECSIZE(secsize)) 20745121Sfrankho validflags |= BPB_SECSIZE_OK; 20755121Sfrankho if (VALID_SPCL(fsp->pcfs_spcl)) 20765121Sfrankho validflags |= BPB_SECPERCLUS_OK; 20775121Sfrankho if (VALID_CLSIZE(fsp->pcfs_clsize)) 20785121Sfrankho validflags |= BPB_CLSIZE_OK; 20795121Sfrankho if (VALID_NUMFATS(fsp->pcfs_numfat)) 20805121Sfrankho validflags |= BPB_NUMFAT_OK; 20815121Sfrankho if (VALID_RSVDSEC(reserved) && reserved < totsec) 20825121Sfrankho validflags |= BPB_RSVDSECCNT_OK; 20835121Sfrankho if (VALID_MEDIA(fsp->pcfs_mediadesc)) 20845121Sfrankho validflags |= BPB_MEDIADESC_OK; 20855121Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig16(bpb))) 20865121Sfrankho validflags |= BPB_BOOTSIG16_OK; 20875121Sfrankho if (VALID_BOOTSIG(bpb_get_BootSig32(bpb))) 20885121Sfrankho validflags |= BPB_BOOTSIG32_OK; 20895121Sfrankho if (VALID_FSTYPSTR16(bpb_FilSysType16(bpb))) 20905121Sfrankho validflags |= BPB_FSTYPSTR16_OK; 20915121Sfrankho if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb))) 20925121Sfrankho validflags |= BPB_FSTYPSTR32_OK; 20935121Sfrankho if (VALID_OEMNAME(bpb_OEMName(bpb))) 20945121Sfrankho validflags |= BPB_OEMNAME_OK; 20955121Sfrankho if (bkbootsec > 0 && bkbootsec <= reserved && fsisec != bkbootsec) 20965121Sfrankho validflags |= BPB_BKBOOTSEC_OK; 20975121Sfrankho if (fsisec > 0 && fsisec <= reserved) 20985121Sfrankho validflags |= BPB_FSISEC_OK; 20995121Sfrankho if (VALID_JMPBOOT(bpb_jmpBoot(bpb))) 21005121Sfrankho validflags |= BPB_JMPBOOT_OK; 21015121Sfrankho if (VALID_FSVER32(bpb_get_FSVer32(bpb))) 21025121Sfrankho validflags |= BPB_FSVER_OK; 21035121Sfrankho if (VALID_VOLLAB(bpb_VolLab16(bpb))) 21045121Sfrankho validflags |= BPB_VOLLAB16_OK; 21055121Sfrankho if (VALID_VOLLAB(bpb_VolLab32(bpb))) 21065121Sfrankho validflags |= BPB_VOLLAB32_OK; 21075121Sfrankho if (VALID_EXTFLAGS(bpb_get_ExtFlags32(bpb))) 21085121Sfrankho validflags |= BPB_EXTFLAGS_OK; 21095121Sfrankho 21105121Sfrankho /* 21115121Sfrankho * Try to determine which FAT format to use. 21125121Sfrankho * 21135121Sfrankho * Calculate the number of clusters in order to determine 21145121Sfrankho * the type of FAT we are looking at. This is the only 21155121Sfrankho * recommended way of determining FAT type, though there 21165121Sfrankho * are other hints in the data, this is the best way. 21175121Sfrankho * 21185121Sfrankho * Since we let just about "anything" pass through this function 21195121Sfrankho * without early exits, fence against divide-by-zero here. 21205121Sfrankho * 21215121Sfrankho * datasec was already validated against UINT32_MAX so we know 21225121Sfrankho * the result will not overflow the 32bit calculation. 21235121Sfrankho */ 21245121Sfrankho if (fsp->pcfs_spcl) 21255121Sfrankho ncl = (uint32_t)datasec / fsp->pcfs_spcl; 21265121Sfrankho else 21275121Sfrankho ncl = 0; 21285121Sfrankho 21295121Sfrankho fsp->pcfs_ncluster = ncl; 21305121Sfrankho 21315121Sfrankho /* 21325121Sfrankho * From the Microsoft FAT specification: 21335121Sfrankho * In the following example, when it says <, it does not mean <=. 21345121Sfrankho * Note also that the numbers are correct. The first number for 21355121Sfrankho * FAT12 is 4085; the second number for FAT16 is 65525. These numbers 21365121Sfrankho * and the '<' signs are not wrong. 21375121Sfrankho * 21385121Sfrankho * We "specialdetect" the corner cases, and use at least one "extra" 21395121Sfrankho * criterion to decide whether it's FAT16 or FAT32 if the cluster 21405121Sfrankho * count is dangerously close to the boundaries. 21415121Sfrankho */ 21425121Sfrankho 21435121Sfrankho if (ncl <= PCF_FIRSTCLUSTER) { 21445121Sfrankho type = FAT_UNKNOWN; 21455121Sfrankho } else if (ncl < 4085) { 21465121Sfrankho type = FAT12; 21475121Sfrankho } else if (ncl <= 4096) { 21485121Sfrankho type = FAT_QUESTIONABLE; 21495121Sfrankho } else if (ncl < 65525) { 21505121Sfrankho type = FAT16; 21515121Sfrankho } else if (ncl <= 65536) { 21525121Sfrankho type = FAT_QUESTIONABLE; 21535121Sfrankho } else if (ncl < PCF_LASTCLUSTER32) { 21545121Sfrankho type = FAT32; 21555121Sfrankho } else { 21565121Sfrankho type = FAT_UNKNOWN; 21575121Sfrankho } 21585121Sfrankho 21595121Sfrankho DTRACE_PROBE4(parseBPB__initial, 21605121Sfrankho struct pcfs *, fsp, unsigned char *, bpb, 21615121Sfrankho int, validflags, fattype_t, type); 21625121Sfrankho 21635121Sfrankho recheck: 21645121Sfrankho fsp->pcfs_fatsec = fatsec; 21655121Sfrankho 21665121Sfrankho /* Do some final sanity checks for each specific type of FAT */ 21675121Sfrankho switch (type) { 21685121Sfrankho case FAT12: 21695121Sfrankho if (rec != 0) 21705121Sfrankho validflags |= BPB_ROOTENTCNT_OK; 21715121Sfrankho if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec || 21725121Sfrankho bpb_get_TotSec16(bpb) == 0) 21735121Sfrankho validflags |= BPB_TOTSEC16_OK; 21745121Sfrankho if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec || 21755121Sfrankho bpb_get_TotSec32(bpb) == 0) 21765121Sfrankho validflags |= BPB_TOTSEC32_OK; 21775121Sfrankho if (bpb_get_FatSz16(bpb) == fatsec) 21785121Sfrankho validflags |= BPB_FATSZ16_OK; 21795121Sfrankho if (fatsec * secsize >= ncl * 3 / 2) 21805121Sfrankho validflags |= BPB_FATSZ_OK; 21815121Sfrankho if (ncl < 4085) 21825121Sfrankho validflags |= BPB_NCLUSTERS_OK; 21835121Sfrankho 21845121Sfrankho fsp->pcfs_lastclmark = (PCF_LASTCLUSTER & 0xfff); 21855121Sfrankho fsp->pcfs_rootblksize = 21865121Sfrankho fsp->pcfs_rdirsec * secsize; 21875121Sfrankho fsp->pcfs_fsistart = 0; 21885121Sfrankho 21895121Sfrankho if ((validflags & FAT12_VALIDMSK) != FAT12_VALIDMSK) 21905121Sfrankho type = FAT_UNKNOWN; 21915121Sfrankho break; 21925121Sfrankho case FAT16: 21935121Sfrankho if (rec != 0) 21945121Sfrankho validflags |= BPB_ROOTENTCNT_OK; 21955121Sfrankho if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec || 21965121Sfrankho bpb_get_TotSec16(bpb) == 0) 21975121Sfrankho validflags |= BPB_TOTSEC16_OK; 21985121Sfrankho if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec || 21995121Sfrankho bpb_get_TotSec32(bpb) == 0) 22005121Sfrankho validflags |= BPB_TOTSEC32_OK; 22015121Sfrankho if (bpb_get_FatSz16(bpb) == fatsec) 22025121Sfrankho validflags |= BPB_FATSZ16_OK; 22035121Sfrankho if (fatsec * secsize >= ncl * 2) 22045121Sfrankho validflags |= BPB_FATSZ_OK; 22055121Sfrankho if (ncl >= 4085 && ncl < 65525) 22065121Sfrankho validflags |= BPB_NCLUSTERS_OK; 22075121Sfrankho 22085121Sfrankho fsp->pcfs_lastclmark = PCF_LASTCLUSTER; 22095121Sfrankho fsp->pcfs_rootblksize = 22105121Sfrankho fsp->pcfs_rdirsec * secsize; 22115121Sfrankho fsp->pcfs_fsistart = 0; 22125121Sfrankho 22135121Sfrankho if ((validflags & FAT16_VALIDMSK) != FAT16_VALIDMSK) 22145121Sfrankho type = FAT_UNKNOWN; 22155121Sfrankho break; 22165121Sfrankho case FAT32: 22175121Sfrankho if (rec == 0) 22185121Sfrankho validflags |= BPB_ROOTENTCNT_OK; 22195121Sfrankho if (bpb_get_TotSec16(bpb) == 0) 22205121Sfrankho validflags |= BPB_TOTSEC16_OK; 22215121Sfrankho if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec) 22225121Sfrankho validflags |= BPB_TOTSEC32_OK; 22235121Sfrankho if (bpb_get_FatSz16(bpb) == 0) 22245121Sfrankho validflags |= BPB_FATSZ16_OK; 22255121Sfrankho if (bpb_get_FatSz32(bpb) == fatsec) 22265121Sfrankho validflags |= BPB_FATSZ32_OK; 22275121Sfrankho if (fatsec * secsize >= ncl * 4) 22285121Sfrankho validflags |= BPB_FATSZ_OK; 22295121Sfrankho if (ncl >= 65525 && ncl < PCF_LASTCLUSTER32) 22305121Sfrankho validflags |= BPB_NCLUSTERS_OK; 22315121Sfrankho 22325121Sfrankho fsp->pcfs_lastclmark = PCF_LASTCLUSTER32; 22335121Sfrankho fsp->pcfs_rootblksize = fsp->pcfs_clsize; 22345121Sfrankho fsp->pcfs_fsistart = fsp->pcfs_dosstart + fsisec; 22355121Sfrankho if (validflags & BPB_FSISEC_OK) 22365121Sfrankho fsp->pcfs_flags |= PCFS_FSINFO_OK; 22375121Sfrankho fsp->pcfs_rootclnum = bpb_get_RootClus32(bpb); 22385121Sfrankho if (pc_validcl(fsp, fsp->pcfs_rootclnum)) 22395121Sfrankho validflags |= BPB_ROOTCLUSTER_OK; 22405121Sfrankho 22415121Sfrankho /* 22425121Sfrankho * Current PCFS code only works if 'pcfs_rdirstart' 22435121Sfrankho * contains the root cluster number on FAT32. 22445121Sfrankho * That's a mis-use and would better be changed. 22455121Sfrankho */ 22465121Sfrankho fsp->pcfs_rdirstart = (daddr_t)fsp->pcfs_rootclnum; 22475121Sfrankho 22485121Sfrankho if ((validflags & FAT32_VALIDMSK) != FAT32_VALIDMSK) 22495121Sfrankho type = FAT_UNKNOWN; 22505121Sfrankho break; 22515121Sfrankho case FAT_QUESTIONABLE: 22525121Sfrankho type = secondaryBPBChecks(fsp, bpb, secsize); 22535121Sfrankho goto recheck; 22545121Sfrankho default: 22555121Sfrankho ASSERT(type == FAT_UNKNOWN); 22565121Sfrankho break; 22575121Sfrankho } 22585121Sfrankho 22595121Sfrankho ASSERT(type != FAT_QUESTIONABLE); 22605121Sfrankho 22615121Sfrankho fsp->pcfs_fattype = type; 22625121Sfrankho 22635121Sfrankho if (valid) 22645121Sfrankho *valid = validflags; 22655121Sfrankho 22665121Sfrankho DTRACE_PROBE4(parseBPB__final, 22675121Sfrankho struct pcfs *, fsp, unsigned char *, bpb, 22685121Sfrankho int, validflags, fattype_t, type); 22695121Sfrankho 22705121Sfrankho if (type != FAT_UNKNOWN) { 22715121Sfrankho ASSERT((secsize & (DEV_BSIZE - 1)) == 0); 22725121Sfrankho ASSERT(ISP2(secsize / DEV_BSIZE)); 22735121Sfrankho return (1); 22745121Sfrankho } 22755121Sfrankho 22765121Sfrankho return (0); 22775121Sfrankho } 22785121Sfrankho 22795121Sfrankho 22805121Sfrankho /* 22815121Sfrankho * Detect the device's native block size (sector size). 22825121Sfrankho * 22835121Sfrankho * Test whether the device is: 22845121Sfrankho * - a floppy device from a known controller type via DKIOCINFO 22855121Sfrankho * - a real floppy using the fd(7d) driver and capable of fdio(7I) ioctls 22865121Sfrankho * - a USB floppy drive (identified by drive geometry) 22875121Sfrankho * 22885121Sfrankho * Detecting a floppy will make PCFS metadata updates on such media synchronous, 22895121Sfrankho * to minimize risks due to slow I/O and user hotplugging / device ejection. 22905121Sfrankho * 22915121Sfrankho * This might be a bit wasteful on kernel stack space; if anyone's 22925121Sfrankho * bothered by this, kmem_alloc/kmem_free the ioctl arguments... 22935121Sfrankho */ 22945121Sfrankho static void 22955121Sfrankho pcfs_device_getinfo(struct pcfs *fsp) 22965121Sfrankho { 22975121Sfrankho dev_t rdev = fsp->pcfs_xdev; 22985121Sfrankho int error; 22995121Sfrankho union { 23005121Sfrankho struct dk_minfo mi; 23015121Sfrankho struct dk_cinfo ci; 23025121Sfrankho struct dk_geom gi; 23035121Sfrankho struct fd_char fc; 23045121Sfrankho } arg; /* save stackspace ... */ 23055121Sfrankho intptr_t argp = (intptr_t)&arg; 23065121Sfrankho ldi_handle_t lh; 23075121Sfrankho ldi_ident_t li; 23085121Sfrankho int isfloppy, isremoveable, ishotpluggable; 23095121Sfrankho cred_t *cr = CRED(); 23105121Sfrankho 23115121Sfrankho if (ldi_ident_from_dev(rdev, &li)) 23125121Sfrankho goto out; 23135121Sfrankho 23145121Sfrankho error = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, cr, &lh, li); 23155121Sfrankho ldi_ident_release(li); 23165121Sfrankho if (error) 23175121Sfrankho goto out; 23185121Sfrankho 23195121Sfrankho /* 23205121Sfrankho * Not sure if this could possibly happen. It'd be a bit like 23215121Sfrankho * VOP_OPEN() changing the passed-in vnode ptr. We're just not 23225121Sfrankho * expecting it, needs some thought if triggered ... 23235121Sfrankho */ 23245121Sfrankho ASSERT(fsp->pcfs_xdev == rdev); 23255121Sfrankho 23265121Sfrankho /* 23275121Sfrankho * Check for removeable/hotpluggable media. 23285121Sfrankho */ 23295121Sfrankho if (ldi_ioctl(lh, DKIOCREMOVABLE, 23305121Sfrankho (intptr_t)&isremoveable, FKIOCTL, cr, NULL)) { 23315121Sfrankho isremoveable = 0; 23325121Sfrankho } 23335121Sfrankho if (ldi_ioctl(lh, DKIOCHOTPLUGGABLE, 23345121Sfrankho (intptr_t)&ishotpluggable, FKIOCTL, cr, NULL)) { 23355121Sfrankho ishotpluggable = 0; 23365121Sfrankho } 23375121Sfrankho 23385121Sfrankho /* 23395121Sfrankho * Make sure we don't use "half-initialized" values if the ioctls fail. 23405121Sfrankho */ 23415121Sfrankho if (ldi_ioctl(lh, DKIOCGMEDIAINFO, argp, FKIOCTL, cr, NULL)) { 23425121Sfrankho bzero(&arg, sizeof (arg)); 23435121Sfrankho fsp->pcfs_mediasize = 0; 23445121Sfrankho } else { 23455121Sfrankho fsp->pcfs_mediasize = 23465121Sfrankho (len_t)arg.mi.dki_lbsize * 23475121Sfrankho (len_t)arg.mi.dki_capacity; 23485121Sfrankho } 23495121Sfrankho 23505121Sfrankho if (VALID_SECSIZE(arg.mi.dki_lbsize)) { 23515121Sfrankho if (fsp->pcfs_secsize == 0) { 23525121Sfrankho fsp->pcfs_secsize = arg.mi.dki_lbsize; 23535121Sfrankho fsp->pcfs_sdshift = 23545121Sfrankho ddi_ffs(arg.mi.dki_lbsize / DEV_BSIZE) - 1; 23555121Sfrankho } else { 23565121Sfrankho PC_DPRINTF4(1, "!pcfs: autodetected media block size " 23575121Sfrankho "%d, device (%x.%x), different from user-provided " 23585121Sfrankho "%d. User override - ignoring autodetect result.\n", 23595121Sfrankho arg.mi.dki_lbsize, 23605121Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 23615121Sfrankho fsp->pcfs_secsize); 23625121Sfrankho } 23635121Sfrankho } else if (arg.mi.dki_lbsize) { 23645121Sfrankho PC_DPRINTF3(1, "!pcfs: autodetected media block size " 23655121Sfrankho "%d, device (%x.%x), invalid (not 512, 1024, 2048, 4096). " 23665121Sfrankho "Ignoring autodetect result.\n", 23675121Sfrankho arg.mi.dki_lbsize, 23685121Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev)); 23695121Sfrankho } 23705121Sfrankho 23715121Sfrankho /* 23725121Sfrankho * We treat the following media types as a floppy by default. 23735121Sfrankho */ 23745121Sfrankho isfloppy = 23755121Sfrankho (arg.mi.dki_media_type == DK_FLOPPY || 23765121Sfrankho arg.mi.dki_media_type == DK_ZIP || 23775121Sfrankho arg.mi.dki_media_type == DK_JAZ); 23785121Sfrankho 23795121Sfrankho /* 23805121Sfrankho * if this device understands fdio(7I) requests it's 23815121Sfrankho * obviously a floppy drive. 23825121Sfrankho */ 23835121Sfrankho if (!isfloppy && 23845121Sfrankho !ldi_ioctl(lh, FDIOGCHAR, argp, FKIOCTL, cr, NULL)) 23855121Sfrankho isfloppy = 1; 23865121Sfrankho 23875121Sfrankho /* 2388*11215Sgdamore@opensolaris.org * some devices we like to treat as floppies, but they don't 2389*11215Sgdamore@opensolaris.org * understand fdio(7I) requests. 23905121Sfrankho */ 23915121Sfrankho if (!isfloppy && 23925121Sfrankho !ldi_ioctl(lh, DKIOCINFO, argp, FKIOCTL, cr, NULL) && 23935121Sfrankho (arg.ci.dki_ctype == DKC_WDC2880 || 23945121Sfrankho arg.ci.dki_ctype == DKC_NCRFLOPPY || 23955121Sfrankho arg.ci.dki_ctype == DKC_SMSFLOPPY || 2396*11215Sgdamore@opensolaris.org arg.ci.dki_ctype == DKC_INTEL82077)) 23975121Sfrankho isfloppy = 1; 23985121Sfrankho 23995121Sfrankho /* 24005121Sfrankho * This is the "final fallback" test - media with 24015121Sfrankho * 2 heads and 80 cylinders are assumed to be floppies. 24025121Sfrankho * This is normally true for USB floppy drives ... 24035121Sfrankho */ 24045121Sfrankho if (!isfloppy && 24055121Sfrankho !ldi_ioctl(lh, DKIOCGGEOM, argp, FKIOCTL, cr, NULL) && 24065121Sfrankho (arg.gi.dkg_ncyl == 80 && arg.gi.dkg_nhead == 2)) 24075121Sfrankho isfloppy = 1; 24085121Sfrankho 24095121Sfrankho /* 24105121Sfrankho * This is similar to the "old" PCFS code that sets this flag 24115121Sfrankho * just based on the media descriptor being 0xf8 (MD_FIXED). 24125121Sfrankho * Should be re-worked. We really need some specialcasing for 24135121Sfrankho * removeable media. 24145121Sfrankho */ 24155121Sfrankho if (!isfloppy) { 24165121Sfrankho fsp->pcfs_flags |= PCFS_NOCHK; 24175121Sfrankho } 24185121Sfrankho 24195121Sfrankho /* 24205121Sfrankho * We automatically disable access time updates if the medium is 24215121Sfrankho * removeable and/or hotpluggable, and the admin did not explicitly 24225121Sfrankho * request access time updates (via the "atime" mount option). 24235121Sfrankho * The majority of flash-based media should fit this category. 24245121Sfrankho * Minimizing write access extends the lifetime of your memory stick ! 24255121Sfrankho */ 24265121Sfrankho if (!vfs_optionisset(fsp->pcfs_vfs, MNTOPT_ATIME, NULL) && 24275121Sfrankho (isremoveable || ishotpluggable | isfloppy)) { 24285121Sfrankho fsp->pcfs_flags |= PCFS_NOATIME; 24295121Sfrankho } 24305121Sfrankho 24315121Sfrankho (void) ldi_close(lh, FREAD, cr); 24325121Sfrankho out: 24335121Sfrankho if (fsp->pcfs_secsize == 0) { 24345121Sfrankho PC_DPRINTF3(1, "!pcfs: media block size autodetection " 24355121Sfrankho "device (%x.%x) failed, no user-provided fallback. " 24365121Sfrankho "Using %d bytes.\n", 24375121Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 24385121Sfrankho DEV_BSIZE); 24395121Sfrankho fsp->pcfs_secsize = DEV_BSIZE; 24405121Sfrankho fsp->pcfs_sdshift = 0; 24415121Sfrankho } 24425121Sfrankho ASSERT(fsp->pcfs_secsize % DEV_BSIZE == 0); 24435121Sfrankho ASSERT(VALID_SECSIZE(fsp->pcfs_secsize)); 24445121Sfrankho } 24455121Sfrankho 24465121Sfrankho /* 24475121Sfrankho * Get the FAT type for the DOS medium. 24485121Sfrankho * 24495121Sfrankho * ------------------------- 24505121Sfrankho * According to Microsoft: 24515121Sfrankho * The FAT type one of FAT12, FAT16, or FAT32 is determined by the 24525121Sfrankho * count of clusters on the volume and nothing else. 24535121Sfrankho * ------------------------- 24545121Sfrankho * 24555121Sfrankho */ 24565121Sfrankho static int 24575121Sfrankho pc_getfattype(struct pcfs *fsp) 24585121Sfrankho { 24595121Sfrankho int error = 0; 24605121Sfrankho buf_t *bp = NULL; 24615121Sfrankho struct vnode *devvp = fsp->pcfs_devvp; 24625121Sfrankho dev_t dev = devvp->v_rdev; 24635121Sfrankho 24645121Sfrankho /* 24655121Sfrankho * Detect the native block size of the medium, and attempt to 24665121Sfrankho * detect whether the medium is removeable. 2467*11215Sgdamore@opensolaris.org * We do treat removable media (floppies, USB and FireWire disks) 2468*11215Sgdamore@opensolaris.org * differently wrt. to the frequency and synchronicity of FAT updates. 24695121Sfrankho * We need to know the media block size in order to be able to 24705121Sfrankho * parse the partition table. 24715121Sfrankho */ 24725121Sfrankho pcfs_device_getinfo(fsp); 24735121Sfrankho 24745121Sfrankho /* 24755121Sfrankho * Unpartitioned media (floppies and some removeable devices) 24765121Sfrankho * don't have a partition table, the FAT BPB is at disk block 0. 24775121Sfrankho * Start out by reading block 0. 24785121Sfrankho */ 24795121Sfrankho fsp->pcfs_dosstart = 0; 24805121Sfrankho bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), fsp->pcfs_secsize); 24815121Sfrankho 24825121Sfrankho if (error = geterror(bp)) 24835121Sfrankho goto out; 24845121Sfrankho 24855121Sfrankho /* 24865121Sfrankho * If a logical drive number is requested, parse the partition table 24875121Sfrankho * and attempt to locate it. Otherwise, proceed immediately to the 24885121Sfrankho * BPB check. findTheDrive(), if successful, returns the disk block 24895121Sfrankho * number where the requested partition starts in "startsec". 24905121Sfrankho */ 24915121Sfrankho if (fsp->pcfs_ldrive != 0) { 24925121Sfrankho PC_DPRINTF3(5, "!pcfs: pc_getfattype: using FDISK table on " 24935121Sfrankho "device (%x,%x):%d to find BPB\n", 24945121Sfrankho getmajor(dev), getminor(dev), fsp->pcfs_ldrive); 24955121Sfrankho 24965121Sfrankho if (error = findTheDrive(fsp, &bp)) 24975121Sfrankho goto out; 24985121Sfrankho 24995121Sfrankho ASSERT(fsp->pcfs_dosstart != 0); 25005121Sfrankho 25015121Sfrankho brelse(bp); 25025121Sfrankho bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), 25035121Sfrankho fsp->pcfs_secsize); 25045121Sfrankho if (error = geterror(bp)) 25055121Sfrankho goto out; 25065121Sfrankho } 25075121Sfrankho 25085121Sfrankho /* 25095121Sfrankho * Validate the BPB and fill in the instance structure. 25105121Sfrankho */ 25115121Sfrankho if (!parseBPB(fsp, (uchar_t *)bp->b_un.b_addr, NULL)) { 25125121Sfrankho PC_DPRINTF4(1, "!pcfs: pc_getfattype: No FAT BPB on " 25135121Sfrankho "device (%x.%x):%d, disk LBA %u\n", 25145121Sfrankho getmajor(dev), getminor(dev), fsp->pcfs_ldrive, 25155121Sfrankho (uint_t)pc_dbdaddr(fsp, fsp->pcfs_dosstart)); 25165121Sfrankho error = EINVAL; 25175121Sfrankho goto out; 25185121Sfrankho } 25195121Sfrankho 25205121Sfrankho ASSERT(fsp->pcfs_fattype != FAT_UNKNOWN); 25215121Sfrankho 25225121Sfrankho out: 25235121Sfrankho /* 25245121Sfrankho * Release the buffer used 25255121Sfrankho */ 25265121Sfrankho if (bp != NULL) 25275121Sfrankho brelse(bp); 25285121Sfrankho return (error); 25295121Sfrankho } 25305121Sfrankho 25315121Sfrankho 25325121Sfrankho /* 25335121Sfrankho * Get the file allocation table. 25345121Sfrankho * If there is an old FAT, invalidate it. 25355121Sfrankho */ 25365121Sfrankho int 25375121Sfrankho pc_getfat(struct pcfs *fsp) 25385121Sfrankho { 25395121Sfrankho struct buf *bp = NULL; 25405121Sfrankho uchar_t *fatp = NULL; 25415121Sfrankho uchar_t *fat_changemap = NULL; 25425121Sfrankho int error; 25435121Sfrankho int fat_changemapsize; 25445121Sfrankho int flags = 0; 25455121Sfrankho int nfat; 25465121Sfrankho int altfat_mustmatch = 0; 25475121Sfrankho int fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize; 25485121Sfrankho 25495121Sfrankho if (fsp->pcfs_fatp) { 25505121Sfrankho /* 25515121Sfrankho * There is a FAT in core. 25525121Sfrankho * If there are open file pcnodes or we have modified it or 25535121Sfrankho * it hasn't timed out yet use the in core FAT. 25545121Sfrankho * Otherwise invalidate it and get a new one 25555121Sfrankho */ 25565121Sfrankho #ifdef notdef 25575121Sfrankho if (fsp->pcfs_frefs || 25585121Sfrankho (fsp->pcfs_flags & PCFS_FATMOD) || 25595121Sfrankho (gethrestime_sec() < fsp->pcfs_fattime)) { 25605121Sfrankho return (0); 25615121Sfrankho } else { 25625121Sfrankho mutex_enter(&pcfslock); 25635121Sfrankho pc_invalfat(fsp); 25645121Sfrankho mutex_exit(&pcfslock); 25655121Sfrankho } 25665121Sfrankho #endif /* notdef */ 25675121Sfrankho return (0); 25685121Sfrankho } 25695121Sfrankho 25705121Sfrankho /* 25715121Sfrankho * Get FAT and check it for validity 25725121Sfrankho */ 25735121Sfrankho fatp = kmem_alloc(fatsize, KM_SLEEP); 25745121Sfrankho error = pc_readfat(fsp, fatp); 25755121Sfrankho if (error) { 25765121Sfrankho flags = B_ERROR; 25775121Sfrankho goto out; 25785121Sfrankho } 25795121Sfrankho fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1; 25805121Sfrankho fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP); 25815121Sfrankho fsp->pcfs_fatp = fatp; 25825121Sfrankho fsp->pcfs_fat_changemapsize = fat_changemapsize; 25835121Sfrankho fsp->pcfs_fat_changemap = fat_changemap; 25845121Sfrankho 25855121Sfrankho /* 25865121Sfrankho * The only definite signature check is that the 25875121Sfrankho * media descriptor byte should match the first byte 25885121Sfrankho * of the FAT block. 25895121Sfrankho */ 25905121Sfrankho if (fatp[0] != fsp->pcfs_mediadesc) { 25915121Sfrankho cmn_err(CE_NOTE, "!pcfs: FAT signature mismatch, " 25925121Sfrankho "media descriptor %x, FAT[0] lowbyte %x\n", 25935121Sfrankho (uint32_t)fsp->pcfs_mediadesc, (uint32_t)fatp[0]); 25945121Sfrankho cmn_err(CE_NOTE, "!pcfs: Enforcing alternate FAT validation\n"); 25955121Sfrankho altfat_mustmatch = 1; 25965121Sfrankho } 25975121Sfrankho 25985121Sfrankho /* 25995121Sfrankho * Get alternate FATs and check for consistency 26005121Sfrankho * This is an inlined version of pc_readfat(). 26015121Sfrankho * Since we're only comparing FAT and alternate FAT, 26025121Sfrankho * there's no reason to let pc_readfat() copy data out 26035121Sfrankho * of the buf. Instead, compare in-situ, one cluster 26045121Sfrankho * at a time. 26055121Sfrankho */ 26065121Sfrankho for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) { 26075121Sfrankho size_t startsec; 26085121Sfrankho size_t off; 26095121Sfrankho 26105121Sfrankho startsec = pc_dbdaddr(fsp, 26115121Sfrankho fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec); 26125121Sfrankho 26135121Sfrankho for (off = 0; off < fatsize; off += fsp->pcfs_clsize) { 26145121Sfrankho daddr_t fatblk = startsec + pc_dbdaddr(fsp, 26155121Sfrankho pc_cltodb(fsp, pc_lblkno(fsp, off))); 26165121Sfrankho 26175121Sfrankho bp = bread(fsp->pcfs_xdev, fatblk, 26185121Sfrankho MIN(fsp->pcfs_clsize, fatsize - off)); 26195121Sfrankho if (bp->b_flags & (B_ERROR | B_STALE)) { 26205121Sfrankho cmn_err(CE_NOTE, 26215121Sfrankho "!pcfs: alternate FAT #%d (start LBA %p)" 26225121Sfrankho " read error at offset %ld on device" 26235121Sfrankho " (%x.%x):%d", 26245121Sfrankho nfat, (void *)(uintptr_t)startsec, off, 26255121Sfrankho getmajor(fsp->pcfs_xdev), 26265121Sfrankho getminor(fsp->pcfs_xdev), 26275121Sfrankho fsp->pcfs_ldrive); 26285121Sfrankho flags = B_ERROR; 26295121Sfrankho error = EIO; 26305121Sfrankho goto out; 26315121Sfrankho } 26325121Sfrankho bp->b_flags |= B_STALE | B_AGE; 26335121Sfrankho if (bcmp(bp->b_un.b_addr, fatp + off, 26345121Sfrankho MIN(fsp->pcfs_clsize, fatsize - off))) { 26355121Sfrankho cmn_err(CE_NOTE, 26365121Sfrankho "!pcfs: alternate FAT #%d (start LBA %p)" 26375121Sfrankho " corrupted at offset %ld on device" 26385121Sfrankho " (%x.%x):%d", 26395121Sfrankho nfat, (void *)(uintptr_t)startsec, off, 26405121Sfrankho getmajor(fsp->pcfs_xdev), 26415121Sfrankho getminor(fsp->pcfs_xdev), 26425121Sfrankho fsp->pcfs_ldrive); 26435121Sfrankho if (altfat_mustmatch) { 26445121Sfrankho flags = B_ERROR; 26455121Sfrankho error = EIO; 26465121Sfrankho goto out; 26475121Sfrankho } 26485121Sfrankho } 26495121Sfrankho brelse(bp); 26505121Sfrankho bp = NULL; /* prevent double release */ 26515121Sfrankho } 26525121Sfrankho } 26535121Sfrankho 26545121Sfrankho fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT; 26555121Sfrankho fsp->pcfs_fatjustread = 1; 26565121Sfrankho 26575121Sfrankho /* 26585121Sfrankho * Retrieve FAT32 fsinfo sector. 26595121Sfrankho * A failure to read this is not fatal to accessing the volume. 26605121Sfrankho * It simply means operations that count or search free blocks 26615121Sfrankho * will have to do a full FAT walk, vs. a possibly quicker lookup 26625121Sfrankho * of the summary information. 26635121Sfrankho * Hence, we log a message but return success overall after this point. 26645121Sfrankho */ 26655121Sfrankho if (IS_FAT32(fsp) && (fsp->pcfs_flags & PCFS_FSINFO_OK)) { 26665121Sfrankho struct fat_od_fsi *fsinfo_disk; 26675121Sfrankho 26685121Sfrankho bp = bread(fsp->pcfs_xdev, 26695121Sfrankho pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize); 26705121Sfrankho fsinfo_disk = (struct fat_od_fsi *)bp->b_un.b_addr; 26715121Sfrankho if (bp->b_flags & (B_ERROR | B_STALE) || 26725121Sfrankho !FSISIG_OK(fsinfo_disk)) { 26735121Sfrankho cmn_err(CE_NOTE, 26745121Sfrankho "!pcfs: error reading fat32 fsinfo from " 26755121Sfrankho "device (%x.%x):%d, block %lld", 26765121Sfrankho getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev), 26775121Sfrankho fsp->pcfs_ldrive, 26785121Sfrankho (long long)pc_dbdaddr(fsp, fsp->pcfs_fsistart)); 26795121Sfrankho fsp->pcfs_flags &= ~PCFS_FSINFO_OK; 26805121Sfrankho fsp->pcfs_fsinfo.fs_free_clusters = FSINFO_UNKNOWN; 26815121Sfrankho fsp->pcfs_fsinfo.fs_next_free = FSINFO_UNKNOWN; 26825121Sfrankho } else { 26835121Sfrankho bp->b_flags |= B_STALE | B_AGE; 26845121Sfrankho fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr); 26855121Sfrankho fsp->pcfs_fsinfo.fs_free_clusters = 26865121Sfrankho LE_32(fsinfo_disk->fsi_incore.fs_free_clusters); 26875121Sfrankho fsp->pcfs_fsinfo.fs_next_free = 26885121Sfrankho LE_32(fsinfo_disk->fsi_incore.fs_next_free); 26895121Sfrankho } 26905121Sfrankho brelse(bp); 26915121Sfrankho bp = NULL; 26925121Sfrankho } 26935121Sfrankho 26945121Sfrankho if (pc_validcl(fsp, (pc_cluster32_t)fsp->pcfs_fsinfo.fs_next_free)) 26955121Sfrankho fsp->pcfs_nxfrecls = fsp->pcfs_fsinfo.fs_next_free; 26965121Sfrankho else 26975121Sfrankho fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER; 26985121Sfrankho 26995121Sfrankho return (0); 27005121Sfrankho 27015121Sfrankho out: 27025121Sfrankho cmn_err(CE_NOTE, "!pcfs: illegal disk format"); 27035121Sfrankho if (bp) 27045121Sfrankho brelse(bp); 27055121Sfrankho if (fatp) 27065121Sfrankho kmem_free(fatp, fatsize); 27075121Sfrankho if (fat_changemap) 27085121Sfrankho kmem_free(fat_changemap, fat_changemapsize); 27095121Sfrankho 27105121Sfrankho if (flags) { 27115121Sfrankho pc_mark_irrecov(fsp); 27125121Sfrankho } 27135121Sfrankho return (error); 27145121Sfrankho } 2715