New APIs:
   part_set_gpt_type
   part_get_gpt_type
 ---
  appliance/packagelist.in |   1 +
  daemon/parted.c          | 125 +++++++++++++++++++++++++++++++++++++++++++++++
  generator/actions.ml     |  31 ++++++++++++
  generator/tests_c_api.ml |   7 +++
  generator/types.ml       |   5 ++
  src/MAX_PROC_NR          |   2 +-
  6 files changed, 170 insertions(+), 1 deletion(-)
 
 diff --git a/appliance/packagelist.in b/appliance/packagelist.in
 index 3ad343b..9b0cc09 100644
 --- a/appliance/packagelist.in
 +++ b/appliance/packagelist.in
 @@ -111,6 +111,7 @@ dosfstools
  file
  findutils
  gawk
 +gdisk
  grep
  gzip
  jfsutils
 diff --git a/daemon/parted.c b/daemon/parted.c
 index cc9a84b..5c080da 100644
 --- a/daemon/parted.c
 +++ b/daemon/parted.c
 @@ -30,6 +30,7 @@
  
  GUESTFSD_EXT_CMD(str_parted, parted);
  GUESTFSD_EXT_CMD(str_sfdisk, sfdisk);
 +GUESTFSD_EXT_CMD(str_sgdisk, sgdisk);
  
  /* Notes:
   *
 @@ -802,3 +803,127 @@ do_part_set_mbr_id (const char *device, int partnum, int idbyte)
  
    return 0;
  }
 +
 +int
 +do_part_set_gpt_type(const char *device, int partnum, const char *guid)
 +{
 +  if (partnum <= 0) {
 +    reply_with_error ("partition number must be >= 1");
 +    return -1;
 +  }
 +
 +  char *typecode = NULL;
 +  if (asprintf (&typecode, "%i:%s", partnum, guid) == -1) {
 +    reply_with_perror ("asprintf");
 +    return -1;
 +  }
 +
 +  char *err = NULL;
 +  int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
 +                    str_sgdisk, device, "-t", typecode, NULL);
 +
 +  if (r == -1) {
 +    reply_with_error ("%s %s -t %s: %s", str_sgdisk, device, typecode, err);
 +    free (typecode);
 +    free (err);
 +    return -1;
 +  }
 +  free (typecode);
 +  free (err);
 +
 +  return 0;
 +}
 +
 +char *
 +do_part_get_gpt_type(const char *device, int partnum)
 +{
 +  if (partnum <= 0) {
 +    reply_with_error ("partition number must be >= 1");
 +    return NULL;
 +  }
 +
 +  char *partnum_str = NULL;
 +  if (asprintf (&partnum_str, "%i", partnum) == -1) {
 +    reply_with_perror ("asprintf");
 +    return NULL;
 +  }
 +
 +  char *err = NULL;
 +  int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
 +                    str_sgdisk, device, "-i", partnum_str, NULL);
 +
 +  if (r == -1) {
 +    reply_with_error ("%s %s -i %s: %s", str_sgdisk, device, partnum_str,
err);
 +    free (partnum_str);
 +    free (err);
 +    return NULL;
 +  }
 +  free (partnum_str);
 +
 +  char **lines = split_lines (err);
 +  if (lines == NULL) {
 +    reply_with_error ("'%s %s -i %i' returned no output",
 +                      str_sgdisk, device, partnum);
 +    free (err);
 +    return NULL;
 +  }
 +
 +  /* Parse the output of sgdisk -i:
 +   * Partition GUID code: 21686148-6449-6E6F-744E-656564454649 (BIOS boot partition)
 +   * Partition unique GUID: 19AEC5FE-D63A-4A15-9D37-6FCBFB873DC0
 +   * First sector: 2048 (at 1024.0 KiB)
 +   * Last sector: 411647 (at 201.0 MiB)
 +   * Partition size: 409600 sectors (200.0 MiB)
 +   * Attribute flags: 0000000000000000
 +   * Partition name: 'EFI System Partition'
 +   */
 +  for (char **i = lines; *i != NULL; i++) {
 +    char *line = *i;
 +
 +    /* Skip blank lines */
 +    if (line[0] == '\0') continue;
 +
 +    /* Split the line in 2 at the colon */
 +    char *colon = strchr (line, ':');
 +    if (colon) {
 +#define SEARCH "Partition GUID code"
 +      if (colon - line == strlen(SEARCH) &&
 +          memcmp (line, SEARCH, strlen(SEARCH)) == 0)
 +      {
 +#undef SEARCH
 +        /* The value starts after the colon */
 +        char *value = colon + 1;
 +
 +        /* Skip any leading whitespace */
 +        value += strspn (value, " \t");
 +
 +        /* The value contains only valid GUID characters */
 +        size_t value_len = strspn (value, "-0123456789ABCDEF");
 +
 +        char *ret = malloc (value_len + 1);
 +        if (ret == NULL) {
 +          reply_with_perror ("malloc");
 +          return NULL;
 +        }
 +
 +        memcpy (ret, value, value_len);
 +        ret[value_len] = '\0';
 +        free (err);
 +        return ret;
 +      }
 +    } else {
 +      /* Ignore lines with no colon. Log to stderr so it will show up in
 +       * LIBGUESTFS_DEBUG. */
 +      if (verbose) {
 +        fprintf (stderr, "get-gpt-type: unexpected sgdisk output ignored:
%s\n",
 +                 line);
 +      }
 +    }
 +  }
 +  free (err);
 +
 +  /* If we got here it means we didn't find the Partition GUID code */
 +  reply_with_error ("sgdisk output did not contain Partition GUID code. "
 +                    "See LIBGUESTFS_DEBUG output for more details");
 +  return NULL;
 +}
 diff --git a/generator/actions.ml b/generator/actions.ml
 index f78d851..9cdd369 100644
 --- a/generator/actions.ml
 +++ b/generator/actions.ml
 @@ -10692,6 +10692,37 @@ not always, the name of a Windows drive, eg. C<E:>."
};
  Return the list of partitions in the volume named C<volume> in the disk
  group with GUID <diskgroup>." };
  
 +  { defaults with
 +    name = "part_set_gpt_type";
 +    style = RErr, [Device "device"; Int "partnum"; String
"guid"], [];
 +    proc_nr = Some 392;
 +    tests = [];
 +    shortdesc = "set the type GUID of a GPT partition";
 +    longdesc = "\
 +Set the type GUID of numbered GPT partition C<partnum> to C<guid>. Return
an
 +error if the partition table of C<device> isn't GPT, or if C<guid> is
not a
 +valid GUID.
 +
 +See 
L<http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUI...
 +for a useful list of type GUIDs." };
 +
 +  { defaults with
 +    name = "part_get_gpt_type";
 +    style = RString "guid", [Device "device"; Int
"partnum"], [];
 +    proc_nr = Some 393;
 +    tests = [
 +      InitGPT, Always, TestOutput (
 +        [["part_set_gpt_type"; "/dev/sda"; "1";
 +          "01234567-89AB-CDEF-0123-456789ABCDEF"];
 +         ["part_get_gpt_type"; "/dev/sda"; "1"]],
 +         "01234567-89AB-CDEF-0123-456789ABCDEF");
 +    ];
 +    shortdesc = "get the type GUID of a GPT partition";
 +    longdesc = "\
 +Return the type GUID of numbered GPT partition C<partnum>. For MBR partitions,
 +return an appropriate GUID corresponding to the MBR type. Behaviour is undefined
 +for other partition types." };
 +
  ]
  
  (* Non-API meta-commands available only in guestfish.
 diff --git a/generator/tests_c_api.ml b/generator/tests_c_api.ml
 index 116a8b0..2aa78df 100644
 --- a/generator/tests_c_api.ml
 +++ b/generator/tests_c_api.ml
 @@ -442,6 +442,13 @@ and generate_one_test_body name i test_name init test =
            ["umount_all"];
            ["lvm_remove_all"];
            ["part_disk"; "/dev/sda"; "mbr"]]
 +   | InitGPT ->
 +       pr "  /* InitGPT for %s: create /dev/sda1 */\n" test_name;
 +       List.iter (generate_test_command_call test_name)
 +         [["blockdev_setrw"; "/dev/sda"];
 +          ["umount_all"];
 +          ["lvm_remove_all"];
 +          ["part_disk"; "/dev/sda"; "gpt"]]
     | InitBasicFS ->
         pr "  /* InitBasicFS for %s: create ext2 on /dev/sda1 */\n" test_name;
         List.iter (generate_test_command_call test_name)
 diff --git a/generator/types.ml b/generator/types.ml
 index cff3c6d..e6f56ec 100644
 --- a/generator/types.ml
 +++ b/generator/types.ml
 @@ -325,6 +325,11 @@ and test_init =
       *)
    | InitPartition
  
 +    (* Identical to InitPartition, except that the partition table is GPT
 +     * instead of MBR.
 +     *)
 +  | InitGPT
 +
      (* /dev/sda contains a single partition /dev/sda1, which is formatted
       * as ext2, empty [except for lost+found] and mounted on /.
       * No LVM.
 diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR
 index b570ddb..25685cf 100644
 --- a/src/MAX_PROC_NR
 +++ b/src/MAX_PROC_NR
 @@ -1 +1 @@
 -391
 +393
 -- 
 1.7.11.7 
virt-p2v converts physical machines to virtual machines.  Boot with a
live CD or over the network (PXE) and turn machines into Xen guests.