fast_ntfs 1.0.2

Forked a low-level NTFS filesystem library
Documentation
mod common;

use fast_ntfs::{Ntfs, NtfsAttributeType, NtfsError};

const FILE_RECORD_DATA_SIZE_OFFSET: usize = 24;

fn truncate_root_record_after_first_attribute(
    extra_bytes: u32,
) -> (Ntfs, std::io::Cursor<Vec<u8>>) {
    let mut fs = common::testfs1();
    let ntfs = Ntfs::new(&mut fs).unwrap();
    let root = ntfs.root_directory(&mut fs).unwrap();
    let root_position = root.position().value().unwrap().get() as usize;
    let first_attribute = root.attributes_raw().next().unwrap().unwrap();
    let first_attribute_end = first_attribute.position().value().unwrap().get()
        - root.position().value().unwrap().get()
        + first_attribute.attribute_length() as u64;
    let data_size = u32::try_from(first_attribute_end).unwrap() + extra_bytes;

    fs.get_mut()[root_position + FILE_RECORD_DATA_SIZE_OFFSET
        ..root_position + FILE_RECORD_DATA_SIZE_OFFSET + size_of::<u32>()]
        .copy_from_slice(&data_size.to_le_bytes());

    (ntfs, fs)
}

#[test]
fn flattened_attribute_iterator_is_fused_after_completion() {
    let mut fs = common::testfs1();
    let ntfs = Ntfs::new(&mut fs).unwrap();
    let root_directory = ntfs.root_directory(&mut fs).unwrap();
    let mut attributes = root_directory.attributes();
    let mut types = Vec::new();

    while let Some(item) = attributes.next(&mut fs) {
        types.push(item.unwrap().to_attribute().unwrap().ty().unwrap());
    }

    assert!(types.contains(&NtfsAttributeType::StandardInformation));
    assert!(types.contains(&NtfsAttributeType::IndexRoot));
    assert!(attributes.next(&mut fs).is_none());
    assert!(attributes.next(&mut fs).is_none());
}

#[test]
fn raw_attributes_stop_when_less_than_a_type_field_remains() {
    let (ntfs, mut fs) = truncate_root_record_after_first_attribute(2);
    let root = ntfs.root_directory(&mut fs).unwrap();
    let mut attributes = root.attributes_raw();

    assert!(attributes.next().unwrap().is_ok());
    assert!(attributes.next().is_none());
    assert!(attributes.next().is_none());
}

#[test]
fn raw_attributes_reject_an_attribute_past_the_used_record_size() {
    let mut fs = common::testfs1();
    let ntfs = Ntfs::new(&mut fs).unwrap();
    let (root_position, first_attribute_offset) = {
        let root = ntfs.root_directory(&mut fs).unwrap();
        let attribute = root.attributes_raw().next().unwrap().unwrap();
        let root_position = root.position();
        let first_attribute_offset =
            attribute.position().value().unwrap().get() - root_position.value().unwrap().get();
        (root_position, first_attribute_offset)
    };
    let truncated_size = u32::try_from(first_attribute_offset + 16).unwrap();
    let absolute_position = root_position.value().unwrap().get() as usize;
    fs.get_mut()[absolute_position + FILE_RECORD_DATA_SIZE_OFFSET
        ..absolute_position + FILE_RECORD_DATA_SIZE_OFFSET + size_of::<u32>()]
        .copy_from_slice(&truncated_size.to_le_bytes());

    let root = ntfs.root_directory(&mut fs).unwrap();
    let error = root.attributes_raw().next().unwrap().unwrap_err();
    assert!(matches!(error, NtfsError::InvalidAttributeLength { .. }));
}