Home | History | Annotate | Download | only in ext4_utils
      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