[Parted-commits] GNU Parted Official Repository: Changes to 'master'
Jim Meyering
meyering at alioth.debian.org
Fri Nov 4 11:50:28 UTC 2011
NEWS | 5
libparted/labels/gpt.c | 11
tests/Makefile.am | 3
tests/gpt-header-munge | 284 +++++++++++++++++++++++
tests/perl-munge-9-PTE-table | 85 ------
tests/t0210-gpt-resized-partition-entry-array.sh | 40 +++
6 files changed, 337 insertions(+), 91 deletions(-)
New commits:
commit 5d12b6ef445b86e1815a029cc87bd26f58d63ad0
Author: Jim Meyering <meyering at redhat.com>
Date: Thu Nov 3 11:09:47 2011 +0100
tests: exercise the "GPT vs other-sized partition entry arrays" fixes
* tests/t0210-gpt-resized-partition-entry-array.sh: New file.
* tests/Makefile.am (TESTS): Add it.
(EXTRA_DIST): Add gpt-header-munge.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9638248..71787e5 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -19,6 +19,7 @@ TESTS = \
t0206-gpt-print-with-corrupt-primary-clobbers-pmbr.sh \
t0207-IEC-binary-notation.sh \
t0208-mkpart-end-in-IEC.sh \
+ t0210-gpt-resized-partition-entry-array.sh \
t0220-gpt-msftres.sh \
t0250-gpt.sh \
t0280-gpt-corrupt.sh \
@@ -62,7 +63,7 @@ TESTS = \
EXTRA_DIST = \
$(TESTS) lvm-utils.sh t-local.sh t-lvm.sh \
- init.cfg init.sh t-lib-helpers.sh
+ init.cfg init.sh t-lib-helpers.sh gpt-header-munge
check_PROGRAMS = print-align print-max dup-clobber duplicate
LDADD = \
diff --git a/tests/t0210-gpt-resized-partition-entry-array.sh b/tests/t0210-gpt-resized-partition-entry-array.sh
new file mode 100755
index 0000000..c3d3ee2
--- /dev/null
+++ b/tests/t0210-gpt-resized-partition-entry-array.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+# exercise GPT handling of n_partition_array_entries != 128
+
+# Copyright (C) 2009-2011 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ../parted $srcdir
+
+ss=$sector_size_
+
+N=2M
+dev=loop-file
+# create a file large enough to hold a GPT partition table
+dd if=/dev/null of=$dev bs=1 seek=$N || framework_failure
+
+# create a GPT partition table
+parted -s $dev mklabel gpt > out 2>&1 || fail=1
+# expect no output
+compare out /dev/null || fail=1
+
+gpt-header-munge --sector-size=$ss --n=9 $dev || fail=1
+
+parted -m -s $dev u s p > out 2>&1 || fail=1
+sed 's/.*:gpt:/:gpt:/' out > k && mv k out
+printf 'BYT;\n:gpt:;\n' > exp || fail=1
+compare out exp || fail=1
+
+Exit $fail
commit cbe1ae27871168ad8d614edd9756ef56f3dc438b
Author: Jim Meyering <meyering at redhat.com>
Date: Fri Oct 28 20:05:15 2011 +0200
tests: rewrite/complete GPT-munging perl script
Rewrite and complete this script.
It was incomplete and buggy. Now it works.
* tests/gpt-header-munge: Rename from ...
* tests/perl-munge-9-PTE-table: ...this.
diff --git a/tests/gpt-header-munge b/tests/gpt-header-munge
new file mode 100755
index 0000000..9b869dd
--- /dev/null
+++ b/tests/gpt-header-munge
@@ -0,0 +1,284 @@
+#!/usr/bin/perl -w
+# Change the size of a GPT image's partition array.
+
+# The vast majority of GPT partition tables have an 128-entry partition array.
+# However, we get reports of ZFS-related arrays with a mere 9 entries, and
+# some others with 140, and parted could not handle either of those because
+# it was effectively hard-coding the 128.
+# This script takes as input a GPT image that might be created by
+# parted itself, and transforms it into one with a different number, N,
+# of partition array entries. That involves the following steps:
+# - poke that value of N into the 4-byte "Number of partition entries" slot,
+# - compute the crc32 of the N partition table entries,
+# - poke the resulting value into its slot,
+# - recompute the GPT header's CRC32 checksum and poke that into its slot.
+# Do the above for both the primary and the backup GPT headers.
+
+use strict;
+use warnings;
+use Digest::CRC qw(crc32);
+use List::Util qw(max);
+
+(my $ME = $0) =~ s|.*/||;
+my $VERSION = '1.0';
+
+# Technically we shouldn't hard-code this, since it's specified
+# as the little-endian number in bytes 12..15 of the GPT header.
+my $gpt_header_len = 92;
+
+# Size of a partition array entry, in bytes.
+# This too is specified in the GPT header, but AFAIK, no one changes it.
+my $pe_size = 128;
+
+# Sector size.
+my $ss;
+
+# Sector number of the backup GPT header, to be read from the primary header.
+my $backup_LBA;
+
+# Given a GPT header $B, extract the my_LBA/backup_LBA sector number.
+sub curr_LBA($) { my ($b) = @_; unpack ('Q<', substr ($b, 24, 8)) }
+sub backup_LBA($) { my ($b) = @_; unpack ('Q<', substr ($b, 32, 8)) }
+
+# Given a GPT header $B, return its "partition entries starting LBA".
+sub pe_start_LBA($) { my ($b) = @_; unpack ('Q<', substr ($b, 72, 8)) }
+
+sub round_up_to_ss ($)
+{
+ my ($n) = @_;
+ return $n + $ss - $n % $ss;
+}
+
+# Return the byte offset of the start of the specified partition array.
+sub partition_array_start_offset ($$)
+{
+ my ($pri_or_backup, $n_pe) = @_;
+ $pri_or_backup eq 'primary'
+ and return 2 * $ss;
+
+ # Backup
+ return $backup_LBA * $ss - round_up_to_ss ($n_pe * $pe_size);
+}
+
+# Calculate and return the specified partition array crc32 checksum.
+sub partition_array_crc ($$$)
+{
+ my ($pri_or_backup, $n_pe, $in) = @_;
+ open F, '<', $in
+ or die "$ME: failed to open $in: $!\n";
+
+ # Seek to start of partition array.
+ my $off = partition_array_start_offset $pri_or_backup, $n_pe;
+ sysseek (F, $off, 0)
+ or die "$ME: $in: failed to seek to $off: $!\n";
+
+ # Read the array.
+ my $p;
+ my $pe_buf;
+ my $n = $n_pe * $pe_size;
+ ($p = sysread F, $pe_buf, $n) && $p == $n
+ or die "$ME: $in: failed to read $pri_or_backup partition array:($p:$n) $!\n";
+ close F;
+
+ return crc32 $pe_buf;
+}
+
+# Verify the initial CRC of BUF.
+sub check_GPT_header ($$$)
+{
+ my ($pri_or_backup, $in, $buf) = @_;
+
+ my $curr = curr_LBA $buf;
+ my $backup = backup_LBA $buf;
+ ($pri_or_backup eq 'primary') == ($curr == 1)
+ or die "$ME: $in: invalid curr_LBA($curr) in $pri_or_backup header\n";
+ ($pri_or_backup eq 'primary') == (34 < $backup)
+ or die "$ME: $in: invalid backup_LBA($backup) in $pri_or_backup header\n";
+
+ $pri_or_backup eq 'backup' && $backup != 1
+ and die "$ME: $in: the backup_LBA in the backup header must be 1\n";
+
+ # A primary partition's "partition entries starting LBA" must be 2.
+ if ($pri_or_backup eq 'primary')
+ {
+ my $p = pe_start_LBA $buf;
+ $p == 2
+ or die "$ME: $in: primary header's PE start LBA is $p (should be 2)\n";
+ }
+
+ # Save a copy of the CRC, then zero that field, bytes 16..19:
+ my $orig_crc = unpack ('L', substr ($buf, 16, 4));
+ substr ($buf, 16, 4) = "\0" x 4;
+
+ # Compute CRC32 of header: it'd better match.
+ my $crc = crc32($buf);
+ $orig_crc == $crc
+ or die "$ME: $in: cannot reproduce $pri_or_backup GPT header's CRC32\n";
+}
+
+# Poke the $N_PE value into $$BUF's number-of-partition-entries slot.
+sub poke_n_pe ($$)
+{
+ my ($buf, $n_pe) = @_;
+
+ # Poke the little-endian value into place.
+ substr ($$buf, 80, 4) = pack ('L<', $n_pe);
+}
+
+# Compute/set partition-array CRC (given $N_PE), then compute a new
+# header-CRC and poke it into its position, too.
+sub set_CRCs ($$$$)
+{
+ my ($pri_or_backup, $buf, $in, $n_pe) = @_;
+
+ # Compute CRC of primary partition array and put it in substr ($pri, 88, 4)
+ my $pa_crc = partition_array_crc $pri_or_backup, $n_pe, $in;
+ substr ($$buf, 88, 4) = pack ('L', $pa_crc);
+
+ # In the backup header, we must also set the 8-byte "Partition entries
+ # starting LBA number" field to reflect our new value of $n_pe.
+ if ($pri_or_backup eq 'backup')
+ {
+ my $off = partition_array_start_offset $pri_or_backup, $n_pe;
+ $off % $ss == 0
+ or die "$ME: internal error: starting LBA byte offset($off) is"
+ . " not a multiple of $ss\n";
+ my $lba = $off / $ss;
+ substr ($$buf, 72, 8) = pack ('Q<', $lba);
+ }
+
+ # Before we compute the checksum, we must zero-out the 4-byte
+ # slot into which we'll store the result.
+ substr ($$buf, 16, 4) = "\0" x 4;
+ my $crc = crc32($$buf);
+ substr ($$buf, 16, 4) = pack ('L', $crc);
+}
+
+sub usage ($)
+{
+ my ($exit_code) = @_;
+ my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
+ if ($exit_code != 0)
+ {
+ print $STREAM "Try `$ME --help' for more information.\n";
+ }
+ else
+ {
+ print $STREAM <<EOF;
+Usage: $ME [OPTIONS] FILE
+Change the number of GPT partition array entries in FILE.
+
+This option must be specified:
+
+ --n-partition-array-entries=N FIXME
+
+The following are optional:
+
+ --sector-size=N assume sector size of N bytes (default: 512)
+ --help display this help and exit
+ --version output version information and exit
+
+EXAMPLE:
+
+ dd if=/dev/null of=F bs=1 seek=6MB
+ parted -s F mklabel gpt
+ $ME --n=9 F
+
+EOF
+ }
+ exit $exit_code;
+}
+
+{
+ my $n_partition_entries;
+
+ use Getopt::Long;
+ GetOptions
+ (
+ 'n-partition-array-entries=i' => \$n_partition_entries,
+ 'sector-size=i' => \$ss,
+
+ help => sub { usage 0 },
+ version => sub { print "$ME version $VERSION\n"; exit },
+ ) or usage 1;
+
+ defined $n_partition_entries
+ or (warn "$ME: --n-partition-array-entries=N not specified\n"), usage 1;
+
+ defined $ss
+ or $ss = 512;
+
+ # Require sensible number:
+ # It must either be <= 128, or else a multiple of 4 so that at 128 bytes each,
+ # this array fully occupies a whole number of 512-byte sectors.
+ 1 <= $n_partition_entries
+ && ($n_partition_entries <= 128
+ || $n_partition_entries % 4 == 0)
+ or die "$ME: invalid number of partition entries: $n_partition_entries\n";
+
+ @ARGV == 1
+ or (warn "$ME: no file specified\n"), usage 1;
+
+ my $in = $ARGV[0];
+ open F, '<', $in
+ or die "$ME: failed to open $in: $!\n";
+
+ # Get length and perform some basic sanity checks.
+ my $len = sysseek (F, 0, 2);
+ defined $len
+ or die "$ME: $in: failed to seek to EOF: $!\n";
+ my $min_n_sectors = 34 + 33 + ($n_partition_entries * $pe_size + $ss - 1) / $ss;
+ my $n_sectors = int (($len + $ss - 1) / $ss);
+ $n_sectors < $min_n_sectors
+ and die "$ME: $in: image file is too small to contain a GPT image\n";
+ $len % $ss == 0
+ or die "$ME: $in: size is not a multiple of $ss: $!\n";
+
+ # Skip 1st sector.
+ sysseek (F, $ss, 0)
+ or die "$ME: $in: failed to seek to byte $ss: $!\n";
+
+ # Read the primary GPT header.
+ my $p;
+ my $pri;
+ ($p = sysread F, $pri, $gpt_header_len) && $p == $gpt_header_len
+ or die "$ME: $in: failed to read the primary GPT header: $!\n";
+
+ $backup_LBA = unpack ('Q<', substr ($pri, 32, 8));
+
+ # Seek-to and read the backup GPT header.
+ sysseek (F, $backup_LBA * $ss, 0)
+ or die "$ME: $in: failed to seek to backup LBA $backup_LBA: $!\n";
+ my $backup;
+ ($p = sysread F, $backup, $gpt_header_len) && $p == $gpt_header_len
+ or die "$ME: $in: read failed: $!\n";
+
+ close F;
+
+ check_GPT_header ('primary', $in, $pri);
+ check_GPT_header ('backup', $in, $backup);
+
+ poke_n_pe (\$pri, $n_partition_entries);
+ poke_n_pe (\$backup, $n_partition_entries);
+
+ # set both PE CRC and header CRCs:
+ set_CRCs 'primary', \$pri, $in, $n_partition_entries;
+ set_CRCs 'backup', \$backup, $in, $n_partition_entries;
+
+ # Write both headers back to the file:
+ open F, '+<', $in
+ or die "$ME: failed to open $in: $!\n";
+
+ sysseek (F, $ss, 0)
+ or die "$ME: $in: failed to seek to byte $ss: $!\n";
+ syswrite F, $pri
+ or die "$ME: $in: failed to write primary header: $!\n";
+
+ sysseek (F, $backup_LBA * $ss, 0)
+ or die "$ME: $in: failed to seek to backup LBA $backup_LBA: $!\n";
+ syswrite F, $backup
+ or die "$ME: $in: failed to write backup header: $!\n";
+
+ close F
+ or die "$ME: failed to close $in: $!\n";
+ }
diff --git a/tests/perl-munge-9-PTE-table b/tests/perl-munge-9-PTE-table
deleted file mode 100755
index 9d65f94..0000000
--- a/tests/perl-munge-9-PTE-table
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/usr/bin/perl -w
-# Read a GPT header, poke "9" into the 4-byte "Number of partition entries" slot
-# compute crc32 of 9 partition table entries and poke that into place,
-# recompute the primary header's CRC32 checksum and poke that into its slot.
-
-# Set up:
-# dev=dev-file
-# dd if=/dev/null of=$dev bs=1 seek=1026MB
-# parted -s -- $dev mklabel gpt
-
-use strict;
-use warnings;
-use Digest::CRC qw(crc32);
-
-(my $ME = $0) =~ s|.*/||;
-
-my $in = 'dev-file';
-my $n_partition_entries = 9;
-
-sub partition_array_crc ($$)
-{
- my ($n_entries, $in) = @_;
- open F, '<', $in
- or die "$ME: failed to open $in: $!\n";
-
- # Skip 1st 512-byte sector.
- sysseek (F, 2*512, 0)
- or die "$ME: $in: failed to seek: $!\n";
-
- # Read the array.
- my $p;
- my $buf;
- my $n = $n_partition_entries * 128;
- ($p = sysread F, $buf, $n) && $p == $n
- or die "$ME: $in: failed to read partition array: $!\n";
- close F;
-
- return crc32 $buf;
-}
-
-
-open F, '<', $in
- or die "$ME: failed to open $in: $!\n";
-
-# Skip 1st 512-byte sector.
-sysseek (F, 512, 0)
- or die "$ME: $in: failed to seek to byte 512: $!\n";
-
-# Read the primary GPT header.
-my $p;
-my $buf;
-($p = sysread F, $buf, 92) && $p == 92
- or die "$ME: $in: read failed: $!\n";
-close F;
-
-print substr ($buf, 0, 8), "\n";
-
-# Zero the CRC32 field, bytes 16..19
-my $orig_crc = unpack ('L', substr ($buf, 16, 4));
-substr ($buf, 16, 4) = "\0" x 4;
-
-# Before proceeding, compute CRC32 of header as a sanity check.
-my $crc = crc32($buf);
-$orig_crc == $crc
- or die "$ME: $in: cannot reproduce GPT header's CRC32\n";
-
-# poke the $n_partition_entries value into place
-substr ($buf, 80, 4) = pack ('L', $n_partition_entries);
-
-# Compute CRC of partition array and put it in substr ($buf, 88, 4)
-my $pa_crc = partition_array_crc $n_partition_entries, $in;
-substr ($buf, 88, 4) = pack ('L', $pa_crc);
-
-$crc = crc32($buf);
-substr ($buf, 16, 4) = pack ('L', $crc);
-
-# Write it back to the file:
-open F, '+<', $in
- or die "$ME: failed to open $in: $!\n";
-sysseek (F, 512, 0)
- or die "$ME: $in: failed to seek to byte 512: $!\n";
-print F $buf
- or die "$ME: $in: write failed: $!\n";
-close F
- or die "$ME: failed to close $in: $!\n";
commit ce85c5145ed5e267eacee338204cc777377e6b02
Author: Jim Meyering <meyering at redhat.com>
Date: Thu Nov 3 13:51:40 2011 +0100
gpt: don't misbehave with e.g., a 9-entry partition array
* libparted/labels/gpt.c (_generate_header): Correct size of
partition array entries to round up to nearest multiple of
sector size, so that we set gpt->PartitionEntryLBA correctly
also when the number of partition entries is not a multiple
of sector_size / 128. Problem diagnosed by Robert Herndon
in http://thread.gmane.org/gmane.comp.gnu.parted.bugs/10173.
diff --git a/NEWS b/NEWS
index b043ba1..c6e22f0 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,11 @@ GNU parted NEWS -*- outline -*-
libparted: gpt_disk_duplicate now copies the flags over to the new
disk object. Previously the flags would be undefined.
+ libparted can now read partition tables with a number of partition
+ array entries that is different from the default of 128. Before,
+ it would fail to recognize them and could even read beyond the end
+ of a heap-allocated buffer.
+
libparted: no longer aborts (failed assertion) due to a nilfs2_probe bug
[bug introduced in parted-2.4 with the addition of nilfs2 support]
diff --git a/libparted/labels/gpt.c b/libparted/labels/gpt.c
index e239a2d..454a177 100644
--- a/libparted/labels/gpt.c
+++ b/libparted/labels/gpt.c
@@ -1137,13 +1137,15 @@ _generate_header (const PedDisk *disk, int alternate, uint32_t ptes_crc,
if (alternate)
{
- PedSector ptes_size = gpt_disk_data->entry_count
- * sizeof (GuidPartitionEntry_t) / disk->dev->sector_size;
+ size_t ss = disk->dev->sector_size;
+ PedSector ptes_bytes = (gpt_disk_data->entry_count
+ * sizeof (GuidPartitionEntry_t));
+ PedSector ptes_sectors = (ptes_bytes + ss - 1) / ss;
gpt->MyLBA = PED_CPU_TO_LE64 (disk->dev->length - 1);
gpt->AlternateLBA = PED_CPU_TO_LE64 (1);
gpt->PartitionEntryLBA
- = PED_CPU_TO_LE64 (disk->dev->length - 1 - ptes_size);
+ = PED_CPU_TO_LE64 (disk->dev->length - 1 - ptes_sectors);
}
else
{
commit bb4e43862127054f2b8744b9d8812cba78912bbb
Author: Robert Herndon <Robert.Herndon at quantum.com>
Date: Sat Oct 29 23:03:49 2011 +0200
gpt: prepare for tables with n_partition_array_entries != 128
* libparted/labels/gpt.c (gpt_read_PE_array): When computing the size
of the partition array entry, use the value of "number of partition
array entries" read from a GPT header, not the default of 128 that we
use when creating new headers. Details here:
http://thread.gmane.org/gmane.comp.gnu.parted.bugs/10173
diff --git a/libparted/labels/gpt.c b/libparted/labels/gpt.c
index 4c5f378..e239a2d 100644
--- a/libparted/labels/gpt.c
+++ b/libparted/labels/gpt.c
@@ -581,9 +581,8 @@ static void *
gpt_read_PE_array (PedDisk const *disk, GuidPartitionTableHeader_t const *gpt,
size_t *ptes_bytes)
{
- GPTDiskData *gpt_disk_data = disk->disk_specific;
uint32_t p_ent_size = PED_LE32_TO_CPU (gpt->SizeOfPartitionEntry);
- *ptes_bytes = p_ent_size * gpt_disk_data->entry_count;
+ *ptes_bytes = p_ent_size * PED_LE32_TO_CPU(gpt->NumberOfPartitionEntries);
size_t ptes_sectors = ped_div_round_up (*ptes_bytes,
disk->dev->sector_size);
More information about the Parted-commits
mailing list