Хорошо, я решил эту проблему сам, но это было неоправданно сложно.
Путь к родителю жестко закодирован внутри .vhd в нескольких различных форматах; в моем случае как относительные, так и абсолютные пути, как в ASCII, так и в UTF-16, для 4 полных путей. Вы можете получить спецификацию формата файла .vhd от Microsoft .
Но быстрое сканирование со строками или аналогичная утилита (я использую Cygwin здесь), которая ищет строковые данные, может найти эти смещения легче, чем обход структур VHD:
$ strings -t x Windows\ XP\ Mode-X.vhd | head -200 | grep vhd
40000 ..\..\..\..\..\..\..\other\xp-mode\Windows XP Mode base.vhd
40200 C:\other\xp-mode\Windows XP Mode base.vhd
$ strings -t x -e l Windows\ XP\ Mode-X.vhd | head -200 | grep vhd
40400 ..\..\..\..\..\..\..\other\xp-mode\Windows XP Mode base.vhd
40600 C:\other\xp-mode\Windows XP Mode base.vhd
Эти строки хранятся с большим количеством пустых байтов; Я использовал шестнадцатеричный редактор, чтобы обновить пути к новым местам.
Но это не сработало; пути были показаны усеченными в редакторе настроек Virtual PC. Длина строки кодируется в другом месте; в моем случае 0x4AB, 0x4C3 и т. д. (я выполнил шестнадцатеричный поиск старых путей). Я обновил эти длины, но тогда редактор VHD считал недействительным VHD. Я решил, что теперь включена контрольная сумма, скачал спецификацию VHD, установил средство просмотра документов Word, чтобы я мог прочитать .doc, и затем я написал программу ниже. Его вывод, когда передается путь к VHD, является обновленной контрольной суммой и местоположением контрольной суммы; пример вывода:
opening Windows XP Mode-X.vhd
dynamic header seems to be at 0x200
new checksum: ffffda30 at 0x224
Ввод этой новой контрольной суммы с помощью шестнадцатеричного редактора заставил VHD работать, и старый режим XP успешно загрузился.
Программа расчета контрольной суммы:
#include <fcntl.h>
#include <stdio.h>
#include <io.h>
#include <stdlib.h>
#include <unistd.h>
const int checksum_offset = 36;
long long flip_endian_ll(long long x)
{
return ((x & 0x00000000000000FFULL) << 56)
| ((x & 0x000000000000FF00ULL) << 40)
| ((x & 0x0000000000FF0000ULL) << 24)
| ((x & 0x00000000FF000000ULL) << 8)
| ((x & 0x000000FF00000000ULL) >> 8)
| ((x & 0x0000FF0000000000ULL) >> 24)
| ((x & 0x00FF000000000000ULL) >> 40)
| ((x & 0xFF00000000000000ULL) >> 56);
}
int die(const char *msg)
{
fprintf(stderr, "error: %s\n", msg);
exit(1);
}
int main(int argc, const char *const *args)
{
int f, r;
long long dyn_ofs;
unsigned char dyn_header[1024];
if (argc != 2)
{
fprintf(stderr, "expected: .vhd argument\n");
return 1;
}
printf("opening %s\n", args[1]);
f = open(args[1], O_RDONLY | O_BINARY);
if (f < 0)
die("failed open");
r = lseek(f, 0x10, SEEK_SET);
if (r < 0)
die("seek failed");
r = read(f, &dyn_ofs, sizeof(dyn_ofs));
if (r != 8)
die("failed read");
dyn_ofs = flip_endian_ll(dyn_ofs);
printf("dynamic header seems to be at 0x%llx\n", dyn_ofs);
r = lseek(f, dyn_ofs, SEEK_SET);
if (r < 0)
die("seek failed");
r = read(f, dyn_header, sizeof(dyn_header));
if (r != sizeof(dyn_header))
die("read of dynamic header failed");
if (memcmp("cxsparse", dyn_header, 8) != 0)
die("dynamic header didn't start with cxsparse");
{
unsigned long sum = 0;
int i;
for (i = checksum_offset; i < checksum_offset + 4; ++i)
dyn_header[i] = 0;
for (i = 0; i < sizeof(dyn_header); ++i)
sum += dyn_header[i];
sum = ~sum; // flip_endian_l(~sum);
printf("new checksum: %.8x at 0x%llx\n", sum, dyn_ofs + checksum_offset);
}
return 0;
}
Это стоило мне нескольких часов, но в итоге я справился. Я уверен, что кто-то в конце концов опубликует редактор VHD, который сделает это проще, но, по крайней мере, я снова перешел в свой старый режим XP.
Обновление: есть гораздо более простой способ сделать это, используя PowerShell для создания сценариев некоторых COM-объектов: https://obligatorymoniker.wordpress.com/2010/08/21/how-to-rebase-differencing-disk-vhds-or-how -в-изменение-The-родитель-о-а-разностного-VHD-когда-The-родитель-имеет-переместил /
По сути:
$vpc=new-object -com VirtualPC.Application
$VHDChild = $vpc.GetHardDisk("C:\NAV2010.06.10_Diff")
$VHDParent = $vpc.GetHardDisk("\\devnas\Data_Backups SQLCluster1\NAV2010.06.10.vhd")
$VHDChild.Parent = $VHDParent