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