generalize some mv code for ln, cp - #14623
Conversation
replace links atomically instead of unlinking first Forced replacement was unlink(dst) then create. The gap lets another user claim dst in a directory they can write to -- including a name the sticky bit forbids them to remove -- so a privileged ln hands them a trusted name.
Renaming the temp onto a destination that is already a link to the same inode does nothing and returns success, so the temp name survived: `ln -f a b` with b already hard-linked to a left a stray Cu* file. Unlink the temp after the rename either way; on the normal path it is already gone.
f70c1c4 to
8fcb17a
Compare
|
GNU testsuite comparison: |
Merging this PR will improve performance by 3.24%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ⚡ | Simulation | du_wide_tree[(5000, 500)] |
18.9 ms | 18.3 ms | +3.24% |
Tip
Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.
Comparing sylvestre:fix-3834-atomic-link-replace (8fcb17a) with main (ebcdac1)
Footnotes
-
50 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
|
|
A THIRD independent root cause, and this one is repo-wide: it blocks the Manifest job on EVERY variant, and nothing in this repository changed to cause it. ln: failed to create symbolic link '/bin/sh': Not a directory The `Install dependencies` step installs uutils, which replaces coreutils in PATH, so the very next line runs uutils' ln. uutils 0.12.0 rewrote the -f path to "replace links atomically instead of unlinking first" (uutils/coreutils#14623; a platform-specific follow-up is already filed as #14632). `ln -sf` takes exactly that rewritten path. The container is pinned by digest but `apk add` is not, so the bump landed mid-run and split one run cleanly by the clock. Build Bonito 35202293222, one run, same image, same command: base 09:23, base-hwe 09:45, base-nvidia 09:49, cosmic 09:52, niri 09:52, gnome 09:54 ALL PASS (uutils 0.11.0-r1) xfce 09:58:55, kde 10:02:03 BOTH FAIL (uutils 0.12.0-r0) Build Gurnard 35205505527 hit the identical error at 10:11 in pantheon/Manifest, which is what rules out a bonito-specific cause. `rm` followed by a plain `ln -s` never enters the rewritten -f path and behaves the same under GNU, busybox and uutils. NOT pinned to uutils 0.11.0-r1 on purpose. A pin here works, but it needs someone to notice and lift it once upstream fixes the regression, and this step already showed what an unpinned moving dependency costs. Removing /bin/sh mid-script is safe even though the step itself runs under `sh -e`: a running shell survives its own binary being unlinked, and its subshells fork the loaded image rather than re-exec the path. Verified both that and the symlinked-parent case (/bin -> usr/bin, matching wolfi-base) locally. Validated: yamllint under ./.yamllint.yml exits 0 with the same 7 pre-existing line-length warnings as origin/main; the workflow still parses; the 235 workflow tests pass. This merges origin/main first so the change does not revert #2559's Renovate bump of setup-runner to a23fee8. CANNOT be proven by this PR's CI: the job runs on workflow_dispatch/schedule against main, never on pull requests. The real test is the first build after merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L59jmWZu8kiH2G9Jx7tcws
|
By the way, $ cargo clippy -p uucore --features fs --all-targets --target x86_64-unknown-redox
error[E0425]: cannot find function `symlinkat` in module `rustix::fs`
--> src/uucore/src/lib/features/fs.rs:1250:21
|
1250 | rustix::fs::symlinkat(target, rustix::fs::CWD, dest).map_err(Into::into) |
|
this is why we need CI :) |
|
I think we should abandon redox as no one can maintain. |
|
@sylvestre Sorry, I think we should revert this; it is the only thing blocking a clean build on Redox. Then we can add check-only Redox job to CI: cargo check -all-targets --features feat_os_unix_redox --target x86_64-unknown-redox |
|
i am sorry but i am not going to revert such change for an OS for which we don't have CI |
|
@sylvestre I have uutils building without errors locally now, so I think we’re very close to being able to add CI. From my side, the remaining pieces are:
I’d really like to avoid letting the code rot in the meantime. |
|
this PR fixes security issues |
This reverts commit 4bf2727.
* generalize the mv code for ln, cp replace links atomically instead of unlinking first Forced replacement was unlink(dst) then create. The gap lets another user claim dst in a directory they can write to -- including a name the sticky bit forbids them to remove -- so a privileged ln hands them a trusted name. * ln: don't leave the temp behind when the rename is a no-op Renaming the temp onto a destination that is already a link to the same inode does nothing and returns success, so the temp name survived: `ln -f a b` with b already hard-linked to a left a stray Cu* file. Unlink the temp after the rename either way; on the normal path it is already gone.
No description provided.