repart: add --grain-size= option for partition alignment
Add a --grain-size= CLI option to override the default 4 KiB partition alignment grain. Setting --grain-size=1M matches the alignment used by fdisk/parted and fixes misaligned partitions after small fixed-size partitions like the 16 KiB verity-sig partition. Also fix context_place_partitions() to re-align the start offset after each partition, not just once per free area. Without this, a small partition would cause all subsequent partitions in the same free area to start at an unaligned offset.
This commit is contained in:
committed by
Luca Boccassi
parent
3236700a67
commit
36d129a7ad
@@ -535,6 +535,19 @@
|
||||
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--grain-size=<replaceable>BYTES</replaceable></option></term>
|
||||
|
||||
<listitem><para>This option controls the partition alignment granularity used when placing
|
||||
partitions. It takes a power-of-2 value that is at least the sector size. All partition start
|
||||
offsets will be rounded up to a multiple of this value. Defaults to
|
||||
<constant>MAX(4096, sector_size)</constant>, matching the conventional 4 KiB alignment. Setting
|
||||
this to <literal>1M</literal> ensures proper alignment for modern storage devices even after
|
||||
small fixed-size partitions such as a verity signature partition.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--architecture=<replaceable>ARCH</replaceable></option></term>
|
||||
|
||||
|
||||
+40
-6
@@ -200,6 +200,7 @@ static size_t arg_n_defer_partitions = 0;
|
||||
static bool arg_defer_partitions_empty = false;
|
||||
static bool arg_defer_partitions_factory_reset = false;
|
||||
static uint64_t arg_sector_size = 0;
|
||||
static uint64_t arg_grain_size = 0;
|
||||
static ImagePolicy *arg_image_policy = NULL;
|
||||
static Architecture arg_architecture = _ARCHITECTURE_INVALID;
|
||||
static int arg_offline = -1;
|
||||
@@ -617,6 +618,10 @@ static const char *progress_phase_table[_PROGRESS_PHASE_MAX] = {
|
||||
[PROGRESS_REREADING_TABLE] = "rereading-table",
|
||||
};
|
||||
|
||||
static uint64_t determine_grain_size(uint64_t sector_size) {
|
||||
return MAX(arg_grain_size > 0 ? arg_grain_size : 4096U, sector_size);
|
||||
}
|
||||
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(empty_mode, EmptyMode);
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(append_mode, AppendMode);
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(encrypt_mode, EncryptMode, ENCRYPT_KEY_FILE);
|
||||
@@ -1701,7 +1706,7 @@ static void context_place_partitions(Context *context) {
|
||||
|
||||
for (size_t i = 0; i < context->n_free_areas; i++) {
|
||||
FreeArea *a = context->free_areas[i];
|
||||
_unused_ uint64_t left;
|
||||
uint64_t left;
|
||||
uint64_t start;
|
||||
|
||||
if (a->after) {
|
||||
@@ -1717,6 +1722,8 @@ static void context_place_partitions(Context *context) {
|
||||
left = a->size;
|
||||
|
||||
LIST_FOREACH(partitions, p, context->partitions) {
|
||||
uint64_t gap;
|
||||
|
||||
if (p->allocated_to_area != a)
|
||||
continue;
|
||||
|
||||
@@ -1730,6 +1737,21 @@ static void context_place_partitions(Context *context) {
|
||||
assert(left >= p->new_padding);
|
||||
start += p->new_padding;
|
||||
left -= p->new_padding;
|
||||
|
||||
/* Re-align start to the grain after each partition, so that the next
|
||||
* partition placed into this free area also starts on a grain boundary.
|
||||
* This matters when the grain is larger than the default (e.g. 1 MiB via
|
||||
* --grain-size=) and a small partition like verity-sig (16 KiB) precedes
|
||||
* a larger one: without this, the successor would start at an unaligned
|
||||
* offset. */
|
||||
gap = round_up_size(start, context->grain_size) - start;
|
||||
if (gap > left) {
|
||||
log_warning("Not enough space left in free area to re-align partition start to grain size, "
|
||||
"next partition may start at an unaligned offset.");
|
||||
gap = 0;
|
||||
}
|
||||
start += gap;
|
||||
left -= gap;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3579,7 +3601,7 @@ static int context_load_fallback_metrics(Context *context) {
|
||||
assert(context);
|
||||
|
||||
context->sector_size = arg_sector_size > 0 ? arg_sector_size : 512;
|
||||
context->grain_size = MAX(context->sector_size, 4096U);
|
||||
context->grain_size = determine_grain_size(context->sector_size);
|
||||
context->default_fs_sector_size = arg_sector_size > 0 ? arg_sector_size : DEFAULT_FILESYSTEM_SECTOR_SIZE;
|
||||
return 1; /* Starting from scratch */
|
||||
}
|
||||
@@ -3672,7 +3694,7 @@ static int context_load_partition_table(Context *context) {
|
||||
/* Use the fallback values if we have no better idea */
|
||||
context->sector_size = fdisk_get_sector_size(c);
|
||||
context->default_fs_sector_size = fs_secsz;
|
||||
context->grain_size = MAX(context->sector_size, 4096U);
|
||||
context->grain_size = determine_grain_size(context->sector_size);
|
||||
return /* from_scratch= */ true;
|
||||
}
|
||||
|
||||
@@ -3701,9 +3723,9 @@ static int context_load_partition_table(Context *context) {
|
||||
if (secsz < 512 || !ISPOWEROF2(secsz))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Sector size %lu is not a power of two larger than 512? Refusing.", secsz);
|
||||
|
||||
/* Use at least 4K, and ensure it's a multiple of the sector size, regardless if that is smaller or
|
||||
* larger */
|
||||
grainsz = MAX(secsz, 4096U);
|
||||
/* Determine the grain size: by default at least 4K and a multiple of the sector size, but may be
|
||||
* overridden via --grain-size=. */
|
||||
grainsz = determine_grain_size(secsz);
|
||||
|
||||
log_debug("Sector size of device is %lu bytes. Using default filesystem sector size of %" PRIu64 " and grain size of %" PRIu64 ".", secsz, fs_secsz, grainsz);
|
||||
|
||||
@@ -9069,6 +9091,7 @@ static int help(void) {
|
||||
" --offline=BOOL Whether to build the image offline\n"
|
||||
" --discard=BOOL Whether to discard backing blocks for new partitions\n"
|
||||
" --sector-size=SIZE Set the logical sector size for the image\n"
|
||||
" --grain-size=BYTES Set the grain size for partition alignment\n"
|
||||
" --architecture=ARCH Set the generic architecture for the image\n"
|
||||
" --size=BYTES Grow loopback file to specified size\n"
|
||||
" --seed=UUID 128-bit seed UUID to derive all UUIDs from\n"
|
||||
@@ -9199,6 +9222,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_DEFER_PARTITIONS_EMPTY,
|
||||
ARG_DEFER_PARTITIONS_FACTORY_RESET,
|
||||
ARG_SECTOR_SIZE,
|
||||
ARG_GRAIN_SIZE,
|
||||
ARG_SKIP_PARTITIONS,
|
||||
ARG_ARCHITECTURE,
|
||||
ARG_OFFLINE,
|
||||
@@ -9248,6 +9272,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "defer-partitions-empty", required_argument, NULL, ARG_DEFER_PARTITIONS_EMPTY },
|
||||
{ "defer-partitions-factory-reset", required_argument, NULL, ARG_DEFER_PARTITIONS_FACTORY_RESET },
|
||||
{ "sector-size", required_argument, NULL, ARG_SECTOR_SIZE },
|
||||
{ "grain-size", required_argument, NULL, ARG_GRAIN_SIZE },
|
||||
{ "architecture", required_argument, NULL, ARG_ARCHITECTURE },
|
||||
{ "offline", required_argument, NULL, ARG_OFFLINE },
|
||||
{ "copy-from", required_argument, NULL, ARG_COPY_FROM },
|
||||
@@ -9570,6 +9595,15 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
break;
|
||||
|
||||
case ARG_GRAIN_SIZE:
|
||||
r = parse_size(optarg, 1024, &arg_grain_size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --grain-size= parameter: %s", optarg);
|
||||
if (arg_grain_size < 512 || !ISPOWEROF2(arg_grain_size))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Grain size must be a power of 2 >= 512.");
|
||||
|
||||
break;
|
||||
|
||||
case ARG_ARCHITECTURE:
|
||||
r = architecture_from_string(optarg);
|
||||
if (r < 0)
|
||||
|
||||
Reference in New Issue
Block a user