[parted-devel] [PATCH] libparted: handle truncated GPT disks properly

Phillip Susi psusi at ubuntu.com
Tue Dec 30 18:35:29 UTC 2014


If a disk had been truncated, or for some other reason the GPT
indicated it was larger than it appeared to be, parted silently
failed to read the partition table.  This patch fixes two errors:
First, gpt_read_headers() would attempt to read beyond the end of
the disk since the primary GPT header claims that is where the
backup lies.  It will now truncate the backup location to the end
of the disk.  Second, _parse_header() now throws a proper exception
indicating the nature of the problem and giving the user the chance
to fix or ignore it.
---
 libparted/labels/gpt.c                            | 48 +++++++++++++++++------
 tests/t0203-gpt-shortened-device-primary-valid.sh | 43 +++++++++++++++++++-
 2 files changed, 76 insertions(+), 15 deletions(-)

diff --git a/libparted/labels/gpt.c b/libparted/labels/gpt.c
index d69377a..05847de 100644
--- a/libparted/labels/gpt.c
+++ b/libparted/labels/gpt.c
@@ -724,6 +724,7 @@ _parse_header (PedDisk *disk, const GuidPartitionTableHeader_t *gpt,
   PedSector first_usable;
   PedSector last_usable;
   PedSector last_usable_if_grown;
+  PedSector gpt_disk_length;
 
 #ifndef DISCOVER_ONLY
   if (PED_LE32_TO_CPU (gpt->Revision) > GPT_HEADER_REVISION_V1_02)
@@ -751,15 +752,37 @@ _parse_header (PedDisk *disk, const GuidPartitionTableHeader_t *gpt,
      parted invocation. */
 
   last_usable_if_grown = disk->dev->length - 2 - _ptes_sectors(disk, gpt);
-
-  if (last_usable <= first_usable
-      || disk->dev->length < last_usable)
-    return 0;
-
-  if (last_usable_if_grown <= first_usable
-      || disk->dev->length < last_usable_if_grown)
-    return 0;
-
+  if (last_usable <= first_usable)
+    {
+      ped_exception_throw (PED_EXCEPTION_ERROR,
+                           PED_EXCEPTION_CANCEL,
+                           _("The GPT on %s claims LastUsableLBA is <= "
+                             "FirstUsableLBA, which is invalid"),
+                           disk->dev->path);
+      return 0;
+    }
+  gpt_disk_length = last_usable + 2 + _ptes_sectors (disk,gpt);
+  if (disk->dev->length < gpt_disk_length)
+    {
+      PedExceptionOption q;
+      q = ped_exception_throw (PED_EXCEPTION_ERROR,
+                               PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE,
+                               _("The GPT claims that %s is %llu blocks "
+                                 "long, but it is only %llu blocks long. "
+                                 "Fix the GPT to show the correct size?"),
+                               disk->dev->path,
+                               gpt_disk_length,
+                               disk->dev->length);
+      if (q == PED_EXCEPTION_FIX)
+        {
+          last_usable = last_usable_if_grown;
+          gpt_disk_data->AlternateLBA = disk->dev->length - 1;
+          *update_needed = 1;
+        }
+      if (q == PED_EXCEPTION_UNHANDLED)
+        return 0;
+    }
+	  
   if (last_usable < last_usable_if_grown)
     {
       PedExceptionOption q;
@@ -903,10 +926,9 @@ gpt_read_headers (PedDisk const *disk,
   else
     pth_free (pri);
 
-  gpt_disk_data->AlternateLBA =
-    (valid_primary
-     ? PED_LE64_TO_CPU (pri->AlternateLBA)
-     : dev->length - 1);
+  gpt_disk_data->AlternateLBA = PED_LE64_TO_CPU (pri->AlternateLBA);
+  if( !valid_primary || gpt_disk_data->AlternateLBA > dev->length - 1 )
+    gpt_disk_data->AlternateLBA = dev->length - 1;
 
   void *s_bak;
   if (!ptt_read_sector (dev, gpt_disk_data->AlternateLBA ,&s_bak))
diff --git a/tests/t0203-gpt-shortened-device-primary-valid.sh b/tests/t0203-gpt-shortened-device-primary-valid.sh
index 3dd28f8..c398df6 100755
--- a/tests/t0203-gpt-shortened-device-primary-valid.sh
+++ b/tests/t0203-gpt-shortened-device-primary-valid.sh
@@ -26,7 +26,7 @@ parted -s -- $dev mklabel gpt || fail=1
 
 # Chop off the last two sectors.
 dd if=/dev/null of=$dev bs=$ss seek=98 || fail=1
-printf 'ignore\nok\n' > in
+printf 'ok\nignore\n' > in
 parted -m ---pretend-input-tty $dev u s p < in > out 2> err || fail=1
 
 # Remove abs name of $dev_file.
@@ -37,8 +37,8 @@ sed "s,.*/$dev:,$dev:," out > k && mv k out || fail=1
 
 emit_superuser_warning > err.exp || fail=1
 cat <<EOF >> err.exp || fail=1
-Error: end of file while reading $dev
 Error: The backup GPT table is corrupt, but the primary appears OK, so that will be used.
+Error: The GPT claims that dev-file is 100 blocks long, but it is only 98 blocks long. Fix the GPT to show the correct size?
 EOF
 
 echo "$dev:98s:file:$ss:$ss:gpt::;" > out.exp || fail=1
@@ -46,4 +46,43 @@ echo "$dev:98s:file:$ss:$ss:gpt::;" > out.exp || fail=1
 compare err.exp err || fail=1
 compare out.exp out || fail=1
 
+# Repeat, only fix it this time
+
+printf 'ok\nfix\n' > in
+parted -m ---pretend-input-tty $dev u s p < in > out 2> err || fail=1
+
+# Remove abs name of $dev_file.
+sed "s, [^ ]*/$dev, $dev," err > k && mv k err || fail=1
+# Compare only the last line, to avoid control chars of interactive mode.
+tail -1 out > k && mv k out || fail=1
+sed "s,.*/$dev:,$dev:," out > k && mv k out || fail=1
+
+emit_superuser_warning > err.exp || fail=1
+cat <<EOF >> err.exp || fail=1
+Error: The backup GPT table is corrupt, but the primary appears OK, so that will be used.
+Error: The GPT claims that dev-file is 100 blocks long, but it is only 98 blocks long. Fix the GPT to show the correct size?
+EOF
+
+echo "$dev:98s:file:$ss:$ss:gpt::;" > out.exp || fail=1
+
+compare err.exp err || fail=1
+compare out.exp out || fail=1
+
+# Make sure it was fixed
+
+cat <<EOF > out.exp
+Model:  (file)
+Disk DEVICE: SIZEkB
+Sector size (logical/physical): SSB/SSB
+Partition Table: gpt
+Disk Flags: 
+
+Number  Start  End  Size  File system  Name  Flags
+
+EOF
+parted -s $dev p > out 2>&1 || fail=1
+# clean up sector size and file name differences
+sed -e "s,/.*/$dev,DEVICE,;s/${ss}B/SSB/g;s/[0-9.]*kB/SIZEkB/" out > out.fixed
+compare out.exp out.fixed || fail=1
+
 Exit $fail
-- 
1.9.1




More information about the parted-devel mailing list