Commit 44fea373 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'ovl-fixes-5.8-rc6' of...

Merge tag 'ovl-fixes-5.8-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs into master

Pull overlayfs fixes from Miklos Szeredi:

 - fix a regression introduced in v4.20 in handling a regenerated
   squashfs lower layer

 - two regression fixes for this cycle, one of which is Oops inducing

 - miscellaneous issues

* tag 'ovl-fixes-5.8-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: fix lookup of indexed hardlinks with metacopy
  ovl: fix unneeded call to ovl_change_flags()
  ovl: fix mount option checks for nfs_export with no upperdir
  ovl: force read-only sb on failure to create index dir
  ovl: fix regression with re-formatted lower squashfs
  ovl: fix oops in ovl_indexdir_cleanup() with nfs_export=on
  ovl: relax WARN_ON() when decoding lower directory file handle
  ovl: remove not used argument in ovl_check_origin
  ovl: change ovl_copy_up_flags static
  ovl: inode reference leak in ovl_is_inuse true case.
parents 33b9108f 4518dfcf
...@@ -560,8 +560,8 @@ When the NFS export feature is enabled, all directory index entries are ...@@ -560,8 +560,8 @@ When the NFS export feature is enabled, all directory index entries are
verified on mount time to check that upper file handles are not stale. verified on mount time to check that upper file handles are not stale.
This verification may cause significant overhead in some cases. This verification may cause significant overhead in some cases.
Note: the mount options index=off,nfs_export=on are conflicting and will Note: the mount options index=off,nfs_export=on are conflicting for a
result in an error. read-write mount and will result in an error.
Testsuite Testsuite
......
...@@ -895,7 +895,7 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, ...@@ -895,7 +895,7 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
return err; return err;
} }
int ovl_copy_up_flags(struct dentry *dentry, int flags) static int ovl_copy_up_flags(struct dentry *dentry, int flags)
{ {
int err = 0; int err = 0;
const struct cred *old_cred = ovl_override_creds(dentry->d_sb); const struct cred *old_cred = ovl_override_creds(dentry->d_sb);
......
...@@ -476,7 +476,7 @@ static struct dentry *ovl_lookup_real_inode(struct super_block *sb, ...@@ -476,7 +476,7 @@ static struct dentry *ovl_lookup_real_inode(struct super_block *sb,
if (IS_ERR_OR_NULL(this)) if (IS_ERR_OR_NULL(this))
return this; return this;
if (WARN_ON(ovl_dentry_real_at(this, layer->idx) != real)) { if (ovl_dentry_real_at(this, layer->idx) != real) {
dput(this); dput(this);
this = ERR_PTR(-EIO); this = ERR_PTR(-EIO);
} }
......
...@@ -33,13 +33,16 @@ static char ovl_whatisit(struct inode *inode, struct inode *realinode) ...@@ -33,13 +33,16 @@ static char ovl_whatisit(struct inode *inode, struct inode *realinode)
return 'm'; return 'm';
} }
/* No atime modificaton nor notify on underlying */
#define OVL_OPEN_FLAGS (O_NOATIME | FMODE_NONOTIFY)
static struct file *ovl_open_realfile(const struct file *file, static struct file *ovl_open_realfile(const struct file *file,
struct inode *realinode) struct inode *realinode)
{ {
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
struct file *realfile; struct file *realfile;
const struct cred *old_cred; const struct cred *old_cred;
int flags = file->f_flags | O_NOATIME | FMODE_NONOTIFY; int flags = file->f_flags | OVL_OPEN_FLAGS;
int acc_mode = ACC_MODE(flags); int acc_mode = ACC_MODE(flags);
int err; int err;
...@@ -72,8 +75,7 @@ static int ovl_change_flags(struct file *file, unsigned int flags) ...@@ -72,8 +75,7 @@ static int ovl_change_flags(struct file *file, unsigned int flags)
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
int err; int err;
/* No atime modificaton on underlying */ flags |= OVL_OPEN_FLAGS;
flags |= O_NOATIME | FMODE_NONOTIFY;
/* If some flag changed that cannot be changed then something's amiss */ /* If some flag changed that cannot be changed then something's amiss */
if (WARN_ON((file->f_flags ^ flags) & ~OVL_SETFL_MASK)) if (WARN_ON((file->f_flags ^ flags) & ~OVL_SETFL_MASK))
...@@ -126,7 +128,7 @@ static int ovl_real_fdget_meta(const struct file *file, struct fd *real, ...@@ -126,7 +128,7 @@ static int ovl_real_fdget_meta(const struct file *file, struct fd *real,
} }
/* Did the flags change since open? */ /* Did the flags change since open? */
if (unlikely((file->f_flags ^ real->file->f_flags) & ~O_NOATIME)) if (unlikely((file->f_flags ^ real->file->f_flags) & ~OVL_OPEN_FLAGS))
return ovl_change_flags(real->file, file->f_flags); return ovl_change_flags(real->file, file->f_flags);
return 0; return 0;
......
...@@ -389,7 +389,7 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected, ...@@ -389,7 +389,7 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
} }
static int ovl_check_origin(struct ovl_fs *ofs, struct dentry *upperdentry, static int ovl_check_origin(struct ovl_fs *ofs, struct dentry *upperdentry,
struct ovl_path **stackp, unsigned int *ctrp) struct ovl_path **stackp)
{ {
struct ovl_fh *fh = ovl_get_fh(upperdentry, OVL_XATTR_ORIGIN); struct ovl_fh *fh = ovl_get_fh(upperdentry, OVL_XATTR_ORIGIN);
int err; int err;
...@@ -406,10 +406,6 @@ static int ovl_check_origin(struct ovl_fs *ofs, struct dentry *upperdentry, ...@@ -406,10 +406,6 @@ static int ovl_check_origin(struct ovl_fs *ofs, struct dentry *upperdentry,
return err; return err;
} }
if (WARN_ON(*ctrp))
return -EIO;
*ctrp = 1;
return 0; return 0;
} }
...@@ -861,8 +857,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -861,8 +857,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
goto out; goto out;
} }
if (upperdentry && !d.is_dir) { if (upperdentry && !d.is_dir) {
unsigned int origin_ctr = 0;
/* /*
* Lookup copy up origin by decoding origin file handle. * Lookup copy up origin by decoding origin file handle.
* We may get a disconnected dentry, which is fine, * We may get a disconnected dentry, which is fine,
...@@ -873,8 +867,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -873,8 +867,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
* number - it's the same as if we held a reference * number - it's the same as if we held a reference
* to a dentry in lower layer that was moved under us. * to a dentry in lower layer that was moved under us.
*/ */
err = ovl_check_origin(ofs, upperdentry, &origin_path, err = ovl_check_origin(ofs, upperdentry, &origin_path);
&origin_ctr);
if (err) if (err)
goto out_put_upper; goto out_put_upper;
...@@ -1073,6 +1066,10 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -1073,6 +1066,10 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
upperredirect = NULL; upperredirect = NULL;
goto out_free_oe; goto out_free_oe;
} }
err = ovl_check_metacopy_xattr(upperdentry);
if (err < 0)
goto out_free_oe;
uppermetacopy = err;
} }
if (upperdentry || ctr) { if (upperdentry || ctr) {
......
...@@ -483,7 +483,6 @@ void ovl_aio_request_cache_destroy(void); ...@@ -483,7 +483,6 @@ void ovl_aio_request_cache_destroy(void);
/* copy_up.c */ /* copy_up.c */
int ovl_copy_up(struct dentry *dentry); int ovl_copy_up(struct dentry *dentry);
int ovl_copy_up_with_data(struct dentry *dentry); int ovl_copy_up_with_data(struct dentry *dentry);
int ovl_copy_up_flags(struct dentry *dentry, int flags);
int ovl_maybe_copy_up(struct dentry *dentry, int flags); 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 dentry *old, struct dentry *new);
int ovl_set_attr(struct dentry *upper, struct kstat *stat); int ovl_set_attr(struct dentry *upper, struct kstat *stat);
......
...@@ -580,13 +580,20 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) ...@@ -580,13 +580,20 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
} }
} }
/* Workdir is useless in non-upper mount */ /* Workdir/index are useless in non-upper mount */
if (!config->upperdir && config->workdir) { if (!config->upperdir) {
if (config->workdir) {
pr_info("option \"workdir=%s\" is useless in a non-upper mount, ignore\n", pr_info("option \"workdir=%s\" is useless in a non-upper mount, ignore\n",
config->workdir); config->workdir);
kfree(config->workdir); kfree(config->workdir);
config->workdir = NULL; config->workdir = NULL;
} }
if (config->index && index_opt) {
pr_info("option \"index=on\" is useless in a non-upper mount, ignore\n");
index_opt = false;
}
config->index = false;
}
err = ovl_parse_redirect_mode(config, config->redirect_mode); err = ovl_parse_redirect_mode(config, config->redirect_mode);
if (err) if (err)
...@@ -622,11 +629,13 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) ...@@ -622,11 +629,13 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
/* Resolve nfs_export -> index dependency */ /* Resolve nfs_export -> index dependency */
if (config->nfs_export && !config->index) { if (config->nfs_export && !config->index) {
if (nfs_export_opt && index_opt) { if (!config->upperdir && config->redirect_follow) {
pr_info("NFS export requires \"redirect_dir=nofollow\" on non-upper mount, falling back to nfs_export=off.\n");
config->nfs_export = false;
} else if (nfs_export_opt && index_opt) {
pr_err("conflicting options: nfs_export=on,index=off\n"); pr_err("conflicting options: nfs_export=on,index=off\n");
return -EINVAL; return -EINVAL;
} } else if (index_opt) {
if (index_opt) {
/* /*
* There was an explicit index=off that resulted * There was an explicit index=off that resulted
* in this conflict. * in this conflict.
...@@ -1352,8 +1361,15 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1352,8 +1361,15 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
goto out; goto out;
} }
/* index dir will act also as workdir */
iput(ofs->workdir_trap);
ofs->workdir_trap = NULL;
dput(ofs->workdir);
ofs->workdir = NULL;
ofs->indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true); ofs->indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true);
if (ofs->indexdir) { if (ofs->indexdir) {
ofs->workdir = dget(ofs->indexdir);
err = ovl_setup_trap(sb, ofs->indexdir, &ofs->indexdir_trap, err = ovl_setup_trap(sb, ofs->indexdir, &ofs->indexdir_trap,
"indexdir"); "indexdir");
if (err) if (err)
...@@ -1396,6 +1412,18 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid) ...@@ -1396,6 +1412,18 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid)
if (!ofs->config.nfs_export && !ovl_upper_mnt(ofs)) if (!ofs->config.nfs_export && !ovl_upper_mnt(ofs))
return true; return true;
/*
* We allow using single lower with null uuid for index and nfs_export
* for example to support those features with single lower squashfs.
* To avoid regressions in setups of overlay with re-formatted lower
* squashfs, do not allow decoding origin with lower null uuid unless
* user opted-in to one of the new features that require following the
* lower inode of non-dir upper.
*/
if (!ofs->config.index && !ofs->config.metacopy && !ofs->config.xino &&
uuid_is_null(uuid))
return false;
for (i = 0; i < ofs->numfs; i++) { for (i = 0; i < ofs->numfs; i++) {
/* /*
* We use uuid to associate an overlay lower file handle with a * We use uuid to associate an overlay lower file handle with a
...@@ -1493,15 +1521,24 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1493,15 +1521,24 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
if (err < 0) if (err < 0)
goto out; goto out;
/*
* Check if lower root conflicts with this overlay layers before
* checking if it is in-use as upperdir/workdir of "another"
* mount, because we do not bother to check in ovl_is_inuse() if
* the upperdir/workdir is in fact in-use by our
* upperdir/workdir.
*/
err = ovl_setup_trap(sb, stack[i].dentry, &trap, "lowerdir"); err = ovl_setup_trap(sb, stack[i].dentry, &trap, "lowerdir");
if (err) if (err)
goto out; goto out;
if (ovl_is_inuse(stack[i].dentry)) { if (ovl_is_inuse(stack[i].dentry)) {
err = ovl_report_in_use(ofs, "lowerdir"); err = ovl_report_in_use(ofs, "lowerdir");
if (err) if (err) {
iput(trap);
goto out; goto out;
} }
}
mnt = clone_private_mount(&stack[i]); mnt = clone_private_mount(&stack[i]);
err = PTR_ERR(mnt); err = PTR_ERR(mnt);
...@@ -1575,10 +1612,6 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, ...@@ -1575,10 +1612,6 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb,
if (!ofs->config.upperdir && numlower == 1) { if (!ofs->config.upperdir && numlower == 1) {
pr_err("at least 2 lowerdir are needed while upperdir nonexistent\n"); pr_err("at least 2 lowerdir are needed while upperdir nonexistent\n");
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} else if (!ofs->config.upperdir && ofs->config.nfs_export &&
ofs->config.redirect_follow) {
pr_warn("NFS export requires \"redirect_dir=nofollow\" on non-upper mount, falling back to nfs_export=off.\n");
ofs->config.nfs_export = false;
} }
stack = kcalloc(numlower, sizeof(struct path), GFP_KERNEL); stack = kcalloc(numlower, sizeof(struct path), GFP_KERNEL);
...@@ -1842,21 +1875,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1842,21 +1875,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
if (!ovl_upper_mnt(ofs)) if (!ovl_upper_mnt(ofs))
sb->s_flags |= SB_RDONLY; sb->s_flags |= SB_RDONLY;
if (!(ovl_force_readonly(ofs)) && ofs->config.index) { if (!ovl_force_readonly(ofs) && ofs->config.index) {
/* index dir will act also as workdir */
dput(ofs->workdir);
ofs->workdir = NULL;
iput(ofs->workdir_trap);
ofs->workdir_trap = NULL;
err = ovl_get_indexdir(sb, ofs, oe, &upperpath); err = ovl_get_indexdir(sb, ofs, oe, &upperpath);
if (err) if (err)
goto out_free_oe; goto out_free_oe;
/* Force r/o mount with no index dir */ /* Force r/o mount with no index dir */
if (ofs->indexdir) if (!ofs->indexdir)
ofs->workdir = dget(ofs->indexdir);
else
sb->s_flags |= SB_RDONLY; sb->s_flags |= SB_RDONLY;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment