Richard W.M. Jones wrote:
This isn't very well tested at the moment because my main
testing
machine is broken. So just for comment, only to be applied with
caution.
This patch has two new commands which make it much easier to look up
filesystems by their UUID or label. They are:
list-devices-by-uuid
list-devices-by-label
Each returns a hashtable (or whatever the equivalent structure is in
your favourite programming language).
The keys of the hash are UUID or label.
The values are the filesystem device, eg. /dev/sda1 or /dev/VG/LV
Looks fine on principle.
(haven't tried it)
From: Richard W.M. Jones <rjones(a)redhat.com>
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
...
+ $(srcdir)/../.gnulib/lib/hash.h \
+ $(srcdir)/../.gnulib/lib/hash.c
Don't include these here.
This can be handled cleanly by running gnulib-tool separately
for (and integrating it into) this subdir. I'm writing details
in a separate message.
-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 <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#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<X><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;
+ }
+
There's enough repetition of "/dev/mapper" here that I'd factor it out:
const char *dev_mapper = "/dev/mapper";
+ dir = opendir ("/dev/mapper");
+ if (!dir) {
+ reply_with_perror ("/dev/mapper");
+ goto error;
+ }
+
If you care about detecting/reporting readdir failure,
you can set errno=0 before each call, and then test afterwards.
errno != 0 means there was an error, e.g., EIO.
+ while ((d = readdir (dir)) != NULL) {
+ snprintf (path, sizeof path, "/dev/mapper/%s", d->d_name);
+ if (stat (path, &statbuf) == -1) {
+ perror (path);