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_psuedo_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 "filesystem for PC", 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 psuedo floppies organization 511 */ 512 if (dos_ldrive == 0 && pcfs_psuedo_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 (void) pc_getfat(fsp); 789 fsp->pcfs_flags |= PCFS_LOCKED; 790 fsp->pcfs_owner = curthread; 791 fsp->pcfs_count++; 792 } 793 return (0); 794 } 795 796 void 797 pc_unlockfs(struct pcfs *fsp) 798 { 799 800 if ((fsp->pcfs_flags & PCFS_LOCKED) == 0) 801 panic("pc_unlockfs"); 802 if (--fsp->pcfs_count < 0) 803 panic("pc_unlockfs: count"); 804 if (fsp->pcfs_count == 0) { 805 fsp->pcfs_flags &= ~PCFS_LOCKED; 806 fsp->pcfs_owner = 0; 807 mutex_exit(&fsp->pcfs_lock); 808 } 809 } 810 811 /* 812 * isDosDrive() 813 * Boolean function. Give it the systid field for an fdisk partition 814 * and it decides if that's a systid that describes a DOS drive. We 815 * use systid values defined in sys/dktp/fdisk.h. 816 */ 817 static int 818 isDosDrive(uchar_t checkMe) 819 { 820 return ((checkMe == DOSOS12) || (checkMe == DOSOS16) || 821 (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) || 822 (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) || 823 (checkMe == DIAGPART)); 824 } 825 826 /* 827 * isDosExtended() 828 * Boolean function. Give it the systid field for an fdisk partition 829 * and it decides if that's a systid that describes an extended DOS 830 * partition. 831 */ 832 static int 833 isDosExtended(uchar_t checkMe) 834 { 835 return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA)); 836 } 837 838 /* 839 * isBootPart() 840 * Boolean function. Give it the systid field for an fdisk partition 841 * and it decides if that's a systid that describes a Solaris boot 842 * partition. 843 */ 844 static int 845 isBootPart(uchar_t checkMe) 846 { 847 return (checkMe == X86BOOT); 848 } 849 850 /* 851 * noLogicalDrive() 852 * Display error message about not being able to find a logical 853 * drive. 854 */ 855 static void 856 noLogicalDrive(int requested) 857 { 858 if (requested == BOOT_PARTITION_DRIVE) { 859 cmn_err(CE_NOTE, "!pcfs: no boot partition"); 860 } else { 861 cmn_err(CE_NOTE, "!pcfs: no such logical drive"); 862 } 863 } 864 865 /* 866 * findTheDrive() 867 * Discover offset of the requested logical drive, and return 868 * that offset (startSector), the systid of that drive (sysid), 869 * and a buffer pointer (bp), with the buffer contents being 870 * the first sector of the logical drive (i.e., the sector that 871 * contains the BPB for that drive). 872 */ 873 static int 874 findTheDrive(dev_t dev, int askedFor, int *error, buf_t **bp, 875 daddr_t *startSector, uchar_t *sysid) 876 { 877 struct ipart dosp[FD_NUMPART]; /* incore fdisk partition structure */ 878 struct mboot *dosp_ptr; /* boot structure pointer */ 879 daddr_t lastseek = 0; /* Disk block we sought previously */ 880 daddr_t diskblk = 0; /* Disk block to get */ 881 daddr_t xstartsect; /* base of Extended DOS partition */ 882 int logicalDriveCount = 0; /* Count of logical drives seen */ 883 int extendedPart = -1; /* index of extended dos partition */ 884 int primaryPart = -1; /* index of primary dos partition */ 885 int bootPart = -1; /* index of a Solaris boot partition */ 886 int xnumsect = -1; /* length of extended DOS partition */ 887 int driveIndex; /* computed FDISK table index */ 888 int i; 889 /* 890 * Count of drives in the current extended partition's 891 * FDISK table, and indexes of the drives themselves. 892 */ 893 int extndDrives[FD_NUMPART]; 894 int numDrives = 0; 895 896 /* 897 * Count of drives (beyond primary) in master boot record's 898 * FDISK table, and indexes of the drives themselves. 899 */ 900 int extraDrives[FD_NUMPART]; 901 int numExtraDrives = 0; 902 903 /* 904 * Copy from disk block into memory aligned structure for fdisk usage. 905 */ 906 dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr; 907 bcopy(dosp_ptr->parts, dosp, sizeof (struct ipart) * FD_NUMPART); 908 909 /* 910 * Get a summary of what is in the Master FDISK table. 911 * Normally we expect to find one partition marked as a DOS drive. 912 * This partition is the one Windows calls the primary dos partition. 913 * If the machine has any logical drives then we also expect 914 * to find a partition marked as an extended DOS partition. 915 * 916 * Sometimes we'll find multiple partitions marked as DOS drives. 917 * The Solaris fdisk program allows these partitions 918 * to be created, but Windows fdisk no longer does. We still need 919 * to support these, though, since Windows does. We also need to fix 920 * our fdisk to behave like the Windows version. 921 * 922 * It turns out that some off-the-shelf media have *only* an 923 * Extended partition, so we need to deal with that case as well. 924 * 925 * Only a single (the first) Extended or Boot Partition will 926 * be recognized. Any others will be ignored. 927 */ 928 for (i = 0; i < FD_NUMPART; i++) { 929 if (isDosDrive(dosp[i].systid)) { 930 if (primaryPart < 0) { 931 logicalDriveCount++; 932 primaryPart = i; 933 } else { 934 extraDrives[numExtraDrives++] = i; 935 } 936 continue; 937 } 938 if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) { 939 extendedPart = i; 940 continue; 941 } 942 if ((bootPart < 0) && isBootPart(dosp[i].systid)) { 943 bootPart = i; 944 continue; 945 } 946 } 947 948 if (askedFor == BOOT_PARTITION_DRIVE) { 949 if (bootPart < 0) { 950 noLogicalDrive(askedFor); 951 *error = EINVAL; 952 return (0); 953 } 954 *sysid = dosp[bootPart].systid; 955 *startSector = ltohi(dosp[bootPart].relsect); 956 return (1); 957 } 958 959 if (askedFor == PRIMARY_DOS_DRIVE && primaryPart >= 0) { 960 *sysid = dosp[primaryPart].systid; 961 *startSector = ltohi(dosp[primaryPart].relsect); 962 return (1); 963 } 964 965 /* 966 * We are not looking for the C: drive (or the primary drive 967 * was not found), so we had better have an extended partition 968 * or extra drives in the Master FDISK table. 969 */ 970 if ((extendedPart < 0) && (numExtraDrives == 0)) { 971 cmn_err(CE_NOTE, "!pcfs: no extended dos partition"); 972 noLogicalDrive(askedFor); 973 *error = EINVAL; 974 return (0); 975 } 976 977 if (extendedPart >= 0) { 978 diskblk = xstartsect = ltohi(dosp[extendedPart].relsect); 979 xnumsect = ltohi(dosp[extendedPart].numsect); 980 do { 981 /* 982 * If the seek would not cause us to change 983 * position on the drive, then we're out of 984 * extended partitions to examine. 985 */ 986 if (diskblk == lastseek) 987 break; 988 logicalDriveCount += numDrives; 989 /* 990 * Seek the next extended partition, and find 991 * logical drives within it. 992 */ 993 brelse(*bp); 994 *bp = bread(dev, diskblk, PC_SAFESECSIZE); 995 if ((*bp)->b_flags & B_ERROR) { 996 PC_DPRINTF0(1, "pc_getfattype: read error\n"); 997 *error = EIO; 998 return (0); 999 } 1000 lastseek = diskblk; 1001 dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr; 1002 if (ltohs(dosp_ptr->signature) != MBB_MAGIC) { 1003 cmn_err(CE_NOTE, "!pcfs: " 1004 "extended partition signature err"); 1005 *error = EINVAL; 1006 return (0); 1007 } 1008 bcopy(dosp_ptr->parts, dosp, 1009 sizeof (struct ipart) * FD_NUMPART); 1010 /* 1011 * Count up drives, and track where the next 1012 * extended partition is in case we need it. We 1013 * are expecting only one extended partition. If 1014 * there is more than one we'll only go to the 1015 * first one we see, but warn about ignoring. 1016 */ 1017 numDrives = 0; 1018 for (i = 0; i < FD_NUMPART; i++) { 1019 if (isDosDrive(dosp[i].systid)) { 1020 extndDrives[numDrives++] = i; 1021 continue; 1022 } else if (isDosExtended(dosp[i].systid)) { 1023 if (diskblk != lastseek) { 1024 /* 1025 * Already found an extended 1026 * partition in this table. 1027 */ 1028 cmn_err(CE_NOTE, 1029 "!pcfs: ignoring unexpected" 1030 " additional extended" 1031 " partition"); 1032 continue; 1033 } 1034 diskblk = xstartsect + 1035 ltohi(dosp[i].relsect); 1036 continue; 1037 } 1038 } 1039 } while (askedFor > logicalDriveCount + numDrives); 1040 1041 if (askedFor <= logicalDriveCount + numDrives) { 1042 /* 1043 * The number of logical drives we've found thus 1044 * far is enough to get us to the one we were 1045 * searching for. 1046 */ 1047 driveIndex = logicalDriveCount + numDrives - askedFor; 1048 *sysid = dosp[extndDrives[driveIndex]].systid; 1049 *startSector = 1050 ltohi(dosp[extndDrives[driveIndex]].relsect) + 1051 lastseek; 1052 if (*startSector > (xstartsect + xnumsect)) { 1053 cmn_err(CE_NOTE, "!pcfs: extended partition " 1054 "values bad"); 1055 *error = EINVAL; 1056 return (0); 1057 } 1058 return (1); 1059 } else { 1060 /* 1061 * We ran out of extended dos partition 1062 * drives. The only hope now is to go 1063 * back to extra drives defined in the master 1064 * fdisk table. But we overwrote that table 1065 * already, so we must load it in again. 1066 */ 1067 logicalDriveCount += numDrives; 1068 brelse(*bp); 1069 *bp = bread(dev, (daddr_t)0, PC_SAFESECSIZE); 1070 if ((*bp)->b_flags & B_ERROR) { 1071 PC_DPRINTF0(1, "pc_getfattype: read error\n"); 1072 *error = EIO; 1073 return (0); 1074 } 1075 dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr; 1076 bcopy(dosp_ptr->parts, dosp, 1077 sizeof (struct ipart) * FD_NUMPART); 1078 } 1079 } 1080 /* 1081 * Still haven't found the drive, is it an extra 1082 * drive defined in the main FDISK table? 1083 */ 1084 if (askedFor <= logicalDriveCount + numExtraDrives) { 1085 driveIndex = logicalDriveCount + numExtraDrives - askedFor; 1086 *sysid = dosp[extraDrives[driveIndex]].systid; 1087 *startSector = ltohi(dosp[extraDrives[driveIndex]].relsect); 1088 return (1); 1089 } 1090 /* 1091 * Still haven't found the drive, and there is 1092 * nowhere else to look. 1093 */ 1094 noLogicalDrive(askedFor); 1095 *error = EINVAL; 1096 return (0); 1097 } 1098 1099 /* 1100 * Get the FAT type for the DOS medium. 1101 * 1102 * We look at sector 0 and determine if we are looking at an FDISK table 1103 * or a BIOS Parameter Block (BPB). Most of the time, if its a floppy 1104 * we'll be looking at a BPB and if we're looking at a hard drive we'll be 1105 * examining an FDISK table. Those fun exceptions do happen, though. 1106 * 1107 * If we are looking at a BPB, we can calculate and verify the FAT size. 1108 * If we are looking at an FDISK partition table, we scan the partition 1109 * table for the requested logical volume. 1110 */ 1111 static int 1112 pc_getfattype( 1113 struct vnode *devvp, 1114 int ldrive, 1115 daddr_t *strtsectp, 1116 int *fattypep) 1117 { 1118 struct mboot *dosp_ptr; /* boot structure pointer */ 1119 struct bootsec *bootp, *bpbp; /* for detailed sector examination */ 1120 uchar_t *cp; /* for searching out FAT string */ 1121 uint_t overhead; /* sectors not part of file area */ 1122 uint_t numclusters; /* number of clusters in file area */ 1123 uint_t bytesoffat; /* computed number of bytes in a FAT */ 1124 int secsize; /* Sector size in bytes/sec */ 1125 buf_t *bp = NULL; /* Disk buffer pointer */ 1126 int rval = 0; 1127 uchar_t sysid = 0; /* System ID character */ 1128 dev_t dev = devvp->v_rdev; 1129 1130 *strtsectp = (daddr_t)0; 1131 1132 /* 1133 * Open the device so we can check out the BPB or FDISK table, 1134 * then read in the sector. 1135 */ 1136 PC_DPRINTF2(5, "pc_getfattype: dev=%x ldrive=%x ", (int)dev, ldrive); 1137 if (rval = VOP_OPEN(&devvp, FREAD, CRED())) { 1138 PC_DPRINTF1(1, "pc_getfattype: open error=%d\n", rval); 1139 return (rval); 1140 } 1141 1142 /* 1143 * Read block 0 from device 1144 */ 1145 bp = bread(dev, (daddr_t)0, PC_SAFESECSIZE); 1146 if (bp->b_flags & B_ERROR) { 1147 PC_DPRINTF0(1, "pc_getfattype: read error\n"); 1148 rval = EIO; 1149 goto out; 1150 } 1151 1152 /* 1153 * If this is a logical volume with a FAT file system, 1154 * then we expect to find a "file system boot sector" 1155 * (also called a BIOS Perameter Block -- or BPB) in 1156 * the first physical sector. 1157 */ 1158 1159 /* 1160 * The word "FAT" is encoded into the BPB beginning at 1161 * PCFS_TYPESTRING_OFFSET16 in 12 and 16 bit FATs. It is 1162 * encoded at FS_TYPESTRING_OFFSET32 in 32 bit FATs. 1163 */ 1164 cp = (uchar_t *)bp->b_un.b_addr; 1165 if (*(cp + PCFS_TYPESTRING_OFFSET16) == 'F' && 1166 *(cp + PCFS_TYPESTRING_OFFSET16 + 1) == 'A' && 1167 *(cp + PCFS_TYPESTRING_OFFSET16 + 2) == 'T') { 1168 PC_DPRINTF0(5, "Found the FAT string at 12/16 location\n"); 1169 /* 1170 * Compute a bits/fat value 1171 */ 1172 bpbp = (struct bootsec *)bp->b_un.b_addr; 1173 secsize = (int)ltohs(bpbp->bps[0]); 1174 /* 1175 * Check for bogus sector size - 1176 * fat should be at least 1 sector 1177 * If anything looks weird, we have to bail and try looking 1178 * for an FDISK table instead. 1179 */ 1180 if (secsize < 512 || (int)ltohs(bpbp->fatsec) < 1 || 1181 bpbp->nfat < 1 || bpbp->spcl < 1) { 1182 PC_DPRINTF4(5, "One or more BPB fields bad\n" 1183 "bytes/sec = %d, sec/fat = %d, numfats = %d, " 1184 "sec/clust = %d\n", secsize, 1185 (int)ltohs(bpbp->fatsec), bpbp->nfat, bpbp->spcl); 1186 goto lookforfdisk; 1187 } 1188 1189 overhead = bpbp->nfat * ltohs(bpbp->fatsec); 1190 overhead += ltohs(bpbp->res_sec[0]); 1191 overhead += (ltohs(bpbp->rdirents[0]) * 1192 sizeof (struct pcdir)) / secsize; 1193 1194 numclusters = ((ltohs(bpbp->numsect[0]) ? 1195 ltohs(bpbp->numsect[0]) : ltohi(bpbp->totalsec)) - 1196 overhead) / bpbp->spcl; 1197 1198 /* 1199 * If the number of clusters looks bad, go look for an 1200 * FDISK table. 1201 */ 1202 if (numclusters < 1) { 1203 PC_DPRINTF1(5, "num clusters is bad ( = %d )\n", 1204 numclusters); 1205 goto lookforfdisk; 1206 } 1207 1208 /* 1209 * The number of bytes of FAT determines the maximum number 1210 * of entries of a given size that FAT can contain. 1211 * The FAT can only contain (bytes of FAT)*8/12 12-bit entries 1212 * and (bytes of FAT)*8/16 16-bit entries. 1213 */ 1214 bytesoffat = ltohs(bpbp->fatsec) * secsize; 1215 PC_DPRINTF1(5, "Computed bytes of fat = %u\n", bytesoffat); 1216 if ((bytesoffat * 2 / 3) >= numclusters && 1217 *(cp + PCFS_TYPESTRING_OFFSET16 + 4) == '2') { 1218 PC_DPRINTF0(4, "pc_getfattype: 12-bit FAT\n"); 1219 *fattypep = 0; 1220 rval = 0; 1221 goto out; 1222 } else if (*(cp + PCFS_TYPESTRING_OFFSET16 + 4) == '6') { 1223 /* 1224 * this check can result in a false positive, where 1225 * we believe a FAT12 filesystem to be a FAT16 one 1226 * (if the type recorded in the header block lies). 1227 * we recover from being lied to, in 'pc_getfat' by 1228 * Forcing fat12 over fat16, if 1229 * 'pcfs_fatsec <= 12' and 1230 * '(byteoffat * 2 / 3) >= numclusters' 1231 * the obvious check would have been: 1232 * else if ((bytesoffat / 2) >= numclusters && 1233 * *(cp + PCFS_TYPESTRING_OFFSET16 + 4) == '6') 1234 */ 1235 PC_DPRINTF0(4, "pc_getfattype: 16-bit FAT\n"); 1236 *fattypep = PCFS_FAT16; 1237 rval = 0; 1238 goto out; 1239 } 1240 goto lookforfdisk; 1241 } else if (*(cp + PCFS_TYPESTRING_OFFSET32) == 'F' && 1242 *(cp + PCFS_TYPESTRING_OFFSET32 + 1) == 'A' && 1243 *(cp + PCFS_TYPESTRING_OFFSET32 + 2) == 'T') { 1244 PC_DPRINTF0(5, "Found the FAT string at 32 location\n"); 1245 bpbp = (struct bootsec *)bp->b_un.b_addr; 1246 if ((int)ltohs(bpbp->fatsec) != 0) { 1247 /* 1248 * Not a good sign, we expect it to be zero if 1249 * this is really a FAT32 BPB. All we can do 1250 * is consider the string an anomaly, and go look 1251 * for an FDISK table. 1252 */ 1253 PC_DPRINTF0(5, "But secs/fat non-zero\n"); 1254 goto lookforfdisk; 1255 } 1256 PC_DPRINTF0(4, "pc_getfattype: 32-bit FAT\n"); 1257 *fattypep = PCFS_FAT32; 1258 rval = 0; 1259 goto out; 1260 } else { 1261 /* 1262 * We've got some legacy cases (like the stuff fdformat 1263 * produces!). Basically this is pre-MSDOS4.0 FATs. 1264 * 1265 * We'll declare a match if: 1266 * 1. Bytes/Sector and other fields seem reasonable 1267 * 2. The media byte matches a known one. 1268 * 1269 * If the media byte indicates a floppy we'll 1270 * assume FAT12, otherwise we'll assume FAT16. 1271 */ 1272 bpbp = (struct bootsec *)bp->b_un.b_addr; 1273 secsize = (int)ltohs(bpbp->bps[0]); 1274 if (secsize && secsize % 512 == 0 && 1275 ltohs(bpbp->fatsec) > 0 && bpbp->nfat > 0 && 1276 bpbp->spcl > 0 && ltohs(bpbp->res_sec[0]) >= 1 && 1277 ltohs(bpbp->numsect[0]) > 0) { 1278 switch (bpbp->mediadesriptor) { 1279 case SS8SPT: 1280 case DS8SPT: 1281 case SS9SPT: 1282 case DS9SPT: 1283 case DS18SPT: 1284 case DS9_15SPT: 1285 *fattypep = 0; 1286 rval = 0; 1287 goto out; 1288 case MD_FIXED: 1289 *fattypep = PCFS_FAT16; 1290 rval = 0; 1291 goto out; 1292 default: 1293 goto lookforfdisk; 1294 } 1295 } 1296 } 1297 1298 lookforfdisk: 1299 /* 1300 * If we got here then we didn't find a BPB. 1301 * We now assume we are looking at the start of a hard drive, 1302 * where the first sector will be a Master Boot Record (MBR). 1303 * The MBR contains a partition table (also called an FDISK 1304 * table) and should end with a signature word (MBB_MAGIC). 1305 * 1306 * Check signature at end of boot block for good value. 1307 * If not then error with invalid request. 1308 */ 1309 dosp_ptr = (struct mboot *)bp->b_un.b_addr; 1310 if (ltohs(dosp_ptr->signature) != MBB_MAGIC) { 1311 cmn_err(CE_NOTE, "!pcfs: MBR signature error"); 1312 rval = EINVAL; 1313 goto out; 1314 } 1315 if (ldrive <= 0) { 1316 cmn_err(CE_NOTE, "!pcfs: no logical drive specified"); 1317 rval = EINVAL; 1318 goto out; 1319 } 1320 1321 if (findTheDrive(dev, ldrive, &rval, &bp, strtsectp, &sysid) == 0) 1322 goto out; 1323 1324 /* 1325 * Check the sysid value of the logical drive. 1326 * Return the correct value for the type of FAT found. 1327 * Else return a value of -1 for unknown FAT type. 1328 */ 1329 if ((sysid == DOS_FAT32) || (sysid == DOS_FAT32_LBA)) { 1330 *fattypep = PCFS_FAT32 | PCFS_NOCHK; 1331 PC_DPRINTF0(4, "pc_getfattype: 32-bit FAT\n"); 1332 } else if ((sysid == DOS_SYSFAT16) || (sysid == DOS_SYSHUGE) || 1333 (sysid == DIAGPART) || 1334 (sysid == DOS_FAT16P_LBA) || (sysid == DOS_FAT16_LBA)) { 1335 *fattypep = PCFS_FAT16 | PCFS_NOCHK; 1336 PC_DPRINTF0(4, "pc_getfattype: 16-bit FAT\n"); 1337 } else if (sysid == DOS_SYSFAT12) { 1338 *fattypep = PCFS_NOCHK; 1339 PC_DPRINTF0(4, "pc_getfattype: 12-bit FAT\n"); 1340 } else if (sysid == X86BOOT) { 1341 brelse(bp); 1342 bp = bread(dev, *strtsectp, PC_SAFESECSIZE); 1343 if (bp->b_flags & B_ERROR) { 1344 PC_DPRINTF0(1, "pc_getfattype: read error\n"); 1345 rval = EIO; 1346 goto out; 1347 } 1348 bootp = (struct bootsec *)bp->b_un.b_addr; 1349 1350 /* get the sector size - may be more than 512 bytes */ 1351 secsize = (int)ltohs(bootp->bps[0]); 1352 /* 1353 * Check for bogus sector size - 1354 * fat should be at least 1 sector 1355 */ 1356 if (secsize < 512 || (int)ltohs(bootp->fatsec) < 1 || 1357 bootp->nfat < 1 || bootp->spcl < 1) { 1358 cmn_err(CE_NOTE, "!pcfs: FAT size error"); 1359 rval = EINVAL; 1360 goto out; 1361 } 1362 1363 overhead = bootp->nfat * ltohs(bootp->fatsec); 1364 overhead += ltohs(bootp->res_sec[0]); 1365 overhead += (ltohs(bootp->rdirents[0]) * 1366 sizeof (struct pcdir)) / secsize; 1367 1368 numclusters = ((ltohs(bootp->numsect[0]) ? 1369 ltohs(bootp->numsect[0]) : ltohi(bootp->totalsec)) - 1370 overhead) / bootp->spcl; 1371 1372 if (numclusters > DOS_F12MAXC) { 1373 PC_DPRINTF0(4, "pc_getfattype: 16-bit FAT BOOTPART\n"); 1374 *fattypep = PCFS_FAT16 | PCFS_NOCHK | PCFS_BOOTPART; 1375 } else { 1376 PC_DPRINTF0(4, "pc_getfattype: 12-bit FAT BOOTPART\n"); 1377 *fattypep = PCFS_NOCHK | PCFS_BOOTPART; 1378 } 1379 } else { 1380 cmn_err(CE_NOTE, "!pcfs: unknown FAT type"); 1381 rval = EINVAL; 1382 } 1383 1384 /* 1385 * Release the buffer used 1386 */ 1387 out: 1388 if (bp != NULL) 1389 brelse(bp); 1390 (void) VOP_CLOSE(devvp, FREAD, 1, (offset_t)0, CRED()); 1391 return (rval); 1392 } 1393 1394 1395 /* 1396 * Get the boot parameter block and file allocation table. 1397 * If there is an old FAT, invalidate it. 1398 */ 1399 int 1400 pc_getfat(struct pcfs *fsp) 1401 { 1402 struct vfs *vfsp = PCFSTOVFS(fsp); 1403 struct buf *tp = 0; 1404 struct buf *bp = 0; 1405 uchar_t *fatp = NULL; 1406 uchar_t *fat_changemap = NULL; 1407 struct bootsec *bootp; 1408 struct fat32_bootsec *f32b; 1409 struct vnode *devvp; 1410 int error; 1411 int fatsize; 1412 int fat_changemapsize; 1413 int flags = 0; 1414 int nfat; 1415 int secsize; 1416 int fatsec; 1417 1418 PC_DPRINTF0(5, "pc_getfat\n"); 1419 devvp = fsp->pcfs_devvp; 1420 if (fsp->pcfs_fatp) { 1421 /* 1422 * There is a FAT in core. 1423 * If there are open file pcnodes or we have modified it or 1424 * it hasn't timed out yet use the in core FAT. 1425 * Otherwise invalidate it and get a new one 1426 */ 1427 #ifdef notdef 1428 if (fsp->pcfs_frefs || 1429 (fsp->pcfs_flags & PCFS_FATMOD) || 1430 (gethrestime_sec() < fsp->pcfs_fattime)) { 1431 return (0); 1432 } else { 1433 mutex_enter(&pcfslock); 1434 pc_invalfat(fsp); 1435 mutex_exit(&pcfslock); 1436 } 1437 #endif /* notdef */ 1438 return (0); 1439 } 1440 /* 1441 * Open block device mounted on. 1442 */ 1443 error = VOP_OPEN(&devvp, 1444 (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, 1445 CRED()); 1446 if (error) { 1447 PC_DPRINTF1(1, "pc_getfat: open error=%d\n", error); 1448 return (error); 1449 } 1450 /* 1451 * Get boot parameter block and check it for validity 1452 */ 1453 tp = bread(fsp->pcfs_xdev, fsp->pcfs_dosstart, PC_SAFESECSIZE); 1454 if (tp->b_flags & (B_ERROR | B_STALE)) { 1455 PC_DPRINTF0(1, "pc_getfat: boot block error\n"); 1456 flags = tp->b_flags & B_ERROR; 1457 error = EIO; 1458 goto out; 1459 } 1460 tp->b_flags |= B_STALE | B_AGE; 1461 bootp = (struct bootsec *)tp->b_un.b_addr; 1462 1463 /* get the sector size - may be more than 512 bytes */ 1464 secsize = (int)ltohs(bootp->bps[0]); 1465 /* check for bogus sector size - fat should be at least 1 sector */ 1466 if (IS_FAT32(fsp)) { 1467 f32b = (struct fat32_bootsec *)bootp; 1468 fatsec = ltohi(f32b->f_fatlength); 1469 } else { 1470 fatsec = ltohs(bootp->fatsec); 1471 } 1472 if (secsize < 512 || fatsec < 1 || bootp->nfat < 1) { 1473 cmn_err(CE_NOTE, "!pcfs: FAT size error"); 1474 error = EINVAL; 1475 goto out; 1476 } 1477 1478 switch (bootp->mediadesriptor) { 1479 default: 1480 cmn_err(CE_NOTE, "!pcfs: media-descriptor error, 0x%x", 1481 bootp->mediadesriptor); 1482 error = EINVAL; 1483 goto out; 1484 1485 case MD_FIXED: 1486 /* 1487 * PCMCIA psuedo floppy is type MD_FIXED, 1488 * but is accessed like a floppy 1489 */ 1490 if (!(fsp->pcfs_flags & PCFS_PCMCIA_NO_CIS)) { 1491 fsp->pcfs_flags |= PCFS_NOCHK; 1492 } 1493 /* FALLTHRU */ 1494 case SS8SPT: 1495 case DS8SPT: 1496 case SS9SPT: 1497 case DS9SPT: 1498 case DS18SPT: 1499 case DS9_15SPT: 1500 fsp->pcfs_secsize = secsize; 1501 fsp->pcfs_sdshift = secsize / DEV_BSIZE - 1; 1502 fsp->pcfs_entps = secsize / sizeof (struct pcdir); 1503 fsp->pcfs_spcl = (int)bootp->spcl; 1504 fsp->pcfs_fatsec = fatsec; 1505 fsp->pcfs_spt = (int)ltohs(bootp->spt); 1506 fsp->pcfs_rdirsec = (int)ltohs(bootp->rdirents[0]) 1507 * sizeof (struct pcdir) / secsize; 1508 fsp->pcfs_clsize = fsp->pcfs_spcl * secsize; 1509 fsp->pcfs_fatstart = fsp->pcfs_dosstart + 1510 (daddr_t)ltohs(bootp->res_sec[0]); 1511 fsp->pcfs_rdirstart = fsp->pcfs_fatstart + 1512 (bootp->nfat * fsp->pcfs_fatsec); 1513 fsp->pcfs_datastart = fsp->pcfs_rdirstart + fsp->pcfs_rdirsec; 1514 if (IS_FAT32(fsp)) 1515 fsp->pcfs_rdirstart = ltohi(f32b->f_rootcluster); 1516 fsp->pcfs_ncluster = (((int)(ltohs(bootp->numsect[0]) ? 1517 ltohs(bootp->numsect[0]) : ltohi(bootp->totalsec))) - 1518 fsp->pcfs_datastart + fsp->pcfs_dosstart) / fsp->pcfs_spcl; 1519 fsp->pcfs_numfat = (int)bootp->nfat; 1520 fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER; 1521 break; 1522 } 1523 1524 /* 1525 * Get FAT and check it for validity 1526 */ 1527 fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize; 1528 fatp = kmem_alloc(fatsize, KM_SLEEP); 1529 error = pc_readfat(fsp, fatp, fsp->pcfs_fatstart, fatsize); 1530 if (error) { 1531 flags = B_ERROR; 1532 goto out; 1533 } 1534 fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1; 1535 fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP); 1536 1537 if (fatp[0] != bootp->mediadesriptor || 1538 fatp[1] != 0xFF || fatp[2] != 0xFF) { 1539 cmn_err(CE_NOTE, "!pcfs: FAT signature error"); 1540 error = EINVAL; 1541 goto out; 1542 } 1543 /* 1544 * Checking for fatsec and number of supported clusters, should 1545 * actually determine a FAT12/FAT media. See pc_getfattype(). 1546 * fatp[3] != 0xFF is necessary for FAT validity. 1547 */ 1548 if (fsp->pcfs_flags & PCFS_FAT16) { 1549 if ((fsp->pcfs_fatsec <= 12) && 1550 ((fatsize * 2 / 3) >= fsp->pcfs_ncluster)) { 1551 /* 1552 * We have a 12-bit FAT, rather than a 16-bit FAT. 1553 * Ignore what the fdisk table says. 1554 */ 1555 PC_DPRINTF0(2, "pc_getfattype: forcing 12-bit FAT\n"); 1556 fsp->pcfs_flags &= ~PCFS_FAT16; 1557 } else if (fatp[3] != 0xFF) { 1558 cmn_err(CE_NOTE, "!pcfs: FAT signature error"); 1559 error = EINVAL; 1560 goto out; 1561 } 1562 } 1563 /* 1564 * Sanity check our FAT is large enough for the 1565 * clusters we think we have. 1566 */ 1567 if ((fsp->pcfs_flags & PCFS_FAT16) && 1568 ((fatsize / 2) < fsp->pcfs_ncluster)) { 1569 cmn_err(CE_NOTE, "!pcfs: FAT too small for number of clusters"); 1570 error = EINVAL; 1571 goto out; 1572 } 1573 1574 /* 1575 * Get alternate FATs and check for consistency 1576 * This is an inlined version of pc_readfat(). 1577 * Since we're only comparing FAT and alternate FAT, 1578 * there's no reason to let pc_readfat() copy data out 1579 * of the buf. Instead, compare in-situ, one cluster 1580 * at a time. 1581 */ 1582 for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) { 1583 size_t startsec; 1584 size_t off; 1585 1586 startsec = fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec; 1587 1588 for (off = 0; off < fatsize; off += fsp->pcfs_clsize) { 1589 bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, 1590 startsec + 1591 pc_cltodb(fsp, pc_lblkno(fsp, off))), 1592 MIN(fsp->pcfs_clsize, fatsize - off)); 1593 if (bp->b_flags & (B_ERROR | B_STALE)) { 1594 cmn_err(CE_NOTE, 1595 "!pcfs: alternate FAT #%d read error" 1596 " at byte %ld", nfat, off); 1597 flags = B_ERROR; 1598 error = EIO; 1599 goto out; 1600 } 1601 bp->b_flags |= B_STALE | B_AGE; 1602 if (bcmp(bp->b_un.b_addr, 1603 fatp + off, 1604 MIN(fsp->pcfs_clsize, fatsize - off))) { 1605 cmn_err(CE_NOTE, 1606 "!pcfs: alternate FAT #%d corrupted" 1607 " at byte %ld", nfat, off); 1608 flags = B_ERROR; 1609 } 1610 brelse(bp); 1611 bp = NULL; /* prevent double release */ 1612 } 1613 } 1614 1615 fsp->pcfs_fatsize = fatsize; 1616 fsp->pcfs_fatp = fatp; 1617 fsp->pcfs_fat_changemapsize = fat_changemapsize; 1618 fsp->pcfs_fat_changemap = fat_changemap; 1619 fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT; 1620 fsp->pcfs_fatjustread = 1; 1621 1622 brelse(tp); 1623 tp = NULL; 1624 if (IS_FAT32(fsp)) { 1625 /* get fsinfo */ 1626 struct fat32_boot_fsinfo fsinfo_disk; 1627 1628 fsp->f32fsinfo_sector = ltohs(f32b->f_infosector); 1629 tp = bread(fsp->pcfs_xdev, 1630 fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector), 1631 PC_SAFESECSIZE); 1632 if (tp->b_flags & (B_ERROR | B_STALE)) { 1633 cmn_err(CE_NOTE, "!pcfs: error reading fat32 fsinfo"); 1634 flags = tp->b_flags & B_ERROR; 1635 brelse(tp); 1636 tp = NULL; 1637 error = EIO; 1638 goto out; 1639 } 1640 tp->b_flags |= B_STALE | B_AGE; 1641 bcopy((void *)(tp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF), 1642 &fsinfo_disk, sizeof (struct fat32_boot_fsinfo)); 1643 brelse(tp); 1644 tp = NULL; 1645 1646 /* translated fields */ 1647 fsp->fsinfo_native.fs_signature = 1648 ltohi(fsinfo_disk.fs_signature); 1649 fsp->fsinfo_native.fs_free_clusters = 1650 ltohi(fsinfo_disk.fs_free_clusters); 1651 if (fsp->fsinfo_native.fs_signature != FAT32_FS_SIGN) { 1652 cmn_err(CE_NOTE, 1653 "!pcfs: fat32 fsinfo signature mismatch."); 1654 error = EINVAL; 1655 goto out; 1656 } 1657 } 1658 1659 return (0); 1660 1661 out: 1662 cmn_err(CE_NOTE, "!pcfs: illegal disk format"); 1663 if (tp) 1664 brelse(tp); 1665 if (bp) 1666 brelse(bp); 1667 if (fatp) 1668 kmem_free(fatp, fatsize); 1669 if (fat_changemap) 1670 kmem_free(fat_changemap, fat_changemapsize); 1671 1672 if (flags) { 1673 pc_mark_irrecov(fsp); 1674 } 1675 (void) VOP_CLOSE(devvp, (vfsp->vfs_flag & VFS_RDONLY) ? 1676 FREAD : FREAD|FWRITE, 1, (offset_t)0, CRED()); 1677 return (error); 1678 } 1679 1680 int 1681 pc_syncfat(struct pcfs *fsp) 1682 { 1683 struct buf *bp; 1684 int nfat; 1685 int error; 1686 struct fat32_boot_fsinfo fsinfo_disk; 1687 1688 PC_DPRINTF0(7, "pcfs_syncfat\n"); 1689 if ((fsp->pcfs_fatp == (uchar_t *)0) || 1690 !(fsp->pcfs_flags & PCFS_FATMOD)) 1691 return (0); 1692 /* 1693 * write out all copies of FATs 1694 */ 1695 fsp->pcfs_flags &= ~PCFS_FATMOD; 1696 fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT; 1697 for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) { 1698 error = pc_writefat(fsp, 1699 fsp->pcfs_fatstart + nfat*fsp->pcfs_fatsec); 1700 if (error) { 1701 pc_mark_irrecov(fsp); 1702 return (EIO); 1703 } 1704 } 1705 pc_clear_fatchanges(fsp); 1706 PC_DPRINTF0(6, "pcfs_syncfat: wrote out FAT\n"); 1707 /* write out fsinfo */ 1708 if (IS_FAT32(fsp)) { 1709 bp = bread(fsp->pcfs_xdev, 1710 fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector), 1711 PC_SAFESECSIZE); 1712 if (bp->b_flags & (B_ERROR | B_STALE)) { 1713 brelse(bp); 1714 return (EIO); 1715 } 1716 bcopy((void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF), 1717 &fsinfo_disk, sizeof (struct fat32_boot_fsinfo)); 1718 /* translate fields */ 1719 fsinfo_disk.fs_free_clusters = 1720 htoli(fsp->fsinfo_native.fs_free_clusters); 1721 fsinfo_disk.fs_next_cluster = (uint32_t)FSINFO_UNKNOWN; 1722 bcopy(&fsinfo_disk, 1723 (void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF), 1724 sizeof (struct fat32_boot_fsinfo)); 1725 bwrite2(bp); 1726 error = geterror(bp); 1727 brelse(bp); 1728 if (error) { 1729 pc_mark_irrecov(fsp); 1730 return (EIO); 1731 } 1732 } 1733 return (0); 1734 } 1735 1736 void 1737 pc_invalfat(struct pcfs *fsp) 1738 { 1739 struct pcfs *xfsp; 1740 int mount_cnt = 0; 1741 1742 PC_DPRINTF0(7, "pc_invalfat\n"); 1743 if (fsp->pcfs_fatp == (uchar_t *)0) 1744 panic("pc_invalfat"); 1745 /* 1746 * Release FAT 1747 */ 1748 kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsize); 1749 fsp->pcfs_fatp = NULL; 1750 kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize); 1751 fsp->pcfs_fat_changemap = NULL; 1752 /* 1753 * Invalidate all the blocks associated with the device. 1754 * Not needed if stateless. 1755 */ 1756 for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt) 1757 if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev) 1758 mount_cnt++; 1759 1760 if (!mount_cnt) 1761 binval(fsp->pcfs_xdev); 1762 /* 1763 * close mounted device 1764 */ 1765 (void) VOP_CLOSE(fsp->pcfs_devvp, 1766 (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, 1767 1, (offset_t)0, CRED()); 1768 } 1769 1770 void 1771 pc_badfs(struct pcfs *fsp) 1772 { 1773 cmn_err(CE_WARN, "corrupted PC file system on dev %x.%x\n", 1774 getmajor(fsp->pcfs_devvp->v_rdev), 1775 getminor(fsp->pcfs_devvp->v_rdev)); 1776 } 1777 1778 /* 1779 * The problem with supporting NFS on the PCFS filesystem is that there 1780 * is no good place to keep the generation number. The only possible 1781 * place is inside a directory entry. There are a few words that we 1782 * don't use - they store NT & OS/2 attributes, and the creation/last access 1783 * time of the file - but it seems wrong to use them. In addition, directory 1784 * entries come and go. If a directory is removed completely, its directory 1785 * blocks are freed and the generation numbers are lost. Whereas in ufs, 1786 * inode blocks are dedicated for inodes, so the generation numbers are 1787 * permanently kept on the disk. 1788 */ 1789 static int 1790 pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp) 1791 { 1792 struct pcnode *pcp; 1793 struct pc_fid *pcfid; 1794 struct pcfs *fsp; 1795 struct pcdir *ep; 1796 daddr_t eblkno; 1797 int eoffset; 1798 struct buf *bp; 1799 int error; 1800 pc_cluster32_t cn; 1801 1802 pcfid = (struct pc_fid *)fidp; 1803 fsp = VFSTOPCFS(vfsp); 1804 1805 error = pc_lockfs(fsp, 0, 0); 1806 if (error) { 1807 *vpp = NULL; 1808 return (error); 1809 } 1810 1811 if (pcfid->pcfid_block == 0) { 1812 pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0); 1813 pcp->pc_flags |= PC_EXTERNAL; 1814 *vpp = PCTOV(pcp); 1815 pc_unlockfs(fsp); 1816 return (0); 1817 } 1818 eblkno = pcfid->pcfid_block; 1819 eoffset = pcfid->pcfid_offset; 1820 if ((pc_dbtocl(fsp, 1821 eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) || 1822 (eoffset > fsp->pcfs_clsize)) { 1823 pc_unlockfs(fsp); 1824 *vpp = NULL; 1825 return (EINVAL); 1826 } 1827 1828 if (eblkno >= fsp->pcfs_datastart || (eblkno-fsp->pcfs_rdirstart) 1829 < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) { 1830 bp = bread(fsp->pcfs_xdev, eblkno, fsp->pcfs_clsize); 1831 } else { 1832 bp = bread(fsp->pcfs_xdev, eblkno, 1833 (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize); 1834 } 1835 if (bp->b_flags & (B_ERROR | B_STALE)) { 1836 error = geterror(bp); 1837 brelse(bp); 1838 if (error) 1839 pc_mark_irrecov(fsp); 1840 *vpp = NULL; 1841 pc_unlockfs(fsp); 1842 return (error); 1843 } 1844 ep = (struct pcdir *)(bp->b_un.b_addr + eoffset); 1845 /* 1846 * Ok, if this is a valid file handle that we gave out, 1847 * then simply ensuring that the creation time matches, 1848 * the entry has not been deleted, and it has a valid first 1849 * character should be enough. 1850 * 1851 * Unfortunately, verifying that the <blkno, offset> _still_ 1852 * refers to a directory entry is not easy, since we'd have 1853 * to search _all_ directories starting from root to find it. 1854 * That's a high price to pay just in case somebody is forging 1855 * file handles. So instead we verify that as much of the 1856 * entry is valid as we can: 1857 * 1858 * 1. The starting cluster is 0 (unallocated) or valid 1859 * 2. It is not an LFN entry 1860 * 3. It is not hidden (unless mounted as such) 1861 * 4. It is not the label 1862 */ 1863 cn = pc_getstartcluster(fsp, ep); 1864 /* 1865 * if the starting cluster is valid, but not valid according 1866 * to pc_validcl(), force it to be to simplify the following if. 1867 */ 1868 if (cn == 0) 1869 cn = PCF_FIRSTCLUSTER; 1870 if (IS_FAT32(fsp)) { 1871 if (cn >= PCF_LASTCLUSTER32) 1872 cn = PCF_FIRSTCLUSTER; 1873 } else { 1874 if (cn >= PCF_LASTCLUSTER) 1875 cn = PCF_FIRSTCLUSTER; 1876 } 1877 if ((!pc_validcl(fsp, cn)) || 1878 (PCDL_IS_LFN(ep)) || 1879 (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) || 1880 ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) { 1881 bp->b_flags |= B_STALE | B_AGE; 1882 brelse(bp); 1883 pc_unlockfs(fsp); 1884 return (EINVAL); 1885 } 1886 if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) && 1887 (ep->pcd_filename[0] != PCD_ERASED) && 1888 (pc_validchar(ep->pcd_filename[0]) || 1889 (ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) { 1890 pcp = pc_getnode(fsp, eblkno, eoffset, ep); 1891 pcp->pc_flags |= PC_EXTERNAL; 1892 *vpp = PCTOV(pcp); 1893 } else { 1894 *vpp = NULL; 1895 } 1896 bp->b_flags |= B_STALE | B_AGE; 1897 brelse(bp); 1898 pc_unlockfs(fsp); 1899 return (0); 1900 } 1901 1902 /* 1903 * if device is a PCMCIA psuedo floppy, return 1 1904 * otherwise, return 0 1905 */ 1906 static int 1907 pcfs_psuedo_floppy(dev_t rdev) 1908 { 1909 int error, err; 1910 struct dk_cinfo info; 1911 ldi_handle_t lh; 1912 ldi_ident_t li; 1913 1914 err = ldi_ident_from_mod(&modlinkage, &li); 1915 if (err) { 1916 PC_DPRINTF1(1, 1917 "pcfs_psuedo_floppy: ldi_ident_from_mod err=%d\n", err); 1918 return (0); 1919 } 1920 1921 err = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, CRED(), &lh, li); 1922 ldi_ident_release(li); 1923 if (err) { 1924 PC_DPRINTF1(1, 1925 "pcfs_psuedo_floppy: ldi_open err=%d\n", err); 1926 return (0); 1927 } 1928 1929 /* return value stored in err is purposfully ignored */ 1930 error = ldi_ioctl(lh, DKIOCINFO, (intptr_t)&info, FKIOCTL, 1931 CRED(), &err); 1932 1933 err = ldi_close(lh, FREAD, CRED()); 1934 if (err != 0) { 1935 PC_DPRINTF1(1, 1936 "pcfs_psuedo_floppy: ldi_close err=%d\n", err); 1937 return (0); 1938 } 1939 1940 if ((error == 0) && (info.dki_ctype == DKC_PCMCIA_MEM) && 1941 (info.dki_flags & DKI_PCMCIA_PFD)) 1942 return (1); 1943 else 1944 return (0); 1945 } 1946 1947 /* 1948 * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about 1949 * a meg), so we can't bread() it all in at once. This routine reads a 1950 * fat a chunk at a time. 1951 */ 1952 static int 1953 pc_readfat(struct pcfs *fsp, uchar_t *fatp, daddr_t start, size_t fatsize) 1954 { 1955 struct buf *bp; 1956 size_t off; 1957 size_t readsize; 1958 1959 readsize = fsp->pcfs_clsize; 1960 for (off = 0; off < fatsize; off += readsize, fatp += readsize) { 1961 if (readsize > (fatsize - off)) 1962 readsize = fatsize - off; 1963 bp = bread(fsp->pcfs_xdev, 1964 pc_dbdaddr(fsp, start + 1965 pc_cltodb(fsp, pc_lblkno(fsp, off))), 1966 readsize); 1967 if (bp->b_flags & (B_ERROR | B_STALE)) { 1968 brelse(bp); 1969 return (EIO); 1970 } 1971 bp->b_flags |= B_STALE | B_AGE; 1972 bcopy(bp->b_un.b_addr, fatp, readsize); 1973 brelse(bp); 1974 } 1975 return (0); 1976 } 1977 1978 /* 1979 * We write the FAT out a _lot_, in order to make sure that it 1980 * is up-to-date. But on a FAT32 system (large drive, small clusters) 1981 * the FAT might be a couple of megabytes, and writing it all out just 1982 * because we created or deleted a small file is painful (especially 1983 * since we do it for each alternate FAT too). So instead, for FAT16 and 1984 * FAT32 we only write out the bit that has changed. We don't clear 1985 * the 'updated' fields here because the caller might be writing out 1986 * several FATs, so the caller must use pc_clear_fatchanges() after 1987 * all FATs have been updated. 1988 */ 1989 static int 1990 pc_writefat(struct pcfs *fsp, daddr_t start) 1991 { 1992 struct buf *bp; 1993 size_t off; 1994 size_t writesize; 1995 int error; 1996 uchar_t *fatp = fsp->pcfs_fatp; 1997 size_t fatsize = fsp->pcfs_fatsize; 1998 1999 writesize = fsp->pcfs_clsize; 2000 for (off = 0; off < fatsize; off += writesize, fatp += writesize) { 2001 if (writesize > (fatsize - off)) 2002 writesize = fatsize - off; 2003 if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) { 2004 continue; 2005 } 2006 bp = ngeteblk(writesize); 2007 bp->b_edev = fsp->pcfs_xdev; 2008 bp->b_dev = cmpdev(bp->b_edev); 2009 bp->b_blkno = pc_dbdaddr(fsp, start + 2010 pc_cltodb(fsp, pc_lblkno(fsp, off))); 2011 bcopy(fatp, bp->b_un.b_addr, writesize); 2012 bwrite2(bp); 2013 error = geterror(bp); 2014 brelse(bp); 2015 if (error) { 2016 return (error); 2017 } 2018 } 2019 return (0); 2020 } 2021 2022 /* 2023 * Mark the FAT cluster that 'cn' is stored in as modified. 2024 */ 2025 void 2026 pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn) 2027 { 2028 pc_cluster32_t bn; 2029 size_t size; 2030 2031 /* which fat block is the cluster number stored in? */ 2032 if (IS_FAT32(fsp)) { 2033 size = sizeof (pc_cluster32_t); 2034 bn = pc_lblkno(fsp, cn * size); 2035 fsp->pcfs_fat_changemap[bn] = 1; 2036 } else if (IS_FAT16(fsp)) { 2037 size = sizeof (pc_cluster16_t); 2038 bn = pc_lblkno(fsp, cn * size); 2039 fsp->pcfs_fat_changemap[bn] = 1; 2040 } else { 2041 offset_t off; 2042 pc_cluster32_t nbn; 2043 2044 ASSERT(IS_FAT12(fsp)); 2045 off = cn + (cn >> 1); 2046 bn = pc_lblkno(fsp, off); 2047 fsp->pcfs_fat_changemap[bn] = 1; 2048 /* does this field wrap into the next fat cluster? */ 2049 nbn = pc_lblkno(fsp, off + 1); 2050 if (nbn != bn) { 2051 fsp->pcfs_fat_changemap[nbn] = 1; 2052 } 2053 } 2054 } 2055 2056 /* 2057 * return whether the FAT cluster 'bn' is updated and needs to 2058 * be written out. 2059 */ 2060 int 2061 pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn) 2062 { 2063 return (fsp->pcfs_fat_changemap[bn] == 1); 2064 } 2065