/* vi: set sw=4 ts=4: */ /* * Support functions for mounting devices by label/uuid * * Copyright (C) 2006 by Jason Schoon * Some portions cribbed from e2fsprogs, util-linux, dosfstools * * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. */ #include "volume_id_internal.h" //#define BLKGETSIZE64 _IOR(0x12,114,size_t) static struct uuidCache_s { struct uuidCache_s *next; // int major, minor; char *device; char *label; char *uc_uuid; /* prefix makes it easier to grep for */ } *uuidCache; /* 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) { int rv = 1; uint64_t size; struct volume_id *vid; vid = volume_id_open_node(device); if (!vid) return rv; if (ioctl(vid->fd, BLKGETSIZE64, &size) != 0) size = 0; #if ENABLE_FEATURE_VOLUMEID_ISO9660 if ((iso_only ? volume_id_probe_iso9660(vid, 0) : volume_id_probe_all(vid, 0, size) ) != 0 ) { goto ret; } #else if (volume_id_probe_all(vid, 0, size) != 0) { goto ret; } #endif if (vid->label[0] != '\0' || vid->uuid[0] != '\0') { *label = xstrndup(vid->label, sizeof(vid->label)); *uuid = xstrndup(vid->uuid, sizeof(vid->uuid)); dbg("found label '%s', uuid '%s' on %s", *label, *uuid, device); rv = 0; } ret: free_volume_id(vid); 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) { struct uuidCache_s *last; if (!uuidCache) { last = uuidCache = xzalloc(sizeof(*uuidCache)); } else { for (last = uuidCache; last->next; last = last->next) continue; last->next = xzalloc(sizeof(*uuidCache)); last = last->next; } /*last->next = NULL; - xzalloc did it*/ // last->major = major; // last->minor = minor; last->device = device; 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, *last_slash; char *uuid, *label; char *ptr; 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! */ ptr = device; while (*ptr) if (*ptr++ == '/') 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 { *last_slash = '/'; } } mknod(device, S_IFBLK | 0600, makedev(ma, mi)); 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 (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) { char line[100]; int ma, mi; unsigned long long sz; FILE *procpt; int firstPass; int handleOnFirst; char *chptr; procpt = xfopen_for_read("/proc/partitions"); /* # cat /proc/partitions major minor #blocks name 8 0 293036184 sda 8 1 6835626 sda1 8 2 1 sda2 8 5 979933 sda5 8 6 15623181 sda6 8 7 97659103 sda7 8 8 171935631 sda8 */ for (firstPass = 1; firstPass >= 0; firstPass--) { fseek(procpt, 0, SEEK_SET); while (fgets(line, sizeof(line), procpt)) { /* The original version of this code used sscanf, but diet's sscanf is quite limited */ chptr = line; if (*chptr != ' ') continue; chptr = skip_whitespace(chptr); ma = bb_strtou(chptr, &chptr, 0); if (ma < 0) continue; chptr = skip_whitespace(chptr); mi = bb_strtou(chptr, &chptr, 0); if (mi < 0) continue; chptr = skip_whitespace(chptr); sz = bb_strtoull(chptr, &chptr, 0); if ((long long)sz == -1LL) continue; chptr = skip_whitespace(chptr); /* skip extended partitions (heuristic: size 1) */ if (sz == 1) continue; *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; /* look only at md devices on first pass */ handleOnFirst = (chptr[0] == 'm' && chptr[1] == 'd'); if (firstPass != handleOnFirst) continue; /* heuristic: partition name ends in a digit */ if (isdigit(chptr[strlen(chptr) - 1])) { uuidcache_check_device(chptr, ma, mi, 0); } } } fclose(procpt); } static void dev_get_major_minor(char *device_name, int *major, int *minor) { 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'; colon = strchr(dev, ':'); if (!colon) goto ret; *major = bb_strtou(dev, NULL, 10); *minor = bb_strtou(colon + 1, NULL, 10); 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_for_read(PROC_CDROMS); if (!proccd) { // 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)) { 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(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"); } break; } } fclose(proccd); } static void uuidcache_init(void) { if (uuidCache) return; uuidcache_init_partitions(); uuidcache_init_cdroms(); } #define UUID 1 #define VOL 2 #ifdef UNUSED static char * get_spec_by_x(int n, const char *t, int *majorPtr, int *minorPtr) { struct uuidCache_s *uc; uuidcache_init(); uc = uuidCache; while (uc) { switch (n) { case 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) == 0) { *majorPtr = uc->major; *minorPtr = uc->minor; return uc->device; } break; } uc = uc->next; } return NULL; } static unsigned char fromhex(char c) { if (isdigit(c)) return (c - '0'); return ((c|0x20) - 'a' + 10); } static char * 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] != '-' ) { 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, (char *)uuid, major, minor); bad_uuid: fprintf(stderr, _("mount: bad UUID")); return 0; } static char * get_spec_by_volume_label(const char *s, int *major, int *minor) { return get_spec_by_x(VOL, s, major, minor); } static int display_uuid_cache(void) { struct uuidCache_s *u; size_t i; uuidcache_init(); u = uuidCache; while (u) { printf("%s %s %s\n", u->device, u->label, u->uc_uuid); u = u->next; } return 0; } #endif // UNUSED /* Used by mount and findfs */ char *get_devname_from_label(const char *spec) { struct uuidCache_s *uc; int spec_len = strlen(spec); uuidcache_init(); uc = uuidCache; while (uc) { // FIXME: empty label ("LABEL=") matches anything??! if (uc->label[0] && strncmp(spec, uc->label, spec_len) == 0) { return xstrdup(uc->device); } uc = uc->next; } return NULL; } char *get_devname_from_uuid(const char *spec) { struct uuidCache_s *uc; uuidcache_init(); uc = uuidCache; while (uc) { /* case of hex numbers doesn't matter */ if (strcasecmp(spec, uc->uc_uuid) == 0) { return xstrdup(uc->device); } uc = uc->next; } return NULL; }