[Libguestfs] [PATCH VERSION 6] Generic partition creation interface.

Richard W.M. Jones rjones at redhat.com
Mon Nov 9 13:12:01 UTC 2009


I think this patch is looking in good shape to be reviewed and applied
now.

On Mon, Nov 09, 2009 at 10:08:32AM +0100, Jim Meyering wrote:
> Richard W.M. Jones wrote:
> ...
> > (3) Work around an apparent bug in parted where it fails to do
> > ioctl(BLKRRPART) [re-read the partition table], by catching this case
> > and rereading the partition table ourselves.
> 
> Hi Rich,
> 
> I haven't looked at that patch yet, but this part rings big bells:
> parted-2.0+ (not yet in rawhide) has had significant changes in this area.

The workaround should be safe, in that it looks for the specific error
string that parted 1.9 throws and tries to reread the partition table.
Rereading the partition table is always safe, even if we do it at the
wrong time.

> > ** NOT TO BE PUSHED **  This doesn't quite work at the moment.  All the
> > tests pass, except: test_list_partitions_0 wipes the disk and creates
> > a single partition covering the whole disk.  However the test fails
> > because it thinks the disk has 3 partitions.  At the moment I have no
> > idea why it would think this.
>
> If this is still a problem, let me know and I'll take a look.

This fixed itself.  That's the sort of bug I like.

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
New in Fedora 11: Fedora Windows cross-compiler. Compile Windows
programs, test, and build Windows installers. Over 70 libraries supprt'd
http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw
-------------- next part --------------
>From 4ebb23ddfd4cedaba19093597162976c3165fe78 Mon Sep 17 00:00:00 2001
From: Richard Jones <rjones at redhat.com>
Date: Wed, 4 Nov 2009 23:15:26 +0000
Subject: [PATCH] Generic partition creation interface.

This commit introduces a generic partition creation interface
which should be future-proof and extensible, and partially
replaces the old sfdisk-based interface.

The implementation is based on parted but is hopefully not too
dependent on the particulars of parted.

The following new calls are introduced:

  guestfs_part_init:
    Initialize a disk with a partition table.  Unlike the sfdisk-
    based interface, we also support GPT and other partition
    types, which is essential to scale to devices larger than 2TB.

  guestfs_part_add: Add a partition to an existing disk.

  guestfs_part_disk:
    Convenience function which combines part_init & part_add,
    creating a single partition that covers the whole disk.

  guestfs_part_set_bootable:
  guestfs_part_set_name:
    Set various aspects of existing partitions.

  guestfs_part_list:
    List partitions on a device.  This returns a programming-friendly
    list of partition structs (in contrast to sfdisk-l which cannot
    be parsed).

  guestfs_part_get_parttype:
    Return the partition table type, eg. "msdos" or "gpt".

The following calls are planned, but not added currently:

  guestfs_part_get_bootable
  guestfs_part_get_name
  guestfs_part_set_type
  guestfs_part_get_type
---
 .gitignore                                         |    1 +
 daemon/Makefile.am                                 |    1 +
 daemon/parted.c                                    |  357 ++++++++++++++++++++
 guestfish.pod                                      |    2 +-
 guestfs.pod                                        |   28 +-
 java/Makefile.inc                                  |    1 +
 ocaml/t/guestfs_060_readdir.ml                     |    2 +-
 perl/t/060-readdir.t                               |    2 +-
 po/POTFILES.in                                     |    1 +
 recipes/tar2vm.sh                                  |    2 +-
 regressions/rhbz503169c10.sh                       |    2 +-
 regressions/rhbz503169c13.sh                       |    2 +-
 .../test-cancellation-upload-daemoncancels.sh      |    2 +-
 regressions/test-remote.sh                         |    2 +-
 src/MAX_PROC_NR                                    |    2 +-
 src/generator.ml                                   |  258 +++++++++++++--
 16 files changed, 613 insertions(+), 52 deletions(-)
 create mode 100644 daemon/parted.c

diff --git a/.gitignore b/.gitignore
index c365865..b3869c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -117,6 +117,7 @@ java/com/redhat/et/libguestfs/GuestFS.java
 java/com/redhat/et/libguestfs/INotifyEvent.java
 java/com/redhat/et/libguestfs/IntBool.java
 java/com/redhat/et/libguestfs/LV.java
+java/com/redhat/et/libguestfs/Partition.java
 java/com/redhat/et/libguestfs/PV.java
 java/com/redhat/et/libguestfs/Stat.java
 java/com/redhat/et/libguestfs/StatVFS.java
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index db311ab..72e1896 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -60,6 +60,7 @@ guestfsd_SOURCES = \
 	mount.c \
 	names.c \
 	ntfs.c \
+	parted.c \
 	pingdaemon.c \
 	proto.c \
 	readdir.c \
