Merge tag 'ovl-update-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayf...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 22 May 2024 16:23:18 +0000 (09:23 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 22 May 2024 16:23:18 +0000 (09:23 -0700)
Pull overlayfs updates from Miklos Szeredi:

 - Add tmpfile support

 - Clean up include

* tag 'ovl-update-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs:
  ovl: remove duplicate included header
  ovl: remove upper umask handling from ovl_create_upper()
  ovl: implement tmpfile

fs/backing-file.c
fs/internal.h
fs/namei.c
fs/overlayfs/dir.c
fs/overlayfs/file.c
fs/overlayfs/inode.c
fs/overlayfs/overlayfs.h
include/linux/backing-file.h

index 740185198db3473163bb141719e6e03514314c72..afb557446c27c7fe8476b1b0d799ffc226d25ec2 100644 (file)
@@ -52,6 +52,29 @@ struct file *backing_file_open(const struct path *user_path, int flags,
 }
 EXPORT_SYMBOL_GPL(backing_file_open);
 
+struct file *backing_tmpfile_open(const struct path *user_path, int flags,
+                                 const struct path *real_parentpath,
+                                 umode_t mode, const struct cred *cred)
+{
+       struct mnt_idmap *real_idmap = mnt_idmap(real_parentpath->mnt);
+       struct file *f;
+       int error;
+
+       f = alloc_empty_backing_file(flags, cred);
+       if (IS_ERR(f))
+               return f;
+
+       path_get(user_path);
+       *backing_file_user_path(f) = *user_path;
+       error = vfs_tmpfile(real_idmap, real_parentpath, f, mode);
+       if (error) {
+               fput(f);
+               f = ERR_PTR(error);
+       }
+       return f;
+}
+EXPORT_SYMBOL(backing_tmpfile_open);
+
 struct backing_aio {
        struct kiocb iocb;
        refcount_t ref;
index 7ca738904e34a24074c26dd277e2800a40895cf5..ab2225136f604193a14eaf0198280343fa636754 100644 (file)
@@ -62,6 +62,9 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode);
 int do_symlinkat(struct filename *from, int newdfd, struct filename *to);
 int do_linkat(int olddfd, struct filename *old, int newdfd,
                        struct filename *new, int flags);
+int vfs_tmpfile(struct mnt_idmap *idmap,
+               const struct path *parentpath,
+               struct file *file, umode_t mode);
 
 /*
  * namespace.c
index cb5dde0e309f7a2d27aae1d00d0f12da4064e92e..37fb0a8aa09a05e93637c984e665158006a50ef8 100644 (file)
@@ -3676,9 +3676,9 @@ static int do_open(struct nameidata *nd,
  * On non-idmapped mounts or if permission checking is to be performed on the
  * raw inode simply pass @nop_mnt_idmap.
  */
