Skip to content

tar: report members containing '..' and exit with status 2 - #397

Merged
sylvestre merged 2 commits into
uutils:mainfrom
ozenyunus35:fix/386-dotdot-member-exit-status
Oct 13, 2026
Merged

sylvestre merged 2 commits into
uutils:mainfrom
ozenyunus35:fix/386-dotdot-member-exit-status

Conversation

@ozenyunus35

Copy link
Copy Markdown
Contributor

Summary

Fixes #386.

When extracting an archive containing a member with a .. path component, tar currently skips that member silently and exits successfully.

This happens because the return value of unpack_in is ignored when it refuses to extract an unsafe path.

This PR changes that behavior to:

  • Report archive members rejected because their paths contain ...
  • Continue extracting the remaining valid members.
  • Exit with status 2 if any members were rejected.
  • Avoid printing "Error is not recoverable" when extraction continued successfully for other members.

Testing

Added a regression test that creates an archive containing an unsafe ../ member followed by a valid member.

The test verifies that:

  • The unsafe member is rejected and reported.
  • The valid member is extracted.
  • No file is written outside the extraction directory.
  • The command exits with status 2.
  • The non-recoverable error message is not printed.

The regression test fails on the original implementation and passes with the fix.

Additional checks passed:

  • cargo test --workspace
  • CI-equivalent Clippy checks with -D warnings
  • cargo fmt --all -- --check

Notes

The error reporting follows the existing project's filename-display conventions. Some formatting differences from GNU tar remain, including escaping special characters in member names.

AI Assistance

Claude Code assisted with implementation and testing. I reviewed the changes and test results before submitting.

When extracting, tar-rs refuses a member whose name contains a `..`
component, so that it cannot be written outside the current directory,
and reports that through the return value of `unpack_in`. That value
was ignored: the member was skipped silently and tar exited 0.

GNU tar reports such a member, extracts the remaining ones, and exits 2
with "Exiting with failure status due to previous errors". Do the same,
and only print "Error is not recoverable: exiting now" when the exit
status comes from an error that stopped tar, not from one it recovered
from.

Fixes uutils#386
@ozenyunus35
ozenyunus35 requested a review from kaladron as a code owner October 11, 2026 16:48
Comment thread src/bin/tarapp.rs
// If exiting with code 2 because of an error, show the "not recoverable"
// message. An exit code set through `set_exit_code` instead records errors that
// tar reported and recovered from, such as members it refused to extract.
if exit_code == 2 && uucore::error::get_exit_code() != 2 {

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.

this relies on nobody else calling set_exit_code(2) before a fatal error, not super robust
could you please document the why in one line?

Comment thread src/uu/tar/src/operations/extract.rs Outdated
out.flush().map_err(TarError::Io)?;
writeln!(
io::stderr(),
"tar: {}: Member name contains '..'",

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.

please add a variant in TarError (errors.rs) instead of a hardcoded string

Comment thread src/uu/tar/src/operations/extract.rs Outdated
if refused_member {
writeln!(
io::stderr(),
"tar: Exiting with failure status due to previous errors"

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.

same here, please move it to errors.rs
this isn't specific to extract either, it might be better in one shared place

@ozenyunus35

Copy link
Copy Markdown
Contributor Author

@sylvestre moved both messages into TarError (MemberNameContainsDotDot and PreviousErrors) in 82c7f71. The exit status is now set by one helper in errors.rs right before returning, so a fatal error can no longer come after it.

@codspeed

codspeed Bot commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

⚠️ 4 benchmarks spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

✅ 7 untouched benchmarks


Comparing ozenyunus35:fix/386-dotdot-member-exit-status (82c7f71) with main (ae582f4)

Open in CodSpeed

@codecov

codecov Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.82%. Comparing base (791ae26) to head (82c7f71).
⚠️ Report is 84 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #397      +/-   ##
==========================================
+ Coverage   96.84%   97.82%   +0.97%     
==========================================
  Files          11       15       +4     
  Lines        1492     1794     +302     
  Branches       29       36       +7     
==========================================
+ Hits         1445     1755     +310     
+ Misses         46       38       -8     
  Partials        1        1              
Flag Coverage Δ
macos_latest 97.82% <100.00%> (+0.97%) ⬆️
ubuntu_latest 97.82% <100.00%> (+0.97%) ⬆️
windows_latest 0.00% <0.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sylvestre
sylvestre merged commit a42dbcc into uutils:main Oct 13, 2026
20 checks passed
@sylvestre

Copy link
Copy Markdown
Contributor

Thanks for your PR

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.

tar: skipping a ../ member does not affect the exit status

2 participants