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, MO_DEFAULT, 109 NULL }, 110 { MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, 0, 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 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 int ret = 1; 1333 struct bootsec *bpb = (struct bootsec *)cp; 1334 1335 uint_t numclusters; /* number of clusters in file area */ 1336 ushort_t secsize = (int)ltohs(bpb->bps[0]); 1337 1338 if (pcfsdebuglevel >= 3) { 1339 if (!VALID_SECSIZE(secsize)) 1340 PC_DPRINTF1(3, "check_bpb: invalid bps value %d", 1341 secsize); 1342 1343 if (!VALID_SPCL(bpb->spcl)) 1344 PC_DPRINTF1(3, "check_bpb: invalid spcl value %d", 1345 bpb->spcl); 1346 1347 if ((secsize * bpb->spcl) >= (32 * 1024)) 1348 PC_DPRINTF3(3, "check_bpb: BPC > 32K %d x %d = %d", 1349 secsize, 1350 bpb->spcl, 1351 secsize * bpb->spcl); 1352 1353 if (bpb->nfat == 0) 1354 PC_DPRINTF1(3, "check_bpb: bad NumFATs value %d", 1355 bpb->nfat); 1356 1357 if (ltohs(bpb->res_sec[0]) == 0) 1358 PC_DPRINTF1(3, "check_bpb: bad RsvdSecCnt value %d", 1359 ltohs(bpb->res_sec[0])); 1360 1361 PC_DPRINTF1(5, "check_bpb: Media byte = %02x", 1362 bpb->mediadesriptor); 1363 1364 } 1365 if ((bpb->nfat == 0) || 1366 (bpb->mediadesriptor != 0xF0 && bpb->mediadesriptor < 0xF8) || 1367 (ltohs(cp[510]) != MBB_MAGIC) || 1368 !VALID_SECSIZE(secsize) || 1369 !VALID_SPCL(bpb->spcl) || 1370 (secsize * bpb->spcl >= (64 * 1024)) || 1371 !(ltohs(bpb->res_sec[0]))) 1372 return (0); 1373 1374 /* 1375 * Basic sanity checks passed so far, now try to determine which 1376 * FAT format to use. 1377 */ 1378 numclusters = bpb_to_numclusters(cp); 1379 1380 *fattypep = fattype(numclusters); 1381 1382 /* Do some final sanity checks for each specific type of FAT */ 1383 switch (*fattypep) { 1384 case 0: /* FAT12 */ 1385 case PCFS_FAT16: 1386 if (!check_bpb_fat16((struct bootsec *)cp)) 1387 return (0); 1388 break; 1389 case PCFS_FAT32: 1390 if (!check_bpb_fat32((struct fat32_bootsec *)cp)) 1391 return (0); 1392 break; 1393 default: /* not sure yet */ 1394 *fattypep = secondaryBPBChecks(cp); 1395 if (*fattypep == -1) { 1396 /* Still nothing, give it up. */ 1397 return (0); 1398 } 1399 break; 1400 } 1401 PC_DPRINTF0(5, "isBPB: BPB passes verification tests"); 1402 return (1); 1403 } 1404 1405 1406 /* 1407 * Get the FAT type for the DOS medium. 1408 * 1409 * ------------------------- 1410 * According to Microsoft: 1411 * The FAT type one of FAT12, FAT16, or FAT32 is determined by the 1412 * count of clusters on the volume and nothing else. 1413 * ------------------------- 1414 * 1415 */ 1416 static int 1417 pc_getfattype( 1418 struct vnode *devvp, 1419 int ldrive, 1420 daddr_t *strtsectp, 1421 int *fattypep) 1422 { 1423 uchar_t *cp; /* for searching out FAT string */ 1424 buf_t *bp = NULL; /* Disk buffer pointer */ 1425 int rval = 0; 1426 uchar_t sysid = 0; /* System ID character */ 1427 dev_t dev = devvp->v_rdev; 1428 1429 *strtsectp = (daddr_t)0; 1430 1431 /* 1432 * Open the device so we can check out the BPB or FDISK table, 1433 * then read in the sector. 1434 */ 1435 PC_DPRINTF2(5, "pc_getfattype: dev=%x ldrive=%x ", (int)dev, ldrive); 1436 if (rval = VOP_OPEN(&devvp, FREAD, CRED())) { 1437 PC_DPRINTF1(1, "pc_getfattype: open error=%d\n", rval); 1438 return (rval); 1439 } 1440 1441 /* 1442 * Read block 0 from device 1443 */ 1444 bp = bread(dev, (daddr_t)0, PC_SAFESECSIZE); 1445 if (bp->b_flags & B_ERROR) { 1446 PC_DPRINTF0(1, "pc_getfattype: read error\n"); 1447 rval = EIO; 1448 goto out; 1449 } 1450 1451 cp = (uchar_t *)bp->b_un.b_addr; 1452 1453 /* 1454 * If the first block is not a valid BPB, look for the 1455 * through the FDISK table. 1456 */ 1457 if (!isBPB(cp, fattypep)) { 1458 /* find the partition table and get 512 bytes from it. */ 1459 PC_DPRINTF0(5, "pc_getfattype: using FDISK table to find BPB"); 1460 1461 if (findTheDrive(dev, ldrive, &rval, &bp, 1462 strtsectp, &sysid) == 0) 1463 goto out; 1464 1465 brelse(bp); 1466 bp = bread(dev, *strtsectp, PC_SAFESECSIZE); 1467 if (bp->b_flags & B_ERROR) { 1468 PC_DPRINTF0(1, "pc_getfattype: read error\n"); 1469 rval = EIO; 1470 goto out; 1471 } 1472 cp = (uchar_t *)bp->b_un.b_addr; 1473 1474 /* If this one is still no good, give it up. */ 1475 if (!isBPB(cp, fattypep)) { 1476 rval = EIO; 1477 goto out; 1478 } 1479 } 1480 1481 out: 1482 /* 1483 * Release the buffer used 1484 */ 1485 if (bp != NULL) 1486 brelse(bp); 1487 (void) VOP_CLOSE(devvp, FREAD, 1, (offset_t)0, CRED()); 1488 return (rval); 1489 } 1490 1491 1492 /* 1493 * Get the boot parameter block and file allocation table. 1494 * If there is an old FAT, invalidate it. 1495 */ 1496 int 1497 pc_getfat(struct pcfs *fsp) 1498 { 1499 struct vfs *vfsp = PCFSTOVFS(fsp); 1500 struct buf *tp = 0; 1501 struct buf *bp = 0; 1502 uchar_t *fatp = NULL; 1503 uchar_t *fat_changemap = NULL; 1504 struct bootsec *bootp; 1505 struct fat32_bootsec *f32b; 1506 struct vnode *devvp; 1507 int error; 1508 int fatsize; 1509 int fat_changemapsize; 1510 int flags = 0; 1511 int nfat; 1512 int secsize; 1513 int fatsec; 1514 int fattype; 1515 1516 PC_DPRINTF0(5, "pc_getfat\n"); 1517 devvp = fsp->pcfs_devvp; 1518 if (fsp->pcfs_fatp) { 1519 /* 1520 * There is a FAT in core. 1521 * If there are open file pcnodes or we have modified it or 1522 * it hasn't timed out yet use the in core FAT. 1523 * Otherwise invalidate it and get a new one 1524 */ 1525 #ifdef notdef 1526 if (fsp->pcfs_frefs || 1527 (fsp->pcfs_flags & PCFS_FATMOD) || 1528 (gethrestime_sec() < fsp->pcfs_fattime)) { 1529 return (0); 1530 } else { 1531 mutex_enter(&pcfslock); 1532 pc_invalfat(fsp); 1533 mutex_exit(&pcfslock); 1534 } 1535 #endif /* notdef */ 1536 return (0); 1537 } 1538 /* 1539 * Open block device mounted on. 1540 */ 1541 error = VOP_OPEN(&devvp, 1542 (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, 1543 CRED()); 1544 if (error) { 1545 PC_DPRINTF1(1, "pc_getfat: open error=%d\n", error); 1546 return (error); 1547 } 1548 /* 1549 * Get boot parameter block and check it for validity 1550 */ 1551 tp = bread(fsp->pcfs_xdev, fsp->pcfs_dosstart, PC_SAFESECSIZE); 1552 if (tp->b_flags & (B_ERROR | B_STALE)) { 1553 PC_DPRINTF0(1, "pc_getfat: boot block error\n"); 1554 flags = tp->b_flags & B_ERROR; 1555 error = EIO; 1556 goto out; 1557 } 1558 tp->b_flags |= B_STALE | B_AGE; 1559 bootp = (struct bootsec *)tp->b_un.b_addr; 1560 1561 1562 /* get the sector size - may be more than 512 bytes */ 1563 secsize = (int)ltohs(bootp->bps[0]); 1564 /* check for bogus sector size - fat should be at least 1 sector */ 1565 if (IS_FAT32(fsp)) { 1566 f32b = (struct fat32_bootsec *)bootp; 1567 fatsec = ltohi(f32b->f_fatlength); 1568 } else { 1569 fatsec = ltohs(bootp->fatsec); 1570 } 1571 if (secsize < 512 || fatsec < 1 || bootp->nfat < 1) { 1572 cmn_err(CE_NOTE, "!pcfs: FAT size error"); 1573 error = EINVAL; 1574 goto out; 1575 } 1576 1577 switch (bootp->mediadesriptor) { 1578 default: 1579 cmn_err(CE_NOTE, "!pcfs: media-descriptor error, 0x%x", 1580 bootp->mediadesriptor); 1581 error = EINVAL; 1582 goto out; 1583 1584 case MD_FIXED: 1585 /* 1586 * PCMCIA pseudo floppy is type MD_FIXED, 1587 * but is accessed like a floppy 1588 */ 1589 if (!(fsp->pcfs_flags & PCFS_PCMCIA_NO_CIS)) { 1590 fsp->pcfs_flags |= PCFS_NOCHK; 1591 } 1592 /* FALLTHRU */ 1593 case SS8SPT: 1594 case DS8SPT: 1595 case SS9SPT: 1596 case DS9SPT: 1597 case DS18SPT: 1598 case DS9_15SPT: 1599 fsp->pcfs_secsize = secsize; 1600 fsp->pcfs_sdshift = secsize / DEV_BSIZE - 1; 1601 fsp->pcfs_entps = secsize / sizeof (struct pcdir); 1602 fsp->pcfs_spcl = (int)bootp->spcl; 1603 fsp->pcfs_fatsec = fatsec; 1604 fsp->pcfs_spt = (int)ltohs(bootp->spt); 1605 fsp->pcfs_rdirsec = (int)ltohs(bootp->rdirents[0]) 1606 * sizeof (struct pcdir) / secsize; 1607 fsp->pcfs_clsize = fsp->pcfs_spcl * secsize; 1608 fsp->pcfs_fatstart = fsp->pcfs_dosstart + 1609 (daddr_t)ltohs(bootp->res_sec[0]); 1610 fsp->pcfs_rdirstart = fsp->pcfs_fatstart + 1611 (bootp->nfat * fsp->pcfs_fatsec); 1612 fsp->pcfs_datastart = fsp->pcfs_rdirstart + fsp->pcfs_rdirsec; 1613 if (IS_FAT32(fsp)) 1614 fsp->pcfs_rdirstart = ltohi(f32b->f_rootcluster); 1615 fsp->pcfs_ncluster = (((int)(ltohs(bootp->numsect[0]) ? 1616 ltohs(bootp->numsect[0]) : ltohi(bootp->totalsec))) - 1617 fsp->pcfs_datastart + fsp->pcfs_dosstart) / fsp->pcfs_spcl; 1618 fsp->pcfs_numfat = (int)bootp->nfat; 1619 fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER; 1620 break; 1621 } 1622 1623 /* 1624 * Get FAT and check it for validity 1625 */ 1626 fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize; 1627 fatp = kmem_alloc(fatsize, KM_SLEEP); 1628 error = pc_readfat(fsp, fatp, fsp->pcfs_fatstart, fatsize); 1629 if (error) { 1630 flags = B_ERROR; 1631 goto out; 1632 } 1633 fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1; 1634 fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP); 1635 1636 /* 1637 * The only definite signature check is that the 1638 * media descriptor byte should match the first byte 1639 * of the FAT block. 1640 */ 1641 if (fatp[0] != bootp->mediadesriptor) { 1642 cmn_err(CE_NOTE, "!pcfs: FAT signature error"); 1643 error = EINVAL; 1644 goto out; 1645 } 1646 /* 1647 * Checking for fatsec and number of supported clusters, should 1648 * actually determine a FAT12/FAT media. 1649 */ 1650 if (fsp->pcfs_flags & PCFS_FAT16) { 1651 if ((fsp->pcfs_fatsec <= 12) && 1652 ((fatsize * 2 / 3) >= fsp->pcfs_ncluster)) { 1653 /* 1654 * We have a 12-bit FAT, rather than a 16-bit FAT. 1655 * Ignore what the fdisk table says. 1656 */ 1657 PC_DPRINTF0(2, "pc_getfattype: forcing 12-bit FAT\n"); 1658 fsp->pcfs_flags &= ~PCFS_FAT16; 1659 } 1660 } 1661 /* 1662 * Sanity check our FAT is large enough for the 1663 * clusters we think we have. 1664 */ 1665 if ((fsp->pcfs_flags & PCFS_FAT16) && 1666 ((fatsize / 2) < fsp->pcfs_ncluster)) { 1667 cmn_err(CE_NOTE, "!pcfs: FAT too small for number of clusters"); 1668 error = EINVAL; 1669 goto out; 1670 } 1671 1672 /* 1673 * Get alternate FATs and check for consistency 1674 * This is an inlined version of pc_readfat(). 1675 * Since we're only comparing FAT and alternate FAT, 1676 * there's no reason to let pc_readfat() copy data out 1677 * of the buf. Instead, compare in-situ, one cluster 1678 * at a time. 1679 */ 1680 for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) { 1681 size_t startsec; 1682 size_t off; 1683 1684 startsec = fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec; 1685 1686 for (off = 0; off < fatsize; off += fsp->pcfs_clsize) { 1687 bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, 1688 startsec + 1689 pc_cltodb(fsp, pc_lblkno(fsp, off))), 1690 MIN(fsp->pcfs_clsize, fatsize - off)); 1691 if (bp->b_flags & (B_ERROR | B_STALE)) { 1692 cmn_err(CE_NOTE, 1693 "!pcfs: alternate FAT #%d read error" 1694 " at byte %ld", nfat, off); 1695 flags = B_ERROR; 1696 error = EIO; 1697 goto out; 1698 } 1699 bp->b_flags |= B_STALE | B_AGE; 1700 if (bcmp(bp->b_un.b_addr, 1701 fatp + off, 1702 MIN(fsp->pcfs_clsize, fatsize - off))) { 1703 cmn_err(CE_NOTE, 1704 "!pcfs: alternate FAT #%d corrupted" 1705 " at byte %ld", nfat, off); 1706 flags = B_ERROR; 1707 } 1708 brelse(bp); 1709 bp = NULL; /* prevent double release */ 1710 } 1711 } 1712 1713 fsp->pcfs_fatsize = fatsize; 1714 fsp->pcfs_fatp = fatp; 1715 fsp->pcfs_fat_changemapsize = fat_changemapsize; 1716 fsp->pcfs_fat_changemap = fat_changemap; 1717 fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT; 1718 fsp->pcfs_fatjustread = 1; 1719 1720 brelse(tp); 1721 tp = NULL; 1722 if (IS_FAT32(fsp)) { 1723 /* get fsinfo */ 1724 struct fat32_boot_fsinfo fsinfo_disk; 1725 1726 fsp->f32fsinfo_sector = ltohs(f32b->f_infosector); 1727 tp = bread(fsp->pcfs_xdev, 1728 fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector), 1729 PC_SAFESECSIZE); 1730 if (tp->b_flags & (B_ERROR | B_STALE)) { 1731 cmn_err(CE_NOTE, "!pcfs: error reading fat32 fsinfo"); 1732 flags = tp->b_flags & B_ERROR; 1733 brelse(tp); 1734 tp = NULL; 1735 error = EIO; 1736 goto out; 1737 } 1738 tp->b_flags |= B_STALE | B_AGE; 1739 bcopy((void *)(tp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF), 1740 &fsinfo_disk, sizeof (struct fat32_boot_fsinfo)); 1741 brelse(tp); 1742 tp = NULL; 1743 1744 /* translated fields */ 1745 fsp->fsinfo_native.fs_signature = 1746 ltohi(fsinfo_disk.fs_signature); 1747 fsp->fsinfo_native.fs_free_clusters = 1748 ltohi(fsinfo_disk.fs_free_clusters); 1749 if (fsp->fsinfo_native.fs_signature != FAT32_FS_SIGN) { 1750 cmn_err(CE_NOTE, 1751 "!pcfs: fat32 fsinfo signature mismatch."); 1752 error = EINVAL; 1753 goto out; 1754 } 1755 } 1756 1757 return (0); 1758 1759 out: 1760 cmn_err(CE_NOTE, "!pcfs: illegal disk format"); 1761 if (tp) 1762 brelse(tp); 1763 if (bp) 1764 brelse(bp); 1765 if (fatp) 1766 kmem_free(fatp, fatsize); 1767 if (fat_changemap) 1768 kmem_free(fat_changemap, fat_changemapsize); 1769 1770 if (flags) { 1771 pc_mark_irrecov(fsp); 1772 } 1773 (void) VOP_CLOSE(devvp, (vfsp->vfs_flag & VFS_RDONLY) ? 1774 FREAD : FREAD|FWRITE, 1, (offset_t)0, CRED()); 1775 return (error); 1776 } 1777 1778 int 1779 pc_syncfat(struct pcfs *fsp) 1780 { 1781 struct buf *bp; 1782 int nfat; 1783 int error; 1784 struct fat32_boot_fsinfo fsinfo_disk; 1785 1786 PC_DPRINTF0(7, "pcfs_syncfat\n"); 1787 if ((fsp->pcfs_fatp == (uchar_t *)0) || 1788 !(fsp->pcfs_flags & PCFS_FATMOD)) 1789 return (0); 1790 /* 1791 * write out all copies of FATs 1792 */ 1793 fsp->pcfs_flags &= ~PCFS_FATMOD; 1794 fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT; 1795 for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) { 1796 error = pc_writefat(fsp, 1797 fsp->pcfs_fatstart + nfat*fsp->pcfs_fatsec); 1798 if (error) { 1799 pc_mark_irrecov(fsp); 1800 return (EIO); 1801 } 1802 } 1803 pc_clear_fatchanges(fsp); 1804 PC_DPRINTF0(6, "pcfs_syncfat: wrote out FAT\n"); 1805 /* write out fsinfo */ 1806 if (IS_FAT32(fsp)) { 1807 bp = bread(fsp->pcfs_xdev, 1808 fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector), 1809 PC_SAFESECSIZE); 1810 if (bp->b_flags & (B_ERROR | B_STALE)) { 1811 brelse(bp); 1812 return (EIO); 1813 } 1814 bcopy((void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF), 1815 &fsinfo_disk, sizeof (struct fat32_boot_fsinfo)); 1816 /* translate fields */ 1817 fsinfo_disk.fs_free_clusters = 1818 htoli(fsp->fsinfo_native.fs_free_clusters); 1819 fsinfo_disk.fs_next_cluster = (uint32_t)FSINFO_UNKNOWN; 1820 bcopy(&fsinfo_disk, 1821 (void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF), 1822 sizeof (struct fat32_boot_fsinfo)); 1823 bwrite2(bp); 1824 error = geterror(bp); 1825 brelse(bp); 1826 if (error) { 1827 pc_mark_irrecov(fsp); 1828 return (EIO); 1829 } 1830 } 1831 return (0); 1832 } 1833 1834 void 1835 pc_invalfat(struct pcfs *fsp) 1836 { 1837 struct pcfs *xfsp; 1838 int mount_cnt = 0; 1839 1840 PC_DPRINTF0(7, "pc_invalfat\n"); 1841 if (fsp->pcfs_fatp == (uchar_t *)0) 1842 panic("pc_invalfat"); 1843 /* 1844 * Release FAT 1845 */ 1846 kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsize); 1847 fsp->pcfs_fatp = NULL; 1848 kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize); 1849 fsp->pcfs_fat_changemap = NULL; 1850 /* 1851 * Invalidate all the blocks associated with the device. 1852 * Not needed if stateless. 1853 */ 1854 for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt) 1855 if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev) 1856 mount_cnt++; 1857 1858 if (!mount_cnt) 1859 binval(fsp->pcfs_xdev); 1860 /* 1861 * close mounted device 1862 */ 1863 (void) VOP_CLOSE(fsp->pcfs_devvp, 1864 (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, 1865 1, (offset_t)0, CRED()); 1866 } 1867 1868 void 1869 pc_badfs(struct pcfs *fsp) 1870 { 1871 cmn_err(CE_WARN, "corrupted PC file system on dev %x.%x\n", 1872 getmajor(fsp->pcfs_devvp->v_rdev), 1873 getminor(fsp->pcfs_devvp->v_rdev)); 1874 } 1875 1876 /* 1877 * The problem with supporting NFS on the PCFS filesystem is that there 1878 * is no good place to keep the generation number. The only possible 1879 * place is inside a directory entry. There are a few words that we 1880 * don't use - they store NT & OS/2 attributes, and the creation/last access 1881 * time of the file - but it seems wrong to use them. In addition, directory 1882 * entries come and go. If a directory is removed completely, its directory 1883 * blocks are freed and the generation numbers are lost. Whereas in ufs, 1884 * inode blocks are dedicated for inodes, so the generation numbers are 1885 * permanently kept on the disk. 1886 */ 1887 static int 1888 pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp) 1889 { 1890 struct pcnode *pcp; 1891 struct pc_fid *pcfid; 1892 struct pcfs *fsp; 1893 struct pcdir *ep; 1894 daddr_t eblkno; 1895 int eoffset; 1896 struct buf *bp; 1897 int error; 1898 pc_cluster32_t cn; 1899 1900 pcfid = (struct pc_fid *)fidp; 1901 fsp = VFSTOPCFS(vfsp); 1902 1903 error = pc_lockfs(fsp, 0, 0); 1904 if (error) { 1905 *vpp = NULL; 1906 return (error); 1907 } 1908 1909 if (pcfid->pcfid_block == 0) { 1910 pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0); 1911 pcp->pc_flags |= PC_EXTERNAL; 1912 *vpp = PCTOV(pcp); 1913 pc_unlockfs(fsp); 1914 return (0); 1915 } 1916 eblkno = pcfid->pcfid_block; 1917 eoffset = pcfid->pcfid_offset; 1918 if ((pc_dbtocl(fsp, 1919 eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) || 1920 (eoffset > fsp->pcfs_clsize)) { 1921 pc_unlockfs(fsp); 1922 *vpp = NULL; 1923 return (EINVAL); 1924 } 1925 1926 if (eblkno >= fsp->pcfs_datastart || (eblkno-fsp->pcfs_rdirstart) 1927 < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) { 1928 bp = bread(fsp->pcfs_xdev, eblkno, fsp->pcfs_clsize); 1929 } else { 1930 bp = bread(fsp->pcfs_xdev, eblkno, 1931 (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize); 1932 } 1933 if (bp->b_flags & (B_ERROR | B_STALE)) { 1934 error = geterror(bp); 1935 brelse(bp); 1936 if (error) 1937 pc_mark_irrecov(fsp); 1938 *vpp = NULL; 1939 pc_unlockfs(fsp); 1940 return (error); 1941 } 1942 ep = (struct pcdir *)(bp->b_un.b_addr + eoffset); 1943 /* 1944 * Ok, if this is a valid file handle that we gave out, 1945 * then simply ensuring that the creation time matches, 1946 * the entry has not been deleted, and it has a valid first 1947 * character should be enough. 1948 * 1949 * Unfortunately, verifying that the <blkno, offset> _still_ 1950 * refers to a directory entry is not easy, since we'd have 1951 * to search _all_ directories starting from root to find it. 1952 * That's a high price to pay just in case somebody is forging 1953 * file handles. So instead we verify that as much of the 1954 * entry is valid as we can: 1955 * 1956 * 1. The starting cluster is 0 (unallocated) or valid 1957 * 2. It is not an LFN entry 1958 * 3. It is not hidden (unless mounted as such) 1959 * 4. It is not the label 1960 */ 1961 cn = pc_getstartcluster(fsp, ep); 1962 /* 1963 * if the starting cluster is valid, but not valid according 1964 * to pc_validcl(), force it to be to simplify the following if. 1965 */ 1966 if (cn == 0) 1967 cn = PCF_FIRSTCLUSTER; 1968 if (IS_FAT32(fsp)) { 1969 if (cn >= PCF_LASTCLUSTER32) 1970 cn = PCF_FIRSTCLUSTER; 1971 } else { 1972 if (cn >= PCF_LASTCLUSTER) 1973 cn = PCF_FIRSTCLUSTER; 1974 } 1975 if ((!pc_validcl(fsp, cn)) || 1976 (PCDL_IS_LFN(ep)) || 1977 (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) || 1978 ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) { 1979 bp->b_flags |= B_STALE | B_AGE; 1980 brelse(bp); 1981 pc_unlockfs(fsp); 1982 return (EINVAL); 1983 } 1984 if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) && 1985 (ep->pcd_filename[0] != PCD_ERASED) && 1986 (pc_validchar(ep->pcd_filename[0]) || 1987 (ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) { 1988 pcp = pc_getnode(fsp, eblkno, eoffset, ep); 1989 pcp->pc_flags |= PC_EXTERNAL; 1990 *vpp = PCTOV(pcp); 1991 } else { 1992 *vpp = NULL; 1993 } 1994 bp->b_flags |= B_STALE | B_AGE; 1995 brelse(bp); 1996 pc_unlockfs(fsp); 1997 return (0); 1998 } 1999 2000 /* 2001 * if device is a PCMCIA pseudo floppy, return 1 2002 * otherwise, return 0 2003 */ 2004 static int 2005 pcfs_pseudo_floppy(dev_t rdev) 2006 { 2007 int error, err; 2008 struct dk_cinfo info; 2009 ldi_handle_t lh; 2010 ldi_ident_t li; 2011 2012 err = ldi_ident_from_mod(&modlinkage, &li); 2013 if (err) { 2014 PC_DPRINTF1(1, 2015 "pcfs_pseudo_floppy: ldi_ident_from_mod err=%d\n", err); 2016 return (0); 2017 } 2018 2019 err = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, CRED(), &lh, li); 2020 ldi_ident_release(li); 2021 if (err) { 2022 PC_DPRINTF1(1, 2023 "pcfs_pseudo_floppy: ldi_open err=%d\n", err); 2024 return (0); 2025 } 2026 2027 /* return value stored in err is purposfully ignored */ 2028 error = ldi_ioctl(lh, DKIOCINFO, (intptr_t)&info, FKIOCTL, 2029 CRED(), &err); 2030 2031 err = ldi_close(lh, FREAD, CRED()); 2032 if (err != 0) { 2033 PC_DPRINTF1(1, 2034 "pcfs_pseudo_floppy: ldi_close err=%d\n", err); 2035 return (0); 2036 } 2037 2038 if ((error == 0) && (info.dki_ctype == DKC_PCMCIA_MEM) && 2039 (info.dki_flags & DKI_PCMCIA_PFD)) 2040 return (1); 2041 else 2042 return (0); 2043 } 2044 2045 /* 2046 * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about 2047 * a meg), so we can't bread() it all in at once. This routine reads a 2048 * fat a chunk at a time. 2049 */ 2050 static int 2051 pc_readfat(struct pcfs *fsp, uchar_t *fatp, daddr_t start, size_t fatsize) 2052 { 2053 struct buf *bp; 2054 size_t off; 2055 size_t readsize; 2056 2057 readsize = fsp->pcfs_clsize; 2058 for (off = 0; off < fatsize; off += readsize, fatp += readsize) { 2059 if (readsize > (fatsize - off)) 2060 readsize = fatsize - off; 2061 bp = bread(fsp->pcfs_xdev, 2062 pc_dbdaddr(fsp, start + 2063 pc_cltodb(fsp, pc_lblkno(fsp, off))), 2064 readsize); 2065 if (bp->b_flags & (B_ERROR | B_STALE)) { 2066 brelse(bp); 2067 return (EIO); 2068 } 2069 bp->b_flags |= B_STALE | B_AGE; 2070 bcopy(bp->b_un.b_addr, fatp, readsize); 2071 brelse(bp); 2072 } 2073 return (0); 2074 } 2075 2076 /* 2077 * We write the FAT out a _lot_, in order to make sure that it 2078 * is up-to-date. But on a FAT32 system (large drive, small clusters) 2079 * the FAT might be a couple of megabytes, and writing it all out just 2080 * because we created or deleted a small file is painful (especially 2081 * since we do it for each alternate FAT too). So instead, for FAT16 and 2082 * FAT32 we only write out the bit that has changed. We don't clear 2083 * the 'updated' fields here because the caller might be writing out 2084 * several FATs, so the caller must use pc_clear_fatchanges() after 2085 * all FATs have been updated. 2086 */ 2087 static int 2088 pc_writefat(struct pcfs *fsp, daddr_t start) 2089 { 2090 struct buf *bp; 2091 size_t off; 2092 size_t writesize; 2093 int error; 2094 uchar_t *fatp = fsp->pcfs_fatp; 2095 size_t fatsize = fsp->pcfs_fatsize; 2096 2097 writesize = fsp->pcfs_clsize; 2098 for (off = 0; off < fatsize; off += writesize, fatp += writesize) { 2099 if (writesize > (fatsize - off)) 2100 writesize = fatsize - off; 2101 if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) { 2102 continue; 2103 } 2104 bp = ngeteblk(writesize); 2105 bp->b_edev = fsp->pcfs_xdev; 2106 bp->b_dev = cmpdev(bp->b_edev); 2107 bp->b_blkno = pc_dbdaddr(fsp, start + 2108 pc_cltodb(fsp, pc_lblkno(fsp, off))); 2109 bcopy(fatp, bp->b_un.b_addr, writesize); 2110 bwrite2(bp); 2111 error = geterror(bp); 2112 brelse(bp); 2113 if (error) { 2114 return (error); 2115 } 2116 } 2117 return (0); 2118 } 2119 2120 /* 2121 * Mark the FAT cluster that 'cn' is stored in as modified. 2122 */ 2123 void 2124 pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn) 2125 { 2126 pc_cluster32_t bn; 2127 size_t size; 2128 2129 /* which fat block is the cluster number stored in? */ 2130 if (IS_FAT32(fsp)) { 2131 size = sizeof (pc_cluster32_t); 2132 bn = pc_lblkno(fsp, cn * size); 2133 fsp->pcfs_fat_changemap[bn] = 1; 2134 } else if (IS_FAT16(fsp)) { 2135 size = sizeof (pc_cluster16_t); 2136 bn = pc_lblkno(fsp, cn * size); 2137 fsp->pcfs_fat_changemap[bn] = 1; 2138 } else { 2139 offset_t off; 2140 pc_cluster32_t nbn; 2141 2142 ASSERT(IS_FAT12(fsp)); 2143 off = cn + (cn >> 1); 2144 bn = pc_lblkno(fsp, off); 2145 fsp->pcfs_fat_changemap[bn] = 1; 2146 /* does this field wrap into the next fat cluster? */ 2147 nbn = pc_lblkno(fsp, off + 1); 2148 if (nbn != bn) { 2149 fsp->pcfs_fat_changemap[nbn] = 1; 2150 } 2151 } 2152 } 2153 2154 /* 2155 * return whether the FAT cluster 'bn' is updated and needs to 2156 * be written out. 2157 */ 2158 int 2159 pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn) 2160 { 2161 return (fsp->pcfs_fat_changemap[bn] == 1); 2162 } 2163