diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:49:25 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:49:25 +0000 |
commit | 464df1d5e5ab1322e2dd0a7796939fff1aeefa9a (patch) | |
tree | 6a403684e0978f0287d7f0ec0e5aab1fd31a59e1 /lib/ext2fs/sparse_io.c | |
parent | Initial commit. (diff) | |
download | e2fsprogs-upstream.tar.xz e2fsprogs-upstream.zip |
Adding upstream version 1.47.0.upstream/1.47.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | lib/ext2fs/sparse_io.c | 554 |
1 files changed, 554 insertions, 0 deletions
diff --git a/lib/ext2fs/sparse_io.c b/lib/ext2fs/sparse_io.c new file mode 100644 index 0000000..f287e76 --- /dev/null +++ b/lib/ext2fs/sparse_io.c @@ -0,0 +1,554 @@ +#include "config.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdint.h> +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#if !defined(ENABLE_LIBSPARSE) +static errcode_t sparse_open(const char *name EXT2FS_ATTR((unused)), + int flags EXT2FS_ATTR((unused)), + io_channel *channel EXT2FS_ATTR((unused))) +{ + return EXT2_ET_UNIMPLEMENTED; +} +static errcode_t sparse_close(io_channel channel EXT2FS_ATTR((unused))) +{ + return EXT2_ET_UNIMPLEMENTED; +} +static struct struct_io_manager struct_sparse_manager = { + .magic = EXT2_ET_MAGIC_IO_MANAGER, + .name = "Android sparse I/O Manager", + .open = sparse_open, + .close = sparse_close, +}; +static struct struct_io_manager struct_sparsefd_manager = { + .magic = EXT2_ET_MAGIC_IO_MANAGER, + .name = "Android sparse fd I/O Manager", + .open = sparse_open, + .close = sparse_close, +}; +#else +#include <sparse/sparse.h> + +struct sparse_map { + int fd; + char **blocks; + int block_size; + uint64_t blocks_count; + char *file; + struct sparse_file *sparse_file; + io_channel channel; +}; + +struct sparse_io_params { + int fd; + char *file; + uint64_t blocks_count; + unsigned int block_size; +}; + +static errcode_t sparse_write_blk(io_channel channel, unsigned long block, + int count, const void *buf); + +static void free_sparse_blocks(struct sparse_map *sm) +{ + uint64_t i; + + for (i = 0; i < sm->blocks_count; ++i) + free(sm->blocks[i]); + free(sm->blocks); + sm->blocks = NULL; +} + +static int sparse_import_segment(void *priv, const void *data, size_t len, + unsigned int block, unsigned int nr_blocks) +{ + struct sparse_map *sm = priv; + + /* Ignore chunk headers, only write the data */ + if (!nr_blocks || len % sm->block_size) + return 0; + + return sparse_write_blk(sm->channel, block, nr_blocks, data); +} + +static errcode_t io_manager_import_sparse(struct sparse_io_params *params, + struct sparse_map *sm, io_channel io) +{ + int fd; + errcode_t retval; + struct sparse_file *sparse_file; + + if (params->fd < 0) { + fd = open(params->file, O_RDONLY); + if (fd < 0) { + retval = -1; + goto err_open; + } + } else + fd = params->fd; + sparse_file = sparse_file_import(fd, false, false); + if (!sparse_file) { + retval = -1; + goto err_sparse; + } + + sm->block_size = sparse_file_block_size(sparse_file); + sm->blocks_count = (sparse_file_len(sparse_file, 0, 0) - 1) + / sm->block_size + 1; + sm->blocks = calloc(sm->blocks_count, sizeof(char*)); + if (!sm->blocks) { + retval = -1; + goto err_alloc; + } + io->block_size = sm->block_size; + + retval = sparse_file_foreach_chunk(sparse_file, true, false, + sparse_import_segment, sm); + + if (retval) + free_sparse_blocks(sm); +err_alloc: + sparse_file_destroy(sparse_file); +err_sparse: + close(fd); +err_open: + return retval; +} + +static errcode_t io_manager_configure(struct sparse_io_params *params, + int flags, io_channel io) +{ + errcode_t retval; + uint64_t img_size; + struct sparse_map *sm = calloc(1, sizeof(*sm)); + if (!sm) + return EXT2_ET_NO_MEMORY; + + sm->file = params->file; + sm->channel = io; + io->private_data = sm; + retval = io_manager_import_sparse(params, sm, io); + if (retval) { + if (!params->block_size || !params->blocks_count) { + retval = EINVAL; + goto err_params; + } + sm->block_size = params->block_size; + sm->blocks_count = params->blocks_count; + sm->blocks = calloc(params->blocks_count, sizeof(void*)); + if (!sm->blocks) { + retval = EXT2_ET_NO_MEMORY; + goto err_alloc; + } + } + io->block_size = sm->block_size; + img_size = (uint64_t)sm->block_size * sm->blocks_count; + + if (flags & IO_FLAG_RW) { + sm->sparse_file = sparse_file_new(sm->block_size, img_size); + if (!sm->sparse_file) { + retval = EXT2_ET_NO_MEMORY; + goto err_alloc; + } + if (params->fd < 0) { + sm->fd = open(params->file, O_CREAT | O_RDWR | O_TRUNC | O_BINARY, + 0644); + if (sm->fd < 0) { + retval = errno; + goto err_open; + } + } else + sm->fd = params->fd; + } else { + sm->fd = -1; + sm->sparse_file = NULL; + } + return 0; + +err_open: + sparse_file_destroy(sm->sparse_file); +err_alloc: + free_sparse_blocks(sm); +err_params: + free(sm); + return retval; +} + +static errcode_t sparse_open_channel(struct sparse_io_params *sparse_params, + int flags, io_channel *channel) +{ + errcode_t retval; + io_channel io; + + io = calloc(1, sizeof(struct struct_io_channel)); + io->magic = EXT2_ET_MAGIC_IO_CHANNEL; + io->block_size = 0; + io->refcount = 1; + + retval = io_manager_configure(sparse_params, flags, io); + if (retval) { + free(io); + return retval; + } + + *channel = io; + return 0; +} + +static errcode_t read_sparse_argv(const char *name, bool is_fd, + struct sparse_io_params *sparse_params) +{ + int ret; + sparse_params->fd = -1; + sparse_params->block_size = 0; + sparse_params->blocks_count = 0; + + sparse_params->file = malloc(strlen(name) + 1); + if (!sparse_params->file) { + fprintf(stderr, "failed to alloc %zu\n", strlen(name) + 1); + return EXT2_ET_NO_MEMORY; + } + + if (is_fd) { + ret = sscanf(name, "(%d):%llu:%u", &sparse_params->fd, + (unsigned long long *)&sparse_params->blocks_count, + &sparse_params->block_size); + } else { + ret = sscanf(name, "(%[^)])%*[:]%llu%*[:]%u", sparse_params->file, + (unsigned long long *)&sparse_params->blocks_count, + &sparse_params->block_size); + } + + if (ret < 1) { + free(sparse_params->file); + return EINVAL; + } + return 0; +} + +static errcode_t sparse_open(const char *name, int flags, io_channel *channel) +{ + errcode_t retval; + struct sparse_io_params sparse_params; + + retval = read_sparse_argv(name, false, &sparse_params); + if (retval) + return EXT2_ET_BAD_DEVICE_NAME; + + retval = sparse_open_channel(&sparse_params, flags, channel); + if (retval) + return retval; + (*channel)->manager = sparse_io_manager; + + return retval; +} + +static errcode_t sparsefd_open(const char *name, int flags, io_channel *channel) +{ + errcode_t retval; + struct sparse_io_params sparse_params; + + retval = read_sparse_argv(name, true, &sparse_params); + if (retval) + return EXT2_ET_BAD_DEVICE_NAME; + + retval = sparse_open_channel(&sparse_params, flags, channel); + if (retval) + return retval; + (*channel)->manager = sparsefd_io_manager; + + return retval; +} + +static errcode_t sparse_merge_blocks(struct sparse_map *sm, uint64_t start, + uint64_t num) +{ + char *buf; + uint64_t i; + unsigned int block_size = sm->block_size; + errcode_t retval = 0; + + buf = calloc(num, block_size); + if (!buf) { + fprintf(stderr, "failed to alloc %llu\n", + (unsigned long long)num * block_size); + return EXT2_ET_NO_MEMORY; + } + + for (i = 0; i < num; i++) { + memcpy(buf + i * block_size, sm->blocks[start + i] , block_size); + free(sm->blocks[start + i]); + sm->blocks[start + i] = NULL; + } + + /* free_sparse_blocks will release this buf. */ + sm->blocks[start] = buf; + + retval = sparse_file_add_data(sm->sparse_file, sm->blocks[start], + block_size * num, start); + + return retval; +} + +static errcode_t sparse_close_channel(io_channel channel) +{ + uint64_t i; + errcode_t retval = 0; + struct sparse_map *sm = channel->private_data; + + if (sm->sparse_file) { + int64_t chunk_start = (sm->blocks[0] == NULL) ? -1 : 0; + for (i = 0; i < sm->blocks_count; ++i) { + if (!sm->blocks[i] && chunk_start != -1) { + retval = sparse_merge_blocks(sm, chunk_start, i - chunk_start); + chunk_start = -1; + } else if (sm->blocks[i] && chunk_start == -1) { + chunk_start = i; + } + if (retval) + goto ret; + } + if (chunk_start != -1) { + retval = sparse_merge_blocks(sm, chunk_start, + sm->blocks_count - chunk_start); + if (retval) + goto ret; + } + retval = sparse_file_write(sm->sparse_file, sm->fd, + /*gzip*/0, /*sparse*/1, /*crc*/0); + } + +ret: + if (sm->sparse_file) + sparse_file_destroy(sm->sparse_file); + free_sparse_blocks(sm); + free(sm->file); + free(sm); + free(channel); + return retval; +} + +static errcode_t sparse_close(io_channel channel) +{ + errcode_t retval; + struct sparse_map *sm = channel->private_data; + int fd = sm->fd; + + retval = sparse_close_channel(channel); + if (fd >= 0) + close(fd); + + return retval; +} + +static errcode_t sparse_set_blksize(io_channel channel, int blksize) +{ + channel->block_size = blksize; + return 0; +} + +static blk64_t block_to_sparse_block(blk64_t block, blk64_t *offset, + io_channel channel, struct sparse_map *sm) +{ + int ratio; + blk64_t ret = block; + + ratio = sm->block_size / channel->block_size; + ret /= ratio; + *offset = (block % ratio) * channel->block_size; + + return ret; +} + +static errcode_t check_block_size(io_channel channel, struct sparse_map *sm) +{ + if (sm->block_size >= channel->block_size) + return 0; + return EXT2_ET_UNEXPECTED_BLOCK_SIZE; +} + +static errcode_t sparse_read_blk64(io_channel channel, blk64_t block, + int count, void *buf) +{ + int i; + char *out = buf; + blk64_t offset = 0, cur_block; + struct sparse_map *sm = channel->private_data; + + if (check_block_size(channel, sm)) + return EXT2_ET_UNEXPECTED_BLOCK_SIZE; + + if (count < 0) { //partial read + count = -count; + cur_block = block_to_sparse_block(block, &offset, channel, sm); + if (sm->blocks[cur_block]) + memcpy(out, (sm->blocks[cur_block]) + offset, count); + else + memset(out, 0, count); + } else { + for (i = 0; i < count; ++i) { + cur_block = block_to_sparse_block(block + i, &offset, + channel, sm); + if (sm->blocks[cur_block]) + memcpy(out + (i * channel->block_size), + sm->blocks[cur_block] + offset, + channel->block_size); + else if (sm->blocks) + memset(out + (i * channel->block_size), 0, + channel->block_size); + } + } + return 0; +} + +static errcode_t sparse_read_blk(io_channel channel, unsigned long block, + int count, void *buf) +{ + return sparse_read_blk64(channel, block, count, buf); +} + +static errcode_t sparse_write_blk64(io_channel channel, blk64_t block, + int count, const void *buf) +{ + int i; + blk64_t offset = 0, cur_block; + const char *in = buf; + struct sparse_map *sm = channel->private_data; + + if (check_block_size(channel, sm)) + return EXT2_ET_UNEXPECTED_BLOCK_SIZE; + + if (count < 0) { //partial write + count = -count; + cur_block = block_to_sparse_block(block, &offset, channel, + sm); + if (!sm->blocks[cur_block]) { + sm->blocks[cur_block] = calloc(1, sm->block_size); + if (!sm->blocks[cur_block]) + return EXT2_ET_NO_MEMORY; + } + memcpy(sm->blocks[cur_block] + offset, in, count); + } else { + for (i = 0; i < count; ++i) { + if (block + i >= sm->blocks_count) + return 0; + cur_block = block_to_sparse_block(block + i, &offset, + channel, sm); + if (!sm->blocks[cur_block]) { + sm->blocks[cur_block] = + calloc(1, sm->block_size); + if (!sm->blocks[cur_block]) + return EXT2_ET_NO_MEMORY; + } + memcpy(sm->blocks[cur_block] + offset, + in + (i * channel->block_size), + channel->block_size); + } + } + return 0; +} + +static errcode_t sparse_write_blk(io_channel channel, unsigned long block, + int count, const void *buf) +{ + return sparse_write_blk64(channel, block, count, buf); +} + +static errcode_t sparse_discard(io_channel channel __attribute__((unused)), + blk64_t blk, unsigned long long count) +{ + blk64_t cur_block, offset; + struct sparse_map *sm = channel->private_data; + + if (check_block_size(channel, sm)) + return EXT2_ET_UNEXPECTED_BLOCK_SIZE; + + for (unsigned long long i = 0; i < count; ++i) { + if (blk + i >= sm->blocks_count) + return 0; + cur_block = block_to_sparse_block(blk + i, &offset, channel, + sm); + if (!sm->blocks[cur_block]) + continue; + free(sm->blocks[cur_block]); + sm->blocks[cur_block] = NULL; + } + return 0; +} + +static errcode_t sparse_zeroout(io_channel channel, blk64_t blk, + unsigned long long count) +{ + return sparse_discard(channel, blk, count); +} + +static errcode_t sparse_flush(io_channel channel __attribute__((unused))) +{ + return 0; +} + +static errcode_t sparse_set_option(io_channel channel __attribute__((unused)), + const char *option __attribute__((unused)), + const char *arg __attribute__((unused))) +{ + return 0; +} + +static errcode_t sparse_cache_readahead( + io_channel channel __attribute__((unused)), + blk64_t blk __attribute__((unused)), + unsigned long long count __attribute__((unused))) +{ + return 0; +} + +static struct struct_io_manager struct_sparse_manager = { + .magic = EXT2_ET_MAGIC_IO_MANAGER, + .name = "Android sparse I/O Manager", + .open = sparse_open, + .close = sparse_close, + .set_blksize = sparse_set_blksize, + .read_blk = sparse_read_blk, + .write_blk = sparse_write_blk, + .flush = sparse_flush, + .write_byte = NULL, + .set_option = sparse_set_option, + .get_stats = NULL, + .read_blk64 = sparse_read_blk64, + .write_blk64 = sparse_write_blk64, + .discard = sparse_discard, + .cache_readahead = sparse_cache_readahead, + .zeroout = sparse_zeroout, +}; + +static struct struct_io_manager struct_sparsefd_manager = { + .magic = EXT2_ET_MAGIC_IO_MANAGER, + .name = "Android sparse fd I/O Manager", + .open = sparsefd_open, + .close = sparse_close, + .set_blksize = sparse_set_blksize, + .read_blk = sparse_read_blk, + .write_blk = sparse_write_blk, + .flush = sparse_flush, + .write_byte = NULL, + .set_option = sparse_set_option, + .get_stats = NULL, + .read_blk64 = sparse_read_blk64, + .write_blk64 = sparse_write_blk64, + .discard = sparse_discard, + .cache_readahead = sparse_cache_readahead, + .zeroout = sparse_zeroout, +}; + +#endif + +io_manager sparse_io_manager = &struct_sparse_manager; +io_manager sparsefd_io_manager = &struct_sparsefd_manager; |