#include "lf_hfs_common.h"
#include <CommonCrypto/CommonDigest.h>
#include <stdatomic.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/disk.h>
#include <sys/stat.h>
#include <stdlib.h>
#include "lf_hfs_logger.h"
#include "lf_hfs_mount.h"
#include "lf_hfs.h"
#include "lf_hfs_catalog.h"
#include "lf_hfs_cnode.h"
#include "lf_hfs_chash.h"
#include "lf_hfs_format.h"
#include "lf_hfs_locks.h"
#include "lf_hfs_endian.h"
#include "lf_hfs_locks.h"
#include "lf_hfs_utils.h"
#include "lf_hfs_raw_read_write.h"
#include "lf_hfs_vfsutils.h"
#include "lf_hfs_vfsops.h"
#include "lf_hfs_volume_allocation.h"
#include "lf_hfs_catalog.h"
#include "lf_hfs_link.h"
#include "lf_hfs_vnops.h"
#include "lf_hfs_generic_buf.h"
#include "lf_hfs_fsops_handler.h"
#include "lf_hfs_journal.h"
#include "lf_hfs_fileops_handler.h"
#include <spawn.h>
static void hfs_locks_destroy(struct hfsmount *hfsmp);
static int hfs_mountfs(struct vnode *devvp, struct mount *mp, struct hfs_mount_args *args);
static int
setup_posix_file_action_for_fsck(posix_spawn_file_actions_t *file_actions, int fd)
{
int error;
if (file_actions == NULL || fd < 0)
{
return EINVAL;
}
error = posix_spawn_file_actions_init(file_actions);
if (error)
{
goto out;
}
error = posix_spawn_file_actions_addinherit_np(file_actions, 0);
if (error)
{
goto out;
}
error = posix_spawn_file_actions_addinherit_np(file_actions, 1);
if (error)
{
goto out;
}
error = posix_spawn_file_actions_addinherit_np(file_actions, 2);
if (error)
{
goto out;
}
error = posix_spawn_file_actions_addinherit_np(file_actions, fd);
out:
return error;
}
static int
setup_spawnattr_for_fsck(posix_spawnattr_t *spawn_attr)
{
int error;
error = posix_spawnattr_init(spawn_attr);
if (error)
{
goto out;
}
error = posix_spawnattr_setflags(spawn_attr, POSIX_SPAWN_CLOEXEC_DEFAULT);
out:
return error;
}
int fsck_mount_and_replay(int iFd) {
int iErr = 0;
LFHFS_LOG(LEVEL_DEBUG, "fsck_mount_and_replay %d", iFd);
UVFSFileNode sRootNode;
iErr = LFHFS_Taste(iFd);
if (iErr) {
LFHFS_LOG(LEVEL_DEBUG, "LFHFS_Taste returned %d", iErr);
return iErr;
}
UVFSScanVolsRequest sScanVolsReq = {0};
UVFSScanVolsReply sScanVolsReply = {0};
iErr = LFHFS_ScanVols(iFd, &sScanVolsReq, &sScanVolsReply);
if (iErr) {
LFHFS_LOG(LEVEL_DEBUG, "LFHFS_ScanVol returned %d", iErr);
return iErr;
}
iErr = LFHFS_Mount(iFd, 0, 0, NULL, &sRootNode); if (iErr) {
LFHFS_LOG(LEVEL_DEBUG, "fsck_mount_and_replay: LFHFS_Mount returned %d", iErr);
return EINVAL;
}
LFHFS_Unmount (sRootNode, UVFSUnmountHintNone);
return iErr;
}
#define PATH_TO_FSCK "/System/Library/Filesystems/hfs.fs/Contents/Resources/fsck_hfs"
int
fsck_hfs(int fd, check_flags_t how)
{
pid_t child;
pid_t child_found;
int child_status;
extern char **environ;
char fdescfs_path[24];
posix_spawn_file_actions_t file_actions;
int result;
posix_spawnattr_t spawn_attr;
if (how == QUICK_CHECK) {
if (fsck_mount_and_replay(fd) == 0) {
return(0);
}
}
LFHFS_LOG(LEVEL_DEFAULT, "fsck_hfs - fsck start for %d", fd);
snprintf(fdescfs_path, sizeof(fdescfs_path), "/dev/fd/%d", fd);
const char * argv[] = {"fsck_hfs", "-q", fdescfs_path, NULL};
switch (how)
{
case QUICK_CHECK:
break;
case CHECK:
argv[1] = "-n";
break;
case CHECK_AND_REPAIR:
argv[1] = "-y";
break;
default:
LFHFS_LOG(LEVEL_ERROR, "Invalid how flags for the check, ignoring; %d", how);
break;
}
LFHFS_LOG(LEVEL_DEBUG, "fsck_hfs params: %s %s %s", argv[1], argv[2], argv[3]);
result = setup_posix_file_action_for_fsck(&file_actions, fd);
if (result)
{
goto out;
}
result = setup_spawnattr_for_fsck(&spawn_attr);
if (result)
{
posix_spawn_file_actions_destroy(&file_actions);
goto out;
}
result = posix_spawn(&child,
PATH_TO_FSCK,
&file_actions,
&spawn_attr,
(char * const *)argv,
environ);
posix_spawn_file_actions_destroy(&file_actions);
posix_spawnattr_destroy(&spawn_attr);
if (result)
{
LFHFS_LOG(LEVEL_ERROR, "posix_spawn fsck_hfs: error=%d", result);
goto out;
}
do {
child_found = waitpid(child, &child_status, 0);
} while (child_found == -1 && errno == EINTR);
if (child_found == -1)
{
result = errno;
LFHFS_LOG(LEVEL_ERROR, "waitpid fsck_hfs: errno=%d", result);
goto out;
}
if (WIFEXITED(child_status))
{
result = WEXITSTATUS(child_status);
if (result)
{
LFHFS_LOG(LEVEL_ERROR, "fsck_hfs: exited with status %d", result);
result = EILSEQ;
} else {
LFHFS_LOG(LEVEL_ERROR, "fsck_hfs: exited with status %d", result);
}
}
else
{
result = WTERMSIG(child_status);
LFHFS_LOG(LEVEL_ERROR, "fsck_hfs: terminated by signal %d", result);
result = EINTR;
}
out:
LFHFS_LOG(LEVEL_DEFAULT, "fsck_hfs - fsck finish for %d with err %d", fd, result);
return result;
}
int
hfs_mount(struct mount *mp, vnode_t devvp, user_addr_t data)
{
struct hfsmount *hfsmp = NULL;
int retval = 0;
if ( devvp == NULL )
{
retval = EINVAL;
goto fail;
}
retval = hfs_mountfs(devvp, mp, NULL);
if (retval)
{
if (retval != ENOTSUP)
{
retval = fsck_hfs(devvp->psFSRecord->iFD, CHECK_AND_REPAIR);
if (!retval) {
retval = hfs_mountfs(devvp, mp, NULL);
if (!retval)
goto mount_passed;
}
}
LFHFS_LOG(LEVEL_ERROR, "hfs_mount: hfs_mountfs returned error=%d\n", retval);
goto fail;
}
mount_passed:
hfsmp = VFSTOHFS(mp);
hfsmp->hfs_defrag_max = HFS_INITIAL_DEFRAG_SIZE;
if (!data)
{
hfsmp->hfs_uid = UNKNOWNUID;
hfsmp->hfs_gid = UNKNOWNGID;
hfsmp->hfs_dir_mask = (S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
hfsmp->hfs_file_mask = (S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
hfsmp->reserveBlocks = (uint32_t) ((u_int64_t)hfsmp->totalBlocks * HFS_MINFREE) / 100;
hfsmp->reserveBlocks = MIN(hfsmp->reserveBlocks, HFS_MAXRESERVE / hfsmp->blockSize);
}
fail:
return (retval);
}
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)
{
int retval = 0;
HFSMasterDirectoryBlock *mdbp = NULL;
void* pvBuffer = NULL;
int mntwrapper;
u_int64_t disksize;
u_int64_t log_blkcnt;
u_int32_t log_blksize;
u_int32_t phys_blksize;
u_int32_t minblksize;
u_int32_t iswritable;
u_int64_t mdb_offset;
u_int32_t device_features = 0;
mntwrapper = 0;
minblksize = kHFSBlockSize;
*hfsmp = NULL;
if (ioctl(devvp->psFSRecord->iFD, DKIOCGETBLOCKSIZE, &log_blksize))
{
LFHFS_LOG(LEVEL_DEBUG, "hfs_mountfs: DKIOCGETBLOCKSIZE failed\n");
retval = ENXIO;
goto error_exit;
}
if (log_blksize == 0 || log_blksize > 1024*1024*1024)
{
LFHFS_LOG(LEVEL_ERROR, "hfs_mountfs: logical block size 0x%x looks bad. Not mounting.\n", log_blksize);
retval = ENXIO;
goto error_exit;
}
if (ioctl(devvp->psFSRecord->iFD, DKIOCGETPHYSICALBLOCKSIZE, &phys_blksize))
{
if ((retval != ENOTSUP) && (retval != ENOTTY))
{
LFHFS_LOG(LEVEL_DEBUG, "hfs_mountfs: DKIOCGETPHYSICALBLOCKSIZE failed\n");
retval = ENXIO;
goto error_exit;
}
phys_blksize = log_blksize;
}
if (phys_blksize == 0 || phys_blksize > MAXBSIZE)
{
LFHFS_LOG(LEVEL_ERROR, "hfs_mountfs: physical block size 0x%x looks bad. Not mounting.\n", phys_blksize);
retval = ENXIO;
goto error_exit;
}
if (phys_blksize < log_blksize) {
phys_blksize = log_blksize;
}
if (ioctl(devvp->psFSRecord->iFD, DKIOCGETBLOCKCOUNT, &log_blkcnt))
{
LFHFS_LOG(LEVEL_DEBUG, "hfs_mountfs: DKIOCGETBLOCKCOUNT failed\n");
retval = ENXIO;
goto error_exit;
}
disksize = (u_int64_t)log_blkcnt * (u_int64_t)log_blksize;
mdbp = hfs_mallocz(kMDBSize);
if (mdbp == NULL)
{
retval = ENOMEM;
goto error_exit;
}
pvBuffer = hfs_malloc(phys_blksize);
if (pvBuffer == NULL)
{
retval = ENOMEM;
goto error_exit;
}
mdb_offset = (uint64_t) HFS_PRI_SECTOR(log_blksize);
retval = raw_readwrite_read_mount( devvp, HFS_PHYSBLK_ROUNDDOWN(mdb_offset, (phys_blksize/log_blksize)), phys_blksize, pvBuffer, phys_blksize, NULL, NULL);
if (retval)
{
LFHFS_LOG(LEVEL_DEBUG, "hfs_mountfs: raw_readwrite_read_mount failed with %d\n", retval);
goto error_exit;
}
bcopy(pvBuffer + HFS_PRI_OFFSET(phys_blksize), mdbp, kMDBSize);
hfs_free(pvBuffer);
pvBuffer = NULL;
*hfsmp = hfs_malloc(sizeof(struct hfsmount));
if (*hfsmp == NULL)
{
retval = ENOMEM;
goto error_exit;
}
memset( *hfsmp, 0, sizeof(struct hfsmount) );
if (mp->mnt_flag == MNT_RDONLY) (*hfsmp)->hfs_flags = HFS_READ_ONLY;
hfs_chashinit_finish(*hfsmp);
hfs_idhash_init (*hfsmp);
if (ioctl(devvp->psFSRecord->iFD, DKIOCGETFEATURES, &device_features) == 0)
{
if (device_features & DK_FEATURE_UNMAP)
{
(*hfsmp)->hfs_flags |= HFS_UNMAP;
}
if(device_features & DK_FEATURE_BARRIER)
{
(*hfsmp)->hfs_flags |= HFS_FEATURE_BARRIER;
}
}
lf_lck_mtx_init(&(*hfsmp)->hfs_mutex);
lf_lck_mtx_init(&(*hfsmp)->sync_mutex);
lf_lck_rw_init(&(*hfsmp)->hfs_global_lock);
lf_lck_spin_init(&(*hfsmp)->vcbFreeExtLock);
if (mp)
{
mp->psHfsmount = (*hfsmp);
}
(*hfsmp)->hfs_mp = mp;
(*hfsmp)->hfs_raw_dev = 0; (*hfsmp)->hfs_devvp = devvp;
(*hfsmp)->hfs_logical_block_size = log_blksize;
(*hfsmp)->hfs_logical_block_count = log_blkcnt;
(*hfsmp)->hfs_logical_bytes = (uint64_t) log_blksize * (uint64_t) log_blkcnt;
(*hfsmp)->hfs_physical_block_size = phys_blksize;
(*hfsmp)->hfs_log_per_phys = (phys_blksize / log_blksize);
(*hfsmp)->hfs_flags |= HFS_WRITEABLE_MEDIA;
if (mp && (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS))
{
(*hfsmp)->hfs_flags |= HFS_UNKNOWN_PERMS;
}
if (mp && (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS))
{
(*hfsmp)->hfs_uid = UNKNOWNUID;
(*hfsmp)->hfs_gid = UNKNOWNGID;
(*hfsmp)->hfs_dir_mask = UNKNOWNPERMISSIONS & ALLPERMS;
(*hfsmp)->hfs_file_mask = UNKNOWNPERMISSIONS & DEFFILEMODE;
}
if (ioctl(devvp->psFSRecord->iFD, DKIOCISWRITABLE, &iswritable) == 0)
{
if (iswritable)
{
(*hfsmp)->hfs_flags |= HFS_WRITEABLE_MEDIA;
}
else
{
(*hfsmp)->hfs_flags &= ~HFS_WRITEABLE_MEDIA;
}
}
rl_init(&(*hfsmp)->hfs_reserved_ranges[0]);
rl_init(&(*hfsmp)->hfs_reserved_ranges[1]);
struct timeval tv;
microuptime(&tv);
(*hfsmp)->hfs_mount_time = tv.tv_sec;
int jnl_disable = 0;
if ((SWAP_BE16(mdbp->drSigWord) == kHFSSigWord) && (mntwrapper || (SWAP_BE16(mdbp->drEmbedSigWord) != kHFSPlusSigWord)))
{
LFHFS_LOG(LEVEL_DEBUG, "hfs_mountfs: Not supporting standard HFS\n");
retval = ENOTSUP;
goto error_exit;
}
else if (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord)
{
*embeddedOffset = SWAP_BE16(mdbp->drAlBlSt) * kHFSBlockSize;
*embeddedOffset += (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.startBlock) * (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz);
if ((*embeddedOffset % log_blksize) != 0)
{
LFHFS_LOG(LEVEL_DEFAULT, "hfs_mountfs: embedded volume offset not a multiple of physical block size (%d); switching to 512\n", log_blksize);
retval = ENXIO;
goto error_exit;
}
disksize = (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.blockCount) * (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz);
(*hfsmp)->hfs_logical_block_count = disksize / log_blksize;
(*hfsmp)->hfs_logical_bytes = (uint64_t) (*hfsmp)->hfs_logical_block_count * (uint64_t) (*hfsmp)->hfs_logical_block_size;
mdb_offset = (uint64_t)((*embeddedOffset / log_blksize) + HFS_PRI_SECTOR(log_blksize));
pvBuffer = hfs_malloc(phys_blksize);
if (pvBuffer == NULL)
{
retval = ENOMEM;
goto error_exit;
}
retval = raw_readwrite_read_mount( devvp, HFS_PHYSBLK_ROUNDDOWN(mdb_offset, (phys_blksize/log_blksize)), phys_blksize, pvBuffer, phys_blksize, NULL, NULL);
if (retval)
{
LFHFS_LOG(LEVEL_DEBUG, "hfs_mountfs: raw_readwrite_read_mount (2) failed with %d\n", retval);
goto error_exit;
}
bcopy(pvBuffer + HFS_PRI_OFFSET(phys_blksize), mdbp, kMDBSize);
*vhp = (HFSPlusVolumeHeader*) mdbp;
hfs_free(pvBuffer);
pvBuffer = NULL;
}
else
{
*embeddedOffset = 0;
*vhp = (HFSPlusVolumeHeader*) mdbp;
}
retval = hfs_ValidateHFSPlusVolumeHeader(*hfsmp, *vhp);
if (retval)
goto error_exit;
if (SWAP_BE32((*vhp)->blockSize) < (*hfsmp)->hfs_physical_block_size)
{
phys_blksize = (*hfsmp)->hfs_logical_block_size;
(*hfsmp)->hfs_physical_block_size = (*hfsmp)->hfs_logical_block_size;
(*hfsmp)->hfs_log_per_phys = 1;
if (retval)
goto error_exit;
}
if ( (mp && !(mp->mnt_flag & MNT_ROOTFS))
&& (SWAP_BE32((*vhp)->attributes) & kHFSVolumeInconsistentMask)
&& !((*hfsmp)->hfs_flags & HFS_READ_ONLY))
{
LFHFS_LOG(LEVEL_DEBUG, "hfs_mountfs: failed to mount non-root inconsistent disk\n");
retval = EINVAL;
goto error_exit;
}
(*hfsmp)->jnl = NULL;
(*hfsmp)->jvp = NULL;
if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS) && args->journal_disable)
{
jnl_disable = 1;
}
uint32_t lastMountedVersion = SWAP_BE32((*vhp)->lastMountedVersion);
uint32_t attributes = SWAP_BE32((*vhp)->attributes);
if ( (lastMountedVersion == kHFSJMountVersion) &&
(attributes & kHFSVolumeJournaledMask) &&
!jnl_disable)
{
if ((retval = hfs_early_journal_init(*hfsmp, *vhp, args, *embeddedOffset, mdb_offset, mdbp)) != 0)
{
if (retval == EROFS)
{
LFHFS_LOG(LEVEL_DEBUG, "hfs_mountfs: hfs_early_journal_init indicated external jnl \n");
retval = EINVAL;
goto error_exit;
}
LFHFS_LOG(LEVEL_DEBUG, "hfs_mountfs: hfs_early_journal_init failed, setting to FSK \n");
HFSPlusVolumeHeader *jvhp;
(*hfsmp)->hfs_flags |= HFS_NEED_JNL_RESET;
if (mdb_offset == 0)
{
mdb_offset = (uint64_t)((*embeddedOffset / log_blksize) + HFS_PRI_SECTOR(log_blksize));
}
pvBuffer = hfs_malloc(phys_blksize);
if (pvBuffer == NULL)
{
retval = ENOMEM;
goto error_exit;
}
retval = raw_readwrite_read_mount( devvp, HFS_PHYSBLK_ROUNDDOWN(mdb_offset, (*hfsmp)->hfs_log_per_phys), phys_blksize, pvBuffer, phys_blksize, NULL, NULL);
if (retval)
{
LFHFS_LOG(LEVEL_DEBUG, "hfs_mountfs: raw_readwrite_read_mount (3) failed with %d\n", retval);
goto error_exit;
}
jvhp = (HFSPlusVolumeHeader *)(pvBuffer + HFS_PRI_OFFSET(phys_blksize));
if (SWAP_BE16(jvhp->signature) == kHFSPlusSigWord || SWAP_BE16(jvhp->signature) == kHFSXSigWord)
{
LFHFS_LOG(LEVEL_DEFAULT, "hfs_mountfs: Journal replay fail. Writing lastMountVersion as FSK!\n");
jvhp->lastMountedVersion = SWAP_BE32(kFSKMountVersion);
retval = raw_readwrite_write_mount( devvp, HFS_PHYSBLK_ROUNDDOWN(mdb_offset, (*hfsmp)->hfs_log_per_phys), phys_blksize, pvBuffer, phys_blksize, NULL, NULL );
if (retval)
{
LFHFS_LOG(LEVEL_DEBUG, "hfs_mountfs: raw_readwrite_write_mount (1) failed with %d\n", retval);
goto error_exit;
}
hfs_free(pvBuffer);
pvBuffer = NULL;
}
LFHFS_LOG(LEVEL_DEBUG, "hfs_mountfs: hfs_early_journal_init failed, erroring out \n");
retval = EINVAL;
goto error_exit;
}
}
retval = hfs_MountHFSPlusVolume(*hfsmp, *vhp, *embeddedOffset, disksize, bFailForDirty);
if ((retval == ENXIO) && (log_blksize > 512) && (log_blksize != minblksize))
{
LFHFS_LOG(LEVEL_DEFAULT, "hfs_mountfs: could not use physical block size (%d).\n", log_blksize);
goto error_exit;
}
else if ( retval )
{
LFHFS_LOG(LEVEL_DEBUG, "hfs_mountfs: hfs_MountHFSPlusVolume encountered failure %d \n", retval);
goto error_exit;
}
return (retval);
error_exit:
if (pvBuffer)
hfs_free(pvBuffer);
hfs_free(mdbp);
if (*hfsmp)
{
hfs_locks_destroy(*hfsmp);
hfs_delete_chash(*hfsmp);
hfs_idhash_destroy (*hfsmp);
hfs_free(*hfsmp);
*hfsmp = NULL;
}
return (retval);
}
int hfs_ScanVolGetVolName(int iFd, char* pcVolumeName)
{
int retval = 0;
HFSPlusVolumeHeader *vhp;
off_t embeddedOffset;
struct hfsmount *hfsmp;
struct mount* psMount = hfs_mallocz(sizeof(struct mount));
struct vnode* psDevVnode = hfs_mallocz(sizeof(struct vnode));
struct cnode* psDevCnode = hfs_mallocz(sizeof(struct cnode));
struct filefork* psDevFileFork = hfs_mallocz(sizeof(struct filefork));
FileSystemRecord_s *psFSRecord = hfs_mallocz(sizeof(FileSystemRecord_s));
if ( psMount == NULL || psDevVnode == NULL || psDevCnode == NULL || psDevFileFork == NULL || psFSRecord == NULL )
{
retval = ENOMEM;
LFHFS_LOG(LEVEL_ERROR, "hfs_ScanVolGetVolName: failed to malloc initial system files\n");
goto exit;
}
psFSRecord->iFD = iFd;
psDevVnode->psFSRecord = psFSRecord;
psDevVnode->sFSParams.vnfs_marksystem = 1;
psDevVnode->bIsMountVnode = true;
psDevFileFork->ff_data.cf_blocks = 3;
psDevFileFork->ff_data.cf_extents[0].blockCount = 1;
psDevFileFork->ff_data.cf_extents[0].startBlock = 0;
psDevVnode->sFSParams.vnfs_fsnode = psDevCnode;
psDevCnode->c_vp = psDevVnode;
psDevVnode->is_rsrc = false;
psDevCnode->c_datafork = psDevFileFork;
psDevVnode->sFSParams.vnfs_mp = psMount;
retval = hfs_InitialMount(psDevVnode, psMount, 0, &vhp, &embeddedOffset, &hfsmp, false);
if (retval)
{
goto exit;
}
else
{
strlcpy(pcVolumeName, (char*) hfsmp->vcbVN, UVFS_SCANVOLS_VOLNAME_MAX);
}
if (vhp) free(vhp);
if (hfsmp)
{
if (hfsmp->jnl) {
journal_release(hfsmp->jnl);
hfsmp->jnl = NULL;
}
hfsUnmount(hfsmp);
hfs_locks_destroy(hfsmp);
hfs_delete_chash(hfsmp);
hfs_idhash_destroy (hfsmp);
hfs_free(hfsmp);
hfsmp = NULL;
}
exit:
if (retval) {
LFHFS_LOG(LEVEL_ERROR, "hfs_ScanVolGetVolName: failed with error %d, returning empty name and no error\n",retval);
pcVolumeName[0] = '\0';
}
if (psMount) free (psMount);
if (psDevVnode) free (psDevVnode);
if (psDevCnode) free (psDevCnode);
if (psDevFileFork) free (psDevFileFork);
if (psFSRecord) free (psFSRecord);
return 0;
}
static int
hfs_mountfs(struct vnode *devvp, struct mount *mp, struct hfs_mount_args *args)
{
int retval = 0;
HFSPlusVolumeHeader *vhp;
off_t embeddedOffset;
struct hfsmount *hfsmp;
retval = hfs_InitialMount(devvp, mp, args, &vhp, &embeddedOffset, &hfsmp, true);
if ( retval )
{
LFHFS_LOG(LEVEL_DEBUG, "hfs_mountfs: hfs_InitialMount encountered failure %d \n", retval);
return retval;
}
retval = hfs_CollectBtreeStats(hfsmp, vhp, embeddedOffset, args);
free(vhp);
vhp = NULL;
if ( retval )
{
LFHFS_LOG(LEVEL_DEBUG, "hfs_mountfs: hfs_CollectBtreeStats encountered failure %d \n", retval);
goto error_exit;
}
hfsmp->hfs_last_mounted_mtime = hfsmp->hfs_mtime;
if ( retval )
{
LFHFS_LOG(LEVEL_DEBUG, "hfs_mountfs: encountered failure %d \n", retval);
goto error_exit;
}
LFHFS_LOG(LEVEL_DEFAULT, "hfs_mountfs: mounted %s on device %s\n", (hfsmp->vcbVN[0] ? (const char*) hfsmp->vcbVN : "unknown"), "unknown device");
hfs_flushvolumeheader(hfsmp, 0);
return (0);
error_exit:
if (vhp) free(vhp);
if (hfsmp)
{
hfsUnmount(hfsmp);
hfs_locks_destroy(hfsmp);
hfs_delete_chash(hfsmp);
hfs_idhash_destroy (hfsmp);
hfs_free(hfsmp);
hfsmp = NULL;
}
return (retval);
}
static void
hfs_locks_destroy(struct hfsmount *hfsmp)
{
lf_lck_mtx_destroy(&hfsmp->hfs_mutex);
lf_lck_mtx_destroy(&hfsmp->sync_mutex);
lf_lck_rw_destroy(&hfsmp->hfs_global_lock);
lf_lck_spin_destroy(&hfsmp->vcbFreeExtLock);
return;
}
int
hfs_flushvolumeheader(struct hfsmount *hfsmp, hfs_flush_volume_header_options_t options)
{
int retval = 0;
ExtendedVCB *vcb = HFSTOVCB(hfsmp);
bool critical = false;
daddr64_t avh_sector;
bool altflush = ISSET(options, HFS_FVH_WRITE_ALT);
void *pvVolHdrData = NULL;
GenericLFBuf *psVolHdrBuf = NULL;
void *pvVolHdr2Data = NULL;
GenericLFBuf *psVolHdr2Buf = NULL;
void *pvAltHdrData = NULL;
GenericLFBuf *psAltHdrBuf = NULL;
if (ISSET(options, HFS_FVH_FLUSH_IF_DIRTY) && !hfs_header_needs_flushing(hfsmp)) {
return 0;
}
if (hfsmp->hfs_flags & HFS_READ_ONLY) {
return 0;
}
if (options & HFS_FVH_MARK_UNMOUNT) {
HFSTOVCB(hfsmp)->vcbAtrb |= kHFSVolumeUnmountedMask;
} else {
HFSTOVCB(hfsmp)->vcbAtrb &= ~kHFSVolumeUnmountedMask;
}
daddr64_t priIDSector = (daddr64_t)((vcb->hfsPlusIOPosOffset / hfsmp->hfs_logical_block_size) + HFS_PRI_SECTOR(hfsmp->hfs_logical_block_size));
if (!(options & HFS_FVH_SKIP_TRANSACTION)) {
if (hfs_start_transaction(hfsmp) != 0) {
return EINVAL;
}
}
psVolHdrBuf = lf_hfs_generic_buf_allocate(hfsmp->hfs_devvp,
HFS_PHYSBLK_ROUNDDOWN(priIDSector, hfsmp->hfs_log_per_phys),
hfsmp->hfs_physical_block_size, GEN_BUF_PHY_BLOCK);
if (psVolHdrBuf == NULL) {
retval = ENOMEM;
goto err_exit;
}
pvVolHdrData = psVolHdrBuf->pvData;
retval = lf_hfs_generic_buf_read(psVolHdrBuf);
if (retval) {
LFHFS_LOG(LEVEL_ERROR, "hfs_flushvolumeheader: err %d reading VH blk (vol=%s)\n", retval, vcb->vcbVN);
goto err_exit;
}
HFSPlusVolumeHeader* volumeHeader = (HFSPlusVolumeHeader *)(pvVolHdrData + HFS_PRI_OFFSET(hfsmp->hfs_physical_block_size));
u_int16_t signature = SWAP_BE16 (volumeHeader->signature);
u_int16_t hfsversion = SWAP_BE16 (volumeHeader->version);
if ((signature != kHFSPlusSigWord && signature != kHFSXSigWord) ||
(hfsversion < kHFSPlusVersion) || (hfsversion > 100) ||
(SWAP_BE32 (volumeHeader->blockSize) != vcb->blockSize))
{
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));
hfs_mark_inconsistent(hfsmp, HFS_INCONSISTENCY_DETECTED);
avh_sector = hfsmp->hfs_partition_avh_sector;
if (hfsmp->hfs_partition_avh_sector != hfsmp->hfs_fs_avh_sector)
{
uint64_t sector_count = 0;
retval = ioctl(hfsmp->hfs_devvp->psFSRecord->iFD, DKIOCGETBLOCKCOUNT, §or_count);
if (retval)
{
LFHFS_LOG(LEVEL_ERROR, "hfs_flushvolumeheader: err %d getting block count (%s) \n", retval, vcb->vcbVN);
retval = ENXIO;
goto err_exit;
}
if (sector_count != (uint64_t)hfsmp->hfs_logical_block_count)
{
hfsmp->hfs_partition_avh_sector = (hfsmp->hfsPlusIOPosOffset / hfsmp->hfs_logical_block_size) + HFS_ALT_SECTOR(hfsmp->hfs_logical_block_size, sector_count);
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);
avh_sector = hfsmp->hfs_fs_avh_sector;
}
}
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);
if (avh_sector)
{
psAltHdrBuf = lf_hfs_generic_buf_allocate(hfsmp->hfs_devvp,
HFS_PHYSBLK_ROUNDDOWN(avh_sector, hfsmp->hfs_log_per_phys),
hfsmp->hfs_physical_block_size, GEN_BUF_PHY_BLOCK);
if (psAltHdrBuf == NULL) {
retval = ENOMEM;
goto err_exit;
}
pvAltHdrData = psAltHdrBuf->pvData;
retval = lf_hfs_generic_buf_read(psAltHdrBuf);
if (retval)
{
LFHFS_LOG(LEVEL_ERROR, "hfs_flushvolumeheader: err %d reading alternate VH blk (vol=%s)\n", retval, vcb->vcbVN);
goto err_exit;
}
HFSPlusVolumeHeader * altVH = (HFSPlusVolumeHeader *)(pvAltHdrData + HFS_ALT_OFFSET(hfsmp->hfs_physical_block_size));
signature = SWAP_BE16(altVH->signature);
hfsversion = SWAP_BE16(altVH->version);
if ((signature != kHFSPlusSigWord && signature != kHFSXSigWord) ||
(hfsversion < kHFSPlusVersion) || (kHFSPlusVersion > 100) ||
(SWAP_BE32(altVH->blockSize) != vcb->blockSize))
{
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));
retval = EIO;
goto err_exit;
}
bcopy(altVH, volumeHeader, kMDBSize);
lf_hfs_generic_buf_release(psAltHdrBuf);
pvAltHdrData = NULL;
}
else
{
retval = EIO;
goto err_exit;
}
}
if (hfsmp->jnl)
{
journal_modify_block_start(hfsmp->jnl, psVolHdrBuf);
}
if ((vcb->hfsPlusIOPosOffset != 0) && (SWAP_BE32 (volumeHeader->createDate) != vcb->localCreateDate))
{
HFSMasterDirectoryBlock *mdb;
psVolHdr2Buf = lf_hfs_generic_buf_allocate(hfsmp->hfs_devvp,
HFS_PHYSBLK_ROUNDDOWN(HFS_PRI_SECTOR(hfsmp->hfs_logical_block_size), hfsmp->hfs_log_per_phys),
hfsmp->hfs_physical_block_size, GEN_BUF_PHY_BLOCK);
if (psVolHdr2Buf == NULL) {
retval = ENOMEM;
goto err_exit;
}
void *pvVolHdr2Data = psVolHdr2Buf->pvData;
retval = lf_hfs_generic_buf_read(psVolHdr2Buf);
if (retval)
{
lf_hfs_generic_buf_release(psVolHdr2Buf);
LFHFS_LOG(LEVEL_ERROR, "hfs_flushvolumeheader: err %d reading alternate VH blk (vol=%s)\n", retval, vcb->vcbVN);
goto err_exit;
}
mdb = (HFSMasterDirectoryBlock *)(pvVolHdr2Data + HFS_PRI_OFFSET(hfsmp->hfs_physical_block_size));
if ( SWAP_BE32 (mdb->drCrDate) != vcb->localCreateDate )
{
if (hfsmp->jnl)
{
journal_modify_block_start(hfsmp->jnl, psVolHdr2Buf);
}
mdb->drCrDate = SWAP_BE32 (vcb->localCreateDate);
if (hfsmp->jnl)
{
journal_modify_block_end(hfsmp->jnl, psVolHdr2Buf, NULL, NULL);
}
else
{
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);
lf_hfs_generic_buf_release(psVolHdr2Buf);
pvVolHdr2Data = NULL;
if (retval)
{
LFHFS_LOG(LEVEL_ERROR, "hfs_flushvolumeheader: err %d writing VH blk (vol=%s)\n", retval, vcb->vcbVN);
goto err_exit;
}
}
}
else
{
lf_hfs_generic_buf_release(psVolHdr2Buf);
pvVolHdr2Data = NULL;
}
}
hfs_lock_mount (hfsmp);
volumeHeader->attributes = SWAP_BE32 (vcb->vcbAtrb);
volumeHeader->journalInfoBlock = SWAP_BE32 (vcb->vcbJinfoBlock);
if (hfsmp->jnl)
{
volumeHeader->lastMountedVersion = SWAP_BE32 (kHFSJMountVersion);
}
else
{
volumeHeader->lastMountedVersion = SWAP_BE32 (kHFSPlusMountVersion);
}
volumeHeader->createDate = SWAP_BE32 (vcb->localCreateDate);
volumeHeader->modifyDate = SWAP_BE32 (to_hfs_time(vcb->vcbLsMod));
volumeHeader->backupDate = SWAP_BE32 (to_hfs_time(vcb->vcbVolBkUp));
volumeHeader->fileCount = SWAP_BE32 (vcb->vcbFilCnt);
volumeHeader->folderCount = SWAP_BE32 (vcb->vcbDirCnt);
volumeHeader->totalBlocks = SWAP_BE32 (vcb->totalBlocks);
volumeHeader->freeBlocks = SWAP_BE32 (vcb->freeBlocks + vcb->reclaimBlocks);
volumeHeader->nextAllocation = SWAP_BE32 (vcb->nextAllocation);
volumeHeader->rsrcClumpSize = SWAP_BE32 (vcb->vcbClpSiz);
volumeHeader->dataClumpSize = SWAP_BE32 (vcb->vcbClpSiz);
volumeHeader->nextCatalogID = SWAP_BE32 (vcb->vcbNxtCNID);
volumeHeader->writeCount = SWAP_BE32 (vcb->vcbWrCnt);
volumeHeader->encodingsBitmap = SWAP_BE64 (vcb->encodingsBitmap);
if (bcmp(vcb->vcbFndrInfo, volumeHeader->finderInfo, sizeof(volumeHeader->finderInfo)) != 0)
{
bcopy(vcb->vcbFndrInfo, volumeHeader->finderInfo, sizeof(volumeHeader->finderInfo));
critical = true;
}
if (!altflush && !ISSET(options, HFS_FVH_FLUSH_IF_DIRTY))
{
goto done;
}
struct filefork * fp = VTOF(vcb->extentsRefNum);
if (FTOC(fp)->c_flag & C_MODIFIED)
{
for (int iExtentCounter = 0; iExtentCounter < kHFSPlusExtentDensity; iExtentCounter++)
{
volumeHeader->extentsFile.extents[iExtentCounter].startBlock = SWAP_BE32 (fp->ff_extents[iExtentCounter].startBlock);
volumeHeader->extentsFile.extents[iExtentCounter].blockCount = SWAP_BE32 (fp->ff_extents[iExtentCounter].blockCount);
}
volumeHeader->extentsFile.logicalSize = SWAP_BE64 (fp->ff_size);
volumeHeader->extentsFile.totalBlocks = SWAP_BE32 (fp->ff_blocks);
volumeHeader->extentsFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize);
FTOC(fp)->c_flag &= ~C_MODIFIED;
altflush = true;
}
fp = VTOF(vcb->catalogRefNum);
if (FTOC(fp)->c_flag & C_MODIFIED)
{
for (int iExtentCounter = 0; iExtentCounter < kHFSPlusExtentDensity; iExtentCounter++)
{
volumeHeader->catalogFile.extents[iExtentCounter].startBlock = SWAP_BE32 (fp->ff_extents[iExtentCounter].startBlock);
volumeHeader->catalogFile.extents[iExtentCounter].blockCount = SWAP_BE32 (fp->ff_extents[iExtentCounter].blockCount);
}
volumeHeader->catalogFile.logicalSize = SWAP_BE64 (fp->ff_size);
volumeHeader->catalogFile.totalBlocks = SWAP_BE32 (fp->ff_blocks);
volumeHeader->catalogFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize);
FTOC(fp)->c_flag &= ~C_MODIFIED;
altflush = true;
}
fp = VTOF(vcb->allocationsRefNum);
if (FTOC(fp)->c_flag & C_MODIFIED)
{
for (int iExtentCounter = 0; iExtentCounter < kHFSPlusExtentDensity; iExtentCounter++)
{
volumeHeader->allocationFile.extents[iExtentCounter].startBlock = SWAP_BE32 (fp->ff_extents[iExtentCounter].startBlock);
volumeHeader->allocationFile.extents[iExtentCounter].blockCount = SWAP_BE32 (fp->ff_extents[iExtentCounter].blockCount);
}
volumeHeader->allocationFile.logicalSize = SWAP_BE64 (fp->ff_size);
volumeHeader->allocationFile.totalBlocks = SWAP_BE32 (fp->ff_blocks);
volumeHeader->allocationFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize);
FTOC(fp)->c_flag &= ~C_MODIFIED;
altflush = true;
}
if (hfsmp->hfs_attribute_vp)
{
fp = VTOF(hfsmp->hfs_attribute_vp);
for (int iExtentCounter = 0; iExtentCounter < kHFSPlusExtentDensity; iExtentCounter++)
{
volumeHeader->attributesFile.extents[iExtentCounter].startBlock = SWAP_BE32 (fp->ff_extents[iExtentCounter].startBlock);
volumeHeader->attributesFile.extents[iExtentCounter].blockCount = SWAP_BE32 (fp->ff_extents[iExtentCounter].blockCount);
}
if (ISSET(FTOC(fp)->c_flag, C_MODIFIED))
{
FTOC(fp)->c_flag &= ~C_MODIFIED;
altflush = true;
}
volumeHeader->attributesFile.logicalSize = SWAP_BE64 (fp->ff_size);
volumeHeader->attributesFile.totalBlocks = SWAP_BE32 (fp->ff_blocks);
volumeHeader->attributesFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize);
}
if (hfsmp->hfs_startup_vp)
{
fp = VTOF(hfsmp->hfs_startup_vp);
if (FTOC(fp)->c_flag & C_MODIFIED)
{
for (int iExtentCounter = 0; iExtentCounter < kHFSPlusExtentDensity; iExtentCounter++)
{
volumeHeader->startupFile.extents[iExtentCounter].startBlock = SWAP_BE32 (fp->ff_extents[iExtentCounter].startBlock);
volumeHeader->startupFile.extents[iExtentCounter].blockCount = SWAP_BE32 (fp->ff_extents[iExtentCounter].blockCount);
}
volumeHeader->startupFile.logicalSize = SWAP_BE64 (fp->ff_size);
volumeHeader->startupFile.totalBlocks = SWAP_BE32 (fp->ff_blocks);
volumeHeader->startupFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize);
FTOC(fp)->c_flag &= ~C_MODIFIED;
altflush = true;
}
}
if (altflush)
critical = true;
done:
MarkVCBClean(hfsmp);
hfs_unlock_mount (hfsmp);
if (altflush) {
if (hfsmp->hfs_partition_avh_sector != hfsmp->hfs_fs_avh_sector)
{
uint64_t sector_count;
retval = ioctl(hfsmp->hfs_devvp->psFSRecord->iFD, DKIOCGETBLOCKCOUNT, §or_count);
if (retval)
{
LFHFS_LOG(LEVEL_ERROR, "hfs_flushvolumeheader: err %d getting block count (%s) \n", retval, vcb->vcbVN);
retval = ENXIO;
goto err_exit;
}
if (sector_count != (uint64_t)hfsmp->hfs_logical_block_count)
{
hfsmp->hfs_partition_avh_sector = (hfsmp->hfsPlusIOPosOffset / hfsmp->hfs_logical_block_size) + HFS_ALT_SECTOR(hfsmp->hfs_logical_block_size, sector_count);
LFHFS_LOG(LEVEL_DEFAULT, "hfs_flushvolumeheader: altflush: partition size changed, partition_avh_sector=%qu, fs_avh_sector=%qu\n",
hfsmp->hfs_partition_avh_sector, hfsmp->hfs_fs_avh_sector);
}
}
if ((hfsmp->hfs_fs_avh_sector) && (hfsmp->hfs_partition_avh_sector != hfsmp->hfs_fs_avh_sector))
{
if (pvAltHdrData != NULL)
{
panic("We shouldn't be here!");
hfs_assert(0);
}
psAltHdrBuf = lf_hfs_generic_buf_allocate(hfsmp->hfs_devvp,
HFS_PHYSBLK_ROUNDDOWN(hfsmp->hfs_fs_avh_sector, hfsmp->hfs_log_per_phys),
hfsmp->hfs_physical_block_size, GEN_BUF_PHY_BLOCK);
if (psAltHdrBuf == NULL) {
retval = ENOMEM;
goto err_exit;
}
pvAltHdrData = psAltHdrBuf->pvData;
retval = lf_hfs_generic_buf_read(psAltHdrBuf);
if (retval)
{
LFHFS_LOG(LEVEL_ERROR, "hfs_flushvolumeheader: err %d reading alternate VH blk (vol=%s)\n", retval, vcb->vcbVN);
goto err_exit;
}
if (hfsmp->jnl)
{
journal_modify_block_start(hfsmp->jnl, psAltHdrBuf);
}
bcopy(volumeHeader, pvAltHdrData + HFS_ALT_OFFSET(hfsmp->hfs_physical_block_size), kMDBSize);
if (hfsmp->jnl)
{
journal_modify_block_end(hfsmp->jnl, psAltHdrBuf, NULL, NULL);
}
else
{
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);
if (retval)
{
LFHFS_LOG(LEVEL_ERROR, "hfs_flushvolumeheader: err %d writing VH blk (vol=%s)\n", retval, vcb->vcbVN);
goto err_exit;
}
lf_hfs_generic_buf_release(psAltHdrBuf);
pvAltHdrData = NULL;
}
}
if (hfsmp->hfs_partition_avh_sector)
{
if (pvAltHdrData != NULL)
{
panic("We shouldn't be here!");
hfs_assert(0);
}
psAltHdrBuf = lf_hfs_generic_buf_allocate(hfsmp->hfs_devvp,
HFS_PHYSBLK_ROUNDDOWN(hfsmp->hfs_fs_avh_sector, hfsmp->hfs_log_per_phys),
hfsmp->hfs_physical_block_size, GEN_BUF_PHY_BLOCK);
if (psAltHdrBuf == NULL) {
retval = ENOMEM;
goto err_exit;
}
pvAltHdrData = psAltHdrBuf->pvData;
retval = lf_hfs_generic_buf_read(psAltHdrBuf);
if (retval)
{
LFHFS_LOG(LEVEL_ERROR, "hfs_flushvolumeheader: err %d reading alternate VH blk (vol=%s)\n", retval, vcb->vcbVN);
goto err_exit;
}
if ((hfsmp->jnl) && (hfsmp->hfs_partition_avh_sector == hfsmp->hfs_fs_avh_sector)) {
journal_modify_block_start(hfsmp->jnl, psAltHdrBuf);
}
bcopy(volumeHeader, pvAltHdrData + HFS_ALT_OFFSET(hfsmp->hfs_physical_block_size), kMDBSize);
if ((hfsmp->jnl) && (hfsmp->hfs_partition_avh_sector == hfsmp->hfs_fs_avh_sector)) {
journal_modify_block_end (hfsmp->jnl, psAltHdrBuf, NULL, NULL);
}
else
{
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);
if (retval)
{
LFHFS_LOG(LEVEL_ERROR, "hfs_flushvolumeheader: err %d writing VH blk (vol=%s)\n", retval, vcb->vcbVN);
goto err_exit;
}
lf_hfs_generic_buf_release(psAltHdrBuf);
pvAltHdrData = NULL;
hfs_flush(hfsmp, HFS_FLUSH_CACHE);
}
}
}
if (hfsmp->jnl) {
journal_modify_block_end(hfsmp->jnl, psVolHdrBuf, NULL, NULL);
}
else
{
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);
if (critical && (retval == 0))
{
hfs_flush(hfsmp, HFS_FLUSH_CACHE);
}
lf_hfs_generic_buf_release(psVolHdrBuf);
pvVolHdrData = NULL;
if (retval)
{
LFHFS_LOG(LEVEL_ERROR, "hfs_flushvolumeheader: err %d reading VH blk (vol=%s)\n", retval, vcb->vcbVN);
goto err_exit;
}
}
if (!(options & HFS_FVH_SKIP_TRANSACTION)) {
hfs_end_transaction(hfsmp);
}
return (retval);
err_exit:
if (pvVolHdrData)
lf_hfs_generic_buf_release(psVolHdrBuf);
if (pvVolHdr2Data)
lf_hfs_generic_buf_release(psVolHdr2Buf);
if (pvAltHdrData)
lf_hfs_generic_buf_release(psAltHdrBuf);
if (!(options & HFS_FVH_SKIP_TRANSACTION)) {
hfs_end_transaction(hfsmp);
}
return retval;
}
void hfs_mark_inconsistent(struct hfsmount *hfsmp, hfs_inconsistency_reason_t reason)
{
hfs_lock_mount (hfsmp);
if ((hfsmp->vcbAtrb & kHFSVolumeInconsistentMask) == 0)
{
hfsmp->vcbAtrb |= kHFSVolumeInconsistentMask;
MarkVCBDirty(hfsmp);
}
if ((hfsmp->hfs_flags & HFS_READ_ONLY)==0)
{
switch (reason)
{
case HFS_INCONSISTENCY_DETECTED:
LFHFS_LOG(LEVEL_ERROR, "hfs_mark_inconsistent: Runtime corruption detected on %s, fsck will be forced on next mount.\n",hfsmp->vcbVN);
break;
case HFS_ROLLBACK_FAILED:
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);
break;
case HFS_OP_INCOMPLETE:
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);
break;
case HFS_FSCK_FORCED:
LFHFS_LOG(LEVEL_ERROR, "hfs_mark_inconsistent: fsck requested for `%s'; fsck will be forced on next mount.\n",hfsmp->vcbVN);
break;
}
}
hfs_unlock_mount (hfsmp);
}
void
hfs_getvoluuid(struct hfsmount *hfsmp, uuid_t result_uuid)
{
if (uuid_is_null(hfsmp->hfs_full_uuid)) {
uuid_t result;
CC_MD5_CTX md5c;
uint8_t rawUUID[8];
((uint32_t *)rawUUID)[0] = hfsmp->vcbFndrInfo[6];
((uint32_t *)rawUUID)[1] = hfsmp->vcbFndrInfo[7];
CC_MD5_Init( &md5c );
CC_MD5_Update( &md5c, HFS_UUID_NAMESPACE_ID, sizeof( uuid_t ) );
CC_MD5_Update( &md5c, rawUUID, sizeof (rawUUID) );
CC_MD5_Final( result, &md5c );
result[6] = 0x30 | ( result[6] & 0x0F );
result[8] = 0x80 | ( result[8] & 0x3F );
uuid_copy(hfsmp->hfs_full_uuid, result);
}
uuid_copy (result_uuid, hfsmp->hfs_full_uuid);
}
void hfs_scan_blocks (struct hfsmount *hfsmp)
{
int flags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
(void) hfs_lock_mount (hfsmp);
hfsmp->scan_var |= HFS_ALLOCATOR_SCAN_INFLIGHT;
hfs_unlock_mount (hfsmp);
if (hfs_init_summary (hfsmp))
{
LFHFS_LOG(LEVEL_DEBUG, "hfs_scan_blocks: could not initialize summary table for %s\n", hfsmp->vcbVN);
}
(void) ScanUnmapBlocks(hfsmp);
(void) hfs_lock_mount (hfsmp);
hfsmp->scan_var &= ~HFS_ALLOCATOR_SCAN_INFLIGHT;
hfsmp->scan_var |= HFS_ALLOCATOR_SCAN_COMPLETED;
hfs_unlock_mount (hfsmp);
hfs_systemfile_unlock(hfsmp, flags);
}
int
hfs_vget(struct hfsmount *hfsmp, cnid_t cnid, struct vnode **vpp, int skiplock, int allow_deleted)
{
struct vnode *vp = NULL;
struct cat_desc cndesc;
struct cat_attr cnattr;
struct cat_fork cnfork;
u_int32_t linkref = 0;
int error;
if ((cnid < kHFSFirstUserCatalogNodeID) &&
(cnid != kHFSRootFolderID && cnid != kHFSRootParentID)) {
return (ENOENT);
}
if (cnid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
cnid == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
return (ENOENT);
}
vp = hfs_chash_getvnode(hfsmp, cnid, 0, skiplock, allow_deleted);
if (vp) {
*vpp = vp;
return(0);
}
bzero(&cndesc, sizeof(cndesc));
bzero(&cnattr, sizeof(cnattr));
bzero(&cnfork, sizeof(cnfork));
if (cnid == kHFSRootParentID) {
static char hfs_rootname[] = "/";
cndesc.cd_nameptr = (const u_int8_t *)&hfs_rootname[0];
cndesc.cd_namelen = 1;
cndesc.cd_parentcnid = kHFSRootParentID;
cndesc.cd_cnid = kHFSRootFolderID;
cndesc.cd_flags = CD_ISDIR;
cnattr.ca_fileid = kHFSRootFolderID;
cnattr.ca_linkcount = 1;
cnattr.ca_entries = 1;
cnattr.ca_dircount = 1;
cnattr.ca_mode = (S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO);
} else {
int lockflags;
cnid_t pid;
const char *nameptr;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
error = cat_idlookup(hfsmp, cnid, 0, 0, &cndesc, &cnattr, &cnfork);
hfs_systemfile_unlock(hfsmp, lockflags);
if (error) {
*vpp = NULL;
return (error);
}
pid = cndesc.cd_parentcnid;
nameptr = (const char *)cndesc.cd_nameptr;
if ((pid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
cndesc.cd_namelen > HFS_INODE_PREFIX_LEN &&
(bcmp(nameptr, HFS_INODE_PREFIX, HFS_INODE_PREFIX_LEN) == 0)) {
linkref = (uint32_t) strtoul(&nameptr[HFS_INODE_PREFIX_LEN], NULL, 10);
} else if ((pid == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) &&
cndesc.cd_namelen > HFS_DIRINODE_PREFIX_LEN &&
(bcmp(nameptr, HFS_DIRINODE_PREFIX, HFS_DIRINODE_PREFIX_LEN) == 0)) {
linkref = (uint32_t) strtoul(&nameptr[HFS_DIRINODE_PREFIX_LEN], NULL, 10);
} else if ((pid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
cndesc.cd_namelen > HFS_DELETE_PREFIX_LEN &&
(bcmp(nameptr, HFS_DELETE_PREFIX, HFS_DELETE_PREFIX_LEN) == 0)) {
*vpp = NULL;
cat_releasedesc(&cndesc);
return (ENOENT);
}
}
if (linkref) {
cnid_t lastid;
struct cat_desc linkdesc;
int linkerr = 0;
cnattr.ca_linkref = linkref;
bzero (&linkdesc, sizeof (linkdesc));
linkerr = hfs_lookup_lastlink (hfsmp, linkref, &lastid, &linkdesc);
if ((linkerr == 0) && (lastid != 0)) {
cat_releasedesc (&cndesc);
bcopy (&linkdesc, &cndesc, sizeof(linkdesc));
}
}
if (linkref) {
int newvnode_flags = 0;
error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cnfork, &vp, &newvnode_flags);
if (error == 0) {
VTOC(vp)->c_flag |= C_HARDLINK;
}
}
else
{
int newvnode_flags = 0;
void *buf = hfs_malloc(MAXPATHLEN);
struct componentname cn = {
.cn_nameiop = LOOKUP,
.cn_flags = ISLASTCN,
.cn_pnlen = MAXPATHLEN,
.cn_namelen = cndesc.cd_namelen,
.cn_pnbuf = buf,
.cn_nameptr = buf
};
bcopy(cndesc.cd_nameptr, cn.cn_nameptr, cndesc.cd_namelen + 1);
error = hfs_getnewvnode(hfsmp, NULL, &cn, &cndesc, 0, &cnattr, &cnfork, &vp, &newvnode_flags);
if (error == 0 && (VTOC(vp)->c_flag & C_HARDLINK)) {
hfs_savelinkorigin(VTOC(vp), cndesc.cd_parentcnid);
}
hfs_free(buf);
}
cat_releasedesc(&cndesc);
*vpp = vp;
if (vp && skiplock) {
hfs_unlock(VTOC(vp));
}
return (error);
}
int
hfs_GetInfoByID(struct hfsmount *hfsmp, cnid_t cnid, UVFSFileAttributes *file_attrs, char pcName[MAX_UTF8_NAME_LENGTH])
{
struct vnode *psVnode = NULL;
int error = hfs_vget(hfsmp, cnid, &psVnode, 0, 0);
if (error || psVnode == NULL) {
if (psVnode != NULL) hfs_unlock(VTOC(psVnode));
hfs_vnop_reclaim(psVnode);
return EFAULT;
} else {
vnode_GetAttrInternal (psVnode, file_attrs);
hfs_unlock(VTOC(psVnode));
}
if (cnid == kHFSRootFolderID)
pcName[0] = 0;
else {
if (psVnode->sFSParams.vnfs_cnp && psVnode->sFSParams.vnfs_cnp->cn_nameptr)
strlcpy(pcName, (char*) psVnode->sFSParams.vnfs_cnp->cn_nameptr, MAX_UTF8_NAME_LENGTH);
else
return EINVAL;
}
error = hfs_vnop_reclaim(psVnode);
return (error);
}
int hfs_vfs_root(struct mount *mp, struct vnode **vpp)
{
return hfs_vget(VFSTOHFS(mp), (cnid_t)kHFSRootFolderID, vpp, 1, 0);
}
int hfs_unmount(struct mount *mp)
{
struct hfsmount *hfsmp = VFSTOHFS(mp);
int retval = E_NONE;
if (hfsmp->hfs_flags & HFS_SUMMARY_TABLE)
{
if (hfsmp->hfs_summary_table)
{
int err = 0;
if (hfsmp->hfs_allocation_vp)
{
err = hfs_lock (VTOC(hfsmp->hfs_allocation_vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
}
hfs_free(hfsmp->hfs_summary_table);
hfsmp->hfs_summary_table = NULL;
hfsmp->hfs_flags &= ~HFS_SUMMARY_TABLE;
if (err == 0 && hfsmp->hfs_allocation_vp)
{
hfs_unlock (VTOC(hfsmp->hfs_allocation_vp));
}
}
}
if (hfsmp->jnl) {
journal_release(hfsmp->jnl);
hfsmp->jnl = NULL;
}
hfsUnmount(hfsmp);
int iFD = hfsmp->hfs_devvp->psFSRecord->iFD;
lf_hfs_generic_buf_cache_clear_by_iFD(iFD);
vnode_rele(hfsmp->hfs_devvp);
hfs_locks_destroy(hfsmp);
hfs_delete_chash(hfsmp);
hfs_idhash_destroy(hfsmp);
hfs_assert(TAILQ_EMPTY(&hfsmp->hfs_reserved_ranges[HFS_TENTATIVE_BLOCKS]) && TAILQ_EMPTY(&hfsmp->hfs_reserved_ranges[HFS_LOCKED_BLOCKS]));
hfs_assert(!hfsmp->lockedBlocks);
hfs_free(hfsmp);
return (retval);
}
void
hfs_setencodingbits(struct hfsmount *hfsmp, u_int32_t encoding)
{
#define kIndexMacUkrainian 48
#define kIndexMacFarsi 49
u_int32_t index;
switch (encoding)
{
case kTextEncodingMacUkrainian:
index = kIndexMacUkrainian;
break;
case kTextEncodingMacFarsi:
index = kIndexMacFarsi;
break;
default:
index = encoding;
break;
}
if (index < 64 && (hfsmp->encodingsBitmap & (u_int64_t)(1ULL << index)) == 0) {
hfs_lock_mount (hfsmp);
hfsmp->encodingsBitmap |= (u_int64_t)(1ULL << index);
MarkVCBDirty(hfsmp);
hfs_unlock_mount(hfsmp);
}
}
int
hfs_volupdate(struct hfsmount *hfsmp, enum volop op, int inroot)
{
struct timeval tv;
microtime(&tv);
hfs_lock_mount (hfsmp);
MarkVCBDirty(hfsmp);
hfsmp->hfs_mtime = tv.tv_sec;
switch (op) {
case VOL_UPDATE:
break;
case VOL_MKDIR:
if (hfsmp->hfs_dircount != 0xFFFFFFFF)
++hfsmp->hfs_dircount;
if (inroot && hfsmp->vcbNmRtDirs != 0xFFFF)
++hfsmp->vcbNmRtDirs;
break;
case VOL_RMDIR:
if (hfsmp->hfs_dircount != 0)
--hfsmp->hfs_dircount;
if (inroot && hfsmp->vcbNmRtDirs != 0xFFFF)
--hfsmp->vcbNmRtDirs;
break;
case VOL_MKFILE:
if (hfsmp->hfs_filecount != 0xFFFFFFFF)
++hfsmp->hfs_filecount;
if (inroot && hfsmp->vcbNmFls != 0xFFFF)
++hfsmp->vcbNmFls;
break;
case VOL_RMFILE:
if (hfsmp->hfs_filecount != 0)
--hfsmp->hfs_filecount;
if (inroot && hfsmp->vcbNmFls != 0xFFFF)
--hfsmp->vcbNmFls;
break;
}
hfs_unlock_mount (hfsmp);
hfs_flushvolumeheader(hfsmp, 0);
return (0);
}