diff --git a/src/filetype.rs b/src/filetype.rs index 8b0f50b..5fb81a4 100644 --- a/src/filetype.rs +++ b/src/filetype.rs @@ -4,7 +4,7 @@ use { }; #[repr(u8)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, PartialEq)] pub(crate) enum Flag { Normal = 0, HardLink = 1, @@ -52,7 +52,15 @@ impl From<&FileType> for Flag { impl Flag { pub(crate) fn append_mode(&self, mode: u16) -> u16 { let mask: u16 = u16::from(*self as u8) << 13; - mode & mask + mode | mask + } + + pub(crate) fn extract_from_raw(raw: u16) -> Result<(Self, u16), Error> { + let mask: u16 = 0b111 << 13; + let mode = raw & 0o777; + let flag: u8 = ((raw & mask) >> 13).try_into()?; + let flag: Self = flag.try_into()?; + Ok((flag, mode)) } } @@ -90,7 +98,7 @@ impl FileType { let len = u16::from_le_bytes(len); let mut buf = Vec::with_capacity(len.into()); let mut handle = reader.take(len.into()); - handle.read_exact(&mut buf)?; + handle.read_to_end(&mut buf)?; let s = String::from_utf8(buf)?; Ok(Self::HardLink(s)) } @@ -100,7 +108,7 @@ impl FileType { let len = u16::from_le_bytes(len); let mut buf = Vec::with_capacity(len.into()); let mut handle = reader.take(len.into()); - handle.read_exact(&mut buf)?; + handle.read_to_end(&mut buf)?; let s = String::from_utf8(buf)?; Ok(Self::SoftLink(s)) } @@ -122,7 +130,7 @@ impl FileType { match self { Self::Normal(f) => f.write(writer)?, Self::HardLink(s) | Self::SoftLink(s) => { - let len = s.len() as u64; + let len = s.len() as u16; writer.write_all(&len.to_le_bytes())?; writer.write_all(s.as_bytes())?; } @@ -132,3 +140,28 @@ impl FileType { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn manipulate_mode_softlink() { + let mode: u16 = 0o644; + let flag = Flag::SoftLink; + let raw = flag.append_mode(mode); + let (f, m) = Flag::extract_from_raw(raw).unwrap(); + assert_eq!(m, mode); + assert_eq!(f, flag); + } + + #[test] + fn manipulate_mode_normal() { + let mode: u16 = 0o755; + let flag = Flag::Normal; + let raw = flag.append_mode(mode); + let (f, m) = Flag::extract_from_raw(raw).unwrap(); + assert_eq!(m, mode); + assert_eq!(f, flag); + } +} diff --git a/src/node.rs b/src/node.rs index be073b6..e1ad0bf 100644 --- a/src/node.rs +++ b/src/node.rs @@ -85,10 +85,7 @@ impl Node { let mtime: [u8; 8] = buf[8..16].try_into()?; let raw_mode: [u8; 2] = buf[16..18].try_into()?; let raw_mode = u16::from_le_bytes(raw_mode); - let mask: u16 = 0b111 << 13; - let mode = raw_mode & mask; - let flag: u8 = ((raw_mode & !mask) >> 13).try_into()?; - let flag: Flag = flag.try_into()?; + let (flag, mode) = Flag::extract_from_raw(raw_mode)?; let filetype = FileType::read(reader, flag)?; Ok(Self { name: String::from_utf8(name)?, @@ -203,12 +200,13 @@ impl Node { } } }; + let mode = Flag::from(&filetype).append_mode(u16::try_from(mode & 0o7777)?); Ok(Self { name, uid, gid, mtime, - mode: (mode & !(0o111 << 13)).try_into()?, + mode, filetype, }) } @@ -405,12 +403,22 @@ mod tests { let mut writer = std::io::BufWriter::new(fd); let links = Mutex::new(HashMap::new()); let node = Node::from_path("test/li.txt", Algorithm::Sha1, &links).unwrap(); + let FileType::Normal(_) = node.filetype else { + eprintln!("Created wrong filetype: {:?}", node.filetype); + panic!(); + }; node.write(&mut writer).unwrap(); } + let meta = fs::metadata("test/li.txt").unwrap(); let fd = std::fs::File::open("test/li.node").unwrap(); let mut reader = BufReader::new(fd); let node = Node::read(&mut reader).unwrap(); + assert_eq!(meta.mode() & 0o777, node.mode as u32); + assert_eq!(meta.uid(), node.uid); + assert_eq!(meta.gid(), node.gid); + assert_eq!(meta.mtime(), node.mtime as i64); let FileType::Normal(f) = node.filetype else { + eprintln!("Read incorrect filetype: {:?}", node.filetype); panic!() }; let Checksum::Sha1(sum) = f.checksum else { @@ -424,6 +432,33 @@ mod tests { assert_eq!(LI, f.data); } + #[test] + fn load_store_symlink() { + { + let _res = remove_file("test/lilnk.txt"); + symlink("li.txt", "test/lilnk.txt").unwrap(); + let _res = remove_file("test/lilnk.node"); + let fd = std::fs::File::create("test/lilnk.node").unwrap(); + let mut writer = std::io::BufWriter::new(fd); + let links = Mutex::new(HashMap::new()); + let node = Node::from_path("test/lilnk.txt", Algorithm::Sha1, &links).unwrap(); + let FileType::SoftLink(ref tgt) = node.filetype else { + eprintln!("Created wrong filetype: {:?}", node.filetype); + panic!(); + }; + assert_eq!(tgt, "li.txt"); + node.write(&mut writer).unwrap(); + } + let fd = std::fs::File::open("test/lilnk.node").unwrap(); + let mut reader = BufReader::new(fd); + let node = Node::read(&mut reader).unwrap(); + let FileType::SoftLink(ref tgt) = node.filetype else { + eprintln!("Read incorrect filetype: {:?}", node.filetype); + panic!(); + }; + assert_eq!(tgt, "li.txt"); + } + #[test] fn extract_file() { let links = Mutex::new(HashMap::new());