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