- halt's -w depends on FEATURE_WTMP (Vladimir Dronnikov)
[oweals/busybox.git] / util-linux / volume_id / get_devname.c
index 75060b0d87266004be1eb14324c771d32d9e89c3..f9a2c905f79d07bd8e85e73a0f53a7e8a6933e6f 100644 (file)
 
 #include "volume_id_internal.h"
 
-#define BLKGETSIZE64 _IOR(0x12,114,size_t)
-
-#define PROC_PARTITIONS "/proc/partitions"
-#define PROC_CDROMS    "/proc/sys/dev/cdrom/info"
-#define DEVLABELDIR    "/dev"
-#define SYS_BLOCK      "/sys/block"
+//#define BLKGETSIZE64 _IOR(0x12,114,size_t)
 
 static struct uuidCache_s {
        struct uuidCache_s *next;
-       char uuid[16];
+//     int major, minor;
        char *device;
        char *label;
-       int major, minor;
+       char *uc_uuid; /* prefix makes it easier to grep for */
 } *uuidCache;
 
-/* for now, only ext2, ext3 and xfs are supported */
+/* Returns !0 on error.
+ * Otherwise, returns malloc'ed strings for label and uuid
+ * (and they can't be NULL, although they can be "") */
+#if !ENABLE_FEATURE_VOLUMEID_ISO9660
+#define get_label_uuid(device, label, uuid, iso_only) \
+       get_label_uuid(device, label, uuid)
+#endif
 static int
 get_label_uuid(const char *device, char **label, char **uuid, int iso_only)
 {
@@ -34,15 +35,18 @@ get_label_uuid(const char *device, char **label, char **uuid, int iso_only)
        struct volume_id *vid;
 
        vid = volume_id_open_node(device);
+       if (!vid)
+               return rv;
 
-       if (ioctl(vid->fd, BLKGETSIZE64, &size) != 0) {
+       if (ioctl(vid->fd, BLKGETSIZE64, &size) != 0)
                size = 0;
-       }
 
 #if ENABLE_FEATURE_VOLUMEID_ISO9660
-       if (iso_only ?
-           volume_id_probe_iso9660(vid, 0) != 0 :
-           volume_id_probe_all(vid, 0, size) != 0) {
+       if ((iso_only ?
+            volume_id_probe_iso9660(vid, 0) :
+            volume_id_probe_all(vid, 0, size)
+           ) != 0
+       ) {
                goto ret;
        }
 #else
@@ -51,10 +55,10 @@ get_label_uuid(const char *device, char **label, char **uuid, int iso_only)
        }
 #endif
 
-       if (vid->label[0] != '\0') {
+       if (vid->label[0] != '\0' || vid->uuid[0] != '\0') {
                *label = xstrndup(vid->label, sizeof(vid->label));
                *uuid  = xstrndup(vid->uuid, sizeof(vid->uuid));
-               printf("Found label %s on %s (uuid:%s)\n", *label, device, *uuid);
+               dbg("found label '%s', uuid '%s' on %s", *label, *uuid, device);
                rv = 0;
        }
  ret:
@@ -62,11 +66,12 @@ get_label_uuid(const char *device, char **label, char **uuid, int iso_only)
        return rv;
 }
 
+/* NB: we take ownership of (malloc'ed) label and uuid */
 static void
-uuidcache_addentry(char * device, int major, int minor, char *label, char *uuid)
+uuidcache_addentry(char *device, /*int major, int minor,*/ char *label, char *uuid)
 {
        struct uuidCache_s *last;
-    
+
        if (!uuidCache) {
                last = uuidCache = xzalloc(sizeof(*uuidCache));
        } else {
@@ -76,55 +81,79 @@ uuidcache_addentry(char * device, int major, int minor, char *label, char *uuid)
                last = last->next;
        }
        /*last->next = NULL; - xzalloc did it*/
-       last->label = label;
+//     last->major = major;
+//     last->minor = minor;
        last->device = device;
-       last->major = major;
-       last->minor = minor;
-       memcpy(last->uuid, uuid, sizeof(last->uuid));
+       last->label = label;
+       last->uc_uuid = uuid;
 }
 
+/* If get_label_uuid() on device_name returns success,
+ * add a cache entry for this device.
+ * If device node does not exist, it will be temporarily created. */
+#if !ENABLE_FEATURE_VOLUMEID_ISO9660
+#define uuidcache_check_device(device_name, ma, mi, iso_only) \
+       uuidcache_check_device(device_name, ma, mi)
+#endif
 static void
 uuidcache_check_device(const char *device_name, int ma, int mi, int iso_only)
 {
-       char device[110];
-       char *uuid = NULL, *label = NULL;
+       char *device, *last_slash;
+       char *uuid, *label;
        char *ptr;
-       char *deviceDir = NULL;
-       int mustRemove = 0;
-       int mustRemoveDir = 0;
-       int i;
+       int must_remove = 0;
+       int added = 0;
+
+       last_slash = NULL;
+       device = xasprintf("/dev/%s", device_name);
+       if (access(device, F_OK) != 0) {
+               /* device does not exist, temporarily create */
+               int slash_cnt = 0;
+
+               if ((ma | mi) < 0)
+                       goto ret; /* we don't know major:minor! */
 
-       sprintf(device, "%s/%s", DEVLABELDIR, device_name);
-       if (access(device, F_OK)) {
                ptr = device;
-               i = 0;
                while (*ptr)
                        if (*ptr++ == '/')
-                               i++;
-               if (i > 2) {
-                       deviceDir = alloca(strlen(device) + 1);
-                       strcpy(deviceDir, device);
-                       ptr = deviceDir + (strlen(device) - 1);
-                       while (*ptr != '/')
-                               *ptr-- = '\0';
-                       if (mkdir(deviceDir, 0644)) {
-                               printf("mkdir: cannot create directory %s: %d\n", deviceDir, errno);
+                               slash_cnt++;
+               if (slash_cnt > 2) {
+// BUG: handles only slash_cnt == 3 case
+                       last_slash = strrchr(device, '/');
+                       *last_slash = '\0';
+                       if (mkdir(device, 0644)) {
+                               bb_perror_msg("can't create directory %s", device);
+                               *last_slash = '/';
+                               last_slash = NULL; /* prevents rmdir */
                        } else {
-                               mustRemoveDir = 1;
+                               *last_slash = '/';
                        }
                }
-
                mknod(device, S_IFBLK | 0600, makedev(ma, mi));
-               mustRemove = 1;
+               must_remove = 1;
+       }
+
+       uuid = NULL;
+       label = NULL;
+       if (get_label_uuid(device, &label, &uuid, iso_only) == 0) {
+               uuidcache_addentry(device, /*ma, mi,*/ label, uuid);
+               /* "device" is owned by cache now, don't free */
+               added = 1;
        }
-       if (!get_label_uuid(device, &label, &uuid, iso_only))
-               uuidcache_addentry(strdup(device), ma, mi, 
-                                  label, uuid);
 
-       if (mustRemove) unlink(device);
-       if (mustRemoveDir) rmdir(deviceDir);
+       if (must_remove)
+               unlink(device);
+       if (last_slash) {
+               *last_slash = '\0';
+               rmdir(device);
+       }
+ ret:
+       if (!added)
+               free(device);
 }
 
+/* Run uuidcache_check_device() for every device mentioned
+ * in /proc/partitions */
 static void
 uuidcache_init_partitions(void)
 {
@@ -136,7 +165,7 @@ uuidcache_init_partitions(void)
        int handleOnFirst;
        char *chptr;
 
-       procpt = xfopen(PROC_PARTITIONS, "r");
+       procpt = xfopen_for_read("/proc/partitions");
 /*
 # cat /proc/partitions
 major minor  #blocks  name
@@ -157,7 +186,7 @@ major minor  #blocks  name
                           diet's sscanf is quite limited */
                        chptr = line;
                        if (*chptr != ' ') continue;
-                       chptr++;
+                       chptr = skip_whitespace(chptr);
 
                        ma = bb_strtou(chptr, &chptr, 0);
                        if (ma < 0) continue;
@@ -177,6 +206,8 @@ major minor  #blocks  name
 
                        *strchrnul(chptr, '\n') = '\0';
                        /* now chptr => device name */
+                       dbg("/proc/partitions: maj:%d min:%d sz:%llu name:'%s'",
+                                               ma, mi, sz, chptr);
                        if (!chptr[0])
                                continue;
 
@@ -195,61 +226,63 @@ major minor  #blocks  name
        fclose(procpt);
 }
 
-static int
+static void
 dev_get_major_minor(char *device_name, int *major, int *minor)
 {
-       char * dev_path;
-       int fd;
-       char dev[7];
-       char *major_ptr, *minor_ptr;
-
-       dev_path = alloca(strlen(SYS_BLOCK) + strlen(device_name) + 6);
-       sprintf(dev_path, "%s/%s/dev", SYS_BLOCK, device_name);
-
-       fd = open(dev_path, O_RDONLY);
-       if (fd < 0) return 1;
-       full_read(fd, dev, sizeof(dev));
-       close(fd);
-
-       major_ptr = dev;
-       minor_ptr = strchr(dev, ':');
-       if (!minor_ptr) return 1;
-       *minor_ptr++ = '\0';
+       char dev[16];
+       char *dev_path;
+       char *colon;
+       int sz;
+
+       dev_path = xasprintf("/sys/block/%s/dev", device_name);
+       sz = open_read_close(dev_path, dev, sizeof(dev) - 1);
+       if (sz < 0)
+               goto ret;
+       dev[sz] = '\0';
 
-       *major = strtol(major_ptr, NULL, 10);
-       *minor = strtol(minor_ptr, NULL, 10);
+       colon = strchr(dev, ':');
+       if (!colon)
+               goto ret;
+       *major = bb_strtou(dev, NULL, 10);
+       *minor = bb_strtou(colon + 1, NULL, 10);
 
-       return 0;
+ ret:
+       free(dev_path);
+       return;
 }
 
 static void
 uuidcache_init_cdroms(void)
 {
+#define PROC_CDROMS "/proc/sys/dev/cdrom/info"
        char line[100];
        int ma, mi;
        FILE *proccd;
 
-       proccd = fopen(PROC_CDROMS, "r");
+       proccd = fopen_for_read(PROC_CDROMS);
        if (!proccd) {
-               static smallint warn = 0;
-               if (!warn) {
-                       warn = 1;
-                       bb_error_msg("mount: could not open %s, so UUID and LABEL "
-                               "conversion cannot be done for CD-Roms.",
-                               PROC_CDROMS);
-               }
+//             static smallint warn = 0;
+//             if (!warn) {
+//                     warn = 1;
+//                     bb_error_msg("can't open %s, UUID and LABEL "
+//                             "conversion cannot be done for CD-Roms",
+//                             PROC_CDROMS);
+//             }
                return;
        }
 
        while (fgets(line, sizeof(line), proccd)) {
-               const char *drive_name_string = "drive name:\t\t";
-               if (!strncmp(line, drive_name_string, strlen(drive_name_string))) {
+               static const char drive_name_string[] ALIGN1 = "drive name:";
+
+               if (strncmp(line, drive_name_string, sizeof(drive_name_string) - 1) == 0) {
                        char *device_name;
-                       device_name = strtok(line + strlen(drive_name_string), "\t\n");
-                       while (device_name) {
+
+                       device_name = strtok(skip_whitespace(line + sizeof(drive_name_string) - 1), " \t\n");
+                       while (device_name && device_name[0]) {
+                               ma = mi = -1;
                                dev_get_major_minor(device_name, &ma, &mi);
                                uuidcache_check_device(device_name, ma, mi, 1);
-                               device_name = strtok(NULL, "\t\n");
+                               device_name = strtok(NULL, " \t\n");
                        }
                        break;
                }
@@ -273,24 +306,24 @@ uuidcache_init(void)
 
 #ifdef UNUSED
 static char *
-get_spec_by_x(int n, const char *t, int * majorPtr, int * minorPtr)
+get_spec_by_x(int n, const char *t, int *majorPtr, int *minorPtr)
 {
        struct uuidCache_s *uc;
 
        uuidcache_init();
        uc = uuidCache;
 
-       while(uc) {
+       while (uc) {
                switch (n) {
                case UUID:
-                       if (!memcmp(t, uc->uuid, sizeof(uc->uuid))) {
+                       if (strcmp(t, uc->uc_uuid) == 0) {
                                *majorPtr = uc->major;
                                *minorPtr = uc->minor;
                                return uc->device;
                        }
                        break;
                case VOL:
-                       if (!strcmp(t, uc->label)) {
+                       if (strcmp(t, uc->label) == 0) {
                                *majorPtr = uc->major;
                                *minorPtr = uc->minor;
                                return uc->device;
@@ -307,26 +340,27 @@ fromhex(char c)
 {
        if (isdigit(c))
                return (c - '0');
-       if (islower(c))
-               return (c - 'a' + 10);
-       return (c - 'A' + 10);
+       return ((c|0x20) - 'a' + 10);
 }
 
 static char *
-get_spec_by_uuid(const char *s, int * major, int * minor)
+get_spec_by_uuid(const char *s, int *major, int *minor)
 {
        unsigned char uuid[16];
        int i;
 
-       if (strlen(s) != 36 ||
-           s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-')
+       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;
+       }
+       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, (char *)uuid, major, minor);
 
@@ -350,13 +384,7 @@ static int display_uuid_cache(void)
 
        u = uuidCache;
        while (u) {
-               printf("%s %s ", u->device, u->label);
-               for (i = 0; i < sizeof(u->uuid); i++) {
-                       if (i == 4 || i == 6 || i == 8 || i == 10)
-                               printf("-");
-                       printf("%x", u->uuid[i] & 0xff);
-               }
-               printf("\n");
+               printf("%s %s %s\n", u->device, u->label, u->uc_uuid);
                u = u->next;
        }
 
@@ -375,7 +403,8 @@ char *get_devname_from_label(const char *spec)
        uuidcache_init();
        uc = uuidCache;
        while (uc) {
-               if (uc->label && !strncmp(spec, uc->label, spec_len)) {
+// FIXME: empty label ("LABEL=") matches anything??!
+               if (uc->label[0] && strncmp(spec, uc->label, spec_len) == 0) {
                        return xstrdup(uc->device);
                }
                uc = uc->next;
@@ -390,7 +419,8 @@ char *get_devname_from_uuid(const char *spec)
        uuidcache_init();
        uc = uuidCache;
        while (uc) {
-               if (!memcmp(spec, uc->uuid, sizeof(uc->uuid))) {
+               /* case of hex numbers doesn't matter */
+               if (strcasecmp(spec, uc->uc_uuid) == 0) {
                        return xstrdup(uc->device);
                }
                uc = uc->next;