summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJerry Zhang2018-06-14 18:58:58 -0500
committerHridya Valsaraju2018-07-25 13:04:03 -0500
commitdb69f0d47f3ccb3ff656c56fe2b68aaf5ab853f6 (patch)
treee10ad5680b134599df00f4322a82242690940eb7 /libsparse
parent1c92fa44800c9d2efd3f2b8f18a3af7e131371be (diff)
downloadplatform-system-core-db69f0d47f3ccb3ff656c56fe2b68aaf5ab853f6.tar.gz
platform-system-core-db69f0d47f3ccb3ff656c56fe2b68aaf5ab853f6.tar.xz
platform-system-core-db69f0d47f3ccb3ff656c56fe2b68aaf5ab853f6.zip
libsparse: Add sparse typed callback
Currently, sparse_file_callback uses libsparse's own logic for reading a file into a buffer. However, a caller may want to use their own logic for doing this in order to customize the buffer size and parallelize the reads/writes. Also, a caller may want to implement fill blocks with their own logic as well. To do this add sparse_file_typed_callback which calls a different callback function depending on the type of the sparse chunk being written. Test: Use typed callback for fd writes Bug: 78793464 Change-Id: I75955a464fc05991f806339830fdfa05fda354b9
Diffstat (limited to 'libsparse')
-rw-r--r--libsparse/include/sparse/sparse.h31
-rw-r--r--libsparse/output_file.cpp264
-rw-r--r--libsparse/output_file.h10
-rw-r--r--libsparse/sparse.cpp37
4 files changed, 246 insertions, 96 deletions
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 3d5fb0c53..586578633 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -210,6 +210,37 @@ int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc,
210 int (*write)(void *priv, const void *data, size_t len), void *priv); 210 int (*write)(void *priv, const void *data, size_t len), void *priv);
211 211
212/** 212/**
213 * sparse_file_callback_typed - call a callback for blocks based on type
214 *
215 * @s - sparse file cookie
216 * @sparse - write in the Android sparse file format
217 * @data_write - function to call for data blocks. must not be null
218 * @fd_write - function to call for fd blocks
219 * @fill_write - function to call for fill blocks
220 * @skip_write - function to call for skip blocks
221 * @priv - value that will be passed as the first argument to each write
222 *
223 * Writes a sparse file by calling callback functions. If sparse is true, the
224 * file will be written in the Android sparse file format, and fill and skip blocks
225 * along with metadata will be written with data_write. If sparse is false, the file
226 * will be expanded into normal format and fill and skip blocks will be written with
227 * the given callbacks.
228 * If a callback function is provided, the library will not unroll data into a buffer,
229 * and will instead pass it directly to the caller for custom implementation. If a
230 * callback is not provided, that type of block will be converted into a void* and
231 * written with data_write. If no callbacks other than data are provided, the behavior
232 * is the same as sparse_file_callback(). The callback should return negative on error,
233 * 0 on success.
234 *
235 * Returns 0 on success, negative errno on error.
236 */
237int sparse_file_callback_typed(struct sparse_file* s, bool sparse,
238 int (*data_write)(void* priv, const void* data, size_t len),
239 int (*fd_write)(void* priv, int fd, size_t len),
240 int (*fill_write)(void* priv, uint32_t fill_val, size_t len),
241 int (*skip_write)(void* priv, int64_t len), void* priv);
242
243/**
213 * sparse_file_foreach_chunk - call a callback for data blocks in sparse file 244 * sparse_file_foreach_chunk - call a callback for data blocks in sparse file
214 * 245 *
215 * @s - sparse file cookie 246 * @s - sparse file cookie
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index fe314b30e..8a21daba1 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -29,6 +29,8 @@
29#include <unistd.h> 29#include <unistd.h>
30#include <zlib.h> 30#include <zlib.h>
31 31
32#include <algorithm>
33
32#include "defs.h" 34#include "defs.h"
33#include "output_file.h" 35#include "output_file.h"
34#include "sparse_crc32.h" 36#include "sparse_crc32.h"
@@ -48,13 +50,6 @@
48#define off64_t off_t 50#define off64_t off_t
49#endif 51#endif
50 52
51#define min(a, b) \
52 ({ \
53 typeof(a) _a = (a); \
54 typeof(b) _b = (b); \
55 (_a < _b) ? _a : _b; \
56 })
57
58#define SPARSE_HEADER_MAJOR_VER 1 53#define SPARSE_HEADER_MAJOR_VER 1
59#define SPARSE_HEADER_MINOR_VER 0 54#define SPARSE_HEADER_MINOR_VER 0
60#define SPARSE_HEADER_LEN (sizeof(sparse_header_t)) 55#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
@@ -67,11 +62,14 @@ struct output_file_ops {
67 int (*skip)(struct output_file*, int64_t); 62 int (*skip)(struct output_file*, int64_t);
68 int (*pad)(struct output_file*, int64_t); 63 int (*pad)(struct output_file*, int64_t);
69 int (*write)(struct output_file*, void*, size_t); 64 int (*write)(struct output_file*, void*, size_t);
65 int (*write_fd)(struct output_file*, int, size_t);
66 int (*write_fill)(struct output_file*, uint32_t, size_t);
70 void (*close)(struct output_file*); 67 void (*close)(struct output_file*);
71}; 68};
72 69
73struct sparse_file_ops { 70struct sparse_file_ops {
74 int (*write_data_chunk)(struct output_file* out, unsigned int len, void* data); 71 int (*write_data_chunk)(struct output_file* out, unsigned int len, void* data);
72 int (*write_fd_chunk)(struct output_file* out, unsigned int len, int fd);
75 int (*write_fill_chunk)(struct output_file* out, unsigned int len, uint32_t fill_val); 73 int (*write_fill_chunk)(struct output_file* out, unsigned int len, uint32_t fill_val);
76 int (*write_skip_chunk)(struct output_file* out, int64_t len); 74 int (*write_skip_chunk)(struct output_file* out, int64_t len);
77 int (*write_end_chunk)(struct output_file* out); 75 int (*write_end_chunk)(struct output_file* out);
@@ -108,11 +106,96 @@ struct output_file_normal {
108struct output_file_callback { 106struct output_file_callback {
109 struct output_file out; 107 struct output_file out;
110 void* priv; 108 void* priv;
111 int (*write)(void* priv, const void* buf, size_t len); 109 int (*data_write)(void* priv, const void* data, size_t len);
110 int (*fd_write)(void* priv, int fd, size_t len);
111 int (*fill_write)(void* priv, uint32_t fill_val, size_t len);
112 int (*skip_write)(void* priv, off64_t len);
112}; 113};
113 114
114#define to_output_file_callback(_o) container_of((_o), struct output_file_callback, out) 115#define to_output_file_callback(_o) container_of((_o), struct output_file_callback, out)
115 116
117union handle_data {
118 void* data_ptr;
119 int fd;
120};
121
122static int default_file_write_fd(struct output_file* out, int fd, size_t len) {
123 int ret;
124 int64_t aligned_offset;
125 int aligned_diff;
126 uint64_t buffer_size;
127 char* ptr;
128 int64_t offset = lseek64(fd, 0, SEEK_CUR);
129
130 if (offset < 0) {
131 return -errno;
132 }
133
134 aligned_offset = offset & ~(4096 - 1);
135 aligned_diff = offset - aligned_offset;
136 buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
137
138#ifndef _WIN32
139 if (buffer_size > SIZE_MAX) {
140 return -E2BIG;
141 }
142 char* data =
143 reinterpret_cast<char*>(mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd, aligned_offset));
144 if (data == MAP_FAILED) {
145 return -errno;
146 }
147 ptr = data + aligned_diff;
148#else
149 char* data = reinterpret_cast<char*>(malloc(len));
150 if (!data) {
151 return -errno;
152 }
153 ret = read_all(fd, data, len);
154 if (ret < 0) {
155 free(data);
156 return -errno;
157 }
158 ptr = data;
159#endif
160
161 ret = out->ops->write(out, ptr, len);
162
163 if (out->use_crc) {
164 out->crc32 = sparse_crc32(out->crc32, ptr, len);
165 }
166
167#ifndef _WIN32
168 munmap(data, buffer_size);
169#else
170 free(data);
171#endif
172
173 return ret;
174}
175
176static int default_file_write_fill(struct output_file* out, uint32_t fill_val, size_t len) {
177 int ret;
178 unsigned int i;
179 unsigned int write_len;
180
181 /* Initialize fill_buf with the fill_val */
182 for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
183 out->fill_buf[i] = fill_val;
184 }
185
186 while (len) {
187 write_len = std::min(len, static_cast<size_t>(out->block_size));
188 ret = out->ops->write(out, out->fill_buf, write_len);
189 if (ret < 0) {
190 return ret;
191 }
192
193 len -= write_len;
194 }
195
196 return 0;
197}
198
116static int file_open(struct output_file* out, int fd) { 199static int file_open(struct output_file* out, int fd) {
117 struct output_file_normal* outn = to_output_file_normal(out); 200 struct output_file_normal* outn = to_output_file_normal(out);
118 201
@@ -176,6 +259,8 @@ static struct output_file_ops file_ops = {
176 .skip = file_skip, 259 .skip = file_skip,
177 .pad = file_pad, 260 .pad = file_pad,
178 .write = file_write, 261 .write = file_write,
262 .write_fd = default_file_write_fd,
263 .write_fill = default_file_write_fill,
179 .close = file_close, 264 .close = file_close,
180}; 265};
181 266
@@ -231,7 +316,7 @@ static int gz_file_write(struct output_file* out, void* data, size_t len) {
231 struct output_file_gz* outgz = to_output_file_gz(out); 316 struct output_file_gz* outgz = to_output_file_gz(out);
232 317
233 while (len > 0) { 318 while (len > 0) {
234 ret = gzwrite(outgz->gz_fd, data, min(len, (unsigned int)INT_MAX)); 319 ret = gzwrite(outgz->gz_fd, data, std::min(len, static_cast<size_t>(INT_MAX)));
235 if (ret == 0) { 320 if (ret == 0) {
236 error("gzwrite %s", gzerror(outgz->gz_fd, nullptr)); 321 error("gzwrite %s", gzerror(outgz->gz_fd, nullptr));
237 return -1; 322 return -1;
@@ -255,6 +340,8 @@ static struct output_file_ops gz_file_ops = {
255 .skip = gz_file_skip, 340 .skip = gz_file_skip,
256 .pad = gz_file_pad, 341 .pad = gz_file_pad,
257 .write = gz_file_write, 342 .write = gz_file_write,
343 .write_fd = default_file_write_fd,
344 .write_fill = default_file_write_fill,
258 .close = gz_file_close, 345 .close = gz_file_close,
259}; 346};
260 347
@@ -267,9 +354,13 @@ static int callback_file_skip(struct output_file* out, int64_t off) {
267 int to_write; 354 int to_write;
268 int ret; 355 int ret;
269 356
357 if (outc->skip_write) {
358 return outc->skip_write(outc->priv, off);
359 }
360
270 while (off > 0) { 361 while (off > 0) {
271 to_write = min(off, (int64_t)INT_MAX); 362 to_write = std::min(off, static_cast<int64_t>(INT_MAX));
272 ret = outc->write(outc->priv, nullptr, to_write); 363 ret = outc->data_write(outc->priv, nullptr, to_write);
273 if (ret < 0) { 364 if (ret < 0) {
274 return ret; 365 return ret;
275 } 366 }
@@ -285,8 +376,23 @@ static int callback_file_pad(struct output_file* out __unused, int64_t len __unu
285 376
286static int callback_file_write(struct output_file* out, void* data, size_t len) { 377static int callback_file_write(struct output_file* out, void* data, size_t len) {
287 struct output_file_callback* outc = to_output_file_callback(out); 378 struct output_file_callback* outc = to_output_file_callback(out);
379 return outc->data_write(outc->priv, data, len);
380}
381
382static int callback_file_write_fd(struct output_file* out, int fd, size_t len) {
383 struct output_file_callback* outc = to_output_file_callback(out);
384 if (outc->fd_write) {
385 return outc->fd_write(outc->priv, fd, len);
386 }
387 return default_file_write_fd(out, fd, len);
388}
288 389
289 return outc->write(outc->priv, data, len); 390static int callback_file_write_fill(struct output_file* out, uint32_t fill_val, size_t len) {
391 struct output_file_callback* outc = to_output_file_callback(out);
392 if (outc->fill_write) {
393 return outc->fill_write(outc->priv, fill_val, len);
394 }
395 return default_file_write_fill(out, fill_val, len);
290} 396}
291 397
292static void callback_file_close(struct output_file* out) { 398static void callback_file_close(struct output_file* out) {
@@ -300,6 +406,8 @@ static struct output_file_ops callback_file_ops = {
300 .skip = callback_file_skip, 406 .skip = callback_file_skip,
301 .pad = callback_file_pad, 407 .pad = callback_file_pad,
302 .write = callback_file_write, 408 .write = callback_file_write,
409 .write_fd = callback_file_write_fd,
410 .write_fill = callback_file_write_fill,
303 .close = callback_file_close, 411 .close = callback_file_close,
304}; 412};
305 413
@@ -376,7 +484,8 @@ static int write_sparse_fill_chunk(struct output_file* out, unsigned int len, ui
376 return 0; 484 return 0;
377} 485}
378 486
379static int write_sparse_data_chunk(struct output_file* out, unsigned int len, void* data) { 487static int write_sparse_data_chunk_variant(struct output_file* out, unsigned int len,
488 handle_data data, bool is_fd) {
380 chunk_header_t chunk_header; 489 chunk_header_t chunk_header;
381 int rnd_up_len, zero_len; 490 int rnd_up_len, zero_len;
382 int ret; 491 int ret;
@@ -393,7 +502,16 @@ static int write_sparse_data_chunk(struct output_file* out, unsigned int len, vo
393 ret = out->ops->write(out, &chunk_header, sizeof(chunk_header)); 502 ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
394 503
395 if (ret < 0) return -1; 504 if (ret < 0) return -1;
396 ret = out->ops->write(out, data, len); 505
506 if (is_fd) {
507 // CRC is handled by write_fd
508 ret = out->ops->write_fd(out, data.fd, len);
509 } else {
510 ret = out->ops->write(out, data.data_ptr, len);
511 if (out->use_crc) {
512 out->crc32 = sparse_crc32(out->crc32, data.data_ptr, len);
513 }
514 }
397 if (ret < 0) return -1; 515 if (ret < 0) return -1;
398 if (zero_len) { 516 if (zero_len) {
399 ret = out->ops->write(out, out->zero_buf, zero_len); 517 ret = out->ops->write(out, out->zero_buf, zero_len);
@@ -401,7 +519,6 @@ static int write_sparse_data_chunk(struct output_file* out, unsigned int len, vo
401 } 519 }
402 520
403 if (out->use_crc) { 521 if (out->use_crc) {
404 out->crc32 = sparse_crc32(out->crc32, data, len);
405 if (zero_len) out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len); 522 if (zero_len) out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
406 } 523 }
407 524
@@ -411,6 +528,16 @@ static int write_sparse_data_chunk(struct output_file* out, unsigned int len, vo
411 return 0; 528 return 0;
412} 529}
413 530
531static int write_sparse_data_chunk(struct output_file* out, unsigned int len, void* data_ptr) {
532 handle_data data = {data_ptr};
533 return write_sparse_data_chunk_variant(out, len, data, false /* isFd */);
534}
535
536static int write_sparse_fd_chunk(struct output_file* out, unsigned int len, int fd) {
537 handle_data data = {.fd = fd};
538 return write_sparse_data_chunk_variant(out, len, data, true /* isFd */);
539}
540
414int write_sparse_end_chunk(struct output_file* out) { 541int write_sparse_end_chunk(struct output_file* out) {
415 chunk_header_t chunk_header; 542 chunk_header_t chunk_header;
416 int ret; 543 int ret;
@@ -438,16 +565,22 @@ int write_sparse_end_chunk(struct output_file* out) {
438 565
439static struct sparse_file_ops sparse_file_ops = { 566static struct sparse_file_ops sparse_file_ops = {
440 .write_data_chunk = write_sparse_data_chunk, 567 .write_data_chunk = write_sparse_data_chunk,
568 .write_fd_chunk = write_sparse_fd_chunk,
441 .write_fill_chunk = write_sparse_fill_chunk, 569 .write_fill_chunk = write_sparse_fill_chunk,
442 .write_skip_chunk = write_sparse_skip_chunk, 570 .write_skip_chunk = write_sparse_skip_chunk,
443 .write_end_chunk = write_sparse_end_chunk, 571 .write_end_chunk = write_sparse_end_chunk,
444}; 572};
445 573
446static int write_normal_data_chunk(struct output_file* out, unsigned int len, void* data) { 574static int write_normal_data_chunk_variant(struct output_file* out, unsigned int len,
575 handle_data data, bool isFd) {
447 int ret; 576 int ret;
448 unsigned int rnd_up_len = ALIGN(len, out->block_size); 577 unsigned int rnd_up_len = ALIGN(len, out->block_size);
449 578
450 ret = out->ops->write(out, data, len); 579 if (isFd) {
580 ret = out->ops->write_fd(out, data.fd, len);
581 } else {
582 ret = out->ops->write(out, data.data_ptr, len);
583 }
451 if (ret < 0) { 584 if (ret < 0) {
452 return ret; 585 return ret;
453 } 586 }
@@ -459,27 +592,18 @@ static int write_normal_data_chunk(struct output_file* out, unsigned int len, vo
459 return ret; 592 return ret;
460} 593}
461 594
462static int write_normal_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) { 595static int write_normal_data_chunk(struct output_file* out, unsigned int len, void* data_ptr) {
463 int ret; 596 handle_data data = {data_ptr};
464 unsigned int i; 597 return write_normal_data_chunk_variant(out, len, data, false /* isFd */);
465 unsigned int write_len; 598}
466
467 /* Initialize fill_buf with the fill_val */
468 for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
469 out->fill_buf[i] = fill_val;
470 }
471
472 while (len) {
473 write_len = min(len, out->block_size);
474 ret = out->ops->write(out, out->fill_buf, write_len);
475 if (ret < 0) {
476 return ret;
477 }
478 599
479 len -= write_len; 600static int write_normal_fd_chunk(struct output_file* out, unsigned int len, int fd) {
480 } 601 handle_data data = {.fd = fd};
602 return write_normal_data_chunk_variant(out, len, data, true /* isFd */);
603}
481 604
482 return 0; 605static int write_normal_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
606 return out->ops->write_fill(out, fill_val, len);
483} 607}
484 608
485static int write_normal_skip_chunk(struct output_file* out, int64_t len) { 609static int write_normal_skip_chunk(struct output_file* out, int64_t len) {
@@ -492,6 +616,7 @@ int write_normal_end_chunk(struct output_file* out) {
492 616
493static struct sparse_file_ops normal_file_ops = { 617static struct sparse_file_ops normal_file_ops = {
494 .write_data_chunk = write_normal_data_chunk, 618 .write_data_chunk = write_normal_data_chunk,
619 .write_fd_chunk = write_normal_fd_chunk,
495 .write_fill_chunk = write_normal_fill_chunk, 620 .write_fill_chunk = write_normal_fill_chunk,
496 .write_skip_chunk = write_normal_skip_chunk, 621 .write_skip_chunk = write_normal_skip_chunk,
497 .write_end_chunk = write_normal_end_chunk, 622 .write_end_chunk = write_normal_end_chunk,
@@ -589,12 +714,20 @@ static struct output_file* output_file_new_normal(void) {
589 return &outn->out; 714 return &outn->out;
590} 715}
591 716
592struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv, 717struct output_file* output_file_open_callback(int (*data_write)(void*, const void*, size_t),
593 unsigned int block_size, int64_t len, int gz __unused, 718 int (*fd_write)(void*, int, size_t),
594 int sparse, int chunks, int crc) { 719 int (*fill_write)(void*, uint32_t, size_t),
720 int (*skip_write)(void*, off64_t), void* priv,
721 unsigned int block_size, int64_t len, int sparse,
722 int chunks, int crc) {
595 int ret; 723 int ret;
596 struct output_file_callback* outc; 724 struct output_file_callback* outc;
597 725
726 if (!data_write || (crc && (fd_write || fill_write))) {
727 errno = EINVAL;
728 return nullptr;
729 }
730
598 outc = 731 outc =
599 reinterpret_cast<struct output_file_callback*>(calloc(1, sizeof(struct output_file_callback))); 732 reinterpret_cast<struct output_file_callback*>(calloc(1, sizeof(struct output_file_callback)));
600 if (!outc) { 733 if (!outc) {
@@ -604,7 +737,10 @@ struct output_file* output_file_open_callback(int (*write)(void*, const void*, s
604 737
605 outc->out.ops = &callback_file_ops; 738 outc->out.ops = &callback_file_ops;
606 outc->priv = priv; 739 outc->priv = priv;
607 outc->write = write; 740 outc->data_write = data_write;
741 outc->fd_write = fd_write;
742 outc->fill_write = fill_write;
743 outc->skip_write = skip_write;
608 744
609 ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc); 745 ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
610 if (ret < 0) { 746 if (ret < 0) {
@@ -651,52 +787,8 @@ int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_va
651} 787}
652 788
653int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset) { 789int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset) {
654 int ret; 790 lseek64(fd, offset, SEEK_SET);
655 int64_t aligned_offset; 791 return out->sparse_ops->write_fd_chunk(out, len, fd);
656 int aligned_diff;
657 uint64_t buffer_size;
658 char* ptr;
659
660 aligned_offset = offset & ~(4096 - 1);
661 aligned_diff = offset - aligned_offset;
662 buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
663
664#ifndef _WIN32
665 if (buffer_size > SIZE_MAX) return -E2BIG;
666 char* data =
667 reinterpret_cast<char*>(mmap64(nullptr, buffer_size, PROT_READ, MAP_SHARED, fd, aligned_offset));
668 if (data == MAP_FAILED) {
669 return -errno;
670 }
671 ptr = data + aligned_diff;
672#else
673 off64_t pos;
674 char* data = reinterpret_cast<char*>(malloc(len));
675 if (!data) {
676 return -errno;
677 }
678 pos = lseek64(fd, offset, SEEK_SET);
679 if (pos < 0) {
680 free(data);
681 return -errno;
682 }
683 ret = read_all(fd, data, len);
684 if (ret < 0) {
685 free(data);
686 return ret;
687 }
688 ptr = data;
689#endif
690
691 ret = out->sparse_ops->write_data_chunk(out, len, ptr);
692
693#ifndef _WIN32
694 munmap(data, buffer_size);
695#else
696 free(data);
697#endif
698
699 return ret;
700} 792}
701 793
702/* Write a contiguous region of data blocks from a file */ 794/* Write a contiguous region of data blocks from a file */
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index 278430b6f..114582e17 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -22,14 +22,18 @@ extern "C" {
22#endif 22#endif
23 23
24#include <sparse/sparse.h> 24#include <sparse/sparse.h>
25#include <sys/types.h>
25 26
26struct output_file; 27struct output_file;
27 28
28struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz, 29struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz,
29 int sparse, int chunks, int crc); 30 int sparse, int chunks, int crc);
30struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv, 31struct output_file* output_file_open_callback(int (*data_write)(void*, const void*, size_t),
31 unsigned int block_size, int64_t len, int gz, 32 int (*fd_write)(void*, int, size_t),
32 int sparse, int chunks, int crc); 33 int (*fill_write)(void*, uint32_t, size_t),
34 int (*skip_write)(void*, off64_t), void* priv,
35 unsigned int block_size, int64_t len, int sparse,
36 int chunks, int crc);
33int write_data_chunk(struct output_file* out, unsigned int len, void* data); 37int write_data_chunk(struct output_file* out, unsigned int len, void* data);
34int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val); 38int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val);
35int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset); 39int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset);
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
index cb288c555..f5ca9071d 100644
--- a/libsparse/sparse.cpp
+++ b/libsparse/sparse.cpp
@@ -160,7 +160,30 @@ int sparse_file_callback(struct sparse_file* s, bool sparse, bool crc,
160 struct output_file* out; 160 struct output_file* out;
161 161
162 chunks = sparse_count_chunks(s); 162 chunks = sparse_count_chunks(s);
163 out = output_file_open_callback(write, priv, s->block_size, s->len, false, sparse, chunks, crc); 163 out = output_file_open_callback(write, nullptr, nullptr, nullptr, priv, s->block_size, s->len,
164 sparse, chunks, crc);
165
166 if (!out) return -ENOMEM;
167
168 ret = write_all_blocks(s, out);
169
170 output_file_close(out);
171
172 return ret;
173}
174
175int sparse_file_callback_typed(struct sparse_file* s, bool sparse,
176 int (*data_write)(void* priv, const void* data, size_t len),
177 int (*fd_write)(void* priv, int fd, size_t len),
178 int (*fill_write)(void* priv, uint32_t fill_val, size_t len),
179 int (*skip_write)(void* priv, int64_t len), void* priv) {
180 int ret;
181 int chunks;
182 struct output_file* out;
183
184 chunks = sparse_count_chunks(s);
185 out = output_file_open_callback(data_write, fd_write, fill_write, skip_write, priv, s->block_size,
186 s->len, sparse, chunks, false);
164 187
165 if (!out) return -ENOMEM; 188 if (!out) return -ENOMEM;
166 189
@@ -198,8 +221,8 @@ int sparse_file_foreach_chunk(struct sparse_file* s, bool sparse, bool crc,
198 chk.write = write; 221 chk.write = write;
199 chk.block = chk.nr_blocks = 0; 222 chk.block = chk.nr_blocks = 0;
200 chunks = sparse_count_chunks(s); 223 chunks = sparse_count_chunks(s);
201 out = output_file_open_callback(foreach_chunk_write, &chk, s->block_size, s->len, false, sparse, 224 out = output_file_open_callback(foreach_chunk_write, nullptr, nullptr, nullptr, &chk,
202 chunks, crc); 225 s->block_size, s->len, sparse, chunks, crc);
203 226
204 if (!out) return -ENOMEM; 227 if (!out) return -ENOMEM;
205 228
@@ -227,8 +250,8 @@ int64_t sparse_file_len(struct sparse_file* s, bool sparse, bool crc) {
227 int64_t count = 0; 250 int64_t count = 0;
228 struct output_file* out; 251 struct output_file* out;
229 252
230 out = output_file_open_callback(out_counter_write, &count, s->block_size, s->len, false, sparse, 253 out = output_file_open_callback(out_counter_write, nullptr, nullptr, nullptr, &count,
231 chunks, crc); 254 s->block_size, s->len, sparse, chunks, crc);
232 if (!out) { 255 if (!out) {
233 return -1; 256 return -1;
234 } 257 }
@@ -267,8 +290,8 @@ static struct backed_block* move_chunks_up_to_len(struct sparse_file* from, stru
267 len -= overhead; 290 len -= overhead;
268 291
269 start = backed_block_iter_new(from->backed_block_list); 292 start = backed_block_iter_new(from->backed_block_list);
270 out_counter = output_file_open_callback(out_counter_write, &count, to->block_size, to->len, false, 293 out_counter = output_file_open_callback(out_counter_write, nullptr, nullptr, nullptr, &count,
271 true, 0, false); 294 to->block_size, to->len, true, 0, false);
272 if (!out_counter) { 295 if (!out_counter) {
273 return nullptr; 296 return nullptr;
274 } 297 }