- * [PATCH v2 1/3] fs: get mnt_writers count for an open backing file's real path
  2023-10-07  8:44 [PATCH v2 0/3] Reduce impact of overlayfs backing files fake path Amir Goldstein
@ 2023-10-07  8:44 ` Amir Goldstein
  2023-10-09  6:43   ` Al Viro
  2023-10-07  8:44 ` [PATCH v2 2/3] fs: create helper file_user_path() for user displayed mapped file path Amir Goldstein
  2023-10-07  8:44 ` [PATCH v2 3/3] fs: store real path instead of fake path in backing file f_path Amir Goldstein
  2 siblings, 1 reply; 11+ messages in thread
From: Amir Goldstein @ 2023-10-07  8:44 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Al Viro, Miklos Szeredi, Paul Moore, James Morris,
	Serge E . Hallyn, Mimi Zohar, linux-security-module,
	linux-integrity, linux-unionfs, linux-fsdevel
A writeable mapped backing file can perform writes to the real inode.
Therefore, the real path mount must be kept writable so long as the
writable map exists.
This may not be strictly needed for ovelrayfs private upper mount,
but it is correct to take the mnt_writers count in the vfs helper.
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
 fs/internal.h | 15 +++++++++++++--
 fs/open.c     | 34 ++++++++++++++++++++++++++++------
 2 files changed, 41 insertions(+), 8 deletions(-)
diff --git a/fs/internal.h b/fs/internal.h
index f08d8fe3ae5e..846d5133dd9c 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -96,13 +96,24 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred);
 struct file *alloc_empty_backing_file(int flags, const struct cred *cred);
 void release_empty_file(struct file *f);
 
+static inline void file_put_write_access(struct file *file)
+{
+	put_write_access(file->f_inode);
+	mnt_put_write_access(file->f_path.mnt);
+	if (unlikely(file->f_mode & FMODE_BACKING)) {
+		struct path *real_path = backing_file_real_path(file);
+
+		if (real_path->mnt)
+			mnt_put_write_access(real_path->mnt);
+	}
+}
+
 static inline void put_file_access(struct file *file)
 {
 	if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
 		i_readcount_dec(file->f_inode);
 	} else if (file->f_mode & FMODE_WRITER) {
-		put_write_access(file->f_inode);
-		mnt_put_write_access(file->f_path.mnt);
+		file_put_write_access(file);
 	}
 }
 
diff --git a/fs/open.c b/fs/open.c
index a65ce47810cf..2f3e28512663 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -870,6 +870,33 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group)
 	return ksys_fchown(fd, user, group);
 }
 
+static inline int file_get_write_access(struct file *f)
+{
+	int error;
+
+	error = get_write_access(f->f_inode);
+	if (unlikely(error))
+		return error;
+	error = mnt_get_write_access(f->f_path.mnt);
+	if (unlikely(error))
+		goto cleanup_inode;
+	if (unlikely(f->f_mode & FMODE_BACKING)) {
+		struct path *real_path = backing_file_real_path(f);
+
+		if (real_path->mnt)
+			error = mnt_get_write_access(real_path->mnt);
+		if (unlikely(error))
+			goto cleanup_mnt;
+	}
+	return 0;
+
+cleanup_mnt:
+	mnt_put_write_access(f->f_path.mnt);
+cleanup_inode:
+	put_write_access(f->f_inode);
+	return error;
+}
+
 static int do_dentry_open(struct file *f,
 			  struct inode *inode,
 			  int (*open)(struct inode *, struct file *))
@@ -892,14 +919,9 @@ static int do_dentry_open(struct file *f,
 	if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
 		i_readcount_inc(inode);
 	} else if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) {
-		error = get_write_access(inode);
+		error = file_get_write_access(f);
 		if (unlikely(error))
 			goto cleanup_file;
-		error = mnt_get_write_access(f->f_path.mnt);
-		if (unlikely(error)) {
-			put_write_access(inode);
-			goto cleanup_file;
-		}
 		f->f_mode |= FMODE_WRITER;
 	}
 
