Whamcloud - gitweb
Fixed up minor documentation issues (man page for badblocks and usage
[tools/e2fsprogs.git] / misc / get_device_by_label.c
1 /*
2  * get_device_by_label.h
3  *
4  * Copyright 1999 by Andries Brouwer
5  * Copyright 1999, 2000 by Theodore Ts'o
6  *
7  * This file may be redistributed under the terms of the GNU Public
8  * License.
9  *
10  * Taken from aeb's mount, 990619
11  * Updated from aeb's mount, 20000725
12  * Added call to ext2fs_find_block_device, so that we can find devices
13  *      even if devfs (ugh) is compiled in, but not mounted, since
14  *      this messes up /proc/partitions, by TYT.
15  */
16
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <ctype.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #ifdef HAVE_SYS_MKDEV_H
26 #include <sys/mkdev.h>
27 #endif
28 #ifdef HAVE_SYS_SYSMACROS_H
29 #include <sys/sysmacros.h>
30 #endif
31 #include <dirent.h>
32 #include "nls-enable.h"
33 #include "fsck.h"
34 #include "get_device_by_label.h"
35
36 /* function prototype from libext2 */
37 extern char *ext2fs_find_block_device(dev_t device);
38
39 #define PROC_PARTITIONS "/proc/partitions"
40 #define PROC_EVMS_VOLUMES "/proc/evms/volumes"
41 #define DEVLABELDIR     "/dev"
42 #define VG_DIR          "/proc/lvm/VGs"
43
44 #define EXT2_SUPER_MAGIC    0xEF53
45 struct ext2_super_block {
46         unsigned char   s_dummy1[56];
47         unsigned char   s_magic[2];
48         unsigned char   s_dummy2[46];
49         unsigned char   s_uuid[16];
50         unsigned char   s_volume_name[16];
51 };
52 #define ext2magic(s)    ((unsigned int) s.s_magic[0] + (((unsigned int) s.s_magic[1]) << 8))
53
54 #define XFS_SUPER_MAGIC "XFSB"
55 struct xfs_super_block {
56         unsigned char   s_magic[4];
57         unsigned char   s_dummy[28];
58         unsigned char   s_uuid[16];
59         unsigned char   s_dummy2[60];
60         unsigned char   s_fname[12];
61 };
62
63 struct reiserfs_super_block
64 {
65         /* Following entries are based on reiserfsutils 3.6.3 
66          * (Copyright Hans Reiser) since Linux kernel headers
67          * (2.4.18) seemed not up-to-date. */
68         unsigned char   s_dummy1[52];
69         unsigned char   s_magic[10];
70         unsigned char   s_dummy2[10];
71         unsigned char   s_version[2];
72         unsigned char   s_dummy3[10];
73         unsigned char   s_uuid[16];
74         unsigned char   s_label[16];
75         unsigned char   s_unused[88];
76 };
77
78 #define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" /* v. 3.6 */
79 #define REISER3FS_SUPER_MAGIC_STRING "ReIsEr3Fs" /* Journal Relocation */
80 #define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024)
81 /* the spot for the super in versions 3.5 - 3.5.10 (inclusive) - 
82  * We'll use it in case volume has been converted. */
83 #define REISERFS_OLD_DISK_OFFSET_IN_BYTES (8 * 1024)
84 #define reiserversion(s)        ((unsigned) (s).s_version[0] + (((unsigned) (s).s_version[1]) << 8))
85
86 /* We're checking for ReiserFS v. 3.6 and RJ 3.6 SB */
87 static int
88 reiser_supports_uuid (struct reiserfs_super_block *sb)
89 {
90         return (strncmp(sb->s_magic, REISER2FS_SUPER_MAGIC_STRING,
91                             strlen (REISER2FS_SUPER_MAGIC_STRING)) == 0)
92                 || (strncmp(sb->s_magic, REISER3FS_SUPER_MAGIC_STRING,
93                             strlen (REISER3FS_SUPER_MAGIC_STRING)) == 0
94                                 && reiserversion(*sb) == 2);
95 }
96
97 static struct uuidCache_s {
98         struct uuidCache_s *next;
99         char uuid[16];
100         char *label;
101         char *device;
102 } *uuidCache = NULL;
103
104 char *string_copy(const char *s)
105 {
106         char    *ret;
107
108         ret = malloc(strlen(s)+1);
109         if (ret)
110                 strcpy(ret, s);
111         return ret;
112 }
113
114 /* for now, only ext2, ext3, xfs and ReiserFS are supported */
115 static int
116 get_label_uuid(const char *device, char **label, char *uuid) {
117
118         /* start with ext2/3, xfs and ReiserFS tests, taken from mount_guess_fstype */
119         /* should merge these later */
120         int fd;
121         size_t label_size;
122         unsigned char *sb_uuid = 0, *sb_label = 0;
123         struct ext2_super_block e2sb;
124         struct xfs_super_block xfsb;
125         struct reiserfs_super_block rfsb;
126
127         fd = open(device, O_RDONLY);
128         if (fd < 0)
129                 return 1;
130
131         if (lseek(fd, 1024, SEEK_SET) == 1024
132             && read(fd, (char *) &e2sb, sizeof(e2sb)) == sizeof(e2sb)
133             && (ext2magic(e2sb) == EXT2_SUPER_MAGIC)) {
134                 sb_uuid = e2sb.s_uuid;
135                 sb_label = e2sb.s_volume_name;
136                 label_size = sizeof(e2sb.s_volume_name);
137         } else if (lseek(fd, 0, SEEK_SET) == 0
138             && read(fd, (char *) &xfsb, sizeof(xfsb)) == sizeof(xfsb)
139             && strncmp((char *) &xfsb.s_magic, XFS_SUPER_MAGIC, 4) == 0) {
140                 sb_uuid = xfsb.s_uuid;
141                 sb_label = xfsb.s_fname;
142                 label_size = sizeof(xfsb.s_fname);
143         } else if ((lseek(fd, REISERFS_OLD_DISK_OFFSET_IN_BYTES, SEEK_SET)
144                             == REISERFS_OLD_DISK_OFFSET_IN_BYTES
145             && read(fd, (char *) &rfsb, sizeof(rfsb)) == sizeof(rfsb)
146             && (reiser_supports_uuid(&rfsb)))
147             || (lseek(fd, REISERFS_DISK_OFFSET_IN_BYTES, SEEK_SET)
148                             == REISERFS_DISK_OFFSET_IN_BYTES
149             && read(fd, (char *) &rfsb, sizeof(rfsb)) == sizeof(rfsb)
150             && (reiser_supports_uuid(&rfsb)))) {
151                 sb_uuid = rfsb.s_uuid;
152                 sb_label = rfsb.s_label;
153                 label_size = sizeof(rfsb.s_label);
154         } else {
155                 close(fd);
156                 return 1;
157         }
158
159         close(fd);
160         if (sb_uuid)
161                 memcpy(uuid, sb_uuid, sizeof(e2sb.s_uuid));
162         if (sb_label) {
163                 if ((*label = calloc(label_size + 1, 1)) != NULL)
164                         memcpy(*label, sb_label, label_size);
165         }
166         return 0;
167 }
168
169 #define CBBUF  (16 * 1024)
170
171 static void
172 uuidcache_addentry(char *device, char *label, char *uuid) {
173         struct uuidCache_s *last;
174
175         if (!uuidCache) {
176                 last = uuidCache = malloc(sizeof(*uuidCache));
177         } else {
178                 for (last = uuidCache; last->next; last = last->next) ;
179                 last->next = malloc(sizeof(*uuidCache));
180                 last = last->next;
181         }
182         last->next = NULL;
183         last->device = device;
184         last->label = label;
185         memcpy(last->uuid, uuid, sizeof(last->uuid));
186 }
187
188 /*
189  * This function initializes the UUID cache with devices from the LVM
190  * proc hierarchy.  We currently depend on the names of the LVM
191  * hierarchy giving us the device structure in /dev.  (XXX is this a
192  * safe thing to do?)
193  */
194 #ifdef VG_DIR
195 static void init_lvm(void)
196 {
197         DIR             *vg_dir, *lv_list;
198         char            *vdirname, *lvm_device;
199         char            uuid[16], *label, *vname, *lname;
200         struct dirent   *vg_iter, *lv_iter;
201         
202         if ((vg_dir = opendir(VG_DIR)) == NULL)
203                 return;
204
205         while ((vg_iter = readdir(vg_dir)) != 0) {
206                 vname = vg_iter->d_name;
207                 if (!strcmp(vname, ".") || !strcmp(vname, ".."))
208                         continue;
209                 vdirname = malloc(strlen(VG_DIR)+strlen(vname)+8);
210                 if (!vdirname) {
211                         closedir(vg_dir);
212                         return;
213                 }
214                 sprintf(vdirname, "%s/%s/LVs", VG_DIR, vname);
215
216                 lv_list = opendir(vdirname);
217                 free(vdirname);
218                 if (lv_list == NULL)
219                         return;
220
221                 while ((lv_iter = readdir(lv_list)) != 0) {
222                         lname = lv_iter->d_name;
223                         if (!strcmp(lname, ".") || !strcmp(lname, ".."))
224                                 continue;
225
226                         lvm_device = malloc(strlen(DEVLABELDIR) +
227                                             strlen(vname)+
228                                             strlen(lname)+8);
229                         if (!lvm_device) {
230                                 closedir(lv_list);
231                                 closedir(vg_dir);
232                                 return;
233                         }
234                         sprintf(lvm_device, "%s/%s/%s", DEVLABELDIR,
235                                 vname, lname);
236                         if (!get_label_uuid(lvm_device, &label, uuid)) {
237                                 uuidcache_addentry(string_copy(lvm_device),
238                                                    label, uuid);
239                         } else
240                                 free(lvm_device);
241                 }
242                 closedir(lv_list);
243         }
244         closedir( vg_dir );
245 }
246 #endif
247
248 static void
249 read_partitions(void)
250 {
251         char line[100];
252         char *s;
253         int ma, mi, sz;
254         static char ptname[100];
255         FILE *procpt;
256         char uuid[16], *label, *devname;
257         char device[110];
258         dev_t   dev;
259         struct stat statbuf;
260         int firstPass;
261         int handleOnFirst;
262         char *iobuf;
263
264         procpt = fopen(PROC_PARTITIONS, "r");
265         if (!procpt)
266                 return;
267
268         iobuf = (char *)malloc(CBBUF);
269         if (iobuf)
270                 setvbuf(procpt, iobuf, _IOFBF, CBBUF);
271
272         for (firstPass = 1; firstPass >= 0; firstPass--) {
273             fseek(procpt, 0, SEEK_SET);
274
275             while (fgets(line, sizeof(line), procpt)) {
276                 if (sscanf (line, " %d %d %d %[^\n ]",
277                             &ma, &mi, &sz, ptname) != 4)
278                         continue;
279
280                 /* skip extended partitions (heuristic: size 1) */
281                 if (sz == 1)
282                         continue;
283
284                 /* look only at md devices on first pass */
285                 handleOnFirst = !strncmp(ptname, "md", 2);
286                 if (firstPass != handleOnFirst)
287                         continue;
288
289                 /* skip entire disk (minor 0, 64, ... on ide;
290                    0, 16, ... on sd) */
291                 /* heuristic: partition name ends in a digit */
292                 /* OR partition name starts with 'lvm' */
293
294                 for(s = ptname; *s; s++);
295                 if (isdigit(s[-1]) || !strncmp(ptname, "lvm", 3)) {
296                         /*
297                          * We first look in /dev for the device, but
298                          * if we don't find it, or if the stat
299                          * information doesn't check out, we use
300                          * ext2fs_find_block_device to find it.
301                          */
302                         sprintf(device, "%s/%s", DEVLABELDIR, ptname);
303                         dev = makedev(ma, mi);
304                         if ((stat(device, &statbuf) < 0) ||
305                             (statbuf.st_rdev != dev)) {
306                                 devname = ext2fs_find_block_device(dev);
307                         } else
308                                 devname = string_copy(device);
309                         if (!devname)
310                                 continue;
311 #ifdef DEBUG
312                         printf("Checking partition %s (%d, %d)\n",
313                                devname, ma, mi);
314 #endif
315                         if (!get_label_uuid(devname, &label, uuid))
316                                 uuidcache_addentry(devname, label, uuid);
317                         else
318                                 free(devname);
319                 }
320             }
321         }
322
323         fclose(procpt);
324         if (iobuf)
325                 free(iobuf);
326 }
327
328 static void
329 read_evms(void)
330 {
331         char line[100];
332         int ma, mi, sz;
333         FILE *procpt;
334         char uuid[16], *label, *devname;
335         char device[110];
336         dev_t   dev;
337         struct stat statbuf;
338
339         procpt = fopen(PROC_EVMS_VOLUMES, "r");
340         if (!procpt)
341                 return;
342         while (fgets(line, sizeof(line), procpt)) {
343                 if (sscanf (line, " %d %d %d %*s %*s %[^\n ]",
344                             &ma, &mi, &sz, device) != 4)
345                         continue;
346
347                 /*
348                  * We first look for the device in the named location,
349                  * but if we don't find it, or if the stat information
350                  * doesn't check out, we use ext2fs_find_block_device
351                  * to find it.
352                  */
353                 dev = makedev(ma, mi);
354                 if ((stat(device, &statbuf) < 0) || (statbuf.st_rdev != dev)) {
355                         devname = ext2fs_find_block_device(dev);
356                 } else
357                         devname = string_copy(device);
358                 if (!devname)
359                         continue;
360 #ifdef DEBUG
361                 printf("Checking partition %s (%d, %d)\n",
362                        devname, ma, mi);
363 #endif
364                 if (!get_label_uuid(devname, &label, uuid))
365                         uuidcache_addentry(devname, label, uuid);
366                 else
367                         free(devname);
368         }
369         fclose(procpt);
370 }
371
372 static void
373 uuidcache_init(void)
374 {
375         if (uuidCache)
376                 return;
377
378 #ifdef VG_DIR
379         init_lvm();
380 #endif
381         read_evms();
382         read_partitions();
383 }
384
385 #define UUID   1
386 #define VOL    2
387
388 static char *
389 get_spec_by_x(int n, const char *t) {
390         struct uuidCache_s *uc;
391
392         uuidcache_init();
393         uc = uuidCache;
394
395         if (t == NULL)
396                 return NULL;
397
398         while(uc) {
399                 switch (n) {
400                 case UUID:
401                         if (!memcmp(t, uc->uuid, sizeof(uc->uuid)))
402                                 return string_copy(uc->device);
403                         break;
404                 case VOL:
405                         if (!strcmp(t, uc->label))
406                                 return string_copy(uc->device);
407                         break;
408                 }
409                 uc = uc->next;
410         }
411         return NULL;
412 }
413
414 static char fromhex(char c)
415 {
416         if (isdigit(c))
417                 return (c - '0');
418         else if (islower(c))
419                 return (c - 'a' + 10);
420         else
421                 return (c - 'A' + 10);
422 }
423
424 char *
425 get_spec_by_uuid(const char *s)
426 {
427         char uuid[16];
428         int i;
429
430         if (strlen(s) != 36 ||
431             s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-')
432                 goto bad_uuid;
433         for (i=0; i<16; i++) {
434             if (*s == '-') s++;
435             if (!isxdigit(s[0]) || !isxdigit(s[1]))
436                     goto bad_uuid;
437             uuid[i] = ((fromhex(s[0])<<4) | fromhex(s[1]));
438             s += 2;
439         }
440         return get_spec_by_x(UUID, uuid);
441
442  bad_uuid:
443         fprintf(stderr, _("WARNING: %s: bad UUID\n"), s);
444         return NULL;
445 }
446
447 char *
448 get_spec_by_volume_label(const char *s) {
449         return get_spec_by_x(VOL, s);
450 }
451
452 const char *
453 get_volume_label_by_spec(const char *spec) {
454         struct uuidCache_s *uc;
455
456         uuidcache_init();
457         uc = uuidCache;
458
459         while(uc) {
460                 if (!strcmp(spec, uc->device))
461                         return uc->label;
462                 uc = uc->next;
463         }
464         return NULL;
465 }
466
467 /*
468  * Interpret the device name if necessary.
469  * Frees the pointer passed to it if we return a different device string.
470  */
471 char *interpret_spec(char *spec)
472 {
473         char *dev = NULL;
474
475         if (!spec)
476                 return NULL;
477
478         if (!strncmp(spec, "UUID=", 5))
479                 dev = get_spec_by_uuid(spec+5);
480         else if (!strncmp(spec, "LABEL=", 6))
481                 dev = get_spec_by_volume_label(spec+6);
482         else
483                 dev = string_copy(spec);
484         return dev;
485 }
486
487 #ifdef DEBUG
488 main(int argc, char **argv)
489 {
490         uuidcache_init();
491 }
492 #endif