diff --git a/daemon/parted.c b/daemon/parted.c
new file mode 100644
index 0000000..e0183fb
--- /dev/null
+++ b/daemon/parted.c
@@ -0,0 +1,357 @@
+/* libguestfs - the guestfsd daemon
+ * Copyright (C) 2009 Red Hat 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "daemon.h"
+#include "actions.h"
+
+/* Notes:
+ *
+ * Parted 1.9 sends error messages to stdout, hence use of the
+ * COMMAND_FLAG_FOLD_STDOUT_ON_STDERR flag.
+ *
+ * parted occasionally fails to do ioctl(BLKRRPART) on the device,
+ * apparently because of some internal race in the code.  We attempt
+ * to detect and recover from this error if we can.
+ */
+static int
+recover_blkrrpart (const char *device, const char *err)
+{
+  int r;
+
+  if (!strstr (err,
+	       "Error informing the kernel about modifications to partition"))
+    return -1;
+
+  r = command (NULL, NULL, "/sbin/blockdev", "--rereadpt", device, NULL);
+  if (r == -1)
+    return -1;
+
+  udev_settle ();
+
+  return 0;
+}
+
+#define RUN_PARTED(device,...)						\
+  do {									\
+    int r;								\
+    char *err;								\
+    									\
+    r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,	\
+		  "/sbin/parted", "-s", "--", (device), __VA_ARGS__);	\
+    if (r == -1) {							\
+      if (recover_blkrrpart ((device), err) == -1) {			\
+	reply_with_error ("%s: parted: %s: %s", __func__, (device), err); \
+	free (err);							\
+	return -1;							\
+      }									\
+    }									\
+									\
+    free (err);								\
+  } while (0)
+
+static const char *
+check_parttype (const char *parttype)
+{
+  /* Check and translate parttype. */
+  if (strcmp (parttype, "aix") == 0 ||
+      strcmp (parttype, "amiga") == 0 ||
+      strcmp (parttype, "bsd") == 0 ||
+      strcmp (parttype, "dasd") == 0 ||
+      strcmp (parttype, "dvh") == 0 ||
+      strcmp (parttype, "gpt") == 0 ||
+      strcmp (parttype, "mac") == 0 ||
+      strcmp (parttype, "msdos") == 0 ||
+      strcmp (parttype, "pc98") == 0 ||
+      strcmp (parttype, "sun") == 0)
+    return parttype;
+  else if (strcmp (parttype, "rdb") == 0)
+    return "amiga";
+  else if (strcmp (parttype, "efi") == 0)
+    return "gpt";
+  else if (strcmp (parttype, "mbr") == 0)
+    return "msdos";
+  else
+    return NULL;
+}
+
+int
+do_part_init (const char *device, const char *parttype)
+{
+  parttype = check_parttype (parttype);
+  if (!parttype) {
+    reply_with_error ("part-init: unknown partition type: common choices are \"gpt\" and \"msdos\"");
+    return -1;
+  }
+
+  RUN_PARTED (device, "mklabel", parttype, NULL);
+
+  udev_settle ();
+
+  return 0;
+}
+
+int
+do_part_add (const char *device, const char *prlogex,
+             int64_t startsect, int64_t endsect)
+{
+  char startstr[32];
+  char endstr[32];
+
+  /* Check and translate prlogex. */
+  if (strcmp (prlogex, "primary") == 0 ||
+      strcmp (prlogex, "logical") == 0 ||
+      strcmp (prlogex, "extended") == 0)
+    ;
+  else if (strcmp (prlogex, "p") == 0)
+    prlogex = "primary";
+  else if (strcmp (prlogex, "l") == 0)
+    prlogex = "logical";
+  else if (strcmp (prlogex, "e") == 0)
+    prlogex = "extended";
+  else {
+    reply_with_error ("part-add: unknown partition type: %s: this should be \"primary\", \"logical\" or \"extended\"", prlogex);
+    return -1;
+  }
+
+  if (startsect < 0) {
+    reply_with_error ("part-add: startsect cannot be negative");
+    return -1;
+  }
+  /* but endsect can be negative */
+
+  snprintf (startstr, sizeof startstr, "%" PRIi64 "s", startsect);
+  snprintf (endstr, sizeof endstr, "%" PRIi64 "s", endsect);
+
+  /* XXX Bug: If the partition table type (which we don't know in this
+   * function) is GPT, then this parted command sets the _partition
+   * name_ to prlogex, eg. "primary".  I would essentially describe
+   * this as a bug in the parted mkpart command.
+   */
+  RUN_PARTED (device, "mkpart", prlogex, startstr, endstr, NULL);
+
+  udev_settle ();
+
+  return 0;
+}
+
+int
+do_part_disk (const char *device, const char *parttype)
+{
+  const char *startstr;
+  const char *endstr;
+
+  parttype = check_parttype (parttype);
+  if (!parttype) {
+    reply_with_error ("part-disk: unknown partition type: common choices are \"gpt\" and \"msdos\"");
+    return -1;
+  }
+
+  /* Voooooodooooooooo (thanks Jim Meyering for working this out). */
+  if (strcmp (parttype, "msdos") == 0) {
+    startstr = "1s";
+    endstr = "-1s";
+  } else if (strcmp (parttype, "gpt") == 0) {
+    startstr = "34s";
+    endstr = "-34s";
+  } else {
+    /* untested */
+    startstr = "1s";
+    endstr = "-1s";
+  }
+
+  RUN_PARTED (device,
+	      "mklabel", parttype,
+	      /* See comment about about the parted mkpart command. */
+	      "mkpart", strcmp (parttype, "gpt") == 0 ? "p1" : "primary",
+	      startstr, endstr, NULL);
+
+  udev_settle ();
+
+  return 0;
+}
+
+int
+do_part_set_bootable (const char *device, int partnum, int bootable)
+{
+  char partstr[16];
+
+  snprintf (partstr, sizeof partstr, "%d", partnum);
+
+  RUN_PARTED (device, "set", partstr, "boot", bootable ? "on" : "off", NULL);
+
+  udev_settle ();
+
+  return 0;
+}
+
+int
+do_part_set_name (const char *device, int partnum, const char *name)
+{
+  char partstr[16];
+
+  snprintf (partstr, sizeof partstr, "%d", partnum);
+
+  RUN_PARTED (device, "name", partstr, name, NULL);
+
+  udev_settle ();
+
+  return 0;
+}
+
+static char **
+print_partition_table (const char *device)
+{
+  char *out, *err;
+  int r;
+  char **lines;
+
+  r = command (&out, &err, "/sbin/parted", "-m", "--", device,
+               "unit", "b",
+               "print", NULL);
+  if (r == -1) {
+    reply_with_error ("parted print: %s: %s", device,
+                      /* Hack for parted 1.x which sends errors to stdout. */
+                      *err ? err : out);
+    free (out);
+    free (err);
+    return NULL;
+  }
+  free (err);
+
+  lines = split_lines (out);
+  free (out);
+
+  if (!lines)
+    return NULL;
+
+  if (lines[0] == NULL || strcmp (lines[0], "BYT;") != 0) {
+    reply_with_error ("parted print: unknown signature, expected \"BYT;\" as first line of the output: %s",
+                      lines[0] ? lines[0] : "(signature was null)");
+    free_strings (lines);
+    return NULL;
+  }
+
+  if (lines[1] == NULL) {
+    reply_with_error ("parted print: parted didn't return a line describing the device");
+    free_strings (lines);
+    return NULL;
+  }
+
+  return lines;
+}
+
+char *
+do_part_get_parttype (const char *device)
+{
+  char **lines;
+  char *r;
+
+  lines = print_partition_table (device);
+  if (!lines)
+    return NULL;
+
+  /* lines[1] is something like:
+   * "/dev/sda:1953525168s:scsi:512:512:msdos:ATA Hitachi HDT72101;"
+   */
+  if (strtok (lines[1], ":") == NULL /* device */
+      || strtok (NULL, ":") == NULL  /* size */
+      || strtok (NULL, ":") == NULL  /* transport */
+      || strtok (NULL, ":") == NULL  /* sector size */
+      || strtok (NULL, ":") == NULL  /* physical sector size */
+      || (r = strtok (NULL, ":")) == NULL /* return value */
+      ) {
+    reply_with_error ("part_get_parttype: too few fields in output from parted print command: %s", lines[1]);
+    free_strings (lines);
+    return NULL;
+  }
+
+  r = strdup (r);
+  if (!r) {
+    reply_with_perror ("strdup");
+    free_strings (lines);
+    return NULL;
+  }
+
+  free_strings (lines);
+
+  return r;
+}
+
+guestfs_int_partition_list *
+do_part_list (const char *device)
+{
+  char **lines;
+  size_t row, nr_rows, i;
+  guestfs_int_partition_list *r;
+
+  lines = print_partition_table (device);
+  if (!lines)
+    return NULL;
+
+  /* lines[0] is "BYT;", lines[1] is the device line which we ignore,
+   * lines[2..] are the partitions themselves.  Count how many.
+   */
+  nr_rows = 0;
+  for (row = 2; lines[row] != NULL; ++row)
+    ++nr_rows;
+
+  r = malloc (sizeof *r);
+  if (r == NULL) {
+    reply_with_perror ("malloc");
+    goto error1;
+  }
+  r->guestfs_int_partition_list_len = nr_rows;
+  r->guestfs_int_partition_list_val =
+    malloc (nr_rows * sizeof (guestfs_int_partition));
+  if (r->guestfs_int_partition_list_val == NULL) {
+    reply_with_perror ("malloc");
+    goto error2;
+  }
+
+  /* Now parse the lines. */
+  for (i = 0, row = 2; lines[row] != NULL; ++i, ++row) {
+    if (sscanf (lines[row], "%d:%" SCNi64 "B:%" SCNi64 "B:%" SCNi64 "B",
+                &r->guestfs_int_partition_list_val[i].part_num,
+                &r->guestfs_int_partition_list_val[i].part_start,
+                &r->guestfs_int_partition_list_val[i].part_end,
+                &r->guestfs_int_partition_list_val[i].part_size) != 4) {
+      reply_with_error ("part_list: could not parse row from output of parted print command: %s", lines[row]);
+      goto error3;
+    }
+  }
+
+  free_strings (lines);
+  return r;
+
+ error3:
+  free (r->guestfs_int_partition_list_val);
+ error2:
+  free (r);
+ error1:
+  free_strings (lines);
+  return NULL;
+}
diff --git a/guestfish.pod b/guestfish.pod
index 8ae1800..2508066 100644
--- a/guestfish.pod
+++ b/guestfish.pod
@@ -63,7 +63,7 @@ Remove C</boot/grub/menu.lst> (in reality not such a great idea):
  #!/usr/bin/guestfish -f
  alloc /tmp/output.img 10M
  run
