ANDROID: Incremental fs: Add .incomplete folder

Bug: 165929150
Test: incfs_test passes
Signed-off-by: Paul Lawrence <paullawrence@google.com>
Change-Id: Ib6952391aea76bf0318cbad8da7a1276f8f9e8ba
This commit is contained in:
Paul Lawrence
2020-09-09 08:16:37 -07:00
parent ad059a7458
commit 7e7bfa94e5
5 changed files with 217 additions and 37 deletions
+1
View File
@@ -110,6 +110,7 @@ void incfs_free_mount_info(struct mount_info *mi)
flush_delayed_work(&mi->mi_log.ml_wakeup_work);
dput(mi->mi_index_dir);
dput(mi->mi_incomplete_dir);
path_put(&mi->mi_backing_dir_path);
mutex_destroy(&mi->mi_dir_struct_mutex);
put_cred(mi->mi_owner);
+2
View File
@@ -114,6 +114,8 @@ struct mount_info {
struct dentry *mi_index_dir;
struct dentry *mi_incomplete_dir;
const struct cred *mi_owner;
struct mount_options mi_options;
+57 -14
View File
@@ -519,6 +519,7 @@ static long ioctl_create_file(struct mount_info *mi,
char *file_id_str = NULL;
struct dentry *index_file_dentry = NULL;
struct dentry *named_file_dentry = NULL;
struct dentry *incomplete_file_dentry = NULL;
struct path parent_dir_path = {};
struct inode *index_dir_inode = NULL;
__le64 size_attr_value = 0;
@@ -526,8 +527,11 @@ static long ioctl_create_file(struct mount_info *mi,
char *attr_value = NULL;
int error = 0;
bool locked = false;
bool index_linked = false;
bool name_linked = false;
bool incomplete_linked = false;
if (!mi || !mi->mi_index_dir) {
if (!mi || !mi->mi_index_dir || !mi->mi_incomplete_dir) {
error = -EFAULT;
goto out;
}
@@ -572,6 +576,12 @@ static long ioctl_create_file(struct mount_info *mi,
goto out;
}
if (parent_dir_path.dentry == mi->mi_incomplete_dir) {
/* Can't create a file directly inside .incomplete */
error = -EBUSY;
goto out;
}
/* Look up a dentry in the parent dir. It should be negative. */
named_file_dentry = incfs_lookup_dentry(parent_dir_path.dentry,
file_name);
@@ -589,6 +599,25 @@ static long ioctl_create_file(struct mount_info *mi,
error = -EEXIST;
goto out;
}
/* Look up a dentry in the incomplete dir. It should be negative. */
incomplete_file_dentry = incfs_lookup_dentry(mi->mi_incomplete_dir,
file_id_str);
if (!incomplete_file_dentry) {
error = -EFAULT;
goto out;
}
if (IS_ERR(incomplete_file_dentry)) {
error = PTR_ERR(incomplete_file_dentry);
incomplete_file_dentry = NULL;
goto out;
}
if (d_really_is_positive(incomplete_file_dentry)) {
/* File with this path already exists. */
error = -EEXIST;
goto out;
}
/* Look up a dentry in the .index dir. It should be negative. */
index_file_dentry = incfs_lookup_dentry(mi->mi_index_dir, file_id_str);
if (!index_file_dentry) {
@@ -623,7 +652,7 @@ static long ioctl_create_file(struct mount_info *mi,
error = chmod(index_file_dentry, args.mode | 0222);
if (error) {
pr_debug("incfs: chmod err: %d\n", error);
goto delete_index_file;
goto out;
}
/* Save the file's ID as an xattr for easy fetching in future. */
@@ -631,7 +660,7 @@ static long ioctl_create_file(struct mount_info *mi,
file_id_str, strlen(file_id_str), XATTR_CREATE);
if (error) {
pr_debug("incfs: vfs_setxattr err:%d\n", error);
goto delete_index_file;
goto out;
}
/* Save the file's size as an xattr for easy fetching in future. */
@@ -641,27 +670,27 @@ static long ioctl_create_file(struct mount_info *mi,
XATTR_CREATE);
if (error) {
pr_debug("incfs: vfs_setxattr err:%d\n", error);
goto delete_index_file;
goto out;
}
/* 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;
goto delete_index_file;
goto out;
}
attr_value = kmalloc(args.file_attr_len, GFP_NOFS);
if (!attr_value) {
error = -ENOMEM;
goto delete_index_file;
goto out;
}
if (copy_from_user(attr_value,
u64_to_user_ptr(args.file_attr),
args.file_attr_len) > 0) {
error = -EFAULT;
goto delete_index_file;
goto out;
}
error = vfs_setxattr(index_file_dentry,
@@ -670,7 +699,7 @@ static long ioctl_create_file(struct mount_info *mi,
XATTR_CREATE);
if (error)
goto delete_index_file;
goto out;
}
/* Initializing a newly created file. */
@@ -679,26 +708,40 @@ static long ioctl_create_file(struct mount_info *mi,
(u8 __user *)args.signature_info,
args.signature_size);
if (error)
goto delete_index_file;
goto out;
index_linked = true;
/* Linking a file with its real name from the requested dir. */
error = incfs_link(index_file_dentry, named_file_dentry);
if (!error)
if (error)
goto out;
name_linked = true;
delete_index_file:
incfs_unlink(index_file_dentry);
if (args.size) {
/* Linking a file with its incomplete entry */
error = incfs_link(index_file_dentry, incomplete_file_dentry);
if (error)
goto out;
incomplete_linked = true;
}
out:
if (error)
if (error) {
pr_debug("incfs: %s err:%d\n", __func__, error);
if (index_linked)
incfs_unlink(index_file_dentry);
if (name_linked)
incfs_unlink(named_file_dentry);
if (incomplete_linked)
incfs_unlink(incomplete_file_dentry);
}
kfree(file_id_str);
kfree(file_name);
kfree(attr_value);
dput(named_file_dentry);
dput(index_file_dentry);
dput(incomplete_file_dentry);
path_put(&parent_dir_path);
if (locked)
mutex_unlock(&mi->mi_dir_struct_mutex);
+128 -23
View File
@@ -16,6 +16,7 @@
#include "vfs.h"
#include "data_mgmt.h"
#include "format.h"
#include "internal.h"
#include "pseudo_files.h"
@@ -381,9 +382,9 @@ static int incfs_init_dentry(struct dentry *dentry, struct path *path)
return 0;
}
static struct dentry *open_or_create_index_dir(struct dentry *backing_dir)
static struct dentry *open_or_create_special_dir(struct dentry *backing_dir,
const char *name)
{
static const char name[] = ".index";
struct dentry *index_dentry;
struct inode *backing_inode = d_inode(backing_dir);
int err = 0;
@@ -520,6 +521,38 @@ static int incfs_rmdir(struct dentry *dentry)
return error;
}
static void maybe_delete_incomplete_file(struct data_file *df)
{
char *file_id_str;
struct dentry *incomplete_file_dentry;
if (atomic_read(&df->df_data_blocks_written) < df->df_data_block_count)
return;
/* This is best effort - there is no useful action to take on failure */
file_id_str = file_id_to_str(df->df_id);
if (!file_id_str)
return;
incomplete_file_dentry = incfs_lookup_dentry(
df->df_mount_info->mi_incomplete_dir,
file_id_str);
if (!incomplete_file_dentry || IS_ERR(incomplete_file_dentry)) {
incomplete_file_dentry = NULL;
goto out;
}
if (!d_really_is_positive(incomplete_file_dentry))
goto out;
vfs_fsync(df->df_backing_file_context->bc_file, 0);
incfs_unlink(incomplete_file_dentry);
out:
dput(incomplete_file_dentry);
kfree(file_id_str);
}
static long ioctl_fill_blocks(struct file *f, void __user *arg)
{
struct incfs_fill_blocks __user *usr_fill_blocks = arg;
@@ -581,6 +614,8 @@ static long ioctl_fill_blocks(struct file *f, void __user *arg)
if (data_buf)
free_pages((unsigned long)data_buf, get_order(data_buf_size));
maybe_delete_incomplete_file(df);
/*
* Only report the error if no records were processed, otherwise
* just return how many were processed successfully.
@@ -815,6 +850,11 @@ static int dir_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
goto out;
}
if (backing_dentry->d_parent == mi->mi_incomplete_dir) {
/* Can't create a subdir inside .incomplete */
err = -EBUSY;
goto out;
}
inode_lock_nested(dir_node->n_backing_inode, I_MUTEX_PARENT);
err = vfs_mkdir(dir_node->n_backing_inode, backing_dentry, mode | 0222);
inode_unlock(dir_node->n_backing_inode);
@@ -847,17 +887,26 @@ path_err:
return err;
}
/* Delete file referenced by backing_dentry and also its hardlink from .index */
static int final_file_delete(struct mount_info *mi,
struct dentry *backing_dentry)
/*
* Delete file referenced by backing_dentry and if appropriate its hardlink
* from .index and .incomplete
*/
static int file_delete(struct mount_info *mi,
struct dentry *backing_dentry,
int nlink)
{
struct dentry *index_file_dentry = NULL;
struct dentry *incomplete_file_dentry = NULL;
/* 2 chars per byte of file ID + 1 char for \0 */
char file_id_str[2 * sizeof(incfs_uuid_t) + 1] = {0};
ssize_t uuid_size = 0;
int error = 0;
WARN_ON(!mutex_is_locked(&mi->mi_dir_struct_mutex));
if (nlink > 3)
goto just_unlink;
uuid_size = vfs_getxattr(backing_dentry, INCFS_XATTR_ID_NAME,
file_id_str, 2 * sizeof(incfs_uuid_t));
if (uuid_size < 0) {
@@ -873,17 +922,46 @@ static int final_file_delete(struct mount_info *mi,
index_file_dentry = incfs_lookup_dentry(mi->mi_index_dir, file_id_str);
if (IS_ERR(index_file_dentry)) {
error = PTR_ERR(index_file_dentry);
index_file_dentry = NULL;
goto out;
}
error = incfs_unlink(backing_dentry);
if (error)
if (d_really_is_positive(index_file_dentry) && nlink > 0)
nlink--;
if (nlink > 2)
goto just_unlink;
incomplete_file_dentry = incfs_lookup_dentry(mi->mi_incomplete_dir,
file_id_str);
if (IS_ERR(incomplete_file_dentry)) {
error = PTR_ERR(incomplete_file_dentry);
incomplete_file_dentry = NULL;
goto out;
}
if (d_really_is_positive(incomplete_file_dentry) && nlink > 0)
nlink--;
if (nlink > 1)
goto just_unlink;
if (d_really_is_positive(index_file_dentry))
error = incfs_unlink(index_file_dentry);
if (error)
goto out;
if (d_really_is_positive(incomplete_file_dentry))
error = incfs_unlink(incomplete_file_dentry);
if (error)
goto out;
just_unlink:
error = incfs_unlink(backing_dentry);
out:
dput(index_file_dentry);
dput(incomplete_file_dentry);
if (error)
pr_debug("incfs: delete_file_from_index err:%d\n", error);
return error;
@@ -915,21 +993,18 @@ static int dir_unlink(struct inode *dir, struct dentry *dentry)
goto out;
}
if (backing_path.dentry->d_parent == mi->mi_incomplete_dir) {
/* Direct unlink from .incomplete are not allowed. */
err = -EBUSY;
goto out;
}
err = vfs_getattr(&backing_path, &stat, STATX_NLINK,
AT_STATX_SYNC_AS_STAT);
if (err)
goto out;
if (stat.nlink == 2) {
/*
* This is the last named link to this file. The only one left
* is in .index. Remove them both now.
*/
err = final_file_delete(mi, backing_path.dentry);
} else {
/* There are other links to this file. Remove just this one. */
err = incfs_unlink(backing_path.dentry);
}
err = file_delete(mi, backing_path.dentry, stat.nlink);
d_drop(dentry);
out:
@@ -965,6 +1040,12 @@ static int dir_link(struct dentry *old_dentry, struct inode *dir,
goto out;
}
if (backing_new_path.dentry->d_parent == mi->mi_incomplete_dir) {
/* Can't link to .incomplete */
error = -EBUSY;
goto out;
}
error = incfs_link(backing_old_path.dentry, backing_new_path.dentry);
if (!error) {
struct inode *inode = NULL;
@@ -1017,6 +1098,12 @@ static int dir_rmdir(struct inode *dir, struct dentry *dentry)
goto out;
}
if (backing_path.dentry == mi->mi_incomplete_dir) {
/* Can't delete .incomplete */
err = -EBUSY;
goto out;
}
err = incfs_rmdir(backing_path.dentry);
if (!err)
d_drop(dentry);
@@ -1048,8 +1135,9 @@ static int dir_rename(struct inode *old_dir, struct dentry *old_dentry,
backing_old_dentry = get_incfs_dentry(old_dentry)->backing_path.dentry;
if (!backing_old_dentry || backing_old_dentry == mi->mi_index_dir) {
/* Renaming .index not allowed */
if (!backing_old_dentry || backing_old_dentry == mi->mi_index_dir ||
backing_old_dentry == mi->mi_incomplete_dir) {
/* Renaming .index or .incomplete not allowed */
error = -EBUSY;
goto exit;
}
@@ -1062,8 +1150,9 @@ static int dir_rename(struct inode *old_dir, struct dentry *old_dentry,
backing_new_dir_dentry = dget_parent(backing_new_dentry);
target_inode = d_inode(new_dentry);
if (backing_old_dir_dentry == mi->mi_index_dir) {
/* Direct moves from .index are not allowed. */
if (backing_old_dir_dentry == mi->mi_index_dir ||
backing_old_dir_dentry == mi->mi_incomplete_dir) {
/* Direct moves from .index or .incomplete are not allowed. */
error = -EBUSY;
goto out;
}
@@ -1401,10 +1490,13 @@ static ssize_t incfs_listxattr(struct dentry *d, char *list, size_t size)
struct dentry *incfs_mount_fs(struct file_system_type *type, int flags,
const char *dev_name, void *data)
{
static const char index_name[] = ".index";
static const char incomplete_name[] = ".incomplete";
struct mount_options options = {};
struct mount_info *mi = NULL;
struct path backing_dir_path = {};
struct dentry *index_dir;
struct dentry *index_dir = NULL;
struct dentry *incomplete_dir = NULL;
struct super_block *src_fs_sb = NULL;
struct inode *root_inode = NULL;
struct super_block *sb = sget(type, NULL, set_anon_super, flags, NULL);
@@ -1457,15 +1549,28 @@ struct dentry *incfs_mount_fs(struct file_system_type *type, int flags,
goto err;
}
index_dir = open_or_create_index_dir(backing_dir_path.dentry);
index_dir = open_or_create_special_dir(backing_dir_path.dentry,
index_name);
if (IS_ERR_OR_NULL(index_dir)) {
error = PTR_ERR(index_dir);
pr_err("incfs: Can't find or create .index dir in %s\n",
dev_name);
/* No need to null index_dir since we don't put it */
goto err;
}
mi->mi_index_dir = index_dir;
incomplete_dir = open_or_create_special_dir(backing_dir_path.dentry,
incomplete_name);
if (IS_ERR_OR_NULL(incomplete_dir)) {
error = PTR_ERR(incomplete_dir);
pr_err("incfs: Can't find or create .incomplete dir in %s\n",
dev_name);
/* No need to null incomplete_dir since we don't put it */
goto err;
}
mi->mi_incomplete_dir = incomplete_dir;
sb->s_fs_info = mi;
root_inode = fetch_regular_inode(sb, backing_dir_path.dentry);
if (IS_ERR(root_inode)) {
@@ -214,6 +214,17 @@ static char *get_index_filename(const char *mnt_dir, incfs_uuid_t id)
return strdup(path);
}
static char *get_incomplete_filename(const char *mnt_dir, incfs_uuid_t id)
{
char path[FILENAME_MAX];
char str_id[1 + 2 * sizeof(id)];
bin2hex(str_id, id.bytes, sizeof(id.bytes));
snprintf(path, ARRAY_SIZE(path), "%s/.incomplete/%s", mnt_dir, str_id);
return strdup(path);
}
int open_file_by_id(const char *mnt_dir, incfs_uuid_t id, bool use_ioctl)
{
char *path = get_index_filename(mnt_dir, id);
@@ -974,6 +985,7 @@ static bool iterate_directory(const char *dir_to_iterate, bool root,
{INCFS_PENDING_READS_FILENAME, true, false},
{INCFS_BLOCKS_WRITTEN_FILENAME, true, false},
{".index", true, false},
{".incomplete", true, false},
{"..", false, false},
{".", false, false},
};
@@ -3189,11 +3201,21 @@ static int validate_data_block_count(const char *mount_dir,
int test_result = TEST_FAILURE;
char *filename = NULL;
char *incomplete_filename = NULL;
struct stat stat_buf_incomplete, stat_buf_file;
int fd = -1;
struct incfs_get_block_count_args bca = {};
int i;
TEST(filename = concat_file_name(mount_dir, file->name), filename);
TEST(incomplete_filename = get_incomplete_filename(mount_dir, file->id),
incomplete_filename);
TESTEQUAL(stat(filename, &stat_buf_file), 0);
TESTEQUAL(stat(incomplete_filename, &stat_buf_incomplete), 0);
TESTEQUAL(stat_buf_file.st_ino, stat_buf_incomplete.st_ino);
TESTEQUAL(stat_buf_file.st_nlink, 3);
TEST(fd = open(filename, O_RDONLY | O_CLOEXEC), fd != -1);
TESTEQUAL(ioctl(fd, INCFS_IOC_GET_BLOCK_COUNT, &bca), 0);
TESTEQUAL(bca.total_data_blocks_out, total_data_blocks);
@@ -3217,9 +3239,16 @@ static int validate_data_block_count(const char *mount_dir,
0, 0),
0);
for (i = 1; i < total_data_blocks; i += 2)
TESTEQUAL(emit_test_block(mount_dir, file, i), 0);
TESTEQUAL(stat(incomplete_filename, &stat_buf_incomplete), -1);
TESTEQUAL(errno, ENOENT);
test_result = TEST_SUCCESS;
out:
close(fd);
free(incomplete_filename);
free(filename);
return test_result;
}