Whamcloud - gitweb
ChangeLog, Makefile.in, get_device_by_label.c:
[tools/e2fsprogs.git] / misc / get_device_by_label.c
index cfb49f5..72378e7 100644 (file)
@@ -1,21 +1,36 @@
 /*
  * get_device_by_label.h
  *
- * Copyright 1999 by Andries Brouwer and Theodore Ts'o
+ * 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
+#include "nls-enable.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"
 
@@ -29,104 +44,156 @@ struct ext2_super_block {
 };
 #define ext2magic(s)    ((unsigned int) s.s_magic[0] + (((unsigned int) s.s_magic[1]) << 8))
 
-
-static FILE *procpt;
-
-static void
-procptclose(void) {
-    if (procpt)
-        fclose (procpt);
-    procpt = 0;
-}
-
-static int
-procptopen(void) {
-    return ((procpt = fopen(PROC_PARTITIONS, "r")) != NULL);
-}
-
-static char *
-procptnext(void) {
-   char line[100];
-   char *s;
-   int ma, mi, sz;
-   static char ptname[100];
-
-   while (fgets(line, sizeof(line), procpt)) {
-      if (sscanf (line, " %d %d %d %[^\n]\n", &ma, &mi, &sz, ptname) != 4)
-             continue;
-
-      /* skip extended partitions (heuristic: size 1) */
-      if (sz == 1)
-             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]))
-             return ptname;
-   }
-   return 0;
-}
-
-#define UUID   1
-#define VOL    2
+static struct uuidCache_s {
+       struct uuidCache_s *next;
+       char uuid[16];
+       char *label;
+       char *device;
+} *uuidCache = NULL;
 
 /* for now, only ext2 is supported */
 static int
-has_right_label(const char *device, int n, const void *label) {
+get_label_uuid(const char *device, char **label, char *uuid) {
 
        /* start with a test for ext2, taken from mount_guess_fstype */
+       /* should merge these later */
        int fd;
        struct ext2_super_block e2sb;
 
        fd = open(device, O_RDONLY);
        if (fd < 0)
-               return 0;
+               return 1;
 
        if (lseek(fd, 1024, SEEK_SET) != 1024
            || read(fd, (char *) &e2sb, sizeof(e2sb)) != sizeof(e2sb)
            || (ext2magic(e2sb) != EXT2_SUPER_MAGIC)) {
                close(fd);
-               return 0;
+               return 1;
        }
 
        close(fd);
 
        /* superblock is ext2 - now what is its label? */
-       if (n == UUID)
-               return (memcmp(e2sb.s_uuid, label, 16) == 0);
-       else
-               return (strncmp(e2sb.s_volume_name,
-                               (const char *) label, 16) == 0);
+       memcpy(uuid, e2sb.s_uuid, sizeof(e2sb.s_uuid));
+       *label = strdup(e2sb.s_volume_name);
+
+       return 0;
 }
 
-static char *
-get_spec_by_x(int n, const void *t) {
-       char *pt;
+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));
+}
+
+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;
+
+       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 = strdup(device);
+                       if (!devname)
+                               continue;
+                       if (!get_label_uuid(devname, &label, uuid))
+                               uuidcache_addentry(devname, label, uuid);
+                       else
+                               free(devname);
+               }
+           }
+       }
+
+       fclose(procpt);
+}
 
-       if(!procptopen())
-               return NULL;
-       while((pt = procptnext()) != NULL) {
-               /* Note: this is a heuristic only - there is no reason
-                  why these devices should live in /dev.
-                  Perhaps this directory should be specifiable by option.
-                  One might for example have /devlabel with links to /dev
-                  for the devices that may be accessed in this way.
-                  (This is useful, if the cdrom on /dev/hdc must not
-                  be accessed.)
-               */
-               sprintf(device, "%s/%s", DEVLABELDIR, pt);
-               if (has_right_label(device, n, t)) {
-                       procptclose();
-                       return strdup(device);
+#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;
+
+       while(uc) {
+               switch (n) {
+               case UUID:
+                       if (!memcmp(t, uc->uuid, sizeof(uc->uuid)))
+                               return strdup(uc->device);
+                       break;
+               case VOL:
+                       if (!strcmp(t, uc->label))
+                               return strdup(uc->device);
+                       break;
                }
+               uc = uc->next;
        }
-       procptclose();
        return NULL;
 }
 
-static unsigned char
+static u_char
 fromhex(char c) {
        if (isdigit(c))
                return (c - '0');
@@ -137,10 +204,9 @@ fromhex(char c) {
 }
 
 char *
-get_spec_by_uuid(const char *s0) {
-       unsigned char uuid[16];
+get_spec_by_uuid(const char *s) {
+       u_char uuid[16];
        int i;
-       const char *s = s0;
 
        if (strlen(s) != 36 ||
            s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-')
@@ -155,7 +221,7 @@ get_spec_by_uuid(const char *s0) {
        return get_spec_by_x(UUID, uuid);
 
  bad_uuid:
-       fprintf(stderr, "WARNING: %s: bad UUID", s0);
+       fprintf(stderr, _("WARNING: %s: bad UUID"), s);
        return NULL;
 }
 
@@ -163,3 +229,18 @@ 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;
+}