You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

编写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
}

你的代码报错原因及修复

报错根源

  1. Tracepoint参数定义错误:sys_enter_openat是tracepoint,其回调函数的参数必须是struct trace_event_raw_sys_enter *ctx,而非直接列出openat的系统调用参数(dfd、filename等),编译器无法识别这些未定义的参数,导致语法错误。
  2. 时机错误:在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 20:04:53