summaryrefslogtreecommitdiff
path: root/fs/incfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/incfs')
-rw-r--r--fs/incfs/data_mgmt.c690
-rw-r--r--fs/incfs/data_mgmt.h130
-rw-r--r--fs/incfs/format.c126
-rw-r--r--fs/incfs/format.h67
-rw-r--r--fs/incfs/integrity.c183
-rw-r--r--fs/incfs/integrity.h20
-rw-r--r--fs/incfs/vfs.c552
7 files changed, 1035 insertions, 733 deletions
diff --git a/fs/incfs/data_mgmt.c b/fs/incfs/data_mgmt.c
index 4698f14bbdf7..d9c43d5cca19 100644
--- a/fs/incfs/data_mgmt.c
+++ b/fs/incfs/data_mgmt.c
@@ -27,28 +27,19 @@ 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);
- 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;
- }
- }
+ error = incfs_realloc_mount_info(mi, options);
+ if (error)
+ goto err;
return mi;
@@ -57,6 +48,47 @@ 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)
@@ -68,6 +100,8 @@ 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);
}
@@ -85,11 +119,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;
- struct backing_file_context *bfc;
+ struct data_file *df = NULL;
+ struct backing_file_context *bfc = NULL;
int md_records;
u64 size;
- int error;
+ int error = 0;
int i;
if (!bf || !mi)
@@ -116,8 +150,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);
+ error = incfs_read_file_header(bfc, &df->df_metadata_off, &df->df_id,
+ &size, &df->df_header_flags);
mutex_unlock(&bfc->bc_mutex);
if (error)
@@ -125,7 +159,7 @@ struct data_file *incfs_open_data_file(struct mount_info *mi, struct file *bf)
df->df_size = size;
if (size > 0)
- df->df_block_count = get_blocks_count_for_size(size);
+ df->df_data_block_count = get_blocks_count_for_size(size);
md_records = incfs_scan_metadata_chain(df);
if (md_records < 0)
@@ -160,7 +194,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;
+ struct data_file *df = NULL;
int err = 0;
inode_lock(inode);
@@ -181,7 +215,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;
+ struct dir_file *dir = NULL;
if (!S_ISDIR(bf->f_inode->i_mode))
return ERR_PTR(-EBADF);
@@ -214,33 +248,120 @@ 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, bool timed_out)
+ int block_index)
{
struct read_log *log = &mi->mi_log;
- struct read_log_state state;
+ struct read_log_state *head, *tail;
s64 now_us = ktime_to_us(ktime_get());
- struct read_log_record record = {
- .file_id = *id,
- .timestamp_us = now_us
- };
-
- set_block_index(&record, block_index);
- set_timed_out(&record, timed_out);
+ s64 relative_us;
+ union log_record record;
+ size_t record_size;
- if (log->rl_size == 0)
+ spin_lock(&log->rl_lock);
+ if (log->rl_size == 0) {
+ spin_unlock(&log->rl_lock);
return;
+ }
- 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 = &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);
}
- WRITE_ONCE(log->rl_state, state);
- spin_unlock(&log->rl_writer_lock);
+ 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;
+ }
+ ++head->current_record_no;
+
+ spin_unlock(&log->rl_lock);
wake_up_all(&log->ml_notif_wq);
}
@@ -249,7 +370,7 @@ static int validate_hash_tree(struct file *bf, struct data_file *df,
{
u8 digest[INCFS_MAX_HASH_SIZE] = {};
struct mtree *tree = NULL;
- struct ondisk_signature *sig = NULL;
+ struct incfs_df_signature *sig = NULL;
struct mem_range calc_digest_rng;
struct mem_range saved_digest_rng;
struct mem_range root_hash_rng;
@@ -272,8 +393,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->mtree_offset;
+ loff_t lvl_off =
+ tree->hash_level_suboffset[lvl] + sig->hash_offset;
loff_t hash_block_off = lvl_off +
round_down(hash_block_index * digest_size,
INCFS_DATA_FILE_BLOCK_SIZE);
@@ -321,72 +442,6 @@ 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)
{
@@ -401,13 +456,28 @@ 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)
@@ -416,26 +486,184 @@ 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 || index >= df->df_block_count || blockmap_off == 0)
+ if (index < 0 || blockmap_off == 0)
return -EINVAL;
error = incfs_read_blockmap_entry(bfc, index, blockmap_off, &bme);
if (error)
return error;
- 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;
+ 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);
+ 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;
@@ -535,7 +763,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_block_count)
+ if (block_index < 0 || block_index >= df->df_data_block_count)
return -EINVAL;
if (df->df_blockmap_off <= 0)
@@ -566,8 +794,7 @@ 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,
- true /*timed out*/);
+ log_block_read(mi, &df->df_id, block_index);
return -ETIME;
}
@@ -586,8 +813,7 @@ 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,
- true /*timed out*/);
+ log_block_read(mi, &df->df_id, block_index);
return -ETIME;
}
if (wait_res < 0) {
@@ -682,22 +908,15 @@ 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, false /*timed out*/);
+ log_block_read(mi, &df->df_id, index);
out:
return result;
}
int incfs_process_new_data_block(struct data_file *df,
- struct incfs_new_data_block *block, u8 *data)
+ struct incfs_fill_block *block, u8 *data)
{
struct mount_info *mi = NULL;
struct backing_file_context *bfc = NULL;
@@ -712,7 +931,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_block_count)
+ if (block->block_index >= df->df_data_block_count)
return -ERANGE;
segment = get_file_segment(df, block->block_index);
@@ -754,7 +973,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 ondisk_signature *sig;
+ struct incfs_df_signature *sig;
int read_res = 0;
if (!dst.data)
@@ -779,12 +998,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_new_data_block *block, u8 *data)
+ struct incfs_fill_block *block, u8 *data)
{
struct backing_file_context *bfc = NULL;
struct mount_info *mi = NULL;
struct mtree *hash_tree = NULL;
- struct ondisk_signature *sig = NULL;
+ struct incfs_df_signature *sig = NULL;
loff_t hash_area_base = 0;
loff_t hash_area_size = 0;
int error = 0;
@@ -803,11 +1022,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->mtree_offset == 0)
+ if (!hash_tree || !sig || sig->hash_offset == 0)
return -ENOTSUPP;
- hash_area_base = sig->mtree_offset;
- hash_area_size = sig->mtree_size;
+ hash_area_base = sig->hash_offset;
+ hash_area_size = sig->hash_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. */
@@ -818,7 +1037,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);
+ hash_area_base, df->df_blockmap_off, df->df_size);
mutex_unlock(&bfc->bc_mutex);
return error;
}
@@ -834,9 +1053,10 @@ static int process_blockmap_md(struct incfs_blockmap *bm,
if (!df)
return -EFAULT;
- if (df->df_block_count != block_count)
+ if (df->df_data_block_count > block_count)
return -EBADMSG;
+ df->df_total_block_count = block_count;
df->df_blockmap_off = base_off;
return error;
}
@@ -865,58 +1085,69 @@ 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;
- 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);
+ 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;
+ }
- if (!df)
- return -ENOENT;
+ 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);
- signature = kzalloc(sizeof(*signature), GFP_NOFS);
- if (!signature) {
+ buf = kzalloc(signature->sig_size, GFP_NOFS);
+ if (!buf) {
error = -ENOMEM;
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;
+ read = incfs_kread(df->df_backing_file_context->bc_file, buf,
+ signature->sig_size, signature->sig_offset);
+ if (read < 0) {
+ error = read;
+ goto out;
+ }
- hash_tree = incfs_alloc_mtree(sg->sg_hash_alg, df->df_block_count,
- range(sg->sg_root_hash, sizeof(sg->sg_root_hash)));
+ if (read != signature->sig_size) {
+ error = -EINVAL;
+ goto out;
+ }
+
+ hash_tree = incfs_alloc_mtree(range(buf, signature->sig_size),
+ df->df_data_block_count);
if (IS_ERR(hash_tree)) {
error = PTR_ERR(hash_tree);
hash_tree = NULL;
goto out;
}
- if (hash_tree->hash_tree_area_size != tree_size) {
+ if (hash_tree->hash_tree_area_size != signature->hash_size) {
error = -EINVAL;
goto out;
}
- if (tree_size > 0 && handler->md_record_offset <= base_tree_off) {
+ if (signature->hash_size > 0 &&
+ handler->md_record_offset <= signature->hash_offset) {
error = -EINVAL;
goto out;
}
- if (handler->md_record_offset <= signature->add_data_offset ||
- handler->md_record_offset <= signature->sig_offset) {
+ if (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:
- if (error) {
- incfs_free_mtree(hash_tree);
- kfree(signature);
- }
+ incfs_free_mtree(hash_tree);
+ kfree(signature);
+ kfree(buf);
return error;
}
@@ -972,6 +1203,17 @@ 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;
@@ -1037,36 +1279,29 @@ 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_writer_lock);
- result = READ_ONCE(log->rl_state);
- spin_unlock(&log->rl_writer_lock);
+ spin_lock(&log->rl_lock);
+ result = log->rl_head;
+ spin_unlock(&log->rl_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,
- struct read_log_state state)
+ const struct read_log_state *state)
{
struct read_log *log = &mi->mi_log;
-
- 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;
+ 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);
}
int incfs_collect_logged_reads(struct mount_info *mi,
@@ -1074,58 +1309,47 @@ int incfs_collect_logged_reads(struct mount_info *mi,
struct incfs_pending_read_info *reads,
int reads_size)
{
- struct read_log *log = &mi->mi_log;
- 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;
+ struct read_log *log = &mi->mi_log;
+ struct read_log_state *head, *tail;
- if (reader_state->next_index >= log->rl_size ||
- read_count > written_count)
- return -ERANGE;
+ spin_lock(&log->rl_lock);
+ head = &log->rl_head;
+ tail = &log->rl_tail;
- if (read_count == written_count)
- return 0;
+ 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) {
- /* This reader is somehow ahead of the writer. */
- pr_debug("incfs: Log reader is ahead of writer\n");
- *reader_state = live_state;
+ *reader_state = (struct read_log_state){
+ .generation_id = head->generation_id,
+ };
}
- 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);
+ 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);
- *reader_state = (struct read_log_state){
- .next_index = live_state.next_index,
- .current_pass_no = live_state.current_pass_no - 1,
- };
+ *reader_state = *tail;
}
for (dst_idx = 0; dst_idx < reads_size; dst_idx++) {
- if (reader_state->next_index == live_state.next_index &&
- reader_state->current_pass_no == live_state.current_pass_no)
+ if (reader_state->current_record_no == head->current_record_no)
break;
- fill_pending_read_from_log_record(
- &reads[dst_idx],
- &log->rl_ring_buf[reader_state->next_index],
- reader_state, log->rl_size);
+ log_read_one_record(log, reader_state);
- reader_state->next_index++;
- if (reader_state->next_index == log->rl_size) {
- reader_state->next_index = 0;
- reader_state->current_pass_no++;
- }
+ 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
+ };
}
+
+ spin_unlock(&log->rl_lock);
return dst_idx;
}
diff --git a/fs/incfs/data_mgmt.h b/fs/incfs/data_mgmt.h
index 6722cef1608c..b7aecdd5bf4a 100644
--- a/fs/incfs/data_mgmt.h
+++ b/fs/incfs/data_mgmt.h
@@ -20,63 +20,74 @@
#define SEGMENTS_PER_FILE 3
-struct read_log_record {
- u32 bitfield;
-
- u64 timestamp_us;
+enum LOG_RECORD_TYPE {
+ FULL,
+ SAME_FILE,
+ SAME_FILE_NEXT_BLOCK,
+ SAME_FILE_NEXT_BLOCK_SHORT,
+};
+struct full_record {
+ enum LOG_RECORD_TYPE type : 2; /* FULL */
+ u32 block_index : 30;
incfs_uuid_t file_id;
-} __packed;
-
-#define RLR_BLOCK_INDEX_MASK 0x7fff
-#define RLR_TIMED_OUT_MASK 0x8000
-
-static inline u32 get_block_index(const struct read_log_record *rlr)
-{
- return rlr->bitfield & RLR_BLOCK_INDEX_MASK;
-}
+ 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;
+};
-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);
-}
+struct read_log_state {
+ /* Log buffer generation id, incremented on configuration changes */
+ u32 generation_id;
-static inline bool get_timed_out(const struct read_log_record *rlr)
-{
- return (rlr->bitfield & RLR_TIMED_OUT_MASK) == RLR_TIMED_OUT_MASK;
-}
+ /* Offset in rl_ring_buf to write into. */
+ u32 next_offset;
-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 number of writer passes over rl_ring_buf */
+ u32 current_pass_no;
-struct read_log_state {
- /* Next slot in rl_ring_buf to write to. */
- u32 next_index;
+ /* Current full_record to diff against */
+ struct full_record base_record;
- /* Current number of writer pass over rl_ring_buf */
- u32 current_pass_no;
+ /* Current record number counting from configuration change */
+ u64 current_record_no;
};
/* A ring buffer to save records about data blocks which were recently read. */
struct read_log {
- struct read_log_record *rl_ring_buf;
+ void *rl_ring_buf;
- struct read_log_state rl_state;
+ int rl_size;
- spinlock_t rl_writer_lock;
+ struct read_log_state rl_head;
- int rl_size;
+ struct read_log_state rl_tail;
- /*
- * A queue of waiters who want to be notified about reads.
- */
+ /* A lock to protect the above fields */
+ spinlock_t rl_lock;
+
+ /* A queue of waiters who want to be notified about reads */
wait_queue_head_t ml_notif_wq;
};
@@ -131,6 +142,12 @@ 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 {
@@ -203,16 +220,20 @@ struct data_file {
/* File size in bytes */
loff_t df_size;
- int df_block_count; /* File size in DATA_FILE_BLOCK_SIZE blocks */
+ /* 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;
struct file_attr n_attr;
struct mtree *df_hash_tree;
- struct ondisk_signature *df_signature;
-
- /* True, if file signature has already been validated. */
- bool df_signature_validated;
+ struct incfs_df_signature *df_signature;
};
struct dir_file {
@@ -239,6 +260,9 @@ 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);
@@ -253,14 +277,16 @@ 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_new_data_block *block, u8 *data);
+ struct incfs_fill_block *block, u8 *data);
int incfs_process_new_hash_block(struct data_file *df,
- struct incfs_new_data_block *block, u8 *data);
-
+ struct incfs_fill_block *block, u8 *data);
bool incfs_fresh_pending_reads_exist(struct mount_info *mi, int last_number);
@@ -279,7 +305,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,
- struct read_log_state state);
+ const struct read_log_state *state);
static inline struct inode_info *get_incfs_node(struct inode *inode)
{
@@ -297,7 +323,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;
+ struct inode_info *node = NULL;
if (!f)
return NULL;
diff --git a/fs/incfs/format.c b/fs/incfs/format.c
index db71f527cf36..c56e559b6893 100644
--- a/fs/incfs/format.c
+++ b/fs/incfs/format.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include "format.h"
+#include "data_mgmt.h"
struct backing_file_context *incfs_alloc_bfc(struct file *backing_file)
{
@@ -93,7 +94,6 @@ 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,28 +110,18 @@ 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;
- 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;
+ return vfs_fallocate(bfc->bc_file, 0, new_last_byte_offset, 1);
}
static int write_to_bf(struct backing_file_context *bfc, const void *buf,
- size_t count, loff_t pos, bool sync)
+ size_t count, loff_t pos)
{
- ssize_t res = 0;
+ ssize_t res = incfs_kwrite(bfc->bc_file, buf, count, pos);
- 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;
}
@@ -185,7 +175,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, true);
+ result = write_to_bf(bfc, record, record_size, file_pos);
if (result)
return result;
@@ -206,7 +196,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, true);
+ file_pos);
if (result)
return result;
@@ -214,12 +204,22 @@ 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, loff_t *map_base_off)
+ u32 block_count)
{
struct incfs_blockmap blockmap = {};
int result = 0;
@@ -245,12 +245,9 @@ 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;
}
@@ -283,7 +280,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, true);
+ result = write_to_bf(bfc, value.data, value.len, value_offset);
if (result)
return result;
@@ -299,9 +296,7 @@ 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,
- u8 hash_alg, u32 tree_size,
- struct mem_range root_hash, struct mem_range add_data,
- struct mem_range sig)
+ struct mem_range sig, u32 tree_size)
{
struct incfs_file_signature sg = {};
int result = 0;
@@ -311,8 +306,6 @@ 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);
@@ -321,32 +314,19 @@ 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, 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);
+ result = write_to_bf(bfc, sig.data, sig.len, pos);
if (result)
goto err;
}
tree_area_pos = incfs_get_end_offset(bfc->bc_file);
- if (hash_alg && tree_size > 0) {
+ if (tree_size > 0) {
if (tree_size > 5 * INCFS_DATA_FILE_BLOCK_SIZE) {
/*
* If hash tree is big enough, it makes sense to
@@ -369,15 +349,13 @@ 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;
}
@@ -411,7 +389,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, true);
+ return write_to_bf(bfc, &fh, sizeof(fh), file_pos);
}
/* Write a given data block and update file's blockmap to point it. */
@@ -440,7 +418,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, false);
+ result = write_to_bf(bfc, block.data, block.len, data_offset);
if (result)
return result;
@@ -450,18 +428,25 @@ 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);
- result = write_to_bf(bfc, &bm_entry, sizeof(bm_entry),
- bm_entry_off, false);
- return result;
+ return write_to_bf(bfc, &bm_entry, sizeof(bm_entry),
+ bm_entry_off);
}
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)
+ struct mem_range block,
+ int block_index,
+ loff_t hash_area_off,
+ loff_t bm_base_off,
+ loff_t file_size)
{
+ 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;
@@ -475,7 +460,16 @@ int incfs_write_hash_block_to_backing_file(struct backing_file_context *bfc,
return -EINVAL;
}
- return write_to_bf(bfc, block.data, block.len, data_offset, false);
+ 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);
}
/* Initialize a new image in a given backing file. */
@@ -505,8 +499,19 @@ int incfs_read_blockmap_entry(struct backing_file_context *bfc, int block_index,
loff_t bm_base_off,
struct incfs_blockmap_entry *bm_entry)
{
- return incfs_read_blockmap_entries(bfc, bm_entry, block_index, 1,
- bm_base_off);
+ 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;
}
int incfs_read_blockmap_entries(struct backing_file_context *bfc,
@@ -530,15 +535,12 @@ int incfs_read_blockmap_entries(struct backing_file_context *bfc,
bm_entry_off);
if (result < 0)
return result;
- if (result < bytes_to_read)
- return -EIO;
- return 0;
+ return result / sizeof(*entries);
}
-
int incfs_read_file_header(struct backing_file_context *bfc,
loff_t *first_md_off, incfs_uuid_t *uuid,
- u64 *file_size)
+ u64 *file_size, u32 *flags)
{
ssize_t bytes_read = 0;
struct incfs_file_header fh = {};
@@ -572,6 +574,8 @@ int incfs_read_file_header(struct backing_file_context *bfc,
*uuid = fh.fh_uuid;
if (file_size)
*file_size = le64_to_cpu(fh.fh_file_size);
+ if (flags)
+ *flags = le32_to_cpu(fh.fh_file_header_flags);
return 0;
}
diff --git a/fs/incfs/format.h b/fs/incfs/format.h
index a86881482e19..1a83349bb2eb 100644
--- a/fs/incfs/format.h
+++ b/fs/incfs/format.h
@@ -121,6 +121,10 @@ 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;
@@ -159,8 +163,8 @@ struct incfs_file_header {
/* INCFS_DATA_FILE_BLOCK_SIZE */
__le16 fh_data_block_size;
- /* Padding, also reserved for future use. */
- __le32 fh_dummy;
+ /* File flags, from incfs_file_header_flags */
+ __le32 fh_file_header_flags;
/* Offset of the first metadata record */
__le64 fh_first_md_offset;
@@ -178,6 +182,7 @@ 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. */
@@ -217,27 +222,27 @@ struct incfs_file_attr {
__le32 fa_crc;
} __packed;
-/* Metadata record for file attribute. Type = INCFS_MD_SIGNATURE */
+/* Metadata record for file signature. Type = INCFS_MD_SIGNATURE */
struct incfs_file_signature {
struct incfs_md_header sg_header;
- __u8 sg_hash_alg; /* Value from incfs_hash_tree_algorithm */
+ __le32 sg_sig_size; /* The size of the signature. */
+
+ __le64 sg_sig_offset; /* Signature's offset in the backing file */
__le32 sg_hash_tree_size; /* The size of the hash tree. */
__le64 sg_hash_tree_offset; /* Hash tree offset in the backing file */
-
- __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;
+/* In memory version of above */
+struct incfs_df_signature {
+ u32 sig_size;
+ u64 sig_offset;
+ u32 hash_size;
+ u64 hash_offset;
+};
+
/* State of the backing file. */
struct backing_file_context {
/* Protects writes to bc_file */
@@ -253,23 +258,6 @@ 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;
@@ -301,7 +289,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, loff_t *map_base_off);
+ u32 block_count);
int incfs_write_fh_to_backing_file(struct backing_file_context *bfc,
incfs_uuid_t *uuid, u64 file_size);
@@ -312,16 +300,19 @@ 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);
+ struct mem_range block,
+ int block_index,
+ loff_t hash_area_off,
+ loff_t bm_base_off,
+ loff_t file_size);
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,
- u8 hash_alg, u32 tree_size,
- struct mem_range root_hash, struct mem_range add_data,
- struct mem_range sig);
+ struct mem_range sig, u32 tree_size);
+
+int incfs_write_file_header_flags(struct backing_file_context *bfc, u32 flags);
int incfs_make_empty_backing_file(struct backing_file_context *bfc,
incfs_uuid_t *uuid, u64 file_size);
@@ -329,7 +320,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);
+ u64 *file_size, u32 *flags);
int incfs_read_blockmap_entry(struct backing_file_context *bfc, int block_index,
loff_t bm_base_off,
diff --git a/fs/incfs/integrity.c b/fs/incfs/integrity.c
index feb212c38945..d049988ef037 100644
--- a/fs/incfs/integrity.c
+++ b/fs/incfs/integrity.c
@@ -10,70 +10,6 @@
#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 = {
@@ -113,11 +49,90 @@ 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;
+}
-struct mtree *incfs_alloc_mtree(enum incfs_hash_tree_algorithm id,
- int data_block_count,
- struct mem_range root_hash)
+static int incfs_parse_signature(struct mem_range signature,
+ struct signature_info *si)
{
+ 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;
@@ -129,11 +144,15 @@ struct mtree *incfs_alloc_mtree(enum incfs_hash_tree_algorithm id,
if (data_block_count <= 0)
return ERR_PTR(-EINVAL);
- hash_alg = incfs_get_hash_alg(id);
+ error = incfs_parse_signature(signature, &si);
+ if (error)
+ return ERR_PTR(error);
+
+ hash_alg = incfs_get_hash_alg(si.hash_algorithm);
if (IS_ERR(hash_alg))
return ERR_PTR(PTR_ERR(hash_alg));
- if (root_hash.len < hash_alg->digest_size)
+ if (si.root_hash.len < hash_alg->digest_size)
return ERR_PTR(-EINVAL);
result = kzalloc(sizeof(*result), GFP_NOFS);
@@ -173,7 +192,7 @@ struct mtree *incfs_alloc_mtree(enum incfs_hash_tree_algorithm id,
}
/* Root hash is stored separately from the rest of the tree. */
- memcpy(result->root_hash, root_hash.data, hash_alg->digest_size);
+ memcpy(result->root_hash, si.root_hash.data, hash_alg->digest_size);
return result;
err:
@@ -198,16 +217,20 @@ int incfs_calc_digest(struct incfs_hash_alg *alg, struct mem_range data,
return -EINVAL;
desc->tfm = alg->shash;
- 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);
+ 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);
}
diff --git a/fs/incfs/integrity.h b/fs/incfs/integrity.h
index da1c38486b2f..cf79b64da736 100644
--- a/fs/incfs/integrity.h
+++ b/fs/incfs/integrity.h
@@ -38,21 +38,10 @@ 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(enum incfs_hash_tree_algorithm id,
- int data_block_count,
- struct mem_range root_hash);
+struct mtree *incfs_alloc_mtree(struct mem_range signature,
+ int data_block_count);
void incfs_free_mtree(struct mtree *tree);
@@ -64,9 +53,4 @@ size_t incfs_get_mtree_hash_count(enum incfs_hash_tree_algorithm alg,
int incfs_calc_digest(struct incfs_hash_alg *alg, struct mem_range data,
struct mem_range digest);
-int incfs_validate_pkcs7_signature(struct mem_range pkcs7_blob,
- struct mem_range root_hash, struct mem_range add_data);
-
-void incfs_free_signature_info(struct signature_info *si);
-
#endif /* _INCFS_INTEGRITY_H */
diff --git a/fs/incfs/vfs.c b/fs/incfs/vfs.c
index aebd2b02bd83..ec028fc96303 100644
--- a/fs/incfs/vfs.c
+++ b/fs/incfs/vfs.c
@@ -52,8 +52,6 @@ static int dir_rename(struct inode *old_dir, struct dentry *old_dentry,
static int file_open(struct inode *inode, struct file *file);
static int file_release(struct inode *inode, struct file *file);
-static ssize_t file_write(struct file *f, const char __user *buf,
- size_t size, loff_t *offset);
static int read_single_page(struct file *f, struct page *page);
static long dispatch_ioctl(struct file *f, unsigned int req, unsigned long arg);
@@ -75,6 +73,8 @@ static void evict_inode(struct inode *inode);
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 *);
@@ -127,7 +127,6 @@ static const struct address_space_operations incfs_address_space_ops = {
static const struct file_operations incfs_file_ops = {
.open = file_open,
.release = file_release,
- .write = file_write,
.read_iter = generic_file_read_iter,
.mmap = generic_file_mmap,
.splice_read = generic_file_splice_read,
@@ -136,6 +135,11 @@ 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,
@@ -169,9 +173,18 @@ 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[] = {
@@ -336,8 +349,8 @@ static int inode_test(struct inode *inode, void *opaque)
return (node->n_backing_inode == backing_inode) &&
inode->i_ino == search->ino;
- }
- return 1;
+ } else
+ return inode->i_ino == search->ino;
}
static int inode_set(struct inode *inode, void *opaque)
@@ -454,9 +467,6 @@ 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;
@@ -574,22 +584,27 @@ 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(size_t, mi->mi_log.rl_size, reads_to_collect);
+ reads_to_collect = min_t(ssize_t, 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(size_t, reads_to_collect, reads_per_page));
+ min_t(ssize_t, reads_to_collect, reads_per_page));
if (reads_collected <= 0) {
result = total_reads_collected ?
total_reads_collected *
@@ -628,7 +643,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;
@@ -789,9 +804,6 @@ static int read_single_page(struct file *f, struct page *page)
size = df->df_size;
timeout_ms = df->df_mount_info->mi_options.read_timeout_ms;
- pr_debug("incfs: %s %s %lld\n", __func__,
- f->f_path.dentry->d_name.name, offset);
-
if (offset < size) {
struct mem_range tmp = {
.len = 2 * INCFS_DATA_FILE_BLOCK_SIZE
@@ -838,107 +850,39 @@ static char *file_id_to_str(incfs_uuid_t id)
return result;
}
-static struct signature_info *incfs_copy_signature_info_from_user(
- struct incfs_file_signature_info __user *original)
+static struct mem_range incfs_copy_signature_info_from_user(u8 __user *original,
+ u64 size)
{
- struct incfs_file_signature_info usr_si;
- struct signature_info *result;
- int error;
+ u8 *result;
if (!original)
- return NULL;
+ return range(NULL, 0);
- 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(sizeof(*result), GFP_NOFS);
+ result = kzalloc(size, GFP_NOFS | __GFP_COMP);
if (!result)
- 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;
- }
+ return range(ERR_PTR(-ENOMEM), 0);
- /* 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 (usr_si.additional_data_size > INCFS_MAX_FILE_ATTR_SIZE) {
- error = -E2BIG;
- goto err;
+ if (copy_from_user(result, original, size)) {
+ kfree(result);
+ return range(ERR_PTR(-EFAULT), 0);
}
- 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);
+ return range(result, size);
}
static int init_new_file(struct mount_info *mi, struct dentry *dentry,
- incfs_uuid_t *uuid, u64 size, struct mem_range attr,
- struct incfs_file_signature_info __user *fsi)
+ incfs_uuid_t *uuid, u64 size, struct mem_range attr,
+ u8 __user *user_signature_info, u64 signature_size)
{
struct path path = {};
struct file *new_file;
int error = 0;
struct backing_file_context *bfc = NULL;
u32 block_count;
- struct mem_range mem_range = {NULL};
- struct signature_info *si = NULL;
+ struct mem_range raw_signature = { NULL };
struct mtree *hash_tree = NULL;
if (!mi || !dentry || !uuid)
@@ -957,6 +901,7 @@ 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;
@@ -968,19 +913,6 @@ 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);
@@ -988,54 +920,46 @@ static int init_new_file(struct mount_info *mi, struct dentry *dentry,
goto out;
}
- if (fsi) {
- si = incfs_copy_signature_info_from_user(fsi);
+ 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 (IS_ERR(si)) {
- error = PTR_ERR(si);
- si = NULL;
+ if (IS_ERR(raw_signature.data)) {
+ error = PTR_ERR(raw_signature.data);
+ raw_signature.data = 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;
- }
-
- /* 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;
+ hash_tree = incfs_alloc_mtree(raw_signature, block_count);
+ if (IS_ERR(hash_tree)) {
+ error = PTR_ERR(hash_tree);
+ hash_tree = NULL;
+ goto out;
+ }
- 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);
+ error = incfs_write_signature_to_backing_file(
+ bfc, raw_signature, hash_tree->hash_tree_area_size);
+ if (error)
+ goto out;
- if (error)
- goto out;
- }
+ block_count += get_blocks_count_for_size(
+ hash_tree->hash_tree_area_size);
}
+ if (block_count)
+ error = incfs_write_blockmap_to_backing_file(bfc, block_count);
+
+ if (error)
+ goto out;
out:
if (bfc) {
mutex_unlock(&bfc->bc_mutex);
incfs_free_bfc(bfc);
}
incfs_free_mtree(hash_tree);
- incfs_free_signature_info(si);
- kfree(mem_range.data);
+ kfree(raw_signature.data);
if (error)
pr_debug("incfs: %s error: %d\n", __func__, error);
@@ -1180,10 +1104,7 @@ 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;
@@ -1296,7 +1217,7 @@ static long ioctl_create_file(struct mount_info *mi,
goto delete_index_file;
}
- /* Save the file's attrubute as an xattr */
+ /* Save the file's attribute as an xattr */
if (args.file_attr_len && args.file_attr) {
if (args.file_attr_len > INCFS_MAX_FILE_ATTR_SIZE) {
error = -E2BIG;
@@ -1309,12 +1230,6 @@ 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) {
@@ -1333,9 +1248,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),
- (struct incfs_file_signature_info __user *)
- args.signature_info);
+ range(attr_value, args.file_attr_len),
+ (u8 __user *)args.signature_info,
+ args.signature_size);
if (error)
goto delete_index_file;
@@ -1363,6 +1278,123 @@ 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;
@@ -1376,20 +1408,14 @@ 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);
+ sig_buffer = kzalloc(sig_buf_size, GFP_NOFS | __GFP_COMP);
if (!sig_buffer)
return -ENOMEM;
@@ -1417,6 +1443,30 @@ 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));
@@ -1424,8 +1474,14 @@ 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;
}
@@ -1632,6 +1688,7 @@ 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;
@@ -1854,9 +1911,10 @@ 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);
- else if (S_ISDIR(inode->i_mode)) {
+ file->private_data = (void *)CANT_FILL;
+ } else if (S_ISDIR(inode->i_mode)) {
struct dir_file *dir = NULL;
dir = incfs_open_dir_file(mi, backing_file);
@@ -1891,77 +1949,6 @@ static int file_release(struct inode *inode, struct file *file)
return 0;
}
-static ssize_t file_write(struct file *f, const char __user *buf,
- size_t size, loff_t *offset)
-{
- struct data_file *df = get_incfs_data_file(f);
- const ssize_t data_buf_size = 2 * INCFS_DATA_FILE_BLOCK_SIZE;
- size_t block_count = size / sizeof(struct incfs_new_data_block);
- struct incfs_new_data_block __user *usr_blocks =
- (struct incfs_new_data_block __user *)buf;
- u8 *data_buf = NULL;
- ssize_t error = 0;
- int i = 0;
-
- if (!df)
- return -EBADF;
-
- 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 = {};
@@ -2004,6 +1991,7 @@ static void dentry_release(struct dentry *d)
if (di)
path_put(&di->backing_path);
+ kfree(d->d_fsdata);
d->d_fsdata = NULL;
}
@@ -2048,11 +2036,74 @@ 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)
+ 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 {
return -ENODATA;
+ }
+
+ kfree (*stored_value);
+ *stored_value = kzalloc(size, GFP_NOFS);
+ if (!*stored_value)
+ return -ENOMEM;
- return vfs_getxattr(di->backing_path.dentry, name, value, size);
+ memcpy(*stored_value, value, size);
+ *stored_size = size;
+ return 0;
}
static ssize_t incfs_listxattr(struct dentry *d, char *list, size_t size)
@@ -2152,7 +2203,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("infs: mount\n");
+ pr_debug("incfs: mount\n");
return dget(sb->s_root);
err:
sb->s_fs_info = NULL;
@@ -2173,12 +2224,11 @@ static int incfs_remount_fs(struct super_block *sb, int *flags, char *data)
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);
- }
+ err = incfs_realloc_mount_info(mi, &options);
+ if (err)
+ return err;
- pr_debug("infs: remount\n");
+ pr_debug("incfs: remount\n");
return 0;
}
@@ -2186,7 +2236,7 @@ void incfs_kill_sb(struct super_block *sb)
{
struct mount_info *mi = sb->s_fs_info;
- pr_debug("infs: unmount\n");
+ pr_debug("incfs: unmount\n");
incfs_free_mount_info(mi);
generic_shutdown_super(sb);
}