/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define _FILE_OFFSET_BITS 64 #define _LARGEFILE64_SOURCE 1 #include <sys/types.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/mman.h> #include <fcntl.h> #include <libgen.h> #include <unistd.h> #include <sparse/sparse.h> #include "ext4_utils.h" #include "make_ext4fs.h" #include "allocate.h" #if defined(__APPLE__) && defined(__MACH__) #define off64_t off_t #endif #ifndef USE_MINGW /* O_BINARY is windows-specific flag */ #define O_BINARY 0 #endif extern struct fs_info info; static int verbose = 0; static void usage(char *path) { fprintf(stderr, "%s [ options ] <image or block device> <output image>\n", path); fprintf(stderr, "\n"); fprintf(stderr, " -c include CRC block\n"); fprintf(stderr, " -v verbose output\n"); fprintf(stderr, " -z gzip output\n"); fprintf(stderr, " -S don't use sparse output format\n"); } static int build_sparse_ext(int fd, const char *filename) { unsigned int i; unsigned int block; int start_contiguous_block; u8 *block_bitmap; off64_t ret; block_bitmap = malloc(info.block_size); if (!block_bitmap) critical_error("failed to allocate block bitmap"); if (aux_info.first_data_block > 0) sparse_file_add_file(ext4_sparse_file, filename, 0, info.block_size * aux_info.first_data_block, 0); for (i = 0; i < aux_info.groups; i++) { u32 first_block = aux_info.first_data_block + i * info.blocks_per_group; u32 last_block = min(info.blocks_per_group, aux_info.len_blocks - first_block); ret = lseek64(fd, (u64)info.block_size * aux_info.bg_desc[i].bg_block_bitmap, SEEK_SET); if (ret < 0) critical_error_errno("failed to seek to block group bitmap %d", i); ret = read(fd, block_bitmap, info.block_size); if (ret < 0) critical_error_errno("failed to read block group bitmap %d", i); if (ret != (int)info.block_size) critical_error("failed to read all of block group bitmap %d", i); start_contiguous_block = -1; for (block = 0; block < last_block; block++) { if (start_contiguous_block >= 0) { if (!bitmap_get_bit(block_bitmap, block)) { u32 start_block = first_block + start_contiguous_block; u32 len_blocks = block - start_contiguous_block; sparse_file_add_file(ext4_sparse_file, filename, (u64)info.block_size * start_block, info.block_size * len_blocks, start_block); start_contiguous_block = -1; } } else { if (bitmap_get_bit(block_bitmap, block)) start_contiguous_block = block; } } if (start_contiguous_block >= 0) { u32 start_block = first_block + start_contiguous_block; u32 len_blocks = last_block - start_contiguous_block; sparse_file_add_file(ext4_sparse_file, filename, (u64)info.block_size * start_block, info.block_size * len_blocks, start_block); } } return 0; } int main(int argc, char **argv) { int opt; const char *in = NULL; const char *out = NULL; int gzip = 0; int sparse = 1; int infd, outfd; int crc = 0; while ((opt = getopt(argc, argv, "cvzS")) != -1) { switch (opt) { case 'c': crc = 1; break; case 'v': verbose = 1; break; case 'z': gzip = 1; break; case 'S': sparse = 0; break; } } if (optind >= argc) { fprintf(stderr, "Expected image or block device after options\n"); usage(argv[0]); exit(EXIT_FAILURE); } in = argv[optind++]; if (optind >= argc) { fprintf(stderr, "Expected output image after input image\n"); usage(argv[0]); exit(EXIT_FAILURE); } out = argv[optind++]; if (optind < argc) { fprintf(stderr, "Unexpected argument: %s\n", argv[optind]); usage(argv[0]); exit(EXIT_FAILURE); } infd = open(in, O_RDONLY); if (infd < 0) critical_error_errno("failed to open input image"); read_ext(infd, verbose); ext4_sparse_file = sparse_file_new(info.block_size, info.len); build_sparse_ext(infd, in); close(infd); if (strcmp(out, "-")) { outfd = open(out, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); if (outfd < 0) { error_errno("open"); return EXIT_FAILURE; } } else { outfd = STDOUT_FILENO; } write_ext4_image(outfd, gzip, sparse, crc); close(outfd); sparse_file_destroy(ext4_sparse_file); return 0; }