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