Hi, This is the new patch, it contain the following changes:
1. command edit -> set in grub-editenv, as it seems more natural. grub-editenv FILE set name=value ... 2. findroot can locate the root device using filename. To use it, just set idfile to any file inside the directory, For example: idfile=normal.mod Scan preference: uuid > label > idfile 3. Change the order device are reported in biosdisk, so that floppy appear last. 4. Add new command loadenv, which can be used to load/save environment variable. load_env [-f FILE] Load environment variable from external environment block file. The default file is $prefix/grubenv, but you can overwrite it with -f parameter. list_env [-f FILE] List the variables in external environment block file. save_env [-f FILE] variable_name .. Save the value of variable_name to external environment block file So, to archive the effect of savedefault command in grub legacy, you can use: load_env .. menuentry aa { .. save_env default } You can also use save_env to save the variable directly to core.img, but it's not recommended. The environment block inside core.img is quite small, it's mainly used to store critical variable like rdir, uuid, label and idfile. -- 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/commands/loadenv.c b/commands/loadenv.c new file mode 100755 index 0000000..970baf3 --- /dev/null +++ b/commands/loadenv.c @@ -0,0 +1,250 @@ +/* loadenv.c - command to load/save environment variable. */ +/* + * 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/normal.h> +#include <grub/dl.h> +#include <grub/mm.h> +#include <grub/arg.h> +#include <grub/file.h> +#include <grub/disk.h> +#include <grub/misc.h> +#include <grub/env.h> +#include <grub/envblk.h> + +static const struct grub_arg_option options[] = + { + {"file", 'f', 0, "specify filename", 0, ARG_TYPE_PATHNAME}, + {0, 0, 0, 0, 0, 0} + }; + +char buffer[GRUB_ENVBLK_MAXLEN]; +grub_envblk_t envblk; + +static grub_file_t +read_envblk_file (char *filename, void NESTED_FUNC_ATTR read_hook (grub_disk_addr_t sector, unsigned offset, unsigned length)) +{ + char *buf = 0; + grub_file_t file; + + if (! filename) + { + char *prefix; + + prefix = grub_env_get ("prefix"); + if (prefix) + { + int len; + + len = grub_strlen (prefix); + buf = grub_malloc (len + 1 + sizeof (GRUB_ENVBLK_DEFCFG)); + grub_strcpy (buf, prefix); + buf[len] = '/'; + grub_strcpy (buf + len + 1, GRUB_ENVBLK_DEFCFG); + filename = buf; + } + else + { + grub_error (GRUB_ERR_FILE_NOT_FOUND, "prefix is not found"); + return 0; + } + } + + file = grub_file_open (filename); + grub_free (buf); + if (! file) + return 0; + + if (read_hook) + { + if (! file->device->disk) + { + grub_file_close (file); + grub_error (GRUB_ERR_BAD_DEVICE, + "this command is available only for disk devices."); + return 0; + } + file->read_hook = read_hook; + } + + if (grub_file_read (file, buffer, GRUB_ENVBLK_MAXLEN) != GRUB_ENVBLK_MAXLEN) + { + grub_file_close (file); + grub_error (GRUB_ERR_BAD_FILE_TYPE, "file too short"); + return 0; + } + + envblk = grub_envblk_find (buffer); + if (! envblk) + { + grub_file_close (file); + grub_error (GRUB_ERR_BAD_FILE_TYPE, "environment block not found"); + return 0; + } + + return file; +} + +static grub_err_t +grub_cmd_load_env (struct grub_arg_list *state, + int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) + +{ + grub_file_t file; + + auto int hook (char *name, char *value); + int hook (char *name, char *value) + { + grub_env_set (name, value); + + return 0; + } + + file = read_envblk_file ((state[0].set) ? state[0].arg : 0, 0); + if (! file) + return grub_errno; + + grub_file_close (file); + + grub_envblk_iterate (envblk, hook); + + return grub_errno; +} + +static grub_err_t +grub_cmd_list_env (struct grub_arg_list *state, + int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) +{ + grub_file_t file; + + auto int hook (char *name, char *value); + int hook (char *name, char *value) + { + grub_printf ("%s=%s\n", name, value); + + return 0; + } + + file = read_envblk_file ((state[0].set) ? state[0].arg : 0, 0); + if (! file) + return grub_errno; + + grub_file_close (file); + + grub_envblk_iterate (envblk, hook); + + return grub_errno; +} + +static grub_err_t +grub_cmd_save_env (struct grub_arg_list *state, int argc, char **args) +{ + grub_file_t file; + grub_disk_t disk; + grub_disk_addr_t addr[GRUB_ENVBLK_MAXLEN >> GRUB_DISK_SECTOR_BITS]; + char buf[GRUB_DISK_SECTOR_SIZE]; + int num = 0; + + auto void NESTED_FUNC_ATTR hook (grub_disk_addr_t sector, unsigned offset, + unsigned length); + + void NESTED_FUNC_ATTR hook (grub_disk_addr_t sector, + unsigned offset, unsigned length) + { + if ((offset != 0) || (length != GRUB_DISK_SECTOR_SIZE)) + return; + + if (num < (GRUB_ENVBLK_MAXLEN >> GRUB_DISK_SECTOR_BITS)) + addr[num++] = sector; + } + + if (! argc) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "No variable is specified"); + + file = read_envblk_file ((state[0].set) ? state[0].arg : 0, hook); + if (! file) + return grub_errno; + + file->read_hook = 0; + + if (num != GRUB_ENVBLK_MAXLEN >> GRUB_DISK_SECTOR_BITS) + { + grub_error (GRUB_ERR_BAD_DEVICE, "invalid blocklist"); + goto quit; + } + + disk = file->device->disk; + for (num = 0; num < (GRUB_ENVBLK_MAXLEN >> GRUB_DISK_SECTOR_BITS); num++) + { + if (disk->dev->read (disk, addr[num], 1, buf)) + goto quit; + + if (grub_memcmp (&buffer[num << GRUB_DISK_SECTOR_BITS], buf, + GRUB_DISK_SECTOR_SIZE)) + { + grub_error (GRUB_ERR_BAD_DEVICE, "invalid blocklist"); + goto quit; + } + } + + while (argc) + { + char *value; + + value = grub_env_get (args[0]); + if (value) + { + if (grub_envblk_insert (envblk, args[0], value)) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "environment block too small"); + goto quit; + } + } + + argc--; + args++; + } + + for (num = 0; num < (GRUB_ENVBLK_MAXLEN >> GRUB_DISK_SECTOR_BITS); num++) + if (disk->dev->write (disk, addr[num], 1, + &buffer[num << GRUB_DISK_SECTOR_BITS])) + goto quit; + +quit: + grub_file_close (file); + + return grub_errno; +} + +GRUB_MOD_INIT(loadenv) +{ + (void) mod; + grub_register_command ("load_env", grub_cmd_load_env, GRUB_COMMAND_FLAG_BOTH, + "load_env [-f FILE]", "Load variables from environment block file.", options); + grub_register_command ("list_env", grub_cmd_list_env, GRUB_COMMAND_FLAG_BOTH, + "list_env [-f FILE]", "List variables from environment block file.", options); + grub_register_command ("save_env", grub_cmd_save_env, GRUB_COMMAND_FLAG_BOTH, + "save_env [-f FILE] variable_name [...]", "Save variables to environment block file.", options); +} + +GRUB_MOD_FINI(loadenv) +{ + grub_unregister_command ("load_env"); + grub_unregister_command ("list_env"); + grub_unregister_command ("save_env"); +} diff --git a/conf/common.rmk b/conf/common.rmk index acbebc7..99252ac 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -94,6 +94,11 @@ 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 +CLEANFILES += grub-editenv + # For update-grub update-grub: util/update-grub.in config.status ./config.status --file=$@:$< @@ -282,7 +287,7 @@ pkglib_MODULES += hello.mod boot.mod terminal.mod ls.mod \ cmp.mod cat.mod help.mod font.mod search.mod \ loopback.mod configfile.mod echo.mod \ terminfo.mod test.mod blocklist.mod hexdump.mod \ - read.mod sleep.mod + read.mod sleep.mod loadenv.mod # For hello.mod. hello_mod_SOURCES = hello/hello.c @@ -364,8 +369,23 @@ hexdump_mod_SOURCES = commands/hexdump.c hexdump_mod_CFLAGS = $(COMMON_CFLAGS) hexdump_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For read.mod. +read_mod_SOURCES = commands/read.c +read_mod_CFLAGS = $(COMMON_CFLAGS) +read_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For sleep.mod. +sleep_mod_SOURCES = commands/sleep.c +sleep_mod_CFLAGS = $(COMMON_CFLAGS) +sleep_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For loadenv.mod. +loadenv_mod_SOURCES = commands/loadenv.c util/envblk.c +loadenv_mod_CFLAGS = $(COMMON_CFLAGS) +loadenv_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 @@ -377,12 +397,7 @@ gzio_mod_SOURCES = io/gzio.c gzio_mod_CFLAGS = $(COMMON_CFLAGS) gzio_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For read.mod. -read_mod_SOURCES = commands/read.c -read_mod_CFLAGS = $(COMMON_CFLAGS) -read_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For sleep.mod. -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/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c index 094bde0..6e5292f 100644 --- a/disk/i386/pc/biosdisk.c +++ b/disk/i386/pc/biosdisk.c @@ -66,12 +66,6 @@ grub_biosdisk_iterate (int (*hook) (const char *name)) int drive; int num_floppies; - /* For floppy disks, we can get the number safely. */ - num_floppies = grub_biosdisk_get_num_floppies (); - for (drive = 0; drive < num_floppies; drive++) - if (grub_biosdisk_call_hook (hook, drive)) - return 1; - /* For hard disks, attempt to read the MBR. */ for (drive = 0x80; drive < 0x90; drive++) { @@ -92,6 +86,12 @@ grub_biosdisk_iterate (int (*hook) (const char *name)) return 1; } + /* For floppy disks, we can get the number safely. */ + num_floppies = grub_biosdisk_get_num_floppies (); + for (drive = 0; drive < num_floppies; drive++) + if (grub_biosdisk_call_hook (hook, drive)) + return 1; + return 0; } diff --git a/include/grub/envblk.h b/include/grub/envblk.h new file mode 100755 index 0000000..e075089 --- /dev/null +++ b/include/grub/envblk.h @@ -0,0 +1,51 @@ +/* + * 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" +#define GRUB_ENVBLK_IDFILE "idfile" + +#define GRUB_ENVBLK_DEFCFG "grubenv" + +#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); +void grub_envblk_iterate (grub_envblk_t envblk, int hook (char *name, char *value)); + +#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..44d5eb8 --- /dev/null +++ b/kern/findroot.c @@ -0,0 +1,110 @@ +/* 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> +#include <grub/envblk.h> + +static void +findroot (void) +{ + char *label, *uuid, *idfile, *rdir; + int found = 0; + + auto int iterate_rdir (const char *filename, int dir); + int iterate_rdir (const char *filename, int dir) + { + (void) dir; + + found = (! grub_strcmp (filename, idfile)); + return found; + } + + auto int iterate_device (const char *name); + int iterate_device (const char *name) + { + 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); + } + } + else if (idfile) + fs->dir (dev, rdir, iterate_rdir); + + if (found) + grub_env_set ("root", name); + } + + grub_device_close (dev); + } + + grub_errno = GRUB_ERR_NONE; + return found; + } + + uuid = grub_env_get (GRUB_ENVBLK_UUID); + label = grub_env_get (GRUB_ENVBLK_LABEL); + idfile = grub_env_get (GRUB_ENVBLK_IDFILE); + rdir = grub_env_get (GRUB_ENVBLK_RDIR); + if (! rdir) + rdir = "/"; + + if ((! label) && (! uuid) && (! idfile)) + 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..5b27062 --- /dev/null +++ b/util/envblk.c @@ -0,0 +1,171 @@ +/* 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/envblk.h> + +#ifdef GRUB_UTIL + +#include <string.h> + +#define grub_strlen strlen +#define grub_strcpy strcpy +#define grub_strchr strchr +#define grub_memcmp memcmp +#define grub_memcpy memcpy + +#else + +#include <grub/misc.h> + +#endif + +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 += grub_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 += grub_strlen (p) + 1; + if (p >= pend) + return; + } + + if (found) + { + int len; + + len = grub_strlen (found); + grub_memcpy (found, found + len + 1, (p - found) - len); + } +} + +void +grub_envblk_iterate (grub_envblk_t envblk, + int hook (char *name, char *value)) +{ + char *p, *pend; + + p = envblk->data; + pend = p + envblk->length; + + while (*p) + { + char *v; + int r; + + v = grub_strchr (p, '='); + if (v) + { + *v = 0; + r = hook (p, v + 1); + *v = '='; + } + else + r = hook (p, ""); + + if (r) + break; + + p += grub_strlen (p) + 1; + if (p >= pend) + break; + } +} diff --git a/util/grub-editenv.c b/util/grub-editenv.c new file mode 100755 index 0000000..8c08694 --- /dev/null +++ b/util/grub-editenv.c @@ -0,0 +1,228 @@ +/* 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> + +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\ + set [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, "wb"); + 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+b"); + 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", envblk->data - buffer); + printf ("Envblk length: %d\n", envblk->length); +} + +static void +cmd_list (void) +{ + auto int hook (char *name, char *value); + int hook (char *name, char *value) + { + printf ("%s=%s\n", name, value); + return 0; + } + + grub_envblk_iterate (envblk, hook); +} + +static void +cmd_set (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], "set")) + cmd_set (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