git: 729883c93e7f - main - mkimg: Add per-partition alignment

From: Colin Percival <cperciva_at_FreeBSD.org>
Date: Wed, 21 Oct 2026 20:48:33 UTC
The branch main has been updated by cperciva:

URL: https://cgit.FreeBSD.org/src/commit/?id=729883c93e7fd89a4b88e8b9a7ef42938a431efa

commit 729883c93e7fd89a4b88e8b9a7ef42938a431efa
Author:     Colin Percival <cperciva@FreeBSD.org>
AuthorDate: 2026-10-07 21:51:26 +0000
Commit:     Colin Percival <cperciva@FreeBSD.org>
CommitDate: 2026-10-21 19:42:43 +0000

    mkimg: Add per-partition alignment
    
    This adds a "%align" field to partition specifications, resulting in
    the start of a partition being shifted to the next aligned position.
    When both :offset and %align are used, a relative offset is treated as
    a minimum value, while an unaligned absolute offset is an error.
    
    Reviewed by:    imp (earlier version)
    MFC after:      1 week
    Sponsored by:   Amazon
    Differential Revision:  https://reviews.freebsd.org/D60450
---
 usr.bin/mkimg/mkimg.c | 62 +++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 50 insertions(+), 12 deletions(-)

diff --git a/usr.bin/mkimg/mkimg.c b/usr.bin/mkimg/mkimg.c
index b727b43683a3..a1881b114b7f 100644
--- a/usr.bin/mkimg/mkimg.c
+++ b/usr.bin/mkimg/mkimg.c
@@ -178,13 +178,15 @@ usage(const char *why)
 	print_schemes(1);
 	fputc('\n', stderr);
 	fprintf(stderr, "    partition specification:\n");
-	fprintf(stderr, "\t<type>[/<label>]::<size>[:[+]<offset>]\t-  "
-	    "empty partition of given size and\n\t\t\t\t\t"
-	    "   optional relative or absolute offset\n");
-	fprintf(stderr, "\t<type>[/<label>]:=<file>[:[+]offset]\t-  partition "
-	    "content and size are\n\t\t\t\t\t"
-	    "   determined by the named file and\n"
-	    "\t\t\t\t\t   optional relative or absolute offset\n");
+	fprintf(stderr, "\t<type>[/<label>]::<size>[:[+]<offset>][%%<align>]\n"
+	    "\t\t\t\t\t-  empty partition of given size and\n"
+	    "\t\t\t\t\t   optional relative or absolute offset\n"
+	    "\t\t\t\t\t   and alignment\n");
+	fprintf(stderr, "\t<type>[/<label>]:=<file>[:[+]<offset>][%%<align>]\n"
+	    "\t\t\t\t\t-  partition content and size are\n"
+	    "\t\t\t\t\t   determined by the named file and\n"
+	    "\t\t\t\t\t   optional relative or absolute offset\n"
+	    "\t\t\t\t\t   and alignment\n");
 	fprintf(stderr, "\t<type>[/<label>]:-<cmd>\t\t-  partition content and size "
 	    "are taken\n\t\t\t\t\t   from the output of the command to run\n");
 	fprintf(stderr, "\t-\t\t\t\t-  unused partition entry\n");
@@ -448,9 +450,9 @@ mkimg(void)
 	struct part *part;
 	struct stat sb;
 	char *p;
-	lba_t block, blkoffset;
-	uint64_t bytesize, byteoffset;
-	char *size, *offset;
+	lba_t block, blkoffset, blkalign;
+	uint64_t bytesize, byteoffset, bytealign;
+	char *size, *offset, *align;
 	bool abs_offset;
 	int error, fd;
 
@@ -466,10 +468,16 @@ mkimg(void)
 	TAILQ_FOREACH(part, &partlist, link) {
 		byteoffset = blkoffset = 0;
 		abs_offset = false;
+		align = NULL;
 
 		/* Look for an offset. Set size too if we can. */
 		switch (part->kind) {
 		case PART_KIND_SIZE:
+			p = strrchr(part->contents, '%');
+			if (p != NULL) {
+				*p = '\0';
+				align = p + 1;
+			}
 			offset = part->contents;
 			size = strsep(&offset, ":");
 			if (expand_number(size, &bytesize) == -1)
@@ -477,6 +485,13 @@ mkimg(void)
 			break;
 		case PART_KIND_FILE:
 			size = part->contents;
+			if (stat(part->contents, &sb) == -1) {
+				p = strrchr(part->contents, '%');
+				if (p != NULL) {
+					*p = '\0';
+					align = p + 1;
+				}
+			}
 			if (stat(part->contents, &sb) == 0) {
 				if (S_ISDIR(sb.st_mode)) {
 					errc(EX_IOERR, EISDIR, "partition %d",
@@ -498,6 +513,18 @@ mkimg(void)
 			break;
 		}
 
+		if (align != NULL) {
+			if (expand_number(align, &bytealign) == -1)
+				err(EX_DATAERR, "partition %d",
+				    part->index + 1);
+			if ((bytealign == 0) || (bytealign % secsz != 0))
+				errx(EX_DATAERR, "partition %d: "
+				    "invalid alignment", part->index + 1);
+			blkalign = bytealign / secsz;
+		} else {
+			blkalign = 1;
+		}
+
 		if (offset != NULL) {
 			if (*offset != '+')
 				abs_offset = true;
@@ -509,12 +536,17 @@ mkimg(void)
 
 		/* Work out exactly where the partition starts. */
 		blkoffset = (byteoffset + secsz - 1) / secsz;
-		if (abs_offset)
+		if (abs_offset) {
 			block = scheme_metadata(SCHEME_META_PART_ABSOLUTE,
 			    blkoffset);
-		else
+			if (block % blkalign)
+				errx(EX_DATAERR, "partition %d: absolute "
+				    "offset incompatible with alignment",
+				    part->index + 1);
+		} else
 			block = scheme_metadata(SCHEME_META_PART_BEFORE,
 			    block + blkoffset);
+		block = ((block + blkalign - 1) / blkalign) * blkalign;
 		part->block = block;
 
 		if (verbose)
@@ -559,6 +591,12 @@ mkimg(void)
 				    (long long)byteoffset,
 				    (long long)blkoffset);
 			}
+			if (blkalign > 1) {
+				fprintf(stderr,
+				    "    alignment %llu bytes (%llu blocks)\n",
+				    (long long)bytealign,
+				    (long long)blkalign);
+			}
 		}
 		block = scheme_metadata(SCHEME_META_PART_AFTER,
 		    part->block + part->size);