]> git.proxmox.com Git - grub2.git/blob - util/getroot.c
Put recheck back
[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 #ifdef __linux__
55 # include <sys/types.h>
56 # include <sys/wait.h>
57 #endif
58
59 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
60 # include <sys/mount.h>
61 #endif
62
63 #if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
64 # include <grub/util/libzfs.h>
65 # include <grub/util/libnvpair.h>
66 #endif
67
68 #ifdef __sun__
69 # include <sys/types.h>
70 # include <sys/mkdev.h>
71 #endif
72
73 #include <grub/mm.h>
74 #include <grub/misc.h>
75 #include <grub/emu/misc.h>
76 #include <grub/emu/hostdisk.h>
77 #include <grub/emu/getroot.h>
78
79 static void
80 strip_extra_slashes (char *dir)
81 {
82 char *p = dir;
83
84 while ((p = strchr (p, '/')) != 0)
85 {
86 if (p[1] == '/')
87 {
88 memmove (p, p + 1, strlen (p));
89 continue;
90 }
91 else if (p[1] == '\0')
92 {
93 if (p > dir)
94 p[0] = '\0';
95 break;
96 }
97
98 p++;
99 }
100 }
101
102 static char *
103 xgetcwd (void)
104 {
105 size_t size = 10;
106 char *path;
107
108 path = xmalloc (size);
109 while (! getcwd (path, size))
110 {
111 size <<= 1;
112 path = xrealloc (path, size);
113 }
114
115 return path;
116 }
117
118 #ifdef __linux__
119
120 #define ESCAPED_PATH_MAX (4 * PATH_MAX)
121 struct mountinfo_entry
122 {
123 int id;
124 int major, minor;
125 char enc_root[ESCAPED_PATH_MAX + 1], enc_path[ESCAPED_PATH_MAX + 1];
126 char fstype[ESCAPED_PATH_MAX + 1], device[ESCAPED_PATH_MAX + 1];
127 };
128
129 /* Statting something on a btrfs filesystem always returns a virtual device
130 major/minor pair rather than the real underlying device, because btrfs
131 can span multiple underlying devices (and even if it's currently only
132 using a single device it can be dynamically extended onto another). We
133 can't deal with the multiple-device case yet, but in the meantime, we can
134 at least cope with the single-device case by scanning
135 /proc/self/mountinfo. */
136 static void
137 unescape (char *str)
138 {
139 char *optr;
140 const char *iptr;
141 for (iptr = optr = str; *iptr; optr++)
142 {
143 if (iptr[0] == '\\' && iptr[1] >= '0' && iptr[1] < '8'
144 && iptr[2] >= '0' && iptr[2] < '8'
145 && iptr[3] >= '0' && iptr[3] < '8')
146 {
147 *optr = (((iptr[1] - '0') << 6) | ((iptr[2] - '0') << 3)
148 | (iptr[3] - '0'));
149 iptr += 4;
150 }
151 else
152 *optr = *iptr++;
153 }
154 *optr = 0;
155 }
156
157 char *
158 grub_find_root_device_from_mountinfo (const char *dir, char **relroot)
159 {
160 FILE *fp;
161 char *buf = NULL;
162 size_t len = 0;
163 char *ret = NULL;
164 int entry_len = 0, entry_max = 4;
165 struct mountinfo_entry *entries;
166 struct mountinfo_entry parent_entry = { 0, 0, 0, "", "", "", "" };
167 int i;
168
169 if (! *dir)
170 dir = "/";
171 if (relroot)
172 *relroot = NULL;
173
174 fp = fopen ("/proc/self/mountinfo", "r");
175 if (! fp)
176 return NULL; /* fall through to other methods */
177
178 entries = xmalloc (entry_max * sizeof (*entries));
179
180 /* First, build a list of relevant visible mounts. */
181 while (getline (&buf, &len, fp) > 0)
182 {
183 struct mountinfo_entry entry;
184 int count;
185 size_t enc_path_len;
186 const char *sep;
187
188 if (sscanf (buf, "%d %d %u:%u %s %s%n",
189 &entry.id, &parent_entry.id, &entry.major, &entry.minor,
190 entry.enc_root, entry.enc_path, &count) < 6)
191 continue;
192
193 unescape (entry.enc_root);
194 unescape (entry.enc_path);
195
196 enc_path_len = strlen (entry.enc_path);
197 /* Check that enc_path is a prefix of dir. The prefix must either be
198 the entire string, or end with a slash, or be immediately followed
199 by a slash. */
200 if (strncmp (dir, entry.enc_path, enc_path_len) != 0 ||
201 (enc_path_len && dir[enc_path_len - 1] != '/' &&
202 dir[enc_path_len] && dir[enc_path_len] != '/'))
203 continue;
204
205 sep = strstr (buf + count, " - ");
206 if (!sep)
207 continue;
208
209 sep += sizeof (" - ") - 1;
210 if (sscanf (sep, "%s %s", entry.fstype, entry.device) != 2)
211 continue;
212
213 /* Using the mount IDs, find out where this fits in the list of
214 visible mount entries we've seen so far. There are three
215 interesting cases. Firstly, it may be inserted at the end: this is
216 the usual case of /foo/bar being mounted after /foo. Secondly, it
217 may be inserted at the start: for example, this can happen for
218 filesystems that are mounted before / and later moved under it.
219 Thirdly, it may occlude part or all of the existing filesystem
220 tree, in which case the end of the list needs to be pruned and this
221 new entry will be inserted at the end. */
222 if (entry_len >= entry_max)
223 {
224 entry_max <<= 1;
225 entries = xrealloc (entries, entry_max * sizeof (*entries));
226 }
227
228 if (!entry_len)
229 {
230 /* Initialise list. */
231 entry_len = 2;
232 entries[0] = parent_entry;
233 entries[1] = entry;
234 }
235 else
236 {
237 for (i = entry_len - 1; i >= 0; i--)
238 {
239 if (entries[i].id == parent_entry.id)
240 {
241 /* Insert at end, pruning anything previously above this. */
242 entry_len = i + 2;
243 entries[i + 1] = entry;
244 break;
245 }
246 else if (i == 0 && entries[i].id == entry.id)
247 {
248 /* Insert at start. */
249 entry_len++;
250 memmove (entries + 1, entries,
251 (entry_len - 1) * sizeof (*entries));
252 entries[0] = parent_entry;
253 entries[1] = entry;
254 break;
255 }
256 }
257 }
258 }
259
260 /* Now scan visible mounts for the ones we're interested in. */
261 for (i = entry_len - 1; i >= 0; i--)
262 {
263 if (!*entries[i].device)
264 continue;
265
266 ret = strdup (entries[i].device);
267 if (relroot)
268 *relroot = strdup (entries[i].enc_root);
269 break;
270 }
271
272 free (buf);
273 free (entries);
274 fclose (fp);
275 return ret;
276 }
277
278 #endif /* __linux__ */
279
280 static char *
281 find_root_device_from_libzfs (const char *dir)
282 {
283 char *device = NULL;
284 char *poolname;
285 char *poolfs;
286
287 grub_find_zpool_from_dir (dir, &poolname, &poolfs);
288 if (! poolname)
289 return NULL;
290
291 #if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
292 {
293 zpool_handle_t *zpool;
294 libzfs_handle_t *libzfs;
295 nvlist_t *config, *vdev_tree;
296 nvlist_t **children, **path;
297 unsigned int nvlist_count;
298 unsigned int i;
299
300 libzfs = grub_get_libzfs_handle ();
301 if (! libzfs)
302 return NULL;
303
304 zpool = zpool_open (libzfs, poolname);
305 config = zpool_get_config (zpool, NULL);
306
307 if (nvlist_lookup_nvlist (config, "vdev_tree", &vdev_tree) != 0)
308 error (1, errno, "nvlist_lookup_nvlist (\"vdev_tree\")");
309
310 if (nvlist_lookup_nvlist_array (vdev_tree, "children", &children, &nvlist_count) != 0)
311 error (1, errno, "nvlist_lookup_nvlist_array (\"children\")");
312 assert (nvlist_count > 0);
313
314 while (nvlist_lookup_nvlist_array (children[0], "children",
315 &children, &nvlist_count) == 0)
316 assert (nvlist_count > 0);
317
318 for (i = 0; i < nvlist_count; i++)
319 {
320 if (nvlist_lookup_string (children[i], "path", &device) != 0)
321 error (1, errno, "nvlist_lookup_string (\"path\")");
322
323 struct stat st;
324 if (stat (device, &st) == 0)
325 {
326 #ifdef __sun__
327 if (grub_memcmp (device, "/dev/dsk/", sizeof ("/dev/dsk/") - 1)
328 == 0)
329 device = xasprintf ("/dev/rdsk/%s",
330 device + sizeof ("/dev/dsk/") - 1);
331 else if (grub_memcmp (device, "/devices", sizeof ("/devices") - 1)
332 == 0
333 && grub_memcmp (device + strlen (device) - 4,
334 ",raw", 4) != 0)
335 device = xasprintf ("%s,raw", device);
336 else
337 #endif
338 device = xstrdup (device);
339 break;
340 }
341
342 device = NULL;
343 }
344
345 zpool_close (zpool);
346 }
347 #else
348 {
349 char *cmd;
350 FILE *fp;
351 int ret;
352 char *line;
353 size_t len;
354 int st;
355
356 char name[PATH_MAX + 1], state[257], readlen[257], writelen[257];
357 char cksum[257], notes[257];
358 unsigned int dummy;
359
360 cmd = xasprintf ("zpool status %s", poolname);
361 fp = popen (cmd, "r");
362 free (cmd);
363
364 st = 0;
365 while (st < 3)
366 {
367 line = NULL;
368 ret = getline (&line, &len, fp);
369 if (ret == -1)
370 goto fail;
371
372 if (sscanf (line, " %s %256s %256s %256s %256s %256s",
373 name, state, readlen, writelen, cksum, notes) >= 5)
374 switch (st)
375 {
376 case 0:
377 if (!strcmp (name, "NAME")
378 && !strcmp (state, "STATE")
379 && !strcmp (readlen, "READ")
380 && !strcmp (writelen, "WRITE")
381 && !strcmp (cksum, "CKSUM"))
382 st++;
383 break;
384 case 1:
385 if (!strcmp (name, poolname))
386 st++;
387 break;
388 case 2:
389 if (strcmp (name, "mirror") && !sscanf (name, "mirror-%u", &dummy)
390 && !sscanf (name, "raidz%u", &dummy)
391 && !strcmp (state, "ONLINE"))
392 st++;
393 break;
394 }
395
396 free (line);
397 }
398 device = xasprintf ("/dev/%s", name);
399
400 fail:
401 pclose (fp);
402 }
403 #endif
404
405 free (poolname);
406 if (poolfs)
407 free (poolfs);
408
409 return device;
410 }
411
412 #ifdef __MINGW32__
413
414 char *
415 grub_find_device (const char *dir __attribute__ ((unused)),
416 dev_t dev __attribute__ ((unused)))
417 {
418 return 0;
419 }
420
421 #elif ! defined(__CYGWIN__)
422
423 char *
424 grub_find_device (const char *dir, dev_t dev)
425 {
426 DIR *dp;
427 char *saved_cwd;
428 struct dirent *ent;
429
430 if (! dir)
431 {
432 #ifdef __CYGWIN__
433 return NULL;
434 #else
435 dir = "/dev";
436 #endif
437 }
438
439 dp = opendir (dir);
440 if (! dp)
441 return 0;
442
443 saved_cwd = xgetcwd ();
444
445 grub_util_info ("changing current directory to %s", dir);
446 if (chdir (dir) < 0)
447 {
448 free (saved_cwd);
449 closedir (dp);
450 return 0;
451 }
452
453 while ((ent = readdir (dp)) != 0)
454 {
455 struct stat st;
456
457 /* Avoid:
458 - dotfiles (like "/dev/.tmp.md0") since they could be duplicates.
459 - dotdirs (like "/dev/.static") since they could contain duplicates. */
460 if (ent->d_name[0] == '.')
461 continue;
462
463 if (lstat (ent->d_name, &st) < 0)
464 /* Ignore any error. */
465 continue;
466
467 if (S_ISLNK (st.st_mode)) {
468 #ifdef __linux__
469 if (strcmp (dir, "mapper") == 0 || strcmp (dir, "/dev/mapper") == 0) {
470 /* Follow symbolic links under /dev/mapper/; the canonical name
471 may be something like /dev/dm-0, but the names under
472 /dev/mapper/ are more human-readable and so we prefer them if
473 we can get them. */
474 if (stat (ent->d_name, &st) < 0)
475 continue;
476 } else
477 #endif /* __linux__ */
478 /* Don't follow other symbolic links. */
479 continue;
480 }
481
482 if (S_ISDIR (st.st_mode))
483 {
484 /* Find it recursively. */
485 char *res;
486
487 res = grub_find_device (ent->d_name, dev);
488
489 if (res)
490 {
491 if (chdir (saved_cwd) < 0)
492 grub_util_error ("cannot restore the original directory");
493
494 free (saved_cwd);
495 closedir (dp);
496 return res;
497 }
498 }
499
500 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
501 if (S_ISCHR (st.st_mode) && st.st_rdev == dev)
502 #else
503 if (S_ISBLK (st.st_mode) && st.st_rdev == dev)
504 #endif
505 {
506 #ifdef __linux__
507 /* Skip device names like /dev/dm-0, which are short-hand aliases
508 to more descriptive device names, e.g. those under /dev/mapper */
509 if (ent->d_name[0] == 'd' &&
510 ent->d_name[1] == 'm' &&
511 ent->d_name[2] == '-' &&
512 ent->d_name[3] >= '0' &&
513 ent->d_name[3] <= '9')
514 continue;
515 #endif
516
517 /* Found! */
518 char *res;
519 char *cwd;
520 #if defined(__NetBSD__)
521 /* Convert this block device to its character (raw) device. */
522 const char *template = "%s/r%s";
523 #else
524 /* Keep the device name as it is. */
525 const char *template = "%s/%s";
526 #endif
527
528 cwd = xgetcwd ();
529 res = xmalloc (strlen (cwd) + strlen (ent->d_name) + 3);
530 sprintf (res, template, cwd, ent->d_name);
531 strip_extra_slashes (res);
532 free (cwd);
533
534 /* /dev/root is not a real block device keep looking, takes care
535 of situation where root filesystem is on the same partition as
536 grub files */
537
538 if (strcmp(res, "/dev/root") == 0)
539 continue;
540
541 if (chdir (saved_cwd) < 0)
542 grub_util_error (_("cannot restore the original directory"));
543
544 free (saved_cwd);
545 closedir (dp);
546 return res;
547 }
548 }
549
550 if (chdir (saved_cwd) < 0)
551 grub_util_error (_("cannot restore the original directory"));
552
553 free (saved_cwd);
554 closedir (dp);
555 return 0;
556 }
557
558 #else /* __CYGWIN__ */
559
560 /* Read drive/partition serial number from mbr/boot sector,
561 return 0 on read error, ~0 on unknown serial. */
562 static unsigned
563 get_bootsec_serial (const char *os_dev, int mbr)
564 {
565 /* Read boot sector. */
566 int fd = open (os_dev, O_RDONLY);
567 if (fd < 0)
568 return 0;
569 unsigned char buf[0x200];
570 int n = read (fd, buf, sizeof (buf));
571 close (fd);
572 if (n != sizeof(buf))
573 return 0;
574
575 /* Check signature. */
576 if (!(buf[0x1fe] == 0x55 && buf[0x1ff] == 0xaa))
577 return ~0;
578
579 /* Serial number offset depends on boot sector type. */
580 if (mbr)
581 n = 0x1b8;
582 else if (memcmp (buf + 0x03, "NTFS", 4) == 0)
583 n = 0x048;
584 else if (memcmp (buf + 0x52, "FAT32", 5) == 0)
585 n = 0x043;
586 else if (memcmp (buf + 0x36, "FAT", 3) == 0)
587 n = 0x027;
588 else
589 return ~0;
590
591 unsigned serial = *(unsigned *)(buf + n);
592 if (serial == 0)
593 return ~0;
594 return serial;
595 }
596
597 char *
598 grub_find_device (const char *path, dev_t dev)
599 {
600 /* No root device for /cygdrive. */
601 if (dev == (DEV_CYGDRIVE_MAJOR << 16))
602 return 0;
603
604 /* Convert to full POSIX and Win32 path. */
605 char fullpath[PATH_MAX], winpath[PATH_MAX];
606 cygwin_conv_to_full_posix_path (path, fullpath);
607 cygwin_conv_to_full_win32_path (fullpath, winpath);
608
609 /* If identical, this is no real filesystem path. */
610 if (strcmp (fullpath, winpath) == 0)
611 return 0;
612
613 /* Check for floppy drive letter. */
614 if (winpath[0] && winpath[1] == ':' && strchr ("AaBb", winpath[0]))
615 return xstrdup (strchr ("Aa", winpath[0]) ? "/dev/fd0" : "/dev/fd1");
616
617 /* Cygwin returns the partition serial number in stat.st_dev.
618 This is never identical to the device number of the emulated
619 /dev/sdXN device, so above grub_find_device () does not work.
620 Search the partition with the same serial in boot sector instead. */
621 char devpath[sizeof ("/dev/sda15") + 13]; /* Size + Paranoia. */
622 int d;
623 for (d = 'a'; d <= 'z'; d++)
624 {
625 sprintf (devpath, "/dev/sd%c", d);
626 if (get_bootsec_serial (devpath, 1) == 0)
627 continue;
628 int p;
629 for (p = 1; p <= 15; p++)
630 {
631 sprintf (devpath, "/dev/sd%c%d", d, p);
632 unsigned ser = get_bootsec_serial (devpath, 0);
633 if (ser == 0)
634 break;
635 if (ser != (unsigned)~0 && dev == (dev_t)ser)
636 return xstrdup (devpath);
637 }
638 }
639 return 0;
640 }
641
642 #endif /* __CYGWIN__ */
643
644 char *
645 grub_guess_root_device (const char *dir)
646 {
647 char *os_dev = NULL;
648 #ifdef __GNU__
649 file_t file;
650 mach_port_t *ports;
651 int *ints;
652 loff_t *offsets;
653 char *data;
654 error_t err;
655 mach_msg_type_number_t num_ports = 0, num_ints = 0, num_offsets = 0, data_len = 0;
656 size_t name_len;
657
658 file = file_name_lookup (dir, 0, 0);
659 if (file == MACH_PORT_NULL)
660 return 0;
661
662 err = file_get_storage_info (file,
663 &ports, &num_ports,
664 &ints, &num_ints,
665 &offsets, &num_offsets,
666 &data, &data_len);
667
668 if (num_ints < 1)
669 grub_util_error (_("Storage info for `%s' does not include type"), dir);
670 if (ints[0] != STORAGE_DEVICE)
671 grub_util_error (_("Filesystem of `%s' is not stored on local disk"), dir);
672
673 if (num_ints < 5)
674 grub_util_error (_("Storage info for `%s' does not include name"), dir);
675 name_len = ints[4];
676 if (name_len < data_len)
677 grub_util_error (_("Bogus name length for storage info for `%s'"), dir);
678 if (data[name_len - 1] != '\0')
679 grub_util_error (_("Storage name for `%s' not NUL-terminated"), dir);
680
681 os_dev = xmalloc (strlen ("/dev/") + data_len);
682 memcpy (os_dev, "/dev/", strlen ("/dev/"));
683 memcpy (os_dev + strlen ("/dev/"), data, data_len);
684
685 if (ports && num_ports > 0)
686 {
687 mach_msg_type_number_t i;
688 for (i = 0; i < num_ports; i++)
689 {
690 mach_port_t port = ports[i];
691 if (port != MACH_PORT_NULL)
692 mach_port_deallocate (mach_task_self(), port);
693 }
694 munmap ((caddr_t) ports, num_ports * sizeof (*ports));
695 }
696
697 if (ints && num_ints > 0)
698 munmap ((caddr_t) ints, num_ints * sizeof (*ints));
699 if (offsets && num_offsets > 0)
700 munmap ((caddr_t) offsets, num_offsets * sizeof (*offsets));
701 if (data && data_len > 0)
702 munmap (data, data_len);
703 mach_port_deallocate (mach_task_self (), file);
704 #else /* !__GNU__ */
705 struct stat st;
706 dev_t dev;
707
708 #ifdef __linux__
709 if (!os_dev)
710 os_dev = grub_find_root_device_from_mountinfo (dir, NULL);
711 #endif /* __linux__ */
712
713 if (!os_dev)
714 os_dev = find_root_device_from_libzfs (dir);
715
716 if (os_dev)
717 {
718 char *tmp = os_dev;
719 os_dev = canonicalize_file_name (os_dev);
720 free (tmp);
721 }
722
723 if (os_dev)
724 {
725 int dm = (strncmp (os_dev, "/dev/dm-", sizeof ("/dev/dm-") - 1) == 0);
726 int root = (strcmp (os_dev, "/dev/root") == 0);
727 if (!dm && !root)
728 return os_dev;
729 if (stat (os_dev, &st) >= 0)
730 {
731 free (os_dev);
732 dev = st.st_rdev;
733 return grub_find_device (dm ? "/dev/mapper" : "/dev", dev);
734 }
735 free (os_dev);
736 }
737
738 if (stat (dir, &st) < 0)
739 grub_util_error (_("cannot stat `%s'"), dir);
740
741 dev = st.st_dev;
742
743 #ifdef __CYGWIN__
744 /* Cygwin specific function. */
745 os_dev = grub_find_device (dir, dev);
746
747 #else
748
749 /* This might be truly slow, but is there any better way? */
750 os_dev = grub_find_device ("/dev", dev);
751 #endif
752 #endif /* !__GNU__ */
753
754 return os_dev;
755 }
756
757 #ifdef HAVE_DEVICE_MAPPER
758
759 static int
760 grub_util_open_dm (const char *os_dev, struct dm_tree **tree,
761 struct dm_tree_node **node)
762 {
763 uint32_t maj, min;
764 struct stat st;
765
766 *node = NULL;
767 *tree = NULL;
768
769 if ((strncmp ("/dev/mapper/", os_dev, 12) != 0))
770 return 0;
771
772 if (stat (os_dev, &st) < 0)
773 return 0;
774
775 *tree = dm_tree_create ();
776 if (! *tree)
777 {
778 grub_puts_ (N_("Failed to create tree"));
779 grub_dprintf ("hostdisk", "dm_tree_create failed\n");
780 return 0;
781 }
782
783 maj = major (st.st_rdev);
784 min = minor (st.st_rdev);
785
786 if (! dm_tree_add_dev (*tree, maj, min))
787 {
788 grub_dprintf ("hostdisk", "dm_tree_add_dev failed\n");
789 dm_tree_free (*tree);
790 *tree = NULL;
791 return 0;
792 }
793
794 *node = dm_tree_find_node (*tree, maj, min);
795 if (! *node)
796 {
797 grub_dprintf ("hostdisk", "dm_tree_find_node failed\n");
798 dm_tree_free (*tree);
799 *tree = NULL;
800 return 0;
801 }
802 return 1;
803 }
804
805 #endif
806
807 static char *
808 get_dm_uuid (const char *os_dev)
809 {
810 if ((strncmp ("/dev/mapper/", os_dev, 12) != 0))
811 return NULL;
812
813 #ifdef HAVE_DEVICE_MAPPER
814 {
815 struct dm_tree *tree;
816 struct dm_tree_node *node;
817 const char *node_uuid;
818 char *ret;
819
820 if (!grub_util_open_dm (os_dev, &tree, &node))
821 return NULL;
822
823 node_uuid = dm_tree_node_get_uuid (node);
824 if (! node_uuid)
825 {
826 grub_dprintf ("hostdisk", "%s has no DM uuid\n", os_dev);
827 dm_tree_free (tree);
828 return NULL;
829 }
830
831 ret = grub_strdup (node_uuid);
832
833 dm_tree_free (tree);
834
835 return ret;
836 }
837 #endif
838
839 return NULL;
840 }
841
842 static enum grub_dev_abstraction_types
843 grub_util_get_dm_abstraction (const char *os_dev)
844 {
845 #ifdef HAVE_DEVICE_MAPPER
846 char *uuid;
847
848 uuid = get_dm_uuid (os_dev);
849
850 if (uuid == NULL)
851 return GRUB_DEV_ABSTRACTION_NONE;
852
853 if (strncmp (uuid, "LVM-", 4) == 0)
854 {
855 grub_free (uuid);
856 return GRUB_DEV_ABSTRACTION_LVM;
857 }
858 if (strncmp (uuid, "CRYPT-LUKS1-", 4) == 0)
859 {
860 grub_free (uuid);
861 return GRUB_DEV_ABSTRACTION_LUKS;
862 }
863
864 grub_free (uuid);
865 return GRUB_DEV_ABSTRACTION_NONE;
866 #else
867 if ((strncmp ("/dev/mapper/", os_dev, 12) != 0))
868 return GRUB_DEV_ABSTRACTION_NONE;
869 return GRUB_DEV_ABSTRACTION_LVM;
870 #endif
871 }
872
873 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
874 #include <libgeom.h>
875
876 static const char *
877 grub_util_get_geom_abstraction (const char *dev)
878 {
879 char *whole;
880 struct gmesh mesh;
881 struct gclass *class;
882 const char *name;
883 int error;
884
885 if (strncmp (dev, "/dev/", sizeof ("/dev/") - 1) != 0)
886 return 0;
887 name = dev + sizeof ("/dev/") - 1;
888 grub_util_follow_gpart_up (name, NULL, &whole);
889
890 grub_util_info ("following geom '%s'", name);
891
892 error = geom_gettree (&mesh);
893 if (error != 0)
894 grub_util_error (_("couldn't open geom"));
895
896 LIST_FOREACH (class, &mesh.lg_class, lg_class)
897 {
898 struct ggeom *geom;
899 LIST_FOREACH (geom, &class->lg_geom, lg_geom)
900 {
901 struct gprovider *provider;
902 LIST_FOREACH (provider, &geom->lg_provider, lg_provider)
903 if (strcmp (provider->lg_name, name) == 0)
904 return class->lg_name;
905 }
906 }
907 return NULL;
908 }
909 #endif
910
911 int
912 grub_util_get_dev_abstraction (const char *os_dev)
913 {
914 #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
915 /* User explicitly claims that this drive is visible by BIOS. */
916 if (grub_util_biosdisk_is_present (os_dev))
917 return GRUB_DEV_ABSTRACTION_NONE;
918 #endif
919
920 #ifdef __linux__
921 enum grub_dev_abstraction_types ret;
922
923 /* Check for LVM and LUKS. */
924 ret = grub_util_get_dm_abstraction (os_dev);
925
926 if (ret != GRUB_DEV_ABSTRACTION_NONE)
927 return ret;
928
929 /* Check for RAID. */
930 if (!strncmp (os_dev, "/dev/md", 7) && ! grub_util_device_is_mapped (os_dev))
931 return GRUB_DEV_ABSTRACTION_RAID;
932 #endif
933
934 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
935 const char *abs;
936 abs = grub_util_get_geom_abstraction (os_dev);
937 grub_util_info ("abstraction of %s is %s", os_dev, abs);
938 if (abs && grub_strcasecmp (abs, "eli") == 0)
939 return GRUB_DEV_ABSTRACTION_GELI;
940
941 /* Check for LVM. */
942 if (!strncmp (os_dev, LVM_DEV_MAPPER_STRING, sizeof(LVM_DEV_MAPPER_STRING)-1))
943 return GRUB_DEV_ABSTRACTION_LVM;
944 #endif
945
946 /* No abstraction found. */
947 return GRUB_DEV_ABSTRACTION_NONE;
948 }
949
950 #ifdef __linux__
951 static char *
952 get_mdadm_uuid (const char *os_dev)
953 {
954 int mdadm_pipe[2];
955 pid_t mdadm_pid;
956 char *name = NULL;
957
958 if (pipe (mdadm_pipe) < 0)
959 {
960 grub_util_warn (_("Unable to create pipe for mdadm: %s"),
961 strerror (errno));
962 return NULL;
963 }
964
965 mdadm_pid = fork ();
966 if (mdadm_pid < 0)
967 grub_util_warn (_("Unable to fork mdadm: %s"), strerror (errno));
968 else if (mdadm_pid == 0)
969 {
970 /* Child. */
971 char *argv[5];
972
973 close (mdadm_pipe[0]);
974 dup2 (mdadm_pipe[1], STDOUT_FILENO);
975 close (mdadm_pipe[1]);
976
977 /* execvp has inconvenient types, hence the casts. None of these
978 strings will actually be modified. */
979 argv[0] = (char *) "mdadm";
980 argv[1] = (char *) "--detail";
981 argv[2] = (char *) "--export";
982 argv[3] = (char *) os_dev;
983 argv[4] = NULL;
984 execvp ("mdadm", argv);
985 exit (127);
986 }
987 else
988 {
989 /* Parent. Read mdadm's output. */
990 FILE *mdadm;
991 char *buf = NULL;
992 size_t len = 0;
993
994 close (mdadm_pipe[1]);
995 mdadm = fdopen (mdadm_pipe[0], "r");
996 if (! mdadm)
997 {
998 grub_util_warn (_("Unable to open stream from mdadm: %s"),
999 strerror (errno));
1000 goto out;
1001 }
1002
1003 while (getline (&buf, &len, mdadm) > 0)
1004 {
1005 if (strncmp (buf, "MD_UUID=", sizeof ("MD_UUID=") - 1) == 0)
1006 {
1007 char *name_start, *ptri, *ptro;
1008 size_t name_len;
1009
1010 free (name);
1011 name_start = buf + sizeof ("MD_UUID=") - 1;
1012 ptro = name = xmalloc (strlen (name_start) + 1);
1013 for (ptri = name_start; *ptri && *ptri != '\n' && *ptri != '\r';
1014 ptri++)
1015 if ((*ptri >= '0' && *ptri <= '9')
1016 || (*ptri >= 'a' && *ptri <= 'f')
1017 || (*ptri >= 'A' && *ptri <= 'F'))
1018 *ptro++ = *ptri;
1019 *ptro = 0;
1020 }
1021 }
1022
1023 out:
1024 close (mdadm_pipe[0]);
1025 waitpid (mdadm_pid, NULL, 0);
1026 }
1027
1028 return name;
1029 }
1030 #endif /* __linux__ */
1031
1032 void
1033 grub_util_pull_device (const char *os_dev)
1034 {
1035 int ab;
1036 ab = grub_util_get_dev_abstraction (os_dev);
1037 switch (ab)
1038 {
1039 case GRUB_DEV_ABSTRACTION_GELI:
1040 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
1041 {
1042 char *whole;
1043 struct gmesh mesh;
1044 struct gclass *class;
1045 const char *name;
1046 int error;
1047 char *lastsubdev = NULL;
1048
1049 if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0)
1050 return;
1051 name = os_dev + sizeof ("/dev/") - 1;
1052 grub_util_follow_gpart_up (name, NULL, &whole);
1053
1054 grub_util_info ("following geom '%s'", name);
1055
1056 error = geom_gettree (&mesh);
1057 if (error != 0)
1058 grub_util_error (_("couldn't open geom"));
1059
1060 LIST_FOREACH (class, &mesh.lg_class, lg_class)
1061 {
1062 struct ggeom *geom;
1063 LIST_FOREACH (geom, &class->lg_geom, lg_geom)
1064 {
1065 struct gprovider *provider;
1066 LIST_FOREACH (provider, &geom->lg_provider, lg_provider)
1067 if (strcmp (provider->lg_name, name) == 0)
1068 {
1069 struct gconsumer *consumer;
1070 char *fname;
1071 char *uuid;
1072
1073 LIST_FOREACH (consumer, &geom->lg_consumer, lg_consumer)
1074 break;
1075 if (!consumer)
1076 grub_util_error (_("couldn't find geli consumer"));
1077 fname = xasprintf ("/dev/%s", consumer->lg_provider->lg_name);
1078 grub_util_info ("consumer %s", consumer->lg_provider->lg_name);
1079 lastsubdev = consumer->lg_provider->lg_name;
1080 grub_util_pull_device (fname);
1081 free (fname);
1082 }
1083 }
1084 }
1085 if (ab == GRUB_DEV_ABSTRACTION_GELI && lastsubdev)
1086 {
1087 char *fname = xasprintf ("/dev/%s", lastsubdev);
1088 char *grdev = grub_util_get_grub_dev (fname);
1089 free (fname);
1090
1091 if (grdev)
1092 {
1093 grub_err_t err;
1094 err = grub_cryptodisk_cheat_mount (grdev, os_dev);
1095 if (err)
1096 grub_util_error (_("Can't mount crypto: %s"), _(grub_errmsg));
1097 }
1098
1099 grub_free (grdev);
1100 }
1101 }
1102 #endif
1103 break;
1104
1105 case GRUB_DEV_ABSTRACTION_LVM:
1106 case GRUB_DEV_ABSTRACTION_LUKS:
1107 #ifdef HAVE_DEVICE_MAPPER
1108 {
1109 struct dm_tree *tree;
1110 struct dm_tree_node *node;
1111 struct dm_tree_node *child;
1112 void *handle = NULL;
1113 char *lastsubdev = NULL;
1114
1115 if (!grub_util_open_dm (os_dev, &tree, &node))
1116 return;
1117
1118 while ((child = dm_tree_next_child (&handle, node, 0)))
1119 {
1120 const struct dm_info *dm = dm_tree_node_get_info (child);
1121 char *subdev;
1122 if (!dm)
1123 continue;
1124 subdev = grub_find_device ("/dev", makedev (dm->major, dm->minor));
1125 if (subdev)
1126 {
1127 lastsubdev = subdev;
1128 grub_util_pull_device (subdev);
1129 }
1130 }
1131 if (ab == GRUB_DEV_ABSTRACTION_LUKS && lastsubdev)
1132 {
1133 char *grdev = grub_util_get_grub_dev (lastsubdev);
1134 dm_tree_free (tree);
1135 if (grdev)
1136 {
1137 grub_err_t err;
1138 err = grub_cryptodisk_cheat_mount (grdev, os_dev);
1139 if (err)
1140 grub_util_error (_("Can't mount crypto: %s"), _(grub_errmsg));
1141 }
1142 grub_free (grdev);
1143 }
1144 else
1145 dm_tree_free (tree);
1146 }
1147 #endif
1148 return;
1149 case GRUB_DEV_ABSTRACTION_RAID:
1150 #ifdef __linux__
1151 {
1152 char **devicelist = grub_util_raid_getmembers (os_dev, 0);
1153 int i;
1154 for (i = 0; devicelist[i];i++)
1155 grub_util_pull_device (devicelist[i]);
1156 free (devicelist);
1157 }
1158 #endif
1159 return;
1160
1161 default: /* GRUB_DEV_ABSTRACTION_NONE */
1162 free (grub_util_biosdisk_get_grub_dev (os_dev));
1163 return;
1164 }
1165 }
1166
1167 char *
1168 grub_util_get_grub_dev (const char *os_dev)
1169 {
1170 char *grub_dev = NULL;
1171
1172 grub_util_pull_device (os_dev);
1173
1174 switch (grub_util_get_dev_abstraction (os_dev))
1175 {
1176 #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1177 case GRUB_DEV_ABSTRACTION_LVM:
1178
1179 {
1180 unsigned short i, len;
1181 grub_size_t offset = sizeof (LVM_DEV_MAPPER_STRING) - 1;
1182
1183 len = strlen (os_dev) - offset + 1;
1184 grub_dev = xmalloc (len + sizeof ("lvm/"));
1185
1186 grub_memcpy (grub_dev, "lvm/", sizeof ("lvm/") - 1);
1187 grub_memcpy (grub_dev + sizeof ("lvm/") - 1, os_dev + offset, len);
1188 }
1189
1190 break;
1191 #endif
1192
1193 #ifdef __linux__
1194 case GRUB_DEV_ABSTRACTION_LUKS:
1195 {
1196 char *uuid, *dash;
1197 uuid = get_dm_uuid (os_dev);
1198 if (!uuid)
1199 break;
1200 dash = grub_strchr (uuid + sizeof ("CRYPT-LUKS1-") - 1, '-');
1201 if (dash)
1202 *dash = 0;
1203 grub_dev = grub_xasprintf ("cryptouuid/%s",
1204 uuid + sizeof ("CRYPT-LUKS1-") - 1);
1205 grub_free (uuid);
1206 }
1207 break;
1208 #endif
1209
1210 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
1211 case GRUB_DEV_ABSTRACTION_GELI:
1212 {
1213 char *whole;
1214 struct gmesh mesh;
1215 struct gclass *class;
1216 const char *name;
1217 int error;
1218
1219 if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0)
1220 return 0;
1221 name = os_dev + sizeof ("/dev/") - 1;
1222 grub_util_follow_gpart_up (name, NULL, &whole);
1223
1224 grub_util_info ("following geom '%s'", name);
1225
1226 error = geom_gettree (&mesh);
1227 if (error != 0)
1228 grub_util_error (_("couldn't open geom"));
1229
1230 LIST_FOREACH (class, &mesh.lg_class, lg_class)
1231 {
1232 struct ggeom *geom;
1233 LIST_FOREACH (geom, &class->lg_geom, lg_geom)
1234 {
1235 struct gprovider *provider;
1236 LIST_FOREACH (provider, &geom->lg_provider, lg_provider)
1237 if (strcmp (provider->lg_name, name) == 0)
1238 {
1239 struct gconsumer *consumer;
1240 char *fname;
1241 char *uuid;
1242
1243 LIST_FOREACH (consumer, &geom->lg_consumer, lg_consumer)
1244 break;
1245 if (!consumer)
1246 grub_util_error (_("couldn't find geli consumer"));
1247 fname = xasprintf ("/dev/%s", consumer->lg_provider->lg_name);
1248 uuid = grub_util_get_geli_uuid (fname);
1249 if (!uuid)
1250 grub_util_error (_("couldn't retrieve geli UUID"));
1251 grub_dev = xasprintf ("cryptouuid/%s", uuid);
1252 free (fname);
1253 free (uuid);
1254 }
1255 }
1256 }
1257 }
1258 break;
1259 #endif
1260
1261 #ifdef __linux__
1262 case GRUB_DEV_ABSTRACTION_RAID:
1263
1264 if (os_dev[7] == '_' && os_dev[8] == 'd')
1265 {
1266 /* This a partitionable RAID device of the form /dev/md_dNNpMM. */
1267
1268 char *p, *q;
1269
1270 p = strdup (os_dev + sizeof ("/dev/md_d") - 1);
1271
1272 q = strchr (p, 'p');
1273 if (q)
1274 *q = ',';
1275
1276 grub_dev = xasprintf ("md%s", p);
1277 free (p);
1278 }
1279 else if (os_dev[7] == '/' && os_dev[8] == 'd')
1280 {
1281 /* This a partitionable RAID device of the form /dev/md/dNNpMM. */
1282
1283 char *p, *q;
1284
1285 p = strdup (os_dev + sizeof ("/dev/md/d") - 1);
1286
1287 q = strchr (p, 'p');
1288 if (q)
1289 *q = ',';
1290
1291 grub_dev = xasprintf ("md%s", p);
1292 free (p);
1293 }
1294 else if (os_dev[7] >= '0' && os_dev[7] <= '9')
1295 {
1296 char *p , *q;
1297
1298 p = strdup (os_dev + sizeof ("/dev/md") - 1);
1299
1300 q = strchr (p, 'p');
1301 if (q)
1302 *q = ',';
1303
1304 grub_dev = xasprintf ("md%s", p);
1305 free (p);
1306 }
1307 else if (os_dev[7] == '/' && os_dev[8] >= '0' && os_dev[8] <= '9')
1308 {
1309 char *p , *q;
1310
1311 p = strdup (os_dev + sizeof ("/dev/md/") - 1);
1312
1313 q = strchr (p, 'p');
1314 if (q)
1315 *q = ',';
1316
1317 grub_dev = xasprintf ("md%s", p);
1318 free (p);
1319 }
1320 else if (os_dev[7] == '/')
1321 {
1322 /* mdraid 1.x with a free name. */
1323 char *p , *q;
1324
1325 p = strdup (os_dev + sizeof ("/dev/md/") - 1);
1326
1327 q = strchr (p, 'p');
1328 if (q)
1329 *q = ',';
1330
1331 grub_dev = xasprintf ("md/%s", p);
1332 free (p);
1333 }
1334 else
1335 grub_util_error (_("unknown kind of RAID device `%s'"), os_dev);
1336
1337 {
1338 char *mdadm_name = get_mdadm_uuid (os_dev);
1339 struct stat st;
1340
1341 if (mdadm_name)
1342 {
1343 const char *q;
1344
1345 for (q = os_dev + strlen (os_dev) - 1; q >= os_dev
1346 && grub_isdigit (*q); q--);
1347
1348 if (q >= os_dev && *q == 'p')
1349 {
1350 free (grub_dev);
1351 grub_dev = xasprintf ("mduuid/%s,%s", mdadm_name, q + 1);
1352 goto done;
1353 }
1354 free (grub_dev);
1355 grub_dev = xasprintf ("mduuid/%s", mdadm_name);
1356
1357 done:
1358 free (mdadm_name);
1359 }
1360 }
1361 break;
1362 #endif /* __linux__ */
1363
1364 default: /* GRUB_DEV_ABSTRACTION_NONE */
1365 grub_dev = grub_util_biosdisk_get_grub_dev (os_dev);
1366 }
1367
1368 return grub_dev;
1369 }
1370
1371 const char *
1372 grub_util_check_block_device (const char *blk_dev)
1373 {
1374 struct stat st;
1375
1376 if (stat (blk_dev, &st) < 0)
1377 grub_util_error (_("cannot stat `%s'"), blk_dev);
1378
1379 if (S_ISBLK (st.st_mode))
1380 return (blk_dev);
1381 else
1382 return 0;
1383 }
1384
1385 const char *
1386 grub_util_check_char_device (const char *blk_dev)
1387 {
1388 struct stat st;
1389
1390 if (stat (blk_dev, &st) < 0)
1391 grub_util_error (_("cannot stat `%s'"), blk_dev);
1392
1393 if (S_ISCHR (st.st_mode))
1394 return (blk_dev);
1395 else
1396 return 0;
1397 }
1398
1399 #ifdef __CYGWIN__
1400 /* Convert POSIX path to Win32 path,
1401 remove drive letter, replace backslashes. */
1402 static char *
1403 get_win32_path (const char *path)
1404 {
1405 char winpath[PATH_MAX];
1406 if (cygwin_conv_path (CCP_POSIX_TO_WIN_A, path, winpath, sizeof(winpath)))
1407 grub_util_error (_("cygwin_conv_path() failed"));
1408
1409 int len = strlen (winpath);
1410 int offs = (len > 2 && winpath[1] == ':' ? 2 : 0);
1411
1412 int i;
1413 for (i = offs; i < len; i++)
1414 if (winpath[i] == '\\')
1415 winpath[i] = '/';
1416 return xstrdup (winpath + offs);
1417 }
1418 #endif
1419
1420 #ifdef HAVE_LIBZFS
1421 static libzfs_handle_t *__libzfs_handle;
1422
1423 static void
1424 fini_libzfs (void)
1425 {
1426 libzfs_fini (__libzfs_handle);
1427 }
1428
1429 libzfs_handle_t *
1430 grub_get_libzfs_handle (void)
1431 {
1432 if (! __libzfs_handle)
1433 {
1434 __libzfs_handle = libzfs_init ();
1435
1436 if (__libzfs_handle)
1437 atexit (fini_libzfs);
1438 }
1439
1440 return __libzfs_handle;
1441 }
1442 #endif /* HAVE_LIBZFS */
1443
1444 /* ZFS has similar problems to those of btrfs (see above). */
1445 void
1446 grub_find_zpool_from_dir (const char *dir, char **poolname, char **poolfs)
1447 {
1448 char *slash;
1449
1450 *poolname = *poolfs = NULL;
1451
1452 #if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) && defined(HAVE_STRUCT_STATFS_F_MNTFROMNAME)
1453 /* FreeBSD and GNU/kFreeBSD. */
1454 {
1455 struct statfs mnt;
1456
1457 if (statfs (dir, &mnt) != 0)
1458 return;
1459
1460 if (strcmp (mnt.f_fstypename, "zfs") != 0)
1461 return;
1462
1463 *poolname = xstrdup (mnt.f_mntfromname);
1464 }
1465 #elif defined(HAVE_GETEXTMNTENT)
1466 /* Solaris. */
1467 {
1468 struct stat st;
1469 struct extmnttab mnt;
1470
1471 if (stat (dir, &st) != 0)
1472 return;
1473
1474 FILE *mnttab = fopen ("/etc/mnttab", "r");
1475 if (! mnttab)
1476 return;
1477
1478 while (getextmntent (mnttab, &mnt, sizeof (mnt)) == 0)
1479 {
1480 if (makedev (mnt.mnt_major, mnt.mnt_minor) == st.st_dev
1481 && !strcmp (mnt.mnt_fstype, "zfs"))
1482 {
1483 *poolname = xstrdup (mnt.mnt_special);
1484 break;
1485 }
1486 }
1487
1488 fclose (mnttab);
1489 }
1490 #endif
1491
1492 if (! *poolname)
1493 return;
1494
1495 slash = strchr (*poolname, '/');
1496 if (slash)
1497 {
1498 *slash = '\0';
1499 *poolfs = xstrdup (slash + 1);
1500 }
1501 else
1502 *poolfs = xstrdup ("");
1503 }
1504
1505 /* This function never prints trailing slashes (so that its output
1506 can be appended a slash unconditionally). */
1507 char *
1508 grub_make_system_path_relative_to_its_root (const char *path)
1509 {
1510 struct stat st;
1511 char *p, *buf, *buf2, *buf3, *ret;
1512 uintptr_t offset = 0;
1513 dev_t num;
1514 size_t len;
1515 char *poolfs = NULL;
1516
1517 /* canonicalize. */
1518 p = canonicalize_file_name (path);
1519 if (p == NULL)
1520 grub_util_error (_("failed to get canonical path of %s"), path);
1521
1522 /* For ZFS sub-pool filesystems, could be extended to others (btrfs?). */
1523 {
1524 char *dummy;
1525 grub_find_zpool_from_dir (p, &dummy, &poolfs);
1526 }
1527
1528 len = strlen (p) + 1;
1529 buf = xstrdup (p);
1530 free (p);
1531
1532 if (stat (buf, &st) < 0)
1533 grub_util_error (_("cannot stat %s: %s"), buf, strerror (errno));
1534
1535 buf2 = xstrdup (buf);
1536 num = st.st_dev;
1537
1538 /* This loop sets offset to the number of chars of the root
1539 directory we're inspecting. */
1540 while (1)
1541 {
1542 p = strrchr (buf, '/');
1543 if (p == NULL)
1544 /* This should never happen. */
1545 grub_util_error (_("FIXME: no / in buf. (make_system_path_relative_to_its_root)"));
1546 if (p != buf)
1547 *p = 0;
1548 else
1549 *++p = 0;
1550
1551 if (stat (buf, &st) < 0)
1552 grub_util_error (_("cannot stat %s: %s"), buf, strerror (errno));
1553
1554 /* buf is another filesystem; we found it. */
1555 if (st.st_dev != num)
1556 {
1557 /* offset == 0 means path given is the mount point.
1558 This works around special-casing of "/" in Un*x. This function never
1559 prints trailing slashes (so that its output can be appended a slash
1560 unconditionally). Each slash in is considered a preceding slash, and
1561 therefore the root directory is an empty string. */
1562 if (offset == 0)
1563 {
1564 free (buf);
1565 #ifdef __linux__
1566 {
1567 char *bind;
1568 grub_free (grub_find_root_device_from_mountinfo (buf2, &bind));
1569 if (bind && bind[0] && bind[1])
1570 {
1571 buf3 = bind;
1572 goto parsedir;
1573 }
1574 grub_free (bind);
1575 }
1576 #endif
1577 free (buf2);
1578 if (poolfs)
1579 return xasprintf ("/%s/@", poolfs);
1580 return xstrdup ("");
1581 }
1582 else
1583 break;
1584 }
1585
1586 offset = p - buf;
1587 /* offset == 1 means root directory. */
1588 if (offset == 1)
1589 {
1590 /* Include leading slash. */
1591 offset = 0;
1592 break;
1593 }
1594 }
1595 free (buf);
1596 buf3 = xstrdup (buf2 + offset);
1597 buf2[offset] = 0;
1598 #ifdef __linux__
1599 {
1600 char *bind;
1601 grub_free (grub_find_root_device_from_mountinfo (buf2, &bind));
1602 if (bind && bind[0] && bind[1])
1603 {
1604 char *temp = buf3;
1605 buf3 = grub_xasprintf ("%s%s%s", bind, buf3[0] == '/' ?"":"/", buf3);
1606 grub_free (temp);
1607 }
1608 grub_free (bind);
1609 }
1610 #endif
1611
1612 free (buf2);
1613
1614 #ifdef __CYGWIN__
1615 if (st.st_dev != (DEV_CYGDRIVE_MAJOR << 16))
1616 {
1617 /* Reached some mount point not below /cygdrive.
1618 GRUB does not know Cygwin's emulated mounts,
1619 convert to Win32 path. */
1620 grub_util_info ("Cygwin path = %s\n", buf3);
1621 char * temp = get_win32_path (buf3);
1622 free (buf3);
1623 buf3 = temp;
1624 }
1625 #endif
1626
1627 parsedir:
1628 /* Remove trailing slashes, return empty string if root directory. */
1629 len = strlen (buf3);
1630 while (len > 0 && buf3[len - 1] == '/')
1631 {
1632 buf3[len - 1] = '\0';
1633 len--;
1634 }
1635
1636 if (poolfs)
1637 {
1638 ret = xasprintf ("/%s/@%s", poolfs, buf3);
1639 free (buf3);
1640 }
1641 else
1642 ret = buf3;
1643
1644 return ret;
1645 }