diff --git a/zipl/include/disk.h b/zipl/include/disk.h index b143c206..3c3b4f74 100644 --- a/zipl/include/disk.h +++ b/zipl/include/disk.h @@ -134,5 +134,6 @@ blocknum_t disk_get_blocklist_from_file(const char* filename, struct disk_info* pinfo); int disk_check_subchannel_set(int devno, dev_t device, char* dev_name); void disk_print_geo(struct disk_info *data); +int fs_map(int fd, uint64_t offset, blocknum_t *mapped, int fs_block_size); #endif /* not DISK_H */ diff --git a/zipl/src/Makefile b/zipl/src/Makefile index 949e73e7..e1c4e3a6 100644 --- a/zipl/src/Makefile +++ b/zipl/src/Makefile @@ -9,7 +9,7 @@ ALL_LDFLAGS += -Wl,-z,noexecstack $(NO_PIE_LDFLAGS) libs = $(rootdir)/libutil/libutil.a -objects = misc.o error.o scan.o job.o boot.o bootmap.o disk.o \ +objects = misc.o error.o scan.o job.o boot.o bootmap.o fs-map.o disk.o \ bootmap_header.o envblk.o install.o zipl.o $(rootdir)/zipl/boot/data.o zipl_helpers = $(basename $(wildcard zipl_helper.*.c)) diff --git a/zipl/src/disk.c b/zipl/src/disk.c index dcfab84b..f8df89a7 100644 --- a/zipl/src/disk.c +++ b/zipl/src/disk.c @@ -554,14 +554,6 @@ disk_free_info(struct disk_info* info) } -#ifndef REISERFS_SUPER_MAGIC -#define REISERFS_SUPER_MAGIC 0x52654973 -#endif /* not REISERFS_SUPER_MAGIC */ - -#ifndef REISERFS_IOC_UNPACK -#define REISERFS_IOC_UNPACK _IOW(0xCD,1,long) -#endif /* not REISERFS_IOC_UNPACK */ - /* Retrieve the physical blocknumber (block on disk) of the specified logical * block (block in file). FD provides the file descriptor, LOGICAL is the * logical block number. Upon success, return 0 and store the physical @@ -571,14 +563,9 @@ int disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical, blocknum_t* physical, struct disk_info* info) { - struct statfs buf; blocknum_t phy_per_fs; 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) { @@ -588,70 +575,15 @@ disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical, *physical = logical + info->geo.start; return 0; } - - /* Get file system type */ - if (fstatfs(fd, &buf)) { - error_reason(strerror(errno)); - return -1; - } - /* Files on ReiserFS need unpacking */ - if (buf.f_type == REISERFS_SUPER_MAGIC) { - if (ioctl(fd, REISERFS_IOC_UNPACK, 1)) { - error_reason("Could not unpack ReiserFS file"); - return -1; - } - } - /* Get mapping in file system blocks */ + /* + * Get mapping in file system blocks + */ phy_per_fs = info->fs_block_size / info->phy_block_size; subblock = logical % phy_per_fs; - /* First try FIEMAP, more complicated to set up */ - fiemap_size = sizeof(struct fiemap) + sizeof(struct fiemap_extent); - - fiemap = misc_malloc(fiemap_size); - if (!fiemap) + if (fs_map(fd, logical * info->phy_block_size, + &mapped, info->fs_block_size) != 0) 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; diff --git a/zipl/src/fs-map.c b/zipl/src/fs-map.c new file mode 100644 index 00000000..e2a418e1 --- /dev/null +++ b/zipl/src/fs-map.c @@ -0,0 +1,127 @@ +/* + * zipl - zSeries Initial Program Loader tool + * + * Functions to map logical addresses to physical ones + * + * Copyright IBM Corp. 2001, 2017 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "disk.h" +#include "error.h" +#include "misc.h" + +#ifndef REISERFS_SUPER_MAGIC +#define REISERFS_SUPER_MAGIC 0x52654973 +#endif /* not REISERFS_SUPER_MAGIC */ + +#ifndef REISERFS_IOC_UNPACK +#define REISERFS_IOC_UNPACK _IOW(0xCD, 1, long) +#endif /* not REISERFS_IOC_UNPACK */ + +/** + * Filesystem-specific hook. + * Do whatever needed before mapping logical address to physical one + */ +static int fs_premap_hook(int fd) +{ + struct statfs buf; + + /* Get file system type */ + if (fstatfs(fd, &buf)) { + error_reason(strerror(errno)); + return -1; + } + switch (buf.f_type) { + case REISERFS_SUPER_MAGIC: + /* Files on ReiserFS need unpacking */ + if (ioctl(fd, REISERFS_IOC_UNPACK, 1)) { + error_reason("Could not unpack ReiserFS file"); + return -1; + } + break; + default: + break; + } + return 0; +} + +/** + * Find a disk address for a file system block, + * where a specified byte of the file body is located + * + * FD: file descriptor + * OFFSET: offset of the byte in the file body + * MAPPED: resulted disk address + * FS_BLOCK_SIZE : file system block size in bytes + */ +int fs_map(int fd, uint64_t offset, blocknum_t *mapped, + int fs_block_size) +{ + struct fiemap *fiemap; + int fiemap_size; + int map_offset; + int block; + + if (fs_premap_hook(fd)) + return -1; + /* + * 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; + fiemap->fm_start = offset; + fiemap->fm_length = fs_block_size; + + if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap)) { + /* FIEMAP failed, fall back to FIBMAP */ + block = offset / fs_block_size; + 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 / fs_block_size; + } else { + *mapped = 0; + } + } + free(fiemap); + return 0; +}