use crate::Result;
use super::attribute::decode_utf16le;
#[derive(Debug, Clone)]
pub struct AttributeListEntry {
pub type_code: u32,
pub starting_vcn: u64,
pub mft_reference: u64,
pub attribute_id: u16,
pub name: String,
}
impl AttributeListEntry {
pub fn record_number(&self) -> u64 {
self.mft_reference & 0x0000_FFFF_FFFF_FFFF
}
}
pub fn decode(buf: &[u8]) -> Result<Vec<AttributeListEntry>> {
let mut out = Vec::new();
let mut cursor = 0usize;
while cursor + 0x1A <= buf.len() {
let type_code = u32::from_le_bytes(buf[cursor..cursor + 4].try_into().unwrap());
if type_code == 0xFFFF_FFFF {
break;
}
let entry_len =
u16::from_le_bytes(buf[cursor + 4..cursor + 6].try_into().unwrap()) as usize;
if entry_len < 0x1A {
return Err(crate::Error::InvalidImage(format!(
"ntfs: $ATTRIBUTE_LIST entry length {entry_len} too small"
)));
}
if cursor + entry_len > buf.len() {
return Err(crate::Error::InvalidImage(
"ntfs: $ATTRIBUTE_LIST entry oversteps buffer".into(),
));
}
let name_len = buf[cursor + 6] as usize;
let name_off = buf[cursor + 7] as usize;
let starting_vcn = u64::from_le_bytes(buf[cursor + 8..cursor + 0x10].try_into().unwrap());
let mft_reference =
u64::from_le_bytes(buf[cursor + 0x10..cursor + 0x18].try_into().unwrap());
let attribute_id =
u16::from_le_bytes(buf[cursor + 0x18..cursor + 0x1A].try_into().unwrap());
let name = if name_len == 0 {
String::new()
} else {
let name_start = cursor + name_off;
let name_end = name_start + name_len * 2;
if name_end > cursor + entry_len {
return Err(crate::Error::InvalidImage(
"ntfs: $ATTRIBUTE_LIST name oversteps entry".into(),
));
}
decode_utf16le(&buf[name_start..name_end])
};
out.push(AttributeListEntry {
type_code,
starting_vcn,
mft_reference,
attribute_id,
name,
});
cursor += entry_len;
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decode_two_entries() {
let mut buf = Vec::new();
let entry_len: u16 = 0x20; buf.extend_from_slice(&0x80u32.to_le_bytes()); buf.extend_from_slice(&entry_len.to_le_bytes()); buf.push(0); buf.push(0x1A); buf.extend_from_slice(&0u64.to_le_bytes()); buf.extend_from_slice(&((42u64) | (1u64 << 48)).to_le_bytes()); buf.extend_from_slice(&7u16.to_le_bytes()); buf.extend(std::iter::repeat_n(0u8, 6)); buf.extend_from_slice(&0xA0u32.to_le_bytes());
buf.extend_from_slice(&entry_len.to_le_bytes());
buf.push(0);
buf.push(0x1A);
buf.extend_from_slice(&0u64.to_le_bytes());
buf.extend_from_slice(&43u64.to_le_bytes());
buf.extend_from_slice(&8u16.to_le_bytes());
buf.extend(std::iter::repeat_n(0u8, 6));
let entries = decode(&buf).unwrap();
assert_eq!(entries.len(), 2);
assert_eq!(entries[0].type_code, 0x80);
assert_eq!(entries[0].record_number(), 42);
assert_eq!(entries[0].attribute_id, 7);
assert_eq!(entries[1].type_code, 0xA0);
assert_eq!(entries[1].record_number(), 43);
}
}