| /* vi: set sw=4 ts=4: */ | |
| /* | |
| * Support functions for mounting devices by label/uuid | |
| * | |
| * Copyright (C) 2006 by Jason Schoon <floydpink@gmail.com> | |
| * Some portions cribbed from e2fsprogs, util-linux, dosfstools | |
| * | |
| * Licensed under GPLv2 or later, see file LICENSE in this source tree. | |
| */ | |
| //kbuild:lib-$(CONFIG_BLKID) += get_devname.o | |
| //kbuild:lib-$(CONFIG_FINDFS) += get_devname.o | |
| //kbuild:lib-$(CONFIG_FEATURE_MOUNT_LABEL) += get_devname.o | |
| //kbuild:lib-$(CONFIG_FEATURE_SWAPONOFF_LABEL) += get_devname.o | |
| #include <sys/mount.h> /* BLKGETSIZE64 */ | |
| #if !defined(BLKGETSIZE64) | |
| # define BLKGETSIZE64 _IOR(0x12,114,size_t) | |
| #endif | |
| #include "volume_id_internal.h" | |
| 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 */ | |
| IF_FEATURE_BLKID_TYPE(const char *type;) | |
| } *uuidCache; | |
| #if !ENABLE_FEATURE_BLKID_TYPE | |
| #define get_label_uuid(fd, label, uuid, type) \ | |
| get_label_uuid(fd, label, uuid) | |
| #define uuidcache_addentry(device, label, uuid, type) \ | |
| uuidcache_addentry(device, label, uuid) | |
| #endif | |
| /* Returns !0 on error. | |
| * Otherwise, returns malloc'ed strings for label and uuid | |
| * (and they can't be NULL, although they can be ""). | |
| * NB: closes fd. */ | |
| static int | |
| get_label_uuid(int fd, char **label, char **uuid, const char **type) | |
| { | |
| int rv = 1; | |
| uint64_t size; | |
| struct volume_id *vid; | |
| /* fd is owned by vid now */ | |
| vid = volume_id_open_node(fd); | |
| if (ioctl(/*vid->*/fd, BLKGETSIZE64, &size) != 0) | |
| size = 0; | |
| if (volume_id_probe_all(vid, /*0,*/ size) != 0) | |
| goto ret; | |
| if (vid->label[0] != '\0' || vid->uuid[0] != '\0' | |
| #if ENABLE_FEATURE_BLKID_TYPE | |
| || vid->type != NULL | |
| #endif | |
| ) { | |
| *label = xstrndup(vid->label, sizeof(vid->label)); | |
| *uuid = xstrndup(vid->uuid, sizeof(vid->uuid)); | |
| #if ENABLE_FEATURE_BLKID_TYPE | |
| *type = vid->type; | |
| dbg("found label '%s', uuid '%s', type '%s'", *label, *uuid, *type); | |
| #else | |
| dbg("found label '%s', uuid '%s'", *label, *uuid); | |
| #endif | |
| rv = 0; | |
| } | |
| ret: | |
| free_volume_id(vid); /* also closes fd */ | |
| 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, const char *type) | |
| { | |
| 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_FEATURE_BLKID_TYPE(last->type = type;) | |
| } | |
| /* 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. */ | |
| static int FAST_FUNC | |
| uuidcache_check_device(const char *device, | |
| struct stat *statbuf, | |
| void *userData UNUSED_PARAM, | |
| int depth UNUSED_PARAM) | |
| { | |
| /* note: this check rejects links to devices, among other nodes */ | |
| if (!S_ISBLK(statbuf->st_mode) | |
| #if ENABLE_FEATURE_VOLUMEID_UBIFS | |
| && !(S_ISCHR(statbuf->st_mode) && strncmp(bb_basename(device), "ubi", 3) == 0) | |
| #endif | |
| ) | |
| return TRUE; | |
| /* Users report that mucking with floppies (especially non-present | |
| * ones) is significant PITA. This is a horribly dirty hack, | |
| * but it is very useful in real world. | |
| * If this will ever need to be enabled, consider using O_NONBLOCK. | |
| */ | |
| if (major(statbuf->st_rdev) == 2) | |
| return TRUE; | |
| add_to_uuid_cache(device); | |
| return TRUE; | |
| } | |
| static struct uuidCache_s* | |
| uuidcache_init(int scan_devices) | |
| { | |
| dbg("DBG: uuidCache=%x, uuidCache"); | |
| if (uuidCache) | |
| return uuidCache; | |
| /* We were scanning /proc/partitions | |
| * and /proc/sys/dev/cdrom/info here. | |
| * Missed volume managers. I see that "standard" blkid uses these: | |
| * /dev/mapper/control | |
| * /proc/devices | |
| * /proc/evms/volumes | |
| * /proc/lvm/VGs | |
| * This is unacceptably complex. Let's just scan /dev. | |
| * (Maybe add scanning of /sys/block/XXX/dev for devices | |
| * somehow not having their /dev/XXX entries created?) */ | |
| if (scan_devices) | |
| recursive_action("/dev", ACTION_RECURSE, | |
| uuidcache_check_device, /* file_action */ | |
| NULL, /* dir_action */ | |
| NULL, /* userData */ | |
| 0 /* depth */); | |
| return uuidCache; | |
| } | |
| #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; | |
| uc = uuidcache_init(/*scan_devices:*/ 1); | |
| 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); | |
| } | |
| #endif // UNUSED | |
| /* Used by blkid */ | |
| void display_uuid_cache(int scan_devices) | |
| { | |
| struct uuidCache_s *uc; | |
| uc = uuidcache_init(scan_devices); | |
| while (uc) { | |
| printf("%s:", uc->device); | |
| if (uc->label[0]) | |
| printf(" LABEL=\"%s\"", uc->label); | |
| if (uc->uc_uuid[0]) | |
| printf(" UUID=\"%s\"", uc->uc_uuid); | |
| #if ENABLE_FEATURE_BLKID_TYPE | |
| if (uc->type) | |
| printf(" TYPE=\"%s\"", uc->type); | |
| #endif | |
| bb_putchar('\n'); | |
| uc = uc->next; | |
| } | |
| } | |
| int add_to_uuid_cache(const char *device) | |
| { | |
| char *uuid = uuid; /* for compiler */ | |
| char *label = label; | |
| #if ENABLE_FEATURE_BLKID_TYPE | |
| const char *type = type; | |
| #endif | |
| int fd; | |
| fd = open(device, O_RDONLY); | |
| if (fd < 0) | |
| return 0; | |
| /* get_label_uuid() closes fd in all cases (success & failure) */ | |
| if (get_label_uuid(fd, &label, &uuid, &type) == 0) { | |
| /* uuidcache_addentry() takes ownership of all four params */ | |
| uuidcache_addentry(xstrdup(device), /*ma, mi,*/ label, uuid, type); | |
| return 1; | |
| } | |
| return 0; | |
| } | |
| /* Used by mount and findfs */ | |
| char *get_devname_from_label(const char *spec) | |
| { | |
| struct uuidCache_s *uc; | |
| uc = uuidcache_init(/*scan_devices:*/ 1); | |
| while (uc) { | |
| if (uc->label[0] && strcmp(spec, uc->label) == 0) { | |
| return xstrdup(uc->device); | |
| } | |
| uc = uc->next; | |
| } | |
| return NULL; | |
| } | |
| char *get_devname_from_uuid(const char *spec) | |
| { | |
| struct uuidCache_s *uc; | |
| uc = uuidcache_init(/*scan_devices:*/ 1); | |
| 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; | |
| } | |
| int resolve_mount_spec(char **fsname) | |
| { | |
| char *tmp = *fsname; | |
| if (is_prefixed_with(*fsname, "UUID=")) | |
| tmp = get_devname_from_uuid(*fsname + 5); | |
| else if (is_prefixed_with(*fsname, "LABEL=")) | |
| tmp = get_devname_from_label(*fsname + 6); | |
| if (tmp == *fsname) | |
| return 0; /* no UUID= or LABEL= prefix found */ | |
| if (tmp) | |
| *fsname = tmp; | |
| return 1; | |
| } |