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;
[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);
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) {
left = a->size;
LIST_FOREACH(partitions, p, context->partitions) {
+ uint64_t gap;
+
if (p->allocated_to_area != a)
continue;
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;
}
}
}
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 */
}
/* 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;
}
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);
" --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"
ARG_DEFER_PARTITIONS_EMPTY,
ARG_DEFER_PARTITIONS_FACTORY_RESET,
ARG_SECTOR_SIZE,
+ ARG_GRAIN_SIZE,
ARG_SKIP_PARTITIONS,
ARG_ARCHITECTURE,
ARG_OFFLINE,
{ "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 },
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)