mirror of
https://github.com/uutils/tar.git
synced 2026-06-10 16:14:35 -07:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
815a1b3036 | ||
|
|
25f4bd7c61 | ||
|
|
26bc94342d | ||
|
|
cef2373196 | ||
|
|
8d4fef7aba | ||
|
|
3be98e9e5c | ||
|
|
3eb916a8c2 | ||
|
|
9161cb6e2d | ||
|
|
ea51e0cd4c | ||
|
|
31c9e09527 | ||
|
|
14185c5626 |
@@ -104,7 +104,7 @@ jobs:
|
|||||||
grcov . --binary-path="${COVERAGE_REPORT_DIR}" --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" --source-dir .
|
grcov . --binary-path="${COVERAGE_REPORT_DIR}" --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" --source-dir .
|
||||||
echo "report=${COVERAGE_REPORT_FILE}" >> $GITHUB_OUTPUT
|
echo "report=${COVERAGE_REPORT_FILE}" >> $GITHUB_OUTPUT
|
||||||
- name: Upload coverage results (to Codecov.io)
|
- name: Upload coverage results (to Codecov.io)
|
||||||
uses: codecov/codecov-action@v6
|
uses: codecov/codecov-action@v7
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
files: ${{ steps.coverage.outputs.report }}
|
files: ${{ steps.coverage.outputs.report }}
|
||||||
|
|||||||
Generated
+27
-65
@@ -185,9 +185,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.44"
|
version = "0.4.45"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@@ -409,33 +409,16 @@ dependencies = [
|
|||||||
"hybrid-array",
|
"hybrid-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ctor"
|
|
||||||
version = "0.9.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c1c888a2a4f677017373fb6c01e13e318dd9e78758445ed5eb985e355d3f8281"
|
|
||||||
dependencies = [
|
|
||||||
"ctor-proc-macro",
|
|
||||||
"dtor",
|
|
||||||
"link-section 0.0.12",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ctor"
|
name = "ctor"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a"
|
checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"link-section 0.18.0",
|
"link-section",
|
||||||
"linktime-proc-macro",
|
"linktime-proc-macro",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ctor-proc-macro"
|
|
||||||
version = "0.0.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a7ab264ea985f1bd27887d7b21ea2bb046728e05d11909ca138d700c494730db"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ctutils"
|
name = "ctutils"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
@@ -523,21 +506,6 @@ dependencies = [
|
|||||||
"windows-sys 0.60.2",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dtor"
|
|
||||||
version = "0.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "30e4690622ab6700ced40fc370a3f07b7d111f0154bb6fb08f73b4c8834f75b6"
|
|
||||||
dependencies = [
|
|
||||||
"dtor-proc-macro",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dtor-proc-macro"
|
|
||||||
version = "0.0.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8c98b077c7463d01d22dde8a24378ddf1ca7263dc687cffbed38819ea6c21131"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dunce"
|
name = "dunce"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
@@ -563,7 +531,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -847,7 +815,7 @@ dependencies = [
|
|||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"portable-atomic-util",
|
"portable-atomic-util",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -916,9 +884,9 @@ checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.182"
|
version = "0.2.186"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
@@ -932,12 +900,6 @@ dependencies = [
|
|||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "link-section"
|
|
||||||
version = "0.0.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f52437d47b0358721ec869cc7374b2a21f7b2237af9b439c0391341a1fbfbf1b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "link-section"
|
name = "link-section"
|
||||||
version = "0.18.0"
|
version = "0.18.0"
|
||||||
@@ -989,9 +951,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.31.2"
|
version = "0.31.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3"
|
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@@ -1219,9 +1181,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.12.3"
|
version = "1.12.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -1248,9 +1210,9 @@ checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.10"
|
version = "0.8.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rlimit"
|
name = "rlimit"
|
||||||
@@ -1283,7 +1245,7 @@ dependencies = [
|
|||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1456,7 +1418,7 @@ dependencies = [
|
|||||||
"clap_complete",
|
"clap_complete",
|
||||||
"clap_mangen",
|
"clap_mangen",
|
||||||
"codspeed-divan-compat",
|
"codspeed-divan-compat",
|
||||||
"ctor 1.0.7",
|
"ctor",
|
||||||
"phf",
|
"phf",
|
||||||
"phf_codegen",
|
"phf_codegen",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
@@ -1481,10 +1443,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"getrandom 0.4.2",
|
"getrandom 0.3.4",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1494,7 +1456,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874"
|
checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1681,20 +1643,20 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"regex",
|
"regex",
|
||||||
"tar",
|
"tar",
|
||||||
|
"tempfile",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"uucore",
|
"uucore",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uucore"
|
name = "uucore"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
source = "git+https://github.com/uutils/coreutils#b13588cc944be76a123c841f94889b10dfbf7932"
|
source = "git+https://github.com/uutils/coreutils#48d030dfb2ca7659ee2735f09cda706c8245a3fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"dns-lookup",
|
"dns-lookup",
|
||||||
"dunce",
|
"dunce",
|
||||||
"fluent",
|
"fluent",
|
||||||
"fluent-bundle",
|
|
||||||
"fluent-syntax",
|
"fluent-syntax",
|
||||||
"jiff",
|
"jiff",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -1713,8 +1675,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uucore_procs"
|
name = "uucore_procs"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
source = "git+https://github.com/uutils/coreutils#b13588cc944be76a123c841f94889b10dfbf7932"
|
source = "git+https://github.com/uutils/coreutils#48d030dfb2ca7659ee2735f09cda706c8245a3fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -1722,10 +1684,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uutests"
|
name = "uutests"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
source = "git+https://github.com/uutils/coreutils#b13588cc944be76a123c841f94889b10dfbf7932"
|
source = "git+https://github.com/uutils/coreutils#48d030dfb2ca7659ee2735f09cda706c8245a3fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ctor 0.9.1",
|
"ctor",
|
||||||
"libc",
|
"libc",
|
||||||
"nix",
|
"nix",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
@@ -1861,7 +1823,7 @@ version = "0.1.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
+20
-8
@@ -4,7 +4,7 @@
|
|||||||
// at various scales to track performance over time.
|
// at various scales to track performance over time.
|
||||||
|
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::Write;
|
use std::io::{self, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use tar::operations;
|
use tar::operations;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
@@ -39,7 +39,9 @@ fn collect_files(dir: &Path) -> Vec<PathBuf> {
|
|||||||
fn build_archive(archive_path: &Path, source_dir: &Path) {
|
fn build_archive(archive_path: &Path, source_dir: &Path) {
|
||||||
let files = collect_files(source_dir);
|
let files = collect_files(source_dir);
|
||||||
let refs: Vec<&Path> = files.iter().map(|p| p.as_path()).collect();
|
let refs: Vec<&Path> = files.iter().map(|p| p.as_path()).collect();
|
||||||
operations::create::create_archive(archive_path, &refs, false, false).unwrap();
|
let output = File::create(archive_path).unwrap();
|
||||||
|
let status_output = io::sink();
|
||||||
|
operations::create::create_archive(output, status_output, &refs, true, false).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -57,7 +59,9 @@ fn create_archive_10_files(bencher: divan::Bencher) {
|
|||||||
|
|
||||||
bencher.bench_local(|| {
|
bencher.bench_local(|| {
|
||||||
let refs: Vec<&Path> = files.iter().map(|p| p.as_path()).collect();
|
let refs: Vec<&Path> = files.iter().map(|p| p.as_path()).collect();
|
||||||
operations::create::create_archive(&archive_path, &refs, false, false).unwrap();
|
let output = File::create(&archive_path).unwrap();
|
||||||
|
let status_output = io::sink();
|
||||||
|
operations::create::create_archive(output, status_output, &refs, true, false).unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +76,9 @@ fn create_archive_100_files(bencher: divan::Bencher) {
|
|||||||
|
|
||||||
bencher.bench_local(|| {
|
bencher.bench_local(|| {
|
||||||
let refs: Vec<&Path> = files.iter().map(|p| p.as_path()).collect();
|
let refs: Vec<&Path> = files.iter().map(|p| p.as_path()).collect();
|
||||||
operations::create::create_archive(&archive_path, &refs, false, false).unwrap();
|
let output = File::create(&archive_path).unwrap();
|
||||||
|
let status_output = io::sink();
|
||||||
|
operations::create::create_archive(output, status_output, &refs, true, false).unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +93,10 @@ fn create_archive_directory(bencher: divan::Bencher) {
|
|||||||
let archive_path = out.path().join("bench.tar");
|
let archive_path = out.path().join("bench.tar");
|
||||||
|
|
||||||
bencher.bench_local(|| {
|
bencher.bench_local(|| {
|
||||||
operations::create::create_archive(&archive_path, &[sub.as_path()], false, false).unwrap();
|
let output = File::create(&archive_path).unwrap();
|
||||||
|
let status_output = io::sink();
|
||||||
|
operations::create::create_archive(output, status_output, &[sub.as_path()], true, false)
|
||||||
|
.unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +113,8 @@ fn list_archive_50_files(bencher: divan::Bencher) {
|
|||||||
build_archive(&archive_path, source.path());
|
build_archive(&archive_path, source.path());
|
||||||
|
|
||||||
bencher.bench_local(|| {
|
bencher.bench_local(|| {
|
||||||
operations::list::list_archive(&archive_path, false).unwrap();
|
let input = File::open(&archive_path).unwrap();
|
||||||
|
operations::list::list_archive(input, false).unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +127,8 @@ fn list_archive_verbose_50_files(bencher: divan::Bencher) {
|
|||||||
build_archive(&archive_path, source.path());
|
build_archive(&archive_path, source.path());
|
||||||
|
|
||||||
bencher.bench_local(|| {
|
bencher.bench_local(|| {
|
||||||
operations::list::list_archive(&archive_path, true).unwrap();
|
let input = File::open(&archive_path).unwrap();
|
||||||
|
operations::list::list_archive(input, true).unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +149,8 @@ fn extract_archive_20_files(bencher: divan::Bencher) {
|
|||||||
.with_inputs(|| TempDir::new().unwrap())
|
.with_inputs(|| TempDir::new().unwrap())
|
||||||
.bench_local_values(|extract_dir| {
|
.bench_local_values(|extract_dir| {
|
||||||
std::env::set_current_dir(extract_dir.path()).unwrap();
|
std::env::set_current_dir(extract_dir.path()).unwrap();
|
||||||
operations::extract::extract_archive(&archive_path, false).unwrap();
|
let input = File::open(&archive_path).unwrap();
|
||||||
|
operations::extract::extract_archive(input, &archive_path, false).unwrap();
|
||||||
});
|
});
|
||||||
std::env::set_current_dir(original_dir).unwrap();
|
std::env::set_current_dir(original_dir).unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+113
-157
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -14,8 +14,8 @@ libc = "0.2.153"
|
|||||||
tempfile = "3.15.0"
|
tempfile = "3.15.0"
|
||||||
rand = { version = "0.10.0" }
|
rand = { version = "0.10.0" }
|
||||||
similar = "3.0.0"
|
similar = "3.0.0"
|
||||||
uucore = { version = "0.8.0", features = ["libc"] }
|
uucore = { version = "0.9.0", features = ["libc"] }
|
||||||
uufuzz = "0.8.0"
|
uufuzz = "0.9.0"
|
||||||
|
|
||||||
uu_tar = { path = "../src/uu/tar/" }
|
uu_tar = { path = "../src/uu/tar/" }
|
||||||
|
|
||||||
|
|||||||
@@ -26,3 +26,6 @@ path = "src/tar.rs"
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "tar"
|
name = "tar"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempfile = { workspace = true }
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ pub enum TarError {
|
|||||||
/// Cannot finalize the archive
|
/// Cannot finalize the archive
|
||||||
#[error("tar: Cannot finalize archive: {0}")]
|
#[error("tar: Cannot finalize archive: {0}")]
|
||||||
CannotFinalizeArchive(io::Error),
|
CannotFinalizeArchive(io::Error),
|
||||||
|
|
||||||
|
/// Refusing to write archive contents to terminal
|
||||||
|
#[error("tar: Refusing to write archive contents to terminal (missing -f option?)")]
|
||||||
|
RefuseWriteArchiveToTerminal,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TarError {
|
impl TarError {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
use crate::errors::TarError;
|
use crate::errors::TarError;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fs::{self, File};
|
use std::fs;
|
||||||
use std::io::{self, BufWriter, Write};
|
use std::io::{BufWriter, Write};
|
||||||
use std::path::Component::{self, ParentDir, Prefix, RootDir};
|
use std::path::Component::{self, ParentDir, Prefix, RootDir};
|
||||||
use std::path::{self, Path, PathBuf};
|
use std::path::{self, Path, PathBuf};
|
||||||
use tar::Builder;
|
use tar::Builder;
|
||||||
@@ -28,23 +28,19 @@ use uucore::error::UResult;
|
|||||||
/// - Any input file cannot be read
|
/// - Any input file cannot be read
|
||||||
/// - Files cannot be added due to I/O or permission errors
|
/// - Files cannot be added due to I/O or permission errors
|
||||||
pub fn create_archive(
|
pub fn create_archive(
|
||||||
archive_path: &Path,
|
output: impl Write,
|
||||||
|
status_output: impl Write,
|
||||||
files: &[&Path],
|
files: &[&Path],
|
||||||
allow_absolute: bool,
|
allow_absolute: bool,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
) -> UResult<()> {
|
) -> UResult<()> {
|
||||||
// Create the output file
|
let mut output = BufWriter::new(output);
|
||||||
let file = File::create(archive_path).map_err(|e| TarError::CannotCreateArchive {
|
let mut status_output = BufWriter::new(status_output);
|
||||||
path: archive_path.to_path_buf(),
|
|
||||||
source: e,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Create Builder instance
|
// Create Builder instance
|
||||||
let mut builder = Builder::new(file);
|
let mut builder = Builder::new(&mut output);
|
||||||
builder.preserve_absolute(allow_absolute);
|
builder.preserve_absolute(allow_absolute);
|
||||||
|
|
||||||
let mut out = BufWriter::new(io::stdout().lock());
|
|
||||||
|
|
||||||
// Add each file or directory to the archive
|
// Add each file or directory to the archive
|
||||||
for &path in files {
|
for &path in files {
|
||||||
// Check if path exists
|
// Check if path exists
|
||||||
@@ -68,7 +64,7 @@ pub fn create_archive(
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("\n");
|
.join("\n");
|
||||||
writeln!(out, "{to_print}").map_err(TarError::Io)?;
|
writeln!(status_output, "{to_print}").map_err(TarError::Io)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize path if needed (so far, handles only absolute paths)
|
// Normalize path if needed (so far, handles only absolute paths)
|
||||||
@@ -81,8 +77,8 @@ pub fn create_archive(
|
|||||||
.iter()
|
.iter()
|
||||||
.collect();
|
.collect();
|
||||||
writeln!(
|
writeln!(
|
||||||
out,
|
std::io::stderr(),
|
||||||
"Removing leading `{}' from member names",
|
"tar: Removing leading `{}' from member names",
|
||||||
removed.display()
|
removed.display()
|
||||||
)
|
)
|
||||||
.map_err(TarError::Io)?;
|
.map_err(TarError::Io)?;
|
||||||
@@ -112,9 +108,11 @@ pub fn create_archive(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finish writing the archive
|
|
||||||
out.flush().map_err(TarError::Io)?;
|
|
||||||
builder.finish().map_err(TarError::CannotFinalizeArchive)?;
|
builder.finish().map_err(TarError::CannotFinalizeArchive)?;
|
||||||
|
drop(builder);
|
||||||
|
|
||||||
|
status_output.flush().map_err(TarError::Io)?;
|
||||||
|
output.flush().map_err(TarError::Io)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -148,3 +146,32 @@ fn normalize_path(path: &Path, allow_absolute: bool) -> Option<PathBuf> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::io::{self, Write};
|
||||||
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
struct FailFlushWriter;
|
||||||
|
impl Write for FailFlushWriter {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Err(io::Error::other("flush failed"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_archive_flush_failed() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let file_path = dir.path().join("test.txt");
|
||||||
|
fs::write(&file_path, "hello").unwrap();
|
||||||
|
|
||||||
|
let output = FailFlushWriter;
|
||||||
|
let status_output = io::sink();
|
||||||
|
|
||||||
|
let res = create_archive(output, status_output, &[file_path.as_path()], false, false);
|
||||||
|
assert!(res.is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,8 +4,7 @@
|
|||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
use crate::errors::TarError;
|
use crate::errors::TarError;
|
||||||
use std::fs::File;
|
use std::io::{self, BufReader, BufWriter, Read, Write};
|
||||||
use std::io::{self, BufWriter, Write};
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use tar::Archive;
|
use tar::Archive;
|
||||||
use uucore::error::UResult;
|
use uucore::error::UResult;
|
||||||
@@ -23,12 +22,9 @@ use uucore::error::UResult;
|
|||||||
/// - The archive file cannot be opened
|
/// - The archive file cannot be opened
|
||||||
/// - The archive format is invalid
|
/// - The archive format is invalid
|
||||||
/// - Files cannot be extracted due to I/O or permission errors
|
/// - Files cannot be extracted due to I/O or permission errors
|
||||||
pub fn extract_archive(archive_path: &Path, verbose: bool) -> UResult<()> {
|
pub fn extract_archive(input: impl Read, archive_path: &Path, verbose: bool) -> UResult<()> {
|
||||||
// Open the archive file
|
|
||||||
let file = File::open(archive_path).map_err(|e| TarError::from_io_error(e, archive_path))?;
|
|
||||||
|
|
||||||
// Create Archive instance
|
// Create Archive instance
|
||||||
let mut archive = Archive::new(file);
|
let mut archive = Archive::new(BufReader::new(input));
|
||||||
let mut out = BufWriter::new(io::stdout().lock());
|
let mut out = BufWriter::new(io::stdout().lock());
|
||||||
|
|
||||||
// Extract to current directory
|
// Extract to current directory
|
||||||
|
|||||||
@@ -5,18 +5,14 @@
|
|||||||
|
|
||||||
use crate::errors::TarError;
|
use crate::errors::TarError;
|
||||||
use chrono::{TimeZone, Utc};
|
use chrono::{TimeZone, Utc};
|
||||||
use std::fs::File;
|
use std::io::{self, BufReader, BufWriter, Read, Write};
|
||||||
use std::io::{self, BufWriter, Write};
|
|
||||||
use std::path::Path;
|
|
||||||
use tar::Archive;
|
use tar::Archive;
|
||||||
use uucore::error::UResult;
|
use uucore::error::UResult;
|
||||||
use uucore::fs::display_permissions_unix;
|
use uucore::fs::display_permissions_unix;
|
||||||
|
|
||||||
/// List the contents of a tar archive, printing one entry per line.
|
/// List the contents of a tar archive, printing one entry per line.
|
||||||
pub fn list_archive(archive_path: &Path, verbose: bool) -> UResult<()> {
|
pub fn list_archive(input: impl Read, verbose: bool) -> UResult<()> {
|
||||||
let file: File =
|
let mut archive = Archive::new(BufReader::new(input));
|
||||||
File::open(archive_path).map_err(|e| TarError::from_io_error(e, archive_path))?;
|
|
||||||
let mut archive = Archive::new(file);
|
|
||||||
let mut out = BufWriter::new(io::stdout().lock());
|
let mut out = BufWriter::new(io::stdout().lock());
|
||||||
|
|
||||||
for entry_result in archive.entries().map_err(TarError::CannotReadEntries)? {
|
for entry_result in archive.entries().map_err(TarError::CannotReadEntries)? {
|
||||||
|
|||||||
+46
-3
@@ -6,7 +6,10 @@
|
|||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod operations;
|
pub mod operations;
|
||||||
|
|
||||||
|
use crate::errors::TarError;
|
||||||
use clap::{arg, crate_version, ArgAction, Command};
|
use clap::{arg, crate_version, ArgAction, Command};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{self, IsTerminal};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use uucore::error::UResult;
|
use uucore::error::UResult;
|
||||||
use uucore::format_usage;
|
use uucore::format_usage;
|
||||||
@@ -139,7 +142,13 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||||||
uucore::error::USimpleError::new(64, "option requires an argument -- 'f'")
|
uucore::error::USimpleError::new(64, "option requires an argument -- 'f'")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
return operations::extract::extract_archive(archive_path, verbose);
|
return if archive_path == Path::new("-") {
|
||||||
|
operations::extract::extract_archive(io::stdin(), archive_path, verbose)
|
||||||
|
} else {
|
||||||
|
let file =
|
||||||
|
File::open(archive_path).map_err(|e| TarError::from_io_error(e, archive_path))?;
|
||||||
|
operations::extract::extract_archive(file, archive_path, verbose)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle create operation
|
// Handle create operation
|
||||||
@@ -160,7 +169,35 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return operations::create::create_archive(archive_path, &files, allow_absolute, verbose);
|
let output_is_stdout = archive_path == Path::new("-");
|
||||||
|
return if output_is_stdout {
|
||||||
|
if io::stdout().is_terminal() {
|
||||||
|
Err(TarError::RefuseWriteArchiveToTerminal.into())
|
||||||
|
} else {
|
||||||
|
let output = io::stdout().lock();
|
||||||
|
let status_output = io::stderr();
|
||||||
|
operations::create::create_archive(
|
||||||
|
output,
|
||||||
|
status_output,
|
||||||
|
&files,
|
||||||
|
allow_absolute,
|
||||||
|
verbose,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let output = File::create(archive_path).map_err(|e| TarError::CannotCreateArchive {
|
||||||
|
path: archive_path.clone(),
|
||||||
|
source: e,
|
||||||
|
})?;
|
||||||
|
let status_output = io::stdout().lock();
|
||||||
|
operations::create::create_archive(
|
||||||
|
output,
|
||||||
|
status_output,
|
||||||
|
&files,
|
||||||
|
allow_absolute,
|
||||||
|
verbose,
|
||||||
|
)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle list operation
|
// Handle list operation
|
||||||
@@ -169,7 +206,13 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||||||
uucore::error::USimpleError::new(64, "option requires an argument -- 'f'")
|
uucore::error::USimpleError::new(64, "option requires an argument -- 'f'")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
return operations::list::list_archive(archive_path, verbose);
|
return if archive_path == Path::new("-") {
|
||||||
|
operations::list::list_archive(io::stdin(), verbose)
|
||||||
|
} else {
|
||||||
|
let file =
|
||||||
|
File::open(archive_path).map_err(|e| TarError::from_io_error(e, archive_path))?;
|
||||||
|
operations::list::list_archive(file, verbose)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no operation specified, show error
|
// If no operation specified, show error
|
||||||
|
|||||||
+124
-1
@@ -145,6 +145,17 @@ fn test_create_verbose() {
|
|||||||
assert!(at.file_exists("archive.tar"));
|
assert!(at.file_exists("archive.tar"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_verbose_to_stdout_uses_stderr() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
|
||||||
|
at.write("file.txt", "hello");
|
||||||
|
|
||||||
|
ucmd.args(&["-cvf", "-", "file.txt"])
|
||||||
|
.succeeds()
|
||||||
|
.stderr_contains("file.txt");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_empty_archive_fails() {
|
fn test_create_empty_archive_fails() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
@@ -179,7 +190,7 @@ fn test_create_absolute_path() {
|
|||||||
&file_abs_path.display().to_string(),
|
&file_abs_path.display().to_string(),
|
||||||
])
|
])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_contains("Removing leading");
|
.stderr_contains("tar: Removing leading");
|
||||||
|
|
||||||
assert!(at.file_exists("archive-trimed.tar"));
|
assert!(at.file_exists("archive-trimed.tar"));
|
||||||
|
|
||||||
@@ -814,3 +825,115 @@ fn test_list_conflicts_with_extract() {
|
|||||||
.code_is(2)
|
.code_is(2)
|
||||||
.stderr_contains("cannot be used with");
|
.stderr_contains("cannot be used with");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stdin/stdout (-f -) tests
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_to_stdout() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
at.write("file.txt", "hello");
|
||||||
|
|
||||||
|
let result = ucmd.args(&["-cf", "-", "file.txt"]).succeeds();
|
||||||
|
let bytes = result.stdout();
|
||||||
|
// A tar archive is at least two blocks: one header + two end-of-archive blocks
|
||||||
|
assert!(
|
||||||
|
bytes.len() >= TAR_BLOCK_SIZE,
|
||||||
|
"stdout should contain tar data (got {} bytes)",
|
||||||
|
bytes.len()
|
||||||
|
);
|
||||||
|
// The first 100 bytes of a tar header are the file name
|
||||||
|
let header_prefix = std::str::from_utf8(&bytes[..100]).unwrap_or("");
|
||||||
|
assert!(
|
||||||
|
header_prefix.contains("file.txt"),
|
||||||
|
"tar header should contain filename"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_verbose_to_stdout_keeps_stdout_as_tar_data() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
at.write("file.txt", "hello");
|
||||||
|
|
||||||
|
let result = ucmd.args(&["-cvf", "-", "file.txt"]).succeeds();
|
||||||
|
let bytes = result.stdout();
|
||||||
|
let header_prefix = std::str::from_utf8(&bytes[..100]).unwrap_or("");
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
bytes.len() >= TAR_BLOCK_SIZE,
|
||||||
|
"stdout should contain tar data (got {} bytes)",
|
||||||
|
bytes.len()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
header_prefix.contains("file.txt"),
|
||||||
|
"tar header should contain filename"
|
||||||
|
);
|
||||||
|
assert_eq!(&bytes[..8], b"file.txt");
|
||||||
|
assert_eq!(
|
||||||
|
bytes[8], 0,
|
||||||
|
"stdout should not contain verbose output prefix"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_absolute_path_to_stdout_uses_stderr_for_normalization_notice() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
|
||||||
|
let mut file_abs_path = PathBuf::from(at.root_dir_resolved());
|
||||||
|
file_abs_path.push("file1.txt");
|
||||||
|
|
||||||
|
at.write(&file_abs_path.display().to_string(), "content1");
|
||||||
|
|
||||||
|
let result = ucmd
|
||||||
|
.args(&["-cf", "-", &file_abs_path.display().to_string()])
|
||||||
|
.succeeds();
|
||||||
|
result.stderr_contains("Removing leading");
|
||||||
|
let bytes = result.stdout();
|
||||||
|
let header_prefix = std::str::from_utf8(&bytes[..100]).unwrap_or("");
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
bytes.len() >= TAR_BLOCK_SIZE,
|
||||||
|
"stdout should contain tar data (got {} bytes)",
|
||||||
|
bytes.len()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!header_prefix.contains("Removing leading"),
|
||||||
|
"stdout should not contain normalization notices"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_list_from_stdin() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
at.write("file.txt", "hello");
|
||||||
|
|
||||||
|
// Create archive to a file in the same sandbox
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-cf", &at.plus_as_string("archive.tar"), "file.txt"])
|
||||||
|
.current_dir(at.as_string())
|
||||||
|
.succeeds();
|
||||||
|
|
||||||
|
let archive_bytes = at.read_bytes("archive.tar");
|
||||||
|
ucmd.args(&["-tf", "-"])
|
||||||
|
.pipe_in(archive_bytes)
|
||||||
|
.succeeds()
|
||||||
|
.stdout_contains("file.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extract_from_stdin() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
at.write("file.txt", "hello world");
|
||||||
|
|
||||||
|
// Create archive to a file in the same sandbox
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-cf", &at.plus_as_string("archive.tar"), "file.txt"])
|
||||||
|
.current_dir(at.as_string())
|
||||||
|
.succeeds();
|
||||||
|
|
||||||
|
at.remove("file.txt");
|
||||||
|
|
||||||
|
let archive_bytes = at.read_bytes("archive.tar");
|
||||||
|
ucmd.args(&["-xf", "-"]).pipe_in(archive_bytes).succeeds();
|
||||||
|
|
||||||
|
assert_eq!(at.read("file.txt"), "hello world");
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user