overlayfs update for 5.10

-----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCX4n9mwAKCRDh3BK/laaZ
 PB7EAP0cxydeomN0m29SpugawMFxzGpB/GnBEr0Qdonz5BJG7wD9EaF9dsLmGbXY
 Q2P/nbTYmNFC3Kz7xJAZNqmg86AgmQU=
 =o+01
 -----END PGP SIGNATURE-----

Merge tag 'ovl-update-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs

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
This commit is contained in:
Linus Torvalds 2020-10-16 15:29:46 -07:00
commit 071a0578b0
12 changed files with 446 additions and 200 deletions

View File

@ -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
---------

View File

@ -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,
capability, cap_size, 0);
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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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,

View File

@ -327,7 +327,7 @@ static const char *ovl_get_link(struct dentry *dentry,
return p;
}
bool ovl_is_private_xattr(const char *name)
bool ovl_is_private_xattr(struct super_block *sb, const char *name)
{
return strncmp(name, OVL_XATTR_PREFIX,
sizeof(OVL_XATTR_PREFIX) - 1) == 0;
@ -391,15 +391,18 @@ int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
return res;
}
static bool ovl_can_list(const char *s)
static bool ovl_can_list(struct super_block *sb, const char *s)
{
/* Never list private (.overlay) */
if (ovl_is_private_xattr(sb, s))
return false;
/* List all non-trusted xatts */
if (strncmp(s, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) != 0)
return true;
/* Never list trusted.overlay, list other trusted for superuser only */
return !ovl_is_private_xattr(s) &&
ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN);
/* list other trusted for superuser only */
return ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN);
}
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
@ -425,7 +428,7 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
return -EIO;
len -= slen;
if (!ovl_can_list(s)) {
if (!ovl_can_list(dentry->d_sb, s)) {
res -= slen;
memmove(s, s + slen, len);
} else {
@ -722,8 +725,8 @@ static int ovl_set_nlink_common(struct dentry *dentry,
if (WARN_ON(len >= sizeof(buf)))
return -EIO;
return ovl_do_setxattr(ovl_dentry_upper(dentry),
OVL_XATTR_NLINK, buf, len, 0);
return ovl_do_setxattr(OVL_FS(inode->i_sb), ovl_dentry_upper(dentry),
OVL_XATTR_NLINK, buf, len);
}
int ovl_set_nlink_upper(struct dentry *dentry)
@ -736,7 +739,7 @@ int ovl_set_nlink_lower(struct dentry *dentry)
return ovl_set_nlink_common(dentry, ovl_dentry_lower(dentry), "L%+i");
}
unsigned int ovl_get_nlink(struct dentry *lowerdentry,
unsigned int ovl_get_nlink(struct ovl_fs *ofs, struct dentry *lowerdentry,
struct dentry *upperdentry,
unsigned int fallback)
{
@ -748,7 +751,8 @@ unsigned int ovl_get_nlink(struct dentry *lowerdentry,
if (!lowerdentry || !upperdentry || d_inode(lowerdentry)->i_nlink == 1)
return fallback;
err = vfs_getxattr(upperdentry, OVL_XATTR_NLINK, &buf, sizeof(buf) - 1);
err = ovl_do_getxattr(ofs, upperdentry, OVL_XATTR_NLINK,
&buf, sizeof(buf) - 1);
if (err < 0)
goto fail;
@ -946,6 +950,7 @@ static struct inode *ovl_iget5(struct super_block *sb, struct inode *newinode,
struct inode *ovl_get_inode(struct super_block *sb,
struct ovl_inode_params *oip)
{
struct ovl_fs *ofs = OVL_FS(sb);
struct dentry *upperdentry = oip->upperdentry;
struct ovl_path *lowerpath = oip->lowerpath;
struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL;
@ -993,7 +998,8 @@ struct inode *ovl_get_inode(struct super_block *sb,
/* Recalculate nlink for non-dir due to indexing */
if (!is_dir)
nlink = ovl_get_nlink(lowerdentry, upperdentry, nlink);
nlink = ovl_get_nlink(ofs, lowerdentry, upperdentry,
nlink);
set_nlink(inode, nlink);
ino = key->i_ino;
} else {
@ -1009,7 +1015,7 @@ struct inode *ovl_get_inode(struct super_block *sb,
ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev);
ovl_inode_init(inode, oip, ino, fsid);
if (upperdentry && ovl_is_impuredir(upperdentry))
if (upperdentry && ovl_is_impuredir(sb, upperdentry))
ovl_set_flag(OVL_IMPURE, inode);
if (oip->index)
@ -1023,7 +1029,7 @@ struct inode *ovl_get_inode(struct super_block *sb,
/* Check for non-merge dir that may have whiteouts */
if (is_dir) {
if (((upperdentry && lowerdentry) || oip->numlower > 1) ||
ovl_check_origin_xattr(upperdentry ?: lowerdentry)) {
ovl_check_origin_xattr(ofs, upperdentry ?: lowerdentry)) {
ovl_set_flag(OVL_WHITEOUTS, inode);
}
}

View File

@ -30,8 +30,9 @@ static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d,
{
int res;
char *buf;
struct ovl_fs *ofs = OVL_FS(d->sb);
buf = ovl_get_redirect_xattr(dentry, prelen + strlen(post));
buf = ovl_get_redirect_xattr(ofs, dentry, prelen + strlen(post));
if (IS_ERR_OR_NULL(buf))
return PTR_ERR(buf);
@ -104,12 +105,13 @@ int ovl_check_fb_len(struct ovl_fb *fb, int fb_len)
return 0;
}
static struct ovl_fh *ovl_get_fh(struct dentry *dentry, const char *name)
static struct ovl_fh *ovl_get_fh(struct ovl_fs *ofs, struct dentry *dentry,
enum ovl_xattr ox)
{
int res, err;
struct ovl_fh *fh = NULL;
res = vfs_getxattr(dentry, name, NULL, 0);
res = ovl_do_getxattr(ofs, dentry, ox, NULL, 0);
if (res < 0) {
if (res == -ENODATA || res == -EOPNOTSUPP)
return NULL;
@ -123,7 +125,7 @@ static struct ovl_fh *ovl_get_fh(struct dentry *dentry, const char *name)
if (!fh)
return ERR_PTR(-ENOMEM);
res = vfs_getxattr(dentry, name, fh->buf, res);
res = ovl_do_getxattr(ofs, dentry, ox, fh->buf, res);
if (res < 0)
goto fail;
@ -186,9 +188,9 @@ struct dentry *ovl_decode_real_fh(struct ovl_fh *fh, struct vfsmount *mnt,
return real;
}
static bool ovl_is_opaquedir(struct dentry *dentry)
static bool ovl_is_opaquedir(struct super_block *sb, struct dentry *dentry)
{
return ovl_check_dir_xattr(dentry, OVL_XATTR_OPAQUE);
return ovl_check_dir_xattr(sb, dentry, OVL_XATTR_OPAQUE);
}
static struct dentry *ovl_lookup_positive_unlocked(const char *name,
@ -251,7 +253,7 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
d->stop = true;
goto put_and_out;
}
err = ovl_check_metacopy_xattr(this);
err = ovl_check_metacopy_xattr(OVL_FS(d->sb), this);
if (err < 0)
goto out_err;
@ -271,7 +273,7 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
if (d->last)
goto out;
if (ovl_is_opaquedir(this)) {
if (ovl_is_opaquedir(d->sb, this)) {
d->stop = true;
if (last_element)
d->opaque = true;
@ -391,7 +393,7 @@ invalid:
static int ovl_check_origin(struct ovl_fs *ofs, struct dentry *upperdentry,
struct ovl_path **stackp)
{
struct ovl_fh *fh = ovl_get_fh(upperdentry, OVL_XATTR_ORIGIN);
struct ovl_fh *fh = ovl_get_fh(ofs, upperdentry, OVL_XATTR_ORIGIN);
int err;
if (IS_ERR_OR_NULL(fh))
@ -413,10 +415,10 @@ static int ovl_check_origin(struct ovl_fs *ofs, struct dentry *upperdentry,
* Verify that @fh matches the file handle stored in xattr @name.
* Return 0 on match, -ESTALE on mismatch, < 0 on error.
*/
static int ovl_verify_fh(struct dentry *dentry, const char *name,
const struct ovl_fh *fh)
static int ovl_verify_fh(struct ovl_fs *ofs, struct dentry *dentry,
enum ovl_xattr ox, const struct ovl_fh *fh)
{
struct ovl_fh *ofh = ovl_get_fh(dentry, name);
struct ovl_fh *ofh = ovl_get_fh(ofs, dentry, ox);
int err = 0;
if (!ofh)
@ -440,8 +442,9 @@ static int ovl_verify_fh(struct dentry *dentry, const char *name,
*
* Return 0 on match, -ESTALE on mismatch, -ENODATA on no xattr, < 0 on error.
*/
int ovl_verify_set_fh(struct dentry *dentry, const char *name,
struct dentry *real, bool is_upper, bool set)
int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry,
enum ovl_xattr ox, struct dentry *real, bool is_upper,
bool set)
{
struct inode *inode;
struct ovl_fh *fh;
@ -454,9 +457,9 @@ int ovl_verify_set_fh(struct dentry *dentry, const char *name,
goto fail;
}
err = ovl_verify_fh(dentry, name, fh);
err = ovl_verify_fh(ofs, dentry, ox, fh);
if (set && err == -ENODATA)
err = ovl_do_setxattr(dentry, name, fh->buf, fh->fb.len, 0);
err = ovl_do_setxattr(ofs, dentry, ox, fh->buf, fh->fb.len);
if (err)
goto fail;
@ -481,7 +484,7 @@ struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index)
if (!d_is_dir(index))
return dget(index);
fh = ovl_get_fh(index, OVL_XATTR_UPPER);
fh = ovl_get_fh(ofs, index, OVL_XATTR_UPPER);
if (IS_ERR_OR_NULL(fh))
return ERR_CAST(fh);
@ -574,7 +577,7 @@ int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index)
goto fail;
}
err = ovl_verify_fh(upper, OVL_XATTR_ORIGIN, fh);
err = ovl_verify_fh(ofs, upper, OVL_XATTR_ORIGIN, fh);
dput(upper);
if (err)
goto fail;
@ -585,7 +588,7 @@ int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index)
if (err)
goto fail;
if (ovl_get_nlink(origin.dentry, index, 0) == 0)
if (ovl_get_nlink(ofs, origin.dentry, index, 0) == 0)
goto orphan;
}
@ -741,7 +744,7 @@ struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
}
/* Verify that dir index 'upper' xattr points to upper dir */
err = ovl_verify_upper(index, upper, false);
err = ovl_verify_upper(ofs, index, upper, false);
if (err) {
if (err == -ESTALE) {
pr_warn_ratelimited("suspected multiply redirected dir found (upper=%pd2, origin=%pd2, index=%pd2).\n",
@ -790,12 +793,12 @@ int ovl_path_next(int idx, struct dentry *dentry, struct path *path)
}
/* Fix missing 'origin' xattr */
static int ovl_fix_origin(struct dentry *dentry, struct dentry *lower,
struct dentry *upper)
static int ovl_fix_origin(struct ovl_fs *ofs, struct dentry *dentry,
struct dentry *lower, struct dentry *upper)
{
int err;
if (ovl_check_origin_xattr(upper))
if (ovl_check_origin_xattr(ofs, upper))
return 0;
err = ovl_want_write(dentry);
@ -920,7 +923,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
* of lower dir and set upper parent "impure".
*/
if (upperdentry && !ctr && !ofs->noxattr && d.is_dir) {
err = ovl_fix_origin(dentry, this, upperdentry);
err = ovl_fix_origin(ofs, dentry, this, upperdentry);
if (err) {
dput(this);
goto out_put;
@ -939,7 +942,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
if (upperdentry && !ctr &&
((d.is_dir && ovl_verify_lower(dentry->d_sb)) ||
(!d.is_dir && ofs->config.index && origin_path))) {
err = ovl_verify_origin(upperdentry, this, false);
err = ovl_verify_origin(ofs, upperdentry, this, false);
if (err) {
dput(this);
if (d.is_dir)
@ -1060,13 +1063,13 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
ovl_dentry_set_upper_alias(dentry);
else if (index) {
upperdentry = dget(index);
upperredirect = ovl_get_redirect_xattr(upperdentry, 0);
upperredirect = ovl_get_redirect_xattr(ofs, upperdentry, 0);
if (IS_ERR(upperredirect)) {
err = PTR_ERR(upperredirect);
upperredirect = NULL;
goto out_free_oe;
}
err = ovl_check_metacopy_xattr(upperdentry);
err = ovl_check_metacopy_xattr(ofs, upperdentry);
if (err < 0)
goto out_free_oe;
uppermetacopy = err;

View File

@ -23,13 +23,16 @@ enum ovl_path_type {
#define OVL_TYPE_ORIGIN(type) ((type) & __OVL_PATH_ORIGIN)
#define OVL_XATTR_PREFIX XATTR_TRUSTED_PREFIX "overlay."
#define OVL_XATTR_OPAQUE OVL_XATTR_PREFIX "opaque"
#define OVL_XATTR_REDIRECT OVL_XATTR_PREFIX "redirect"
#define OVL_XATTR_ORIGIN OVL_XATTR_PREFIX "origin"
#define OVL_XATTR_IMPURE OVL_XATTR_PREFIX "impure"
#define OVL_XATTR_NLINK OVL_XATTR_PREFIX "nlink"
#define OVL_XATTR_UPPER OVL_XATTR_PREFIX "upper"
#define OVL_XATTR_METACOPY OVL_XATTR_PREFIX "metacopy"
enum ovl_xattr {
OVL_XATTR_OPAQUE,
OVL_XATTR_REDIRECT,
OVL_XATTR_ORIGIN,
OVL_XATTR_IMPURE,
OVL_XATTR_NLINK,
OVL_XATTR_UPPER,
OVL_XATTR_METACOPY,
};
enum ovl_inode_flag {
/* Pure upper dir that may contain non pure upper entries */
@ -110,6 +113,12 @@ struct ovl_fh {
#define OVL_FH_FID_OFFSET (OVL_FH_WIRE_OFFSET + \
offsetof(struct ovl_fb, fid))
extern const char *ovl_xattr_table[];
static inline const char *ovl_xattr(struct ovl_fs *ofs, enum ovl_xattr ox)
{
return ovl_xattr_table[ox];
}
static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry)
{
int err = vfs_rmdir(dir, dentry);
@ -170,17 +179,29 @@ static inline int ovl_do_symlink(struct inode *dir, struct dentry *dentry,
return err;
}
static inline int ovl_do_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
static inline ssize_t ovl_do_getxattr(struct ovl_fs *ofs, struct dentry *dentry,
enum ovl_xattr ox, void *value,
size_t size)
{
int err = vfs_setxattr(dentry, name, value, size, flags);
pr_debug("setxattr(%pd2, \"%s\", \"%*pE\", %zu, 0x%x) = %i\n",
dentry, name, min((int)size, 48), value, size, flags, err);
const char *name = ovl_xattr(ofs, ox);
return vfs_getxattr(dentry, name, value, size);
}
static inline int ovl_do_setxattr(struct ovl_fs *ofs, struct dentry *dentry,
enum ovl_xattr ox, const void *value,
size_t size)
{
const char *name = ovl_xattr(ofs, ox);
int err = vfs_setxattr(dentry, name, value, size, 0);
pr_debug("setxattr(%pd2, \"%s\", \"%*pE\", %zu, 0) = %i\n",
dentry, name, min((int)size, 48), value, size, err);
return err;
}
static inline int ovl_do_removexattr(struct dentry *dentry, const char *name)
static inline int ovl_do_removexattr(struct ovl_fs *ofs, struct dentry *dentry,
enum ovl_xattr ox)
{
const char *name = ovl_xattr(ofs, ox);
int err = vfs_removexattr(dentry, name);
pr_debug("removexattr(%pd2, \"%s\") = %i\n", dentry, name, err);
return err;
@ -280,10 +301,11 @@ struct file *ovl_path_open(struct path *path, int flags);
int ovl_copy_up_start(struct dentry *dentry, int flags);
void ovl_copy_up_end(struct dentry *dentry);
bool ovl_already_copied_up(struct dentry *dentry, int flags);
bool ovl_check_origin_xattr(struct dentry *dentry);
bool ovl_check_dir_xattr(struct dentry *dentry, const char *name);
bool ovl_check_origin_xattr(struct ovl_fs *ofs, struct dentry *dentry);
bool ovl_check_dir_xattr(struct super_block *sb, struct dentry *dentry,
enum ovl_xattr ox);
int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
const char *name, const void *value, size_t size,
enum ovl_xattr ox, const void *value, size_t size,
int xerr);
int ovl_set_impure(struct dentry *dentry, struct dentry *upperdentry);
void ovl_set_flag(unsigned long flag, struct inode *inode);
@ -296,15 +318,15 @@ bool ovl_need_index(struct dentry *dentry);
int ovl_nlink_start(struct dentry *dentry);
void ovl_nlink_end(struct dentry *dentry);
int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir);
int ovl_check_metacopy_xattr(struct dentry *dentry);
int ovl_check_metacopy_xattr(struct ovl_fs *ofs, struct dentry *dentry);
bool ovl_is_metacopy_dentry(struct dentry *dentry);
char *ovl_get_redirect_xattr(struct dentry *dentry, int padding);
ssize_t ovl_getxattr(struct dentry *dentry, char *name, char **value,
size_t padding);
char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct dentry *dentry,
int padding);
static inline bool ovl_is_impuredir(struct dentry *dentry)
static inline bool ovl_is_impuredir(struct super_block *sb,
struct dentry *dentry)
{
return ovl_check_dir_xattr(dentry, OVL_XATTR_IMPURE);
return ovl_check_dir_xattr(sb, dentry, OVL_XATTR_IMPURE);
}
/*
@ -365,8 +387,9 @@ struct dentry *ovl_decode_real_fh(struct ovl_fh *fh, struct vfsmount *mnt,
bool connected);
int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
struct dentry *upperdentry, struct ovl_path **stackp);
int ovl_verify_set_fh(struct dentry *dentry, const char *name,
struct dentry *real, bool is_upper, bool set);
int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry,
enum ovl_xattr ox, struct dentry *real, bool is_upper,
bool set);
struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index);
int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index);
int ovl_get_index_name(struct dentry *origin, struct qstr *name);
@ -378,20 +401,22 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags);
bool ovl_lower_positive(struct dentry *dentry);
static inline int ovl_verify_origin(struct dentry *upper,
static inline int ovl_verify_origin(struct ovl_fs *ofs, struct dentry *upper,
struct dentry *origin, bool set)
{
return ovl_verify_set_fh(upper, OVL_XATTR_ORIGIN, origin, false, set);
return ovl_verify_set_fh(ofs, upper, OVL_XATTR_ORIGIN, origin,
false, set);
}
static inline int ovl_verify_upper(struct dentry *index,
struct dentry *upper, bool set)
static inline int ovl_verify_upper(struct ovl_fs *ofs, struct dentry *index,
struct dentry *upper, bool set)
{
return ovl_verify_set_fh(index, OVL_XATTR_UPPER, upper, true, set);
return ovl_verify_set_fh(ofs, index, OVL_XATTR_UPPER, upper, true, set);
}
/* readdir.c */
extern const struct file_operations ovl_dir_operations;
struct file *ovl_dir_real_file(const struct file *file, bool want_upper);
int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list);
void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list);
void ovl_cache_free(struct list_head *list);
@ -404,7 +429,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs);
/* inode.c */
int ovl_set_nlink_upper(struct dentry *dentry);
int ovl_set_nlink_lower(struct dentry *dentry);
unsigned int ovl_get_nlink(struct dentry *lowerdentry,
unsigned int ovl_get_nlink(struct ovl_fs *ofs, struct dentry *lowerdentry,
struct dentry *upperdentry,
unsigned int fallback);
int ovl_setattr(struct dentry *dentry, struct iattr *attr);
@ -418,7 +443,7 @@ int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
struct posix_acl *ovl_get_acl(struct inode *inode, int type);
int ovl_update_time(struct inode *inode, struct timespec64 *ts, int flags);
bool ovl_is_private_xattr(const char *name);
bool ovl_is_private_xattr(struct super_block *sb, const char *name);
struct ovl_inode_params {
struct inode *newinode;
@ -479,12 +504,15 @@ struct dentry *ovl_create_temp(struct dentry *workdir, struct ovl_cattr *attr);
extern const struct file_operations ovl_file_operations;
int __init ovl_aio_request_cache_init(void);
void ovl_aio_request_cache_destroy(void);
long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
long ovl_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
/* copy_up.c */
int ovl_copy_up(struct dentry *dentry);
int ovl_copy_up_with_data(struct dentry *dentry);
int ovl_maybe_copy_up(struct dentry *dentry, int flags);
int ovl_copy_xattr(struct dentry *old, struct dentry *new);
int ovl_copy_xattr(struct super_block *sb, struct dentry *old,
struct dentry *new);
int ovl_set_attr(struct dentry *upper, struct kstat *stat);
struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper);
int ovl_set_origin(struct dentry *dentry, struct dentry *lower,

View File

@ -17,6 +17,7 @@ struct ovl_config {
bool nfs_export;
int xino;
bool metacopy;
bool ovl_volatile;
};
struct ovl_sb {
@ -90,6 +91,11 @@ static inline struct ovl_fs *OVL_FS(struct super_block *sb)
return (struct ovl_fs *)sb->s_fs_info;
}
static inline bool ovl_should_sync(struct ovl_fs *ofs)
{
return !ofs->config.ovl_volatile;
}
/* private information held for every overlayfs dentry */
struct ovl_entry {
union {

View File

@ -606,6 +606,7 @@ static struct ovl_dir_cache *ovl_cache_get_impure(struct path *path)
{
int res;
struct dentry *dentry = path->dentry;
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
struct ovl_dir_cache *cache;
cache = ovl_dir_cache(d_inode(dentry));
@ -632,7 +633,7 @@ static struct ovl_dir_cache *ovl_cache_get_impure(struct path *path)
* Removing the "impure" xattr is best effort.
*/
if (!ovl_want_write(dentry)) {
ovl_do_removexattr(ovl_dentry_upper(dentry),
ovl_do_removexattr(ofs, ovl_dentry_upper(dentry),
OVL_XATTR_IMPURE);
ovl_drop_write(dentry);
}
@ -839,7 +840,7 @@ out_unlock:
return res;
}
static struct file *ovl_dir_open_realfile(struct file *file,
static struct file *ovl_dir_open_realfile(const struct file *file,
struct path *realpath)
{
struct file *res;
@ -852,16 +853,22 @@ static struct file *ovl_dir_open_realfile(struct file *file,
return res;
}
static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
int datasync)
/*
* Like ovl_real_fdget(), returns upperfile if dir was copied up since open.
* Unlike ovl_real_fdget(), this caches upperfile in file->private_data.
*
* TODO: use same abstract type for file->private_data of dir and file so
* upperfile could also be cached for files as well.
*/
struct file *ovl_dir_real_file(const struct file *file, bool want_upper)
{
struct ovl_dir_file *od = file->private_data;
struct dentry *dentry = file->f_path.dentry;
struct file *realfile = od->realfile;
/* Nothing to sync for lower */
if (!OVL_TYPE_UPPER(ovl_path_type(dentry)))
return 0;
return want_upper ? NULL : realfile;
/*
* Need to check if we started out being a lower dir, but got copied up
@ -880,7 +887,7 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
if (!od->upperfile) {
if (IS_ERR(realfile)) {
inode_unlock(inode);
return PTR_ERR(realfile);
return realfile;
}
smp_store_release(&od->upperfile, realfile);
} else {
@ -893,6 +900,25 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
}
}
return realfile;
}
static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
int datasync)
{
struct file *realfile;
int err;
if (!ovl_should_sync(OVL_FS(file->f_path.dentry->d_sb)))
return 0;
realfile = ovl_dir_real_file(file, true);
err = PTR_ERR_OR_ZERO(realfile);
/* Nothing to sync for lower */
if (!realfile || err)
return err;
return vfs_fsync_range(realfile, start, end, datasync);
}
@ -945,6 +971,10 @@ const struct file_operations ovl_dir_operations = {
.llseek = ovl_dir_llseek,
.fsync = ovl_dir_fsync,
.release = ovl_dir_release,
.unlocked_ioctl = ovl_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ovl_compat_ioctl,
#endif
};
int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
@ -1051,7 +1081,9 @@ int ovl_check_d_type_supported(struct path *realpath)
return rdd.d_type_supported;
}
static void ovl_workdir_cleanup_recurse(struct path *path, int level)
#define OVL_INCOMPATDIR_NAME "incompat"
static int ovl_workdir_cleanup_recurse(struct path *path, int level)
{
int err;
struct inode *dir = path->dentry->d_inode;
@ -1065,6 +1097,19 @@ static void ovl_workdir_cleanup_recurse(struct path *path, int level)
.root = &root,
.is_lowest = false,
};
bool incompat = false;
/*
* The "work/incompat" directory is treated specially - if it is not
* empty, instead of printing a generic error and mounting read-only,
* we will error about incompat features and fail the mount.
*
* When called from ovl_indexdir_cleanup(), path->dentry->d_name.name
* starts with '#'.
*/
if (level == 2 &&
!strcmp(path->dentry->d_name.name, OVL_INCOMPATDIR_NAME))
incompat = true;
err = ovl_dir_read(path, &rdd);
if (err)
@ -1079,17 +1124,25 @@ static void ovl_workdir_cleanup_recurse(struct path *path, int level)
continue;
if (p->len == 2 && p->name[1] == '.')
continue;
} else if (incompat) {
pr_err("overlay with incompat feature '%s' cannot be mounted\n",
p->name);
err = -EINVAL;
break;
}
dentry = lookup_one_len(p->name, path->dentry, p->len);
if (IS_ERR(dentry))
continue;
if (dentry->d_inode)
ovl_workdir_cleanup(dir, path->mnt, dentry, level);
err = ovl_workdir_cleanup(dir, path->mnt, dentry, level);
dput(dentry);
if (err)
break;
}
inode_unlock(dir);
out:
ovl_cache_free(&list);
return err;
}
int ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
@ -1106,9 +1159,10 @@ int ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
struct path path = { .mnt = mnt, .dentry = dentry };
inode_unlock(dir);
ovl_workdir_cleanup_recurse(&path, level + 1);
err = ovl_workdir_cleanup_recurse(&path, level + 1);
inode_lock_nested(dir, I_MUTEX_PARENT);
err = ovl_cleanup(dir, dentry);
if (!err)
err = ovl_cleanup(dir, dentry);
}
return err;

View File

@ -264,6 +264,8 @@ static int ovl_sync_fs(struct super_block *sb, int wait)
if (!ovl_upper_mnt(ofs))
return 0;
if (!ovl_should_sync(ofs))
return 0;
/*
* Not called for sync(2) call or an emergency sync (SB_I_SKIP_SYNC).
* All the super blocks will be iterated, including upper_sb.
@ -362,6 +364,8 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
if (ofs->config.metacopy != ovl_metacopy_def)
seq_printf(m, ",metacopy=%s",
ofs->config.metacopy ? "on" : "off");
if (ofs->config.ovl_volatile)
seq_puts(m, ",volatile");
return 0;
}
@ -376,9 +380,11 @@ static int ovl_remount(struct super_block *sb, int *flags, char *data)
if (*flags & SB_RDONLY && !sb_rdonly(sb)) {
upper_sb = ovl_upper_mnt(ofs)->mnt_sb;
down_read(&upper_sb->s_umount);
ret = sync_filesystem(upper_sb);
up_read(&upper_sb->s_umount);
if (ovl_should_sync(ofs)) {
down_read(&upper_sb->s_umount);
ret = sync_filesystem(upper_sb);
up_read(&upper_sb->s_umount);
}
}
return ret;
@ -411,6 +417,7 @@ enum {
OPT_XINO_AUTO,
OPT_METACOPY_ON,
OPT_METACOPY_OFF,
OPT_VOLATILE,
OPT_ERR,
};
@ -429,6 +436,7 @@ static const match_table_t ovl_tokens = {
{OPT_XINO_AUTO, "xino=auto"},
{OPT_METACOPY_ON, "metacopy=on"},
{OPT_METACOPY_OFF, "metacopy=off"},
{OPT_VOLATILE, "volatile"},
{OPT_ERR, NULL}
};
@ -573,6 +581,10 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
metacopy_opt = true;
break;
case OPT_VOLATILE:
config->ovl_volatile = true;
break;
default:
pr_err("unrecognized mount option \"%s\" or missing value\n",
p);
@ -595,6 +607,11 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
config->index = false;
}
if (!config->upperdir && config->ovl_volatile) {
pr_info("option \"volatile\" is meaningless in a non-upper mount, ignoring it.\n");
config->ovl_volatile = false;
}
err = ovl_parse_redirect_mode(config, config->redirect_mode);
if (err)
return err;
@ -705,8 +722,12 @@ retry:
goto out_unlock;
retried = true;
ovl_workdir_cleanup(dir, mnt, work, 0);
err = ovl_workdir_cleanup(dir, mnt, work, 0);
dput(work);
if (err == -EINVAL) {
work = ERR_PTR(err);
goto out_unlock;
}
goto retry;
}
@ -1199,11 +1220,50 @@ out_unlock:
return err;
}
static struct dentry *ovl_lookup_or_create(struct dentry *parent,
const char *name, umode_t mode)
{
size_t len = strlen(name);
struct dentry *child;
inode_lock_nested(parent->d_inode, I_MUTEX_PARENT);
child = lookup_one_len(name, parent, len);
if (!IS_ERR(child) && !child->d_inode)
child = ovl_create_real(parent->d_inode, child,
OVL_CATTR(mode));
inode_unlock(parent->d_inode);
dput(parent);
return child;
}
/*
* Creates $workdir/work/incompat/volatile/dirty file if it is not already
* present.
*/
static int ovl_create_volatile_dirty(struct ovl_fs *ofs)
{
unsigned int ctr;
struct dentry *d = dget(ofs->workbasedir);
static const char *const volatile_path[] = {
OVL_WORKDIR_NAME, "incompat", "volatile", "dirty"
};
const char *const *name = volatile_path;
for (ctr = ARRAY_SIZE(volatile_path); ctr; ctr--, name++) {
d = ovl_lookup_or_create(d, *name, ctr > 1 ? S_IFDIR : S_IFREG);
if (IS_ERR(d))
return PTR_ERR(d);
}
dput(d);
return 0;
}
static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
struct path *workpath)
{
struct vfsmount *mnt = ovl_upper_mnt(ofs);
struct dentry *temp;
struct dentry *temp, *workdir;
bool rename_whiteout;
bool d_type;
int fh_type;
@ -1213,10 +1273,13 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
if (err)
return err;
ofs->workdir = ovl_workdir_create(ofs, OVL_WORKDIR_NAME, false);
if (!ofs->workdir)
workdir = ovl_workdir_create(ofs, OVL_WORKDIR_NAME, false);
err = PTR_ERR(workdir);
if (IS_ERR_OR_NULL(workdir))
goto out;
ofs->workdir = workdir;
err = ovl_setup_trap(sb, ofs->workdir, &ofs->workdir_trap, "workdir");
if (err)
goto out;
@ -1256,7 +1319,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
/*
* Check if upper/work fs supports trusted.overlay.* xattr
*/
err = ovl_do_setxattr(ofs->workdir, OVL_XATTR_OPAQUE, "0", 1, 0);
err = ovl_do_setxattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE, "0", 1);
if (err) {
ofs->noxattr = true;
ofs->config.index = false;
@ -1264,7 +1327,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
pr_warn("upper fs does not support xattr, falling back to index=off and metacopy=off.\n");
err = 0;
} else {
vfs_removexattr(ofs->workdir, OVL_XATTR_OPAQUE);
ovl_do_removexattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE);
}
/*
@ -1279,6 +1342,18 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
goto out;
}
/*
* For volatile mount, create a incompat/volatile/dirty file to keep
* track of it.
*/
if (ofs->config.ovl_volatile) {
err = ovl_create_volatile_dirty(ofs);
if (err < 0) {
pr_err("Failed to create volatile/dirty file.\n");
goto out;
}
}
/* Check if upper/work fs supports file handles */
fh_type = ovl_can_decode_fh(ofs->workdir->d_sb);
if (ofs->config.index && !fh_type) {
@ -1347,6 +1422,7 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
struct ovl_entry *oe, struct path *upperpath)
{
struct vfsmount *mnt = ovl_upper_mnt(ofs);
struct dentry *indexdir;
int err;
err = mnt_want_write(mnt);
@ -1354,8 +1430,8 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
return err;
/* Verify lower root is upper root origin */
err = ovl_verify_origin(upperpath->dentry, oe->lowerstack[0].dentry,
true);
err = ovl_verify_origin(ofs, upperpath->dentry,
oe->lowerstack[0].dentry, true);
if (err) {
pr_err("failed to verify upper root origin\n");
goto out;
@ -1366,9 +1442,12 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
ofs->workdir_trap = NULL;
dput(ofs->workdir);
ofs->workdir = NULL;
ofs->indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true);
if (ofs->indexdir) {
ofs->workdir = dget(ofs->indexdir);
indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true);
if (IS_ERR(indexdir)) {
err = PTR_ERR(indexdir);
} else if (indexdir) {
ofs->indexdir = indexdir;
ofs->workdir = dget(indexdir);
err = ovl_setup_trap(sb, ofs->indexdir, &ofs->indexdir_trap,
"indexdir");
@ -1383,13 +1462,15 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
* "trusted.overlay.upper" to indicate that index may have
* directory entries.
*/
if (ovl_check_origin_xattr(ofs->indexdir)) {
err = ovl_verify_set_fh(ofs->indexdir, OVL_XATTR_ORIGIN,
if (ovl_check_origin_xattr(ofs, ofs->indexdir)) {
err = ovl_verify_set_fh(ofs, ofs->indexdir,
OVL_XATTR_ORIGIN,
upperpath->dentry, true, false);
if (err)
pr_err("failed to verify index dir 'origin' xattr\n");
}
err = ovl_verify_upper(ofs->indexdir, upperpath->dentry, true);
err = ovl_verify_upper(ofs, ofs->indexdir, upperpath->dentry,
true);
if (err)
pr_err("failed to verify index dir 'upper' xattr\n");
@ -1755,7 +1836,7 @@ static struct dentry *ovl_get_root(struct super_block *sb,
ino = d_inode(upperdentry)->i_ino;
fsid = 0;
ovl_dentry_set_upper_alias(root);
if (ovl_is_impuredir(upperdentry))
if (ovl_is_impuredir(sb, upperdentry))
ovl_set_flag(OVL_IMPURE, d_inode(root));
}

View File

@ -544,11 +544,11 @@ void ovl_copy_up_end(struct dentry *dentry)
ovl_inode_unlock(d_inode(dentry));
}
bool ovl_check_origin_xattr(struct dentry *dentry)
bool ovl_check_origin_xattr(struct ovl_fs *ofs, struct dentry *dentry)
{
int res;
res = vfs_getxattr(dentry, OVL_XATTR_ORIGIN, NULL, 0);
res = ovl_do_getxattr(ofs, dentry, OVL_XATTR_ORIGIN, NULL, 0);
/* Zero size value means "copied up but origin unknown" */
if (res >= 0)
@ -557,7 +557,8 @@ bool ovl_check_origin_xattr(struct dentry *dentry)
return false;
}
bool ovl_check_dir_xattr(struct dentry *dentry, const char *name)
bool ovl_check_dir_xattr(struct super_block *sb, struct dentry *dentry,
enum ovl_xattr ox)
{
int res;
char val;
@ -565,15 +566,36 @@ bool ovl_check_dir_xattr(struct dentry *dentry, const char *name)
if (!d_is_dir(dentry))
return false;
res = vfs_getxattr(dentry, name, &val, 1);
res = ovl_do_getxattr(OVL_FS(sb), dentry, ox, &val, 1);
if (res == 1 && val == 'y')
return true;
return false;
}
#define OVL_XATTR_OPAQUE_POSTFIX "opaque"
#define OVL_XATTR_REDIRECT_POSTFIX "redirect"
#define OVL_XATTR_ORIGIN_POSTFIX "origin"
#define OVL_XATTR_IMPURE_POSTFIX "impure"
#define OVL_XATTR_NLINK_POSTFIX "nlink"
#define OVL_XATTR_UPPER_POSTFIX "upper"
#define OVL_XATTR_METACOPY_POSTFIX "metacopy"
#define OVL_XATTR_TAB_ENTRY(x) \
[x] = OVL_XATTR_PREFIX x ## _POSTFIX
const char *ovl_xattr_table[] = {
OVL_XATTR_TAB_ENTRY(OVL_XATTR_OPAQUE),
OVL_XATTR_TAB_ENTRY(OVL_XATTR_REDIRECT),
OVL_XATTR_TAB_ENTRY(OVL_XATTR_ORIGIN),
OVL_XATTR_TAB_ENTRY(OVL_XATTR_IMPURE),
OVL_XATTR_TAB_ENTRY(OVL_XATTR_NLINK),
OVL_XATTR_TAB_ENTRY(OVL_XATTR_UPPER),
OVL_XATTR_TAB_ENTRY(OVL_XATTR_METACOPY),
};
int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
const char *name, const void *value, size_t size,
enum ovl_xattr ox, const void *value, size_t size,
int xerr)
{
int err;
@ -582,10 +604,10 @@ int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
if (ofs->noxattr)
return xerr;
err = ovl_do_setxattr(upperdentry, name, value, size, 0);
err = ovl_do_setxattr(ofs, upperdentry, ox, value, size);
if (err == -EOPNOTSUPP) {
pr_warn("cannot set %s xattr on upper\n", name);
pr_warn("cannot set %s xattr on upper\n", ovl_xattr(ofs, ox));
ofs->noxattr = true;
return xerr;
}
@ -845,7 +867,7 @@ err:
}
/* err < 0, 0 if no metacopy xattr, 1 if metacopy xattr found */
int ovl_check_metacopy_xattr(struct dentry *dentry)
int ovl_check_metacopy_xattr(struct ovl_fs *ofs, struct dentry *dentry)
{
int res;
@ -853,7 +875,7 @@ int ovl_check_metacopy_xattr(struct dentry *dentry)
if (!S_ISREG(d_inode(dentry)->i_mode))
return 0;
res = vfs_getxattr(dentry, OVL_XATTR_METACOPY, NULL, 0);
res = ovl_do_getxattr(ofs, dentry, OVL_XATTR_METACOPY, NULL, 0);
if (res < 0) {
if (res == -ENODATA || res == -EOPNOTSUPP)
return 0;
@ -882,49 +904,27 @@ bool ovl_is_metacopy_dentry(struct dentry *dentry)
return (oe->numlower > 1);
}
ssize_t ovl_getxattr(struct dentry *dentry, char *name, char **value,
size_t padding)
{
ssize_t res;
char *buf = NULL;
res = vfs_getxattr(dentry, name, NULL, 0);
if (res < 0) {
if (res == -ENODATA || res == -EOPNOTSUPP)
return -ENODATA;
goto fail;
}
if (res != 0) {
buf = kzalloc(res + padding, GFP_KERNEL);
if (!buf)
return -ENOMEM;
res = vfs_getxattr(dentry, name, buf, res);
if (res < 0)
goto fail;
}
*value = buf;
return res;
fail:
pr_warn_ratelimited("failed to get xattr %s: err=%zi)\n",
name, res);
kfree(buf);
return res;
}
char *ovl_get_redirect_xattr(struct dentry *dentry, int padding)
char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct dentry *dentry,
int padding)
{
int res;
char *s, *next, *buf = NULL;
res = ovl_getxattr(dentry, OVL_XATTR_REDIRECT, &buf, padding + 1);
if (res == -ENODATA)
res = ovl_do_getxattr(ofs, dentry, OVL_XATTR_REDIRECT, NULL, 0);
if (res == -ENODATA || res == -EOPNOTSUPP)
return NULL;
if (res < 0)
return ERR_PTR(res);
goto fail;
if (res == 0)
goto invalid;
buf = kzalloc(res + padding + 1, GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
res = ovl_do_getxattr(ofs, dentry, OVL_XATTR_REDIRECT, buf, res);
if (res < 0)
goto fail;
if (res == 0)
goto invalid;
@ -943,6 +943,10 @@ char *ovl_get_redirect_xattr(struct dentry *dentry, int padding)
invalid:
pr_warn_ratelimited("invalid redirect (%s)\n", buf);
res = -EINVAL;
goto err_free;
fail:
pr_warn_ratelimited("failed to get redirect (%i)\n", res);
err_free:
kfree(buf);
return ERR_PTR(res);
}