1/* 2 * Copyright (c) 2000-2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* $FreeBSD: src/sys/msdosfs/msdosfs_vfsops.c,v 1.63 2000/05/05 09:58:36 phk Exp $ */ 24/* $NetBSD: msdosfs_vfsops.c,v 1.51 1997/11/17 15:36:58 ws Exp $ */ 25 26/*- 27 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 28 * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 29 * All rights reserved. 30 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 31 * 32 * Redistribution and use in source and binary forms, with or without 33 * modification, are permitted provided that the following conditions 34 * are met: 35 * 1. Redistributions of source code must retain the above copyright 36 * notice, this list of conditions and the following disclaimer. 37 * 2. Redistributions in binary form must reproduce the above copyright 38 * notice, this list of conditions and the following disclaimer in the 39 * documentation and/or other materials provided with the distribution. 40 * 3. All advertising materials mentioning features or use of this software 41 * must display the following acknowledgement: 42 * This product includes software developed by TooLs GmbH. 43 * 4. The name of TooLs GmbH may not be used to endorse or promote products 44 * derived from this software without specific prior written permission. 45 * 46 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 47 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 48 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 49 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 50 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 51 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 52 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 53 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 54 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 55 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 56 */ 57/* 58 * Written by Paul Popelka (paulp@uts.amdahl.com) 59 * 60 * You can do anything you want with this software, just don't say you wrote 61 * it, and don't remove this notice. 62 * 63 * This software is provided "as is". 64 * 65 * The author supplies this software to be publicly redistributed on the 66 * understanding that the author is not responsible for the correct 67 * functioning of this software in any circumstances and is not liable for 68 * any damages caused by this software. 69 * 70 * October 1992 71 */ 72#include <sys/param.h> 73#include <sys/systm.h> 74#include <sys/conf.h> 75#include <sys/proc.h> 76#include <sys/kernel.h> 77#include <sys/vnode.h> 78#include <sys/mount.h> 79#include <sys/buf.h> 80#include <sys/fcntl.h> 81#include <sys/malloc.h> 82#include <sys/stat.h> /* defines ALLPERMS */ 83#include <sys/ubc.h> 84#include <sys/utfconv.h> 85#include <sys/disk.h> 86#include <sys/sysctl.h> 87#include <mach/kmod.h> 88#include <libkern/OSBase.h> 89#include <libkern/OSAtomic.h> 90#include <kern/clock.h> 91#include <kern/thread.h> 92#include <kern/thread_call.h> 93#include <miscfs/specfs/specdev.h> 94#include <IOKit/IOTypes.h> 95#include <libkern/OSMalloc.h> 96#include <libkern/OSKextLib.h> 97#include <libkern/crypto/md5.h> 98#include <TargetConditionals.h> 99 100#include "bpb.h" 101#include "bootsect.h" 102#include "direntry.h" 103#include "denode.h" 104#include "msdosfsmount.h" 105#include "fat.h" 106#include "msdosfs_kdebug.h" 107 108/* 109 * By default, don't try to auto unload the KEXT on embedded systems, since 110 * that isn't currently supported. You can always explicitly set 111 * MSDOSFS_AUTO_UNLOAD to 0 or 1 to override the default. 112 */ 113#ifndef MSDOSFS_AUTO_UNLOAD 114#if TARGET_OS_EMBEDDED 115#define MSDOSFS_AUTO_UNLOAD 0 116#else 117#define MSDOSFS_AUTO_UNLOAD 1 118#endif 119#endif 120 121#define MSDOSFS_DFLTBSIZE 4096 122 123#define rounddown(x,y) (((x)/(y))*(y)) 124 125extern u_int16_t dos2unicode[32]; 126 127extern int32_t msdos_secondsWest; /* In msdosfs_conv.c */ 128 129__private_extern__ lck_grp_attr_t *msdosfs_lck_grp_attr = NULL; 130__private_extern__ lck_grp_t *msdosfs_lck_grp = NULL; 131__private_extern__ lck_attr_t *msdosfs_lck_attr = NULL; 132__private_extern__ OSMallocTag msdosfs_malloc_tag = NULL; 133 134#if DEBUG 135SYSCTL_DECL(_vfs_generic); 136SYSCTL_NODE(_vfs_generic, OID_AUTO, msdosfs, CTLFLAG_RW, 0, "msdosfs (FAT) file system"); 137SYSCTL_INT(_vfs_generic_msdosfs, OID_AUTO, meta_delay, CTLFLAG_RW, &msdosfs_meta_delay, 0, "max delay before flushing metadata (ms)"); 138#endif 139 140int msdosfs_init(struct vfsconf *vfsp); 141int msdosfs_uninit(void); 142int msdosfs_start(struct mount *mp, int flags, vfs_context_t context); 143 144int msdosfs_update_mp(struct mount *mp, struct msdosfs_args *argp); 145int msdosfs_mount(vnode_t devvp, struct mount *mp, vfs_context_t context); 146int msdosfs_vfs_mount(struct mount *mp, vnode_t devvp, user_addr_t data, vfs_context_t); 147int msdosfs_vfs_root(struct mount *, vnode_t *, vfs_context_t); 148int msdosfs_vfs_statfs(struct mount *, struct vfsstatfs *, vfs_context_t); 149int msdosfs_vfs_getattr(mount_t mp, struct vfs_attr *attr, vfs_context_t context); 150int msdosfs_vfs_setattr(mount_t mp, struct vfs_attr *attr, vfs_context_t context); 151int msdosfs_vfs_sync(struct mount *, int, vfs_context_t); 152int msdosfs_vfs_unmount(struct mount *, int, vfs_context_t); 153 154int msdosfs_scan_root_dir(struct mount *mp, vfs_context_t context); 155int msdosfs_sync_callback(vnode_t vp, void *cargs); 156 157/* The routines are exported for the KEXT glue to link against. */ 158int msdosfs_module_start(kmod_info_t *ki, void *data); 159int msdosfs_module_stop (kmod_info_t *ki, void *data); 160 161/*ARGSUSED*/ 162int msdosfs_init(struct vfsconf *vfsp) 163{ 164#pragma unused (vfsp) 165 msdosfs_lck_grp_attr = lck_grp_attr_alloc_init(); 166 msdosfs_lck_grp = lck_grp_alloc_init("msdosfs", msdosfs_lck_grp_attr); 167 msdosfs_lck_attr = lck_attr_alloc_init(); 168 169 msdosfs_malloc_tag = OSMalloc_Tagalloc("msdosfs", OSMT_DEFAULT); 170 171 msdosfs_hash_init(); 172 return 0; 173} 174 175 176/* 177 * There is no "un-init" VFS operation. This routine is only called by 178 * the KEXT as it is about to be unloaded. 179 */ 180 181int msdosfs_uninit(void) 182{ 183 msdosfs_hash_uninit(); 184 185 OSMalloc_Tagfree(msdosfs_malloc_tag); 186 187 lck_attr_free(msdosfs_lck_attr); 188 lck_grp_free(msdosfs_lck_grp); 189 lck_grp_attr_free(msdosfs_lck_grp_attr); 190 191 return 0; 192} 193 194 195int msdosfs_update_mp(struct mount *mp, struct msdosfs_args *argp) 196{ 197 struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 198 199 pmp->pm_gid = argp->gid; 200 pmp->pm_uid = argp->uid; 201 pmp->pm_mask = argp->mask & ALLPERMS; 202 pmp->pm_flags |= argp->flags & MSDOSFSMNT_MNTOPT; 203 if (argp->flags & MSDOSFSMNT_SECONDSWEST) 204 msdos_secondsWest = argp->secondsWest; 205 206 if (argp->flags & MSDOSFSMNT_LABEL) 207 bcopy(argp->label, pmp->pm_label, sizeof(pmp->pm_label)); 208 209 return 0; 210} 211 212 213/* 214 * mp - path - addr in user space of mount point (ie /usr or whatever) 215 * data - addr in user space of mount params including the name of the block 216 * special file to treat as a filesystem. 217 */ 218int msdosfs_vfs_mount(struct mount *mp, vnode_t devvp, user_addr_t data, vfs_context_t context) 219{ 220 struct msdosfs_args args; /* will hold data from mount request */ 221 /* msdosfs specific mount control block */ 222 struct msdosfsmount *pmp = NULL; 223 int error, flags; 224 225 KERNEL_DEBUG_CONSTANT(MSDOSFS_VFS_MOUNT|DBG_FUNC_START, 0, 0, 0, 0, 0); 226#if MSDOSFS_AUTO_UNLOAD 227 OSKextRetainKextWithLoadTag(OSKextGetCurrentLoadTag()); 228#endif 229 230 error = copyin(data, &args, sizeof(struct msdosfs_args)); 231 if (error) 232 goto error_exit; 233 if (args.magic != MSDOSFS_ARGSMAGIC) 234 args.flags = 0; 235 236 /* 237 * If updating, check whether changing from read-only to 238 * read/write; if there is no device name, that's all we do. 239 */ 240 if (vfs_isupdate(mp)) { 241 pmp = VFSTOMSDOSFS(mp); 242 error = 0; 243 if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && vfs_isrdonly(mp)) { 244 /* Downgrading from read/write to read-only */ 245 /* � Is vflush() sufficient? Is there more we should flush out? */ 246 flags = WRITECLOSE; 247 if (vfs_isforce(mp)) 248 flags |= FORCECLOSE; 249 error = vflush(mp, NULLVP, flags); 250 } 251 if (!error && vfs_isreload(mp)) 252 /* not yet implemented */ 253 error = ENOTSUP; 254 if (error) 255 goto error_exit; 256 if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && vfs_iswriteupgrade(mp)) { 257 /* � Assuming that VFS has verified we can write to the device */ 258 259 pmp->pm_flags &= ~MSDOSFSMNT_RONLY; 260 261 /* Now that the volume is modifiable, mark it dirty */ 262 error = msdosfs_markvoldirty(pmp, 1); 263 if (error) { 264 pmp->pm_flags |= MSDOSFSMNT_RONLY; 265 goto error_exit; 266 } 267 } 268 } 269 270 if ( !vfs_isupdate(mp)) { 271 error = msdosfs_mount(devvp, mp, context); 272 if (error) 273 goto error_exit; /* msdosfs_mount cleaned up already */ 274 } 275 276 if (error == 0) 277 error = msdosfs_update_mp(mp, &args); 278 279 if (error == 0) 280 (void) msdosfs_vfs_statfs(mp, vfs_statfs(mp), context); 281 282 if (error) 283 msdosfs_vfs_unmount(mp, MNT_FORCE, context); /* NOTE: calls OSKextReleaseKextWithLoadTag */ 284 285 KERNEL_DEBUG_CONSTANT(MSDOSFS_VFS_MOUNT|DBG_FUNC_END, error, 0, 0, 0, 0); 286 return error; 287 288error_exit: 289#if MSDOSFS_AUTO_UNLOAD 290 OSKextReleaseKextWithLoadTag(OSKextGetCurrentLoadTag()); 291#endif 292 KERNEL_DEBUG_CONSTANT(MSDOSFS_VFS_MOUNT|DBG_FUNC_END, error, 0, 0, 0, 0); 293 return error; 294} 295 296/* 297 * Create a version 3 UUID from unique data in the SHA1 "name space". 298 * Version 3 UUIDs are derived using MD5 checksum. Here, the unique 299 * data is the 4-byte volume ID and the number of sectors (normalized 300 * to a 4-byte little endian value). 301 */ 302static void msdosfs_generate_volume_uuid(uuid_t result_uuid, uint8_t volumeID[4], uint32_t totalSectors) 303{ 304 MD5_CTX c; 305 uint8_t sectorsLittleEndian[4]; 306 307 UUID_DEFINE( kFSUUIDNamespaceSHA1, 0xB3, 0xE2, 0x0F, 0x39, 0xF2, 0x92, 0x11, 0xD6, 0x97, 0xA4, 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC ); 308 309 /* 310 * Normalize totalSectors to a little endian value so that this returns the 311 * same UUID regardless of endianness. 312 */ 313 putuint32(sectorsLittleEndian, totalSectors); 314 315 /* 316 * Generate an MD5 hash of our "name space", and our unique bits of data 317 * (the volume ID and total sectors). 318 */ 319 MD5Init(&c); 320 MD5Update(&c, kFSUUIDNamespaceSHA1, sizeof(uuid_t)); 321 MD5Update(&c, volumeID, 4); 322 MD5Update(&c, sectorsLittleEndian, sizeof(sectorsLittleEndian)); 323 MD5Final(result_uuid, &c); 324 325 /* Force the resulting UUID to be a version 3 UUID. */ 326 result_uuid[6] = (result_uuid[6] & 0x0F) | 0x30; 327 result_uuid[8] = (result_uuid[8] & 0x3F) | 0x80; 328} 329 330int msdosfs_mount(vnode_t devvp, struct mount *mp, vfs_context_t context) 331{ 332 struct msdosfsmount *pmp; 333 struct buf *bp; 334 dev_t dev = vnode_specrdev(devvp); 335 union bootsector *bsp; 336 struct byte_bpb50 *b50; 337 struct byte_bpb710 *b710; 338 uint32_t total_sectors; 339 uint32_t fat_sectors; 340 uint32_t clusters; 341 uint32_t fsinfo = 0; 342 int error; 343 struct vfsstatfs *vfsstatfs; 344 u_int8_t SecPerClust; 345 346 /* 347 * Disallow multiple mounts of the same device. 348 * Disallow mounting of a device that is currently in use 349 * (except for root, which might share swap device for miniroot). 350 * Flush out any old buffers remaining from a previous use. 351 * 352 *� Obsolete? 353 354 error = vfs_mountedon(devvp); 355 if (error) 356 return (error); 357 if (vcount(devvp) > 1 && devvp != rootvp) 358 return (EBUSY); 359 */ 360 361 error = buf_invalidateblks(devvp, BUF_WRITE_DATA, 0, 0); 362 if (error) 363 return (error); 364 365 vfs_setlocklocal(mp); 366 367 bp = NULL; /* both used in error_exit */ 368 pmp = NULL; 369 370 /* 371 * Read the boot sector of the filesystem, and then check the 372 * boot signature. If not a dos boot sector then error out. 373 * 374 * NOTE: 4096 is a maximum sector size in current... 375 */ 376 error = (int)buf_meta_bread(devvp, 0, 4096, vfs_context_ucred(context), &bp); 377 if (error) 378 goto error_exit; 379 buf_markaged(bp); 380 bsp = (union bootsector *)buf_dataptr(bp); 381 b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; 382 b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB; 383 384 385 /* [2699033] 386 * 387 * The first three bytes are an Intel x86 jump instruction. It should be one 388 * of the following forms: 389 * 0xE9 0x?? 0x?? 390 * 0xEB 0x?? 0x90 391 * where 0x?? means any byte value is OK. 392 * 393 * [5016947] 394 * 395 * Windows doesn't actually check the third byte if the first byte is 0xEB, 396 * so we don't either 397 */ 398 if (bsp->bs50.bsJump[0] != 0xE9 399 && bsp->bs50.bsJump[0] != 0xEB) 400 { 401 error = EINVAL; 402 goto error_exit; 403 } 404 405 MALLOC(pmp, struct msdosfsmount *, sizeof(*pmp), M_TEMP, M_WAITOK); 406 bzero((caddr_t)pmp, sizeof *pmp); 407 pmp->pm_mountp = mp; 408 pmp->pm_fat_lock = lck_mtx_alloc_init(msdosfs_lck_grp, msdosfs_lck_attr); 409 pmp->pm_rename_lock = lck_mtx_alloc_init(msdosfs_lck_grp, msdosfs_lck_attr); 410 411 /* 412 * Compute several useful quantities from the bpb in the 413 * bootsector. Copy in the dos 5 variant of the bpb then fix up 414 * the fields that are different between dos 5 and dos 3.3. 415 */ 416 SecPerClust = b50->bpbSecPerClust; 417 pmp->pm_BytesPerSec = getuint16(b50->bpbBytesPerSec); 418 pmp->pm_bpcluster = (u_int32_t) SecPerClust * (u_int32_t) pmp->pm_BytesPerSec; 419 pmp->pm_ResSectors = getuint16(b50->bpbResSectors); 420 pmp->pm_FATs = b50->bpbFATs; 421 pmp->pm_RootDirEnts = getuint16(b50->bpbRootDirEnts); 422 total_sectors = getuint16(b50->bpbSectors); 423 if (total_sectors == 0) 424 total_sectors = getuint32(b50->bpbHugeSectors); 425 fat_sectors = getuint16(b50->bpbFATsecs); 426 if (fat_sectors == 0) 427 fat_sectors = getuint32(b710->bpbBigFATsecs); 428 pmp->pm_label_cluster = CLUST_EOFE; /* Assume there is no label in the root */ 429 430 /* 431 * Check a few values (could do some more): 432 * - logical sector size: power of 2, >= block size 433 * - sectors per cluster: power of 2, >= 1 434 * - number of sectors: >= 1, <= size of partition 435 * - number of FAT sectors > 0 (too large values handled later) 436 */ 437 if (total_sectors == 0) 438 { 439 printf("msdosfs_mount: invalid total sectors (%u)\n", total_sectors); 440 error = EINVAL; 441 goto error_exit; 442 } 443 if (fat_sectors == 0) 444 { 445 printf("msdosfs_mount: invalid sectors per FAT (%u)\n", fat_sectors); 446 error = EINVAL; 447 goto error_exit; 448 } 449 if (SecPerClust == 0 || (SecPerClust & (SecPerClust - 1))) 450 { 451 printf("msdosfs_mount: invalid sectors per cluster (%u)\n", SecPerClust); 452 error = EINVAL; 453 goto error_exit; 454 } 455 if ((pmp->pm_BytesPerSec < DEV_BSIZE) || 456 (pmp->pm_BytesPerSec > 4096) || 457 (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1))) 458 { 459 printf("msdosfs_mount: invalid bytes per sector (%u)\n", pmp->pm_BytesPerSec); 460 error = EINVAL; 461 goto error_exit; 462 } 463 464 /* calculate the ratio of sector size to device block size */ 465 error = VNOP_IOCTL(devvp, DKIOCGETBLOCKSIZE, (caddr_t) &pmp->pm_BlockSize, 0, context); 466 if (error) { 467 error = ENXIO; 468 goto error_exit; 469 } 470 pmp->pm_BlocksPerSec = pmp->pm_BytesPerSec / pmp->pm_BlockSize; 471 pmp->pm_bnshift = ffs(pmp->pm_BlockSize) - 1; 472 473 /* Get the device's physical sector size */ 474 error = VNOP_IOCTL(devvp, DKIOCGETPHYSICALBLOCKSIZE, (caddr_t) &pmp->pm_PhysBlockSize, 0, context); 475 if (error) 476 pmp->pm_PhysBlockSize = pmp->pm_BlockSize; 477 478 /* 479 * For now, assume a FAT12 or FAT16 volume with a dedicated root directory. 480 * We need these values before we can figure out how many clusters (and 481 * thus whether the volume is FAT32 or not). Start by calculating sectors. 482 */ 483 pmp->pm_rootdirblk = (pmp->pm_ResSectors + (pmp->pm_FATs * fat_sectors)); 484 pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct dosdirentry) + pmp->pm_BytesPerSec - 1) / pmp->pm_BytesPerSec; 485 pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; 486 487 /* Change the root directory values to physical (device) blocks */ 488 pmp->pm_rootdirblk *= pmp->pm_BlocksPerSec; 489 pmp->pm_rootdirsize *= pmp->pm_BlocksPerSec; 490 491 if (fat_sectors > total_sectors || 492 pmp->pm_rootdirblk < fat_sectors || /* Catch numeric overflow! */ 493 pmp->pm_firstcluster + SecPerClust > total_sectors) 494 { 495 /* We think there isn't room for even a single cluster. */ 496 printf("msdosfs_mount: invalid configuration; no room for clusters\n"); 497 error = EINVAL; 498 goto error_exit; 499 } 500 501 /* 502 * Usable clusters are numbered starting at 2, so the maximum usable cluster 503 * is (number of clusters) + 1. Convert the pm_firstcluster to device blocks. 504 */ 505 pmp->pm_maxcluster = (total_sectors - pmp->pm_firstcluster) / SecPerClust + 1; 506 pmp->pm_firstcluster *= pmp->pm_BlocksPerSec; 507 508 /* 509 * Figure out the FAT type based on the number of clusters. 510 */ 511 if (pmp->pm_maxcluster < (CLUST_RSRVD & FAT12_MASK)) 512 { 513 pmp->pm_fatmask = FAT12_MASK; 514 pmp->pm_fatmult = 3; 515 pmp->pm_fatdiv = 2; 516 } 517 else if (pmp->pm_maxcluster < (CLUST_RSRVD & FAT16_MASK)) 518 { 519 pmp->pm_fatmask = FAT16_MASK; 520 pmp->pm_fatmult = 2; 521 pmp->pm_fatdiv = 1; 522 } 523 else if (pmp->pm_maxcluster < (CLUST_RSRVD & FAT32_MASK)) 524 { 525 pmp->pm_fatmask = FAT32_MASK; 526 pmp->pm_fatmult = 4; 527 pmp->pm_fatdiv = 1; 528 } 529 else 530 { 531 printf("msdosfs_mount: number of clusters (0x%x) is too large\n", pmp->pm_maxcluster + 1); 532 error = EINVAL; 533 goto error_exit; 534 } 535 536 /* See if FAT32 has its FAT mirrored or not. */ 537 if (FAT32(pmp) && (getuint16(b710->bpbExtFlags) & FATMIRROR)) 538 { 539 pmp->pm_curfat = getuint16(b710->bpbExtFlags) & FATNUM; 540 } 541 else 542 { 543 pmp->pm_flags |= MSDOSFS_FATMIRROR; 544 } 545 546 /* 547 * Sanity check some differences between FAT32 and FAT12/16. 548 * Also set up FAT32's root directory and FSInfo sector. 549 */ 550 if (FAT32(pmp)) 551 { 552 fsinfo = getuint16(b710->bpbFSInfo); 553 pmp->pm_rootdirblk = getuint32(b710->bpbRootClust); 554 if (pmp->pm_rootdirblk < CLUST_FIRST || pmp->pm_rootdirblk > pmp->pm_maxcluster) 555 { 556 printf("msdosfs_mount: FAT32 root starting cluster (%u) out of range (%u..%u)\n", 557 pmp->pm_rootdirblk, CLUST_FIRST, pmp->pm_maxcluster); 558 error = EINVAL; 559 goto error_exit; 560 } 561 if (pmp->pm_RootDirEnts) 562 { 563 printf("msdosfs_mount: FAT32 has non-zero root directory count\n"); 564 error = EINVAL; 565 goto error_exit; 566 } 567 if (getuint16(b710->bpbFSVers) != 0) 568 { 569 printf("msdosfs_mount: FAT32 has non-zero version\n"); 570 error = EINVAL; 571 goto error_exit; 572 } 573 if (getuint16(b50->bpbSectors) != 0) 574 { 575 printf("msdosfs_mount: FAT32 has 16-bit total sectors\n"); 576 error = EINVAL; 577 goto error_exit; 578 } 579 if (getuint16(b50->bpbFATsecs) != 0) 580 { 581 printf("msdosfs_mount: FAT32 has 16-bit FAT sectors\n"); 582 error = EINVAL; 583 goto error_exit; 584 } 585 } 586 else 587 { 588 if (pmp->pm_RootDirEnts == 0) 589 { 590 printf("msdosfs_mount: FAT12/16 has zero-length root directory\n"); 591 error = EINVAL; 592 goto error_exit; 593 } 594 if (total_sectors < 0x10000 && getuint16(b50->bpbSectors) == 0) 595 { 596 printf("msdosfs_mount: Warning: FAT12/16 total sectors (%u) fit in 16 bits, but stored in 32 bits\n", total_sectors); 597 } 598 if (getuint16(b50->bpbFATsecs) == 0) 599 { 600 printf("msdosfs_mount: Warning: FAT12/16 has 32-bit FAT sectors\n"); 601 } 602 } 603 604 /* 605 * Compute number of clusters this FAT could hold based on its total size. 606 * Pin the maximum cluster number to the size of the FAT. 607 * NOTE: We have to do this AFTER determining the FAT type. If we did this 608 * before, we could end up deducing a different FAT type than what's actually 609 * on disk, and that would be very bad. 610 */ 611 clusters = fat_sectors * pmp->pm_BytesPerSec; /* Size of FAT in bytes */ 612 clusters *= pmp->pm_fatdiv; 613 clusters /= pmp->pm_fatmult; /* Max number of clusters, rounded down */ 614 if (pmp->pm_maxcluster >= clusters) { 615 printf("msdosfs_mount: Warning: number of clusters (%d) exceeds FAT " 616 "capacity (%d)\n", pmp->pm_maxcluster + 1, clusters); 617 pmp->pm_maxcluster = clusters - 1; 618 } 619 620 /* 621 * Pin the maximum cluster number based on the number of sectors the device 622 * is reporting (in case that is smaller than the total sectors field(s) 623 * in the boot sector). 624 */ 625 uint64_t block_count; 626 error = VNOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t) &block_count, 0, context); 627 if (error == 0 && block_count < total_sectors) 628 { 629 if (pmp->pm_firstcluster + SecPerClust > block_count) 630 { 631 printf("msdosfs_mount: device sector count (%llu) too small; no room for clusters\n", block_count); 632 error = EINVAL; 633 goto error_exit; 634 } 635 636 uint32_t maxcluster = (uint32_t)((block_count - pmp->pm_firstcluster) / SecPerClust + 1); 637 if (maxcluster < pmp->pm_maxcluster) 638 { 639 printf("msdosfs_mount: device sector count (%llu) is less than volume sector count (%u); limiting maximum cluster to %u (was %u)\n", 640 block_count, total_sectors, maxcluster, pmp->pm_maxcluster); 641 pmp->pm_maxcluster = maxcluster; 642 } 643 else 644 { 645 printf("msdosfs_mount: device sector count (%llu) is less than volume sector count (%u)\n", 646 block_count, total_sectors); 647 } 648 } 649 650 /* 651 * Set up values for accessing the FAT. 652 */ 653 if (FAT12(pmp)) 654 pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec; 655 else 656 pmp->pm_fatblocksize = PAGE_SIZE; 657 pmp->pm_fat_bytes = fat_sectors * pmp->pm_BytesPerSec; 658 659 /* 660 * Compute mask and shift value for isolating cluster relative byte 661 * offsets and cluster numbers from a file offset. 662 */ 663 pmp->pm_crbomask = pmp->pm_bpcluster - 1; 664 pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1; 665 666 /* 667 * Compute an "optimal" I/O size based on the largest I/O that both 668 * UBC and the device can handle. 669 */ 670 { 671 u_int32_t temp_size; 672 struct vfsioattr ioattr; 673 674 pmp->pm_iosize = ubc_upl_maxbufsize(); 675 vfs_ioattr(mp, &ioattr); 676 if (ioattr.io_maxreadcnt < pmp->pm_iosize) 677 pmp->pm_iosize = ioattr.io_maxreadcnt; 678 if (ioattr.io_maxwritecnt < pmp->pm_iosize) 679 pmp->pm_iosize = ioattr.io_maxwritecnt; 680 temp_size = ioattr.io_segreadcnt * ioattr.io_maxsegreadsize; 681 if (temp_size < pmp->pm_iosize) 682 pmp->pm_iosize = temp_size; 683 temp_size = ioattr.io_segwritecnt * ioattr.io_maxsegwritesize; 684 if (temp_size < pmp->pm_iosize) 685 pmp->pm_iosize = temp_size; 686 687 /* 688 * If the device returned bogus values, like zeroes, pin the optimal 689 * size to the cluster size. 690 */ 691 if (pmp->pm_iosize < pmp->pm_bpcluster) 692 pmp->pm_iosize = pmp->pm_bpcluster; 693 } 694 695 /* Copy volume label and serial number from boot sector into mount point */ 696 { 697 struct extboot *extboot; 698 int i; 699 u_char uc; 700 701 /* Start out assuming no label (empty string) */ 702 pmp->pm_label[0] = '\0'; 703 704 if (FAT32(pmp)) { 705 extboot = (struct extboot *) bsp->bs710.bsExt; 706 } else { 707 extboot = (struct extboot *) bsp->bs50.bsExt; 708 } 709 710 if (extboot->exBootSignature == EXBOOTSIG) { 711 pmp->pm_flags |= MSDOSFS_HAS_EXT_BOOT; 712 713 /* Copy the volume serial number into the mount point. */ 714 bcopy(extboot->exVolumeID, pmp->pm_volume_serial_num, sizeof(pmp->pm_volume_serial_num)); 715 716 /* 717 * See if the volume has a non-zero ID; if so, turn it into a UUID. 718 * The field is odd aligned, so don't cast to uint32_t. 719 */ 720 if (pmp->pm_volume_serial_num[0] || pmp->pm_volume_serial_num[1] || 721 pmp->pm_volume_serial_num[2] || pmp->pm_volume_serial_num[3]) 722 { 723 pmp->pm_flags |= MSDOSFS_HAS_VOL_UUID; 724 msdosfs_generate_volume_uuid(pmp->pm_uuid, pmp->pm_volume_serial_num, total_sectors); 725 } 726 727 /* 728 * Copy the label from the boot sector into the mount point. 729 * 730 * We don't call msdosfs_dos2unicodefn() because it assumes the last three 731 * characters are an extension, and it will put a period before the 732 * extension. 733 */ 734 for (i=0; i<SHORT_NAME_LEN; i++) { 735 uc = extboot->exVolumeLabel[i]; 736 if (i==0 && uc == SLOT_E5) 737 uc = 0xE5; 738 pmp->pm_label[i] = (uc < 0x80 || uc > 0x9F ? uc : dos2unicode[uc - 0x80]); 739 } 740 741 /* Remove trailing spaces, add NUL terminator */ 742 for (i=10; i>=0 && pmp->pm_label[i]==' '; --i) 743 ; 744 pmp->pm_label[i+1] = '\0'; 745 } 746 } 747 748 /* 749 * Release the bootsector buffer. 750 */ 751 buf_brelse(bp); 752 bp = NULL; 753 754 /* 755 * We always reset the "next allocation" pointer to the start of the volume 756 * on mount. FAT12 and FAT16 don't have a way to persistently store it, and 757 * we need a valid value in all cases. 758 * 759 * For FAT32, we ignore the value in the FSInfo sector so that we'll tend 760 * to put allocations toward the start of the volume. This is a trade 761 * off. The benefits are that reads and writes to sectors near the start 762 * of the volume are often faster, and some broken devices report more 763 * sectors than they can actually access. The cost is large volumes with 764 * all clusters near the start of the volume already allocated; we'll 765 * have to read through that part of the FAT once, which we could have 766 * skipped if we didn't ignore the "next free cluster" in the FSInfo sector. 767 */ 768 pmp->pm_nxtfree = CLUST_FIRST; 769 770 /* 771 * Check FSInfo. 772 */ 773 if (fsinfo) { 774 struct fsinfo *fp; 775 u_int32_t log_per_phys; 776 777 /* Convert FSInfo logical sector number to device block number */ 778 fsinfo *= pmp->pm_BytesPerSec / pmp->pm_BlockSize; 779 780 /* 781 * %%% We want to read/write an entire physical sector when we access 782 * %%% the FSInfo sector. So precompute the starting logical sector 783 * %%% number, size of the physical sector, and offset of FSInfo from 784 * %%% the start of the physical sector. 785 */ 786 log_per_phys = pmp->pm_PhysBlockSize / pmp->pm_BlockSize; 787 if ((rounddown(fsinfo,log_per_phys) + log_per_phys) <= pmp->pm_ResSectors) { 788 pmp->pm_fsinfo_sector = rounddown(fsinfo,log_per_phys); 789 pmp->pm_fsinfo_size = pmp->pm_PhysBlockSize; 790 pmp->pm_fsinfo_offset = (fsinfo % log_per_phys) * pmp->pm_BlockSize; 791 } else { 792 pmp->pm_fsinfo_sector = fsinfo; 793 pmp->pm_fsinfo_size = pmp->pm_BlockSize; 794 pmp->pm_fsinfo_offset = 0; 795 } 796 797 /* 798 * The FSInfo sector occupies pm_BytesPerSec bytes on disk, 799 * but only 512 of those have meaningful contents. There's no point 800 * in reading all pm_BytesPerSec bytes if the device block size is 801 * smaller. So just use the device block size here. 802 */ 803 error = buf_meta_bread(devvp, pmp->pm_fsinfo_sector, pmp->pm_fsinfo_size, vfs_context_ucred(context), &bp); 804 if (error) 805 goto error_exit; 806 fp = (struct fsinfo *)(buf_dataptr(bp) + pmp->pm_fsinfo_offset); 807 if (!bcmp(fp->fsisig1, "RRaA", 4) 808 && !bcmp(fp->fsisig2, "rrAa", 4) 809 && !bcmp(fp->fsisig3, "\0\0\125\252", 4)) 810 { 811 pmp->pm_nxtfree = getuint32(fp->fsinxtfree); 812 pmp->pm_freeclustercount = getuint32(fp->fsinfree); 813 } else { 814 printf("msdosfs_mount: FSInfo has bad signature\n"); 815 pmp->pm_fsinfo_size = 0; 816 } 817 buf_brelse(bp); 818 bp = NULL; 819 } 820 821 /* 822 * Check and validate (or perhaps invalidate?) the fsinfo structure? XXX 823 */ 824 825 /* 826 * If they want fat updates to be synchronous then let them suffer 827 * the performance degradation in exchange for the on disk copy of 828 * the fat being correct just about all the time. I suppose this 829 * would be a good thing to turn on if the kernel is still flakey. 830 */ 831 if (vfs_issynchronous(mp)) 832 pmp->pm_flags |= MSDOSFSMNT_WAITONFAT; 833 834 /* 835 * msdosfs_fat_init_vol() needs pm_devvp. 836 */ 837 pmp->pm_dev = dev; 838 pmp->pm_devvp = devvp; 839 840 /* 841 * Set up the per-volume FAT structures, including 842 * the in-use map. 843 */ 844 error = msdosfs_fat_init_vol(pmp); 845 if (error != 0) 846 goto error_exit; 847 848 /* 849 * Initialize a timer to automatically sync shortly after writing. 850 */ 851 pmp->pm_sync_timer = thread_call_allocate(msdosfs_meta_sync_callback, pmp); 852 if (pmp->pm_sync_timer == NULL) 853 { 854 error = ENOMEM; 855 goto error_exit; 856 } 857 858 /* 859 * Set up our private data pointer for use by other routines. 860 */ 861 vfs_setfsprivate(mp, (void *)pmp); 862 863 /* 864 * Look through the root directory for volume name, and Windows hibernation. 865 */ 866 error = msdosfs_scan_root_dir(mp, context); 867 if (error) 868 { 869 if (error == EIO && vfs_isrdwr(mp)) 870 { 871 (void) msdosfs_markvoldirty(pmp, 1); /* Verify/repair the volume next time. */ 872 } 873 goto error_exit; 874 } 875 876 /* 877 * NOTE: we have to call vfs_isrdonly here, not cache the value from earlier. 878 * It is possible that msdosfs_scan_root_dir made the mount read-only due to a 879 * Windows hibernation image. 880 */ 881 if (vfs_isrdonly(mp)) 882 pmp->pm_flags |= MSDOSFSMNT_RONLY; 883 else { 884 /* [2753891] Mark the volume dirty while it is mounted read/write */ 885 if ((error = msdosfs_markvoldirty(pmp, 1)) != 0) 886 goto error_exit; 887 } 888 889 /* 890 * Fill in the statvfs fields that are constant (not updated by msdosfs_vfs_statfs) 891 */ 892 vfsstatfs = vfs_statfs(mp); 893 vfsstatfs->f_bsize = pmp->pm_bpcluster; 894 vfsstatfs->f_iosize = pmp->pm_iosize; 895 /* Clusters are numbered from 2..pm_maxcluster, so pm_maxcluster - 2 + 1 of them */ 896 vfsstatfs->f_blocks = pmp->pm_maxcluster - 1; 897 vfsstatfs->f_fsid.val[0] = dev; 898 vfsstatfs->f_fsid.val[1] = vfs_typenum(mp); 899 900 vfs_setflags(mp, MNT_IGNORE_OWNERSHIP); 901 902 903 return 0; 904 905error_exit: 906 if (bp) 907 buf_brelse(bp); 908 if (pmp) { 909 if (pmp->pm_sync_timer) 910 { 911 thread_call_cancel(pmp->pm_sync_timer); 912 thread_call_free(pmp->pm_sync_timer); 913 pmp->pm_sync_timer = NULL; 914 } 915 916 (void) vflush(mp, NULLVP, SKIPSYSTEM|FORCECLOSE); 917 918 msdosfs_fat_uninit_vol(pmp); 919 920 (void) vflush(mp, NULLVP, FORCECLOSE); 921 922 lck_mtx_free(pmp->pm_fat_lock, msdosfs_lck_grp); 923 lck_mtx_free(pmp->pm_rename_lock, msdosfs_lck_grp); 924 925 FREE(pmp, M_TEMP); 926 927 vfs_setfsprivate(mp, (void *)NULL); 928 } 929 return (error); 930} 931 932/* 933 * Make a filesystem operational. 934 * Nothing to do at the moment. 935 */ 936/* ARGSUSED */ 937int msdosfs_start(struct mount *mp, int flags, vfs_context_t context) 938{ 939#pragma unused (mp) 940#pragma unused (flags) 941#pragma unused (context) 942 return (0); 943} 944 945/* 946 * Unmount the filesystem described by mp. 947 */ 948int msdosfs_vfs_unmount(struct mount *mp, int mntflags, vfs_context_t context) 949{ 950 struct msdosfsmount *pmp; 951 int error, flags; 952 int force; 953 954 pmp = VFSTOMSDOSFS(mp); 955 956 KERNEL_DEBUG_CONSTANT(MSDOSFS_VFS_UNMOUNT|DBG_FUNC_START, pmp, 0, 0, 0, 0); 957 958 flags = SKIPSYSTEM; 959 force = 0; 960 if (mntflags & MNT_FORCE) { 961 flags |= FORCECLOSE; 962 force = 1; 963 } 964 965 /* 966 * Cancel any pending timers for this volume. Then wait for any timers 967 * which have fired, but whose callbacks have not yet completed. 968 */ 969 if (pmp->pm_sync_timer) 970 { 971 struct timespec ts = {0, 100000000}; /* 0.1 seconds */ 972 973 /* 974 * Cancel any timers that have been scheduled, but have not 975 * fired yet. NOTE: The kernel considers a timer complete as 976 * soon as it starts your callback, so the kernel does not 977 * keep track of the number of callbacks in progress. 978 */ 979 if (thread_call_cancel(pmp->pm_sync_timer)) 980 OSDecrementAtomic(&pmp->pm_sync_incomplete); 981 thread_call_free(pmp->pm_sync_timer); 982 pmp->pm_sync_timer = NULL; 983 984 /* 985 * This waits for all of the callbacks that were entered before 986 * we did thread_call_cancel above, but have not completed yet. 987 */ 988 while(pmp->pm_sync_incomplete > 0) 989 { 990 msleep(&pmp->pm_sync_incomplete, NULL, PWAIT, "msdosfs_vfs_unmount", &ts); 991 } 992 993 if (pmp->pm_sync_incomplete < 0) 994 panic("msdosfs_vfs_unmount: pm_sync_incomplete underflow!\n"); 995 } 996 997 error = vflush(mp, NULLVP, flags); 998 if (error && !force) 999 goto error_exit; 1000 1001 /* 1002 * [2753891] If the volume was mounted read/write, and no corruption 1003 * was detected, mark it clean now. 1004 */ 1005 if ((pmp->pm_flags & (MSDOSFSMNT_RONLY | MSDOSFS_CORRUPT)) == 0) { 1006 error = msdosfs_markvoldirty(pmp, 0); 1007 if (error && !force) 1008 goto error_exit; 1009 } 1010 1011 msdosfs_fat_uninit_vol(pmp); 1012 (void) vflush(mp, NULLVP, FORCECLOSE); 1013 VNOP_FSYNC(pmp->pm_devvp, MNT_WAIT, context); 1014 1015 lck_mtx_free(pmp->pm_fat_lock, msdosfs_lck_grp); 1016 lck_mtx_free(pmp->pm_rename_lock, msdosfs_lck_grp); 1017 1018 FREE(pmp, M_TEMP); 1019 1020 vfs_setfsprivate(mp, (void *)NULL); 1021 1022#if MSDOSFS_AUTO_UNLOAD 1023 OSKextReleaseKextWithLoadTag(OSKextGetCurrentLoadTag()); 1024#endif 1025 1026 /* 1027 * If "force" was set, we may get here with error != 0. Since we have 1028 * in fact completed the unmount (as best we can), we need to return 1029 * no error so that VFS can clean up our mount point. 1030 */ 1031 error = 0; 1032 1033error_exit: 1034 KERNEL_DEBUG_CONSTANT(MSDOSFS_VFS_UNMOUNT|DBG_FUNC_END, error, 0, 0, 0, 0); 1035 return (error); 1036} 1037 1038int msdosfs_vfs_root(struct mount *mp, vnode_t *vpp, vfs_context_t context) 1039{ 1040 struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 1041 struct denode *ndep; 1042 int error; 1043 1044 KERNEL_DEBUG_CONSTANT(MSDOSFS_VFS_ROOT|DBG_FUNC_START, pmp, 0, 0, 0, 0); 1045 1046 error = msdosfs_deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, NULLVP, NULL, &ndep, context); 1047 if (error) 1048 return (error); 1049 *vpp = DETOV(ndep); 1050 1051 KERNEL_DEBUG_CONSTANT(MSDOSFS_VFS_ROOT|DBG_FUNC_END, 0, ndep, 0, 0, 0); 1052 return 0; 1053} 1054 1055 1056int msdosfs_vfs_statfs(struct mount *mp, struct vfsstatfs *sbp, vfs_context_t context) 1057{ 1058#pragma unused (context) 1059 struct msdosfsmount *pmp; 1060 1061 pmp = VFSTOMSDOSFS(mp); 1062 1063 KERNEL_DEBUG_CONSTANT(MSDOSFS_VFS_STATFS|DBG_FUNC_START, pmp, 0, 0, 0, 0); 1064 1065 /* 1066 * � VFS fills in everything from a cached copy. 1067 * We only need to fill in fields that can change. 1068 */ 1069 sbp->f_bfree = pmp->pm_freeclustercount; 1070 sbp->f_bavail = pmp->pm_freeclustercount; 1071 sbp->f_bused = sbp->f_blocks - sbp->f_bavail; 1072 sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ 1073 sbp->f_ffree = 0; /* what to put in here? */ 1074 vfs_name(mp, sbp->f_fstypename); 1075 1076 /* Subtypes (flavors) for MSDOS 1077 0 - FAT12 1078 1 - FAT16 1079 2 - FAT32 1080 */ 1081 if (pmp->pm_fatmask == FAT12_MASK) { 1082 sbp->f_fssubtype = 0; /* FAT12 */ 1083 } else if (pmp->pm_fatmask == FAT16_MASK) { 1084 sbp->f_fssubtype = 1; /* FAT16 */ 1085 } else { 1086 sbp->f_fssubtype = 2; /* FAT32 */ 1087 } 1088 1089 KERNEL_DEBUG_CONSTANT(MSDOSFS_VFS_STATFS|DBG_FUNC_END, 0, sbp->f_blocks, sbp->f_bfree, sbp->f_fssubtype, 0); 1090 return 0; 1091} 1092 1093 1094int msdosfs_vfs_getattr(mount_t mp, struct vfs_attr *attr, vfs_context_t context) 1095{ 1096#pragma unused (context) 1097 struct msdosfsmount *pmp; 1098 1099 pmp = VFSTOMSDOSFS(mp); 1100 1101 KERNEL_DEBUG_CONSTANT(MSDOSFS_VFS_GETATTR|DBG_FUNC_START, pmp, attr->f_active, 0, 0, 0); 1102 1103 /* FAT doesn't track the object counts */ 1104 1105 VFSATTR_RETURN(attr, f_bsize, pmp->pm_bpcluster); 1106 VFSATTR_RETURN(attr, f_iosize, pmp->pm_iosize); 1107 /* Clusters are numbered from 2..pm_maxcluster, so pm_maxcluster - 2 + 1 of them */ 1108 VFSATTR_RETURN(attr, f_blocks, pmp->pm_maxcluster - 1); 1109 VFSATTR_RETURN(attr, f_bfree, pmp->pm_freeclustercount); 1110 VFSATTR_RETURN(attr, f_bavail, pmp->pm_freeclustercount); 1111 VFSATTR_RETURN(attr, f_bused, attr->f_blocks - attr->f_bfree); 1112 1113 /* FAT doesn't have a fixed limit on the number of file nodes */ 1114 1115 if (VFSATTR_IS_ACTIVE(attr, f_fsid)) { 1116 attr->f_fsid.val[0] = pmp->pm_dev; 1117 attr->f_fsid.val[1] = vfs_typenum(mp); 1118 VFSATTR_SET_SUPPORTED(attr, f_fsid); 1119 } 1120 1121 if (VFSATTR_IS_ACTIVE(attr, f_capabilities)) { 1122 attr->f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] = 1123 VOL_CAP_FMT_SYMBOLICLINKS | 1124 VOL_CAP_FMT_NO_ROOT_TIMES | 1125 VOL_CAP_FMT_CASE_PRESERVING | 1126 VOL_CAP_FMT_FAST_STATFS | 1127 VOL_CAP_FMT_HIDDEN_FILES ; 1128 attr->f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] = 1129 VOL_CAP_INT_VOL_RENAME | 1130 VOL_CAP_INT_ADVLOCK | 1131 VOL_CAP_INT_FLOCK ; 1132 attr->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED1] = 0; 1133 attr->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED2] = 0; 1134 1135 attr->f_capabilities.valid[VOL_CAPABILITIES_FORMAT] = 1136 VOL_CAP_FMT_PERSISTENTOBJECTIDS | 1137 VOL_CAP_FMT_SYMBOLICLINKS | 1138 VOL_CAP_FMT_HARDLINKS | 1139 VOL_CAP_FMT_JOURNAL | 1140 VOL_CAP_FMT_JOURNAL_ACTIVE | 1141 VOL_CAP_FMT_NO_ROOT_TIMES | 1142 VOL_CAP_FMT_SPARSE_FILES | 1143 VOL_CAP_FMT_ZERO_RUNS | 1144 VOL_CAP_FMT_CASE_SENSITIVE | 1145 VOL_CAP_FMT_CASE_PRESERVING | 1146 VOL_CAP_FMT_FAST_STATFS | 1147 VOL_CAP_FMT_2TB_FILESIZE | 1148 VOL_CAP_FMT_OPENDENYMODES | 1149 VOL_CAP_FMT_HIDDEN_FILES ; 1150 attr->f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] = 1151 VOL_CAP_INT_SEARCHFS | 1152 VOL_CAP_INT_ATTRLIST | 1153 VOL_CAP_INT_NFSEXPORT | 1154 VOL_CAP_INT_READDIRATTR | 1155 VOL_CAP_INT_EXCHANGEDATA | 1156 VOL_CAP_INT_COPYFILE | 1157 VOL_CAP_INT_ALLOCATE | 1158 VOL_CAP_INT_VOL_RENAME | 1159 VOL_CAP_INT_ADVLOCK | 1160 VOL_CAP_INT_FLOCK | 1161 VOL_CAP_INT_MANLOCK ; 1162 attr->f_capabilities.valid[VOL_CAPABILITIES_RESERVED1] = 0; 1163 attr->f_capabilities.valid[VOL_CAPABILITIES_RESERVED2] = 0; 1164 VFSATTR_SET_SUPPORTED(attr, f_capabilities); 1165 } 1166 1167 if (VFSATTR_IS_ACTIVE(attr, f_attributes)) { 1168 attr->f_attributes.validattr.commonattr = 1169 ATTR_CMN_NAME | 1170 ATTR_CMN_DEVID | 1171 ATTR_CMN_FSID | 1172 ATTR_CMN_OBJTYPE | 1173 ATTR_CMN_OBJTAG | 1174 ATTR_CMN_OBJID | 1175 /* ATTR_CMN_OBJPERMANENTID | */ 1176 ATTR_CMN_PAROBJID | 1177 /* ATTR_CMN_SCRIPT | */ 1178 ATTR_CMN_CRTIME | 1179 ATTR_CMN_MODTIME | 1180 ATTR_CMN_CHGTIME | 1181 ATTR_CMN_ACCTIME | 1182 /* ATTR_CMN_BKUPTIME | */ 1183 /* ATTR_CMN_FNDRINFO | */ 1184 ATTR_CMN_OWNERID | 1185 ATTR_CMN_GRPID | 1186 ATTR_CMN_ACCESSMASK | 1187 ATTR_CMN_FLAGS | 1188 ATTR_CMN_USERACCESS | 1189 /* ATTR_CMN_EXTENDED_SECURITY | */ 1190 /* ATTR_CMN_UUID | */ 1191 /* ATTR_CMN_GRPUUID | */ 1192 0; 1193 attr->f_attributes.validattr.volattr = 1194 ATTR_VOL_FSTYPE | 1195 /* ATTR_VOL_SIGNATURE */ 1196 ATTR_VOL_SIZE | 1197 ATTR_VOL_SPACEFREE | 1198 ATTR_VOL_SPACEAVAIL | 1199 ATTR_VOL_MINALLOCATION | 1200 ATTR_VOL_ALLOCATIONCLUMP | 1201 ATTR_VOL_IOBLOCKSIZE | 1202 /* ATTR_VOL_OBJCOUNT */ 1203 /* ATTR_VOL_FILECOUNT */ 1204 /* ATTR_VOL_DIRCOUNT */ 1205 /* ATTR_VOL_MAXOBJCOUNT */ 1206 ATTR_VOL_MOUNTPOINT | 1207 ATTR_VOL_NAME | 1208 ATTR_VOL_MOUNTFLAGS | 1209 ATTR_VOL_MOUNTEDDEVICE | 1210 /* ATTR_VOL_ENCODINGSUSED */ 1211 ATTR_VOL_CAPABILITIES | 1212 ATTR_VOL_ATTRIBUTES; 1213 attr->f_attributes.validattr.dirattr = 1214 ATTR_DIR_LINKCOUNT | 1215 /* ATTR_DIR_ENTRYCOUNT */ 1216 ATTR_DIR_MOUNTSTATUS; 1217 attr->f_attributes.validattr.fileattr = 1218 ATTR_FILE_LINKCOUNT | 1219 ATTR_FILE_TOTALSIZE | 1220 ATTR_FILE_ALLOCSIZE | 1221 /* ATTR_FILE_IOBLOCKSIZE */ 1222 ATTR_FILE_DEVTYPE | 1223 /* ATTR_FILE_FORKCOUNT */ 1224 /* ATTR_FILE_FORKLIST */ 1225 ATTR_FILE_DATALENGTH | 1226 ATTR_FILE_DATAALLOCSIZE | 1227 ATTR_FILE_RSRCLENGTH | 1228 ATTR_FILE_RSRCALLOCSIZE; 1229 attr->f_attributes.validattr.forkattr = 0; 1230 attr->f_attributes.nativeattr.commonattr = 1231 ATTR_CMN_NAME | 1232 ATTR_CMN_DEVID | 1233 ATTR_CMN_FSID | 1234 ATTR_CMN_OBJTYPE | 1235 ATTR_CMN_OBJTAG | 1236 ATTR_CMN_OBJID | 1237 /* ATTR_CMN_OBJPERMANENTID | */ 1238 ATTR_CMN_PAROBJID | 1239 /* ATTR_CMN_SCRIPT | */ 1240 ATTR_CMN_CRTIME | 1241 ATTR_CMN_MODTIME | 1242 /* ATTR_CMN_CHGTIME | */ /* Supported but not native */ 1243 ATTR_CMN_ACCTIME | 1244 /* ATTR_CMN_BKUPTIME | */ 1245 /* ATTR_CMN_FNDRINFO | */ 1246 /* ATTR_CMN_OWNERID | */ /* Supported but not native */ 1247 /* ATTR_CMN_GRPID | */ /* Supported but not native */ 1248 /* ATTR_CMN_ACCESSMASK | */ /* Supported but not native */ 1249 ATTR_CMN_FLAGS | 1250 ATTR_CMN_USERACCESS | 1251 /* ATTR_CMN_EXTENDED_SECURITY | */ 1252 /* ATTR_CMN_UUID | */ 1253 /* ATTR_CMN_GRPUUID | */ 1254 0; 1255 attr->f_attributes.nativeattr.volattr = 1256 ATTR_VOL_FSTYPE | 1257 /* ATTR_VOL_SIGNATURE */ 1258 ATTR_VOL_SIZE | 1259 ATTR_VOL_SPACEFREE | 1260 ATTR_VOL_SPACEAVAIL | 1261 ATTR_VOL_MINALLOCATION | 1262 ATTR_VOL_ALLOCATIONCLUMP | 1263 ATTR_VOL_IOBLOCKSIZE | 1264 /* ATTR_VOL_OBJCOUNT */ 1265 /* ATTR_VOL_FILECOUNT */ 1266 /* ATTR_VOL_DIRCOUNT */ 1267 /* ATTR_VOL_MAXOBJCOUNT */ 1268 ATTR_VOL_MOUNTPOINT | 1269 ATTR_VOL_NAME | 1270 ATTR_VOL_MOUNTFLAGS | 1271 ATTR_VOL_MOUNTEDDEVICE | 1272 /* ATTR_VOL_ENCODINGSUSED */ 1273 ATTR_VOL_CAPABILITIES | 1274 ATTR_VOL_ATTRIBUTES; 1275 attr->f_attributes.nativeattr.dirattr = 0; 1276 attr->f_attributes.nativeattr.fileattr = 1277 /* ATTR_FILE_LINKCOUNT | */ /* Supported but not native */ 1278 ATTR_FILE_TOTALSIZE | 1279 ATTR_FILE_ALLOCSIZE | 1280 /* ATTR_FILE_IOBLOCKSIZE */ 1281 ATTR_FILE_DEVTYPE | 1282 /* ATTR_FILE_FORKCOUNT */ 1283 /* ATTR_FILE_FORKLIST */ 1284 ATTR_FILE_DATALENGTH | 1285 ATTR_FILE_DATAALLOCSIZE | 1286 ATTR_FILE_RSRCLENGTH | 1287 ATTR_FILE_RSRCALLOCSIZE; 1288 attr->f_attributes.nativeattr.forkattr = 0; 1289 VFSATTR_SET_SUPPORTED(attr, f_attributes); 1290 } 1291 1292 /* FAT doesn't have volume dates */ 1293 1294 if (VFSATTR_IS_ACTIVE(attr, f_fssubtype)) { 1295 /* Subtypes (flavors) for MSDOS 1296 0 - FAT12 1297 1 - FAT16 1298 2 - FAT32 1299 */ 1300 if (pmp->pm_fatmask == FAT12_MASK) { 1301 attr->f_fssubtype = 0; /* FAT12 */ 1302 } else if (pmp->pm_fatmask == FAT16_MASK) { 1303 attr->f_fssubtype = 1; /* FAT16 */ 1304 } else { 1305 attr->f_fssubtype = 2; /* FAT32 */ 1306 } 1307 VFSATTR_SET_SUPPORTED(attr, f_fssubtype); 1308 } 1309 1310 /* f_bsize returned above */ 1311 1312 if (VFSATTR_IS_ACTIVE(attr, f_vol_name)) { 1313 strlcpy(attr->f_vol_name, (char*)pmp->pm_label, MAXPATHLEN); 1314 VFSATTR_SET_SUPPORTED(attr, f_vol_name); 1315 } 1316 1317 if (VFSATTR_IS_ACTIVE(attr, f_uuid) && (pmp->pm_flags & MSDOSFS_HAS_VOL_UUID)) 1318 { 1319 memcpy(attr->f_uuid, pmp->pm_uuid, sizeof(uuid_t)); 1320 VFSATTR_SET_SUPPORTED(attr, f_uuid); 1321 } 1322 1323 KERNEL_DEBUG_CONSTANT(MSDOSFS_VFS_GETATTR|DBG_FUNC_END, pmp, attr->f_supported, 0, 0, 0); 1324 return 0; 1325} 1326 1327 1328int msdosfs_vfs_setattr(mount_t mp, struct vfs_attr *attr, vfs_context_t context) 1329{ 1330 int error = 0; 1331 struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 1332 1333 KERNEL_DEBUG_CONSTANT(MSDOSFS_VFS_SETATTR|DBG_FUNC_START, pmp, attr->f_active, 0, 0, 0); 1334 1335 if (VFSATTR_IS_ACTIVE(attr, f_vol_name)) 1336 { 1337 struct buf *bp = NULL; 1338 size_t i; 1339 size_t len; 1340 size_t unichars; 1341 u_int16_t c; 1342 u_int16_t volName[SHORT_NAME_LEN]; 1343 u_char label[SHORT_NAME_LEN]; 1344 1345 len = strlen(attr->f_vol_name); 1346 if (len > 63) 1347 return EINVAL; 1348 1349 /* Convert the UTF-8 to UTF-16 */ 1350 error = utf8_decodestr((u_int8_t*)attr->f_vol_name, len, volName, 1351 &unichars, sizeof(volName), 0, UTF_PRECOMPOSED); 1352 if (error) 1353 return error; 1354 unichars /= 2; /* Bytes to characters */ 1355 if (unichars > SHORT_NAME_LEN) 1356 return EINVAL; 1357 1358 /* 1359 * Convert from UTF-16 to local encoding (like a short name). 1360 * We can't call msdosfs_unicode_to_dos_name here because it 1361 * assumes a dot between the first 8 and last 3 characters. 1362 * 1363 * The specification doesn't say what syntax limitations exist 1364 * for volume labels. By experimentation, they appear to be 1365 * upper case only. I am assuming they are like short names, 1366 * but no period is assumed/required after the 8th character. 1367 */ 1368 1369 /* Name is trailing space padded, so init to all spaces. */ 1370 for (i=0; i<SHORT_NAME_LEN; ++i) 1371 label[i] = ' '; 1372 1373 for (i=0; i<unichars; ++i) { 1374 c = volName[i]; 1375 if (c < 0x100) 1376 c = l2u[c]; /* Convert to lower case */ 1377 if (c != ' ') /* Allow space to pass unchanged */ 1378 c = msdosfs_unicode2dos(c); /* Convert to local encoding */ 1379 if (c < 3) 1380 return EINVAL; /* Illegal char in name */ 1381 label[i] = c; 1382 } 1383 1384 /* Copy the UTF-8 to pmp->pm_label */ 1385 bcopy(attr->f_vol_name, pmp->pm_label, len); 1386 pmp->pm_label[len] = '\0'; 1387 1388 /* Update label in boot sector */ 1389 error = (int)buf_meta_bread(pmp->pm_devvp, 0, pmp->pm_BlockSize, vfs_context_ucred(context), &bp); 1390 if (!error) { 1391 if (FAT32(pmp)) 1392 bcopy(label, (char*)buf_dataptr(bp)+71, SHORT_NAME_LEN); 1393 else 1394 bcopy(label, (char*)buf_dataptr(bp)+43, SHORT_NAME_LEN); 1395 buf_bdwrite(bp); 1396 bp = NULL; 1397 } 1398 if (bp) 1399 buf_brelse(bp); 1400 bp = NULL; 1401 1402 /* 1403 * Update label in root directory, if any. For now, don't 1404 * create one if it doesn't exist (in case devices like 1405 * cameras don't understand them). 1406 */ 1407 if (pmp->pm_label_cluster != CLUST_EOFE) { 1408 error = msdosfs_readep(pmp, pmp->pm_label_cluster, pmp->pm_label_offset, &bp, NULL, context); 1409 if (!error) { 1410 bcopy(label, (char *)buf_dataptr(bp) + pmp->pm_label_offset, SHORT_NAME_LEN); 1411 buf_bdwrite(bp); 1412 bp = NULL; 1413 } 1414 if (bp) 1415 buf_brelse(bp); 1416 bp=NULL; 1417 } 1418 1419 if (error == 0) 1420 VFSATTR_SET_SUPPORTED(attr, f_vol_name); 1421 } 1422 1423 KERNEL_DEBUG_CONSTANT(MSDOSFS_VFS_SETATTR|DBG_FUNC_END, pmp, attr->f_supported, 0, 0, 0); 1424 return error; 1425} 1426 1427 1428struct msdosfs_sync_cargs { 1429 vfs_context_t context; 1430 int waitfor; 1431 int error; 1432}; 1433 1434 1435int msdosfs_sync_callback(vnode_t vp, void *cargs) 1436{ 1437 struct msdosfs_sync_cargs *args; 1438 struct denode *dep; 1439 int error; 1440 1441 /* 1442 * msdosfs_check_link creates a temporary vnode whose v_data is 1443 * NULL. It normally gets reclaimed very quickly, but it is 1444 * possible for a sync() to race with that reclaim. Since that 1445 * vnode doesn't have a denode to go with it, we can just ignore 1446 * it for the purposes of syncing. 1447 */ 1448 dep = VTODE(vp); 1449 if (dep == NULL) 1450 return VNODE_RETURNED; 1451 1452 args = (struct msdosfs_sync_cargs *)cargs; 1453 1454 /* 1455 * If this is a FAT vnode, then don't sync it here. It will be sync'ed 1456 * separately in msdosfs_vfs_sync. 1457 */ 1458 if (vnode_issystem(vp)) 1459 return VNODE_RETURNED; 1460 1461 lck_mtx_lock(dep->de_lock); 1462 1463 error = msdosfs_fsync_internal(vp, args->waitfor, 0, args->context); 1464 if (error) 1465 args->error = error; 1466 1467 lck_mtx_unlock(dep->de_lock); 1468 1469 return VNODE_RETURNED; 1470} 1471 1472 1473int msdosfs_vfs_sync(mp, waitfor, context) 1474 struct mount *mp; 1475 int waitfor; 1476 vfs_context_t context; 1477{ 1478 struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 1479 int error, allerror = 0; 1480 struct msdosfs_sync_cargs args; 1481 1482 if (pmp->pm_flags & MSDOSFSMNT_RONLY) { 1483 /* For read-only mounts, there's no syncing to do */ 1484 return 0; 1485 } 1486 1487 KERNEL_DEBUG_CONSTANT(MSDOSFS_VFS_SYNC|DBG_FUNC_START, pmp, 0, 0, 0, 0); 1488 1489 /* 1490 * Flush the FSInfo sector and all copies of the FAT. 1491 */ 1492 error = msdosfs_update_fsinfo(pmp, waitfor, context); 1493 if (error) 1494 allerror = error; 1495 1496 error = VNOP_FSYNC(pmp->pm_fat_active_vp, waitfor, context); 1497 if (error) 1498 allerror = error; 1499 if (pmp->pm_fat_mirror_vp) 1500 error = VNOP_FSYNC(pmp->pm_fat_mirror_vp, waitfor, context); 1501 if (error) 1502 allerror = error; 1503 1504 /* 1505 * Write back each (modified) denode. 1506 */ 1507 args.context = context; 1508 args.waitfor = waitfor; 1509 args.error = 0; 1510 /* 1511 * msdosfs_sync_callback will be called for each vnode 1512 * hung off of this mount point... the vnode will be 1513 * properly referenced and unreferenced around the callback 1514 */ 1515 vnode_iterate(mp, VNODE_ITERATE_ACTIVE, msdosfs_sync_callback, (void *)&args); 1516 1517 if (args.error) 1518 allerror = args.error; 1519 1520 /* 1521 * Flush directories. 1522 */ 1523 error = VNOP_FSYNC(pmp->pm_devvp, waitfor, context); 1524 if (error) 1525 allerror = error; 1526 1527 KERNEL_DEBUG_CONSTANT(MSDOSFS_VFS_SYNC|DBG_FUNC_END, allerror, 0, 0, 0, 0); 1528 return allerror; 1529} 1530 1531 1532struct vfsops msdosfs_vfsops = { 1533 msdosfs_vfs_mount, 1534 msdosfs_start, 1535 msdosfs_vfs_unmount, 1536 msdosfs_vfs_root, 1537 NULL, /* msdosfs_quotactl */ 1538 msdosfs_vfs_getattr, 1539 msdosfs_vfs_sync, 1540 NULL, /* msdosfs_vget */ 1541 NULL, /* msdosfs_fhtovp */ 1542 NULL, /* msdosfs_vptofh */ 1543 msdosfs_init, 1544 NULL, /* msdosfs_sysctl */ 1545 msdosfs_vfs_setattr, 1546 {0} 1547}; 1548 1549extern struct vnodeopv_desc msdosfs_vnodeop_opv_desc; 1550extern struct vnodeopv_desc msdosfs_fat_vnodeop_opv_desc; 1551static struct vnodeopv_desc *msdosfs_vnodeop_opv_desc_list[2] = 1552{ 1553 &msdosfs_vnodeop_opv_desc, 1554 &msdosfs_fat_vnodeop_opv_desc 1555}; 1556 1557 1558static vfstable_t msdosfs_vfsconf; 1559 1560int msdosfs_module_start(kmod_info_t *ki, void *data) 1561{ 1562#pragma unused(ki) 1563#pragma unused(data) 1564 errno_t error; 1565 struct vfs_fsentry vfe; 1566 1567 vfe.vfe_vfsops = &msdosfs_vfsops; 1568 vfe.vfe_vopcnt = 2; /* We just have vnode operations for regular files and directories, and the FAT */ 1569 vfe.vfe_opvdescs = msdosfs_vnodeop_opv_desc_list; 1570 strlcpy(vfe.vfe_fsname, "msdos", sizeof(vfe.vfe_fsname)); 1571 vfe.vfe_flags = VFS_TBLTHREADSAFE | VFS_TBLNOTYPENUM | VFS_TBLLOCALVOL | VFS_TBL64BITREADY | VFS_TBLREADDIR_EXTENDED; 1572 vfe.vfe_reserv[0] = 0; 1573 vfe.vfe_reserv[1] = 0; 1574 1575 error = vfs_fsadd(&vfe, &msdosfs_vfsconf); 1576 1577#if DEBUG 1578 if (!error) 1579 { 1580 sysctl_register_oid(&sysctl__vfs_generic_msdosfs); 1581 sysctl_register_oid(&sysctl__vfs_generic_msdosfs_meta_delay); 1582 } 1583#endif 1584 1585 return error ? KERN_FAILURE : KERN_SUCCESS; 1586} 1587 1588int msdosfs_module_stop(kmod_info_t *ki, void *data) 1589{ 1590#pragma unused(ki) 1591#pragma unused(data) 1592 errno_t error; 1593 1594 error = vfs_fsremove(msdosfs_vfsconf); 1595 1596 if (!error) 1597 { 1598 msdosfs_uninit(); 1599#if DEBUG 1600 sysctl_unregister_oid(&sysctl__vfs_generic_msdosfs_meta_delay); 1601 sysctl_unregister_oid(&sysctl__vfs_generic_msdosfs); 1602#endif 1603 } 1604 1605 return error ? KERN_FAILURE : KERN_SUCCESS; 1606} 1607 1608 1609/* 1610 * Look through the root directory for a volume label entry. 1611 * If found, use it to replace the label in the mount point. 1612 * Also look for a file with short name HIBERFIL.SYS; if it is less than 1613 * 4KiB in size, or has non-zero bytes in the first 4KiB, then force the 1614 * mount to read-only because Windows is hibernated on this volume. 1615 */ 1616int msdosfs_scan_root_dir(struct mount *mp, vfs_context_t context) 1617{ 1618 int error; 1619 struct msdosfsmount *pmp; 1620 vnode_t vp = NULL; 1621 struct buf *bp = NULL; 1622 uint32_t frcn; /* file relative cluster number in root directory */ 1623 daddr64_t bn; /* block number of current dir block */ 1624 uint32_t cluster; /* cluster number of current dir block */ 1625 uint32_t blsize; /* size of current dir block */ 1626 unsigned blkoff; /* dir entry offset within current dir block */ 1627 unsigned diroff; /* offset from start of directory */ 1628 struct dosdirentry *dep = NULL; 1629 struct denode *root; 1630 u_int16_t unichars; 1631 u_int16_t ucfn[12]; 1632 u_char uc; 1633 int i; 1634 size_t outbytes; 1635 char *bdata = NULL; 1636 1637 pmp = VFSTOMSDOSFS(mp); 1638 1639 error = msdosfs_vfs_root(mp, &vp, context); 1640 if (error) 1641 return error; 1642 root = VTODE(vp); 1643 1644 diroff = 0; 1645 for (frcn=0; ; frcn++) { 1646 error = msdosfs_pcbmap(root, frcn, 1, &bn, &cluster, &blsize); 1647 if (error) { 1648 /* It is fine if no volume label entry was found in the root directory */ 1649 if (error == E2BIG) 1650 error = 0; 1651 goto not_found; 1652 } 1653 1654 for (blkoff = 0; blkoff < blsize; blkoff += sizeof(struct dosdirentry), diroff += sizeof(struct dosdirentry)) { 1655 /* Make sure we have the buffer containing the current entry */ 1656 if (bp == NULL) { 1657 error = (int)buf_meta_bread(pmp->pm_devvp, bn, blsize, vfs_context_ucred(context), &bp); 1658 if (error) { 1659 goto not_found; 1660 } 1661 bdata = (char *)buf_dataptr(bp); 1662 } 1663 1664 dep = (struct dosdirentry *) (bdata + blkoff); 1665 1666 /* Skip deleted directory entries */ 1667 if (dep->deName[0] == SLOT_DELETED) 1668 continue; 1669 1670 /* Stop if we hit the end of the directory (a never used entry) */ 1671 if (dep->deName[0] == SLOT_EMPTY) { 1672 goto not_found; 1673 } 1674 1675 /* Skip long name entries */ 1676 if (dep->deAttributes == ATTR_WIN95) 1677 continue; 1678 1679 if (dep->deAttributes & ATTR_VOLUME) { 1680 pmp->pm_label_cluster = cluster; 1681 pmp->pm_label_offset = blkoff; 1682 1683 /* 1684 * Copy the dates from the label to the root vnode. 1685 */ 1686 root->de_CHun = dep->deCHundredth; 1687 root->de_CTime = getuint16(dep->deCTime); 1688 root->de_CDate = getuint16(dep->deCDate); 1689 root->de_ADate = getuint16(dep->deADate); 1690 root->de_MTime = getuint16(dep->deMTime); 1691 root->de_MDate = getuint16(dep->deMDate); 1692 1693 /* 1694 * We don't call msdosfs_dos2unicodefn() because it assumes the last three 1695 * characters are an extension, and it will put a period before the 1696 * extension. 1697 */ 1698 for (i=0; i<SHORT_NAME_LEN; i++) { 1699 uc = dep->deName[i]; 1700 if (i==0 && uc == SLOT_E5) 1701 uc = 0xE5; 1702 ucfn[i] = (uc < 0x80 || uc > 0x9F ? (u_int16_t)uc : dos2unicode[uc - 0x80]); 1703 } 1704 for (i=10; i>=0 && ucfn[i]==' '; --i) 1705 ; 1706 unichars = i+1; 1707 1708 /* translate the name in ucfn into UTF-8 */ 1709 error = utf8_encodestr(ucfn, unichars * 2, 1710 pmp->pm_label, &outbytes, 1711 sizeof(pmp->pm_label), 0, UTF_DECOMPOSED); 1712 goto found; 1713 } 1714 1715 if (!bcmp(dep->deName, "HIBERFILSYS", SHORT_NAME_LEN)) 1716 { 1717 struct denode *hibernate = NULL; 1718 buf_t hibernate_bp = NULL; 1719 char *hibernate_data = NULL; 1720 1721 if (getuint32(dep->deFileSize) < 4096) 1722 { 1723 if (DEBUG) printf("msdosfs: Volume is hibernated; hiberfile.sys < 4096 bytes\n"); 1724 goto hibernated; 1725 } 1726 1727 /* Release the current directory block so we can msdosfs_deget() the current entry. */ 1728 buf_brelse(bp); 1729 bp = NULL; 1730 1731 /* Need to open the file so we can check the first 4KiB */ 1732 error = msdosfs_deget(pmp, cluster, diroff, NULL, NULL, &hibernate, context); 1733 if (error) 1734 { 1735 printf("msdosfs: error %d trying to open hiberfil.sys\n", error); 1736 error = 0; 1737 goto hibernated; 1738 } 1739 error = buf_meta_bread(DETOV(hibernate), 0, 4096, vfs_context_ucred(context), &hibernate_bp); 1740 if (error) 1741 { 1742 printf("msdosfs: error %d trying to read hiberfil.sys\n", error); 1743 error = 0; 1744 goto hibernated; 1745 } 1746 hibernate_data = (char *) buf_dataptr(hibernate_bp); 1747 if (!strncmp(hibernate_data, "hibr", 4)) 1748 { 1749 if (DEBUG) printf("msdosfs: Volume is hibernated; signature = 'hibr'\n"); 1750 goto hibernated; 1751 } 1752 if (!strncmp(hibernate_data, "HIBR", 4)) 1753 { 1754 if (DEBUG) printf("msdosfs: Volume is hibernated; signature = 'HIBR'\n"); 1755 goto hibernated; 1756 } 1757 for (i=0; i<4096; ++i) 1758 { 1759 if (hibernate_data[i]) 1760 { 1761 if (DEBUG) printf("msdosfs: Volume is hibernated (non-zero header)\n"); 1762 goto hibernated; 1763 } 1764 } 1765 1766 if (DEBUG) printf("msdosfs: Volume is not hibernated (hiberfil.sys header all zeroes)\n"); 1767 goto not_hibernated; 1768 1769hibernated: 1770 printf("msdosfs: Mounting hibernated volume read-only\n"); 1771 vfs_setflags(mp, MNT_RDONLY); 1772 1773not_hibernated: 1774 if (hibernate_bp) 1775 buf_brelse(hibernate_bp); 1776 if (hibernate) 1777 { 1778 vnode_recycle(DETOV(hibernate)); 1779 vnode_put(DETOV(hibernate)); 1780 } 1781 } 1782 } 1783 1784 /* We're done with the current block. Release it if we still have it. */ 1785 if (bp != NULL) { 1786 buf_brelse(bp); 1787 bp = NULL; 1788 } 1789 } 1790 1791found: 1792not_found: 1793 if (bp) 1794 buf_brelse(bp); 1795 1796 if (vp) { 1797 if (error) 1798 vnode_recycle(vp); 1799 vnode_put(vp); 1800 } 1801 1802 return error; 1803} 1804