fix(cli): preserve EROFS errno on share mount RO target#4315
Conversation
Node's `fs.mkdirSync(path, { recursive: true })` masks EROFS as ENOENT
when the leaf is missing on a read-only parent. The recursive call in
`checkLocalMountWritable` therefore never hit the EROFS branch, so
`nemoclaw <name> share mount /sandbox <target>` reported the misleading
"ENOENT: no such file or directory, mkdir '<...>'" instead of the
intended "parent filesystem is read-only" message.
When the immediate parent already exists, fall back to non-recursive
`fs.mkdirSync(localMount)` so the kernel's EROFS errno propagates and
the existing EROFS branch fires. Treat EEXIST from the non-recursive
call as success (the leaf was already present from a prior mount).
Recursive mkdir is still used when the parent is genuinely missing.
Fixes #4311.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Jason Ma <jama@nvidia.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughcheckLocalMountWritable now checks whether the parent directory exists and uses non-recursive mkdir when it does (so EROFS propagates); it falls back to recursive mkdir only when the parent is missing. Tests were added to verify non-recursive vs recursive mkdir behavior, EROFS mapping, EEXIST handling, and subsequent writability checks. ChangesEROFS error propagation in directory creation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
E2E Advisor RecommendationRequired E2E: None Dispatch hint: Full advisor summaryE2E Recommendation AdvisorBase: Required E2E
Optional E2E
New E2E recommendations
Dispatch hint
|
E2E Scenario Advisor RecommendationRequired scenario E2E: None Full scenario advisor summaryE2E Scenario AdvisorBase: Required scenario E2E
Optional scenario E2E
Relevant changed files
|
PR Review AdvisorFindings: 0 needs attention, 2 worth checking, 0 nice ideas Review findings🛠️ Needs attention
🔎 Worth checking
🌱 Nice ideas
Since last review detailsCurrent findings:
This is an automated advisory review. A human maintainer must make the final merge decision. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@src/lib/share-command.ts`:
- Around line 131-133: The EEXIST fast-path in the catch block for localMount
incorrectly treats any existing file as a successful mount; update the handler
to stat/lstat the path (localMount) when errno === "EEXIST" and only swallow the
error if the target exists and is a directory (isDirectory() true); if it exists
but is not a directory, rethrow the error (or convert to a clear error) so the
later writable check doesn't report writable for an invalid mount target.
🪄 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.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: f0d33560-5bbc-4473-bd3e-1a0a8a6e1c7c
📒 Files selected for processing (2)
src/lib/share-command.tstest/share-command-writable.test.ts
Address coderabbitai review on #4315: swallowing EEXIST from the non-recursive `fs.mkdirSync` was a regression. If `localMount` is an existing regular file, the prior recursive `mkdirSync` also threw EEXIST but it surfaced through the generic catch as an error; the new EEXIST fast-path silently fell through to `accessSync`, which passes on a writable file and returned `{ writable: true }` — a false positive that would let SSHFS try to mount over a regular file. On EEXIST, stat the path and only swallow the error if the existing entry is a directory; otherwise return a clear "mount target exists and is not a directory" reason. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Jason Ma <jama@nvidia.com>
Summary
nemoclaw <name> share mount /sandbox <target>was reporting "ENOENT: no such file or directory, mkdir '<...>'" when the local target's parent filesystem was read-only, instead of the intended "parent filesystem is read-only" message. Root cause: Node'sfs.mkdirSync(path, { recursive: true })masks EROFS as ENOENT when the leaf is missing on a read-only parent, so the EROFS branch incheckLocalMountWritablenever fired.Related Issue
Fixes #4311.
Changes
src/lib/share-command.ts: when the immediate parent oflocalMountalready exists, callfs.mkdirSync(localMount)without{ recursive: true }so the kernel's EROFS errno propagates and the existing EROFS branch reports "parent filesystem is read-only". TreatEEXISTfrom the non-recursive call as success (the leaf is already present from a prior mount). Fall back to recursive mkdir only when the parent is genuinely missing.test/share-command-writable.test.ts: four new#4311tests covering non-recursive vs recursive branch selection, EROFS propagation on an existing parent, and EEXIST idempotency.Type of Change
Verification
npx prek run --all-filespassesnpm testpassesnpm run docsbuilds without warnings (doc changes only)Signed-off-by: Jason Ma jama@nvidia.com
Summary by CodeRabbit
Bug Fixes
Tests