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
"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)
{
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 dentry *newdentry = NULL;
struct dentry *temp;
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
};
temp = ovl_get_tmpfile(c);
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))
return PTR_ERR(temp);
goto unlock;
err = ovl_copy_up_inode(c, temp);
if (err)
goto out;
goto cleanup;
if (S_ISDIR(c->stat.mode) && c->indexed) {
err = ovl_create_index(c->dentry, c->lowerpath.dentry, temp);
if (err)
goto out;
goto cleanup;
}
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))
goto cleanup;
err = ovl_do_rename(wdir, temp, udir, upper, 0);
dput(upper);
if (err)
goto out;
goto cleanup;
if (!c->metacopy)
ovl_set_upperdata(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))
ovl_set_flag(OVL_WHITEOUTS, inode);
unlock:
unlock_rename(c->workdir, c->destdir);
out:
if (err && !c->tmpfile)
ovl_cleanup(d_inode(c->workdir), temp);
dput(temp);
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_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)
}
/* 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 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
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,9 +1197,29 @@ static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe,
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 */
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;
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 @@ static void ovl_cleanup_index(struct dentry *dentry)
* 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)
......
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