ANDROID: Incremental fs: Add INCFS_IOC_GET_BLOCK_COUNT
Bug: 166638631 Test: incfs_test passes Signed-off-by: Paul Lawrence <paullawrence@google.com> Change-Id: Ia7a8cab87688fc401f0719df84fe79ea75887692
This commit is contained in:
@@ -274,9 +274,29 @@ out:
|
|||||||
|
|
||||||
void incfs_free_data_file(struct data_file *df)
|
void incfs_free_data_file(struct data_file *df)
|
||||||
{
|
{
|
||||||
|
u32 blocks_written;
|
||||||
|
|
||||||
if (!df)
|
if (!df)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
blocks_written = atomic_read(&df->df_data_blocks_written);
|
||||||
|
if (blocks_written != df->df_initial_data_blocks_written) {
|
||||||
|
struct backing_file_context *bfc = df->df_backing_file_context;
|
||||||
|
int error = -1;
|
||||||
|
|
||||||
|
if (bfc && !mutex_lock_interruptible(&bfc->bc_mutex)) {
|
||||||
|
error = incfs_write_status_to_backing_file(
|
||||||
|
df->df_backing_file_context,
|
||||||
|
df->df_status_offset,
|
||||||
|
blocks_written);
|
||||||
|
mutex_unlock(&bfc->bc_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
/* Nothing can be done, just warn */
|
||||||
|
pr_warn("incfs: failed to write status to backing file\n");
|
||||||
|
}
|
||||||
|
|
||||||
incfs_free_mtree(df->df_hash_tree);
|
incfs_free_mtree(df->df_hash_tree);
|
||||||
incfs_free_bfc(df->df_backing_file_context);
|
incfs_free_bfc(df->df_backing_file_context);
|
||||||
kfree(df);
|
kfree(df);
|
||||||
@@ -1125,8 +1145,10 @@ int incfs_process_new_data_block(struct data_file *df,
|
|||||||
df->df_blockmap_off, flags);
|
df->df_blockmap_off, flags);
|
||||||
mutex_unlock(&bfc->bc_mutex);
|
mutex_unlock(&bfc->bc_mutex);
|
||||||
}
|
}
|
||||||
if (!error)
|
if (!error) {
|
||||||
notify_pending_reads(mi, segment, block->block_index);
|
notify_pending_reads(mi, segment, block->block_index);
|
||||||
|
atomic_inc(&df->df_data_blocks_written);
|
||||||
|
}
|
||||||
|
|
||||||
up_write(&segment->rwsem);
|
up_write(&segment->rwsem);
|
||||||
|
|
||||||
@@ -1303,6 +1325,19 @@ out:
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int process_status_md(struct incfs_status *is,
|
||||||
|
struct metadata_handler *handler)
|
||||||
|
{
|
||||||
|
struct data_file *df = handler->context;
|
||||||
|
|
||||||
|
df->df_initial_data_blocks_written = le32_to_cpu(is->is_blocks_written);
|
||||||
|
atomic_set(&df->df_data_blocks_written,
|
||||||
|
df->df_initial_data_blocks_written);
|
||||||
|
df->df_status_offset = handler->md_record_offset;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int incfs_scan_metadata_chain(struct data_file *df)
|
int incfs_scan_metadata_chain(struct data_file *df)
|
||||||
{
|
{
|
||||||
struct metadata_handler *handler = NULL;
|
struct metadata_handler *handler = NULL;
|
||||||
@@ -1330,6 +1365,7 @@ int incfs_scan_metadata_chain(struct data_file *df)
|
|||||||
handler->context = df;
|
handler->context = df;
|
||||||
handler->handle_blockmap = process_blockmap_md;
|
handler->handle_blockmap = process_blockmap_md;
|
||||||
handler->handle_signature = process_file_signature_md;
|
handler->handle_signature = process_file_signature_md;
|
||||||
|
handler->handle_status = process_status_md;
|
||||||
|
|
||||||
while (handler->md_record_offset > 0) {
|
while (handler->md_record_offset > 0) {
|
||||||
error = incfs_read_next_metadata_record(bfc, handler);
|
error = incfs_read_next_metadata_record(bfc, handler);
|
||||||
|
@@ -249,7 +249,14 @@ struct data_file {
|
|||||||
/* For mapped files, the offset into the actual file */
|
/* For mapped files, the offset into the actual file */
|
||||||
loff_t df_mapped_offset;
|
loff_t df_mapped_offset;
|
||||||
|
|
||||||
struct file_attr n_attr;
|
/* Number of data blocks written to file */
|
||||||
|
atomic_t df_data_blocks_written;
|
||||||
|
|
||||||
|
/* Number of data blocks in the status block */
|
||||||
|
u32 df_initial_data_blocks_written;
|
||||||
|
|
||||||
|
/* Offset to status metadata header */
|
||||||
|
loff_t df_status_offset;
|
||||||
|
|
||||||
struct mtree *df_hash_tree;
|
struct mtree *df_hash_tree;
|
||||||
|
|
||||||
|
@@ -316,6 +316,53 @@ err:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int write_new_status_to_backing_file(struct backing_file_context *bfc,
|
||||||
|
u32 blocks_written)
|
||||||
|
{
|
||||||
|
struct incfs_status is = {};
|
||||||
|
int result;
|
||||||
|
loff_t rollback_pos;
|
||||||
|
|
||||||
|
if (!bfc)
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
LOCK_REQUIRED(bfc->bc_mutex);
|
||||||
|
|
||||||
|
rollback_pos = incfs_get_end_offset(bfc->bc_file);
|
||||||
|
|
||||||
|
is.is_header.h_md_entry_type = INCFS_MD_STATUS;
|
||||||
|
is.is_header.h_record_size = cpu_to_le16(sizeof(is));
|
||||||
|
is.is_blocks_written = cpu_to_le32(blocks_written);
|
||||||
|
|
||||||
|
result = append_md_to_backing_file(bfc, &is.is_header);
|
||||||
|
if (result)
|
||||||
|
truncate_backing_file(bfc, rollback_pos);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int incfs_write_status_to_backing_file(struct backing_file_context *bfc,
|
||||||
|
loff_t status_offset,
|
||||||
|
u32 blocks_written)
|
||||||
|
{
|
||||||
|
struct incfs_status is;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (status_offset == 0)
|
||||||
|
return write_new_status_to_backing_file(bfc, blocks_written);
|
||||||
|
|
||||||
|
result = incfs_kread(bfc->bc_file, &is, sizeof(is), status_offset);
|
||||||
|
if (result != sizeof(is))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
is.is_blocks_written = cpu_to_le32(blocks_written);
|
||||||
|
result = incfs_kwrite(bfc->bc_file, &is, sizeof(is), status_offset);
|
||||||
|
if (result != sizeof(is))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write a backing file header
|
* Write a backing file header
|
||||||
* It should always be called only on empty file.
|
* It should always be called only on empty file.
|
||||||
@@ -637,6 +684,11 @@ int incfs_read_next_metadata_record(struct backing_file_context *bfc,
|
|||||||
res = handler->handle_signature(
|
res = handler->handle_signature(
|
||||||
&handler->md_buffer.signature, handler);
|
&handler->md_buffer.signature, handler);
|
||||||
break;
|
break;
|
||||||
|
case INCFS_MD_STATUS:
|
||||||
|
if (handler->handle_status)
|
||||||
|
res = handler->handle_status(
|
||||||
|
&handler->md_buffer.status, handler);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
res = -ENOTSUPP;
|
res = -ENOTSUPP;
|
||||||
break;
|
break;
|
||||||
|
@@ -118,7 +118,8 @@ enum incfs_metadata_type {
|
|||||||
INCFS_MD_NONE = 0,
|
INCFS_MD_NONE = 0,
|
||||||
INCFS_MD_BLOCK_MAP = 1,
|
INCFS_MD_BLOCK_MAP = 1,
|
||||||
INCFS_MD_FILE_ATTR = 2,
|
INCFS_MD_FILE_ATTR = 2,
|
||||||
INCFS_MD_SIGNATURE = 3
|
INCFS_MD_SIGNATURE = 3,
|
||||||
|
INCFS_MD_STATUS = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum incfs_file_header_flags {
|
enum incfs_file_header_flags {
|
||||||
@@ -245,6 +246,14 @@ struct incfs_df_signature {
|
|||||||
u64 hash_offset;
|
u64 hash_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct incfs_status {
|
||||||
|
struct incfs_md_header is_header;
|
||||||
|
|
||||||
|
__le32 is_blocks_written; /* Number of blocks written */
|
||||||
|
|
||||||
|
__le32 is_dummy[7]; /* Three spare fields */
|
||||||
|
};
|
||||||
|
|
||||||
/* State of the backing file. */
|
/* State of the backing file. */
|
||||||
struct backing_file_context {
|
struct backing_file_context {
|
||||||
/* Protects writes to bc_file */
|
/* Protects writes to bc_file */
|
||||||
@@ -269,12 +278,15 @@ struct metadata_handler {
|
|||||||
struct incfs_md_header md_header;
|
struct incfs_md_header md_header;
|
||||||
struct incfs_blockmap blockmap;
|
struct incfs_blockmap blockmap;
|
||||||
struct incfs_file_signature signature;
|
struct incfs_file_signature signature;
|
||||||
|
struct incfs_status status;
|
||||||
} md_buffer;
|
} md_buffer;
|
||||||
|
|
||||||
int (*handle_blockmap)(struct incfs_blockmap *bm,
|
int (*handle_blockmap)(struct incfs_blockmap *bm,
|
||||||
struct metadata_handler *handler);
|
struct metadata_handler *handler);
|
||||||
int (*handle_signature)(struct incfs_file_signature *sig,
|
int (*handle_signature)(struct incfs_file_signature *sig,
|
||||||
struct metadata_handler *handler);
|
struct metadata_handler *handler);
|
||||||
|
int (*handle_status)(struct incfs_status *sig,
|
||||||
|
struct metadata_handler *handler);
|
||||||
};
|
};
|
||||||
#define INCFS_MAX_METADATA_RECORD_SIZE \
|
#define INCFS_MAX_METADATA_RECORD_SIZE \
|
||||||
sizeof_field(struct metadata_handler, md_buffer)
|
sizeof_field(struct metadata_handler, md_buffer)
|
||||||
@@ -311,6 +323,10 @@ int incfs_write_hash_block_to_backing_file(struct backing_file_context *bfc,
|
|||||||
int incfs_write_signature_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);
|
struct mem_range sig, u32 tree_size);
|
||||||
|
|
||||||
|
int incfs_write_status_to_backing_file(struct backing_file_context *bfc,
|
||||||
|
loff_t status_offset,
|
||||||
|
u32 blocks_written);
|
||||||
|
|
||||||
int incfs_write_file_header_flags(struct backing_file_context *bfc, u32 flags);
|
int incfs_write_file_header_flags(struct backing_file_context *bfc, u32 flags);
|
||||||
|
|
||||||
int incfs_make_empty_backing_file(struct backing_file_context *bfc,
|
int incfs_make_empty_backing_file(struct backing_file_context *bfc,
|
||||||
|
@@ -85,8 +85,6 @@ static const struct file_operations incfs_dir_fops = {
|
|||||||
.iterate = iterate_incfs_dir,
|
.iterate = iterate_incfs_dir,
|
||||||
.open = file_open,
|
.open = file_open,
|
||||||
.release = file_release,
|
.release = file_release,
|
||||||
.unlocked_ioctl = dispatch_ioctl,
|
|
||||||
.compat_ioctl = dispatch_ioctl
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct dentry_operations incfs_dentry_ops = {
|
static const struct dentry_operations incfs_dentry_ops = {
|
||||||
@@ -664,6 +662,25 @@ static long ioctl_get_filled_blocks(struct file *f, void __user *arg)
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long ioctl_get_block_count(struct file *f, void __user *arg)
|
||||||
|
{
|
||||||
|
struct incfs_get_block_count_args __user *args_usr_ptr = arg;
|
||||||
|
struct incfs_get_block_count_args args = {};
|
||||||
|
struct data_file *df = get_incfs_data_file(f);
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (!df)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
args.total_blocks_out = df->df_data_block_count;
|
||||||
|
args.filled_blocks_out = atomic_read(&df->df_data_blocks_written);
|
||||||
|
|
||||||
|
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)
|
static long dispatch_ioctl(struct file *f, unsigned int req, unsigned long arg)
|
||||||
{
|
{
|
||||||
switch (req) {
|
switch (req) {
|
||||||
@@ -673,6 +690,8 @@ static long dispatch_ioctl(struct file *f, unsigned int req, unsigned long arg)
|
|||||||
return ioctl_read_file_signature(f, (void __user *)arg);
|
return ioctl_read_file_signature(f, (void __user *)arg);
|
||||||
case INCFS_IOC_GET_FILLED_BLOCKS:
|
case INCFS_IOC_GET_FILLED_BLOCKS:
|
||||||
return ioctl_get_filled_blocks(f, (void __user *)arg);
|
return ioctl_get_filled_blocks(f, (void __user *)arg);
|
||||||
|
case INCFS_IOC_GET_BLOCK_COUNT:
|
||||||
|
return ioctl_get_block_count(f, (void __user *)arg);
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@@ -97,6 +97,10 @@
|
|||||||
#define INCFS_IOC_CREATE_MAPPED_FILE \
|
#define INCFS_IOC_CREATE_MAPPED_FILE \
|
||||||
_IOWR(INCFS_IOCTL_BASE_CODE, 35, struct incfs_create_mapped_file_args)
|
_IOWR(INCFS_IOCTL_BASE_CODE, 35, struct incfs_create_mapped_file_args)
|
||||||
|
|
||||||
|
/* Get number of blocks, total and filled */
|
||||||
|
#define INCFS_IOC_GET_BLOCK_COUNT \
|
||||||
|
_IOR(INCFS_IOCTL_BASE_CODE, 36, struct incfs_get_block_count_args)
|
||||||
|
|
||||||
/* ===== sysfs feature flags ===== */
|
/* ===== sysfs feature flags ===== */
|
||||||
/*
|
/*
|
||||||
* Each flag is represented by a file in /sys/fs/incremental-fs/features
|
* Each flag is represented by a file in /sys/fs/incremental-fs/features
|
||||||
@@ -427,4 +431,12 @@ struct incfs_create_mapped_file_args {
|
|||||||
__aligned_u64 source_offset;
|
__aligned_u64 source_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct incfs_get_block_count_args {
|
||||||
|
/* Total number of data blocks in the file */
|
||||||
|
__u32 total_blocks_out;
|
||||||
|
|
||||||
|
/* Number of filled data blocks in the file */
|
||||||
|
__u32 filled_blocks_out;
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* _UAPI_LINUX_INCREMENTALFS_H */
|
#endif /* _UAPI_LINUX_INCREMENTALFS_H */
|
||||||
|
@@ -3052,6 +3052,99 @@ out:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int validate_block_count(const char *mount_dir, struct test_file *file)
|
||||||
|
{
|
||||||
|
int block_cnt = 1 + (file->size - 1) / INCFS_DATA_FILE_BLOCK_SIZE;
|
||||||
|
char *filename = concat_file_name(mount_dir, file->name);
|
||||||
|
int fd;
|
||||||
|
struct incfs_get_block_count_args bca = {};
|
||||||
|
int test_result = TEST_FAILURE;
|
||||||
|
int result;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
fd = open(filename, O_RDONLY | O_CLOEXEC);
|
||||||
|
if (fd <= 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
result = ioctl(fd, INCFS_IOC_GET_BLOCK_COUNT, &bca);
|
||||||
|
if (result != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (bca.total_blocks_out != block_cnt ||
|
||||||
|
bca.filled_blocks_out != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
for (i = 0; i < block_cnt; i += 2)
|
||||||
|
if (emit_test_block(mount_dir, file, i))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
result = ioctl(fd, INCFS_IOC_GET_BLOCK_COUNT, &bca);
|
||||||
|
if (result != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (bca.total_blocks_out != block_cnt ||
|
||||||
|
bca.filled_blocks_out != (block_cnt + 1) / 2)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
fd = open(filename, O_RDONLY | O_CLOEXEC);
|
||||||
|
if (fd <= 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
result = ioctl(fd, INCFS_IOC_GET_BLOCK_COUNT, &bca);
|
||||||
|
if (result != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (bca.total_blocks_out != block_cnt ||
|
||||||
|
bca.filled_blocks_out != (block_cnt + 1) / 2)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
test_result = TEST_SUCCESS;
|
||||||
|
out:
|
||||||
|
free(filename);
|
||||||
|
close(fd);
|
||||||
|
return test_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int block_count_test(const char *mount_dir)
|
||||||
|
{
|
||||||
|
char *backing_dir;
|
||||||
|
int result = TEST_FAILURE;
|
||||||
|
int cmd_fd = -1;
|
||||||
|
int i;
|
||||||
|
struct test_files_set test = get_test_files_set();
|
||||||
|
const int file_num = test.files_count;
|
||||||
|
|
||||||
|
backing_dir = create_backing_dir(mount_dir);
|
||||||
|
if (!backing_dir)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
if (mount_fs_opt(mount_dir, backing_dir, "readahead=0", false) != 0)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
cmd_fd = open_commands_file(mount_dir);
|
||||||
|
if (cmd_fd < 0)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
for (i = 0; i < file_num; ++i) {
|
||||||
|
struct test_file *file = &test.files[i];
|
||||||
|
|
||||||
|
if (emit_file(cmd_fd, NULL, file->name, &file->id, file->size,
|
||||||
|
NULL) < 0)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
result = validate_block_count(mount_dir, file);
|
||||||
|
if (result)
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
failure:
|
||||||
|
close(cmd_fd);
|
||||||
|
umount(mount_dir);
|
||||||
|
free(backing_dir);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static char *setup_mount_dir()
|
static char *setup_mount_dir()
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
@@ -3163,6 +3256,7 @@ int main(int argc, char *argv[])
|
|||||||
MAKE_TEST(large_file_test),
|
MAKE_TEST(large_file_test),
|
||||||
MAKE_TEST(mapped_file_test),
|
MAKE_TEST(mapped_file_test),
|
||||||
MAKE_TEST(compatibility_test),
|
MAKE_TEST(compatibility_test),
|
||||||
|
MAKE_TEST(block_count_test),
|
||||||
};
|
};
|
||||||
#undef MAKE_TEST
|
#undef MAKE_TEST
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user