BLIsMountAPFSSSV.c [plain text]
#include <sys/mount.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <string.h>
#include <APFS/APFS.h>
#include "bless.h"
#include "bless_private.h"
static int GetIfGroupOfVolumeDevContainsCompatibleSystemRoleVolume(BLContextPtr context, const char *volumeDev, bool *result)
{
int ret = 0;
char targetVolBSD[MAXPATHLEN];
io_service_t targetVolIOMedia = IO_OBJECT_NULL;
CFStringRef targetVolGroupUUID = NULL;
io_service_t conIOMedia = IO_OBJECT_NULL;
io_iterator_t volIter = IO_OBJECT_NULL;
io_service_t volIOMedia = IO_OBJECT_NULL;
CFArrayRef volRoles = NULL;
CFStringRef volRole = NULL;
CFStringRef volStatus = NULL;
CFStringRef volGroup = NULL;
kern_return_t kret = KERN_SUCCESS;
*result = false;
if (strnlen (volumeDev, MAXPATHLEN-1) < 10 ) {
contextprintf(context, kBLLogLevelError, "Volume dev format error\n");
ret = 1;
goto exit;
}
sscanf (volumeDev, "/dev/%s", targetVolBSD);
targetVolIOMedia = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, targetVolBSD));
if (targetVolIOMedia == IO_OBJECT_NULL) {
contextprintf(context, kBLLogLevelError, "Could not get IOService for %s\n", volumeDev);
ret = 2;
goto exit;
}
if (IOObjectConformsTo(targetVolIOMedia, "AppleAPFSSnapshot")) {
io_registry_entry_t volMedia;
contextprintf(context, kBLLogLevelVerbose, "%s is a snapshot\n", targetVolBSD);
ret = BLAPFSSnapshotToVolume(context, targetVolIOMedia, &volMedia);
if (ret) {
contextprintf(context, kBLLogLevelError, "Could not resolve snapshot at %s to a volume\n", targetVolBSD);
IOObjectRelease(targetVolIOMedia);
ret = 2;
goto exit;
}
IOObjectRelease(targetVolIOMedia);
targetVolIOMedia = volMedia;
}
if (!IOObjectConformsTo(targetVolIOMedia, APFS_VOLUME_OBJECT)) {
contextprintf(context, kBLLogLevelError, "%s is not an APFS volume\n", volumeDev);
ret = 3;
goto exit;
}
targetVolGroupUUID = IORegistryEntryCreateCFProperty(targetVolIOMedia, CFSTR(kAPFSVolGroupUUIDKey), kCFAllocatorDefault, 0);
if (!targetVolGroupUUID) {
contextprintf(context, kBLLogLevelError, "Volume has no group UUID\n");
ret = 4;
goto exit;
}
kret = IORegistryEntryGetParentEntry(targetVolIOMedia, kIOServicePlane, &conIOMedia);
if (kret) {
contextprintf(context, kBLLogLevelError, "Could not get parent for volume\n");
ret = 5;
goto exit;
}
if (!IOObjectConformsTo(conIOMedia, APFS_CONTAINER_OBJECT)) {
ret = 6;
goto exit;
}
kret = IORegistryEntryCreateIterator (conIOMedia, kIOServicePlane, 0, &volIter);
if (kret) {
contextprintf(context, kBLLogLevelError, "Could not get iterator for sibling volumes\n");
ret = 7;
goto exit;
}
while (IO_OBJECT_NULL != (volIOMedia = IOIteratorNext(volIter)))
{
if (IOObjectConformsTo(volIOMedia, APFS_VOLUME_OBJECT))
{
volRoles = IORegistryEntryCreateCFProperty(volIOMedia, CFSTR(kAPFSRoleKey), kCFAllocatorDefault, 0);
if (volRoles != NULL) {
if (CFArrayGetCount(volRoles) == 1) {
volRole = CFArrayGetValueAtIndex(volRoles, 0);
if (CFStringCompare(volRole, CFSTR(kAPFSVolumeRoleSystem), 0) == kCFCompareEqualTo) {
volStatus = IORegistryEntryCreateCFProperty(volIOMedia, CFSTR(kAPFSStatusKey), kCFAllocatorDefault, 0);
if (volStatus != NULL) {
if (CFStringCompare(volStatus, CFSTR("Online"), 0) == kCFCompareEqualTo) {
volGroup = IORegistryEntryCreateCFProperty(volIOMedia, CFSTR(kAPFSVolGroupUUIDKey), kCFAllocatorDefault, 0);
if (volGroup != NULL && CFEqual(volGroup, targetVolGroupUUID)) {
*result = true;
CFRelease(volGroup);
volGroup = NULL;
break;
}
}
CFRelease(volStatus);
volStatus = NULL;
}
}
}
CFRelease(volRoles);
volRoles = NULL;
}
}
IOObjectRelease(volIOMedia);
volIOMedia = IO_OBJECT_NULL;
}
exit:;
if (volGroup) CFRelease(volGroup);
if (volStatus) CFRelease(volStatus);
if (volRoles) CFRelease(volRoles);
if (volIter) IOObjectRelease(volIter);
if (volIOMedia) IOObjectRelease(volIOMedia);
if (conIOMedia) IOObjectRelease(conIOMedia);
if (targetVolIOMedia) IOObjectRelease(targetVolIOMedia);
if (targetVolGroupUUID) CFRelease(targetVolGroupUUID);
contextprintf(context, kBLLogLevelVerbose, "Among the APFS volumes in %s's volume group %s System-roled volume which is compatible with the currently-running macOS\n", volumeDev, *result ? "exists a" : "there is no");
return ret;
}
int BLIsMountAPFSDataRolePreSSVToSSV(BLContextPtr context, const char * mountpt, bool *isPreSSVToSSV)
{
struct statfs sc;
char specialMountPointPath[MAXPATHLEN];
bool mustUnmountPreboot = false;
bool yn = false;
int ret = 0;
struct stat sb;
*isPreSSVToSSV = false;
ret = statfs(mountpt, &sc);
if (ret) {
contextprintf(context, kBLLogLevelError, "Could not statfs() %s\n", mountpt);
return 1;
}
if (strcmp(sc.f_fstypename, "apfs") != 0) { goto exit; }
ret = BLIsDataRoleForAPFSVolumeDev(context, sc.f_mntfromname, &yn);
if (ret) {
contextprintf(context, kBLLogLevelError, "Could not determine volume role\n");
return 2;
}
if (!yn) {
contextprintf(context, kBLLogLevelVerbose, "Non-Data-role volume, so not considered Data-Given-Pre-SSV-to-SSV case\n");
goto exit;
}
ret = GetIfGroupOfVolumeDevContainsCompatibleSystemRoleVolume (context, sc.f_mntfromname, &yn);
if (ret) {
contextprintf(context, kBLLogLevelVerbose, "Indeterminate if reachable system volume case (%d)\n", ret);
ret = 0;
}
if (yn) {
contextprintf(context, kBLLogLevelVerbose, "A System volume is reachable, so not considered Data-Given-Pre-SSV-to-SSV\n");
goto exit;
}
ret = BLEnsureSpecialAPFSVolumeUUIDPath(context, sc.f_mntfromname,
APFS_VOL_ROLE_PREBOOT, true,
specialMountPointPath, sizeof specialMountPointPath,
&mustUnmountPreboot);
if (ret) { ret = 0; goto exit; }
if (stat(specialMountPointPath, &sb) < 0 || !S_ISDIR(sb.st_mode)) {
goto exit;
}
contextprintf(context, kBLLogLevelVerbose, "Did mount or confirm Preboot at %s\n", specialMountPointPath);
ret = BLGetOSVersion(context, specialMountPointPath, NULL);
if (ret) { ret = 0; goto exit; }
contextprintf(context, kBLLogLevelVerbose, "Confirmed valid system on Preboot\n");
*isPreSSVToSSV = true;
exit:;
if (mustUnmountPreboot) {
BLUnmountContainerVolume(context, specialMountPointPath);
}
contextprintf(context, kBLLogLevelVerbose, "This is%s an APFS Data-Volume-Parameter-Driven Pre-SSV to SSV case\n",
*isPreSSVToSSV ? "" : " not");
return ret;
}