diff --git a/src/catar/encoder.rs b/src/catar/encoder.rs index ddb19d65..8ab8c6d4 100644 --- a/src/catar/encoder.rs +++ b/src/catar/encoder.rs @@ -11,6 +11,7 @@ use std::path::{Path, PathBuf}; use nix::fcntl::OFlag; use nix::sys::stat::Mode; use nix::errno::Errno; +use nix::sys::stat::FileStat; pub struct CaTarEncoder { current_path: PathBuf, // used for error reporting @@ -34,7 +35,48 @@ impl CaTarEncoder { Ok(()) } - //fn report_vanished + fn write_header(&mut self, htype: u64, size: u64) -> Result<(), Error> { + + let mut buffer = [0u8; std::mem::size_of::()]; + let mut header = crate::tools::map_struct_mut::(&mut buffer)?; + header.size = u64::to_le((std::mem::size_of::() as u64) + size); + header.htype = htype; + + self.writer.write(&buffer)?; + + Ok(()) + } + + fn write_entry(&mut self, stat: &FileStat) -> Result<(), Error> { + + let mut buffer = [0u8; std::mem::size_of::() + std::mem::size_of::()]; + let mut header = crate::tools::map_struct_mut::(&mut buffer)?; + header.size = u64::to_le((std::mem::size_of::() + std::mem::size_of::()) as u64); + header.htype = CA_FORMAT_ENTRY; + + let mut entry = crate::tools::map_struct_mut::(&mut buffer[std::mem::size_of::()..])?; + + entry.feature_flags = 0; // fixme ?? + + if (stat.st_mode & libc::S_IFMT) == libc::S_IFLNK { + entry.mode = u64::to_le((libc::S_IFLNK | 0o777) as u64); + } else { + let mode = stat.st_mode & (libc::S_IFMT | 0o7777); + entry.mode = u64::to_le(mode as u64); + } + + entry.flags = 0; // todo: CHATTR, FAT_ATTRS, subvolume? + + entry.uid = u64::to_le(stat.st_uid as u64); + entry.gid = u64::to_le(stat.st_gid as u64); + + let mtime = stat.st_mtime * 1_000_000_000 + stat.st_mtime_nsec; + if mtime > 0 { entry.mtime = mtime as u64 }; + + self.writer.write(&buffer)?; + + Ok(()) + } fn encode_dir(&mut self, dir: &mut nix::dir::Dir) -> Result<(), Error> { @@ -49,6 +91,8 @@ impl CaTarEncoder { Err(err) => bail!("fstat {:?} failed - {}", self.current_path, err), }; + self.write_entry(&dir_stat)?; + for entry in dir.iter() { let entry = match entry { Ok(entry) => entry, @@ -81,7 +125,7 @@ impl CaTarEncoder { Err(nix::Error::Sys(Errno::ENOENT)) => self.report_vanished_file(&self.current_path)?, Err(err) => bail!("open dir {:?} failed - {}", self.current_path, err), } - + } else if (stat.st_mode & libc::S_IFMT) == libc::S_IFREG { match nix::fcntl::openat(rawfd, filename.as_ref(), OFlag::O_NOFOLLOW, Mode::empty()) { Ok(filefd) => { @@ -94,8 +138,14 @@ impl CaTarEncoder { } } else if (stat.st_mode & libc::S_IFMT) == libc::S_IFLNK { let mut buffer = [0u8; libc::PATH_MAX as usize]; - match nix::fcntl::readlinkat(rawfd, filename.as_ref(), &mut buffer) { - Ok(target) => self.encode_symlink(&target)?, + + use nix::NixPath; + let res = filename.with_nix_path(|cstr| { + unsafe { libc::readlink(cstr.as_ptr(), buffer.as_mut_ptr() as *mut libc::c_char, buffer.len()) } + })?; + + match Errno::result(res) { + Ok(len) => self.encode_symlink(&buffer[..(len as usize)], &stat)?, Err(nix::Error::Sys(Errno::ENOENT)) => self.report_vanished_file(&self.current_path)?, Err(err) => bail!("readlink {:?} failed - {}", self.current_path, err), } @@ -113,18 +163,25 @@ impl CaTarEncoder { println!("encode_file: {:?}", self.current_path); + self.write_entry(stat)?; + Ok(()) } - fn encode_symlink(&mut self, target: &std::ffi::OsStr) -> Result<(), Error> { + fn encode_symlink(&mut self, target: &[u8], stat: &FileStat) -> Result<(), Error> { println!("encode_symlink: {:?} -> {:?}", self.current_path, target); + self.write_entry(stat)?; + + self.write_header(CA_FORMAT_SYMLINK, target.len() as u64); + self.writer.write(target)?; + Ok(()) } // the report_XXX method may raise and error - depending on encoder configuration - + fn report_vanished_file(&self, path: &Path) -> Result<(), Error> { eprintln!("WARNING: detected vanished file {:?}", path); diff --git a/src/catar/format_definition.rs b/src/catar/format_definition.rs index bd52965b..7ffde206 100644 --- a/src/catar/format_definition.rs +++ b/src/catar/format_definition.rs @@ -1,33 +1,34 @@ use failure::*; -const CA_FORMAT_ENTRY: u64 = 0x1396fabcea5bbb51; -const CA_FORMAT_FILENAME: u64 = 0x6dbb6ebcb3161f0b; - -const CA_FORMAT_GOODBYE: u64 = 0xdfd35c5e8327c403; +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_GOODBYE: u64 = 0xdfd35c5e8327c403; /* The end marker used in the GOODBYE object */ -const CA_FORMAT_GOODBYE_TAIL_MARKER: u64 = 0x57446fa533702943; +pub const CA_FORMAT_GOODBYE_TAIL_MARKER: u64 = 0x57446fa533702943; #[repr(C)] pub struct CaFormatHeader { - size: u64, - htype: u64, + pub size: u64, + pub htype: u64, } #[repr(C)] pub struct CaFormatEntry { - feature_flags: u64, - mode: u64, - flags: u64, - uid: u64, - gid: u64, - mtime: u64, + pub feature_flags: u64, + pub mode: u64, + pub flags: u64, + pub uid: u64, + pub gid: u64, + pub mtime: u64, } #[repr(C)] pub struct CaFormatGoodbyeItem { - offset: u64, - size: u64, - hash: u64, + pub offset: u64, + pub size: u64, + pub hash: u64, } fn read_os_string(buffer: &[u8]) -> std::ffi::OsString {