make_ext4fs.c revision ec0a2e83dc66d67addeb90e83144187691852a3e
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