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 #define PROC_PARTITIONS "/proc/partitions"
16 #define PROC_CDROMS "/proc/sys/dev/cdrom/info"
17 #define DEVLABELDIR "/dev"
18 #define SYS_BLOCK "/sys/block"
20 static struct uuidCache_s {
21 struct uuidCache_s *next;
28 /* for now, only ext2, ext3 and xfs are supported */
29 #if !ENABLE_FEATURE_VOLUMEID_ISO9660
30 #define get_label_uuid(device, label, uuid, iso_only) \
31 get_label_uuid(device, label, uuid)
34 get_label_uuid(const char *device, char **label, char **uuid, int iso_only)
38 struct volume_id *vid;
40 vid = volume_id_open_node(device);
42 if (ioctl(vid->fd, BLKGETSIZE64, &size) != 0) {
46 #if ENABLE_FEATURE_VOLUMEID_ISO9660
48 volume_id_probe_iso9660(vid, 0) != 0 :
49 volume_id_probe_all(vid, 0, size) != 0) {
53 if (volume_id_probe_all(vid, 0, size) != 0) {
58 if (vid->label[0] != '\0') {
59 *label = xstrndup(vid->label, sizeof(vid->label));
60 *uuid = xstrndup(vid->uuid, sizeof(vid->uuid));
61 printf("Found label %s on %s (uuid:%s)\n", *label, device, *uuid);
70 uuidcache_addentry(char * device, int major, int minor, char *label, char *uuid)
72 struct uuidCache_s *last;
75 last = uuidCache = xzalloc(sizeof(*uuidCache));
77 for (last = uuidCache; last->next; last = last->next)
79 last->next = xzalloc(sizeof(*uuidCache));
82 /*last->next = NULL; - xzalloc did it*/
84 last->device = device;
87 memcpy(last->uuid, uuid, sizeof(last->uuid));
90 #if !ENABLE_FEATURE_VOLUMEID_ISO9660
91 #define uuidcache_check_device(device_name, ma, mi, iso_only) \
92 uuidcache_check_device(device_name, ma, mi)
95 uuidcache_check_device(const char *device_name, int ma, int mi, int iso_only)
98 char *uuid = NULL, *label = NULL;
100 char *deviceDir = NULL;
102 int mustRemoveDir = 0;
105 sprintf(device, "%s/%s", DEVLABELDIR, device_name);
106 if (access(device, F_OK)) {
113 deviceDir = alloca(strlen(device) + 1);
114 strcpy(deviceDir, device);
115 ptr = deviceDir + (strlen(device) - 1);
118 if (mkdir(deviceDir, 0644)) {
119 printf("mkdir: cannot create directory %s: %d\n", deviceDir, errno);
125 mknod(device, S_IFBLK | 0600, makedev(ma, mi));
128 if (!get_label_uuid(device, &label, &uuid, iso_only))
129 uuidcache_addentry(strdup(device), ma, mi,
132 if (mustRemove) unlink(device);
133 if (mustRemoveDir) rmdir(deviceDir);
137 uuidcache_init_partitions(void)
141 unsigned long long sz;
147 procpt = xfopen(PROC_PARTITIONS, "r");
149 # cat /proc/partitions
150 major minor #blocks name
160 for (firstPass = 1; firstPass >= 0; firstPass--) {
161 fseek(procpt, 0, SEEK_SET);
163 while (fgets(line, sizeof(line), procpt)) {
164 /* The original version of this code used sscanf, but
165 diet's sscanf is quite limited */
167 if (*chptr != ' ') continue;
170 ma = bb_strtou(chptr, &chptr, 0);
171 if (ma < 0) continue;
172 chptr = skip_whitespace(chptr);
174 mi = bb_strtou(chptr, &chptr, 0);
175 if (mi < 0) continue;
176 chptr = skip_whitespace(chptr);
178 sz = bb_strtoull(chptr, &chptr, 0);
179 if ((long long)sz == -1LL) continue;
180 chptr = skip_whitespace(chptr);
182 /* skip extended partitions (heuristic: size 1) */
186 *strchrnul(chptr, '\n') = '\0';
187 /* now chptr => device name */
191 /* look only at md devices on first pass */
192 handleOnFirst = (chptr[0] == 'm' && chptr[1] == 'd');
193 if (firstPass != handleOnFirst)
196 /* heuristic: partition name ends in a digit */
197 if (isdigit(chptr[strlen(chptr) - 1])) {
198 uuidcache_check_device(chptr, ma, mi, 0);
207 dev_get_major_minor(char *device_name, int *major, int *minor)
212 char *major_ptr, *minor_ptr;
214 dev_path = alloca(strlen(SYS_BLOCK) + strlen(device_name) + 6);
215 sprintf(dev_path, "%s/%s/dev", SYS_BLOCK, device_name);
217 fd = open(dev_path, O_RDONLY);
218 if (fd < 0) return 1;
219 full_read(fd, dev, sizeof(dev));
223 minor_ptr = strchr(dev, ':');
224 if (!minor_ptr) return 1;
227 *major = strtol(major_ptr, NULL, 10);
228 *minor = strtol(minor_ptr, NULL, 10);
234 uuidcache_init_cdroms(void)
240 proccd = fopen(PROC_CDROMS, "r");
242 static smallint warn = 0;
245 bb_error_msg("mount: could not open %s, so UUID and LABEL "
246 "conversion cannot be done for CD-Roms.",
252 while (fgets(line, sizeof(line), proccd)) {
253 const char *drive_name_string = "drive name:\t\t";
254 if (!strncmp(line, drive_name_string, strlen(drive_name_string))) {
256 device_name = strtok(line + strlen(drive_name_string), "\t\n");
257 while (device_name) {
258 dev_get_major_minor(device_name, &ma, &mi);
259 uuidcache_check_device(device_name, ma, mi, 1);
260 device_name = strtok(NULL, "\t\n");
275 uuidcache_init_partitions();
276 uuidcache_init_cdroms();
284 get_spec_by_x(int n, const char *t, int * majorPtr, int * minorPtr)
286 struct uuidCache_s *uc;
294 if (!memcmp(t, uc->uuid, sizeof(uc->uuid))) {
295 *majorPtr = uc->major;
296 *minorPtr = uc->minor;
301 if (!strcmp(t, uc->label)) {
302 *majorPtr = uc->major;
303 *minorPtr = uc->minor;
319 return (c - 'a' + 10);
320 return (c - 'A' + 10);
324 get_spec_by_uuid(const char *s, int * major, int * minor)
326 unsigned char uuid[16];
329 if (strlen(s) != 36 ||
330 s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-')
332 for (i=0; i<16; i++) {
334 if (!isxdigit(s[0]) || !isxdigit(s[1]))
336 uuid[i] = ((fromhex(s[0])<<4) | fromhex(s[1]));
339 return get_spec_by_x(UUID, (char *)uuid, major, minor);
342 fprintf(stderr, _("mount: bad UUID"));
347 get_spec_by_volume_label(const char *s, int *major, int *minor)
349 return get_spec_by_x(VOL, s, major, minor);
352 static int display_uuid_cache(void)
354 struct uuidCache_s *u;
361 printf("%s %s ", u->device, u->label);
362 for (i = 0; i < sizeof(u->uuid); i++) {
363 if (i == 4 || i == 6 || i == 8 || i == 10)
365 printf("%x", u->uuid[i] & 0xff);
376 /* Used by mount and findfs */
378 char *get_devname_from_label(const char *spec)
380 struct uuidCache_s *uc;
381 int spec_len = strlen(spec);
386 if (uc->label && !strncmp(spec, uc->label, spec_len)) {
387 return xstrdup(uc->device);
394 char *get_devname_from_uuid(const char *spec)
396 struct uuidCache_s *uc;
401 if (!memcmp(spec, uc->uuid, sizeof(uc->uuid))) {
402 return xstrdup(uc->device);