overlayfs fixes for 5.8-rc6
-----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCXxGF+QAKCRDh3BK/laaZ PCHnAQCqNxcxncKMebpJ2hNIEPuSvUPRA4+iOOnc+9HTZ4A09wD/d/8ryybORTZN IHq2PpQUtuGgASv6GrptJSmpDvG6RA0= =lOD9 -----END PGP SIGNATURE----- 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.
This commit is contained in:
commit
44fea37378
@ -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.
|
||||
This verification may cause significant overhead in some cases.
|
||||
|
||||
Note: the mount options index=off,nfs_export=on are conflicting and will
|
||||
result in an error.
|
||||
Note: the mount options index=off,nfs_export=on are conflicting for a
|
||||
read-write mount and will result in an error.
|
||||
|
||||
|
||||
Testsuite
|
||||
|
@ -895,7 +895,7 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
|
||||
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;
|
||||
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,
|
||||
if (IS_ERR_OR_NULL(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);
|
||||
this = ERR_PTR(-EIO);
|
||||
}
|
||||
|
@ -33,13 +33,16 @@ static char ovl_whatisit(struct inode *inode, struct inode *realinode)
|
||||
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,
|
||||
struct inode *realinode)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct file *realfile;
|
||||
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 err;
|
||||
|
||||
@ -72,8 +75,7 @@ static int ovl_change_flags(struct file *file, unsigned int flags)
|
||||
struct inode *inode = file_inode(file);
|
||||
int err;
|
||||
|
||||
/* No atime modificaton on underlying */
|
||||
flags |= O_NOATIME | FMODE_NONOTIFY;
|
||||
flags |= OVL_OPEN_FLAGS;
|
||||
|
||||
/* If some flag changed that cannot be changed then something's amiss */
|
||||
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,
|
||||
}
|
||||
|
||||
/* 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 0;
|
||||
|
@ -389,7 +389,7 @@ invalid:
|
||||
}
|
||||
|
||||
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);
|
||||
int err;
|
||||
@ -406,10 +406,6 @@ static int ovl_check_origin(struct ovl_fs *ofs, struct dentry *upperdentry,
|
||||
return err;
|
||||
}
|
||||
|
||||
if (WARN_ON(*ctrp))
|
||||
return -EIO;
|
||||
|
||||
*ctrp = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -861,8 +857,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
goto out;
|
||||
}
|
||||
if (upperdentry && !d.is_dir) {
|
||||
unsigned int origin_ctr = 0;
|
||||
|
||||
/*
|
||||
* Lookup copy up origin by decoding origin file handle.
|
||||
* We may get a disconnected dentry, which is fine,
|
||||
@ -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
|
||||
* to a dentry in lower layer that was moved under us.
|
||||
*/
|
||||
err = ovl_check_origin(ofs, upperdentry, &origin_path,
|
||||
&origin_ctr);
|
||||
err = ovl_check_origin(ofs, upperdentry, &origin_path);
|
||||
if (err)
|
||||
goto out_put_upper;
|
||||
|
||||
@ -1073,6 +1066,10 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
upperredirect = NULL;
|
||||
goto out_free_oe;
|
||||
}
|
||||
err = ovl_check_metacopy_xattr(upperdentry);
|
||||
if (err < 0)
|
||||
goto out_free_oe;
|
||||
uppermetacopy = err;
|
||||
}
|
||||
|
||||
if (upperdentry || ctr) {
|
||||
|
@ -483,7 +483,6 @@ void ovl_aio_request_cache_destroy(void);
|
||||
/* copy_up.c */
|
||||
int ovl_copy_up(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_copy_xattr(struct dentry *old, struct dentry *new);
|
||||
int ovl_set_attr(struct dentry *upper, struct kstat *stat);
|
||||
|
@ -580,12 +580,19 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
|
||||
}
|
||||
}
|
||||
|
||||
/* Workdir is useless in non-upper mount */
|
||||
if (!config->upperdir && config->workdir) {
|
||||
pr_info("option \"workdir=%s\" is useless in a non-upper mount, ignore\n",
|
||||
config->workdir);
|
||||
kfree(config->workdir);
|
||||
config->workdir = NULL;
|
||||
/* Workdir/index are useless in non-upper mount */
|
||||
if (!config->upperdir) {
|
||||
if (config->workdir) {
|
||||
pr_info("option \"workdir=%s\" is useless in a non-upper mount, ignore\n",
|
||||
config->workdir);
|
||||
kfree(config->workdir);
|
||||
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);
|
||||
@ -622,11 +629,13 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
|
||||
|
||||
/* Resolve nfs_export -> index dependency */
|
||||
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");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (index_opt) {
|
||||
} else if (index_opt) {
|
||||
/*
|
||||
* There was an explicit index=off that resulted
|
||||
* in this conflict.
|
||||
@ -1352,8 +1361,15 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
|
||||
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);
|
||||
if (ofs->indexdir) {
|
||||
ofs->workdir = dget(ofs->indexdir);
|
||||
|
||||
err = ovl_setup_trap(sb, ofs->indexdir, &ofs->indexdir_trap,
|
||||
"indexdir");
|
||||
if (err)
|
||||
@ -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))
|
||||
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++) {
|
||||
/*
|
||||
* We use uuid to associate an overlay lower file handle with a
|
||||
@ -1493,14 +1521,23 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
|
||||
if (err < 0)
|
||||
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");
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (ovl_is_inuse(stack[i].dentry)) {
|
||||
err = ovl_report_in_use(ofs, "lowerdir");
|
||||
if (err)
|
||||
if (err) {
|
||||
iput(trap);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
mnt = clone_private_mount(&stack[i]);
|
||||
@ -1575,10 +1612,6 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb,
|
||||
if (!ofs->config.upperdir && numlower == 1) {
|
||||
pr_err("at least 2 lowerdir are needed while upperdir nonexistent\n");
|
||||
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);
|
||||
@ -1842,21 +1875,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
||||
if (!ovl_upper_mnt(ofs))
|
||||
sb->s_flags |= SB_RDONLY;
|
||||
|
||||
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;
|
||||
|
||||
if (!ovl_force_readonly(ofs) && ofs->config.index) {
|
||||
err = ovl_get_indexdir(sb, ofs, oe, &upperpath);
|
||||
if (err)
|
||||
goto out_free_oe;
|
||||
|
||||
/* Force r/o mount with no index dir */
|
||||
if (ofs->indexdir)
|
||||
ofs->workdir = dget(ofs->indexdir);
|
||||
else
|
||||
if (!ofs->indexdir)
|
||||
sb->s_flags |= SB_RDONLY;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user