Skip to content

ROX-33036: add mount related operations - #1059

Draft
Molter73 wants to merge 6 commits into
mauro/feat/inodes-introspectionfrom
mauro/ROX-33036/track-mount
Draft

ROX-33036: add mount related operations#1059
Molter73 wants to merge 6 commits into
mauro/feat/inodes-introspectionfrom
mauro/ROX-33036/track-mount

Conversation

@Molter73

@Molter73 Molter73 commented Jul 9, 2026

Copy link
Copy Markdown
Member

Description

This was originally going to be about adding just lsm/sb_mount, however while adding this hook it became pretty clear we needed lsm/sb_umount and lsm/move_mount for a comprehensive implementation and it really didn't add too much code, so they are all added in.

These operations are not currently intended to be forwarded via gRPC, they only trigger inode tracking related behavior (scans on new/moved mounts, inode map cleanups on moved/unmounted directories).

The move mount operation shares quite a bit of similarities with rename, so there is a bit of refactoring mixed in so they can be reused while keeping the code clean.

Checklist

  • Patch has a change log entry OR does not need one.
  • Investigated and inspected CI test results
  • Updated documentation accordingly

Automated testing

  • Added unit tests
  • Added integration tests
  • Added regression tests

If any of these don't apply, please comment below.

Testing Performed

Added integration tests.

Summary by CodeRabbit

  • New Features

    • Added monitoring for mount, unmount, and move-mount activity.
    • Automatically triggers a full filesystem scan when mount-related changes occur.
    • Added metrics for mount-related event processing.
    • Added inode-tracking coverage for mounted directories.
  • Bug Fixes

    • Improved rename event handling and output serialization.
    • Updated endpoint hot-reload behavior to return HTTP 503 when all endpoints are disabled.
  • Documentation

    • Added mount-related operations to the upcoming changelog.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Enterprise

Run ID: e1328f62-9bda-42a7-86b7-188280f65b37

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mauro/ROX-33036/track-mount

Comment @coderabbitai help to get the list of available commands.

@codecov-commenter

codecov-commenter commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 139 lines in your changes missing coverage. Please review.
✅ Project coverage is 35.04%. Comparing base (91987cb) to head (4a5ee00).

Files with missing lines Patch % Lines
fact/src/event/mod.rs 0.00% 130 Missing ⚠️
fact/src/host_scanner.rs 0.00% 9 Missing ⚠️
Additional details and impacted files
@@                         Coverage Diff                         @@
##           mauro/feat/inodes-introspection    #1059      +/-   ##
===================================================================
- Coverage                            35.42%   35.04%   -0.38%     
===================================================================
  Files                                   22       22              
  Lines                                 3241     3276      +35     
  Branches                              3241     3276      +35     
===================================================================
  Hits                                  1148     1148              
- Misses                                2088     2123      +35     
  Partials                                 5        5              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Molter73
Molter73 force-pushed the mauro/ROX-33036/track-mount branch from 3cc1980 to b15f103 Compare July 20, 2026 09:12
@Molter73
Molter73 force-pushed the mauro/ROX-33036/track-mount branch from b15f103 to 0eb199b Compare July 27, 2026 14:50
@Molter73
Molter73 changed the base branch from main to mauro/feat/inodes-introspection July 27, 2026 14:50
Molter73 added 2 commits July 27, 2026 17:34
This was originally going to be about adding just `lsm/sb_mount`,
however while adding this hook it became pretty clear we needed
`lsm/sb_umount` and `lsm/move_mount` for a comprehensive implementation
and it really didn't add too much code, so they are all added in.

These operations are not currently intended to be forwarded via gRPC,
they only trigger inode tracking related behavior (scans on new/moved
mounts, inode map cleanups on moved/unmounted directories).

The move mount operation shares quite a bit of similarities with rename,
so there is a bit of refactoring mixed in so they can be reused while
keeping the code clean.

TODO: add integration tests.
Add a basic test for checking mount operations are properly tracked.
This test works by checking the monitored directory is tracked using the
inodes introspection endpoint, then a tmpfs is mounted on top of this
directory and we validate we see the new inode, then we unmount and
check the introspection endpoint one last time.
@Molter73
Molter73 force-pushed the mauro/ROX-33036/track-mount branch from 0eb199b to 5e41f99 Compare July 28, 2026 09:37
@Molter73

