>From d045b5f8d0389042db48c1078e417a29cb0029ad Mon Sep 17 00:00:00 2001 From: Richard W.M. Jones Date: Mon, 3 Aug 2009 11:44:51 +0100 Subject: [PATCH] New commands: list-disks-by-uuid and list-disks-by-label These commands returned a hash from UUID/label -> device, making it simpler to locate partitions which have UUIDs or labels. --- daemon/Makefile.am | 7 +- daemon/deviceby.c | 295 ++++++++++++++++++++++++++++++++++++++++++++++++++++ po/POTFILES.in | 1 + src/MAX_PROC_NR | 2 +- src/generator.ml | 16 +++ 5 files changed, 318 insertions(+), 3 deletions(-) create mode 100644 daemon/deviceby.c diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 024c097..0fec611 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -28,6 +28,7 @@ guestfsd_SOURCES = \ cpmv.c \ daemon.h \ debug.c \ + deviceby.c \ devsparts.c \ df.c \ dir.c \ @@ -74,6 +75,8 @@ guestfsd_SOURCES = \ zero.c \ zerofree.c \ $(top_builddir)/../src/guestfs_protocol.h \ - $(top_builddir)/../src/guestfs_protocol.c + $(top_builddir)/../src/guestfs_protocol.c \ + $(srcdir)/../.gnulib/lib/hash.h \ + $(srcdir)/../.gnulib/lib/hash.c -guestfsd_CFLAGS = -Wall +guestfsd_CFLAGS = -Wall -I$(srcdir)/../.gnulib/lib diff --git a/daemon/deviceby.c b/daemon/deviceby.c new file mode 100644 index 0000000..2006442 --- /dev/null +++ b/daemon/deviceby.c @@ -0,0 +1,295 @@ +/* libguestfs - the guestfsd daemon + * Copyright (C) 2009 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "daemon.h" +#include "actions.h" + +#include "hash.h" /* Gnulib hash table. */ + +/* Hash used in the first pass. */ +struct h1 { + dev_t dev; /* device */ + char *dname; /* d_dname entry (ie. UUID or label) */ +}; + +static size_t +hasher1 (const void *h1v, size_t table_size) +{ + const struct h1 *h1 = h1v; + + return (unsigned) h1->dev % table_size; +} + +static bool +comparator1 (const void *h11v, const void *h12v) +{ + const struct h1 *h11 = h11v; + const struct h1 *h12 = h12v; + + return h11->dev == h12->dev; +} + +static void +freer1 (void *h1v) +{ + struct h1 *h1 = h1v; + + free (h1->dname); + free (h1); +} + +/* Note that d->d_name can contain \x sequences which + * we must replace with a character (eg. "\x2f" -> "/") + * + * How escaping really works in udev is very unclear, so this + * code is at best a guess. + */ +static int +getxdigit (char c) +{ + switch (c) { + case '0'...'9': return c - '0'; + case 'a'...'f': return c - 'a' + 10; + case 'A'...'F': return c - 'A' + 10; + default: abort (); + } +} + +static char * +unescape (const char *dname) +{ + int i, j, len; + char *r; + + len = strlen (dname); + r = malloc (len + 1); + if (r == NULL) { + reply_with_perror ("malloc"); + return NULL; + } + + for (i = j = 0; i < len; ++i, ++j) { + if (i < len-4 && /* \xAB */ + dname[i] == '\\' && dname[i+1] == 'x' && + isxdigit (dname[i+2]) && isxdigit (dname[i+3])) { + r[j] = getxdigit (dname[i+2]) * 16 + getxdigit (dname[i+3]); + i += 3; + } + else if (i < len-2 && dname[i] == '\\') { /* \\ etc */ + r[j] = dname[i+1]; + i++; + } + else + r[j] = dname[i]; + } + + r[j] = '\0'; + + return r; +} + +static char ** +list (const char *udevpath) +{ + DIR *dir = NULL; + struct dirent *d; + char path[PATH_MAX]; + struct stat statbuf; + Hash_table *hash1 = NULL; + char **ret = NULL; + int size = 0, alloc = 0; + + /* /dev/disk is populated by udev */ + udev_settle (); + + dir = opendir (udevpath); + if (!dir) { + /* If there are no labels/UUIDs then udev sometimes won't + * even create this directory. In this case let's return + * an empty hashtable. + */ + perror (udevpath); + if (add_string (&ret, &size, &alloc, NULL) == -1) + return NULL; + return ret; + } + + /* First pass, over the udev /dev/disk directory. */ + hash1 = hash_initialize (128, NULL, hasher1, comparator1, freer1); + if (hash1 == NULL) { + reply_with_perror ("hash_initialize"); + goto error; + } + + while ((d = readdir (dir)) != NULL) { + snprintf (path, sizeof path, "%s/%s", udevpath, d->d_name); + if (stat (path, &statbuf) == -1) { /* Stat the target of the link. */ + perror (path); + continue; + } + /* Ignore ".", "..", and any other non-block devices. */ + if (!S_ISBLK (statbuf.st_mode)) + continue; + + /* Now add a hash entry rdev -> d_name. We'll look up the + * device name in the second pass. + */ + struct h1 *h1 = malloc (sizeof *h1); + if (h1 == NULL) { + reply_with_perror ("malloc"); + goto error; + } + h1->dev = statbuf.st_rdev; + h1->dname = unescape (d->d_name); + if (!h1->dname) { + free (h1); + goto error; + } + if (hash_insert (hash1, h1) == NULL) { + reply_with_perror ("hash_insert"); + free (h1); + goto error; + } + } + + if (closedir (dir) == -1) { + reply_with_perror (udevpath); + dir = NULL; + goto error; + } + + /* In the second pass we look at all devices in /dev and /dev/mapper + * and try to find matching device major/minor numbers for them in + * the hash. + */ + dir = opendir ("/dev"); + if (!dir) { + reply_with_perror ("/dev"); + goto error; + } + + while ((d = readdir (dir)) != NULL) { + snprintf (path, sizeof path, "/dev/%s", d->d_name); + if (stat (path, &statbuf) == -1) { + perror (path); + continue; + } + + /* st_rdev is in the hash? If so, it's an entry in the result. */ + struct h1 h1, *r; + h1.dev = statbuf.st_rdev; + r = hash_lookup (hash1, &h1); + if (r) { + /* Key: UUID or label */ + if (add_string (&ret, &size, &alloc, r->dname) == -1) + goto error; + /* Value: /dev/... */ + if (add_string (&ret, &size, &alloc, path) == -1) + goto error; + } + } + + if (closedir (dir) == -1) { + reply_with_perror (udevpath); + free_strings (ret); + dir = NULL; + goto error; + } + + dir = opendir ("/dev/mapper"); + if (!dir) { + reply_with_perror ("/dev/mapper"); + goto error; + } + + while ((d = readdir (dir)) != NULL) { + snprintf (path, sizeof path, "/dev/mapper/%s", d->d_name); + if (stat (path, &statbuf) == -1) { + perror (path); + continue; + } + + /* st_rdev is in the hash? If so, it's an entry in the result. */ + struct h1 h1, *r; + h1.dev = statbuf.st_rdev; + r = hash_lookup (hash1, &h1); + if (r) { + /* Key: UUID or label */ + if (add_string (&ret, &size, &alloc, r->dname) == -1) + goto error; + /* Value needs to be rewritten from /dev/mapper/VG-LV to /dev/VG/LV. */ + char *p = strchr (d->d_name, '-'); + if (p) { + *p++ = '\0'; + snprintf (path, sizeof path, "/dev/%s/%s", d->d_name, p); + } + + if (add_string (&ret, &size, &alloc, path) == -1) + goto error; + } + } + + if (closedir (dir) == -1) { + reply_with_perror (udevpath); + free_strings (ret); + dir = NULL; + goto error; + } + dir = NULL; + + hash_free (hash1); + hash1 = NULL; + + /* Terminate the returned structure. */ + if (add_string (&ret, &size, &alloc, NULL) == -1) + goto error; + + return ret; /* caller frees */ + + error: + if (dir) + closedir (dir); + if (hash1) + hash_free (hash1); + + return NULL; +} + +char ** +do_list_devices_by_uuid (void) +{ + return list ("/dev/disk/by-uuid"); +} + +char ** +do_list_devices_by_label (void) +{ + return list ("/dev/disk/by-label"); +} diff --git a/po/POTFILES.in b/po/POTFILES.in index 2a57823..3853d4a 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -6,6 +6,7 @@ daemon/cmp.c daemon/command.c daemon/cpmv.c daemon/debug.c +daemon/deviceby.c daemon/devsparts.c daemon/df.c daemon/dir.c diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR index dc37bbd..bc3d544 100644 --- a/src/MAX_PROC_NR +++ b/src/MAX_PROC_NR @@ -1 +1 @@ -184 +186 diff --git a/src/generator.ml b/src/generator.ml index b6f6f42..f2d8cfe 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -3393,6 +3393,22 @@ This closes the inotify handle which was previously opened by inotify_init. It removes all watches, throws away any pending events, and deallocates all resources."); + ("list_devices_by_uuid", (RHashtable "disks", []), 185, [], + [], + "map of UUIDs to device names", + "\ +This call returns a hash table where the keys are the +UUIDs of all filesystems found, and the values are the +device, partition or LV name where the filesystem is located."); + + ("list_devices_by_label", (RHashtable "disks", []), 186, [], + [], + "map of labels to device names", + "\ +This call returns a hash table where the keys are the +labels of all filesystems found, and the values are the +device, partition or LV name where the filesystem is located."); + ] let all_functions = non_daemon_functions @ daemon_functions -- 1.6.2.5