]> git.proxmox.com Git - grub2.git/blame - grub-core/kern/emu/getroot.c
* grub-core/kern/emu/getroot.c (grub_guess_root_device): Revert to
[grub2.git] / grub-core / kern / emu / getroot.c
CommitLineData
1f7315a3 1/* getroot.c - Get root device */
2/*
4b13b216 3 * GRUB -- GRand Unified Bootloader
31cfe714 4 * Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
1f7315a3 5 *
5a79f472 6 * GRUB is free software: you can redistribute it and/or modify
1f7315a3 7 * it under the terms of the GNU General Public License as published by
5a79f472 8 * the Free Software Foundation, either version 3 of the License, or
1f7315a3 9 * (at your option) any later version.
10 *
5a79f472 11 * GRUB is distributed in the hope that it will be useful,
1f7315a3 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
5a79f472 17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
1f7315a3 18 */
19
3533413c 20#include <config-util.h>
4ee55921 21#include <config.h>
3533413c 22
1f7315a3 23#include <sys/stat.h>
da6e6f17 24#include <sys/types.h>
a184f9c8 25#include <assert.h>
da6e6f17 26#include <fcntl.h>
1f7315a3 27#include <unistd.h>
28#include <string.h>
29#include <dirent.h>
81827e24 30#include <errno.h>
a184f9c8 31#include <error.h>
840b61d8
BC
32#include <stdio.h>
33#include <stdlib.h>
81827e24 34#include <stdint.h>
ebf53056 35#include <grub/util/misc.h>
eedf167f 36
c0e103e4
VS
37#ifdef HAVE_DEVICE_MAPPER
38# include <libdevmapper.h>
39#endif
40
994cc3a3
ST
41#ifdef __GNU__
42#include <hurd.h>
43#include <hurd/lookup.h>
44#include <hurd/fs.h>
cb7f64b2 45#include <sys/mman.h>
994cc3a3
ST
46#endif
47
139ab97d
CW
48#ifdef __linux__
49# include <sys/types.h>
50# include <sys/wait.h>
51#endif
52
a184f9c8
RM
53#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
54# include <grub/util/libzfs.h>
55# include <grub/util/libnvpair.h>
56#endif
57
840b61d8
BC
58#include <grub/mm.h>
59#include <grub/misc.h>
60#include <grub/emu/misc.h>
61#include <grub/emu/hostdisk.h>
62#include <grub/emu/getroot.h>
1f7315a3 63
64static void
65strip_extra_slashes (char *dir)
66{
67 char *p = dir;
68
69 while ((p = strchr (p, '/')) != 0)
70 {
71 if (p[1] == '/')
72 {
73 memmove (p, p + 1, strlen (p));
74 continue;
75 }
76 else if (p[1] == '\0')
77 {
eedf167f 78 if (p > dir)
79 p[0] = '\0';
1f7315a3 80 break;
81 }
b39f9d20 82
1f7315a3 83 p++;
84 }
85}
86
87static char *
88xgetcwd (void)
89{
90 size_t size = 10;
91 char *path;
92
93 path = xmalloc (size);
94 while (! getcwd (path, size))
95 {
96 size <<= 1;
97 path = xrealloc (path, size);
98 }
99
100 return path;
101}
102
4db50964 103#ifdef __linux__
eedf167f 104
78fa584f
CW
105struct mountinfo_entry
106{
107 int id;
108 int major, minor;
109 char enc_root[PATH_MAX], enc_path[PATH_MAX];
110 char fstype[PATH_MAX], device[PATH_MAX];
111};
112
4db50964
CW
113/* Statting something on a btrfs filesystem always returns a virtual device
114 major/minor pair rather than the real underlying device, because btrfs
115 can span multiple underlying devices (and even if it's currently only
116 using a single device it can be dynamically extended onto another). We
117 can't deal with the multiple-device case yet, but in the meantime, we can
118 at least cope with the single-device case by scanning
119 /proc/self/mountinfo. */
228cfb40
VS
120char *
121grub_find_root_device_from_mountinfo (const char *dir, char **relroot)
1f7315a3 122{
4db50964 123 FILE *fp;
0d9ff593
CW
124 char *buf = NULL;
125 size_t len = 0;
4db50964 126 char *ret = NULL;
78fa584f
CW
127 int entry_len = 0, entry_max = 4;
128 struct mountinfo_entry *entries;
129 struct mountinfo_entry parent_entry = { 0, 0, 0, "", "", "", "" };
130 int i;
b39f9d20 131
8c2c4ff2
CW
132 if (! *dir)
133 dir = "/";
11b970d7
CW
134 if (relroot)
135 *relroot = NULL;
136
4db50964
CW
137 fp = fopen ("/proc/self/mountinfo", "r");
138 if (! fp)
139 return NULL; /* fall through to other methods */
1f7315a3 140
78fa584f
CW
141 entries = xmalloc (entry_max * sizeof (*entries));
142
143 /* First, build a list of relevant visible mounts. */
0d9ff593 144 while (getline (&buf, &len, fp) > 0)
4db50964 145 {
78fa584f 146 struct mountinfo_entry entry;
4db50964
CW
147 int count;
148 size_t enc_path_len;
149 const char *sep;
1f7315a3 150
4db50964 151 if (sscanf (buf, "%d %d %u:%u %s %s%n",
78fa584f
CW
152 &entry.id, &parent_entry.id, &entry.major, &entry.minor,
153 entry.enc_root, entry.enc_path, &count) < 6)
4db50964 154 continue;
b39f9d20 155
78fa584f 156 enc_path_len = strlen (entry.enc_path);
71b6a2b7
CW
157 /* Check that enc_path is a prefix of dir. The prefix must either be
158 the entire string, or end with a slash, or be immediately followed
159 by a slash. */
78fa584f 160 if (strncmp (dir, entry.enc_path, enc_path_len) != 0 ||
71b6a2b7
CW
161 (enc_path_len && dir[enc_path_len - 1] != '/' &&
162 dir[enc_path_len] && dir[enc_path_len] != '/'))
4db50964 163 continue;
1f7315a3 164
4db50964
CW
165 sep = strstr (buf + count, " - ");
166 if (!sep)
167 continue;
1f7315a3 168
0d9ff593 169 sep += sizeof (" - ") - 1;
78fa584f
CW
170 if (sscanf (sep, "%s %s", entry.fstype, entry.device) != 2)
171 continue;
172
173 /* Using the mount IDs, find out where this fits in the list of
174 visible mount entries we've seen so far. There are three
175 interesting cases. Firstly, it may be inserted at the end: this is
176 the usual case of /foo/bar being mounted after /foo. Secondly, it
177 may be inserted at the start: for example, this can happen for
178 filesystems that are mounted before / and later moved under it.
179 Thirdly, it may occlude part or all of the existing filesystem
180 tree, in which case the end of the list needs to be pruned and this
181 new entry will be inserted at the end. */
182 if (entry_len >= entry_max)
183 {
184 entry_max <<= 1;
185 entries = xrealloc (entries, entry_max * sizeof (*entries));
186 }
187
188 if (!entry_len)
189 {
190 /* Initialise list. */
191 entry_len = 2;
192 entries[0] = parent_entry;
193 entries[1] = entry;
194 }
195 else
196 {
197 for (i = entry_len - 1; i >= 0; i--)
198 {
199 if (entries[i].id == parent_entry.id)
200 {
201 /* Insert at end, pruning anything previously above this. */
202 entry_len = i + 2;
203 entries[i + 1] = entry;
204 break;
205 }
206 else if (i == 0 && entries[i].id == entry.id)
207 {
208 /* Insert at start. */
209 entry_len++;
210 memmove (entries + 1, entries,
211 (entry_len - 1) * sizeof (*entries));
212 entries[0] = parent_entry;
213 entries[1] = entry;
214 break;
215 }
216 }
217 }
218 }
219
220 /* Now scan visible mounts for the ones we're interested in. */
221 for (i = entry_len - 1; i >= 0; i--)
222 {
78fa584f 223 if (!*entries[i].device)
4db50964 224 continue;
1f7315a3 225
78fa584f 226 ret = strdup (entries[i].device);
228cfb40 227 if (relroot)
78fa584f
CW
228 *relroot = strdup (entries[i].enc_root);
229 break;
eedf167f 230 }
1f7315a3 231
0d9ff593 232 free (buf);
78fa584f 233 free (entries);
4db50964
CW
234 fclose (fp);
235 return ret;
1f7315a3 236}
237
4db50964
CW
238#endif /* __linux__ */
239
a184f9c8 240#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
a184f9c8
RM
241static char *
242find_root_device_from_libzfs (const char *dir)
243{
c482ad98 244 char *device = NULL;
c882acc0
RM
245 char *poolname;
246 char *poolfs;
a184f9c8 247
0de22aa9 248 grub_find_zpool_from_dir (dir, &poolname, &poolfs);
a184f9c8 249 if (! poolname)
0de22aa9 250 return NULL;
a184f9c8
RM
251
252 {
253 zpool_handle_t *zpool;
729a0f2e 254 libzfs_handle_t *libzfs;
8e57a6ca
RM
255 nvlist_t *config, *vdev_tree;
256 nvlist_t **children, **path;
a184f9c8 257 unsigned int nvlist_count;
8e57a6ca 258 unsigned int i;
a184f9c8 259
729a0f2e
RM
260 libzfs = grub_get_libzfs_handle ();
261 if (! libzfs)
262 return NULL;
263
264 zpool = zpool_open (libzfs, poolname);
8e57a6ca 265 config = zpool_get_config (zpool, NULL);
a184f9c8 266
8e57a6ca 267 if (nvlist_lookup_nvlist (config, "vdev_tree", &vdev_tree) != 0)
a184f9c8
RM
268 error (1, errno, "nvlist_lookup_nvlist (\"vdev_tree\")");
269
8e57a6ca 270 if (nvlist_lookup_nvlist_array (vdev_tree, "children", &children, &nvlist_count) != 0)
a184f9c8 271 error (1, errno, "nvlist_lookup_nvlist_array (\"children\")");
8e57a6ca 272 assert (nvlist_count > 0);
a184f9c8 273
8e57a6ca
RM
274 while (nvlist_lookup_nvlist_array (children[0], "children",
275 &children, &nvlist_count) == 0)
276 assert (nvlist_count > 0);
277
278 for (i = 0; i < nvlist_count; i++)
a184f9c8 279 {
8e57a6ca
RM
280 if (nvlist_lookup_string (children[i], "path", &device) != 0)
281 error (1, errno, "nvlist_lookup_string (\"path\")");
282
283 struct stat st;
284 if (stat (device, &st) == 0)
c482ad98
SG
285 {
286 device = xstrdup (device);
287 break;
288 }
a184f9c8 289
8e57a6ca
RM
290 device = NULL;
291 }
a184f9c8
RM
292
293 zpool_close (zpool);
294 }
295
296 free (poolname);
c882acc0
RM
297 if (poolfs)
298 free (poolfs);
a184f9c8
RM
299
300 return device;
301}
302#endif
303
6e5a42fe 304#ifdef __MINGW32__
305
108538d8
CW
306char *
307grub_find_device (const char *dir __attribute__ ((unused)),
6e5a42fe 308 dev_t dev __attribute__ ((unused)))
309{
310 return 0;
311}
312
313#elif ! defined(__CYGWIN__)
eedf167f 314
108538d8
CW
315char *
316grub_find_device (const char *dir, dev_t dev)
1f7315a3 317{
318 DIR *dp;
319 char *saved_cwd;
320 struct dirent *ent;
b39f9d20 321
108538d8
CW
322 if (! dir)
323 {
324#ifdef __CYGWIN__
325 return NULL;
326#else
327 dir = "/dev";
328#endif
329 }
330
1f7315a3 331 dp = opendir (dir);
332 if (! dp)
333 return 0;
334
335 saved_cwd = xgetcwd ();
336
4b13b216 337 grub_util_info ("changing current directory to %s", dir);
1f7315a3 338 if (chdir (dir) < 0)
339 {
340 free (saved_cwd);
341 closedir (dp);
342 return 0;
343 }
b39f9d20 344
1f7315a3 345 while ((ent = readdir (dp)) != 0)
346 {
347 struct stat st;
b39f9d20 348
8415f261 349 /* Avoid:
350 - dotfiles (like "/dev/.tmp.md0") since they could be duplicates.
351 - dotdirs (like "/dev/.static") since they could contain duplicates. */
352 if (ent->d_name[0] == '.')
1f7315a3 353 continue;
354
355 if (lstat (ent->d_name, &st) < 0)
356 /* Ignore any error. */
357 continue;
358
15fb2ae8
CW
359 if (S_ISLNK (st.st_mode)) {
360#ifdef __linux__
f767c929 361 if (strcmp (dir, "mapper") == 0 || strcmp (dir, "/dev/mapper") == 0) {
15fb2ae8
CW
362 /* Follow symbolic links under /dev/mapper/; the canonical name
363 may be something like /dev/dm-0, but the names under
364 /dev/mapper/ are more human-readable and so we prefer them if
365 we can get them. */
366 if (stat (ent->d_name, &st) < 0)
367 continue;
368 } else
369#endif /* __linux__ */
370 /* Don't follow other symbolic links. */
1f7315a3 371 continue;
15fb2ae8 372 }
b39f9d20 373
8415f261 374 if (S_ISDIR (st.st_mode))
1f7315a3 375 {
8415f261 376 /* Find it recursively. */
1f7315a3 377 char *res;
378
108538d8 379 res = grub_find_device (ent->d_name, dev);
1f7315a3 380
381 if (res)
382 {
383 if (chdir (saved_cwd) < 0)
70a14d3d 384 grub_util_error ("cannot restore the original directory");
b39f9d20 385
1f7315a3 386 free (saved_cwd);
387 closedir (dp);
388 return res;
389 }
390 }
391
4f253044 392#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
b1ac8644 393 if (S_ISCHR (st.st_mode) && st.st_rdev == dev)
394#else
1f7315a3 395 if (S_ISBLK (st.st_mode) && st.st_rdev == dev)
b1ac8644 396#endif
1f7315a3 397 {
9051607e 398#ifdef __linux__
cc349fb3 399 /* Skip device names like /dev/dm-0, which are short-hand aliases
400 to more descriptive device names, e.g. those under /dev/mapper */
9051607e 401 if (ent->d_name[0] == 'd' &&
402 ent->d_name[1] == 'm' &&
403 ent->d_name[2] == '-' &&
404 ent->d_name[3] >= '0' &&
405 ent->d_name[3] <= '9')
406 continue;
407#endif
408
1f7315a3 409 /* Found! */
410 char *res;
411 char *cwd;
2c7031b1
GS
412#if defined(__NetBSD__)
413 /* Convert this block device to its character (raw) device. */
414 const char *template = "%s/r%s";
415#else
416 /* Keep the device name as it is. */
417 const char *template = "%s/%s";
418#endif
1f7315a3 419
420 cwd = xgetcwd ();
2c7031b1
GS
421 res = xmalloc (strlen (cwd) + strlen (ent->d_name) + 3);
422 sprintf (res, template, cwd, ent->d_name);
1f7315a3 423 strip_extra_slashes (res);
424 free (cwd);
425
08db4632 426 /* /dev/root is not a real block device keep looking, takes care
427 of situation where root filesystem is on the same partition as
428 grub files */
429
430 if (strcmp(res, "/dev/root") == 0)
431 continue;
432
1f7315a3 433 if (chdir (saved_cwd) < 0)
70a14d3d 434 grub_util_error ("cannot restore the original directory");
1f7315a3 435
436 free (saved_cwd);
437 closedir (dp);
438 return res;
439 }
440 }
441
442 if (chdir (saved_cwd) < 0)
70a14d3d 443 grub_util_error ("cannot restore the original directory");
1f7315a3 444
445 free (saved_cwd);
446 closedir (dp);
447 return 0;
448}
449
eedf167f 450#else /* __CYGWIN__ */
451
452/* Read drive/partition serial number from mbr/boot sector,
453 return 0 on read error, ~0 on unknown serial. */
454static unsigned
455get_bootsec_serial (const char *os_dev, int mbr)
456{
457 /* Read boot sector. */
458 int fd = open (os_dev, O_RDONLY);
459 if (fd < 0)
460 return 0;
461 unsigned char buf[0x200];
462 int n = read (fd, buf, sizeof (buf));
463 close (fd);
464 if (n != sizeof(buf))
465 return 0;
466
467 /* Check signature. */
468 if (!(buf[0x1fe] == 0x55 && buf[0x1ff] == 0xaa))
469 return ~0;
470
471 /* Serial number offset depends on boot sector type. */
472 if (mbr)
473 n = 0x1b8;
474 else if (memcmp (buf + 0x03, "NTFS", 4) == 0)
475 n = 0x048;
476 else if (memcmp (buf + 0x52, "FAT32", 5) == 0)
477 n = 0x043;
478 else if (memcmp (buf + 0x36, "FAT", 3) == 0)
479 n = 0x027;
480 else
481 return ~0;
482
483 unsigned serial = *(unsigned *)(buf + n);
484 if (serial == 0)
485 return ~0;
486 return serial;
487}
488
108538d8
CW
489char *
490grub_find_device (const char *path, dev_t dev)
eedf167f 491{
492 /* No root device for /cygdrive. */
493 if (dev == (DEV_CYGDRIVE_MAJOR << 16))
494 return 0;
495
496 /* Convert to full POSIX and Win32 path. */
497 char fullpath[PATH_MAX], winpath[PATH_MAX];
498 cygwin_conv_to_full_posix_path (path, fullpath);
499 cygwin_conv_to_full_win32_path (fullpath, winpath);
500
501 /* If identical, this is no real filesystem path. */
502 if (strcmp (fullpath, winpath) == 0)
503 return 0;
504
505 /* Check for floppy drive letter. */
506 if (winpath[0] && winpath[1] == ':' && strchr ("AaBb", winpath[0]))
507 return xstrdup (strchr ("Aa", winpath[0]) ? "/dev/fd0" : "/dev/fd1");
508
509 /* Cygwin returns the partition serial number in stat.st_dev.
510 This is never identical to the device number of the emulated
108538d8 511 /dev/sdXN device, so above grub_find_device () does not work.
4241d2b1 512 Search the partition with the same serial in boot sector instead. */
eedf167f 513 char devpath[sizeof ("/dev/sda15") + 13]; /* Size + Paranoia. */
514 int d;
515 for (d = 'a'; d <= 'z'; d++)
516 {
517 sprintf (devpath, "/dev/sd%c", d);
518 if (get_bootsec_serial (devpath, 1) == 0)
519 continue;
520 int p;
521 for (p = 1; p <= 15; p++)
522 {
523 sprintf (devpath, "/dev/sd%c%d", d, p);
524 unsigned ser = get_bootsec_serial (devpath, 0);
525 if (ser == 0)
526 break;
527 if (ser != (unsigned)~0 && dev == (dev_t)ser)
528 return xstrdup (devpath);
529 }
530 }
531 return 0;
532}
533
534#endif /* __CYGWIN__ */
535
1f7315a3 536char *
4b13b216 537grub_guess_root_device (const char *dir)
1f7315a3 538{
bd1a4147 539 char *os_dev = NULL;
994cc3a3
ST
540#ifdef __GNU__
541 file_t file;
542 mach_port_t *ports;
543 int *ints;
544 loff_t *offsets;
545 char *data;
546 error_t err;
547 mach_msg_type_number_t num_ports = 0, num_ints = 0, num_offsets = 0, data_len = 0;
548 size_t name_len;
549
550 file = file_name_lookup (dir, 0, 0);
551 if (file == MACH_PORT_NULL)
552 return 0;
553
554 err = file_get_storage_info (file,
555 &ports, &num_ports,
556 &ints, &num_ints,
557 &offsets, &num_offsets,
558 &data, &data_len);
559
560 if (num_ints < 1)
561 grub_util_error ("Storage info for `%s' does not include type", dir);
562 if (ints[0] != STORAGE_DEVICE)
563 grub_util_error ("Filesystem of `%s' is not stored on local disk", dir);
564
565 if (num_ints < 5)
566 grub_util_error ("Storage info for `%s' does not include name", dir);
567 name_len = ints[4];
568 if (name_len < data_len)
569 grub_util_error ("Bogus name length for storage info for `%s'", dir);
570 if (data[name_len - 1] != '\0')
571 grub_util_error ("Storage name for `%s' not NUL-terminated", dir);
572
573 os_dev = xmalloc (strlen ("/dev/") + data_len);
574 memcpy (os_dev, "/dev/", strlen ("/dev/"));
575 memcpy (os_dev + strlen ("/dev/"), data, data_len);
576
577 if (ports && num_ports > 0)
578 {
579 mach_msg_type_number_t i;
580 for (i = 0; i < num_ports; i++)
581 {
582 mach_port_t port = ports[i];
583 if (port != MACH_PORT_NULL)
584 mach_port_deallocate (mach_task_self(), port);
585 }
586 munmap ((caddr_t) ports, num_ports * sizeof (*ports));
587 }
588
589 if (ints && num_ints > 0)
590 munmap ((caddr_t) ints, num_ints * sizeof (*ints));
591 if (offsets && num_offsets > 0)
592 munmap ((caddr_t) offsets, num_offsets * sizeof (*offsets));
593 if (data && data_len > 0)
594 munmap (data, data_len);
595 mach_port_deallocate (mach_task_self (), file);
596#else /* !__GNU__ */
597 struct stat st;
bd1a4147 598 dev_t dev;
b39f9d20 599
4db50964 600#ifdef __linux__
bd1a4147
CW
601 if (!os_dev)
602 os_dev = grub_find_root_device_from_mountinfo (dir, NULL);
4db50964
CW
603#endif /* __linux__ */
604
a184f9c8 605#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
bd1a4147
CW
606 if (!os_dev)
607 os_dev = find_root_device_from_libzfs (dir);
a184f9c8
RM
608#endif
609
bd1a4147
CW
610 if (os_dev)
611 {
f767c929
VS
612 char *tmp = os_dev;
613 os_dev = canonicalize_file_name (os_dev);
614 free (tmp);
bd1a4147 615 }
f767c929
VS
616
617 if (os_dev)
bd1a4147 618 {
e2d1dba0
VS
619 int dm = (strncmp (os_dev, "/dev/dm-", sizeof ("/dev/dm-") - 1) == 0);
620 int root = (strcmp (os_dev, "/dev/root") == 0);
621 if (!dm && !root)
f767c929 622 return os_dev;
bc09e1a2
VS
623 if (stat (os_dev, &st) >= 0)
624 {
625 free (os_dev);
626 dev = st.st_rdev;
627 return grub_find_device (dm ? "/dev/mapper" : "/dev", dev);
628 }
f767c929 629 free (os_dev);
bd1a4147 630 }
1f7315a3 631
f767c929
VS
632 if (stat (dir, &st) < 0)
633 grub_util_error ("cannot stat `%s'", dir);
634
635 dev = st.st_dev;
636
eedf167f 637#ifdef __CYGWIN__
638 /* Cygwin specific function. */
bd1a4147 639 os_dev = grub_find_device (dir, dev);
c74493e0 640
eedf167f 641#else
642
643 /* This might be truly slow, but is there any better way? */
bd1a4147 644 os_dev = grub_find_device ("/dev", dev);
21c8cbb1 645#endif
994cc3a3 646#endif /* !__GNU__ */
849d55d3 647
648 return os_dev;
649}
74e4934e
VS
650
651static int
c0e103e4 652grub_util_is_lvm (const char *os_dev)
62b47f22 653{
c0e103e4
VS
654 if ((strncmp ("/dev/mapper/", os_dev, 12) != 0))
655 return 0;
656
657#ifdef HAVE_DEVICE_MAPPER
658 {
659 struct dm_tree *tree;
660 uint32_t maj, min;
307ed0b4
CW
661 struct dm_tree_node *node = NULL;
662 const char *node_uuid;
c0e103e4 663 struct stat st;
a2c1332b 664
c0e103e4
VS
665 if (stat (os_dev, &st) < 0)
666 return 0;
a2c1332b 667
c0e103e4
VS
668 tree = dm_tree_create ();
669 if (! tree)
670 {
671 grub_printf ("Failed to create tree\n");
672 grub_dprintf ("hostdisk", "dm_tree_create failed\n");
673 return 0;
674 }
675
676 maj = major (st.st_rdev);
677 min = minor (st.st_rdev);
678
679 if (! dm_tree_add_dev (tree, maj, min))
680 {
681 grub_dprintf ("hostdisk", "dm_tree_add_dev failed\n");
682 dm_tree_free (tree);
683 return 0;
684 }
685
686 node = dm_tree_find_node (tree, maj, min);
687 if (! node)
688 {
689 grub_dprintf ("hostdisk", "dm_tree_find_node failed\n");
690 dm_tree_free (tree);
691 return 0;
692 }
693 node_uuid = dm_tree_node_get_uuid (node);
694 if (! node_uuid)
695 {
696 grub_dprintf ("hostdisk", "%s has no DM uuid\n", os_dev);
697 dm_tree_free (tree);
698 return 0;
699 }
700 if (strncmp (node_uuid, "LVM-", 4) != 0)
701 {
702 dm_tree_free (tree);
703 return 0;
704 }
705 dm_tree_free (tree);
706 return 1;
707 }
708#else
709 return 1;
710#endif /* HAVE_DEVICE_MAPPER */
62b47f22 711}
849d55d3 712
1eb8c802 713int
e0a6ca52 714grub_util_get_dev_abstraction (const char *os_dev __attribute__((unused)))
849d55d3 715{
eedf167f 716#ifdef __linux__
cf9827de
VS
717 /* User explicitly claims that this drive is visible by BIOS. */
718 if (grub_util_biosdisk_is_present (os_dev))
719 return GRUB_DEV_ABSTRACTION_NONE;
720
2b002173 721 /* Check for LVM. */
c0e103e4 722 if (grub_util_is_lvm (os_dev))
1eb8c802 723 return GRUB_DEV_ABSTRACTION_LVM;
2b002173 724
849d55d3 725 /* Check for RAID. */
4b078266 726 if (!strncmp (os_dev, "/dev/md", 7) && ! grub_util_device_is_mapped (os_dev))
1eb8c802 727 return GRUB_DEV_ABSTRACTION_RAID;
eedf167f 728#endif
1eb8c802 729
730 /* No abstraction found. */
731 return GRUB_DEV_ABSTRACTION_NONE;
732}
733
139ab97d
CW
734#ifdef __linux__
735static char *
50d2cc5a 736get_mdadm_uuid (const char *os_dev)
139ab97d
CW
737{
738 int mdadm_pipe[2];
739 pid_t mdadm_pid;
740 char *name = NULL;
741
742 if (pipe (mdadm_pipe) < 0)
743 {
744 grub_util_warn ("Unable to create pipe for mdadm: %s", strerror (errno));
745 return NULL;
746 }
747
748 mdadm_pid = fork ();
749 if (mdadm_pid < 0)
750 grub_util_warn ("Unable to fork mdadm: %s", strerror (errno));
751 else if (mdadm_pid == 0)
752 {
753 /* Child. */
754 char *argv[5];
755
756 close (mdadm_pipe[0]);
757 dup2 (mdadm_pipe[1], STDOUT_FILENO);
758 close (mdadm_pipe[1]);
759
760 /* execvp has inconvenient types, hence the casts. None of these
761 strings will actually be modified. */
762 argv[0] = (char *) "mdadm";
763 argv[1] = (char *) "--detail";
764 argv[2] = (char *) "--export";
765 argv[3] = (char *) os_dev;
766 argv[4] = NULL;
767 execvp ("mdadm", argv);
768 exit (127);
769 }
770 else
771 {
772 /* Parent. Read mdadm's output. */
773 FILE *mdadm;
774 char *buf = NULL;
775 size_t len = 0;
776
777 close (mdadm_pipe[1]);
778 mdadm = fdopen (mdadm_pipe[0], "r");
779 if (! mdadm)
780 {
781 grub_util_warn ("Unable to open stream from mdadm: %s",
782 strerror (errno));
783 goto out;
784 }
785
786 while (getline (&buf, &len, mdadm) > 0)
787 {
50d2cc5a 788 if (strncmp (buf, "MD_UUID=", sizeof ("MD_UUID=") - 1) == 0)
139ab97d 789 {
50d2cc5a 790 char *name_start, *ptri, *ptro;
139ab97d
CW
791 size_t name_len;
792
793 free (name);
50d2cc5a
VS
794 name_start = buf + sizeof ("MD_UUID=") - 1;
795 ptro = name = xmalloc (strlen (name_start) + 1);
796 for (ptri = name_start; *ptri && *ptri != '\n' && *ptri != '\r';
797 ptri++)
798 if ((*ptri >= '0' && *ptri <= '9')
799 || (*ptri >= 'a' && *ptri <= 'f')
800 || (*ptri >= 'A' && *ptri <= 'F'))
801 *ptro++ = *ptri;
802 *ptro = 0;
139ab97d
CW
803 }
804 }
805
806out:
807 close (mdadm_pipe[0]);
808 waitpid (mdadm_pid, NULL, 0);
809 }
810
811 return name;
812}
813#endif /* __linux__ */
814
1eb8c802 815char *
816grub_util_get_grub_dev (const char *os_dev)
817{
139ab97d 818 char *grub_dev = NULL;
1eb8c802 819
820 switch (grub_util_get_dev_abstraction (os_dev))
2b002173 821 {
1eb8c802 822 case GRUB_DEV_ABSTRACTION_LVM:
1eb8c802 823
4ee55921 824 {
825 unsigned short i, len;
826 grub_size_t offset = sizeof ("/dev/mapper/") - 1;
1eb8c802 827
4ee55921 828 len = strlen (os_dev) - offset + 1;
829 grub_dev = xmalloc (len);
830
831 for (i = 0; i < len; i++, offset++)
832 {
833 grub_dev[i] = os_dev[offset];
834 if (os_dev[offset] == '-' && os_dev[offset + 1] == '-')
835 offset++;
836 }
837 }
b39f9d20 838
1eb8c802 839 break;
840
841 case GRUB_DEV_ABSTRACTION_RAID:
260ba823 842
843 if (os_dev[7] == '_' && os_dev[8] == 'd')
844 {
845 /* This a partitionable RAID device of the form /dev/md_dNNpMM. */
278922e8 846
847 char *p, *q;
260ba823 848
4ee55921 849 p = strdup (os_dev + sizeof ("/dev/md_d") - 1);
850
278922e8 851 q = strchr (p, 'p');
852 if (q)
853 *q = ',';
854
d6ceebf1 855 grub_dev = xasprintf ("md%s", p);
278922e8 856 free (p);
857 }
858 else if (os_dev[7] == '/' && os_dev[8] == 'd')
859 {
860 /* This a partitionable RAID device of the form /dev/md/dNNpMM. */
861
862 char *p, *q;
863
864 p = strdup (os_dev + sizeof ("/dev/md/d") - 1);
865
4ee55921 866 q = strchr (p, 'p');
867 if (q)
868 *q = ',';
869
d6ceebf1 870 grub_dev = xasprintf ("md%s", p);
4ee55921 871 free (p);
260ba823 872 }
873 else if (os_dev[7] >= '0' && os_dev[7] <= '9')
874 {
3143cc1c 875 char *p , *q;
876
877 p = strdup (os_dev + sizeof ("/dev/md") - 1);
878
879 q = strchr (p, 'p');
880 if (q)
881 *q = ',';
882
d6ceebf1 883 grub_dev = xasprintf ("md%s", p);
3143cc1c 884 free (p);
4ee55921 885 }
886 else if (os_dev[7] == '/' && os_dev[8] >= '0' && os_dev[8] <= '9')
887 {
3143cc1c 888 char *p , *q;
889
890 p = strdup (os_dev + sizeof ("/dev/md/") - 1);
891
892 q = strchr (p, 'p');
893 if (q)
894 *q = ',';
895
d6ceebf1 896 grub_dev = xasprintf ("md%s", p);
3143cc1c 897 free (p);
260ba823 898 }
c8ec30a0
FZ
899 else if (os_dev[7] == '/')
900 {
901 /* mdraid 1.x with a free name. */
902 char *p , *q;
903
904 p = strdup (os_dev + sizeof ("/dev/md/") - 1);
905
906 q = strchr (p, 'p');
907 if (q)
908 *q = ',';
909
b26f1c11 910 grub_dev = xasprintf ("md/%s", p);
c8ec30a0
FZ
911 free (p);
912 }
260ba823 913 else
70a14d3d 914 grub_util_error ("unknown kind of RAID device `%s'", os_dev);
b39f9d20 915
139ab97d
CW
916#ifdef __linux__
917 {
50d2cc5a 918 char *mdadm_name = get_mdadm_uuid (os_dev);
ebc71d28 919 struct stat st;
139ab97d
CW
920
921 if (mdadm_name)
922 {
72a89a54 923 const char *q;
56445fb2 924
72a89a54
VS
925 for (q = os_dev + strlen (os_dev) - 1; q >= os_dev
926 && grub_isdigit (*q); q--);
56445fb2
VS
927
928 if (q >= os_dev && *q == 'p')
ebc71d28
VS
929 {
930 free (grub_dev);
50d2cc5a
VS
931 grub_dev = xasprintf ("mduuid/%s,%s", mdadm_name, q + 1);
932 goto done;
ebc71d28 933 }
50d2cc5a
VS
934 free (grub_dev);
935 grub_dev = xasprintf ("mduuid/%s", mdadm_name);
56445fb2
VS
936
937 done:
139ab97d
CW
938 free (mdadm_name);
939 }
940 }
941#endif /* __linux__ */
942
1eb8c802 943 break;
2b002173 944
1eb8c802 945 default: /* GRUB_DEV_ABSTRACTION_NONE */
946 grub_dev = grub_util_biosdisk_get_grub_dev (os_dev);
2b002173 947 }
849d55d3 948
1eb8c802 949 return grub_dev;
1f7315a3 950}
79ca2d78 951
8790bb04 952const char *
79ca2d78 953grub_util_check_block_device (const char *blk_dev)
954{
955 struct stat st;
956
957 if (stat (blk_dev, &st) < 0)
70a14d3d 958 grub_util_error ("cannot stat `%s'", blk_dev);
79ca2d78 959
960 if (S_ISBLK (st.st_mode))
961 return (blk_dev);
962 else
963 return 0;
964}
b1ac8644 965
966const char *
967grub_util_check_char_device (const char *blk_dev)
968{
969 struct stat st;
970
971 if (stat (blk_dev, &st) < 0)
70a14d3d 972 grub_util_error ("cannot stat `%s'", blk_dev);
b1ac8644 973
974 if (S_ISCHR (st.st_mode))
975 return (blk_dev);
976 else
977 return 0;
978}