As discussed previously you can use libguestfs to extract raw filesystem content from a disk image.
The second part of our LVM to non-LVM converter involves a utility called virt-implode which takes the filesystem images and creates a new disk image with partitions containing the image content. It’s best to run this program using LIBGUESTFS_TRACE=1 so you can easily see what it’s doing.
$ LIBGUESTFS_TRACE=1 ./virt-implode.pl \
sda1.img VolGroup00_LogVol00.img VolGroup00_LogVol01.img \
output.img
libguestfs: trace: add_drive "output.img" "format:raw"
libguestfs: trace: add_drive = 0
libguestfs: trace: launch
libguestfs: trace: get_tmpdir
libguestfs: trace: get_tmpdir = "/tmp"
libguestfs: trace: get_cachedir
libguestfs: trace: get_cachedir = "/var/tmp"
libguestfs: trace: get_cachedir
libguestfs: trace: get_cachedir = "/var/tmp"
libguestfs: trace: launch = 0
libguestfs: trace: part_init "/dev/sda" "gpt"
libguestfs: trace: part_init = 0
libguestfs: trace: part_add "/dev/sda" "p" 2048 210943
libguestfs: trace: part_add = 0
libguestfs: trace: part_add "/dev/sda" "p" 210944 14694399
libguestfs: trace: part_add = 0
libguestfs: trace: part_add "/dev/sda" "p" 14694400 16726015
libguestfs: trace: part_add = 0
libguestfs: trace: upload "sda1.img" "/dev/sda1"
libguestfs: trace: upload = 0
libguestfs: trace: upload "VolGroup00_LogVol00.img" "/dev/sda2"
libguestfs: trace: upload = 0
libguestfs: trace: upload "VolGroup00_LogVol01.img" "/dev/sda3"
libguestfs: trace: upload = 0
libguestfs: trace: shutdown
libguestfs: trace: internal_autosync
libguestfs: trace: internal_autosync = 0
libguestfs: trace: shutdown = 0
libguestfs: trace: close
Use virt-filesystems to examine the result:
$ virt-filesystems -a output.img --all --long -h Name Type VFS Label MBR Size Parent /dev/sda1 filesystem ext3 /boot - 102M - /dev/sda2 filesystem ext3 - - 6.9G - /dev/sda3 filesystem swap - - 992M - /dev/sda1 partition - - - 102M /dev/sda /dev/sda2 partition - - - 6.9G /dev/sda /dev/sda3 partition - - - 992M /dev/sda /dev/sda device - - - 8.0G -
Here is the virt-implode script:
#!/usr/bin/perl -w
use strict;
use Sys::Guestfs;
die "$0 *.img output.img" unless @ARGV >= 2;
my $output = pop @ARGV;
# Work out how we'll partition the output image.
# Assumes that the input filesystem images are
# in raw format, and therefore size == file size.
my $fs;
my $size = 1024*1024;
my @start_sectors = ();
foreach $fs (@ARGV) {
push @start_sectors, $size / 512;
$size += (-s $fs);
# Round up to next 1MB boundary.
$size = round_up ($size, 1024*1024);
}
push @start_sectors, $size / 512;
$size += 1024 * 1024;
open FILE, ">$output" or die "$output: $!";
truncate FILE, $size or die "$output: truncate: $!";
close FILE or die "$output: close: $!";
my $g = Sys::Guestfs->new ();
$g->add_drive_opts ($output, format => "raw");
$g->launch ();
$g->part_init ("/dev/sda", "gpt");
my $i;
for (my $i = 0; $i < @start_sectors-1; ++$i) {
$g->part_add ("/dev/sda", "p", $start_sectors[$i], $start_sectors[$i+1]-1);
}
$i = 1;
foreach $fs (@ARGV) {
$g->upload ($fs, "/dev/sda$i");
$i++;
}
$g->shutdown ();
sub round_up
{
my $n = shift;
my $r = shift;
$n += $r-1;
$n &= ~($r-1);
$n
}
Next time I’ll see if I can get this guest to boot …
