diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 51546e3..9b86c3a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -267,7 +267,8 @@ elseif(APPLE) find_library(CoreFoundation CoreFoundation) find_library(DiskArbitration DiskArbitration) find_library(Security Security) - set(EXTRALIBS ${EXTRALIBS} ${CoreFoundation} ${DiskArbitration} ${Security} ${Cocoa}) + find_library(IOKit IOKit) + set(EXTRALIBS ${EXTRALIBS} ${CoreFoundation} ${DiskArbitration} ${Security} ${Cocoa} ${IOKit}) set_target_properties(${PROJECT_NAME} PROPERTIES MACOSX_BUNDLE YES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/mac/Info.plist.in) find_program(MACDEPLOYQT "macdeployqt" PATHS "${Qt5_DIR}/../../../bin") diff --git a/src/dependencies/drivelist/src/darwin/REDiskList.m b/src/dependencies/drivelist/src/darwin/REDiskList.m index 60a63d0..b08abc3 100644 --- a/src/dependencies/drivelist/src/darwin/REDiskList.m +++ b/src/dependencies/drivelist/src/darwin/REDiskList.m @@ -26,7 +26,7 @@ if (self) { _disks = [[NSMutableArray alloc] init]; [self populateDisksBlocking]; - [_disks sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; + [(NSMutableArray *)_disks sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; } return self; diff --git a/src/dependencies/drivelist/src/darwin/list.mm b/src/dependencies/drivelist/src/darwin/list.mm index f8f1dd4..4509df5 100644 --- a/src/dependencies/drivelist/src/darwin/list.mm +++ b/src/dependencies/drivelist/src/darwin/list.mm @@ -21,6 +21,9 @@ #import "REDiskList.h" #import #import +#import +#import +#import namespace Drivelist { bool IsDiskPartition(NSString *disk) { @@ -54,6 +57,55 @@ namespace Drivelist { return (NSNumber*)CFDictionaryGetValue(dict, key); } + std::string GetParentOfAPFS(const char *diskBsdName) { + /* Inspired by: https://opensource.apple.com/source/bless/bless-152/libbless/APFS/BLAPFSUtilities.c.auto.html + Simplified, assumes APFS only has a single physical drive */ + std::string result; + kern_return_t kret; + io_iterator_t psIter; + CFTypeRef data; + NSString *s; + io_service_t parent, p = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, diskBsdName)); + + if (p) + { + /* Go three levels up in hierarchy */ + for (int i=0; i<3; i++) + { + kret = IORegistryEntryGetParentEntry(p, kIOServicePlane, &parent); + IOObjectRelease(p); + if (kret) + { + /* Error. Return empty string */ + return result; + } + p = parent; + } + + IORegistryEntryGetParentIterator(p, kIOServicePlane, &psIter); + parent = IOIteratorNext(psIter); + if (parent) + { + if (IOObjectConformsTo(parent, kIOMediaClass)) + { + data = IORegistryEntryCreateCFProperty(parent, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0); + + if (data && CFGetTypeID(data) == CFStringGetTypeID()) + { + s = (NSString *) data; + result = std::string([s UTF8String]); + CFRelease(data); + } + } + IOObjectRelease(parent); + } + IOObjectRelease(psIter); + IOObjectRelease(p); + } + + return result; + } + DeviceDescriptor CreateDeviceDescriptorFromDiskDescription(std::string diskBsdName, CFDictionaryRef diskDescription) { NSString *deviceProtocol = (NSString*)CFDictionaryGetValue(diskDescription, kDADiskDescriptionDeviceProtocolKey); NSNumber *blockSize = DictionaryGetNumber(diskDescription, kDADiskDescriptionMediaBlockSizeKey); @@ -126,6 +178,13 @@ namespace Drivelist { } DeviceDescriptor device = CreateDeviceDescriptorFromDiskDescription(diskBsdNameStr, diskDescription); + + if (device.description == "AppleAPFSMedia") + { + device.isVirtual = true; + device.parentDevice = GetParentOfAPFS(diskBsdNameStr.c_str()); + } + deviceList.push_back(device); CFRelease(diskDescription); @@ -158,6 +217,20 @@ namespace Drivelist { std::string partitionBsdName = std::string(bsdnameChar); std::string diskBsdName = partitionBsdName.substr(0, partitionBsdName.find("s", 5)); + std::string childDevice; + + /* Check if it concerns APFS volume first, and if so attribute mountpoints to parent device instead */ + for(std::vector::size_type i = 0; i != deviceList.size(); i++) { + DeviceDescriptor *dd = &deviceList[i]; + + if (dd->device == "/dev/" + diskBsdName) { + if (!dd->parentDevice.empty()) { + childDevice = diskBsdName; + diskBsdName = dd->parentDevice; + } + break; + } + } for(std::vector::size_type i = 0; i != deviceList.size(); i++) { DeviceDescriptor *dd = &deviceList[i]; @@ -165,6 +238,9 @@ namespace Drivelist { if (dd->device == "/dev/" + diskBsdName) { dd->mountpoints.push_back([[path path] UTF8String]); dd->mountpointLabels.push_back([volumeName UTF8String]); + if (!childDevice.empty()) { + dd->childDevices.push_back("/dev/"+childDevice); + } break; } } diff --git a/src/dependencies/drivelist/src/drivelist.hpp b/src/dependencies/drivelist/src/drivelist.hpp index 1de452f..096b181 100644 --- a/src/dependencies/drivelist/src/drivelist.hpp +++ b/src/dependencies/drivelist/src/drivelist.hpp @@ -38,11 +38,13 @@ struct DeviceDescriptor { std::string raw; std::string description; std::string error; + std::string parentDevice; uint64_t size; uint32_t blockSize = 512; uint32_t logicalBlockSize = 512; std::vector mountpoints; std::vector mountpointLabels; + std::vector childDevices; bool isReadOnly; // Device is read-only bool isSystem; // Device is a system drive bool isVirtual; // Device is a virtual storage device diff --git a/src/downloadthread.cpp b/src/downloadthread.cpp index da1e294..d6778a8 100644 --- a/src/downloadthread.cpp +++ b/src/downloadthread.cpp @@ -111,12 +111,29 @@ QByteArray DownloadThread::_fileGetContentsTrimmed(const QString &filename) bool DownloadThread::_openAndPrepareDevice() { - emit preparationStatusUpdate(tr("opening drive")); - if (_filename.startsWith("/dev/")) { + emit preparationStatusUpdate(tr("unmounting drive")); +#ifdef Q_OS_DARWIN + /* Also unmount any APFS volumes using this physical disk */ + auto l = Drivelist::ListStorageDevices(); + for (const auto &i : l) + { + if (QByteArray::fromStdString(i.device) == _filename) + { + for (const auto &j : i.childDevices) + { + qDebug() << "Unmounting APFS volume:" << j.c_str(); + unmount_disk(j.c_str()); + } + break; + } + } +#endif + qDebug() << "Unmounting:" << _filename; unmount_disk(_filename.constData()); } + emit preparationStatusUpdate(tr("opening drive")); _file.setFileName(_filename);