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>
40 #include <grub/cryptodisk.h>
41 #include <grub/i18n.h>
44 #include <sys/ioctl.h> /* ioctl */
45 #include <sys/mount.h>
48 #include <sys/types.h>
50 #if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
51 # include <grub/util/libzfs.h>
52 # include <grub/util/libnvpair.h>
56 #include <grub/misc.h>
57 #include <grub/emu/misc.h>
58 #include <grub/emu/hostdisk.h>
59 #include <grub/emu/getroot.h>
61 #if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
62 #include <sys/mount.h>
65 #if defined(__NetBSD__) || defined(__OpenBSD__)
66 # include <sys/ioctl.h>
67 # include <sys/disklabel.h> /* struct disklabel */
68 # include <sys/disk.h> /* struct dkwedge_info */
69 #include <sys/param.h>
70 #include <sys/mount.h>
71 #endif /* defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
73 #if defined(__NetBSD__)
74 # include <sys/fdio.h>
78 grub_util_find_partition_start (const char *dev
)
80 #if GRUB_UTIL_FD_STAT_IS_FUNCTIONAL
82 grub_disk_addr_t partition_start
;
84 if (stat (dev
, &st
) >= 0
85 && grub_util_device_is_mapped_stat (&st
)
86 && grub_util_get_dm_node_linear_info (st
.st_rdev
, 0, 0, &partition_start
))
87 return partition_start
;
90 return grub_util_find_partition_start_os (dev
);
94 grub_util_pull_device (const char *os_dev
)
96 enum grub_dev_abstraction_types ab
;
97 ab
= grub_util_get_dev_abstraction (os_dev
);
100 case GRUB_DEV_ABSTRACTION_LVM
:
101 grub_util_pull_lvm_by_command (os_dev
);
102 /* Fallthrough in case that lvm-tools are unavailable. */
103 case GRUB_DEV_ABSTRACTION_LUKS
:
104 grub_util_pull_devmapper (os_dev
);
108 if (grub_util_pull_device_os (os_dev
, ab
))
111 case GRUB_DEV_ABSTRACTION_NONE
:
112 free (grub_util_biosdisk_get_grub_dev (os_dev
));
118 grub_util_get_grub_dev (const char *os_dev
)
122 grub_util_pull_device (os_dev
);
124 ret
= grub_util_get_devmapper_grub_dev (os_dev
);
127 ret
= grub_util_get_grub_dev_os (os_dev
);
130 return grub_util_biosdisk_get_grub_dev (os_dev
);
134 grub_util_get_dev_abstraction (const char *os_dev
)
136 enum grub_dev_abstraction_types ret
;
138 /* User explicitly claims that this drive is visible by BIOS. */
139 if (grub_util_biosdisk_is_present (os_dev
))
140 return GRUB_DEV_ABSTRACTION_NONE
;
142 /* Check for LVM and LUKS. */
143 ret
= grub_util_get_dm_abstraction (os_dev
);
145 if (ret
!= GRUB_DEV_ABSTRACTION_NONE
)
148 return grub_util_get_dev_abstraction_os (os_dev
);
152 convert_system_partition_to_system_disk (const char *os_dev
, int *is_part
)
154 #if GRUB_UTIL_FD_STAT_IS_FUNCTIONAL
157 if (stat (os_dev
, &st
) < 0)
159 const char *errstr
= strerror (errno
);
160 grub_error (GRUB_ERR_BAD_DEVICE
, N_("cannot stat `%s': %s"),
162 grub_util_info (_("cannot stat `%s': %s"), os_dev
, errstr
);
168 if (grub_util_device_is_mapped_stat (&st
))
169 return grub_util_devmapper_part_to_disk (&st
, is_part
, os_dev
);
173 return grub_util_part_to_disk (os_dev
, &st
, is_part
);
177 return grub_util_part_to_disk (os_dev
, NULL
, is_part
);
182 find_system_device (const char *os_dev
)
188 os_disk
= convert_system_partition_to_system_disk (os_dev
, &is_part
);
192 drive
= grub_hostdisk_os_dev_to_grub_drive (os_disk
, 0);
198 make_device_name (const char *drive
)
203 ret
= xmalloc (strlen (drive
) * 2);
205 for (iptr
= drive
; *iptr
; iptr
++)
207 if (*iptr
== ',' || *iptr
== '\\')
217 grub_util_get_os_disk (const char *os_dev
)
221 grub_util_info ("Looking for %s", os_dev
);
223 return convert_system_partition_to_system_disk (os_dev
, &is_part
);
226 #if !defined(__APPLE__)
227 /* Context for grub_util_biosdisk_get_grub_dev. */
228 struct grub_util_biosdisk_get_grub_dev_ctx
231 grub_disk_addr_t start
;
234 /* Helper for grub_util_biosdisk_get_grub_dev. */
236 find_partition (grub_disk_t dsk
__attribute__ ((unused
)),
237 const grub_partition_t partition
, void *data
)
239 struct grub_util_biosdisk_get_grub_dev_ctx
*ctx
= data
;
240 grub_disk_addr_t part_start
= 0;
241 grub_util_info ("Partition %d starts from %" GRUB_HOST_PRIuLONG_LONG
,
242 partition
->number
, (unsigned long long) partition
->start
);
244 part_start
= grub_partition_get_start (partition
);
246 if (ctx
->start
== part_start
)
248 ctx
->partname
= grub_partition_get_name (partition
);
257 grub_util_biosdisk_get_grub_dev (const char *os_dev
)
263 grub_util_info ("Looking for %s", os_dev
);
265 sys_disk
= convert_system_partition_to_system_disk (os_dev
, &is_part
);
270 drive
= grub_hostdisk_os_dev_to_grub_drive (sys_disk
, 1);
272 grub_util_info ("%s is a parent of %s", sys_disk
, os_dev
);
276 return make_device_name (drive
);
280 #if defined(__APPLE__)
281 /* Apple uses "/dev/r?disk[0-9]+(s[0-9]+)?". */
283 * Note: we do not use the new partition naming scheme as dos_part does not
284 * necessarily correspond to an msdos partition.
290 int disk
= (grub_memcmp (os_dev
, "/dev/disk", sizeof ("/dev/disk") - 1)
292 int rdisk
= (grub_memcmp (os_dev
, "/dev/rdisk", sizeof ("/dev/rdisk") - 1)
295 dri
= make_device_name (drive
);
300 p
= os_dev
+ sizeof ("/dev/disk") + rdisk
- 1;
301 while (*p
>= '0' && *p
<= '9')
307 part
= strtol (p
, NULL
, 10);
311 ret
= xasprintf ("%s,%d", dri
, part
);
319 /* Linux counts partitions uniformly, whether a BSD partition or a DOS
320 partition, so mapping them to GRUB devices is not trivial.
321 Here, get the start sector of a partition by HDIO_GETGEO, and
322 compare it with each partition GRUB recognizes.
324 Cygwin /dev/sdXN emulation uses Windows partition mapping. It
325 does not count the extended partition and missing primary
326 partitions. Use same method as on Linux here.
328 For NetBSD and FreeBSD, proceed as for Linux, except that the start
329 sector is obtained from the disk label. */
333 struct grub_util_biosdisk_get_grub_dev_ctx ctx
;
335 name
= make_device_name (drive
);
337 ctx
.start
= grub_util_find_partition_start (os_dev
);
338 if (grub_errno
!= GRUB_ERR_NONE
)
345 /* Some versions of Hurd use badly glued Linux code to handle partitions
346 resulting in partitions being promoted to disks. */
347 /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
349 * Note: we do not use the new partition naming scheme as dos_part does not
350 * necessarily correspond to an msdos partition.
352 if (ctx
.start
== (grub_disk_addr_t
) -1)
357 dri
= make_device_name (drive
);
359 p
= strrchr (os_dev
+ sizeof ("/dev/hd") - 1, 's');
366 n
= strtol (p
, &q
, 10);
367 if (p
!= q
&& n
> 0 && n
!= GRUB_LONG_MAX
)
371 if (*q
>= 'a' && *q
<= 'g')
372 dri
= xasprintf ("%s,%ld,%d", t
, n
, *q
- 'a' + 1);
374 dri
= xasprintf ("%s,%ld", t
, n
);
383 grub_util_info ("%s starts from %" GRUB_HOST_PRIuLONG_LONG
,
384 os_dev
, (unsigned long long) ctx
.start
);
386 if (ctx
.start
== 0 && !is_part
)
389 grub_util_info ("opening the device %s", name
);
390 disk
= grub_disk_open (name
);
395 /* We already know that the partition exists. Given that we already
396 checked the device map above, we can only get
397 GRUB_ERR_UNKNOWN_DEVICE at this point if the disk does not exist.
398 This can happen on Xen, where disk images in the host can be
399 assigned to devices that have partition-like names in the guest
400 but are really more like disks. */
401 if (grub_errno
== GRUB_ERR_UNKNOWN_DEVICE
)
405 (_("disk does not exist, so falling back to partition device %s"),
407 grub_errno
= GRUB_ERR_NONE
;
409 canon
= canonicalize_file_name (os_dev
);
410 drive
= grub_hostdisk_os_dev_to_grub_drive (canon
? : os_dev
, 1);
413 return make_device_name (drive
);
419 name
= grub_util_get_ldm (disk
, ctx
.start
);
422 grub_disk_close (disk
);
428 grub_partition_iterate (disk
, find_partition
, &ctx
);
429 if (grub_errno
!= GRUB_ERR_NONE
)
431 grub_disk_close (disk
);
435 if (ctx
.partname
== NULL
)
437 grub_disk_close (disk
);
438 grub_util_info ("cannot find the partition of `%s'", os_dev
);
439 grub_error (GRUB_ERR_BAD_DEVICE
,
440 "cannot find the partition of `%s'", os_dev
);
444 name
= grub_xasprintf ("%s,%s", disk
->name
, ctx
.partname
);
446 grub_disk_close (disk
);
454 grub_util_biosdisk_is_present (const char *os_dev
)
456 int ret
= (find_system_device (os_dev
) != NULL
);
457 grub_util_info ((ret
? "%s is present" : "%s is not present"),
463 static libzfs_handle_t
*__libzfs_handle
;
468 libzfs_fini (__libzfs_handle
);
472 grub_get_libzfs_handle (void)
474 if (! __libzfs_handle
)
476 __libzfs_handle
= libzfs_init ();
479 atexit (fini_libzfs
);
482 return __libzfs_handle
;
484 #endif /* HAVE_LIBZFS */