On 09.01.2014 22:07, Dr. Tilmann Bubeck wrote: > 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. > I've already commented on this design here: it not only doesn't guarantee any kind of blockstability that we need but guarantees quite the opposite in several scenarios (detailed in previous mails). > The patchs needs > 0001-Refactor-grub_read_mountinfo-out-of-grub_find_root_d.patch > to work correctly. > > Signed-off-by: Dr. Tilmann Bubeck <tilm...@bubecks.de> > --- > 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); > >
signature.asc
Description: OpenPGP digital signature
_______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel