1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #include <sys/param.h>
  28 #include <sys/systm.h>
  29 #include <sys/kmem.h>
  30 #include <sys/user.h>
  31 #include <sys/proc.h>
  32 #include <sys/cred.h>
  33 #include <sys/disp.h>
  34 #include <sys/buf.h>
  35 #include <sys/vfs.h>
  36 #include <sys/vfs_opreg.h>
  37 #include <sys/vnode.h>
  38 #include <sys/fdio.h>
  39 #include <sys/file.h>
  40 #include <sys/uio.h>
  41 #include <sys/conf.h>
  42 #include <sys/statvfs.h>
  43 #include <sys/mount.h>
  44 #include <sys/pathname.h>
  45 #include <sys/cmn_err.h>
  46 #include <sys/debug.h>
  47 #include <sys/sysmacros.h>
  48 #include <sys/conf.h>
  49 #include <sys/mkdev.h>
  50 #include <sys/swap.h>
  51 #include <sys/sunddi.h>
  52 #include <sys/sunldi.h>
  53 #include <sys/dktp/fdisk.h>
  54 #include <sys/fs/pc_label.h>
  55 #include <sys/fs/pc_fs.h>
  56 #include <sys/fs/pc_dir.h>
  57 #include <sys/fs/pc_node.h>
  58 #include <fs/fs_subr.h>
  59 #include <sys/modctl.h>
  60 #include <sys/dkio.h>
  61 #include <sys/open.h>
  62 #include <sys/mntent.h>
  63 #include <sys/policy.h>
  64 #include <sys/atomic.h>
  65 #include <sys/sdt.h>
  66 
  67 /*
  68  * The majority of PC media use a 512 sector size, but
  69  * occasionally you will run across a 1k sector size.
  70  * For media with a 1k sector size, fd_strategy() requires
  71  * the I/O size to be a 1k multiple; so when the sector size
  72  * is not yet known, always read 1k.
  73  */
  74 #define PC_SAFESECSIZE  (PC_SECSIZE * 2)
  75 
  76 static int pcfs_pseudo_floppy(dev_t);
  77 
  78 static int pcfsinit(int, char *);
  79 static int pcfs_mount(struct vfs *, struct vnode *, struct mounta *,
  80         struct cred *);
  81 static int pcfs_unmount(struct vfs *, int, struct cred *);
  82 static int pcfs_root(struct vfs *, struct vnode **);
  83 static int pcfs_statvfs(struct vfs *, struct statvfs64 *);
  84 static int pc_syncfsnodes(struct pcfs *);
  85 static int pcfs_sync(struct vfs *, short, struct cred *);
  86 static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp);
  87 static void pcfs_freevfs(vfs_t *vfsp);
  88 
  89 static int pc_readfat(struct pcfs *fsp, uchar_t *fatp);
  90 static int pc_writefat(struct pcfs *fsp, daddr_t start);
  91 
  92 static int pc_getfattype(struct pcfs *fsp);
  93 static void pcfs_parse_mntopts(struct pcfs *fsp);
  94 
  95 
  96 /*
  97  * pcfs mount options table
  98  */
  99 
 100 static char *nohidden_cancel[] = { MNTOPT_PCFS_HIDDEN, NULL };
 101 static char *hidden_cancel[] = { MNTOPT_PCFS_NOHIDDEN, NULL };
 102 static char *nofoldcase_cancel[] = { MNTOPT_PCFS_FOLDCASE, NULL };
 103 static char *foldcase_cancel[] = { MNTOPT_PCFS_NOFOLDCASE, NULL };
 104 static char *clamptime_cancel[] = { MNTOPT_PCFS_NOCLAMPTIME, NULL };
 105 static char *noclamptime_cancel[] = { MNTOPT_PCFS_CLAMPTIME, NULL };
 106 static char *atime_cancel[] = { MNTOPT_NOATIME, NULL };
 107 static char *noatime_cancel[] = { MNTOPT_ATIME, NULL };
 108 
 109 static mntopt_t mntopts[] = {
 110 /*
 111  *      option name     cancel option   default arg     flags   opt data
 112  */
 113         { MNTOPT_PCFS_NOHIDDEN, nohidden_cancel, NULL, 0, NULL },
 114         { MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, MO_DEFAULT, NULL },
 115         { MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, NULL },
 116         { MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0, NULL },
 117         { MNTOPT_PCFS_CLAMPTIME, clamptime_cancel, NULL, MO_DEFAULT, NULL },
 118         { MNTOPT_PCFS_NOCLAMPTIME, noclamptime_cancel, NULL, NULL, NULL },
 119         { MNTOPT_NOATIME, noatime_cancel, NULL, NULL, NULL },
 120         { MNTOPT_ATIME, atime_cancel, NULL, NULL, NULL },
 121         { MNTOPT_PCFS_TIMEZONE, NULL, "+0", MO_DEFAULT | MO_HASVALUE, NULL },
 122         { MNTOPT_PCFS_SECSIZE, NULL, NULL, MO_HASVALUE, NULL }
 123 };
 124 
 125 static mntopts_t pcfs_mntopts = {
 126         sizeof (mntopts) / sizeof (mntopt_t),
 127         mntopts
 128 };
 129 
 130 int pcfsdebuglevel = 0;
 131 
 132 /*
 133  * pcfslock:    protects the list of mounted pc filesystems "pc_mounttab.
 134  * pcfs_lock:   (inside per filesystem structure "pcfs")
 135  *              per filesystem lock. Most of the vfsops and vnodeops are
 136  *              protected by this lock.
 137  * pcnodes_lock: protects the pcnode hash table "pcdhead", "pcfhead".
 138  *
 139  * Lock hierarchy: pcfslock > pcfs_lock > pcnodes_lock
 140  *
 141  * pcfs_mountcount:     used to prevent module unloads while there is still
 142  *                      pcfs state from a former mount hanging around. With
 143  *                      forced umount support, the filesystem module must not
 144  *                      be allowed to go away before the last VFS_FREEVFS()
 145  *                      call has been made.
 146  *                      Since this is just an atomic counter, there's no need
 147  *                      for locking.
 148  */
 149 kmutex_t        pcfslock;
 150 krwlock_t       pcnodes_lock;
 151 uint32_t        pcfs_mountcount;
 152 
 153 static int pcfstype;
 154 
 155 static vfsdef_t vfw = {
 156         VFSDEF_VERSION,
 157         "pcfs",
 158         pcfsinit,
 159         VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS|VSW_CANLOFI,
 160         &pcfs_mntopts
 161 };
 162 
 163 extern struct mod_ops mod_fsops;
 164 
 165 static struct modlfs modlfs = {
 166         &mod_fsops,
 167         "PC filesystem",
 168         &vfw
 169 };
 170 
 171 static struct modlinkage modlinkage = {
 172         MODREV_1,
 173         { &modlfs, NULL }
 174 };
 175 
 176 int
 177 _init(void)
 178 {
 179         int     error;
 180 
 181 #if !defined(lint)
 182         /* make sure the on-disk structures are sane */
 183         ASSERT(sizeof (struct pcdir) == 32);
 184         ASSERT(sizeof (struct pcdir_lfn) == 32);
 185 #endif
 186         mutex_init(&pcfslock, NULL, MUTEX_DEFAULT, NULL);
 187         rw_init(&pcnodes_lock, NULL, RW_DEFAULT, NULL);
 188         error = mod_install(&modlinkage);
 189         if (error) {
 190                 mutex_destroy(&pcfslock);
 191                 rw_destroy(&pcnodes_lock);
 192         }
 193         return (error);
 194 }
 195 
 196 int
 197 _fini(void)
 198 {
 199         int     error;
 200 
 201         /*
 202          * If a forcedly unmounted instance is still hanging around,
 203          * we cannot allow the module to be unloaded because that would
 204          * cause panics once the VFS framework decides it's time to call
 205          * into VFS_FREEVFS().
 206          */
 207         if (pcfs_mountcount)
 208                 return (EBUSY);
 209 
 210         error = mod_remove(&modlinkage);
 211         if (error)
 212                 return (error);
 213         mutex_destroy(&pcfslock);
 214         rw_destroy(&pcnodes_lock);
 215         /*
 216          * Tear down the operations vectors
 217          */
 218         (void) vfs_freevfsops_by_type(pcfstype);
 219         vn_freevnodeops(pcfs_fvnodeops);
 220         vn_freevnodeops(pcfs_dvnodeops);
 221         return (0);
 222 }
 223 
 224 int
 225 _info(struct modinfo *modinfop)
 226 {
 227         return (mod_info(&modlinkage, modinfop));
 228 }
 229 
 230 /* ARGSUSED1 */
 231 static int
 232 pcfsinit(int fstype, char *name)
 233 {
 234         static const fs_operation_def_t pcfs_vfsops_template[] = {
 235                 { VFSNAME_MOUNT,        { .vfs_mount = pcfs_mount } },
 236                 { VFSNAME_UNMOUNT,      { .vfs_unmount = pcfs_unmount } },
 237                 { VFSNAME_ROOT,         { .vfs_root = pcfs_root } },
 238                 { VFSNAME_STATVFS,      { .vfs_statvfs = pcfs_statvfs } },
 239                 { VFSNAME_SYNC,         { .vfs_sync = pcfs_sync } },
 240                 { VFSNAME_VGET,         { .vfs_vget = pcfs_vget } },
 241                 { VFSNAME_FREEVFS,      { .vfs_freevfs = pcfs_freevfs } },
 242                 { NULL,                 { NULL } }
 243         };
 244         int error;
 245 
 246         error = vfs_setfsops(fstype, pcfs_vfsops_template, NULL);
 247         if (error != 0) {
 248                 cmn_err(CE_WARN, "pcfsinit: bad vfs ops template");
 249                 return (error);
 250         }
 251 
 252         error = vn_make_ops("pcfs", pcfs_fvnodeops_template, &pcfs_fvnodeops);
 253         if (error != 0) {
 254                 (void) vfs_freevfsops_by_type(fstype);
 255                 cmn_err(CE_WARN, "pcfsinit: bad file vnode ops template");
 256                 return (error);
 257         }
 258 
 259         error = vn_make_ops("pcfsd", pcfs_dvnodeops_template, &pcfs_dvnodeops);
 260         if (error != 0) {
 261                 (void) vfs_freevfsops_by_type(fstype);
 262                 vn_freevnodeops(pcfs_fvnodeops);
 263                 cmn_err(CE_WARN, "pcfsinit: bad dir vnode ops template");
 264                 return (error);
 265         }
 266 
 267         pcfstype = fstype;
 268         (void) pc_init();
 269         pcfs_mountcount = 0;
 270         return (0);
 271 }
 272 
 273 static struct pcfs *pc_mounttab = NULL;
 274 
 275 extern struct pcfs_args pc_tz;
 276 
 277 /*
 278  *  Define some special logical drives we use internal to this file.
 279  */
 280 #define BOOT_PARTITION_DRIVE    99
 281 #define PRIMARY_DOS_DRIVE       1
 282 #define UNPARTITIONED_DRIVE     0
 283 
 284 static int
 285 pcfs_device_identify(
 286         struct vfs *vfsp,
 287         struct mounta *uap,
 288         struct cred *cr,
 289         int *dos_ldrive,
 290         dev_t *xdev)
 291 {
 292         struct pathname special;
 293         char *c;
 294         struct vnode *svp = NULL;
 295         struct vnode *lvp = NULL;
 296         int oflag, aflag;
 297         int error;
 298 
 299         /*
 300          * Resolve path name of special file being mounted.
 301          */
 302         if (error = pn_get(uap->spec, UIO_USERSPACE, &special)) {
 303                 return (error);
 304         }
 305 
 306         *dos_ldrive = -1;
 307 
 308         if (error =
 309             lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &svp)) {
 310                 /*
 311                  * If there's no device node, the name specified most likely
 312                  * maps to a PCFS-style "partition specifier" to select a
 313                  * harddisk primary/logical partition. Disable floppy-specific
 314                  * checks in such cases unless an explicit :A or :B is
 315                  * requested.
 316                  */
 317 
 318                 /*
 319                  * Split the pathname string at the last ':' separator.
 320                  * If there's no ':' in the device name, or the ':' is the
 321                  * last character in the string, the name is invalid and
 322                  * the error from the previous lookup will be returned.
 323                  */
 324                 c = strrchr(special.pn_path, ':');
 325                 if (c == NULL || strlen(c) == 0)
 326                         goto devlookup_done;
 327 
 328                 *c++ = '\0';
 329 
 330                 /*
 331                  * PCFS partition name suffixes can be:
 332                  *      - "boot" to indicate the X86BOOT partition
 333                  *      - a drive letter [c-z] for the "DOS logical drive"
 334                  *      - a drive number 1..24 for the "DOS logical drive"
 335                  *      - a "floppy name letter", 'a' or 'b' (just strip this)
 336                  */
 337                 if (strcasecmp(c, "boot") == 0) {
 338                         /*
 339                          * The Solaris boot partition is requested.
 340                          */
 341                         *dos_ldrive = BOOT_PARTITION_DRIVE;
 342                 } else if (strspn(c, "0123456789") == strlen(c)) {
 343                         /*
 344                          * All digits - parse the partition number.
 345                          */
 346                         long drvnum = 0;
 347 
 348                         if ((error = ddi_strtol(c, NULL, 10, &drvnum)) == 0) {
 349                                 /*
 350                                  * A number alright - in the allowed range ?
 351                                  */
 352                                 if (drvnum > 24 || drvnum == 0)
 353                                         error = ENXIO;
 354                         }
 355                         if (error)
 356                                 goto devlookup_done;
 357                         *dos_ldrive = (int)drvnum;
 358                 } else if (strlen(c) == 1) {
 359                         /*
 360                          * A single trailing character was specified.
 361                          *      - [c-zC-Z] means a harddisk partition, and
 362                          *        we retrieve the partition number.
 363                          *      - [abAB] means a floppy drive, so we swallow
 364                          *        the "drive specifier" and test later
 365                          *        whether the physical device is a floppy.
 366                          */
 367                         *c = tolower(*c);
 368                         if (*c == 'a' || *c == 'b') {
 369                                 *dos_ldrive = UNPARTITIONED_DRIVE;
 370                         } else if (*c < 'c' || *c > 'z') {
 371                                 error = ENXIO;
 372                                 goto devlookup_done;
 373                         } else {
 374                                 *dos_ldrive = 1 + *c - 'c';
 375                         }
 376                 } else {
 377                         /*
 378                          * Can't parse this - pass through previous error.
 379                          */
 380                         goto devlookup_done;
 381                 }
 382 
 383 
 384                 error = lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW,
 385                     NULLVPP, &svp);
 386         } else {
 387                 *dos_ldrive = UNPARTITIONED_DRIVE;
 388         }
 389 devlookup_done:
 390         pn_free(&special);
 391         if (error)
 392                 return (error);
 393 
 394         ASSERT(*dos_ldrive >= UNPARTITIONED_DRIVE);
 395 
 396         /*
 397          * Verify caller's permission to open the device special file.
 398          */
 399         if ((vfsp->vfs_flag & VFS_RDONLY) != 0 ||
 400             ((uap->flags & MS_RDONLY) != 0)) {
 401                 oflag = FREAD;
 402                 aflag = VREAD;
 403         } else {
 404                 oflag = FREAD | FWRITE;
 405                 aflag = VREAD | VWRITE;
 406         }
 407 
 408         error = vfs_get_lofi(vfsp, &lvp);
 409 
 410         if (error > 0) {
 411                 if (error == ENOENT)
 412                         error = ENODEV;
 413                 goto out;
 414         } else if (error == 0) {
 415                 *xdev = lvp->v_rdev;
 416         } else {
 417                 *xdev = svp->v_rdev;
 418 
 419                 if (svp->v_type != VBLK) {
 420                         error = ENOTBLK;
 421                         goto out;
 422                 }
 423 
 424                 if ((error = secpolicy_spec_open(cr, svp, oflag)) != 0)
 425                         goto out;
 426         }
 427 
 428         if (getmajor(*xdev) >= devcnt) {
 429                 error = ENXIO;
 430                 goto out;
 431         }
 432 
 433         if ((error = VOP_ACCESS(svp, aflag, 0, cr, NULL)) != 0)
 434                 goto out;
 435 
 436 out:
 437         if (svp != NULL)
 438                 VN_RELE(svp);
 439         if (lvp != NULL)
 440                 VN_RELE(lvp);
 441         return (error);
 442 }
 443 
 444 static int
 445 pcfs_device_ismounted(
 446         struct vfs *vfsp,
 447         int dos_ldrive,
 448         dev_t xdev,
 449         int *remounting,
 450         dev_t *pseudodev)
 451 {
 452         struct pcfs *fsp;
 453         int remount = *remounting;
 454 
 455         /*
 456          * Ensure that this logical drive isn't already mounted, unless
 457          * this is a REMOUNT request.
 458          * Note: The framework will perform this check if the "...:c"
 459          * PCFS-style "logical drive" syntax has not been used and an
 460          * actually existing physical device is backing this filesystem.
 461          * Once all block device drivers support PC-style partitioning,
 462          * this codeblock can be dropped.
 463          */
 464         *pseudodev = xdev;
 465 
 466         if (dos_ldrive) {
 467                 mutex_enter(&pcfslock);
 468                 for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt)
 469                         if (fsp->pcfs_xdev == xdev &&
 470                             fsp->pcfs_ldrive == dos_ldrive) {
 471                                 mutex_exit(&pcfslock);
 472                                 if (remount) {
 473                                         return (0);
 474                                 } else {
 475                                         return (EBUSY);
 476                                 }
 477                         }
 478                 /*
 479                  * Assign a unique device number for the vfs
 480                  * The old way (getudev() + a constantly incrementing
 481                  * major number) was wrong because it changes vfs_dev
 482                  * across mounts and reboots, which breaks nfs file handles.
 483                  * UFS just uses the real dev_t. We can't do that because
 484                  * of the way pcfs opens fdisk partitons (the :c and :d
 485                  * partitions are on the same dev_t). Though that _might_
 486                  * actually be ok, since the file handle contains an
 487                  * absolute block number, it's probably better to make them
 488                  * different. So I think we should retain the original
 489                  * dev_t, but come up with a different minor number based
 490                  * on the logical drive that will _always_ come up the same.
 491                  * For now, we steal the upper 6 bits.
 492                  */
 493 #ifdef notdef
 494                 /* what should we do here? */
 495                 if (((getminor(xdev) >> 12) & 0x3F) != 0)
 496                         printf("whoops - upper bits used!\n");
 497 #endif
 498                 *pseudodev = makedevice(getmajor(xdev),
 499                     ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32);
 500                 if (vfs_devmounting(*pseudodev, vfsp)) {
 501                         mutex_exit(&pcfslock);
 502                         return (EBUSY);
 503                 }
 504                 if (vfs_devismounted(*pseudodev)) {
 505                         mutex_exit(&pcfslock);
 506                         if (remount) {
 507                                 return (0);
 508                         } else {
 509                                 return (EBUSY);
 510                         }
 511                 }
 512                 mutex_exit(&pcfslock);
 513         } else {
 514                 *pseudodev = xdev;
 515                 if (vfs_devmounting(*pseudodev, vfsp)) {
 516                         return (EBUSY);
 517                 }
 518                 if (vfs_devismounted(*pseudodev))
 519                         if (remount) {
 520                                 return (0);
 521                         } else {
 522                                 return (EBUSY);
 523                         }
 524         }
 525 
 526         /*
 527          * This is not a remount. Even if MS_REMOUNT was requested,
 528          * the caller needs to proceed as it would on an ordinary
 529          * mount.
 530          */
 531         *remounting = 0;
 532 
 533         ASSERT(*pseudodev);
 534         return (0);
 535 }
 536 
 537 /*
 538  * Get the PCFS-specific mount options from the VFS framework.
 539  * For "timezone" and "secsize", we need to parse the number
 540  * ourselves and ensure its validity.
 541  * Note: "secsize" is deliberately undocumented at this time,
 542  * it's a workaround for devices (particularly: lofi image files)
 543  * that don't support the DKIOCGMEDIAINFO ioctl for autodetection.
 544  */
 545 static void
 546 pcfs_parse_mntopts(struct pcfs *fsp)
 547 {
 548         char *c;
 549         char *endptr;
 550         long l;
 551         struct vfs *vfsp = fsp->pcfs_vfs;
 552 
 553         ASSERT(fsp->pcfs_secondswest == 0);
 554         ASSERT(fsp->pcfs_secsize == 0);
 555 
 556         if (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL))
 557                 fsp->pcfs_flags |= PCFS_HIDDEN;
 558         if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL))
 559                 fsp->pcfs_flags |= PCFS_FOLDCASE;
 560         if (vfs_optionisset(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL))
 561                 fsp->pcfs_flags |= PCFS_NOCLAMPTIME;
 562         if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL))
 563                 fsp->pcfs_flags |= PCFS_NOATIME;
 564 
 565         if (vfs_optionisset(vfsp, MNTOPT_PCFS_TIMEZONE, &c)) {
 566                 if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
 567                     endptr == c + strlen(c)) {
 568                         /*
 569                          * A number alright - in the allowed range ?
 570                          */
 571                         if (l <= -12*3600 || l >= 12*3600) {
 572                                 cmn_err(CE_WARN, "!pcfs: invalid use of "
 573                                     "'timezone' mount option - %ld "
 574                                     "is out of range. Assuming 0.", l);
 575                                 l = 0;
 576                         }
 577                 } else {
 578                         cmn_err(CE_WARN, "!pcfs: invalid use of "
 579                             "'timezone' mount option - argument %s "
 580                             "is not a valid number. Assuming 0.", c);
 581                         l = 0;
 582                 }
 583                 fsp->pcfs_secondswest = l;
 584         }
 585 
 586         /*
 587          * The "secsize=..." mount option is a workaround for the lack of
 588          * lofi(7d) support for DKIOCGMEDIAINFO. If PCFS wants to parse the
 589          * partition table of a disk image and it has been partitioned with
 590          * sector sizes other than 512 bytes, we'd fail on loopback'ed disk
 591          * images.
 592          * That should really be fixed in lofi ... this is a workaround.
 593          */
 594         if (vfs_optionisset(vfsp, MNTOPT_PCFS_SECSIZE, &c)) {
 595                 if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
 596                     endptr == c + strlen(c)) {
 597                         /*
 598                          * A number alright - a valid sector size as well ?
 599                          */
 600                         if (!VALID_SECSIZE(l)) {
 601                                 cmn_err(CE_WARN, "!pcfs: invalid use of "
 602                                     "'secsize' mount option - %ld is "
 603                                     "unsupported. Autodetecting.", l);
 604                                 l = 0;
 605                         }
 606                 } else {
 607                         cmn_err(CE_WARN, "!pcfs: invalid use of "
 608                             "'secsize' mount option - argument %s "
 609                             "is not a valid number. Autodetecting.", c);
 610                         l = 0;
 611                 }
 612                 fsp->pcfs_secsize = l;
 613                 fsp->pcfs_sdshift = ddi_ffs(l / DEV_BSIZE) - 1;
 614         }
 615 }
 616 
 617 /*
 618  * vfs operations
 619  */
 620 
 621 /*
 622  * pcfs_mount - backend for VFS_MOUNT() on PCFS.
 623  */
 624 static int
 625 pcfs_mount(
 626         struct vfs *vfsp,
 627         struct vnode *mvp,
 628         struct mounta *uap,
 629         struct cred *cr)
 630 {
 631         struct pcfs *fsp;
 632         struct vnode *devvp;
 633         dev_t pseudodev;
 634         dev_t xdev;
 635         int dos_ldrive = 0;
 636         int error;
 637         int remounting;
 638 
 639         if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
 640                 return (error);
 641 
 642         if (mvp->v_type != VDIR)
 643                 return (ENOTDIR);
 644 
 645         mutex_enter(&mvp->v_lock);
 646         if ((uap->flags & MS_REMOUNT) == 0 &&
 647             (uap->flags & MS_OVERLAY) == 0 &&
 648             (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
 649                 mutex_exit(&mvp->v_lock);
 650                 return (EBUSY);
 651         }
 652         mutex_exit(&mvp->v_lock);
 653 
 654         /*
 655          * PCFS doesn't do mount arguments anymore - everything's a mount
 656          * option these days. In order not to break existing callers, we
 657          * don't reject it yet, just warn that the data (if any) is ignored.
 658          */
 659         if (uap->datalen != 0)
 660                 cmn_err(CE_WARN, "!pcfs: deprecated use of mount(2) with "
 661                     "mount argument structures instead of mount options. "
 662                     "Ignoring mount(2) 'dataptr' argument.");
 663 
 664         /*
 665          * This is needed early, to make sure the access / open calls
 666          * are done using the correct mode. Processing this mount option
 667          * only when calling pcfs_parse_mntopts() would lead us to attempt
 668          * a read/write access to a possibly writeprotected device, and
 669          * a readonly mount attempt might fail because of that.
 670          */
 671         if (uap->flags & MS_RDONLY) {
 672                 vfsp->vfs_flag |= VFS_RDONLY;
 673                 vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
 674         }
 675 
 676         /*
 677          * For most filesystems, this is just a lookupname() on the
 678          * mount pathname string. PCFS historically has to do its own
 679          * partition table parsing because not all Solaris architectures
 680          * support all styles of partitioning that PC media can have, and
 681          * hence PCFS understands "device names" that don't map to actual
 682          * physical device nodes. Parsing the "PCFS syntax" for device
 683          * names is done in pcfs_device_identify() - see there.
 684          *
 685          * Once all block device drivers that can host FAT filesystems have
 686          * been enhanced to create device nodes for all PC-style partitions,
 687          * this code can go away.
 688          */
 689         if (error = pcfs_device_identify(vfsp, uap, cr, &dos_ldrive, &xdev))
 690                 return (error);
 691 
 692         /*
 693          * As with looking up the actual device to mount, PCFS cannot rely
 694          * on just the checks done by vfs_ismounted() whether a given device
 695          * is mounted already. The additional check against the "PCFS syntax"
 696          * is done in  pcfs_device_ismounted().
 697          */
 698         remounting = (uap->flags & MS_REMOUNT);
 699 
 700         if (error = pcfs_device_ismounted(vfsp, dos_ldrive, xdev, &remounting,
 701             &pseudodev))
 702                 return (error);
 703 
 704         if (remounting)
 705                 return (0);
 706 
 707         /*
 708          * Mount the filesystem.
 709          * An instance structure is required before the attempt to locate
 710          * and parse the FAT BPB. This is because mount options may change
 711          * the behaviour of the filesystem type matching code. Precreate
 712          * it and fill it in to a degree that allows parsing the mount
 713          * options.
 714          */
 715         devvp = makespecvp(xdev, VBLK);
 716         if (IS_SWAPVP(devvp)) {
 717                 VN_RELE(devvp);
 718                 return (EBUSY);
 719         }
 720         error = VOP_OPEN(&devvp,
 721             (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD | FWRITE, cr, NULL);
 722         if (error) {
 723                 VN_RELE(devvp);
 724                 return (error);
 725         }
 726 
 727         fsp = kmem_zalloc(sizeof (*fsp), KM_SLEEP);
 728         fsp->pcfs_vfs = vfsp;
 729         fsp->pcfs_xdev = xdev;
 730         fsp->pcfs_devvp = devvp;
 731         fsp->pcfs_ldrive = dos_ldrive;
 732         mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL);
 733 
 734         pcfs_parse_mntopts(fsp);
 735 
 736         /*
 737          * This is the actual "mount" - the PCFS superblock check.
 738          *
 739          * Find the requested logical drive and the FAT BPB therein.
 740          * Check device type and flag the instance if media is removeable.
 741          *
 742          * Initializes most members of the filesystem instance structure.
 743          * Returns EINVAL if no valid BPB can be found. Other errors may
 744          * occur after I/O failures, or when invalid / unparseable partition
 745          * tables are encountered.
 746          */
 747         if (error = pc_getfattype(fsp))
 748                 goto errout;
 749 
 750         /*
 751          * Now that the BPB has been parsed, this structural information
 752          * is available and known to be valid. Initialize the VFS.
 753          */
 754         vfsp->vfs_data = fsp;
 755         vfsp->vfs_dev = pseudodev;
 756         vfsp->vfs_fstype = pcfstype;
 757         vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype);
 758         vfsp->vfs_bcount = 0;
 759         vfsp->vfs_bsize = fsp->pcfs_clsize;
 760 
 761         /*
 762          * Validate that we can access the FAT and that it is, to the
 763          * degree we can verify here, self-consistent.
 764          */
 765         if (error = pc_verify(fsp))
 766                 goto errout;
 767 
 768         /*
 769          * Record the time of the mount, to return as an "approximate"
 770          * timestamp for the FAT root directory. Since FAT roots don't
 771          * have timestamps, this is less confusing to the user than
 772          * claiming "zero" / Jan/01/1970.
 773          */
 774         gethrestime(&fsp->pcfs_mounttime);
 775 
 776         /*
 777          * Fix up the mount options. Because "noatime" is made default on
 778          * removeable media only, a fixed disk will have neither "atime"
 779          * nor "noatime" set. We set the options explicitly depending on
 780          * the PCFS_NOATIME flag, to inform the user of what applies.
 781          * Mount option cancellation will take care that the mutually
 782          * exclusive 'other' is cleared.
 783          */
 784         vfs_setmntopt(vfsp,
 785             fsp->pcfs_flags & PCFS_NOATIME ? MNTOPT_NOATIME : MNTOPT_ATIME,
 786             NULL, 0);
 787 
 788         /*
 789          * All clear - insert the FS instance into PCFS' list.
 790          */
 791         mutex_enter(&pcfslock);
 792         fsp->pcfs_nxt = pc_mounttab;
 793         pc_mounttab = fsp;
 794         mutex_exit(&pcfslock);
 795         atomic_inc_32(&pcfs_mountcount);
 796         return (0);
 797 
 798 errout:
 799         (void) VOP_CLOSE(devvp,
 800             vfsp->vfs_flag & VFS_RDONLY ? FREAD : FREAD | FWRITE,
 801             1, (offset_t)0, cr, NULL);
 802         VN_RELE(devvp);
 803         mutex_destroy(&fsp->pcfs_lock);
 804         kmem_free(fsp, sizeof (*fsp));
 805         return (error);
 806 
 807 }
 808 
 809 static int
 810 pcfs_unmount(
 811         struct vfs *vfsp,
 812         int flag,
 813         struct cred *cr)
 814 {
 815         struct pcfs *fsp, *fsp1;
 816 
 817         if (secpolicy_fs_unmount(cr, vfsp) != 0)
 818                 return (EPERM);
 819 
 820         fsp = VFSTOPCFS(vfsp);
 821 
 822         /*
 823          * We don't have to lock fsp because the VVFSLOCK in vfs layer will
 824          * prevent lookuppn from crossing the mount point.
 825          * If this is not a forced umount request and there's ongoing I/O,
 826          * don't allow the mount to proceed.
 827          */
 828         if (flag & MS_FORCE)
 829                 vfsp->vfs_flag |= VFS_UNMOUNTED;
 830         else if (fsp->pcfs_nrefs)
 831                 return (EBUSY);
 832 
 833         mutex_enter(&pcfslock);
 834 
 835         /*
 836          * If this is a forced umount request or if the fs instance has
 837          * been marked as beyond recovery, allow the umount to proceed
 838          * regardless of state. pc_diskchanged() forcibly releases all
 839          * inactive vnodes/pcnodes.
 840          */
 841         if (flag & MS_FORCE || fsp->pcfs_flags & PCFS_IRRECOV) {
 842                 rw_enter(&pcnodes_lock, RW_WRITER);
 843                 pc_diskchanged(fsp);
 844                 rw_exit(&pcnodes_lock);
 845         }
 846 
 847         /* now there should be no pcp node on pcfhead or pcdhead. */
 848 
 849         if (fsp == pc_mounttab) {
 850                 pc_mounttab = fsp->pcfs_nxt;
 851         } else {
 852                 for (fsp1 = pc_mounttab; fsp1 != NULL; fsp1 = fsp1->pcfs_nxt)
 853                         if (fsp1->pcfs_nxt == fsp)
 854                                 fsp1->pcfs_nxt = fsp->pcfs_nxt;
 855         }
 856 
 857         mutex_exit(&pcfslock);
 858 
 859         /*
 860          * Since we support VFS_FREEVFS(), there's no need to
 861          * free the fsp right now. The framework will tell us
 862          * when the right time to do so has arrived by calling
 863          * into pcfs_freevfs.
 864          */
 865         return (0);
 866 }
 867 
 868 /*
 869  * find root of pcfs
 870  */
 871 static int
 872 pcfs_root(
 873         struct vfs *vfsp,
 874         struct vnode **vpp)
 875 {
 876         struct pcfs *fsp;
 877         struct pcnode *pcp;
 878         int error;
 879 
 880         fsp = VFSTOPCFS(vfsp);
 881         if (error = pc_lockfs(fsp, 0, 0))
 882                 return (error);
 883 
 884         pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
 885         pc_unlockfs(fsp);
 886         *vpp = PCTOV(pcp);
 887         pcp->pc_flags |= PC_EXTERNAL;
 888         return (0);
 889 }
 890 
 891 /*
 892  * Get file system statistics.
 893  */
 894 static int
 895 pcfs_statvfs(
 896         struct vfs *vfsp,
 897         struct statvfs64 *sp)
 898 {
 899         struct pcfs *fsp;
 900         int error;
 901         dev32_t d32;
 902 
 903         fsp = VFSTOPCFS(vfsp);
 904         error = pc_getfat(fsp);
 905         if (error)
 906                 return (error);
 907         bzero(sp, sizeof (*sp));
 908         sp->f_bsize = sp->f_frsize = fsp->pcfs_clsize;
 909         sp->f_blocks = (fsblkcnt64_t)fsp->pcfs_ncluster;
 910         sp->f_bavail = sp->f_bfree = (fsblkcnt64_t)pc_freeclusters(fsp);
 911         sp->f_files = (fsfilcnt64_t)-1;
 912         sp->f_ffree = (fsfilcnt64_t)-1;
 913         sp->f_favail = (fsfilcnt64_t)-1;
 914 #ifdef notdef
 915         (void) cmpldev(&d32, fsp->pcfs_devvp->v_rdev);
 916 #endif /* notdef */
 917         (void) cmpldev(&d32, vfsp->vfs_dev);
 918         sp->f_fsid = d32;
 919         (void) strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name);
 920         sp->f_flag = vf_to_stf(vfsp->vfs_flag);
 921         sp->f_namemax = PCMAXNAMLEN;
 922         return (0);
 923 }
 924 
 925 static int
 926 pc_syncfsnodes(struct pcfs *fsp)
 927 {
 928         struct pchead *hp;
 929         struct pcnode *pcp;
 930         int error;
 931 
 932         if (error = pc_lockfs(fsp, 0, 0))
 933                 return (error);
 934 
 935         if (!(error = pc_syncfat(fsp))) {
 936                 hp = pcfhead;
 937                 while (hp < & pcfhead [ NPCHASH ]) {
 938                         rw_enter(&pcnodes_lock, RW_READER);
 939                         pcp = hp->pch_forw;
 940                         while (pcp != (struct pcnode *)hp) {
 941                                 if (VFSTOPCFS(PCTOV(pcp) -> v_vfsp) == fsp)
 942                                         if (error = pc_nodesync(pcp))
 943                                                 break;
 944                                 pcp = pcp -> pc_forw;
 945                         }
 946                         rw_exit(&pcnodes_lock);
 947                         if (error)
 948                                 break;
 949                         hp++;
 950                 }
 951         }
 952         pc_unlockfs(fsp);
 953         return (error);
 954 }
 955 
 956 /*
 957  * Flush any pending I/O.
 958  */
 959 /*ARGSUSED*/
 960 static int
 961 pcfs_sync(
 962         struct vfs *vfsp,
 963         short flag,
 964         struct cred *cr)
 965 {
 966         struct pcfs *fsp;
 967         int error = 0;
 968 
 969         /* this prevents the filesystem from being umounted. */
 970         mutex_enter(&pcfslock);
 971         if (vfsp != NULL) {
 972                 fsp = VFSTOPCFS(vfsp);
 973                 if (!(fsp->pcfs_flags & PCFS_IRRECOV)) {
 974                         error = pc_syncfsnodes(fsp);
 975                 } else {
 976                         rw_enter(&pcnodes_lock, RW_WRITER);
 977                         pc_diskchanged(fsp);
 978                         rw_exit(&pcnodes_lock);
 979                         error = EIO;
 980                 }
 981         } else {
 982                 fsp = pc_mounttab;
 983                 while (fsp != NULL) {
 984                         if (fsp->pcfs_flags & PCFS_IRRECOV) {
 985                                 rw_enter(&pcnodes_lock, RW_WRITER);
 986                                 pc_diskchanged(fsp);
 987                                 rw_exit(&pcnodes_lock);
 988                                 error = EIO;
 989                                 break;
 990                         }
 991                         error = pc_syncfsnodes(fsp);
 992                         if (error) break;
 993                         fsp = fsp->pcfs_nxt;
 994                 }
 995         }
 996         mutex_exit(&pcfslock);
 997         return (error);
 998 }
 999 
