Hi,

This patch add environment block support, which can be used to locate
root device using uuid or label.

The environment block occupied the space previously used to store
prefix. The structure is like this:

Magic numbe: "EvbK" (4 bytes)
Length of environment block (2 bytes)
null-ended name=value pairs, ended with an empty string.

The previous prefix is stored in variable rdir.

Different platform use grub_machine_get_envblk to return a pointer to
the name=value area of environment block. In main.c, it uses
grub_parse_envblk to walk through the list and assign values to
variables.

I also replace grub_machine_set_prefix with grub_machine_set_root. As
it seems strange to compute the prefix in grub_machine_set_prefix, and
then split it in grub_set_root_dev to get the root device. Now,
grub_machine_set_root set the root directly, and in grub_set_root_dev,
it uses root and rdir to generate the prefix.

A new tool grub-editenv is added to manage the environment block, for example:

grub-editenv FILE create
Create a blank environment block file. It's used to store external
environment file.

grub-editenv FILE info
Show the info about location and size of environment block in core.img
or external environment file.

grub-editenv FILE list
List the content of environment block.

grub-editenv FILE edit name=value
Edit the environment block. If no value is specify, name=, it deletes the item.

grub-editenv FILE clear
Remove all variables in the environment block.

And there is a new module findroot.mod. It will scan all partitions
and compare their uuid/label with the uuid and label variable, whose
value can be set in the environment block.

Here is an example:

First, use grub-mkimage to create core.img:
grub-mkimage -o core.img pc ext2 findroot

grub-mkimage will initialize the environment block with one item:
rdir=/boot/grub

Second, use grub-editenv to set the uuid or label of the root device.
For example:

grub-editenv core.img edit uuid=xxx-xxxx
grub-editenv core.img edit label=System

If both uuid and label is specified, uuid will take preference, unless
the fs don't support uuid.

You can also change rdir to change the default root directory, or add
debug=all to show debug message, etc.

-- 
Bean
diff --git a/boot/i386/pc/lnxboot.S b/boot/i386/pc/lnxboot.S
index 955cc41..380ef08 100644
--- a/boot/i386/pc/lnxboot.S
+++ b/boot/i386/pc/lnxboot.S
@@ -185,7 +185,7 @@ real_code_2:
 	call	move_memory
 
 	/* Check for multiboot signature.  */
-	cmpl	$MULTIBOOT_MAGIC, %ss:(DATA_ADDR + 0x50)
+	cmpl	$MULTIBOOT_MAGIC, %ss:(DATA_ADDR + GRUB_KERNEL_MACHINE_DATA_END)
 	jz	1f
 
 	movl	(ramdisk_image - start), %esi
