Skip to contentSkip to conversation

bpf: adding get_file_path helper

PATCH Superseded Wenbo Zhang 7 participants 52 messages Nov 19, 2019 – Jun 2, 2020

Subsystems (9)
lore
Share & tools
lore
#0 lore raw

v10 cover bpf: adding get_file_path helper

This patch series introduce a bpf helper that can be used to map a file descriptor to a pathname.

This requirement is mainly discussed here:

https://github.com/iovisor/bcc/issues/237

This implementation supports both local and mountable pseudo file systems, and ensure we're in user context which is safe for this helper to run.

Changes since v9:

* Associate help patch with its selftests patch to this series

* Refactor selftests code for further simplification

Changes since v8:

* format helper description

Changes since v7:

* Use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/

* Ensure we're in user context which is safe fot the help to run

* Filter unmountable pseudo filesystem, because they don't have real path

* Supplement the description of this helper function

Changes since v6:

* Fix missing signed-off-by line

Changes since v5:

* Refactor helper avoid unnecessary goto end by having two explicit returns

Changes since v4:

* Rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names

* When fdget_raw fails, set ret to -EBADF instead of -EINVAL

* Remove fdput from fdget_raw's error path

* Use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long

* Modify the normal path's return value to return copied string length including NUL

* Update helper description's Return bits.

* Refactor selftests code for further simplification

Changes since v3:

* Remove unnecessary LOCKDOWN_BPF_READ

* Refactor error handling section for enhanced readability

* Provide a test case in tools/testing/selftests/bpf

* Refactor sefltests code to use real global variables instead of maps

Changes since v2:

* Fix backward compatibility

* Add helper description

* Refactor selftests use global data instead of perf_buffer to simplified code

* Fix signed-off name

Wenbo Zhang (2):
bpf: add new helper get_file_path for mapping a file descriptor to a
pathname
selftests/bpf: test for bpf_get_file_path() from tracepoint

 include/uapi/linux/bpf.h                      |  29 ++-
 kernel/trace/bpf_trace.c                      |  63 +++++++
 tools/include/uapi/linux/bpf.h                |  29 ++-
 .../selftests/bpf/prog_tests/get_file_path.c  | 171 ++++++++++++++++++
 .../selftests/bpf/progs/test_get_file_path.c  |  43 +++++
 5 files changed, 333 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/get_file_path.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_get_file_path.c
Signature · 1 line
2.17.1
#1 lore raw

PATCH v10 1/2 bpf: add new helper get_file_path for mapping a file descriptor to a pathname

When people want to identify which file system files are being opened, read, and written to, they can use this helper with file descriptor as input to achieve this goal. Other pseudo filesystems are also supported.

This requirement is mainly discussed here:

https://github.com/iovisor/bcc/issues/237

v9->v10: addressed Andrii's feedback
- send this patch together with the patch selftests as one patch series

v8->v9:
- format helper description

v7->v8: addressed Alexei's feedback
- use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/
- ensure we're in user context which is safe fot the help to run
- filter unmountable pseudo filesystem, because they don't have real path
- supplement the description of this helper function

v6->v7:
- fix missing signed-off-by line

v5->v6: addressed Andrii's feedback
- avoid unnecessary goto end by having two explicit returns

v4->v5: addressed Andrii and Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names
- when fdget_raw fails, set ret to -EBADF instead of -EINVAL
- remove fdput from fdget_raw's error path
- use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long
- modify the normal path's return value to return copied string length including NUL
- update this helper description's Return bits.

v3->v4: addressed Daniel's feedback
- fix missing fdput()
- move fd2path from kernel/bpf/trace.c to kernel/trace/bpf_trace.c
- move fd2path's test code to another patch
- add comment to explain why use fdget_raw instead of fdget

v2->v3: addressed Yonghong's feedback
- remove unnecessary LOCKDOWN_BPF_READ
- refactor error handling section for enhanced readability
- provide a test case in tools/testing/selftests/bpf

v1->v2: addressed Daniel's feedback
- fix backward compatibility
- add this helper description
- fix signed-off name

---
 include/uapi/linux/bpf.h       | 29 +++++++++++++++-
 kernel/trace/bpf_trace.c       | 63 ++++++++++++++++++++++++++++++++++
 tools/include/uapi/linux/bpf.h | 29 +++++++++++++++-
 3 files changed, 119 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..71d9705df120 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
  * 	Return
  * 		On success, the strictly positive length of the string,	including
  * 		the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_file_path(char *path, u32 size, int fd)
+ *	Description
+ *		Get **file** atrribute from the current task by *fd*, then call
+ *		**d_path** to get it's absolute path and copy it as string into
+ *		*path* of *size*. Notice the **path** don't support unmountable
+ *		pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *		The *size* must be strictly positive. On success, the helper
+ *		makes sure that the *path* is NUL-terminated, and the buffer
+ *		could be:
+ *		- a regular full path (include mountable fs eg: /proc, /sys)
+ *		- a regular full path with "(deleted)" at the end.
+ *		On failure, it is filled with zeroes.
+ *	Return
+ *		On success, returns the length of the copied string INCLUDING
+ *		the trailing NUL.
+ *
+ *		On failure, the returned value is one of the following:
+ *
+ *		**-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *		**-EBADF** if *fd* is invalid.
+ *
+ *		**-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *		**-ENAMETOOLONG** if full path is longer than *size*
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -2938,7 +2964,8 @@ union bpf_attr {
 	FN(probe_read_user),		\
 	FN(probe_read_kernel),		\
 	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_file_path),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
kernel/trace/bpf_trace.c +63 -0

75 lines not on this page · raw message

tools/include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..71d9705df120 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
  * 	Return
  * 		On success, the strictly positive length of the string,	including
  * 		the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_file_path(char *path, u32 size, int fd)
+ *	Description
+ *		Get **file** atrribute from the current task by *fd*, then call
+ *		**d_path** to get it's absolute path and copy it as string into
+ *		*path* of *size*. Notice the **path** don't support unmountable
+ *		pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *		The *size* must be strictly positive. On success, the helper
+ *		makes sure that the *path* is NUL-terminated, and the buffer
+ *		could be:
+ *		- a regular full path (include mountable fs eg: /proc, /sys)
+ *		- a regular full path with "(deleted)" at the end.
+ *		On failure, it is filled with zeroes.
+ *	Return
+ *		On success, returns the length of the copied string INCLUDING
+ *		the trailing NUL.
+ *
+ *		On failure, the returned value is one of the following:
+ *
+ *		**-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *		**-EBADF** if *fd* is invalid.
+ *
+ *		**-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *		**-ENAMETOOLONG** if full path is longer than *size*
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -2938,7 +2964,8 @@ union bpf_attr {
 	FN(probe_read_user),		\
 	FN(probe_read_kernel),		\
 	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_file_path),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
Signature · 1 line
2.17.1
#2 lore raw

Re: [PATCH bpf-next v10 1/2] bpf: add new helper get_file_path for mapping a file descriptor to a pathname

Quoted 54 lines · Wenbo Zhang

When people want to identify which file system files are being opened, read, and written to, they can use this helper with file descriptor as input to achieve this goal. Other pseudo filesystems are also supported.

This requirement is mainly discussed here:

https://github.com/iovisor/bcc/issues/237

v9->v10: addressed Andrii's feedback
- send this patch together with the patch selftests as one patch series

v8->v9:
- format helper description

v7->v8: addressed Alexei's feedback
- use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/
- ensure we're in user context which is safe fot the help to run
- filter unmountable pseudo filesystem, because they don't have real path
- supplement the description of this helper function

v6->v7:
- fix missing signed-off-by line

v5->v6: addressed Andrii's feedback
- avoid unnecessary goto end by having two explicit returns

v4->v5: addressed Andrii and Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names
- when fdget_raw fails, set ret to -EBADF instead of -EINVAL
- remove fdput from fdget_raw's error path
- use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long
- modify the normal path's return value to return copied string length including NUL
- update this helper description's Return bits.

v3->v4: addressed Daniel's feedback
- fix missing fdput()
- move fd2path from kernel/bpf/trace.c to kernel/trace/bpf_trace.c
- move fd2path's test code to another patch
- add comment to explain why use fdget_raw instead of fdget

v2->v3: addressed Yonghong's feedback
- remove unnecessary LOCKDOWN_BPF_READ
- refactor error handling section for enhanced readability
- provide a test case in tools/testing/selftests/bpf

v1->v2: addressed Daniel's feedback
- fix backward compatibility
- add this helper description
- fix signed-off name

...

