]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - lib/ext2fs/sparse_io.c
AOSP: libext2fs: Track the libsparse API change.
[thirdparty/e2fsprogs.git] / lib / ext2fs / sparse_io.c
CommitLineData
105cdfe5
AS
1#include "config.h"
2#include <sys/types.h>
3#include <sys/stat.h>
4#include <fcntl.h>
5#include <unistd.h>
6#include <stdint.h>
7#include "ext2_fs.h"
8#include "ext2fs.h"
9
2d545e37
JQ
10#ifndef O_BINARY
11#define O_BINARY 0
12#endif
13
105cdfe5
AS
14#if !defined(ENABLE_LIBSPARSE)
15static errcode_t sparse_open(const char *name EXT2FS_ATTR((unused)),
16 int flags EXT2FS_ATTR((unused)),
17 io_channel *channel EXT2FS_ATTR((unused)))
18{
19 return EXT2_ET_UNIMPLEMENTED;
20}
21static errcode_t sparse_close(io_channel channel EXT2FS_ATTR((unused)))
22{
23 return EXT2_ET_UNIMPLEMENTED;
24}
25static struct struct_io_manager struct_sparse_manager = {
26 .magic = EXT2_ET_MAGIC_IO_MANAGER,
27 .name = "Android sparse I/O Manager",
28 .open = sparse_open,
29 .close = sparse_close,
30};
31static struct struct_io_manager struct_sparsefd_manager = {
32 .magic = EXT2_ET_MAGIC_IO_MANAGER,
33 .name = "Android sparse fd I/O Manager",
34 .open = sparse_open,
35 .close = sparse_close,
36};
37#else
38#include <sparse/sparse.h>
39
40struct sparse_map {
41 int fd;
42 char **blocks;
43 int block_size;
44 uint64_t blocks_count;
45 char *file;
46 struct sparse_file *sparse_file;
47 io_channel channel;
48};
49
50struct sparse_io_params {
51 int fd;
52 char *file;
53 uint64_t blocks_count;
54 unsigned int block_size;
55};
56
57static errcode_t sparse_write_blk(io_channel channel, unsigned long block,
58 int count, const void *buf);
59
60static void free_sparse_blocks(struct sparse_map *sm)
61{
62 uint64_t i;
63
64 for (i = 0; i < sm->blocks_count; ++i)
65 free(sm->blocks[i]);
66 free(sm->blocks);
67 sm->blocks = NULL;
68}
69
7c40d7bc 70static int sparse_import_segment(void *priv, const void *data, size_t len,
105cdfe5
AS
71 unsigned int block, unsigned int nr_blocks)
72{
73 struct sparse_map *sm = priv;
74
75 /* Ignore chunk headers, only write the data */
76 if (!nr_blocks || len % sm->block_size)
77 return 0;
78
79 return sparse_write_blk(sm->channel, block, nr_blocks, data);
80}
81
82static errcode_t io_manager_import_sparse(struct sparse_io_params *params,
83 struct sparse_map *sm, io_channel io)
84{
85 int fd;
86 errcode_t retval;
87 struct sparse_file *sparse_file;
88
89 if (params->fd < 0) {
90 fd = open(params->file, O_RDONLY);
91 if (fd < 0) {
92 retval = -1;
93 goto err_open;
94 }
95 } else
96 fd = params->fd;
97 sparse_file = sparse_file_import(fd, false, false);
98 if (!sparse_file) {
99 retval = -1;
100 goto err_sparse;
101 }
102
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*));
107 if (!sm->blocks) {
108 retval = -1;
109 goto err_alloc;
110 }
111 io->block_size = sm->block_size;
112
113 retval = sparse_file_foreach_chunk(sparse_file, true, false,
114 sparse_import_segment, sm);
115
116 if (retval)
117 free_sparse_blocks(sm);
118err_alloc:
119 sparse_file_destroy(sparse_file);
120err_sparse:
121 close(fd);
122err_open:
123 return retval;
124}
125
126static errcode_t io_manager_configure(struct sparse_io_params *params,
127 int flags, io_channel io)
128{
129 errcode_t retval;
130 uint64_t img_size;
131 struct sparse_map *sm = calloc(1, sizeof(*sm));
132 if (!sm)
133 return EXT2_ET_NO_MEMORY;
134
135 sm->file = params->file;
136 sm->channel = io;
137 io->private_data = sm;
138 retval = io_manager_import_sparse(params, sm, io);
139 if (retval) {
140 if (!params->block_size || !params->blocks_count) {
141 retval = -EINVAL;
142 goto err_params;
143 }
144 sm->block_size = params->block_size;
145 sm->blocks_count = params->blocks_count;
146 sm->blocks = calloc(params->blocks_count, sizeof(void*));
147 if (!sm->blocks) {
148 retval = EXT2_ET_NO_MEMORY;
149 goto err_alloc;
150 }
151 }
152 io->block_size = sm->block_size;
153 img_size = (uint64_t)sm->block_size * sm->blocks_count;
154
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;
159 goto err_alloc;
160 }
161 if (params->fd < 0) {
2d545e37 162 sm->fd = open(params->file, O_CREAT | O_RDWR | O_TRUNC | O_BINARY,
105cdfe5
AS
163 0644);
164 if (sm->fd < 0) {
165 retval = errno;
166 goto err_open;
167 }
168 } else
169 sm->fd = params->fd;
170 } else {
171 sm->fd = -1;
172 sm->sparse_file = NULL;
173 }
174 return 0;
175
176err_open:
177 sparse_file_destroy(sm->sparse_file);
178err_alloc:
179 free_sparse_blocks(sm);
180err_params:
181 free(sm);
182 return retval;
183}
184
185static errcode_t sparse_open_channel(struct sparse_io_params *sparse_params,
186 int flags, io_channel *channel)
187{
188 io_channel io;
189
190 io = calloc(1, sizeof(struct struct_io_channel));
191 io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
192 io->block_size = 0;
193 io->refcount = 1;
194 *channel = io;
195 return io_manager_configure(sparse_params, flags, io);
196}
197
198static errcode_t read_sparse_argv(const char *name, bool is_fd,
199 struct sparse_io_params *sparse_params)
200{
201 int ret;
202 sparse_params->fd = -1;
105cdfe5
AS
203 sparse_params->block_size = 0;
204 sparse_params->blocks_count = 0;
205
a5d3c5ba
JQ
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;
210 }
211
105cdfe5 212 if (is_fd) {
2d545e37 213 ret = sscanf(name, "(%d):%llu:%u", &sparse_params->fd,
105cdfe5
AS
214 (unsigned long long *)&sparse_params->blocks_count,
215 &sparse_params->block_size);
216 } else {
2d545e37 217 ret = sscanf(name, "(%[^)])%*[:]%llu%*[:]%u", sparse_params->file,
105cdfe5
AS
218 (unsigned long long *)&sparse_params->blocks_count,
219 &sparse_params->block_size);
220 }
221
222 if (ret < 1) {
223 free(sparse_params->file);
224 return -EINVAL;
225 }
226 return 0;
227}
228
229static errcode_t sparse_open(const char *name, int flags, io_channel *channel)
230{
231 errcode_t retval;
232 struct sparse_io_params sparse_params;
233
234 retval = read_sparse_argv(name, false, &sparse_params);
235 if (retval)
236 return EXT2_ET_BAD_DEVICE_NAME;
237
238 retval = sparse_open_channel(&sparse_params, flags, channel);
239 if (retval)
240 return retval;
241 (*channel)->manager = sparse_io_manager;
242
243 return retval;
244}
245
246static errcode_t sparsefd_open(const char *name, int flags, io_channel *channel)
247{
248 errcode_t retval;
249 struct sparse_io_params sparse_params;
250
251 retval = read_sparse_argv(name, true, &sparse_params);
252 if (retval)
253 return EXT2_ET_BAD_DEVICE_NAME;
254
255 retval = sparse_open_channel(&sparse_params, flags, channel);
256 if (retval)
257 return retval;
258 (*channel)->manager = sparsefd_io_manager;
259
260 return retval;
261}
262
b27343db
JQ
263static errcode_t sparse_merge_blocks(struct sparse_map *sm, uint64_t start,
264 uint64_t num)
265{
266 char *buf;
267 uint64_t i;
268 unsigned int block_size = sm->block_size;
269 errcode_t retval = 0;
270
271 buf = calloc(num, block_size);
272 if (!buf) {
8f8f7a71
JQ
273 fprintf(stderr, "failed to alloc %llu\n",
274 (unsigned long long)num * block_size);
b27343db
JQ
275 return EXT2_ET_NO_MEMORY;
276 }
277
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;
282 }
283
284 /* free_sparse_blocks will release this buf. */
285 sm->blocks[start] = buf;
286
287 retval = sparse_file_add_data(sm->sparse_file, sm->blocks[start],
288 block_size * num, start);
289
290 return retval;
291}
292
105cdfe5
AS
293static errcode_t sparse_close_channel(io_channel channel)
294{
295 uint64_t i;
296 errcode_t retval = 0;
297 struct sparse_map *sm = channel->private_data;
298
299 if (sm->sparse_file) {
b27343db 300 int64_t chunk_start = (sm->blocks[0] == NULL) ? -1 : 0;
105cdfe5 301 for (i = 0; i < sm->blocks_count; ++i) {
b27343db
JQ
302 if (!sm->blocks[i] && chunk_start != -1) {
303 retval = sparse_merge_blocks(sm, chunk_start, i - chunk_start);
304 chunk_start = -1;
305 } else if (sm->blocks[i] && chunk_start == -1) {
306 chunk_start = i;
307 }
308 if (retval)
309 goto ret;
310 }
311 if (chunk_start != -1) {
312 retval = sparse_merge_blocks(sm, chunk_start,
313 sm->blocks_count - chunk_start);
105cdfe5
AS
314 if (retval)
315 goto ret;
316 }
317 retval = sparse_file_write(sm->sparse_file, sm->fd,
318 /*gzip*/0, /*sparse*/1, /*crc*/0);
319 }
320
321ret:
322 if (sm->sparse_file)
323 sparse_file_destroy(sm->sparse_file);
324 free_sparse_blocks(sm);
325 free(sm->file);
326 free(sm);
327 free(channel);
328 return retval;
329}
330
331static errcode_t sparse_close(io_channel channel)
332{
333 errcode_t retval;
334 struct sparse_map *sm = channel->private_data;
335 int fd = sm->fd;
336
337 retval = sparse_close_channel(channel);
338 if (fd >= 0)
339 close(fd);
340
341 return retval;
342}
343
344static errcode_t sparse_set_blksize(io_channel channel, int blksize)
345{
346 channel->block_size = blksize;
347 return 0;
348}
349
350static blk64_t block_to_sparse_block(blk64_t block, blk64_t *offset,
351 io_channel channel, struct sparse_map *sm)
352{
353 int ratio;
354 blk64_t ret = block;
355
356 ratio = sm->block_size / channel->block_size;
357 ret /= ratio;
358 *offset = (block % ratio) * channel->block_size;
359
360 return ret;
361}
362
363static errcode_t check_block_size(io_channel channel, struct sparse_map *sm)
364{
365 if (sm->block_size >= channel->block_size)
366 return 0;
367 return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
368}
369
370static errcode_t sparse_read_blk64(io_channel channel, blk64_t block,
371 int count, void *buf)
372{
373 int i;
374 char *out = buf;
375 blk64_t offset = 0, cur_block;
376 struct sparse_map *sm = channel->private_data;
377
378 if (check_block_size(channel, sm))
379 return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
380
381 if (count < 0) { //partial read
382 count = -count;
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);
386 else
387 memset(out, 0, count);
388 } else {
389 for (i = 0; i < count; ++i) {
390 cur_block = block_to_sparse_block(block + i, &offset,
391 channel, sm);
392 if (sm->blocks[cur_block])
393 memcpy(out + (i * channel->block_size),
394 sm->blocks[cur_block] + offset,
395 channel->block_size);
396 else if (sm->blocks)
397 memset(out + (i * channel->block_size), 0,
398 channel->block_size);
399 }
400 }
401 return 0;
402}
403
404static errcode_t sparse_read_blk(io_channel channel, unsigned long block,
405 int count, void *buf)
406{
407 return sparse_read_blk64(channel, block, count, buf);
408}
409
410static errcode_t sparse_write_blk64(io_channel channel, blk64_t block,
411 int count, const void *buf)
412{
413 int i;
414 blk64_t offset = 0, cur_block;
415 const char *in = buf;
416 struct sparse_map *sm = channel->private_data;
417
418 if (check_block_size(channel, sm))
419 return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
420
421 if (count < 0) { //partial write
422 count = -count;
423 cur_block = block_to_sparse_block(block, &offset, channel,
424 sm);
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;
429 }
430 memcpy(sm->blocks[cur_block] + offset, in, count);
431 } else {
432 for (i = 0; i < count; ++i) {
433 if (block + i >= sm->blocks_count)
434 return 0;
435 cur_block = block_to_sparse_block(block + i, &offset,
436 channel, sm);
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;
442 }
443 memcpy(sm->blocks[cur_block] + offset,
444 in + (i * channel->block_size),
445 channel->block_size);
446 }
447 }
448 return 0;
449}
450
451static errcode_t sparse_write_blk(io_channel channel, unsigned long block,
452 int count, const void *buf)
453{
454 return sparse_write_blk64(channel, block, count, buf);
455}
456
457static errcode_t sparse_discard(io_channel channel __attribute__((unused)),
458 blk64_t blk, unsigned long long count)
459{
460 blk64_t cur_block, offset;
461 struct sparse_map *sm = channel->private_data;
462
463 if (check_block_size(channel, sm))
464 return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
465
466 for (unsigned long long i = 0; i < count; ++i) {
467 if (blk + i >= sm->blocks_count)
468 return 0;
469 cur_block = block_to_sparse_block(blk + i, &offset, channel,
470 sm);
471 if (!sm->blocks[cur_block])
472 continue;
473 free(sm->blocks[cur_block]);
474 sm->blocks[cur_block] = NULL;
475 }
476 return 0;
477}
478
479static errcode_t sparse_zeroout(io_channel channel, blk64_t blk,
480 unsigned long long count)
481{
482 return sparse_discard(channel, blk, count);
483}
484
485static errcode_t sparse_flush(io_channel channel __attribute__((unused)))
486{
487 return 0;
488}
489
490static errcode_t sparse_set_option(io_channel channel __attribute__((unused)),
491 const char *option __attribute__((unused)),
492 const char *arg __attribute__((unused)))
493{
494 return 0;
495}
496
497static errcode_t sparse_cache_readahead(
498 io_channel channel __attribute__((unused)),
499 blk64_t blk __attribute__((unused)),
500 unsigned long long count __attribute__((unused)))
501{
502 return 0;
503}
504
505static struct struct_io_manager struct_sparse_manager = {
506 .magic = EXT2_ET_MAGIC_IO_MANAGER,
507 .name = "Android sparse I/O Manager",
508 .open = sparse_open,
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,
514 .write_byte = NULL,
515 .set_option = sparse_set_option,
516 .get_stats = NULL,
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,
522};
523
524static 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,
533 .write_byte = NULL,
534 .set_option = sparse_set_option,
535 .get_stats = NULL,
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,
541};
542
543#endif
544
545io_manager sparse_io_manager = &struct_sparse_manager;
546io_manager sparsefd_io_manager = &struct_sparsefd_manager;