Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 70539f7c authored by Paul Lawrence's avatar Paul Lawrence
Browse files

ANDROID: Incremental fs: Make fill block an ioctl



Filling blocks is not equivalent to writing a file, since they are
constrained by the root hash. selinux policy may wish to treat them
differently, for instance.

Test: incfs_test passes
Bug: 138149732
Signed-off-by: default avatarPaul Lawrence <paullawrence@google.com>
Change-Id: Ic369b84b92547b1cfefe422bd881c4e466090aed
parent e2999b39
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -698,7 +698,7 @@ ssize_t incfs_read_data_file_block(struct mem_range dst, struct data_file *df,
}

int incfs_process_new_data_block(struct data_file *df,
				 struct incfs_new_data_block *block, u8 *data)
				 struct incfs_fill_block *block, u8 *data)
{
	struct mount_info *mi = NULL;
	struct backing_file_context *bfc = NULL;
@@ -780,7 +780,7 @@ int incfs_read_file_signature(struct data_file *df, struct mem_range dst)
}

int incfs_process_new_hash_block(struct data_file *df,
				 struct incfs_new_data_block *block, u8 *data)
				 struct incfs_fill_block *block, u8 *data)
{
	struct backing_file_context *bfc = NULL;
	struct mount_info *mi = NULL;
+2 −3
Original line number Diff line number Diff line
@@ -236,11 +236,10 @@ ssize_t incfs_read_data_file_block(struct mem_range dst, struct data_file *df,
int incfs_read_file_signature(struct data_file *df, struct mem_range dst);

int incfs_process_new_data_block(struct data_file *df,
				 struct incfs_new_data_block *block, u8 *data);
				 struct incfs_fill_block *block, u8 *data);

int incfs_process_new_hash_block(struct data_file *df,
				 struct incfs_new_data_block *block, u8 *data);

				 struct incfs_fill_block *block, u8 *data);

bool incfs_fresh_pending_reads_exist(struct mount_info *mi, int last_number);

