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#define _FILE_OFFSET_BITS 64 18#define _LARGEFILE64_SOURCE 1 19 20#include <sys/types.h> 21#include <sys/stat.h> 22#include <sys/types.h> 23#include <sys/mman.h> 24#include <fcntl.h> 25#include <libgen.h> 26#include <unistd.h> 27 28#include <sparse/sparse.h> 29 30#include "ext4_utils.h" 31#include "make_ext4fs.h" 32#include "allocate.h" 33 34#if defined(__APPLE__) && defined(__MACH__) 35#define off64_t off_t 36#endif 37 38#ifndef USE_MINGW /* O_BINARY is windows-specific flag */ 39#define O_BINARY 0 40#endif 41 42extern struct fs_info info; 43 44static int verbose = 0; 45 46static void usage(char *path) 47{ 48 fprintf(stderr, "%s [ options ] <image or block device> <output image>\n", path); 49 fprintf(stderr, "\n"); 50 fprintf(stderr, " -c include CRC block\n"); 51 fprintf(stderr, " -v verbose output\n"); 52 fprintf(stderr, " -z gzip output\n"); 53 fprintf(stderr, " -S don't use sparse output format\n"); 54} 55 56static int read_ext(int fd) 57{ 58 off64_t ret; 59 struct ext4_super_block sb; 60 61 ret = lseek64(fd, 1024, SEEK_SET); 62 if (ret < 0) 63 critical_error_errno("failed to seek to superblock"); 64 65 ret = read(fd, &sb, sizeof(sb)); 66 if (ret < 0) 67 critical_error_errno("failed to read superblock"); 68 if (ret != sizeof(sb)) 69 critical_error("failed to read all of superblock"); 70 71 ext4_parse_sb(&sb); 72 73 ret = lseek64(fd, info.len, SEEK_SET); 74 if (ret < 0) 75 critical_error_errno("failed to seek to end of input image"); 76 77 ret = lseek64(fd, info.block_size * (aux_info.first_data_block + 1), SEEK_SET); 78 if (ret < 0) 79 critical_error_errno("failed to seek to block group descriptors"); 80 81 ret = read(fd, aux_info.bg_desc, info.block_size * aux_info.bg_desc_blocks); 82 if (ret < 0) 83 critical_error_errno("failed to read block group descriptors"); 84 if (ret != (int)info.block_size * (int)aux_info.bg_desc_blocks) 85 critical_error("failed to read all of block group descriptors"); 86 87 if (verbose) { 88 printf("Found filesystem with parameters:\n"); 89 printf(" Size: %llu\n", info.len); 90 printf(" Block size: %d\n", info.block_size); 91 printf(" Blocks per group: %d\n", info.blocks_per_group); 92 printf(" Inodes per group: %d\n", info.inodes_per_group); 93 printf(" Inode size: %d\n", info.inode_size); 94 printf(" Label: %s\n", info.label); 95 printf(" Blocks: %llu\n", aux_info.len_blocks); 96 printf(" Block groups: %d\n", aux_info.groups); 97 printf(" Reserved block group size: %d\n", info.bg_desc_reserve_blocks); 98 printf(" Used %d/%d inodes and %d/%d blocks\n", 99 aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count, 100 aux_info.sb->s_inodes_count, 101 aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo, 102 aux_info.sb->s_blocks_count_lo); 103 } 104 105 return 0; 106} 107 108static int bitmap_get_bit(u8 *bitmap, u32 bit) 109{ 110 if (bitmap[bit / 8] & 1 << (bit % 8)) 111 return 1; 112 113 return 0; 114} 115 116static int build_sparse_ext(int fd, const char *filename) 117{ 118 unsigned int i; 119 unsigned int block; 120 int start_contiguous_block; 121 u8 *block_bitmap; 122 off64_t ret; 123 124 block_bitmap = malloc(info.block_size); 125 if (!block_bitmap) 126 critical_error("failed to allocate block bitmap"); 127 128 if (aux_info.first_data_block > 0) 129 sparse_file_add_file(info.sparse_file, filename, 0, 130 info.block_size * aux_info.first_data_block, 0); 131 132 for (i = 0; i < aux_info.groups; i++) { 133 u32 first_block = aux_info.first_data_block + i * info.blocks_per_group; 134 u32 last_block = min(info.blocks_per_group, aux_info.len_blocks - first_block); 135 136 ret = lseek64(fd, (u64)info.block_size * aux_info.bg_desc[i].bg_block_bitmap, 137 SEEK_SET); 138 if (ret < 0) 139 critical_error_errno("failed to seek to block group bitmap %d", i); 140 141 ret = read(fd, block_bitmap, info.block_size); 142 if (ret < 0) 143 critical_error_errno("failed to read block group bitmap %d", i); 144 if (ret != (int)info.block_size) 145 critical_error("failed to read all of block group bitmap %d", i); 146 147 start_contiguous_block = -1; 148 for (block = 0; block < last_block; block++) { 149 if (start_contiguous_block >= 0) { 150 if (!bitmap_get_bit(block_bitmap, block)) { 151 u32 start_block = first_block + start_contiguous_block; 152 u32 len_blocks = block - start_contiguous_block; 153 154 sparse_file_add_file(info.sparse_file, filename, 155 (u64)info.block_size * start_block, 156 info.block_size * len_blocks, start_block); 157 start_contiguous_block = -1; 158 } 159 } else { 160 if (bitmap_get_bit(block_bitmap, block)) 161 start_contiguous_block = block; 162 } 163 } 164 165 if (start_contiguous_block >= 0) { 166 u32 start_block = first_block + start_contiguous_block; 167 u32 len_blocks = last_block - start_contiguous_block; 168 sparse_file_add_file(info.sparse_file, filename, 169 (u64)info.block_size * start_block, 170 info.block_size * len_blocks, start_block); 171 } 172 } 173 174 return 0; 175} 176 177int main(int argc, char **argv) 178{ 179 int opt; 180 const char *in = NULL; 181 const char *out = NULL; 182 int gzip = 0; 183 int sparse = 1; 184 int infd, outfd; 185 int crc = 0; 186 187 while ((opt = getopt(argc, argv, "cvzS")) != -1) { 188 switch (opt) { 189 case 'c': 190 crc = 1; 191 break; 192 case 'v': 193 verbose = 1; 194 break; 195 case 'z': 196 gzip = 1; 197 break; 198 case 'S': 199 sparse = 0; 200 break; 201 } 202 } 203 204 if (optind >= argc) { 205 fprintf(stderr, "Expected image or block device after options\n"); 206 usage(argv[0]); 207 exit(EXIT_FAILURE); 208 } 209 210 in = argv[optind++]; 211 212 if (optind >= argc) { 213 fprintf(stderr, "Expected output image after input image\n"); 214 usage(argv[0]); 215 exit(EXIT_FAILURE); 216 } 217 218 out = argv[optind++]; 219 220 if (optind < argc) { 221 fprintf(stderr, "Unexpected argument: %s\n", argv[optind]); 222 usage(argv[0]); 223 exit(EXIT_FAILURE); 224 } 225 226 infd = open(in, O_RDONLY); 227 228 if (infd < 0) 229 critical_error_errno("failed to open input image"); 230 231 read_ext(infd); 232 233 info.sparse_file = sparse_file_new(info.block_size, info.len); 234 235 build_sparse_ext(infd, in); 236 237 close(infd); 238 239 if (strcmp(out, "-")) { 240 outfd = open(out, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); 241 if (outfd < 0) { 242 error_errno("open"); 243 return EXIT_FAILURE; 244 } 245 } else { 246 outfd = STDOUT_FILENO; 247 } 248 249 write_ext4_image(outfd, gzip, sparse, crc); 250 close(outfd); 251 252 sparse_file_destroy(info.sparse_file); 253 254 return 0; 255} 256