- sfdisk /dev/sda 0 0 0 ,
+ part-disk /dev/sda mbr
  mkfs ext2 /dev/sda1
 
 =head2 Remote control
diff --git a/guestfs.pod b/guestfs.pod
index 33b84d7..fdac80a 100644
--- a/guestfs.pod
+++ b/guestfs.pod
@@ -217,29 +217,27 @@ L<http://tldp.org/HOWTO/LVM-HOWTO/>.
 
 =head2 PARTITIONING
 
-To create MBR-style (ie. normal PC) partitions use one of the
-C<guestfs_sfdisk*> variants.  These calls use the external
-L<sfdisk(8)> command.
+In the common case where you want to create a single partition
+covering the whole disk, you should use the C<guestfs_part_disk>
+call:
 
-The simplest call is:
-
- char *lines[] = { ",", NULL };
- guestfs_sfdiskM (g, "/dev/sda", lines);
-
-This will create a single partition on C</dev/sda> called
-C</dev/sda1> covering the whole disk.
+ const char *parttype = "mbr";
+ if (disk_is_larger_than_2TB)
+   parttype = "gpt";
+ guestfs_part_disk (g, "/dev/sda", parttype);
 
 In general MBR partitions are both unnecessarily complicated and
 depend on archaic details, namely the Cylinder-Head-Sector (CHS)
