mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH 0/6] GPT: fixes around last_usable_lba
@ 2026-07-01  9:27 Sascha Hauer
  2026-07-01  9:27 ` [PATCH 1/6] partitions: gpt: Adjust last_usable_lba to real device size when writing Sascha Hauer
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Sascha Hauer @ 2026-07-01  9:27 UTC (permalink / raw)
  To: BAREBOX; +Cc: Sascha Hauer

The last_usable_lba field in a GPT specifies just that: The last LBA we
are allowed to use for partitions. There are some bugs around that fixed
in this series:

- When creating partitions we never actually check against
  last_usable_lba, but instead the device end.
- When writing a disk image to a device the last_usable_lba is usually
  set to some value matching the image size. When writing the partition
  table later, adjust the value for the actual device size so that the
  full device becomes usable

Also some logging fixes/cleanups included

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
Sascha Hauer (6):
      partitions: gpt: Adjust last_usable_lba to real device size when writing
      partitions: gpt: refresh also when primary or alternate is invalid
      partitions: gpt: validate mkpart range against GPT usable LBAs
      partitions: check new partitions against usable LBAs, not device size
      partitions: gpt: fix wrong error messages
      partitions: gpt: log a message when partition table refreshed

 commands/parted.c       |  8 +-------
 common/partitions.c     | 28 +++++++++++++++++++-------
 common/partitions/efi.c | 53 +++++++++++++++++++++++++++++++++++--------------
 include/partitions.h    |  2 ++
 4 files changed, 62 insertions(+), 29 deletions(-)
---
base-commit: 18599acfcbf38e95edc605f89e257e32f67d0ac4
change-id: 20260701-gpt-size-fixes-fa23624e09b0

Best regards,
-- 
Sascha Hauer <s.hauer@pengutronix.de>




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/6] partitions: gpt: Adjust last_usable_lba to real device size when writing
  2026-07-01  9:27 [PATCH 0/6] GPT: fixes around last_usable_lba Sascha Hauer
@ 2026-07-01  9:27 ` Sascha Hauer
  2026-07-01  9:27 ` [PATCH 2/6] partitions: gpt: refresh also when primary or alternate is invalid Sascha Hauer
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Sascha Hauer @ 2026-07-01  9:27 UTC (permalink / raw)
  To: BAREBOX

When writing disk images to a device the last_usable_lba field in the GPT
header normally doesn't match the actual device size. Adjust the field
to the actual device size when writing a partition table.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/partitions/efi.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/common/partitions/efi.c b/common/partitions/efi.c
index 674f920743..28198f25b1 100644
--- a/common/partitions/efi.c
+++ b/common/partitions/efi.c
@@ -862,6 +862,8 @@ static int __efi_partition_write(struct efi_partition_desc *epd, bool primary)
 
 	size = count / GPT_BLOCK_SIZE;
 
+	gpt->last_usable_lba = cpu_to_le64(last_lba(blk) - (size + 2));
+
 	if (primary) {
 		my_lba = 1;
 

-- 
2.47.3




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 2/6] partitions: gpt: refresh also when primary or alternate is invalid
  2026-07-01  9:27 [PATCH 0/6] GPT: fixes around last_usable_lba Sascha Hauer
  2026-07-01  9:27 ` [PATCH 1/6] partitions: gpt: Adjust last_usable_lba to real device size when writing Sascha Hauer
@ 2026-07-01  9:27 ` Sascha Hauer
  2026-07-01  9:27 ` [PATCH 3/6] partitions: gpt: validate mkpart range against GPT usable LBAs Sascha Hauer
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Sascha Hauer @ 2026-07-01  9:27 UTC (permalink / raw)
  To: BAREBOX

Currently we only refresh/repair when primary and alternate GPT differ. When
either of them is missing, we should repair as well.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/partitions/efi.c | 32 +++++++++++++++++++++-----------
 1 file changed, 21 insertions(+), 11 deletions(-)

