/* getroot.c - Get root device */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc.
*
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#define FORCE_INCLUDE_CONFIG_UTIL_H
#include <config.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <error.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#include <grub/cryptodisk.h>
#ifdef HAVE_DEVICE_MAPPER
# include <libdevmapper.h>
#endif
#ifdef __GNU__
#include <hurd.h>
#endif
#ifdef __linux__
#endif
#ifdef __sun__
#endif
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#endif
#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
#endif
#ifdef __sun__
#endif
#if ! defined(__sun__)
static void
{
char *p = dir;
while ((p = strchr (p, '/')) != 0)
{
if (p[1] == '/')
{
continue;
}
else if (p[1] == '\0')
{
if (p > dir)
p[0] = '\0';
break;
}
p++;
}
}
static char *
xgetcwd (void)
{
char *path;
{
size <<= 1;
}
return path;
}
#endif
#ifdef __linux__
struct mountinfo_entry
{
int id;
};
/* Statting something on a btrfs filesystem always returns a virtual device
can span multiple underlying devices (and even if it's currently only
using a single device it can be dynamically extended onto another). We
can't deal with the multiple-device case yet, but in the meantime, we can
at least cope with the single-device case by scanning
static void
{
char *optr;
const char *iptr;
{
{
iptr += 4;
}
else
}
*optr = 0;
}
char *
{
int i;
if (! *dir)
dir = "/";
if (relroot)
if (! fp)
return NULL; /* fall through to other methods */
/* First, build a list of relevant visible mounts. */
{
int count;
const char *sep;
continue;
/* Check that enc_path is a prefix of dir. The prefix must either be
the entire string, or end with a slash, or be immediately followed
by a slash. */
continue;
if (!sep)
continue;
continue;
/* Using the mount IDs, find out where this fits in the list of
visible mount entries we've seen so far. There are three
interesting cases. Firstly, it may be inserted at the end: this is
may be inserted at the start: for example, this can happen for
filesystems that are mounted before / and later moved under it.
Thirdly, it may occlude part or all of the existing filesystem
tree, in which case the end of the list needs to be pruned and this
new entry will be inserted at the end. */
{
entry_max <<= 1;
}
if (!entry_len)
{
/* Initialise list. */
entry_len = 2;
entries[0] = parent_entry;
}
else
{
for (i = entry_len - 1; i >= 0; i--)
{
{
/* Insert at end, pruning anything previously above this. */
entry_len = i + 2;
break;
}
{
/* Insert at start. */
entry_len++;
entries[0] = parent_entry;
break;
}
}
}
}
/* Now scan visible mounts for the ones we're interested in. */
for (i = entry_len - 1; i >= 0; i--)
{
continue;
if (relroot)
break;
}
return ret;
}
static char *grub_find_root_device_from_solaris_mountinfo (const char *dir);
static char *
{
return NULL;
if (! mnttab)
return NULL;
{
continue;
break;
break;
break;
break;
24) == 0) {
/* To find the minor name, we need to skip the instance # */
minor_name++;
if (*minor_name == ':') {
minor_name++;
}
break;
}
}
}
return (device);
}
#endif /* __linux__ */
#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
static int
{
if (ival)
{
grub_util_info ("ZPOOL_CONFIG_OFFLINE");
return (1);
}
if (ival)
{
grub_util_info ("ZPOOL_CONFIG_FAULTED");
return (1);
}
if (ival)
{
grub_util_info ("ZPOOL_CONFIG_REMOVED");
return (1);
}
return (0);
}
static int
{
{
return (1);
}
{
if (vdev_validate (nv) != 0)
return (1);
{
return (1);
}
{
return (0);
} else {
return (1);
}
{
unsigned int nelm, i;
{
return (1);
}
for (i = 0; i < nelm; i++)
{
return (0);
}
}
return (1);
}
#endif
static char *
{
char *poolname;
char *poolfs;
if (! poolname)
return NULL;
#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
{
libzfs = grub_get_libzfs_handle ();
if (! libzfs)
return NULL;
/* get the root vdev */
grub_util_error ("nvlist_lookup_nvlist (\"vdev_tree\")");
zpool_close (zpool);
}
#else
{
char *cmd;
int ret;
char *line;
int st;
unsigned int dummy;
st = 0;
while (st < 3)
{
if (ret == -1)
goto fail;
switch (st)
{
case 0:
st++;
break;
case 1:
st++;
break;
case 2:
st++;
break;
}
}
fail:
}
#endif
if (poolfs)
#if defined(__sun__)
}
#endif
return device;
}
#ifdef __MINGW32__
char *
{
return 0;
}
char *
{
char *saved_cwd;
if (! dir)
{
#ifdef __CYGWIN__
return NULL;
#else
dir = "/dev";
#endif
}
if (! dp)
return 0;
{
return 0;
}
{
/* Avoid:
- dotfiles (like "/dev/.tmp.md0") since they could be duplicates.
- dotdirs (like "/dev/.static") since they could contain duplicates. */
continue;
/* Ignore any error. */
continue;
#ifdef __linux__
we can get them. */
continue;
} else
#endif /* __linux__ */
/* Don't follow other symbolic links. */
continue;
}
{
/* Find it recursively. */
char *res;
if (res)
{
grub_util_error ("cannot restore the original directory");
return res;
}
}
#else
#endif
{
#ifdef __linux__
continue;
#endif
/* Found! */
char *res;
char *cwd;
#if defined(__NetBSD__)
/* Convert this block device to its character (raw) device. */
#else
/* Keep the device name as it is. */
#endif
of situation where root filesystem is on the same partition as
grub files */
continue;
grub_util_error (_("cannot restore the original directory"));
return res;
}
}
grub_util_error (_("cannot restore the original directory"));
return 0;
}
#elif defined(__CYGWIN__)
return 0 on read error, ~0 on unknown serial. */
static unsigned
{
/* Read boot sector. */
if (fd < 0)
return 0;
if (n != sizeof(buf))
return 0;
/* Check signature. */
return ~0;
/* Serial number offset depends on boot sector type. */
if (mbr)
n = 0x1b8;
n = 0x048;
n = 0x043;
n = 0x027;
else
return ~0;
if (serial == 0)
return ~0;
return serial;
}
char *
{
/* No root device for /cygdrive. */
return 0;
/* Convert to full POSIX and Win32 path. */
/* If identical, this is no real filesystem path. */
return 0;
/* Check for floppy drive letter. */
/* Cygwin returns the partition serial number in stat.st_dev.
This is never identical to the device number of the emulated
Search the partition with the same serial in boot sector instead. */
int d;
for (d = 'a'; d <= 'z'; d++)
{
continue;
int p;
for (p = 1; p <= 15; p++)
{
if (ser == 0)
break;
}
}
return 0;
}
#endif /* __CYGWIN__ */
char *
{
#ifdef __GNU__
int *ints;
char *data;
if (file == MACH_PORT_NULL)
return 0;
&offsets, &num_offsets,
if (num_ints < 1)
if (ints[0] != STORAGE_DEVICE)
if (num_ints < 5)
{
for (i = 0; i < num_ports; i++)
{
if (port != MACH_PORT_NULL)
}
}
if (offsets && num_offsets > 0)
#else /* !__GNU__ */
#ifdef __linux__
if (!os_dev)
#endif /* __linux__ */
if (!os_dev)
{
#if defined(__sun__)
if (os_dev)
return os_dev;
#endif
}
#if !defined(__sun__)
if (os_dev)
{
}
if (os_dev)
{
return os_dev;
{
}
}
#else /* Solaris */
if (!os_dev)
if (!os_dev)
grub_util_error("Unable to determine device");
else
return os_dev;
#endif
#ifdef __CYGWIN__
/* Cygwin specific function. */
#else
#if !defined(__sun__)
/* This might be truly slow, but is there any better way? */
#else
grub_util_error("cannot determine root device");
#endif
#endif /* __CYGWIN__ */
#endif /* !__GNU__ */
return os_dev;
}
#ifdef HAVE_DEVICE_MAPPER
static int
struct dm_tree_node **node)
{
return 0;
return 0;
*tree = dm_tree_create ();
if (! *tree)
{
return 0;
}
{
dm_tree_free (*tree);
return 0;
}
if (! *node)
{
dm_tree_free (*tree);
return 0;
}
return 1;
}
#endif
static char *
{
return NULL;
#ifdef HAVE_DEVICE_MAPPER
{
const char *node_uuid;
char *ret;
return NULL;
if (! node_uuid)
{
dm_tree_free (tree);
return NULL;
}
dm_tree_free (tree);
return ret;
}
#endif
return NULL;
}
static enum grub_dev_abstraction_types
{
#ifdef HAVE_DEVICE_MAPPER
char *uuid;
return GRUB_DEV_ABSTRACTION_NONE;
{
return GRUB_DEV_ABSTRACTION_LVM;
}
{
return GRUB_DEV_ABSTRACTION_LUKS;
}
return GRUB_DEV_ABSTRACTION_NONE;
#else
return GRUB_DEV_ABSTRACTION_NONE;
return GRUB_DEV_ABSTRACTION_LVM;
#endif
}
#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <libgeom.h>
static const char *
{
char *whole;
const char *name;
int error;
return 0;
if (error != 0)
grub_util_error (_("couldn't open geom"));
{
{
}
}
return NULL;
}
#endif
int
{
/* User explicitly claims that this drive is visible by BIOS. */
return GRUB_DEV_ABSTRACTION_NONE;
#endif
#ifdef __linux__
/* Check for LVM and LUKS. */
if (ret != GRUB_DEV_ABSTRACTION_NONE)
return ret;
/* Check for RAID. */
return GRUB_DEV_ABSTRACTION_RAID;
#endif
#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
const char *abs;
return GRUB_DEV_ABSTRACTION_GELI;
/* Check for LVM. */
return GRUB_DEV_ABSTRACTION_LVM;
#endif
/* No abstraction found. */
return GRUB_DEV_ABSTRACTION_NONE;
}
#ifdef __linux__
static char *
{
if (pipe (mdadm_pipe) < 0)
{
grub_util_warn (_("Unable to create pipe for mdadm: %s"),
return NULL;
}
if (mdadm_pid < 0)
else if (mdadm_pid == 0)
{
/* Child. */
close (mdadm_pipe[0]);
/* execvp has inconvenient types, hence the casts. None of these
strings will actually be modified. */
argv[0] = (char *) "mdadm";
exit (127);
}
else
{
/* Parent. Read mdadm's output. */
if (! mdadm)
{
grub_util_warn (_("Unable to open stream from mdadm: %s"),
goto out;
}
{
{
ptri++)
*ptro = 0;
}
}
out:
close (mdadm_pipe[0]);
}
return name;
}
#endif /* __linux__ */
void
{
int ab;
switch (ab)
{
#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
{
char *whole;
const char *name;
int error;
return;
if (error != 0)
grub_util_error (_("couldn't open geom"));
{
{
{
char *fname;
char *uuid;
break;
if (!consumer)
grub_util_error (_("couldn't find geli consumer"));
}
}
}
{
if (grdev)
{
if (err)
}
}
}
#endif
break;
case GRUB_DEV_ABSTRACTION_LVM:
#ifdef HAVE_DEVICE_MAPPER
{
return;
{
char *subdev;
if (!dm)
continue;
if (subdev)
{
lastsubdev = subdev;
}
}
{
dm_tree_free (tree);
if (grdev)
{
if (err)
}
}
else
dm_tree_free (tree);
}
#endif
return;
#ifdef __linux__
{
int i;
for (i = 0; devicelist[i];i++)
free (devicelist);
}
#endif
return;
default: /* GRUB_DEV_ABSTRACTION_NONE */
return;
}
}
char *
{
switch (grub_util_get_dev_abstraction (os_dev))
{
case GRUB_DEV_ABSTRACTION_LVM:
{
unsigned short i, len;
}
break;
#endif
#ifdef __linux__
{
if (!uuid)
break;
if (dash)
*dash = 0;
}
break;
#endif
#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
{
char *whole;
const char *name;
int error;
return 0;
if (error != 0)
grub_util_error (_("couldn't open geom"));
{
{
{
char *fname;
char *uuid;
break;
if (!consumer)
grub_util_error (_("couldn't find geli consumer"));
if (!uuid)
grub_util_error (_("couldn't retrieve geli UUID"));
}
}
}
}
break;
#endif
#ifdef __linux__
{
char *p, *q;
q = strchr (p, 'p');
if (q)
*q = ',';
free (p);
}
{
char *p, *q;
q = strchr (p, 'p');
if (q)
*q = ',';
free (p);
}
{
char *p , *q;
q = strchr (p, 'p');
if (q)
*q = ',';
free (p);
}
{
char *p , *q;
q = strchr (p, 'p');
if (q)
*q = ',';
free (p);
}
{
/* mdraid 1.x with a free name. */
char *p , *q;
q = strchr (p, 'p');
if (q)
*q = ',';
free (p);
}
else
{
if (mdadm_name)
{
const char *q;
&& grub_isdigit (*q); q--);
if (q >= os_dev && *q == 'p')
{
goto done;
}
done:
free (mdadm_name);
}
}
break;
#endif /* __linux__ */
default: /* GRUB_DEV_ABSTRACTION_NONE */
}
return grub_dev;
}
const char *
{
return (blk_dev);
else
return 0;
}
const char *
{
return (blk_dev);
else
return 0;
}
#ifdef __CYGWIN__
/* Convert POSIX path to Win32 path,
remove drive letter, replace backslashes. */
static char *
{
grub_util_error (_("cygwin_conv_path() failed"));
int i;
if (winpath[i] == '\\')
winpath[i] = '/';
}
#endif
#ifdef HAVE_LIBZFS
static void
fini_libzfs (void)
{
}
grub_get_libzfs_handle (void)
{
if (! __libzfs_handle)
{
__libzfs_handle = libzfs_init ();
if (__libzfs_handle)
}
return __libzfs_handle;
}
#endif /* HAVE_LIBZFS */
/* ZFS has similar problems to those of btrfs (see above). */
void
{
char *slash;
#if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) && defined(HAVE_STRUCT_STATFS_F_MNTFROMNAME)
{
return;
return;
}
#elif defined(HAVE_GETEXTMNTENT)
/* Solaris. */
{
return;
if (! mnttab)
return;
{
{
break;
}
}
}
#endif
if (! *poolname)
return;
if (slash)
{
*slash = '\0';
}
else
}
/* This function never prints trailing slashes (so that its output
can be appended a slash unconditionally). */
char *
{
/* canonicalize. */
p = canonicalize_file_name (path);
if (p == NULL)
/* For ZFS sub-pool filesystems, could be extended to others (btrfs?). */
{
char *dummy;
}
free (p);
/* This loop sets offset to the number of chars of the root
directory we're inspecting. */
while (1)
{
if (p == NULL)
/* This should never happen. */
grub_util_error (_("FIXME: no / in buf. (make_system_path_relative_to_its_root)"));
if (p != buf)
*p = 0;
else
*++p = 0;
/* buf is another filesystem; we found it. */
{
/* offset == 0 means path given is the mount point.
This works around special-casing of "/" in Un*x. This function never
prints trailing slashes (so that its output can be appended a slash
unconditionally). Each slash in is considered a preceding slash, and
therefore the root directory is an empty string. */
if (offset == 0)
{
#ifdef __linux__
{
char *bind;
{
goto parsedir;
}
}
#endif
if (poolfs)
return xstrdup ("");
}
else
break;
}
/* offset == 1 means root directory. */
if (offset == 1)
{
/* Include leading slash. */
offset = 0;
break;
}
}
#ifdef __linux__
{
char *bind;
{
}
}
#endif
#ifdef __CYGWIN__
{
/* Reached some mount point not below /cygdrive.
GRUB does not know Cygwin's emulated mounts,
convert to Win32 path. */
}
#endif
/* Remove trailing slashes, return empty string if root directory. */
{
len--;
}
if (poolfs)
{
}
else
return ret;
}