OK, eu mesmo resolvi esse problema, mas foi extremamente difícil.
O caminho para o pai é codificado dentro do .vhd, em vários formatos diferentes; no meu caso, ambos os caminhos relativo e absoluto, em ASCII e UTF-16, para 4 caminhos totais. Você pode obter a especificação de formato de arquivo .vhd da Microsoft .
Mas uma verificação rápida com strings ou um utilitário semelhante (estou usando o Cygwin aqui) pesquisas por dados de string podem encontrar esses deslocamentos mais facilmente do que percorrer as estruturas de 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
Essas cadeias são armazenadas com muitos preenchimentos de bytes nulos; Eu usei um editor hexadecimal para atualizar os caminhos para novos locais.
Mas não funcionou; os caminhos foram mostrados truncados no editor de configurações do Virtual PC. Os comprimentos de string são codificados em outro lugar; no meu caso em 0x4AB, 0x4C3, etc. (Eu fiz uma pesquisa hexadecimal para os comprimentos do caminho antigo). Eu atualizei esses comprimentos, mas o vhd foi considerado inválido pelo editor de configurações do Virtual PC. Eu percebi que uma soma de verificação agora estava envolvida, baixei a especificação VHD, instalei o visualizador de documentos do Word para que eu pudesse ler o arquivo .doc, e então eu escrevi o programa abaixo. Sua saída, quando entregue o caminho para o vhd, é a soma de verificação atualizada e a localização da soma de verificação; exemplo de saída:
opening Windows XP Mode-X.vhd
dynamic header seems to be at 0x200
new checksum: ffffda30 at 0x224
A inserção dessa nova soma de verificação com um editor hexadecimal fez o vhd funcionar e o modo XP antigo foi inicializado com êxito.
Programa de cálculo da soma de verificação:
#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;
}
Isso me custou algumas horas, mas eu passei no final. Tenho certeza de que alguém acabará postando um editor de VHD que faz isso mais facilmente, mas pelo menos eu passei para o meu antigo modo XP mais uma vez.
Atualização: Há uma maneira muito mais fácil de fazer isso, usando o PowerShell para criar scripts de alguns objetos COM: link
Em essência:
$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