/* 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 . */ /* * Darwin support by Julian Einwag . * This is still an early version. */ #include "dvdisaster.h" #include "scsi-layer.h" #include "udf.h" #ifdef SYS_DARWIN #include #include #include #include #include #include #include #include /* * Unmount media before trying to access them. * Added by Bernd Heller, */ 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(0, 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