OSDN Git Service

9c1ad37027f98048f2fbec8968f47bb31147d15b
[android-x86/system-extras.git] / ext4_utils / simg2img.c
1 /*
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 #define _LARGEFILE64_SOURCE
17 #define _FILE_OFFSET_BITS 64
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <sys/mman.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <stdio.h>
26
27 #include "ext4_utils.h"
28 #include "output_file.h"
29 #include "sparse_format.h"
30
31 #if defined(__APPLE__) && defined(__MACH__)
32 #define lseek64 lseek
33 #define off64_t off_t
34 #endif
35
36 #define COPY_BUF_SIZE (1024*1024)
37 u8 *copybuf;
38
39 #define SPARSE_HEADER_MAJOR_VER 1
40 #define SPARSE_HEADER_LEN       (sizeof(sparse_header_t))
41 #define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
42
43 void usage()
44 {
45   fprintf(stderr, "Usage: simg2img <sparse_image_file> <raw_image_file>\n");
46 }
47
48 int process_raw_chunk(FILE *in, FILE *out, u32 blocks, u32 blk_sz, u32 *crc32)
49 {
50         u64 len = (u64)blocks * blk_sz;
51         int chunk;
52
53         while (len) {
54                 chunk = (len > COPY_BUF_SIZE) ? COPY_BUF_SIZE : len;
55                 fread(copybuf, chunk, 1, in);
56                 fwrite(copybuf, chunk, 1, out);
57                 len -= chunk;
58         }
59
60         return blocks;
61 }
62
63
64 int process_skip_chunk(FILE *out, u32 blocks, u32 blk_sz, u32 *crc32)
65 {
66         /* len needs to be 64 bits, as the sparse file specifies the skip amount
67          * as a 32 bit value of blocks.
68          */
69         u64 len = (u64)blocks * blk_sz;
70         long skip_chunk;
71
72         /* Fseek takes the offset as a long, which may be 32 bits on some systems.
73          * So, lets do a sequence of fseeks() with SEEK_CUR to get the file pointer
74          * where we want it.
75          */
76         while (len) {
77                 skip_chunk = (len > 0x80000000) ? 0x80000000 : len;
78                 fseek(out, skip_chunk, SEEK_CUR);
79                 len -= skip_chunk;
80         }
81
82         return blocks;
83 }
84
85 int main(int argc, char *argv[])
86 {
87         FILE *in, *out;
88         unsigned int i;
89         sparse_header_t sparse_header;
90         chunk_header_t chunk_header;
91         u32 crc32 = 0;
92         u32 total_blocks = 0;
93
94         if (argc != 3) {
95                 usage();
96                 exit(-1);
97         }
98
99         if ( (copybuf = malloc(COPY_BUF_SIZE)) == 0) {
100                 fprintf(stderr, "Cannot malloc copy buf\n");
101                 exit(-1);
102         }
103
104         if ((in = fopen(argv[1], "rb")) == 0) {
105                 fprintf(stderr, "Cannot open input file %s\n", argv[1]);
106                 exit(-1);
107         }
108
109         if ((out = fopen(argv[2], "wb")) == 0) {
110                 fprintf(stderr, "Cannot open output file %s\n", argv[2]);
111                 exit(-1);
112         }
113
114         if (fread(&sparse_header, sizeof(sparse_header), 1, in) != 1) {
115                 fprintf(stderr, "Error reading sparse file header\n");
116                 exit(-1);
117         }
118
119         if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
120                 fprintf(stderr, "Bad magic\n");
121                 exit(-1);
122         }
123
124         if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
125                 fprintf(stderr, "Unknown major version number\n");
126                 exit(-1);
127         }
128
129         if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
130                 /* Skip the remaining bytes in a header that is longer than
131                  * we expected.
132                  */
133                 fseek(in, sparse_header.file_hdr_sz - SPARSE_HEADER_LEN, SEEK_CUR);
134         }
135
136         for (i=0; i<sparse_header.total_chunks; i++) {
137                 if (fread(&chunk_header, sizeof(chunk_header), 1, in) != 1) {
138                         fprintf(stderr, "Error reading chunk header\n");
139                         exit(-1);
140                 }
141  
142                 if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
143                         /* Skip the remaining bytes in a header that is longer than
144                          * we expected.
145                          */
146                         fseek(in, sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN, SEEK_CUR);
147                 }
148
149                 switch (chunk_header.chunk_type) {
150                     case CHUNK_TYPE_RAW:
151                         if (chunk_header.total_sz != (sparse_header.chunk_hdr_sz +
152                                  (chunk_header.chunk_sz * sparse_header.blk_sz)) ) {
153                                 fprintf(stderr, "Bogus chunk size for chunk %d, type Raw\n", i);
154                                 exit(-1);
155                         }
156                         total_blocks += process_raw_chunk(in, out,
157                                          chunk_header.chunk_sz, sparse_header.blk_sz, &crc32);
158                         break;
159                     case CHUNK_TYPE_DONT_CARE:
160                         if (chunk_header.total_sz != sparse_header.chunk_hdr_sz) {
161                                 fprintf(stderr, "Bogus chunk size for chunk %d, type Dont Care\n", i);
162                                 exit(-1);
163                         }
164                         total_blocks += process_skip_chunk(out,
165                                          chunk_header.chunk_sz, sparse_header.blk_sz, &crc32);
166                         break;
167                     default:
168                         fprintf(stderr, "Unknown chunk type 0x%4.4x\n", chunk_header.chunk_type);
169                         exit(-1);
170                 }
171
172         }
173
174         /* If the last chunk was a skip, then the code just did a seek, but
175          * no write, and the file won't actually be the correct size.  This
176          * will make the file the correct size.  Make sure the offset is
177          * computed in 64 bits, and the function called can handle 64 bits.
178          */
179         ftruncate(fileno(out), (u64)total_blocks * sparse_header.blk_sz);
180
181         fclose(in);
182         fclose(out);
183
184         if (sparse_header.total_blks != total_blocks) {
185                 fprintf(stderr, "Wrote %d blocks, expected to write %d blocks\n",
186                          total_blocks, sparse_header.total_blks);
187                 exit(-1);
188         }
189
190         if (sparse_header.image_checksum != crc32) {
191                 fprintf(stderr, "computed crc32 of %d, expected %d\n",
192                          crc32, sparse_header.image_checksum);
193                 exit(-1);
194         }
195
196         exit(0);
197 }
198