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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/kmem.h> 32 #include <sys/user.h> 33 #include <sys/proc.h> 34 #include <sys/cred.h> 35 #include <sys/disp.h> 36 #include <sys/buf.h> 37 #include <sys/vfs.h> 38 #include <sys/vnode.h> 39 #include <sys/fdio.h> 40 #include <sys/file.h> 41 #include <sys/uio.h> 42 #include <sys/conf.h> 43 #undef NFSCLIENT 44 #include <sys/statvfs.h> 45 #include <sys/mount.h> 46 #include <sys/pathname.h> 47 #include <sys/cmn_err.h> 48 #include <sys/debug.h> 49 #include <sys/sysmacros.h> 50 #include <sys/conf.h> 51 #include <sys/mkdev.h> 52 #include <sys/swap.h> 53 #include <sys/sunddi.h> 54 #include <sys/sunldi.h> 55 #include <sys/dktp/fdisk.h> 56 #include <sys/fs/pc_label.h> 57 #include <sys/fs/pc_fs.h> 58 #include <sys/fs/pc_dir.h> 59 #include <sys/fs/pc_node.h> 60 #include <fs/fs_subr.h> 61 #include <sys/modctl.h> 62 #include <sys/vol.h> 63 #include <sys/dkio.h> 64 #include <sys/open.h> 65 #include <sys/mntent.h> 66 #include <sys/policy.h> 67 68 /* 69 * The majority of PC media use a 512 sector size, but 70 * occasionally you will run across a 1k sector size. 71 * For media with a 1k sector size, fd_strategy() requires 72 * the I/O size to be a 1k multiple; so when the sector size 73 * is not yet known, always read 1k. 74 */ 75 #define PC_SAFESECSIZE (PC_SECSIZE * 2) 76 77 static int pcfs_pseudo_floppy(dev_t); 78 79 static int pcfsinit(int, char *); 80 static int pcfs_mount(struct vfs *, struct vnode *, struct mounta *, 81 struct cred *); 82 static int pcfs_unmount(struct vfs *, int, struct cred *); 83 static int pcfs_root(struct vfs *, struct vnode **); 84 static int pcfs_statvfs(struct vfs *, struct statvfs64 *); 85 static int pc_syncfsnodes(struct pcfs *); 86 static int pcfs_sync(struct vfs *, short, struct cred *); 87 static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp); 88 89 static int pc_getfattype(struct vnode *, int, daddr_t *, int *); 90 static int pc_readfat(struct pcfs *fsp, uchar_t *fatp, daddr_t start, 91 size_t fatsize); 92 static int pc_writefat(struct pcfs *fsp, daddr_t start); 93 94 /* 95 * pcfs mount options table 96 */ 97 98 static char *nohidden_cancel[] = {MNTOPT_PCFS_HIDDEN, NULL}; 99 static char *hidden_cancel[] = {MNTOPT_PCFS_NOHIDDEN, NULL}; 100 static char *nofoldcase_cancel[] = {MNTOPT_PCFS_FOLDCASE, NULL}; 101 static char *foldcase_cancel[] = {MNTOPT_PCFS_NOFOLDCASE, NULL}; 102 103 static mntopt_t mntopts[] = { 104 /* 105 * option name cancel option default arg flags 106 * opt data 107 */ 108 { MNTOPT_PCFS_NOHIDDEN, nohidden_cancel, NULL, 0, 109 NULL }, 110 { MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, MO_DEFAULT, 111 NULL }, 112 { MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, 113 NULL }, 114 { MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0, 115 NULL } 116 }; 117 118 static mntopts_t pcfs_mntopts = { 119 sizeof (mntopts) / sizeof (mntopt_t), 120 mntopts 121 }; 122 123 int pcfsdebuglevel = 0; 124 125 /* 126 * pcfslock: protects the list of mounted pc filesystems "pc_mounttab. 127 * pcfs_lock: (inside per filesystem structure "pcfs") 128 * per filesystem lock. Most of the vfsops and vnodeops are 129 * protected by this lock. 130 * pcnodes_lock: protects the pcnode hash table "pcdhead", "pcfhead". 131 * 132 * Lock hierarchy: pcfslock > pcfs_lock > pcnodes_lock 133 */ 134 kmutex_t pcfslock; 135 krwlock_t pcnodes_lock; /* protect the pcnode hash table "pcdhead", "pcfhead" */ 136 137 static int pcfstype; 138 139 static vfsdef_t vfw = { 140 VFSDEF_VERSION, 141 "pcfs", 142 pcfsinit, 143 VSW_HASPROTO|VSW_CANREMOUNT, 144 &pcfs_mntopts 145 }; 146 147 extern struct mod_ops mod_fsops; 148 149 static struct modlfs modlfs = { 150 &mod_fsops, 151 "PC filesystem v%I%", 152 &vfw 153 }; 154 155 static struct modlinkage modlinkage = { 156 MODREV_1, 157 &modlfs, 158 NULL 159 }; 160 161 int 162 _init(void) 163 { 164 int error; 165 166 #if !defined(lint) 167 /* make sure the on-disk structures are sane */ 168 ASSERT(sizeof (struct pcdir) == 32); 169 ASSERT(sizeof (struct pcdir_lfn) == 32); 170 #endif 171 mutex_init(&pcfslock, NULL, MUTEX_DEFAULT, NULL); 172 rw_init(&pcnodes_lock, NULL, RW_DEFAULT, NULL); 173 error = mod_install(&modlinkage); 174 if (error) { 175 mutex_destroy(&pcfslock); 176 rw_destroy(&pcnodes_lock); 177 } 178 return (error); 179 } 180 181 int 182 _fini(void) 183 { 184 int error; 185 186 error = mod_remove(&modlinkage); 187 if (error) 188 return (error); 189 mutex_destroy(&pcfslock); 190 rw_destroy(&pcnodes_lock); 191 /* 192 * Tear down the operations vectors 193 */ 194 (void) vfs_freevfsops_by_type(pcfstype); 195 vn_freevnodeops(pcfs_fvnodeops); 196 vn_freevnodeops(pcfs_dvnodeops); 197 return (0); 198 } 199 200 int 201 _info(struct modinfo *modinfop) 202 { 203 return (mod_info(&modlinkage, modinfop)); 204 } 205 206 /* ARGSUSED1 */ 207 static int 208 pcfsinit(int fstype, char *name) 209 { 210 static const fs_operation_def_t pcfs_vfsops_template[] = { 211 VFSNAME_MOUNT, pcfs_mount, 212 VFSNAME_UNMOUNT, pcfs_unmount, 213 VFSNAME_ROOT, pcfs_root, 214 VFSNAME_STATVFS, pcfs_statvfs, 215 VFSNAME_SYNC, (fs_generic_func_p) pcfs_sync, 216 VFSNAME_VGET, pcfs_vget, 217 NULL, NULL 218 }; 219 int error; 220 221 error = vfs_setfsops(fstype, pcfs_vfsops_template, NULL); 222 if (error != 0) { 223 cmn_err(CE_WARN, "pcfsinit: bad vfs ops template"); 224 return (error); 225 } 226 227 error = vn_make_ops("pcfs", pcfs_fvnodeops_template, &pcfs_fvnodeops); 228 if (error != 0) { 229 (void) vfs_freevfsops_by_type(fstype); 230 cmn_err(CE_WARN, "pcfsinit: bad file vnode ops template"); 231 return (error); 232 } 233 234 error = vn_make_ops("pcfsd", pcfs_dvnodeops_template, &pcfs_dvnodeops); 235 if (error != 0) { 236 (void) vfs_freevfsops_by_type(fstype); 237 vn_freevnodeops(pcfs_fvnodeops); 238 cmn_err(CE_WARN, "pcfsinit: bad dir vnode ops template"); 239 return (error); 240 } 241 242 pcfstype = fstype; 243 (void) pc_init(); 244 return (0); 245 } 246 247 static struct pcfs *pc_mounttab = NULL; 248 249 extern struct pcfs_args pc_tz; 250 251 /* 252 * Define some special logical drives we use internal to this file. 253 */ 254 #define BOOT_PARTITION_DRIVE 99 255 #define PRIMARY_DOS_DRIVE 1 256 257 /* 258 * pc_mount system call 259 */ 260 static int 261 pcfs_mount( 262 struct vfs *vfsp, 263 struct vnode *mvp, 264 struct mounta *uap, 265 struct cred *cr) 266 { 267 struct pcfs *fsp; 268 struct vnode *bvp; 269 struct vnode *devvp; 270 struct pathname special; 271 daddr_t dosstart; 272 dev_t pseudodev; 273 dev_t xdev; 274 char *spnp; 275 char *data = uap->dataptr; 276 int datalen = uap->datalen; 277 int dos_ldrive = 0; 278 int error; 279 int fattype; 280 int spnlen; 281 int wantbootpart = 0; 282 struct vioc_info info; 283 int rval; /* set but not used */ 284 minor_t minor; 285 int oflag, aflag; 286 287 if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0) 288 return (error); 289 290 PC_DPRINTF0(4, "pcfs_mount\n"); 291 if (mvp->v_type != VDIR) { 292 return (ENOTDIR); 293 } 294 mutex_enter(&mvp->v_lock); 295 if ((uap->flags & MS_REMOUNT) == 0 && 296 (uap->flags & MS_OVERLAY) == 0 && 297 (mvp->v_count != 1 || (mvp->v_flag & VROOT))) { 298 mutex_exit(&mvp->v_lock); 299 return (EBUSY); 300 } 301 mutex_exit(&mvp->v_lock); 302 303 /* 304 * The caller is responsible for making sure to always 305 * pass in sizeof(struct pcfs_args) (or the old one). 306 * Doing this is the only way to know an EINVAL return 307 * from mount(2) is due to the "not a DOS filesystem" 308 * EINVAL that pc_verify/pc_getfattype could return. 309 */ 310 if ((datalen != sizeof (struct pcfs_args)) && 311 (datalen != sizeof (struct old_pcfs_args))) { 312 return (EINVAL); 313 } else { 314 struct pcfs_args tmp_tz; 315 int hidden = 0; 316 int foldcase = 0; 317 318 tmp_tz.flags = 0; 319 if (copyin(data, &tmp_tz, datalen)) { 320 return (EFAULT); 321 } 322 if (datalen == sizeof (struct pcfs_args)) { 323 hidden = tmp_tz.flags & PCFS_MNT_HIDDEN; 324 foldcase = tmp_tz.flags & PCFS_MNT_FOLDCASE; 325 } 326 327 if (hidden) 328 vfs_setmntopt(vfsp, MNTOPT_PCFS_HIDDEN, NULL, 0); 329 if (foldcase) 330 vfs_setmntopt(vfsp, MNTOPT_PCFS_FOLDCASE, NULL, 0); 331 /* 332 * more than one pc filesystem can be mounted on x86 333 * so the pc_tz structure is now a critical region 334 */ 335 mutex_enter(&pcfslock); 336 if (pc_mounttab == NULL) 337 bcopy(&tmp_tz, &pc_tz, sizeof (struct pcfs_args)); 338 mutex_exit(&pcfslock); 339 } 340 /* 341 * Resolve path name of special file being mounted. 342 */ 343 if (error = pn_get(uap->spec, UIO_USERSPACE, &special)) { 344 return (error); 345 } 346 if (error = 347 lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &bvp)) { 348 /* 349 * look for suffix to special 350 * which indicates a request to mount the solaris boot 351 * partition, or a DOS logical drive on the hard disk 352 */ 353 spnlen = special.pn_pathlen; 354 355 if (spnlen > 5) { 356 spnp = special.pn_path + spnlen - 5; 357 if (*spnp++ == ':' && *spnp++ == 'b' && 358 *spnp++ == 'o' && *spnp++ == 'o' && 359 *spnp++ == 't') { 360 /* 361 * Looks as if they want to mount 362 * the Solaris boot partition 363 */ 364 wantbootpart = 1; 365 dos_ldrive = BOOT_PARTITION_DRIVE; 366 spnp = special.pn_path + spnlen - 5; 367 *spnp = '\0'; 368 error = lookupname(special.pn_path, 369 UIO_SYSSPACE, FOLLOW, NULLVPP, &bvp); 370 } 371 } 372 373 if (!wantbootpart) { 374 spnp = special.pn_path + spnlen - 1; 375 if (spnlen > 2 && *spnp >= 'c' && *spnp <= 'z') { 376 spnlen--; 377 dos_ldrive = *spnp-- - 'c' + 1; 378 } else if (spnlen > 2 && *spnp >= '0' && *spnp <= '9') { 379 spnlen--; 380 dos_ldrive = *spnp-- - '0'; 381 if (spnlen > 2 && *spnp >= '0' && 382 *spnp <= '9') { 383 spnlen--; 384 dos_ldrive += 10 * (*spnp-- - '0'); 385 } 386 } 387 if (spnlen > 1 && dos_ldrive && dos_ldrive <= 24 && 388 *spnp == ':') { 389 /* 390 * remove suffix so that we have a real 391 * device name 392 */ 393 *spnp = '\0'; 394 error = lookupname(special.pn_path, 395 UIO_SYSSPACE, FOLLOW, NULLVPP, &bvp); 396 } 397 } 398 if (error) { 399 pn_free(&special); 400 return (error); 401 } 402 } 403 pn_free(&special); 404 if (bvp->v_type != VBLK) { 405 VN_RELE(bvp); 406 return (ENOTBLK); 407 } 408 xdev = bvp->v_rdev; 409 /* 410 * Verify caller's permission to open the device special file. 411 */ 412 if ((vfsp->vfs_flag & VFS_RDONLY) != 0 || 413 ((uap->flags & MS_RDONLY) != 0)) { 414 oflag = FREAD; 415 aflag = VREAD; 416 } else { 417 oflag = FREAD | FWRITE; 418 aflag = VREAD | VWRITE; 419 } 420 if ((error = VOP_ACCESS(bvp, aflag, 0, cr)) != 0 || 421 (error = secpolicy_spec_open(cr, bvp, oflag)) != 0) { 422 VN_RELE(bvp); 423 return (error); 424 } 425 426 VN_RELE(bvp); 427 if (getmajor(xdev) >= devcnt) { 428 return (ENXIO); 429 } 430 /* 431 * Ensure that this device (or logical drive) isn't already mounted, 432 * unless this is a REMOUNT request 433 */ 434 if (dos_ldrive) { 435 mutex_enter(&pcfslock); 436 for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt) 437 if (fsp->pcfs_xdev == xdev && 438 fsp->pcfs_ldrv == dos_ldrive) { 439 mutex_exit(&pcfslock); 440 if (uap->flags & MS_REMOUNT) { 441 return (0); 442 } else { 443 return (EBUSY); 444 } 445 } 446 /* 447 * Assign a unique device number for the vfs 448 * The old way (getudev() + a constantly incrementing 449 * major number) was wrong because it changes vfs_dev 450 * across mounts and reboots, which breaks nfs file handles. 451 * UFS just uses the real dev_t. We can't do that because 452 * of the way pcfs opens fdisk partitons (the :c and :d 453 * partitions are on the same dev_t). Though that _might_ 454 * actually be ok, since the file handle contains an 455 * absolute block number, it's probably better to make them 456 * different. So I think we should retain the original 457 * dev_t, but come up with a different minor number based 458 * on the logical drive that will _always_ come up the same. 459 * For now, we steal the upper 6 bits. 460 */ 461 #ifdef notdef 462 /* what should we do here? */ 463 if (((getminor(xdev) >> 12) & 0x3F) != 0) 464 printf("whoops - upper bits used!\n"); 465 #endif 466 minor = ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32; 467 pseudodev = makedevice(getmajor(xdev), minor); 468 if (vfs_devmounting(pseudodev, vfsp)) { 469 mutex_exit(&pcfslock); 470 return (EBUSY); 471 } 472 if (vfs_devismounted(pseudodev)) { 473 mutex_exit(&pcfslock); 474 if (uap->flags & MS_REMOUNT) { 475 return (0); 476 } else { 477 return (EBUSY); 478 } 479 } 480 mutex_exit(&pcfslock); 481 } else { 482 if (vfs_devmounting(xdev, vfsp)) { 483 return (EBUSY); 484 } 485 if (vfs_devismounted(xdev)) 486 if (uap->flags & MS_REMOUNT) { 487 return (0); 488 } else { 489 return (EBUSY); 490 } 491 pseudodev = xdev; 492 } 493 494 if (uap->flags & MS_RDONLY) { 495 vfsp->vfs_flag |= VFS_RDONLY; 496 vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0); 497 } 498 499 /* 500 * Mount the filesystem 501 */ 502 devvp = makespecvp(xdev, VBLK); 503 if (IS_SWAPVP(devvp)) { 504 VN_RELE(devvp); 505 return (EBUSY); 506 } 507 508 /* 509 * special handling for PCMCIA memory card 510 * with pseudo floppies organization 511 */ 512 if (dos_ldrive == 0 && pcfs_pseudo_floppy(xdev)) { 513 dosstart = (daddr_t)0; 514 fattype = PCFS_PCMCIA_NO_CIS; 515 } else { 516 if (error = pc_getfattype(devvp, dos_ldrive, &dosstart, 517 &fattype)) { 518 VN_RELE(devvp); 519 return (error); 520 } 521 } 522 523 (void) VOP_PUTPAGE(devvp, (offset_t)0, (uint_t)0, B_INVAL, cr); 524 fsp = kmem_zalloc((uint_t)sizeof (struct pcfs), KM_SLEEP); 525 fsp->pcfs_vfs = vfsp; 526 fsp->pcfs_flags = fattype; 527 fsp->pcfs_devvp = devvp; 528 fsp->pcfs_xdev = xdev; 529 fsp->pcfs_ldrv = dos_ldrive; 530 fsp->pcfs_dosstart = dosstart; 531 mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL); 532 533 /* set the "nocheck" flag if volmgt is managing this volume */ 534 info.vii_pathlen = 0; 535 info.vii_devpath = 0; 536 error = cdev_ioctl(fsp->pcfs_xdev, VOLIOCINFO, (intptr_t)&info, 537 FKIOCTL|FREAD, kcred, &rval); 538 if (error == 0) { 539 fsp->pcfs_flags |= PCFS_NOCHK; 540 } 541 542 if (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL)) 543 fsp->pcfs_flags |= PCFS_HIDDEN; 544 if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL)) 545 fsp->pcfs_flags |= PCFS_FOLDCASE; 546 vfsp->vfs_dev = pseudodev; 547 vfsp->vfs_fstype = pcfstype; 548 vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype); 549 vfsp->vfs_data = (caddr_t)fsp; 550 vfsp->vfs_bcount = 0; 551 552 error = pc_verify(fsp); 553 if (error) { 554 VN_RELE(devvp); 555 mutex_destroy(&fsp->pcfs_lock); 556 kmem_free(fsp, (uint_t)sizeof (struct pcfs)); 557 return (error); 558 } 559 vfsp->vfs_bsize = fsp->pcfs_clsize; 560 561 mutex_enter(&pcfslock); 562 fsp->pcfs_nxt = pc_mounttab; 563 pc_mounttab = fsp; 564 mutex_exit(&pcfslock); 565 return (0); 566 } 567 568 /* 569 * vfs operations 570 */ 571 572 /* ARGSUSED */ 573 static int 574 pcfs_unmount( 575 struct vfs *vfsp, 576 int flag, 577 struct cred *cr) 578 { 579 struct pcfs *fsp, *fsp1; 580 581 if (secpolicy_fs_unmount(cr, vfsp) != 0) 582 return (EPERM); 583 584 /* 585 * forced unmount is not supported by this file system 586 * and thus, ENOTSUP, is being returned. 587 */ 588 if (flag & MS_FORCE) 589 return (ENOTSUP); 590 591 PC_DPRINTF0(4, "pcfs_unmount\n"); 592 fsp = VFSTOPCFS(vfsp); 593 /* 594 * We don't have to lock fsp because the VVFSLOCK in vfs layer will 595 * prevent lookuppn from crossing the mount point. 596 */ 597 if (fsp->pcfs_nrefs) { 598 return (EBUSY); 599 } 600 601 /* 602 * Allow an unmount (regardless of state) if the fs instance has 603 * been marked as beyond recovery. 604 */ 605 if (fsp->pcfs_flags & PCFS_IRRECOV) { 606 mutex_enter(&pcfslock); 607 rw_enter(&pcnodes_lock, RW_WRITER); 608 pc_diskchanged(fsp); 609 rw_exit(&pcnodes_lock); 610 mutex_exit(&pcfslock); 611 } 612 613 /* now there should be no pcp node on pcfhead or pcdhead. */ 614 615 mutex_enter(&pcfslock); 616 if (fsp == pc_mounttab) { 617 pc_mounttab = fsp->pcfs_nxt; 618 } else { 619 for (fsp1 = pc_mounttab; fsp1 != NULL; fsp1 = fsp1->pcfs_nxt) 620 if (fsp1->pcfs_nxt == fsp) 621 fsp1->pcfs_nxt = fsp->pcfs_nxt; 622 } 623 624 if (fsp->pcfs_fatp != (uchar_t *)0) { 625 pc_invalfat(fsp); 626 } 627 mutex_exit(&pcfslock); 628 629 VN_RELE(fsp->pcfs_devvp); 630 mutex_destroy(&fsp->pcfs_lock); 631 kmem_free(fsp, (uint_t)sizeof (struct pcfs)); 632 return (0); 633 } 634 635 /* 636 * find root of pcfs 637 */ 638 static int 639 pcfs_root( 640 struct vfs *vfsp, 641 struct vnode **vpp) 642 { 643 struct pcfs *fsp; 644 struct pcnode *pcp; 645 int error; 646 647 fsp = VFSTOPCFS(vfsp); 648 if (error = pc_lockfs(fsp, 0, 0)) 649 return (error); 650 pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0); 651 PC_DPRINTF2(9, "pcfs_root(0x%p) pcp= 0x%p\n", 652 (void *)vfsp, (void *)pcp); 653 pc_unlockfs(fsp); 654 *vpp = PCTOV(pcp); 655 pcp->pc_flags |= PC_EXTERNAL; 656 return (0); 657 } 658 659 /* 660 * Get file system statistics. 661 */ 662 static int 663 pcfs_statvfs( 664 struct vfs *vfsp, 665 struct statvfs64 *sp) 666 { 667 struct pcfs *fsp; 668 int error; 669 dev32_t d32; 670 671 fsp = VFSTOPCFS(vfsp); 672 error = pc_getfat(fsp); 673 if (error) 674 return (error); 675 bzero(sp, sizeof (*sp)); 676 sp->f_bsize = sp->f_frsize = fsp->pcfs_clsize; 677 sp->f_blocks = (fsblkcnt64_t)fsp->pcfs_ncluster; 678 sp->f_bavail = sp->f_bfree = (fsblkcnt64_t)pc_freeclusters(fsp); 679 sp->f_files = (fsfilcnt64_t)-1; 680 sp->f_ffree = (fsfilcnt64_t)-1; 681 sp->f_favail = (fsfilcnt64_t)-1; 682 #ifdef notdef 683 (void) cmpldev(&d32, fsp->pcfs_devvp->v_rdev); 684 #endif /* notdef */ 685 (void) cmpldev(&d32, vfsp->vfs_dev); 686 sp->f_fsid = d32; 687 (void) strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name); 688 sp->f_flag = vf_to_stf(vfsp->vfs_flag); 689 sp->f_namemax = PCFNAMESIZE; 690 return (0); 691 } 692 693 static int 694 pc_syncfsnodes(struct pcfs *fsp) 695 { 696 struct pchead *hp; 697 struct pcnode *pcp; 698 int error; 699 700 PC_DPRINTF0(7, "pcfs_syncfsnodes\n"); 701 if (error = pc_lockfs(fsp, 0, 0)) 702 return (error); 703 704 if (!(error = pc_syncfat(fsp))) { 705 hp = pcfhead; 706 while (hp < & pcfhead [ NPCHASH ]) { 707 rw_enter(&pcnodes_lock, RW_READER); 708 pcp = hp->pch_forw; 709 while (pcp != (struct pcnode *)hp) { 710 if (VFSTOPCFS(PCTOV(pcp) -> v_vfsp) == fsp) 711 if (error = pc_nodesync(pcp)) 712 break; 713 pcp = pcp -> pc_forw; 714 } 715 rw_exit(&pcnodes_lock); 716 if (error) 717 break; 718 hp++; 719 } 720 } 721 pc_unlockfs(fsp); 722 return (error); 723 } 724 725 /* 726 * Flush any pending I/O. 727 */ 728 /*ARGSUSED*/ 729 static int 730 pcfs_sync( 731 struct vfs *vfsp, 732 short flag, 733 struct cred *cr) 734 { 735 struct pcfs *fsp; 736 int error = 0; 737 738 /* this prevents the filesystem from being umounted. */ 739 mutex_enter(&pcfslock); 740 if (vfsp != NULL) { 741 fsp = VFSTOPCFS(vfsp); 742 if (!(fsp->pcfs_flags & PCFS_IRRECOV)) { 743 error = pc_syncfsnodes(fsp); 744 } else { 745 rw_enter(&pcnodes_lock, RW_WRITER); 746 pc_diskchanged(fsp); 747 rw_exit(&pcnodes_lock); 748 error = EIO; 749 } 750 } else { 751 fsp = pc_mounttab; 752 while (fsp != NULL) { 753 if (fsp->pcfs_flags & PCFS_IRRECOV) { 754 rw_enter(&pcnodes_lock, RW_WRITER); 755 pc_diskchanged(fsp); 756 rw_exit(&pcnodes_lock); 757 error = EIO; 758 break; 759 } 760 error = pc_syncfsnodes(fsp); 761 if (error) break; 762 fsp = fsp->pcfs_nxt; 763 } 764 } 765 mutex_exit(&pcfslock); 766 return (error); 767 } 768 769 int 770 pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing) 771 { 772 if ((fsp->pcfs_flags & PCFS_IRRECOV) && !releasing) 773 return (EIO); 774 775 if ((fsp->pcfs_flags & PCFS_LOCKED) && (fsp->pcfs_owner == curthread)) { 776 fsp->pcfs_count++; 777 } else { 778 mutex_enter(&fsp->pcfs_lock); 779 if (fsp->pcfs_flags & PCFS_LOCKED) 780 panic("pc_lockfs"); 781 /* 782 * We check the IRRECOV bit again just in case somebody 783 * snuck past the initial check but then got held up before 784 * they could grab the lock. (And in the meantime someone 785 * had grabbed the lock and set the bit) 786 */ 787 if (!diskchanged && !(fsp->pcfs_flags & PCFS_IRRECOV)) { 788 int err; 789 if ((err = pc_getfat(fsp))) 790 return (err); 791 } 792 fsp->pcfs_flags |= PCFS_LOCKED; 793 fsp->pcfs_owner = curthread; 794 fsp->pcfs_count++; 795 } 796 return (0); 797 } 798 799 void 800 pc_unlockfs(struct pcfs *fsp) 801 { 802 803 if ((fsp->pcfs_flags & PCFS_LOCKED) == 0) 804 panic("pc_unlockfs"); 805 if (--fsp->pcfs_count < 0) 806 panic("pc_unlockfs: count"); 807 if (fsp->pcfs_count == 0) { 808 fsp->pcfs_flags &= ~PCFS_LOCKED; 809 fsp->pcfs_owner = 0; 810 mutex_exit(&fsp->pcfs_lock); 811 } 812 } 813 814 /* 815 * isDosDrive() 816 * Boolean function. Give it the systid field for an fdisk partition 817 * and it decides if that's a systid that describes a DOS drive. We 818 * use systid values defined in sys/dktp/fdisk.h. 819 */ 820 static int 821 isDosDrive(uchar_t checkMe) 822 { 823 return ((checkMe == DOSOS12) || (checkMe == DOSOS16) || 824 (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) || 825 (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) || 826 (checkMe == DIAGPART)); 827 } 828 829 /* 830 * isDosExtended() 831 * Boolean function. Give it the systid field for an fdisk partition 832 * and it decides if that's a systid that describes an extended DOS 833 * partition. 834 */ 835 static int 836 isDosExtended(uchar_t checkMe) 837 { 838 return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA)); 839 } 840 841 /* 842 * isBootPart() 843 * Boolean function. Give it the systid field for an fdisk partition 844 * and it decides if that's a systid that describes a Solaris boot 845 * partition. 846 */ 847 static int 848 isBootPart(uchar_t checkMe) 849 { 850 return (checkMe == X86BOOT); 851 } 852 853 /* 854 * noLogicalDrive() 855 * Display error message about not being able to find a logical 856 * drive. 857 */ 858 static void 859 noLogicalDrive(int requested) 860 { 861 if (requested == BOOT_PARTITION_DRIVE) { 862 cmn_err(CE_NOTE, "!pcfs: no boot partition"); 863 } else { 864 cmn_err(CE_NOTE, "!pcfs: no such logical drive"); 865 } 866 } 867 868 /* 869 * findTheDrive() 870 * Discover offset of the requested logical drive, and return 871 * that offset (startSector), the systid of that drive (sysid), 872 * and a buffer pointer (bp), with the buffer contents being 873 * the first sector of the logical drive (i.e., the sector that 874 * contains the BPB for that drive). 875 */ 876 static int 877 findTheDrive(dev_t dev, int askedFor, int *error, buf_t **bp, 878 daddr_t *startSector, uchar_t *sysid) 879 { 880 struct ipart dosp[FD_NUMPART]; /* incore fdisk partition structure */ 881 struct mboot *dosp_ptr; /* boot structure pointer */ 882 daddr_t lastseek = 0; /* Disk block we sought previously */ 883 daddr_t diskblk = 0; /* Disk block to get */ 884 daddr_t xstartsect; /* base of Extended DOS partition */ 885 int logicalDriveCount = 0; /* Count of logical drives seen */ 886 int extendedPart = -1; /* index of extended dos partition */ 887 int primaryPart = -1; /* index of primary dos partition */ 888 int bootPart = -1; /* index of a Solaris boot partition */ 889 int xnumsect = -1; /* length of extended DOS partition */ 890 int driveIndex; /* computed FDISK table index */ 891 int i; 892 /* 893 * Count of drives in the current extended partition's 894 * FDISK table, and indexes of the drives themselves. 895 */ 896 int extndDrives[FD_NUMPART]; 897 int numDrives = 0; 898 899 /* 900 * Count of drives (beyond primary) in master boot record's 901 * FDISK table, and indexes of the drives themselves. 902 */ 903 int extraDrives[FD_NUMPART]; 904 int numExtraDrives = 0; 905 906 /* 907 * Copy from disk block into memory aligned structure for fdisk usage. 908 */ 909 dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr; 910 bcopy(dosp_ptr->parts, dosp, sizeof (struct ipart) * FD_NUMPART); 911 912 /* 913 * Get a summary of what is in the Master FDISK table. 914 * Normally we expect to find one partition marked as a DOS drive. 915 * This partition is the one Windows calls the primary dos partition. 916 * If the machine has any logical drives then we also expect 917 * to find a partition marked as an extended DOS partition. 918 * 919 * Sometimes we'll find multiple partitions marked as DOS drives. 920 * The Solaris fdisk program allows these partitions 921 * to be created, but Windows fdisk no longer does. We still need 922 * to support these, though, since Windows does. We also need to fix 923 * our fdisk to behave like the Windows version. 924 * 925 * It turns out that some off-the-shelf media have *only* an 926 * Extended partition, so we need to deal with that case as well. 927 * 928 * Only a single (the first) Extended or Boot Partition will 929 * be recognized. Any others will be ignored. 930 */ 931 for (i = 0; i < FD_NUMPART; i++) { 932 PC_DPRINTF1(2, "findTheDrive: found partition type %02x", 933 dosp[i].systid); 934 935 if (isDosDrive(dosp[i].systid)) { 936 if (primaryPart < 0) { 937 logicalDriveCount++; 938 primaryPart = i; 939 } else { 940 extraDrives[numExtraDrives++] = i; 941 } 942 continue; 943 } 944 if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) { 945 extendedPart = i; 946 continue; 947 } 948 if ((bootPart < 0) && isBootPart(dosp[i].systid)) { 949 bootPart = i; 950 continue; 951 } 952 } 953 954 if (askedFor == BOOT_PARTITION_DRIVE) { 955 if (bootPart < 0) { 956 noLogicalDrive(askedFor); 957 *error = EINVAL; 958 return (0); 959 } 960 *sysid = dosp[bootPart].systid; 961 *startSector = ltohi(dosp[bootPart].relsect); 962 return (1); 963 } 964 965 if (askedFor == PRIMARY_DOS_DRIVE && primaryPart >= 0) { 966 *sysid = dosp[primaryPart].systid; 967 *startSector = ltohi(dosp[primaryPart].relsect); 968 return (1); 969 } 970 971 /* 972 * We are not looking for the C: drive (or the primary drive 973 * was not found), so we had better have an extended partition 974 * or extra drives in the Master FDISK table. 975 */ 976 if ((extendedPart < 0) && (numExtraDrives == 0)) { 977 cmn_err(CE_NOTE, "!pcfs: no extended dos partition"); 978 noLogicalDrive(askedFor); 979 *error = EINVAL; 980 return (0); 981 } 982 983 if (extendedPart >= 0) { 984 diskblk = xstartsect = ltohi(dosp[extendedPart].relsect); 985 xnumsect = ltohi(dosp[extendedPart].numsect); 986 do { 987 /* 988 * If the seek would not cause us to change 989 * position on the drive, then we're out of 990 * extended partitions to examine. 991 */ 992 if (diskblk == lastseek) 993 break; 994 logicalDriveCount += numDrives; 995 /* 996 * Seek the next extended partition, and find 997 * logical drives within it. 998 */ 999 brelse(*bp); 1000 *bp = bread(dev, diskblk, PC_SAFESECSIZE); 1001 if ((*bp)->b_flags & B_ERROR) { 1002 PC_DPRINTF0(1, "pc_getfattype: read error\n"); 1003 *error = EIO; 1004 return (0); 1005 } 1006 lastseek = diskblk; 1007 dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr; 1008 if (ltohs(dosp_ptr->signature) != MBB_MAGIC) { 1009 cmn_err(CE_NOTE, "!pcfs: " 1010 "extended partition signature err"); 1011 *error = EINVAL; 1012 return (0); 1013 } 1014 bcopy(dosp_ptr->parts, dosp, 1015 sizeof (struct ipart) * FD_NUMPART); 1016 /* 1017 * Count up drives, and track where the next 1018 * extended partition is in case we need it. We 1019 * are expecting only one extended partition. If 1020 * there is more than one we'll only go to the 1021 * first one we see, but warn about ignoring. 1022 */ 1023 numDrives = 0; 1024 for (i = 0; i < FD_NUMPART; i++) { 1025 if (isDosDrive(dosp[i].systid)) { 1026 extndDrives[numDrives++] = i; 1027 continue; 1028 } else if (isDosExtended(dosp[i].systid)) { 1029 if (diskblk != lastseek) { 1030 /* 1031 * Already found an extended 1032 * partition in this table. 1033 */ 1034 cmn_err(CE_NOTE, 1035 "!pcfs: ignoring unexpected" 1036 " additional extended" 1037 " partition"); 1038 continue; 1039 } 1040 diskblk = xstartsect + 1041 ltohi(dosp[i].relsect); 1042 continue; 1043 } 1044 } 1045 } while (askedFor > logicalDriveCount + numDrives); 1046 1047 if (askedFor <= logicalDriveCount + numDrives) { 1048 /* 1049 * The number of logical drives we've found thus 1050 * far is enough to get us to the one we were 1051 * searching for. 1052 */ 1053 driveIndex = logicalDriveCount + numDrives - askedFor; 1054 *sysid = dosp[extndDrives[driveIndex]].systid; 1055 *startSector = 1056 ltohi(dosp[extndDrives[driveIndex]].relsect) + 1057 lastseek; 1058 if (*startSector > (xstartsect + xnumsect)) { 1059 cmn_err(CE_NOTE, "!pcfs: extended partition " 1060 "values bad"); 1061 *error = EINVAL; 1062 return (0); 1063 } 1064 return (1); 1065 } else { 1066 /* 1067 * We ran out of extended dos partition 1068 * drives. The only hope now is to go 1069 * back to extra drives defined in the master 1070 * fdisk table. But we overwrote that table 1071 * already, so we must load it in again. 1072 */ 1073 logicalDriveCount += numDrives; 1074 brelse(*bp); 1075 *bp = bread(dev, (daddr_t)0, PC_SAFESECSIZE); 1076 if ((*bp)->b_flags & B_ERROR) { 1077 PC_DPRINTF0(1, "pc_getfattype: read error\n"); 1078 *error = EIO; 1079 return (0); 1080 } 1081 dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr; 1082 bcopy(dosp_ptr->parts, dosp, 1083 sizeof (struct ipart) * FD_NUMPART); 1084 } 1085 } 1086 /* 1087 * Still haven't found the drive, is it an extra 1088 * drive defined in the main FDISK table? 1089 */ 1090 if (askedFor <= logicalDriveCount + numExtraDrives) { 1091 driveIndex = logicalDriveCount + numExtraDrives - askedFor; 1092 *sysid = dosp[extraDrives[driveIndex]].systid; 1093 *startSector = ltohi(dosp[extraDrives[driveIndex]].relsect); 1094 return (1); 1095 } 1096 /* 1097 * Still haven't found the drive, and there is 1098 * nowhere else to look. 1099 */ 1100 noLogicalDrive(askedFor); 1101 *error = EINVAL; 1102 return (0); 1103 } 1104 1105 /* 1106 * FAT12/FAT16 specific consistency checks. 1107 */ 1108 static int 1109 check_bpb_fat16(struct bootsec *bpb) 1110 { 1111 if (pcfsdebuglevel >= 5) { 1112 PC_DPRINTF1(5, "check_bpb_fat: RootEntCount = %d", 1113 ltohs(bpb->rdirents[0])); 1114 PC_DPRINTF1(5, "check_bpb_fat16: TotSec16 = %d", 1115 ltohs(bpb->numsect[0])); 1116 PC_DPRINTF1(5, "check_bpb_fat16: FATSz16 = %d", 1117 ltohs(bpb->fatsec)); 1118 PC_DPRINTF1(5, "check_bpb_fat16: TotSec32 = %d", 1119 ltohi(bpb->totalsec)); 1120 } 1121 return (ltohs(bpb->rdirents[0]) > 0 && /* RootEntCnt > 0 */ 1122 ((ltohs(bpb->numsect[0]) == 0 && /* TotSec16 == 0 */ 1123 ltohi(bpb->totalsec) > 0) || /* TotSec32 > 0 */ 1124 ltohs(bpb->numsect[0]) > 0) && /* TotSec16 > 0 */ 1125 ltohs(bpb->fatsec) > 0); /* FatSz16 > 0 */ 1126 } 1127 1128 /* 1129 * FAT32 specific consistency checks. 1130 */ 1131 static int 1132 check_bpb_fat32(struct fat32_bootsec *bpb) 1133 { 1134 if (pcfsdebuglevel >= 5) { 1135 PC_DPRINTF1(5, "check_bpb_fat32: RootEntCount = %d", 1136 ltohs(bpb->f_bs.rdirents[0])); 1137 PC_DPRINTF1(5, "check_bpb_fat32: TotSec16 = %d", 1138 ltohs(bpb->f_bs.numsect[0])); 1139 PC_DPRINTF1(5, "check_bpb_fat32: FATSz16 = %d", 1140 ltohs(bpb->f_bs.fatsec)); 1141 PC_DPRINTF1(5, "check_bpb_fat32: TotSec32 = %d", 1142 ltohi(bpb->f_bs.totalsec)); 1143 PC_DPRINTF1(5, "check_bpb_fat32: FATSz32 = %d", 1144 ltohi(bpb->f_fatlength)); 1145 } 1146 return (ltohs(bpb->f_bs.rdirents[0]) == 0 && 1147 ltohs(bpb->f_bs.numsect[0]) == 0 && 1148 ltohs(bpb->f_bs.fatsec) == 0 && 1149 ltohi(bpb->f_bs.totalsec) > 0 && 1150 ltohi(bpb->f_fatlength) > 0); 1151 } 1152 1153 /* 1154 * Calculate the number of clusters in order to determine 1155 * the type of FAT we are looking at. This is the only 1156 * recommended way of determining FAT type, though there 1157 * are other hints in the data, this is the best way. 1158 */ 1159 static ulong_t 1160 bpb_to_numclusters(uchar_t *cp) 1161 { 1162 struct fat32_bootsec *bpb; 1163 1164 ulong_t rootdirsectors; 1165 ulong_t FATsz; 1166 ulong_t TotSec; 1167 ulong_t DataSec; 1168 ulong_t CountOfClusters; 1169 char FileSysType[9]; 1170 1171 /* 1172 * Cast it to FAT32 bpb. If it turns out to be FAT12/16, its 1173 * OK, we won't try accessing the data beyond the FAT16 header 1174 * boundary. 1175 */ 1176 bpb = (struct fat32_bootsec *)cp; 1177 1178 if (pcfsdebuglevel >= 5) { 1179 if (ltohs(bpb->f_bs.rdirents[0]) != 0) { 1180 (void) memcpy(FileSysType, &cp[54], 8); 1181 FileSysType[8] = 0; 1182 PC_DPRINTF1(5, "debug_bpb: FAT12/FAT16 FileSysType = " 1183 "%s", FileSysType); 1184 } 1185 } 1186 1187 rootdirsectors = ((ltohs(bpb->f_bs.rdirents[0]) * 32) + 1188 (ltohs(bpb->f_bs.bps[0]) - 1)) / ltohs(bpb->f_bs.bps[0]); 1189 1190 if (ltohs(bpb->f_bs.fatsec) != 0) 1191 FATsz = ltohs(bpb->f_bs.fatsec); 1192 else 1193 FATsz = ltohi(bpb->f_fatlength); 1194 1195 if (ltohs(bpb->f_bs.numsect[0]) != 0) 1196 TotSec = ltohs(bpb->f_bs.numsect[0]); 1197 else 1198 TotSec = ltohi(bpb->f_bs.totalsec); 1199 1200 DataSec = TotSec - (ltohs(bpb->f_bs.res_sec[0]) + 1201 (bpb->f_bs.nfat * FATsz) + rootdirsectors); 1202 1203 CountOfClusters = DataSec / bpb->f_bs.spcl; 1204 1205 PC_DPRINTF1(5, "debug_bpb: CountOfClusters = %ld", CountOfClusters); 1206 1207 return (CountOfClusters); 1208 1209 } 1210 1211 static int 1212 fattype(ulong_t CountOfClusters) 1213 { 1214 /* 1215 * From Microsoft: 1216 * In the following example, when it says <, it does not mean <=. 1217 * Note also that the numbers are correct. The first number for 1218 * FAT12 is 4085; the second number for FAT16 is 65525. These numbers 1219 * and the '<' signs are not wrong. 1220 */ 1221 1222 /* Watch for edge cases */ 1223 if ((CountOfClusters >= 4085 && CountOfClusters <= 4095) || 1224 (CountOfClusters >= 65525 && CountOfClusters <= 65535)) { 1225 PC_DPRINTF1(5, "debug_bpb: Cannot determine FAT yet - %ld", 1226 CountOfClusters); 1227 return (-1); /* Cannot be determined yet */ 1228 } else if (CountOfClusters < 4085) { 1229 /* Volume is FAT12 */ 1230 PC_DPRINTF0(5, "debug_bpb: This must be FAT12"); 1231 return (0); 1232 } else if (CountOfClusters < 65525) { 1233 /* Volume is FAT16 */ 1234 PC_DPRINTF0(5, "debug_bpb: This must be FAT16"); 1235 return (PCFS_FAT16); 1236 } else { 1237 /* Volume is FAT32 */ 1238 PC_DPRINTF0(5, "debug_bpb: This must be FAT32"); 1239 return (PCFS_FAT32); 1240 } 1241 } 1242 1243 #define VALID_SECSIZE(s) (s == 512 || s == 1024 || s == 2048 || s == 4096) 1244 1245 #define VALID_SPCL(s) (s == 1 || s == 2 || s == 4 || s == 8 || s == 16 ||\ 1246 s == 32 || s == 64 || s == 128) 1247 1248 static int 1249 secondaryBPBChecks(uchar_t *cp) 1250 { 1251 struct bootsec *bpb = (struct bootsec *)cp; 1252 struct fat32_bootsec *f32bpb = (struct fat32_bootsec *)cp; 1253 1254 /* 1255 * Perform secondary checks to try and determine what sort 1256 * of FAT partition we have based on other, less reliable, 1257 * data in the BPB header. 1258 */ 1259 if (ltohs(bpb->fatsec) != 0) { 1260 /* 1261 * Must be FAT12 or FAT16, check the 1262 * FilSysType string (not 100% reliable). 1263 */ 1264 if (!memcmp((cp + PCFS_TYPESTRING_OFFSET16), "FAT12", 5)) { 1265 PC_DPRINTF0(5, "secondaryBPBCheck says: FAT12"); 1266 return (0); /* FAT12 */ 1267 } else if (!memcmp((cp + PCFS_TYPESTRING_OFFSET16), "FAT16", 1268 5)) { 1269 PC_DPRINTF0(5, "secondaryBPBCheck says: FAT16"); 1270 return (PCFS_FAT16); 1271 } else { 1272 /* 1273 * Try to use the BPB_Media byte 1274 * 1275 * If the media byte indicates a floppy we'll 1276 * assume FAT12, otherwise we'll assume FAT16. 1277 */ 1278 switch (bpb->mediadesriptor) { 1279 case SS8SPT: 1280 case DS8SPT: 1281 case SS9SPT: 1282 case DS9SPT: 1283 case DS18SPT: 1284 case DS9_15SPT: 1285 PC_DPRINTF0(5, 1286 "secondaryBPBCheck says: FAT12"); 1287 return (0); /* FAT12 */ 1288 case MD_FIXED: 1289 PC_DPRINTF0(5, 1290 "secondaryBPBCheck says: FAT16"); 1291 return (PCFS_FAT16); 1292 default: 1293 cmn_err(CE_NOTE, 1294 "!pcfs: unknown FAT type"); 1295 return (-1); 1296 } 1297 } 1298 } else if (ltohi(f32bpb->f_fatlength) > 0) { 1299 PC_DPRINTF0(5, "secondaryBPBCheck says: FAT32"); 1300 return (PCFS_FAT32); 1301 } else { 1302 /* We don't know */ 1303 PC_DPRINTF0(5, "secondaryBPBCheck says: unknown!!"); 1304 return (-1); 1305 } 1306 } 1307 1308 /* 1309 * Check to see if the BPB we found is correct. 1310 * 1311 * First, look for obvious, tell-tale signs of trouble: 1312 * The NumFATs value should always be 2. Sometimes it can be a '1' 1313 * on FLASH memory cards and other non-disk-based media, so we 1314 * will allow that as well. 1315 * 1316 * We also look at the Media byte, the valid range is 0xF0, or 1317 * 0xF8 thru 0xFF, anything else means this is probably not a good 1318 * BPB. 1319 * 1320 * Finally, check the BPB Magic number at the end of the 512 byte 1321 * block, it must be 0xAA55. 1322 * 1323 * If that all is good, calculate the number of clusters and 1324 * do some final verification steps. 1325 * 1326 * If all is well, return success (1) and set the fattypep 1327 * value to the correct FAT value. 1328 */ 1329 static int 1330 isBPB(uchar_t *cp, int *fattypep) 1331 { 1332 struct bootsec *bpb = (struct bootsec *)cp; 1333 1334 uint_t numclusters; /* number of clusters in file area */ 1335 ushort_t secsize = (int)ltohs(bpb->bps[0]); 1336 1337 if (pcfsdebuglevel >= 3) { 1338 if (!VALID_SECSIZE(secsize)) 1339 PC_DPRINTF1(3, "check_bpb: invalid bps value %d", 1340 secsize); 1341 1342 if (!VALID_SPCL(bpb->spcl)) 1343 PC_DPRINTF1(3, "check_bpb: invalid spcl value %d", 1344 bpb->spcl); 1345 1346 if ((secsize * bpb->spcl) >= (32 * 1024)) 1347 PC_DPRINTF3(3, "check_bpb: BPC > 32K %d x %d = %d", 1348 secsize, 1349 bpb->spcl, 1350 secsize * bpb->spcl); 1351 1352 if (bpb->nfat == 0) 1353 PC_DPRINTF1(3, "check_bpb: bad NumFATs value %d", 1354 bpb->nfat); 1355 1356 if (ltohs(bpb->res_sec[0]) == 0) 1357 PC_DPRINTF1(3, "check_bpb: bad RsvdSecCnt value %d", 1358 ltohs(bpb->res_sec[0])); 1359 1360 PC_DPRINTF1(5, "check_bpb: Media byte = %02x", 1361 bpb->mediadesriptor); 1362 1363 } 1364 if ((bpb->nfat == 0) || 1365 (bpb->mediadesriptor != 0xF0 && bpb->mediadesriptor < 0xF8) || 1366 (ltohs(cp[510]) != MBB_MAGIC) || 1367 !VALID_SECSIZE(secsize) || 1368 !VALID_SPCL(bpb->spcl) || 1369 (secsize * bpb->spcl >= (64 * 1024)) || 1370 !(ltohs(bpb->res_sec[0]))) 1371 return (0); 1372 1373 /* 1374 * Basic sanity checks passed so far, now try to determine which 1375 * FAT format to use. 1376 */ 1377 numclusters = bpb_to_numclusters(cp); 1378 1379 *fattypep = fattype(numclusters); 1380 1381 /* Do some final sanity checks for each specific type of FAT */ 1382 switch (*fattypep) { 1383 case 0: /* FAT12 */ 1384 case PCFS_FAT16: 1385 if (!check_bpb_fat16((struct bootsec *)cp)) 1386 return (0); 1387 break; 1388 case PCFS_FAT32: 1389 if (!check_bpb_fat32((struct fat32_bootsec *)cp)) 1390 return (0); 1391 break; 1392 default: /* not sure yet */ 1393 *fattypep = secondaryBPBChecks(cp); 1394 if (*fattypep == -1) { 1395 /* Still nothing, give it up. */ 1396 return (0); 1397 } 1398 break; 1399 } 1400 PC_DPRINTF0(5, "isBPB: BPB passes verification tests"); 1401 return (1); 1402 } 1403 1404 1405 /* 1406 * Get the FAT type for the DOS medium. 1407 * 1408 * ------------------------- 1409 * According to Microsoft: 1410 * The FAT type one of FAT12, FAT16, or FAT32 is determined by the 1411 * count of clusters on the volume and nothing else. 1412 * ------------------------- 1413 * 1414 */ 1415 static int 1416 pc_getfattype( 1417 struct vnode *devvp, 1418 int ldrive, 1419 daddr_t *strtsectp, 1420 int *fattypep) 1421 { 1422 uchar_t *cp; /* for searching out FAT string */ 1423 buf_t *bp = NULL; /* Disk buffer pointer */ 1424 int rval = 0; 1425 uchar_t sysid = 0; /* System ID character */ 1426 dev_t dev = devvp->v_rdev; 1427 1428 *strtsectp = (daddr_t)0; 1429 1430 /* 1431 * Open the device so we can check out the BPB or FDISK table, 1432 * then read in the sector. 1433 */ 1434 PC_DPRINTF2(5, "pc_getfattype: dev=%x ldrive=%x ", (int)dev, ldrive); 1435 if (rval = VOP_OPEN(&devvp, FREAD, CRED())) { 1436 PC_DPRINTF1(1, "pc_getfattype: open error=%d\n", rval); 1437 return (rval); 1438 } 1439 1440 /* 1441 * Read block 0 from device 1442 */ 1443 bp = bread(dev, (daddr_t)0, PC_SAFESECSIZE); 1444 if (bp->b_flags & B_ERROR) { 1445 PC_DPRINTF0(1, "pc_getfattype: read error\n"); 1446 rval = EIO; 1447 goto out; 1448 } 1449 1450 cp = (uchar_t *)bp->b_un.b_addr; 1451 1452 /* 1453 * If the first block is not a valid BPB, look for the 1454 * through the FDISK table. 1455 */ 1456 if (!isBPB(cp, fattypep)) { 1457 /* find the partition table and get 512 bytes from it. */ 1458 PC_DPRINTF0(5, "pc_getfattype: using FDISK table to find BPB"); 1459 1460 if (findTheDrive(dev, ldrive, &rval, &bp, 1461 strtsectp, &sysid) == 0) 1462 goto out; 1463 1464 brelse(bp); 1465 bp = bread(dev, *strtsectp, PC_SAFESECSIZE); 1466 if (bp->b_flags & B_ERROR) { 1467 PC_DPRINTF0(1, "pc_getfattype: read error\n"); 1468 rval = EIO; 1469 goto out; 1470 } 1471 cp = (uchar_t *)bp->b_un.b_addr; 1472 1473 /* If this one is still no good, give it up. */ 1474 if (!isBPB(cp, fattypep)) { 1475 rval = EIO; 1476 goto out; 1477 } 1478 } 1479 1480 out: 1481 /* 1482 * Release the buffer used 1483 */ 1484 if (bp != NULL) 1485 brelse(bp); 1486 (void) VOP_CLOSE(devvp, FREAD, 1, (offset_t)0, CRED()); 1487 return (rval); 1488 } 1489 1490 1491 /* 1492 * Get the boot parameter block and file allocation table. 1493 * If there is an old FAT, invalidate it. 1494 */ 1495 int 1496 pc_getfat(struct pcfs *fsp) 1497 { 1498 struct vfs *vfsp = PCFSTOVFS(fsp); 1499 struct buf *tp = 0; 1500 struct buf *bp = 0; 1501 uchar_t *fatp = NULL; 1502 uchar_t *fat_changemap = NULL; 1503 struct bootsec *bootp; 1504 struct fat32_bootsec *f32b; 1505 struct vnode *devvp; 1506 int error; 1507 int fatsize; 1508 int fat_changemapsize; 1509 int flags = 0; 1510 int nfat; 1511 int secsize; 1512 int fatsec; 1513 1514 PC_DPRINTF0(5, "pc_getfat\n"); 1515 devvp = fsp->pcfs_devvp; 1516 if (fsp->pcfs_fatp) { 1517 /* 1518 * There is a FAT in core. 1519 * If there are open file pcnodes or we have modified it or 1520 * it hasn't timed out yet use the in core FAT. 1521 * Otherwise invalidate it and get a new one 1522 */ 1523 #ifdef notdef 1524 if (fsp->pcfs_frefs || 1525 (fsp->pcfs_flags & PCFS_FATMOD) || 1526 (gethrestime_sec() < fsp->pcfs_fattime)) { 1527 return (0); 1528 } else { 1529 mutex_enter(&pcfslock); 1530 pc_invalfat(fsp); 1531 mutex_exit(&pcfslock); 1532 } 1533 #endif /* notdef */ 1534 return (0); 1535 } 1536 /* 1537 * Open block device mounted on. 1538 */ 1539 error = VOP_OPEN(&devvp, 1540 (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, 1541 CRED()); 1542 if (error) { 1543 PC_DPRINTF1(1, "pc_getfat: open error=%d\n", error); 1544 return (error); 1545 } 1546 /* 1547 * Get boot parameter block and check it for validity 1548 */ 1549 tp = bread(fsp->pcfs_xdev, fsp->pcfs_dosstart, PC_SAFESECSIZE); 1550 if (tp->b_flags & (B_ERROR | B_STALE)) { 1551 PC_DPRINTF0(1, "pc_getfat: boot block error\n"); 1552 flags = tp->b_flags & B_ERROR; 1553 error = EIO; 1554 goto out; 1555 } 1556 tp->b_flags |= B_STALE | B_AGE; 1557 bootp = (struct bootsec *)tp->b_un.b_addr; 1558 1559 1560 /* get the sector size - may be more than 512 bytes */ 1561 secsize = (int)ltohs(bootp->bps[0]); 1562 /* check for bogus sector size - fat should be at least 1 sector */ 1563 if (IS_FAT32(fsp)) { 1564 f32b = (struct fat32_bootsec *)bootp; 1565 fatsec = ltohi(f32b->f_fatlength); 1566 } else { 1567 fatsec = ltohs(bootp->fatsec); 1568 } 1569 if (secsize < 512 || fatsec < 1 || bootp->nfat < 1) { 1570 cmn_err(CE_NOTE, "!pcfs: FAT size error"); 1571 error = EINVAL; 1572 goto out; 1573 } 1574 1575 switch (bootp->mediadesriptor) { 1576 default: 1577 cmn_err(CE_NOTE, "!pcfs: media-descriptor error, 0x%x", 1578 bootp->mediadesriptor); 1579 error = EINVAL; 1580 goto out; 1581 1582 case MD_FIXED: 1583 /* 1584 * PCMCIA pseudo floppy is type MD_FIXED, 1585 * but is accessed like a floppy 1586 */ 1587 if (!(fsp->pcfs_flags & PCFS_PCMCIA_NO_CIS)) { 1588 fsp->pcfs_flags |= PCFS_NOCHK; 1589 } 1590 /* FALLTHRU */ 1591 case SS8SPT: 1592 case DS8SPT: 1593 case SS9SPT: 1594 case DS9SPT: 1595 case DS18SPT: 1596 case DS9_15SPT: 1597 fsp->pcfs_secsize = secsize; 1598 fsp->pcfs_sdshift = secsize / DEV_BSIZE - 1; 1599 fsp->pcfs_entps = secsize / sizeof (struct pcdir); 1600 fsp->pcfs_spcl = (int)bootp->spcl; 1601 fsp->pcfs_fatsec = fatsec; 1602 fsp->pcfs_spt = (int)ltohs(bootp->spt); 1603 fsp->pcfs_rdirsec = (int)ltohs(bootp->rdirents[0]) 1604 * sizeof (struct pcdir) / secsize; 1605 fsp->pcfs_clsize = fsp->pcfs_spcl * secsize; 1606 fsp->pcfs_fatstart = fsp->pcfs_dosstart + 1607 (daddr_t)ltohs(bootp->res_sec[0]); 1608 fsp->pcfs_rdirstart = fsp->pcfs_fatstart + 1609 (bootp->nfat * fsp->pcfs_fatsec); 1610 fsp->pcfs_datastart = fsp->pcfs_rdirstart + fsp->pcfs_rdirsec; 1611 if (IS_FAT32(fsp)) 1612 fsp->pcfs_rdirstart = ltohi(f32b->f_rootcluster); 1613 fsp->pcfs_ncluster = (((int)(ltohs(bootp->numsect[0]) ? 1614 ltohs(bootp->numsect[0]) : ltohi(bootp->totalsec))) - 1615 fsp->pcfs_datastart + fsp->pcfs_dosstart) / fsp->pcfs_spcl; 1616 fsp->pcfs_numfat = (int)bootp->nfat; 1617 fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER; 1618 break; 1619 } 1620 1621 /* 1622 * Get FAT and check it for validity 1623 */ 1624 fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize; 1625 fatp = kmem_alloc(fatsize, KM_SLEEP); 1626 error = pc_readfat(fsp, fatp, fsp->pcfs_fatstart, fatsize); 1627 if (error) { 1628 flags = B_ERROR; 1629 goto out; 1630 } 1631 fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1; 1632 fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP); 1633 1634 /* 1635 * The only definite signature check is that the 1636 * media descriptor byte should match the first byte 1637 * of the FAT block. 1638 */ 1639 if (fatp[0] != bootp->mediadesriptor) { 1640 cmn_err(CE_NOTE, "!pcfs: FAT signature error"); 1641 error = EINVAL; 1642 goto out; 1643 } 1644 /* 1645 * Checking for fatsec and number of supported clusters, should 1646 * actually determine a FAT12/FAT media. 1647 */ 1648 if (fsp->pcfs_flags & PCFS_FAT16) { 1649 if ((fsp->pcfs_fatsec <= 12) && 1650 ((fatsize * 2 / 3) >= fsp->pcfs_ncluster)) { 1651 /* 1652 * We have a 12-bit FAT, rather than a 16-bit FAT. 1653 * Ignore what the fdisk table says. 1654 */ 1655 PC_DPRINTF0(2, "pc_getfattype: forcing 12-bit FAT\n"); 1656 fsp->pcfs_flags &= ~PCFS_FAT16; 1657 } 1658 } 1659 /* 1660 * Sanity check our FAT is large enough for the 1661 * clusters we think we have. 1662 */ 1663 if ((fsp->pcfs_flags & PCFS_FAT16) && 1664 ((fatsize / 2) < fsp->pcfs_ncluster)) { 1665 cmn_err(CE_NOTE, "!pcfs: FAT too small for number of clusters"); 1666 error = EINVAL; 1667 goto out; 1668 } 1669 1670 /* 1671 * Get alternate FATs and check for consistency 1672 * This is an inlined version of pc_readfat(). 1673 * Since we're only comparing FAT and alternate FAT, 1674 * there's no reason to let pc_readfat() copy data out 1675 * of the buf. Instead, compare in-situ, one cluster 1676 * at a time. 1677 */ 1678 for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) { 1679 size_t startsec; 1680 size_t off; 1681 1682 startsec = fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec; 1683 1684 for (off = 0; off < fatsize; off += fsp->pcfs_clsize) { 1685 bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, 1686 startsec + 1687 pc_cltodb(fsp, pc_lblkno(fsp, off))), 1688 MIN(fsp->pcfs_clsize, fatsize - off)); 1689 if (bp->b_flags & (B_ERROR | B_STALE)) { 1690 cmn_err(CE_NOTE, 1691 "!pcfs: alternate FAT #%d read error" 1692 " at byte %ld", nfat, off); 1693 flags = B_ERROR; 1694 error = EIO; 1695 goto out; 1696 } 1697 bp->b_flags |= B_STALE | B_AGE; 1698 if (bcmp(bp->b_un.b_addr, 1699 fatp + off, 1700 MIN(fsp->pcfs_clsize, fatsize - off))) { 1701 cmn_err(CE_NOTE, 1702 "!pcfs: alternate FAT #%d corrupted" 1703 " at byte %ld", nfat, off); 1704 flags = B_ERROR; 1705 } 1706 brelse(bp); 1707 bp = NULL; /* prevent double release */ 1708 } 1709 } 1710 1711 fsp->pcfs_fatsize = fatsize; 1712 fsp->pcfs_fatp = fatp; 1713 fsp->pcfs_fat_changemapsize = fat_changemapsize; 1714 fsp->pcfs_fat_changemap = fat_changemap; 1715 fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT; 1716 fsp->pcfs_fatjustread = 1; 1717 1718 brelse(tp); 1719 tp = NULL; 1720 if (IS_FAT32(fsp)) { 1721 /* get fsinfo */ 1722 struct fat32_boot_fsinfo fsinfo_disk; 1723 1724 fsp->f32fsinfo_sector = ltohs(f32b->f_infosector); 1725 tp = bread(fsp->pcfs_xdev, 1726 fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector), 1727 PC_SAFESECSIZE); 1728 if (tp->b_flags & (B_ERROR | B_STALE)) { 1729 cmn_err(CE_NOTE, "!pcfs: error reading fat32 fsinfo"); 1730 flags = tp->b_flags & B_ERROR; 1731 brelse(tp); 1732 tp = NULL; 1733 error = EIO; 1734 goto out; 1735 } 1736 tp->b_flags |= B_STALE | B_AGE; 1737 bcopy((void *)(tp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF), 1738 &fsinfo_disk, sizeof (struct fat32_boot_fsinfo)); 1739 brelse(tp); 1740 tp = NULL; 1741 1742 /* translated fields */ 1743 fsp->fsinfo_native.fs_signature = 1744 ltohi(fsinfo_disk.fs_signature); 1745 fsp->fsinfo_native.fs_free_clusters = 1746 ltohi(fsinfo_disk.fs_free_clusters); 1747 if (fsp->fsinfo_native.fs_signature != FAT32_FS_SIGN) { 1748 cmn_err(CE_NOTE, 1749 "!pcfs: fat32 fsinfo signature mismatch."); 1750 error = EINVAL; 1751 goto out; 1752 } 1753 } 1754 1755 return (0); 1756 1757 out: 1758 cmn_err(CE_NOTE, "!pcfs: illegal disk format"); 1759 if (tp) 1760 brelse(tp); 1761 if (bp) 1762 brelse(bp); 1763 if (fatp) 1764 kmem_free(fatp, fatsize); 1765 if (fat_changemap) 1766 kmem_free(fat_changemap, fat_changemapsize); 1767 1768 if (flags) { 1769 pc_mark_irrecov(fsp); 1770 } 1771 (void) VOP_CLOSE(devvp, (vfsp->vfs_flag & VFS_RDONLY) ? 1772 FREAD : FREAD|FWRITE, 1, (offset_t)0, CRED()); 1773 return (error); 1774 } 1775 1776 int 1777 pc_syncfat(struct pcfs *fsp) 1778 { 1779 struct buf *bp; 1780 int nfat; 1781 int error; 1782 struct fat32_boot_fsinfo fsinfo_disk; 1783 1784 PC_DPRINTF0(7, "pcfs_syncfat\n"); 1785 if ((fsp->pcfs_fatp == (uchar_t *)0) || 1786 !(fsp->pcfs_flags & PCFS_FATMOD)) 1787 return (0); 1788 /* 1789 * write out all copies of FATs 1790 */ 1791 fsp->pcfs_flags &= ~PCFS_FATMOD; 1792 fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT; 1793 for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) { 1794 error = pc_writefat(fsp, 1795 fsp->pcfs_fatstart + nfat*fsp->pcfs_fatsec); 1796 if (error) { 1797 pc_mark_irrecov(fsp); 1798 return (EIO); 1799 } 1800 } 1801 pc_clear_fatchanges(fsp); 1802 PC_DPRINTF0(6, "pcfs_syncfat: wrote out FAT\n"); 1803 /* write out fsinfo */ 1804 if (IS_FAT32(fsp)) { 1805 bp = bread(fsp->pcfs_xdev, 1806 fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector), 1807 PC_SAFESECSIZE); 1808 if (bp->b_flags & (B_ERROR | B_STALE)) { 1809 brelse(bp); 1810 return (EIO); 1811 } 1812 bcopy((void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF), 1813 &fsinfo_disk, sizeof (struct fat32_boot_fsinfo)); 1814 /* translate fields */ 1815 fsinfo_disk.fs_free_clusters = 1816 htoli(fsp->fsinfo_native.fs_free_clusters); 1817 fsinfo_disk.fs_next_cluster = (uint32_t)FSINFO_UNKNOWN; 1818 bcopy(&fsinfo_disk, 1819 (void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF), 1820 sizeof (struct fat32_boot_fsinfo)); 1821 bwrite2(bp); 1822 error = geterror(bp); 1823 brelse(bp); 1824 if (error) { 1825 pc_mark_irrecov(fsp); 1826 return (EIO); 1827 } 1828 } 1829 return (0); 1830 } 1831 1832 void 1833 pc_invalfat(struct pcfs *fsp) 1834 { 1835 struct pcfs *xfsp; 1836 int mount_cnt = 0; 1837 1838 PC_DPRINTF0(7, "pc_invalfat\n"); 1839 if (fsp->pcfs_fatp == (uchar_t *)0) 1840 panic("pc_invalfat"); 1841 /* 1842 * Release FAT 1843 */ 1844 kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsize); 1845 fsp->pcfs_fatp = NULL; 1846 kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize); 1847 fsp->pcfs_fat_changemap = NULL; 1848 /* 1849 * Invalidate all the blocks associated with the device. 1850 * Not needed if stateless. 1851 */ 1852 for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt) 1853 if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev) 1854 mount_cnt++; 1855 1856 if (!mount_cnt) 1857 binval(fsp->pcfs_xdev); 1858 /* 1859 * close mounted device 1860 */ 1861 (void) VOP_CLOSE(fsp->pcfs_devvp, 1862 (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, 1863 1, (offset_t)0, CRED()); 1864 } 1865 1866 void 1867 pc_badfs(struct pcfs *fsp) 1868 { 1869 cmn_err(CE_WARN, "corrupted PC file system on dev %x.%x\n", 1870 getmajor(fsp->pcfs_devvp->v_rdev), 1871 getminor(fsp->pcfs_devvp->v_rdev)); 1872 } 1873 1874 /* 1875 * The problem with supporting NFS on the PCFS filesystem is that there 1876 * is no good place to keep the generation number. The only possible 1877 * place is inside a directory entry. There are a few words that we 1878 * don't use - they store NT & OS/2 attributes, and the creation/last access 1879 * time of the file - but it seems wrong to use them. In addition, directory 1880 * entries come and go. If a directory is removed completely, its directory 1881 * blocks are freed and the generation numbers are lost. Whereas in ufs, 1882 * inode blocks are dedicated for inodes, so the generation numbers are 1883 * permanently kept on the disk. 1884 */ 1885 static int 1886 pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp) 1887 { 1888 struct pcnode *pcp; 1889 struct pc_fid *pcfid; 1890 struct pcfs *fsp; 1891 struct pcdir *ep; 1892 daddr_t eblkno; 1893 int eoffset; 1894 struct buf *bp; 1895 int error; 1896 pc_cluster32_t cn; 1897 1898 pcfid = (struct pc_fid *)fidp; 1899 fsp = VFSTOPCFS(vfsp); 1900 1901 error = pc_lockfs(fsp, 0, 0); 1902 if (error) { 1903 *vpp = NULL; 1904 return (error); 1905 } 1906 1907 if (pcfid->pcfid_block == 0) { 1908 pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0); 1909 pcp->pc_flags |= PC_EXTERNAL; 1910 *vpp = PCTOV(pcp); 1911 pc_unlockfs(fsp); 1912 return (0); 1913 } 1914 eblkno = pcfid->pcfid_block; 1915 eoffset = pcfid->pcfid_offset; 1916 if ((pc_dbtocl(fsp, 1917 eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) || 1918 (eoffset > fsp->pcfs_clsize)) { 1919 pc_unlockfs(fsp); 1920 *vpp = NULL; 1921 return (EINVAL); 1922 } 1923 1924 if (eblkno >= fsp->pcfs_datastart || (eblkno-fsp->pcfs_rdirstart) 1925 < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) { 1926 bp = bread(fsp->pcfs_xdev, eblkno, fsp->pcfs_clsize); 1927 } else { 1928 bp = bread(fsp->pcfs_xdev, eblkno, 1929 (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize); 1930 } 1931 if (bp->b_flags & (B_ERROR | B_STALE)) { 1932 error = geterror(bp); 1933 brelse(bp); 1934 if (error) 1935 pc_mark_irrecov(fsp); 1936 *vpp = NULL; 1937 pc_unlockfs(fsp); 1938 return (error); 1939 } 1940 ep = (struct pcdir *)(bp->b_un.b_addr + eoffset); 1941 /* 1942 * Ok, if this is a valid file handle that we gave out, 1943 * then simply ensuring that the creation time matches, 1944 * the entry has not been deleted, and it has a valid first 1945 * character should be enough. 1946 * 1947 * Unfortunately, verifying that the <blkno, offset> _still_ 1948 * refers to a directory entry is not easy, since we'd have 1949 * to search _all_ directories starting from root to find it. 1950 * That's a high price to pay just in case somebody is forging 1951 * file handles. So instead we verify that as much of the 1952 * entry is valid as we can: 1953 * 1954 * 1. The starting cluster is 0 (unallocated) or valid 1955 * 2. It is not an LFN entry 1956 * 3. It is not hidden (unless mounted as such) 1957 * 4. It is not the label 1958 */ 1959 cn = pc_getstartcluster(fsp, ep); 1960 /* 1961 * if the starting cluster is valid, but not valid according 1962 * to pc_validcl(), force it to be to simplify the following if. 1963 */ 1964 if (cn == 0) 1965 cn = PCF_FIRSTCLUSTER; 1966 if (IS_FAT32(fsp)) { 1967 if (cn >= PCF_LASTCLUSTER32) 1968 cn = PCF_FIRSTCLUSTER; 1969 } else { 1970 if (cn >= PCF_LASTCLUSTER) 1971 cn = PCF_FIRSTCLUSTER; 1972 } 1973 if ((!pc_validcl(fsp, cn)) || 1974 (PCDL_IS_LFN(ep)) || 1975 (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) || 1976 ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) { 1977 bp->b_flags |= B_STALE | B_AGE; 1978 brelse(bp); 1979 pc_unlockfs(fsp); 1980 return (EINVAL); 1981 } 1982 if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) && 1983 (ep->pcd_filename[0] != PCD_ERASED) && 1984 (pc_validchar(ep->pcd_filename[0]) || 1985 (ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) { 1986 pcp = pc_getnode(fsp, eblkno, eoffset, ep); 1987 pcp->pc_flags |= PC_EXTERNAL; 1988 *vpp = PCTOV(pcp); 1989 } else { 1990 *vpp = NULL; 1991 } 1992 bp->b_flags |= B_STALE | B_AGE; 1993 brelse(bp); 1994 pc_unlockfs(fsp); 1995 return (0); 1996 } 1997 1998 /* 1999 * if device is a PCMCIA pseudo floppy, return 1 2000 * otherwise, return 0 2001 */ 2002 static int 2003 pcfs_pseudo_floppy(dev_t rdev) 2004 { 2005 int error, err; 2006 struct dk_cinfo info; 2007 ldi_handle_t lh; 2008 ldi_ident_t li; 2009 2010 err = ldi_ident_from_mod(&modlinkage, &li); 2011 if (err) { 2012 PC_DPRINTF1(1, 2013 "pcfs_pseudo_floppy: ldi_ident_from_mod err=%d\n", err); 2014 return (0); 2015 } 2016 2017 err = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, CRED(), &lh, li); 2018 ldi_ident_release(li); 2019 if (err) { 2020 PC_DPRINTF1(1, 2021 "pcfs_pseudo_floppy: ldi_open err=%d\n", err); 2022 return (0); 2023 } 2024 2025 /* return value stored in err is purposfully ignored */ 2026 error = ldi_ioctl(lh, DKIOCINFO, (intptr_t)&info, FKIOCTL, 2027 CRED(), &err); 2028 2029 err = ldi_close(lh, FREAD, CRED()); 2030 if (err != 0) { 2031 PC_DPRINTF1(1, 2032 "pcfs_pseudo_floppy: ldi_close err=%d\n", err); 2033 return (0); 2034 } 2035 2036 if ((error == 0) && (info.dki_ctype == DKC_PCMCIA_MEM) && 2037 (info.dki_flags & DKI_PCMCIA_PFD)) 2038 return (1); 2039 else 2040 return (0); 2041 } 2042 2043 /* 2044 * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about 2045 * a meg), so we can't bread() it all in at once. This routine reads a 2046 * fat a chunk at a time. 2047 */ 2048 static int 2049 pc_readfat(struct pcfs *fsp, uchar_t *fatp, daddr_t start, size_t fatsize) 2050 { 2051 struct buf *bp; 2052 size_t off; 2053 size_t readsize; 2054 2055 readsize = fsp->pcfs_clsize; 2056 for (off = 0; off < fatsize; off += readsize, fatp += readsize) { 2057 if (readsize > (fatsize - off)) 2058 readsize = fatsize - off; 2059 bp = bread(fsp->pcfs_xdev, 2060 pc_dbdaddr(fsp, start + 2061 pc_cltodb(fsp, pc_lblkno(fsp, off))), 2062 readsize); 2063 if (bp->b_flags & (B_ERROR | B_STALE)) { 2064 brelse(bp); 2065 return (EIO); 2066 } 2067 bp->b_flags |= B_STALE | B_AGE; 2068 bcopy(bp->b_un.b_addr, fatp, readsize); 2069 brelse(bp); 2070 } 2071 return (0); 2072 } 2073 2074 /* 2075 * We write the FAT out a _lot_, in order to make sure that it 2076 * is up-to-date. But on a FAT32 system (large drive, small clusters) 2077 * the FAT might be a couple of megabytes, and writing it all out just 2078 * because we created or deleted a small file is painful (especially 2079 * since we do it for each alternate FAT too). So instead, for FAT16 and 2080 * FAT32 we only write out the bit that has changed. We don't clear 2081 * the 'updated' fields here because the caller might be writing out 2082 * several FATs, so the caller must use pc_clear_fatchanges() after 2083 * all FATs have been updated. 2084 */ 2085 static int 2086 pc_writefat(struct pcfs *fsp, daddr_t start) 2087 { 2088 struct buf *bp; 2089 size_t off; 2090 size_t writesize; 2091 int error; 2092 uchar_t *fatp = fsp->pcfs_fatp; 2093 size_t fatsize = fsp->pcfs_fatsize; 2094 2095 writesize = fsp->pcfs_clsize; 2096 for (off = 0; off < fatsize; off += writesize, fatp += writesize) { 2097 if (writesize > (fatsize - off)) 2098 writesize = fatsize - off; 2099 if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) { 2100 continue; 2101 } 2102 bp = ngeteblk(writesize); 2103 bp->b_edev = fsp->pcfs_xdev; 2104 bp->b_dev = cmpdev(bp->b_edev); 2105 bp->b_blkno = pc_dbdaddr(fsp, start + 2106 pc_cltodb(fsp, pc_lblkno(fsp, off))); 2107 bcopy(fatp, bp->b_un.b_addr, writesize); 2108 bwrite2(bp); 2109 error = geterror(bp); 2110 brelse(bp); 2111 if (error) { 2112 return (error); 2113 } 2114 } 2115 return (0); 2116 } 2117 2118 /* 2119 * Mark the FAT cluster that 'cn' is stored in as modified. 2120 */ 2121 void 2122 pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn) 2123 { 2124 pc_cluster32_t bn; 2125 size_t size; 2126 2127 /* which fat block is the cluster number stored in? */ 2128 if (IS_FAT32(fsp)) { 2129 size = sizeof (pc_cluster32_t); 2130 bn = pc_lblkno(fsp, cn * size); 2131 fsp->pcfs_fat_changemap[bn] = 1; 2132 } else if (IS_FAT16(fsp)) { 2133 size = sizeof (pc_cluster16_t); 2134 bn = pc_lblkno(fsp, cn * size); 2135 fsp->pcfs_fat_changemap[bn] = 1; 2136 } else { 2137 offset_t off; 2138 pc_cluster32_t nbn; 2139 2140 ASSERT(IS_FAT12(fsp)); 2141 off = cn + (cn >> 1); 2142 bn = pc_lblkno(fsp, off); 2143 fsp->pcfs_fat_changemap[bn] = 1; 2144 /* does this field wrap into the next fat cluster? */ 2145 nbn = pc_lblkno(fsp, off + 1); 2146 if (nbn != bn) { 2147 fsp->pcfs_fat_changemap[nbn] = 1; 2148 } 2149 } 2150 } 2151 2152 /* 2153 * return whether the FAT cluster 'bn' is updated and needs to 2154 * be written out. 2155 */ 2156 int 2157 pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn) 2158 { 2159 return (fsp->pcfs_fat_changemap[bn] == 1); 2160 } 2161