1111# 08-Nov-2022 Curu. modified from filelife
1212# 19-Nov-2022 Rong Tao Check btf struct field instead of KERNEL_VERSION macro.
1313# 05-Nov-2023 Rong Tao Support rename/unlink failed situation.
14+ # 01-Jul-2025 Rong Tao Support file path
1415
1516from __future__ import print_function
1617from bcc import BPF
2021# arguments
2122examples = """examples:
2223 ./filegone # trace all file gone events
24+ ./filegone -P # show file path
2325 ./filegone -p 181 # only trace PID 181
2426"""
2527parser = argparse .ArgumentParser (
2830 epilog = examples )
2931parser .add_argument ("-p" , "--pid" ,
3032 help = "trace this PID only" )
33+ parser .add_argument ("-P" , "--path" , action = "store_true" ,
34+ help = "show file path" )
3135parser .add_argument ("--ebpf" , action = "store_true" ,
3236 help = argparse .SUPPRESS )
3337args = parser .parse_args ()
3741bpf_text = """
3842#include <uapi/linux/ptrace.h>
3943#include <linux/fs.h>
44+ #include <linux/fs_struct.h>
4045#include <linux/sched.h>
46+ #ifdef FULLPATH
47+ INCLUDE_FULL_PATH_H
48+ INCLUDE_PATH_HELPERS_BPF_H
49+ #endif
4150
4251struct data_t {
4352 u32 pid;
4453 u8 action;
4554 char comm[TASK_COMM_LEN];
46- char fname[DNAME_INLINE_LEN];
47- char fname2[DNAME_INLINE_LEN];
55+ u32 path_depth, path_depth2;
56+ #ifdef FULLPATH
57+ FULL_PATH_FIELD(fname);
58+ FULL_PATH_FIELD(fname2);
59+ #else
60+ char fname[NAME_MAX];
61+ char fname2[NAME_MAX];
62+ #endif
4863};
4964
5065struct entry_t {
5166 u32 pid;
5267 u8 action;
5368 struct {
5469 char name[DNAME_INLINE_LEN];
70+ struct dentry *dentry;
5571 } old, new;
5672};
5773
7995
8096 entry.pid = pid;
8197 entry.action = 'D';
98+ entry.old.dentry = dentry;
8299 get_dentry_name((char **)&entry.old.name, dentry);
83100
84101 currentry.update(&tid, &entry);
103120 * Couldn't get new and old dentry name in trace_return(), because you'll
104121 * get new-name for old.
105122 */
123+ entry.old.dentry = old_dentry;
106124 get_dentry_name((char **)&entry.old.name, old_dentry);
125+ entry.new.dentry = new_dentry;
107126 get_dentry_name((char **)&entry.new.name, new_dentry);
108127
109128 currentry.update(&tid, &entry);
138157
139158 bpf_probe_read(&data->fname, sizeof(data->fname), entry->old.name);
140159
160+ data->path_depth = data->path_depth2 = 0;
141161 if (entry->action == 'R')
142162 bpf_probe_read(&data->fname2, sizeof(data->fname2), entry->new.name);
143163
164+ #ifdef FULLPATH
165+ struct task_struct *task;
166+ struct fs_struct *fs;
167+ struct vfsmount *cwd_vfsmnt;
168+
169+ task = (struct task_struct *)bpf_get_current_task_btf();
170+ bpf_probe_read_kernel(&fs, sizeof(fs), &task->fs);
171+ bpf_probe_read_kernel(&cwd_vfsmnt, sizeof(cwd_vfsmnt), &fs->pwd.mnt);
172+ if (data->fname[0] != '/') {
173+ data->path_depth = 1;
174+ bpf_dentry_full_path(data->fname + NAME_MAX, NAME_MAX, MAX_ENTRIES - 1,
175+ entry->old.dentry->d_parent, cwd_vfsmnt,
176+ &data->path_depth);
177+ }
178+
179+ if (entry->action == 'R' && data->fname2[0] != '/') {
180+ data->path_depth2 = 1;
181+ bpf_dentry_full_path(data->fname2 + NAME_MAX, NAME_MAX, MAX_ENTRIES - 1,
182+ entry->new.dentry->d_parent, cwd_vfsmnt,
183+ &data->path_depth2);
184+ }
185+ #endif
186+
144187 events.ringbuf_submit(data, sizeof(*data));
145188 return 0;
146189}
@@ -192,6 +235,17 @@ def action2str(action):
192235 bpf_text = bpf_text .replace ('TRACE_VFS_RENAME_FUNC' , bpf_vfs_rename_text_old )
193236 bpf_text = bpf_text .replace ('TRACE_VFS_UNLINK_FUNC' , bpf_vfs_unlink_text_1 )
194237
238+ if args .path :
239+ bpf_text = "#define FULLPATH\n " + bpf_text
240+
241+ with open (BPF ._find_file ("full_path.h" .encode ("utf-8" ))) as fileobj :
242+ progtxt = fileobj .read ()
243+ bpf_text = bpf_text .replace ('INCLUDE_FULL_PATH_H' , progtxt )
244+
245+ with open (BPF ._find_file ("path_helpers.bpf.c" .encode ("utf-8" ))) as fileobj :
246+ progtxt = fileobj .read ()
247+ bpf_text = bpf_text .replace ('INCLUDE_PATH_HELPERS_BPF_H' , progtxt )
248+
195249# NOTE: After bpf_text modification
196250if debug or args .ebpf :
197251 print (bpf_text )
@@ -214,10 +268,20 @@ def action2str(action):
214268def print_event (cpu , data , size ):
215269 event = b ["events" ].event (data )
216270 action_str = action2str (event .action )
217- file_str = event .fname .decode ('utf-8' , 'replace' )
218- if action_str == "RENAME" :
219- file2_str = event .fname2 .decode ('utf-8' , 'replace' )
220- file_str = "%s > %s" % (file_str , file2_str )
271+ if args .path :
272+ import os
273+ import sys
274+ sys .path .append (os .path .dirname (sys .argv [0 ]))
275+ from path_helpers import get_full_path
276+ file_str = get_full_path (event .fname , event .path_depth )
277+ if action_str == "RENAME" :
278+ file2_str = get_full_path (event .fname2 , event .path_depth2 )
279+ file_str = "%s > %s" % (file_str , file2_str )
280+ else :
281+ file_str = event .fname .decode ('utf-8' , 'replace' )
282+ if action_str == "RENAME" :
283+ file2_str = event .fname2 .decode ('utf-8' , 'replace' )
284+ file_str = "%s > %s" % (file_str , file2_str )
221285 print ("%-8s %-7d %-16s %6s %s" % (strftime ("%H:%M:%S" ), event .pid ,
222286 event .comm .decode ('utf-8' , 'replace' ), action_str , file_str ))
223287
0 commit comments