diff --git a/src/header.rs b/src/header.rs index b8b590be..7e507fc7 100644 --- a/src/header.rs +++ b/src/header.rs @@ -435,6 +435,18 @@ impl Header { }) } + /// Sets the link name for this header without any transformation. + /// + /// This function is like [`Self::set_link_name`] but accepts an arbitrary byte array. + /// Hence it will not perform any canonicalization, such as replacing duplicate `//` with `/`. + pub fn set_link_name_literal>(&mut self, p: P) -> io::Result<()> { + self._set_link_name_literal(p.as_ref()) + } + + fn _set_link_name_literal(&mut self, bytes: &[u8]) -> io::Result<()> { + copy_into(&mut self.as_old_mut().linkname, bytes) + } + /// Returns the mode bits for this file /// /// May return an error if the field is corrupted. diff --git a/tests/all.rs b/tests/all.rs index 63f6539f..11103bd6 100644 --- a/tests/all.rs +++ b/tests/all.rs @@ -935,6 +935,28 @@ fn long_linkname_gnu() { } } +#[test] +fn linkname_literal() { + for t in [tar::EntryType::Symlink, tar::EntryType::Link] { + let mut b = Builder::new(Vec::::new()); + let mut h = Header::new_gnu(); + h.set_entry_type(t); + h.set_size(0); + let path = "usr/lib/systemd/systemd-sysv-install"; + let target = "../../..//sbin/chkconfig"; + h.set_link_name_literal(target).unwrap(); + t!(b.append_data(&mut h, path, std::io::empty())); + + let contents = t!(b.into_inner()); + let mut a = Archive::new(&contents[..]); + + let e = &t!(t!(a.entries()).next().unwrap()); + assert_eq!(e.header().entry_type(), t); + assert_eq!(e.path().unwrap().to_str().unwrap(), path); + assert_eq!(e.link_name().unwrap().unwrap().to_str().unwrap(), target); + } +} + #[test] fn encoded_long_name_has_trailing_nul() { let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());