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