Skip to content

Commit ebb47d5

Browse files
Rtoaxyonghong-song
authored andcommitted
tools/filegone: Check btf struct field instead of KERNEL_VERSION macro
kernel commit 9fe61450972d("namei: introduce struct renamedata") introduce 'struct renamedata', at the same time, the function declarations changed. use kernel_struct_has_field() to determine if structure 'renamedata-> old_mnt_userns' exists, thus solving the vfs_rename/rmdir/unlink using CO-RE way instead of using the kernel version number. Signed-off-by: Rong Tao <[email protected]>
1 parent daf35cd commit ebb47d5

File tree

2 files changed

+181
-15
lines changed

2 files changed

+181
-15
lines changed

tools/filegone.py

+29-15
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
# Licensed under the Apache License, Version 2.0 (the "License")
1010
#
1111
# 08-Nov-2022 Curu. modified from filelife
12+
# 19-Nov-2022 Rong Tao Check btf struct field instead of KERNEL_VERSION macro.
1213

1314
from __future__ import print_function
1415
from bcc import BPF
@@ -48,11 +49,7 @@
4849
BPF_PERF_OUTPUT(events);
4950
5051
// trace file deletion and output details
51-
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
52-
int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
53-
#else
54-
int trace_unlink(struct pt_regs *ctx, struct user_namespace *ns, struct inode *dir, struct dentry *dentry)
55-
#endif
52+
TRACE_VFS_UNLINK_FUNC
5653
{
5754
u32 pid = bpf_get_current_pid_tgid() >> 32;
5855
@@ -74,16 +71,7 @@
7471
}
7572
7673
// trace file rename
77-
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
78-
int trace_rename(struct pt_regs *ctx, struct inode *old_dir, struct dentry *old_dentry,
79-
struct inode *new_dir, struct dentry *new_dentry)
80-
{
81-
#else
82-
int trace_rename(struct pt_regs *ctx, struct renamedata *rd)
83-
{
84-
struct dentry *old_dentry = rd->old_dentry;
85-
struct dentry *new_dentry = rd->new_dentry;
86-
#endif
74+
TRACE_VFS_RENAME_FUNC
8775
8876
u32 pid = bpf_get_current_pid_tgid() >> 32;
8977
@@ -106,6 +94,24 @@
10694
}
10795
"""
10896

97+
bpf_vfs_rename_text_old="""
98+
int trace_rename(struct pt_regs *ctx, struct inode *old_dir, struct dentry *old_dentry,
99+
struct inode *new_dir, struct dentry *new_dentry)
100+
{
101+
"""
102+
bpf_vfs_rename_text_new="""
103+
int trace_rename(struct pt_regs *ctx, struct renamedata *rd)
104+
{
105+
struct dentry *old_dentry = rd->old_dentry;
106+
struct dentry *new_dentry = rd->new_dentry;
107+
"""
108+
109+
bpf_vfs_unlink_text_old="""
110+
int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
111+
"""
112+
bpf_vfs_unlink_text_new="""
113+
int trace_unlink(struct pt_regs *ctx, struct user_namespace *ns, struct inode *dir, struct dentry *dentry)
114+
"""
109115

110116
def action2str(action):
111117
if chr(action) == 'D':
@@ -125,6 +131,14 @@ def action2str(action):
125131
if args.ebpf:
126132
exit()
127133

134+
# check 'struct renamedata' exist or not
135+
if BPF.kernel_struct_has_field("renamedata", "old_mnt_userns") == 1:
136+
bpf_text = bpf_text.replace('TRACE_VFS_RENAME_FUNC', bpf_vfs_rename_text_new)
137+
bpf_text = bpf_text.replace('TRACE_VFS_UNLINK_FUNC', bpf_vfs_unlink_text_new)
138+
else:
139+
bpf_text = bpf_text.replace('TRACE_VFS_RENAME_FUNC', bpf_vfs_rename_text_old)
140+
bpf_text = bpf_text.replace('TRACE_VFS_UNLINK_FUNC', bpf_vfs_unlink_text_old)
141+
128142
# initialize BPF
129143
b = BPF(text=bpf_text)
130144
b.attach_kprobe(event="vfs_unlink", fn_name="trace_unlink")

tools/old/filegone.py

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#!/usr/bin/python
2+
# @lint-avoid-python-3-compatibility-imports
3+
#
4+
# filegone Trace why file gone (deleted or renamed).
5+
# For Linux, uses BCC, eBPF. Embedded C.
6+
#
7+
# USAGE: filegone [-h] [-p PID]
8+
#
9+
# Licensed under the Apache License, Version 2.0 (the "License")
10+
#
11+
# 08-Nov-2022 Curu. modified from filelife
12+
13+
from __future__ import print_function
14+
from bcc import BPF
15+
import argparse
16+
from time import strftime
17+
18+
# arguments
19+
examples = """examples:
20+
./filegone # trace all file gone events
21+
./filegone -p 181 # only trace PID 181
22+
"""
23+
parser = argparse.ArgumentParser(
24+
description="Trace why file gone (deleted or renamed)",
25+
formatter_class=argparse.RawDescriptionHelpFormatter,
26+
epilog=examples)
27+
parser.add_argument("-p", "--pid",
28+
help="trace this PID only")
29+
parser.add_argument("--ebpf", action="store_true",
30+
help=argparse.SUPPRESS)
31+
args = parser.parse_args()
32+
debug = 0
33+
34+
# define BPF program
35+
bpf_text = """
36+
#include <uapi/linux/ptrace.h>
37+
#include <linux/fs.h>
38+
#include <linux/sched.h>
39+
40+
struct data_t {
41+
u32 pid;
42+
u8 action;
43+
char comm[TASK_COMM_LEN];
44+
char fname[DNAME_INLINE_LEN];
45+
char fname2[DNAME_INLINE_LEN];
46+
};
47+
48+
BPF_PERF_OUTPUT(events);
49+
50+
// trace file deletion and output details
51+
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
52+
int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
53+
#else
54+
int trace_unlink(struct pt_regs *ctx, struct user_namespace *ns, struct inode *dir, struct dentry *dentry)
55+
#endif
56+
{
57+
u32 pid = bpf_get_current_pid_tgid() >> 32;
58+
59+
FILTER
60+
61+
struct data_t data = {};
62+
struct qstr d_name = dentry->d_name;
63+
if (d_name.len == 0)
64+
return 0;
65+
66+
bpf_get_current_comm(&data.comm, sizeof(data.comm));
67+
data.pid = pid;
68+
data.action = 'D';
69+
bpf_probe_read(&data.fname, sizeof(data.fname), d_name.name);
70+
71+
events.perf_submit(ctx, &data, sizeof(data));
72+
73+
return 0;
74+
}
75+
76+
// trace file rename
77+
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
78+
int trace_rename(struct pt_regs *ctx, struct inode *old_dir, struct dentry *old_dentry,
79+
struct inode *new_dir, struct dentry *new_dentry)
80+
{
81+
#else
82+
int trace_rename(struct pt_regs *ctx, struct renamedata *rd)
83+
{
84+
struct dentry *old_dentry = rd->old_dentry;
85+
struct dentry *new_dentry = rd->new_dentry;
86+
#endif
87+
88+
u32 pid = bpf_get_current_pid_tgid() >> 32;
89+
90+
FILTER
91+
92+
struct data_t data = {};
93+
struct qstr s_name = old_dentry->d_name;
94+
struct qstr d_name = new_dentry->d_name;
95+
if (s_name.len == 0 || d_name.len == 0)
96+
return 0;
97+
98+
bpf_get_current_comm(&data.comm, sizeof(data.comm));
99+
data.pid = pid;
100+
data.action = 'R';
101+
bpf_probe_read(&data.fname, sizeof(data.fname), s_name.name);
102+
bpf_probe_read(&data.fname2, sizeof(data.fname), d_name.name);
103+
events.perf_submit(ctx, &data, sizeof(data));
104+
105+
return 0;
106+
}
107+
"""
108+
109+
110+
def action2str(action):
111+
if chr(action) == 'D':
112+
action_str = "DELETE"
113+
else:
114+
action_str = "RENAME"
115+
return action_str
116+
117+
if args.pid:
118+
bpf_text = bpf_text.replace('FILTER',
119+
'if (pid != %s) { return 0; }' % args.pid)
120+
else:
121+
bpf_text = bpf_text.replace('FILTER', '')
122+
123+
if debug or args.ebpf:
124+
print(bpf_text)
125+
if args.ebpf:
126+
exit()
127+
128+
# initialize BPF
129+
b = BPF(text=bpf_text)
130+
b.attach_kprobe(event="vfs_unlink", fn_name="trace_unlink")
131+
b.attach_kprobe(event="vfs_rmdir", fn_name="trace_unlink")
132+
b.attach_kprobe(event="vfs_rename", fn_name="trace_rename")
133+
134+
# header
135+
print("%-8s %-7s %-16s %6s %s" % ("TIME", "PID", "COMM", "ACTION", "FILE"))
136+
137+
# process event
138+
def print_event(cpu, data, size):
139+
event = b["events"].event(data)
140+
action_str = action2str(event.action)
141+
file_str = event.fname.decode('utf-8', 'replace')
142+
if action_str == "RENAME":
143+
file_str = "%s > %s" % (file_str, event.fname2.decode('utf-8', 'replace'))
144+
print("%-8s %-7d %-16s %6s %s" % (strftime("%H:%M:%S"), event.pid,
145+
event.comm.decode('utf-8', 'replace'), action_str, file_str))
146+
147+
b["events"].open_perf_buffer(print_event)
148+
while 1:
149+
try:
150+
b.perf_buffer_poll()
151+
except KeyboardInterrupt:
152+
exit()

0 commit comments

Comments
 (0)