Home | History | Annotate | Download | only in ext4_utils
      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 #include "make_ext4fs.h"
     18 #include "output_file.h"
     19 #include "ext4_utils.h"
     20 #include "allocate.h"
     21 #include "contents.h"
     22 #include "uuid.h"
     23 #include "backed_block.h"
     24 
     25 #include <dirent.h>
     26 #include <libgen.h>
     27 #include <stdio.h>
     28 #include <stdlib.h>
     29 #include <string.h>
     30 #include <unistd.h>
     31 #include <sys/stat.h>
     32 #include <sys/types.h>
     33 
     34 #ifdef ANDROID
     35 #include <private/android_filesystem_config.h>
     36 #endif
     37 
     38 /* TODO: Not implemented:
     39    Allocating blocks in the same block group as the file inode
     40    Hash or binary tree directories
     41    Special files: sockets, devices, fifos
     42  */
     43 
     44 static int filter_dot(const struct dirent *d)
     45 {
     46 	return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
     47 }
     48 
     49 static u32 build_default_directory_structure()
     50 {
     51 	u32 inode;
     52 	u32 root_inode;
     53 	struct dentry dentries = {
     54 			.filename = "lost+found",
     55 			.file_type = EXT4_FT_DIR,
     56 			.mode = S_IRWXU,
     57 			.uid = 0,
     58 			.gid = 0,
     59 			.mtime = 0,
     60 	};
     61 	root_inode = make_directory(0, 1, &dentries, 1);
     62 	inode = make_directory(root_inode, 0, NULL, 0);
     63 	*dentries.inode = inode;
     64 	inode_set_permissions(inode, dentries.mode,
     65 		dentries.uid, dentries.gid, dentries.mtime);
     66 
     67 	return root_inode;
     68 }
     69 
     70 /* Read a local directory and create the same tree in the generated filesystem.
     71    Calls itself recursively with each directory in the given directory */
     72 static u32 build_directory_structure(const char *full_path, const char *dir_path,
     73 		u32 dir_inode, int android)
     74 {
     75 	int entries = 0;
     76 	struct dentry *dentries;
     77 	struct dirent **namelist;
     78 	struct stat stat;
     79 	int ret;
     80 	int i;
     81 	u32 inode;
     82 	u32 entry_inode;
     83 	u32 dirs = 0;
     84 
     85 	entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort);
     86 	if (entries < 0) {
     87 		error_errno("scandir");
     88 		return EXT4_ALLOCATE_FAILED;
     89 	}
     90 
     91 	dentries = calloc(entries, sizeof(struct dentry));
     92 	if (dentries == NULL)
     93 		critical_error_errno("malloc");
     94 
     95 	for (i = 0; i < entries; i++) {
     96 		dentries[i].filename = strdup(namelist[i]->d_name);
     97 		if (dentries[i].filename == NULL)
     98 			critical_error_errno("strdup");
     99 
    100 		asprintf(&dentries[i].path, "%s/%s", dir_path, namelist[i]->d_name);
    101 		asprintf(&dentries[i].full_path, "%s/%s", full_path, namelist[i]->d_name);
    102 
    103 		free(namelist[i]);
    104 
    105 		ret = lstat(dentries[i].full_path, &stat);
    106 		if (ret < 0) {
    107 			error_errno("lstat");
    108 			i--;
    109 			entries--;
    110 			continue;
    111 		}
    112 
    113 		dentries[i].size = stat.st_size;
    114 		dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
    115 		dentries[i].mtime = stat.st_mtime;
    116 		if (android) {
    117 #ifdef ANDROID
    118 			unsigned int mode = 0;
    119 			unsigned int uid = 0;
    120 			unsigned int gid = 0;
    121 			int dir = S_ISDIR(stat.st_mode);
    122 			fs_config(dentries[i].path, dir, &uid, &gid, &mode);
    123 			dentries[i].mode = mode;
    124 			dentries[i].uid = uid;
    125 			dentries[i].gid = gid;
    126 #else
    127 			error("can't set android permissions - built without android support");
    128 #endif
    129 		}
    130 
    131 		if (S_ISREG(stat.st_mode)) {
    132 			dentries[i].file_type = EXT4_FT_REG_FILE;
    133 		} else if (S_ISDIR(stat.st_mode)) {
    134 			dentries[i].file_type = EXT4_FT_DIR;
    135 			dirs++;
    136 		} else if (S_ISCHR(stat.st_mode)) {
    137 			dentries[i].file_type = EXT4_FT_CHRDEV;
    138 		} else if (S_ISBLK(stat.st_mode)) {
    139 			dentries[i].file_type = EXT4_FT_BLKDEV;
    140 		} else if (S_ISFIFO(stat.st_mode)) {
    141 			dentries[i].file_type = EXT4_FT_FIFO;
    142 		} else if (S_ISSOCK(stat.st_mode)) {
    143 			dentries[i].file_type = EXT4_FT_SOCK;
    144 		} else if (S_ISLNK(stat.st_mode)) {
    145 			dentries[i].file_type = EXT4_FT_SYMLINK;
    146 			dentries[i].link = calloc(info.block_size, 1);
    147 			readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1);
    148 		} else {
    149 			error("unknown file type on %s", dentries[i].path);
    150 			i--;
    151 			entries--;
    152 		}
    153 	}
    154 	free(namelist);
    155 
    156 	inode = make_directory(dir_inode, entries, dentries, dirs);
    157 
    158 	for (i = 0; i < entries; i++) {
    159 		if (dentries[i].file_type == EXT4_FT_REG_FILE) {
    160 			entry_inode = make_file(dentries[i].full_path, dentries[i].size);
    161 		} else if (dentries[i].file_type == EXT4_FT_DIR) {
    162 			entry_inode = build_directory_structure(dentries[i].full_path,
    163 					dentries[i].path, inode, android);
    164 		} else if (dentries[i].file_type == EXT4_FT_SYMLINK) {
    165 			entry_inode = make_link(dentries[i].full_path, dentries[i].link);
    166 		} else {
    167 			error("unknown file type on %s", dentries[i].path);
    168 			entry_inode = 0;
    169 		}
    170 		*dentries[i].inode = entry_inode;
    171 
    172 		ret = inode_set_permissions(entry_inode, dentries[i].mode,
    173 			dentries[i].uid, dentries[i].gid,
    174 			dentries[i].mtime);
    175 		if (ret)
    176 			error("failed to set permissions on %s\n", dentries[i].path);
    177 
    178 		free(dentries[i].path);
    179 		free(dentries[i].full_path);
    180 		free(dentries[i].link);
    181 		free((void *)dentries[i].filename);
    182 	}
    183 
    184 	free(dentries);
    185 	return inode;
    186 }
    187 
    188 static u32 compute_block_size()
    189 {
    190 	return 4096;
    191 }
    192 
    193 static u32 compute_journal_blocks()
    194 {
    195 	u32 journal_blocks = DIV_ROUND_UP(info.len, info.block_size) / 64;
    196 	if (journal_blocks < 1024)
    197 		journal_blocks = 1024;
    198 	if (journal_blocks > 32768)
    199 		journal_blocks = 32768;
    200 	return journal_blocks;
    201 }
    202 
    203 static u32 compute_blocks_per_group()
    204 {
    205 	return info.block_size * 8;
    206 }
    207 
    208 static u32 compute_inodes()
    209 {
    210 	return DIV_ROUND_UP(info.len, info.block_size) / 4;
    211 }
    212 
    213 static u32 compute_inodes_per_group()
    214 {
    215 	u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
    216 	u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
    217 	u32 inodes = DIV_ROUND_UP(info.inodes, block_groups);
    218 	inodes = ALIGN(inodes, (info.block_size / info.inode_size));
    219 
    220 	/* After properly rounding up the number of inodes/group,
    221 	 * make sure to update the total inodes field in the info struct.
    222 	 */
    223 	info.inodes = inodes * block_groups;
    224 
    225 	return inodes;
    226 }
    227 
    228 static u32 compute_bg_desc_reserve_blocks()
    229 {
    230 	u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
    231 	u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
    232 	u32 bg_desc_blocks = DIV_ROUND_UP(block_groups * sizeof(struct ext2_group_desc),
    233 			info.block_size);
    234 
    235 	u32 bg_desc_reserve_blocks =
    236 			DIV_ROUND_UP(block_groups * 1024 * sizeof(struct ext2_group_desc),
    237 					info.block_size) - bg_desc_blocks;
    238 
    239 	if (bg_desc_reserve_blocks > info.block_size / sizeof(u32))
    240 		bg_desc_reserve_blocks = info.block_size / sizeof(u32);
    241 
    242 	return bg_desc_reserve_blocks;
    243 }
    244 
    245 void reset_ext4fs_info() {
    246     // Reset all the global data structures used by make_ext4fs so it
    247     // can be called again.
    248     memset(&info, 0, sizeof(info));
    249     memset(&aux_info, 0, sizeof(aux_info));
    250     free_data_blocks();
    251 }
    252 
    253 int make_ext4fs(const char *filename, s64 len)
    254 {
    255     reset_ext4fs_info();
    256     info.len = len;
    257     return make_ext4fs_internal(filename, NULL, NULL, 0, 0, 0, 0, 1, 0);
    258 }
    259 
    260 int make_ext4fs_internal(const char *filename, const char *directory,
    261                          char *mountpoint, int android, int gzip, int sparse,
    262                          int crc, int wipe, int init_itabs)
    263 {
    264         u32 root_inode_num;
    265         u16 root_mode;
    266 
    267 	if (setjmp(setjmp_env))
    268 		return EXIT_FAILURE; /* Handle a call to longjmp() */
    269 
    270 	if (info.len <= 0)
    271 		info.len = get_file_size(filename);
    272 
    273 	if (info.len <= 0) {
    274 		fprintf(stderr, "Need size of filesystem\n");
    275                 return EXIT_FAILURE;
    276 	}
    277 
    278 	if (info.block_size <= 0)
    279 		info.block_size = compute_block_size();
    280 
    281 	/* Round down the filesystem length to be a multiple of the block size */
    282 	info.len &= ~((u64)info.block_size - 1);
    283 
    284 	if (info.journal_blocks == 0)
    285 		info.journal_blocks = compute_journal_blocks();
    286 
    287 	if (info.no_journal == 0)
    288 		info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL;
    289 	else
    290 		info.journal_blocks = 0;
    291 
    292 	if (info.blocks_per_group <= 0)
    293 		info.blocks_per_group = compute_blocks_per_group();
    294 
    295 	if (info.inodes <= 0)
    296 		info.inodes = compute_inodes();
    297 
    298 	if (info.inode_size <= 0)
    299 		info.inode_size = 256;
    300 
    301 	if (info.label == NULL)
    302 		info.label = "";
    303 
    304 	info.inodes_per_group = compute_inodes_per_group();
    305 
    306 	info.feat_compat |=
    307 			EXT4_FEATURE_COMPAT_RESIZE_INODE;
    308 
    309 	info.feat_ro_compat |=
    310 			EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER |
    311 			EXT4_FEATURE_RO_COMPAT_LARGE_FILE;
    312 
    313 	info.feat_incompat |=
    314 			EXT4_FEATURE_INCOMPAT_EXTENTS |
    315 			EXT4_FEATURE_INCOMPAT_FILETYPE;
    316 
    317 
    318 	info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks();
    319 
    320 	printf("Creating filesystem with parameters:\n");
    321 	printf("    Size: %llu\n", info.len);
    322 	printf("    Block size: %d\n", info.block_size);
    323 	printf("    Blocks per group: %d\n", info.blocks_per_group);
    324 	printf("    Inodes per group: %d\n", info.inodes_per_group);
    325 	printf("    Inode size: %d\n", info.inode_size);
    326 	printf("    Journal blocks: %d\n", info.journal_blocks);
    327 	printf("    Label: %s\n", info.label);
    328 
    329 	ext4_create_fs_aux_info();
    330 
    331 	printf("    Blocks: %llu\n", aux_info.len_blocks);
    332 	printf("    Block groups: %d\n", aux_info.groups);
    333 	printf("    Reserved block group size: %d\n", info.bg_desc_reserve_blocks);
    334 
    335 	block_allocator_init();
    336 
    337 	ext4_fill_in_sb();
    338 
    339 	if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED)
    340 		error("failed to reserve first 10 inodes");
    341 
    342 	if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL)
    343 		ext4_create_journal_inode();
    344 
    345 	if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE)
    346 		ext4_create_resize_inode();
    347 
    348 	if (directory)
    349 		root_inode_num = build_directory_structure(directory, mountpoint, 0, android);
    350 	else
    351 		root_inode_num = build_default_directory_structure();
    352 
    353 	root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
    354 	inode_set_permissions(root_inode_num, root_mode, 0, 0, 0);
    355 
    356 	ext4_update_free();
    357 
    358 	if (init_itabs)
    359 		init_unused_inode_tables();
    360 
    361 	ext4_queue_sb();
    362 
    363 	printf("Created filesystem with %d/%d inodes and %d/%d blocks\n",
    364 			aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count,
    365 			aux_info.sb->s_inodes_count,
    366 			aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo,
    367 			aux_info.sb->s_blocks_count_lo);
    368 
    369 	write_ext4_image(filename, gzip, sparse, crc, wipe);
    370 
    371 	return 0;
    372 }
    373