如何将 Mac OS X 上卷的 UUID 更改为指定值?

bow*_*ers 7 hfs uuid c logical-drive macos

这类似于这里提出的问题:

如何在 Mac OS X 10.6 上更改卷的 UUID?

唯一的区别是我想将其更改为特定值,而不是随机值。hfs.util 似乎只是随机的。

我考虑修改hfs.util 源以允许我指定值。当我在代码中寻找从哪里开始进行更改时,我想起了为什么 C不是我最喜欢的语言。后来几次编译错误和段错误,我失去了尝试修改这个工具的热情。我愿意在休息后再次尝试,但我认为必须有一种更简单的方法来更改我只是不知道的卷的 UUID。

所以在我浪费时间之前,有没有人知道一种简单的方法来做到这一点?或者有没有 C 专家愿意加入我的努力,让 hfs.util 将 UUID 更改为指定值?

以下是我为了能够从源 OS X 10.6.8 编译该工具所做的更改:

hfsutil_jnl.c:

47: #include <hfs_fsctl.h>
Run Code Online (Sandbox Code Playgroud)

hfsutil_main.c:

80: #include <uuid/uuid.h>
81: /* REMOVED */
Run Code Online (Sandbox Code Playgroud)

并且,正如本文所暗示的,将fs.c 中第 166 行的以下内容添加到 hfsutil_main.c(因为 namespace.h 不在系统中的任何位置):

static unsigned char kFSUUIDNamespaceSHA1[] = {0xB3,0xE2,0x0F,0x39,0xF2,0x92,0x11,0xD6,0x97,0xA4,0x00,0x30,0x65,0x43,0xEC,0xAC};
Run Code Online (Sandbox Code Playgroud)

最后,我抓取了这个文件并将其添加到工作目录http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/hfs/hfs_fsctl.h

chr*_*riv 7

我解决了这个问题。我下载了 的源代码hfs.util,进行了您提到的更改(以便它可以编译),重新启用 UUID 功能(例如在当前源代码中禁用的 -s),并添加了一个新命令(-S 用于指定一个 UUID)。

新命令的格式是这样的:

sudo hfs.util -S disk0s2 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Run Code Online (Sandbox Code Playgroud)

您必须使用 sudo 或以 root 身份运行,但您不必卸载该卷(即使它是您当前的系统卷)。

但是,我仍在调试它(我对 C 很糟糕)。我可以无误地运行它,但它仍然有效地使用 -S 随机化新的 UUID。不管我指定的 UUID 是什么,我仍然得到一个不同的。

如果我让它工作,我会在这里发布一个差异,或者我会找到某个地方来上传修改后的源代码,并从这里链接到它。

如果我失败了,我可能仍然会发布差异,也许其他人(更擅长 C 的人)可以修复它。

编辑:好的。我在 Apple 源代码中发现了处理 UUID 字符串 <--> 二进制函数的函数的一个主要错误hfs.util。作为二进制值,需要 4 个(无符号)32 位字来存储 128 位 UUID。Apple 源似乎只处理 16 个十六进制字符(它应该是 16 个八位组,每个 2 个十六进制字符)。

它似乎还使用有缺陷的结构来保存 UUID(它似乎只存储一个“高”32 位字和一个“低”32 位字,但需要 4 个(无符号)32 位字)。我可以认为我可以修复它,但我必须更改结构和使用该结构的所有功能(因此可能需要一段时间)。

编辑 #2:在花费太多时间调试 hfs.util 的 Apple 源代码(我真的不是 C 程序员)之后,Apple 似乎在处理 UUID 时故意只处理 64 位。其余部分由 64 位常量、计算机组件值、当前时间等的散列生成。因此,如果您两次使用相同的 64 位“密钥”,最终仍会得到两个不同的 UUID(即使您使用同一台计算机,时间仍然会发生变化)。只要对卷标头的实际写入发送实际的 UUID(而不仅仅是一个键),并且只要卷标头存储了实际的 UUID(而不仅仅是一个键和/或可用于计算的值) UUID),那么就有希望了。我的调试还没有那么远。当我知道更多时,我会再次发布。


Ana*_*ile 7

我还没有研究过 hfs.util 的源代码,现在对你有用可能为时已晚,但我想我可以贡献一些有用的东西。

用于 HFS+ 卷的 UUID 似乎是 UUID 规范涵盖的所有变体,并且属于版本 3 类型,即命名空间和名称通过 MD5 转换为 UUID(请参阅维基百科上的详细信息)。

