summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/overlayfs/copy_up.c175
1 files changed, 67 insertions, 108 deletions
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index 618cffc14f91..d6a3346e2672 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -490,59 +490,32 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
return err;
}
-static struct dentry *ovl_get_workdir_temp(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;
- 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);
+ cc->old = cc->new = NULL;
+ err = security_inode_copy_up(dentry, &cc->new);
if (err < 0)
- return ERR_PTR(err);
-
- if (new_creds)
- old_creds = override_creds(new_creds);
-
- temp = ovl_create_temp(c->workdir, &cattr);
+ return err;
- if (new_creds) {
- revert_creds(old_creds);
- put_cred(new_creds);
- }
+ if (cc->new)
+ cc->old = override_creds(cc->new);
- return temp;
+ return 0;
}
-/*
- * Move temp file from workdir into place on upper dir.
- * Used when copying up directories and when upper fs doesn't support O_TMPFILE.
- *
- * Caller must hold ovl_lock_rename_workdir().
- */
-static int ovl_rename_temp(struct ovl_copy_up_ctx *c, struct dentry *temp,
- struct dentry **newdentry)
+static void ovl_revert_cu_creds(struct ovl_cu_creds *cc)
{
- 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);
-
- err = ovl_do_rename(d_inode(c->workdir), temp, udir, upper, 0);
- if (!err)
- *newdentry = dget(temp);
- dput(upper);
-
- return err;
+ if (cc->new) {
+ revert_creds(cc->old);
+ put_cred(cc->new);
+ }
}
/*
@@ -552,15 +525,28 @@ static int ovl_rename_temp(struct ovl_copy_up_ctx *c, struct dentry *temp,
static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
{
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
+ };
err = ovl_lock_rename_workdir(c->workdir, c->destdir);
if (err)
return err;
- temp = ovl_get_workdir_temp(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))
goto unlock;
@@ -575,102 +561,75 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
goto cleanup;
}
- err = ovl_rename_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 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);
-out_dput:
- dput(temp);
unlock:
unlock_rename(c->workdir, c->destdir);
return err;
cleanup:
- ovl_cleanup(d_inode(c->workdir), temp);
- goto out_dput;
+ ovl_cleanup(wdir, temp);
+ dput(temp);
+ goto unlock;
}
-static struct dentry *ovl_get_tmpfile(struct ovl_copy_up_ctx *c)
+/* 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;
- struct dentry *temp;
- const struct cred *old_creds = NULL;
- struct cred *new_creds = NULL;
- err = security_inode_copy_up(c->dentry, &new_creds);
- if (err < 0)
- return ERR_PTR(err);
-
- if (new_creds)
- old_creds = override_creds(new_creds);
+ 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 (new_creds) {
- revert_creds(old_creds);
- put_cred(new_creds);
- }
-
- return temp;
-}
+ if (IS_ERR(temp))
+ return PTR_ERR(temp);
-/* Link O_TMPFILE into place on upper dir */
-static int ovl_link_tmpfile(struct ovl_copy_up_ctx *c, struct dentry *temp,
- struct dentry **newdentry)
-{
- int err;
- struct dentry *upper;
- struct inode *udir = d_inode(c->destdir);
+ 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))
- goto unlock;
-
- err = ovl_do_link(temp, udir, upper);
- if (!err)
- *newdentry = dget(upper);
- dput(upper);
-
-unlock:
+ if (!IS_ERR(upper)) {
+ err = ovl_do_link(temp, udir, upper);
+ dput(upper);
+ }
inode_unlock(udir);
- return err;
-}
-
-/* Copyup using O_TMPFILE which does not require cross dir locking */
-static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
-{
- struct dentry *newdentry = NULL;
- struct dentry *temp;
- int err;
-
- temp = ovl_get_tmpfile(c);
- if (IS_ERR(temp))
- return PTR_ERR(temp);
-
- err = ovl_copy_up_inode(c, temp);
- if (err)
- goto out;
-
- err = ovl_link_tmpfile(c, temp, &newdentry);
if (err)
- goto out;
+ goto out_dput;
if (!c->metacopy)
ovl_set_upperdata(d_inode(c->dentry));
- ovl_inode_update(d_inode(c->dentry), newdentry);
+ ovl_inode_update(d_inode(c->dentry), temp);
-out:
+ return 0;
+
+out_dput:
dput(temp);
return err;
}