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:
Christian Ebner 2019-07-04 16:15:54 +02:00 committed by Dietmar Maurer
parent 48a4509c67
commit be9b39e17a

View File

@ -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.