diff --git a/src/uu/tar/src/operations/create.rs b/src/uu/tar/src/operations/create.rs index a33d801..39b6d63 100644 --- a/src/uu/tar/src/operations/create.rs +++ b/src/uu/tar/src/operations/create.rs @@ -18,6 +18,7 @@ use uucore::error::UResult; /// /// * `archive_path` - Path where the tar archive should be created /// * `files` - Slice of file paths to add to the archive +/// * `allow_absolute` - Allow absolute paths while creating archive /// * `verbose` - Whether to print verbose output during creation /// /// # Errors @@ -26,7 +27,12 @@ use uucore::error::UResult; /// - The archive file cannot be created /// - Any input file cannot be read /// - Files cannot be added due to I/O or permission errors -pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UResult<()> { +pub fn create_archive( + archive_path: &Path, + files: &[&Path], + allow_absolute: bool, + verbose: bool, +) -> UResult<()> { // Create the output file let file = File::create(archive_path).map_err(|e| TarError::CannotCreateArchive { path: archive_path.to_path_buf(), @@ -35,6 +41,8 @@ pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UR // Create Builder instance let mut builder = Builder::new(file); + builder.preserve_absolute(allow_absolute); + let mut out = BufWriter::new(io::stdout().lock()); // Add each file or directory to the archive @@ -64,7 +72,7 @@ pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UR } // Normalize path if needed (so far, handles only absolute paths) - let normalized_name = if let Some(normalized) = normalize_path(path) { + let normalized_name = if let Some(normalized) = normalize_path(path, allow_absolute) { let original_components: Vec = path.components().collect(); let normalized_components: Vec = normalized.components().collect(); if original_components.len() > normalized_components.len() { @@ -129,8 +137,8 @@ fn get_tree(path: &Path) -> Result, std::io::Error> { Ok(paths) } -fn normalize_path(path: &Path) -> Option { - if path.is_absolute() { +fn normalize_path(path: &Path, allow_absolute: bool) -> Option { + if path.is_absolute() && !allow_absolute { Some( path.components() .filter(|c| !matches!(c, RootDir | ParentDir | Prefix(_))) diff --git a/src/uu/tar/src/tar.rs b/src/uu/tar/src/tar.rs index 16e29d1..991d4fa 100644 --- a/src/uu/tar/src/tar.rs +++ b/src/uu/tar/src/tar.rs @@ -131,6 +131,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { }; let verbose = matches.get_flag("verbose"); + let allow_absolute = matches.get_flag("absolute-names"); // Handle extract operation if matches.get_flag("extract") { @@ -159,7 +160,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { )); } - return operations::create::create_archive(archive_path, &files, verbose); + return operations::create::create_archive(archive_path, &files, allow_absolute, verbose); } // Handle list operation @@ -199,6 +200,10 @@ pub fn uu_app() -> Command { // Archive file arg!(-f --file "Use archive file or device ARCHIVE") .value_parser(clap::value_parser!(PathBuf)), + arg!( + -P --"absolute-names" + "Don't strip leading '/'s from file names" + ), // Compression options // arg!(-z --gzip "Filter through gzip"), // arg!(-j --bzip2 "Filter through bzip2"), @@ -207,7 +212,6 @@ pub fn uu_app() -> Command { arg!(-v --verbose "Verbosely list files processed"), // arg!(-h --dereference "Follow symlinks"), // arg!(-p --"preserve-permissions" "Extract information about file permissions"), - // arg!(-P --"absolute-names" "Don't strip leading '/' from file names"), // Help arg!(--help "Print help information").action(ArgAction::Help), // Files to process diff --git a/tests/by-util/test_tar.rs b/tests/by-util/test_tar.rs index 51395a3..3fb603f 100644 --- a/tests/by-util/test_tar.rs +++ b/tests/by-util/test_tar.rs @@ -171,11 +171,55 @@ fn test_create_absolute_path() { file_abs_path.push("file1.txt"); at.write(&file_abs_path.display().to_string(), "content1"); - ucmd.args(&["-cf", "archive.tar", &file_abs_path.display().to_string()]) + + // Trim leading '/' + ucmd.args(&[ + "-cf", + "archive-trimed.tar", + &file_abs_path.display().to_string(), + ]) + .succeeds() + .stdout_contains("Removing leading"); + + assert!(at.file_exists("archive-trimed.tar")); + + let expected_trimmed_path = file_abs_path + .components() + .filter(|c| !matches!(c, path::Component::RootDir | path::Component::Prefix(_))) + .map(|c| c.as_os_str().display().to_string()) + .collect::>() + .join("/"); + + new_ucmd!() + .args(&["-tf", "archive-trimed.tar"]) + .current_dir(at.as_string()) .succeeds() - .stdout_contains("Removing leading"); + .stdout_contains(&expected_trimmed_path); - assert!(at.file_exists("archive.tar")); + let (at, mut ucmd) = at_and_ucmd!(); + // Preserve leading '/' + ucmd.args(&[ + "-cPf", + "archive-preserved.tar", + &file_abs_path.display().to_string(), + ]) + .succeeds() + .no_output(); + + assert!(at.file_exists("archive-preserved.tar")); + + let expected_prefix = file_abs_path + .components() + .find(|c| matches!(c, path::Component::RootDir | path::Component::Prefix(_))) + .map(|c| c.as_os_str().display().to_string()) + .unwrap(); + + new_ucmd!() + .args(&["-tf", "archive-preserved.tar"]) + .current_dir(at.as_string()) + .succeeds() + .stdout_contains(expected_prefix) + .stdout_contains(expected_trimmed_path); } // Extract operation tests @@ -554,11 +598,15 @@ fn test_extract_created_from_absolute_path() { file_abs_path.push("file1.txt"); at.write(&file_abs_path.display().to_string(), "content1"); - ucmd.args(&["-cf", "archive.tar", &file_abs_path.display().to_string()]) - .succeeds(); + ucmd.args(&[ + "-cf", + "archive-trimed.tar", + &file_abs_path.display().to_string(), + ]) + .succeeds(); new_ucmd!() - .args(&["-xf", "archive.tar"]) + .args(&["-xf", "archive-trimed.tar"]) .current_dir(at.as_string()) .succeeds(); @@ -569,6 +617,23 @@ fn test_extract_created_from_absolute_path() { .collect::>() .join(std::path::MAIN_SEPARATOR_STR); + assert!(at.file_exists(&expected_path)); + + new_ucmd!() + .args(&[ + "-cPf", + "archive-preserved.tar", + &file_abs_path.display().to_string(), + ]) + .current_dir(at.as_string()) + .succeeds(); + + at.remove(&expected_path); + new_ucmd!() + .args(&["-xf", "archive-preserved.tar"]) + .current_dir(at.as_string()) + .succeeds(); + assert!(at.file_exists(expected_path)); }