[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[Libguestfs] [PATCH] New commands to list devices by UUID and label



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

Rich.

-- 
Richard Jones, Emerging Technologies, Red Hat  http://et.redhat.com/~rjones
Read my programming blog: http://rwmj.wordpress.com
Fedora now supports 75 OCaml packages (the OPEN alternative to F#)
http://cocan.org/getting_started_with_ocaml_on_red_hat_and_fedora
>From d045b5f8d0389042db48c1078e417a29cb0029ad Mon Sep 17 00:00:00 2001
From: Richard W.M. Jones <rjones 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
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 <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;
+  }
+
+  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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]