1 /* Copyright © 2017-2018 Apple Inc. All rights reserved.
6 * Created by Or Haimovich on 18/3/18.
9 #include "lf_hfs_common.h"
10 #include <CommonCrypto/CommonDigest.h>
11 #include <stdatomic.h>
12 #include <sys/ioctl.h>
13 #include <sys/mount.h>
17 #include "lf_hfs_logger.h"
18 #include "lf_hfs_mount.h"
20 #include "lf_hfs_catalog.h"
21 #include "lf_hfs_cnode.h"
22 #include "lf_hfs_chash.h"
23 #include "lf_hfs_format.h"
24 #include "lf_hfs_locks.h"
25 #include "lf_hfs_endian.h"
26 #include "lf_hfs_locks.h"
27 #include "lf_hfs_utils.h"
28 #include "lf_hfs_raw_read_write.h"
29 #include "lf_hfs_vfsutils.h"
30 #include "lf_hfs_vfsops.h"
31 #include "lf_hfs_volume_allocation.h"
32 #include "lf_hfs_catalog.h"
33 #include "lf_hfs_link.h"
34 #include "lf_hfs_vnops.h"
35 #include "lf_hfs_generic_buf.h"
36 #include "lf_hfs_fsops_handler.h"
37 #include "lf_hfs_journal.h"
38 #include "lf_hfs_fileops_handler.h"
42 static void hfs_locks_destroy(struct hfsmount
*hfsmp
);
43 static int hfs_mountfs(struct vnode
*devvp
, struct mount
*mp
, struct hfs_mount_args
*args
);
47 setup_posix_file_action_for_fsck(posix_spawn_file_actions_t
*file_actions
, int fd
)
51 if (file_actions
== NULL
|| fd
< 0)
56 error
= posix_spawn_file_actions_init(file_actions
);
62 error
= posix_spawn_file_actions_addinherit_np(file_actions
, 0);
68 error
= posix_spawn_file_actions_addinherit_np(file_actions
, 1);
74 error
= posix_spawn_file_actions_addinherit_np(file_actions
, 2);
80 error
= posix_spawn_file_actions_addinherit_np(file_actions
, fd
);
87 setup_spawnattr_for_fsck(posix_spawnattr_t
*spawn_attr
)
91 error
= posix_spawnattr_init(spawn_attr
);
96 error
= posix_spawnattr_setflags(spawn_attr
, POSIX_SPAWN_CLOEXEC_DEFAULT
);
103 // fsck_mount_and_replay: executed on fsck_hfs -quick
104 // Try to mount, and if a journaled volume, play the journal.
107 // 1) On journaled volumes, the journal has been replayed and the dirty bit cleared.
108 // 2) On non-journalled volumes, the dirty is cleared.
110 // 1) On non-journalled volumes the dirty bit is set. Please run fsck_hfs to fix.
111 // 2) On journalled volume, the replay failed. Try fsck_hfs.
112 int fsck_mount_and_replay(int iFd
) {
115 LFHFS_LOG(LEVEL_DEBUG
, "fsck_mount_and_replay %d", iFd
);
117 UVFSFileNode sRootNode
;
119 iErr
= LFHFS_Taste(iFd
);
121 LFHFS_LOG(LEVEL_DEBUG
, "LFHFS_Taste returned %d", iErr
);
125 UVFSScanVolsRequest sScanVolsReq
= {0};
126 UVFSScanVolsReply sScanVolsReply
= {0};
127 iErr
= LFHFS_ScanVols(iFd
, &sScanVolsReq
, &sScanVolsReply
);
129 LFHFS_LOG(LEVEL_DEBUG
, "LFHFS_ScanVol returned %d", iErr
);
133 // Mount and replay journal if possible
134 iErr
= LFHFS_Mount(iFd
, 0, 0, NULL
, &sRootNode
); // On journaled volumes, this replays the journal.
135 // Non-journaled volumes fails to mount if dirty (Unmounted == 0).
137 LFHFS_LOG(LEVEL_DEBUG
, "fsck_mount_and_replay: LFHFS_Mount returned %d", iErr
);
141 LFHFS_Unmount (sRootNode
, UVFSUnmountHintNone
);
146 #define PATH_TO_FSCK "/System/Library/Filesystems/hfs.fs/Contents/Resources/fsck_hfs"
149 fsck_hfs(int fd
, check_flags_t how
)
154 extern char **environ
;
155 char fdescfs_path
[24];
156 posix_spawn_file_actions_t file_actions
;
158 posix_spawnattr_t spawn_attr
;
161 * XXXJRT There are dragons related to how the journal is replayed in
162 * fsck_hfs. Until we can sort out the mess, disable running fsck_hfs.
163 * <rdar://problem/47262605> USB: Re-enable Detonator fsck_hfs
165 if (how
== QUICK_CHECK
) {
166 if (fsck_mount_and_replay(fd
) == 0) {
171 LFHFS_LOG(LEVEL_DEFAULT
, "fsck_hfs - fsck start for %d", fd
);
172 snprintf(fdescfs_path
, sizeof(fdescfs_path
), "/dev/fd/%d", fd
);
173 const char * argv
[] = {"fsck_hfs", "-q", fdescfs_path
, NULL
};
178 /* Do nothing, already setup for this */
183 case CHECK_AND_REPAIR
:
187 LFHFS_LOG(LEVEL_ERROR
, "Invalid how flags for the check, ignoring; %d", how
);
191 LFHFS_LOG(LEVEL_DEBUG
, "fsck_hfs params: %s %s %s", argv
[1], argv
[2], argv
[3]);
192 result
= setup_posix_file_action_for_fsck(&file_actions
, fd
);
198 result
= setup_spawnattr_for_fsck(&spawn_attr
);
201 posix_spawn_file_actions_destroy(&file_actions
);
205 result
= posix_spawn(&child
,
209 (char * const *)argv
,
212 posix_spawn_file_actions_destroy(&file_actions
);
213 posix_spawnattr_destroy(&spawn_attr
);
216 LFHFS_LOG(LEVEL_ERROR
, "posix_spawn fsck_hfs: error=%d", result
);
220 // Wait for child to finish, XXXab: revisit, need sensible timeout?
222 child_found
= waitpid(child
, &child_status
, 0);
223 } while (child_found
== -1 && errno
== EINTR
);
225 if (child_found
== -1)
228 LFHFS_LOG(LEVEL_ERROR
, "waitpid fsck_hfs: errno=%d", result
);
232 if (WIFEXITED(child_status
))
234 result
= WEXITSTATUS(child_status
);
237 LFHFS_LOG(LEVEL_ERROR
, "fsck_hfs: exited with status %d", result
);
240 LFHFS_LOG(LEVEL_ERROR
, "fsck_hfs: exited with status %d", result
);
245 result
= WTERMSIG(child_status
);
246 LFHFS_LOG(LEVEL_ERROR
, "fsck_hfs: terminated by signal %d", result
);
251 LFHFS_LOG(LEVEL_DEFAULT
, "fsck_hfs - fsck finish for %d with err %d", fd
, result
);
256 hfs_mount(struct mount
*mp
, vnode_t devvp
, user_addr_t data
)
258 struct hfsmount
*hfsmp
= NULL
;
266 retval
= hfs_mountfs(devvp
, mp
, NULL
);
269 // ENOTSUP is for regular HFS -> just fail
270 if (retval
!= ENOTSUP
)
272 //Failed during mount, try to run fsck to fix and try mount again
273 retval
= fsck_hfs(devvp
->psFSRecord
->iFD
, CHECK_AND_REPAIR
);
275 // fsck succeeded, try to mount
277 retval
= hfs_mountfs(devvp
, mp
, NULL
);
283 LFHFS_LOG(LEVEL_ERROR
, "hfs_mount: hfs_mountfs returned error=%d\n", retval
);
287 /* After hfs_mountfs succeeds, we should have valid hfsmp */
288 hfsmp
= VFSTOHFS(mp
);
290 /* Set up the maximum defrag file size */
291 hfsmp
->hfs_defrag_max
= HFS_INITIAL_DEFRAG_SIZE
;
296 hfsmp
->hfs_uid
= UNKNOWNUID
;
297 hfsmp
->hfs_gid
= UNKNOWNGID
;
298 hfsmp
->hfs_dir_mask
= (S_IRWXU
| S_IRGRP
|S_IXGRP
| S_IROTH
|S_IXOTH
); /* 0755 */
299 hfsmp
->hfs_file_mask
= (S_IRWXU
| S_IRGRP
|S_IXGRP
| S_IROTH
|S_IXOTH
); /* 0755 */
301 /* Establish the free block reserve. */
302 hfsmp
->reserveBlocks
= (uint32_t) ((u_int64_t
)hfsmp
->totalBlocks
* HFS_MINFREE
) / 100;
303 hfsmp
->reserveBlocks
= MIN(hfsmp
->reserveBlocks
, HFS_MAXRESERVE
/ hfsmp
->blockSize
);
310 static int hfs_InitialMount(struct vnode
*devvp
, struct mount
*mp
, struct hfs_mount_args
*args
, HFSPlusVolumeHeader
**vhp
, off_t
*embeddedOffset
, struct hfsmount
**hfsmp
, bool bFailForDirty
)
313 HFSMasterDirectoryBlock
*mdbp
= NULL
;
314 void* pvBuffer
= NULL
;
317 u_int64_t log_blkcnt
;
318 u_int32_t log_blksize
;
319 u_int32_t phys_blksize
;
320 u_int32_t minblksize
;
321 u_int32_t iswritable
;
322 u_int64_t mdb_offset
;
323 u_int32_t device_features
= 0;
326 minblksize
= kHFSBlockSize
;
329 /* Get the logical block size (treated as physical block size everywhere) */
330 if (ioctl(devvp
->psFSRecord
->iFD
, DKIOCGETBLOCKSIZE
, &log_blksize
))
332 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: DKIOCGETBLOCKSIZE failed\n");
336 if (log_blksize
== 0 || log_blksize
> 1024*1024*1024)
338 LFHFS_LOG(LEVEL_ERROR
, "hfs_mountfs: logical block size 0x%x looks bad. Not mounting.\n", log_blksize
);
343 /* Get the physical block size. */
344 if (ioctl(devvp
->psFSRecord
->iFD
, DKIOCGETPHYSICALBLOCKSIZE
, &phys_blksize
))
346 if ((retval
!= ENOTSUP
) && (retval
!= ENOTTY
))
348 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: DKIOCGETPHYSICALBLOCKSIZE failed\n");
352 /* If device does not support this ioctl, assume that physical
353 * block size is same as logical block size
355 phys_blksize
= log_blksize
;
358 if (phys_blksize
== 0 || phys_blksize
> MAXBSIZE
)
360 LFHFS_LOG(LEVEL_ERROR
, "hfs_mountfs: physical block size 0x%x looks bad. Not mounting.\n", phys_blksize
);
365 /* Don't let phys_blksize be smaller than the logical */
366 if (phys_blksize
< log_blksize
) {
368 * In the off chance that the phys_blksize is SMALLER than the logical
369 * then don't let that happen. Pretend that the PHYSICALBLOCKSIZE
370 * ioctl was not supported.
372 phys_blksize
= log_blksize
;
375 /* Get the number of physical blocks. */
376 if (ioctl(devvp
->psFSRecord
->iFD
, DKIOCGETBLOCKCOUNT
, &log_blkcnt
))
378 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: DKIOCGETBLOCKCOUNT failed\n");
383 /* Compute an accurate disk size (i.e. within 512 bytes) */
384 disksize
= (u_int64_t
)log_blkcnt
* (u_int64_t
)log_blksize
;
388 * minblksize is the minimum physical block size
389 * log_blksize has our preferred physical block size
390 * log_blkcnt has the total number of physical blocks
392 mdbp
= hfs_mallocz(kMDBSize
);
399 pvBuffer
= hfs_malloc(phys_blksize
);
400 if (pvBuffer
== NULL
)
406 mdb_offset
= (uint64_t) HFS_PRI_SECTOR(log_blksize
);
407 retval
= raw_readwrite_read_mount( devvp
, HFS_PHYSBLK_ROUNDDOWN(mdb_offset
, (phys_blksize
/log_blksize
)), phys_blksize
, pvBuffer
, phys_blksize
, NULL
, NULL
);
410 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: raw_readwrite_read_mount failed with %d\n", retval
);
414 bcopy(pvBuffer
+ HFS_PRI_OFFSET(phys_blksize
), mdbp
, kMDBSize
);
418 *hfsmp
= hfs_malloc(sizeof(struct hfsmount
));
424 memset( *hfsmp
, 0, sizeof(struct hfsmount
) );
426 //Copy read only flag
427 if (mp
->mnt_flag
== MNT_RDONLY
) (*hfsmp
)->hfs_flags
= HFS_READ_ONLY
;
429 hfs_chashinit_finish(*hfsmp
);
431 /* Init the ID lookup hashtable */
432 hfs_idhash_init (*hfsmp
);
435 * See if the disk supports unmap (trim).
437 * NOTE: vfs_init_io_attributes has not been called yet, so we can't use the io_flags field
438 * returned by vfs_ioattr. We need to call VNOP_IOCTL ourselves.
440 if (ioctl(devvp
->psFSRecord
->iFD
, DKIOCGETFEATURES
, &device_features
) == 0)
442 if (device_features
& DK_FEATURE_UNMAP
)
444 (*hfsmp
)->hfs_flags
|= HFS_UNMAP
;
447 if(device_features
& DK_FEATURE_BARRIER
)
449 (*hfsmp
)->hfs_flags
|= HFS_FEATURE_BARRIER
;
454 * Init the volume information structure
456 lf_lck_mtx_init(&(*hfsmp
)->hfs_mutex
);
457 lf_lck_mtx_init(&(*hfsmp
)->sync_mutex
);
458 lf_lck_rw_init(&(*hfsmp
)->hfs_global_lock
);
459 lf_lck_spin_init(&(*hfsmp
)->vcbFreeExtLock
);
463 mp
->psHfsmount
= (*hfsmp
);
466 (*hfsmp
)->hfs_mp
= mp
; /* Make VFSTOHFS work */
467 (*hfsmp
)->hfs_raw_dev
= 0; //vnode_specrdev(devvp);
468 (*hfsmp
)->hfs_devvp
= devvp
;
469 (*hfsmp
)->hfs_logical_block_size
= log_blksize
;
470 (*hfsmp
)->hfs_logical_block_count
= log_blkcnt
;
471 (*hfsmp
)->hfs_logical_bytes
= (uint64_t) log_blksize
* (uint64_t) log_blkcnt
;
472 (*hfsmp
)->hfs_physical_block_size
= phys_blksize
;
473 (*hfsmp
)->hfs_log_per_phys
= (phys_blksize
/ log_blksize
);
474 (*hfsmp
)->hfs_flags
|= HFS_WRITEABLE_MEDIA
;
476 if (mp
&& (mp
->mnt_flag
& MNT_UNKNOWNPERMISSIONS
))
478 (*hfsmp
)->hfs_flags
|= HFS_UNKNOWN_PERMS
;
481 /* MNT_UNKNOWNPERMISSIONS requires setting up uid, gid, and mask: */
482 if (mp
&& (mp
->mnt_flag
& MNT_UNKNOWNPERMISSIONS
))
484 (*hfsmp
)->hfs_uid
= UNKNOWNUID
;
485 (*hfsmp
)->hfs_gid
= UNKNOWNGID
;
486 // vfs_setowner(mp, hfsmp->hfs_uid, hfsmp->hfs_gid); /* tell the VFS */
487 (*hfsmp
)->hfs_dir_mask
= UNKNOWNPERMISSIONS
& ALLPERMS
; /* 0777: rwx---rwx */
488 (*hfsmp
)->hfs_file_mask
= UNKNOWNPERMISSIONS
& DEFFILEMODE
; /* 0666: no --x by default? */
491 /* Find out if disk media is writable. */
492 if (ioctl(devvp
->psFSRecord
->iFD
, DKIOCISWRITABLE
, &iswritable
) == 0)
496 (*hfsmp
)->hfs_flags
|= HFS_WRITEABLE_MEDIA
;
500 (*hfsmp
)->hfs_flags
&= ~HFS_WRITEABLE_MEDIA
;
505 rl_init(&(*hfsmp
)->hfs_reserved_ranges
[0]);
506 rl_init(&(*hfsmp
)->hfs_reserved_ranges
[1]);
508 // record the current time at which we're mounting this volume
511 (*hfsmp
)->hfs_mount_time
= tv
.tv_sec
;
513 /* Mount an HFS Plus disk */
516 /* Mount a standard HFS disk */
517 if ((SWAP_BE16(mdbp
->drSigWord
) == kHFSSigWord
) && (mntwrapper
|| (SWAP_BE16(mdbp
->drEmbedSigWord
) != kHFSPlusSigWord
)))
519 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: Not supporting standard HFS\n");
523 /* Get the embedded Volume Header */
524 else if (SWAP_BE16(mdbp
->drEmbedSigWord
) == kHFSPlusSigWord
)
526 *embeddedOffset
= SWAP_BE16(mdbp
->drAlBlSt
) * kHFSBlockSize
;
527 *embeddedOffset
+= (u_int64_t
)SWAP_BE16(mdbp
->drEmbedExtent
.startBlock
) * (u_int64_t
)SWAP_BE32(mdbp
->drAlBlkSiz
);
530 * If the embedded volume doesn't start on a block
531 * boundary, then switch the device to a 512-byte
532 * block size so everything will line up on a block
535 if ((*embeddedOffset
% log_blksize
) != 0)
537 // LF not support DKIOCSETBLOCKSIZE, return error.
538 LFHFS_LOG(LEVEL_DEFAULT
, "hfs_mountfs: embedded volume offset not a multiple of physical block size (%d); switching to 512\n", log_blksize
);
543 disksize
= (u_int64_t
)SWAP_BE16(mdbp
->drEmbedExtent
.blockCount
) * (u_int64_t
)SWAP_BE32(mdbp
->drAlBlkSiz
);
545 (*hfsmp
)->hfs_logical_block_count
= disksize
/ log_blksize
;
547 (*hfsmp
)->hfs_logical_bytes
= (uint64_t) (*hfsmp
)->hfs_logical_block_count
* (uint64_t) (*hfsmp
)->hfs_logical_block_size
;
549 mdb_offset
= (uint64_t)((*embeddedOffset
/ log_blksize
) + HFS_PRI_SECTOR(log_blksize
));
551 pvBuffer
= hfs_malloc(phys_blksize
);
552 if (pvBuffer
== NULL
)
558 retval
= raw_readwrite_read_mount( devvp
, HFS_PHYSBLK_ROUNDDOWN(mdb_offset
, (phys_blksize
/log_blksize
)), phys_blksize
, pvBuffer
, phys_blksize
, NULL
, NULL
);
561 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: raw_readwrite_read_mount (2) failed with %d\n", retval
);
565 bcopy(pvBuffer
+ HFS_PRI_OFFSET(phys_blksize
), mdbp
, kMDBSize
);
566 *vhp
= (HFSPlusVolumeHeader
*) mdbp
;
573 *vhp
= (HFSPlusVolumeHeader
*) mdbp
;
576 retval
= hfs_ValidateHFSPlusVolumeHeader(*hfsmp
, *vhp
);
581 * If allocation block size is less than the physical block size,
582 * invalidate the buffer read in using native physical block size
583 * to ensure data consistency.
585 * HFS Plus reserves one allocation block for the Volume Header.
586 * If the physical size is larger, then when we read the volume header,
587 * we will also end up reading in the next allocation block(s).
588 * If those other allocation block(s) is/are modified, and then the volume
589 * header is modified, the write of the volume header's buffer will write
590 * out the old contents of the other allocation blocks.
592 * We assume that the physical block size is same as logical block size.
593 * The physical block size value is used to round down the offsets for
594 * reading and writing the primary and alternate volume headers.
596 * The same logic is also in hfs_MountHFSPlusVolume to ensure that
597 * hfs_mountfs, hfs_MountHFSPlusVolume and later are doing the I/Os
598 * using same block size.
600 if (SWAP_BE32((*vhp
)->blockSize
) < (*hfsmp
)->hfs_physical_block_size
)
602 phys_blksize
= (*hfsmp
)->hfs_logical_block_size
;
603 (*hfsmp
)->hfs_physical_block_size
= (*hfsmp
)->hfs_logical_block_size
;
604 (*hfsmp
)->hfs_log_per_phys
= 1;
611 * On inconsistent disks, do not allow read-write mount
612 * unless it is the boot volume being mounted. We also
613 * always want to replay the journal if the journal_replay_only
614 * flag is set because that will (most likely) get the
615 * disk into a consistent state before fsck_hfs starts
618 if ( (mp
&& !(mp
->mnt_flag
& MNT_ROOTFS
))
619 && (SWAP_BE32((*vhp
)->attributes
) & kHFSVolumeInconsistentMask
)
620 && !((*hfsmp
)->hfs_flags
& HFS_READ_ONLY
))
622 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: failed to mount non-root inconsistent disk\n");
627 (*hfsmp
)->jnl
= NULL
;
628 (*hfsmp
)->jvp
= NULL
;
629 if (args
!= NULL
&& (args
->flags
& HFSFSMNT_EXTENDED_ARGS
) && args
->journal_disable
)
635 * We only initialize the journal here if the last person
636 * to mount this volume was journaling aware. Otherwise
637 * we delay journal initialization until later at the end
638 * of hfs_MountHFSPlusVolume() because the last person who
639 * mounted it could have messed things up behind our back
640 * (so we need to go find the .journal file, make sure it's
641 * the right size, re-sync up if it was moved, etc).
643 uint32_t lastMountedVersion
= SWAP_BE32((*vhp
)->lastMountedVersion
);
644 uint32_t attributes
= SWAP_BE32((*vhp
)->attributes
);
645 if ( (lastMountedVersion
== kHFSJMountVersion
) &&
646 (attributes
& kHFSVolumeJournaledMask
) &&
650 // if we're able to init the journal, mark the mount
651 // point as journaled.
652 if ((retval
= hfs_early_journal_init(*hfsmp
, *vhp
, args
, *embeddedOffset
, mdb_offset
, mdbp
)) != 0)
656 // EROFS is a special error code that means the volume has an external
657 // journal which we couldn't find. in that case we do not want to
658 // rewrite the volume header - we'll just refuse to mount the volume.
659 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: hfs_early_journal_init indicated external jnl \n");
664 // if the journal failed to open, then set the lastMountedVersion
665 // to be "FSK!" which fsck_hfs will see and force the fsck instead
666 // of just bailing out because the volume is journaled.
667 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: hfs_early_journal_init failed, setting to FSK \n");
668 HFSPlusVolumeHeader
*jvhp
;
670 (*hfsmp
)->hfs_flags
|= HFS_NEED_JNL_RESET
;
674 mdb_offset
= (uint64_t)((*embeddedOffset
/ log_blksize
) + HFS_PRI_SECTOR(log_blksize
));
677 pvBuffer
= hfs_malloc(phys_blksize
);
678 if (pvBuffer
== NULL
)
684 retval
= raw_readwrite_read_mount( devvp
, HFS_PHYSBLK_ROUNDDOWN(mdb_offset
, (*hfsmp
)->hfs_log_per_phys
), phys_blksize
, pvBuffer
, phys_blksize
, NULL
, NULL
);
687 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: raw_readwrite_read_mount (3) failed with %d\n", retval
);
691 jvhp
= (HFSPlusVolumeHeader
*)(pvBuffer
+ HFS_PRI_OFFSET(phys_blksize
));
693 if (SWAP_BE16(jvhp
->signature
) == kHFSPlusSigWord
|| SWAP_BE16(jvhp
->signature
) == kHFSXSigWord
)
695 LFHFS_LOG(LEVEL_DEFAULT
, "hfs_mountfs: Journal replay fail. Writing lastMountVersion as FSK!\n");
697 jvhp
->lastMountedVersion
= SWAP_BE32(kFSKMountVersion
);
698 retval
= raw_readwrite_write_mount( devvp
, HFS_PHYSBLK_ROUNDDOWN(mdb_offset
, (*hfsmp
)->hfs_log_per_phys
), phys_blksize
, pvBuffer
, phys_blksize
, NULL
, NULL
);
701 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: raw_readwrite_write_mount (1) failed with %d\n", retval
);
708 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: hfs_early_journal_init failed, erroring out \n");
714 retval
= hfs_MountHFSPlusVolume(*hfsmp
, *vhp
, *embeddedOffset
, disksize
, bFailForDirty
);
716 * If the backend didn't like our physical blocksize
717 * then retry with physical blocksize of 512.
719 if ((retval
== ENXIO
) && (log_blksize
> 512) && (log_blksize
!= minblksize
))
721 // LF not support DKIOCSETBLOCKSIZE, return error.
722 LFHFS_LOG(LEVEL_DEFAULT
, "hfs_mountfs: could not use physical block size (%d).\n", log_blksize
);
727 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: hfs_MountHFSPlusVolume encountered failure %d \n", retval
);
741 hfs_locks_destroy(*hfsmp
);
742 hfs_delete_chash(*hfsmp
);
743 hfs_idhash_destroy (*hfsmp
);
752 int hfs_ScanVolGetVolName(int iFd
, char* pcVolumeName
)
756 HFSPlusVolumeHeader
*vhp
;
757 off_t embeddedOffset
;
758 struct hfsmount
*hfsmp
;
759 struct mount
* psMount
= hfs_mallocz(sizeof(struct mount
));
760 struct vnode
* psDevVnode
= hfs_mallocz(sizeof(struct vnode
));
761 struct cnode
* psDevCnode
= hfs_mallocz(sizeof(struct cnode
));
762 struct filefork
* psDevFileFork
= hfs_mallocz(sizeof(struct filefork
));
763 FileSystemRecord_s
*psFSRecord
= hfs_mallocz(sizeof(FileSystemRecord_s
));
765 if ( psMount
== NULL
|| psDevVnode
== NULL
|| psDevCnode
== NULL
|| psDevFileFork
== NULL
|| psFSRecord
== NULL
)
768 LFHFS_LOG(LEVEL_ERROR
, "hfs_ScanVolGetVolName: failed to malloc initial system files\n");
772 psFSRecord
->iFD
= iFd
;
773 psDevVnode
->psFSRecord
= psFSRecord
;
774 psDevVnode
->sFSParams
.vnfs_marksystem
= 1;
775 psDevVnode
->bIsMountVnode
= true;
777 // Initializing inputs for hfs_mount
778 psDevFileFork
->ff_data
.cf_blocks
= 3;
779 psDevFileFork
->ff_data
.cf_extents
[0].blockCount
= 1;
780 psDevFileFork
->ff_data
.cf_extents
[0].startBlock
= 0;
782 psDevVnode
->sFSParams
.vnfs_fsnode
= psDevCnode
;
783 psDevCnode
->c_vp
= psDevVnode
;
784 psDevVnode
->is_rsrc
= false;
785 psDevCnode
->c_datafork
= psDevFileFork
;
786 psDevVnode
->sFSParams
.vnfs_mp
= psMount
;
788 retval
= hfs_InitialMount(psDevVnode
, psMount
, 0, &vhp
, &embeddedOffset
, &hfsmp
, false);
796 strlcpy(pcVolumeName
, (char*) hfsmp
->vcbVN
, UVFS_SCANVOLS_VOLNAME_MAX
);
803 journal_release(hfsmp
->jnl
);
809 hfs_locks_destroy(hfsmp
);
810 hfs_delete_chash(hfsmp
);
811 hfs_idhash_destroy (hfsmp
);
819 LFHFS_LOG(LEVEL_ERROR
, "hfs_ScanVolGetVolName: failed with error %d, returning empty name and no error\n",retval
);
820 pcVolumeName
[0] = '\0';
823 if (psMount
) free (psMount
);
824 if (psDevVnode
) free (psDevVnode
);
825 if (psDevCnode
) free (psDevCnode
);
826 if (psDevFileFork
) free (psDevFileFork
);
827 if (psFSRecord
) free (psFSRecord
);
833 * Common code for mount and mountroot
836 hfs_mountfs(struct vnode
*devvp
, struct mount
*mp
, struct hfs_mount_args
*args
)
840 HFSPlusVolumeHeader
*vhp
;
841 off_t embeddedOffset
;
842 struct hfsmount
*hfsmp
;
843 retval
= hfs_InitialMount(devvp
, mp
, args
, &vhp
, &embeddedOffset
, &hfsmp
, true);
846 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: hfs_InitialMount encountered failure %d \n", retval
);
847 //No need to go to error_exit, since everything got reset at the Initial Mount
851 retval
= hfs_CollectBtreeStats(hfsmp
, vhp
, embeddedOffset
, args
);
856 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: hfs_CollectBtreeStats encountered failure %d \n", retval
);
860 // save off a snapshot of the mtime from the previous mount
862 hfsmp
->hfs_last_mounted_mtime
= hfsmp
->hfs_mtime
;
866 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: encountered failure %d \n", retval
);
870 LFHFS_LOG(LEVEL_DEFAULT
, "hfs_mountfs: mounted %s on device %s\n", (hfsmp
->vcbVN
[0] ? (const char*) hfsmp
->vcbVN
: "unknown"), "unknown device");
872 hfs_flushvolumeheader(hfsmp
, 0);
883 hfs_locks_destroy(hfsmp
);
884 hfs_delete_chash(hfsmp
);
885 hfs_idhash_destroy (hfsmp
);
894 * Destroy all locks, mutexes and spinlocks in hfsmp on unmount or failed mount
897 hfs_locks_destroy(struct hfsmount
*hfsmp
)
900 lf_lck_mtx_destroy(&hfsmp
->hfs_mutex
);
901 lf_lck_mtx_destroy(&hfsmp
->sync_mutex
);
902 lf_lck_rw_destroy(&hfsmp
->hfs_global_lock
);
903 lf_lck_spin_destroy(&hfsmp
->vcbFreeExtLock
);
910 * Flush any dirty in-memory mount data to the on-disk
913 * Note: the on-disk volume signature is intentionally
914 * not flushed since the on-disk "H+" and "HX" signatures
915 * are always stored in-memory as "H+".
918 hfs_flushvolumeheader(struct hfsmount
*hfsmp
, hfs_flush_volume_header_options_t options
)
922 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
923 bool critical
= false;
924 daddr64_t avh_sector
;
925 bool altflush
= ISSET(options
, HFS_FVH_WRITE_ALT
);
927 void *pvVolHdrData
= NULL
;
928 GenericLFBuf
*psVolHdrBuf
= NULL
;
929 void *pvVolHdr2Data
= NULL
;
930 GenericLFBuf
*psVolHdr2Buf
= NULL
;
931 void *pvAltHdrData
= NULL
;
932 GenericLFBuf
*psAltHdrBuf
= NULL
;
935 if (ISSET(options
, HFS_FVH_FLUSH_IF_DIRTY
) && !hfs_header_needs_flushing(hfsmp
)) {
939 if (hfsmp
->hfs_flags
& HFS_READ_ONLY
) {
943 if (options
& HFS_FVH_MARK_UNMOUNT
) {
944 HFSTOVCB(hfsmp
)->vcbAtrb
|= kHFSVolumeUnmountedMask
;
946 HFSTOVCB(hfsmp
)->vcbAtrb
&= ~kHFSVolumeUnmountedMask
;
949 daddr64_t priIDSector
= (daddr64_t
)((vcb
->hfsPlusIOPosOffset
/ hfsmp
->hfs_logical_block_size
) + HFS_PRI_SECTOR(hfsmp
->hfs_logical_block_size
));
951 if (!(options
& HFS_FVH_SKIP_TRANSACTION
)) {
952 if (hfs_start_transaction(hfsmp
) != 0) {
957 psVolHdrBuf
= lf_hfs_generic_buf_allocate(hfsmp
->hfs_devvp
,
958 HFS_PHYSBLK_ROUNDDOWN(priIDSector
, hfsmp
->hfs_log_per_phys
),
959 hfsmp
->hfs_physical_block_size
, GEN_BUF_PHY_BLOCK
);
960 if (psVolHdrBuf
== NULL
) {
964 pvVolHdrData
= psVolHdrBuf
->pvData
;
966 retval
= lf_hfs_generic_buf_read(psVolHdrBuf
);
968 LFHFS_LOG(LEVEL_ERROR
, "hfs_flushvolumeheader: err %d reading VH blk (vol=%s)\n", retval
, vcb
->vcbVN
);
972 HFSPlusVolumeHeader
* volumeHeader
= (HFSPlusVolumeHeader
*)(pvVolHdrData
+ HFS_PRI_OFFSET(hfsmp
->hfs_physical_block_size
));
975 * Sanity check what we just read. If it's bad, try the alternate instead.
977 u_int16_t signature
= SWAP_BE16 (volumeHeader
->signature
);
978 u_int16_t hfsversion
= SWAP_BE16 (volumeHeader
->version
);
980 if ((signature
!= kHFSPlusSigWord
&& signature
!= kHFSXSigWord
) ||
981 (hfsversion
< kHFSPlusVersion
) || (hfsversion
> 100) ||
982 (SWAP_BE32 (volumeHeader
->blockSize
) != vcb
->blockSize
))
984 LFHFS_LOG(LEVEL_DEFAULT
, "hfs_flushvolumeheader: corrupt VH on %s, sig 0x%04x, ver %d, blksize %d\n", vcb
->vcbVN
, signature
, hfsversion
, SWAP_BE32 (volumeHeader
->blockSize
));
985 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
987 /* Almost always we read AVH relative to the partition size */
988 avh_sector
= hfsmp
->hfs_partition_avh_sector
;
990 if (hfsmp
->hfs_partition_avh_sector
!= hfsmp
->hfs_fs_avh_sector
)
993 * The two altVH offsets do not match --- which means that a smaller file
994 * system exists in a larger partition. Verify that we have the correct
995 * alternate volume header sector as per the current parititon size.
996 * The GPT device that we are mounted on top could have changed sizes
997 * without us knowing.
999 * We're in a transaction, so it's safe to modify the partition_avh_sector
1000 * field if necessary.
1003 uint64_t sector_count
= 0;
1005 /* Get underlying device block count */
1006 retval
= ioctl(hfsmp
->hfs_devvp
->psFSRecord
->iFD
, DKIOCGETBLOCKCOUNT
, §or_count
);
1009 LFHFS_LOG(LEVEL_ERROR
, "hfs_flushvolumeheader: err %d getting block count (%s) \n", retval
, vcb
->vcbVN
);
1014 /* Partition size was changed without our knowledge */
1015 if (sector_count
!= (uint64_t)hfsmp
->hfs_logical_block_count
)
1017 hfsmp
->hfs_partition_avh_sector
= (hfsmp
->hfsPlusIOPosOffset
/ hfsmp
->hfs_logical_block_size
) + HFS_ALT_SECTOR(hfsmp
->hfs_logical_block_size
, sector_count
);
1018 /* Note: hfs_fs_avh_sector will remain unchanged */
1019 LFHFS_LOG(LEVEL_DEFAULT
, "hfs_flushvolumeheader: partition size changed, partition_avh_sector=%qu, fs_avh_sector=%qu\n", hfsmp
->hfs_partition_avh_sector
, hfsmp
->hfs_fs_avh_sector
);
1022 * We just updated the offset for AVH relative to
1023 * the partition size, so the content of that AVH
1024 * will be invalid. But since we are also maintaining
1025 * a valid AVH relative to the file system size, we
1026 * can read it since primary VH and partition AVH
1029 avh_sector
= hfsmp
->hfs_fs_avh_sector
;
1033 LFHFS_LOG(LEVEL_DEFAULT
, "hfs_flushvolumeheader: trying alternate (for %s) avh_sector=%qu\n", (avh_sector
== hfsmp
->hfs_fs_avh_sector
) ? "file system" : "partition", avh_sector
);
1037 psAltHdrBuf
= lf_hfs_generic_buf_allocate(hfsmp
->hfs_devvp
,
1038 HFS_PHYSBLK_ROUNDDOWN(avh_sector
, hfsmp
->hfs_log_per_phys
),
1039 hfsmp
->hfs_physical_block_size
, GEN_BUF_PHY_BLOCK
);
1040 if (psAltHdrBuf
== NULL
) {
1044 pvAltHdrData
= psAltHdrBuf
->pvData
;
1046 retval
= lf_hfs_generic_buf_read(psAltHdrBuf
);
1050 LFHFS_LOG(LEVEL_ERROR
, "hfs_flushvolumeheader: err %d reading alternate VH blk (vol=%s)\n", retval
, vcb
->vcbVN
);
1054 HFSPlusVolumeHeader
* altVH
= (HFSPlusVolumeHeader
*)(pvAltHdrData
+ HFS_ALT_OFFSET(hfsmp
->hfs_physical_block_size
));
1055 signature
= SWAP_BE16(altVH
->signature
);
1056 hfsversion
= SWAP_BE16(altVH
->version
);
1058 if ((signature
!= kHFSPlusSigWord
&& signature
!= kHFSXSigWord
) ||
1059 (hfsversion
< kHFSPlusVersion
) || (kHFSPlusVersion
> 100) ||
1060 (SWAP_BE32(altVH
->blockSize
) != vcb
->blockSize
))
1062 LFHFS_LOG(LEVEL_ERROR
, "hfs_flushvolumeheader: corrupt alternate VH on %s, sig 0x%04x, ver %d, blksize %d\n", vcb
->vcbVN
, signature
, hfsversion
, SWAP_BE32(altVH
->blockSize
));
1067 /* The alternate is plausible, so use it. */
1068 bcopy(altVH
, volumeHeader
, kMDBSize
);
1069 lf_hfs_generic_buf_release(psAltHdrBuf
);
1070 pvAltHdrData
= NULL
;
1074 /* No alternate VH, nothing more we can do. */
1082 journal_modify_block_start(hfsmp
->jnl
, psVolHdrBuf
);
1086 * For embedded HFS+ volumes, update create date if it changed
1087 * (ie from a setattrlist call)
1089 if ((vcb
->hfsPlusIOPosOffset
!= 0) && (SWAP_BE32 (volumeHeader
->createDate
) != vcb
->localCreateDate
))
1091 HFSMasterDirectoryBlock
*mdb
;
1093 psVolHdr2Buf
= lf_hfs_generic_buf_allocate(hfsmp
->hfs_devvp
,
1094 HFS_PHYSBLK_ROUNDDOWN(HFS_PRI_SECTOR(hfsmp
->hfs_logical_block_size
), hfsmp
->hfs_log_per_phys
),
1095 hfsmp
->hfs_physical_block_size
, GEN_BUF_PHY_BLOCK
);
1096 if (psVolHdr2Buf
== NULL
) {
1100 void *pvVolHdr2Data
= psVolHdr2Buf
->pvData
;
1102 retval
= lf_hfs_generic_buf_read(psVolHdr2Buf
);
1106 lf_hfs_generic_buf_release(psVolHdr2Buf
);
1107 LFHFS_LOG(LEVEL_ERROR
, "hfs_flushvolumeheader: err %d reading alternate VH blk (vol=%s)\n", retval
, vcb
->vcbVN
);
1111 mdb
= (HFSMasterDirectoryBlock
*)(pvVolHdr2Data
+ HFS_PRI_OFFSET(hfsmp
->hfs_physical_block_size
));
1113 if ( SWAP_BE32 (mdb
->drCrDate
) != vcb
->localCreateDate
)
1117 journal_modify_block_start(hfsmp
->jnl
, psVolHdr2Buf
);
1119 mdb
->drCrDate
= SWAP_BE32 (vcb
->localCreateDate
); /* pick up the new create date */
1122 journal_modify_block_end(hfsmp
->jnl
, psVolHdr2Buf
, NULL
, NULL
);
1126 retval
= raw_readwrite_write_mount( hfsmp
->hfs_devvp
, HFS_PHYSBLK_ROUNDDOWN(HFS_PRI_SECTOR(hfsmp
->hfs_logical_block_size
), hfsmp
->hfs_log_per_phys
), hfsmp
->hfs_physical_block_size
, pvVolHdr2Data
, hfsmp
->hfs_physical_block_size
, NULL
, NULL
);
1128 lf_hfs_generic_buf_release(psVolHdr2Buf
);
1129 pvVolHdr2Data
= NULL
;
1132 LFHFS_LOG(LEVEL_ERROR
, "hfs_flushvolumeheader: err %d writing VH blk (vol=%s)\n", retval
, vcb
->vcbVN
);
1139 lf_hfs_generic_buf_release(psVolHdr2Buf
); /* just release it */
1140 pvVolHdr2Data
= NULL
;
1144 hfs_lock_mount (hfsmp
);
1146 /* Note: only update the lower 16 bits worth of attributes */
1147 volumeHeader
->attributes
= SWAP_BE32 (vcb
->vcbAtrb
);
1148 volumeHeader
->journalInfoBlock
= SWAP_BE32 (vcb
->vcbJinfoBlock
);
1151 volumeHeader
->lastMountedVersion
= SWAP_BE32 (kHFSJMountVersion
);
1155 volumeHeader
->lastMountedVersion
= SWAP_BE32 (kHFSPlusMountVersion
);
1157 volumeHeader
->createDate
= SWAP_BE32 (vcb
->localCreateDate
); /* volume create date is in local time */
1158 volumeHeader
->modifyDate
= SWAP_BE32 (to_hfs_time(vcb
->vcbLsMod
));
1159 volumeHeader
->backupDate
= SWAP_BE32 (to_hfs_time(vcb
->vcbVolBkUp
));
1160 volumeHeader
->fileCount
= SWAP_BE32 (vcb
->vcbFilCnt
);
1161 volumeHeader
->folderCount
= SWAP_BE32 (vcb
->vcbDirCnt
);
1162 volumeHeader
->totalBlocks
= SWAP_BE32 (vcb
->totalBlocks
);
1163 volumeHeader
->freeBlocks
= SWAP_BE32 (vcb
->freeBlocks
+ vcb
->reclaimBlocks
);
1164 volumeHeader
->nextAllocation
= SWAP_BE32 (vcb
->nextAllocation
);
1165 volumeHeader
->rsrcClumpSize
= SWAP_BE32 (vcb
->vcbClpSiz
);
1166 volumeHeader
->dataClumpSize
= SWAP_BE32 (vcb
->vcbClpSiz
);
1167 volumeHeader
->nextCatalogID
= SWAP_BE32 (vcb
->vcbNxtCNID
);
1168 volumeHeader
->writeCount
= SWAP_BE32 (vcb
->vcbWrCnt
);
1169 volumeHeader
->encodingsBitmap
= SWAP_BE64 (vcb
->encodingsBitmap
);
1171 if (bcmp(vcb
->vcbFndrInfo
, volumeHeader
->finderInfo
, sizeof(volumeHeader
->finderInfo
)) != 0)
1173 bcopy(vcb
->vcbFndrInfo
, volumeHeader
->finderInfo
, sizeof(volumeHeader
->finderInfo
));
1177 if (!altflush
&& !ISSET(options
, HFS_FVH_FLUSH_IF_DIRTY
))
1182 /* Sync Extents over-flow file meta data */
1183 struct filefork
* fp
= VTOF(vcb
->extentsRefNum
);
1184 if (FTOC(fp
)->c_flag
& C_MODIFIED
)
1186 for (int iExtentCounter
= 0; iExtentCounter
< kHFSPlusExtentDensity
; iExtentCounter
++)
1188 volumeHeader
->extentsFile
.extents
[iExtentCounter
].startBlock
= SWAP_BE32 (fp
->ff_extents
[iExtentCounter
].startBlock
);
1189 volumeHeader
->extentsFile
.extents
[iExtentCounter
].blockCount
= SWAP_BE32 (fp
->ff_extents
[iExtentCounter
].blockCount
);
1191 volumeHeader
->extentsFile
.logicalSize
= SWAP_BE64 (fp
->ff_size
);
1192 volumeHeader
->extentsFile
.totalBlocks
= SWAP_BE32 (fp
->ff_blocks
);
1193 volumeHeader
->extentsFile
.clumpSize
= SWAP_BE32 (fp
->ff_clumpsize
);
1194 FTOC(fp
)->c_flag
&= ~C_MODIFIED
;
1198 /* Sync Catalog file meta data */
1199 fp
= VTOF(vcb
->catalogRefNum
);
1200 if (FTOC(fp
)->c_flag
& C_MODIFIED
)
1202 for (int iExtentCounter
= 0; iExtentCounter
< kHFSPlusExtentDensity
; iExtentCounter
++)
1204 volumeHeader
->catalogFile
.extents
[iExtentCounter
].startBlock
= SWAP_BE32 (fp
->ff_extents
[iExtentCounter
].startBlock
);
1205 volumeHeader
->catalogFile
.extents
[iExtentCounter
].blockCount
= SWAP_BE32 (fp
->ff_extents
[iExtentCounter
].blockCount
);
1207 volumeHeader
->catalogFile
.logicalSize
= SWAP_BE64 (fp
->ff_size
);
1208 volumeHeader
->catalogFile
.totalBlocks
= SWAP_BE32 (fp
->ff_blocks
);
1209 volumeHeader
->catalogFile
.clumpSize
= SWAP_BE32 (fp
->ff_clumpsize
);
1210 FTOC(fp
)->c_flag
&= ~C_MODIFIED
;
1214 /* Sync Allocation file meta data */
1215 fp
= VTOF(vcb
->allocationsRefNum
);
1216 if (FTOC(fp
)->c_flag
& C_MODIFIED
)
1218 for (int iExtentCounter
= 0; iExtentCounter
< kHFSPlusExtentDensity
; iExtentCounter
++)
1220 volumeHeader
->allocationFile
.extents
[iExtentCounter
].startBlock
= SWAP_BE32 (fp
->ff_extents
[iExtentCounter
].startBlock
);
1221 volumeHeader
->allocationFile
.extents
[iExtentCounter
].blockCount
= SWAP_BE32 (fp
->ff_extents
[iExtentCounter
].blockCount
);
1223 volumeHeader
->allocationFile
.logicalSize
= SWAP_BE64 (fp
->ff_size
);
1224 volumeHeader
->allocationFile
.totalBlocks
= SWAP_BE32 (fp
->ff_blocks
);
1225 volumeHeader
->allocationFile
.clumpSize
= SWAP_BE32 (fp
->ff_clumpsize
);
1226 FTOC(fp
)->c_flag
&= ~C_MODIFIED
;
1230 /* Sync Attribute file meta data */
1231 if (hfsmp
->hfs_attribute_vp
)
1233 fp
= VTOF(hfsmp
->hfs_attribute_vp
);
1234 for (int iExtentCounter
= 0; iExtentCounter
< kHFSPlusExtentDensity
; iExtentCounter
++)
1236 volumeHeader
->attributesFile
.extents
[iExtentCounter
].startBlock
= SWAP_BE32 (fp
->ff_extents
[iExtentCounter
].startBlock
);
1237 volumeHeader
->attributesFile
.extents
[iExtentCounter
].blockCount
= SWAP_BE32 (fp
->ff_extents
[iExtentCounter
].blockCount
);
1239 if (ISSET(FTOC(fp
)->c_flag
, C_MODIFIED
))
1241 FTOC(fp
)->c_flag
&= ~C_MODIFIED
;
1244 volumeHeader
->attributesFile
.logicalSize
= SWAP_BE64 (fp
->ff_size
);
1245 volumeHeader
->attributesFile
.totalBlocks
= SWAP_BE32 (fp
->ff_blocks
);
1246 volumeHeader
->attributesFile
.clumpSize
= SWAP_BE32 (fp
->ff_clumpsize
);
1249 /* Sync Startup file meta data */
1250 if (hfsmp
->hfs_startup_vp
)
1252 fp
= VTOF(hfsmp
->hfs_startup_vp
);
1253 if (FTOC(fp
)->c_flag
& C_MODIFIED
)
1255 for (int iExtentCounter
= 0; iExtentCounter
< kHFSPlusExtentDensity
; iExtentCounter
++)
1257 volumeHeader
->startupFile
.extents
[iExtentCounter
].startBlock
= SWAP_BE32 (fp
->ff_extents
[iExtentCounter
].startBlock
);
1258 volumeHeader
->startupFile
.extents
[iExtentCounter
].blockCount
= SWAP_BE32 (fp
->ff_extents
[iExtentCounter
].blockCount
);
1260 volumeHeader
->startupFile
.logicalSize
= SWAP_BE64 (fp
->ff_size
);
1261 volumeHeader
->startupFile
.totalBlocks
= SWAP_BE32 (fp
->ff_blocks
);
1262 volumeHeader
->startupFile
.clumpSize
= SWAP_BE32 (fp
->ff_clumpsize
);
1263 FTOC(fp
)->c_flag
&= ~C_MODIFIED
;
1272 MarkVCBClean(hfsmp
);
1273 hfs_unlock_mount (hfsmp
);
1275 /* If requested, flush out the alternate volume header */
1278 * The two altVH offsets do not match --- which means that a smaller file
1279 * system exists in a larger partition. Verify that we have the correct
1280 * alternate volume header sector as per the current parititon size.
1281 * The GPT device that we are mounted on top could have changed sizes
1282 * without us knowning.
1284 * We're in a transaction, so it's safe to modify the partition_avh_sector
1285 * field if necessary.
1287 if (hfsmp
->hfs_partition_avh_sector
!= hfsmp
->hfs_fs_avh_sector
)
1289 uint64_t sector_count
;
1291 /* Get underlying device block count */
1292 retval
= ioctl(hfsmp
->hfs_devvp
->psFSRecord
->iFD
, DKIOCGETBLOCKCOUNT
, §or_count
);
1295 LFHFS_LOG(LEVEL_ERROR
, "hfs_flushvolumeheader: err %d getting block count (%s) \n", retval
, vcb
->vcbVN
);
1300 /* Partition size was changed without our knowledge */
1301 if (sector_count
!= (uint64_t)hfsmp
->hfs_logical_block_count
)
1303 hfsmp
->hfs_partition_avh_sector
= (hfsmp
->hfsPlusIOPosOffset
/ hfsmp
->hfs_logical_block_size
) + HFS_ALT_SECTOR(hfsmp
->hfs_logical_block_size
, sector_count
);
1304 /* Note: hfs_fs_avh_sector will remain unchanged */
1305 LFHFS_LOG(LEVEL_DEFAULT
, "hfs_flushvolumeheader: altflush: partition size changed, partition_avh_sector=%qu, fs_avh_sector=%qu\n",
1306 hfsmp
->hfs_partition_avh_sector
, hfsmp
->hfs_fs_avh_sector
);
1311 * First see if we need to write I/O to the "secondary" AVH
1312 * located at FS Size - 1024 bytes, because this one will
1313 * always go into the journal. We put this AVH into the journal
1314 * because even if the filesystem size has shrunk, this LBA should be
1315 * reachable after the partition-size modification has occurred.
1316 * The one where we need to be careful is partitionsize-1024, since the
1317 * partition size should hopefully shrink.
1319 * Most of the time this block will not execute.
1321 if ((hfsmp
->hfs_fs_avh_sector
) && (hfsmp
->hfs_partition_avh_sector
!= hfsmp
->hfs_fs_avh_sector
))
1323 if (pvAltHdrData
!= NULL
)
1325 panic("We shouldn't be here!");
1329 psAltHdrBuf
= lf_hfs_generic_buf_allocate(hfsmp
->hfs_devvp
,
1330 HFS_PHYSBLK_ROUNDDOWN(hfsmp
->hfs_fs_avh_sector
, hfsmp
->hfs_log_per_phys
),
1331 hfsmp
->hfs_physical_block_size
, GEN_BUF_PHY_BLOCK
);
1333 if (psAltHdrBuf
== NULL
) {
1337 pvAltHdrData
= psAltHdrBuf
->pvData
;
1339 retval
= lf_hfs_generic_buf_read(psAltHdrBuf
);
1342 LFHFS_LOG(LEVEL_ERROR
, "hfs_flushvolumeheader: err %d reading alternate VH blk (vol=%s)\n", retval
, vcb
->vcbVN
);
1348 journal_modify_block_start(hfsmp
->jnl
, psAltHdrBuf
);
1351 bcopy(volumeHeader
, pvAltHdrData
+ HFS_ALT_OFFSET(hfsmp
->hfs_physical_block_size
), kMDBSize
);
1355 journal_modify_block_end(hfsmp
->jnl
, psAltHdrBuf
, NULL
, NULL
);
1359 retval
= raw_readwrite_write_mount( hfsmp
->hfs_devvp
, HFS_PHYSBLK_ROUNDDOWN(hfsmp
->hfs_fs_avh_sector
, hfsmp
->hfs_log_per_phys
), hfsmp
->hfs_physical_block_size
, pvAltHdrData
, hfsmp
->hfs_physical_block_size
, NULL
, NULL
);
1362 LFHFS_LOG(LEVEL_ERROR
, "hfs_flushvolumeheader: err %d writing VH blk (vol=%s)\n", retval
, vcb
->vcbVN
);
1365 lf_hfs_generic_buf_release(psAltHdrBuf
);
1366 pvAltHdrData
= NULL
;
1371 * Flush out alternate volume header located at 1024 bytes before
1372 * end of the partition as part of journal transaction. In
1373 * most cases, this will be the only alternate volume header
1374 * that we need to worry about because the file system size is
1375 * same as the partition size, therefore hfs_fs_avh_sector is
1376 * same as hfs_partition_avh_sector. This is the "priority" AVH.
1378 * However, do not always put this I/O into the journal. If we skipped the
1379 * FS-Size AVH write above, then we will put this I/O into the journal as
1380 * that indicates the two were in sync. However, if the FS size is
1381 * not the same as the partition size, we are tracking two. We don't
1382 * put it in the journal in that case, since if the partition
1383 * size changes between uptimes, and we need to replay the journal,
1384 * this I/O could generate an EIO if during replay it is now trying
1385 * to access blocks beyond the device EOF.
1387 if (hfsmp
->hfs_partition_avh_sector
)
1389 if (pvAltHdrData
!= NULL
)
1391 panic("We shouldn't be here!");
1395 psAltHdrBuf
= lf_hfs_generic_buf_allocate(hfsmp
->hfs_devvp
,
1396 HFS_PHYSBLK_ROUNDDOWN(hfsmp
->hfs_fs_avh_sector
, hfsmp
->hfs_log_per_phys
),
1397 hfsmp
->hfs_physical_block_size
, GEN_BUF_PHY_BLOCK
);
1398 if (psAltHdrBuf
== NULL
) {
1402 pvAltHdrData
= psAltHdrBuf
->pvData
;
1404 retval
= lf_hfs_generic_buf_read(psAltHdrBuf
);
1408 LFHFS_LOG(LEVEL_ERROR
, "hfs_flushvolumeheader: err %d reading alternate VH blk (vol=%s)\n", retval
, vcb
->vcbVN
);
1412 /* only one AVH, put this I/O in the journal. */
1413 if ((hfsmp
->jnl
) && (hfsmp
->hfs_partition_avh_sector
== hfsmp
->hfs_fs_avh_sector
)) {
1414 journal_modify_block_start(hfsmp
->jnl
, psAltHdrBuf
);
1417 bcopy(volumeHeader
, pvAltHdrData
+ HFS_ALT_OFFSET(hfsmp
->hfs_physical_block_size
), kMDBSize
);
1419 /* If journaled and we only have one AVH to track */
1420 if ((hfsmp
->jnl
) && (hfsmp
->hfs_partition_avh_sector
== hfsmp
->hfs_fs_avh_sector
)) {
1421 journal_modify_block_end (hfsmp
->jnl
, psAltHdrBuf
, NULL
, NULL
);
1426 * If we don't have a journal or there are two AVH's at the
1427 * moment, then this one doesn't go in the journal. Note that
1428 * this one may generate I/O errors, since the partition
1429 * can be resized behind our backs at any moment and this I/O
1430 * may now appear to be beyond the device EOF.
1432 retval
= raw_readwrite_write_mount( hfsmp
->hfs_devvp
, HFS_PHYSBLK_ROUNDDOWN(hfsmp
->hfs_fs_avh_sector
, hfsmp
->hfs_log_per_phys
), hfsmp
->hfs_physical_block_size
, pvAltHdrData
, hfsmp
->hfs_physical_block_size
, NULL
, NULL
);
1435 LFHFS_LOG(LEVEL_ERROR
, "hfs_flushvolumeheader: err %d writing VH blk (vol=%s)\n", retval
, vcb
->vcbVN
);
1438 lf_hfs_generic_buf_release(psAltHdrBuf
);
1439 pvAltHdrData
= NULL
;
1440 hfs_flush(hfsmp
, HFS_FLUSH_CACHE
);
1445 /* Finish modifying the block for the primary VH */
1447 journal_modify_block_end(hfsmp
->jnl
, psVolHdrBuf
, NULL
, NULL
);
1451 retval
= raw_readwrite_write_mount( hfsmp
->hfs_devvp
, HFS_PHYSBLK_ROUNDDOWN(priIDSector
, hfsmp
->hfs_log_per_phys
), hfsmp
->hfs_physical_block_size
, pvVolHdrData
, hfsmp
->hfs_physical_block_size
, NULL
, NULL
);
1452 /* When critical data changes, flush the device cache */
1453 if (critical
&& (retval
== 0))
1455 hfs_flush(hfsmp
, HFS_FLUSH_CACHE
);
1458 lf_hfs_generic_buf_release(psVolHdrBuf
);
1459 pvVolHdrData
= NULL
;
1462 LFHFS_LOG(LEVEL_ERROR
, "hfs_flushvolumeheader: err %d reading VH blk (vol=%s)\n", retval
, vcb
->vcbVN
);
1466 if (!(options
& HFS_FVH_SKIP_TRANSACTION
)) {
1467 hfs_end_transaction(hfsmp
);
1474 lf_hfs_generic_buf_release(psVolHdrBuf
);
1476 lf_hfs_generic_buf_release(psVolHdr2Buf
);
1478 lf_hfs_generic_buf_release(psAltHdrBuf
);
1480 if (!(options
& HFS_FVH_SKIP_TRANSACTION
)) {
1481 hfs_end_transaction(hfsmp
);
1486 /* If a runtime corruption is detected, set the volume inconsistent
1487 * bit in the volume attributes. The volume inconsistent bit is a persistent
1488 * bit which represents that the volume is corrupt and needs repair.
1489 * The volume inconsistent bit can be set from the kernel when it detects
1490 * runtime corruption or from file system repair utilities like fsck_hfs when
1491 * a repair operation fails. The bit should be cleared only from file system
1492 * verify/repair utility like fsck_hfs when a verify/repair succeeds.
1494 void hfs_mark_inconsistent(struct hfsmount
*hfsmp
, hfs_inconsistency_reason_t reason
)
1496 hfs_lock_mount (hfsmp
);
1497 if ((hfsmp
->vcbAtrb
& kHFSVolumeInconsistentMask
) == 0)
1499 hfsmp
->vcbAtrb
|= kHFSVolumeInconsistentMask
;
1500 MarkVCBDirty(hfsmp
);
1502 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
)==0)
1506 case HFS_INCONSISTENCY_DETECTED
:
1507 LFHFS_LOG(LEVEL_ERROR
, "hfs_mark_inconsistent: Runtime corruption detected on %s, fsck will be forced on next mount.\n",hfsmp
->vcbVN
);
1509 case HFS_ROLLBACK_FAILED
:
1510 LFHFS_LOG(LEVEL_ERROR
, "hfs_mark_inconsistent: Failed to roll back; volume `%s' might be inconsistent; fsck will be forced on next mount.\n", hfsmp
->vcbVN
);
1512 case HFS_OP_INCOMPLETE
:
1513 LFHFS_LOG(LEVEL_ERROR
, "hfs_mark_inconsistent: Failed to complete operation; volume `%s' might be inconsistent; fsck will be forced on next mount.\n",hfsmp
->vcbVN
);
1515 case HFS_FSCK_FORCED
:
1516 LFHFS_LOG(LEVEL_ERROR
, "hfs_mark_inconsistent: fsck requested for `%s'; fsck will be forced on next mount.\n",hfsmp
->vcbVN
);
1520 hfs_unlock_mount (hfsmp
);
1524 * Creates a UUID from a unique "name" in the HFS UUID Name space.
1525 * See version 3 UUID.
1528 hfs_getvoluuid(struct hfsmount
*hfsmp
, uuid_t result_uuid
)
1531 if (uuid_is_null(hfsmp
->hfs_full_uuid
)) {
1537 ((uint32_t *)rawUUID
)[0] = hfsmp
->vcbFndrInfo
[6];
1538 ((uint32_t *)rawUUID
)[1] = hfsmp
->vcbFndrInfo
[7];
1540 CC_MD5_Init( &md5c
);
1541 CC_MD5_Update( &md5c
, HFS_UUID_NAMESPACE_ID
, sizeof( uuid_t
) );
1542 CC_MD5_Update( &md5c
, rawUUID
, sizeof (rawUUID
) );
1543 CC_MD5_Final( result
, &md5c
);
1545 result
[6] = 0x30 | ( result
[6] & 0x0F );
1546 result
[8] = 0x80 | ( result
[8] & 0x3F );
1548 uuid_copy(hfsmp
->hfs_full_uuid
, result
);
1550 uuid_copy (result_uuid
, hfsmp
->hfs_full_uuid
);
1555 * Call into the allocator code and perform a full scan of the bitmap file.
1557 * This allows us to TRIM unallocated ranges if needed, and also to build up
1558 * an in-memory summary table of the state of the allocated blocks.
1560 void hfs_scan_blocks (struct hfsmount
*hfsmp
)
1563 * Take the allocation file lock. Journal transactions will block until
1566 int flags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
1569 * We serialize here with the HFS mount lock as we're mounting.
1571 * The mount can only proceed once this thread has acquired the bitmap
1572 * lock, since we absolutely do not want someone else racing in and
1573 * getting the bitmap lock, doing a read/write of the bitmap file,
1574 * then us getting the bitmap lock.
1576 * To prevent this, the mount thread takes the HFS mount mutex, starts us
1577 * up, then immediately msleeps on the scan_var variable in the mount
1578 * point as a condition variable. This serialization is safe since
1579 * if we race in and try to proceed while they're still holding the lock,
1580 * we'll block trying to acquire the global lock. Since the mount thread
1581 * acquires the HFS mutex before starting this function in a new thread,
1582 * any lock acquisition on our part must be linearizably AFTER the mount thread's.
1584 * Note that the HFS mount mutex is always taken last, and always for only
1585 * a short time. In this case, we just take it long enough to mark the
1586 * scan-in-flight bit.
1588 (void) hfs_lock_mount (hfsmp
);
1589 hfsmp
->scan_var
|= HFS_ALLOCATOR_SCAN_INFLIGHT
;
1590 hfs_unlock_mount (hfsmp
);
1592 /* Initialize the summary table */
1593 if (hfs_init_summary (hfsmp
))
1595 LFHFS_LOG(LEVEL_DEBUG
, "hfs_scan_blocks: could not initialize summary table for %s\n", hfsmp
->vcbVN
);
1599 * ScanUnmapBlocks assumes that the bitmap lock is held when you
1600 * call the function. We don't care if there were any errors issuing unmaps.
1602 * It will also attempt to build up the summary table for subsequent
1603 * allocator use, as configured.
1605 (void) ScanUnmapBlocks(hfsmp
);
1607 (void) hfs_lock_mount (hfsmp
);
1608 hfsmp
->scan_var
&= ~HFS_ALLOCATOR_SCAN_INFLIGHT
;
1609 hfsmp
->scan_var
|= HFS_ALLOCATOR_SCAN_COMPLETED
;
1610 hfs_unlock_mount (hfsmp
);
1612 hfs_systemfile_unlock(hfsmp
, flags
);
1616 * Look up an HFS object by ID.
1618 * The object is returned with an iocount reference and the cnode locked.
1620 * If the object is a file then it will represent the data fork.
1623 hfs_vget(struct hfsmount
*hfsmp
, cnid_t cnid
, struct vnode
**vpp
, int skiplock
, int allow_deleted
)
1625 struct vnode
*vp
= NULL
;
1626 struct cat_desc cndesc
;
1627 struct cat_attr cnattr
;
1628 struct cat_fork cnfork
;
1630 u_int32_t linkref
= 0;
1634 /* Check for cnids that should't be exported. */
1635 if ((cnid
< kHFSFirstUserCatalogNodeID
) &&
1636 (cnid
!= kHFSRootFolderID
&& cnid
!= kHFSRootParentID
)) {
1639 /* Don't export our private directories. */
1640 if (cnid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
1641 cnid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1645 * Check the hash first
1647 vp
= hfs_chash_getvnode(hfsmp
, cnid
, 0, skiplock
, allow_deleted
);
1653 bzero(&cndesc
, sizeof(cndesc
));
1654 bzero(&cnattr
, sizeof(cnattr
));
1655 bzero(&cnfork
, sizeof(cnfork
));
1658 * Not in hash, lookup in catalog
1660 if (cnid
== kHFSRootParentID
) {
1661 static char hfs_rootname
[] = "/";
1663 cndesc
.cd_nameptr
= (const u_int8_t
*)&hfs_rootname
[0];
1664 cndesc
.cd_namelen
= 1;
1665 cndesc
.cd_parentcnid
= kHFSRootParentID
;
1666 cndesc
.cd_cnid
= kHFSRootFolderID
;
1667 cndesc
.cd_flags
= CD_ISDIR
;
1669 cnattr
.ca_fileid
= kHFSRootFolderID
;
1670 cnattr
.ca_linkcount
= 1;
1671 cnattr
.ca_entries
= 1;
1672 cnattr
.ca_dircount
= 1;
1673 cnattr
.ca_mode
= (S_IFDIR
| S_IRWXU
| S_IRWXG
| S_IRWXO
);
1677 const char *nameptr
;
1679 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
1680 error
= cat_idlookup(hfsmp
, cnid
, 0, 0, &cndesc
, &cnattr
, &cnfork
);
1681 hfs_systemfile_unlock(hfsmp
, lockflags
);
1689 * Check for a raw hardlink inode and save its linkref.
1691 pid
= cndesc
.cd_parentcnid
;
1692 nameptr
= (const char *)cndesc
.cd_nameptr
;
1693 if ((pid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
1694 cndesc
.cd_namelen
> HFS_INODE_PREFIX_LEN
&&
1695 (bcmp(nameptr
, HFS_INODE_PREFIX
, HFS_INODE_PREFIX_LEN
) == 0)) {
1696 linkref
= (uint32_t) strtoul(&nameptr
[HFS_INODE_PREFIX_LEN
], NULL
, 10);
1698 } else if ((pid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) &&
1699 cndesc
.cd_namelen
> HFS_DIRINODE_PREFIX_LEN
&&
1700 (bcmp(nameptr
, HFS_DIRINODE_PREFIX
, HFS_DIRINODE_PREFIX_LEN
) == 0)) {
1701 linkref
= (uint32_t) strtoul(&nameptr
[HFS_DIRINODE_PREFIX_LEN
], NULL
, 10);
1703 } else if ((pid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
1704 cndesc
.cd_namelen
> HFS_DELETE_PREFIX_LEN
&&
1705 (bcmp(nameptr
, HFS_DELETE_PREFIX
, HFS_DELETE_PREFIX_LEN
) == 0)) {
1707 cat_releasedesc(&cndesc
);
1708 return (ENOENT
); /* open unlinked file */
1713 * Finish initializing cnode descriptor for hardlinks.
1715 * We need a valid name and parent for reverse lookups.
1719 struct cat_desc linkdesc
;
1722 cnattr
.ca_linkref
= linkref
;
1723 bzero (&linkdesc
, sizeof (linkdesc
));
1726 * If the caller supplied the raw inode value, then we don't know exactly
1727 * which hardlink they wanted. It's likely that they acquired the raw inode
1728 * value BEFORE the item became a hardlink, in which case, they probably
1729 * want the oldest link. So request the oldest link from the catalog.
1731 * Unfortunately, this requires that we iterate through all N hardlinks. On the plus
1732 * side, since we know that we want the last linkID, we can also have this one
1733 * call give us back the name of the last ID, since it's going to have it in-hand...
1735 linkerr
= hfs_lookup_lastlink (hfsmp
, linkref
, &lastid
, &linkdesc
);
1736 if ((linkerr
== 0) && (lastid
!= 0)) {
1738 * Release any lingering buffers attached to our local descriptor.
1739 * Then copy the name and other business into the cndesc
1741 cat_releasedesc (&cndesc
);
1742 bcopy (&linkdesc
, &cndesc
, sizeof(linkdesc
));
1744 /* If it failed, the linkref code will just use whatever it had in-hand below. */
1748 int newvnode_flags
= 0;
1749 error
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cndesc
, 0, &cnattr
, &cnfork
, &vp
, &newvnode_flags
);
1751 VTOC(vp
)->c_flag
|= C_HARDLINK
;
1753 //TBD - this set is for vfs -> since we have the C_HARDLINK
1754 // currently disable this set.
1755 //vnode_setmultipath(vp);
1760 int newvnode_flags
= 0;
1762 void *buf
= hfs_malloc(MAXPATHLEN
);
1764 /* Supply hfs_getnewvnode with a component name. */
1765 struct componentname cn
= {
1766 .cn_nameiop
= LOOKUP
,
1767 .cn_flags
= ISLASTCN
,
1768 .cn_pnlen
= MAXPATHLEN
,
1769 .cn_namelen
= cndesc
.cd_namelen
,
1774 bcopy(cndesc
.cd_nameptr
, cn
.cn_nameptr
, cndesc
.cd_namelen
+ 1);
1775 error
= hfs_getnewvnode(hfsmp
, NULL
, &cn
, &cndesc
, 0, &cnattr
, &cnfork
, &vp
, &newvnode_flags
);
1777 if (error
== 0 && (VTOC(vp
)->c_flag
& C_HARDLINK
)) {
1778 hfs_savelinkorigin(VTOC(vp
), cndesc
.cd_parentcnid
);
1783 cat_releasedesc(&cndesc
);
1786 if (vp
&& skiplock
) {
1787 hfs_unlock(VTOC(vp
));
1793 hfs_GetInfoByID(struct hfsmount
*hfsmp
, cnid_t cnid
, UVFSFileAttributes
*file_attrs
, char pcName
[MAX_UTF8_NAME_LENGTH
])
1795 struct vnode
*psVnode
= NULL
;
1796 int error
= hfs_vget(hfsmp
, cnid
, &psVnode
, 0, 0);
1797 if (error
|| psVnode
== NULL
) {
1798 if (psVnode
!= NULL
) hfs_unlock(VTOC(psVnode
));
1799 hfs_vnop_reclaim(psVnode
);
1802 vnode_GetAttrInternal (psVnode
, file_attrs
);
1803 hfs_unlock(VTOC(psVnode
));
1806 if (cnid
== kHFSRootFolderID
)
1809 //Make sure we actually have the name in the vnode
1810 if (psVnode
->sFSParams
.vnfs_cnp
&& psVnode
->sFSParams
.vnfs_cnp
->cn_nameptr
)
1811 strlcpy(pcName
, (char*) psVnode
->sFSParams
.vnfs_cnp
->cn_nameptr
, MAX_UTF8_NAME_LENGTH
);
1816 error
= hfs_vnop_reclaim(psVnode
);
1821 * Return the root of a filesystem.
1823 int hfs_vfs_root(struct mount
*mp
, struct vnode
**vpp
)
1825 return hfs_vget(VFSTOHFS(mp
), (cnid_t
)kHFSRootFolderID
, vpp
, 1, 0);
1829 * unmount system call
1831 int hfs_unmount(struct mount
*mp
)
1833 struct hfsmount
*hfsmp
= VFSTOHFS(mp
);
1834 int retval
= E_NONE
;
1836 if (hfsmp
->hfs_flags
& HFS_SUMMARY_TABLE
)
1838 if (hfsmp
->hfs_summary_table
)
1842 * Take the bitmap lock to serialize against a concurrent bitmap scan still in progress
1844 if (hfsmp
->hfs_allocation_vp
)
1846 err
= hfs_lock (VTOC(hfsmp
->hfs_allocation_vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
);
1848 hfs_free(hfsmp
->hfs_summary_table
);
1849 hfsmp
->hfs_summary_table
= NULL
;
1850 hfsmp
->hfs_flags
&= ~HFS_SUMMARY_TABLE
;
1852 if (err
== 0 && hfsmp
->hfs_allocation_vp
)
1854 hfs_unlock (VTOC(hfsmp
->hfs_allocation_vp
));
1860 * Invalidate our caches and release metadata vnodes
1863 journal_release(hfsmp
->jnl
);
1868 int iFD
= hfsmp
->hfs_devvp
->psFSRecord
->iFD
;
1869 // Remove Buffer cache entries realted to the mount
1870 lf_hfs_generic_buf_cache_clear_by_iFD(iFD
);
1872 vnode_rele(hfsmp
->hfs_devvp
);
1874 hfs_locks_destroy(hfsmp
);
1875 hfs_delete_chash(hfsmp
);
1876 hfs_idhash_destroy(hfsmp
);
1878 hfs_assert(TAILQ_EMPTY(&hfsmp
->hfs_reserved_ranges
[HFS_TENTATIVE_BLOCKS
]) && TAILQ_EMPTY(&hfsmp
->hfs_reserved_ranges
[HFS_LOCKED_BLOCKS
]));
1879 hfs_assert(!hfsmp
->lockedBlocks
);
1885 /* Update volume encoding bitmap (HFS Plus only)
1887 * Mark a legacy text encoding as in-use (as needed)
1888 * in the volume header of this HFS+ filesystem.
1891 hfs_setencodingbits(struct hfsmount
*hfsmp
, u_int32_t encoding
)
1893 #define kIndexMacUkrainian 48 /* MacUkrainian encoding is 152 */
1894 #define kIndexMacFarsi 49 /* MacFarsi encoding is 140 */
1900 case kTextEncodingMacUkrainian
:
1901 index
= kIndexMacUkrainian
;
1903 case kTextEncodingMacFarsi
:
1904 index
= kIndexMacFarsi
;
1911 /* Only mark the encoding as in-use if it wasn't already set */
1912 if (index
< 64 && (hfsmp
->encodingsBitmap
& (u_int64_t
)(1ULL << index
)) == 0) {
1913 hfs_lock_mount (hfsmp
);
1914 hfsmp
->encodingsBitmap
|= (u_int64_t
)(1ULL << index
);
1915 MarkVCBDirty(hfsmp
);
1916 hfs_unlock_mount(hfsmp
);
1921 * Update volume stats
1923 * On journal volumes this will cause a volume header flush
1926 hfs_volupdate(struct hfsmount
*hfsmp
, enum volop op
, int inroot
)
1930 hfs_lock_mount (hfsmp
);
1932 MarkVCBDirty(hfsmp
);
1933 hfsmp
->hfs_mtime
= tv
.tv_sec
;
1939 if (hfsmp
->hfs_dircount
!= 0xFFFFFFFF)
1940 ++hfsmp
->hfs_dircount
;
1941 if (inroot
&& hfsmp
->vcbNmRtDirs
!= 0xFFFF)
1942 ++hfsmp
->vcbNmRtDirs
;
1945 if (hfsmp
->hfs_dircount
!= 0)
1946 --hfsmp
->hfs_dircount
;
1947 if (inroot
&& hfsmp
->vcbNmRtDirs
!= 0xFFFF)
1948 --hfsmp
->vcbNmRtDirs
;
1951 if (hfsmp
->hfs_filecount
!= 0xFFFFFFFF)
1952 ++hfsmp
->hfs_filecount
;
1953 if (inroot
&& hfsmp
->vcbNmFls
!= 0xFFFF)
1957 if (hfsmp
->hfs_filecount
!= 0)
1958 --hfsmp
->hfs_filecount
;
1959 if (inroot
&& hfsmp
->vcbNmFls
!= 0xFFFF)
1964 hfs_unlock_mount (hfsmp
);
1965 hfs_flushvolumeheader(hfsmp
, 0);