Commit 071a0578 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull overlayfs updates from Miklos Szeredi:

 - Improve performance for certain container setups by introducing a
   "volatile" mode

 - ioctl improvements

 - continue preparation for unprivileged overlay mounts

* tag 'ovl-update-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: use generic vfs_ioc_setflags_prepare() helper
  ovl: support [S|G]ETFLAGS and FS[S|G]ETXATTR ioctls for directories
  ovl: rearrange ovl_can_list()
  ovl: enumerate private xattrs
  ovl: pass ovl_fs down to functions accessing private xattrs
  ovl: drop flags argument from ovl_do_setxattr()
  ovl: adhere to the vfs_ vs. ovl_do_ conventions for xattrs
  ovl: use ovl_do_getxattr() for private xattr
  ovl: fold ovl_getxattr() into ovl_get_redirect_xattr()
  ovl: clean up ovl_getxattr() in copy_up.c
  duplicate ovl_getxattr()
  ovl: provide a mount option "volatile"
  ovl: check for incompatible features in work dir
parents fad70111 be4df0ce
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -564,6 +564,25 @@ Note: the mount options index=off,nfs_export=on are conflicting for a
read-write mount and will result in an error.


Volatile mount
--------------

This is enabled with the "volatile" mount option.  Volatile mounts are not
guaranteed to survive a crash.  It is strongly recommended that volatile
mounts are only used if data written to the overlay can be recreated
without significant effort.

The advantage of mounting with the "volatile" option is that all forms of
sync calls to the upper filesystem are omitted.

When overlay is mounted with "volatile" option, the directory
"$workdir/work/incompat/volatile" is created.  During next mount, overlay
checks for this directory and refuses to mount if present. This is a strong
indicator that user should throw away upper and work directories and create
fresh one. In very limited cases where the user knows that the system has
not crashed and contents of upperdir are intact, The "volatile" directory
can be removed.

Testsuite
---------

+44 −15
Original line number Diff line number Diff line
@@ -43,7 +43,8 @@ static bool ovl_must_copy_xattr(const char *name)
	       !strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN);
}

int ovl_copy_xattr(struct dentry *old, struct dentry *new)
int ovl_copy_xattr(struct super_block *sb, struct dentry *old,
		   struct dentry *new)
{
	ssize_t list_size, size, value_size = 0;
	char *buf, *name, *value = NULL;
@@ -81,7 +82,7 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
		}
		list_size -= slen;

		if (ovl_is_private_xattr(name))
		if (ovl_is_private_xattr(sb, name))
			continue;
retry:
		size = vfs_getxattr(old, name, value, value_size);
@@ -128,7 +129,8 @@ out:
	return error;
}

static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)
static int ovl_copy_up_data(struct ovl_fs *ofs, struct path *old,
			    struct path *new, loff_t len)
{
	struct file *old_file;
	struct file *new_file;
@@ -218,7 +220,7 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)
		len -= bytes;
	}
out:
	if (!error)
	if (!error && ovl_should_sync(ofs))
		error = vfs_fsync(new_file, 0);
	fput(new_file);
out_fput:
@@ -354,7 +356,8 @@ int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
}

/* Store file handle of @upper dir in @index dir entry */
static int ovl_set_upper_fh(struct dentry *upper, struct dentry *index)
static int ovl_set_upper_fh(struct ovl_fs *ofs, struct dentry *upper,
			    struct dentry *index)
{
	const struct ovl_fh *fh;
	int err;
@@ -363,7 +366,7 @@ static int ovl_set_upper_fh(struct dentry *upper, struct dentry *index)
	if (IS_ERR(fh))
		return PTR_ERR(fh);

	err = ovl_do_setxattr(index, OVL_XATTR_UPPER, fh->buf, fh->fb.len, 0);
	err = ovl_do_setxattr(ofs, index, OVL_XATTR_UPPER, fh->buf, fh->fb.len);

