overlayfs update for 4.20
-----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCW9tlWwAKCRDh3BK/laaZ PEszAQDnwCMuh4WwhmS4d4X9/rLwzxBqFcHbBUMxvUSpD2LSvQD/fbV3EVkcTGUc DVfV2e9Zdy2vq36fcR3EMa1oUGNzpgc= =oscW -----END PGP SIGNATURE----- Merge tag 'ovl-update-4.20' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs Pull overlayfs updates from Miklos Szeredi: "A mix of fixes and cleanups" * tag 'ovl-update-4.20' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: ovl: automatically enable redirect_dir on metacopy=on ovl: check whiteout in ovl_create_over_whiteout() ovl: using posix_acl_xattr_size() to get size instead of posix_acl_to_xattr() ovl: abstract ovl_inode lock with a helper ovl: remove the 'locked' argument of ovl_nlink_{start,end} ovl: relax requirement for non null uuid of lower fs ovl: fold copy-up helpers into callers ovl: untangle copy up call chain ovl: relax permission checking on underlying layers ovl: fix recursive oi->lock in ovl_link() vfs: fix FIGETBSZ ioctl on an overlayfs file ovl: clean up error handling in ovl_get_tmpfile() ovl: fix error handling in ovl_verify_set_fh()
This commit is contained in:
commit
7260935d71
|
@ -286,6 +286,12 @@ pointed by REDIRECT. This should not be possible on local system as setting
|
|||
"trusted." xattrs will require CAP_SYS_ADMIN. But it should be possible
|
||||
for untrusted layers like from a pen drive.
|
||||
|
||||
Note: redirect_dir={off|nofollow|follow(*)} conflicts with metacopy=on, and
|
||||
results in an error.
|
||||
|
||||
(*) redirect_dir=follow only conflicts with metacopy=on if upperdir=... is
|
||||
given.
|
||||
|
||||
Sharing and copying layers
|
||||
--------------------------
|
||||
|
||||
|
|
|
@ -669,6 +669,9 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
|
|||
return ioctl_fiemap(filp, arg);
|
||||
|
||||
case FIGETBSZ:
|
||||
/* anon_bdev filesystems may not have a block size */
|
||||
if (!inode->i_sb->s_blocksize)
|
||||
return -EINVAL;
|
||||
return put_user(inode->i_sb->s_blocksize, argp);
|
||||
|
||||
case FICLONE:
|
||||
|
|
|
@ -395,7 +395,6 @@ struct ovl_copy_up_ctx {
|
|||
struct dentry *destdir;
|
||||
struct qstr destname;
|
||||
struct dentry *workdir;
|
||||
bool tmpfile;
|
||||
bool origin;
|
||||
bool indexed;
|
||||
bool metacopy;
|
||||
|
@ -440,63 +439,6 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int ovl_install_temp(struct ovl_copy_up_ctx *c, struct dentry *temp,
|
||||
struct dentry **newdentry)
|
||||
{
|
||||
int err;
|
||||
struct dentry *upper;
|
||||
struct inode *udir = d_inode(c->destdir);
|
||||
|
||||
upper = lookup_one_len(c->destname.name, c->destdir, c->destname.len);
|
||||
if (IS_ERR(upper))
|
||||
return PTR_ERR(upper);
|
||||
|
||||
if (c->tmpfile)
|
||||
err = ovl_do_link(temp, udir, upper);
|
||||
else
|
||||
err = ovl_do_rename(d_inode(c->workdir), temp, udir, upper, 0);
|
||||
|
||||
if (!err)
|
||||
*newdentry = dget(c->tmpfile ? upper : temp);
|
||||
dput(upper);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct dentry *ovl_get_tmpfile(struct ovl_copy_up_ctx *c)
|
||||
{
|
||||
int err;
|
||||
struct dentry *temp;
|
||||
const struct cred *old_creds = NULL;
|
||||
struct cred *new_creds = NULL;
|
||||
struct ovl_cattr cattr = {
|
||||
/* Can't properly set mode on creation because of the umask */
|
||||
.mode = c->stat.mode & S_IFMT,
|
||||
.rdev = c->stat.rdev,
|
||||
.link = c->link
|
||||
};
|
||||
|
||||
err = security_inode_copy_up(c->dentry, &new_creds);
|
||||
temp = ERR_PTR(err);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
if (new_creds)
|
||||
old_creds = override_creds(new_creds);
|
||||
|
||||
if (c->tmpfile)
|
||||
temp = ovl_do_tmpfile(c->workdir, c->stat.mode);
|
||||
else
|
||||
temp = ovl_create_temp(c->workdir, &cattr);
|
||||
out:
|
||||
if (new_creds) {
|
||||
revert_creds(old_creds);
|
||||
put_cred(new_creds);
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
|
||||
{
|
||||
int err;
|
||||
|
@ -548,51 +490,148 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)
|
||||
struct ovl_cu_creds {
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
};
|
||||
|
||||
static int ovl_prep_cu_creds(struct dentry *dentry, struct ovl_cu_creds *cc)
|
||||
{
|
||||
struct inode *udir = c->destdir->d_inode;
|
||||
struct inode *inode;
|
||||
struct dentry *newdentry = NULL;
|
||||
struct dentry *temp;
|
||||
int err;
|
||||
|
||||
temp = ovl_get_tmpfile(c);
|
||||
cc->old = cc->new = NULL;
|
||||
err = security_inode_copy_up(dentry, &cc->new);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (cc->new)
|
||||
cc->old = override_creds(cc->new);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ovl_revert_cu_creds(struct ovl_cu_creds *cc)
|
||||
{
|
||||
if (cc->new) {
|
||||
revert_creds(cc->old);
|
||||
put_cred(cc->new);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copyup using workdir to prepare temp file. Used when copying up directories,
|
||||
* special files or when upper fs doesn't support O_TMPFILE.
|
||||
*/
|
||||
static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct inode *udir = d_inode(c->destdir), *wdir = d_inode(c->workdir);
|
||||
struct dentry *temp, *upper;
|
||||
struct ovl_cu_creds cc;
|
||||
int err;
|
||||
struct ovl_cattr cattr = {
|
||||
/* Can't properly set mode on creation because of the umask */
|
||||
.mode = c->stat.mode & S_IFMT,
|
||||
.rdev = c->stat.rdev,
|
||||
.link = c->link
|
||||
};
|
||||
|
||||
err = ovl_lock_rename_workdir(c->workdir, c->destdir);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ovl_prep_cu_creds(c->dentry, &cc);
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
temp = ovl_create_temp(c->workdir, &cattr);
|
||||
ovl_revert_cu_creds(&cc);
|
||||
|
||||
err = PTR_ERR(temp);
|
||||
if (IS_ERR(temp))
|
||||
goto unlock;
|
||||
|
||||
err = ovl_copy_up_inode(c, temp);
|
||||
if (err)
|
||||
goto cleanup;
|
||||
|
||||
if (S_ISDIR(c->stat.mode) && c->indexed) {
|
||||
err = ovl_create_index(c->dentry, c->lowerpath.dentry, temp);
|
||||
if (err)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
upper = lookup_one_len(c->destname.name, c->destdir, c->destname.len);
|
||||
err = PTR_ERR(upper);
|
||||
if (IS_ERR(upper))
|
||||
goto cleanup;
|
||||
|
||||
err = ovl_do_rename(wdir, temp, udir, upper, 0);
|
||||
dput(upper);
|
||||
if (err)
|
||||
goto cleanup;
|
||||
|
||||
if (!c->metacopy)
|
||||
ovl_set_upperdata(d_inode(c->dentry));
|
||||
inode = d_inode(c->dentry);
|
||||
ovl_inode_update(inode, temp);
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
ovl_set_flag(OVL_WHITEOUTS, inode);
|
||||
unlock:
|
||||
unlock_rename(c->workdir, c->destdir);
|
||||
|
||||
return err;
|
||||
|
||||
cleanup:
|
||||
ovl_cleanup(wdir, temp);
|
||||
dput(temp);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Copyup using O_TMPFILE which does not require cross dir locking */
|
||||
static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
|
||||
{
|
||||
struct inode *udir = d_inode(c->destdir);
|
||||
struct dentry *temp, *upper;
|
||||
struct ovl_cu_creds cc;
|
||||
int err;
|
||||
|
||||
err = ovl_prep_cu_creds(c->dentry, &cc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
temp = ovl_do_tmpfile(c->workdir, c->stat.mode);
|
||||
ovl_revert_cu_creds(&cc);
|
||||
|
||||
if (IS_ERR(temp))
|
||||
return PTR_ERR(temp);
|
||||
|
||||
err = ovl_copy_up_inode(c, temp);
|
||||
if (err)
|
||||
goto out;
|
||||
goto out_dput;
|
||||
|
||||
if (S_ISDIR(c->stat.mode) && c->indexed) {
|
||||
err = ovl_create_index(c->dentry, c->lowerpath.dentry, temp);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
inode_lock_nested(udir, I_MUTEX_PARENT);
|
||||
|
||||
if (c->tmpfile) {
|
||||
inode_lock_nested(udir, I_MUTEX_PARENT);
|
||||
err = ovl_install_temp(c, temp, &newdentry);
|
||||
inode_unlock(udir);
|
||||
} else {
|
||||
err = ovl_install_temp(c, temp, &newdentry);
|
||||
upper = lookup_one_len(c->destname.name, c->destdir, c->destname.len);
|
||||
err = PTR_ERR(upper);
|
||||
if (!IS_ERR(upper)) {
|
||||
err = ovl_do_link(temp, udir, upper);
|
||||
dput(upper);
|
||||
}
|
||||
inode_unlock(udir);
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
goto out_dput;
|
||||
|
||||
if (!c->metacopy)
|
||||
ovl_set_upperdata(d_inode(c->dentry));
|
||||
inode = d_inode(c->dentry);
|
||||
ovl_inode_update(inode, newdentry);
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
ovl_set_flag(OVL_WHITEOUTS, inode);
|
||||
ovl_inode_update(d_inode(c->dentry), temp);
|
||||
|
||||
out:
|
||||
if (err && !c->tmpfile)
|
||||
ovl_cleanup(d_inode(c->workdir), temp);
|
||||
return 0;
|
||||
|
||||
out_dput:
|
||||
dput(temp);
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -646,18 +685,10 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
|
|||
}
|
||||
|
||||
/* Should we copyup with O_TMPFILE or with workdir? */
|
||||
if (S_ISREG(c->stat.mode) && ofs->tmpfile) {
|
||||
c->tmpfile = true;
|
||||
err = ovl_copy_up_locked(c);
|
||||
} else {
|
||||
err = ovl_lock_rename_workdir(c->workdir, c->destdir);
|
||||
if (!err) {
|
||||
err = ovl_copy_up_locked(c);
|
||||
unlock_rename(c->workdir, c->destdir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (S_ISREG(c->stat.mode) && ofs->tmpfile)
|
||||
err = ovl_copy_up_tmpfile(c);
|
||||
else
|
||||
err = ovl_copy_up_workdir(c);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
|
|
|
@ -414,13 +414,12 @@ static int ovl_set_upper_acl(struct dentry *upperdentry, const char *name,
|
|||
if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !acl)
|
||||
return 0;
|
||||
|
||||
size = posix_acl_to_xattr(NULL, acl, NULL, 0);
|
||||
size = posix_acl_xattr_size(acl->a_count);
|
||||
buffer = kmalloc(size, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
size = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
|
||||
err = size;
|
||||
err = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
|
||||
|
@ -463,6 +462,10 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
|
|||
if (IS_ERR(upper))
|
||||
goto out_unlock;
|
||||
|
||||
err = -ESTALE;
|
||||
if (d_is_negative(upper) || !IS_WHITEOUT(d_inode(upper)))
|
||||
goto out_dput;
|
||||
|
||||
newdentry = ovl_create_temp(workdir, cattr);
|
||||
err = PTR_ERR(newdentry);
|
||||
if (IS_ERR(newdentry))
|
||||
|
@ -652,7 +655,6 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
|
|||
struct dentry *new)
|
||||
{
|
||||
int err;
|
||||
bool locked = false;
|
||||
struct inode *inode;
|
||||
|
||||
err = ovl_want_write(old);
|
||||
|
@ -663,13 +665,17 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
|
|||
if (err)
|
||||
goto out_drop_write;
|
||||
|
||||
err = ovl_copy_up(new->d_parent);
|
||||
if (err)
|
||||
goto out_drop_write;
|
||||
|
||||
if (ovl_is_metacopy_dentry(old)) {
|
||||
err = ovl_set_redirect(old, false);
|
||||
if (err)
|
||||
goto out_drop_write;
|
||||
}
|
||||
|
||||
err = ovl_nlink_start(old, &locked);
|
||||
err = ovl_nlink_start(old);
|
||||
if (err)
|
||||
goto out_drop_write;
|
||||
|
||||
|
@ -682,7 +688,7 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
|
|||
if (err)
|
||||
iput(inode);
|
||||
|
||||
ovl_nlink_end(old, locked);
|
||||
ovl_nlink_end(old);
|
||||
out_drop_write:
|
||||
ovl_drop_write(old);
|
||||
out:
|
||||
|
@ -807,7 +813,6 @@ static bool ovl_pure_upper(struct dentry *dentry)
|
|||
static int ovl_do_remove(struct dentry *dentry, bool is_dir)
|
||||
{
|
||||
int err;
|
||||
bool locked = false;
|
||||
const struct cred *old_cred;
|
||||
struct dentry *upperdentry;
|
||||
bool lower_positive = ovl_lower_positive(dentry);
|
||||
|
@ -828,7 +833,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
|
|||
if (err)
|
||||
goto out_drop_write;
|
||||
|
||||
err = ovl_nlink_start(dentry, &locked);
|
||||
err = ovl_nlink_start(dentry);
|
||||
if (err)
|
||||
goto out_drop_write;
|
||||
|
||||
|
@ -844,7 +849,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
|
|||
else
|
||||
drop_nlink(dentry->d_inode);
|
||||
}
|
||||
ovl_nlink_end(dentry, locked);
|
||||
ovl_nlink_end(dentry);
|
||||
|
||||
/*
|
||||
* Copy ctime
|
||||
|
@ -1008,7 +1013,6 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
|
|||
unsigned int flags)
|
||||
{
|
||||
int err;
|
||||
bool locked = false;
|
||||
struct dentry *old_upperdir;
|
||||
struct dentry *new_upperdir;
|
||||
struct dentry *olddentry;
|
||||
|
@ -1017,6 +1021,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
|
|||
bool old_opaque;
|
||||
bool new_opaque;
|
||||
bool cleanup_whiteout = false;
|
||||
bool update_nlink = false;
|
||||
bool overwrite = !(flags & RENAME_EXCHANGE);
|
||||
bool is_dir = d_is_dir(old);
|
||||
bool new_is_dir = d_is_dir(new);
|
||||
|
@ -1074,10 +1079,12 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
|
|||
err = ovl_copy_up(new);
|
||||
if (err)
|
||||
goto out_drop_write;
|
||||
} else {
|
||||
err = ovl_nlink_start(new, &locked);
|
||||
} else if (d_inode(new)) {
|
||||
err = ovl_nlink_start(new);
|
||||
if (err)
|
||||
goto out_drop_write;
|
||||
|
||||
update_nlink = true;
|
||||
}
|
||||
|
||||
old_cred = ovl_override_creds(old->d_sb);
|
||||
|
@ -1206,7 +1213,8 @@ out_unlock:
|
|||
unlock_rename(new_upperdir, old_upperdir);
|
||||
out_revert_creds:
|
||||
revert_creds(old_cred);
|
||||
ovl_nlink_end(new, locked);
|
||||
if (update_nlink)
|
||||
ovl_nlink_end(new);
|
||||
out_drop_write:
|
||||
ovl_drop_write(old);
|
||||
out:
|
||||
|
|
|
@ -286,13 +286,22 @@ int ovl_permission(struct inode *inode, int mask)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
old_cred = ovl_override_creds(inode->i_sb);
|
||||
if (!upperinode &&
|
||||
!special_file(realinode->i_mode) && mask & MAY_WRITE) {
|
||||
/* No need to do any access on underlying for special files */
|
||||
if (special_file(realinode->i_mode))
|
||||
return 0;
|
||||
|
||||
/* No need to access underlying for execute */
|
||||
mask &= ~MAY_EXEC;
|
||||
if ((mask & (MAY_READ | MAY_WRITE)) == 0)
|
||||
return 0;
|
||||
|
||||
/* Lower files get copied up, so turn write access into read */
|
||||
if (!upperinode && mask & MAY_WRITE) {
|
||||
mask &= ~(MAY_WRITE | MAY_APPEND);
|
||||
/* Make sure mounter can read file for copy up later */
|
||||
mask |= MAY_READ;
|
||||
}
|
||||
|
||||
old_cred = ovl_override_creds(inode->i_sb);
|
||||
err = inode_permission(realinode, mask);
|
||||
revert_creds(old_cred);
|
||||
|
||||
|
|
|
@ -422,8 +422,10 @@ int ovl_verify_set_fh(struct dentry *dentry, const char *name,
|
|||
|
||||
fh = ovl_encode_real_fh(real, is_upper);
|
||||
err = PTR_ERR(fh);
|
||||
if (IS_ERR(fh))
|
||||
if (IS_ERR(fh)) {
|
||||
fh = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = ovl_verify_fh(dentry, name, fh);
|
||||
if (set && err == -ENODATA)
|
||||
|
|
|
@ -271,8 +271,8 @@ bool ovl_test_flag(unsigned long flag, struct inode *inode);
|
|||
bool ovl_inuse_trylock(struct dentry *dentry);
|
||||
void ovl_inuse_unlock(struct dentry *dentry);
|
||||
bool ovl_need_index(struct dentry *dentry);
|
||||
int ovl_nlink_start(struct dentry *dentry, bool *locked);
|
||||
void ovl_nlink_end(struct dentry *dentry, bool locked);
|
||||
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);
|
||||
bool ovl_is_metacopy_dentry(struct dentry *dentry);
|
||||
|
@ -290,6 +290,16 @@ static inline unsigned int ovl_xino_bits(struct super_block *sb)
|
|||
return ofs->xino_bits;
|
||||
}
|
||||
|
||||
static inline int ovl_inode_lock(struct inode *inode)
|
||||
{
|
||||
return mutex_lock_interruptible(&OVL_I(inode)->lock);
|
||||
}
|
||||
|
||||
static inline void ovl_inode_unlock(struct inode *inode)
|
||||
{
|
||||
mutex_unlock(&OVL_I(inode)->lock);
|
||||
}
|
||||
|
||||
|
||||
/* namei.c */
|
||||
int ovl_check_fh_len(struct ovl_fh *fh, int fh_len);
|
||||
|
|
|
@ -472,6 +472,7 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
|
|||
{
|
||||
char *p;
|
||||
int err;
|
||||
bool metacopy_opt = false, redirect_opt = false;
|
||||
|
||||
config->redirect_mode = kstrdup(ovl_redirect_mode_def(), GFP_KERNEL);
|
||||
if (!config->redirect_mode)
|
||||
|
@ -516,6 +517,7 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
|
|||
config->redirect_mode = match_strdup(&args[0]);
|
||||
if (!config->redirect_mode)
|
||||
return -ENOMEM;
|
||||
redirect_opt = true;
|
||||
break;
|
||||
|
||||
case OPT_INDEX_ON:
|
||||
|
@ -548,6 +550,7 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
|
|||
|
||||
case OPT_METACOPY_ON:
|
||||
config->metacopy = true;
|
||||
metacopy_opt = true;
|
||||
break;
|
||||
|
||||
case OPT_METACOPY_OFF:
|
||||
|
@ -572,13 +575,32 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
/* metacopy feature with upper requires redirect_dir=on */
|
||||
if (config->upperdir && config->metacopy && !config->redirect_dir) {
|
||||
pr_warn("overlayfs: metadata only copy up requires \"redirect_dir=on\", falling back to metacopy=off.\n");
|
||||
config->metacopy = false;
|
||||
} else if (config->metacopy && !config->redirect_follow) {
|
||||
pr_warn("overlayfs: metadata only copy up requires \"redirect_dir=follow\" on non-upper mount, falling back to metacopy=off.\n");
|
||||
config->metacopy = false;
|
||||
/*
|
||||
* This is to make the logic below simpler. It doesn't make any other
|
||||
* difference, since config->redirect_dir is only used for upper.
|
||||
*/
|
||||
if (!config->upperdir && config->redirect_follow)
|
||||
config->redirect_dir = true;
|
||||
|
||||
/* Resolve metacopy -> redirect_dir dependency */
|
||||
if (config->metacopy && !config->redirect_dir) {
|
||||
if (metacopy_opt && redirect_opt) {
|
||||
pr_err("overlayfs: conflicting options: metacopy=on,redirect_dir=%s\n",
|
||||
config->redirect_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (redirect_opt) {
|
||||
/*
|
||||
* There was an explicit redirect_dir=... that resulted
|
||||
* in this conflict.
|
||||
*/
|
||||
pr_info("overlayfs: disabling metacopy due to redirect_dir=%s\n",
|
||||
config->redirect_mode);
|
||||
config->metacopy = false;
|
||||
} else {
|
||||
/* Automatically enable redirect otherwise. */
|
||||
config->redirect_follow = config->redirect_dir = true;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1175,10 +1197,30 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
/* Get a unique fsid for the layer */
|
||||
static int ovl_get_fsid(struct ovl_fs *ofs, struct super_block *sb)
|
||||
static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (!ofs->config.nfs_export && !(ofs->config.index && ofs->upper_mnt))
|
||||
return true;
|
||||
|
||||
for (i = 0; i < ofs->numlowerfs; i++) {
|
||||
/*
|
||||
* We use uuid to associate an overlay lower file handle with a
|
||||
* lower layer, so we can accept lower fs with null uuid as long
|
||||
* as all lower layers with null uuid are on the same fs.
|
||||
*/
|
||||
if (uuid_equal(&ofs->lower_fs[i].sb->s_uuid, uuid))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Get a unique fsid for the layer */
|
||||
static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
|
||||
{
|
||||
struct super_block *sb = path->mnt->mnt_sb;
|
||||
unsigned int i;
|
||||
dev_t dev;
|
||||
int err;
|
||||
|
||||
|
@ -1191,6 +1233,14 @@ static int ovl_get_fsid(struct ovl_fs *ofs, struct super_block *sb)
|
|||
return i + 1;
|
||||
}
|
||||
|
||||
if (!ovl_lower_uuid_ok(ofs, &sb->s_uuid)) {
|
||||
ofs->config.index = false;
|
||||
ofs->config.nfs_export = false;
|
||||
pr_warn("overlayfs: %s uuid detected in lower fs '%pd2', falling back to index=off,nfs_export=off.\n",
|
||||
uuid_is_null(&sb->s_uuid) ? "null" : "conflicting",
|
||||
path->dentry);
|
||||
}
|
||||
|
||||
err = get_anon_bdev(&dev);
|
||||
if (err) {
|
||||
pr_err("overlayfs: failed to get anonymous bdev for lowerpath\n");
|
||||
|
@ -1225,7 +1275,7 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack,
|
|||
struct vfsmount *mnt;
|
||||
int fsid;
|
||||
|
||||
err = fsid = ovl_get_fsid(ofs, stack[i].mnt->mnt_sb);
|
||||
err = fsid = ovl_get_fsid(ofs, &stack[i]);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
|
|
|
@ -65,8 +65,7 @@ struct super_block *ovl_same_sb(struct super_block *sb)
|
|||
*/
|
||||
int ovl_can_decode_fh(struct super_block *sb)
|
||||
{
|
||||
if (!sb->s_export_op || !sb->s_export_op->fh_to_dentry ||
|
||||
uuid_is_null(&sb->s_uuid))
|
||||
if (!sb->s_export_op || !sb->s_export_op->fh_to_dentry)
|
||||
return 0;
|
||||
|
||||
return sb->s_export_op->encode_fh ? -1 : FILEID_INO32_GEN;
|
||||
|
@ -522,13 +521,13 @@ bool ovl_already_copied_up(struct dentry *dentry, int flags)
|
|||
|
||||
int ovl_copy_up_start(struct dentry *dentry, int flags)
|
||||
{
|
||||
struct ovl_inode *oi = OVL_I(d_inode(dentry));
|
||||
struct inode *inode = d_inode(dentry);
|
||||
int err;
|
||||
|
||||
err = mutex_lock_interruptible(&oi->lock);
|
||||
err = ovl_inode_lock(inode);
|
||||
if (!err && ovl_already_copied_up_locked(dentry, flags)) {
|
||||
err = 1; /* Already copied up */
|
||||
mutex_unlock(&oi->lock);
|
||||
ovl_inode_unlock(inode);
|
||||
}
|
||||
|
||||
return err;
|
||||
|
@ -536,7 +535,7 @@ int ovl_copy_up_start(struct dentry *dentry, int flags)
|
|||
|
||||
void ovl_copy_up_end(struct dentry *dentry)
|
||||
{
|
||||
mutex_unlock(&OVL_I(d_inode(dentry))->lock);
|
||||
ovl_inode_unlock(d_inode(dentry));
|
||||
}
|
||||
|
||||
bool ovl_check_origin_xattr(struct dentry *dentry)
|
||||
|
@ -739,14 +738,14 @@ fail:
|
|||
* Operations that change overlay inode and upper inode nlink need to be
|
||||
* synchronized with copy up for persistent nlink accounting.
|
||||
*/
|
||||
int ovl_nlink_start(struct dentry *dentry, bool *locked)
|
||||
int ovl_nlink_start(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_inode *oi = OVL_I(d_inode(dentry));
|
||||
struct inode *inode = d_inode(dentry);
|
||||
const struct cred *old_cred;
|
||||
int err;
|
||||
|
||||
if (!d_inode(dentry))
|
||||
return 0;
|
||||
if (WARN_ON(!inode))
|
||||
return -ENOENT;
|
||||
|
||||
/*
|
||||
* With inodes index is enabled, we store the union overlay nlink
|
||||
|
@ -768,11 +767,11 @@ int ovl_nlink_start(struct dentry *dentry, bool *locked)
|
|||
return err;
|
||||
}
|
||||
|
||||
err = mutex_lock_interruptible(&oi->lock);
|
||||
err = ovl_inode_lock(inode);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (d_is_dir(dentry) || !ovl_test_flag(OVL_INDEX, d_inode(dentry)))
|
||||
if (d_is_dir(dentry) || !ovl_test_flag(OVL_INDEX, inode))
|
||||
goto out;
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
|
@ -787,27 +786,24 @@ int ovl_nlink_start(struct dentry *dentry, bool *locked)
|
|||
|
||||
out:
|
||||
if (err)
|
||||
mutex_unlock(&oi->lock);
|
||||
else
|
||||
*locked = true;
|
||||
ovl_inode_unlock(inode);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void ovl_nlink_end(struct dentry *dentry, bool locked)
|
||||
void ovl_nlink_end(struct dentry *dentry)
|
||||
{
|
||||
if (locked) {
|
||||
if (ovl_test_flag(OVL_INDEX, d_inode(dentry)) &&
|
||||
d_inode(dentry)->i_nlink == 0) {
|
||||
const struct cred *old_cred;
|
||||
struct inode *inode = d_inode(dentry);
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
ovl_cleanup_index(dentry);
|
||||
revert_creds(old_cred);
|
||||
}
|
||||
if (ovl_test_flag(OVL_INDEX, inode) && inode->i_nlink == 0) {
|
||||
const struct cred *old_cred;
|
||||
|
||||
mutex_unlock(&OVL_I(d_inode(dentry))->lock);
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
ovl_cleanup_index(dentry);
|
||||
revert_creds(old_cred);
|
||||
}
|
||||
|
||||
ovl_inode_unlock(inode);
|
||||
}
|
||||
|
||||
int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir)
|
||||
|
|
Loading…
Reference in New Issue