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:
parent
e2999b39c7
commit
70539f7cfd
5 changed files with 118 additions and 113 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
138
fs/incfs/vfs.c
138
fs/incfs/vfs.c
|
@ -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 = {};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue