]> git.proxmox.com Git - pve-container.git/blobdiff - src/PVE/LXC/Create.pm
restore: correctly handle fw config from archive
[pve-container.git] / src / PVE / LXC / Create.pm
index 6f099894a7d572ca03d00aab7fd23d2ec14cd682..a46a50c268e3222f3c1a9ff4028c611975e09964 100644 (file)
@@ -4,7 +4,6 @@ use strict;
 use warnings;
 use File::Basename;
 use File::Path;
-use Data::Dumper;
 use Fcntl;
 
 use PVE::Storage;
@@ -17,9 +16,13 @@ use POSIX;
 sub detect_architecture {
     my ($rootdir) = @_;
 
-    my $supported_elf_class = {
-       1 => 'i386',
-       2 => 'amd64',
+    # see https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
+
+    my $supported_elf_machine = {
+       0x03 => 'i386',
+       0x3e => 'amd64',
+       0x28 => 'armhf',
+       0xb7 => 'arm64',
     };
 
     my $elf_fn = '/bin/sh'; # '/bin/sh' is POSIX mandatory
@@ -32,18 +35,19 @@ sub detect_architecture {
        open(my $fh, "<", $elf_fn) or die "open '$elf_fn' failed: $!\n";
        binmode($fh);
 
-       my $length = read($fh, my $data, 5) or die "read failed: $!\n";
+       my $length = read($fh, my $data, 20) or die "read failed: $!\n";
 
-       # 4 bytes ELF magic number and 1 byte ELF class
-       my ($magic, $class) = unpack("A4C", $data);
+       # 4 bytes ELF magic number and 1 byte ELF class, padding, machine
+       my ($magic, $class, undef, $machine) = unpack("A4CA12n", $data);
 
        die "'$elf_fn' does not resolve to an ELF!\n"
            if (!defined($class) || !defined($magic) || $magic ne "\177ELF");
 
-       die "'$elf_fn' has unknown ELF class '$class'!\n"
-           if !defined($supported_elf_class->{$class});
+       my $arch = $supported_elf_machine->{$machine};
+       die "'$elf_fn' has unknown ELF machine '$machine'!\n"
+           if !defined($arch);
 
-       return $supported_elf_class->{$class};
+       return $arch;
     };
 
     my $arch = eval { PVE::Tools::run_fork_with_timeout(5, $detect_arch) };
@@ -73,11 +77,12 @@ sub restore_archive {
            '.gz'  => '-z',
            '.bz2' => '-j',
            '.xz'  => '-J',
+           '.lzo'  => '--lzop',
        );
        if ($archive =~ /\.tar(\.[^.]+)?$/) {
            if (defined($1)) {
-               @compression_opt = $compression_map{$1}
-                   or die "unrecognized compression format: $1\n";
+               die "unrecognized compression format: $1\n" if !defined($compression_map{$1});
+               @compression_opt = $compression_map{$1};
            }
        } else {
            die "file does not look like a template archive: $archive\n";
@@ -133,7 +138,6 @@ sub recover_config {
        $conf = PVE::LXC::Config::parse_pct_config("/lxc/0.conf" , $raw);
 
        delete $conf->{snapshots};
-       delete $conf->{template}; # restored CT is never a template
 
        PVE::LXC::Config->foreach_mountpoint($conf, sub {
            my ($ms, $mountpoint) = @_;
@@ -153,7 +157,7 @@ sub recover_config {
 }
 
 sub restore_configuration {
-    my ($vmid, $rootdir, $conf, $restricted) = @_;
+    my ($vmid, $rootdir, $conf, $restricted, $unique, $skip_fw) = @_;
 
     # restore: try to extract configuration from archive
 
@@ -168,24 +172,51 @@ sub restore_configuration {
            next if $key eq 'digest' || $key eq 'rootfs' || $key eq 'snapshots' || $key eq 'unprivileged' || $key eq 'parent';
            next if $key =~ /^mp\d+$/; # don't recover mountpoints
            next if $key =~ /^unused\d+$/; # don't recover unused disks
-           if ($restricted && $key eq 'lxc') {
-               warn "skipping custom lxc options, restore manually as root:\n";
-               warn "--------------------------------\n";
+           # we know if it was a template in the restore API call and check if the target
+           # storage supports creating a template there
+           next if $key =~ /^template$/;
+
+           if ($key eq 'lxc') {
                my $lxc_list = $oldconf->{'lxc'};
-               foreach my $lxc_opt (@$lxc_list) {
-                   warn "$lxc_opt->[0]: $lxc_opt->[1]\n"
+               if ($restricted) {
+                   warn "skipping custom lxc options, restore manually as root:\n";
+                   warn "--------------------------------\n";
+                   foreach my $lxc_opt (@$lxc_list) {
+                       warn "$lxc_opt->[0]: $lxc_opt->[1]\n"
+                   }
+                   warn "--------------------------------\n";
+               } else {
+                   @{$conf->{$key}} = (@$lxc_list, @{$conf->{$key}});
                }
-               warn "--------------------------------\n";
+               next;
+           }
+
+           if ($unique && $key =~ /^net\d+$/) {
+               my $net = PVE::LXC::Config->parse_lxc_network($oldconf->{$key});
+               my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
+               $net->{hwaddr} = PVE::Tools::random_ether_addr($dc->{mac_prefix});
+               $conf->{$key} = PVE::LXC::Config->print_lxc_network($net);
                next;
            }
            $conf->{$key} = $oldconf->{$key} if !defined($conf->{$key});
        }
        unlink($pct_cfg_fn);
 
-       if (-f $pct_fwcfg_fn) {
+       # note: this file is possibly from the container itself in backups
+       # created prior to pve-container 2.0-40 (PVE 5.x) / 3.0-5 (PVE 6.x)
+       # only copy non-empty, non-symlink files, and only if the user is
+       # allowed to modify the firewall config anyways
+       if (-f $pct_fwcfg_fn && ! -l $pct_fwcfg_fn && -s $pct_fwcfg_fn) {
            my $pve_firewall_dir = '/etc/pve/firewall';
-           mkdir $pve_firewall_dir; # make sure the directory exists
-           PVE::Tools::file_copy($pct_fwcfg_fn, "${pve_firewall_dir}/$vmid.fw");
+           my $pct_fwcfg_target = "${pve_firewall_dir}/${vmid}.fw";
+           if ($skip_fw) {
+               warn "ignoring firewall config from backup archive's '$pct_fwcfg_fn', lacking API permission to modify firewall.\n";
+               warn "old firewall configuration in '$pct_fwcfg_target' left in place!\n"
+                   if -e $pct_fwcfg_target;
+           } else {
+               mkdir $pve_firewall_dir; # make sure the directory exists
+               PVE::Tools::file_copy($pct_fwcfg_fn, $pct_fwcfg_target);
+           }
            unlink $pct_fwcfg_fn;
        }