diff --git a/src/checksum.rs b/src/checksum.rs new file mode 100644 index 0000000..8ba684d --- /dev/null +++ b/src/checksum.rs @@ -0,0 +1,56 @@ +use { + crate::Error, + std::io::{Read, Write}, +}; + +#[derive(Debug)] +pub enum Checksum { + Md5([u8; 16]), + Sha1([u8; 20]), + Sha256([u8; 32]), + Skip, +} + +impl Checksum { + pub fn read(reader: &mut T) -> Result { + let mut buf = [0; 1]; + reader.read_exact(&mut buf)?; + match buf[0] { + 0 => { + let mut sum = [0; 16]; + reader.read_exact(&mut sum)?; + Ok(Self::Md5(sum)) + } + 1 => { + let mut sum = [0; 20]; + reader.read_exact(&mut sum)?; + Ok(Self::Sha1(sum)) + } + 2 => { + let mut sum = [0; 32]; + reader.read_exact(&mut sum)?; + Ok(Self::Sha256(sum)) + } + _ => Ok(Self::Skip), + } + } + + pub fn write(&self, writer: &mut T) -> Result<(), Error> { + match self { + Self::Md5(sum) => { + writer.write_all(&[b'0'])?; + writer.write_all(&sum[..])?; + } + Self::Sha1(sum) => { + writer.write_all(&[b'1'])?; + writer.write_all(&sum[..])?; + } + Self::Sha256(sum) => { + writer.write_all(&[b'2'])?; + writer.write_all(&sum[..])?; + } + Self::Skip => writer.write_all(&[b'3'])?, + } + Ok(()) + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..bd5d703 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,66 @@ +use std::{array::TryFromSliceError, fmt, io, num::TryFromIntError, string::FromUtf8Error}; + +#[derive(Debug)] +pub enum Error { + Int(TryFromIntError), + Io(io::Error), + Utf8(FromUtf8Error), + Slice(TryFromSliceError), + InvalidChecksum, + MissingData, + UnexpectedData, + UnknownFileType, + Other(String), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Int(e) => write!(f, "{e}"), + Self::Utf8(e) => write!(f, "{e}"), + Self::Slice(e) => write!(f, "{e}"), + Self::Io(e) => write!(f, "{e}"), + Self::InvalidChecksum => write!(f, "invalid checksum"), + Self::MissingData => write!(f, "missing data"), + Self::UnexpectedData => write!(f, "unexpected data"), + Self::UnknownFileType => write!(f, "unknown file type"), + Self::Other(s) => write!(f, "{s}"), + } + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::Int(e) => Some(e), + Self::Io(e) => Some(e), + Self::Utf8(e) => Some(e), + Self::Slice(e) => Some(e), + _ => None, + } + } +} + +impl From for Error { + fn from(value: TryFromIntError) -> Self { + Self::Int(value) + } +} + +impl From for Error { + fn from(value: io::Error) -> Self { + Self::Io(value) + } +} + +impl From for Error { + fn from(value: FromUtf8Error) -> Self { + Self::Utf8(value) + } +} + +impl From for Error { + fn from(value: TryFromSliceError) -> Self { + Self::Slice(value) + } +} diff --git a/src/file.rs b/src/file.rs new file mode 100644 index 0000000..3cbb7f6 --- /dev/null +++ b/src/file.rs @@ -0,0 +1,82 @@ +use { + crate::{Checksum, Error}, + md5::{Digest, Md5}, + sha1::Sha1, + sha2::Sha256, + std::io::{Read, Write}, +}; + +#[derive(Debug)] +pub struct File { + pub len: u64, + pub checksum: Checksum, + pub data: Vec, +} + +impl File { + pub fn read(reader: &mut T) -> Result { + let mut len = [0; 8]; + reader.read_exact(&mut len)?; + let len = u64::from_le_bytes(len); + let checksum = Checksum::read(reader)?; + let mut data = Vec::with_capacity(len.try_into()?); + let mut handle = reader.take(len); + handle.read_exact(&mut data)?; + match checksum { + Checksum::Md5(sum) => { + let mut hasher = Md5::new(); + hasher.update(&data); + let res = hasher.finalize(); + if res == sum.into() { + Ok(Self { + len, + checksum, + data, + }) + } else { + Err(Error::InvalidChecksum) + } + } + Checksum::Sha1(sum) => { + let mut hasher = Sha1::new(); + hasher.update(&data); + let res = hasher.finalize(); + if res == sum.into() { + Ok(Self { + len, + checksum, + data, + }) + } else { + Err(Error::InvalidChecksum) + } + } + Checksum::Sha256(sum) => { + let mut hasher = Sha256::new(); + hasher.update(&data); + let res = hasher.finalize(); + if res == sum.into() { + Ok(Self { + len, + checksum, + data, + }) + } else { + Err(Error::InvalidChecksum) + } + } + _ => Ok(Self { + len, + checksum, + data, + }), + } + } + + pub fn write(&self, writer: &mut T) -> Result<(), Error> { + writer.write_all(&self.len.to_be_bytes())?; + Checksum::write(&self.checksum, writer)?; + writer.write_all(&self.data)?; + Ok(()) + } +} diff --git a/src/filetype.rs b/src/filetype.rs new file mode 100644 index 0000000..4270f23 --- /dev/null +++ b/src/filetype.rs @@ -0,0 +1,92 @@ +use { + crate::{Error, File, Special}, + std::io::{Read, Write}, +}; + +#[repr(u8)] +#[derive(Debug)] +pub enum FileType { + Normal(File), + HardLink(String), + SoftLink(String), + Directory, + Character(Special), + Block(Special), + Fifo, +} + +impl FileType { + pub fn read(reader: &mut T) -> Result { + let mut buf = [0; 1]; + reader.read_exact(&mut buf)?; + match buf[0] { + 0 => { + let file = File::read(reader)?; + Ok(Self::Normal(file)) + } + 1 => { + let mut len = [0; 8]; + reader.read_exact(&mut len)?; + let len = u64::from_le_bytes(len); + let mut buf = Vec::with_capacity(len.try_into()?); + let mut handle = reader.take(len); + handle.read_exact(&mut buf)?; + let s = String::from_utf8(buf)?; + Ok(Self::HardLink(s)) + } + 2 => { + let mut len = [0; 8]; + reader.read_exact(&mut len)?; + let len = u64::from_le_bytes(len); + let mut buf = Vec::with_capacity(len.try_into()?); + let mut handle = reader.take(len); + handle.read_exact(&mut buf)?; + let s = String::from_utf8(buf)?; + Ok(Self::SoftLink(s)) + } + 3 => Ok(Self::Directory), + 4 => { + let sp = Special::read(reader)?; + Ok(Self::Character(sp)) + } + 5 => { + let sp = Special::read(reader)?; + Ok(Self::Block(sp)) + } + 6 => Ok(Self::Fifo), + _ => Err(Error::UnknownFileType), + } + } + + pub fn write(&self, writer: &mut T) -> Result<(), Error> { + match self { + Self::Normal(f) => { + writer.write_all(&[0])?; + f.write(writer)?; + } + Self::HardLink(s) => { + writer.write_all(&[1])?; + let len = s.len() as u64; + writer.write_all(&len.to_le_bytes())?; + writer.write_all(s.as_bytes())?; + } + Self::SoftLink(s) => { + writer.write_all(&[2])?; + let len = s.len() as u64; + writer.write_all(&len.to_le_bytes())?; + writer.write_all(s.as_bytes())?; + } + Self::Directory => writer.write_all(&[3])?, + Self::Character(s) => { + writer.write_all(&[4])?; + s.write(writer)?; + } + Self::Block(s) => { + writer.write_all(&[5])?; + s.write(writer)?; + } + Self::Fifo => writer.write_all(&[6])?, + } + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 8586214..d053d01 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,490 +1,9 @@ -use { - md5::{Digest, Md5}, - sha1::Sha1, - sha2::Sha256, - std::{ - array::TryFromSliceError, - collections::HashMap, - fmt, fs, - io::{self, BufReader, Read, Write}, - num::TryFromIntError, - os::unix::fs::MetadataExt, - string::FromUtf8Error, - sync::Mutex, - }, +mod checksum; +mod error; +mod file; +mod filetype; +mod node; +mod special; +pub use { + checksum::Checksum, error::Error, file::File, filetype::FileType, node::Node, special::Special, }; - -#[derive(Debug)] -pub enum Error { - Int(TryFromIntError), - Io(io::Error), - Utf8(FromUtf8Error), - Slice(TryFromSliceError), - InvalidChecksum, - MissingData, - UnexpectedData, - UnknownFileType, - Other(String), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Int(e) => write!(f, "{e}"), - Self::Utf8(e) => write!(f, "{e}"), - Self::Slice(e) => write!(f, "{e}"), - Self::Io(e) => write!(f, "{e}"), - Self::InvalidChecksum => write!(f, "invalid checksum"), - Self::MissingData => write!(f, "missing data"), - Self::UnexpectedData => write!(f, "unexpected data"), - Self::UnknownFileType => write!(f, "unknown file type"), - Self::Other(s) => write!(f, "{s}"), - } - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Self::Int(e) => Some(e), - Self::Io(e) => Some(e), - Self::Utf8(e) => Some(e), - Self::Slice(e) => Some(e), - _ => None, - } - } -} - -impl From for Error { - fn from(value: TryFromIntError) -> Self { - Self::Int(value) - } -} - -impl From for Error { - fn from(value: io::Error) -> Self { - Self::Io(value) - } -} - -impl From for Error { - fn from(value: FromUtf8Error) -> Self { - Self::Utf8(value) - } -} - -impl From for Error { - fn from(value: TryFromSliceError) -> Self { - Self::Slice(value) - } -} - -#[derive(Debug)] -pub struct Special { - pub major: u32, - pub minor: u32, -} - -impl Special { - pub fn read(reader: &mut T) -> Result { - let mut buf = [0; 8]; - reader.read_exact(&mut buf)?; - let major: [u8; 4] = buf[..4].try_into()?; - let major = u32::from_le_bytes(major); - let minor: [u8; 4] = buf[4..].try_into()?; - let minor = u32::from_le_bytes(minor); - Ok(Self { major, minor }) - } - - pub fn write(&self, writer: &mut T) -> Result<(), Error> { - writer.write_all(&self.major.to_le_bytes())?; - writer.write_all(&self.minor.to_le_bytes())?; - Ok(()) - } - - fn from_rdev(rdev: u32) -> Self { - Self { - major: rdev >> 8, - minor: rdev & 0xff, - } - } -} - -#[derive(Debug)] -pub enum Checksum { - Md5([u8; 16]), - Sha1([u8; 20]), - Sha256([u8; 32]), - Skip, -} - -impl Checksum { - pub fn read(reader: &mut T) -> Result { - let mut buf = [0; 1]; - reader.read_exact(&mut buf)?; - match buf[0] { - 0 => { - let mut sum = [0; 16]; - reader.read_exact(&mut sum)?; - Ok(Self::Md5(sum)) - } - 1 => { - let mut sum = [0; 20]; - reader.read_exact(&mut sum)?; - Ok(Self::Sha1(sum)) - } - 2 => { - let mut sum = [0; 32]; - reader.read_exact(&mut sum)?; - Ok(Self::Sha256(sum)) - } - _ => Ok(Self::Skip), - } - } - - pub fn write(&self, writer: &mut T) -> Result<(), Error> { - match self { - Self::Md5(sum) => { - writer.write_all(&[b'0'])?; - writer.write_all(&sum[..])?; - } - Self::Sha1(sum) => { - writer.write_all(&[b'1'])?; - writer.write_all(&sum[..])?; - } - Self::Sha256(sum) => { - writer.write_all(&[b'2'])?; - writer.write_all(&sum[..])?; - } - Self::Skip => writer.write_all(&[b'3'])?, - } - Ok(()) - } -} - -#[derive(Debug)] -pub struct File { - pub len: u64, - pub checksum: Checksum, - pub data: Vec, -} - -impl File { - pub fn read(reader: &mut T) -> Result { - let mut len = [0; 8]; - reader.read_exact(&mut len)?; - let len = u64::from_le_bytes(len); - let checksum = Checksum::read(reader)?; - let mut data = Vec::with_capacity(len.try_into()?); - let mut handle = reader.take(len); - handle.read_exact(&mut data)?; - match checksum { - Checksum::Md5(sum) => { - let mut hasher = Md5::new(); - hasher.update(&data); - let res = hasher.finalize(); - if res == sum.into() { - Ok(Self { - len, - checksum, - data, - }) - } else { - Err(Error::InvalidChecksum) - } - } - Checksum::Sha1(sum) => { - let mut hasher = Sha1::new(); - hasher.update(&data); - let res = hasher.finalize(); - if res == sum.into() { - Ok(Self { - len, - checksum, - data, - }) - } else { - Err(Error::InvalidChecksum) - } - } - Checksum::Sha256(sum) => { - let mut hasher = Sha256::new(); - hasher.update(&data); - let res = hasher.finalize(); - if res == sum.into() { - Ok(Self { - len, - checksum, - data, - }) - } else { - Err(Error::InvalidChecksum) - } - } - _ => Ok(Self { - len, - checksum, - data, - }), - } - } - - pub fn write(&self, writer: &mut T) -> Result<(), Error> { - writer.write_all(&self.len.to_be_bytes())?; - Checksum::write(&self.checksum, writer)?; - writer.write_all(&self.data)?; - Ok(()) - } -} - -#[repr(u8)] -#[derive(Debug)] -pub enum FileType { - Normal(File), - HardLink(String), - SoftLink(String), - Directory, - Character(Special), - Block(Special), - Fifo, -} - -impl FileType { - pub fn read(reader: &mut T) -> Result { - let mut buf = [0; 1]; - reader.read_exact(&mut buf)?; - match buf[0] { - 0 => { - let file = File::read(reader)?; - Ok(Self::Normal(file)) - } - 1 => { - let mut len = [0; 8]; - reader.read_exact(&mut len)?; - let len = u64::from_le_bytes(len); - let mut buf = Vec::with_capacity(len.try_into()?); - let mut handle = reader.take(len); - handle.read_exact(&mut buf)?; - let s = String::from_utf8(buf)?; - Ok(Self::HardLink(s)) - } - 2 => { - let mut len = [0; 8]; - reader.read_exact(&mut len)?; - let len = u64::from_le_bytes(len); - let mut buf = Vec::with_capacity(len.try_into()?); - let mut handle = reader.take(len); - handle.read_exact(&mut buf)?; - let s = String::from_utf8(buf)?; - Ok(Self::SoftLink(s)) - } - 3 => Ok(Self::Directory), - 4 => { - let sp = Special::read(reader)?; - Ok(Self::Character(sp)) - } - 5 => { - let sp = Special::read(reader)?; - Ok(Self::Block(sp)) - } - 6 => Ok(Self::Fifo), - _ => Err(Error::UnknownFileType), - } - } - - pub fn write(&self, writer: &mut T) -> Result<(), Error> { - match self { - Self::Normal(f) => { - writer.write_all(&[0])?; - f.write(writer)?; - } - Self::HardLink(s) => { - writer.write_all(&[1])?; - let len = s.len() as u64; - writer.write_all(&len.to_le_bytes())?; - writer.write_all(s.as_bytes())?; - } - Self::SoftLink(s) => { - writer.write_all(&[2])?; - let len = s.len() as u64; - writer.write_all(&len.to_le_bytes())?; - writer.write_all(s.as_bytes())?; - } - Self::Directory => writer.write_all(&[3])?, - Self::Character(s) => { - writer.write_all(&[4])?; - s.write(writer)?; - } - Self::Block(s) => { - writer.write_all(&[5])?; - s.write(writer)?; - } - Self::Fifo => writer.write_all(&[6])?, - } - Ok(()) - } -} - -#[derive(Debug, PartialEq)] -enum Kind { - Normal, - Dir, - Char, - Block, - Pipe, -} - -impl From for Kind { - fn from(value: u32) -> Self { - if value & 0o60000 != 0 { - Self::Block - } else if value & 0o20000 != 0 { - Self::Char - } else if value & 0o10000 != 0 { - Self::Pipe - } else if value & 0o40000 != 0 { - Self::Dir - } else if value & 0o100000 != 0 { - Self::Normal - } else { - panic!(); - } - } -} - -#[derive(Debug)] -pub struct Node { - pub name: String, - pub mode: u32, - pub uid: u32, - pub gid: u32, - pub mtime: u64, - pub filetype: FileType, -} - -impl Node { - pub fn read(reader: &mut T) -> Result { - let mut len = [0; 8]; - reader.read_exact(&mut len)?; - let len = u64::from_le_bytes(len); - let mut name = Vec::with_capacity(len.try_into()?); - let mut handle = reader.take(len); - handle.read_exact(&mut name)?; - let mut buf = [0; 20]; - reader.read_exact(&mut buf)?; - let mode: [u8; 4] = buf[..4].try_into()?; - let uid: [u8; 4] = buf[4..8].try_into()?; - let gid: [u8; 4] = buf[8..12].try_into()?; - let mtime: [u8; 8] = buf[12..].try_into()?; - let filetype = FileType::read(reader)?; - Ok(Self { - name: String::from_utf8(name)?, - mode: u32::from_le_bytes(mode), - uid: u32::from_le_bytes(uid), - gid: u32::from_le_bytes(gid), - mtime: u64::from_le_bytes(mtime), - filetype, - }) - } - - pub fn write(&self, writer: &mut T) -> Result<(), Error> { - let len = self.name.len() as u64; - writer.write_all(&len.to_le_bytes())?; - writer.write_all(self.name.as_bytes())?; - [self.mode, self.uid, self.gid] - .iter() - .try_for_each(|f| writer.write_all(&f.to_le_bytes()))?; - writer.write_all(&self.mtime.to_le_bytes())?; - self.filetype.write(writer)?; - Ok(()) - } - - pub fn from_path( - path: &str, - checksum: Checksum, - links: Mutex>, - ) -> Result { - let name = String::from(path); - let fd = fs::File::open(path)?; - let meta = fd.metadata()?; - let mode = meta.mode(); - let uid = meta.uid(); - let gid = meta.gid(); - let mtime = meta.mtime().try_into()?; - let mut reader = BufReader::new(fd); - let ft = meta.file_type(); - let filetype = 'blk: { - if ft.is_dir() { - FileType::Directory - } else if ft.is_symlink() { - let target = fs::read_link(path)?; - let target = target - .to_str() - .ok_or(Error::Other("bad path".to_string()))? - .to_string(); - FileType::SoftLink(target) - } else { - if meta.nlink() > 1 { - if let Ok(mut list) = links.lock() { - let inode = meta.ino(); - if let Some(target) = list.get(&inode).cloned() { - break 'blk FileType::HardLink(target); - } else { - list.insert(inode, name.clone()); - } - } - } - let kind = Kind::from(mode); - if kind == Kind::Char { - let device = Special::from_rdev(meta.rdev().try_into()?); - break 'blk FileType::Character(device); - } else if kind == Kind::Block { - let device = Special::from_rdev(meta.rdev().try_into()?); - break 'blk FileType::Block(device); - } else if kind == Kind::Pipe { - break 'blk FileType::Fifo; - } else if kind == Kind::Normal { - let mut len = meta.len(); - let mut data = Vec::with_capacity(len.try_into()?); - len = reader.read_to_end(&mut data)?.try_into()?; - let checksum = match checksum { - Checksum::Md5(mut cs) => { - let mut hasher = Md5::new(); - hasher.update(&data); - cs = hasher.finalize().into(); - Checksum::Md5(cs) - } - Checksum::Sha1(mut cs) => { - let mut hasher = Sha1::new(); - hasher.update(&data); - cs = hasher.finalize().into(); - Checksum::Sha1(cs) - } - Checksum::Sha256(mut cs) => { - let mut hasher = Sha256::new(); - hasher.update(&data); - cs = hasher.finalize().into(); - Checksum::Sha256(cs) - } - Checksum::Skip => checksum, - }; - break 'blk FileType::Normal(File { - len, - checksum, - data, - }); - } else { - return Err(Error::UnknownFileType); - } - } - }; - Ok(Self { - name, - mode, - uid, - gid, - mtime, - filetype, - }) - } -} diff --git a/src/node.rs b/src/node.rs new file mode 100644 index 0000000..d94974a --- /dev/null +++ b/src/node.rs @@ -0,0 +1,177 @@ +use { + crate::{Checksum, Error, File, FileType, Special}, + md5::{Digest, Md5}, + sha1::Sha1, + sha2::Sha256, + std::{ + collections::HashMap, + fs, + io::{BufReader, Read, Write}, + os::unix::fs::MetadataExt, + sync::Mutex, + }, +}; + +#[derive(Debug, PartialEq)] +enum Kind { + Normal, + Dir, + Char, + Block, + Pipe, +} + +impl From for Kind { + fn from(value: u32) -> Self { + if value & 0o60000 != 0 { + Self::Block + } else if value & 0o20000 != 0 { + Self::Char + } else if value & 0o10000 != 0 { + Self::Pipe + } else if value & 0o40000 != 0 { + Self::Dir + } else if value & 0o100000 != 0 { + Self::Normal + } else { + panic!(); + } + } +} + +#[derive(Debug)] +pub struct Node { + pub name: String, + pub mode: u32, + pub uid: u32, + pub gid: u32, + pub mtime: u64, + pub filetype: FileType, +} + +impl Node { + pub fn read(reader: &mut T) -> Result { + let mut len = [0; 8]; + reader.read_exact(&mut len)?; + let len = u64::from_le_bytes(len); + let mut name = Vec::with_capacity(len.try_into()?); + let mut handle = reader.take(len); + handle.read_exact(&mut name)?; + let mut buf = [0; 20]; + reader.read_exact(&mut buf)?; + let mode: [u8; 4] = buf[..4].try_into()?; + let uid: [u8; 4] = buf[4..8].try_into()?; + let gid: [u8; 4] = buf[8..12].try_into()?; + let mtime: [u8; 8] = buf[12..].try_into()?; + let filetype = FileType::read(reader)?; + Ok(Self { + name: String::from_utf8(name)?, + mode: u32::from_le_bytes(mode), + uid: u32::from_le_bytes(uid), + gid: u32::from_le_bytes(gid), + mtime: u64::from_le_bytes(mtime), + filetype, + }) + } + + pub fn write(&self, writer: &mut T) -> Result<(), Error> { + let len = self.name.len() as u64; + writer.write_all(&len.to_le_bytes())?; + writer.write_all(self.name.as_bytes())?; + [self.mode, self.uid, self.gid] + .iter() + .try_for_each(|f| writer.write_all(&f.to_le_bytes()))?; + writer.write_all(&self.mtime.to_le_bytes())?; + self.filetype.write(writer)?; + Ok(()) + } + + pub fn from_path( + path: &str, + checksum: Checksum, + links: Mutex>, + ) -> Result { + let name = String::from(path); + let fd = fs::File::open(path)?; + let meta = fd.metadata()?; + let mode = meta.mode(); + let uid = meta.uid(); + let gid = meta.gid(); + let mtime = meta.mtime().try_into()?; + let mut reader = BufReader::new(fd); + let ft = meta.file_type(); + let filetype = 'blk: { + if ft.is_dir() { + FileType::Directory + } else if ft.is_symlink() { + let target = fs::read_link(path)?; + let target = target + .to_str() + .ok_or(Error::Other("bad path".to_string()))? + .to_string(); + FileType::SoftLink(target) + } else { + if meta.nlink() > 1 { + if let Ok(mut list) = links.lock() { + let inode = meta.ino(); + if let Some(target) = list.get(&inode).cloned() { + break 'blk FileType::HardLink(target); + } else { + list.insert(inode, name.clone()); + } + } + } + let kind = Kind::from(mode); + if kind == Kind::Char { + let device = Special::from_rdev(meta.rdev().try_into()?); + break 'blk FileType::Character(device); + } else if kind == Kind::Block { + let device = Special::from_rdev(meta.rdev().try_into()?); + break 'blk FileType::Block(device); + } else if kind == Kind::Pipe { + break 'blk FileType::Fifo; + } else if kind == Kind::Normal { + let mut len = meta.len(); + let mut data = Vec::with_capacity(len.try_into()?); + len = reader.read_to_end(&mut data)?.try_into()?; + let checksum = match checksum { + Checksum::Md5(mut cs) => { + let mut hasher = Md5::new(); + hasher.update(&data); + cs = hasher.finalize().into(); + Checksum::Md5(cs) + } + Checksum::Sha1(mut cs) => { + let mut hasher = Sha1::new(); + hasher.update(&data); + cs = hasher.finalize().into(); + Checksum::Sha1(cs) + } + Checksum::Sha256(mut cs) => { + let mut hasher = Sha256::new(); + hasher.update(&data); + cs = hasher.finalize().into(); + Checksum::Sha256(cs) + } + Checksum::Skip => checksum, + }; + break 'blk FileType::Normal(File { + len, + checksum, + data, + }); + } else { + return Err(Error::UnknownFileType); + } + } + }; + Ok(Self { + name, + mode, + uid, + gid, + mtime, + filetype, + }) + } +} diff --git a/src/special.rs b/src/special.rs new file mode 100644 index 0000000..7537d40 --- /dev/null +++ b/src/special.rs @@ -0,0 +1,35 @@ +use { + crate::Error, + std::io::{Read, Write}, +}; + +#[derive(Debug)] +pub struct Special { + pub major: u32, + pub minor: u32, +} + +impl Special { + pub fn read(reader: &mut T) -> Result { + let mut buf = [0; 8]; + reader.read_exact(&mut buf)?; + let major: [u8; 4] = buf[..4].try_into()?; + let major = u32::from_le_bytes(major); + let minor: [u8; 4] = buf[4..].try_into()?; + let minor = u32::from_le_bytes(minor); + Ok(Self { major, minor }) + } + + pub fn write(&self, writer: &mut T) -> Result<(), Error> { + writer.write_all(&self.major.to_le_bytes())?; + writer.write_all(&self.minor.to_le_bytes())?; + Ok(()) + } + + pub(super) fn from_rdev(rdev: u32) -> Self { + Self { + major: rdev >> 8, + minor: rdev & 0xff, + } + } +}