107 lines
3.2 KiB
Diff
107 lines
3.2 KiB
Diff
From 0318dfbc726a82ce1e9309e55186f3c4faae72f1 Mon Sep 17 00:00:00 2001
|
|
From: Eric Sandeen <sandeen@sandeen.net>
|
|
Date: Wed, 12 Sep 2018 09:40:22 -0500
|
|
Subject: [PATCH] zipl: use FIEMAP mapping ioctl if it exists
|
|
|
|
zipl currently uses the FIBMAP ioctl to map blocks for the bootloader;
|
|
on XFS, if FIBMAP is requested on a reflinked file, it will fail -
|
|
and FIBMAP returns 0 in this case, which is indistinguishable from a
|
|
hole. This causes boot to fail because the file is not mapped.
|
|
|
|
We can use the FIEMAP ioctl instead, which is able to map reflinked
|
|
files. While FIEMAP is able to map entire extents at once, here we
|
|
simply use it to obtain the mapping block-by-block so that it fits
|
|
in with the current FIBMAP calls.
|
|
|
|
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
|
|
---
|
|
zipl/src/disk.c | 57 +++++++++++++++++++++++++++++++++++++++++++++----
|
|
1 file changed, 53 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/zipl/src/disk.c b/zipl/src/disk.c
|
|
index 0d8e7796..43092bf1 100644
|
|
--- a/zipl/src/disk.c
|
|
+++ b/zipl/src/disk.c
|
|
@@ -21,6 +21,8 @@
|
|
#include <sys/sysmacros.h>
|
|
#include <sys/vfs.h>
|
|
#include <unistd.h>
|
|
+#include <linux/fs.h>
|
|
+#include <linux/fiemap.h>
|
|
|
|
#include "lib/util_proc.h"
|
|
|
|
@@ -550,8 +552,12 @@ disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical,
|
|
{
|
|
struct statfs buf;
|
|
blocknum_t phy_per_fs;
|
|
- int mapped;
|
|
+ blocknum_t mapped;
|
|
+ int block;
|
|
int subblock;
|
|
+ int fiemap_size;
|
|
+ int map_offset;
|
|
+ struct fiemap *fiemap;
|
|
|
|
/* No file system: partition or raw disk */
|
|
if (info->fs_block_size == -1) {
|
|
@@ -576,12 +582,55 @@ disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical,
|
|
}
|
|
/* Get mapping in file system blocks */
|
|
phy_per_fs = info->fs_block_size / info->phy_block_size;
|
|
- mapped = logical / phy_per_fs;
|
|
subblock = logical % phy_per_fs;
|
|
- if (ioctl(fd, FIBMAP, &mapped)) {
|
|
- error_reason("Could not get file mapping");
|
|
+
|
|
+ /* First try FIEMAP, more complicated to set up */
|
|
+ fiemap_size = sizeof(struct fiemap) + sizeof(struct fiemap_extent);
|
|
+
|
|
+ fiemap = misc_malloc(fiemap_size);
|
|
+ if (!fiemap)
|
|
return -1;
|
|
+ memset(fiemap, 0, fiemap_size);
|
|
+
|
|
+ fiemap->fm_extent_count = 1;
|
|
+ fiemap->fm_flags = FIEMAP_FLAG_SYNC;
|
|
+ /* fm_start, fm_length in bytes; logical is in physical block units */
|
|
+ fiemap->fm_start = logical * info->phy_block_size;
|
|
+ fiemap->fm_length = info->phy_block_size;
|
|
+
|
|
+ if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap)) {
|
|
+ /* FIEMAP failed, fall back to FIBMAP */
|
|
+ block = logical / phy_per_fs;
|
|
+ if (ioctl(fd, FIBMAP, &block)) {
|
|
+ error_reason("Could not get file mapping");
|
|
+ free(fiemap);
|
|
+ return -1;
|
|
+ }
|
|
+ mapped = block;
|
|
+ } else {
|
|
+ if (fiemap->fm_mapped_extents) {
|
|
+ if (fiemap->fm_extents[0].fe_flags &
|
|
+ FIEMAP_EXTENT_ENCODED) {
|
|
+ error_reason("File mapping is encoded");
|
|
+ free(fiemap);
|
|
+ return -1;
|
|
+ }
|
|
+ /*
|
|
+ * returned extent may start prior to our request
|
|
+ */
|
|
+ map_offset = fiemap->fm_start -
|
|
+ fiemap->fm_extents[0].fe_logical;
|
|
+ mapped = fiemap->fm_extents[0].fe_physical +
|
|
+ map_offset;
|
|
+ /* set mapped to fs block units */
|
|
+ mapped = mapped / info->fs_block_size;
|
|
+ } else {
|
|
+ mapped = 0;
|
|
+ }
|
|
}
|
|
+
|
|
+ free(fiemap);
|
|
+
|
|
if (mapped == 0) {
|
|
/* This is a hole in the file */
|
|
*physical = 0;
|