14 #if !defined(ENABLE_LIBSPARSE)
15 static errcode_t
sparse_open(const char *name
EXT2FS_ATTR((unused
)),
16 int flags
EXT2FS_ATTR((unused
)),
17 io_channel
*channel
EXT2FS_ATTR((unused
)))
19 return EXT2_ET_UNIMPLEMENTED
;
21 static errcode_t
sparse_close(io_channel channel
EXT2FS_ATTR((unused
)))
23 return EXT2_ET_UNIMPLEMENTED
;
25 static struct struct_io_manager struct_sparse_manager
= {
26 .magic
= EXT2_ET_MAGIC_IO_MANAGER
,
27 .name
= "Android sparse I/O Manager",
29 .close
= sparse_close
,
31 static struct struct_io_manager struct_sparsefd_manager
= {
32 .magic
= EXT2_ET_MAGIC_IO_MANAGER
,
33 .name
= "Android sparse fd I/O Manager",
35 .close
= sparse_close
,
38 #include <sparse/sparse.h>
44 uint64_t blocks_count
;
46 struct sparse_file
*sparse_file
;
50 struct sparse_io_params
{
53 uint64_t blocks_count
;
54 unsigned int block_size
;
57 static errcode_t
sparse_write_blk(io_channel channel
, unsigned long block
,
58 int count
, const void *buf
);
60 static void free_sparse_blocks(struct sparse_map
*sm
)
64 for (i
= 0; i
< sm
->blocks_count
; ++i
)
70 static int sparse_import_segment(void *priv
, const void *data
, int len
,
71 unsigned int block
, unsigned int nr_blocks
)
73 struct sparse_map
*sm
= priv
;
75 /* Ignore chunk headers, only write the data */
76 if (!nr_blocks
|| len
% sm
->block_size
)
79 return sparse_write_blk(sm
->channel
, block
, nr_blocks
, data
);
82 static errcode_t
io_manager_import_sparse(struct sparse_io_params
*params
,
83 struct sparse_map
*sm
, io_channel io
)
87 struct sparse_file
*sparse_file
;
90 fd
= open(params
->file
, O_RDONLY
);
97 sparse_file
= sparse_file_import(fd
, false, false);
103 sm
->block_size
= sparse_file_block_size(sparse_file
);
104 sm
->blocks_count
= (sparse_file_len(sparse_file
, 0, 0) - 1)
105 / sm
->block_size
+ 1;
106 sm
->blocks
= calloc(sm
->blocks_count
, sizeof(char*));
111 io
->block_size
= sm
->block_size
;
113 retval
= sparse_file_foreach_chunk(sparse_file
, true, false,
114 sparse_import_segment
, sm
);
117 free_sparse_blocks(sm
);
119 sparse_file_destroy(sparse_file
);
126 static errcode_t
io_manager_configure(struct sparse_io_params
*params
,
127 int flags
, io_channel io
)
131 struct sparse_map
*sm
= calloc(1, sizeof(*sm
));
133 return EXT2_ET_NO_MEMORY
;
135 sm
->file
= params
->file
;
137 io
->private_data
= sm
;
138 retval
= io_manager_import_sparse(params
, sm
, io
);
140 if (!params
->block_size
|| !params
->blocks_count
) {
144 sm
->block_size
= params
->block_size
;
145 sm
->blocks_count
= params
->blocks_count
;
146 sm
->blocks
= calloc(params
->blocks_count
, sizeof(void*));
148 retval
= EXT2_ET_NO_MEMORY
;
152 io
->block_size
= sm
->block_size
;
153 img_size
= (uint64_t)sm
->block_size
* sm
->blocks_count
;
155 if (flags
& IO_FLAG_RW
) {
156 sm
->sparse_file
= sparse_file_new(sm
->block_size
, img_size
);
157 if (!sm
->sparse_file
) {
158 retval
= EXT2_ET_NO_MEMORY
;
161 if (params
->fd
< 0) {
162 sm
->fd
= open(params
->file
, O_CREAT
| O_RDWR
| O_TRUNC
| O_BINARY
,
172 sm
->sparse_file
= NULL
;
177 sparse_file_destroy(sm
->sparse_file
);
179 free_sparse_blocks(sm
);
185 static errcode_t
sparse_open_channel(struct sparse_io_params
*sparse_params
,
186 int flags
, io_channel
*channel
)
190 io
= calloc(1, sizeof(struct struct_io_channel
));
191 io
->magic
= EXT2_ET_MAGIC_IO_CHANNEL
;
195 return io_manager_configure(sparse_params
, flags
, io
);
198 static errcode_t
read_sparse_argv(const char *name
, bool is_fd
,
199 struct sparse_io_params
*sparse_params
)
202 sparse_params
->fd
= -1;
203 sparse_params
->block_size
= 0;
204 sparse_params
->blocks_count
= 0;
206 sparse_params
->file
= malloc(strlen(name
) + 1);
207 if (!sparse_params
->file
) {
208 fprintf(stderr
, "failed to alloc %zu\n", strlen(name
) + 1);
209 return EXT2_ET_NO_MEMORY
;
213 ret
= sscanf(name
, "(%d):%llu:%u", &sparse_params
->fd
,
214 (unsigned long long *)&sparse_params
->blocks_count
,
215 &sparse_params
->block_size
);
217 ret
= sscanf(name
, "(%[^)])%*[:]%llu%*[:]%u", sparse_params
->file
,
218 (unsigned long long *)&sparse_params
->blocks_count
,
219 &sparse_params
->block_size
);
223 free(sparse_params
->file
);
229 static errcode_t
sparse_open(const char *name
, int flags
, io_channel
*channel
)
232 struct sparse_io_params sparse_params
;
234 retval
= read_sparse_argv(name
, false, &sparse_params
);
236 return EXT2_ET_BAD_DEVICE_NAME
;
238 retval
= sparse_open_channel(&sparse_params
, flags
, channel
);
241 (*channel
)->manager
= sparse_io_manager
;
246 static errcode_t
sparsefd_open(const char *name
, int flags
, io_channel
*channel
)
249 struct sparse_io_params sparse_params
;
251 retval
= read_sparse_argv(name
, true, &sparse_params
);
253 return EXT2_ET_BAD_DEVICE_NAME
;
255 retval
= sparse_open_channel(&sparse_params
, flags
, channel
);
258 (*channel
)->manager
= sparsefd_io_manager
;
263 static errcode_t
sparse_merge_blocks(struct sparse_map
*sm
, uint64_t start
,
268 unsigned int block_size
= sm
->block_size
;
269 errcode_t retval
= 0;
271 buf
= calloc(num
, block_size
);
273 fprintf(stderr
, "failed to alloc %llu\n",
274 (unsigned long long)num
* block_size
);
275 return EXT2_ET_NO_MEMORY
;
278 for (i
= 0; i
< num
; i
++) {
279 memcpy(buf
+ i
* block_size
, sm
->blocks
[start
+ i
] , block_size
);
280 free(sm
->blocks
[start
+ i
]);
281 sm
->blocks
[start
+ i
] = NULL
;
284 /* free_sparse_blocks will release this buf. */
285 sm
->blocks
[start
] = buf
;
287 retval
= sparse_file_add_data(sm
->sparse_file
, sm
->blocks
[start
],
288 block_size
* num
, start
);
293 static errcode_t
sparse_close_channel(io_channel channel
)
296 errcode_t retval
= 0;
297 struct sparse_map
*sm
= channel
->private_data
;
299 if (sm
->sparse_file
) {
300 int64_t chunk_start
= (sm
->blocks
[0] == NULL
) ? -1 : 0;
301 for (i
= 0; i
< sm
->blocks_count
; ++i
) {
302 if (!sm
->blocks
[i
] && chunk_start
!= -1) {
303 retval
= sparse_merge_blocks(sm
, chunk_start
, i
- chunk_start
);
305 } else if (sm
->blocks
[i
] && chunk_start
== -1) {
311 if (chunk_start
!= -1) {
312 retval
= sparse_merge_blocks(sm
, chunk_start
,
313 sm
->blocks_count
- chunk_start
);
317 retval
= sparse_file_write(sm
->sparse_file
, sm
->fd
,
318 /*gzip*/0, /*sparse*/1, /*crc*/0);
323 sparse_file_destroy(sm
->sparse_file
);
324 free_sparse_blocks(sm
);
331 static errcode_t
sparse_close(io_channel channel
)
334 struct sparse_map
*sm
= channel
->private_data
;
337 retval
= sparse_close_channel(channel
);
344 static errcode_t
sparse_set_blksize(io_channel channel
, int blksize
)
346 channel
->block_size
= blksize
;
350 static blk64_t
block_to_sparse_block(blk64_t block
, blk64_t
*offset
,
351 io_channel channel
, struct sparse_map
*sm
)
356 ratio
= sm
->block_size
/ channel
->block_size
;
358 *offset
= (block
% ratio
) * channel
->block_size
;
363 static errcode_t
check_block_size(io_channel channel
, struct sparse_map
*sm
)
365 if (sm
->block_size
>= channel
->block_size
)
367 return EXT2_ET_UNEXPECTED_BLOCK_SIZE
;
370 static errcode_t
sparse_read_blk64(io_channel channel
, blk64_t block
,
371 int count
, void *buf
)
375 blk64_t offset
= 0, cur_block
;
376 struct sparse_map
*sm
= channel
->private_data
;
378 if (check_block_size(channel
, sm
))
379 return EXT2_ET_UNEXPECTED_BLOCK_SIZE
;
381 if (count
< 0) { //partial read
383 cur_block
= block_to_sparse_block(block
, &offset
, channel
, sm
);
384 if (sm
->blocks
[cur_block
])
385 memcpy(out
, (sm
->blocks
[cur_block
]) + offset
, count
);
387 memset(out
, 0, count
);
389 for (i
= 0; i
< count
; ++i
) {
390 cur_block
= block_to_sparse_block(block
+ i
, &offset
,
392 if (sm
->blocks
[cur_block
])
393 memcpy(out
+ (i
* channel
->block_size
),
394 sm
->blocks
[cur_block
] + offset
,
395 channel
->block_size
);
397 memset(out
+ (i
* channel
->block_size
), 0,
398 channel
->block_size
);
404 static errcode_t
sparse_read_blk(io_channel channel
, unsigned long block
,
405 int count
, void *buf
)
407 return sparse_read_blk64(channel
, block
, count
, buf
);
410 static errcode_t
sparse_write_blk64(io_channel channel
, blk64_t block
,
411 int count
, const void *buf
)
414 blk64_t offset
= 0, cur_block
;
415 const char *in
= buf
;
416 struct sparse_map
*sm
= channel
->private_data
;
418 if (check_block_size(channel
, sm
))
419 return EXT2_ET_UNEXPECTED_BLOCK_SIZE
;
421 if (count
< 0) { //partial write
423 cur_block
= block_to_sparse_block(block
, &offset
, channel
,
425 if (!sm
->blocks
[cur_block
]) {
426 sm
->blocks
[cur_block
] = calloc(1, sm
->block_size
);
427 if (!sm
->blocks
[cur_block
])
428 return EXT2_ET_NO_MEMORY
;
430 memcpy(sm
->blocks
[cur_block
] + offset
, in
, count
);
432 for (i
= 0; i
< count
; ++i
) {
433 if (block
+ i
>= sm
->blocks_count
)
435 cur_block
= block_to_sparse_block(block
+ i
, &offset
,
437 if (!sm
->blocks
[cur_block
]) {
438 sm
->blocks
[cur_block
] =
439 calloc(1, sm
->block_size
);
440 if (!sm
->blocks
[cur_block
])
441 return EXT2_ET_NO_MEMORY
;
443 memcpy(sm
->blocks
[cur_block
] + offset
,
444 in
+ (i
* channel
->block_size
),
445 channel
->block_size
);
451 static errcode_t
sparse_write_blk(io_channel channel
, unsigned long block
,
452 int count
, const void *buf
)
454 return sparse_write_blk64(channel
, block
, count
, buf
);
457 static errcode_t
sparse_discard(io_channel channel
__attribute__((unused
)),
458 blk64_t blk
, unsigned long long count
)
460 blk64_t cur_block
, offset
;
461 struct sparse_map
*sm
= channel
->private_data
;
463 if (check_block_size(channel
, sm
))
464 return EXT2_ET_UNEXPECTED_BLOCK_SIZE
;
466 for (unsigned long long i
= 0; i
< count
; ++i
) {
467 if (blk
+ i
>= sm
->blocks_count
)
469 cur_block
= block_to_sparse_block(blk
+ i
, &offset
, channel
,
471 if (!sm
->blocks
[cur_block
])
473 free(sm
->blocks
[cur_block
]);
474 sm
->blocks
[cur_block
] = NULL
;
479 static errcode_t
sparse_zeroout(io_channel channel
, blk64_t blk
,
480 unsigned long long count
)
482 return sparse_discard(channel
, blk
, count
);
485 static errcode_t
sparse_flush(io_channel channel
__attribute__((unused
)))
490 static errcode_t
sparse_set_option(io_channel channel
__attribute__((unused
)),
491 const char *option
__attribute__((unused
)),
492 const char *arg
__attribute__((unused
)))
497 static errcode_t
sparse_cache_readahead(
498 io_channel channel
__attribute__((unused
)),
499 blk64_t blk
__attribute__((unused
)),
500 unsigned long long count
__attribute__((unused
)))
505 static struct struct_io_manager struct_sparse_manager
= {
506 .magic
= EXT2_ET_MAGIC_IO_MANAGER
,
507 .name
= "Android sparse I/O Manager",
509 .close
= sparse_close
,
510 .set_blksize
= sparse_set_blksize
,
511 .read_blk
= sparse_read_blk
,
512 .write_blk
= sparse_write_blk
,
513 .flush
= sparse_flush
,
515 .set_option
= sparse_set_option
,
517 .read_blk64
= sparse_read_blk64
,
518 .write_blk64
= sparse_write_blk64
,
519 .discard
= sparse_discard
,
520 .cache_readahead
= sparse_cache_readahead
,
521 .zeroout
= sparse_zeroout
,
524 static struct struct_io_manager struct_sparsefd_manager
= {
525 .magic
= EXT2_ET_MAGIC_IO_MANAGER
,
526 .name
= "Android sparse fd I/O Manager",
527 .open
= sparsefd_open
,
528 .close
= sparse_close
,
529 .set_blksize
= sparse_set_blksize
,
530 .read_blk
= sparse_read_blk
,
531 .write_blk
= sparse_write_blk
,
532 .flush
= sparse_flush
,
534 .set_option
= sparse_set_option
,
536 .read_blk64
= sparse_read_blk64
,
537 .write_blk64
= sparse_write_blk64
,
538 .discard
= sparse_discard
,
539 .cache_readahead
= sparse_cache_readahead
,
540 .zeroout
= sparse_zeroout
,
545 io_manager sparse_io_manager
= &struct_sparse_manager
;
546 io_manager sparsefd_io_manager
= &struct_sparsefd_manager
;