From 5a869940dea4f34be0aeee5e017aca87d2757a92 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Tue, 15 Oct 2013 17:02:26 +0200 Subject: [PATCH] Split out blocklist retrieving from setup.c to grub-core/osdep/blocklist.c and add windows implementation since generic version doesn't work on NTFS on Windows due to aggressive unflushable cache. --- ChangeLog | 7 + Makefile.util.def | 5 + grub-core/fs/fat.c | 48 +++-- grub-core/fs/udf.c | 19 ++ grub-core/osdep/blocklist.c | 7 + grub-core/osdep/generic/blocklist.c | 132 ++++++++++++ grub-core/osdep/linux/blocklist.c | 144 +++++++++++++ grub-core/osdep/windows/blocklist.c | 119 +++++++++++ include/grub/exfat.h | 53 +++++ include/grub/fat.h | 7 + include/grub/udf.h | 30 +++ include/grub/util/install.h | 11 + util/setup.c | 308 ++++------------------------ 13 files changed, 602 insertions(+), 288 deletions(-) create mode 100644 grub-core/osdep/blocklist.c create mode 100644 grub-core/osdep/generic/blocklist.c create mode 100644 grub-core/osdep/linux/blocklist.c create mode 100644 grub-core/osdep/windows/blocklist.c create mode 100644 include/grub/exfat.h create mode 100644 include/grub/udf.h diff --git a/ChangeLog b/ChangeLog index 7e76edb2e..92ef4b0b4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2013-10-15 Vladimir Serbinenko + + Split out blocklist retrieving from setup.c to + grub-core/osdep/blocklist.c and add windows implementation since + generic version doesn't work on NTFS on Windows due to aggressive + unflushable cache. + 2013-10-15 Vladimir Serbinenko Split grub-setup.c into frontend (grub-setup.c) and backend (setup.c) diff --git a/Makefile.util.def b/Makefile.util.def index 81bbb23ff..0e7974ffe 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -320,6 +320,10 @@ program = { common = util/setup_bios.c; common = grub-core/kern/emu/argp_common.c; common = grub-core/lib/reed_solomon.c; + common = grub-core/osdep/blocklist.c; + extra_dist = grub-core/osdep/generic/blocklist.c; + extra_dist = grub-core/osdep/linux/blocklist.c; + extra_dist = grub-core/osdep/windows/blocklist.c; common = grub-core/osdep/init.c; ldadd = libgrubmods.a; @@ -339,6 +343,7 @@ program = { common = grub-core/kern/emu/argp_common.c; common = grub-core/lib/reed_solomon.c; common = grub-core/osdep/ofpath.c; + common = grub-core/osdep/blocklist.c; common = grub-core/osdep/init.c; ldadd = libgrubmods.a; diff --git a/grub-core/fs/fat.c b/grub-core/fs/fat.c index a26a9edf8..e84f6c672 100644 --- a/grub-core/fs/fat.c +++ b/grub-core/fs/fat.c @@ -26,7 +26,11 @@ #include #include #include +#ifndef MODE_EXFAT #include +#else +#include +#endif #include GRUB_MOD_LICENSE ("GPLv3+"); @@ -60,27 +64,6 @@ enum }; #ifdef MODE_EXFAT -struct grub_exfat_bpb -{ - grub_uint8_t jmp_boot[3]; - grub_uint8_t oem_name[8]; - grub_uint8_t mbz[53]; - grub_uint64_t num_hidden_sectors; - grub_uint64_t num_total_sectors; - grub_uint32_t num_reserved_sectors; - grub_uint32_t sectors_per_fat; - grub_uint32_t cluster_offset; - grub_uint32_t cluster_count; - grub_uint32_t root_cluster; - grub_uint32_t num_serial; - grub_uint16_t fs_revision; - grub_uint16_t volume_flags; - grub_uint8_t bytes_per_sector_shift; - grub_uint8_t sectors_per_cluster_shift; - grub_uint8_t num_fats; - grub_uint8_t num_ph_drive; - grub_uint8_t reserved[8]; -} __attribute__ ((packed)); typedef struct grub_exfat_bpb grub_current_fat_bpb_t; #else typedef struct grub_fat_bpb grub_current_fat_bpb_t; @@ -1208,6 +1191,29 @@ grub_fat_uuid (grub_device_t device, char **uuid) return grub_errno; } +#ifdef GRUB_UTIL +#ifndef MODE_EXFAT +grub_disk_addr_t +grub_fat_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn) +#else +grub_disk_addr_t + grub_exfat_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn) +#endif +{ + grub_disk_addr_t ret; + struct grub_fat_data *data; + data = grub_fat_mount (disk); + if (!data) + return 0; + ret = data->cluster_sector; + + *sec_per_lcn = 1ULL << data->cluster_bits; + + grub_free (data); + return ret; +} +#endif + static struct grub_fs grub_fat_fs = { #ifdef MODE_EXFAT diff --git a/grub-core/fs/udf.c b/grub-core/fs/udf.c index 405935efb..8f3305367 100644 --- a/grub-core/fs/udf.c +++ b/grub-core/fs/udf.c @@ -27,6 +27,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -800,6 +801,24 @@ fail: return 0; } +#ifdef GRUB_UTIL +grub_disk_addr_t +grub_udf_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn) +{ + grub_disk_addr_t ret; + static struct grub_udf_data *data; + + data = grub_udf_mount (disk); + if (!data) + return 0; + + ret = U32 (data->pds[data->pms[0]->type1.part_num].start); + *sec_per_lcn = 1ULL << data->lbshift; + grub_free (data); + return ret; +} +#endif + static char * read_string (const grub_uint8_t *raw, grub_size_t sz, char *outbuf) { diff --git a/grub-core/osdep/blocklist.c b/grub-core/osdep/blocklist.c new file mode 100644 index 000000000..1f58b0f3a --- /dev/null +++ b/grub-core/osdep/blocklist.c @@ -0,0 +1,7 @@ +#ifdef __linux__ +#include "linux/blocklist.c" +#elif defined (__MINGW32__) || defined (__CYGWIN__) +#include "windows/blocklist.c" +#else +#include "generic/blocklist.c" +#endif diff --git a/grub-core/osdep/generic/blocklist.c b/grub-core/osdep/generic/blocklist.c new file mode 100644 index 000000000..623243e7a --- /dev/null +++ b/grub-core/osdep/generic/blocklist.c @@ -0,0 +1,132 @@ +/* grub-setup.c - make GRUB usable */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,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 . + */ + +#include + +#include +#include +#include +#include +#include +#include + +#define MAX_TRIES 5 + +void +grub_install_get_blocklist (grub_device_t root_dev, + const char *core_path, const char *core_img, + size_t core_size, + void (*callback) (grub_disk_addr_t sector, + unsigned offset, + unsigned length, + void *data), + void *hook_data) +{ + int i; + char *tmp_img; + char *core_path_dev; + + core_path_dev = grub_make_system_path_relative_to_its_root (core_path); + + /* Make sure that GRUB reads the identical image as the OS. */ + tmp_img = xmalloc (core_size); + + for (i = 0; i < MAX_TRIES; i++) + { + grub_file_t file; + + grub_util_info ((i == 0) ? _("attempting to read the core image `%s' from GRUB") + : _("attempting to read the core image `%s' from GRUB again"), + core_path_dev); + + grub_disk_cache_invalidate_all (); + + grub_file_filter_disable_compression (); + file = grub_file_open (core_path_dev); + if (file) + { + if (grub_file_size (file) != core_size) + grub_util_info ("succeeded in opening the core image but the size is different (%d != %d)", + (int) grub_file_size (file), (int) core_size); + else if (grub_file_read (file, tmp_img, core_size) + != (grub_ssize_t) core_size) + grub_util_info ("succeeded in opening the core image but cannot read %d bytes", + (int) core_size); + else if (memcmp (core_img, tmp_img, core_size) != 0) + { +#if 0 + FILE *dump; + FILE *dump2; + + dump = fopen ("dump.img", "wb"); + if (dump) + { + fwrite (tmp_img, 1, core_size, dump); + fclose (dump); + } + + dump2 = fopen ("dump2.img", "wb"); + if (dump2) + { + fwrite (core_img, 1, core_size, dump2); + fclose (dump2); + } + +#endif + grub_util_info ("succeeded in opening the core image but the data is different"); + } + else + { + grub_file_close (file); + break; + } + + grub_file_close (file); + } + else + grub_util_info ("couldn't open the core image"); + + if (grub_errno) + grub_util_info ("error message = %s", grub_errmsg); + + grub_errno = GRUB_ERR_NONE; + grub_util_biosdisk_flush (root_dev->disk); + sleep (1); + } + + if (i == MAX_TRIES) + grub_util_error (_("cannot read `%s' correctly"), core_path_dev); + + grub_file_t file; + /* Now read the core image to determine where the sectors are. */ + grub_file_filter_disable_compression (); + file = grub_file_open (core_path_dev); + if (! file) + grub_util_error ("%s", grub_errmsg); + + file->read_hook = callback; + file->read_hook_data = hook_data; + if (grub_file_read (file, tmp_img, core_size) != (grub_ssize_t) core_size) + grub_util_error ("%s", _("failed to read the sectors of the core image")); + + grub_file_close (file); + free (tmp_img); + + free (core_path_dev); +} diff --git a/grub-core/osdep/linux/blocklist.c b/grub-core/osdep/linux/blocklist.c new file mode 100644 index 000000000..b8aa2bfa7 --- /dev/null +++ b/grub-core/osdep/linux/blocklist.c @@ -0,0 +1,144 @@ +/* grub-setup.c - make GRUB usable */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,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 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +void +grub_install_get_blocklist (grub_device_t root_dev, + const char *core_path, + const char *core_img __attribute__ ((unused)), + size_t core_size, + void (*callback) (grub_disk_addr_t sector, + unsigned offset, + unsigned length, + void *data), + void *hook_data) +{ + grub_partition_t container = root_dev->disk->partition; + grub_uint64_t container_start = grub_partition_get_start (container); + struct fiemap fie1; + int fd; + + /* Write the first two sectors of the core image onto the disk. */ + grub_util_info ("opening the core image `%s'", core_path); + fd = open (core_path, O_RDONLY); + if (fd < 0) + grub_util_error (_("cannot open `%s': %s"), core_path, + strerror (errno)); + + grub_memset (&fie1, 0, sizeof (fie1)); + fie1.fm_length = core_size; + fie1.fm_flags = FIEMAP_FLAG_SYNC; + + if (ioctl (fd, FS_IOC_FIEMAP, &fie1) < 0) + { + int nblocks, i, j; + int bsize; + int mul; + + grub_util_info ("FIEMAP failed. Reverting to FIBMAP"); + + if (ioctl (fd, FIGETBSZ, &bsize) < 0) + grub_util_error (_("can't retrieve blocklists: %s"), + strerror (errno)); + if (bsize & (GRUB_DISK_SECTOR_SIZE - 1)) + grub_util_error ("%s", _("blocksize is not divisible by 512")); + mul = bsize >> GRUB_DISK_SECTOR_BITS; + nblocks = (core_size + bsize - 1) / bsize; + if (mul == 0 || nblocks == 0) + grub_util_error ("%s", _("can't retrieve blocklists")); + for (i = 0; i < nblocks; i++) + { + unsigned blk = i; + if (ioctl (fd, FIBMAP, &blk) < 0) + grub_util_error (_("can't retrieve blocklists: %s"), + strerror (errno)); + + for (j = 0; j < mul; j++) + { + int rest = core_size - ((i * mul + j) << GRUB_DISK_SECTOR_BITS); + if (rest <= 0) + break; + if (rest > GRUB_DISK_SECTOR_SIZE) + rest = GRUB_DISK_SECTOR_SIZE; + callback (((grub_uint64_t) blk) * mul + j + + container_start, + 0, rest, hook_data); + } + } + } + else + { + struct fiemap *fie2; + int i, j; + fie2 = xmalloc (sizeof (*fie2) + + fie1.fm_mapped_extents + * sizeof (fie1.fm_extents[1])); + memset (fie2, 0, sizeof (*fie2) + + fie1.fm_mapped_extents * sizeof (fie2->fm_extents[1])); + fie2->fm_length = core_size; + fie2->fm_flags = FIEMAP_FLAG_SYNC; + fie2->fm_extent_count = fie1.fm_mapped_extents; + if (ioctl (fd, FS_IOC_FIEMAP, fie2) < 0) + grub_util_error (_("can't retrieve blocklists: %s"), + strerror (errno)); + for (i = 0; i < fie2->fm_mapped_extents; i++) + { + for (j = 0; + j < ((fie2->fm_extents[i].fe_length + + GRUB_DISK_SECTOR_SIZE - 1) + >> GRUB_DISK_SECTOR_BITS); + j++) + { + size_t len = (fie2->fm_extents[i].fe_length + - j * GRUB_DISK_SECTOR_SIZE); + if (len > GRUB_DISK_SECTOR_SIZE) + len = GRUB_DISK_SECTOR_SIZE; + callback ((fie2->fm_extents[i].fe_physical + >> GRUB_DISK_SECTOR_BITS) + + j + container_start, + fie2->fm_extents[i].fe_physical + & (GRUB_DISK_SECTOR_SIZE - 1), len, hook_data); + } + } + } + close (fd); +} diff --git a/grub-core/osdep/windows/blocklist.c b/grub-core/osdep/windows/blocklist.c new file mode 100644 index 000000000..bddec367d --- /dev/null +++ b/grub-core/osdep/windows/blocklist.c @@ -0,0 +1,119 @@ +/* + * 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 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +void +grub_install_get_blocklist (grub_device_t root_dev, + const char *core_path, + const char *core_img __attribute__ ((unused)), + size_t core_size, + void (*callback) (grub_disk_addr_t sector, + unsigned offset, + unsigned length, + void *data), + void *hook_data) +{ + grub_disk_addr_t first_lcn = 0; + HANDLE filehd; + DWORD rets; + RETRIEVAL_POINTERS_BUFFER *extbuf; + size_t extbuf_size; + DWORD i, j, k; + grub_uint64_t sec_per_lcn; + grub_uint64_t curvcn = 0; + STARTING_VCN_INPUT_BUFFER start_vcn; + grub_fs_t fs; + grub_err_t err; + + fs = grub_fs_probe (root_dev); + if (!fs) + grub_util_error ("%s", grub_errmsg); + + /* This is ugly but windows doesn't give all needed data. Or does anyone + have a pointer how to retrieve it? + */ + if (grub_strcmp (fs->name, "ntfs") == 0) + { + struct grub_ntfs_bpb bpb; + err = grub_disk_read (root_dev->disk, 0, 0, sizeof (bpb), &bpb); + if (err) + grub_util_error ("%s", grub_errmsg); + sec_per_lcn = ((grub_uint32_t) bpb.sectors_per_cluster + * (grub_uint32_t) grub_le_to_cpu16 (bpb.bytes_per_sector)) + >> 9; + first_lcn = 0; + } + else if (grub_strcmp (fs->name, "exfat") == 0) + first_lcn = grub_exfat_get_cluster_sector (root_dev->disk, &sec_per_lcn); + else if (grub_strcmp (fs->name, "fat") == 0) + first_lcn = grub_fat_get_cluster_sector (root_dev->disk, &sec_per_lcn); + else if (grub_strcmp (fs->name, "udf") == 0) + first_lcn = grub_udf_get_cluster_sector (root_dev->disk, &sec_per_lcn); + else + grub_util_error ("unsupported fs for blocklist under windows: %s", + fs->name); + + grub_util_info ("sec_per_lcn = %lld, first_lcn=%lld", sec_per_lcn, first_lcn); + + first_lcn += grub_partition_get_start (root_dev->disk->partition); + + start_vcn.StartingVcn.QuadPart = 0; + + filehd = grub_util_fd_open (core_path, GRUB_UTIL_FD_O_RDONLY); + if (!GRUB_UTIL_FD_IS_VALID (filehd)) + grub_util_error (_("cannot open `%s': %s"), core_path, + grub_util_fd_strerror ()); + + extbuf_size = sizeof (*extbuf) + sizeof (extbuf->Extents[0]) + * ((core_size + 511) / 512); + extbuf = xmalloc (extbuf_size); + + if (!DeviceIoControl(filehd, FSCTL_GET_RETRIEVAL_POINTERS, + &start_vcn, sizeof (start_vcn), + extbuf, extbuf_size, &rets, NULL)) + grub_util_error ("FSCTL_GET_RETRIEVAL_POINTERS fails: %s", + grub_util_fd_strerror ()); + + CloseHandle (filehd); + + for (i = 0; i < extbuf->ExtentCount; i++) + { + for (j = 0; j < extbuf->Extents[i].NextVcn.QuadPart - curvcn; j++) + for (k = 0; k < sec_per_lcn; k++) + callback ((extbuf->Extents[i].Lcn.QuadPart + j) + * sec_per_lcn + first_lcn + + k, 0, 512, hook_data); + } + free (extbuf); +} diff --git a/include/grub/exfat.h b/include/grub/exfat.h new file mode 100644 index 000000000..e5cb82d79 --- /dev/null +++ b/include/grub/exfat.h @@ -0,0 +1,53 @@ +/* + * 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 . + */ + +#ifndef GRUB_EXFAT_H +#define GRUB_EXFAT_H 1 + +#include + +struct grub_exfat_bpb +{ + grub_uint8_t jmp_boot[3]; + grub_uint8_t oem_name[8]; + grub_uint8_t mbz[53]; + grub_uint64_t num_hidden_sectors; + grub_uint64_t num_total_sectors; + grub_uint32_t num_reserved_sectors; + grub_uint32_t sectors_per_fat; + grub_uint32_t cluster_offset; + grub_uint32_t cluster_count; + grub_uint32_t root_cluster; + grub_uint32_t num_serial; + grub_uint16_t fs_revision; + grub_uint16_t volume_flags; + grub_uint8_t bytes_per_sector_shift; + grub_uint8_t sectors_per_cluster_shift; + grub_uint8_t num_fats; + grub_uint8_t num_ph_drive; + grub_uint8_t reserved[8]; +} __attribute__ ((packed)); + +#ifdef GRUB_UTIL +#include + +grub_disk_addr_t +grub_exfat_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn); +#endif + +#endif diff --git a/include/grub/fat.h b/include/grub/fat.h index 491ddfdb0..ae352c6d6 100644 --- a/include/grub/fat.h +++ b/include/grub/fat.h @@ -72,4 +72,11 @@ struct grub_fat_bpb } __attribute__ ((packed)) version_specific; } __attribute__ ((packed)); +#ifdef GRUB_UTIL +#include + +grub_disk_addr_t +grub_fat_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn); +#endif + #endif diff --git a/include/grub/udf.h b/include/grub/udf.h new file mode 100644 index 000000000..905306947 --- /dev/null +++ b/include/grub/udf.h @@ -0,0 +1,30 @@ +/* + * 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 . + */ + +#ifndef GRUB_UDF_H +#define GRUB_UDF_H 1 + +#include + +#ifdef GRUB_UTIL +#include + +grub_disk_addr_t +grub_udf_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn); +#endif +#endif diff --git a/include/grub/util/install.h b/include/grub/util/install.h index 4ce3e9d5d..36f84be5a 100644 --- a/include/grub/util/install.h +++ b/include/grub/util/install.h @@ -36,4 +36,15 @@ grub_util_sparc_setup (const char *dir, const char *dest, int force, int fs_probe, int allow_floppy); + +void +grub_install_get_blocklist (grub_device_t root_dev, + const char *core_path, const char *core_img, + size_t core_size, + void (*callback) (grub_disk_addr_t sector, + unsigned offset, + unsigned length, + void *data), + void *hook_data); + #endif diff --git a/util/setup.c b/util/setup.c index 15db86b94..5b7c38430 100644 --- a/util/setup.c +++ b/util/setup.c @@ -54,15 +54,9 @@ #include #include #include +#include -#ifdef __linux__ -#include -#include -#include -#endif - -#define _GNU_SOURCE 1 -#include +#include /* On SPARC this program fills in various fields inside of the 'boot' and 'core' * image files. @@ -137,19 +131,6 @@ write_rootdev (grub_device_t root_dev, #endif /* Helper for setup. */ -static void -save_first_sector (grub_disk_addr_t sector, unsigned offset, unsigned length, - void *data) -{ - grub_disk_addr_t *first_sector = data; - grub_util_info ("the first sector is <%" PRIuGRUB_UINT64_T ",%u,%u>", - sector, offset, length); - - if (offset != 0 || length != GRUB_DISK_SECTOR_SIZE) - grub_util_error ("%s", _("the first sector of the core file is not sector-aligned")); - - *first_sector = sector; -} struct blocklists { @@ -158,6 +139,7 @@ struct blocklists grub_uint16_t current_segment; #endif grub_uint16_t last_length; + grub_disk_addr_t first_sector; }; /* Helper for setup. */ @@ -171,6 +153,15 @@ save_blocklists (grub_disk_addr_t sector, unsigned offset, unsigned length, grub_util_info ("saving <%" PRIuGRUB_UINT64_T ",%u,%u>", sector, offset, length); + if (bl->first_sector == (grub_disk_addr_t) -1) + { + if (offset != 0 || length != GRUB_DISK_SECTOR_SIZE) + grub_util_error ("%s", _("the first sector of the core file is not sector-aligned")); + + bl->first_sector = sector; + return; + } + if (offset != 0 || bl->last_length != GRUB_DISK_SECTOR_SIZE) grub_util_error ("%s", _("non-sector-aligned data is found in the core file")); @@ -252,18 +243,18 @@ SETUP (const char *dir, const char *dest, int force, int fs_probe, int allow_floppy) { - char *boot_path, *core_path, *core_path_dev, *core_path_dev_full; - char *boot_img, *core_img; + char *core_path; + char *boot_img, *core_img, *boot_path; char *root = 0; size_t boot_size, core_size; #ifdef GRUB_SETUP_BIOS grub_uint16_t core_sectors; #endif grub_device_t root_dev = 0, dest_dev, core_dev; + grub_util_fd_t fp; struct blocklists bl; - char *tmp_img; - grub_disk_addr_t first_sector = (grub_disk_addr_t)-1; - FILE *fp; + + bl.first_sector = (grub_disk_addr_t) -1; #ifdef GRUB_SETUP_BIOS bl.current_segment = @@ -357,16 +348,16 @@ SETUP (const char *dir, if (grub_env_set ("root", root) != GRUB_ERR_NONE) grub_util_error ("%s", grub_errmsg); -#ifdef GRUB_SETUP_BIOS - /* Read the original sector from the disk. */ - tmp_img = xmalloc (GRUB_DISK_SECTOR_SIZE); - if (grub_disk_read (dest_dev->disk, 0, 0, GRUB_DISK_SECTOR_SIZE, tmp_img)) - grub_util_error ("%s", grub_errmsg); -#endif - #ifdef GRUB_SETUP_BIOS { + char *tmp_img; grub_uint8_t *boot_drive_check; + + /* Read the original sector from the disk. */ + tmp_img = xmalloc (GRUB_DISK_SECTOR_SIZE); + if (grub_disk_read (dest_dev->disk, 0, 0, GRUB_DISK_SECTOR_SIZE, tmp_img)) + grub_util_error ("%s", grub_errmsg); + boot_drive_check = (grub_uint8_t *) (boot_img + GRUB_BOOT_MACHINE_DRIVE_CHECK); /* Copy the possible DOS BPB. */ @@ -383,11 +374,7 @@ SETUP (const char *dir, boot_drive_check[0] = 0x90; boot_drive_check[1] = 0x90; } - } -#endif -#ifdef GRUB_SETUP_BIOS - { struct identify_partmap_ctx ctx = { .dest_partmap = NULL, .container = dest_dev->disk->partition, @@ -535,11 +522,8 @@ SETUP (const char *dir, grub_util_error ("%s", _("no terminator in the core image")); } - save_first_sector (sectors[0] + grub_partition_get_start (ctx.container), - 0, GRUB_DISK_SECTOR_SIZE, &first_sector); - bl.block = bl.first_block; - for (i = 1; i < nsec; i++) + for (i = 0; i < nsec; i++) save_blocklists (sectors[i] + grub_partition_get_start (ctx.container), 0, GRUB_DISK_SECTOR_SIZE, &bl); @@ -550,7 +534,7 @@ SETUP (const char *dir, bl.block->len = 0; bl.block->segment = 0; - write_rootdev (root_dev, boot_img, first_sector); + write_rootdev (root_dev, boot_img, bl.first_sector); core_img = realloc (core_img, nsec * GRUB_DISK_SECTOR_SIZE); bl.first_block = (struct grub_boot_blocklist *) (core_img @@ -628,88 +612,8 @@ unable_to_embed: /* The core image must be put on a filesystem unfortunately. */ grub_util_info ("will leave the core image on the filesystem"); - /* Make sure that GRUB reads the identical image as the OS. */ - tmp_img = xmalloc (core_size); - core_path_dev_full = grub_util_get_path (dir, core_file); - core_path_dev = grub_make_system_path_relative_to_its_root (core_path_dev_full); - free (core_path_dev_full); - grub_util_biosdisk_flush (root_dev->disk); -#ifndef __linux__ - -#define MAX_TRIES 5 - { - int i; - for (i = 0; i < MAX_TRIES; i++) - { - grub_file_t file; - - grub_util_info ((i == 0) ? _("attempting to read the core image `%s' from GRUB") - : _("attempting to read the core image `%s' from GRUB again"), - core_path_dev); - - grub_disk_cache_invalidate_all (); - - grub_file_filter_disable_compression (); - file = grub_file_open (core_path_dev); - if (file) - { - if (grub_file_size (file) != core_size) - grub_util_info ("succeeded in opening the core image but the size is different (%d != %d)", - (int) grub_file_size (file), (int) core_size); - else if (grub_file_read (file, tmp_img, core_size) - != (grub_ssize_t) core_size) - grub_util_info ("succeeded in opening the core image but cannot read %d bytes", - (int) core_size); - else if (memcmp (core_img, tmp_img, core_size) != 0) - { -#if 0 - FILE *dump; - FILE *dump2; - - dump = grub_util_fopen ("dump.img", "wb"); - if (dump) - { - fwrite (tmp_img, 1, core_size, dump); - fclose (dump); - } - - dump2 = grub_util_fopen ("dump2.img", "wb"); - if (dump2) - { - fwrite (core_img, 1, core_size, dump2); - fclose (dump2); - } - -#endif - grub_util_info ("succeeded in opening the core image but the data is different"); - } - else - { - grub_file_close (file); - break; - } - - grub_file_close (file); - } - else - grub_util_info ("couldn't open the core image"); - - if (grub_errno) - grub_util_info ("error message = %s", grub_errmsg); - - grub_errno = GRUB_ERR_NONE; - grub_util_biosdisk_flush (root_dev->disk); - sleep (1); - } - - if (i == MAX_TRIES) - grub_util_error (_("cannot read `%s' correctly"), core_path_dev); - } - -#endif - /* Clean out the blocklists. */ bl.block = bl.first_block; while (bl.block->len) @@ -728,140 +632,11 @@ unable_to_embed: bl.block = bl.first_block; -#ifdef __linux__ - { - grub_partition_t container = root_dev->disk->partition; - grub_uint64_t container_start = grub_partition_get_start (container); - struct fiemap fie1; - int fd; + grub_install_get_blocklist (root_dev, core_path, core_img, core_size, + save_blocklists, &bl); - /* Write the first two sectors of the core image onto the disk. */ - grub_util_info ("opening the core image `%s'", core_path); - fp = grub_util_fopen (core_path, "rb"); - if (! fp) - grub_util_error (_("cannot open `%s': %s"), core_path, - strerror (errno)); - fd = fileno (fp); - - grub_memset (&fie1, 0, sizeof (fie1)); - fie1.fm_length = core_size; - fie1.fm_flags = FIEMAP_FLAG_SYNC; - - if (ioctl (fd, FS_IOC_FIEMAP, &fie1) < 0) - { - int nblocks, i, j; - int bsize; - int mul; - - grub_util_info ("FIEMAP failed. Reverting to FIBMAP"); - - if (ioctl (fd, FIGETBSZ, &bsize) < 0) - grub_util_error (_("can't retrieve blocklists: %s"), - strerror (errno)); - if (bsize & (GRUB_DISK_SECTOR_SIZE - 1)) - grub_util_error ("%s", _("blocksize is not divisible by 512")); - mul = bsize >> GRUB_DISK_SECTOR_BITS; - nblocks = (core_size + bsize - 1) / bsize; - if (mul == 0 || nblocks == 0) - grub_util_error ("%s", _("can't retrieve blocklists")); - for (i = 0; i < nblocks; i++) - { - unsigned blk = i; - if (ioctl (fd, FIBMAP, &blk) < 0) - grub_util_error (_("can't retrieve blocklists: %s"), - strerror (errno)); - - for (j = 0; j < mul; j++) - { - int rest = core_size - ((i * mul + j) << GRUB_DISK_SECTOR_BITS); - if (rest <= 0) - break; - if (rest > GRUB_DISK_SECTOR_SIZE) - rest = GRUB_DISK_SECTOR_SIZE; - if (i == 0 && j == 0) - save_first_sector (((grub_uint64_t) blk) * mul - + container_start, - 0, rest, &first_sector); - else - save_blocklists (((grub_uint64_t) blk) * mul + j - + container_start, - 0, rest, &bl); - } - } - } - else - { - struct fiemap *fie2; - int i, j; - fie2 = xmalloc (sizeof (*fie2) - + fie1.fm_mapped_extents - * sizeof (fie1.fm_extents[1])); - memset (fie2, 0, sizeof (*fie2) - + fie1.fm_mapped_extents * sizeof (fie2->fm_extents[1])); - fie2->fm_length = core_size; - fie2->fm_flags = FIEMAP_FLAG_SYNC; - fie2->fm_extent_count = fie1.fm_mapped_extents; - if (ioctl (fd, FS_IOC_FIEMAP, fie2) < 0) - grub_util_error (_("can't retrieve blocklists: %s"), - strerror (errno)); - for (i = 0; i < fie2->fm_mapped_extents; i++) - { - for (j = 0; - j < ((fie2->fm_extents[i].fe_length - + GRUB_DISK_SECTOR_SIZE - 1) - >> GRUB_DISK_SECTOR_BITS); - j++) - { - size_t len = (fie2->fm_extents[i].fe_length - - j * GRUB_DISK_SECTOR_SIZE); - if (len > GRUB_DISK_SECTOR_SIZE) - len = GRUB_DISK_SECTOR_SIZE; - if (first_sector == (grub_disk_addr_t)-1) - save_first_sector ((fie2->fm_extents[i].fe_physical - >> GRUB_DISK_SECTOR_BITS) - + j + container_start, - fie2->fm_extents[i].fe_physical - & (GRUB_DISK_SECTOR_SIZE - 1), len, - &first_sector); - else - save_blocklists ((fie2->fm_extents[i].fe_physical - >> GRUB_DISK_SECTOR_BITS) - + j + container_start, - fie2->fm_extents[i].fe_physical - & (GRUB_DISK_SECTOR_SIZE - 1), len, &bl); - - - } - } - if (first_sector == (grub_disk_addr_t)-1) - grub_util_error ("%s", _("can't retrieve blocklists")); - } - fclose (fp); - } -#else - { - grub_file_t file; - /* Now read the core image to determine where the sectors are. */ - grub_file_filter_disable_compression (); - file = grub_file_open (core_path_dev); - if (! file) - grub_util_error ("%s", grub_errmsg); - - file->read_hook = save_first_sector; - file->read_hook_data = &first_sector; - if (grub_file_read (file, tmp_img, GRUB_DISK_SECTOR_SIZE) - != GRUB_DISK_SECTOR_SIZE) - grub_util_error ("%s", _("failed to read the first sector of the core image")); - - bl.block = bl.first_block; - file->read_hook = save_blocklists; - file->read_hook_data = &bl; - if (grub_file_read (file, tmp_img, core_size - GRUB_DISK_SECTOR_SIZE) - != (grub_ssize_t) core_size - GRUB_DISK_SECTOR_SIZE) - grub_util_error ("%s", _("failed to read the rest sectors of the core image")); - grub_file_close (file); - } -#endif + if (bl.first_sector == (grub_disk_addr_t)-1) + grub_util_error ("%s", _("can't retrieve blocklists")); #ifdef GRUB_SETUP_SPARC64 { @@ -892,22 +667,21 @@ unable_to_embed: } #endif - free (core_path_dev); - free (tmp_img); - - write_rootdev (root_dev, boot_img, first_sector); + write_rootdev (root_dev, boot_img, bl.first_sector); /* Write the first two sectors of the core image onto the disk. */ grub_util_info ("opening the core image `%s'", core_path); - fp = grub_util_fopen (core_path, "r+b"); - if (! fp) + fp = grub_util_fd_open (core_path, GRUB_UTIL_FD_O_WRONLY); + if (! GRUB_UTIL_FD_IS_VALID (fp)) grub_util_error (_("cannot open `%s': %s"), core_path, - strerror (errno)); + grub_util_fd_strerror ()); - grub_util_write_image (core_img, GRUB_DISK_SECTOR_SIZE * 2, fp, core_path); - fflush (fp); - fsync (fileno (fp)); - fclose (fp); + if (grub_util_fd_write (fp, core_img, GRUB_DISK_SECTOR_SIZE * 2) + != GRUB_DISK_SECTOR_SIZE * 2) + grub_util_error (_("cannot write to `%s': %s"), + core_path, strerror (errno)); + grub_util_fd_sync (fp); + grub_util_fd_close (fp); grub_util_biosdisk_flush (root_dev->disk); grub_disk_cache_invalidate_all (); @@ -922,7 +696,7 @@ unable_to_embed: core_dev->disk->partition = 0; buf = xmalloc (core_size); - blk = first_sector; + blk = bl.first_sector; err = grub_disk_read (core_dev->disk, blk, 0, GRUB_DISK_SECTOR_SIZE, buf); if (err) grub_util_error (_("cannot read `%s': %s"), core_dev->disk->name,