Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 27 Dec 2016 03:19:51 +1100 (EST)
From:      Ian Smith <smithi@nimnet.asn.au>
To:        Perry Hutchison <perryh@pluto.rain.com>
Cc:        freebsd-stable@freebsd.org, marcel@freebsd.org
Subject:   Re: bugs in memstick GPT table, and in gpart(8)
Message-ID:  <20161227012948.K26979@sola.nimnet.asn.au>
In-Reply-To: <58606c72.4VCGEwexwvhtqyXW%perryh@pluto.rain.com>
References:  <57e87099.y0HRwon108cNG6uj%perryh@pluto.rain.com> <57ddefcf.jk4kZ2Kp%2BEcHfcFr%perryh@pluto.rain.com> <57d4c674.bo2VPtTSitHw8Suf%perryh@pluto.rain.com> <58606c72.4VCGEwexwvhtqyXW%perryh@pluto.rain.com>

next in thread | previous in thread | raw e-mail | index | archive | help
On Sun, 25 Dec 2016 17:03:46 -0800, Perry Hutchison wrote:
 > Some months ago:
 > 
 > > I dd'd FreeBSD-10.3-RELEASE-i386-memstick.img to a 4GB flash drive,
 > > and booted it into single-user mode where it appeared as da0.  Then,
 > > to resize the GPT to the media (to make space for another partition):
 > >
 > > # gpart show da0
 > > # gpart recover da0
 > > # gpart show da0
 > >
 > > which appeared to work ... [but] when I tried to create a 4th
 > > partition in that free space:
 > >
 > > # gpart show da0  # showed 3 partitions and about 3GB of free space
 > > # gpart add -t freebsd-ufs da0	# reported "da0p4 added"
 > > # gpart show da0  # showed 4 partitions including the new one, and
 > >                   # no free space -- as expected
 > > # shutdown -r now
 > >
 > > a "gpart show da0" after the reboot showed 3 partitions and about
 > > 3GB of free space, the same as before the "gpart add" operation.
 > > In other words, the new partition did not survive the reboot.
 > 
 > I finally had time to track down what was going on, and it turns
 > out that both the 10.3 GPT support, and the construction of the
 > 10.3-RELEASE-i386-memstick, are buggy.
 > 
 > Details:
 > 
 > The 10.3-RELEASE-i386-memstick has the smallest possible GPT table
 > (one sector), three GPT table entries (boot, rootfs, & swap), and
 > hdr_entries == 3 in the GPT header.  Despite that setting, the
 > "gpart add" operation did create a fourth table entry -- and the
 > new entry was (temporarily) available for the system to use (e.g.
 > I was able to run an apparently-successful newfs on it).
 > 
 > A subsequent hexdump(1) of the GPT table sector showed that the new
 > entry had even been written to the device.  However, after a reboot
 > (or, likely, any event causing the device to be tasted again), the
 > hdr_entries setting causes the new, fourth entry to be ignored:
 > only the three original entries are recognized.
 > 
 > Bugs:
 > 
 > Since the GPT table is always a whole number of sectors, and each
 > sector has room for four GPT table entries, there's no reason for
 > hdr_entries not to be a multiple of 4.  Whatever constructed the
 > 10.3-RELEASE-i386-memstick image should have rounded it up.

That would be mkimg(1).  Recall that the 10.3-R amd64 memstick.img still 
(or rather, again?) used the BSD scheme (daXa) rather than GPT, due to a 
some-BIOSes issue.  In 11+ both use mkimg, both adding a 1MB 'vestigial' 
swap partition to keep some BIOSes happy (thanks to Glen who pointed me 
to the relevant r265017)

However in both cases, this is affected by a quite early commit to 
mkimg.c: https://svnweb.freebsd.org/base?view=revision&revision=263382

"Also (...), remove the enforcement of creating a GPT table with at 
least 128 entries. While this is generally advised as the default or 
minimum, it's not actually a hard requirement. We now recreate a table 
that's precisely enough (rounded of course)."

So '3' definitely seems an unrounded result, long after this commit.

Have you checked this against an 11.0-RELEASE-i386-memstick.img ?

 > The GPT support in the kernel and in gpart(8) should handle
 > hdr_entries consistently:  either always round it up to a multiple
 > of four, or always take it at face value.  It should not be possible
 > for gpart(8) to create a partition which will effectively disappear
 > the next time the provider is tasted.

Agreed.  I'm not sure, even after checking the _possibly somewhat_ 
authoritative https://en.wikipedia.org/wiki/GUID_Partition_Table whether 
this refers to the maximum (as I suspect) or the current number of 
partitions, as listed in 'GPT header format':

72 (0x48)   8 bytes   Starting LBA of array of partition entries (always 2 in primary copy)
80 (0x50)   4 bytes   Number of partition entries in array
84 (0x54)   4 bytes   Size of a single partition entry (usually 80h or 128)

Also from that, we're apparently disregarding: "The UEFI specification 
stipulates that a minimum of 16,384 bytes, regardless of sector size, be 
allocated for the Partition Entry Array."  Ie, 128 entries.

Which is probably just as well, though losing 16KB at the start and end 
of a physical disk seems fairly minute these days, even on 1GB sticks,

Still, there are various references, as in mkimg(1) above, stating this 
is not required.  Similarly in gpart(8), optional for 'create' we have:

                   -n entries  The number of entries in the partition table.
                               Every partitioning scheme has a minimum and
                               maximum number of entries.  This option allows
                               tables to be created with a number of entries
                               that is within the limits.  Some schemes have a
                               maximum equal to the minimum and some schemes
                               have a maximum large enough to be considered
                               unlimited.  By default, partition tables are
                               created with the minimum number of entries.

In none of the instances of use of gpart(8) I've seen in any /release is 
that -n flag used.  mkimg(1) doesn't take anything like the -n switch, 
though perhaps it could / should?

 > The willingness of gpart(8) to add a partition exceeding the defined
 > size of the GPT table is a recent regression (as well as, arguably,
 > a buffer overrun):

I'm surprised it didn't extend that '3' count (clearly bogus, not at all 
rounded) to 4 when it added another partition.  It didn't even need to 
allocate another table sector, as going from 4 to 5 or more would.

I totally agree that memsticks ought to be extendable; the root fs is RO 
anyway, but there's no reason not to allow other RW partition/s that one 
could mount from 'live CD' or a single user boot, as you wanted.

 > Under the same conditions the FreeBSD 8 version
 > of gpart(8) complained "gpart: index '4': No space left on device".
 > (The message is wrong -- it should be something like "Partition
 > table full" -- but apart from the misleading message the situation
 > seems to have been handled correctly.)  Meanwhile even FreeBSD 8's
 > gpart(8) is less helpful than its predecessor, gpt(8).  Trying to
 > perform this same operation on FreeBSD 6 produces:
 > 
 > # gpt -r show da0
 >     start     size  index  contents
 >         0        1         PMBR
 >         1        1         Pri GPT header
 >         2        1         Pri GPT table
 >         3       32      1  GPT part - 83bd6b9d-7f41-11dc-be0b-001560b84f0f
 >        35  1348832      2  GPT part - FreeBSD UFS/UFS2
 >   1348867     2048      3  GPT part - FreeBSD swap
 >   1350915  6460155         
 >   7811070        1         Sec GPT table
 >   7811071        1         Sec GPT header
 > # gpt add da0
 > gpt add: da0: error: no available table entries
 > # gpt add -i 4 da0
 > gpt add: da0: error: index 4 out of range (3 max)

I missed gpt(8) altogether through 6.x and 7.x.  I wish gpart could at 
least optionally report the Pri / Sec header / table LBAs that clearly.

I guess gpart(8) and mking(1) should have complementary behaviour in 
this regard.  Perhaps they now do, though I can't spot commits since 
10.3 through current head to either gpart nor mkimg that mention this, 
but I'm just looking through about a dozen tabs on svnweb here ..

While one may agree that we'll never need 128 table entries, surely 4 is 
too few?  On and64, we already assign 4 partitions including the extra 
1MB swap partition, so there's now no scope to add a partition anyway, 
unless gpart can learn to grow the number of partitions in the table.

I wonder if, as a workaround, we could use mkimg's -p- empty or skipped 
partition option _after_ defining the 3 | 4 partitions initially wanted, 
in order to have mkimg allocate another LBA to the pri and sec GPTs?  

The example in mkimg(1) only indicates intermediate entries.  Perhaps a 
few "-p-" followed by another e.g 1MB swap partition would do the job?
Ot even a small bunch of say 64KB swap partitions .. is that too ugly?

cheers, Ian



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20161227012948.K26979>