Copy link
Copy Markdown
Member Author

/retest

@Molter73
Molter73 force-pushed the mauro/ROX-33036/track-mount branch from a0d0d78 to edc7efa Compare July 28, 2026 16:11

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
fact/src/event/mod.rs (1)

657-687: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

PartialEq for FileData missing arms for Mount, Umount, MoveMount.

Every other variant (including all pre-existing ones) has an explicit equality arm, but the three new mount variants fall through to _ => false. This means two identical Mount/Umount/MoveMount events will never compare equal.

🐛 Proposed fix
             (FileData::AclSet(this), FileData::AclSet(other)) => {
                 this.inner == other.inner
                     && this.acl_type == other.acl_type
                     && this.entries == other.entries
             }
+            (FileData::Mount(this), FileData::Mount(other)) => this == other,
+            (FileData::Umount(this), FileData::Umount(other)) => this == other,
+            (
+                FileData::MoveMount { to: l_to, from: l_from },
+                FileData::MoveMount { to: r_to, from: r_from },
+            ) => l_to == r_to && l_from == r_from,
             _ => false,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fact/src/event/mod.rs` around lines 657 - 687, Add explicit equality arms to
PartialEq::eq for FileData covering Mount, Umount, and MoveMount, comparing each
variant’s corresponding fields consistently with the other variant arms. Keep
the fallback _ => false for differing variants.
🧹 Nitpick comments (3)
fact/src/host_scanner.rs (1)

429-439: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

Mount events trigger a synchronous full scan per event, with no coalescing for bursts.

handle_mount_event calls self.scan() directly and inline in the select! branch, blocking the task for the scan's duration and running once per mount-related event with no debouncing. The existing scan_trigger/Notify mechanism used for periodic and paths.changed() scans already coalesces repeated triggers (multiple notify_one() calls before consumption collapse to a single pending scan), but that path isn't reused here. Under a burst of mount/unmount activity this could serialize many redundant full scans and delay processing of other events in the loop (e.g. introspection queries).

Also applies to: 493-497

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fact/src/host_scanner.rs` around lines 429 - 439, Update handle_mount_event
to signal the existing scan_trigger/Notify mechanism instead of calling
self.scan() synchronously. Route mount events through the same coalesced scan
path used by periodic and paths.changed() triggers, preserving a single pending
scan during event bursts and keeping the select loop responsive.
fact/src/event/mod.rs (1)

437-535: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Consider adding Rust-side test coverage for the new Mount/Umount/MoveMount variants.

