Logo Search packages:      
Sourcecode: yaboot version File versions  Download package

get_device_by_label.c

/*
 * get_device_by_label.h
 *
 * Copyright 1999 by Andries Brouwer
 * Copyright 1999, 2000 by Theodore Ts'o
 *
 * This file may be redistributed under the terms of the GNU Public
 * License.
 *
 * Taken from aeb's mount, 990619
 * Updated from aeb's mount, 20000725
 * Added call to ext2fs_find_block_device, so that we can find devices
 *    even if devfs (ugh) is compiled in, but not mounted, since
 *    this messes up /proc/partitions, by TYT.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_SYS_MKDEV_H
#include <sys/mkdev.h>
#endif
#ifdef HAVE_SYS_SYSMACROS_H
#include <sys/sysmacros.h>
#endif
#include <dirent.h>
#include "nls-enable.h"
#include "fsck.h"
#include "get_device_by_label.h"

/* function prototype from libext2 */
extern char *ext2fs_find_block_device(dev_t device);

#define PROC_PARTITIONS "/proc/partitions"
#define DEVLABELDIR     "/dev"
#define VG_DIR          "/proc/lvm/VGs"

#define EXT2_SUPER_MAGIC    0xEF53
struct ext2_super_block {
        unsigned char   s_dummy1[56];
        unsigned char   s_magic[2];
        unsigned char   s_dummy2[46];
        unsigned char   s_uuid[16];
        unsigned char   s_volume_name[16];
};
#define ext2magic(s)    ((unsigned int) s.s_magic[0] + (((unsigned int) s.s_magic[1]) << 8))

#define XFS_SUPER_MAGIC "XFSB"
struct xfs_super_block {
      unsigned char     s_magic[4];
      unsigned char     s_dummy[28];
      unsigned char     s_uuid[16];
      unsigned char     s_dummy2[60];
      unsigned char     s_fname[12];
};

static struct uuidCache_s {
      struct uuidCache_s *next;
      char uuid[16];
      char *label;
      char *device;
} *uuidCache = NULL;

char *string_copy(const char *s)
{
      char  *ret;

      ret = malloc(strlen(s)+1);
      if (ret)
            strcpy(ret, s);
      return ret;
}

/* for now, only ext2 and xfs are supported */
static int
get_label_uuid(const char *device, char **label, char *uuid) {

      /* start with ext2 and xfs tests, taken from mount_guess_fstype */
      /* should merge these later */
      int fd;
      size_t label_size;
      unsigned char *sb_uuid = 0, *sb_label = 0;
      struct ext2_super_block e2sb;
      struct xfs_super_block xfsb;

      fd = open(device, O_RDONLY);
      if (fd < 0)
            return 1;

      if (lseek(fd, 1024, SEEK_SET) == 1024
          && read(fd, (char *) &e2sb, sizeof(e2sb)) == sizeof(e2sb)
          && (ext2magic(e2sb) == EXT2_SUPER_MAGIC)) {
            sb_uuid = e2sb.s_uuid;
            sb_label = e2sb.s_volume_name;
            label_size = sizeof(e2sb.s_volume_name);
      } else if (lseek(fd, 0, SEEK_SET) == 0
          && read(fd, (char *) &xfsb, sizeof(xfsb)) == sizeof(xfsb)
          && strncmp((char *) &xfsb.s_magic, XFS_SUPER_MAGIC, 4) == 0) {
            sb_uuid = xfsb.s_uuid;
            sb_label = xfsb.s_fname;
            label_size = sizeof(xfsb.s_fname);
      } else {
            close(fd);
            return 1;
      }

      close(fd);
      if (sb_uuid)
            memcpy(uuid, sb_uuid, sizeof(e2sb.s_uuid));
      if (sb_label) {
            if ((*label = calloc(label_size + 1, 1)) != NULL)
                  memcpy(*label, sb_label, label_size);
      }
      return 0;
}

static void
uuidcache_addentry(char *device, char *label, char *uuid) {
      struct uuidCache_s *last;

      if (!uuidCache) {
            last = uuidCache = malloc(sizeof(*uuidCache));
      } else {
            for (last = uuidCache; last->next; last = last->next) ;
            last->next = malloc(sizeof(*uuidCache));
            last = last->next;
      }
      last->next = NULL;
      last->device = device;
      last->label = label;
      memcpy(last->uuid, uuid, sizeof(last->uuid));
}

/*
 * This function initializes the UUID cache with devices from the LVM
 * proc hierarchy.  We currently depend on the names of the LVM
 * hierarchy giving us the device structure in /dev.  (XXX is this a
 * safe thing to do?)
 */
#ifdef VG_DIR
static void init_lvm(void)
{
      DIR         *vg_dir, *lv_list;
      char        *vdirname, *lvm_device;
      char        uuid[16], *label, *vname, *lname;
      struct dirent     *vg_iter, *lv_iter;
      
      if ((vg_dir = opendir(VG_DIR)) == NULL)
            return;

      while ((vg_iter = readdir(vg_dir)) != 0) {
            vname = vg_iter->d_name;
            if (!strcmp(vname, ".") || !strcmp(vname, ".."))
                  continue;
            vdirname = malloc(strlen(VG_DIR)+strlen(vname)+8);
            if (!vdirname) {
                  closedir(vg_dir);
                  return;
            }
            sprintf(vdirname, "%s/%s/LVs", VG_DIR, vname);

            lv_list = opendir(vdirname);
            free(vdirname);
            if (lv_list != NULL)
                  return;

            while ((lv_iter = readdir(lv_list)) != 0) {
                  lname = lv_iter->d_name;
                  if (!strcmp(lname, ".") || !strcmp(lname, ".."))
                        continue;

                  lvm_device = malloc(strlen(DEVLABELDIR) +
                                  strlen(vname)+
                                  strlen(lname)+8);
                  if (!lvm_device) {
                        closedir(lv_list);
                        closedir(vg_dir);
                        return;
                  }
                  sprintf(lvm_device, "%s/%s/%s", DEVLABELDIR,
                        vname, lname);
                  if (!get_label_uuid(lvm_device, &label, uuid)) {
                        uuidcache_addentry(string_copy(lvm_device),
                                       label, uuid);
                  } else
                        free(lvm_device);
            }
            closedir(lv_list);
      }
      closedir( vg_dir );
}
#endif

static void
uuidcache_init(void) {
      char line[100];
      char *s;
      int ma, mi, sz;
      static char ptname[100];
      FILE *procpt;
      char uuid[16], *label, *devname;
      char device[110];
      dev_t dev;
      struct stat statbuf;
      int firstPass;
      int handleOnFirst;

      if (uuidCache)
            return;

#ifdef VG_DIR
      init_lvm();
#endif
      
      procpt = fopen(PROC_PARTITIONS, "r");
      if (!procpt)
            return;

      for (firstPass = 1; firstPass >= 0; firstPass--) {
          fseek(procpt, 0, SEEK_SET);

          while (fgets(line, sizeof(line), procpt)) {
            if (sscanf (line, " %d %d %d %[^\n ]",
                      &ma, &mi, &sz, ptname) != 4)
                  continue;

            /* skip extended partitions (heuristic: size 1) */
            if (sz == 1)
                  continue;

            /* look only at md devices on first pass */
            handleOnFirst = !strncmp(ptname, "md", 2);
            if (firstPass != handleOnFirst)
                  continue;

            /* skip entire disk (minor 0, 64, ... on ide;
               0, 16, ... on sd) */
            /* heuristic: partition name ends in a digit */

            for(s = ptname; *s; s++);
            if (isdigit(s[-1])) {
                  /*
                   * We first look in /dev for the device, but
                   * if we don't find it, or if the stat
                   * information doesn't check out, we use
                   * ext2fs_find_block_device to find it.
                   */
                  sprintf(device, "%s/%s", DEVLABELDIR, ptname);
                  dev = makedev(ma, mi);
                  if ((stat(device, &statbuf) < 0) ||
                      (statbuf.st_rdev != dev)) {
                        devname = ext2fs_find_block_device(dev);
                  } else
                        devname = string_copy(device);
                  if (!devname)
                        continue;
                  if (!get_label_uuid(devname, &label, uuid))
                        uuidcache_addentry(devname, label, uuid);
                  else
                        free(devname);
            }
          }
      }

      fclose(procpt);
}

#define UUID   1
#define VOL    2

static char *
get_spec_by_x(int n, const char *t) {
      struct uuidCache_s *uc;

      uuidcache_init();
      uc = uuidCache;

      if (t == NULL)
            return NULL;

      while(uc) {
            switch (n) {
            case UUID:
                  if (!memcmp(t, uc->uuid, sizeof(uc->uuid)))
                        return string_copy(uc->device);
                  break;
            case VOL:
                  if (!strcmp(t, uc->label))
                        return string_copy(uc->device);
                  break;
            }
            uc = uc->next;
      }
      return NULL;
}

static char fromhex(char c)
{
      if (isdigit(c))
            return (c - '0');
      else if (islower(c))
            return (c - 'a' + 10);
      else
            return (c - 'A' + 10);
}

char *
get_spec_by_uuid(const char *s)
{
      char uuid[16];
      int i;

      if (strlen(s) != 36 ||
          s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-')
            goto bad_uuid;
      for (i=0; i<16; i++) {
          if (*s == '-') s++;
          if (!isxdigit(s[0]) || !isxdigit(s[1]))
                goto bad_uuid;
          uuid[i] = ((fromhex(s[0])<<4) | fromhex(s[1]));
          s += 2;
      }
      return get_spec_by_x(UUID, uuid);

 bad_uuid:
      fprintf(stderr, _("WARNING: %s: bad UUID"), s);
      return NULL;
}

char *
get_spec_by_volume_label(const char *s) {
      return get_spec_by_x(VOL, s);
}

const char *
get_volume_label_by_spec(const char *spec) {
        struct uuidCache_s *uc;

        uuidcache_init();
        uc = uuidCache;

      while(uc) {
            if (!strcmp(spec, uc->device))
                  return uc->label;
            uc = uc->next;
      }
      return NULL;
}

/*
 * Interpret the device name if necessary.
 * Frees the pointer passed to it if we return a different device string.
 */
char *interpret_spec(char *spec)
{
      char *dev = NULL;

      if (!spec)
            return NULL;

      if (!strncmp(spec, "UUID=", 5))
            dev = get_spec_by_uuid(spec+5);
      else if (!strncmp(spec, "LABEL=", 6))
            dev = get_spec_by_volume_label(spec+6);
      else
            dev = string_copy(spec);
      return dev;
}

Generated by  Doxygen 1.6.0   Back to index