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 */
30 get_label_uuid(const char *device, char **label, char **uuid, int iso_only)
34 struct volume_id *vid;
36 vid = volume_id_open_node(device);
38 if (ioctl(vid->fd, BLKGETSIZE64, &size) != 0) {
42 #if ENABLE_FEATURE_VOLUMEID_ISO9660
44 volume_id_probe_iso9660(vid, 0) != 0 :
45 volume_id_probe_all(vid, 0, size) != 0) {
49 if (volume_id_probe_all(vid, 0, size) != 0) {
54 if (vid->label[0] != '\0') {
55 *label = xstrndup(vid->label, sizeof(vid->label));
56 *uuid = xstrndup(vid->uuid, sizeof(vid->uuid));
57 printf("Found label %s on %s (uuid:%s)\n", *label, device, *uuid);
66 uuidcache_addentry(char * device, int major, int minor, char *label, char *uuid)
68 struct uuidCache_s *last;
71 last = uuidCache = xzalloc(sizeof(*uuidCache));
73 for (last = uuidCache; last->next; last = last->next)
75 last->next = xzalloc(sizeof(*uuidCache));
78 /*last->next = NULL; - xzalloc did it*/
80 last->device = device;
83 memcpy(last->uuid, uuid, sizeof(last->uuid));
87 uuidcache_check_device(const char *device_name, int ma, int mi, int iso_only)
90 char *uuid = NULL, *label = NULL;
92 char *deviceDir = NULL;
94 int mustRemoveDir = 0;
97 sprintf(device, "%s/%s", DEVLABELDIR, device_name);
98 if (access(device, F_OK)) {
105 deviceDir = alloca(strlen(device) + 1);
106 strcpy(deviceDir, device);
107 ptr = deviceDir + (strlen(device) - 1);
110 if (mkdir(deviceDir, 0644)) {
111 printf("mkdir: cannot create directory %s: %d\n", deviceDir, errno);
117 mknod(device, S_IFBLK | 0600, makedev(ma, mi));
120 if (!get_label_uuid(device, &label, &uuid, iso_only))
121 uuidcache_addentry(strdup(device), ma, mi,
124 if (mustRemove) unlink(device);
125 if (mustRemoveDir) rmdir(deviceDir);
129 uuidcache_init_partitions(void)
133 unsigned long long sz;
139 procpt = xfopen(PROC_PARTITIONS, "r");
141 # cat /proc/partitions
142 major minor #blocks name
152 for (firstPass = 1; firstPass >= 0; firstPass--) {
153 fseek(procpt, 0, SEEK_SET);
155 while (fgets(line, sizeof(line), procpt)) {
156 /* The original version of this code used sscanf, but
157 diet's sscanf is quite limited */
159 if (*chptr != ' ') continue;
162 ma = bb_strtou(chptr, &chptr, 0);
163 if (ma < 0) continue;
164 chptr = skip_whitespace(chptr);
166 mi = bb_strtou(chptr, &chptr, 0);
167 if (mi < 0) continue;
168 chptr = skip_whitespace(chptr);
170 sz = bb_strtoull(chptr, &chptr, 0);
171 if ((long long)sz == -1LL) continue;
172 chptr = skip_whitespace(chptr);
174 /* skip extended partitions (heuristic: size 1) */
178 *strchrnul(chptr, '\n') = '\0';
179 /* now chptr => device name */
183 /* look only at md devices on first pass */
184 handleOnFirst = (chptr[0] == 'm' && chptr[1] == 'd');
185 if (firstPass != handleOnFirst)
188 /* heuristic: partition name ends in a digit */
189 if (isdigit(chptr[strlen(chptr) - 1])) {
190 uuidcache_check_device(chptr, ma, mi, 0);
199 dev_get_major_minor(char *device_name, int *major, int *minor)
204 char *major_ptr, *minor_ptr;
206 dev_path = alloca(strlen(SYS_BLOCK) + strlen(device_name) + 6);
207 sprintf(dev_path, "%s/%s/dev", SYS_BLOCK, device_name);
209 fd = open(dev_path, O_RDONLY);
210 if (fd < 0) return 1;
211 full_read(fd, dev, sizeof(dev));
215 minor_ptr = strchr(dev, ':');
216 if (!minor_ptr) return 1;
219 *major = strtol(major_ptr, NULL, 10);
220 *minor = strtol(minor_ptr, NULL, 10);
226 uuidcache_init_cdroms(void)
232 proccd = fopen(PROC_CDROMS, "r");
234 static smallint warn = 0;
237 bb_error_msg("mount: could not open %s, so UUID and LABEL "
238 "conversion cannot be done for CD-Roms.",
244 while (fgets(line, sizeof(line), proccd)) {
245 const char *drive_name_string = "drive name:\t\t";
246 if (!strncmp(line, drive_name_string, strlen(drive_name_string))) {
248 device_name = strtok(line + strlen(drive_name_string), "\t\n");
249 while (device_name) {
250 dev_get_major_minor(device_name, &ma, &mi);
251 uuidcache_check_device(device_name, ma, mi, 1);
252 device_name = strtok(NULL, "\t\n");
267 uuidcache_init_partitions();
268 uuidcache_init_cdroms();
276 get_spec_by_x(int n, const char *t, int * majorPtr, int * minorPtr)
278 struct uuidCache_s *uc;
286 if (!memcmp(t, uc->uuid, sizeof(uc->uuid))) {
287 *majorPtr = uc->major;
288 *minorPtr = uc->minor;
293 if (!strcmp(t, uc->label)) {
294 *majorPtr = uc->major;
295 *minorPtr = uc->minor;
311 return (c - 'a' + 10);
312 return (c - 'A' + 10);
316 get_spec_by_uuid(const char *s, int * major, int * minor)
318 unsigned char uuid[16];
321 if (strlen(s) != 36 ||
322 s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-')
324 for (i=0; i<16; i++) {
326 if (!isxdigit(s[0]) || !isxdigit(s[1]))
328 uuid[i] = ((fromhex(s[0])<<4) | fromhex(s[1]));
331 return get_spec_by_x(UUID, (char *)uuid, major, minor);
334 fprintf(stderr, _("mount: bad UUID"));
339 get_spec_by_volume_label(const char *s, int *major, int *minor)
341 return get_spec_by_x(VOL, s, major, minor);
344 static int display_uuid_cache(void)
346 struct uuidCache_s *u;
353 printf("%s %s ", u->device, u->label);
354 for (i = 0; i < sizeof(u->uuid); i++) {
355 if (i == 4 || i == 6 || i == 8 || i == 10)
357 printf("%x", u->uuid[i] & 0xff);
368 /* Used by mount and findfs */
370 char *get_devname_from_label(const char *spec)
372 struct uuidCache_s *uc;
373 int spec_len = strlen(spec);
378 if (uc->label && !strncmp(spec, uc->label, spec_len)) {
379 return xstrdup(uc->device);
386 char *get_devname_from_uuid(const char *spec)
388 struct uuidCache_s *uc;
393 if (!memcmp(spec, uc->uuid, sizeof(uc->uuid))) {
394 return xstrdup(uc->device);