Commit 7260935d authored by Linus Torvalds's avatar Linus Torvalds

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()
parents 34c7685a d47748e5
...@@ -286,6 +286,12 @@ pointed by REDIRECT. This should not be possible on local system as setting ...@@ -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 "trusted." xattrs will require CAP_SYS_ADMIN. But it should be possible
for untrusted layers like from a pen drive. 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 Sharing and copying layers
-------------------------- --------------------------
......
...@@ -669,6 +669,9 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, ...@@ -669,6 +669,9 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
return ioctl_fiemap(filp, arg); return ioctl_fiemap(filp, arg);
case FIGETBSZ: 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); return put_user(inode->i_sb->s_blocksize, argp);
case FICLONE: case FICLONE:
......
...@@ -395,7 +395,6 @@ struct ovl_copy_up_ctx { ...@@ -395,7 +395,6 @@ struct ovl_copy_up_ctx {
struct dentry *destdir; struct dentry *destdir;
struct qstr destname; struct qstr destname;
struct dentry *workdir; struct dentry *workdir;
bool tmpfile;
bool origin; bool origin;
bool indexed; bool indexed;
bool metacopy; bool metacopy;
...@@ -440,63 +439,6 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c) ...@@ -440,63 +439,6 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
return err; 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) static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
{ {
int err; int err;
...@@ -548,51 +490,148 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp) ...@@ -548,51 +490,148 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
return err; 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)
{
int err;
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 *udir = c->destdir->d_inode;
struct inode *inode; struct inode *inode;
struct dentry *newdentry = NULL; struct inode *udir = d_inode(c->destdir), *wdir = d_inode(c->workdir);
struct dentry *temp; struct dentry *temp, *upper;
struct ovl_cu_creds cc;
int err; 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;
temp = ovl_get_tmpfile(c); 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)) if (IS_ERR(temp))
return PTR_ERR(temp); goto unlock;
err = ovl_copy_up_inode(c, temp); err = ovl_copy_up_inode(c, temp);
if (err) if (err)
goto out; goto cleanup;
if (S_ISDIR(c->stat.mode) && c->indexed) { if (S_ISDIR(c->stat.mode) && c->indexed) {
err = ovl_create_index(c->dentry, c->lowerpath.dentry, temp); err = ovl_create_index(c->dentry, c->lowerpath.dentry, temp);
if (err) if (err)
goto out; goto cleanup;
} }
if (c->tmpfile) { upper = lookup_one_len(c->destname.name, c->destdir, c->destname.len);
inode_lock_nested(udir, I_MUTEX_PARENT); err = PTR_ERR(upper);
err = ovl_install_temp(c, temp, &newdentry); if (IS_ERR(upper))
inode_unlock(udir); goto cleanup;
} else {
err = ovl_install_temp(c, temp, &newdentry); err = ovl_do_rename(wdir, temp, udir, upper, 0);
} dput(upper);
if (err) if (err)
goto out; goto cleanup;
if (!c->metacopy) if (!c->metacopy)
ovl_set_upperdata(d_inode(c->dentry)); ovl_set_upperdata(d_inode(c->dentry));
inode = d_inode(c->dentry); inode = d_inode(c->dentry);
ovl_inode_update(inode, newdentry); ovl_inode_update(inode, temp);
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
ovl_set_flag(OVL_WHITEOUTS, inode); ovl_set_flag(OVL_WHITEOUTS, inode);
unlock:
unlock_rename(c->workdir, c->destdir);
out: return err;
if (err && !c->tmpfile)
ovl_cleanup(d_inode(c->workdir), temp); cleanup:
ovl_cleanup(wdir, temp);
dput(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; 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_dput;
inode_lock_nested(udir, I_MUTEX_PARENT);
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_dput;
if (!c->metacopy)
ovl_set_upperdata(d_inode(c->dentry));
ovl_inode_update(d_inode(c->dentry), 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) ...@@ -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? */ /* Should we copyup with O_TMPFILE or with workdir? */
if (S_ISREG(c->stat.mode) && ofs->tmpfile) { if (S_ISREG(c->stat.mode) && ofs->tmpfile)
c->tmpfile = true; err = ovl_copy_up_tmpfile(c);
err = ovl_copy_up_locked(c); else
} else { err = ovl_copy_up_workdir(c);
err = ovl_lock_rename_workdir(c->workdir, c->destdir);
if (!err) {
err = ovl_copy_up_locked(c);
unlock_rename(c->workdir, c->destdir);
}
}
if (err) if (err)
goto out; goto out;
......
...@@ -414,13 +414,12 @@ static int ovl_set_upper_acl(struct dentry *upperdentry, const char *name, ...@@ -414,13 +414,12 @@ static int ovl_set_upper_acl(struct dentry *upperdentry, const char *name,
if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !acl) if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !acl)
return 0; return 0;
size = posix_acl_to_xattr(NULL, acl, NULL, 0); size = posix_acl_xattr_size(acl->a_count);
buffer = kmalloc(size, GFP_KERNEL); buffer = kmalloc(size, GFP_KERNEL);
if (!buffer) if (!buffer)
return -ENOMEM; return -ENOMEM;
size = posix_acl_to_xattr(&init_user_ns, acl, buffer, size); err = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
err = size;
if (err < 0) if (err < 0)
goto out_free; goto out_free;
...@@ -463,6 +462,10 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, ...@@ -463,6 +462,10 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
if (IS_ERR(upper)) if (IS_ERR(upper))
goto out_unlock; goto out_unlock;
err = -ESTALE;
if (d_is_negative(upper) || !IS_WHITEOUT(d_inode(upper)))
goto out_dput;
newdentry = ovl_create_temp(workdir, cattr); newdentry = ovl_create_temp(workdir, cattr);
err = PTR_ERR(newdentry); err = PTR_ERR(newdentry);
if (IS_ERR(newdentry)) if (IS_ERR(newdentry))
...@@ -652,7 +655,6 @@ static int ovl_link(struct dentry *old, struct inode *newdir, ...@@ -652,7 +655,6 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
struct dentry *new) struct dentry *new)
{ {
int err; int err;
bool locked = false;
struct inode *inode; struct inode *inode;
err = ovl_want_write(old); err = ovl_want_write(old);
...@@ -663,13 +665,17 @@ static int ovl_link(struct dentry *old, struct inode *newdir, ...@@ -663,13 +665,17 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
if (err) if (err)
goto out_drop_write; goto out_drop_write;
err = ovl_copy_up(new->d_parent);
if (err)
goto out_drop_write;
if (ovl_is_metacopy_dentry(old)) { if (ovl_is_metacopy_dentry(old)) {
err = ovl_set_redirect(old, false); err = ovl_set_redirect(old, false);
if (err) if (err)
goto out_drop_write; goto out_drop_write;
} }
err = ovl_nlink_start(old, &locked); err = ovl_nlink_start(old);
if (err) if (err)
goto out_drop_write; goto out_drop_write;
...@@ -682,7 +688,7 @@ static int ovl_link(struct dentry *old, struct inode *newdir, ...@@ -682,7 +688,7 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
if (err) if (err)
iput(inode); iput(inode);
ovl_nlink_end(old, locked); ovl_nlink_end(old);
out_drop_write: out_drop_write:
ovl_drop_write(old); ovl_drop_write(old);
out: out:
...@@ -807,7 +813,6 @@ static bool ovl_pure_upper(struct dentry *dentry) ...@@ -807,7 +813,6 @@ static bool ovl_pure_upper(struct dentry *dentry)
static int ovl_do_remove(struct dentry *dentry, bool is_dir) static int ovl_do_remove(struct dentry *dentry, bool is_dir)
{ {
int err; int err;
bool locked = false;
const struct cred *old_cred; const struct cred *old_cred;
struct dentry *upperdentry; struct dentry *upperdentry;
bool lower_positive = ovl_lower_positive(dentry); bool lower_positive = ovl_lower_positive(dentry);
...@@ -828,7 +833,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) ...@@ -828,7 +833,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
if (err) if (err)
goto out_drop_write; goto out_drop_write;
err = ovl_nlink_start(dentry, &locked); err = ovl_nlink_start(dentry);
if (err) if (err)
goto out_drop_write; goto out_drop_write;
...@@ -844,7 +849,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) ...@@ -844,7 +849,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
else else
drop_nlink(dentry->d_inode); drop_nlink(dentry->d_inode);
} }
ovl_nlink_end(dentry, locked); ovl_nlink_end(dentry);
/* /*
* Copy ctime * Copy ctime
...@@ -1008,7 +1013,6 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, ...@@ -1008,7 +1013,6 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
unsigned int flags) unsigned int flags)
{ {
int err; int err;
bool locked = false;
struct dentry *old_upperdir; struct dentry *old_upperdir;
struct dentry *new_upperdir; struct dentry *new_upperdir;
struct dentry *olddentry; struct dentry *olddentry;
...@@ -1017,6 +1021,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, ...@@ -1017,6 +1021,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
bool old_opaque; bool old_opaque;
bool new_opaque; bool new_opaque;
bool cleanup_whiteout = false; bool cleanup_whiteout = false;
bool update_nlink = false;
bool overwrite = !(flags & RENAME_EXCHANGE); bool overwrite = !(flags & RENAME_EXCHANGE);
bool is_dir = d_is_dir(old); bool is_dir = d_is_dir(old);
bool new_is_dir = d_is_dir(new); bool new_is_dir = d_is_dir(new);
...@@ -1074,10 +1079,12 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, ...@@ -1074,10 +1079,12 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
err = ovl_copy_up(new); err = ovl_copy_up(new);
if (err) if (err)
goto out_drop_write; goto out_drop_write;
} else { } else if (d_inode(new)) {
err = ovl_nlink_start(new, &locked); err = ovl_nlink_start(new);
if (err) if (err)
goto out_drop_write; goto out_drop_write;
update_nlink = true;
} }
old_cred = ovl_override_creds(old->d_sb); old_cred = ovl_override_creds(old->d_sb);
...@@ -1206,7 +1213,8 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, ...@@ -1206,7 +1213,8 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
unlock_rename(new_upperdir, old_upperdir); unlock_rename(new_upperdir, old_upperdir);
out_revert_creds: out_revert_creds:
revert_creds(old_cred); revert_creds(old_cred);
ovl_nlink_end(new, locked); if (update_nlink)
ovl_nlink_end(new);
out_drop_write: out_drop_write:
ovl_drop_write(old); ovl_drop_write(old);
out: out:
......
...@@ -286,13 +286,22 @@ int ovl_permission(struct inode *inode, int mask) ...@@ -286,13 +286,22 @@ int ovl_permission(struct inode *inode, int mask)
if (err) if (err)
return err; return err;
old_cred = ovl_override_creds(inode->i_sb); /* No need to do any access on underlying for special files */
if (!upperinode && if (special_file(realinode->i_mode))
!special_file(realinode->i_mode) && mask & MAY_WRITE) { 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); mask &= ~(MAY_WRITE | MAY_APPEND);
/* Make sure mounter can read file for copy up later */
mask |= MAY_READ; mask |= MAY_READ;
} }
old_cred = ovl_override_creds(inode->i_sb);
err = inode_permission(realinode, mask); err = inode_permission(realinode, mask);
revert_creds(old_cred); revert_creds(old_cred);
......
...@@ -422,8 +422,10 @@ int ovl_verify_set_fh(struct dentry *dentry, const char *name, ...@@ -422,8 +422,10 @@ int ovl_verify_set_fh(struct dentry *dentry, const char *name,
fh = ovl_encode_real_fh(real, is_upper); fh = ovl_encode_real_fh(real, is_upper);
err = PTR_ERR(fh); err = PTR_ERR(fh);
if (IS_ERR(fh)) if (IS_ERR(fh)) {
fh = NULL;
goto fail; goto fail;
}
err = ovl_verify_fh(dentry, name, fh); err = ovl_verify_fh(dentry, name, fh);
if (set && err == -ENODATA) if (set && err == -ENODATA)
......
...@@ -271,8 +271,8 @@ bool ovl_test_flag(unsigned long flag, struct inode *inode); ...@@ -271,8 +271,8 @@ bool ovl_test_flag(unsigned long flag, struct inode *inode);
bool ovl_inuse_trylock(struct dentry *dentry); bool ovl_inuse_trylock(struct dentry *dentry);
void ovl_inuse_unlock(struct dentry *dentry); void ovl_inuse_unlock(struct dentry *dentry);
bool ovl_need_index(struct dentry *dentry); bool ovl_need_index(struct dentry *dentry);
int ovl_nlink_start(struct dentry *dentry, bool *locked); int ovl_nlink_start(struct dentry *dentry);
void ovl_nlink_end(struct dentry *dentry, bool locked); void ovl_nlink_end(struct dentry *dentry);
int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir); 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 dentry *dentry);
bool ovl_is_metacopy_dentry(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) ...@@ -290,6 +290,16 @@ static inline unsigned int ovl_xino_bits(struct super_block *sb)
return ofs->xino_bits; 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 */ /* namei.c */
int ovl_check_fh_len(struct ovl_fh *fh, int fh_len); 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) ...@@ -472,6 +472,7 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
{ {
char *p; char *p;
int err; int err;
bool metacopy_opt = false, redirect_opt = false;
config->redirect_mode = kstrdup(ovl_redirect_mode_def(), GFP_KERNEL); config->redirect_mode = kstrdup(ovl_redirect_mode_def(), GFP_KERNEL);
if (!config->redirect_mode) if (!config->redirect_mode)
...@@ -516,6 +517,7 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) ...@@ -516,6 +517,7 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
config->redirect_mode = match_strdup(&args[0]); config->redirect_mode = match_strdup(&args[0]);
if (!config->redirect_mode) if (!config->redirect_mode)
return -ENOMEM; return -ENOMEM;
redirect_opt = true;
break; break;
case OPT_INDEX_ON: case OPT_INDEX_ON:
...@@ -548,6 +550,7 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) ...@@ -548,6 +550,7 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
case OPT_METACOPY_ON: case OPT_METACOPY_ON:
config->metacopy = true; config->metacopy = true;
metacopy_opt = true;
break; break;
case OPT_METACOPY_OFF: case OPT_METACOPY_OFF:
...@@ -572,13 +575,32 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) ...@@ -572,13 +575,32 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
if (err) if (err)
return err; return err;
/* metacopy feature with upper requires redirect_dir=on */ /*
if (config->upperdir && config->metacopy && !config->redirect_dir) { * This is to make the logic below simpler. It doesn't make any other
pr_warn("overlayfs: metadata only copy up requires \"redirect_dir=on\", falling back to metacopy=off.\n"); * difference, since config->redirect_dir is only used for upper.
config->metacopy = false; */
} else if (config->metacopy && !config->redirect_follow) { if (!config->upperdir && 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->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; config->metacopy = false;
} else {
/* Automatically enable redirect otherwise. */
config->redirect_follow = config->redirect_dir = true;
}
} }
return 0; return 0;
...@@ -1175,9 +1197,29 @@ static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe, ...@@ -1175,9 +1197,29 @@ static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe,
return err; return err;
} }
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 */ /* Get a unique fsid for the layer */
static int ovl_get_fsid(struct ovl_fs *ofs, struct super_block *sb) static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
{ {
struct super_block *sb = path->mnt->mnt_sb;
unsigned int i; unsigned int i;
dev_t dev; dev_t dev;
int err; int err;
...@@ -1191,6 +1233,14 @@ static int ovl_get_fsid(struct ovl_fs *ofs, struct super_block *sb) ...@@ -1191,6 +1233,14 @@ static int ovl_get_fsid(struct ovl_fs *ofs, struct super_block *sb)
return i + 1; 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); err = get_anon_bdev(&dev);
if (err) { if (err) {
pr_err("overlayfs: failed to get anonymous bdev for lowerpath\n"); 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, ...@@ -1225,7 +1275,7 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack,
struct vfsmount *mnt; struct vfsmount *mnt;
int fsid; int fsid;
err = fsid = ovl_get_fsid(ofs, stack[i].mnt->mnt_sb); err = fsid = ovl_get_fsid(ofs, &stack[i]);
if (err < 0) if (err < 0)
goto out; goto out;
......
...@@ -65,8 +65,7 @@ struct super_block *ovl_same_sb(struct super_block *sb) ...@@ -65,8 +65,7 @@ struct super_block *ovl_same_sb(struct super_block *sb)
*/ */
int ovl_can_decode_fh(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 || if (!sb->s_export_op || !sb->s_export_op->fh_to_dentry)
uuid_is_null(&sb->s_uuid))
return 0; return 0;
return sb->s_export_op->encode_fh ? -1 : FILEID_INO32_GEN; 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) ...@@ -522,13 +521,13 @@ bool ovl_already_copied_up(struct dentry *dentry, int flags)
int ovl_copy_up_start(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; int err;
err = mutex_lock_interruptible(&oi->lock); err = ovl_inode_lock(inode);
if (!err && ovl_already_copied_up_locked(dentry, flags)) { if (!err && ovl_already_copied_up_locked(dentry, flags)) {
err = 1; /* Already copied up */ err = 1; /* Already copied up */
mutex_unlock(&oi->lock); ovl_inode_unlock(inode);
} }
return err; return err;
...@@ -536,7 +535,7 @@ int ovl_copy_up_start(struct dentry *dentry, int flags) ...@@ -536,7 +535,7 @@ int ovl_copy_up_start(struct dentry *dentry, int flags)
void ovl_copy_up_end(struct dentry *dentry) 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) bool ovl_check_origin_xattr(struct dentry *dentry)
...@@ -739,14 +738,14 @@ static void ovl_cleanup_index(struct dentry *dentry) ...@@ -739,14 +738,14 @@ static void ovl_cleanup_index(struct dentry *dentry)
* Operations that change overlay inode and upper inode nlink need to be * Operations that change overlay inode and upper inode nlink need to be
* synchronized with copy up for persistent nlink accounting. * 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; const struct cred *old_cred;
int err; int err;
if (!d_inode(dentry)) if (WARN_ON(!inode))
return 0; return -ENOENT;
/* /*
* With inodes index is enabled, we store the union overlay nlink * With inodes index is enabled, we store the union overlay nlink
...@@ -768,11 +767,11 @@ int ovl_nlink_start(struct dentry *dentry, bool *locked) ...@@ -768,11 +767,11 @@ int ovl_nlink_start(struct dentry *dentry, bool *locked)
return err; return err;
} }
err = mutex_lock_interruptible(&oi->lock); err = ovl_inode_lock(inode);
if (err) if (err)
return 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; goto out;
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
...@@ -787,18 +786,16 @@ int ovl_nlink_start(struct dentry *dentry, bool *locked) ...@@ -787,18 +786,16 @@ int ovl_nlink_start(struct dentry *dentry, bool *locked)
out: out:
if (err) if (err)
mutex_unlock(&oi->lock); ovl_inode_unlock(inode);
else
*locked = true;
return err; return err;
} }
void ovl_nlink_end(struct dentry *dentry, bool locked) void ovl_nlink_end(struct dentry *dentry)
{ {
if (locked) { struct inode *inode = d_inode(dentry);
if (ovl_test_flag(OVL_INDEX, d_inode(dentry)) &&
d_inode(dentry)->i_nlink == 0) { if (ovl_test_flag(OVL_INDEX, inode) && inode->i_nlink == 0) {
const struct cred *old_cred; const struct cred *old_cred;
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
...@@ -806,8 +803,7 @@ void ovl_nlink_end(struct dentry *dentry, bool locked) ...@@ -806,8 +803,7 @@ void ovl_nlink_end(struct dentry *dentry, bool locked)
revert_creds(old_cred); revert_creds(old_cred);
} }
mutex_unlock(&OVL_I(d_inode(dentry))->lock); ovl_inode_unlock(inode);
}
} }
int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir) int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir)
......
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