-my %fses = map { $_ => check_fs ($_) } @devices;
-
-# Now the complex checking code itself.
-# check_fs takes a device name (LV or partition name) and returns
-# a hashref containing everything we can find out about the device.
-sub check_fs {
- local $_;
- my $dev = shift; # LV or partition name.
-
- my %r; # Result hash.
-
- # First try 'file(1)' on it.
- my $file = $g->file ($dev);
- if ($file =~ /ext2 filesystem data/) {
- $r{fstype} = "ext2";
- $r{fsos} = "linux";
- } elsif ($file =~ /ext3 filesystem data/) {
- $r{fstype} = "ext3";
- $r{fsos} = "linux";
- } elsif ($file =~ /ext4 filesystem data/) {
- $r{fstype} = "ext4";
- $r{fsos} = "linux";
- } elsif ($file =~ m{Linux/i386 swap file}) {
- $r{fstype} = "swap";
- $r{fsos} = "linux";
- $r{is_swap} = 1;
- }
-
- # If it's ext2/3/4, then we want the UUID and label.
- if (exists $r{fstype} && $r{fstype} =~ /^ext/) {
- $r{uuid} = $g->get_e2uuid ($dev);
- $r{label} = $g->get_e2label ($dev);
- }
-
- # Try mounting it, fnarrr.
- if (!$r{is_swap}) {
- $r{is_mountable} = 1;
- eval { $g->mount_ro ($dev, "/") };
- if ($@) {
- # It's not mountable, probably empty or some format
- # we don't understand.
- $r{is_mountable} = 0;
- goto OUT;
- }
-
- # Grub /boot?
- if ($g->is_file ("/grub/menu.lst") ||
- $g->is_file ("/grub/grub.conf")) {
- $r{content} = "linux-grub";
- check_grub (\%r);
- goto OUT;
- }
-
- # Linux root?
- if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
- $g->is_file ("/etc/fstab")) {
- $r{content} = "linux-root";
- $r{is_root} = 1;
- check_linux_root (\%r);
- goto OUT;
- }
-
- # Linux /usr/local.
- if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
- $g->is_dir ("/share") && !$g->exists ("/local") &&
- !$g->is_file ("/etc/fstab")) {
- $r{content} = "linux-usrlocal";
- goto OUT;
- }
-
- # Linux /usr.
- if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
- $g->is_dir ("/share") && $g->exists ("/local") &&
- !$g->is_file ("/etc/fstab")) {
- $r{content} = "linux-usr";
- goto OUT;
- }
-
- # Windows root?
- if ($g->is_file ("/AUTOEXEC.BAT") ||
- $g->is_file ("/autoexec.bat") ||
- $g->is_dir ("/Program Files") ||
- $g->is_dir ("/WINDOWS") ||
- $g->is_file ("/boot.ini") ||
- $g->is_file ("/ntldr")) {
- $r{fstype} = "ntfs"; # XXX this is a guess
- $r{fsos} = "windows";
- $r{content} = "windows-root";
- $r{is_root} = 1;
- check_windows_root (\%r);
- goto OUT;
- }
- }
-
- OUT:
- $g->umount_all ();
- return \%r;
-}
-
-sub check_linux_root
-{
- local $_;
- my $r = shift;
-
- # Look into /etc to see if we recognise the operating system.
- if ($g->is_file ("/etc/redhat-release")) {
- $_ = $g->cat ("/etc/redhat-release");
- if (/Fedora release (\d+\.\d+)/) {
- $r->{osdistro} = "fedora";
- $r->{osversion} = "$1"
- } elsif (/(Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\d+).*Update (\d+)/) {
- $r->{osdistro} = "redhat";
- $r->{osversion} = "$2.$3";
- } elsif (/(Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\d+(?:\.(\d+))?)/) {
- $r->{osdistro} = "redhat";
- $r->{osversion} = "$2";
- } else {
- $r->{osdistro} = "redhat";
- }
- } elsif ($g->is_file ("/etc/debian_version")) {
- $_ = $g->cat ("/etc/debian_version");
- if (/(\d+\.\d+)/) {
- $r->{osdistro} = "debian";
- $r->{osversion} = "$1";
- } else {
- $r->{osdistro} = "debian";
- }
- }
-
- # Parse the contents of /etc/fstab. This is pretty vital so
- # we can determine where filesystems are supposed to be mounted.
- eval "\$_ = \$g->cat ('/etc/fstab');";
- if (!$@ && $_) {
- my @lines = split /\n/;
- my @fstab;
- foreach (@lines) {
- my @fields = split /[ \t]+/;
- if (@fields >= 2) {
- my $spec = $fields[0]; # first column (dev/label/uuid)
- my $file = $fields[1]; # second column (mountpoint)
- if ($spec =~ m{^/} ||
- $spec =~ m{^LABEL=} ||
- $spec =~ m{^UUID=} ||
- $file eq "swap") {
- push @fstab, [$spec, $file]
- }
- }
- }
- $r->{fstab} = \@fstab if @fstab;
- }
-}
-
-# We only support NT. The control file /boot.ini contains a list of
-# Windows installations and their %systemroot%s in a simple text
-# format.
-#
-# XXX We could parse this better. This won't work if /boot.ini is on
-# a different drive from the %systemroot%, and in other unusual cases.
-
-sub check_windows_root
-{
- local $_;
- my $r = shift;
-
- my $boot_ini = resolve_windows_path ("/", "boot.ini");
- $r->{boot_ini} = $boot_ini;
-
- if (defined $r->{boot_ini}) {
- $_ = $g->cat ($boot_ini);
- my @lines = split /\n/;
- my $section;
- my $systemroot;
- foreach (@lines) {
- if (m/\[.*\]/) {
- $section = $1;
- } elsif (m/^default=.*?\\(\w+)$/i) {
- $systemroot = $1;
- last;
- } elsif (m/\\(\w+)=/) {
- $systemroot = $1;
- last;
- }
- }
-
- if (defined $systemroot) {
- $r->{systemroot} = resolve_windows_path ("/", $systemroot);
- if (defined $r->{systemroot} && $windows_registry) {
- check_windows_registry ($r, $r->{systemroot});
- }
- }
- }
-}
-
-sub check_windows_registry
-{
- local $_;
- my $r = shift;
- my $systemroot = shift;
-
- # Download the system registry files. Only download the
- # interesting ones, and we don't bother with user profiles at all.
- my $system32 = resolve_windows_path ($systemroot, "system32");
- if (defined $system32) {
- my $config = resolve_windows_path ($system32, "config");
- if (defined $config) {
- my $software = resolve_windows_path ($config, "software");
- if (defined $software) {
- load_windows_registry ($r, $software,
- "HKEY_LOCAL_MACHINE\\SOFTWARE");
- }
- my $system = resolve_windows_path ($config, "system");
- if (defined $system) {
- load_windows_registry ($r, $system,
- "HKEY_LOCAL_MACHINE\\System");
- }
- }
- }
-}
-
-sub load_windows_registry
-{
- local $_;
- my $r = shift;
- my $regfile = shift;
- my $prefix = shift;
-
- my $dir = tempdir (CLEANUP => 1);
-
- $g->download ($regfile, "$dir/reg");
-
- # 'reged' command is particularly noisy. Redirect stdout and
- # stderr to /dev/null temporarily.
- open SAVEOUT, ">&STDOUT";
- open SAVEERR, ">&STDERR";
- open STDOUT, ">/dev/null";
- open STDERR, ">/dev/null";
-
- my @cmd = ("reged", "-x", "$dir/reg", "$prefix", "\\", "$dir/out");
- my $res = system (@cmd);
-
- close STDOUT;
- close STDERR;
- open STDOUT, ">&SAVEOUT";
- open STDERR, ">&SAVEERR";
- close SAVEOUT;
- close SAVEERR;
-
- unless ($res == 0) {
- warn "reged command failed: $?";
- return;
- }
-
- # Some versions of reged segfault on inputs. If that happens we
- # may get no / partial output file. Anyway, if it exists, load
- # it.
- my $content;
- unless (open F, "$dir/out") {
- warn "no output from reged command: $!";
- return;
- }
- { local $/ = undef; $content = <F>; }
- close F;
-
- my @registry = ();
- @registry = @{$r->{registry}} if exists $r->{registry};
- push @registry, $content;
- $r->{registry} = \@registry;
-}
-
-# Because of case sensitivity, the actual path might have a different
-# name, and ntfs-3g is always case sensitive. Find out what the real
-# path is. Returns the correct full path, or undef.
-sub resolve_windows_path
-{
- local $_;
- my $parent = shift; # Must exist, with correct case.
- my $dir = shift;
-
- foreach ($g->ls ($parent)) {
- if (lc ($_) eq lc ($dir)) {
- if ($parent eq "/") {
- return "/$_"
- } else {
- return "$parent/$_"
- }
- }
- }
-
- undef;
-}
-
-sub check_grub
-{
- local $_;
- my $r = shift;
-
- # Grub version, if we care.
-}
-
-#print Dumper (\%fses);
-
-#----------------------------------------------------------------------
-# Now find out how many operating systems we've got. Usually just one.
-
-my %oses = ();
-
-foreach (sort keys %fses) {
- if ($fses{$_}->{is_root}) {
- my %r = (
- root => $fses{$_},
- root_device => $_
- );
- get_os_version (\%r);
- assign_mount_points (\%r);
- $oses{$_} = \%r;
- }
-}
-
-sub get_os_version
-{
- local $_;
- my $r = shift;
-
- $r->{os} = $r->{root}->{fsos} if exists $r->{root}->{fsos};
- $r->{distro} = $r->{root}->{osdistro} if exists $r->{root}->{osdistro};
- $r->{version} = $r->{root}->{osversion} if exists $r->{root}->{osversion};
-}
-
-sub assign_mount_points
-{
- local $_;
- my $r = shift;
-
- $r->{mounts} = { "/" => $r->{root_device} };
- $r->{filesystems} = { $r->{root_device} => $r->{root} };
-
- # Use /etc/fstab if we have it to mount the rest.
- if (exists $r->{root}->{fstab}) {
- my @fstab = @{$r->{root}->{fstab}};
- foreach (@fstab) {
- my ($spec, $file) = @$_;
-
- my ($dev, $fs) = find_filesystem ($spec);
- if ($dev) {
- $r->{mounts}->{$file} = $dev;
- $r->{filesystems}->{$dev} = $fs;
- if (exists $fs->{used}) {
- $fs->{used}++
- } else {
- $fs->{used} = 1
- }
- }
- }
- }
-}
-
-# Find filesystem by device name, LABEL=.. or UUID=..
-sub find_filesystem
-{
- local $_ = shift;
-
- if (/^LABEL=(.*)/) {
- my $label = $1;
- foreach (sort keys %fses) {
- if (exists $fses{$_}->{label} &&
- $fses{$_}->{label} eq $label) {
- return ($_, $fses{$_});
- }
- }
- warn "unknown filesystem label $label\n";
- return ();
- } elsif (/^UUID=(.*)/) {
- my $uuid = $1;
- foreach (sort keys %fses) {
- if (exists $fses{$_}->{uuid} &&
- $fses{$_}->{uuid} eq $uuid) {
- return ($_, $fses{$_});
- }
- }
- warn "unknown filesystem UUID $uuid\n";
- return ();
- } else {
- return ($_, $fses{$_}) if exists $fses{$_};