Files
pve-storage/src/PVE/GuestImport.pm
Thomas Lamprecht 811aa863cb guest import: extract staging copy with same owner than running process
Some OVAs have a UID/GID set for their inner file, for example the one
from GNS3:

> tar tvf 'GNS3 VM.ova' --numeric-owner
> -rw-r----- 6/1            9047 2024-11-07 10:22 GNS3 VM.ovf
> -rw-rw---- 6/1       904088064 2024-11-07 10:22 GNS3 VM-disk001.vmdk
> -rw-rw---- 6/1         2879488 2024-11-07 10:22 GNS3 VM-disk002.vmdk

As we run as root, tar is defaulting to the `--same-owner` option,
where it tries extracting files with the same ownership as exists in
the archive.

This might not be ideal and results in an error for GNS3:

> tar: GNS3 VM-disk001.vmdk: Cannot change ownership to uid 6, gid 1: Operation not permitted

So, explicitly set the `--no-same-owner` option to make tar always use
the UID/GID of the running process, which is what we want here.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2024-11-18 20:50:28 +01:00

88 lines
2.3 KiB
Perl

package PVE::GuestImport;
use strict;
use warnings;
use File::Path;
use PVE::Storage;
use PVE::Tools qw(run_command);
sub extract_disk_from_import_file {
my ($volid, $vmid, $target_storeid) = @_;
my ($source_storeid, $volname) = PVE::Storage::parse_volume_id($volid);
$target_storeid //= $source_storeid;
my $cfg = PVE::Storage::config();
my ($vtype, $name, undef, undef, undef, undef, $fmt) =
PVE::Storage::parse_volname($cfg, $volid);
die "only files with content type 'import' can be extracted\n"
if $vtype ne 'import';
die "only files from 'ova' format can be extracted\n"
if $fmt !~ m/^ova\+/;
# extract the inner file from the name
my $archive_volid;
my $inner_file;
my $inner_fmt;
if ($name =~ m!^(.*\.ova)/(${PVE::Storage::SAFE_CHAR_WITH_WHITESPACE_CLASS_RE}+)$!) {
$archive_volid = "$source_storeid:import/$1";
$inner_file = $2;
($inner_fmt) = $fmt =~ /^ova\+(.*)$/;
} else {
die "cannot extract $volid - invalid volname $volname\n";
}
my $ova_path = PVE::Storage::path($cfg, $archive_volid);
my $tmpdir = PVE::Storage::get_image_dir($cfg, $target_storeid, $vmid);
my $pid = $$;
$tmpdir .= "/tmp_${pid}_${vmid}";
mkpath $tmpdir;
my $source_path = "$tmpdir/$inner_file";
my $target_path;
my $target_volid;
eval {
run_command([
'tar',
'-x',
'--force-local',
'--no-same-owner',
'-C', $tmpdir,
'-f', $ova_path,
$inner_file,
]);
# check for symlinks and other non regular files
if (-l $source_path || ! -f $source_path) {
die "extracted file '$inner_file' from archive '$archive_volid' is not a regular file\n";
}
# check potentially untrusted image file!
PVE::Storage::file_size_info($source_path, undef, 1);
# create temporary 1M image that will get overwritten by the rename
# to reserve the filename and take care of locking
$target_volid = PVE::Storage::vdisk_alloc($cfg, $target_storeid, $vmid, $inner_fmt, undef, 1024);
$target_path = PVE::Storage::path($cfg, $target_volid);
print "renaming $source_path to $target_path\n";
rename($source_path, $target_path) or die "unable to move - $!\n";
};
if (my $err = $@) {
File::Path::remove_tree($tmpdir);
die "error during extraction: $err\n";
}
File::Path::remove_tree($tmpdir);
return $target_volid;
}
1;