1 /* getroot.c - Get root device */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc.
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.
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.
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/>.
20 #include <config-util.h>
24 #include <sys/types.h>
38 #include <grub/util/misc.h>
39 #include <grub/util/lvm.h>
40 #include <grub/cryptodisk.h>
41 #include <grub/i18n.h>
43 #ifdef HAVE_DEVICE_MAPPER
44 # include <libdevmapper.h>
49 #include <hurd/lookup.h>
55 # include <sys/types.h>
56 # include <sys/wait.h>
59 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
60 # include <sys/mount.h>
63 #if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
64 # include <grub/util/libzfs.h>
65 # include <grub/util/libnvpair.h>
69 # include <sys/types.h>
70 # include <sys/mkdev.h>
74 #include <grub/misc.h>
75 #include <grub/emu/misc.h>
76 #include <grub/emu/hostdisk.h>
77 #include <grub/emu/getroot.h>
80 strip_extra_slashes (char *dir
)
84 while ((p
= strchr (p
, '/')) != 0)
88 memmove (p
, p
+ 1, strlen (p
));
91 else if (p
[1] == '\0')
108 path
= xmalloc (size
);
109 while (! getcwd (path
, size
))
112 path
= xrealloc (path
, size
);
120 #define ESCAPED_PATH_MAX (4 * PATH_MAX)
121 struct mountinfo_entry
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];
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. */
141 for (iptr
= optr
= str
; *iptr
; optr
++)
143 if (iptr
[0] == '\\' && iptr
[1] >= '0' && iptr
[1] < '8'
144 && iptr
[2] >= '0' && iptr
[2] < '8'
145 && iptr
[3] >= '0' && iptr
[3] < '8')
147 *optr
= (((iptr
[1] - '0') << 6) | ((iptr
[2] - '0') << 3)
158 grub_find_root_device_from_mountinfo (const char *dir
, char **relroot
)
164 int entry_len
= 0, entry_max
= 4;
165 struct mountinfo_entry
*entries
;
166 struct mountinfo_entry parent_entry
= { 0, 0, 0, "", "", "", "" };
174 fp
= fopen ("/proc/self/mountinfo", "r");
176 return NULL
; /* fall through to other methods */
178 entries
= xmalloc (entry_max
* sizeof (*entries
));
180 /* First, build a list of relevant visible mounts. */
181 while (getline (&buf
, &len
, fp
) > 0)
183 struct mountinfo_entry entry
;
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)
193 unescape (entry
.enc_root
);
194 unescape (entry
.enc_path
);
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
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
] != '/'))
205 sep
= strstr (buf
+ count
, " - ");
209 sep
+= sizeof (" - ") - 1;
210 if (sscanf (sep
, "%s %s", entry
.fstype
, entry
.device
) != 2)
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
)
225 entries
= xrealloc (entries
, entry_max
* sizeof (*entries
));
230 /* Initialise list. */
232 entries
[0] = parent_entry
;
237 for (i
= entry_len
- 1; i
>= 0; i
--)
239 if (entries
[i
].id
== parent_entry
.id
)
241 /* Insert at end, pruning anything previously above this. */
243 entries
[i
+ 1] = entry
;
246 else if (i
== 0 && entries
[i
].id
== entry
.id
)
248 /* Insert at start. */
250 memmove (entries
+ 1, entries
,
251 (entry_len
- 1) * sizeof (*entries
));
252 entries
[0] = parent_entry
;
260 /* Now scan visible mounts for the ones we're interested in. */
261 for (i
= entry_len
- 1; i
>= 0; i
--)
263 if (!*entries
[i
].device
)
266 ret
= strdup (entries
[i
].device
);
268 *relroot
= strdup (entries
[i
].enc_root
);
278 #endif /* __linux__ */
281 find_root_device_from_libzfs (const char *dir
)
287 grub_find_zpool_from_dir (dir
, &poolname
, &poolfs
);
291 #if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
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
;
300 libzfs
= grub_get_libzfs_handle ();
304 zpool
= zpool_open (libzfs
, poolname
);
305 config
= zpool_get_config (zpool
, NULL
);
307 if (nvlist_lookup_nvlist (config
, "vdev_tree", &vdev_tree
) != 0)
308 error (1, errno
, "nvlist_lookup_nvlist (\"vdev_tree\")");
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);
314 while (nvlist_lookup_nvlist_array (children
[0], "children",
315 &children
, &nvlist_count
) == 0)
316 assert (nvlist_count
> 0);
318 for (i
= 0; i
< nvlist_count
; i
++)
320 if (nvlist_lookup_string (children
[i
], "path", &device
) != 0)
321 error (1, errno
, "nvlist_lookup_string (\"path\")");
324 if (stat (device
, &st
) == 0)
327 if (grub_memcmp (device
, "/dev/dsk/", sizeof ("/dev/dsk/") - 1)
329 device
= xasprintf ("/dev/rdsk/%s",
330 device
+ sizeof ("/dev/dsk/") - 1);
331 else if (grub_memcmp (device
, "/devices", sizeof ("/devices") - 1)
333 && grub_memcmp (device
+ strlen (device
) - 4,
335 device
= xasprintf ("%s,raw", device
);
338 device
= xstrdup (device
);
356 char name
[PATH_MAX
+ 1], state
[257], readlen
[257], writelen
[257];
357 char cksum
[257], notes
[257];
360 cmd
= xasprintf ("zpool status %s", poolname
);
361 fp
= popen (cmd
, "r");
368 ret
= getline (&line
, &len
, fp
);
372 if (sscanf (line
, " %s %256s %256s %256s %256s %256s",
373 name
, state
, readlen
, writelen
, cksum
, notes
) >= 5)
377 if (!strcmp (name
, "NAME")
378 && !strcmp (state
, "STATE")
379 && !strcmp (readlen
, "READ")
380 && !strcmp (writelen
, "WRITE")
381 && !strcmp (cksum
, "CKSUM"))
385 if (!strcmp (name
, poolname
))
389 if (strcmp (name
, "mirror") && !sscanf (name
, "mirror-%u", &dummy
)
390 && !sscanf (name
, "raidz%u", &dummy
)
391 && !strcmp (state
, "ONLINE"))
398 device
= xasprintf ("/dev/%s", name
);
415 grub_find_device (const char *dir
__attribute__ ((unused
)),
416 dev_t dev
__attribute__ ((unused
)))
421 #elif ! defined(__CYGWIN__)
424 grub_find_device (const char *dir
, dev_t dev
)
443 saved_cwd
= xgetcwd ();
445 grub_util_info ("changing current directory to %s", dir
);
453 while ((ent
= readdir (dp
)) != 0)
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] == '.')
463 if (lstat (ent
->d_name
, &st
) < 0)
464 /* Ignore any error. */
467 if (S_ISLNK (st
.st_mode
)) {
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
474 if (stat (ent
->d_name
, &st
) < 0)
477 #endif /* __linux__ */
478 /* Don't follow other symbolic links. */
482 if (S_ISDIR (st
.st_mode
))
484 /* Find it recursively. */
487 res
= grub_find_device (ent
->d_name
, dev
);
491 if (chdir (saved_cwd
) < 0)
492 grub_util_error ("cannot restore the original directory");
500 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
501 if (S_ISCHR (st
.st_mode
) && st
.st_rdev
== dev
)
503 if (S_ISBLK (st
.st_mode
) && st
.st_rdev
== dev
)
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')
520 #if defined(__NetBSD__)
521 /* Convert this block device to its character (raw) device. */
522 const char *template = "%s/r%s";
524 /* Keep the device name as it is. */
525 const char *template = "%s/%s";
529 res
= xmalloc (strlen (cwd
) + strlen (ent
->d_name
) + 3);
530 sprintf (res
, template, cwd
, ent
->d_name
);
531 strip_extra_slashes (res
);
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
538 if (strcmp(res
, "/dev/root") == 0)
541 if (chdir (saved_cwd
) < 0)
542 grub_util_error (_("cannot restore the original directory"));
550 if (chdir (saved_cwd
) < 0)
551 grub_util_error (_("cannot restore the original directory"));
558 #else /* __CYGWIN__ */
560 /* Read drive/partition serial number from mbr/boot sector,
561 return 0 on read error, ~0 on unknown serial. */
563 get_bootsec_serial (const char *os_dev
, int mbr
)
565 /* Read boot sector. */
566 int fd
= open (os_dev
, O_RDONLY
);
569 unsigned char buf
[0x200];
570 int n
= read (fd
, buf
, sizeof (buf
));
572 if (n
!= sizeof(buf
))
575 /* Check signature. */
576 if (!(buf
[0x1fe] == 0x55 && buf
[0x1ff] == 0xaa))
579 /* Serial number offset depends on boot sector type. */
582 else if (memcmp (buf
+ 0x03, "NTFS", 4) == 0)
584 else if (memcmp (buf
+ 0x52, "FAT32", 5) == 0)
586 else if (memcmp (buf
+ 0x36, "FAT", 3) == 0)
591 unsigned serial
= *(unsigned *)(buf
+ n
);
598 grub_find_device (const char *path
, dev_t dev
)
600 /* No root device for /cygdrive. */
601 if (dev
== (DEV_CYGDRIVE_MAJOR
<< 16))
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
);
609 /* If identical, this is no real filesystem path. */
610 if (strcmp (fullpath
, winpath
) == 0)
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");
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. */
623 for (d
= 'a'; d
<= 'z'; d
++)
625 sprintf (devpath
, "/dev/sd%c", d
);
626 if (get_bootsec_serial (devpath
, 1) == 0)
629 for (p
= 1; p
<= 15; p
++)
631 sprintf (devpath
, "/dev/sd%c%d", d
, p
);
632 unsigned ser
= get_bootsec_serial (devpath
, 0);
635 if (ser
!= (unsigned)~0 && dev
== (dev_t
)ser
)
636 return xstrdup (devpath
);
642 #endif /* __CYGWIN__ */
645 grub_guess_root_device (const char *dir
)
655 mach_msg_type_number_t num_ports
= 0, num_ints
= 0, num_offsets
= 0, data_len
= 0;
658 file
= file_name_lookup (dir
, 0, 0);
659 if (file
== MACH_PORT_NULL
)
662 err
= file_get_storage_info (file
,
665 &offsets
, &num_offsets
,
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
);
674 grub_util_error (_("Storage info for `%s' does not include name"), dir
);
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
);
681 os_dev
= xmalloc (strlen ("/dev/") + data_len
);
682 memcpy (os_dev
, "/dev/", strlen ("/dev/"));
683 memcpy (os_dev
+ strlen ("/dev/"), data
, data_len
);
685 if (ports
&& num_ports
> 0)
687 mach_msg_type_number_t i
;
688 for (i
= 0; i
< num_ports
; i
++)
690 mach_port_t port
= ports
[i
];
691 if (port
!= MACH_PORT_NULL
)
692 mach_port_deallocate (mach_task_self(), port
);
694 munmap ((caddr_t
) ports
, num_ports
* sizeof (*ports
));
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
);
710 os_dev
= grub_find_root_device_from_mountinfo (dir
, NULL
);
711 #endif /* __linux__ */
714 os_dev
= find_root_device_from_libzfs (dir
);
719 os_dev
= canonicalize_file_name (os_dev
);
725 int dm
= (strncmp (os_dev
, "/dev/dm-", sizeof ("/dev/dm-") - 1) == 0);
726 int root
= (strcmp (os_dev
, "/dev/root") == 0);
729 if (stat (os_dev
, &st
) >= 0)
733 return grub_find_device (dm
? "/dev/mapper" : "/dev", dev
);
738 if (stat (dir
, &st
) < 0)
739 grub_util_error (_("cannot stat `%s'"), dir
);
744 /* Cygwin specific function. */
745 os_dev
= grub_find_device (dir
, dev
);
749 /* This might be truly slow, but is there any better way? */
750 os_dev
= grub_find_device ("/dev", dev
);
752 #endif /* !__GNU__ */
757 #ifdef HAVE_DEVICE_MAPPER
760 grub_util_open_dm (const char *os_dev
, struct dm_tree
**tree
,
761 struct dm_tree_node
**node
)
769 if ((strncmp ("/dev/mapper/", os_dev
, 12) != 0))
772 if (stat (os_dev
, &st
) < 0)
775 *tree
= dm_tree_create ();
778 grub_puts_ (N_("Failed to create tree"));
779 grub_dprintf ("hostdisk", "dm_tree_create failed\n");
783 maj
= major (st
.st_rdev
);
784 min
= minor (st
.st_rdev
);
786 if (! dm_tree_add_dev (*tree
, maj
, min
))
788 grub_dprintf ("hostdisk", "dm_tree_add_dev failed\n");
789 dm_tree_free (*tree
);
794 *node
= dm_tree_find_node (*tree
, maj
, min
);
797 grub_dprintf ("hostdisk", "dm_tree_find_node failed\n");
798 dm_tree_free (*tree
);
808 get_dm_uuid (const char *os_dev
)
810 if ((strncmp ("/dev/mapper/", os_dev
, 12) != 0))
813 #ifdef HAVE_DEVICE_MAPPER
815 struct dm_tree
*tree
;
816 struct dm_tree_node
*node
;
817 const char *node_uuid
;
820 if (!grub_util_open_dm (os_dev
, &tree
, &node
))
823 node_uuid
= dm_tree_node_get_uuid (node
);
826 grub_dprintf ("hostdisk", "%s has no DM uuid\n", os_dev
);
831 ret
= grub_strdup (node_uuid
);
842 static enum grub_dev_abstraction_types
843 grub_util_get_dm_abstraction (const char *os_dev
)
845 #ifdef HAVE_DEVICE_MAPPER
848 uuid
= get_dm_uuid (os_dev
);
851 return GRUB_DEV_ABSTRACTION_NONE
;
853 if (strncmp (uuid
, "LVM-", 4) == 0)
856 return GRUB_DEV_ABSTRACTION_LVM
;
858 if (strncmp (uuid
, "CRYPT-LUKS1-", 4) == 0)
861 return GRUB_DEV_ABSTRACTION_LUKS
;
865 return GRUB_DEV_ABSTRACTION_NONE
;
867 if ((strncmp ("/dev/mapper/", os_dev
, 12) != 0))
868 return GRUB_DEV_ABSTRACTION_NONE
;
869 return GRUB_DEV_ABSTRACTION_LVM
;
873 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
877 grub_util_get_geom_abstraction (const char *dev
)
881 struct gclass
*class;
885 if (strncmp (dev
, "/dev/", sizeof ("/dev/") - 1) != 0)
887 name
= dev
+ sizeof ("/dev/") - 1;
888 grub_util_follow_gpart_up (name
, NULL
, &whole
);
890 grub_util_info ("following geom '%s'", name
);
892 error
= geom_gettree (&mesh
);
894 grub_util_error (_("couldn't open geom"));
896 LIST_FOREACH (class, &mesh
.lg_class
, lg_class
)
899 LIST_FOREACH (geom
, &class->lg_geom
, lg_geom
)
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
;
912 grub_util_get_dev_abstraction (const char *os_dev
)
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
;
921 enum grub_dev_abstraction_types ret
;
923 /* Check for LVM and LUKS. */
924 ret
= grub_util_get_dm_abstraction (os_dev
);
926 if (ret
!= GRUB_DEV_ABSTRACTION_NONE
)
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
;
934 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
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
;
942 if (!strncmp (os_dev
, LVM_DEV_MAPPER_STRING
, sizeof(LVM_DEV_MAPPER_STRING
)-1))
943 return GRUB_DEV_ABSTRACTION_LVM
;
946 /* No abstraction found. */
947 return GRUB_DEV_ABSTRACTION_NONE
;
952 get_mdadm_uuid (const char *os_dev
)
958 if (pipe (mdadm_pipe
) < 0)
960 grub_util_warn (_("Unable to create pipe for mdadm: %s"),
967 grub_util_warn (_("Unable to fork mdadm: %s"), strerror (errno
));
968 else if (mdadm_pid
== 0)
973 close (mdadm_pipe
[0]);
974 dup2 (mdadm_pipe
[1], STDOUT_FILENO
);
975 close (mdadm_pipe
[1]);
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
;
984 execvp ("mdadm", argv
);
989 /* Parent. Read mdadm's output. */
994 close (mdadm_pipe
[1]);
995 mdadm
= fdopen (mdadm_pipe
[0], "r");
998 grub_util_warn (_("Unable to open stream from mdadm: %s"),
1003 while (getline (&buf
, &len
, mdadm
) > 0)
1005 if (strncmp (buf
, "MD_UUID=", sizeof ("MD_UUID=") - 1) == 0)
1007 char *name_start
, *ptri
, *ptro
;
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';
1015 if ((*ptri
>= '0' && *ptri
<= '9')
1016 || (*ptri
>= 'a' && *ptri
<= 'f')
1017 || (*ptri
>= 'A' && *ptri
<= 'F'))
1024 close (mdadm_pipe
[0]);
1025 waitpid (mdadm_pid
, NULL
, 0);
1030 #endif /* __linux__ */
1033 grub_util_pull_device (const char *os_dev
)
1036 ab
= grub_util_get_dev_abstraction (os_dev
);
1039 case GRUB_DEV_ABSTRACTION_GELI
:
1040 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
1044 struct gclass
*class;
1047 char *lastsubdev
= NULL
;
1049 if (strncmp (os_dev
, "/dev/", sizeof ("/dev/") - 1) != 0)
1051 name
= os_dev
+ sizeof ("/dev/") - 1;
1052 grub_util_follow_gpart_up (name
, NULL
, &whole
);
1054 grub_util_info ("following geom '%s'", name
);
1056 error
= geom_gettree (&mesh
);
1058 grub_util_error (_("couldn't open geom"));
1060 LIST_FOREACH (class, &mesh
.lg_class
, lg_class
)
1063 LIST_FOREACH (geom
, &class->lg_geom
, lg_geom
)
1065 struct gprovider
*provider
;
1066 LIST_FOREACH (provider
, &geom
->lg_provider
, lg_provider
)
1067 if (strcmp (provider
->lg_name
, name
) == 0)
1069 struct gconsumer
*consumer
;
1073 LIST_FOREACH (consumer
, &geom
->lg_consumer
, lg_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
);
1085 if (ab
== GRUB_DEV_ABSTRACTION_GELI
&& lastsubdev
)
1087 char *fname
= xasprintf ("/dev/%s", lastsubdev
);
1088 char *grdev
= grub_util_get_grub_dev (fname
);
1094 err
= grub_cryptodisk_cheat_mount (grdev
, os_dev
);
1096 grub_util_error (_("Can't mount crypto: %s"), _(grub_errmsg
));
1105 case GRUB_DEV_ABSTRACTION_LVM
:
1106 case GRUB_DEV_ABSTRACTION_LUKS
:
1107 #ifdef HAVE_DEVICE_MAPPER
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
;
1115 if (!grub_util_open_dm (os_dev
, &tree
, &node
))
1118 while ((child
= dm_tree_next_child (&handle
, node
, 0)))
1120 const struct dm_info
*dm
= dm_tree_node_get_info (child
);
1124 subdev
= grub_find_device ("/dev", makedev (dm
->major
, dm
->minor
));
1127 lastsubdev
= subdev
;
1128 grub_util_pull_device (subdev
);
1131 if (ab
== GRUB_DEV_ABSTRACTION_LUKS
&& lastsubdev
)
1133 char *grdev
= grub_util_get_grub_dev (lastsubdev
);
1134 dm_tree_free (tree
);
1138 err
= grub_cryptodisk_cheat_mount (grdev
, os_dev
);
1140 grub_util_error (_("Can't mount crypto: %s"), _(grub_errmsg
));
1145 dm_tree_free (tree
);
1149 case GRUB_DEV_ABSTRACTION_RAID
:
1152 char **devicelist
= grub_util_raid_getmembers (os_dev
, 0);
1154 for (i
= 0; devicelist
[i
];i
++)
1155 grub_util_pull_device (devicelist
[i
]);
1161 default: /* GRUB_DEV_ABSTRACTION_NONE */
1162 free (grub_util_biosdisk_get_grub_dev (os_dev
));
1168 grub_util_get_grub_dev (const char *os_dev
)
1170 char *grub_dev
= NULL
;
1172 grub_util_pull_device (os_dev
);
1174 switch (grub_util_get_dev_abstraction (os_dev
))
1176 #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1177 case GRUB_DEV_ABSTRACTION_LVM
:
1180 unsigned short i
, len
;
1181 grub_size_t offset
= sizeof (LVM_DEV_MAPPER_STRING
) - 1;
1183 len
= strlen (os_dev
) - offset
+ 1;
1184 grub_dev
= xmalloc (len
+ sizeof ("lvm/"));
1186 grub_memcpy (grub_dev
, "lvm/", sizeof ("lvm/") - 1);
1187 grub_memcpy (grub_dev
+ sizeof ("lvm/") - 1, os_dev
+ offset
, len
);
1194 case GRUB_DEV_ABSTRACTION_LUKS
:
1197 uuid
= get_dm_uuid (os_dev
);
1200 dash
= grub_strchr (uuid
+ sizeof ("CRYPT-LUKS1-") - 1, '-');
1203 grub_dev
= grub_xasprintf ("cryptouuid/%s",
1204 uuid
+ sizeof ("CRYPT-LUKS1-") - 1);
1210 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
1211 case GRUB_DEV_ABSTRACTION_GELI
:
1215 struct gclass
*class;
1219 if (strncmp (os_dev
, "/dev/", sizeof ("/dev/") - 1) != 0)
1221 name
= os_dev
+ sizeof ("/dev/") - 1;
1222 grub_util_follow_gpart_up (name
, NULL
, &whole
);
1224 grub_util_info ("following geom '%s'", name
);
1226 error
= geom_gettree (&mesh
);
1228 grub_util_error (_("couldn't open geom"));
1230 LIST_FOREACH (class, &mesh
.lg_class
, lg_class
)
1233 LIST_FOREACH (geom
, &class->lg_geom
, lg_geom
)
1235 struct gprovider
*provider
;
1236 LIST_FOREACH (provider
, &geom
->lg_provider
, lg_provider
)
1237 if (strcmp (provider
->lg_name
, name
) == 0)
1239 struct gconsumer
*consumer
;
1243 LIST_FOREACH (consumer
, &geom
->lg_consumer
, lg_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
);
1250 grub_util_error (_("couldn't retrieve geli UUID"));
1251 grub_dev
= xasprintf ("cryptouuid/%s", uuid
);
1262 case GRUB_DEV_ABSTRACTION_RAID
:
1264 if (os_dev
[7] == '_' && os_dev
[8] == 'd')
1266 /* This a partitionable RAID device of the form /dev/md_dNNpMM. */
1270 p
= strdup (os_dev
+ sizeof ("/dev/md_d") - 1);
1272 q
= strchr (p
, 'p');
1276 grub_dev
= xasprintf ("md%s", p
);
1279 else if (os_dev
[7] == '/' && os_dev
[8] == 'd')
1281 /* This a partitionable RAID device of the form /dev/md/dNNpMM. */
1285 p
= strdup (os_dev
+ sizeof ("/dev/md/d") - 1);
1287 q
= strchr (p
, 'p');
1291 grub_dev
= xasprintf ("md%s", p
);
1294 else if (os_dev
[7] >= '0' && os_dev
[7] <= '9')
1298 p
= strdup (os_dev
+ sizeof ("/dev/md") - 1);
1300 q
= strchr (p
, 'p');
1304 grub_dev
= xasprintf ("md%s", p
);
1307 else if (os_dev
[7] == '/' && os_dev
[8] >= '0' && os_dev
[8] <= '9')
1311 p
= strdup (os_dev
+ sizeof ("/dev/md/") - 1);
1313 q
= strchr (p
, 'p');
1317 grub_dev
= xasprintf ("md%s", p
);
1320 else if (os_dev
[7] == '/')
1322 /* mdraid 1.x with a free name. */
1325 p
= strdup (os_dev
+ sizeof ("/dev/md/") - 1);
1327 q
= strchr (p
, 'p');
1331 grub_dev
= xasprintf ("md/%s", p
);
1335 grub_util_error (_("unknown kind of RAID device `%s'"), os_dev
);
1338 char *mdadm_name
= get_mdadm_uuid (os_dev
);
1345 for (q
= os_dev
+ strlen (os_dev
) - 1; q
>= os_dev
1346 && grub_isdigit (*q
); q
--);
1348 if (q
>= os_dev
&& *q
== 'p')
1351 grub_dev
= xasprintf ("mduuid/%s,%s", mdadm_name
, q
+ 1);
1355 grub_dev
= xasprintf ("mduuid/%s", mdadm_name
);
1362 #endif /* __linux__ */
1364 default: /* GRUB_DEV_ABSTRACTION_NONE */
1365 grub_dev
= grub_util_biosdisk_get_grub_dev (os_dev
);
1372 grub_util_check_block_device (const char *blk_dev
)
1376 if (stat (blk_dev
, &st
) < 0)
1377 grub_util_error (_("cannot stat `%s'"), blk_dev
);
1379 if (S_ISBLK (st
.st_mode
))
1386 grub_util_check_char_device (const char *blk_dev
)
1390 if (stat (blk_dev
, &st
) < 0)
1391 grub_util_error (_("cannot stat `%s'"), blk_dev
);
1393 if (S_ISCHR (st
.st_mode
))
1400 /* Convert POSIX path to Win32 path,
1401 remove drive letter, replace backslashes. */
1403 get_win32_path (const char *path
)
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"));
1409 int len
= strlen (winpath
);
1410 int offs
= (len
> 2 && winpath
[1] == ':' ? 2 : 0);
1413 for (i
= offs
; i
< len
; i
++)
1414 if (winpath
[i
] == '\\')
1416 return xstrdup (winpath
+ offs
);
1421 static libzfs_handle_t
*__libzfs_handle
;
1426 libzfs_fini (__libzfs_handle
);
1430 grub_get_libzfs_handle (void)
1432 if (! __libzfs_handle
)
1434 __libzfs_handle
= libzfs_init ();
1436 if (__libzfs_handle
)
1437 atexit (fini_libzfs
);
1440 return __libzfs_handle
;
1442 #endif /* HAVE_LIBZFS */
1444 /* ZFS has similar problems to those of btrfs (see above). */
1446 grub_find_zpool_from_dir (const char *dir
, char **poolname
, char **poolfs
)
1450 *poolname
= *poolfs
= NULL
;
1452 #if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) && defined(HAVE_STRUCT_STATFS_F_MNTFROMNAME)
1453 /* FreeBSD and GNU/kFreeBSD. */
1457 if (statfs (dir
, &mnt
) != 0)
1460 if (strcmp (mnt
.f_fstypename
, "zfs") != 0)
1463 *poolname
= xstrdup (mnt
.f_mntfromname
);
1465 #elif defined(HAVE_GETEXTMNTENT)
1469 struct extmnttab mnt
;
1471 if (stat (dir
, &st
) != 0)
1474 FILE *mnttab
= fopen ("/etc/mnttab", "r");
1478 while (getextmntent (mnttab
, &mnt
, sizeof (mnt
)) == 0)
1480 if (makedev (mnt
.mnt_major
, mnt
.mnt_minor
) == st
.st_dev
1481 && !strcmp (mnt
.mnt_fstype
, "zfs"))
1483 *poolname
= xstrdup (mnt
.mnt_special
);
1495 slash
= strchr (*poolname
, '/');
1499 *poolfs
= xstrdup (slash
+ 1);
1502 *poolfs
= xstrdup ("");
1505 /* This function never prints trailing slashes (so that its output
1506 can be appended a slash unconditionally). */
1508 grub_make_system_path_relative_to_its_root (const char *path
)
1511 char *p
, *buf
, *buf2
, *buf3
, *ret
;
1512 uintptr_t offset
= 0;
1515 char *poolfs
= NULL
;
1518 p
= canonicalize_file_name (path
);
1520 grub_util_error (_("failed to get canonical path of %s"), path
);
1522 /* For ZFS sub-pool filesystems, could be extended to others (btrfs?). */
1525 grub_find_zpool_from_dir (p
, &dummy
, &poolfs
);
1528 len
= strlen (p
) + 1;
1532 if (stat (buf
, &st
) < 0)
1533 grub_util_error (_("cannot stat %s: %s"), buf
, strerror (errno
));
1535 buf2
= xstrdup (buf
);
1538 /* This loop sets offset to the number of chars of the root
1539 directory we're inspecting. */
1542 p
= strrchr (buf
, '/');
1544 /* This should never happen. */
1545 grub_util_error (_("FIXME: no / in buf. (make_system_path_relative_to_its_root)"));
1551 if (stat (buf
, &st
) < 0)
1552 grub_util_error (_("cannot stat %s: %s"), buf
, strerror (errno
));
1554 /* buf is another filesystem; we found it. */
1555 if (st
.st_dev
!= num
)
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. */
1568 grub_free (grub_find_root_device_from_mountinfo (buf2
, &bind
));
1569 if (bind
&& bind
[0] && bind
[1])
1579 return xasprintf ("/%s/@", poolfs
);
1580 return xstrdup ("");
1587 /* offset == 1 means root directory. */
1590 /* Include leading slash. */
1596 buf3
= xstrdup (buf2
+ offset
);
1601 grub_free (grub_find_root_device_from_mountinfo (buf2
, &bind
));
1602 if (bind
&& bind
[0] && bind
[1])
1605 buf3
= grub_xasprintf ("%s%s%s", bind
, buf3
[0] == '/' ?"":"/", buf3
);
1615 if (st
.st_dev
!= (DEV_CYGDRIVE_MAJOR
<< 16))
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
);
1628 /* Remove trailing slashes, return empty string if root directory. */
1629 len
= strlen (buf3
);
1630 while (len
> 0 && buf3
[len
- 1] == '/')
1632 buf3
[len
- 1] = '\0';
1638 ret
= xasprintf ("/%s/@%s", poolfs
, buf3
);