blob: e69b11f93dcca5aa68187c099b9831670a69cf73 [file] [log] [blame]
Colin Crossec0a2e82010-06-11 14:21:37 -07001/*
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>
Colin Cross7a8bee72010-06-20 14:53:14 -070022#include <sys/ioctl.h>
Colin Crossec0a2e82010-06-11 14:21:37 -070023#include <limits.h>
24#include <arpa/inet.h>
25#include <fcntl.h>
Colin Crossec0a2e82010-06-11 14:21:37 -070026#include <stdlib.h>
27#include <strings.h>
28#include <string.h>
29#include <stdio.h>
30#include <dirent.h>
31#include <libgen.h>
32
Colin Cross4b83b8a2010-06-17 20:16:30 -070033#if defined(__linux__)
Colin Crossec0a2e82010-06-11 14:21:37 -070034#include <linux/fs.h>
35#elif defined(__APPLE__) && defined(__MACH__)
36#include <sys/disk.h>
37#endif
38
39#include "make_ext4fs.h"
Colin Cross7a8bee72010-06-20 14:53:14 -070040#include "output_file.h"
Colin Crossec0a2e82010-06-11 14:21:37 -070041#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
Colin Cross7a8bee72010-06-20 14:53:14 -070060 Special files: sockets, devices, fifos
Colin Crossec0a2e82010-06-11 14:21:37 -070061 */
62
63int force = 0;
64
65struct fs_info info;
66struct fs_aux_info aux_info;
67
Colin Crossec0a2e82010-06-11 14:21:37 -070068/* Write the filesystem image to a file */
Colin Cross7a8bee72010-06-20 14:53:14 -070069static void write_ext4_image(const char *filename, int gz)
Colin Crossec0a2e82010-06-11 14:21:37 -070070{
71 int ret = 0;
Colin Cross7a8bee72010-06-20 14:53:14 -070072 struct output_file *out = open_output_file(filename, gz);
Colin Crossec0a2e82010-06-11 14:21:37 -070073 off_t off;
74
Colin Cross7a8bee72010-06-20 14:53:14 -070075 if (!out)
Colin Crossec0a2e82010-06-11 14:21:37 -070076 return;
Colin Crossec0a2e82010-06-11 14:21:37 -070077
Colin Cross7a8bee72010-06-20 14:53:14 -070078 write_data_block(out, 1024, (u8*)aux_info.sb, 1024);
Colin Crossec0a2e82010-06-11 14:21:37 -070079
Colin Cross7a8bee72010-06-20 14:53:14 -070080 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);
Colin Crossec0a2e82010-06-11 14:21:37 -070083
Colin Cross7a8bee72010-06-20 14:53:14 -070084 for_each_data_block(write_data_block, write_data_file, out);
Colin Crossec0a2e82010-06-11 14:21:37 -070085
Colin Cross7a8bee72010-06-20 14:53:14 -070086 write_data_block(out, info.len - 1, (u8*)"", 1);
Colin Crossec0a2e82010-06-11 14:21:37 -070087
Colin Cross7a8bee72010-06-20 14:53:14 -070088 close_output_file(out);
Colin Crossec0a2e82010-06-11 14:21:37 -070089}
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,
Colin Cross8aef66d2010-06-20 23:22:12 -070097 info.block_size);
Colin Crossec0a2e82010-06-11 14:21:37 -070098 aux_info.groups = DIV_ROUND_UP(aux_info.len_blocks - aux_info.first_data_block,
Colin Cross8aef66d2010-06-20 23:22:12 -070099 info.blocks_per_group);
Colin Crossec0a2e82010-06-11 14:21:37 -0700100 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 =
Colin Cross8aef66d2010-06-20 23:22:12 -0700105 DIV_ROUND_UP(aux_info.groups * sizeof(struct ext2_group_desc),
106 info.block_size);
Colin Crossec0a2e82010-06-11 14:21:37 -0700107
108 aux_info.bg_desc_reserve_blocks =
Colin Cross8aef66d2010-06-20 23:22:12 -0700109 DIV_ROUND_UP(aux_info.groups * 1024 * sizeof(struct ext2_group_desc),
110 info.block_size) - aux_info.bg_desc_blocks;
Colin Crossec0a2e82010-06-11 14:21:37 -0700111
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;
Colin Cross8aef66d2010-06-20 23:22:12 -0700207 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;
Colin Crossec0a2e82010-06-11 14:21:37 -0700211 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++) {
Colin Cross8aef66d2010-06-20 23:22:12 -0700220 u64 group_start_block = aux_info.first_data_block + i *
221 info.blocks_per_group;
Colin Crossec0a2e82010-06-11 14:21:37 -0700222 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,
Colin Cross8aef66d2010-06-20 23:22:12 -0700227 aux_info.bg_desc_blocks * info.block_size,
228 group_start_block + 1);
Colin Crossec0a2e82010-06-11 14:21:37 -0700229 }
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 *
Colin Cross8aef66d2010-06-20 23:22:12 -0700258 info.blocks_per_group;
Colin Crossec0a2e82010-06-11 14:21:37 -0700259 u32 reserved_block_start = group_start_block + 1 +
Colin Cross8aef66d2010-06-20 23:22:12 -0700260 aux_info.bg_desc_blocks;
Colin Crossec0a2e82010-06-11 14:21:37 -0700261 u32 reserved_block_len = aux_info.bg_desc_reserve_blocks;
262 append_region(reserve_inode_alloc, reserved_block_start,
Colin Cross8aef66d2010-06-20 23:22:12 -0700263 reserved_block_len, i);
Colin Crossec0a2e82010-06-11 14:21:37 -0700264 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,
Colin Cross8aef66d2010-06-20 23:22:12 -0700288 info.block_size);
Colin Crossec0a2e82010-06-11 14:21:37 -0700289 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
Colin Cross8aef66d2010-06-20 23:22:12 -0700367 entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort);
Colin Crossec0a2e82010-06-11 14:21:37 -0700368 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
Colin Cross4b83b8a2010-06-17 20:16:30 -0700499#if defined(__linux__)
Colin Crossec0a2e82010-06-11 14:21:37 -0700500 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;
Colin Cross7a8bee72010-06-20 14:53:14 -0700561 int gzip = 0;
Colin Crossec0a2e82010-06-11 14:21:37 -0700562 u32 root_inode_num;
563 u16 root_mode;
564
Colin Cross7a8bee72010-06-20 14:53:14 -0700565 while ((opt = getopt(argc, argv, "l:j:b:g:i:I:L:a:fz")) != -1) {
Colin Crossec0a2e82010-06-11 14:21:37 -0700566 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;
Colin Cross7a8bee72010-06-20 14:53:14 -0700595 case 'z':
596 gzip = 1;
597 break;
Colin Crossec0a2e82010-06-11 14:21:37 -0700598 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
Colin Cross7a8bee72010-06-20 14:53:14 -0700705 write_ext4_image(filename, gzip);
Colin Crossec0a2e82010-06-11 14:21:37 -0700706
707 return 0;
708}