blob: 75060b0d87266004be1eb14324c771d32d9e89c3 [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
13#define BLKGETSIZE64 _IOR(0x12,114,size_t)
14
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"
19
20static struct uuidCache_s {
21 struct uuidCache_s *next;
22 char uuid[16];
23 char *device;
24 char *label;
25 int major, minor;
26} *uuidCache;
27
28/* for now, only ext2, ext3 and xfs are supported */
29static int
30get_label_uuid(const char *device, char **label, char **uuid, int iso_only)
31{
32 int rv = 1;
33 uint64_t size;
34 struct volume_id *vid;
35
36 vid = volume_id_open_node(device);
37
38 if (ioctl(vid->fd, BLKGETSIZE64, &size) != 0) {
39 size = 0;
40 }
41
42#if ENABLE_FEATURE_VOLUMEID_ISO9660
43 if (iso_only ?
44 volume_id_probe_iso9660(vid, 0) != 0 :
45 volume_id_probe_all(vid, 0, size) != 0) {
46 goto ret;
47 }
48#else
49 if (volume_id_probe_all(vid, 0, size) != 0) {
50 goto ret;
51 }
52#endif
53
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);
58 rv = 0;
59 }
60 ret:
61 free_volume_id(vid);
62 return rv;
63}
64
65static void
66uuidcache_addentry(char * device, int major, int minor, char *label, char *uuid)
67{
68 struct uuidCache_s *last;
69
70 if (!uuidCache) {
71 last = uuidCache = xzalloc(sizeof(*uuidCache));
72 } else {
73 for (last = uuidCache; last->next; last = last->next)
74 continue;
75 last->next = xzalloc(sizeof(*uuidCache));
76 last = last->next;
77 }
78 /*last->next = NULL; - xzalloc did it*/
79 last->label = label;
80 last->device = device;
81 last->major = major;
82 last->minor = minor;
83 memcpy(last->uuid, uuid, sizeof(last->uuid));
84}
85
86static void
87uuidcache_check_device(const char *device_name, int ma, int mi, int iso_only)
88{
89 char device[110];
90 char *uuid = NULL, *label = NULL;
91 char *ptr;
92 char *deviceDir = NULL;
93 int mustRemove = 0;
94 int mustRemoveDir = 0;
95 int i;
96
97 sprintf(device, "%s/%s", DEVLABELDIR, device_name);
98 if (access(device, F_OK)) {
99 ptr = device;
100 i = 0;
101 while (*ptr)
102 if (*ptr++ == '/')
103 i++;
104 if (i > 2) {
105 deviceDir = alloca(strlen(device) + 1);
106 strcpy(deviceDir, device);
107 ptr = deviceDir + (strlen(device) - 1);
108 while (*ptr != '/')
109 *ptr-- = '\0';
110 if (mkdir(deviceDir, 0644)) {
111 printf("mkdir: cannot create directory %s: %d\n", deviceDir, errno);
112 } else {
113 mustRemoveDir = 1;
114 }
115 }
116
117 mknod(device, S_IFBLK | 0600, makedev(ma, mi));
118 mustRemove = 1;
119 }
120 if (!get_label_uuid(device, &label, &uuid, iso_only))
121 uuidcache_addentry(strdup(device), ma, mi,
122 label, uuid);
123
124 if (mustRemove) unlink(device);
125 if (mustRemoveDir) rmdir(deviceDir);
126}
127
128static void
129uuidcache_init_partitions(void)
130{
131 char line[100];
132 int ma, mi;
133 unsigned long long sz;
134 FILE *procpt;
135 int firstPass;
136 int handleOnFirst;
137 char *chptr;
138
139 procpt = xfopen(PROC_PARTITIONS, "r");
140/*
141# cat /proc/partitions
142major minor #blocks name
143
144 8 0 293036184 sda
145 8 1 6835626 sda1
146 8 2 1 sda2
147 8 5 979933 sda5
148 8 6 15623181 sda6
149 8 7 97659103 sda7
150 8 8 171935631 sda8
151*/
152 for (firstPass = 1; firstPass >= 0; firstPass--) {
153 fseek(procpt, 0, SEEK_SET);
154
155 while (fgets(line, sizeof(line), procpt)) {
156 /* The original version of this code used sscanf, but
157 diet's sscanf is quite limited */
158 chptr = line;
159 if (*chptr != ' ') continue;
160 chptr++;
161
162 ma = bb_strtou(chptr, &chptr, 0);
163 if (ma < 0) continue;
164 chptr = skip_whitespace(chptr);
165
166 mi = bb_strtou(chptr, &chptr, 0);
167 if (mi < 0) continue;
168 chptr = skip_whitespace(chptr);
169
170 sz = bb_strtoull(chptr, &chptr, 0);
171 if ((long long)sz == -1LL) continue;
172 chptr = skip_whitespace(chptr);
173
174 /* skip extended partitions (heuristic: size 1) */
175 if (sz == 1)
176 continue;
177
178 *strchrnul(chptr, '\n') = '\0';
179 /* now chptr => device name */
180 if (!chptr[0])
181 continue;
182
183 /* look only at md devices on first pass */
184 handleOnFirst = (chptr[0] == 'm' && chptr[1] == 'd');
185 if (firstPass != handleOnFirst)
186 continue;
187
188 /* heuristic: partition name ends in a digit */
189 if (isdigit(chptr[strlen(chptr) - 1])) {
190 uuidcache_check_device(chptr, ma, mi, 0);
191 }
192 }
193 }
194
195 fclose(procpt);
196}
197
198static int
199dev_get_major_minor(char *device_name, int *major, int *minor)
200{
201 char * dev_path;
202 int fd;
203 char dev[7];
204 char *major_ptr, *minor_ptr;
205
206 dev_path = alloca(strlen(SYS_BLOCK) + strlen(device_name) + 6);
207 sprintf(dev_path, "%s/%s/dev", SYS_BLOCK, device_name);
208
209 fd = open(dev_path, O_RDONLY);
210 if (fd < 0) return 1;
211 full_read(fd, dev, sizeof(dev));
212 close(fd);
213
214 major_ptr = dev;
215 minor_ptr = strchr(dev, ':');
216 if (!minor_ptr) return 1;
217 *minor_ptr++ = '\0';
218
219 *major = strtol(major_ptr, NULL, 10);
220 *minor = strtol(minor_ptr, NULL, 10);
221
222 return 0;
223}
224
225static void
226uuidcache_init_cdroms(void)
227{
228 char line[100];
229 int ma, mi;
230 FILE *proccd;
231
232 proccd = fopen(PROC_CDROMS, "r");
233 if (!proccd) {
234 static smallint warn = 0;
235 if (!warn) {
236 warn = 1;
237 bb_error_msg("mount: could not open %s, so UUID and LABEL "
238 "conversion cannot be done for CD-Roms.",
239 PROC_CDROMS);
240 }
241 return;
242 }
243
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))) {
247 char *device_name;
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");
253 }
254 break;
255 }
256 }
257
258 fclose(proccd);
259}
260
261static void
262uuidcache_init(void)
263{
264 if (uuidCache)
265 return;
266
267 uuidcache_init_partitions();
268 uuidcache_init_cdroms();
269}
270
271#define UUID 1
272#define VOL 2
273
274#ifdef UNUSED
275static char *
276get_spec_by_x(int n, const char *t, int * majorPtr, int * minorPtr)
277{
278 struct uuidCache_s *uc;
279
280 uuidcache_init();
281 uc = uuidCache;
282
283 while(uc) {
284 switch (n) {
285 case UUID:
286 if (!memcmp(t, uc->uuid, sizeof(uc->uuid))) {
287 *majorPtr = uc->major;
288 *minorPtr = uc->minor;
289 return uc->device;
290 }
291 break;
292 case VOL:
293 if (!strcmp(t, uc->label)) {
294 *majorPtr = uc->major;
295 *minorPtr = uc->minor;
296 return uc->device;
297 }
298 break;
299 }
300 uc = uc->next;
301 }
302 return NULL;
303}
304
305static unsigned char
306fromhex(char c)
307{
308 if (isdigit(c))
309 return (c - '0');
310 if (islower(c))
311 return (c - 'a' + 10);
312 return (c - 'A' + 10);
313}
314
315static char *
316get_spec_by_uuid(const char *s, int * major, int * minor)
317{
318 unsigned char uuid[16];
319 int i;
320
321 if (strlen(s) != 36 ||
322 s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-')
323 goto bad_uuid;
324 for (i=0; i<16; i++) {
325 if (*s == '-') s++;
326 if (!isxdigit(s[0]) || !isxdigit(s[1]))
327 goto bad_uuid;
328 uuid[i] = ((fromhex(s[0])<<4) | fromhex(s[1]));
329 s += 2;
330 }
331 return get_spec_by_x(UUID, (char *)uuid, major, minor);
332
333 bad_uuid:
334 fprintf(stderr, _("mount: bad UUID"));
335 return 0;
336}
337
338static char *
339get_spec_by_volume_label(const char *s, int *major, int *minor)
340{
341 return get_spec_by_x(VOL, s, major, minor);
342}
343
344static int display_uuid_cache(void)
345{
346 struct uuidCache_s *u;
347 size_t i;
348
349 uuidcache_init();
350
351 u = uuidCache;
352 while (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)
356 printf("-");
357 printf("%x", u->uuid[i] & 0xff);
358 }
359 printf("\n");
360 u = u->next;
361 }
362
363 return 0;
364}
365#endif // UNUSED
366
367
368/* Used by mount and findfs */
369
370char *get_devname_from_label(const char *spec)
371{
372 struct uuidCache_s *uc;
373 int spec_len = strlen(spec);
374
375 uuidcache_init();
376 uc = uuidCache;
377 while (uc) {
378 if (uc->label && !strncmp(spec, uc->label, spec_len)) {
379 return xstrdup(uc->device);
380 }
381 uc = uc->next;
382 }
383 return NULL;
384}
385
386char *get_devname_from_uuid(const char *spec)
387{
388 struct uuidCache_s *uc;
389
390 uuidcache_init();
391 uc = uuidCache;
392 while (uc) {
393 if (!memcmp(spec, uc->uuid, sizeof(uc->uuid))) {
394 return xstrdup(uc->device);
395 }
396 uc = uc->next;
397 }
398 return NULL;
399}