-- 
2.34.1
^ permalink raw reply related	[flat|nested] 11+ messages in thread
- * Re: [PATCH v2 1/3] fs: get mnt_writers count for an open backing file's real path
  2023-10-07  8:44 ` [PATCH v2 1/3] fs: get mnt_writers count for an open backing file's real path Amir Goldstein
@ 2023-10-09  6:43   ` Al Viro
  2023-10-09  8:03     ` Amir Goldstein
  0 siblings, 1 reply; 11+ messages in thread
From: Al Viro @ 2023-10-09  6:43 UTC (permalink / raw)
  To: Amir Goldstein
  Cc: Christian Brauner, Miklos Szeredi, Paul Moore, James Morris,
	Serge E . Hallyn, Mimi Zohar, linux-security-module,
	linux-integrity, linux-unionfs, linux-fsdevel
On Sat, Oct 07, 2023 at 11:44:31AM +0300, Amir Goldstein wrote:
> +static inline void file_put_write_access(struct file *file)
> +{
> +	put_write_access(file->f_inode);
> +	mnt_put_write_access(file->f_path.mnt);
> +	if (unlikely(file->f_mode & FMODE_BACKING)) {
> +		struct path *real_path = backing_file_real_path(file);
> +
> +		if (real_path->mnt)
> +			mnt_put_write_access(real_path->mnt);
IDGI.  Where do we get FMODE_BACKING combined with NULL real_path.mnt *AND*
put_file_access() possibly called?  Or file_get_write_access(), for
that matter...
FMODE_BACKING is set only in alloc_empty_backing_file().  The only caller
is backing_file_open(), which immediately sets real_path to its third
argument.  That could only come from ovl_open_realfile().  And if that
had been called with buggered struct path, it would have already blown
up on mnt_idmap(realpath->mnt).
The only interval where such beasts exist is from
        ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT;
	return &ff->file;
in alloc_empty_backing_file() through
	f->f_path = *path;
	path_get(real_path);
	*backing_file_real_path(f) = *real_path;
in backing_file_open().  Where would that struct file (just allocated,
never seen outside of local variables in those two scopes) be passed
to get_file_write_access() or put_file_access()?
Or am I misreading something?
^ permalink raw reply	[flat|nested] 11+ messages in thread
- * Re: [PATCH v2 1/3] fs: get mnt_writers count for an open backing file's real path
  2023-10-09  6:43   ` Al Viro
@ 2023-10-09  8:03     ` Amir Goldstein
  0 siblings, 0 replies; 11+ messages in thread
From: Amir Goldstein @ 2023-10-09  8:03 UTC (permalink / raw)
  To: Al Viro
  Cc: Christian Brauner, Miklos Szeredi, Paul Moore, James Morris,
	Serge E . Hallyn, Mimi Zohar, linux-security-module,
	linux-integrity, linux-unionfs, linux-fsdevel
On Mon, Oct 9, 2023 at 9:43 AM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> On Sat, Oct 07, 2023 at 11:44:31AM +0300, Amir Goldstein wrote:
> > +static inline void file_put_write_access(struct file *file)
> > +{
> > +     put_write_access(file->f_inode);
> > +     mnt_put_write_access(file->f_path.mnt);
> > +     if (unlikely(file->f_mode & FMODE_BACKING)) {
> > +             struct path *real_path = backing_file_real_path(file);
> > +
> > +             if (real_path->mnt)
> > +                     mnt_put_write_access(real_path->mnt);
>
> IDGI.  Where do we get FMODE_BACKING combined with NULL real_path.mnt *AND*
> put_file_access() possibly called?  Or file_get_write_access(), for
> that matter...
Right.
I was being over prudent here.
>
> FMODE_BACKING is set only in alloc_empty_backing_file().  The only caller
> is backing_file_open(), which immediately sets real_path to its third
> argument.  That could only come from ovl_open_realfile().  And if that
> had been called with buggered struct path, it would have already blown
> up on mnt_idmap(realpath->mnt).
>
> The only interval where such beasts exist is from
>         ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT;
>         return &ff->file;
> in alloc_empty_backing_file() through
>
>         f->f_path = *path;
>         path_get(real_path);
>         *backing_file_real_path(f) = *real_path;
>
> in backing_file_open().  Where would that struct file (just allocated,
> never seen outside of local variables in those two scopes) be passed
> to get_file_write_access() or put_file_access()?
>
> Or am I misreading something?
No. You are right.
I admit that I did consider adding use cases in the future
where a backing_file real_path is initialized lazily, but that
is not the case with current overlayfs code, so we don't
need to worry about that now.
Thanks,
Amir.
^ permalink raw reply	[flat|nested] 11+ messages in thread
 
 
- * [PATCH v2 2/3] fs: create helper file_user_path() for user displayed mapped file path
  2023-10-07  8:44 [PATCH v2 0/3] Reduce impact of overlayfs backing files fake path Amir Goldstein
  2023-10-07  8:44 ` [PATCH v2 1/3] fs: get mnt_writers count for an open backing file's real path Amir Goldstein
