summaryrefslogtreecommitdiffstats
path: root/fish
diff options
context:
space:
mode:
authorRichard W.M. Jones <rjones@redhat.com>2012-05-02 16:14:41 +0100
committerRichard W.M. Jones <rjones@redhat.com>2012-05-02 16:33:23 +0100
commit620ad8eb1a5df298c5701a7b438b12f9627f06ab (patch)
treee8cf3f33bcf6dce8918dfb6040e9c3735bd97ca0 /fish
parent79bf966ceaa3508698f19bdc951ffc51693eb499 (diff)
downloadlibguestfs-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
Diffstat (limited to 'fish')
-rw-r--r--fish/glob.c143
1 files changed, 141 insertions, 2 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)