Refactor into separate modules
This commit is contained in:
parent
162e027365
commit
82ede31688
56
src/checksum.rs
Normal file
56
src/checksum.rs
Normal file
@ -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<T: Read>(reader: &mut T) -> Result<Self, Error> {
|
||||||
|
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<T: 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(())
|
||||||
|
}
|
||||||
|
}
|
66
src/error.rs
Normal file
66
src/error.rs
Normal file
@ -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<TryFromIntError> for Error {
|
||||||
|
fn from(value: TryFromIntError) -> Self {
|
||||||
|
Self::Int(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for Error {
|
||||||
|
fn from(value: io::Error) -> Self {
|
||||||
|
Self::Io(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FromUtf8Error> for Error {
|
||||||
|
fn from(value: FromUtf8Error) -> Self {
|
||||||
|
Self::Utf8(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TryFromSliceError> for Error {
|
||||||
|
fn from(value: TryFromSliceError) -> Self {
|
||||||
|
Self::Slice(value)
|
||||||
|
}
|
||||||
|
}
|
82
src/file.rs
Normal file
82
src/file.rs
Normal file
@ -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<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl File {
|
||||||
|
pub fn read<T: Read>(reader: &mut T) -> Result<Self, Error> {
|
||||||
|
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<T: 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(())
|
||||||
|
}
|
||||||
|
}
|
92
src/filetype.rs
Normal file
92
src/filetype.rs
Normal file
@ -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<T: Read>(reader: &mut T) -> Result<Self, Error> {
|
||||||
|
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<T: 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(())
|
||||||
|
}
|
||||||
|
}
|
497
src/lib.rs
497
src/lib.rs
@ -1,490 +1,9 @@
|
|||||||
use {
|
mod checksum;
|
||||||
md5::{Digest, Md5},
|
mod error;
|
||||||
sha1::Sha1,
|
mod file;
|
||||||
sha2::Sha256,
|
mod filetype;
|
||||||
std::{
|
mod node;
|
||||||
array::TryFromSliceError,
|
mod special;
|
||||||
collections::HashMap,
|
pub use {
|
||||||
fmt, fs,
|
checksum::Checksum, error::Error, file::File, filetype::FileType, node::Node, special::Special,
|
||||||
io::{self, BufReader, Read, Write},
|
|
||||||
num::TryFromIntError,
|
|
||||||
os::unix::fs::MetadataExt,
|
|
||||||
string::FromUtf8Error,
|
|
||||||
sync::Mutex,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[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<TryFromIntError> for Error {
|
|
||||||
fn from(value: TryFromIntError) -> Self {
|
|
||||||
Self::Int(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<io::Error> for Error {
|
|
||||||
fn from(value: io::Error) -> Self {
|
|
||||||
Self::Io(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<FromUtf8Error> for Error {
|
|
||||||
fn from(value: FromUtf8Error) -> Self {
|
|
||||||
Self::Utf8(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TryFromSliceError> 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<T: Read>(reader: &mut T) -> Result<Self, Error> {
|
|
||||||
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<T: 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<T: Read>(reader: &mut T) -> Result<Self, Error> {
|
|
||||||
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<T: 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<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl File {
|
|
||||||
pub fn read<T: Read>(reader: &mut T) -> Result<Self, Error> {
|
|
||||||
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<T: 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<T: Read>(reader: &mut T) -> Result<Self, Error> {
|
|
||||||
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<T: 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<u32> 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<T: Read>(reader: &mut T) -> Result<Self, Error> {
|
|
||||||
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<T: 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<HashMap<u64, String>>,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
177
src/node.rs
Normal file
177
src/node.rs
Normal file
@ -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<u32> 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<T: Read>(reader: &mut T) -> Result<Self, Error> {
|
||||||
|
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<T: 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<HashMap<u64, String>>,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
35
src/special.rs
Normal file
35
src/special.rs
Normal file
@ -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<T: Read>(reader: &mut T) -> Result<Self, Error> {
|
||||||
|
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<T: 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user