diff mbox series

[v2,6/8] vfs: move open right after ->tmpfile()

Message ID 20220919141031.1834447-7-mszeredi@redhat.com (mailing list archive)
State New, archived
Headers show
Series fuse tmpfile | expand

Commit Message

Miklos Szeredi Sept. 19, 2022, 2:10 p.m. UTC
Create a helper finish_open_simple() that opens the file with the original
dentry.  Handle the error case here as well to simplify callers.

Call this helper right after ->tmpfile() is called.

Next patch will change the tmpfile API and move this call into tmpfile
instances.

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
---
 fs/namei.c         | 65 ++++++++++++++++++++--------------------------
 include/linux/fs.h |  9 +++++++
 2 files changed, 37 insertions(+), 37 deletions(-)

Comments

Al Viro Sept. 20, 2022, 1:40 a.m. UTC | #1
On Mon, Sep 19, 2022 at 04:10:29PM +0200, Miklos Szeredi wrote:

> -	child = d_alloc(dentry, &slash_name);
> +	child = d_alloc(parentpath->dentry, &slash_name);
>  	if (unlikely(!child))
>  		goto out_err;
> +	file->f_path.mnt = parentpath->mnt;
> +	file->f_path.dentry = child;
>  	mode = vfs_prepare_mode(mnt_userns, dir, mode, mode, mode);
>  	error = dir->i_op->tmpfile(mnt_userns, dir, child, mode);
> +	error = finish_open_simple(file, error);
> +	dput(child);
> +	if (error)
> +		goto out_err;
> +	error = may_open(mnt_userns, &file->f_path, 0, file->f_flags);
>  	if (error)
>  		goto out_err;
>  	error = -ENOENT;
>  	inode = child->d_inode;
>  	if (unlikely(!inode))
>  		goto out_err;

Ugh...  First of all, goto out_err leading to immediate return error;
is obfuscation for no good reason.  What's more, how the hell can
we get a negative dentry here?  The only thing that makes this check
valid is that after successful open child is pinned as file->f_path.dentry -
otherwise dput() above might have very well freed it.  And if we ever
end up with a negative dentry in file->f_path.dentry of an opened
file, we are really screwed...
Miklos Szeredi Sept. 20, 2022, 8:08 a.m. UTC | #2
On Tue, 20 Sept 2022 at 03:41, Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> On Mon, Sep 19, 2022 at 04:10:29PM +0200, Miklos Szeredi wrote:
>
> > -     child = d_alloc(dentry, &slash_name);
> > +     child = d_alloc(parentpath->dentry, &slash_name);
> >       if (unlikely(!child))
> >               goto out_err;
> > +     file->f_path.mnt = parentpath->mnt;
> > +     file->f_path.dentry = child;
> >       mode = vfs_prepare_mode(mnt_userns, dir, mode, mode, mode);
> >       error = dir->i_op->tmpfile(mnt_userns, dir, child, mode);
> > +     error = finish_open_simple(file, error);
> > +     dput(child);
> > +     if (error)
> > +             goto out_err;
> > +     error = may_open(mnt_userns, &file->f_path, 0, file->f_flags);
> >       if (error)
> >               goto out_err;
> >       error = -ENOENT;
> >       inode = child->d_inode;
> >       if (unlikely(!inode))
> >               goto out_err;
>
> Ugh...  First of all, goto out_err leading to immediate return error;
> is obfuscation for no good reason.  What's more, how the hell can
> we get a negative dentry here?  The only thing that makes this check
> valid is that after successful open child is pinned as file->f_path.dentry -
> otherwise dput() above might have very well freed it.  And if we ever
> end up with a negative dentry in file->f_path.dentry of an opened
> file, we are really screwed...

OK.

Thanks,
Miklos
diff mbox series

Patch

