diff --git a/libsparse/backed_block.c b/libsparse/backed_block.c index b25919008..8c3fab04a 100644 --- a/libsparse/backed_block.c +++ b/libsparse/backed_block.c @@ -34,6 +34,10 @@ struct backed_block { char *filename; int64_t offset; } file; + struct { + int fd; + int64_t offset; + } fd; struct { uint32_t val; } fill; @@ -78,10 +82,20 @@ const char *backed_block_filename(struct backed_block *bb) return bb->file.filename; } +int backed_block_fd(struct backed_block *bb) +{ + assert(bb->type == BACKED_BLOCK_FD); + return bb->fd.fd; +} + int64_t backed_block_file_offset(struct backed_block *bb) { - assert(bb->type == BACKED_BLOCK_FILE); - return bb->file.offset; + assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD); + if (bb->type == BACKED_BLOCK_FILE) { + return bb->file.offset; + } else { /* bb->type == BACKED_BLOCK_FD */ + return bb->fd.offset; + } } uint32_t backed_block_fill_val(struct backed_block *bb) @@ -211,3 +225,22 @@ int backed_block_add_file(struct backed_block_list *bbl, const char *filename, return queue_bb(bbl, bb); } + +/* Queues a chunk of a fd to be written to the specified data blocks */ +int backed_block_add_fd(struct backed_block_list *bbl, int fd, int64_t offset, + unsigned int len, unsigned int block) +{ + struct backed_block *bb = calloc(1, sizeof(struct backed_block)); + if (bb == NULL) { + return -ENOMEM; + } + + bb->block = block; + bb->len = len; + bb->type = BACKED_BLOCK_FD; + bb->fd.fd = fd; + bb->fd.offset = offset; + bb->next = NULL; + + return queue_bb(bbl, bb); +} diff --git a/libsparse/backed_block.h b/libsparse/backed_block.h index 316650505..ca2ad1d9b 100644 --- a/libsparse/backed_block.h +++ b/libsparse/backed_block.h @@ -25,6 +25,7 @@ struct backed_block; enum backed_block_type { BACKED_BLOCK_DATA, BACKED_BLOCK_FILE, + BACKED_BLOCK_FD, BACKED_BLOCK_FILL, }; @@ -34,6 +35,8 @@ int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val, unsigned int len, unsigned int block); int backed_block_add_file(struct backed_block_list *bbl, const char *filename, int64_t offset, unsigned int len, unsigned int block); +int backed_block_add_fd(struct backed_block_list *bbl, int fd, + int64_t offset, unsigned int len, unsigned int block); struct backed_block *backed_block_iter_new(struct backed_block_list *bbl); struct backed_block *backed_block_iter_next(struct backed_block *bb); @@ -41,6 +44,7 @@ unsigned int backed_block_len(struct backed_block *bb); unsigned int backed_block_block(struct backed_block *bb); void *backed_block_data(struct backed_block *bb); const char *backed_block_filename(struct backed_block *bb); +int backed_block_fd(struct backed_block *bb); int64_t backed_block_file_offset(struct backed_block *bb); uint32_t backed_block_fill_val(struct backed_block *bb); enum backed_block_type backed_block_type(struct backed_block *bb); diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h index db0688471..6484333ef 100644 --- a/libsparse/include/sparse/sparse.h +++ b/libsparse/include/sparse/sparse.h @@ -110,6 +110,32 @@ int sparse_file_add_file(struct sparse_file *s, const char *filename, int64_t file_offset, unsigned int len, unsigned int block); +/** + * sparse_file_add_file - associate a chunk of a file with a sparse file + * + * @s - sparse file cookie + * @filename - filename of the file to be copied + * @file_offset - offset into the copied file + * @len - length of the copied block + * @block - offset in blocks into the sparse file to place the file chunk + * + * Associates a chunk of an existing fd with a sparse file cookie. + * The region [block * block_size : block * block_size + len) must not already + * be used in the sparse file. If len is not a multiple of the block size the + * data will be padded with zeros. + * + * Allows adding large amounts of data to a sparse file without needing to keep + * it all mapped. File size is limited by available virtual address space, + * exceptionally large files may need to be added in multiple chunks. + * + * The fd must remain open until the sparse file is closed or the fd block is + * removed from the sparse file. + * + * Returns 0 on success, negative errno on error. + */ +int sparse_file_add_fd(struct sparse_file *s, + int fd, int64_t file_offset, unsigned int len, unsigned int block); + /** * sparse_file_write - write a sparse file to a file * diff --git a/libsparse/output_file.c b/libsparse/output_file.c index f911f8cc5..4193fd17d 100644 --- a/libsparse/output_file.c +++ b/libsparse/output_file.c @@ -511,38 +511,28 @@ int write_fill_chunk(struct output_file *out, unsigned int len, return out->sparse_ops->write_fill_chunk(out, len, fill_val); } -/* Write a contiguous region of data blocks from a file */ -int write_file_chunk(struct output_file *out, unsigned int len, - const char *file, int64_t offset) +int write_fd_chunk(struct output_file *out, unsigned int len, + int fd, int64_t offset) { int ret; int64_t aligned_offset; int aligned_diff; int buffer_size; - int file_fd = open(file, O_RDONLY | O_BINARY); - if (file_fd < 0) { - return -errno; - } - aligned_offset = offset & ~(4096 - 1); aligned_diff = offset - aligned_offset; buffer_size = len + aligned_diff; #ifndef USE_MINGW - char *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, file_fd, + char *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd, aligned_offset); if (data == MAP_FAILED) { - ret = -errno; - close(file_fd); - return ret; + return -errno; } #else char *data = malloc(buffer_size); if (!data) { - ret = -errno; - close(file_fd); - return ret; + return -errno; } memset(data, 0, buffer_size); #endif @@ -554,6 +544,23 @@ int write_file_chunk(struct output_file *out, unsigned int len, #else free(data); #endif + + return ret; +} + +/* Write a contiguous region of data blocks from a file */ +int write_file_chunk(struct output_file *out, unsigned int len, + const char *file, int64_t offset) +{ + int ret; + + int file_fd = open(file, O_RDONLY | O_BINARY); + if (file_fd < 0) { + return -errno; + } + + ret = write_fd_chunk(out, len, file_fd, offset); + close(file_fd); return ret; diff --git a/libsparse/output_file.h b/libsparse/output_file.h index cb2feb7fb..d23abf381 100644 --- a/libsparse/output_file.h +++ b/libsparse/output_file.h @@ -31,6 +31,8 @@ int write_fill_chunk(struct output_file *out, unsigned int len, uint32_t fill_val); int write_file_chunk(struct output_file *out, unsigned int len, const char *file, int64_t offset); +int write_fd_chunk(struct output_file *out, unsigned int len, + int fd, int64_t offset); int write_skip_chunk(struct output_file *out, int64_t len); void close_output_file(struct output_file *out); diff --git a/libsparse/sparse.c b/libsparse/sparse.c index fce9dbbb6..4ebcf0f8d 100644 --- a/libsparse/sparse.c +++ b/libsparse/sparse.c @@ -70,6 +70,12 @@ int sparse_file_add_file(struct sparse_file *s, len, block); } +int sparse_file_add_fd(struct sparse_file *s, + int fd, int64_t file_offset, unsigned int len, unsigned int block) +{ + return backed_block_add_fd(s->backed_block_list, fd, file_offset, + len, block); +} unsigned int sparse_count_chunks(struct sparse_file *s) { struct backed_block *bb; @@ -122,6 +128,10 @@ int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse, write_file_chunk(out, backed_block_len(bb), backed_block_filename(bb), backed_block_file_offset(bb)); break; + case BACKED_BLOCK_FD: + write_fd_chunk(out, backed_block_len(bb), + backed_block_fd(bb), backed_block_file_offset(bb)); + break; case BACKED_BLOCK_FILL: write_fill_chunk(out, backed_block_len(bb), backed_block_fill_val(bb));