Quoted 41 lines
kernel/trace/bpf_trace.c +41 -0
+BPF_CALL_3(bpf_get_file_path, char *, dst, u32, size, int, fd)
+{
+	struct file *f;
+	char *p;
+	int ret = -EBADF;
+
+	/* Ensure we're in user context which is safe for the helper to
+	 * run. This helper has no business in a kthread.
+	 */
+	if (unlikely(in_interrupt() ||
+		     current->flags & (PF_KTHREAD | PF_EXITING)))
+		return -EPERM;
+
+	/* Use fget_raw instead of fget to support O_PATH, and it doesn't
+	 * have any sleepable code, so it's ok to be here.
+	 */
+	f = fget_raw(fd);
+	if (!f)
+		goto error;
+
+	/* For unmountable pseudo filesystem, it seems to have no meaning
+	 * to get their fake paths as they don't have path, and to be no
+	 * way to validate this function pointer can be always safe to call
+	 * in the current context.
+	 */
+	if (f->f_path.dentry->d_op && f->f_path.dentry->d_op->d_dname)
+		return -EINVAL;
+
+	/* After filter unmountable pseudo filesytem, d_path won't call
+	 * dentry->d_op->d_name(), the normally path doesn't have any
+	 * sleepable code, and despite it uses the current macro to get
+	 * fs_struct (current->fs), we've already ensured we're in user
+	 * context, so it's ok to be here.
+	 */
+	p = d_path(&f->f_path, dst, size);
+	if (IS_ERR(p)) {
+		ret = PTR_ERR(p);
+		fput(f);
+		goto error;
+	}
+

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_send_signal_proto = { · line 815

Quoted 10 lines
kernel/trace/bpf_trace.c +10 -0
+	ret = strlen(p);
+	memmove(dst, p, ret);
+	dst[ret++] = '\0';
+	fput(f);
+	return ret;
+
+error:
+	memset(dst, '0', size);
+	return ret;
+}

Al,

could you please review about code whether it's doing enough checks to be called safely from preempt_disabled region?

It's been under review for many weeks and looks good from bpf pov. Essentially tracing folks need easy way to convert FD to full path name. This feature request first came in 2015.

Thanks!

maintainer
#3 lore raw

Re: [PATCH bpf-next v10 1/2] bpf: add new helper get_file_path for mapping a file descriptor to a pathname

Quoted 26 lines · Alexei Starovoitov
Quoted 13 lines
kernel/trace/bpf_trace.c +13 -0
+	/* After filter unmountable pseudo filesytem, d_path won't call
+	 * dentry->d_op->d_name(), the normally path doesn't have any
+	 * sleepable code, and despite it uses the current macro to get
+	 * fs_struct (current->fs), we've already ensured we're in user
+	 * context, so it's ok to be here.
+	 */
+	p = d_path(&f->f_path, dst, size);
+	if (IS_ERR(p)) {
+		ret = PTR_ERR(p);
+		fput(f);
+		goto error;
+	}
+

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_send_signal_proto = { · line 815

Quoted 10 lines
kernel/trace/bpf_trace.c +10 -0
+	ret = strlen(p);
+	memmove(dst, p, ret);
+	dst[ret++] = '\0';
+	fput(f);
+	return ret;
+
+error:
+	memset(dst, '0', size);
+	return ret;
+}

Al,

Quoted 2 lines

could you please review about code whether it's doing enough checks to be called safely from preempt_disabled region?

Depends. Which context is it running in? In particular, which locks might be already held?

maintainer
#4 lore raw

Re: [PATCH bpf-next v10 1/2] bpf: add new helper get_file_path for mapping a file descriptor to a pathname

In reply to Alexei Starovoitov
Quoted 11 lines · Alexei Starovoitov

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_send_signal_proto = { · line 791

Quoted 11 lines
kernel/trace/bpf_trace.c +11 -0
+	f = fget_raw(fd);
+	if (!f)
+		goto error;
+
+	/* For unmountable pseudo filesystem, it seems to have no meaning
+	 * to get their fake paths as they don't have path, and to be no
+	 * way to validate this function pointer can be always safe to call
+	 * in the current context.
+	 */
+	if (f->f_path.dentry->d_op && f->f_path.dentry->d_op->d_dname)
+		return -EINVAL;

An obvious leak here, BTW.

Anyway, what could that be used for? I mean, if you want to check something about syscall arguments, that's an unfixably racy way to go. Descriptor table can be a shared data structure, and two consequent fdget() on the same number can bloody well yield completely unrelated struct file references.

IOW, anything that does descriptor -> struct file * translation more than once is an instant TOCTOU suspect. In this particular case, the function will produce a pathname of something that was once reachable via descriptor with this number; quite possibly never before that function had been called _and_ not once after it has returned.

#5 lore raw

Re: [PATCH bpf-next v10 1/2] bpf: add new helper get_file_path for mapping a file descriptor to a pathname

Quoted 14 lines · Al Viro
Quoted 11 lines · Alexei Starovoitov

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_send_signal_proto = { · line 791

Quoted 11 lines
kernel/trace/bpf_trace.c +11 -0
+	f = fget_raw(fd);
+	if (!f)
+		goto error;
+
+	/* For unmountable pseudo filesystem, it seems to have no meaning
+	 * to get their fake paths as they don't have path, and to be no
+	 * way to validate this function pointer can be always safe to call
+	 * in the current context.
+	 */
+	if (f->f_path.dentry->d_op && f->f_path.dentry->d_op->d_dname)
+		return -EINVAL;

An obvious leak here, BTW.

ohh. right.

Quoted 2 lines

Depends. Which context is it running in? In particular, which locks might be already held?

hard to tell. It will be run out of bpf prog that attaches to kprobe or tracepoint. What is the concern about locking?
d_path() doesn't take any locks and doesn't depend on any locks. Above 'if' checks that plain d_path() is used and not some specilized callback with unknown logic.

Quoted 5 lines

Anyway, what could that be used for? I mean, if you want to check something about syscall arguments, that's an unfixably racy way to go. Descriptor table can be a shared data structure, and two consequent fdget() on the same number can bloody well yield completely unrelated struct file references.

yes. It is racy. There are no guarantees on correctness of FD. The program can pass arbitrary integer into this helper.

Quoted 5 lines

IOW, anything that does descriptor -> struct file * translation more than once is an instant TOCTOU suspect. In this particular case, the function will produce a pathname of something that was once reachable via descriptor with this number; quite possibly never before that function had been called _and_ not once after it has returned.

Right. TOCTOU is not a concern here. It's tracing. It's ok for full path to be 'one time deal'. Right now people use bpf_probe_read() to replicate what d_path() does.
See https://github.com/iovisor/bcc/issues/237#issuecomment-547564661
It sort of works, but calling d_path() is simpler and more accurate. The key thing that bpf helpers need to make sure is that regardless of how they're called and what integer is passed in as an FD the helper must not crash or lockup the kernel or cause it to misbehave. Hence above in_interrupt() and other checks to limit the context.

maintainer
#6 lore raw

Re: [PATCH bpf-next v10 1/2] bpf: add new helper get_file_path for mapping a file descriptor to a pathname

Quoted 5 lines · Alexei Starovoitov

hard to tell. It will be run out of bpf prog that attaches to kprobe or tracepoint. What is the concern about locking?
d_path() doesn't take any locks and doesn't depend on any locks. Above 'if' checks that plain d_path() is used and not some specilized callback with unknown logic.

It sure as hell does. It might end up taking rename_lock and/or mount_lock spinlock components. It'll try not to, but if the first pass ends up with seqlock mismatch, it will just grab the spinlock the second time around.

Quoted 5 lines
Quoted 2 lines

with this number; quite possibly never before that function had been called _and_ not once after it has returned.

Right. TOCTOU is not a concern here. It's tracing. It's ok for full path to be 'one time deal'.

It might very well be a full path of something completely unrelated to what the syscall ends up operating upon. It's not that the file might've been moved; it might be a different file. IOW, results of that tracing might be misleading.

#7 lore raw

Re: [PATCH bpf-next v10 1/2] bpf: add new helper get_file_path for mapping a file descriptor to a pathname

Quoted 11 lines · Al Viro
Quoted 5 lines · Alexei Starovoitov

hard to tell. It will be run out of bpf prog that attaches to kprobe or tracepoint. What is the concern about locking?
d_path() doesn't take any locks and doesn't depend on any locks. Above 'if' checks that plain d_path() is used and not some specilized callback with unknown logic.

It sure as hell does. It might end up taking rename_lock and/or mount_lock spinlock components. It'll try not to, but if the first pass ends up with seqlock mismatch, it will just grab the spinlock the second time around.

ohh. got it. I missed _or_lock() part in there.
The need_seqretry() logic is tricky. afaics there is no way for the checks outside of prepend_path() to prevent spin_lock to happen. And adding a flag to prepend_path() to return early if retry is needed is too ugly. So this helper won't be safe to be run out of kprobe. But if we allow it for tracepoints only it should be ok. I think. There are no tracepoints in inner guts of vfs and I don't think they will ever be. So running in tracepoint->bpf_prog->d_path we will be sure that rename_lock+mount_lock can be safely spinlocked. Am I missing something?

Quoted 10 lines
Quoted 5 lines
Quoted 2 lines

with this number; quite possibly never before that function had been called _and_ not once after it has returned.

Right. TOCTOU is not a concern here. It's tracing. It's ok for full path to be 'one time deal'.

It might very well be a full path of something completely unrelated to what the syscall ends up operating upon. It's not that the file might've been moved; it might be a different file. IOW, results of that tracing might be misleading.

That is correct. Tracing is fine with such limitation. Still better than probe_read.

#8 lore raw

Re: [PATCH bpf-next v10 1/2] bpf: add new helper get_file_path for mapping a file descriptor to a pathname

Quoted 14 lines · Alexei Starovoitov
Quoted 11 lines · Al Viro
Quoted 5 lines · Alexei Starovoitov

hard to tell. It will be run out of bpf prog that attaches to kprobe or tracepoint. What is the concern about locking?
d_path() doesn't take any locks and doesn't depend on any locks. Above 'if' checks that plain d_path() is used and not some specilized callback with unknown logic.

It sure as hell does. It might end up taking rename_lock and/or mount_lock spinlock components. It'll try not to, but if the first pass ends up with seqlock mismatch, it will just grab the spinlock the second time around.

Quoted 9 lines

ohh. got it. I missed _or_lock() part in there.
The need_seqretry() logic is tricky. afaics there is no way for the checks outside of prepend_path() to prevent spin_lock to happen. And adding a flag to prepend_path() to return early if retry is needed is too ugly. So this helper won't be safe to be run out of kprobe. But if we allow it for tracepoints only it should be ok. I think. There are no tracepoints in inner guts of vfs and I don't think they will ever be. So running in tracepoint->bpf_prog->d_path we will be sure that rename_lock+mount_lock can be safely spinlocked. Am I missing something?

It seems rather restrictive to only allow tracepoints (especially without VFS tracepoints), although I'll use it to improve my syscall tracepoint tools, so I'd be happy to see this merged even with that restriction.

Just a thought: if *buffer is in BPF memory, can prepend_path() check it's memory location and not try to grab the lock based on that? This would be to avoid adding a flag.

Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
Quoted 3 lines

with this number; quite possibly never before that function had been called _and_ not once after it has returned.

Right. TOCTOU is not a concern here. It's tracing. It's ok for full path to be 'one time deal'.

Quoted 7 lines
Quoted 4 lines

It might very well be a full path of something completely unrelated to what the syscall ends up operating upon. It's not that the file might've been moved; it might be a different file. IOW, results of that tracing might be misleading.

That is correct. Tracing is fine with such limitation. Still better than probe_read.

+1

Tracing is observability tools and we document these caveats, and this won't be the first time I've published tools where the printed path may not be the one you think (e.g., the case of hard links.)

Brendan

Signature · 1 line
Brendan Gregg, Senior Performance Architect, Netflix
#9 lore raw

v11 cover bpf: adding get_file_path helper

In reply to Wenbo Zhang

This patch series introduce a bpf helper that can be used to map a file descriptor to a pathname.

This requirement is mainly discussed here:

https://github.com/iovisor/bcc/issues/237

This implementation supports both local and mountable pseudo file systems, and ensure we're in user context which is safe for this helper to run.

Changes since v10:

* fix missing fput

Changes since v9:

* Associate help patch with its selftests patch to this series

* Refactor selftests code for further simplification

Changes since v8:

* format helper description

Changes since v7:

* Use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/

* Ensure we're in user context which is safe fot the help to run

* Filter unmountable pseudo filesystem, because they don't have real path

* Supplement the description of this helper function

Changes since v6:

* Fix missing signed-off-by line

Changes since v5:

* Refactor helper avoid unnecessary goto end by having two explicit returns

Changes since v4:

* Rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names

* When fdget_raw fails, set ret to -EBADF instead of -EINVAL

* Remove fdput from fdget_raw's error path

* Use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long

* Modify the normal path's return value to return copied string length including NUL

* Update helper description's Return bits.

* Refactor selftests code for further simplification

Changes since v3:

* Remove unnecessary LOCKDOWN_BPF_READ

* Refactor error handling section for enhanced readability

* Provide a test case in tools/testing/selftests/bpf

* Refactor sefltests code to use real global variables instead of maps

Changes since v2:

* Fix backward compatibility

* Add helper description

* Refactor selftests use global data instead of perf_buffer to simplified code

* Fix signed-off name

Wenbo Zhang (2):
bpf: add new helper get_file_path for mapping a file descriptor to a
pathname
selftests/bpf: test for bpf_get_file_path() from tracepoint

 include/uapi/linux/bpf.h                      |  29 ++-
 kernel/trace/bpf_trace.c                      |  68 +++++++
 tools/include/uapi/linux/bpf.h                |  29 ++-
 .../selftests/bpf/prog_tests/get_file_path.c  | 171 ++++++++++++++++++
 .../selftests/bpf/progs/test_get_file_path.c  |  43 +++++
 5 files changed, 338 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/get_file_path.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_get_file_path.c
Signature · 1 line
2.17.1
#10 lore raw

PATCH v11 1/2 bpf: add new helper get_file_path for mapping a file descriptor to a pathname

When people want to identify which file system files are being opened, read, and written to, they can use this helper with file descriptor as input to achieve this goal. Other pseudo filesystems are also supported.

This requirement is mainly discussed here:

https://github.com/iovisor/bcc/issues/237

v10->v11: addressed Al and Alexei's feedback
- fix missing fput()

v9->v10: addressed Andrii's feedback
- send this patch together with the patch selftests as one patch series

v8->v9:
- format helper description

v7->v8: addressed Alexei's feedback
- use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/
- ensure we're in user context which is safe fot the help to run
- filter unmountable pseudo filesystem, because they don't have real path
- supplement the description of this helper function

v6->v7:
- fix missing signed-off-by line

v5->v6: addressed Andrii's feedback
- avoid unnecessary goto end by having two explicit returns

v4->v5: addressed Andrii and Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names
- when fdget_raw fails, set ret to -EBADF instead of -EINVAL
- remove fdput from fdget_raw's error path
- use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long
- modify the normal path's return value to return copied string length including NUL
- update this helper description's Return bits.

v3->v4: addressed Daniel's feedback
- fix missing fdput()
- move fd2path from kernel/bpf/trace.c to kernel/trace/bpf_trace.c
- move fd2path's test code to another patch
- add comment to explain why use fdget_raw instead of fdget

v2->v3: addressed Yonghong's feedback
- remove unnecessary LOCKDOWN_BPF_READ
- refactor error handling section for enhanced readability
- provide a test case in tools/testing/selftests/bpf

v1->v2: addressed Daniel's feedback
- fix backward compatibility
- add this helper description
- fix signed-off name

---
 include/uapi/linux/bpf.h       | 29 ++++++++++++++-
 kernel/trace/bpf_trace.c       | 68 ++++++++++++++++++++++++++++++++++
 tools/include/uapi/linux/bpf.h | 29 ++++++++++++++-
 3 files changed, 124 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..71d9705df120 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
  * 	Return
  * 		On success, the strictly positive length of the string,	including
  * 		the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_file_path(char *path, u32 size, int fd)
+ *	Description
+ *		Get **file** atrribute from the current task by *fd*, then call
+ *		**d_path** to get it's absolute path and copy it as string into
+ *		*path* of *size*. Notice the **path** don't support unmountable
+ *		pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *		The *size* must be strictly positive. On success, the helper
+ *		makes sure that the *path* is NUL-terminated, and the buffer
+ *		could be:
+ *		- a regular full path (include mountable fs eg: /proc, /sys)
+ *		- a regular full path with "(deleted)" at the end.
+ *		On failure, it is filled with zeroes.
+ *	Return
+ *		On success, returns the length of the copied string INCLUDING
+ *		the trailing NUL.
+ *
+ *		On failure, the returned value is one of the following:
+ *
+ *		**-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *		**-EBADF** if *fd* is invalid.
+ *
+ *		**-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *		**-ENAMETOOLONG** if full path is longer than *size*
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -2938,7 +2964,8 @@ union bpf_attr {
 	FN(probe_read_user),		\
 	FN(probe_read_kernel),		\
 	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_file_path),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
kernel/trace/bpf_trace.c +68 -0

80 lines not on this page · raw message

tools/include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..71d9705df120 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
  * 	Return
  * 		On success, the strictly positive length of the string,	including
  * 		the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_file_path(char *path, u32 size, int fd)
+ *	Description
+ *		Get **file** atrribute from the current task by *fd*, then call
+ *		**d_path** to get it's absolute path and copy it as string into
+ *		*path* of *size*. Notice the **path** don't support unmountable
+ *		pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *		The *size* must be strictly positive. On success, the helper
+ *		makes sure that the *path* is NUL-terminated, and the buffer
+ *		could be:
+ *		- a regular full path (include mountable fs eg: /proc, /sys)
+ *		- a regular full path with "(deleted)" at the end.
+ *		On failure, it is filled with zeroes.
+ *	Return
+ *		On success, returns the length of the copied string INCLUDING
+ *		the trailing NUL.
+ *
+ *		On failure, the returned value is one of the following:
+ *
+ *		**-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *		**-EBADF** if *fd* is invalid.
+ *
+ *		**-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *		**-ENAMETOOLONG** if full path is longer than *size*
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -2938,7 +2964,8 @@ union bpf_attr {
 	FN(probe_read_user),		\
 	FN(probe_read_kernel),		\
 	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_file_path),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
Signature · 1 line
2.17.1
#11 lore raw

Re: [PATCH bpf-next v11 1/2] bpf: add new helper get_file_path for mapping a file descriptor to a pathname

Quoted 31 lines · Wenbo Zhang
kernel/trace/bpf_trace.c +30 -0
+BPF_CALL_3(bpf_get_file_path, char *, dst, u32, size, int, fd)
+{
+	struct file *f;
+	char *p;
+	int ret = -EBADF;
+
+	/* Ensure we're in user context which is safe for the helper to
+	 * run. This helper has no business in a kthread.
+	 */
+	if (unlikely(in_interrupt() ||
+		     current->flags & (PF_KTHREAD | PF_EXITING))) {
+		ret = -EPERM;
+		goto error;
+	}
+
+	/* Use fget_raw instead of fget to support O_PATH, and it doesn't
+	 * have any sleepable code, so it's ok to be here.
+	 */
+	f = fget_raw(fd);
+	if (!f)
+		goto error;
+
+	/* For unmountable pseudo filesystem, it seems to have no meaning
+	 * to get their fake paths as they don't have path, and to be no
+	 * way to validate this function pointer can be always safe to call
+	 * in the current context.
+	 */
+	if (f->f_path.dentry->d_op && f->f_path.dentry->d_op->d_dname) {
+		ret = -EINVAL;
+		fput(f);

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_send_signal_proto = { · line 804

Quoted 10 lines
kernel/trace/bpf_trace.c +10 -0
+		goto error;
+	}
+
+	/* After filter unmountable pseudo filesytem, d_path won't call
+	 * dentry->d_op->d_name(), the normally path doesn't have any
+	 * sleepable code, and despite it uses the current macro to get
+	 * fs_struct (current->fs), we've already ensured we're in user
+	 * context, so it's ok to be here.
+	 */
+	p = d_path(&f->f_path, dst, size);

Above 'if's are not enough to make sure that it won't dead lock. Allowing it in tracing_func_proto() means that it's available to kprobe too. Hence deadlock is possible. Please see previous email thread. This helper is safe in tracepoint+bpf only.

#12 lore raw

Re: [PATCH bpf-next v11 1/2] bpf: add new helper get_file_path for mapping a file descriptor to a pathname

Quoted 13 lines · Al Viro
Quoted 11 lines · Alexei Starovoitov
Quoted 6 lines

hard to tell. It will be run out of bpf prog that attaches to kprobe or tracepoint. What is the concern about locking?
d_path() doesn't take any locks and doesn't depend on any locks. Above 'if' checks that plain d_path() is used and not some specilized callback with unknown logic.

It sure as hell does. It might end up taking rename_lock and/or mount_lock spinlock components. It'll try not to, but if the first pass ends up with seqlock mismatch, it will just grab the spinlock the second time around.

Quoted 9 lines

ohh. got it. I missed _or_lock() part in there.
The need_seqretry() logic is tricky. afaics there is no way for the checks outside of prepend_path() to prevent spin_lock to happen. And adding a flag to prepend_path() to return early if retry is needed is too ugly. So this helper won't be safe to be run out of kprobe. But if we allow it for tracepoints only it should be ok. I think. There are no tracepoints in inner guts of vfs and I don't think they will ever be. So running in tracepoint->bpf_prog->d_path we will be sure that rename_lock+mount_lock can be safely spinlocked. Am I missing something?

Hi Alexei,

Would you please give me an example of a deadlock condition under kprobe+bpf? I'm not familiar with this detail and want to learn more.

Quoted 4 lines

Above 'if's are not enough to make sure that it won't dead lock. Allowing it in tracing_func_proto() means that it's available to kprobe too. Hence deadlock is possible. Please see previous email thread. This helper is safe in tracepoint+bpf only.

So I should move it to tp_prog_prog_func_proto and raw_tp_prog_func_prog right? Is raw tracepoint+bpf safe?

Thank you.

Quoted 49 lines · Alexei Starovoitov <[email protected]> 于2019年12月5日周四 下午3:19写道:
Quoted 31 lines · Wenbo Zhang
kernel/trace/bpf_trace.c +30 -0
+BPF_CALL_3(bpf_get_file_path, char *, dst, u32, size, int, fd)
+{
+     struct file *f;
+     char *p;
+     int ret = -EBADF;
+
+     /* Ensure we're in user context which is safe for the helper to
+      * run. This helper has no business in a kthread.
+      */
+     if (unlikely(in_interrupt() ||
+                  current->flags & (PF_KTHREAD | PF_EXITING))) {
+             ret = -EPERM;
+             goto error;
+     }
+
+     /* Use fget_raw instead of fget to support O_PATH, and it doesn't
+      * have any sleepable code, so it's ok to be here.
+      */
+     f = fget_raw(fd);
+     if (!f)
+             goto error;
+
+     /* For unmountable pseudo filesystem, it seems to have no meaning
+      * to get their fake paths as they don't have path, and to be no
+      * way to validate this function pointer can be always safe to call
+      * in the current context.
+      */
+     if (f->f_path.dentry->d_op && f->f_path.dentry->d_op->d_dname) {
+             ret = -EINVAL;
+             fput(f);

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_send_signal_proto = { · line 804

Quoted 10 lines
kernel/trace/bpf_trace.c +10 -0
+             goto error;
+     }
+
+     /* After filter unmountable pseudo filesytem, d_path won't call
+      * dentry->d_op->d_name(), the normally path doesn't have any
+      * sleepable code, and despite it uses the current macro to get
+      * fs_struct (current->fs), we've already ensured we're in user
+      * context, so it's ok to be here.
+      */
+     p = d_path(&f->f_path, dst, size);

Above 'if's are not enough to make sure that it won't dead lock. Allowing it in tracing_func_proto() means that it's available to kprobe too. Hence deadlock is possible. Please see previous email thread. This helper is safe in tracepoint+bpf only.

#13 lore raw

v12 cover bpf: adding get_file_path helper

In reply to Wenbo Zhang

This patch series introduce a bpf helper that can be used to map a file descriptor to a pathname.

This requirement is mainly discussed here:

https://github.com/iovisor/bcc/issues/237

This implementation supports both local and mountable pseudo file systems, and ensure we're in user context which is safe for this helper to run.

Changes since v11:

* only allow tracepoints to make sure it won't dead lock

Changes since v10:

* fix missing fput

Changes since v9:

* Associate help patch with its selftests patch to this series

* Refactor selftests code for further simplification

Changes since v8:

* format helper description

Changes since v7:

* Use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/

* Ensure we're in user context which is safe fot the help to run

* Filter unmountable pseudo filesystem, because they don't have real path

* Supplement the description of this helper function

Changes since v6:

* Fix missing signed-off-by line

Changes since v5:

* Refactor helper avoid unnecessary goto end by having two explicit returns

Changes since v4:

* Rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names

* When fdget_raw fails, set ret to -EBADF instead of -EINVAL

* Remove fdput from fdget_raw's error path

* Use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long

* Modify the normal path's return value to return copied string length including NUL

* Update helper description's Return bits.

* Refactor selftests code for further simplification

Changes since v3:

* Remove unnecessary LOCKDOWN_BPF_READ

* Refactor error handling section for enhanced readability

* Provide a test case in tools/testing/selftests/bpf

* Refactor sefltests code to use real global variables instead of maps

Changes since v2:

* Fix backward compatibility

* Add helper description

* Refactor selftests use global data instead of perf_buffer to simplified code

* Fix signed-off name

Wenbo Zhang (2):
bpf: add new helper get_file_path for mapping a file descriptor to a
pathname
selftests/bpf: test for bpf_get_file_path() from tracepoint

 include/uapi/linux/bpf.h                      |  29 ++-
 kernel/trace/bpf_trace.c                      |  70 +++++++
 tools/include/uapi/linux/bpf.h                |  29 ++-
 .../selftests/bpf/prog_tests/get_file_path.c  | 171 ++++++++++++++++++
 .../selftests/bpf/progs/test_get_file_path.c  |  43 +++++
 5 files changed, 340 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/get_file_path.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_get_file_path.c
Signature · 1 line
2.17.1
#14 lore raw

PATCH v12 1/2 bpf: add new helper get_file_path for mapping a file descriptor to a pathname

When people want to identify which file system files are being opened, read, and written to, they can use this helper with file descriptor as input to achieve this goal. Other pseudo filesystems are also supported.

This requirement is mainly discussed here:

https://github.com/iovisor/bcc/issues/237

v11->v12: addressed Alexei's feedback
- only allow tracepoints to make sure it won't dead lock

v10->v11: addressed Al and Alexei's feedback
- fix missing fput()

v9->v10: addressed Andrii's feedback
- send this patch together with the patch selftests as one patch series

v8->v9:
- format helper description

v7->v8: addressed Alexei's feedback
- use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/
- ensure we're in user context which is safe fot the help to run
- filter unmountable pseudo filesystem, because they don't have real path
- supplement the description of this helper function

v6->v7:
- fix missing signed-off-by line

v5->v6: addressed Andrii's feedback
- avoid unnecessary goto end by having two explicit returns

v4->v5: addressed Andrii and Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names
- when fdget_raw fails, set ret to -EBADF instead of -EINVAL
- remove fdput from fdget_raw's error path
- use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long
- modify the normal path's return value to return copied string length including NUL
- update this helper description's Return bits.

v3->v4: addressed Daniel's feedback
- fix missing fdput()
- move fd2path from kernel/bpf/trace.c to kernel/trace/bpf_trace.c
- move fd2path's test code to another patch
- add comment to explain why use fdget_raw instead of fdget

v2->v3: addressed Yonghong's feedback
- remove unnecessary LOCKDOWN_BPF_READ
- refactor error handling section for enhanced readability
- provide a test case in tools/testing/selftests/bpf

v1->v2: addressed Daniel's feedback
- fix backward compatibility
- add this helper description
- fix signed-off name

---
 include/uapi/linux/bpf.h       | 29 +++++++++++++-
 kernel/trace/bpf_trace.c       | 70 ++++++++++++++++++++++++++++++++++
 tools/include/uapi/linux/bpf.h | 29 +++++++++++++-
 3 files changed, 126 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..71d9705df120 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
  * 	Return
  * 		On success, the strictly positive length of the string,	including
  * 		the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_file_path(char *path, u32 size, int fd)
+ *	Description
+ *		Get **file** atrribute from the current task by *fd*, then call
+ *		**d_path** to get it's absolute path and copy it as string into
+ *		*path* of *size*. Notice the **path** don't support unmountable
+ *		pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *		The *size* must be strictly positive. On success, the helper
+ *		makes sure that the *path* is NUL-terminated, and the buffer
+ *		could be:
+ *		- a regular full path (include mountable fs eg: /proc, /sys)
+ *		- a regular full path with "(deleted)" at the end.
+ *		On failure, it is filled with zeroes.
+ *	Return
+ *		On success, returns the length of the copied string INCLUDING
+ *		the trailing NUL.
+ *
+ *		On failure, the returned value is one of the following:
+ *
+ *		**-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *		**-EBADF** if *fd* is invalid.
+ *
+ *		**-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *		**-ENAMETOOLONG** if full path is longer than *size*
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -2938,7 +2964,8 @@ union bpf_attr {
 	FN(probe_read_user),		\
 	FN(probe_read_kernel),		\
 	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_file_path),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
kernel/trace/bpf_trace.c +70 -0

88 lines not on this page · raw message

tools/include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..71d9705df120 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
  * 	Return
  * 		On success, the strictly positive length of the string,	including
  * 		the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_file_path(char *path, u32 size, int fd)
+ *	Description
+ *		Get **file** atrribute from the current task by *fd*, then call
+ *		**d_path** to get it's absolute path and copy it as string into
+ *		*path* of *size*. Notice the **path** don't support unmountable
+ *		pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *		The *size* must be strictly positive. On success, the helper
+ *		makes sure that the *path* is NUL-terminated, and the buffer
+ *		could be:
+ *		- a regular full path (include mountable fs eg: /proc, /sys)
+ *		- a regular full path with "(deleted)" at the end.
+ *		On failure, it is filled with zeroes.
+ *	Return
+ *		On success, returns the length of the copied string INCLUDING
+ *		the trailing NUL.
+ *
+ *		On failure, the returned value is one of the following:
+ *
+ *		**-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *		**-EBADF** if *fd* is invalid.
+ *
+ *		**-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *		**-ENAMETOOLONG** if full path is longer than *size*
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -2938,7 +2964,8 @@ union bpf_attr {
 	FN(probe_read_user),		\
 	FN(probe_read_kernel),		\
 	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_file_path),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
Signature · 1 line
2.17.1
#15 lore raw

Re: [PATCH bpf-next v12 1/2] bpf: add new helper get_file_path for mapping a file descriptor to a pathname

Quoted 76 lines · Wenbo Zhang

When people want to identify which file system files are being opened, read, and written to, they can use this helper with file descriptor as input to achieve this goal. Other pseudo filesystems are also supported.

This requirement is mainly discussed here:

   https://github.com/iovisor/bcc/issues/237

v11->v12: addressed Alexei's feedback
- only allow tracepoints to make sure it won't dead lock

v10->v11: addressed Al and Alexei's feedback
- fix missing fput()

v9->v10: addressed Andrii's feedback
- send this patch together with the patch selftests as one patch series

v8->v9:
- format helper description

v7->v8: addressed Alexei's feedback
- use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/
- ensure we're in user context which is safe fot the help to run
- filter unmountable pseudo filesystem, because they don't have real path
- supplement the description of this helper function

v6->v7:
- fix missing signed-off-by line

v5->v6: addressed Andrii's feedback
- avoid unnecessary goto end by having two explicit returns

v4->v5: addressed Andrii and Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names
- when fdget_raw fails, set ret to -EBADF instead of -EINVAL
- remove fdput from fdget_raw's error path
- use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long
- modify the normal path's return value to return copied string length including NUL
- update this helper description's Return bits.

v3->v4: addressed Daniel's feedback
- fix missing fdput()
- move fd2path from kernel/bpf/trace.c to kernel/trace/bpf_trace.c
- move fd2path's test code to another patch
- add comment to explain why use fdget_raw instead of fdget

v2->v3: addressed Yonghong's feedback
- remove unnecessary LOCKDOWN_BPF_READ
- refactor error handling section for enhanced readability
- provide a test case in tools/testing/selftests/bpf

v1->v2: addressed Daniel's feedback
- fix backward compatibility
- add this helper description
- fix signed-off name

---
  include/uapi/linux/bpf.h       | 29 +++++++++++++-
  kernel/trace/bpf_trace.c       | 70 ++++++++++++++++++++++++++++++++++
  tools/include/uapi/linux/bpf.h | 29 +++++++++++++-
  3 files changed, 126 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +2 -0
index dbbcf0b02970..71d9705df120 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
   * 	Return
   * 		On success, the strictly positive length of the string,	including
   * 		the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_file_path(char *path, u32 size, int fd)

include/uapi/linux/bpf.h union bpf_attr { · line 2835

Quoted 10 lines
include/uapi/linux/bpf.h +10 -0
+ *	Description
+ *		Get **file** atrribute from the current task by *fd*, then call
+ *		**d_path** to get it's absolute path and copy it as string into
+ *		*path* of *size*. Notice the **path** don't support unmountable
+ *		pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *		The *size* must be strictly positive. On success, the helper
+ *		makes sure that the *path* is NUL-terminated, and the buffer
+ *		could be:
+ *		- a regular full path (include mountable fs eg: /proc, /sys)
+ *		- a regular full path with "(deleted)" at the end.

Let us say with " (deleted)" is appended to be consistent with comments in d_path() and is more clear to user what the format will looks like.

include/uapi/linux/bpf.h union bpf_attr { · line 2839

Quoted 4 lines
include/uapi/linux/bpf.h +4 -0
+ *		On failure, it is filled with zeroes.
+ *	Return
+ *		On success, returns the length of the copied string INCLUDING
+ *		the trailing NUL.

trailing '\0'.

Quoted 70 lines
include/uapi/linux/bpf.h +12 -1
+ *
+ *		On failure, the returned value is one of the following:
+ *
+ *		**-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *		**-EBADF** if *fd* is invalid.
+ *
+ *		**-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *		**-ENAMETOOLONG** if full path is longer than *size*
   */
  #define __BPF_FUNC_MAPPER(FN)		\
  	FN(unspec),			\
@@ -2938,7 +2964,8 @@ union bpf_attr {
  	FN(probe_read_user),		\
  	FN(probe_read_kernel),		\
  	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_file_path),
 
  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
   * function eBPF program intends to call
kernel/trace/bpf_trace.c +39 -0
index e5ef4ae9edb5..db9c0ec46a5d 100644
@@ -762,6 +762,72 @@ static const struct bpf_func_proto bpf_send_signal_proto = {
  	.arg1_type	= ARG_ANYTHING,
  };
 
+BPF_CALL_3(bpf_get_file_path, char *, dst, u32, size, int, fd)
+{
+	struct file *f;
+	char *p;
+	int ret = -EBADF;
+
+	/* Ensure we're in user context which is safe for the helper to
+	 * run. This helper has no business in a kthread.
+	 */
+	if (unlikely(in_interrupt() ||
+		     current->flags & (PF_KTHREAD | PF_EXITING))) {
+		ret = -EPERM;
+		goto error;
+	}
+
+	/* Use fget_raw instead of fget to support O_PATH, and it doesn't
+	 * have any sleepable code, so it's ok to be here.
+	 */
+	f = fget_raw(fd);
+	if (!f)
+		goto error;
+
+	/* For unmountable pseudo filesystem, it seems to have no meaning
+	 * to get their fake paths as they don't have path, and to be no
+	 * way to validate this function pointer can be always safe to call
+	 * in the current context.
+	 */
+	if (f->f_path.dentry->d_op && f->f_path.dentry->d_op->d_dname) {
+		ret = -EINVAL;
+		fput(f);
+		goto error;
+	}
+
+	/* After filter unmountable pseudo filesytem, d_path won't call
+	 * dentry->d_op->d_name(), the normally path doesn't have any
+	 * sleepable code, and despite it uses the current macro to get
+	 * fs_struct (current->fs), we've already ensured we're in user
+	 * context, so it's ok to be here.
+	 */

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_send_signal_proto = { · line 813

Quoted 10 lines
kernel/trace/bpf_trace.c +10 -0
+	p = d_path(&f->f_path, dst, size);
+	if (IS_ERR(p)) {
+		ret = PTR_ERR(p);
+		fput(f);
+		goto error;
+	}
+
+	ret = strlen(p);
+	memmove(dst, p, ret);
+	dst[ret++] = '\0';

nit: you could do memmove(dst, p, ret + 1)?

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_send_signal_proto = { · line 815

Quoted 2 lines
kernel/trace/bpf_trace.c +2 -0
+	fput(f);
+	return ret;

The description says the return value length including trailing '\0'. The above 'ret' does not include trailing '\0'.

Quoted 46 lines
kernel/trace/bpf_trace.c +19 -0
+
+error:
+	memset(dst, '0', size);
+	return ret;
+}
+
+static const struct bpf_func_proto bpf_get_file_path_proto = {
+	.func       = bpf_get_file_path,
+	.gpl_only   = true,
+	.ret_type   = RET_INTEGER,
+	.arg1_type  = ARG_PTR_TO_UNINIT_MEM,
+	.arg2_type  = ARG_CONST_SIZE,
+	.arg3_type  = ARG_ANYTHING,
+};
+
  static const struct bpf_func_proto *
  tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
  {
@@ -953,6 +1019,8 @@ tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
  		return &bpf_get_stackid_proto_tp;
  	case BPF_FUNC_get_stack:
  		return &bpf_get_stack_proto_tp;
+	case BPF_FUNC_get_file_path:
+		return &bpf_get_file_path_proto;
  	default:
  		return tracing_func_proto(func_id, prog);
  	}
@@ -1146,6 +1214,8 @@ raw_tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
  		return &bpf_get_stackid_proto_raw_tp;
  	case BPF_FUNC_get_stack:
  		return &bpf_get_stack_proto_raw_tp;
+	case BPF_FUNC_get_file_path:
+		return &bpf_get_file_path_proto;
  	default:
  		return tracing_func_proto(func_id, prog);
  	}
tools/include/uapi/linux/bpf.h +2 -0
index dbbcf0b02970..71d9705df120 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
   * 	Return
   * 		On success, the strictly positive length of the string,	including
   * 		the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_file_path(char *path, u32 size, int fd)

tools/include/uapi/linux/bpf.h union bpf_attr { · line 2835

Quoted 10 lines
tools/include/uapi/linux/bpf.h +10 -0
+ *	Description
+ *		Get **file** atrribute from the current task by *fd*, then call
+ *		**d_path** to get it's absolute path and copy it as string into
+ *		*path* of *size*. Notice the **path** don't support unmountable
+ *		pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *		The *size* must be strictly positive. On success, the helper
+ *		makes sure that the *path* is NUL-terminated, and the buffer
+ *		could be:
+ *		- a regular full path (include mountable fs eg: /proc, /sys)
+ *		- a regular full path with "(deleted)" at the end.

ditto

tools/include/uapi/linux/bpf.h union bpf_attr { · line 2839

Quoted 4 lines
tools/include/uapi/linux/bpf.h +4 -0
+ *		On failure, it is filled with zeroes.
+ *	Return
+ *		On success, returns the length of the copied string INCLUDING
+ *		the trailing NUL.

ditto

Quoted 24 lines
tools/include/uapi/linux/bpf.h +12 -1
+ *
+ *		On failure, the returned value is one of the following:
+ *
+ *		**-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *		**-EBADF** if *fd* is invalid.
+ *
+ *		**-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *		**-ENAMETOOLONG** if full path is longer than *size*
   */
  #define __BPF_FUNC_MAPPER(FN)		\
  	FN(unspec),			\
@@ -2938,7 +2964,8 @@ union bpf_attr {
  	FN(probe_read_user),		\
  	FN(probe_read_kernel),		\
  	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_file_path),
 
  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
   * function eBPF program intends to call
#16 lore raw

Re: [PATCH bpf-next v12 1/2] bpf: add new helper get_file_path for mapping a file descriptor to a pathname

Quoted 5 lines
Quoted 2 lines

+ * - a regular full path (include mountable fs eg: /proc, /sys)
+ * - a regular full path with "(deleted)" at the end.

Let us say with " (deleted)" is appended to be consistent with comments in d_path() and is more clear to user what the format will looks like.

Thank you, I'll fix this.

Quoted 5 lines

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_send_signal_proto = { · line 813

Quoted 3 lines
kernel/trace/bpf_trace.c +3 -0
+     ret = strlen(p);
+     memmove(dst, p, ret);
+     dst[ret++] = '\0';

nit: you could do memmove(dst, p, ret + 1)?

I did with dst[ret++]='\0'; to return value length including trailing '\0'. as you mentioned below:

Quoted 5 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+     fput(f);
+     return ret;

The description says the return value length including trailing '\0'. The above 'ret' does not include trailing '\0'.

It seems [ret++] not very clear to read and '\0' can be done by memmove. I think I'll refactor to

```
ret = strlen(p) + 1;
memmove(dst, p, ret);
fput(f);
return ret;
```

Is this better?

Quoted 280 lines · Yonghong Song <[email protected]> 于2019年12月16日周一 上午12:06写道:
Quoted 76 lines · Wenbo Zhang

When people want to identify which file system files are being opened, read, and written to, they can use this helper with file descriptor as input to achieve this goal. Other pseudo filesystems are also supported.

This requirement is mainly discussed here:

   https://github.com/iovisor/bcc/issues/237

v11->v12: addressed Alexei's feedback
- only allow tracepoints to make sure it won't dead lock

v10->v11: addressed Al and Alexei's feedback
- fix missing fput()

v9->v10: addressed Andrii's feedback
- send this patch together with the patch selftests as one patch series

v8->v9:
- format helper description

v7->v8: addressed Alexei's feedback
- use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/
- ensure we're in user context which is safe fot the help to run
- filter unmountable pseudo filesystem, because they don't have real path
- supplement the description of this helper function

v6->v7:
- fix missing signed-off-by line

v5->v6: addressed Andrii's feedback
- avoid unnecessary goto end by having two explicit returns

v4->v5: addressed Andrii and Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names
- when fdget_raw fails, set ret to -EBADF instead of -EINVAL
- remove fdput from fdget_raw's error path
- use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long
- modify the normal path's return value to return copied string length including NUL
- update this helper description's Return bits.

v3->v4: addressed Daniel's feedback
- fix missing fdput()
- move fd2path from kernel/bpf/trace.c to kernel/trace/bpf_trace.c
- move fd2path's test code to another patch
- add comment to explain why use fdget_raw instead of fdget

v2->v3: addressed Yonghong's feedback
- remove unnecessary LOCKDOWN_BPF_READ
- refactor error handling section for enhanced readability
- provide a test case in tools/testing/selftests/bpf

v1->v2: addressed Daniel's feedback
- fix backward compatibility
- add this helper description
- fix signed-off name

---
  include/uapi/linux/bpf.h       | 29 +++++++++++++-
  kernel/trace/bpf_trace.c       | 70 ++++++++++++++++++++++++++++++++++
  tools/include/uapi/linux/bpf.h | 29 +++++++++++++-
  3 files changed, 126 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +2 -0
index dbbcf0b02970..71d9705df120 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
   *  Return
   *          On success, the strictly positive length of the string, including
   *          the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_file_path(char *path, u32 size, int fd)

include/uapi/linux/bpf.h union bpf_attr { · line 2835

Quoted 10 lines
include/uapi/linux/bpf.h +10 -0
+ *   Description
+ *           Get **file** atrribute from the current task by *fd*, then call
+ *           **d_path** to get it's absolute path and copy it as string into
+ *           *path* of *size*. Notice the **path** don't support unmountable
+ *           pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *           The *size* must be strictly positive. On success, the helper
+ *           makes sure that the *path* is NUL-terminated, and the buffer
+ *           could be:
+ *           - a regular full path (include mountable fs eg: /proc, /sys)
+ *           - a regular full path with "(deleted)" at the end.

Let us say with " (deleted)" is appended to be consistent with comments in d_path() and is more clear to user what the format will looks like.

include/uapi/linux/bpf.h union bpf_attr { · line 2839

Quoted 4 lines
include/uapi/linux/bpf.h +4 -0
+ *           On failure, it is filled with zeroes.
+ *   Return
+ *           On success, returns the length of the copied string INCLUDING
+ *           the trailing NUL.

trailing '\0'.

Quoted 70 lines
include/uapi/linux/bpf.h +12 -1
+ *
+ *           On failure, the returned value is one of the following:
+ *
+ *           **-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *           **-EBADF** if *fd* is invalid.
+ *
+ *           **-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *           **-ENAMETOOLONG** if full path is longer than *size*
   */
  #define __BPF_FUNC_MAPPER(FN)               \
      FN(unspec),                     \
@@ -2938,7 +2964,8 @@ union bpf_attr {
      FN(probe_read_user),            \
      FN(probe_read_kernel),          \
      FN(probe_read_user_str),        \
-     FN(probe_read_kernel_str),
+     FN(probe_read_kernel_str),      \
+     FN(get_file_path),
 
  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
   * function eBPF program intends to call
kernel/trace/bpf_trace.c +39 -0
index e5ef4ae9edb5..db9c0ec46a5d 100644
@@ -762,6 +762,72 @@ static const struct bpf_func_proto bpf_send_signal_proto = {
      .arg1_type      = ARG_ANYTHING,
  };
 
+BPF_CALL_3(bpf_get_file_path, char *, dst, u32, size, int, fd)
+{
+     struct file *f;
+     char *p;
+     int ret = -EBADF;
+
+     /* Ensure we're in user context which is safe for the helper to
+      * run. This helper has no business in a kthread.
+      */
+     if (unlikely(in_interrupt() ||
+                  current->flags & (PF_KTHREAD | PF_EXITING))) {
+             ret = -EPERM;
+             goto error;
+     }
+
+     /* Use fget_raw instead of fget to support O_PATH, and it doesn't
+      * have any sleepable code, so it's ok to be here.
+      */
+     f = fget_raw(fd);
+     if (!f)
+             goto error;
+
+     /* For unmountable pseudo filesystem, it seems to have no meaning
+      * to get their fake paths as they don't have path, and to be no
+      * way to validate this function pointer can be always safe to call
+      * in the current context.
+      */
+     if (f->f_path.dentry->d_op && f->f_path.dentry->d_op->d_dname) {
+             ret = -EINVAL;
+             fput(f);
+             goto error;
+     }
+
+     /* After filter unmountable pseudo filesytem, d_path won't call
+      * dentry->d_op->d_name(), the normally path doesn't have any
+      * sleepable code, and despite it uses the current macro to get
+      * fs_struct (current->fs), we've already ensured we're in user
+      * context, so it's ok to be here.
+      */

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_send_signal_proto = { · line 813

Quoted 10 lines
kernel/trace/bpf_trace.c +10 -0
+     p = d_path(&f->f_path, dst, size);
+     if (IS_ERR(p)) {
+             ret = PTR_ERR(p);
+             fput(f);
+             goto error;
+     }
+
+     ret = strlen(p);
+     memmove(dst, p, ret);
+     dst[ret++] = '\0';

nit: you could do memmove(dst, p, ret + 1)?

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_send_signal_proto = { · line 815

Quoted 2 lines
kernel/trace/bpf_trace.c +2 -0
+     fput(f);
+     return ret;

The description says the return value length including trailing '\0'. The above 'ret' does not include trailing '\0'.

Quoted 46 lines
kernel/trace/bpf_trace.c +19 -0
+
+error:
+     memset(dst, '0', size);
+     return ret;
+}
+
+static const struct bpf_func_proto bpf_get_file_path_proto = {
+     .func       = bpf_get_file_path,
+     .gpl_only   = true,
+     .ret_type   = RET_INTEGER,
+     .arg1_type  = ARG_PTR_TO_UNINIT_MEM,
+     .arg2_type  = ARG_CONST_SIZE,
+     .arg3_type  = ARG_ANYTHING,
+};
+
  static const struct bpf_func_proto *
  tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
  {
@@ -953,6 +1019,8 @@ tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
              return &bpf_get_stackid_proto_tp;
      case BPF_FUNC_get_stack:
              return &bpf_get_stack_proto_tp;
+     case BPF_FUNC_get_file_path:
+             return &bpf_get_file_path_proto;
      default:
              return tracing_func_proto(func_id, prog);
      }
@@ -1146,6 +1214,8 @@ raw_tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
              return &bpf_get_stackid_proto_raw_tp;
      case BPF_FUNC_get_stack:
              return &bpf_get_stack_proto_raw_tp;
+     case BPF_FUNC_get_file_path:
+             return &bpf_get_file_path_proto;
      default:
              return tracing_func_proto(func_id, prog);
      }
tools/include/uapi/linux/bpf.h +2 -0
index dbbcf0b02970..71d9705df120 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
   *  Return
   *          On success, the strictly positive length of the string, including
   *          the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_file_path(char *path, u32 size, int fd)

tools/include/uapi/linux/bpf.h union bpf_attr { · line 2835

Quoted 10 lines
tools/include/uapi/linux/bpf.h +10 -0
+ *   Description
+ *           Get **file** atrribute from the current task by *fd*, then call
+ *           **d_path** to get it's absolute path and copy it as string into
+ *           *path* of *size*. Notice the **path** don't support unmountable
+ *           pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *           The *size* must be strictly positive. On success, the helper
+ *           makes sure that the *path* is NUL-terminated, and the buffer
+ *           could be:
+ *           - a regular full path (include mountable fs eg: /proc, /sys)
+ *           - a regular full path with "(deleted)" at the end.

ditto

tools/include/uapi/linux/bpf.h union bpf_attr { · line 2839

Quoted 4 lines
tools/include/uapi/linux/bpf.h +4 -0
+ *           On failure, it is filled with zeroes.
+ *   Return
+ *           On success, returns the length of the copied string INCLUDING
+ *           the trailing NUL.

ditto

Quoted 24 lines
tools/include/uapi/linux/bpf.h +12 -1
+ *
+ *           On failure, the returned value is one of the following:
+ *
+ *           **-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *           **-EBADF** if *fd* is invalid.
+ *
+ *           **-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *           **-ENAMETOOLONG** if full path is longer than *size*
   */
  #define __BPF_FUNC_MAPPER(FN)               \
      FN(unspec),                     \
@@ -2938,7 +2964,8 @@ union bpf_attr {
      FN(probe_read_user),            \
      FN(probe_read_kernel),          \
      FN(probe_read_user_str),        \
-     FN(probe_read_kernel_str),
+     FN(probe_read_kernel_str),      \
+     FN(get_file_path),
 
  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
   * function eBPF program intends to call
#17 lore raw

Re: [PATCH bpf-next v12 1/2] bpf: add new helper get_file_path for mapping a file descriptor to a pathname

Quoted 26 lines · Wenbo Zhang
Quoted 5 lines
Quoted 2 lines

+ * - a regular full path (include mountable fs eg: /proc, /sys)
+ * - a regular full path with "(deleted)" at the end.

Let us say with " (deleted)" is appended to be consistent with comments in d_path() and is more clear to user what the format will looks like.

Thank you, I'll fix this.

Quoted 5 lines

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_send_signal_proto = { · line 813

Quoted 3 lines
kernel/trace/bpf_trace.c +3 -0
+     ret = strlen(p);
+     memmove(dst, p, ret);
+     dst[ret++] = '\0';

nit: you could do memmove(dst, p, ret + 1)?

I did with dst[ret++]='\0'; to return value length including trailing '\0'. as you mentioned below:

Quoted 5 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+     fput(f);
+     return ret;

The description says the return value length including trailing '\0'. The above 'ret' does not include trailing '\0'.

It seems [ret++] not very clear to read and '\0' can be done by memmove. I think I'll refactor to

Quoted 8 lines
```
ret = strlen(p) + 1;
memmove(dst, p, ret);
fput(f);
return ret;
```

Is this better?

Ah, I missed ret++ in dst[ret++]. Indeed the above code is better.

#18 lore raw

Re: [PATCH bpf-next v12 1/2] bpf: add new helper get_file_path for mapping a file descriptor to a pathname

In reply to Wenbo Zhang
Quoted 118 lines · Wenbo Zhang

When people want to identify which file system files are being opened, read, and written to, they can use this helper with file descriptor as input to achieve this goal. Other pseudo filesystems are also supported.

This requirement is mainly discussed here:

   https://github.com/iovisor/bcc/issues/237

v11->v12: addressed Alexei's feedback
- only allow tracepoints to make sure it won't dead lock

v10->v11: addressed Al and Alexei's feedback
- fix missing fput()

v9->v10: addressed Andrii's feedback
- send this patch together with the patch selftests as one patch series

v8->v9:
- format helper description

v7->v8: addressed Alexei's feedback
- use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/
- ensure we're in user context which is safe fot the help to run
- filter unmountable pseudo filesystem, because they don't have real path
- supplement the description of this helper function

v6->v7:
- fix missing signed-off-by line

v5->v6: addressed Andrii's feedback
- avoid unnecessary goto end by having two explicit returns

v4->v5: addressed Andrii and Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names
- when fdget_raw fails, set ret to -EBADF instead of -EINVAL
- remove fdput from fdget_raw's error path
- use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long
- modify the normal path's return value to return copied string length including NUL
- update this helper description's Return bits.

v3->v4: addressed Daniel's feedback
- fix missing fdput()
- move fd2path from kernel/bpf/trace.c to kernel/trace/bpf_trace.c
- move fd2path's test code to another patch
- add comment to explain why use fdget_raw instead of fdget

v2->v3: addressed Yonghong's feedback
- remove unnecessary LOCKDOWN_BPF_READ
- refactor error handling section for enhanced readability
- provide a test case in tools/testing/selftests/bpf

v1->v2: addressed Daniel's feedback
- fix backward compatibility
- add this helper description
- fix signed-off name

---
  include/uapi/linux/bpf.h       | 29 +++++++++++++-
  kernel/trace/bpf_trace.c       | 70 ++++++++++++++++++++++++++++++++++
  tools/include/uapi/linux/bpf.h | 29 +++++++++++++-
  3 files changed, 126 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..71d9705df120 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
   * 	Return
   * 		On success, the strictly positive length of the string,	including
   * 		the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_file_path(char *path, u32 size, int fd)
+ *	Description
+ *		Get **file** atrribute from the current task by *fd*, then call
+ *		**d_path** to get it's absolute path and copy it as string into
+ *		*path* of *size*. Notice the **path** don't support unmountable
+ *		pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *		The *size* must be strictly positive. On success, the helper
+ *		makes sure that the *path* is NUL-terminated, and the buffer
+ *		could be:
+ *		- a regular full path (include mountable fs eg: /proc, /sys)
+ *		- a regular full path with "(deleted)" at the end.
+ *		On failure, it is filled with zeroes.
+ *	Return
+ *		On success, returns the length of the copied string INCLUDING
+ *		the trailing NUL.
+ *
+ *		On failure, the returned value is one of the following:
+ *
+ *		**-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *		**-EBADF** if *fd* is invalid.
+ *
+ *		**-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *		**-ENAMETOOLONG** if full path is longer than *size*
   */
  #define __BPF_FUNC_MAPPER(FN)		\
  	FN(unspec),			\
@@ -2938,7 +2964,8 @@ union bpf_attr {
  	FN(probe_read_user),		\
  	FN(probe_read_kernel),		\
  	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_file_path),
 
  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
   * function eBPF program intends to call
kernel/trace/bpf_trace.c +0 -0
index e5ef4ae9edb5..db9c0ec46a5d 100644

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_send_signal_proto = { · line 769

Quoted 8 lines
kernel/trace/bpf_trace.c +5 -0
@@ -762,6 +762,72 @@ static const struct bpf_func_proto bpf_send_signal_proto = {
  	.arg1_type	= ARG_ANYTHING,
  };
 
+BPF_CALL_3(bpf_get_file_path, char *, dst, u32, size, int, fd)
+{
+	struct file *f;
+	char *p;
+	int ret = -EBADF;

please try to use reverse Christmas tree for declarations.

Quoted 52 lines
kernel/trace/bpf_trace.c +52 -0
+
+	/* Ensure we're in user context which is safe for the helper to
+	 * run. This helper has no business in a kthread.
+	 */
+	if (unlikely(in_interrupt() ||
+		     current->flags & (PF_KTHREAD | PF_EXITING))) {
+		ret = -EPERM;
+		goto error;
+	}
+
+	/* Use fget_raw instead of fget to support O_PATH, and it doesn't
+	 * have any sleepable code, so it's ok to be here.
+	 */
+	f = fget_raw(fd);
+	if (!f)
+		goto error;
+
+	/* For unmountable pseudo filesystem, it seems to have no meaning
+	 * to get their fake paths as they don't have path, and to be no
+	 * way to validate this function pointer can be always safe to call
+	 * in the current context.
+	 */
+	if (f->f_path.dentry->d_op && f->f_path.dentry->d_op->d_dname) {
+		ret = -EINVAL;
+		fput(f);
+		goto error;
+	}
+
+	/* After filter unmountable pseudo filesytem, d_path won't call
+	 * dentry->d_op->d_name(), the normally path doesn't have any
+	 * sleepable code, and despite it uses the current macro to get
+	 * fs_struct (current->fs), we've already ensured we're in user
+	 * context, so it's ok to be here.
+	 */
+	p = d_path(&f->f_path, dst, size);
+	if (IS_ERR(p)) {
+		ret = PTR_ERR(p);
+		fput(f);
+		goto error;
+	}
+
+	ret = strlen(p);
+	memmove(dst, p, ret);
+	dst[ret++] = '\0';
+	fput(f);
+	return ret;
+
+error:
+	memset(dst, '0', size);
+	return ret;
+}
+

[...]

#19 lore raw

Re: [PATCH bpf-next v12 1/2] bpf: add new helper get_file_path for mapping a file descriptor to a pathname

Quoted 7 lines

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_send_signal_proto = { · line 769

Quoted 5 lines
kernel/trace/bpf_trace.c +5 -0
+BPF_CALL_3(bpf_get_file_path, char *, dst, u32, size, int, fd)
+{
+     struct file *f;
+     char *p;
+     int ret = -EBADF;

please try to use reverse Christmas tree for declarations.

Thank you, I'll fix this.

Quoted 186 lines · Yonghong Song <[email protected]> 于2019年12月16日周一 上午12:10写道:
Quoted 118 lines · Wenbo Zhang

When people want to identify which file system files are being opened, read, and written to, they can use this helper with file descriptor as input to achieve this goal. Other pseudo filesystems are also supported.

This requirement is mainly discussed here:

   https://github.com/iovisor/bcc/issues/237

v11->v12: addressed Alexei's feedback
- only allow tracepoints to make sure it won't dead lock

v10->v11: addressed Al and Alexei's feedback
- fix missing fput()

v9->v10: addressed Andrii's feedback
- send this patch together with the patch selftests as one patch series

v8->v9:
- format helper description

v7->v8: addressed Alexei's feedback
- use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/
- ensure we're in user context which is safe fot the help to run
- filter unmountable pseudo filesystem, because they don't have real path
- supplement the description of this helper function

v6->v7:
- fix missing signed-off-by line

v5->v6: addressed Andrii's feedback
- avoid unnecessary goto end by having two explicit returns

v4->v5: addressed Andrii and Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names
- when fdget_raw fails, set ret to -EBADF instead of -EINVAL
- remove fdput from fdget_raw's error path
- use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long
- modify the normal path's return value to return copied string length including NUL
- update this helper description's Return bits.

v3->v4: addressed Daniel's feedback
- fix missing fdput()
- move fd2path from kernel/bpf/trace.c to kernel/trace/bpf_trace.c
- move fd2path's test code to another patch
- add comment to explain why use fdget_raw instead of fdget

v2->v3: addressed Yonghong's feedback
- remove unnecessary LOCKDOWN_BPF_READ
- refactor error handling section for enhanced readability
- provide a test case in tools/testing/selftests/bpf

v1->v2: addressed Daniel's feedback
- fix backward compatibility
- add this helper description
- fix signed-off name

---
  include/uapi/linux/bpf.h       | 29 +++++++++++++-
  kernel/trace/bpf_trace.c       | 70 ++++++++++++++++++++++++++++++++++
  tools/include/uapi/linux/bpf.h | 29 +++++++++++++-
  3 files changed, 126 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..71d9705df120 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
   *  Return
   *          On success, the strictly positive length of the string, including
   *          the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_file_path(char *path, u32 size, int fd)
+ *   Description
+ *           Get **file** atrribute from the current task by *fd*, then call
+ *           **d_path** to get it's absolute path and copy it as string into
+ *           *path* of *size*. Notice the **path** don't support unmountable
+ *           pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *           The *size* must be strictly positive. On success, the helper
+ *           makes sure that the *path* is NUL-terminated, and the buffer
+ *           could be:
+ *           - a regular full path (include mountable fs eg: /proc, /sys)
+ *           - a regular full path with "(deleted)" at the end.
+ *           On failure, it is filled with zeroes.
+ *   Return
+ *           On success, returns the length of the copied string INCLUDING
+ *           the trailing NUL.
+ *
+ *           On failure, the returned value is one of the following:
+ *
+ *           **-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *           **-EBADF** if *fd* is invalid.
+ *
+ *           **-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *           **-ENAMETOOLONG** if full path is longer than *size*
   */
  #define __BPF_FUNC_MAPPER(FN)               \
      FN(unspec),                     \
@@ -2938,7 +2964,8 @@ union bpf_attr {
      FN(probe_read_user),            \
      FN(probe_read_kernel),          \
      FN(probe_read_user_str),        \
-     FN(probe_read_kernel_str),
+     FN(probe_read_kernel_str),      \
+     FN(get_file_path),
 
  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
   * function eBPF program intends to call
kernel/trace/bpf_trace.c +0 -0
index e5ef4ae9edb5..db9c0ec46a5d 100644

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_send_signal_proto = { · line 769

Quoted 8 lines
kernel/trace/bpf_trace.c +5 -0
@@ -762,6 +762,72 @@ static const struct bpf_func_proto bpf_send_signal_proto = {
      .arg1_type      = ARG_ANYTHING,
  };
 
+BPF_CALL_3(bpf_get_file_path, char *, dst, u32, size, int, fd)
+{
+     struct file *f;
+     char *p;
+     int ret = -EBADF;

please try to use reverse Christmas tree for declarations.

Quoted 52 lines
kernel/trace/bpf_trace.c +52 -0
+
+     /* Ensure we're in user context which is safe for the helper to
+      * run. This helper has no business in a kthread.
+      */
+     if (unlikely(in_interrupt() ||
+                  current->flags & (PF_KTHREAD | PF_EXITING))) {
+             ret = -EPERM;
+             goto error;
+     }
+
+     /* Use fget_raw instead of fget to support O_PATH, and it doesn't
+      * have any sleepable code, so it's ok to be here.
+      */
+     f = fget_raw(fd);
+     if (!f)
+             goto error;
+
+     /* For unmountable pseudo filesystem, it seems to have no meaning
+      * to get their fake paths as they don't have path, and to be no
+      * way to validate this function pointer can be always safe to call
+      * in the current context.
+      */
+     if (f->f_path.dentry->d_op && f->f_path.dentry->d_op->d_dname) {
+             ret = -EINVAL;
+             fput(f);
+             goto error;
+     }
+
+     /* After filter unmountable pseudo filesytem, d_path won't call
+      * dentry->d_op->d_name(), the normally path doesn't have any
+      * sleepable code, and despite it uses the current macro to get
+      * fs_struct (current->fs), we've already ensured we're in user
+      * context, so it's ok to be here.
+      */
+     p = d_path(&f->f_path, dst, size);
+     if (IS_ERR(p)) {
+             ret = PTR_ERR(p);
+             fput(f);
+             goto error;
+     }
+
+     ret = strlen(p);
+     memmove(dst, p, ret);
+     dst[ret++] = '\0';
+     fput(f);
+     return ret;
+
+error:
+     memset(dst, '0', size);
+     return ret;
+}
+

[...]

#20 lore raw

Re: [PATCH bpf-next v12 1/2] bpf: add new helper get_file_path for mapping a file descriptor to a pathname

In reply to Wenbo Zhang
Quoted 247 lines · Wenbo Zhang

When people want to identify which file system files are being opened, read, and written to, they can use this helper with file descriptor as input to achieve this goal. Other pseudo filesystems are also supported.

This requirement is mainly discussed here:

https://github.com/iovisor/bcc/issues/237

v11->v12: addressed Alexei's feedback
- only allow tracepoints to make sure it won't dead lock

v10->v11: addressed Al and Alexei's feedback
- fix missing fput()

v9->v10: addressed Andrii's feedback
- send this patch together with the patch selftests as one patch series

v8->v9:
- format helper description

v7->v8: addressed Alexei's feedback
- use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/
- ensure we're in user context which is safe fot the help to run
- filter unmountable pseudo filesystem, because they don't have real path
- supplement the description of this helper function

v6->v7:
- fix missing signed-off-by line

v5->v6: addressed Andrii's feedback
- avoid unnecessary goto end by having two explicit returns

v4->v5: addressed Andrii and Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names
- when fdget_raw fails, set ret to -EBADF instead of -EINVAL
- remove fdput from fdget_raw's error path
- use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long
- modify the normal path's return value to return copied string length including NUL
- update this helper description's Return bits.

v3->v4: addressed Daniel's feedback
- fix missing fdput()
- move fd2path from kernel/bpf/trace.c to kernel/trace/bpf_trace.c
- move fd2path's test code to another patch
- add comment to explain why use fdget_raw instead of fdget

v2->v3: addressed Yonghong's feedback
- remove unnecessary LOCKDOWN_BPF_READ
- refactor error handling section for enhanced readability
- provide a test case in tools/testing/selftests/bpf

v1->v2: addressed Daniel's feedback
- fix backward compatibility
- add this helper description
- fix signed-off name

---
 include/uapi/linux/bpf.h       | 29 +++++++++++++-
 kernel/trace/bpf_trace.c       | 70 ++++++++++++++++++++++++++++++++++
 tools/include/uapi/linux/bpf.h | 29 +++++++++++++-
 3 files changed, 126 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..71d9705df120 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
  *     Return
  *             On success, the strictly positive length of the string, including
  *             the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_file_path(char *path, u32 size, int fd)
+ *     Description
+ *             Get **file** atrribute from the current task by *fd*, then call
+ *             **d_path** to get it's absolute path and copy it as string into
+ *             *path* of *size*. Notice the **path** don't support unmountable
+ *             pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *             The *size* must be strictly positive. On success, the helper
+ *             makes sure that the *path* is NUL-terminated, and the buffer
+ *             could be:
+ *             - a regular full path (include mountable fs eg: /proc, /sys)
+ *             - a regular full path with "(deleted)" at the end.
+ *             On failure, it is filled with zeroes.
+ *     Return
+ *             On success, returns the length of the copied string INCLUDING
+ *             the trailing NUL.
+ *
+ *             On failure, the returned value is one of the following:
+ *
+ *             **-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *             **-EBADF** if *fd* is invalid.
+ *
+ *             **-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *             **-ENAMETOOLONG** if full path is longer than *size*
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -2938,7 +2964,8 @@ union bpf_attr {
        FN(probe_read_user),            \
        FN(probe_read_kernel),          \
        FN(probe_read_user_str),        \
-       FN(probe_read_kernel_str),
+       FN(probe_read_kernel_str),      \
+       FN(get_file_path),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
kernel/trace/bpf_trace.c +70 -0
index e5ef4ae9edb5..db9c0ec46a5d 100644
@@ -762,6 +762,72 @@ static const struct bpf_func_proto bpf_send_signal_proto = {
        .arg1_type      = ARG_ANYTHING,
 };
 
+BPF_CALL_3(bpf_get_file_path, char *, dst, u32, size, int, fd)
+{
+       struct file *f;
+       char *p;
+       int ret = -EBADF;
+
+       /* Ensure we're in user context which is safe for the helper to
+        * run. This helper has no business in a kthread.
+        */
+       if (unlikely(in_interrupt() ||
+                    current->flags & (PF_KTHREAD | PF_EXITING))) {
+               ret = -EPERM;
+               goto error;
+       }
+
+       /* Use fget_raw instead of fget to support O_PATH, and it doesn't
+        * have any sleepable code, so it's ok to be here.
+        */
+       f = fget_raw(fd);
+       if (!f)
+               goto error;
+
+       /* For unmountable pseudo filesystem, it seems to have no meaning
+        * to get their fake paths as they don't have path, and to be no
+        * way to validate this function pointer can be always safe to call
+        * in the current context.
+        */
+       if (f->f_path.dentry->d_op && f->f_path.dentry->d_op->d_dname) {
+               ret = -EINVAL;
+               fput(f);
+               goto error;
+       }
+
+       /* After filter unmountable pseudo filesytem, d_path won't call
+        * dentry->d_op->d_name(), the normally path doesn't have any
+        * sleepable code, and despite it uses the current macro to get
+        * fs_struct (current->fs), we've already ensured we're in user
+        * context, so it's ok to be here.
+        */
+       p = d_path(&f->f_path, dst, size);
+       if (IS_ERR(p)) {
+               ret = PTR_ERR(p);
+               fput(f);
+               goto error;
+       }
+
+       ret = strlen(p);
+       memmove(dst, p, ret);
+       dst[ret++] = '\0';
+       fput(f);
+       return ret;
+
+error:
+       memset(dst, '0', size);
+       return ret;
+}
+
+static const struct bpf_func_proto bpf_get_file_path_proto = {
+       .func       = bpf_get_file_path,
+       .gpl_only   = true,
+       .ret_type   = RET_INTEGER,
+       .arg1_type  = ARG_PTR_TO_UNINIT_MEM,
+       .arg2_type  = ARG_CONST_SIZE,
+       .arg3_type  = ARG_ANYTHING,
+};
+
 static const struct bpf_func_proto *
 tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 {
@@ -953,6 +1019,8 @@ tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_get_stackid_proto_tp;
        case BPF_FUNC_get_stack:
                return &bpf_get_stack_proto_tp;
+       case BPF_FUNC_get_file_path:
+               return &bpf_get_file_path_proto;
        default:
                return tracing_func_proto(func_id, prog);
        }
@@ -1146,6 +1214,8 @@ raw_tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_get_stackid_proto_raw_tp;
        case BPF_FUNC_get_stack:
                return &bpf_get_stack_proto_raw_tp;
+       case BPF_FUNC_get_file_path:
+               return &bpf_get_file_path_proto;
        default:
                return tracing_func_proto(func_id, prog);
        }
tools/include/uapi/linux/bpf.h +26 -0
index dbbcf0b02970..71d9705df120 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
  *     Return
  *             On success, the strictly positive length of the string, including
  *             the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_file_path(char *path, u32 size, int fd)
+ *     Description
+ *             Get **file** atrribute from the current task by *fd*, then call
+ *             **d_path** to get it's absolute path and copy it as string into
+ *             *path* of *size*. Notice the **path** don't support unmountable
+ *             pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *             The *size* must be strictly positive. On success, the helper
+ *             makes sure that the *path* is NUL-terminated, and the buffer
+ *             could be:
+ *             - a regular full path (include mountable fs eg: /proc, /sys)
+ *             - a regular full path with "(deleted)" at the end.
+ *             On failure, it is filled with zeroes.
+ *     Return
+ *             On success, returns the length of the copied string INCLUDING
+ *             the trailing NUL.
+ *
+ *             On failure, the returned value is one of the following:
+ *
+ *             **-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *             **-EBADF** if *fd* is invalid.
+ *
+ *             **-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *             **-ENAMETOOLONG** if full path is longer than *size*
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \

tools/include/uapi/linux/bpf.h union bpf_attr { · line 2968

Quoted 6 lines
tools/include/uapi/linux/bpf.h +2 -1
@@ -2938,7 +2964,8 @@ union bpf_attr {
        FN(probe_read_user),            \
        FN(probe_read_kernel),          \
        FN(probe_read_user_str),        \
-       FN(probe_read_kernel_str),
+       FN(probe_read_kernel_str),      \
+       FN(get_file_path),

I just realized that among my tools that want the path, the input is either:

A) syscall tracepoints: int fd
B) kprobes: struct file *

This serves (A). If we ever add a different helper for (B), we might think that this helper was misnamed. Should it be called get_fd_path instead? That leaves get_file_path available for a later "struct file *" -> pathname helper.

Brendan

Signature · 1 line
Brendan Gregg, Senior Performance Architect, Netflix
#21 lore raw

Re: [PATCH bpf-next v12 1/2] bpf: add new helper get_file_path for mapping a file descriptor to a pathname

Quoted 9 lines

I just realized that among my tools that want the path, the input is either:

A) syscall tracepoints: int fd
B) kprobes: struct file *

This serves (A). If we ever add a different helper for (B), we might think that this helper was misnamed. Should it be called get_fd_path instead? That leaves get_file_path available for a later "struct file *" -> pathname helper.

+1, I'll rename it in the next version.

Quoted 271 lines · Brendan Gregg <[email protected]> 于2019年12月17日周二 上午6:09写道:
Quoted 247 lines · Wenbo Zhang

When people want to identify which file system files are being opened, read, and written to, they can use this helper with file descriptor as input to achieve this goal. Other pseudo filesystems are also supported.

This requirement is mainly discussed here:

https://github.com/iovisor/bcc/issues/237

v11->v12: addressed Alexei's feedback
- only allow tracepoints to make sure it won't dead lock

v10->v11: addressed Al and Alexei's feedback
- fix missing fput()

v9->v10: addressed Andrii's feedback
- send this patch together with the patch selftests as one patch series

v8->v9:
- format helper description

v7->v8: addressed Alexei's feedback
- use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/
- ensure we're in user context which is safe fot the help to run
- filter unmountable pseudo filesystem, because they don't have real path
- supplement the description of this helper function

v6->v7:
- fix missing signed-off-by line

v5->v6: addressed Andrii's feedback
- avoid unnecessary goto end by having two explicit returns

v4->v5: addressed Andrii and Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names
- when fdget_raw fails, set ret to -EBADF instead of -EINVAL
- remove fdput from fdget_raw's error path
- use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long
- modify the normal path's return value to return copied string length including NUL
- update this helper description's Return bits.

v3->v4: addressed Daniel's feedback
- fix missing fdput()
- move fd2path from kernel/bpf/trace.c to kernel/trace/bpf_trace.c
- move fd2path's test code to another patch
- add comment to explain why use fdget_raw instead of fdget

v2->v3: addressed Yonghong's feedback
- remove unnecessary LOCKDOWN_BPF_READ
- refactor error handling section for enhanced readability
- provide a test case in tools/testing/selftests/bpf

v1->v2: addressed Daniel's feedback
- fix backward compatibility
- add this helper description
- fix signed-off name

---
 include/uapi/linux/bpf.h       | 29 +++++++++++++-
 kernel/trace/bpf_trace.c       | 70 ++++++++++++++++++++++++++++++++++
 tools/include/uapi/linux/bpf.h | 29 +++++++++++++-
 3 files changed, 126 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..71d9705df120 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
  *     Return
  *             On success, the strictly positive length of the string, including
  *             the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_file_path(char *path, u32 size, int fd)
+ *     Description
+ *             Get **file** atrribute from the current task by *fd*, then call
+ *             **d_path** to get it's absolute path and copy it as string into
+ *             *path* of *size*. Notice the **path** don't support unmountable
+ *             pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *             The *size* must be strictly positive. On success, the helper
+ *             makes sure that the *path* is NUL-terminated, and the buffer
+ *             could be:
+ *             - a regular full path (include mountable fs eg: /proc, /sys)
+ *             - a regular full path with "(deleted)" at the end.
+ *             On failure, it is filled with zeroes.
+ *     Return
+ *             On success, returns the length of the copied string INCLUDING
+ *             the trailing NUL.
+ *
+ *             On failure, the returned value is one of the following:
+ *
+ *             **-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *             **-EBADF** if *fd* is invalid.
+ *
+ *             **-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *             **-ENAMETOOLONG** if full path is longer than *size*
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -2938,7 +2964,8 @@ union bpf_attr {
        FN(probe_read_user),            \
        FN(probe_read_kernel),          \
        FN(probe_read_user_str),        \
-       FN(probe_read_kernel_str),
+       FN(probe_read_kernel_str),      \
+       FN(get_file_path),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
kernel/trace/bpf_trace.c +70 -0
index e5ef4ae9edb5..db9c0ec46a5d 100644
@@ -762,6 +762,72 @@ static const struct bpf_func_proto bpf_send_signal_proto = {
        .arg1_type      = ARG_ANYTHING,
 };
 
+BPF_CALL_3(bpf_get_file_path, char *, dst, u32, size, int, fd)
+{
+       struct file *f;
+       char *p;
+       int ret = -EBADF;
+
+       /* Ensure we're in user context which is safe for the helper to
+        * run. This helper has no business in a kthread.
+        */
+       if (unlikely(in_interrupt() ||
+                    current->flags & (PF_KTHREAD | PF_EXITING))) {
+               ret = -EPERM;
+               goto error;
+       }
+
+       /* Use fget_raw instead of fget to support O_PATH, and it doesn't
+        * have any sleepable code, so it's ok to be here.
+        */
+       f = fget_raw(fd);
+       if (!f)
+               goto error;
+
+       /* For unmountable pseudo filesystem, it seems to have no meaning
+        * to get their fake paths as they don't have path, and to be no
+        * way to validate this function pointer can be always safe to call
+        * in the current context.
+        */
+       if (f->f_path.dentry->d_op && f->f_path.dentry->d_op->d_dname) {
+               ret = -EINVAL;
+               fput(f);
+               goto error;
+       }
+
+       /* After filter unmountable pseudo filesytem, d_path won't call
+        * dentry->d_op->d_name(), the normally path doesn't have any
+        * sleepable code, and despite it uses the current macro to get
+        * fs_struct (current->fs), we've already ensured we're in user
+        * context, so it's ok to be here.
+        */
+       p = d_path(&f->f_path, dst, size);
+       if (IS_ERR(p)) {
+               ret = PTR_ERR(p);
+               fput(f);
+               goto error;
+       }
+
+       ret = strlen(p);
+       memmove(dst, p, ret);
+       dst[ret++] = '\0';
+       fput(f);
+       return ret;
+
+error:
+       memset(dst, '0', size);
+       return ret;
+}
+
+static const struct bpf_func_proto bpf_get_file_path_proto = {
+       .func       = bpf_get_file_path,
+       .gpl_only   = true,
+       .ret_type   = RET_INTEGER,
+       .arg1_type  = ARG_PTR_TO_UNINIT_MEM,
+       .arg2_type  = ARG_CONST_SIZE,
+       .arg3_type  = ARG_ANYTHING,
+};
+
 static const struct bpf_func_proto *
 tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 {
@@ -953,6 +1019,8 @@ tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_get_stackid_proto_tp;
        case BPF_FUNC_get_stack:
                return &bpf_get_stack_proto_tp;
+       case BPF_FUNC_get_file_path:
+               return &bpf_get_file_path_proto;
        default:
                return tracing_func_proto(func_id, prog);
        }
@@ -1146,6 +1214,8 @@ raw_tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_get_stackid_proto_raw_tp;
        case BPF_FUNC_get_stack:
                return &bpf_get_stack_proto_raw_tp;
+       case BPF_FUNC_get_file_path:
+               return &bpf_get_file_path_proto;
        default:
                return tracing_func_proto(func_id, prog);
        }
tools/include/uapi/linux/bpf.h +26 -0
index dbbcf0b02970..71d9705df120 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
  *     Return
  *             On success, the strictly positive length of the string, including
  *             the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_file_path(char *path, u32 size, int fd)
+ *     Description
+ *             Get **file** atrribute from the current task by *fd*, then call
+ *             **d_path** to get it's absolute path and copy it as string into
+ *             *path* of *size*. Notice the **path** don't support unmountable
+ *             pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *             The *size* must be strictly positive. On success, the helper
+ *             makes sure that the *path* is NUL-terminated, and the buffer
+ *             could be:
+ *             - a regular full path (include mountable fs eg: /proc, /sys)
+ *             - a regular full path with "(deleted)" at the end.
+ *             On failure, it is filled with zeroes.
+ *     Return
+ *             On success, returns the length of the copied string INCLUDING
+ *             the trailing NUL.
+ *
+ *             On failure, the returned value is one of the following:
+ *
+ *             **-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *             **-EBADF** if *fd* is invalid.
+ *
+ *             **-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *             **-ENAMETOOLONG** if full path is longer than *size*
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \

tools/include/uapi/linux/bpf.h union bpf_attr { · line 2968

Quoted 6 lines
tools/include/uapi/linux/bpf.h +2 -1
@@ -2938,7 +2964,8 @@ union bpf_attr {
        FN(probe_read_user),            \
        FN(probe_read_kernel),          \
        FN(probe_read_user_str),        \
-       FN(probe_read_kernel_str),
+       FN(probe_read_kernel_str),      \
+       FN(get_file_path),

I just realized that among my tools that want the path, the input is either:

A) syscall tracepoints: int fd
B) kprobes: struct file *

This serves (A). If we ever add a different helper for (B), we might think that this helper was misnamed. Should it be called get_fd_path instead? That leaves get_file_path available for a later "struct file *" -> pathname helper.

Brendan

--
Brendan Gregg, Senior Performance Architect, Netflix

#22 lore raw

v13 cover bpf: adding get_fd_path helper

In reply to Wenbo Zhang

This patch series introduce a bpf helper that can be used to map a file descriptor to a pathname.

This requirement is mainly discussed here:

https://github.com/iovisor/bcc/issues/237

This implementation supports both local and mountable pseudo file systems, and ensure we're in user context which is safe for this helper to run.

Changes since v12:

* Rename to get_fd_patch

* Fix test issue on big-endian machines

Changes since v11:

* Only allow tracepoints to make sure it won't dead lock

Changes since v10:

* Fix missing fput

Changes since v9:

* Associate help patch with its selftests patch to this series

* Refactor selftests code for further simplification

Changes since v8:

* Format helper description

Changes since v7:

* Use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/

* Ensure we're in user context which is safe fot the help to run

* Filter unmountable pseudo filesystem, because they don't have real path

* Supplement the description of this helper function

Changes since v6:

* Fix missing signed-off-by line

Changes since v5:

* Refactor helper avoid unnecessary goto end by having two explicit returns

Changes since v4:

* Rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names

* When fdget_raw fails, set ret to -EBADF instead of -EINVAL

* Remove fdput from fdget_raw's error path

* Use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long

* Modify the normal path's return value to return copied string length including NUL

* Update helper description's Return bits.

* Refactor selftests code for further simplification

Changes since v3:

* Remove unnecessary LOCKDOWN_BPF_READ

* Refactor error handling section for enhanced readability

* Provide a test case in tools/testing/selftests/bpf

* Refactor sefltests code to use real global variables instead of maps

Changes since v2:

* Fix backward compatibility

* Add helper description

* Refactor selftests use global data instead of perf_buffer to simplified code

* Fix signed-off name

Wenbo Zhang (2):
bpf: add new helper get_fd_path for mapping a file descriptor to a
pathname
selftests/bpf: test for bpf_get_fd_path() from tracepoint

 include/uapi/linux/bpf.h                      |  29 ++-
 kernel/trace/bpf_trace.c                      |  69 +++++++
 tools/include/uapi/linux/bpf.h                |  29 ++-
 .../selftests/bpf/prog_tests/get_fd_path.c    | 171 ++++++++++++++++++
 .../selftests/bpf/progs/test_get_fd_path.c    |  43 +++++
 5 files changed, 339 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/get_fd_path.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_get_fd_path.c
Signature · 1 line
2.17.1
#23 lore raw

PATCH v13 1/2 bpf: add new helper get_fd_path for mapping a file descriptor to a pathname

When people want to identify which file system files are being opened, read, and written to, they can use this helper with file descriptor as input to achieve this goal. Other pseudo filesystems are also supported.

This requirement is mainly discussed here:

https://github.com/iovisor/bcc/issues/237

v12->v13: addressed Gregg and Yonghong's feedback
- rename to get_fd_path
- refactor code & comment to be clearer and more compliant

v11->v12: addressed Alexei's feedback
- only allow tracepoints to make sure it won't dead lock

v10->v11: addressed Al and Alexei's feedback
- fix missing fput()

v9->v10: addressed Andrii's feedback
- send this patch together with the patch selftests as one patch series

v8->v9:
- format helper description

v7->v8: addressed Alexei's feedback
- use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/
- ensure we're in user context which is safe fot the help to run
- filter unmountable pseudo filesystem, because they don't have real path
- supplement the description of this helper function

v6->v7:
- fix missing signed-off-by line

v5->v6: addressed Andrii's feedback
- avoid unnecessary goto end by having two explicit returns

v4->v5: addressed Andrii and Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names
- when fdget_raw fails, set ret to -EBADF instead of -EINVAL
- remove fdput from fdget_raw's error path
- use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long
- modify the normal path's return value to return copied string length including NUL
- update this helper description's Return bits.

v3->v4: addressed Daniel's feedback
- fix missing fdput()
- move fd2path from kernel/bpf/trace.c to kernel/trace/bpf_trace.c
- move fd2path's test code to another patch
- add comment to explain why use fdget_raw instead of fdget

v2->v3: addressed Yonghong's feedback
- remove unnecessary LOCKDOWN_BPF_READ
- refactor error handling section for enhanced readability
- provide a test case in tools/testing/selftests/bpf

v1->v2: addressed Daniel's feedback
- fix backward compatibility
- add this helper description
- fix signed-off name

---
 include/uapi/linux/bpf.h       | 29 +++++++++++++-
 kernel/trace/bpf_trace.c       | 69 ++++++++++++++++++++++++++++++++++
 tools/include/uapi/linux/bpf.h | 29 +++++++++++++-
 3 files changed, 125 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..c1e4fd286614 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
  * 	Return
  * 		On success, the strictly positive length of the string,	including
  * 		the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_fd_path(char *path, u32 size, int fd)
+ *	Description
+ *		Get **file** atrribute from the current task by *fd*, then call
+ *		**d_path** to get it's absolute path and copy it as string into
+ *		*path* of *size*. Notice the **path** don't support unmountable
+ *		pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *		The *size* must be strictly positive. On success, the helper
+ *		makes sure that the *path* is NUL-terminated, and the buffer
+ *		could be:
+ *		- a regular full path (include mountable fs eg: /proc, /sys)
+ *		- a regular full path with "(deleted)" is appended.
+ *		On failure, it is filled with zeroes.
+ *	Return
+ *		On success, returns the length of the copied string INCLUDING
+ *		the trailing '\0'.
+ *
+ *		On failure, the returned value is one of the following:
+ *
+ *		**-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *		**-EBADF** if *fd* is invalid.
+ *
+ *		**-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *		**-ENAMETOOLONG** if full path is longer than *size*
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -2938,7 +2964,8 @@ union bpf_attr {
 	FN(probe_read_user),		\
 	FN(probe_read_kernel),		\
 	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_fd_path),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
kernel/trace/bpf_trace.c +69 -0

87 lines not on this page · raw message

tools/include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..c1e4fd286614 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
  * 	Return
  * 		On success, the strictly positive length of the string,	including
  * 		the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_fd_path(char *path, u32 size, int fd)
+ *	Description
+ *		Get **file** atrribute from the current task by *fd*, then call
+ *		**d_path** to get it's absolute path and copy it as string into
+ *		*path* of *size*. Notice the **path** don't support unmountable
+ *		pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *		The *size* must be strictly positive. On success, the helper
+ *		makes sure that the *path* is NUL-terminated, and the buffer
+ *		could be:
+ *		- a regular full path (include mountable fs eg: /proc, /sys)
+ *		- a regular full path with "(deleted)" is appended.
+ *		On failure, it is filled with zeroes.
+ *	Return
+ *		On success, returns the length of the copied string INCLUDING
+ *		the trailing '\0'.
+ *
+ *		On failure, the returned value is one of the following:
+ *
+ *		**-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *		**-EBADF** if *fd* is invalid.
+ *
+ *		**-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *		**-ENAMETOOLONG** if full path is longer than *size*
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -2938,7 +2964,8 @@ union bpf_attr {
 	FN(probe_read_user),		\
 	FN(probe_read_kernel),		\
 	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_fd_path),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
Signature · 1 line
2.17.1
Acked-by on v13 1/2
#24 lore raw

Re: [PATCH bpf-next v13 1/2] bpf: add new helper get_fd_path for mapping a file descriptor to a pathname

Quoted 58 lines · Wenbo Zhang

When people want to identify which file system files are being opened, read, and written to, they can use this helper with file descriptor as input to achieve this goal. Other pseudo filesystems are also supported.

This requirement is mainly discussed here:

   https://github.com/iovisor/bcc/issues/237

v12->v13: addressed Gregg and Yonghong's feedback
- rename to get_fd_path
- refactor code & comment to be clearer and more compliant

v11->v12: addressed Alexei's feedback
- only allow tracepoints to make sure it won't dead lock

v10->v11: addressed Al and Alexei's feedback
- fix missing fput()

v9->v10: addressed Andrii's feedback
- send this patch together with the patch selftests as one patch series

v8->v9:
- format helper description

v7->v8: addressed Alexei's feedback
- use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/
- ensure we're in user context which is safe fot the help to run
- filter unmountable pseudo filesystem, because they don't have real path
- supplement the description of this helper function

v6->v7:
- fix missing signed-off-by line

v5->v6: addressed Andrii's feedback
- avoid unnecessary goto end by having two explicit returns

v4->v5: addressed Andrii and Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names
- when fdget_raw fails, set ret to -EBADF instead of -EINVAL
- remove fdput from fdget_raw's error path
- use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long
- modify the normal path's return value to return copied string length including NUL
- update this helper description's Return bits.

v3->v4: addressed Daniel's feedback
- fix missing fdput()
- move fd2path from kernel/bpf/trace.c to kernel/trace/bpf_trace.c
- move fd2path's test code to another patch
- add comment to explain why use fdget_raw instead of fdget

v2->v3: addressed Yonghong's feedback
- remove unnecessary LOCKDOWN_BPF_READ
- refactor error handling section for enhanced readability
- provide a test case in tools/testing/selftests/bpf

Quoted 6 lines

v1->v2: addressed Daniel's feedback
- fix backward compatibility
- add this helper description
- fix signed-off name

Ack with still the minor issue below, not sure whether another revision will be needed or not or the maintainer can just fix up before merging.

Quoted 16 lines
---
  include/uapi/linux/bpf.h       | 29 +++++++++++++-
  kernel/trace/bpf_trace.c       | 69 ++++++++++++++++++++++++++++++++++
  tools/include/uapi/linux/bpf.h | 29 +++++++++++++-
  3 files changed, 125 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +2 -0
index dbbcf0b02970..c1e4fd286614 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
   * 	Return
   * 		On success, the strictly positive length of the string,	including
   * 		the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_fd_path(char *path, u32 size, int fd)

include/uapi/linux/bpf.h union bpf_attr { · line 2835

Quoted 10 lines
include/uapi/linux/bpf.h +10 -0
+ *	Description
+ *		Get **file** atrribute from the current task by *fd*, then call
+ *		**d_path** to get it's absolute path and copy it as string into
+ *		*path* of *size*. Notice the **path** don't support unmountable
+ *		pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *		The *size* must be strictly positive. On success, the helper
+ *		makes sure that the *path* is NUL-terminated, and the buffer
+ *		could be:
+ *		- a regular full path (include mountable fs eg: /proc, /sys)
+ *		- a regular full path with "(deleted)" is appended.

Sorry about a little pedantic. In d_path() function comments, we have:
* Convert a dentry into an ASCII path name. If the entry has been deleted
* the string " (deleted)" is appended. Note that this is ambiguous.

Note that there is a space before "(deleted)". I would like to the above changed to
- a regular full path with " (deleted)" is appended.

Quoted 169 lines
include/uapi/linux/bpf.h +16 -1
+ *		On failure, it is filled with zeroes.
+ *	Return
+ *		On success, returns the length of the copied string INCLUDING
+ *		the trailing '\0'.
+ *
+ *		On failure, the returned value is one of the following:
+ *
+ *		**-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *		**-EBADF** if *fd* is invalid.
+ *
+ *		**-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *		**-ENAMETOOLONG** if full path is longer than *size*
   */
  #define __BPF_FUNC_MAPPER(FN)		\
  	FN(unspec),			\
@@ -2938,7 +2964,8 @@ union bpf_attr {
  	FN(probe_read_user),		\
  	FN(probe_read_kernel),		\
  	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_fd_path),
 
  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
   * function eBPF program intends to call
kernel/trace/bpf_trace.c +69 -0
index e5ef4ae9edb5..43a6aa6ad967 100644
@@ -762,6 +762,71 @@ static const struct bpf_func_proto bpf_send_signal_proto = {
  	.arg1_type	= ARG_ANYTHING,
  };
 
+BPF_CALL_3(bpf_get_fd_path, char *, dst, u32, size, int, fd)
+{
+	int ret = -EBADF;
+	struct file *f;
+	char *p;
+
+	/* Ensure we're in user context which is safe for the helper to
+	 * run. This helper has no business in a kthread.
+	 */
+	if (unlikely(in_interrupt() ||
+		     current->flags & (PF_KTHREAD | PF_EXITING))) {
+		ret = -EPERM;
+		goto error;
+	}
+
+	/* Use fget_raw instead of fget to support O_PATH, and it doesn't
+	 * have any sleepable code, so it's ok to be here.
+	 */
+	f = fget_raw(fd);
+	if (!f)
+		goto error;
+
+	/* For unmountable pseudo filesystem, it seems to have no meaning
+	 * to get their fake paths as they don't have path, and to be no
+	 * way to validate this function pointer can be always safe to call
+	 * in the current context.
+	 */
+	if (f->f_path.dentry->d_op && f->f_path.dentry->d_op->d_dname) {
+		ret = -EINVAL;
+		fput(f);
+		goto error;
+	}
+
+	/* After filter unmountable pseudo filesytem, d_path won't call
+	 * dentry->d_op->d_name(), the normally path doesn't have any
+	 * sleepable code, and despite it uses the current macro to get
+	 * fs_struct (current->fs), we've already ensured we're in user
+	 * context, so it's ok to be here.
+	 */
+	p = d_path(&f->f_path, dst, size);
+	if (IS_ERR(p)) {
+		ret = PTR_ERR(p);
+		fput(f);
+		goto error;
+	}
+
+	ret = strlen(p) + 1;
+	memmove(dst, p, ret);
+	fput(f);
+	return ret;
+
+error:
+	memset(dst, '0', size);
+	return ret;
+}
+
+static const struct bpf_func_proto bpf_get_fd_path_proto = {
+	.func       = bpf_get_fd_path,
+	.gpl_only   = true,
+	.ret_type   = RET_INTEGER,
+	.arg1_type  = ARG_PTR_TO_UNINIT_MEM,
+	.arg2_type  = ARG_CONST_SIZE,
+	.arg3_type  = ARG_ANYTHING,
+};
+
  static const struct bpf_func_proto *
  tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
  {
@@ -953,6 +1018,8 @@ tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
  		return &bpf_get_stackid_proto_tp;
  	case BPF_FUNC_get_stack:
  		return &bpf_get_stack_proto_tp;
+	case BPF_FUNC_get_fd_path:
+		return &bpf_get_fd_path_proto;
  	default:
  		return tracing_func_proto(func_id, prog);
  	}
@@ -1146,6 +1213,8 @@ raw_tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
  		return &bpf_get_stackid_proto_raw_tp;
  	case BPF_FUNC_get_stack:
  		return &bpf_get_stack_proto_raw_tp;
+	case BPF_FUNC_get_fd_path:
+		return &bpf_get_fd_path_proto;
  	default:
  		return tracing_func_proto(func_id, prog);
  	}
tools/include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..c1e4fd286614 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
   * 	Return
   * 		On success, the strictly positive length of the string,	including
   * 		the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_fd_path(char *path, u32 size, int fd)
+ *	Description
+ *		Get **file** atrribute from the current task by *fd*, then call
+ *		**d_path** to get it's absolute path and copy it as string into
+ *		*path* of *size*. Notice the **path** don't support unmountable
+ *		pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *		The *size* must be strictly positive. On success, the helper
+ *		makes sure that the *path* is NUL-terminated, and the buffer
+ *		could be:
+ *		- a regular full path (include mountable fs eg: /proc, /sys)
+ *		- a regular full path with "(deleted)" is appended.
+ *		On failure, it is filled with zeroes.
+ *	Return
+ *		On success, returns the length of the copied string INCLUDING
+ *		the trailing '\0'.
+ *
+ *		On failure, the returned value is one of the following:
+ *
+ *		**-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *		**-EBADF** if *fd* is invalid.
+ *
+ *		**-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *		**-ENAMETOOLONG** if full path is longer than *size*
   */
  #define __BPF_FUNC_MAPPER(FN)		\
  	FN(unspec),			\
@@ -2938,7 +2964,8 @@ union bpf_attr {
  	FN(probe_read_user),		\
  	FN(probe_read_kernel),		\
  	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_fd_path),
 
  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
   * function eBPF program intends to call
maintainer
#25 lore raw

Re: [PATCH bpf-next v13 1/2] bpf: add new helper get_fd_path for mapping a file descriptor to a pathname

Quoted 1 line · Yonghong Song

On 12/17/19 1:47 AM, Wenbo Zhang wrote:

[...]

Quoted 1 line
Quoted 1 line

+ * On failure, it is filled with zeroes.

[...]

Quoted 66 lines
Quoted 66 lines
    */
   #define __BPF_FUNC_MAPPER(FN)		\
   	FN(unspec),			\
include/uapi/linux/bpf.h +2 -1
@@ -2938,7 +2964,8 @@ union bpf_attr {
   	FN(probe_read_user),		\
   	FN(probe_read_kernel),		\
   	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_fd_path),
 
   /* integer value in 'imm' field of BPF_CALL instruction selects which helper
    * function eBPF program intends to call
kernel/trace/bpf_trace.c +45 -0
index e5ef4ae9edb5..43a6aa6ad967 100644
@@ -762,6 +762,71 @@ static const struct bpf_func_proto bpf_send_signal_proto = {
   	.arg1_type	= ARG_ANYTHING,
   };
 
+BPF_CALL_3(bpf_get_fd_path, char *, dst, u32, size, int, fd)
+{
+	int ret = -EBADF;
+	struct file *f;
+	char *p;
+
+	/* Ensure we're in user context which is safe for the helper to
+	 * run. This helper has no business in a kthread.
+	 */
+	if (unlikely(in_interrupt() ||
+		     current->flags & (PF_KTHREAD | PF_EXITING))) {
+		ret = -EPERM;
+		goto error;
+	}
+
+	/* Use fget_raw instead of fget to support O_PATH, and it doesn't
+	 * have any sleepable code, so it's ok to be here.
+	 */
+	f = fget_raw(fd);
+	if (!f)
+		goto error;
+
+	/* For unmountable pseudo filesystem, it seems to have no meaning
+	 * to get their fake paths as they don't have path, and to be no
+	 * way to validate this function pointer can be always safe to call
+	 * in the current context.
+	 */
+	if (f->f_path.dentry->d_op && f->f_path.dentry->d_op->d_dname) {
+		ret = -EINVAL;
+		fput(f);
+		goto error;
+	}
+
+	/* After filter unmountable pseudo filesytem, d_path won't call
+	 * dentry->d_op->d_name(), the normally path doesn't have any
+	 * sleepable code, and despite it uses the current macro to get
+	 * fs_struct (current->fs), we've already ensured we're in user
+	 * context, so it's ok to be here.
+	 */
+	p = d_path(&f->f_path, dst, size);
+	if (IS_ERR(p)) {
+		ret = PTR_ERR(p);
+		fput(f);
+		goto error;
+	}
Quoted 8 lines

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_send_signal_proto = { · line 817

Quoted 8 lines
kernel/trace/bpf_trace.c +8 -0
+
+	ret = strlen(p) + 1;
+	memmove(dst, p, ret);
+	fput(f);
+	return ret;
+
+error:
+	memset(dst, '0', size);

You fill it with 0x30's ...

Quoted 2 lines
Quoted 2 lines
kernel/trace/bpf_trace.c +2 -0
+	return ret;
+}
#26 lore raw

Re: [PATCH bpf-next v13 1/2] bpf: add new helper get_fd_path for mapping a file descriptor to a pathname

Quoted 4 lines

[...]

Quoted 1 line
Quoted 1 line

+ * On failure, it is filled with zeroes.

[...]
You fill it with 0x30's ...

So sorry about this, I'll submit another revision to fix this. Thanks again for your
preciseness and patience.

Quoted 85 lines · Daniel Borkmann <[email protected]> 于2019年12月18日周三 上午3:39写道:
Quoted 1 line · Yonghong Song

On 12/17/19 1:47 AM, Wenbo Zhang wrote:

[...]

Quoted 1 line
Quoted 1 line

+ * On failure, it is filled with zeroes.

[...]

Quoted 66 lines
Quoted 66 lines
    */
   #define __BPF_FUNC_MAPPER(FN)             \
     FN(unspec),                     \
include/uapi/linux/bpf.h +2 -1
@@ -2938,7 +2964,8 @@ union bpf_attr {
     FN(probe_read_user),            \
     FN(probe_read_kernel),          \
     FN(probe_read_user_str),        \
-    FN(probe_read_kernel_str),
+    FN(probe_read_kernel_str),      \
+    FN(get_fd_path),
 
   /* integer value in 'imm' field of BPF_CALL instruction selects which helper
    * function eBPF program intends to call
kernel/trace/bpf_trace.c +45 -0
index e5ef4ae9edb5..43a6aa6ad967 100644
@@ -762,6 +762,71 @@ static const struct bpf_func_proto bpf_send_signal_proto = {
     .arg1_type      = ARG_ANYTHING,
   };
 
+BPF_CALL_3(bpf_get_fd_path, char *, dst, u32, size, int, fd)
+{
+    int ret = -EBADF;
+    struct file *f;
+    char *p;
+
+    /* Ensure we're in user context which is safe for the helper to
+     * run. This helper has no business in a kthread.
+     */
+    if (unlikely(in_interrupt() ||
+                 current->flags & (PF_KTHREAD | PF_EXITING))) {
+            ret = -EPERM;
+            goto error;
+    }
+
+    /* Use fget_raw instead of fget to support O_PATH, and it doesn't
+     * have any sleepable code, so it's ok to be here.
+     */
+    f = fget_raw(fd);
+    if (!f)
+            goto error;
+
+    /* For unmountable pseudo filesystem, it seems to have no meaning
+     * to get their fake paths as they don't have path, and to be no
+     * way to validate this function pointer can be always safe to call
+     * in the current context.
+     */
+    if (f->f_path.dentry->d_op && f->f_path.dentry->d_op->d_dname) {
+            ret = -EINVAL;
+            fput(f);
+            goto error;
+    }
+
+    /* After filter unmountable pseudo filesytem, d_path won't call
+     * dentry->d_op->d_name(), the normally path doesn't have any
+     * sleepable code, and despite it uses the current macro to get
+     * fs_struct (current->fs), we've already ensured we're in user
+     * context, so it's ok to be here.
+     */
+    p = d_path(&f->f_path, dst, size);
+    if (IS_ERR(p)) {
+            ret = PTR_ERR(p);
+            fput(f);
+            goto error;
+    }
Quoted 8 lines

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_send_signal_proto = { · line 817

Quoted 8 lines
kernel/trace/bpf_trace.c +8 -0
+
+    ret = strlen(p) + 1;
+    memmove(dst, p, ret);
+    fput(f);
+    return ret;
+
+error:
+    memset(dst, '0', size);

You fill it with 0x30's ...

Quoted 2 lines
Quoted 2 lines
kernel/trace/bpf_trace.c +2 -0
+    return ret;
+}
#27 lore raw

Re: [PATCH bpf-next v13 1/2] bpf: add new helper get_fd_path for mapping a file descriptor to a pathname

In reply to Yonghong Song
Quoted 7 lines

Sorry about a little pedantic. In d_path() function comments, we have:
* Convert a dentry into an ASCII path name. If the entry has been deleted
* the string " (deleted)" is appended. Note that this is ambiguous.

Note that there is a space before "(deleted)". I would like to the above changed to
- a regular full path with " (deleted)" is appended.

Ah, so sorry about this, I should be more preciseness and thanks again for your preciseness and patience.I'll submit another revision to fix this.

Quoted 279 lines · Yonghong Song <[email protected]> 于2019年12月18日周三 上午12:29写道:
Quoted 58 lines · Wenbo Zhang

When people want to identify which file system files are being opened, read, and written to, they can use this helper with file descriptor as input to achieve this goal. Other pseudo filesystems are also supported.

This requirement is mainly discussed here:

   https://github.com/iovisor/bcc/issues/237

v12->v13: addressed Gregg and Yonghong's feedback
- rename to get_fd_path
- refactor code & comment to be clearer and more compliant

v11->v12: addressed Alexei's feedback
- only allow tracepoints to make sure it won't dead lock

v10->v11: addressed Al and Alexei's feedback
- fix missing fput()

v9->v10: addressed Andrii's feedback
- send this patch together with the patch selftests as one patch series

v8->v9:
- format helper description

v7->v8: addressed Alexei's feedback
- use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/
- ensure we're in user context which is safe fot the help to run
- filter unmountable pseudo filesystem, because they don't have real path
- supplement the description of this helper function

v6->v7:
- fix missing signed-off-by line

v5->v6: addressed Andrii's feedback
- avoid unnecessary goto end by having two explicit returns

v4->v5: addressed Andrii and Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names
- when fdget_raw fails, set ret to -EBADF instead of -EINVAL
- remove fdput from fdget_raw's error path
- use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long
- modify the normal path's return value to return copied string length including NUL
- update this helper description's Return bits.

v3->v4: addressed Daniel's feedback
- fix missing fdput()
- move fd2path from kernel/bpf/trace.c to kernel/trace/bpf_trace.c
- move fd2path's test code to another patch
- add comment to explain why use fdget_raw instead of fdget

v2->v3: addressed Yonghong's feedback
- remove unnecessary LOCKDOWN_BPF_READ
- refactor error handling section for enhanced readability
- provide a test case in tools/testing/selftests/bpf

Quoted 6 lines

v1->v2: addressed Daniel's feedback
- fix backward compatibility
- add this helper description
- fix signed-off name

Ack with still the minor issue below, not sure whether another revision will be needed or not or the maintainer can just fix up before merging.

Quoted 16 lines
---
  include/uapi/linux/bpf.h       | 29 +++++++++++++-
  kernel/trace/bpf_trace.c       | 69 ++++++++++++++++++++++++++++++++++
  tools/include/uapi/linux/bpf.h | 29 +++++++++++++-
  3 files changed, 125 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +2 -0
index dbbcf0b02970..c1e4fd286614 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
   *  Return
   *          On success, the strictly positive length of the string, including
   *          the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_fd_path(char *path, u32 size, int fd)

include/uapi/linux/bpf.h union bpf_attr { · line 2835

Quoted 10 lines
include/uapi/linux/bpf.h +10 -0
+ *   Description
+ *           Get **file** atrribute from the current task by *fd*, then call
+ *           **d_path** to get it's absolute path and copy it as string into
+ *           *path* of *size*. Notice the **path** don't support unmountable
+ *           pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *           The *size* must be strictly positive. On success, the helper
+ *           makes sure that the *path* is NUL-terminated, and the buffer
+ *           could be:
+ *           - a regular full path (include mountable fs eg: /proc, /sys)
+ *           - a regular full path with "(deleted)" is appended.

Sorry about a little pedantic. In d_path() function comments, we have:
* Convert a dentry into an ASCII path name. If the entry has been deleted
* the string " (deleted)" is appended. Note that this is ambiguous.

Note that there is a space before "(deleted)". I would like to the above changed to
- a regular full path with " (deleted)" is appended.

Quoted 169 lines
include/uapi/linux/bpf.h +16 -1
+ *           On failure, it is filled with zeroes.
+ *   Return
+ *           On success, returns the length of the copied string INCLUDING
+ *           the trailing '\0'.
+ *
+ *           On failure, the returned value is one of the following:
+ *
+ *           **-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *           **-EBADF** if *fd* is invalid.
+ *
+ *           **-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *           **-ENAMETOOLONG** if full path is longer than *size*
   */
  #define __BPF_FUNC_MAPPER(FN)               \
      FN(unspec),                     \
@@ -2938,7 +2964,8 @@ union bpf_attr {
      FN(probe_read_user),            \
      FN(probe_read_kernel),          \
      FN(probe_read_user_str),        \
-     FN(probe_read_kernel_str),
+     FN(probe_read_kernel_str),      \
+     FN(get_fd_path),
 
  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
   * function eBPF program intends to call
kernel/trace/bpf_trace.c +69 -0
index e5ef4ae9edb5..43a6aa6ad967 100644
@@ -762,6 +762,71 @@ static const struct bpf_func_proto bpf_send_signal_proto = {
      .arg1_type      = ARG_ANYTHING,
  };
 
+BPF_CALL_3(bpf_get_fd_path, char *, dst, u32, size, int, fd)
+{
+     int ret = -EBADF;
+     struct file *f;
+     char *p;
+
+     /* Ensure we're in user context which is safe for the helper to
+      * run. This helper has no business in a kthread.
+      */
+     if (unlikely(in_interrupt() ||
+                  current->flags & (PF_KTHREAD | PF_EXITING))) {
+             ret = -EPERM;
+             goto error;
+     }
+
+     /* Use fget_raw instead of fget to support O_PATH, and it doesn't
+      * have any sleepable code, so it's ok to be here.
+      */
+     f = fget_raw(fd);
+     if (!f)
+             goto error;
+
+     /* For unmountable pseudo filesystem, it seems to have no meaning
+      * to get their fake paths as they don't have path, and to be no
+      * way to validate this function pointer can be always safe to call
+      * in the current context.
+      */
+     if (f->f_path.dentry->d_op && f->f_path.dentry->d_op->d_dname) {
+             ret = -EINVAL;
+             fput(f);
+             goto error;
+     }
+
+     /* After filter unmountable pseudo filesytem, d_path won't call
+      * dentry->d_op->d_name(), the normally path doesn't have any
+      * sleepable code, and despite it uses the current macro to get
+      * fs_struct (current->fs), we've already ensured we're in user
+      * context, so it's ok to be here.
+      */
+     p = d_path(&f->f_path, dst, size);
+     if (IS_ERR(p)) {
+             ret = PTR_ERR(p);
+             fput(f);
+             goto error;
+     }
+
+     ret = strlen(p) + 1;
+     memmove(dst, p, ret);
+     fput(f);
+     return ret;
+
+error:
+     memset(dst, '0', size);
+     return ret;
+}
+
+static const struct bpf_func_proto bpf_get_fd_path_proto = {
+     .func       = bpf_get_fd_path,
+     .gpl_only   = true,
+     .ret_type   = RET_INTEGER,
+     .arg1_type  = ARG_PTR_TO_UNINIT_MEM,
+     .arg2_type  = ARG_CONST_SIZE,
+     .arg3_type  = ARG_ANYTHING,
+};
+
  static const struct bpf_func_proto *
  tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
  {
@@ -953,6 +1018,8 @@ tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
              return &bpf_get_stackid_proto_tp;
      case BPF_FUNC_get_stack:
              return &bpf_get_stack_proto_tp;
+     case BPF_FUNC_get_fd_path:
+             return &bpf_get_fd_path_proto;
      default:
              return tracing_func_proto(func_id, prog);
      }
@@ -1146,6 +1213,8 @@ raw_tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
              return &bpf_get_stackid_proto_raw_tp;
      case BPF_FUNC_get_stack:
              return &bpf_get_stack_proto_raw_tp;
+     case BPF_FUNC_get_fd_path:
+             return &bpf_get_fd_path_proto;
      default:
              return tracing_func_proto(func_id, prog);
      }
tools/include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..c1e4fd286614 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
   *  Return
   *          On success, the strictly positive length of the string, including
   *          the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_fd_path(char *path, u32 size, int fd)
+ *   Description
+ *           Get **file** atrribute from the current task by *fd*, then call
+ *           **d_path** to get it's absolute path and copy it as string into
+ *           *path* of *size*. Notice the **path** don't support unmountable
+ *           pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *           The *size* must be strictly positive. On success, the helper
+ *           makes sure that the *path* is NUL-terminated, and the buffer
+ *           could be:
+ *           - a regular full path (include mountable fs eg: /proc, /sys)
+ *           - a regular full path with "(deleted)" is appended.
+ *           On failure, it is filled with zeroes.
+ *   Return
+ *           On success, returns the length of the copied string INCLUDING
+ *           the trailing '\0'.
+ *
+ *           On failure, the returned value is one of the following:
+ *
+ *           **-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *           **-EBADF** if *fd* is invalid.
+ *
+ *           **-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *           **-ENAMETOOLONG** if full path is longer than *size*
   */
  #define __BPF_FUNC_MAPPER(FN)               \
      FN(unspec),                     \
@@ -2938,7 +2964,8 @@ union bpf_attr {
      FN(probe_read_user),            \
      FN(probe_read_kernel),          \
      FN(probe_read_user_str),        \
-     FN(probe_read_kernel_str),
+     FN(probe_read_kernel_str),      \
+     FN(get_fd_path),
 
  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
   * function eBPF program intends to call
#28 lore raw

v14 cover bpf: adding get_fd_path helper

In reply to Wenbo Zhang

This patch series introduce a bpf helper that can be used to map a file descriptor to a pathname.

This requirement is mainly discussed here:

https://github.com/iovisor/bcc/issues/237

This implementation supports both local and mountable pseudo file systems, and ensure we're in user context which is safe for this helper to run.

Changes since v13:

* Fix this helper's description to be consistent with comments in d_path

* Fix error handling logic fill zeroes not '0's

Changes since v12:

* Rename to get_fd_patch

* Fix test issue on big-endian machines

Changes since v11:

* Only allow tracepoints to make sure it won't dead lock

Changes since v10:

* Fix missing fput

Changes since v9:

* Associate help patch with its selftests patch to this series

* Refactor selftests code for further simplification

Changes since v8:

* Format helper description

Changes since v7:

* Use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/

* Ensure we're in user context which is safe fot the help to run

* Filter unmountable pseudo filesystem, because they don't have real path

* Supplement the description of this helper function

Changes since v6:

* Fix missing signed-off-by line

Changes since v5:

* Refactor helper avoid unnecessary goto end by having two explicit returns

Changes since v4:

* Rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names

* When fdget_raw fails, set ret to -EBADF instead of -EINVAL

* Remove fdput from fdget_raw's error path

* Use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long

* Modify the normal path's return value to return copied string length including NUL

* Update helper description's Return bits.

* Refactor selftests code for further simplification

Changes since v3:

* Remove unnecessary LOCKDOWN_BPF_READ

* Refactor error handling section for enhanced readability

* Provide a test case in tools/testing/selftests/bpf

* Refactor sefltests code to use real global variables instead of maps

Changes since v2:

* Fix backward compatibility

* Add helper description

* Refactor selftests use global data instead of perf_buffer to simplified code

* Fix signed-off name

Wenbo Zhang (2):
bpf: add new helper get_fd_path for mapping a file descriptor to a
pathname
selftests/bpf: test for bpf_get_fd_path() from tracepoint

 include/uapi/linux/bpf.h                      |  29 ++-
 kernel/trace/bpf_trace.c                      |  69 +++++++
 tools/include/uapi/linux/bpf.h                |  29 ++-
 .../selftests/bpf/prog_tests/get_fd_path.c    | 171 ++++++++++++++++++
 .../selftests/bpf/progs/test_get_fd_path.c    |  43 +++++
 5 files changed, 339 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/get_fd_path.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_get_fd_path.c
Signature · 1 line
2.17.1
#29 lore raw

PATCH v14 1/2 bpf: add new helper get_fd_path for mapping a file descriptor to a pathname

When people want to identify which file system files are being opened, read, and written to, they can use this helper with file descriptor as input to achieve this goal. Other pseudo filesystems are also supported.

This requirement is mainly discussed here:

https://github.com/iovisor/bcc/issues/237

v13->v14: addressed Yonghong and Daniel's feedback
- fix this helper's description to be consistent with comments in d_path
- fix error handling logic fill zeroes not '0's

v12->v13: addressed Brendan and Yonghong's feedback
- rename to get_fd_path
- refactor code & comment to be clearer and more compliant

v11->v12: addressed Alexei's feedback
- only allow tracepoints to make sure it won't dead lock

v10->v11: addressed Al and Alexei's feedback
- fix missing fput()

v9->v10: addressed Andrii's feedback
- send this patch together with the patch selftests as one patch series

v8->v9:
- format helper description

v7->v8: addressed Alexei's feedback
- use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/
- ensure we're in user context which is safe fot the help to run
- filter unmountable pseudo filesystem, because they don't have real path
- supplement the description of this helper function

v6->v7:
- fix missing signed-off-by line

v5->v6: addressed Andrii's feedback
- avoid unnecessary goto end by having two explicit returns

v4->v5: addressed Andrii and Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names
- when fdget_raw fails, set ret to -EBADF instead of -EINVAL
- remove fdput from fdget_raw's error path
- use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long
- modify the normal path's return value to return copied string length including NUL
- update this helper description's Return bits.

v3->v4: addressed Daniel's feedback
- fix missing fdput()
- move fd2path from kernel/bpf/trace.c to kernel/trace/bpf_trace.c
- move fd2path's test code to another patch
- add comment to explain why use fdget_raw instead of fdget

v2->v3: addressed Yonghong's feedback
- remove unnecessary LOCKDOWN_BPF_READ
- refactor error handling section for enhanced readability
- provide a test case in tools/testing/selftests/bpf

v1->v2: addressed Daniel's feedback
- fix backward compatibility
- add this helper description
- fix signed-off name

---
 include/uapi/linux/bpf.h       | 29 +++++++++++++-
 kernel/trace/bpf_trace.c       | 69 ++++++++++++++++++++++++++++++++++
 tools/include/uapi/linux/bpf.h | 29 +++++++++++++-
 3 files changed, 125 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..4534ce49f838 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
  * 	Return
  * 		On success, the strictly positive length of the string,	including
  * 		the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_fd_path(char *path, u32 size, int fd)
+ *	Description
+ *		Get **file** atrribute from the current task by *fd*, then call
+ *		**d_path** to get it's absolute path and copy it as string into
+ *		*path* of *size*. Notice the **path** don't support unmountable
+ *		pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *		The *size* must be strictly positive. On success, the helper
+ *		makes sure that the *path* is NUL-terminated, and the buffer
+ *		could be:
+ *		- a regular full path (include mountable fs eg: /proc, /sys)
+ *		- a regular full path with " (deleted)" is appended.
+ *		On failure, it is filled with zeroes.
+ *	Return
+ *		On success, returns the length of the copied string INCLUDING
+ *		the trailing '\0'.
+ *
+ *		On failure, the returned value is one of the following:
+ *
+ *		**-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *		**-EBADF** if *fd* is invalid.
+ *
+ *		**-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *		**-ENAMETOOLONG** if full path is longer than *size*
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -2938,7 +2964,8 @@ union bpf_attr {
 	FN(probe_read_user),		\
 	FN(probe_read_kernel),		\
 	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_fd_path),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
kernel/trace/bpf_trace.c +69 -0

87 lines not on this page · raw message

tools/include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..4534ce49f838 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
  * 	Return
  * 		On success, the strictly positive length of the string,	including
  * 		the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_fd_path(char *path, u32 size, int fd)
+ *	Description
+ *		Get **file** atrribute from the current task by *fd*, then call
+ *		**d_path** to get it's absolute path and copy it as string into
+ *		*path* of *size*. Notice the **path** don't support unmountable
+ *		pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *		The *size* must be strictly positive. On success, the helper
+ *		makes sure that the *path* is NUL-terminated, and the buffer
+ *		could be:
+ *		- a regular full path (include mountable fs eg: /proc, /sys)
+ *		- a regular full path with " (deleted)" is appended.
+ *		On failure, it is filled with zeroes.
+ *	Return
+ *		On success, returns the length of the copied string INCLUDING
+ *		the trailing '\0'.
+ *
+ *		On failure, the returned value is one of the following:
+ *
+ *		**-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *		**-EBADF** if *fd* is invalid.
+ *
+ *		**-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *		**-ENAMETOOLONG** if full path is longer than *size*
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -2938,7 +2964,8 @@ union bpf_attr {
 	FN(probe_read_user),		\
 	FN(probe_read_kernel),		\
 	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_fd_path),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
Signature · 1 line
2.17.1
Acked-by on v14 1/2
#30 lore raw

Re: [PATCH bpf-next v14 1/2] bpf: add new helper get_fd_path for mapping a file descriptor to a pathname

Quoted 62 lines · Wenbo Zhang

When people want to identify which file system files are being opened, read, and written to, they can use this helper with file descriptor as input to achieve this goal. Other pseudo filesystems are also supported.

This requirement is mainly discussed here:

   https://github.com/iovisor/bcc/issues/237

v13->v14: addressed Yonghong and Daniel's feedback
- fix this helper's description to be consistent with comments in d_path
- fix error handling logic fill zeroes not '0's

v12->v13: addressed Brendan and Yonghong's feedback
- rename to get_fd_path
- refactor code & comment to be clearer and more compliant

v11->v12: addressed Alexei's feedback
- only allow tracepoints to make sure it won't dead lock

v10->v11: addressed Al and Alexei's feedback
- fix missing fput()

v9->v10: addressed Andrii's feedback
- send this patch together with the patch selftests as one patch series

v8->v9:
- format helper description

v7->v8: addressed Alexei's feedback
- use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/
- ensure we're in user context which is safe fot the help to run
- filter unmountable pseudo filesystem, because they don't have real path
- supplement the description of this helper function

v6->v7:
- fix missing signed-off-by line

v5->v6: addressed Andrii's feedback
- avoid unnecessary goto end by having two explicit returns

v4->v5: addressed Andrii and Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names
- when fdget_raw fails, set ret to -EBADF instead of -EINVAL
- remove fdput from fdget_raw's error path
- use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long
- modify the normal path's return value to return copied string length including NUL
- update this helper description's Return bits.

v3->v4: addressed Daniel's feedback
- fix missing fdput()
- move fd2path from kernel/bpf/trace.c to kernel/trace/bpf_trace.c
- move fd2path's test code to another patch
- add comment to explain why use fdget_raw instead of fdget

v2->v3: addressed Yonghong's feedback
- remove unnecessary LOCKDOWN_BPF_READ
- refactor error handling section for enhanced readability
- provide a test case in tools/testing/selftests/bpf

Quoted 6 lines

v1->v2: addressed Daniel's feedback
- fix backward compatibility
- add this helper description
- fix signed-off name

maintainer
#31 lore raw

Re: [PATCH bpf-next v14 1/2] bpf: add new helper get_fd_path for mapping a file descriptor to a pathname

In reply to Wenbo Zhang

[ Wenbo, please keep also Al (added here) in the loop since he was providing
feedback on prior submissions as well wrt vfs bits. ]

Quoted 263 lines · Wenbo Zhang

When people want to identify which file system files are being opened, read, and written to, they can use this helper with file descriptor as input to achieve this goal. Other pseudo filesystems are also supported.

This requirement is mainly discussed here:

   https://github.com/iovisor/bcc/issues/237

v13->v14: addressed Yonghong and Daniel's feedback
- fix this helper's description to be consistent with comments in d_path
- fix error handling logic fill zeroes not '0's

v12->v13: addressed Brendan and Yonghong's feedback
- rename to get_fd_path
- refactor code & comment to be clearer and more compliant

v11->v12: addressed Alexei's feedback
- only allow tracepoints to make sure it won't dead lock

v10->v11: addressed Al and Alexei's feedback
- fix missing fput()

v9->v10: addressed Andrii's feedback
- send this patch together with the patch selftests as one patch series

v8->v9:
- format helper description

v7->v8: addressed Alexei's feedback
- use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/
- ensure we're in user context which is safe fot the help to run
- filter unmountable pseudo filesystem, because they don't have real path
- supplement the description of this helper function

v6->v7:
- fix missing signed-off-by line

v5->v6: addressed Andrii's feedback
- avoid unnecessary goto end by having two explicit returns

v4->v5: addressed Andrii and Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names
- when fdget_raw fails, set ret to -EBADF instead of -EINVAL
- remove fdput from fdget_raw's error path
- use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long
- modify the normal path's return value to return copied string length including NUL
- update this helper description's Return bits.

v3->v4: addressed Daniel's feedback
- fix missing fdput()
- move fd2path from kernel/bpf/trace.c to kernel/trace/bpf_trace.c
- move fd2path's test code to another patch
- add comment to explain why use fdget_raw instead of fdget

v2->v3: addressed Yonghong's feedback
- remove unnecessary LOCKDOWN_BPF_READ
- refactor error handling section for enhanced readability
- provide a test case in tools/testing/selftests/bpf

v1->v2: addressed Daniel's feedback
- fix backward compatibility
- add this helper description
- fix signed-off name

---
  include/uapi/linux/bpf.h       | 29 +++++++++++++-
  kernel/trace/bpf_trace.c       | 69 ++++++++++++++++++++++++++++++++++
  tools/include/uapi/linux/bpf.h | 29 +++++++++++++-
  3 files changed, 125 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..4534ce49f838 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
   * 	Return
   * 		On success, the strictly positive length of the string,	including
   * 		the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_fd_path(char *path, u32 size, int fd)
+ *	Description
+ *		Get **file** atrribute from the current task by *fd*, then call
+ *		**d_path** to get it's absolute path and copy it as string into
+ *		*path* of *size*. Notice the **path** don't support unmountable
+ *		pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *		The *size* must be strictly positive. On success, the helper
+ *		makes sure that the *path* is NUL-terminated, and the buffer
+ *		could be:
+ *		- a regular full path (include mountable fs eg: /proc, /sys)
+ *		- a regular full path with " (deleted)" is appended.
+ *		On failure, it is filled with zeroes.
+ *	Return
+ *		On success, returns the length of the copied string INCLUDING
+ *		the trailing '\0'.
+ *
+ *		On failure, the returned value is one of the following:
+ *
+ *		**-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *		**-EBADF** if *fd* is invalid.
+ *
+ *		**-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *		**-ENAMETOOLONG** if full path is longer than *size*
   */
  #define __BPF_FUNC_MAPPER(FN)		\
  	FN(unspec),			\
@@ -2938,7 +2964,8 @@ union bpf_attr {
  	FN(probe_read_user),		\
  	FN(probe_read_kernel),		\
  	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_fd_path),
 
  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
   * function eBPF program intends to call
kernel/trace/bpf_trace.c +69 -0
index e5ef4ae9edb5..a2c18b193141 100644
@@ -762,6 +762,71 @@ static const struct bpf_func_proto bpf_send_signal_proto = {
  	.arg1_type	= ARG_ANYTHING,
  };
 
+BPF_CALL_3(bpf_get_fd_path, char *, dst, u32, size, int, fd)
+{
+	int ret = -EBADF;
+	struct file *f;
+	char *p;
+
+	/* Ensure we're in user context which is safe for the helper to
+	 * run. This helper has no business in a kthread.
+	 */
+	if (unlikely(in_interrupt() ||
+		     current->flags & (PF_KTHREAD | PF_EXITING))) {
+		ret = -EPERM;
+		goto error;
+	}
+
+	/* Use fget_raw instead of fget to support O_PATH, and it doesn't
+	 * have any sleepable code, so it's ok to be here.
+	 */
+	f = fget_raw(fd);
+	if (!f)
+		goto error;
+
+	/* For unmountable pseudo filesystem, it seems to have no meaning
+	 * to get their fake paths as they don't have path, and to be no
+	 * way to validate this function pointer can be always safe to call
+	 * in the current context.
+	 */
+	if (f->f_path.dentry->d_op && f->f_path.dentry->d_op->d_dname) {
+		ret = -EINVAL;
+		fput(f);
+		goto error;
+	}
+
+	/* After filter unmountable pseudo filesytem, d_path won't call
+	 * dentry->d_op->d_name(), the normally path doesn't have any
+	 * sleepable code, and despite it uses the current macro to get
+	 * fs_struct (current->fs), we've already ensured we're in user
+	 * context, so it's ok to be here.
+	 */
+	p = d_path(&f->f_path, dst, size);
+	if (IS_ERR(p)) {
+		ret = PTR_ERR(p);
+		fput(f);
+		goto error;
+	}
+
+	ret = strlen(p) + 1;
+	memmove(dst, p, ret);
+	fput(f);
+	return ret;
+
+error:
+	memset(dst, 0, size);
+	return ret;
+}
+
+static const struct bpf_func_proto bpf_get_fd_path_proto = {
+	.func       = bpf_get_fd_path,
+	.gpl_only   = true,
+	.ret_type   = RET_INTEGER,
+	.arg1_type  = ARG_PTR_TO_UNINIT_MEM,
+	.arg2_type  = ARG_CONST_SIZE,
+	.arg3_type  = ARG_ANYTHING,
+};
+
  static const struct bpf_func_proto *
  tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
  {
@@ -953,6 +1018,8 @@ tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
  		return &bpf_get_stackid_proto_tp;
  	case BPF_FUNC_get_stack:
  		return &bpf_get_stack_proto_tp;
+	case BPF_FUNC_get_fd_path:
+		return &bpf_get_fd_path_proto;
  	default:
  		return tracing_func_proto(func_id, prog);
  	}
@@ -1146,6 +1213,8 @@ raw_tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
  		return &bpf_get_stackid_proto_raw_tp;
  	case BPF_FUNC_get_stack:
  		return &bpf_get_stack_proto_raw_tp;
+	case BPF_FUNC_get_fd_path:
+		return &bpf_get_fd_path_proto;
  	default:
  		return tracing_func_proto(func_id, prog);
  	}
tools/include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..4534ce49f838 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
   * 	Return
   * 		On success, the strictly positive length of the string,	including
   * 		the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_fd_path(char *path, u32 size, int fd)
+ *	Description
+ *		Get **file** atrribute from the current task by *fd*, then call
+ *		**d_path** to get it's absolute path and copy it as string into
+ *		*path* of *size*. Notice the **path** don't support unmountable
+ *		pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *		The *size* must be strictly positive. On success, the helper
+ *		makes sure that the *path* is NUL-terminated, and the buffer
+ *		could be:
+ *		- a regular full path (include mountable fs eg: /proc, /sys)
+ *		- a regular full path with " (deleted)" is appended.
+ *		On failure, it is filled with zeroes.
+ *	Return
+ *		On success, returns the length of the copied string INCLUDING
+ *		the trailing '\0'.
+ *
+ *		On failure, the returned value is one of the following:
+ *
+ *		**-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *		**-EBADF** if *fd* is invalid.
+ *
+ *		**-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *		**-ENAMETOOLONG** if full path is longer than *size*
   */
  #define __BPF_FUNC_MAPPER(FN)		\
  	FN(unspec),			\
@@ -2938,7 +2964,8 @@ union bpf_attr {
  	FN(probe_read_user),		\
  	FN(probe_read_kernel),		\
  	FN(probe_read_user_str),	\
-	FN(probe_read_kernel_str),
+	FN(probe_read_kernel_str),	\
+	FN(get_fd_path),
 
  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
   * function eBPF program intends to call
#32 lore raw

Re: [PATCH bpf-next v14 1/2] bpf: add new helper get_fd_path for mapping a file descriptor to a pathname

Quoted 2 lines

[ Wenbo, please keep also Al (added here) in the loop since he was providing
feedback on prior submissions as well wrt vfs bits. ]

Get it, thank you!

Quoted 269 lines · Daniel Borkmann <[email protected]> 于2019年12月20日周五 上午12:14写道:

[ Wenbo, please keep also Al (added here) in the loop since he was providing
feedback on prior submissions as well wrt vfs bits. ]

Quoted 263 lines · Wenbo Zhang

When people want to identify which file system files are being opened, read, and written to, they can use this helper with file descriptor as input to achieve this goal. Other pseudo filesystems are also supported.

This requirement is mainly discussed here:

   https://github.com/iovisor/bcc/issues/237

v13->v14: addressed Yonghong and Daniel's feedback
- fix this helper's description to be consistent with comments in d_path
- fix error handling logic fill zeroes not '0's

v12->v13: addressed Brendan and Yonghong's feedback
- rename to get_fd_path
- refactor code & comment to be clearer and more compliant

v11->v12: addressed Alexei's feedback
- only allow tracepoints to make sure it won't dead lock

v10->v11: addressed Al and Alexei's feedback
- fix missing fput()

v9->v10: addressed Andrii's feedback
- send this patch together with the patch selftests as one patch series

v8->v9:
- format helper description

v7->v8: addressed Alexei's feedback
- use fget_raw instead of fdget_raw, as fdget_raw is only used inside fs/
- ensure we're in user context which is safe fot the help to run
- filter unmountable pseudo filesystem, because they don't have real path
- supplement the description of this helper function

v6->v7:
- fix missing signed-off-by line

v5->v6: addressed Andrii's feedback
- avoid unnecessary goto end by having two explicit returns

v4->v5: addressed Andrii and Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names
- when fdget_raw fails, set ret to -EBADF instead of -EINVAL
- remove fdput from fdget_raw's error path
- use IS_ERR instead of IS_ERR_OR_NULL as d_path ether returns a pointer into the buffer or an error code if the path was too long
- modify the normal path's return value to return copied string length including NUL
- update this helper description's Return bits.

v3->v4: addressed Daniel's feedback
- fix missing fdput()
- move fd2path from kernel/bpf/trace.c to kernel/trace/bpf_trace.c
- move fd2path's test code to another patch
- add comment to explain why use fdget_raw instead of fdget

v2->v3: addressed Yonghong's feedback
- remove unnecessary LOCKDOWN_BPF_READ
- refactor error handling section for enhanced readability
- provide a test case in tools/testing/selftests/bpf

v1->v2: addressed Daniel's feedback
- fix backward compatibility
- add this helper description
- fix signed-off name

---
  include/uapi/linux/bpf.h       | 29 +++++++++++++-
  kernel/trace/bpf_trace.c       | 69 ++++++++++++++++++++++++++++++++++
  tools/include/uapi/linux/bpf.h | 29 +++++++++++++-
  3 files changed, 125 insertions(+), 2 deletions(-)
include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..4534ce49f838 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
   *  Return
   *          On success, the strictly positive length of the string, including
   *          the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_fd_path(char *path, u32 size, int fd)
+ *   Description
+ *           Get **file** atrribute from the current task by *fd*, then call
+ *           **d_path** to get it's absolute path and copy it as string into
+ *           *path* of *size*. Notice the **path** don't support unmountable
+ *           pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *           The *size* must be strictly positive. On success, the helper
+ *           makes sure that the *path* is NUL-terminated, and the buffer
+ *           could be:
+ *           - a regular full path (include mountable fs eg: /proc, /sys)
+ *           - a regular full path with " (deleted)" is appended.
+ *           On failure, it is filled with zeroes.
+ *   Return
+ *           On success, returns the length of the copied string INCLUDING
+ *           the trailing '\0'.
+ *
+ *           On failure, the returned value is one of the following:
+ *
+ *           **-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *           **-EBADF** if *fd* is invalid.
+ *
+ *           **-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *           **-ENAMETOOLONG** if full path is longer than *size*
   */
  #define __BPF_FUNC_MAPPER(FN)               \
      FN(unspec),                     \
@@ -2938,7 +2964,8 @@ union bpf_attr {
      FN(probe_read_user),            \
      FN(probe_read_kernel),          \
      FN(probe_read_user_str),        \
-     FN(probe_read_kernel_str),
+     FN(probe_read_kernel_str),      \
+     FN(get_fd_path),
 
  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
   * function eBPF program intends to call
kernel/trace/bpf_trace.c +69 -0
index e5ef4ae9edb5..a2c18b193141 100644
@@ -762,6 +762,71 @@ static const struct bpf_func_proto bpf_send_signal_proto = {
      .arg1_type      = ARG_ANYTHING,
  };
 
+BPF_CALL_3(bpf_get_fd_path, char *, dst, u32, size, int, fd)
+{
+     int ret = -EBADF;
+     struct file *f;
+     char *p;
+
+     /* Ensure we're in user context which is safe for the helper to
+      * run. This helper has no business in a kthread.
+      */
+     if (unlikely(in_interrupt() ||
+                  current->flags & (PF_KTHREAD | PF_EXITING))) {
+             ret = -EPERM;
+             goto error;
+     }
+
+     /* Use fget_raw instead of fget to support O_PATH, and it doesn't
+      * have any sleepable code, so it's ok to be here.
+      */
+     f = fget_raw(fd);
+     if (!f)
+             goto error;
+
+     /* For unmountable pseudo filesystem, it seems to have no meaning
+      * to get their fake paths as they don't have path, and to be no
+      * way to validate this function pointer can be always safe to call
+      * in the current context.
+      */
+     if (f->f_path.dentry->d_op && f->f_path.dentry->d_op->d_dname) {
+             ret = -EINVAL;
+             fput(f);
+             goto error;
+     }
+
+     /* After filter unmountable pseudo filesytem, d_path won't call
+      * dentry->d_op->d_name(), the normally path doesn't have any
+      * sleepable code, and despite it uses the current macro to get
+      * fs_struct (current->fs), we've already ensured we're in user
+      * context, so it's ok to be here.
+      */
+     p = d_path(&f->f_path, dst, size);
+     if (IS_ERR(p)) {
+             ret = PTR_ERR(p);
+             fput(f);
+             goto error;
+     }
+
+     ret = strlen(p) + 1;
+     memmove(dst, p, ret);
+     fput(f);
+     return ret;
+
+error:
+     memset(dst, 0, size);
+     return ret;
+}
+
+static const struct bpf_func_proto bpf_get_fd_path_proto = {
+     .func       = bpf_get_fd_path,
+     .gpl_only   = true,
+     .ret_type   = RET_INTEGER,
+     .arg1_type  = ARG_PTR_TO_UNINIT_MEM,
+     .arg2_type  = ARG_CONST_SIZE,
+     .arg3_type  = ARG_ANYTHING,
+};
+
  static const struct bpf_func_proto *
  tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
  {
@@ -953,6 +1018,8 @@ tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
              return &bpf_get_stackid_proto_tp;
      case BPF_FUNC_get_stack:
              return &bpf_get_stack_proto_tp;
+     case BPF_FUNC_get_fd_path:
+             return &bpf_get_fd_path_proto;
      default:
              return tracing_func_proto(func_id, prog);
      }
@@ -1146,6 +1213,8 @@ raw_tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
              return &bpf_get_stackid_proto_raw_tp;
      case BPF_FUNC_get_stack:
              return &bpf_get_stack_proto_raw_tp;
+     case BPF_FUNC_get_fd_path:
+             return &bpf_get_fd_path_proto;
      default:
              return tracing_func_proto(func_id, prog);
      }
tools/include/uapi/linux/bpf.h +28 -1
index dbbcf0b02970..4534ce49f838 100644
@@ -2821,6 +2821,32 @@ union bpf_attr {
   *  Return
   *          On success, the strictly positive length of the string, including
   *          the trailing NUL character. On error, a negative value.
+ *
+ * int bpf_get_fd_path(char *path, u32 size, int fd)
+ *   Description
+ *           Get **file** atrribute from the current task by *fd*, then call
+ *           **d_path** to get it's absolute path and copy it as string into
+ *           *path* of *size*. Notice the **path** don't support unmountable
+ *           pseudo filesystems as they don't have path (eg: SOCKFS, PIPEFS).
+ *           The *size* must be strictly positive. On success, the helper
+ *           makes sure that the *path* is NUL-terminated, and the buffer
+ *           could be:
+ *           - a regular full path (include mountable fs eg: /proc, /sys)
+ *           - a regular full path with " (deleted)" is appended.
+ *           On failure, it is filled with zeroes.
+ *   Return
+ *           On success, returns the length of the copied string INCLUDING
+ *           the trailing '\0'.
+ *
+ *           On failure, the returned value is one of the following:
+ *
+ *           **-EPERM** if no permission to get the path (eg: in irq ctx).
+ *
+ *           **-EBADF** if *fd* is invalid.
+ *
+ *           **-EINVAL** if *fd* corresponds to a unmountable pseudo fs
+ *
+ *           **-ENAMETOOLONG** if full path is longer than *size*
   */
  #define __BPF_FUNC_MAPPER(FN)               \
      FN(unspec),                     \
@@ -2938,7 +2964,8 @@ union bpf_attr {
      FN(probe_read_user),            \
      FN(probe_read_kernel),          \
      FN(probe_read_user_str),        \
-     FN(probe_read_kernel_str),
+     FN(probe_read_kernel_str),      \
+     FN(get_fd_path),
 
  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
   * function eBPF program intends to call
Tested-by
#33 lore raw

Re: [PATCH bpf-next v14 1/2] bpf: add new helper get_fd_path for mapping a file descriptor to a pathname

Quoted 4 lines · Wenbo Zhang
Quoted 2 lines

[ Wenbo, please keep also Al (added here) in the loop since he was providing
feedback on prior submissions as well wrt vfs bits. ]

Get it, thank you!

hi,
is this stuck on review? I'd like to see this merged ;-) we have bpftrace change using it already.. from that side:

thanks,
jirka

#34 lore raw

Re: [PATCH bpf-next v14 1/2] bpf: add new helper get_fd_path for mapping a file descriptor to a pathname

Quoted 9 lines · Jiri Olsa
Quoted 4 lines · Wenbo Zhang
Quoted 2 lines

[ Wenbo, please keep also Al (added here) in the loop since he was providing
feedback on prior submissions as well wrt vfs bits. ]

Get it, thank you!

hi,
is this stuck on review? I'd like to see this merged ;-)

Is this still waiting on someone? I'm writing some docs on analyzing file systems via syscall tracing and this will be a big improvement. Thanks,

Brendan

Quoted 8 lines

we have bpftrace change using it already.. from that side:

thanks,
jirka

Signature · 1 line
Brendan Gregg, Senior Performance Architect, Netflix
maintainer
#35 lore raw

Re: [PATCH bpf-next v14 1/2] bpf: add new helper get_fd_path for mapping a file descriptor to a pathname

Quoted 5 lines · Brendan Gregg
Quoted 4 lines · Jiri Olsa
Quoted 3 lines · Wenbo Zhang
Quoted 2 lines

[ Wenbo, please keep also Al (added here) in the loop since he was providing
feedback on prior submissions as well wrt vfs bits. ]

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

Get it, thank you!

hi,
is this stuck on review? I'd like to see this merged ;-)

Is this still waiting on someone? I'm writing some docs on analyzing file systems via syscall tracing and this will be a big improvement.

It was waiting on final review/ACK from vfs folks, but given that didn't happen so far :/, this series should get rebased for proceeding with merge.

#36 lore raw

Re: [PATCH bpf-next v14 1/2] bpf: add new helper get_fd_path for mapping a file descriptor to a pathname

Quoted 8 lines · Daniel Borkmann
Quoted 7 lines · Brendan Gregg
Quoted 6 lines · Jiri Olsa
Quoted 4 lines · Wenbo Zhang
Quoted 2 lines

[ Wenbo, please keep also Al (added here) in the loop since he was providing
feedback on prior submissions as well wrt vfs bits. ]

Get it, thank you!

Quoted 9 lines
Quoted 5 lines
Quoted 2 lines

hi,
is this stuck on review? I'd like to see this merged ;-)

Is this still waiting on someone? I'm writing some docs on analyzing file systems via syscall tracing and this will be a big improvement.

It was waiting on final review/ACK from vfs folks, but given that didn't happen so far :/, this series should get rebased for proceeding with merge.

Al Viro, any chance you could check on the latest version?

thanks,
jirka

#37 lore raw

Re: [PATCH bpf-next v14 1/2] bpf: add new helper get_fd_path for mapping a file descriptor to a pathname

Hi Daniel,

I find https://patchwork.ozlabs.org/project/netdev/patch/7464919bd9c15f2496ca29dceb6a4048b3199774.1576629200.git.ethercflow@gmail.com/
this PR's current state is Awaiting Upstream. I don't know much about
this state. I want to ask if this PR will be merged.

Thank you
Wenbo

Quoted 25 lines · Jiri Olsa <[email protected]> 于2020年2月12日周三 下午11:22写道:
Quoted 8 lines · Daniel Borkmann
Quoted 7 lines · Brendan Gregg
Quoted 6 lines · Jiri Olsa
Quoted 4 lines · Wenbo Zhang
Quoted 2 lines

[ Wenbo, please keep also Al (added here) in the loop since he was providing
feedback on prior submissions as well wrt vfs bits. ]

Get it, thank you!

Quoted 9 lines
Quoted 5 lines
Quoted 2 lines

hi,
is this stuck on review? I'd like to see this merged ;-)

Is this still waiting on someone? I'm writing some docs on analyzing file systems via syscall tracing and this will be a big improvement.

It was waiting on final review/ACK from vfs folks, but given that didn't happen so far :/, this series should get rebased for proceeding with merge.

Al Viro, any chance you could check on the latest version?

thanks,
jirka

#38 lore raw

Re: [PATCH bpf-next v14 1/2] bpf: add new helper get_fd_path for mapping a file descriptor to a pathname

Quoted 6 lines · Wenbo Zhang

Hi Daniel,

I find https://patchwork.ozlabs.org/project/netdev/patch/7464919bd9c15f2496ca29dceb6a4048b3199774.1576629200.git.ethercflow@gmail.com/
this PR's current state is Awaiting Upstream. I don't know much about
this state. I want to ask if this PR will be merged.

This one won't be merged.
Jiri had sent patches based on whitelist approach. That's a proper direction to address locking concerns.

#39 lore raw

Re: [PATCH bpf-next v14 1/2] bpf: add new helper get_fd_path for mapping a file descriptor to a pathname

Get it, I'll search Jiri's patches to see how to use that. Thanks.

Quoted 12 lines · Alexei Starovoitov <[email protected]> 于2020年6月2日周二 上午12:38写道:
Quoted 6 lines · Wenbo Zhang

Hi Daniel,

I find https://patchwork.ozlabs.org/project/netdev/patch/7464919bd9c15f2496ca29dceb6a4048b3199774.1576629200.git.ethercflow@gmail.com/
this PR's current state is Awaiting Upstream. I don't know much about
this state. I want to ask if this PR will be merged.

This one won't be merged.
Jiri had sent patches based on whitelist approach. That's a proper direction to address locking concerns.

#40 lore raw

Re: [PATCH bpf-next v14 1/2] bpf: add new helper get_fd_path for mapping a file descriptor to a pathname

Quoted 1 line · Wenbo Zhang

Get it, I'll search Jiri's patches to see how to use that. Thanks.

I'll cc you in the next post

jirka

Quoted 15 lines
Quoted 12 lines · Alexei Starovoitov <[email protected]> 于2020年6月2日周二 上午12:38写道:
Quoted 6 lines · Wenbo Zhang

Hi Daniel,

I find https://patchwork.ozlabs.org/project/netdev/patch/7464919bd9c15f2496ca29dceb6a4048b3199774.1576629200.git.ethercflow@gmail.com/
this PR's current state is Awaiting Upstream. I don't know much about
this state. I want to ask if this PR will be merged.

This one won't be merged.
Jiri had sent patches based on whitelist approach. That's a proper direction to address locking concerns.

#41 lore raw

PATCH v14 2/2 selftests/bpf: test for bpf_get_fd_path() from tracepoint

In reply to Wenbo Zhang

trace fstat events by tracepoint syscalls/sys_enter_newfstat, and handle events only produced by test_file_fd_path, which call fstat on several different types of files to test bpf_fd_file_path's feature.

v5->v6: addressed Gregg and Yonghong's feedback
- rename to get_fd_path
- change sys_enter_newfstat_args's fd type to long to fix issue on big-endian machines

v4->v5: addressed Andrii's feedback
- pass NULL for opts as bpf_object__open_file's PARAM2, as not really using any
- modify patch subject to keep up with test code
- as this test is single-threaded, so use getpid instead of SYS_gettid
- remove unnecessary parens around check which after if (i < 3)
- in kern use bpf_get_current_pid_tgid() >> 32 to fit getpid() in userspace part
- with the patch adding helper as one patch series

v3->v4: addressed Andrii's feedback
- use a set of fd instead of fds array
- use global variables instead of maps (in v3, I mistakenly thought that the bpf maps are global variables.)
- remove uncessary global variable path_info_index
- remove fd compare as the fstat's order is fixed

v2->v3: addressed Andrii's feedback
- use global data instead of perf_buffer to simplified code

v1->v2: addressed Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names

---
 .../selftests/bpf/prog_tests/get_fd_path.c    | 171 ++++++++++++++++++
 .../selftests/bpf/progs/test_get_fd_path.c    |  43 +++++
 2 files changed, 214 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/get_fd_path.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_get_fd_path.c
tools/testing/selftests/bpf/prog_tests/get_fd_path.c new file +171 -0
new file mode 100644
index 000000000000..2846f0a4e84b
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <test_progs.h>
+#include <sys/stat.h>
+#include <linux/sched.h>
+#include <sys/syscall.h>
+
+#define MAX_PATH_LEN		128
+#define MAX_FDS			7
+#define MAX_EVENT_NUM		16
+
+static struct fd_path_test_data {
+	pid_t pid;
+	__u32 cnt;
+	__u32 fds[MAX_EVENT_NUM];
+	char paths[MAX_EVENT_NUM][MAX_PATH_LEN];
+} src, dst;
+
+static int set_pathname(int fd)
+{
+	char buf[MAX_PATH_LEN];
+
+	snprintf(buf, MAX_PATH_LEN, "/proc/%d/fd/%d", src.pid, fd);
+	src.fds[src.cnt] = fd;
+	return readlink(buf, src.paths[src.cnt++], MAX_PATH_LEN);
+}
+
+static int trigger_fstat_events(pid_t pid)
+{
+	int pipefd[2] = { -1, -1 };
+	int sockfd = -1, procfd = -1, devfd = -1;
+	int localfd = -1, indicatorfd = -1;
+	struct stat fileStat;
+	int ret = -1;
+
+	/* unmountable pseudo-filesystems */
+	if (CHECK_FAIL(pipe(pipefd) < 0))
+		return ret;
+	/* unmountable pseudo-filesystems */
+	sockfd = socket(AF_INET, SOCK_STREAM, 0);
+	if (CHECK_FAIL(sockfd < 0))
+		goto out_close;
+	/* mountable pseudo-filesystems */
+	procfd = open("/proc/self/comm", O_RDONLY);
+	if (CHECK_FAIL(procfd < 0))
+		goto out_close;
+	devfd = open("/dev/urandom", O_RDONLY);
+	if (CHECK_FAIL(devfd < 0))
+		goto out_close;
+	localfd = open("/tmp/fd2path_loadgen.txt", O_CREAT | O_RDONLY);
+	if (CHECK_FAIL(localfd < 0))
+		goto out_close;
+	/* bpf_get_fd_path will return path with (deleted) */
+	remove("/tmp/fd2path_loadgen.txt");
+	indicatorfd = open("/tmp/", O_PATH);
+	if (CHECK_FAIL(indicatorfd < 0))
+		goto out_close;
+
+	src.pid = pid;
+
+	ret = set_pathname(pipefd[0]);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(pipefd[1]);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(sockfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(procfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(devfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(localfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(indicatorfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+
+	fstat(pipefd[0], &fileStat);
+	fstat(pipefd[1], &fileStat);
+	fstat(sockfd, &fileStat);
+	fstat(procfd, &fileStat);
+	fstat(devfd, &fileStat);
+	fstat(localfd, &fileStat);
+	fstat(indicatorfd, &fileStat);
+
+out_close:
+	close(indicatorfd);
+	close(localfd);
+	close(devfd);
+	close(procfd);
+	close(sockfd);
+	close(pipefd[1]);
+	close(pipefd[0]);
+
+	return ret;
+}
+
+void test_get_fd_path(void)
+{
+	const char *prog_name = "tracepoint/syscalls/sys_enter_newfstat";
+	const char *obj_file = "test_get_fd_path.o";
+	int err, results_map_fd, duration = 0;
+	struct bpf_program *tp_prog = NULL;
+	struct bpf_link *tp_link = NULL;
+	struct bpf_object *obj = NULL;
+	const int zero = 0;
+
+	obj = bpf_object__open_file(obj_file, NULL);
+	if (CHECK(IS_ERR(obj), "obj_open_file", "err %ld\n", PTR_ERR(obj)))
+		return;
+
+	tp_prog = bpf_object__find_program_by_title(obj, prog_name);
+	if (CHECK(!tp_prog, "find_tp",
+		  "prog '%s' not found\n", prog_name))
+		goto cleanup;
+
+	err = bpf_object__load(obj);
+	if (CHECK(err, "obj_load", "err %d\n", err))
+		goto cleanup;
+
+	results_map_fd = bpf_find_map(__func__, obj, "test_get.bss");
+	if (CHECK(results_map_fd < 0, "find_bss_map",
+		  "err %d\n", results_map_fd))
+		goto cleanup;
+
+	tp_link = bpf_program__attach_tracepoint(tp_prog, "syscalls",
+						 "sys_enter_newfstat");
+	if (CHECK(IS_ERR(tp_link), "attach_tp",
+		  "err %ld\n", PTR_ERR(tp_link))) {
+		tp_link = NULL;
+		goto cleanup;
+	}
+
+	dst.pid = getpid();
+	err = bpf_map_update_elem(results_map_fd, &zero, &dst, 0);
+	if (CHECK(err, "update_elem",
+		  "failed to set pid filter: %d\n", err))
+		goto cleanup;
+
+	err = trigger_fstat_events(dst.pid);
+	if (CHECK_FAIL(err < 0))
+		goto cleanup;
+
+	err = bpf_map_lookup_elem(results_map_fd, &zero, &dst);
+	if (CHECK(err, "get_results",
+		  "failed to get results: %d\n", err))
+		goto cleanup;
+
+	for (int i = 0; i < MAX_FDS; i++) {
+		if (i < 3)
+			CHECK((dst.paths[i][0] != 0), "get_fd_path",
+			      "failed to filter fs [%d]: %u(%s) vs %u(%s)\n",
+			      i, src.fds[i], src.paths[i], dst.fds[i],
+			      dst.paths[i]);
+		else
+			CHECK(strncmp(src.paths[i], dst.paths[i], MAX_PATH_LEN),
+			      "get_fd_path",
+			      "failed to get path[%d]: %u(%s) vs %u(%s)\n",
+			      i, src.fds[i], src.paths[i], dst.fds[i],
+			      dst.paths[i]);
+	}
+
+cleanup:
+	bpf_link__destroy(tp_link);
+	bpf_object__close(obj);
+}
tools/testing/selftests/bpf/progs/test_get_fd_path.c new file +43 -0

43 lines not on this page · raw message

Signature · 1 line
2.17.1
Acked-by on v14 2/2
#42 lore raw

Re: [PATCH bpf-next v14 2/2] selftests/bpf: test for bpf_get_fd_path() from tracepoint

Quoted 26 lines · Wenbo Zhang

trace fstat events by tracepoint syscalls/sys_enter_newfstat, and handle events only produced by test_file_fd_path, which call fstat on several different types of files to test bpf_fd_file_path's feature.

v5->v6: addressed Gregg and Yonghong's feedback
- rename to get_fd_path
- change sys_enter_newfstat_args's fd type to long to fix issue on big-endian machines

v4->v5: addressed Andrii's feedback
- pass NULL for opts as bpf_object__open_file's PARAM2, as not really using any
- modify patch subject to keep up with test code
- as this test is single-threaded, so use getpid instead of SYS_gettid
- remove unnecessary parens around check which after if (i < 3)
- in kern use bpf_get_current_pid_tgid() >> 32 to fit getpid() in userspace part
- with the patch adding helper as one patch series

v3->v4: addressed Andrii's feedback
- use a set of fd instead of fds array
- use global variables instead of maps (in v3, I mistakenly thought that the bpf maps are global variables.)
- remove uncessary global variable path_info_index
- remove fd compare as the fstat's order is fixed

Quoted 8 lines

v2->v3: addressed Andrii's feedback
- use global data instead of perf_buffer to simplified code

v1->v2: addressed Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names

#43 lore raw

PATCH v13 2/2 selftests/bpf: test for bpf_get_fd_path() from tracepoint

In reply to Wenbo Zhang

trace fstat events by tracepoint syscalls/sys_enter_newfstat, and handle events only produced by test_file_fd_path, which call fstat on several different types of files to test bpf_fd_file_path's feature.

v5->v6: addressed Gregg and Yonghong's feedback
- rename to get_fd_path
- change sys_enter_newfstat_args's fd type to long to fix issue on big-endian machines

v4->v5: addressed Andrii's feedback
- pass NULL for opts as bpf_object__open_file's PARAM2, as not really using any
- modify patch subject to keep up with test code
- as this test is single-threaded, so use getpid instead of SYS_gettid
- remove unnecessary parens around check which after if (i < 3)
- in kern use bpf_get_current_pid_tgid() >> 32 to fit getpid() in userspace part
- with the patch adding helper as one patch series

v3->v4: addressed Andrii's feedback
- use a set of fd instead of fds array
- use global variables instead of maps (in v3, I mistakenly thought that the bpf maps are global variables.)
- remove uncessary global variable path_info_index
- remove fd compare as the fstat's order is fixed

v2->v3: addressed Andrii's feedback
- use global data instead of perf_buffer to simplified code

v1->v2: addressed Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names

---
 .../selftests/bpf/prog_tests/get_fd_path.c    | 171 ++++++++++++++++++
 .../selftests/bpf/progs/test_get_fd_path.c    |  43 +++++
 2 files changed, 214 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/get_fd_path.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_get_fd_path.c
tools/testing/selftests/bpf/prog_tests/get_fd_path.c new file +171 -0
new file mode 100644
index 000000000000..5cf58028a8d2
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <test_progs.h>
+#include <sys/stat.h>
+#include <linux/sched.h>
+#include <sys/syscall.h>
+
+#define MAX_PATH_LEN		128
+#define MAX_FDS			7
+#define MAX_EVENT_NUM		16
+
+static struct fd_path_test_data {
+	pid_t pid;
+	__u32 cnt;
+	__u32 fds[MAX_EVENT_NUM];
+	char paths[MAX_EVENT_NUM][MAX_PATH_LEN];
+} src, dst;
+
+static int set_pathname(int fd)
+{
+	char buf[MAX_PATH_LEN];
+
+	snprintf(buf, MAX_PATH_LEN, "/proc/%d/fd/%d", src.pid, fd);
+	src.fds[src.cnt] = fd;
+	return readlink(buf, src.paths[src.cnt++], MAX_PATH_LEN);
+}
+
+static int trigger_fstat_events(pid_t pid)
+{
+	int pipefd[2] = { -1, -1 };
+	int sockfd = -1, procfd = -1, devfd = -1;
+	int localfd = -1, indicatorfd = -1;
+	struct stat fileStat;
+	int ret = -1;
+
+	/* unmountable pseudo-filesystems */
+	if (CHECK_FAIL(pipe(pipefd) < 0))
+		return ret;
+	/* unmountable pseudo-filesystems */
+	sockfd = socket(AF_INET, SOCK_STREAM, 0);
+	if (CHECK_FAIL(sockfd < 0))
+		goto out_close;
+	/* mountable pseudo-filesystems */
+	procfd = open("/proc/self/comm", O_RDONLY);
+	if (CHECK_FAIL(procfd < 0))
+		goto out_close;
+	devfd = open("/dev/urandom", O_RDONLY);
+	if (CHECK_FAIL(devfd < 0))
+		goto out_close;
+	localfd = open("/tmp/fd2path_loadgen.txt", O_CREAT | O_RDONLY);
+	if (CHECK_FAIL(localfd < 0))
+		goto out_close;
+	/* bpf_get_fd_path will return path with (deleted) */
+	remove("/tmp/fd2path_loadgen.txt");
+	indicatorfd = open("/tmp/", O_PATH);
+	if (CHECK_FAIL(indicatorfd < 0))
+		goto out_close;
+
+	src.pid = pid;
+
+	ret = set_pathname(pipefd[0]);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(pipefd[1]);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(sockfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(procfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(devfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(localfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(indicatorfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+
+	fstat(pipefd[0], &fileStat);
+	fstat(pipefd[1], &fileStat);
+	fstat(sockfd, &fileStat);
+	fstat(procfd, &fileStat);
+	fstat(devfd, &fileStat);
+	fstat(localfd, &fileStat);
+	fstat(indicatorfd, &fileStat);
+
+out_close:
+	close(indicatorfd);
+	close(localfd);
+	close(devfd);
+	close(procfd);
+	close(sockfd);
+	close(pipefd[1]);
+	close(pipefd[0]);
+
+	return ret;
+}
+
+void test_get_fd_path(void)
+{
+	const char *prog_name = "tracepoint/syscalls/sys_enter_newfstat";
+	const char *obj_file = "test_get_fd_path.o";
+	int err, results_map_fd, duration = 0;
+	struct bpf_program *tp_prog = NULL;
+	struct bpf_link *tp_link = NULL;
+	struct bpf_object *obj = NULL;
+	const int zero = 0;
+
+	obj = bpf_object__open_file(obj_file, NULL);
+	if (CHECK(IS_ERR(obj), "obj_open_file", "err %ld\n", PTR_ERR(obj)))
+		return;
+
+	tp_prog = bpf_object__find_program_by_title(obj, prog_name);
+	if (CHECK(!tp_prog, "find_tp",
+		  "prog '%s' not found\n", prog_name))
+		goto cleanup;
+
+	err = bpf_object__load(obj);
+	if (CHECK(err, "obj_load", "err %d\n", err))
+		goto cleanup;
+
+	results_map_fd = bpf_find_map(__func__, obj, "test_get.bss");
+	if (CHECK(results_map_fd < 0, "find_bss_map",
+		  "err %d\n", results_map_fd))
+		goto cleanup;
+
+	tp_link = bpf_program__attach_tracepoint(tp_prog, "syscalls",
+						 "sys_enter_newfstat");
+	if (CHECK(IS_ERR(tp_link), "attach_tp",
+		  "err %ld\n", PTR_ERR(tp_link))) {
+		tp_link = NULL;
+		goto cleanup;
+	}
+
+	dst.pid = getpid();
+	err = bpf_map_update_elem(results_map_fd, &zero, &dst, 0);
+	if (CHECK(err, "update_elem",
+		  "failed to set pid filter: %d\n", err))
+		goto cleanup;
+
+	err = trigger_fstat_events(dst.pid);
+	if (CHECK_FAIL(err < 0))
+		goto cleanup;
+
+	err = bpf_map_lookup_elem(results_map_fd, &zero, &dst);
+	if (CHECK(err, "get_results",
+		  "failed to get results: %d\n", err))
+		goto cleanup;
+
+	for (int i = 0; i < MAX_FDS; i++) {
+		if (i < 3)
+			CHECK((dst.paths[i][0] != '0'), "get_fd_path",
+			      "failed to filter fs [%d]: %u(%s) vs %u(%s)\n",
+			      i, src.fds[i], src.paths[i], dst.fds[i],
+			      dst.paths[i]);
+		else
+			CHECK(strncmp(src.paths[i], dst.paths[i], MAX_PATH_LEN),
+			      "get_fd_path",
+			      "failed to get path[%d]: %u(%s) vs %u(%s)\n",
+			      i, src.fds[i], src.paths[i], dst.fds[i],
+			      dst.paths[i]);
+	}
+
+cleanup:
+	bpf_link__destroy(tp_link);
+	bpf_object__close(obj);
+}
tools/testing/selftests/bpf/progs/test_get_fd_path.c new file +43 -0

43 lines not on this page · raw message

Signature · 1 line
2.17.1
Acked-by on v13 2/2
#44 lore raw

Re: [PATCH bpf-next v13 2/2] selftests/bpf: test for bpf_get_fd_path() from tracepoint

Quoted 26 lines · Wenbo Zhang

trace fstat events by tracepoint syscalls/sys_enter_newfstat, and handle events only produced by test_file_fd_path, which call fstat on several different types of files to test bpf_fd_file_path's feature.

v5->v6: addressed Gregg and Yonghong's feedback
- rename to get_fd_path
- change sys_enter_newfstat_args's fd type to long to fix issue on big-endian machines

v4->v5: addressed Andrii's feedback
- pass NULL for opts as bpf_object__open_file's PARAM2, as not really using any
- modify patch subject to keep up with test code
- as this test is single-threaded, so use getpid instead of SYS_gettid
- remove unnecessary parens around check which after if (i < 3)
- in kern use bpf_get_current_pid_tgid() >> 32 to fit getpid() in userspace part
- with the patch adding helper as one patch series

v3->v4: addressed Andrii's feedback
- use a set of fd instead of fds array
- use global variables instead of maps (in v3, I mistakenly thought that the bpf maps are global variables.)
- remove uncessary global variable path_info_index
- remove fd compare as the fstat's order is fixed

Quoted 8 lines

v2->v3: addressed Andrii's feedback
- use global data instead of perf_buffer to simplified code

v1->v2: addressed Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names

#45 lore raw

PATCH v12 2/2 selftests/bpf: test for bpf_get_file_path() from tracepoint

In reply to Wenbo Zhang

trace fstat events by tracepoint syscalls/sys_enter_newfstat, and handle events only produced by test_file_get_path, which call fstat on several different types of files to test bpf_get_file_path's feature.

v4->v5: addressed Andrii's feedback
- pass NULL for opts as bpf_object__open_file's PARAM2, as not really using any
- modify patch subject to keep up with test code
- as this test is single-threaded, so use getpid instead of SYS_gettid
- remove unnecessary parens around check which after if (i < 3)
- in kern use bpf_get_current_pid_tgid() >> 32 to fit getpid() in userspace part
- with the patch adding helper as one patch series

v3->v4: addressed Andrii's feedback
- use a set of fd instead of fds array
- use global variables instead of maps (in v3, I mistakenly thought that the bpf maps are global variables.)
- remove uncessary global variable path_info_index
- remove fd compare as the fstat's order is fixed

v2->v3: addressed Andrii's feedback
- use global data instead of perf_buffer to simplified code

v1->v2: addressed Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names

---
 .../selftests/bpf/prog_tests/get_file_path.c  | 171 ++++++++++++++++++
 .../selftests/bpf/progs/test_get_file_path.c  |  43 +++++
 2 files changed, 214 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/get_file_path.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_get_file_path.c
tools/testing/selftests/bpf/prog_tests/get_file_path.c new file +171 -0
new file mode 100644
index 000000000000..7ec11e43e0fc
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <test_progs.h>
+#include <sys/stat.h>
+#include <linux/sched.h>
+#include <sys/syscall.h>
+
+#define MAX_PATH_LEN		128
+#define MAX_FDS			7
+#define MAX_EVENT_NUM		16
+
+static struct file_path_test_data {
+	pid_t pid;
+	__u32 cnt;
+	__u32 fds[MAX_EVENT_NUM];
+	char paths[MAX_EVENT_NUM][MAX_PATH_LEN];
+} src, dst;
+
+static inline int set_pathname(int fd)
+{
+	char buf[MAX_PATH_LEN];
+
+	snprintf(buf, MAX_PATH_LEN, "/proc/%d/fd/%d", src.pid, fd);
+	src.fds[src.cnt] = fd;
+	return readlink(buf, src.paths[src.cnt++], MAX_PATH_LEN);
+}
+
+static int trigger_fstat_events(pid_t pid)
+{
+	int pipefd[2] = { -1, -1 };
+	int sockfd = -1, procfd = -1, devfd = -1;
+	int localfd = -1, indicatorfd = -1;
+	struct stat fileStat;
+	int ret = -1;
+
+	/* unmountable pseudo-filesystems */
+	if (CHECK_FAIL(pipe(pipefd) < 0))
+		return ret;
+	/* unmountable pseudo-filesystems */
+	sockfd = socket(AF_INET, SOCK_STREAM, 0);
+	if (CHECK_FAIL(sockfd < 0))
+		goto out_close;
+	/* mountable pseudo-filesystems */
+	procfd = open("/proc/self/comm", O_RDONLY);
+	if (CHECK_FAIL(procfd < 0))
+		goto out_close;
+	devfd = open("/dev/urandom", O_RDONLY);
+	if (CHECK_FAIL(devfd < 0))
+		goto out_close;
+	localfd = open("/tmp/fd2path_loadgen.txt", O_CREAT | O_RDONLY);
+	if (CHECK_FAIL(localfd < 0))
+		goto out_close;
+	/* bpf_get_file_path will return path with (deleted) */
+	remove("/tmp/fd2path_loadgen.txt");
+	indicatorfd = open("/tmp/", O_PATH);
+	if (CHECK_FAIL(indicatorfd < 0))
+		goto out_close;
+
+	src.pid = pid;
+
+	ret = set_pathname(pipefd[0]);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(pipefd[1]);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(sockfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(procfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(devfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(localfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(indicatorfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+
+	fstat(pipefd[0], &fileStat);
+	fstat(pipefd[1], &fileStat);
+	fstat(sockfd, &fileStat);
+	fstat(procfd, &fileStat);
+	fstat(devfd, &fileStat);
+	fstat(localfd, &fileStat);
+	fstat(indicatorfd, &fileStat);
+
+out_close:
+	close(indicatorfd);
+	close(localfd);
+	close(devfd);
+	close(procfd);
+	close(sockfd);
+	close(pipefd[1]);
+	close(pipefd[0]);
+
+	return ret;
+}
+
+void test_get_file_path(void)
+{
+	const char *prog_name = "tracepoint/syscalls/sys_enter_newfstat";
+	const char *obj_file = "test_get_file_path.o";
+	int err, results_map_fd, duration = 0;
+	struct bpf_program *tp_prog = NULL;
+	struct bpf_link *tp_link = NULL;
+	struct bpf_object *obj = NULL;
+	const int zero = 0;
+
+	obj = bpf_object__open_file(obj_file, NULL);
+	if (CHECK(IS_ERR(obj), "obj_open_file", "err %ld\n", PTR_ERR(obj)))
+		return;
+
+	tp_prog = bpf_object__find_program_by_title(obj, prog_name);
+	if (CHECK(!tp_prog, "find_tp",
+		  "prog '%s' not found\n", prog_name))
+		goto cleanup;
+
+	err = bpf_object__load(obj);
+	if (CHECK(err, "obj_load", "err %d\n", err))
+		goto cleanup;
+
+	results_map_fd = bpf_find_map(__func__, obj, "test_get.bss");
+	if (CHECK(results_map_fd < 0, "find_bss_map",
+		  "err %d\n", results_map_fd))
+		goto cleanup;
+
+	tp_link = bpf_program__attach_tracepoint(tp_prog, "syscalls",
+						 "sys_enter_newfstat");
+	if (CHECK(IS_ERR(tp_link), "attach_tp",
+		  "err %ld\n", PTR_ERR(tp_link))) {
+		tp_link = NULL;
+		goto cleanup;
+	}
+
+	dst.pid = getpid();
+	err = bpf_map_update_elem(results_map_fd, &zero, &dst, 0);
+	if (CHECK(err, "update_elem",
+		  "failed to set pid filter: %d\n", err))
+		goto cleanup;
+
+	err = trigger_fstat_events(dst.pid);
+	if (CHECK_FAIL(err < 0))
+		goto cleanup;
+
+	err = bpf_map_lookup_elem(results_map_fd, &zero, &dst);
+	if (CHECK(err, "get_results",
+		  "failed to get results: %d\n", err))
+		goto cleanup;
+
+	for (int i = 0; i < MAX_FDS; i++) {
+		if (i < 3)
+			CHECK((dst.paths[i][0] != '0'), "get_file_path",
+			      "failed to filter fs [%d]: %u(%s) vs %u(%s)\n",
+			      i, src.fds[i], src.paths[i], dst.fds[i],
+			      dst.paths[i]);
+		else
+			CHECK(strncmp(src.paths[i], dst.paths[i], MAX_PATH_LEN),
+			      "get_file_path",
+			      "failed to get path[%d]: %u(%s) vs %u(%s)\n",
+			      i, src.fds[i], src.paths[i], dst.fds[i],
+			      dst.paths[i]);
+	}
+
+cleanup:
+	bpf_link__destroy(tp_link);
+	bpf_object__close(obj);
+}
tools/testing/selftests/bpf/progs/test_get_file_path.c new file +43 -0

43 lines not on this page · raw message

Signature · 1 line
2.17.1
#46 lore raw

Re: [PATCH bpf-next v12 2/2] selftests/bpf: test for bpf_get_file_path() from tracepoint

Quoted 51 lines · Wenbo Zhang

trace fstat events by tracepoint syscalls/sys_enter_newfstat, and handle events only produced by test_file_get_path, which call fstat on several different types of files to test bpf_get_file_path's feature.

v4->v5: addressed Andrii's feedback
- pass NULL for opts as bpf_object__open_file's PARAM2, as not really using any
- modify patch subject to keep up with test code
- as this test is single-threaded, so use getpid instead of SYS_gettid
- remove unnecessary parens around check which after if (i < 3)
- in kern use bpf_get_current_pid_tgid() >> 32 to fit getpid() in userspace part
- with the patch adding helper as one patch series

v3->v4: addressed Andrii's feedback
- use a set of fd instead of fds array
- use global variables instead of maps (in v3, I mistakenly thought that the bpf maps are global variables.)
- remove uncessary global variable path_info_index
- remove fd compare as the fstat's order is fixed

v2->v3: addressed Andrii's feedback
- use global data instead of perf_buffer to simplified code

v1->v2: addressed Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names

---
  .../selftests/bpf/prog_tests/get_file_path.c  | 171 ++++++++++++++++++
  .../selftests/bpf/progs/test_get_file_path.c  |  43 +++++
  2 files changed, 214 insertions(+)

create mode 100644 tools/testing/selftests/bpf/prog_tests/get_file_path.c create mode 100644 tools/testing/selftests/bpf/progs/test_get_file_path.c

tools/testing/selftests/bpf/prog_tests/get_file_path.c new file +9 -0
new file mode 100644
index 000000000000..7ec11e43e0fc
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <test_progs.h>
+#include <sys/stat.h>
+#include <linux/sched.h>
+#include <sys/syscall.h>
+
+#define MAX_PATH_LEN		128
+#define MAX_FDS			7

tools/testing/selftests/bpf/prog_tests/get_file_path.c line 19

Quoted 10 lines
tools/testing/selftests/bpf/prog_tests/get_file_path.c new file +10 -0
+#define MAX_EVENT_NUM		16
+
+static struct file_path_test_data {
+	pid_t pid;
+	__u32 cnt;
+	__u32 fds[MAX_EVENT_NUM];
+	char paths[MAX_EVENT_NUM][MAX_PATH_LEN];
+} src, dst;
+
+static inline int set_pathname(int fd)

In non-bpf .c file, typically we do not add 'inline' attribute. It is up to compiler to decide whether it should be inlined.

Quoted 8 lines
tools/testing/selftests/bpf/prog_tests/get_file_path.c new file +8 -0
+{
+	char buf[MAX_PATH_LEN];
+
+	snprintf(buf, MAX_PATH_LEN, "/proc/%d/fd/%d", src.pid, fd);
+	src.fds[src.cnt] = fd;
+	return readlink(buf, src.paths[src.cnt++], MAX_PATH_LEN);
+}
+

[...]

Quoted 20 lines
tools/testing/selftests/bpf/progs/test_get_file_path.c new file +14 -0
new file mode 100644
index 000000000000..eae663c1262a
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include <linux/ptrace.h>
+#include <string.h>
+#include <unistd.h>
+#include "bpf_helpers.h"
+#include "bpf_tracing.h"
+
+#define MAX_PATH_LEN		128
+#define MAX_EVENT_NUM		16
+
+static struct file_path_test_data {
+	pid_t pid;

tools/testing/selftests/bpf/progs/test_get_file_path.c line 24

Quoted 10 lines
tools/testing/selftests/bpf/progs/test_get_file_path.c new file +10 -0
+	__u32 cnt;
+	__u32 fds[MAX_EVENT_NUM];
+	char paths[MAX_EVENT_NUM][MAX_PATH_LEN];
+} data;
+
+struct sys_enter_newfstat_args {
+	unsigned long long pad1;
+	unsigned long long pad2;
+	unsigned int fd;
+};

The BTF generated vmlinux.h has the following structure,

struct trace_entry {
         short unsigned int type;
         unsigned char flags;
         unsigned char preempt_count;
         int pid;
};
struct trace_event_raw_sys_enter {
         struct trace_entry ent;
         long int id;
         long unsigned int args[6];
         char __data[0];
};

The third parameter type should be long, otherwise, it may have issue on big endian machines?

#47 lore raw

Re: [PATCH bpf-next v12 2/2] selftests/bpf: test for bpf_get_file_path() from tracepoint

Quoted 2 lines

In non-bpf .c file, typically we do not add 'inline' attribute. It is up to compiler to decide whether it should be inlined.

Thank you, I'll fix this.

Quoted 20 lines

tools/testing/selftests/bpf/progs/test_get_file_path.c line 24

Quoted 5 lines
tools/testing/selftests/bpf/progs/test_get_file_path.c new file +5 -0
+struct sys_enter_newfstat_args {
+     unsigned long long pad1;
+     unsigned long long pad2;
+     unsigned int fd;
+};

The BTF generated vmlinux.h has the following structure,

struct trace_entry {
         short unsigned int type;
         unsigned char flags;
         unsigned char preempt_count;
         int pid;
};
struct trace_event_raw_sys_enter {
         struct trace_entry ent;
         long int id;
         long unsigned int args[6];
         char __data[0];
};
Quoted 2 lines

The third parameter type should be long, otherwise, it may have issue on big endian machines?

Sorry, I don't understand why there is a problem on big-endian machines. Would you please explain that in more detail? Thank you.

Quoted 125 lines · Yonghong Song <[email protected]> 于2019年12月16日周一 上午12:25写道:
Quoted 51 lines · Wenbo Zhang

trace fstat events by tracepoint syscalls/sys_enter_newfstat, and handle events only produced by test_file_get_path, which call fstat on several different types of files to test bpf_get_file_path's feature.

v4->v5: addressed Andrii's feedback
- pass NULL for opts as bpf_object__open_file's PARAM2, as not really using any
- modify patch subject to keep up with test code
- as this test is single-threaded, so use getpid instead of SYS_gettid
- remove unnecessary parens around check which after if (i < 3)
- in kern use bpf_get_current_pid_tgid() >> 32 to fit getpid() in userspace part
- with the patch adding helper as one patch series

v3->v4: addressed Andrii's feedback
- use a set of fd instead of fds array
- use global variables instead of maps (in v3, I mistakenly thought that the bpf maps are global variables.)
- remove uncessary global variable path_info_index
- remove fd compare as the fstat's order is fixed

v2->v3: addressed Andrii's feedback
- use global data instead of perf_buffer to simplified code

v1->v2: addressed Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names

---
  .../selftests/bpf/prog_tests/get_file_path.c  | 171 ++++++++++++++++++
  .../selftests/bpf/progs/test_get_file_path.c  |  43 +++++
  2 files changed, 214 insertions(+)

create mode 100644 tools/testing/selftests/bpf/prog_tests/get_file_path.c create mode 100644 tools/testing/selftests/bpf/progs/test_get_file_path.c

tools/testing/selftests/bpf/prog_tests/get_file_path.c new file +9 -0
new file mode 100644
index 000000000000..7ec11e43e0fc
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <test_progs.h>
+#include <sys/stat.h>
+#include <linux/sched.h>
+#include <sys/syscall.h>
+
+#define MAX_PATH_LEN         128
+#define MAX_FDS                      7

tools/testing/selftests/bpf/prog_tests/get_file_path.c line 19

Quoted 10 lines
tools/testing/selftests/bpf/prog_tests/get_file_path.c new file +10 -0
+#define MAX_EVENT_NUM                16
+
+static struct file_path_test_data {
+     pid_t pid;
+     __u32 cnt;
+     __u32 fds[MAX_EVENT_NUM];
+     char paths[MAX_EVENT_NUM][MAX_PATH_LEN];
+} src, dst;
+
+static inline int set_pathname(int fd)

In non-bpf .c file, typically we do not add 'inline' attribute. It is up to compiler to decide whether it should be inlined.

Quoted 8 lines
tools/testing/selftests/bpf/prog_tests/get_file_path.c new file +8 -0
+{
+     char buf[MAX_PATH_LEN];
+
+     snprintf(buf, MAX_PATH_LEN, "/proc/%d/fd/%d", src.pid, fd);
+     src.fds[src.cnt] = fd;
+     return readlink(buf, src.paths[src.cnt++], MAX_PATH_LEN);
+}
+

[...]

Quoted 20 lines
tools/testing/selftests/bpf/progs/test_get_file_path.c new file +14 -0
new file mode 100644
index 000000000000..eae663c1262a
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include <linux/ptrace.h>
+#include <string.h>
+#include <unistd.h>
+#include "bpf_helpers.h"
+#include "bpf_tracing.h"
+
+#define MAX_PATH_LEN         128
+#define MAX_EVENT_NUM                16
+
+static struct file_path_test_data {
+     pid_t pid;

tools/testing/selftests/bpf/progs/test_get_file_path.c line 24

Quoted 10 lines
tools/testing/selftests/bpf/progs/test_get_file_path.c new file +10 -0
+     __u32 cnt;
+     __u32 fds[MAX_EVENT_NUM];
+     char paths[MAX_EVENT_NUM][MAX_PATH_LEN];
+} data;
+
+struct sys_enter_newfstat_args {
+     unsigned long long pad1;
+     unsigned long long pad2;
+     unsigned int fd;
+};

The BTF generated vmlinux.h has the following structure,

struct trace_entry {
         short unsigned int type;
         unsigned char flags;
         unsigned char preempt_count;
         int pid;
};
struct trace_event_raw_sys_enter {
         struct trace_entry ent;
         long int id;
         long unsigned int args[6];
         char __data[0];
};

The third parameter type should be long, otherwise, it may have issue on big endian machines?

#48 lore raw

Re: [PATCH bpf-next v12 2/2] selftests/bpf: test for bpf_get_file_path() from tracepoint

Quoted 25 lines · Wenbo Zhang
Quoted 2 lines

In non-bpf .c file, typically we do not add 'inline' attribute. It is up to compiler to decide whether it should be inlined.

Thank you, I'll fix this.

Quoted 19 lines

tools/testing/selftests/bpf/progs/test_get_file_path.c line 24

Quoted 5 lines
tools/testing/selftests/bpf/progs/test_get_file_path.c new file +5 -0
+struct sys_enter_newfstat_args {
+     unsigned long long pad1;
+     unsigned long long pad2;
+     unsigned int fd;
+};

The BTF generated vmlinux.h has the following structure,

struct trace_entry {
          short unsigned int type;
          unsigned char flags;
          unsigned char preempt_count;
          int pid;
};
struct trace_event_raw_sys_enter {
          struct trace_entry ent;
          long int id;
          long unsigned int args[6];
          char __data[0];
};
Quoted 5 lines
Quoted 2 lines

The third parameter type should be long, otherwise, it may have issue on big endian machines?

Sorry, I don't understand why there is a problem on big-endian machines. Would you please explain that in more detail? Thank you.

The kernel will actually have 8 bytes of memory to store fd based on trace_event_raw_sys_enter.

For little endian machine, the lower 4 bytes are read based on your sys_enter_newfstat_args, which is "accidentally" the lower 4 bytes in u64, so you get the correct answer.

For big endian machine, the lower 4 bytes read based on your sys_enter_newfstat_args will be high 4 bytes in u64, which is incorrect.

Quoted 127 lines
Quoted 125 lines · Yonghong Song <[email protected]> 于2019年12月16日周一 上午12:25写道:
Quoted 51 lines · Wenbo Zhang

trace fstat events by tracepoint syscalls/sys_enter_newfstat, and handle events only produced by test_file_get_path, which call fstat on several different types of files to test bpf_get_file_path's feature.

v4->v5: addressed Andrii's feedback
- pass NULL for opts as bpf_object__open_file's PARAM2, as not really using any
- modify patch subject to keep up with test code
- as this test is single-threaded, so use getpid instead of SYS_gettid
- remove unnecessary parens around check which after if (i < 3)
- in kern use bpf_get_current_pid_tgid() >> 32 to fit getpid() in userspace part
- with the patch adding helper as one patch series

v3->v4: addressed Andrii's feedback
- use a set of fd instead of fds array
- use global variables instead of maps (in v3, I mistakenly thought that the bpf maps are global variables.)
- remove uncessary global variable path_info_index
- remove fd compare as the fstat's order is fixed

v2->v3: addressed Andrii's feedback
- use global data instead of perf_buffer to simplified code

v1->v2: addressed Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names

---
   .../selftests/bpf/prog_tests/get_file_path.c  | 171 ++++++++++++++++++
   .../selftests/bpf/progs/test_get_file_path.c  |  43 +++++
   2 files changed, 214 insertions(+)

create mode 100644 tools/testing/selftests/bpf/prog_tests/get_file_path.c create mode 100644 tools/testing/selftests/bpf/progs/test_get_file_path.c

tools/testing/selftests/bpf/prog_tests/get_file_path.c new file +9 -0
new file mode 100644
index 000000000000..7ec11e43e0fc
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <test_progs.h>
+#include <sys/stat.h>
+#include <linux/sched.h>
+#include <sys/syscall.h>
+
+#define MAX_PATH_LEN         128
+#define MAX_FDS                      7

tools/testing/selftests/bpf/prog_tests/get_file_path.c line 19

Quoted 10 lines
tools/testing/selftests/bpf/prog_tests/get_file_path.c new file +10 -0
+#define MAX_EVENT_NUM                16
+
+static struct file_path_test_data {
+     pid_t pid;
+     __u32 cnt;
+     __u32 fds[MAX_EVENT_NUM];
+     char paths[MAX_EVENT_NUM][MAX_PATH_LEN];
+} src, dst;
+
+static inline int set_pathname(int fd)

In non-bpf .c file, typically we do not add 'inline' attribute. It is up to compiler to decide whether it should be inlined.

Quoted 8 lines
tools/testing/selftests/bpf/prog_tests/get_file_path.c new file +8 -0
+{
+     char buf[MAX_PATH_LEN];
+
+     snprintf(buf, MAX_PATH_LEN, "/proc/%d/fd/%d", src.pid, fd);
+     src.fds[src.cnt] = fd;
+     return readlink(buf, src.paths[src.cnt++], MAX_PATH_LEN);
+}
+

[...]

Quoted 20 lines
tools/testing/selftests/bpf/progs/test_get_file_path.c new file +14 -0
new file mode 100644
index 000000000000..eae663c1262a
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include <linux/ptrace.h>
+#include <string.h>
+#include <unistd.h>
+#include "bpf_helpers.h"
+#include "bpf_tracing.h"
+
+#define MAX_PATH_LEN         128
+#define MAX_EVENT_NUM                16
+
+static struct file_path_test_data {
+     pid_t pid;

tools/testing/selftests/bpf/progs/test_get_file_path.c line 24

Quoted 10 lines
tools/testing/selftests/bpf/progs/test_get_file_path.c new file +10 -0
+     __u32 cnt;
+     __u32 fds[MAX_EVENT_NUM];
+     char paths[MAX_EVENT_NUM][MAX_PATH_LEN];
+} data;
+
+struct sys_enter_newfstat_args {
+     unsigned long long pad1;
+     unsigned long long pad2;
+     unsigned int fd;
+};

The BTF generated vmlinux.h has the following structure,

struct trace_entry {
          short unsigned int type;
          unsigned char flags;
          unsigned char preempt_count;
          int pid;
};
struct trace_event_raw_sys_enter {
          struct trace_entry ent;
          long int id;
          long unsigned int args[6];
          char __data[0];
};

The third parameter type should be long, otherwise, it may have issue on big endian machines?

#49 lore raw

Re: [PATCH bpf-next v12 2/2] selftests/bpf: test for bpf_get_file_path() from tracepoint

Quoted 10 lines

The kernel will actually have 8 bytes of memory to store fd based on trace_event_raw_sys_enter.

For little endian machine, the lower 4 bytes are read based on your sys_enter_newfstat_args, which is "accidentally" the lower 4 bytes in u64, so you get the correct answer.

For big endian machine, the lower 4 bytes read based on your sys_enter_newfstat_args will be high 4 bytes in u64, which is incorrect.

Oh, get it. Thank you, I'll fix this in the next version.

Quoted 173 lines · Yonghong Song <[email protected]> 于2019年12月17日周二 下午12:14写道:
Quoted 25 lines · Wenbo Zhang
Quoted 2 lines

In non-bpf .c file, typically we do not add 'inline' attribute. It is up to compiler to decide whether it should be inlined.

Thank you, I'll fix this.

Quoted 19 lines

tools/testing/selftests/bpf/progs/test_get_file_path.c line 24

Quoted 5 lines
tools/testing/selftests/bpf/progs/test_get_file_path.c new file +5 -0
+struct sys_enter_newfstat_args {
+     unsigned long long pad1;
+     unsigned long long pad2;
+     unsigned int fd;
+};

The BTF generated vmlinux.h has the following structure,

struct trace_entry {
          short unsigned int type;
          unsigned char flags;
          unsigned char preempt_count;
          int pid;
};
struct trace_event_raw_sys_enter {
          struct trace_entry ent;
          long int id;
          long unsigned int args[6];
          char __data[0];
};
Quoted 5 lines
Quoted 2 lines

The third parameter type should be long, otherwise, it may have issue on big endian machines?

Sorry, I don't understand why there is a problem on big-endian machines. Would you please explain that in more detail? Thank you.

The kernel will actually have 8 bytes of memory to store fd based on trace_event_raw_sys_enter.

For little endian machine, the lower 4 bytes are read based on your sys_enter_newfstat_args, which is "accidentally" the lower 4 bytes in u64, so you get the correct answer.

For big endian machine, the lower 4 bytes read based on your sys_enter_newfstat_args will be high 4 bytes in u64, which is incorrect.

Quoted 127 lines
Quoted 125 lines · Yonghong Song <[email protected]> 于2019年12月16日周一 上午12:25写道:
Quoted 51 lines · Wenbo Zhang

trace fstat events by tracepoint syscalls/sys_enter_newfstat, and handle events only produced by test_file_get_path, which call fstat on several different types of files to test bpf_get_file_path's feature.

v4->v5: addressed Andrii's feedback
- pass NULL for opts as bpf_object__open_file's PARAM2, as not really using any
- modify patch subject to keep up with test code
- as this test is single-threaded, so use getpid instead of SYS_gettid
- remove unnecessary parens around check which after if (i < 3)
- in kern use bpf_get_current_pid_tgid() >> 32 to fit getpid() in userspace part
- with the patch adding helper as one patch series

v3->v4: addressed Andrii's feedback
- use a set of fd instead of fds array
- use global variables instead of maps (in v3, I mistakenly thought that the bpf maps are global variables.)
- remove uncessary global variable path_info_index
- remove fd compare as the fstat's order is fixed

v2->v3: addressed Andrii's feedback
- use global data instead of perf_buffer to simplified code

v1->v2: addressed Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names

---
   .../selftests/bpf/prog_tests/get_file_path.c  | 171 ++++++++++++++++++
   .../selftests/bpf/progs/test_get_file_path.c  |  43 +++++
   2 files changed, 214 insertions(+)

create mode 100644 tools/testing/selftests/bpf/prog_tests/get_file_path.c create mode 100644 tools/testing/selftests/bpf/progs/test_get_file_path.c

tools/testing/selftests/bpf/prog_tests/get_file_path.c new file +9 -0
new file mode 100644
index 000000000000..7ec11e43e0fc
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <test_progs.h>
+#include <sys/stat.h>
+#include <linux/sched.h>
+#include <sys/syscall.h>
+
+#define MAX_PATH_LEN         128
+#define MAX_FDS                      7

tools/testing/selftests/bpf/prog_tests/get_file_path.c line 19

Quoted 10 lines
tools/testing/selftests/bpf/prog_tests/get_file_path.c new file +10 -0
+#define MAX_EVENT_NUM                16
+
+static struct file_path_test_data {
+     pid_t pid;
+     __u32 cnt;
+     __u32 fds[MAX_EVENT_NUM];
+     char paths[MAX_EVENT_NUM][MAX_PATH_LEN];
+} src, dst;
+
+static inline int set_pathname(int fd)

In non-bpf .c file, typically we do not add 'inline' attribute. It is up to compiler to decide whether it should be inlined.

Quoted 8 lines
tools/testing/selftests/bpf/prog_tests/get_file_path.c new file +8 -0
+{
+     char buf[MAX_PATH_LEN];
+
+     snprintf(buf, MAX_PATH_LEN, "/proc/%d/fd/%d", src.pid, fd);
+     src.fds[src.cnt] = fd;
+     return readlink(buf, src.paths[src.cnt++], MAX_PATH_LEN);
+}
+

[...]

Quoted 20 lines
tools/testing/selftests/bpf/progs/test_get_file_path.c new file +14 -0
new file mode 100644
index 000000000000..eae663c1262a
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include <linux/ptrace.h>
+#include <string.h>
+#include <unistd.h>
+#include "bpf_helpers.h"
+#include "bpf_tracing.h"
+
+#define MAX_PATH_LEN         128
+#define MAX_EVENT_NUM                16
+
+static struct file_path_test_data {
+     pid_t pid;

tools/testing/selftests/bpf/progs/test_get_file_path.c line 24

Quoted 10 lines
tools/testing/selftests/bpf/progs/test_get_file_path.c new file +10 -0
+     __u32 cnt;
+     __u32 fds[MAX_EVENT_NUM];
+     char paths[MAX_EVENT_NUM][MAX_PATH_LEN];
+} data;
+
+struct sys_enter_newfstat_args {
+     unsigned long long pad1;
+     unsigned long long pad2;
+     unsigned int fd;
+};

The BTF generated vmlinux.h has the following structure,

struct trace_entry {
          short unsigned int type;
          unsigned char flags;
          unsigned char preempt_count;
          int pid;
};
struct trace_event_raw_sys_enter {
          struct trace_entry ent;
          long int id;
          long unsigned int args[6];
          char __data[0];
};

The third parameter type should be long, otherwise, it may have issue on big endian machines?

#50 lore raw

PATCH v11 2/2 selftests/bpf: test for bpf_get_file_path() from tracepoint

In reply to Wenbo Zhang

trace fstat events by tracepoint syscalls/sys_enter_newfstat, and handle events only produced by test_file_get_path, which call fstat on several different types of files to test bpf_get_file_path's feature.

v4->v5: addressed Andrii's feedback
- pass NULL for opts as bpf_object__open_file's PARAM2, as not really using any
- modify patch subject to keep up with test code
- as this test is single-threaded, so use getpid instead of SYS_gettid
- remove unnecessary parens around check which after if (i < 3)
- in kern use bpf_get_current_pid_tgid() >> 32 to fit getpid() in userspace part
- with the patch adding helper as one patch series

v3->v4: addressed Andrii's feedback
- use a set of fd instead of fds array
- use global variables instead of maps (in v3, I mistakenly thought that the bpf maps are global variables.)
- remove uncessary global variable path_info_index
- remove fd compare as the fstat's order is fixed

v2->v3: addressed Andrii's feedback
- use global data instead of perf_buffer to simplified code

v1->v2: addressed Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names

---
 .../selftests/bpf/prog_tests/get_file_path.c  | 171 ++++++++++++++++++
 .../selftests/bpf/progs/test_get_file_path.c  |  43 +++++
 2 files changed, 214 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/get_file_path.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_get_file_path.c
tools/testing/selftests/bpf/prog_tests/get_file_path.c new file +171 -0
new file mode 100644
index 000000000000..db88545e127b
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <test_progs.h>
+#include <sys/stat.h>
+#include <linux/sched.h>
+#include <sys/syscall.h>
+
+#define MAX_PATH_LEN		128
+#define MAX_FDS			7
+#define MAX_EVENT_NUM		16
+
+static struct file_path_test_data {
+	pid_t pid;
+	__u32 cnt;
+	__u32 fds[MAX_EVENT_NUM];
+	char paths[MAX_EVENT_NUM][MAX_PATH_LEN];
+} src, dst;
+
+static inline int set_pathname(int fd)
+{
+	char buf[MAX_PATH_LEN];
+
+	snprintf(buf, MAX_PATH_LEN, "/proc/%d/fd/%d", src.pid, fd);
+	src.fds[src.cnt] = fd;
+	return readlink(buf, src.paths[src.cnt++], MAX_PATH_LEN);
+}
+
+static int trigger_fstat_events(pid_t pid)
+{
+	int pipefd[2] = { -1, -1 };
+	int sockfd = -1, procfd = -1, devfd = -1;
+	int localfd = -1, indicatorfd = -1;
+	struct stat fileStat;
+	int ret = -1;
+
+	/* unmountable pseudo-filesystems */
+	if (CHECK_FAIL(pipe(pipefd) < 0))
+		return ret;
+	/* unmountable pseudo-filesystems */
+	sockfd = socket(AF_INET, SOCK_STREAM, 0);
+	if (CHECK_FAIL(sockfd < 0))
+		goto out_close;
+	/* mountable pseudo-filesystems */
+	procfd = open("/proc/self/comm", O_RDONLY);
+	if (CHECK_FAIL(procfd < 0))
+		goto out_close;
+	devfd = open("/dev/urandom", O_RDONLY);
+	if (CHECK_FAIL(devfd < 0))
+		goto out_close;
+	localfd = open("/tmp/fd2path_loadgen.txt", O_CREAT | O_RDONLY);
+	if (CHECK_FAIL(localfd < 0))
+		goto out_close;
+	/* bpf_get_file_path will return path with (deleted) */
+	remove("/tmp/fd2path_loadgen.txt");
+	indicatorfd = open("/tmp/", O_PATH);
+	if (CHECK_FAIL(indicatorfd < 0))
+		goto out_close;
+
+	src.pid = pid;
+
+	ret = set_pathname(pipefd[0]);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(pipefd[1]);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(sockfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(procfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(devfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(localfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(indicatorfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+
+	fstat(pipefd[0], &fileStat);
+	fstat(pipefd[1], &fileStat);
+	fstat(sockfd, &fileStat);
+	fstat(procfd, &fileStat);
+	fstat(devfd, &fileStat);
+	fstat(localfd, &fileStat);
+	fstat(indicatorfd, &fileStat);
+
+out_close:
+	close(indicatorfd);
+	close(localfd);
+	close(devfd);
+	close(procfd);
+	close(sockfd);
+	close(pipefd[1]);
+	close(pipefd[0]);
+
+	return ret;
+}
+
+void test_get_file_path(void)
+{
+	const char *prog_name = "tracepoint/syscalls/sys_enter_newfstat";
+	const char *obj_file = "test_get_file_path.o";
+	int err, results_map_fd, duration = 0;
+	struct bpf_program *tp_prog = NULL;
+	struct bpf_link *tp_link = NULL;
+	struct bpf_object *obj = NULL;
+	const int zero = 0;
+
+	obj = bpf_object__open_file(obj_file, NULL);
+	if (CHECK(IS_ERR(obj), "obj_open_file", "err %ld\n", PTR_ERR(obj)))
+		return;
+
+	tp_prog = bpf_object__find_program_by_title(obj, prog_name);
+	if (CHECK(!tp_prog, "find_tp",
+		  "prog '%s' not found\n", prog_name))
+		goto cleanup;
+
+	err = bpf_object__load(obj);
+	if (CHECK(err, "obj_load", "err %d\n", err))
+		goto cleanup;
+
+	results_map_fd = bpf_find_map(__func__, obj, "test_get.bss");
+	if (CHECK(results_map_fd < 0, "find_bss_map",
+		  "err %d\n", results_map_fd))
+		goto cleanup;
+
+	tp_link = bpf_program__attach_tracepoint(tp_prog, "syscalls",
+						 "sys_enter_newfstat");
+	if (CHECK(IS_ERR(tp_link), "attach_tp",
+		  "err %ld\n", PTR_ERR(tp_link))) {
+		tp_link = NULL;
+		goto cleanup;
+	}
+
+	dst.pid = getpid();
+	err = bpf_map_update_elem(results_map_fd, &zero, &dst, 0);
+	if (CHECK(err, "update_elem",
+		  "failed to set pid filter: %d\n", err))
+		goto cleanup;
+
+	err = trigger_fstat_events(dst.pid);
+	if (CHECK_FAIL(err < 0))
+		goto cleanup;
+
+	err = bpf_map_lookup_elem(results_map_fd, &zero, &dst);
+	if (CHECK(err, "get_results",
+		  "failed to get results: %d\n", err))
+		goto cleanup;
+
+	for (int i = 0; i < MAX_FDS; i++) {
+		if (i < 3)
+			CHECK((dst.paths[i][0] != '\0'), "get_file_path",
+			      "failed to filter fs [%d]: %u(%s) vs %u(%s)\n",
+			      i, src.fds[i], src.paths[i], dst.fds[i],
+			      dst.paths[i]);
+		else
+			CHECK(strncmp(src.paths[i], dst.paths[i], MAX_PATH_LEN),
+			      "get_file_path",
+			      "failed to get path[%d]: %u(%s) vs %u(%s)\n",
+			      i, src.fds[i], src.paths[i], dst.fds[i],
+			      dst.paths[i]);
+	}
+
+cleanup:
+	bpf_link__destroy(tp_link);
+	bpf_object__close(obj);
+}
tools/testing/selftests/bpf/progs/test_get_file_path.c new file +43 -0

43 lines not on this page · raw message

Signature · 1 line
2.17.1
#51 lore raw

PATCH v10 2/2 selftests/bpf: test for bpf_get_file_path() from tracepoint

In reply to Wenbo Zhang

trace fstat events by tracepoint syscalls/sys_enter_newfstat, and handle events only produced by test_file_get_path, which call fstat on several different types of files to test bpf_get_file_path's feature.

v4->v5: addressed Andrii's feedback
- pass NULL for opts as bpf_object__open_file's PARAM2, as not really using any
- modify patch subject to keep up with test code
- as this test is single-threaded, so use getpid instead of SYS_gettid
- remove unnecessary parens around check which after if (i < 3)
- in kern use bpf_get_current_pid_tgid() >> 32 to fit getpid() in userspace part
- with the patch adding helper as one patch series

v3->v4: addressed Andrii's feedback
- use a set of fd instead of fds array
- use global variables instead of maps (in v3, I mistakenly thought that the bpf maps are global variables.)
- remove uncessary global variable path_info_index
- remove fd compare as the fstat's order is fixed

v2->v3: addressed Andrii's feedback
- use global data instead of perf_buffer to simplified code

v1->v2: addressed Daniel's feedback
- rename bpf_fd2path to bpf_get_file_path to be consistent with other helper's names

---
 .../selftests/bpf/prog_tests/get_file_path.c  | 171 ++++++++++++++++++
 .../selftests/bpf/progs/test_get_file_path.c  |  43 +++++
 2 files changed, 214 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/get_file_path.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_get_file_path.c
tools/testing/selftests/bpf/prog_tests/get_file_path.c new file +171 -0
new file mode 100644
index 000000000000..db88545e127b
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <test_progs.h>
+#include <sys/stat.h>
+#include <linux/sched.h>
+#include <sys/syscall.h>
+
+#define MAX_PATH_LEN		128
+#define MAX_FDS			7
+#define MAX_EVENT_NUM		16
+
+static struct file_path_test_data {
+	pid_t pid;
+	__u32 cnt;
+	__u32 fds[MAX_EVENT_NUM];
+	char paths[MAX_EVENT_NUM][MAX_PATH_LEN];
+} src, dst;
+
+static inline int set_pathname(int fd)
+{
+	char buf[MAX_PATH_LEN];
+
+	snprintf(buf, MAX_PATH_LEN, "/proc/%d/fd/%d", src.pid, fd);
+	src.fds[src.cnt] = fd;
+	return readlink(buf, src.paths[src.cnt++], MAX_PATH_LEN);
+}
+
+static int trigger_fstat_events(pid_t pid)
+{
+	int pipefd[2] = { -1, -1 };
+	int sockfd = -1, procfd = -1, devfd = -1;
+	int localfd = -1, indicatorfd = -1;
+	struct stat fileStat;
+	int ret = -1;
+
+	/* unmountable pseudo-filesystems */
+	if (CHECK_FAIL(pipe(pipefd) < 0))
+		return ret;
+	/* unmountable pseudo-filesystems */
+	sockfd = socket(AF_INET, SOCK_STREAM, 0);
+	if (CHECK_FAIL(sockfd < 0))
+		goto out_close;
+	/* mountable pseudo-filesystems */
+	procfd = open("/proc/self/comm", O_RDONLY);
+	if (CHECK_FAIL(procfd < 0))
+		goto out_close;
+	devfd = open("/dev/urandom", O_RDONLY);
+	if (CHECK_FAIL(devfd < 0))
+		goto out_close;
+	localfd = open("/tmp/fd2path_loadgen.txt", O_CREAT | O_RDONLY);
+	if (CHECK_FAIL(localfd < 0))
+		goto out_close;
+	/* bpf_get_file_path will return path with (deleted) */
+	remove("/tmp/fd2path_loadgen.txt");
+	indicatorfd = open("/tmp/", O_PATH);
+	if (CHECK_FAIL(indicatorfd < 0))
+		goto out_close;
+
+	src.pid = pid;
+
+	ret = set_pathname(pipefd[0]);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(pipefd[1]);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(sockfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(procfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(devfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(localfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+	ret = set_pathname(indicatorfd);
+	if (CHECK_FAIL(ret < 0))
+		goto out_close;
+
+	fstat(pipefd[0], &fileStat);
+	fstat(pipefd[1], &fileStat);
+	fstat(sockfd, &fileStat);
+	fstat(procfd, &fileStat);
+	fstat(devfd, &fileStat);
+	fstat(localfd, &fileStat);
+	fstat(indicatorfd, &fileStat);
+
+out_close:
+	close(indicatorfd);
+	close(localfd);
+	close(devfd);
+	close(procfd);
+	close(sockfd);
+	close(pipefd[1]);
+	close(pipefd[0]);
+
+	return ret;
+}
+
+void test_get_file_path(void)
+{
+	const char *prog_name = "tracepoint/syscalls/sys_enter_newfstat";
+	const char *obj_file = "test_get_file_path.o";
+	int err, results_map_fd, duration = 0;
+	struct bpf_program *tp_prog = NULL;
+	struct bpf_link *tp_link = NULL;
+	struct bpf_object *obj = NULL;
+	const int zero = 0;
+
+	obj = bpf_object__open_file(obj_file, NULL);
+	if (CHECK(IS_ERR(obj), "obj_open_file", "err %ld\n", PTR_ERR(obj)))
+		return;
+
+	tp_prog = bpf_object__find_program_by_title(obj, prog_name);
+	if (CHECK(!tp_prog, "find_tp",
+		  "prog '%s' not found\n", prog_name))
+		goto cleanup;
+
+	err = bpf_object__load(obj);
+	if (CHECK(err, "obj_load", "err %d\n", err))
+		goto cleanup;
+
+	results_map_fd = bpf_find_map(__func__, obj, "test_get.bss");
+	if (CHECK(results_map_fd < 0, "find_bss_map",
+		  "err %d\n", results_map_fd))
+		goto cleanup;
+
+	tp_link = bpf_program__attach_tracepoint(tp_prog, "syscalls",
+						 "sys_enter_newfstat");
+	if (CHECK(IS_ERR(tp_link), "attach_tp",
+		  "err %ld\n", PTR_ERR(tp_link))) {
+		tp_link = NULL;
+		goto cleanup;
+	}
+
+	dst.pid = getpid();
+	err = bpf_map_update_elem(results_map_fd, &zero, &dst, 0);
+	if (CHECK(err, "update_elem",
+		  "failed to set pid filter: %d\n", err))
+		goto cleanup;
+
+	err = trigger_fstat_events(dst.pid);
+	if (CHECK_FAIL(err < 0))
+		goto cleanup;
+
+	err = bpf_map_lookup_elem(results_map_fd, &zero, &dst);
+	if (CHECK(err, "get_results",
+		  "failed to get results: %d\n", err))
+		goto cleanup;
+
+	for (int i = 0; i < MAX_FDS; i++) {
+		if (i < 3)
+			CHECK((dst.paths[i][0] != '\0'), "get_file_path",
+			      "failed to filter fs [%d]: %u(%s) vs %u(%s)\n",
+			      i, src.fds[i], src.paths[i], dst.fds[i],
+			      dst.paths[i]);
+		else
+			CHECK(strncmp(src.paths[i], dst.paths[i], MAX_PATH_LEN),
+			      "get_file_path",
+			      "failed to get path[%d]: %u(%s) vs %u(%s)\n",
+			      i, src.fds[i], src.paths[i], dst.fds[i],
+			      dst.paths[i]);
+	}
+
+cleanup:
+	bpf_link__destroy(tp_link);
+	bpf_object__close(obj);
+}
tools/testing/selftests/bpf/progs/test_get_file_path.c new file +43 -0

43 lines not on this page · raw message

Signature · 1 line
2.17.1