@ 2023-10-07  8:44 ` Amir Goldstein
  2023-10-09  7:00   ` Al Viro
  2023-10-07  8:44 ` [PATCH v2 3/3] fs: store real path instead of fake path in backing file f_path Amir Goldstein
  2 siblings, 1 reply; 11+ messages in thread
From: Amir Goldstein @ 2023-10-07  8:44 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Al Viro, Miklos Szeredi, Paul Moore, James Morris,
	Serge E . Hallyn, Mimi Zohar, linux-security-module,
	linux-integrity, linux-unionfs, linux-fsdevel
Overlayfs uses backing files with "fake" overlayfs f_path and "real"
underlying f_inode, in order to use underlying inode aops for mapped
files and to display the overlayfs path in /proc/<pid>/maps.
In preparation for storing the overlayfs "fake" path instead of the
underlying "real" path in struct backing_file, define a noop helper
file_user_path() that returns f_path for now.
Use the new helper in procfs and kernel logs whenever a path of a
mapped file is displayed to users.
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
 arch/arc/kernel/troubleshoot.c |  3 ++-
 fs/proc/base.c                 |  2 +-
 fs/proc/nommu.c                |  2 +-
 fs/proc/task_mmu.c             |  4 ++--
 fs/proc/task_nommu.c           |  2 +-
 include/linux/fs.h             | 14 ++++++++++++++
 kernel/trace/trace_output.c    |  2 +-
 7 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/arch/arc/kernel/troubleshoot.c b/arch/arc/kernel/troubleshoot.c
index d5b3ed2c58f5..097887e4a9b7 100644
--- a/arch/arc/kernel/troubleshoot.c
+++ b/arch/arc/kernel/troubleshoot.c
@@ -93,7 +93,8 @@ static void show_faulting_vma(unsigned long address)
 		char *nm = "?";
 
 		if (vma->vm_file) {
-			nm = file_path(vma->vm_file, buf, ARC_PATH_MAX-1);
+			nm = d_path(file_user_path(vma->vm_file), buf,
+				    ARC_PATH_MAX-1);
 			if (IS_ERR(nm))
 				nm = "?";
 		}
diff --git a/fs/proc/base.c b/fs/proc/base.c
index ffd54617c354..20695c928ee6 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2218,7 +2218,7 @@ static int map_files_get_link(struct dentry *dentry, struct path *path)
 	rc = -ENOENT;
 	vma = find_exact_vma(mm, vm_start, vm_end);
 	if (vma && vma->vm_file) {
-		*path = vma->vm_file->f_path;
+		*path = *file_user_path(vma->vm_file);
 		path_get(path);
 		rc = 0;
 	}
diff --git a/fs/proc/nommu.c b/fs/proc/nommu.c
index 4d3493579458..c6e7ebc63756 100644
--- a/fs/proc/nommu.c
+++ b/fs/proc/nommu.c
@@ -58,7 +58,7 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region)
 
 	if (file) {
 		seq_pad(m, ' ');
-		seq_file_path(m, file, "");
+		seq_path(m, file_user_path(file), "");
 	}
 
 	seq_putc(m, '\n');
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 3dd5be96691b..1593940ca01e 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -296,7 +296,7 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma)
 		if (anon_name)
 			seq_printf(m, "[anon_shmem:%s]", anon_name->name);
 		else
-			seq_file_path(m, file, "\n");
+			seq_path(m, file_user_path(file), "\n");
 		goto done;
 	}
 
@@ -1967,7 +1967,7 @@ static int show_numa_map(struct seq_file *m, void *v)
 
 	if (file) {
 		seq_puts(m, " file=");
-		seq_file_path(m, file, "\n\t= ");
+		seq_path(m, file_user_path(file), "\n\t= ");
 	} else if (vma_is_initial_heap(vma)) {
 		seq_puts(m, " heap");
 	} else if (vma_is_initial_stack(vma)) {
diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c
index 7cebd397cc26..bce674533000 100644
--- a/fs/proc/task_nommu.c
+++ b/fs/proc/task_nommu.c
@@ -157,7 +157,7 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma)
 
 	if (file) {
 		seq_pad(m, ' ');
-		seq_file_path(m, file, "");
+		seq_path(m, file_user_path(file), "");
 	} else if (mm && vma_is_initial_stack(vma)) {
 		seq_pad(m, ' ');
 		seq_puts(m, "[stack]");
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 7f7e7d2efbeb..a8e4e1cac48e 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2472,6 +2472,20 @@ static inline const struct path *file_real_path(struct file *f)
 	return &f->f_path;
 }
 
+/*
+ * file_user_path - get the path to display for memory mapped file
+ *
+ * When mmapping a file on a stackable filesystem (e.g., overlayfs), the file
+ * stored in ->vm_file is a backing file whose f_inode is on the underlying
+ * filesystem.  When the mapped file path is displayed to user (e.g. via
+ * /proc/<pid>/maps), this helper should be used to get the path to display
+ * to the user, which is the path of the fd that user has requested to map.
+ */
+static inline const struct path *file_user_path(struct file *f)
+{
+	return &f->f_path;
+}
+
 static inline struct file *file_clone_open(struct file *file)
 {
 	return dentry_open(&file->f_path, file->f_flags, file->f_cred);
diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c
index db575094c498..d8b302d01083 100644
--- a/kernel/trace/trace_output.c
+++ b/kernel/trace/trace_output.c
@@ -404,7 +404,7 @@ static int seq_print_user_ip(struct trace_seq *s, struct mm_struct *mm,
 			vmstart = vma->vm_start;
 		}
 		if (file) {
-			ret = trace_seq_path(s, &file->f_path);
+			ret = trace_seq_path(s, file_user_path(file));
 			if (ret)
 				trace_seq_printf(s, "[+0x%lx]",
 						 ip - vmstart);
-- 
2.34.1
^ permalink raw reply related	[flat|nested] 11+ messages in thread
- * Re: [PATCH v2 2/3] fs: create helper file_user_path() for user displayed mapped file path
  2023-10-07  8:44 ` [PATCH v2 2/3] fs: create helper file_user_path() for user displayed mapped file path Amir Goldstein
