在 macOS Catalina、Big Sur 和更高版本中确定卷组

Posted

技术标签:

【中文标题】在 macOS Catalina、Big Sur 和更高版本中确定卷组【英文标题】:Determine Volume Groups in macOS Catalina, Big Sur and later 【发布时间】:2020-12-31 17:13:28 【问题描述】:

从 macOS 10.15 (Catalina) 开始,用户看到的卷实际上可能由多个卷组成,例如系统卷和数据卷。

我正在编写一个需要单独识别这些卷的工具,因为当使用特定的文件操作时,例如searchfsfts_read,它们不会跨越这样的卷边界,我需要了解哪些卷属于一起,这样,当用户想要搜索系统卷时,我知道在文件操作中同时包含“系统”和它的“数据”卷。

我如何安全地确定哪些卷属于一起?

单独使用[NSFileManager mountedVolumeURLsIncludingResourceValuesForKeys:options:] 并没有多大帮助,因为它不会包含/System/Volumes/Data 处的根系统数据卷(但可能包括隐藏的/System/Volumes/Data/home 卷)。使用df等命令行工具也是如此。

我需要考虑当前未启动的其他系统卷。例如,如果我同时拥有 BigSur 和 Catalina 系统,并且从前者启动,我希望能够识别这四个卷:

/                              BigSur System volume
/System/Volumes/Data           BigSur Data volume
/Volumes/Catalina              Catalina System volume
/Volumes/Catalina - Daten      Catalina Data volume (created on a German system)

如何判断包含“Catalina”的两卷实际上属于同一组?我不喜欢用部分名称来匹配它们,因为这对我来说似乎相当随机且不可靠。如果数据量不是在英文系统上创建的,那么数据量甚至没有在名称中使用“Data”这一事实使得这已经很难做到。

是否有其他卷属性可以帮助识别这些卷组?

【问题讨论】:

【参考方案1】:

Mike Bombich 为我提供了这个解决方案:

您可以从 IOKit 获取卷 UUID 和卷组 UUID。同一组中的两个卷将具有相同的组 UUID。请注意,组 UUID 始终与数据卷的 UUID 相同(至少在实践中)。

这是获取已安装卷列表的代码,包括属于卷组的隐藏卷:

- (void)listVolumes

    NSArray<NSURL*> *vols = [NSFileManager.defaultManager mountedVolumeURLsIncludingResourceValuesForKeys:nil options: 0 ];
    vols = [vols arrayByAddingObject:[NSURL fileURLWithPath:@"/System/Volumes/Data"]]; // the root's Data vol isn't added by default
    NSMutableArray<NSString*> *lines = [NSMutableArray new];
    for (NSURL *vol in vols) 
        NSDictionary *d = [vol resourceValuesForKeys:@[
            NSURLVolumeIsBrowsableKey,
            NSURLVolumeIsRootFileSystemKey,
            NSURLVolumeIdentifierKey,
            NSURLVolumeNameKey
        ] error:nil];

        struct statfs fsinfo;
        statfs(vol.path.UTF8String, &fsinfo);
        NSString *bsdName = [NSString stringWithUTF8String:fsinfo.f_mntfromname];
        bsdName = [bsdName lastPathComponent];

        [lines addObject:[NSString stringWithFormat:@"%@, %@, %@, %@", bsdName, vol.path, d[NSURLVolumeIsBrowsableKey], d[NSURLVolumeNameKey]]];
    
    NSLog(@"\n%@", [lines componentsJoinedByString:@"\n"]);

以及列出卷组 ID 及其角色的代码:

- (void)listGroupIDs

    io_iterator_t iterator; io_object_t obj;
    ioserviceGetMatchingServices (kIOMasterPortDefault, IOServiceMatching("IOMediaBSDClient"), &iterator);
    while ((obj = IOIteratorNext (iterator)) != 0) 
        io_object_t obj2;
        IORegistryEntryGetParentEntry (obj, kIOServicePlane, &obj2);
        NSString *bsdName = CFBridgingRelease(IORegistryEntryCreateCFProperty(obj2, CFSTR("BSD Name"), kCFAllocatorDefault, 0));
        //NSString *volID = CFBridgingRelease(IORegistryEntryCreateCFProperty(obj2, CFSTR("UUID"), kCFAllocatorDefault, 0));
        NSString *groupID = CFBridgingRelease(IORegistryEntryCreateCFProperty(obj2, CFSTR("VolGroupUUID"), kCFAllocatorDefault, 0));
        NSArray *roles = CFBridgingRelease(IORegistryEntryCreateCFProperty(obj2, CFSTR("Role"), kCFAllocatorDefault, 0));
        if (groupID != nil && ![groupID isEqualToString:@"00000000-0000-0000-0000-000000000000"]) 
            NSLog(@"%@: %@, %@", bsdName, groupID, roles);
        
    

