struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc) { int ret; sparse_header_t sparse_header; int64_t len; struct sparse_file *s; ret = read_all(fd, &sparse_header, sizeof(sparse_header)); if (ret < 0) { verbose_error(verbose, ret, "header"); return NULL; } if (sparse_header.magic != SPARSE_HEADER_MAGIC) { verbose_error(verbose, -EINVAL, "header magic"); return NULL; } if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) { verbose_error(verbose, -EINVAL, "header major version"); return NULL; } if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) { return NULL; } if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) { return NULL; } len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz; s = sparse_file_new(sparse_header.blk_sz, len); if (!s) { verbose_error(verbose, -EINVAL, NULL); return NULL; } ret = lseek64(fd, 0, SEEK_SET); if (ret < 0) { verbose_error(verbose, ret, "seeking"); sparse_file_destroy(s); return NULL; } s->verbose = verbose; ret = sparse_file_read(s, fd, true, crc); if (ret < 0) { sparse_file_destroy(s); return NULL; } return s; }
struct sparse_file *sparse_file_import_auto(int fd, bool crc, bool verbose) { struct sparse_file *s; int64_t len; int ret; s = sparse_file_import(fd, verbose, crc); if (s) { return s; } len = lseek64(fd, 0, SEEK_END); if (len < 0) { return NULL; } lseek64(fd, 0, SEEK_SET); s = sparse_file_new(4096, len); if (!s) { return NULL; } ret = sparse_file_read_normal(s, fd); if (ret < 0) { sparse_file_destroy(s); return NULL; } return s; }
static void reset_f2fs_info() { // Reset all the global data structures used by make_f2fs so it // can be called again. memset(&config, 0, sizeof(config)); config.fd = -1; if (f2fs_sparse_file) { sparse_file_destroy(f2fs_sparse_file); f2fs_sparse_file = NULL; } }
void reset_ext4fs_info() { // Reset all the global data structures used by make_ext4fs so it // can be called again. memset(&info, 0, sizeof(info)); memset(&aux_info, 0, sizeof(aux_info)); if (info.sparse_file) { sparse_file_destroy(info.sparse_file); info.sparse_file = NULL; } }
int main(int argc, char *argv[]) { int in; int out; int i; int ret; struct sparse_file *s; if (argc < 3) { usage(); exit(-1); } out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664); if (out < 0) { fprintf(stderr, "Cannot open output file %s\n", argv[argc - 1]); exit(-1); } for (i = 1; i < argc - 1; i++) { if (strcmp(argv[i], "-") == 0) { in = STDIN_FILENO; } else { in = open(argv[i], O_RDONLY | O_BINARY); if (in < 0) { fprintf(stderr, "Cannot open input file %s\n", argv[i]); exit(-1); } } s = sparse_file_import(in, true, false); if (!s) { fprintf(stderr, "Failed to read sparse file\n"); exit(-1); } lseek(out, SEEK_SET, 0); ret = sparse_file_write(s, out, false, false, false); if (ret < 0) { fprintf(stderr, "Cannot write output file\n"); exit(-1); } sparse_file_destroy(s); close(in); } close(out); exit(0); }
bool generate_verity_tree(const std::string& data_filename, const std::string& verity_filename, HashTreeBuilder* builder, const std::vector<unsigned char>& salt_content, size_t block_size, bool sparse, bool verbose) { android::base::unique_fd data_fd(open(data_filename.c_str(), O_RDONLY)); if (data_fd == -1) { PLOG(ERROR) << "failed to open " << data_filename; return false; } struct sparse_file* file; if (sparse) { file = sparse_file_import(data_fd, false, false); } else { file = sparse_file_import_auto(data_fd, false, verbose); } if (!file) { LOG(ERROR) << "failed to read file " << data_filename; return false; } int64_t len = sparse_file_len(file, false, false); if (len % block_size != 0) { LOG(ERROR) << "file size " << len << " is not a multiple of " << block_size << " byte"; return false; } // Initialize the builder to compute the hash tree. if (!builder->Initialize(len, salt_content)) { LOG(ERROR) << "Failed to initialize HashTreeBuilder"; return false; } auto hash_callback = [](void* priv, const void* data, size_t len) { auto sparse_hasher = static_cast<HashTreeBuilder*>(priv); return sparse_hasher->Update(static_cast<const unsigned char*>(data), len) ? 0 : 1; }; sparse_file_callback(file, false, false, hash_callback, builder); sparse_file_destroy(file); if (!builder->BuildHashTree()) { return false; } return builder->WriteHashTreeToFile(verity_filename); }
int sparse_file_resparse(struct sparse_file *in_s, unsigned int max_len, struct sparse_file **out_s, int out_s_count) { struct backed_block *bb; unsigned int overhead; struct sparse_file *s; struct sparse_file *tmp; int c = 0; tmp = sparse_file_new(in_s->block_size, in_s->len); if (!tmp) { return -ENOMEM; } do { s = sparse_file_new(in_s->block_size, in_s->len); bb = move_chunks_up_to_len(in_s, s, max_len); if (c < out_s_count) { out_s[c] = s; } else { backed_block_list_move(s->backed_block_list, tmp->backed_block_list, NULL, NULL); sparse_file_destroy(s); } c++; } while (bb); backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list, NULL, NULL); sparse_file_destroy(tmp); return c; }
int main(int argc, char* argv[]) { if (argc != 4 || strcmp(argv[2], "-mincrypt") != 0) { printf("Usage: %s <image> -mincrypt <verity_key>\n" " image the image file (raw or sparse image) to be verified\n" " verity_key the verity key in mincrypt format (/verity_key on device)\n", argv[0]); return 2; } // Get the raw image. android::base::unique_fd fd(open(argv[1], O_RDONLY)); if (!fd) { fprintf(stderr, "failed to open %s: %s\n", argv[1], strerror(errno)); return 1; } struct sparse_file* file = sparse_file_import_auto(fd, false, false); if (file == nullptr) { fprintf(stderr, "failed to read file %s\n", argv[1]); return 1; } TemporaryFile tf; if (sparse_file_write(file, tf.fd, false, false, false) < 0) { fprintf(stderr, "failed to write output file\n"); return 1; } sparse_file_destroy(file); // Verify. fec::io input(tf.path); if (!input) { return 1; } fec_verity_metadata verity; if (!input.get_verity_metadata(verity)) { fprintf(stderr, "failed to get verity metadata\n"); return 1; } int ret = verify_table(argv[3], verity.signature, sizeof(verity.signature), verity.table, verity.table_length); printf("%s\n", ret == 0 ? "VERIFIED" : "FAILED"); return ret; }
int make_f2fs_sparse_fd(int fd, long long len, const char *mountpoint, struct selabel_handle *sehnd) { if (dlopenf2fs() < 0) { return -1; } reset_f2fs_info(); f2fs_init_configuration(&config); len &= ~((__u64)F2FS_BLKSIZE); config.total_sectors = len / config.sector_size; config.start_sector = 0; f2fs_sparse_file = sparse_file_new(F2FS_BLKSIZE, len); f2fs_format_device(); sparse_file_write(f2fs_sparse_file, fd, /*gzip*/0, /*sparse*/1, /*crc*/0); sparse_file_destroy(f2fs_sparse_file); flush_sparse_buffs(); f2fs_sparse_file = NULL; return 0; }
int make_ext4fs_internal(int fd, const char *directory, char *mountpoint, fs_config_func_t fs_config_func, int gzip, int sparse, int crc, int wipe, int init_itabs, struct selabel_handle *sehnd) { u32 root_inode_num; u16 root_mode; if (setjmp(setjmp_env)) return EXIT_FAILURE; /* Handle a call to longjmp() */ if (info.len <= 0) info.len = get_file_size(fd); if (info.len <= 0) { fprintf(stderr, "Need size of filesystem\n"); return EXIT_FAILURE; } if (info.block_size <= 0) info.block_size = compute_block_size(); /* Round down the filesystem length to be a multiple of the block size */ info.len &= ~((u64)info.block_size - 1); if (info.journal_blocks == 0) info.journal_blocks = compute_journal_blocks(); if (info.no_journal == 0) info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL; else info.journal_blocks = 0; if (info.blocks_per_group <= 0) info.blocks_per_group = compute_blocks_per_group(); if (info.inodes <= 0) info.inodes = compute_inodes(); if (info.inode_size <= 0) info.inode_size = 256; if (info.label == NULL) info.label = ""; info.inodes_per_group = compute_inodes_per_group(); info.feat_compat |= EXT4_FEATURE_COMPAT_RESIZE_INODE; info.feat_ro_compat |= EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER | EXT4_FEATURE_RO_COMPAT_LARGE_FILE; info.feat_incompat |= EXT4_FEATURE_INCOMPAT_EXTENTS | EXT4_FEATURE_INCOMPAT_FILETYPE; info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks(); printf("Creating filesystem with parameters:\n"); printf(" Size: %llu\n", info.len); printf(" Block size: %d\n", info.block_size); printf(" Blocks per group: %d\n", info.blocks_per_group); printf(" Inodes per group: %d\n", info.inodes_per_group); printf(" Inode size: %d\n", info.inode_size); printf(" Journal blocks: %d\n", info.journal_blocks); printf(" Label: %s\n", info.label); ext4_create_fs_aux_info(); printf(" Blocks: %llu\n", aux_info.len_blocks); printf(" Block groups: %d\n", aux_info.groups); printf(" Reserved block group size: %d\n", info.bg_desc_reserve_blocks); info.sparse_file = sparse_file_new(info.block_size, info.len); block_allocator_init(); ext4_fill_in_sb(); MTK_add_mountpoint(aux_info.sb,mountpoint); if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED) error("failed to reserve first 10 inodes"); if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL) ext4_create_journal_inode(); if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE) ext4_create_resize_inode(); #ifdef USE_MINGW // Windows needs only 'create an empty fs image' functionality assert(!directory); root_inode_num = build_default_directory_structure(); #else if (directory) root_inode_num = build_directory_structure(directory, mountpoint, 0, fs_config_func, sehnd); else root_inode_num = build_default_directory_structure(); #endif root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; inode_set_permissions(root_inode_num, root_mode, 0, 0, 0); #ifdef HAVE_SELINUX if (sehnd) { char *sepath = NULL; char *secontext = NULL; if (mountpoint[0] == '/') sepath = strdup(mountpoint); else asprintf(&sepath, "/%s", mountpoint); if (!sepath) critical_error_errno("malloc"); if (selabel_lookup(sehnd, &secontext, sepath, S_IFDIR) < 0) { error("cannot lookup security context for %s", sepath); } if (secontext) { printf("Labeling %s as %s\n", sepath, secontext); inode_set_selinux(root_inode_num, secontext); } free(sepath); freecon(secontext); } #endif ext4_update_free(); if (init_itabs) init_unused_inode_tables(); ext4_queue_sb(); printf("Created filesystem with %d/%d inodes and %d/%d blocks\n", aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count, aux_info.sb->s_inodes_count, aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo, aux_info.sb->s_blocks_count_lo); if (wipe) wipe_block_device(fd, info.len); write_ext4_image(fd, gzip, sparse, crc); sparse_file_destroy(info.sparse_file); info.sparse_file = NULL; return 0; }
int make_ext4fs_internal(int fd, const char *_directory, fs_config_func_t fs_config_func, int gzip, int sparse, int crc, int wipe, int verbose, time_t fixed_time, FILE* block_list_file) { u32 root_inode_num; u16 root_mode; char *directory = NULL; if (setjmp(setjmp_env)) return EXIT_FAILURE; /* Handle a call to longjmp() */ if (_directory == NULL) { fprintf(stderr, "Need a source directory\n"); return EXIT_FAILURE; } directory = canonicalize_rel_slashes(_directory); if (info.len <= 0) info.len = get_file_size(fd); if (info.len <= 0) { fprintf(stderr, "Need size of filesystem\n"); return EXIT_FAILURE; } if (info.block_size <= 0) info.block_size = compute_block_size(); /* Round down the filesystem length to be a multiple of the block size */ info.len &= ~((u64)info.block_size - 1); if (info.journal_blocks == 0) info.journal_blocks = compute_journal_blocks(); if (info.no_journal == 0) info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL; else info.journal_blocks = 0; if (info.blocks_per_group <= 0) info.blocks_per_group = compute_blocks_per_group(); if (info.inodes <= 0) info.inodes = compute_inodes(); if (info.inode_size <= 0) info.inode_size = 256; if (info.label == NULL) info.label = ""; info.inodes_per_group = compute_inodes_per_group(); info.feat_compat |= EXT4_FEATURE_COMPAT_RESIZE_INODE | EXT4_FEATURE_COMPAT_EXT_ATTR; info.feat_ro_compat |= EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER | EXT4_FEATURE_RO_COMPAT_LARGE_FILE | EXT4_FEATURE_RO_COMPAT_GDT_CSUM; info.feat_incompat |= EXT4_FEATURE_INCOMPAT_EXTENTS | EXT4_FEATURE_INCOMPAT_FILETYPE; info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks(); printf("Creating filesystem with parameters:\n"); printf(" Size: %"PRIu64"\n", info.len); printf(" Block size: %d\n", info.block_size); printf(" Blocks per group: %d\n", info.blocks_per_group); printf(" Inodes per group: %d\n", info.inodes_per_group); printf(" Inode size: %d\n", info.inode_size); printf(" Journal blocks: %d\n", info.journal_blocks); printf(" Label: %s\n", info.label); ext4_create_fs_aux_info(); printf(" Blocks: %"PRIu64"\n", aux_info.len_blocks); printf(" Block groups: %d\n", aux_info.groups); printf(" Reserved blocks: %"PRIu64"\n", (aux_info.len_blocks / 100) * info.reserve_pcnt); printf(" Reserved block group size: %d\n", info.bg_desc_reserve_blocks); ext4_sparse_file = sparse_file_new(info.block_size, info.len); block_allocator_init(); ext4_fill_in_sb(); if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED) error("failed to reserve first 10 inodes"); if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL) ext4_create_journal_inode(); if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE) ext4_create_resize_inode(); root_inode_num = build_directory_structure(directory, "", 0, fs_config_func, verbose, fixed_time); root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; inode_set_permissions(root_inode_num, root_mode, 0, 0, 0); ext4_update_free(); ext4_queue_sb(); if (block_list_file) { size_t dirlen = strlen(directory); struct block_allocation* p = get_saved_allocation_chain(); while (p) { if (strncmp(p->filename, directory, dirlen) == 0) { fprintf(block_list_file, "%s", p->filename + dirlen); } else { fprintf(block_list_file, "%s", p->filename); } print_blocks(block_list_file, p); struct block_allocation* pn = p->next; free_alloc(p); p = pn; } } printf("Created filesystem with %d/%d inodes and %d/%d blocks\n", aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count, aux_info.sb->s_inodes_count, aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo, aux_info.sb->s_blocks_count_lo); if (wipe && WIPE_IS_SUPPORTED) { wipe_block_device(fd, info.len); } write_ext4_image(fd, gzip, sparse, crc); sparse_file_destroy(ext4_sparse_file); ext4_sparse_file = NULL; free(directory); return 0; }
int main(int argc, char **argv) { int opt; const char *in = NULL; const char *out = NULL; int gzip = 0; int sparse = 1; int infd, outfd; int crc = 0; while ((opt = getopt(argc, argv, "cvzS")) != -1) { switch (opt) { case 'c': crc = 1; break; case 'v': verbose = 1; break; case 'z': gzip = 1; break; case 'S': sparse = 0; break; } } if (optind >= argc) { fprintf(stderr, "Expected image or block device after options\n"); usage(argv[0]); exit(EXIT_FAILURE); } in = argv[optind++]; if (optind >= argc) { fprintf(stderr, "Expected output image after input image\n"); usage(argv[0]); exit(EXIT_FAILURE); } out = argv[optind++]; if (optind < argc) { fprintf(stderr, "Unexpected argument: %s\n", argv[optind]); usage(argv[0]); exit(EXIT_FAILURE); } infd = open(in, O_RDONLY); if (infd < 0) critical_error_errno("failed to open input image"); read_ext(infd); info.sparse_file = sparse_file_new(info.block_size, info.len); build_sparse_ext(infd, in); close(infd); if (strcmp(out, "-")) { outfd = open(out, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); if (outfd < 0) { error_errno("open"); return EXIT_FAILURE; } } else { outfd = STDOUT_FILENO; } write_ext4_image(outfd, gzip, sparse, crc); close(outfd); sparse_file_destroy(info.sparse_file); return 0; }
bool handle_command(Socket *client, std::string cmd, std::vector<std::string> args) { const char *trampfile = "/data/misc/fastbootd/mid.bin"; if (cmd == "getvar") { if (args[0] == "max-download-size") { return send_reply(client, "OKAY", "%d", kMaxDownloadSize); } else if (args[0] == "partition-type") { for (size_t i = 0; i < sizeof(part_info) / sizeof(part_info[0]); i++) { if (args[1] == part_info[i].name) { return send_reply(client, "OKAY", part_info[i].type); } } } else if (args[0] == "product") { char property[PROPERTY_VALUE_MAX]; property_get("ro.product.board", property, ""); return send_reply(client, "OKAY", property); } else if (args[0] == "serialno") { char property[PROPERTY_VALUE_MAX]; property_get("ro.serialno", property, ""); return send_reply(client, "OKAY", property); } else if (args[0] == "version-bootloader") { return send_reply(client, "OKAY", "0.1"); } return send_reply(client, "OKAY", ""); } else if (cmd == "download") { uint32_t size = strtol(args[0].c_str(), 0, 16); send_reply(client, "DATA", "%08x", size); int fd = open(trampfile, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd < 0) { send_reply(client, "FAIL", "fail to create trampoline file to store data!"); return false; } while (size > 0) { char buffer[4096]; ssize_t read = client->Receive(buffer, 8, 0); if (read != 8) { send_reply(client, "FAIL", "fail to receive data!"); close(fd); return false; } size_t length = ExtractMessageLength(buffer); do { read = client->Receive(buffer, std::min(length, sizeof(buffer)), 0); if (read < 0) { close(fd); return false; } write(fd, buffer, read); length -= read; size -= read; } while (length > 0); } close(fd); return send_reply(client, "OKAY", ""); } else if (cmd == "flash") { std::unique_ptr<char, int (*)(const char *)> tmpfile((char *)trampfile, unlink); int fd = open(tmpfile.get(), O_RDONLY); if (fd < 0) { send_reply(client, "FAIL", "please run download command first!"); return false; } const char *devname = NULL; const char *partname = NULL; for (size_t i = 0; i < sizeof(part_info) / sizeof(part_info[0]); i++) { if (args[0] == part_info[i].name) { devname = part_info[i].device; partname = part_info[i].name; break; } } if (devname == NULL) { close(fd); send_reply(client, "FAIL", "partition: %s does not exist!", args[0].c_str()); return false; } if (!strcmp("boot", partname)) { close(fd); return handle_command_flash_boot_partition(client, tmpfile.get(), devname); } int fddev = open(devname, O_WRONLY | O_CREAT, 0600); if (fddev < 0) { close(fd); send_reply(client, "FAIL", "failed to open partition: %s", args[0].c_str()); return false; } struct sparse_file *s = sparse_file_import(fd, true, false); if (!s) { close(fd); close(fddev); send_reply(client, "FAIL", "failed to read sparse file!"); return false; } sparse_file_write(s, fddev, false, false, false); sparse_file_destroy(s); close(fd); close(fddev); sync(); return send_reply(client, "OKAY", ""); } else if (cmd == "erase") { const char *devname = NULL; for (size_t i = 0; i < sizeof(part_info) / sizeof(part_info[0]); i++) { if (args[0] == part_info[i].name) { devname = part_info[i].device; break; } } if (devname == NULL) { send_reply(client, "FAIL", "partition: %s does not exist!", args[0].c_str()); return false; } uint64_t devsize = 0; int fd = open(devname, O_RDONLY); ioctl(fd, BLKGETSIZE64, &devsize); const uint64_t blksize = 64 * 1024; const uint64_t numblk = (devsize + blksize - 1) / blksize; const uint64_t updsize = (numblk / 10) * blksize; for (uint64_t offset = 0; offset < devsize; offset += updsize) { uint64_t realsize = std::min(updsize, devsize - offset); const char *argv[] = { "/system/bin/dd", "if=/dev/zero", android::base::StringPrintf("of=%s", devname).c_str(), android::base::StringPrintf("seek=%lld", offset).c_str(), android::base::StringPrintf("bs=%lld", realsize).c_str(), "count=1", }; int status; android_fork_execvp(sizeof(argv) / sizeof(argv[0]), (char **)argv, &status, true, true); send_reply(client, "INFO", android::base::StringPrintf("erase %s: %3lld/100", devname, (offset + realsize) * 100 / devsize).c_str()); } return send_reply(client, "OKAY", ""); } else if (cmd == "continue") { android::base::WriteStringToFile("5", "/sys/module/bcm2709/parameters/reboot_part"); android_reboot(ANDROID_RB_RESTART, 0, NULL); // while (true) { pause(); } return send_reply(client, "OKAY", ""); } else if (cmd == "reboot" || cmd == "reboot-bootloader") { android::base::WriteStringToFile("0", "/sys/module/bcm2709/parameters/reboot_part"); android_reboot(ANDROID_RB_RESTART, 0, NULL); // while (true) { pause(); } return send_reply(client, "OKAY", ""); } return send_reply(client, "FAIL", "unknown command: %s", cmd.c_str()); }
int make_ext4fs_internal(int fd, const char *_directory, const char *_target_out_directory, const char *_mountpoint, fs_config_func_t fs_config_func, int gzip, int sparse, int crc, int wipe, int real_uuid, struct selabel_handle *sehnd, int verbose, time_t fixed_time, FILE* block_list_file) { u32 root_inode_num; u16 root_mode; char *mountpoint; char *directory = NULL; char *target_out_directory = NULL; if (setjmp(setjmp_env)) return EXIT_FAILURE; /* Handle a call to longjmp() */ info.block_device = is_block_device_fd(fd); if (info.block_device && (sparse || gzip || crc)) { fprintf(stderr, "No sparse/gzip/crc allowed for block device\n"); return EXIT_FAILURE; } if (_mountpoint == NULL) { mountpoint = strdup(""); } else { mountpoint = canonicalize_abs_slashes(_mountpoint); } if (_directory) { directory = canonicalize_rel_slashes(_directory); } if (_target_out_directory) { target_out_directory = canonicalize_rel_slashes(_target_out_directory); } if (info.len <= 0) info.len = get_file_size(fd); if (info.len <= 0) { fprintf(stderr, "Need size of filesystem\n"); return EXIT_FAILURE; } if (info.block_size <= 0) info.block_size = compute_block_size(); /* Round down the filesystem length to be a multiple of the block size */ info.len &= ~((u64)info.block_size - 1); if (info.journal_blocks == 0) info.journal_blocks = compute_journal_blocks(); if (info.no_journal == 0) info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL; else info.journal_blocks = 0; if (info.blocks_per_group <= 0) info.blocks_per_group = compute_blocks_per_group(); if (info.inodes <= 0) info.inodes = compute_inodes(); if (info.inode_size <= 0) info.inode_size = 256; if (info.label == NULL) info.label = ""; info.inodes_per_group = compute_inodes_per_group(); info.feat_compat |= EXT4_FEATURE_COMPAT_RESIZE_INODE | EXT4_FEATURE_COMPAT_EXT_ATTR; info.feat_ro_compat |= EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER | EXT4_FEATURE_RO_COMPAT_LARGE_FILE | EXT4_FEATURE_RO_COMPAT_GDT_CSUM; info.feat_incompat |= EXT4_FEATURE_INCOMPAT_EXTENTS | EXT4_FEATURE_INCOMPAT_FILETYPE; info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks(); printf("Creating filesystem with parameters:\n"); printf(" Size: %"PRIu64"\n", info.len); printf(" Block size: %d\n", info.block_size); printf(" Blocks per group: %d\n", info.blocks_per_group); printf(" Inodes per group: %d\n", info.inodes_per_group); printf(" Inode size: %d\n", info.inode_size); printf(" Journal blocks: %d\n", info.journal_blocks); printf(" Label: %s\n", info.label); ext4_create_fs_aux_info(); printf(" Blocks: %"PRIu64"\n", aux_info.len_blocks); printf(" Block groups: %d\n", aux_info.groups); printf(" Reserved block group size: %d\n", info.bg_desc_reserve_blocks); ext4_sparse_file = sparse_file_new(info.block_size, info.len); block_allocator_init(); ext4_fill_in_sb(real_uuid); if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED) error("failed to reserve first 10 inodes"); if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL) ext4_create_journal_inode(); if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE) ext4_create_resize_inode(); #ifdef USE_MINGW // Windows needs only 'create an empty fs image' functionality assert(!directory); root_inode_num = build_default_directory_structure(mountpoint, sehnd); #else if (directory) root_inode_num = build_directory_structure(directory, mountpoint, target_out_directory, 0, fs_config_func, sehnd, verbose, fixed_time); else root_inode_num = build_default_directory_structure(mountpoint, sehnd); #endif root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; inode_set_permissions(root_inode_num, root_mode, 0, 0, 0); #ifndef USE_MINGW if (sehnd) { char *secontext = NULL; if (selabel_lookup(sehnd, &secontext, mountpoint, S_IFDIR) < 0) { error("cannot lookup security context for %s", mountpoint); } if (secontext) { if (verbose) { printf("Labeling %s as %s\n", mountpoint, secontext); } inode_set_selinux(root_inode_num, secontext); } freecon(secontext); } #endif ext4_update_free(); if (block_list_file) { size_t dirlen = directory ? strlen(directory) : 0; struct block_allocation* p = get_saved_allocation_chain(); while (p) { if (directory && strncmp(p->filename, directory, dirlen) == 0) { // substitute mountpoint for the leading directory in the filename, in the output file fprintf(block_list_file, "%s%s", mountpoint, p->filename + dirlen); } else { fprintf(block_list_file, "%s", p->filename); } print_blocks(block_list_file, p); struct block_allocation* pn = p->next; free_alloc(p); p = pn; } } printf("Created filesystem with %d/%d inodes and %d/%d blocks\n", aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count, aux_info.sb->s_inodes_count, aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo, aux_info.sb->s_blocks_count_lo); if (wipe && WIPE_IS_SUPPORTED) { wipe_block_device(fd, info.len); } write_ext4_image(fd, gzip, sparse, crc); sparse_file_destroy(ext4_sparse_file); ext4_sparse_file = NULL; free(mountpoint); free(directory); return 0; }
int main(int argc, char **argv) { char *data_filename; char *verity_filename; unsigned char *salt = NULL; size_t salt_size = 0; bool sparse = false; size_t block_size = 4096; uint64_t calculate_size = 0; bool verbose = false; while (1) { const static struct option long_options[] = { {"salt-str", required_argument, 0, 'a'}, {"salt-hex", required_argument, 0, 'A'}, {"help", no_argument, 0, 'h'}, {"sparse", no_argument, 0, 'S'}, {"verity-size", required_argument, 0, 's'}, {"verbose", no_argument, 0, 'v'}, {NULL, 0, 0, 0} }; int c = getopt_long(argc, argv, "a:A:hSs:v", long_options, NULL); if (c < 0) { break; } switch (c) { case 'a': salt_size = strlen(optarg); salt = new unsigned char[salt_size](); if (salt == NULL) { FATAL("failed to allocate memory for salt\n"); } memcpy(salt, optarg, salt_size); break; case 'A': { BIGNUM *bn = NULL; if(!BN_hex2bn(&bn, optarg)) { FATAL("failed to convert salt from hex\n"); } salt_size = BN_num_bytes(bn); salt = new unsigned char[salt_size](); if (salt == NULL) { FATAL("failed to allocate memory for salt\n"); } if((size_t)BN_bn2bin(bn, salt) != salt_size) { FATAL("failed to convert salt to bytes\n"); } } break; case 'h': usage(); return 1; case 'S': sparse = true; break; case 's': { char* endptr; errno = 0; unsigned long long int inSize = strtoull(optarg, &endptr, 0); if (optarg[0] == '\0' || *endptr != '\0' || (errno == ERANGE && inSize == ULLONG_MAX)) { FATAL("invalid value of verity-size\n"); } if (inSize > UINT64_MAX) { FATAL("invalid value of verity-size\n"); } calculate_size = (uint64_t)inSize; } break; case 'v': verbose = true; break; case '?': usage(); return 1; default: abort(); } } argc -= optind; argv += optind; const EVP_MD *md = EVP_sha256(); if (!md) { FATAL("failed to get digest\n"); } size_t hash_size = EVP_MD_size(md); assert(hash_size * 2 < block_size); if (!salt || !salt_size) { salt_size = hash_size; salt = new unsigned char[salt_size]; if (salt == NULL) { FATAL("failed to allocate memory for salt\n"); } int random_fd = open("/dev/urandom", O_RDONLY); if (random_fd < 0) { FATAL("failed to open /dev/urandom\n"); } ssize_t ret = read(random_fd, salt, salt_size); if (ret != (ssize_t)salt_size) { FATAL("failed to read %zu bytes from /dev/urandom: %zd %d\n", salt_size, ret, errno); } close(random_fd); } if (calculate_size) { if (argc != 0) { usage(); return 1; } size_t verity_blocks = 0; size_t level_blocks; int levels = 0; do { level_blocks = verity_tree_blocks(calculate_size, block_size, hash_size, levels); levels++; verity_blocks += level_blocks; } while (level_blocks > 1); printf("%" PRIu64 "\n", (uint64_t)verity_blocks * block_size); return 0; } if (argc != 2) { usage(); return 1; } data_filename = argv[0]; verity_filename = argv[1]; int fd = open(data_filename, O_RDONLY); if (fd < 0) { FATAL("failed to open %s\n", data_filename); } struct sparse_file *file; if (sparse) { file = sparse_file_import(fd, false, false); } else { file = sparse_file_import_auto(fd, false, verbose); } if (!file) { FATAL("failed to read file %s\n", data_filename); } int64_t len = sparse_file_len(file, false, false); if (len % block_size != 0) { FATAL("file size %" PRIu64 " is not a multiple of %zu bytes\n", len, block_size); } int levels = 0; size_t verity_blocks = 0; size_t level_blocks; do { level_blocks = verity_tree_blocks(len, block_size, hash_size, levels); levels++; verity_blocks += level_blocks; } while (level_blocks > 1); unsigned char *verity_tree = new unsigned char[verity_blocks * block_size](); unsigned char **verity_tree_levels = new unsigned char *[levels + 1](); size_t *verity_tree_level_blocks = new size_t[levels](); if (verity_tree == NULL || verity_tree_levels == NULL || verity_tree_level_blocks == NULL) { FATAL("failed to allocate memory for verity tree\n"); } unsigned char *ptr = verity_tree; for (int i = levels - 1; i >= 0; i--) { verity_tree_levels[i] = ptr; verity_tree_level_blocks[i] = verity_tree_blocks(len, block_size, hash_size, i); ptr += verity_tree_level_blocks[i] * block_size; } assert(ptr == verity_tree + verity_blocks * block_size); assert(verity_tree_level_blocks[levels - 1] == 1); unsigned char zero_block_hash[hash_size]; unsigned char zero_block[block_size]; memset(zero_block, 0, block_size); hash_block(md, zero_block, block_size, salt, salt_size, zero_block_hash, NULL); unsigned char root_hash[hash_size]; verity_tree_levels[levels] = root_hash; struct sparse_hash_ctx ctx; ctx.hashes = verity_tree_levels[0]; ctx.salt = salt; ctx.salt_size = salt_size; ctx.hash_size = hash_size; ctx.block_size = block_size; ctx.zero_block_hash = zero_block_hash; ctx.md = md; sparse_file_callback(file, false, false, hash_chunk, &ctx); sparse_file_destroy(file); close(fd); for (int i = 0; i < levels; i++) { size_t out_size; hash_blocks(md, verity_tree_levels[i], verity_tree_level_blocks[i] * block_size, verity_tree_levels[i + 1], &out_size, salt, salt_size, block_size); if (i < levels - 1) { assert(div_round_up(out_size, block_size) == verity_tree_level_blocks[i + 1]); } else { assert(out_size == hash_size); } } for (size_t i = 0; i < hash_size; i++) { printf("%02x", root_hash[i]); } printf(" "); for (size_t i = 0; i < salt_size; i++) { printf("%02x", salt[i]); } printf("\n"); fd = open(verity_filename, O_WRONLY|O_CREAT, 0666); if (fd < 0) { FATAL("failed to open output file '%s'\n", verity_filename); } write(fd, verity_tree, verity_blocks * block_size); close(fd); delete[] verity_tree_levels; delete[] verity_tree_level_blocks; delete[] verity_tree; delete[] salt; }