file size info: allow specifying file format

Allow callers to opt-out of 'qemu-img' autodetecting the format.

Currently not supported to be done together with untrusted, because it
can lead to less checks being done. Could be further refined (e.g.
disallow only untrusted together with format being 'raw') should the
need arise.

For 'subvol' format, the checking is handled outside of 'qemu-img' of
course, based on whether it is a directory or not.

Currently, there is a fallback to 'raw' should the format not be among
the ones allowed for the 'pve-qm-image-format' standard option. This
is to reduce potential for fallout, in particular for the plan to
change the base plugin's volume_size_info() to pass in the expected
format when calling file_size_info() too.

While not explicitly part of the storage plugin API, the 'untrusted'
parameter is now in a different place, so a compat check is added for
external plugins that might've still used it.

Breaks for qemu-server needed (if we don't want to just rely on the
compat check).

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
This commit is contained in:
Fiona Ebner
2024-12-06 17:25:20 +01:00
committed by Fabian Grünbichler
parent 10d338baa8
commit 83648951d7
3 changed files with 35 additions and 6 deletions

View File

@ -63,7 +63,7 @@ sub extract_disk_from_import_file {
}
# check potentially untrusted image file!
PVE::Storage::file_size_info($source_path, undef, 1);
PVE::Storage::file_size_info($source_path, undef, undef, 1);
# create temporary 1M image that will get overwritten by the rename
# to reserve the filename and take care of locking

View File

@ -242,9 +242,9 @@ sub storage_ids {
}
sub file_size_info {
my ($filename, $timeout, $untrusted) = @_;
my ($filename, $timeout, $file_format, $untrusted) = @_;
return PVE::Storage::Plugin::file_size_info($filename, $timeout, $untrusted);
return PVE::Storage::Plugin::file_size_info($filename, $timeout, $file_format, $untrusted);
}
sub get_volume_attribute {
@ -2223,7 +2223,7 @@ sub assert_iso_content {
my ($path) = @_;
# check for things like backing image
file_size_info($path, undef, 1);
file_size_info($path, undef, undef, 1);
return 1;
}

View File

@ -950,13 +950,27 @@ sub free_image {
return undef;
}
# TODO taken from PVE/QemuServer/Drive.pm, avoiding duplication would be nice
my @checked_qemu_img_formats = qw(raw cow qcow qcow2 qed vmdk cloop);
# set $untrusted if the file in question might be malicious since it isn't
# created by our stack
# this makes certain checks fatal, and adds extra checks for known problems like
# - backing files (qcow2/vmdk)
# - external data files (qcow2)
#
# Set $file_format to force qemu-img to treat the image as being a specific format.
sub file_size_info {
my ($filename, $timeout, $untrusted) = @_;
my ($filename, $timeout, $file_format, $untrusted) = @_;
# compat for old parameter order
# TODO PVE 9 remove
if (defined($file_format) && ($file_format eq '1' || $file_format eq '0')) {
warn "file_size_info: detected call with legacy parameter order: \$untrusted before"
." \$file_format\n";
$untrusted = $file_format;
$file_format = undef;
}
my $st = File::stat::stat($filename);
@ -982,13 +996,24 @@ sub file_size_info {
};
if (S_ISDIR($st->mode)) {
$handle_error->("expected format '$file_format', but '$filename' is a directory\n")
if $file_format && $file_format ne 'subvol';
return wantarray ? (0, 'subvol', 0, undef, $st->ctime) : 1;
} elsif ($file_format && $file_format eq 'subvol') {
$handle_error->("expected format '$file_format', but '$filename' is not a directory\n");
}
# TODO PVE 9 - consider upgrading to "die" if an unsupported format is passed in after
# evaluating breakage potential.
$file_format = 'raw' if $file_format && !grep { $_ eq $file_format } @checked_qemu_img_formats;
my $cmd = ['/usr/bin/qemu-img', 'info', '--output=json', $filename];
push $cmd->@*, '-f', $file_format if $file_format;
my $json = '';
my $err_output = '';
eval {
run_command(['/usr/bin/qemu-img', 'info', '--output=json', $filename],
run_command($cmd,
timeout => $timeout,
outfunc => sub { $json .= shift },
errfunc => sub { $err_output .= shift . "\n"},
@ -1041,6 +1066,10 @@ sub file_size_info {
warn "strange parent name path '$parent' found\n" if $parent =~ m/[^\S]/;
($parent) = ($parent =~ /^(\S+)$/); # untaint
}
die "qemu-img bug: queried format does not match format in result '$file_format ne $format'"
if $file_format && $file_format ne $format;
return wantarray ? ($size, $format, $used, $parent, $st->ctime) : $size;
}