mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-30 14:19:16 +00:00
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
7 changed files with 65 additions and 42 deletions
|
@ -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,
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
* 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,
|
||||||
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);
|
||||||
/* 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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)
|
||||||
|
|
||||||
/* 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,
|
||||||
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)
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue