volude_id: remove unused fields and functions which were setting them
[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 static struct uuidCache_s {
16         struct uuidCache_s *next;
17 //      int major, minor;
18         char *device;
19         char *label;
20         char *uc_uuid; /* prefix makes it easier to grep for */
21 } *uuidCache;
22
23 /* 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 "") */
26 #if !ENABLE_FEATURE_VOLUMEID_ISO9660
27 #define get_label_uuid(device, label, uuid, iso_only) \
28         get_label_uuid(device, label, uuid)
29 #endif
30 static int
31 get_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);
38         if (!vid)
39                 return rv;
40
41         if (ioctl(vid->fd, BLKGETSIZE64, &size) != 0)
42                 size = 0;
43
44 #if ENABLE_FEATURE_VOLUMEID_ISO9660
45         if ((iso_only ?
46              volume_id_probe_iso9660(vid, 0) :
47              volume_id_probe_all(vid, 0, size)
48             ) != 0
49         ) {
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' || vid->uuid[0] != '\0') {
59                 *label = xstrndup(vid->label, sizeof(vid->label));
60                 *uuid  = xstrndup(vid->uuid, sizeof(vid->uuid));
61                 dbg("found label '%s', uuid '%s' on %s", *label, *uuid, device);
62                 rv = 0;
63         }
64  ret:
65         free_volume_id(vid);
66         return rv;
67 }
68
69 /* NB: we take ownership of (malloc'ed) label and uuid */
70 static void
71 uuidcache_addentry(char *device, /*int major, int minor,*/ char *label, char *uuid)
72 {
73         struct uuidCache_s *last;
74     
75         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*/
84 //      last->major = major;
85 //      last->minor = minor;
86         last->device = device;
87         last->label = label;
88         last->uc_uuid = uuid;
89 }
90
91 /* 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. */
94 #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
98 static void
99 uuidcache_check_device(const char *device_name, int ma, int mi, int iso_only)
100 {
101         char *device, *last_slash;
102         char *uuid, *label;
103         char *ptr;
104         int must_remove = 0;
105         int added = 0;
106
107         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
116                 ptr = device;
117                 while (*ptr)
118                         if (*ptr++ == '/')
119                                 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 */
128                         } else {
129                                 *last_slash = '/';
130                         }
131                 }
132                 mknod(device, S_IFBLK | 0600, makedev(ma, mi));
133                 must_remove = 1;
134         }
135
136         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);
153 }
154
155 /* Run uuidcache_check_device() for every device mentioned
156  * in /proc/partitions */
157 static void
158 uuidcache_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
168         procpt = xfopen("/proc/partitions", "r");
169 /*
170 # cat /proc/partitions
171 major 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;
189                         chptr = skip_whitespace(chptr);
190
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 */
209                         dbg("/proc/partitions: maj:%d min:%d sz:%llu name:'%s'",
210                                                 ma, mi, sz, chptr);
211                         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
229 static void
230 dev_get_major_minor(char *device_name, int *major, int *minor)
231 {
232         char dev[16];
233         char *dev_path;
234         char *colon;
235         int sz;
236
237         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';
242
243         colon = strchr(dev, ':');
244         if (!colon)
245                 goto ret;
246         *major = strtol(dev, NULL, 10);
247         *minor = strtol(colon + 1, NULL, 10);
248
249  ret:
250         free(dev_path);
251         return;
252 }
253
254 static void
255 uuidcache_init_cdroms(void)
256 {
257 #define PROC_CDROMS "/proc/sys/dev/cdrom/info"
258         char line[100];
259         int ma, mi;
260         FILE *proccd;
261
262         proccd = fopen(PROC_CDROMS, "r");
263         if (!proccd) {
264                 static smallint warn = 0;
265                 if (!warn) {
266                         warn = 1;
267                         bb_error_msg("mount: could not open %s, so UUID and LABEL "
268                                 "conversion cannot be done for CD-Roms.",
269                                 PROC_CDROMS);
270                 }
271                 return;
272         }
273
274         while (fgets(line, sizeof(line), proccd)) {
275                 static const char drive_name_string[] ALIGN1 = "drive name:\t\t";
276
277                 if (strncmp(line, drive_name_string, sizeof(drive_name_string) - 1) == 0) {
278                         char *device_name;
279                         device_name = strtok(line + sizeof(drive_name_string) - 1, "\t\n");
280                         while (device_name) {
281                                 ma = mi = -1;
282                                 dev_get_major_minor(device_name, &ma, &mi);
283                                 uuidcache_check_device(device_name, ma, mi, 1);
284                                 device_name = strtok(NULL, "\t\n");
285                         }
286                         break;
287                 }
288         }
289
290         fclose(proccd);
291 }
292
293 static void
294 uuidcache_init(void)
295 {
296         if (uuidCache)
297                 return;
298
299         uuidcache_init_partitions();
300         uuidcache_init_cdroms();
301 }
302
303 #define UUID   1
304 #define VOL    2
305
306 #ifdef UNUSED
307 static char *
308 get_spec_by_x(int n, const char *t, int *majorPtr, int *minorPtr)
309 {
310         struct uuidCache_s *uc;
311
312         uuidcache_init();
313         uc = uuidCache;
314
315         while (uc) {
316                 switch (n) {
317                 case UUID:
318                         if (strcmp(t, uc->uc_uuid) == 0) {
319                                 *majorPtr = uc->major;
320                                 *minorPtr = uc->minor;
321                                 return uc->device;
322                         }
323                         break;
324                 case VOL:
325                         if (strcmp(t, uc->label) == 0) {
326                                 *majorPtr = uc->major;
327                                 *minorPtr = uc->minor;
328                                 return uc->device;
329                         }
330                         break;
331                 }
332                 uc = uc->next;
333         }
334         return NULL;
335 }
336
337 static unsigned char
338 fromhex(char c)
339 {
340         if (isdigit(c))
341                 return (c - '0');
342         return ((c|0x20) - 'a' + 10);
343 }
344
345 static char *
346 get_spec_by_uuid(const char *s, int *major, int *minor)
347 {
348         unsigned char uuid[16];
349         int i;
350
351         if (strlen(s) != 36 || s[8] != '-' || s[13] != '-'
352          || s[18] != '-' || s[23] != '-'
353         ) {
354                 goto bad_uuid;
355         }
356         for (i = 0; i < 16; i++) {
357                 if (*s == '-')
358                         s++;
359                 if (!isxdigit(s[0]) || !isxdigit(s[1]))
360                         goto bad_uuid;
361                 uuid[i] = ((fromhex(s[0]) << 4) | fromhex(s[1]));
362                 s += 2;
363         }
364         return get_spec_by_x(UUID, (char *)uuid, major, minor);
365
366  bad_uuid:
367         fprintf(stderr, _("mount: bad UUID"));
368         return 0;
369 }
370
371 static char *
372 get_spec_by_volume_label(const char *s, int *major, int *minor)
373 {
374         return get_spec_by_x(VOL, s, major, minor);
375 }
376
377 static int display_uuid_cache(void)
378 {
379         struct uuidCache_s *u;
380         size_t i;
381
382         uuidcache_init();
383
384         u = uuidCache;
385         while (u) {
386                 printf("%s %s %s\n", u->device, u->label, u->uc_uuid);
387                 u = u->next;
388         }
389
390         return 0;
391 }
392 #endif // UNUSED
393
394
395 /* Used by mount and findfs */
396
397 char *get_devname_from_label(const char *spec)
398 {
399         struct uuidCache_s *uc;
400         int spec_len = strlen(spec);
401
402         uuidcache_init();
403         uc = uuidCache;
404         while (uc) {
405 // FIXME: empty label ("LABEL=") matches anything??!
406                 if (uc->label[0] && strncmp(spec, uc->label, spec_len) == 0) {
407                         return xstrdup(uc->device);
408                 }
409                 uc = uc->next;
410         }
411         return NULL;
412 }
413
414 char *get_devname_from_uuid(const char *spec)
415 {
416         struct uuidCache_s *uc;
417
418         uuidcache_init();
419         uc = uuidCache;
420         while (uc) {
421                 if (strcmp(spec, uc->uc_uuid) == 0) {
422                         return xstrdup(uc->device);
423                 }
424                 uc = uc->next;
425         }
426         return NULL;
427 }