From 70539f7cfd9f01b6374d5bb9119c3a3945f14aa2 Mon Sep 17 00:00:00 2001 From: Paul Lawrence Date: Tue, 10 Mar 2020 13:03:38 -0700 Subject: [PATCH] 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 Change-Id: Ic369b84b92547b1cfefe422bd881c4e466090aed --- fs/incfs/data_mgmt.c | 4 +- fs/incfs/data_mgmt.h | 5 +- fs/incfs/vfs.c | 138 +++++++++--------- include/uapi/linux/incrementalfs.h | 28 +++- .../selftests/filesystems/incfs/incfs_test.c | 56 +++---- 5 files changed, 118 insertions(+), 113 deletions(-) diff --git a/fs/incfs/data_mgmt.c b/fs/incfs/data_mgmt.c index 91a81a571d51..06a1f0c70f43 100644 --- a/fs/incfs/data_mgmt.c +++ b/fs/incfs/data_mgmt.c @@ -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; diff --git a/fs/incfs/data_mgmt.h b/fs/incfs/data_mgmt.h index e86951c7845a..aa08d18a61c2 100644 --- a/fs/incfs/data_mgmt.h +++ b/fs/incfs/data_mgmt.h @@ -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); diff --git a/fs/incfs/vfs.c b/fs/incfs/vfs.c index 9e774e97dbfc..c3e528e41d94 100644 --- a/fs/incfs/vfs.c +++ b/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 = {}; diff --git a/include/uapi/linux/incrementalfs.h b/include/uapi/linux/incrementalfs.h index b257b9f0ec3f..82add05a32c9 100644 --- a/include/uapi/linux/incrementalfs.h +++ b/include/uapi/linux/incrementalfs.h @@ -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 diff --git a/tools/testing/selftests/filesystems/incfs/incfs_test.c b/tools/testing/selftests/filesystems/incfs/incfs_test.c index dd70e019dc4c..5d0012b3972b 100644 --- a/tools/testing/selftests/filesystems/incfs/incfs_test.c +++ b/tools/testing/selftests/filesystems/incfs/incfs_test.c @@ -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);