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: Paul Lawrence <paullawrence@google.com>
Change-Id: Ic369b84b92547b1cfefe422bd881c4e466090aed
This commit is contained in:
Paul Lawrence 2020-03-10 13:03:38 -07:00
parent e2999b39c7
commit 70539f7cfd
5 changed files with 118 additions and 113 deletions

View file

@ -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;

View file

@ -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);

View file

@ -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 = {};

View file

@ -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

View file

@ -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,14 +1483,7 @@ 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. */
close(cmd_fd);