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