diff --git a/common/partitions/efi.c b/common/partitions/efi.c
index 28198f25b1..98172672ff 100644
--- a/common/partitions/efi.c
+++ b/common/partitions/efi.c
@@ -385,10 +385,20 @@ compare_gpts(struct block_device *blk, gpt_header *pgpt, gpt_header *agpt,
 	     u64 lastlba)
 {
 	struct device *dev = blk->dev;
-
 	int error_found = 0;
-	if (!pgpt || !agpt)
-		return;
+
+	if (!pgpt) {
+		dev_warn(dev, "GPT:Primary GPT not found\n");
+		error_found++;
+		goto refresh;
+	}
+
+	if (!agpt) {
+		dev_warn(dev, "GPT:Alternate GPT not found\n");
+		error_found++;
+		goto refresh;
+	}
+
 	if (le64_to_cpu(pgpt->my_lba) != le64_to_cpu(agpt->alternate_lba)) {
 		dev_warn(dev,
 		       "GPT:Primary header LBA != Alt. header alternate_lba\n");
@@ -469,15 +479,15 @@ compare_gpts(struct block_device *blk, gpt_header *pgpt, gpt_header *agpt,
 		error_found++;
 	}
 
-	if (error_found) {
-		add_gpt_refresh(blk);
-		if (!IS_ENABLED(CONFIG_PARTITION_DISK_EFI_REFRESH))
-			dev_info(dev, "GPT: will repair later if global.system.gpt_refresh=1\n");
-		else
-			dev_warn(dev, "GPT: Use parted to correct GPT errors.\n");
-	}
+	if (!error_found)
+		return;
 
-	return;
+refresh:
+	add_gpt_refresh(blk);
+	if (!IS_ENABLED(CONFIG_PARTITION_DISK_EFI_REFRESH))
+		dev_info(dev, "GPT: will repair later if global.system.gpt_refresh=1\n");
+	else
+		dev_warn(dev, "GPT: Use parted to correct GPT errors.\n");
 }
 
 /**

-- 
2.47.3




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 3/6] partitions: gpt: validate mkpart range against GPT usable LBAs
  2026-07-01  9:27 [PATCH 0/6] GPT: fixes around last_usable_lba Sascha Hauer
  2026-07-01  9:27 ` [PATCH 1/6] partitions: gpt: Adjust last_usable_lba to real device size when writing Sascha Hauer
  2026-07-01  9:27 ` [PATCH 2/6] partitions: gpt: refresh also when primary or alternate is invalid Sascha Hauer
@ 2026-07-01  9:27 ` Sascha Hauer
  2026-07-01  9:27 ` [PATCH 4/6] partitions: check new partitions against usable LBAs, not device size Sascha Hauer
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Sascha Hauer @ 2026-07-01  9:27 UTC (permalink / raw)
  To: BAREBOX; +Cc: Sascha Hauer

efi_partition_mkpart() validated the requested partition range against
hardcoded device geometry: start_lba against a fixed 34 and end_lba
against last_lba(blk) - 33. These offsets assume the default layout of
128 partition entries (32 sectors) plus the backup GPT header.

The authoritative bounds are first_usable_lba and last_usable_lba stored
in the GPT header being modified. They can differ from the hardcoded
values, e.g. when a table is read from disk that was created for a
smaller device (last_usable_lba retains the smaller value until the GPT
is rewritten) or that reserves a differently sized partition entry
array. In those cases the old check allowed a partition to extend past
last_usable_lba, overlapping the reserved backup entry array and GPT
header.

Check start_lba/end_lba against the header's first_usable_lba and
last_usable_lba instead.

Signed-off-by: Sascha Hauer <sascha@saschahauer.de>
---
 common/partitions/efi.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/common/partitions/efi.c b/common/partitions/efi.c
index 98172672ff..59b9fa9b55 100644
--- a/common/partitions/efi.c
+++ b/common/partitions/efi.c
@@ -764,15 +764,18 @@ static __maybe_unused int efi_partition_mkpart(struct partition_desc *pd,
 	gpt_entry *pte;
 	int i;
 	const guid_t *guid;
+	uint64_t first_usable_lba = le64_to_cpu(gpt->first_usable_lba);
+	uint64_t last_usable_lba = le64_to_cpu(gpt->last_usable_lba);
 
-	if (start_lba < 34) {
-		pr_err("invalid start LBA %lld, minimum is 34\n", start_lba);
+	if (start_lba < first_usable_lba) {
+		pr_err("invalid start LBA %lld, minimum is %lld\n", start_lba,
+		       first_usable_lba);
 		return -EINVAL;
 	}
 
-	if (end_lba >= last_lba(pd->blk) - 33) {
+	if (end_lba > last_usable_lba) {
 		pr_err("invalid end LBA %lld, maximum is %lld\n", end_lba,
-		       last_lba(pd->blk) - 34);
+		       last_usable_lba);
 		return -EINVAL;
 	}
 

-- 
2.47.3




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 4/6] partitions: check new partitions against usable LBAs, not device size
  2026-07-01  9:27 [PATCH 0/6] GPT: fixes around last_usable_lba Sascha Hauer
                   ` (2 preceding siblings ...)
  2026-07-01  9:27 ` [PATCH 3/6] partitions: gpt: validate mkpart range against GPT usable LBAs Sascha Hauer
@ 2026-07-01  9:27 ` Sascha Hauer
  2026-07-01  9:27 ` [PATCH 5/6] partitions: gpt: fix wrong error messages Sascha Hauer
  2026-07-01  9:27 ` [PATCH 6/6] partitions: gpt: log a message when partition table refreshed Sascha Hauer
  5 siblings, 0 replies; 7+ messages in thread
From: Sascha Hauer @ 2026-07-01  9:27 UTC (permalink / raw)
  To: BAREBOX; +Cc: Sascha Hauer

parted could create partitions using the raw device size instead of the
partition table's usable LBA range. Two paths were affected:

  - "mkpart ... max" computed the end as blk->num_blocks - 35 (GPT) or
    blk->num_blocks - 1 (MBR), hardcoding GPT geometry instead of using
    the table's last_usable_lba.

  - "mkpart_size" placed partitions via partition_find_free_space() /
    partition_is_free(), which bounded against blk->num_blocks.

For GPT these device-size bounds can exceed last_usable_lba (e.g. for a
table read from a device larger than the one it was created for, whose
last_usable_lba still reflects the smaller size, or a table reserving a
larger partition entry array). A partition placed there overlaps the
backup GPT header and partition entry array at the end of the device.
Linux does not reject such a partition - is_pte_valid() only bounds
entries against the physical device end - so the overlap surfaces as
silent corruption once the backup GPT is written.

Expose first_usable_lba/last_usable_lba on struct partition_desc,
populated by each parser from its own table geometry (GPT from the
header, others defaulting to the whole device), and validate partition
placement against them in partition_create(), partition_is_free(),
partition_find_free_space() and the parted "max" handler.

The lower bound remains the maximum of the table's first_usable_lba and
the global partitions.first_usable_lba, so space reserved at the start
of the device is still honoured even when the table would allow a lower
start.

Signed-off-by: Sascha Hauer <sascha@saschahauer.de>
---
 commands/parted.c       |  8 +-------
 common/partitions.c     | 28 +++++++++++++++++++++-------
 common/partitions/efi.c |  6 ++++++
 include/partitions.h    |  2 ++
 4 files changed, 30 insertions(+), 14 deletions(-)

diff --git a/commands/parted.c b/commands/parted.c
index dd79def62a..423eff7aa0 100644
--- a/commands/parted.c
+++ b/commands/parted.c
@@ -165,13 +165,7 @@ static int do_mkpart(struct block_device *blk, int argc, char *argv[])
 	start >>= SECTOR_SHIFT;
 
 	if (!strcmp(argv[4], "max")) {
-		/* gpt requires 34 blocks at the end */
-		if (pdesc->parser->type == filetype_gpt)
-			end = blk->num_blocks - 35;
-		else if (pdesc->parser->type == filetype_mbr)
-			end = blk->num_blocks - 1;
-		else
-			return -ENOSYS;
+		end = pdesc->last_usable_lba;
 	} else {
 		ret = parted_strtoull(argv[4], &end, &mult);
 		if (ret)
diff --git a/common/partitions.c b/common/partitions.c
index 40f4c629e1..82dadd515b 100644
--- a/common/partitions.c
+++ b/common/partitions.c
@@ -189,7 +189,10 @@ bool partition_is_free(struct partition_desc *pdesc, uint64_t start, uint64_t si
 	if (start < PARTITION_ALIGN_SECTORS)
 		return false;
 
-	if (start + size >= pdesc->blk->num_blocks)
+	if (start < pdesc->first_usable_lba)
+		return false;
+
+	if (start + size - 1 > pdesc->last_usable_lba)
 		return false;
 
 	list_for_each_entry(p, &pdesc->partitions, list) {
@@ -204,9 +207,10 @@ int partition_find_free_space(struct partition_desc *pdesc, uint64_t sectors, ui
 {
 	struct partition *p;
 	uint64_t min_sec = PARTITION_ALIGN_SECTORS;
+	uint64_t first_usable = max(partition_first_usable_lba(), pdesc->first_usable_lba);
 
-	if (min_sec < partition_first_usable_lba())
-		min_sec = partition_first_usable_lba();
+	if (min_sec < first_usable)
+		min_sec = first_usable;
 
 	min_sec = ALIGN(min_sec, PARTITION_ALIGN_SECTORS);
 
@@ -231,6 +235,7 @@ int partition_create(struct partition_desc *pdesc, const char *name,
 		     const char *fs_type, uint64_t lba_start, uint64_t lba_end)
 {
 	struct partition *part;
+	uint64_t first_usable;
 
 	if (!pdesc->parser->mkpart)
 		return -ENOSYS;
@@ -240,14 +245,16 @@ int partition_create(struct partition_desc *pdesc, const char *name,
 		return -EINVAL;
 	}
 
-	if (lba_end >= pdesc->blk->num_blocks) {
-		pr_err("lba_end exceeds device: %llu >= %llu\n", lba_end, pdesc->blk->num_blocks);
+	if (lba_end > pdesc->last_usable_lba) {
+		pr_err("lba_end exceeds last usable lba: %llu > %llu\n",
+		       lba_end, pdesc->last_usable_lba);
 		return -EINVAL;
 	}
 
-	if (lba_start < partition_first_usable_lba()) {
+	first_usable = max(partition_first_usable_lba(), pdesc->first_usable_lba);
+	if (lba_start < first_usable) {
 		pr_err("partition starts before first usable lba: %llu < %llu\n",
-		       lba_start, partition_first_usable_lba());
+		       lba_start, first_usable);
 		return -EINVAL;
 	}
 
@@ -289,6 +296,13 @@ void partition_table_free(struct partition_desc *pdesc)
 void partition_desc_init(struct partition_desc *pd, struct block_device *blk)
 {
 	pd->blk = blk;
+	/*
+	 * Default usable range spanning the whole device. Parsers that know
+	 * better (e.g. GPT reserves space for its headers and partition entry
+	 * arrays) override these from the on-disk table geometry.
+	 */
+	pd->first_usable_lba = partition_first_usable_lba();
+	pd->last_usable_lba = blk->num_blocks - 1;
 	INIT_LIST_HEAD(&pd->partitions);
 }
 
diff --git a/common/partitions/efi.c b/common/partitions/efi.c
index 59b9fa9b55..e58f86cd9d 100644
--- a/common/partitions/efi.c
+++ b/common/partitions/efi.c
@@ -636,6 +636,9 @@ static struct partition_desc *efi_partition(void *buf, struct block_device *blk)
 	gpt = epd->gpt;
 	ptes = epd->ptes;
 
+	epd->pd.first_usable_lba = le64_to_cpu(gpt->first_usable_lba);
+	epd->pd.last_usable_lba = le64_to_cpu(gpt->last_usable_lba);
+
 	blk->cdev.flags |= DEVFS_IS_GPT_PARTITIONED;
 
 	nb_part = le32_to_cpu(gpt->num_partition_entries);
@@ -713,6 +716,9 @@ static __maybe_unused struct partition_desc *efi_partition_create_table(struct b
 	gpt->alternate_lba = cpu_to_le64(last_lba(blk));
 	gpt->first_usable_lba = cpu_to_le64(first_usable_lba);
 	gpt->last_usable_lba = cpu_to_le64(last_lba(blk) - (gpt_size + 2));;
+
+	epd->pd.first_usable_lba = le64_to_cpu(gpt->first_usable_lba);
+	epd->pd.last_usable_lba = le64_to_cpu(gpt->last_usable_lba);
 	generate_random_guid((unsigned char *)&gpt->disk_guid);
 	gpt->partition_entry_lba = cpu_to_le64(first_usable_lba - gpt_size);
 	gpt->num_partition_entries = cpu_to_le32(num_partition_entries);
diff --git a/include/partitions.h b/include/partitions.h
index f73d028e29..3376731334 100644
--- a/include/partitions.h
+++ b/include/partitions.h
@@ -38,6 +38,8 @@ struct partition_desc {
 	struct list_head partitions;
 	struct partition_parser *parser;
 	struct block_device *blk;
+	uint64_t first_usable_lba;
+	uint64_t last_usable_lba;
 };
 
 struct partition_parser {

-- 
2.47.3




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 5/6] partitions: gpt: fix wrong error messages
  2026-07-01  9:27 [PATCH 0/6] GPT: fixes around last_usable_lba Sascha Hauer
                   ` (3 preceding siblings ...)
  2026-07-01  9:27 ` [PATCH 4/6] partitions: check new partitions against usable LBAs, not device size Sascha Hauer
@ 2026-07-01  9:27 ` Sascha Hauer
  2026-07-01  9:27 ` [PATCH 6/6] partitions: gpt: log a message when partition table refreshed Sascha Hauer
  5 siblings, 0 replies; 7+ messages in thread
From: Sascha Hauer @ 2026-07-01  9:27 UTC (permalink / raw)
  To: BAREBOX

When CONFIG_PARTITION_DISK_EFI_REFRESH is enabled we should say that it
might be automatically repaired, when it's disabled say that it can be
repaired with parted, so inverted to what's implemented. Fix that.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/partitions/efi.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common/partitions/efi.c b/common/partitions/efi.c
index e58f86cd9d..04f7117389 100644
--- a/common/partitions/efi.c
+++ b/common/partitions/efi.c
@@ -484,7 +484,7 @@ compare_gpts(struct block_device *blk, gpt_header *pgpt, gpt_header *agpt,
 
 refresh:
 	add_gpt_refresh(blk);
-	if (!IS_ENABLED(CONFIG_PARTITION_DISK_EFI_REFRESH))
+	if (IS_ENABLED(CONFIG_PARTITION_DISK_EFI_REFRESH))
 		dev_info(dev, "GPT: will repair later if global.system.gpt_refresh=1\n");
 	else
 		dev_warn(dev, "GPT: Use parted to correct GPT errors.\n");

-- 
2.47.3




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 6/6] partitions: gpt: log a message when partition table refreshed
  2026-07-01  9:27 [PATCH 0/6] GPT: fixes around last_usable_lba Sascha Hauer
                   ` (4 preceding siblings ...)
  2026-07-01  9:27 ` [PATCH 5/6] partitions: gpt: fix wrong error messages Sascha Hauer
@ 2026-07-01  9:27 ` Sascha Hauer
  5 siblings, 0 replies; 7+ messages in thread
From: Sascha Hauer @ 2026-07-01  9:27 UTC (permalink / raw)
  To: BAREBOX

Inform the user when the partition table has been changed as that's a
very useful information.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/partitions/efi.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/common/partitions/efi.c b/common/partitions/efi.c
index 04f7117389..ab920e645a 100644
--- a/common/partitions/efi.c
+++ b/common/partitions/efi.c
@@ -82,6 +82,8 @@ static int gpt_refresh_one(struct block_device *blk)
 	if (ret)
 		dev_err(blk->dev, "Refreshing partition table failed: %pe\n",
 			ERR_PTR(ret));
+	else
+		dev_info(blk->dev, "Refreshed partition table\n");
 
 	return ret;
 }

-- 
2.47.3




^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-07-01  9:32 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-01  9:27 [PATCH 0/6] GPT: fixes around last_usable_lba Sascha Hauer
2026-07-01  9:27 ` [PATCH 1/6] partitions: gpt: Adjust last_usable_lba to real device size when writing Sascha Hauer
2026-07-01  9:27 ` [PATCH 2/6] partitions: gpt: refresh also when primary or alternate is invalid Sascha Hauer
2026-07-01  9:27 ` [PATCH 3/6] partitions: gpt: validate mkpart range against GPT usable LBAs Sascha Hauer
2026-07-01  9:27 ` [PATCH 4/6] partitions: check new partitions against usable LBAs, not device size Sascha Hauer
2026-07-01  9:27 ` [PATCH 5/6] partitions: gpt: fix wrong error messages Sascha Hauer
2026-07-01  9:27 ` [PATCH 6/6] partitions: gpt: log a message when partition table refreshed Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox