Skip to content

fix: fs_watch calls trace_file_init instead of mountsnoop_init with path argument #111

Description

@JoeSergen

Bug Report

Affected Code

File: so/dkapture.cpp, line 388, function dkapture::fs_watch()

Description

When fs_watch() is called with a non-null path, the else branch calls trace_file_init() instead of mountsnoop_init(). This is a copy-paste error from file_watch().

int dkapture::fs_watch(const char *path, DKCallback cb, void *ctx)
{
    if (!cb)    return mountsnoop_deinit();        // ✓ correct
    if (!path)  return mountsnoop_init(1, ...);     // ✓ correct
    else        return trace_file_init(3, ...);     // ✗ BUG: should be mountsnoop_init
}

Why mountsnoop_init is Correct

fs_watch is supposed to monitor filesystem-level mount/umount events, not per-file I/O operations. The evidence comes from comparing what each function does at the BPF kernel level:

mountsnoop_init — Filesystem Event Monitoring

observe/mountsnoop.cpp:87 — The -p argument is stored as a path prefix:

strncpy(filter_path, arg, PATH_MAX);

bpf/observe/mountsnoop.bpf.c:36-42 — BPF map stores a char[4096] path string:

struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __type(value, char[4096]);
} filter SEC(".maps");

bpf/observe/mountsnoop.bpf.c:44-60filter_path() does prefix matching on mount source/target paths:

static bool filter_path(const char *path, int n) {
    char *rule_path = bpf_map_lookup_elem(&filter, &key);
    if (!rule_path || rule_path[0] == '\0') return true;
    if (!path) return false;
    return strncmp(path, rule_path, n) == 0;
}

Monitored events (mountsnoop.bpf.c): mount, umount, fsopen, fsconfig, fsmount, move_mount, fspick, mount_setattr, open_tree — all filesystem-level syscalls.

trace_file_init — Per-File I/O Monitoring

observe/trace-file.cpp:1206-1212 — The -p argument is used to open()+fstat() the file, resolving it to an inode:

target_fd = open(rule.path, O_RDONLY);
fstat(target_fd, &statbuf);
rule.inode = statbuf.st_ino;

bpf/observe/trace-file.bpf.c:26-35 — BPF filter uses inode+dev for matching:

union Rule {
    char path[PATH_MAX];
    struct { u64 not_inode; u64 inode; dev_t dev; };
};

bpf/observe/trace-file.bpf.c:128-146find_file_inode resolves path to inode:

if (rule->not_inode) strncmp(path, rule->path, PATH_MAX);  // path matching
else {
    rule->dev != file->f_path.mnt->mnt_sb->s_dev;           // device matching
    rule->inode != BPF_CORE_READ(file, f_inode, i_ino);     // inode matching
}

Monitored events (trace-file.bpf.c, 3749 lines): vfs_open, vfs_read, vfs_write, vfs_chmod, vfs_chown, vfs_removexattr — all per-file I/O operations.

Summary

trace_file_init mountsnoop_init
Monitors Single file (by inode) Filesystem mount/umount events
-p semantics File path → inode lookup Path prefix for filtering mount source/target
BPF hooks fexit/vfs_open, vfs_read, vfs_write, ... tracepoint/.../sys_enter_mount, sys_enter_umount, ...

Calling trace_file_init from fs_watch is semantically wrong — it would try to open the given path as a regular file, resolve its inode, and attach file I/O hooks, which is completely unrelated to filesystem event monitoring.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions