aboutsummaryrefslogtreecommitdiffstats
path: root/grub-core/kern/emu
diff options
context:
space:
mode:
Diffstat (limited to 'grub-core/kern/emu')
-rw-r--r--grub-core/kern/emu/cache.S28
-rw-r--r--grub-core/kern/emu/console.c293
-rw-r--r--grub-core/kern/emu/full.c71
-rw-r--r--grub-core/kern/emu/getroot.c932
-rw-r--r--grub-core/kern/emu/hostdisk.c1776
-rw-r--r--grub-core/kern/emu/hostfs.c175
-rw-r--r--grub-core/kern/emu/lite.c45
-rw-r--r--grub-core/kern/emu/main.c259
-rw-r--r--grub-core/kern/emu/misc.c537
-rw-r--r--grub-core/kern/emu/mm.c87
-rw-r--r--grub-core/kern/emu/time.c46
11 files changed, 4249 insertions, 0 deletions
diff --git a/grub-core/kern/emu/cache.S b/grub-core/kern/emu/cache.S
new file mode 100644
index 0000000..abd81c9
--- /dev/null
+++ b/grub-core/kern/emu/cache.S
@@ -0,0 +1,28 @@
+#ifndef GRUB_MACHINE_EMU
+#error "This source is only meant for grub-emu platform"
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+/* Nothing is necessary. */
+#elif defined(__sparc__)
+#include "../sparc64/cache.S"
+#elif defined(__mips__)
+/* On MIPS we must go through standard functions. */
+#include <grub/symbol.h>
+
+FUNCTION (grub_cpu_flush_cache)
+FUNCTION (grub_arch_sync_caches)
+ .set nomacro
+ .set noreorder
+ lui $t0, %hi(_flush_cache)
+ addui $t0, $t0, %lo(_flush_cache)
+ move $a3, $zero
+ jr $t0
+ nop
+ .set reorder
+ .set macro
+#elif defined(__powerpc__)
+#include "../powerpc/cache.S"
+#else
+#error "No target cpu type is defined"
+#endif
diff --git a/grub-core/kern/emu/console.c b/grub-core/kern/emu/console.c
new file mode 100644
index 0000000..7fd1fc0
--- /dev/null
+++ b/grub-core/kern/emu/console.c
@@ -0,0 +1,293 @@
+/* console.c -- Ncurses console for GRUB. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003,2005,2007,2008 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * 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/>.
+ */
+
+#include <config.h>
+#include <config-util.h>
+
+/* For compatibility. */
+#ifndef A_NORMAL
+# define A_NORMAL 0
+#endif /* ! A_NORMAL */
+#ifndef A_STANDOUT
+# define A_STANDOUT 0
+#endif /* ! A_STANDOUT */
+
+#include <grub/emu/console.h>
+#include <grub/term.h>
+#include <grub/types.h>
+
+#if defined(HAVE_NCURSES_CURSES_H)
+# include <ncurses/curses.h>
+#elif defined(HAVE_NCURSES_H)
+# include <ncurses.h>
+#elif defined(HAVE_CURSES_H)
+# include <curses.h>
+#else
+#error What the hell?
+#endif
+
+static int grub_console_attr = A_NORMAL;
+
+grub_uint8_t grub_console_cur_color = 7;
+
+static const grub_uint8_t grub_console_standard_color = 0x7;
+
+#define NUM_COLORS 8
+
+static grub_uint8_t color_map[NUM_COLORS] =
+{
+ COLOR_BLACK,
+ COLOR_BLUE,
+ COLOR_GREEN,
+ COLOR_CYAN,
+ COLOR_RED,
+ COLOR_MAGENTA,
+ COLOR_YELLOW,
+ COLOR_WHITE
+};
+
+static int use_color;
+
+static void
+grub_ncurses_putchar (struct grub_term_output *term __attribute__ ((unused)),
+ const struct grub_unicode_glyph *c)
+{
+ addch (c->base | grub_console_attr);
+}
+
+static void
+grub_ncurses_setcolorstate (struct grub_term_output *term,
+ grub_term_color_state state)
+{
+ switch (state)
+ {
+ case GRUB_TERM_COLOR_STANDARD:
+ grub_console_cur_color = grub_console_standard_color;
+ grub_console_attr = A_NORMAL;
+ break;
+ case GRUB_TERM_COLOR_NORMAL:
+ grub_console_cur_color = term->normal_color;
+ grub_console_attr = A_NORMAL;
+ break;
+ case GRUB_TERM_COLOR_HIGHLIGHT:
+ grub_console_cur_color = term->highlight_color;
+ grub_console_attr = A_STANDOUT;
+ break;
+ default:
+ break;
+ }
+
+ if (use_color)
+ {
+ grub_uint8_t fg, bg;
+
+ fg = (grub_console_cur_color & 7);
+ bg = (grub_console_cur_color >> 4) & 7;
+
+ grub_console_attr = (grub_console_cur_color & 8) ? A_BOLD : A_NORMAL;
+ color_set ((bg << 3) + fg, 0);
+ }
+}
+
+static int
+grub_ncurses_getkey (struct grub_term_input *term __attribute__ ((unused)))
+{
+ int c;
+
+ wtimeout (stdscr, 100);
+ c = getch ();
+
+ switch (c)
+ {
+ case ERR:
+ return -1;
+ case KEY_LEFT:
+ c = GRUB_TERM_KEY_LEFT;
+ break;
+
+ case KEY_RIGHT:
+ c = GRUB_TERM_KEY_RIGHT;
+ break;
+
+ case KEY_UP:
+ c = GRUB_TERM_KEY_UP;
+ break;
+
+ case KEY_DOWN:
+ c = GRUB_TERM_KEY_DOWN;
+ break;
+
+ case KEY_IC:
+ c = 24;
+ break;
+
+ case KEY_DC:
+ c = GRUB_TERM_KEY_DC;
+ break;
+
+ case KEY_BACKSPACE:
+ /* XXX: For some reason ncurses on xterm does not return
+ KEY_BACKSPACE. */
+ case 127:
+ c = '\b';
+ break;
+
+ case KEY_HOME:
+ c = GRUB_TERM_KEY_HOME;
+ break;
+
+ case KEY_END:
+ c = GRUB_TERM_KEY_END;
+ break;
+
+ case KEY_NPAGE:
+ c = GRUB_TERM_KEY_NPAGE;
+ break;
+
+ case KEY_PPAGE:
+ c = GRUB_TERM_KEY_PPAGE;
+ break;
+ }
+
+ return c;
+}
+
+static grub_uint16_t
+grub_ncurses_getxy (struct grub_term_output *term __attribute__ ((unused)))
+{
+ int x;
+ int y;
+
+ getyx (stdscr, y, x);
+
+ return (x << 8) | y;
+}
+
+static grub_uint16_t
+grub_ncurses_getwh (struct grub_term_output *term __attribute__ ((unused)))
+{
+ int x;
+ int y;
+
+ getmaxyx (stdscr, y, x);
+
+ return (x << 8) | y;
+}
+
+static void
+grub_ncurses_gotoxy (struct grub_term_output *term __attribute__ ((unused)),
+ grub_uint8_t x, grub_uint8_t y)
+{
+ move (y, x);
+}
+
+static void
+grub_ncurses_cls (struct grub_term_output *term __attribute__ ((unused)))
+{
+ clear ();
+ refresh ();
+}
+
+static void
+grub_ncurses_setcursor (struct grub_term_output *term __attribute__ ((unused)),
+ int on)
+{
+ curs_set (on ? 1 : 0);
+}
+
+static void
+grub_ncurses_refresh (struct grub_term_output *term __attribute__ ((unused)))
+{
+ refresh ();
+}
+
+static grub_err_t
+grub_ncurses_init (struct grub_term_output *term __attribute__ ((unused)))
+{
+ initscr ();
+ raw ();
+ noecho ();
+ scrollok (stdscr, TRUE);
+
+ nonl ();
+ intrflush (stdscr, FALSE);
+ keypad (stdscr, TRUE);
+
+ if (has_colors ())
+ {
+ start_color ();
+
+ if ((COLORS >= NUM_COLORS) && (COLOR_PAIRS >= NUM_COLORS * NUM_COLORS))
+ {
+ int i, j, n;
+
+ n = 0;
+ for (i = 0; i < NUM_COLORS; i++)
+ for (j = 0; j < NUM_COLORS; j++)
+ init_pair(n++, color_map[j], color_map[i]);
+
+ use_color = 1;
+ }
+ }
+
+ return 0;
+}
+
+static grub_err_t
+grub_ncurses_fini (struct grub_term_output *term __attribute__ ((unused)))
+{
+ endwin ();
+ return 0;
+}
+
+
+static struct grub_term_input grub_ncurses_term_input =
+ {
+ .name = "console",
+ .getkey = grub_ncurses_getkey,
+ };
+
+static struct grub_term_output grub_ncurses_term_output =
+ {
+ .name = "console",
+ .init = grub_ncurses_init,
+ .fini = grub_ncurses_fini,
+ .putchar = grub_ncurses_putchar,
+ .getxy = grub_ncurses_getxy,
+ .getwh = grub_ncurses_getwh,
+ .gotoxy = grub_ncurses_gotoxy,
+ .cls = grub_ncurses_cls,
+ .setcolorstate = grub_ncurses_setcolorstate,
+ .setcursor = grub_ncurses_setcursor,
+ .refresh = grub_ncurses_refresh,
+ .flags = GRUB_TERM_CODE_TYPE_ASCII
+ };
+
+void
+grub_console_init (void)
+{
+ grub_term_register_output ("console", &grub_ncurses_term_output);
+ grub_term_register_input ("console", &grub_ncurses_term_input);
+}
+
+void
+grub_console_fini (void)
+{
+ grub_ncurses_fini (&grub_ncurses_term_output);
+}
diff --git a/grub-core/kern/emu/full.c b/grub-core/kern/emu/full.c
new file mode 100644
index 0000000..70bcae7
--- /dev/null
+++ b/grub-core/kern/emu/full.c
@@ -0,0 +1,71 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * 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/>.
+ */
+
+#include <config.h>
+#include <grub/dl.h>
+#include <grub/env.h>
+#include <grub/kernel.h>
+#include <grub/misc.h>
+#include <grub/emu/misc.h>
+#include <grub/disk.h>
+
+void
+grub_register_exported_symbols (void)
+{
+}
+
+grub_err_t
+grub_arch_dl_check_header (void *ehdr)
+{
+ (void) ehdr;
+ return GRUB_ERR_BAD_MODULE;
+}
+
+grub_err_t
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
+{
+ (void) mod;
+ (void) ehdr;
+ return GRUB_ERR_BAD_MODULE;
+}
+
+void
+grub_emu_init (void)
+{
+ grub_no_autoload = 1;
+}
+
+#ifdef GRUB_LINKER_HAVE_INIT
+void
+grub_arch_dl_init_linker (void)
+{
+}
+#endif
+
+void
+grub_emu_post_init (void)
+{
+ grub_lvm_fini ();
+ grub_mdraid09_fini ();
+ grub_mdraid1x_fini ();
+ grub_raid_fini ();
+ grub_raid_init ();
+ grub_mdraid09_init ();
+ grub_mdraid1x_init ();
+ grub_lvm_init ();
+}
diff --git a/grub-core/kern/emu/getroot.c b/grub-core/kern/emu/getroot.c
new file mode 100644
index 0000000..a274f3c
--- /dev/null
+++ b/grub-core/kern/emu/getroot.c
@@ -0,0 +1,932 @@
+/* getroot.c - Get root device */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * 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/>.
+ */
+
+#include <config-util.h>
+#include <config.h>
+
+#include <sys/stat.h>
+#include <sys/types.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>
+#include <grub/util/misc.h>
+
+#ifdef __GNU__
+#include <hurd.h>
+#include <hurd/lookup.h>
+#include <hurd/fs.h>
+#include <sys/mman.h>
+#endif
+
+#ifdef __linux__
+# include <sys/types.h>
+# include <sys/wait.h>
+#endif
+
+#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
+# include <grub/util/libzfs.h>
+# include <grub/util/libnvpair.h>
+#endif
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/emu/misc.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/getroot.h>
+
+static void
+strip_extra_slashes (char *dir)
+{
+ char *p = dir;
+
+ while ((p = strchr (p, '/')) != 0)
+ {
+ if (p[1] == '/')
+ {
+ memmove (p, p + 1, strlen (p));
+ continue;
+ }
+ else if (p[1] == '\0')
+ {
+ if (p > dir)
+ p[0] = '\0';
+ break;
+ }
+
+ p++;
+ }
+}
+
+static char *
+xgetcwd (void)
+{
+ size_t size = 10;
+ char *path;
+
+ path = xmalloc (size);
+ while (! getcwd (path, size))
+ {
+ size <<= 1;
+ path = xrealloc (path, size);
+ }
+
+ return path;
+}
+
+#ifdef __linux__
+
+struct mountinfo_entry
+{
+ int id;
+ int major, minor;
+ char enc_root[PATH_MAX], enc_path[PATH_MAX];
+ char fstype[PATH_MAX], device[PATH_MAX];
+};
+
+/* Statting something on a btrfs filesystem always returns a virtual device
+ major/minor pair rather than the real underlying device, because btrfs
+ 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
+ /proc/self/mountinfo. */
+char *
+grub_find_root_device_from_mountinfo (const char *dir, char **relroot)
+{
+ FILE *fp;
+ char *buf = NULL;
+ size_t len = 0;
+ char *ret = NULL;
+ int entry_len = 0, entry_max = 4;
+ struct mountinfo_entry *entries;
+ struct mountinfo_entry parent_entry = { 0, 0, 0, "", "", "", "" };
+ int i;
+
+ if (! *dir)
+ dir = "/";
+ if (relroot)
+ *relroot = NULL;
+
+ fp = fopen ("/proc/self/mountinfo", "r");
+ if (! fp)
+ return NULL; /* fall through to other methods */
+
+ entries = xmalloc (entry_max * sizeof (*entries));
+
+ /* First, build a list of relevant visible mounts. */
+ while (getline (&buf, &len, fp) > 0)
+ {
+ struct mountinfo_entry entry;
+ int count;
+ size_t enc_path_len;
+ const char *sep;
+
+ if (sscanf (buf, "%d %d %u:%u %s %s%n",
+ &entry.id, &parent_entry.id, &entry.major, &entry.minor,
+ entry.enc_root, entry.enc_path, &count) < 6)
+ continue;
+
+ enc_path_len = strlen (entry.enc_path);
+ /* 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. */
+ if (strncmp (dir, entry.enc_path, enc_path_len) != 0 ||
+ (enc_path_len && dir[enc_path_len - 1] != '/' &&
+ dir[enc_path_len] && dir[enc_path_len] != '/'))
+ continue;
+
+ sep = strstr (buf + count, " - ");
+ if (!sep)
+ continue;
+
+ sep += sizeof (" - ") - 1;
+ if (sscanf (sep, "%s %s", entry.fstype, entry.device) != 2)
+ 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
+ the usual case of /foo/bar being mounted after /foo. Secondly, it
+ 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. */
+ if (entry_len >= entry_max)
+ {
+ entry_max <<= 1;
+ entries = xrealloc (entries, entry_max * sizeof (*entries));
+ }
+
+ if (!entry_len)
+ {
+ /* Initialise list. */
+ entry_len = 2;
+ entries[0] = parent_entry;
+ entries[1] = entry;
+ }
+ else
+ {
+ for (i = entry_len - 1; i >= 0; i--)
+ {
+ if (entries[i].id == parent_entry.id)
+ {
+ /* Insert at end, pruning anything previously above this. */
+ entry_len = i + 2;
+ entries[i + 1] = entry;
+ break;
+ }
+ else if (i == 0 && entries[i].id == entry.id)
+ {
+ /* Insert at start. */
+ entry_len++;
+ memmove (entries + 1, entries,
+ (entry_len - 1) * sizeof (*entries));
+ entries[0] = parent_entry;
+ entries[1] = entry;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Now scan visible mounts for the ones we're interested in. */
+ for (i = entry_len - 1; i >= 0; i--)
+ {
+ if (!*entries[i].device)
+ continue;
+
+ ret = strdup (entries[i].device);
+ if (relroot)
+ *relroot = strdup (entries[i].enc_root);
+ break;
+ }
+
+ free (buf);
+ free (entries);
+ fclose (fp);
+ return ret;
+}
+
+#endif /* __linux__ */
+
+#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
+static char *
+find_root_device_from_libzfs (const char *dir)
+{
+ char *device = NULL;
+ char *poolname;
+ char *poolfs;
+
+ grub_find_zpool_from_dir (dir, &poolname, &poolfs);
+ if (! poolname)
+ return NULL;
+
+ {
+ zpool_handle_t *zpool;
+ libzfs_handle_t *libzfs;
+ nvlist_t *config, *vdev_tree;
+ nvlist_t **children, **path;
+ unsigned int nvlist_count;
+ unsigned int i;
+
+ libzfs = grub_get_libzfs_handle ();
+ if (! libzfs)
+ return NULL;
+
+ zpool = zpool_open (libzfs, poolname);
+ config = zpool_get_config (zpool, NULL);
+
+ if (nvlist_lookup_nvlist (config, "vdev_tree", &vdev_tree) != 0)
+ error (1, errno, "nvlist_lookup_nvlist (\"vdev_tree\")");
+
+ if (nvlist_lookup_nvlist_array (vdev_tree, "children", &children, &nvlist_count) != 0)
+ error (1, errno, "nvlist_lookup_nvlist_array (\"children\")");
+ assert (nvlist_count > 0);
+
+ while (nvlist_lookup_nvlist_array (children[0], "children",
+ &children, &nvlist_count) == 0)
+ assert (nvlist_count > 0);
+
+ for (i = 0; i < nvlist_count; i++)
+ {
+ if (nvlist_lookup_string (children[i], "path", &device) != 0)
+ error (1, errno, "nvlist_lookup_string (\"path\")");
+
+ struct stat st;
+ if (stat (device, &st) == 0)
+ {
+ device = xstrdup (device);
+ break;
+ }
+
+ device = NULL;
+ }
+
+ zpool_close (zpool);
+ }
+
+ free (poolname);
+ if (poolfs)
+ free (poolfs);
+
+ return device;
+}
+#endif
+
+#ifdef __MINGW32__
+
+char *
+grub_find_device (const char *dir __attribute__ ((unused)),
+ dev_t dev __attribute__ ((unused)))
+{
+ return 0;
+}
+
+#elif ! defined(__CYGWIN__)
+
+char *
+grub_find_device (const char *dir, dev_t dev)
+{
+ DIR *dp;
+ char *saved_cwd;
+ struct dirent *ent;
+
+ if (! dir)
+ {
+#ifdef __CYGWIN__
+ return NULL;
+#else
+ dir = "/dev";
+#endif
+ }
+
+ dp = opendir (dir);
+ if (! dp)
+ return 0;
+
+ saved_cwd = xgetcwd ();
+
+ grub_util_info ("changing current directory to %s", dir);
+ if (chdir (dir) < 0)
+ {
+ free (saved_cwd);
+ closedir (dp);
+ return 0;
+ }
+
+ while ((ent = readdir (dp)) != 0)
+ {
+ struct stat st;
+
+ /* Avoid:
+ - dotfiles (like "/dev/.tmp.md0") since they could be duplicates.
+ - dotdirs (like "/dev/.static") since they could contain duplicates. */
+ if (ent->d_name[0] == '.')
+ continue;
+
+ if (lstat (ent->d_name, &st) < 0)
+ /* Ignore any error. */
+ continue;
+
+ if (S_ISLNK (st.st_mode)) {
+#ifdef __linux__
+ if (strcmp (dir, "mapper") == 0) {
+ /* Follow symbolic links under /dev/mapper/; the canonical name
+ may be something like /dev/dm-0, but the names under
+ /dev/mapper/ are more human-readable and so we prefer them if
+ we can get them. */
+ if (stat (ent->d_name, &st) < 0)
+ continue;
+ } else
+#endif /* __linux__ */
+ /* Don't follow other symbolic links. */
+ continue;
+ }
+
+ if (S_ISDIR (st.st_mode))
+ {
+ /* Find it recursively. */
+ char *res;
+
+ res = grub_find_device (ent->d_name, dev);
+
+ if (res)
+ {
+ if (chdir (saved_cwd) < 0)
+ grub_util_error ("cannot restore the original directory");
+
+ free (saved_cwd);
+ closedir (dp);
+ return res;
+ }
+ }
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
+ if (S_ISCHR (st.st_mode) && st.st_rdev == dev)
+#else
+ if (S_ISBLK (st.st_mode) && st.st_rdev == dev)
+#endif
+ {
+#ifdef __linux__
+ /* Skip device names like /dev/dm-0, which are short-hand aliases
+ to more descriptive device names, e.g. those under /dev/mapper */
+ if (ent->d_name[0] == 'd' &&
+ ent->d_name[1] == 'm' &&
+ ent->d_name[2] == '-' &&
+ ent->d_name[3] >= '0' &&
+ ent->d_name[3] <= '9')
+ continue;
+#endif
+
+ /* Found! */
+ char *res;
+ char *cwd;
+#if defined(__NetBSD__)
+ /* Convert this block device to its character (raw) device. */
+ const char *template = "%s/r%s";
+#else
+ /* Keep the device name as it is. */
+ const char *template = "%s/%s";
+#endif
+
+ cwd = xgetcwd ();
+ res = xmalloc (strlen (cwd) + strlen (ent->d_name) + 3);
+ sprintf (res, template, cwd, ent->d_name);
+ strip_extra_slashes (res);
+ free (cwd);
+
+ /* /dev/root is not a real block device keep looking, takes care
+ of situation where root filesystem is on the same partition as
+ grub files */
+
+ if (strcmp(res, "/dev/root") == 0)
+ continue;
+
+ if (chdir (saved_cwd) < 0)
+ grub_util_error ("cannot restore the original directory");
+
+ free (saved_cwd);
+ closedir (dp);
+ return res;
+ }
+ }
+
+ if (chdir (saved_cwd) < 0)
+ grub_util_error ("cannot restore the original directory");
+
+ free (saved_cwd);
+ closedir (dp);
+ return 0;
+}
+
+#else /* __CYGWIN__ */
+
+/* Read drive/partition serial number from mbr/boot sector,
+ return 0 on read error, ~0 on unknown serial. */
+static unsigned
+get_bootsec_serial (const char *os_dev, int mbr)
+{
+ /* Read boot sector. */
+ int fd = open (os_dev, O_RDONLY);
+ if (fd < 0)
+ return 0;
+ unsigned char buf[0x200];
+ int n = read (fd, buf, sizeof (buf));
+ close (fd);
+ if (n != sizeof(buf))
+ return 0;
+
+ /* Check signature. */
+ if (!(buf[0x1fe] == 0x55 && buf[0x1ff] == 0xaa))
+ return ~0;
+
+ /* Serial number offset depends on boot sector type. */
+ if (mbr)
+ n = 0x1b8;
+ else if (memcmp (buf + 0x03, "NTFS", 4) == 0)
+ n = 0x048;
+ else if (memcmp (buf + 0x52, "FAT32", 5) == 0)
+ n = 0x043;
+ else if (memcmp (buf + 0x36, "FAT", 3) == 0)
+ n = 0x027;
+ else
+ return ~0;
+
+ unsigned serial = *(unsigned *)(buf + n);
+ if (serial == 0)
+ return ~0;
+ return serial;
+}
+
+char *
+grub_find_device (const char *path, dev_t dev)
+{
+ /* No root device for /cygdrive. */
+ if (dev == (DEV_CYGDRIVE_MAJOR << 16))
+ return 0;
+
+ /* Convert to full POSIX and Win32 path. */
+ char fullpath[PATH_MAX], winpath[PATH_MAX];
+ cygwin_conv_to_full_posix_path (path, fullpath);
+ cygwin_conv_to_full_win32_path (fullpath, winpath);
+
+ /* If identical, this is no real filesystem path. */
+ if (strcmp (fullpath, winpath) == 0)
+ return 0;
+
+ /* Check for floppy drive letter. */
+ if (winpath[0] && winpath[1] == ':' && strchr ("AaBb", winpath[0]))
+ return xstrdup (strchr ("Aa", winpath[0]) ? "/dev/fd0" : "/dev/fd1");
+
+ /* Cygwin returns the partition serial number in stat.st_dev.
+ This is never identical to the device number of the emulated
+ /dev/sdXN device, so above grub_find_device () does not work.
+ Search the partition with the same serial in boot sector instead. */
+ char devpath[sizeof ("/dev/sda15") + 13]; /* Size + Paranoia. */
+ int d;
+ for (d = 'a'; d <= 'z'; d++)
+ {
+ sprintf (devpath, "/dev/sd%c", d);
+ if (get_bootsec_serial (devpath, 1) == 0)
+ continue;
+ int p;
+ for (p = 1; p <= 15; p++)
+ {
+ sprintf (devpath, "/dev/sd%c%d", d, p);
+ unsigned ser = get_bootsec_serial (devpath, 0);
+ if (ser == 0)
+ break;
+ if (ser != (unsigned)~0 && dev == (dev_t)ser)
+ return xstrdup (devpath);
+ }
+ }
+ return 0;
+}
+
+#endif /* __CYGWIN__ */
+
+char *
+grub_guess_root_device (const char *dir)
+{
+ char *os_dev = NULL;
+#ifdef __GNU__
+ file_t file;
+ mach_port_t *ports;
+ int *ints;
+ loff_t *offsets;
+ char *data;
+ error_t err;
+ mach_msg_type_number_t num_ports = 0, num_ints = 0, num_offsets = 0, data_len = 0;
+ size_t name_len;
+
+ file = file_name_lookup (dir, 0, 0);
+ if (file == MACH_PORT_NULL)
+ return 0;
+
+ err = file_get_storage_info (file,
+ &ports, &num_ports,
+ &ints, &num_ints,
+ &offsets, &num_offsets,
+ &data, &data_len);
+
+ if (num_ints < 1)
+ grub_util_error ("Storage info for `%s' does not include type", dir);
+ if (ints[0] != STORAGE_DEVICE)
+ grub_util_error ("Filesystem of `%s' is not stored on local disk", dir);
+
+ if (num_ints < 5)
+ grub_util_error ("Storage info for `%s' does not include name", dir);
+ name_len = ints[4];
+ if (name_len < data_len)
+ grub_util_error ("Bogus name length for storage info for `%s'", dir);
+ if (data[name_len - 1] != '\0')
+ grub_util_error ("Storage name for `%s' not NUL-terminated", dir);
+
+ os_dev = xmalloc (strlen ("/dev/") + data_len);
+ memcpy (os_dev, "/dev/", strlen ("/dev/"));
+ memcpy (os_dev + strlen ("/dev/"), data, data_len);
+
+ if (ports && num_ports > 0)
+ {
+ mach_msg_type_number_t i;
+ for (i = 0; i < num_ports; i++)
+ {
+ mach_port_t port = ports[i];
+ if (port != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self(), port);
+ }
+ munmap ((caddr_t) ports, num_ports * sizeof (*ports));
+ }
+
+ if (ints && num_ints > 0)
+ munmap ((caddr_t) ints, num_ints * sizeof (*ints));
+ if (offsets && num_offsets > 0)
+ munmap ((caddr_t) offsets, num_offsets * sizeof (*offsets));
+ if (data && data_len > 0)
+ munmap (data, data_len);
+ mach_port_deallocate (mach_task_self (), file);
+#else /* !__GNU__ */
+ struct stat st;
+ dev_t dev;
+
+#ifdef __linux__
+ if (!os_dev)
+ os_dev = grub_find_root_device_from_mountinfo (dir, NULL);
+#endif /* __linux__ */
+
+#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
+ if (!os_dev)
+ os_dev = find_root_device_from_libzfs (dir);
+#endif
+
+ if (os_dev)
+ {
+ if (stat (os_dev, &st) >= 0)
+ dev = st.st_rdev;
+ else
+ grub_util_error ("cannot stat `%s'", os_dev);
+ free (os_dev);
+ }
+ else
+ {
+ if (stat (dir, &st) >= 0)
+ dev = st.st_dev;
+ else
+ grub_util_error ("cannot stat `%s'", dir);
+ }
+
+#ifdef __CYGWIN__
+ /* Cygwin specific function. */
+ os_dev = grub_find_device (dir, dev);
+
+#else
+
+ /* This might be truly slow, but is there any better way? */
+ os_dev = grub_find_device ("/dev", dev);
+#endif
+#endif /* !__GNU__ */
+
+ return os_dev;
+}
+
+static int
+grub_util_is_dmraid (const char *os_dev)
+{
+ if (! strncmp (os_dev, "/dev/mapper/nvidia_", 19))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/isw_", 16))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/hpt37x_", 19))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/hpt45x_", 19))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/via_", 16))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/lsi_", 16))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/pdc_", 16))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/jmicron_", 20))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/asr_", 16))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/sil_", 16))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/ddf1_", 17))
+ return 1;
+
+ return 0;
+}
+
+int
+grub_util_get_dev_abstraction (const char *os_dev __attribute__((unused)))
+{
+#ifdef __linux__
+ /* User explicitly claims that this drive is visible by BIOS. */
+ if (grub_util_biosdisk_is_present (os_dev))
+ return GRUB_DEV_ABSTRACTION_NONE;
+
+ /* Check for LVM. */
+ if (!strncmp (os_dev, "/dev/mapper/", 12)
+ && ! grub_util_is_dmraid (os_dev)
+ && strncmp (os_dev, "/dev/mapper/mpath", 17) != 0)
+ return GRUB_DEV_ABSTRACTION_LVM;
+
+ /* Check for RAID. */
+ if (!strncmp (os_dev, "/dev/md", 7))
+ return GRUB_DEV_ABSTRACTION_RAID;
+#endif
+
+ /* No abstraction found. */
+ return GRUB_DEV_ABSTRACTION_NONE;
+}
+
+#ifdef __linux__
+static char *
+get_mdadm_uuid (const char *os_dev)
+{
+ int mdadm_pipe[2];
+ pid_t mdadm_pid;
+ char *name = NULL;
+
+ if (pipe (mdadm_pipe) < 0)
+ {
+ grub_util_warn ("Unable to create pipe for mdadm: %s", strerror (errno));
+ return NULL;
+ }
+
+ mdadm_pid = fork ();
+ if (mdadm_pid < 0)
+ grub_util_warn ("Unable to fork mdadm: %s", strerror (errno));
+ else if (mdadm_pid == 0)
+ {
+ /* Child. */
+ char *argv[5];
+
+ close (mdadm_pipe[0]);
+ dup2 (mdadm_pipe[1], STDOUT_FILENO);
+ close (mdadm_pipe[1]);
+
+ /* execvp has inconvenient types, hence the casts. None of these
+ strings will actually be modified. */
+ argv[0] = (char *) "mdadm";
+ argv[1] = (char *) "--detail";
+ argv[2] = (char *) "--export";
+ argv[3] = (char *) os_dev;
+ argv[4] = NULL;
+ execvp ("mdadm", argv);
+ exit (127);
+ }
+ else
+ {
+ /* Parent. Read mdadm's output. */
+ FILE *mdadm;
+ char *buf = NULL;
+ size_t len = 0;
+
+ close (mdadm_pipe[1]);
+ mdadm = fdopen (mdadm_pipe[0], "r");
+ if (! mdadm)
+ {
+ grub_util_warn ("Unable to open stream from mdadm: %s",
+ strerror (errno));
+ goto out;
+ }
+
+ while (getline (&buf, &len, mdadm) > 0)
+ {
+ if (strncmp (buf, "MD_UUID=", sizeof ("MD_UUID=") - 1) == 0)
+ {
+ char *name_start, *ptri, *ptro;
+ size_t name_len;
+
+ free (name);
+ name_start = buf + sizeof ("MD_UUID=") - 1;
+ ptro = name = xmalloc (strlen (name_start) + 1);
+ for (ptri = name_start; *ptri && *ptri != '\n' && *ptri != '\r';
+ ptri++)
+ if ((*ptri >= '0' && *ptri <= '9')
+ || (*ptri >= 'a' && *ptri <= 'f')
+ || (*ptri >= 'A' && *ptri <= 'F'))
+ *ptro++ = *ptri;
+ *ptro = 0;
+ }
+ }
+
+out:
+ close (mdadm_pipe[0]);
+ waitpid (mdadm_pid, NULL, 0);
+ }
+
+ return name;
+}
+#endif /* __linux__ */
+
+char *
+grub_util_get_grub_dev (const char *os_dev)
+{
+ char *grub_dev = NULL;
+
+ switch (grub_util_get_dev_abstraction (os_dev))
+ {
+ case GRUB_DEV_ABSTRACTION_LVM:
+
+ {
+ unsigned short i, len;
+ grub_size_t offset = sizeof ("/dev/mapper/") - 1;
+
+ len = strlen (os_dev) - offset + 1;
+ grub_dev = xmalloc (len);
+
+ for (i = 0; i < len; i++, offset++)
+ {
+ grub_dev[i] = os_dev[offset];
+ if (os_dev[offset] == '-' && os_dev[offset + 1] == '-')
+ offset++;
+ }
+ }
+
+ break;
+
+ case GRUB_DEV_ABSTRACTION_RAID:
+
+ if (os_dev[7] == '_' && os_dev[8] == 'd')
+ {
+ /* This a partitionable RAID device of the form /dev/md_dNNpMM. */
+
+ char *p, *q;
+
+ p = strdup (os_dev + sizeof ("/dev/md_d") - 1);
+
+ q = strchr (p, 'p');
+ if (q)
+ *q = ',';
+
+ grub_dev = xasprintf ("md%s", p);
+ free (p);
+ }
+ else if (os_dev[7] == '/' && os_dev[8] == 'd')
+ {
+ /* This a partitionable RAID device of the form /dev/md/dNNpMM. */
+
+ char *p, *q;
+
+ p = strdup (os_dev + sizeof ("/dev/md/d") - 1);
+
+ q = strchr (p, 'p');
+ if (q)
+ *q = ',';
+
+ grub_dev = xasprintf ("md%s", p);
+ free (p);
+ }
+ else if (os_dev[7] >= '0' && os_dev[7] <= '9')
+ {
+ char *p , *q;
+
+ p = strdup (os_dev + sizeof ("/dev/md") - 1);
+
+ q = strchr (p, 'p');
+ if (q)
+ *q = ',';
+
+ grub_dev = xasprintf ("md%s", p);
+ free (p);
+ }
+ else if (os_dev[7] == '/' && os_dev[8] >= '0' && os_dev[8] <= '9')
+ {
+ char *p , *q;
+
+ p = strdup (os_dev + sizeof ("/dev/md/") - 1);
+
+ q = strchr (p, 'p');
+ if (q)
+ *q = ',';
+
+ grub_dev = xasprintf ("md%s", p);
+ free (p);
+ }
+ else if (os_dev[7] == '/')
+ {
+ /* mdraid 1.x with a free name. */
+ char *p , *q;
+
+ p = strdup (os_dev + sizeof ("/dev/md/") - 1);
+
+ q = strchr (p, 'p');
+ if (q)
+ *q = ',';
+
+ grub_dev = xasprintf ("md/%s", p);
+ free (p);
+ }
+ else
+ grub_util_error ("unknown kind of RAID device `%s'", os_dev);
+
+#ifdef __linux__
+ {
+ char *mdadm_name = get_mdadm_uuid (os_dev);
+ struct stat st;
+
+ if (mdadm_name)
+ {
+ const char *q;
+
+ for (q = os_dev + strlen (os_dev) - 1; q >= os_dev
+ && grub_isdigit (*q); q--);
+
+ if (q >= os_dev && *q == 'p')
+ {
+ free (grub_dev);
+ grub_dev = xasprintf ("mduuid/%s,%s", mdadm_name, q + 1);
+ goto done;
+ }
+ free (grub_dev);
+ grub_dev = xasprintf ("mduuid/%s", mdadm_name);
+
+ done:
+ free (mdadm_name);
+ }
+ }
+#endif /* __linux__ */
+
+ break;
+
+ default: /* GRUB_DEV_ABSTRACTION_NONE */
+ grub_dev = grub_util_biosdisk_get_grub_dev (os_dev);
+ }
+
+ return grub_dev;
+}
+
+const char *
+grub_util_check_block_device (const char *blk_dev)
+{
+ struct stat st;
+
+ if (stat (blk_dev, &st) < 0)
+ grub_util_error ("cannot stat `%s'", blk_dev);
+
+ if (S_ISBLK (st.st_mode))
+ return (blk_dev);
+ else
+ return 0;
+}
+
+const char *
+grub_util_check_char_device (const char *blk_dev)
+{
+ struct stat st;
+
+ if (stat (blk_dev, &st) < 0)
+ grub_util_error ("cannot stat `%s'", blk_dev);
+
+ if (S_ISCHR (st.st_mode))
+ return (blk_dev);
+ else
+ return 0;
+}
diff --git a/grub-core/kern/emu/hostdisk.c b/grub-core/kern/emu/hostdisk.c
new file mode 100644
index 0000000..feb2a8f
--- /dev/null
+++ b/grub-core/kern/emu/hostdisk.c
@@ -0,0 +1,1776 @@
+/* hostdisk.c - emulate biosdisk */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2004,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * 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/>.
+ */
+
+#include <grub/disk.h>
+#include <grub/partition.h>
+#include <grub/msdos_partition.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/emu/misc.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/misc.h>
+#include <grub/i18n.h>
+#include <grub/list.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+
+#ifdef __linux__
+# include <sys/ioctl.h> /* ioctl */
+# if !defined(__GLIBC__) || \
+ ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))
+/* Maybe libc doesn't have large file support. */
+# include <linux/unistd.h> /* _llseek */
+# endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */
+# ifndef BLKFLSBUF
+# define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */
+# endif /* ! BLKFLSBUF */
+# include <sys/ioctl.h> /* ioctl */
+# ifndef HDIO_GETGEO
+# define HDIO_GETGEO 0x0301 /* get device geometry */
+/* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
+ defined. */
+struct hd_geometry
+{
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned short cylinders;
+ unsigned long start;
+};
+# endif /* ! HDIO_GETGEO */
+# ifndef BLKGETSIZE64
+# define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size */
+# endif /* ! BLKGETSIZE64 */
+# ifndef MAJOR
+# ifndef MINORBITS
+# define MINORBITS 8
+# endif /* ! MINORBITS */
+# define MAJOR(dev) ((unsigned) ((dev) >> MINORBITS))
+# endif /* ! MAJOR */
+# ifndef FLOPPY_MAJOR
+# define FLOPPY_MAJOR 2
+# endif /* ! FLOPPY_MAJOR */
+# ifndef LOOP_MAJOR
+# define LOOP_MAJOR 7
+# endif /* ! LOOP_MAJOR */
+#endif /* __linux__ */
+
+#ifdef __CYGWIN__
+# include <sys/ioctl.h>
+# include <cygwin/fs.h> /* BLKGETSIZE64 */
+# include <cygwin/hdreg.h> /* HDIO_GETGEO */
+# define MAJOR(dev) ((unsigned) ((dev) >> 16))
+# define FLOPPY_MAJOR 2
+#endif
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+# include <sys/disk.h> /* DIOCGMEDIASIZE */
+# include <sys/param.h>
+# include <sys/sysctl.h>
+# define MAJOR(dev) major(dev)
+# define FLOPPY_MAJOR 2
+#endif
+
+#if defined(__APPLE__)
+# include <sys/disk.h>
+#endif
+
+#ifdef HAVE_DEVICE_MAPPER
+# include <libdevmapper.h>
+#endif
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <libgeom.h>
+#elif defined(__NetBSD__)
+# define HAVE_DIOCGDINFO
+# include <sys/ioctl.h>
+# include <sys/disklabel.h> /* struct disklabel */
+#else /* !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) */
+# undef HAVE_DIOCGDINFO
+#endif /* defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
+
+#if defined(__NetBSD__)
+# ifdef HAVE_GETRAWPARTITION
+# include <util.h> /* getrawpartition */
+# endif /* HAVE_GETRAWPARTITION */
+# include <sys/fdio.h>
+# ifndef FLOPPY_MAJOR
+# define FLOPPY_MAJOR 2
+# endif /* ! FLOPPY_MAJOR */
+# ifndef RAW_FLOPPY_MAJOR
+# define RAW_FLOPPY_MAJOR 9
+# endif /* ! RAW_FLOPPY_MAJOR */
+#endif /* defined(__NetBSD__) */
+
+struct
+{
+ char *drive;
+ char *device;
+} map[256];
+
+struct grub_util_biosdisk_data
+{
+ char *dev;
+ int access_mode;
+ int fd;
+ int is_disk;
+};
+
+#ifdef __linux__
+/* Check if we have devfs support. */
+static int
+have_devfs (void)
+{
+ static int dev_devfsd_exists = -1;
+
+ if (dev_devfsd_exists < 0)
+ {
+ struct stat st;
+
+ dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0;
+ }
+
+ return dev_devfsd_exists;
+}
+#endif /* __linux__ */
+
+#if defined(__NetBSD__)
+/* Adjust device driver parameters. This function should be called just
+ after successfully opening the device. For now, it simply prevents the
+ floppy driver from retrying operations on failure, as otherwise the
+ driver takes a while to abort when there is no floppy in the drive. */
+static void
+configure_device_driver (int fd)
+{
+ struct stat st;
+
+ if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode))
+ return;
+ if (major(st.st_rdev) == RAW_FLOPPY_MAJOR)
+ {
+ int floppy_opts;
+
+ if (ioctl (fd, FDIOCGETOPTS, &floppy_opts) == -1)
+ return;
+ floppy_opts |= FDOPT_NORETRY;
+ if (ioctl (fd, FDIOCSETOPTS, &floppy_opts) == -1)
+ return;
+ }
+}
+#endif /* defined(__NetBSD__) */
+
+static int
+find_grub_drive (const char *name)
+{
+ unsigned int i;
+
+ if (name)
+ {
+ for (i = 0; i < ARRAY_SIZE (map); i++)
+ if (map[i].drive && ! strcmp (map[i].drive, name))
+ return i;
+ }
+
+ return -1;
+}
+
+static int
+find_free_slot (void)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
+ if (! map[i].drive)
+ return i;
+
+ return -1;
+}
+
+static int
+grub_util_biosdisk_iterate (int (*hook) (const char *name))
+{
+ unsigned i;
+
+ for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
+ if (map[i].drive && hook (map[i].drive))
+ return 1;
+
+ return 0;
+}
+
+static grub_err_t
+grub_util_biosdisk_open (const char *name, grub_disk_t disk)
+{
+ int drive;
+ struct stat st;
+ struct grub_util_biosdisk_data *data;
+
+ drive = find_grub_drive (name);
+ if (drive < 0)
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+ "no mapping exists for `%s'", name);
+
+ disk->id = drive;
+ disk->data = data = xmalloc (sizeof (struct grub_util_biosdisk_data));
+ data->dev = NULL;
+ data->access_mode = 0;
+ data->fd = -1;
+ data->is_disk = 0;
+
+ /* Get the size. */
+#if defined(__MINGW32__)
+ {
+ grub_uint64_t size;
+
+ size = grub_util_get_disk_size (map[drive].device);
+
+ if (size % 512)
+ grub_util_error ("unaligned device size");
+
+ disk->total_sectors = size >> 9;
+
+ grub_util_info ("the size of %s is %llu", name, disk->total_sectors);
+
+ return GRUB_ERR_NONE;
+ }
+#elif defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || \
+ defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__)
+ {
+# if defined(__NetBSD__)
+ struct disklabel label;
+# else
+ unsigned long long nr;
+# endif
+ int fd;
+
+ fd = open (map[drive].device, O_RDONLY);
+ if (fd == -1)
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot open `%s' while attempting to get disk size", map[drive].device);
+
+# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__)
+ if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode))
+# else
+ if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode))
+# endif
+ {
+ close (fd);
+ goto fail;
+ }
+ data->is_disk = 1;
+
+# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ if (ioctl (fd, DIOCGMEDIASIZE, &nr))
+# elif defined(__APPLE__)
+ if (ioctl (fd, DKIOCGETBLOCKCOUNT, &nr))
+# elif defined(__NetBSD__)
+ configure_device_driver (fd);
+ if (ioctl (fd, DIOCGDINFO, &label) == -1)
+# else
+ if (ioctl (fd, BLKGETSIZE64, &nr))
+# endif
+ {
+ close (fd);
+ goto fail;
+ }
+
+ close (fd);
+
+# if defined (__APPLE__)
+ disk->total_sectors = nr;
+# elif defined(__NetBSD__)
+ disk->total_sectors = label.d_secperunit;
+# else
+ disk->total_sectors = nr / 512;
+
+ if (nr % 512)
+ grub_util_error ("unaligned device size");
+# endif
+
+ grub_util_info ("the size of %s is %llu", name, disk->total_sectors);
+
+ return GRUB_ERR_NONE;
+ }
+
+ fail:
+ /* In GNU/Hurd, stat() will return the right size. */
+#elif !defined (__GNU__)
+# warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
+#endif
+ if (stat (map[drive].device, &st) < 0)
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot stat `%s'", map[drive].device);
+
+ disk->total_sectors = st.st_size >> GRUB_DISK_SECTOR_BITS;
+
+ grub_util_info ("the size of %s is %lu", name, disk->total_sectors);
+
+ return GRUB_ERR_NONE;
+}
+
+#ifdef HAVE_DEVICE_MAPPER
+static int
+device_is_mapped (const char *dev)
+{
+ struct stat st;
+
+ if (stat (dev, &st) < 0)
+ return 0;
+
+ return dm_is_dm_major (major (st.st_rdev));
+}
+#endif /* HAVE_DEVICE_MAPPER */
+
+#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
+/* FIXME: geom actually gives us the whole container hierarchy.
+ It can be used more efficiently than this. */
+static void
+follow_geom_up (const char *name, grub_disk_addr_t *off_out, char **name_out)
+{
+ struct gmesh mesh;
+ struct gclass *class;
+ int error;
+ struct ggeom *geom;
+
+ grub_util_info ("following geom '%s'", name);
+
+ error = geom_gettree (&mesh);
+ if (error != 0)
+ grub_util_error ("couldn't open geom");
+
+ LIST_FOREACH (class, &mesh.lg_class, lg_class)
+ if (strcasecmp (class->lg_name, "part") == 0)
+ break;
+ if (!class)
+ grub_util_error ("couldn't open geom part");
+
+ LIST_FOREACH (geom, &class->lg_geom, lg_geom)
+ {
+ struct gprovider *provider;
+ LIST_FOREACH (provider, &geom->lg_provider, lg_provider)
+ if (strcmp (provider->lg_name, name) == 0)
+ {
+ char *name_tmp = xstrdup (geom->lg_name);
+ grub_disk_addr_t off = 0;
+ struct gconfig *config;
+ grub_util_info ("geom '%s' has parent '%s'", name, geom->lg_name);
+
+ follow_geom_up (name_tmp, &off, name_out);
+ free (name_tmp);
+ LIST_FOREACH (config, &provider->lg_config, lg_config)
+ if (strcasecmp (config->lg_name, "start") == 0)
+ off += strtoull (config->lg_val, 0, 10);
+ if (off_out)
+ *off_out = off;
+ return;
+ }
+ }
+ grub_util_info ("geom '%s' has no parent", name);
+ if (name_out)
+ *name_out = xstrdup (name);
+ if (off_out)
+ *off_out = 0;
+}
+
+static grub_disk_addr_t
+find_partition_start (const char *dev)
+{
+ grub_disk_addr_t out;
+ if (strncmp (dev, "/dev/", sizeof ("/dev/") - 1) != 0)
+ return 0;
+ follow_geom_up (dev + sizeof ("/dev/") - 1, &out, NULL);
+
+ return out;
+}
+#elif defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO)
+static grub_disk_addr_t
+find_partition_start (const char *dev)
+{
+ int fd;
+# if !defined(HAVE_DIOCGDINFO)
+ struct hd_geometry hdg;
+# else /* defined(HAVE_DIOCGDINFO) */
+ struct disklabel label;
+ int p_index;
+# endif /* !defined(HAVE_DIOCGDINFO) */
+
+# ifdef HAVE_DEVICE_MAPPER
+ if (grub_device_mapper_supported () && device_is_mapped (dev)) {
+ struct dm_task *task = NULL;
+ grub_uint64_t start, length;
+ char *target_type, *params, *space;
+ grub_disk_addr_t partition_start;
+
+ /* If any device-mapper operation fails, we fall back silently to
+ HDIO_GETGEO. */
+ task = dm_task_create (DM_DEVICE_TABLE);
+ if (! task)
+ {
+ grub_dprintf ("hostdisk", "dm_task_create failed\n");
+ goto devmapper_fail;
+ }
+
+ if (! dm_task_set_name (task, dev))
+ {
+ grub_dprintf ("hostdisk", "dm_task_set_name failed\n");
+ goto devmapper_fail;
+ }
+
+ if (! dm_task_run (task))
+ {
+ grub_dprintf ("hostdisk", "dm_task_run failed\n");
+ goto devmapper_fail;
+ }
+
+ dm_get_next_target (task, NULL, &start, &length, &target_type, &params);
+ if (! target_type)
+ {
+ grub_dprintf ("hostdisk", "no dm target\n");
+ goto devmapper_fail;
+ }
+ if (strcmp (target_type, "linear") != 0)
+ {
+ grub_dprintf ("hostdisk", "ignoring dm target %s (not linear)\n",
+ target_type);
+ goto devmapper_fail;
+ }
+ if (! params)
+ {
+ grub_dprintf ("hostdisk", "no dm params\n");
+ goto devmapper_fail;
+ }
+
+ /* The params string for a linear target looks like this:
+ DEVICE-NAME START-SECTOR
+ Parse this out. */
+ space = strchr (params, ' ');
+ if (! space)
+ goto devmapper_fail;
+ errno = 0;
+ partition_start = strtoull (space + 1, NULL, 10);
+ if (errno == 0)
+ {
+ grub_dprintf ("hostdisk", "dm %s starts at %llu\n",
+ dev, (unsigned long long) partition_start);
+ dm_task_destroy (task);
+ return partition_start;
+ }
+
+devmapper_fail:
+ if (task)
+ dm_task_destroy (task);
+ }
+# endif /* HAVE_DEVICE_MAPPER */
+
+ fd = open (dev, O_RDONLY);
+ if (fd == -1)
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE,
+# if !defined(HAVE_DIOCGDINFO)
+ "cannot open `%s' while attempting to get disk geometry", dev);
+# else /* defined(HAVE_DIOCGDINFO) */
+ "cannot open `%s' while attempting to get disk label", dev);
+# endif /* !defined(HAVE_DIOCGDINFO) */
+ return 0;
+ }
+
+# if !defined(HAVE_DIOCGDINFO)
+ if (ioctl (fd, HDIO_GETGEO, &hdg))
+# else /* defined(HAVE_DIOCGDINFO) */
+# if defined(__NetBSD__)
+ configure_device_driver (fd);
+# endif /* defined(__NetBSD__) */
+ if (ioctl (fd, DIOCGDINFO, &label) == -1)
+# endif /* !defined(HAVE_DIOCGDINFO) */
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE,
+# if !defined(HAVE_DIOCGDINFO)
+ "cannot get disk geometry of `%s'", dev);
+# else /* defined(HAVE_DIOCGDINFO) */
+ "cannot get disk label of `%s'", dev);
+# endif /* !defined(HAVE_DIOCGDINFO) */
+ close (fd);
+ return 0;
+ }
+
+ close (fd);
+
+# if !defined(HAVE_DIOCGDINFO)
+ return hdg.start;
+# else /* defined(HAVE_DIOCGDINFO) */
+ if (dev[0])
+ p_index = dev[strlen(dev) - 1] - 'a';
+ else
+ p_index = -1;
+
+ if (p_index >= label.d_npartitions || p_index < 0)
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE,
+ "no disk label entry for `%s'", dev);
+ return 0;
+ }
+ return (grub_disk_addr_t) label.d_partitions[p_index].p_offset;
+# endif /* !defined(HAVE_DIOCGDINFO) */
+}
+#endif /* __linux__ || __CYGWIN__ || HAVE_DIOCGDINFO */
+
+#ifdef __linux__
+/* Cache of partition start sectors for each disk. */
+struct linux_partition_cache
+{
+ struct linux_partition_cache *next;
+ char *dev;
+ unsigned long start;
+ int partno;
+};
+
+struct linux_partition_cache *linux_partition_cache_list;
+
+static int
+linux_find_partition (char *dev, grub_disk_addr_t sector)
+{
+ size_t len = strlen (dev);
+ const char *format;
+ char *p;
+ int i;
+ char real_dev[PATH_MAX];
+ struct linux_partition_cache *cache;
+
+ strcpy(real_dev, dev);
+
+ if (have_devfs () && strcmp (real_dev + len - 5, "/disc") == 0)
+ {
+ p = real_dev + len - 4;
+ format = "part%d";
+ }
+ else if (real_dev[len - 1] >= '0' && real_dev[len - 1] <= '9')
+ {
+ p = real_dev + len;
+ format = "p%d";
+ }
+ else
+ {
+ p = real_dev + len;
+ format = "%d";
+ }
+
+ for (cache = linux_partition_cache_list; cache; cache = cache->next)
+ {
+ if (strcmp (cache->dev, dev) == 0 && cache->start == sector)
+ {
+ sprintf (p, format, cache->partno);
+ strcpy (dev, real_dev);
+ return 1;
+ }
+ }
+
+ for (i = 1; i < 10000; i++)
+ {
+ int fd;
+ grub_disk_addr_t start;
+
+ sprintf (p, format, i);
+
+ fd = open (real_dev, O_RDONLY);
+ if (fd == -1)
+ continue;
+ close (fd);
+
+ start = find_partition_start (real_dev);
+ /* We don't care about errors here. */
+ grub_errno = GRUB_ERR_NONE;
+
+ if (start == sector)
+ {
+ struct linux_partition_cache *new_cache_item;
+
+ new_cache_item = xmalloc (sizeof *new_cache_item);
+ new_cache_item->dev = xstrdup (dev);
+ new_cache_item->start = start;
+ new_cache_item->partno = i;
+ grub_list_push (GRUB_AS_LIST_P (&linux_partition_cache_list),
+ GRUB_AS_LIST (new_cache_item));
+
+ strcpy (dev, real_dev);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+#endif /* __linux__ */
+
+static int
+open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags)
+{
+ int fd;
+ struct grub_util_biosdisk_data *data = disk->data;
+
+#ifdef O_LARGEFILE
+ flags |= O_LARGEFILE;
+#endif
+#ifdef O_SYNC
+ flags |= O_SYNC;
+#endif
+#ifdef O_FSYNC
+ flags |= O_FSYNC;
+#endif
+#ifdef O_BINARY
+ flags |= O_BINARY;
+#endif
+
+#ifdef __linux__
+ /* Linux has a bug that the disk cache for a whole disk is not consistent
+ with the one for a partition of the disk. */
+ {
+ int is_partition = 0;
+ char dev[PATH_MAX];
+ grub_disk_addr_t part_start = 0;
+
+ part_start = grub_partition_get_start (disk->partition);
+
+ strcpy (dev, map[disk->id].device);
+ if (disk->partition && sector >= part_start
+ && strncmp (map[disk->id].device, "/dev/", 5) == 0)
+ is_partition = linux_find_partition (dev, part_start);
+
+ if (data->dev && strcmp (data->dev, dev) == 0 &&
+ data->access_mode == (flags & O_ACCMODE))
+ {
+ grub_dprintf ("hostdisk", "reusing open device `%s'\n", dev);
+ fd = data->fd;
+ }
+ else
+ {
+ free (data->dev);
+ if (data->fd != -1)
+ {
+ if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY)
+ {
+ fsync (data->fd);
+#ifdef __linux__
+ if (data->is_disk)
+ ioctl (data->fd, BLKFLSBUF, 0);
+#endif
+ }
+
+ close (data->fd);
+ data->fd = -1;
+ }
+
+ /* Open the partition. */
+ grub_dprintf ("hostdisk", "opening the device `%s' in open_device()\n", dev);
+ fd = open (dev, flags);
+ if (fd < 0)
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", dev);
+ return -1;
+ }
+
+ data->dev = xstrdup (dev);
+ data->access_mode = (flags & O_ACCMODE);
+ data->fd = fd;
+ }
+
+ if (is_partition)
+ sector -= part_start;
+ }
+#else /* ! __linux__ */
+#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
+ int sysctl_flags, sysctl_oldflags;
+ size_t sysctl_size = sizeof (sysctl_flags);
+
+ if (sysctlbyname ("kern.geom.debugflags", &sysctl_oldflags, &sysctl_size, NULL, 0))
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "cannot get current flags of sysctl kern.geom.debugflags");
+ return -1;
+ }
+ sysctl_flags = sysctl_oldflags | 0x10;
+ if (! (sysctl_oldflags & 0x10)
+ && sysctlbyname ("kern.geom.debugflags", NULL , 0, &sysctl_flags, sysctl_size))
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "cannot set flags of sysctl kern.geom.debugflags");
+ return -1;
+ }
+#endif
+
+ if (data->dev && strcmp (data->dev, map[disk->id].device) == 0 &&
+ data->access_mode == (flags & O_ACCMODE))
+ {
+ grub_dprintf ("hostdisk", "reusing open device `%s'\n", data->dev);
+ fd = data->fd;
+ }
+ else
+ {
+ free (data->dev);
+ if (data->fd != -1)
+ {
+ if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY)
+ {
+ fsync (data->fd);
+#ifdef __linux__
+ if (data->is_disk)
+ ioctl (data->fd, BLKFLSBUF, 0);
+#endif
+ }
+ close (data->fd);
+ data->fd = -1;
+ }
+
+ fd = open (map[disk->id].device, flags);
+ if (fd >= 0)
+ {
+ data->dev = xstrdup (map[disk->id].device);
+ data->access_mode = (flags & O_ACCMODE);
+ data->fd = fd;
+ }
+ }
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ if (! (sysctl_oldflags & 0x10)
+ && sysctlbyname ("kern.geom.debugflags", NULL , 0, &sysctl_oldflags, sysctl_size))
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "cannot set flags back to the old value for sysctl kern.geom.debugflags");
+ return -1;
+ }
+#endif
+
+#if defined(__APPLE__)
+ /* If we can't have exclusive access, try shared access */
+ if (fd < 0)
+ fd = open(map[disk->id].device, flags | O_SHLOCK);
+#endif
+
+ if (fd < 0)
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' in open_device()", map[disk->id].device);
+ return -1;
+ }
+#endif /* ! __linux__ */
+
+#if defined(__NetBSD__)
+ configure_device_driver (fd);
+#endif /* defined(__NetBSD__) */
+
+#if defined(__linux__) && (!defined(__GLIBC__) || \
+ ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
+ /* Maybe libc doesn't have large file support. */
+ {
+ loff_t offset, result;
+ static int _llseek (uint filedes, ulong hi, ulong lo,
+ loff_t *res, uint wh);
+ _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo,
+ loff_t *, res, uint, wh);
+
+ offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS;
+ if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET))
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
+ close (fd);
+ return -1;
+ }
+ }
+#else
+ {
+ off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS;
+
+ if (lseek (fd, offset, SEEK_SET) != offset)
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
+ close (fd);
+ return -1;
+ }
+ }
+#endif
+
+ return fd;
+}
+
+/* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
+ error occurs, otherwise return LEN. */
+static ssize_t
+nread (int fd, char *buf, size_t len)
+{
+ ssize_t size = len;
+
+ while (len)
+ {
+ ssize_t ret = read (fd, buf, len);
+
+ if (ret <= 0)
+ {
+ if (errno == EINTR)
+ continue;
+ else
+ return ret;
+ }
+
+ len -= ret;
+ buf += ret;
+ }
+
+ return size;
+}
+
+/* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
+ error occurs, otherwise return LEN. */
+static ssize_t
+nwrite (int fd, const char *buf, size_t len)
+{
+ ssize_t size = len;
+
+ while (len)
+ {
+ ssize_t ret = write (fd, buf, len);
+
+ if (ret <= 0)
+ {
+ if (errno == EINTR)
+ continue;
+ else
+ return ret;
+ }
+
+ len -= ret;
+ buf += ret;
+ }
+
+ return size;
+}
+
+static grub_err_t
+grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
+ grub_size_t size, char *buf)
+{
+ int fd;
+
+ /* Split pre-partition and partition reads. */
+ if (disk->partition && sector < disk->partition->start
+ && sector + size > disk->partition->start)
+ {
+ grub_err_t err;
+ err = grub_util_biosdisk_read (disk, sector,
+ disk->partition->start - sector,
+ buf);
+ if (err)
+ return err;
+
+ return grub_util_biosdisk_read (disk, disk->partition->start,
+ size - (disk->partition->start - sector),
+ buf + ((disk->partition->start - sector)
+ << GRUB_DISK_SECTOR_BITS));
+ }
+
+ fd = open_device (disk, sector, O_RDONLY);
+ if (fd < 0)
+ return grub_errno;
+
+#ifdef __linux__
+ if (sector == 0 && size > 1)
+ {
+ /* Work around a bug in Linux ez remapping. Linux remaps all
+ sectors that are read together with the MBR in one read. It
+ should only remap the MBR, so we split the read in two
+ parts. -jochen */
+ if (nread (fd, buf, GRUB_DISK_SECTOR_SIZE) != GRUB_DISK_SECTOR_SIZE)
+ {
+ grub_error (GRUB_ERR_READ_ERROR, "cannot read `%s'", map[disk->id].device);
+ return grub_errno;
+ }
+
+ buf += GRUB_DISK_SECTOR_SIZE;
+ size--;
+ }
+#endif /* __linux__ */
+
+ if (nread (fd, buf, size << GRUB_DISK_SECTOR_BITS)
+ != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
+ grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", map[disk->id].device);
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
+ grub_size_t size, const char *buf)
+{
+ int fd;
+
+ /* Split pre-partition and partition writes. */
+ if (disk->partition && sector < disk->partition->start
+ && sector + size > disk->partition->start)
+ {
+ grub_err_t err;
+ err = grub_util_biosdisk_write (disk, sector,
+ disk->partition->start - sector,
+ buf);
+ if (err)
+ return err;
+
+ return grub_util_biosdisk_write (disk, disk->partition->start,
+ size - (disk->partition->start - sector),
+ buf + ((disk->partition->start - sector)
+ << GRUB_DISK_SECTOR_BITS));
+ }
+
+ fd = open_device (disk, sector, O_WRONLY);
+ if (fd < 0)
+ return grub_errno;
+
+ if (nwrite (fd, buf, size << GRUB_DISK_SECTOR_BITS)
+ != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
+ grub_error (GRUB_ERR_WRITE_ERROR, "cannot write to `%s'", map[disk->id].device);
+
+ return grub_errno;
+}
+
+grub_err_t
+grub_util_biosdisk_flush (struct grub_disk *disk)
+{
+ struct grub_util_biosdisk_data *data = disk->data;
+
+ if (disk->dev->id != GRUB_DISK_DEVICE_BIOSDISK_ID)
+ return GRUB_ERR_NONE;
+ if (data->fd == -1)
+ {
+ data->fd = open_device (disk, 0, O_RDONLY);
+ if (data->fd < 0)
+ return grub_errno;
+ }
+ fsync (data->fd);
+#ifdef __linux__
+ if (data->is_disk)
+ ioctl (data->fd, BLKFLSBUF, 0);
+#endif
+ return GRUB_ERR_NONE;
+}
+
+static void
+grub_util_biosdisk_close (struct grub_disk *disk)
+{
+ struct grub_util_biosdisk_data *data = disk->data;
+
+ free (data->dev);
+ if (data->fd != -1)
+ {
+ if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY)
+ grub_util_biosdisk_flush (disk);
+ close (data->fd);
+ }
+ free (data);
+}
+
+static struct grub_disk_dev grub_util_biosdisk_dev =
+ {
+ .name = "biosdisk",
+ .id = GRUB_DISK_DEVICE_BIOSDISK_ID,
+ .iterate = grub_util_biosdisk_iterate,
+ .open = grub_util_biosdisk_open,
+ .close = grub_util_biosdisk_close,
+ .read = grub_util_biosdisk_read,
+ .write = grub_util_biosdisk_write,
+ .next = 0
+ };
+
+static void
+read_device_map (const char *dev_map)
+{
+ FILE *fp;
+ char buf[1024]; /* XXX */
+ int lineno = 0;
+ struct stat st;
+
+ auto void show_error (const char *msg);
+ void show_error (const char *msg)
+ {
+ grub_util_error ("%s:%d: %s", dev_map, lineno, msg);
+ }
+
+ fp = fopen (dev_map, "r");
+ if (! fp)
+ {
+ grub_util_info (_("cannot open `%s'"), dev_map);
+ return;
+ }
+
+ while (fgets (buf, sizeof (buf), fp))
+ {
+ char *p = buf;
+ char *e;
+ int drive;
+
+ lineno++;
+
+ /* Skip leading spaces. */
+ while (*p && isspace (*p))
+ p++;
+
+ /* If the first character is `#' or NUL, skip this line. */
+ if (*p == '\0' || *p == '#')
+ continue;
+
+ if (*p != '(')
+ show_error ("No open parenthesis found");
+
+ p++;
+ /* Find a free slot. */
+ drive = find_free_slot ();
+ if (drive < 0)
+ show_error ("Map table size exceeded");
+
+ e = p;
+ p = strchr (p, ')');
+ if (! p)
+ show_error ("No close parenthesis found");
+
+ map[drive].drive = xmalloc (p - e + sizeof ('\0'));
+ strncpy (map[drive].drive, e, p - e + sizeof ('\0'));
+ map[drive].drive[p - e] = '\0';
+
+ p++;
+ /* Skip leading spaces. */
+ while (*p && isspace (*p))
+ p++;
+
+ if (*p == '\0')
+ show_error ("No filename found");
+
+ /* NUL-terminate the filename. */
+ e = p;
+ while (*e && ! isspace (*e))
+ e++;
+ *e = '\0';
+
+#ifdef __MINGW32__
+ (void) st;
+ if (grub_util_get_disk_size (p) == -1LL)
+#else
+ if (stat (p, &st) == -1)
+#endif
+ {
+ free (map[drive].drive);
+ map[drive].drive = NULL;
+ grub_util_info ("Cannot stat `%s', skipping", p);
+ continue;
+ }
+
+#ifdef __linux__
+ /* On Linux, the devfs uses symbolic links horribly, and that
+ confuses the interface very much, so use realpath to expand
+ symbolic links. Leave /dev/mapper/ alone, though. */
+ if (strncmp (p, "/dev/mapper/", 12) != 0)
+ {
+ map[drive].device = xmalloc (PATH_MAX);
+ if (! realpath (p, map[drive].device))
+ grub_util_error ("cannot get the real path of `%s'", p);
+ }
+ else
+#endif
+ map[drive].device = xstrdup (p);
+ }
+
+ fclose (fp);
+}
+
+void
+grub_util_biosdisk_init (const char *dev_map)
+{
+ read_device_map (dev_map);
+ grub_disk_dev_register (&grub_util_biosdisk_dev);
+}
+
+void
+grub_util_biosdisk_fini (void)
+{
+ unsigned i;
+
+ for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
+ {
+ if (map[i].drive)
+ free (map[i].drive);
+ if (map[i].device)
+ free (map[i].device);
+ map[i].drive = map[i].device = NULL;
+ }
+
+ grub_disk_dev_unregister (&grub_util_biosdisk_dev);
+}
+
+/*
+ * Note: we do not use the new partition naming scheme as dos_part does not
+ * necessarily correspond to an msdos partition.
+ */
+static char *
+make_device_name (int drive, int dos_part, int bsd_part)
+{
+ char *ret;
+ char *dos_part_str = NULL;
+ char *bsd_part_str = NULL;
+
+ if (dos_part >= 0)
+ dos_part_str = xasprintf (",%d", dos_part + 1);
+
+ if (bsd_part >= 0)
+ bsd_part_str = xasprintf (",%d", bsd_part + 1);
+
+ ret = xasprintf ("%s%s%s", map[drive].drive,
+ dos_part_str ? : "",
+ bsd_part_str ? : "");
+
+ if (dos_part_str)
+ free (dos_part_str);
+
+ if (bsd_part_str)
+ free (bsd_part_str);
+
+ return ret;
+}
+
+static char *
+convert_system_partition_to_system_disk (const char *os_dev, struct stat *st)
+{
+#if defined(__linux__)
+ char *path = xmalloc (PATH_MAX);
+ if (! realpath (os_dev, path))
+ return NULL;
+
+ if (strncmp ("/dev/", path, 5) == 0)
+ {
+ char *p = path + 5;
+
+ /* If this is an IDE disk. */
+ if (strncmp ("ide/", p, 4) == 0)
+ {
+ p = strstr (p, "part");
+ if (p)
+ strcpy (p, "disc");
+
+ return path;
+ }
+
+ /* If this is a SCSI disk. */
+ if (strncmp ("scsi/", p, 5) == 0)
+ {
+ p = strstr (p, "part");
+ if (p)
+ strcpy (p, "disc");
+
+ return path;
+ }
+
+ /* If this is a DAC960 disk. */
+ if (strncmp ("rd/c", p, 4) == 0)
+ {
+ /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
+ p = strchr (p, 'p');
+ if (p)
+ *p = '\0';
+
+ return path;
+ }
+
+ /* If this is a Mylex AcceleRAID Array. */
+ if (strncmp ("rs/c", p, 4) == 0)
+ {
+ /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
+ p = strchr (p, 'p');
+ if (p)
+ *p = '\0';
+
+ return path;
+ }
+ /* If this is a CCISS disk. */
+ if (strncmp ("cciss/c", p, sizeof ("cciss/c") - 1) == 0)
+ {
+ /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
+ p = strchr (p, 'p');
+ if (p)
+ *p = '\0';
+
+ return path;
+ }
+
+ /* If this is a Compaq Intelligent Drive Array. */
+ if (strncmp ("ida/c", p, sizeof ("ida/c") - 1) == 0)
+ {
+ /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */
+ p = strchr (p, 'p');
+ if (p)
+ *p = '\0';
+
+ return path;
+ }
+
+ /* If this is an I2O disk. */
+ if (strncmp ("i2o/hd", p, sizeof ("i2o/hd") - 1) == 0)
+ {
+ /* /dev/i2o/hd[a-z]([0-9]+)? */
+ p[sizeof ("i2o/hda") - 1] = '\0';
+ return path;
+ }
+
+ /* If this is a MultiMediaCard (MMC). */
+ if (strncmp ("mmcblk", p, sizeof ("mmcblk") - 1) == 0)
+ {
+ /* /dev/mmcblk[0-9]+(p[0-9]+)? */
+ p = strchr (p, 'p');
+ if (p)
+ *p = '\0';
+
+ return path;
+ }
+
+ if (strncmp ("md", p, 2) == 0
+ && p[2] >= '0' && p[2] <= '9')
+ {
+ char *ptr = p + 2;
+ while (*ptr >= '0' && *ptr <= '9')
+ ptr++;
+ *ptr = 0;
+ return path;
+ }
+
+ /* If this is an IDE, SCSI or Virtio disk. */
+ if (strncmp ("vdisk", p, 5) == 0
+ && p[5] >= 'a' && p[5] <= 'z')
+ {
+ /* /dev/vdisk[a-z][0-9]* */
+ p[6] = '\0';
+ return path;
+ }
+ if ((strncmp ("hd", p, 2) == 0
+ || strncmp ("vd", p, 2) == 0
+ || strncmp ("sd", p, 2) == 0)
+ && p[2] >= 'a' && p[2] <= 'z')
+ {
+ char *pp = p + 2;
+ while (*pp >= 'a' && *pp <= 'z')
+ pp++;
+ /* /dev/[hsv]d[a-z]+[0-9]* */
+ *pp = '\0';
+ return path;
+ }
+
+ /* If this is a Xen virtual block device. */
+ if ((strncmp ("xvd", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z')
+ {
+ char *pp = p + 3;
+ while (*pp >= 'a' && *pp <= 'z')
+ pp++;
+ /* /dev/xvd[a-z]+[0-9]* */
+ *pp = '\0';
+ return path;
+ }
+
+#ifdef HAVE_DEVICE_MAPPER
+ /* If this is a DM-RAID device.
+ Compare os_dev rather than path here, since nodes under
+ /dev/mapper/ are often symlinks. */
+ if ((strncmp ("/dev/mapper/", os_dev, 12) == 0))
+ {
+ struct dm_tree *tree;
+ uint32_t maj, min;
+ struct dm_tree_node *node = NULL, *child;
+ void *handle;
+ const char *node_uuid, *mapper_name = NULL, *child_uuid, *child_name;
+
+ tree = dm_tree_create ();
+ if (! tree)
+ {
+ grub_dprintf ("hostdisk", "dm_tree_create failed\n");
+ goto devmapper_out;
+ }
+
+ maj = major (st->st_rdev);
+ min = minor (st->st_rdev);
+ if (! dm_tree_add_dev (tree, maj, min))
+ {
+ grub_dprintf ("hostdisk", "dm_tree_add_dev failed\n");
+ goto devmapper_out;
+ }
+
+ node = dm_tree_find_node (tree, maj, min);
+ if (! node)
+ {
+ grub_dprintf ("hostdisk", "dm_tree_find_node failed\n");
+ goto devmapper_out;
+ }
+ node_uuid = dm_tree_node_get_uuid (node);
+ if (! node_uuid)
+ {
+ grub_dprintf ("hostdisk", "%s has no DM uuid\n", path);
+ node = NULL;
+ goto devmapper_out;
+ }
+ else if (strncmp (node_uuid, "DMRAID-", 7) != 0)
+ {
+ grub_dprintf ("hostdisk", "%s is not DM-RAID\n", path);
+ node = NULL;
+ goto devmapper_out;
+ }
+
+ handle = NULL;
+ /* Counter-intuitively, device-mapper refers to the disk-like
+ device containing a DM-RAID partition device as a "child" of
+ the partition device. */
+ child = dm_tree_next_child (&handle, node, 0);
+ if (! child)
+ {
+ grub_dprintf ("hostdisk", "%s has no DM children\n", path);
+ goto devmapper_out;
+ }
+ child_uuid = dm_tree_node_get_uuid (child);
+ if (! child_uuid)
+ {
+ grub_dprintf ("hostdisk", "%s child has no DM uuid\n", path);
+ goto devmapper_out;
+ }
+ else if (strncmp (child_uuid, "DMRAID-", 7) != 0)
+ {
+ grub_dprintf ("hostdisk", "%s child is not DM-RAID\n", path);
+ goto devmapper_out;
+ }
+ child_name = dm_tree_node_get_name (child);
+ if (! child_name)
+ {
+ grub_dprintf ("hostdisk", "%s child has no DM name\n", path);
+ goto devmapper_out;
+ }
+ mapper_name = child_name;
+
+devmapper_out:
+ if (! mapper_name && node)
+ {
+ /* This is a DM-RAID disk, not a partition. */
+ mapper_name = dm_tree_node_get_name (node);
+ if (! mapper_name)
+ grub_dprintf ("hostdisk", "%s has no DM name\n", path);
+ }
+ if (tree)
+ dm_tree_free (tree);
+ free (path);
+ if (mapper_name)
+ return xasprintf ("/dev/mapper/%s", mapper_name);
+ else
+ return NULL;
+ }
+#endif /* HAVE_DEVICE_MAPPER */
+ }
+
+ return path;
+
+#elif defined(__GNU__)
+ char *path = xstrdup (os_dev);
+ if (strncmp ("/dev/sd", path, 7) == 0 || strncmp ("/dev/hd", path, 7) == 0)
+ {
+ char *p = strchr (path + 7, 's');
+ if (p)
+ *p = '\0';
+ }
+ return path;
+
+#elif defined(__CYGWIN__)
+ char *path = xstrdup (os_dev);
+ if (strncmp ("/dev/sd", path, 7) == 0 && 'a' <= path[7] && path[7] <= 'z')
+ path[8] = 0;
+ return path;
+
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ char *out, *out2;
+ if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0)
+ return xstrdup (os_dev);
+ follow_geom_up (os_dev + sizeof ("/dev/") - 1, NULL, &out);
+
+ out2 = xasprintf ("/dev/%s", out);
+ free (out);
+
+ return out2;
+#elif defined(__APPLE__)
+ char *path = xstrdup (os_dev);
+ if (strncmp ("/dev/", path, 5) == 0)
+ {
+ char *p;
+ for (p = path + 5; *p; ++p)
+ if (grub_isdigit(*p))
+ {
+ p = strpbrk (p, "sp");
+ if (p)
+ *p = '\0';
+ break;
+ }
+ }
+ return path;
+
+#elif defined(__NetBSD__)
+ /* NetBSD uses "/dev/r[a-z]+[0-9][a-z]". */
+ char *path = xstrdup (os_dev);
+ if (strncmp ("/dev/r", path, sizeof("/dev/r") - 1) == 0 &&
+ (path[sizeof("/dev/r") - 1] >= 'a' && path[sizeof("/dev/r") - 1] <= 'z') &&
+ strncmp ("fd", path + sizeof("/dev/r") - 1, sizeof("fd") - 1) != 0) /* not a floppy device name */
+ {
+ char *p;
+ for (p = path + sizeof("/dev/r"); *p >= 'a' && *p <= 'z'; p++);
+ if (grub_isdigit(*p))
+ {
+ p++;
+ if ((*p >= 'a' && *p <= 'z') && (*(p+1) == '\0'))
+ {
+ /* path matches the required regular expression and
+ p points to its last character. */
+ int rawpart = -1;
+# ifdef HAVE_GETRAWPARTITION
+ rawpart = getrawpartition();
+# endif /* HAVE_GETRAWPARTITION */
+ if (rawpart >= 0)
+ *p = 'a' + rawpart;
+ }
+ }
+ }
+ return path;
+
+#else
+# warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly."
+ return xstrdup (os_dev);
+#endif
+}
+
+#if defined(__linux__) || defined(__CYGWIN__)
+static int
+device_is_wholedisk (const char *os_dev)
+{
+ int len = strlen (os_dev);
+
+ if (os_dev[len - 1] < '0' || os_dev[len - 1] > '9')
+ return 1;
+ return 0;
+}
+#endif
+
+#if defined(__NetBSD__)
+/* Try to determine whether a given device name corresponds to a whole disk.
+ This function should give in most cases a definite answer, but it may
+ actually give an approximate one in the following sense: if the return
+ value is 0 then the device name does not correspond to a whole disk. */
+static int
+device_is_wholedisk (const char *os_dev)
+{
+ int len = strlen (os_dev);
+ int rawpart = -1;
+
+# ifdef HAVE_GETRAWPARTITION
+ rawpart = getrawpartition();
+# endif /* HAVE_GETRAWPARTITION */
+ if (rawpart < 0)
+ return 1;
+ return (os_dev[len - 1] == ('a' + rawpart));
+}
+#endif /* defined(__NetBSD__) */
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+static int
+device_is_wholedisk (const char *os_dev)
+{
+ const char *p;
+
+ if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0)
+ return 0;
+
+ for (p = os_dev + sizeof ("/dev/") - 1; *p; ++p)
+ if (grub_isdigit (*p))
+ {
+ if (strchr (p, 's'))
+ return 0;
+ break;
+ }
+
+ return 1;
+}
+#endif /* defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
+
+static int
+find_system_device (const char *os_dev, struct stat *st, int convert, int add)
+{
+ unsigned int i;
+ char *os_disk;
+
+ if (convert)
+ os_disk = convert_system_partition_to_system_disk (os_dev, st);
+ else
+ os_disk = xstrdup (os_dev);
+ if (! os_disk)
+ return -1;
+
+ for (i = 0; i < ARRAY_SIZE (map); i++)
+ if (! map[i].device)
+ break;
+ else if (strcmp (map[i].device, os_disk) == 0)
+ {
+ free (os_disk);
+ return i;
+ }
+
+ if (!add)
+ return -1;
+
+ if (i == ARRAY_SIZE (map))
+ grub_util_error (_("device count exceeds limit"));
+
+ map[i].device = os_disk;
+ map[i].drive = xstrdup (os_disk);
+
+ return i;
+}
+
+int
+grub_util_biosdisk_is_present (const char *os_dev)
+{
+ struct stat st;
+
+ if (stat (os_dev, &st) < 0)
+ return 0;
+
+ return find_system_device (os_dev, &st, 1, 0) != -1;
+}
+
+char *
+grub_util_biosdisk_get_grub_dev (const char *os_dev)
+{
+ struct stat st;
+ int drive;
+
+ if (stat (os_dev, &st) < 0)
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", os_dev);
+ grub_util_info ("cannot stat `%s'", os_dev);
+ return 0;
+ }
+
+ drive = find_system_device (os_dev, &st, 1, 1);
+ if (drive < 0)
+ {
+ grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+ "no mapping exists for `%s'", os_dev);
+ grub_util_info ("no mapping exists for `%s'", os_dev);
+ return 0;
+ }
+
+ if (grub_strcmp (os_dev,
+ convert_system_partition_to_system_disk (os_dev, &st)) == 0)
+ return make_device_name (drive, -1, -1);
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__)
+ if (! S_ISCHR (st.st_mode))
+#else
+ if (! S_ISBLK (st.st_mode))
+#endif
+ return make_device_name (drive, -1, -1);
+
+#if defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+
+ /* Linux counts partitions uniformly, whether a BSD partition or a DOS
+ partition, so mapping them to GRUB devices is not trivial.
+ Here, get the start sector of a partition by HDIO_GETGEO, and
+ compare it with each partition GRUB recognizes.
+
+ Cygwin /dev/sdXN emulation uses Windows partition mapping. It
+ does not count the extended partition and missing primary
+ partitions. Use same method as on Linux here.
+
+ For NetBSD and FreeBSD, proceed as for Linux, except that the start
+ sector is obtained from the disk label. */
+ {
+ char *name, *partname;
+ grub_disk_t disk;
+ grub_disk_addr_t start;
+ auto int find_partition (grub_disk_t dsk,
+ const grub_partition_t partition);
+
+ int find_partition (grub_disk_t dsk __attribute__ ((unused)),
+ const grub_partition_t partition)
+ {
+ grub_disk_addr_t part_start = 0;
+ grub_util_info ("Partition %d starts from %lu",
+ partition->number, partition->start);
+
+ part_start = grub_partition_get_start (partition);
+
+ if (start == part_start)
+ {
+ partname = grub_partition_get_name (partition);
+ return 1;
+ }
+
+ return 0;
+ }
+
+ name = make_device_name (drive, -1, -1);
+
+# if !defined(HAVE_DIOCGDINFO)
+ if (MAJOR (st.st_rdev) == FLOPPY_MAJOR)
+ return name;
+# else /* defined(HAVE_DIOCGDINFO) */
+ /* Since os_dev and convert_system_partition_to_system_disk (os_dev) are
+ * different, we know that os_dev cannot be a floppy device. */
+# endif /* !defined(HAVE_DIOCGDINFO) */
+
+ start = find_partition_start (os_dev);
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ free (name);
+ return 0;
+ }
+
+ grub_util_info ("%s starts from %lu", os_dev, start);
+
+ if (start == 0 && device_is_wholedisk (os_dev))
+ return name;
+
+ grub_util_info ("opening the device %s", name);
+ disk = grub_disk_open (name);
+ free (name);
+
+ if (! disk)
+ {
+ /* We already know that the partition exists. Given that we already
+ checked the device map above, we can only get
+ GRUB_ERR_UNKNOWN_DEVICE at this point if the disk does not exist.
+ This can happen on Xen, where disk images in the host can be
+ assigned to devices that have partition-like names in the guest
+ but are really more like disks. */
+ if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
+ {
+ grub_util_warn
+ ("disk does not exist, so falling back to partition device %s",
+ os_dev);
+
+ drive = find_system_device (os_dev, &st, 0, 1);
+ if (drive < 0)
+ {
+ grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+ "no mapping exists for `%s'", os_dev);
+ return 0;
+ }
+
+ return make_device_name (drive, -1, -1);
+ }
+ else
+ return 0;
+ }
+
+ partname = NULL;
+ grub_partition_iterate (disk, find_partition);
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ grub_disk_close (disk);
+ return 0;
+ }
+
+ if (partname == NULL)
+ {
+ grub_disk_close (disk);
+ grub_error (GRUB_ERR_BAD_DEVICE,
+ "cannot find the partition of `%s'", os_dev);
+ return 0;
+ }
+
+ name = grub_xasprintf ("%s,%s", disk->name, partname);
+ free (partname);
+ return name;
+ }
+
+#elif defined(__GNU__)
+ /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
+ {
+ char *p;
+ int dos_part = -1;
+ int bsd_part = -1;
+
+ p = strrchr (os_dev, 's');
+ if (p)
+ {
+ long int n;
+ char *q;
+
+ p++;
+ n = strtol (p, &q, 10);
+ if (p != q && n != GRUB_LONG_MIN && n != GRUB_LONG_MAX)
+ {
+ dos_part = (int) n - 1;
+
+ if (*q >= 'a' && *q <= 'g')
+ bsd_part = *q - 'a';
+ }
+ }
+
+ return make_device_name (drive, dos_part, bsd_part);
+ }
+
+#else
+# warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
+ return make_device_name (drive, -1, -1);
+#endif
+}
+
+const char *
+grub_util_biosdisk_get_osdev (grub_disk_t disk)
+{
+ return map[disk->id].device;
+}
+
+int
+grub_util_biosdisk_is_floppy (grub_disk_t disk)
+{
+ struct stat st;
+ int fd;
+
+ fd = open (map[disk->id].device, O_RDONLY);
+ /* Shouldn't happen. */
+ if (fd == -1)
+ return 0;
+
+ /* Shouldn't happen either. */
+ if (fstat (fd, &st) < 0)
+ return 0;
+
+#if defined(__NetBSD__)
+ if (major(st.st_rdev) == RAW_FLOPPY_MAJOR)
+ return 1;
+#endif
+
+#if defined(FLOPPY_MAJOR)
+ if (major(st.st_rdev) == FLOPPY_MAJOR)
+#else
+ /* Some kernels (e.g. kFreeBSD) don't have a static major number
+ for floppies, but they still use a "fd[0-9]" pathname. */
+ if (map[disk->id].device[5] == 'f'
+ && map[disk->id].device[6] == 'd'
+ && map[disk->id].device[7] >= '0'
+ && map[disk->id].device[7] <= '9')
+#endif
+ return 1;
+
+ return 0;
+}
diff --git a/grub-core/kern/emu/hostfs.c b/grub-core/kern/emu/hostfs.c
new file mode 100644
index 0000000..501ad46
--- /dev/null
+++ b/grub-core/kern/emu/hostfs.c
@@ -0,0 +1,175 @@
+/* hostfs.c - Dummy filesystem to provide access to the hosts filesystem */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2007,2008,2009,2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * 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 _BSD_SOURCE
+#include <grub/fs.h>
+#include <grub/file.h>
+#include <grub/disk.h>
+#include <grub/misc.h>
+#include <grub/dl.h>
+#include <grub/util/misc.h>
+
+#include <dirent.h>
+#include <stdio.h>
+#include <errno.h>
+
+
+/* dirent.d_type is a BSD extension, not part of POSIX */
+#include <sys/stat.h>
+#include <string.h>
+
+static int
+is_dir (const char *path, const char *name)
+{
+ int len1 = strlen(path);
+ int len2 = strlen(name);
+
+ char pathname[len1 + 1 + len2 + 1 + 13];
+ strcpy (pathname, path);
+
+ /* Avoid UNC-path "//name" on Cygwin. */
+ if (len1 > 0 && pathname[len1 - 1] != '/')
+ strcat (pathname, "/");
+
+ strcat (pathname, name);
+
+ struct stat st;
+ if (stat (pathname, &st))
+ return 0;
+ return S_ISDIR (st.st_mode);
+}
+
+static grub_err_t
+grub_hostfs_dir (grub_device_t device, const char *path,
+ int (*hook) (const char *filename,
+ const struct grub_dirhook_info *info))
+{
+ DIR *dir;
+
+ /* Check if the disk is our dummy disk. */
+ if (grub_strcmp (device->disk->name, "host"))
+ return grub_error (GRUB_ERR_BAD_FS, "not a hostfs");
+
+ dir = opendir (path);
+ if (! dir)
+ return grub_error (GRUB_ERR_BAD_FILENAME,
+ "can't open the hostfs directory `%s'", path);
+
+ while (1)
+ {
+ struct dirent *de;
+ struct grub_dirhook_info info;
+ grub_memset (&info, 0, sizeof (info));
+
+ de = readdir (dir);
+ if (! de)
+ break;
+
+ info.dir = !! is_dir (path, de->d_name);
+ hook (de->d_name, &info);
+
+ }
+
+ closedir (dir);
+
+ return GRUB_ERR_NONE;
+}
+
+/* Open a file named NAME and initialize FILE. */
+static grub_err_t
+grub_hostfs_open (struct grub_file *file, const char *name)
+{
+ FILE *f;
+
+ f = fopen (name, "rb");
+ if (! f)
+ return grub_error (GRUB_ERR_BAD_FILENAME,
+ "can't open `%s'", name);
+ file->data = f;
+
+#ifdef __MINGW32__
+ file->size = grub_util_get_disk_size (name);
+#else
+ fseeko (f, 0, SEEK_END);
+ file->size = ftello (f);
+ fseeko (f, 0, SEEK_SET);
+#endif
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_ssize_t
+grub_hostfs_read (grub_file_t file, char *buf, grub_size_t len)
+{
+ FILE *f;
+
+ f = (FILE *) file->data;
+ if (fseeko (f, file->offset, SEEK_SET) != 0)
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "fseeko: %s", strerror (errno));
+ return -1;
+ }
+
+ unsigned int s = fread (buf, 1, len, f);
+ if (s != len)
+ grub_error (GRUB_ERR_FILE_READ_ERROR, "fread: %s", strerror (errno));
+
+ return (signed) s;
+}
+
+static grub_err_t
+grub_hostfs_close (grub_file_t file)
+{
+ FILE *f;
+
+ f = (FILE *) file->data;
+ fclose (f);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_hostfs_label (grub_device_t device __attribute ((unused)),
+ char **label __attribute ((unused)))
+{
+ *label = 0;
+ return GRUB_ERR_NONE;
+}
+
+static struct grub_fs grub_hostfs_fs =
+ {
+ .name = "hostfs",
+ .dir = grub_hostfs_dir,
+ .open = grub_hostfs_open,
+ .read = grub_hostfs_read,
+ .close = grub_hostfs_close,
+ .label = grub_hostfs_label,
+ .next = 0
+ };
+
+
+
+GRUB_MOD_INIT(hostfs)
+{
+ grub_fs_register (&grub_hostfs_fs);
+}
+
+GRUB_MOD_FINI(hostfs)
+{
+ grub_fs_unregister (&grub_hostfs_fs);
+}
diff --git a/grub-core/kern/emu/lite.c b/grub-core/kern/emu/lite.c
new file mode 100644
index 0000000..32e12a0
--- /dev/null
+++ b/grub-core/kern/emu/lite.c
@@ -0,0 +1,45 @@
+#include <config.h>
+#include <grub/emu/misc.h>
+
+#ifndef GRUB_MACHINE_EMU
+#error "This source is only meant for grub-emu platform"
+#endif
+
+#if defined(__i386__)
+#include "../i386/dl.c"
+#elif defined(__x86_64__)
+#include "../x86_64/dl.c"
+#elif defined(__sparc__)
+#include "../sparc64/dl.c"
+#elif defined(__mips__)
+#include "../mips/dl.c"
+#elif defined(__powerpc__)
+#include "../powerpc/dl.c"
+#else
+#error "No target cpu type is defined"
+#endif
+
+/* grub-emu-lite supports dynamic module loading, so it won't have any
+ embedded modules. */
+void
+grub_init_all (void)
+{
+ return;
+}
+
+void
+grub_fini_all (void)
+{
+ return;
+}
+
+void
+grub_emu_init (void)
+{
+ return;
+}
+
+void
+grub_emu_post_init (void)
+{
+}
diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c
new file mode 100644
index 0000000..0a76459
--- /dev/null
+++ b/grub-core/kern/emu/main.c
@@ -0,0 +1,259 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003,2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * 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/>.
+ */
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/setjmp.h>
+#include <grub/fs.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/time.h>
+#include <grub/emu/console.h>
+#include <grub/emu/misc.h>
+#include <grub/kernel.h>
+#include <grub/normal.h>
+#include <grub/emu/getroot.h>
+#include <grub/env.h>
+#include <grub/partition.h>
+#include <grub/i18n.h>
+
+#include "progname.h"
+
+#define ENABLE_RELOCATABLE 0
+
+/* Used for going back to the main function. */
+static jmp_buf main_env;
+
+/* Store the prefix specified by an argument. */
+static char *prefix = NULL;
+
+int grub_no_autoload;
+
+grub_addr_t
+grub_arch_modules_addr (void)
+{
+ return 0;
+}
+
+void
+grub_reboot (void)
+{
+ longjmp (main_env, 1);
+}
+
+void
+grub_machine_init (void)
+{
+}
+
+void
+grub_machine_set_prefix (void)
+{
+ grub_env_set ("prefix", prefix);
+ free (prefix);
+ prefix = 0;
+}
+
+void
+grub_machine_fini (void)
+{
+ grub_console_fini ();
+}
+
+
+
+static struct option options[] =
+ {
+ {"root-device", required_argument, 0, 'r'},
+ {"device-map", required_argument, 0, 'm'},
+ {"directory", required_argument, 0, 'd'},
+ {"hold", optional_argument, 0, 'H'},
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'V'},
+ {"verbose", no_argument, 0, 'v'},
+ { 0, 0, 0, 0 }
+ };
+
+static int
+usage (int status)
+{
+ if (status)
+ fprintf (stderr,
+ "Try `%s --help' for more information.\n", program_name);
+ else
+ printf (
+ "Usage: %s [OPTION]...\n"
+ "\n"
+ "GRUB emulator.\n"
+ "\n"
+ " -r, --root-device=DEV use DEV as the root device [default=guessed]\n"
+ " -m, --device-map=FILE use FILE as the device map [default=%s]\n"
+ " -d, --directory=DIR use GRUB files in the directory DIR [default=%s]\n"
+ " -v, --verbose print verbose messages\n"
+ " -H, --hold[=SECONDS] wait until a debugger will attach\n"
+ " -h, --help display this message and exit\n"
+ " -V, --version print version information and exit\n"
+ "\n"
+ "Report bugs to <%s>.\n", program_name, DEFAULT_DEVICE_MAP, DEFAULT_DIRECTORY, PACKAGE_BUGREPORT);
+ return status;
+}
+
+
+void grub_hostfs_init (void);
+void grub_hostfs_fini (void);
+void grub_host_init (void);
+void grub_host_fini (void);
+void grub_emu_init (void);
+
+int
+main (int argc, char *argv[])
+{
+ char *root_dev = 0;
+ char *dir = DEFAULT_DIRECTORY;
+ char *dev_map = DEFAULT_DEVICE_MAP;
+ volatile int hold = 0;
+ int opt;
+
+ set_program_name (argv[0]);
+
+ while ((opt = getopt_long (argc, argv, "r:d:m:vH:hV", options, 0)) != -1)
+ switch (opt)
+ {
+ case 'r':
+ root_dev = optarg;
+ break;
+ case 'd':
+ dir = optarg;
+ break;
+ case 'm':
+ dev_map = optarg;
+ break;
+ case 'v':
+ verbosity++;
+ break;
+ case 'H':
+ hold = (optarg ? atoi (optarg) : -1);
+ break;
+ case 'h':
+ return usage (0);
+ case 'V':
+ printf ("%s (%s) %s\n", program_name, PACKAGE_NAME, PACKAGE_VERSION);
+ return 0;
+ default:
+ return usage (1);
+ }
+
+ if (optind < argc)
+ {
+ fprintf (stderr, "Unknown extra argument `%s'.\n", argv[optind]);
+ return usage (1);
+ }
+
+ /* Wait until the ARGS.HOLD variable is cleared by an attached debugger. */
+ if (hold && verbosity > 0)
+ printf ("Run \"gdb %s %d\", and set ARGS.HOLD to zero.\n",
+ program_name, (int) getpid ());
+ while (hold)
+ {
+ if (hold > 0)
+ hold--;
+
+ sleep (1);
+ }
+
+ signal (SIGINT, SIG_IGN);
+ grub_emu_init ();
+ grub_console_init ();
+ grub_host_init ();
+
+ /* XXX: This is a bit unportable. */
+ grub_util_biosdisk_init (dev_map);
+
+ grub_init_all ();
+
+ grub_hostfs_init ();
+
+ grub_emu_post_init ();
+
+ /* Make sure that there is a root device. */
+ if (! root_dev)
+ {
+ char *device_name = grub_guess_root_device (dir);
+ if (! device_name)
+ grub_util_error ("cannot find a device for %s", dir);
+
+ root_dev = grub_util_get_grub_dev (device_name);
+ if (! root_dev)
+ {
+ grub_util_info ("guessing the root device failed, because of `%s'",
+ grub_errmsg);
+ grub_util_error ("cannot guess the root device. Specify the option `--root-device'");
+ }
+ }
+
+ if (strcmp (root_dev, "host") == 0)
+ dir = xstrdup (dir);
+ else
+ dir = grub_make_system_path_relative_to_its_root (dir);
+ prefix = xmalloc (strlen (root_dev) + 2 + strlen (dir) + 1);
+ sprintf (prefix, "(%s)%s", root_dev, dir);
+ free (dir);
+
+ /* Start GRUB! */
+ if (setjmp (main_env) == 0)
+ grub_main ();
+
+ grub_fini_all ();
+ grub_hostfs_fini ();
+ grub_host_fini ();
+
+ grub_machine_fini ();
+
+ return 0;
+}
+
+#ifdef __MINGW32__
+
+void
+grub_millisleep (grub_uint32_t ms)
+{
+ Sleep (ms);
+}
+
+#else
+
+void
+grub_millisleep (grub_uint32_t ms)
+{
+ struct timespec ts;
+
+ ts.tv_sec = ms / 1000;
+ ts.tv_nsec = (ms % 1000) * 1000000;
+ nanosleep (&ts, NULL);
+}
+
+#endif
diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c
new file mode 100644
index 0000000..44c40b0
--- /dev/null
+++ b/grub-core/kern/emu/misc.c
@@ -0,0 +1,537 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * 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/>.
+ */
+
+#include <config-util.h>
+#include <config.h>
+
+#include <errno.h>
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/env.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/i18n.h>
+#include <grub/time.h>
+#include <grub/emu/misc.h>
+
+#ifdef HAVE_DEVICE_MAPPER
+# include <libdevmapper.h>
+#endif
+
+#ifdef HAVE_LIBZFS
+# include <grub/util/libzfs.h>
+#endif
+
+#ifdef HAVE_LIBNVPAIR
+# include <grub/util/libnvpair.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_MOUNT_H
+# include <sys/mount.h>
+#endif
+
+#ifdef HAVE_SYS_MNTTAB_H
+# include <stdio.h> /* Needed by sys/mnttab.h. */
+# include <sys/mnttab.h>
+#endif
+
+#ifdef HAVE_SYS_MKDEV_H
+# include <sys/mkdev.h> /* makedev */
+#endif
+
+int verbosity;
+
+void
+grub_util_warn (const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf (stderr, _("%s: warn:"), program_name);
+ fprintf (stderr, " ");
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ fprintf (stderr, ".\n");
+ fflush (stderr);
+}
+
+void
+grub_util_info (const char *fmt, ...)
+{
+ if (verbosity > 0)
+ {
+ va_list ap;
+
+ fprintf (stderr, _("%s: info:"), program_name);
+ fprintf (stderr, " ");
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ fprintf (stderr, ".\n");
+ fflush (stderr);
+ }
+}
+
+void
+grub_util_error (const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf (stderr, _("%s: error:"), program_name);
+ fprintf (stderr, " ");
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ fprintf (stderr, ".\n");
+ exit (1);
+}
+
+void *
+xmalloc (grub_size_t size)
+{
+ void *p;
+
+ p = malloc (size);
+ if (! p)
+ grub_util_error ("out of memory");
+
+ return p;
+}
+
+void *
+xrealloc (void *ptr, grub_size_t size)
+{
+ ptr = realloc (ptr, size);
+ if (! ptr)
+ grub_util_error ("out of memory");
+
+ return ptr;
+}
+
+char *
+xstrdup (const char *str)
+{
+ size_t len;
+ char *newstr;
+
+ len = strlen (str);
+ newstr = (char *) xmalloc (len + 1);
+ memcpy (newstr, str, len + 1);
+
+ return newstr;
+}
+
+#ifndef HAVE_VASPRINTF
+
+int
+vasprintf (char **buf, const char *fmt, va_list ap)
+{
+ /* Should be large enough. */
+ *buf = xmalloc (512);
+
+ return vsnprintf (*buf, 512, fmt, ap);
+}
+
+#endif
+
+#ifndef HAVE_ASPRINTF
+
+int
+asprintf (char **buf, const char *fmt, ...)
+{
+ int status;
+ va_list ap;
+
+ va_start (ap, fmt);
+ status = vasprintf (buf, fmt, ap);
+ va_end (ap);
+
+ return status;
+}
+
+#endif
+
+char *
+xasprintf (const char *fmt, ...)
+{
+ va_list ap;
+ char *result;
+
+ va_start (ap, fmt);
+ if (vasprintf (&result, fmt, ap) < 0)
+ {
+ if (errno == ENOMEM)
+ grub_util_error ("out of memory");
+ return NULL;
+ }
+
+ return result;
+}
+
+void
+grub_exit (void)
+{
+ exit (1);
+}
+
+grub_uint64_t
+grub_get_time_ms (void)
+{
+ struct timeval tv;
+
+ gettimeofday (&tv, 0);
+
+ return (tv.tv_sec * 1000 + tv.tv_usec / 1000);
+}
+
+grub_uint32_t
+grub_get_rtc (void)
+{
+ struct timeval tv;
+
+ gettimeofday (&tv, 0);
+
+ return (tv.tv_sec * GRUB_TICKS_PER_SECOND
+ + (((tv.tv_sec % GRUB_TICKS_PER_SECOND) * 1000000 + tv.tv_usec)
+ * GRUB_TICKS_PER_SECOND / 1000000));
+}
+
+char *
+canonicalize_file_name (const char *path)
+{
+ char *ret;
+#ifdef PATH_MAX
+ ret = xmalloc (PATH_MAX);
+ if (!realpath (path, ret))
+ return NULL;
+#else
+ ret = realpath (path, NULL);
+#endif
+ return ret;
+}
+
+#ifdef __CYGWIN__
+/* Convert POSIX path to Win32 path,
+ remove drive letter, replace backslashes. */
+static char *
+get_win32_path (const char *path)
+{
+ char winpath[PATH_MAX];
+ if (cygwin_conv_path (CCP_POSIX_TO_WIN_A, path, winpath, sizeof(winpath)))
+ grub_util_error ("cygwin_conv_path() failed");
+
+ int len = strlen (winpath);
+ int offs = (len > 2 && winpath[1] == ':' ? 2 : 0);
+
+ int i;
+ for (i = offs; i < len; i++)
+ if (winpath[i] == '\\')
+ winpath[i] = '/';
+ return xstrdup (winpath + offs);
+}
+#endif
+
+#ifdef HAVE_LIBZFS
+static libzfs_handle_t *__libzfs_handle;
+
+static void
+fini_libzfs (void)
+{
+ libzfs_fini (__libzfs_handle);
+}
+
+libzfs_handle_t *
+grub_get_libzfs_handle (void)
+{
+ if (! __libzfs_handle)
+ {
+ __libzfs_handle = libzfs_init ();
+
+ if (__libzfs_handle)
+ atexit (fini_libzfs);
+ }
+
+ return __libzfs_handle;
+}
+#endif /* HAVE_LIBZFS */
+
+#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
+/* ZFS has similar problems to those of btrfs (see above). */
+void
+grub_find_zpool_from_dir (const char *dir, char **poolname, char **poolfs)
+{
+ char *slash;
+
+ *poolname = *poolfs = NULL;
+
+#if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) && defined(HAVE_STRUCT_STATFS_F_MNTFROMNAME)
+ /* FreeBSD and GNU/kFreeBSD. */
+ {
+ struct statfs mnt;
+
+ if (statfs (dir, &mnt) != 0)
+ return;
+
+ if (strcmp (mnt.f_fstypename, "zfs") != 0)
+ return;
+
+ *poolname = xstrdup (mnt.f_mntfromname);
+ }
+#elif defined(HAVE_GETEXTMNTENT)
+ /* Solaris. */
+ {
+ struct stat st;
+ struct extmnttab mnt;
+
+ if (stat (dir, &st) != 0)
+ return;
+
+ FILE *mnttab = fopen ("/etc/mnttab", "r");
+ if (! mnttab)
+ return;
+
+ while (getextmntent (mnttab, &mnt, sizeof (mnt)) == 0)
+ {
+ if (makedev (mnt.mnt_major, mnt.mnt_minor) == st.st_dev
+ && !strcmp (mnt.mnt_fstype, "zfs"))
+ {
+ *poolname = xstrdup (mnt.mnt_special);
+ break;
+ }
+ }
+
+ fclose (mnttab);
+ }
+#endif
+
+ if (! *poolname)
+ return;
+
+ slash = strchr (*poolname, '/');
+ if (slash)
+ {
+ *slash = '\0';
+ *poolfs = xstrdup (slash + 1);
+ }
+ else
+ *poolfs = xstrdup ("");
+}
+#endif
+
+/* This function never prints trailing slashes (so that its output
+ can be appended a slash unconditionally). */
+char *
+grub_make_system_path_relative_to_its_root (const char *path)
+{
+ struct stat st;
+ char *p, *buf, *buf2, *buf3, *ret;
+ uintptr_t offset = 0;
+ dev_t num;
+ size_t len;
+
+#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
+ char *poolfs = NULL;
+#endif
+
+ /* canonicalize. */
+ p = canonicalize_file_name (path);
+ if (p == NULL)
+ grub_util_error ("failed to get canonical path of %s", path);
+
+#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
+ /* For ZFS sub-pool filesystems, could be extended to others (btrfs?). */
+ {
+ char *dummy;
+ grub_find_zpool_from_dir (p, &dummy, &poolfs);
+ }
+#endif
+
+ len = strlen (p) + 1;
+ buf = xstrdup (p);
+ free (p);
+
+ if (stat (buf, &st) < 0)
+ grub_util_error ("cannot stat %s: %s", buf, strerror (errno));
+
+ buf2 = xstrdup (buf);
+ num = st.st_dev;
+
+ /* This loop sets offset to the number of chars of the root
+ directory we're inspecting. */
+ while (1)
+ {
+ p = strrchr (buf, '/');
+ 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;
+
+ if (stat (buf, &st) < 0)
+ grub_util_error ("cannot stat %s: %s", buf, strerror (errno));
+
+ /* buf is another filesystem; we found it. */
+ if (st.st_dev != num)
+ {
+ /* 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)
+ {
+ free (buf);
+#ifdef __linux__
+ {
+ char *bind;
+ grub_free (grub_find_root_device_from_mountinfo (buf2, &bind));
+ if (bind && bind[0] && bind[1])
+ {
+ buf3 = bind;
+ goto parsedir;
+ }
+ grub_free (bind);
+ }
+#endif
+ free (buf2);
+#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
+ if (poolfs)
+ return xasprintf ("/%s/@", poolfs);
+#endif
+ return xstrdup ("");
+ }
+ else
+ break;
+ }
+
+ offset = p - buf;
+ /* offset == 1 means root directory. */
+ if (offset == 1)
+ {
+ /* Include leading slash. */
+ offset = 0;
+ break;
+ }
+ }
+ free (buf);
+ buf3 = xstrdup (buf2 + offset);
+ buf2[offset] = 0;
+#ifdef __linux__
+ {
+ char *bind;
+ grub_free (grub_find_root_device_from_mountinfo (buf2, &bind));
+ if (bind && bind[0] && bind[1])
+ {
+ char *temp = buf3;
+ buf3 = grub_xasprintf ("%s%s%s", bind, buf3[0] == '/' ?"":"/", buf3);
+ grub_free (temp);
+ }
+ grub_free (bind);
+ }
+#endif
+
+ free (buf2);
+
+#ifdef __CYGWIN__
+ if (st.st_dev != (DEV_CYGDRIVE_MAJOR << 16))
+ {
+ /* Reached some mount point not below /cygdrive.
+ GRUB does not know Cygwin's emulated mounts,
+ convert to Win32 path. */
+ grub_util_info ("Cygwin path = %s\n", buf3);
+ char * temp = get_win32_path (buf3);
+ free (buf3);
+ buf3 = temp;
+ }
+#endif
+
+ parsedir:
+ /* Remove trailing slashes, return empty string if root directory. */
+ len = strlen (buf3);
+ while (len > 0 && buf3[len - 1] == '/')
+ {
+ buf3[len - 1] = '\0';
+ len--;
+ }
+
+#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
+ if (poolfs)
+ {
+ ret = xasprintf ("/%s/@%s", poolfs, buf3);
+ free (buf3);
+ }
+ else
+#endif
+ ret = buf3;
+
+ return ret;
+}
+
+#ifdef HAVE_DEVICE_MAPPER
+static void device_mapper_null_log (int level __attribute__ ((unused)),
+ const char *file __attribute__ ((unused)),
+ int line __attribute__ ((unused)),
+ int dm_errno __attribute__ ((unused)),
+ const char *f __attribute__ ((unused)),
+ ...)
+{
+}
+
+int
+grub_device_mapper_supported (void)
+{
+ static int supported = -1;
+
+ if (supported == -1)
+ {
+ struct dm_task *dmt;
+
+ /* Suppress annoying log messages. */
+ dm_log_with_errno_init (&device_mapper_null_log);
+
+ dmt = dm_task_create (DM_DEVICE_VERSION);
+ supported = (dmt != NULL);
+ if (dmt)
+ dm_task_destroy (dmt);
+
+ /* Restore the original logger. */
+ dm_log_with_errno_init (NULL);
+ }
+
+ return supported;
+}
+#endif /* HAVE_DEVICE_MAPPER */
diff --git a/grub-core/kern/emu/mm.c b/grub-core/kern/emu/mm.c
new file mode 100644
index 0000000..6a70efb
--- /dev/null
+++ b/grub-core/kern/emu/mm.c
@@ -0,0 +1,87 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * 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/>.
+ */
+
+#include <config-util.h>
+
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <stdlib.h>
+#include <string.h>
+
+void *
+grub_malloc (grub_size_t size)
+{
+ void *ret;
+ ret = malloc (size);
+ if (!ret)
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+ return ret;
+}
+
+void *
+grub_zalloc (grub_size_t size)
+{
+ void *ret;
+
+ ret = grub_malloc (size);
+ if (!ret)
+ return NULL;
+ memset (ret, 0, size);
+ return ret;
+}
+
+void
+grub_free (void *ptr)
+{
+ free (ptr);
+}
+
+void *
+grub_realloc (void *ptr, grub_size_t size)
+{
+ void *ret;
+ ret = realloc (ptr, size);
+ if (!ret)
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+ return ret;
+}
+
+void *
+grub_memalign (grub_size_t align, grub_size_t size)
+{
+ void *p;
+
+#if defined(HAVE_POSIX_MEMALIGN)
+ if (align < sizeof (void *))
+ align = sizeof (void *);
+ if (posix_memalign (&p, align, size) != 0)
+ p = 0;
+#elif defined(HAVE_MEMALIGN)
+ p = memalign (align, size);
+#else
+ (void) align;
+ (void) size;
+ grub_util_error ("grub_memalign is not supported");
+#endif
+
+ if (!p)
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+
+ return p;
+}
diff --git a/grub-core/kern/emu/time.c b/grub-core/kern/emu/time.c
new file mode 100644
index 0000000..5da8092
--- /dev/null
+++ b/grub-core/kern/emu/time.c
@@ -0,0 +1,46 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * 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/>.
+ */
+
+#include <grub/datetime.h>
+#include <time.h>
+
+grub_err_t
+grub_get_datetime (struct grub_datetime *datetime)
+{
+ struct tm *mytm;
+ time_t mytime;
+
+ mytime = time (&mytime);
+ mytm = gmtime (&mytime);
+
+ datetime->year = mytm->tm_year + 1900;
+ datetime->month = mytm->tm_mon + 1;
+ datetime->day = mytm->tm_mday;
+ datetime->hour = mytm->tm_hour;
+ datetime->minute = mytm->tm_min;
+ datetime->second = mytm->tm_sec;
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_set_datetime (struct grub_datetime *datetime __attribute__ ((unused)))
+{
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "no clock setting routine available");
+}