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 <spathi@codeaurora.org>
tirimbino
Srinivasarao P 4 years ago
parent 816f245a4e
commit 975fd0628f
  1. 690
      fs/incfs/data_mgmt.c
  2. 130
      fs/incfs/data_mgmt.h
  3. 126
      fs/incfs/format.c
  4. 67
      fs/incfs/format.h
  5. 183
      fs/incfs/integrity.c
  6. 20
      fs/incfs/integrity.h
  7. 605
      fs/incfs/vfs.c
  8. 191
      include/uapi/linux/incrementalfs.h
  9. 17
      tools/testing/selftests/filesystems/incfs/Makefile
  10. 1
      tools/testing/selftests/filesystems/incfs/config
  11. 1081
      tools/testing/selftests/filesystems/incfs/incfs_test.c
  12. 260
      tools/testing/selftests/filesystems/incfs/utils.c
  13. 36
      tools/testing/selftests/filesystems/incfs/utils.h

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

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

@ -13,7 +13,6 @@
#include <linux/kernel.h>
#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;
}

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

@ -2,31 +2,28 @@
/*
* Copyright 2018 Google LLC
*/
#include <stdio.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <poll.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/pkcs7.h>
#include <openssl/sha.h>
#include <openssl/md5.h>
#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;

@ -5,12 +5,10 @@
#include <stdbool.h>
#include <sys/stat.h>
#include <include/uapi/linux/incrementalfs.h>
#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);

Loading…
Cancel
Save