From 975fd0628f2dc323f4ac0108526f8eb5f2deaeb4 Mon Sep 17 00:00:00 2001 From: Srinivasarao P Date: Mon, 27 Jul 2020 11:36:52 +0530 Subject: [PATCH] Reverting incremental fs changes e282124 ANDROID: Incremental fs: Fix issues with very large files b564958 ANDROID: Incremental fs: Add setattr call fdd560e ANDROID: Incremental fs: Use simple compression in log buffer 4f81903 ANDROID: Incremental fs: Fix create_file performance 3c76e8d ANDROID: Incremental fs: Fix compound page usercopy crash 3aee2b9 ANDROID: Incremental fs: Clean up incfs_test build process e158e20 ANDROID: Incremental fs: make remount log buffer change atomic 467d1f6 ANDROID: Incremental fs: Optimize get_filled_block 91ef6b6 ANDROID: Incremental fs: Fix mislabeled __user ptrs 1b7e2d0 ANDROID: Incremental fs: Use 64-bit int for file_size when writing hash blocks df76f38 Revert "ANDROID: Incremental fs: Fix initialization, use of bitfields" d73d0b4 ANDROID: Incremental fs: Fix remount ecd6f86 ANDROID: Incremental fs: Protect get_fill_block, and add a field 1a00062 ANDROID: Incremental fs: Fix crash polling 0 size read_log df5824e ANDROID: Incremental fs: get_filled_blocks: better index_out a4cad4e ANDROID: Incremental fs: Fix four resource bugs 76f5f1c ANDROID: Incremental fs: Add INCFS_IOC_GET_FILLED_BLOCKS 2d41ac8 ANDROID: Incremental fs: Fix two typos cb94ec7 ANDROID: Incremental fs: Add INCFS_IOC_PERMIT_FILL 758073b ANDROID: Incremental fs: Remove signature checks from kernel 8118f34 ANDROID: Incremental fs: Pad hash blocks dd3909c ANDROID: Incremental fs: Make fill block an ioctl 89e0905 ANDROID: Incremental fs: Remove all access_ok checks ee1d24d ANDROID: Incremental fs: Support xattrs Change-Id: Ib455db0ab788d08e968dcc665e2c9bd98c701b91 Signed-off-by: Srinivasarao P --- fs/incfs/data_mgmt.c | 690 ++++------- fs/incfs/data_mgmt.h | 130 +- fs/incfs/format.c | 126 +- fs/incfs/format.h | 67 +- fs/incfs/integrity.c | 183 ++- fs/incfs/integrity.h | 20 +- fs/incfs/vfs.c | 605 ++++----- include/uapi/linux/incrementalfs.h | 191 +-- .../selftests/filesystems/incfs/Makefile | 17 +- .../selftests/filesystems/incfs/config | 1 + .../selftests/filesystems/incfs/incfs_test.c | 1081 ++++++----------- .../selftests/filesystems/incfs/utils.c | 260 ++-- .../selftests/filesystems/incfs/utils.h | 36 +- 13 files changed, 1365 insertions(+), 2042 deletions(-) create mode 100644 tools/testing/selftests/filesystems/incfs/config diff --git a/fs/incfs/data_mgmt.c b/fs/incfs/data_mgmt.c index d9c43d5cca19..4698f14bbdf7 100644 --- a/fs/incfs/data_mgmt.c +++ b/fs/incfs/data_mgmt.c @@ -27,19 +27,28 @@ struct mount_info *incfs_alloc_mount_info(struct super_block *sb, return ERR_PTR(-ENOMEM); mi->mi_sb = sb; + mi->mi_options = *options; mi->mi_backing_dir_path = *backing_dir_path; mi->mi_owner = get_current_cred(); path_get(&mi->mi_backing_dir_path); mutex_init(&mi->mi_dir_struct_mutex); mutex_init(&mi->mi_pending_reads_mutex); init_waitqueue_head(&mi->mi_pending_reads_notif_wq); - init_waitqueue_head(&mi->mi_log.ml_notif_wq); - spin_lock_init(&mi->mi_log.rl_lock); INIT_LIST_HEAD(&mi->mi_reads_list_head); - error = incfs_realloc_mount_info(mi, options); - if (error) - goto err; + if (options->read_log_pages != 0) { + size_t buf_size = PAGE_SIZE * options->read_log_pages; + + spin_lock_init(&mi->mi_log.rl_writer_lock); + init_waitqueue_head(&mi->mi_log.ml_notif_wq); + + mi->mi_log.rl_size = buf_size / sizeof(*mi->mi_log.rl_ring_buf); + mi->mi_log.rl_ring_buf = kzalloc(buf_size, GFP_NOFS); + if (!mi->mi_log.rl_ring_buf) { + error = -ENOMEM; + goto err; + } + } return mi; @@ -48,47 +57,6 @@ err: return ERR_PTR(error); } -int incfs_realloc_mount_info(struct mount_info *mi, - struct mount_options *options) -{ - void *new_buffer = NULL; - void *old_buffer; - size_t new_buffer_size = 0; - - if (options->read_log_pages != mi->mi_options.read_log_pages) { - struct read_log_state log_state; - /* - * Even though having two buffers allocated at once isn't - * usually good, allocating a multipage buffer under a spinlock - * is even worse, so let's optimize for the shorter lock - * duration. It's not end of the world if we fail to increase - * the buffer size anyway. - */ - if (options->read_log_pages > 0) { - new_buffer_size = PAGE_SIZE * options->read_log_pages; - new_buffer = kzalloc(new_buffer_size, GFP_NOFS); - if (!new_buffer) - return -ENOMEM; - } - - spin_lock(&mi->mi_log.rl_lock); - old_buffer = mi->mi_log.rl_ring_buf; - mi->mi_log.rl_ring_buf = new_buffer; - mi->mi_log.rl_size = new_buffer_size; - log_state = (struct read_log_state){ - .generation_id = mi->mi_log.rl_head.generation_id + 1, - }; - mi->mi_log.rl_head = log_state; - mi->mi_log.rl_tail = log_state; - spin_unlock(&mi->mi_log.rl_lock); - - kfree(old_buffer); - } - - mi->mi_options = *options; - return 0; -} - void incfs_free_mount_info(struct mount_info *mi) { if (!mi) @@ -100,8 +68,6 @@ void incfs_free_mount_info(struct mount_info *mi) mutex_destroy(&mi->mi_pending_reads_mutex); put_cred(mi->mi_owner); kfree(mi->mi_log.rl_ring_buf); - kfree(mi->log_xattr); - kfree(mi->pending_read_xattr); kfree(mi); } @@ -119,11 +85,11 @@ static void data_file_segment_destroy(struct data_file_segment *segment) struct data_file *incfs_open_data_file(struct mount_info *mi, struct file *bf) { - struct data_file *df = NULL; - struct backing_file_context *bfc = NULL; + struct data_file *df; + struct backing_file_context *bfc; int md_records; u64 size; - int error = 0; + int error; int i; if (!bf || !mi) @@ -150,8 +116,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, &df->df_header_flags); + error = incfs_read_file_header(bfc, &df->df_metadata_off, + &df->df_id, &size); mutex_unlock(&bfc->bc_mutex); if (error) @@ -159,7 +125,7 @@ struct data_file *incfs_open_data_file(struct mount_info *mi, struct file *bf) df->df_size = size; if (size > 0) - df->df_data_block_count = get_blocks_count_for_size(size); + df->df_block_count = get_blocks_count_for_size(size); md_records = incfs_scan_metadata_chain(df); if (md_records < 0) @@ -194,7 +160,7 @@ int make_inode_ready_for_data_ops(struct mount_info *mi, struct file *backing_file) { struct inode_info *node = get_incfs_node(inode); - struct data_file *df = NULL; + struct data_file *df; int err = 0; inode_lock(inode); @@ -215,7 +181,7 @@ int make_inode_ready_for_data_ops(struct mount_info *mi, struct dir_file *incfs_open_dir_file(struct mount_info *mi, struct file *bf) { - struct dir_file *dir = NULL; + struct dir_file *dir; if (!S_ISDIR(bf->f_inode->i_mode)) return ERR_PTR(-EBADF); @@ -248,120 +214,33 @@ static ssize_t decompress(struct mem_range src, struct mem_range dst) return result; } -static void log_read_one_record(struct read_log *rl, struct read_log_state *rs) -{ - union log_record *record = - (union log_record *)((u8 *)rl->rl_ring_buf + rs->next_offset); - size_t record_size; - - switch (record->full_record.type) { - case FULL: - rs->base_record = record->full_record; - record_size = sizeof(record->full_record); - break; - - case SAME_FILE: - rs->base_record.block_index = - record->same_file_record.block_index; - rs->base_record.absolute_ts_us += - record->same_file_record.relative_ts_us; - record_size = sizeof(record->same_file_record); - break; - - case SAME_FILE_NEXT_BLOCK: - ++rs->base_record.block_index; - rs->base_record.absolute_ts_us += - record->same_file_next_block.relative_ts_us; - record_size = sizeof(record->same_file_next_block); - break; - - case SAME_FILE_NEXT_BLOCK_SHORT: - ++rs->base_record.block_index; - rs->base_record.absolute_ts_us += - record->same_file_next_block_short.relative_ts_us; - record_size = sizeof(record->same_file_next_block_short); - break; - } - - rs->next_offset += record_size; - if (rs->next_offset > rl->rl_size - sizeof(*record)) { - rs->next_offset = 0; - ++rs->current_pass_no; - } - ++rs->current_record_no; -} - static void log_block_read(struct mount_info *mi, incfs_uuid_t *id, - int block_index) + int block_index, bool timed_out) { struct read_log *log = &mi->mi_log; - struct read_log_state *head, *tail; + struct read_log_state state; s64 now_us = ktime_to_us(ktime_get()); - s64 relative_us; - union log_record record; - size_t record_size; + struct read_log_record record = { + .file_id = *id, + .timestamp_us = now_us + }; - spin_lock(&log->rl_lock); - if (log->rl_size == 0) { - spin_unlock(&log->rl_lock); - return; - } + set_block_index(&record, block_index); + set_timed_out(&record, timed_out); - head = &log->rl_head; - tail = &log->rl_tail; - relative_us = now_us - head->base_record.absolute_ts_us; - - if (memcmp(id, &head->base_record.file_id, sizeof(incfs_uuid_t)) || - relative_us >= 1ll << 32) { - record.full_record = (struct full_record){ - .type = FULL, - .block_index = block_index, - .file_id = *id, - .absolute_ts_us = now_us, - }; - record_size = sizeof(struct full_record); - } else if (block_index != head->base_record.block_index + 1 || - relative_us >= 1 << 30) { - record.same_file_record = (struct same_file_record){ - .type = SAME_FILE, - .block_index = block_index, - .relative_ts_us = relative_us, - }; - record_size = sizeof(struct same_file_record); - } else if (relative_us >= 1 << 14) { - record.same_file_next_block = (struct same_file_next_block){ - .type = SAME_FILE_NEXT_BLOCK, - .relative_ts_us = relative_us, - }; - record_size = sizeof(struct same_file_next_block); - } else { - record.same_file_next_block_short = - (struct same_file_next_block_short){ - .type = SAME_FILE_NEXT_BLOCK_SHORT, - .relative_ts_us = relative_us, - }; - record_size = sizeof(struct same_file_next_block_short); - } + if (log->rl_size == 0) + return; - head->base_record.file_id = *id; - head->base_record.block_index = block_index; - head->base_record.absolute_ts_us = now_us; - - /* Advance tail beyond area we are going to overwrite */ - while (tail->current_pass_no < head->current_pass_no && - tail->next_offset < head->next_offset + record_size) - log_read_one_record(log, tail); - - memcpy(((u8 *)log->rl_ring_buf) + head->next_offset, &record, - record_size); - head->next_offset += record_size; - if (head->next_offset > log->rl_size - sizeof(record)) { - head->next_offset = 0; - ++head->current_pass_no; + spin_lock(&log->rl_writer_lock); + state = READ_ONCE(log->rl_state); + log->rl_ring_buf[state.next_index] = record; + if (++state.next_index == log->rl_size) { + state.next_index = 0; + ++state.current_pass_no; } - ++head->current_record_no; + WRITE_ONCE(log->rl_state, state); + spin_unlock(&log->rl_writer_lock); - spin_unlock(&log->rl_lock); wake_up_all(&log->ml_notif_wq); } @@ -370,7 +249,7 @@ static int validate_hash_tree(struct file *bf, struct data_file *df, { u8 digest[INCFS_MAX_HASH_SIZE] = {}; struct mtree *tree = NULL; - struct incfs_df_signature *sig = NULL; + struct ondisk_signature *sig = NULL; struct mem_range calc_digest_rng; struct mem_range saved_digest_rng; struct mem_range root_hash_rng; @@ -393,8 +272,8 @@ static int validate_hash_tree(struct file *bf, struct data_file *df, return res; for (lvl = 0; lvl < tree->depth; lvl++) { - loff_t lvl_off = - tree->hash_level_suboffset[lvl] + sig->hash_offset; + loff_t lvl_off = tree->hash_level_suboffset[lvl] + + sig->mtree_offset; loff_t hash_block_off = lvl_off + round_down(hash_block_index * digest_size, INCFS_DATA_FILE_BLOCK_SIZE); @@ -442,6 +321,72 @@ static int validate_hash_tree(struct file *bf, struct data_file *df, return 0; } +static int revalidate_signature(struct file *bf, struct data_file *df) +{ + struct ondisk_signature *sig = df->df_signature; + struct mem_range root_hash = {}; + int result = 0; + u8 *sig_buf = NULL; + u8 *add_data_buf = NULL; + ssize_t read_res; + + /* File has no signature. */ + if (!sig || !df->df_hash_tree || sig->sig_size == 0) + return 0; + + /* Signature has already been validated. */ + if (df->df_signature_validated) + return 0; + + add_data_buf = kzalloc(sig->add_data_size, GFP_NOFS); + if (!add_data_buf) { + result = -ENOMEM; + goto out; + } + + read_res = incfs_kread(bf, add_data_buf, sig->add_data_size, + sig->add_data_offset); + if (read_res < 0) { + result = read_res; + goto out; + } + if (read_res != sig->add_data_size) { + result = -EIO; + goto out; + } + + sig_buf = kzalloc(sig->sig_size, GFP_NOFS); + if (!sig_buf) { + result = -ENOMEM; + goto out; + } + + read_res = incfs_kread(bf, sig_buf, sig->sig_size, sig->sig_offset); + if (read_res < 0) { + result = read_res; + goto out; + } + if (read_res != sig->sig_size) { + result = -EIO; + goto out; + } + + root_hash = range(df->df_hash_tree->root_hash, + df->df_hash_tree->alg->digest_size); + + result = incfs_validate_pkcs7_signature( + range(sig_buf, sig->sig_size), + root_hash, + range(add_data_buf, sig->add_data_size)); + + if (result == 0) + df->df_signature_validated = true; +out: + kfree(sig_buf); + kfree(add_data_buf); + return result; +} + static struct data_file_segment *get_file_segment(struct data_file *df, int block_index) { @@ -456,28 +401,13 @@ static bool is_data_block_present(struct data_file_block *block) (block->db_stored_size != 0); } -static void convert_data_file_block(struct incfs_blockmap_entry *bme, - struct data_file_block *res_block) -{ - u16 flags = le16_to_cpu(bme->me_flags); - - res_block->db_backing_file_data_offset = - le16_to_cpu(bme->me_data_offset_hi); - res_block->db_backing_file_data_offset <<= 32; - res_block->db_backing_file_data_offset |= - le32_to_cpu(bme->me_data_offset_lo); - res_block->db_stored_size = le16_to_cpu(bme->me_data_size); - res_block->db_comp_alg = (flags & INCFS_BLOCK_COMPRESSED_LZ4) ? - COMPRESSION_LZ4 : - COMPRESSION_NONE; -} - static int get_data_file_block(struct data_file *df, int index, struct data_file_block *res_block) { struct incfs_blockmap_entry bme = {}; struct backing_file_context *bfc = NULL; loff_t blockmap_off = 0; + u16 flags = 0; int error = 0; if (!df || !res_block) @@ -486,184 +416,26 @@ 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 || blockmap_off == 0) + if (index < 0 || index >= df->df_block_count || blockmap_off == 0) return -EINVAL; error = incfs_read_blockmap_entry(bfc, index, blockmap_off, &bme); if (error) return error; - convert_data_file_block(&bme, res_block); - return 0; -} - -static int check_room_for_one_range(u32 size, u32 size_out) -{ - if (size_out + sizeof(struct incfs_filled_range) > size) - return -ERANGE; - return 0; -} - -static int copy_one_range(struct incfs_filled_range *range, void __user *buffer, - u32 size, u32 *size_out) -{ - int error = check_room_for_one_range(size, *size_out); - if (error) - return error; - - if (copy_to_user(((char __user *)buffer) + *size_out, range, - sizeof(*range))) - return -EFAULT; - - *size_out += sizeof(*range); + flags = le16_to_cpu(bme.me_flags); + res_block->db_backing_file_data_offset = + le16_to_cpu(bme.me_data_offset_hi); + res_block->db_backing_file_data_offset <<= 32; + res_block->db_backing_file_data_offset |= + le32_to_cpu(bme.me_data_offset_lo); + res_block->db_stored_size = le16_to_cpu(bme.me_data_size); + res_block->db_comp_alg = (flags & INCFS_BLOCK_COMPRESSED_LZ4) ? + COMPRESSION_LZ4 : + COMPRESSION_NONE; return 0; } -static int update_file_header_flags(struct data_file *df, u32 bits_to_reset, - u32 bits_to_set) -{ - int result; - u32 new_flags; - struct backing_file_context *bfc; - - if (!df) - return -EFAULT; - bfc = df->df_backing_file_context; - if (!bfc) - return -EFAULT; - - result = mutex_lock_interruptible(&bfc->bc_mutex); - if (result) - return result; - - new_flags = (df->df_header_flags & ~bits_to_reset) | bits_to_set; - if (new_flags != df->df_header_flags) { - df->df_header_flags = new_flags; - result = incfs_write_file_header_flags(bfc, new_flags); - } - - mutex_unlock(&bfc->bc_mutex); - - return result; -} - -#define READ_BLOCKMAP_ENTRIES 512 -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 __user *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; - int i = READ_BLOCKMAP_ENTRIES - 1; - int entries_read = 0; - struct incfs_blockmap_entry *bme; - - *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; - arg->data_blocks_out = df->df_data_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; - } - arg->index_out = arg->start_index; - - error = check_room_for_one_range(size, *size_out); - if (error) - return error; - - range = (struct incfs_filled_range){ - .begin = arg->start_index, - .end = end_index, - }; - - error = copy_one_range(&range, buffer, size, size_out); - if (error) - return error; - arg->index_out = end_index; - return 0; - } - - bme = kzalloc(sizeof(*bme) * READ_BLOCKMAP_ENTRIES, - GFP_NOFS | __GFP_COMP); - if (!bme) - return -ENOMEM; - - for (arg->index_out = arg->start_index; arg->index_out < end_index; - ++arg->index_out) { - struct data_file_block dfb; - - if (++i == READ_BLOCKMAP_ENTRIES) { - entries_read = incfs_read_blockmap_entries( - df->df_backing_file_context, bme, - arg->index_out, READ_BLOCKMAP_ENTRIES, - df->df_blockmap_off); - if (entries_read < 0) { - error = entries_read; - break; - } - - i = 0; - } - - if (i >= entries_read) { - error = -EIO; - break; - } - - convert_data_file_block(bme + i, &dfb); - - if (is_data_block_present(&dfb) == in_range) - continue; - - if (!in_range) { - error = check_room_for_one_range(size, *size_out); - if (error) - break; - 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) { - /* there will be another try out of the loop, - * it will reset the index_out if it fails too - */ - break; - } - in_range = false; - } - } - - if (in_range) { - range.end = arg->index_out; - error = copy_one_range(&range, buffer, size, size_out); - if (error) - arg->index_out = range.begin; - } - - if (!error && in_range && arg->start_index == 0 && - end_index == df->df_total_block_count && - *size_out == sizeof(struct incfs_filled_range)) { - int result = - update_file_header_flags(df, 0, INCFS_FILE_COMPLETE); - /* Log failure only, since it's just a failed optimization */ - pr_debug("Marked file full with result %d", result); - } - - kfree(bme); - return error; -} - static bool is_read_done(struct pending_read *read) { return atomic_read_acquire(&read->done) != 0; @@ -763,7 +535,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_data_block_count) + if (block_index < 0 || block_index >= df->df_block_count) return -EINVAL; if (df->df_blockmap_off <= 0) @@ -794,7 +566,8 @@ static int wait_for_data_block(struct data_file *df, int block_index, mi = df->df_mount_info; if (timeout_ms == 0) { - log_block_read(mi, &df->df_id, block_index); + log_block_read(mi, &df->df_id, block_index, + true /*timed out*/); return -ETIME; } @@ -813,7 +586,8 @@ static int wait_for_data_block(struct data_file *df, int block_index, if (wait_res == 0) { /* Wait has timed out */ - log_block_read(mi, &df->df_id, block_index); + log_block_read(mi, &df->df_id, block_index, + true /*timed out*/); return -ETIME; } if (wait_res < 0) { @@ -908,15 +682,22 @@ ssize_t incfs_read_data_file_block(struct mem_range dst, struct data_file *df, result = err; } + if (result > 0) { + int err = revalidate_signature(bf, df); + + if (err < 0) + result = err; + } + if (result >= 0) - log_block_read(mi, &df->df_id, index); + log_block_read(mi, &df->df_id, index, false /*timed out*/); out: return result; } int incfs_process_new_data_block(struct data_file *df, - struct incfs_fill_block *block, u8 *data) + struct incfs_new_data_block *block, u8 *data) { struct mount_info *mi = NULL; struct backing_file_context *bfc = NULL; @@ -931,7 +712,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_data_block_count) + if (block->block_index >= df->df_block_count) return -ERANGE; segment = get_file_segment(df, block->block_index); @@ -973,7 +754,7 @@ unlock: int incfs_read_file_signature(struct data_file *df, struct mem_range dst) { struct file *bf = df->df_backing_file_context->bc_file; - struct incfs_df_signature *sig; + struct ondisk_signature *sig; int read_res = 0; if (!dst.data) @@ -998,12 +779,12 @@ 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_fill_block *block, u8 *data) + struct incfs_new_data_block *block, u8 *data) { struct backing_file_context *bfc = NULL; struct mount_info *mi = NULL; struct mtree *hash_tree = NULL; - struct incfs_df_signature *sig = NULL; + struct ondisk_signature *sig = NULL; loff_t hash_area_base = 0; loff_t hash_area_size = 0; int error = 0; @@ -1022,11 +803,11 @@ int incfs_process_new_hash_block(struct data_file *df, hash_tree = df->df_hash_tree; sig = df->df_signature; - if (!hash_tree || !sig || sig->hash_offset == 0) + if (!hash_tree || !sig || sig->mtree_offset == 0) return -ENOTSUPP; - hash_area_base = sig->hash_offset; - hash_area_size = sig->hash_size; + hash_area_base = sig->mtree_offset; + hash_area_size = sig->mtree_size; if (hash_area_size < block->block_index * INCFS_DATA_FILE_BLOCK_SIZE + block->data_len) { /* Hash block goes beyond dedicated hash area of this file. */ @@ -1037,7 +818,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, df->df_blockmap_off, df->df_size); + hash_area_base); mutex_unlock(&bfc->bc_mutex); return error; } @@ -1053,10 +834,9 @@ static int process_blockmap_md(struct incfs_blockmap *bm, if (!df) return -EFAULT; - if (df->df_data_block_count > block_count) + if (df->df_block_count != block_count) return -EBADMSG; - df->df_total_block_count = block_count; df->df_blockmap_off = base_off; return error; } @@ -1085,69 +865,58 @@ static int process_file_signature_md(struct incfs_file_signature *sg, { struct data_file *df = handler->context; struct mtree *hash_tree = NULL; + struct ondisk_signature *signature = NULL; int error = 0; - struct incfs_df_signature *signature = - kzalloc(sizeof(*signature), GFP_NOFS); - void *buf = NULL; - ssize_t read; - - if (!df || !df->df_backing_file_context || - !df->df_backing_file_context->bc_file) { - error = -ENOENT; - goto out; - } + loff_t base_tree_off = le64_to_cpu(sg->sg_hash_tree_offset); + u32 tree_size = le32_to_cpu(sg->sg_hash_tree_size); + loff_t sig_off = le64_to_cpu(sg->sg_sig_offset); + u32 sig_size = le32_to_cpu(sg->sg_sig_size); + loff_t add_data_off = le64_to_cpu(sg->sg_add_data_offset); + u32 add_data_size = le32_to_cpu(sg->sg_add_data_size); - signature->hash_offset = le64_to_cpu(sg->sg_hash_tree_offset); - signature->hash_size = le32_to_cpu(sg->sg_hash_tree_size); - signature->sig_offset = le64_to_cpu(sg->sg_sig_offset); - signature->sig_size = le32_to_cpu(sg->sg_sig_size); + if (!df) + return -ENOENT; - buf = kzalloc(signature->sig_size, GFP_NOFS); - if (!buf) { + signature = kzalloc(sizeof(*signature), GFP_NOFS); + if (!signature) { error = -ENOMEM; goto out; } - read = incfs_kread(df->df_backing_file_context->bc_file, buf, - signature->sig_size, signature->sig_offset); - if (read < 0) { - error = read; - goto out; - } + signature->add_data_offset = add_data_off; + signature->add_data_size = add_data_size; + signature->sig_offset = sig_off; + signature->sig_size = sig_size; + signature->mtree_offset = base_tree_off; + signature->mtree_size = tree_size; - if (read != signature->sig_size) { - error = -EINVAL; - goto out; - } - - hash_tree = incfs_alloc_mtree(range(buf, signature->sig_size), - df->df_data_block_count); + hash_tree = incfs_alloc_mtree(sg->sg_hash_alg, df->df_block_count, + range(sg->sg_root_hash, sizeof(sg->sg_root_hash))); if (IS_ERR(hash_tree)) { error = PTR_ERR(hash_tree); hash_tree = NULL; goto out; } - if (hash_tree->hash_tree_area_size != signature->hash_size) { + if (hash_tree->hash_tree_area_size != tree_size) { error = -EINVAL; goto out; } - if (signature->hash_size > 0 && - handler->md_record_offset <= signature->hash_offset) { + if (tree_size > 0 && handler->md_record_offset <= base_tree_off) { error = -EINVAL; goto out; } - if (handler->md_record_offset <= signature->sig_offset) { + if (handler->md_record_offset <= signature->add_data_offset || + handler->md_record_offset <= signature->sig_offset) { error = -EINVAL; goto out; } df->df_hash_tree = hash_tree; - hash_tree = NULL; df->df_signature = signature; - signature = NULL; out: - incfs_free_mtree(hash_tree); - kfree(signature); - kfree(buf); + if (error) { + incfs_free_mtree(hash_tree); + kfree(signature); + } return error; } @@ -1203,17 +972,6 @@ 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; @@ -1279,29 +1037,36 @@ struct read_log_state incfs_get_log_state(struct mount_info *mi) struct read_log *log = &mi->mi_log; struct read_log_state result; - spin_lock(&log->rl_lock); - result = log->rl_head; - spin_unlock(&log->rl_lock); + spin_lock(&log->rl_writer_lock); + result = READ_ONCE(log->rl_state); + spin_unlock(&log->rl_writer_lock); return result; } +static u64 calc_record_count(const struct read_log_state *state, int rl_size) +{ + return state->current_pass_no * (u64)rl_size + state->next_index; +} + int incfs_get_uncollected_logs_count(struct mount_info *mi, - const struct read_log_state *state) + struct read_log_state state) { struct read_log *log = &mi->mi_log; - u32 generation; - u64 head_no, tail_no; - - spin_lock(&log->rl_lock); - tail_no = log->rl_tail.current_record_no; - head_no = log->rl_head.current_record_no; - generation = log->rl_head.generation_id; - spin_unlock(&log->rl_lock); - - if (generation != state->generation_id) - return head_no - tail_no; - else - return head_no - max_t(u64, tail_no, state->current_record_no); + + u64 count = calc_record_count(&log->rl_state, log->rl_size) - + calc_record_count(&state, log->rl_size); + return min_t(int, count, log->rl_size); +} + +static void fill_pending_read_from_log_record( + struct incfs_pending_read_info *dest, const struct read_log_record *src, + struct read_log_state *state, u64 log_size) +{ + dest->file_id = src->file_id; + dest->block_index = get_block_index(src); + dest->serial_number = + state->current_pass_no * log_size + state->next_index; + dest->timestamp_us = src->timestamp_us; } int incfs_collect_logged_reads(struct mount_info *mi, @@ -1309,47 +1074,58 @@ int incfs_collect_logged_reads(struct mount_info *mi, struct incfs_pending_read_info *reads, int reads_size) { - int dst_idx; struct read_log *log = &mi->mi_log; - struct read_log_state *head, *tail; + struct read_log_state live_state = incfs_get_log_state(mi); + u64 read_count = calc_record_count(reader_state, log->rl_size); + u64 written_count = calc_record_count(&live_state, log->rl_size); + int dst_idx; - spin_lock(&log->rl_lock); - head = &log->rl_head; - tail = &log->rl_tail; + if (reader_state->next_index >= log->rl_size || + read_count > written_count) + return -ERANGE; - if (reader_state->generation_id != head->generation_id) { - pr_debug("read ptr is wrong generation: %u/%u", - reader_state->generation_id, head->generation_id); + if (read_count == written_count) + return 0; - *reader_state = (struct read_log_state){ - .generation_id = head->generation_id, - }; + if (read_count > written_count) { + /* This reader is somehow ahead of the writer. */ + pr_debug("incfs: Log reader is ahead of writer\n"); + *reader_state = live_state; } - if (reader_state->current_record_no < tail->current_record_no) { - pr_debug("read ptr is behind, moving: %u/%u -> %u/%u\n", - (u32)reader_state->next_offset, - (u32)reader_state->current_pass_no, - (u32)tail->next_offset, (u32)tail->current_pass_no); + if (written_count - read_count > log->rl_size) { + /* + * Reading pointer is too far behind, + * start from the record following the write pointer. + */ + pr_debug("incfs: read pointer is behind, moving: %u/%u -> %u/%u / %u\n", + (u32)reader_state->next_index, + (u32)reader_state->current_pass_no, + (u32)live_state.next_index, + (u32)live_state.current_pass_no - 1, (u32)log->rl_size); - *reader_state = *tail; + *reader_state = (struct read_log_state){ + .next_index = live_state.next_index, + .current_pass_no = live_state.current_pass_no - 1, + }; } for (dst_idx = 0; dst_idx < reads_size; dst_idx++) { - if (reader_state->current_record_no == head->current_record_no) + if (reader_state->next_index == live_state.next_index && + reader_state->current_pass_no == live_state.current_pass_no) break; - log_read_one_record(log, reader_state); + fill_pending_read_from_log_record( + &reads[dst_idx], + &log->rl_ring_buf[reader_state->next_index], + reader_state, log->rl_size); - reads[dst_idx] = (struct incfs_pending_read_info){ - .file_id = reader_state->base_record.file_id, - .block_index = reader_state->base_record.block_index, - .serial_number = reader_state->current_record_no, - .timestamp_us = reader_state->base_record.absolute_ts_us - }; + reader_state->next_index++; + if (reader_state->next_index == log->rl_size) { + reader_state->next_index = 0; + reader_state->current_pass_no++; + } } - - spin_unlock(&log->rl_lock); return dst_idx; } diff --git a/fs/incfs/data_mgmt.h b/fs/incfs/data_mgmt.h index b7aecdd5bf4a..6722cef1608c 100644 --- a/fs/incfs/data_mgmt.h +++ b/fs/incfs/data_mgmt.h @@ -20,74 +20,63 @@ #define SEGMENTS_PER_FILE 3 -enum LOG_RECORD_TYPE { - FULL, - SAME_FILE, - SAME_FILE_NEXT_BLOCK, - SAME_FILE_NEXT_BLOCK_SHORT, -}; +struct read_log_record { + u32 bitfield; + + u64 timestamp_us; -struct full_record { - enum LOG_RECORD_TYPE type : 2; /* FULL */ - u32 block_index : 30; incfs_uuid_t file_id; - u64 absolute_ts_us; -} __packed; /* 28 bytes */ - -struct same_file_record { - enum LOG_RECORD_TYPE type : 2; /* SAME_FILE */ - u32 block_index : 30; - u32 relative_ts_us; /* max 2^32 us ~= 1 hour (1:11:30) */ -} __packed; /* 12 bytes */ - -struct same_file_next_block { - enum LOG_RECORD_TYPE type : 2; /* SAME_FILE_NEXT_BLOCK */ - u32 relative_ts_us : 30; /* max 2^30 us ~= 15 min (17:50) */ -} __packed; /* 4 bytes */ - -struct same_file_next_block_short { - enum LOG_RECORD_TYPE type : 2; /* SAME_FILE_NEXT_BLOCK_SHORT */ - u16 relative_ts_us : 14; /* max 2^14 us ~= 16 ms */ -} __packed; /* 2 bytes */ - -union log_record { - struct full_record full_record; - struct same_file_record same_file_record; - struct same_file_next_block same_file_next_block; - struct same_file_next_block_short same_file_next_block_short; -}; +} __packed; -struct read_log_state { - /* Log buffer generation id, incremented on configuration changes */ - u32 generation_id; +#define RLR_BLOCK_INDEX_MASK 0x7fff +#define RLR_TIMED_OUT_MASK 0x8000 - /* Offset in rl_ring_buf to write into. */ - u32 next_offset; +static inline u32 get_block_index(const struct read_log_record *rlr) +{ + return rlr->bitfield & RLR_BLOCK_INDEX_MASK; +} - /* Current number of writer passes over rl_ring_buf */ - u32 current_pass_no; +static inline void set_block_index(struct read_log_record *rlr, + u32 block_index) +{ + rlr->bitfield = (rlr->bitfield & ~RLR_BLOCK_INDEX_MASK) + | (block_index & RLR_BLOCK_INDEX_MASK); +} + +static inline bool get_timed_out(const struct read_log_record *rlr) +{ + return (rlr->bitfield & RLR_TIMED_OUT_MASK) == RLR_TIMED_OUT_MASK; +} + +static inline void set_timed_out(struct read_log_record *rlr, bool timed_out) +{ + if (timed_out) + rlr->bitfield |= RLR_TIMED_OUT_MASK; + else + rlr->bitfield &= ~RLR_TIMED_OUT_MASK; +} - /* Current full_record to diff against */ - struct full_record base_record; +struct read_log_state { + /* Next slot in rl_ring_buf to write to. */ + u32 next_index; - /* Current record number counting from configuration change */ - u64 current_record_no; + /* Current number of writer pass over rl_ring_buf */ + u32 current_pass_no; }; /* A ring buffer to save records about data blocks which were recently read. */ struct read_log { - void *rl_ring_buf; - - int rl_size; + struct read_log_record *rl_ring_buf; - struct read_log_state rl_head; + struct read_log_state rl_state; - struct read_log_state rl_tail; + spinlock_t rl_writer_lock; - /* A lock to protect the above fields */ - spinlock_t rl_lock; + int rl_size; - /* A queue of waiters who want to be notified about reads */ + /* + * A queue of waiters who want to be notified about reads. + */ wait_queue_head_t ml_notif_wq; }; @@ -142,12 +131,6 @@ struct mount_info { /* Temporary buffer for read logger. */ struct read_log mi_log; - - void *log_xattr; - size_t log_xattr_size; - - void *pending_read_xattr; - size_t pending_read_xattr_size; }; struct data_file_block { @@ -220,20 +203,16 @@ struct data_file { /* File size in bytes */ loff_t df_size; - /* 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; + int df_block_count; /* File size in DATA_FILE_BLOCK_SIZE blocks */ struct file_attr n_attr; struct mtree *df_hash_tree; - struct incfs_df_signature *df_signature; + struct ondisk_signature *df_signature; + + /* True, if file signature has already been validated. */ + bool df_signature_validated; }; struct dir_file { @@ -260,9 +239,6 @@ struct mount_info *incfs_alloc_mount_info(struct super_block *sb, struct mount_options *options, struct path *backing_dir_path); -int incfs_realloc_mount_info(struct mount_info *mi, - struct mount_options *options); - void incfs_free_mount_info(struct mount_info *mi); struct data_file *incfs_open_data_file(struct mount_info *mi, struct file *bf); @@ -277,16 +253,14 @@ 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, - struct incfs_fill_block *block, u8 *data); + struct incfs_new_data_block *block, u8 *data); int incfs_process_new_hash_block(struct data_file *df, - struct incfs_fill_block *block, u8 *data); + struct incfs_new_data_block *block, u8 *data); + bool incfs_fresh_pending_reads_exist(struct mount_info *mi, int last_number); @@ -305,7 +279,7 @@ int incfs_collect_logged_reads(struct mount_info *mi, int reads_size); struct read_log_state incfs_get_log_state(struct mount_info *mi); int incfs_get_uncollected_logs_count(struct mount_info *mi, - const struct read_log_state *state); + struct read_log_state state); static inline struct inode_info *get_incfs_node(struct inode *inode) { @@ -323,7 +297,7 @@ static inline struct inode_info *get_incfs_node(struct inode *inode) static inline struct data_file *get_incfs_data_file(struct file *f) { - struct inode_info *node = NULL; + struct inode_info *node; if (!f) return NULL; diff --git a/fs/incfs/format.c b/fs/incfs/format.c index c56e559b6893..db71f527cf36 100644 --- a/fs/incfs/format.c +++ b/fs/incfs/format.c @@ -13,7 +13,6 @@ #include #include "format.h" -#include "data_mgmt.h" struct backing_file_context *incfs_alloc_bfc(struct file *backing_file) { @@ -94,6 +93,7 @@ static int append_zeros(struct backing_file_context *bfc, size_t len) { loff_t file_size = 0; loff_t new_last_byte_offset = 0; + int res = 0; if (!bfc) return -EFAULT; @@ -110,18 +110,28 @@ static int append_zeros(struct backing_file_context *bfc, size_t len) */ file_size = incfs_get_end_offset(bfc->bc_file); new_last_byte_offset = file_size + len - 1; - return vfs_fallocate(bfc->bc_file, 0, new_last_byte_offset, 1); + res = vfs_fallocate(bfc->bc_file, 0, new_last_byte_offset, 1); + if (res) + return res; + + res = vfs_fsync_range(bfc->bc_file, file_size, file_size + len, 1); + return res; } static int write_to_bf(struct backing_file_context *bfc, const void *buf, - size_t count, loff_t pos) + size_t count, loff_t pos, bool sync) { - ssize_t res = incfs_kwrite(bfc->bc_file, buf, count, pos); + ssize_t res = 0; + res = incfs_kwrite(bfc->bc_file, buf, count, pos); if (res < 0) return res; if (res != count) return -EIO; + + if (sync) + return vfs_fsync_range(bfc->bc_file, pos, pos + count, 1); + return 0; } @@ -175,7 +185,7 @@ static int append_md_to_backing_file(struct backing_file_context *bfc, /* Write the metadata record to the end of the backing file */ record_offset = file_pos; new_md_offset = cpu_to_le64(record_offset); - result = write_to_bf(bfc, record, record_size, file_pos); + result = write_to_bf(bfc, record, record_size, file_pos, true); if (result) return result; @@ -196,7 +206,7 @@ static int append_md_to_backing_file(struct backing_file_context *bfc, fh_first_md_offset); } result = write_to_bf(bfc, &new_md_offset, sizeof(new_md_offset), - file_pos); + file_pos, true); if (result) return result; @@ -204,22 +214,12 @@ static int append_md_to_backing_file(struct backing_file_context *bfc, return result; } -int incfs_write_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)); -} - /* * 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) + u32 block_count, loff_t *map_base_off) { struct incfs_blockmap blockmap = {}; int result = 0; @@ -245,9 +245,12 @@ 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; } @@ -280,7 +283,7 @@ int incfs_write_file_attr_to_backing_file(struct backing_file_context *bfc, file_attr.fa_offset = cpu_to_le64(value_offset); file_attr.fa_crc = cpu_to_le32(crc); - result = write_to_bf(bfc, value.data, value.len, value_offset); + result = write_to_bf(bfc, value.data, value.len, value_offset, true); if (result) return result; @@ -296,7 +299,9 @@ 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) + u8 hash_alg, u32 tree_size, + struct mem_range root_hash, struct mem_range add_data, + struct mem_range sig) { struct incfs_file_signature sg = {}; int result = 0; @@ -306,6 +311,8 @@ int incfs_write_signature_to_backing_file(struct backing_file_context *bfc, if (!bfc) return -EFAULT; + if (root_hash.len > sizeof(sg.sg_root_hash)) + return -E2BIG; LOCK_REQUIRED(bfc->bc_mutex); @@ -314,19 +321,32 @@ int incfs_write_signature_to_backing_file(struct backing_file_context *bfc, sg.sg_header.h_md_entry_type = INCFS_MD_SIGNATURE; sg.sg_header.h_record_size = cpu_to_le16(sizeof(sg)); sg.sg_header.h_next_md_offset = cpu_to_le64(0); + sg.sg_hash_alg = hash_alg; if (sig.data != NULL && sig.len > 0) { loff_t pos = incfs_get_end_offset(bfc->bc_file); sg.sg_sig_size = cpu_to_le32(sig.len); sg.sg_sig_offset = cpu_to_le64(pos); - result = write_to_bf(bfc, sig.data, sig.len, pos); + result = write_to_bf(bfc, sig.data, sig.len, pos, false); + if (result) + goto err; + } + + if (add_data.len > 0) { + loff_t pos = incfs_get_end_offset(bfc->bc_file); + + sg.sg_add_data_size = cpu_to_le32(add_data.len); + sg.sg_add_data_offset = cpu_to_le64(pos); + + result = write_to_bf(bfc, add_data.data, + add_data.len, pos, false); if (result) goto err; } tree_area_pos = incfs_get_end_offset(bfc->bc_file); - if (tree_size > 0) { + if (hash_alg && tree_size > 0) { if (tree_size > 5 * INCFS_DATA_FILE_BLOCK_SIZE) { /* * If hash tree is big enough, it makes sense to @@ -349,13 +369,15 @@ int incfs_write_signature_to_backing_file(struct backing_file_context *bfc, sg.sg_hash_tree_size = cpu_to_le32(tree_size); sg.sg_hash_tree_offset = cpu_to_le64(tree_area_pos); } + memcpy(sg.sg_root_hash, root_hash.data, root_hash.len); /* Write a hash tree metadata record pointing to the hash tree above. */ result = append_md_to_backing_file(bfc, &sg.sg_header); err: - if (result) + if (result) { /* Error, rollback file changes */ truncate_backing_file(bfc, rollback_pos); + } return result; } @@ -389,7 +411,7 @@ int incfs_write_fh_to_backing_file(struct backing_file_context *bfc, if (file_pos != 0) return -EEXIST; - return write_to_bf(bfc, &fh, sizeof(fh), file_pos); + return write_to_bf(bfc, &fh, sizeof(fh), file_pos, true); } /* Write a given data block and update file's blockmap to point it. */ @@ -418,7 +440,7 @@ int incfs_write_data_block_to_backing_file(struct backing_file_context *bfc, } /* Write the block data at the end of the backing file. */ - result = write_to_bf(bfc, block.data, block.len, data_offset); + result = write_to_bf(bfc, block.data, block.len, data_offset, false); if (result) return result; @@ -428,25 +450,18 @@ int incfs_write_data_block_to_backing_file(struct backing_file_context *bfc, bm_entry.me_data_size = cpu_to_le16((u16)block.len); bm_entry.me_flags = cpu_to_le16(flags); - return write_to_bf(bfc, &bm_entry, sizeof(bm_entry), - bm_entry_off); + result = write_to_bf(bfc, &bm_entry, sizeof(bm_entry), + bm_entry_off, false); + return result; } 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, - loff_t bm_base_off, - loff_t file_size) + struct mem_range block, + int block_index, loff_t hash_area_off) { - 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; @@ -460,16 +475,7 @@ int incfs_write_hash_block_to_backing_file(struct backing_file_context *bfc, return -EINVAL; } - result = write_to_bf(bfc, block.data, block.len, data_offset); - 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); + return write_to_bf(bfc, block.data, block.len, data_offset, false); } /* Initialize a new image in a given backing file. */ @@ -499,19 +505,8 @@ int incfs_read_blockmap_entry(struct backing_file_context *bfc, int block_index, loff_t bm_base_off, struct incfs_blockmap_entry *bm_entry) { - int error = incfs_read_blockmap_entries(bfc, bm_entry, block_index, 1, - bm_base_off); - - if (error < 0) - return error; - - if (error == 0) - return -EIO; - - if (error != 1) - return -EFAULT; - - return 0; + return incfs_read_blockmap_entries(bfc, bm_entry, block_index, 1, + bm_base_off); } int incfs_read_blockmap_entries(struct backing_file_context *bfc, @@ -535,12 +530,15 @@ int incfs_read_blockmap_entries(struct backing_file_context *bfc, bm_entry_off); if (result < 0) return result; - return result / sizeof(*entries); + if (result < bytes_to_read) + return -EIO; + return 0; } + int incfs_read_file_header(struct backing_file_context *bfc, loff_t *first_md_off, incfs_uuid_t *uuid, - u64 *file_size, u32 *flags) + u64 *file_size) { ssize_t bytes_read = 0; struct incfs_file_header fh = {}; @@ -574,8 +572,6 @@ 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; } diff --git a/fs/incfs/format.h b/fs/incfs/format.h index 1a83349bb2eb..a86881482e19 100644 --- a/fs/incfs/format.h +++ b/fs/incfs/format.h @@ -121,10 +121,6 @@ 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; @@ -163,8 +159,8 @@ struct incfs_file_header { /* INCFS_DATA_FILE_BLOCK_SIZE */ __le16 fh_data_block_size; - /* File flags, from incfs_file_header_flags */ - __le32 fh_file_header_flags; + /* Padding, also reserved for future use. */ + __le32 fh_dummy; /* Offset of the first metadata record */ __le64 fh_first_md_offset; @@ -182,7 +178,6 @@ 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. */ @@ -222,26 +217,26 @@ struct incfs_file_attr { __le32 fa_crc; } __packed; -/* Metadata record for file signature. Type = INCFS_MD_SIGNATURE */ +/* Metadata record for file attribute. Type = INCFS_MD_SIGNATURE */ struct incfs_file_signature { struct incfs_md_header sg_header; - __le32 sg_sig_size; /* The size of the signature. */ - - __le64 sg_sig_offset; /* Signature's offset in the backing file */ + __u8 sg_hash_alg; /* Value from incfs_hash_tree_algorithm */ __le32 sg_hash_tree_size; /* The size of the hash tree. */ __le64 sg_hash_tree_offset; /* Hash tree offset in the backing file */ -} __packed; -/* In memory version of above */ -struct incfs_df_signature { - u32 sig_size; - u64 sig_offset; - u32 hash_size; - u64 hash_offset; -}; + __u8 sg_root_hash[INCFS_MAX_HASH_SIZE]; + + __le32 sg_sig_size; /* The size of the pkcs7 signature. */ + + __le64 sg_sig_offset; /* pkcs7 signature's offset in the backing file */ + + __le32 sg_add_data_size; /* The size of the additional data. */ + + __le64 sg_add_data_offset; /* Additional data's offset */ +} __packed; /* State of the backing file. */ struct backing_file_context { @@ -258,6 +253,23 @@ struct backing_file_context { loff_t bc_last_md_record_offset; }; + +/* Backing file locations of things required for signature validation. */ +struct ondisk_signature { + + loff_t add_data_offset; /* Additional data's offset */ + + loff_t sig_offset; /* pkcs7 signature's offset in the backing file */ + + loff_t mtree_offset; /* Backing file offset of the hash tree. */ + + u32 add_data_size; /* The size of the additional data. */ + + u32 sig_size; /* The size of the pkcs7 signature. */ + + u32 mtree_size; /* The size of the hash tree. */ +}; + struct metadata_handler { loff_t md_record_offset; loff_t md_prev_record_offset; @@ -289,7 +301,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); + u32 block_count, loff_t *map_base_off); int incfs_write_fh_to_backing_file(struct backing_file_context *bfc, incfs_uuid_t *uuid, u64 file_size); @@ -300,19 +312,16 @@ int incfs_write_data_block_to_backing_file(struct backing_file_context *bfc, u16 flags); 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, - loff_t bm_base_off, - loff_t file_size); + struct mem_range block, + int block_index, loff_t hash_area_off); int incfs_write_file_attr_to_backing_file(struct backing_file_context *bfc, struct mem_range value, struct incfs_file_attr *attr); int incfs_write_signature_to_backing_file(struct backing_file_context *bfc, - struct mem_range sig, u32 tree_size); - -int incfs_write_file_header_flags(struct backing_file_context *bfc, u32 flags); + u8 hash_alg, u32 tree_size, + struct mem_range root_hash, struct mem_range add_data, + struct mem_range sig); int incfs_make_empty_backing_file(struct backing_file_context *bfc, incfs_uuid_t *uuid, u64 file_size); @@ -320,7 +329,7 @@ int incfs_make_empty_backing_file(struct backing_file_context *bfc, /* Reading stuff */ int incfs_read_file_header(struct backing_file_context *bfc, loff_t *first_md_off, incfs_uuid_t *uuid, - u64 *file_size, u32 *flags); + u64 *file_size); int incfs_read_blockmap_entry(struct backing_file_context *bfc, int block_index, loff_t bm_base_off, diff --git a/fs/incfs/integrity.c b/fs/incfs/integrity.c index d049988ef037..feb212c38945 100644 --- a/fs/incfs/integrity.c +++ b/fs/incfs/integrity.c @@ -10,6 +10,70 @@ #include "integrity.h" +int incfs_validate_pkcs7_signature(struct mem_range pkcs7_blob, + struct mem_range root_hash, struct mem_range add_data) +{ + struct pkcs7_message *pkcs7 = NULL; + const void *data = NULL; + size_t data_len = 0; + const char *p; + int err; + + pkcs7 = pkcs7_parse_message(pkcs7_blob.data, pkcs7_blob.len); + if (IS_ERR(pkcs7)) { + pr_debug("PKCS#7 parsing error. ptr=%p size=%ld err=%ld\n", + pkcs7_blob.data, pkcs7_blob.len, -PTR_ERR(pkcs7)); + return PTR_ERR(pkcs7); + } + + err = pkcs7_get_content_data(pkcs7, &data, &data_len, NULL); + if (err || data_len == 0 || data == NULL) { + pr_debug("PKCS#7 message does not contain data\n"); + err = -EBADMSG; + goto out; + } + + if (root_hash.len == 0) { + pr_debug("Root hash is empty.\n"); + err = -EBADMSG; + goto out; + } + + if (data_len != root_hash.len + add_data.len) { + pr_debug("PKCS#7 data size doesn't match arguments.\n"); + err = -EKEYREJECTED; + goto out; + } + + p = data; + if (memcmp(p, root_hash.data, root_hash.len) != 0) { + pr_debug("Root hash mismatch.\n"); + err = -EKEYREJECTED; + goto out; + } + p += root_hash.len; + if (memcmp(p, add_data.data, add_data.len) != 0) { + pr_debug("Additional data mismatch.\n"); + err = -EKEYREJECTED; + goto out; + } + + err = pkcs7_verify(pkcs7, VERIFYING_UNSPECIFIED_SIGNATURE); + if (err) + pr_debug("PKCS#7 signature verification error: %d\n", -err); + + /* + * RSA signature verification sometimes returns unexpected error codes + * when signature doesn't match. + */ + if (err == -ERANGE || err == -EINVAL) + err = -EBADMSG; + +out: + pkcs7_free_message(pkcs7); + return err; +} + struct incfs_hash_alg *incfs_get_hash_alg(enum incfs_hash_tree_algorithm id) { static struct incfs_hash_alg sha256 = { @@ -49,90 +113,11 @@ struct incfs_hash_alg *incfs_get_hash_alg(enum incfs_hash_tree_algorithm id) return result; } -struct signature_info { - u32 version; - enum incfs_hash_tree_algorithm hash_algorithm; - u8 log2_blocksize; - struct mem_range salt; - struct mem_range root_hash; -}; - -static bool read_u32(u8 **p, u8 *top, u32 *result) -{ - if (*p + sizeof(u32) > top) - return false; - - *result = le32_to_cpu(*(__le32 *)*p); - *p += sizeof(u32); - return true; -} - -static bool read_u8(u8 **p, u8 *top, u8 *result) -{ - if (*p + sizeof(u8) > top) - return false; - - *result = *(u8 *)*p; - *p += sizeof(u8); - return true; -} - -static bool read_mem_range(u8 **p, u8 *top, struct mem_range *range) -{ - u32 len; - - if (!read_u32(p, top, &len) || *p + len > top) - return false; - - range->len = len; - range->data = *p; - *p += len; - return true; -} -static int incfs_parse_signature(struct mem_range signature, - struct signature_info *si) +struct mtree *incfs_alloc_mtree(enum incfs_hash_tree_algorithm id, + int data_block_count, + struct mem_range root_hash) { - u8 *p = signature.data; - u8 *top = signature.data + signature.len; - u32 hash_section_size; - - if (signature.len > INCFS_MAX_SIGNATURE_SIZE) - return -EINVAL; - - if (!read_u32(&p, top, &si->version) || - si->version != INCFS_SIGNATURE_VERSION) - return -EINVAL; - - if (!read_u32(&p, top, &hash_section_size) || - p + hash_section_size > top) - return -EINVAL; - top = p + hash_section_size; - - if (!read_u32(&p, top, &si->hash_algorithm) || - si->hash_algorithm != INCFS_HASH_TREE_SHA256) - return -EINVAL; - - if (!read_u8(&p, top, &si->log2_blocksize) || si->log2_blocksize != 12) - return -EINVAL; - - if (!read_mem_range(&p, top, &si->salt)) - return -EINVAL; - - if (!read_mem_range(&p, top, &si->root_hash)) - return -EINVAL; - - if (p != top) - return -EINVAL; - - return 0; -} - -struct mtree *incfs_alloc_mtree(struct mem_range signature, - int data_block_count) -{ - int error; - struct signature_info si; struct mtree *result = NULL; struct incfs_hash_alg *hash_alg = NULL; int hash_per_block; @@ -144,15 +129,11 @@ struct mtree *incfs_alloc_mtree(struct mem_range signature, if (data_block_count <= 0) return ERR_PTR(-EINVAL); - error = incfs_parse_signature(signature, &si); - if (error) - return ERR_PTR(error); - - hash_alg = incfs_get_hash_alg(si.hash_algorithm); + hash_alg = incfs_get_hash_alg(id); if (IS_ERR(hash_alg)) return ERR_PTR(PTR_ERR(hash_alg)); - if (si.root_hash.len < hash_alg->digest_size) + if (root_hash.len < hash_alg->digest_size) return ERR_PTR(-EINVAL); result = kzalloc(sizeof(*result), GFP_NOFS); @@ -192,7 +173,7 @@ struct mtree *incfs_alloc_mtree(struct mem_range signature, } /* Root hash is stored separately from the rest of the tree. */ - memcpy(result->root_hash, si.root_hash.data, hash_alg->digest_size); + memcpy(result->root_hash, root_hash.data, hash_alg->digest_size); return result; err: @@ -217,20 +198,16 @@ int incfs_calc_digest(struct incfs_hash_alg *alg, struct mem_range data, return -EINVAL; desc->tfm = alg->shash; - - if (data.len < INCFS_DATA_FILE_BLOCK_SIZE) { - int err; - void *buf = kzalloc(INCFS_DATA_FILE_BLOCK_SIZE, GFP_NOFS); - - if (!buf) - return -ENOMEM; - - memcpy(buf, data.data, data.len); - err = crypto_shash_digest(desc, buf, INCFS_DATA_FILE_BLOCK_SIZE, - digest.data); - kfree(buf); - return err; - } return crypto_shash_digest(desc, data.data, data.len, digest.data); } +void incfs_free_signature_info(struct signature_info *si) +{ + if (!si) + return; + kfree(si->root_hash.data); + kfree(si->additional_data.data); + kfree(si->signature.data); + kfree(si); +} + diff --git a/fs/incfs/integrity.h b/fs/incfs/integrity.h index cf79b64da736..da1c38486b2f 100644 --- a/fs/incfs/integrity.h +++ b/fs/incfs/integrity.h @@ -38,10 +38,21 @@ struct mtree { int depth; }; +struct signature_info { + struct mem_range root_hash; + + struct mem_range additional_data; + + struct mem_range signature; + + enum incfs_hash_tree_algorithm hash_alg; +}; + struct incfs_hash_alg *incfs_get_hash_alg(enum incfs_hash_tree_algorithm id); -struct mtree *incfs_alloc_mtree(struct mem_range signature, - int data_block_count); +struct mtree *incfs_alloc_mtree(enum incfs_hash_tree_algorithm id, + int data_block_count, + struct mem_range root_hash); void incfs_free_mtree(struct mtree *tree); @@ -53,4 +64,9 @@ size_t incfs_get_mtree_hash_count(enum incfs_hash_tree_algorithm alg, int incfs_calc_digest(struct incfs_hash_alg *alg, struct mem_range data, struct mem_range digest); +int incfs_validate_pkcs7_signature(struct mem_range pkcs7_blob, + struct mem_range root_hash, struct mem_range add_data); + +void incfs_free_signature_info(struct signature_info *si); + #endif /* _INCFS_INTEGRITY_H */ diff --git a/fs/incfs/vfs.c b/fs/incfs/vfs.c index d2c82047dd7c..aebd2b02bd83 100644 --- a/fs/incfs/vfs.c +++ b/fs/incfs/vfs.c @@ -52,6 +52,8 @@ 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); @@ -71,11 +73,8 @@ static struct inode *alloc_inode(struct super_block *sb); static void free_inode(struct inode *inode); static void evict_inode(struct inode *inode); -static int incfs_setattr(struct dentry *dentry, struct iattr *ia); static ssize_t incfs_getxattr(struct dentry *d, const char *name, void *value, size_t size); -static ssize_t incfs_setxattr(struct dentry *d, const char *name, - const void *value, size_t size, int flags); static ssize_t incfs_listxattr(struct dentry *d, char *list, size_t size); static int show_options(struct seq_file *, struct dentry *); @@ -102,8 +101,7 @@ static const struct inode_operations incfs_dir_inode_ops = { .rename = dir_rename_wrap, .unlink = dir_unlink, .link = dir_link, - .rmdir = dir_rmdir, - .setattr = incfs_setattr, + .rmdir = dir_rmdir }; static const struct file_operations incfs_dir_fops = { @@ -129,6 +127,7 @@ 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, @@ -137,11 +136,6 @@ static const struct file_operations incfs_file_ops = { .compat_ioctl = dispatch_ioctl }; -enum FILL_PERMISSION { - CANT_FILL = 0, - CAN_FILL = 1, -}; - static const struct file_operations incfs_pending_read_file_ops = { .read = pending_reads_read, .poll = pending_reads_poll, @@ -163,7 +157,7 @@ static const struct file_operations incfs_log_file_ops = { }; static const struct inode_operations incfs_file_inode_ops = { - .setattr = incfs_setattr, + .setattr = simple_setattr, .getattr = simple_getattr, .listxattr = incfs_listxattr }; @@ -175,18 +169,9 @@ static int incfs_handler_getxattr(const struct xattr_handler *xh, return incfs_getxattr(d, name, buffer, size); } -static int incfs_handler_setxattr(const struct xattr_handler *xh, - struct dentry *d, struct inode *inode, - const char *name, const void *buffer, - size_t size, int flags) -{ - return incfs_setxattr(d, name, buffer, size, flags); -} - static const struct xattr_handler incfs_xattr_handler = { .prefix = "", /* AKA all attributes */ .get = incfs_handler_getxattr, - .set = incfs_handler_setxattr, }; static const struct xattr_handler *incfs_xattr_ops[] = { @@ -351,8 +336,8 @@ static int inode_test(struct inode *inode, void *opaque) return (node->n_backing_inode == backing_inode) && inode->i_ino == search->ino; - } else - return inode->i_ino == search->ino; + } + return 1; } static int inode_set(struct inode *inode, void *opaque) @@ -374,7 +359,6 @@ static int inode_set(struct inode *inode, void *opaque) inode->i_mapping->a_ops = &incfs_address_space_ops; inode->i_op = &incfs_file_inode_ops; inode->i_fop = &incfs_file_ops; - inode->i_mode &= ~0222; } else if (S_ISDIR(inode->i_mode)) { inode->i_size = 0; inode->i_blocks = 1; @@ -470,6 +454,9 @@ static ssize_t pending_reads_read(struct file *f, char __user *buf, size_t len, ssize_t result = 0; int i = 0; + if (!access_ok(VERIFY_WRITE, buf, len)) + return -EFAULT; + if (!incfs_fresh_pending_reads_exist(mi, last_known_read_sn)) return 0; @@ -587,27 +574,22 @@ static ssize_t log_read(struct file *f, char __user *buf, size_t len, { struct log_file_state *log_state = f->private_data; struct mount_info *mi = get_mount_info(file_superblock(f)); + struct incfs_pending_read_info *reads_buf = + (struct incfs_pending_read_info *)__get_free_page(GFP_NOFS); + size_t reads_to_collect = len / sizeof(*reads_buf); + size_t reads_per_page = PAGE_SIZE / sizeof(*reads_buf); int total_reads_collected = 0; - int rl_size; ssize_t result = 0; - struct incfs_pending_read_info *reads_buf; - ssize_t reads_to_collect = len / sizeof(*reads_buf); - ssize_t reads_per_page = PAGE_SIZE / sizeof(*reads_buf); - rl_size = READ_ONCE(mi->mi_log.rl_size); - if (rl_size == 0) - return 0; - - reads_buf = (struct incfs_pending_read_info *)__get_free_page(GFP_NOFS); if (!reads_buf) return -ENOMEM; - reads_to_collect = min_t(ssize_t, rl_size, reads_to_collect); + reads_to_collect = min_t(size_t, mi->mi_log.rl_size, reads_to_collect); while (reads_to_collect > 0) { struct read_log_state next_state = READ_ONCE(log_state->state); int reads_collected = incfs_collect_logged_reads( mi, &next_state, reads_buf, - min_t(ssize_t, reads_to_collect, reads_per_page)); + min_t(size_t, reads_to_collect, reads_per_page)); if (reads_collected <= 0) { result = total_reads_collected ? total_reads_collected * @@ -646,7 +628,7 @@ static __poll_t log_poll(struct file *file, poll_table *wait) __poll_t ret = 0; poll_wait(file, &mi->mi_log.ml_notif_wq, wait); - count = incfs_get_uncollected_logs_count(mi, &log_state->state); + count = incfs_get_uncollected_logs_count(mi, log_state->state); if (count >= mi->mi_options.read_log_wakeup_count) ret = EPOLLIN | EPOLLRDNORM; @@ -807,6 +789,9 @@ 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 @@ -853,39 +838,107 @@ static char *file_id_to_str(incfs_uuid_t id) return result; } -static struct mem_range incfs_copy_signature_info_from_user(u8 __user *original, - u64 size) +static struct signature_info *incfs_copy_signature_info_from_user( + struct incfs_file_signature_info __user *original) { - u8 *result; + struct incfs_file_signature_info usr_si; + struct signature_info *result; + int error; if (!original) - return range(NULL, 0); + return NULL; + + if (!access_ok(VERIFY_READ, original, sizeof(usr_si))) + return ERR_PTR(-EFAULT); - if (size > INCFS_MAX_SIGNATURE_SIZE) - return range(ERR_PTR(-EFAULT), 0); + if (copy_from_user(&usr_si, original, sizeof(usr_si)) > 0) + return ERR_PTR(-EFAULT); - result = kzalloc(size, GFP_NOFS | __GFP_COMP); + result = kzalloc(sizeof(*result), GFP_NOFS); if (!result) - return range(ERR_PTR(-ENOMEM), 0); + return ERR_PTR(-ENOMEM); + + result->hash_alg = usr_si.hash_tree_alg; + + if (result->hash_alg) { + void *p = kzalloc(INCFS_MAX_HASH_SIZE, GFP_NOFS); + + if (!p) { + error = -ENOMEM; + goto err; + } + + /* TODO this sets the root_hash length to MAX_HASH_SIZE not + * the actual size. Fix, then set INCFS_MAX_HASH_SIZE back + * to 64 + */ + result->root_hash = range(p, INCFS_MAX_HASH_SIZE); + if (copy_from_user(p, u64_to_user_ptr(usr_si.root_hash), + result->root_hash.len) > 0) { + error = -EFAULT; + goto err; + } + } - if (copy_from_user(result, original, size)) { - kfree(result); - return range(ERR_PTR(-EFAULT), 0); + if (usr_si.additional_data_size > INCFS_MAX_FILE_ATTR_SIZE) { + error = -E2BIG; + goto err; } - return range(result, size); + if (usr_si.additional_data && usr_si.additional_data_size) { + void *p = kzalloc(usr_si.additional_data_size, GFP_NOFS); + + if (!p) { + error = -ENOMEM; + goto err; + } + result->additional_data = range(p, + usr_si.additional_data_size); + if (copy_from_user(p, u64_to_user_ptr(usr_si.additional_data), + result->additional_data.len) > 0) { + error = -EFAULT; + goto err; + } + } + + if (usr_si.signature_size > INCFS_MAX_SIGNATURE_SIZE) { + error = -E2BIG; + goto err; + } + + if (usr_si.signature && usr_si.signature_size) { + void *p = kzalloc(usr_si.signature_size, GFP_NOFS); + + if (!p) { + error = -ENOMEM; + goto err; + } + result->signature = range(p, usr_si.signature_size); + if (copy_from_user(p, u64_to_user_ptr(usr_si.signature), + result->signature.len) > 0) { + error = -EFAULT; + goto err; + } + } + + return result; + +err: + incfs_free_signature_info(result); + return ERR_PTR(-error); } static int init_new_file(struct mount_info *mi, struct dentry *dentry, - incfs_uuid_t *uuid, u64 size, struct mem_range attr, - u8 __user *user_signature_info, u64 signature_size) + incfs_uuid_t *uuid, u64 size, struct mem_range attr, + struct incfs_file_signature_info __user *fsi) { struct path path = {}; struct file *new_file; int error = 0; struct backing_file_context *bfc = NULL; u32 block_count; - struct mem_range raw_signature = { NULL }; + struct mem_range mem_range = {NULL}; + struct signature_info *si = NULL; struct mtree *hash_tree = NULL; if (!mi || !dentry || !uuid) @@ -896,8 +949,7 @@ static int init_new_file(struct mount_info *mi, struct dentry *dentry, .mnt = mi->mi_backing_dir_path.mnt, .dentry = dentry }; - new_file = dentry_open(&path, O_RDWR | O_NOATIME | O_LARGEFILE, - mi->mi_owner); + new_file = dentry_open(&path, O_RDWR | O_NOATIME, mi->mi_owner); if (IS_ERR(new_file)) { error = PTR_ERR(new_file); @@ -905,7 +957,6 @@ static int init_new_file(struct mount_info *mi, struct dentry *dentry, } bfc = incfs_alloc_bfc(new_file); - fput(new_file); if (IS_ERR(bfc)) { error = PTR_ERR(bfc); bfc = NULL; @@ -917,6 +968,19 @@ 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,46 +988,54 @@ 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); + if (fsi) { + si = incfs_copy_signature_info_from_user(fsi); - if (IS_ERR(raw_signature.data)) { - error = PTR_ERR(raw_signature.data); - raw_signature.data = NULL; + if (IS_ERR(si)) { + error = PTR_ERR(si); + si = NULL; goto out; } - hash_tree = incfs_alloc_mtree(raw_signature, block_count); - if (IS_ERR(hash_tree)) { - error = PTR_ERR(hash_tree); - hash_tree = NULL; - goto out; - } + if (si->hash_alg) { + hash_tree = incfs_alloc_mtree(si->hash_alg, block_count, + si->root_hash); + if (IS_ERR(hash_tree)) { + error = PTR_ERR(hash_tree); + hash_tree = NULL; + goto out; + } - error = incfs_write_signature_to_backing_file( - bfc, raw_signature, hash_tree->hash_tree_area_size); - if (error) - goto out; + /* TODO This code seems wrong when len is zero - we + * should error out?? + */ + if (si->signature.len > 0) + error = incfs_validate_pkcs7_signature( + si->signature, + si->root_hash, + si->additional_data); + if (error) + goto out; - block_count += get_blocks_count_for_size( - hash_tree->hash_tree_area_size); - } + error = incfs_write_signature_to_backing_file(bfc, + si->hash_alg, + hash_tree->hash_tree_area_size, + si->root_hash, si->additional_data, + si->signature); - if (block_count) - error = incfs_write_blockmap_to_backing_file(bfc, block_count); + if (error) + goto out; + } + } - if (error) - goto out; out: if (bfc) { mutex_unlock(&bfc->bc_mutex); incfs_free_bfc(bfc); } incfs_free_mtree(hash_tree); - kfree(raw_signature.data); + incfs_free_signature_info(si); + kfree(mem_range.data); if (error) pr_debug("incfs: %s error: %d\n", __func__, error); @@ -1108,7 +1180,10 @@ static long ioctl_create_file(struct mount_info *mi, error = -EFAULT; goto out; } - + if (!access_ok(VERIFY_READ, usr_args, sizeof(args))) { + error = -EFAULT; + goto out; + } if (copy_from_user(&args, usr_args, sizeof(args)) > 0) { error = -EFAULT; goto out; @@ -1221,7 +1296,7 @@ static long ioctl_create_file(struct mount_info *mi, goto delete_index_file; } - /* Save the file's attribute as an xattr */ + /* Save the file's attrubute as an xattr */ if (args.file_attr_len && args.file_attr) { if (args.file_attr_len > INCFS_MAX_FILE_ATTR_SIZE) { error = -E2BIG; @@ -1234,6 +1309,12 @@ static long ioctl_create_file(struct mount_info *mi, goto delete_index_file; } + if (!access_ok(VERIFY_READ, u64_to_user_ptr(args.file_attr), + args.file_attr_len)) { + error = -EFAULT; + goto delete_index_file; + } + if (copy_from_user(attr_value, u64_to_user_ptr(args.file_attr), args.file_attr_len) > 0) { @@ -1252,9 +1333,9 @@ static long ioctl_create_file(struct mount_info *mi, /* Initializing a newly created file. */ error = init_new_file(mi, index_file_dentry, &args.file_id, args.size, - range(attr_value, args.file_attr_len), - (u8 __user *)args.signature_info, - args.signature_size); + range(attr_value, args.file_attr_len), + (struct incfs_file_signature_info __user *) + args.signature_info); if (error) goto delete_index_file; @@ -1282,123 +1363,6 @@ out: 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 __user *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 ((uintptr_t)f->private_data != CAN_FILL) - return -EPERM; - - 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 | __GFP_COMP, - 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_permit_fill(struct file *f, void __user *arg) -{ - struct incfs_permit_fill __user *usr_permit_fill = arg; - struct incfs_permit_fill permit_fill; - long error = 0; - struct file *file = NULL; - - if (f->f_op != &incfs_pending_read_file_ops) - return -EPERM; - - if (copy_from_user(&permit_fill, usr_permit_fill, sizeof(permit_fill))) - return -EFAULT; - - file = fget(permit_fill.file_descriptor); - if (IS_ERR(file)) - return PTR_ERR(file); - - if (file->f_op != &incfs_file_ops) { - error = -EPERM; - goto out; - } - - if (file->f_inode->i_sb != f->f_inode->i_sb) { - error = -EPERM; - goto out; - } - - switch ((uintptr_t)file->private_data) { - case CANT_FILL: - file->private_data = (void *)CAN_FILL; - break; - - case CAN_FILL: - pr_debug("CAN_FILL already set"); - break; - - default: - pr_warn("Invalid file private data"); - error = -EFAULT; - goto out; - } - -out: - fput(file); - return error; -} - static long ioctl_read_file_signature(struct file *f, void __user *arg) { struct incfs_get_file_sig_args __user *args_usr_ptr = arg; @@ -1412,14 +1376,20 @@ static long ioctl_read_file_signature(struct file *f, void __user *arg) if (!df) return -EINVAL; + if (!access_ok(VERIFY_READ, args_usr_ptr, sizeof(args))) + return -EFAULT; if (copy_from_user(&args, args_usr_ptr, sizeof(args)) > 0) return -EINVAL; + if (!access_ok(VERIFY_WRITE, u64_to_user_ptr(args.file_signature), + args.file_signature_buf_size)) + return -EFAULT; + sig_buf_size = args.file_signature_buf_size; if (sig_buf_size > INCFS_MAX_SIGNATURE_SIZE) return -E2BIG; - sig_buffer = kzalloc(sig_buf_size, GFP_NOFS | __GFP_COMP); + sig_buffer = kzalloc(sig_buf_size, GFP_NOFS); if (!sig_buffer) return -ENOMEM; @@ -1447,30 +1417,6 @@ out: 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 ((uintptr_t)f->private_data != CAN_FILL) - return -EPERM; - - 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)); @@ -1478,14 +1424,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_PERMIT_FILL: - 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; } @@ -1692,7 +1632,6 @@ static int final_file_delete(struct mount_info *mi, if (d_really_is_positive(index_file_dentry)) error = incfs_unlink(index_file_dentry); out: - dput(index_file_dentry); if (error) pr_debug("incfs: delete_file_from_index err:%d\n", error); return error; @@ -1905,8 +1844,8 @@ static int file_open(struct inode *inode, struct file *file) int err = 0; get_incfs_backing_path(file->f_path.dentry, &backing_path); - backing_file = dentry_open( - &backing_path, O_RDWR | O_NOATIME | O_LARGEFILE, mi->mi_owner); + backing_file = dentry_open(&backing_path, O_RDWR | O_NOATIME, + mi->mi_owner); path_put(&backing_path); if (IS_ERR(backing_file)) { @@ -1915,10 +1854,9 @@ static int file_open(struct inode *inode, struct file *file) goto out; } - if (S_ISREG(inode->i_mode)) { + if (S_ISREG(inode->i_mode)) err = make_inode_ready_for_data_ops(mi, inode, backing_file); - file->private_data = (void *)CANT_FILL; - } else if (S_ISDIR(inode->i_mode)) { + else if (S_ISDIR(inode->i_mode)) { struct dir_file *dir = NULL; dir = incfs_open_dir_file(mi, backing_file); @@ -1953,6 +1891,77 @@ 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; + + if (!access_ok(VERIFY_READ, usr_blocks, size)) + return -EFAULT; + + 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 (!access_ok(VERIFY_READ, u64_to_user_ptr(block.data), + block.data_len)) { + error = -EFAULT; + 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 = {}; @@ -1995,7 +2004,6 @@ static void dentry_release(struct dentry *d) if (di) path_put(&di->backing_path); - kfree(d->d_fsdata); d->d_fsdata = NULL; } @@ -2036,117 +2044,15 @@ static void evict_inode(struct inode *inode) clear_inode(inode); } -static int incfs_setattr(struct dentry *dentry, struct iattr *ia) -{ - struct dentry_info *di = get_incfs_dentry(dentry); - struct dentry *backing_dentry; - struct inode *backing_inode; - int error; - - if (ia->ia_valid & ATTR_SIZE) - return -EINVAL; - - if (!di) - return -EINVAL; - backing_dentry = di->backing_path.dentry; - if (!backing_dentry) - return -EINVAL; - - backing_inode = d_inode(backing_dentry); - - /* incfs files are readonly, but the backing files must be writeable */ - if (S_ISREG(backing_inode->i_mode)) { - if ((ia->ia_valid & ATTR_MODE) && (ia->ia_mode & 0222)) - return -EINVAL; - - ia->ia_mode |= 0222; - } - - inode_lock(d_inode(backing_dentry)); - error = notify_change(backing_dentry, ia, NULL); - inode_unlock(d_inode(backing_dentry)); - - if (error) - return error; - - if (S_ISREG(backing_inode->i_mode)) - ia->ia_mode &= ~0222; - - return simple_setattr(dentry, ia); -} - static ssize_t incfs_getxattr(struct dentry *d, const char *name, void *value, size_t size) { struct dentry_info *di = get_incfs_dentry(d); - struct mount_info *mi = get_mount_info(d->d_sb); - char *stored_value; - size_t stored_size; - - if (di && di->backing_path.dentry) - return vfs_getxattr(di->backing_path.dentry, name, value, size); - - if (strcmp(name, "security.selinux")) - return -ENODATA; - - if (!strcmp(d->d_iname, INCFS_PENDING_READS_FILENAME)) { - stored_value = mi->pending_read_xattr; - stored_size = mi->pending_read_xattr_size; - } else if (!strcmp(d->d_iname, INCFS_LOG_FILENAME)) { - stored_value = mi->log_xattr; - stored_size = mi->log_xattr_size; - } else { - return -ENODATA; - } - - if (!stored_value) - return -ENODATA; - - if (stored_size > size) - return -E2BIG; - - memcpy(value, stored_value, stored_size); - return stored_size; - -} - -static ssize_t incfs_setxattr(struct dentry *d, const char *name, - const void *value, size_t size, int flags) -{ - struct dentry_info *di = get_incfs_dentry(d); - struct mount_info *mi = get_mount_info(d->d_sb); - void **stored_value; - size_t *stored_size; - - if (di && di->backing_path.dentry) - return vfs_setxattr(di->backing_path.dentry, name, value, size, - flags); - - if (strcmp(name, "security.selinux")) - return -ENODATA; - - if (size > INCFS_MAX_FILE_ATTR_SIZE) - return -E2BIG; - - if (!strcmp(d->d_iname, INCFS_PENDING_READS_FILENAME)) { - stored_value = &mi->pending_read_xattr; - stored_size = &mi->pending_read_xattr_size; - } else if (!strcmp(d->d_iname, INCFS_LOG_FILENAME)) { - stored_value = &mi->log_xattr; - stored_size = &mi->log_xattr_size; - } else { + if (!di || !di->backing_path.dentry) return -ENODATA; - } - - kfree (*stored_value); - *stored_value = kzalloc(size, GFP_NOFS); - if (!*stored_value) - return -ENOMEM; - memcpy(*stored_value, value, size); - *stored_size = size; - return 0; + return vfs_getxattr(di->backing_path.dentry, name, value, size); } static ssize_t incfs_listxattr(struct dentry *d, char *list, size_t size) @@ -2246,7 +2152,7 @@ struct dentry *incfs_mount_fs(struct file_system_type *type, int flags, path_put(&backing_dir_path); sb->s_flags |= SB_ACTIVE; - pr_debug("incfs: mount\n"); + pr_debug("infs: mount\n"); return dget(sb->s_root); err: sb->s_fs_info = NULL; @@ -2267,11 +2173,12 @@ static int incfs_remount_fs(struct super_block *sb, int *flags, char *data) if (err) return err; - err = incfs_realloc_mount_info(mi, &options); - if (err) - return err; + if (mi->mi_options.read_timeout_ms != options.read_timeout_ms) { + mi->mi_options.read_timeout_ms = options.read_timeout_ms; + pr_debug("incfs: new timeout_ms=%d", options.read_timeout_ms); + } - pr_debug("incfs: remount\n"); + pr_debug("infs: remount\n"); return 0; } @@ -2279,7 +2186,7 @@ void incfs_kill_sb(struct super_block *sb) { struct mount_info *mi = sb->s_fs_info; - pr_debug("incfs: unmount\n"); + pr_debug("infs: unmount\n"); incfs_free_mount_info(mi); generic_shutdown_super(sb); } diff --git a/include/uapi/linux/incrementalfs.h b/include/uapi/linux/incrementalfs.h index 13c3d5173e14..787049031cca 100644 --- a/include/uapi/linux/incrementalfs.h +++ b/include/uapi/linux/incrementalfs.h @@ -35,8 +35,6 @@ #define INCFS_XATTR_METADATA_NAME (XATTR_USER_PREFIX "incfs.metadata") #define INCFS_MAX_SIGNATURE_SIZE 8096 -#define INCFS_SIGNATURE_VERSION 2 -#define INCFS_SIGNATURE_SECTIONS 2 #define INCFS_IOCTL_BASE_CODE 'g' @@ -48,49 +46,7 @@ /* Read file signature */ #define INCFS_IOC_READ_FILE_SIGNATURE \ - _IOR(INCFS_IOCTL_BASE_CODE, 31, struct incfs_get_file_sig_args) - -/* - * Fill in one or more data block. This may only be called on a handle - * passed as a parameter to INCFS_IOC_PERMIT_FILLING - * - * 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) - -/* - * Permit INCFS_IOC_FILL_BLOCKS on the given file descriptor - * May only be called on .pending_reads file - * - * Returns 0 on success or error - */ -#define INCFS_IOC_PERMIT_FILL \ - _IOW(INCFS_IOCTL_BASE_CODE, 33, struct incfs_permit_fill) - -/* - * Fills buffer with ranges of populated blocks - * - * Returns 0 if all ranges written - * error otherwise - * - * Either way, range_buffer_size_out is set to the number - * of bytes written. Should be set to 0 by caller. The ranges - * filled are valid, but if an error was returned there might - * be more ranges to come. - * - * Ranges are ranges of filled blocks: - * - * 1 2 7 9 - * - * means blocks 1, 2, 7, 8, 9 are filled, 0, 3, 4, 5, 6 and 10 on - * are not - * - * If hashing is enabled for the file, the hash blocks are simply - * treated as though they immediately followed the data blocks. - */ -#define INCFS_IOC_GET_FILLED_BLOCKS \ - _IOR(INCFS_IOCTL_BASE_CODE, 34, struct incfs_get_filled_blocks_args) + _IOWR(INCFS_IOCTL_BASE_CODE, 31, struct incfs_get_file_sig_args) enum incfs_compression_alg { COMPRESSION_NONE = 0, @@ -125,9 +81,10 @@ struct incfs_pending_read_info { }; /* - * Description of a data or hash block to add to a data file. + * A struct to be written into a control file to load a data or hash + * block to a data file. */ -struct incfs_fill_block { +struct incfs_new_data_block { /* Index of a data block. */ __u32 block_index; @@ -150,42 +107,63 @@ struct incfs_fill_block { /* Values from enum incfs_block_flags */ __u8 flags; + /* Reserved - must be 0 */ __u16 reserved1; + /* Reserved - must be 0 */ __u32 reserved2; + /* Reserved - must be 0 */ __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; -}; - -/* - * Permit INCFS_IOC_FILL_BLOCKS on the given file descriptor - * May only be called on .pending_reads file - * - * Argument for INCFS_IOC_PERMIT_FILL - */ -struct incfs_permit_fill { - /* File to permit fills on */ - __u32 file_descriptor; -}; - enum incfs_hash_tree_algorithm { INCFS_HASH_TREE_NONE = 0, INCFS_HASH_TREE_SHA256 = 1 }; +struct incfs_file_signature_info { + /* + * A pointer to file's root hash (if determined != 0) + * Actual hash size determined by hash_tree_alg. + * Size of the buffer should be at least INCFS_MAX_HASH_SIZE + * + * Equivalent to: u8 *root_hash; + */ + __aligned_u64 root_hash; + + /* + * A pointer to additional data that was attached to the root hash + * before signing. + * + * Equivalent to: u8 *additional_data; + */ + __aligned_u64 additional_data; + + /* Size of additional data. */ + __u32 additional_data_size; + + /* Reserved - must be 0 */ + __u32 reserved1; + + /* + * A pointer to pkcs7 signature DER blob. + * + * Equivalent to: u8 *signature; + */ + __aligned_u64 signature; + + + /* Size of pkcs7 signature DER blob */ + __u32 signature_size; + + /* Reserved - must be 0 */ + __u32 reserved2; + + /* Value from incfs_hash_tree_algorithm */ + __u8 hash_tree_alg; +}; + /* * Create a new file or directory. */ @@ -203,8 +181,10 @@ struct incfs_new_file_args { */ __u16 mode; + /* Reserved - must be 0 */ __u16 reserved1; + /* Reserved - must be 0 */ __u32 reserved2; /* @@ -237,33 +217,16 @@ struct incfs_new_file_args { */ __u32 file_attr_len; + /* Reserved - must be 0 */ __u32 reserved4; - /* - * Points to an APK V4 Signature data blob - * Signature must have two sections - * Format is: - * u32 version - * u32 size_of_hash_info_section - * u8 hash_info_section[] - * u32 size_of_signing_info_section - * u8 signing_info_section[] - * - * Note that incfs does not care about what is in signing_info_section - * - * hash_info_section has following format: - * u32 hash_algorithm; // Must be SHA256 == 1 - * u8 log2_blocksize; // Must be 12 for 4096 byte blocks - * u32 salt_size; - * u8 salt[]; - * u32 hash_size; - * u8 root_hash[]; - */ + /* struct incfs_file_signature_info *signature_info; */ __aligned_u64 signature_info; - /* Size of signature_info */ - __aligned_u64 signature_size; + /* Reserved - must be 0 */ + __aligned_u64 reserved5; + /* Reserved - must be 0 */ __aligned_u64 reserved6; }; @@ -289,46 +252,4 @@ struct incfs_get_file_sig_args { __u32 file_signature_len_out; }; -struct incfs_filled_range { - __u32 begin; - __u32 end; -}; - -/* - * Request ranges of filled blocks - * Argument for INCFS_IOC_GET_FILLED_BLOCKS - */ -struct incfs_get_filled_blocks_args { - /* - * A buffer to populate with ranges of filled blocks - * - * Equivalent to struct incfs_filled_ranges *range_buffer - */ - __aligned_u64 range_buffer; - - /* Size of range_buffer */ - __u32 range_buffer_size; - - /* Start index to read from */ - __u32 start_index; - - /* - * End index to read to. 0 means read to end. This is a range, - * so incfs will read from start_index to end_index - 1 - */ - __u32 end_index; - - /* Actual number of blocks in file */ - __u32 total_blocks_out; - - /* The number of data blocks in file */ - __u32 data_blocks_out; - - /* Number of bytes written to range buffer */ - __u32 range_buffer_size_out; - - /* Sector scanned up to, if the call was interrupted */ - __u32 index_out; -}; - #endif /* _UAPI_LINUX_INCREMENTALFS_H */ diff --git a/tools/testing/selftests/filesystems/incfs/Makefile b/tools/testing/selftests/filesystems/incfs/Makefile index 5b2e627ce883..1f13573d3617 100644 --- a/tools/testing/selftests/filesystems/incfs/Makefile +++ b/tools/testing/selftests/filesystems/incfs/Makefile @@ -1,11 +1,18 @@ # SPDX-License-Identifier: GPL-2.0 -CFLAGS += -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -Wall -CFLAGS += -I../.. -I../../../../.. +CFLAGS += -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -Wall -lssl -lcrypto -llz4 +CFLAGS += -I../../../../../usr/include/ +CFLAGS += -I../../../../include/uapi/ +CFLAGS += -I../../../../lib -LDLIBS := -llz4 -lcrypto EXTRA_SOURCES := utils.c -TEST_GEN_PROGS := incfs_test +CFLAGS += $(EXTRA_SOURCES) -$(TEST_GEN_PROGS): $(EXTRA_SOURCES) +TEST_GEN_PROGS := incfs_test include ../../lib.mk + +$(OUTPUT)incfs_test: incfs_test.c $(EXTRA_SOURCES) +all: $(OUTPUT)incfs_test + +clean: + rm -rf $(OUTPUT)incfs_test *.o diff --git a/tools/testing/selftests/filesystems/incfs/config b/tools/testing/selftests/filesystems/incfs/config new file mode 100644 index 000000000000..b6749837a318 --- /dev/null +++ b/tools/testing/selftests/filesystems/incfs/config @@ -0,0 +1 @@ +CONFIG_INCREMENTAL_FS=y \ No newline at end of file diff --git a/tools/testing/selftests/filesystems/incfs/incfs_test.c b/tools/testing/selftests/filesystems/incfs/incfs_test.c index 6809399eac97..dd70e019dc4c 100644 --- a/tools/testing/selftests/filesystems/incfs/incfs_test.c +++ b/tools/testing/selftests/filesystems/incfs/incfs_test.c @@ -2,31 +2,31 @@ /* * Copyright 2018 Google LLC */ -#include -#include -#include -#include -#include -#include -#include -#include #include -#include #include - -#include -#include #include +#include +#include +#include +#include +#include #include #include - +#include +#include +#include +#include +#include #include #include -#include +#include "../../kselftest.h" +#include "lz4.h" #include "utils.h" +#define __packed __attribute__((__packed__)) + #define TEST_FAILURE 1 #define TEST_SUCCESS 0 #define INCFS_MAX_MTREE_LEVELS 8 @@ -69,6 +69,101 @@ struct linux_dirent64 { char d_name[0]; } __packed; +/* + * The certificate below and the private key were created by calling: + * openssl req -x509 -newkey rsa:4096 -keyout private.key -out cert.crt + * -days 1000 -sha256 -nodes -outform PEM -subj + * "/C=US/ST=WA/L=Kirkland/O=Example/OU=Org/CN=www.example.com" + */ +char x509_cert[] = +"-----BEGIN CERTIFICATE-----\n" +"MIIFvzCCA6egAwIBAgIUXpwqelEljm6BBllRQGHLrls2MYgwDQYJKoZIhvcNAQEL\n" +"BQAwbzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcM\n" +"CEtpcmtsYW5kMRAwDgYDVQQKDAdFeGFtcGxlMQwwCgYDVQQLDANPcmcxGDAWBgNV\n" +"BAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xOTA4MDgyMzA3MDZaFw0yMjA1MDQyMzA3\n" +"MDZaMG8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMREwDwYDVQQH\n" +"DAhLaXJrbGFuZDEQMA4GA1UECgwHRXhhbXBsZTEMMAoGA1UECwwDT3JnMRgwFgYD\n" +"VQQDDA93d3cuZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n" +"AoICAQC1LuFW/lDV/GflqFMz7RDvFFgWld982ZuDJRaK55JNj+MI4RZNL61PDw43\n" +"NeeJtqUoVxSLS9wHURjSjD/CV5GudUOnzGfbwFlLko+jhYRT4HNFS+5ys1FEJLtA\n" +"uYcY4P9GHQEXYUX+ue82A2kJ91oY6G3vCQYJFiGteb6TRDICmug31x4pBfB8rOdt\n" +"4/NXS/Dn+S0/mJlxw34IKfqrlFjzUziRZtAWWqDcfxFDUizSggkdXIUq4GY38RAD\n" +"qGewNNCab3ClJDP7/M32BhSNgsIKhgtSTM2+ocfvBhwup+BjV6UbL21DPAshlolV\n" +"gSL1HM2jin5bi4bpFMreY0LXwFih87/6AVSfQHY9TZrombVZnMxvB7NG1NCSwDBT\n" +"qjjFb3oiSMugJzY+MhISM754m46fwUyHZ1ylWCLJEU8kQ5A1q9vvqMcaDa4uTGP3\n" +"UgC6SyVmZxG2o+AO6m8TRTCtqHN41mPTM9HK4T1UyuzVpykSc2LlYkKE517SyEiV\n" +"XDmotNb2myXNYHHTjRYNxkq75Lbii2I4Q4z8XtDngaIrhZqACKSqIt2CocGjx61S\n" +"oxKWi+LGa7B4NaCMjz1LnaOIsXn1rJDRnUWL49T42g4kOi/5QaC2JDygfefw1hAb\n" +"uxkq9EYUDg+w9broltiBf4rKAnw8JMySARnyPZbj0lhZK3va5wIDAQABo1MwUTAd\n" +"BgNVHQ4EFgQUo6JN3gY2yGbzOTNj8Al7hNB3rw0wHwYDVR0jBBgwFoAUo6JN3gY2\n" +"yGbzOTNj8Al7hNB3rw0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC\n" +"AgEAQb3pJqOzM4whfNVdpEOswd1EApcWNM1ps9iTlEEjDoRv9F7F1PW0uXCIpk3B\n" +"j5JgCmIxAcPnzj42rduRSx421hHMZhbAIWI/JL4ZSF64qlG0YrmJDXlJgSMoyst5\n" +"biUqeWgO7Js5udPt3zhkeA62z3hGM6dE5B3k7gHTaKKtK17+UeR9imZKsOK8GBnM\n" +"rxMPI6XghxxAK2OQ/r09DHDiyf/GxgOE46oknfXfMPx3HaSvDKrZUTZ+UvVbM5c2\n" +"5eXOgH5UO/e4llLknJK7CoP/R6G7pV44iT4t4t9FMnvCYvavAHwfR+6z5vTF3o8a\n" +"wd80fC8z1vfLsIPLROdzBl9rGCvv536fPiEA677CM1AZkjfT0a9DVzrE1NDvuCUF\n" +"0KgEdiNwux+hO6dbTyiS38yPT6TbpoWJptJmFhFkC4hGvUgoX/TI0covSyf74VRH\n" +"k3BHojOBMYiX1K66xoN7fhlGK8cith3L0XXPB8CgSEUPWURvm8RCaGuX2T3FZomF\n" +"BCnNpN+WNnN3Yf4OkjtuvtxxktUU7pfVLsUxrdpo/ph4rWm6U83VT/Zlq92aF4vW\n" +"QJ+7uraQFip7e+Gy9g3UJINm3B7b1C4ch/Z/upCZESOI/23sVGzkfTgOrS+23i6/\n" +"Vi9YW75zySC2FCa1AWMS1NmS5qfDSycJUgD6YvOUg0C54ZI=\n" +"-----END CERTIFICATE-----"; + +char private_key[] = +"-----BEGIN PRIVATE KEY-----\n" +"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\n" +"GyPbW1yZa5vE302a1Cr0Cd7RC4AFAAw=\n" +"-----END PRIVATE KEY-----"; + struct test_files_set get_test_files_set(void) { static struct test_file files[] = { @@ -195,7 +290,7 @@ char *bin2hex(char *dst, const void *src, size_t count) return dst; } -static char *get_index_filename(const char *mnt_dir, incfs_uuid_t id) +static char *get_index_filename(char *mnt_dir, incfs_uuid_t id) { char path[FILENAME_MAX]; char str_id[1 + 2 * sizeof(id)]; @@ -206,43 +301,15 @@ static char *get_index_filename(const char *mnt_dir, incfs_uuid_t id) return strdup(path); } -int open_file_by_id(const char *mnt_dir, incfs_uuid_t id, bool use_ioctl) +int open_file_by_id(char *mnt_dir, incfs_uuid_t id) { char *path = get_index_filename(mnt_dir, id); - int cmd_fd = open_commands_file(mnt_dir); - int fd = open(path, O_RDWR | O_CLOEXEC); - struct incfs_permit_fill permit_fill = { - .file_descriptor = fd, - }; - int error = 0; + int fd = open(path, O_RDWR); + free(path); if (fd < 0) { print_error("Can't open file by id."); - error = -errno; - goto out; - } - - if (use_ioctl && ioctl(cmd_fd, INCFS_IOC_PERMIT_FILL, &permit_fill)) { - print_error("Failed to call PERMIT_FILL"); - error = -errno; - goto out; - } - - if (ioctl(fd, INCFS_IOC_PERMIT_FILL, &permit_fill) != -1 || - errno != EPERM) { - print_error( - "Successfully called PERMIT_FILL on non pending_read file"); return -errno; - goto out; - } - -out: - free(path); - close(cmd_fd); - - if (error) { - close(fd); - return error; } return fd; @@ -276,18 +343,20 @@ 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_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), - }; + struct incfs_new_data_block *block_buf = + calloc(block_count, sizeof(*block_buf)); ssize_t write_res = 0; - int fd = -1; + int fd; int error = 0; int i = 0; int blocks_written = 0; + fd = open_file_by_id(mnt_dir, file->id); + if (fd <= 0) { + error = -errno; + goto out; + } + for (i = 0; i < block_count; i++) { int block_index = blocks[i]; bool compress = (file->index + block_index) % 2 == 0; @@ -335,33 +404,17 @@ 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) { - fd = open_file_by_id(mnt_dir, file->id, false); - if (fd < 0) { - error = -errno; - goto out; - } - write_res = ioctl(fd, INCFS_IOC_FILL_BLOCKS, &fill_blocks); - if (write_res >= 0) { - ksft_print_msg("Wrote to file via normal fd error\n"); - error = -EPERM; - goto out; - } - - close(fd); - fd = open_file_by_id(mnt_dir, file->id, true); - if (fd < 0) { - error = -errno; - goto out; - } - write_res = ioctl(fd, INCFS_IOC_FILL_BLOCKS, &fill_blocks); + write_res = write(fd, block_buf, sizeof(*block_buf) * i); if (write_res < 0) error = -errno; else - blocks_written = write_res; + blocks_written = write_res / sizeof(*block_buf); } if (error) { ksft_print_msg( @@ -446,7 +499,7 @@ static loff_t read_whole_file(char *filename) loff_t bytes_read = 0; uint8_t buff[16 * 1024]; - fd = open(filename, O_RDONLY | O_CLOEXEC); + fd = open(filename, O_RDONLY); if (fd <= 0) return fd; @@ -478,7 +531,7 @@ static int read_test_file(uint8_t *buf, size_t len, char *filename, size_t bytes_to_read = len; off_t offset = ((off_t)block_idx) * INCFS_DATA_FILE_BLOCK_SIZE; - fd = open(filename, O_RDONLY | O_CLOEXEC); + fd = open(filename, O_RDONLY); if (fd <= 0) return fd; @@ -667,6 +720,8 @@ static int build_mtree(struct test_file *file) int tree_lvl_index[INCFS_MAX_MTREE_LEVELS] = {}; int tree_lvl_count[INCFS_MAX_MTREE_LEVELS] = {}; int levels_count = 0; + char data_to_sign[256] = {}; + int sig_data_size; int i, level; if (file->size == 0) @@ -693,9 +748,8 @@ static int build_mtree(struct test_file *file) if (block_count == 1) { int seed = get_file_block_seed(file->index, 0); - memset(data, 0, INCFS_DATA_FILE_BLOCK_SIZE); rnd_buf((uint8_t *)data, file->size, seed); - sha256(data, INCFS_DATA_FILE_BLOCK_SIZE, file->root_hash); + sha256(data, file->size, file->root_hash); return 0; } @@ -710,13 +764,11 @@ static int build_mtree(struct test_file *file) int seed = get_file_block_seed(file->index, i); char *hash_ptr = file->mtree[block_index].data + block_off; - if (file->size - offset < block_size) { + if (file->size - offset < block_size) block_size = file->size - offset; - memset(data, 0, INCFS_DATA_FILE_BLOCK_SIZE); - } rnd_buf((uint8_t *)data, block_size, seed); - sha256(data, INCFS_DATA_FILE_BLOCK_SIZE, hash_ptr); + sha256(data, block_size, hash_ptr); } /* Build higher levels of hash tree. */ @@ -740,6 +792,19 @@ static int build_mtree(struct test_file *file) sha256(file->mtree[0].data, INCFS_DATA_FILE_BLOCK_SIZE, file->root_hash); + /* Calculating digital signature */ + snprintf(file->sig.add_data, sizeof(file->sig.add_data), "%ld", + file->size); + memcpy(data_to_sign, file->root_hash, SHA256_DIGEST_SIZE); + memcpy(data_to_sign + SHA256_DIGEST_SIZE, file->sig.add_data, + strlen(file->sig.add_data)); + sig_data_size = SHA256_DIGEST_SIZE + strlen(file->sig.add_data); + if (!sign_pkcs7(data_to_sign, sig_data_size, private_key, x509_cert, + &file->sig.data, &file->sig.size)) { + ksft_print_msg("Signing failed.\n"); + return -EINVAL; + } + return 0; } @@ -748,21 +813,21 @@ static int load_hash_tree(const char *mount_dir, struct test_file *file) int err; int i; int fd; - 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 (fill_blocks.count == 0) + size_t blocks_size = + file->mtree_block_count * sizeof(struct incfs_new_data_block); + struct incfs_new_data_block *blocks = NULL; + char *file_path; + + if (blocks_size == 0) return 0; - if (!fill_block_array) + blocks = malloc(blocks_size); + if (!blocks) return -ENOMEM; - fill_blocks.fill_blocks = ptr_to_u64(fill_block_array); - for (i = 0; i < fill_blocks.count; i++) { - fill_block_array[i] = (struct incfs_fill_block){ + for (i = 0; i < file->mtree_block_count; i++) { + blocks[i] = (struct incfs_new_data_block){ .block_index = i, .data_len = INCFS_DATA_FILE_BLOCK_SIZE, .data = ptr_to_u64(file->mtree[i].data), @@ -770,28 +835,18 @@ static int load_hash_tree(const char *mount_dir, struct test_file *file) }; } - fd = open_file_by_id(mount_dir, file->id, false); + file_path = concat_file_name(mount_dir, file->name); + fd = open(file_path, O_RDWR); + free(file_path); if (fd < 0) { err = errno; goto failure; } - err = ioctl(fd, INCFS_IOC_FILL_BLOCKS, &fill_blocks); + err = write(fd, blocks, blocks_size); close(fd); - if (err >= 0) { - err = -EPERM; - goto failure; - } - - fd = open_file_by_id(mount_dir, file->id, true); - if (fd < 0) { - err = errno; - goto failure; - } - err = ioctl(fd, INCFS_IOC_FILL_BLOCKS, &fill_blocks); - close(fd); - if (err < fill_blocks.count) + if (err < blocks_size) err = errno; else { err = 0; @@ -799,7 +854,7 @@ static int load_hash_tree(const char *mount_dir, struct test_file *file) } failure: - free(fill_block_array); + free(blocks); return err; } @@ -911,7 +966,7 @@ static bool iterate_directory(char *dir_to_iterate, bool root, int file_count) int i; /* Test directory iteration */ - int fd = open(dir_to_iterate, O_RDONLY | O_DIRECTORY | O_CLOEXEC); + int fd = open(dir_to_iterate, O_RDONLY | O_DIRECTORY); if (fd < 0) { print_error("Can't open directory\n"); @@ -1112,7 +1167,7 @@ static int basic_file_ops_test(char *mount_dir) char *path = concat_file_name(mount_dir, file->name); int fd; - fd = open(path, O_RDWR | O_CLOEXEC); + fd = open(path, O_RDWR); free(path); if (fd <= 0) { print_error("Can't open file"); @@ -1219,6 +1274,13 @@ 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", @@ -1417,6 +1479,7 @@ 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, @@ -1425,7 +1488,14 @@ 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); @@ -1812,6 +1882,162 @@ failure: return TEST_FAILURE; } +static int signature_test(char *mount_dir) +{ + struct test_files_set test = get_test_files_set(); + const int file_num = test.files_count; + int i = 0; + unsigned char sig_buf[INCFS_MAX_SIGNATURE_SIZE]; + char *backing_dir; + int cmd_fd = -1; + + backing_dir = create_backing_dir(mount_dir); + if (!backing_dir) + goto failure; + + /* Mount FS and release the backing file. (10s wait time) */ + if (mount_fs(mount_dir, backing_dir, 10000) != 0) + goto failure; + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + /* Write hashes and data. */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + int res; + + build_mtree(file); + + res = crypto_emit_file(cmd_fd, NULL, file->name, &file->id, + file->size, file->root_hash, + file->sig.data, file->sig.size, file->sig.add_data); + + if (res) { + ksft_print_msg("Emit failed for %s. error: %s\n", + file->name, strerror(-res)); + goto failure; + } + + 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; + } + } + + /* Validate data */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + int sig_len; + char *path; + int fd; + + if (validate_test_file_content(mount_dir, file) < 0) + goto failure; + + path = concat_file_name(mount_dir, file->name); + fd = open(path, O_RDWR); + free(path); + if (fd < 0) { + print_error("Can't open file"); + goto failure; + } + + sig_len = get_file_signature(fd, sig_buf, ARRAY_SIZE(sig_buf)); + + if (close(fd)) { + print_error("Can't close file"); + goto failure; + } + + if (sig_len < 0) { + ksft_print_msg("Can't load signature %s. error: %s\n", + file->name, strerror(-sig_len)); + goto failure; + } + + if (sig_len != file->sig.size || + memcmp(sig_buf, file->sig.data, sig_len)) { + ksft_print_msg("Signature mismatch %s.\n", + file->name); + goto failure; + } + } + + /* Unmount and mount again, to make sure the signature is persistent. */ + close(cmd_fd); + cmd_fd = -1; + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + if (mount_fs(mount_dir, backing_dir, 50) != 0) + goto failure; + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + /* Validate data again */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + int sig_len; + char *path; + int fd; + + if (validate_test_file_content(mount_dir, file) < 0) + goto failure; + + path = concat_file_name(mount_dir, file->name); + fd = open(path, O_RDWR); + free(path); + if (fd < 0) { + print_error("Can't open file"); + goto failure; + } + + sig_len = get_file_signature(fd, sig_buf, ARRAY_SIZE(sig_buf)); + + if (close(fd)) { + print_error("Can't close file"); + goto failure; + } + + if (sig_len < 0) { + ksft_print_msg("Can't load signature %s. error: %s\n", + file->name, strerror(-sig_len)); + goto failure; + } + if (sig_len != file->sig.size || + memcmp(sig_buf, file->sig.data, sig_len)) { + ksft_print_msg("Signature mismatch %s.\n", + file->name); + goto failure; + } + } + + /* Final unmount */ + close(cmd_fd); + cmd_fd = -1; + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + return TEST_SUCCESS; + +failure: + close(cmd_fd); + free(backing_dir); + umount(mount_dir); + return TEST_FAILURE; +} + static int hash_tree_test(char *mount_dir) { char *backing_dir; @@ -1840,8 +2066,8 @@ static int hash_tree_test(char *mount_dir) build_mtree(file); res = crypto_emit_file(cmd_fd, NULL, file->name, &file->id, - file->size, file->root_hash, - file->sig.add_data); + file->size, file->root_hash, + file->sig.data, file->sig.size, file->sig.add_data); if (i == corrupted_file_idx) { /* Corrupt third blocks hash */ @@ -1932,88 +2158,48 @@ failure: return TEST_FAILURE; } -enum expected_log { FULL_LOG, NO_LOG, PARTIAL_LOG }; - -static int validate_logs(char *mount_dir, int log_fd, struct test_file *file, - enum expected_log expected_log) +static int validate_logs(char *mount_dir, int log_fd, struct test_file *file) { uint8_t data[INCFS_DATA_FILE_BLOCK_SIZE]; - struct incfs_pending_read_info prs[2048] = {}; + struct incfs_pending_read_info prs[100] = {}; int prs_size = ARRAY_SIZE(prs); int block_cnt = 1 + (file->size - 1) / INCFS_DATA_FILE_BLOCK_SIZE; - int expected_read_block_cnt; int res; int read_count; - int i, j; + int i; char *filename = concat_file_name(mount_dir, file->name); int fd; - fd = open(filename, O_RDONLY | O_CLOEXEC); + fd = open(filename, O_RDONLY); free(filename); if (fd <= 0) return TEST_FAILURE; if (block_cnt > prs_size) block_cnt = prs_size; - expected_read_block_cnt = block_cnt; for (i = 0; i < block_cnt; i++) { res = pread(fd, data, sizeof(data), INCFS_DATA_FILE_BLOCK_SIZE * i); - - /* Make some read logs of type SAME_FILE_NEXT_BLOCK */ - if (i % 10 == 0) - usleep(20000); - - /* Skip some blocks to make logs of type SAME_FILE */ - if (i % 10 == 5) { - ++i; - --expected_read_block_cnt; - } - if (res <= 0) goto failure; } - read_count = wait_for_pending_reads( - log_fd, expected_log == NO_LOG ? 10 : 0, prs, prs_size); - if (expected_log == NO_LOG) { - if (read_count == 0) - goto success; - if (read_count < 0) - ksft_print_msg("Error reading logged reads %s.\n", - strerror(-read_count)); - else - ksft_print_msg("Somehow read empty logs.\n"); - goto failure; - } - + read_count = wait_for_pending_reads(log_fd, 0, prs, prs_size); if (read_count < 0) { ksft_print_msg("Error reading logged reads %s.\n", strerror(-read_count)); goto failure; } - i = 0; - if (expected_log == PARTIAL_LOG) { - if (read_count == 0) { - ksft_print_msg("No logs %s.\n", file->name); - goto failure; - } - - for (i = 0, j = 0; j < expected_read_block_cnt - read_count; - i++, j++) - if (i % 10 == 5) - ++i; - - } else if (read_count != expected_read_block_cnt) { + if (read_count != block_cnt) { ksft_print_msg("Bad log read count %s %d %d.\n", file->name, - read_count, expected_read_block_cnt); + read_count, block_cnt); goto failure; } - for (j = 0; j < read_count; i++, j++) { - struct incfs_pending_read_info *read = &prs[j]; + for (i = 0; i < read_count; i++) { + struct incfs_pending_read_info *read = &prs[i]; if (!same_id(&read->file_id, &file->id)) { ksft_print_msg("Bad log read ino %s\n", file->name); @@ -2026,8 +2212,8 @@ static int validate_logs(char *mount_dir, int log_fd, struct test_file *file, goto failure; } - if (j != 0) { - unsigned long psn = prs[j - 1].serial_number; + if (i != 0) { + unsigned long psn = prs[i - 1].serial_number; if (read->serial_number != psn + 1) { ksft_print_msg("Bad log read sn %s %d %d.\n", @@ -2042,12 +2228,7 @@ static int validate_logs(char *mount_dir, int log_fd, struct test_file *file, file->name); goto failure; } - - if (i % 10 == 5) - ++i; } - -success: close(fd); return TEST_SUCCESS; @@ -2061,14 +2242,14 @@ static int read_log_test(char *mount_dir) struct test_files_set test = get_test_files_set(); const int file_num = test.files_count; int i = 0; - int cmd_fd = -1, log_fd = -1, drop_caches = -1; + int cmd_fd = -1, log_fd = -1; char *backing_dir; backing_dir = create_backing_dir(mount_dir); if (!backing_dir) goto failure; - if (mount_fs_opt(mount_dir, backing_dir, "readahead=0", false) != 0) + if (mount_fs_opt(mount_dir, backing_dir, "readahead=0") != 0) goto failure; cmd_fd = open_commands_file(mount_dir); @@ -2076,7 +2257,7 @@ static int read_log_test(char *mount_dir) goto failure; log_fd = open_log_file(mount_dir); - if (log_fd < 0) + if (cmd_fd < 0) ksft_print_msg("Can't open log file.\n"); /* Write data. */ @@ -2095,7 +2276,7 @@ static int read_log_test(char *mount_dir) for (i = 0; i < file_num; i++) { struct test_file *file = &test.files[i]; - if (validate_logs(mount_dir, log_fd, file, FULL_LOG)) + if (validate_logs(mount_dir, log_fd, file)) goto failure; } @@ -2108,7 +2289,7 @@ static int read_log_test(char *mount_dir) goto failure; } - if (mount_fs_opt(mount_dir, backing_dir, "readahead=0", false) != 0) + if (mount_fs_opt(mount_dir, backing_dir, "readahead=0") != 0) goto failure; cmd_fd = open_commands_file(mount_dir); @@ -2116,557 +2297,35 @@ static int read_log_test(char *mount_dir) goto failure; log_fd = open_log_file(mount_dir); - if (log_fd < 0) + if (cmd_fd < 0) ksft_print_msg("Can't open log file.\n"); /* Validate data again */ for (i = 0; i < file_num; i++) { struct test_file *file = &test.files[i]; - if (validate_logs(mount_dir, log_fd, file, FULL_LOG)) + if (validate_logs(mount_dir, log_fd, file)) goto failure; } - /* - * Unmount and mount again with no read log to make sure poll - * doesn't crash - */ + /* Final unmount */ close(cmd_fd); close(log_fd); + free(backing_dir); if (umount(mount_dir) != 0) { print_error("Can't unmout FS"); goto failure; } - if (mount_fs_opt(mount_dir, backing_dir, "readahead=0,rlog_pages=0", - false) != 0) - goto failure; - - log_fd = open_log_file(mount_dir); - if (log_fd < 0) - ksft_print_msg("Can't open log file.\n"); + return TEST_SUCCESS; - /* Validate data again - note should fail this time */ - for (i = 0; i < file_num; i++) { - struct test_file *file = &test.files[i]; - - if (validate_logs(mount_dir, log_fd, file, NO_LOG)) - goto failure; - } - - /* - * Remount and check that logs start working again - */ - drop_caches = open("/proc/sys/vm/drop_caches", O_WRONLY | O_CLOEXEC); - if (drop_caches == -1) - goto failure; - i = write(drop_caches, "3", 1); - close(drop_caches); - if (i != 1) - goto failure; - - if (mount_fs_opt(mount_dir, backing_dir, "readahead=0,rlog_pages=1", - true) != 0) - goto failure; - - /* Validate data again */ - for (i = 0; i < file_num; i++) { - struct test_file *file = &test.files[i]; - - if (validate_logs(mount_dir, log_fd, file, PARTIAL_LOG)) - goto failure; - } - - /* - * Remount and check that logs start working again - */ - drop_caches = open("/proc/sys/vm/drop_caches", O_WRONLY | O_CLOEXEC); - if (drop_caches == -1) - goto failure; - i = write(drop_caches, "3", 1); - close(drop_caches); - if (i != 1) - goto failure; - - if (mount_fs_opt(mount_dir, backing_dir, "readahead=0,rlog_pages=4", - true) != 0) - goto failure; - - /* Validate data again */ - for (i = 0; i < file_num; i++) { - struct test_file *file = &test.files[i]; - - if (validate_logs(mount_dir, log_fd, file, FULL_LOG)) - goto failure; - } - - /* Final unmount */ - close(log_fd); - free(backing_dir); - if (umount(mount_dir) != 0) { - print_error("Can't unmout FS"); - goto failure; - } - - return TEST_SUCCESS; - -failure: - close(cmd_fd); - close(log_fd); - free(backing_dir); - umount(mount_dir); - return TEST_FAILURE; -} - -static int emit_partial_test_file_data(char *mount_dir, struct test_file *file) -{ - int i, j; - int block_cnt = 1 + (file->size - 1) / INCFS_DATA_FILE_BLOCK_SIZE; - int *block_indexes = NULL; - int result = 0; - int blocks_written = 0; - - if (file->size == 0) - return 0; - - /* Emit 2 blocks, skip 2 blocks etc*/ - block_indexes = calloc(block_cnt, sizeof(*block_indexes)); - for (i = 0, j = 0; i < block_cnt; ++i) - if ((i & 2) == 0) { - block_indexes[j] = i; - ++j; - } - - for (i = 0; i < j; i += blocks_written) { - blocks_written = emit_test_blocks(mount_dir, file, - block_indexes + i, j - i); - if (blocks_written < 0) { - result = blocks_written; - goto out; - } - if (blocks_written == 0) { - result = -EIO; - goto out; - } - } -out: - free(block_indexes); - return result; -} - -static int validate_ranges(const char *mount_dir, struct test_file *file) -{ - int block_cnt = 1 + (file->size - 1) / INCFS_DATA_FILE_BLOCK_SIZE; - char *filename = concat_file_name(mount_dir, file->name); - int fd; - struct incfs_filled_range ranges[128]; - struct incfs_get_filled_blocks_args fba = { - .range_buffer = ptr_to_u64(ranges), - .range_buffer_size = sizeof(ranges), - }; - int error = TEST_SUCCESS; - int i; - int range_cnt; - int cmd_fd = -1; - struct incfs_permit_fill permit_fill; - - fd = open(filename, O_RDONLY | O_CLOEXEC); - free(filename); - if (fd <= 0) - return TEST_FAILURE; - - error = ioctl(fd, INCFS_IOC_GET_FILLED_BLOCKS, &fba); - if (error != -1 || errno != EPERM) { - ksft_print_msg("INCFS_IOC_GET_FILLED_BLOCKS not blocked\n"); - error = -EPERM; - goto out; - } - - cmd_fd = open_commands_file(mount_dir); - permit_fill.file_descriptor = fd; - if (ioctl(cmd_fd, INCFS_IOC_PERMIT_FILL, &permit_fill)) { - print_error("INCFS_IOC_PERMIT_FILL failed"); - return -EPERM; - goto out; - } - - error = ioctl(fd, INCFS_IOC_GET_FILLED_BLOCKS, &fba); - if (error && errno != ERANGE) - goto out; - - if (error && errno == ERANGE && block_cnt < 509) - goto out; - - if (!error && block_cnt >= 509) { - error = -ERANGE; - goto out; - } - - if (fba.total_blocks_out != block_cnt) { - error = -EINVAL; - goto out; - } - - if (fba.data_blocks_out != block_cnt) { - error = -EINVAL; - goto out; - } - - range_cnt = (block_cnt + 3) / 4; - if (range_cnt > 128) - range_cnt = 128; - if (range_cnt != fba.range_buffer_size_out / sizeof(*ranges)) { - error = -ERANGE; - goto out; - } - - error = TEST_SUCCESS; - for (i = 0; i < fba.range_buffer_size_out / sizeof(*ranges) - 1; ++i) - if (ranges[i].begin != i * 4 || ranges[i].end != i * 4 + 2) { - error = -EINVAL; - goto out; - } - - if (ranges[i].begin != i * 4 || - (ranges[i].end != i * 4 + 1 && ranges[i].end != i * 4 + 2)) { - error = -EINVAL; - goto out; - } - - for (i = 0; i < 64; ++i) { - fba.start_index = i * 2; - fba.end_index = i * 2 + 2; - error = ioctl(fd, INCFS_IOC_GET_FILLED_BLOCKS, &fba); - if (error) - goto out; - - if (fba.total_blocks_out != block_cnt) { - error = -EINVAL; - goto out; - } - - if (fba.start_index >= block_cnt) { - if (fba.index_out != fba.start_index) { - error = -EINVAL; - goto out; - } - - break; - } - - if (i % 2) { - if (fba.range_buffer_size_out != 0) { - error = -EINVAL; - goto out; - } - } else { - if (fba.range_buffer_size_out != sizeof(*ranges)) { - error = -EINVAL; - goto out; - } - - if (ranges[0].begin != i * 2) { - error = -EINVAL; - goto out; - } - - if (ranges[0].end != i * 2 + 1 && - ranges[0].end != i * 2 + 2) { - error = -EINVAL; - goto out; - } - } - } - -out: - close(fd); - close(cmd_fd); - return error; -} - -static int get_blocks_test(char *mount_dir) -{ - char *backing_dir; - int cmd_fd = -1; - int i; - struct test_files_set test = get_test_files_set(); - const int file_num = test.files_count; - - backing_dir = create_backing_dir(mount_dir); - if (!backing_dir) - goto failure; - - if (mount_fs_opt(mount_dir, backing_dir, "readahead=0", false) != 0) - goto failure; - - cmd_fd = open_commands_file(mount_dir); - if (cmd_fd < 0) - goto failure; - - /* Write data. */ - for (i = 0; i < file_num; i++) { - struct test_file *file = &test.files[i]; - - if (emit_file(cmd_fd, NULL, file->name, &file->id, file->size, - NULL)) - goto failure; - - if (emit_partial_test_file_data(mount_dir, file)) - goto failure; - } - - for (i = 0; i < file_num; i++) { - struct test_file *file = &test.files[i]; - - if (validate_ranges(mount_dir, file)) - goto failure; - - /* - * The smallest files are filled completely, so this checks that - * the fast get_filled_blocks path is not causing issues - */ - if (validate_ranges(mount_dir, file)) - goto failure; - } - - close(cmd_fd); - umount(mount_dir); - free(backing_dir); - return TEST_SUCCESS; - -failure: - close(cmd_fd); - umount(mount_dir); - free(backing_dir); - return TEST_FAILURE; -} - -static int emit_partial_test_file_hash(char *mount_dir, struct test_file *file) -{ - int err; - int fd; - struct incfs_fill_blocks fill_blocks = { - .count = 1, - }; - struct incfs_fill_block *fill_block_array = - calloc(fill_blocks.count, sizeof(struct incfs_fill_block)); - uint8_t data[INCFS_DATA_FILE_BLOCK_SIZE]; - - if (file->size <= 4096 / 32 * 4096) - return 0; - - if (fill_blocks.count == 0) - return 0; - - if (!fill_block_array) - return -ENOMEM; - fill_blocks.fill_blocks = ptr_to_u64(fill_block_array); - - rnd_buf(data, sizeof(data), 0); - - fill_block_array[0] = - (struct incfs_fill_block){ .block_index = 1, - .data_len = - INCFS_DATA_FILE_BLOCK_SIZE, - .data = ptr_to_u64(data), - .flags = INCFS_BLOCK_FLAGS_HASH }; - - fd = open_file_by_id(mount_dir, file->id, true); - if (fd < 0) { - err = errno; - goto failure; - } - - err = ioctl(fd, INCFS_IOC_FILL_BLOCKS, &fill_blocks); - close(fd); - if (err < fill_blocks.count) - err = errno; - else - err = 0; - -failure: - free(fill_block_array); - return err; -} - -static int validate_hash_ranges(const char *mount_dir, struct test_file *file) -{ - int block_cnt = 1 + (file->size - 1) / INCFS_DATA_FILE_BLOCK_SIZE; - char *filename = concat_file_name(mount_dir, file->name); - int fd; - struct incfs_filled_range ranges[128]; - struct incfs_get_filled_blocks_args fba = { - .range_buffer = ptr_to_u64(ranges), - .range_buffer_size = sizeof(ranges), - }; - int error = TEST_SUCCESS; - int file_blocks = (file->size + INCFS_DATA_FILE_BLOCK_SIZE - 1) / - INCFS_DATA_FILE_BLOCK_SIZE; - int cmd_fd = -1; - struct incfs_permit_fill permit_fill; - - if (file->size <= 4096 / 32 * 4096) - return 0; - - fd = open(filename, O_RDONLY | O_CLOEXEC); - free(filename); - if (fd <= 0) - return TEST_FAILURE; - - error = ioctl(fd, INCFS_IOC_GET_FILLED_BLOCKS, &fba); - if (error != -1 || errno != EPERM) { - ksft_print_msg("INCFS_IOC_GET_FILLED_BLOCKS not blocked\n"); - error = -EPERM; - goto out; - } - - cmd_fd = open_commands_file(mount_dir); - permit_fill.file_descriptor = fd; - if (ioctl(cmd_fd, INCFS_IOC_PERMIT_FILL, &permit_fill)) { - print_error("INCFS_IOC_PERMIT_FILL failed"); - return -EPERM; - goto out; - } - - error = ioctl(fd, INCFS_IOC_GET_FILLED_BLOCKS, &fba); - if (error) - goto out; - - if (fba.total_blocks_out <= block_cnt) { - error = -EINVAL; - goto out; - } - - if (fba.data_blocks_out != block_cnt) { - error = -EINVAL; - goto out; - } - - if (fba.range_buffer_size_out != sizeof(struct incfs_filled_range)) { - error = -EINVAL; - goto out; - } - - if (ranges[0].begin != file_blocks + 1 || - ranges[0].end != file_blocks + 2) { - error = -EINVAL; - goto out; - } - -out: - close(cmd_fd); - close(fd); - return error; -} - -static int get_hash_blocks_test(char *mount_dir) -{ - char *backing_dir; - int cmd_fd = -1; - int i; - struct test_files_set test = get_test_files_set(); - const int file_num = test.files_count; - - backing_dir = create_backing_dir(mount_dir); - if (!backing_dir) - goto failure; - - if (mount_fs_opt(mount_dir, backing_dir, "readahead=0", false) != 0) - goto failure; - - cmd_fd = open_commands_file(mount_dir); - if (cmd_fd < 0) - goto failure; - - for (i = 0; i < file_num; i++) { - struct test_file *file = &test.files[i]; - - if (crypto_emit_file(cmd_fd, NULL, file->name, &file->id, - file->size, file->root_hash, - file->sig.add_data)) - goto failure; - - if (emit_partial_test_file_hash(mount_dir, file)) - goto failure; - } - - for (i = 0; i < file_num; i++) { - struct test_file *file = &test.files[i]; - - if (validate_hash_ranges(mount_dir, file)) - goto failure; - } - - close(cmd_fd); - umount(mount_dir); - free(backing_dir); - return TEST_SUCCESS; - -failure: - close(cmd_fd); - umount(mount_dir); - free(backing_dir); - return TEST_FAILURE; -} - -static int large_file(char *mount_dir) -{ - char *backing_dir; - int cmd_fd = -1; - int i; - int result = TEST_FAILURE; - uint8_t data[INCFS_DATA_FILE_BLOCK_SIZE] = {}; - int block_count = 3LL * 1024 * 1024 * 1024 / INCFS_DATA_FILE_BLOCK_SIZE; - 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), - }; - incfs_uuid_t id; - int fd; - - backing_dir = create_backing_dir(mount_dir); - if (!backing_dir) - goto failure; - - if (mount_fs_opt(mount_dir, backing_dir, "readahead=0", false) != 0) - goto failure; - - cmd_fd = open_commands_file(mount_dir); - if (cmd_fd < 0) - goto failure; - - if (emit_file(cmd_fd, NULL, "very_large_file", &id, - (uint64_t)block_count * INCFS_DATA_FILE_BLOCK_SIZE, - NULL) < 0) - goto failure; - - for (i = 0; i < block_count; i++) { - block_buf[i].compression = COMPRESSION_NONE; - block_buf[i].block_index = i; - block_buf[i].data_len = INCFS_DATA_FILE_BLOCK_SIZE; - block_buf[i].data = ptr_to_u64(data); - } - - fd = open_file_by_id(mount_dir, id, true); - if (fd < 0) - goto failure; - - if (ioctl(fd, INCFS_IOC_FILL_BLOCKS, &fill_blocks) != block_count) - goto failure; - - if (emit_file(cmd_fd, NULL, "very_very_large_file", &id, 1LL << 40, - NULL) < 0) - goto failure; - - result = TEST_SUCCESS; - -failure: - close(fd); - close(cmd_fd); - return result; -} +failure: + close(cmd_fd); + close(log_fd); + free(backing_dir); + umount(mount_dir); + return TEST_FAILURE; +} static char *setup_mount_dir() { @@ -2702,7 +2361,7 @@ int main(int argc, char *argv[]) // NOTE - this abuses the concept of randomness - do *not* ever do this // on a machine for production use - the device will think it has good // randomness when it does not. - fd = open("/dev/urandom", O_WRONLY | O_CLOEXEC); + fd = open("/dev/urandom", O_WRONLY); count = 4096; for (int i = 0; i < 128; ++i) ioctl(fd, RNDADDTOENTCNT, &count); @@ -2733,11 +2392,9 @@ int main(int argc, char *argv[]) MAKE_TEST(work_after_remount_test), MAKE_TEST(child_procs_waiting_for_data_test), MAKE_TEST(multiple_providers_test), + MAKE_TEST(signature_test), MAKE_TEST(hash_tree_test), MAKE_TEST(read_log_test), - MAKE_TEST(get_blocks_test), - MAKE_TEST(get_hash_blocks_test), - MAKE_TEST(large_file), }; #undef MAKE_TEST @@ -2758,7 +2415,7 @@ int main(int argc, char *argv[]) rmdir(mount_dir); if (fails > 0) - ksft_exit_fail(); + ksft_exit_pass(); else ksft_exit_pass(); return 0; diff --git a/tools/testing/selftests/filesystems/incfs/utils.c b/tools/testing/selftests/filesystems/incfs/utils.c index e194f63ba922..08b8452ad0bc 100644 --- a/tools/testing/selftests/filesystems/incfs/utils.c +++ b/tools/testing/selftests/filesystems/incfs/utils.c @@ -2,31 +2,28 @@ /* * Copyright 2018 Google LLC */ +#include +#include #include -#include +#include +#include #include -#include -#include -#include -#include #include - #include #include -#include -#include - +#include +#include +#include +#include +#include +#include +#include #include #include #include "utils.h" -#ifndef __S_IFREG -#define __S_IFREG S_IFREG -#endif - -int mount_fs(const char *mount_dir, const char *backing_dir, - int read_timeout_ms) +int mount_fs(char *mount_dir, char *backing_dir, int read_timeout_ms) { static const char fs_name[] = INCFS_NAME; char mount_options[512]; @@ -42,107 +39,190 @@ int mount_fs(const char *mount_dir, const char *backing_dir, return result; } -int mount_fs_opt(const char *mount_dir, const char *backing_dir, - const char *opt, bool remount) +int mount_fs_opt(char *mount_dir, char *backing_dir, char *opt) { static const char fs_name[] = INCFS_NAME; int result; - result = mount(backing_dir, mount_dir, fs_name, - remount ? MS_REMOUNT : 0, opt); + result = mount(backing_dir, mount_dir, fs_name, 0, opt); if (result != 0) perror("Error mounting fs."); return result; } -struct hash_section { - uint32_t algorithm; - uint8_t log2_blocksize; - uint32_t salt_size; - /* no salt */ - uint32_t hash_size; - uint8_t hash[SHA256_DIGEST_SIZE]; -} __packed; - -struct signature_blob { - uint32_t version; - uint32_t hash_section_size; - struct hash_section hash_section; - uint32_t signing_section_size; - uint8_t signing_section[]; -} __packed; - -size_t format_signature(void **buf, const char *root_hash, const char *add_data) +int unlink_node(int fd, int parent_ino, char *filename) { - size_t size = sizeof(struct signature_blob) + strlen(add_data) + 1; - struct signature_blob *sb = malloc(size); - - *sb = (struct signature_blob){ - .version = INCFS_SIGNATURE_VERSION, - .hash_section_size = sizeof(struct hash_section), - .hash_section = - (struct hash_section){ - .algorithm = INCFS_HASH_TREE_SHA256, - .log2_blocksize = 12, - .salt_size = 0, - .hash_size = SHA256_DIGEST_SIZE, - }, - .signing_section_size = sizeof(uint32_t) + strlen(add_data) + 1, - }; + return 0; +} + + +static EVP_PKEY *deserialize_private_key(const char *pem_key) +{ + BIO *bio = NULL; + EVP_PKEY *pkey = NULL; + int len = strlen(pem_key); + + bio = BIO_new_mem_buf(pem_key, len); + if (!bio) + return NULL; + + pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + BIO_free(bio); + return pkey; +} + +static X509 *deserialize_cert(const char *pem_cert) +{ + BIO *bio = NULL; + X509 *cert = NULL; + int len = strlen(pem_cert); + + bio = BIO_new_mem_buf(pem_cert, len); + if (!bio) + return NULL; + + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); + BIO_free(bio); + return cert; +} + +bool sign_pkcs7(const void *data_to_sign, size_t data_size, + char *pkey_pem, char *cert_pem, + void **sig_ret, size_t *sig_size_ret) +{ + /* + * PKCS#7 signing flags: + * + * - PKCS7_BINARY signing binary data, so skip MIME translation + * + * - PKCS7_NOATTR omit extra authenticated attributes, such as + * SMIMECapabilities + * + * - PKCS7_PARTIAL PKCS7_sign() creates a handle only, then + * PKCS7_sign_add_signer() can add a signer later. + * This is necessary to change the message digest + * algorithm from the default of SHA-1. Requires + * OpenSSL 1.0.0 or later. + */ + int pkcs7_flags = PKCS7_BINARY | PKCS7_NOATTR | PKCS7_PARTIAL; + void *sig; + size_t sig_size; + BIO *bio = NULL; + PKCS7 *p7 = NULL; + EVP_PKEY *pkey = NULL; + X509 *cert = NULL; + bool ok = false; + + const EVP_MD *md = EVP_sha256(); + + pkey = deserialize_private_key(pkey_pem); + if (!pkey) { + printf("deserialize_private_key failed\n"); + goto out; + } + + cert = deserialize_cert(cert_pem); + if (!cert) { + printf("deserialize_cert failed\n"); + goto out; + } + + bio = BIO_new_mem_buf(data_to_sign, data_size); + if (!bio) + goto out; + + p7 = PKCS7_sign(NULL, NULL, NULL, bio, pkcs7_flags); + if (!p7) { + printf("failed to initialize PKCS#7 signature object\n"); + goto out; + } + + if (!PKCS7_sign_add_signer(p7, cert, pkey, md, pkcs7_flags)) { + printf("failed to add signer to PKCS#7 signature object\n"); + goto out; + } + + if (PKCS7_final(p7, bio, pkcs7_flags) != 1) { + printf("failed to finalize PKCS#7 signature\n"); + goto out; + } + + BIO_free(bio); + bio = BIO_new(BIO_s_mem()); + if (!bio) { + printf("out of memory\n"); + goto out; + } + + if (i2d_PKCS7_bio(bio, p7) != 1) { + printf("failed to DER-encode PKCS#7 signature object\n"); + goto out; + } - memcpy(sb->hash_section.hash, root_hash, SHA256_DIGEST_SIZE); - memcpy((char *)sb->signing_section, add_data, strlen(add_data) + 1); - *buf = sb; - return size; + sig_size = BIO_get_mem_data(bio, &sig); + *sig_ret = malloc(sig_size); + memcpy(*sig_ret, sig, sig_size); + *sig_size_ret = sig_size; + ok = true; +out: + PKCS7_free(p7); + BIO_free(bio); + return ok; } -int crypto_emit_file(int fd, const char *dir, const char *filename, - incfs_uuid_t *id_out, size_t size, const char *root_hash, - const char *add_data) +int crypto_emit_file(int fd, char *dir, char *filename, incfs_uuid_t *id_out, + size_t size, const char *root_hash, char *sig, size_t sig_size, + char *add_data) { int mode = __S_IFREG | 0555; - void *signature; - int error = 0; + struct incfs_file_signature_info sig_info = { + .hash_tree_alg = root_hash + ? INCFS_HASH_TREE_SHA256 + : 0, + .root_hash = ptr_to_u64(root_hash), + .additional_data = ptr_to_u64(add_data), + .additional_data_size = strlen(add_data), + .signature = ptr_to_u64(sig), + .signature_size = sig_size, + }; struct incfs_new_file_args args = { .size = size, .mode = mode, .file_name = ptr_to_u64(filename), .directory_path = ptr_to_u64(dir), + .signature_info = ptr_to_u64(&sig_info), .file_attr = 0, .file_attr_len = 0 }; - args.signature_size = format_signature(&signature, root_hash, add_data); - args.signature_info = ptr_to_u64(signature); - md5(filename, strlen(filename), (char *)args.file_id.bytes); - if (ioctl(fd, INCFS_IOC_CREATE_FILE, &args) != 0) { - error = -errno; - goto out; - } + if (ioctl(fd, INCFS_IOC_CREATE_FILE, &args) != 0) + return -errno; *id_out = args.file_id; - -out: - free(signature); - return error; + return 0; } -int emit_file(int fd, const char *dir, const char *filename, - incfs_uuid_t *id_out, size_t size, const char *attr) + +int emit_file(int fd, char *dir, char *filename, incfs_uuid_t *id_out, + size_t size, char *attr) { int mode = __S_IFREG | 0555; - struct incfs_new_file_args args = { .size = size, - .mode = mode, - .file_name = ptr_to_u64(filename), - .directory_path = ptr_to_u64(dir), - .signature_info = ptr_to_u64(NULL), - .signature_size = 0, - .file_attr = ptr_to_u64(attr), - .file_attr_len = - attr ? strlen(attr) : 0 }; + struct incfs_file_signature_info sig_info = { + .hash_tree_alg = 0, + .root_hash = ptr_to_u64(NULL) + }; + struct incfs_new_file_args args = { + .size = size, + .mode = mode, + .file_name = ptr_to_u64(filename), + .directory_path = ptr_to_u64(dir), + .signature_info = ptr_to_u64(&sig_info), + .file_attr = ptr_to_u64(attr), + .file_attr_len = attr ? strlen(attr) : 0 + }; md5(filename, strlen(filename), (char *)args.file_id.bytes); @@ -170,7 +250,7 @@ int get_file_signature(int fd, unsigned char *buf, int buf_size) return -errno; } -loff_t get_file_size(const char *name) +loff_t get_file_size(char *name) { struct stat st; @@ -179,27 +259,27 @@ loff_t get_file_size(const char *name) return -ENOENT; } -int open_commands_file(const char *mount_dir) +int open_commands_file(char *mount_dir) { char cmd_file[255]; int cmd_fd; snprintf(cmd_file, ARRAY_SIZE(cmd_file), "%s/%s", mount_dir, INCFS_PENDING_READS_FILENAME); - cmd_fd = open(cmd_file, O_RDONLY | O_CLOEXEC); + cmd_fd = open(cmd_file, O_RDONLY); if (cmd_fd < 0) perror("Can't open commands file"); return cmd_fd; } -int open_log_file(const char *mount_dir) +int open_log_file(char *mount_dir) { char cmd_file[255]; int cmd_fd; snprintf(cmd_file, ARRAY_SIZE(cmd_file), "%s/.log", mount_dir); - cmd_fd = open(cmd_file, O_RDWR | O_CLOEXEC); + cmd_fd = open(cmd_file, O_RDWR); if (cmd_fd < 0) perror("Can't open log file"); return cmd_fd; @@ -278,7 +358,7 @@ out: return result; } -void sha256(const char *data, size_t dsize, char *hash) +void sha256(char *data, size_t dsize, char *hash) { SHA256_CTX ctx; @@ -287,7 +367,7 @@ void sha256(const char *data, size_t dsize, char *hash) SHA256_Final((unsigned char *)hash, &ctx); } -void md5(const char *data, size_t dsize, char *hash) +void md5(char *data, size_t dsize, char *hash) { MD5_CTX ctx; diff --git a/tools/testing/selftests/filesystems/incfs/utils.h b/tools/testing/selftests/filesystems/incfs/utils.h index 9af63e4e922c..9c9ba3c5f70a 100644 --- a/tools/testing/selftests/filesystems/incfs/utils.h +++ b/tools/testing/selftests/filesystems/incfs/utils.h @@ -5,12 +5,10 @@ #include #include -#include +#include "../../include/uapi/linux/incrementalfs.h" #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) -#define __packed __attribute__((__packed__)) - #ifdef __LP64__ #define ptr_to_u64(p) ((__u64)p) #else @@ -19,11 +17,9 @@ #define SHA256_DIGEST_SIZE 32 -int mount_fs(const char *mount_dir, const char *backing_dir, - int read_timeout_ms); +int mount_fs(char *mount_dir, char *backing_dir, int read_timeout_ms); -int mount_fs_opt(const char *mount_dir, const char *backing_dir, - const char *opt, bool remount); +int mount_fs_opt(char *mount_dir, char *backing_dir, char *opt); int get_file_bmap(int cmd_fd, int ino, unsigned char *buf, int buf_size); @@ -32,26 +28,32 @@ int get_file_signature(int fd, unsigned char *buf, int buf_size); int emit_node(int fd, char *filename, int *ino_out, int parent_ino, size_t size, mode_t mode, char *attr); -int emit_file(int fd, const char *dir, const char *filename, - incfs_uuid_t *id_out, size_t size, const char *attr); +int emit_file(int fd, char *dir, char *filename, incfs_uuid_t *id_out, + size_t size, char *attr); + +int crypto_emit_file(int fd, char *dir, char *filename, incfs_uuid_t *id_out, + size_t size, const char *root_hash, char *sig, size_t sig_size, + char *add_data); -int crypto_emit_file(int fd, const char *dir, const char *filename, - incfs_uuid_t *id_out, size_t size, const char *root_hash, - const char *add_data); +int unlink_node(int fd, int parent_ino, char *filename); -loff_t get_file_size(const char *name); +loff_t get_file_size(char *name); -int open_commands_file(const char *mount_dir); +int open_commands_file(char *mount_dir); -int open_log_file(const char *mount_dir); +int open_log_file(char *mount_dir); int wait_for_pending_reads(int fd, int timeout_ms, struct incfs_pending_read_info *prs, int prs_count); char *concat_file_name(const char *dir, char *file); -void sha256(const char *data, size_t dsize, char *hash); +void sha256(char *data, size_t dsize, char *hash); + +void md5(char *data, size_t dsize, char *hash); -void md5(const char *data, size_t dsize, char *hash); +bool sign_pkcs7(const void *data_to_sign, size_t data_size, + char *pkey_pem, char *cert_pem, + void **sig_ret, size_t *sig_size_ret); int delete_dir_tree(const char *path);