[Libguestfs] [PATCH v6 39/41] daemon: Reimplement ‘part_get_parttype’, ‘part_get_gpt_type’, ‘part_get_gpt_guid’ APIs in OCaml.

Richard W.M. Jones
Thu, 15 Jun 2017 17:06:29 -0000

---
 daemon/parted.c           | 176 +++++-----------------------------------------
 daemon/parted.ml          |  70 +++++++++++++++++-
 daemon/parted.mli         |   5 ++
 generator/actions_core.ml |   3 +
 4 files changed, 92 insertions(+), 162 deletions(-)

diff --git a/daemon/parted.c b/daemon/parted.c
index 125aec60b..1c81cd968 100644
--- a/daemon/parted.c
+++ b/daemon/parted.c
@@ -348,45 +348,6 @@ print_partition_table (const char *device, bool
add_m_option)
   return out;
 }

-char *
-do_part_get_parttype (const char *device)
-{
-  CLEANUP_FREE char *out = print_partition_table (device, true);
-  if (!out)
-    return NULL;
-
-  CLEANUP_FREE_STRING_LIST char **lines = split_lines (out);
-  if (!lines)
-    return NULL;
-
-  if (lines[0] == NULL || STRNEQ (lines[0], "BYT;")) {
-    reply_with_error ("unknown signature, expected \"BYT;\" as
first line of the output: %s",
-                      lines[0] ? lines[0] : "(signature was null)");
-    return NULL;
-  }
-
-  if (lines[1] == NULL) {
-    reply_with_error ("parted didn't return a line describing the
device");
-    return NULL;
-  }
-
-  /* lines[1] is something like:
-   * "/dev/sda:1953525168s:scsi:512:512:msdos:ATA Hitachi HDT72101;"
-   */
-  char *r = get_table_field (lines[1], 5);
-  if (r == NULL)
-    return NULL;
-
-  /* If "loop" return an error (RHBZ#634246). */
-  if (STREQ (r, "loop")) {
-    free (r);
-    reply_with_error ("not a partitioned device");
-    return NULL;
-  }
-
-  return r;
-}
-
 int
 do_part_get_bootable (const char *device, int partnum)
 {
@@ -557,126 +518,6 @@ do_part_set_gpt_guid (const char *device, int partnum,
const char *guid)
   return 0;
 }

-static char *
-sgdisk_info_extract_field (const char *device, int partnum, const char *field,
-                           char *(*extract) (const char *path))
-{
-  if (partnum <= 0) {
-    reply_with_error ("partition number must be >= 1");
-    return NULL;
-  }
-
-  CLEANUP_FREE char *partnum_str = NULL;
-  if (asprintf (&partnum_str, "%i", partnum) == -1) {
-    reply_with_perror ("asprintf");
-    return NULL;
-  }
-
-  udev_settle ();
-
-  CLEANUP_FREE 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);
-    return NULL;
-  }
-
-  udev_settle ();
-
-  CLEANUP_FREE_STRING_LIST char **lines = split_lines (err);
-  if (lines == NULL) {
-    reply_with_error ("'%s %s -i %i' returned no output",
-                      str_sgdisk, device, partnum);
-    return NULL;
-  }
-
-  const int fieldlen = strlen (field);
-
-  /* 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) {
-      if (colon - line == fieldlen &&
-          memcmp (line, field, fieldlen) == 0)
-      {
-        /* The value starts after the colon */
-        char *value = colon + 1;
-
-        /* Skip any leading whitespace */
-        value += strspn (value, " \t");
-
-        /* Extract the actual information from the field. */
-        char *ret = extract (value);
-        if (ret == NULL) {
-          /* The extraction function already sends the error. */
-          return NULL;
-        }
-
-        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);
-      }
-    }
-  }
-
-  /* If we got here it means we didn't find the field */
-  reply_with_error ("sgdisk output did not contain '%s'. "
-                    "See LIBGUESTFS_DEBUG output for more details",
field);
-  return NULL;
-}
-
-static char *
-extract_uuid (const char *value)
-{
-  /* The value contains only valid GUID characters */
-  const 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';
-  return ret;
-}
-
-char *
-do_part_get_gpt_type (const char *device, int partnum)
-{
-  return sgdisk_info_extract_field (device, partnum,
-                                    "Partition GUID code",
extract_uuid);
-}
-
-char *
-do_part_get_gpt_guid (const char *device, int partnum)
-{
-  return sgdisk_info_extract_field (device, partnum,
-                                    "Partition unique GUID",
extract_uuid);
-}
-
 char *
 do_part_get_name (const char *device, int partnum)
 {
@@ -840,6 +681,23 @@ do_part_get_mbr_part_type (const char *device, int partnum)
   return NULL;
 }

