make_ext4fs.c revision 8aef66d2125af8de7672a12895276802fcc1948f
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 _GNU_SOURCE 18 19#include <sys/types.h> 20#include <sys/ioctl.h> 21#include <sys/stat.h> 22#include <sys/ioctl.h> 23#include <limits.h> 24#include <arpa/inet.h> 25#include <fcntl.h> 26#include <stdlib.h> 27#include <strings.h> 28#include <string.h> 29#include <stdio.h> 30#include <dirent.h> 31#include <libgen.h> 32 33#if defined(__linux__) 34#include <linux/fs.h> 35#elif defined(__APPLE__) && defined(__MACH__) 36#include <sys/disk.h> 37#endif 38 39#include "make_ext4fs.h" 40#include "output_file.h" 41#include "ext4_utils.h" 42#include "allocate.h" 43#include "ext_utils.h" 44#include "backed_block.h" 45#include "contents.h" 46#include "extent.h" 47#include "indirect.h" 48#include "uuid.h" 49 50#include "jbd2.h" 51#include "ext4.h" 52 53#ifdef ANDROID 54#include <private/android_filesystem_config.h> 55#endif 56 57/* TODO: Not implemented: 58 Allocating blocks in the same block group as the file inode 59 Hash or binary tree directories 60 Special files: sockets, devices, fifos 61 */ 62 63int force = 0; 64 65struct fs_info info; 66struct fs_aux_info aux_info; 67 68/* Write the filesystem image to a file */ 69static void write_ext4_image(const char *filename, int gz) 70{ 71 int ret = 0; 72 struct output_file *out = open_output_file(filename, gz); 73 off_t off; 74 75 if (!out) 76 return; 77 78 write_data_block(out, 1024, (u8*)aux_info.sb, 1024); 79 80 write_data_block(out, (aux_info.first_data_block + 1) * info.block_size, 81 (u8*)aux_info.bg_desc, 82 aux_info.bg_desc_blocks * info.block_size); 83 84 for_each_data_block(write_data_block, write_data_file, out); 85 86 write_data_block(out, info.len - 1, (u8*)"", 1); 87 88 close_output_file(out); 89} 90 91/* Compute the rest of the parameters of the filesystem from the basic info */ 92static void ext4_create_fs_aux_info() 93{ 94 aux_info.first_data_block = (info.block_size > 1024) ? 0 : 1; 95 aux_info.len_blocks = info.len / info.block_size; 96 aux_info.inode_table_blocks = DIV_ROUND_UP(info.inodes_per_group * info.inode_size, 97 info.block_size); 98 aux_info.groups = DIV_ROUND_UP(aux_info.len_blocks - aux_info.first_data_block, 99 info.blocks_per_group); 100 aux_info.blocks_per_ind = info.block_size / sizeof(u32); 101 aux_info.blocks_per_dind = aux_info.blocks_per_ind * aux_info.blocks_per_ind; 102 aux_info.blocks_per_tind = aux_info.blocks_per_dind * aux_info.blocks_per_dind; 103 104 aux_info.bg_desc_blocks = 105 DIV_ROUND_UP(aux_info.groups * sizeof(struct ext2_group_desc), 106 info.block_size); 107 108 aux_info.bg_desc_reserve_blocks = 109 DIV_ROUND_UP(aux_info.groups * 1024 * sizeof(struct ext2_group_desc), 110 info.block_size) - aux_info.bg_desc_blocks; 111 112 if (aux_info.bg_desc_reserve_blocks > aux_info.blocks_per_ind) 113 aux_info.bg_desc_reserve_blocks = aux_info.blocks_per_ind; 114 115 aux_info.default_i_flags = EXT4_NOATIME_FL; 116 117 u32 last_group_size = aux_info.len_blocks % info.blocks_per_group; 118 u32 last_header_size = 2 + aux_info.inode_table_blocks; 119 if (ext4_bg_has_super_block(aux_info.groups - 1)) 120 last_header_size += 1 + aux_info.bg_desc_blocks + 121 aux_info.bg_desc_reserve_blocks; 122 if (last_group_size > 0 && last_group_size < last_header_size) { 123 aux_info.groups--; 124 aux_info.len_blocks -= last_group_size; 125 } 126 127 aux_info.sb = calloc(info.block_size, 1); 128 if (!aux_info.sb) 129 critical_error_errno("calloc"); 130 131 aux_info.bg_desc = calloc(info.block_size, aux_info.bg_desc_blocks); 132 if (!aux_info.bg_desc) 133 critical_error_errno("calloc"); 134} 135 136void ext4_free_fs_aux_info() 137{ 138 free(aux_info.sb); 139 free(aux_info.bg_desc); 140} 141 142/* Fill in the superblock memory buffer based on the filesystem parameters */ 143static void ext4_fill_in_sb() 144{ 145 unsigned int i; 146 struct ext4_super_block *sb = aux_info.sb; 147 148 sb->s_inodes_count = info.inodes_per_group * aux_info.groups; 149 sb->s_blocks_count_lo = aux_info.len_blocks; 150 sb->s_r_blocks_count_lo = 0; 151 sb->s_free_blocks_count_lo = 0; 152 sb->s_free_inodes_count = 0; 153 sb->s_first_data_block = aux_info.first_data_block; 154 sb->s_log_block_size = log_2(info.block_size / 1024); 155 sb->s_obso_log_frag_size = log_2(info.block_size / 1024); 156 sb->s_blocks_per_group = info.blocks_per_group; 157 sb->s_obso_frags_per_group = info.blocks_per_group; 158 sb->s_inodes_per_group = info.inodes_per_group; 159 sb->s_mtime = 0; 160 sb->s_wtime = 0; 161 sb->s_mnt_count = 0; 162 sb->s_max_mnt_count = 0xFFFF; 163 sb->s_magic = EXT4_SUPER_MAGIC; 164 sb->s_state = EXT4_VALID_FS; 165 sb->s_errors = EXT4_ERRORS_RO; 166 sb->s_minor_rev_level = 0; 167 sb->s_lastcheck = 0; 168 sb->s_checkinterval = 0; 169 sb->s_creator_os = EXT4_OS_LINUX; 170 sb->s_rev_level = EXT4_DYNAMIC_REV; 171 sb->s_def_resuid = EXT4_DEF_RESUID; 172 sb->s_def_resgid = EXT4_DEF_RESGID; 173 174 sb->s_first_ino = EXT4_GOOD_OLD_FIRST_INO; 175 sb->s_inode_size = info.inode_size; 176 sb->s_block_group_nr = 0; 177 sb->s_feature_compat = info.feat_compat; 178 sb->s_feature_incompat = info.feat_incompat; 179 sb->s_feature_ro_compat = info.feat_ro_compat; 180 generate_uuid("extandroid/make_ext4fs", info.label, sb->s_uuid); 181 memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name)); 182 strncpy(sb->s_volume_name, info.label, sizeof(sb->s_volume_name)); 183 memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted)); 184 sb->s_algorithm_usage_bitmap = 0; 185 186 sb->s_reserved_gdt_blocks = aux_info.bg_desc_reserve_blocks; 187 sb->s_prealloc_blocks = 0; 188 sb->s_prealloc_dir_blocks = 0; 189 190 //memcpy(sb->s_journal_uuid, sb->s_uuid, sizeof(sb->s_journal_uuid)); 191 if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL) 192 sb->s_journal_inum = EXT4_JOURNAL_INO; 193 sb->s_journal_dev = 0; 194 sb->s_last_orphan = 0; 195 sb->s_hash_seed[0] = 0; /* FIXME */ 196 sb->s_def_hash_version = DX_HASH_TEA; 197 sb->s_reserved_char_pad = EXT4_JNL_BACKUP_BLOCKS; 198 sb->s_desc_size = sizeof(struct ext2_group_desc); 199 sb->s_default_mount_opts = 0; /* FIXME */ 200 sb->s_first_meta_bg = 0; 201 sb->s_mkfs_time = 0; 202 //sb->s_jnl_blocks[17]; /* FIXME */ 203 204 sb->s_blocks_count_hi = aux_info.len_blocks >> 32; 205 sb->s_r_blocks_count_hi = 0; 206 sb->s_free_blocks_count_hi = 0; 207 sb->s_min_extra_isize = sizeof(struct ext4_inode) - 208 EXT4_GOOD_OLD_INODE_SIZE; 209 sb->s_want_extra_isize = sizeof(struct ext4_inode) - 210 EXT4_GOOD_OLD_INODE_SIZE; 211 sb->s_flags = 0; 212 sb->s_raid_stride = 0; 213 sb->s_mmp_interval = 0; 214 sb->s_mmp_block = 0; 215 sb->s_raid_stripe_width = 0; 216 sb->s_log_groups_per_flex = 0; 217 sb->s_kbytes_written = 0; 218 219 for (i = 0; i < aux_info.groups; i++) { 220 u64 group_start_block = aux_info.first_data_block + i * 221 info.blocks_per_group; 222 u32 header_size = 0; 223 if (ext4_bg_has_super_block(i)) { 224 if (i != 0) { 225 queue_data_block((u8 *)sb, info.block_size, group_start_block); 226 queue_data_block((u8 *)aux_info.bg_desc, 227 aux_info.bg_desc_blocks * info.block_size, 228 group_start_block + 1); 229 } 230 header_size = 1 + aux_info.bg_desc_blocks + aux_info.bg_desc_reserve_blocks; 231 } 232 233 aux_info.bg_desc[i].bg_block_bitmap = group_start_block + header_size; 234 aux_info.bg_desc[i].bg_inode_bitmap = group_start_block + header_size + 1; 235 aux_info.bg_desc[i].bg_inode_table = group_start_block + header_size + 2; 236 237 aux_info.bg_desc[i].bg_free_blocks_count = sb->s_blocks_per_group; 238 aux_info.bg_desc[i].bg_free_inodes_count = sb->s_inodes_per_group; 239 aux_info.bg_desc[i].bg_used_dirs_count = 0; 240 } 241} 242 243static void ext4_create_resize_inode() 244{ 245 struct block_allocation *reserve_inode_alloc = create_allocation(); 246 u32 reserve_inode_len = 0; 247 unsigned int i; 248 249 struct ext4_inode *inode = get_inode(EXT4_RESIZE_INO); 250 if (inode == NULL) { 251 error("failed to get resize inode"); 252 return; 253 } 254 255 for (i = 0; i < aux_info.groups; i++) { 256 if (ext4_bg_has_super_block(i)) { 257 u64 group_start_block = aux_info.first_data_block + i * 258 info.blocks_per_group; 259 u32 reserved_block_start = group_start_block + 1 + 260 aux_info.bg_desc_blocks; 261 u32 reserved_block_len = aux_info.bg_desc_reserve_blocks; 262 append_region(reserve_inode_alloc, reserved_block_start, 263 reserved_block_len, i); 264 reserve_inode_len += reserved_block_len; 265 } 266 } 267 268 inode_attach_resize(inode, reserve_inode_alloc); 269 270 inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR; 271 inode->i_links_count = 1; 272 273 free_alloc(reserve_inode_alloc); 274} 275 276/* Allocate the blocks to hold a journal inode and connect them to the 277 reserved journal inode */ 278static void ext4_create_journal_inode() 279{ 280 struct ext4_inode *inode = get_inode(EXT4_JOURNAL_INO); 281 if (inode == NULL) { 282 error("failed to get journal inode"); 283 return; 284 } 285 286 u8 *journal_data = inode_allocate_data_extents(inode, 287 info.journal_blocks * info.block_size, 288 info.block_size); 289 if (!journal_data) { 290 error("failed to allocate extents for journal data"); 291 return; 292 } 293 294 inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR; 295 inode->i_links_count = 1; 296 297 journal_superblock_t *jsb = (journal_superblock_t *)journal_data; 298 jsb->s_header.h_magic = htonl(JBD2_MAGIC_NUMBER); 299 jsb->s_header.h_blocktype = htonl(JBD2_SUPERBLOCK_V2); 300 jsb->s_blocksize = htonl(info.block_size); 301 jsb->s_maxlen = htonl(info.journal_blocks); 302 jsb->s_nr_users = htonl(1); 303 jsb->s_first = htonl(1); 304 jsb->s_sequence = htonl(1); 305 306 memcpy(aux_info.sb->s_jnl_blocks, &inode->i_block, sizeof(inode->i_block)); 307} 308 309/* Update the number of free blocks and inodes in the filesystem and in each 310 block group */ 311static void ext4_update_free() 312{ 313 unsigned int i; 314 315 for (i = 0; i < aux_info.groups; i++) { 316 u32 bg_free_blocks = get_free_blocks(i); 317 u32 bg_free_inodes = get_free_inodes(i); 318 319 aux_info.bg_desc[i].bg_free_blocks_count = bg_free_blocks; 320 aux_info.sb->s_free_blocks_count_lo += bg_free_blocks; 321 322 aux_info.bg_desc[i].bg_free_inodes_count = bg_free_inodes; 323 aux_info.sb->s_free_inodes_count += bg_free_inodes; 324 325 aux_info.bg_desc[i].bg_used_dirs_count += get_directories(i); 326 } 327} 328 329static int filter_dot(const struct dirent *d) 330{ 331 return (strcmp(d->d_name, "..") && strcmp(d->d_name, ".")); 332} 333 334static u32 build_default_directory_structure() 335{ 336 u32 inode; 337 u32 root_inode; 338 struct dentry dentries = { 339 .filename = "lost+found", 340 .file_type = EXT4_FT_DIR, 341 .mode = S_IRWXU, 342 .uid = 0, 343 .gid = 0 344 }; 345 root_inode = make_directory(0, 1, &dentries, 1); 346 inode = make_directory(root_inode, 0, NULL, 0); 347 *dentries.inode = inode; 348 349 return root_inode; 350} 351 352/* Read a local directory and create the same tree in the generated filesystem. 353 Calls itself recursively with each directory in the given directory */ 354static u32 build_directory_structure(const char *full_path, const char *dir_path, 355 u32 dir_inode, int android) 356{ 357 int entries = 0; 358 struct dentry *dentries; 359 struct dirent **namelist; 360 struct stat stat; 361 int ret; 362 int i; 363 u32 inode; 364 u32 entry_inode; 365 u32 dirs = 0; 366 367 entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort); 368 if (entries < 0) { 369 error_errno("scandir"); 370 return EXT4_ALLOCATE_FAILED; 371 } 372 373 dentries = calloc(entries, sizeof(struct dentry)); 374 if (dentries == NULL) 375 critical_error_errno("malloc"); 376 377 for (i = 0; i < entries; i++) { 378 dentries[i].filename = strdup(namelist[i]->d_name); 379 if (dentries[i].filename == NULL) 380 critical_error_errno("strdup"); 381 382 asprintf(&dentries[i].path, "%s/%s", dir_path, namelist[i]->d_name); 383 asprintf(&dentries[i].full_path, "%s/%s", full_path, namelist[i]->d_name); 384 385 free(namelist[i]); 386 387 ret = lstat(dentries[i].full_path, &stat); 388 if (ret < 0) { 389 error_errno("lstat"); 390 i--; 391 entries--; 392 continue; 393 } 394 395 dentries[i].size = stat.st_size; 396 dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); 397 if (android) { 398#ifdef ANDROID 399 unsigned int mode = 0; 400 unsigned int uid = 0; 401 unsigned int gid = 0; 402 int dir = S_ISDIR(stat.st_mode); 403 fs_config(dentries[i].path, dir, &uid, &gid, &mode); 404 dentries[i].mode = mode; 405 dentries[i].uid = uid; 406 dentries[i].gid = gid; 407#else 408 error("can't set android permissions - built without android support"); 409#endif 410 } 411 412 if (S_ISREG(stat.st_mode)) { 413 dentries[i].file_type = EXT4_FT_REG_FILE; 414 } else if (S_ISDIR(stat.st_mode)) { 415 dentries[i].file_type = EXT4_FT_DIR; 416 dirs++; 417 } else if (S_ISCHR(stat.st_mode)) { 418 dentries[i].file_type = EXT4_FT_CHRDEV; 419 } else if (S_ISBLK(stat.st_mode)) { 420 dentries[i].file_type = EXT4_FT_BLKDEV; 421 } else if (S_ISFIFO(stat.st_mode)) { 422 dentries[i].file_type = EXT4_FT_FIFO; 423 } else if (S_ISSOCK(stat.st_mode)) { 424 dentries[i].file_type = EXT4_FT_SOCK; 425 } else if (S_ISLNK(stat.st_mode)) { 426 dentries[i].file_type = EXT4_FT_SYMLINK; 427 dentries[i].link = calloc(info.block_size, 1); 428 readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1); 429 } else { 430 error("unknown file type on %s", dentries[i].path); 431 i--; 432 entries--; 433 } 434 } 435 free(namelist); 436 437 inode = make_directory(dir_inode, entries, dentries, dirs); 438 439 for (i = 0; i < entries; i++) { 440 if (dentries[i].file_type == EXT4_FT_REG_FILE) { 441 entry_inode = make_file(dentries[i].full_path, dentries[i].size); 442 } else if (dentries[i].file_type == EXT4_FT_DIR) { 443 entry_inode = build_directory_structure(dentries[i].full_path, 444 dentries[i].path, inode, android); 445 } else if (dentries[i].file_type == EXT4_FT_SYMLINK) { 446 entry_inode = make_link(dentries[i].full_path, dentries[i].link); 447 } else { 448 error("unknown file type on %s", dentries[i].path); 449 entry_inode = 0; 450 } 451 *dentries[i].inode = entry_inode; 452 453 ret = inode_set_permissions(entry_inode, dentries[i].mode, 454 dentries[i].uid, dentries[i].gid); 455 if (ret) 456 error("failed to set permissions on %s\n", dentries[i].path); 457 458 free(dentries[i].path); 459 free(dentries[i].full_path); 460 free(dentries[i].link); 461 free((void *)dentries[i].filename); 462 } 463 464 free(dentries); 465 return inode; 466} 467 468static u32 compute_block_size() 469{ 470 return 4096; 471} 472 473static u32 compute_blocks_per_group() 474{ 475 return info.block_size * 8; 476} 477 478static u32 compute_inodes() 479{ 480 return DIV_ROUND_UP(info.len, info.block_size) / 4; 481} 482 483static u32 compute_inodes_per_group() 484{ 485 u32 blocks = DIV_ROUND_UP(info.len, info.block_size); 486 u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group); 487 return DIV_ROUND_UP(info.inodes, block_groups); 488} 489 490static u64 get_block_device_size(const char *filename) 491{ 492 int fd = open(filename, O_RDONLY); 493 u64 size = 0; 494 int ret; 495 496 if (fd < 0) 497 return 0; 498 499#if defined(__linux__) 500 ret = ioctl(fd, BLKGETSIZE64, &size); 501#elif defined(__APPLE__) && defined(__MACH__) 502 ret = ioctl(fd, DKIOCGETBLOCKCOUNT, &size); 503#else 504 return 0; 505#endif 506 507 close(fd); 508 509 if (ret) 510 return 0; 511 512 return size; 513} 514 515static u64 get_file_size(const char *filename) 516{ 517 struct stat buf; 518 int ret; 519 520 ret = stat(filename, &buf); 521 if (ret) 522 return 0; 523 524 if (S_ISREG(buf.st_mode)) 525 return buf.st_size; 526 else if (S_ISBLK(buf.st_mode)) 527 return get_block_device_size(filename); 528 else 529 return 0; 530} 531 532static void usage(char *path) 533{ 534 fprintf(stderr, "%s [ -l <len> ] [ -j <journal size> ] [ -b <block_size> ]\n", basename(path)); 535 fprintf(stderr, " [ -g <blocks per group> ] [ -i <inodes> ] [ -I <inode size> ]\n"); 536 fprintf(stderr, " [ -L <label> ] [ -f ] [ -a <android mountpoint> ]\n"); 537 fprintf(stderr, " <filename> [<directory>]\n"); 538} 539 540static u64 parse_num(const char *arg) 541{ 542 char *endptr; 543 u64 num = strtoull(arg, &endptr, 10); 544 if (*endptr == 'k' || *endptr == 'K') 545 num *= 1024LL; 546 else if (*endptr == 'm' || *endptr == 'M') 547 num *= 1024LL * 1024LL; 548 else if (*endptr == 'g' || *endptr == 'G') 549 num *= 1024LL * 1024LL * 1024LL; 550 551 return num; 552} 553 554int main(int argc, char **argv) 555{ 556 int opt; 557 const char *filename = NULL; 558 const char *directory = NULL; 559 char *mountpoint = ""; 560 int android = 0; 561 int gzip = 0; 562 u32 root_inode_num; 563 u16 root_mode; 564 565 while ((opt = getopt(argc, argv, "l:j:b:g:i:I:L:a:fz")) != -1) { 566 switch (opt) { 567 case 'l': 568 info.len = parse_num(optarg); 569 break; 570 case 'j': 571 info.journal_blocks = parse_num(optarg); 572 break; 573 case 'b': 574 info.block_size = parse_num(optarg); 575 break; 576 case 'g': 577 info.blocks_per_group = parse_num(optarg); 578 break; 579 case 'i': 580 info.inodes = parse_num(optarg); 581 break; 582 case 'I': 583 info.inode_size = parse_num(optarg); 584 break; 585 case 'L': 586 info.label = optarg; 587 break; 588 case 'f': 589 force = 1; 590 break; 591 case 'a': 592 android = 1; 593 mountpoint = optarg; 594 break; 595 case 'z': 596 gzip = 1; 597 break; 598 default: /* '?' */ 599 usage(argv[0]); 600 exit(EXIT_FAILURE); 601 } 602 } 603 604 if (optind >= argc) { 605 fprintf(stderr, "Expected filename after options\n"); 606 usage(argv[0]); 607 exit(EXIT_FAILURE); 608 } 609 610 filename = argv[optind++]; 611 612 if (optind < argc) 613 directory = argv[optind++]; 614 615 if (optind < argc) { 616 fprintf(stderr, "Unexpected argument: %s\n", argv[optind]); 617 usage(argv[0]); 618 exit(EXIT_FAILURE); 619 } 620 621 if (info.len == 0) 622 info.len = get_file_size(filename); 623 624 if (info.len <= 0) { 625 fprintf(stderr, "Need size of filesystem\n"); 626 usage(argv[0]); 627 exit(EXIT_FAILURE); 628 } 629 630 if (info.journal_blocks > 0) 631 info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL; 632 633 if (info.block_size <= 0) 634 info.block_size = compute_block_size(); 635 636 if (info.blocks_per_group <= 0) 637 info.blocks_per_group = compute_blocks_per_group(); 638 639 if (info.inodes <= 0) 640 info.inodes = compute_inodes(); 641 642 if (info.inode_size <= 0) 643 info.inode_size = 256; 644 645 if (info.label == NULL) 646 info.label = ""; 647 648 info.inodes_per_group = compute_inodes_per_group(); 649 650 info.feat_compat |= 651 EXT4_FEATURE_COMPAT_RESIZE_INODE; 652 653 info.feat_ro_compat |= 654 EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER | 655 EXT4_FEATURE_RO_COMPAT_LARGE_FILE; 656 657 info.feat_incompat |= 658 EXT4_FEATURE_INCOMPAT_EXTENTS | 659 EXT4_FEATURE_INCOMPAT_FILETYPE; 660 661 662 printf("Creating filesystem with parameters:\n"); 663 printf(" Size: %llu\n", info.len); 664 printf(" Block size: %d\n", info.block_size); 665 printf(" Blocks per group: %d\n", info.blocks_per_group); 666 printf(" Inodes per group: %d\n", info.inodes_per_group); 667 printf(" Inode size: %d\n", info.inode_size); 668 printf(" Label: %s\n", info.label); 669 670 ext4_create_fs_aux_info(); 671 672 printf(" Blocks: %llu\n", aux_info.len_blocks); 673 printf(" Block groups: %d\n", aux_info.groups); 674 printf(" Reserved block group size: %d\n", aux_info.bg_desc_reserve_blocks); 675 676 block_allocator_init(); 677 678 ext4_fill_in_sb(); 679 680 if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED) 681 error("failed to reserve first 10 inodes"); 682 683 if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL) 684 ext4_create_journal_inode(); 685 686 if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE) 687 ext4_create_resize_inode(); 688 689 if (directory) 690 root_inode_num = build_directory_structure(directory, mountpoint, 0, android); 691 else 692 root_inode_num = build_default_directory_structure(); 693 694 root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; 695 inode_set_permissions(root_inode_num, root_mode, 0, 0); 696 697 ext4_update_free(); 698 699 printf("Created filesystem with %d/%d inodes and %d/%d blocks\n", 700 aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count, 701 aux_info.sb->s_inodes_count, 702 aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo, 703 aux_info.sb->s_blocks_count_lo); 704 705 write_ext4_image(filename, gzip); 706 707 return 0; 708} 709