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