|
@@ -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;
|
|
|
|