diff options
author | Stefan Sørensen <stefan.sorensen@spectralink.com> | 2019-03-11 11:13:37 +0100 |
---|---|---|
committer | Stefan Sørensen <stefan.sorensen@spectralink.com> | 2019-03-11 11:16:25 +0100 |
commit | 1b98d6fadfca76cc0202a2df3debc67a8fe0ee1a (patch) | |
tree | 774f15ea6bbc55aa7f7daeb5b69768f5edad70c7 | |
parent | abc1c32378debaccdfb63c711de56f3a1becb84b (diff) | |
download | genimage-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-x | test/basic-images.test | 20 | ||||
-rw-r--r-- | util.c | 158 |
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 @@ -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) |