]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
vmspawn: optionally grow image
authorLennart Poettering <lennart@poettering.net>
Wed, 5 Mar 2025 14:24:28 +0000 (15:24 +0100)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 9 Mar 2025 18:57:46 +0000 (03:57 +0900)
man/systemd-vmspawn.xml
src/vmspawn/vmspawn.c

index 0be886cb131d7983144f74be4f680747d1618ce2..2db88d3b7fac525de6fbaa69c324371bcc19e99b 100644 (file)
           <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>
index 821e08ea6acb6adbb62f61e5326805fde02f58bd..e49d18c148e6a762eb515968ffc148de6916224a 100644 (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)