#include <sys/types.h>
#include "edt_fstab.h"
#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
#include <APFS/APFS.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <os/bsd.h>
#include <sys/stat.h>
#include <paths.h>
char boot_container[EDTVolumePropertySize] = {};
char data_volume[EDTVolumePropertySize] = {};
static uint32_t edt_os_environment = EDT_OS_ENV_MAIN;
const char *
get_boot_container(uint32_t *os_envp)
{
kern_return_t err;
CFMutableDictionaryRef fs_info = NULL;
CFStringRef container = NULL;
CFDataRef os_env = NULL;
if (strnlen(boot_container, sizeof(boot_container)) > 0) {
*os_envp = edt_os_environment;
return boot_container;
}
fs_info = IORegistryEntryFromPath(kIOMasterPortDefault, kEDTFilesystemEntry);
if (fs_info == IO_OBJECT_NULL) {
fprintf(stderr, "failed to get filesystem info\n");
return NULL;
}
os_env = IORegistryEntryCreateCFProperty(fs_info, kEDTOSEnvironment, kCFAllocatorDefault, 0);
if (os_env) {
CFDataGetBytes(os_env, CFRangeMake(0, CFDataGetLength(os_env)), (UInt8*)(&edt_os_environment));
CFRelease(os_env);
}
IOObjectRelease(fs_info);
*os_envp = edt_os_environment;
err = APFSContainerGetBootDevice(&container);
if (!err) {
strcpy(boot_container, _PATH_DEV);
CFStringGetCString(container,
boot_container + strlen(_PATH_DEV),
EDTVolumePropertySize - strlen(_PATH_DEV),
kCFStringEncodingUTF8);
CFRelease(container);
return boot_container;
} else {
fprintf(stderr, "%sfailed to get boot device - %s\n",
(edt_os_environment == EDT_OS_ENV_MAIN) ? "" : "warning: ",
strerror(err_get_code(err)));
return NULL;
}
}
const char *
get_data_volume(void)
{
const char *container = NULL;
CFMutableArrayRef matches = NULL;
OSStatus status;
if (strnlen(data_volume, sizeof(data_volume)) > 0) {
return data_volume;
}
if (strlen(boot_container) > 0) {
container = boot_container;
} else {
uint32_t os_env;
container = get_boot_container(&os_env);
}
if (!container) {
return NULL;
}
status = APFSVolumeRoleFind(container, APFS_VOL_ROLE_DATA, &matches);
if (status) {
fprintf(stderr, "%sfailed to lookup data volume - %s\n",
(edt_os_environment == EDT_OS_ENV_MAIN) ? "" : "warning: ",
strerror(err_get_code(status)));
return NULL;
} else if (CFArrayGetCount(matches) > 1) {
fprintf(stderr, "found multiple data volumes\n");
CFRelease(matches);
return NULL;
} else {
CFStringGetCString(CFArrayGetValueAtIndex(matches, 0),
data_volume,
EDTVolumePropertySize,
kCFStringEncodingUTF8);
CFRelease(matches);
return data_volume;
}
}
int
get_boot_manifest_hash(char *boot_manifest_hash, size_t boot_manifest_hash_len)
{
kern_return_t err = 0;
io_registry_entry_t chosen;
CFDataRef bm_hash = NULL;
size_t bm_hash_size;
uint8_t bm_hash_buf[EDTVolumePropertyMaxSize] = {};
const char *hexmap = "0123456789ABCDEF";
chosen = IORegistryEntryFromPath(kIOMasterPortDefault, kIODeviceTreePlane ":/chosen");
if (chosen == IO_OBJECT_NULL) {
fprintf(stderr, "failed to get chosen info\n");
return ENOENT;
}
bm_hash = IORegistryEntryCreateCFProperty(chosen, CFSTR("boot-manifest-hash"), kCFAllocatorDefault, 0);
if (!bm_hash) {
fprintf(stderr, "failed to get boot-manifest-hash\n");
IOObjectRelease(chosen);
return ENOENT;
} else {
bm_hash_size = CFDataGetLength(bm_hash);
CFDataGetBytes(bm_hash, CFRangeMake(0, bm_hash_size), bm_hash_buf);
if (boot_manifest_hash_len < (bm_hash_size * 2 + 1)) {
err = EINVAL;
} else {
for (size_t i = 0; i < (2 * bm_hash_size); i++)
*boot_manifest_hash++ = hexmap[(bm_hash_buf[i / 2] >> ((i % 2) ? 0 : 4)) & 0xf];
*boot_manifest_hash = '\0';
}
CFRelease(bm_hash);
IOObjectRelease(chosen);
}
return err;
}
#endif