编写eBPF程序从文件描述符获取文件名及代码报错排查
从FD获取文件名的BPF方案及代码修复
有没有直接从FD获取文件名的BPF辅助函数?
有,内核提供了**bpf_fd_get_path()**辅助函数,可直接通过文件描述符获取对应文件的完整路径,无需手动维护FD与文件名的映射关系。该函数要求Linux内核5.8及以上版本支持,基本用法如下:
char file_path[256]; // ctx为tracepoint或系统调用上下文,path为存储路径的缓冲区,size为缓冲区大小 int ret = bpf_fd_get_path(ctx, file_path, sizeof(file_path)); if (ret == 0) { // 成功获取路径,可直接使用file_path }
你的代码报错原因及修复
报错根源
- Tracepoint参数定义错误:
sys_enter_openat是tracepoint,其回调函数的参数必须是struct trace_event_raw_sys_enter *ctx,而非直接列出openat的系统调用参数(dfd、filename等),编译器无法识别这些未定义的参数,导致语法错误。 - 时机错误:在
sys_enter_openat阶段,openat系统调用尚未执行完成,此时调用PT_REGS_RC(ctx)无法获取有效的返回FD。
修复后的代码(推荐方案,使用bpf_fd_get_path())
我们改用sys_exit_openat追踪系统调用退出,获取返回的FD后直接用辅助函数获取路径:
#include "vmlinux.h" #include <bpf/bpf_helpers.h> #include <bpf/bpf_tracing.h> // FD到文件名的哈希映射 struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, int); // 文件描述符 __type(value, char[256]); // 文件完整路径 __uint(max_entries, 1024); } fd_to_filename_map SEC(".maps"); // 追踪openat系统调用退出,关联FD与文件路径 SEC("sys_exit_openat") int trace_openat_exit(struct trace_event_raw_sys_exit *ctx) { int fd = ctx->ret; char file_path[256]; // 仅处理成功打开的FD(FD >= 0) if (fd < 0) return 0; // 通过FD获取文件路径 if (bpf_fd_get_path(ctx, file_path, sizeof(file_path)) != 0) return 0; // 更新映射 bpf_map_update_elem(&fd_to_filename_map, &fd, file_path, BPF_ANY); return 0; } // 追踪close系统调用,清理映射中的无效FD条目 SEC("sys_enter_close") int trace_close_enter(struct trace_event_raw_sys_enter *ctx) { int fd = ctx->args[0]; bpf_map_delete_elem(&fd_to_filename_map, &fd); return 0; } char _license[] SEC("license") = "GPL";
低内核版本兼容方案(无法使用bpf_fd_get_path()时)
如果你的内核版本低于5.8,需要在sys_enter_openat中暂存用户传入的路径,再在sys_exit_openat中关联FD:
#include "vmlinux.h" #include <bpf/bpf_helpers.h> #include <bpf/bpf_tracing.h> // 临时映射:用进程ID+线程ID作为key,暂存用户传入的文件名 struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, u64); // pid_tgid = bpf_get_current_pid_tgid() __type(value, char[256]); __uint(max_entries, 1024); } temp_filename_map SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, int); __type(value, char[256]); __uint(max_entries, 1024); } fd_to_filename_map SEC(".maps"); // 进入openat时暂存文件名 SEC("sys_enter_openat") int trace_openat_enter(struct trace_event_raw_sys_enter *ctx) { char fname[256]; const char __user *user_filename = (const char __user *)ctx->args[1]; u64 pid_tgid = bpf_get_current_pid_tgid(); // 读取用户态传入的文件名 if (bpf_probe_read_user_str(fname, sizeof(fname), user_filename) < 0) return 0; // 存入临时映射 bpf_map_update_elem(&temp_filename_map, &pid_tgid, fname, BPF_ANY); return 0; } // 退出openat时关联FD与文件名 SEC("sys_exit_openat") int trace_openat_exit(struct trace_event_raw_sys_exit *ctx) { int fd = ctx->ret; char fname[256]; u64 pid_tgid = bpf_get_current_pid_tgid(); if (fd < 0) goto cleanup; // 从临时映射取出文件名 if (bpf_map_lookup_elem(&temp_filename_map, &pid_tgid, fname) != 0) goto cleanup; // 更新FD映射 bpf_map_update_elem(&fd_to_filename_map, &fd, fname, BPF_ANY); cleanup: // 清理临时映射条目,避免内存泄漏 bpf_map_delete_elem(&temp_filename_map, &pid_tgid); return 0; } // 清理关闭的FD条目 SEC("sys_enter_close") int trace_close_enter(struct trace_event_raw_sys_enter *ctx) { int fd = ctx->args[0]; bpf_map_delete_elem(&fd_to_filename_map, &fd); return 0; } char _license[] SEC("license") = "GPL";
内容的提问来源于stack exchange,提问作者Vibhav Kanakamedala
相关产品推荐
相关产品推荐

