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