diff --git a/conf/common.rmk b/conf/common.rmk
index acbebc7..0582d56 100644
--- a/conf/common.rmk
+++ b/conf/common.rmk
@@ -94,6 +94,12 @@ grub_fstest_init.c: grub_fstest_init.lst $(filter-out grub_fstest_init.c,$(grub_
 	rm -f $@; sh $(srcdir)/geninit.sh $< $(filter %.c,$^) > $@
 DISTCLEANFILES += grub_fstest_init.c
 
+# for grub-editenv
+bin_UTILITIES += grub-editenv
+grub_editenv_SOURCES = util/grub-editenv.c util/envblk.c util/misc.c \
+                       kern/err.c kern/misc.c
+CLEANFILES += grub-editenv
+
 # For update-grub
 update-grub: util/update-grub.in config.status
 	./config.status --file=$@:$<
@@ -365,7 +371,7 @@ hexdump_mod_CFLAGS = $(COMMON_CFLAGS)
 hexdump_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
 # Misc.
-pkglib_MODULES += gzio.mod elf.mod
+pkglib_MODULES += gzio.mod elf.mod findroot.mod
 
 # For elf.mod.
 elf_mod_SOURCES = kern/elf.c
@@ -386,3 +392,8 @@ read_mod_LDFLAGS = $(COMMON_LDFLAGS)
 sleep_mod_SOURCES = commands/sleep.c
 sleep_mod_CFLAGS = $(COMMON_CFLAGS)
 sleep_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For findroot.mod.
+findroot_mod_SOURCES = kern/findroot.c
+findroot_mod_CFLAGS = $(COMMON_CFLAGS)
+findroot_mod_LDFLAGS = $(COMMON_LDFLAGS)
diff --git a/include/grub/envblk.h b/include/grub/envblk.h
new file mode 100755
index 0000000..7811723
--- /dev/null
+++ b/include/grub/envblk.h
@@ -0,0 +1,47 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/>.
+ */
+
+#ifndef GRUB_ENVBLK_HEADER
+#define GRUB_ENVBLK_HEADER	1
+
+#define GRUB_ENVBLK_SIGNATURE	0x4b627645	/* EvbK  */
+
+#define GRUB_ENVBLK_MAXLEN	8192
+
+/* Names of important environment variables.  */
+#define GRUB_ENVBLK_RDIR	"rdir"
+#define GRUB_ENVBLK_UUID	"uuid"
+#define GRUB_ENVBLK_LABEL	"label"
+
+#ifndef ASM_FILE
+
+struct grub_envblk
+{
+  grub_uint32_t signature;
+  grub_uint16_t length;
+  char data[0];
+} __attribute__ ((packed));
+typedef struct grub_envblk *grub_envblk_t;
+
+grub_envblk_t grub_envblk_find (char *buf);
+int grub_envblk_insert (grub_envblk_t envblk, char *name, char *value);
+void grub_envblk_delete (grub_envblk_t envblk, char *name);
+
+#endif
+
+#endif /* ! GRUB_ENVBLK_HEADER */
diff --git a/include/grub/i386/pc/kernel.h b/include/grub/i386/pc/kernel.h
index 43a8d5b..13548c5 100644
--- a/include/grub/i386/pc/kernel.h
+++ b/include/grub/i386/pc/kernel.h
@@ -37,14 +37,14 @@
 /* The offset of GRUB_MEMDISK_IMAGE_SIZE.  */
 #define GRUB_KERNEL_MACHINE_MEMDISK_IMAGE_SIZE	0x1c
 
-/* The offset of GRUB_PREFIX.  */
-#define GRUB_KERNEL_MACHINE_PREFIX		0x20
+/* The offset of GRUB_ENVBLK.  */
+#define GRUB_KERNEL_MACHINE_ENVBLK		0x26
 
 /* End of the data section. */
-#define GRUB_KERNEL_MACHINE_DATA_END		0x50
+#define GRUB_KERNEL_MACHINE_DATA_END		0x70
 
 /* The size of the first region which won't be compressed.  */
-#define GRUB_KERNEL_MACHINE_RAW_SIZE		0x4A0
+#define GRUB_KERNEL_MACHINE_RAW_SIZE		0x4C0
 
 #ifndef ASM_FILE
 
@@ -66,9 +66,9 @@ extern grub_int32_t grub_install_bsd_part;
 /* The size of memory disk image, if present.  */
 extern grub_int32_t grub_memdisk_image_size;
 
-/* The prefix which points to the directory where GRUB modules and its
-   configuration file are located.  */
-extern char grub_prefix[];
+/* The envblk contains variable which can be used to locate the directory where
+   GRUB modules and its configuration file.  */
+extern char grub_envblk[];
 
 /* The boot BIOS drive number.  */
 extern grub_int32_t EXPORT_VAR(grub_boot_drive);
diff --git a/include/grub/kernel.h b/include/grub/kernel.h
index 4a4e2cc..49f3516 100644
--- a/include/grub/kernel.h
+++ b/include/grub/kernel.h
@@ -55,8 +55,10 @@ void grub_machine_init (void);
 /* The machine-specific finalization.  */
 void grub_machine_fini (void);
 
-/* The machine-specific prefix initialization.  */
-void grub_machine_set_prefix (void);
+/* The machine-specific root initialization.  */
+void grub_machine_set_root (void);
+
+char *grub_machine_get_envblk (void);
 
 /* Register all the exported symbols. This is automatically generated.  */
 void grub_register_exported_symbols (void);
diff --git a/kern/findroot.c b/kern/findroot.c
new file mode 100755
index 0000000..3ac5ce5
--- /dev/null
+++ b/kern/findroot.c
@@ -0,0 +1,96 @@
+/* findroot.c - search for root device */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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 <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/device.h>
+#include <grub/file.h>
+#include <grub/env.h>
+#include <grub/machine/kernel.h>
+
+#define KERNEL_FILE	"/normal.mod"
+
+static void
+findroot (void)
+{
+  char *label, *uuid;
+
+  auto int iterate_device (const char *name);
+  int iterate_device (const char *name)
+    {
+      int found = 0;
+      grub_device_t dev;
+      grub_fs_t fs;
+
+      dev = grub_device_open (name);
+      if (dev)
+        {
+          fs = grub_fs_probe (dev);
+          if (fs)
+            {
+              if ((uuid) && (fs->uuid))
+                {
+                  char *cur;
+
+                  fs->uuid (dev, &cur);
+                  if (cur)
+                    {
+                      found = (! grub_strcmp (uuid, cur));
+                      grub_free (cur);
+                    }
+                }
+              else if ((label) && (fs->label))
+                {
+                  char *cur;
+
+                  fs->label (dev, &cur);
+                  if (cur)
+                    {
+                      found = (! grub_strcmp (label, cur));
+                      grub_free (cur);
+                    }
+                }
+
+              if (found)
+                grub_env_set ("root", name);
+            }
+
+          grub_device_close (dev);
+        }
+
+      grub_errno = GRUB_ERR_NONE;
+      return found;
+    }
+
+  label = grub_env_get ("label");
+  uuid = grub_env_get ("uuid");
+
+  if ((! label) && (! uuid))
+    return;
+
+  grub_device_iterate (iterate_device);
+}
+
+GRUB_MOD_INIT(findroot)
+{
+  findroot ();
+}
diff --git a/kern/i386/pc/init.c b/kern/i386/pc/init.c
index 757f5d5..aabdff2 100644
--- a/kern/i386/pc/init.c
+++ b/kern/i386/pc/init.c
@@ -58,40 +58,6 @@ grub_arch_sync_caches (void *address __attribute__ ((unused)),
 {
 }
 
-static char *
-make_install_device (void)
-{
-  /* XXX: This should be enough.  */
-  char dev[100];
-
-  if (grub_memdisk_image_size)
-    {
-      grub_sprintf (dev, "(memdisk)%s", grub_prefix);
-      grub_strcpy (grub_prefix, dev);
-    }
-  else if (grub_install_dos_part != -2)
-    {
-      /* If the root drive is not set explicitly, assume that it is identical
-         to the boot drive.  */
-      if (grub_root_drive == 0xFF)
-        grub_root_drive = grub_boot_drive;
-      
-      grub_sprintf (dev, "(%cd%u", (grub_root_drive & 0x80) ? 'h' : 'f',
-                    grub_root_drive & 0x7f);
-      
-      if (grub_install_dos_part >= 0)
-	grub_sprintf (dev + grub_strlen (dev), ",%u", grub_install_dos_part + 1);
-      
-      if (grub_install_bsd_part >= 0)
-	grub_sprintf (dev + grub_strlen (dev), ",%c", grub_install_bsd_part + 'a');
-      
-      grub_sprintf (dev + grub_strlen (dev), ")%s", grub_prefix);
-      grub_strcpy (grub_prefix, dev);
-    }
-      
-  return grub_prefix;
-}
-
 /* Add a memory region.  */
 static void
 add_mem_region (grub_addr_t addr, grub_size_t size)
@@ -238,11 +204,40 @@ grub_machine_init (void)
     grub_fatal ("no upper memory");
 }
 
+char *
+grub_machine_get_envblk (void)
+{
+  return grub_envblk;
+}
+
 void
-grub_machine_set_prefix (void)
+grub_machine_set_root (void)
 {
-  /* Initialize the prefix.  */
-  grub_env_set ("prefix", make_install_device ());
+  /* XXX: This should be enough.  */
+  char dev[100];
+
+  if (grub_memdisk_image_size)
+    {
+      grub_env_set ("root", "memdisk");
+    }
+  else if (grub_install_dos_part != -2)
+    {
+      /* If the root drive is not set explicitly, assume that it is identical
+         to the boot drive.  */
+      if (grub_root_drive == 0xFF)
+        grub_root_drive = grub_boot_drive;
+
+      grub_sprintf (dev, "%cd%u", (grub_root_drive & 0x80) ? 'h' : 'f',
+                    grub_root_drive & 0x7f);
+
+      if (grub_install_dos_part >= 0)
+	grub_sprintf (dev + grub_strlen (dev), ",%u", grub_install_dos_part + 1);
+
+      if (grub_install_bsd_part >= 0)
+	grub_sprintf (dev + grub_strlen (dev), ",%c", grub_install_bsd_part + 'a');
+
+      grub_env_set ("root", dev);
+    }
 }
 
 void
diff --git a/kern/i386/pc/startup.S b/kern/i386/pc/startup.S
index ebb98fe..cba710a 100644
--- a/kern/i386/pc/startup.S
+++ b/kern/i386/pc/startup.S
@@ -52,6 +52,7 @@
 #include <grub/term.h>
 #include <multiboot.h>
 #include <multiboot2.h>
+#include <grub/envblk.h>
 		
 #define ABS(x)	((x) - EXT_C(start) + GRUB_BOOT_MACHINE_KERNEL_ADDR + 0x200)
 	
@@ -98,14 +99,19 @@ VARIABLE(grub_install_bsd_part)
 	.long	0xFFFFFFFF
 VARIABLE(grub_memdisk_image_size)
 	.long	0
-VARIABLE(grub_prefix)
+	.long	GRUB_ENVBLK_SIGNATURE
+	.word	envblk_end - grub_envblk
+VARIABLE(grub_envblk)
+	.byte	0
 	/* to be filled by grub-mkimage */
 
 	/*
 	 *  Leave some breathing room for the prefix.
 	 */
 
-	. = EXT_C(start) + 0x50
+	. = EXT_C(start) + 0x70
+
+envblk_end:
 
 /*
  * Support for booting GRUB from a Multiboot boot loader (e.g. GRUB itself).
diff --git a/kern/main.c b/kern/main.c
index 09de03a..729dd78 100644
--- a/kern/main.c
+++ b/kern/main.c
@@ -28,6 +28,33 @@
 #include <grub/device.h>
 #include <grub/env.h>
 
+static void
+grub_parse_envblk (void)
+{
+  char *env;
+
+  env = grub_machine_get_envblk ();
+  if (! env)
+    return;
+
+  while (*env)
+    {
+      char *value;
+
+      value = grub_strchr (env, '=');
+      if (value)
+        {
+          *(value++) = 0;
+          grub_env_set (env, value);
+          env = value;
+        }
+      else
+        grub_env_set (env, "");
+
+      env += grub_strlen (env) + 1;
+    }
+}
+
 /* Load all modules in core.  */
 static void
 grub_load_modules (void)
@@ -75,24 +102,28 @@ grub_env_write_root (struct grub_env_var *var __attribute__ ((unused)),
 static void
 grub_set_root_dev (void)
 {
-  const char *prefix;
+  char *root, *rdir, *prefix;
+
+  if (! grub_env_get ("root"))
+    grub_machine_set_root ();
 
   grub_register_variable_hook ("root", 0, grub_env_write_root);
   grub_env_export ("root");
   
-  prefix = grub_env_get ("prefix");
+  root = grub_env_get ("root");
+  if (! *root)
+    return;
   
-  if (prefix)
-    {
-      char *dev;
-
-      dev = grub_file_get_device_name (prefix);
-      if (dev)
-	{
-	  grub_env_set ("root", dev);
-	  grub_free (dev);
-	}
-    }
+  rdir = grub_env_get ("rdir");
+  if (! rdir)
+    rdir = "/";
+
+  prefix = grub_malloc (grub_strlen (root) + grub_strlen (rdir) + 3);
+  grub_sprintf (prefix, "(%s)%s", root, rdir);
+
+  grub_env_set ("prefix", prefix);
+
+  grub_free (prefix);
 }
 
 /* Load the normal mode module and execute the normal mode if possible.  */
@@ -118,13 +149,15 @@ grub_main (void)
   grub_printf ("Welcome to GRUB!\n\n");
   grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
 
+  /* Parse the environment block.  */
+  grub_parse_envblk ();
+
   /* Load pre-loaded modules and free the space.  */
   grub_register_exported_symbols ();
   grub_load_modules ();
 
   /* It is better to set the root device as soon as possible,
      for convenience.  */
-  grub_machine_set_prefix ();
   grub_set_root_dev ();
 
   /* Load the normal mode module.  */
diff --git a/util/envblk.c b/util/envblk.c
new file mode 100755
index 0000000..82ec674
--- /dev/null
+++ b/util/envblk.c
@@ -0,0 +1,123 @@
+/* envblk.c - Common function for environment block.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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 <grub/types.h>
+#include <grub/misc.h>
+#include <grub/envblk.h>
+
+grub_envblk_t
+grub_envblk_find (char *buf)
+{
+  grub_uint32_t *pd;
+  int len;
+
+  pd = (grub_uint32_t *) buf;
+
+  for (len = GRUB_ENVBLK_MAXLEN - 6; len > 0; len -= 4, pd++)
+    if (*pd == GRUB_ENVBLK_SIGNATURE)
+      {
+        grub_envblk_t p;
+
+        p = (grub_envblk_t) pd;
+        if (p->length <= len)
+          return p;
+      }
+
+  return 0;
+}
+
+int
+grub_envblk_insert (grub_envblk_t envblk, char *name, char *value)
+{
+  char *p, *pend;
+  char *found = 0;
+  int nl;
+
+  nl = grub_strlen (name);
+  p = envblk->data;
+  pend = p + envblk->length;
+
+  while (*p)
+    {
+      if ((! found) && (! grub_memcmp (name, p, nl)) && (p[nl] == '='))
+        found = p + nl + 1;
+
+      p += strlen (p) + 1;
+      if (p >= pend)
+        return 1;
+    }
+
+  if (found)
+    {
+      int len1, len2;
+
+      len1 = grub_strlen (found);
+      len2 = grub_strlen (value);
+      if ((p - envblk->data) + 1 - len1 + len2 > envblk->length)
+        return 1;
+
+      grub_memcpy (found + len2 + 1, found + len1 + 1, (p - found) - len1);
+      grub_strcpy (found, value);
+    }
+  else
+    {
+      int len2 = grub_strlen (value);
+
+      if ((p - envblk->data) + nl + 1 + len2 + 2 > envblk->length)
+        return 1;
+
+      grub_strcpy (p, name);
+      p[nl] = '=';
+      grub_strcpy (p + nl + 1, value);
+      p[nl + 1 + len2 + 1] = 0;
+    }
+
+  return 0;
+}
+
+void
+grub_envblk_delete (grub_envblk_t envblk, char *name)
+{
+  char *p, *pend;
+  char *found = 0;
+  int nl;
+
+  nl = grub_strlen (name);
+  p = envblk->data;
+  pend = p + envblk->length;
+
+  while (*p)
+    {
+      if ((! found) && (! grub_memcmp (name, p, nl)) && (p[nl] == '='))
+        found = p;
+
+      p += strlen (p) + 1;
+      if (p >= pend)
+        return;
+    }
+
+  if (found)
+    {
+      int len;
+
+      len = grub_strlen (found);
+      grub_memcpy (found, found + len + 1, (p - found) - len);
+    }
+}
diff --git a/util/grub-editenv.c b/util/grub-editenv.c
new file mode 100755
index 0000000..d4d7919
--- /dev/null
+++ b/util/grub-editenv.c
@@ -0,0 +1,259 @@
+/* grub-editenv.c - tool to edit environment block.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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 <grub/types.h>
+#include <grub/util/misc.h>
+
+#include <grub/envblk.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+void
+grub_putchar (int c)
+{
+  putchar (c);
+}
+
+int
+grub_getkey (void)
+{
+  return -1;
+}
+
+void *
+grub_term_get_current (void)
+{
+  return 0;
+}
+
+void
+grub_refresh (void)
+{
+  fflush (stdout);
+}
+
+char *
+grub_env_get (const char * name)
+{
+  (void) name;
+
+  return 0;
+}
+
+static struct option options[] = {
+  {"help", no_argument, 0, 'h'},
+  {"version", no_argument, 0, 'V'},
+  {"verbose", no_argument, 0, 'v'},
+  {0, 0, 0, 0}
+};
+
+char buffer[GRUB_ENVBLK_MAXLEN];
+grub_envblk_t envblk;
+
+static void
+usage (int status)
+{
+  if (status)
+    fprintf (stderr, "Try ``grub-editenv --help'' for more information.\n");
+  else
+    printf ("\
+Usage: grub-editenv [OPTIONS] FILENAME COMMAND\n\
+\n\
+Tool to edit environment block.\n\
+\nCommands:\n\
+  create                    create a blank environment block file\n\
+  info                      show information about the environment block\n\
+  list                      list the current variables\n\
+  edit [name=value] ...     change/delete variables\n\
+  clear                     delete all variables\n\
+\nOptions:\n\
+  -h, --help                display this message and exit\n\
+  -V, --version             print version information and exit\n\
+  -v, --verbose             print verbose messages\n\
+\n\
+Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
+
+  exit (status);
+}
+
+int
+create_envblk_file (char *name)
+{
+  FILE *f;
+  grub_envblk_t p;
+
+  f = fopen (name, "w");
+  if (! f)
+    return 1;
+
+  /* Just in case OS don't save 0s.  */
+  memset (buffer, -1, sizeof (buffer));
+
+  p = (grub_envblk_t) &buffer[0];
+  p->signature = GRUB_ENVBLK_SIGNATURE;
+  p->length = sizeof (buffer) - sizeof (struct grub_envblk);
+  p->data[0] = p->data[1] = 0;
+
+  fwrite (buffer, sizeof (buffer), 1, f);
+
+  fclose (f);
+  return 0;
+}
+
+FILE *
+open_envblk_file (char *name)
+{
+  FILE *f;
+
+  f = fopen (name, "r+");
+  if (! f)
+    grub_util_error ("Can\'t open file %s", name);
+
+  if (fread (buffer, 1, sizeof (buffer), f) != sizeof (buffer))
+    grub_util_error ("The envblk file is too short");
+
+  envblk = grub_envblk_find (buffer);
+  if (! envblk)
+    grub_util_error ("Can\'t find environment block");
+
+  return f;
+}
+
+static void
+cmd_info (void)
+{
+  printf ("Envblk offset: %d\n", (char *) envblk - buffer);
+  printf ("Envblk length: %d\n", envblk->length);
+}
+
+static void
+cmd_list (void)
+{
+  char *p = envblk->data;
+
+  while (*p)
+    {
+      printf ("%s\n", p);
+      p += strlen (p) + 1;
+    }
+}
+
+static void
+cmd_edit (int argc, char *argv[])
+{
+  while (argc)
+    {
+      char *p;
+
+      p = strchr (argv[0], '=');
+      if (! p)
+        grub_util_error ("Invalid parameter");
+
+      *(p++) = 0;
+
+      if (*p)
+        {
+          if (grub_envblk_insert (envblk, argv[0], p))
+            grub_util_error ("Environment block too small");
+        }
+      else
+        grub_envblk_delete (envblk, argv[0]);
+
+      argc--;
+      argv++;
+    }
+}
+
+static void
+cmd_clear (void)
+{
+  envblk->data[0] = envblk->data[1] = 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+  FILE *f;
+
+  progname = "grub-editenv";
+
+  /* Check for options.  */
+  while (1)
+    {
+      int c = getopt_long (argc, argv, "hVv", options, 0);
+
+      if (c == -1)
+	break;
+      else
+	switch (c)
+	  {
+	  case 'h':
+	    usage (0);
+	    break;
+
+	  case 'V':
+	    printf ("%s (%s) %s\n", progname, PACKAGE_NAME, PACKAGE_VERSION);
+	    return 0;
+
+	  case 'v':
+	    verbosity++;
+	    break;
+
+	  default:
+	    usage (1);
+	    break;
+	  }
+    }
+
+  /* Obtain PATH.  */
+  if (optind + 1 >= argc)
+    {
+      fprintf (stderr, "Not enough parameter.\n");
+      usage (1);
+    }
+
+  if (! strcmp (argv[optind + 1], "create"))
+    return create_envblk_file (argv[optind]);
+
+  f = open_envblk_file (argv[optind]);
+
+  optind++;
+  if (! strcmp (argv[optind], "info"))
+    cmd_info ();
+  else if (! strcmp (argv[optind], "list"))
+    cmd_list ();
+  else
+    {
+      if (! strcmp (argv[optind], "edit"))
+        cmd_edit (argc - optind - 1, argv + optind + 1);
+      else if (! strcmp (argv[optind], "clear"))
+        cmd_clear ();
+
+      fseek (f, 0, SEEK_SET);
+      fwrite (buffer, sizeof (buffer), 1, f);
+    }
+  fclose (f);
+
+  return 0;
+}
diff --git a/util/grub-emu.c b/util/grub-emu.c
index 00a2c49..aa7ceb9 100644
--- a/util/grub-emu.c
+++ b/util/grub-emu.c
@@ -36,15 +36,13 @@
 #include <grub/util/getroot.h>
 #include <grub/env.h>
 #include <grub/partition.h>
+#include <grub/envblk.h>
 
 #include <grub_emu_init.h>
 
 /* Used for going back to the main function.  */
 jmp_buf main_env;
 
-/* Store the prefix specified by an argument.  */
-static char *prefix = 0;
-
 grub_addr_t
 grub_arch_modules_addr (void)
 {
@@ -76,11 +74,15 @@ grub_machine_init (void)
 }
 
 void
-grub_machine_set_prefix (void)
+grub_machine_set_root (void)
 {
-  grub_env_set ("prefix", prefix);
-  free (prefix);
-  prefix = 0;
+  /* Do nothing, as root is already set.  */
+}
+
+char *
+grub_machine_get_envblk (void)
+{
+  return 0;
 }
 
 void
@@ -205,8 +207,8 @@ main (int argc, char *argv[])
     }
 
   dir = grub_get_prefix (dir);
-  prefix = xmalloc (strlen (root_dev) + 2 + strlen (dir) + 1);
-  sprintf (prefix, "(%s)%s", root_dev, dir);
+  grub_env_set (GRUB_ENVBLK_RDIR, dir);
+  grub_env_set ("root", root_dev);
   free (dir);
   
   /* Start GRUB!  */
diff --git a/util/i386/pc/grub-mkimage.c b/util/i386/pc/grub-mkimage.c
index 48d6dfc..f71924b 100644
--- a/util/i386/pc/grub-mkimage.c
+++ b/util/i386/pc/grub-mkimage.c
@@ -27,6 +27,7 @@
 #include <grub/util/misc.h>
 #include <grub/util/resolve.h>
 #include <grub/misc.h>
+#include <grub/envblk.h>
 
 #include <stdio.h>
 #include <unistd.h>
@@ -109,9 +110,10 @@ generate_image (const char *dir, char *prefix, FILE *out, char *mods[], char *me
   kernel_img = xmalloc (kernel_size + total_module_size + memdisk_size);
   grub_util_load_image (kernel_path, kernel_img);
 
-  if (GRUB_KERNEL_MACHINE_PREFIX + strlen (prefix) + 1 > GRUB_KERNEL_MACHINE_DATA_END)
+  if (GRUB_KERNEL_MACHINE_ENVBLK + sizeof (GRUB_ENVBLK_RDIR) + 2 + strlen (prefix) > GRUB_KERNEL_MACHINE_DATA_END)
     grub_util_error ("prefix too long");
-  strcpy (kernel_img + GRUB_KERNEL_MACHINE_PREFIX, prefix);
+  strcpy (kernel_img + GRUB_KERNEL_MACHINE_ENVBLK, GRUB_ENVBLK_RDIR "=");
+  strcpy (kernel_img + GRUB_KERNEL_MACHINE_ENVBLK + sizeof (GRUB_ENVBLK_RDIR), prefix);
 
   /* Fill in the grub_module_info structure.  */
   modinfo = (struct grub_module_info *) (kernel_img + kernel_size);
diff --git a/util/i386/pc/grub-setup.c b/util/i386/pc/grub-setup.c
index 535a8d0..2a243e8 100644
--- a/util/i386/pc/grub-setup.c
+++ b/util/i386/pc/grub-setup.c
@@ -34,6 +34,7 @@
 #include <grub/term.h>
 #include <grub/util/raid.h>
 #include <grub/util/lvm.h>
+#include <grub/envblk.h>
 
 static const grub_gpt_part_type_t grub_gpt_partition_type_bios_boot = GRUB_GPT_PARTITION_TYPE_BIOS_BOOT;
 
@@ -235,7 +236,10 @@ setup (const char *prefix, const char *dir,
   install_bsd_part = (grub_int32_t *) (core_img + GRUB_DISK_SECTOR_SIZE
 				       + GRUB_KERNEL_MACHINE_INSTALL_BSD_PART);
   install_prefix = (core_img + GRUB_DISK_SECTOR_SIZE
-		    + GRUB_KERNEL_MACHINE_PREFIX);
+		    + GRUB_KERNEL_MACHINE_ENVBLK);
+
+  strcpy (install_prefix, GRUB_ENVBLK_RDIR "=");
+  install_prefix += sizeof (GRUB_ENVBLK_RDIR);
 
   /* Open the root device and the destination device.  */
   root_dev = grub_device_open (root);
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to