[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH] Improve ext2 driver to allow embedding of the boot loader code.
From: |
Dr. Tilmann Bubeck |
Subject: |
[PATCH] Improve ext2 driver to allow embedding of the boot loader code. |
Date: |
Thu, 9 Jan 2014 22:07:37 +0100 |
This patch extends the ext2 driver to allow embedding core.img into the
filesystem. This allows the long needed installation of GRUB into a partition
without the need to use (unsafe) block lists.
It is realized using the ioctl(EXT4_IOC_SWAP_BOOT) introduced into
Linux 3.10. This ioctl basically swaps the data blocks of the associated
file with the EXT2_BOOT_LOADER_INO inode. After using the ioctl the code
of core.img is stored in the BOOT_LOADER incode of the filesystem and it
is not accessible to file system tools anymore. This ensures, that the boot
code is safe and installation into a partition is possible.
The patchs needs 0001-Refactor-grub_read_mountinfo-out-of-grub_find_root_d.patch
to work correctly.
Signed-off-by: Dr. Tilmann Bubeck <address@hidden>
---
grub-core/fs/ext2.c | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++--
util/setup.c | 11 ++-
2 files changed, 215 insertions(+), 8 deletions(-)
diff --git a/grub-core/fs/ext2.c b/grub-core/fs/ext2.c
index 5f7a2b9..643969e 100644
--- a/grub-core/fs/ext2.c
+++ b/grub-core/fs/ext2.c
@@ -133,6 +133,14 @@ GRUB_MOD_LICENSE ("GPLv3+");
#define EXT4_EXTENTS_FLAG 0x80000
+/*
+ * Special inodes numbers
+ */
+#define EXT2_ROOT_INO 2 /* Root inode */
+#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */
+
+#define EXT4_IOC_SWAP_BOOT _IO('f', 17)
+
/* The ext2 superblock. */
struct grub_ext2_sblock
{
@@ -393,10 +401,10 @@ grub_ext4_find_leaf (struct grub_ext2_data *data,
}
static grub_disk_addr_t
-grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
+grub_ext2_read_block2 (struct grub_ext2_data *data,
+ struct grub_ext2_inode *inode,
+ grub_disk_addr_t fileblock)
{
- struct grub_ext2_data *data = node->data;
- struct grub_ext2_inode *inode = &node->inode;
unsigned int blksz = EXT2_BLOCK_SIZE (data);
grub_disk_addr_t blksz_quarter = blksz / 4;
int log2_blksz = LOG2_EXT2_BLOCK_SIZE (data);
@@ -497,6 +505,12 @@ indirect:
return grub_le_to_cpu32 (indir);
}
+static grub_disk_addr_t
+grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
+{
+ return grub_ext2_read_block2(node->data, &node->inode, fileblock);
+}
+
/* Read LEN bytes from the file described by DATA starting with byte
POS. Return the amount of read bytes in READ. */
static grub_ssize_t
@@ -607,12 +621,12 @@ grub_ext2_mount (grub_disk_t disk)
data->disk = disk;
data->diropen.data = data;
- data->diropen.ino = 2;
+ data->diropen.ino = EXT2_ROOT_INO;
data->diropen.inode_read = 1;
data->inode = &data->diropen.inode;
- grub_ext2_read_inode (data, 2, data->inode);
+ grub_ext2_read_inode (data, EXT2_ROOT_INO, data->inode);
if (grub_errno)
goto fail;
@@ -977,6 +991,193 @@ grub_ext2_mtime (grub_device_t device, grub_int32_t *tm)
}
+#ifdef GRUB_UTIL
+
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <include/grub/emu/misc.h>
+#include <include/grub/emu/getroot.h>
+#include <include/grub/emu/hostfile.h>
+
+/* Read the addresses of all sectors occupied by the file with the
+ given inode from the filesystem. Return the number of sectors in
+ "nsector" and the addresses in "sectors". "sectors" is allocated
+ in this function and must be freed by the caller after usage.
+ A sector in sectors has size GRUB_DISK_SECTOR_SIZE. */
+static grub_err_t
+grub_ext2_read_sectorlist(grub_device_t device,
+ int ino,
+ unsigned int *nsectors,
+ grub_disk_addr_t **sectors)
+{
+ struct grub_ext2_data *data;
+ struct grub_ext2_inode inode;
+ grub_off_t size;
+ grub_err_t err;
+ grub_disk_addr_t fileblock;
+ int i;
+ grub_disk_addr_t fileblock_count;
+ int sectors_per_block;
+
+ data = grub_ext2_mount (device->disk);
+ if (! data) {
+ return grub_error (grub_errno, N_("Unable to mount device"));
+ }
+
+ err = grub_ext2_read_inode (data, ino, &inode);
+ if (err)
+ return grub_error (err, N_("Unable to read inode #%d"), ino);
+
+ size = grub_le_to_cpu32 (inode.size);
+ size |= ((grub_off_t) grub_le_to_cpu32 (inode.size_high)) << 32;
+
+ fileblock_count = size / EXT2_BLOCK_SIZE(data);
+ if ( size % EXT2_BLOCK_SIZE(data) != 0 ) fileblock_count++;
+
+ sectors_per_block = EXT2_BLOCK_SIZE(data) / GRUB_DISK_SECTOR_SIZE;
+ *sectors = grub_malloc (fileblock_count * sectors_per_block
+ * sizeof (**sectors));
+ *nsectors = 0;
+ for ( fileblock = 0; fileblock < fileblock_count; fileblock++ ) {
+ (*sectors)[*nsectors] = grub_ext2_read_block2 (data, &inode, fileblock)
+ * sectors_per_block;
+ for ( i = 1; i < sectors_per_block; i++) {
+ (*sectors)[(*nsectors) + i] = (*sectors)[*nsectors] + i;
+ }
+ (*nsectors) += sectors_per_block;
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_ext2_embed (grub_device_t device,
+ unsigned int *nsectors,
+ unsigned int max_nsectors,
+ grub_embed_type_t embed_type,
+ grub_disk_addr_t **sectors)
+{
+ unsigned i;
+ grub_err_t err;
+ char *mountpoint;
+ struct mountinfo_entry *entries;
+ grub_util_fd_t out;
+ char *core_name;
+ char diskbuffer[GRUB_DISK_SECTOR_SIZE];
+ unsigned int nsectors_wanted;
+ char *device_name;
+ int ioctl_err;
+
+ if (embed_type != GRUB_EMBED_PCBIOS)
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ N_("ext2 currently supports only PC-BIOS embedding"));
+
+ if ( device->disk->partition)
+ device_name = grub_xasprintf ("%s,%s", device->disk->name,
+ grub_partition_get_name(device->disk->partition));
+ else
+ device_name = grub_xasprintf ("%s", device->disk->name);
+
+ nsectors_wanted = *nsectors;
+
+ grub_util_info (N_("Embedding %d/%d sectors into ext2 filesystem of %s"),
+ nsectors_wanted, max_nsectors, device_name);
+
+ /* [1] Check, if the existing boot loader inode exists and has the
+ wanted size: */
+ err = grub_ext2_read_sectorlist (device, EXT2_BOOT_LOADER_INO,
+ nsectors, sectors);
+ if (!err && *nsectors >= nsectors_wanted && *nsectors <= max_nsectors) {
+ grub_util_info(N_("Reusing existing boot loader inode"
+ " offering %d sectors"),
+ *nsectors);
+ grub_free (device_name);
+ return GRUB_ERR_NONE; /* YES! Everything is fine. */
+ }
+
+ /* [2] We have to create a new boot loader inode. */
+
+ /* [2.1] Check if device is mounted and get mountpoint: */
+ mountpoint = NULL;
+ entries = grub_read_mountinfo ();
+ if ( entries ) {
+ for ( i = 0; entries[i].enc_root[0] != 0; i++) {
+ char *grub_dev_of_mount;
+ grub_errno = GRUB_ERR_NONE; /* Clear errno set previously */
+ grub_dev_of_mount = grub_util_get_grub_dev (entries[i].device);
+ if ( grub_dev_of_mount ) {
+ if ( grub_strcmp (grub_dev_of_mount, device_name) == 0 ) {
+ mountpoint = grub_strdup (entries[i].enc_path);
+ break;
+ }
+ }
+ }
+ free (entries);
+ }
+
+ grub_errno = GRUB_ERR_NONE; /* Clear errno set previously */
+
+ if (!mountpoint) {
+ /* We were unable to find the mountpoint for the device of the
+ filesystem. Maybe it is not mounted? */
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+ N_("We are unable to find the mountpoint of %s"),
+ device_name);
+
+ /** ToDo: Try to mount the filesystem to a temporary location. */
+ }
+
+ grub_free (device_name);
+
+ /* [2.2] Create a new (temporary) file, which then gets the boot loader: */
+ core_name = grub_util_path_concat (2, mountpoint, ".core.img");
+ free (mountpoint);
+ out = grub_util_fd_open (core_name, GRUB_UTIL_FD_O_WRONLY
+ | GRUB_UTIL_FD_O_CREATTRUNC);
+ if (!GRUB_UTIL_FD_IS_VALID (out)) {
+ return grub_error (GRUB_ERR_BAD_FS,
+ N_("cannot create `%s': %s"), core_name,
+ grub_util_fd_strerror ());
+ }
+ for ( i = 0; i < max_nsectors; i++ ) {
+ grub_util_fd_write (out, diskbuffer, GRUB_DISK_SECTOR_SIZE);
+ }
+ grub_util_fd_sync (out);
+
+ /* [2.3] Make that file to the new boot loader inode by swapping the
+ file of "out" with the boot loader inode: */
+ ioctl_err = ioctl (out, EXT4_IOC_SWAP_BOOT);
+ if ( ioctl_err ) {
+ err = grub_error (GRUB_ERR_BAD_FS,
+ N_("Error in ioctl(EXT4_IOC_SWAP_BOOT);"
+ "you need Linux >= 3.10: %s"),
+ grub_util_fd_strerror ());
+ grub_util_fd_close (out);
+ grub_util_unlink (core_name);
+ return err;
+ }
+ grub_util_fd_close (out);
+
+ /* [2.4] Unlink the core file, now containing the previous boot loader. */
+ grub_util_unlink (core_name);
+
+ /* [2.5] Invalidate disk cache and read block list again: */
+ grub_disk_cache_invalidate_all ();
+
+ err = grub_ext2_read_sectorlist (device, EXT2_BOOT_LOADER_INO,
+ nsectors, sectors);
+ if ( err ) {
+ return grub_error (GRUB_ERR_BAD_FS,
+ N_("unable to read boot loader inode"));
+ }
+
+ grub_util_info (N_("Created new boot loader inode offering %d sectors"),
+ *nsectors);
+
+ return GRUB_ERR_NONE;
+}
+#endif
+
static struct grub_fs grub_ext2_fs =
@@ -990,6 +1191,7 @@ static struct grub_fs grub_ext2_fs =
.uuid = grub_ext2_uuid,
.mtime = grub_ext2_mtime,
#ifdef GRUB_UTIL
+ .embed = grub_ext2_embed,
.reserved_first_sector = 1,
.blocklist_install = 1,
#endif
diff --git a/util/setup.c b/util/setup.c
index 9fb91a8..3f4a007 100644
--- a/util/setup.c
+++ b/util/setup.c
@@ -509,8 +509,10 @@ SETUP (const char *dir,
if (!err && nsec < core_sectors)
{
err = grub_error (GRUB_ERR_OUT_OF_RANGE,
- N_("Your embedding area is unusually small. "
- "core.img won't fit in it."));
+ N_("Your embedding area is unusually small "
+ "(only %d sectors). "
+ "core.img won't fit in it (needs %d sectors)."),
+ nsec, core_sectors);
}
if (err)
@@ -583,10 +585,13 @@ SETUP (const char *dir,
}
/* Write the core image onto the disk. */
- for (i = 0; i < nsec; i++)
+ for (i = 0; i < nsec; i++) {
+ grub_util_info ("writing core.img/%d to sector %" PRIuGRUB_UINT64_T, i,
+ sectors[i]);
grub_disk_write (dest_dev->disk, sectors[i], 0,
GRUB_DISK_SECTOR_SIZE,
core_img + i * GRUB_DISK_SECTOR_SIZE);
+ }
grub_free (sectors);
--
1.8.1.4
- [PATCH] Improve ext2 driver to allow embedding of the boot loader code.,
Dr. Tilmann Bubeck <=
- Re: [PATCH] Improve ext2 driver to allow embedding of the boot loader code., Vladimir 'φ-coder/phcoder' Serbinenko, 2014/01/09
- Re: [PATCH] Improve ext2 driver to allow embedding of the boot loader code., Dr. Tilmann Bubeck, 2014/01/10
- Re: [PATCH] Improve ext2 driver to allow embedding of the boot loader code., Vladimir 'φ-coder/phcoder' Serbinenko, 2014/01/21
- Re: [PATCH] Improve ext2 driver to allow embedding of the boot loader code., Andrey Borzenkov, 2014/01/21
- Re: [PATCH] Improve ext2 driver to allow embedding of the boot loader code., Vladimir 'φ-coder/phcoder' Serbinenko, 2014/01/21
- Re: [PATCH] Improve ext2 driver to allow embedding of the boot loader code., Andrey Borzenkov, 2014/01/21
- Re: [PATCH] Improve ext2 driver to allow embedding of the boot loader code., Vladimir 'φ-coder/phcoder' Serbinenko, 2014/01/21
- Re: [PATCH] Improve ext2 driver to allow embedding of the boot loader code., Dr. Tilmann Bubeck, 2014/01/21