@ 2023-10-09  7:00   ` Al Viro
  2023-10-09  7:51     ` Amir Goldstein
  0 siblings, 1 reply; 11+ messages in thread
From: Al Viro @ 2023-10-09  7:00 UTC (permalink / raw)
  To: Amir Goldstein
  Cc: Christian Brauner, Miklos Szeredi, Paul Moore, James Morris,
	Serge E . Hallyn, Mimi Zohar, linux-security-module,
	linux-integrity, linux-unionfs, linux-fsdevel
On Sat, Oct 07, 2023 at 11:44:32AM +0300, Amir Goldstein wrote:
> @@ -93,7 +93,8 @@ static void show_faulting_vma(unsigned long address)
>  		char *nm = "?";
>  
>  		if (vma->vm_file) {
> -			nm = file_path(vma->vm_file, buf, ARC_PATH_MAX-1);
> +			nm = d_path(file_user_path(vma->vm_file), buf,
> +				    ARC_PATH_MAX-1);
>  			if (IS_ERR(nm))
>  				nm = "?";
Umm...  At one point I considered this:
	if (vma->vm_file)
                pr_info("  @off 0x%lx in [%pD]  VMA: 0x%08lx to 0x%08lx\n",
                        vma->vm_start < TASK_UNMAPPED_BASE ?
                                address : address - vma->vm_start,
                        vma->vm_file, vma->vm_start, vma->vm_end);
	else
                pr_info("  @off 0x%lx in [anon]  VMA: 0x%08lx to 0x%08lx\n",
                        vma->vm_start < TASK_UNMAPPED_BASE ?
                                address : address - vma->vm_start,
                        vma->vm_start, vma->vm_end);
and to hell with that 'buf' thing...
^ permalink raw reply	[flat|nested] 11+ messages in thread
- * Re: [PATCH v2 2/3] fs: create helper file_user_path() for user displayed mapped file path
  2023-10-09  7:00   ` Al Viro
@ 2023-10-09  7:51     ` Amir Goldstein
  0 siblings, 0 replies; 11+ messages in thread
From: Amir Goldstein @ 2023-10-09  7:51 UTC (permalink / raw)
  To: Al Viro
  Cc: Christian Brauner, Miklos Szeredi, Paul Moore, James Morris,
	Serge E . Hallyn, Mimi Zohar, linux-security-module,
	linux-integrity, linux-unionfs, linux-fsdevel
On Mon, Oct 9, 2023 at 10:00 AM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> On Sat, Oct 07, 2023 at 11:44:32AM +0300, Amir Goldstein wrote:
>
> > @@ -93,7 +93,8 @@ static void show_faulting_vma(unsigned long address)
> >               char *nm = "?";
> >
> >               if (vma->vm_file) {
> > -                     nm = file_path(vma->vm_file, buf, ARC_PATH_MAX-1);
> > +                     nm = d_path(file_user_path(vma->vm_file), buf,
> > +                                 ARC_PATH_MAX-1);
> >                       if (IS_ERR(nm))
> >                               nm = "?";
>
> Umm...  At one point I considered this:
>         if (vma->vm_file)
>                 pr_info("  @off 0x%lx in [%pD]  VMA: 0x%08lx to 0x%08lx\n",
>                         vma->vm_start < TASK_UNMAPPED_BASE ?
>                                 address : address - vma->vm_start,
>                         vma->vm_file, vma->vm_start, vma->vm_end);
>         else
>                 pr_info("  @off 0x%lx in [anon]  VMA: 0x%08lx to 0x%08lx\n",
>                         vma->vm_start < TASK_UNMAPPED_BASE ?
>                                 address : address - vma->vm_start,
>                         vma->vm_start, vma->vm_end);
> and to hell with that 'buf' thing...
It's fine by me.
That would be consistent with print_bad_pte().
I've never had to debug vma/pte faults, so I don't know
how much of the path is really valuable for debugging.
Thanks,
Amir.
^ permalink raw reply	[flat|nested] 11+ messages in thread
 
 
- * [PATCH v2 3/3] fs: store real path instead of fake path in backing file f_path
  2023-10-07  8:44 [PATCH v2 0/3] Reduce impact of overlayfs backing files fake path Amir Goldstein
  2023-10-07  8:44 ` [PATCH v2 1/3] fs: get mnt_writers count for an open backing file's real path Amir Goldstein
  2023-10-07  8:44 ` [PATCH v2 2/3] fs: create helper file_user_path() for user displayed mapped file path Amir Goldstein
@ 2023-10-07  8:44 ` Amir Goldstein
  2023-10-09  7:48   ` Al Viro
  2 siblings, 1 reply; 11+ messages in thread
From: Amir Goldstein @ 2023-10-07  8:44 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Al Viro, Miklos Szeredi, Paul Moore, James Morris,
	Serge E . Hallyn, Mimi Zohar, linux-security-module,
	linux-integrity, linux-unionfs, linux-fsdevel
A backing file struct stores two path's, one "real" path that is referring
to f_inode and one "fake" path, which should be displayed to users in
/proc/<pid>/maps.
There is a lot more potential code that needs to know the "real" path, then
code that needs to know the "fake" path.
Instead of code having to request the "real" path with file_real_path(),
store the "real" path in f_path and require code that needs to know the
"fake" path request it with file_user_path().
Replace the file_real_path() helper with a simple const accessor f_path().
After this change, file_dentry() is not expected to observe any files
with overlayfs f_path and real f_inode, so the call to ->d_real() should
not be needed.  Leave the ->d_real() call for now and add an assertion
in ovl_d_real() to catch if we made wrong assumptions.
Suggested-by: Miklos Szeredi <miklos@szeredi.hu>
Link: https://lore.kernel.org/r/CAJfpegtt48eXhhjDFA1ojcHPNKj3Go6joryCPtEFAKpocyBsnw@mail.gmail.com/
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
 fs/file_table.c          | 12 ++++++------
 fs/internal.h            |  6 +++---
 fs/open.c                | 27 +++++++++++++--------------
 fs/overlayfs/super.c     | 11 ++++++++++-
 include/linux/fs.h       | 19 +++++--------------
 include/linux/fsnotify.h |  3 +--
 6 files changed, 38 insertions(+), 40 deletions(-)
diff --git a/fs/file_table.c b/fs/file_table.c
index 08fd1dd6d863..fa92743ba6a9 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -44,10 +44,10 @@ static struct kmem_cache *filp_cachep __read_mostly;
 
 static struct percpu_counter nr_files __cacheline_aligned_in_smp;
 
-/* Container for backing file with optional real path */
+/* Container for backing file with optional user path */
 struct backing_file {
 	struct file file;
-	struct path real_path;
+	struct path user_path;
 };
 
 static inline struct backing_file *backing_file(struct file *f)
@@ -55,11 +55,11 @@ static inline struct backing_file *backing_file(struct file *f)
 	return container_of(f, struct backing_file, file);
 }
 
-struct path *backing_file_real_path(struct file *f)
+struct path *backing_file_user_path(struct file *f)
 {
-	return &backing_file(f)->real_path;
+	return &backing_file(f)->user_path;
 }
-EXPORT_SYMBOL_GPL(backing_file_real_path);
+EXPORT_SYMBOL_GPL(backing_file_user_path);
 
 static inline void file_free(struct file *f)
 {
@@ -68,7 +68,7 @@ static inline void file_free(struct file *f)
 		percpu_counter_dec(&nr_files);
 	put_cred(f->f_cred);
 	if (unlikely(f->f_mode & FMODE_BACKING)) {
-		path_put(backing_file_real_path(f));
+		path_put(backing_file_user_path(f));
 		kfree(backing_file(f));
 	} else {
 		kmem_cache_free(filp_cachep, f);
diff --git a/fs/internal.h b/fs/internal.h
index 846d5133dd9c..652a1703668e 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -101,10 +101,10 @@ static inline void file_put_write_access(struct file *file)
 	put_write_access(file->f_inode);
 	mnt_put_write_access(file->f_path.mnt);
 	if (unlikely(file->f_mode & FMODE_BACKING)) {
-		struct path *real_path = backing_file_real_path(file);
+		struct path *user_path = backing_file_user_path(file);
 
-		if (real_path->mnt)
-			mnt_put_write_access(real_path->mnt);
+		if (user_path->mnt)
+			mnt_put_write_access(user_path->mnt);
 	}
 }
 
diff --git a/fs/open.c b/fs/open.c
index 2f3e28512663..1bfedc314e49 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -881,10 +881,10 @@ static inline int file_get_write_access(struct file *f)
 	if (unlikely(error))
 		goto cleanup_inode;
 	if (unlikely(f->f_mode & FMODE_BACKING)) {
-		struct path *real_path = backing_file_real_path(f);
+		struct path *user_path = backing_file_user_path(f);
 
-		if (real_path->mnt)
-			error = mnt_get_write_access(real_path->mnt);
+		if (user_path->mnt)
+			error = mnt_get_write_access(user_path->mnt);
 		if (unlikely(error))
 			goto cleanup_mnt;
 	}
@@ -1185,20 +1185,19 @@ EXPORT_SYMBOL_GPL(kernel_file_open);
 
 /**
  * backing_file_open - open a backing file for kernel internal use
- * @path:	path of the file to open
+ * @user_path:	path that the user reuqested to open
  * @flags:	open flags
  * @real_path:	path of the backing file
  * @cred:	credentials for open
  *
  * Open a backing file for a stackable filesystem (e.g., overlayfs).
- * @path may be on the stackable filesystem and backing inode on the
- * underlying filesystem. In this case, we want to be able to return
- * the @real_path of the backing inode. This is done by embedding the
- * returned file into a container structure that also stores the path of
- * the backing inode on the underlying filesystem, which can be
- * retrieved using backing_file_real_path().
+ * @user_path may be on the stackable filesystem and @real_path on the
+ * underlying filesystem.  In this case, we want to be able to return the
+ * @user_path of the stackable filesystem. This is done by embedding the
+ * returned file into a container structure that also stores the stacked
+ * file's path, which can be retrieved using backing_file_user_path().
  */
-struct file *backing_file_open(const struct path *path, int flags,
+struct file *backing_file_open(const struct path *user_path, int flags,
 			       const struct path *real_path,
 			       const struct cred *cred)
 {
@@ -1209,9 +1208,9 @@ struct file *backing_file_open(const struct path *path, int flags,
 	if (IS_ERR(f))
 		return f;
 
-	f->f_path = *path;
-	path_get(real_path);
-	*backing_file_real_path(f) = *real_path;
+	path_get(user_path);
+	*backing_file_user_path(f) = *user_path;
+	f->f_path = *real_path;
 	error = do_dentry_open(f, d_inode(real_path->dentry), NULL);
 	if (error) {
 		fput(f);
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 3fa2416264a4..0245db1a3e29 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -34,9 +34,18 @@ static struct dentry *ovl_d_real(struct dentry *dentry,
 	struct dentry *real = NULL, *lower;
 	int err;
 
-	/* It's an overlay file */
+	/*
+	 * vfs is only expected to call d_real() with NULL from d_real_inode()
+	 * and with overlay inode from file_dentry() on an overlay file.
+	 *
+	 * TODO: remove @inode argument from d_real() API, remove code in this
+	 * function that deals with non-NULL @inode and remove d_real() call
+	 * from file_dentry().
+	 */
 	if (inode && d_inode(dentry) == inode)
 		return dentry;
+	else
+		WARN_ON_ONCE(inode);
 
 	if (!d_is_reg(dentry)) {
 		if (!inode || inode == d_inode(dentry))
diff --git a/include/linux/fs.h b/include/linux/fs.h
index a8e4e1cac48e..75073a9d0fab 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2451,24 +2451,13 @@ struct file *dentry_open(const struct path *path, int flags,
 			 const struct cred *creds);
 struct file *dentry_create(const struct path *path, int flags, umode_t mode,
 			   const struct cred *cred);
-struct file *backing_file_open(const struct path *path, int flags,
+struct file *backing_file_open(const struct path *user_path, int flags,
 			       const struct path *real_path,
 			       const struct cred *cred);
-struct path *backing_file_real_path(struct file *f);
+struct path *backing_file_user_path(struct file *f);
 
-/*
- * file_real_path - get the path corresponding to f_inode
- *
- * When opening a backing file for a stackable filesystem (e.g.,
- * overlayfs) f_path may be on the stackable filesystem and f_inode on
- * the underlying filesystem.  When the path associated with f_inode is
- * needed, this helper should be used instead of accessing f_path
- * directly.
-*/
-static inline const struct path *file_real_path(struct file *f)
+static inline const struct path *f_path(struct file *f)
 {
-	if (unlikely(f->f_mode & FMODE_BACKING))
-		return backing_file_real_path(f);
 	return &f->f_path;
 }
 
@@ -2483,6 +2472,8 @@ static inline const struct path *file_real_path(struct file *f)
  */
 static inline const struct path *file_user_path(struct file *f)
 {
+	if (unlikely(f->f_mode & FMODE_BACKING))
+		return backing_file_user_path(f);
 	return &f->f_path;
 }
 
diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h
index ed48e4f1e755..001307e12a49 100644
--- a/include/linux/fsnotify.h
+++ b/include/linux/fsnotify.h
@@ -96,8 +96,7 @@ static inline int fsnotify_file(struct file *file, __u32 mask)
 	if (file->f_mode & FMODE_NONOTIFY)
 		return 0;
 
-	/* Overlayfs internal files have fake f_path */
-	path = file_real_path(file);
+	path = f_path(file);
 	return fsnotify_parent(path->dentry, mask, path, FSNOTIFY_EVENT_PATH);
 }
 
-- 
2.34.1
^ permalink raw reply related	[flat|nested] 11+ messages in thread
- * Re: [PATCH v2 3/3] fs: store real path instead of fake path in backing file f_path
  2023-10-07  8:44 ` [PATCH v2 3/3] fs: store real path instead of fake path in backing file f_path Amir Goldstein
@ 2023-10-09  7:48   ` Al Viro
  2023-10-09  8:25     ` Amir Goldstein
  0 siblings, 1 reply; 11+ messages in thread
From: Al Viro @ 2023-10-09  7:48 UTC (permalink / raw)
  To: Amir Goldstein
  Cc: Christian Brauner, Miklos Szeredi, Paul Moore, James Morris,
	Serge E . Hallyn, Mimi Zohar, linux-security-module,
	linux-integrity, linux-unionfs, linux-fsdevel
On Sat, Oct 07, 2023 at 11:44:33AM +0300, Amir Goldstein wrote:
> -		if (real_path->mnt)
> -			mnt_put_write_access(real_path->mnt);
> +		if (user_path->mnt)
> +			mnt_put_write_access(user_path->mnt);
>  	}
>  }
Again, how can the predicates be ever false here?  We should *not*
have struct path with NULL .mnt unless it's {NULL, NULL} pair.
For the record, struct path with NULL .dentry and non-NULL .mnt
*IS* possible, but only in a very narrow area - if, during
an attempt to fall back from rcu pathwalk to normal one we
have __legitimize_path() successfully validate (== grab) the
reference to mount, but fail to validate dentry.  In that
case we need to drop mount, but not dentry when we get to
cleanup (pretty much as soon as we drop rcu_read_lock()).
That gets indicated by clearing path->dentry, and only
while we are propagating the error back to the point where
references would be dropped.  No filesystem code should
ever see struct path instances in that state.
Please, don't make the things more confusing; "incomplete"
struct path like that are very much not normal (and this
variety is flat-out impossible).
> @@ -34,9 +34,18 @@ static struct dentry *ovl_d_real(struct dentry *dentry,
>  	struct dentry *real = NULL, *lower;
>  	int err;
>  
> -	/* It's an overlay file */
> +	/*
> +	 * vfs is only expected to call d_real() with NULL from d_real_inode()
> +	 * and with overlay inode from file_dentry() on an overlay file.
> +	 *
> +	 * TODO: remove @inode argument from d_real() API, remove code in this
> +	 * function that deals with non-NULL @inode and remove d_real() call
> +	 * from file_dentry().
> +	 */
>  	if (inode && d_inode(dentry) == inode)
>  		return dentry;
> +	else
> +		WARN_ON_ONCE(inode);
>  
>  	if (!d_is_reg(dentry)) {
>  		if (!inode || inode == d_inode(dentry))
		   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
	BTW, that condition is confusing as hell (both before and
after this patch).  AFAICS, it's a pointlessly obfuscated
		if (!inode)
Look: we get to evaluating that test only if we hadn't buggered
off on
 	if (inode && d_inode(dentry) == inode)
 		return dentry;
above.  Which means that either inode is NULL (in which case the
evaluation yields true as soon as we see that !inode is true) or
it's neither NULL nor equal to d_inode(dentry).  In which case
we see that !inode is false and proceed yield false *after*
comparing inode with d_inode(dentry) and seeing that they
are not equal.
<checks history>
e8c985bace13 "ovl: deal with overlay files in ovl_d_real()"
had introduced the first check, and nobody noticed that the
older check below could've been simplified.  Oh, well...
> -static inline const struct path *file_real_path(struct file *f)
> +static inline const struct path *f_path(struct file *f)
>  {
> -	if (unlikely(f->f_mode & FMODE_BACKING))
> -		return backing_file_real_path(f);
>  	return &f->f_path;
>  }
Bad name, IMO - makes grepping harder and... what the hell do
we need it for, anyway?  You have only one caller, and no
obvious reason why it would be worse off as path = &file->f_path...
^ permalink raw reply	[flat|nested] 11+ messages in thread
- * Re: [PATCH v2 3/3] fs: store real path instead of fake path in backing file f_path
  2023-10-09  7:48   ` Al Viro
@ 2023-10-09  8:25     ` Amir Goldstein
  2023-10-09 18:53       ` Al Viro
  0 siblings, 1 reply; 11+ messages in thread
From: Amir Goldstein @ 2023-10-09  8:25 UTC (permalink / raw)
  To: Al Viro
  Cc: Christian Brauner, Miklos Szeredi, Paul Moore, James Morris,
	Serge E . Hallyn, Mimi Zohar, linux-security-module,
	linux-integrity, linux-unionfs, linux-fsdevel
On Mon, Oct 9, 2023 at 10:48 AM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> On Sat, Oct 07, 2023 at 11:44:33AM +0300, Amir Goldstein wrote:
>
> > -             if (real_path->mnt)
> > -                     mnt_put_write_access(real_path->mnt);
> > +             if (user_path->mnt)
> > +                     mnt_put_write_access(user_path->mnt);
> >       }
> >  }
>
> Again, how can the predicates be ever false here?  We should *not*
> have struct path with NULL .mnt unless it's {NULL, NULL} pair.
>
> For the record, struct path with NULL .dentry and non-NULL .mnt
> *IS* possible, but only in a very narrow area - if, during
> an attempt to fall back from rcu pathwalk to normal one we
> have __legitimize_path() successfully validate (== grab) the
> reference to mount, but fail to validate dentry.  In that
> case we need to drop mount, but not dentry when we get to
> cleanup (pretty much as soon as we drop rcu_read_lock()).
> That gets indicated by clearing path->dentry, and only
> while we are propagating the error back to the point where
> references would be dropped.  No filesystem code should
> ever see struct path instances in that state.
>
> Please, don't make the things more confusing; "incomplete"
> struct path like that are very much not normal (and this
> variety is flat-out impossible).
>
>
No problem.
I will remove the conditional.
> > @@ -34,9 +34,18 @@ static struct dentry *ovl_d_real(struct dentry *dentry,
> >       struct dentry *real = NULL, *lower;
> >       int err;
> >
> > -     /* It's an overlay file */
> > +     /*
> > +      * vfs is only expected to call d_real() with NULL from d_real_inode()
> > +      * and with overlay inode from file_dentry() on an overlay file.
> > +      *
> > +      * TODO: remove @inode argument from d_real() API, remove code in this
> > +      * function that deals with non-NULL @inode and remove d_real() call
> > +      * from file_dentry().
> > +      */
> >       if (inode && d_inode(dentry) == inode)
> >               return dentry;
> > +     else
> > +             WARN_ON_ONCE(inode);
> >
> >       if (!d_is_reg(dentry)) {
> >               if (!inode || inode == d_inode(dentry))
>                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>         BTW, that condition is confusing as hell (both before and
> after this patch).  AFAICS, it's a pointlessly obfuscated
>                 if (!inode)
> Look: we get to evaluating that test only if we hadn't buggered
> off on
>         if (inode && d_inode(dentry) == inode)
>                 return dentry;
> above.  Which means that either inode is NULL (in which case the
> evaluation yields true as soon as we see that !inode is true) or
> it's neither NULL nor equal to d_inode(dentry).  In which case
> we see that !inode is false and proceed yield false *after*
> comparing inode with d_inode(dentry) and seeing that they
> are not equal.
>
> <checks history>
> e8c985bace13 "ovl: deal with overlay files in ovl_d_real()"
> had introduced the first check, and nobody noticed that the
> older check below could've been simplified.  Oh, well...
>
Absolutely right.
I can remove the pointless condition.
FWIW, the next step after dust from this patch set settles
is to make file_dentry(f) := ((f)->f_path.dentry) and remove
the non-NULL inode case from ->d_real() interface altogether,
so this confusing check was going to go away soon anyway.
> > -static inline const struct path *file_real_path(struct file *f)
> > +static inline const struct path *f_path(struct file *f)
> >  {
> > -     if (unlikely(f->f_mode & FMODE_BACKING))
> > -             return backing_file_real_path(f);
> >       return &f->f_path;
> >  }
>
> Bad name, IMO - makes grepping harder and... what the hell do
> we need it for, anyway?  You have only one caller, and no
> obvious reason why it would be worse off as path = &file->f_path...
It's not important. I don't mind dropping it.
If you dislike that name f_path(), I guess you are not a fan of
d_inode() either...
FYI, I wanted to do a file_path() accessor to be consistent with
file_inode() and file_dentry(), alas file_path() is used for something
completely different.
I find it confusing that {file,dentry,d}_path() do not return a path
but a path string, but whatever.
Thanks,
Amir.
^ permalink raw reply	[flat|nested] 11+ messages in thread
- * Re: [PATCH v2 3/3] fs: store real path instead of fake path in backing file f_path
  2023-10-09  8:25     ` Amir Goldstein
@ 2023-10-09 18:53       ` Al Viro
  0 siblings, 0 replies; 11+ messages in thread
From: Al Viro @ 2023-10-09 18:53 UTC (permalink / raw)
  To: Amir Goldstein
  Cc: Christian Brauner, Miklos Szeredi, Paul Moore, James Morris,
	Serge E . Hallyn, Mimi Zohar, linux-security-module,
	linux-integrity, linux-unionfs, linux-fsdevel
On Mon, Oct 09, 2023 at 11:25:38AM +0300, Amir Goldstein wrote:
> It's not important. I don't mind dropping it.
> 
> If you dislike that name f_path(), I guess you are not a fan of
> d_inode() either...
In case of d_inode() there's an opposition between d_inode() and
d_inode_rcu(), and that bears useful information.  In case of
f_path()...
> FYI, I wanted to do a file_path() accessor to be consistent with
> file_inode() and file_dentry(), alas file_path() is used for something
> completely different.
> 
> I find it confusing that {file,dentry,d}_path() do not return a path
> but a path string, but whatever.
*blink*
How would one possibly produce struct path (i.e. mount/dentry pair)
out of dentry?
Anyway, I admit that struct path hadn't been a great name to start with;
it's basically "location in namespace", and it clashes with the use of
the same word for "string interpreted to get a location in namespace".
Originally it's been just in the pathname resolution internals; TBH,
I don't remember I had specific plans regarding converting such
pairs (a plenty of those in the tree at the time) back then.
<checks the historical tree>
<checks old mail archives>
Probably hopeless to reconstruct the details, I'm afraid - everything
else aside, the timestamps in that patch are in the first half of
the day on Apr 29 2002; (hopefully) tested and sent out at about
6pm.  Followed by sitting down for birthday celebration, of all things,
so the details of rationale for name choice would probably be hard
to recover on the next morning, nevermind 21 years later ;-)
Bringing it out of fs/namei.c (into include/linux/namei.h) had happened
in late 2006 (by Jeff Sipek) and after that it was too late to change
the name; I'm still not sure what name would be good for that, TBH...
^ permalink raw reply	[flat|nested] 11+ messages in thread