-geometry of the disk.  C<guestfs_sfdiskM> allows you to specify sizes
-in megabytes instead of cylinders, which is a small win.
+geometry of the disk.  C<guestfs_sfdiskM> can be used to
+create more complex arrangements where the relative sizes are
+expressed in megabytes instead of cylinders, which is a small win.
 C<guestfs_sfdiskM> will choose the nearest cylinder to approximate the
 requested size.  There's a lot of crazy stuff to do with IDE and
 virtio disks having different, incompatible CHS geometries, that you
-probably don't want to know about.  My advice: make a single partition
-to cover the whole disk, then use LVM on top.
+probably don't want to know about.
 
-In future we aim to provide access to libparted.
+My advice: make a single partition to cover the whole disk, then use
+LVM on top.
 
 =head2 UPLOADING
 
diff --git a/java/Makefile.inc b/java/Makefile.inc
index 03b6049..c76184f 100644
--- a/java/Makefile.inc
+++ b/java/Makefile.inc
@@ -29,4 +29,5 @@ java_built_sources = \
 	com/redhat/et/libguestfs/Version.java \
 	com/redhat/et/libguestfs/XAttr.java \
 	com/redhat/et/libguestfs/INotifyEvent.java \
+	com/redhat/et/libguestfs/Partition.java \
 	com/redhat/et/libguestfs/GuestFS.java
diff --git a/ocaml/t/guestfs_060_readdir.ml b/ocaml/t/guestfs_060_readdir.ml
index 8035a09..f560700 100644
--- a/ocaml/t/guestfs_060_readdir.ml
+++ b/ocaml/t/guestfs_060_readdir.ml
@@ -28,7 +28,7 @@ let () =
   Guestfs.add_drive g "test.img";
   Guestfs.launch g;
 
-  Guestfs.sfdisk g "/dev/sda" 0 0 0 [|","|];
+  Guestfs.part_disk g "/dev/sda" "mbr";
   Guestfs.mkfs g "ext2" "/dev/sda1";
   Guestfs.mount g "/dev/sda1" "/";
   Guestfs.mkdir g "/p";
diff --git a/perl/t/060-readdir.t b/perl/t/060-readdir.t
index 898b44f..5ed5b7a 100644
--- a/perl/t/060-readdir.t
+++ b/perl/t/060-readdir.t
@@ -34,7 +34,7 @@ ok (1);
 $h->launch ();
 ok (1);
 
-$h->sfdisk ("/dev/sda", 0, 0, 0, [","]);
+$h->part_disk ("/dev/sda", "mbr");
 ok (1);
 $h->mkfs ("ext2", "/dev/sda1");
 ok (1);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index d7d12f7..a125f2a 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -35,6 +35,7 @@ daemon/modprobe.c
 daemon/mount.c
 daemon/names.c
 daemon/ntfs.c
+daemon/parted.c
 daemon/pingdaemon.c
 daemon/proto.c
 daemon/readdir.c
diff --git a/recipes/tar2vm.sh b/recipes/tar2vm.sh
index d71a5ef..713e9e3 100755
--- a/recipes/tar2vm.sh
+++ b/recipes/tar2vm.sh
@@ -3,7 +3,7 @@
 guestfish <<EOF
 alloc $2 $3
 run