实际的磁盘标识符(代替规范中的名称)似乎很可能只是 64 位,根据规范通过在 Apple 用于卷标识符的任何命名空间的 UUID 前面加上,然后应用,转换为 128 位的 UUID一个 MD5 哈希值。

这不涉及计算机组件值、当前时间等。这些用于其他类型的 UUID。然而,它确实涉及一个“命名空间”UUID(以识别我们正在“命名”磁盘卷的事实),然后是一个“名称”(磁盘的实际标识符)。

让我这么想的一件事不仅是@chriv 的声明,即代码似乎只使用 64 位,而且还有 SuperDuper 附带的“秘密”实用程序处理 UUID 的方式!

超级超级!Mac OS X 的备份实用程序有一个“隐藏的”命令行工具,可以让您检索和设置卷 UUID。但它既检索又将其设置为 64 位序列(以十六进制表示)。这些位似乎与 Apple 磁盘实用程序报告的实际值大不相同。

有关更多信息,请参阅:

http://www.shirt-pocket.com/forums/archive/index.php/t-1186.html

http://www.shirt-pocket.com/forums/archive/index.php/t-6173.html

注意:请通读这些支持讨论,因为似乎存在一些问题,例如有时需要重新启动。


更新

我看过苹果的消息来源。我确认我上面写的。磁盘上保存的是卷的 64 位标识符(通过获取一组伪随机数据的 SHA1 哈希值的前 64 位随机生成:正常运行时间、启动时间、主机 ID、主机名、内核版本字符串、内核版本字符串、平均负载、VM 统计信息和当前时间)。

在版本 3 UUID 用语中,这是一个“名称”。所以磁盘上保存的是卷的 64 位“名称”,而不是 UUID。

工具报告的 128 位 UUID不会保存,出于显示目的,每次都会从“名称”和“命名空间”(命名空间是固定的,并且是 OP 必须手动添加的 kFSUUIDNamespaceSHA1 常量)进行计算源,因为缺少包含它的标头:它代表卷“名称”的“命名空间”,这是实际保存在卷上以识别它们的 64 位事物)。

从“名称”到 UUID 很容易(基本上你应用了版本 3 UUID 的标准算法),但从 UUID 到“名称”基本上是不可能的。换句话说,OP 的答案是:如果您知道卷的“名称”(例如,如果您想将备份恢复到新磁盘并且您已经保存了它的名称和数据),则有可能,但如果您只知道 UUID,则不会。正确设置名称产生预期的 UUID,但您需要名称并且无法从 UUID 计算它。


关于 Apple 代码的注释(阅读这些并查看代码,一切都会变得清晰):

正如我所写的,磁盘上只有“名称”。UUID 仅用于可视化计算,使用版本 3 算法(“命名空间”中“名称”的 UUID)。

  • kFSUUIDNamespaceSHA1是“命名空间”常量,如上所述。
  • uuid_create_md5_from_name是第 3 版 UUID 算法,它计算给定“命名空间”和“名称”的第 3 版 UUID。
  • GenerateVolumeUUID生成一个新的随机“名称”(注意:只是“名称”,而不是 UUID,尽管有函数名称)。

根据卷当前是否已安装,设置和获取磁盘的“名称”是不同的。“名称”存储在卷的“查找器信息”中。可以使用getattrlistsetattrlist获取和设置已挂载卷的“Finder Info”数据,但如果未挂载卷,它们会直接访问卷数据(毕竟这是 unix,而未挂载的卷是一个块可以作为文件访问的设备)。

  • SetVolumeUUIDSetVolumeUUIDRawSetVolumeUUIDAttrGetVolumeUUIDGetVolumeUUIDrawGetVolumeUUIDAttr读取/写入“名称”(同样,尽管它们的名称,它们只处理卷的“名称”,而不是 UUID)。*Raw 函数通过设备“文件”处理卸载卷的直接访问,*Attr 函数使用 get/setattrlist API。普通的检查卷是否已安装并调用适当的 *Raw/*Attr 版本。

然后是实现工具功能的“高级”功能:

  • DoGetUUIDKey获取“名称”,调整字节序,然后计算用于显示的 UUID。
  • DoChangeUUIDKey创建一个新的随机“名称”并将其写入卷。

所以你能做的最好的事情就是重新编码嵌入在 Shirt Pocket 的 SuperDuper 中的小命令行工具的相同功能!(请参阅我上面发布的链接)。