summaryrefslogtreecommitdiffstats
path: root/grub-core/osdep/unix
diff options
context:
space:
mode:
Diffstat (limited to 'grub-core/osdep/unix')
-rw-r--r--grub-core/osdep/unix/compress.c41
-rw-r--r--grub-core/osdep/unix/config.c139
-rw-r--r--grub-core/osdep/unix/cputime.c22
-rw-r--r--grub-core/osdep/unix/dl.c61
-rw-r--r--grub-core/osdep/unix/emuconsole.c184
-rw-r--r--grub-core/osdep/unix/exec.c245
-rw-r--r--grub-core/osdep/unix/getroot.c787
-rw-r--r--grub-core/osdep/unix/hostdisk.c319
-rw-r--r--grub-core/osdep/unix/password.c75
-rw-r--r--grub-core/osdep/unix/platform.c241
-rw-r--r--grub-core/osdep/unix/random.c48
-rw-r--r--grub-core/osdep/unix/relpath.c151
-rw-r--r--grub-core/osdep/unix/sleep.c30
13 files changed, 2343 insertions, 0 deletions
diff --git a/grub-core/osdep/unix/compress.c b/grub-core/osdep/unix/compress.c
new file mode 100644
index 0000000..dee5620
--- /dev/null
+++ b/grub-core/osdep/unix/compress.c
@@ -0,0 +1,41 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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/emu/exec.h>
+#include <grub/util/install.h>
+
+int
+grub_install_compress_gzip (const char *src, const char *dest)
+{
+ return grub_util_exec_redirect ((const char * []) { "gzip", "--best",
+ "--stdout", NULL }, src, dest);
+}
+
+int
+grub_install_compress_xz (const char *src, const char *dest)
+{
+ return grub_util_exec_redirect ((const char * []) { "xz",
+ "--lzma2=dict=128KiB", "--check=none", "--stdout", NULL }, src, dest);
+}
+
+int
+grub_install_compress_lzop (const char *src, const char *dest)
+{
+ return grub_util_exec_redirect ((const char * []) { "lzop", "-9", "-c",
+ NULL }, src, dest);
+}
diff --git a/grub-core/osdep/unix/config.c b/grub-core/osdep/unix/config.c
new file mode 100644
index 0000000..7d63251
--- /dev/null
+++ b/grub-core/osdep/unix/config.c
@@ -0,0 +1,139 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2004,2006,2007,2008,2009,2010,2011,2012,2013 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>
+
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/exec.h>
+#include <grub/emu/config.h>
+#include <grub/util/install.h>
+#include <grub/util/misc.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <stdlib.h>
+
+const char *
+grub_util_get_config_filename (void)
+{
+ static char *value = NULL;
+ if (!value)
+ value = grub_util_path_concat (3, GRUB_SYSCONFDIR,
+ "default", "grub");
+ return value;
+}
+
+const char *
+grub_util_get_pkgdatadir (void)
+{
+ const char *ret = getenv ("pkgdatadir");
+ if (ret)
+ return ret;
+ return GRUB_DATADIR "/" PACKAGE;
+}
+
+const char *
+grub_util_get_pkglibdir (void)
+{
+ return GRUB_LIBDIR "/" PACKAGE;
+}
+
+const char *
+grub_util_get_localedir (void)
+{
+ return LOCALEDIR;
+}
+
+void
+grub_util_load_config (struct grub_util_config *cfg)
+{
+ pid_t pid;
+ const char *argv[4];
+ char *script, *ptr;
+ const char *cfgfile, *iptr;
+ FILE *f = NULL;
+ int fd;
+ const char *v;
+
+ memset (cfg, 0, sizeof (*cfg));
+
+ v = getenv ("GRUB_ENABLE_CRYPTODISK");
+ if (v && v[0] == 'y' && v[1] == '\0')
+ cfg->is_cryptodisk_enabled = 1;
+
+ v = getenv ("GRUB_DISTRIBUTOR");
+ if (v)
+ cfg->grub_distributor = xstrdup (v);
+
+ cfgfile = grub_util_get_config_filename ();
+ if (!grub_util_is_regular (cfgfile))
+ return;
+
+ argv[0] = "sh";
+ argv[1] = "-c";
+
+ script = xcalloc (4, strlen (cfgfile) + 300);
+
+ ptr = script;
+ memcpy (ptr, ". '", 3);
+ ptr += 3;
+ for (iptr = cfgfile; *iptr; iptr++)
+ {
+ if (*iptr == '\\')
+ {
+ memcpy (ptr, "'\\''", 4);
+ ptr += 4;
+ continue;
+ }
+ *ptr++ = *iptr;
+ }
+
+ strcpy (ptr, "'; printf \"GRUB_ENABLE_CRYPTODISK=%s\\nGRUB_DISTRIBUTOR=%s\\n\" "
+ "\"$GRUB_ENABLE_CRYPTODISK\" \"$GRUB_DISTRIBUTOR\"");
+
+ argv[2] = script;
+ argv[3] = '\0';
+
+ pid = grub_util_exec_pipe (argv, &fd);
+ if (pid)
+ f = fdopen (fd, "r");
+ if (f)
+ {
+ grub_util_parse_config (f, cfg, 1);
+ fclose (f);
+ }
+ if (pid)
+ {
+ close (fd);
+ waitpid (pid, NULL, 0);
+ }
+ if (f)
+ return;
+
+ f = grub_util_fopen (cfgfile, "r");
+ if (f)
+ {
+ grub_util_parse_config (f, cfg, 0);
+ fclose (f);
+ }
+ else
+ grub_util_warn (_("cannot open configuration file `%s': %s"),
+ cfgfile, strerror (errno));
+}
diff --git a/grub-core/osdep/unix/cputime.c b/grub-core/osdep/unix/cputime.c
new file mode 100644
index 0000000..cff359a
--- /dev/null
+++ b/grub-core/osdep/unix/cputime.c
@@ -0,0 +1,22 @@
+#include <config.h>
+#include <config-util.h>
+
+#include <sys/times.h>
+#include <unistd.h>
+#include <grub/emu/misc.h>
+
+grub_uint64_t
+grub_util_get_cpu_time_ms (void)
+{
+ struct tms tm;
+ static long sc_clk_tck;
+ if (!sc_clk_tck)
+ {
+ sc_clk_tck = sysconf(_SC_CLK_TCK);
+ if (sc_clk_tck <= 0)
+ sc_clk_tck = 1000;
+ }
+
+ times (&tm);
+ return (tm.tms_utime * 1000ULL) / sc_clk_tck;
+}
diff --git a/grub-core/osdep/unix/dl.c b/grub-core/osdep/unix/dl.c
new file mode 100644
index 0000000..562b101
--- /dev/null
+++ b/grub-core/osdep/unix/dl.c
@@ -0,0 +1,61 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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>
+
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <string.h>
+
+void *
+grub_osdep_dl_memalign (grub_size_t align, grub_size_t size)
+{
+ void *ret;
+ if (align < 8192 * 16)
+ align = 8192 * 16;
+ size = ALIGN_UP (size, 8192 * 16);
+
+#if defined(HAVE_POSIX_MEMALIGN)
+ if (posix_memalign (&ret, align, size) != 0)
+ ret = 0;
+#elif defined(HAVE_MEMALIGN)
+ ret = memalign (align, size);
+#else
+#error "Complete this"
+#endif
+
+ if (!ret)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+ return NULL;
+ }
+
+ mprotect (ret, size, PROT_READ | PROT_WRITE | PROT_EXEC);
+ return ret;
+}
+
+void
+grub_dl_osdep_dl_free (void *ptr)
+{
+ if (ptr)
+ free (ptr);
+}
diff --git a/grub-core/osdep/unix/emuconsole.c b/grub-core/osdep/unix/emuconsole.c
new file mode 100644
index 0000000..7308798
--- /dev/null
+++ b/grub-core/osdep/unix/emuconsole.c
@@ -0,0 +1,184 @@
+/* console.c -- console for GRUB. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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>
+
+#include <grub/term.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/time.h>
+#include <grub/terminfo.h>
+#include <grub/dl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <langinfo.h>
+
+#include <grub/emu/console.h>
+
+extern struct grub_terminfo_output_state grub_console_terminfo_output;
+static int original_fl;
+static int saved_orig;
+static struct termios orig_tty;
+static struct termios new_tty;
+
+static void
+put (struct grub_term_output *term __attribute__ ((unused)), const int c)
+{
+ char chr = c;
+ ssize_t actual;
+
+ actual = write (STDOUT_FILENO, &chr, 1);
+ if (actual < 1)
+ {
+ /* We cannot do anything about this, but some systems require us to at
+ least pretend to check the result. */
+ }
+}
+
+static int
+readkey (struct grub_term_input *term __attribute__ ((unused)))
+{
+ grub_uint8_t c;
+ ssize_t actual;
+
+ actual = read (STDIN_FILENO, &c, 1);
+ if (actual > 0)
+ return c;
+ return -1;
+}
+
+static grub_err_t
+grub_console_init_input (struct grub_term_input *term)
+{
+ if (!saved_orig)
+ {
+ original_fl = fcntl (STDIN_FILENO, F_GETFL);
+ fcntl (STDIN_FILENO, F_SETFL, original_fl | O_NONBLOCK);
+ }
+
+ saved_orig = 1;
+
+ tcgetattr(STDIN_FILENO, &orig_tty);
+ new_tty = orig_tty;
+ new_tty.c_lflag &= ~(ICANON | ECHO);
+ new_tty.c_cc[VMIN] = 1;
+ tcsetattr(STDIN_FILENO, TCSANOW, &new_tty);
+
+ return grub_terminfo_input_init (term);
+}
+
+static grub_err_t
+grub_console_fini_input (struct grub_term_input *term
+ __attribute__ ((unused)))
+{
+ fcntl (STDIN_FILENO, F_SETFL, original_fl);
+ tcsetattr(STDIN_FILENO, TCSANOW, &orig_tty);
+ saved_orig = 0;
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_console_init_output (struct grub_term_output *term)
+{
+ struct winsize size;
+ if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &size) >= 0)
+ {
+ grub_console_terminfo_output.size.x = size.ws_col;
+ grub_console_terminfo_output.size.y = size.ws_row;
+ }
+ else
+ {
+ grub_console_terminfo_output.size.x = 80;
+ grub_console_terminfo_output.size.y = 24;
+ }
+
+ grub_terminfo_output_init (term);
+
+ return 0;
+}
+
+
+
+struct grub_terminfo_input_state grub_console_terminfo_input =
+ {
+ .readkey = readkey
+ };
+
+struct grub_terminfo_output_state grub_console_terminfo_output =
+ {
+ .put = put,
+ .size = { 80, 24 }
+ };
+
+static struct grub_term_input grub_console_term_input =
+ {
+ .name = "console",
+ .init = grub_console_init_input,
+ .fini = grub_console_fini_input,
+ .getkey = grub_terminfo_getkey,
+ .data = &grub_console_terminfo_input
+ };
+
+static struct grub_term_output grub_console_term_output =
+ {
+ .name = "console",
+ .init = grub_console_init_output,
+ .putchar = grub_terminfo_putchar,
+ .getxy = grub_terminfo_getxy,
+ .getwh = grub_terminfo_getwh,
+ .gotoxy = grub_terminfo_gotoxy,
+ .cls = grub_terminfo_cls,
+ .setcolorstate = grub_terminfo_setcolorstate,
+ .setcursor = grub_terminfo_setcursor,
+ .data = &grub_console_terminfo_output,
+ .progress_update_divisor = GRUB_PROGRESS_FAST
+ };
+
+void
+grub_console_init (void)
+{
+ const char *cs = nl_langinfo (CODESET);
+ if (cs && grub_strcasecmp (cs, "UTF-8"))
+ grub_console_term_output.flags = GRUB_TERM_CODE_TYPE_UTF8_LOGICAL;
+ else
+ grub_console_term_output.flags = GRUB_TERM_CODE_TYPE_ASCII;
+ grub_term_register_input ("console", &grub_console_term_input);
+ grub_term_register_output ("console", &grub_console_term_output);
+ grub_terminfo_init ();
+ grub_terminfo_output_register (&grub_console_term_output, "vt100-color");
+}
+
+void
+grub_console_fini (void)
+{
+ if (saved_orig)
+ {
+ fcntl (STDIN_FILENO, F_SETFL, original_fl);
+ tcsetattr(STDIN_FILENO, TCSANOW, &orig_tty);
+ }
+ saved_orig = 0;
+}
diff --git a/grub-core/osdep/unix/exec.c b/grub-core/osdep/unix/exec.c
new file mode 100644
index 0000000..e8db920
--- /dev/null
+++ b/grub-core/osdep/unix/exec.c
@@ -0,0 +1,245 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009,2010,2011 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 <grub/misc.h>
+#include <unistd.h>
+#include <grub/emu/exec.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/getroot.h>
+#include <grub/util/misc.h>
+#include <grub/disk.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/wait.h>
+
+int
+grub_util_exec_redirect_all (const char *const *argv, const char *stdin_file,
+ const char *stdout_file, const char *stderr_file)
+{
+ pid_t pid;
+ int status = -1;
+ char *str, *pstr;
+ const char *const *ptr;
+ grub_size_t strl = 0;
+ for (ptr = argv; *ptr; ptr++)
+ strl += grub_strlen (*ptr) + 1;
+ if (stdin_file)
+ strl += grub_strlen (stdin_file) + 2;
+ if (stdout_file)
+ strl += grub_strlen (stdout_file) + 2;
+ if (stderr_file)
+ strl += grub_strlen (stderr_file) + 3;
+
+ pstr = str = xmalloc (strl);
+ for (ptr = argv; *ptr; ptr++)
+ {
+ pstr = grub_stpcpy (pstr, *ptr);
+ *pstr++ = ' ';
+ }
+ if (stdin_file)
+ {
+ *pstr++ = '<';
+ pstr = grub_stpcpy (pstr, stdin_file);
+ *pstr++ = ' ';
+ }
+ if (stdout_file)
+ {
+ *pstr++ = '>';
+ pstr = grub_stpcpy (pstr, stdout_file);
+ *pstr++ = ' ';
+ }
+ if (stderr_file)
+ {
+ *pstr++ = '2';
+ *pstr++ = '>';
+ pstr = grub_stpcpy (pstr, stderr_file);
+ pstr++;
+ }
+ *--pstr = '\0';
+
+ grub_util_info ("executing %s", str);
+ grub_free (str);
+
+ pid = fork ();
+ if (pid < 0)
+ grub_util_error (_("Unable to fork: %s"), strerror (errno));
+ else if (pid == 0)
+ {
+ int fd;
+ /* Child. */
+
+ /* Close fd's. */
+#ifdef GRUB_UTIL
+ grub_util_devmapper_cleanup ();
+ grub_diskfilter_fini ();
+#endif
+
+ if (stdin_file)
+ {
+ fd = open (stdin_file, O_RDONLY);
+ if (fd < 0)
+ _exit (127);
+ dup2 (fd, STDIN_FILENO);
+ close (fd);
+ }
+
+ if (stdout_file)
+ {
+ fd = open (stdout_file, O_WRONLY | O_CREAT, 0700);
+ if (fd < 0)
+ _exit (127);
+ dup2 (fd, STDOUT_FILENO);
+ close (fd);
+ }
+
+ if (stderr_file)
+ {
+ fd = open (stderr_file, O_WRONLY | O_CREAT, 0700);
+ if (fd < 0)
+ _exit (127);
+ dup2 (fd, STDERR_FILENO);
+ close (fd);
+ }
+
+ /* Ensure child is not localised. */
+ setenv ("LC_ALL", "C", 1);
+
+ execvp ((char *) argv[0], (char **) argv);
+ _exit (127);
+ }
+ waitpid (pid, &status, 0);
+ if (!WIFEXITED (status))
+ return -1;
+ return WEXITSTATUS (status);
+}
+
+int
+grub_util_exec (const char *const *argv)
+{
+ return grub_util_exec_redirect_all (argv, NULL, NULL, NULL);
+}
+
+int
+grub_util_exec_redirect (const char *const *argv, const char *stdin_file,
+ const char *stdout_file)
+{
+ return grub_util_exec_redirect_all (argv, stdin_file, stdout_file, NULL);
+}
+
+int
+grub_util_exec_redirect_null (const char *const *argv)
+{
+ return grub_util_exec_redirect_all (argv, "/dev/null", "/dev/null", NULL);
+}
+
+pid_t
+grub_util_exec_pipe (const char *const *argv, int *fd)
+{
+ int pipe_fd[2];
+ pid_t pid;
+
+ *fd = 0;
+
+ if (pipe (pipe_fd) < 0)
+ {
+ grub_util_warn (_("Unable to create pipe: %s"),
+ strerror (errno));
+ return 0;
+ }
+ pid = fork ();
+ if (pid < 0)
+ grub_util_error (_("Unable to fork: %s"), strerror (errno));
+ else if (pid == 0)
+ {
+ /* Child. */
+
+ /* Close fd's. */
+#ifdef GRUB_UTIL
+ grub_util_devmapper_cleanup ();
+ grub_diskfilter_fini ();
+#endif
+
+ /* Ensure child is not localised. */
+ setenv ("LC_ALL", "C", 1);
+
+ close (pipe_fd[0]);
+ dup2 (pipe_fd[1], STDOUT_FILENO);
+ close (pipe_fd[1]);
+
+ execvp ((char *) argv[0], (char **) argv);
+ _exit (127);
+ }
+ else
+ {
+ close (pipe_fd[1]);
+ *fd = pipe_fd[0];
+ return pid;
+ }
+}
+
+pid_t
+grub_util_exec_pipe_stderr (const char *const *argv, int *fd)
+{
+ int pipe_fd[2];
+ pid_t pid;
+
+ *fd = 0;
+
+ if (pipe (pipe_fd) < 0)
+ {
+ grub_util_warn (_("Unable to create pipe: %s"),
+ strerror (errno));
+ return 0;
+ }
+ pid = fork ();
+ if (pid < 0)
+ grub_util_error (_("Unable to fork: %s"), strerror (errno));
+ else if (pid == 0)
+ {
+ /* Child. */
+
+ /* Close fd's. */
+#ifdef GRUB_UTIL
+ grub_util_devmapper_cleanup ();
+ grub_diskfilter_fini ();
+#endif
+
+ /* Ensure child is not localised. */
+ setenv ("LC_ALL", "C", 1);
+
+ close (pipe_fd[0]);
+ dup2 (pipe_fd[1], STDOUT_FILENO);
+ dup2 (pipe_fd[1], STDERR_FILENO);
+ close (pipe_fd[1]);
+
+ execvp ((char *) argv[0], (char **) argv);
+ _exit (127);
+ }
+ else
+ {
+ close (pipe_fd[1]);
+ *fd = pipe_fd[0];
+ return pid;
+ }
+}
diff --git a/grub-core/osdep/unix/getroot.c b/grub-core/osdep/unix/getroot.c
new file mode 100644
index 0000000..46d7116
--- /dev/null
+++ b/grub-core/osdep/unix/getroot.c
@@ -0,0 +1,787 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009,2010,2011 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>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <grub/util/misc.h>
+#include <grub/emu/exec.h>
+
+#include <grub/cryptodisk.h>
+#include <grub/i18n.h>
+
+#if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__AROS__) && !defined (__HAIKU__)
+
+#ifdef __linux__
+#include <sys/ioctl.h> /* ioctl */
+#include <sys/mount.h>
+#ifndef FLOPPY_MAJOR
+# define FLOPPY_MAJOR 2
+#endif /* ! FLOPPY_MAJOR */
+#endif
+
+#include <sys/types.h>
+#if defined(MAJOR_IN_MKDEV)
+#include <sys/mkdev.h>
+#elif defined(MAJOR_IN_SYSMACROS)
+#include <sys/sysmacros.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>
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+# define FLOPPY_MAJOR 2
+#endif
+
+#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
+#include <sys/mount.h>
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+# include <sys/ioctl.h>
+# include <sys/disklabel.h> /* struct disklabel */
+# include <sys/disk.h> /* struct dkwedge_info */
+#include <sys/param.h>
+#include <sys/mount.h>
+#endif /* defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+# ifdef HAVE_GETRAWPARTITION
+# include <util.h> /* getrawpartition */
+# endif /* HAVE_GETRAWPARTITION */
+#if defined(__NetBSD__)
+# include <sys/fdio.h>
+#endif
+# 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__) */
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#define LVM_DEV_MAPPER_STRING "/dev/linux_lvm/"
+#else
+#define LVM_DEV_MAPPER_STRING "/dev/mapper/"
+#endif
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) && defined(HAVE_STRUCT_STATFS_F_MNTFROMNAME)
+#include <sys/param.h>
+#include <sys/mount.h>
+#endif
+
+#include "save-cwd.h"
+
+#if !defined (__GNU__)
+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;
+}
+
+char **
+grub_util_find_root_devices_from_poolname (char *poolname)
+{
+ char **devices = 0;
+ size_t ndevices = 0;
+ size_t devices_allocated = 0;
+
+#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
+ zpool_handle_t *zpool;
+ libzfs_handle_t *libzfs;
+ nvlist_t *config, *vdev_tree;
+ nvlist_t **children;
+ unsigned int nvlist_count;
+ unsigned int i;
+ char *device = 0;
+
+ 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)
+ {
+#ifdef __sun__
+ if (grub_memcmp (device, "/dev/dsk/", sizeof ("/dev/dsk/") - 1)
+ == 0)
+ device = xasprintf ("/dev/rdsk/%s",
+ device + sizeof ("/dev/dsk/") - 1);
+ else if (grub_memcmp (device, "/devices", sizeof ("/devices") - 1)
+ == 0
+ && grub_memcmp (device + strlen (device) - 4,
+ ",raw", 4) != 0)
+ device = xasprintf ("%s,raw", device);
+ else
+#endif
+ device = xstrdup (device);
+ if (ndevices >= devices_allocated)
+ {
+ devices_allocated = 2 * (devices_allocated + 8);
+ devices = xrealloc (devices, sizeof (devices[0])
+ * devices_allocated);
+ }
+ devices[ndevices++] = device;
+ }
+
+ device = NULL;
+ }
+
+ zpool_close (zpool);
+#else
+ FILE *fp;
+ int ret;
+ char *line;
+ size_t len;
+ int st;
+
+ char name[PATH_MAX + 1], state[257], readlen[257], writelen[257];
+ char cksum[257], notes[257];
+ unsigned int dummy;
+ const char *argv[4];
+ pid_t pid;
+ int fd;
+
+ argv[0] = "zpool";
+ argv[1] = "status";
+ argv[2] = poolname;
+ argv[3] = NULL;
+
+ pid = grub_util_exec_pipe (argv, &fd);
+ if (!pid)
+ return NULL;
+
+ fp = fdopen (fd, "r");
+ if (!fp)
+ {
+ grub_util_warn (_("Unable to open stream from %s: %s"),
+ "zpool", strerror (errno));
+ goto out;
+ }
+
+ st = 0;
+ while (1)
+ {
+ line = NULL;
+ ret = getline (&line, &len, fp);
+ if (ret == -1)
+ break;
+
+ if (sscanf (line, " %s %256s %256s %256s %256s %256s",
+ name, state, readlen, writelen, cksum, notes) >= 5)
+ switch (st)
+ {
+ case 0:
+ if (!strcmp (name, "NAME")
+ && !strcmp (state, "STATE")
+ && !strcmp (readlen, "READ")
+ && !strcmp (writelen, "WRITE")
+ && !strcmp (cksum, "CKSUM"))
+ st++;
+ break;
+ case 1:
+ {
+ char *ptr = line;
+ while (1)
+ {
+ if (strncmp (ptr, poolname, strlen (poolname)) == 0
+ && grub_isspace(ptr[strlen (poolname)]))
+ st++;
+ if (!grub_isspace (*ptr))
+ break;
+ ptr++;
+ }
+ }
+ break;
+ case 2:
+ if (strcmp (name, "mirror") && !sscanf (name, "mirror-%u", &dummy)
+ && !sscanf (name, "raidz%u", &dummy)
+ && !sscanf (name, "raidz1%u", &dummy)
+ && !sscanf (name, "raidz2%u", &dummy)
+ && !sscanf (name, "raidz3%u", &dummy)
+ && !strcmp (state, "ONLINE"))
+ {
+ if (ndevices >= devices_allocated)
+ {
+ devices_allocated = 2 * (devices_allocated + 8);
+ devices = xrealloc (devices, sizeof (devices[0])
+ * devices_allocated);
+ }
+ if (name[0] == '/')
+ devices[ndevices++] = xstrdup (name);
+ else
+ devices[ndevices++] = xasprintf ("/dev/%s", name);
+ }
+ break;
+ }
+
+ free (line);
+ }
+
+ out:
+ close (fd);
+ waitpid (pid, NULL, 0);
+#endif
+ if (devices)
+ {
+ if (ndevices >= devices_allocated)
+ {
+ devices_allocated = 2 * (devices_allocated + 8);
+ devices = xrealloc (devices, sizeof (devices[0])
+ * devices_allocated);
+ }
+ devices[ndevices++] = 0;
+ }
+ return devices;
+}
+
+static char **
+find_root_devices_from_libzfs (const char *dir)
+{
+ char **devices = NULL;
+ char *poolname;
+ char *poolfs;
+
+ grub_find_zpool_from_dir (dir, &poolname, &poolfs);
+ if (! poolname)
+ return NULL;
+
+ devices = grub_util_find_root_devices_from_poolname (poolname);
+
+ free (poolname);
+ if (poolfs)
+ free (poolfs);
+
+ return devices;
+}
+
+char *
+grub_find_device (const char *dir, dev_t dev)
+{
+ DIR *dp;
+ struct saved_cwd saved_cwd;
+ struct dirent *ent;
+
+ if (! dir)
+ dir = "/dev";
+
+ dp = opendir (dir);
+ if (! dp)
+ return 0;
+
+ if (save_cwd (&saved_cwd) < 0)
+ {
+ grub_util_error ("%s", _("cannot save the original directory"));
+ closedir (dp);
+ return 0;
+ }
+
+ grub_util_info ("changing current directory to %s", dir);
+ if (chdir (dir) < 0)
+ {
+ free_cwd (&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 || strcmp (dir, "/dev/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 (restore_cwd (&saved_cwd) < 0)
+ grub_util_error ("%s",
+ _("cannot restore the original directory"));
+
+ free_cwd (&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.
+ Also, don't skip devices which names start with dm-[0-9] in
+ directories below /dev, e.g. /dev/mapper/dm-0-luks. */
+ if (strcmp (dir, "/dev") == 0 &&
+ 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;
+
+ cwd = xgetcwd ();
+ res = xmalloc (strlen (cwd) + strlen (ent->d_name) + 3);
+ sprintf (res,
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ /* Convert this block device to its character (raw) device. */
+ "%s/r%s",
+#else
+ /* Keep the device name as it is. */
+ "%s/%s",
+#endif
+ 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)
+ {
+ free (res);
+ continue;
+ }
+
+ if (restore_cwd (&saved_cwd) < 0)
+ grub_util_error ("%s", _("cannot restore the original directory"));
+
+ free_cwd (&saved_cwd);
+ closedir (dp);
+ return res;
+ }
+ }
+
+ if (restore_cwd (&saved_cwd) < 0)
+ grub_util_error ("%s", _("cannot restore the original directory"));
+
+ free_cwd (&saved_cwd);
+ closedir (dp);
+ return 0;
+}
+
+char **
+grub_guess_root_devices (const char *dir_in)
+{
+ char **os_dev = NULL;
+ struct stat st;
+ dev_t dev;
+ char *dir = grub_canonicalize_file_name (dir_in);
+
+ if (!dir)
+ grub_util_error (_("failed to get canonical path of `%s'"), dir_in);
+
+#ifdef __linux__
+ if (!os_dev)
+ os_dev = grub_find_root_devices_from_mountinfo (dir, NULL);
+#endif /* __linux__ */
+
+ if (!os_dev)
+ os_dev = find_root_devices_from_libzfs (dir);
+
+ if (os_dev)
+ {
+ char **cur;
+ for (cur = os_dev; *cur; cur++)
+ {
+ char *tmp = *cur;
+ int root, dm;
+ if (strcmp (*cur, "/dev/root") == 0
+ || strncmp (*cur, "/dev/dm-", sizeof ("/dev/dm-") - 1) == 0)
+ *cur = tmp;
+ else
+ {
+ *cur = grub_canonicalize_file_name (tmp);
+ if (*cur == NULL)
+ grub_util_error (_("failed to get canonical path of `%s'"), tmp);
+ free (tmp);
+ }
+ root = (strcmp (*cur, "/dev/root") == 0);
+ dm = (strncmp (*cur, "/dev/dm-", sizeof ("/dev/dm-") - 1) == 0);
+ if (!dm && !root)
+ continue;
+ if (stat (*cur, &st) < 0)
+ break;
+ free (*cur);
+ dev = st.st_rdev;
+ *cur = grub_find_device (dm ? "/dev/mapper" : "/dev", dev);
+ }
+ if (!*cur)
+ return os_dev;
+ for (cur = os_dev; *cur; cur++)
+ free (*cur);
+ free (os_dev);
+ os_dev = 0;
+ }
+
+ if (stat (dir, &st) < 0)
+ grub_util_error (_("cannot stat `%s': %s"), dir, strerror (errno));
+ free (dir);
+
+ dev = st.st_dev;
+
+ os_dev = xmalloc (2 * sizeof (os_dev[0]));
+
+ /* This might be truly slow, but is there any better way? */
+ os_dev[0] = grub_find_device ("/dev", dev);
+
+ if (!os_dev[0])
+ {
+ free (os_dev);
+ return 0;
+ }
+
+ os_dev[1] = 0;
+
+ return os_dev;
+}
+
+#endif
+
+void
+grub_util_pull_lvm_by_command (const char *os_dev)
+{
+ const char *argv[8];
+ int fd;
+ pid_t pid;
+ FILE *vgs;
+ char *buf = NULL;
+ size_t len = 0;
+ char *vgname = NULL;
+ const char *iptr;
+ char *optr;
+ char *vgid = NULL;
+ grub_size_t vgidlen = 0;
+
+ vgid = grub_util_get_vg_uuid (os_dev);
+ if (vgid)
+ vgidlen = grub_strlen (vgid);
+
+ if (!vgid)
+ {
+ if (strncmp (os_dev, LVM_DEV_MAPPER_STRING,
+ sizeof (LVM_DEV_MAPPER_STRING) - 1)
+ != 0)
+ return;
+
+ vgname = xmalloc (strlen (os_dev + sizeof (LVM_DEV_MAPPER_STRING) - 1) + 1);
+ for (iptr = os_dev + sizeof (LVM_DEV_MAPPER_STRING) - 1, optr = vgname; *iptr; )
+ if (*iptr != '-')
+ *optr++ = *iptr++;
+ else if (iptr[0] == '-' && iptr[1] == '-')
+ {
+ iptr += 2;
+ *optr++ = '-';
+ }
+ else
+ break;
+ *optr = '\0';
+ }
+
+ /* by default PV name is left aligned in 10 character field, meaning that
+ we do not know where name ends. Using dummy --separator disables
+ alignment. We have a single field, so separator itself is not output */
+ argv[0] = "vgs";
+ argv[1] = "--options";
+ if (vgid)
+ argv[2] = "vg_uuid,pv_name";
+ else
+ argv[2] = "pv_name";
+ argv[3] = "--noheadings";
+ argv[4] = "--separator";
+ argv[5] = ":";
+ argv[6] = vgname;
+ argv[7] = NULL;
+
+ pid = grub_util_exec_pipe (argv, &fd);
+ free (vgname);
+
+ if (!pid)
+ {
+ free (vgid);
+ return;
+ }
+
+ /* Parent. Read vgs' output. */
+ vgs = fdopen (fd, "r");
+ if (! vgs)
+ {
+ grub_util_warn (_("Unable to open stream from %s: %s"),
+ "vgs", strerror (errno));
+ goto out;
+ }
+
+ while (getline (&buf, &len, vgs) > 0)
+ {
+ char *ptr;
+ /* LVM adds two spaces as standard prefix */
+ for (ptr = buf; ptr < buf + 2 && *ptr == ' '; ptr++);
+
+ if (vgid && (grub_strncmp (vgid, ptr, vgidlen) != 0
+ || ptr[vgidlen] != ':'))
+ continue;
+ if (vgid)
+ ptr += vgidlen + 1;
+ if (*ptr == '\0')
+ continue;
+ *(ptr + strlen (ptr) - 1) = '\0';
+ grub_util_pull_device (ptr);
+ }
+
+out:
+ close (fd);
+ waitpid (pid, NULL, 0);
+ free (buf);
+ free (vgid);
+}
+
+/* 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 = grub_util_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 ("");
+}
+
+int
+grub_util_biosdisk_is_floppy (grub_disk_t disk)
+{
+ struct stat st;
+ int fd;
+ const char *dname;
+
+ dname = grub_util_biosdisk_get_osdev (disk);
+
+ if (!dname)
+ return 0;
+
+ fd = open (dname, O_RDONLY);
+ /* Shouldn't happen. */
+ if (fd == -1)
+ return 0;
+
+ /* Shouldn't happen either. */
+ if (fstat (fd, &st) < 0)
+ {
+ close (fd);
+ return 0;
+ }
+
+ close (fd);
+
+#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 (dname[5] == 'f'
+ && dname[6] == 'd'
+ && dname[7] >= '0'
+ && dname[7] <= '9')
+#endif
+ return 1;
+
+ return 0;
+}
+
+#else
+
+#include <grub/emu/getroot.h>
+
+void
+grub_util_pull_lvm_by_command (const char *os_dev __attribute__ ((unused)))
+{
+}
+
+#endif
diff --git a/grub-core/osdep/unix/hostdisk.c b/grub-core/osdep/unix/hostdisk.c
new file mode 100644
index 0000000..3a00d74
--- /dev/null
+++ b/grub-core/osdep/unix/hostdisk.c
@@ -0,0 +1,319 @@
+/* hostdisk.c - emulate biosdisk */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2004,2006,2007,2008,2009,2010,2011,2012,2013 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/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/emu/getroot.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>
+
+#if !defined (__CYGWIN__) && !defined (__MINGW32__) && !defined (__AROS__)
+
+#ifdef __linux__
+# include <sys/ioctl.h> /* ioctl */
+# include <sys/mount.h>
+#endif /* __linux__ */
+
+grub_uint64_t
+grub_util_get_fd_size (grub_util_fd_t fd, const char *name, unsigned *log_secsize)
+{
+ struct stat st;
+ grub_int64_t ret = -1;
+
+ if (fstat (fd, &st) < 0)
+ /* TRANSLATORS: "stat" comes from the name of POSIX function. */
+ grub_util_error (_("cannot stat `%s': %s"), name, strerror (errno));
+#if GRUB_DISK_DEVS_ARE_CHAR
+ if (S_ISCHR (st.st_mode))
+#else
+ if (S_ISBLK (st.st_mode))
+#endif
+ ret = grub_util_get_fd_size_os (fd, name, log_secsize);
+ if (ret != -1LL)
+ return ret;
+
+ if (log_secsize)
+ *log_secsize = 9;
+
+ return st.st_size;
+}
+
+int
+grub_util_fd_seek (grub_util_fd_t fd, grub_uint64_t off)
+{
+#if SIZEOF_OFF_T == 8
+ off_t offset = (off_t) off;
+
+ if (lseek (fd, offset, SEEK_SET) != offset)
+ return -1;
+#elif SIZEOF_OFF64_T == 8
+ off64_t offset = (off64_t) off;
+
+ if (lseek64 (fd, offset, SEEK_SET) != offset)
+ return -1;
+#else
+#error "No large file support"
+#endif
+ return 0;
+}
+
+
+/* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
+ error occurs, otherwise return LEN. */
+ssize_t
+grub_util_fd_read (grub_util_fd_t fd, char *buf, size_t len)
+{
+ ssize_t size = 0;
+
+ while (len)
+ {
+ ssize_t ret = read (fd, buf, len);
+
+ if (ret == 0)
+ break;
+
+ if (ret < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ else
+ return ret;
+ }
+
+ len -= ret;
+ buf += ret;
+ size += ret;
+ }
+
+ return size;
+}
+
+/* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
+ error occurs, otherwise return LEN. */
+ssize_t
+grub_util_fd_write (grub_util_fd_t fd, const char *buf, size_t len)
+{
+ ssize_t size = 0;
+
+ while (len)
+ {
+ ssize_t ret = write (fd, buf, len);
+
+ if (ret == 0)
+ break;
+
+ if (ret < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ else
+ return ret;
+ }
+
+ len -= ret;
+ buf += ret;
+ size += ret;
+ }
+
+ return size;
+}
+
+#if !defined (__NetBSD__) && !defined (__APPLE__) && !defined (__FreeBSD__) && !defined(__FreeBSD_kernel__)
+grub_util_fd_t
+grub_util_fd_open (const char *os_dev, int flags)
+{
+#ifdef O_LARGEFILE
+ flags |= O_LARGEFILE;
+#endif
+#ifdef O_BINARY
+ flags |= O_BINARY;
+#endif
+#ifdef O_CLOEXEC
+ flags |= O_CLOEXEC;
+#endif
+
+ return open (os_dev, flags, S_IROTH | S_IRGRP | S_IRUSR | S_IWUSR);
+}
+#endif
+
+const char *
+grub_util_fd_strerror (void)
+{
+ return strerror (errno);
+}
+
+static int allow_fd_syncs = 1;
+
+int
+grub_util_fd_sync (grub_util_fd_t fd)
+{
+ if (allow_fd_syncs)
+ return fsync (fd);
+ return 0;
+}
+
+int
+grub_util_file_sync (FILE *f)
+{
+ if (fflush (f) != 0)
+ return -1;
+ if (!allow_fd_syncs)
+ return 0;
+ return fsync (fileno (f));
+}
+
+void
+grub_util_disable_fd_syncs (void)
+{
+ allow_fd_syncs = 0;
+}
+
+int
+grub_util_fd_close (grub_util_fd_t fd)
+{
+ return close (fd);
+}
+
+char *
+grub_canonicalize_file_name (const char *path)
+{
+#if defined (PATH_MAX)
+ char *ret;
+
+ ret = xmalloc (PATH_MAX);
+ if (!realpath (path, ret))
+ return NULL;
+ return ret;
+#else
+ return realpath (path, NULL);
+#endif
+}
+
+FILE *
+grub_util_fopen (const char *path, const char *mode)
+{
+ return fopen (path, mode);
+}
+
+int
+grub_util_is_directory (const char *path)
+{
+ struct stat st;
+
+ if (stat (path, &st) == -1)
+ return 0;
+
+ return S_ISDIR (st.st_mode);
+}
+
+int
+grub_util_is_regular (const char *path)
+{
+ struct stat st;
+
+ if (stat (path, &st) == -1)
+ return 0;
+
+ return S_ISREG (st.st_mode);
+}
+
+grub_uint32_t
+grub_util_get_mtime (const char *path)
+{
+ struct stat st;
+
+ if (stat (path, &st) == -1)
+ return 0;
+
+ return st.st_mtime;
+}
+
+#endif
+
+#if defined (__CYGWIN__) || (!defined (__MINGW32__) && !defined (__AROS__))
+
+int
+grub_util_is_special_file (const char *path)
+{
+ struct stat st;
+
+ if (lstat (path, &st) == -1)
+ return 1;
+ return (!S_ISREG (st.st_mode) && !S_ISDIR (st.st_mode));
+}
+
+
+char *
+grub_util_make_temporary_file (void)
+{
+ const char *t = getenv ("TMPDIR");
+ size_t tl;
+ char *tmp;
+ if (!t)
+ t = "/tmp";
+ tl = strlen (t);
+ tmp = xmalloc (tl + sizeof ("/grub.XXXXXX"));
+ memcpy (tmp, t, tl);
+ memcpy (tmp + tl, "/grub.XXXXXX",
+ sizeof ("/grub.XXXXXX"));
+ if (mkstemp (tmp) == -1)
+ grub_util_error (_("cannot make temporary file: %s"), strerror (errno));
+ return tmp;
+}
+
+char *
+grub_util_make_temporary_dir (void)
+{
+ const char *t = getenv ("TMPDIR");
+ size_t tl;
+ char *tmp;
+ if (!t)
+ t = "/tmp";
+ tl = strlen (t);
+ tmp = xmalloc (tl + sizeof ("/grub.XXXXXX"));
+ memcpy (tmp, t, tl);
+ memcpy (tmp + tl, "/grub.XXXXXX",
+ sizeof ("/grub.XXXXXX"));
+ if (!mkdtemp (tmp))
+ grub_util_error (_("cannot make temporary directory: %s"),
+ strerror (errno));
+ return tmp;
+}
+
+#endif
diff --git a/grub-core/osdep/unix/password.c b/grub-core/osdep/unix/password.c
new file mode 100644
index 0000000..9996b24
--- /dev/null
+++ b/grub-core/osdep/unix/password.c
@@ -0,0 +1,75 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2006
+ * 2007, 2008, 2009, 2010, 2011, 2012, 2013 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/crypto.h>
+#include <grub/mm.h>
+#include <grub/term.h>
+
+#include <termios.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+int
+grub_password_get (char buf[], unsigned buf_size)
+{
+ FILE *in;
+ struct termios s, t;
+ int tty_changed = 0;
+ char *ptr;
+
+ grub_refresh ();
+
+ /* Disable echoing. Based on glibc. */
+ in = fopen ("/dev/tty", "w+c");
+ if (in == NULL)
+ in = stdin;
+
+ if (tcgetattr (fileno (in), &t) == 0)
+ {
+ /* Save the old one. */
+ s = t;
+ /* Tricky, tricky. */
+ t.c_lflag &= ~(ECHO|ISIG);
+ tty_changed = (tcsetattr (fileno (in), TCSAFLUSH, &t) == 0);
+ }
+ else
+ tty_changed = 0;
+ grub_memset (buf, 0, buf_size);
+ if (!fgets (buf, buf_size, stdin))
+ {
+ if (in != stdin)
+ fclose (in);
+ return 0;
+ }
+ ptr = buf + strlen (buf) - 1;
+ while (buf <= ptr && (*ptr == '\n' || *ptr == '\r'))
+ *ptr-- = 0;
+ /* Restore the original setting. */
+ if (tty_changed)
+ (void) tcsetattr (fileno (in), TCSAFLUSH, &s);
+
+ grub_xputs ("\n");
+ grub_refresh ();
+
+ if (in != stdin)
+ fclose (in);
+
+ return 1;
+}
diff --git a/grub-core/osdep/unix/platform.c b/grub-core/osdep/unix/platform.c
new file mode 100644
index 0000000..55b8f40
--- /dev/null
+++ b/grub-core/osdep/unix/platform.c
@@ -0,0 +1,241 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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/util/install.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/util/misc.h>
+#include <grub/misc.h>
+#include <grub/i18n.h>
+#include <grub/emu/exec.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+
+static char *
+get_ofpathname (const char *dev)
+{
+ size_t alloced = 4096;
+ char *ret = xmalloc (alloced);
+ size_t offset = 0;
+ int fd;
+ pid_t pid;
+
+ pid = grub_util_exec_pipe ((const char * []){ "ofpathname", dev, NULL }, &fd);
+ if (!pid)
+ goto fail;
+
+ FILE *fp = fdopen (fd, "r");
+ if (!fp)
+ goto fail;
+
+ while (!feof (fp))
+ {
+ size_t r;
+ if (alloced == offset)
+ {
+ alloced *= 2;
+ ret = xrealloc (ret, alloced);
+ }
+ r = fread (ret + offset, 1, alloced - offset, fp);
+ offset += r;
+ }
+
+ if (offset > 0 && ret[offset - 1] == '\n')
+ offset--;
+ if (offset > 0 && ret[offset - 1] == '\r')
+ offset--;
+ if (alloced == offset)
+ {
+ alloced++;
+ ret = xrealloc (ret, alloced);
+ }
+ ret[offset] = '\0';
+
+ fclose (fp);
+
+ return ret;
+
+ fail:
+ grub_util_error (_("couldn't find IEEE1275 device path for %s.\nYou will have to set `boot-device' variable manually"),
+ dev);
+}
+
+static int
+grub_install_remove_efi_entries_by_distributor (const char *efi_distributor)
+{
+ int fd;
+ pid_t pid = grub_util_exec_pipe ((const char * []){ "efibootmgr", NULL }, &fd);
+ char *line = NULL;
+ size_t len = 0;
+ int rc = 0;
+
+ if (!pid)
+ {
+ grub_util_warn (_("Unable to open stream from %s: %s"),
+ "efibootmgr", strerror (errno));
+ return errno;
+ }
+
+ FILE *fp = fdopen (fd, "r");
+ if (!fp)
+ {
+ grub_util_warn (_("Unable to open stream from %s: %s"),
+ "efibootmgr", strerror (errno));
+ return errno;
+ }
+
+ line = xmalloc (80);
+ len = 80;
+ while (1)
+ {
+ int ret;
+ char *bootnum;
+ ret = getline (&line, &len, fp);
+ if (ret == -1)
+ break;
+ if (grub_memcmp (line, "Boot", sizeof ("Boot") - 1) != 0
+ || line[sizeof ("Boot") - 1] < '0'
+ || line[sizeof ("Boot") - 1] > '9')
+ continue;
+ if (!strcasestr (line, efi_distributor))
+ continue;
+ bootnum = line + sizeof ("Boot") - 1;
+ bootnum[4] = '\0';
+ if (!verbosity)
+ rc = grub_util_exec ((const char * []){ "efibootmgr", "-q",
+ "-b", bootnum, "-B", NULL });
+ else
+ rc = grub_util_exec ((const char * []){ "efibootmgr",
+ "-b", bootnum, "-B", NULL });
+ }
+
+ free (line);
+ return rc;
+}
+
+int
+grub_install_register_efi (grub_device_t efidir_grub_dev,
+ const char *efifile_path,
+ const char *efi_distributor)
+{
+ const char * efidir_disk;
+ int efidir_part;
+ int ret;
+ efidir_disk = grub_util_biosdisk_get_osdev (efidir_grub_dev->disk);
+ efidir_part = efidir_grub_dev->disk->partition ? efidir_grub_dev->disk->partition->number + 1 : 1;
+
+ if (grub_util_exec_redirect_null ((const char * []){ "efibootmgr", "--version", NULL }))
+ {
+ /* TRANSLATORS: This message is shown when required executable `%s'
+ isn't found. */
+ grub_util_error (_("%s: not found"), "efibootmgr");
+ }
+
+ /* On Linux, we need the efivars kernel modules. */
+#ifdef __linux__
+ grub_util_exec ((const char * []){ "modprobe", "-q", "efivars", NULL });
+#endif
+ /* Delete old entries from the same distributor. */
+ ret = grub_install_remove_efi_entries_by_distributor (efi_distributor);
+ if (ret)
+ return ret;
+
+ char *efidir_part_str = xasprintf ("%d", efidir_part);
+
+ if (!verbosity)
+ ret = grub_util_exec ((const char * []){ "efibootmgr", "-q",
+ "-c", "-d", efidir_disk,
+ "-p", efidir_part_str, "-w",
+ "-L", efi_distributor, "-l",
+ efifile_path, NULL });
+ else
+ ret = grub_util_exec ((const char * []){ "efibootmgr",
+ "-c", "-d", efidir_disk,
+ "-p", efidir_part_str, "-w",
+ "-L", efi_distributor, "-l",
+ efifile_path, NULL });
+ free (efidir_part_str);
+ return ret;
+}
+
+void
+grub_install_register_ieee1275 (int is_prep, const char *install_device,
+ int partno, const char *relpath)
+{
+ char *boot_device;
+
+ if (grub_util_exec_redirect_null ((const char * []){ "ofpathname", "--version", NULL }))
+ {
+ /* TRANSLATORS: This message is shown when required executable `%s'
+ isn't found. */
+ grub_util_error (_("%s: not found"), "ofpathname");
+ }
+
+ /* Get the Open Firmware device tree path translation. */
+ if (!is_prep)
+ {
+ char *ptr;
+ char *ofpath;
+ const char *iptr;
+
+ ofpath = get_ofpathname (install_device);
+ boot_device = xmalloc (strlen (ofpath) + 1
+ + sizeof ("XXXXXXXXXXXXXXXXXXXX")
+ + 1 + strlen (relpath) + 1);
+ ptr = grub_stpcpy (boot_device, ofpath);
+ *ptr++ = ':';
+ grub_snprintf (ptr, sizeof ("XXXXXXXXXXXXXXXXXXXX"), "%d",
+ partno);
+ ptr += strlen (ptr);
+ *ptr++ = ',';
+ for (iptr = relpath; *iptr; iptr++, ptr++)
+ {
+ if (*iptr == '/')
+ *ptr = '\\';
+ else
+ *ptr = *iptr;
+ }
+ *ptr = '\0';
+ }
+ else
+ boot_device = get_ofpathname (install_device);
+
+ if (grub_util_exec ((const char * []){ "nvsetenv", "boot-device",
+ boot_device, NULL }))
+ {
+ char *cmd = xasprintf ("setenv boot-device %s", boot_device);
+ grub_util_error (_("`nvsetenv' failed. \nYou will have to set `boot-device' variable manually. At the IEEE1275 prompt, type:\n %s\n"),
+ cmd);
+ free (cmd);
+ }
+
+ free (boot_device);
+}
+
+void
+grub_install_sgi_setup (const char *install_device,
+ const char *imgfile, const char *destname)
+{
+ grub_util_exec ((const char * []){ "dvhtool", "-d",
+ install_device, "--unix-to-vh",
+ imgfile, destname, NULL });
+ grub_util_warn ("%s", _("You will have to set `SystemPartition' and `OSLoader' manually."));
+}
diff --git a/grub-core/osdep/unix/random.c b/grub-core/osdep/unix/random.c
new file mode 100644
index 0000000..fa0101b
--- /dev/null
+++ b/grub-core/osdep/unix/random.c
@@ -0,0 +1,48 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1992-1999,2001,2003,2004,2005,2009,2010,2011,2012,2013 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/types.h>
+#include <grub/crypto.h>
+#include <grub/auth.h>
+#include <grub/emu/misc.h>
+#include <grub/util/misc.h>
+#include <grub/i18n.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int
+grub_get_random (void *out, grub_size_t len)
+{
+ FILE *f;
+ size_t rd;
+
+ f = grub_util_fopen ("/dev/urandom", "rb");
+ if (!f)
+ return 1;
+ rd = fread (out, 1, len, f);
+ fclose (f);
+
+ if (rd != len)
+ return 1;
+ return 0;
+}
diff --git a/grub-core/osdep/unix/relpath.c b/grub-core/osdep/unix/relpath.c
new file mode 100644
index 0000000..f719950
--- /dev/null
+++ b/grub-core/osdep/unix/relpath.c
@@ -0,0 +1,151 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009,2010,2011,2012,2013 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/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <grub/util/misc.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/getroot.h>
+#include <grub/mm.h>
+
+/* 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;
+ char *poolfs = NULL;
+
+ /* canonicalize. */
+ p = grub_canonicalize_file_name (path);
+ if (p == NULL)
+ grub_util_error (_("failed to get canonical path of `%s'"), path);
+
+#ifdef __linux__
+ ret = grub_make_system_path_relative_to_its_root_os (p);
+ if (ret)
+ {
+ free (p);
+ return ret;
+ }
+#endif
+
+ /* For ZFS sub-pool filesystems. */
+#ifndef __HAIKU__
+ {
+ 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 ("%s",
+ /* TRANSLATORS: canonical pathname is the
+ complete one e.g. /etc/fstab. It has
+ to contain `/' normally, if it doesn't
+ we're in trouble and throw this error. */
+ _("no `/' in canonical filename"));
+ 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);
+ free (buf2);
+ if (poolfs)
+ return xasprintf ("/%s/@", poolfs);
+ 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;
+
+ free (buf2);
+
+ /* Remove trailing slashes, return empty string if root directory. */
+ len = strlen (buf3);
+ while (len > 0 && buf3[len - 1] == '/')
+ {
+ buf3[len - 1] = '\0';
+ len--;
+ }
+
+ if (poolfs)
+ {
+ ret = xasprintf ("/%s/@%s", poolfs, buf3);
+ free (buf3);
+ }
+ else
+ ret = buf3;
+
+ return ret;
+}
diff --git a/grub-core/osdep/unix/sleep.c b/grub-core/osdep/unix/sleep.c
new file mode 100644
index 0000000..5b00a38
--- /dev/null
+++ b/grub-core/osdep/unix/sleep.c
@@ -0,0 +1,30 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,2005,2006,2007,2008,2009,2010,2011,2012,2013 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 <grub/time.h>
+
+void
+grub_millisleep (grub_uint32_t ms)
+{
+ struct timespec ts;
+
+ ts.tv_sec = ms / 1000;
+ ts.tv_nsec = (ms % 1000) * 1000000;
+ nanosleep (&ts, NULL);
+}