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 "ext4_utils.h"
     19 #include "allocate.h"
     20 #include "contents.h"
     21 #include "uuid.h"
     22 #include "wipe.h"
     23 
     24 #include <sparse/sparse.h>
     25 
     26 #include <assert.h>
     27 #include <dirent.h>
     28 #include <fcntl.h>
     29 #include <libgen.h>
     30 #include <stdio.h>
     31 #include <stdlib.h>
     32 #include <string.h>
     33 #include <unistd.h>
     34 #include <sys/stat.h>
     35 #include <sys/types.h>
     36 
     37 #ifdef USE_MINGW
     38 
     39 #include <winsock2.h>
     40 
     41 /* These match the Linux definitions of these flags.
     42    L_xx is defined to avoid conflicting with the win32 versions.
     43 */
     44 #define L_S_IRUSR 00400
     45 #define L_S_IWUSR 00200
     46 #define L_S_IXUSR 00100
     47 #define S_IRWXU (L_S_IRUSR | L_S_IWUSR | L_S_IXUSR)
     48 #define S_IRGRP 00040
     49 #define S_IWGRP 00020
     50 #define S_IXGRP 00010
     51 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
     52 #define S_IROTH 00004
     53 #define S_IWOTH 00002
     54 #define S_IXOTH 00001
     55 #define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
     56 #define S_ISUID 0004000
     57 #define S_ISGID 0002000
     58 #define S_ISVTX 0001000
     59 
     60 #else
     61 
     62 #include <selinux/selinux.h>
     63 #include <selinux/label.h>
     64 #include <selinux/android.h>
     65 
     66 #define O_BINARY 0
     67 
     68 #endif
     69 
     70 /* TODO: Not implemented:
     71    Allocating blocks in the same block group as the file inode
     72    Hash or binary tree directories
     73    Special files: sockets, devices, fifos
     74  */
     75 
     76 static int filter_dot(const struct dirent *d)
     77 {
     78 	return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
     79 }
     80 
     81 static u32 build_default_directory_structure()
     82 {
     83 	u32 inode;
     84 	u32 root_inode;
     85 	struct dentry dentries = {
     86 			.filename = "lost+found",
     87 			.file_type = EXT4_FT_DIR,
     88 			.mode = S_IRWXU,
     89 			.uid = 0,
     90 			.gid = 0,
     91 			.mtime = 0,
     92 	};
     93 	root_inode = make_directory(0, 1, &dentries, 1);
     94 	inode = make_directory(root_inode, 0, NULL, 0);
     95 	*dentries.inode = inode;
     96 	inode_set_permissions(inode, dentries.mode,
     97 		dentries.uid, dentries.gid, dentries.mtime);
     98 
     99 	return root_inode;
    100 }
    101 
    102 #ifndef USE_MINGW
    103 /* Read a local directory and create the same tree in the generated filesystem.
    104    Calls itself recursively with each directory in the given directory.
    105    full_path is an absolute or relative path, with a trailing slash, to the
    106    directory on disk that should be copied, or NULL if this is a directory
    107    that does not exist on disk (e.g. lost+found).
    108    dir_path is an absolute path, with trailing slash, to the same directory
    109    if the image were mounted at the specified mount point */
    110 static u32 build_directory_structure(const char *full_path, const char *dir_path,
    111 		u32 dir_inode, fs_config_func_t fs_config_func,
    112 		struct selabel_handle *sehnd, int verbose)
    113 {
    114 	int entries = 0;
    115 	struct dentry *dentries;
    116 	struct dirent **namelist = NULL;
    117 	struct stat stat;
    118 	int ret;
    119 	int i;
    120 	u32 inode;
    121 	u32 entry_inode;
    122 	u32 dirs = 0;
    123 	bool needs_lost_and_found = false;
    124 
    125 	if (full_path) {
    126 		entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort);
    127 		if (entries < 0) {
    128 			error_errno("scandir");
    129 			return EXT4_ALLOCATE_FAILED;
    130 		}
    131 	}
    132 
    133 	if (dir_inode == 0) {
    134 		/* root directory, check if lost+found already exists */
    135 		for (i = 0; i < entries; i++)
    136 			if (strcmp(namelist[i]->d_name, "lost+found") == 0)
    137 				break;
    138 		if (i == entries)
    139 			needs_lost_and_found = true;
    140 	}
    141 
    142 	dentries = calloc(entries, sizeof(struct dentry));
    143 	if (dentries == NULL)
    144 		critical_error_errno("malloc");
    145 
    146 	for (i = 0; i < entries; i++) {
    147 		dentries[i].filename = strdup(namelist[i]->d_name);
    148 		if (dentries[i].filename == NULL)
    149 			critical_error_errno("strdup");
    150 
    151 		asprintf(&dentries[i].path, "%s%s", dir_path, namelist[i]->d_name);
    152 		asprintf(&dentries[i].full_path, "%s%s", full_path, namelist[i]->d_name);
    153 
    154 		free(namelist[i]);
    155 
    156 		ret = lstat(dentries[i].full_path, &stat);
    157 		if (ret < 0) {
    158 			error_errno("lstat");
    159 			i--;
    160 			entries--;
    161 			continue;
    162 		}
    163 
    164 		dentries[i].size = stat.st_size;
    165 		dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
    166 		dentries[i].mtime = stat.st_mtime;
    167 		uint64_t capabilities;
    168 		if (fs_config_func != NULL) {
    169 #ifdef ANDROID
    170 			unsigned int mode = 0;
    171 			unsigned int uid = 0;
    172 			unsigned int gid = 0;
    173 			int dir = S_ISDIR(stat.st_mode);
    174 			fs_config_func(dentries[i].path, dir, &uid, &gid, &mode, &capabilities);
    175 			dentries[i].mode = mode;
    176 			dentries[i].uid = uid;
    177 			dentries[i].gid = gid;
    178 			dentries[i].capabilities = capabilities;
    179 #else
    180 			error("can't set android permissions - built without android support");
    181 #endif
    182 		}
    183 #ifndef USE_MINGW
    184 		if (sehnd) {
    185 			if (selabel_lookup(sehnd, &dentries[i].secon, dentries[i].path, stat.st_mode) < 0) {
    186 				error("cannot lookup security context for %s", dentries[i].path);
    187 			}
    188 
    189 			if (dentries[i].secon && verbose)
    190 				printf("Labeling %s as %s\n", dentries[i].path, dentries[i].secon);
    191 		}
    192 #endif
    193 
    194 		if (S_ISREG(stat.st_mode)) {
    195 			dentries[i].file_type = EXT4_FT_REG_FILE;
    196 		} else if (S_ISDIR(stat.st_mode)) {
    197 			dentries[i].file_type = EXT4_FT_DIR;
    198 			dirs++;
    199 		} else if (S_ISCHR(stat.st_mode)) {
    200 			dentries[i].file_type = EXT4_FT_CHRDEV;
    201 		} else if (S_ISBLK(stat.st_mode)) {
    202 			dentries[i].file_type = EXT4_FT_BLKDEV;
    203 		} else if (S_ISFIFO(stat.st_mode)) {
    204 			dentries[i].file_type = EXT4_FT_FIFO;
    205 		} else if (S_ISSOCK(stat.st_mode)) {
    206 			dentries[i].file_type = EXT4_FT_SOCK;
    207 		} else if (S_ISLNK(stat.st_mode)) {
    208 			dentries[i].file_type = EXT4_FT_SYMLINK;
    209 			dentries[i].link = calloc(info.block_size, 1);
    210 			readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1);
    211 		} else {
    212 			error("unknown file type on %s", dentries[i].path);
    213 			i--;
    214 			entries--;
    215 		}
    216 	}
    217 	free(namelist);
    218 
    219 	if (needs_lost_and_found) {
    220 		/* insert a lost+found directory at the beginning of the dentries */
    221 		struct dentry *tmp = calloc(entries + 1, sizeof(struct dentry));
    222 		memset(tmp, 0, sizeof(struct dentry));
    223 		memcpy(tmp + 1, dentries, entries * sizeof(struct dentry));
    224 		dentries = tmp;
    225 
    226 		dentries[0].filename = strdup("lost+found");
    227 		asprintf(&dentries[0].path, "%slost+found", dir_path);
    228 		dentries[0].full_path = NULL;
    229 		dentries[0].size = 0;
    230 		dentries[0].mode = S_IRWXU;
    231 		dentries[0].file_type = EXT4_FT_DIR;
    232 		dentries[0].uid = 0;
    233 		dentries[0].gid = 0;
    234 		if (sehnd) {
    235 			if (selabel_lookup(sehnd, &dentries[0].secon, dentries[0].path, dentries[0].mode) < 0)
    236 				error("cannot lookup security context for %s", dentries[0].path);
    237 		}
    238 		entries++;
    239 		dirs++;
    240 	}
    241 
    242 	inode = make_directory(dir_inode, entries, dentries, dirs);
    243 
    244 	for (i = 0; i < entries; i++) {
    245 		if (dentries[i].file_type == EXT4_FT_REG_FILE) {
    246 			entry_inode = make_file(dentries[i].full_path, dentries[i].size);
    247 		} else if (dentries[i].file_type == EXT4_FT_DIR) {
    248 			char *subdir_full_path = NULL;
    249 			char *subdir_dir_path;
    250 			if (dentries[i].full_path) {
    251 				ret = asprintf(&subdir_full_path, "%s/", dentries[i].full_path);
    252 				if (ret < 0)
    253 					critical_error_errno("asprintf");
    254 			}
    255 			ret = asprintf(&subdir_dir_path, "%s/", dentries[i].path);
    256 			if (ret < 0)
    257 				critical_error_errno("asprintf");
    258 			entry_inode = build_directory_structure(subdir_full_path,
    259 					subdir_dir_path, inode, fs_config_func, sehnd, verbose);
    260 			free(subdir_full_path);
    261 			free(subdir_dir_path);
    262 		} else if (dentries[i].file_type == EXT4_FT_SYMLINK) {
    263 			entry_inode = make_link(dentries[i].link);
    264 		} else {
    265 			error("unknown file type on %s", dentries[i].path);
    266 			entry_inode = 0;
    267 		}
    268 		*dentries[i].inode = entry_inode;
    269 
    270 		ret = inode_set_permissions(entry_inode, dentries[i].mode,
    271 			dentries[i].uid, dentries[i].gid,
    272 			dentries[i].mtime);
    273 		if (ret)
    274 			error("failed to set permissions on %s\n", dentries[i].path);
    275 
    276 		/*
    277 		 * It's important to call inode_set_selinux() before
    278 		 * inode_set_capabilities(). Extended attributes need to
    279 		 * be stored sorted order, and we guarantee this by making
    280 		 * the calls in the proper order.
    281 		 * Please see xattr_assert_sane() in contents.c
    282 		 */
    283 		ret = inode_set_selinux(entry_inode, dentries[i].secon);
    284 		if (ret)
    285 			error("failed to set SELinux context on %s\n", dentries[i].path);
    286 		ret = inode_set_capabilities(entry_inode, dentries[i].capabilities);
    287 		if (ret)
    288 			error("failed to set capability on %s\n", dentries[i].path);
    289 
    290 		free(dentries[i].path);
    291 		free(dentries[i].full_path);
    292 		free(dentries[i].link);
    293 		free((void *)dentries[i].filename);
    294 		free(dentries[i].secon);
    295 	}
    296 
    297 	free(dentries);
    298 	return inode;
    299 }
    300 #endif
    301 
    302 static u32 compute_block_size()
    303 {
    304 	return 4096;
    305 }
    306 
    307 static u32 compute_journal_blocks()
    308 {
    309 	u32 journal_blocks = DIV_ROUND_UP(info.len, info.block_size) / 64;
    310 	if (journal_blocks < 1024)
    311 		journal_blocks = 1024;
    312 	if (journal_blocks > 32768)
    313 		journal_blocks = 32768;
    314 	return journal_blocks;
    315 }
    316 
    317 static u32 compute_blocks_per_group()
    318 {
    319 	return info.block_size * 8;
    320 }
    321 
    322 static u32 compute_inodes()
    323 {
    324 	return DIV_ROUND_UP(info.len, info.block_size) / 4;
    325 }
    326 
    327 static u32 compute_inodes_per_group()
    328 {
    329 	u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
    330 	u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
    331 	u32 inodes = DIV_ROUND_UP(info.inodes, block_groups);
    332 	inodes = ALIGN(inodes, (info.block_size / info.inode_size));
    333 
    334 	/* After properly rounding up the number of inodes/group,
    335 	 * make sure to update the total inodes field in the info struct.
    336 	 */
    337 	info.inodes = inodes * block_groups;
    338 
    339 	return inodes;
    340 }
    341 
    342 static u32 compute_bg_desc_reserve_blocks()
    343 {
    344 	u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
    345 	u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
    346 	u32 bg_desc_blocks = DIV_ROUND_UP(block_groups * sizeof(struct ext2_group_desc),
    347 			info.block_size);
    348 
    349 	u32 bg_desc_reserve_blocks =
    350 			DIV_ROUND_UP(block_groups * 1024 * sizeof(struct ext2_group_desc),
    351 					info.block_size) - bg_desc_blocks;
    352 
    353 	if (bg_desc_reserve_blocks > info.block_size / sizeof(u32))
    354 		bg_desc_reserve_blocks = info.block_size / sizeof(u32);
    355 
    356 	return bg_desc_reserve_blocks;
    357 }
    358 
    359 void reset_ext4fs_info() {
    360     // Reset all the global data structures used by make_ext4fs so it
    361     // can be called again.
    362     memset(&info, 0, sizeof(info));
    363     memset(&aux_info, 0, sizeof(aux_info));
    364 
    365     if (info.sparse_file) {
    366         sparse_file_destroy(info.sparse_file);
    367         info.sparse_file = NULL;
    368     }
    369 }
    370 
    371 int make_ext4fs_sparse_fd(int fd, long long len,
    372                 const char *mountpoint, struct selabel_handle *sehnd)
    373 {
    374 	reset_ext4fs_info();
    375 	info.len = len;
    376 
    377 	return make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 1, 0, 0, sehnd, 0);
    378 }
    379 
    380 int make_ext4fs(const char *filename, long long len,
    381                 const char *mountpoint, struct selabel_handle *sehnd)
    382 {
    383 	int fd;
    384 	int status;
    385 
    386 	reset_ext4fs_info();
    387 	info.len = len;
    388 
    389 	fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
    390 	if (fd < 0) {
    391 		error_errno("open");
    392 		return EXIT_FAILURE;
    393 	}
    394 
    395 	status = make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 0, 0, 1, sehnd, 0);
    396 	close(fd);
    397 
    398 	return status;
    399 }
    400 
    401 /* return a newly-malloc'd string that is a copy of str.  The new string
    402    is guaranteed to have a trailing slash.  If absolute is true, the new string
    403    is also guaranteed to have a leading slash.
    404 */
    405 static char *canonicalize_slashes(const char *str, bool absolute)
    406 {
    407 	char *ret;
    408 	int len = strlen(str);
    409 	int newlen = len;
    410 	char *ptr;
    411 
    412 	if (len == 0 && absolute) {
    413 		return strdup("/");
    414 	}
    415 
    416 	if (str[0] != '/' && absolute) {
    417 		newlen++;
    418 	}
    419 	if (str[len - 1] != '/') {
    420 		newlen++;
    421 	}
    422 	ret = malloc(newlen + 1);
    423 	if (!ret) {
    424 		critical_error("malloc");
    425 	}
    426 
    427 	ptr = ret;
    428 	if (str[0] != '/' && absolute) {
    429 		*ptr++ = '/';
    430 	}
    431 
    432 	strcpy(ptr, str);
    433 	ptr += len;
    434 
    435 	if (str[len - 1] != '/') {
    436 		*ptr++ = '/';
    437 	}
    438 
    439 	if (ptr != ret + newlen) {
    440 		critical_error("assertion failed\n");
    441 	}
    442 
    443 	*ptr = '\0';
    444 
    445 	return ret;
    446 }
    447 
    448 static char *canonicalize_abs_slashes(const char *str)
    449 {
    450 	return canonicalize_slashes(str, true);
    451 }
    452 
    453 static char *canonicalize_rel_slashes(const char *str)
    454 {
    455 	return canonicalize_slashes(str, false);
    456 }
    457 
    458 int make_ext4fs_internal(int fd, const char *_directory,
    459                          const char *_mountpoint, fs_config_func_t fs_config_func, int gzip,
    460                          int sparse, int crc, int wipe,
    461                          struct selabel_handle *sehnd, int verbose)
    462 {
    463 	u32 root_inode_num;
    464 	u16 root_mode;
    465 	char *mountpoint;
    466 	char *directory = NULL;
    467 
    468 	if (setjmp(setjmp_env))
    469 		return EXIT_FAILURE; /* Handle a call to longjmp() */
    470 
    471 	if (_mountpoint == NULL) {
    472 		mountpoint = strdup("");
    473 	} else {
    474 		mountpoint = canonicalize_abs_slashes(_mountpoint);
    475 	}
    476 
    477 	if (_directory) {
    478 		directory = canonicalize_rel_slashes(_directory);
    479 	}
    480 
    481 	if (info.len <= 0)
    482 		info.len = get_file_size(fd);
    483 
    484 	if (info.len <= 0) {
    485 		fprintf(stderr, "Need size of filesystem\n");
    486 		return EXIT_FAILURE;
    487 	}
    488 
    489 	if (info.block_size <= 0)
    490 		info.block_size = compute_block_size();
    491 
    492 	/* Round down the filesystem length to be a multiple of the block size */
    493 	info.len &= ~((u64)info.block_size - 1);
    494 
    495 	if (info.journal_blocks == 0)
    496 		info.journal_blocks = compute_journal_blocks();
    497 
    498 	if (info.no_journal == 0)
    499 		info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL;
    500 	else
    501 		info.journal_blocks = 0;
    502 
    503 	if (info.blocks_per_group <= 0)
    504 		info.blocks_per_group = compute_blocks_per_group();
    505 
    506 	if (info.inodes <= 0)
    507 		info.inodes = compute_inodes();
    508 
    509 	if (info.inode_size <= 0)
    510 		info.inode_size = 256;
    511 
    512 	if (info.label == NULL)
    513 		info.label = "";
    514 
    515 	info.inodes_per_group = compute_inodes_per_group();
    516 
    517 	info.feat_compat |=
    518 			EXT4_FEATURE_COMPAT_RESIZE_INODE |
    519 			EXT4_FEATURE_COMPAT_EXT_ATTR;
    520 
    521 	info.feat_ro_compat |=
    522 			EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER |
    523 			EXT4_FEATURE_RO_COMPAT_LARGE_FILE |
    524 			EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
    525 
    526 	info.feat_incompat |=
    527 			EXT4_FEATURE_INCOMPAT_EXTENTS |
    528 			EXT4_FEATURE_INCOMPAT_FILETYPE;
    529 
    530 
    531 	info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks();
    532 
    533 	printf("Creating filesystem with parameters:\n");
    534 	printf("    Size: %llu\n", info.len);
    535 	printf("    Block size: %d\n", info.block_size);
    536 	printf("    Blocks per group: %d\n", info.blocks_per_group);
    537 	printf("    Inodes per group: %d\n", info.inodes_per_group);
    538 	printf("    Inode size: %d\n", info.inode_size);
    539 	printf("    Journal blocks: %d\n", info.journal_blocks);
    540 	printf("    Label: %s\n", info.label);
    541 
    542 	ext4_create_fs_aux_info();
    543 
    544 	printf("    Blocks: %llu\n", aux_info.len_blocks);
    545 	printf("    Block groups: %d\n", aux_info.groups);
    546 	printf("    Reserved block group size: %d\n", info.bg_desc_reserve_blocks);
    547 
    548 	info.sparse_file = sparse_file_new(info.block_size, info.len);
    549 
    550 	block_allocator_init();
    551 
    552 	ext4_fill_in_sb();
    553 
    554 	if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED)
    555 		error("failed to reserve first 10 inodes");
    556 
    557 	if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL)
    558 		ext4_create_journal_inode();
    559 
    560 	if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE)
    561 		ext4_create_resize_inode();
    562 
    563 #ifdef USE_MINGW
    564 	// Windows needs only 'create an empty fs image' functionality
    565 	assert(!directory);
    566 	root_inode_num = build_default_directory_structure();
    567 #else
    568 	if (directory)
    569 		root_inode_num = build_directory_structure(directory, mountpoint, 0,
    570                         fs_config_func, sehnd, verbose);
    571 	else
    572 		root_inode_num = build_default_directory_structure();
    573 #endif
    574 
    575 	root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
    576 	inode_set_permissions(root_inode_num, root_mode, 0, 0, 0);
    577 
    578 #ifndef USE_MINGW
    579 	if (sehnd) {
    580 		char *secontext = NULL;
    581 
    582 		if (selabel_lookup(sehnd, &secontext, mountpoint, S_IFDIR) < 0) {
    583 			error("cannot lookup security context for %s", mountpoint);
    584 		}
    585 		if (secontext) {
    586 			if (verbose) {
    587 				printf("Labeling %s as %s\n", mountpoint, secontext);
    588 			}
    589 			inode_set_selinux(root_inode_num, secontext);
    590 		}
    591 		freecon(secontext);
    592 	}
    593 #endif
    594 
    595 	ext4_update_free();
    596 
    597 	ext4_queue_sb();
    598 
    599 	printf("Created filesystem with %d/%d inodes and %d/%d blocks\n",
    600 			aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count,
    601 			aux_info.sb->s_inodes_count,
    602 			aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo,
    603 			aux_info.sb->s_blocks_count_lo);
    604 
    605 	if (wipe)
    606 		wipe_block_device(fd, info.len);
    607 
    608 	write_ext4_image(fd, gzip, sparse, crc);
    609 
    610 	sparse_file_destroy(info.sparse_file);
    611 	info.sparse_file = NULL;
    612 
    613 	free(mountpoint);
    614 	free(directory);
    615 
    616 	return 0;
    617 }
    618