blob: a80bfbcabc57a54ceb9ab43b9226a326da127021 [file] [log] [blame]
Theodore Ts'o2d8defd1999-07-03 01:59:42 +00001/*
2 * get_device_by_label.h
3 *
Theodore Ts'o556ccbd2000-08-20 19:11:05 +00004 * Copyright 1999 by Andries Brouwer
5 * Copyright 1999, 2000 by Theodore Ts'o
Theodore Ts'o2d8defd1999-07-03 01:59:42 +00006 *
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 *
10 * Taken from aeb's mount, 990619
Theodore Ts'o28412de2000-07-27 02:45:55 +000011 * Updated from aeb's mount, 20000725
Theodore Ts'o556ccbd2000-08-20 19:11:05 +000012 * 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.
Theodore Ts'o2d8defd1999-07-03 01:59:42 +000015 */
16
17#include <stdio.h>
18#include <string.h>
Theodore Ts'o556ccbd2000-08-20 19:11:05 +000019#include <stdlib.h>
Theodore Ts'o2d8defd1999-07-03 01:59:42 +000020#include <ctype.h>
21#include <fcntl.h>
22#include <unistd.h>
Theodore Ts'o556ccbd2000-08-20 19:11:05 +000023#include <sys/types.h>
24#include <sys/stat.h>
25#ifdef HAVE_SYS_MKDEV_H
26#include <sys/mkdev.h>
27#endif
Theodore Ts'o8820c792001-01-06 04:20:03 +000028#ifdef HAVE_SYS_SYSMACROS_H
29#include <sys/sysmacros.h>
30#endif
Theodore Ts'oeaf4dc52001-12-22 00:04:22 -050031#include <dirent.h>
Theodore Ts'od9c56d32000-02-08 00:47:55 +000032#include "nls-enable.h"
Theodore Ts'o8820c792001-01-06 04:20:03 +000033#include "fsck.h"
Theodore Ts'o7d4343d2002-02-12 02:34:44 -050034#include "get_device_by_label.h"
Theodore Ts'o2d8defd1999-07-03 01:59:42 +000035
Theodore Ts'o556ccbd2000-08-20 19:11:05 +000036/* function prototype from libext2 */
37extern char *ext2fs_find_block_device(dev_t device);
38
Theodore Ts'o2d8defd1999-07-03 01:59:42 +000039#define PROC_PARTITIONS "/proc/partitions"
Theodore Ts'o118d7da2002-08-17 23:01:22 -040040#define PROC_EVMS_VOLUMES "/proc/evms/volumes"
Theodore Ts'o2d8defd1999-07-03 01:59:42 +000041#define DEVLABELDIR "/dev"
Theodore Ts'oeaf4dc52001-12-22 00:04:22 -050042#define VG_DIR "/proc/lvm/VGs"
Theodore Ts'o2d8defd1999-07-03 01:59:42 +000043
44#define EXT2_SUPER_MAGIC 0xEF53
45struct ext2_super_block {
Theodore Ts'o36b01301999-10-26 14:38:36 +000046 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];
Theodore Ts'o2d8defd1999-07-03 01:59:42 +000051};
Theodore Ts'o36b01301999-10-26 14:38:36 +000052#define ext2magic(s) ((unsigned int) s.s_magic[0] + (((unsigned int) s.s_magic[1]) << 8))
Theodore Ts'o2d8defd1999-07-03 01:59:42 +000053
Theodore Ts'o21d14ec2001-07-03 21:43:07 -040054#define XFS_SUPER_MAGIC "XFSB"
55struct 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
Theodore Ts'odbfb3f02002-09-29 22:42:27 -040063struct 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 */
87static int
88reiser_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
Theodore Ts'o28412de2000-07-27 02:45:55 +000097static struct uuidCache_s {
98 struct uuidCache_s *next;
99 char uuid[16];
100 char *label;
101 char *device;
102} *uuidCache = NULL;
Theodore Ts'o2d8defd1999-07-03 01:59:42 +0000103
Andreas Dilger2d155762001-08-17 03:48:11 -0600104char *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
Theodore Ts'odbfb3f02002-09-29 22:42:27 -0400114/* for now, only ext2, ext3, xfs and ReiserFS are supported */
Theodore Ts'o2d8defd1999-07-03 01:59:42 +0000115static int
Theodore Ts'o28412de2000-07-27 02:45:55 +0000116get_label_uuid(const char *device, char **label, char *uuid) {
Theodore Ts'o2d8defd1999-07-03 01:59:42 +0000117
Theodore Ts'odbfb3f02002-09-29 22:42:27 -0400118 /* start with ext2/3, xfs and ReiserFS tests, taken from mount_guess_fstype */
Theodore Ts'o28412de2000-07-27 02:45:55 +0000119 /* should merge these later */
Theodore Ts'o2d8defd1999-07-03 01:59:42 +0000120 int fd;
Theodore Ts'o21d14ec2001-07-03 21:43:07 -0400121 size_t label_size;
Theodore Ts'o4ea7bd02001-12-16 23:23:37 -0500122 unsigned char *sb_uuid = 0, *sb_label = 0;
Theodore Ts'o2d8defd1999-07-03 01:59:42 +0000123 struct ext2_super_block e2sb;
Theodore Ts'o21d14ec2001-07-03 21:43:07 -0400124 struct xfs_super_block xfsb;
Theodore Ts'odbfb3f02002-09-29 22:42:27 -0400125 struct reiserfs_super_block rfsb;
Theodore Ts'o2d8defd1999-07-03 01:59:42 +0000126
127 fd = open(device, O_RDONLY);
128 if (fd < 0)
Theodore Ts'o28412de2000-07-27 02:45:55 +0000129 return 1;
Theodore Ts'o2d8defd1999-07-03 01:59:42 +0000130
Theodore Ts'o21d14ec2001-07-03 21:43:07 -0400131 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);
Theodore Ts'odbfb3f02002-09-29 22:42:27 -0400143 } 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);
Theodore Ts'o21d14ec2001-07-03 21:43:07 -0400154 } else {
Theodore Ts'o2d8defd1999-07-03 01:59:42 +0000155 close(fd);
Theodore Ts'o28412de2000-07-27 02:45:55 +0000156 return 1;
Theodore Ts'o2d8defd1999-07-03 01:59:42 +0000157 }
158
159 close(fd);
Theodore Ts'o21d14ec2001-07-03 21:43:07 -0400160 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 }
Theodore Ts'o28412de2000-07-27 02:45:55 +0000166 return 0;
Theodore Ts'o2d8defd1999-07-03 01:59:42 +0000167}
168
Theodore Ts'o3e699062002-10-13 23:56:28 -0400169#define CBBUF (16 * 1024)
170
Theodore Ts'o28412de2000-07-27 02:45:55 +0000171static void
172uuidcache_addentry(char *device, char *label, char *uuid) {
173 struct uuidCache_s *last;
Theodore Ts'o2d8defd1999-07-03 01:59:42 +0000174
Theodore Ts'o28412de2000-07-27 02:45:55 +0000175 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;
Theodore Ts'o2d8defd1999-07-03 01:59:42 +0000181 }
Theodore Ts'o28412de2000-07-27 02:45:55 +0000182 last->next = NULL;
183 last->device = device;
184 last->label = label;
185 memcpy(last->uuid, uuid, sizeof(last->uuid));
186}
187
Theodore Ts'oeaf4dc52001-12-22 00:04:22 -0500188/*
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
195static 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
Andreas Dilgerc713f602002-01-06 21:52:44 -0700216 lv_list = opendir(vdirname);
Theodore Ts'oeaf4dc52001-12-22 00:04:22 -0500217 free(vdirname);
Theodore Ts'of9e4abf2002-09-21 06:46:43 -0400218 if (lv_list == NULL)
Andreas Dilgerc713f602002-01-06 21:52:44 -0700219 return;
220
Theodore Ts'oeaf4dc52001-12-22 00:04:22 -0500221 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
Theodore Ts'o28412de2000-07-27 02:45:55 +0000248static void
Theodore Ts'o118d7da2002-08-17 23:01:22 -0400249read_partitions(void)
250{
Theodore Ts'o28412de2000-07-27 02:45:55 +0000251 char line[100];
252 char *s;
253 int ma, mi, sz;
254 static char ptname[100];
255 FILE *procpt;
Theodore Ts'o556ccbd2000-08-20 19:11:05 +0000256 char uuid[16], *label, *devname;
Theodore Ts'o28412de2000-07-27 02:45:55 +0000257 char device[110];
Theodore Ts'o556ccbd2000-08-20 19:11:05 +0000258 dev_t dev;
259 struct stat statbuf;
Theodore Ts'o28412de2000-07-27 02:45:55 +0000260 int firstPass;
261 int handleOnFirst;
Theodore Ts'o3e699062002-10-13 23:56:28 -0400262 char *iobuf;
Theodore Ts'o28412de2000-07-27 02:45:55 +0000263
Theodore Ts'o28412de2000-07-27 02:45:55 +0000264 procpt = fopen(PROC_PARTITIONS, "r");
265 if (!procpt)
266 return;
267
Theodore Ts'o3e699062002-10-13 23:56:28 -0400268 iobuf = (char *)malloc(CBBUF);
269 if (iobuf)
270 setvbuf(procpt, iobuf, _IOFBF, CBBUF);
271
Theodore Ts'o28412de2000-07-27 02:45:55 +0000272 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
293 for(s = ptname; *s; s++);
294 if (isdigit(s[-1])) {
Theodore Ts'o556ccbd2000-08-20 19:11:05 +0000295 /*
296 * We first look in /dev for the device, but
297 * if we don't find it, or if the stat
298 * information doesn't check out, we use
299 * ext2fs_find_block_device to find it.
300 */
Theodore Ts'o28412de2000-07-27 02:45:55 +0000301 sprintf(device, "%s/%s", DEVLABELDIR, ptname);
Theodore Ts'o556ccbd2000-08-20 19:11:05 +0000302 dev = makedev(ma, mi);
303 if ((stat(device, &statbuf) < 0) ||
304 (statbuf.st_rdev != dev)) {
305 devname = ext2fs_find_block_device(dev);
306 } else
Theodore Ts'o8820c792001-01-06 04:20:03 +0000307 devname = string_copy(device);
Theodore Ts'o556ccbd2000-08-20 19:11:05 +0000308 if (!devname)
309 continue;
Theodore Ts'o118d7da2002-08-17 23:01:22 -0400310#ifdef DEBUG
311 printf("Checking partition %s (%d, %d)\n",
312 devname, ma, mi);
313#endif
Theodore Ts'o556ccbd2000-08-20 19:11:05 +0000314 if (!get_label_uuid(devname, &label, uuid))
315 uuidcache_addentry(devname, label, uuid);
316 else
317 free(devname);
Theodore Ts'o28412de2000-07-27 02:45:55 +0000318 }
319 }
320 }
321
322 fclose(procpt);
Theodore Ts'o3e699062002-10-13 23:56:28 -0400323 if (iobuf)
324 free(iobuf);
Theodore Ts'o28412de2000-07-27 02:45:55 +0000325}
326
Theodore Ts'o118d7da2002-08-17 23:01:22 -0400327static void
328read_evms(void)
329{
330 char line[100];
Theodore Ts'o118d7da2002-08-17 23:01:22 -0400331 int ma, mi, sz;
332 FILE *procpt;
333 char uuid[16], *label, *devname;
334 char device[110];
335 dev_t dev;
336 struct stat statbuf;
337
338 procpt = fopen(PROC_EVMS_VOLUMES, "r");
339 if (!procpt)
340 return;
341 while (fgets(line, sizeof(line), procpt)) {
342 if (sscanf (line, " %d %d %d %*s %*s %[^\n ]",
343 &ma, &mi, &sz, device) != 4)
344 continue;
345
346 /*
347 * We first look for the device in the named location,
348 * but if we don't find it, or if the stat information
349 * doesn't check out, we use ext2fs_find_block_device
350 * to find it.
351 */
352 dev = makedev(ma, mi);
353 if ((stat(device, &statbuf) < 0) || (statbuf.st_rdev != dev)) {
354 devname = ext2fs_find_block_device(dev);
355 } else
356 devname = string_copy(device);
357 if (!devname)
358 continue;
359#ifdef DEBUG
360 printf("Checking partition %s (%d, %d)\n",
361 devname, ma, mi);
362#endif
363 if (!get_label_uuid(devname, &label, uuid))
364 uuidcache_addentry(devname, label, uuid);
365 else
366 free(devname);
367 }
368 fclose(procpt);
369}
370
371static void
372uuidcache_init(void)
373{
374 if (uuidCache)
375 return;
376
377#ifdef VG_DIR
378 init_lvm();
379#endif
380 read_evms();
381 read_partitions();
382}
383
Theodore Ts'o28412de2000-07-27 02:45:55 +0000384#define UUID 1
385#define VOL 2
386
387static char *
388get_spec_by_x(int n, const char *t) {
389 struct uuidCache_s *uc;
390
391 uuidcache_init();
392 uc = uuidCache;
393
Andreas Dilger2d155762001-08-17 03:48:11 -0600394 if (t == NULL)
395 return NULL;
396
Theodore Ts'o28412de2000-07-27 02:45:55 +0000397 while(uc) {
398 switch (n) {
399 case UUID:
400 if (!memcmp(t, uc->uuid, sizeof(uc->uuid)))
Theodore Ts'o8820c792001-01-06 04:20:03 +0000401 return string_copy(uc->device);
Theodore Ts'o28412de2000-07-27 02:45:55 +0000402 break;
403 case VOL:
404 if (!strcmp(t, uc->label))
Theodore Ts'o8820c792001-01-06 04:20:03 +0000405 return string_copy(uc->device);
Theodore Ts'o28412de2000-07-27 02:45:55 +0000406 break;
407 }
408 uc = uc->next;
409 }
Theodore Ts'o2d8defd1999-07-03 01:59:42 +0000410 return NULL;
411}
412
Theodore Ts'o8820c792001-01-06 04:20:03 +0000413static char fromhex(char c)
414{
Theodore Ts'o2d8defd1999-07-03 01:59:42 +0000415 if (isdigit(c))
416 return (c - '0');
417 else if (islower(c))
418 return (c - 'a' + 10);
419 else
420 return (c - 'A' + 10);
421}
422
423char *
Theodore Ts'o8820c792001-01-06 04:20:03 +0000424get_spec_by_uuid(const char *s)
425{
426 char uuid[16];
Theodore Ts'o2d8defd1999-07-03 01:59:42 +0000427 int i;
Theodore Ts'o2d8defd1999-07-03 01:59:42 +0000428
429 if (strlen(s) != 36 ||
430 s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-')
431 goto bad_uuid;
432 for (i=0; i<16; i++) {
433 if (*s == '-') s++;
434 if (!isxdigit(s[0]) || !isxdigit(s[1]))
435 goto bad_uuid;
436 uuid[i] = ((fromhex(s[0])<<4) | fromhex(s[1]));
437 s += 2;
438 }
439 return get_spec_by_x(UUID, uuid);
440
441 bad_uuid:
Theodore Ts'o118d7da2002-08-17 23:01:22 -0400442 fprintf(stderr, _("WARNING: %s: bad UUID\n"), s);
Theodore Ts'o2d8defd1999-07-03 01:59:42 +0000443 return NULL;
444}
445
446char *
447get_spec_by_volume_label(const char *s) {
448 return get_spec_by_x(VOL, s);
449}
Theodore Ts'o28412de2000-07-27 02:45:55 +0000450
451const char *
452get_volume_label_by_spec(const char *spec) {
453 struct uuidCache_s *uc;
454
455 uuidcache_init();
456 uc = uuidCache;
457
458 while(uc) {
459 if (!strcmp(spec, uc->device))
460 return uc->label;
461 uc = uc->next;
462 }
463 return NULL;
464}
Andreas Dilger2d155762001-08-17 03:48:11 -0600465
466/*
467 * Interpret the device name if necessary.
468 * Frees the pointer passed to it if we return a different device string.
469 */
470char *interpret_spec(char *spec)
471{
472 char *dev = NULL;
473
474 if (!spec)
475 return NULL;
476
477 if (!strncmp(spec, "UUID=", 5))
478 dev = get_spec_by_uuid(spec+5);
479 else if (!strncmp(spec, "LABEL=", 6))
480 dev = get_spec_by_volume_label(spec+6);
481 else
482 dev = string_copy(spec);
483 return dev;
484}
Theodore Ts'o118d7da2002-08-17 23:01:22 -0400485
486#ifdef DEBUG
487main(int argc, char **argv)
488{
489 uuidcache_init();
490}
491#endif