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 #ifndef O_BINARY
11 #define O_BINARY 0
12 #endif
13
14 #if !defined(ENABLE_LIBSPARSE)
sparse_open(const char * name EXT2FS_ATTR ((unused)),int flags EXT2FS_ATTR ((unused)),io_channel * channel EXT2FS_ATTR ((unused)))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)))
18 {
19 return EXT2_ET_UNIMPLEMENTED;
20 }
sparse_close(io_channel channel EXT2FS_ATTR ((unused)))21 static errcode_t sparse_close(io_channel channel EXT2FS_ATTR((unused)))
22 {
23 return EXT2_ET_UNIMPLEMENTED;
24 }
25 static 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 };
31 static 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
40 struct 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
50 struct sparse_io_params {
51 int fd;
52 char *file;
53 uint64_t blocks_count;
54 unsigned int block_size;
55 };
56
57 static errcode_t sparse_write_blk(io_channel channel, unsigned long block,
58 int count, const void *buf);
59
free_sparse_blocks(struct sparse_map * sm)60 static 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
sparse_import_segment(void * priv,const void * data,size_t len,unsigned int block,unsigned int nr_blocks)70 static int sparse_import_segment(void *priv, const void *data, size_t len,
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
io_manager_import_sparse(struct sparse_io_params * params,struct sparse_map * sm,io_channel io)82 static 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);
118 err_alloc:
119 sparse_file_destroy(sparse_file);
120 err_sparse:
121 close(fd);
122 err_open:
123 return retval;
124 }
125
io_manager_configure(struct sparse_io_params * params,int flags,io_channel io)126 static 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) {
162 sm->fd = open(params->file, O_CREAT | O_RDWR | O_TRUNC | O_BINARY,
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
176 err_open:
177 sparse_file_destroy(sm->sparse_file);
178 err_alloc:
179 free_sparse_blocks(sm);
180 err_params:
181 free(sm);
182 return retval;
183 }
184
sparse_open_channel(struct sparse_io_params * sparse_params,int flags,io_channel * channel)185 static errcode_t sparse_open_channel(struct sparse_io_params *sparse_params,
186 int flags, io_channel *channel)
187 {
188 errcode_t retval;
189 io_channel io;
190
191 io = calloc(1, sizeof(struct struct_io_channel));
192 io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
193 io->block_size = 0;
194 io->refcount = 1;
195
196 retval = io_manager_configure(sparse_params, flags, io);
197 if (retval) {
198 free(io);
199 return retval;
200 }
201
202 *channel = io;
203 return 0;
204 }
205
read_sparse_argv(const char * name,bool is_fd,struct sparse_io_params * sparse_params)206 static errcode_t read_sparse_argv(const char *name, bool is_fd,
207 struct sparse_io_params *sparse_params)
208 {
209 int ret;
210 sparse_params->fd = -1;
211 sparse_params->block_size = 0;
212 sparse_params->blocks_count = 0;
213
214 sparse_params->file = malloc(strlen(name) + 1);
215 if (!sparse_params->file) {
216 fprintf(stderr, "failed to alloc %zu\n", strlen(name) + 1);
217 return EXT2_ET_NO_MEMORY;
218 }
219
220 if (is_fd) {
221 ret = sscanf(name, "(%d):%llu:%u", &sparse_params->fd,
222 (unsigned long long *)&sparse_params->blocks_count,
223 &sparse_params->block_size);
224 } else {
225 ret = sscanf(name, "(%[^)])%*[:]%llu%*[:]%u", sparse_params->file,
226 (unsigned long long *)&sparse_params->blocks_count,
227 &sparse_params->block_size);
228 }
229
230 if (ret < 1) {
231 free(sparse_params->file);
232 return -EINVAL;
233 }
234 return 0;
235 }
236
sparse_open(const char * name,int flags,io_channel * channel)237 static errcode_t sparse_open(const char *name, int flags, io_channel *channel)
238 {
239 errcode_t retval;
240 struct sparse_io_params sparse_params;
241
242 retval = read_sparse_argv(name, false, &sparse_params);
243 if (retval)
244 return EXT2_ET_BAD_DEVICE_NAME;
245
246 retval = sparse_open_channel(&sparse_params, flags, channel);
247 if (retval)
248 return retval;
249 (*channel)->manager = sparse_io_manager;
250
251 return retval;
252 }
253
sparsefd_open(const char * name,int flags,io_channel * channel)254 static errcode_t sparsefd_open(const char *name, int flags, io_channel *channel)
255 {
256 errcode_t retval;
257 struct sparse_io_params sparse_params;
258
259 retval = read_sparse_argv(name, true, &sparse_params);
260 if (retval)
261 return EXT2_ET_BAD_DEVICE_NAME;
262
263 retval = sparse_open_channel(&sparse_params, flags, channel);
264 if (retval)
265 return retval;
266 (*channel)->manager = sparsefd_io_manager;
267
268 return retval;
269 }
270
sparse_merge_blocks(struct sparse_map * sm,uint64_t start,uint64_t num)271 static errcode_t sparse_merge_blocks(struct sparse_map *sm, uint64_t start,
272 uint64_t num)
273 {
274 char *buf;
275 uint64_t i;
276 unsigned int block_size = sm->block_size;
277 errcode_t retval = 0;
278
279 buf = calloc(num, block_size);
280 if (!buf) {
281 fprintf(stderr, "failed to alloc %llu\n",
282 (unsigned long long)num * block_size);
283 return EXT2_ET_NO_MEMORY;
284 }
285
286 for (i = 0; i < num; i++) {
287 memcpy(buf + i * block_size, sm->blocks[start + i] , block_size);
288 free(sm->blocks[start + i]);
289 sm->blocks[start + i] = NULL;
290 }
291
292 /* free_sparse_blocks will release this buf. */
293 sm->blocks[start] = buf;
294
295 retval = sparse_file_add_data(sm->sparse_file, sm->blocks[start],
296 block_size * num, start);
297
298 return retval;
299 }
300
sparse_close_channel(io_channel channel)301 static errcode_t sparse_close_channel(io_channel channel)
302 {
303 uint64_t i;
304 errcode_t retval = 0;
305 struct sparse_map *sm = channel->private_data;
306
307 if (sm->sparse_file) {
308 int64_t chunk_start = (sm->blocks[0] == NULL) ? -1 : 0;
309 for (i = 0; i < sm->blocks_count; ++i) {
310 if (!sm->blocks[i] && chunk_start != -1) {
311 retval = sparse_merge_blocks(sm, chunk_start, i - chunk_start);
312 chunk_start = -1;
313 } else if (sm->blocks[i] && chunk_start == -1) {
314 chunk_start = i;
315 }
316 if (retval)
317 goto ret;
318 }
319 if (chunk_start != -1) {
320 retval = sparse_merge_blocks(sm, chunk_start,
321 sm->blocks_count - chunk_start);
322 if (retval)
323 goto ret;
324 }
325 retval = sparse_file_write(sm->sparse_file, sm->fd,
326 /*gzip*/0, /*sparse*/1, /*crc*/0);
327 }
328
329 ret:
330 if (sm->sparse_file)
331 sparse_file_destroy(sm->sparse_file);
332 free_sparse_blocks(sm);
333 free(sm->file);
334 free(sm);
335 free(channel);
336 return retval;
337 }
338
sparse_close(io_channel channel)339 static errcode_t sparse_close(io_channel channel)
340 {
341 errcode_t retval;
342 struct sparse_map *sm = channel->private_data;
343 int fd = sm->fd;
344
345 retval = sparse_close_channel(channel);
346 if (fd >= 0)
347 close(fd);
348
349 return retval;
350 }
351
sparse_set_blksize(io_channel channel,int blksize)352 static errcode_t sparse_set_blksize(io_channel channel, int blksize)
353 {
354 channel->block_size = blksize;
355 return 0;
356 }
357
block_to_sparse_block(blk64_t block,blk64_t * offset,io_channel channel,struct sparse_map * sm)358 static blk64_t block_to_sparse_block(blk64_t block, blk64_t *offset,
359 io_channel channel, struct sparse_map *sm)
360 {
361 int ratio;
362 blk64_t ret = block;
363
364 ratio = sm->block_size / channel->block_size;
365 ret /= ratio;
366 *offset = (block % ratio) * channel->block_size;
367
368 return ret;
369 }
370
check_block_size(io_channel channel,struct sparse_map * sm)371 static errcode_t check_block_size(io_channel channel, struct sparse_map *sm)
372 {
373 if (sm->block_size >= channel->block_size)
374 return 0;
375 return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
376 }
377
sparse_read_blk64(io_channel channel,blk64_t block,int count,void * buf)378 static errcode_t sparse_read_blk64(io_channel channel, blk64_t block,
379 int count, void *buf)
380 {
381 int i;
382 char *out = buf;
383 blk64_t offset = 0, cur_block;
384 struct sparse_map *sm = channel->private_data;
385
386 if (check_block_size(channel, sm))
387 return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
388
389 if (count < 0) { //partial read
390 count = -count;
391 cur_block = block_to_sparse_block(block, &offset, channel, sm);
392 if (sm->blocks[cur_block])
393 memcpy(out, (sm->blocks[cur_block]) + offset, count);
394 else
395 memset(out, 0, count);
396 } else {
397 for (i = 0; i < count; ++i) {
398 cur_block = block_to_sparse_block(block + i, &offset,
399 channel, sm);
400 if (sm->blocks[cur_block])
401 memcpy(out + (i * channel->block_size),
402 sm->blocks[cur_block] + offset,
403 channel->block_size);
404 else if (sm->blocks)
405 memset(out + (i * channel->block_size), 0,
406 channel->block_size);
407 }
408 }
409 return 0;
410 }
411
sparse_read_blk(io_channel channel,unsigned long block,int count,void * buf)412 static errcode_t sparse_read_blk(io_channel channel, unsigned long block,
413 int count, void *buf)
414 {
415 return sparse_read_blk64(channel, block, count, buf);
416 }
417
sparse_write_blk64(io_channel channel,blk64_t block,int count,const void * buf)418 static errcode_t sparse_write_blk64(io_channel channel, blk64_t block,
419 int count, const void *buf)
420 {
421 int i;
422 blk64_t offset = 0, cur_block;
423 const char *in = buf;
424 struct sparse_map *sm = channel->private_data;
425
426 if (check_block_size(channel, sm))
427 return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
428
429 if (count < 0) { //partial write
430 count = -count;
431 cur_block = block_to_sparse_block(block, &offset, channel,
432 sm);
433 if (!sm->blocks[cur_block]) {
434 sm->blocks[cur_block] = 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, in, count);
439 } else {
440 for (i = 0; i < count; ++i) {
441 if (block + i >= sm->blocks_count)
442 return 0;
443 cur_block = block_to_sparse_block(block + i, &offset,
444 channel, sm);
445 if (!sm->blocks[cur_block]) {
446 sm->blocks[cur_block] =
447 calloc(1, sm->block_size);
448 if (!sm->blocks[cur_block])
449 return EXT2_ET_NO_MEMORY;
450 }
451 memcpy(sm->blocks[cur_block] + offset,
452 in + (i * channel->block_size),
453 channel->block_size);
454 }
455 }
456 return 0;
457 }
458
sparse_write_blk(io_channel channel,unsigned long block,int count,const void * buf)459 static errcode_t sparse_write_blk(io_channel channel, unsigned long block,
460 int count, const void *buf)
461 {
462 return sparse_write_blk64(channel, block, count, buf);
463 }
464
sparse_discard(io_channel channel,blk64_t blk,unsigned long long count)465 static errcode_t sparse_discard(io_channel channel __attribute__((unused)),
466 blk64_t blk, unsigned long long count)
467 {
468 blk64_t cur_block, offset;
469 struct sparse_map *sm = channel->private_data;
470
471 if (check_block_size(channel, sm))
472 return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
473
474 for (unsigned long long i = 0; i < count; ++i) {
475 if (blk + i >= sm->blocks_count)
476 return 0;
477 cur_block = block_to_sparse_block(blk + i, &offset, channel,
478 sm);
479 if (!sm->blocks[cur_block])
480 continue;
481 free(sm->blocks[cur_block]);
482 sm->blocks[cur_block] = NULL;
483 }
484 return 0;
485 }
486
sparse_zeroout(io_channel channel,blk64_t blk,unsigned long long count)487 static errcode_t sparse_zeroout(io_channel channel, blk64_t blk,
488 unsigned long long count)
489 {
490 return sparse_discard(channel, blk, count);
491 }
492
sparse_flush(io_channel channel)493 static errcode_t sparse_flush(io_channel channel __attribute__((unused)))
494 {
495 return 0;
496 }
497
sparse_set_option(io_channel channel,const char * option,const char * arg)498 static errcode_t sparse_set_option(io_channel channel __attribute__((unused)),
499 const char *option __attribute__((unused)),
500 const char *arg __attribute__((unused)))
501 {
502 return 0;
503 }
504
sparse_cache_readahead(io_channel channel,blk64_t blk,unsigned long long count)505 static errcode_t sparse_cache_readahead(
506 io_channel channel __attribute__((unused)),
507 blk64_t blk __attribute__((unused)),
508 unsigned long long count __attribute__((unused)))
509 {
510 return 0;
511 }
512
513 static struct struct_io_manager struct_sparse_manager = {
514 .magic = EXT2_ET_MAGIC_IO_MANAGER,
515 .name = "Android sparse I/O Manager",
516 .open = sparse_open,
517 .close = sparse_close,
518 .set_blksize = sparse_set_blksize,
519 .read_blk = sparse_read_blk,
520 .write_blk = sparse_write_blk,
521 .flush = sparse_flush,
522 .write_byte = NULL,
523 .set_option = sparse_set_option,
524 .get_stats = NULL,
525 .read_blk64 = sparse_read_blk64,
526 .write_blk64 = sparse_write_blk64,
527 .discard = sparse_discard,
528 .cache_readahead = sparse_cache_readahead,
529 .zeroout = sparse_zeroout,
530 };
531
532 static struct struct_io_manager struct_sparsefd_manager = {
533 .magic = EXT2_ET_MAGIC_IO_MANAGER,
534 .name = "Android sparse fd I/O Manager",
535 .open = sparsefd_open,
536 .close = sparse_close,
537 .set_blksize = sparse_set_blksize,
538 .read_blk = sparse_read_blk,
539 .write_blk = sparse_write_blk,
540 .flush = sparse_flush,
541 .write_byte = NULL,
542 .set_option = sparse_set_option,
543 .get_stats = NULL,
544 .read_blk64 = sparse_read_blk64,
545 .write_blk64 = sparse_write_blk64,
546 .discard = sparse_discard,
547 .cache_readahead = sparse_cache_readahead,
548 .zeroout = sparse_zeroout,
549 };
550
551 #endif
552
553 io_manager sparse_io_manager = &struct_sparse_manager;
554 io_manager sparsefd_io_manager = &struct_sparsefd_manager;
555