+static char *
+extract_uuid (const char *value)
+{
+  /* The value contains only valid GUID characters */
+  const 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';
+  return ret;
+}
+
 char *
 do_part_get_disk_guid (const char *device)
 {
diff --git a/daemon/parted.ml b/daemon/parted.ml
index 37e1b42be..737b1a96e 100644
--- a/daemon/parted.ml
+++ b/daemon/parted.ml
@@ -81,18 +81,18 @@ let print_partition_table ~add_m_option device    let out =
String.trim out in
   let lines = String.nsplit "\n" out in

-  (* lines[0] is "BYT;", lines[1] is the device line which we ignore,
+  (* lines[0] is "BYT;", lines[1] is the device line,
    * lines[2..] are the partitions themselves.
    *)
   match lines with
-  | "BYT;" :: _ :: lines -> lines
+  | "BYT;" :: device_line :: lines -> device_line, lines
   | [] | [_] ->
      failwith "too few rows of output from 'parted print'
command"
   | _ ->
      failwith "did not see 'BYT;' magic value in 'parted
print' command"

 let part_list device -  let lines = print_partition_table ~add_m_option:true
device in
+  let _, lines = print_partition_table ~add_m_option:true device in

   List.map (
     fun line ->
@@ -104,3 +104,67 @@ let part_list device          failwithf "could not
parse row from output of 'parted print' command: %s: %s"
                   line err
   ) lines
+
+let part_get_parttype device +  let device_line, _ = print_partition_table
~add_m_option:true device in
+
+  (* device_line is something like:
+   * "/dev/sda:1953525168s:scsi:512:512:msdos:ATA Hitachi HDT72101;"
+   *)
+  let fields = String.nsplit ":" device_line in
+  match fields with
+  | _::_::_::_::_::"loop"::_ -> (* If "loop" return an
error (RHBZ#634246). *)
+     failwithf "%s: not a partitioned device" device
+  | _::_::_::_::_::ret::_ -> ret
+  | _ ->
+     failwithf "%s: cannot parse the output of parted" device
+
+let rec part_get_gpt_type device partnum +  sgdisk_info_extract_uuid_field
device partnum "Partition GUID code"
+and part_get_gpt_guid device partnum +  sgdisk_info_extract_uuid_field device
partnum "Partition unique GUID"
+
+and sgdisk_info_extract_uuid_field device partnum field +  if partnum <= 0
then failwith "partition number must be >= 1";
+
+  udev_settle ();
+
+  let out = command ~flags:[CommandFlagFoldStdoutOnStderr]
+                    "sgdisk" [ device; "-i"; string_of_int
partnum ] in
+
+  udev_settle ();
+
+  let out = String.trim out in
+  let lines = String.nsplit "\n" out in
+
+  (* 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'
+   *)
+  let field_len = String.length field in
+  let rec loop = function
+    | [] ->
+       failwithf "%s: sgdisk output did not contain '%s'"
device field
+    | line :: lines when String.is_prefix line field &&
+                         String.length line > field_len &&
+                         line.[field_len] = ':' ->
+       let value +         String.sub line field_len (String.length line -
field_len) in
+
+       (* Skip any whitespace after the colon. *)
+       let value = String.triml value in
+
+       (* Extract the UUID. *)
+       extract_uuid value
+
+    | _ :: lines -> loop lines in
+  loop lines
+
+and extract_uuid value +  (* The value contains only valid GUID characters. *)
+  String.sub value 0 (String.span value "-0123456789ABCDEF")
diff --git a/daemon/parted.mli b/daemon/parted.mli
index 057d7e8c7..5a77a8779 100644
--- a/daemon/parted.mli
+++ b/daemon/parted.mli
@@ -25,3 +25,8 @@ type partition = {

 val part_get_mbr_id : string -> int -> int
 val part_list : string -> partition list
+
+val part_get_parttype : string -> string
+
+val part_get_gpt_type : string -> int -> string
+val part_get_gpt_guid : string -> int -> string
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 4ec83d22d..c3421133e 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -5073,6 +5073,7 @@ Size of the partition in bytes.
   { defaults with
     name = "part_get_parttype"; added = (1, 0, 78);
     style = RString (RPlainString, "parttype"), [String (Device,
"device")], [];
+    impl = OCaml "Parted.part_get_parttype";
     tests = [
       InitEmpty, Always, TestResultString (
         [["part_disk"; "/dev/sda"; "gpt"];
@@ -8247,6 +8248,7 @@ for a useful list of type GUIDs." };
   { defaults with
     name = "part_get_gpt_type"; added = (1, 21, 1);
     style = RString (RPlainString, "guid"), [String (Device,
"device"); Int "partnum"], [];
+    impl = OCaml "Parted.part_get_gpt_type";
     optional = Some "gdisk";
     tests = [
       InitGPT, Always, TestResultString (
@@ -9067,6 +9069,7 @@ valid GUID." };
   { defaults with
     name = "part_get_gpt_guid"; added = (1, 29, 25);
     style = RString (RPlainString, "guid"), [String (Device,
"device"); Int "partnum"], [];
+    impl = OCaml "Parted.part_get_gpt_guid";
     optional = Some "gdisk";
     tests = [
       InitGPT, Always, TestResultString (
--
2.13.0