mirror of
https://github.com/cmclark00/retro-imager.git
synced 2025-05-18 16:05:21 +01:00
OSX: fix unmounting drives that have APFS volumes
- If a drive is formatted APFS it will have a seperate disk devices for physical drive (e.g. /dev/disk2) and volumes (e.g. /dev/disk3). Need to unmount all, or opening the device for writing will subsequently fail. (User will see an "Error running authopen" error in Imager in that case). - Also do not show APFS volumes seperately in the disk selection dialog. List mount points under physical drive instead. Closes #501
This commit is contained in:
parent
9d4665dbca
commit
30225187bd
5 changed files with 100 additions and 4 deletions
|
@ -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")
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
if (self) {
|
||||
_disks = [[NSMutableArray alloc] init];
|
||||
[self populateDisksBlocking];
|
||||
[_disks sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
|
||||
[(NSMutableArray *)_disks sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
|
||||
}
|
||||
|
||||
return self;
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
#import "REDiskList.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <DiskArbitration/DiskArbitration.h>
|
||||
#import <IOKit/IOKitLib.h>
|
||||
#import <IOKit/storage/IOMedia.h>
|
||||
#import <IOKit/IOBSD.h>
|
||||
|
||||
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<int>::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<int>::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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<std::string> mountpoints;
|
||||
std::vector<std::string> mountpointLabels;
|
||||
std::vector<std::string> childDevices;
|
||||
bool isReadOnly; // Device is read-only
|
||||
bool isSystem; // Device is a system drive
|
||||
bool isVirtual; // Device is a virtual storage device
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue