### Install Mise Source: https://github.com/kikijiki/ntfs-reader/blob/master/README.md Installs the Mise version manager using a curl command. ```sh curl https://mise.run | sh ``` -------------------------------- ### Open USN Journal with Custom Options Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Opens the volume handle for overlapped I/O and initializes an I/O Completion Port. Use JournalOptions to control the starting USN, reason bitmask, and history buffer size. ```rust use ntfs_reader::{ journal::{HistorySize, Journal, JournalOptions, NextUsn}, volume::Volume, }; use windows::Win32::System::Ioctl::{USN_REASON_FILE_CREATE, USN_REASON_FILE_DELETE}; fn main() -> Result<(), Box> { let volume = Volume::new("\\\\?\\C:")?; let options = JournalOptions { // Only report file creation and deletion events reason_mask: USN_REASON_FILE_CREATE | USN_REASON_FILE_DELETE, // Start from the very beginning of the journal history next_usn: NextUsn::First, // Keep at most 512 old records in the rename-tracking history max_history_size: HistorySize::Limited(512), }; let mut journal = Journal::new(volume, options)?; println!("Journal opened. Next USN before read: {}", journal.get_next_usn()); Ok(()) } ``` -------------------------------- ### Volume::new — Open an NTFS volume Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Opens a raw Windows volume path and initializes a Volume handle. This is the starting point for interacting with NTFS volumes and requires administrator privileges. ```APIDOC ## Volume::new — Open an NTFS volume ### Description Opens a raw Windows volume path (e.g., `\\.\C:`), verifies that the calling process is elevated, reads the NTFS boot sector, and computes geometry values (cluster size, MFT position, file record size). This `Volume` handle is the starting point for both the MFT and journal APIs. ### Method ```rust use ntfs_reader::volume::Volume; fn main() -> Result<(), Box> { // Requires administrator privileges; returns NtfsReaderError::ElevationError otherwise. let volume = Volume::new("\\\\.\\C:")?; println!("Cluster size : {} bytes", volume.cluster_size); println!("Volume size : {} bytes", volume.volume_size); println!("File record sz : {} bytes", volume.file_record_size); println!("MFT position : 0x{:X}", volume.mft_position); Ok(()) } ``` ### Expected output (values vary by drive) ``` Cluster size : 4096 bytes Volume size : 500105249792 bytes File record sz : 1024 bytes MFT position : 0xC000000 ``` ``` -------------------------------- ### Journal::new Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Opens the volume handle for overlapped I/O, queries FSCTL_QUERY_USN_JOURNAL to obtain journal metadata, and initialises an I/O Completion Port for asynchronous reads. JournalOptions controls the starting USN, the reason bitmask, and optional history buffer size. ```APIDOC ## `Journal::new` — Open the USN journal Opens the volume handle for overlapped I/O, queries `FSCTL_QUERY_USN_JOURNAL` to obtain journal metadata, and initialises an I/O Completion Port for asynchronous reads. `JournalOptions` controls the starting USN (`NextUsn::First`, `NextUsn::Next`, or `NextUsn::Custom(i64)`), the reason bitmask, and optional history buffer size. ```rust use ntfs_reader::{ journal::{HistorySize, Journal, JournalOptions, NextUsn}, volume::Volume, }; use windows::Win32::System::Ioctl::{USN_REASON_FILE_CREATE, USN_REASON_FILE_DELETE}; fn main() -> Result<(), Box> { let volume = Volume::new("\\\\?\C:")?; let options = JournalOptions { // Only report file creation and deletion events reason_mask: USN_REASON_FILE_CREATE | USN_REASON_FILE_DELETE, // Start from the very beginning of the journal history next_usn: NextUsn::First, // Keep at most 512 old records in the rename-tracking history max_history_size: HistorySize::Limited(512), }; let mut journal = Journal::new(volume, options)?; println!("Journal opened. Next USN before read: {}", journal.get_next_usn()); Ok(()) } ``` ``` -------------------------------- ### Error Handling — NtfsReaderError Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Details the error handling mechanism using `NtfsReaderError` and provides examples of how to handle specific errors. ```APIDOC ## Error Handling — `NtfsReaderError` All fallible functions return `NtfsReaderResult` (an alias for `Result`). The error enum wraps OS-level errors from `std::io`, `binread`, and the `windows` crate, and adds NTFS-specific variants. ```rust use ntfs_reader::{errors::NtfsReaderError, mft::Mft, volume::Volume}; fn open_volume(path: &str) -> Result { let volume = Volume::new(path)?; let mft = Mft::new(volume)?; Ok(mft) } fn main() { match open_volume("\\\\.\\C:") { Ok(mft) => println!("Opened MFT, {} records", mft.max_record), Err(NtfsReaderError::ElevationError) => { eprintln!("Run as administrator"); } Err(NtfsReaderError::CorruptMftRecord { number }) => { eprintln!("Corrupt record #{number}"); } Err(NtfsReaderError::MissingMftAttribute(name)) => { eprintln!("MFT missing attribute: {name}"); } Err(e) => eprintln!("Other error: {e}"), } } ``` ``` -------------------------------- ### Journal::trim_history and Journal::get_next_usn Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Manages journal history by evicting old-name records and provides a way to get the current read cursor for resuming operations. ```APIDOC ## `Journal::trim_history` and `Journal::get_next_usn` — History management `trim_history` evicts old-name records from the internal rename-tracking deque, either dropping everything (`None`) or retaining only records with USN greater than a threshold. `get_next_usn` returns the current read cursor — save this value to resume reading from the same position across process restarts. ```rust use ntfs_reader::{ journal::{Journal, JournalOptions}, volume::Volume, }; fn main() -> Result<(), Box> { let volume = Volume::new("\\\\?\\C:")?; let mut journal = Journal::new(volume, JournalOptions::default())?; let _ = journal.read()?; // Persist this USN so the next process run can resume here let checkpoint_usn = journal.get_next_usn(); println!("Checkpoint USN: {checkpoint_usn}"); // Free rename history older than the checkpoint journal.trim_history(Some(checkpoint_usn)); // Or clear the entire history buffer journal.trim_history(None); Ok(()) } ``` ``` -------------------------------- ### Open an NTFS volume using Volume::new Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Opens a raw Windows volume path and reads NTFS boot sector information. Requires administrator privileges. ```rust use ntfs_reader::volume::Volume; fn main() -> Result<(), Box> { // Requires administrator privileges; returns NtfsReaderError::ElevationError otherwise. let volume = Volume::new("\\.\\C:")?; println!("Cluster size : {} bytes", volume.cluster_size); println!("Volume size : {} bytes", volume.volume_size); println!("File record sz : {} bytes", volume.file_record_size); println!("MFT position : 0x{:X}", volume.mft_position); Ok(()) } ``` -------------------------------- ### `NtfsFile` methods Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Provides methods to inspect raw MFT file records. `NtfsFile` offers a zero-copy view into the MFT buffer, allowing access to record metadata and iteration over attributes. Key methods include `number()`, `reference_number()`, `is_used()`, `is_directory()`, `attributes()`, `get_attribute()`, `get_best_file_name()`, and `read_data()`. ```APIDOC ## `NtfsFile` methods — Inspect a raw file record `NtfsFile` is a zero-copy view into the in-memory MFT buffer exposing record metadata and attribute iteration. Key methods: `number()`, `reference_number()`, `is_used()`, `is_directory()`, `attributes()` (callback iterator), `get_attribute()`, `get_best_file_name()`, `read_data()` (resident-only). ### Available Methods * `number() -> u64`: Returns the MFT record number. * `reference_number() -> u64`: Returns the MFT reference number. * `is_used() -> bool`: Checks if the MFT record is currently in use. * `is_directory() -> bool`: Checks if the MFT record represents a directory. * `attributes(&self, callback: F)` where `F: FnMut(Attribute)`: Iterates over the attributes of the file record. * `get_attribute(&self, type_id: u32) -> Option`: Retrieves a specific attribute by its type ID. * `get_best_file_name(&self, mft: &Mft) -> Option`: Gets the preferred Win32 or POSIX file name. * `read_data() -> Option<&[u8]>`: Reads the resident data content (returns `None` for non-resident or large files). ### Example ```rust use ntfs_reader::{api::NtfsAttributeType, mft::Mft, volume::Volume}; fn main() -> Result<(), Box> { let volume = Volume::new("\\\\.\\C:")?; let mft = Mft::new(volume)?; for file in mft.files().take(3) { println!("---"Record #{}"---, file.number()); println!(" in use : {}", file.is_used()); println!(" directory : {}", file.is_directory()); println!(" ref number : 0x{:X}", file.reference_number()); // Iterate every attribute and print its type id file.attributes(|attr| { println!(" attribute type: 0x{:X}", attr.header.type_id); }); // Read small resident $DATA content (returns None for large / non-resident files) if let Some(data) = file.read_data() { println!(" resident data bytes: {}", data.len()); } // Preferred Win32 / Win32AndDos name (falls back to POSIX) if let Some(name) = file.get_best_file_name(&mft) { println!(" name: {name} parent_record: {}", name.parent()); } } Ok(()) } ``` ``` -------------------------------- ### Mise Development Tasks Source: https://github.com/kikijiki/ntfs-reader/blob/master/README.md Common development tasks using Mise, including formatting, linting, building, testing, and benchmarking. ```sh mise fix # Fix format and fixable linting errors ``` ```sh mise check # Check format and linting issues ``` ```sh mise build # Build debug ``` ```sh mise release # Build release ``` ```sh mise test # Run tests ``` ```sh mise test-32 # Run tests with the `i686-pc-windows-msvc` target, single threaded ``` ```sh mise bench # Run benchmarks (slow!) ``` -------------------------------- ### Read NTFS MFT Records Source: https://github.com/kikijiki/ntfs-reader/blob/master/README.md Opens the C volume's MFT and iterates through all files. Requires elevated privileges. Performance comparison shows caching benefits. ```rust let volume = Volume::new("\\\\.\\C:")?; let mft = Mft::new(volume)?; // Iterate all files for file in mft.files() { // Can also use FileInfo::with_cache(). let info = FileInfo::new(&mft, &file); // Available fields: name, path, is_directory, size, timestamps (created, accessed, modified). } ``` -------------------------------- ### Load the MFT into memory using Mft::new Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Reads the entire MFT data stream and its bitmap into memory for fast, O(1) record lookups. Requires a Volume handle. ```rust use ntfs_reader::mft::Mft; use ntfs_reader::volume::Volume; fn main() -> Result<(), Box> { let volume = Volume::new("\\.\\C:")?; let mft = Mft::new(volume)?; println!("Max records in MFT: {}", mft.max_record); // Check if a specific record number exists in the bitmap println!("Record 5 exists (root): {}", mft.record_exists(5)); println!("Record 0 exists ($MFT): {}", mft.record_exists(0)); Ok(()) } ``` -------------------------------- ### Inspect Raw MFT File Record with `NtfsFile` Methods Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Examine raw MFT file records using `NtfsFile` methods like `number()`, `is_used()`, `is_directory()`, `attributes()`, `read_data()`, and `get_best_file_name()`. This provides direct access to record metadata and attribute iteration without zero-copy views. ```rust use ntfs_reader::{api::NtfsAttributeType, mft::Mft, volume::Volume}; fn main() -> Result<(), Box> { let volume = Volume::new("\\\\.\\C:")?; let mft = Mft::new(volume)?; for file in mft.files().take(3) { println!("---"Record #{}" ---", file.number()); println!(" in use : {}", file.is_used()); println!(" directory : {}", file.is_directory()); println!(" ref number : 0x{:X}", file.reference_number()); // Iterate every attribute and print its type id file.attributes(|attr| { println!(" attribute type: 0x{:X}", attr.header.type_id); }); // Read small resident $DATA content (returns None for large / non-resident files) if let Some(data) = file.read_data() { println!(" resident data bytes: {}", data.len()); } // Preferred Win32 / Win32AndDos name (falls back to POSIX) if let Some(name) = file.get_best_file_name(&mft) { println!(" name: {name} parent_record: {}", name.parent()); } } Ok(()) } ``` -------------------------------- ### Iterate all live file records using Mft::files Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Provides a lazy iterator over all in-use NtfsFile records, skipping system meta-files. Each yielded NtfsFile is a zero-copy view into the in-memory MFT. ```rust use ntfs_reader::mft::Mft; use ntfs_reader::volume::Volume; use ntfs_reader::file_info::FileInfo; fn main() -> Result<(), Box> { let volume = Volume::new("\\.\\C:")?; let mft = Mft::new(volume)?; let mut file_count = 0usize; let mut dir_count = 0usize; for file in mft.files() { let info = FileInfo::new(&mft, &file); if info.is_directory { dir_count += 1; } else { file_count += 1; } } println!("Files : {file_count}"); println!("Directories: {dir_count}"); Ok(()) } ``` -------------------------------- ### Resolve File Metadata With Cache using `FileInfo::with_cache` Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Utilize `FileInfo::with_cache` for efficient file metadata resolution by leveraging a provided cache (e.g., `VecCache` or `HashMapCache`). This significantly speeds up path resolution during MFT scans by storing previously resolved directory paths. ```rust use ntfs_reader::{ file_info::{FileInfo, VecCache, HashMapCache}, mft::Mft, volume::Volume, }; fn main() -> Result<(), Box> { let volume = Volume::new("\\\\.\\C:")?; let mft = Mft::new(volume)?; // --- VecCache: fastest (O(1) index), higher memory usage --- let mut vec_cache = VecCache::default(); vec_cache.0.resize(mft.max_record as usize, None); // pre-allocate for file in mft.files() { let info = FileInfo::with_cache(&mft, &file, &mut vec_cache); let _ = info.path; // path already resolved and cached } // --- HashMapCache: lower memory, slightly slower --- let mut map_cache = HashMapCache::default(); for file in mft.files().take(1000) { let info = FileInfo::with_cache(&mft, &file, &mut map_cache); println!("{}", info.path.display()); } Ok(()) } ``` -------------------------------- ### Manage NTFS Journal History with `trim_history` and `get_next_usn` Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Persist the current read USN to resume reading from the same position across process restarts. Free rename history older than the checkpoint or clear the entire history buffer. ```rust use ntfs_reader::{ journal::{Journal, JournalOptions}, volume::Volume, }; fn main() -> Result<(), Box> { let volume = Volume::new("\\\\?\\C:")?; let mut journal = Journal::new(volume, JournalOptions::default())?; let _ = journal.read()?; // Persist this USN so the next process run can resume here let checkpoint_usn = journal.get_next_usn(); println!("Checkpoint USN: {checkpoint_usn}"); // Free rename history older than the checkpoint journal.trim_history(Some(checkpoint_usn)); // Or clear the entire history buffer journal.trim_history(None); Ok(()) } ``` -------------------------------- ### Read NTFS USN Journal Source: https://github.com/kikijiki/ntfs-reader/blob/master/README.md Opens the C volume and its USN journal, allowing iteration over journal events. JournalOptions can customize reading behavior. ```rust let volume = Volume::new("\\\\?\\C:")?; // With `JournalOptions` you can customize things like where to start reading // from (beginning, end, specific point), the mask to use for the events and more. let mut journal = Journal::new(volume, JournalOptions::default())?; // Try to read some events. // You can call `read_sized` to use a custom buffer size. for result in journal.read()? { // Available fields are: usn, timestamp, file_id, parent_id, reason, path. } ``` -------------------------------- ### Mft::new — Load the MFT into memory Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Loads the entire Master File Table (MFT) and its bitmap into memory, applying necessary fixups. This allows for fast, O(1) lookups of file records. ```APIDOC ## Mft::new — Load the MFT into memory ### Description Reads the entire `$MFT` data stream and its bitmap from disk into a `Vec`, applies the NTFS update-sequence-array (USA) fixup to every record, and stores the result for zero-copy, read-only access. Because the whole MFT is in memory after construction, all subsequent record lookups are O(1) index operations. ### Method ```rust use ntfs_reader::mft::Mft; use ntfs_reader::volume::Volume; fn main() -> Result<(), Box> { let volume = Volume::new("\\\\.\\C:")?; let mft = Mft::new(volume)?; println!("Max records in MFT: {}", mft.max_record); // Check if a specific record number exists in the bitmap println!("Record 5 exists (root): {}", mft.record_exists(5)); println!("Record 0 exists ($MFT): {}", mft.record_exists(0)); Ok(()) } ``` ### Expected output ``` Max records in MFT: 1245184 Record 5 exists (root): true Record 0 exists ($MFT): true ``` ``` -------------------------------- ### Resolve File Metadata Without Cache using `FileInfo::new` Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Use `FileInfo::new` to resolve file metadata, including its full path by walking parent directory references. This method performs no caching and is suitable for one-off lookups or memory-constrained environments. ```rust use ntfs_reader::{file_info::FileInfo, mft::Mft, volume::Volume}; fn main() -> Result<(), Box> { let volume = Volume::new("\\\\.\\C:")?; let mft = Mft::new(volume)?; for file in mft.files().take(5) { let info = FileInfo::new(&mft, &file); println!( "[{}] {:?} size={} bytes created={:?}", if info.is_directory { "DIR " } else { "FILE" }, info.path, info.size, info.created, ); } Ok(()) } ``` -------------------------------- ### `FileInfo::with_cache` Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Resolves file metadata with path caching. This method is similar to `FileInfo::new` but accepts a mutable cache reference (e.g., `HashMapCache` or `VecCache`) to store and retrieve previously resolved directory paths, significantly speeding up path resolution during full MFT scans. ```APIDOC ## `FileInfo::with_cache` — Resolve file metadata with path caching Same as `FileInfo::new` but accepts a mutable cache reference (`HashMapCache` or `VecCache`) that stores previously resolved directory paths. Dramatically reduces path-resolution time for full MFT scans (measured ~3× faster with `VecCache` vs. no cache on large volumes). ### Method Signature `pub fn with_cache(mft: &Mft, file: &NtfsFile, cache: &mut C) -> FileInfo` ### Parameters * `mft` (&Mft) - A reference to the MFT structure. * `file` (&NtfsFile) - A reference to the `NtfsFile` record. * `cache` (&mut C) - A mutable reference to a type implementing the `PathCache` trait (e.g., `VecCache`, `HashMapCache`). ### Returns * `FileInfo` - An object containing file metadata, with the path potentially retrieved from the cache. ### Example ```rust use ntfs_reader::{ file_info::{FileInfo, VecCache, HashMapCache}, mft::Mft, volume::Volume, }; fn main() -> Result<(), Box> { let volume = Volume::new("\\\\.\\C:")?; let mft = Mft::new(volume)?; // --- VecCache: fastest (O(1) index), higher memory usage --- let mut vec_cache = VecCache::default(); vec_cache.0.resize(mft.max_record as usize, None); // pre-allocate for file in mft.files() { let info = FileInfo::with_cache(&mft, &file, &mut vec_cache); let _ = info.path; // path already resolved and cached } // --- HashMapCache: lower memory, slightly slower --- let mut map_cache = HashMapCache::default(); for file in mft.files().take(1000) { let info = FileInfo::with_cache(&mft, &file, &mut map_cache); println!("{}", info.path.display()); } Ok(()) } ``` ``` -------------------------------- ### Decode USN Reason Bitmask with `Journal::get_reason_str` Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Converts a `u32` USN reason bitmask into a human-readable, space-separated list of `USN_REASON_*` flag names. Useful for understanding filesystem event types. ```rust use ntfs_reader::journal::Journal; use windows::Win32::System::Ioctl::{ USN_REASON_DATA_OVERWRITE, USN_REASON_CLOSE, USN_REASON_FILE_CREATE, }; fn main() { let reason = USN_REASON_DATA_OVERWRITE | USN_REASON_CLOSE; println!("{}", Journal::get_reason_str(reason).trim()); // Output: USN_REASON_CLOSE USN_REASON_DATA_OVERWRITE let reason2 = USN_REASON_FILE_CREATE | USN_REASON_CLOSE; println!("{}", Journal::get_reason_str(reason2).trim()); // Output: USN_REASON_CLOSE USN_REASON_FILE_CREATE } ``` -------------------------------- ### `FileInfo::new` Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Resolves file metadata, including the full path, without using a cache. This method reads standard information and data attributes and traverses parent directory references up to the volume root. It's suitable for one-off lookups or when memory is limited. ```APIDOC ## `FileInfo::new` — Resolve file metadata without a cache Reads the `$STANDARD_INFORMATION` and `$DATA` attributes from an `NtfsFile` and walks parent-directory references up to the volume root to compute the full path. No caching is performed; every call re-walks the directory chain, making this suitable for one-off lookups or when memory is constrained. ### Method Signature `pub fn new(mft: &Mft, file: &NtfsFile) -> FileInfo` ### Parameters * `mft` (&Mft) - A reference to the MFT structure. * `file` (&NtfsFile) - A reference to the `NtfsFile` record. ### Returns * `FileInfo` - An object containing file metadata including path, size, and creation time. ### Example ```rust use ntfs_reader::{file_info::FileInfo, mft::Mft, volume::Volume}; fn main() -> Result<(), Box> { let volume = Volume::new("\\\\.\\C:")?; let mft = Mft::new(volume)?; for file in mft.files().take(5) { let info = FileInfo::new(&mft, &file); println!( "[{}] {:?} size={} bytes created={:?}", if info.is_directory { "DIR " } else { "FILE" }, info.path, info.size, info.created, ); } Ok(()) } ``` ``` -------------------------------- ### Fetch MFT Record by Number with `Mft::get_record` Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Use `Mft::get_record` to retrieve a specific MFT record by its number. It returns `Some(NtfsFile)` for valid records or `None` if the record is out of range or invalid. This is useful for looking up parent directories. ```rust use ntfs_reader::mft::Mft; use ntfs_reader::volume::Volume; use ntfs_reader::api::ROOT_RECORD; // = 5 fn main() -> Result<(), Box> { let volume = Volume::new("\\\\.\\C:")?; let mft = Mft::new(volume)?; // Fetch the root directory record (always record #5 on NTFS) if let Some(root) = mft.get_record(ROOT_RECORD) { println!("Root record #{}", root.number()); println!("Is directory: {}", root.is_directory()); println!("Is in use : {}", root.is_used()); } // Out-of-range or invalid records return None safely assert!(mft.get_record(u64::MAX).is_none()); Ok(()) } ``` -------------------------------- ### Mft::files — Iterate all live file records Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Provides a lazy iterator over all active file records in the MFT, excluding system meta-files. Each yielded record is a zero-copy view into the MFT data. ```APIDOC ## Mft::files — Iterate all live file records ### Description Returns a lazy iterator over every in-use `NtfsFile` record, skipping system meta-files (records 0–23) and records absent from the bitmap. Each yielded `NtfsFile` is a zero-copy view into the in-memory MFT buffer. ### Method ```rust use ntfs_reader::mft::Mft; use ntfs_reader::volume::Volume; use ntfs_reader::file_info::FileInfo; fn main() -> Result<(), Box> { let volume = Volume::new("\\\\.\\C:")?; let mft = Mft::new(volume)?; let mut file_count = 0usize; let mut dir_count = 0usize; for file in mft.files() { let info = FileInfo::new(&mft, &file); if info.is_directory { dir_count += 1; } else { file_count += 1; } } println!("Files : {file_count}"); println!("Directories: {dir_count}"); Ok(()) } ``` ### Expected output (varies by drive) ``` Files : 312847 Directories: 43201 ``` ``` -------------------------------- ### Journal::get_reason_str Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Decodes a USN reason bitmask into a human-readable string. ```APIDOC ## `Journal::get_reason_str` — Decode a USN reason bitmask Static helper that converts the `u32` reason bitmask from a `UsnRecord` into a human-readable space-separated list of `USN_REASON_*` flag names. ```rust use ntfs_reader::journal::Journal; use windows::Win32::System::Ioctl::{ USN_REASON_DATA_OVERWRITE, USN_REASON_CLOSE, USN_REASON_FILE_CREATE, }; fn main() { let reason = USN_REASON_DATA_OVERWRITE | USN_REASON_CLOSE; println!("{}", Journal::get_reason_str(reason).trim()); // Output: USN_REASON_CLOSE USN_REASON_DATA_OVERWRITE let reason2 = USN_REASON_FILE_CREATE | USN_REASON_CLOSE; println!("{}", Journal::get_reason_str(reason2).trim()); // Output: USN_REASON_CLOSE USN_REASON_FILE_CREATE } ``` ``` -------------------------------- ### `Mft::get_record` Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Fetches a single MFT record by its number. Returns `Some(NtfsFile)` if the record is valid and exists, otherwise returns `None`. This is useful for looking up parent directories by their MFT reference number. ```APIDOC ## `Mft::get_record` — Fetch a single MFT record by number Returns `Some(NtfsFile)` if the record number is within range and its data begins with the `FILE` signature; `None` otherwise. Useful for looking up a parent directory by its MFT reference number. ### Method Signature `pub fn get_record(&self, record_number: u64) -> Option` ### Parameters * `record_number` (u64) - The MFT record number to fetch. ### Returns * `Option` - `Some(NtfsFile)` if the record is found and valid, `None` otherwise. ### Example ```rust use ntfs_reader::mft::Mft; use ntfs_reader::volume::Volume; use ntfs_reader::api::ROOT_RECORD; // = 5 fn main() -> Result<(), Box> { let volume = Volume::new("\\\\.\\C:")?; let mft = Mft::new(volume)?; // Fetch the root directory record (always record #5 on NTFS) if let Some(root) = mft.get_record(ROOT_RECORD) { println!("Root record #{}", root.number()); println!("Is directory: {}", root.is_directory()); println!("Is in use : {}", root.is_used()); } // Out-of-range or invalid records return None safely assert!(mft.get_record(u64::MAX).is_none()); Ok(()) } ``` ``` -------------------------------- ### Journal::match_rename Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Correlates rename operations by matching USN records with USN_REASON_RENAME_OLD_NAME and USN_REASON_RENAME_NEW_NAME flags. It retrieves the previous path for a given new-name record. ```APIDOC ## `Journal::match_rename` — Correlate rename old/new name pairs When a file is renamed the USN journal emits two records: one with `USN_REASON_RENAME_OLD_NAME` and one with `USN_REASON_RENAME_NEW_NAME`. The journal internally buffers old-name records and `match_rename` finds the matching old-name record for a given new-name record, returning the previous path. ```rust use ntfs_reader::{ journal::{HistorySize, Journal, JournalOptions}, volume::Volume, }; use windows::Win32::System::Ioctl::USN_REASON_RENAME_NEW_NAME; fn main() -> Result<(), Box> { let volume = Volume::new("\\\\?\C:")?; let mut journal = Journal::new(volume, JournalOptions { max_history_size: HistorySize::Limited(1024), ..Default::default() })?; loop { let records = journal.read()?; if records.is_empty() { break; } for rec in &records { if rec.reason & USN_REASON_RENAME_NEW_NAME != 0 { if let Some(old_path) = journal.match_rename(rec) { println!("RENAMED: {:?} -> {:?}", old_path, rec.path); } } } } Ok(()) } // Sample output: // RENAMED: "C:\Temp\old_name.txt" -> "C:\Temp\new_name.txt" ``` ``` -------------------------------- ### Correlate Rename Events with Journal History Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Correlates rename old/new name pairs by buffering old-name records internally. `match_rename` finds the matching old-name record for a given new-name record, returning the previous path. ```rust use ntfs_reader::{ journal::{HistorySize, Journal, JournalOptions}, volume::Volume, }; use windows::Win32::System::Ioctl::USN_REASON_RENAME_NEW_NAME; fn main() -> Result<(), Box> { let volume = Volume::new("\\\\?\\C:")?; let mut journal = Journal::new(volume, JournalOptions { max_history_size: HistorySize::Limited(1024), ..Default::default() })?; loop { let records = journal.read()?; if records.is_empty() { break; } for rec in &records { if rec.reason & USN_REASON_RENAME_NEW_NAME != 0 { if let Some(old_path) = journal.match_rename(rec) { println!("RENAMED: {:?} -> {:?}", old_path, rec.path); } } } } Ok(()) } ``` -------------------------------- ### Poll USN Journal Records Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Polls the USN journal for new records using a default 4 KiB buffer or a custom size with `read_sized`. The loop continues until no new records are found. ```rust use ntfs_reader::{ journal::{Journal, JournalOptions}, volume::Volume, }; fn main() -> Result<(), Box> { let volume = Volume::new("\\\\?\\C:")?; let mut journal = Journal::new(volume, JournalOptions::default())?; // Poll in a loop (in a real program, add a sleep / event wait between iterations) loop { let records = journal.read()?; if records.is_empty() { break; // no more events right now } for rec in &records { println!( "USN={} path={} reason={}", rec.usn, rec.path.display(), Journal::get_reason_str(rec.reason).trim(), ); } } // Use read_sized for a larger buffer to reduce ioctl round-trips let _records = journal.read_sized::<65536>()?; Ok(()) } ``` -------------------------------- ### Journal::read / Journal::read_sized Source: https://context7.com/kikijiki/ntfs-reader/llms.txt Polls for USN records using FSCTL_READ_USN_JOURNAL with a specified buffer size. It waits on the IOCP, deserialises USN V2/V3 records, and returns a Vec. The internal cursor is advanced for subsequent reads. ```APIDOC ## `Journal::read` / `Journal::read_sized` — Poll for USN records Fires a `FSCTL_READ_USN_JOURNAL` ioctl with a 4 KiB buffer (or a custom `const` size via `read_sized`), waits on the IOCP, then deserialises USN V2/V3 records. Returns `Vec` for the current batch; advances the internal cursor so the next call continues from where the last left off. ```rust use ntfs_reader::{ journal::{Journal, JournalOptions}, volume::Volume, }; fn main() -> Result<(), Box> { let volume = Volume::new("\\\\?\C:")?; let mut journal = Journal::new(volume, JournalOptions::default())?; // Poll in a loop (in a real program, add a sleep / event wait between iterations) loop { let records = journal.read()?; if records.is_empty() { break; // no more events right now } for rec in &records { println!( "USN={} path={} reason={}", rec.usn, rec.path.display(), Journal::get_reason_str(rec.reason).trim(), ); } } // Use read_sized for a larger buffer to reduce ioctl round-trips let _records = journal.read_sized::<65536>()?; Ok(()) } // Sample output: // USN=12345678 path=\\?\C:\Users\Alice\Documents\report.docx reason=USN_REASON_DATA_OVERWRITE USN_REASON_CLOSE // USN=12345696 path=\\?\C:\Temp\tmp123.dat reason=USN_REASON_FILE_DELETE USN_REASON_CLOSE ``` ``` -------------------------------- ### Handle NTFS Reader Errors with `NtfsReaderError` Source: https://context7.com/kikijiki/ntfs-reader/llms.txt All fallible functions return `NtfsReaderResult`, which is an alias for `Result`. This enum wraps OS-level errors and adds NTFS-specific variants for robust error management. ```rust use ntfs_reader::{errors::NtfsReaderError, mft::Mft, volume::Volume}; fn open_volume(path: &str) -> Result { let volume = Volume::new(path)?; let mft = Mft::new(volume)?; Ok(mft) } fn main() { match open_volume("\\\\.\\C:") { Ok(mft) => println!("Opened MFT, {} records", mft.max_record), Err(NtfsReaderError::ElevationError) => { eprintln!("Run as administrator"); } Err(NtfsReaderError::CorruptMftRecord { number }) => { eprintln!("Corrupt record #{number}"); } Err(NtfsReaderError::MissingMftAttribute(name)) => { eprintln!("MFT missing attribute: {name}"); } Err(e) => eprintln!("Other error: {e}"), } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.