8d419f
From 389cc9af2087aa5369ac6bf0124d14877d541966 Mon Sep 17 00:00:00 2001
8d419f
From: Lennart Poettering <lennart@poettering.net>
8d419f
Date: Fri, 4 Feb 2022 17:39:44 +0100
8d419f
Subject: [PATCH] repart: fix sector size handling
8d419f
8d419f
This queries the sector size from libfdisk instead of assuming 512, and
8d419f
uses that when converting from bytes to the offset/size values libfdisk
8d419f
expects.
8d419f
8d419f
This is an alternative to Tom Yan's #21823, but prefers using libfdisk's
8d419f
own ideas of the sector size instead of going directly to the backing
8d419f
device via ioctls. (libfdisk can after all also operate on regular
8d419f
files, where the sector size concept doesn't necessarily apply the same
8d419f
way.)
8d419f
8d419f
This also makes the "grain" variable, i.e. how we'll align the
8d419f
partitions. Previously this was hardcoded to 4K, and that still will be
8d419f
the minimum grain we use, but should the sector size be larger than that
8d419f
we'll use the next multiple of the sector size instead.
8d419f
8d419f
(cherry picked from commit 994b303123ebe6a140bf3e56c66aa66119ae7d95)
8d419f
8d419f
Related: #2017035
8d419f
---
8d419f
 src/partition/repart.c | 212 +++++++++++++++++++++++++----------------
8d419f
 1 file changed, 132 insertions(+), 80 deletions(-)
8d419f
8d419f
diff --git a/src/partition/repart.c b/src/partition/repart.c
8d419f
index d08f47f2c4..0862a37a8d 100644
8d419f
--- a/src/partition/repart.c
8d419f
+++ b/src/partition/repart.c
8d419f
@@ -195,6 +195,8 @@ struct Context {
8d419f
         uint64_t start, end, total;
8d419f
 
8d419f
         struct fdisk_context *fdisk_context;
8d419f
+        uint64_t sector_size;
8d419f
+        uint64_t grain_size;
8d419f
 
8d419f
         sd_id128_t seed;
8d419f
 };
8d419f
@@ -407,9 +409,12 @@ static bool context_drop_one_priority(Context *context) {
8d419f
         return true;
8d419f
 }
8d419f
 
8d419f
-static uint64_t partition_min_size(const Partition *p) {
8d419f
+static uint64_t partition_min_size(Context *context, const Partition *p) {
8d419f
         uint64_t sz;
8d419f
 
8d419f
+        assert(context);
8d419f
+        assert(p);
8d419f
+
8d419f
         /* Calculate the disk space we really need at minimum for this partition. If the partition already
8d419f
          * exists the current size is what we really need. If it doesn't exist yet refuse to allocate less
8d419f
          * than 4K.
8d419f
@@ -428,50 +433,60 @@ static uint64_t partition_min_size(const Partition *p) {
8d419f
                 uint64_t d = 0;
8d419f
 
8d419f
                 if (p->encrypt != ENCRYPT_OFF)
8d419f
-                        d += round_up_size(LUKS2_METADATA_SIZE, 4096);
8d419f
+                        d += round_up_size(LUKS2_METADATA_SIZE, context->grain_size);
8d419f
 
8d419f
                 if (p->copy_blocks_size != UINT64_MAX)
8d419f
-                        d += round_up_size(p->copy_blocks_size, 4096);
8d419f
+                        d += round_up_size(p->copy_blocks_size, context->grain_size);
8d419f
                 else if (p->format || p->encrypt != ENCRYPT_OFF) {
8d419f
                         uint64_t f;
8d419f
 
8d419f
                         /* If we shall synthesize a file system, take minimal fs size into account (assumed to be 4K if not known) */
8d419f
-                        f = p->format ? minimal_size_by_fs_name(p->format) : UINT64_MAX;
8d419f
-                        d += f == UINT64_MAX ? 4096 : f;
8d419f
+                        f = p->format ? round_up_size(minimal_size_by_fs_name(p->format), context->grain_size) : UINT64_MAX;
8d419f
+                        d += f == UINT64_MAX ? context->grain_size : f;
8d419f
                 }
8d419f
 
8d419f
                 if (d > sz)
8d419f
                         sz = d;
8d419f
         }
8d419f
 
