blob: d1968b744f17c61e37916d7d1b6b2e243368a779 [file] [log] [blame]
Denis Vlasenkode7684a2008-02-18 21:08:49 +00001/* vi: set sw=4 ts=4: */
2/*
3 * Support functions for mounting devices by label/uuid
4 *
5 * Copyright (C) 2006 by Jason Schoon <floydpink@gmail.com>
6 * Some portions cribbed from e2fsprogs, util-linux, dosfstools
7 *
8 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
9 */
10
11#include "volume_id_internal.h"
12
Denis Vlasenkoc5b73722008-03-17 09:21:26 +000013//#define BLKGETSIZE64 _IOR(0x12,114,size_t)
Denis Vlasenkode7684a2008-02-18 21:08:49 +000014
15static struct uuidCache_s {
16 struct uuidCache_s *next;
Denis Vlasenkoc5b73722008-03-17 09:21:26 +000017// int major, minor;
Denis Vlasenkode7684a2008-02-18 21:08:49 +000018 char *device;
19 char *label;
Denis Vlasenkoc5b73722008-03-17 09:21:26 +000020 char *uc_uuid; /* prefix makes it easier to grep for */
Denis Vlasenkode7684a2008-02-18 21:08:49 +000021} *uuidCache;
22
Denis Vlasenkoc5b73722008-03-17 09:21:26 +000023/* 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 "") */
Denis Vlasenko85c24712008-03-17 09:04:04 +000026#if !ENABLE_FEATURE_VOLUMEID_ISO9660
27#define get_label_uuid(device, label, uuid, iso_only) \
28 get_label_uuid(device, label, uuid)
29#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +000030static int
31get_label_uuid(const char *device, char **label, char **uuid, int iso_only)
32{
33 int rv = 1;
34 uint64_t size;
35 struct volume_id *vid;
36
37 vid = volume_id_open_node(device);
Denis Vlasenkoc5b73722008-03-17 09:21:26 +000038 if (!vid)
39 return rv;
Denis Vlasenkode7684a2008-02-18 21:08:49 +000040
Denis Vlasenkoc5b73722008-03-17 09:21:26 +000041 if (ioctl(vid->fd, BLKGETSIZE64, &size) != 0)
Denis Vlasenkode7684a2008-02-18 21:08:49 +000042 size = 0;
Denis Vlasenkode7684a2008-02-18 21:08:49 +000043
44#if ENABLE_FEATURE_VOLUMEID_ISO9660
Denis Vlasenkoc5b73722008-03-17 09:21:26 +000045 if ((iso_only ?
46 volume_id_probe_iso9660(vid, 0) :
47 volume_id_probe_all(vid, 0, size)
48 ) != 0
49 ) {
Denis Vlasenkode7684a2008-02-18 21:08:49 +000050 goto ret;
51 }
52#else
53 if (volume_id_probe_all(vid, 0, size) != 0) {
54 goto ret;
55 }
56#endif
57
Denis Vlasenkoc5b73722008-03-17 09:21:26 +000058 if (vid->label[0] != '\0' || vid->uuid[0] != '\0') {
Denis Vlasenkode7684a2008-02-18 21:08:49 +000059 *label = xstrndup(vid->label, sizeof(vid->label));
60 *uuid = xstrndup(vid->uuid, sizeof(vid->uuid));
Denis Vlasenkoc5b73722008-03-17 09:21:26 +000061 dbg("found label '%s', uuid '%s' on %s", *label, *uuid, device);
Denis Vlasenkode7684a2008-02-18 21:08:49 +000062 rv = 0;
63 }
64 ret:
65 free_volume_id(vid);
66 return rv;
67}
68
Denis Vlasenkoc5b73722008-03-17 09:21:26 +000069/* NB: we take ownership of (malloc'ed) label and uuid */
Denis Vlasenkode7684a2008-02-18 21:08:49 +000070static void
Denis Vlasenkoc5b73722008-03-17 09:21:26 +000071uuidcache_addentry(char *device, /*int major, int minor,*/ char *label, char *uuid)
Denis Vlasenkode7684a2008-02-18 21:08:49 +000072{
73 struct uuidCache_s *last;
Denis Vlasenko42cc3042008-03-24 02:05:58 +000074
Denis Vlasenkode7684a2008-02-18 21:08:49 +000075 if (!uuidCache) {
76 last = uuidCache = xzalloc(sizeof(*uuidCache));
77 } else {
78 for (last = uuidCache; last->next; last = last->next)
79 continue;
80 last->next = xzalloc(sizeof(*uuidCache));
81 last = last->next;
82 }
83 /*last->next = NULL; - xzalloc did it*/
Denis Vlasenkoc5b73722008-03-17 09:21:26 +000084// last->major = major;
85// last->minor = minor;
Denis Vlasenkode7684a2008-02-18 21:08:49 +000086 last->device = device;
Denis Vlasenkoc5b73722008-03-17 09:21:26 +000087 last->label = label;
88 last->uc_uuid = uuid;
Denis Vlasenkode7684a2008-02-18 21:08:49 +000089}
90
Denis Vlasenkoc5b73722008-03-17 09:21:26 +000091/* 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. */
Denis Vlasenko85c24712008-03-17 09:04:04 +000094#if !ENABLE_FEATURE_VOLUMEID_ISO9660
95#define uuidcache_check_device(device_name, ma, mi, iso_only) \
96 uuidcache_check_device(device_name, ma, mi)
97#endif
Denis Vlasenkode7684a2008-02-18 21:08:49 +000098static void
99uuidcache_check_device(const char *device_name, int ma, int mi, int iso_only)
100{
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000101 char *device, *last_slash;
102 char *uuid, *label;
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000103 char *ptr;
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000104 int must_remove = 0;
105 int added = 0;
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000106
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000107 last_slash = NULL;
108 device = xasprintf("/dev/%s", device_name);
109 if (access(device, F_OK) != 0) {
110 /* device does not exist, temporarily create */
111 int slash_cnt = 0;
112
113 if ((ma | mi) < 0)
114 goto ret; /* we don't know major:minor! */
115
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000116 ptr = device;
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000117 while (*ptr)
118 if (*ptr++ == '/')
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000119 slash_cnt++;
120 if (slash_cnt > 2) {
121// BUG: handles only slash_cnt == 3 case
122 last_slash = strrchr(device, '/');
123 *last_slash = '\0';
124 if (mkdir(device, 0644)) {
125 bb_perror_msg("can't create directory %s", device);
126 *last_slash = '/';
127 last_slash = NULL; /* prevents rmdir */
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000128 } else {
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000129 *last_slash = '/';
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000130 }
131 }
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000132 mknod(device, S_IFBLK | 0600, makedev(ma, mi));
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000133 must_remove = 1;
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000134 }
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000135
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000136 uuid = NULL;
137 label = NULL;
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 */
141 added = 1;
142 }
143
144 if (must_remove)
145 unlink(device);
146 if (last_slash) {
147 *last_slash = '\0';
148 rmdir(device);
149 }
150 ret:
151 if (!added)
152 free(device);
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000153}
154
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000155/* Run uuidcache_check_device() for every device mentioned
156 * in /proc/partitions */
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000157static void
158uuidcache_init_partitions(void)
159{
160 char line[100];
161 int ma, mi;
162 unsigned long long sz;
163 FILE *procpt;
164 int firstPass;
165 int handleOnFirst;
166 char *chptr;
167
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000168 procpt = xfopen("/proc/partitions", "r");
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000169/*
170# cat /proc/partitions
171major minor #blocks name
172
173 8 0 293036184 sda
174 8 1 6835626 sda1
175 8 2 1 sda2
176 8 5 979933 sda5
177 8 6 15623181 sda6
178 8 7 97659103 sda7
179 8 8 171935631 sda8
180*/
181 for (firstPass = 1; firstPass >= 0; firstPass--) {
182 fseek(procpt, 0, SEEK_SET);
183
184 while (fgets(line, sizeof(line), procpt)) {
185 /* The original version of this code used sscanf, but
186 diet's sscanf is quite limited */
187 chptr = line;
188 if (*chptr != ' ') continue;
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000189 chptr = skip_whitespace(chptr);
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000190
191 ma = bb_strtou(chptr, &chptr, 0);
192 if (ma < 0) continue;
193 chptr = skip_whitespace(chptr);
194
195 mi = bb_strtou(chptr, &chptr, 0);
196 if (mi < 0) continue;
197 chptr = skip_whitespace(chptr);
198
199 sz = bb_strtoull(chptr, &chptr, 0);
200 if ((long long)sz == -1LL) continue;
201 chptr = skip_whitespace(chptr);
202
203 /* skip extended partitions (heuristic: size 1) */
204 if (sz == 1)
205 continue;
206
207 *strchrnul(chptr, '\n') = '\0';
208 /* now chptr => device name */
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000209 dbg("/proc/partitions: maj:%d min:%d sz:%llu name:'%s'",
210 ma, mi, sz, chptr);
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000211 if (!chptr[0])
212 continue;
213
214 /* look only at md devices on first pass */
215 handleOnFirst = (chptr[0] == 'm' && chptr[1] == 'd');
216 if (firstPass != handleOnFirst)
217 continue;
218
219 /* heuristic: partition name ends in a digit */
220 if (isdigit(chptr[strlen(chptr) - 1])) {
221 uuidcache_check_device(chptr, ma, mi, 0);
222 }
223 }
224 }
225
226 fclose(procpt);
227}
228
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000229static void
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000230dev_get_major_minor(char *device_name, int *major, int *minor)
231{
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000232 char dev[16];
233 char *dev_path;
234 char *colon;
235 int sz;
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000236
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000237 dev_path = xasprintf("/sys/block/%s/dev", device_name);
238 sz = open_read_close(dev_path, dev, sizeof(dev) - 1);
239 if (sz < 0)
240 goto ret;
241 dev[sz] = '\0';
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000242
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000243 colon = strchr(dev, ':');
244 if (!colon)
245 goto ret;
Denis Vlasenkod25c33f2008-03-17 09:25:05 +0000246 *major = bb_strtou(dev, NULL, 10);
247 *minor = bb_strtou(colon + 1, NULL, 10);
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000248
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000249 ret:
250 free(dev_path);
251 return;
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000252}
253
254static void
255uuidcache_init_cdroms(void)
256{
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000257#define PROC_CDROMS "/proc/sys/dev/cdrom/info"
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000258 char line[100];
259 int ma, mi;
260 FILE *proccd;
261
262 proccd = fopen(PROC_CDROMS, "r");
263 if (!proccd) {
Denis Vlasenkod25c33f2008-03-17 09:25:05 +0000264// static smallint warn = 0;
265// if (!warn) {
266// warn = 1;
267// bb_error_msg("can't open %s, UUID and LABEL "
268// "conversion cannot be done for CD-Roms",
269// PROC_CDROMS);
270// }
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000271 return;
272 }
273
274 while (fgets(line, sizeof(line), proccd)) {
Denis Vlasenkod25c33f2008-03-17 09:25:05 +0000275 static const char drive_name_string[] ALIGN1 = "drive name:";
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000276
277 if (strncmp(line, drive_name_string, sizeof(drive_name_string) - 1) == 0) {
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000278 char *device_name;
Denis Vlasenkod25c33f2008-03-17 09:25:05 +0000279
280 device_name = strtok(skip_whitespace(line + sizeof(drive_name_string) - 1), " \t\n");
281 while (device_name && device_name[0]) {
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000282 ma = mi = -1;
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000283 dev_get_major_minor(device_name, &ma, &mi);
284 uuidcache_check_device(device_name, ma, mi, 1);
Denis Vlasenkod25c33f2008-03-17 09:25:05 +0000285 device_name = strtok(NULL, " \t\n");
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000286 }
287 break;
288 }
289 }
290
291 fclose(proccd);
292}
293
294static void
295uuidcache_init(void)
296{
297 if (uuidCache)
298 return;
299
300 uuidcache_init_partitions();
301 uuidcache_init_cdroms();
302}
303
304#define UUID 1
305#define VOL 2
306
307#ifdef UNUSED
308static char *
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000309get_spec_by_x(int n, const char *t, int *majorPtr, int *minorPtr)
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000310{
311 struct uuidCache_s *uc;
312
313 uuidcache_init();
314 uc = uuidCache;
315
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000316 while (uc) {
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000317 switch (n) {
318 case UUID:
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000319 if (strcmp(t, uc->uc_uuid) == 0) {
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000320 *majorPtr = uc->major;
321 *minorPtr = uc->minor;
322 return uc->device;
323 }
324 break;
325 case VOL:
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000326 if (strcmp(t, uc->label) == 0) {
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000327 *majorPtr = uc->major;
328 *minorPtr = uc->minor;
329 return uc->device;
330 }
331 break;
332 }
333 uc = uc->next;
334 }
335 return NULL;
336}
337
338static unsigned char
339fromhex(char c)
340{
341 if (isdigit(c))
342 return (c - '0');
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000343 return ((c|0x20) - 'a' + 10);
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000344}
345
346static char *
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000347get_spec_by_uuid(const char *s, int *major, int *minor)
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000348{
349 unsigned char uuid[16];
350 int i;
351
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000352 if (strlen(s) != 36 || s[8] != '-' || s[13] != '-'
353 || s[18] != '-' || s[23] != '-'
354 ) {
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000355 goto bad_uuid;
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000356 }
357 for (i = 0; i < 16; i++) {
358 if (*s == '-')
359 s++;
360 if (!isxdigit(s[0]) || !isxdigit(s[1]))
361 goto bad_uuid;
362 uuid[i] = ((fromhex(s[0]) << 4) | fromhex(s[1]));
363 s += 2;
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000364 }
365 return get_spec_by_x(UUID, (char *)uuid, major, minor);
366
367 bad_uuid:
368 fprintf(stderr, _("mount: bad UUID"));
369 return 0;
370}
371
372static char *
373get_spec_by_volume_label(const char *s, int *major, int *minor)
374{
375 return get_spec_by_x(VOL, s, major, minor);
376}
377
378static int display_uuid_cache(void)
379{
380 struct uuidCache_s *u;
381 size_t i;
382
383 uuidcache_init();
384
385 u = uuidCache;
386 while (u) {
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000387 printf("%s %s %s\n", u->device, u->label, u->uc_uuid);
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000388 u = u->next;
389 }
390
391 return 0;
392}
393#endif // UNUSED
394
395
396/* Used by mount and findfs */
397
398char *get_devname_from_label(const char *spec)
399{
400 struct uuidCache_s *uc;
401 int spec_len = strlen(spec);
402
403 uuidcache_init();
404 uc = uuidCache;
405 while (uc) {
Denis Vlasenkoc5b73722008-03-17 09:21:26 +0000406// FIXME: empty label ("LABEL=") matches anything??!
407 if (uc->label[0] && strncmp(spec, uc->label, spec_len) == 0) {
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000408 return xstrdup(uc->device);
409 }
410 uc = uc->next;
411 }
412 return NULL;
413}
414
415char *get_devname_from_uuid(const char *spec)
416{
417 struct uuidCache_s *uc;
418
419 uuidcache_init();
420 uc = uuidCache;
421 while (uc) {
Denis Vlasenkod25c33f2008-03-17 09:25:05 +0000422 /* case of hex numbers doesn't matter */
423 if (strcasecmp(spec, uc->uc_uuid) == 0) {
Denis Vlasenkode7684a2008-02-18 21:08:49 +0000424 return xstrdup(uc->device);
425 }
426 uc = uc->next;
427 }
428 return NULL;
429}