1 /* $NetBSD: msdosfs_vfsops.c,v 1.61 2008/01/30 11:47:00 ad Exp $ */ 2 3 /*- 4 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 5 * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 6 * All rights reserved. 7 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by TooLs GmbH. 20 * 4. The name of TooLs GmbH may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 29 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 31 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 32 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 /* 35 * Written by Paul Popelka (paulp@uts.amdahl.com) 36 * 37 * You can do anything you want with this software, just don't say you wrote 38 * it, and don't remove this notice. 39 * 40 * This software is provided "as is". 41 * 42 * The author supplies this software to be publicly redistributed on the 43 * understanding that the author is not responsible for the correct 44 * functioning of this software in any circumstances and is not liable for 45 * any damages caused by this software. 46 * 47 * October 1992 48 */ 49 50 #include <sys/cdefs.h> 51 __KERNEL_RCSID(0, "$NetBSD: msdosfs_vfsops.c,v 1.61 2008/01/30 11:47:00 ad Exp $"); 52 53 #if defined(_KERNEL_OPT) 54 #include "opt_quota.h" 55 #include "opt_compat_netbsd.h" 56 #endif 57 58 #include <sys/param.h> 59 #include <sys/systm.h> 60 #include <sys/sysctl.h> 61 #include <sys/namei.h> 62 #include <sys/proc.h> 63 #include <sys/kernel.h> 64 #include <sys/vnode.h> 65 #include <miscfs/genfs/genfs.h> 66 #include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */ 67 #include <sys/mount.h> 68 #include <sys/buf.h> 69 #include <sys/file.h> 70 #include <sys/device.h> 71 #include <sys/disklabel.h> 72 #include <sys/disk.h> 73 #include <sys/ioctl.h> 74 #include <sys/malloc.h> 75 #include <sys/dirent.h> 76 #include <sys/stat.h> 77 #include <sys/conf.h> 78 #include <sys/kauth.h> 79 80 #include <fs/msdosfs/bpb.h> 81 #include <fs/msdosfs/bootsect.h> 82 #include <fs/msdosfs/direntry.h> 83 #include <fs/msdosfs/denode.h> 84 #include <fs/msdosfs/msdosfsmount.h> 85 #include <fs/msdosfs/fat.h> 86 87 #ifdef MSDOSFS_DEBUG 88 #define DPRINTF(a) uprintf a 89 #else 90 #define DPRINTF(a) 91 #endif 92 93 #define MSDOSFS_NAMEMAX(pmp) \ 94 (pmp)->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12 95 96 VFS_PROTOS(msdosfs); 97 98 int msdosfs_mountfs(struct vnode *, struct mount *, struct lwp *, 99 struct msdosfs_args *); 100 101 static int update_mp(struct mount *, struct msdosfs_args *); 102 103 MALLOC_JUSTDEFINE(M_MSDOSFSMNT, "MSDOSFS mount", "MSDOS FS mount structure"); 104 MALLOC_JUSTDEFINE(M_MSDOSFSFAT, "MSDOSFS fat", "MSDOS FS fat table"); 105 MALLOC_JUSTDEFINE(M_MSDOSFSTMP, "MSDOSFS temp", "MSDOS FS temp. structures"); 106 107 #define ROOTNAME "root_device" 108 109 extern const struct vnodeopv_desc msdosfs_vnodeop_opv_desc; 110 111 const struct vnodeopv_desc * const msdosfs_vnodeopv_descs[] = { 112 &msdosfs_vnodeop_opv_desc, 113 NULL, 114 }; 115 116 struct vfsops msdosfs_vfsops = { 117 MOUNT_MSDOS, 118 sizeof (struct msdosfs_args), 119 msdosfs_mount, 120 msdosfs_start, 121 msdosfs_unmount, 122 msdosfs_root, 123 (void *)eopnotsupp, /* vfs_quotactl */ 124 msdosfs_statvfs, 125 msdosfs_sync, 126 msdosfs_vget, 127 msdosfs_fhtovp, 128 msdosfs_vptofh, 129 msdosfs_init, 130 msdosfs_reinit, 131 msdosfs_done, 132 msdosfs_mountroot, 133 (int (*)(struct mount *, struct vnode *, struct timespec *)) eopnotsupp, 134 vfs_stdextattrctl, 135 (void *)eopnotsupp, /* vfs_suspendctl */ 136 genfs_renamelock_enter, 137 genfs_renamelock_exit, 138 msdosfs_vnodeopv_descs, 139 0, 140 { NULL, NULL }, 141 }; 142 VFS_ATTACH(msdosfs_vfsops); 143 144 static int 145 update_mp(mp, argp) 146 struct mount *mp; 147 struct msdosfs_args *argp; 148 { 149 struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 150 int error; 151 152 pmp->pm_gid = argp->gid; 153 pmp->pm_uid = argp->uid; 154 pmp->pm_mask = argp->mask & ALLPERMS; 155 pmp->pm_dirmask = argp->dirmask & ALLPERMS; 156 pmp->pm_gmtoff = argp->gmtoff; 157 pmp->pm_flags |= argp->flags & MSDOSFSMNT_MNTOPT; 158 159 /* 160 * GEMDOS knows nothing (yet) about win95 161 */ 162 if (pmp->pm_flags & MSDOSFSMNT_GEMDOSFS) 163 pmp->pm_flags |= MSDOSFSMNT_NOWIN95; 164 165 if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) 166 pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; 167 else if (!(pmp->pm_flags & 168 (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME))) { 169 struct vnode *rtvp; 170 171 /* 172 * Try to divine whether to support Win'95 long filenames 173 */ 174 if (FAT32(pmp)) 175 pmp->pm_flags |= MSDOSFSMNT_LONGNAME; 176 else { 177 if ((error = msdosfs_root(mp, &rtvp)) != 0) 178 return error; 179 pmp->pm_flags |= findwin95(VTODE(rtvp)) 180 ? MSDOSFSMNT_LONGNAME 181 : MSDOSFSMNT_SHORTNAME; 182 vput(rtvp); 183 } 184 } 185 186 mp->mnt_stat.f_namemax = MSDOSFS_NAMEMAX(pmp); 187 188 return 0; 189 } 190 191 int 192 msdosfs_mountroot() 193 { 194 struct mount *mp; 195 struct lwp *l = curlwp; /* XXX */ 196 int error; 197 struct msdosfs_args args; 198 199 if (device_class(root_device) != DV_DISK) 200 return (ENODEV); 201 202 if ((error = vfs_rootmountalloc(MOUNT_MSDOS, "root_device", &mp))) { 203 vrele(rootvp); 204 return (error); 205 } 206 207 args.flags = MSDOSFSMNT_VERSIONED; 208 args.uid = 0; 209 args.gid = 0; 210 args.mask = 0777; 211 args.version = MSDOSFSMNT_VERSION; 212 args.dirmask = 0777; 213 214 if ((error = msdosfs_mountfs(rootvp, mp, l, &args)) != 0) { 215 vfs_unbusy(mp, false); 216 vfs_destroy(mp); 217 return (error); 218 } 219 220 if ((error = update_mp(mp, &args)) != 0) { 221 (void)msdosfs_unmount(mp, 0); 222 vfs_unbusy(mp, false); 223 vfs_destroy(mp); 224 vrele(rootvp); 225 return (error); 226 } 227 228 mutex_enter(&mountlist_lock); 229 CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list); 230 mutex_exit(&mountlist_lock); 231 (void)msdosfs_statvfs(mp, &mp->mnt_stat); 232 vfs_unbusy(mp, false); 233 return (0); 234 } 235 236 /* 237 * mp - path - addr in user space of mount point (ie /usr or whatever) 238 * data - addr in user space of mount params including the name of the block 239 * special file to treat as a filesystem. 240 */ 241 int 242 msdosfs_mount(mp, path, data, data_len) 243 struct mount *mp; 244 const char *path; 245 void *data; 246 size_t *data_len; 247 { 248 struct lwp *l = curlwp; 249 struct nameidata nd; 250 struct vnode *devvp; /* vnode for blk device to mount */ 251 struct msdosfs_args *args = data; /* holds data from mount request */ 252 /* msdosfs specific mount control block */ 253 struct msdosfsmount *pmp = NULL; 254 int error, flags; 255 mode_t accessmode; 256 257 if (*data_len < sizeof *args) 258 return EINVAL; 259 260 if (mp->mnt_flag & MNT_GETARGS) { 261 pmp = VFSTOMSDOSFS(mp); 262 if (pmp == NULL) 263 return EIO; 264 args->fspec = NULL; 265 args->uid = pmp->pm_uid; 266 args->gid = pmp->pm_gid; 267 args->mask = pmp->pm_mask; 268 args->flags = pmp->pm_flags; 269 args->version = MSDOSFSMNT_VERSION; 270 args->dirmask = pmp->pm_dirmask; 271 args->gmtoff = pmp->pm_gmtoff; 272 *data_len = sizeof *args; 273 return 0; 274 } 275 276 /* 277 * If not versioned (i.e. using old mount_msdos(8)), fill in 278 * the additional structure items with suitable defaults. 279 */ 280 if ((args->flags & MSDOSFSMNT_VERSIONED) == 0) { 281 args->version = 1; 282 args->dirmask = args->mask; 283 } 284 285 /* 286 * Reset GMT offset for pre-v3 mount structure args. 287 */ 288 if (args->version < 3) 289 args->gmtoff = 0; 290 291 /* 292 * If updating, check whether changing from read-only to 293 * read/write; if there is no device name, that's all we do. 294 */ 295 if (mp->mnt_flag & MNT_UPDATE) { 296 pmp = VFSTOMSDOSFS(mp); 297 error = 0; 298 if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_flag & MNT_RDONLY)) { 299 flags = WRITECLOSE; 300 if (mp->mnt_flag & MNT_FORCE) 301 flags |= FORCECLOSE; 302 error = vflush(mp, NULLVP, flags); 303 } 304 if (!error && (mp->mnt_flag & MNT_RELOAD)) 305 /* not yet implemented */ 306 error = EOPNOTSUPP; 307 if (error) { 308 DPRINTF(("vflush %d\n", error)); 309 return (error); 310 } 311 if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_iflag & IMNT_WANTRDWR)) { 312 /* 313 * If upgrade to read-write by non-root, then verify 314 * that user has necessary permissions on the device. 315 */ 316 if (kauth_authorize_generic(l->l_cred, 317 KAUTH_GENERIC_ISSUSER, NULL) != 0) { 318 devvp = pmp->pm_devvp; 319 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 320 error = VOP_ACCESS(devvp, VREAD | VWRITE, 321 l->l_cred); 322 VOP_UNLOCK(devvp, 0); 323 DPRINTF(("VOP_ACCESS %d\n", error)); 324 if (error) 325 return (error); 326 } 327 pmp->pm_flags &= ~MSDOSFSMNT_RONLY; 328 } 329 if (args->fspec == NULL) { 330 DPRINTF(("missing fspec\n")); 331 return EINVAL; 332 } 333 } 334 /* 335 * Not an update, or updating the name: look up the name 336 * and verify that it refers to a sensible block device. 337 */ 338 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, args->fspec); 339 if ((error = namei(&nd)) != 0) { 340 DPRINTF(("namei %d\n", error)); 341 return (error); 342 } 343 devvp = nd.ni_vp; 344 345 if (devvp->v_type != VBLK) { 346 DPRINTF(("not block\n")); 347 vrele(devvp); 348 return (ENOTBLK); 349 } 350 if (bdevsw_lookup(devvp->v_rdev) == NULL) { 351 DPRINTF(("no block switch\n")); 352 vrele(devvp); 353 return (ENXIO); 354 } 355 /* 356 * If mount by non-root, then verify that user has necessary 357 * permissions on the device. 358 */ 359 if (kauth_authorize_generic(l->l_cred, KAUTH_GENERIC_ISSUSER, NULL) != 0) { 360 accessmode = VREAD; 361 if ((mp->mnt_flag & MNT_RDONLY) == 0) 362 accessmode |= VWRITE; 363 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 364 error = VOP_ACCESS(devvp, accessmode, l->l_cred); 365 VOP_UNLOCK(devvp, 0); 366 if (error) { 367 DPRINTF(("VOP_ACCESS2 %d\n", error)); 368 vrele(devvp); 369 return (error); 370 } 371 } 372 if ((mp->mnt_flag & MNT_UPDATE) == 0) { 373 int xflags; 374 375 if (mp->mnt_flag & MNT_RDONLY) 376 xflags = FREAD; 377 else 378 xflags = FREAD|FWRITE; 379 error = VOP_OPEN(devvp, xflags, FSCRED); 380 if (error) { 381 DPRINTF(("VOP_OPEN %d\n", error)); 382 goto fail; 383 } 384 error = msdosfs_mountfs(devvp, mp, l, args); 385 if (error) { 386 DPRINTF(("msdosfs_mountfs %d\n", error)); 387 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 388 (void) VOP_CLOSE(devvp, xflags, NOCRED); 389 VOP_UNLOCK(devvp, 0); 390 goto fail; 391 } 392 #ifdef MSDOSFS_DEBUG /* only needed for the printf below */ 393 pmp = VFSTOMSDOSFS(mp); 394 #endif 395 } else { 396 vrele(devvp); 397 if (devvp != pmp->pm_devvp) { 398 DPRINTF(("devvp %p pmp %p\n", 399 devvp, pmp->pm_devvp)); 400 return (EINVAL); /* needs translation */ 401 } 402 } 403 if ((error = update_mp(mp, args)) != 0) { 404 msdosfs_unmount(mp, MNT_FORCE); 405 DPRINTF(("update_mp %d\n", error)); 406 return error; 407 } 408 409 #ifdef MSDOSFS_DEBUG 410 printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap); 411 #endif 412 return set_statvfs_info(path, UIO_USERSPACE, args->fspec, UIO_USERSPACE, 413 mp->mnt_op->vfs_name, mp, l); 414 415 fail: 416 vrele(devvp); 417 return (error); 418 } 419 420 int 421 msdosfs_mountfs(devvp, mp, l, argp) 422 struct vnode *devvp; 423 struct mount *mp; 424 struct lwp *l; 425 struct msdosfs_args *argp; 426 { 427 struct msdosfsmount *pmp; 428 struct buf *bp; 429 dev_t dev = devvp->v_rdev; 430 struct partinfo dpart; 431 union bootsector *bsp; 432 struct byte_bpb33 *b33; 433 struct byte_bpb50 *b50; 434 struct byte_bpb710 *b710; 435 u_int8_t SecPerClust; 436 int ronly, error, tmp; 437 int bsize, dtype, fstype, secsize; 438 u_int64_t psize; 439 440 /* Flush out any old buffers remaining from a previous use. */ 441 if ((error = vinvalbuf(devvp, V_SAVE, l->l_cred, l, 0, 0)) != 0) 442 return (error); 443 444 ronly = (mp->mnt_flag & MNT_RDONLY) != 0; 445 446 bp = NULL; /* both used in error_exit */ 447 pmp = NULL; 448 449 /* 450 * We need the disklabel to calculate the size of a FAT entry 451 * later on. Also make sure the partition contains a filesystem 452 * of type FS_MSDOS. This doesn't work for floppies, so we have 453 * to check for them too. 454 * 455 * There might still be parts of the msdos fs driver which assume 456 * that the size of a disk block will always be 512 bytes. 457 * Let's root them out... 458 */ 459 error = VOP_IOCTL(devvp, DIOCGPART, &dpart, FREAD, NOCRED); 460 if (error == 0) { 461 secsize = dpart.disklab->d_secsize; 462 dtype = dpart.disklab->d_type; 463 fstype = dpart.part->p_fstype; 464 psize = dpart.part->p_size; 465 } else { 466 struct dkwedge_info dkw; 467 error = VOP_IOCTL(devvp, DIOCGWEDGEINFO, &dkw, FREAD, NOCRED); 468 secsize = 512; /* XXX */ 469 dtype = DTYPE_FLOPPY; /* XXX */ 470 fstype = FS_MSDOS; 471 psize = -1; 472 if (error) { 473 if (error != ENOTTY) { 474 DPRINTF(("Error getting partition info %d\n", 475 error)); 476 goto error_exit; 477 } 478 } else { 479 fstype = strcmp(dkw.dkw_ptype, DKW_PTYPE_FAT) == 0 ? 480 FS_MSDOS : -1; 481 psize = dkw.dkw_size; 482 } 483 } 484 if (argp->flags & MSDOSFSMNT_GEMDOSFS) { 485 bsize = secsize; 486 if (bsize != 512 || 487 (dtype != DTYPE_FLOPPY && fstype != FS_MSDOS)) { 488 DPRINTF(("bsize %d dtype %d fstype %d\n", bsize, dtype, 489 fstype)); 490 error = EINVAL; 491 goto error_exit; 492 } 493 } else 494 bsize = 0; 495 496 /* 497 * Read the boot sector of the filesystem, and then check the 498 * boot signature. If not a dos boot sector then error out. 499 */ 500 if ((error = bread(devvp, 0, secsize, NOCRED, &bp)) != 0) 501 goto error_exit; 502 bsp = (union bootsector *)bp->b_data; 503 b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB; 504 b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; 505 b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB; 506 507 if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) { 508 if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 509 || bsp->bs50.bsBootSectSig1 != BOOTSIG1) { 510 DPRINTF(("bootsig0 %d bootsig1 %d\n", 511 bsp->bs50.bsBootSectSig0, 512 bsp->bs50.bsBootSectSig1)); 513 error = EINVAL; 514 goto error_exit; 515 } 516 } 517 518 pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK); 519 memset(pmp, 0, sizeof *pmp); 520 pmp->pm_mountp = mp; 521 522 /* 523 * Compute several useful quantities from the bpb in the 524 * bootsector. Copy in the dos 5 variant of the bpb then fix up 525 * the fields that are different between dos 5 and dos 3.3. 526 */ 527 SecPerClust = b50->bpbSecPerClust; 528 pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); 529 pmp->pm_ResSectors = getushort(b50->bpbResSectors); 530 pmp->pm_FATs = b50->bpbFATs; 531 pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); 532 pmp->pm_Sectors = getushort(b50->bpbSectors); 533 pmp->pm_FATsecs = getushort(b50->bpbFATsecs); 534 pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); 535 pmp->pm_Heads = getushort(b50->bpbHeads); 536 pmp->pm_Media = b50->bpbMedia; 537 538 if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) { 539 /* XXX - We should probably check more values here */ 540 if (!pmp->pm_BytesPerSec || !SecPerClust 541 || pmp->pm_Heads > 255 || pmp->pm_SecPerTrack > 63) { 542 DPRINTF(("bytespersec %d secperclust %d " 543 "heads %d secpertrack %d\n", 544 pmp->pm_BytesPerSec, SecPerClust, 545 pmp->pm_Heads, pmp->pm_SecPerTrack)); 546 error = EINVAL; 547 goto error_exit; 548 } 549 } 550 551 if (pmp->pm_Sectors == 0) { 552 pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); 553 pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); 554 } else { 555 pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); 556 pmp->pm_HugeSectors = pmp->pm_Sectors; 557 } 558 559 if (pmp->pm_RootDirEnts == 0) { 560 unsigned short vers = getushort(b710->bpbFSVers); 561 /* 562 * Some say that bsBootSectSig[23] must be zero, but 563 * Windows does not require this and some digital cameras 564 * do not set these to zero. Therefore, do not insist. 565 */ 566 if (pmp->pm_Sectors || pmp->pm_FATsecs || vers) { 567 DPRINTF(("sectors %d fatsecs %lu vers %d\n", 568 pmp->pm_Sectors, pmp->pm_FATsecs, vers)); 569 error = EINVAL; 570 goto error_exit; 571 } 572 pmp->pm_fatmask = FAT32_MASK; 573 pmp->pm_fatmult = 4; 574 pmp->pm_fatdiv = 1; 575 pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs); 576 577 /* mirrorring is enabled if the FATMIRROR bit is not set */ 578 if ((getushort(b710->bpbExtFlags) & FATMIRROR) == 0) 579 pmp->pm_flags |= MSDOSFS_FATMIRROR; 580 else 581 pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM; 582 } else 583 pmp->pm_flags |= MSDOSFS_FATMIRROR; 584 585 if (argp->flags & MSDOSFSMNT_GEMDOSFS) { 586 if (FAT32(pmp)) { 587 DPRINTF(("fat32 for gemdos\n")); 588 /* 589 * GEMDOS doesn't know fat32. 590 */ 591 error = EINVAL; 592 goto error_exit; 593 } 594 595 /* 596 * Check a few values (could do some more): 597 * - logical sector size: power of 2, >= block size 598 * - sectors per cluster: power of 2, >= 1 599 * - number of sectors: >= 1, <= size of partition 600 */ 601 if ( (SecPerClust == 0) 602 || (SecPerClust & (SecPerClust - 1)) 603 || (pmp->pm_BytesPerSec < bsize) 604 || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1)) 605 || (pmp->pm_HugeSectors == 0) 606 || (pmp->pm_HugeSectors * (pmp->pm_BytesPerSec / bsize) 607 > psize)) { 608 DPRINTF(("consistency checks for gemdos\n")); 609 error = EINVAL; 610 goto error_exit; 611 } 612 /* 613 * XXX - Many parts of the msdos fs driver seem to assume that 614 * the number of bytes per logical sector (BytesPerSec) will 615 * always be the same as the number of bytes per disk block 616 * Let's pretend it is. 617 */ 618 tmp = pmp->pm_BytesPerSec / bsize; 619 pmp->pm_BytesPerSec = bsize; 620 pmp->pm_HugeSectors *= tmp; 621 pmp->pm_HiddenSects *= tmp; 622 pmp->pm_ResSectors *= tmp; 623 pmp->pm_Sectors *= tmp; 624 pmp->pm_FATsecs *= tmp; 625 SecPerClust *= tmp; 626 } 627 628 /* Check that fs has nonzero FAT size */ 629 if (pmp->pm_FATsecs == 0) { 630 DPRINTF(("FATsecs is 0\n")); 631 error = EINVAL; 632 goto error_exit; 633 } 634 635 pmp->pm_fatblk = pmp->pm_ResSectors; 636 if (FAT32(pmp)) { 637 pmp->pm_rootdirblk = getulong(b710->bpbRootClust); 638 pmp->pm_firstcluster = pmp->pm_fatblk 639 + (pmp->pm_FATs * pmp->pm_FATsecs); 640 pmp->pm_fsinfo = getushort(b710->bpbFSInfo); 641 } else { 642 pmp->pm_rootdirblk = pmp->pm_fatblk + 643 (pmp->pm_FATs * pmp->pm_FATsecs); 644 pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry) 645 + pmp->pm_BytesPerSec - 1) 646 / pmp->pm_BytesPerSec;/* in sectors */ 647 pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; 648 } 649 650 pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / 651 SecPerClust; 652 pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1; 653 pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec; 654 655 if (argp->flags & MSDOSFSMNT_GEMDOSFS) { 656 if (pmp->pm_nmbrofclusters <= (0xff0 - 2) 657 && (dtype == DTYPE_FLOPPY 658 || (dtype == DTYPE_VND 659 && (pmp->pm_Heads == 1 || pmp->pm_Heads == 2))) 660 ) { 661 pmp->pm_fatmask = FAT12_MASK; 662 pmp->pm_fatmult = 3; 663 pmp->pm_fatdiv = 2; 664 } else { 665 pmp->pm_fatmask = FAT16_MASK; 666 pmp->pm_fatmult = 2; 667 pmp->pm_fatdiv = 1; 668 } 669 } else if (pmp->pm_fatmask == 0) { 670 if (pmp->pm_maxcluster 671 <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) { 672 /* 673 * This will usually be a floppy disk. This size makes 674 * sure that one fat entry will not be split across 675 * multiple blocks. 676 */ 677 pmp->pm_fatmask = FAT12_MASK; 678 pmp->pm_fatmult = 3; 679 pmp->pm_fatdiv = 2; 680 } else { 681 pmp->pm_fatmask = FAT16_MASK; 682 pmp->pm_fatmult = 2; 683 pmp->pm_fatdiv = 1; 684 } 685 } 686 if (FAT12(pmp)) 687 pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec; 688 else 689 pmp->pm_fatblocksize = MAXBSIZE; 690 691 pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec; 692 pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1; 693 694 /* 695 * Compute mask and shift value for isolating cluster relative byte 696 * offsets and cluster numbers from a file offset. 697 */ 698 pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec; 699 pmp->pm_crbomask = pmp->pm_bpcluster - 1; 700 pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1; 701 702 /* 703 * Check for valid cluster size 704 * must be a power of 2 705 */ 706 if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) { 707 DPRINTF(("bpcluster %lu cnshift %lu\n", 708 pmp->pm_bpcluster, pmp->pm_cnshift)); 709 error = EINVAL; 710 goto error_exit; 711 } 712 713 /* 714 * Release the bootsector buffer. 715 */ 716 brelse(bp, BC_AGE); 717 bp = NULL; 718 719 /* 720 * Check FSInfo. 721 */ 722 if (pmp->pm_fsinfo) { 723 struct fsinfo *fp; 724 725 /* 726 * XXX If the fsinfo block is stored on media with 727 * 2KB or larger sectors, is the fsinfo structure 728 * padded at the end or in the middle? 729 */ 730 if ((error = bread(devvp, de_bn2kb(pmp, pmp->pm_fsinfo), 731 pmp->pm_BytesPerSec, NOCRED, &bp)) != 0) 732 goto error_exit; 733 fp = (struct fsinfo *)bp->b_data; 734 if (!memcmp(fp->fsisig1, "RRaA", 4) 735 && !memcmp(fp->fsisig2, "rrAa", 4) 736 && !memcmp(fp->fsisig3, "\0\0\125\252", 4) 737 && !memcmp(fp->fsisig4, "\0\0\125\252", 4)) 738 pmp->pm_nxtfree = getulong(fp->fsinxtfree); 739 else 740 pmp->pm_fsinfo = 0; 741 brelse(bp, 0); 742 bp = NULL; 743 } 744 745 /* 746 * Check and validate (or perhaps invalidate?) the fsinfo structure? 747 * XXX 748 */ 749 if (pmp->pm_fsinfo) { 750 if (pmp->pm_nxtfree == (u_long)-1) 751 pmp->pm_fsinfo = 0; 752 } 753 754 /* 755 * Allocate memory for the bitmap of allocated clusters, and then 756 * fill it in. 757 */ 758 pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS - 1) 759 / N_INUSEBITS) 760 * sizeof(*pmp->pm_inusemap), 761 M_MSDOSFSFAT, M_WAITOK); 762 763 /* 764 * fillinusemap() needs pm_devvp. 765 */ 766 pmp->pm_dev = dev; 767 pmp->pm_devvp = devvp; 768 769 /* 770 * Have the inuse map filled in. 771 */ 772 if ((error = fillinusemap(pmp)) != 0) { 773 DPRINTF(("fillinusemap %d\n", error)); 774 goto error_exit; 775 } 776 777 /* 778 * If they want fat updates to be synchronous then let them suffer 779 * the performance degradation in exchange for the on disk copy of 780 * the fat being correct just about all the time. I suppose this 781 * would be a good thing to turn on if the kernel is still flakey. 782 */ 783 if (mp->mnt_flag & MNT_SYNCHRONOUS) 784 pmp->pm_flags |= MSDOSFSMNT_WAITONFAT; 785 786 /* 787 * Finish up. 788 */ 789 if (ronly) 790 pmp->pm_flags |= MSDOSFSMNT_RONLY; 791 else 792 pmp->pm_fmod = 1; 793 mp->mnt_data = pmp; 794 mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)dev; 795 mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_MSDOS); 796 mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0]; 797 mp->mnt_stat.f_namemax = MSDOSFS_NAMEMAX(pmp); 798 mp->mnt_flag |= MNT_LOCAL; 799 mp->mnt_dev_bshift = pmp->pm_bnshift; 800 mp->mnt_fs_bshift = pmp->pm_cnshift; 801 802 #ifdef QUOTA 803 /* 804 * If we ever do quotas for DOS filesystems this would be a place 805 * to fill in the info in the msdosfsmount structure. You dolt, 806 * quotas on dos filesystems make no sense because files have no 807 * owners on dos filesystems. of course there is some empty space 808 * in the directory entry where we could put uid's and gid's. 809 */ 810 #endif 811 devvp->v_specmountpoint = mp; 812 813 return (0); 814 815 error_exit:; 816 if (bp) 817 brelse(bp, BC_AGE); 818 if (pmp) { 819 if (pmp->pm_inusemap) 820 free(pmp->pm_inusemap, M_MSDOSFSFAT); 821 free(pmp, M_MSDOSFSMNT); 822 mp->mnt_data = NULL; 823 } 824 return (error); 825 } 826 827 int 828 msdosfs_start(struct mount *mp, int flags) 829 { 830 831 return (0); 832 } 833 834 /* 835 * Unmount the filesystem described by mp. 836 */ 837 int 838 msdosfs_unmount(mp, mntflags) 839 struct mount *mp; 840 int mntflags; 841 { 842 struct msdosfsmount *pmp; 843 int error, flags; 844 845 flags = 0; 846 if (mntflags & MNT_FORCE) 847 flags |= FORCECLOSE; 848 #ifdef QUOTA 849 #endif 850 if ((error = vflush(mp, NULLVP, flags)) != 0) 851 return (error); 852 pmp = VFSTOMSDOSFS(mp); 853 if (pmp->pm_devvp->v_type != VBAD) 854 pmp->pm_devvp->v_specmountpoint = NULL; 855 #ifdef MSDOSFS_DEBUG 856 { 857 struct vnode *vp = pmp->pm_devvp; 858 859 printf("msdosfs_umount(): just before calling VOP_CLOSE()\n"); 860 printf("flag %08x, usecount %d, writecount %ld, holdcnt %ld\n", 861 vp->v_vflag | vp->v_iflag | vp->v_uflag, vp->v_usecount, 862 vp->v_writecount, vp->v_holdcnt); 863 printf("mount %p, op %p\n", 864 vp->v_mount, vp->v_op); 865 printf("freef %p, freeb %p, mount %p\n", 866 vp->v_freelist.tqe_next, vp->v_freelist.tqe_prev, 867 vp->v_mount); 868 printf("cleanblkhd %p, dirtyblkhd %p, numoutput %d, type %d\n", 869 vp->v_cleanblkhd.lh_first, 870 vp->v_dirtyblkhd.lh_first, 871 vp->v_numoutput, vp->v_type); 872 printf("union %p, tag %d, data[0] %08x, data[1] %08x\n", 873 vp->v_socket, vp->v_tag, 874 ((u_int *)vp->v_data)[0], 875 ((u_int *)vp->v_data)[1]); 876 } 877 #endif 878 vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY); 879 error = VOP_CLOSE(pmp->pm_devvp, 880 pmp->pm_flags & MSDOSFSMNT_RONLY ? FREAD : FREAD|FWRITE, NOCRED); 881 vput(pmp->pm_devvp); 882 free(pmp->pm_inusemap, M_MSDOSFSFAT); 883 free(pmp, M_MSDOSFSMNT); 884 mp->mnt_data = NULL; 885 mp->mnt_flag &= ~MNT_LOCAL; 886 return (error); 887 } 888 889 int 890 msdosfs_root(mp, vpp) 891 struct mount *mp; 892 struct vnode **vpp; 893 { 894 struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 895 struct denode *ndep; 896 int error; 897 898 #ifdef MSDOSFS_DEBUG 899 printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp); 900 #endif 901 if ((error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep)) != 0) 902 return (error); 903 *vpp = DETOV(ndep); 904 return (0); 905 } 906 907 int 908 msdosfs_statvfs(struct mount *mp, struct statvfs *sbp) 909 { 910 struct msdosfsmount *pmp; 911 912 pmp = VFSTOMSDOSFS(mp); 913 sbp->f_bsize = pmp->pm_bpcluster; 914 sbp->f_frsize = sbp->f_bsize; 915 sbp->f_iosize = pmp->pm_bpcluster; 916 sbp->f_blocks = pmp->pm_nmbrofclusters; 917 sbp->f_bfree = pmp->pm_freeclustercount; 918 sbp->f_bavail = pmp->pm_freeclustercount; 919 sbp->f_bresvd = 0; 920 sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ 921 sbp->f_ffree = 0; /* what to put in here? */ 922 sbp->f_favail = 0; /* what to put in here? */ 923 sbp->f_fresvd = 0; 924 copy_statvfs_info(sbp, mp); 925 return (0); 926 } 927 928 int 929 msdosfs_sync(mp, waitfor, cred) 930 struct mount *mp; 931 int waitfor; 932 kauth_cred_t cred; 933 { 934 struct vnode *vp, *mvp; 935 struct denode *dep; 936 struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 937 int error, allerror = 0; 938 939 /* 940 * If we ever switch to not updating all of the fats all the time, 941 * this would be the place to update them from the first one. 942 */ 943 if (pmp->pm_fmod != 0) { 944 if (pmp->pm_flags & MSDOSFSMNT_RONLY) 945 panic("msdosfs_sync: rofs mod"); 946 else { 947 /* update fats here */ 948 } 949 } 950 /* Allocate a marker vnode. */ 951 if ((mvp = vnalloc(mp)) == NULL) 952 return ENOMEM; 953 /* 954 * Write back each (modified) denode. 955 */ 956 mutex_enter(&mntvnode_lock); 957 loop: 958 for (vp = TAILQ_FIRST(&mp->mnt_vnodelist); vp; vp = vunmark(mvp)) { 959 vmark(mvp, vp); 960 if (vp->v_mount != mp || vismarker(vp)) 961 continue; 962 mutex_enter(&vp->v_interlock); 963 dep = VTODE(vp); 964 if (waitfor == MNT_LAZY || vp->v_type == VNON || 965 (((dep->de_flag & 966 (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0) && 967 (LIST_EMPTY(&vp->v_dirtyblkhd) && 968 UVM_OBJ_IS_CLEAN(&vp->v_uobj)))) { 969 mutex_exit(&vp->v_interlock); 970 continue; 971 } 972 mutex_exit(&mntvnode_lock); 973 error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK); 974 if (error) { 975 mutex_enter(&mntvnode_lock); 976 if (error == ENOENT) { 977 (void)vunmark(mvp); 978 goto loop; 979 } 980 continue; 981 } 982 if ((error = VOP_FSYNC(vp, cred, 983 waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0)) != 0) 984 allerror = error; 985 vput(vp); 986 mutex_enter(&mntvnode_lock); 987 } 988 mutex_exit(&mntvnode_lock); 989 vnfree(mvp); 990 991 /* 992 * Force stale file system control information to be flushed. 993 */ 994 if ((error = VOP_FSYNC(pmp->pm_devvp, cred, 995 waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0)) != 0) 996 allerror = error; 997 #ifdef QUOTA 998 /* qsync(mp); */ 999 #endif 1000 return (allerror); 1001 } 1002 1003 int 1004 msdosfs_fhtovp(mp, fhp, vpp) 1005 struct mount *mp; 1006 struct fid *fhp; 1007 struct vnode **vpp; 1008 { 1009 struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 1010 struct defid defh; 1011 struct denode *dep; 1012 int error; 1013 1014 if (fhp->fid_len != sizeof(struct defid)) { 1015 DPRINTF(("fid_len %d %zd\n", fhp->fid_len, 1016 sizeof(struct defid))); 1017 return EINVAL; 1018 } 1019 1020 memcpy(&defh, fhp, sizeof(defh)); 1021 error = deget(pmp, defh.defid_dirclust, defh.defid_dirofs, &dep); 1022 if (error) { 1023 DPRINTF(("deget %d\n", error)); 1024 *vpp = NULLVP; 1025 return (error); 1026 } 1027 *vpp = DETOV(dep); 1028 return (0); 1029 } 1030 1031 int 1032 msdosfs_vptofh(vp, fhp, fh_size) 1033 struct vnode *vp; 1034 struct fid *fhp; 1035 size_t *fh_size; 1036 { 1037 struct denode *dep; 1038 struct defid defh; 1039 1040 if (*fh_size < sizeof(struct defid)) { 1041 *fh_size = sizeof(struct defid); 1042 return E2BIG; 1043 } 1044 *fh_size = sizeof(struct defid); 1045 dep = VTODE(vp); 1046 memset(&defh, 0, sizeof(defh)); 1047 defh.defid_len = sizeof(struct defid); 1048 defh.defid_dirclust = dep->de_dirclust; 1049 defh.defid_dirofs = dep->de_diroffset; 1050 /* defh.defid_gen = dep->de_gen; */ 1051 memcpy(fhp, &defh, sizeof(defh)); 1052 return (0); 1053 } 1054 1055 int 1056 msdosfs_vget(struct mount *mp, ino_t ino, 1057 struct vnode **vpp) 1058 { 1059 1060 return (EOPNOTSUPP); 1061 } 1062 1063 SYSCTL_SETUP(sysctl_vfs_msdosfs_setup, "sysctl vfs.msdosfs subtree setup") 1064 { 1065 1066 sysctl_createv(clog, 0, NULL, NULL, 1067 CTLFLAG_PERMANENT, 1068 CTLTYPE_NODE, "vfs", NULL, 1069 NULL, 0, NULL, 0, 1070 CTL_VFS, CTL_EOL); 1071 sysctl_createv(clog, 0, NULL, NULL, 1072 CTLFLAG_PERMANENT, 1073 CTLTYPE_NODE, "msdosfs", 1074 SYSCTL_DESCR("MS-DOS file system"), 1075 NULL, 0, NULL, 0, 1076 CTL_VFS, 4, CTL_EOL); 1077 /* 1078 * XXX the "4" above could be dynamic, thereby eliminating one 1079 * more instance of the "number to vfs" mapping problem, but 1080 * "4" is the order as taken from sys/mount.h 1081 */ 1082 } 1083