2424 */
2525#define PATH_LEN_CLAMP (len ) ((len) & PATH_MAX_MASK)
2626
27+ // Context for __d_path_inner.
28+ //
29+ // Supports two modes:
30+ // Full path mode: mnt and root must both be set. Crosses mount
31+ // boundaries and terminates at the process root.
32+ // Dentry-only mode: mnt and root must both be NULL. Walks the
33+ // dentry chain to the filesystem root, producing a
34+ // filesystem-relative path.
2735struct d_path_ctx {
2836 struct helper_t * helper ;
2937 struct path * root ;
@@ -38,39 +46,49 @@ static long __d_path_inner(uint32_t index, void* _ctx) {
3846 struct d_path_ctx * ctx = (struct d_path_ctx * )_ctx ;
3947 struct dentry * dentry = ctx -> dentry ;
4048 struct dentry * parent = BPF_CORE_READ (dentry , d_parent );
41- struct mount * mnt = ctx -> mnt ;
42- struct dentry * mnt_root = BPF_CORE_READ (mnt , mnt .mnt_root );
4349
44- if (dentry == ctx -> root -> dentry && & mnt -> mnt == ctx -> root -> mnt ) {
45- // Found the root of the process, we are done
46- ctx -> success = true;
47- return 1 ;
48- }
50+ if (ctx -> mnt != NULL ) {
51+ // Full path mode: we have mount context and can cross mount
52+ // boundaries and detect the process root.
53+ struct mount * mnt = ctx -> mnt ;
54+ struct dentry * mnt_root = BPF_CORE_READ ( mnt , mnt . mnt_root );
4955
50- if (dentry == mnt_root ) {
51- struct mount * m = BPF_CORE_READ (mnt , mnt_parent );
52- if (m != mnt ) {
53- // Current dentry is a mount root different to the previous one we
54- // had (to prevent looping), switch over to that mount position
55- // and keep walking up the path.
56- ctx -> dentry = BPF_CORE_READ (mnt , mnt_mountpoint );
57- ctx -> mnt = m ;
58- return 0 ;
56+ if (dentry == ctx -> root -> dentry && & mnt -> mnt == ctx -> root -> mnt ) {
57+ // Found the root of the process, we are done
58+ ctx -> success = true;
59+ return 1 ;
5960 }
6061
61- // Ended up in a global root, the path might need re-processing or
62- // the root is not attached yet, we are not getting a better path,
63- // so we assume we are correct and stop iterating.
64- ctx -> success = true;
65- return 1 ;
62+ if (dentry == mnt_root ) {
63+ struct mount * m = BPF_CORE_READ (mnt , mnt_parent );
64+ if (m != mnt ) {
65+ // Current dentry is a mount root different to the previous one we
66+ // had (to prevent looping), switch over to that mount position
67+ // and keep walking up the path.
68+ ctx -> dentry = BPF_CORE_READ (mnt , mnt_mountpoint );
69+ ctx -> mnt = m ;
70+ return 0 ;
71+ }
72+
73+ // Ended up in a global root, the path might need re-processing or
74+ // the root is not attached yet, we are not getting a better path,
75+ // so we assume we are correct and stop iterating.
76+ ctx -> success = true;
77+ return 1 ;
78+ }
6679 }
6780
6881 if (dentry == parent ) {
69- // We escaped the mounts and ended up at (most likely) the root of
70- // the device, the path we formed will be wrong.
82+ // Reached the root of the filesystem's dentry tree.
83+ //
84+ // In full path mode (mnt != NULL) this means we escaped the mounts
85+ // and the path may be wrong due to a race condition.
7186 //
72- // This may happen in race conditions where some dentries go away
73- // while we are iterating.
87+ // In dentry-only mode (mnt == NULL) this is the expected
88+ // termination: we've reached the filesystem root and have a
89+ // filesystem-relative path. This is correct for overlayfs
90+ // (containers) and for files on the root filesystem.
91+ ctx -> success = (ctx -> mnt == NULL );
7492 return 1 ;
7593 }
7694
@@ -140,6 +158,46 @@ __always_inline static long __d_path(const struct path* path, char* buf, int buf
140158 return buflen - ctx .offset ;
141159}
142160
161+ /**
162+ * Resolve a filesystem-relative path from a bare dentry.
163+ *
164+ * This is used when no struct path is available (e.g. inode_set_acl).
165+ * It walks the dentry chain up to the filesystem root, producing a
166+ * path relative to the filesystem's root dentry. This is correct for
167+ * overlayfs (containers) and for files on the root filesystem. It
168+ * cannot cross mount boundaries, so paths on nested host mounts (e.g.
169+ * a separate /var partition) will be relative to that mount's root.
170+ */
171+ __always_inline static long __d_path_from_dentry (struct dentry * dentry , char * buf , int buflen ) {
172+ if (buflen <= 0 ) {
173+ return -1 ;
174+ }
175+
176+ int offset = PATH_LEN_CLAMP (buflen - 1 );
177+ struct d_path_ctx ctx = {
178+ .buflen = buflen ,
179+ .helper = get_helper (),
180+ .offset = offset ,
181+ .mnt = NULL ,
182+ .root = NULL ,
183+ };
184+
185+ if (ctx .helper == NULL ) {
186+ return -1 ;
187+ }
188+
189+ ctx .helper -> buf [offset ] = '\0' ;
190+ ctx .dentry = dentry ;
191+
192+ long res = bpf_loop (PATH_MAX , __d_path_inner , & ctx , 0 );
193+ if (res <= 0 || !ctx .success ) {
194+ return -1 ;
195+ }
196+
197+ bpf_probe_read_str (buf , buflen , & ctx .helper -> buf [PATH_LEN_CLAMP (ctx .offset )]);
198+ return buflen - ctx .offset ;
199+ }
200+
143201__always_inline static long d_path (struct path * path , char * buf , int buflen , bool use_bpf_helper ) {
144202 if (use_bpf_helper ) {
145203 return bpf_d_path (path , buf , buflen );
0 commit comments