img2simg.c revision 28fa5bc347390480fe190294c6c385b6a9f0d68b
1/*
2 * Copyright (C) 2010-2012 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#define DEFAULT_BLOCK_SIZE	"4K"
18#define DEFAULT_CHUNK_SIZE	"64M"
19#define DEFAULT_SUFFIX		"%03d"
20
21#include "sparse_format.h"
22#if 0 /* endian.h is not on all platforms */
23# include <endian.h>
24#else
25  /* For now, just assume we're going to run on little-endian. */
26# define my_htole32(h) (h)
27# define my_htole16(h) (h)
28#endif
29#include <errno.h>
30#include <fcntl.h>
31#include <limits.h>
32#include <stdarg.h>
33#include <stddef.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <unistd.h>
38#include <sys/stat.h>
39#include <sys/types.h>
40
41#define COPY_BUF_SIZE (1024*1024)
42static char *copy_buf;
43
44static const char *progname(const char *argv0)
45{
46    const char *prog_name;
47    if ((prog_name = strrchr(argv0, '/')))
48	return(prog_name + 1);	/* Advance beyond '/'. */
49    return(argv0);		/* No '/' in argv0, use it as is. */
50}
51
52static void error_exit(const char *fmt, ...)
53{
54    va_list ap;
55    va_start(ap, fmt);
56    vfprintf(stderr, fmt, ap);
57    fputc('\n', stderr);
58    va_end(ap);
59
60    exit(EXIT_FAILURE);
61}
62
63static void usage(const char *argv0, const char *error_fmt, ...)
64{
65    fprintf(stderr,
66	    "Usage: %s [OPTIONS] <raw_image_file>\n",
67	    progname(argv0));
68    fprintf(stderr, "The <raw_image_file> will be split into as many sparse\n");
69    fprintf(stderr, "files as needed.  Each sparse file will contain a single\n");
70    fprintf(stderr, "DONT CARE chunk to offset to the correct block and then\n");
71    fprintf(stderr, "a single RAW chunk containing a portion of the data from\n");
72    fprintf(stderr, "the raw image file.  The sparse files will be named by\n");
73    fprintf(stderr, "appending a number to the name of the raw image file.\n");
74    fprintf(stderr, "\n");
75    fprintf(stderr, "OPTIONS (Defaults are enclosed by square brackets):\n");
76    fprintf(stderr, "  -s SUFFIX      Format appended number with SUFFIX [%s]\n",
77	    DEFAULT_SUFFIX);
78    fprintf(stderr, "  -B SIZE        Use a block size of SIZE [%s]\n",
79	    DEFAULT_BLOCK_SIZE);
80    fprintf(stderr, "  -C SIZE        Use a chunk size of SIZE [%s]\n",
81	    DEFAULT_CHUNK_SIZE);
82    fprintf(stderr, "SIZE is a decimal integer that may optionally be\n");
83    fprintf(stderr, "followed by a suffix that specifies a multiplier for\n");
84    fprintf(stderr, "the integer:\n");
85    fprintf(stderr, "       c         1 byte (the default when omitted)\n");
86    fprintf(stderr, "       w         2 bytes\n");
87    fprintf(stderr, "       b         512 bytes\n");
88    fprintf(stderr, "       kB        1000 bytes\n");
89    fprintf(stderr, "       K         1024 bytes\n");
90    fprintf(stderr, "       MB        1000*1000 bytes\n");
91    fprintf(stderr, "       M         1024*1024 bytes\n");
92    fprintf(stderr, "       GB        1000*1000*1000 bytes\n");
93    fprintf(stderr, "       G         1024*1024*1024 bytes\n");
94
95    if (error_fmt && *error_fmt)
96    {
97	fprintf(stderr, "\n");
98	va_list ap;
99	va_start(ap, error_fmt);
100	vfprintf(stderr, error_fmt, ap);
101	va_end(ap);
102	fprintf(stderr, "\n");
103    }
104
105    exit(EXIT_FAILURE);
106}
107
108static void cpy_file(int out_fd, char *out_path, int in_fd, char *in_path,
109		     size_t len)
110{
111    ssize_t s, cpy_len = COPY_BUF_SIZE;
112
113    while (len) {
114	if (len < COPY_BUF_SIZE)
115	    cpy_len = len;
116
117	s = read(in_fd, copy_buf, cpy_len);
118	if (s < 0)
119	    error_exit("\"%s\": %s", in_path, strerror(errno));
120	if (!s)
121	    error_exit("\"%s\": Unexpected EOF", in_path);
122
123	cpy_len = s;
124
125	s = write(out_fd, copy_buf, cpy_len);
126	if (s < 0)
127	    error_exit("\"%s\": %s", out_path, strerror(errno));
128	if (s != cpy_len)
129	    error_exit("\"%s\": Short data write (%lu)", out_path,
130		       (unsigned long)s);
131
132	len -= cpy_len;
133    }
134}
135
136static int parse_size(const char *size_str, size_t *size)
137{
138    static const size_t MAX_SIZE_T = ~(size_t)0;
139    size_t mult;
140    unsigned long long int value;
141    const char *end;
142    errno = 0;
143    value = strtoull(size_str, (char **)&end, 10);
144    if (errno != 0 || end == size_str || value > MAX_SIZE_T)
145	return -1;
146    if (*end == '\0') {
147	*size = value;
148	return 0;
149    }
150    if (!strcmp(end, "c"))
151	mult = 1;
152    else if (!strcmp(end, "w"))
153	mult = 2;
154    else if (!strcmp(end, "b"))
155	mult = 512;
156    else if (!strcmp(end, "kB"))
157	mult = 1000;
158    else if (!strcmp(end, "K"))
159	mult = 1024;
160    else if (!strcmp(end, "MB"))
161	mult = (size_t)1000*1000;
162    else if (!strcmp(end, "M"))
163	mult = (size_t)1024*1024;
164    else if (!strcmp(end, "GB"))
165	mult = (size_t)1000*1000*1000;
166    else if (!strcmp(end, "G"))
167	mult = (size_t)1024*1024*1024;
168    else
169	return -1;
170
171    if (value > MAX_SIZE_T / mult)
172	return -1;
173    *size = value * mult;
174    return 0;
175}
176
177int main(int argc, char *argv[])
178{
179    char *suffix = DEFAULT_SUFFIX;
180    char *block_size_str = DEFAULT_BLOCK_SIZE;
181    char *chunk_size_str = DEFAULT_CHUNK_SIZE;
182    size_t block_size, chunk_size, blocks_per_chunk, to_write;
183    char *in_path, *out_path, *out_fmt;
184    int in_fd, out_fd;
185    struct stat in_st;
186    off_t left_to_write;
187    struct {
188	sparse_header_t sparse_hdr;
189	chunk_header_t dont_care_hdr;
190	chunk_header_t raw_hdr;
191    } file_hdr;
192    unsigned int file_count;
193    ssize_t s;
194    int i;
195
196    /* Parse the command line. */
197    while ((i = getopt(argc, argv, "s:B:C:")) != -1)
198    {
199	switch (i) {
200	case 's':
201	    suffix = optarg;
202	    break;
203	case 'B':
204	    block_size_str = optarg;
205	    break;
206	case 'C':
207	    chunk_size_str = optarg;
208	    break;
209	default:
210	    usage(argv[0], NULL);
211	    break;
212	}
213    }
214
215    if (parse_size(block_size_str, &block_size))
216	usage(argv[0], "Can not parse \"%s\" as a block size.",
217	      block_size_str);
218    if (block_size % 4096)
219	usage(argv[0], "Block size is not a multiple of 4096.");
220
221    if (parse_size(chunk_size_str, &chunk_size))
222	usage(argv[0], "Can not parse \"%s\" as a chunk size.",
223	      chunk_size_str);
224    if (chunk_size % block_size)
225	usage(argv[0], "Chunk size is not a multiple of the block size.");
226    blocks_per_chunk = chunk_size / block_size;
227
228    if ((argc - optind) != 1)
229	usage(argv[0], "Missing or extra arguments.");
230    in_path = argv[optind];
231
232    /* Open the input file and validate it. */
233    if ((in_fd = open(in_path, O_RDONLY)) < 0)
234	error_exit("open \"%s\": %s", in_path, strerror(errno));
235    if (fstat(in_fd, &in_st))
236	error_exit("fstat \"%s\": %s", in_path, strerror(errno));
237    left_to_write = in_st.st_size;
238    if (left_to_write % block_size)
239	error_exit(
240	    "\"%s\" size (%llu) is not a multiple of the block size (%llu).\n",
241	    in_path,
242	    (unsigned long long)left_to_write, (unsigned long long)block_size);
243
244    /* Get a buffer for copying the chunks. */
245    if ((copy_buf = malloc(COPY_BUF_SIZE)) == 0)
246	error_exit("malloc copy buffer: %s", strerror(errno));
247
248    /* Get a buffer for a sprintf format to form output paths. */
249    if ((out_fmt = malloc(sizeof("%s") + strlen(suffix))) == 0)
250	error_exit("malloc format buffer: %s", strerror(errno));
251    out_fmt[0] = '%';
252    out_fmt[1] = 's';
253    strcpy(out_fmt + 2, suffix);
254
255    /* Get a buffer for an output path. */
256    i = snprintf(copy_buf, COPY_BUF_SIZE, out_fmt, in_path, UINT_MAX);
257    if (i >= COPY_BUF_SIZE)
258	error_exit("Ridulously long suffix: %s", suffix);
259    if ((out_path = malloc(i + 1)) == 0)
260	error_exit("malloc output path buffer: %s", strerror(errno));
261
262    /*
263     * Each file gets a sparse_header, a Don't Care chunk to offset to
264     * where the data belongs and then a Raw chunk with the actual data.
265     */
266    memset((void *)&file_hdr.sparse_hdr, 0, sizeof(file_hdr.sparse_hdr));
267    file_hdr.sparse_hdr.magic = my_htole32(SPARSE_HEADER_MAGIC);
268    file_hdr.sparse_hdr.major_version = my_htole16(1);
269    file_hdr.sparse_hdr.minor_version = my_htole16(0);
270    file_hdr.sparse_hdr.file_hdr_sz = my_htole16(sizeof(sparse_header_t));
271    file_hdr.sparse_hdr.chunk_hdr_sz = my_htole16(sizeof(chunk_header_t));
272    file_hdr.sparse_hdr.blk_sz = my_htole32(block_size);
273    /* The total_blks will be set in the file loop below. */
274    file_hdr.sparse_hdr.total_chunks = my_htole32(2);
275    file_hdr.sparse_hdr.image_checksum = my_htole32(0); /* Typically unused. */
276
277    memset((void *)&file_hdr.dont_care_hdr, 0, sizeof(file_hdr.dont_care_hdr));
278    file_hdr.dont_care_hdr.chunk_type = my_htole16(CHUNK_TYPE_DONT_CARE);
279    /* The Don't Care's chunk_sz will be set in the file loop below. */
280    file_hdr.dont_care_hdr.total_sz = my_htole32(sizeof(chunk_header_t));
281
282    memset((void *)&file_hdr.raw_hdr, 0, sizeof(file_hdr.raw_hdr));
283    file_hdr.raw_hdr.chunk_type = my_htole16(CHUNK_TYPE_RAW);
284    file_hdr.raw_hdr.chunk_sz = my_htole32(blocks_per_chunk);
285    file_hdr.raw_hdr.total_sz = my_htole32(chunk_size + sizeof(chunk_header_t));
286
287    /* Loop through writing chunk_size to each of the output files. */
288    to_write = chunk_size;
289    for (file_count = 1; left_to_write ; file_count++) {
290	/* Fix up the headers on the last block. */
291	if (left_to_write < (off_t)chunk_size) {
292	    to_write = left_to_write;
293	    file_hdr.raw_hdr.chunk_sz = my_htole32(left_to_write / block_size);
294	    file_hdr.raw_hdr.total_sz = my_htole32(left_to_write
295						+ sizeof(chunk_header_t));
296	}
297
298	/* Form the pathname for this output file and open it. */
299	sprintf(out_path, out_fmt, in_path, file_count);
300	if ((out_fd = creat(out_path, 0666)) < 0)
301	    error_exit("\"%s\": %s", out_path, strerror(errno));
302
303	/* Update and write the headers to this output file. */
304	s = (file_count-1) * blocks_per_chunk;
305	file_hdr.dont_care_hdr.chunk_sz = my_htole32(s);
306	file_hdr.sparse_hdr.total_blks = my_htole32(s
307						+ (to_write / block_size));
308	s = write(out_fd, (void *)&file_hdr, sizeof(file_hdr));
309	if (s < 0)
310	    error_exit("\"%s\": %s", out_path, strerror(errno));
311	if (s != sizeof(file_hdr))
312	    error_exit("\"%s\": Short write (%lu)", out_path, (unsigned long)s);
313
314	/* Copy this chunk from the input file to the output file. */
315	cpy_file(out_fd, out_path, in_fd, in_path, to_write);
316
317	/* Close this output file and update the amount left to write. */
318	if (close(out_fd))
319	    error_exit("close \"%s\": %s", out_path, strerror(errno));
320	left_to_write -= to_write;
321    }
322
323    if (close(in_fd))
324	error_exit("close \"%s\": %s", in_path, strerror(errno));
325
326    exit(EXIT_SUCCESS);
327}
328