有了这两个信息,来自 IOKit 的卷可以通过它们的 BSD 名称匹配到 NSURL。

但是,还有一种特殊情况:在 macOS Big Sur 上,根系统的设备不是常规的“diskXsY”,而是一个快照设备,例如“diskXsYsZ”。虽然 IOKit 代码也列出了这一点,但它的条目缺少角色信息。

这是一个来自 Mac 的示例输出,其中包含 Big Sur 和 Catalina 系统,如问题所示(为​​便于阅读,稍作编辑):

disk3s1s1, /, 1, BigSur
disk3s5,   /System/Volumes/VM, 0, VM
disk3s3,   /System/Volumes/Preboot, 0, Preboot
disk3s6,   /System/Volumes/Update, 0, Update
disk4s1,   /Volumes/Catalina - Daten, 0, Catalina - Daten
disk4s2,   /Volumes/Catalina, 1, Catalina
disk3s2,   /System/Volumes/Data, 1, BigSur

disk4s1:   18464FE4-8321-4D36-B87A-53AC38EF6AEF, 18464FE4-8321-4D36-B87A-53AC38EF6AEF, ("Data")
disk3s1:   86812DBD-9252-4A2E-8887-752418DECE13, 058517A6-48DD-46AB-8A78-C1F115AE6E13, ("System")
disk4s2:   51DEC6AC-2D68-4B60-AE23-74BCA2C3A484, 18464FE4-8321-4D36-B87A-53AC38EF6AEF, ("System")
disk3s2:   058517A6-48DD-46AB-8A78-C1F115AE6E13, 058517A6-48DD-46AB-8A78-C1F115AE6E13, ("Data")
disk3s1s1: C26440B0-0207-4227-A4B1-EBDD62C90D24, 058517A6-48DD-46AB-8A78-C1F115AE6E13, (null)

我发布了一个工作代码示例,它确定所有已安装的卷及其组关系。完整的可编译代码(您可以在新的 Obj-C App 项目的 AppDelegate.m 文件中替换它:https://gist.github.com/tempelmann/80efc2eb84f0171a96822290dee7d8d9

【讨论】:

【参考方案2】:

以下 shell 命令列出卷组:

diskutil apfs listVolumeGroups

对于通过代码进行解析,可以将选项附加到输出为 plist,然后可以使用 CFPropertyListCreateWithDataformat:kCFPropertyListXMLFormat_v1_0 导入:

diskutil apfs listVolumeGroups -plist

(tweet byHoward Oakley 中提供的答案)

【讨论】:

【参考方案3】:

使用diskutil list 检查卷及其名称。 system_profiler 显示很多信息,但您必须解析卷信息的输出,使用 system_profiler -xml &gt; output.xml 将创建一个文件以了解特定查询的可用值。

【讨论】:

您建议如何从“diskutil 列表”中识别卷组?它们在同一个 diskX 上这一事实是不够的,因为一个 APFS 容器中可以有多个系统。

以上是关于在 macOS Catalina、Big Sur 和更高版本中确定卷组的主要内容,如果未能解决你的问题,请参考以下文章

直观对比:macOS Big Sur较Catalina迎来了多大程度上的UI设计改进?

Big Sur 内置的 CMake MacOS 应用程序无法在 Catalina 上运行

苹果修复macOS 12.3.1 Monterey漏洞,但Big Sur/Catalina未修复

手贱升 macOS Big Sur 后,该如何降级?

latexindent:自 macOS 11 Big Sur 以来安装 File::HomeDir、Mac::SystemDirectory 时遇到问题

黑苹果:Big Sur更换DW1820A网卡