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

Commit 05cf04f6 authored by Paul Lawrence's avatar Paul Lawrence Committed by Alistair Delva
Browse files

ANDROID: Incremental fs: Add INCFS_IOC_GET_FILLED_BLOCKS



Test: incfs_test passes
Bug: 151240628
Signed-off-by: default avatarPaul Lawrence <paullawrence@google.com>
Change-Id: I66d0ba1911adc5d68ed404585222e6a81a7eb94f
(cherry picked from commit 8d963bb24076b60cb2de0f2d49deaff1d52e8270)
parent f2e257e6
Loading
Loading
Loading
Loading
+111 −9
Original line number Diff line number Diff line
@@ -118,8 +118,8 @@ struct data_file *incfs_open_data_file(struct mount_info *mi, struct file *bf)
	error = mutex_lock_interruptible(&bfc->bc_mutex);
	if (error)
		goto out;
	error = incfs_read_file_header(bfc, &df->df_metadata_off,
					&df->df_id, &size);
	error = incfs_read_file_header(bfc, &df->df_metadata_off, &df->df_id,
				       &size, &df->df_header_flags);
	mutex_unlock(&bfc->bc_mutex);

	if (error)
@@ -127,7 +127,7 @@ struct data_file *incfs_open_data_file(struct mount_info *mi, struct file *bf)

	df->df_size = size;
	if (size > 0)
		df->df_block_count = get_blocks_count_for_size(size);
		df->df_data_block_count = get_blocks_count_for_size(size);

	md_records = incfs_scan_metadata_chain(df);
	if (md_records < 0)
