]> git.proxmox.com Git - grub2.git/blob - util/getroot.c
88bbf6afc8c868fe283cc6edc8c754f1e0f70050
[grub2.git] / util / getroot.c
1 /* getroot.c - Get root device */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc.
5 *
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB is distributed in the hope that it will be useful,
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
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <config-util.h>
21 #include <config.h>
22
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <assert.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <dirent.h>
30 #include <errno.h>
31 #include <error.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <stdint.h>
35 #ifdef HAVE_LIMITS_H
36 #include <limits.h>
37 #endif
38 #include <grub/util/misc.h>
39 #include <grub/util/lvm.h>
40 #include <grub/cryptodisk.h>
41 #include <grub/i18n.h>
42
43 #ifdef HAVE_DEVICE_MAPPER
44 # include <libdevmapper.h>
45 #endif
46
47 #ifdef __GNU__
48 #include <hurd.h>
49 #include <hurd/lookup.h>
50 #include <hurd/fs.h>
51 #include <sys/mman.h>
52 #endif
53
54 #include <sys/types.h>
55
56 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__)
57 # include <sys/param.h>
58 # include <sys/mount.h>
59 #endif
60
61 #if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
62 # include <grub/util/libzfs.h>
63 # include <grub/util/libnvpair.h>
64 #endif
65
66 #ifdef __sun__
67 # include <sys/types.h>
68 # include <sys/mkdev.h>
69 #endif
70
71 #include <grub/mm.h>
72 #include <grub/misc.h>
73 #include <grub/emu/misc.h>
74 #include <grub/emu/hostdisk.h>
75 #include <grub/emu/getroot.h>
76
77 #ifdef __linux__
78 # include <sys/ioctl.h> /* ioctl */
79 # include <sys/mount.h>
80 # ifndef MAJOR
81 # ifndef MINORBITS
82 # define MINORBITS 8
83 # endif /* ! MINORBITS */
84 # define MAJOR(dev) ((unsigned) ((dev) >> MINORBITS))
85 # endif /* ! MAJOR */
86 # ifndef FLOPPY_MAJOR
87 # define FLOPPY_MAJOR 2
88 # endif /* ! FLOPPY_MAJOR */
89 #endif
90
91 #ifdef __CYGWIN__
92 # include <sys/ioctl.h>
93 # include <cygwin/fs.h> /* BLKGETSIZE64 */
94 # include <cygwin/hdreg.h> /* HDIO_GETGEO */
95 # include <sys/cygwin.h>
96
97 # define MAJOR(dev) ((unsigned) ((dev) >> 16))
98 # define FLOPPY_MAJOR 2
99 #endif
100
101 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
102 # include <sys/disk.h> /* DIOCGMEDIASIZE */
103 # include <sys/param.h>
104 # include <sys/sysctl.h>
105 # include <sys/mount.h>
106 #include <libgeom.h>
107 # define MAJOR(dev) major(dev)
108 # define FLOPPY_MAJOR 2
109 #endif
110
111 #if defined (__sun__)
112 # include <sys/dkio.h>
113 #endif
114
115 #if defined(__APPLE__)
116 # include <sys/disk.h>
117 # include <sys/param.h>
118 # include <sys/sysctl.h>
119 # include <sys/mount.h>
120 #endif
121
122 #ifdef HAVE_DEVICE_MAPPER
123 # include <libdevmapper.h>
124 #endif
125
126 #if defined(__NetBSD__) || defined(__OpenBSD__)
127 # define HAVE_DIOCGDINFO
128 # include <sys/ioctl.h>
129 # include <sys/disklabel.h> /* struct disklabel */
130 # include <sys/disk.h> /* struct dkwedge_info */
131 #else /* !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) */
132 # undef HAVE_DIOCGDINFO
133 #endif /* defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
134
135 #if defined(__NetBSD__) || defined(__OpenBSD__)
136 # ifdef HAVE_GETRAWPARTITION
137 # include <util.h> /* getrawpartition */
138 # endif /* HAVE_GETRAWPARTITION */
139 #if defined(__NetBSD__)
140 # include <sys/fdio.h>
141 #endif
142 # ifndef FLOPPY_MAJOR
143 # define FLOPPY_MAJOR 2
144 # endif /* ! FLOPPY_MAJOR */
145 # ifndef RAW_FLOPPY_MAJOR
146 # define RAW_FLOPPY_MAJOR 9
147 # endif /* ! RAW_FLOPPY_MAJOR */
148 #endif /* defined(__NetBSD__) */
149
150 #ifdef __linux__
151 /* Defines taken from btrfs/ioctl.h. */
152
153 struct btrfs_ioctl_dev_info_args
154 {
155 grub_uint64_t devid;
156 grub_uint8_t uuid[16];
157 grub_uint64_t bytes_used;
158 grub_uint64_t total_bytes;
159 grub_uint64_t unused[379];
160 grub_uint8_t path[1024];
161 };
162
163 struct btrfs_ioctl_fs_info_args
164 {
165 grub_uint64_t max_id;
166 grub_uint64_t num_devices;
167 grub_uint8_t fsid[16];
168 grub_uint64_t reserved[124];
169 };
170
171 #define BTRFS_IOC_DEV_INFO _IOWR(0x94, 30, \
172 struct btrfs_ioctl_dev_info_args)
173 #define BTRFS_IOC_FS_INFO _IOR(0x94, 31, \
174 struct btrfs_ioctl_fs_info_args)
175 #endif
176
177 #ifdef __linux__
178 static int
179 grub_util_is_imsm (const char *os_dev);
180 #endif
181
182 #if ! defined(__CYGWIN__) && !defined(__GNU__)
183
184 static void
185 strip_extra_slashes (char *dir)
186 {
187 char *p = dir;
188
189 while ((p = strchr (p, '/')) != 0)
190 {
191 if (p[1] == '/')
192 {
193 memmove (p, p + 1, strlen (p));
194 continue;
195 }
196 else if (p[1] == '\0')
197 {
198 if (p > dir)
199 p[0] = '\0';
200 break;
201 }
202
203 p++;
204 }
205 }
206
207 static char *
208 xgetcwd (void)
209 {
210 size_t size = 10;
211 char *path;
212
213 path = xmalloc (size);
214 while (! getcwd (path, size))
215 {
216 size <<= 1;
217 path = xrealloc (path, size);
218 }
219
220 return path;
221 }
222
223 #endif
224
225 #if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__GNU__)
226
227 #include <sys/types.h>
228 #include <sys/wait.h>
229
230 static pid_t
231 exec_pipe (char **argv, int *fd)
232 {
233 int mdadm_pipe[2];
234 pid_t mdadm_pid;
235
236 *fd = 0;
237
238 if (pipe (mdadm_pipe) < 0)
239 {
240 grub_util_warn (_("Unable to create pipe: %s"),
241 strerror (errno));
242 return 0;
243 }
244 mdadm_pid = fork ();
245 if (mdadm_pid < 0)
246 grub_util_error (_("Unable to fork: %s"), strerror (errno));
247 else if (mdadm_pid == 0)
248 {
249 /* Child. */
250
251 /* Close fd's. */
252 #ifdef HAVE_DEVICE_MAPPER
253 dm_lib_release ();
254 #endif
255 grub_diskfilter_fini ();
256
257 /* Ensure child is not localised. */
258 setenv ("LC_ALL", "C", 1);
259
260 close (mdadm_pipe[0]);
261 dup2 (mdadm_pipe[1], STDOUT_FILENO);
262 close (mdadm_pipe[1]);
263
264 execvp (argv[0], argv);
265 exit (127);
266 }
267 else
268 {
269 close (mdadm_pipe[1]);
270 *fd = mdadm_pipe[0];
271 return mdadm_pid;
272 }
273 }
274
275 static char **
276 find_root_devices_from_poolname (char *poolname)
277 {
278 char **devices = 0;
279 size_t ndevices = 0;
280 size_t devices_allocated = 0;
281
282 #if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
283 zpool_handle_t *zpool;
284 libzfs_handle_t *libzfs;
285 nvlist_t *config, *vdev_tree;
286 nvlist_t **children;
287 unsigned int nvlist_count;
288 unsigned int i;
289 char *device = 0;
290
291 libzfs = grub_get_libzfs_handle ();
292 if (! libzfs)
293 return NULL;
294
295 zpool = zpool_open (libzfs, poolname);
296 config = zpool_get_config (zpool, NULL);
297
298 if (nvlist_lookup_nvlist (config, "vdev_tree", &vdev_tree) != 0)
299 error (1, errno, "nvlist_lookup_nvlist (\"vdev_tree\")");
300
301 if (nvlist_lookup_nvlist_array (vdev_tree, "children", &children, &nvlist_count) != 0)
302 error (1, errno, "nvlist_lookup_nvlist_array (\"children\")");
303 assert (nvlist_count > 0);
304
305 while (nvlist_lookup_nvlist_array (children[0], "children",
306 &children, &nvlist_count) == 0)
307 assert (nvlist_count > 0);
308
309 for (i = 0; i < nvlist_count; i++)
310 {
311 if (nvlist_lookup_string (children[i], "path", &device) != 0)
312 error (1, errno, "nvlist_lookup_string (\"path\")");
313
314 struct stat st;
315 if (stat (device, &st) == 0)
316 {
317 #ifdef __sun__
318 if (grub_memcmp (device, "/dev/dsk/", sizeof ("/dev/dsk/") - 1)
319 == 0)
320 device = xasprintf ("/dev/rdsk/%s",
321 device + sizeof ("/dev/dsk/") - 1);
322 else if (grub_memcmp (device, "/devices", sizeof ("/devices") - 1)
323 == 0
324 && grub_memcmp (device + strlen (device) - 4,
325 ",raw", 4) != 0)
326 device = xasprintf ("%s,raw", device);
327 else
328 #endif
329 device = xstrdup (device);
330 if (ndevices >= devices_allocated)
331 {
332 devices_allocated = 2 * (devices_allocated + 8);
333 devices = xrealloc (devices, sizeof (devices[0])
334 * devices_allocated);
335 }
336 devices[ndevices++] = device;
337 }
338
339 device = NULL;
340 }
341
342 zpool_close (zpool);
343 #else
344 FILE *fp;
345 int ret;
346 char *line;
347 size_t len;
348 int st;
349
350 char name[PATH_MAX + 1], state[257], readlen[257], writelen[257];
351 char cksum[257], notes[257];
352 unsigned int dummy;
353 char *argv[4];
354 pid_t pid;
355 int fd;
356
357 /* execvp has inconvenient types, hence the casts. None of these
358 strings will actually be modified. */
359 argv[0] = (char *) "zpool";
360 argv[1] = (char *) "status";
361 argv[2] = (char *) poolname;
362 argv[3] = NULL;
363
364 pid = exec_pipe (argv, &fd);
365 if (!pid)
366 return NULL;
367
368 fp = fdopen (fd, "r");
369 if (!fp)
370 {
371 grub_util_warn (_("Unable to open stream from %s: %s"),
372 "zpool", strerror (errno));
373 goto out;
374 }
375
376 st = 0;
377 while (1)
378 {
379 line = NULL;
380 ret = getline (&line, &len, fp);
381 if (ret == -1)
382 break;
383
384 if (sscanf (line, " %s %256s %256s %256s %256s %256s",
385 name, state, readlen, writelen, cksum, notes) >= 5)
386 switch (st)
387 {
388 case 0:
389 if (!strcmp (name, "NAME")
390 && !strcmp (state, "STATE")
391 && !strcmp (readlen, "READ")
392 && !strcmp (writelen, "WRITE")
393 && !strcmp (cksum, "CKSUM"))
394 st++;
395 break;
396 case 1:
397 {
398 char *ptr = line;
399 while (1)
400 {
401 if (strncmp (ptr, poolname, strlen (poolname)) == 0
402 && grub_isspace(ptr[strlen (poolname)]))
403 st++;
404 if (!grub_isspace (*ptr))
405 break;
406 ptr++;
407 }
408 }
409 break;
410 case 2:
411 if (strcmp (name, "mirror") && !sscanf (name, "mirror-%u", &dummy)
412 && !sscanf (name, "raidz%u", &dummy)
413 && !sscanf (name, "raidz1%u", &dummy)
414 && !sscanf (name, "raidz2%u", &dummy)
415 && !sscanf (name, "raidz3%u", &dummy)
416 && !strcmp (state, "ONLINE"))
417 {
418 if (ndevices >= devices_allocated)
419 {
420 devices_allocated = 2 * (devices_allocated + 8);
421 devices = xrealloc (devices, sizeof (devices[0])
422 * devices_allocated);
423 }
424 if (name[0] == '/')
425 devices[ndevices++] = xstrdup (name);
426 else
427 devices[ndevices++] = xasprintf ("/dev/%s", name);
428 }
429 break;
430 }
431
432 free (line);
433 }
434
435 out:
436 close (fd);
437 waitpid (pid, NULL, 0);
438 #endif
439 if (devices)
440 {
441 if (ndevices >= devices_allocated)
442 {
443 devices_allocated = 2 * (devices_allocated + 8);
444 devices = xrealloc (devices, sizeof (devices[0])
445 * devices_allocated);
446 }
447 devices[ndevices++] = 0;
448 }
449 return devices;
450 }
451
452 #endif
453
454 #ifdef __linux__
455
456 #define ESCAPED_PATH_MAX (4 * PATH_MAX)
457 struct mountinfo_entry
458 {
459 int id;
460 int major, minor;
461 char enc_root[ESCAPED_PATH_MAX + 1], enc_path[ESCAPED_PATH_MAX + 1];
462 char fstype[ESCAPED_PATH_MAX + 1], device[ESCAPED_PATH_MAX + 1];
463 };
464
465 /* Statting something on a btrfs filesystem always returns a virtual device
466 major/minor pair rather than the real underlying device, because btrfs
467 can span multiple underlying devices (and even if it's currently only
468 using a single device it can be dynamically extended onto another). We
469 can't deal with the multiple-device case yet, but in the meantime, we can
470 at least cope with the single-device case by scanning
471 /proc/self/mountinfo. */
472 static void
473 unescape (char *str)
474 {
475 char *optr;
476 const char *iptr;
477 for (iptr = optr = str; *iptr; optr++)
478 {
479 if (iptr[0] == '\\' && iptr[1] >= '0' && iptr[1] < '8'
480 && iptr[2] >= '0' && iptr[2] < '8'
481 && iptr[3] >= '0' && iptr[3] < '8')
482 {
483 *optr = (((iptr[1] - '0') << 6) | ((iptr[2] - '0') << 3)
484 | (iptr[3] - '0'));
485 iptr += 4;
486 }
487 else
488 *optr = *iptr++;
489 }
490 *optr = 0;
491 }
492
493 static char **
494 grub_find_root_devices_from_btrfs (const char *dir)
495 {
496 int fd;
497 struct btrfs_ioctl_fs_info_args fsi;
498 int i, j = 0;
499 char **ret;
500
501 fd = open (dir, 0);
502 if (!fd)
503 return NULL;
504
505 if (ioctl (fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
506 {
507 close (fd);
508 return NULL;
509 }
510
511 ret = xmalloc ((fsi.num_devices + 1) * sizeof (ret[0]));
512
513 for (i = 1; i <= fsi.max_id && j < fsi.num_devices; i++)
514 {
515 struct btrfs_ioctl_dev_info_args devi;
516 memset (&devi, 0, sizeof (devi));
517 devi.devid = i;
518 if (ioctl (fd, BTRFS_IOC_DEV_INFO, &devi) < 0)
519 {
520 close (fd);
521 free (ret);
522 return NULL;
523 }
524 ret[j++] = xstrdup ((char *) devi.path);
525 if (j >= fsi.num_devices)
526 break;
527 }
528 close (fd);
529 ret[j] = 0;
530 return ret;
531 }
532
533 static char **
534 grub_find_root_devices_from_mountinfo (const char *dir, char **relroot)
535 {
536 FILE *fp;
537 char *buf = NULL;
538 size_t len = 0;
539 grub_size_t entry_len = 0, entry_max = 4;
540 struct mountinfo_entry *entries;
541 struct mountinfo_entry parent_entry = { 0, 0, 0, "", "", "", "" };
542 int i;
543
544 if (! *dir)
545 dir = "/";
546 if (relroot)
547 *relroot = NULL;
548
549 fp = fopen ("/proc/self/mountinfo", "r");
550 if (! fp)
551 return NULL; /* fall through to other methods */
552
553 entries = xmalloc (entry_max * sizeof (*entries));
554
555 /* First, build a list of relevant visible mounts. */
556 while (getline (&buf, &len, fp) > 0)
557 {
558 struct mountinfo_entry entry;
559 int count;
560 size_t enc_path_len;
561 const char *sep;
562
563 if (sscanf (buf, "%d %d %u:%u %s %s%n",
564 &entry.id, &parent_entry.id, &entry.major, &entry.minor,
565 entry.enc_root, entry.enc_path, &count) < 6)
566 continue;
567
568 unescape (entry.enc_root);
569 unescape (entry.enc_path);
570
571 enc_path_len = strlen (entry.enc_path);
572 /* Check that enc_path is a prefix of dir. The prefix must either be
573 the entire string, or end with a slash, or be immediately followed
574 by a slash. */
575 if (strncmp (dir, entry.enc_path, enc_path_len) != 0 ||
576 (enc_path_len && dir[enc_path_len - 1] != '/' &&
577 dir[enc_path_len] && dir[enc_path_len] != '/'))
578 continue;
579
580 sep = strstr (buf + count, " - ");
581 if (!sep)
582 continue;
583
584 sep += sizeof (" - ") - 1;
585 if (sscanf (sep, "%s %s", entry.fstype, entry.device) != 2)
586 continue;
587
588 unescape (entry.device);
589
590 /* Using the mount IDs, find out where this fits in the list of
591 visible mount entries we've seen so far. There are three
592 interesting cases. Firstly, it may be inserted at the end: this is
593 the usual case of /foo/bar being mounted after /foo. Secondly, it
594 may be inserted at the start: for example, this can happen for
595 filesystems that are mounted before / and later moved under it.
596 Thirdly, it may occlude part or all of the existing filesystem
597 tree, in which case the end of the list needs to be pruned and this
598 new entry will be inserted at the end. */
599 if (entry_len >= entry_max)
600 {
601 entry_max <<= 1;
602 entries = xrealloc (entries, entry_max * sizeof (*entries));
603 }
604
605 if (!entry_len)
606 {
607 /* Initialise list. */
608 entry_len = 2;
609 entries[0] = parent_entry;
610 entries[1] = entry;
611 }
612 else
613 {
614 for (i = entry_len - 1; i >= 0; i--)
615 {
616 if (entries[i].id == parent_entry.id)
617 {
618 /* Insert at end, pruning anything previously above this. */
619 entry_len = i + 2;
620 entries[i + 1] = entry;
621 break;
622 }
623 else if (i == 0 && entries[i].id == entry.id)
624 {
625 /* Insert at start. */
626 entry_len++;
627 memmove (entries + 1, entries,
628 (entry_len - 1) * sizeof (*entries));
629 entries[0] = parent_entry;
630 entries[1] = entry;
631 break;
632 }
633 }
634 }
635 }
636
637 /* Now scan visible mounts for the ones we're interested in. */
638 for (i = entry_len - 1; i >= 0; i--)
639 {
640 char **ret = NULL;
641 if (!*entries[i].device)
642 continue;
643
644 if (grub_strcmp (entries[i].fstype, "fuse.zfs") == 0
645 || grub_strcmp (entries[i].fstype, "zfs") == 0)
646 {
647 char *slash;
648 slash = strchr (entries[i].device, '/');
649 if (slash)
650 *slash = 0;
651 ret = find_root_devices_from_poolname (entries[i].device);
652 if (slash)
653 *slash = '/';
654 if (relroot)
655 {
656 if (!slash)
657 *relroot = xasprintf ("/@%s", entries[i].enc_root);
658 else if (strchr (slash + 1, '@'))
659 *relroot = xasprintf ("/%s%s", slash + 1, entries[i].enc_root);
660 else
661 *relroot = xasprintf ("/%s@%s", slash + 1, entries[i].enc_root);
662 }
663 }
664 else if (grub_strcmp (entries[i].fstype, "btrfs") == 0)
665 {
666 ret = grub_find_root_devices_from_btrfs (dir);
667 if (relroot)
668 {
669 char *ptr;
670 *relroot = xmalloc (strlen (entries[i].enc_root) +
671 2 + strlen (dir));
672 ptr = grub_stpcpy (*relroot, entries[i].enc_root);
673 if (strlen (dir) > strlen (entries[i].enc_path))
674 {
675 while (ptr > *relroot && *(ptr - 1) == '/')
676 ptr--;
677 if (dir[strlen (entries[i].enc_path)] != '/')
678 *ptr++ = '/';
679 ptr = grub_stpcpy (ptr, dir + strlen (entries[i].enc_path));
680 }
681 *ptr = 0;
682 }
683 }
684 if (!ret)
685 {
686 ret = xmalloc (2 * sizeof (ret[0]));
687 ret[0] = strdup (entries[i].device);
688 ret[1] = 0;
689 if (relroot)
690 *relroot = strdup (entries[i].enc_root);
691 }
692 free (buf);
693 free (entries);
694 fclose (fp);
695 return ret;
696 }
697
698 free (buf);
699 free (entries);
700 fclose (fp);
701 return NULL;
702 }
703
704 #endif /* __linux__ */
705
706 #if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__GNU__)
707
708 static char **
709 find_root_devices_from_libzfs (const char *dir)
710 {
711 char **devices = NULL;
712 char *poolname;
713 char *poolfs;
714
715 grub_find_zpool_from_dir (dir, &poolname, &poolfs);
716 if (! poolname)
717 return NULL;
718
719 devices = find_root_devices_from_poolname (poolname);
720
721 free (poolname);
722 if (poolfs)
723 free (poolfs);
724
725 return devices;
726 }
727
728 #endif
729
730 #ifdef __MINGW32__
731
732 char *
733 grub_find_device (const char *dir __attribute__ ((unused)),
734 dev_t dev __attribute__ ((unused)))
735 {
736 return 0;
737 }
738
739 #elif defined (__GNU__)
740
741 static char *
742 find_hurd_root_device (const char *path)
743 {
744 file_t file;
745 error_t err;
746 char *argz = NULL, *name = NULL, *ret;
747 size_t argz_len = 0;
748 int i;
749
750 file = file_name_lookup (path, 0, 0);
751 if (file == MACH_PORT_NULL)
752 /* TRANSLATORS: The first %s is the file being looked at, the second %s is
753 the error message. */
754 grub_util_error (_("cannot open `%s': %s"), path, strerror (errno));
755
756 /* This returns catenated 0-terminated strings. */
757 err = file_get_fs_options (file, &argz, &argz_len);
758 if (err)
759 /* TRANSLATORS: On GNU/Hurd, a "translator" is similar to a filesystem
760 mount, but handled by a userland daemon, whose invocation command line
761 is being fetched here. First %s is the file being looked at (for which
762 we are fetching the "translator" command line), second %s is the error
763 message.
764 */
765 grub_util_error (_("cannot get translator command line "
766 "for path `%s': %s"), path, strerror(err));
767 if (argz_len == 0)
768 grub_util_error (_("translator command line is empty for path `%s'"), path);
769
770 /* Make sure the string is terminated. */
771 argz[argz_len-1] = 0;
772
773 /* Skip first word (translator path) and options. */
774 for (i = strlen (argz) + 1; i < argz_len; i += strlen (argz + i) + 1)
775 {
776 if (argz[i] != '-')
777 {
778 /* Non-option. Only accept one, assumed to be the FS path. */
779 /* XXX: this should be replaced by an RPC to the translator. */
780 if (name)
781 /* TRANSLATORS: we expect to get something like
782 /hurd/foobar --option1 --option2=baz /dev/something
783 */
784 grub_util_error (_("translator `%s' for path `%s' has several "
785 "non-option words, at least `%s' and `%s'"),
786 argz, path, name, argz + i);
787 name = argz + i;
788 }
789 }
790
791 if (!name)
792 /* TRANSLATORS: we expect to get something like
793 /hurd/foobar --option1 --option2=baz /dev/something
794 */
795 grub_util_error (_("translator `%s' for path `%s' is given only options, "
796 "cannot find device part"), argz, path);
797
798 if (strncmp (name, "device:", sizeof ("device:") - 1) == 0)
799 {
800 char *dev_name = name + sizeof ("device:") - 1;
801 size_t size = sizeof ("/dev/") - 1 + strlen (dev_name) + 1;
802 char *next;
803 ret = malloc (size);
804 next = stpncpy (ret, "/dev/", size);
805 stpncpy (next, dev_name, size - (next - ret));
806 }
807 else if (!strncmp (name, "file:", sizeof ("file:") - 1))
808 ret = strdup (name + sizeof ("file:") - 1);
809 else
810 ret = strdup (name);
811
812 munmap (argz, argz_len);
813 return ret;
814 }
815
816 #elif ! defined(__CYGWIN__)
817
818 char *
819 grub_find_device (const char *dir, dev_t dev)
820 {
821 DIR *dp;
822 char *saved_cwd;
823 struct dirent *ent;
824
825 if (! dir)
826 {
827 #ifdef __CYGWIN__
828 return NULL;
829 #else
830 dir = "/dev";
831 #endif
832 }
833
834 dp = opendir (dir);
835 if (! dp)
836 return 0;
837
838 saved_cwd = xgetcwd ();
839
840 grub_util_info ("changing current directory to %s", dir);
841 if (chdir (dir) < 0)
842 {
843 free (saved_cwd);
844 closedir (dp);
845 return 0;
846 }
847
848 while ((ent = readdir (dp)) != 0)
849 {
850 struct stat st;
851
852 /* Avoid:
853 - dotfiles (like "/dev/.tmp.md0") since they could be duplicates.
854 - dotdirs (like "/dev/.static") since they could contain duplicates. */
855 if (ent->d_name[0] == '.')
856 continue;
857
858 if (lstat (ent->d_name, &st) < 0)
859 /* Ignore any error. */
860 continue;
861
862 if (S_ISLNK (st.st_mode)) {
863 #ifdef __linux__
864 if (strcmp (dir, "mapper") == 0 || strcmp (dir, "/dev/mapper") == 0) {
865 /* Follow symbolic links under /dev/mapper/; the canonical name
866 may be something like /dev/dm-0, but the names under
867 /dev/mapper/ are more human-readable and so we prefer them if
868 we can get them. */
869 if (stat (ent->d_name, &st) < 0)
870 continue;
871 } else
872 #endif /* __linux__ */
873 /* Don't follow other symbolic links. */
874 continue;
875 }
876
877 if (S_ISDIR (st.st_mode))
878 {
879 /* Find it recursively. */
880 char *res;
881
882 res = grub_find_device (ent->d_name, dev);
883
884 if (res)
885 {
886 if (chdir (saved_cwd) < 0)
887 grub_util_error ("%s",
888 _("cannot restore the original directory"));
889
890 free (saved_cwd);
891 closedir (dp);
892 return res;
893 }
894 }
895
896 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
897 if (S_ISCHR (st.st_mode) && st.st_rdev == dev)
898 #else
899 if (S_ISBLK (st.st_mode) && st.st_rdev == dev)
900 #endif
901 {
902 #ifdef __linux__
903 /* Skip device names like /dev/dm-0, which are short-hand aliases
904 to more descriptive device names, e.g. those under /dev/mapper */
905 if (ent->d_name[0] == 'd' &&
906 ent->d_name[1] == 'm' &&
907 ent->d_name[2] == '-' &&
908 ent->d_name[3] >= '0' &&
909 ent->d_name[3] <= '9')
910 continue;
911 #endif
912
913 /* Found! */
914 char *res;
915 char *cwd;
916 #if defined(__NetBSD__)
917 /* Convert this block device to its character (raw) device. */
918 const char *template = "%s/r%s";
919 #else
920 /* Keep the device name as it is. */
921 const char *template = "%s/%s";
922 #endif
923
924 cwd = xgetcwd ();
925 res = xmalloc (strlen (cwd) + strlen (ent->d_name) + 3);
926 sprintf (res, template, cwd, ent->d_name);
927 strip_extra_slashes (res);
928 free (cwd);
929
930 /* /dev/root is not a real block device keep looking, takes care
931 of situation where root filesystem is on the same partition as
932 grub files */
933
934 if (strcmp(res, "/dev/root") == 0)
935 {
936 free (res);
937 continue;
938 }
939
940 if (chdir (saved_cwd) < 0)
941 grub_util_error ("%s", _("cannot restore the original directory"));
942
943 free (saved_cwd);
944 closedir (dp);
945 return res;
946 }
947 }
948
949 if (chdir (saved_cwd) < 0)
950 grub_util_error ("%s", _("cannot restore the original directory"));
951
952 free (saved_cwd);
953 closedir (dp);
954 return 0;
955 }
956
957 #else /* __CYGWIN__ */
958
959 /* Read drive/partition serial number from mbr/boot sector,
960 return 0 on read error, ~0 on unknown serial. */
961 static unsigned
962 get_bootsec_serial (const char *os_dev, int mbr)
963 {
964 /* Read boot sector. */
965 int fd = open (os_dev, O_RDONLY);
966 if (fd < 0)
967 return 0;
968 unsigned char buf[0x200];
969 int n = read (fd, buf, sizeof (buf));
970 close (fd);
971 if (n != sizeof(buf))
972 return 0;
973
974 /* Check signature. */
975 if (!(buf[0x1fe] == 0x55 && buf[0x1ff] == 0xaa))
976 return ~0;
977
978 /* Serial number offset depends on boot sector type. */
979 if (mbr)
980 n = 0x1b8;
981 else if (memcmp (buf + 0x03, "NTFS", 4) == 0)
982 n = 0x048;
983 else if (memcmp (buf + 0x52, "FAT32", 5) == 0)
984 n = 0x043;
985 else if (memcmp (buf + 0x36, "FAT", 3) == 0)
986 n = 0x027;
987 else
988 return ~0;
989
990 unsigned serial = *(unsigned *)(buf + n);
991 if (serial == 0)
992 return ~0;
993 return serial;
994 }
995
996 #pragma GCC diagnostic warning "-Wdeprecated-declarations"
997
998 char *
999 grub_find_device (const char *path, dev_t dev)
1000 {
1001 /* No root device for /cygdrive. */
1002 if (dev == (DEV_CYGDRIVE_MAJOR << 16))
1003 return 0;
1004
1005 /* Convert to full POSIX and Win32 path. */
1006 char fullpath[PATH_MAX], winpath[PATH_MAX];
1007
1008 cygwin_conv_path (CCP_WIN_A_TO_POSIX, path, fullpath, sizeof (fullpath));
1009 cygwin_conv_path (CCP_POSIX_TO_WIN_A, fullpath, winpath, sizeof (winpath));
1010
1011 /* If identical, this is no real filesystem path. */
1012 if (strcmp (fullpath, winpath) == 0)
1013 return 0;
1014
1015 /* Check for floppy drive letter. */
1016 if (winpath[0] && winpath[1] == ':' && strchr ("AaBb", winpath[0]))
1017 return xstrdup (strchr ("Aa", winpath[0]) ? "/dev/fd0" : "/dev/fd1");
1018
1019 /* Cygwin returns the partition serial number in stat.st_dev.
1020 This is never identical to the device number of the emulated
1021 /dev/sdXN device, so above grub_find_device () does not work.
1022 Search the partition with the same serial in boot sector instead. */
1023 char devpath[sizeof ("/dev/sda15") + 13]; /* Size + Paranoia. */
1024 int d;
1025 for (d = 'a'; d <= 'z'; d++)
1026 {
1027 sprintf (devpath, "/dev/sd%c", d);
1028 if (get_bootsec_serial (devpath, 1) == 0)
1029 continue;
1030 int p;
1031 for (p = 1; p <= 15; p++)
1032 {
1033 sprintf (devpath, "/dev/sd%c%d", d, p);
1034 unsigned ser = get_bootsec_serial (devpath, 0);
1035 if (ser == 0)
1036 break;
1037 if (ser != (unsigned)~0 && dev == (dev_t)ser)
1038 return xstrdup (devpath);
1039 }
1040 }
1041 return 0;
1042 }
1043
1044 #endif /* __CYGWIN__ */
1045
1046 char **
1047 grub_guess_root_devices (const char *dir)
1048 {
1049 char **os_dev = NULL;
1050 #ifndef __GNU__
1051 struct stat st;
1052 dev_t dev;
1053
1054 #ifdef __linux__
1055 if (!os_dev)
1056 os_dev = grub_find_root_devices_from_mountinfo (dir, NULL);
1057 #endif /* __linux__ */
1058
1059 #if !defined (__MINGW32__) && !defined (__CYGWIN__)
1060 if (!os_dev)
1061 os_dev = find_root_devices_from_libzfs (dir);
1062 #endif
1063
1064 if (os_dev)
1065 {
1066 char **cur;
1067 for (cur = os_dev; *cur; cur++)
1068 {
1069 char *tmp = *cur;
1070 int root, dm;
1071 if (strcmp (*cur, "/dev/root") == 0
1072 || strncmp (*cur, "/dev/dm-", sizeof ("/dev/dm-") - 1) == 0)
1073 *cur = tmp;
1074 else
1075 {
1076 *cur = canonicalize_file_name (tmp);
1077 if (*cur == NULL)
1078 grub_util_error (_("failed to get canonical path of `%s'"), tmp);
1079 free (tmp);
1080 }
1081 root = (strcmp (*cur, "/dev/root") == 0);
1082 dm = (strncmp (*cur, "/dev/dm-", sizeof ("/dev/dm-") - 1) == 0);
1083 if (!dm && !root)
1084 continue;
1085 if (stat (*cur, &st) < 0)
1086 break;
1087 free (*cur);
1088 dev = st.st_rdev;
1089 *cur = grub_find_device (dm ? "/dev/mapper" : "/dev", dev);
1090 }
1091 if (!*cur)
1092 return os_dev;
1093 for (cur = os_dev; *cur; cur++)
1094 free (*cur);
1095 free (os_dev);
1096 os_dev = 0;
1097 }
1098
1099 if (stat (dir, &st) < 0)
1100 grub_util_error (_("cannot stat `%s': %s"), dir, strerror (errno));
1101
1102 dev = st.st_dev;
1103 #endif /* !__GNU__ */
1104
1105 os_dev = xmalloc (2 * sizeof (os_dev[0]));
1106
1107 #ifdef __CYGWIN__
1108 /* Cygwin specific function. */
1109 os_dev[0] = grub_find_device (dir, dev);
1110
1111 #elif defined __GNU__
1112 /* GNU/Hurd specific function. */
1113 os_dev[0] = find_hurd_root_device (dir);
1114
1115 #else
1116
1117 /* This might be truly slow, but is there any better way? */
1118 os_dev[0] = grub_find_device ("/dev", dev);
1119 #endif
1120 if (!os_dev[0])
1121 {
1122 free (os_dev);
1123 return 0;
1124 }
1125
1126 os_dev[1] = 0;
1127
1128 return os_dev;
1129 }
1130
1131 #ifdef HAVE_DEVICE_MAPPER
1132
1133 static int
1134 grub_util_open_dm (const char *os_dev, struct dm_tree **tree,
1135 struct dm_tree_node **node)
1136 {
1137 uint32_t maj, min;
1138 struct stat st;
1139
1140 *node = NULL;
1141 *tree = NULL;
1142
1143 if (stat (os_dev, &st) < 0)
1144 return 0;
1145
1146 maj = major (st.st_rdev);
1147 min = minor (st.st_rdev);
1148
1149 if (!dm_is_dm_major (maj))
1150 return 0;
1151
1152 *tree = dm_tree_create ();
1153 if (! *tree)
1154 {
1155 grub_puts_ (N_("Failed to create `device-mapper' tree"));
1156 grub_dprintf ("hostdisk", "dm_tree_create failed\n");
1157 return 0;
1158 }
1159
1160 if (! dm_tree_add_dev (*tree, maj, min))
1161 {
1162 grub_dprintf ("hostdisk", "dm_tree_add_dev failed\n");
1163 dm_tree_free (*tree);
1164 *tree = NULL;
1165 return 0;
1166 }
1167
1168 *node = dm_tree_find_node (*tree, maj, min);
1169 if (! *node)
1170 {
1171 grub_dprintf ("hostdisk", "dm_tree_find_node failed\n");
1172 dm_tree_free (*tree);
1173 *tree = NULL;
1174 return 0;
1175 }
1176 return 1;
1177 }
1178
1179 #endif
1180
1181 #ifdef HAVE_DEVICE_MAPPER
1182 static char *
1183 get_dm_uuid (const char *os_dev)
1184 {
1185 struct dm_tree *tree;
1186 struct dm_tree_node *node;
1187 const char *node_uuid;
1188 char *ret;
1189
1190 if (!grub_util_open_dm (os_dev, &tree, &node))
1191 return NULL;
1192
1193 node_uuid = dm_tree_node_get_uuid (node);
1194 if (! node_uuid)
1195 {
1196 grub_dprintf ("hostdisk", "%s has no DM uuid\n", os_dev);
1197 dm_tree_free (tree);
1198 return NULL;
1199 }
1200
1201 ret = grub_strdup (node_uuid);
1202
1203 dm_tree_free (tree);
1204
1205 return ret;
1206 }
1207 #endif
1208
1209 #ifdef __linux__
1210
1211 static enum grub_dev_abstraction_types
1212 grub_util_get_dm_abstraction (const char *os_dev)
1213 {
1214 #ifdef HAVE_DEVICE_MAPPER
1215 char *uuid;
1216
1217 uuid = get_dm_uuid (os_dev);
1218
1219 if (uuid == NULL)
1220 return GRUB_DEV_ABSTRACTION_NONE;
1221
1222 if (strncmp (uuid, "LVM-", 4) == 0)
1223 {
1224 grub_free (uuid);
1225 return GRUB_DEV_ABSTRACTION_LVM;
1226 }
1227 if (strncmp (uuid, "CRYPT-LUKS1-", 4) == 0)
1228 {
1229 grub_free (uuid);
1230 return GRUB_DEV_ABSTRACTION_LUKS;
1231 }
1232
1233 grub_free (uuid);
1234 return GRUB_DEV_ABSTRACTION_NONE;
1235 #else
1236 if ((strncmp ("/dev/mapper/", os_dev, 12) != 0))
1237 return GRUB_DEV_ABSTRACTION_NONE;
1238 return GRUB_DEV_ABSTRACTION_LVM;
1239 #endif
1240 }
1241
1242 #endif
1243
1244 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
1245 #include <libgeom.h>
1246
1247 static const char *
1248 grub_util_get_geom_abstraction (const char *dev)
1249 {
1250 char *whole;
1251 struct gmesh mesh;
1252 struct gclass *class;
1253 const char *name;
1254 int err;
1255
1256 if (strncmp (dev, "/dev/", sizeof ("/dev/") - 1) != 0)
1257 return 0;
1258 name = dev + sizeof ("/dev/") - 1;
1259 grub_util_follow_gpart_up (name, NULL, &whole);
1260
1261 grub_util_info ("following geom '%s'", name);
1262
1263 err = geom_gettree (&mesh);
1264 if (err != 0)
1265 /* TRANSLATORS: geom is the name of (k)FreeBSD device framework.
1266 Usually left untranslated.
1267 */
1268 grub_util_error ("%s", _("couldn't open geom"));
1269
1270 LIST_FOREACH (class, &mesh.lg_class, lg_class)
1271 {
1272 struct ggeom *geom;
1273 LIST_FOREACH (geom, &class->lg_geom, lg_geom)
1274 {
1275 struct gprovider *provider;
1276 LIST_FOREACH (provider, &geom->lg_provider, lg_provider)
1277 if (strcmp (provider->lg_name, name) == 0)
1278 return class->lg_name;
1279 }
1280 }
1281 return NULL;
1282 }
1283 #endif
1284
1285 int
1286 grub_util_get_dev_abstraction (const char *os_dev)
1287 {
1288 #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1289 /* User explicitly claims that this drive is visible by BIOS. */
1290 if (grub_util_biosdisk_is_present (os_dev))
1291 return GRUB_DEV_ABSTRACTION_NONE;
1292 #endif
1293
1294 #ifdef __linux__
1295 enum grub_dev_abstraction_types ret;
1296
1297 /* Check for LVM and LUKS. */
1298 ret = grub_util_get_dm_abstraction (os_dev);
1299
1300 if (ret != GRUB_DEV_ABSTRACTION_NONE)
1301 return ret;
1302
1303 /* Check for RAID. */
1304 if (!strncmp (os_dev, "/dev/md", 7) && ! grub_util_device_is_mapped (os_dev)
1305 && !grub_util_is_imsm (os_dev))
1306 return GRUB_DEV_ABSTRACTION_RAID;
1307 #endif
1308
1309 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
1310 const char *abstrac;
1311 abstrac = grub_util_get_geom_abstraction (os_dev);
1312 grub_util_info ("abstraction of %s is %s", os_dev, abstrac);
1313 if (abstrac && grub_strcasecmp (abstrac, "eli") == 0)
1314 return GRUB_DEV_ABSTRACTION_GELI;
1315
1316 /* Check for LVM. */
1317 if (!strncmp (os_dev, LVM_DEV_MAPPER_STRING, sizeof(LVM_DEV_MAPPER_STRING)-1))
1318 return GRUB_DEV_ABSTRACTION_LVM;
1319 #endif
1320
1321 /* No abstraction found. */
1322 return GRUB_DEV_ABSTRACTION_NONE;
1323 }
1324
1325 #if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__GNU__)
1326
1327 static void
1328 pull_lvm_by_command (const char *os_dev)
1329 {
1330 char *argv[8];
1331 int fd;
1332 pid_t pid;
1333 FILE *mdadm;
1334 char *buf = NULL;
1335 size_t len = 0;
1336 char *vgname;
1337 const char *iptr;
1338 char *optr;
1339
1340 if (strncmp (os_dev, "/dev/mapper/", sizeof ("/dev/mapper/") - 1)
1341 != 0)
1342 return;
1343
1344 vgname = xmalloc (strlen (os_dev + sizeof ("/dev/mapper/") - 1) + 1);
1345 for (iptr = os_dev + sizeof ("/dev/mapper/") - 1, optr = vgname; *iptr; )
1346 if (*iptr != '-')
1347 *optr++ = *iptr++;
1348 else if (iptr[0] == '-' && iptr[1] == '-')
1349 {
1350 iptr += 2;
1351 *optr++ = '-';
1352 }
1353 else
1354 break;
1355 *optr = '\0';
1356
1357 /* execvp has inconvenient types, hence the casts. None of these
1358 strings will actually be modified. */
1359 /* by default PV name is left aligned in 10 character field, meaning that
1360 we do not know where name ends. Using dummy --separator disables
1361 alignment. We have a single field, so separator itself is not output */
1362 argv[0] = (char *) "vgs";
1363 argv[1] = (char *) "--options";
1364 argv[2] = (char *) "pv_name";
1365 argv[3] = (char *) "--noheadings";
1366 argv[4] = (char *) "--separator";
1367 argv[5] = (char *) ":";
1368 argv[6] = vgname;
1369 argv[7] = NULL;
1370
1371 pid = exec_pipe (argv, &fd);
1372 free (vgname);
1373
1374 if (!pid)
1375 return;
1376
1377 /* Parent. Read mdadm's output. */
1378 mdadm = fdopen (fd, "r");
1379 if (! mdadm)
1380 {
1381 grub_util_warn (_("Unable to open stream from %s: %s"),
1382 "vgs", strerror (errno));
1383 goto out;
1384 }
1385
1386 while (getline (&buf, &len, mdadm) > 0)
1387 {
1388 char *ptr;
1389 /* LVM adds two spaces as standard prefix */
1390 for (ptr = buf; ptr < buf + 2 && *ptr == ' '; ptr++);
1391 if (*ptr == '\0')
1392 continue;
1393 *(ptr + strlen (ptr) - 1) = '\0';
1394 grub_util_pull_device (ptr);
1395 }
1396
1397 out:
1398 close (fd);
1399 waitpid (pid, NULL, 0);
1400 free (buf);
1401 }
1402
1403 #endif
1404
1405 #ifdef __linux__
1406 static char *
1407 get_mdadm_uuid (const char *os_dev)
1408 {
1409 char *argv[5];
1410 int fd;
1411 pid_t pid;
1412 FILE *mdadm;
1413 char *buf = NULL;
1414 size_t len = 0;
1415 char *name = NULL;
1416
1417 /* execvp has inconvenient types, hence the casts. None of these
1418 strings will actually be modified. */
1419 argv[0] = (char *) "mdadm";
1420 argv[1] = (char *) "--detail";
1421 argv[2] = (char *) "--export";
1422 argv[3] = (char *) os_dev;
1423 argv[4] = NULL;
1424
1425 pid = exec_pipe (argv, &fd);
1426
1427 if (!pid)
1428 return NULL;
1429
1430 /* Parent. Read mdadm's output. */
1431 mdadm = fdopen (fd, "r");
1432 if (! mdadm)
1433 {
1434 grub_util_warn (_("Unable to open stream from %s: %s"),
1435 "mdadm", strerror (errno));
1436 goto out;
1437 }
1438
1439 while (getline (&buf, &len, mdadm) > 0)
1440 {
1441 if (strncmp (buf, "MD_UUID=", sizeof ("MD_UUID=") - 1) == 0)
1442 {
1443 char *name_start, *ptri, *ptro;
1444
1445 free (name);
1446 name_start = buf + sizeof ("MD_UUID=") - 1;
1447 ptro = name = xmalloc (strlen (name_start) + 1);
1448 for (ptri = name_start; *ptri && *ptri != '\n' && *ptri != '\r';
1449 ptri++)
1450 if ((*ptri >= '0' && *ptri <= '9')
1451 || (*ptri >= 'a' && *ptri <= 'f')
1452 || (*ptri >= 'A' && *ptri <= 'F'))
1453 *ptro++ = *ptri;
1454 *ptro = 0;
1455 }
1456 }
1457
1458 out:
1459 close (fd);
1460 waitpid (pid, NULL, 0);
1461 free (buf);
1462
1463 return name;
1464 }
1465
1466 static int
1467 grub_util_is_imsm (const char *os_dev)
1468 {
1469 int retry;
1470 int is_imsm = 0;
1471 int container_seen = 0;
1472 const char *dev = os_dev;
1473
1474 do
1475 {
1476 char *argv[5];
1477 int fd;
1478 pid_t pid;
1479 FILE *mdadm;
1480 char *buf = NULL;
1481 size_t len = 0;
1482
1483 retry = 0; /* We'll do one more pass if device is part of container */
1484
1485 /* execvp has inconvenient types, hence the casts. None of these
1486 strings will actually be modified. */
1487 argv[0] = (char *) "mdadm";
1488 argv[1] = (char *) "--detail";
1489 argv[2] = (char *) "--export";
1490 argv[3] = (char *) dev;
1491 argv[4] = NULL;
1492
1493 pid = exec_pipe (argv, &fd);
1494
1495 if (!pid)
1496 {
1497 if (dev != os_dev)
1498 free ((void *) dev);
1499 return 0;
1500 }
1501
1502 /* Parent. Read mdadm's output. */
1503 mdadm = fdopen (fd, "r");
1504 if (! mdadm)
1505 {
1506 grub_util_warn (_("Unable to open stream from %s: %s"),
1507 "mdadm", strerror (errno));
1508 close (fd);
1509 waitpid (pid, NULL, 0);
1510 if (dev != os_dev)
1511 free ((void *) dev);
1512 return 0;
1513 }
1514
1515 while (getline (&buf, &len, mdadm) > 0)
1516 {
1517 if (strncmp (buf, "MD_CONTAINER=", sizeof ("MD_CONTAINER=") - 1) == 0
1518 && !container_seen)
1519 {
1520 char *newdev, *ptr;
1521 newdev = xstrdup (buf + sizeof ("MD_CONTAINER=") - 1);
1522 ptr = newdev + strlen (newdev) - 1;
1523 for (; ptr >= newdev && (*ptr == '\n' || *ptr == '\r'); ptr--);
1524 ptr[1] = 0;
1525 grub_util_info ("Container of %s is %s", dev, newdev);
1526 dev = newdev;
1527 container_seen = retry = 1;
1528 break;
1529 }
1530 if (strncmp (buf, "MD_METADATA=imsm",
1531 sizeof ("MD_METADATA=imsm") - 1) == 0)
1532 {
1533 is_imsm = 1;
1534 grub_util_info ("%s is imsm", dev);
1535 break;
1536 }
1537 }
1538
1539 free (buf);
1540 close (fd);
1541 waitpid (pid, NULL, 0);
1542 }
1543 while (retry);
1544
1545 if (dev != os_dev)
1546 free ((void *) dev);
1547 return is_imsm;
1548 }
1549 #endif /* __linux__ */
1550
1551 void
1552 grub_util_pull_device (const char *os_dev)
1553 {
1554 int ab;
1555 ab = grub_util_get_dev_abstraction (os_dev);
1556 switch (ab)
1557 {
1558 case GRUB_DEV_ABSTRACTION_GELI:
1559 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
1560 {
1561 char *whole;
1562 struct gmesh mesh;
1563 struct gclass *class;
1564 const char *name;
1565 int err;
1566 char *lastsubdev = NULL;
1567
1568 if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0)
1569 return;
1570 name = os_dev + sizeof ("/dev/") - 1;
1571 grub_util_follow_gpart_up (name, NULL, &whole);
1572
1573 grub_util_info ("following geom '%s'", name);
1574
1575 err = geom_gettree (&mesh);
1576 if (err != 0)
1577 /* TRANSLATORS: geom is the name of (k)FreeBSD device framework.
1578 Usually left untranslated.
1579 */
1580 grub_util_error ("%s", _("couldn't open geom"));
1581
1582 LIST_FOREACH (class, &mesh.lg_class, lg_class)
1583 {
1584 struct ggeom *geom;
1585 LIST_FOREACH (geom, &class->lg_geom, lg_geom)
1586 {
1587 struct gprovider *provider;
1588 LIST_FOREACH (provider, &geom->lg_provider, lg_provider)
1589 if (strcmp (provider->lg_name, name) == 0)
1590 {
1591 struct gconsumer *consumer;
1592 char *fname;
1593
1594 LIST_FOREACH (consumer, &geom->lg_consumer, lg_consumer)
1595 break;
1596 if (!consumer)
1597 grub_util_error ("%s",
1598 _("couldn't find geli consumer"));
1599 fname = xasprintf ("/dev/%s", consumer->lg_provider->lg_name);
1600 grub_util_info ("consumer %s", consumer->lg_provider->lg_name);
1601 lastsubdev = consumer->lg_provider->lg_name;
1602 grub_util_pull_device (fname);
1603 free (fname);
1604 }
1605 }
1606 }
1607 if (ab == GRUB_DEV_ABSTRACTION_GELI && lastsubdev)
1608 {
1609 char *fname = xasprintf ("/dev/%s", lastsubdev);
1610 char *grdev = grub_util_get_grub_dev (fname);
1611 free (fname);
1612
1613 if (grdev)
1614 {
1615 grub_err_t gr_err;
1616 gr_err = grub_cryptodisk_cheat_mount (grdev, os_dev);
1617 if (gr_err)
1618 grub_util_error (_("can't mount encrypted volume `%s': %s"),
1619 lastsubdev, grub_errmsg);
1620 }
1621
1622 grub_free (grdev);
1623 }
1624 }
1625 #endif
1626 break;
1627
1628 case GRUB_DEV_ABSTRACTION_LVM:
1629 #if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__GNU__)
1630 pull_lvm_by_command (os_dev);
1631 #endif
1632 /* Fallthrough in case that lvm-tools are unavailable. */
1633 case GRUB_DEV_ABSTRACTION_LUKS:
1634 #ifdef HAVE_DEVICE_MAPPER
1635 {
1636 struct dm_tree *tree;
1637 struct dm_tree_node *node;
1638 struct dm_tree_node *child;
1639 void *handle = NULL;
1640 char *lastsubdev = NULL;
1641
1642 if (!grub_util_open_dm (os_dev, &tree, &node))
1643 return;
1644
1645 while ((child = dm_tree_next_child (&handle, node, 0)))
1646 {
1647 const struct dm_info *dm = dm_tree_node_get_info (child);
1648 char *subdev;
1649 if (!dm)
1650 continue;
1651 subdev = grub_find_device ("/dev", makedev (dm->major, dm->minor));
1652 if (subdev)
1653 {
1654 lastsubdev = subdev;
1655 grub_util_pull_device (subdev);
1656 }
1657 }
1658 if (ab == GRUB_DEV_ABSTRACTION_LUKS && lastsubdev)
1659 {
1660 char *grdev = grub_util_get_grub_dev (lastsubdev);
1661 dm_tree_free (tree);
1662 if (grdev)
1663 {
1664 grub_err_t err;
1665 err = grub_cryptodisk_cheat_mount (grdev, os_dev);
1666 if (err)
1667 grub_util_error (_("can't mount encrypted volume `%s': %s"),
1668 lastsubdev, grub_errmsg);
1669 }
1670 grub_free (grdev);
1671 }
1672 else
1673 dm_tree_free (tree);
1674 }
1675 #endif
1676 return;
1677 case GRUB_DEV_ABSTRACTION_RAID:
1678 #ifdef __linux__
1679 {
1680 char **devicelist = grub_util_raid_getmembers (os_dev, 0);
1681 int i;
1682 for (i = 0; devicelist[i];i++)
1683 {
1684 grub_util_pull_device (devicelist[i]);
1685 free (devicelist[i]);
1686 }
1687 free (devicelist);
1688 }
1689 #endif
1690 return;
1691
1692 default: /* GRUB_DEV_ABSTRACTION_NONE */
1693 free (grub_util_biosdisk_get_grub_dev (os_dev));
1694 return;
1695 }
1696 }
1697
1698 int
1699 grub_util_biosdisk_is_floppy (grub_disk_t disk)
1700 {
1701 struct stat st;
1702 int fd;
1703 const char *dname;
1704
1705 dname = grub_util_biosdisk_get_osdev (disk);
1706
1707 if (!dname)
1708 return 0;
1709
1710 fd = open (dname, O_RDONLY);
1711 /* Shouldn't happen. */
1712 if (fd == -1)
1713 return 0;
1714
1715 /* Shouldn't happen either. */
1716 if (fstat (fd, &st) < 0)
1717 {
1718 close (fd);
1719 return 0;
1720 }
1721
1722 close (fd);
1723
1724 #if defined(__NetBSD__)
1725 if (major(st.st_rdev) == RAW_FLOPPY_MAJOR)
1726 return 1;
1727 #endif
1728
1729 #if defined(FLOPPY_MAJOR)
1730 if (major(st.st_rdev) == FLOPPY_MAJOR)
1731 #else
1732 /* Some kernels (e.g. kFreeBSD) don't have a static major number
1733 for floppies, but they still use a "fd[0-9]" pathname. */
1734 if (dname[5] == 'f'
1735 && dname[6] == 'd'
1736 && dname[7] >= '0'
1737 && dname[7] <= '9')
1738 #endif
1739 return 1;
1740
1741 return 0;
1742 }
1743
1744 static char *
1745 convert_system_partition_to_system_disk (const char *os_dev, struct stat *st,
1746 int *is_part)
1747 {
1748 *is_part = 0;
1749
1750 #if defined(__linux__)
1751 char *path = xmalloc (PATH_MAX);
1752
1753 if (! realpath (os_dev, path))
1754 return NULL;
1755
1756 if (strncmp ("/dev/", path, 5) == 0)
1757 {
1758 char *p = path + 5;
1759
1760 /* If this is an IDE disk. */
1761 if (strncmp ("ide/", p, 4) == 0)
1762 {
1763 p = strstr (p, "part");
1764 if (p)
1765 {
1766 *is_part = 1;
1767 strcpy (p, "disc");
1768 }
1769
1770 return path;
1771 }
1772
1773 /* If this is a SCSI disk. */
1774 if (strncmp ("scsi/", p, 5) == 0)
1775 {
1776 p = strstr (p, "part");
1777 if (p)
1778 {
1779 *is_part = 1;
1780 strcpy (p, "disc");
1781 }
1782
1783 return path;
1784 }
1785
1786 /* If this is a DAC960 disk. */
1787 if (strncmp ("rd/c", p, 4) == 0)
1788 {
1789 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
1790 p = strchr (p, 'p');
1791 if (p)
1792 {
1793 *is_part = 1;
1794 *p = '\0';
1795 }
1796
1797 return path;
1798 }
1799
1800 /* If this is a Mylex AcceleRAID Array. */
1801 if (strncmp ("rs/c", p, 4) == 0)
1802 {
1803 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
1804 p = strchr (p, 'p');
1805 if (p)
1806 {
1807 *is_part = 1;
1808 *p = '\0';
1809 }
1810
1811 return path;
1812 }
1813 /* If this is a CCISS disk. */
1814 if (strncmp ("cciss/c", p, sizeof ("cciss/c") - 1) == 0)
1815 {
1816 /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
1817 p = strchr (p, 'p');
1818 if (p)
1819 {
1820 *is_part = 1;
1821 *p = '\0';
1822 }
1823
1824 return path;
1825 }
1826
1827 /* If this is an AOE disk. */
1828 if (strncmp ("etherd/e", p, sizeof ("etherd/e") - 1) == 0)
1829 {
1830 /* /dev/etherd/e[0-9]+\.[0-9]+(p[0-9]+)? */
1831 p = strchr (p, 'p');
1832 if (p)
1833 {
1834 *is_part = 1;
1835 *p = '\0';
1836 }
1837
1838 return path;
1839 }
1840
1841 /* If this is a Compaq Intelligent Drive Array. */
1842 if (strncmp ("ida/c", p, sizeof ("ida/c") - 1) == 0)
1843 {
1844 /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */
1845 p = strchr (p, 'p');
1846 if (p)
1847 {
1848 *is_part = 1;
1849 *p = '\0';
1850 }
1851
1852 return path;
1853 }
1854
1855 /* If this is an I2O disk. */
1856 if (strncmp ("i2o/hd", p, sizeof ("i2o/hd") - 1) == 0)
1857 {
1858 /* /dev/i2o/hd[a-z]([0-9]+)? */
1859 if (p[sizeof ("i2o/hda") - 1])
1860 *is_part = 1;
1861 p[sizeof ("i2o/hda") - 1] = '\0';
1862 return path;
1863 }
1864
1865 /* If this is a MultiMediaCard (MMC). */
1866 if (strncmp ("mmcblk", p, sizeof ("mmcblk") - 1) == 0)
1867 {
1868 /* /dev/mmcblk[0-9]+(p[0-9]+)? */
1869 p = strchr (p, 'p');
1870 if (p)
1871 {
1872 *is_part = 1;
1873 *p = '\0';
1874 }
1875
1876 return path;
1877 }
1878
1879 if (strncmp ("md", p, 2) == 0
1880 && p[2] >= '0' && p[2] <= '9')
1881 {
1882 char *ptr = p + 2;
1883 while (*ptr >= '0' && *ptr <= '9')
1884 ptr++;
1885 if (*ptr)
1886 *is_part = 1;
1887 *ptr = 0;
1888 return path;
1889 }
1890
1891 if (strncmp ("nbd", p, 3) == 0
1892 && p[3] >= '0' && p[3] <= '9')
1893 {
1894 char *ptr = p + 3;
1895 while (*ptr >= '0' && *ptr <= '9')
1896 ptr++;
1897 if (*ptr)
1898 *is_part = 1;
1899 *ptr = 0;
1900 return path;
1901 }
1902
1903 /* If this is an IDE, SCSI or Virtio disk. */
1904 if (strncmp ("vdisk", p, 5) == 0
1905 && p[5] >= 'a' && p[5] <= 'z')
1906 {
1907 /* /dev/vdisk[a-z][0-9]* */
1908 if (p[6])
1909 *is_part = 1;
1910 p[6] = '\0';
1911 return path;
1912 }
1913 if ((strncmp ("hd", p, 2) == 0
1914 || strncmp ("vd", p, 2) == 0
1915 || strncmp ("sd", p, 2) == 0)
1916 && p[2] >= 'a' && p[2] <= 'z')
1917 {
1918 char *pp = p + 2;
1919 while (*pp >= 'a' && *pp <= 'z')
1920 pp++;
1921 if (*pp)
1922 *is_part = 1;
1923 /* /dev/[hsv]d[a-z]+[0-9]* */
1924 *pp = '\0';
1925 return path;
1926 }
1927
1928 /* If this is a Xen virtual block device. */
1929 if ((strncmp ("xvd", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z')
1930 {
1931 char *pp = p + 3;
1932 while (*pp >= 'a' && *pp <= 'z')
1933 pp++;
1934 if (*pp)
1935 *is_part = 1;
1936 /* /dev/xvd[a-z]+[0-9]* */
1937 *pp = '\0';
1938 return path;
1939 }
1940
1941 #ifdef HAVE_DEVICE_MAPPER
1942 if (dm_is_dm_major (major (st->st_rdev)))
1943 {
1944 struct dm_tree *tree;
1945 uint32_t maj, min;
1946 struct dm_tree_node *node = NULL, *child;
1947 void *handle;
1948 const char *node_uuid, *mapper_name = NULL, *child_uuid, *child_name;
1949
1950 tree = dm_tree_create ();
1951 if (! tree)
1952 {
1953 grub_util_info ("dm_tree_create failed");
1954 goto devmapper_out;
1955 }
1956
1957 maj = major (st->st_rdev);
1958 min = minor (st->st_rdev);
1959 if (! dm_tree_add_dev (tree, maj, min))
1960 {
1961 grub_util_info ("dm_tree_add_dev failed");
1962 goto devmapper_out;
1963 }
1964
1965 node = dm_tree_find_node (tree, maj, min);
1966 if (! node)
1967 {
1968 grub_util_info ("dm_tree_find_node failed");
1969 goto devmapper_out;
1970 }
1971 reiterate:
1972 node_uuid = dm_tree_node_get_uuid (node);
1973 if (! node_uuid)
1974 {
1975 grub_util_info ("%s has no DM uuid", path);
1976 goto devmapper_out;
1977 }
1978 if (strncmp (node_uuid, "LVM-", 4) == 0)
1979 {
1980 grub_util_info ("%s is an LVM", path);
1981 goto devmapper_out;
1982 }
1983 if (strncmp (node_uuid, "mpath-", 6) == 0)
1984 {
1985 /* Multipath partitions have partN-mpath-* UUIDs, and are
1986 linear mappings so are handled by
1987 grub_util_get_dm_node_linear_info. Multipath disks are not
1988 linear mappings and must be handled specially. */
1989 grub_util_info ("%s is a multipath disk", path);
1990 goto devmapper_out;
1991 }
1992 if (strncmp (node_uuid, "DMRAID-", 7) != 0)
1993 {
1994 int major, minor;
1995 const char *node_name;
1996 grub_util_info ("%s is not DM-RAID", path);
1997
1998 if ((node_name = dm_tree_node_get_name (node))
1999 && grub_util_get_dm_node_linear_info (node_name,
2000 &major, &minor, 0))
2001 {
2002 *is_part = 1;
2003 if (tree)
2004 dm_tree_free (tree);
2005 free (path);
2006 char *ret = grub_find_device ("/dev",
2007 (major << 8) | minor);
2008 return ret;
2009 }
2010
2011 goto devmapper_out;
2012 }
2013
2014 handle = NULL;
2015 /* Counter-intuitively, device-mapper refers to the disk-like
2016 device containing a DM-RAID partition device as a "child" of
2017 the partition device. */
2018 child = dm_tree_next_child (&handle, node, 0);
2019 if (! child)
2020 {
2021 grub_util_info ("%s has no DM children", path);
2022 goto devmapper_out;
2023 }
2024 child_uuid = dm_tree_node_get_uuid (child);
2025 if (! child_uuid)
2026 {
2027 grub_util_info ("%s child has no DM uuid", path);
2028 goto devmapper_out;
2029 }
2030 else if (strncmp (child_uuid, "DMRAID-", 7) != 0)
2031 {
2032 grub_util_info ("%s child is not DM-RAID", path);
2033 goto devmapper_out;
2034 }
2035 child_name = dm_tree_node_get_name (child);
2036 if (! child_name)
2037 {
2038 grub_util_info ("%s child has no DM name", path);
2039 goto devmapper_out;
2040 }
2041 mapper_name = child_name;
2042 *is_part = 1;
2043 node = child;
2044 goto reiterate;
2045
2046 devmapper_out:
2047 if (! mapper_name && node)
2048 {
2049 /* This is a DM-RAID disk, not a partition. */
2050 mapper_name = dm_tree_node_get_name (node);
2051 if (! mapper_name)
2052 grub_util_info ("%s has no DM name", path);
2053 }
2054 char *ret;
2055 if (mapper_name)
2056 ret = xasprintf ("/dev/mapper/%s", mapper_name);
2057 else
2058 ret = NULL;
2059
2060 if (tree)
2061 dm_tree_free (tree);
2062 free (path);
2063 return ret;
2064 }
2065 #endif /* HAVE_DEVICE_MAPPER */
2066 }
2067
2068 return path;
2069
2070 #elif defined(__GNU__)
2071 char *path = xstrdup (os_dev);
2072 if (strncmp ("/dev/sd", path, 7) == 0 || strncmp ("/dev/hd", path, 7) == 0)
2073 {
2074 char *p = strchr (path + 7, 's');
2075 if (p)
2076 {
2077 *is_part = 1;
2078 *p = '\0';
2079 }
2080 }
2081 return path;
2082
2083 #elif defined(__CYGWIN__)
2084 char *path = xstrdup (os_dev);
2085 if (strncmp ("/dev/sd", path, 7) == 0 && 'a' <= path[7] && path[7] <= 'z'
2086 && path[8])
2087 {
2088 *is_part = 1;
2089 path[8] = 0;
2090 }
2091 return path;
2092
2093 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
2094 char *out, *out2;
2095 if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0)
2096 return xstrdup (os_dev);
2097 grub_util_follow_gpart_up (os_dev + sizeof ("/dev/") - 1, NULL, &out);
2098
2099 if (grub_strcmp (os_dev + sizeof ("/dev/") - 1, out) != 0)
2100 *is_part = 1;
2101 out2 = xasprintf ("/dev/%s", out);
2102 free (out);
2103
2104 return out2;
2105 #elif defined(__APPLE__)
2106 char *path = xstrdup (os_dev);
2107 if (strncmp ("/dev/", path, 5) == 0)
2108 {
2109 char *p;
2110 for (p = path + 5; *p; ++p)
2111 if (grub_isdigit(*p))
2112 {
2113 p = strpbrk (p, "sp");
2114 if (p)
2115 {
2116 *is_part = 1;
2117 *p = '\0';
2118 }
2119 break;
2120 }
2121 }
2122 return path;
2123
2124 #elif defined(__NetBSD__) || defined(__OpenBSD__)
2125 int rawpart = -1;
2126 # ifdef HAVE_GETRAWPARTITION
2127 rawpart = getrawpartition();
2128 # endif /* HAVE_GETRAWPARTITION */
2129 if (rawpart < 0)
2130 return xstrdup (os_dev);
2131
2132 #if defined(__NetBSD__)
2133 /* NetBSD disk wedges are of the form "/dev/rdk.*". */
2134 if (strncmp ("/dev/rdk", os_dev, sizeof("/dev/rdk") - 1) == 0)
2135 {
2136 struct dkwedge_info dkw;
2137 int fd;
2138
2139 fd = open (os_dev, O_RDONLY);
2140 if (fd == -1)
2141 {
2142 grub_error (GRUB_ERR_BAD_DEVICE,
2143 N_("cannot open `%s': %s"), os_dev,
2144 strerror (errno));
2145 return xstrdup (os_dev);
2146 }
2147 /* We don't call configure_device_driver since this isn't a floppy device name. */
2148 if (ioctl (fd, DIOCGWEDGEINFO, &dkw) == -1)
2149 {
2150 grub_error (GRUB_ERR_BAD_DEVICE,
2151 "cannot get disk wedge info of `%s'", os_dev);
2152 close (fd);
2153 return xstrdup (os_dev);
2154 }
2155 *is_part = (dkw.dkw_offset != 0);
2156 close (fd);
2157 return xasprintf ("/dev/r%s%c", dkw.dkw_parent, 'a' + rawpart);
2158 }
2159 #endif
2160
2161 /* NetBSD (disk label) partitions are of the form "/dev/r[a-z]+[0-9][a-z]". */
2162 if (strncmp ("/dev/r", os_dev, sizeof("/dev/r") - 1) == 0 &&
2163 (os_dev[sizeof("/dev/r") - 1] >= 'a' && os_dev[sizeof("/dev/r") - 1] <= 'z') &&
2164 strncmp ("fd", os_dev + sizeof("/dev/r") - 1, sizeof("fd") - 1) != 0) /* not a floppy device name */
2165 {
2166 char *path = xstrdup (os_dev);
2167 char *p;
2168 for (p = path + sizeof("/dev/r"); *p >= 'a' && *p <= 'z'; p++);
2169 if (grub_isdigit(*p))
2170 {
2171 p++;
2172 if ((*p >= 'a' && *p <= 'z') && (*(p+1) == '\0'))
2173 {
2174 if (*p != 'a' + rawpart)
2175 *is_part = 1;
2176 /* path matches the required regular expression and
2177 p points to its last character. */
2178 *p = 'a' + rawpart;
2179 }
2180 }
2181 return path;
2182 }
2183
2184 return xstrdup (os_dev);
2185
2186 #elif defined (__sun__)
2187 char *colon = grub_strrchr (os_dev, ':');
2188 if (grub_memcmp (os_dev, "/devices", sizeof ("/devices") - 1) == 0
2189 && colon)
2190 {
2191 char *ret = xmalloc (colon - os_dev + sizeof (":q,raw"));
2192 if (grub_strcmp (colon, ":q,raw") != 0)
2193 *is_part = 1;
2194 grub_memcpy (ret, os_dev, colon - os_dev);
2195 grub_memcpy (ret + (colon - os_dev), ":q,raw", sizeof (":q,raw"));
2196 return ret;
2197 }
2198 else
2199 return xstrdup (os_dev);
2200 #elif defined (__APPLE__)
2201 char *ptr;
2202 char *ret = xstrdup (os_dev);
2203 int disk = grub_memcmp (ret, "/dev/disk", sizeof ("/dev/disk") - 1) == 0;
2204 int rdisk = grub_memcmp (ret, "/dev/rdisk", sizeof ("/dev/rdisk") - 1) == 0;
2205 if (!disk && !rdisk)
2206 return ret;
2207 ptr = ret + sizeof ("/dev/disk") + rdisk - 1;
2208 while (*ptr >= '0' && *ptr <= '9')
2209 ptr++;
2210 if (*ptr)
2211 {
2212 *is_part = 1;
2213 *ptr = 0;
2214 }
2215 return ret;
2216 #else
2217 # warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly."
2218 return xstrdup (os_dev);
2219 #endif
2220 }
2221
2222 static const char *
2223 find_system_device (const char *os_dev, struct stat *st, int convert, int add)
2224 {
2225 char *os_disk;
2226 const char *drive;
2227 int is_part;
2228
2229 if (convert)
2230 os_disk = convert_system_partition_to_system_disk (os_dev, st, &is_part);
2231 else
2232 os_disk = xstrdup (os_dev);
2233 if (! os_disk)
2234 return NULL;
2235
2236 drive = grub_hostdisk_os_dev_to_grub_drive (os_disk, add);
2237 free (os_disk);
2238 return drive;
2239 }
2240
2241 /*
2242 * Note: we do not use the new partition naming scheme as dos_part does not
2243 * necessarily correspond to an msdos partition.
2244 */
2245 static char *
2246 make_device_name (const char *drive, int dos_part, int bsd_part)
2247 {
2248 char *ret, *ptr, *end;
2249 const char *iptr;
2250
2251 ret = xmalloc (strlen (drive) * 2
2252 + sizeof (",XXXXXXXXXXXXXXXXXXXXXXXXXX"
2253 ",XXXXXXXXXXXXXXXXXXXXXXXXXX"));
2254 end = (ret + strlen (drive) * 2
2255 + sizeof (",XXXXXXXXXXXXXXXXXXXXXXXXXX"
2256 ",XXXXXXXXXXXXXXXXXXXXXXXXXX"));
2257 ptr = ret;
2258 for (iptr = drive; *iptr; iptr++)
2259 {
2260 if (*iptr == ',')
2261 *ptr++ = '\\';
2262 *ptr++ = *iptr;
2263 }
2264 *ptr = 0;
2265 if (dos_part >= 0)
2266 snprintf (ptr, end - ptr, ",%d", dos_part + 1);
2267 ptr += strlen (ptr);
2268 if (bsd_part >= 0)
2269 snprintf (ptr, end - ptr, ",%d", bsd_part + 1);
2270
2271 return ret;
2272 }
2273
2274 char *
2275 grub_util_get_os_disk (const char *os_dev)
2276 {
2277 struct stat st;
2278 int is_part;
2279
2280 grub_util_info ("Looking for %s", os_dev);
2281
2282 if (stat (os_dev, &st) < 0)
2283 {
2284 const char *errstr = strerror (errno);
2285 grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot stat `%s': %s"),
2286 os_dev, errstr);
2287 grub_util_info (_("cannot stat `%s': %s"), os_dev, errstr);
2288 return 0;
2289 }
2290
2291 return convert_system_partition_to_system_disk (os_dev, &st, &is_part);
2292 }
2293
2294 #if defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined (__sun__)
2295 /* Context for grub_util_biosdisk_get_grub_dev. */
2296 struct grub_util_biosdisk_get_grub_dev_ctx
2297 {
2298 char *partname;
2299 grub_disk_addr_t start;
2300 };
2301
2302 /* Helper for grub_util_biosdisk_get_grub_dev. */
2303 static int
2304 find_partition (grub_disk_t dsk __attribute__ ((unused)),
2305 const grub_partition_t partition, void *data)
2306 {
2307 struct grub_util_biosdisk_get_grub_dev_ctx *ctx = data;
2308 grub_disk_addr_t part_start = 0;
2309 grub_util_info ("Partition %d starts from %" PRIuGRUB_UINT64_T,
2310 partition->number, partition->start);
2311
2312 part_start = grub_partition_get_start (partition);
2313
2314 if (ctx->start == part_start)
2315 {
2316 ctx->partname = grub_partition_get_name (partition);
2317 return 1;
2318 }
2319
2320 return 0;
2321 }
2322 #endif
2323
2324 char *
2325 grub_util_biosdisk_get_grub_dev (const char *os_dev)
2326 {
2327 struct stat st;
2328 const char *drive;
2329 char *sys_disk;
2330 int is_part;
2331
2332 grub_util_info ("Looking for %s", os_dev);
2333
2334 if (stat (os_dev, &st) < 0)
2335 {
2336 const char *errstr = strerror (errno);
2337 grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot stat `%s': %s"), os_dev,
2338 errstr);
2339 grub_util_info (_("cannot stat `%s': %s"), os_dev, errstr);
2340 return 0;
2341 }
2342
2343 drive = find_system_device (os_dev, &st, 1, 1);
2344 sys_disk = convert_system_partition_to_system_disk (os_dev, &st, &is_part);
2345 if (!sys_disk)
2346 return 0;
2347 grub_util_info ("%s is a parent of %s", sys_disk, os_dev);
2348 if (!is_part)
2349 {
2350 free (sys_disk);
2351 return make_device_name (drive, -1, -1);
2352 }
2353 free (sys_disk);
2354
2355 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) || defined (__sun__)
2356 if (! S_ISCHR (st.st_mode))
2357 #else
2358 if (! S_ISBLK (st.st_mode))
2359 #endif
2360 return make_device_name (drive, -1, -1);
2361
2362 #if defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined (__sun__) || defined(__OpenBSD__)
2363
2364 /* Linux counts partitions uniformly, whether a BSD partition or a DOS
2365 partition, so mapping them to GRUB devices is not trivial.
2366 Here, get the start sector of a partition by HDIO_GETGEO, and
2367 compare it with each partition GRUB recognizes.
2368
2369 Cygwin /dev/sdXN emulation uses Windows partition mapping. It
2370 does not count the extended partition and missing primary
2371 partitions. Use same method as on Linux here.
2372
2373 For NetBSD and FreeBSD, proceed as for Linux, except that the start
2374 sector is obtained from the disk label. */
2375 {
2376 char *name;
2377 grub_disk_t disk;
2378 struct grub_util_biosdisk_get_grub_dev_ctx ctx;
2379
2380 name = make_device_name (drive, -1, -1);
2381
2382 # if !defined(HAVE_DIOCGDINFO) && !defined(__sun__)
2383 if (MAJOR (st.st_rdev) == FLOPPY_MAJOR)
2384 return name;
2385 # else /* defined(HAVE_DIOCGDINFO) */
2386 /* Since os_dev and convert_system_partition_to_system_disk (os_dev) are
2387 * different, we know that os_dev cannot be a floppy device. */
2388 # endif /* !defined(HAVE_DIOCGDINFO) */
2389
2390 ctx.start = grub_hostdisk_find_partition_start (os_dev);
2391 if (grub_errno != GRUB_ERR_NONE)
2392 {
2393 free (name);
2394 return 0;
2395 }
2396
2397 grub_util_info ("%s starts from %" PRIuGRUB_UINT64_T, os_dev, ctx.start);
2398
2399 if (ctx.start == 0 && !is_part)
2400 return name;
2401
2402 grub_util_info ("opening the device %s", name);
2403 disk = grub_disk_open (name);
2404 free (name);
2405
2406 if (! disk)
2407 {
2408 /* We already know that the partition exists. Given that we already
2409 checked the device map above, we can only get
2410 GRUB_ERR_UNKNOWN_DEVICE at this point if the disk does not exist.
2411 This can happen on Xen, where disk images in the host can be
2412 assigned to devices that have partition-like names in the guest
2413 but are really more like disks. */
2414 if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
2415 {
2416 char *canon;
2417 grub_util_warn
2418 (_("disk does not exist, so falling back to partition device %s"),
2419 os_dev);
2420 grub_errno = GRUB_ERR_NONE;
2421
2422 canon = canonicalize_file_name (os_dev);
2423 drive = find_system_device (canon ? : os_dev, &st, 0, 1);
2424 if (canon)
2425 free (canon);
2426 return make_device_name (drive, -1, -1);
2427 }
2428 else
2429 return 0;
2430 }
2431
2432 name = grub_util_get_ldm (disk, ctx.start);
2433 if (name)
2434 return name;
2435
2436 ctx.partname = NULL;
2437
2438 grub_partition_iterate (disk, find_partition, &ctx);
2439 if (grub_errno != GRUB_ERR_NONE)
2440 {
2441 grub_disk_close (disk);
2442 return 0;
2443 }
2444
2445 if (ctx.partname == NULL)
2446 {
2447 grub_disk_close (disk);
2448 grub_util_info ("cannot find the partition of `%s'", os_dev);
2449 grub_error (GRUB_ERR_BAD_DEVICE,
2450 "cannot find the partition of `%s'", os_dev);
2451 return 0;
2452 }
2453
2454 name = grub_xasprintf ("%s,%s", disk->name, ctx.partname);
2455 free (ctx.partname);
2456 grub_disk_close (disk);
2457 return name;
2458 }
2459
2460 #elif defined(__GNU__)
2461 /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
2462 {
2463 char *p;
2464 int dos_part = -1;
2465 int bsd_part = -1;
2466
2467 p = strrchr (os_dev, 's');
2468 if (p)
2469 {
2470 long int n;
2471 char *q;
2472
2473 p++;
2474 n = strtol (p, &q, 10);
2475 if (p != q && n != GRUB_LONG_MIN && n != GRUB_LONG_MAX)
2476 {
2477 dos_part = (int) n - 1;
2478
2479 if (*q >= 'a' && *q <= 'g')
2480 bsd_part = *q - 'a';
2481 }
2482 }
2483
2484 return make_device_name (drive, dos_part, bsd_part);
2485 }
2486
2487 #elif defined(__APPLE__)
2488 /* Apple uses "/dev/r?disk[0-9]+(s[0-9]+)?". */
2489 {
2490 const char *p;
2491 int disk = (grub_memcmp (os_dev, "/dev/disk", sizeof ("/dev/disk") - 1)
2492 == 0);
2493 int rdisk = (grub_memcmp (os_dev, "/dev/rdisk", sizeof ("/dev/rdisk") - 1)
2494 == 0);
2495
2496 if (!disk && !rdisk)
2497 return make_device_name (drive, -1, -1);
2498
2499 p = os_dev + sizeof ("/dev/disk") + rdisk - 1;
2500 while (*p >= '0' && *p <= '9')
2501 p++;
2502 if (*p != 's')
2503 return make_device_name (drive, -1, -1);
2504 p++;
2505
2506 return make_device_name (drive, strtol (p, NULL, 10) - 1, -1);
2507 }
2508 #else
2509 # warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
2510 return make_device_name (drive, -1, -1);
2511 #endif
2512 }
2513
2514 int
2515 grub_util_biosdisk_is_present (const char *os_dev)
2516 {
2517 struct stat st;
2518
2519 if (stat (os_dev, &st) < 0)
2520 return 0;
2521
2522 int ret= (find_system_device (os_dev, &st, 1, 0) != NULL);
2523 grub_util_info ((ret ? "%s is present" : "%s is not present"),
2524 os_dev);
2525 return ret;
2526 }
2527
2528 char *
2529 grub_util_get_grub_dev (const char *os_dev)
2530 {
2531 char *grub_dev = NULL;
2532
2533 grub_util_pull_device (os_dev);
2534
2535 switch (grub_util_get_dev_abstraction (os_dev))
2536 {
2537 #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
2538 case GRUB_DEV_ABSTRACTION_LVM:
2539
2540 {
2541 unsigned short len;
2542 grub_size_t offset = sizeof (LVM_DEV_MAPPER_STRING) - 1;
2543
2544 len = strlen (os_dev) - offset + 1;
2545 grub_dev = xmalloc (len + sizeof ("lvm/"));
2546
2547 grub_memcpy (grub_dev, "lvm/", sizeof ("lvm/") - 1);
2548 grub_memcpy (grub_dev + sizeof ("lvm/") - 1, os_dev + offset, len);
2549 }
2550
2551 break;
2552 #endif
2553
2554 case GRUB_DEV_ABSTRACTION_LUKS:
2555 #ifdef HAVE_DEVICE_MAPPER
2556 {
2557 char *uuid, *dash;
2558 uuid = get_dm_uuid (os_dev);
2559 if (!uuid)
2560 break;
2561 dash = grub_strchr (uuid + sizeof ("CRYPT-LUKS1-") - 1, '-');
2562 if (dash)
2563 *dash = 0;
2564 grub_dev = grub_xasprintf ("cryptouuid/%s",
2565 uuid + sizeof ("CRYPT-LUKS1-") - 1);
2566 grub_free (uuid);
2567 }
2568 #endif
2569 break;
2570
2571 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
2572 case GRUB_DEV_ABSTRACTION_GELI:
2573 {
2574 char *whole;
2575 struct gmesh mesh;
2576 struct gclass *class;
2577 const char *name;
2578 int err;
2579
2580 if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0)
2581 return 0;
2582 name = os_dev + sizeof ("/dev/") - 1;
2583 grub_util_follow_gpart_up (name, NULL, &whole);
2584
2585 grub_util_info ("following geom '%s'", name);
2586
2587 err = geom_gettree (&mesh);
2588 if (err != 0)
2589 /* TRANSLATORS: geom is the name of (k)FreeBSD device framework.
2590 Usually left untranslated.
2591 */
2592 grub_util_error ("%s", _("couldn't open geom"));
2593
2594 LIST_FOREACH (class, &mesh.lg_class, lg_class)
2595 {
2596 struct ggeom *geom;
2597 LIST_FOREACH (geom, &class->lg_geom, lg_geom)
2598 {
2599 struct gprovider *provider;
2600 LIST_FOREACH (provider, &geom->lg_provider, lg_provider)
2601 if (strcmp (provider->lg_name, name) == 0)
2602 {
2603 struct gconsumer *consumer;
2604 char *fname;
2605 char *uuid;
2606
2607 LIST_FOREACH (consumer, &geom->lg_consumer, lg_consumer)
2608 break;
2609 if (!consumer)
2610 grub_util_error ("%s",
2611 _("couldn't find geli consumer"));
2612 fname = xasprintf ("/dev/%s", consumer->lg_provider->lg_name);
2613 uuid = grub_util_get_geli_uuid (fname);
2614 if (!uuid)
2615 grub_util_error ("%s",
2616 _("couldn't retrieve geli UUID"));
2617 grub_dev = xasprintf ("cryptouuid/%s", uuid);
2618 free (fname);
2619 free (uuid);
2620 }
2621 }
2622 }
2623 }
2624 break;
2625 #endif
2626
2627 #ifdef __linux__
2628 case GRUB_DEV_ABSTRACTION_RAID:
2629
2630 if (os_dev[7] == '_' && os_dev[8] == 'd')
2631 {
2632 /* This a partitionable RAID device of the form /dev/md_dNNpMM. */
2633
2634 char *p, *q;
2635
2636 p = strdup (os_dev + sizeof ("/dev/md_d") - 1);
2637
2638 q = strchr (p, 'p');
2639 if (q)
2640 *q = ',';
2641
2642 grub_dev = xasprintf ("md%s", p);
2643 free (p);
2644 }
2645 else if (os_dev[7] == '/' && os_dev[8] == 'd')
2646 {
2647 /* This a partitionable RAID device of the form /dev/md/dNNpMM. */
2648
2649 char *p, *q;
2650
2651 p = strdup (os_dev + sizeof ("/dev/md/d") - 1);
2652
2653 q = strchr (p, 'p');
2654 if (q)
2655 *q = ',';
2656
2657 grub_dev = xasprintf ("md%s", p);
2658 free (p);
2659 }
2660 else if (os_dev[7] >= '0' && os_dev[7] <= '9')
2661 {
2662 char *p , *q;
2663
2664 p = strdup (os_dev + sizeof ("/dev/md") - 1);
2665
2666 q = strchr (p, 'p');
2667 if (q)
2668 *q = ',';
2669
2670 grub_dev = xasprintf ("md%s", p);
2671 free (p);
2672 }
2673 else if (os_dev[7] == '/' && os_dev[8] >= '0' && os_dev[8] <= '9')
2674 {
2675 char *p , *q;
2676
2677 p = strdup (os_dev + sizeof ("/dev/md/") - 1);
2678
2679 q = strchr (p, 'p');
2680 if (q)
2681 *q = ',';
2682
2683 grub_dev = xasprintf ("md%s", p);
2684 free (p);
2685 }
2686 else if (os_dev[7] == '/')
2687 {
2688 /* mdraid 1.x with a free name. */
2689 char *p , *q;
2690
2691 p = strdup (os_dev + sizeof ("/dev/md/") - 1);
2692
2693 q = strchr (p, 'p');
2694 if (q)
2695 *q = ',';
2696
2697 grub_dev = xasprintf ("md/%s", p);
2698 free (p);
2699 }
2700 else
2701 grub_util_error (_("unknown kind of RAID device `%s'"), os_dev);
2702
2703 {
2704 char *mdadm_name = get_mdadm_uuid (os_dev);
2705
2706 if (mdadm_name)
2707 {
2708 const char *q;
2709
2710 for (q = os_dev + strlen (os_dev) - 1; q >= os_dev
2711 && grub_isdigit (*q); q--);
2712
2713 if (q >= os_dev && *q == 'p')
2714 {
2715 free (grub_dev);
2716 grub_dev = xasprintf ("mduuid/%s,%s", mdadm_name, q + 1);
2717 goto done;
2718 }
2719 free (grub_dev);
2720 grub_dev = xasprintf ("mduuid/%s", mdadm_name);
2721
2722 done:
2723 free (mdadm_name);
2724 }
2725 }
2726 break;
2727 #endif /* __linux__ */
2728
2729 default: /* GRUB_DEV_ABSTRACTION_NONE */
2730 grub_dev = grub_util_biosdisk_get_grub_dev (os_dev);
2731 }
2732
2733 return grub_dev;
2734 }
2735
2736 const char *
2737 grub_util_check_block_device (const char *blk_dev)
2738 {
2739 struct stat st;
2740
2741 if (stat (blk_dev, &st) < 0)
2742 grub_util_error (_("cannot stat `%s': %s"), blk_dev,
2743 strerror (errno));
2744
2745 if (S_ISBLK (st.st_mode))
2746 return (blk_dev);
2747 else
2748 return 0;
2749 }
2750
2751 const char *
2752 grub_util_check_char_device (const char *blk_dev)
2753 {
2754 struct stat st;
2755
2756 if (stat (blk_dev, &st) < 0)
2757 grub_util_error (_("cannot stat `%s': %s"), blk_dev, strerror (errno));
2758
2759 if (S_ISCHR (st.st_mode))
2760 return (blk_dev);
2761 else
2762 return 0;
2763 }
2764
2765 #ifdef __CYGWIN__
2766 /* Convert POSIX path to Win32 path,
2767 remove drive letter, replace backslashes. */
2768 static char *
2769 get_win32_path (const char *path)
2770 {
2771 char winpath[PATH_MAX];
2772 if (cygwin_conv_path (CCP_POSIX_TO_WIN_A, path, winpath, sizeof(winpath)))
2773 grub_util_error ("%s", _("cygwin_conv_path() failed"));
2774
2775 int len = strlen (winpath);
2776 int offs = (len > 2 && winpath[1] == ':' ? 2 : 0);
2777
2778 int i;
2779 for (i = offs; i < len; i++)
2780 if (winpath[i] == '\\')
2781 winpath[i] = '/';
2782 return xstrdup (winpath + offs);
2783 }
2784 #endif
2785
2786 #ifdef HAVE_LIBZFS
2787 static libzfs_handle_t *__libzfs_handle;
2788
2789 static void
2790 fini_libzfs (void)
2791 {
2792 libzfs_fini (__libzfs_handle);
2793 }
2794
2795 libzfs_handle_t *
2796 grub_get_libzfs_handle (void)
2797 {
2798 if (! __libzfs_handle)
2799 {
2800 __libzfs_handle = libzfs_init ();
2801
2802 if (__libzfs_handle)
2803 atexit (fini_libzfs);
2804 }
2805
2806 return __libzfs_handle;
2807 }
2808 #endif /* HAVE_LIBZFS */
2809
2810 #if !defined (__MINGW32__) && !defined (__CYGWIN__)
2811 /* ZFS has similar problems to those of btrfs (see above). */
2812 void
2813 grub_find_zpool_from_dir (const char *dir, char **poolname, char **poolfs)
2814 {
2815 char *slash;
2816
2817 *poolname = *poolfs = NULL;
2818
2819 #if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) && defined(HAVE_STRUCT_STATFS_F_MNTFROMNAME)
2820 /* FreeBSD and GNU/kFreeBSD. */
2821 {
2822 struct statfs mnt;
2823
2824 if (statfs (dir, &mnt) != 0)
2825 return;
2826
2827 if (strcmp (mnt.f_fstypename, "zfs") != 0)
2828 return;
2829
2830 *poolname = xstrdup (mnt.f_mntfromname);
2831 }
2832 #elif defined(HAVE_GETEXTMNTENT)
2833 /* Solaris. */
2834 {
2835 struct stat st;
2836 struct extmnttab mnt;
2837
2838 if (stat (dir, &st) != 0)
2839 return;
2840
2841 FILE *mnttab = fopen ("/etc/mnttab", "r");
2842 if (! mnttab)
2843 return;
2844
2845 while (getextmntent (mnttab, &mnt, sizeof (mnt)) == 0)
2846 {
2847 if (makedev (mnt.mnt_major, mnt.mnt_minor) == st.st_dev
2848 && !strcmp (mnt.mnt_fstype, "zfs"))
2849 {
2850 *poolname = xstrdup (mnt.mnt_special);
2851 break;
2852 }
2853 }
2854
2855 fclose (mnttab);
2856 }
2857 #endif
2858
2859 if (! *poolname)
2860 return;
2861
2862 slash = strchr (*poolname, '/');
2863 if (slash)
2864 {
2865 *slash = '\0';
2866 *poolfs = xstrdup (slash + 1);
2867 }
2868 else
2869 *poolfs = xstrdup ("");
2870 }
2871 #endif
2872
2873 /* This function never prints trailing slashes (so that its output
2874 can be appended a slash unconditionally). */
2875 char *
2876 grub_make_system_path_relative_to_its_root (const char *path)
2877 {
2878 struct stat st;
2879 char *p, *buf, *buf2, *buf3, *ret;
2880 uintptr_t offset = 0;
2881 dev_t num;
2882 size_t len;
2883 char *poolfs = NULL;
2884
2885 /* canonicalize. */
2886 p = canonicalize_file_name (path);
2887 if (p == NULL)
2888 grub_util_error (_("failed to get canonical path of `%s'"), path);
2889
2890 /* For ZFS sub-pool filesystems, could be extended to others (btrfs?). */
2891 #if !defined (__MINGW32__) && !defined (__CYGWIN__)
2892 {
2893 char *dummy;
2894 grub_find_zpool_from_dir (p, &dummy, &poolfs);
2895 }
2896 #endif
2897
2898 len = strlen (p) + 1;
2899 buf = xstrdup (p);
2900 free (p);
2901
2902 if (stat (buf, &st) < 0)
2903 grub_util_error (_("cannot stat `%s': %s"), buf, strerror (errno));
2904
2905 buf2 = xstrdup (buf);
2906 num = st.st_dev;
2907
2908 /* This loop sets offset to the number of chars of the root
2909 directory we're inspecting. */
2910 while (1)
2911 {
2912 p = strrchr (buf, '/');
2913 if (p == NULL)
2914 /* This should never happen. */
2915 grub_util_error ("%s",
2916 /* TRANSLATORS: canonical pathname is the
2917 complete one e.g. /etc/fstab. It has
2918 to contain `/' normally, if it doesn't
2919 we're in trouble and throw this error. */
2920 _("no `/' in canonical filename"));
2921 if (p != buf)
2922 *p = 0;
2923 else
2924 *++p = 0;
2925
2926 if (stat (buf, &st) < 0)
2927 grub_util_error (_("cannot stat `%s': %s"), buf, strerror (errno));
2928
2929 /* buf is another filesystem; we found it. */
2930 if (st.st_dev != num)
2931 {
2932 /* offset == 0 means path given is the mount point.
2933 This works around special-casing of "/" in Un*x. This function never
2934 prints trailing slashes (so that its output can be appended a slash
2935 unconditionally). Each slash in is considered a preceding slash, and
2936 therefore the root directory is an empty string. */
2937 if (offset == 0)
2938 {
2939 free (buf);
2940 #ifdef __linux__
2941 {
2942 char *bind;
2943 grub_free (grub_find_root_devices_from_mountinfo (buf2, &bind));
2944 if (bind && bind[0] && bind[1])
2945 {
2946 buf3 = bind;
2947 goto parsedir;
2948 }
2949 grub_free (bind);
2950 }
2951 #endif
2952 free (buf2);
2953 if (poolfs)
2954 return xasprintf ("/%s/@", poolfs);
2955 return xstrdup ("");
2956 }
2957 else
2958 break;
2959 }
2960
2961 offset = p - buf;
2962 /* offset == 1 means root directory. */
2963 if (offset == 1)
2964 {
2965 /* Include leading slash. */
2966 offset = 0;
2967 break;
2968 }
2969 }
2970 free (buf);
2971 buf3 = xstrdup (buf2 + offset);
2972 buf2[offset] = 0;
2973 #ifdef __linux__
2974 {
2975 char *bind;
2976 grub_free (grub_find_root_devices_from_mountinfo (buf2, &bind));
2977 if (bind && bind[0] && bind[1])
2978 {
2979 char *temp = buf3;
2980 buf3 = grub_xasprintf ("%s%s%s", bind, buf3[0] == '/' ?"":"/", buf3);
2981 grub_free (temp);
2982 }
2983 grub_free (bind);
2984 }
2985 #endif
2986
2987 free (buf2);
2988
2989 #ifdef __CYGWIN__
2990 if (st.st_dev != (DEV_CYGDRIVE_MAJOR << 16))
2991 {
2992 /* Reached some mount point not below /cygdrive.
2993 GRUB does not know Cygwin's emulated mounts,
2994 convert to Win32 path. */
2995 grub_util_info ("Cygwin path = %s\n", buf3);
2996 char * temp = get_win32_path (buf3);
2997 free (buf3);
2998 buf3 = temp;
2999 }
3000 #endif
3001
3002 #ifdef __linux__
3003 parsedir:
3004 #endif
3005 /* Remove trailing slashes, return empty string if root directory. */
3006 len = strlen (buf3);
3007 while (len > 0 && buf3[len - 1] == '/')
3008 {
3009 buf3[len - 1] = '\0';
3010 len--;
3011 }
3012
3013 if (poolfs)
3014 {
3015 ret = xasprintf ("/%s/@%s", poolfs, buf3);
3016 free (buf3);
3017 }
3018 else
3019 ret = buf3;
3020
3021 return ret;
3022 }