diff options
author | Richard W.M. Jones <rjones@redhat.com> | 2012-05-02 16:14:41 +0100 |
---|---|---|
committer | Richard W.M. Jones <rjones@redhat.com> | 2012-05-02 16:33:23 +0100 |
commit | 620ad8eb1a5df298c5701a7b438b12f9627f06ab (patch) | |
tree | e8cf3f33bcf6dce8918dfb6040e9c3735bd97ca0 | |
parent | 79bf966ceaa3508698f19bdc951ffc51693eb499 (diff) | |
download | libguestfs-620ad8eb1a5df298c5701a7b438b12f9627f06ab.tar.gz libguestfs-620ad8eb1a5df298c5701a7b438b12f9627f06ab.tar.xz libguestfs-620ad8eb1a5df298c5701a7b438b12f9627f06ab.zip |
fish: glob command now expands /dev/ patterns (RHBZ#635971).
For example:
><fs> glob echo /dev/*
/dev/vda
/dev/vda1
/dev/vda2
/dev/vda3
><fs> glob echo /dev/v*/*
/dev/vg_f16x64/lv_root
/dev/vg_f16x64/lv_swap
-rw-r--r-- | fish/glob.c | 143 | ||||
-rw-r--r-- | generator/generator_actions.ml | 6 |
2 files changed, 146 insertions, 3 deletions
diff --git a/fish/glob.c b/fish/glob.c index 88e8927e..075f69a0 100644 --- a/fish/glob.c +++ b/fish/glob.c @@ -22,6 +22,8 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <errno.h> +#include <fnmatch.h> #include <libintl.h> #include "fish.h" @@ -31,6 +33,9 @@ */ static char **expand_pathname (guestfs_h *g, const char *path); +static char **expand_devicename (guestfs_h *g, const char *device); +static int add_strings_matching (char **pp, const char *glob, char ***ret, size_t *size_r); +static int add_string (const char *str, char ***ret, size_t *size_r); static char **single_element_list (const char *element); static void glob_issue (char *cmd, size_t argc, char ***globs, size_t *posn, size_t *count, int *r); @@ -70,8 +75,18 @@ run_glob (const char *cmd, size_t argc, char *argv[]) for (i = 1; i < argc; ++i) { char **pp; - /* Only if it begins with '/' can it possibly be a globbable path. */ - if (argv[i][0] == '/') { + /* If it begins with "/dev/" then treat it as a globbable device + * name. + */ + if (STRPREFIX (argv[i], "/dev/")) { + pp = expand_devicename (g, argv[i]); + if (pp == NULL) { + r = -1; + goto error; + } + } + /* If it begins with "/" it might be a globbable pathname. */ + else if (argv[i][0] == '/') { pp = expand_pathname (g, argv[i]); if (pp == NULL) { r = -1; @@ -123,6 +138,130 @@ expand_pathname (guestfs_h *g, const char *path) return single_element_list (path); } +/* Glob-expand device patterns, such as "/dev/sd*" (RHBZ#635971). + * + * There is no 'guestfs_glob_expand_device' function because the + * equivalent can be implemented using functions like + * 'guestfs_list_devices'. + * + * It's not immediately clear what it means to expand a pattern like + * "/dev/sd*". Should that include device name translation? Should + * the result include partitions as well as devices? + * + * Should "/dev/" + "*" return every possible device and filesystem? + * How about VGs? LVs? + * + * To solve this what we do is build up a list of every device, + * partition, etc., then glob against that list. + * + * Notes for future work (XXX): + * - This doesn't handle device name translation. It wouldn't be + * too hard to add. + * - Could have an API function for returning all device-like things. + */ +static char ** +expand_devicename (guestfs_h *g, const char *device) +{ + char **pp = NULL; + char **ret = NULL; + size_t size = 0; + + pp = guestfs_list_devices (g); + if (pp == NULL) goto error; + if (add_strings_matching (pp, device, &ret, &size) == -1) goto error; + free_strings (pp); + + pp = guestfs_list_partitions (g); + if (pp == NULL) goto error; + if (add_strings_matching (pp, device, &ret, &size) == -1) goto error; + free_strings (pp); + + pp = guestfs_list_md_devices (g); + if (pp == NULL) goto error; + if (add_strings_matching (pp, device, &ret, &size) == -1) goto error; + free_strings (pp); + + if (feature_available (g, "lvm2")) { + pp = guestfs_lvs (g); + if (pp == NULL) goto error; + if (add_strings_matching (pp, device, &ret, &size) == -1) goto error; + free_strings (pp); + pp = NULL; + } + + /* None matched? Add the original glob pattern. */ + if (ret == NULL) + ret = single_element_list (device); + return ret; + + error: + if (pp) + free_strings (pp); + if (ret) + free_strings (ret); + + return NULL; +} + +/* Using fnmatch, find strings in the list 'pp' which match pattern + * 'glob'. Add strings which match to the 'ret' array. '*size_r' is + * the current size of the 'ret' array, which is updated with the new + * size. + */ +static int +add_strings_matching (char **pp, const char *glob, + char ***ret, size_t *size_r) +{ + size_t i; + int r; + + for (i = 0; pp[i] != NULL; ++i) { + errno = 0; + r = fnmatch (glob, pp[i], FNM_PATHNAME); + if (r == 0) { /* matches - add it */ + if (add_string (pp[i], ret, size_r) == -1) + return -1; + } + else if (r != FNM_NOMATCH) { /* error */ + /* I checked the glibc impl and it returns random negative + * numbers for errors. It doesn't always set errno. Do our + * best here to record the error state. + */ + fprintf (stderr, "glob: fnmatch: error (r = %d, errno = %d)\n", + r, errno); + return -1; + } + } + + return 0; +} + +static int +add_string (const char *str, char ***ret, size_t *size_r) +{ + char **new_ret = *ret; + size_t size = *size_r; + + new_ret = realloc (new_ret, (size + 2) * (sizeof (char *))); + if (!new_ret) { + perror ("realloc"); + return -1; + } + *ret = new_ret; + + new_ret[size] = strdup (str); + if (new_ret[size] == NULL) { + perror ("strdup"); + return -1; + } + + size++; + new_ret[size] = NULL; + *size_r = size; + + return 0; +} + /* Return a single element list containing 'element'. */ static char ** single_element_list (const char *element) diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml index ace46cb1..6948dab3 100644 --- a/generator/generator_actions.ml +++ b/generator/generator_actions.ml @@ -3700,7 +3700,11 @@ If no paths match, then this returns an empty list It is just a wrapper around the C L<glob(3)> function with flags C<GLOB_MARK|GLOB_BRACE>. -See that manual page for more details."); +See that manual page for more details. + +Notice that there is no equivalent command for expanding a device +name (eg. C</dev/sd*>). Use C<guestfs_list_devices>, +C<guestfs_list_partitions> etc functions instead."); ("scrub_device", (RErr, [Device "device"], []), 114, [Optional "scrub"], [InitNone, Always, TestRun ( (* use /dev/sdc because it's smaller *) |