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