[pve-devel] applied: [PATCH container v3] Create: fix architecture detection in restore_archive

Wolfgang Bumiller w.bumiller at proxmox.com
Fri Feb 17 09:08:11 CET 2017


applied

On Thu, Feb 16, 2017 at 05:55:29PM +0100, Thomas Lamprecht wrote:
> For detecting a CT templates architecture we used the `file -b -L`
> output from the PVE host side.
> If the container has a link:
> /bin/sh -> /bin/bash
> (Alpine Linux does that, for example) the '-L' flag from file
> resolves the $rootfs/bin/sh to /bin/bash and thus checks the
> architecture of bash on the PVE system, which is always 64 bit.
> 
> Add a helper which chroots in the rootfs to avoid problems with
> absolute symlinks and use 'open' to avoid relative symlink problems
> read the first 5 bytes from /bin/sh, 4 bytes for the ELF magic number
> and the fifth for the ELF class, which tells us if we have a 32
> (class 1) or 64 (class 2) bit ELF binary.
> 
> Return this information as an exit code to the parent.
> 
> Signed-off-by: Thomas Lamprecht <t.lamprecht at proxmox.com>
> ---
> 
> changes v2 -> v3:
> * add check for whenn waitpid could be interupted by a signal
> 
> The refactoring out of common components from this and
> LXC::Setup::protected_call is, imo, currently not really useful
> as while both have a fork and chroot call they handle a lot different besides
> that.
> 
> Here I communicate the class over the exit code from the child, incorporating
> or allowing this in a general forked_call seems not feasible as such behavior
> is really specific and not easily merge able with such a generic written method
> as protected_call is, imo.
> 
> Also the protected call needs recursion protection, and adds devices in the
> chroot environment, this can be done nonetheless but with this restrictions the
> end result seems worse than just letting both, protected_call and get_elf_class
> coexist.
> 
>  src/PVE/LXC/Create.pm | 71 ++++++++++++++++++++++++++++++++++++++++-----------
>  1 file changed, 56 insertions(+), 15 deletions(-)
> 
> diff --git a/src/PVE/LXC/Create.pm b/src/PVE/LXC/Create.pm
> index 1f1ece1..d93159c 100644
> --- a/src/PVE/LXC/Create.pm
> +++ b/src/PVE/LXC/Create.pm
> @@ -11,6 +11,7 @@ use PVE::LXC;
>  use PVE::LXC::Setup;
>  use PVE::VZDump::ConvertOVZ;
>  use PVE::Tools;
> +use POSIX;
>  
>  sub next_free_nbd_dev {
>      
> @@ -23,6 +24,49 @@ sub next_free_nbd_dev {
>      die "unable to find free nbd device\n";
>  }
>  
> +sub get_elf_class {
> +    my ($rootdir, $elf_fn) = @_;
> +
> +    my $child_pid = fork();
> +    die "fork failed: $!\n" if !defined($child_pid);
> +
> +    if (!$child_pid) {
> +	# chroot avoids a problem where we check the binary of the host system
> +	# if $elf_fn is an absolut symlink (e.g. $rootdir/bin/sh -> /bin/bash)
> +	chroot($rootdir) or die "chroot '$rootdir' failed: $!\n";
> +	chdir('/') or die "failed to change to root directory\n";
> +
> +	my $fh;
> +	open($fh, "<", $elf_fn) or die "open '$elf_fn' failed: $!\n";
> +	binmode($fh);
> +
> +	my $data;
> +	my $length = read($fh, $data, 5);
> +	die "read failed: $!\n" if !defined($length);
> +
> +	# 4 bytes ELF magic number and 1 byte ELF class
> +	my ($magic, $class) = unpack("A4C", $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 ($class != 1 && $class != 2);
> +
> +	POSIX::_exit($class);
> +    }
> +
> +    while (waitpid($child_pid, 0) != $child_pid) {}
> +    my $exit_code = $?;
> +    if (my $sig = ($exit_code & 127)) {
> +	warn "could not get architecture, got signal $sig\n";
> +    } else {
> +	$exit_code >>= 8;
> +    }
> +
> +    return $exit_code;
> +}
> +
>  sub restore_archive {
>      my ($archive, $rootdir, $conf, $no_unpack_error) = @_;
>  
> @@ -53,21 +97,18 @@ sub restore_archive {
>      # if arch is set, we do not try to autodetect it
>      return if defined($conf->{arch});
>  
> -    # determine file type of /usr/bin/file itself to get guests' architecture
> -    $cmd = [@$userns_cmd, '/usr/bin/file', '-b', '-L', "$rootdir/bin/sh"];
> -    PVE::Tools::run_command($cmd, outfunc => sub {
> -	shift =~ /^ELF (\d{2}-bit)/; # safely assumes x86 linux
> -	my $arch_str = $1;
> -	$conf->{'arch'} = 'amd64'; # defaults to 64bit
> -	if(defined($arch_str)) {
> -	    $conf->{'arch'} = 'i386' if $arch_str =~ /32/;
> -	    print "Detected container architecture: $conf->{'arch'}\n";
> -	} else {
> -	    print "CT architecture detection failed, falling back to amd64.\n" .
> -	          "Edit the config in /etc/pve/nodes/{node}/lxc/{vmid}.conf " .
> -	          "to set another architecture.\n";
> -	}
> -    });
> +
> +    my $elf_class = get_elf_class($rootdir, '/bin/sh'); # /bin/sh is POSIX mandatory
> +
> +    $conf->{'arch'} = 'amd64'; # defaults to 64bit
> +    if ($elf_class == 1 || $elf_class == 2) {
> +	$conf->{'arch'} = 'i386' if $elf_class == 1;
> +	print "Detected container architecture: $conf->{'arch'}\n";
> +    } else {
> +	print "CT architecture detection failed, falling back to amd64.\n" .
> +	      "Edit the config in /etc/pve/nodes/{node}/lxc/{vmid}.conf " .
> +	      "to set another architecture.\n";
> +    }
>  }
>  
>  sub recover_config {
> -- 
> 2.1.4




More information about the pve-devel mailing list