Как изменить UUID тома в Mac OS X на значение SPECIFIED?

5847
bowmasters

Это похоже на вопрос, заданный здесь:

Как изменить UUID тома в Mac OS X 10.6?

Разница лишь в том, что я хочу изменить его на конкретное значение, а не на случайное. Hfs.util только кажется случайным.

Я подумал об изменении источника hfs.util, чтобы позволить мне указывать значения. Пока я искал код и искал, с чего начать вносить изменения, я вспомнил, почему C не мой любимый язык. Несколько ошибок компиляции и ошибки позже, я потерял энтузиазм, пытаясь изменить этот инструмент. Я готов снова попробовать это после того, как немного отдохну, но я думаю, что должен быть более простой способ изменить UUID тома, о котором я просто не знаю.

Итак, прежде чем я потрачу больше времени, кто-нибудь знает простой способ сделать это? Или кто-нибудь из экспертов C хотел бы присоединиться к моим усилиям, чтобы hfs.util изменил UUID на указанное значение?

Вот изменения, которые я сделал, чтобы иметь возможность скомпилировать инструмент из исходной версии OS X 10.6.8:

hfsutil_jnl.c:

47: #include <hfs_fsctl.h> 

hfsutil_main.c:

80: #include <uuid/uuid.h> 81: /* REMOVED */ 

И, как указывалось в этой статье, добавлено следующее из строки 166 в файле fs.c в файл hfsutil_main.c (поскольку namespace.h нет в системе):

static unsigned char kFSUUIDNamespaceSHA1[] = ; 

Наконец, я взял этот файл и добавил его в рабочий каталог http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/hfs/hfs_fsctl.h

6
Я добавил большое обновление к своему ответу. Если вы или @chriv (или кто-либо еще) все еще заинтересованы и / или у вас есть сомнения, не стесняйтесь спрашивать. Но действительно не должно быть намного больше, чтобы добавить. С моим объяснением и вашими ссылками на код все должно быть довольно ясно. Analog File 9 лет назад 1

2 ответа на вопрос

6
chriv

I tackled this. I downloaded the source for hfs.util, made the changes you mentioned (so that it could compile), re-enabled the UUID functionality (like -s which is disabled in the current source), and added a new command (-S which is used to specify a UUID).

The format for the new command is this:

sudo hfs.util -S disk0s2 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 

You have to use sudo or run as root, but you DO NOT have to dismount the volume (even if it is your current system volume).

