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>
88 lines
2.3 KiB
Perl
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;
|