[Libguestfs] [PATCH v6 33/41] daemon: Reimplement ‘part_list’ API in OCaml.

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

---
 daemon/parted.c           | 56 -----------------------------------------------
 daemon/parted.ml          | 51 ++++++++++++++++++++++++++++++++++++++++++
 daemon/parted.mli         |  8 +++++++
 generator/actions_core.ml |  1 +
 4 files changed, 60 insertions(+), 56 deletions(-)

diff --git a/daemon/parted.c b/daemon/parted.c
index a1e5c81cf..125aec60b 100644
--- a/daemon/parted.c
+++ b/daemon/parted.c
@@ -387,62 +387,6 @@ do_part_get_parttype (const char *device)
   return r;
 }

-guestfs_int_partition_list *
-do_part_list (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;
-
-  guestfs_int_partition_list *r;
-
-  /* lines[0] is "BYT;", lines[1] is the device line which we ignore,
-   * lines[2..] are the partitions themselves.  Count how many.
-   */
-  size_t nr_rows = 0, row;
-  for (row = 2; lines[row] != NULL; ++row)
-    ++nr_rows;
-
-  r = malloc (sizeof *r);
-  if (r == NULL) {
-    reply_with_perror ("malloc");
-    return NULL;
-  }
-  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. */
-  size_t i;
-  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 ("could not parse row from output of parted print
command: %s", lines[row]);
-      goto error3;
-    }
-  }
-
-  return r;
-
- error3:
-  free (r->guestfs_int_partition_list_val);
- error2:
-  free (r);
-  return NULL;
-}
-
 int
 do_part_get_bootable (const char *device, int partnum)
 {
diff --git a/daemon/parted.ml b/daemon/parted.ml
index 6be41cf66..37e1b42be 100644
--- a/daemon/parted.ml
+++ b/daemon/parted.ml
@@ -22,6 +22,8 @@ open Std_utils

 open Utils

+include Structs
+
 (* Test if [sfdisk] is recent enough to have [--part-type], to be used
  * instead of [--print-id] and [--change-id].
  *)
@@ -53,3 +55,52 @@ let part_get_mbr_id device partnum
   (* It's printed in hex, possibly with a leading space. *)
   sscanf out " %x" identity
+
+let print_partition_table ~add_m_option device +  udev_settle ();
+
+  let args = ref [] in
+  if add_m_option then push_back args "-m";
+  push_back args "-s";
+  push_back args "--";
+  push_back args device;
+  push_back args "unit";
+  push_back args "b";
+  push_back args "print";
+
+  let out +    try command "parted" !args
+    with
+      (* Translate "unrecognised disk label" into an errno code. *)
+      Failure str when String.find str "unrecognised disk label"
>= 0 ->
+        raise (Unix.Unix_error (Unix.EINVAL, "parted", device ^
": " ^ str)) in
+
+  udev_settle ();
+
+  (* Split the output into lines. *)
+  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[2..] are the partitions themselves.
+   *)
+  match lines with
+  | "BYT;" :: _ :: lines -> 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
+
+  List.map (
+    fun line ->
+      try sscanf line "%d:%LdB:%LdB:%LdB"
+                 (fun num start end_ size ->
+                   { part_num = Int32.of_int num;
+                     part_start = start; part_end = end_; part_size = size })
+      with Scan_failure err ->
+        failwithf "could not parse row from output of 'parted
print' command: %s: %s"
+                  line err
+  ) lines
diff --git a/daemon/parted.mli b/daemon/parted.mli
index 33eb6d30d..057d7e8c7 100644
--- a/daemon/parted.mli
+++ b/daemon/parted.mli
@@ -16,4 +16,12 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *)

+type partition = {
+  part_num : int32;
+  part_start : int64;
+  part_end : int64;
+  part_size : int64;
+}
+
 val part_get_mbr_id : string -> int -> int
+val part_list : string -> partition list
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index d5946b3f5..b1e2559e0 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -5039,6 +5039,7 @@ table.  This works on C<gpt> but not on C<mbr>
partitions." };
   { defaults with
     name = "part_list"; added = (1, 0, 78);
     style = RStructList ("partitions", "partition"),
[String (Device, "device")], [];
+    impl = OCaml "Parted.part_list";
     tests = [] (* XXX Add a regression test for this. *);
     shortdesc = "list partitions on a device";
     longdesc = "\
--
2.13.0