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); + return err; - if (new_creds) - old_creds = override_creds(new_creds); + if (cc->new) + cc->old = override_creds(cc->new); - temp = ovl_create_temp(c->workdir, &cattr); - - if (new_creds) { - revert_creds(old_creds); - put_cred(new_creds); - } - - 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; -} - -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; - - 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); - - temp = ovl_do_tmpfile(c->workdir, c->stat.mode); - - if (new_creds) { - revert_creds(old_creds); - put_cred(new_creds); - } - - return 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); - - 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: - inode_unlock(udir); - - return err; + 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 dentry *newdentry = NULL; - struct dentry *temp; + struct inode *udir = d_inode(c->destdir); + struct dentry *temp, *upper; + struct ovl_cu_creds cc; int err; - temp = ovl_get_tmpfile(c); + 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; + + 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); - 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; }