Skip to content

Commit

Permalink
Merge tag 'ovl-update-4.20' of git://git.kernel.org/pub/scm/linux/ker…
Browse files Browse the repository at this point in the history
…nel/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()
  • Loading branch information
torvalds committed Nov 1, 2018
2 parents 34c7685 + d47748e commit 7260935
Show file tree
Hide file tree
Showing 9 changed files with 260 additions and 145 deletions.
6 changes: 6 additions & 0 deletions Documentation/filesystems/overlayfs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
--------------------------

Expand Down
3 changes: 3 additions & 0 deletions fs/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
213 changes: 122 additions & 91 deletions fs/overlayfs/copy_up.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

/*
Expand Down Expand Up @@ -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;

Expand Down
Loading

0 comments on commit 7260935

Please sign in to comment.