catar: encode/decode devices

This commit is contained in:
Dietmar Maurer 2019-01-11 12:22:00 +01:00
parent 1bdb3130f7
commit a7e3713122
3 changed files with 75 additions and 8 deletions

View File

@ -120,7 +120,7 @@ impl <'a, R: Read + Seek> CaTarDecoder<'a, R> {
bail!("filename entry not nul terminated."); bail!("filename entry not nul terminated.");
} }
if buffer.iter().find(|b| (**b == b'/') || (**b == b'\\')).is_some() { if buffer.iter().find(|b| (**b == b'/')).is_some() {
bail!("found invalid filename with slashes."); bail!("found invalid filename with slashes.");
} }
@ -178,7 +178,9 @@ impl <'a, R: Read + Seek> CaTarDecoder<'a, R> {
let mode = Mode::from_bits_truncate((entry.mode as u32) & 0o7777); let mode = Mode::from_bits_truncate((entry.mode as u32) & 0o7777);
nix::sys::stat::fchmodat(Some(dirfd), filename, mode, nix::sys::stat::FchmodatFlags::NoFollowSymlink)?; // NOTE: we want :FchmodatFlags::NoFollowSymlink, but fchmodat does not support that
// on linux (see man fchmodat). Fortunately, we can simply avoid calling this on symlinks.
nix::sys::stat::fchmodat(Some(dirfd), filename, mode, nix::sys::stat::FchmodatFlags::FollowSymlink)?;
Ok(()) Ok(())
} }
@ -229,6 +231,17 @@ impl <'a, R: Read + Seek> CaTarDecoder<'a, R> {
Ok(()) Ok(())
} }
fn restore_device_at(&mut self, entry: &CaFormatEntry, dirfd: RawFd, filename: &OsStr, device: &CaFormatDevice) -> Result<(), Error> {
let rdev = nix::sys::stat::makedev(device.major, device.minor);
let res = filename.with_nix_path(|cstr| unsafe {
libc::mknodat(dirfd, cstr.as_ptr(), 0o0600, rdev)
})?;
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
@ -306,6 +319,26 @@ impl <'a, R: Read + Seek> CaTarDecoder<'a, R> {
return Ok(()); return Ok(());
} }
if (ifmt == libc::S_IFBLK) || (ifmt == libc::S_IFCHR) {
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)?;
}
_ => {
bail!("got unknown header type inside device entry {:016x}", head.htype);
}
}
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(());
}
if ifmt == libc::S_IFREG { if ifmt == libc::S_IFREG {
let mut read_buffer: [u8; 64*1024] = unsafe { std::mem::uninitialized() }; let mut read_buffer: [u8; 64*1024] = unsafe { std::mem::uninitialized() };
@ -490,14 +523,18 @@ impl <'a, R: Read + Seek> CaTarDecoder<'a, R> {
let mode = item.entry.mode as u32; let mode = item.entry.mode as u32;
let ifmt = mode & libc::S_IFMT;
let osstr: &OsStr = prefix.as_ref(); let osstr: &OsStr = prefix.as_ref();
output.write(osstr.as_bytes())?; output.write(osstr.as_bytes())?;
output.write(b"\n")?; output.write(b"\n")?;
if (mode & libc::S_IFMT) == libc::S_IFDIR { if ifmt == libc::S_IFDIR {
self.print_filenames(output, prefix, item)?; self.print_filenames(output, prefix, item)?;
} else if (mode & libc::S_IFMT) == libc::S_IFREG { } else if ifmt == libc::S_IFREG {
} else if (mode & libc::S_IFMT) == libc::S_IFLNK { } else if ifmt == libc::S_IFLNK {
} else if ifmt == libc::S_IFBLK {
} else if ifmt == libc::S_IFCHR {
} else { } else {
bail!("unknown item mode/type for {:?}", prefix); bail!("unknown item mode/type for {:?}", prefix);
} }

View File

@ -280,7 +280,9 @@ impl <'a, W: Write> CaTarEncoder<'a, W> {
self.write_filename(&filename)?; self.write_filename(&filename)?;
if (stat.st_mode & libc::S_IFMT) == libc::S_IFDIR { let ifmt = stat.st_mode & libc::S_IFMT;
if ifmt == libc::S_IFDIR {
match nix::dir::Dir::openat(rawfd, filename.as_ref(), OFlag::O_NOFOLLOW, Mode::empty()) { match nix::dir::Dir::openat(rawfd, filename.as_ref(), OFlag::O_NOFOLLOW, Mode::empty()) {
Ok(mut dir) => self.encode_dir(&mut dir, &stat)?, Ok(mut dir) => self.encode_dir(&mut dir, &stat)?,
@ -288,7 +290,7 @@ impl <'a, W: Write> CaTarEncoder<'a, W> {
Err(err) => bail!("open dir {:?} failed - {}", self.current_path, err), Err(err) => bail!("open dir {:?} failed - {}", self.current_path, err),
} }
} else if (stat.st_mode & libc::S_IFMT) == libc::S_IFREG { } else if ifmt == libc::S_IFREG {
match nix::fcntl::openat(rawfd, filename.as_ref(), OFlag::O_NOFOLLOW, Mode::empty()) { match nix::fcntl::openat(rawfd, filename.as_ref(), OFlag::O_NOFOLLOW, Mode::empty()) {
Ok(filefd) => { Ok(filefd) => {
let res = self.encode_file(filefd, &stat); let res = self.encode_file(filefd, &stat);
@ -298,7 +300,7 @@ impl <'a, W: Write> CaTarEncoder<'a, W> {
Err(nix::Error::Sys(Errno::ENOENT)) => self.report_vanished_file(&self.current_path)?, Err(nix::Error::Sys(Errno::ENOENT)) => self.report_vanished_file(&self.current_path)?,
Err(err) => bail!("open file {:?} failed - {}", self.current_path, err), Err(err) => bail!("open file {:?} failed - {}", self.current_path, err),
} }
} else if (stat.st_mode & libc::S_IFMT) == libc::S_IFLNK { } else if ifmt == libc::S_IFLNK {
let mut buffer = [0u8; libc::PATH_MAX as usize]; let mut buffer = [0u8; libc::PATH_MAX as usize];
let res = filename.with_nix_path(|cstr| { let res = filename.with_nix_path(|cstr| {
@ -313,6 +315,8 @@ impl <'a, W: Write> CaTarEncoder<'a, W> {
Err(nix::Error::Sys(Errno::ENOENT)) => self.report_vanished_file(&self.current_path)?, Err(nix::Error::Sys(Errno::ENOENT)) => self.report_vanished_file(&self.current_path)?,
Err(err) => bail!("readlink {:?} failed - {}", self.current_path, err), Err(err) => bail!("readlink {:?} failed - {}", self.current_path, err),
} }
} else if (ifmt == libc::S_IFBLK) || (ifmt == libc::S_IFCHR) {
self.encode_device(&stat)?;
} else { } else {
bail!("unsupported file type (mode {:o} {:?})", stat.st_mode, self.current_path); bail!("unsupported file type (mode {:o} {:?})", stat.st_mode, self.current_path);
} }
@ -390,6 +394,23 @@ impl <'a, W: Write> CaTarEncoder<'a, W> {
Ok(()) Ok(())
} }
fn encode_device(&mut self, stat: &FileStat) -> Result<(), Error> {
let mut entry = self.create_entry(&stat)?;
self.write_entry(entry)?;
let major = unsafe { libc::major(stat.st_rdev) } as u64;
let minor = unsafe { libc::minor(stat.st_rdev) } as u64;
println!("encode_device: {:?} {} {} {}", self.current_path, stat.st_rdev, major, minor);
self.write_header(CA_FORMAT_DEVICE, std::mem::size_of::<CaFormatDevice>() as u64)?;
self.write_item(CaFormatDevice { major, minor })?;
Ok(())
}
fn encode_symlink(&mut self, target: &[u8], stat: &FileStat) -> Result<(), Error> { fn encode_symlink(&mut self, target: &[u8], stat: &FileStat) -> Result<(), Error> {
//println!("encode_symlink: {:?} -> {:?}", self.current_path, target); //println!("encode_symlink: {:?} -> {:?}", self.current_path, target);

View File

@ -13,6 +13,8 @@ use siphasher::sip::SipHasher24;
pub const CA_FORMAT_ENTRY: u64 = 0x1396fabcea5bbb51; pub const CA_FORMAT_ENTRY: u64 = 0x1396fabcea5bbb51;
pub const CA_FORMAT_FILENAME: u64 = 0x6dbb6ebcb3161f0b; pub const CA_FORMAT_FILENAME: u64 = 0x6dbb6ebcb3161f0b;
pub const CA_FORMAT_SYMLINK: u64 = 0x664a6fb6830e0d6c; pub const CA_FORMAT_SYMLINK: u64 = 0x664a6fb6830e0d6c;
pub const CA_FORMAT_DEVICE: u64 = 0xac3dace369dfe643;
pub const CA_FORMAT_PAYLOAD: u64 = 0x8b9e1d93d6dcffc9; pub const CA_FORMAT_PAYLOAD: u64 = 0x8b9e1d93d6dcffc9;
pub const CA_FORMAT_GOODBYE: u64 = 0xdfd35c5e8327c403; pub const CA_FORMAT_GOODBYE: u64 = 0xdfd35c5e8327c403;
@ -95,6 +97,13 @@ pub struct CaFormatEntry {
pub mtime: u64, pub mtime: u64,
} }
#[derive(Endian)]
#[repr(C)]
pub struct CaFormatDevice {
pub major: u64,
pub minor: u64,
}
#[derive(Endian)] #[derive(Endian)]
#[repr(C)] #[repr(C)]
pub struct CaFormatGoodbyeItem { pub struct CaFormatGoodbyeItem {