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) 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 70static int sparse_import_segment(void *priv, const void *data, int 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 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) { 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 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; 203 sparse_params->block_size = 0; 204 sparse_params->blocks_count = 0; 205 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 212 if (is_fd) { 213 ret = sscanf(name, "(%d):%llu:%u", &sparse_params->fd, 214 (unsigned long long *)&sparse_params->blocks_count, 215 &sparse_params->block_size); 216 } else { 217 ret = sscanf(name, "(%[^)])%*[:]%llu%*[:]%u", sparse_params->file, 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 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) { 273 fprintf(stderr, "failed to alloc %llu\n", 274 (unsigned long long)num * block_size); 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 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) { 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); 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); 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; 547