Repository: alexcrichton/tar-rs (language: rust)

Issue:
Crashes when directory contains symlink to a directory. (When `append_dir_all`.)
Hi @alexcrichton! Respect your hard works on Rust!

I've found a bug for the tar crate. This is a small project to re-create the error.

https://github.com/tbrand/tar-error

The error occurs when source directory contains a symlink **to a directory**.
It might be related [this issue](https://github.com/alexcrichton/tar-rs/issues/147).
I hope this will be fixed soon. :smile:

Thanks!


Discussion / hints:
I found here creates the error.
https://github.com/alexcrichton/tar-rs/blob/master/src/builder.rs#L426

So should I put a `.follow_symlinks(false)` then?

Fix patch:
diff --git a/src/builder.rs b/src/builder.rs
index d2168738..f5f4a3bc 100644
--- a/src/builder.rs
+++ b/src/builder.rs
@@ -489,7 +489,8 @@ fn append_dir_all(
     let mut stack = vec![(src_path.to_path_buf(), true, false)];
     while let Some((src, is_dir, is_symlink)) = stack.pop() {
         let dest = path.join(src.strip_prefix(&src_path).unwrap());
-        if is_dir {
+        // In case of a symlink pointing to a directory, is_dir is false, but src.is_dir() will return true
+        if is_dir || (is_symlink && follow && src.is_dir()) {
             for entry in fs::read_dir(&src)? {
                 let entry = entry?;
                 let file_type = entry.file_type()?;


Test patch:
diff --git a/tests/all.rs b/tests/all.rs
index 13612284..893b98c6 100644
--- a/tests/all.rs
+++ b/tests/all.rs
@@ -1013,3 +1013,20 @@ fn insert_local_file_different_name() {
     assert_eq!(t!(entry.path()), Path::new("archive/dir/f"));
     assert!(entries.next().is_none());
 }
+
+#[test]
+#[cfg(unix)]
+fn tar_directory_containing_symlink_to_directory() {
+    use std::os::unix::fs::symlink;
+
+    let td = t!(TempDir::new("tar-rs"));
+    let dummy_src = t!(TempDir::new("dummy_src"));
+    let dummy_dst = td.path().join("dummy_dst");
+    let mut ar = Builder::new(Vec::new());
+    t!(symlink(dummy_src.path().display().to_string(), &dummy_dst));
+
+    assert!(dummy_dst.read_link().is_ok());
+    assert!(dummy_dst.read_link().unwrap().is_dir());
+    ar.append_dir_all("symlinks", td.path()).unwrap();
+    ar.finish().unwrap();
+}