| /* 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; |
| } |