Home | History | Annotate | Download | only in android
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <libgen.h>
     18 #include <unistd.h>
     19 #include <sys/types.h>
     20 #include <sys/stat.h>
     21 #include <fcntl.h>
     22 #include <ext2fs/ext2fs.h>
     23 #include <et/com_err.h>
     24 #include <sparse/sparse.h>
     25 
     26 struct {
     27 	int	crc;
     28 	int	sparse;
     29 	int	gzip;
     30 	char	*in_file;
     31 	char	*out_file;
     32 	bool	overwrite_input;
     33 } params = {
     34 	.crc	    = 0,
     35 	.sparse	    = 1,
     36 	.gzip	    = 0,
     37 };
     38 
     39 #define ext2fs_fatal(Retval, Format, ...) \
     40 	do { \
     41 		com_err("error", Retval, Format, __VA_ARGS__); \
     42 		exit(EXIT_FAILURE); \
     43 	} while(0)
     44 
     45 #define sparse_fatal(Format) \
     46 	do { \
     47 		fprintf(stderr, "sparse: "Format); \
     48 		exit(EXIT_FAILURE); \
     49 	} while(0)
     50 
     51 static void usage(char *path)
     52 {
     53 	char *progname = basename(path);
     54 
     55 	fprintf(stderr, "%s [ options ] <image or block device> <output image>\n"
     56 			"  -c include CRC block\n"
     57 			"  -z gzip output\n"
     58 			"  -S don't use sparse output format\n", progname);
     59 }
     60 
     61 static struct buf_item {
     62 	struct buf_item	    *next;
     63 	void		    *buf[0];
     64 } *buf_list;
     65 
     66 static void add_chunk(ext2_filsys fs, struct sparse_file *s, blk_t chunk_start, blk_t chunk_end)
     67 {
     68 	int retval;
     69 	unsigned int nb_blk = chunk_end - chunk_start;
     70 	size_t len = nb_blk * fs->blocksize;
     71 	int64_t offset = (int64_t)chunk_start * (int64_t)fs->blocksize;
     72 
     73 	if (params.overwrite_input == false) {
     74 		if (sparse_file_add_file(s, params.in_file, offset, len, chunk_start) < 0)
     75 			sparse_fatal("adding data to the sparse file");
     76 	} else {
     77 		/*
     78 		 * The input file will be overwritten, make a copy of
     79 		 * the blocks
     80 		 */
     81 		struct buf_item *bi = calloc(1, sizeof(struct buf_item) + len);
     82 		if (buf_list == NULL)
     83 			buf_list = bi;
     84 		else {
     85 			bi->next = buf_list;
     86 			buf_list = bi;
     87 		}
     88 
     89 		retval = io_channel_read_blk64(fs->io, chunk_start, nb_blk, bi->buf);
     90 		if (retval < 0)
     91 			ext2fs_fatal(retval, "reading block %u - %u", chunk_start, chunk_end);
     92 
     93 		if (sparse_file_add_data(s, bi->buf, len, chunk_start) < 0)
     94 			sparse_fatal("adding data to the sparse file");
     95 	}
     96 }
     97 
     98 static void free_chunks(void)
     99 {
    100 	struct buf_item *bi;
    101 
    102 	while (buf_list) {
    103 		bi = buf_list->next;
    104 		free(buf_list);
    105 		buf_list = bi;
    106 	}
    107 }
    108 
    109 static struct sparse_file *ext_to_sparse(const char *in_file)
    110 {
    111 	errcode_t retval;
    112 	ext2_filsys fs;
    113 	struct sparse_file *s;
    114 	int64_t chunk_start = -1;
    115 	blk_t first_blk, last_blk, nb_blk, cur_blk;
    116 
    117 	retval = ext2fs_open(in_file, 0, 0, 0, unix_io_manager, &fs);
    118 	if (retval)
    119 		ext2fs_fatal(retval, "while reading %s", in_file);
    120 
    121 	retval = ext2fs_read_block_bitmap(fs);
    122 	if (retval)
    123 		ext2fs_fatal(retval, "while reading block bitmap of %s", in_file);
    124 
    125 	first_blk = ext2fs_get_block_bitmap_start2(fs->block_map);
    126 	last_blk = ext2fs_get_block_bitmap_end2(fs->block_map);
    127 	nb_blk = last_blk - first_blk + 1;
    128 
    129 	s = sparse_file_new(fs->blocksize, (uint64_t)fs->blocksize * (uint64_t)nb_blk);
    130 	if (!s)
    131 		sparse_fatal("creating sparse file");
    132 
    133 	/*
    134 	 * The sparse format encodes the size of a chunk (and its header) in a
    135 	 * 32-bit unsigned integer (UINT32_MAX)
    136 	 * When writing the chunk, the library uses a single call to write().
    137 	 * Linux's implementation of the 'write' syscall does not allow transfers
    138 	 * larger than INT32_MAX (32-bit _and_ 64-bit systems).
    139 	 * Make sure we do not create chunks larger than this limit.
    140 	 */
    141 	int64_t max_blk_per_chunk = (INT32_MAX - 12) / fs->blocksize;
    142 
    143 	/* Iter on the blocks to merge contiguous chunk */
    144 	for (cur_blk = first_blk; cur_blk <= last_blk; ++cur_blk) {
    145 		if (ext2fs_test_block_bitmap2(fs->block_map, cur_blk)) {
    146 			if (chunk_start == -1) {
    147 				chunk_start = cur_blk;
    148 			} else if (cur_blk - chunk_start + 1 == max_blk_per_chunk) {
    149 				add_chunk(fs, s, chunk_start, cur_blk);
    150 				chunk_start = -1;
    151 			}
    152 		} else if (chunk_start != -1) {
    153 			add_chunk(fs, s, chunk_start, cur_blk);
    154 			chunk_start = -1;
    155 		}
    156 	}
    157 	if (chunk_start != -1)
    158 		add_chunk(fs, s, chunk_start, cur_blk - 1);
    159 
    160 	ext2fs_free(fs);
    161 	return s;
    162 }
    163 
    164 static bool same_file(const char *in, const char *out)
    165 {
    166 	struct stat st1, st2;
    167 
    168 	if (access(out, F_OK) == -1)
    169 		return false;
    170 
    171 	if (lstat(in, &st1) == -1)
    172 		ext2fs_fatal(errno, "stat %s\n", in);
    173 	if (lstat(out, &st2) == -1)
    174 		ext2fs_fatal(errno, "stat %s\n", out);
    175 	return st1.st_ino == st2.st_ino;
    176 }
    177 
    178 int main(int argc, char *argv[])
    179 {
    180 	int opt;
    181 	int out_fd;
    182 	struct sparse_file *s;
    183 
    184 	while ((opt = getopt(argc, argv, "czS")) != -1) {
    185 		switch(opt) {
    186 		case 'c':
    187 			params.crc = 1;
    188 			break;
    189 		case 'z':
    190 			params.gzip = 1;
    191 			break;
    192 		case 'S':
    193 			params.sparse = 0;
    194 			break;
    195 		default:
    196 			usage(argv[0]);
    197 			exit(EXIT_FAILURE);
    198 		}
    199 	}
    200 	if (optind + 1 >= argc) {
    201 		usage(argv[0]);
    202 		exit(EXIT_FAILURE);
    203 	}
    204 	params.in_file = strdup(argv[optind++]);
    205 	params.out_file = strdup(argv[optind]);
    206 	params.overwrite_input = same_file(params.in_file, params.out_file);
    207 
    208 	s = ext_to_sparse(params.in_file);
    209 
    210 	out_fd = open(params.out_file, O_WRONLY | O_CREAT | O_TRUNC, 0664);
    211 	if (out_fd == -1)
    212 		ext2fs_fatal(errno, "opening %s\n", params.out_file);
    213 	if (sparse_file_write(s, out_fd, params.gzip, params.sparse, params.crc) < 0)
    214 		sparse_fatal("writing sparse file");
    215 
    216 	sparse_file_destroy(s);
    217 
    218 	free(params.in_file);
    219 	free(params.out_file);
    220 	free_chunks();
    221 	close(out_fd);
    222 
    223 	return 0;
    224 }
    225