summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Sørensen <stefan.sorensen@spectralink.com>2019-03-11 11:13:37 +0100
committerStefan Sørensen <stefan.sorensen@spectralink.com>2019-03-11 11:16:25 +0100
commit1b98d6fadfca76cc0202a2df3debc67a8fe0ee1a (patch)
tree774f15ea6bbc55aa7f7daeb5b69768f5edad70c7
parentabc1c32378debaccdfb63c711de56f3a1becb84b (diff)
downloadgenimage-1b98d6fadfca76cc0202a2df3debc67a8fe0ee1a.tar.gz
genimage-1b98d6fadfca76cc0202a2df3debc67a8fe0ee1a.tar.xz
pad_file: Write sparse files
When writing the output to a regular file, only write the file extents actually allocated in the input file. This utilized the FIEMAP ioctl on supported filesystems with fallback to non-sparse output otherwise. Signed-off-by: Stefan Sørensen <stefan.sorensen@spectralink.com>
-rwxr-xr-xtest/basic-images.test20
-rw-r--r--util.c158
2 files changed, 148 insertions, 30 deletions
diff --git a/test/basic-images.test b/test/basic-images.test
index 9b53b45..e1cd784 100755
--- a/test/basic-images.test
+++ b/test/basic-images.test
@@ -89,6 +89,25 @@ check_size() {
fi
}
+get_disk_usage() {
+ local file="${1}"
+ if [ ! -f "${file}" ]; then
+ echo "Failed to check file disk usage: '${file}' does not exist!"
+ return 1
+ fi
+ set -- $(du -B 1 "${file}")
+ usage="${1}"
+}
+
+check_disk_usage_range() {
+ local usage
+ get_disk_usage "${1}" || return
+ if [ "${usage}" -lt "${2}" -o "${usage}" -gt "${3}" ]; then
+ echo "Incorrect file disk usage for '${1}': expected min: ${2} max: ${3} found: ${usage}"
+ return 1
+ fi
+}
+
exec_test_set_prereq() {
command -v "${1}" > /dev/null && test_set_prereq "${1/./_}"
}
@@ -194,6 +213,7 @@ test_expect_success fdisk,sfdisk "hdimage" "
setup_test_images &&
run_genimage hdimage.config test.hdimage &&
check_size images/test.hdimage 9442816 &&
+ check_disk_usage_range images/test.hdimage 40960 57344 &&
# check the this identifier
fdisk -l images/test.hdimage | grep identifier: > hdimage.fdisk &&
# check partitions; filter output to handle different sfdisk versions
diff --git a/util.c b/util.c
index 2b91a94..2310c90 100644
--- a/util.c
+++ b/util.c
@@ -25,6 +25,7 @@
#include <sys/stat.h>
#include <sys/wait.h>
#include <linux/fs.h>
+#include <linux/fiemap.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
@@ -312,13 +313,83 @@ static int open_file(struct image *image, const char *filename, int extra_flags)
return fd;
}
+struct extent {
+ unsigned long long start, end;
+};
+
+/* Build a file extent covering the whole file */
+static int whole_file_exent(size_t size, struct extent **extents,
+ size_t *extent_count)
+{
+ *extents = xzalloc(sizeof(struct extent));
+ (*extents)[0].start = 0;
+ (*extents)[0].end = size;
+ *extent_count = 1;
+ return 0;
+}
+
+/* Build an file extent array for the file */
+static int map_file_extents(struct image *image, const char *filename, int f,
+ size_t size, struct extent **extents,
+ size_t *extent_count)
+{
+ struct fiemap *fiemap;
+ unsigned i;
+ int ret;
+
+ /* Get extent count */
+ fiemap = xzalloc(sizeof(struct fiemap));
+ fiemap->fm_length = size;
+ ret = ioctl(f, FS_IOC_FIEMAP, fiemap);
+ if (ret == -1)
+ goto err_out;
+
+ /* Get extents */
+ fiemap = realloc(fiemap, sizeof(struct fiemap) + fiemap->fm_mapped_extents * sizeof(struct fiemap_extent));
+ fiemap->fm_extent_count = fiemap->fm_mapped_extents;
+ ret = ioctl(f, FS_IOC_FIEMAP, fiemap);
+ if (ret == -1)
+ goto err_out;
+
+ /* Build extent array */
+ *extent_count = fiemap->fm_mapped_extents;
+ *extents = xzalloc(fiemap->fm_mapped_extents * sizeof(struct extent));
+ for (i = 0; i < fiemap->fm_mapped_extents; i++) {
+ (*extents)[i].start = fiemap->fm_extents[i].fe_logical;
+ (*extents)[i].end = fiemap->fm_extents[i].fe_logical + fiemap->fm_extents[i].fe_length;
+ }
+ free(fiemap);
+
+ /* The last extent may extend beyond the end of file, limit it to the actual end */
+ if ((*extents)[i-1].end > size)
+ (*extents)[i-1].end = size;
+
+ return 0;
+
+err_out:
+ ret = -errno;
+
+ free(fiemap);
+
+ /* If failure is due to no filesystem support, return a single extent */
+ if (ret == -EOPNOTSUPP)
+ return whole_file_exent(size, extents, extent_count);
+
+ image_error(image, "fiemap %s: %d %s\n", filename, errno, strerror(errno));
+ return ret;
+}
+
int pad_file(struct image *image, const char *infile,
size_t size, unsigned char fillpattern, enum pad_mode mode)
{
const char *outfile = imageoutfile(image);
int f = -1, outf = -1, flags = 0;
+ unsigned long f_offset = 0;
+ struct extent *extents;
+ size_t extent_count;
void *buf = NULL;
int now, r, w;
+ unsigned e;
struct stat s;
int ret = 0;
@@ -386,44 +457,71 @@ int pad_file(struct image *image, const char *infile,
goto fill;
}
- while (size) {
- now = min(size, 4096);
-
- r = read(f, buf, now);
- w = write(outf, buf, r);
- if (w < r) {
- ret = -errno;
- image_error(image, "write %s: %s\n", outfile, strerror(errno));
+ if ((s.st_mode & S_IFMT) == S_IFREG) {
+ ret = map_file_extents(image, infile, f, size, &extents, &extent_count);
+ if (ret != 0)
goto err_out;
- }
- size -= r;
-
- if (r < now)
- goto fill;
}
-
- now = read(f, buf, 1);
- if (now == 1) {
- image_error(image, "input file '%s' too large\n", infile);
- ret = -EINVAL;
- goto err_out;
+ else {
+ whole_file_exent(size, &extents, &extent_count);
}
-fill:
- memset(buf, fillpattern, 4096);
+ for (e = 0; e < extent_count && size > 0; e++) {
+ /* Ship over any holes in the input file */
+ if (f_offset != extents[e].start) {
+ unsigned long skip = extents[e].start - f_offset;
+ lseek(f, skip, SEEK_CUR);
+ lseek(outf, skip, SEEK_CUR);
+ size -= skip;
+ f_offset += skip;
+ }
- while (size) {
- now = min(size, 4096);
+ /* Copy the data in the extent */
+ while (f_offset < extents[e].end) {
+ now = min(extents[e].end - f_offset, 4096);
+
+ r = read(f, buf, now);
+ w = write(outf, buf, r);
+ if (w < r) {
+ ret = -errno;
+ image_error(image, "write %s: %s\n", outfile, strerror(errno));
+ goto err_out;
+ }
+ size -= r;
+ f_offset += r;
+
+ if (r < now)
+ goto fill;
+ }
+ }
- r = write(outf, buf, now);
- if (r < now) {
- ret = -errno;
- image_error(image, "write %s: %s\n", outfile, strerror(errno));
- goto err_out;
+fill:
+ if (fillpattern == 0 && (s.st_mode & S_IFMT) == S_IFREG) {
+ /* Truncate output to desired size */
+ image_info(image, "f_offset=%lu filesize=%llu\n", f_offset, (unsigned long long)lseek(outf, 0, SEEK_CUR));
+ image->last_offset = lseek(outf, 0, SEEK_CUR) + size;
+ ret = ftruncate(outf, image->last_offset);
+ if (ret == -1) {
+ image_error(image, "ftruncate %s: %s\n", outfile, strerror(errno));
+ goto err_out;
+ }
+ }
+ else {
+ memset(buf, fillpattern, 4096);
+
+ while (size) {
+ now = min(size, 4096);
+
+ r = write(outf, buf, now);
+ if (r < now) {
+ ret = -errno;
+ image_error(image, "write %s: %s\n", outfile, strerror(errno));
+ goto err_out;
+ }
+ size -= now;
}
- size -= now;
+ image->last_offset = lseek(outf, 0, SEEK_CUR);
}
- image->last_offset = lseek(outf, 0, SEEK_CUR);
err_out:
free(buf);
if (f >= 0)