daemon: count_strings function returns size_t
[libguestfs.git] / daemon / parted.c
index 2b0df44..bf45f8b 100644 (file)
@@ -46,7 +46,7 @@ recover_blkrrpart (const char *device, const char *err)
                "Error informing the kernel about modifications to partition"))
     return -1;
 
-  r = command (NULL, NULL, "/sbin/blockdev", "--rereadpt", device, NULL);
+  r = command (NULL, NULL, "blockdev", "--rereadpt", device, NULL);
   if (r == -1)
     return -1;
 
@@ -55,18 +55,18 @@ recover_blkrrpart (const char *device, const char *err)
   return 0;
 }
 
-#define RUN_PARTED(device,...)                                          \
+#define RUN_PARTED(error,device,...)                                    \
   do {                                                                  \
     int r;                                                              \
     char *err;                                                          \
                                                                         \
     r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,       \
-                  "/sbin/parted", "-s", "--", (device), __VA_ARGS__);   \
+                  "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;                                                      \
+        error;                                                          \
       }                                                                 \
     }                                                                   \
                                                                         \
@@ -103,11 +103,11 @@ 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\"");
+    reply_with_error ("unknown partition type: common choices are \"gpt\" and \"msdos\"");
     return -1;
   }
 
-  RUN_PARTED (device, "mklabel", parttype, NULL);
+  RUN_PARTED (return -1, device, "mklabel", parttype, NULL);
 
   udev_settle ();
 
