7c9930543782c5916e8e3abd9626e4418a301029
[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 source tree.
9  */
10 #include <sys/mount.h> /* BLKGETSIZE64 */
11 #if !defined(BLKGETSIZE64)
12 # define BLKGETSIZE64 _IOR(0x12,114,size_t)
13 #endif
14 #include "volume_id_internal.h"
15
16 static struct uuidCache_s {
17         struct uuidCache_s *next;
18 //      int major, minor;
19         char *device;
20         char *label;
21         char *uc_uuid; /* prefix makes it easier to grep for */
22         IF_FEATURE_BLKID_TYPE(const char *type;)
23 } *uuidCache;
24
25 #if !ENABLE_FEATURE_BLKID_TYPE
26 #define get_label_uuid(fd, label, uuid, type) \
27         get_label_uuid(fd, label, uuid)
28 #define uuidcache_addentry(device, label, uuid, type) \
29         uuidcache_addentry(device, label, uuid)
30 #endif
31
32 /* Returns !0 on error.
33  * Otherwise, returns malloc'ed strings for label and uuid
34  * (and they can't be NULL, although they can be "").
35  * NB: closes fd. */
36 static int
37 get_label_uuid(int fd, char **label, char **uuid, const char **type)
38 {
39         int rv = 1;
40         uint64_t size;
41         struct volume_id *vid;
42
43         /* fd is owned by vid now */
44         vid = volume_id_open_node(fd);
45
46         if (ioctl(/*vid->*/fd, BLKGETSIZE64, &size) != 0)
47                 size = 0;
48
49         if (volume_id_probe_all(vid, /*0,*/ size) != 0)
50                 goto ret;
51
52         if (vid->label[0] != '\0' || vid->uuid[0] != '\0') {
53                 *label = xstrndup(vid->label, sizeof(vid->label));
54                 *uuid  = xstrndup(vid->uuid, sizeof(vid->uuid));
55 #if ENABLE_FEATURE_BLKID_TYPE
56                 *type = vid->type;
57                 dbg("found label '%s', uuid '%s', type '%s'", *label, *uuid, *type);
58 #else
59                 dbg("found label '%s', uuid '%s'", *label, *uuid);
60 #endif
61                 rv = 0;
62         }
63  ret:
64         free_volume_id(vid); /* also closes fd */
65         return rv;
66 }
67
68 /* NB: we take ownership of (malloc'ed) label and uuid */
69 static void
70 uuidcache_addentry(char *device, /*int major, int minor,*/ char *label, char *uuid, const char *type)
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->major = major;
84 //      last->minor = minor;
85         last->device = device;
86         last->label = label;
87         last->uc_uuid = uuid;
88         IF_FEATURE_BLKID_TYPE(last->type = type;)
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 static int FAST_FUNC
95 uuidcache_check_device(const char *device,
96                 struct stat *statbuf,
97                 void *userData UNUSED_PARAM,
98                 int depth UNUSED_PARAM)
99 {
100         /* note: this check rejects links to devices, among other nodes */
101         if (!S_ISBLK(statbuf->st_mode))
102                 return TRUE;
103
104         /* Users report that mucking with floppies (especially non-present
105          * ones) is significant PITA. This is a horribly dirty hack,
106          * but it is very useful in real world.
107          * If this will ever need to be enabled, consider using O_NONBLOCK.
108          */
109         if (major(statbuf->st_rdev) == 2)
110                 return TRUE;
111
112         add_to_uuid_cache(device);
113
114         return TRUE;
115 }
116
117 static void
118 uuidcache_init(void)
119 {
120         dbg("DBG: uuidCache=%x, uuidCache");
121         if (uuidCache)
122                 return;
123
124         /* We were scanning /proc/partitions
125          * and /proc/sys/dev/cdrom/info here.
126          * Missed volume managers. I see that "standard" blkid uses these:
127          * /dev/mapper/control
128          * /proc/devices
129          * /proc/evms/volumes
130          * /proc/lvm/VGs
131          * This is unacceptably complex. Let's just scan /dev.
132          * (Maybe add scanning of /sys/block/XXX/dev for devices
133          * somehow not having their /dev/XXX entries created?) */
134
135         recursive_action("/dev", ACTION_RECURSE,
136                 uuidcache_check_device, /* file_action */
137                 NULL, /* dir_action */
138                 NULL, /* userData */
139                 0 /* depth */);
140 }
141
142 #define UUID   1
143 #define VOL    2
144
145 #ifdef UNUSED
146 static char *
147 get_spec_by_x(int n, const char *t, int *majorPtr, int *minorPtr)
148 {
149         struct uuidCache_s *uc;
150
151         uuidcache_init();
152         uc = uuidCache;
153
154         while (uc) {
155                 switch (n) {
156                 case UUID:
157                         if (strcmp(t, uc->uc_uuid) == 0) {
158                                 *majorPtr = uc->major;
159                                 *minorPtr = uc->minor;
160                                 return uc->device;
161                         }
162                         break;
163                 case VOL:
164                         if (strcmp(t, uc->label) == 0) {
165                                 *majorPtr = uc->major;
166                                 *minorPtr = uc->minor;
167                                 return uc->device;
168                         }
169                         break;
170                 }
171                 uc = uc->next;
172         }
173         return NULL;
174 }
175
176 static unsigned char
177 fromhex(char c)
178 {
179         if (isdigit(c))
180                 return (c - '0');
181         return ((c|0x20) - 'a' + 10);
182 }
183
184 static char *
185 get_spec_by_uuid(const char *s, int *major, int *minor)
186 {
187         unsigned char uuid[16];
188         int i;
189
190         if (strlen(s) != 36 || s[8] != '-' || s[13] != '-'
191          || s[18] != '-' || s[23] != '-'
192         ) {
193                 goto bad_uuid;
194         }
195         for (i = 0; i < 16; i++) {
196                 if (*s == '-')
197                         s++;
198                 if (!isxdigit(s[0]) || !isxdigit(s[1]))
199                         goto bad_uuid;
200                 uuid[i] = ((fromhex(s[0]) << 4) | fromhex(s[1]));
201                 s += 2;
202         }
203         return get_spec_by_x(UUID, (char *)uuid, major, minor);
204
205  bad_uuid:
206         fprintf(stderr, _("mount: bad UUID"));
207         return 0;
208 }
209
210 static char *
211 get_spec_by_volume_label(const char *s, int *major, int *minor)
212 {
213         return get_spec_by_x(VOL, s, major, minor);
214 }
215 #endif // UNUSED
216
217 /* Used by blkid */
218 void display_uuid_cache(void)
219 {
220         struct uuidCache_s *u;
221
222         uuidcache_init();
223         u = uuidCache;
224         while (u) {
225                 printf("%s:", u->device);
226                 if (u->label[0])
227                         printf(" LABEL=\"%s\"", u->label);
228                 if (u->uc_uuid[0])
229                         printf(" UUID=\"%s\"", u->uc_uuid);
230 #if ENABLE_FEATURE_BLKID_TYPE
231         if (u->type)
232                 printf(" TYPE=\"%s\"", u->type);
233 #endif
234                 bb_putchar('\n');
235                 u = u->next;
236         }
237 }
238
239 int add_to_uuid_cache(const char *device)
240 {
241         char *uuid = uuid; /* for compiler */
242         char *label = label;
243 #if ENABLE_FEATURE_BLKID_TYPE
244         const char *type = type;
245 #endif
246         int fd;
247
248         fd = open(device, O_RDONLY);
249         if (fd < 0)
250                 return 0;
251
252         /* get_label_uuid() closes fd in all cases (success & failure) */
253         if (get_label_uuid(fd, &label, &uuid, &type) == 0) {
254                 /* uuidcache_addentry() takes ownership of all four params */
255                 uuidcache_addentry(xstrdup(device), /*ma, mi,*/ label, uuid, type);
256                 return 1;
257         }
258         return 0;
259 }
260
261
262 /* Used by mount and findfs */
263
264 char *get_devname_from_label(const char *spec)
265 {
266         struct uuidCache_s *uc;
267
268         uuidcache_init();
269         uc = uuidCache;
270         while (uc) {
271                 if (uc->label[0] && strcmp(spec, uc->label) == 0) {
272                         return xstrdup(uc->device);
273                 }
274                 uc = uc->next;
275         }
276         return NULL;
277 }
278
279 char *get_devname_from_uuid(const char *spec)
280 {
281         struct uuidCache_s *uc;
282
283         uuidcache_init();
284         uc = uuidCache;
285         while (uc) {
286                 /* case of hex numbers doesn't matter */
287                 if (strcasecmp(spec, uc->uc_uuid) == 0) {
288                         return xstrdup(uc->device);
289                 }
290                 uc = uc->next;
291         }
292         return NULL;
293 }
294
295 int resolve_mount_spec(char **fsname)
296 {
297         char *tmp = *fsname;
298
299         if (strncmp(*fsname, "UUID=", 5) == 0)
300                 tmp = get_devname_from_uuid(*fsname + 5);
301         else if (strncmp(*fsname, "LABEL=", 6) == 0)
302                 tmp = get_devname_from_label(*fsname + 6);
303
304         if (tmp == *fsname)
305                 return 0; /* no UUID= or LABEL= prefix found */
306
307         if (tmp)
308                 *fsname = tmp;
309         return 1;
310 }