-sfdisk /dev/sda 0 0 0 ,
+part-disk /dev/sda mbr
 mkfs ext3 /dev/sda1
 mount /dev/sda1 /
 tgz-in $1 /
diff --git a/regressions/rhbz503169c10.sh b/regressions/rhbz503169c10.sh
index 91284e0..5cf7069 100755
--- a/regressions/rhbz503169c10.sh
+++ b/regressions/rhbz503169c10.sh
@@ -26,7 +26,7 @@ dd if=/dev/zero of=test1.img bs=1024k count=10
 
 ../fish/guestfish -a test1.img <<EOF
 launch
-sfdisk /dev/sda 0 0 0 ,
+part-disk /dev/sda mbr
 mkfs ext2 /dev/sda1
 mount /dev/sda1 /
 ll /../dev/console
diff --git a/regressions/rhbz503169c13.sh b/regressions/rhbz503169c13.sh
index 7bace82..d360d5c 100755
--- a/regressions/rhbz503169c13.sh
+++ b/regressions/rhbz503169c13.sh
@@ -31,7 +31,7 @@ dd if=/dev/zero of=test1.img bs=1024k count=10
 
 ../fish/guestfish -a test1.img <<EOF
 run
-sfdisk /dev/sda 0 0 0 ,
+part-disk /dev/sda mbr
 mkfs ext2 /dev/sda1
 mount /dev/sda1 /
 mkdir /dev
diff --git a/regressions/test-cancellation-upload-daemoncancels.sh b/regressions/test-cancellation-upload-daemoncancels.sh
index 835c3bb..296d368 100755
--- a/regressions/test-cancellation-upload-daemoncancels.sh
+++ b/regressions/test-cancellation-upload-daemoncancels.sh
@@ -28,7 +28,7 @@ rm -f test.img
 alloc test.img 10M
 run
 
-sfdiskM /dev/sda ,
+part-disk /dev/sda mbr
 mkfs ext2 /dev/sda1
 mount /dev/sda1 /
 
diff --git a/regressions/test-remote.sh b/regressions/test-remote.sh
index ed02ccf..d778a07 100755
--- a/regressions/test-remote.sh
+++ b/regressions/test-remote.sh
@@ -26,7 +26,7 @@ eval `../fish/guestfish --listen`
 
 ../fish/guestfish --remote alloc test.img 10M
 ../fish/guestfish --remote run
-../fish/guestfish --remote sfdiskM /dev/sda ,
+../fish/guestfish --remote part-disk /dev/sda mbr
 ../fish/guestfish --remote mkfs ext2 /dev/sda1
 ../fish/guestfish --remote mount /dev/sda1 /
 
diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR
index c92ba56..9d683f8 100644
--- a/src/MAX_PROC_NR
+++ b/src/MAX_PROC_NR
@@ -1 +1 @@
-207
+214
diff --git a/src/generator.ml b/src/generator.ml
index d91fc24..abeb2b7 100755
--- a/src/generator.ml
+++ b/src/generator.ml
@@ -870,7 +870,7 @@ Return the recovery process enabled flag.");
 let daemon_functions = [
   ("mount", (RErr, [Device "device"; String "mountpoint"]), 1, [],
    [InitEmpty, Always, TestOutput (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["write_file"; "/new"; "new file contents"; "0"];
@@ -1414,7 +1414,7 @@ on the volume group C<volgroup>, with C<size> megabytes.");
 
   ("mkfs", (RErr, [String "fstype"; Device "device"]), 42, [],
    [InitEmpty, Always, TestOutput (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["write_file"; "/new"; "new file contents"; "0"];
@@ -1451,7 +1451,8 @@ To create a single partition occupying the whole disk, you would
 pass C<lines> as a single element list, when the single element being
 the string C<,> (comma).
 
-See also: C<guestfs_sfdisk_l>, C<guestfs_sfdisk_N>");
+See also: C<guestfs_sfdisk_l>, C<guestfs_sfdisk_N>,
+C<guestfs_part_init>");
 
   ("write_file", (RErr, [Pathname "path"; String "content"; Int "size"]), 44, [ProtocolLimitWarning],
    [InitBasicFS, Always, TestOutput (
@@ -1489,12 +1490,12 @@ use C<guestfs_upload>.");
 
   ("umount", (RErr, [String "pathordevice"]), 45, [FishAlias "unmount"],
    [InitEmpty, Always, TestOutputListOfDevices (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["mounts"]], ["/dev/sda1"]);
     InitEmpty, Always, TestOutputList (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["umount"; "/"];
@@ -2034,7 +2035,7 @@ to find out what you can do.");
 
   ("lvremove", (RErr, [Device "device"]), 77, [],
    [InitEmpty, Always, TestOutputList (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
@@ -2042,7 +2043,7 @@ to find out what you can do.");
        ["lvremove"; "/dev/VG/LV1"];
        ["lvs"]], ["/dev/VG/LV2"]);
     InitEmpty, Always, TestOutputList (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
@@ -2050,7 +2051,7 @@ to find out what you can do.");
        ["lvremove"; "/dev/VG"];
        ["lvs"]], []);
     InitEmpty, Always, TestOutputList (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
@@ -2067,7 +2068,7 @@ the VG name, C</dev/VG>.");
 
   ("vgremove", (RErr, [String "vgname"]), 78, [],
    [InitEmpty, Always, TestOutputList (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
@@ -2075,7 +2076,7 @@ the VG name, C</dev/VG>.");
        ["vgremove"; "VG"];
        ["lvs"]], []);
     InitEmpty, Always, TestOutputList (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
@@ -2091,7 +2092,7 @@ group (if any).");
 
   ("pvremove", (RErr, [Device "device"]), 79, [],
    [InitEmpty, Always, TestOutputListOfDevices (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
@@ -2100,7 +2101,7 @@ group (if any).");
        ["pvremove"; "/dev/sda1"];
        ["lvs"]], []);
     InitEmpty, Always, TestOutputListOfDevices (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
@@ -2109,7 +2110,7 @@ group (if any).");
        ["pvremove"; "/dev/sda1"];
        ["vgs"]], []);
     InitEmpty, Always, TestOutputListOfDevices (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV1"; "VG"; "50"];
@@ -2386,7 +2387,7 @@ the human-readable, canonical hex dump of the file.");
 
   ("zerofree", (RErr, [Device "device"]), 97, [],
    [InitNone, Always, TestOutput (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs"; "ext3"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["write_file"; "/new"; "test file"; "0"];
@@ -2424,7 +2425,9 @@ This runs L<sfdisk(8)> option to modify just the single
 partition C<n> (note: C<n> counts from 1).
 
 For other parameters, see C<guestfs_sfdisk>.  You should usually
-pass C<0> for the cyls/heads/sectors parameters.");
+pass C<0> for the cyls/heads/sectors parameters.
+
+See also: C<guestfs_part_add>");
 
   ("sfdisk_l", (RString "partitions", [Device "device"]), 100, [],
    [],
@@ -2432,7 +2435,9 @@ pass C<0> for the cyls/heads/sectors parameters.");
    "\
 This displays the partition table on C<device>, in the
 human-readable output of the L<sfdisk(8)> command.  It is
-not intended to be parsed.");
+not intended to be parsed.
+
+See also: C<guestfs_part_list>");
 
   ("sfdisk_kernel_geometry", (RString "partitions", [Device "device"]), 101, [],
    [],
@@ -2484,7 +2489,7 @@ are activated or deactivated.");
 
   ("lvresize", (RErr, [Device "device"; Int "mbytes"]), 105, [],
    [InitNone, Always, TestOutput (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["pvcreate"; "/dev/sda1"];
        ["vgcreate"; "VG"; "/dev/sda1"];
        ["lvcreate"; "LV"; "VG"; "10"];
@@ -2577,11 +2582,11 @@ Sleep for C<secs> seconds.");
 
   ("ntfs_3g_probe", (RInt "status", [Bool "rw"; Device "device"]), 110, [],
    [InitNone, Always, TestOutputInt (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs"; "ntfs"; "/dev/sda1"];
        ["ntfs_3g_probe"; "true"; "/dev/sda1"]], 0);
     InitNone, Always, TestOutputInt (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs"; "ext2"; "/dev/sda1"];
        ["ntfs_3g_probe"; "true"; "/dev/sda1"]], 12)],
    "probe NTFS volume",
@@ -2859,7 +2864,7 @@ the command C<mount -o loop file mountpoint>.");
 
   ("mkswap", (RErr, [Device "device"]), 130, [],
    [InitEmpty, Always, TestRun (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkswap"; "/dev/sda1"]])],
    "create a swap partition",
    "\
@@ -2867,7 +2872,7 @@ Create a swap partition on C<device>.");
 
   ("mkswap_L", (RErr, [String "label"; Device "device"]), 131, [],
    [InitEmpty, Always, TestRun (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkswap_L"; "hello"; "/dev/sda1"]])],
    "create a swap partition with a label",
    "\
@@ -2880,7 +2885,7 @@ a limitation of the kernel or swap tools.");
   ("mkswap_U", (RErr, [String "uuid"; Device "device"]), 132, [],
    (let uuid = uuidgen () in
     [InitEmpty, Always, TestRun (
-       [["sfdiskM"; "/dev/sda"; ","];
+       [["part_disk"; "/dev/sda"; "mbr"];
         ["mkswap_U"; uuid; "/dev/sda1"]])]),
    "create a swap partition with an explicit UUID",
    "\
@@ -3024,7 +3029,8 @@ only (rounded to the nearest cylinder) and you don't need
 to specify the cyls, heads and sectors parameters which
 were rarely if ever used anyway.
 
-See also C<guestfs_sfdisk> and the L<sfdisk(8)> manpage.");
+See also: C<guestfs_sfdisk>, the L<sfdisk(8)> manpage
+and C<guestfs_part_disk>");
 
   ("zfile", (RString "description", [String "meth"; Pathname "path"]), 140, [DeprecatedBy "file"],
    [],
@@ -3371,7 +3377,7 @@ This command disables the libguestfs appliance swap on file.");
 
   ("swapon_label", (RErr, [String "label"]), 174, [],
    [InitEmpty, Always, TestRun (
-      [["sfdiskM"; "/dev/sdb"; ","];
+      [["part_disk"; "/dev/sdb"; "mbr"];
        ["mkswap_L"; "swapit"; "/dev/sdb1"];
        ["swapon_label"; "swapit"];
        ["swapoff_label"; "swapit"];
@@ -3536,7 +3542,7 @@ and C<guestfs_setcon>");
 
   ("mkfs_b", (RErr, [String "fstype"; Int "blocksize"; Device "device"]), 187, [],
    [InitEmpty, Always, TestOutput (
-      [["sfdiskM"; "/dev/sda"; ","];
+      [["part_disk"; "/dev/sda"; "mbr"];
        ["mkfs_b"; "ext2"; "4096"; "/dev/sda1"];
        ["mount"; "/dev/sda1"; "/"];
        ["write_file"; "/new"; "new file contents"; "0"];
@@ -3893,6 +3899,193 @@ bytes of the file, starting at C<offset>, from file C<path>.
 This may read fewer bytes than requested.  For further details
 see the L<pread(2)> system call.");
 
+  ("part_init", (RErr, [Device "device"; String "parttype"]), 208, [],
+   [InitEmpty, Always, TestRun (
+      [["part_init"; "/dev/sda"; "gpt"]])],
+   "create an empty partition table",
+   "\
+This creates an empty partition table on C<device> of one of the
+partition types listed below.  Usually C<parttype> should be
+either C<msdos> or C<gpt> (for large disks).
+
+Initially there are no partitions.  Following this, you should
+call C<guestfs_part_add> for each partition required.
+
+Possible values for C<parttype> are:
+
+=over 4
+
+=item B<efi> | B<gpt>
+
+Intel EFI / GPT partition table.
+
+This is recommended for >= 2 TB partitions that will be accessed
+from Linux and Intel-based Mac OS X.  It also has limited backwards
+compatibility with the C<mbr> format.
+
+=item B<mbr> | B<msdos>
+
+The standard PC \"Master Boot Record\" (MBR) format used
+by MS-DOS and Windows.  This partition type will B<only> work
+for device sizes up to 2 TB.  For large disks we recommend
+using C<gpt>.
+
+=back
+
+Other partition table types that may work but are not
+supported include:
+
+=over 4
+
+=item B<aix>
+
+AIX disk labels.
+
+=item B<amiga> | B<rdb>
+
+Amiga \"Rigid Disk Block\" format.
+
+=item B<bsd>
+
+BSD disk labels.
+
+=item B<dasd>
+
+DASD, used on IBM mainframes.
+
+=item B<dvh>
+
+MIPS/SGI volumes.
+
+=item B<mac>
+
+Old Mac partition format.  Modern Macs use C<gpt>.
+
+=item B<pc98>
+
+NEC PC-98 format, common in Japan apparently.
+
+=item B<sun>
+
+Sun disk labels.
+
+=back");
+
+  ("part_add", (RErr, [Device "device"; String "prlogex"; Int64 "startsect"; Int64 "endsect"]), 209, [],
+   [InitEmpty, Always, TestRun (
+      [["part_init"; "/dev/sda"; "mbr"];
+       ["part_add"; "/dev/sda"; "primary"; "1"; "-1"]]);
+    InitEmpty, Always, TestRun (
+      [["part_init"; "/dev/sda"; "gpt"];
+       ["part_add"; "/dev/sda"; "primary"; "34"; "127"];
+       ["part_add"; "/dev/sda"; "primary"; "128"; "-34"]]);
+    InitEmpty, Always, TestRun (
+      [["part_init"; "/dev/sda"; "mbr"];
+       ["part_add"; "/dev/sda"; "primary"; "32"; "127"];
+       ["part_add"; "/dev/sda"; "primary"; "128"; "255"];
+       ["part_add"; "/dev/sda"; "primary"; "256"; "511"];
+       ["part_add"; "/dev/sda"; "primary"; "512"; "-1"]])],
+   "add a partition to the device",
+   "\
+This command adds a partition to C<device>.  If there is no partition
+table on the device, call C<guestfs_part_init> first.
+
+The C<prlogex> parameter is the type of partition.  Normally you
+should pass C<p> or C<primary> here, but MBR partition tables also
+support C<l> (or C<logical>) and C<e> (or C<extended>) partition
+types.
+
+C<startsect> and C<endsect> are the start and end of the partition
+in I<sectors>.  C<endsect> may be negative, which means it counts
+backwards from the end of the disk (C<-1> is the last sector).
+
+Creating a partition which covers the whole disk is not so easy.
+Use C<guestfs_part_disk> to do that.");
+
+  ("part_disk", (RErr, [Device "device"; String "parttype"]), 210, [DangerWillRobinson],
+   [InitEmpty, Always, TestRun (
+      [["part_disk"; "/dev/sda"; "mbr"]]);
+    InitEmpty, Always, TestRun (
+      [["part_disk"; "/dev/sda"; "gpt"]])],
+   "partition whole disk with a single primary partition",
+   "\
+This command is simply a combination of C<guestfs_part_init>
+followed by C<guestfs_part_add> to create a single primary partition
+covering the whole disk.
+
+C<parttype> is the partition table type, usually C<mbr> or C<gpt>,
+but other possible values are described in C<guestfs_part_init>.");
+
+  ("part_set_bootable", (RErr, [Device "device"; Int "partnum"; Bool "bootable"]), 211, [],
+   [InitEmpty, Always, TestRun (
+      [["part_disk"; "/dev/sda"; "mbr"];
+       ["part_set_bootable"; "/dev/sda"; "1"; "true"]])],
+   "make a partition bootable",
+   "\
+This sets the bootable flag on partition numbered C<partnum> on
+device C<device>.  Note that partitions are numbered from 1.
+
+The bootable flag is used by some PC BIOSes to determine which
+partition to boot from.  It is by no means universally recognized,
+and in any case if your operating system installed a boot
+sector on the device itself, then that takes precedence.");
+
+  ("part_set_name", (RErr, [Device "device"; Int "partnum"; String "name"]), 212, [],
+   [InitEmpty, Always, TestRun (
+      [["part_disk"; "/dev/sda"; "gpt"];
+       ["part_set_name"; "/dev/sda"; "1"; "thepartname"]])],
+   "set partition name",
+   "\
+This sets the partition name on partition numbered C<partnum> on
+device C<device>.  Note that partitions are numbered from 1.
+
+The partition name can only be set on certain types of partition
+table.  This works on C<gpt> but not on C<mbr> partitions.");
+
+  ("part_list", (RStructList ("partitions", "partition"), [Device "device"]), 213, [],
+   [], (* Tested in a regression test. *)
+   "list partitions on a device",
+   "\
+This command parses the partition table on C<device> and
+returns the list of partitions found.
+
+The fields in the returned structure are:
+
+=over 4
+
+=item B<part_num>
+
+Partition number, counting from 1.
+
+=item B<part_start>
+
+Start of the partition I<in bytes>.  To get sectors you have to
+divide by the device's sector size, see C<guestfs_blockdev_getss>.
+
+=item B<part_end>
+
+End of the partition in bytes.
+
+=item B<part_size>
+
+Size of the partition in bytes.
+
+=back");
+
+  ("part_get_parttype", (RString "parttype", [Device "device"]), 214, [],
+   [InitEmpty, Always, TestOutput (
+      [["part_disk"; "/dev/sda"; "gpt"];
+       ["part_get_parttype"; "/dev/sda"]], "gpt")],
+   "get the partition table type",
+   "\
+This command examines the partition table on C<device> and
+returns the partition table type (format) being used.
+
+Common return values include: C<msdos> (a DOS/Windows style MBR
+partition table), C<gpt> (a GPT/EFI-style partition table).  Other
+values are possible, although unusual.  See C<guestfs_part_init>
+for a full list.");
+
 ]
 
 let all_functions = non_daemon_functions @ daemon_functions
@@ -4061,6 +4254,14 @@ let structs = [
     "in_cookie", FUInt32;
     "in_name", FString;
   ];
+
+  (* Partition table entry. *)
+  "partition", [
+    "part_num", FInt32;
+    "part_start", FBytes;
+    "part_end", FBytes;
+    "part_size", FBytes;
+  ];
 ] (* end of structs *)
 
 (* Ugh, Java has to be different ..
@@ -4077,6 +4278,7 @@ let java_structs = [
   "version", "Version";
   "xattr", "XAttr";
   "inotify_event", "INotifyEvent";
+  "partition", "Partition";
 ]
 
 (* What structs are actually returned. *)
@@ -5951,14 +6153,14 @@ and generate_one_test_body name i test_name init test =
          [["blockdev_setrw"; "/dev/sda"];
           ["umount_all"];
           ["lvm_remove_all"];
-          ["sfdiskM"; "/dev/sda"; ","]]
+          ["part_disk"; "/dev/sda"; "mbr"]]
    | InitBasicFS ->
        pr "  /* InitBasicFS for %s: create ext2 on /dev/sda1 */\n" test_name;
        List.iter (generate_test_command_call test_name)
          [["blockdev_setrw"; "/dev/sda"];
           ["umount_all"];
           ["lvm_remove_all"];
-          ["sfdiskM"; "/dev/sda"; ","];
+          ["part_disk"; "/dev/sda"; "mbr"];
           ["mkfs"; "ext2"; "/dev/sda1"];
           ["mount"; "/dev/sda1"; "/"]]
    | InitBasicFSonLVM ->
@@ -5968,7 +6170,7 @@ and generate_one_test_body name i test_name init test =
          [["blockdev_setrw"; "/dev/sda"];
           ["umount_all"];
           ["lvm_remove_all"];
-          ["sfdiskM"; "/dev/sda"; ","];
+          ["part_disk"; "/dev/sda"; "mbr"];
           ["pvcreate"; "/dev/sda1"];
           ["vgcreate"; "VG"; "/dev/sda1"];
           ["lvcreate"; "LV"; "VG"; "8"];
-- 
1.6.5.rc2



More information about the Libguestfs mailing list