[Libguestfs] [PATCH 03/12] mountable: Implement Mountable support for all apis which take it

Matthew Booth mbooth at redhat.com
Thu Feb 7 15:57:49 UTC 2013


A Mountable is passed from the library to the daemon as a string. The daemon
stub parses it into a mountable_t, which it passes to the implementation.

Update all implementations which now take a mountable_t.
---
 daemon/blkid.c      | 33 +++++++++++++++++++++++-----
 daemon/daemon.h     | 41 ++++++++++++++++++++++++++++++++++
 daemon/ext2.c       | 15 ++++++++++---
 daemon/guestfsd.c   | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 daemon/labels.c     | 16 ++++++++++++--
 daemon/mount.c      | 49 +++++++++++++++++++++++++++++++++--------
 generator/c.ml      | 12 +++++++++-
 generator/daemon.ml | 11 +++++++---
 8 files changed, 213 insertions(+), 27 deletions(-)

diff --git a/daemon/blkid.c b/daemon/blkid.c
index 64919dd..d54ac22 100644
--- a/daemon/blkid.c
+++ b/daemon/blkid.c
@@ -67,21 +67,42 @@ get_blkid_tag (const char *device, const char *tag)
 }
 
 char *
-do_vfs_type (const char *device)
+do_vfs_type (const mountable_t *mountable)
 {
-  return get_blkid_tag (device, "TYPE");
+  switch (mountable->type) {
+    case MOUNTABLE_DEVICE:
+    case MOUNTABLE_BTRFSVOL:
+      return get_blkid_tag (mountable->device, "TYPE");
+    default:
+      reply_with_error ("Invalid mountable type: %i", mountable->type);
+      return NULL;
+  }
 }
 
 char *
-do_vfs_label (const char *device)
+do_vfs_label (const mountable_t *mountable)
 {
-  return get_blkid_tag (device, "LABEL");
+  switch (mountable->type) {
+    case MOUNTABLE_DEVICE:
+    case MOUNTABLE_BTRFSVOL:
+      return get_blkid_tag (mountable->device, "LABEL");
+    default:
+      reply_with_error ("Invalid mountable type: %i", mountable->type);
+      return NULL;
+  }
 }
 
 char *
-do_vfs_uuid (const char *device)
+do_vfs_uuid (const mountable_t *mountable)
 {
-  return get_blkid_tag (device, "UUID");
+  switch (mountable->type) {
+    case MOUNTABLE_DEVICE:
+    case MOUNTABLE_BTRFSVOL:
+      return get_blkid_tag (mountable->device, "UUID");
+    default:
+      reply_with_error ("Invalid mountable type: %i", mountable->type);
+      return NULL;
+  }
 }
 
 /* RHEL5 blkid doesn't have the -p (low-level probing) option and the
diff --git a/daemon/daemon.h b/daemon/daemon.h
index 44fb9ab..502e0eb 100644
--- a/daemon/daemon.h
+++ b/daemon/daemon.h
@@ -49,6 +49,19 @@ extern int xwrite (int sock, const void *buf, size_t len)
 extern int xread (int sock, void *buf, size_t len)
   __attribute__((__warn_unused_result__));
 
+/* Mountables */
+
+typedef enum {
+  MOUNTABLE_DEVICE,
+  MOUNTABLE_BTRFSVOL
+} mountable_type_t;
+
+typedef struct {
+  mountable_type_t type;
+  const char *device;
+  const char *volume;
+} mountable_t;
+
 /* Growable strings buffer. */
 struct stringsbuf {
   char **argv;
@@ -116,6 +129,8 @@ extern void trim (char *str);
 
 extern int device_name_translation (char *device);
 
+extern int parse_btrfsvol (char *desc, mountable_t *mountable);
+
 extern int prog_exists (const char *prog);
 
 extern void udev_settle (void);
@@ -330,6 +345,32 @@ is_zero (const char *buffer, size_t size)
     }                                                                   \
   } while (0)
 
