fix storing filenames longer than 100bytes in tar headers

This commit is contained in:
Nathan Fisher 2023-03-28 19:14:17 -04:00
parent 749bac6876
commit 4c80e08731

View File

@ -6,7 +6,7 @@ use std::{
fmt::{self, Write}, fmt::{self, Write},
fs::{self, Metadata}, fs::{self, Metadata},
ops::Deref, ops::Deref,
os::{linux::fs::MetadataExt, unix::fs::FileTypeExt}, os::{linux::fs::MetadataExt, unix::fs::FileTypeExt}, path::PathBuf, io,
}; };
#[repr(u8)] #[repr(u8)]
@ -203,9 +203,36 @@ impl Header {
}) })
} }
pub fn prefix(&self) -> Result<String, fmt::Error> {
let mut s = String::new();
for c in self.file_prefix {
if c != b'\0' {
write!(s, "{c}")?;
} else {
break;
}
}
Ok(s)
}
pub fn new(filename: &str) -> Result<Self, Error> { pub fn new(filename: &str) -> Result<Self, Error> {
let mut header = Header::default(); let mut header = Header::default();
let meta = fs::symlink_metadata(filename)?; let meta = fs::symlink_metadata(filename)?;
let (filename, prefix) = if filename.len() < 100 {
(filename.to_string(), None)
} else {
// Deal with file names longer than 100 bytes
let path = PathBuf::from(&filename);
let name = match path.file_name().and_then(|n| n.to_str()) {
Some(n) => n.to_string(),
None => return Err(Error::Io(io::Error::new(io::ErrorKind::Other, "Cannot get file name"))),
};
let dir = match path.parent() {
Some(d) => d,
None => return Err(Error::Io(io::Error::new(io::ErrorKind::Other, "Cannot get path prefix"))),
};
(name, Some(format!("{}", dir.display())))
};
/* Fill in metadata */ /* Fill in metadata */
header.fname[..filename.len()].copy_from_slice(filename.as_bytes()); header.fname[..filename.len()].copy_from_slice(filename.as_bytes());
@ -219,6 +246,9 @@ impl Header {
header.size[..size.len()].copy_from_slice(size.as_bytes()); header.size[..size.len()].copy_from_slice(size.as_bytes());
let mtime = format!("{:011o}", meta.st_mtime()); let mtime = format!("{:011o}", meta.st_mtime());
header.mtime[..mtime.len()].copy_from_slice(mtime.as_bytes()); header.mtime[..mtime.len()].copy_from_slice(mtime.as_bytes());
if let Some(prefix) = prefix {
header.file_prefix[..prefix.len()].copy_from_slice(prefix.as_bytes());
}
/* Get the file type and conditional metadata */ /* Get the file type and conditional metadata */
header.link_indicator[0] = FileType::from(&meta) as u8; header.link_indicator[0] = FileType::from(&meta) as u8;