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