+/* All functions that take a mountable argument must call this macro.
+ * It parses the mountable into a mountable_t, ensures any
+ * underlying device exists, and does device name translation
+ * (described in the guestfs(3) manpage).
+ *
+ * Note that the "string" argument may be modified.
+ */
+#define RESOLVE_MOUNTABLE(string,mountable,cancel_stmt,fail_stmt)       \
+  do {                                                                  \
+    if (strncmp ((string), "btrfsvol:", strlen ("btrfsvol:")) == 0) {   \
+      if (parse_btrfsvol ((string) + strlen ("btrfsvol:"), &(mountable)) == -1)\
+      {                                                                 \
+        cancel_stmt;                                                    \
+        reply_with_error ("%s: %s: expecting a btrfs volume",           \
+                          __func__, (string));                          \
+        fail_stmt;                                                      \
+      }                                                                 \
+    }                                                                   \
+                                                                        \
+    else {                                                              \
+      (mountable).type = MOUNTABLE_DEVICE;                              \
+      (mountable).device = (string);                                    \
+      RESOLVE_DEVICE((string), cancel_stmt, fail_stmt);                 \
+    }                                                                   \
+  } while (0)
+
 /* Helper for functions which need either an absolute path in the
  * mounted filesystem, OR a /dev/ device which exists.
  *
diff --git a/daemon/ext2.c b/daemon/ext2.c
index e4548d6..e10facd 100644
--- a/daemon/ext2.c
+++ b/daemon/ext2.c
@@ -116,13 +116,19 @@ do_tune2fs_l (const char *device)
 int
 do_set_e2label (const char *device, const char *label)
 {
-  return do_set_label (device, label);
+  mountable_t mountable;
+  mountable.type = MOUNTABLE_DEVICE;
+  mountable.device = device;
+  return do_set_label (&mountable, label);
 }
 
 char *
 do_get_e2label (const char *device)
 {
-  return do_vfs_label (device);
+  mountable_t mountable;
+  mountable.type = MOUNTABLE_DEVICE;
+  mountable.device = device;
+  return do_vfs_label (&mountable);
 }
 
 int
@@ -143,7 +149,10 @@ do_set_e2uuid (const char *device, const char *uuid)
 char *
 do_get_e2uuid (const char *device)
 {
-  return do_vfs_uuid (device);
+  mountable_t mountable;
+  mountable.type = MOUNTABLE_DEVICE;
+  mountable.device = device;
+  return do_vfs_uuid (&mountable);
 }
 
 /* If the filesystem is not mounted, run e2fsck -f on it unconditionally. */
diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c
index 70abbea..02454e3 100644
--- a/daemon/guestfsd.c
+++ b/daemon/guestfsd.c
@@ -358,6 +358,13 @@ read_cmdline (void)
 /* Return true iff device is the root device (and therefore should be
  * ignored from the point of view of user calls).
  */
+static int
+is_root_device_stat (struct stat *statbuf)
+{
+  if (statbuf->st_rdev == root_device) return 1;
+  return 0;
+}
+
 int
 is_root_device (const char *device)
 {
@@ -366,9 +373,8 @@ is_root_device (const char *device)
     perror (device);
     return 0;
   }
-  if (statbuf.st_rdev == root_device)
-    return 1;
-  return 0;
+
+  return is_root_device_stat (&statbuf);
 }
 
 /* Turn "/path" into "/sysroot/path".
@@ -1164,6 +1170,57 @@ device_name_translation (char *device)
   return -1;
 }
 
+/* Parse the mountable descriptor for a btrfs subvolume.  Don't call this
+ * directly - use the RESOLVE_MOUNTABLE macro.
+ *
+ * A btrfs subvolume is given as:
+ *
+ * btrfsvol:/dev/sda3/root
+ *
+ * where /dev/sda3 is a block device containing a btrfs filesystem, and root is
+ * the name of a subvolume on it. This function is passed the string following
+ * 'btrfsvol:'.
+ */
+int
+parse_btrfsvol (char *desc, mountable_t *mountable)
+{
+  mountable->type = MOUNTABLE_BTRFSVOL;
+
+  char *device = desc;
+
+  if (strncmp (device, "/dev/", strlen("/dev/")) == -1)
+    return -1;
+
+  char *volume = NULL;
+  char *slash = device + strlen("/dev/") - 1;
+  while ((slash = strchr (slash + 1, '/'))) {
+    *slash = '\0';
+
+    struct stat statbuf;
+    if (stat (device, &statbuf) == -1) {
+      perror (device);
+      return -1;
+    }
+
+    if (!S_ISDIR (statbuf.st_mode) &&
+        !is_root_device_stat(&statbuf) &&
+        device_name_translation (device) == 0)
+    {
+      volume = slash + 1;
+      break;
+    }
+
+    *slash = '/';
+  }
+
+  if (!volume) return -1;
+
+  mountable->device = device;
+  mountable->volume = volume;
+
+  return 0;
+}
+
 /* Check program exists and is executable on $PATH.  Actually, we
  * just assume PATH contains the default entries (see main() above).
  */
