[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