Codecov flags 130 uncovered lines in this file for this PR, and the PartialEq gap above went unnoticed likely due to this. EventTestData (used by the #[cfg(all(test, feature = "bpf-test"))] constructor) doesn't have variants for the new mount events either.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fact/src/event/mod.rs` around lines 437 - 535, Add Rust-side tests covering
FileData::Mount, FileData::Umount, and FileData::MoveMount, including their
constructed fields and equality behavior. Extend EventTestData and its bpf-test
constructor with corresponding mount-event variants so these cases can be
exercised through the existing test path, and ensure the tests cover both source
and destination data for MoveMount.
tests/test_mount.py (1)

44-50: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Subprocess calls don't check exit status.

mount/umount failures (e.g. wrong tmpfs support, misconfigured environment) will pass silently, and the test would then just fail (or worse, pass) based on stale inode state rather than surfacing the real cause.

♻️ Proposed fix
-    subprocess.run(
-        ['mount', '-t', 'tmpfs', '-o', 'size=10M', 'tmpfs', monitored_dir]
-    )
+    subprocess.run(
+        ['mount', '-t', 'tmpfs', '-o', 'size=10M', 'tmpfs', monitored_dir],
+        check=True,
+    )
     assert_tracked_path(monitored_dir)

-    subprocess.run(['umount', monitored_dir])
+    subprocess.run(['umount', monitored_dir], check=True)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_mount.py` around lines 44 - 50, Update the subprocess.run calls in
the mount/umount test to enforce successful exit status, so mount or unmount
failures raise immediately instead of allowing assertions to use stale inode
state. Preserve the existing command arguments and tracking assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CHANGELOG.md`:
- Line 9: Update the ROX-33036 changelog entry to hyphenate “mount-related” in
the description of the operations.

In `@fact-ebpf/src/bpf/main.c`:
- Around line 557-603: Update trace_move_mount so events are emitted for every
monitored classification except MONITORED_NOT, matching the sb_mount/sb_umount
handling and allowing parent/path-monitored moves to reach rescan. In the same
function, derive args.parent_inode from to’s actual parent inode rather than
reusing to->dentry->d_inode, and pass that value to is_monitored.

---

Outside diff comments:
In `@fact/src/event/mod.rs`:
- Around line 657-687: Add explicit equality arms to PartialEq::eq for FileData
covering Mount, Umount, and MoveMount, comparing each variant’s corresponding
fields consistently with the other variant arms. Keep the fallback _ => false
for differing variants.

---

Nitpick comments:
In `@fact/src/event/mod.rs`:
- Around line 437-535: Add Rust-side tests covering FileData::Mount,
FileData::Umount, and FileData::MoveMount, including their constructed fields
and equality behavior. Extend EventTestData and its bpf-test constructor with
corresponding mount-event variants so these cases can be exercised through the
existing test path, and ensure the tests cover both source and destination data
for MoveMount.

In `@fact/src/host_scanner.rs`:
- Around line 429-439: Update handle_mount_event to signal the existing
scan_trigger/Notify mechanism instead of calling self.scan() synchronously.
Route mount events through the same coalesced scan path used by periodic and
paths.changed() triggers, preserving a single pending scan during event bursts
and keeping the select loop responsive.

In `@tests/test_mount.py`:
- Around line 44-50: Update the subprocess.run calls in the mount/umount test to
enforce successful exit status, so mount or unmount failures raise immediately
instead of allowing assertions to use stale inode state. Preserve the existing
command arguments and tracking assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Enterprise

Run ID: 05fc42f0-315c-46df-9f9b-5b00f13b4fd4

📥 Commits

Reviewing files that changed from the base of the PR and between 91987cb and 4a5ee00.

📒 Files selected for processing (12)
  • CHANGELOG.md
  • fact-ebpf/src/bpf/bound_path.h
  • fact-ebpf/src/bpf/events.h
  • fact-ebpf/src/bpf/main.c
  • fact-ebpf/src/bpf/types.h
  • fact-ebpf/src/lib.rs
  • fact/src/event/mod.rs
  • fact/src/host_scanner.rs
  • fact/src/metrics/kernel_metrics.rs
  • tests/conftest.py
  • tests/test_config_hotreload.py
  • tests/test_mount.py

Comment thread CHANGELOG.md

## Next

* ROX-33036: add mount related operations (#1059)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Minor grammar: hyphenate "mount-related".

✏️ Proposed fix
-* ROX-33036: add mount related operations (`#1059`)
+* ROX-33036: add mount-related operations (`#1059`)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
* ROX-33036: add mount related operations (#1059)
* ROX-33036: add mount-related operations (`#1059`)
🧰 Tools
🪛 LanguageTool

[grammar] ~9-~9: Use a hyphen to join words.
Context: ...acking. ## Next * ROX-33036: add mount related operations (#1059) * feat(endpoi...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CHANGELOG.md` at line 9, Update the ROX-33036 changelog entry to hyphenate
“mount-related” in the description of the operations.

Source: Linters/SAST tools

Comment thread fact-ebpf/src/bpf/main.c
Comment on lines +557 to +603
SEC("lsm/move_mount")
int BPF_PROG(trace_move_mount, struct path* from, struct path* to) {
struct metrics_t* m = get_metrics();
if (m == NULL) {
return 0;
}
struct submit_event_args_t args = {.metrics = &m->move_mount};

args.metrics->total++;

struct bound_path_t* to_path = path_read_unchecked(to, false);
if (to_path == NULL) {
bpf_printk("Failed to read to_path");
goto error;
}
args.filename = to_path->path;

struct bound_path_t* from_path = path_read_alt_unchecked(from, false);
if (from_path == NULL) {
bpf_printk("Failed to read from_path");
goto error;
}

args.inode = inode_to_key(to->dentry->d_inode);
args.parent_inode = inode_to_key(to->dentry->d_inode);
args.monitored = is_monitored(&args.inode, to_path, &args.parent_inode);

inode_key_t from_inode = inode_to_key(from->dentry->d_inode);
monitored_t from_monitored = is_monitored(&from_inode, from_path, NULL);

if (args.monitored != MONITORED_BY_INODE) {
args.metrics->ignored++;
return 0;
}

// Ensure the new mount is tracked.
if (from_monitored != MONITORED_BY_INODE) {
inode_add(&from_inode);
}

submit_move_mount_event(&args, from_path->path, &from_inode, from_monitored);
return 0;

error:
args.metrics->error++;
return 0;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the relevant hooks and inspect the surrounding code.
git ls-files fact-ebpf/src/bpf/main.c
ast-grep outline fact-ebpf/src/bpf/main.c --view expanded

printf '\n--- trace_sb_mount / trace_sb_umount / trace_move_mount ---\n'
sed -n '470,620p' fact-ebpf/src/bpf/main.c

Repository: stackrox/fact

Length of output: 5221


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the monitoring helper and the enum values it returns.
rg -n "is_monitored|monitored_t|MONITORED_BY_PARENT|MONITORED_BY_INODE|NOT_MONITORED" fact-ebpf/src/bpf/main.c fact-ebpf/src/bpf -g '!**/target/**'

printf '\n--- is_monitored context ---\n'
# Read the helper definition once located.
helper_file=$(rg -l "is_monitored" fact-ebpf/src/bpf | head -n 1)
if [ -n "${helper_file:-}" ]; then
  sed -n '1,260p' "$helper_file"
fi

Repository: stackrox/fact

Length of output: 9290


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- inode_is_monitored ---'
sed -n '100,140p' fact-ebpf/src/bpf/inode.h

printf '\n%s\n' '--- submit_move_mount_event ---'
sed -n '210,280p' fact-ebpf/src/bpf/events.h

Repository: stackrox/fact

Length of output: 2181


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect any move-mount specific handling and nearby monitoring rules.
rg -n "FILE_ACTIVITY_MOVE_MOUNT|move_mount|MONITORED_BY_PARENT|MONITORED_BY_PATH|ignored\+\+" fact-ebpf -g '!**/target/**'

printf '\n--- event type definitions ---\n'
sed -n '1,260p' fact-ebpf/src/bpf/types.h

printf '\n--- move-mount event payload and consumers ---\n'
sed -n '1,320p' fact-ebpf/src/bpf/events.h

Repository: stackrox/fact

Length of output: 17119


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# See whether parent_inode is consumed anywhere outside event serialization.
rg -n "\.parent_inode\b|parent_inode" fact-ebpf fact -g '!**/target/**' -g '!**/node_modules/**'

Repository: stackrox/fact

Length of output: 4186


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- host_scanner.rs move_mount handling ---'
sed -n '250,390p' fact/src/host_scanner.rs

printf '\n%s\n' '--- event/mod.rs relevant accessors ---'
sed -n '150,240p' fact/src/event/mod.rs

Repository: stackrox/fact

Length of output: 10010


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- host_scanner.rs move_mount handling later in file ---'
sed -n '390,520p' fact/src/host_scanner.rs

printf '\n%s\n' '--- FileData::MoveMount definitions ---'
sed -n '430,760p' fact/src/event/mod.rs

Repository: stackrox/fact

Length of output: 18576


trace_move_mount is too strict about monitored state: it only emits when args.monitored == MONITORED_BY_INODE, so move_mounts under monitored parents/paths are ignored and never reach the rescan path. Match the sb_mount/sb_umount NOT_MONITORED check here; if parent-based classification is intended, also read to’s real parent inode instead of reusing to->dentry->d_inode.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fact-ebpf/src/bpf/main.c` around lines 557 - 603, Update trace_move_mount so
events are emitted for every monitored classification except MONITORED_NOT,
matching the sb_mount/sb_umount handling and allowing parent/path-monitored
moves to reach rescan. In the same function, derive args.parent_inode from to’s
actual parent inode rather than reusing to->dentry->d_inode, and pass that value
to is_monitored.

Source: Coding guidelines

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants