75060b0d87266004be1eb14324c771d32d9e89c3
[platform/upstream/busybox.git] / util-linux / volume_id / get_devname.c
1 /* 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
20 static 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 */
29 static int
30 get_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
65 static void
66 uuidcache_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
86 static void
87 uuidcache_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
128 static void
129 uuidcache_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
142 major 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
198 static int
199 dev_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
225 static void
226 uuidcache_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
261 static void
262 uuidcache_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
275 static char *
276 get_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
305 static unsigned char
306 fromhex(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
315 static char *
316 get_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
338 static char *
339 get_spec_by_volume_label(const char *s, int *major, int *minor)
340 {
341         return get_spec_by_x(VOL, s, major, minor);
342 }
343
344 static 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
370 char *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
386 char *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 }