Commit 40226a3d authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull vboxsf fixes from Hans de Goede:
 "This adds support for the atomic_open directory-inode op to vboxsf.

  Note this is not just an enhancement this also fixes an actual issue
  which users are hitting, see the commit message of the "boxsf: Add
  support for the atomic_open directory-inode" patch"

* tag 'vboxsf-v5.14-1' of git://git.kernel.org/pub/scm/linux/kernel/git/hansg/linux:
  vboxsf: Add support for the atomic_open directory-inode op
  vboxsf: Add vboxsf_[create|release]_sf_handle() helpers
  vboxsf: Make vboxsf_dir_create() return the handle for the created file
  vboxsf: Honor excl flag to the dir-inode create op
parents f02bf857 52dfd86a
Loading
Loading
Loading
Loading
+65 −11
Original line number Diff line number Diff line
@@ -253,7 +253,7 @@ static int vboxsf_dir_instantiate(struct inode *parent, struct dentry *dentry,
}

static int vboxsf_dir_create(struct inode *parent, struct dentry *dentry,
			     umode_t mode, int is_dir)
			     umode_t mode, bool is_dir, bool excl, u64 *handle_ret)
{
	struct vboxsf_inode *sf_parent_i = VBOXSF_I(parent);
	struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb);
@@ -261,10 +261,12 @@ static int vboxsf_dir_create(struct inode *parent, struct dentry *dentry,
	int err;

	params.handle = SHFL_HANDLE_NIL;
	params.create_flags = SHFL_CF_ACT_CREATE_IF_NEW |
			      SHFL_CF_ACT_FAIL_IF_EXISTS |
			      SHFL_CF_ACCESS_READWRITE |
			      (is_dir ? SHFL_CF_DIRECTORY : 0);
	params.create_flags = SHFL_CF_ACT_CREATE_IF_NEW | SHFL_CF_ACCESS_READWRITE;
	if (is_dir)
		params.create_flags |= SHFL_CF_DIRECTORY;
	if (excl)
		params.create_flags |= SHFL_CF_ACT_FAIL_IF_EXISTS;

	params.info.attr.mode = (mode & 0777) |
				(is_dir ? SHFL_TYPE_DIRECTORY : SHFL_TYPE_FILE);
	params.info.attr.additional = SHFLFSOBJATTRADD_NOTHING;
@@ -276,30 +278,81 @@ static int vboxsf_dir_create(struct inode *parent, struct dentry *dentry,
	if (params.result != SHFL_FILE_CREATED)
		return -EPERM;

	vboxsf_close(sbi->root, params.handle);

	err = vboxsf_dir_instantiate(parent, dentry, &params.info);
	if (err)
		return err;
		goto out;

	/* parent directory access/change time changed */
	sf_parent_i->force_restat = 1;

	return 0;
out:
	if (err == 0 && handle_ret)
		*handle_ret = params.handle;
	else
		vboxsf_close(sbi->root, params.handle);

	return err;
}

static int vboxsf_dir_mkfile(struct user_namespace *mnt_userns,
			     struct inode *parent, struct dentry *dentry,
			     umode_t mode, bool excl)
{
	return vboxsf_dir_create(parent, dentry, mode, 0);
	return vboxsf_dir_create(parent, dentry, mode, false, excl, NULL);
}

static int vboxsf_dir_mkdir(struct user_namespace *mnt_userns,
			    struct inode *parent, struct dentry *dentry,
			    umode_t mode)
{
	return vboxsf_dir_create(parent, dentry, mode, 1);
	return vboxsf_dir_create(parent, dentry, mode, true, true, NULL);
}

static int vboxsf_dir_atomic_open(struct inode *parent, struct dentry *dentry,
				  struct file *file, unsigned int flags, umode_t mode)
{
	struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb);
	struct vboxsf_handle *sf_handle;
	struct dentry *res = NULL;
	u64 handle;
	int err;

	if (d_in_lookup(dentry)) {
		res = vboxsf_dir_lookup(parent, dentry, 0);
		if (IS_ERR(res))
			return PTR_ERR(res);

		if (res)
			dentry = res;
	}

	/* Only creates */
	if (!(flags & O_CREAT) || d_really_is_positive(dentry))
		return finish_no_open(file, res);

	err = vboxsf_dir_create(parent, dentry, mode, false, flags & O_EXCL, &handle);
	if (err)
		goto out;

	sf_handle = vboxsf_create_sf_handle(d_inode(dentry), handle, SHFL_CF_ACCESS_READWRITE);
	if (IS_ERR(sf_handle)) {
		vboxsf_close(sbi->root, handle);
		err = PTR_ERR(sf_handle);
		goto out;
	}

	err = finish_open(file, dentry, generic_file_open);
	if (err) {
		/* This also closes the handle passed to vboxsf_create_sf_handle() */
		vboxsf_release_sf_handle(d_inode(dentry), sf_handle);
		goto out;
	}

	file->private_data = sf_handle;
	file->f_mode |= FMODE_CREATED;
out:
	dput(res);
	return err;
}

