vmspawn: optionally grow image

This commit is contained in:
Lennart Poettering
2025-03-05 15:24:28 +01:00
committed by Yu Watanabe
parent 37cfab0e82
commit ef430b84e9
2 changed files with 74 additions and 1 deletions

View File

@@ -266,6 +266,18 @@
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--grow-image=<replaceable>BYTES</replaceable></option></term>
<term><option>-G <replaceable>BYTES</replaceable></option></term>
<listitem><para>Grows the image file specified by <option>--image=</option> to the specified size
in bytes if it is smaller. Executes no operation if no image file is used or the image file is
already as large or larger than requested. The specified size accepts the usual K, M, G suffixes
(to the base of 1024). Specified values are rounded up to multiples of 4096.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--smbios11=<replaceable>STRING</replaceable></option></term>
<term><option>-s <replaceable>STRING</replaceable></option></term>

View File

@@ -66,6 +66,7 @@
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "sync-util.h"
#include "time-util.h"
#include "tmpfile-util.h"
#include "unit-name.h"
@@ -119,6 +120,7 @@ static char *arg_ssh_key_type = NULL;
static bool arg_discard_disk = true;
struct ether_addr arg_network_provided_mac = {};
static char **arg_smbios11 = NULL;
static uint64_t arg_grow_image = 0;
STATIC_DESTRUCTOR_REGISTER(arg_directory, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
@@ -170,6 +172,7 @@ static int help(void) {
" --secure-boot=BOOL Enable searching for firmware supporting SecureBoot\n"
" --firmware=PATH|list Select firmware definition file (or list available)\n"
" --discard-disk=BOOL Control processing of discard requests\n"
" -G --grow-image=BYTES Grow image file to specified size in bytes\n"
" -s --smbios11=STRING Pass an arbitrary SMBIOS Type #11 string to the VM\n"
"\n%3$sSystem Identity:%4$s\n"
" -M --machine=NAME Set the machine name for the VM\n"
@@ -300,6 +303,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "discard-disk", required_argument, NULL, ARG_DISCARD_DISK },
{ "background", required_argument, NULL, ARG_BACKGROUND },
{ "smbios11", required_argument, NULL, 's' },
{ "grow-image", required_argument, NULL, 'G' },
{}
};
@@ -309,7 +313,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argv);
optind = 0;
while ((c = getopt_long(argc, argv, "+hD:i:M:nqs:", options, NULL)) >= 0)
while ((c = getopt_long(argc, argv, "+hD:i:M:nqs:G:", options, NULL)) >= 0)
switch (c) {
case 'h':
return help();
@@ -583,6 +587,18 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'G':
if (isempty(optarg)) {
arg_grow_image = 0;
break;
}
r = parse_size(optarg, 1024, &arg_grow_image);
if (r < 0)
return log_error_errno(r, "Failed to parse --grow-image= paramater: %s", optarg);
break;
case '?':
return -EINVAL;
@@ -1465,6 +1481,47 @@ static int generate_ssh_keypair(const char *key_path, const char *key_type) {
return 0;
}
static int grow_image(const char *path, uint64_t size) {
int r;
assert(path);
if (size == 0)
return 0;
/* Round up to multiple of 4K */
size = DIV_ROUND_UP(size, 4096);
if (size > UINT64_MAX / 4096)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified file size too large, refusing.");
size *= 4096;
_cleanup_close_ int fd = xopenat_full(AT_FDCWD, path, O_RDWR|O_CLOEXEC, XO_REGULAR, /* mode= */ 0);
if (fd < 0)
return log_error_errno(fd, "Failed to open image file '%s': %m", path);
struct stat st;
if (fstat(fd, &st) < 0)
return log_error_errno(errno, "Failed to stat '%s': %m", path);
if ((uint64_t) st.st_size >= size) {
log_debug("Not growing image '%s' to %s, size already at %s.", path,
FORMAT_BYTES(size), FORMAT_BYTES(st.st_size));
return 0;
}
if (ftruncate(fd, size) < 0)
return log_error_errno(errno, "Failed grow image file '%s' from %s to %s: %m", path,
FORMAT_BYTES(st.st_size), FORMAT_BYTES(size));
r = fsync_full(fd);
if (r < 0)
return log_error_errno(r, "Failed to sync image file '%s' after growing to %s: %m", path, FORMAT_BYTES(size));
if (!arg_quiet)
log_info("Image file '%s' successfully grown from %s to %s.", path, FORMAT_BYTES(st.st_size), FORMAT_BYTES(size));
return 1;
}
static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
SSHInfo ssh_info; /* Used when talking to pid1 via SSH, but must survive until the function ends. */
_cleanup_(ovmf_config_freep) OvmfConfig *ovmf_config = NULL;
@@ -2156,6 +2213,10 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
return log_error_errno(r, "Failed to parse $SYSTEMD_VMSPAWN_QEMU_EXTRA: %m");
}
r = grow_image(arg_image, arg_grow_image);
if (r < 0)
return r;
if (DEBUG_LOGGING) {
_cleanup_free_ char *joined = quote_command_line(cmdline, SHELL_ESCAPE_EMPTY);
if (!joined)