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

Issue:
Can't extract gcc 7.2.0
Given the file `gcc-7.2.0.tar.xz` from [here](https://ftp.gnu.org/gnu/gcc/gcc-7.2.0/gcc-7.2.0.tar.xz), tar-rs consistently fails to extract the decompressed `gcc-7.2.0.tar` archive using the following code:
```rust
extern crate tar;

fn main() {
    let file = std::fs::File::open("gcc-7.2.0.tar").unwrap();

    let mut archive = tar::Archive::new(file);
    archive.unpack(std::path::Path::new("testpath")).unwrap();
}
```

It always spits out this error:
```
Custom { kind: Other, error: TarError { desc: "failed to unpack `testpath/gcc-7.2.0/libstdc++-v3/testsuite/25_algorithms/stable_partition/requirements/explicit_instantiation`", io: Custom { kind: Other, error: TarError { desc: "failed to unpack `gcc-7.2.0/libstdc++-v3/testsuite/25_algorithms/stable_partition/requirements/explicit_instantiation/` into `testpath/gcc-7.2.0/libstdc++-v3/testsuite/25_algorithms/stable_partition/requirements/explicit_instantiation`", io: Os { code: 21, kind: Other, message: "Is a directory" } } } } }
```


Discussion / hints:
It seems that it fails on what `file` claims is a "POSIX tar archive" (I assume this means pax).  If I extract `gcc-7.2.0.tar` with GNU `tar` and then test tar-rs on a re-archived version of the data using the GNU tar format, tar-rs is perfectly happy.

Fix patch:
diff --git a/examples/raw_list.rs b/examples/raw_list.rs
index 072865a4..8465bdd7 100644
--- a/examples/raw_list.rs
+++ b/examples/raw_list.rs
@@ -17,8 +17,8 @@ fn main() {
         println!("size: {}", f.header().size().unwrap());
         println!("entry size: {}", f.header().entry_size().unwrap());
         println!("link name: {:?}", f.link_name().unwrap());
-        println!("file type: {:x}", f.header().entry_type().as_byte());
-        println!("mode: {:o}", f.header().mode().unwrap());
+        println!("file type: {:#x}", f.header().entry_type().as_byte());
+        println!("mode: {:#o}", f.header().mode().unwrap());
         println!("uid: {}", f.header().uid().unwrap());
         println!("gid: {}", f.header().gid().unwrap());
         println!("mtime: {}", f.header().mtime().unwrap());
diff --git a/src/entry.rs b/src/entry.rs
index 6025cd26..32e56c4a 100644
--- a/src/entry.rs
+++ b/src/entry.rs
@@ -267,7 +267,18 @@ impl<'a> EntryFields<'a> {
                     Cow::Borrowed(bytes)
                 }
             }
-            None => self.header.path_bytes(),
+            None => {
+                if let Some(ref pax) = self.pax_extensions {
+                    let pax = pax_extensions(pax)
+                        .filter_map(|f| f.ok())
+                        .find(|f| f.key_bytes() == b"path")
+                        .map(|f| f.value_bytes());
+                    if let Some(field) = pax {
+                        return Cow::Borrowed(field)
+                    }
+                }
+                self.header.path_bytes()
+            }
         }
     }
 


Test patch:
diff --git a/tests/all.rs b/tests/all.rs
index 6be2eaee..10eca5e0 100644
--- a/tests/all.rs
+++ b/tests/all.rs
@@ -571,6 +571,15 @@ fn pax_simple() {
     assert_eq!(third.value(), Ok("1453146164.953123768"));
 }
 
+#[test]
+fn pax_path() {
+    let mut ar = Archive::new(tar!("pax2.tar"));
+    let mut entries = t!(ar.entries());
+
+    let first = t!(entries.next().unwrap());
+    assert!(first.path().unwrap().ends_with("aaaaaaaaaaaaaaa"));
+}
+
 #[test]
 fn long_name_trailing_nul() {
     let mut b = Builder::new(Vec::<u8>::new());
diff --git a/tests/archives/pax2.tar b/tests/archives/pax2.tar
new file mode 100644
index 00000000..c0c8ed31
Binary files /dev/null and b/tests/archives/pax2.tar differ