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);
}
Esempio n. 7
0
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;
}
Esempio n. 8
0
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;
}
Esempio n. 11
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;
}
Esempio n. 12
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;
}
Esempio n. 13
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;
}