Merge tag 'pull-tmpfile' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[platform/kernel/linux-starfive.git] / fs / overlayfs / copy_up.c
index 3ffea29..f436d88 100644 (file)
@@ -193,11 +193,11 @@ static int ovl_copy_fileattr(struct inode *inode, const struct path *old,
        return ovl_real_fileattr_set(new, &newfa);
 }
 
-static int ovl_copy_up_data(struct ovl_fs *ofs, const struct path *old,
-                           const struct path *new, loff_t len)
+static int ovl_copy_up_file(struct ovl_fs *ofs, struct dentry *dentry,
+                           struct file *new_file, loff_t len)
 {
+       struct path datapath;
        struct file *old_file;
-       struct file *new_file;
        loff_t old_pos = 0;
        loff_t new_pos = 0;
        loff_t cloned;
@@ -206,23 +206,18 @@ static int ovl_copy_up_data(struct ovl_fs *ofs, const struct path *old,
        bool skip_hole = false;
        int error = 0;
 
-       if (len == 0)
-               return 0;
+       ovl_path_lowerdata(dentry, &datapath);
+       if (WARN_ON(datapath.dentry == NULL))
+               return -EIO;
 
-       old_file = ovl_path_open(old, O_LARGEFILE | O_RDONLY);
+       old_file = ovl_path_open(&datapath, O_LARGEFILE | O_RDONLY);
        if (IS_ERR(old_file))
                return PTR_ERR(old_file);
 
-       new_file = ovl_path_open(new, O_LARGEFILE | O_WRONLY);
-       if (IS_ERR(new_file)) {
-               error = PTR_ERR(new_file);
-               goto out_fput;
-       }
-
        /* Try to use clone_file_range to clone up within the same fs */
        cloned = do_clone_file_range(old_file, 0, new_file, 0, len, 0);
        if (cloned == len)
-               goto out;
+               goto out_fput;
        /* Couldn't clone, so now we try to copy the data */
 
        /* Check if lower fs supports seek operation */
@@ -282,10 +277,8 @@ static int ovl_copy_up_data(struct ovl_fs *ofs, const struct path *old,
 
                len -= bytes;
        }
-out:
        if (!error && ovl_should_sync(ofs))
                error = vfs_fsync(new_file, 0);
-       fput(new_file);
 out_fput:
        fput(old_file);
        return error;
@@ -556,30 +549,31 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
        return err;
 }
 
-static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
+static int ovl_copy_up_data(struct ovl_copy_up_ctx *c, const struct path *temp)
 {
        struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
-       struct inode *inode = d_inode(c->dentry);
-       struct path upperpath, datapath;
+       struct file *new_file;
        int err;
 
-       ovl_path_upper(c->dentry, &upperpath);
-       if (WARN_ON(upperpath.dentry != NULL))
-               return -EIO;
+       if (!S_ISREG(c->stat.mode) || c->metacopy || !c->stat.size)
+               return 0;
 
-       upperpath.dentry = temp;
+       new_file = ovl_path_open(temp, O_LARGEFILE | O_WRONLY);
+       if (IS_ERR(new_file))
+               return PTR_ERR(new_file);
 
-       /*
-        * Copy up data first and then xattrs. Writing data after
-        * xattrs will remove security.capability xattr automatically.
-        */
-       if (S_ISREG(c->stat.mode) && !c->metacopy) {
-               ovl_path_lowerdata(c->dentry, &datapath);
-               err = ovl_copy_up_data(ofs, &datapath, &upperpath,
-                                      c->stat.size);
-               if (err)
-                       return err;
-       }
+       err = ovl_copy_up_file(ofs, c->dentry, new_file, c->stat.size);
+       fput(new_file);
+
+       return err;
+}
+
+static int ovl_copy_up_metadata(struct ovl_copy_up_ctx *c, struct dentry *temp)
+{
+       struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
+       struct inode *inode = d_inode(c->dentry);
+       struct path upperpath = { .mnt = ovl_upper_mnt(ofs), .dentry = temp };
+       int err;
 
        err = ovl_copy_xattr(c->dentry->d_sb, &c->lowerpath, temp);
        if (err)
@@ -662,6 +656,7 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
        struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
        struct inode *inode;
        struct inode *udir = d_inode(c->destdir), *wdir = d_inode(c->workdir);
+       struct path path = { .mnt = ovl_upper_mnt(ofs) };
        struct dentry *temp, *upper;
        struct ovl_cu_creds cc;
        int err;
@@ -688,7 +683,16 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
        if (IS_ERR(temp))
                goto unlock;
 
-       err = ovl_copy_up_inode(c, temp);
+       /*
+        * Copy up data first and then xattrs. Writing data after
+        * xattrs will remove security.capability xattr automatically.
+        */
+       path.dentry = temp;
+       err = ovl_copy_up_data(c, &path);
+       if (err)
+               goto cleanup;
+
+       err = ovl_copy_up_metadata(c, temp);
        if (err)
                goto cleanup;
 
@@ -732,6 +736,7 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
        struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
        struct inode *udir = d_inode(c->destdir);
        struct dentry *temp, *upper;
+       struct file *tmpfile;
        struct ovl_cu_creds cc;
        int err;
 
@@ -739,15 +744,22 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
        if (err)
                return err;
 
-       temp = ovl_do_tmpfile(ofs, c->workdir, c->stat.mode);
+       tmpfile = ovl_do_tmpfile(ofs, c->workdir, c->stat.mode);
        ovl_revert_cu_creds(&cc);
 
-       if (IS_ERR(temp))
-               return PTR_ERR(temp);
+       if (IS_ERR(tmpfile))
+               return PTR_ERR(tmpfile);
 
-       err = ovl_copy_up_inode(c, temp);
+       temp = tmpfile->f_path.dentry;
+       if (!c->metacopy && c->stat.size) {
+               err = ovl_copy_up_file(ofs, c->dentry, tmpfile, c->stat.size);
+               if (err)
+                       return err;
+       }
+
+       err = ovl_copy_up_metadata(c, temp);
        if (err)
-               goto out_dput;
+               goto out_fput;
 
        inode_lock_nested(udir, I_MUTEX_PARENT);
 
@@ -761,16 +773,14 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
        inode_unlock(udir);
 
        if (err)
-               goto out_dput;
+               goto out_fput;
 
        if (!c->metacopy)
                ovl_set_upperdata(d_inode(c->dentry));
-       ovl_inode_update(d_inode(c->dentry), temp);
+       ovl_inode_update(d_inode(c->dentry), dget(temp));
 
-       return 0;
-
-out_dput:
-       dput(temp);
+out_fput:
+       fput(tmpfile);
        return err;
 }
 
@@ -899,7 +909,7 @@ static ssize_t ovl_getxattr_value(const struct path *path, char *name, char **va
 static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
 {
        struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
-       struct path upperpath, datapath;
+       struct path upperpath;
        int err;
        char *capability = NULL;
        ssize_t cap_size;
@@ -908,10 +918,6 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
        if (WARN_ON(upperpath.dentry == NULL))
                return -EIO;
 
-       ovl_path_lowerdata(c->dentry, &datapath);
-       if (WARN_ON(datapath.dentry == NULL))
-               return -EIO;
-
        if (c->stat.size) {
                err = cap_size = ovl_getxattr_value(&upperpath, XATTR_NAME_CAPS,
                                                    &capability);
@@ -919,7 +925,7 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
                        goto out;
        }
 
-       err = ovl_copy_up_data(ofs, &datapath, &upperpath, c->stat.size);
+       err = ovl_copy_up_data(c, &upperpath);
        if (err)
                goto out_free;