-static int vfs_tmpfile(struct mnt_idmap *idmap,
-                      const struct path *parentpath,
-                      struct file *file, umode_t mode)
+int vfs_tmpfile(struct mnt_idmap *idmap,
+               const struct path *parentpath,
+               struct file *file, umode_t mode)
 {
        struct dentry *child;
        struct inode *dir = d_inode(parentpath->dentry);
index 0f8b4a719237c7202bbf89d8e9fb192b2f2e1a77..116f542442ddd670c3a62861ef83c44d76f0a7f9 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/posix_acl_xattr.h>
 #include <linux/atomic.h>
 #include <linux/ratelimit.h>
+#include <linux/backing-file.h>
 #include "overlayfs.h"
 
 static unsigned short ovl_redirect_max = 256;
@@ -260,14 +261,13 @@ static int ovl_set_opaque(struct dentry *dentry, struct dentry *upperdentry)
  * may not use to instantiate the new dentry.
  */
 static int ovl_instantiate(struct dentry *dentry, struct inode *inode,
-                          struct dentry *newdentry, bool hardlink)
+                          struct dentry *newdentry, bool hardlink, struct file *tmpfile)
 {
        struct ovl_inode_params oip = {
                .upperdentry = newdentry,
                .newinode = inode,
        };
 
-       ovl_dir_modified(dentry->d_parent, false);
        ovl_dentry_set_upper_alias(dentry);
        ovl_dentry_init_reval(dentry, newdentry, NULL);
 
@@ -295,6 +295,9 @@ static int ovl_instantiate(struct dentry *dentry, struct inode *inode,
                inc_nlink(inode);
        }
 
+       if (tmpfile)
+               d_mark_tmpfile(tmpfile, inode);
+
        d_instantiate(dentry, inode);
        if (inode != oip.newinode) {
                pr_warn_ratelimited("newly created inode found in cache (%pd2)\n",
@@ -327,9 +330,6 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
        struct dentry *newdentry;
        int err;
 
-       if (!attr->hardlink && !IS_POSIXACL(udir))
-               attr->mode &= ~current_umask();
-
        inode_lock_nested(udir, I_MUTEX_PARENT);
        newdentry = ovl_create_real(ofs, udir,
                                    ovl_lookup_upper(ofs, dentry->d_name.name,
@@ -345,7 +345,8 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
                ovl_set_opaque(dentry, newdentry);
        }
 
-       err = ovl_instantiate(dentry, inode, newdentry, !!attr->hardlink);
+       ovl_dir_modified(dentry->d_parent, false);
+       err = ovl_instantiate(dentry, inode, newdentry, !!attr->hardlink, NULL);
        if (err)
                goto out_cleanup;
 out_unlock:
@@ -529,7 +530,8 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
                if (err)
                        goto out_cleanup;
        }
-       err = ovl_instantiate(dentry, inode, newdentry, hardlink);
+       ovl_dir_modified(dentry->d_parent, false);
+       err = ovl_instantiate(dentry, inode, newdentry, hardlink, NULL);
        if (err) {
                ovl_cleanup(ofs, udir, newdentry);
                dput(newdentry);
@@ -551,12 +553,35 @@ out_cleanup:
        goto out_dput;
 }
 
+static int ovl_setup_cred_for_create(struct dentry *dentry, struct inode *inode,
+                                    umode_t mode, const struct cred *old_cred)
+{
+       int err;
+       struct cred *override_cred;
+
+       override_cred = prepare_creds();
+       if (!override_cred)
+               return -ENOMEM;
+
+       override_cred->fsuid = inode->i_uid;
+       override_cred->fsgid = inode->i_gid;
+       err = security_dentry_create_files_as(dentry, mode, &dentry->d_name,
+                                             old_cred, override_cred);
+       if (err) {
+               put_cred(override_cred);
+               return err;
+       }
+       put_cred(override_creds(override_cred));
+       put_cred(override_cred);
+
+       return 0;
+}
+
 static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
                              struct ovl_cattr *attr, bool origin)
 {
        int err;
        const struct cred *old_cred;
-       struct cred *override_cred;
        struct dentry *parent = dentry->d_parent;
 
        old_cred = ovl_override_creds(dentry->d_sb);
@@ -572,10 +597,6 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
        }
 
        if (!attr->hardlink) {
-               err = -ENOMEM;
-               override_cred = prepare_creds();
-               if (!override_cred)
-                       goto out_revert_creds;
                /*
                 * In the creation cases(create, mkdir, mknod, symlink),
                 * ovl should transfer current's fs{u,g}id to underlying
@@ -589,17 +610,9 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
                 * create a new inode, so just use the ovl mounter's
                 * fs{u,g}id.
                 */
-               override_cred->fsuid = inode->i_uid;
-               override_cred->fsgid = inode->i_gid;
-               err = security_dentry_create_files_as(dentry,
-                               attr->mode, &dentry->d_name, old_cred,
-                               override_cred);
-               if (err) {
-                       put_cred(override_cred);
+               err = ovl_setup_cred_for_create(dentry, inode, attr->mode, old_cred);
+               if (err)
                        goto out_revert_creds;
-               }
-               put_cred(override_creds(override_cred));
-               put_cred(override_cred);
        }
 
        if (!ovl_dentry_is_whiteout(dentry))
@@ -1290,6 +1303,100 @@ out:
        return err;
 }
 
+static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
+                             struct inode *inode, umode_t mode)
+{
+       const struct cred *old_cred;
+       struct path realparentpath;
+       struct file *realfile;
+       struct dentry *newdentry;
+       /* It's okay to set O_NOATIME, since the owner will be current fsuid */
+       int flags = file->f_flags | OVL_OPEN_FLAGS;
+       int err;
+
+       err = ovl_copy_up(dentry->d_parent);
+       if (err)
+               return err;
+
+       old_cred = ovl_override_creds(dentry->d_sb);
+       err = ovl_setup_cred_for_create(dentry, inode, mode, old_cred);
+       if (err)
+               goto out_revert_creds;
+
+       ovl_path_upper(dentry->d_parent, &realparentpath);
+       realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath,
+                                       mode, current_cred());
+       err = PTR_ERR_OR_ZERO(realfile);
+       pr_debug("tmpfile/open(%pd2, 0%o) = %i\n", realparentpath.dentry, mode, err);
+       if (err)
+               goto out_revert_creds;
+
+       /* ovl_instantiate() consumes the newdentry reference on success */
+       newdentry = dget(realfile->f_path.dentry);
+       err = ovl_instantiate(dentry, inode, newdentry, false, file);
+       if (!err) {
+               file->private_data = realfile;
+       } else {
+               dput(newdentry);
+               fput(realfile);
+       }
+out_revert_creds:
+       revert_creds(old_cred);
+       return err;
+}
+
+static int ovl_dummy_open(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+static int ovl_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
+                      struct file *file, umode_t mode)
+{
+       int err;
+       struct dentry *dentry = file->f_path.dentry;
+       struct inode *inode;
+
+       if (!OVL_FS(dentry->d_sb)->tmpfile)
+               return -EOPNOTSUPP;
+
+       err = ovl_want_write(dentry);
+       if (err)
+               return err;
+
+       err = -ENOMEM;
+       inode = ovl_new_inode(dentry->d_sb, mode, 0);
+       if (!inode)
+               goto drop_write;
+
+       inode_init_owner(&nop_mnt_idmap, inode, dir, mode);
+       err = ovl_create_tmpfile(file, dentry, inode, inode->i_mode);
+       if (err)
+               goto put_inode;
+
+       /*
+        * Check if the preallocated inode was actually used.  Having something
+        * else assigned to the dentry shouldn't happen as that would indicate
+        * that the backing tmpfile "leaked" out of overlayfs.
+        */
+       err = -EIO;
+       if (WARN_ON(inode != d_inode(dentry)))
+               goto put_realfile;
+
+       /* inode reference was transferred to dentry */
+       inode = NULL;
+       err = finish_open(file, dentry, ovl_dummy_open);
+put_realfile:
+       /* Without FMODE_OPENED ->release() won't be called on @file */
+       if (!(file->f_mode & FMODE_OPENED))
+               fput(file->private_data);
+put_inode:
+       iput(inode);
+drop_write:
+       ovl_drop_write(dentry);
+       return err;
+}
+
 const struct inode_operations ovl_dir_inode_operations = {
        .lookup         = ovl_lookup,
        .mkdir          = ovl_mkdir,
@@ -1310,4 +1417,5 @@ const struct inode_operations ovl_dir_inode_operations = {
        .update_time    = ovl_update_time,
        .fileattr_get   = ovl_fileattr_get,
        .fileattr_set   = ovl_fileattr_set,
+       .tmpfile        = ovl_tmpfile,
 };
index 05536964d37fdfae71508d15dbfab9bf2f75f38e..1a411cae57ed9cb8441b27f7a11a3e69d2a68184 100644 (file)
@@ -24,9 +24,6 @@ static char ovl_whatisit(struct inode *inode, struct inode *realinode)
                return 'm';
 }
 
-/* No atime modification on underlying */
-#define OVL_OPEN_FLAGS (O_NOATIME)
-
 static struct file *ovl_open_realfile(const struct file *file,
                                      const struct path *realpath)
 {
index c63b31a460befcc9f35a573618a1fb13cd20657f..35fd3e3e177807fd5d157f4e9d93b1e7f198610a 100644 (file)
@@ -8,7 +8,6 @@
 #include <linux/slab.h>
 #include <linux/cred.h>
 #include <linux/xattr.h>
-#include <linux/posix_acl.h>
 #include <linux/ratelimit.h>
 #include <linux/fiemap.h>
 #include <linux/fileattr.h>
index ee949f3e7c77839e999cb6f5344cb167fd4e43b5..0bfe35da4b7b7a92b032a26369ae0d89c014bad3 100644 (file)
@@ -175,6 +175,9 @@ static inline int ovl_metadata_digest_size(const struct ovl_metacopy *metacopy)
        return (int)metacopy->len - OVL_METACOPY_MIN_SIZE;
 }
 
+/* No atime modification on underlying */
+#define OVL_OPEN_FLAGS (O_NOATIME)
+
 extern const char *const ovl_xattr_table[][2];
 static inline const char *ovl_xattr(struct ovl_fs *ofs, enum ovl_xattr ox)
 {
index 3f1fe1774f1b6984ad0edc64f21e12a7abe7f7c4..4b61b0e577209e4ea20803752bb55b6e44983bed 100644 (file)
@@ -22,6 +22,9 @@ struct backing_file_ctx {
 struct file *backing_file_open(const struct path *user_path, int flags,
                               const struct path *real_path,
                               const struct cred *cred);
+struct file *backing_tmpfile_open(const struct path *user_path, int flags,
+                                 const struct path *real_parentpath,
+                                 umode_t mode, const struct cred *cred);
 ssize_t backing_file_read_iter(struct file *file, struct iov_iter *iter,
                               struct kiocb *iocb, int flags,
                               struct backing_file_ctx *ctx);