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
Git-commit: 70539f7cfd
Git-repo: https://android.googlesource.com/kernel/common/
Signed-off-by: Sayali Lokhande <sayalil@codeaurora.org>
This commit is contained in:
committed by
Blagovest Kolenichev
parent
8dda29e75e
commit
6659adcabb
@@ -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,
|
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 mount_info *mi = NULL;
|
||||||
struct backing_file_context *bfc = 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,
|
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 backing_file_context *bfc = NULL;
|
||||||
struct mount_info *mi = 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_read_file_signature(struct data_file *df, struct mem_range dst);
|
||||||
|
|
||||||
int incfs_process_new_data_block(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);
|
||||||
|
|
||||||
int incfs_process_new_hash_block(struct data_file *df,
|
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);
|
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_open(struct inode *inode, struct file *file);
|
||||||
static int file_release(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 int read_single_page(struct file *f, struct page *page);
|
||||||
static long dispatch_ioctl(struct file *f, unsigned int req, unsigned long arg);
|
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 = {
|
static const struct file_operations incfs_file_ops = {
|
||||||
.open = file_open,
|
.open = file_open,
|
||||||
.release = file_release,
|
.release = file_release,
|
||||||
.write = file_write,
|
|
||||||
.read_iter = generic_file_read_iter,
|
.read_iter = generic_file_read_iter,
|
||||||
.mmap = generic_file_mmap,
|
.mmap = generic_file_mmap,
|
||||||
.splice_read = generic_file_splice_read,
|
.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;
|
size = df->df_size;
|
||||||
timeout_ms = df->df_mount_info->mi_options.read_timeout_ms;
|
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) {
|
if (offset < size) {
|
||||||
struct mem_range tmp = {
|
struct mem_range tmp = {
|
||||||
.len = 2 * INCFS_DATA_FILE_BLOCK_SIZE
|
.len = 2 * INCFS_DATA_FILE_BLOCK_SIZE
|
||||||
@@ -1356,6 +1350,72 @@ static long ioctl_create_file(struct mount_info *mi,
|
|||||||
return error;
|
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)
|
static long ioctl_read_file_signature(struct file *f, void __user *arg)
|
||||||
{
|
{
|
||||||
struct incfs_get_file_sig_args __user *args_usr_ptr = 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) {
|
switch (req) {
|
||||||
case INCFS_IOC_CREATE_FILE:
|
case INCFS_IOC_CREATE_FILE:
|
||||||
return ioctl_create_file(mi, (void __user *)arg);
|
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:
|
case INCFS_IOC_READ_FILE_SIGNATURE:
|
||||||
return ioctl_read_file_signature(f, (void __user *)arg);
|
return ioctl_read_file_signature(f, (void __user *)arg);
|
||||||
default:
|
default:
|
||||||
@@ -1878,70 +1940,6 @@ static int file_release(struct inode *inode, struct file *file)
|
|||||||
return 0;
|
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)
|
static int dentry_revalidate(struct dentry *d, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct path backing_path = {};
|
struct path backing_path = {};
|
||||||
|
|||||||
@@ -46,7 +46,15 @@
|
|||||||
|
|
||||||
/* Read file signature */
|
/* Read file signature */
|
||||||
#define INCFS_IOC_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 {
|
enum incfs_compression_alg {
|
||||||
COMPRESSION_NONE = 0,
|
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
|
* Description of a data or hash block to add to a data file.
|
||||||
* block to a data file.
|
|
||||||
*/
|
*/
|
||||||
struct incfs_new_data_block {
|
struct incfs_fill_block {
|
||||||
/* Index of a data block. */
|
/* Index of a data block. */
|
||||||
__u32 block_index;
|
__u32 block_index;
|
||||||
|
|
||||||
@@ -114,6 +121,19 @@ struct incfs_new_data_block {
|
|||||||
__aligned_u64 reserved3;
|
__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 {
|
enum incfs_hash_tree_algorithm {
|
||||||
INCFS_HASH_TREE_NONE = 0,
|
INCFS_HASH_TREE_NONE = 0,
|
||||||
INCFS_HASH_TREE_SHA256 = 1
|
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 *data_buf = malloc(data_buf_size);
|
||||||
uint8_t *current_data = data_buf;
|
uint8_t *current_data = data_buf;
|
||||||
uint8_t *data_end = data_buf + data_buf_size;
|
uint8_t *data_end = data_buf + data_buf_size;
|
||||||
struct incfs_new_data_block *block_buf =
|
struct incfs_fill_block *block_buf =
|
||||||
calloc(block_count, sizeof(*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;
|
ssize_t write_res = 0;
|
||||||
int fd;
|
int fd;
|
||||||
int error = 0;
|
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].block_index = block_index;
|
||||||
block_buf[i].data_len = block_size;
|
block_buf[i].data_len = block_size;
|
||||||
block_buf[i].data = ptr_to_u64(current_data);
|
block_buf[i].data = ptr_to_u64(current_data);
|
||||||
block_buf[i].compression =
|
|
||||||
compress ? COMPRESSION_LZ4 : COMPRESSION_NONE;
|
|
||||||
current_data += block_size;
|
current_data += block_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!error) {
|
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)
|
if (write_res < 0)
|
||||||
error = -errno;
|
error = -errno;
|
||||||
else
|
else
|
||||||
blocks_written = write_res / sizeof(*block_buf);
|
blocks_written = write_res;
|
||||||
}
|
}
|
||||||
if (error) {
|
if (error) {
|
||||||
ksft_print_msg(
|
ksft_print_msg(
|
||||||
@@ -813,21 +815,22 @@ static int load_hash_tree(const char *mount_dir, struct test_file *file)
|
|||||||
int err;
|
int err;
|
||||||
int i;
|
int i;
|
||||||
int fd;
|
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;
|
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;
|
return 0;
|
||||||
|
|
||||||
blocks = malloc(blocks_size);
|
if (!fill_block_array)
|
||||||
if (!blocks)
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
fill_blocks.fill_blocks = ptr_to_u64(fill_block_array);
|
||||||
|
|
||||||
for (i = 0; i < file->mtree_block_count; i++) {
|
for (i = 0; i < fill_blocks.count; i++) {
|
||||||
blocks[i] = (struct incfs_new_data_block){
|
fill_block_array[i] = (struct incfs_fill_block){
|
||||||
.block_index = i,
|
.block_index = i,
|
||||||
.data_len = INCFS_DATA_FILE_BLOCK_SIZE,
|
.data_len = INCFS_DATA_FILE_BLOCK_SIZE,
|
||||||
.data = ptr_to_u64(file->mtree[i].data),
|
.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;
|
goto failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = write(fd, blocks, blocks_size);
|
err = ioctl(fd, INCFS_IOC_FILL_BLOCKS, &fill_blocks);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
if (err < blocks_size)
|
if (err < fill_blocks.count)
|
||||||
err = errno;
|
err = errno;
|
||||||
else {
|
else {
|
||||||
err = 0;
|
err = 0;
|
||||||
@@ -854,7 +857,7 @@ static int load_hash_tree(const char *mount_dir, struct test_file *file)
|
|||||||
}
|
}
|
||||||
|
|
||||||
failure:
|
failure:
|
||||||
free(blocks);
|
free(fill_block_array);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1274,13 +1277,6 @@ static int dynamic_files_and_data_test(char *mount_dir)
|
|||||||
if (i == missing_file_idx)
|
if (i == missing_file_idx)
|
||||||
continue;
|
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);
|
res = emit_test_file_data(mount_dir, file);
|
||||||
if (res) {
|
if (res) {
|
||||||
ksft_print_msg("Error %s emiting data for %s.\n",
|
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) */
|
/* Write first half of the data into the command file. (stage 1) */
|
||||||
for (i = 0; i < file_num_stage1; i++) {
|
for (i = 0; i < file_num_stage1; i++) {
|
||||||
struct test_file *file = &test.files[i];
|
struct test_file *file = &test.files[i];
|
||||||
int res;
|
|
||||||
|
|
||||||
build_mtree(file);
|
build_mtree(file);
|
||||||
if (emit_file(cmd_fd, NULL, file->name, &file->id,
|
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))
|
if (emit_test_file_data(mount_dir, file))
|
||||||
goto failure;
|
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. */
|
/* Unmount and mount again, to see that data is persistent. */
|
||||||
close(cmd_fd);
|
close(cmd_fd);
|
||||||
|
|||||||
Reference in New Issue
Block a user