From a7e3713122b340247c02f510a4936fd6dd36bc9e Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Fri, 11 Jan 2019 12:22:00 +0100 Subject: [PATCH] catar: encode/decode devices --- src/catar/decoder.rs | 47 ++++++++++++++++++++++++++++++---- src/catar/encoder.rs | 27 ++++++++++++++++--- src/catar/format_definition.rs | 9 +++++++ 3 files changed, 75 insertions(+), 8 deletions(-) diff --git a/src/catar/decoder.rs b/src/catar/decoder.rs index 877d85b1..33585988 100644 --- a/src/catar/decoder.rs +++ b/src/catar/decoder.rs @@ -120,7 +120,7 @@ impl <'a, R: Read + Seek> CaTarDecoder<'a, R> { 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."); } @@ -178,7 +178,9 @@ impl <'a, R: Read + Seek> CaTarDecoder<'a, R> { 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(()) } @@ -229,6 +231,17 @@ impl <'a, R: Read + Seek> CaTarDecoder<'a, R> { 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 Result<(), Error>>( &mut self, path: &mut PathBuf, // user for error reporting @@ -306,6 +319,26 @@ impl <'a, R: Read + Seek> CaTarDecoder<'a, R> { 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 { 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 ifmt = mode & libc::S_IFMT; + let osstr: &OsStr = prefix.as_ref(); output.write(osstr.as_bytes())?; output.write(b"\n")?; - if (mode & libc::S_IFMT) == libc::S_IFDIR { + if ifmt == libc::S_IFDIR { self.print_filenames(output, prefix, item)?; - } else if (mode & libc::S_IFMT) == libc::S_IFREG { - } else if (mode & libc::S_IFMT) == libc::S_IFLNK { + } else if ifmt == libc::S_IFREG { + } else if ifmt == libc::S_IFLNK { + } else if ifmt == libc::S_IFBLK { + } else if ifmt == libc::S_IFCHR { } else { bail!("unknown item mode/type for {:?}", prefix); } diff --git a/src/catar/encoder.rs b/src/catar/encoder.rs index 447b323a..775a6390 100644 --- a/src/catar/encoder.rs +++ b/src/catar/encoder.rs @@ -280,7 +280,9 @@ impl <'a, W: Write> CaTarEncoder<'a, W> { 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()) { 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), } - } 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()) { Ok(filefd) => { 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(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 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(err) => bail!("readlink {:?} failed - {}", self.current_path, err), } + } else if (ifmt == libc::S_IFBLK) || (ifmt == libc::S_IFCHR) { + self.encode_device(&stat)?; } else { bail!("unsupported file type (mode {:o} {:?})", stat.st_mode, self.current_path); } @@ -390,6 +394,23 @@ impl <'a, W: Write> CaTarEncoder<'a, W> { 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::() as u64)?; + self.write_item(CaFormatDevice { major, minor })?; + + Ok(()) + } + fn encode_symlink(&mut self, target: &[u8], stat: &FileStat) -> Result<(), Error> { //println!("encode_symlink: {:?} -> {:?}", self.current_path, target); diff --git a/src/catar/format_definition.rs b/src/catar/format_definition.rs index cabdd9c3..c7889458 100644 --- a/src/catar/format_definition.rs +++ b/src/catar/format_definition.rs @@ -13,6 +13,8 @@ use siphasher::sip::SipHasher24; pub const CA_FORMAT_ENTRY: u64 = 0x1396fabcea5bbb51; pub const CA_FORMAT_FILENAME: u64 = 0x6dbb6ebcb3161f0b; 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_GOODBYE: u64 = 0xdfd35c5e8327c403; @@ -95,6 +97,13 @@ pub struct CaFormatEntry { pub mtime: u64, } +#[derive(Endian)] +#[repr(C)] +pub struct CaFormatDevice { + pub major: u64, + pub minor: u64, +} + #[derive(Endian)] #[repr(C)] pub struct CaFormatGoodbyeItem {