src/catar/encoder.rs: write entry and symlink data
This commit is contained in:
		| @ -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<W: Write> { | ||||
|     current_path: PathBuf, // used for error reporting | ||||
| @ -34,7 +35,48 @@ impl <W: Write> CaTarEncoder<W> { | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     //fn report_vanished | ||||
|     fn write_header(&mut self, htype: u64, size: u64) -> Result<(), Error> { | ||||
|  | ||||
|         let mut buffer = [0u8; std::mem::size_of::<CaFormatHeader>()]; | ||||
|         let mut header = crate::tools::map_struct_mut::<CaFormatHeader>(&mut buffer)?; | ||||
|         header.size = u64::to_le((std::mem::size_of::<CaFormatHeader>() 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::<CaFormatHeader>() + std::mem::size_of::<CaFormatEntry>()]; | ||||
|         let mut header = crate::tools::map_struct_mut::<CaFormatHeader>(&mut buffer)?; | ||||
|         header.size = u64::to_le((std::mem::size_of::<CaFormatHeader>() + std::mem::size_of::<CaFormatEntry>()) as u64); | ||||
|         header.htype = CA_FORMAT_ENTRY; | ||||
|  | ||||
|         let mut entry = crate::tools::map_struct_mut::<CaFormatEntry>(&mut buffer[std::mem::size_of::<CaFormatHeader>()..])?; | ||||
|  | ||||
|         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 <W: Write> CaTarEncoder<W> { | ||||
|             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 <W: Write> CaTarEncoder<W> { | ||||
|                     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 <W: Write> CaTarEncoder<W> { | ||||
|                 } | ||||
|             } 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 <W: Write> CaTarEncoder<W> { | ||||
|  | ||||
|         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); | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
		Reference in New Issue
	
	Block a user