diff options
author | Denis Vlasenko | 2008-02-18 21:08:49 +0000 |
---|---|---|
committer | Denis Vlasenko | 2008-02-18 21:08:49 +0000 |
commit | de7684a309ad20c1b889d048d741cb1dd52245f7 (patch) | |
tree | efae3387e1978cdd128ff2a922b734d0e9d0180f /util-linux/volume_id/get_devname.c | |
parent | 27dd495b98a6135554b1d839fefe436ba3c6ca71 (diff) | |
download | busybox-de7684a309ad20c1b889d048d741cb1dd52245f7.zip busybox-de7684a309ad20c1b889d048d741cb1dd52245f7.tar.gz |
support for mount by label (not yet tested)
Also adds findfs applet. Closes bug 1143.
Diffstat (limited to 'util-linux/volume_id/get_devname.c')
-rw-r--r-- | util-linux/volume_id/get_devname.c | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/util-linux/volume_id/get_devname.c b/util-linux/volume_id/get_devname.c new file mode 100644 index 0000000..75060b0 --- /dev/null +++ b/util-linux/volume_id/get_devname.c @@ -0,0 +1,399 @@ +/* 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 tarball for details. + */ + +#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" + +static struct uuidCache_s { + struct uuidCache_s *next; + char uuid[16]; + char *device; + char *label; + int major, minor; +} *uuidCache; + +/* for now, only ext2, ext3 and xfs are supported */ +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 (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) { + goto ret; + } +#else + if (volume_id_probe_all(vid, 0, size) != 0) { + goto ret; + } +#endif + + if (vid->label[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); + rv = 0; + } + ret: + free_volume_id(vid); + return rv; +} + +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->label = label; + last->device = device; + last->major = major; + last->minor = minor; + memcpy(last->uuid, uuid, sizeof(last->uuid)); +} + +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 *ptr; + char *deviceDir = NULL; + int mustRemove = 0; + int mustRemoveDir = 0; + int i; + + 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); + } else { + mustRemoveDir = 1; + } + } + + mknod(device, S_IFBLK | 0600, makedev(ma, mi)); + mustRemove = 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); +} + +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(PROC_PARTITIONS, "r"); +/* +# 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++; + + 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 */ + 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 int +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'; + + *major = strtol(major_ptr, NULL, 10); + *minor = strtol(minor_ptr, NULL, 10); + + return 0; +} + +static void +uuidcache_init_cdroms(void) +{ + char line[100]; + int ma, mi; + FILE *proccd; + + proccd = fopen(PROC_CDROMS, "r"); + 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); + } + 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))) { + char *device_name; + device_name = strtok(line + strlen(drive_name_string), "\t\n"); + while (device_name) { + 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 (!memcmp(t, uc->uuid, sizeof(uc->uuid))) { + *majorPtr = uc->major; + *minorPtr = uc->minor; + return uc->device; + } + break; + case VOL: + if (!strcmp(t, uc->label)) { + *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'); + if (islower(c)) + return (c - 'a' + 10); + return (c - '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 ", 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"); + 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) { + if (uc->label && !strncmp(spec, uc->label, spec_len)) { + 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) { + if (!memcmp(spec, uc->uuid, sizeof(uc->uuid))) { + return xstrdup(uc->device); + } + uc = uc->next; + } + return NULL; +} |