macOS SCSI transport rewritten from hybrid MMC+pread to single-path raw CDB dispatch through SCSITaskDeviceInterface. All CDBs (INQUIRY, READ, REPORT KEY, etc.) now go through ExecuteTaskSync — 1:1 with the Linux SG_IO backend. Key changes: - New macos_shim.c: diskutil unmount → find IOBDServices → ObtainExclusiveAccess → raw CDB dispatch. Eliminates Rust-side IOKit COM vtable complexity. - build.rs compiles macos_shim.c via cc into static lib - macos.rs simplified to three FFI calls (open/close/execute) - disc/mod.rs: graduated batch restore after errors, skip-ahead through bad zones, configurable error pause
143 lines
4.2 KiB
C
143 lines
4.2 KiB
C
#include <IOKit/IOKitLib.h>
|
|
#include <IOKit/IOCFPlugIn.h>
|
|
#include <IOKit/scsi/SCSITaskLib.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
typedef struct {
|
|
IOCFPlugInInterface **plugin;
|
|
MMCDeviceInterface **mmc;
|
|
SCSITaskDeviceInterface **scsi;
|
|
int exclusive;
|
|
} ShimHandle;
|
|
|
|
static ShimHandle g_handle = {NULL, NULL, NULL, 0};
|
|
|
|
int shim_open_exclusive(const char *bsd_name) {
|
|
kern_return_t kr;
|
|
HRESULT hr;
|
|
SInt32 score = 0;
|
|
|
|
if (g_handle.exclusive && g_handle.scsi) {
|
|
return 0;
|
|
}
|
|
|
|
char cmd[128];
|
|
snprintf(cmd, sizeof(cmd), "diskutil unmountDisk force %s 2>/dev/null", bsd_name);
|
|
system(cmd);
|
|
usleep(500000);
|
|
|
|
mach_port_t mp;
|
|
IOMainPort(0, &mp);
|
|
|
|
CFMutableDictionaryRef matching = IOServiceMatching("IOBDServices");
|
|
io_service_t svc = IOServiceGetMatchingService(mp, matching);
|
|
if (!svc) return -1;
|
|
|
|
kr = IOCreatePlugInInterfaceForService(svc,
|
|
kIOMMCDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
|
|
&g_handle.plugin, &score);
|
|
IOObjectRelease(svc);
|
|
|
|
if (kr != KERN_SUCCESS || !g_handle.plugin) return -2;
|
|
|
|
hr = (*g_handle.plugin)->QueryInterface(g_handle.plugin,
|
|
CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID), (LPVOID *)&g_handle.mmc);
|
|
if (hr != S_OK || !g_handle.mmc) {
|
|
IODestroyPlugInInterface(g_handle.plugin);
|
|
g_handle.plugin = NULL;
|
|
return -3;
|
|
}
|
|
|
|
g_handle.scsi = (*g_handle.mmc)->GetSCSITaskDeviceInterface(g_handle.mmc);
|
|
if (!g_handle.scsi) {
|
|
(*g_handle.mmc)->Release(g_handle.mmc);
|
|
IODestroyPlugInInterface(g_handle.plugin);
|
|
g_handle.mmc = NULL;
|
|
g_handle.plugin = NULL;
|
|
return -4;
|
|
}
|
|
|
|
kr = (*g_handle.scsi)->ObtainExclusiveAccess(g_handle.scsi);
|
|
if (kr != kIOReturnSuccess) {
|
|
(*g_handle.scsi)->Release(g_handle.scsi);
|
|
(*g_handle.mmc)->Release(g_handle.mmc);
|
|
IODestroyPlugInInterface(g_handle.plugin);
|
|
g_handle.scsi = NULL;
|
|
g_handle.mmc = NULL;
|
|
g_handle.plugin = NULL;
|
|
return -5;
|
|
}
|
|
|
|
g_handle.exclusive = 1;
|
|
return 0;
|
|
}
|
|
|
|
void shim_close(void) {
|
|
if (g_handle.exclusive && g_handle.scsi) {
|
|
(*g_handle.scsi)->ReleaseExclusiveAccess(g_handle.scsi);
|
|
}
|
|
if (g_handle.scsi) {
|
|
(*g_handle.scsi)->Release(g_handle.scsi);
|
|
g_handle.scsi = NULL;
|
|
}
|
|
if (g_handle.mmc) {
|
|
(*g_handle.mmc)->Release(g_handle.mmc);
|
|
g_handle.mmc = NULL;
|
|
}
|
|
if (g_handle.plugin) {
|
|
IODestroyPlugInInterface(g_handle.plugin);
|
|
g_handle.plugin = NULL;
|
|
}
|
|
g_handle.exclusive = 0;
|
|
}
|
|
|
|
int shim_execute(const unsigned char *cdb, unsigned char cdb_len,
|
|
void *buf, unsigned int buf_len, int data_in,
|
|
unsigned char *sense_out, unsigned int sense_len,
|
|
unsigned char *task_status_out, unsigned long long *transfer_count) {
|
|
if (!g_handle.scsi) return -1;
|
|
|
|
SCSITaskInterface **task = (*g_handle.scsi)->CreateSCSITask(g_handle.scsi);
|
|
if (!task) return -2;
|
|
|
|
SCSICommandDescriptorBlock cdb_buf;
|
|
memset(&cdb_buf, 0, sizeof(cdb_buf));
|
|
memcpy(&cdb_buf, cdb, cdb_len);
|
|
|
|
(*task)->SetCommandDescriptorBlock(task, cdb_buf, cdb_len);
|
|
|
|
if (buf_len > 0 && buf) {
|
|
SCSITaskSGElement sg;
|
|
sg.address = (UInt64)(uintptr_t)buf;
|
|
sg.length = buf_len;
|
|
(*task)->SetScatterGatherEntries(task, &sg, 1, buf_len,
|
|
data_in ? kSCSIDataTransfer_FromTargetToInitiator
|
|
: kSCSIDataTransfer_FromInitiatorToTarget);
|
|
} else {
|
|
(*task)->SetScatterGatherEntries(task, NULL, 0, 0,
|
|
kSCSIDataTransfer_NoDataTransfer);
|
|
}
|
|
|
|
(*task)->SetTimeoutDuration(task, 30000);
|
|
|
|
SCSI_Sense_Data sense;
|
|
memset(&sense, 0, sizeof(sense));
|
|
SCSITaskStatus status = 0xFF;
|
|
UInt64 count = 0;
|
|
|
|
IOReturn kr = (*task)->ExecuteTaskSync(task, &sense, &status, &count);
|
|
|
|
if (sense_out && sense_len > 0) {
|
|
size_t copy = sense_len < sizeof(sense) ? sense_len : sizeof(sense);
|
|
memcpy(sense_out, &sense, copy);
|
|
}
|
|
if (task_status_out) *task_status_out = (unsigned char)status;
|
|
if (transfer_count) *transfer_count = count;
|
|
|
|
(*task)->Release(task);
|
|
|
|
return (int)kr;
|
|
}
|