static int vboxsf_dir_unlink(struct inode *parent, struct dentry *dentry)
@@ -422,6 +475,7 @@ const struct inode_operations vboxsf_dir_iops = {
	.lookup  = vboxsf_dir_lookup,
	.create  = vboxsf_dir_mkfile,
	.mkdir   = vboxsf_dir_mkdir,
	.atomic_open = vboxsf_dir_atomic_open,
	.rmdir   = vboxsf_dir_unlink,
	.unlink  = vboxsf_dir_unlink,
	.rename  = vboxsf_dir_rename,
+44 −27
Original line number Diff line number Diff line
@@ -20,17 +20,39 @@ struct vboxsf_handle {
	struct list_head head;
};

static int vboxsf_file_open(struct inode *inode, struct file *file)
struct vboxsf_handle *vboxsf_create_sf_handle(struct inode *inode,
					      u64 handle, u32 access_flags)
{
	struct vboxsf_inode *sf_i = VBOXSF_I(inode);
	struct shfl_createparms params = {};
	struct vboxsf_handle *sf_handle;
	u32 access_flags = 0;
	int err;

	sf_handle = kmalloc(sizeof(*sf_handle), GFP_KERNEL);
	if (!sf_handle)
		return -ENOMEM;
		return ERR_PTR(-ENOMEM);

	/* the host may have given us different attr then requested */
	sf_i->force_restat = 1;

	/* init our handle struct and add it to the inode's handles list */
	sf_handle->handle = handle;
	sf_handle->root = VBOXSF_SBI(inode->i_sb)->root;
	sf_handle->access_flags = access_flags;
	kref_init(&sf_handle->refcount);

	mutex_lock(&sf_i->handle_list_mutex);
	list_add(&sf_handle->head, &sf_i->handle_list);
	mutex_unlock(&sf_i->handle_list_mutex);

	return sf_handle;
}

static int vboxsf_file_open(struct inode *inode, struct file *file)
{
	struct vboxsf_sbi *sbi = VBOXSF_SBI(inode->i_sb);
	struct shfl_createparms params = {};
	struct vboxsf_handle *sf_handle;
	u32 access_flags = 0;
	int err;

	/*
	 * We check the value of params.handle afterwards to find out if
@@ -83,23 +105,14 @@ static int vboxsf_file_open(struct inode *inode, struct file *file)
	err = vboxsf_create_at_dentry(file_dentry(file), &params);
	if (err == 0 && params.handle == SHFL_HANDLE_NIL)
		err = (params.result == SHFL_FILE_EXISTS) ? -EEXIST : -ENOENT;
	if (err) {
		kfree(sf_handle);
	if (err)
		return err;
	}

	/* the host may have given us different attr then requested */
	sf_i->force_restat = 1;

	/* init our handle struct and add it to the inode's handles list */
	sf_handle->handle = params.handle;
	sf_handle->root = VBOXSF_SBI(inode->i_sb)->root;
	sf_handle->access_flags = access_flags;
	kref_init(&sf_handle->refcount);

	mutex_lock(&sf_i->handle_list_mutex);
	list_add(&sf_handle->head, &sf_i->handle_list);
	mutex_unlock(&sf_i->handle_list_mutex);
	sf_handle = vboxsf_create_sf_handle(inode, params.handle, access_flags);
	if (IS_ERR(sf_handle)) {
		vboxsf_close(sbi->root, params.handle);
		return PTR_ERR(sf_handle);
	}

	file->private_data = sf_handle;
	return 0;
@@ -114,22 +127,26 @@ static void vboxsf_handle_release(struct kref *refcount)
	kfree(sf_handle);
}

static int vboxsf_file_release(struct inode *inode, struct file *file)
void vboxsf_release_sf_handle(struct inode *inode, struct vboxsf_handle *sf_handle)
{
	struct vboxsf_inode *sf_i = VBOXSF_I(inode);
	struct vboxsf_handle *sf_handle = file->private_data;

	mutex_lock(&sf_i->handle_list_mutex);
	list_del(&sf_handle->head);
	mutex_unlock(&sf_i->handle_list_mutex);

	kref_put(&sf_handle->refcount, vboxsf_handle_release);
}

static int vboxsf_file_release(struct inode *inode, struct file *file)
{
	/*
	 * When a file is closed on our (the guest) side, we want any subsequent
	 * accesses done on the host side to see all changes done from our side.
	 */
	filemap_write_and_wait(inode->i_mapping);

	mutex_lock(&sf_i->handle_list_mutex);
	list_del(&sf_handle->head);
	mutex_unlock(&sf_i->handle_list_mutex);

	kref_put(&sf_handle->refcount, vboxsf_handle_release);
	vboxsf_release_sf_handle(inode, file->private_data);
	return 0;
}

+7 −0
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@
#define VBOXSF_SBI(sb)	((struct vboxsf_sbi *)(sb)->s_fs_info)
#define VBOXSF_I(i)	container_of(i, struct vboxsf_inode, vfs_inode)

struct vboxsf_handle;

struct vboxsf_options {
	unsigned long ttl;
	kuid_t uid;
@@ -80,6 +82,11 @@ extern const struct file_operations vboxsf_reg_fops;
extern const struct address_space_operations vboxsf_reg_aops;
extern const struct dentry_operations vboxsf_dentry_ops;

/* from file.c */
struct vboxsf_handle *vboxsf_create_sf_handle(struct inode *inode,
					      u64 handle, u32 access_flags);
void vboxsf_release_sf_handle(struct inode *inode, struct vboxsf_handle *sf_handle);

/* from utils.c */
struct inode *vboxsf_new_inode(struct super_block *sb);
int vboxsf_init_inode(struct vboxsf_sbi *sbi, struct inode *inode,