OSDN Git Service

Initial commit of ext4_utils
[android-x86/system-extras.git] / ext4_utils / make_ext4fs.c
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
64 int force = 0;
65
66 struct fs_info info;
67 struct fs_aux_info aux_info;
68
69 /* Write a contiguous region of data blocks from a memory buffer */
70 static 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 */
97 static 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 */
142 static 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 */
198 static 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
242 void 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 */
249 static 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
349 static 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 */
384 static 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 */
417 static 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
435 static int filter_dot(const struct dirent *d)
436 {
437         return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
438 }
439
440 static 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 */
460 static 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
574 static u32 compute_block_size()
575 {
576         return 4096;
577 }
578
579 static u32 compute_blocks_per_group()
580 {
581         return info.block_size * 8;
582 }
583
584 static u32 compute_inodes()
585 {
586         return DIV_ROUND_UP(info.len, info.block_size) / 4;
587 }
588
589 static 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
596 static 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
621 static 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
638 static 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
646 static 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
660 int 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 }