simg2img.c revision fe4a03182b5b17d333511c72406f926a791345d3
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) 30u8 *copybuf; 31 32/* This will be malloc'ed with the size of blk_sz from the sparse file header */ 33u8* 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 39void usage() 40{ 41 fprintf(stderr, "Usage: simg2img <sparse_image_file> <raw_image_file>\n"); 42} 43 44static 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 66static 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 88int 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 115int process_skip_chunk(int out, u32 blocks, u32 blk_sz, u32 *crc32) 116{ 117 /* len needs to be 64 bits, as the sparse file specifies the skip amount 118 * as a 32 bit value of blocks. 119 */ 120 u64 len = (u64)blocks * blk_sz; 121 122 lseek64(out, len, SEEK_CUR); 123 124 return blocks; 125} 126 127int process_crc32_chunk(int in, u32 crc32) 128{ 129 u32 file_crc32; 130 int ret; 131 132 ret = read_all(in, &file_crc32, 4); 133 if (ret != 4) { 134 fprintf(stderr, "read returned an error copying a crc32 chunk\n"); 135 exit(-1); 136 } 137 138 if (file_crc32 != crc32) { 139 fprintf(stderr, "computed crc32 of 0x%8.8x, expected 0x%8.8x\n", 140 crc32, file_crc32); 141 exit(-1); 142 } 143 144 return 0; 145} 146 147int main(int argc, char *argv[]) 148{ 149 int in; 150 int out; 151 unsigned int i; 152 sparse_header_t sparse_header; 153 chunk_header_t chunk_header; 154 u32 crc32 = 0; 155 u32 total_blocks = 0; 156 int ret; 157 158 if (argc != 3) { 159 usage(); 160 exit(-1); 161 } 162 163 if ( (copybuf = malloc(COPY_BUF_SIZE)) == 0) { 164 fprintf(stderr, "Cannot malloc copy buf\n"); 165 exit(-1); 166 } 167 168 if (strcmp(argv[1], "-") == 0) { 169 in = STDIN_FILENO; 170 } else { 171 if ((in = open(argv[1], O_RDONLY)) == 0) { 172 fprintf(stderr, "Cannot open input file %s\n", argv[1]); 173 exit(-1); 174 } 175 } 176 177 if (strcmp(argv[2], "-") == 0) { 178 out = STDOUT_FILENO; 179 } else { 180 if ((out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC)) == 0) { 181 fprintf(stderr, "Cannot open output file %s\n", argv[2]); 182 exit(-1); 183 } 184 } 185 186 ret = read_all(in, &sparse_header, sizeof(sparse_header)); 187 if (ret != sizeof(sparse_header)) { 188 fprintf(stderr, "Error reading sparse file header\n"); 189 exit(-1); 190 } 191 192 if (sparse_header.magic != SPARSE_HEADER_MAGIC) { 193 fprintf(stderr, "Bad magic\n"); 194 exit(-1); 195 } 196 197 if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) { 198 fprintf(stderr, "Unknown major version number\n"); 199 exit(-1); 200 } 201 202 if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) { 203 /* Skip the remaining bytes in a header that is longer than 204 * we expected. 205 */ 206 lseek64(in, sparse_header.file_hdr_sz - SPARSE_HEADER_LEN, SEEK_CUR); 207 } 208 209 if ( (zerobuf = malloc(sparse_header.blk_sz)) == 0) { 210 fprintf(stderr, "Cannot malloc zero buf\n"); 211 exit(-1); 212 } 213 214 for (i=0; i<sparse_header.total_chunks; i++) { 215 ret = read_all(in, &chunk_header, sizeof(chunk_header)); 216 if (ret != sizeof(chunk_header)) { 217 fprintf(stderr, "Error reading chunk header\n"); 218 exit(-1); 219 } 220 221 if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) { 222 /* Skip the remaining bytes in a header that is longer than 223 * we expected. 224 */ 225 lseek64(in, sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN, SEEK_CUR); 226 } 227 228 switch (chunk_header.chunk_type) { 229 case CHUNK_TYPE_RAW: 230 if (chunk_header.total_sz != (sparse_header.chunk_hdr_sz + 231 (chunk_header.chunk_sz * sparse_header.blk_sz)) ) { 232 fprintf(stderr, "Bogus chunk size for chunk %d, type Raw\n", i); 233 exit(-1); 234 } 235 total_blocks += process_raw_chunk(in, out, 236 chunk_header.chunk_sz, sparse_header.blk_sz, &crc32); 237 break; 238 case CHUNK_TYPE_DONT_CARE: 239 if (chunk_header.total_sz != sparse_header.chunk_hdr_sz) { 240 fprintf(stderr, "Bogus chunk size for chunk %d, type Dont Care\n", i); 241 exit(-1); 242 } 243 total_blocks += process_skip_chunk(out, 244 chunk_header.chunk_sz, sparse_header.blk_sz, &crc32); 245 break; 246 case CHUNK_TYPE_CRC32: 247 process_crc32_chunk(in, crc32); 248 break; 249 default: 250 fprintf(stderr, "Unknown chunk type 0x%4.4x\n", chunk_header.chunk_type); 251 } 252 253 } 254 255 /* If the last chunk was a skip, then the code just did a seek, but 256 * no write, and the file won't actually be the correct size. This 257 * will make the file the correct size. Make sure the offset is 258 * computed in 64 bits, and the function called can handle 64 bits. 259 */ 260 if (ftruncate64(out, (u64)total_blocks * sparse_header.blk_sz)) { 261 fprintf(stderr, "Error calling ftruncate() to set the image size\n"); 262 exit(-1); 263 } 264 265 close(in); 266 close(out); 267 268 if (sparse_header.total_blks != total_blocks) { 269 fprintf(stderr, "Wrote %d blocks, expected to write %d blocks\n", 270 total_blocks, sparse_header.total_blks); 271 exit(-1); 272 } 273 274 exit(0); 275} 276 277