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 #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