catar/decoder.rs: restore mode, uid, gid and mtime

This commit is contained in:
Dietmar Maurer 2019-01-09 14:44:00 +01:00
parent 25f60394d2
commit 23f68e53b4
1 changed files with 143 additions and 46 deletions

View File

@ -154,6 +154,80 @@ impl <'a, R: Read + Seek> CaTarDecoder<'a, R> {
Ok(()) Ok(())
} }
fn restore_attributes(&mut self, entry: &CaFormatEntry) -> Result<CaFormatHeader, Error> {
loop {
let head: CaFormatHeader = self.read_item()?;
match head.htype {
_ => return Ok(head),
}
}
}
fn restore_mode(&mut self, entry: &CaFormatEntry, fd: RawFd) -> Result<(), Error> {
let mode = Mode::from_bits_truncate((entry.mode as u32) & 0o7777);
nix::sys::stat::fchmod(fd, mode)?;
Ok(())
}
fn restore_mode_at(&mut self, entry: &CaFormatEntry, dirfd: RawFd, filename: &OsStr) -> Result<(), Error> {
let mode = Mode::from_bits_truncate((entry.mode as u32) & 0o7777);
nix::sys::stat::fchmodat(Some(dirfd), filename, mode, nix::sys::stat::FchmodatFlags::NoFollowSymlink)?;
Ok(())
}
fn restore_ugid(&mut self, entry: &CaFormatEntry, fd: RawFd) -> Result<(), Error> {
let uid = entry.uid as u32;
let gid = entry.gid as u32;
let res = unsafe { libc::fchown(fd, uid, gid) };
Errno::result(res)?;
Ok(())
}
fn restore_ugid_at(&mut self, entry: &CaFormatEntry, dirfd: RawFd, filename: &OsStr) -> Result<(), Error> {
let uid = entry.uid as u32;
let gid = entry.gid as u32;
let res = filename.with_nix_path(|cstr| unsafe {
libc::fchownat(dirfd, cstr.as_ptr(), uid, gid, libc::AT_SYMLINK_NOFOLLOW)
})?;
Errno::result(res)?;
Ok(())
}
fn restore_mtime(&mut self, entry: &CaFormatEntry, fd: RawFd) -> Result<(), Error> {
let times = nsec_to_update_timespec(entry.mtime);
let res = unsafe { libc::futimens(fd, &times[0]) };
Errno::result(res)?;
Ok(())
}
fn restore_mtime_at(&mut self, entry: &CaFormatEntry, dirfd: RawFd, filename: &OsStr) -> Result<(), Error> {
let times = nsec_to_update_timespec(entry.mtime);
let res = filename.with_nix_path(|cstr| unsafe {
libc::utimensat(dirfd, cstr.as_ptr(), &times[0], libc::AT_SYMLINK_NOFOLLOW)
})?;
Errno::result(res)?;
Ok(())
}
pub fn restore_sequential<F: Fn(&Path) -> Result<(), Error>>( pub fn restore_sequential<F: Fn(&Path) -> Result<(), Error>>(
&mut self, &mut self,
path: &mut PathBuf, // user for error reporting path: &mut PathBuf, // user for error reporting
@ -179,34 +253,37 @@ impl <'a, R: Read + Seek> CaTarDecoder<'a, R> {
Err(err) => bail!("unable to open directory {:?} - {}", path, err), Err(err) => bail!("unable to open directory {:?} - {}", path, err),
}; };
//fixme: restore permission, acls, xattr, ... let mut head = self.restore_attributes(&entry)?;
loop { while head.htype == CA_FORMAT_FILENAME {
let head: CaFormatHeader = self.read_item()?; let name = self.read_filename(head.size)?;
match head.htype { path.push(&name);
CA_FORMAT_FILENAME => { println!("NAME: {:?}", path);
let name = self.read_filename(head.size)?; self.restore_sequential(path, &name, &dir, callback)?;
path.push(&name); path.pop();
println!("NAME: {:?}", path);
self.restore_sequential(path, &name, &dir, callback)?; head = self.read_item()?;
path.pop();
}
CA_FORMAT_GOODBYE => {
println!("Skip Goodbye");
if head.size < HEADER_SIZE { bail!("detected short goodbye table"); }
self.reader.seek(SeekFrom::Current((head.size - HEADER_SIZE) as i64))?;
return Ok(());
}
_ => {
bail!("got unknown header type inside directory entry {:016x}", head.htype);
}
}
} }
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.reader.seek(SeekFrom::Current((head.size - HEADER_SIZE) as i64))?;
self.restore_mode(&entry, dir.as_raw_fd())?;
self.restore_mtime(&entry, dir.as_raw_fd())?;
self.restore_ugid(&entry, dir.as_raw_fd())?;
return Ok(());
} }
if ifmt == libc::S_IFLNK { if ifmt == libc::S_IFLNK {
// fixme: create symlink // fixme: create symlink
//fixme: restore permission, acls, xattr, ... //fixme: restore permission, acls, xattr, ...
let head: CaFormatHeader = self.read_item()?; let head: CaFormatHeader = self.read_item()?;
match head.htype { match head.htype {
CA_FORMAT_SYMLINK => { CA_FORMAT_SYMLINK => {
@ -223,6 +300,11 @@ impl <'a, R: Read + Seek> CaTarDecoder<'a, R> {
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
self.restore_ugid_at(&entry, parent_fd, filename)?;
self.restore_mtime_at(&entry, parent_fd, filename)?;
return Ok(()); return Ok(());
} }
@ -238,34 +320,32 @@ impl <'a, R: Read + Seek> CaTarDecoder<'a, R> {
Err(err) => bail!("open file {:?} failed - {}", path, err), Err(err) => bail!("open file {:?} failed - {}", path, err),
}; };
//fixme: restore permission, acls, xattr, ... let mut head = self.restore_attributes(&entry)?;
let head: CaFormatHeader = self.read_item()?; if head.htype != CA_FORMAT_PAYLOAD {
match head.htype { bail!("got unknown header type for file entry {:016x}", head.htype);
CA_FORMAT_PAYLOAD => {
if head.size < HEADER_SIZE {
bail!("detected short payload");
}
let need = (head.size - HEADER_SIZE) as usize;
//self.reader.seek(SeekFrom::Current(need as i64))?;
// fixme:: create file
let mut done = 0;
while (done < need) {
let todo = need - done;
let n = if todo > read_buffer.len() { read_buffer.len() } else { todo };
let data = &mut read_buffer[..n];
self.reader.read_exact(data)?;
file.write_all(data)?;
done += n;
}
}
_ => {
bail!("got unknown header type for file entry {:016x}", head.htype);
}
} }
if head.size < HEADER_SIZE {
bail!("detected short payload");
}
let need = (head.size - HEADER_SIZE) as usize;
//self.reader.seek(SeekFrom::Current(need as i64))?;
let mut done = 0;
while (done < need) {
let todo = need - done;
let n = if todo > read_buffer.len() { read_buffer.len() } else { todo };
let data = &mut read_buffer[..n];
self.reader.read_exact(data)?;
file.write_all(data)?;
done += n;
}
self.restore_mode(&entry, file.as_raw_fd())?;
self.restore_mtime(&entry, file.as_raw_fd())?;
self.restore_ugid(&entry, file.as_raw_fd())?;
return Ok(()); return Ok(());
} }
@ -465,3 +545,20 @@ fn symlinkat(target: &Path, parent: RawFd, linkname: &OsStr) -> Result<(), Error
})? })?
})? })?
} }
fn nsec_to_update_timespec(mtime_nsec: u64) -> [libc::timespec; 2] {
// restore mtime
const UTIME_OMIT: i64 = ((1 << 30) - 2);
const NANOS_PER_SEC: i64 = 1_000_000_000;
let sec = (mtime_nsec as i64) / NANOS_PER_SEC;
let nsec = (mtime_nsec as i64) % NANOS_PER_SEC;
let times: [libc::timespec; 2] = [
libc::timespec { tv_sec: 0, tv_nsec: UTIME_OMIT },
libc::timespec { tv_sec: sec, tv_nsec: nsec },
];
times
}