1 /* vi: set sw=4 ts=4: */
3 * Support functions for mounting devices by label/uuid
5 * Copyright (C) 2006 by Jason Schoon <floydpink@gmail.com>
6 * Some portions cribbed from e2fsprogs, util-linux, dosfstools
8 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
11 #include "volume_id_internal.h"
13 //#define BLKGETSIZE64 _IOR(0x12,114,size_t)
15 static struct uuidCache_s {
16 struct uuidCache_s *next;
20 char *uc_uuid; /* prefix makes it easier to grep for */
23 /* Returns !0 on error.
24 * Otherwise, returns malloc'ed strings for label and uuid
25 * (and they can't be NULL, although they can be "") */
26 #if !ENABLE_FEATURE_VOLUMEID_ISO9660
27 #define get_label_uuid(device, label, uuid, iso_only) \
28 get_label_uuid(device, label, uuid)
31 get_label_uuid(const char *device, char **label, char **uuid, int iso_only)
35 struct volume_id *vid;
37 vid = volume_id_open_node(device);
41 if (ioctl(vid->fd, BLKGETSIZE64, &size) != 0)
44 #if ENABLE_FEATURE_VOLUMEID_ISO9660
46 volume_id_probe_iso9660(vid, 0) :
47 volume_id_probe_all(vid, 0, size)
53 if (volume_id_probe_all(vid, 0, size) != 0) {
58 if (vid->label[0] != '\0' || vid->uuid[0] != '\0') {
59 *label = xstrndup(vid->label, sizeof(vid->label));
60 *uuid = xstrndup(vid->uuid, sizeof(vid->uuid));
61 dbg("found label '%s', uuid '%s' on %s", *label, *uuid, device);
69 /* NB: we take ownership of (malloc'ed) label and uuid */
71 uuidcache_addentry(char *device, /*int major, int minor,*/ char *label, char *uuid)
73 struct uuidCache_s *last;
76 last = uuidCache = xzalloc(sizeof(*uuidCache));
78 for (last = uuidCache; last->next; last = last->next)
80 last->next = xzalloc(sizeof(*uuidCache));
83 /*last->next = NULL; - xzalloc did it*/
84 // last->major = major;
85 // last->minor = minor;
86 last->device = device;
91 /* If get_label_uuid() on device_name returns success,
92 * add a cache entry for this device.
93 * If device node does not exist, it will be temporarily created. */
94 #if !ENABLE_FEATURE_VOLUMEID_ISO9660
95 #define uuidcache_check_device(device_name, ma, mi, iso_only) \
96 uuidcache_check_device(device_name, ma, mi)
99 uuidcache_check_device(const char *device_name, int ma, int mi, int iso_only)
101 char *device, *last_slash;
108 device = xasprintf("/dev/%s", device_name);
109 if (access(device, F_OK) != 0) {
110 /* device does not exist, temporarily create */
114 goto ret; /* we don't know major:minor! */
121 // BUG: handles only slash_cnt == 3 case
122 last_slash = strrchr(device, '/');
124 if (mkdir(device, 0644)) {
125 bb_perror_msg("can't create directory %s", device);
127 last_slash = NULL; /* prevents rmdir */
132 mknod(device, S_IFBLK | 0600, makedev(ma, mi));
138 if (get_label_uuid(device, &label, &uuid, iso_only) == 0) {
139 uuidcache_addentry(device, /*ma, mi,*/ label, uuid);
140 /* "device" is owned by cache now, don't free */
155 /* Run uuidcache_check_device() for every device mentioned
156 * in /proc/partitions */
158 uuidcache_init_partitions(void)
162 unsigned long long sz;
168 procpt = xfopen("/proc/partitions", "r");
170 # cat /proc/partitions
171 major minor #blocks name
181 for (firstPass = 1; firstPass >= 0; firstPass--) {
182 fseek(procpt, 0, SEEK_SET);
184 while (fgets(line, sizeof(line), procpt)) {
185 /* The original version of this code used sscanf, but
186 diet's sscanf is quite limited */
188 if (*chptr != ' ') continue;
189 chptr = skip_whitespace(chptr);
191 ma = bb_strtou(chptr, &chptr, 0);
192 if (ma < 0) continue;
193 chptr = skip_whitespace(chptr);
195 mi = bb_strtou(chptr, &chptr, 0);
196 if (mi < 0) continue;
197 chptr = skip_whitespace(chptr);
199 sz = bb_strtoull(chptr, &chptr, 0);
200 if ((long long)sz == -1LL) continue;
201 chptr = skip_whitespace(chptr);
203 /* skip extended partitions (heuristic: size 1) */
207 *strchrnul(chptr, '\n') = '\0';
208 /* now chptr => device name */
209 dbg("/proc/partitions: maj:%d min:%d sz:%llu name:'%s'",
214 /* look only at md devices on first pass */
215 handleOnFirst = (chptr[0] == 'm' && chptr[1] == 'd');
216 if (firstPass != handleOnFirst)
219 /* heuristic: partition name ends in a digit */
220 if (isdigit(chptr[strlen(chptr) - 1])) {
221 uuidcache_check_device(chptr, ma, mi, 0);
230 dev_get_major_minor(char *device_name, int *major, int *minor)
237 dev_path = xasprintf("/sys/block/%s/dev", device_name);
238 sz = open_read_close(dev_path, dev, sizeof(dev) - 1);
243 colon = strchr(dev, ':');
246 *major = bb_strtou(dev, NULL, 10);
247 *minor = bb_strtou(colon + 1, NULL, 10);
255 uuidcache_init_cdroms(void)
257 #define PROC_CDROMS "/proc/sys/dev/cdrom/info"
262 proccd = fopen(PROC_CDROMS, "r");
264 // static smallint warn = 0;
267 // bb_error_msg("can't open %s, UUID and LABEL "
268 // "conversion cannot be done for CD-Roms",
274 while (fgets(line, sizeof(line), proccd)) {
275 static const char drive_name_string[] ALIGN1 = "drive name:";
277 if (strncmp(line, drive_name_string, sizeof(drive_name_string) - 1) == 0) {
280 device_name = strtok(skip_whitespace(line + sizeof(drive_name_string) - 1), " \t\n");
281 while (device_name && device_name[0]) {
283 dev_get_major_minor(device_name, &ma, &mi);
284 uuidcache_check_device(device_name, ma, mi, 1);
285 device_name = strtok(NULL, " \t\n");
300 uuidcache_init_partitions();
301 uuidcache_init_cdroms();
309 get_spec_by_x(int n, const char *t, int *majorPtr, int *minorPtr)
311 struct uuidCache_s *uc;
319 if (strcmp(t, uc->uc_uuid) == 0) {
320 *majorPtr = uc->major;
321 *minorPtr = uc->minor;
326 if (strcmp(t, uc->label) == 0) {
327 *majorPtr = uc->major;
328 *minorPtr = uc->minor;
343 return ((c|0x20) - 'a' + 10);
347 get_spec_by_uuid(const char *s, int *major, int *minor)
349 unsigned char uuid[16];
352 if (strlen(s) != 36 || s[8] != '-' || s[13] != '-'
353 || s[18] != '-' || s[23] != '-'
357 for (i = 0; i < 16; i++) {
360 if (!isxdigit(s[0]) || !isxdigit(s[1]))
362 uuid[i] = ((fromhex(s[0]) << 4) | fromhex(s[1]));
365 return get_spec_by_x(UUID, (char *)uuid, major, minor);
368 fprintf(stderr, _("mount: bad UUID"));
373 get_spec_by_volume_label(const char *s, int *major, int *minor)
375 return get_spec_by_x(VOL, s, major, minor);
378 static int display_uuid_cache(void)
380 struct uuidCache_s *u;
387 printf("%s %s %s\n", u->device, u->label, u->uc_uuid);
396 /* Used by mount and findfs */
398 char *get_devname_from_label(const char *spec)
400 struct uuidCache_s *uc;
401 int spec_len = strlen(spec);
406 // FIXME: empty label ("LABEL=") matches anything??!
407 if (uc->label[0] && strncmp(spec, uc->label, spec_len) == 0) {
408 return xstrdup(uc->device);
415 char *get_devname_from_uuid(const char *spec)
417 struct uuidCache_s *uc;
422 /* case of hex numbers doesn't matter */
423 if (strcasecmp(spec, uc->uc_uuid) == 0) {
424 return xstrdup(uc->device);