make_ext4fs.c revision 2e5c52322d54d0f98d36b499fcaa31a0e84ca87c
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 <assert.h> 26#include <dirent.h> 27#include <fcntl.h> 28#include <libgen.h> 29#include <stdio.h> 30#include <stdlib.h> 31#include <string.h> 32#include <unistd.h> 33#include <sys/stat.h> 34#include <sys/types.h> 35 36#ifdef USE_MINGW 37 38#include <winsock2.h> 39 40/* These match the Linux definitions of these flags. 41 L_xx is defined to avoid conflicting with the win32 versions. 42*/ 43#define L_S_IRUSR 00400 44#define L_S_IWUSR 00200 45#define L_S_IXUSR 00100 46#define S_IRWXU (L_S_IRUSR | L_S_IWUSR | L_S_IXUSR) 47#define S_IRGRP 00040 48#define S_IWGRP 00020 49#define S_IXGRP 00010 50#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP) 51#define S_IROTH 00004 52#define S_IWOTH 00002 53#define S_IXOTH 00001 54#define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH) 55#define S_ISUID 0004000 56#define S_ISGID 0002000 57#define S_ISVTX 0001000 58 59#else 60 61#define O_BINARY 0 62 63#endif 64 65/* TODO: Not implemented: 66 Allocating blocks in the same block group as the file inode 67 Hash or binary tree directories 68 Special files: sockets, devices, fifos 69 */ 70 71static int filter_dot(const struct dirent *d) 72{ 73 return (strcmp(d->d_name, "..") && strcmp(d->d_name, ".")); 74} 75 76static u32 build_default_directory_structure() 77{ 78 u32 inode; 79 u32 root_inode; 80 struct dentry dentries = { 81 .filename = "lost+found", 82 .file_type = EXT4_FT_DIR, 83 .mode = S_IRWXU, 84 .uid = 0, 85 .gid = 0, 86 .mtime = 0, 87 }; 88 root_inode = make_directory(0, 1, &dentries, 1); 89 inode = make_directory(root_inode, 0, NULL, 0); 90 *dentries.inode = inode; 91 inode_set_permissions(inode, dentries.mode, 92 dentries.uid, dentries.gid, dentries.mtime); 93 94 return root_inode; 95} 96 97#ifndef USE_MINGW 98/* Read a local directory and create the same tree in the generated filesystem. 99 Calls itself recursively with each directory in the given directory */ 100static u32 build_directory_structure(const char *full_path, const char *dir_path, 101 u32 dir_inode, fs_config_func_t fs_config_func, 102 struct selabel_handle *sehnd) 103{ 104 int entries = 0; 105 struct dentry *dentries; 106 struct dirent **namelist; 107 struct stat stat; 108 int ret; 109 int i; 110 u32 inode; 111 u32 entry_inode; 112 u32 dirs = 0; 113 114 entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort); 115 if (entries < 0) { 116 error_errno("scandir"); 117 return EXT4_ALLOCATE_FAILED; 118 } 119 120 dentries = calloc(entries, sizeof(struct dentry)); 121 if (dentries == NULL) 122 critical_error_errno("malloc"); 123 124 for (i = 0; i < entries; i++) { 125 dentries[i].filename = strdup(namelist[i]->d_name); 126 if (dentries[i].filename == NULL) 127 critical_error_errno("strdup"); 128 129 asprintf(&dentries[i].path, "%s/%s", dir_path, namelist[i]->d_name); 130 asprintf(&dentries[i].full_path, "%s/%s", full_path, namelist[i]->d_name); 131 132 free(namelist[i]); 133 134 ret = lstat(dentries[i].full_path, &stat); 135 if (ret < 0) { 136 error_errno("lstat"); 137 i--; 138 entries--; 139 continue; 140 } 141 142 dentries[i].size = stat.st_size; 143 dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); 144 dentries[i].mtime = stat.st_mtime; 145 if (fs_config_func != NULL) { 146#ifdef ANDROID 147 unsigned int mode = 0; 148 unsigned int uid = 0; 149 unsigned int gid = 0; 150 int dir = S_ISDIR(stat.st_mode); 151 fs_config_func(dentries[i].path, dir, &uid, &gid, &mode); 152 dentries[i].mode = mode; 153 dentries[i].uid = uid; 154 dentries[i].gid = gid; 155#else 156 error("can't set android permissions - built without android support"); 157#endif 158 } 159#ifdef HAVE_SELINUX 160 if (sehnd) { 161 char *sepath = NULL; 162 asprintf(&sepath, "/%s", dentries[i].path); 163 if (selabel_lookup(sehnd, &dentries[i].secon, sepath, stat.st_mode) < 0) { 164 error("cannot lookup security context for %s", sepath); 165 } 166 if (dentries[i].secon) 167 printf("Labeling %s as %s\n", sepath, dentries[i].secon); 168 free(sepath); 169 } 170#endif 171 172 if (S_ISREG(stat.st_mode)) { 173 dentries[i].file_type = EXT4_FT_REG_FILE; 174 } else if (S_ISDIR(stat.st_mode)) { 175 dentries[i].file_type = EXT4_FT_DIR; 176 dirs++; 177 } else if (S_ISCHR(stat.st_mode)) { 178 dentries[i].file_type = EXT4_FT_CHRDEV; 179 } else if (S_ISBLK(stat.st_mode)) { 180 dentries[i].file_type = EXT4_FT_BLKDEV; 181 } else if (S_ISFIFO(stat.st_mode)) { 182 dentries[i].file_type = EXT4_FT_FIFO; 183 } else if (S_ISSOCK(stat.st_mode)) { 184 dentries[i].file_type = EXT4_FT_SOCK; 185 } else if (S_ISLNK(stat.st_mode)) { 186 dentries[i].file_type = EXT4_FT_SYMLINK; 187 dentries[i].link = calloc(info.block_size, 1); 188 readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1); 189 } else { 190 error("unknown file type on %s", dentries[i].path); 191 i--; 192 entries--; 193 } 194 } 195 free(namelist); 196 197 inode = make_directory(dir_inode, entries, dentries, dirs); 198 199 for (i = 0; i < entries; i++) { 200 if (dentries[i].file_type == EXT4_FT_REG_FILE) { 201 entry_inode = make_file(dentries[i].full_path, dentries[i].size); 202 } else if (dentries[i].file_type == EXT4_FT_DIR) { 203 entry_inode = build_directory_structure(dentries[i].full_path, 204 dentries[i].path, inode, fs_config_func, sehnd); 205 } else if (dentries[i].file_type == EXT4_FT_SYMLINK) { 206 entry_inode = make_link(dentries[i].full_path, dentries[i].link); 207 } else { 208 error("unknown file type on %s", dentries[i].path); 209 entry_inode = 0; 210 } 211 *dentries[i].inode = entry_inode; 212 213 ret = inode_set_permissions(entry_inode, dentries[i].mode, 214 dentries[i].uid, dentries[i].gid, 215 dentries[i].mtime); 216 if (ret) 217 error("failed to set permissions on %s\n", dentries[i].path); 218 ret = inode_set_selinux(entry_inode, dentries[i].secon); 219 if (ret) 220 error("failed to set SELinux context on %s\n", dentries[i].path); 221 222 free(dentries[i].path); 223 free(dentries[i].full_path); 224 free(dentries[i].link); 225 free((void *)dentries[i].filename); 226 free(dentries[i].secon); 227 } 228 229 free(dentries); 230 return inode; 231} 232#endif 233 234static u32 compute_block_size() 235{ 236 return 4096; 237} 238 239static u32 compute_journal_blocks() 240{ 241 u32 journal_blocks = DIV_ROUND_UP(info.len, info.block_size) / 64; 242 if (journal_blocks < 1024) 243 journal_blocks = 1024; 244 if (journal_blocks > 32768) 245 journal_blocks = 32768; 246 return journal_blocks; 247} 248 249static u32 compute_blocks_per_group() 250{ 251 return info.block_size * 8; 252} 253 254static u32 compute_inodes() 255{ 256 return DIV_ROUND_UP(info.len, info.block_size) / 4; 257} 258 259static u32 compute_inodes_per_group() 260{ 261 u32 blocks = DIV_ROUND_UP(info.len, info.block_size); 262 u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group); 263 u32 inodes = DIV_ROUND_UP(info.inodes, block_groups); 264 inodes = ALIGN(inodes, (info.block_size / info.inode_size)); 265 266 /* After properly rounding up the number of inodes/group, 267 * make sure to update the total inodes field in the info struct. 268 */ 269 info.inodes = inodes * block_groups; 270 271 return inodes; 272} 273 274static u32 compute_bg_desc_reserve_blocks() 275{ 276 u32 blocks = DIV_ROUND_UP(info.len, info.block_size); 277 u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group); 278 u32 bg_desc_blocks = DIV_ROUND_UP(block_groups * sizeof(struct ext2_group_desc), 279 info.block_size); 280 281 u32 bg_desc_reserve_blocks = 282 DIV_ROUND_UP(block_groups * 1024 * sizeof(struct ext2_group_desc), 283 info.block_size) - bg_desc_blocks; 284 285 if (bg_desc_reserve_blocks > info.block_size / sizeof(u32)) 286 bg_desc_reserve_blocks = info.block_size / sizeof(u32); 287 288 return bg_desc_reserve_blocks; 289} 290 291void reset_ext4fs_info() { 292 // Reset all the global data structures used by make_ext4fs so it 293 // can be called again. 294 memset(&info, 0, sizeof(info)); 295 memset(&aux_info, 0, sizeof(aux_info)); 296 free_data_blocks(); 297} 298 299int make_ext4fs(const char *filename, s64 len, 300 const char *mountpoint, struct selabel_handle *sehnd) 301{ 302 int fd; 303 int status; 304 305 reset_ext4fs_info(); 306 info.len = len; 307 308 fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); 309 if (fd < 0) { 310 error_errno("open"); 311 return EXIT_FAILURE; 312 } 313 314 status = make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 0, 0, 1, 0, sehnd); 315 close(fd); 316 317 return status; 318} 319 320int make_ext4fs_internal(int fd, const char *directory, 321 char *mountpoint, fs_config_func_t fs_config_func, int gzip, int sparse, 322 int crc, int wipe, int init_itabs, struct selabel_handle *sehnd) 323{ 324 u32 root_inode_num; 325 u16 root_mode; 326 327 if (setjmp(setjmp_env)) 328 return EXIT_FAILURE; /* Handle a call to longjmp() */ 329 330 if (info.len <= 0) 331 info.len = get_file_size(fd); 332 333 if (info.len <= 0) { 334 fprintf(stderr, "Need size of filesystem\n"); 335 return EXIT_FAILURE; 336 } 337 338 if (info.block_size <= 0) 339 info.block_size = compute_block_size(); 340 341 /* Round down the filesystem length to be a multiple of the block size */ 342 info.len &= ~((u64)info.block_size - 1); 343 344 if (info.journal_blocks == 0) 345 info.journal_blocks = compute_journal_blocks(); 346 347 if (info.no_journal == 0) 348 info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL; 349 else 350 info.journal_blocks = 0; 351 352 if (info.blocks_per_group <= 0) 353 info.blocks_per_group = compute_blocks_per_group(); 354 355 if (info.inodes <= 0) 356 info.inodes = compute_inodes(); 357 358 if (info.inode_size <= 0) 359 info.inode_size = 256; 360 361 if (info.label == NULL) 362 info.label = ""; 363 364 info.inodes_per_group = compute_inodes_per_group(); 365 366 info.feat_compat |= 367 EXT4_FEATURE_COMPAT_RESIZE_INODE; 368 369 info.feat_ro_compat |= 370 EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER | 371 EXT4_FEATURE_RO_COMPAT_LARGE_FILE; 372 373 info.feat_incompat |= 374 EXT4_FEATURE_INCOMPAT_EXTENTS | 375 EXT4_FEATURE_INCOMPAT_FILETYPE; 376 377 378 info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks(); 379 380 printf("Creating filesystem with parameters:\n"); 381 printf(" Size: %llu\n", info.len); 382 printf(" Block size: %d\n", info.block_size); 383 printf(" Blocks per group: %d\n", info.blocks_per_group); 384 printf(" Inodes per group: %d\n", info.inodes_per_group); 385 printf(" Inode size: %d\n", info.inode_size); 386 printf(" Journal blocks: %d\n", info.journal_blocks); 387 printf(" Label: %s\n", info.label); 388 389 ext4_create_fs_aux_info(); 390 391 printf(" Blocks: %llu\n", aux_info.len_blocks); 392 printf(" Block groups: %d\n", aux_info.groups); 393 printf(" Reserved block group size: %d\n", info.bg_desc_reserve_blocks); 394 395 block_allocator_init(); 396 397 ext4_fill_in_sb(); 398 399 if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED) 400 error("failed to reserve first 10 inodes"); 401 402 if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL) 403 ext4_create_journal_inode(); 404 405 if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE) 406 ext4_create_resize_inode(); 407 408#ifdef USE_MINGW 409 // Windows needs only 'create an empty fs image' functionality 410 assert(!directory); 411 root_inode_num = build_default_directory_structure(); 412#else 413 if (directory) 414 root_inode_num = build_directory_structure(directory, mountpoint, 0, 415 fs_config_func, sehnd); 416 else 417 root_inode_num = build_default_directory_structure(); 418#endif 419 420 root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; 421 inode_set_permissions(root_inode_num, root_mode, 0, 0, 0); 422 423#ifdef HAVE_SELINUX 424 if (sehnd) { 425 char *sepath = NULL; 426 char *secontext = NULL; 427 428 if (mountpoint[0] == '/') 429 sepath = strdup(mountpoint); 430 else 431 asprintf(&sepath, "/%s", mountpoint); 432 if (!sepath) 433 critical_error_errno("malloc"); 434 if (selabel_lookup(sehnd, &secontext, sepath, S_IFDIR) < 0) { 435 error("cannot lookup security context for %s", sepath); 436 } 437 if (secontext) { 438 printf("Labeling %s as %s\n", sepath, secontext); 439 inode_set_selinux(root_inode_num, secontext); 440 } 441 free(sepath); 442 freecon(secontext); 443 } 444#endif 445 446 ext4_update_free(); 447 448 if (init_itabs) 449 init_unused_inode_tables(); 450 451 ext4_queue_sb(); 452 453 printf("Created filesystem with %d/%d inodes and %d/%d blocks\n", 454 aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count, 455 aux_info.sb->s_inodes_count, 456 aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo, 457 aux_info.sb->s_blocks_count_lo); 458 459 write_ext4_image(fd, gzip, sparse, crc, wipe); 460 461 return 0; 462} 463