From 47b929b7893b3a76ff22760d245cb80720ae6345 Mon Sep 17 00:00:00 2001 From: Matthew Booth Date: Thu, 24 Jan 2013 10:10:13 +0000 Subject: [PATCH] mountable: Implement Mountable support for all apis which take it 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 | 12 ++++----- daemon/daemon.h | 41 +++++++++++++++++++++++++++++ daemon/ext2.c | 15 ++++++++--- daemon/guestfsd.c | 63 ++++++++++++++++++++++++++++++++++++++++++--- daemon/labels.c | 8 +++--- daemon/mount.c | 40 +++++++++++++++++++++------- generator/c.ml | 12 ++++++++- generator/daemon.ml | 11 +++++--- 8 files changed, 173 insertions(+), 29 deletions(-) diff --git a/daemon/blkid.c b/daemon/blkid.c index 64919dd7e..01ee044ea 100644 --- a/daemon/blkid.c +++ b/daemon/blkid.c @@ -67,21 +67,21 @@ 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"); + return get_blkid_tag (mountable->device, "TYPE"); } char * -do_vfs_label (const char *device) +do_vfs_label (const mountable_t *mountable) { - return get_blkid_tag (device, "LABEL"); + return get_blkid_tag (mountable->device, "LABEL"); } char * -do_vfs_uuid (const char *device) +do_vfs_uuid (const mountable_t *mountable) { - return get_blkid_tag (device, "UUID"); + return get_blkid_tag (mountable->device, "UUID"); } /* RHEL5 blkid doesn't have the -p (low-level probing) option and the diff --git a/daemon/daemon.h b/daemon/daemon.h index 44fb9abe8..cb3da804f 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 (STRPREFIX ((string), "btrfsvol:")) { \ + 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 e4548d6f6..e10facda2 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 70abbea54..02454e3e9 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 2fda354c8..5ac74c3d2 100644 --- a/daemon/labels.c +++ b/daemon/labels.c @@ -71,21 +71,21 @@ 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; if (STREQ (vfs_type, "ext2") || STREQ (vfs_type, "ext3") || STREQ (vfs_type, "ext4")) - r = e2label (device, label); + r = e2label (mountable->device, label); else if (STREQ (vfs_type, "ntfs")) - r = ntfslabel (device, label); + r = ntfslabel (mountable->device, label); else { reply_with_error ("don't know how to set the label for '%s' filesystems", diff --git a/daemon/mount.c b/daemon/mount.c index af9283452..7e1199ff2 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,34 @@ do_mount_vfs (const char *options, const char *vfstype, return -1; } + CLEANUP_FREE char *options_plus = NULL; + const char *device = mountable->device; + if (mountable->type == 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; + } + } + } + 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 +187,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 3e5b6e3c4..386bbcd6d 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 a075bdc3d..d81af9398 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