Skip to content

cp: open file only once (+32.93%) + add tests for mv & cp - #14272

Merged
sylvestre merged 3 commits into
uutils:mainfrom
sylvestre:cp-open-once
Aug 30, 2026
Merged

cp: open file only once (+32.93%) + add tests for mv & cp#14272
sylvestre merged 3 commits into
uutils:mainfrom
sylvestre:cp-open-once

Conversation

@sylvestre

Copy link
Copy Markdown
Contributor

No description provided.

@codspeed-hq

codspeed-hq Bot commented Aug 30, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 32.93%

⚡ 5 improved benchmarks
✅ 6 untouched benchmarks
⏩ 400 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation cp_recursive_wide_tree[(6000, 800)] 333.8 ms 236.1 ms +41.4%
Simulation cp_recursive_balanced_tree[(5, 4, 10)] 130.1 ms 96.6 ms +34.59%
Simulation cp_preserve_metadata[(5, 4, 10)] 135.8 ms 102.5 ms +32.52%
Simulation cp_archive_balanced_tree[(5, 4, 10)] 143.2 ms 109.8 ms +30.38%
Simulation cp_recursive_deep_tree[(120, 4)] 22.9 ms 18.1 ms +26.25%

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:cp-open-once (9ddecff) with main (abe8aaf)2

Open in CodSpeed

Footnotes

  1. 400 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.

  2. No successful run was found on main (6cb1d5e) during the generation of this report, so abe8aaf was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@github-actions

Copy link
Copy Markdown

Binary size comparison:

Individual binary size comparison VS main (threshold: >=5% AND >=4 KB).

Total size of compared binaries: 152.18 MB (+1000 KB, +0.65%)

Significant per-binary changes:
  comm     1.12 MB ->    2.33 MB  (+1.21 MB, +107.29%)

@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

GNU test failed: tests/tail/truncate. tests/tail/truncate is passing on 'main'. Maybe you have to rebase?
Skipping an intermittent issue tests/pr/bounded-memory (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/tail/symlink (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/tail/tail-n0f (passes in this run but fails in the 'main' branch)
Congrats! The gnu test tests/printf/printf-surprise is now passing!

@sylvestre
sylvestre marked this pull request as ready for review August 30, 2026 17:58
Copilot AI lite review requested due to automatic review settings August 30, 2026 17:58
Every regular-file copy on Linux opened the source three times: once in
check_for_data, once in check_sparse_detection, then again in the copy
itself. Open it once and thread the descriptor through the probe and the
copy, so the strategy and the bytes come from the same fd.

  before: open("src", O_RDONLY) x3 + open("dst", O_WRONLY|O_CREAT) x1
  after:  open("src", O_RDONLY) x1 + open("dst", O_WRONLY|O_CREAT) x1

Copying 3000 small files, 50 runs: 199.9ms +/- 14.9 -> 171.7ms +/- 23.6.
The ratio's error bars overlap; the stable signal is system time,
177.3ms -> 146.9ms, with user time unchanged at ~18ms.
New util/check-open-once.sh straces cp and asserts the source path is
opened exactly once, across the default, --sparse=always and
--sparse=never paths. Verified it fails on the previous code with 'opened
the source 3 times', so it guards the invariant rather than restating it.

Kept out of check-toctou.sh: no exploit is known for the old window --
every open used the same credentials and flags -- and the security
scripts should stay about attacker-exploitable splits. The new header
says so and points at the two that do.

Traces open(2) as well as openat(2): rustix issues the former on Linux,
so an openat-only filter matches nothing and passes vacuously.
mv already holds the invariant: it has its own cross-device fallback
rather than cp's copy machinery, so it never had the three-open problem.
These pin it in place, since the pending fd-anchored rework of the EXDEV
directory path (GHSA-pp2g) touches exactly this code.

Three assertions: a same-filesystem move must open the source zero times
(it is a rename, not a copy), and the cross-device file and directory
fallbacks must open each source exactly once. Each was verified to fail
by injecting a redundant File::open at the matching call site.

The directory case matches any path ending in the source name opened
O_RDONLY, so it keeps working once the traversal opens by bare name
under a directory fd. Cross-device needs a second filesystem; /dev/shm
supplies it, and the checks skip loudly when it shares a device with
TEMP_DIR rather than passing vacuously.

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@sylvestre sylvestre changed the title cp: open file only once + add tests for mv & cp cp: open file only once (+32.93%) + add tests for mv & cp Aug 30, 2026
@sylvestre
sylvestre merged commit 21a5e91 into uutils:main Aug 30, 2026
102 of 103 checks passed
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