1000 int
1001 pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing)
1002 {
1003         int err;
1004 
1005         if ((fsp->pcfs_flags & PCFS_IRRECOV) && !releasing)
1006                 return (EIO);
1007 
1008         if ((fsp->pcfs_flags & PCFS_LOCKED) && (fsp->pcfs_owner == curthread)) {
1009                 fsp->pcfs_count++;
1010         } else {
1011                 mutex_enter(&fsp->pcfs_lock);
1012                 if (fsp->pcfs_flags & PCFS_LOCKED)
1013                         panic("pc_lockfs");
1014                 /*
1015                  * We check the IRRECOV bit again just in case somebody
1016                  * snuck past the initial check but then got held up before
1017                  * they could grab the lock.  (And in the meantime someone
1018                  * had grabbed the lock and set the bit)
1019                  */
1020                 if (!diskchanged && !(fsp->pcfs_flags & PCFS_IRRECOV)) {
1021                         if ((err = pc_getfat(fsp))) {
1022                                 mutex_exit(&fsp->pcfs_lock);
1023                                 return (err);
1024                         }
1025                 }
1026                 fsp->pcfs_flags |= PCFS_LOCKED;
1027                 fsp->pcfs_owner = curthread;
1028                 fsp->pcfs_count++;
1029         }
1030         return (0);
1031 }
1032 
1033 void
1034 pc_unlockfs(struct pcfs *fsp)
1035 {
1036 
1037         if ((fsp->pcfs_flags & PCFS_LOCKED) == 0)
1038                 panic("pc_unlockfs");
1039         if (--fsp->pcfs_count < 0)
1040                 panic("pc_unlockfs: count");
1041         if (fsp->pcfs_count == 0) {
1042                 fsp->pcfs_flags &= ~PCFS_LOCKED;
1043                 fsp->pcfs_owner = 0;
1044                 mutex_exit(&fsp->pcfs_lock);
1045         }
1046 }
1047 
1048 int
1049 pc_syncfat(struct pcfs *fsp)
1050 {
1051         struct buf *bp;
1052         int nfat;
1053         int     error = 0;
1054         struct fat_od_fsi *fsinfo_disk;
1055 
1056         if ((fsp->pcfs_fatp == (uchar_t *)0) ||
1057             !(fsp->pcfs_flags & PCFS_FATMOD))
1058                 return (0);
1059         /*
1060          * write out all copies of FATs
1061          */
1062         fsp->pcfs_flags &= ~PCFS_FATMOD;
1063         fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
1064         for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) {
1065                 error = pc_writefat(fsp, pc_dbdaddr(fsp,
1066                     fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec));
1067                 if (error) {
1068                         pc_mark_irrecov(fsp);
1069                         return (EIO);
1070                 }
1071         }
1072         pc_clear_fatchanges(fsp);
1073 
1074         /*
1075          * Write out fsinfo sector.
1076          */
1077         if (IS_FAT32(fsp)) {
1078                 bp = bread(fsp->pcfs_xdev,
1079                     pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
1080                 if (bp->b_flags & (B_ERROR | B_STALE)) {
1081                         error = geterror(bp);
1082                 }
1083                 fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
1084                 if (!error && FSISIG_OK(fsinfo_disk)) {
1085                         fsinfo_disk->fsi_incore.fs_free_clusters =
1086                             LE_32(fsp->pcfs_fsinfo.fs_free_clusters);
1087                         fsinfo_disk->fsi_incore.fs_next_free =
1088                             LE_32(FSINFO_UNKNOWN);
1089                         bwrite2(bp);
1090                         error = geterror(bp);
1091                 }
1092                 brelse(bp);
1093                 if (error) {
1094                         pc_mark_irrecov(fsp);
1095                         return (EIO);
1096                 }
1097         }
1098         return (0);
1099 }
1100 
1101 void
1102 pc_invalfat(struct pcfs *fsp)
1103 {
1104         struct pcfs *xfsp;
1105         int mount_cnt = 0;
1106 
1107         if (fsp->pcfs_fatp == (uchar_t *)0)
1108                 panic("pc_invalfat");
1109         /*
1110          * Release FAT
1111          */
1112         kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsec * fsp->pcfs_secsize);
1113         fsp->pcfs_fatp = NULL;
1114         kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize);
1115         fsp->pcfs_fat_changemap = NULL;
1116         /*
1117          * Invalidate all the blocks associated with the device.
1118          * Not needed if stateless.
1119          */
1120         for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt)
1121                 if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev)
1122                         mount_cnt++;
1123 
1124         if (!mount_cnt)
1125                 binval(fsp->pcfs_xdev);
1126         /*
1127          * close mounted device
1128          */
1129         (void) VOP_CLOSE(fsp->pcfs_devvp,
1130             (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
1131             1, (offset_t)0, CRED(), NULL);
1132 }
1133 
1134 void
1135 pc_badfs(struct pcfs *fsp)
1136 {
1137         cmn_err(CE_WARN, "corrupted PC file system on dev (%x.%x):%d\n",
1138             getmajor(fsp->pcfs_devvp->v_rdev),
1139             getminor(fsp->pcfs_devvp->v_rdev), fsp->pcfs_ldrive);
1140 }
1141 
1142 /*
1143  * The problem with supporting NFS on the PCFS filesystem is that there
1144  * is no good place to keep the generation number. The only possible
1145  * place is inside a directory entry. There are a few words that we
1146  * don't use - they store NT & OS/2 attributes, and the creation/last access
1147  * time of the file - but it seems wrong to use them. In addition, directory
1148  * entries come and go. If a directory is removed completely, its directory
1149  * blocks are freed and the generation numbers are lost. Whereas in ufs,
1150  * inode blocks are dedicated for inodes, so the generation numbers are
1151  * permanently kept on the disk.
1152  */
1153 static int
1154 pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp)
1155 {
1156         struct pcnode *pcp;
1157         struct pc_fid *pcfid;
1158         struct pcfs *fsp;
1159         struct pcdir *ep;
1160         daddr_t eblkno;
1161         int eoffset;
1162         struct buf *bp;
1163         int error;
1164         pc_cluster32_t  cn;
1165 
1166         pcfid = (struct pc_fid *)fidp;
1167         fsp = VFSTOPCFS(vfsp);
1168 
1169         error = pc_lockfs(fsp, 0, 0);
1170         if (error) {
1171                 *vpp = NULL;
1172                 return (error);
1173         }
1174 
1175         if (pcfid->pcfid_block == 0) {
1176                 pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
1177                 pcp->pc_flags |= PC_EXTERNAL;
1178                 *vpp = PCTOV(pcp);
1179                 pc_unlockfs(fsp);
1180                 return (0);
1181         }
1182         eblkno = pcfid->pcfid_block;
1183         eoffset = pcfid->pcfid_offset;
1184 
1185         if ((pc_dbtocl(fsp,
1186             eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) ||
1187             (eoffset > fsp->pcfs_clsize)) {
1188                 pc_unlockfs(fsp);
1189                 *vpp = NULL;
1190                 return (EINVAL);
1191         }
1192 
1193         if (eblkno >= fsp->pcfs_datastart || (eblkno - fsp->pcfs_rdirstart)
1194             < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
1195                 bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
1196                     fsp->pcfs_clsize);
1197         } else {
1198                 /*
1199                  * This is an access "backwards" into the FAT12/FAT16
1200                  * root directory. A better code structure would
1201                  * significantly improve maintainability here ...
1202                  */
1203                 bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
1204                     (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize);
1205         }
1206         if (bp->b_flags & (B_ERROR | B_STALE)) {
1207                 error = geterror(bp);
1208                 brelse(bp);
1209                 if (error)
1210                         pc_mark_irrecov(fsp);
1211                 *vpp = NULL;
1212                 pc_unlockfs(fsp);
1213                 return (error);
1214         }
1215         ep = (struct pcdir *)(bp->b_un.b_addr + eoffset);
1216         /*
1217          * Ok, if this is a valid file handle that we gave out,
1218          * then simply ensuring that the creation time matches,
1219          * the entry has not been deleted, and it has a valid first
1220          * character should be enough.
1221          *
1222          * Unfortunately, verifying that the <blkno, offset> _still_
1223          * refers to a directory entry is not easy, since we'd have
1224          * to search _all_ directories starting from root to find it.
1225          * That's a high price to pay just in case somebody is forging
1226          * file handles. So instead we verify that as much of the
1227          * entry is valid as we can:
1228          *
1229          * 1. The starting cluster is 0 (unallocated) or valid
1230          * 2. It is not an LFN entry
1231          * 3. It is not hidden (unless mounted as such)
1232          * 4. It is not the label
1233          */
1234         cn = pc_getstartcluster(fsp, ep);
1235         /*
1236          * if the starting cluster is valid, but not valid according
1237          * to pc_validcl(), force it to be to simplify the following if.
1238          */
1239         if (cn == 0)
1240                 cn = PCF_FIRSTCLUSTER;
1241         if (IS_FAT32(fsp)) {
1242                 if (cn >= PCF_LASTCLUSTER32)
1243                         cn = PCF_FIRSTCLUSTER;
1244         } else {
1245                 if (cn >= PCF_LASTCLUSTER)
1246                         cn = PCF_FIRSTCLUSTER;
1247         }
1248         if ((!pc_validcl(fsp, cn)) ||
1249             (PCDL_IS_LFN(ep)) ||
1250             (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) ||
1251             ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) {
1252                 bp->b_flags |= B_STALE | B_AGE;
1253                 brelse(bp);
1254                 pc_unlockfs(fsp);
1255                 return (EINVAL);
1256         }
1257         if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) &&
1258             (ep->pcd_filename[0] != PCD_ERASED) &&
1259             (pc_validchar(ep->pcd_filename[0]) ||
1260             (ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) {
1261                 pcp = pc_getnode(fsp, eblkno, eoffset, ep);
1262                 pcp->pc_flags |= PC_EXTERNAL;
1263                 *vpp = PCTOV(pcp);
1264         } else {
1265                 *vpp = NULL;
1266         }
1267         bp->b_flags |= B_STALE | B_AGE;
1268         brelse(bp);
1269         pc_unlockfs(fsp);
1270         return (0);
1271 }
1272 
1273 /*
1274  * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about
1275  * a meg), so we can't bread() it all in at once. This routine reads a
1276  * fat a chunk at a time.
1277  */
1278 static int
1279 pc_readfat(struct pcfs *fsp, uchar_t *fatp)
1280 {
1281         struct buf *bp;
1282         size_t off;
1283         size_t readsize;
1284         daddr_t diskblk;
1285         size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
1286         daddr_t start = fsp->pcfs_fatstart;
1287 
1288         readsize = fsp->pcfs_clsize;
1289         for (off = 0; off < fatsize; off += readsize, fatp += readsize) {
1290                 if (readsize > (fatsize - off))
1291                         readsize = fatsize - off;
1292                 diskblk = pc_dbdaddr(fsp, start +
1293                     pc_cltodb(fsp, pc_lblkno(fsp, off)));
1294                 bp = bread(fsp->pcfs_xdev, diskblk, readsize);
1295                 if (bp->b_flags & (B_ERROR | B_STALE)) {
1296                         brelse(bp);
1297                         return (EIO);
1298                 }
1299                 bp->b_flags |= B_STALE | B_AGE;
1300                 bcopy(bp->b_un.b_addr, fatp, readsize);
1301                 brelse(bp);
1302         }
1303         return (0);
1304 }
1305 
1306 /*
1307  * We write the FAT out a _lot_, in order to make sure that it
1308  * is up-to-date. But on a FAT32 system (large drive, small clusters)
1309  * the FAT might be a couple of megabytes, and writing it all out just
1310  * because we created or deleted a small file is painful (especially
1311  * since we do it for each alternate FAT too). So instead, for FAT16 and
1312  * FAT32 we only write out the bit that has changed. We don't clear
1313  * the 'updated' fields here because the caller might be writing out
1314  * several FATs, so the caller must use pc_clear_fatchanges() after
1315  * all FATs have been updated.
1316  * This function doesn't take "start" from fsp->pcfs_dosstart because
1317  * callers can use it to write either the primary or any of the alternate
1318  * FAT tables.
1319  */
1320 static int
1321 pc_writefat(struct pcfs *fsp, daddr_t start)
1322 {
1323         struct buf *bp;
1324         size_t off;
1325         size_t writesize;
1326         int     error;
1327         uchar_t *fatp = fsp->pcfs_fatp;
1328         size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
1329 
1330         writesize = fsp->pcfs_clsize;
1331         for (off = 0; off < fatsize; off += writesize, fatp += writesize) {
1332                 if (writesize > (fatsize - off))
1333                         writesize = fatsize - off;
1334                 if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) {
1335                         continue;
1336                 }
1337                 bp = ngeteblk(writesize);
1338                 bp->b_edev = fsp->pcfs_xdev;
1339                 bp->b_dev = cmpdev(bp->b_edev);
1340                 bp->b_blkno = pc_dbdaddr(fsp, start +
1341                     pc_cltodb(fsp, pc_lblkno(fsp, off)));
1342                 bcopy(fatp, bp->b_un.b_addr, writesize);
1343                 bwrite2(bp);
1344                 error = geterror(bp);
1345                 brelse(bp);
1346                 if (error) {
1347                         return (error);
1348                 }
1349         }
1350         return (0);
1351 }
1352 
1353 /*
1354  * Mark the FAT cluster that 'cn' is stored in as modified.
1355  */
1356 void
1357 pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn)
1358 {
1359         pc_cluster32_t  bn;
1360         size_t          size;
1361 
1362         /* which fat block is the cluster number stored in? */
1363         if (IS_FAT32(fsp)) {
1364                 size = sizeof (pc_cluster32_t);
1365                 bn = pc_lblkno(fsp, cn * size);
1366                 fsp->pcfs_fat_changemap[bn] = 1;
1367         } else if (IS_FAT16(fsp)) {
1368                 size = sizeof (pc_cluster16_t);
1369                 bn = pc_lblkno(fsp, cn * size);
1370                 fsp->pcfs_fat_changemap[bn] = 1;
1371         } else {
1372                 offset_t off;
1373                 pc_cluster32_t nbn;
1374 
1375                 ASSERT(IS_FAT12(fsp));
1376                 off = cn + (cn >> 1);
1377                 bn = pc_lblkno(fsp, off);
1378                 fsp->pcfs_fat_changemap[bn] = 1;
1379                 /* does this field wrap into the next fat cluster? */
1380                 nbn = pc_lblkno(fsp, off + 1);
1381                 if (nbn != bn) {
1382                         fsp->pcfs_fat_changemap[nbn] = 1;
1383                 }
1384         }
1385 }
1386 
1387 /*
1388  * return whether the FAT cluster 'bn' is updated and needs to
1389  * be written out.
1390  */
1391 int
1392 pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn)
1393 {
1394         return (fsp->pcfs_fat_changemap[bn] == 1);
1395 }
1396 
1397 /*
1398  * Implementation of VFS_FREEVFS() to support forced umounts.
1399  * This is called by the vfs framework after umount, to trigger
1400  * the release of any resources still associated with the given
1401  * vfs_t once the need to keep them has gone away.
1402  */
1403 void
1404 pcfs_freevfs(vfs_t *vfsp)
1405 {
1406         struct pcfs *fsp = VFSTOPCFS(vfsp);
1407 
1408         mutex_enter(&pcfslock);
1409         /*
1410          * Purging the FAT closes the device - can't do any more
1411          * I/O after this.
1412          */
1413         if (fsp->pcfs_fatp != (uchar_t *)0)
1414                 pc_invalfat(fsp);
1415         mutex_exit(&pcfslock);
1416 
1417         VN_RELE(fsp->pcfs_devvp);
1418         mutex_destroy(&fsp->pcfs_lock);
1419         kmem_free(fsp, sizeof (*fsp));
1420 
1421         /*
1422          * Allow _fini() to succeed now, if so desired.
1423          */
1424         atomic_dec_32(&pcfs_mountcount);
1425 }
1426 
1427 
1428 /*
1429  * PC-style partition parsing and FAT BPB identification/validation code.
1430  * The partition parsers here assume:
1431  *      - a FAT filesystem will be in a partition that has one of a set of
1432  *        recognized partition IDs
1433  *      - the user wants the 'numbering' (C:, D:, ...) that one would get
1434  *        on MSDOS 6.x.
1435  *        That means any non-FAT partition type (NTFS, HPFS, or any Linux fs)
1436  *        will not factor in the enumeration.
1437  * These days, such assumptions should be revisited. FAT is no longer the
1438  * only game in 'PC town'.
1439  */
1440 /*
1441  * isDosDrive()
1442  *      Boolean function.  Give it the systid field for an fdisk partition
1443  *      and it decides if that's a systid that describes a DOS drive.  We
1444  *      use systid values defined in sys/dktp/fdisk.h.
1445  */
1446 static int
1447 isDosDrive(uchar_t checkMe)
1448 {
1449         return ((checkMe == DOSOS12) || (checkMe == DOSOS16) ||
1450             (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) ||
1451             (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) ||
1452             (checkMe == DIAGPART));
1453 }
1454 
1455 
1456 /*
1457  * isDosExtended()
1458  *      Boolean function.  Give it the systid field for an fdisk partition
1459  *      and it decides if that's a systid that describes an extended DOS
1460  *      partition.
1461  */
1462 static int
1463 isDosExtended(uchar_t checkMe)
1464 {
1465         return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA));
1466 }
1467 
1468 
1469 /*
1470  * isBootPart()
1471  *      Boolean function.  Give it the systid field for an fdisk partition
1472  *      and it decides if that's a systid that describes a Solaris boot
1473  *      partition.
1474  */
1475 static int
1476 isBootPart(uchar_t checkMe)
1477 {
1478         return (checkMe == X86BOOT);
1479 }
1480 
1481 
1482 /*
1483  * noLogicalDrive()
1484  *      Display error message about not being able to find a logical
1485  *      drive.
1486  */
1487 static void
1488 noLogicalDrive(int ldrive)
1489 {
1490         if (ldrive == BOOT_PARTITION_DRIVE) {
1491                 cmn_err(CE_NOTE, "!pcfs: no boot partition");
1492         } else {
1493                 cmn_err(CE_NOTE, "!pcfs: %d: no such logical drive", ldrive);
1494         }
1495 }
1496 
1497 
1498 /*
1499  * findTheDrive()
1500  *      Discover offset of the requested logical drive, and return
1501  *      that offset (startSector), the systid of that drive (sysid),
1502  *      and a buffer pointer (bp), with the buffer contents being
1503  *      the first sector of the logical drive (i.e., the sector that
1504  *      contains the BPB for that drive).
1505  *
1506  * Note: this code is not capable of addressing >2TB disks, as it uses
1507  *       daddr_t not diskaddr_t, some of the calculations would overflow
1508  */
1509 #define COPY_PTBL(mbr, ptblp)                                   \
1510         bcopy(&(((struct mboot *)(mbr))->parts), (ptblp),        \
1511             FD_NUMPART * sizeof (struct ipart))
1512 
1513 static int
1514 findTheDrive(struct pcfs *fsp, buf_t **bp)
1515 {
1516         int ldrive = fsp->pcfs_ldrive;
1517         dev_t dev = fsp->pcfs_devvp->v_rdev;
1518 
1519         struct ipart dosp[FD_NUMPART];  /* incore fdisk partition structure */
1520         daddr_t lastseek = 0;           /* Disk block we sought previously */
1521         daddr_t diskblk = 0;            /* Disk block to get */
1522         daddr_t xstartsect;             /* base of Extended DOS partition */
1523         int logicalDriveCount = 0;      /* Count of logical drives seen */
1524         int extendedPart = -1;          /* index of extended dos partition */
1525         int primaryPart = -1;           /* index of primary dos partition */
1526         int bootPart = -1;              /* index of a Solaris boot partition */
1527         uint32_t xnumsect = 0;          /* length of extended DOS partition */
1528         int driveIndex;                 /* computed FDISK table index */
1529         daddr_t startsec;
1530         len_t mediasize;
1531         int i;
1532         /*
1533          * Count of drives in the current extended partition's
1534          * FDISK table, and indexes of the drives themselves.
1535          */
1536         int extndDrives[FD_NUMPART];
1537         int numDrives = 0;
1538 
1539         /*
1540          * Count of drives (beyond primary) in master boot record's
1541          * FDISK table, and indexes of the drives themselves.
1542          */
1543         int extraDrives[FD_NUMPART];
1544         int numExtraDrives = 0;
1545 
1546         /*
1547          * "ldrive == 0" should never happen, as this is a request to
1548          * mount the physical device (and ignore partitioning). The code
1549          * in pcfs_mount() should have made sure that a logical drive number
1550          * is at least 1, meaning we're looking for drive "C:". It is not
1551          * safe (and a bug in the callers of this function) to request logical
1552          * drive number 0; we could ASSERT() but a graceful EIO is a more
1553          * polite way.
1554          */
1555         if (ldrive == 0) {
1556                 cmn_err(CE_NOTE, "!pcfs: request for logical partition zero");
1557                 noLogicalDrive(ldrive);
1558                 return (EIO);
1559         }
1560 
1561         /*
1562          *  Copy from disk block into memory aligned structure for fdisk usage.
1563          */
1564         COPY_PTBL((*bp)->b_un.b_addr, dosp);
1565 
1566         /*
1567          * This check is ok because a FAT BPB and a master boot record (MBB)
1568          * have the same signature, in the same position within the block.
1569          */
1570         if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
1571                 cmn_err(CE_NOTE, "!pcfs: MBR partition table signature err, "
1572                     "device (%x.%x):%d\n",
1573                     getmajor(dev), getminor(dev), ldrive);
1574                 return (EINVAL);
1575         }
1576 
1577         /*
1578          * Get a summary of what is in the Master FDISK table.
1579          * Normally we expect to find one partition marked as a DOS drive.
1580          * This partition is the one Windows calls the primary dos partition.
1581          * If the machine has any logical drives then we also expect
1582          * to find a partition marked as an extended DOS partition.
1583          *
1584          * Sometimes we'll find multiple partitions marked as DOS drives.
1585          * The Solaris fdisk program allows these partitions
1586          * to be created, but Windows fdisk no longer does.  We still need
1587          * to support these, though, since Windows does.  We also need to fix
1588          * our fdisk to behave like the Windows version.
1589          *
1590          * It turns out that some off-the-shelf media have *only* an
1591          * Extended partition, so we need to deal with that case as well.
1592          *
1593          * Only a single (the first) Extended or Boot Partition will
1594          * be recognized.  Any others will be ignored.
1595          */
1596         for (i = 0; i < FD_NUMPART; i++) {
1597                 DTRACE_PROBE4(primarypart, struct pcfs *, fsp,
1598                     uint_t, (uint_t)dosp[i].systid,
1599                     uint_t, LE_32(dosp[i].relsect),
1600                     uint_t, LE_32(dosp[i].numsect));
1601 
1602                 if (isDosDrive(dosp[i].systid)) {
1603                         if (primaryPart < 0) {
1604                                 logicalDriveCount++;
1605                                 primaryPart = i;
1606                         } else {
1607                                 extraDrives[numExtraDrives++] = i;
1608                         }
1609                         continue;
1610                 }
1611                 if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) {
1612                         extendedPart = i;
1613                         continue;
1614                 }
1615                 if ((bootPart < 0) && isBootPart(dosp[i].systid)) {
1616                         bootPart = i;
1617                         continue;
1618                 }
1619         }
1620 
1621         if (ldrive == BOOT_PARTITION_DRIVE) {
1622                 if (bootPart < 0) {
1623                         noLogicalDrive(ldrive);
1624                         return (EINVAL);
1625                 }
1626                 startsec = LE_32(dosp[bootPart].relsect);
1627                 mediasize = LE_32(dosp[bootPart].numsect);
1628                 goto found;
1629         }
1630 
1631         if (ldrive == PRIMARY_DOS_DRIVE && primaryPart >= 0) {
1632                 startsec = LE_32(dosp[primaryPart].relsect);
1633                 mediasize = LE_32(dosp[primaryPart].numsect);
1634                 goto found;
1635         }
1636 
1637         /*
1638          * We are not looking for the C: drive (or the primary drive
1639          * was not found), so we had better have an extended partition
1640          * or extra drives in the Master FDISK table.
1641          */
1642         if ((extendedPart < 0) && (numExtraDrives == 0)) {
1643                 cmn_err(CE_NOTE, "!pcfs: no extended dos partition");
1644                 noLogicalDrive(ldrive);
1645                 return (EINVAL);
1646         }
1647 
1648         if (extendedPart >= 0) {
1649                 diskblk = xstartsect = LE_32(dosp[extendedPart].relsect);
1650                 xnumsect = LE_32(dosp[extendedPart].numsect);
1651                 do {
1652                         /*
1653                          *  If the seek would not cause us to change
1654                          *  position on the drive, then we're out of
1655                          *  extended partitions to examine.
1656                          */
1657                         if (diskblk == lastseek)
1658                                 break;
1659                         logicalDriveCount += numDrives;
1660                         /*
1661                          *  Seek the next extended partition, and find
1662                          *  logical drives within it.
1663                          */
1664                         brelse(*bp);
1665                         /*
1666                          * bread() block numbers are multiples of DEV_BSIZE
1667                          * but the device sector size (the unit of partitioning)
1668                          * might be larger than that; pcfs_get_device_info()
1669                          * has calculated the multiplicator for us.
1670                          */
1671                         *bp = bread(dev,
1672                             pc_dbdaddr(fsp, diskblk), fsp->pcfs_secsize);
1673                         if ((*bp)->b_flags & B_ERROR) {
1674                                 return (EIO);
1675                         }
1676 
1677                         lastseek = diskblk;
1678                         COPY_PTBL((*bp)->b_un.b_addr, dosp);
1679                         if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
1680                                 cmn_err(CE_NOTE, "!pcfs: "
1681                                     "extended partition table signature err, "
1682                                     "device (%x.%x):%d, LBA %u",
1683                                     getmajor(dev), getminor(dev), ldrive,
1684                                     (uint_t)pc_dbdaddr(fsp, diskblk));
1685                                 return (EINVAL);
1686                         }
1687                         /*
1688                          *  Count up drives, and track where the next
1689                          *  extended partition is in case we need it.  We
1690                          *  are expecting only one extended partition.  If
1691                          *  there is more than one we'll only go to the
1692                          *  first one we see, but warn about ignoring.
1693                          */
1694                         numDrives = 0;
1695                         for (i = 0; i < FD_NUMPART; i++) {
1696                                 DTRACE_PROBE4(extendedpart,
1697                                     struct pcfs *, fsp,
1698                                     uint_t, (uint_t)dosp[i].systid,
1699                                     uint_t, LE_32(dosp[i].relsect),
1700                                     uint_t, LE_32(dosp[i].numsect));
1701                                 if (isDosDrive(dosp[i].systid)) {
1702                                         extndDrives[numDrives++] = i;
1703                                 } else if (isDosExtended(dosp[i].systid)) {
1704                                         if (diskblk != lastseek) {
1705                                                 /*
1706                                                  * Already found an extended
1707                                                  * partition in this table.
1708                                                  */
1709                                                 cmn_err(CE_NOTE,
1710                                                     "!pcfs: ignoring unexpected"
1711                                                     " additional extended"
1712                                                     " partition");
1713                                         } else {
1714                                                 diskblk = xstartsect +
1715                                                     LE_32(dosp[i].relsect);
1716                                         }
1717                                 }
1718                         }
1719                 } while (ldrive > logicalDriveCount + numDrives);
1720 
1721                 ASSERT(numDrives <= FD_NUMPART);
1722 
1723                 if (ldrive <= logicalDriveCount + numDrives) {
1724                         /*
1725                          * The number of logical drives we've found thus
1726                          * far is enough to get us to the one we were
1727                          * searching for.
1728                          */
1729                         driveIndex = logicalDriveCount + numDrives - ldrive;
1730                         mediasize =
1731                             LE_32(dosp[extndDrives[driveIndex]].numsect);
1732                         startsec =
1733                             LE_32(dosp[extndDrives[driveIndex]].relsect) +
1734                             lastseek;
1735                         if (startsec > (xstartsect + xnumsect)) {
1736                                 cmn_err(CE_NOTE, "!pcfs: extended partition "
1737                                     "values bad");
1738                                 return (EINVAL);
1739                         }
1740                         goto found;
1741                 } else {
1742                         /*
1743                          * We ran out of extended dos partition
1744                          * drives.  The only hope now is to go
1745                          * back to extra drives defined in the master
1746                          * fdisk table.  But we overwrote that table
1747                          * already, so we must load it in again.
1748                          */
1749                         logicalDriveCount += numDrives;
1750                         brelse(*bp);
1751                         ASSERT(fsp->pcfs_dosstart == 0);
1752                         *bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
1753                             fsp->pcfs_secsize);
1754                         if ((*bp)->b_flags & B_ERROR) {
1755                                 return (EIO);
1756                         }
1757                         COPY_PTBL((*bp)->b_un.b_addr, dosp);
1758                 }
1759         }
1760         /*
1761          *  Still haven't found the drive, is it an extra
1762          *  drive defined in the main FDISK table?
1763          */
1764         if (ldrive <= logicalDriveCount + numExtraDrives) {
1765                 driveIndex = logicalDriveCount + numExtraDrives - ldrive;
1766                 ASSERT(driveIndex < MIN(numExtraDrives, FD_NUMPART));
1767                 mediasize = LE_32(dosp[extraDrives[driveIndex]].numsect);
1768                 startsec = LE_32(dosp[extraDrives[driveIndex]].relsect);
1769                 goto found;
1770         }
1771         /*
1772          *  Still haven't found the drive, and there is
1773          *  nowhere else to look.
1774          */
1775         noLogicalDrive(ldrive);
1776         return (EINVAL);
1777 
1778 found:
1779         /*
1780          * We need this value in units of sectorsize, because PCFS' internal
1781          * offset calculations go haywire for > 512Byte sectors unless all
1782          * pcfs_.*start values are in units of sectors.
1783          * So, assign before the capacity check (that's done in DEV_BSIZE)
1784          */
1785         fsp->pcfs_dosstart = startsec;
1786 
1787         /*
1788          * convert from device sectors to proper units:
1789          *      - starting sector: DEV_BSIZE (as argument to bread())
1790          *      - media size: Bytes
1791          */
1792         startsec = pc_dbdaddr(fsp, startsec);
1793         mediasize *= fsp->pcfs_secsize;
1794 
1795         /*
1796          * some additional validation / warnings in case the partition table
1797          * and the actual media capacity are not in accordance ...
1798          */
1799         if (fsp->pcfs_mediasize != 0) {
1800                 diskaddr_t startoff =
1801                     (diskaddr_t)startsec * (diskaddr_t)DEV_BSIZE;
1802 
1803                 if (startoff >= fsp->pcfs_mediasize ||
1804                     startoff + mediasize > fsp->pcfs_mediasize) {
1805                         cmn_err(CE_WARN,
1806                             "!pcfs: partition size (LBA start %u, %lld bytes, "
1807                             "device (%x.%x):%d) smaller than "
1808                             "mediasize (%lld bytes).\n"
1809                             "filesystem may be truncated, access errors "
1810                             "may result.\n",
1811                             (uint_t)startsec, (long long)mediasize,
1812                             getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
1813                             fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
1814                 }
1815         } else {
1816                 fsp->pcfs_mediasize = mediasize;
1817         }
1818 
1819         return (0);
1820 }
1821 
1822 
1823 static fattype_t
1824 secondaryBPBChecks(struct pcfs *fsp, uchar_t *bpb, size_t secsize)
1825 {
1826         uint32_t ncl = fsp->pcfs_ncluster;
1827 
1828         if (ncl <= 4096) {
1829                 if (bpb_get_FatSz16(bpb) == 0)
1830                         return (FAT_UNKNOWN);
1831 
1832                 if (bpb_get_FatSz16(bpb) * secsize < ncl * 2 &&
1833                     bpb_get_FatSz16(bpb) * secsize >= (3 * ncl / 2))
1834                         return (FAT12);
1835                 if (bcmp(bpb_FilSysType16(bpb), "FAT12", 5) == 0)
1836                         return (FAT12);
1837                 if (bcmp(bpb_FilSysType16(bpb), "FAT16", 5) == 0)
1838                         return (FAT16);
1839 
1840                 switch (bpb_get_Media(bpb)) {
1841                         case SS8SPT:
1842                         case DS8SPT:
1843                         case SS9SPT:
1844                         case DS9SPT:
1845                         case DS18SPT:
1846                         case DS9_15SPT:
1847                                 /*
1848                                  * Is this reliable - all floppies are FAT12 ?
1849                                  */
1850                                 return (FAT12);
1851                         case MD_FIXED:
1852                                 /*
1853                                  * Is this reliable - disks are always FAT16 ?
1854                                  */
1855                                 return (FAT16);
1856                         default:
1857                                 break;
1858                 }
1859         } else if (ncl <= 65536) {
1860                 if (bpb_get_FatSz16(bpb) == 0 && bpb_get_FatSz32(bpb) > 0)
1861                         return (FAT32);
1862                 if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
1863                         return (FAT32);
1864                 if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
1865                         return (FAT32);
1866 
1867                 if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
1868                         return (FAT16);
1869                 if (bpb_get_FatSz16(bpb) * secsize < ncl * 4)
1870                         return (FAT16);
1871         }
1872 
1873         /*
1874          * We don't know
1875          */
1876         return (FAT_UNKNOWN);
1877 }
1878 
1879 /*
1880  * Check to see if the BPB we found is correct.
1881  *
1882  * This looks far more complicated that it needs to be for pure structural
1883  * validation. The reason for this is that parseBPB() is also used for
1884  * debugging purposes (mdb dcmd) and we therefore want a bitmap of which
1885  * BPB fields (do not) have 'known good' values, even if we (do not) reject
1886  * the BPB when attempting to mount the filesystem.
1887  *
1888  * Real-world usage of FAT shows there are a lot of corner-case situations
1889  * and, following the specification strictly, invalid filesystems out there.
1890  * Known are situations such as:
1891  *      - FAT12/FAT16 filesystems with garbage in either totsec16/32
1892  *        instead of the zero in one of the fields mandated by the spec
1893  *      - filesystems that claim to be larger than the partition they're in
1894  *      - filesystems without valid media descriptor
1895  *      - FAT32 filesystems with RootEntCnt != 0
1896  *      - FAT32 filesystems with less than 65526 clusters
1897  *      - FAT32 filesystems without valid FSI sector
1898  *      - FAT32 filesystems with FAT size in fatsec16 instead of fatsec32
1899  *
1900  * Such filesystems are accessible by PCFS - if it'd know to start with that
1901  * the filesystem should be treated as a specific FAT type. Before S10, it
1902  * relied on the PC/fdisk partition type for the purpose and almost completely
1903  * ignored the BPB; now it ignores the partition type for anything else but
1904  * logical drive enumeration, which can result in rejection of (invalid)
1905  * FAT32 - if the partition ID says FAT32, but the filesystem, for example
1906  * has less than 65526 clusters.
1907  *
1908  * Without a "force this fs as FAT{12,16,32}" tunable or mount option, it's
1909  * not possible to allow all such mostly-compliant filesystems in unless one
1910  * accepts false positives (definitely invalid filesystems that cause problems
1911  * later). This at least allows to pinpoint why the mount failed.
1912  *
1913  * Due to the use of FAT on removeable media, all relaxations of the rules
1914  * here need to be carefully evaluated wrt. to potential effects on PCFS
1915  * resilience. A faulty/"mis-crafted" filesystem must not cause a panic, so
1916  * beware.
1917  */
1918 static int
1919 parseBPB(struct pcfs *fsp, uchar_t *bpb, int *valid)
1920 {
1921         fattype_t type;
1922 
1923         uint32_t        ncl;    /* number of clusters in file area */
1924         uint32_t        rec;
1925         uint32_t        reserved;
1926         uint32_t        fsisec, bkbootsec;
1927         blkcnt_t        totsec, totsec16, totsec32, datasec;
1928         size_t          fatsec, fatsec16, fatsec32, rdirsec;
1929         size_t          secsize;
1930         len_t           mediasize;
1931         uint64_t        validflags = 0;
1932 
1933         if (VALID_BPBSIG(bpb_get_BPBSig(bpb)))
1934                 validflags |= BPB_BPBSIG_OK;
1935 
1936         rec = bpb_get_RootEntCnt(bpb);
1937         reserved = bpb_get_RsvdSecCnt(bpb);
1938         fsisec = bpb_get_FSInfo32(bpb);
1939         bkbootsec = bpb_get_BkBootSec32(bpb);
1940         totsec16 = (blkcnt_t)bpb_get_TotSec16(bpb);
1941         totsec32 = (blkcnt_t)bpb_get_TotSec32(bpb);
1942         fatsec16 = bpb_get_FatSz16(bpb);
1943         fatsec32 = bpb_get_FatSz32(bpb);
1944 
1945         totsec = totsec16 ? totsec16 : totsec32;
1946         fatsec = fatsec16 ? fatsec16 : fatsec32;
1947 
1948         secsize = bpb_get_BytesPerSec(bpb);
1949         if (!VALID_SECSIZE(secsize))
1950                 secsize = fsp->pcfs_secsize;
1951         if (secsize != fsp->pcfs_secsize) {
1952                 PC_DPRINTF3(3, "!pcfs: parseBPB, device (%x.%x):%d:\n",
1953                     getmajor(fsp->pcfs_xdev),
1954                     getminor(fsp->pcfs_xdev), fsp->pcfs_ldrive);
1955                 PC_DPRINTF2(3, "!BPB secsize %d != "
1956                     "autodetected media block size %d\n",
1957                     (int)secsize, (int)fsp->pcfs_secsize);
1958                 if (fsp->pcfs_ldrive) {
1959                         /*
1960                          * We've already attempted to parse the partition
1961                          * table. If the block size used for that don't match
1962                          * the PCFS sector size, we're hosed one way or the
1963                          * other. Just try what happens.
1964                          */
1965                         secsize = fsp->pcfs_secsize;
1966                         PC_DPRINTF1(3,
1967                             "!pcfs: Using autodetected secsize %d\n",
1968                             (int)secsize);
1969                 } else {
1970                         /*
1971                          * This allows mounting lofi images of PCFS partitions
1972                          * with sectorsize != DEV_BSIZE. We can't parse the
1973                          * partition table on whole-disk images unless the
1974                          * (undocumented) "secsize=..." mount option is used,
1975                          * but at least this allows us to mount if we have
1976                          * an image of a partition.
1977                          */
1978                         PC_DPRINTF1(3,
1979                             "!pcfs: Using BPB secsize %d\n", (int)secsize);
1980                 }
1981         }
1982 
1983         if (fsp->pcfs_mediasize == 0) {
1984                 mediasize = (len_t)totsec * (len_t)secsize;
1985                 /*
1986                  * This is not an error because not all devices support the
1987                  * dkio(7i) mediasize queries, and/or not all devices are
1988                  * partitioned. If we have not been able to figure out the
1989                  * size of the underlaying medium, we have to trust the BPB.
1990                  */
1991                 PC_DPRINTF4(3, "!pcfs: parseBPB: mediasize autodetect failed "
1992                     "on device (%x.%x):%d, trusting BPB totsec (%lld Bytes)\n",
1993                     getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
1994                     fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
1995         } else if ((len_t)totsec * (len_t)secsize > fsp->pcfs_mediasize) {
1996                 cmn_err(CE_WARN,
1997                     "!pcfs: autodetected mediasize (%lld Bytes) smaller than "
1998                     "FAT BPB mediasize (%lld Bytes).\n"
1999                     "truncated filesystem on device (%x.%x):%d, access errors "
2000                     "possible.\n",
2001                     (long long)fsp->pcfs_mediasize,
2002                     (long long)(totsec * (blkcnt_t)secsize),
2003                     getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
2004                     fsp->pcfs_ldrive);
2005                 mediasize = fsp->pcfs_mediasize;
2006         } else {
2007                 /*
2008                  * This is actually ok. A FAT needs not occupy the maximum
2009                  * space available in its partition, it can be shorter.
2010                  */
2011                 mediasize = (len_t)totsec * (len_t)secsize;
2012         }
2013 
2014         /*
2015          * Since we let just about anything pass through this function,
2016          * fence against divide-by-zero here.
2017          */
2018         if (secsize)
2019                 rdirsec = roundup(rec * 32, secsize) / secsize;
2020         else
2021                 rdirsec = 0;
2022 
2023         /*
2024          * This assignment is necessary before pc_dbdaddr() can first be
2025          * used. Must initialize the value here.
2026          */
2027         fsp->pcfs_secsize = secsize;
2028         fsp->pcfs_sdshift = ddi_ffs(secsize / DEV_BSIZE) - 1;
2029 
2030         fsp->pcfs_mediasize = mediasize;
2031 
2032         fsp->pcfs_spcl = bpb_get_SecPerClus(bpb);
2033         fsp->pcfs_numfat = bpb_get_NumFATs(bpb);
2034         fsp->pcfs_mediadesc = bpb_get_Media(bpb);
2035         fsp->pcfs_clsize = secsize * fsp->pcfs_spcl;
2036         fsp->pcfs_rdirsec = rdirsec;
2037 
2038         /*
2039          * Remember: All PCFS offset calculations in sectors. Before I/O
2040          * is done, convert to DEV_BSIZE units via pc_dbdaddr(). This is
2041          * necessary so that media with > 512Byte sector sizes work correctly.
2042          */
2043         fsp->pcfs_fatstart = fsp->pcfs_dosstart + reserved;
2044         fsp->pcfs_rdirstart = fsp->pcfs_fatstart + fsp->pcfs_numfat * fatsec;
2045         fsp->pcfs_datastart = fsp->pcfs_rdirstart + rdirsec;
2046         datasec = totsec -
2047             (blkcnt_t)fatsec * fsp->pcfs_numfat -
2048             (blkcnt_t)rdirsec -
2049             (blkcnt_t)reserved;
2050 
2051         DTRACE_PROBE4(fatgeometry,
2052             blkcnt_t, totsec, size_t, fatsec,
2053             size_t, rdirsec, blkcnt_t, datasec);
2054 
2055         /*
2056          * 'totsec' is taken directly from the BPB and guaranteed to fit
2057          * into a 32bit unsigned integer. The calculation of 'datasec',
2058          * on the other hand, could underflow for incorrect values in
2059          * rdirsec/reserved/fatsec. Check for that.
2060          * We also check that the BPB conforms to the FAT specification's
2061          * requirement that either of the 16/32bit total sector counts
2062          * must be zero.
2063          */
2064         if (totsec != 0 &&
2065             (totsec16 == totsec32 || totsec16 == 0 || totsec32 == 0) &&
2066             datasec < totsec && datasec <= UINT32_MAX)
2067                 validflags |= BPB_TOTSEC_OK;
2068 
2069         if ((len_t)totsec * (len_t)secsize <= mediasize)
2070                 validflags |= BPB_MEDIASZ_OK;
2071 
2072         if (VALID_SECSIZE(secsize))
2073                 validflags |= BPB_SECSIZE_OK;
2074         if (VALID_SPCL(fsp->pcfs_spcl))
2075                 validflags |= BPB_SECPERCLUS_OK;
2076         if (VALID_CLSIZE(fsp->pcfs_clsize))
2077                 validflags |= BPB_CLSIZE_OK;
2078         if (VALID_NUMFATS(fsp->pcfs_numfat))
2079                 validflags |= BPB_NUMFAT_OK;
2080         if (VALID_RSVDSEC(reserved) && reserved < totsec)
2081                 validflags |= BPB_RSVDSECCNT_OK;
2082         if (VALID_MEDIA(fsp->pcfs_mediadesc))
2083                 validflags |= BPB_MEDIADESC_OK;
2084         if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
2085                 validflags |= BPB_BOOTSIG16_OK;
2086         if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
2087                 validflags |= BPB_BOOTSIG32_OK;
2088         if (VALID_FSTYPSTR16(bpb_FilSysType16(bpb)))
2089                 validflags |= BPB_FSTYPSTR16_OK;
2090         if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
2091                 validflags |= BPB_FSTYPSTR32_OK;
2092         if (VALID_OEMNAME(bpb_OEMName(bpb)))
2093                 validflags |= BPB_OEMNAME_OK;
2094         if (bkbootsec > 0 && bkbootsec <= reserved && fsisec != bkbootsec)
2095                 validflags |= BPB_BKBOOTSEC_OK;
2096         if (fsisec > 0 && fsisec <= reserved)
2097                 validflags |= BPB_FSISEC_OK;
2098         if (VALID_JMPBOOT(bpb_jmpBoot(bpb)))
2099                 validflags |= BPB_JMPBOOT_OK;
2100         if (VALID_FSVER32(bpb_get_FSVer32(bpb)))
2101                 validflags |= BPB_FSVER_OK;
2102         if (VALID_VOLLAB(bpb_VolLab16(bpb)))
2103                 validflags |= BPB_VOLLAB16_OK;
2104         if (VALID_VOLLAB(bpb_VolLab32(bpb)))
2105                 validflags |= BPB_VOLLAB32_OK;
2106         if (VALID_EXTFLAGS(bpb_get_ExtFlags32(bpb)))
2107                 validflags |= BPB_EXTFLAGS_OK;
2108 
2109         /*
2110          * Try to determine which FAT format to use.
2111          *
2112          * Calculate the number of clusters in order to determine
2113          * the type of FAT we are looking at.  This is the only
2114          * recommended way of determining FAT type, though there
2115          * are other hints in the data, this is the best way.
2116          *
2117          * Since we let just about "anything" pass through this function
2118          * without early exits, fence against divide-by-zero here.
2119          *
2120          * datasec was already validated against UINT32_MAX so we know
2121          * the result will not overflow the 32bit calculation.
2122          */
2123         if (fsp->pcfs_spcl)
2124                 ncl = (uint32_t)datasec / fsp->pcfs_spcl;
2125         else
2126                 ncl = 0;
2127 
2128         fsp->pcfs_ncluster = ncl;
2129 
2130         /*
2131          * From the Microsoft FAT specification:
2132          * In the following example, when it says <, it does not mean <=.
2133          * Note also that the numbers are correct.  The first number for
2134          * FAT12 is 4085; the second number for FAT16 is 65525. These numbers
2135          * and the '<' signs are not wrong.
2136          *
2137          * We "specialdetect" the corner cases, and use at least one "extra"
2138          * criterion to decide whether it's FAT16 or FAT32 if the cluster
2139          * count is dangerously close to the boundaries.
2140          */
2141 
2142         if (ncl <= PCF_FIRSTCLUSTER) {
2143                 type = FAT_UNKNOWN;
2144         } else if (ncl < 4085) {
2145                 type = FAT12;
2146         } else if (ncl <= 4096) {
2147                 type = FAT_QUESTIONABLE;
2148         } else if (ncl < 65525) {
2149                 type = FAT16;
2150         } else if (ncl <= 65536) {
2151                 type = FAT_QUESTIONABLE;
2152         } else if (ncl < PCF_LASTCLUSTER32) {
2153                 type = FAT32;
2154         } else {
2155                 type = FAT_UNKNOWN;
2156         }
2157 
2158         DTRACE_PROBE4(parseBPB__initial,
2159             struct pcfs *, fsp, unsigned char *, bpb,
2160             int, validflags, fattype_t, type);
2161 
2162 recheck:
2163         fsp->pcfs_fatsec = fatsec;
2164 
2165         /* Do some final sanity checks for each specific type of FAT */
2166         switch (type) {
2167                 case FAT12:
2168                         if (rec != 0)
2169                                 validflags |= BPB_ROOTENTCNT_OK;
2170                         if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
2171                             bpb_get_TotSec16(bpb) == 0)
2172                                 validflags |= BPB_TOTSEC16_OK;
2173                         if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
2174                             bpb_get_TotSec32(bpb) == 0)
2175                                 validflags |= BPB_TOTSEC32_OK;
2176                         if (bpb_get_FatSz16(bpb) == fatsec)
2177                                 validflags |= BPB_FATSZ16_OK;
2178                         if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER)
2179                             * 3 / 2)
2180                                 validflags |= BPB_FATSZ_OK;
2181                         if (ncl < 4085)
2182                                 validflags |= BPB_NCLUSTERS_OK;
2183 
2184                         fsp->pcfs_lastclmark = (PCF_LASTCLUSTER & 0xfff);
2185                         fsp->pcfs_rootblksize =
2186                             fsp->pcfs_rdirsec * secsize;
2187                         fsp->pcfs_fsistart = 0;
2188 
2189                         if ((validflags & FAT12_VALIDMSK) != FAT12_VALIDMSK)
2190                                 type = FAT_UNKNOWN;
2191                         break;
2192                 case FAT16:
2193                         if (rec != 0)
2194                                 validflags |= BPB_ROOTENTCNT_OK;
2195                         if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
2196                             bpb_get_TotSec16(bpb) == 0)
2197                                 validflags |= BPB_TOTSEC16_OK;
2198                         if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
2199                             bpb_get_TotSec32(bpb) == 0)
2200                                 validflags |= BPB_TOTSEC32_OK;
2201                         if (bpb_get_FatSz16(bpb) == fatsec)
2202                                 validflags |= BPB_FATSZ16_OK;
2203                         if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER) * 2)
2204                                 validflags |= BPB_FATSZ_OK;
2205                         if (ncl >= 4085 && ncl < 65525)
2206                                 validflags |= BPB_NCLUSTERS_OK;
2207 
2208                         fsp->pcfs_lastclmark = PCF_LASTCLUSTER;
2209                         fsp->pcfs_rootblksize =
2210                             fsp->pcfs_rdirsec * secsize;
2211                         fsp->pcfs_fsistart = 0;
2212 
2213                         if ((validflags & FAT16_VALIDMSK) != FAT16_VALIDMSK)
2214                                 type = FAT_UNKNOWN;
2215                         break;
2216                 case FAT32:
2217                         if (rec == 0)
2218                                 validflags |= BPB_ROOTENTCNT_OK;
2219                         if (bpb_get_TotSec16(bpb) == 0)
2220                                 validflags |= BPB_TOTSEC16_OK;
2221                         if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec)
2222                                 validflags |= BPB_TOTSEC32_OK;
2223                         if (bpb_get_FatSz16(bpb) == 0)
2224                                 validflags |= BPB_FATSZ16_OK;
2225                         if (bpb_get_FatSz32(bpb) == fatsec)
2226                                 validflags |= BPB_FATSZ32_OK;
2227                         if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER) * 4)
2228                                 validflags |= BPB_FATSZ_OK;
2229                         if (ncl >= 65525 && ncl < PCF_LASTCLUSTER32)
2230                                 validflags |= BPB_NCLUSTERS_OK;
2231 
2232                         fsp->pcfs_lastclmark = PCF_LASTCLUSTER32;
2233                         fsp->pcfs_rootblksize = fsp->pcfs_clsize;
2234                         fsp->pcfs_fsistart = fsp->pcfs_dosstart + fsisec;
2235                         if (validflags & BPB_FSISEC_OK)
2236                                 fsp->pcfs_flags |= PCFS_FSINFO_OK;
2237                         fsp->pcfs_rootclnum = bpb_get_RootClus32(bpb);
2238                         if (pc_validcl(fsp, fsp->pcfs_rootclnum))
2239                                 validflags |= BPB_ROOTCLUSTER_OK;
2240 
2241                         /*
2242                          * Current PCFS code only works if 'pcfs_rdirstart'
2243                          * contains the root cluster number on FAT32.
2244                          * That's a mis-use and would better be changed.
2245                          */
2246                         fsp->pcfs_rdirstart = (daddr_t)fsp->pcfs_rootclnum;
2247 
2248                         if ((validflags & FAT32_VALIDMSK) != FAT32_VALIDMSK)
2249                                 type = FAT_UNKNOWN;
2250                         break;
2251                 case FAT_QUESTIONABLE:
2252                         type = secondaryBPBChecks(fsp, bpb, secsize);
2253                         goto recheck;
2254                 default:
2255                         ASSERT(type == FAT_UNKNOWN);
2256                         break;
2257         }
2258 
2259         ASSERT(type != FAT_QUESTIONABLE);
2260 
2261         fsp->pcfs_fattype = type;
2262 
2263         if (valid)
2264                 *valid = validflags;
2265 
2266         DTRACE_PROBE4(parseBPB__final,
2267             struct pcfs *, fsp, unsigned char *, bpb,
2268             int, validflags, fattype_t, type);
2269 
2270         if (type != FAT_UNKNOWN) {
2271                 ASSERT((secsize & (DEV_BSIZE - 1)) == 0);
2272                 ASSERT(ISP2(secsize / DEV_BSIZE));
2273                 return (1);
2274         }
2275 
2276         return (0);
2277 }
2278 
2279 
2280 /*
2281  * Detect the device's native block size (sector size).
2282  *
2283  * Test whether the device is:
2284  *      - a floppy device from a known controller type via DKIOCINFO
2285  *      - a real floppy using the fd(7d) driver and capable of fdio(7I) ioctls
2286  *      - a USB floppy drive (identified by drive geometry)
2287  *
2288  * Detecting a floppy will make PCFS metadata updates on such media synchronous,
2289  * to minimize risks due to slow I/O and user hotplugging / device ejection.
2290  *
2291  * This might be a bit wasteful on kernel stack space; if anyone's
2292  * bothered by this, kmem_alloc/kmem_free the ioctl arguments...
2293  */
2294 static void
2295 pcfs_device_getinfo(struct pcfs *fsp)
2296 {
2297         dev_t                   rdev = fsp->pcfs_xdev;
2298         int                     error;
2299         union {
2300                 struct dk_minfo         mi;
2301                 struct dk_cinfo         ci;
2302                 struct dk_geom          gi;
2303                 struct fd_char          fc;
2304         } arg;                          /* save stackspace ... */
2305         intptr_t argp = (intptr_t)&arg;
2306         ldi_handle_t            lh;
2307         ldi_ident_t             li;
2308         int isfloppy, isremoveable, ishotpluggable;
2309         cred_t                  *cr = CRED();
2310 
2311         if (ldi_ident_from_dev(rdev, &li))
2312                 goto out;
2313 
2314         error = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, cr, &lh, li);
2315         ldi_ident_release(li);
2316         if (error)
2317                 goto out;
2318 
2319         /*
2320          * Not sure if this could possibly happen. It'd be a bit like
2321          * VOP_OPEN() changing the passed-in vnode ptr. We're just not
2322          * expecting it, needs some thought if triggered ...
2323          */
2324         ASSERT(fsp->pcfs_xdev == rdev);
2325 
2326         /*
2327          * Check for removeable/hotpluggable media.
2328          */
2329         if (ldi_ioctl(lh, DKIOCREMOVABLE,
2330             (intptr_t)&isremoveable, FKIOCTL, cr, NULL)) {
2331                 isremoveable = 0;
2332         }
2333         if (ldi_ioctl(lh, DKIOCHOTPLUGGABLE,
2334             (intptr_t)&ishotpluggable, FKIOCTL, cr, NULL)) {
2335                 ishotpluggable = 0;
2336         }
2337 
2338         /*
2339          * Make sure we don't use "half-initialized" values if the ioctls fail.
2340          */
2341         if (ldi_ioctl(lh, DKIOCGMEDIAINFO, argp, FKIOCTL, cr, NULL)) {
2342                 bzero(&arg, sizeof (arg));
2343                 fsp->pcfs_mediasize = 0;
2344         } else {
2345                 fsp->pcfs_mediasize =
2346                     (len_t)arg.mi.dki_lbsize *
2347                     (len_t)arg.mi.dki_capacity;
2348         }
2349 
2350         if (VALID_SECSIZE(arg.mi.dki_lbsize)) {
2351                 if (fsp->pcfs_secsize == 0) {
2352                         fsp->pcfs_secsize = arg.mi.dki_lbsize;
2353                         fsp->pcfs_sdshift =
2354                             ddi_ffs(arg.mi.dki_lbsize / DEV_BSIZE) - 1;
2355                 } else {
2356                         PC_DPRINTF4(1, "!pcfs: autodetected media block size "
2357                             "%d, device (%x.%x), different from user-provided "
2358                             "%d. User override - ignoring autodetect result.\n",
2359                             arg.mi.dki_lbsize,
2360                             getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
2361                             fsp->pcfs_secsize);
2362                 }
2363         } else if (arg.mi.dki_lbsize) {
2364                 PC_DPRINTF3(1, "!pcfs: autodetected media block size "
2365                     "%d, device (%x.%x), invalid (not 512, 1024, 2048, 4096). "
2366                     "Ignoring autodetect result.\n",
2367                     arg.mi.dki_lbsize,
2368                     getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev));
2369         }
2370 
2371         /*
2372          * We treat the following media types as a floppy by default.
2373          */
2374         isfloppy =
2375             (arg.mi.dki_media_type == DK_FLOPPY ||
2376             arg.mi.dki_media_type == DK_ZIP ||
2377             arg.mi.dki_media_type == DK_JAZ);
2378 
2379         /*
2380          * if this device understands fdio(7I) requests it's
2381          * obviously a floppy drive.
2382          */
2383         if (!isfloppy &&
2384             !ldi_ioctl(lh, FDIOGCHAR, argp, FKIOCTL, cr, NULL))
2385                 isfloppy = 1;
2386 
2387         /*
2388          * some devices we like to treat as floppies, but they don't
2389          * understand fdio(7I) requests.
2390          */
2391         if (!isfloppy &&
2392             !ldi_ioctl(lh, DKIOCINFO, argp, FKIOCTL, cr, NULL) &&
2393             (arg.ci.dki_ctype == DKC_WDC2880 ||
2394             arg.ci.dki_ctype == DKC_NCRFLOPPY ||
2395             arg.ci.dki_ctype == DKC_SMSFLOPPY ||
2396             arg.ci.dki_ctype == DKC_INTEL82077))
2397                 isfloppy = 1;
2398 
2399         /*
2400          * This is the "final fallback" test - media with
2401          * 2 heads and 80 cylinders are assumed to be floppies.
2402          * This is normally true for USB floppy drives ...
2403          */
2404         if (!isfloppy &&
2405             !ldi_ioctl(lh, DKIOCGGEOM, argp, FKIOCTL, cr, NULL) &&
2406             (arg.gi.dkg_ncyl == 80 && arg.gi.dkg_nhead == 2))
2407                 isfloppy = 1;
2408 
2409         /*
2410          * This is similar to the "old" PCFS code that sets this flag
2411          * just based on the media descriptor being 0xf8 (MD_FIXED).
2412          * Should be re-worked. We really need some specialcasing for
2413          * removeable media.
2414          */
2415         if (!isfloppy) {
2416                 fsp->pcfs_flags |= PCFS_NOCHK;
2417         }
2418 
2419         /*
2420          * We automatically disable access time updates if the medium is
2421          * removeable and/or hotpluggable, and the admin did not explicitly
2422          * request access time updates (via the "atime" mount option).
2423          * The majority of flash-based media should fit this category.
2424          * Minimizing write access extends the lifetime of your memory stick !
2425          */
2426         if (!vfs_optionisset(fsp->pcfs_vfs, MNTOPT_ATIME, NULL) &&
2427             (isremoveable || ishotpluggable | isfloppy)) {
2428                 fsp->pcfs_flags |= PCFS_NOATIME;
2429         }
2430 
2431         (void) ldi_close(lh, FREAD, cr);
2432 out:
2433         if (fsp->pcfs_secsize == 0) {
2434                 PC_DPRINTF3(1, "!pcfs: media block size autodetection "
2435                     "device (%x.%x) failed, no user-provided fallback. "
2436                     "Using %d bytes.\n",
2437                     getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
2438                     DEV_BSIZE);
2439                 fsp->pcfs_secsize = DEV_BSIZE;
2440                 fsp->pcfs_sdshift = 0;
2441         }
2442         ASSERT(fsp->pcfs_secsize % DEV_BSIZE == 0);
2443         ASSERT(VALID_SECSIZE(fsp->pcfs_secsize));
2444 }
2445 
2446 /*
2447  * Get the FAT type for the DOS medium.
2448  *
2449  * -------------------------
2450  * According to Microsoft:
2451  *   The FAT type one of FAT12, FAT16, or FAT32 is determined by the
2452  * count of clusters on the volume and nothing else.
2453  * -------------------------
2454  *
2455  */
2456 static int
2457 pc_getfattype(struct pcfs *fsp)
2458 {
2459         int error = 0;
2460         buf_t *bp = NULL;
2461         struct vnode *devvp = fsp->pcfs_devvp;
2462         dev_t   dev = devvp->v_rdev;
2463 
2464         /*
2465          * Detect the native block size of the medium, and attempt to
2466          * detect whether the medium is removeable.
2467          * We do treat removable media (floppies, USB and FireWire disks)
2468          * differently wrt. to the frequency and synchronicity of FAT updates.
2469          * We need to know the media block size in order to be able to
2470          * parse the partition table.
2471          */
2472         pcfs_device_getinfo(fsp);
2473 
2474         /*
2475          * Unpartitioned media (floppies and some removeable devices)
2476          * don't have a partition table, the FAT BPB is at disk block 0.
2477          * Start out by reading block 0.
2478          */
2479         fsp->pcfs_dosstart = 0;
2480         bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), fsp->pcfs_secsize);
2481 
2482         if (error = geterror(bp))
2483                 goto out;
2484 
2485         /*
2486          * If a logical drive number is requested, parse the partition table
2487          * and attempt to locate it. Otherwise, proceed immediately to the
2488          * BPB check. findTheDrive(), if successful, returns the disk block
2489          * number where the requested partition starts in "startsec".
2490          */
2491         if (fsp->pcfs_ldrive != 0) {
2492                 PC_DPRINTF3(5, "!pcfs: pc_getfattype: using FDISK table on "
2493                     "device (%x,%x):%d to find BPB\n",
2494                     getmajor(dev), getminor(dev), fsp->pcfs_ldrive);
2495 
2496                 if (error = findTheDrive(fsp, &bp))
2497                         goto out;
2498 
2499                 ASSERT(fsp->pcfs_dosstart != 0);
2500 
2501                 brelse(bp);
2502                 bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
2503                     fsp->pcfs_secsize);
2504                 if (error = geterror(bp))
2505                         goto out;
2506         }
2507 
2508         /*
2509          * Validate the BPB and fill in the instance structure.
2510          */
2511         if (!parseBPB(fsp, (uchar_t *)bp->b_un.b_addr, NULL)) {
2512                 PC_DPRINTF4(1, "!pcfs: pc_getfattype: No FAT BPB on "
2513                     "device (%x.%x):%d, disk LBA %u\n",
2514                     getmajor(dev), getminor(dev), fsp->pcfs_ldrive,
2515                     (uint_t)pc_dbdaddr(fsp, fsp->pcfs_dosstart));
2516                 error = EINVAL;
2517                 goto out;
2518         }
2519 
2520         ASSERT(fsp->pcfs_fattype != FAT_UNKNOWN);
2521 
2522 out:
2523         /*
2524          * Release the buffer used
2525          */
2526         if (bp != NULL)
2527                 brelse(bp);
2528         return (error);
2529 }
2530 
2531 
2532 /*
2533  * Get the file allocation table.
2534  * If there is an old FAT, invalidate it.
2535  */
2536 int
2537 pc_getfat(struct pcfs *fsp)
2538 {
2539         struct buf *bp = NULL;
2540         uchar_t *fatp = NULL;
2541         uchar_t *fat_changemap = NULL;
2542         int error;
2543         int fat_changemapsize;
2544         int flags = 0;
2545         int nfat;
2546         int altfat_mustmatch = 0;
2547         int fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
2548 
2549         if (fsp->pcfs_fatp) {
2550                 /*
2551                  * There is a FAT in core.
2552                  * If there are open file pcnodes or we have modified it or
2553                  * it hasn't timed out yet use the in core FAT.
2554                  * Otherwise invalidate it and get a new one
2555                  */
2556 #ifdef notdef
2557                 if (fsp->pcfs_frefs ||
2558                     (fsp->pcfs_flags & PCFS_FATMOD) ||
2559                     (gethrestime_sec() < fsp->pcfs_fattime)) {
2560                         return (0);
2561                 } else {
2562                         mutex_enter(&pcfslock);
2563                         pc_invalfat(fsp);
2564                         mutex_exit(&pcfslock);
2565                 }
2566 #endif /* notdef */
2567                 return (0);
2568         }
2569 
2570         /*
2571          * Get FAT and check it for validity
2572          */
2573         fatp = kmem_alloc(fatsize, KM_SLEEP);
2574         error = pc_readfat(fsp, fatp);
2575         if (error) {
2576                 flags = B_ERROR;
2577                 goto out;
2578         }
2579         fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1;
2580         fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP);
2581         fsp->pcfs_fatp = fatp;
2582         fsp->pcfs_fat_changemapsize = fat_changemapsize;
2583         fsp->pcfs_fat_changemap = fat_changemap;
2584 
2585         /*
2586          * The only definite signature check is that the
2587          * media descriptor byte should match the first byte
2588          * of the FAT block.
2589          */
2590         if (fatp[0] != fsp->pcfs_mediadesc) {
2591                 cmn_err(CE_NOTE, "!pcfs: FAT signature mismatch, "
2592                     "media descriptor %x, FAT[0] lowbyte %x\n",
2593                     (uint32_t)fsp->pcfs_mediadesc, (uint32_t)fatp[0]);
2594                 cmn_err(CE_NOTE, "!pcfs: Enforcing alternate FAT validation\n");
2595                 altfat_mustmatch = 1;
2596         }
2597 
2598         /*
2599          * Get alternate FATs and check for consistency
2600          * This is an inlined version of pc_readfat().
2601          * Since we're only comparing FAT and alternate FAT,
2602          * there's no reason to let pc_readfat() copy data out
2603          * of the buf. Instead, compare in-situ, one cluster
2604          * at a time.
2605          */
2606         for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) {
2607                 size_t startsec;
2608                 size_t off;
2609 
2610                 startsec = pc_dbdaddr(fsp,
2611                     fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec);
2612 
2613                 for (off = 0; off < fatsize; off += fsp->pcfs_clsize) {
2614                         daddr_t fatblk = startsec + pc_dbdaddr(fsp,
2615                             pc_cltodb(fsp, pc_lblkno(fsp, off)));
2616 
2617                         bp = bread(fsp->pcfs_xdev, fatblk,
2618                             MIN(fsp->pcfs_clsize, fatsize - off));
2619                         if (bp->b_flags & (B_ERROR | B_STALE)) {
2620                                 cmn_err(CE_NOTE,
2621                                     "!pcfs: alternate FAT #%d (start LBA %p)"
2622                                     " read error at offset %ld on device"
2623                                     " (%x.%x):%d",
2624                                     nfat, (void *)(uintptr_t)startsec, off,
2625                                     getmajor(fsp->pcfs_xdev),
2626                                     getminor(fsp->pcfs_xdev),
2627                                     fsp->pcfs_ldrive);
2628                                 flags = B_ERROR;
2629                                 error = EIO;
2630                                 goto out;
2631                         }
2632                         bp->b_flags |= B_STALE | B_AGE;
2633                         if (bcmp(bp->b_un.b_addr, fatp + off,
2634                             MIN(fsp->pcfs_clsize, fatsize - off))) {
2635                                 cmn_err(CE_NOTE,
2636                                     "!pcfs: alternate FAT #%d (start LBA %p)"
2637                                     " corrupted at offset %ld on device"
2638                                     " (%x.%x):%d",
2639                                     nfat, (void *)(uintptr_t)startsec, off,
2640                                     getmajor(fsp->pcfs_xdev),
2641                                     getminor(fsp->pcfs_xdev),
2642                                     fsp->pcfs_ldrive);
2643                                 if (altfat_mustmatch) {
2644                                         flags = B_ERROR;
2645                                         error = EIO;
2646                                         goto out;
2647                                 }
2648                         }
2649                         brelse(bp);
2650                         bp = NULL;      /* prevent double release */
2651                 }
2652         }
2653 
2654         fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
2655         fsp->pcfs_fatjustread = 1;
2656 
2657         /*
2658          * Retrieve FAT32 fsinfo sector.
2659          * A failure to read this is not fatal to accessing the volume.
2660          * It simply means operations that count or search free blocks
2661          * will have to do a full FAT walk, vs. a possibly quicker lookup
2662          * of the summary information.
2663          * Hence, we log a message but return success overall after this point.
2664          */
2665         if (IS_FAT32(fsp) && (fsp->pcfs_flags & PCFS_FSINFO_OK)) {
2666                 struct fat_od_fsi *fsinfo_disk;
2667 
2668                 bp = bread(fsp->pcfs_xdev,
2669                     pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
2670                 fsinfo_disk = (struct fat_od_fsi *)bp->b_un.b_addr;
2671                 if (bp->b_flags & (B_ERROR | B_STALE) ||
2672                     !FSISIG_OK(fsinfo_disk)) {
2673                         cmn_err(CE_NOTE,
2674                             "!pcfs: error reading fat32 fsinfo from "
2675                             "device (%x.%x):%d, block %lld",
2676                             getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
2677                             fsp->pcfs_ldrive,
2678                             (long long)pc_dbdaddr(fsp, fsp->pcfs_fsistart));
2679                         fsp->pcfs_flags &= ~PCFS_FSINFO_OK;
2680                         fsp->pcfs_fsinfo.fs_free_clusters = FSINFO_UNKNOWN;
2681                         fsp->pcfs_fsinfo.fs_next_free = FSINFO_UNKNOWN;
2682                 } else {
2683                         bp->b_flags |= B_STALE | B_AGE;
2684                         fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
2685                         fsp->pcfs_fsinfo.fs_free_clusters =
2686                             LE_32(fsinfo_disk->fsi_incore.fs_free_clusters);
2687                         fsp->pcfs_fsinfo.fs_next_free =
2688                             LE_32(fsinfo_disk->fsi_incore.fs_next_free);
2689                 }
2690                 brelse(bp);
2691                 bp = NULL;
2692         }
2693 
2694         if (pc_validcl(fsp, (pc_cluster32_t)fsp->pcfs_fsinfo.fs_next_free))
2695                 fsp->pcfs_nxfrecls = fsp->pcfs_fsinfo.fs_next_free;
2696         else
2697                 fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
2698 
2699         return (0);
2700 
2701 out:
2702         cmn_err(CE_NOTE, "!pcfs: illegal disk format");
2703         if (bp)
2704                 brelse(bp);
2705         if (fatp)
2706                 kmem_free(fatp, fatsize);
2707         if (fat_changemap)
2708                 kmem_free(fat_changemap, fat_changemapsize);
2709 
2710         if (flags) {
2711                 pc_mark_irrecov(fsp);
2712         }
2713         return (error);
2714 }