	kfree(fh);
	return err;
@@ -408,7 +411,7 @@ static int ovl_create_index(struct dentry *dentry, struct dentry *origin,
	if (IS_ERR(temp))
		goto free_name;

	err = ovl_set_upper_fh(upper, temp);
	err = ovl_set_upper_fh(OVL_FS(dentry->d_sb), upper, temp);
	if (err)
		goto out;

@@ -484,6 +487,7 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)

static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
{
	struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
	int err;

	/*
@@ -499,12 +503,13 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
		upperpath.dentry = temp;

		ovl_path_lowerdata(c->dentry, &datapath);
		err = ovl_copy_up_data(&datapath, &upperpath, c->stat.size);
		err = ovl_copy_up_data(ofs, &datapath, &upperpath,
				       c->stat.size);
		if (err)
			return err;
	}

	err = ovl_copy_xattr(c->lowerpath.dentry, temp);
	err = ovl_copy_xattr(c->dentry->d_sb, c->lowerpath.dentry, temp);
	if (err)
		return err;

@@ -781,9 +786,33 @@ static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode,
	return true;
}

static ssize_t ovl_getxattr(struct dentry *dentry, char *name, char **value)
{
	ssize_t res;
	char *buf;

	res = vfs_getxattr(dentry, name, NULL, 0);
	if (res == -ENODATA || res == -EOPNOTSUPP)
		res = 0;

	if (res > 0) {
		buf = kzalloc(res, GFP_KERNEL);
		if (!buf)
			return -ENOMEM;

		res = vfs_getxattr(dentry, name, buf, res);
		if (res < 0)
			kfree(buf);
		else
			*value = buf;
	}
	return res;
}

/* Copy up data of an inode which was copied up metadata only in the past. */
static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
{
	struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
	struct path upperpath, datapath;
	int err;
	char *capability = NULL;
@@ -799,12 +828,12 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)

	if (c->stat.size) {
		err = cap_size = ovl_getxattr(upperpath.dentry, XATTR_NAME_CAPS,
					      &capability, 0);
		if (err < 0 && err != -ENODATA)
					      &capability);
		if (cap_size < 0)
			goto out;
	}

