src/pxar/sequential_decoder.rs: adapt code for partial restore by match pattern

Allows to partially restore an archive by passing match patterns to the restore
function.
The whole restore is performed in sequential, therefore the whole archive has to
be read.
By wrapping the RawFd into an Option it can be controlled if the corresponding
part is restored (in case of Some(fd)) or if the Reader reads over it
without restore (in case of None).

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
This commit is contained in:
Christian Ebner 2019-07-16 18:19:43 +02:00 committed by Dietmar Maurer
parent 4902291673
commit d32c2e4d27

View File

@ -172,7 +172,11 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
Ok(CaFormatFCaps { data: buffer }) Ok(CaFormatFCaps { data: buffer })
} }
fn restore_attributes(&mut self, entry: &CaFormatEntry, fd: RawFd) -> Result<CaFormatHeader, Error> { fn restore_attributes(
&mut self,
entry: &CaFormatEntry,
fd: Option<RawFd>
) -> Result<CaFormatHeader, Error> {
let mut xattrs = Vec::new(); let mut xattrs = Vec::new();
let mut fcaps = None; let mut fcaps = None;
let mut quota_projid = None; let mut quota_projid = None;
@ -257,6 +261,9 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
head = self.read_item()?; head = self.read_item()?;
size = (head.size - HEADER_SIZE) as usize; size = (head.size - HEADER_SIZE) as usize;
} }
// If fd is None, this indicates that we just want to skip over these entries (no restore).
// If on the other hand there is Some(fd), restore the attributes on it.
if let Some(fd) = fd {
self.restore_xattrs_fcaps_fd(fd, xattrs, fcaps)?; self.restore_xattrs_fcaps_fd(fd, xattrs, fcaps)?;
let mut acl = acl::ACL::init(5)?; let mut acl = acl::ACL::init(5)?;
@ -303,11 +310,18 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
acl.set_file(&proc_path, acl::ACL_TYPE_DEFAULT)?; acl.set_file(&proc_path, acl::ACL_TYPE_DEFAULT)?;
} }
self.restore_quota_projid(fd, quota_projid)?; self.restore_quota_projid(fd, quota_projid)?;
}
Ok(head) Ok(head)
} }
fn restore_xattrs_fcaps_fd(&mut self, fd: RawFd, xattrs: Vec<CaFormatXAttr>, fcaps: Option<CaFormatFCaps>) -> Result<(), Error> { // Restore xattrs and fcaps to the given RawFd.
fn restore_xattrs_fcaps_fd(
&mut self,
fd: RawFd,
xattrs: Vec<CaFormatXAttr>,
fcaps: Option<CaFormatFCaps>
) -> Result<(), Error> {
for xattr in xattrs { for xattr in xattrs {
if let Err(err) = xattr::fsetxattr(fd, xattr) { if let Err(err) = xattr::fsetxattr(fd, xattr) {
bail!("fsetxattr failed with error: {}", err); bail!("fsetxattr failed with error: {}", err);
@ -322,7 +336,11 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
Ok(()) Ok(())
} }
fn restore_quota_projid(&mut self, fd: RawFd, projid: Option<CaFormatQuotaProjID>) -> Result<(), Error> { fn restore_quota_projid(
&mut self,
fd: RawFd,
projid: Option<CaFormatQuotaProjID>
) -> Result<(), Error> {
if let Some(projid) = projid { if let Some(projid) = projid {
let mut fsxattr = fs::FSXAttr::default(); let mut fsxattr = fs::FSXAttr::default();
unsafe { unsafe {
@ -453,7 +471,7 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
fn restore_symlink( fn restore_symlink(
&mut self, &mut self,
parent_fd: RawFd, parent_fd: Option<RawFd>,
full_path: &PathBuf, full_path: &PathBuf,
entry: &CaFormatEntry, entry: &CaFormatEntry,
filename: &OsStr filename: &OsStr
@ -466,53 +484,61 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
CA_FORMAT_SYMLINK => { CA_FORMAT_SYMLINK => {
let target = self.read_link(head.size)?; let target = self.read_link(head.size)?;
//println!("TARGET: {:?}", target); //println!("TARGET: {:?}", target);
if let Err(err) = symlinkat(&target, parent_fd, filename) { if let Some(fd) = parent_fd {
if let Err(err) = symlinkat(&target, fd, filename) {
bail!("create symlink {:?} failed - {}", full_path, err); bail!("create symlink {:?} failed - {}", full_path, err);
} }
} }
}
_ => { _ => {
bail!("got unknown header type inside symlink entry {:016x}", head.htype); bail!("got unknown header type inside symlink entry {:016x}", head.htype);
} }
} }
// self.restore_mode_at(&entry, parent_fd, filename)?; //not supported on symlinks if let Some(fd) = parent_fd {
self.restore_ugid_at(&entry, parent_fd, filename)?; // self.restore_mode_at(&entry, fd, filename)?; //not supported on symlinks
self.restore_mtime_at(&entry, parent_fd, filename)?; self.restore_ugid_at(&entry, fd, filename)?;
self.restore_mtime_at(&entry, fd, filename)?;
}
Ok(()) Ok(())
} }
fn restore_socket( fn restore_socket(
&mut self, &mut self,
parent_fd: RawFd, parent_fd: Option<RawFd>,
entry: &CaFormatEntry, entry: &CaFormatEntry,
filename: &OsStr filename: &OsStr
) -> Result<(), Error> { ) -> Result<(), Error> {
self.restore_socket_at(parent_fd, filename)?; if let Some(fd) = parent_fd {
self.restore_mode_at(&entry, parent_fd, filename)?; self.restore_socket_at(fd, filename)?;
self.restore_ugid_at(&entry, parent_fd, filename)?; self.restore_mode_at(&entry, fd, filename)?;
self.restore_mtime_at(&entry, parent_fd, filename)?; self.restore_ugid_at(&entry, fd, filename)?;
self.restore_mtime_at(&entry, fd, filename)?;
}
Ok(()) Ok(())
} }
fn restore_fifo( fn restore_fifo(
&mut self, &mut self,
parent_fd: RawFd, parent_fd: Option<RawFd>,
entry: &CaFormatEntry, entry: &CaFormatEntry,
filename: &OsStr filename: &OsStr
) -> Result<(), Error> { ) -> Result<(), Error> {
self.restore_fifo_at(parent_fd, filename)?; if let Some(fd) = parent_fd {
self.restore_mode_at(&entry, parent_fd, filename)?; self.restore_fifo_at(fd, filename)?;
self.restore_ugid_at(&entry, parent_fd, filename)?; self.restore_mode_at(&entry, fd, filename)?;
self.restore_mtime_at(&entry, parent_fd, filename)?; self.restore_ugid_at(&entry, fd, filename)?;
self.restore_mtime_at(&entry, fd, filename)?;
}
Ok(()) Ok(())
} }
fn restore_device( fn restore_device(
&mut self, &mut self,
parent_fd: RawFd, parent_fd: Option<RawFd>,
entry: &CaFormatEntry, entry: &CaFormatEntry,
filename: &OsStr filename: &OsStr
) -> Result<(), Error> { ) -> Result<(), Error> {
@ -521,32 +547,38 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
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()?; let device: CaFormatDevice = self.read_item()?;
self.restore_device_at(&entry, parent_fd, filename, &device)?; if let Some(fd) = parent_fd {
self.restore_mode_at(&entry, parent_fd, filename)?; self.restore_device_at(&entry, fd, filename, &device)?;
self.restore_ugid_at(&entry, parent_fd, filename)?; self.restore_mode_at(&entry, fd, filename)?;
self.restore_mtime_at(&entry, parent_fd, filename)?; self.restore_ugid_at(&entry, fd, filename)?;
self.restore_mtime_at(&entry, fd, filename)?;
}
Ok(()) Ok(())
} }
/// Restores a regular file with its content and associated attributes to the
/// folder provided by the raw filedescriptor.
/// If None is passed instead of a filedescriptor, the file is not restored but
/// the archive reader is skipping over it instead.
fn restore_regular_file( fn restore_regular_file(
&mut self, &mut self,
parent_fd: RawFd, parent_fd: Option<RawFd>,
full_path: &PathBuf, full_path: &PathBuf,
entry: &CaFormatEntry, entry: &CaFormatEntry,
filename: &OsStr filename: &OsStr
) -> Result<(), Error> { ) -> Result<(), Error> {
let mut read_buffer: [u8; 64*1024] = unsafe { std::mem::uninitialized() }; let mut read_buffer: [u8; 64*1024] = unsafe { std::mem::uninitialized() };
if let Some(fd) = parent_fd {
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 | entry.mode as u32); //fixme: upper 32bits of entry.mode? let open_mode = Mode::from_bits_truncate(0o0600 | entry.mode as u32); //fixme: upper 32bits of entry.mode?
let mut file = file_openat(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))?;
self.restore_ugid(&entry, file.as_raw_fd())?; self.restore_ugid(&entry, file.as_raw_fd())?;
// fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350 // fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
let head = self.restore_attributes(&entry, file.as_raw_fd()) let head = self.restore_attributes(&entry, Some(file.as_raw_fd()))
.map_err(|err| format_err!("Restoring of file attributes failed - {}", err))?; .map_err(|err| format_err!("Restoring of file attributes failed - {}", err))?;
if head.htype != CA_FORMAT_PAYLOAD { if head.htype != CA_FORMAT_PAYLOAD {
@ -570,6 +602,17 @@ 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())?;
} else {
let head = self.restore_attributes(&entry, None)
.map_err(|err| format_err!("Restoring of file attributes failed - {}", err))?;
if head.htype != CA_FORMAT_PAYLOAD {
bail!("got unknown header type for file entry {:016x}", head.htype);
}
if head.size < HEADER_SIZE {
bail!("detected short payload");
}
self.skip_bytes((head.size - HEADER_SIZE) as usize)?;
}
Ok(()) Ok(())
} }
@ -579,26 +622,35 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
base_path: &Path, base_path: &Path,
relative_path: &mut PathBuf, relative_path: &mut PathBuf,
full_path: &PathBuf, full_path: &PathBuf,
parent_fd: RawFd, parent_fd: Option<RawFd>,
entry: &CaFormatEntry, entry: &CaFormatEntry,
filename: &OsStr, filename: &OsStr,
match_pattern: &Vec<PxarExcludePattern>,
) -> Result<(), Error> { ) -> Result<(), Error> {
// By passing back Some(dir) we assure the fd stays open and in scope
let (fd, _dir) = if let Some(pfd) = parent_fd {
let dir = if filename.is_empty() { let dir = if filename.is_empty() {
nix::dir::Dir::openat(parent_fd, ".", OFlag::O_DIRECTORY, Mode::empty())? nix::dir::Dir::openat(pfd, ".", OFlag::O_DIRECTORY, Mode::empty())?
} else { } else {
dir_mkdirat(parent_fd, filename, true) dir_mkdirat(pfd, filename, true)
.map_err(|err| format_err!("unable to open directory {:?} - {}", full_path, err))? .map_err(|err| format_err!("unable to open directory {:?} - {}", full_path, err))?
}; };
(Some(dir.as_raw_fd()), Some(dir))
} else {
(None, None)
};
self.restore_ugid(&entry, dir.as_raw_fd())?; if let Some(fd) = fd {
self.restore_ugid(&entry, fd)?;
}
// fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350 // 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()) let mut head = self.restore_attributes(&entry, fd)
.map_err(|err| format_err!("Restoring of directory attributes failed - {}", err))?; .map_err(|err| format_err!("Restoring of directory attributes failed - {}", err))?;
while head.htype == CA_FORMAT_FILENAME { while head.htype == CA_FORMAT_FILENAME {
let name = self.read_filename(head.size)?; let name = self.read_filename(head.size)?;
relative_path.push(&name); relative_path.push(&name);
self.restore_sequential(base_path, relative_path, &name, &dir)?; self.restore_sequential(base_path, relative_path, &name, fd, match_pattern)?;
relative_path.pop(); relative_path.pop();
head = self.read_item()?; head = self.read_item()?;
} }
@ -610,8 +662,12 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
//println!("Skip Goodbye"); //println!("Skip Goodbye");
if head.size < HEADER_SIZE { bail!("detected short goodbye table"); } if head.size < HEADER_SIZE { bail!("detected short goodbye table"); }
self.skip_bytes((head.size - HEADER_SIZE) as usize)?; 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())?; // Only restore if we want to restore this part of the archive
if let Some(fd) = fd {
self.restore_mode(&entry, fd)?;
self.restore_mtime(&entry, fd)?;
}
Ok(()) Ok(())
} }
@ -619,15 +675,16 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
/// Restore an archive into the specified directory. /// Restore an archive into the specified directory.
/// ///
/// The directory is created if it does not exist. /// The directory is created if it does not exist.
pub fn restore(&mut self, path: &Path) -> Result<(), Error> { pub fn restore(&mut self, path: &Path, match_pattern: &Vec<PxarExcludePattern>) -> Result<(), Error> {
let _ = std::fs::create_dir(path); let _ = std::fs::create_dir(path);
let dir = nix::dir::Dir::open(path, nix::fcntl::OFlag::O_DIRECTORY, nix::sys::stat::Mode::empty()) 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))?; .map_err(|err| format_err!("unable to open target directory {:?} - {}", path, err))?;
let fd = Some(dir.as_raw_fd());
let mut relative_path = PathBuf::new(); let mut relative_path = PathBuf::new();
self.restore_sequential(path, &mut relative_path, &OsString::new(), &dir) self.restore_sequential(path, &mut relative_path, &OsString::new(), fd, match_pattern)
} }
fn restore_sequential( fn restore_sequential(
@ -635,9 +692,9 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
base_path: &Path, base_path: &Path,
relative_path: &mut PathBuf, relative_path: &mut PathBuf,
filename: &OsStr, // repeats path last relative_path component filename: &OsStr, // repeats path last relative_path component
parent: &nix::dir::Dir, parent_fd: Option<RawFd>,
match_pattern: &Vec<PxarExcludePattern>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let parent_fd = parent.as_raw_fd();
let full_path = base_path.join(&relative_path); let full_path = base_path.join(&relative_path);
(self.callback)(&full_path)?; (self.callback)(&full_path)?;
@ -646,19 +703,36 @@ impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F
if head.htype == PXAR_FORMAT_HARDLINK { if head.htype == PXAR_FORMAT_HARDLINK {
let (target, _offset) = self.read_hardlink(head.size)?; let (target, _offset) = self.read_hardlink(head.size)?;
let target_path = base_path.join(&target); let target_path = base_path.join(&target);
if let Some(_) = parent_fd {
hardlink(&target_path, &full_path)?; hardlink(&target_path, &full_path)?;
}
return Ok(()); return Ok(());
} }
check_ca_header::<CaFormatEntry>(&head, CA_FORMAT_ENTRY)?; check_ca_header::<CaFormatEntry>(&head, CA_FORMAT_ENTRY)?;
let entry: CaFormatEntry = self.read_item()?; let entry: CaFormatEntry = self.read_item()?;
let mut fd = parent_fd;
let mut child_pattern = Vec::new();
if match_pattern.len() > 0 {
if filename.is_empty() {
child_pattern = match_pattern.clone();
} else {
match match_filename(filename, entry.mode as u32 & libc::S_IFMT == libc::S_IFDIR, match_pattern) {
(MatchType::None, _) => fd = None,
(MatchType::Exclude, _) => fd = None,
(_, pattern) => child_pattern = pattern,
}
}
}
match entry.mode as u32 & libc::S_IFMT { match entry.mode as u32 & libc::S_IFMT {
libc::S_IFDIR => self.restore_dir_sequential(base_path, relative_path, &full_path, parent_fd, &entry, &filename), libc::S_IFDIR => self.restore_dir_sequential(base_path, relative_path, &full_path, fd, &entry, &filename, &child_pattern),
libc::S_IFLNK => self.restore_symlink(parent_fd, &full_path, &entry, &filename), libc::S_IFLNK => self.restore_symlink(fd, &full_path, &entry, &filename),
libc::S_IFSOCK => self.restore_socket(parent_fd, &entry, &filename), libc::S_IFSOCK => self.restore_socket(fd, &entry, &filename),
libc::S_IFIFO => self.restore_fifo(parent_fd, &entry, &filename), libc::S_IFIFO => self.restore_fifo(fd, &entry, &filename),
libc::S_IFBLK | libc::S_IFCHR => self.restore_device(parent_fd, &entry, &filename), libc::S_IFBLK | libc::S_IFCHR => self.restore_device(fd, &entry, &filename),
libc::S_IFREG => self.restore_regular_file(parent_fd, &full_path, &entry, &filename), libc::S_IFREG => self.restore_regular_file(fd, &full_path, &entry, &filename),
_ => Ok(()), _ => Ok(()),
} }
} }