diff --git a/daemon/labels.c b/daemon/labels.c
index 2fda354..6e21bd8 100644
--- a/daemon/labels.c
+++ b/daemon/labels.c
@@ -71,15 +71,27 @@ ntfslabel (const char *device, const char *label)
 }
 
 int
-do_set_label (const char *device, const char *label)
+do_set_label (const mountable_t *mountable, const char *label)
 {
   int r;
 
   /* How we set the label depends on the filesystem type. */
-  CLEANUP_FREE char *vfs_type = do_vfs_type (device);
+  CLEANUP_FREE char *vfs_type = do_vfs_type (mountable);
   if (vfs_type == NULL)
     return -1;
 
+  const char *device;
+  switch (mountable->type) {
+    case MOUNTABLE_DEVICE:
+    case MOUNTABLE_BTRFSVOL:
+      device = mountable->device;
+      break;
+
+    default:
+      reply_with_error ("Invalid mountable type: %i", mountable->type);
+      return -1;
+  }
+
   if (STREQ (vfs_type, "ext2") || STREQ (vfs_type, "ext3")
       || STREQ (vfs_type, "ext4"))
     r = e2label (device, label);
diff --git a/daemon/mount.c b/daemon/mount.c
index af92834..484e45c 100644
--- a/daemon/mount.c
+++ b/daemon/mount.c
@@ -124,7 +124,7 @@ is_device_mounted (const char *device)
 
 int
 do_mount_vfs (const char *options, const char *vfstype,
-              const char *device, const char *mountpoint)
+              const mountable_t *mountable, const char *mountpoint)
 {
   int r;
   CLEANUP_FREE char *mp = NULL;
@@ -149,12 +149,43 @@ do_mount_vfs (const char *options, const char *vfstype,
     return -1;
   }
 
+  CLEANUP_FREE char *options_plus = NULL;
+  const char *device = mountable->device;
+  switch (mountable->type) {
+    case MOUNTABLE_DEVICE:
+      break;
+
+    case MOUNTABLE_BTRFSVOL:
+      if (options && strlen (options) > 0) {
+        if (asprintf (&options_plus, "subvol=%s,%s",
+                      mountable->volume, options) == -1)
+        {
+          reply_with_perror ("asprintf");
+          return -1;
+        }
+      }
+      
+      else {
+        if (asprintf (&options_plus, "subvol=%s", mountable->volume) == -1) {
+          reply_with_perror ("asprintf");
+          return -1;
+        }
+      }
+      break;
+
+    default:
+      reply_with_error ("Invalid mountable type: %i", mountable->type);
+      return -1;
+  }
+
   if (vfstype)
     r = command (NULL, &error,
-                 str_mount, "-o", options, "-t", vfstype, device, mp, NULL);
+                 str_mount, "-o", options_plus ? options_plus : options,
+                 "-t", vfstype, device, mp, NULL);
   else
     r = command (NULL, &error,
-                 str_mount, "-o", options, device, mp, NULL);
+                 str_mount, "-o", options_plus ? options_plus : options,
+                 device, mp, NULL);
   if (r == -1) {
     reply_with_error ("%s on %s (options: '%s'): %s",
                       device, mountpoint, options, error);
@@ -165,22 +196,22 @@ do_mount_vfs (const char *options, const char *vfstype,
 }
 
 int
-do_mount (const char *device, const char *mountpoint)
+do_mount (const mountable_t *mountable, const char *mountpoint)
 {
-  return do_mount_vfs ("", NULL, device, mountpoint);
+  return do_mount_vfs ("", NULL, mountable, mountpoint);
 }
 
 int
-do_mount_ro (const char *device, const char *mountpoint)
+do_mount_ro (const mountable_t *mountable, const char *mountpoint)
 {
-  return do_mount_vfs ("ro", NULL, device, mountpoint);
+  return do_mount_vfs ("ro", NULL, mountable, mountpoint);
 }
 
 int
-do_mount_options (const char *options, const char *device,
+do_mount_options (const char *options, const mountable_t *mountable,
                   const char *mountpoint)
 {
-  return do_mount_vfs (options, NULL, device, mountpoint);
+  return do_mount_vfs (options, NULL, mountable, mountpoint);
 }
 
 /* Takes optional arguments, consult optargs_bitmask. */
diff --git a/generator/c.ml b/generator/c.ml
index afa81ed..6d26868 100644
--- a/generator/c.ml
+++ b/generator/c.ml
@@ -121,12 +121,18 @@ let rec generate_prototype ?(extern = true) ?(static = false)
     List.iter (
       function
       | Pathname n
-      | Device n | Mountable n | Dev_or_Path n
+      | Device n | Dev_or_Path n
       | String n
       | OptString n
       | Key n ->
           next ();
           pr "const char *%s" n
+      | Mountable n ->
+          next();
+          if in_daemon then
+            pr "const mountable_t *%s" n
+          else
+            pr "const char *%s" n
       | StringList n | DeviceList n ->
           next ();
           pr "char *const *%s" n
@@ -160,6 +166,7 @@ let rec generate_prototype ?(extern = true) ?(static = false)
 
 (* Generate C call arguments, eg "(handle, foo, bar)" *)
 and generate_c_call_args ?handle ?(implicit_size_ptr = "&size")
+    ?(in_daemon = false)
     (ret, args, optargs) =
   pr "(";
   let comma = ref false in
@@ -176,6 +183,9 @@ and generate_c_call_args ?handle ?(implicit_size_ptr = "&size")
     | BufferIn n ->
         next ();
         pr "%s, %s_size" n n
+    | Mountable n ->
+        next ();
+        pr (if in_daemon then "&%s" else "%s") n
     | arg ->
         next ();
         pr "%s" (name_of_argt arg)
diff --git a/generator/daemon.ml b/generator/daemon.ml
index a075bdc..d81af93 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -38,6 +38,7 @@ let generate_daemon_actions_h () =
   pr "\n";
 
   pr "#include \"guestfs_protocol.h\"\n";
+  pr "#include \"daemon.h\"\n";
   pr "\n";
 
   List.iter (
@@ -111,11 +112,12 @@ and generate_daemon_actions () =
         pr "  struct guestfs_%s_args args;\n" name;
         List.iter (
           function
-          | Device n | Mountable n | Dev_or_Path n
+          | Device n | Dev_or_Path n
           | Pathname n
           | String n
           | Key n
           | OptString n -> pr "  char *%s;\n" n
+          | Mountable n -> pr "  mountable_t %s;\n" n
           | StringList n | DeviceList n -> pr "  char **%s;\n" n
           | Bool n -> pr "  int %s;\n" n
           | Int n -> pr "  int %s;\n" n
@@ -205,10 +207,13 @@ and generate_daemon_actions () =
               pr_args n;
               pr "  ABS_PATH (%s, %s, goto done);\n"
                 n (if is_filein then "cancel_receive ()" else "");
-          | Device n | Mountable n ->
+          | Device n ->
               pr_args n;
               pr "  RESOLVE_DEVICE (%s, %s, goto done);\n"
                 n (if is_filein then "cancel_receive ()" else "");
+          | Mountable n ->
+              pr "  RESOLVE_MOUNTABLE(args.%s, %s, %s, goto done);\n"
+                n n (if is_filein then "cancel_receive ()" else "");
           | Dev_or_Path n ->
               pr_args n;
               pr "  REQUIRE_ROOT_OR_RESOLVE_DEVICE (%s, %s, goto done);\n"
@@ -257,7 +262,7 @@ and generate_daemon_actions () =
             (function FileIn _ | FileOut _ -> false | _ -> true) args in
         let style = ret, args' @ args_of_optargs optargs, [] in
         pr "  r = do_%s " name;
-        generate_c_call_args style;
+        generate_c_call_args ~in_daemon:true style;
         pr ";\n" in
 
       (match ret with
-- 
1.8.1




More information about the Libguestfs mailing list