Hello, all. When someone installs using blocklists (discouraged and
unreliable), we use GRUB internal fs reader in order to determine
core.img blocklists. With Linux we call fsync and an ioctl on partition
in order to flush the cache. I also tried adding "sync" which didn't
make any difference. Unfortunately it doesn't seem to be good enough on
some filesystems e.g. ext2 (ext3 and ext4 seem to work).
Does anyone know how to ensure flush of a file AND of all filesystem
metadata needed to read it?
Alternatively we can ask Linux itself to tell us the blocks the file
resides in. There are 2 ioctls for this: FIBMAP and FIEMAP. FIBMAP is
unfortunately limited to 32-bit blocks. Most of FS supported by both
GRUB and Linux lack FIEMAP. If we exclude the 32-bit fs (which will work
fine with FIBMAP as long as it uses the same block size as underlying
FS, which seems reasonable but I haven't verified it) (minix1/2/3,
reiserfs, hfs, jfs, udf, fat, affs) and read-only filesystems
(unsuitable for install) (iso9660, romfs, squash4), the remaining
trouble is NTFS and HFS+. So with this approach we'll be unable to
install on >2TiB HFS+ or NTFS filesystems. I see following possibilities:
- Someone write fiemap support for HFS+ and NTFS. This would be probably
the best approach
- We find a way to flush disk reliably. This would eliminate the
necessity of having different branches for Linux and rest.
- Ask Linux devs if they are willing to provide FIBMAP64 since
internally 64-bits are used.
Another problem with FIBMAP/FIEMAP is that fuse-mounted BFS lacks them
altogether. We will need to find some other way of doing BFS install.
Also some COW filesystems may supply a somewhat valid blocklist with
FIBMAP/FIEMAP but which is unusable to GRUB because of COW nature. So we
need an FS-whitelist.
Experiment patch attached.
Any thoughts?
--
Regards
Vladimir 'φ-coder/phcoder' Serbinenko
=== modified file 'grub-core/fs/affs.c'
--- grub-core/fs/affs.c 2012-01-14 14:44:34 +0000
+++ grub-core/fs/affs.c 2012-01-27 11:55:52 +0000
@@ -571,6 +571,7 @@
.label = grub_affs_label,
#ifdef GRUB_UTIL
.reserved_first_sector = 0,
+ .blocklist_install = 1,
#endif
.next = 0
};
=== modified file 'grub-core/fs/bfs.c'
--- grub-core/fs/bfs.c 2011-10-30 15:10:18 +0000
+++ grub-core/fs/bfs.c 2012-01-27 11:56:05 +0000
@@ -1026,6 +1026,7 @@
#endif
#ifdef GRUB_UTIL
.reserved_first_sector = 1,
+ .blocklist_install = 1,
#endif
};
=== modified file 'grub-core/fs/btrfs.c'
--- grub-core/fs/btrfs.c 2012-01-25 14:10:56 +0000
+++ grub-core/fs/btrfs.c 2012-01-27 11:56:17 +0000
@@ -1653,6 +1653,7 @@
#ifdef GRUB_UTIL
.embed = grub_btrfs_embed,
.reserved_first_sector = 1,
+ .blocklist_install = 0,
#endif
};
=== modified file 'grub-core/fs/cpio.c'
--- grub-core/fs/cpio.c 2011-12-24 14:09:26 +0000
+++ grub-core/fs/cpio.c 2012-01-27 11:56:31 +0000
@@ -714,6 +714,7 @@
.close = grub_cpio_close,
#ifdef GRUB_UTIL
.reserved_first_sector = 0,
+ .blocklist_install = 0,
#endif
};
=== modified file 'grub-core/fs/ext2.c'
--- grub-core/fs/ext2.c 2012-01-14 10:55:20 +0000
+++ grub-core/fs/ext2.c 2012-01-27 11:56:43 +0000
@@ -971,6 +971,7 @@
.mtime = grub_ext2_mtime,
#ifdef GRUB_UTIL
.reserved_first_sector = 1,
+ .blocklist_install = 1,
#endif
.next = 0
};
=== modified file 'grub-core/fs/fat.c'
--- grub-core/fs/fat.c 2011-12-13 00:41:16 +0000
+++ grub-core/fs/fat.c 2012-01-27 11:56:56 +0000
@@ -1158,6 +1158,7 @@
#else
.reserved_first_sector = 1,
#endif
+ .blocklist_install = 1,
#endif
.next = 0
};
=== modified file 'grub-core/fs/hfs.c'
--- grub-core/fs/hfs.c 2012-01-14 22:34:33 +0000
+++ grub-core/fs/hfs.c 2012-01-27 11:57:13 +0000
@@ -1356,6 +1356,10 @@
.label = grub_hfs_label,
.uuid = grub_hfs_uuid,
.mtime = grub_hfs_mtime,
+#ifdef GRUB_UTIL
+ .reserved_first_sector = 1,
+ .blocklist_install = 1,
+#endif
.next = 0
};
=== modified file 'grub-core/fs/hfsplus.c'
--- grub-core/fs/hfsplus.c 2012-01-14 22:34:33 +0000
+++ grub-core/fs/hfsplus.c 2012-01-27 11:57:20 +0000
@@ -1084,6 +1084,7 @@
.uuid = grub_hfsplus_uuid,
#ifdef GRUB_UTIL
.reserved_first_sector = 1,
+ .blocklist_install = 1,
#endif
.next = 0
};
=== modified file 'grub-core/fs/iso9660.c'
--- grub-core/fs/iso9660.c 2011-12-13 15:35:12 +0000
+++ grub-core/fs/iso9660.c 2012-01-27 11:57:51 +0000
@@ -1092,6 +1092,10 @@
.label = grub_iso9660_label,
.uuid = grub_iso9660_uuid,
.mtime = grub_iso9660_mtime,
+#ifdef GRUB_UTIL
+ .reserved_first_sector = 1,
+ .blocklist_install = 1,
+#endif
.next = 0
};
=== modified file 'grub-core/fs/jfs.c'
--- grub-core/fs/jfs.c 2011-12-13 22:11:48 +0000
+++ grub-core/fs/jfs.c 2012-01-27 11:58:04 +0000
@@ -906,6 +906,10 @@
.close = grub_jfs_close,
.label = grub_jfs_label,
.uuid = grub_jfs_uuid,
+#ifdef GRUB_UTIL
+ .reserved_first_sector = 1,
+ .blocklist_install = 1,
+#endif
.next = 0
};
=== modified file 'grub-core/fs/minix.c'
--- grub-core/fs/minix.c 2011-12-25 21:44:42 +0000
+++ grub-core/fs/minix.c 2012-01-27 11:58:13 +0000
@@ -653,6 +653,10 @@
.open = grub_minix_open,
.read = grub_minix_read,
.close = grub_minix_close,
+#ifdef GRUB_UTIL
+ .reserved_first_sector = 1,
+ .blocklist_install = 1,
+#endif
.next = 0
};
=== modified file 'grub-core/fs/nilfs2.c'
--- grub-core/fs/nilfs2.c 2012-01-25 14:09:00 +0000
+++ grub-core/fs/nilfs2.c 2012-01-27 11:58:24 +0000
@@ -1178,6 +1178,7 @@
.mtime = grub_nilfs2_mtime,
#ifdef GRUB_UTIL
.reserved_first_sector = 1,
+ .blocklist_install = 0,
#endif
.next = 0
};
=== modified file 'grub-core/fs/ntfs.c'
--- grub-core/fs/ntfs.c 2012-01-20 14:01:35 +0000
+++ grub-core/fs/ntfs.c 2012-01-27 11:58:35 +0000
@@ -1248,6 +1248,7 @@
.uuid = grub_ntfs_uuid,
#ifdef GRUB_UTIL
.reserved_first_sector = 1,
+ .blocklist_install = 1,
#endif
.next = 0
};
=== modified file 'grub-core/fs/reiserfs.c'
--- grub-core/fs/reiserfs.c 2012-01-14 22:36:21 +0000
+++ grub-core/fs/reiserfs.c 2012-01-27 11:58:46 +0000
@@ -1387,6 +1387,10 @@
.close = grub_reiserfs_close,
.label = grub_reiserfs_label,
.uuid = grub_reiserfs_uuid,
+#ifdef GRUB_UTIL
+ .reserved_first_sector = 1,
+ .blocklist_install = 1,
+#endif
.next = 0
};
=== modified file 'grub-core/fs/romfs.c'
--- grub-core/fs/romfs.c 2011-12-13 22:15:56 +0000
+++ grub-core/fs/romfs.c 2012-01-27 11:58:54 +0000
@@ -452,6 +452,7 @@
.label = grub_romfs_label,
#ifdef GRUB_UTIL
.reserved_first_sector = 0,
+ .blocklist_install = 0,
#endif
.next = 0
};
=== modified file 'grub-core/fs/sfs.c'
--- grub-core/fs/sfs.c 2011-12-25 21:39:56 +0000
+++ grub-core/fs/sfs.c 2012-01-27 11:59:04 +0000
@@ -616,6 +616,7 @@
.label = grub_sfs_label,
#ifdef GRUB_UTIL
.reserved_first_sector = 0,
+ .blocklist_install = 1,
#endif
.next = 0
};
=== modified file 'grub-core/fs/squash4.c'
--- grub-core/fs/squash4.c 2012-01-27 12:09:57 +0000
+++ grub-core/fs/squash4.c 2012-01-27 12:11:00 +0000
@@ -935,6 +935,7 @@
.mtime = grub_squash_mtime,
#ifdef GRUB_UTIL
.reserved_first_sector = 0,
+ .blocklist_install = 0,
#endif
.next = 0
};
=== modified file 'grub-core/fs/udf.c'
--- grub-core/fs/udf.c 2011-12-13 12:58:33 +0000
+++ grub-core/fs/udf.c 2012-01-27 11:59:22 +0000
@@ -1097,6 +1097,10 @@
.read = grub_udf_read,
.close = grub_udf_close,
.label = grub_udf_label,
+#ifdef GRUB_UTIL
+ .reserved_first_sector = 1,
+ .blocklist_install = 1,
+#endif
.next = 0
};
=== modified file 'grub-core/fs/ufs.c'
--- grub-core/fs/ufs.c 2011-11-03 14:00:45 +0000
+++ grub-core/fs/ufs.c 2012-01-27 12:00:04 +0000
@@ -792,6 +792,10 @@
.label = grub_ufs_label,
.uuid = grub_ufs_uuid,
.mtime = grub_ufs_mtime,
+ /* FIXME: set reserved_first_sector. */
+#ifdef GRUB_UTIL
+ .blocklist_install = 1,
+#endif
.next = 0
};
=== modified file 'grub-core/fs/xfs.c'
--- grub-core/fs/xfs.c 2011-12-13 01:02:38 +0000
+++ grub-core/fs/xfs.c 2012-01-27 12:00:20 +0000
@@ -884,6 +884,7 @@
.uuid = grub_xfs_uuid,
#ifdef GRUB_UTIL
.reserved_first_sector = 0,
+ .blocklist_install = 1,
#endif
.next = 0
};
=== modified file 'grub-core/fs/zfs/zfs.c'
--- grub-core/fs/zfs/zfs.c 2012-01-27 12:50:21 +0000
+++ grub-core/fs/zfs/zfs.c 2012-01-27 12:51:06 +0000
@@ -3931,6 +3931,7 @@
#ifdef GRUB_UTIL
.embed = grub_zfs_embed,
.reserved_first_sector = 1,
+ .blocklist_install = 0,
#endif
.next = 0
};
=== modified file 'include/grub/fs.h'
--- include/grub/fs.h 2012-01-24 12:31:12 +0000
+++ include/grub/fs.h 2012-01-27 11:55:24 +0000
@@ -86,6 +86,9 @@
/* Whether this filesystem reserves first sector for DOS-style boot. */
int reserved_first_sector;
+
+ /* Whether blocklist installs have a chance to work. */
+ int blocklist_install;
#endif
};
typedef struct grub_fs *grub_fs_t;
=== modified file 'util/grub-setup.c'
--- util/grub-setup.c 2012-01-24 13:39:29 +0000
+++ util/grub-setup.c 2012-01-27 12:24:59 +0000
@@ -51,6 +51,12 @@
#include <grub/msdos_partition.h>
#include <include/grub/crypto.h>
+#ifdef __linux__
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <linux/fiemap.h>
+#endif
+
#define _GNU_SOURCE 1
#include <argp.h>
@@ -141,7 +147,7 @@
size_t boot_size, core_size;
grub_uint16_t core_sectors;
grub_device_t root_dev, dest_dev;
- struct grub_boot_blocklist *first_block, *block;
+ struct grub_boot_blocklist *first_block, *block, *last_block;
char *tmp_img;
int i;
grub_disk_addr_t first_sector;
@@ -150,7 +156,6 @@
= GRUB_BOOT_MACHINE_KERNEL_SEG + (GRUB_DISK_SECTOR_SIZE >> 4);
#endif
grub_uint16_t last_length = GRUB_DISK_SECTOR_SIZE;
- grub_file_t file;
FILE *fp;
auto void NESTED_FUNC_ATTR save_first_sector (grub_disk_addr_t sector,
@@ -490,11 +495,23 @@
"the root device is on a RAID array or LVM volume"));
#ifdef GRUB_MACHINE_PCBIOS
- if (dest_dev->disk->id != root_dev->disk->id)
+ if (dest_dev->disk->id != root_dev->disk->id
+ || dest_dev->disk->dev->id != root_dev->disk->dev->id)
grub_util_error (_("embedding is not possible, but this is required for "
"cross-disk install"));
#endif
+ {
+ grub_fs_t fs;
+ fs = grub_fs_probe (root_dev);
+ if (!fs)
+ grub_util_error (_("can't determine filesystem"));
+
+ if (!fs->blocklist_install)
+ grub_util_error (_("filesystem '%s' doesn't support blocklists"),
+ fs->name);
+ }
+
grub_util_warn (_("Embedding is not possible. GRUB can only be installed in this "
"setup by using blocklists. However, blocklists are UNRELIABLE and "
"their use is discouraged."));
@@ -512,10 +529,14 @@
grub_util_biosdisk_flush (root_dev->disk);
+#ifndef __linux__
+
#define MAX_TRIES 5
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);
@@ -578,6 +599,8 @@
if (i == MAX_TRIES)
grub_util_error (_("cannot read `%s' correctly"), core_path_dev);
+#endif
+
/* Clean out the blocklists. */
block = first_block;
while (block->len)
@@ -593,23 +616,130 @@
if ((char *) block <= core_img)
grub_util_error (_("no terminator in the core image"));
}
-
- /* 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;
- if (grub_file_read (file, tmp_img, GRUB_DISK_SECTOR_SIZE)
- != GRUB_DISK_SECTOR_SIZE)
- grub_util_error (_("failed to read the first sector of the core image"));
-
+ last_block = block + 1;
block = first_block;
- file->read_hook = save_blocklists;
- 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 (_("failed to read the rest sectors of the core image"));
+
+#ifdef __linux__
+ {
+ grub_partition_t container = root_dev->disk->partition;
+ 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);
+ fp = fopen (core_path, "rb");
+ if (! fp)
+ grub_util_error (_("cannot open `%s'"), core_path);
+ 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_warn ("FIEMAP failed. Reverting to FIBMAP");
+
+ if (ioctl (fd, FIGETBSZ, &bsize) < 0)
+ grub_util_error (_("can't get blocklist"));
+ if (bsize & (GRUB_DISK_SECTOR_SIZE - 1))
+ grub_util_error (_("blocksize not divisible by 512"));
+ mul = bsize >> GRUB_DISK_SECTOR_BITS;
+ nblocks = (core_size + bsize - 1) / bsize;
+ for (i = 0; i < nblocks; i++)
+ {
+ unsigned blk = i;
+ grub_err_t err;
+ if (ioctl (fd, FIBMAP, &blk) < 0)
+ grub_util_error (_("can't get blocklist"));
+
+ 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
+ + grub_partition_get_start (container),
+ 0, rest);
+ else
+ save_blocklists (((grub_uint64_t) blk) * mul + j
+ + grub_partition_get_start (container),
+ 0, rest);
+ }
+ }
+ }
+ 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 ("FIEMAP fails");
+ 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 (i == 0 && j == 0)
+ save_first_sector ((fie2->fm_extents[i].fe_physical
+ >> GRUB_DISK_SECTOR_BITS)
+ + j,
+ fie2->fm_extents[i].fe_physical
+ & (GRUB_DISK_SECTOR_SIZE - 1), len);
+ else
+ save_blocklists ((fie2->fm_extents[i].fe_physical
+ >> GRUB_DISK_SECTOR_BITS)
+ + j,
+ fie2->fm_extents[i].fe_physical
+ & (GRUB_DISK_SECTOR_SIZE - 1), len);
+
+
+ }
+ }
+ }
+ fclose (fp);
+ }
+#else
+ {
+ /* 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;
+ if (grub_file_read (file, tmp_img, GRUB_DISK_SECTOR_SIZE)
+ != GRUB_DISK_SECTOR_SIZE)
+ grub_util_error (_("failed to read the first sector of the core image"));
+
+ file->read_hook = save_blocklists;
+ 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 (_("failed to read the rest sectors of the core image"));
+ grub_file_close (file);
+ }
+#endif
#ifdef GRUB_MACHINE_IEEE1275
{
@@ -617,11 +747,12 @@
boot_devpath = (char *) (boot_img
+ GRUB_BOOT_AOUT_HEADER_SIZE
+ GRUB_BOOT_MACHINE_BOOT_DEVPATH);
- if (file->device->disk->id != dest_dev->disk->id)
+ if (dest_dev->disk->id != root_dev->disk->id
+ || dest_dev->disk->dev->id != root_dev->disk->dev->id)
{
const char *dest_ofpath;
dest_ofpath
- = grub_util_devname_to_ofpath (grub_util_biosdisk_get_osdev (file->device->disk));
+ = grub_util_devname_to_ofpath (grub_util_biosdisk_get_osdev (root_dev->disk));
grub_util_info ("dest_ofpath is `%s'", dest_ofpath);
strncpy (boot_devpath, dest_ofpath, GRUB_BOOT_MACHINE_BOOT_DEVPATH_END
- GRUB_BOOT_MACHINE_BOOT_DEVPATH - 1);
@@ -638,8 +769,6 @@
}
#endif
- grub_file_close (file);
-
free (core_path_dev);
free (tmp_img);
@@ -652,8 +781,80 @@
grub_util_error (_("cannot open `%s'"), core_path);
grub_util_write_image (core_img, GRUB_DISK_SECTOR_SIZE * 2, fp);
+ fsync (fileno (fp));
fclose (fp);
+ grub_disk_cache_invalidate_all ();
+
+ {
+ char *buf, *ptr = core_img;
+ size_t len = core_size;
+ grub_uint64_t blk;
+ grub_partition_t container = root_dev->disk->partition;
+ grub_err_t err;
+
+ root_dev->disk->partition = 0;
+
+ buf = xmalloc (core_size);
+ blk = first_sector;
+ err = grub_disk_read (dest_dev->disk, blk, 0, GRUB_DISK_SECTOR_SIZE, buf);
+ if (err)
+ grub_util_error (_("read failed: %s"), grub_errmsg);
+ if (grub_memcmp (buf, ptr, (char *) last_block - core_img) != 0)
+ {
+ int i;
+ grub_printf ("%ld\n", ptr - core_img);
+ for (i = 0; i < 512; i++)
+ grub_printf ("%02x%c", (unsigned char) ptr[i],
+ ((i & 0xf) == 0xf) ? '\n': ' ');
+ grub_printf ("--\n");
+ for (i = 0; i < 512; i++)
+ grub_printf ("%02x%c", (unsigned char) buf[i],
+ ((i & 0xf) == 0xf) ? '\n': ' ');
+ grub_util_error (_("blocklists are invalid"));
+ }
+
+ ptr += GRUB_DISK_SECTOR_SIZE;
+ len -= GRUB_DISK_SECTOR_SIZE;
+
+ block = first_block;
+ while (block->len)
+ {
+ size_t cur = grub_target_to_host16 (block->len) << GRUB_DISK_SECTOR_BITS;
+ blk = grub_target_to_host64 (block->start);
+
+ if (cur > len)
+ cur = len;
+
+ err = grub_disk_read (dest_dev->disk, blk, 0, cur, buf);
+ if (err)
+ grub_util_error (_("read failed: %s"), grub_errmsg);
+
+ if (grub_memcmp (buf, ptr, cur) != 0)
+ {
+ int i;
+ grub_printf ("%ld\n", ptr - core_img);
+ for (i = 0; i < cur; i++)
+ grub_printf ("%02x%c", (unsigned char) ptr[i],
+ ((i & 0xf) == 0xf) ? '\n': ' ');
+ grub_printf ("--\n");
+ for (i = 0; i < cur; i++)
+ grub_printf ("%02x%c", (unsigned char) buf[i],
+ ((i & 0xf) == 0xf) ? '\n': ' ');
+ grub_util_error (_("blocklists are invalid"));
+ }
+
+ ptr += cur;
+ len -= cur;
+ block--;
+
+ if ((char *) block <= core_img)
+ grub_util_error (_("no terminator in the core image"));
+ }
+ root_dev->disk->partition = container;
+ free (buf);
+ }
+
finish:
/* Write the boot image onto the disk. */
_______________________________________________
Grub-devel mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/grub-devel