GNU bug report logs - #79720
libparted: wrong metadata length with empty GPT partition table

Previous Next

Package: parted;

Reported by: Pascal Hambourg <pascal <at> plouf.fr.eu.org>

Date: Wed, 29 Oct 2025 21:08:02 UTC

Severity: minor

Found in version 3.6

To reply to this bug, email your comments to 79720 AT debbugs.gnu.org.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-parted <at> gnu.org:
bug#79720; Package parted. (Wed, 29 Oct 2025 21:08:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Pascal Hambourg <pascal <at> plouf.fr.eu.org>:
New bug report received and forwarded. Copy sent to bug-parted <at> gnu.org. (Wed, 29 Oct 2025 21:08:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Pascal Hambourg <pascal <at> plouf.fr.eu.org>
To: bug-parted <at> gnu.org
Subject: libparted: wrong metadata length with empty GPT partition table
Date: Wed, 29 Oct 2025 22:06:23 +0100
Package: parted
Version: 3.6
Severity: minor

Dear maintainers,

When libparted reads an existing empty GPT partition table, it wrongly 
creates the main and backup virtual metadata partitions with a length 
based on GPT_DEFAULT_PARTITION_ENTRIES (128) instead of the actual 
partition table length based on NumberOfPartitionEntries in the GPT 
header. Only after an active partition is added (either read from the 
on-disk partition table or new), the metadata partitions have the 
correct length.

Looking at libparted/label/gpt.c, it seems that gpt_read() initializes 
the partition table object with GPT_DEFAULT_PARTITION_ENTRIES and does 
not trigger an update of metadata and free space after parsing the GPT 
header but only when partitions are added.

(My use case is with partman, the Debian installer partitioning tool 
based on libparted. I need to check whether the current GPT metadata 
would overlap with U-Boot location for Allwinner sunxi SoCs starting at 
offset 8KiB (sector 16). A GPT partition table with a maximum length of 
56 entries can be created with gdisk and ends at sector 15 so is 
compatible with U-Boot for sunxi. But when the GPT partition table is 
empty, the reported metadata length is inaccurate.)

As a workaround, is there a simple reliable way using libparted API to 
update metadata without altering the state of the partition table ?

Otherwise, what is the best place and way to fix this in libparted, in 
other word make sure metadata and free space are properly updated when 
the partition table is empty ?
IIUC it involves _disk_push_update_mode() and _disk_pop_update_mode() 
which can be called only in libparted/disk.c.





Information forwarded to bug-parted <at> gnu.org:
bug#79720; Package parted. (Fri, 31 Oct 2025 21:54:01 GMT) Full text and rfc822 format available.

Message #8 received at 79720 <at> debbugs.gnu.org (full text, mbox):

From: "Brian C. Lane" <bcl <at> redhat.com>
To: Pascal Hambourg <pascal <at> plouf.fr.eu.org>
Cc: 79720 <at> debbugs.gnu.org
Subject: Re: bug#79720: libparted: wrong metadata length with empty GPT
 partition table
Date: Fri, 31 Oct 2025 14:53:13 -0700
On Wed, Oct 29, 2025 at 10:06:23PM +0100, Pascal Hambourg wrote:
> Package: parted
> Version: 3.6
> Severity: minor
> 
> Dear maintainers,
> 
> When libparted reads an existing empty GPT partition table, it wrongly
> creates the main and backup virtual metadata partitions with a length based
> on GPT_DEFAULT_PARTITION_ENTRIES (128) instead of the actual partition table
> length based on NumberOfPartitionEntries in the GPT header. Only after an
> active partition is added (either read from the on-disk partition table or
> new), the metadata partitions have the correct length.
> 
> Looking at libparted/label/gpt.c, it seems that gpt_read() initializes the
> partition table object with GPT_DEFAULT_PARTITION_ENTRIES and does not
> trigger an update of metadata and free space after parsing the GPT header
> but only when partitions are added.

I've tried to figure out where this could be going wrong, but am not
seeing it. When gpt_alloc is called it sets gpt_disk_data->entry_count
to the default. But when gpt_read is called it calls _parse_header, and
parse_header sets entry_count from the value it read from the header
(gpt->NumberOfPartitionEntries).

So as long as ped_disk_new is called the entry_count should be correct
and used everywhere. So I'm not sure how you are seeing the behavior you
are describing.

I do agree that parted has problems with relocated partition tables, I
think the first thing to do there is to detect that and raise an
exception instead of rewriting it to LBA 2.

Brian

-- 
Brian C. Lane (PST8PDT) - weldr.io - lorax - parted - pykickstart





Information forwarded to bug-parted <at> gnu.org:
bug#79720; Package parted. (Fri, 31 Oct 2025 23:01:02 GMT) Full text and rfc822 format available.

Message #11 received at 79720 <at> debbugs.gnu.org (full text, mbox):

From: Pascal Hambourg <pascal <at> plouf.fr.eu.org>
To: "Brian C. Lane" <bcl <at> redhat.com>
Cc: 79720 <at> debbugs.gnu.org
Subject: Re: bug#79720: libparted: wrong metadata length with empty GPT
 partition table
Date: Sat, 1 Nov 2025 00:00:40 +0100
On 31/10/2025 at 22:53, Brian C. Lane wrote:
>>
>> When libparted reads an existing empty GPT partition table, it wrongly
>> creates the main and backup virtual metadata partitions with a length based
>> on GPT_DEFAULT_PARTITION_ENTRIES (128) instead of the actual partition table
>> length based on NumberOfPartitionEntries in the GPT header. Only after an
>> active partition is added (either read from the on-disk partition table or
>> new), the metadata partitions have the correct length.
>>
>> Looking at libparted/label/gpt.c, it seems that gpt_read() initializes the
>> partition table object with GPT_DEFAULT_PARTITION_ENTRIES and does not
>> trigger an update of metadata and free space after parsing the GPT header
>> but only when partitions are added.
> 
> I've tried to figure out where this could be going wrong, but am not
> seeing it. When gpt_alloc is called it sets gpt_disk_data->entry_count
> to the default. But when gpt_read is called it calls _parse_header, and
> parse_header sets entry_count from the value it read from the header
> (gpt->NumberOfPartitionEntries).

Here is the (trimmed) call chain as I understand it:

ped_disk_new
  ped_disk_new_fresh
    type->ops->alloc (-> gpt_alloc)
      _ped_disk_alloc
      gpt_disk_data->entry_count = GPT_DEFAULT_PARTITION_ENTRIES
    _disk_pop_update_mode <- update metadata and free space
  type->ops->read (-> gpt_read)
    ped_disk_delete_all
    gpt_read_headers
    _parse_header
      gpt_disk_data->entry_count = gpt->NumberOfPartitionEntries
    gpt_read_PE_array
    for each active partition entry
      _parse_part_entry
      ped_disk_add_partition
        _disk_push_update_mode
        _disk_raw_add
        _disk_pop_update_mode <- update metadata and free space

Indeed _parse_header updates entry_count, but then _disk_pop_update_mode 
(which updates metadata and free space partitions) is called only if the 
partition table has at least one active partition. Or later when a new 
partition is created, or the pmbr_boot flag is set.

I quickly tested that surrounding the call to type->ops->read with 
_disk_push_update_mode and _disk_pop_update_mode in ped_disk_new seems 
to fix the issue:

diff --git a/libparted/disk.c b/libparted/disk.c
index 2d6b9d49..768f4681 100644
--- a/libparted/disk.c
+++ b/libparted/disk.c
@@ -199,8 +199,10 @@ ped_disk_new (PedDevice* dev)
 	if (!disk)
 		goto error_close_dev;
+	_disk_push_update_mode (disk);
 	if (!type->ops->read (disk))
 		goto error_destroy_disk;
+	_disk_pop_update_mode (disk);
 	disk->needs_clobber = 0;
 	ped_device_close (dev);
 	return disk;

But I am now working on a larger patch which aims to stay in update mode 
and avoid useless transient metadata and free space partition updates 
until after reading the partition table. Would it be welcome ?

> I do agree that parted has problems with relocated partition tables, I
> think the first thing to do there is to detect that and raise an
> exception instead of rewriting it to LBA 2.
I observed this issue and found related bug #68379. But IMO it is a 
totally unrelated issue which should be addressed separately.




Information forwarded to bug-parted <at> gnu.org:
bug#79720; Package parted. (Mon, 03 Nov 2025 21:46:01 GMT) Full text and rfc822 format available.

Message #14 received at 79720 <at> debbugs.gnu.org (full text, mbox):

From: "Brian C. Lane" <bcl <at> redhat.com>
To: Pascal Hambourg <pascal <at> plouf.fr.eu.org>
Cc: 79720 <at> debbugs.gnu.org
Subject: Re: bug#79720: libparted: wrong metadata length with empty GPT
 partition table
Date: Mon, 3 Nov 2025 13:45:48 -0800
On Sat, Nov 01, 2025 at 12:00:40AM +0100, Pascal Hambourg wrote:
> On 31/10/2025 at 22:53, Brian C. Lane wrote:
> > > 
> > > When libparted reads an existing empty GPT partition table, it wrongly
> > > creates the main and backup virtual metadata partitions with a length based
> > > on GPT_DEFAULT_PARTITION_ENTRIES (128) instead of the actual partition table
> > > length based on NumberOfPartitionEntries in the GPT header. Only after an
> > > active partition is added (either read from the on-disk partition table or
> > > new), the metadata partitions have the correct length.
> > > 
> > > Looking at libparted/label/gpt.c, it seems that gpt_read() initializes the
> > > partition table object with GPT_DEFAULT_PARTITION_ENTRIES and does not
> > > trigger an update of metadata and free space after parsing the GPT header
> > > but only when partitions are added.
> > 
> > I've tried to figure out where this could be going wrong, but am not
> > seeing it. When gpt_alloc is called it sets gpt_disk_data->entry_count
> > to the default. But when gpt_read is called it calls _parse_header, and
> > parse_header sets entry_count from the value it read from the header
> > (gpt->NumberOfPartitionEntries).
> 
> Here is the (trimmed) call chain as I understand it:
> 
> ped_disk_new
>   ped_disk_new_fresh
>     type->ops->alloc (-> gpt_alloc)
>       _ped_disk_alloc
>       gpt_disk_data->entry_count = GPT_DEFAULT_PARTITION_ENTRIES
>     _disk_pop_update_mode <- update metadata and free space
>   type->ops->read (-> gpt_read)
>     ped_disk_delete_all
>     gpt_read_headers
>     _parse_header
>       gpt_disk_data->entry_count = gpt->NumberOfPartitionEntries
>     gpt_read_PE_array
>     for each active partition entry
>       _parse_part_entry
>       ped_disk_add_partition
>         _disk_push_update_mode
>         _disk_raw_add
>         _disk_pop_update_mode <- update metadata and free space
> 
> Indeed _parse_header updates entry_count, but then _disk_pop_update_mode
> (which updates metadata and free space partitions) is called only if the
> partition table has at least one active partition. Or later when a new
> partition is created, or the pmbr_boot flag is set.

Ah, ok. Now it's clear :) Thanks for that.

> I quickly tested that surrounding the call to type->ops->read with
> _disk_push_update_mode and _disk_pop_update_mode in ped_disk_new seems to
> fix the issue:
> 
> diff --git a/libparted/disk.c b/libparted/disk.c
> index 2d6b9d49..768f4681 100644
> --- a/libparted/disk.c
> +++ b/libparted/disk.c
> @@ -199,8 +199,10 @@ ped_disk_new (PedDevice* dev)
>  	if (!disk)
>  		goto error_close_dev;
> +	_disk_push_update_mode (disk);
>  	if (!type->ops->read (disk))
>  		goto error_destroy_disk;
> +	_disk_pop_update_mode (disk);
>  	disk->needs_clobber = 0;
>  	ped_device_close (dev);
>  	return disk;

This doesn't pass the tests -- gpt_read calls ped_disk_commit_to_dev
when it needs to fix one of the headers, and that has an assert check on
update_mode.

But, while it seems a bit like a kludge, I think we could just do a
push/pop right after doing the read instead of wrapping it. That at
least passes the tests and I don't think it would have any unexpected
side-effects.

> But I am now working on a larger patch which aims to stay in update mode and
> avoid useless transient metadata and free space partition updates until
> after reading the partition table. Would it be welcome ?

I'm open to anything -- but am very reluctant to make large changes
without really good reasons. parted is, for the most part, really stable
and I'm worried about breaking things.

Brian

-- 
Brian C. Lane (PST8PDT) - weldr.io - lorax - parted - pykickstart





Information forwarded to bug-parted <at> gnu.org:
bug#79720; Package parted. (Tue, 04 Nov 2025 10:00:02 GMT) Full text and rfc822 format available.

Message #17 received at 79720 <at> debbugs.gnu.org (full text, mbox):

From: Pascal Hambourg <pascal <at> plouf.fr.eu.org>
To: "Brian C. Lane" <bcl <at> redhat.com>
Cc: 79720 <at> debbugs.gnu.org
Subject: Re: bug#79720: libparted: wrong metadata length with empty GPT
 partition table
Date: Tue, 4 Nov 2025 10:59:00 +0100
On 03/11/2025 à 22:45, Brian C. Lane wrote:
> On Sat, Nov 01, 2025 at 12:00:40AM +0100, Pascal Hambourg wrote:
>>
>> Indeed _parse_header updates entry_count, but then _disk_pop_update_mode
>> (which updates metadata and free space partitions) is called only if the
>> partition table has at least one active partition. Or later when a new
>> partition is created, or the pmbr_boot flag is set.
> 
> Ah, ok. Now it's clear :) Thanks for that.
> 
>> I quickly tested that surrounding the call to type->ops->read with
>> _disk_push_update_mode and _disk_pop_update_mode in ped_disk_new seems to
>> fix the issue:
(...)> This doesn't pass the tests -- gpt_read calls ped_disk_commit_to_dev
> when it needs to fix one of the headers, and that has an assert check on
> update_mode.

Oops, my poor knowledge of parted made me miss that.

> But, while it seems a bit like a kludge, I think we could just do a
> push/pop right after doing the read instead of wrapping it. That at
> least passes the tests and I don't think it would have any unexpected
> side-effects.

You know parted better and I cannot come with another suggestion. Want 
me to submit the trivial patch ?

>> But I am now working on a larger patch which aims to stay in update mode and
>> avoid useless transient metadata and free space partition updates until
>> after reading the partition table.

Never mind, if type->ops->read must not be called while the disk is in 
update mode then this approach cannot work.




Information forwarded to bug-parted <at> gnu.org:
bug#79720; Package parted. (Tue, 04 Nov 2025 19:33:02 GMT) Full text and rfc822 format available.

Message #20 received at 79720 <at> debbugs.gnu.org (full text, mbox):

From: "Brian C. Lane" <bcl <at> redhat.com>
To: Pascal Hambourg <pascal <at> plouf.fr.eu.org>
Cc: 79720 <at> debbugs.gnu.org
Subject: Re: bug#79720: libparted: wrong metadata length with empty GPT
 partition table
Date: Tue, 4 Nov 2025 11:32:48 -0800
On Tue, Nov 04, 2025 at 10:59:00AM +0100, Pascal Hambourg wrote:
> On 03/11/2025 à 22:45, Brian C. Lane wrote:

[snip]

> > But, while it seems a bit like a kludge, I think we could just do a
> > push/pop right after doing the read instead of wrapping it. That at
> > least passes the tests and I don't think it would have any unexpected
> > side-effects.
> 
> You know parted better and I cannot come with another suggestion. Want me to
> submit the trivial patch ?

Sure! Send it to parted-devel <at> lists.alioth.debian.org so that it gets
more exposure.

Thanks,

Brian

-- 
Brian C. Lane (PST8PDT) - weldr.io - lorax - parted - pykickstart





This bug report was last modified 1 day ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.