]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
homework: resize to maximum disk space if disk size is not specified
authorGibeom Gwon <gb.gwon@stackframe.dev>
Wed, 19 Oct 2022 09:12:29 +0000 (18:12 +0900)
committerLennart Poettering <lennart@poettering.net>
Mon, 19 Jun 2023 10:26:34 +0000 (12:26 +0200)
If the backing storage is LUKS2 on a block device, auto resize mode
is enabled, and disk size is not specified, resize the partition to
the maximum expandable size.

Fixes: #22255, #23967
src/home/homework-luks.c

index 235969980139fe68f2fc08a66acd8ab08b8c7a07..659a4826ee4a886ea133fc9f9acc2afa12caa22e 100644 (file)
@@ -2749,6 +2749,43 @@ static int prepare_resize_partition(
         return 1;
 }
 
+static int get_maximum_partition_size(
+                int fd,
+                struct fdisk_partition *p,
+                uint64_t *ret_maximum_partition_size) {
+
+        _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
+        uint64_t start_lba, start, last_lba, end;
+        int r;
+
+        assert(fd >= 0);
+        assert(p);
+        assert(ret_maximum_partition_size);
+
+        c = fdisk_new_context();
+        if (!c)
+                return log_oom();
+
+        r = fdisk_assign_device(c, FORMAT_PROC_FD_PATH(fd), 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open device: %m");
+
+        start_lba = fdisk_partition_get_start(p);
+        assert(start_lba <= UINT64_MAX/512);
+        start = start_lba * 512;
+
+        last_lba = fdisk_get_last_lba(c); /* One sector before boundary where usable space ends */
+        assert(last_lba < UINT64_MAX/512);
+        end = DISK_SIZE_ROUND_DOWN((last_lba + 1) * 512); /* Round down to multiple of 4K */
+
+        if (start > end)
+                return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Last LBA is before partition start.");
+
+        *ret_maximum_partition_size = DISK_SIZE_ROUND_DOWN(end - start);
+
+        return 1;
+}
+
 static int ask_cb(struct fdisk_context *c, struct fdisk_ask *ask, void *userdata) {
         char *result;
 
@@ -3214,6 +3251,17 @@ int home_resize_luks(
             setup->partition_offset + setup->partition_size > old_image_size)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Old partition doesn't fit in backing storage, refusing.");
 
+        /* Get target partition information in here for new_partition_size calculation */
+        r = prepare_resize_partition(
+                        image_fd,
+                        setup->partition_offset,
+                        setup->partition_size,
+                        &disk_uuid,
+                        &table,
+                        &partition);
+        if (r < 0)
+                return r;
+
         if (S_ISREG(st.st_mode)) {
                 uint64_t partition_table_extra, largest_size;
 
@@ -3236,9 +3284,13 @@ int home_resize_luks(
                         new_partition_size = 0;
                         intention = INTENTION_SHRINK;
                 } else {
-                        uint64_t new_partition_size_rounded;
+                        uint64_t new_partition_size_rounded = DISK_SIZE_ROUND_DOWN(h->disk_size);
 
-                        new_partition_size_rounded = DISK_SIZE_ROUND_DOWN(h->disk_size);
+                        if (h->disk_size == UINT64_MAX && partition) {
+                                r = get_maximum_partition_size(image_fd, partition, &new_partition_size_rounded);
+                                if (r < 0)
+                                        return r;
+                        }
 
                         if (setup->partition_size >= new_partition_size_rounded &&
                             setup->partition_size <= h->disk_size) {
@@ -3332,16 +3384,6 @@ int home_resize_luks(
                  special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
                  FORMAT_BYTES(new_fs_size));
 
-        r = prepare_resize_partition(
-                        image_fd,
-                        setup->partition_offset,
-                        setup->partition_size,
-                        &disk_uuid,
-                        &table,
-                        &partition);
-        if (r < 0)
-                return r;
-
         if (new_fs_size > old_fs_size) { /* → Grow */
 
                 if (S_ISREG(st.st_mode)) {