diff --git a/src/lib.rs b/src/lib.rs index 03f9a19..8586214 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,12 +4,13 @@ use { sha2::Sha256, std::{ array::TryFromSliceError, + collections::HashMap, fmt, fs, - io::{self, BufRead, BufReader, Read, Write}, + io::{self, BufReader, Read, Write}, num::TryFromIntError, os::unix::fs::MetadataExt, - path::PathBuf, string::FromUtf8Error, + sync::Mutex, }, }; @@ -100,6 +101,13 @@ impl Special { writer.write_all(&self.minor.to_le_bytes())?; Ok(()) } + + fn from_rdev(rdev: u32) -> Self { + Self { + major: rdev >> 8, + minor: rdev & 0xff, + } + } } #[derive(Debug)] @@ -249,7 +257,7 @@ impl FileType { 0 => { let file = File::read(reader)?; Ok(Self::Normal(file)) - }, + } 1 => { let mut len = [0; 8]; reader.read_exact(&mut len)?; @@ -289,7 +297,7 @@ impl FileType { Self::Normal(f) => { writer.write_all(&[0])?; f.write(writer)?; - }, + } Self::HardLink(s) => { writer.write_all(&[1])?; let len = s.len() as u64; @@ -317,7 +325,7 @@ impl FileType { } } -#[derive(Debug)] +#[derive(Debug, PartialEq)] enum Kind { Normal, Dir, @@ -391,31 +399,92 @@ impl Node { Ok(()) } - /*pub fn from_path(path: &str) -> Result { + pub fn from_path( + path: &str, + checksum: Checksum, + links: Mutex>, + ) -> Result { let name = String::from(path); - let mut fd = fs::File::open(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(); + let mtime = meta.mtime().try_into()?; let mut reader = BufReader::new(fd); let ft = meta.file_type(); - let filetype = 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 mode & 0o20000 != 0 { - let rdev = meta.rdev(); - if rdev != 0 { + 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); + } } - todo!() }; - todo!() - }*/ + Ok(Self { + name, + mode, + uid, + gid, + mtime, + filetype, + }) + } }