src/pxar/sequential_decoder.rs: major refactoring of restore_sequential()
This splits the functionality of restore_sequential() into several smaller functions in order to allow to reuse them when restoring by seeking based on the goodbye table offsets. Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
This commit is contained in:
parent
48a4509c67
commit
be9b39e17a
@ -449,95 +449,13 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restore an archive into the specified directory.
|
fn restore_symlink(
|
||||||
///
|
|
||||||
/// The directory is created if it does not exist.
|
|
||||||
pub fn restore(&mut self, path: &Path) -> Result<(), Error> {
|
|
||||||
|
|
||||||
let _ = std::fs::create_dir(path);
|
|
||||||
|
|
||||||
let dir = nix::dir::Dir::open(path, nix::fcntl::OFlag::O_DIRECTORY, nix::sys::stat::Mode::empty())
|
|
||||||
.map_err(|err| format_err!("unable to open target directory {:?} - {}", path, err))?;
|
|
||||||
|
|
||||||
let mut relative_path = PathBuf::new();
|
|
||||||
self.restore_sequential(path, &mut relative_path, &OsString::new(), &dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn restore_sequential(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
base_path: &Path,
|
parent_fd: RawFd,
|
||||||
relative_path: &mut PathBuf,
|
full_path: &PathBuf,
|
||||||
filename: &OsStr, // repeats path last relative_path component
|
entry: &CaFormatEntry,
|
||||||
parent: &nix::dir::Dir,
|
filename: &OsStr
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
|
||||||
let parent_fd = parent.as_raw_fd();
|
|
||||||
|
|
||||||
let full_path = base_path.join(&relative_path);
|
|
||||||
|
|
||||||
(self.callback)(&full_path)?;
|
|
||||||
|
|
||||||
let head: CaFormatHeader = self.read_item()?;
|
|
||||||
|
|
||||||
if head.htype == PXAR_FORMAT_HARDLINK {
|
|
||||||
let (target, _offset) = self.read_hardlink(head.size)?;
|
|
||||||
let target_path = base_path.join(&target);
|
|
||||||
//println!("HARDLINK: {} {:?} -> {:?}", offset, full_path, target_path);
|
|
||||||
hardlink(&target_path, &full_path)?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
check_ca_header::<CaFormatEntry>(&head, CA_FORMAT_ENTRY)?;
|
|
||||||
let entry: CaFormatEntry = self.read_item()?;
|
|
||||||
|
|
||||||
|
|
||||||
let mode = entry.mode as u32; //fixme: upper 32bits?
|
|
||||||
|
|
||||||
let ifmt = mode & libc::S_IFMT;
|
|
||||||
|
|
||||||
if ifmt == libc::S_IFDIR {
|
|
||||||
let dir;
|
|
||||||
if filename.is_empty() {
|
|
||||||
dir = nix::dir::Dir::openat(parent_fd, ".", OFlag::O_DIRECTORY, Mode::empty())?;
|
|
||||||
} else {
|
|
||||||
dir = dir_mkdirat(parent_fd, filename, true)
|
|
||||||
.map_err(|err| format_err!("unable to open directory {:?} - {}", full_path, err))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.restore_ugid(&entry, dir.as_raw_fd())?;
|
|
||||||
// fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
|
|
||||||
let mut head = self.restore_attributes(&entry, dir.as_raw_fd())
|
|
||||||
.map_err(|err| format_err!("Restoring of directory attributes failed - {}", err))?;
|
|
||||||
|
|
||||||
while head.htype == CA_FORMAT_FILENAME {
|
|
||||||
let name = self.read_filename(head.size)?;
|
|
||||||
relative_path.push(&name);
|
|
||||||
self.restore_sequential(base_path, relative_path, &name, &dir)?;
|
|
||||||
relative_path.pop();
|
|
||||||
|
|
||||||
head = self.read_item()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if head.htype != CA_FORMAT_GOODBYE {
|
|
||||||
bail!("got unknown header type inside directory entry {:016x}", head.htype);
|
|
||||||
}
|
|
||||||
|
|
||||||
//println!("Skip Goodbye");
|
|
||||||
if head.size < HEADER_SIZE { bail!("detected short goodbye table"); }
|
|
||||||
|
|
||||||
self.skip_bytes((head.size - HEADER_SIZE) as usize)?;
|
|
||||||
|
|
||||||
self.restore_mode(&entry, dir.as_raw_fd())?;
|
|
||||||
self.restore_mtime(&entry, dir.as_raw_fd())?;
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if filename.is_empty() {
|
|
||||||
bail!("got empty file name at {:?}", full_path)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ifmt == libc::S_IFLNK {
|
|
||||||
//fixme: create symlink
|
//fixme: create symlink
|
||||||
//fixme: restore permission, acls, xattr, ...
|
//fixme: restore permission, acls, xattr, ...
|
||||||
|
|
||||||
@ -559,57 +477,67 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
|
|||||||
self.restore_ugid_at(&entry, parent_fd, filename)?;
|
self.restore_ugid_at(&entry, parent_fd, filename)?;
|
||||||
self.restore_mtime_at(&entry, parent_fd, filename)?;
|
self.restore_mtime_at(&entry, parent_fd, filename)?;
|
||||||
|
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
if ifmt == libc::S_IFSOCK {
|
fn restore_socket(
|
||||||
|
&mut self,
|
||||||
|
parent_fd: RawFd,
|
||||||
|
entry: &CaFormatEntry,
|
||||||
|
filename: &OsStr
|
||||||
|
) -> Result<(), Error> {
|
||||||
self.restore_socket_at(parent_fd, filename)?;
|
self.restore_socket_at(parent_fd, filename)?;
|
||||||
|
|
||||||
self.restore_mode_at(&entry, parent_fd, filename)?;
|
self.restore_mode_at(&entry, parent_fd, filename)?;
|
||||||
self.restore_ugid_at(&entry, parent_fd, filename)?;
|
self.restore_ugid_at(&entry, parent_fd, filename)?;
|
||||||
self.restore_mtime_at(&entry, parent_fd, filename)?;
|
self.restore_mtime_at(&entry, parent_fd, filename)?;
|
||||||
|
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
if ifmt == libc::S_IFIFO {
|
fn restore_fifo(
|
||||||
|
&mut self,
|
||||||
|
parent_fd: RawFd,
|
||||||
|
entry: &CaFormatEntry,
|
||||||
|
filename: &OsStr
|
||||||
|
) -> Result<(), Error> {
|
||||||
self.restore_fifo_at(parent_fd, filename)?;
|
self.restore_fifo_at(parent_fd, filename)?;
|
||||||
|
|
||||||
self.restore_mode_at(&entry, parent_fd, filename)?;
|
self.restore_mode_at(&entry, parent_fd, filename)?;
|
||||||
self.restore_ugid_at(&entry, parent_fd, filename)?;
|
self.restore_ugid_at(&entry, parent_fd, filename)?;
|
||||||
self.restore_mtime_at(&entry, parent_fd, filename)?;
|
self.restore_mtime_at(&entry, parent_fd, filename)?;
|
||||||
|
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ifmt == libc::S_IFBLK) || (ifmt == libc::S_IFCHR) {
|
fn restore_device(
|
||||||
|
&mut self,
|
||||||
|
parent_fd: RawFd,
|
||||||
|
entry: &CaFormatEntry,
|
||||||
|
filename: &OsStr
|
||||||
|
) -> Result<(), Error> {
|
||||||
let head: CaFormatHeader = self.read_item()?;
|
let head: CaFormatHeader = self.read_item()?;
|
||||||
match head.htype {
|
if head.htype != CA_FORMAT_DEVICE {
|
||||||
CA_FORMAT_DEVICE => {
|
|
||||||
let device: CaFormatDevice = self.read_item()?;
|
|
||||||
self.restore_device_at(&entry, parent_fd, filename, &device)?;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
bail!("got unknown header type inside device entry {:016x}", head.htype);
|
bail!("got unknown header type inside device entry {:016x}", head.htype);
|
||||||
}
|
}
|
||||||
}
|
let device: CaFormatDevice = self.read_item()?;
|
||||||
|
self.restore_device_at(&entry, parent_fd, filename, &device)?;
|
||||||
self.restore_mode_at(&entry, parent_fd, filename)?;
|
self.restore_mode_at(&entry, parent_fd, filename)?;
|
||||||
self.restore_ugid_at(&entry, parent_fd, filename)?;
|
self.restore_ugid_at(&entry, parent_fd, filename)?;
|
||||||
self.restore_mtime_at(&entry, parent_fd, filename)?;
|
self.restore_mtime_at(&entry, parent_fd, filename)?;
|
||||||
|
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
if ifmt == libc::S_IFREG {
|
fn restore_regular_file(
|
||||||
|
&mut self,
|
||||||
|
parent_fd: RawFd,
|
||||||
|
full_path: &PathBuf,
|
||||||
|
entry: &CaFormatEntry,
|
||||||
|
filename: &OsStr
|
||||||
|
) -> Result<(), Error> {
|
||||||
let mut read_buffer: [u8; 64*1024] = unsafe { std::mem::uninitialized() };
|
let mut read_buffer: [u8; 64*1024] = unsafe { std::mem::uninitialized() };
|
||||||
|
|
||||||
let flags = OFlag::O_CREAT|OFlag::O_WRONLY|OFlag::O_EXCL;
|
let flags = OFlag::O_CREAT|OFlag::O_WRONLY|OFlag::O_EXCL;
|
||||||
let open_mode = Mode::from_bits_truncate(0o0600 | mode);
|
let open_mode = Mode::from_bits_truncate(0o0600 | entry.mode as u32); //fixme: upper 32bits of entry.mode?
|
||||||
|
|
||||||
let mut file = file_openat(parent_fd, filename, flags, open_mode)
|
let mut file = file_openat(parent_fd, filename, flags, open_mode)
|
||||||
.map_err(|err| format_err!("open file {:?} failed - {}", full_path, err))?;
|
.map_err(|err| format_err!("open file {:?} failed - {}", full_path, err))?;
|
||||||
@ -642,10 +570,107 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
|
|||||||
self.restore_mode(&entry, file.as_raw_fd())?;
|
self.restore_mode(&entry, file.as_raw_fd())?;
|
||||||
self.restore_mtime(&entry, file.as_raw_fd())?;
|
self.restore_mtime(&entry, file.as_raw_fd())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restore_dir_sequential(
|
||||||
|
&mut self,
|
||||||
|
base_path: &Path,
|
||||||
|
relative_path: &mut PathBuf,
|
||||||
|
full_path: &PathBuf,
|
||||||
|
parent_fd: RawFd,
|
||||||
|
entry: &CaFormatEntry,
|
||||||
|
filename: &OsStr,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let dir = if filename.is_empty() {
|
||||||
|
nix::dir::Dir::openat(parent_fd, ".", OFlag::O_DIRECTORY, Mode::empty())?
|
||||||
|
} else {
|
||||||
|
dir_mkdirat(parent_fd, filename, true)
|
||||||
|
.map_err(|err| format_err!("unable to open directory {:?} - {}", full_path, err))?
|
||||||
|
};
|
||||||
|
|
||||||
|
self.restore_ugid(&entry, dir.as_raw_fd())?;
|
||||||
|
// fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
|
||||||
|
let mut head = self.restore_attributes(&entry, dir.as_raw_fd())
|
||||||
|
.map_err(|err| format_err!("Restoring of directory attributes failed - {}", err))?;
|
||||||
|
|
||||||
|
while head.htype == CA_FORMAT_FILENAME {
|
||||||
|
let name = self.read_filename(head.size)?;
|
||||||
|
relative_path.push(&name);
|
||||||
|
self.restore_sequential(base_path, relative_path, &name, &dir)?;
|
||||||
|
relative_path.pop();
|
||||||
|
head = self.read_item()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if head.htype != CA_FORMAT_GOODBYE {
|
||||||
|
bail!("got unknown header type inside directory entry {:016x}", head.htype);
|
||||||
|
}
|
||||||
|
|
||||||
|
//println!("Skip Goodbye");
|
||||||
|
if head.size < HEADER_SIZE { bail!("detected short goodbye table"); }
|
||||||
|
self.skip_bytes((head.size - HEADER_SIZE) as usize)?;
|
||||||
|
self.restore_mode(&entry, dir.as_raw_fd())?;
|
||||||
|
self.restore_mtime(&entry, dir.as_raw_fd())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Restore an archive into the specified directory.
|
||||||
|
///
|
||||||
|
/// The directory is created if it does not exist.
|
||||||
|
pub fn restore(&mut self, path: &Path) -> Result<(), Error> {
|
||||||
|
|
||||||
|
let _ = std::fs::create_dir(path);
|
||||||
|
|
||||||
|
let dir = nix::dir::Dir::open(path, nix::fcntl::OFlag::O_DIRECTORY, nix::sys::stat::Mode::empty())
|
||||||
|
.map_err(|err| format_err!("unable to open target directory {:?} - {}", path, err))?;
|
||||||
|
|
||||||
|
let mut relative_path = PathBuf::new();
|
||||||
|
self.restore_sequential(path, &mut relative_path, &OsString::new(), &dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restore_sequential(
|
||||||
|
&mut self,
|
||||||
|
base_path: &Path,
|
||||||
|
relative_path: &mut PathBuf,
|
||||||
|
filename: &OsStr, // repeats path last relative_path component
|
||||||
|
parent: &nix::dir::Dir,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let parent_fd = parent.as_raw_fd();
|
||||||
|
let full_path = base_path.join(&relative_path);
|
||||||
|
|
||||||
|
(self.callback)(&full_path)?;
|
||||||
|
|
||||||
|
let head: CaFormatHeader = self.read_item()?;
|
||||||
|
if head.htype == PXAR_FORMAT_HARDLINK {
|
||||||
|
let (target, _offset) = self.read_hardlink(head.size)?;
|
||||||
|
let target_path = base_path.join(&target);
|
||||||
|
//println!("HARDLINK: {} {:?} -> {:?}", offset, full_path, target_path);
|
||||||
|
hardlink(&target_path, &full_path)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
check_ca_header::<CaFormatEntry>(&head, CA_FORMAT_ENTRY)?;
|
||||||
|
let entry: CaFormatEntry = self.read_item()?;
|
||||||
|
|
||||||
|
let mode = entry.mode as u32; //fixme: upper 32bits?
|
||||||
|
let ifmt = mode & libc::S_IFMT;
|
||||||
|
if ifmt == libc::S_IFDIR {
|
||||||
|
return self.restore_dir_sequential(base_path, relative_path, &full_path, parent_fd, &entry, &filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if filename.is_empty() {
|
||||||
|
bail!("got empty file name at {:?}", full_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
match ifmt {
|
||||||
|
libc::S_IFLNK => self.restore_symlink(parent_fd, &full_path, &entry, &filename),
|
||||||
|
libc::S_IFSOCK => self.restore_socket(parent_fd, &entry, &filename),
|
||||||
|
libc::S_IFIFO => self.restore_fifo(parent_fd, &entry, &filename),
|
||||||
|
libc::S_IFBLK | libc::S_IFCHR => self.restore_device(parent_fd, &entry, &filename),
|
||||||
|
libc::S_IFREG => self.restore_regular_file(parent_fd, &full_path, &entry, &filename),
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List/Dump archive content.
|
/// List/Dump archive content.
|
||||||
|
Loading…
Reference in New Issue
Block a user