	err = ovl_copy_up_data(&datapath, &upperpath, c->stat.size);
	err = ovl_copy_up_data(ofs, &datapath, &upperpath, c->stat.size);
	if (err)
		goto out_free;

@@ -813,14 +842,14 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
	 * don't want that to happen for normal copy-up operation.
	 */
	if (capability) {
		err = ovl_do_setxattr(upperpath.dentry, XATTR_NAME_CAPS,
		err = vfs_setxattr(upperpath.dentry, XATTR_NAME_CAPS,
				   capability, cap_size, 0);
		if (err)
			goto out_free;
	}


	err = vfs_removexattr(upperpath.dentry, OVL_XATTR_METACOPY);
	err = ovl_do_removexattr(ofs, upperpath.dentry, OVL_XATTR_METACOPY);
	if (err)
		goto out_free;

+1 −1
Original line number Diff line number Diff line
@@ -394,7 +394,7 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
	if (IS_ERR(opaquedir))
		goto out_unlock;

	err = ovl_copy_xattr(upper, opaquedir);
	err = ovl_copy_xattr(dentry->d_sb, upper, opaquedir);
	if (err)
		goto out_cleanup;

+1 −1
Original line number Diff line number Diff line
@@ -752,7 +752,7 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
			goto out_err;
	}
	if (index) {
		err = ovl_verify_origin(index, origin.dentry, false);
		err = ovl_verify_origin(ofs, index, origin.dentry, false);
		if (err)
			goto out_err;
	}
+52 −36
Original line number Diff line number Diff line
@@ -136,6 +136,13 @@ static int ovl_real_fdget_meta(const struct file *file, struct fd *real,

static int ovl_real_fdget(const struct file *file, struct fd *real)
{
	if (d_is_dir(file_dentry(file))) {
		real->flags = 0;
		real->file = ovl_dir_real_file(file, false);

		return PTR_ERR_OR_ZERO(real->file);
	}

	return ovl_real_fdget_meta(file, real, false);
}

@@ -331,6 +338,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
	struct fd real;
	const struct cred *old_cred;
	ssize_t ret;
	int ifl = iocb->ki_flags;

	if (!iov_iter_count(iter))
		return 0;
@@ -346,11 +354,14 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
	if (ret)
		goto out_unlock;

	if (!ovl_should_sync(OVL_FS(inode->i_sb)))
		ifl &= ~(IOCB_DSYNC | IOCB_SYNC);

	old_cred = ovl_override_creds(file_inode(file)->i_sb);
	if (is_sync_kiocb(iocb)) {
		file_start_write(real.file);
		ret = vfs_iter_write(real.file, iter, &iocb->ki_pos,
				     ovl_iocb_to_rwf(iocb->ki_flags));
				     ovl_iocb_to_rwf(ifl));
		file_end_write(real.file);
		/* Update size */
		ovl_copyattr(ovl_inode_real(inode), inode);
@@ -370,6 +381,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
		real.flags = 0;
		aio_req->orig_iocb = iocb;
		kiocb_clone(&aio_req->iocb, iocb, real.file);
		aio_req->iocb.ki_flags = ifl;
		aio_req->iocb.ki_complete = ovl_aio_rw_complete;
		ret = vfs_iocb_iter_write(real.file, &aio_req->iocb, iter);
		if (ret != -EIOCBQUEUED)
@@ -433,6 +445,9 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
	const struct cred *old_cred;
	int ret;

	if (!ovl_should_sync(OVL_FS(file_inode(file)->i_sb)))
		return 0;

	ret = ovl_real_fdget_meta(file, &real, !datasync);
	if (ret)
		return ret;
@@ -544,12 +559,28 @@ static long ovl_real_ioctl(struct file *file, unsigned int cmd,
	return ret;
}

static unsigned int ovl_iflags_to_fsflags(unsigned int iflags)
{
	unsigned int flags = 0;

	if (iflags & S_SYNC)
		flags |= FS_SYNC_FL;
	if (iflags & S_APPEND)
		flags |= FS_APPEND_FL;
	if (iflags & S_IMMUTABLE)
		flags |= FS_IMMUTABLE_FL;
	if (iflags & S_NOATIME)
		flags |= FS_NOATIME_FL;

	return flags;
}

static long ovl_ioctl_set_flags(struct file *file, unsigned int cmd,
				unsigned long arg, unsigned int iflags)
				unsigned long arg, unsigned int flags)
{
	long ret;
	struct inode *inode = file_inode(file);
	unsigned int old_iflags;
	unsigned int oldflags;

	if (!inode_owner_or_capable(inode))
		return -EACCES;
@@ -561,10 +592,9 @@ static long ovl_ioctl_set_flags(struct file *file, unsigned int cmd,
	inode_lock(inode);

	/* Check the capability before cred override */
	ret = -EPERM;
	old_iflags = READ_ONCE(inode->i_flags);
	if (((iflags ^ old_iflags) & (S_APPEND | S_IMMUTABLE)) &&
	    !capable(CAP_LINUX_IMMUTABLE))
	oldflags = ovl_iflags_to_fsflags(READ_ONCE(inode->i_flags));
	ret = vfs_ioc_setflags_prepare(inode, oldflags, flags);
	if (ret)
		goto unlock;

	ret = ovl_maybe_copy_up(file_dentry(file), O_WRONLY);
@@ -583,22 +613,6 @@ unlock:

}

static unsigned int ovl_fsflags_to_iflags(unsigned int flags)
{
	unsigned int iflags = 0;

	if (flags & FS_SYNC_FL)
		iflags |= S_SYNC;
	if (flags & FS_APPEND_FL)
		iflags |= S_APPEND;
	if (flags & FS_IMMUTABLE_FL)
		iflags |= S_IMMUTABLE;
	if (flags & FS_NOATIME_FL)
		iflags |= S_NOATIME;

	return iflags;
}

static long ovl_ioctl_set_fsflags(struct file *file, unsigned int cmd,
				  unsigned long arg)
{
@@ -607,24 +621,23 @@ static long ovl_ioctl_set_fsflags(struct file *file, unsigned int cmd,
	if (get_user(flags, (int __user *) arg))
		return -EFAULT;

	return ovl_ioctl_set_flags(file, cmd, arg,
				   ovl_fsflags_to_iflags(flags));
	return ovl_ioctl_set_flags(file, cmd, arg, flags);
}

static unsigned int ovl_fsxflags_to_iflags(unsigned int xflags)
static unsigned int ovl_fsxflags_to_fsflags(unsigned int xflags)
{
	unsigned int iflags = 0;
	unsigned int flags = 0;

	if (xflags & FS_XFLAG_SYNC)
		iflags |= S_SYNC;
		flags |= FS_SYNC_FL;
	if (xflags & FS_XFLAG_APPEND)
		iflags |= S_APPEND;
		flags |= FS_APPEND_FL;
	if (xflags & FS_XFLAG_IMMUTABLE)
		iflags |= S_IMMUTABLE;
		flags |= FS_IMMUTABLE_FL;
	if (xflags & FS_XFLAG_NOATIME)
		iflags |= S_NOATIME;
		flags |= FS_NOATIME_FL;

	return iflags;
	return flags;
}

static long ovl_ioctl_set_fsxflags(struct file *file, unsigned int cmd,
@@ -637,10 +650,10 @@ static long ovl_ioctl_set_fsxflags(struct file *file, unsigned int cmd,
		return -EFAULT;

	return ovl_ioctl_set_flags(file, cmd, arg,
				   ovl_fsxflags_to_iflags(fa.fsx_xflags));
				   ovl_fsxflags_to_fsflags(fa.fsx_xflags));
}

static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	long ret;

@@ -665,8 +678,8 @@ static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
	return ret;
}

static long ovl_compat_ioctl(struct file *file, unsigned int cmd,
			     unsigned long arg)
#ifdef CONFIG_COMPAT
long ovl_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	switch (cmd) {
	case FS_IOC32_GETFLAGS:
@@ -683,6 +696,7 @@ static long ovl_compat_ioctl(struct file *file, unsigned int cmd,

	return ovl_ioctl(file, cmd, arg);
}
#endif

enum ovl_copyop {
	OVL_COPY,
@@ -784,7 +798,9 @@ const struct file_operations ovl_file_operations = {
	.fallocate	= ovl_fallocate,
	.fadvise	= ovl_fadvise,
	.unlocked_ioctl	= ovl_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= ovl_compat_ioctl,
#endif
	.splice_read    = ovl_splice_read,
	.splice_write   = ovl_splice_write,

Loading