@@ -351,7 +351,7 @@ static int get_data_file_block(struct data_file *df, int index,
	blockmap_off = df->df_blockmap_off;
	bfc = df->df_backing_file_context;

	if (index < 0 || index >= df->df_block_count || blockmap_off == 0)
	if (index < 0 || blockmap_off == 0)
		return -EINVAL;

	error = incfs_read_blockmap_entry(bfc, index, blockmap_off, &bme);
@@ -371,6 +371,96 @@ static int get_data_file_block(struct data_file *df, int index,
	return 0;
}

static int copy_one_range(struct incfs_filled_range *range, void __user *buffer,
			  u32 size, u32 *size_out)
{
	if (*size_out + sizeof(*range) > size)
		return -ERANGE;

	if (copy_to_user(((char *)buffer) + *size_out, range, sizeof(*range)))
		return -EFAULT;

	*size_out += sizeof(*range);
	return 0;
}

int incfs_get_filled_blocks(struct data_file *df,
			    struct incfs_get_filled_blocks_args *arg)
{
	int error = 0;
	bool in_range = false;
	struct incfs_filled_range range;
	void *buffer = u64_to_user_ptr(arg->range_buffer);
	u32 size = arg->range_buffer_size;
	u32 end_index =
		arg->end_index ? arg->end_index : df->df_total_block_count;
	u32 *size_out = &arg->range_buffer_size_out;

	*size_out = 0;
	if (end_index > df->df_total_block_count)
		end_index = df->df_total_block_count;
	arg->total_blocks_out = df->df_total_block_count;

	if (df->df_header_flags & INCFS_FILE_COMPLETE) {
		pr_debug("File marked full, fast get_filled_blocks");
		if (arg->start_index > end_index) {
			arg->index_out = arg->start_index;
			return 0;
		}

		range = (struct incfs_filled_range){
			.begin = arg->start_index,
			.end = end_index,
		};

		arg->index_out = end_index;
		return copy_one_range(&range, buffer, size, size_out);
	}

	for (arg->index_out = arg->start_index; arg->index_out < end_index;
	     ++arg->index_out) {
		struct data_file_block dfb;

		error = get_data_file_block(df, arg->index_out, &dfb);
		if (error)
			break;

		if (is_data_block_present(&dfb) == in_range)
			continue;

		if (!in_range) {
			in_range = true;
			range.begin = arg->index_out;
		} else {
			range.end = arg->index_out;
			error = copy_one_range(&range, buffer, size, size_out);
			if (error)
				break;
			in_range = false;
		}
	}

	if (in_range) {
		range.end = arg->index_out;
		error = copy_one_range(&range, buffer, size, size_out);
	}

	if (!error && in_range && arg->start_index == 0 &&
	    end_index == df->df_total_block_count &&
	    *size_out == sizeof(struct incfs_filled_range)) {
		int result;

		df->df_header_flags |= INCFS_FILE_COMPLETE;
		result = incfs_update_file_header_flags(
			df->df_backing_file_context, df->df_header_flags);

		/* Log failure only, since it's just a failed optimization */
		pr_debug("Marked file full with result %d", result);
	}

	return error;
}

static bool is_read_done(struct pending_read *read)
{
	return atomic_read_acquire(&read->done) != 0;
@@ -470,7 +560,7 @@ static int wait_for_data_block(struct data_file *df, int block_index,
	if (!df || !res_block)
		return -EFAULT;

	if (block_index < 0 || block_index >= df->df_block_count)
	if (block_index < 0 || block_index >= df->df_data_block_count)
		return -EINVAL;

	if (df->df_blockmap_off <= 0)
@@ -640,7 +730,7 @@ int incfs_process_new_data_block(struct data_file *df,
	bfc = df->df_backing_file_context;
	mi = df->df_mount_info;

	if (block->block_index >= df->df_block_count)
	if (block->block_index >= df->df_data_block_count)
		return -ERANGE;

	segment = get_file_segment(df, block->block_index);
@@ -746,7 +836,7 @@ int incfs_process_new_hash_block(struct data_file *df,
	if (!error)
		error = incfs_write_hash_block_to_backing_file(
			bfc, range(data, block->data_len), block->block_index,
			hash_area_base);
			hash_area_base, df->df_blockmap_off, df->df_size);
	mutex_unlock(&bfc->bc_mutex);
	return error;
}
@@ -762,9 +852,10 @@ static int process_blockmap_md(struct incfs_blockmap *bm,
	if (!df)
		return -EFAULT;

	if (df->df_block_count != block_count)
	if (df->df_data_block_count > block_count)
		return -EBADMSG;

	df->df_total_block_count = block_count;
	df->df_blockmap_off = base_off;
	return error;
}
@@ -829,7 +920,7 @@ static int process_file_signature_md(struct incfs_file_signature *sg,
	}

	hash_tree = incfs_alloc_mtree(range(buf, signature->sig_size),
				      df->df_block_count);
				      df->df_data_block_count);
	if (IS_ERR(hash_tree)) {
		error = PTR_ERR(hash_tree);
		hash_tree = NULL;
@@ -911,6 +1002,17 @@ int incfs_scan_metadata_chain(struct data_file *df)
		result = records_count;
	}
	mutex_unlock(&bfc->bc_mutex);

	if (df->df_hash_tree) {
		int hash_block_count = get_blocks_count_for_size(
			df->df_hash_tree->hash_tree_area_size);

		if (df->df_data_block_count + hash_block_count !=
		    df->df_total_block_count)
			result = -EINVAL;
	} else if (df->df_data_block_count != df->df_total_block_count)
		result = -EINVAL;

out:
	kfree(handler);
	return result;
+11 −1
Original line number Diff line number Diff line
@@ -183,7 +183,14 @@ struct data_file {
	/* File size in bytes */
	loff_t df_size;

	int df_block_count; /* File size in DATA_FILE_BLOCK_SIZE blocks */
	/* File header flags */
	u32 df_header_flags;

	/* File size in DATA_FILE_BLOCK_SIZE blocks */
	int df_data_block_count;

	/* Total number of blocks, data + hash */
	int df_total_block_count;

	struct file_attr n_attr;

@@ -230,6 +237,9 @@ ssize_t incfs_read_data_file_block(struct mem_range dst, struct data_file *df,
				   int index, int timeout_ms,
				   struct mem_range tmp);

int incfs_get_filled_blocks(struct data_file *df,
			    struct incfs_get_filled_blocks_args *arg);

int incfs_read_file_signature(struct data_file *df, struct mem_range dst);

int incfs_process_new_data_block(struct data_file *df,
+38 −11
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <linux/kernel.h>

#include "format.h"
#include "data_mgmt.h"

struct backing_file_context *incfs_alloc_bfc(struct file *backing_file)
{
@@ -214,12 +215,23 @@ static int append_md_to_backing_file(struct backing_file_context *bfc,
	return result;
}

int incfs_update_file_header_flags(struct backing_file_context *bfc, u32 flags)
{
	if (!bfc)
		return -EFAULT;

	return write_to_bf(bfc, &flags, sizeof(flags),
			   offsetof(struct incfs_file_header,
				    fh_file_header_flags),
			   false);
}

/*
 * Reserve 0-filled space for the blockmap body, and append
 * incfs_blockmap metadata record pointing to it.
 */
int incfs_write_blockmap_to_backing_file(struct backing_file_context *bfc,
				u32 block_count, loff_t *map_base_off)
					 u32 block_count)
{
	struct incfs_blockmap blockmap = {};
	int result = 0;
@@ -245,12 +257,9 @@ int incfs_write_blockmap_to_backing_file(struct backing_file_context *bfc,
	/* Write blockmap metadata record pointing to the body written above. */
	blockmap.m_base_offset = cpu_to_le64(file_end);
	result = append_md_to_backing_file(bfc, &blockmap.m_header);
	if (result) {
	if (result)
		/* Error, rollback file changes */
		truncate_backing_file(bfc, file_end);
	} else if (map_base_off) {
		*map_base_off = file_end;
	}

	return result;
}
@@ -439,11 +448,18 @@ int incfs_write_data_block_to_backing_file(struct backing_file_context *bfc,

int incfs_write_hash_block_to_backing_file(struct backing_file_context *bfc,
					   struct mem_range block,
					int block_index, loff_t hash_area_off)
					   int block_index,
					   loff_t hash_area_off,
					   loff_t bm_base_off, int file_size)
{
	struct incfs_blockmap_entry bm_entry = {};
	int result;
	loff_t data_offset = 0;
	loff_t file_end = 0;

	loff_t bm_entry_off =
		bm_base_off +
		sizeof(struct incfs_blockmap_entry) *
			(block_index + get_blocks_count_for_size(file_size));

	if (!bfc)
		return -EFAULT;
@@ -457,7 +473,17 @@ int incfs_write_hash_block_to_backing_file(struct backing_file_context *bfc,
		return -EINVAL;
	}

	return write_to_bf(bfc, block.data, block.len, data_offset, false);
	result = write_to_bf(bfc, block.data, block.len, data_offset, false);
	if (result)
		return result;

	bm_entry.me_data_offset_lo = cpu_to_le32((u32)data_offset);
	bm_entry.me_data_offset_hi = cpu_to_le16((u16)(data_offset >> 32));
	bm_entry.me_data_size = cpu_to_le16(INCFS_DATA_FILE_BLOCK_SIZE);
	bm_entry.me_flags = cpu_to_le16(INCFS_BLOCK_HASH);

	return write_to_bf(bfc, &bm_entry, sizeof(bm_entry), bm_entry_off,
			   false);
}

/* Initialize a new image in a given backing file. */
@@ -517,10 +543,9 @@ int incfs_read_blockmap_entries(struct backing_file_context *bfc,
	return 0;
}


int incfs_read_file_header(struct backing_file_context *bfc,
			   loff_t *first_md_off, incfs_uuid_t *uuid,
			   u64 *file_size)
			   u64 *file_size, u32 *flags)
{
	ssize_t bytes_read = 0;
	struct incfs_file_header fh = {};
@@ -554,6 +579,8 @@ int incfs_read_file_header(struct backing_file_context *bfc,
		*uuid = fh.fh_uuid;
	if (file_size)
		*file_size = le64_to_cpu(fh.fh_file_size);
	if (flags)
		*flags = le32_to_cpu(fh.fh_file_header_flags);
	return 0;
}

+15 −6
Original line number Diff line number Diff line
@@ -121,6 +121,10 @@ enum incfs_metadata_type {
	INCFS_MD_SIGNATURE = 3
};

enum incfs_file_header_flags {
	INCFS_FILE_COMPLETE = 1 << 0,
};

/* Header included at the beginning of all metadata records on the disk. */
struct incfs_md_header {
	__u8 h_md_entry_type;
@@ -159,8 +163,8 @@ struct incfs_file_header {
	/* INCFS_DATA_FILE_BLOCK_SIZE */
	__le16 fh_data_block_size;

	/* Padding, also reserved for future use. */
	__le32 fh_dummy;
	/* File flags, from incfs_file_header_flags */
	__le32 fh_file_header_flags;

	/* Offset of the first metadata record */
	__le64 fh_first_md_offset;
@@ -178,6 +182,7 @@ struct incfs_file_header {

enum incfs_block_map_entry_flags {
	INCFS_BLOCK_COMPRESSED_LZ4 = (1 << 0),
	INCFS_BLOCK_HASH = (1 << 1),
};

/* Block map entry pointing to an actual location of the data block. */
@@ -284,7 +289,7 @@ void incfs_free_bfc(struct backing_file_context *bfc);

/* Writing stuff */
int incfs_write_blockmap_to_backing_file(struct backing_file_context *bfc,
					 u32 block_count, loff_t *map_base_off);
					 u32 block_count);

int incfs_write_fh_to_backing_file(struct backing_file_context *bfc,
				   incfs_uuid_t *uuid, u64 file_size);
@@ -296,7 +301,9 @@ int incfs_write_data_block_to_backing_file(struct backing_file_context *bfc,

int incfs_write_hash_block_to_backing_file(struct backing_file_context *bfc,
					   struct mem_range block,
					int block_index, loff_t hash_area_off);
					   int block_index,
					   loff_t hash_area_off,
					   loff_t bm_base_off, int file_size);

int incfs_write_file_attr_to_backing_file(struct backing_file_context *bfc,
		struct mem_range value, struct incfs_file_attr *attr);
@@ -304,13 +311,15 @@ int incfs_write_file_attr_to_backing_file(struct backing_file_context *bfc,
int incfs_write_signature_to_backing_file(struct backing_file_context *bfc,
					  struct mem_range sig, u32 tree_size);

int incfs_update_file_header_flags(struct backing_file_context *bfc, u32 flags);

int incfs_make_empty_backing_file(struct backing_file_context *bfc,
				  incfs_uuid_t *uuid, u64 file_size);

/* Reading stuff */
int incfs_read_file_header(struct backing_file_context *bfc,
			   loff_t *first_md_off, incfs_uuid_t *uuid,
			   u64 *file_size);
			   u64 *file_size, u32 *flags);

int incfs_read_blockmap_entry(struct backing_file_context *bfc, int block_index,
			      loff_t bm_base_off,
+33 −13
Original line number Diff line number Diff line
@@ -904,19 +904,6 @@ static int init_new_file(struct mount_info *mi, struct dentry *dentry,
	if (error)
		goto out;

	block_count = (u32)get_blocks_count_for_size(size);
	error = incfs_write_blockmap_to_backing_file(bfc, block_count, NULL);
	if (error)
		goto out;

	/* This fill has data, reserve space for the block map. */
	if (block_count > 0) {
		error = incfs_write_blockmap_to_backing_file(
			bfc, block_count, NULL);
		if (error)
			goto out;
	}

	if (attr.data && attr.len) {
		error = incfs_write_file_attr_to_backing_file(bfc,
							attr, NULL);
@@ -924,6 +911,8 @@ static int init_new_file(struct mount_info *mi, struct dentry *dentry,
			goto out;
	}

	block_count = (u32)get_blocks_count_for_size(size);

	if (user_signature_info) {
		raw_signature = incfs_copy_signature_info_from_user(
			user_signature_info, signature_size);
@@ -945,8 +934,16 @@ static int init_new_file(struct mount_info *mi, struct dentry *dentry,
			bfc, raw_signature, hash_tree->hash_tree_area_size);
		if (error)
			goto out;

		block_count += get_blocks_count_for_size(
			hash_tree->hash_tree_area_size);
	}

	if (block_count)
		error = incfs_write_blockmap_to_backing_file(bfc, block_count);

	if (error)
		goto out;
out:
	if (bfc) {
		mutex_unlock(&bfc->bc_mutex);
@@ -1436,6 +1433,27 @@ static long ioctl_read_file_signature(struct file *f, void __user *arg)
	return error;
}

static long ioctl_get_filled_blocks(struct file *f, void __user *arg)
{
	struct incfs_get_filled_blocks_args __user *args_usr_ptr = arg;
	struct incfs_get_filled_blocks_args args = {};
	struct data_file *df = get_incfs_data_file(f);
	int error;

	if (!df)
		return -EINVAL;

	if (copy_from_user(&args, args_usr_ptr, sizeof(args)) > 0)
		return -EINVAL;

	error = incfs_get_filled_blocks(df, &args);

	if (copy_to_user(args_usr_ptr, &args, sizeof(args)))
		return -EFAULT;

	return error;
}

static long dispatch_ioctl(struct file *f, unsigned int req, unsigned long arg)
{
	struct mount_info *mi = get_mount_info(file_superblock(f));
@@ -1449,6 +1467,8 @@ static long dispatch_ioctl(struct file *f, unsigned int req, unsigned long arg)
		return ioctl_permit_fill(f, (void __user *)arg);
	case INCFS_IOC_READ_FILE_SIGNATURE:
		return ioctl_read_file_signature(f, (void __user *)arg);
	case INCFS_IOC_GET_FILLED_BLOCKS:
		return ioctl_get_filled_blocks(f, (void __user *)arg);
	default:
		return -EINVAL;
	}
Loading