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

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



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 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);


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