/* 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 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 #include /* 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)) 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 (strncmp(*fsname, "UUID=", 5) == 0) tmp = get_devname_from_uuid(*fsname + 5); else if (strncmp(*fsname, "LABEL=", 6) == 0) tmp = get_devname_from_label(*fsname + 6); if (tmp == *fsname) return 0; /* no UUID= or LABEL= prefix found */ if (tmp) *fsname = tmp; return 1; }