However, I’m still debugging it (I'm terrible with C). I can run it error-free, but it is still effectively randomizing the new UUID with -S. Regardless of the UUID I specify, I'm still getting a different one.

If I get it working, I’ll either post a diff here, or I’ll find someplace to upload the modified source, and link to it from here.

If I fail, I’ll probably still post the diff, and maybe someone else (who is better with C) can fix it.

EDIT: OK. I found what appears to be a major bug with functions that handle UUID string <--> binary functions in the Apple source code for hfs.util. As a binary value, it takes 4 (unsigned) 32-bit words to store a 128-bit UUID. The Apple source only appears to only process 16 hexadecimal characters (it should be 16 octets of 2 hexadecimal characters each).

It also appears to use a defective structure to hold the UUID (it appears to only store a “high” 32-bit word and a “low” 32-bit word, but 4 (unsigned) 32-bit words would be needed). I can think I can fix it, but I have to change the structure and all the functions that use the structure (so it may take me a while).

EDIT #2: After spending way too much time debugging the Apple source for hfs.util (I'm really not a C programmer) it appears that Apple intentionally only handles 64-bits when handling a UUID. The rest is generated from a hash of a 64-bit constant, computer component values, the current time, and so on. So if you used the same 64-bit “key” twice, you would still end up with two different UUIDs (even if you used the same computer, the time has still changed). As long as the actual write to the volume header sends the actual UUID (and not just a key), and as long as the volume header stores the actual UUID (and not just a key and/or values that can be used to compute the UUID), then there is hope. My debugging hasn't made it that far yet. I’ll post again when I know more.

6
Analog File

I havent looked into the source code of hfs.util and it is probably too late to be useful for you, but I think I can contribute something useful.

The UUIDs used for HFS+ volumes seem to be all of the variant covered by the UUID specification and be of the version 3 type, that is a namespace and a name converted to an UUID via MD5 (see details on wikipedia).

It seems likely that the actual disk identifier (taking the place of the name in the specification) is just 64 bits, converted into a 128 bits UUID according to the specification by prepending the UUID of whatever namespace Apple is using for volume identifiers and then applying an MD5 hash.

This does not involve computer component values, current time etc. Those are used for other kinds of UUIDs. However it does involve a "namespace" UUID (to identify the fact that we are "naming" a disk volume) and then a "name" (the actual identifier of the disk).

One thing that makes me think so is not only @chriv's statement that it the code seems to only use 64 bits but also the way the UUIDs are handled by the "secret" utility that comes with SuperDuper!

The SuperDuper! backup utility for Mac OS X has a "hidden" command line tool that would let you retrieve and set a volume UUID. But it both retrieves and sets it as a sequence of 64 bits (expressed in hex). And it seems those bits are quite different than the actual values reported by Apple disk utilities.

For more information see:

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

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

Note: read those support discussions all the way through as it seems there are some gotchas, like sometimes a need to reboot.


Update

I have glanced at Apple sources. I confirm what I wrote above. What is saved on disk is a 64bits identifier for the volume (which is generated randomly by taking the first 64 bits of the SHA1 hash of a set of pseudo random bits of data: uptime, boot time, host id, host name, kernel release string, kernel version string, load average, VM statistics and current time).

In Version 3 UUID parlance this is a "name". So what is saved on the disk is a 64bit "name" of the volume, not the UUID.

The 128bits UUID that is reported by the tools is not saved, it is computed each time, for display purposes, from the "name" and the "namespace" (the namespace is fixed and is that kFSUUIDNamespaceSHA1 constant the OP had to manually add to the source because the header containing it is missing: it represent the "namespace" for volume "names" which are the 64bit things that are actually saved on the volumes to identify them).

It's easy to go from the "name" to the UUID (basically you apply the standard algorithm for Version 3 UUIDs) but it's basically impossible to go back from the UUID to the "name". In other words the answer to the OP is: it's possible if you know the "name" of the volume (for example if you want to restore a backup to a new disk AND you have saved the name of it as well as the data), but not if you only know the UUID. Setting the name correctly will result in the expected UUID, but you need the name and cannot compute it from the UUID.


Notes on Apple's code (read these and look at the code and everything becomes clear):

As I wrote, all there is on disk is the "name". The UUID is computed only for visualization, using the Version 3 algorithm (an UUID for a "name" in a "namespace").

  • kFSUUIDNamespaceSHA1 is the "namespace" constant, as explained above.
  • uuid_create_md5_from_name is the Version 3 UUID algorithm that computes a Version 3 UUID given a "namespace" and a "name".
  • GenerateVolumeUUID generates a new random "name" (note: just the "name", not the UUID, despite the name of the function).

Setting and getting the "name" to disk is different depending whether the volume is currently mounted. The "name" is stored in the "Finder Info" of the volume. Getting and setting the "Finder Info" data for a mounted volume can be done with getattrlist and setattrlist but if the volume is not mounted they resort to accessing the volume data directly (this is unix, after all, and an unmounted volume is a block device which is accessible, by root, as a file).

  • SetVolumeUUID, SetVolumeUUIDRaw, SetVolumeUUIDAttr, GetVolumeUUID, GetVolumeUUIDRaw, GetVolumeUUIDAttr do read/write the "name" (again, despite their name, they only handle the "name" of the volume, not the UUID). The *Raw functions handle direct access via the device "file" for unmounted volumes, the *Attr ones use the get/setattrlist API. The plain ones check if the volume is mounted and call the appropriate *Raw/*Attr version.

Then there are the "high level" functions that implement the functionality of the tool:

  • DoGetUUIDKey gets the "name", adjusts for endianness, then computes the UUID for display.
  • DoChangeUUIDKey creates a new, random, "name" and writes it to the volume.

So the best you can do is re-code the same functionality of the little command line tool embedded in Shirt Pocket's SuperDuper! (see the links I posted above).