From 3633764004da00f75d1076598eee504bcede8822 Mon Sep 17 00:00:00 2001 From: tommady Date: Sun, 13 Apr 2025 04:41:47 +0000 Subject: [PATCH] add --follow-symlinks flag into compress command with test case Signed-off-by: tommady --- src/archive/tar.rs | 3 ++- src/archive/zip.rs | 3 ++- src/cli/args.rs | 8 +++++++ src/commands/compress.rs | 11 ++++++++- src/commands/mod.rs | 2 ++ tests/integration.rs | 50 +++++++++++++++++++++++++++++----------- 6 files changed, 61 insertions(+), 16 deletions(-) diff --git a/src/archive/tar.rs b/src/archive/tar.rs index 06f9280..07c9df4 100644 --- a/src/archive/tar.rs +++ b/src/archive/tar.rs @@ -104,6 +104,7 @@ pub fn build_archive_from_paths( writer: W, file_visibility_policy: FileVisibilityPolicy, quiet: bool, + follow_symlinks: bool, ) -> crate::Result where W: Write, @@ -144,7 +145,7 @@ where if path.is_dir() { builder.append_dir(path, path)?; - } else if path.is_symlink() { + } else if path.is_symlink() && !follow_symlinks { let target_path = path.read_link()?; let mut header = tar::Header::new_gnu(); diff --git a/src/archive/zip.rs b/src/archive/zip.rs index 8d23f63..b02e6e3 100644 --- a/src/archive/zip.rs +++ b/src/archive/zip.rs @@ -170,6 +170,7 @@ pub fn build_archive_from_paths( writer: W, file_visibility_policy: FileVisibilityPolicy, quiet: bool, + follow_symlinks: bool, ) -> crate::Result where W: Write + Seek, @@ -247,7 +248,7 @@ where if metadata.is_dir() { writer.add_directory(entry_name, options)?; - } else if path.is_symlink() { + } else if path.is_symlink() && !follow_symlinks { let target_path = path.read_link()?; let target_name = target_path.to_str().ok_or_else(|| { FinalError::with_title("Zip requires that all directories names are valid UTF-8") diff --git a/src/cli/args.rs b/src/cli/args.rs index 5701f4e..6afc847 100644 --- a/src/cli/args.rs +++ b/src/cli/args.rs @@ -81,6 +81,10 @@ pub enum Subcommand { /// conflicts with --level and --fast #[arg(long, group = "compression-level")] slow: bool, + + /// Tar and Zip specific: add the symlink target to the archive instead of the symlink itself + #[arg(long)] + follow_symlinks: bool, }, /// Decompresses one or more files, optionally into another folder #[command(visible_alias = "d")] @@ -201,6 +205,7 @@ mod tests { level: None, fast: false, slow: false, + follow_symlinks: false, }, ..mock_cli_args() } @@ -214,6 +219,7 @@ mod tests { level: None, fast: false, slow: false, + follow_symlinks: false, }, ..mock_cli_args() } @@ -227,6 +233,7 @@ mod tests { level: None, fast: false, slow: false, + follow_symlinks: false, }, ..mock_cli_args() } @@ -251,6 +258,7 @@ mod tests { level: None, fast: false, slow: false, + follow_symlinks: false, }, format: Some("tar.gz".into()), ..mock_cli_args() diff --git a/src/commands/compress.rs b/src/commands/compress.rs index 59987b8..b6f7042 100644 --- a/src/commands/compress.rs +++ b/src/commands/compress.rs @@ -31,6 +31,7 @@ pub fn compress_files( output_file: fs::File, output_path: &Path, quiet: bool, + follow_symlinks: bool, question_policy: QuestionPolicy, file_visibility_policy: FileVisibilityPolicy, level: Option, @@ -108,7 +109,14 @@ pub fn compress_files( io::copy(&mut reader, &mut writer)?; } Tar => { - archive::tar::build_archive_from_paths(&files, output_path, &mut writer, file_visibility_policy, quiet)?; + archive::tar::build_archive_from_paths( + &files, + output_path, + &mut writer, + file_visibility_policy, + quiet, + follow_symlinks, + )?; writer.flush()?; } Zip => { @@ -131,6 +139,7 @@ pub fn compress_files( &mut vec_buffer, file_visibility_policy, quiet, + follow_symlinks, )?; vec_buffer.rewind()?; io::copy(&mut vec_buffer, &mut writer)?; diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 60a2866..3e8718b 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -67,6 +67,7 @@ pub fn run( level, fast, slow, + follow_symlinks, } => { // After cleaning, if there are no input files left, exit if files.is_empty() { @@ -109,6 +110,7 @@ pub fn run( output_file, &output_path, args.quiet, + follow_symlinks, question_policy, file_visibility_policy, level, diff --git a/tests/integration.rs b/tests/integration.rs index fa5fe5f..1a984a5 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -482,11 +482,11 @@ fn symlink_pack_and_unpack( return Ok(()); } - let temp_dir = tempdir().unwrap(); + let temp_dir = tempdir()?; let root_path = temp_dir.path(); let src_files_path = root_path.join("src_files"); - fs::create_dir_all(&src_files_path).unwrap(); + fs::create_dir_all(&src_files_path)?; let mut files_path = ["file1.txt", "file2.txt", "file3.txt", "file4.txt", "file5.txt"] .into_iter() @@ -499,13 +499,13 @@ fn symlink_pack_and_unpack( .collect::>(); let dest_files_path = root_path.join("dest_files"); - fs::create_dir_all(&dest_files_path).unwrap(); + fs::create_dir_all(&dest_files_path)?; let symlink_path = src_files_path.join(Path::new("symlink")); #[cfg(unix)] - std::os::unix::fs::symlink(&files_path[0], &symlink_path).unwrap(); + std::os::unix::fs::symlink(&files_path[0], &symlink_path)?; #[cfg(windows)] - std::os::windows::fs::symlink_file(&files_path[0], &symlink_path).unwrap(); + std::os::windows::fs::symlink_file(&files_path[0], &symlink_path)?; files_path.push(symlink_path); @@ -513,6 +513,34 @@ fn symlink_pack_and_unpack( crate::utils::cargo_bin() .arg("compress") + .args(files_path.clone()) + .arg(archive) + .assert() + .success(); + + crate::utils::cargo_bin() + .arg("decompress") + .arg(archive) + .arg("-d") + .arg(&dest_files_path) + .assert() + .success(); + + assert_same_directory(&src_files_path, &dest_files_path, false); + // check the symlink stand still + for f in dest_files_path.as_path().read_dir()? { + let f = f?; + if f.file_name() == "symlink" { + assert!(f.file_type()?.is_symlink()) + } + } + + fs::remove_file(archive)?; + fs::remove_dir_all(&dest_files_path)?; + + crate::utils::cargo_bin() + .arg("compress") + .arg("--follow-symlinks") .args(files_path) .arg(archive) .assert() @@ -526,13 +554,9 @@ fn symlink_pack_and_unpack( .assert() .success(); - println!("archive: {:?}", archive); - assert_same_directory(&src_files_path, &dest_files_path, false); - // check the symlink stand still - for f in dest_files_path.as_path().read_dir().unwrap() { - let f = f.unwrap(); - if f.file_name() == "symlink" { - assert!(f.file_type().unwrap().is_symlink()) - } + // check there is no symlinks + for f in dest_files_path.as_path().read_dir()? { + let f = f?; + assert!(!f.file_type().unwrap().is_symlink()) } }