1 /* $FreeBSD$ */ 2 /* $NetBSD: msdosfs_vfsops.c,v 1.51 1997/11/17 15:36:58 ws Exp $ */ 3 4 /*- 5 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 6 * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 7 * All rights reserved. 8 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by TooLs GmbH. 21 * 4. The name of TooLs GmbH may not be used to endorse or promote products 22 * derived from this software without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 27 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 30 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 32 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 */ 35 /*- 36 * Written by Paul Popelka (paulp@uts.amdahl.com) 37 * 38 * You can do anything you want with this software, just don't say you wrote 39 * it, and don't remove this notice. 40 * 41 * This software is provided "as is". 42 * 43 * The author supplies this software to be publicly redistributed on the 44 * understanding that the author is not responsible for the correct 45 * functioning of this software in any circumstances and is not liable for 46 * any damages caused by this software. 47 * 48 * October 1992 49 */ 50 51 #include <sys/param.h> 52 #include <sys/systm.h> 53 #include <sys/buf.h> 54 #include <sys/conf.h> 55 #include <sys/fcntl.h> 56 #include <sys/iconv.h> 57 #include <sys/kernel.h> 58 #include <sys/lock.h> 59 #include <sys/malloc.h> 60 #include <sys/mount.h> 61 #include <sys/mutex.h> 62 #include <sys/namei.h> 63 #include <sys/priv.h> 64 #include <sys/proc.h> 65 #include <sys/stat.h> 66 #include <sys/vnode.h> 67 68 #include <geom/geom.h> 69 #include <geom/geom_vfs.h> 70 71 #include <fs/msdosfs/bootsect.h> 72 #include <fs/msdosfs/bpb.h> 73 #include <fs/msdosfs/direntry.h> 74 #include <fs/msdosfs/denode.h> 75 #include <fs/msdosfs/fat.h> 76 #include <fs/msdosfs/msdosfsmount.h> 77 78 static const char msdosfs_lock_msg[] = "fatlk"; 79 80 /* Mount options that we support. */ 81 static const char *msdosfs_opts[] = { 82 "async", "noatime", "noclusterr", "noclusterw", 83 "export", "force", "from", "sync", 84 "cs_dos", "cs_local", "cs_win", "dirmask", 85 "gid", "kiconv", "longname", 86 "longnames", "mask", "shortname", "shortnames", 87 "uid", "win95", "nowin95", 88 NULL 89 }; 90 91 #if 1 /*def PC98*/ 92 /* 93 * XXX - The boot signature formatted by NEC PC-98 DOS looks like a 94 * garbage or a random value :-{ 95 * If you want to use that broken-signatured media, define the 96 * following symbol even though PC/AT. 97 * (ex. mount PC-98 DOS formatted FD on PC/AT) 98 */ 99 #define MSDOSFS_NOCHECKSIG 100 #endif 101 102 MALLOC_DEFINE(M_MSDOSFSMNT, "msdosfs_mount", "MSDOSFS mount structure"); 103 static MALLOC_DEFINE(M_MSDOSFSFAT, "msdosfs_fat", "MSDOSFS file allocation table"); 104 105 struct iconv_functions *msdosfs_iconv; 106 107 static int update_mp(struct mount *mp, struct thread *td); 108 static int mountmsdosfs(struct vnode *devvp, struct mount *mp); 109 static vfs_fhtovp_t msdosfs_fhtovp; 110 static vfs_mount_t msdosfs_mount; 111 static vfs_root_t msdosfs_root; 112 static vfs_statfs_t msdosfs_statfs; 113 static vfs_sync_t msdosfs_sync; 114 static vfs_unmount_t msdosfs_unmount; 115 116 /* Maximum length of a character set name (arbitrary). */ 117 #define MAXCSLEN 64 118 119 static int 120 update_mp(struct mount *mp, struct thread *td) 121 { 122 struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 123 void *dos, *win, *local; 124 int error, v; 125 126 if (!vfs_getopt(mp->mnt_optnew, "kiconv", NULL, NULL)) { 127 if (msdosfs_iconv != NULL) { 128 error = vfs_getopt(mp->mnt_optnew, 129 "cs_win", &win, NULL); 130 if (!error) 131 error = vfs_getopt(mp->mnt_optnew, 132 "cs_local", &local, NULL); 133 if (!error) 134 error = vfs_getopt(mp->mnt_optnew, 135 "cs_dos", &dos, NULL); 136 if (!error) { 137 msdosfs_iconv->open(win, local, &pmp->pm_u2w); 138 msdosfs_iconv->open(local, win, &pmp->pm_w2u); 139 msdosfs_iconv->open(dos, local, &pmp->pm_u2d); 140 msdosfs_iconv->open(local, dos, &pmp->pm_d2u); 141 } 142 if (error != 0) 143 return (error); 144 } else { 145 pmp->pm_w2u = NULL; 146 pmp->pm_u2w = NULL; 147 pmp->pm_d2u = NULL; 148 pmp->pm_u2d = NULL; 149 } 150 } 151 152 if (vfs_scanopt(mp->mnt_optnew, "gid", "%d", &v) == 1) 153 pmp->pm_gid = v; 154 if (vfs_scanopt(mp->mnt_optnew, "uid", "%d", &v) == 1) 155 pmp->pm_uid = v; 156 if (vfs_scanopt(mp->mnt_optnew, "mask", "%d", &v) == 1) 157 pmp->pm_mask = v & ALLPERMS; 158 if (vfs_scanopt(mp->mnt_optnew, "dirmask", "%d", &v) == 1) 159 pmp->pm_dirmask = v & ALLPERMS; 160 vfs_flagopt(mp->mnt_optnew, "shortname", 161 &pmp->pm_flags, MSDOSFSMNT_SHORTNAME); 162 vfs_flagopt(mp->mnt_optnew, "shortnames", 163 &pmp->pm_flags, MSDOSFSMNT_SHORTNAME); 164 vfs_flagopt(mp->mnt_optnew, "longname", 165 &pmp->pm_flags, MSDOSFSMNT_LONGNAME); 166 vfs_flagopt(mp->mnt_optnew, "longnames", 167 &pmp->pm_flags, MSDOSFSMNT_LONGNAME); 168 vfs_flagopt(mp->mnt_optnew, "kiconv", 169 &pmp->pm_flags, MSDOSFSMNT_KICONV); 170 171 if (vfs_getopt(mp->mnt_optnew, "nowin95", NULL, NULL) == 0) 172 pmp->pm_flags |= MSDOSFSMNT_NOWIN95; 173 else 174 pmp->pm_flags &= ~MSDOSFSMNT_NOWIN95; 175 176 if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) 177 pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; 178 else 179 pmp->pm_flags |= MSDOSFSMNT_LONGNAME; 180 return 0; 181 } 182 183 static int 184 msdosfs_cmount(struct mntarg *ma, void *data, uint64_t flags) 185 { 186 struct msdosfs_args args; 187 struct export_args exp; 188 int error; 189 190 if (data == NULL) 191 return (EINVAL); 192 error = copyin(data, &args, sizeof args); 193 if (error) 194 return (error); 195 vfs_oexport_conv(&args.export, &exp); 196 197 ma = mount_argsu(ma, "from", args.fspec, MAXPATHLEN); 198 ma = mount_arg(ma, "export", &exp, sizeof(exp)); 199 ma = mount_argf(ma, "uid", "%d", args.uid); 200 ma = mount_argf(ma, "gid", "%d", args.gid); 201 ma = mount_argf(ma, "mask", "%d", args.mask); 202 ma = mount_argf(ma, "dirmask", "%d", args.dirmask); 203 204 ma = mount_argb(ma, args.flags & MSDOSFSMNT_SHORTNAME, "noshortname"); 205 ma = mount_argb(ma, args.flags & MSDOSFSMNT_LONGNAME, "nolongname"); 206 ma = mount_argb(ma, !(args.flags & MSDOSFSMNT_NOWIN95), "nowin95"); 207 ma = mount_argb(ma, args.flags & MSDOSFSMNT_KICONV, "nokiconv"); 208 209 ma = mount_argsu(ma, "cs_win", args.cs_win, MAXCSLEN); 210 ma = mount_argsu(ma, "cs_dos", args.cs_dos, MAXCSLEN); 211 ma = mount_argsu(ma, "cs_local", args.cs_local, MAXCSLEN); 212 213 error = kernel_mount(ma, flags); 214 215 return (error); 216 } 217 218 /* 219 * mp - path - addr in user space of mount point (ie /usr or whatever) 220 * data - addr in user space of mount params including the name of the block 221 * special file to treat as a filesystem. 222 */ 223 static int 224 msdosfs_mount(struct mount *mp) 225 { 226 struct vnode *devvp; /* vnode for blk device to mount */ 227 struct thread *td; 228 /* msdosfs specific mount control block */ 229 struct msdosfsmount *pmp = NULL; 230 struct nameidata ndp; 231 int error, flags; 232 accmode_t accmode; 233 char *from; 234 235 td = curthread; 236 if (vfs_filteropt(mp->mnt_optnew, msdosfs_opts)) 237 return (EINVAL); 238 239 /* 240 * If updating, check whether changing from read-only to 241 * read/write; if there is no device name, that's all we do. 242 */ 243 if (mp->mnt_flag & MNT_UPDATE) { 244 pmp = VFSTOMSDOSFS(mp); 245 if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && 246 vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) { 247 error = VFS_SYNC(mp, MNT_WAIT); 248 if (error) 249 return (error); 250 flags = WRITECLOSE; 251 if (mp->mnt_flag & MNT_FORCE) 252 flags |= FORCECLOSE; 253 error = vflush(mp, 0, flags, td); 254 if (error) 255 return (error); 256 257 /* 258 * Now the volume is clean. Mark it so while the 259 * device is still rw. 260 */ 261 error = markvoldirty(pmp, 0); 262 if (error) { 263 (void)markvoldirty(pmp, 1); 264 return (error); 265 } 266 267 /* Downgrade the device from rw to ro. */ 268 g_topology_lock(); 269 error = g_access(pmp->pm_cp, 0, -1, 0); 270 g_topology_unlock(); 271 if (error) { 272 (void)markvoldirty(pmp, 1); 273 return (error); 274 } 275 276 /* 277 * Backing out after an error was painful in the 278 * above. Now we are committed to succeeding. 279 */ 280 pmp->pm_fmod = 0; 281 pmp->pm_flags |= MSDOSFSMNT_RONLY; 282 MNT_ILOCK(mp); 283 mp->mnt_flag |= MNT_RDONLY; 284 MNT_IUNLOCK(mp); 285 } else if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && 286 !vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) { 287 /* 288 * If upgrade to read-write by non-root, then verify 289 * that user has necessary permissions on the device. 290 */ 291 devvp = pmp->pm_devvp; 292 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 293 error = VOP_ACCESS(devvp, VREAD | VWRITE, 294 td->td_ucred, td); 295 if (error) 296 error = priv_check(td, PRIV_VFS_MOUNT_PERM); 297 if (error) { 298 VOP_UNLOCK(devvp, 0); 299 return (error); 300 } 301 VOP_UNLOCK(devvp, 0); 302 g_topology_lock(); 303 error = g_access(pmp->pm_cp, 0, 1, 0); 304 g_topology_unlock(); 305 if (error) 306 return (error); 307 308 pmp->pm_fmod = 1; 309 pmp->pm_flags &= ~MSDOSFSMNT_RONLY; 310 MNT_ILOCK(mp); 311 mp->mnt_flag &= ~MNT_RDONLY; 312 MNT_IUNLOCK(mp); 313 314 /* Now that the volume is modifiable, mark it dirty. */ 315 error = markvoldirty(pmp, 1); 316 if (error) 317 return (error); 318 } 319 } 320 /* 321 * Not an update, or updating the name: look up the name 322 * and verify that it refers to a sensible disk device. 323 */ 324 if (vfs_getopt(mp->mnt_optnew, "from", (void **)&from, NULL)) 325 return (EINVAL); 326 NDINIT(&ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, from, td); 327 error = namei(&ndp); 328 if (error) 329 return (error); 330 devvp = ndp.ni_vp; 331 NDFREE(&ndp, NDF_ONLY_PNBUF); 332 333 if (!vn_isdisk(devvp, &error)) { 334 vput(devvp); 335 return (error); 336 } 337 /* 338 * If mount by non-root, then verify that user has necessary 339 * permissions on the device. 340 */ 341 accmode = VREAD; 342 if ((mp->mnt_flag & MNT_RDONLY) == 0) 343 accmode |= VWRITE; 344 error = VOP_ACCESS(devvp, accmode, td->td_ucred, td); 345 if (error) 346 error = priv_check(td, PRIV_VFS_MOUNT_PERM); 347 if (error) { 348 vput(devvp); 349 return (error); 350 } 351 if ((mp->mnt_flag & MNT_UPDATE) == 0) { 352 error = mountmsdosfs(devvp, mp); 353 #ifdef MSDOSFS_DEBUG /* only needed for the printf below */ 354 pmp = VFSTOMSDOSFS(mp); 355 #endif 356 } else { 357 vput(devvp); 358 if (devvp != pmp->pm_devvp) 359 return (EINVAL); /* XXX needs translation */ 360 } 361 if (error) { 362 vrele(devvp); 363 return (error); 364 } 365 366 error = update_mp(mp, td); 367 if (error) { 368 if ((mp->mnt_flag & MNT_UPDATE) == 0) 369 msdosfs_unmount(mp, MNT_FORCE); 370 return error; 371 } 372 373 vfs_mountedfrom(mp, from); 374 #ifdef MSDOSFS_DEBUG 375 printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap); 376 #endif 377 return (0); 378 } 379 380 static int 381 mountmsdosfs(struct vnode *devvp, struct mount *mp) 382 { 383 struct msdosfsmount *pmp; 384 struct buf *bp; 385 struct cdev *dev; 386 union bootsector *bsp; 387 struct byte_bpb33 *b33; 388 struct byte_bpb50 *b50; 389 struct byte_bpb710 *b710; 390 uint8_t SecPerClust; 391 u_long clusters; 392 int ronly, error; 393 struct g_consumer *cp; 394 struct bufobj *bo; 395 396 bp = NULL; /* This and pmp both used in error_exit. */ 397 pmp = NULL; 398 ronly = (mp->mnt_flag & MNT_RDONLY) != 0; 399 400 dev = devvp->v_rdev; 401 if (atomic_cmpset_acq_ptr((uintptr_t *)&dev->si_mountpt, 0, 402 (uintptr_t)mp) == 0) { 403 VOP_UNLOCK(devvp, 0); 404 return (EBUSY); 405 } 406 g_topology_lock(); 407 error = g_vfs_open(devvp, &cp, "msdosfs", ronly ? 0 : 1); 408 g_topology_unlock(); 409 if (error != 0) { 410 atomic_store_rel_ptr((uintptr_t *)&dev->si_mountpt, 0); 411 VOP_UNLOCK(devvp, 0); 412 return (error); 413 } 414 dev_ref(dev); 415 VOP_UNLOCK(devvp, 0); 416 417 bo = &devvp->v_bufobj; 418 419 /* 420 * Read the boot sector of the filesystem, and then check the 421 * boot signature. If not a dos boot sector then error out. 422 * 423 * NOTE: 8192 is a magic size that works for ffs. 424 */ 425 error = bread(devvp, 0, 8192, NOCRED, &bp); 426 if (error) 427 goto error_exit; 428 bp->b_flags |= B_AGE; 429 bsp = (union bootsector *)bp->b_data; 430 b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB; 431 b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; 432 b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB; 433 434 #ifndef MSDOSFS_NOCHECKSIG 435 if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 436 || bsp->bs50.bsBootSectSig1 != BOOTSIG1) { 437 error = EINVAL; 438 goto error_exit; 439 } 440 #endif 441 442 pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK | M_ZERO); 443 pmp->pm_mountp = mp; 444 pmp->pm_cp = cp; 445 pmp->pm_bo = bo; 446 447 lockinit(&pmp->pm_fatlock, 0, msdosfs_lock_msg, 0, 0); 448 449 /* 450 * Initialize ownerships and permissions, since nothing else will 451 * initialize them iff we are mounting root. 452 */ 453 pmp->pm_uid = UID_ROOT; 454 pmp->pm_gid = GID_WHEEL; 455 pmp->pm_mask = pmp->pm_dirmask = S_IXUSR | S_IXGRP | S_IXOTH | 456 S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR; 457 458 /* 459 * Compute several useful quantities from the bpb in the 460 * bootsector. Copy in the dos 5 variant of the bpb then fix up 461 * the fields that are different between dos 5 and dos 3.3. 462 */ 463 SecPerClust = b50->bpbSecPerClust; 464 pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); 465 if (pmp->pm_BytesPerSec < DEV_BSIZE) { 466 error = EINVAL; 467 goto error_exit; 468 } 469 pmp->pm_ResSectors = getushort(b50->bpbResSectors); 470 pmp->pm_FATs = b50->bpbFATs; 471 pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); 472 pmp->pm_Sectors = getushort(b50->bpbSectors); 473 pmp->pm_FATsecs = getushort(b50->bpbFATsecs); 474 pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); 475 pmp->pm_Heads = getushort(b50->bpbHeads); 476 pmp->pm_Media = b50->bpbMedia; 477 478 /* calculate the ratio of sector size to DEV_BSIZE */ 479 pmp->pm_BlkPerSec = pmp->pm_BytesPerSec / DEV_BSIZE; 480 481 /* 482 * We don't check pm_Heads nor pm_SecPerTrack, because 483 * these may not be set for EFI file systems. We don't 484 * use these anyway, so we're unaffected if they are 485 * invalid. 486 */ 487 if (!pmp->pm_BytesPerSec || !SecPerClust) { 488 error = EINVAL; 489 goto error_exit; 490 } 491 492 if (pmp->pm_Sectors == 0) { 493 pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); 494 pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); 495 } else { 496 pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); 497 pmp->pm_HugeSectors = pmp->pm_Sectors; 498 } 499 500 if (pmp->pm_RootDirEnts == 0) { 501 if (pmp->pm_FATsecs 502 || getushort(b710->bpbFSVers)) { 503 error = EINVAL; 504 #ifdef MSDOSFS_DEBUG 505 printf("mountmsdosfs(): bad FAT32 filesystem\n"); 506 #endif 507 goto error_exit; 508 } 509 pmp->pm_fatmask = FAT32_MASK; 510 pmp->pm_fatmult = 4; 511 pmp->pm_fatdiv = 1; 512 pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs); 513 if (getushort(b710->bpbExtFlags) & FATMIRROR) 514 pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM; 515 else 516 pmp->pm_flags |= MSDOSFS_FATMIRROR; 517 } else 518 pmp->pm_flags |= MSDOSFS_FATMIRROR; 519 520 /* 521 * Check a few values (could do some more): 522 * - logical sector size: power of 2, >= block size 523 * - sectors per cluster: power of 2, >= 1 524 * - number of sectors: >= 1, <= size of partition 525 * - number of FAT sectors: >= 1 526 */ 527 if ( (SecPerClust == 0) 528 || (SecPerClust & (SecPerClust - 1)) 529 || (pmp->pm_BytesPerSec < DEV_BSIZE) 530 || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1)) 531 || (pmp->pm_HugeSectors == 0) 532 || (pmp->pm_FATsecs == 0) 533 || (SecPerClust * pmp->pm_BlkPerSec > MAXBSIZE / DEV_BSIZE) 534 ) { 535 error = EINVAL; 536 goto error_exit; 537 } 538 539 pmp->pm_HugeSectors *= pmp->pm_BlkPerSec; 540 pmp->pm_HiddenSects *= pmp->pm_BlkPerSec; /* XXX not used? */ 541 pmp->pm_FATsecs *= pmp->pm_BlkPerSec; 542 SecPerClust *= pmp->pm_BlkPerSec; 543 544 pmp->pm_fatblk = pmp->pm_ResSectors * pmp->pm_BlkPerSec; 545 546 if (FAT32(pmp)) { 547 pmp->pm_rootdirblk = getulong(b710->bpbRootClust); 548 pmp->pm_firstcluster = pmp->pm_fatblk 549 + (pmp->pm_FATs * pmp->pm_FATsecs); 550 pmp->pm_fsinfo = getushort(b710->bpbFSInfo) * pmp->pm_BlkPerSec; 551 } else { 552 pmp->pm_rootdirblk = pmp->pm_fatblk + 553 (pmp->pm_FATs * pmp->pm_FATsecs); 554 pmp->pm_rootdirsize = howmany(pmp->pm_RootDirEnts * 555 sizeof(struct direntry), DEV_BSIZE); /* in blocks */ 556 pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; 557 } 558 559 pmp->pm_maxcluster = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / 560 SecPerClust + 1; 561 pmp->pm_fatsize = pmp->pm_FATsecs * DEV_BSIZE; /* XXX not used? */ 562 563 if (pmp->pm_fatmask == 0) { 564 if (pmp->pm_maxcluster 565 <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) { 566 /* 567 * This will usually be a floppy disk. This size makes 568 * sure that one FAT entry will not be split across 569 * multiple blocks. 570 */ 571 pmp->pm_fatmask = FAT12_MASK; 572 pmp->pm_fatmult = 3; 573 pmp->pm_fatdiv = 2; 574 } else { 575 pmp->pm_fatmask = FAT16_MASK; 576 pmp->pm_fatmult = 2; 577 pmp->pm_fatdiv = 1; 578 } 579 } 580 581 clusters = (pmp->pm_fatsize / pmp->pm_fatmult) * pmp->pm_fatdiv; 582 if (pmp->pm_maxcluster >= clusters) { 583 #ifdef MSDOSFS_DEBUG 584 printf("Warning: number of clusters (%ld) exceeds FAT " 585 "capacity (%ld)\n", pmp->pm_maxcluster + 1, clusters); 586 #endif 587 pmp->pm_maxcluster = clusters - 1; 588 } 589 590 if (FAT12(pmp)) 591 pmp->pm_fatblocksize = 3 * 512; 592 else 593 pmp->pm_fatblocksize = PAGE_SIZE; 594 pmp->pm_fatblocksize = roundup(pmp->pm_fatblocksize, 595 pmp->pm_BytesPerSec); 596 pmp->pm_fatblocksec = pmp->pm_fatblocksize / DEV_BSIZE; 597 pmp->pm_bnshift = ffs(DEV_BSIZE) - 1; 598 599 /* 600 * Compute mask and shift value for isolating cluster relative byte 601 * offsets and cluster numbers from a file offset. 602 */ 603 pmp->pm_bpcluster = SecPerClust * DEV_BSIZE; 604 pmp->pm_crbomask = pmp->pm_bpcluster - 1; 605 pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1; 606 607 /* 608 * Check for valid cluster size 609 * must be a power of 2 610 */ 611 if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) { 612 error = EINVAL; 613 goto error_exit; 614 } 615 616 /* 617 * Release the bootsector buffer. 618 */ 619 brelse(bp); 620 bp = NULL; 621 622 /* 623 * Check the fsinfo sector if we have one. Silently fix up our 624 * in-core copy of fp->fsinxtfree if it is unknown (0xffffffff) 625 * or too large. Ignore fp->fsinfree for now, since we need to 626 * read the entire FAT anyway to fill the inuse map. 627 */ 628 if (pmp->pm_fsinfo) { 629 struct fsinfo *fp; 630 631 if ((error = bread(devvp, pmp->pm_fsinfo, pmp->pm_BytesPerSec, 632 NOCRED, &bp)) != 0) 633 goto error_exit; 634 fp = (struct fsinfo *)bp->b_data; 635 if (!bcmp(fp->fsisig1, "RRaA", 4) 636 && !bcmp(fp->fsisig2, "rrAa", 4) 637 && !bcmp(fp->fsisig3, "\0\0\125\252", 4)) { 638 pmp->pm_nxtfree = getulong(fp->fsinxtfree); 639 if (pmp->pm_nxtfree > pmp->pm_maxcluster) 640 pmp->pm_nxtfree = CLUST_FIRST; 641 } else 642 pmp->pm_fsinfo = 0; 643 brelse(bp); 644 bp = NULL; 645 } 646 647 /* 648 * Finish initializing pmp->pm_nxtfree (just in case the first few 649 * sectors aren't properly reserved in the FAT). This completes 650 * the fixup for fp->fsinxtfree, and fixes up the zero-initialized 651 * value if there is no fsinfo. We will use pmp->pm_nxtfree 652 * internally even if there is no fsinfo. 653 */ 654 if (pmp->pm_nxtfree < CLUST_FIRST) 655 pmp->pm_nxtfree = CLUST_FIRST; 656 657 /* 658 * Allocate memory for the bitmap of allocated clusters, and then 659 * fill it in. 660 */ 661 pmp->pm_inusemap = malloc(howmany(pmp->pm_maxcluster + 1, N_INUSEBITS) 662 * sizeof(*pmp->pm_inusemap), 663 M_MSDOSFSFAT, M_WAITOK); 664 665 /* 666 * fillinusemap() needs pm_devvp. 667 */ 668 pmp->pm_devvp = devvp; 669 pmp->pm_dev = dev; 670 671 /* 672 * Have the inuse map filled in. 673 */ 674 MSDOSFS_LOCK_MP(pmp); 675 error = fillinusemap(pmp); 676 MSDOSFS_UNLOCK_MP(pmp); 677 if (error != 0) 678 goto error_exit; 679 680 /* 681 * If they want FAT updates to be synchronous then let them suffer 682 * the performance degradation in exchange for the on disk copy of 683 * the FAT being correct just about all the time. I suppose this 684 * would be a good thing to turn on if the kernel is still flakey. 685 */ 686 if (mp->mnt_flag & MNT_SYNCHRONOUS) 687 pmp->pm_flags |= MSDOSFSMNT_WAITONFAT; 688 689 /* 690 * Finish up. 691 */ 692 if (ronly) 693 pmp->pm_flags |= MSDOSFSMNT_RONLY; 694 else { 695 if ((error = markvoldirty(pmp, 1)) != 0) { 696 (void)markvoldirty(pmp, 0); 697 goto error_exit; 698 } 699 pmp->pm_fmod = 1; 700 } 701 mp->mnt_data = pmp; 702 mp->mnt_stat.f_fsid.val[0] = dev2udev(dev); 703 mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; 704 MNT_ILOCK(mp); 705 mp->mnt_flag |= MNT_LOCAL; 706 mp->mnt_kern_flag |= MNTK_USES_BCACHE | MNTK_NO_IOPF; 707 MNT_IUNLOCK(mp); 708 709 return (0); 710 711 error_exit: 712 if (bp) 713 brelse(bp); 714 if (cp != NULL) { 715 g_topology_lock(); 716 g_vfs_close(cp); 717 g_topology_unlock(); 718 } 719 if (pmp) { 720 lockdestroy(&pmp->pm_fatlock); 721 free(pmp->pm_inusemap, M_MSDOSFSFAT); 722 free(pmp, M_MSDOSFSMNT); 723 mp->mnt_data = NULL; 724 } 725 atomic_store_rel_ptr((uintptr_t *)&dev->si_mountpt, 0); 726 dev_rel(dev); 727 return (error); 728 } 729 730 /* 731 * Unmount the filesystem described by mp. 732 */ 733 static int 734 msdosfs_unmount(struct mount *mp, int mntflags) 735 { 736 struct msdosfsmount *pmp; 737 int error, flags; 738 739 error = flags = 0; 740 pmp = VFSTOMSDOSFS(mp); 741 if ((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0) 742 error = msdosfs_sync(mp, MNT_WAIT); 743 if ((mntflags & MNT_FORCE) != 0) 744 flags |= FORCECLOSE; 745 else if (error != 0) 746 return (error); 747 error = vflush(mp, 0, flags, curthread); 748 if (error != 0 && error != ENXIO) 749 return (error); 750 if ((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0) { 751 error = markvoldirty(pmp, 0); 752 if (error && error != ENXIO) { 753 (void)markvoldirty(pmp, 1); 754 return (error); 755 } 756 } 757 if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdosfs_iconv) { 758 if (pmp->pm_w2u) 759 msdosfs_iconv->close(pmp->pm_w2u); 760 if (pmp->pm_u2w) 761 msdosfs_iconv->close(pmp->pm_u2w); 762 if (pmp->pm_d2u) 763 msdosfs_iconv->close(pmp->pm_d2u); 764 if (pmp->pm_u2d) 765 msdosfs_iconv->close(pmp->pm_u2d); 766 } 767 768 #ifdef MSDOSFS_DEBUG 769 { 770 struct vnode *vp = pmp->pm_devvp; 771 struct bufobj *bo; 772 773 bo = &vp->v_bufobj; 774 BO_LOCK(bo); 775 VI_LOCK(vp); 776 vn_printf(vp, 777 "msdosfs_umount(): just before calling VOP_CLOSE()\n"); 778 printf("freef %p, freeb %p, mount %p\n", 779 TAILQ_NEXT(vp, v_actfreelist), vp->v_actfreelist.tqe_prev, 780 vp->v_mount); 781 printf("cleanblkhd %p, dirtyblkhd %p, numoutput %ld, type %d\n", 782 TAILQ_FIRST(&vp->v_bufobj.bo_clean.bv_hd), 783 TAILQ_FIRST(&vp->v_bufobj.bo_dirty.bv_hd), 784 vp->v_bufobj.bo_numoutput, vp->v_type); 785 VI_UNLOCK(vp); 786 BO_UNLOCK(bo); 787 } 788 #endif 789 g_topology_lock(); 790 g_vfs_close(pmp->pm_cp); 791 g_topology_unlock(); 792 atomic_store_rel_ptr((uintptr_t *)&pmp->pm_dev->si_mountpt, 0); 793 vrele(pmp->pm_devvp); 794 dev_rel(pmp->pm_dev); 795 free(pmp->pm_inusemap, M_MSDOSFSFAT); 796 lockdestroy(&pmp->pm_fatlock); 797 free(pmp, M_MSDOSFSMNT); 798 mp->mnt_data = NULL; 799 MNT_ILOCK(mp); 800 mp->mnt_flag &= ~MNT_LOCAL; 801 MNT_IUNLOCK(mp); 802 return (error); 803 } 804 805 static int 806 msdosfs_root(struct mount *mp, int flags, struct vnode **vpp) 807 { 808 struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 809 struct denode *ndep; 810 int error; 811 812 #ifdef MSDOSFS_DEBUG 813 printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp); 814 #endif 815 error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep); 816 if (error) 817 return (error); 818 *vpp = DETOV(ndep); 819 return (0); 820 } 821 822 static int 823 msdosfs_statfs(struct mount *mp, struct statfs *sbp) 824 { 825 struct msdosfsmount *pmp; 826 827 pmp = VFSTOMSDOSFS(mp); 828 sbp->f_bsize = pmp->pm_bpcluster; 829 sbp->f_iosize = pmp->pm_bpcluster; 830 sbp->f_blocks = pmp->pm_maxcluster + 1; 831 sbp->f_bfree = pmp->pm_freeclustercount; 832 sbp->f_bavail = pmp->pm_freeclustercount; 833 sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ 834 sbp->f_ffree = 0; /* what to put in here? */ 835 return (0); 836 } 837 838 /* 839 * If we have an FSInfo block, update it. 840 */ 841 static int 842 msdosfs_fsiflush(struct msdosfsmount *pmp, int waitfor) 843 { 844 struct fsinfo *fp; 845 struct buf *bp; 846 int error; 847 848 MSDOSFS_LOCK_MP(pmp); 849 if (pmp->pm_fsinfo == 0 || (pmp->pm_flags & MSDOSFS_FSIMOD) == 0) { 850 error = 0; 851 goto unlock; 852 } 853 error = bread(pmp->pm_devvp, pmp->pm_fsinfo, pmp->pm_BytesPerSec, 854 NOCRED, &bp); 855 if (error != 0) { 856 brelse(bp); 857 goto unlock; 858 } 859 fp = (struct fsinfo *)bp->b_data; 860 putulong(fp->fsinfree, pmp->pm_freeclustercount); 861 putulong(fp->fsinxtfree, pmp->pm_nxtfree); 862 pmp->pm_flags &= ~MSDOSFS_FSIMOD; 863 if (waitfor == MNT_WAIT) 864 error = bwrite(bp); 865 else 866 bawrite(bp); 867 unlock: 868 MSDOSFS_UNLOCK_MP(pmp); 869 return (error); 870 } 871 872 static int 873 msdosfs_sync(struct mount *mp, int waitfor) 874 { 875 struct vnode *vp, *nvp; 876 struct thread *td; 877 struct denode *dep; 878 struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 879 int error, allerror = 0; 880 881 td = curthread; 882 883 /* 884 * If we ever switch to not updating all of the FATs all the time, 885 * this would be the place to update them from the first one. 886 */ 887 if (pmp->pm_fmod != 0) { 888 if (pmp->pm_flags & MSDOSFSMNT_RONLY) 889 panic("msdosfs_sync: rofs mod"); 890 else { 891 /* update FATs here */ 892 } 893 } 894 /* 895 * Write back each (modified) denode. 896 */ 897 loop: 898 MNT_VNODE_FOREACH_ALL(vp, mp, nvp) { 899 if (vp->v_type == VNON) { 900 VI_UNLOCK(vp); 901 continue; 902 } 903 dep = VTODE(vp); 904 if ((dep->de_flag & 905 (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0 && 906 (vp->v_bufobj.bo_dirty.bv_cnt == 0 || 907 waitfor == MNT_LAZY)) { 908 VI_UNLOCK(vp); 909 continue; 910 } 911 error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, td); 912 if (error) { 913 if (error == ENOENT) 914 goto loop; 915 continue; 916 } 917 error = VOP_FSYNC(vp, waitfor, td); 918 if (error) 919 allerror = error; 920 VOP_UNLOCK(vp, 0); 921 vrele(vp); 922 } 923 924 /* 925 * Flush filesystem control info. 926 */ 927 if (waitfor != MNT_LAZY) { 928 vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY); 929 error = VOP_FSYNC(pmp->pm_devvp, waitfor, td); 930 if (error) 931 allerror = error; 932 VOP_UNLOCK(pmp->pm_devvp, 0); 933 } 934 935 error = msdosfs_fsiflush(pmp, waitfor); 936 if (error != 0) 937 allerror = error; 938 return (allerror); 939 } 940 941 static int 942 msdosfs_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp) 943 { 944 struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 945 struct defid *defhp = (struct defid *) fhp; 946 struct denode *dep; 947 int error; 948 949 error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, &dep); 950 if (error) { 951 *vpp = NULLVP; 952 return (error); 953 } 954 *vpp = DETOV(dep); 955 vnode_create_vobject(*vpp, dep->de_FileSize, curthread); 956 return (0); 957 } 958 959 static struct vfsops msdosfs_vfsops = { 960 .vfs_fhtovp = msdosfs_fhtovp, 961 .vfs_mount = msdosfs_mount, 962 .vfs_cmount = msdosfs_cmount, 963 .vfs_root = msdosfs_root, 964 .vfs_statfs = msdosfs_statfs, 965 .vfs_sync = msdosfs_sync, 966 .vfs_unmount = msdosfs_unmount, 967 }; 968 969 VFS_SET(msdosfs_vfsops, msdosfs, 0); 970 MODULE_VERSION(msdosfs, 1); 971