pxar: implement hardlinks

So we are no longer compatible with catar ...
This commit is contained in:
Dietmar Maurer
2019-03-16 11:02:12 +01:00
parent c238e4d4b3
commit af572aaa4c
4 changed files with 125 additions and 41 deletions

View File

@ -44,7 +44,7 @@ pub struct Encoder<'a, W: Write> {
all_file_systems: bool,
root_st_dev: u64,
verbose: bool,
hardlinks: HashMap<HardLinkInfo, PathBuf>,
hardlinks: HashMap<HardLinkInfo, (PathBuf, u64)>,
}
impl <'a, W: Write> Encoder<'a, W> {
@ -355,35 +355,47 @@ impl <'a, W: Write> Encoder<'a, W> {
} else if ifmt == libc::S_IFREG {
let mut hardlink_target = None;
if stat.st_nlink > 1 {
let link_info = HardLinkInfo { st_dev: stat.st_dev, st_ino: stat.st_ino };
if let Some(target) = self.hardlinks.get(&link_info) {
// fixme: store hardlink info somwhow?
eprintln!("FOUND HARDLINK {:?}", target);
} else {
self.hardlinks.insert(link_info, self.relative_path.clone());
hardlink_target = self.hardlinks.get(&link_info).map(|(v, offset)| {
let mut target = v.clone().into_os_string();
target.push("\0"); // add Nul byte
(target, (start_pos as u64) - offset)
});
if hardlink_target == None {
self.hardlinks.insert(link_info, (self.relative_path.clone(), start_pos as u64));
}
}
let filefd = match nix::fcntl::openat(rawfd, filename.as_ref(), OFlag::O_NOFOLLOW, Mode::empty()) {
Ok(filefd) => filefd,
Err(nix::Error::Sys(Errno::ENOENT)) => {
self.report_vanished_file(&self.full_path())?;
continue;
},
Err(err) => bail!("open file {:?} failed - {}", self.full_path(), err),
};
if let Some((target, offset)) = hardlink_target {
self.write_filename(&filename)?;
self.encode_hardlink(target.as_bytes(), offset)?;
let child_magic = if dir_stat.st_dev != stat.st_dev {
detect_fs_type(filefd)?
} else {
magic
};
self.write_filename(&filename)?;
let res = self.encode_file(filefd, &stat, child_magic);
let _ = nix::unistd::close(filefd); // ignore close errors
res?;
let filefd = match nix::fcntl::openat(rawfd, filename.as_ref(), OFlag::O_NOFOLLOW, Mode::empty()) {
Ok(filefd) => filefd,
Err(nix::Error::Sys(Errno::ENOENT)) => {
self.report_vanished_file(&self.full_path())?;
continue;
},
Err(err) => bail!("open file {:?} failed - {}", self.full_path(), err),
};
let child_magic = if dir_stat.st_dev != stat.st_dev {
detect_fs_type(filefd)?
} else {
magic
};
self.write_filename(&filename)?;
let res = self.encode_file(filefd, &stat, child_magic);
let _ = nix::unistd::close(filefd); // ignore close errors
res?;
}
} else if ifmt == libc::S_IFLNK {
let mut buffer = [0u8; libc::PATH_MAX as usize];
@ -540,6 +552,18 @@ impl <'a, W: Write> Encoder<'a, W> {
Ok(())
}
fn encode_hardlink(&mut self, target: &[u8], offset: u64) -> Result<(), Error> {
//println!("encode_hardlink: {:?} -> {:?}", self.full_path(), target);
// Note: HARDLINK replaces an ENTRY.
self.write_header(PXAR_FORMAT_HARDLINK, (target.len() as u64) + 8)?;
self.write_item(offset)?;
self.write(target)?;
Ok(())
}
// the report_XXX method may raise and error - depending on encoder configuration
fn report_vanished_file(&self, path: &Path) -> Result<(), Error> {