diff --git a/fs/namei.c b/fs/namei.c
index 652d09ae66fb..eb1e1956450f 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3583,11 +3583,12 @@  static int do_open(struct nameidata *nd,
  * On non-idmapped mounts or if permission checking is to be performed on the
  * raw inode simply passs init_user_ns.
  */
-static struct dentry *vfs_tmpfile(struct user_namespace *mnt_userns,
-			   struct dentry *dentry, umode_t mode, int open_flag)
+static int vfs_tmpfile(struct user_namespace *mnt_userns,
+		       const struct path *parentpath,
+		       struct file *file, umode_t mode)
 {
-	struct dentry *child = NULL;
-	struct inode *dir = dentry->d_inode;
+	struct dentry *child;
+	struct inode *dir = d_inode(parentpath->dentry);
 	struct inode *inode;
 	int error;
 
@@ -3599,28 +3600,34 @@  static struct dentry *vfs_tmpfile(struct user_namespace *mnt_userns,
 	if (!dir->i_op->tmpfile)
 		goto out_err;
 	error = -ENOMEM;
-	child = d_alloc(dentry, &slash_name);
+	child = d_alloc(parentpath->dentry, &slash_name);
 	if (unlikely(!child))
 		goto out_err;
+	file->f_path.mnt = parentpath->mnt;
+	file->f_path.dentry = child;
 	mode = vfs_prepare_mode(mnt_userns, dir, mode, mode, mode);
 	error = dir->i_op->tmpfile(mnt_userns, dir, child, mode);
+	error = finish_open_simple(file, error);
+	dput(child);
+	if (error)
+		goto out_err;
+	error = may_open(mnt_userns, &file->f_path, 0, file->f_flags);
 	if (error)
 		goto out_err;
 	error = -ENOENT;
 	inode = child->d_inode;
 	if (unlikely(!inode))
 		goto out_err;
-	if (!(open_flag & O_EXCL)) {
+	if (!(file->f_flags & O_EXCL)) {
 		spin_lock(&inode->i_lock);
 		inode->i_state |= I_LINKABLE;
 		spin_unlock(&inode->i_lock);
 	}
 	ima_post_create_tmpfile(mnt_userns, inode);
-	return child;
+	return 0;
 
 out_err:
-	dput(child);
-	return ERR_PTR(error);
+	return error;
 }
 
 /**
@@ -3641,25 +3648,15 @@  struct file *tmpfile_open(struct user_namespace *mnt_userns,
 {
 	struct file *file;
 	int error;
-	struct path path = { .mnt = parentpath->mnt };
-
-	path.dentry = vfs_tmpfile(mnt_userns, parentpath->dentry, mode, open_flag);
-	if (IS_ERR(path.dentry))
-		return ERR_CAST(path.dentry);
-
-	error = may_open(mnt_userns, &path, 0, open_flag);
-	file = ERR_PTR(error);
-	if (error)
-		goto out_dput;
-
-	/*
-	 * This relies on the "noaccount" property of fake open, otherwise
-	 * equivalent to dentry_open().
-	 */
-	file = open_with_fake_path(&path, open_flag, d_inode(path.dentry), cred);
-out_dput:
-	dput(path.dentry);
 
+	file = alloc_empty_file_noaccount(open_flag, cred);
+	if (!IS_ERR(file)) {
+		error = vfs_tmpfile(mnt_userns, parentpath, file, mode);
+		if (error) {
+			fput(file);
+			file = ERR_PTR(error);
+		}
+	}
 	return file;
 }
 EXPORT_SYMBOL(tmpfile_open);
@@ -3669,26 +3666,20 @@  static int do_tmpfile(struct nameidata *nd, unsigned flags,
 		struct file *file)
 {
 	struct user_namespace *mnt_userns;
-	struct dentry *child;
 	struct path path;
 	int error = path_lookupat(nd, flags | LOOKUP_DIRECTORY, &path);
+
 	if (unlikely(error))
 		return error;
 	error = mnt_want_write(path.mnt);
 	if (unlikely(error))
 		goto out;
 	mnt_userns = mnt_user_ns(path.mnt);
-	child = vfs_tmpfile(mnt_userns, path.dentry, op->mode, op->open_flag);
-	error = PTR_ERR(child);
-	if (IS_ERR(child))
+	error = vfs_tmpfile(mnt_userns, &path, file, op->mode);
+	if (error)
 		goto out2;
-	dput(path.dentry);
-	path.dentry = child;
-	audit_inode(nd->name, child, 0);
+	audit_inode(nd->name, file->f_path.dentry, 0);
 	/* Don't check for other permissions, the inode was just created */
-	error = may_open(mnt_userns, &path, 0, op->open_flag);
-	if (!error)
-		error = vfs_open(&path, file);
 out2:
 	mnt_drop_write(path.mnt);
 out:
diff --git a/include/linux/fs.h b/include/linux/fs.h
index a445da4842e0..f0d17eefb966 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2780,6 +2780,15 @@  extern int finish_open(struct file *file, struct dentry *dentry,
 			int (*open)(struct inode *, struct file *));
 extern int finish_no_open(struct file *file, struct dentry *dentry);
 
+/* Helper for the simple case when original dentry is used */
+static inline int finish_open_simple(struct file *file, int error)
+{
+	if (error)
+		return error;
+
+	return finish_open(file, file->f_path.dentry, NULL);
+}
+
 /* fs/dcache.c */
 extern void __init vfs_caches_init_early(void);
 extern void __init vfs_caches_init(void);