8d419f
-        return MAX(p->size_min != UINT64_MAX ? p->size_min : DEFAULT_MIN_SIZE, sz);
8d419f
+        return MAX(round_up_size(p->size_min != UINT64_MAX ? p->size_min : DEFAULT_MIN_SIZE, context->grain_size), sz);
8d419f
 }
8d419f
 
8d419f
-static uint64_t partition_max_size(const Partition *p) {
8d419f
+static uint64_t partition_max_size(const Context *context, const Partition *p) {
8d419f
+        uint64_t sm;
8d419f
+
8d419f
         /* Calculate how large the partition may become at max. This is generally the configured maximum
8d419f
          * size, except when it already exists and is larger than that. In that case it's the existing size,
8d419f
          * since we never want to shrink partitions. */
8d419f
 
8d419f
+        assert(context);
8d419f
+        assert(p);
8d419f
+
8d419f
         if (PARTITION_IS_FOREIGN(p)) {
8d419f
                 /* Don't allow changing size of partitions not managed by us */
8d419f
                 assert(p->current_size != UINT64_MAX);
8d419f
                 return p->current_size;
8d419f
         }
8d419f
 
8d419f
+        sm = round_down_size(p->size_max, context->grain_size);
8d419f
+
8d419f
         if (p->current_size != UINT64_MAX)
8d419f
-                return MAX(p->current_size, p->size_max);
8d419f
+                return MAX(p->current_size, sm);
8d419f
 
8d419f
-        return p->size_max;
8d419f
+        return sm;
8d419f
 }
8d419f
 
8d419f
-static uint64_t partition_min_size_with_padding(const Partition *p) {
8d419f
+static uint64_t partition_min_size_with_padding(Context *context, const Partition *p) {
8d419f
         uint64_t sz;
8d419f
 
8d419f
         /* Calculate the disk space we need for this partition plus any free space coming after it. This
8d419f
          * takes user configured padding into account as well as any additional whitespace needed to align
8d419f
          * the next partition to 4K again. */
8d419f
 
8d419f
-        sz = partition_min_size(p);
8d419f
+        assert(context);
8d419f
+        assert(p);
8d419f
+
8d419f
+        sz = partition_min_size(context, p);
8d419f
 
8d419f
         if (p->padding_min != UINT64_MAX)
8d419f
                 sz += p->padding_min;
8d419f
@@ -479,11 +494,11 @@ static uint64_t partition_min_size_with_padding(const Partition *p) {
8d419f
         if (PARTITION_EXISTS(p)) {
8d419f
                 /* If the partition wasn't aligned, add extra space so that any we might add will be aligned */
8d419f
                 assert(p->offset != UINT64_MAX);
8d419f
-                return round_up_size(p->offset + sz, 4096) - p->offset;
8d419f
+                return round_up_size(p->offset + sz, context->grain_size) - p->offset;
8d419f
         }
8d419f
 
8d419f
         /* If this is a new partition we'll place it aligned, hence we just need to round up the required size here */
8d419f
-        return round_up_size(sz, 4096);
8d419f
+        return round_up_size(sz, context->grain_size);
8d419f
 }
8d419f
 
8d419f
 static uint64_t free_area_available(const FreeArea *a) {
8d419f
@@ -495,9 +510,12 @@ static uint64_t free_area_available(const FreeArea *a) {
8d419f
         return a->size - a->allocated;
8d419f
 }
8d419f
 
8d419f
-static uint64_t free_area_available_for_new_partitions(const FreeArea *a) {
8d419f
+static uint64_t free_area_available_for_new_partitions(Context *context, const FreeArea *a) {
8d419f
         uint64_t avail;
8d419f
 
8d419f
+        assert(context);
8d419f
+        assert(a);
8d419f
+
8d419f
         /* Similar to free_area_available(), but takes into account that the required size and padding of the
8d419f
          * preceding partition is honoured. */
8d419f
 
8d419f
@@ -505,16 +523,16 @@ static uint64_t free_area_available_for_new_partitions(const FreeArea *a) {
8d419f
         if (a->after) {
8d419f
                 uint64_t need, space_end, new_end;
8d419f
 
8d419f
-                need = partition_min_size_with_padding(a->after);
8d419f
+                need = partition_min_size_with_padding(context, a->after);
8d419f
 
8d419f
                 assert(a->after->offset != UINT64_MAX);
8d419f
                 assert(a->after->current_size != UINT64_MAX);
8d419f
 
8d419f
                 /* Calculate where the free area ends, based on the offset of the partition preceding it */
8d419f
-                space_end = round_up_size(a->after->offset + a->after->current_size, 4096) + avail;
8d419f
+                space_end = round_up_size(a->after->offset + a->after->current_size, context->grain_size) + avail;
8d419f
 
8d419f
                 /* Calculate where the partition would end when we give it as much as it needs */
8d419f
-                new_end = round_up_size(a->after->offset + need, 4096);
8d419f
+                new_end = round_up_size(a->after->offset + need, context->grain_size);
8d419f
 
8d419f
                 /* Calculate saturated difference of the two: that's how much we have free for other partitions */
8d419f
                 return LESS_BY(space_end, new_end);
8d419f
@@ -523,15 +541,18 @@ static uint64_t free_area_available_for_new_partitions(const FreeArea *a) {
8d419f
         return avail;
8d419f
 }
8d419f
 
8d419f
-static int free_area_compare(FreeArea *const *a, FreeArea *const*b) {
8d419f
-        return CMP(free_area_available_for_new_partitions(*a),
8d419f
-                   free_area_available_for_new_partitions(*b));
8d419f
+static int free_area_compare(FreeArea *const *a, FreeArea *const*b, Context *context) {
8d419f
+        assert(context);
8d419f
+
8d419f
+        return CMP(free_area_available_for_new_partitions(context, *a),
8d419f
+                   free_area_available_for_new_partitions(context, *b));
8d419f
 }
8d419f
 
8d419f
-static uint64_t charge_size(uint64_t total, uint64_t amount) {
8d419f
+static uint64_t charge_size(Context *context, uint64_t total, uint64_t amount) {
8d419f
+        assert(context);
8d419f
         /* Subtract the specified amount from total, rounding up to multiple of 4K if there's room */
8d419f
         assert(amount <= total);
8d419f
-        return LESS_BY(total, round_up_size(amount, 4096));
8d419f
+        return LESS_BY(total, round_up_size(amount, context->grain_size));
8d419f
 }
8d419f
 
8d419f
 static uint64_t charge_weight(uint64_t total, uint64_t amount) {
8d419f
@@ -545,14 +566,14 @@ static bool context_allocate_partitions(Context *context, uint64_t *ret_largest_
8d419f
         assert(context);
8d419f
 
8d419f
         /* Sort free areas by size, putting smallest first */
8d419f
-        typesafe_qsort(context->free_areas, context->n_free_areas, free_area_compare);
8d419f
+        typesafe_qsort_r(context->free_areas, context->n_free_areas, free_area_compare, context);
8d419f
 
8d419f
         /* In any case return size of the largest free area (i.e. not the size of all free areas
8d419f
          * combined!) */
8d419f
         if (ret_largest_free_area)
8d419f
                 *ret_largest_free_area =
8d419f
                         context->n_free_areas == 0 ? 0 :
8d419f
-                        free_area_available_for_new_partitions(context->free_areas[context->n_free_areas-1]);
8d419f
+                        free_area_available_for_new_partitions(context, context->free_areas[context->n_free_areas-1]);
8d419f
 
8d419f
         /* A simple first-fit algorithm. We return true if we can fit the partitions in, otherwise false. */
8d419f
         LIST_FOREACH(partitions, p, context->partitions) {
8d419f
@@ -565,13 +586,13 @@ static bool context_allocate_partitions(Context *context, uint64_t *ret_largest_
8d419f
                         continue;
8d419f
 
8d419f
                 /* How much do we need to fit? */
8d419f
-                required = partition_min_size_with_padding(p);
8d419f
-                assert(required % 4096 == 0);
8d419f
+                required = partition_min_size_with_padding(context, p);
8d419f
+                assert(required % context->grain_size == 0);
8d419f
 
8d419f
                 for (size_t i = 0; i < context->n_free_areas; i++) {
8d419f
                         a = context->free_areas[i];
8d419f
 
8d419f
-                        if (free_area_available_for_new_partitions(a) >= required) {
8d419f
+                        if (free_area_available_for_new_partitions(context, a) >= required) {
8d419f
                                 fits = true;
8d419f
                                 break;
8d419f
                         }
8d419f
@@ -683,8 +704,8 @@ static int context_grow_partitions_phase(
8d419f
                         if (r < 0)
8d419f
                                 return r;
8d419f
 
8d419f
-                        rsz = partition_min_size(p);
8d419f
-                        xsz = partition_max_size(p);
8d419f
+                        rsz = partition_min_size(context, p);
8d419f
+                        xsz = partition_max_size(context, p);
8d419f
 
8d419f
                         if (phase == PHASE_OVERCHARGE && rsz > share) {
8d419f
                                 /* This partition needs more than its calculated share. Let's assign
8d419f
@@ -712,13 +733,13 @@ static int context_grow_partitions_phase(
8d419f
                                         /* Never change of foreign partitions (i.e. those we don't manage) */
8d419f
                                         p->new_size = p->current_size;
8d419f
                                 else
8d419f
-                                        p->new_size = MAX(round_down_size(share, 4096), rsz);
8d419f
+                                        p->new_size = MAX(round_down_size(share, context->grain_size), rsz);
8d419f
 
8d419f
                                 charge = true;
8d419f
                         }
8d419f
 
8d419f
                         if (charge) {
8d419f
-                                *span = charge_size(*span, p->new_size);
8d419f
+                                *span = charge_size(context, *span, p->new_size);
8d419f
                                 *weight_sum = charge_weight(*weight_sum, p->weight);
8d419f
                         }
8d419f
 
8d419f
@@ -742,7 +763,7 @@ static int context_grow_partitions_phase(
8d419f
                                 charge = try_again = true;
8d419f
                         } else if (phase == PHASE_DISTRIBUTE) {
8d419f
 
8d419f
-                                p->new_padding = round_down_size(share, 4096);
8d419f
+                                p->new_padding = round_down_size(share, context->grain_size);
8d419f
                                 if (p->padding_min != UINT64_MAX && p->new_padding < p->padding_min)
8d419f
                                         p->new_padding = p->padding_min;
8d419f
 
8d419f
@@ -750,7 +771,7 @@ static int context_grow_partitions_phase(
8d419f
                         }
8d419f
 
8d419f
                         if (charge) {
8d419f
-                                *span = charge_size(*span, p->new_padding);
8d419f
+                                *span = charge_size(context, *span, p->new_padding);
8d419f
                                 *weight_sum = charge_weight(*weight_sum, p->padding_weight);
8d419f
                         }
8d419f
 
8d419f
@@ -779,7 +800,7 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
8d419f
                 assert(a->after->offset != UINT64_MAX);
8d419f
                 assert(a->after->current_size != UINT64_MAX);
8d419f
 
8d419f
-                span += round_up_size(a->after->offset + a->after->current_size, 4096) - a->after->offset;
8d419f
+                span += round_up_size(a->after->offset + a->after->current_size, context->grain_size) - a->after->offset;
8d419f
         }
8d419f
 
8d419f
         for (GrowPartitionPhase phase = 0; phase < _GROW_PARTITION_PHASE_MAX;) {
8d419f
@@ -799,13 +820,13 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
8d419f
                 assert(a->after->new_size != UINT64_MAX);
8d419f
 
8d419f
                 /* Calculate new size and align (but ensure this doesn't shrink the size) */
8d419f
-                m = MAX(a->after->new_size, round_down_size(a->after->new_size + span, 4096));
8d419f
+                m = MAX(a->after->new_size, round_down_size(a->after->new_size + span, context->grain_size));
8d419f
 
8d419f
-                xsz = partition_max_size(a->after);
8d419f
+                xsz = partition_max_size(context, a->after);
8d419f
                 if (xsz != UINT64_MAX && m > xsz)
8d419f
                         m = xsz;
8d419f
 
8d419f
-                span = charge_size(span, m - a->after->new_size);
8d419f
+                span = charge_size(context, span, m - a->after->new_size);
8d419f
                 a->after->new_size = m;
8d419f
         }
8d419f
 
8d419f
@@ -824,13 +845,13 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
8d419f
                                 continue;
8d419f
 
8d419f
                         assert(p->new_size != UINT64_MAX);
8d419f
-                        m = MAX(p->new_size, round_down_size(p->new_size + span, 4096));
8d419f
+                        m = MAX(p->new_size, round_down_size(p->new_size + span, context->grain_size));
8d419f
 
8d419f
-                        xsz = partition_max_size(p);
8d419f
+                        xsz = partition_max_size(context, p);
8d419f
                         if (xsz != UINT64_MAX && m > xsz)
8d419f
                                 m = xsz;
8d419f
 
8d419f
-                        span = charge_size(span, m - p->new_size);
8d419f
+                        span = charge_size(context, span, m - p->new_size);
8d419f
                         p->new_size = m;
8d419f
 
8d419f
                         if (span == 0)
8d419f
@@ -910,7 +931,7 @@ static void context_place_partitions(Context *context) {
8d419f
                 } else
8d419f
                         start = context->start;
8d419f
 
8d419f
-                start = round_up_size(start, 4096);
8d419f
+                start = round_up_size(start, context->grain_size);
8d419f
                 left = a->size;
8d419f
 
8d419f
                 LIST_FOREACH(partitions, p, context->partitions) {
8d419f
@@ -1422,6 +1443,8 @@ static int determine_current_padding(
8d419f
                 struct fdisk_context *c,
8d419f
                 struct fdisk_table *t,
8d419f
                 struct fdisk_partition *p,
8d419f
+                uint64_t secsz,
8d419f
+                uint64_t grainsz,
8d419f
                 uint64_t *ret) {
8d419f
 
8d419f
         size_t n_partitions;
8d419f
@@ -1435,8 +1458,8 @@ static int determine_current_padding(
8d419f
                 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition has no end!");
8d419f
 
8d419f
         offset = fdisk_partition_get_end(p);
8d419f
-        assert(offset < UINT64_MAX / 512);
8d419f
-        offset *= 512;
8d419f
+        assert(offset < UINT64_MAX / secsz);
8d419f
+        offset *= secsz;
8d419f
 
8d419f
         n_partitions = fdisk_table_get_nents(t);
8d419f
         for (size_t i = 0; i < n_partitions; i++)  {
8d419f
@@ -1454,8 +1477,8 @@ static int determine_current_padding(
8d419f
                         continue;
8d419f
 
8d419f
                 start = fdisk_partition_get_start(q);
8d419f
-                assert(start < UINT64_MAX / 512);
8d419f
-                start *= 512;
8d419f
+                assert(start < UINT64_MAX / secsz);
8d419f
+                start *= secsz;
8d419f
 
8d419f
                 if (start >= offset && (next == UINT64_MAX || next > start))
8d419f
                         next = start;
8d419f
@@ -1467,16 +1490,16 @@ static int determine_current_padding(
8d419f
                 assert(next < UINT64_MAX);
8d419f
                 next++; /* The last LBA is one sector before the end */
8d419f
 
8d419f
-                assert(next < UINT64_MAX / 512);
8d419f
-                next *= 512;
8d419f
+                assert(next < UINT64_MAX / secsz);
8d419f
+                next *= secsz;
8d419f
 
8d419f
                 if (offset > next)
8d419f
                         return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition end beyond disk end.");
8d419f
         }
8d419f
 
8d419f
         assert(next >= offset);
8d419f
-        offset = round_up_size(offset, 4096);
8d419f
-        next = round_down_size(next, 4096);
8d419f
+        offset = round_up_size(offset, grainsz);
8d419f
+        next = round_down_size(next, grainsz);
8d419f
 
8d419f
         *ret = LESS_BY(next, offset); /* Saturated subtraction, rounding might have fucked things up */
8d419f
         return 0;
8d419f
@@ -1549,6 +1572,8 @@ static int context_load_partition_table(
8d419f
         bool from_scratch = false;
8d419f
         sd_id128_t disk_uuid;
8d419f
         size_t n_partitions;
8d419f
+        unsigned long secsz;
8d419f
+        uint64_t grainsz;
8d419f
         int r;
8d419f
 
8d419f
         assert(context);
8d419f
@@ -1583,8 +1608,12 @@ static int context_load_partition_table(
8d419f
                 if (r < 0)
8d419f
                         return log_error_errno(errno, "Failed to stat block device '%s': %m", node);
8d419f
 
8d419f
-                if (S_ISREG(st.st_mode) && st.st_size == 0)
8d419f
+                if (S_ISREG(st.st_mode) && st.st_size == 0) {
8d419f
+                        /* User the fallback values if we have no better idea */
8d419f
+                        context->sector_size = 512;
8d419f
+                        context->grain_size = 4096;
8d419f
                         return /* from_scratch = */ true;
8d419f
+                }
8d419f
 
8d419f
                 r = -EINVAL;
8d419f
         }
8d419f
@@ -1602,6 +1631,23 @@ static int context_load_partition_table(
8d419f
         if (flock(fdisk_get_devfd(c), arg_dry_run ? LOCK_SH : LOCK_EX) < 0)
8d419f
                 return log_error_errno(errno, "Failed to lock block device: %m");
8d419f
 
8d419f
+        /* The offsets/sizes libfdisk returns to us will be in multiple of the sector size of the
8d419f
+         * device. This is typically 512, and sometimes 4096. Let's query libfdisk once for it, and then use
8d419f
+         * it for all our needs. Note that the values we use ourselves always are in bytes though, thus mean
8d419f
+         * the same thing universally. Also note that regardless what kind of sector size is in use we'll
8d419f
+         * place partitions at multiples of 4K. */
8d419f
+        secsz = fdisk_get_sector_size(c);
8d419f
+
8d419f
+        /* Insist on a power of two, and that it's a multiple of 512, i.e. the traditional sector size. */
8d419f
+        if (secsz < 512 || secsz != 1UL << log2u64(secsz))
8d419f
+                return log_error_errno(errno, "Sector size %lu is not a power of two larger than 512? Refusing.", secsz);
8d419f
+
8d419f
+        /* Use at least 4K, and ensure it's a multiple of the sector size, regardless if that is smaller or
8d419f
+         * larger */
8d419f
+        grainsz = secsz < 4096 ? 4096 : secsz;
8d419f
+
8d419f
+        log_debug("Sector size of device is %lu bytes. Using grain size of %" PRIu64 ".", secsz, grainsz);
8d419f
+
8d419f
         switch (arg_empty) {
8d419f
 
8d419f
         case EMPTY_REFUSE:
8d419f
@@ -1732,12 +1778,12 @@ static int context_load_partition_table(
8d419f
                 }
8d419f
 
8d419f
                 sz = fdisk_partition_get_size(p);
8d419f
-                assert_se(sz <= UINT64_MAX/512);
8d419f
-                sz *= 512;
8d419f
+                assert_se(sz <= UINT64_MAX/secsz);
8d419f
+                sz *= secsz;
8d419f
 
8d419f
                 start = fdisk_partition_get_start(p);
8d419f
-                assert_se(start <= UINT64_MAX/512);
8d419f
-                start *= 512;
8d419f
+                assert_se(start <= UINT64_MAX/secsz);
8d419f
+                start *= secsz;
8d419f
 
8d419f
                 partno = fdisk_partition_get_partno(p);
8d419f
 
8d419f
@@ -1762,7 +1808,7 @@ static int context_load_partition_table(
8d419f
                                 pp->current_partition = p;
8d419f
                                 fdisk_ref_partition(p);
8d419f
 
8d419f
-                                r = determine_current_padding(c, t, p, &pp->current_padding);
8d419f
+                                r = determine_current_padding(c, t, p, secsz, grainsz, &pp->current_padding);
8d419f
                                 if (r < 0)
8d419f
                                         return r;
8d419f
 
8d419f
@@ -1795,7 +1841,7 @@ static int context_load_partition_table(
8d419f
                         np->current_partition = p;
8d419f
                         fdisk_ref_partition(p);
8d419f
 
8d419f
-                        r = determine_current_padding(c, t, p, &np->current_padding);
8d419f
+                        r = determine_current_padding(c, t, p, secsz, grainsz, &np->current_padding);
8d419f
                         if (r < 0)
8d419f
                                 return r;
8d419f
 
8d419f
@@ -1812,26 +1858,26 @@ static int context_load_partition_table(
8d419f
 
8d419f
 add_initial_free_area:
8d419f
         nsectors = fdisk_get_nsectors(c);
8d419f
-        assert(nsectors <= UINT64_MAX/512);
8d419f
-        nsectors *= 512;
8d419f
+        assert(nsectors <= UINT64_MAX/secsz);
8d419f
+        nsectors *= secsz;
8d419f
 
8d419f
         first_lba = fdisk_get_first_lba(c);
8d419f
-        assert(first_lba <= UINT64_MAX/512);
8d419f
-        first_lba *= 512;
8d419f
+        assert(first_lba <= UINT64_MAX/secsz);
8d419f
+        first_lba *= secsz;
8d419f
 
8d419f
         last_lba = fdisk_get_last_lba(c);
8d419f
         assert(last_lba < UINT64_MAX);
8d419f
         last_lba++;
8d419f
-        assert(last_lba <= UINT64_MAX/512);
8d419f
-        last_lba *= 512;
8d419f
+        assert(last_lba <= UINT64_MAX/secsz);
8d419f
+        last_lba *= secsz;
8d419f
 
8d419f
         assert(last_lba >= first_lba);
8d419f
 
8d419f
         if (left_boundary == UINT64_MAX) {
8d419f
                 /* No partitions at all? Then the whole disk is up for grabs. */
8d419f
 
8d419f
-                first_lba = round_up_size(first_lba, 4096);
8d419f
-                last_lba = round_down_size(last_lba, 4096);
8d419f
+                first_lba = round_up_size(first_lba, grainsz);
8d419f
+                last_lba = round_down_size(last_lba, grainsz);
8d419f
 
8d419f
                 if (last_lba > first_lba) {
8d419f
                         r = context_add_free_area(context, last_lba - first_lba, NULL);
8d419f
@@ -1842,9 +1888,9 @@ add_initial_free_area:
8d419f
                 /* Add space left of first partition */
8d419f
                 assert(left_boundary >= first_lba);
8d419f
 
8d419f
-                first_lba = round_up_size(first_lba, 4096);
8d419f
-                left_boundary = round_down_size(left_boundary, 4096);
8d419f
-                last_lba = round_down_size(last_lba, 4096);
8d419f
+                first_lba = round_up_size(first_lba, grainsz);
8d419f
+                left_boundary = round_down_size(left_boundary, grainsz);
8d419f
+                last_lba = round_down_size(last_lba, grainsz);
8d419f
 
8d419f
                 if (left_boundary > first_lba) {
8d419f
                         r = context_add_free_area(context, left_boundary - first_lba, NULL);
8d419f
@@ -1856,6 +1902,8 @@ add_initial_free_area:
8d419f
         context->start = first_lba;
8d419f
         context->end = last_lba;
8d419f
         context->total = nsectors;
8d419f
+        context->sector_size = secsz;
8d419f
+        context->grain_size = grainsz;
8d419f
         context->fdisk_context = TAKE_PTR(c);
8d419f
 
8d419f
         return from_scratch;
8d419f
@@ -2360,7 +2408,7 @@ static int context_discard_range(
8d419f
         if (S_ISBLK(st.st_mode)) {
8d419f
                 uint64_t range[2], end;
8d419f
 
8d419f
-                range[0] = round_up_size(offset, 512);
8d419f
+                range[0] = round_up_size(offset, context->sector_size);
8d419f
 
8d419f
                 if (offset > UINT64_MAX - size)
8d419f
                         return -ERANGE;
8d419f
@@ -2369,7 +2417,7 @@ static int context_discard_range(
8d419f
                 if (end <= range[0])
8d419f
                         return 0;
8d419f
 
8d419f
-                range[1] = round_down_size(end - range[0], 512);
8d419f
+                range[1] = round_down_size(end - range[0], context->sector_size);
8d419f
                 if (range[1] <= 0)
8d419f
                         return 0;
8d419f
 
8d419f
@@ -2519,6 +2567,7 @@ static int context_wipe_and_discard(Context *context, bool from_scratch) {
8d419f
 }
8d419f
 
8d419f
 static int partition_encrypt(
8d419f
+                Context *context,
8d419f
                 Partition *p,
8d419f
                 const char *node,
8d419f
                 struct crypt_device **ret_cd,
8d419f
@@ -2532,6 +2581,7 @@ static int partition_encrypt(
8d419f
         sd_id128_t uuid;
8d419f
         int r;
8d419f
 
8d419f
+        assert(context);
8d419f
         assert(p);
8d419f
         assert(p->encrypt != ENCRYPT_OFF);
8d419f
 
8d419f
@@ -2579,7 +2629,7 @@ static int partition_encrypt(
8d419f
                          volume_key_size,
8d419f
                          &(struct crypt_params_luks2) {
8d419f
                                  .label = strempty(p->new_label),
8d419f
-                                 .sector_size = 512U,
8d419f
+                                 .sector_size = context->sector_size,
8d419f
                          });
8d419f
         if (r < 0)
8d419f
                 return log_error_errno(r, "Failed to LUKS2 format future partition: %m");
8d419f
@@ -2735,7 +2785,7 @@ static int context_copy_blocks(Context *context) {
8d419f
                         if (r < 0)
8d419f
                                 return log_error_errno(r, "Failed to lock loopback device: %m");
8d419f
 
8d419f
-                        r = partition_encrypt(p, d->node, &cd, &encrypted, &encrypted_dev_fd);
8d419f
+                        r = partition_encrypt(context, p, d->node, &cd, &encrypted, &encrypted_dev_fd);
8d419f
                         if (r < 0)
8d419f
                                 return log_error_errno(r, "Failed to encrypt device: %m");
8d419f
 
8d419f
@@ -2988,7 +3038,7 @@ static int context_mkfs(Context *context) {
8d419f
                         return log_error_errno(r, "Failed to lock loopback device: %m");
8d419f
 
8d419f
                 if (p->encrypt != ENCRYPT_OFF) {
8d419f
-                        r = partition_encrypt(p, d->node, &cd, &encrypted, &encrypted_dev_fd);
8d419f
+                        r = partition_encrypt(context, p, d->node, &cd, &encrypted, &encrypted_dev_fd);
8d419f
                         if (r < 0)
8d419f
                                 return log_error_errno(r, "Failed to encrypt device: %m");
8d419f
 
8d419f
@@ -3307,13 +3357,13 @@ static int context_mangle_partitions(Context *context) {
8d419f
 
8d419f
                         if (p->new_size != p->current_size) {
8d419f
                                 assert(p->new_size >= p->current_size);
8d419f
-                                assert(p->new_size % 512 == 0);
8d419f
+                                assert(p->new_size % context->sector_size == 0);
8d419f
 
8d419f
                                 r = fdisk_partition_size_explicit(p->current_partition, true);
8d419f
                                 if (r < 0)
8d419f
                                         return log_error_errno(r, "Failed to enable explicit sizing: %m");
8d419f
 
8d419f
-                                r = fdisk_partition_set_size(p->current_partition, p->new_size / 512);
8d419f
+                                r = fdisk_partition_set_size(p->current_partition, p->new_size / context->sector_size);
8d419f
                                 if (r < 0)
8d419f
                                         return log_error_errno(r, "Failed to grow partition: %m");
8d419f
 
8d419f
@@ -3353,8 +3403,8 @@ static int context_mangle_partitions(Context *context) {
8d419f
                         _cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL;
8d419f
 
8d419f
                         assert(!p->new_partition);
8d419f
-                        assert(p->offset % 512 == 0);
8d419f
-                        assert(p->new_size % 512 == 0);
8d419f
+                        assert(p->offset % context->sector_size == 0);
8d419f
+                        assert(p->new_size % context->sector_size == 0);
8d419f
                         assert(!sd_id128_is_null(p->new_uuid));
8d419f
                         assert(p->new_label);
8d419f
 
8d419f
@@ -3378,11 +3428,11 @@ static int context_mangle_partitions(Context *context) {
8d419f
                         if (r < 0)
8d419f
                                 return log_error_errno(r, "Failed to enable explicit sizing: %m");
8d419f
 
8d419f
-                        r = fdisk_partition_set_start(q, p->offset / 512);
8d419f
+                        r = fdisk_partition_set_start(q, p->offset / context->sector_size);
8d419f
                         if (r < 0)
8d419f
                                 return log_error_errno(r, "Failed to position partition: %m");
8d419f
 
8d419f
-                        r = fdisk_partition_set_size(q, p->new_size / 512);
8d419f
+                        r = fdisk_partition_set_size(q, p->new_size / context->sector_size);
8d419f
                         if (r < 0)
8d419f
                                 return log_error_errno(r, "Failed to grow partition: %m");
8d419f
 
8d419f
@@ -4746,18 +4796,20 @@ done:
8d419f
 }
8d419f
 
8d419f
 static int determine_auto_size(Context *c) {
8d419f
-        uint64_t sum = round_up_size(GPT_METADATA_SIZE, 4096);
8d419f
+        uint64_t sum;
8d419f
         Partition *p;
8d419f
 
8d419f
         assert_se(c);
8d419f
 
8d419f
+        sum = round_up_size(GPT_METADATA_SIZE, 4096);
8d419f
+
8d419f
         LIST_FOREACH(partitions, p, c->partitions) {
8d419f
                 uint64_t m;
8d419f
 
8d419f
                 if (p->dropped)
8d419f
                         continue;
8d419f
 
8d419f
-                m = partition_min_size_with_padding(p);
8d419f
+                m = partition_min_size_with_padding(c, p);
8d419f
                 if (m > UINT64_MAX - sum)
8d419f
                         return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Image would grow too large, refusing.");
8d419f