Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5b188fc3 authored by Paul Lawrence's avatar Paul Lawrence
Browse files

ANDROID: Incremental fs: Add .incomplete folder



Bug: 165929150
Test: incfs_test passes
Signed-off-by: default avatarPaul Lawrence <paullawrence@google.com>
Change-Id: Ib6952391aea76bf0318cbad8da7a1276f8f9e8ba
parent a040750a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include "vfs.h"

#include "data_mgmt.h"
#include "format.h"
#include "internal.h"
#include "pseudo_files.h"

@@ -380,9 +381,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;
@@ -519,6 +520,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;
@@ -580,6 +613,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.
@@ -814,6 +849,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);
@@ -846,17 +886,26 @@ static int dir_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
	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) {
@@ -872,17 +921,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;
@@ -914,21 +992,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:
@@ -964,6 +1039,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;
@@ -1016,6 +1097,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);
@@ -1047,8 +1134,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;
	}
@@ -1061,8 +1149,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;
	}
@@ -1400,10 +1489,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);
@@ -1456,15 +1548,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)) {
+29 −0
Original line number Diff line number Diff line
@@ -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;
}