v0.16.0: IOKit registry-based drive enumeration, BSD name → IOBDServices matching, reverse patch default

This commit is contained in:
MattJackson
2026-04-30 15:17:20 -07:00
parent 7dd5001d45
commit a1f4dff6f6
5 changed files with 286 additions and 42 deletions
+236 -2
View File
@@ -1,10 +1,13 @@
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/scsi/SCSITaskLib.h>
#include <CoreFoundation/CoreFoundation.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// ── Types ──────────────────────────────────────────────────────────────────
typedef struct {
IOCFPlugInInterface **plugin;
MMCDeviceInterface **mmc;
@@ -12,8 +15,196 @@ typedef struct {
int exclusive;
} ShimHandle;
typedef struct {
char bsd_name[32];
char vendor[32];
char model[48];
char firmware[16];
} ShimDriveInfo;
// ── Global handle (single-drive, same as before) ──────────────────────────
static ShimHandle g_handle = {NULL, NULL, NULL, 0};
// ── Registry helpers ──────────────────────────────────────────────────────
static int cfstring_to_cstr(CFStringRef cf, char *buf, size_t buflen) {
if (!cf) return 0;
if (!CFStringGetCString(cf, buf, buflen, kCFStringEncodingUTF8)) return 0;
return 1;
}
static int registry_entry_bsd_name(io_registry_entry_t entry, char *buf, size_t buflen) {
CFStringRef cf = IORegistryEntryCreateCFProperty(entry, CFSTR("BSD Name"),
kCFAllocatorDefault, 0);
if (!cf) return 0;
int ok = cfstring_to_cstr(cf, buf, buflen);
CFRelease(cf);
return ok;
}
static io_registry_entry_t find_iomedia_child(io_registry_entry_t parent) {
io_iterator_t iter;
kern_return_t kr = IORegistryEntryGetChildIterator(parent, kIOServicePlane, &iter);
if (kr != KERN_SUCCESS) return 0;
io_registry_entry_t child;
while ((child = IOIteratorNext(iter)) != 0) {
char cls[128];
kr = IOObjectGetClass(child, cls);
if (kr == KERN_SUCCESS) {
if (strcmp(cls, "IOMedia") == 0) {
IOObjectRelease(iter);
return child;
}
}
IOObjectRelease(child);
}
IOObjectRelease(iter);
return 0;
}
static io_registry_entry_t find_child_of_class(io_registry_entry_t parent, const char *target_class) {
io_iterator_t iter;
kern_return_t kr = IORegistryEntryGetChildIterator(parent, kIOServicePlane, &iter);
if (kr != KERN_SUCCESS) return 0;
io_registry_entry_t child;
while ((child = IOIteratorNext(iter)) != 0) {
char cls[128];
kr = IOObjectGetClass(child, cls);
if (kr == KERN_SUCCESS && strcmp(cls, target_class) == 0) {
IOObjectRelease(iter);
return child;
}
IOObjectRelease(child);
}
IOObjectRelease(iter);
return 0;
}
static io_registry_entry_t find_parent_of_class(io_registry_entry_t entry, const char *target_class) {
io_registry_entry_t parent;
kern_return_t kr = IORegistryEntryGetParentEntry(entry, kIOServicePlane, &parent);
if (kr != KERN_SUCCESS) return 0;
char cls[128];
kr = IOObjectGetClass(parent, cls);
if (kr == KERN_SUCCESS && strcmp(cls, target_class) == 0) {
return parent;
}
IOObjectRelease(parent);
return 0;
}
// Given an IOBDServices, find the BSD name of its IOMedia child.
// Chain: IOBDServices -> IOBDBlockStorageDriver -> IOMedia (has "BSD Name")
static int bdsvc_to_bsd_name(io_registry_entry_t bdsvc, char *buf, size_t buflen) {
io_registry_entry_t driver = find_child_of_class(bdsvc, "IOBDBlockStorageDriver");
if (!driver) return 0;
io_registry_entry_t media = find_iomedia_child(driver);
IOObjectRelease(driver);
if (!media) return 0;
int ok = registry_entry_bsd_name(media, buf, buflen);
IOObjectRelease(media);
return ok;
}
// Given an IOBDServices, extract Device Characteristics strings.
static void bdsvc_device_info(io_registry_entry_t bdsvc, ShimDriveInfo *info) {
CFDictionaryRef dc = IORegistryEntryCreateCFProperty(bdsvc,
CFSTR("Device Characteristics"), kCFAllocatorDefault, 0);
if (!dc) return;
CFStringRef val;
val = CFDictionaryGetValue(dc, CFSTR("Vendor Name"));
if (val) cfstring_to_cstr(val, info->vendor, sizeof(info->vendor));
val = CFDictionaryGetValue(dc, CFSTR("Product Name"));
if (val) cfstring_to_cstr(val, info->model, sizeof(info->model));
val = CFDictionaryGetValue(dc, CFSTR("Product Revision Level"));
if (val) cfstring_to_cstr(val, info->firmware, sizeof(info->firmware));
CFRelease(dc);
}
// Find the IOBDServices that owns the given BSD name.
// Returns a retained io_service_t (caller must release), or 0.
static io_service_t find_bdsvc_by_bsd_name(mach_port_t mp, const char *bsd_name) {
CFMutableDictionaryRef matching = IOServiceMatching("IOBDServices");
if (!matching) return 0;
io_iterator_t iter;
kern_return_t kr = IOServiceGetMatchingServices(mp, matching, &iter);
if (kr != KERN_SUCCESS) return 0;
io_service_t result = 0;
io_service_t svc;
while ((svc = IOIteratorNext(iter)) != 0) {
char name[64];
if (bdsvc_to_bsd_name(svc, name, sizeof(name))) {
if (strcmp(name, bsd_name) == 0) {
result = svc;
break;
}
}
IOObjectRelease(svc);
}
if (!result) {
IOIteratorReset(iter);
while ((svc = IOIteratorNext(iter)) != 0) {
IOObjectRelease(svc);
}
}
IOObjectRelease(iter);
return result;
}
// Find the IOBDServices that owns the given BSD name by walking from
// IOMedia upward. Used as fallback when bdsvc_to_bsd_name fails
// (e.g. disc under exclusive access, no IOMedia child).
// Chain: IOMedia -> IOBDBlockStorageDriver -> IOBDServices
static io_service_t find_bdsvc_from_iomedia(mach_port_t mp, const char *bsd_name) {
CFMutableDictionaryRef matching = IOServiceMatching("IOMedia");
if (!matching) return 0;
io_iterator_t iter;
kern_return_t kr = IOServiceGetMatchingServices(mp, matching, &iter);
if (kr != KERN_SUCCESS) return 0;
io_service_t result = 0;
io_service_t media;
while ((media = IOIteratorNext(iter)) != 0) {
char name[64];
if (registry_entry_bsd_name(media, name, sizeof(name))
&& strcmp(name, bsd_name) == 0)
{
io_registry_entry_t driver = find_parent_of_class(media, "IOBDBlockStorageDriver");
if (driver) {
io_registry_entry_t bdsvc = find_parent_of_class(driver, "IOBDServices");
IOObjectRelease(driver);
if (bdsvc) {
result = bdsvc;
IOObjectRelease(media);
break;
}
}
}
IOObjectRelease(media);
}
IOObjectRelease(iter);
return result;
}
// ── Public API ────────────────────────────────────────────────────────────
int shim_open_exclusive(const char *bsd_name) {
kern_return_t kr;
HRESULT hr;
@@ -31,8 +222,14 @@ int shim_open_exclusive(const char *bsd_name) {
mach_port_t mp;
IOMainPort(0, &mp);
CFMutableDictionaryRef matching = IOServiceMatching("IOBDServices");
io_service_t svc = IOServiceGetMatchingService(mp, matching);
io_service_t svc = find_bdsvc_by_bsd_name(mp, bsd_name);
if (!svc) {
svc = find_bdsvc_from_iomedia(mp, bsd_name);
}
if (!svc) {
CFMutableDictionaryRef matching = IOServiceMatching("IOBDServices");
svc = IOServiceGetMatchingService(mp, matching);
}
if (!svc) return -1;
kr = IOCreatePlugInInterfaceForService(svc,
@@ -140,3 +337,40 @@ int shim_execute(const unsigned char *cdb, unsigned char cdb_len,
return (int)kr;
}
// ── Registry-based drive enumeration ──────────────────────────────────────
//
// Walks IOBDServices entries in the IOKit registry. No exclusive access,
// no SCSI commands, no unmounts. Returns up to max_entries drives.
int shim_list_drives(ShimDriveInfo *out, int max_entries) {
mach_port_t mp;
IOReturn ret = IOMainPort(0, &mp);
if (ret != kIOReturnSuccess) return 0;
CFMutableDictionaryRef matching = IOServiceMatching("IOBDServices");
if (!matching) return 0;
io_iterator_t iter;
kern_return_t kr = IOServiceGetMatchingServices(mp, matching, &iter);
if (kr != KERN_SUCCESS) return 0;
int count = 0;
io_service_t svc;
while ((svc = IOIteratorNext(iter)) != 0 && count < max_entries) {
ShimDriveInfo *info = &out[count];
memset(info, 0, sizeof(*info));
bdsvc_device_info(svc, info);
bdsvc_to_bsd_name(svc, info->bsd_name, sizeof(info->bsd_name));
if (info->bsd_name[0]) {
count++;
}
IOObjectRelease(svc);
}
IOObjectRelease(iter);
return count;
}