+68 −70
Original line number Diff line number Diff line
@@ -49,8 +49,6 @@ static int dir_rename(struct inode *old_dir, struct dentry *old_dentry,

static int file_open(struct inode *inode, struct file *file);
static int file_release(struct inode *inode, struct file *file);
static ssize_t file_write(struct file *f, const char __user *buf,
			     size_t size, loff_t *offset);
static int read_single_page(struct file *f, struct page *page);
static long dispatch_ioctl(struct file *f, unsigned int req, unsigned long arg);

@@ -126,7 +124,6 @@ static const struct address_space_operations incfs_address_space_ops = {
static const struct file_operations incfs_file_ops = {
	.open = file_open,
	.release = file_release,
	.write = file_write,
	.read_iter = generic_file_read_iter,
	.mmap = generic_file_mmap,
	.splice_read = generic_file_splice_read,
@@ -794,9 +791,6 @@ static int read_single_page(struct file *f, struct page *page)
	size = df->df_size;
	timeout_ms = df->df_mount_info->mi_options.read_timeout_ms;

	pr_debug("incfs: %s %s %lld\n", __func__,
		f->f_path.dentry->d_name.name, offset);

	if (offset < size) {
		struct mem_range tmp = {
			.len = 2 * INCFS_DATA_FILE_BLOCK_SIZE
@@ -1356,6 +1350,72 @@ static long ioctl_create_file(struct mount_info *mi,
	return error;
}

static long ioctl_fill_blocks(struct file *f, void __user *arg)
{
	struct incfs_fill_blocks __user *usr_fill_blocks = arg;
	struct incfs_fill_blocks fill_blocks;
	struct incfs_fill_block *usr_fill_block_array;
	struct data_file *df = get_incfs_data_file(f);
	const ssize_t data_buf_size = 2 * INCFS_DATA_FILE_BLOCK_SIZE;
	u8 *data_buf = NULL;
	ssize_t error = 0;
	int i = 0;

	if (!df)
		return -EBADF;

	if (copy_from_user(&fill_blocks, usr_fill_blocks, sizeof(fill_blocks)))
		return -EFAULT;

	usr_fill_block_array = u64_to_user_ptr(fill_blocks.fill_blocks);
	data_buf = (u8 *)__get_free_pages(GFP_NOFS, get_order(data_buf_size));
	if (!data_buf)
		return -ENOMEM;

	for (i = 0; i < fill_blocks.count; i++) {
		struct incfs_fill_block fill_block = {};

		if (copy_from_user(&fill_block, &usr_fill_block_array[i],
				   sizeof(fill_block)) > 0) {
			error = -EFAULT;
			break;
		}

		if (fill_block.data_len > data_buf_size) {
			error = -E2BIG;
			break;
		}

		if (copy_from_user(data_buf, u64_to_user_ptr(fill_block.data),
				   fill_block.data_len) > 0) {
			error = -EFAULT;
			break;
		}
		fill_block.data = 0; /* To make sure nobody uses it. */
		if (fill_block.flags & INCFS_BLOCK_FLAGS_HASH) {
			error = incfs_process_new_hash_block(df, &fill_block,
							     data_buf);
		} else {
			error = incfs_process_new_data_block(df, &fill_block,
							     data_buf);
		}
		if (error)
			break;
	}

	if (data_buf)
		free_pages((unsigned long)data_buf, get_order(data_buf_size));

	/*
	 * Only report the error if no records were processed, otherwise
	 * just return how many were processed successfully.
	 */
	if (i == 0)
		return error;

	return i;
}

static long ioctl_read_file_signature(struct file *f, void __user *arg)
{
	struct incfs_get_file_sig_args __user *args_usr_ptr = arg;
@@ -1411,6 +1471,8 @@ static long dispatch_ioctl(struct file *f, unsigned int req, unsigned long arg)
	switch (req) {
	case INCFS_IOC_CREATE_FILE:
		return ioctl_create_file(mi, (void __user *)arg);
	case INCFS_IOC_FILL_BLOCKS:
		return ioctl_fill_blocks(f, (void __user *)arg);
	case INCFS_IOC_READ_FILE_SIGNATURE:
		return ioctl_read_file_signature(f, (void __user *)arg);
	default:
@@ -1878,70 +1940,6 @@ static int file_release(struct inode *inode, struct file *file)
	return 0;
}

static ssize_t file_write(struct file *f, const char __user *buf,
			     size_t size, loff_t *offset)
{
	struct data_file *df = get_incfs_data_file(f);
	const ssize_t data_buf_size = 2 * INCFS_DATA_FILE_BLOCK_SIZE;
	size_t block_count = size / sizeof(struct incfs_new_data_block);
	struct incfs_new_data_block __user *usr_blocks =
		(struct incfs_new_data_block __user *)buf;
	u8 *data_buf = NULL;
	ssize_t error = 0;
	int i = 0;

	if (!df)
		return -EBADF;

	data_buf = (u8 *)__get_free_pages(GFP_NOFS, get_order(data_buf_size));
	if (!data_buf)
		return -ENOMEM;

	for (i = 0; i < block_count; i++) {
		struct incfs_new_data_block block = {};

		if (copy_from_user(&block, &usr_blocks[i], sizeof(block)) > 0) {
			error = -EFAULT;
			break;
		}

		if (block.data_len > data_buf_size) {
			error = -E2BIG;
			break;
		}

		if (copy_from_user(data_buf, u64_to_user_ptr(block.data),
				   block.data_len) > 0) {
			error = -EFAULT;
			break;
		}
		block.data = 0; /* To make sure nobody uses it. */
		if (block.flags & INCFS_BLOCK_FLAGS_HASH) {
			error = incfs_process_new_hash_block(df, &block,
							     data_buf);
		} else {
			error = incfs_process_new_data_block(df, &block,
							     data_buf);
		}
		if (error)
			break;
	}

	if (data_buf)
		free_pages((unsigned long)data_buf, get_order(data_buf_size));
	*offset = 0;

	/*
	 * Only report the error if no records were processed, otherwise
	 * just return how many were processed successfully.
	 */
	if (i == 0)
		return error;

	return i * sizeof(struct incfs_new_data_block);
}


static int dentry_revalidate(struct dentry *d, unsigned int flags)
{
	struct path backing_path = {};
+24 −4
Original line number Diff line number Diff line
@@ -46,7 +46,15 @@

/* Read file signature */
#define INCFS_IOC_READ_FILE_SIGNATURE                                          \
	_IOWR(INCFS_IOCTL_BASE_CODE, 31, struct incfs_get_file_sig_args)
	_IOR(INCFS_IOCTL_BASE_CODE, 31, struct incfs_get_file_sig_args)

/*
 * Fill in one or more data block
 *
 * Returns number of blocks filled in, or error if none were
 */
#define INCFS_IOC_FILL_BLOCKS                                                  \
	_IOR(INCFS_IOCTL_BASE_CODE, 32, struct incfs_fill_blocks)

enum incfs_compression_alg {
	COMPRESSION_NONE = 0,
@@ -81,10 +89,9 @@ struct incfs_pending_read_info {
};

/*
 * A struct to be written into a control file to load a data or hash
 * block to a data file.
 * Description of a data or hash block to add to a data file.
 */
struct incfs_new_data_block {
struct incfs_fill_block {
	/* Index of a data block. */
	__u32 block_index;

@@ -114,6 +121,19 @@ struct incfs_new_data_block {
	__aligned_u64 reserved3;
};

/*
 * Description of a number of blocks to add to a data file
 *
 * Argument for INCFS_IOC_FILL_BLOCKS
 */
struct incfs_fill_blocks {
	/* Number of blocks */
	__u64 count;

	/* A pointer to an array of incfs_fill_block structs */
	__aligned_u64 fill_blocks;
};

enum incfs_hash_tree_algorithm {
	INCFS_HASH_TREE_NONE = 0,
	INCFS_HASH_TREE_SHA256 = 1
+22 −34
Original line number Diff line number Diff line
@@ -343,8 +343,12 @@ static int emit_test_blocks(char *mnt_dir, struct test_file *file,
	uint8_t *data_buf = malloc(data_buf_size);
	uint8_t *current_data = data_buf;
	uint8_t *data_end = data_buf + data_buf_size;
	struct incfs_new_data_block *block_buf =
		calloc(block_count, sizeof(*block_buf));
	struct incfs_fill_block *block_buf =
		calloc(block_count, sizeof(struct incfs_fill_block));
	struct incfs_fill_blocks fill_blocks = {
		.count = block_count,
		.fill_blocks = ptr_to_u64(block_buf),
	};
	ssize_t write_res = 0;
	int fd;
	int error = 0;
@@ -404,17 +408,15 @@ static int emit_test_blocks(char *mnt_dir, struct test_file *file,
		block_buf[i].block_index = block_index;
		block_buf[i].data_len = block_size;
		block_buf[i].data = ptr_to_u64(current_data);
		block_buf[i].compression =
			compress ? COMPRESSION_LZ4 : COMPRESSION_NONE;
		current_data += block_size;
	}

	if (!error) {
		write_res = write(fd, block_buf, sizeof(*block_buf) * i);
		write_res = ioctl(fd, INCFS_IOC_FILL_BLOCKS, &fill_blocks);
		if (write_res < 0)
			error = -errno;
		else
			blocks_written = write_res / sizeof(*block_buf);
			blocks_written = write_res;
	}
	if (error) {
		ksft_print_msg(
@@ -813,21 +815,22 @@ static int load_hash_tree(const char *mount_dir, struct test_file *file)
	int err;
	int i;
	int fd;

	size_t blocks_size =
		file->mtree_block_count * sizeof(struct incfs_new_data_block);
	struct incfs_new_data_block *blocks = NULL;
	char *file_path;
	struct incfs_fill_blocks fill_blocks = {
		.count = file->mtree_block_count,
	};
	struct incfs_fill_block *fill_block_array =
		calloc(fill_blocks.count, sizeof(struct incfs_fill_block));

	if (blocks_size == 0)
	if (fill_blocks.count == 0)
		return 0;

	blocks = malloc(blocks_size);
	if (!blocks)
	if (!fill_block_array)
		return -ENOMEM;
	fill_blocks.fill_blocks = ptr_to_u64(fill_block_array);

	for (i = 0; i < file->mtree_block_count; i++) {
		blocks[i] = (struct incfs_new_data_block){
	for (i = 0; i < fill_blocks.count; i++) {
		fill_block_array[i] = (struct incfs_fill_block){
			.block_index = i,
			.data_len = INCFS_DATA_FILE_BLOCK_SIZE,
			.data = ptr_to_u64(file->mtree[i].data),
@@ -843,10 +846,10 @@ static int load_hash_tree(const char *mount_dir, struct test_file *file)
		goto failure;
	}

	err = write(fd, blocks, blocks_size);
	err = ioctl(fd, INCFS_IOC_FILL_BLOCKS, &fill_blocks);
	close(fd);

	if (err < blocks_size)
	if (err < fill_blocks.count)
		err = errno;
	else {
		err = 0;
@@ -854,7 +857,7 @@ static int load_hash_tree(const char *mount_dir, struct test_file *file)
	}

failure:
	free(blocks);
	free(fill_block_array);
	return err;
}

@@ -1274,13 +1277,6 @@ static int dynamic_files_and_data_test(char *mount_dir)
		if (i == missing_file_idx)
			continue;

		res = load_hash_tree(mount_dir, file);
		if (res) {
			ksft_print_msg("Can't load hashes for %s. error: %s\n",
				file->name, strerror(-res));
			goto failure;
		}

		res = emit_test_file_data(mount_dir, file);
		if (res) {
			ksft_print_msg("Error %s emiting data for %s.\n",
@@ -1479,7 +1475,6 @@ static int work_after_remount_test(char *mount_dir)
	/* Write first half of the data into the command file. (stage 1) */
	for (i = 0; i < file_num_stage1; i++) {
		struct test_file *file = &test.files[i];
		int res;

		build_mtree(file);
		if (emit_file(cmd_fd, NULL, file->name, &file->id,
@@ -1488,13 +1483,6 @@ static int work_after_remount_test(char *mount_dir)

		if (emit_test_file_data(mount_dir, file))
			goto failure;

		res = load_hash_tree(mount_dir, file);
		if (res) {
			ksft_print_msg("Can't load hashes for %s. error: %s\n",
				file->name, strerror(-res));
			goto failure;
		}
	}

	/* Unmount and mount again, to see that data is persistent. */