mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
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
This commit is contained in:
143
fish/glob.c
143
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)
|
||||
|
||||
Reference in New Issue
Block a user