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 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 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 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 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 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 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