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(())
|
||||
}
|
||||
|
||||
/// 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(
|
||||
fn restore_symlink(
|
||||
&mut self,
|
||||
base_path: &Path,
|
||||
relative_path: &mut PathBuf,
|
||||
filename: &OsStr, // repeats path last relative_path component
|
||||
parent: &nix::dir::Dir,
|
||||
parent_fd: RawFd,
|
||||
full_path: &PathBuf,
|
||||
entry: &CaFormatEntry,
|
||||
filename: &OsStr
|
||||
) -> 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: 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_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_mode_at(&entry, parent_fd, filename)?;
|
||||
self.restore_ugid_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_mode_at(&entry, parent_fd, filename)?;
|
||||
self.restore_ugid_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()?;
|
||||
match head.htype {
|
||||
CA_FORMAT_DEVICE => {
|
||||
let device: CaFormatDevice = self.read_item()?;
|
||||
self.restore_device_at(&entry, parent_fd, filename, &device)?;
|
||||
}
|
||||
_ => {
|
||||
if head.htype != CA_FORMAT_DEVICE {
|
||||
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_ugid_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 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)
|
||||
.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_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(());
|
||||
}
|
||||
|
||||
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.
|
||||
|
Loading…
Reference in New Issue
Block a user