From: Carl Hetherington Date: Mon, 17 Jan 2022 23:09:36 +0000 (+0100) Subject: Adjust how macOS drives are analysed and add a couple of tests. X-Git-Tag: v2.15.187~3 X-Git-Url: https://git.carlh.net/gitweb/?p=dcpomatic.git;a=commitdiff_plain;h=9b9202c7f9fc26fcef0984189aaed366b7c6d726 Adjust how macOS drives are analysed and add a couple of tests. --- diff --git a/src/lib/cross.h b/src/lib/cross.h index ac1f028f4..aad223d60 100644 --- a/src/lib/cross.h +++ b/src/lib/cross.h @@ -142,19 +142,17 @@ void disk_write_finished (); struct OSXMediaPath { - bool real; ///< true for a "real" disk, false for a synthesized APFS one - std::string prt; ///< "PRT" entry from the media path + bool real; ///< true for a "real" disk, false for a synthesized APFS one + std::vector parts; ///< parts of the media path after the : }; - struct OSXDisk { - std::string mount_point; + std::string device; boost::optional vendor; boost::optional model; - bool real; - std::string prt; + OSXMediaPath media_path; bool whole; std::vector mount_points; unsigned long size; diff --git a/src/lib/cross_common.cc b/src/lib/cross_common.cc index 250db3cd5..bc1a54367 100644 --- a/src/lib/cross_common.cc +++ b/src/lib/cross_common.cc @@ -124,8 +124,6 @@ Drive::log_summary () const optional analyse_osx_media_path (string path) { - using namespace boost::algorithm; - if (path.find("/IOHDIXController") != string::npos) { /* This is a disk image, so we completely ignore it */ LOG_DISK_NC("Ignoring this as it seems to be a disk image"); @@ -133,27 +131,32 @@ analyse_osx_media_path (string path) } OSXMediaPath mp; - if (starts_with(path, "IODeviceTree:")) { + vector parts; + split(parts, path, boost::is_any_of("/")); + std::copy(parts.begin() + 1, parts.end(), back_inserter(mp.parts)); + + if (!parts.empty() && parts[0] == "IODeviceTree:") { mp.real = true; - } else if (starts_with(path, "IOService:")) { + if (mp.parts.size() < 2) { + /* Later we expect at least 2 parts in a IODeviceTree */ + LOG_DISK_NC("Ignoring this as it has a strange media path"); + return {}; + } + } else if (!parts.empty() && parts[0] == "IOService:") { mp.real = false; } else { return {}; } - vector bits; - split(bits, path, boost::is_any_of("/")); - for (auto i: bits) { - if (starts_with(i, "PRT")) { - mp.prt = i; - } - } - return mp; } -/* This is in _common so we can use it in unit tests */ +/* Take soem OSXDisk objects, representing disks that `DARegisterDiskAppearedCallback` told us about, + * and find those drives that we could write a DCP to. The drives returned are "real" (not synthesized) + * and are whole disks (not partitions). They may be mounted, or contain mounted partitions, in which + * their mounted() method will return true. + */ vector osx_disks_to_drives (vector disks) { @@ -165,38 +168,37 @@ osx_disks_to_drives (vector disks) continue; } for (auto& j: disks) { - if (!j.mount_points.empty() && starts_with(j.mount_point, i.mount_point)) { - LOG_DISK("Marking %1 as mounted because %2 is", i.mount_point, j.mount_point); + if (!j.mount_points.empty() && starts_with(j.device, i.device)) { + LOG_DISK("Marking %1 as mounted because %2 is", i.device, j.device); std::copy(j.mount_points.begin(), j.mount_points.end(), back_inserter(i.mount_points)); } } } - /* Make a map of the PRT codes and mount points of mounted, synthesized disks */ - map> mounted_synths; - for (auto const& i: disks) { - if (!i.real && !i.mount_points.empty()) { - LOG_DISK("Found a mounted synth %1 with %2", i.mount_point, i.prt); - mounted_synths[i.prt] = i.mount_points; - } - } - - /* Mark containers of those mounted synths as themselves mounted */ + /* Mark containers of mounted synths as themselves mounted */ for (auto& i: disks) { - if (i.real) { - auto j = mounted_synths.find(i.prt); - if (j != mounted_synths.end()) { - LOG_DISK("Marking %1 (%2) as mounted because it contains a mounted synth", i.mount_point, i.prt); - std::copy(j->second.begin(), j->second.end(), back_inserter(i.mount_points)); + if (i.media_path.real) { + for (auto& j: disks) { + if (!j.media_path.real && !j.mount_points.empty()) { + /* i is real, j is a mounted synth; if we see the first two parts + * of i anywhere in j we assume they are related and so i shares + * j's mount points. + */ + if ( + find(j.media_path.parts.begin(), j.media_path.parts.end(), i.media_path.parts[0]) != j.media_path.parts.end() && + find(j.media_path.parts.begin(), j.media_path.parts.end(), i.media_path.parts[1]) != j.media_path.parts.end()) { + LOG_DISK("Marking %1 as mounted because %2 is (found %3 and %4)", i.device, j.device, i.media_path.parts[0], i.media_path.parts[1]); + std::copy(j.mount_points.begin(), j.mount_points.end(), back_inserter(i.mount_points)); + } + } } } } vector drives; for (auto const& i: disks) { - if (i.whole) { - /* A whole disk that is not a container for a mounted synth */ - drives.push_back(Drive(i.mount_point, i.mount_points, i.size, i.vendor, i.model)); + if (i.whole && i.media_path.real) { + drives.push_back(Drive(i.device, i.mount_points, i.size, i.vendor, i.model)); LOG_DISK_NC(drives.back().log_summary()); } } diff --git a/src/lib/cross_osx.cc b/src/lib/cross_osx.cc index 018516abb..2dc9b2702 100644 --- a/src/lib/cross_osx.cc +++ b/src/lib/cross_osx.cc @@ -380,8 +380,8 @@ mount_point (CFDictionaryRef& description) * a partition /dev/disk2s2 whose content is made into a synthesized /dev/disk3, itself containing some partitions * which are mounted. /dev/disk2s2 is not considered to be mounted, in this case. So we need to know that * disk2s2 is related to disk3 so we can consider disk2s2 as mounted if any parts of disk3 are. In order to do - * this I am picking out what looks like a suitable identifier prefixed with PRT from the MediaContentKey. - * If disk2s2 and disk3 have the same PRT code I am assuming they are linked. + * this I am taking the first two parts of the IODeviceTree and seeing if they exist anywhere in a + * IOService identifier. If they do, I am assuming the IOService device is on the matching IODeviceTree device. * * Lots of this is guesswork and may be broken. In my defence the documentation that I have been able to * unearth is, to put it impolitely, crap. @@ -399,8 +399,8 @@ disk_appeared (DADiskRef disk, void* context) OSXDisk this_disk; - this_disk.mount_point = string("/dev/") + bsd_name; - LOG_DISK("Mount point is %1", this_disk.mount_point); + this_disk.device = string("/dev/") + bsd_name; + LOG_DISK("Device is %1", this_disk.device); CFDictionaryRef description = DADiskCopyDescription (disk); @@ -414,8 +414,7 @@ disk_appeared (DADiskRef disk, void* context) return; } - this_disk.real = media_path->real; - this_disk.prt = media_path->prt; + this_disk.media_path = *media_path; this_disk.whole = is_whole_drive (disk); auto mp = mount_point (description); if (mp) { @@ -423,11 +422,10 @@ disk_appeared (DADiskRef disk, void* context) } LOG_DISK( - "%1 prt=%2 %3 %4", - this_disk.real ? "Real" : "Synth", - this_disk.prt, + "%1 %2 mounted at %3", + this_disk.media_path.real ? "Real" : "Synth", this_disk.whole ? "whole" : "part", - mp ? ("mounted at " + mp->string()) : "unmounted" + mp ? mp->string() : "[nowhere]" ); auto media_size_cstr = CFDictionaryGetValue (description, kDADiskDescriptionMediaSizeKey); diff --git a/test/disk_writer_test.cc b/test/disk_writer_test.cc index 60d7fe22a..9e5f88b80 100644 --- a/test/disk_writer_test.cc +++ b/test/disk_writer_test.cc @@ -138,3 +138,81 @@ BOOST_AUTO_TEST_CASE (disk_writer_test1) check_file ("build/test/disk_writer_test1/foo", "build/test/disk_writer_test1_foo_back"); } + +BOOST_AUTO_TEST_CASE (osx_drive_identification_test) +{ + vector disks; + + auto disk = [&disks](string mount_point, string media_path, bool whole, std::vector mount_points) + { + auto mp = analyse_osx_media_path (media_path); + if (mp) { + disks.push_back({mount_point, {}, {}, *mp, whole, mount_points, 0}); + } + }; + + auto find_unmounted = [](vector disks) { + auto drives = osx_disks_to_drives (disks); + vector unmounted; + std::copy_if (drives.begin(), drives.end(), std::back_inserter(unmounted), [](Drive const& drive) { return !drive.mounted(); }); + return unmounted; + }; + + disk("/dev/disk4s1", "IODeviceTree:/arm-io@10F00000/apcie@90000000/pci-bridge1@1/pcie-xhci@0/@7:1", false, {}); + disk("/dev/disk4", "IODeviceTree:/arm-io@10F00000/apcie@90000000/pci-bridge1@1/pcie-xhci@0/@7:0", true, {}); + disk("/dev/disk0", "IODeviceTree:/arm-io@10F00000/ans@77400000/iop-ans-nub/AppleANS3NVMeController/@1:0", true, {}); + disk("/dev/disk0s1", "IODeviceTree:/arm-io@10F00000/ans@77400000/iop-ans-nub/AppleANS3NVMeController/@1:1", false, {}); + disk("/dev/disk0s2", "IODeviceTree:/arm-io@10F00000/ans@77400000/iop-ans-nub/AppleANS3NVMeController/@1:2", false, {}); + disk("/dev/disk0s3", "IODeviceTree:/arm-io@10F00000/ans@77400000/iop-ans-nub/AppleANS3NVMeController/@1:3", false, {}); + disk("/dev/disk1", "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/ans@77400000/AppleASCWrapV4/iop-ans-nub/RTBuddyV2/RTBuddyService/AppleANS3NVMeController/NS_01@1/IOBlockStorageDriver/ APPLE SSD AP0512Q Media/IOGUIDPartitionScheme/iBootSystemContainer@1/AppleAPFSContainerScheme/AppleAPFSMedia", true, {}); + disk("/dev/disk2", "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/ans@77400000/AppleASCWrapV4/iop-ans-nub/RTBuddyV2/RTBuddyService/AppleANS3NVMeController/NS_01@1/IOBlockStorageDriver/APPLE SSD AP0512Q Media/IOGUIDPartitionScheme/RecoveryOSContainer@3/AppleAPFSContainerScheme/AppleAPFSMedia", true, {}); + disk("/dev/disk3", "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/ans@77400000/AppleASCWrapV4/iop-ans-nub/RTBuddyV2/RTBuddyService/AppleANS3NVMeController/NS_01@1/IOBlockStorageDriver/APPLE SSD AP0512Q Media/IOGUIDPartitionScheme/Container@2/AppleAPFSContainerScheme/AppleAPFSMedia", false, {}); + disk("/dev/disk1s1", "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/ans@77400000/AppleASCWrapV4/iop-ans-nub/RTBuddyV2/RTBuddyService/AppleANS3NVMeController/NS_01@1/IOBlockStorageDriver/APPLE SSD AP0512Q Media/IOGUIDPartitionScheme/iBootSystemContainer@1/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/iSCPreboot@1", false, {}); + disk("/dev/disk1s2", "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/ans@77400000/AppleASCWrapV4/iop-ans-nub/RTBuddyV2/RTBuddyService/AppleANS3NVMeController/NS_01@1/IOBlockStorageDriver/APPLE SSD AP0512Q Media/IOGUIDPartitionScheme/iBootSystemContainer@1/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/xART@2", false, {}); + disk("/dev/disk1s3", "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/ans@77400000/AppleASCWrapV4/iop-ans-nub/RTBuddyV2/RTBuddyService/AppleANS3NVMeController/NS_01@1/IOBlockStorageDriver/APPLE SSD AP0512Q Media/IOGUIDPartitionScheme/iBootSystemContainer@1/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Hardware@3", false, {}); + disk("/dev/disk1s4", "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/ans@77400000/AppleASCWrapV4/iop-ans-nub/RTBuddyV2/RTBuddyService/AppleANS3NVMeController/NS_01@1/IOBlockStorageDriver/APPLE SSD AP0512Q Media/IOGUIDPartitionScheme/iBootSystemContainer@1/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Recovery@4", false, {}); + disk("/dev/disk2s1", "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/ans@77400000/AppleASCWrapV4/iop-ans-nub/RTBuddyV2/RTBuddyService/AppleANS3NVMeController/NS_01@1/IOBlockStorageDriver/APPLE SSD AP0512Q Media/IOGUIDPartitionScheme/RecoveryOSContainer@3/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Recovery@1", false, {}); + disk("/dev/disk2s2", "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/ans@77400000/AppleASCWrapV4/iop-ans-nub/RTBuddyV2/RTBuddyService/AppleANS3NVMeController/NS_01@1/IOBlockStorageDriver/APPLE SSD AP0512Q Media/IOGUIDPartitionScheme/RecoveryOSContainer@3/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Update@2", false, {}); + disk("/dev/disk3s1", "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/ans@77400000/AppleASCWrapV4/iop-ans-nub/RTBuddyV2/RTBuddyService/AppleANS3NVMeController/NS_01@1/IOBlockStorageDriver/APPLE SSD AP0512Q Media/IOGUIDPartitionScheme/Container@2/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Macintosh HD@1", false, {}); + disk("/dev/disk3s4", "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/ans@77400000/AppleASCWrapV4/iop-ans-nub/RTBuddyV2/RTBuddyService/AppleANS3NVMeController/NS_01@1/IOBlockStorageDriver/APPLE SSD AP0512Q Media/IOGUIDPartitionScheme/Container@2/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Update@4", false, {"/System/Volumes/Update"}); + disk("/dev/disk3s5", "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/ans@77400000/AppleASCWrapV4/iop-ans-nub/RTBuddyV2/RTBuddyService/AppleANS3NVMeController/NS_01@1/IOBlockStorageDriver/APPLE SSD AP0512Q Media/IOGUIDPartitionScheme/Container@2/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Data@5", false, {"/System/Volumes/Data"}); + disk("/dev/disk3s2", "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/ans@77400000/AppleASCWrapV4/iop-ans-nub/RTBuddyV2/RTBuddyService/AppleANS3NVMeController/NS_01@1/IOBlockStorageDriver/APPLE SSD AP0512Q Media/IOGUIDPartitionScheme/Container@2/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Preboot@2", false, {"/System/Volumes/Preboot"}); + disk("/dev/disk3s3", "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/ans@77400000/AppleASCWrapV4/iop-ans-nub/RTBuddyV2/RTBuddyService/AppleANS3NVMeController/NS_01@1/IOBlockStorageDriver/APPLE SSD AP0512Q Media/IOGUIDPartitionScheme/Container@2/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Recovery@3", false, {}); + disk("/dev/disk3s6", "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/ans@77400000/AppleASCWrapV4/iop-ans-nub/RTBuddyV2/RTBuddyService/AppleANS3NVMeController/NS_01@1/IOBlockStorageDriver/APPLE SSD AP0512Q Media/IOGUIDPartitionScheme/Container@2/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/VM@6", false, {"/System/Volumes/VM"}); + disk("/dev/disk3s1s1", "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/ans@77400000/AppleASCWrapV4/iop-ans-nub/RTBuddyV2/RTBuddyService/AppleANS3NVMeController/NS_01@1/IOBlockStorageDriver/APPLE SSD AP0512Q Media/IOGUIDPartitionScheme/Container@2/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Macintosh HD@1/com.apple.os.update-EA882DCA7A28EBA0A6E94689836BB10D77D84D1AEE2468E17775A447AA815278@1", false, {"/"}); + + vector writeable = find_unmounted (disks); + BOOST_REQUIRE_EQUAL (writeable.size(), 1U); + BOOST_CHECK_EQUAL (writeable[0].device(), "/dev/disk4"); + + disks.clear (); + disk("/dev/disk4s1", "IOService:/IOResources/IOHDIXController/IOHDIXHDDriveOutKernel@0/IODiskImageBlockStorageDeviceOutKernel/IOBlockStorageDriver/Apple UDIF read-only compressed (zlib) Media/IOGUIDPartitionScheme/disk image@1", false, {}); + disk("/dev/disk4", "IOService:/IOResources/IOHDIXController/IOHDIXHDDriveOutKernel@0/IODiskImageBlockStorageDeviceOutKernel/IOBlockStorageDriver/Apple UDIF read-only compressed (zlib) Media", true, {}); + disk("/dev/disk3s1", "IODeviceTree:/PCI0@0/XHC1@14/@2:1", false, {}); + disk("/dev/disk3", "IODeviceTree:/PCI0@0/XHC1@14/@2:0", true, {}); + disk("/dev/disk0", "IODeviceTree:/PCI0@0/SATA@1F,2/PRT1@1/PMP@0/@0:0", true, {}); + disk("/dev/disk0s1", "IODeviceTree:/PCI0@0/SATA@1F,2/PRT1@1/PMP@0/@0:1", false, {}); + disk("/dev/disk0s2", "IODeviceTree:/PCI0@0/SATA@1F,2/PRT1@1/PMP@0/@0:2", false, {"/Volumes/Macintosh HD"}); + disk("/dev/disk0s3", "IODeviceTree:/PCI0@0/SATA@1F,2/PRT1@1/PMP@0/@0:3", false, {}); + disk("/dev/disk0s4", "IODeviceTree:/PCI0@0/SATA@1F,2/PRT1@1/PMP@0/@0:4", false, {}); + disk("/dev/disk0s5", "IODeviceTree:/PCI0@0/SATA@1F,2/PRT1@1/PMP@0/@0:5", false, {"/Volumes/High Sierra"}); + disk("/dev/disk0s6", "IODeviceTree:/PCI0@0/SATA@1F,2/PRT1@1/PMP@0/@0:6", false, {}); + disk("/dev/disk0s7", "IODeviceTree:/PCI0@0/SATA@1F,2/PRT1@1/PMP@0/@0:7", false, {"/Volumes/Recovery HD"}); + disk("/dev/disk1", "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/SATA@1F,2/AppleIntelPchSeriesAHCI/PRT1@1/IOAHCIDevice@0/AppleAHCIDiskDriver/IOAHCIBlockStorageDevice/IOBlockStorageDriver/APPLE HDD ST500LM012 Media/IOGUIDPartitionScheme/Untitled 3@3/AppleAPFSContainerScheme/AppleAPFSMedia", true, {}); + disk("/dev/disk", "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/SATA@1F,2/AppleIntelPchSeriesAHCI/PRT1@1/IOAHCIDevice@0/AppleAHCIDiskDriver/IOAHCIBlockStorageDevice/IOBlockStorageDriver/APPLE HDD ST500LM012 Media/IOGUIDPartitionScheme/Untitled 4@4/AppleAPFSContainerScheme/AppleAPFSMedia", true, {}); + disk("/dev/disk1s1", "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/SATA@1F,2/AppleIntelPchSeriesAHCI/PRT1@1/IOAHCIDevice@0/AppleAHCIDiskDriver/IOAHCIBlockStorageDevice/IOBlockStorageDriver/APPLE HDD ST500LM012 Media/IOGUIDPartitionScheme/Untitled 3@3/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Untitled - Data@1", false, {"/Volumes/Untitled - Data"}); + disk("/dev/disk1s2", "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/SATA@1F,2/AppleIntelPchSeriesAHCI/PRT1@1/IOAHCIDevice@0/AppleAHCIDiskDriver/IOAHCIBlockStorageDevice/IOBlockStorageDriver/APPLE HDD ST500LM012 Media/IOGUIDPartitionScheme/Untitled 3@3/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Preboot@2", false, {}); + disk("/dev/disk1s3", "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/SATA@1F,2/AppleIntelPchSeriesAHCI/PRT1@1/IOAHCIDevice@0/AppleAHCIDiskDriver/IOAHCIBlockStorageDevice/IOBlockStorageDriver/APPLE HDD ST500LM012 Media/IOGUIDPartitionScheme/Untitled 3@3/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Recovery@3", false, {}); + disk("/dev/disk1s4", "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/SATA@1F,2/AppleIntelPchSeriesAHCI/PRT1@1/IOAHCIDevice@0/AppleAHCIDiskDriver/IOAHCIBlockStorageDevice/IOBlockStorageDriver/APPLE HDD ST500LM012 Media/IOGUIDPartitionScheme/Untitled 3@3/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/VM@4", false, {}); + disk("/dev/disk1s5", "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/SATA@1F,2/AppleIntelPchSeriesAHCI/PRT1@1/IOAHCIDevice@0/AppleAHCIDiskDriver/IOAHCIBlockStorageDevice/IOBlockStorageDriver/APPLE HDD ST500LM012 Media/IOGUIDPartitionScheme/Untitled 3@3/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Untitled@5", false, {"/Volumes/Untitled"}); + disk("/dev/disk2s1", "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/SATA@1F,2/AppleIntelPchSeriesAHCI/PRT1@1/IOAHCIDevice@0/AppleAHCIDiskDriver/IOAHCIBlockStorageDevice/IOBlockStorageDriver/APPLE HDD ST500LM012 Media/IOGUIDPartitionScheme/Untitled 4@4/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Catalina - Data@1", false, {}); + disk("/dev/disk2s2", "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/SATA@1F,2/AppleIntelPchSeriesAHCI/PRT1@1/IOAHCIDevice@0/AppleAHCIDiskDriver/IOAHCIBlockStorageDevice/IOBlockStorageDriver/APPLE HDD ST500LM012 Media/IOGUIDPartitionScheme/Untitled 4@4/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Preboot@2", false, {}); + disk("/dev/disk2s3", "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/SATA@1F,2/AppleIntelPchSeriesAHCI/PRT1@1/IOAHCIDevice@0/AppleAHCIDiskDriver/IOAHCIBlockStorageDevice/IOBlockStorageDriver/APPLE HDD ST500LM012 Media/IOGUIDPartitionScheme/Untitled 4@4/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Recovery@3", false, {}); + disk("/dev/disk2s4", "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/SATA@1F,2/AppleIntelPchSeriesAHCI/PRT1@1/IOAHCIDevice@0/AppleAHCIDiskDriver/IOAHCIBlockStorageDevice/IOBlockStorageDriver/APPLE HDD ST500LM012 Media/IOGUIDPartitionScheme/Untitled 4@4/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/VM@4", false, {"/private/var/vm"}); + disk("/dev/disk2s5", "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/SATA@1F,2/AppleIntelPchSeriesAHCI/PRT1@1/IOAHCIDevice@0/AppleAHCIDiskDriver/IOAHCIBlockStorageDevice/IOBlockStorageDriver/APPLE HDD ST500LM012 Media/IOGUIDPartitionScheme/Untitled 4@4/AppleAPFSContainerScheme/AppleAPFSMedia/AppleAPFSContainer/Catalina@5", false, {"/"}); + + writeable = find_unmounted (disks); + BOOST_REQUIRE_EQUAL (writeable.size(), 1U); + BOOST_CHECK_EQUAL (writeable[0].device(), "/dev/disk3"); +} +