@@ -133,12 +133,12 @@ do_part_add (const char *device, const char *prlogex,
   else if (STREQ (prlogex, "e"))
     prlogex = "extended";
   else {
-    reply_with_error ("part-add: unknown partition type: %s: this should be \"primary\", \"logical\" or \"extended\"", prlogex);
+    reply_with_error ("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");
+    reply_with_error ("startsect cannot be negative");
     return -1;
   }
   /* but endsect can be negative */
@@ -151,7 +151,7 @@ do_part_add (const char *device, const char *prlogex,
    * 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);
+  RUN_PARTED (return -1, device, "mkpart", prlogex, startstr, endstr, NULL);
 
   udev_settle ();
 
@@ -159,6 +159,18 @@ do_part_add (const char *device, const char *prlogex,
 }
 
 int
+do_part_del (const char *device, int partnum)
+{
+  char partnum_str[16];
+  snprintf (partnum_str, sizeof partnum_str, "%d", partnum);
+
+  RUN_PARTED (return -1, device, "rm", partnum_str, NULL);
+
+  udev_settle ();
+  return 0;
+}
+
+int
 do_part_disk (const char *device, const char *parttype)
 {
   const char *startstr;
@@ -166,7 +178,7 @@ do_part_disk (const char *device, const char *parttype)
 
   parttype = check_parttype (parttype);
   if (!parttype) {
-    reply_with_error ("part-disk: unknown partition type: common choices are \"gpt\" and \"msdos\"");
+    reply_with_error ("unknown partition type: common choices are \"gpt\" and \"msdos\"");
     return -1;
   }
 
@@ -183,7 +195,8 @@ do_part_disk (const char *device, const char *parttype)
     endstr = "-1s";
   }
 
-  RUN_PARTED (device,
+  RUN_PARTED (return -1,
+              device,
               "mklabel", parttype,
               /* See comment about about the parted mkpart command. */
               "mkpart", STREQ (parttype, "gpt") ? "p1" : "primary",
@@ -201,7 +214,8 @@ do_part_set_bootable (const char *device, int partnum, int bootable)
 
   snprintf (partstr, sizeof partstr, "%d", partnum);
 
-  RUN_PARTED (device, "set", partstr, "boot", bootable ? "on" : "off", NULL);
+  RUN_PARTED (return -1,
+              device, "set", partstr, "boot", bootable ? "on" : "off", NULL);
 
   udev_settle ();
 
@@ -215,13 +229,43 @@ do_part_set_name (const char *device, int partnum, const char *name)
 
   snprintf (partstr, sizeof partstr, "%d", partnum);
 
-  RUN_PARTED (device, "name", partstr, name, NULL);
+  RUN_PARTED (return -1, device, "name", partstr, name, NULL);
 
   udev_settle ();
 
   return 0;
 }
 
+/* Return the nth field from a string of ':'/';'-delimited strings.
+ * Useful for parsing the return value from print_partition_table
+ * function below.
+ */
+static char *
+get_table_field (const char *line, int n)
+{
+  const char *p = line;
+
+  while (*p && n > 0) {
+    p += strcspn (p, ":;") + 1;
+    n--;
+  }
+
+  if (n > 0) {
+    reply_with_error ("not enough fields in output of parted print command: %s",
+                      line);
+    return NULL;
+  }
+
+  size_t len = strcspn (p, ":;");
+  char *q = strndup (p, len);
+  if (q == NULL) {
+    reply_with_perror ("strndup");
+    return NULL;
+  }
+
+  return q;
+}
+
 static char **
 print_partition_table (const char *device)
 {
@@ -229,7 +273,7 @@ print_partition_table (const char *device)
   int r;
   char **lines;
 
-  r = command (&out, &err, "/sbin/parted", "-m", "--", device,
+  r = command (&out, &err, "parted", "-m", "--", device,
                "unit", "b",
                "print", NULL);
   if (r == -1) {
@@ -249,14 +293,14 @@ print_partition_table (const char *device)
     return NULL;
 
   if (lines[0] == NULL || STRNEQ (lines[0], "BYT;")) {
-    reply_with_error ("parted print: unknown signature, expected \"BYT;\" as first line of the output: %s",
+    reply_with_error ("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");
+    reply_with_error ("parted didn't return a line describing the device");
     free_strings (lines);
     return NULL;
   }
@@ -267,31 +311,15 @@ print_partition_table (const char *device)
 char *
 do_part_get_parttype (const char *device)
 {
-  char **lines;
-  char *r;
-
-  lines = print_partition_table (device);
+  char **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");
+  char *r = get_table_field (lines[1], 5);
+  if (r == NULL) {
     free_strings (lines);
     return NULL;
   }
@@ -339,7 +367,7 @@ do_part_list (const char *device)
                 &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]);
+      reply_with_error ("could not parse row from output of parted print command: %s", lines[row]);
       goto error3;
     }
   }
@@ -355,3 +383,92 @@ do_part_list (const char *device)
   free_strings (lines);
   return NULL;
 }
+
+int
+do_part_get_bootable (const char *device, int partnum)
+{
+  char **lines = print_partition_table (device);
+  if (!lines)
+    return -1;
+
+  /* We want lines[1+partnum]. */
+  if (count_strings (lines) < (size_t) (1+partnum)) {
+    reply_with_error ("partition number out of range: %d", partnum);
+    free_strings (lines);
+    return -1;
+  }
+
+  char *boot = get_table_field (lines[1+partnum], 6);
+  if (boot == NULL) {
+    free_strings (lines);
+    return -1;
+  }
+
+  int r = STREQ (boot, "boot");
+
+  free (boot);
+  free_strings (lines);
+
+  return r;
+}
+
+/* Currently we use sfdisk for getting and setting the ID byte.  In
+ * future, extend parted to provide this functionality.  As a result
+ * of using sfdisk, this won't work for non-MBR-style partitions, but
+ * that limitation is noted in the documentation and we can extend it
+ * later without breaking the ABI.
+ */
+int
+do_part_get_mbr_id (const char *device, int partnum)
+{
+  char partnum_str[16];
+  snprintf (partnum_str, sizeof partnum_str, "%d", partnum);
+
+  char *out, *err;
+  int r;
+
+  r = command (&out, &err, "sfdisk", "--print-id", device, partnum_str, NULL);
+  if (r == -1) {
+    reply_with_error ("sfdisk --print-id: %s", err);
+    free (out);
+    free (err);
+    return -1;
+  }
+
+  free (err);
+
+  /* It's printed in hex ... */
+  int id;
+  if (sscanf (out, "%x", &id) != 1) {
+    reply_with_error ("sfdisk --print-id: cannot parse output: %s", out);
+    free (out);
+    return -1;
+  }
+
+  free (out);
+  return id;
+}
+
+int
+do_part_set_mbr_id (const char *device, int partnum, int idbyte)
+{
+  char partnum_str[16];
+  snprintf (partnum_str, sizeof partnum_str, "%d", partnum);
+
+  char idbyte_str[16];
+  snprintf (idbyte_str, sizeof partnum_str, "%x", idbyte); /* NB: hex */
+
+  char *err;
+  int r;
+
+  r = command (NULL, &err, "sfdisk",
+               "--change-id", device, partnum_str, idbyte_str, NULL);
+  if (r == -1) {
+    reply_with_error ("sfdisk --change-id: %s", err);
+    free (err);
+    return -1;
+  }
+
+  free (err);
+  return 0;
+}