feat: re-introduced MacOS support (#71)
* preparatory changes to make dvdisaster compilable under MacOS * Implemented workaround to macOS Test Unit Ready bug * fixed manual.pdf not opening on macOS * Added case for binary being in app bundle * updated locale files due to change in closure.c * added back mac app bundle specific files * reverted make-dist.sh to windows/linux only version and created separate mac script which uses dylibbundler * altered release.yml accordingly * chore: fix build under MacOS --------- Co-authored-by: Stéphane Lesimple <speed47_github@speed47.net> Co-authored-by: jlnbxn <julianboxan@gmail.com> Co-authored-by: wojas <github@m.wojas.nl>
This commit is contained in:
committed by
GitHub
parent
f38969c7c8
commit
eeb9f0705d
378
src/scsi-darwin.c
Normal file
378
src/scsi-darwin.c
Normal file
@@ -0,0 +1,378 @@
|
||||
/* dvdisaster: Additional error correction for optical media.
|
||||
* Copyright (C) 2004-2011 Carsten Gnoerlich.
|
||||
*
|
||||
* Email: carsten@dvdisaster.org -or- cgnoerlich@fsfe.org
|
||||
* Project homepage: http://www.dvdisaster.org
|
||||
*
|
||||
* This file is part of dvdisaster.
|
||||
*
|
||||
* dvdisaster is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* dvdisaster is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with dvdisaster. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Darwin support by Julian Einwag <julian@einwag.de>.
|
||||
* This is still an early version.
|
||||
*/
|
||||
|
||||
#include "dvdisaster.h"
|
||||
|
||||
#include "scsi-layer.h"
|
||||
#include "udf.h"
|
||||
|
||||
#ifdef SYS_DARWIN
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
|
||||
#include <IOKit/IOBSD.h>
|
||||
#include <IOKit/IOCFPlugIn.h>
|
||||
#include <IOKit/usb/IOUSBLib.h>
|
||||
#include <IOKit/usb/USBSpec.h>
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* Unmount media before trying to access them.
|
||||
* Added by Bernd Heller, <bdheller@users.sourceforge.net>
|
||||
*/
|
||||
|
||||
static void unmountDVD(io_object_t scsiDevice)
|
||||
{
|
||||
CFStringRef bsdName = NULL;
|
||||
|
||||
bsdName = (CFStringRef) IORegistryEntrySearchCFProperty(scsiDevice,
|
||||
kIOServicePlane,
|
||||
CFSTR(kIOBSDNameKey),
|
||||
kCFAllocatorDefault,
|
||||
kIORegistryIterateRecursively);
|
||||
if (bsdName != NULL) {
|
||||
// unmount all partitions
|
||||
char cmd[4096];
|
||||
char bsdNameStr[100];
|
||||
CFStringGetCString(bsdName, bsdNameStr, sizeof(bsdNameStr), kCFStringEncodingUTF8);
|
||||
|
||||
sprintf(cmd, "/usr/sbin/diskutil unmountDisk /dev/%s", bsdNameStr);
|
||||
system(cmd);
|
||||
|
||||
CFRelease(bsdName);
|
||||
} else {
|
||||
// no media to unmount
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
char *getProductName(io_object_t device)
|
||||
{
|
||||
CFDictionaryRef devCharacteristics;
|
||||
CFStringRef nameRef;
|
||||
CFIndex length;
|
||||
char *prodName;
|
||||
|
||||
devCharacteristics = IORegistryEntryCreateCFProperty(device, CFSTR(kIOPropertyDeviceCharacteristicsKey), kCFAllocatorDefault, 0);
|
||||
|
||||
if (CFDictionaryGetValueIfPresent(devCharacteristics, CFSTR("Product Name"), (const void **) &nameRef)) {
|
||||
length = CFStringGetLength(nameRef)+1;
|
||||
prodName = malloc(length);
|
||||
CFStringGetCString(nameRef, prodName, length, 0);
|
||||
CFRelease(nameRef);
|
||||
return prodName;
|
||||
} else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
io_iterator_t getDVDIterator(char* ioClass)
|
||||
{
|
||||
io_iterator_t scsiObjectIterator = (io_iterator_t) NULL;
|
||||
CFMutableDictionaryRef matchingDict = NULL;
|
||||
IOReturn ioReturnValue = kIOReturnSuccess;
|
||||
|
||||
matchingDict = IOServiceMatching(ioClass);
|
||||
if (matchingDict == (CFMutableDictionaryRef) NULL) {
|
||||
return (io_iterator_t) NULL;
|
||||
}
|
||||
ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &scsiObjectIterator);
|
||||
if (scsiObjectIterator == (io_iterator_t) NULL || (ioReturnValue != kIOReturnSuccess)) {
|
||||
return (io_iterator_t) NULL;
|
||||
}
|
||||
return scsiObjectIterator;
|
||||
|
||||
}
|
||||
|
||||
IOCFPlugInInterface **getPlugInInterface(io_object_t scsiDevice)
|
||||
{
|
||||
IOReturn ioReturnValue;
|
||||
SInt32 score = 0;
|
||||
IOCFPlugInInterface **plugInInterface = NULL;
|
||||
|
||||
ioReturnValue = IOCreatePlugInInterfaceForService(scsiDevice, kIOMMCDeviceUserClientTypeID, kIOCFPlugInInterfaceID,&plugInInterface, &score);
|
||||
if (ioReturnValue != kIOReturnSuccess) {
|
||||
return NULL;
|
||||
}
|
||||
return plugInInterface;
|
||||
}
|
||||
|
||||
MMCDeviceInterface** getMMCInterface(IOCFPlugInInterface** plugInInterface)
|
||||
{
|
||||
MMCDeviceInterface **mmcDeviceInterface = NULL;
|
||||
HRESULT pluginResult;
|
||||
pluginResult = (*plugInInterface)->QueryInterface(plugInInterface,
|
||||
CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID),
|
||||
(LPVOID)&mmcDeviceInterface);
|
||||
if (pluginResult != KERN_SUCCESS) {
|
||||
return NULL;
|
||||
}
|
||||
return mmcDeviceInterface;
|
||||
}
|
||||
|
||||
char *DefaultDevice()
|
||||
{
|
||||
int i;
|
||||
io_iterator_t scsiObjectIterator = (io_iterator_t) NULL;
|
||||
io_object_t scsiDevice;
|
||||
char *deviceName, *prodName;
|
||||
|
||||
/* As a convenience, add the simulated drive first. */
|
||||
|
||||
InitSimulatedCD();
|
||||
|
||||
/* Now probe the physical drives. */
|
||||
|
||||
scsiObjectIterator = getDVDIterator("IOBDServices");
|
||||
for (i = 1; (scsiDevice = (io_object_t) IOIteratorNext(scsiObjectIterator)) != (io_object_t) NULL; i++) {
|
||||
deviceName = g_malloc0(80);
|
||||
sprintf(deviceName,"IOBDServices/%d",i);
|
||||
|
||||
prodName = getProductName(scsiDevice);
|
||||
g_ptr_array_add(Closure->deviceNodes, g_strdup(deviceName));
|
||||
g_ptr_array_add(Closure->deviceNames, g_strdup(prodName));
|
||||
g_free(deviceName);
|
||||
}
|
||||
IOObjectRelease(scsiObjectIterator);
|
||||
scsiObjectIterator = getDVDIterator("IODVDServices");
|
||||
for (i = 1; (scsiDevice = (io_object_t) IOIteratorNext(scsiObjectIterator)) != (io_object_t) NULL; i++) {
|
||||
deviceName = g_malloc0(80);
|
||||
sprintf(deviceName,"IODVDServices/%d",i);
|
||||
|
||||
prodName = getProductName(scsiDevice);
|
||||
g_ptr_array_add(Closure->deviceNodes, g_strdup(deviceName));
|
||||
g_ptr_array_add(Closure->deviceNames, g_strdup(prodName));
|
||||
g_free(deviceName);
|
||||
}
|
||||
IOObjectRelease(scsiObjectIterator);
|
||||
scsiObjectIterator = getDVDIterator("IOCompactDiscServices");
|
||||
for (i = 1; (scsiDevice = IOIteratorNext(scsiObjectIterator)) != (io_object_t) NULL; i++) {
|
||||
deviceName = g_malloc0(80);
|
||||
sprintf(deviceName,"IOCompactDiscServices/%d",i);
|
||||
g_ptr_array_add(Closure->deviceNodes, g_strdup(deviceName));
|
||||
g_ptr_array_add(Closure->deviceNames, g_strdup(deviceName));
|
||||
g_free(deviceName);
|
||||
}
|
||||
if(Closure->deviceNodes->len)
|
||||
return g_strdup(g_ptr_array_index(Closure->deviceNodes, 0));
|
||||
else { PrintLog(_("No CD/DVD drives found."));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DeviceHandle* OpenDevice(char *device)
|
||||
{
|
||||
DeviceHandle *dh;
|
||||
io_iterator_t scsiObjectIterator;
|
||||
io_object_t scsiDevice;
|
||||
IOReturn ioReturnValue = kIOReturnSuccess;
|
||||
int numericalId = 1, i;
|
||||
char *realdevice = NULL, *tmp;
|
||||
|
||||
/* use a naming scheme identical to cdrtools */
|
||||
realdevice = tmp = strdup(device);
|
||||
tmp = strchr(tmp, '/');
|
||||
if (tmp != NULL) {
|
||||
*tmp++ = '\0';
|
||||
numericalId = atoi(tmp);
|
||||
}
|
||||
dh = g_malloc0(sizeof(DeviceHandle));
|
||||
dh->senseSize = sizeof(SCSI_Sense_Data);
|
||||
|
||||
if(!strcmp(device, "sim-cd"))
|
||||
{ if(!Closure->simulateCD) /* can happen via resource file / last-device */
|
||||
{ g_free(dh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dh->simImage = LargeOpen(Closure->simulateCD, O_RDONLY, IMG_PERMS);
|
||||
if(!dh->simImage)
|
||||
{ g_free(dh);
|
||||
|
||||
Stop(_("Could not open %s: %s"), Closure->simulateCD, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dh->device = g_strdup(device);
|
||||
return dh;
|
||||
}
|
||||
|
||||
scsiObjectIterator = getDVDIterator(realdevice);
|
||||
if (scsiObjectIterator == (io_iterator_t) NULL) {
|
||||
g_free(dh);
|
||||
Stop(_("Could not open %s (%s)."), device, realdevice);
|
||||
return NULL;
|
||||
}
|
||||
/* look up handle for the selected device */
|
||||
for (i = 1; (scsiDevice = IOIteratorNext(scsiObjectIterator)) != (io_object_t) NULL; i++) {
|
||||
if (i == numericalId) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unmountDVD(scsiDevice);
|
||||
|
||||
dh->plugInInterface = getPlugInInterface(scsiDevice);
|
||||
if (dh->plugInInterface == NULL) {
|
||||
g_free(dh);
|
||||
Stop("Could not get PlugInInterface.");
|
||||
return NULL;
|
||||
}
|
||||
dh->mmcDeviceInterface = getMMCInterface(dh->plugInInterface);
|
||||
if (dh->mmcDeviceInterface == NULL) {
|
||||
g_free(dh);
|
||||
Stop("Could not get MMCDeviceInterface.");
|
||||
return NULL;
|
||||
}
|
||||
(dh->scsiTaskDeviceInterface) = (*dh->mmcDeviceInterface)->GetSCSITaskDeviceInterface(dh->mmcDeviceInterface);
|
||||
if (dh->scsiTaskDeviceInterface == NULL) {
|
||||
g_free(dh);
|
||||
Stop("Could not get SCSITaskDeviceInterface.");
|
||||
return NULL;
|
||||
}
|
||||
ioReturnValue = (*dh->scsiTaskDeviceInterface)->ObtainExclusiveAccess(dh->scsiTaskDeviceInterface);
|
||||
if (ioReturnValue != kIOReturnSuccess) {
|
||||
Stop("Couldn't obtain exclusive access to drive.");
|
||||
return NULL;
|
||||
}
|
||||
dh->taskInterface = (*dh->scsiTaskDeviceInterface)->CreateSCSITask(dh->scsiTaskDeviceInterface);
|
||||
if (dh->taskInterface == NULL) {
|
||||
Stop("Could not create taskInterface.");
|
||||
return NULL;
|
||||
}
|
||||
(*dh->taskInterface)->SetTimeoutDuration(dh->taskInterface, 120*1000);
|
||||
dh->device = g_strdup(device);
|
||||
return dh;
|
||||
}
|
||||
|
||||
void CloseDevice(DeviceHandle *dh)
|
||||
{
|
||||
if(dh->simImage)
|
||||
LargeClose(dh->simImage);
|
||||
|
||||
if(dh->canReadDefective)
|
||||
SetRawMode(dh, MODE_PAGE_UNSET);
|
||||
|
||||
if(dh->rawBuffer)
|
||||
FreeRawBuffer(dh->rawBuffer);
|
||||
|
||||
if (dh->taskInterface) {
|
||||
(*dh->taskInterface)->Release(dh->taskInterface);
|
||||
}
|
||||
if (dh->scsiTaskDeviceInterface) {
|
||||
(*dh->scsiTaskDeviceInterface)->ReleaseExclusiveAccess(dh->scsiTaskDeviceInterface);
|
||||
(*dh->scsiTaskDeviceInterface)->Release(dh->scsiTaskDeviceInterface);
|
||||
}
|
||||
if (dh->mmcDeviceInterface) {
|
||||
(*dh->mmcDeviceInterface)->Release(dh->mmcDeviceInterface);
|
||||
}
|
||||
if(dh->plugInInterface) {
|
||||
IODestroyPlugInInterface(dh->plugInInterface);
|
||||
}
|
||||
if(dh->range) {
|
||||
g_free(dh->range);
|
||||
}
|
||||
|
||||
if(dh->typeDescr)
|
||||
g_free(dh->typeDescr);
|
||||
if(dh->mediumDescr)
|
||||
g_free(dh->mediumDescr);
|
||||
if(dh->defects)
|
||||
FreeBitmap(dh->defects);
|
||||
|
||||
g_free(dh);
|
||||
|
||||
}
|
||||
|
||||
int SendPacket(DeviceHandle *dh, unsigned char *cmd, int cdb_size, unsigned char *buf, int size, Sense *sense, int data_mode)
|
||||
{
|
||||
u_int32_t flags = 0;
|
||||
SCSITaskInterface **taskInterface;
|
||||
SCSITaskStatus taskStatus = kSCSITaskStatus_No_Status;
|
||||
IOReturn ioReturnValue;
|
||||
|
||||
if(dh->simImage)
|
||||
return SimulateSendPacket(dh, cmd, cdb_size, buf, size, sense, data_mode);
|
||||
|
||||
switch(data_mode) {
|
||||
case DATA_READ:
|
||||
flags = kSCSIDataTransfer_FromTargetToInitiator;
|
||||
break;
|
||||
case DATA_WRITE:
|
||||
flags = kSCSIDataTransfer_FromInitiatorToTarget;
|
||||
break;
|
||||
case DATA_NONE:
|
||||
flags = kSCSIDataTransfer_NoDataTransfer;
|
||||
break;
|
||||
default:
|
||||
Stop("illegal data_mode: %d",data_mode);
|
||||
}
|
||||
|
||||
taskInterface = dh->taskInterface;
|
||||
if (flags == kSCSIDataTransfer_FromTargetToInitiator || flags == kSCSIDataTransfer_FromInitiatorToTarget) {
|
||||
dh->range = (IOVirtualRange *) g_malloc(sizeof(IOVirtualRange));
|
||||
dh->range->address = (IOVirtualAddress) buf;
|
||||
dh->range->length = size;
|
||||
}
|
||||
ioReturnValue = (*taskInterface)->SetCommandDescriptorBlock(taskInterface, cmd, cdb_size);
|
||||
if (ioReturnValue != kIOReturnSuccess) {
|
||||
Stop("Couldn't set command descriptor block.");
|
||||
}
|
||||
if (flags == kSCSIDataTransfer_FromTargetToInitiator || flags == kSCSIDataTransfer_FromInitiatorToTarget) {
|
||||
ioReturnValue = (*taskInterface)->SetScatterGatherEntries(taskInterface, dh->range, 1, size, flags);
|
||||
if (ioReturnValue != kIOReturnSuccess) {
|
||||
Stop("Couldn't set scatter-gather.");
|
||||
}
|
||||
}
|
||||
|
||||
// Set the timeout in the task
|
||||
ioReturnValue = ( *taskInterface )->SetTimeoutDuration ( taskInterface, 5000 );
|
||||
if ( ioReturnValue != kIOReturnSuccess ) {
|
||||
Stop("Operation timed out.");;
|
||||
}
|
||||
|
||||
|
||||
ioReturnValue = (*taskInterface)->ExecuteTaskSync(taskInterface, (SCSI_Sense_Data*) sense, &taskStatus, NULL);
|
||||
if (ioReturnValue != kIOReturnSuccess) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (taskStatus == kSCSITaskStatus_DeliveryFailure && cmd[0] == 0x00) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (taskStatus != kSCSITaskStatus_GOOD) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user