blob: 486b8054fa796998a7aa49c9b22e0449441c22ef [file] [log] [blame]
Colin Cross28fa5bc2012-05-20 13:28:05 -07001/*
2 * Copyright (C) 2010 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 _FILE_OFFSET_BITS 64
18#define _LARGEFILE64_SOURCE 1
19#include <sys/types.h>
20#include <unistd.h>
21
22#include "sparse_defs.h"
23#include "sparse_format.h"
24#include "sparse_crc32.h"
25
26#include <fcntl.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <sys/types.h>
33#include <sys/mman.h>
34#include <unistd.h>
35
36#define COPY_BUF_SIZE (1024*1024)
37u8 *copybuf;
38
39/* This will be malloc'ed with the size of blk_sz from the sparse file header */
40u8* zerobuf;
41
42#define SPARSE_HEADER_MAJOR_VER 1
43#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
44#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
45
46void usage()
47{
48 fprintf(stderr, "Usage: simg2img <sparse_image_file> <raw_image_file>\n");
49}
50
51static int read_all(int fd, void *buf, size_t len)
52{
53 size_t total = 0;
54 int ret;
55 char *ptr = buf;
56
57 while (total < len) {
58 ret = read(fd, ptr, len - total);
59
60 if (ret < 0)
61 return ret;
62
63 if (ret == 0)
64 return total;
65
66 ptr += ret;
67 total += ret;
68 }
69
70 return total;
71}
72
73static int write_all(int fd, void *buf, size_t len)
74{
75 size_t total = 0;
76 int ret;
77 char *ptr = buf;
78
79 while (total < len) {
80 ret = write(fd, ptr, len - total);
81
82 if (ret < 0)
83 return ret;
84
85 if (ret == 0)
86 return total;
87
88 ptr += ret;
89 total += ret;
90 }
91
92 return total;
93}
94
95int process_raw_chunk(int in, int out, u32 blocks, u32 blk_sz, u32 *crc32)
96{
97 u64 len = (u64)blocks * blk_sz;
98 int ret;
99 int chunk;
100
101 while (len) {
102 chunk = (len > COPY_BUF_SIZE) ? COPY_BUF_SIZE : len;
103 ret = read_all(in, copybuf, chunk);
104 if (ret != chunk) {
105 fprintf(stderr, "read returned an error copying a raw chunk: %d %d\n",
106 ret, chunk);
107 exit(-1);
108 }
109 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
110 ret = write_all(out, copybuf, chunk);
111 if (ret != chunk) {
112 fprintf(stderr, "write returned an error copying a raw chunk\n");
113 exit(-1);
114 }
115 len -= chunk;
116 }
117
118 return blocks;
119}
120
121
122int process_fill_chunk(int in, int out, u32 blocks, u32 blk_sz, u32 *crc32)
123{
124 u64 len = (u64)blocks * blk_sz;
125 int ret;
126 int chunk;
127 u32 fill_val;
128 u32 *fillbuf;
129 unsigned int i;
130
131 /* Fill copy_buf with the fill value */
132 ret = read_all(in, &fill_val, sizeof(fill_val));
133 fillbuf = (u32 *)copybuf;
134 for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
135 fillbuf[i] = fill_val;
136 }
137
138 while (len) {
139 chunk = (len > COPY_BUF_SIZE) ? COPY_BUF_SIZE : len;
140 *crc32 = sparse_crc32(*crc32, copybuf, chunk);
141 ret = write_all(out, copybuf, chunk);
142 if (ret != chunk) {
143 fprintf(stderr, "write returned an error copying a raw chunk\n");
144 exit(-1);
145 }
146 len -= chunk;
147 }
148
149 return blocks;
150}
151
152int process_skip_chunk(int out, u32 blocks, u32 blk_sz, u32 *crc32)
153{
154 /* len needs to be 64 bits, as the sparse file specifies the skip amount
155 * as a 32 bit value of blocks.
156 */
157 u64 len = (u64)blocks * blk_sz;
158
159 lseek64(out, len, SEEK_CUR);
160
161 return blocks;
162}
163
164int process_crc32_chunk(int in, u32 crc32)
165{
166 u32 file_crc32;
167 int ret;
168
169 ret = read_all(in, &file_crc32, 4);
170 if (ret != 4) {
171 fprintf(stderr, "read returned an error copying a crc32 chunk\n");
172 exit(-1);
173 }
174
175 if (file_crc32 != crc32) {
176 fprintf(stderr, "computed crc32 of 0x%8.8x, expected 0x%8.8x\n",
177 crc32, file_crc32);
178 exit(-1);
179 }
180
181 return 0;
182}
183
184int main(int argc, char *argv[])
185{
186 int in;
187 int out;
188 unsigned int i;
189 sparse_header_t sparse_header;
190 chunk_header_t chunk_header;
191 u32 crc32 = 0;
192 u32 total_blocks = 0;
193 int ret;
194
195 if (argc != 3) {
196 usage();
197 exit(-1);
198 }
199
200 if ( (copybuf = malloc(COPY_BUF_SIZE)) == 0) {
201 fprintf(stderr, "Cannot malloc copy buf\n");
202 exit(-1);
203 }
204
205 if (strcmp(argv[1], "-") == 0) {
206 in = STDIN_FILENO;
207 } else {
208 if ((in = open(argv[1], O_RDONLY)) == 0) {
209 fprintf(stderr, "Cannot open input file %s\n", argv[1]);
210 exit(-1);
211 }
212 }
213
214 if (strcmp(argv[2], "-") == 0) {
215 out = STDOUT_FILENO;
216 } else {
217 if ((out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666)) == 0) {
218 fprintf(stderr, "Cannot open output file %s\n", argv[2]);
219 exit(-1);
220 }
221 }
222
223 ret = read_all(in, &sparse_header, sizeof(sparse_header));
224 if (ret != sizeof(sparse_header)) {
225 fprintf(stderr, "Error reading sparse file header\n");
226 exit(-1);
227 }
228
229 if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
230 fprintf(stderr, "Bad magic\n");
231 exit(-1);
232 }
233
234 if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
235 fprintf(stderr, "Unknown major version number\n");
236 exit(-1);
237 }
238
239 if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
240 /* Skip the remaining bytes in a header that is longer than
241 * we expected.
242 */
243 lseek64(in, sparse_header.file_hdr_sz - SPARSE_HEADER_LEN, SEEK_CUR);
244 }
245
246 if ( (zerobuf = malloc(sparse_header.blk_sz)) == 0) {
247 fprintf(stderr, "Cannot malloc zero buf\n");
248 exit(-1);
249 }
250
251 for (i=0; i<sparse_header.total_chunks; i++) {
252 ret = read_all(in, &chunk_header, sizeof(chunk_header));
253 if (ret != sizeof(chunk_header)) {
254 fprintf(stderr, "Error reading chunk header\n");
255 exit(-1);
256 }
257
258 if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
259 /* Skip the remaining bytes in a header that is longer than
260 * we expected.
261 */
262 lseek64(in, sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN, SEEK_CUR);
263 }
264
265 switch (chunk_header.chunk_type) {
266 case CHUNK_TYPE_RAW:
267 if (chunk_header.total_sz != (sparse_header.chunk_hdr_sz +
268 (chunk_header.chunk_sz * sparse_header.blk_sz)) ) {
269 fprintf(stderr, "Bogus chunk size for chunk %d, type Raw\n", i);
270 exit(-1);
271 }
272 total_blocks += process_raw_chunk(in, out,
273 chunk_header.chunk_sz, sparse_header.blk_sz, &crc32);
274 break;
275 case CHUNK_TYPE_FILL:
276 if (chunk_header.total_sz != (sparse_header.chunk_hdr_sz + sizeof(u32)) ) {
277 fprintf(stderr, "Bogus chunk size for chunk %d, type Fill\n", i);
278 exit(-1);
279 }
280 total_blocks += process_fill_chunk(in, out,
281 chunk_header.chunk_sz, sparse_header.blk_sz, &crc32);
282 break;
283 case CHUNK_TYPE_DONT_CARE:
284 if (chunk_header.total_sz != sparse_header.chunk_hdr_sz) {
285 fprintf(stderr, "Bogus chunk size for chunk %d, type Dont Care\n", i);
286 exit(-1);
287 }
288 total_blocks += process_skip_chunk(out,
289 chunk_header.chunk_sz, sparse_header.blk_sz, &crc32);
290 break;
291 case CHUNK_TYPE_CRC32:
292 process_crc32_chunk(in, crc32);
293 break;
294 default:
295 fprintf(stderr, "Unknown chunk type 0x%4.4x\n", chunk_header.chunk_type);
296 }
297
298 }
299
300 /* If the last chunk was a skip, then the code just did a seek, but
301 * no write, and the file won't actually be the correct size. This
302 * will make the file the correct size. Make sure the offset is
303 * computed in 64 bits, and the function called can handle 64 bits.
304 */
305 if (ftruncate64(out, (u64)total_blocks * sparse_header.blk_sz)) {
306 fprintf(stderr, "Error calling ftruncate() to set the image size\n");
307 exit(-1);
308 }
309
310 close(in);
311 close(out);
312
313 if (sparse_header.total_blks != total_blocks) {
314 fprintf(stderr, "Wrote %d blocks, expected to write %d blocks\n",
315 total_blocks, sparse_header.total_blks);
316 exit(-1);
317 }
318
319 exit(0);
320}
321