Хорошо, я решил эту проблему сам, но это было неоправданно сложно.
Путь к родителю жестко закодирован в .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