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) 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 } 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 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 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 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 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 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 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 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 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 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 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 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 339 static errcode_t sparse_set_blksize(io_channel channel, int blksize) 340 { 341 channel->block_size = blksize; 342 return 0; 343 } 344 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 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 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 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 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 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 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 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 480 static errcode_t sparse_flush(io_channel channel __attribute__((unused))) 481 { 482 return 0; 483 } 484 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 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