Skip to content

fix(certgen): stage temp dir inside output dir to fix cross-device rename#2241

Merged
TaylorMutch merged 1 commit into
NVIDIA:mainfrom
gracesmith6504:fix/2173-certgen-cross-device-rename
Jul 13, 2026
Merged

fix(certgen): stage temp dir inside output dir to fix cross-device rename#2241
TaylorMutch merged 1 commit into
NVIDIA:mainfrom
gracesmith6504:fix/2173-certgen-cross-device-rename

Conversation

@gracesmith6504

Copy link
Copy Markdown
Contributor

Summary

The temp dir was placed beside the output dir instead of inside it. When --output-dir is a volume mount, the sibling ends up on a different filesystem -- std::fs::rename fails with EXDEV (os error 18). Moving the temp inside the output dir keeps both on the same filesystem.

Related Issue

Fixes #2173

Changes

  • staging_temp_dir: use dir.join(".certgen.tmp") instead of dir.with_file_name(...) -- places temp inside the output dir
  • write_local_bundle, write_local_tls_bundle, write_local_jwt_bundle: move create_dir_restricted calls before staging_temp_dir so the output dir exists before staging inside it
  • Renamed sibling_temp_dir to staging_temp_dir to reflect the new placement
  • Updated unit test to assert the new path

Testing

  • mise run pre-commit passes
  • Unit tests added/updated
  • E2E tests added/updated (not applicable)

Reproduced EXVED on WSL with Podman + named volume, confirmed patched binary succeeds with "PKI files created dir=/extvol".

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Architecture docs updated (not applicable)

…name

generate-certs stages temp files beside --output-dir using
dir.with_file_name(). When --output-dir is a container or WSL volume
mount, the staging dir lands on a different filesystem and
std::fs::rename fails with EXDEV.

Move staging inside the output dir so the rename always stays on the
same filesystem, preserving atomic replacement.

Fixes NVIDIA#2173

Signed-off-by: Grace Smith <grasmith@redhat.com>
@copy-pr-bot

copy-pr-bot Bot commented Jul 13, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown

All contributors have signed the DCO ✍️ ✅
Posted by the DCO Assistant Lite bot.

@gracesmith6504

Copy link
Copy Markdown
Contributor Author

I have read the DCO document and I hereby sign the DCO.

@gracesmith6504

Copy link
Copy Markdown
Contributor Author

recheck

@r3v5 r3v5 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 Automatic review using OpenShell's skill review-github-pr in Claude Code with Opus 4.6 (1M context)

PR: #2241
Author: Grace Smith
Branch: fix/2173-certgen-cross-device-rename -> main

Overview

Fixes EXDEV (cross-device rename) when --output-dir is a volume mount. Temp dir was created as sibling (dir.with_file_name(...)) — lands on different filesystem from target. Now created inside output dir (dir.join(...)) so rename stays same-filesystem.

Key Design Decisions

  • Temp dir placement changed from sibling to child (certgen.rs:771-773): dir.join(".certgen.tmp") instead of dir.with_file_name(name). Core fix — guarantees same-filesystem rename.
  • create_dir_restricted moved before staging (certgen.rs:627-628, 690-691, 733): Output dir must exist before creating temp subdir inside it. Previously created after staging, which would fail with new approach.
  • Dotfile naming convention (.certgen.tmp): Hidden prefix avoids collision with real cert artifacts.

Notable Code

crates/openshell-server/src/certgen.rs:769-773
 fn staging_temp_dir(dir: &Path) -> PathBuf {
     // Place temp inside dir so rename stays on the same filesystem as the destination.
     dir.join(".certgen.tmp")
 }

Potential Concerns

  • Stale temp cleanup on crash: If process dies mid-write, .certgen.tmp now lives inside output dir rather than beside it. Next run cleans it up (existing remove_dir_all logic handles this), but operators browsing output dir might see it. Dotfile prefix mitigates visibility.
  • create_dir_restricted now called twice for some dirs (e.g. server_dir, client_dir in write_local_tls_bundle): once early at line 690-691, then the original calls were removed. Net effect correct but worth confirming no ordering assumption elsewhere depends on late creation.

Clean, minimal fix. Net -2 lines. Tests updated. Looks good.

@TaylorMutch

Copy link
Copy Markdown
Collaborator

/ok to test d7ffd0a

@TaylorMutch TaylorMutch added the test:e2e Requires end-to-end coverage label Jul 13, 2026
@github-actions

Copy link
Copy Markdown

Label test:e2e applied for d7ffd0a. Open the existing run and click Re-run all jobs to execute with the label set. The run will execute the standard E2E suite after building the required gateway and supervisor images once. The matching required CI gate status on this PR will flip green automatically once the run finishes.

@TaylorMutch TaylorMutch left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed the certgen staging change. The implementation is focused and correct, focused certgen tests pass locally, and the full labeled E2E suite completed successfully.

@TaylorMutch TaylorMutch merged commit 4e1ffef into NVIDIA:main Jul 13, 2026
63 of 65 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test:e2e Requires end-to-end coverage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

certgen: std::fs::rename fails with EXDEV on WSL/container cross-device mounts

3 participants