Skip to content

[Bug]: Linux without openat2 cannot complete nested no-clobber moves; native-off also disables required operations #511

Description

@ericpearson

Summary

On Linux 4.4.302+, fs-safe 0.13.1's Linux native path requires unavailable openat2 for nested paths. Native-off avoids that call but intentionally disables no-clobber Root.move(), leaving an OpenClaw retained-state migration without a working path. Please expose/validate the actual operation capabilities and consider secure older-kernel support while retaining native no-replace operations that the host does implement.

Minimal reproduction

Tested in a disposable published OpenClaw 2026.9.5 container, without production mounts, Node v24.19.0, with its installed @openclaw/fs-safe@0.13.1:

import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { root, configureFsSafeNative } from '@openclaw/fs-safe';

for (const mode of ['auto', 'off']) {
  const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'fs-safe-probe-'));
  try {
    await fs.mkdir(path.join(dir, 'nested'));
    await fs.writeFile(path.join(dir, 'nested/source'), 'probe-source', {mode: 0o600});
    configureFsSafeNative({mode});
    try {
      const scoped = await root(dir);
      await scoped.move('nested/source', 'nested/destination');
      console.log({mode, result: 'move succeeded'});
    } catch (error) {
      console.log({mode, code: error.code, message: error.message});
    }
  } finally {
    await fs.rm(dir, {recursive: true, force: true});
  }
}

Observed for both auto and off:

code: helper-unavailable
message: native no-replace move is unavailable

Positive control: with source and destination directly under the root (no nested/ prefix), native auto successfully moves the file on this host; off still refuses. Nested parent resolution matters.

Expected behavior

Keep collision and path-containment guarantees. Detect and report unavailable operations before a consumer starts migration; document the supported kernel/capability floor. If older-kernel support is intended, retain available native primitives and provide a reviewed resolver or explicitly labeled policy-controlled fallback. A userspace walk must not falsely report kernel-atomic containment. This is not a request to change no-clobber moves into overwriting rename or silently ignore safety failures.

Actual behavior and downstream impact

The Linux resolver calls openat2(BENEATH | NO_MAGICLINKS) directly. A real pinned-plugin installation failed with openat2 beneath root: Function not implemented (os error 38).

We then rehearsed the full downstream upgrade on Btrfs-cloned state with both OPENCLAW_FS_SAFE_NATIVE_MODE=off and FS_SAFE_NATIVE_MODE=off verified in the candidate. First Doctor exited 1:

Failed staging config audit log legacy recovery restore journal: FsSafeError: native no-replace move is unavailable

OpenClaw's stageAuditRecoveryRestore() writes and syncs a staging journal, then uses default no-overwrite Root.move() to publish it. Native-off returns no binding, so this fails as designed. The old installation was restored and independently verified healthy; candidate startup and subsequent gates were not reached.

A separate direct syscall probe on disposable files in the updater's Btrfs data mount returned ENOSYS for openat2, but renameat2(RENAME_NOREPLACE) correctly returned EEXIST for an occupied destination without changing either file, and succeeded for an absent destination. All probe resources were removed. This establishes capabilities in that tested container/filesystem, not every mount or security policy; syscall filtering was not independently excluded.

Package version or commit

@openclaw/fs-safe@0.13.1, source 7022a0a10c53e36f34a467df68ed5614a1db1741. Main 6953a1d85a92160f036b9ce9cfd5c8f096f92cbe still has direct openat2 resolution. Updating to current main has not been tested.

Affected surface

Atomic operations and file locking; rooted path resolution.

Environment

Synology DSM; Linux 4.4.302+; x86_64; Node v24.19.0; Docker. Minimal package reproducer uses disposable container temporary storage; real migration uses Btrfs bind mounts. No credentials, personal paths, hostnames, or private content included.

Cross-references and provenance

Searches for openat2, ENOSYS, Synology, and older-kernel reports found no matching existing fs-safe issue. Happy to consolidate if a canonical tracker exists.

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

    P2Normal priority bug or improvement with limited blast radius.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:securityThis issue is about security boundaries, credentials, authz, sandboxing, or sensitive data.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions