• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
sparse_open(const char * name EXT2FS_ATTR ((unused)),int flags EXT2FS_ATTR ((unused)),io_channel * channel EXT2FS_ATTR ((unused)))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 }
sparse_close(io_channel channel EXT2FS_ATTR ((unused)))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 
free_sparse_blocks(struct sparse_map * sm)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 
sparse_import_segment(void * priv,const void * data,int len,unsigned int block,unsigned int nr_blocks)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 
io_manager_import_sparse(struct sparse_io_params * params,struct sparse_map * sm,io_channel io)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 
io_manager_configure(struct sparse_io_params * params,int flags,io_channel io)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 
sparse_open_channel(struct sparse_io_params * sparse_params,int flags,io_channel * channel)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 
read_sparse_argv(const char * name,bool is_fd,struct sparse_io_params * sparse_params)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 
sparse_open(const char * name,int flags,io_channel * channel)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 
sparsefd_open(const char * name,int flags,io_channel * channel)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 
sparse_merge_blocks(struct sparse_map * sm,uint64_t start,uint64_t num)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 
sparse_close_channel(io_channel channel)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 
sparse_close(io_channel channel)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 
sparse_set_blksize(io_channel channel,int blksize)339 static errcode_t sparse_set_blksize(io_channel channel, int blksize)
340 {
341 	channel->block_size = blksize;
342 	return 0;
343 }
344 
block_to_sparse_block(blk64_t block,blk64_t * offset,io_channel channel,struct sparse_map * sm)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 
check_block_size(io_channel channel,struct sparse_map * sm)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 
sparse_read_blk64(io_channel channel,blk64_t block,int count,void * buf)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 
sparse_read_blk(io_channel channel,unsigned long block,int count,void * buf)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 
sparse_write_blk64(io_channel channel,blk64_t block,int count,const void * buf)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 
sparse_write_blk(io_channel channel,unsigned long block,int count,const void * buf)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 
sparse_discard(io_channel channel,blk64_t blk,unsigned long long count)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 
sparse_zeroout(io_channel channel,blk64_t blk,unsigned long long count)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 
sparse_flush(io_channel channel)480 static errcode_t sparse_flush(io_channel channel __attribute__((unused)))
481 {
482 	return 0;
483 }
484 
sparse_set_option(io_channel channel,const char * option,const char * arg)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 
sparse_cache_readahead(io_channel channel,blk64_t blk,unsigned long long count)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