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