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