[Libguestfs] [PATCHv2] New API: part_expand_gpt.

Maxim Perevedentsev
2015-12-01T15:28:44Z

This action moves second(backup) GPT header to the end of the disk.
It is usable in in-place image expanding, since free space after
second GPT header is unusable. To use additional space, we have
to move second header. This is what sgdisk -e does.

However, sgdisk -e may perform additional actions if the partition
table has unexpected params (e.g. if we call sgdisk -e /dev/sda1,
it may fix partition table thus destroying the filesystem).
To prevent such cases, we do a dry-run at first and fail if
additional actions are scheduled.
---
 daemon/parted.c                 | 33 ++++++++++++++++++++
 generator/actions.ml            | 14 +++++++++
 src/MAX_PROC_NR                 |  2 +-
 tests/daemon/Makefile.am        |  3 +-
 tests/daemon/test-expand-gpt.pl | 69 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 119 insertions(+), 2 deletions(-)
 create mode 100755 tests/daemon/test-expand-gpt.pl

diff --git a/daemon/parted.c b/daemon/parted.c
index df6b7e7..033c136 100644
--- a/daemon/parted.c
+++ b/daemon/parted.c
@@ -928,3 +928,36 @@ do_part_get_mbr_part_type (const char *device, int partnum)
   reply_with_error ("strdup failed");
   return NULL;
 }
+
+int
+do_part_expand_gpt(const char *device)
+{
+  CLEANUP_FREE char *err = NULL;
+
+  /* If something is broken, sgdisk may try to correct it.
+   * (e.g. recreate partition table and so on).
+   * We do not want such behavior, so dry-run at first.*/
+  int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
+                    str_sgdisk, "--pretend", "-e", device,
NULL);
+
+  if (r == -1) {
+    reply_with_error ("%s --pretend -e %s: %s", str_sgdisk, device,
err);
+    return -1;
+  }
+  if (err && strlen(err) != 0) {
+    /* Unexpected actions. */
+    reply_with_error ("%s --pretend -e %s: %s", str_sgdisk, device,
err);
+    return -1;
+  }
+
+  /* Now we can do a real run. */
+  r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
+                str_sgdisk, "-e", device, NULL);
+
+  if (r == -1) {
+    reply_with_error ("%s -e %s: %s", str_sgdisk, device, err);
+    return -1;
+  }
+
+  return 0;
+}
diff --git a/generator/actions.ml b/generator/actions.ml
index 7ab8ee4..3a14683 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -12787,6 +12787,20 @@ See also L<ntfsresize(8)>, L<resize2fs(8)>,
L<btrfs(8)>, L<xfs_info(8)>." };
     longdesc = "\
 This is the internal call which implements
C<guestfs_feature_available>." };

+  { defaults with
+    name = "part_expand_gpt"; added = (1, 31, 29);
+    style = RErr, [Device "device"], [];
+    proc_nr = Some 459;
+    optional = Some "gdisk";
+    shortdesc = "move backup GPT header to the end of the disk";
+    longdesc = "\
+Move backup GPT data structures to the end of the disk.
+This is useful in case of in-place image expand
+since disk space after backup GPT header is not usable.
+This is equivalent to C<sgdisk -e>.
+
+See also L<sgdisk(8)>." };
+
 ]

 (* Non-API meta-commands available only in guestfish.
diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR
index c92ddb6..658bd78 100644
--- a/src/MAX_PROC_NR
+++ b/src/MAX_PROC_NR
@@ -1 +1 @@
-458
+459
diff --git a/tests/daemon/Makefile.am b/tests/daemon/Makefile.am
index bb380c5..329b00c 100644
--- a/tests/daemon/Makefile.am
+++ b/tests/daemon/Makefile.am
@@ -25,7 +25,8 @@ check_DATA = captive-daemon.pm

 TESTS = \
 	test-daemon-start.pl \
-	test-btrfs.pl
+	test-btrfs.pl \
+	test-expand-gpt.pl

 TESTS_ENVIRONMENT = $(top_builddir)/run --test $(VG)

diff --git a/tests/daemon/test-expand-gpt.pl b/tests/daemon/test-expand-gpt.pl
new file mode 100755
index 0000000..637b90e
--- /dev/null
+++ b/tests/daemon/test-expand-gpt.pl
@@ -0,0 +1,69 @@
+#!/usr/bin/env perl
+# Copyright (C) 2015 Maxim Perevedentsev mperevedentsev@virtuozzo.com
+#
+# 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 2 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use strict;
+use warnings;
+
+use Sys::Guestfs;
+
+sub tests {
+	my $g = Sys::Guestfs->new ();
+
+	foreach ("gpt", "mbr") {
+		$g->disk_create ("disk_$_.img", "qcow2", 50 * 1024 *
1024);
+		$g->add_drive ("disk_$_.img");
+	}
+
+	$g->launch ();
+
+	$g->part_disk ("/dev/sda", "gpt");
+	$g->part_disk ("/dev/sdb", "mbr");
+
+	$g->close ();
+
+	die if system ("qemu-img resize disk_gpt.img 100M
&>/dev/null");
+
+	$g = Sys::Guestfs->new ();
+
+	foreach ("gpt", "mbr") {
+		$g->add_drive ("disk_$_.img");
+	}
+
+	$g->launch ();
+	die if $g->part_expand_gpt ("/dev/sda");
+
+	my $output = $g->debug ("sh", ["sgdisk",
"-p", "/dev/sda"]);
+	die if $output eq "";
+	$output =~ s/\n/ /g;
+	$output =~ s/.*last usable sector is (\d+).*/$1/g;
+
+	my $end_sectors = 100 * 1024 * 2 - $output;
+	die unless $end_sectors <= 34;
+
+	# Negative tests.
+	eval { $g->part_expand_gpt ("/dev/sdb") };
+	die unless $@;
+	eval { $g->part_expand_gpt ("/dev/sda1") };
+	die unless $@;
+}
+
+eval { tests() };
+system ("rm -f disk_*.img");
+if ($@) {
+    die;
+}
+exit 0
--
1.8.3.1