#include #include #include #include #include #include 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; }