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