Blob Blame History Raw
From f9b3afae96c72564cd4cd766555845f17e3c12a9 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 19 Mar 2021 10:36:48 +0100
Subject: [PATCH] repart: make sure to grow partition table after growing
 backing loopback file

This fixes the --size= switch, i.e. where we grow a disk image: after
growing it we need to expand the partition table so that its idea of the
the medium size matches the new reality. Otherwise our disk size
calculations in the subsequent steps might still use the original
ungrown size.

(This used to work, I guess this was borked when libfdisk learnt the
concept of "minimized" partition tables)
---
 src/partition/repart.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/src/partition/repart.c b/src/partition/repart.c
index be16f5a067b..7b6201efa83 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -3977,6 +3977,40 @@ static int find_root(char **ret, int *ret_fd) {
         return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "Failed to discover root block device.");
 }
 
+static int resize_pt(int fd) {
+        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+        _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
+        int r;
+
+        /* After resizing the backing file we need to resize the partition table itself too, so that it takes
+         * possession of the enlarged backing file. For this it suffices to open the device with libfdisk and
+         * immediately write it again, with no changes. */
+
+        c = fdisk_new_context();
+        if (!c)
+                return log_oom();
+
+        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
+        r = fdisk_assign_device(c, procfs_path, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open device '%s': %m", procfs_path);
+
+        r = fdisk_has_label(c);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine whether disk '%s' has a disk label: %m", procfs_path);
+        if (r == 0) {
+                log_debug("Not resizing partition table, as there currently is none.");
+                return 0;
+        }
+
+        r = fdisk_write_disklabel(c);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write resized partition table: %m");
+
+        log_info("Resized partition table.");
+        return 1;
+}
+
 static int resize_backing_fd(const char *node, int *fd) {
         char buf1[FORMAT_BYTES_MAX], buf2[FORMAT_BYTES_MAX];
         _cleanup_close_ int writable_fd = -1;
@@ -4029,6 +4063,10 @@ static int resize_backing_fd(const char *node, int *fd) {
                         /* Fallback to truncation, if fallocate() is not supported. */
                         log_debug("Backing file system does not support fallocate(), falling back to ftruncate().");
                 } else {
+                        r = resize_pt(writable_fd);
+                        if (r < 0)
+                                return r;
+
                         if (st.st_size == 0) /* Likely regular file just created by us */
                                 log_info("Allocated %s for '%s'.", buf2, node);
                         else
@@ -4042,6 +4080,10 @@ static int resize_backing_fd(const char *node, int *fd) {
                 return log_error_errno(errno, "Failed to grow '%s' from %s to %s by truncation: %m",
                                        node, buf1, buf2);
 
+        r = resize_pt(writable_fd);
+        if (r < 0)
+                return r;
+
         if (st.st_size == 0) /* Likely regular file just created by us */
                 log_info("Sized '%s' to %s.", node, buf2);
         else