Home | History | Annotate | Download | only in ext2fs
      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)
     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 }
     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 
     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 
     70 static 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 
     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 
    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 
    185 static 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 
    198 static 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 
    229 static 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 
    246 static 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 
    263 static 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 
    293 static 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 
    321 ret:
    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 
    331 static 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 
    344 static errcode_t sparse_set_blksize(io_channel channel, int blksize)
    345 {
    346 	channel->block_size = blksize;
    347 	return 0;
    348 }
    349 
    350 static 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 
    363 static 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 
    370 static 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 
    404 static 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 
    410 static 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 
    451 static 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 
    457 static 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 
    479 static 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 
    485 static errcode_t sparse_flush(io_channel channel __attribute__((unused)))
    486 {
    487 	return 0;
    488 }
    489 
    490 static 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 
    497 static 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 
    505 static 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 
    524 static 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 
    545 io_manager sparse_io_manager = &struct_sparse_manager;
    546 io_manager sparsefd_io_manager = &struct_sparsefd_manager;
    547