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