├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── UsnParser.sln ├── UsnParser ├── Cache │ └── LRUCache.cs ├── Enumeration │ ├── BaseEnumerable.cs │ ├── BaseEnumerationOptions.cs │ ├── BaseEnumerator.cs │ ├── ChangeJournalEnumerable.cs │ ├── ChangeJournalEnumerationOptions.cs │ ├── ChangeJournalEnumerator.cs │ ├── MasterFileTableEnumerable.cs │ ├── MasterFileTableEnumerationOptions.cs │ ├── MasterFileTableEnumerator.cs │ └── UsnEntry.cs ├── Extensions │ └── ConsoleExtension.cs ├── FilterOptions.cs ├── Native │ ├── ACCESS_MASK.cs │ ├── BY_HANDLE_FILE_INFORMATION.cs │ ├── CREATE_USN_JOURNAL_DATA.cs │ ├── FILE_INFORMATION_CLASS.cs │ ├── FILE_NAME_INFORMATION.cs │ ├── FileAccess.cs │ ├── FileFlagsAndAttributes.cs │ ├── IO_STATUS_BLOCK.cs │ ├── Kernel32.cs │ ├── LongFileTime.cs │ ├── MFT_ENUM_DATA_V0.cs │ ├── NtFileCreateOptions.cs │ ├── NtFileMode.cs │ ├── Ntdll.cs │ ├── OBJECT_ATTRIBUTES.cs │ ├── ObjectAttribute.cs │ ├── READ_USN_JOURNAL_DATA_V0.cs │ ├── UNICODE_STRING.cs │ ├── USN_JOURNAL_DATA_V0.cs │ ├── USN_RECORD_V2.cs │ ├── UsnReason.cs │ ├── UsnSource.cs │ └── Win32Error.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── UsnJournal.cs └── UsnParser.csproj └── publish.ps1 /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | [*.{cs,vb}] 3 | #### Naming styles #### 4 | 5 | # Naming rules 6 | 7 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion 8 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface 9 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i 10 | 11 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion 12 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types 13 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case 14 | 15 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion 16 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members 17 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case 18 | 19 | # Symbol specifications 20 | 21 | dotnet_naming_symbols.interface.applicable_kinds = interface 22 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 23 | dotnet_naming_symbols.interface.required_modifiers = 24 | 25 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum 26 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 27 | dotnet_naming_symbols.types.required_modifiers = 28 | 29 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method 30 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 31 | dotnet_naming_symbols.non_field_members.required_modifiers = 32 | 33 | # Naming styles 34 | 35 | dotnet_naming_style.begins_with_i.required_prefix = I 36 | dotnet_naming_style.begins_with_i.required_suffix = 37 | dotnet_naming_style.begins_with_i.word_separator = 38 | dotnet_naming_style.begins_with_i.capitalization = pascal_case 39 | 40 | dotnet_naming_style.pascal_case.required_prefix = 41 | dotnet_naming_style.pascal_case.required_suffix = 42 | dotnet_naming_style.pascal_case.word_separator = 43 | dotnet_naming_style.pascal_case.capitalization = pascal_case 44 | 45 | dotnet_naming_style.pascal_case.required_prefix = 46 | dotnet_naming_style.pascal_case.required_suffix = 47 | dotnet_naming_style.pascal_case.word_separator = 48 | dotnet_naming_style.pascal_case.capitalization = pascal_case 49 | dotnet_style_operator_placement_when_wrapping = beginning_of_line 50 | tab_width = 4 51 | indent_size = 4 52 | end_of_line = crlf 53 | dotnet_style_coalesce_expression = true:suggestion 54 | dotnet_style_null_propagation = true:suggestion 55 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion 56 | dotnet_style_prefer_auto_properties = true:silent 57 | dotnet_style_object_initializer = true:suggestion 58 | dotnet_style_collection_initializer = true:suggestion 59 | dotnet_style_prefer_simplified_boolean_expressions = true:suggestion 60 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent 61 | dotnet_style_prefer_conditional_expression_over_return = true:silent 62 | dotnet_style_explicit_tuple_names = true:suggestion 63 | dotnet_style_prefer_inferred_tuple_names = true:suggestion 64 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion 65 | dotnet_style_prefer_compound_assignment = true:suggestion 66 | dotnet_style_prefer_simplified_interpolation = true:suggestion 67 | dotnet_style_namespace_match_folder = true:suggestion 68 | dotnet_style_readonly_field = true:suggestion 69 | dotnet_style_predefined_type_for_locals_parameters_members = true:silent 70 | dotnet_style_predefined_type_for_member_access = true:silent 71 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent 72 | dotnet_diagnostic.CA1806.severity = warning 73 | dotnet_diagnostic.CA1821.severity = warning 74 | dotnet_diagnostic.CA1822.severity = warning 75 | dotnet_diagnostic.CA1826.severity = warning 76 | dotnet_diagnostic.CA1827.severity = warning 77 | dotnet_diagnostic.CA1828.severity = warning 78 | dotnet_diagnostic.CA1829.severity = warning 79 | dotnet_diagnostic.CA1830.severity = warning 80 | dotnet_diagnostic.CA1832.severity = warning 81 | dotnet_diagnostic.CA1833.severity = warning 82 | dotnet_diagnostic.CA1834.severity = warning 83 | dotnet_diagnostic.CA1835.severity = warning 84 | dotnet_diagnostic.CA1836.severity = warning 85 | dotnet_diagnostic.CA1837.severity = warning 86 | dotnet_diagnostic.CA1838.severity = warning 87 | dotnet_diagnostic.CA1839.severity = warning 88 | dotnet_diagnostic.CA1840.severity = warning 89 | dotnet_diagnostic.CA1842.severity = warning 90 | dotnet_diagnostic.CA1843.severity = warning 91 | dotnet_diagnostic.CA1844.severity = warning 92 | dotnet_diagnostic.CA1846.severity = warning 93 | dotnet_diagnostic.CA1847.severity = warning 94 | dotnet_diagnostic.CA1848.severity = warning 95 | dotnet_diagnostic.CA1850.severity = warning 96 | dotnet_diagnostic.CA1852.severity = suggestion 97 | dotnet_diagnostic.CA1853.severity = warning 98 | dotnet_diagnostic.CA1854.severity = warning 99 | dotnet_diagnostic.CA1401.severity = warning 100 | dotnet_diagnostic.CA1419.severity = warning 101 | dotnet_diagnostic.CA1421.severity = error 102 | dotnet_style_prefer_collection_expression = true:suggestion 103 | 104 | [*.cs] 105 | csharp_indent_labels = one_less_than_current 106 | csharp_space_around_binary_operators = before_and_after 107 | csharp_using_directive_placement = outside_namespace:silent 108 | csharp_prefer_simple_using_statement = true:suggestion 109 | csharp_prefer_braces = true:silent 110 | csharp_style_namespace_declarations = block_scoped:silent 111 | csharp_style_prefer_method_group_conversion = true:silent 112 | csharp_style_prefer_top_level_statements = true:silent 113 | csharp_style_expression_bodied_methods = false:silent 114 | csharp_style_expression_bodied_constructors = false:silent 115 | csharp_style_expression_bodied_operators = false:silent 116 | csharp_style_expression_bodied_properties = true:silent 117 | csharp_style_expression_bodied_indexers = true:silent 118 | csharp_style_expression_bodied_accessors = true:silent 119 | csharp_style_expression_bodied_lambdas = true:silent 120 | csharp_style_expression_bodied_local_functions = false:silent 121 | csharp_style_throw_expression = true:suggestion 122 | csharp_style_prefer_null_check_over_type_check = true:suggestion 123 | csharp_prefer_simple_default_expression = true:suggestion 124 | csharp_style_prefer_local_over_anonymous_function = true:suggestion 125 | csharp_style_prefer_index_operator = true:suggestion 126 | csharp_style_prefer_range_operator = true:suggestion 127 | csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion 128 | csharp_style_prefer_tuple_swap = true:suggestion 129 | csharp_style_prefer_utf8_string_literals = true:suggestion 130 | csharp_style_inlined_variable_declaration = true:suggestion 131 | csharp_style_deconstructed_variable_declaration = true:suggestion 132 | csharp_style_unused_value_assignment_preference = discard_variable:suggestion 133 | csharp_style_unused_value_expression_statement_preference = discard_variable:silent 134 | csharp_prefer_static_local_function = true:suggestion 135 | csharp_style_prefer_readonly_struct = true:suggestion 136 | csharp_style_prefer_readonly_struct_member = true:suggestion 137 | dotnet_diagnostic.CA1001.severity = warning 138 | dotnet_diagnostic.CA1805.severity = warning 139 | dotnet_diagnostic.CA1824.severity = warning 140 | dotnet_diagnostic.CA1825.severity = warning 141 | dotnet_diagnostic.CA1841.severity = warning 142 | dotnet_diagnostic.CA1845.severity = warning 143 | dotnet_diagnostic.CA1855.severity = warning 144 | dotnet_diagnostic.SYSLIB1054.severity = suggestion 145 | dotnet_diagnostic.CA1507.severity = warning 146 | csharp_style_prefer_primary_constructors = true:suggestion 147 | dotnet_diagnostic.IL3050.severity = error 148 | dotnet_diagnostic.IL3051.severity = error 149 | dotnet_diagnostic.IL3056.severity = error -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #ignore thumbnails created by windows 2 | Thumbs.db 3 | #Ignore files build by Visual Studio 4 | *.obj 5 | #*.exe 6 | *.pdb 7 | *.user 8 | *.aps 9 | *.pch 10 | *.vspscc 11 | *_i.c 12 | *_p.c 13 | *.ncb 14 | *.suo 15 | *.tlb 16 | *.tlh 17 | *.bak 18 | *.cache 19 | *.ilk 20 | *.log 21 | #*.dll 22 | #*.lib 23 | *.sbr 24 | bin 25 | obj 26 | .vs 27 | .vscode 28 | 29 | /UsnParser/publish 30 | /tool 31 | /publish 32 | /release 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Fu Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Windows USN Change Journal Parser 2 | 3 | A command utility for NTFS/ReFS to search the MFT & monitoring the changes of USN Journal. 4 | 5 | ## Download 6 | 7 | Latest version can be downloaded from the [releases/latest](https://github.com/wangfu91/UsnParser/releases/latest) page. 8 | 9 | ## Usage 10 | 11 | ``` 12 | Usage: UsnParser [command] [options] 13 | 14 | Options: 15 | --version Show version information. 16 | -h|--help Show help information. 17 | 18 | Commands: 19 | monitor Monitor real-time USN journal changes 20 | read Read history USN journal entries 21 | search Search the Master File Table 22 | 23 | Run 'UsnParser [command] -h|--help' for more information about a command. 24 | ``` 25 | 26 | ### Example 27 | 28 | ```bash 29 | # Search through Master File Table of volume D, print out all the files who's extension is ".xlsx". 30 | UsnParser search D: -f *.xlsx 31 | ``` 32 | 33 | ```bash 34 | # Print out the change history for file "Report.docx" in the USN journal of volume D. 35 | UsnParser read D: -f Report.docx 36 | ``` 37 | 38 | ```bash 39 | # Monitor realtime USN reacords of volume C. 40 | UsnParser monitor C: 41 | ``` 42 | 43 | ```bash 44 | # Monitor realtime USN records of volume C with a filter for txt files whose name starts with "abc". 45 | UsnParser monitor C: -f abc*.txt 46 | ``` 47 | 48 | ## Dependencies 49 | 50 | * [DotNet.Glob](https://github.com/dazinator/DotNet.Glob) 51 | 52 | * [McMaster.Extensions.CommandLineUtils](https://github.com/natemcmaster/CommandLineUtils) 53 | 54 | -------------------------------------------------------------------------------- /UsnParser.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31612.314 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UsnParser", "UsnParser\UsnParser.csproj", "{D6333950-3FCB-4CFB-A7B4-484E1451565D}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FD9527BA-2514-45B9-A5F8-D459C4143872}" 9 | ProjectSection(SolutionItems) = preProject 10 | .editorconfig = .editorconfig 11 | Publish.ps1 = Publish.ps1 12 | README.md = README.md 13 | EndProjectSection 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {D6333950-3FCB-4CFB-A7B4-484E1451565D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {D6333950-3FCB-4CFB-A7B4-484E1451565D}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {D6333950-3FCB-4CFB-A7B4-484E1451565D}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {D6333950-3FCB-4CFB-A7B4-484E1451565D}.Release|Any CPU.Build.0 = Release|Any CPU 25 | EndGlobalSection 26 | GlobalSection(SolutionProperties) = preSolution 27 | HideSolutionNode = FALSE 28 | EndGlobalSection 29 | GlobalSection(ExtensibilityGlobals) = postSolution 30 | SolutionGuid = {21F19715-1F27-473D-A3CD-6639C065D383} 31 | EndGlobalSection 32 | EndGlobal 33 | -------------------------------------------------------------------------------- /UsnParser/Cache/LRUCache.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace UsnParser.Cache 4 | { 5 | // Adapted from: 6 | // https://github.com/microsoft/botbuilder-dotnet/blob/main/libraries/AdaptiveExpressions/LRUCache.cs 7 | 8 | /// 9 | /// A least-recently-used cache stored like a dictionary. 10 | /// 11 | /// The type of the key to the cached item. 12 | /// The type of the cached item. 13 | public sealed class LRUCache where TKey : notnull 14 | { 15 | /// 16 | /// Default maximum number of elements to cache. 17 | /// 18 | private const int DefaultCapacity = 255; 19 | 20 | private readonly object _lockObj = new(); 21 | private readonly int _capacity; 22 | private readonly Dictionary _cacheMap; 23 | private readonly LinkedList _cacheList; 24 | 25 | /// 26 | /// Initializes a new instance of the class. 27 | /// 28 | public LRUCache() 29 | : this(DefaultCapacity) 30 | { 31 | } 32 | 33 | /// 34 | /// Initializes a new instance of the class. 35 | /// 36 | /// Maximum number of elements to cache. 37 | public LRUCache(int capacity) 38 | { 39 | _capacity = capacity > 0 ? capacity : DefaultCapacity; 40 | _cacheMap = []; 41 | _cacheList = new LinkedList(); 42 | } 43 | 44 | /// 45 | /// Gets the value associated with the specified key. 46 | /// 47 | /// The key of the value to get. 48 | /// When this method returns, contains the value associated with 49 | /// the specified key, if the key is found; otherwise, the default value for the 50 | /// type of the parameter. 51 | /// true if contains an element with the specified key; otherwise, false. 52 | public bool TryGet(TKey key, out TValue? value) 53 | { 54 | lock (_lockObj) 55 | { 56 | if (_cacheMap.TryGetValue(key, out var entry)) 57 | { 58 | Touch(entry.Node); 59 | value = entry.Value; 60 | return true; 61 | } 62 | } 63 | 64 | value = default; 65 | return false; 66 | } 67 | 68 | /// 69 | /// Adds the specified key and value to the cache. 70 | /// 71 | /// The key of the element to add. 72 | /// The value of the element to add. 73 | public void Set(TKey key, TValue value) 74 | { 75 | lock (_lockObj) 76 | { 77 | if (!_cacheMap.TryGetValue(key, out var entry)) 78 | { 79 | LinkedListNode node; 80 | if (_cacheMap.Count >= _capacity) 81 | { 82 | node = _cacheList.Last!; 83 | _cacheMap.Remove(node.Value); 84 | _cacheList.RemoveLast(); 85 | node.Value = key; 86 | } 87 | else 88 | { 89 | node = new LinkedListNode(key); 90 | } 91 | 92 | _cacheList.AddFirst(node); 93 | _cacheMap.Add(key, new Entry(node, value)); 94 | } 95 | else 96 | { 97 | entry.Value = value; 98 | _cacheMap[key] = entry; 99 | Touch(entry.Node); 100 | } 101 | } 102 | } 103 | 104 | private void Touch(LinkedListNode node) 105 | { 106 | if (node != _cacheList.First) 107 | { 108 | _cacheList.Remove(node); 109 | _cacheList.AddFirst(node); 110 | } 111 | } 112 | 113 | private struct Entry(LinkedListNode node, TValue value) 114 | { 115 | public LinkedListNode Node = node; 116 | public TValue Value = value; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /UsnParser/Enumeration/BaseEnumerable.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace UsnParser.Enumeration 6 | { 7 | public abstract class BaseEnumerable : IEnumerable 8 | { 9 | protected readonly SafeFileHandle _volumeRootHandle; 10 | protected readonly FindPredicate? _shouldIncludePredicate; 11 | 12 | public BaseEnumerable(SafeFileHandle volumeRootHandle, FindPredicate? shouldIncludePredicate = null) 13 | { 14 | _volumeRootHandle = volumeRootHandle; 15 | _shouldIncludePredicate = shouldIncludePredicate; 16 | } 17 | 18 | public abstract IEnumerator GetEnumerator(); 19 | 20 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 21 | 22 | public delegate bool FindPredicate(UsnEntry entry); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /UsnParser/Enumeration/BaseEnumerationOptions.cs: -------------------------------------------------------------------------------- 1 | namespace UsnParser.Enumeration 2 | { 3 | public abstract class BaseEnumerationOptions 4 | { 5 | public int BufferSize { get; set; } = 256 * 1024; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /UsnParser/Enumeration/BaseEnumerator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Runtime.InteropServices; 6 | using UsnParser.Native; 7 | 8 | namespace UsnParser.Enumeration 9 | { 10 | public unsafe abstract class BaseEnumerator : IEnumerator 11 | { 12 | protected bool _disposed; 13 | protected nint _buffer; 14 | protected readonly int _bufferLength; 15 | protected readonly SafeFileHandle _volumeRootHandle; 16 | protected uint _offset; 17 | protected uint _bytesRead; 18 | protected USN_RECORD_V2* _record; 19 | protected UsnEntry? _current; 20 | 21 | public UsnEntry Current => _current!; 22 | 23 | object IEnumerator.Current => Current; 24 | 25 | public BaseEnumerator(SafeFileHandle volumeRootHandle, int bufferSize) 26 | { 27 | _volumeRootHandle = volumeRootHandle; 28 | _bufferLength = bufferSize; 29 | _buffer = Marshal.AllocHGlobal(_bufferLength); 30 | } 31 | 32 | public bool MoveNext() 33 | { 34 | do 35 | { 36 | FindNextEntry(); 37 | if (_record == null) return false; 38 | 39 | var entry = new UsnEntry(_record); 40 | if (ShouldIncludeEntry(entry)) 41 | { 42 | _current = entry; 43 | return true; 44 | } 45 | } 46 | while (true); 47 | } 48 | 49 | public void Reset() 50 | { 51 | throw new NotSupportedException(); 52 | } 53 | 54 | protected unsafe abstract void FindNextEntry(); 55 | 56 | protected abstract bool ShouldIncludeEntry(UsnEntry entry); 57 | 58 | // Override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources 59 | ~BaseEnumerator() 60 | { 61 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 62 | Dispose(disposing: false); 63 | } 64 | 65 | protected virtual void Dispose(bool disposing) 66 | { 67 | if (_disposed) return; 68 | 69 | if (disposing) 70 | { 71 | // Dispose managed state (managed objects) 72 | } 73 | 74 | // Free unmanaged resources (unmanaged objects) and override finalizer 75 | // Set large fields to null 76 | if (_buffer != default) 77 | { 78 | Marshal.FreeHGlobal(_buffer); 79 | _buffer = default; 80 | } 81 | 82 | _disposed = true; 83 | } 84 | 85 | public void Dispose() 86 | { 87 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 88 | Dispose(disposing: true); 89 | GC.SuppressFinalize(this); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /UsnParser/Enumeration/ChangeJournalEnumerable.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | 5 | namespace UsnParser.Enumeration 6 | { 7 | public class ChangeJournalEnumerable : BaseEnumerable 8 | { 9 | private ChangeJournalEnumerator? _enumerator; 10 | private readonly ulong _changeJournalId; 11 | private readonly ChangeJournalEnumerationOptions _options; 12 | 13 | public ChangeJournalEnumerable(SafeFileHandle volumeRootHandle, ulong usnJournalId, ChangeJournalEnumerationOptions? options = null, FindPredicate? shouldIncludePredicate = null) 14 | : base(volumeRootHandle, shouldIncludePredicate) 15 | { 16 | _changeJournalId = usnJournalId; 17 | _options = options ?? ChangeJournalEnumerationOptions.Default; 18 | } 19 | 20 | public override IEnumerator GetEnumerator() 21 | { 22 | return Interlocked.Exchange(ref _enumerator, null) ?? new ChangeJournalEnumerator(_volumeRootHandle, _changeJournalId, _options, _shouldIncludePredicate); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /UsnParser/Enumeration/ChangeJournalEnumerationOptions.cs: -------------------------------------------------------------------------------- 1 | namespace UsnParser.Enumeration 2 | { 3 | public class ChangeJournalEnumerationOptions : BaseEnumerationOptions 4 | { 5 | public static ChangeJournalEnumerationOptions Default { get; } = new(); 6 | 7 | public bool ReturnOnlyOnClose { get; set; } 8 | 9 | public ulong BytesToWaitFor { get; set; } 10 | 11 | public ulong Timeout { get; set; } 12 | 13 | public long StartUsn { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /UsnParser/Enumeration/ChangeJournalEnumerator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System.ComponentModel; 3 | using System.Runtime.InteropServices; 4 | using UsnParser.Native; 5 | using static UsnParser.Enumeration.BaseEnumerable; 6 | using static UsnParser.Native.Kernel32; 7 | 8 | namespace UsnParser.Enumeration 9 | { 10 | internal unsafe class ChangeJournalEnumerator : BaseEnumerator 11 | { 12 | private readonly ChangeJournalEnumerationOptions _options; 13 | private long _nextStartUsn; 14 | private readonly ulong _usnJournalId; 15 | private readonly FindPredicate? _shouldIncludePredicate; 16 | 17 | public ChangeJournalEnumerator(SafeFileHandle volumeRootHandle, ulong usnJournalId, ChangeJournalEnumerationOptions options, FindPredicate? shouldIncludePredicate) 18 | : base(volumeRootHandle, options.BufferSize) 19 | { 20 | _usnJournalId = usnJournalId; 21 | _options = options; 22 | _nextStartUsn = options.StartUsn; 23 | _shouldIncludePredicate = shouldIncludePredicate; 24 | } 25 | 26 | private unsafe bool GetData() 27 | { 28 | var readData = new READ_USN_JOURNAL_DATA_V0 29 | { 30 | StartUsn = _nextStartUsn, 31 | ReasonMask = USN_REASON_MASK_ALL, 32 | ReturnOnlyOnClose = _options.ReturnOnlyOnClose ? 1u : 0u, 33 | Timeout = _options.Timeout, 34 | BytesToWaitFor = _options.BytesToWaitFor, 35 | UsnJournalID = _usnJournalId 36 | }; 37 | var readDataSize = Marshal.SizeOf(readData); 38 | var readDataBuffer = Marshal.AllocHGlobal(readDataSize); 39 | try 40 | { 41 | Marshal.StructureToPtr(readData, readDataBuffer, true); 42 | var success = DeviceIoControl(_volumeRootHandle, 43 | FSCTL_READ_USN_JOURNAL, 44 | readDataBuffer, 45 | readDataSize, 46 | _buffer, 47 | _bufferLength, 48 | out _bytesRead, 49 | nint.Zero); 50 | 51 | if (!success) 52 | { 53 | var error = Marshal.GetLastWin32Error(); 54 | if (error != (int)Win32Error.ERROR_HANDLE_EOF) 55 | { 56 | throw new Win32Exception(error); 57 | } 58 | } 59 | 60 | return success; 61 | } 62 | finally 63 | { 64 | Marshal.FreeHGlobal(readDataBuffer); 65 | } 66 | } 67 | 68 | protected override unsafe void FindNextEntry() 69 | { 70 | if (_record != null && _offset < _bytesRead) 71 | { 72 | _record = (USN_RECORD_V2*)((byte*)_record + _record->RecordLength); 73 | _offset += _record->RecordLength; 74 | return; 75 | } 76 | 77 | // We need read more data 78 | if (GetData()) 79 | { 80 | // The USN returned as the first item in the output buffer is the USN of the next record number to be retrieved. 81 | // Use this value to continue reading records from the end boundary forward. 82 | _nextStartUsn = *(long*)_buffer; 83 | _offset = sizeof(long); 84 | if (_offset < _bytesRead) 85 | { 86 | _record = (USN_RECORD_V2*)(_buffer + _offset); 87 | _offset += _record->RecordLength; 88 | return; 89 | } 90 | } 91 | 92 | // EOF, no more records 93 | _record = default; 94 | } 95 | 96 | protected override bool ShouldIncludeEntry(UsnEntry entry) => 97 | _shouldIncludePredicate?.Invoke(entry) ?? true; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /UsnParser/Enumeration/MasterFileTableEnumerable.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | 5 | namespace UsnParser.Enumeration 6 | { 7 | public class MasterFileTableEnumerable : BaseEnumerable 8 | { 9 | private MasterFileTableEnumerator? _enumerator; 10 | private readonly MasterFileTableEnumerationOptions _options; 11 | private readonly long _highUsn; 12 | 13 | public MasterFileTableEnumerable(SafeFileHandle volumeRootHandle, long highUsn, MasterFileTableEnumerationOptions? options = null, FindPredicate? shouldIncludePredicate = null) 14 | : base(volumeRootHandle, shouldIncludePredicate) 15 | { 16 | _highUsn = highUsn; 17 | _options = options ?? MasterFileTableEnumerationOptions.Default; 18 | } 19 | 20 | public override IEnumerator GetEnumerator() 21 | { 22 | return Interlocked.Exchange(ref _enumerator, null) ?? new MasterFileTableEnumerator(_volumeRootHandle, _highUsn, _options, _shouldIncludePredicate); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /UsnParser/Enumeration/MasterFileTableEnumerationOptions.cs: -------------------------------------------------------------------------------- 1 | namespace UsnParser.Enumeration 2 | { 3 | public class MasterFileTableEnumerationOptions : BaseEnumerationOptions 4 | { 5 | public static MasterFileTableEnumerationOptions Default { get; } = new(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /UsnParser/Enumeration/MasterFileTableEnumerator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System.ComponentModel; 3 | using System.Runtime.InteropServices; 4 | using UsnParser.Native; 5 | using static UsnParser.Enumeration.BaseEnumerable; 6 | using static UsnParser.Native.Kernel32; 7 | 8 | namespace UsnParser.Enumeration 9 | { 10 | internal unsafe class MasterFileTableEnumerator : BaseEnumerator 11 | { 12 | private ulong _nextStartFileId; 13 | private readonly long _highUsn; 14 | private readonly MasterFileTableEnumerationOptions _options; 15 | private readonly FindPredicate? _shouldIncludePredicate; 16 | 17 | public MasterFileTableEnumerator(SafeFileHandle volumeRootHandle, long highUsn, MasterFileTableEnumerationOptions options, FindPredicate? shouldIncludePredicate) 18 | : base(volumeRootHandle, options.BufferSize) 19 | { 20 | _highUsn = highUsn; 21 | _options = options; 22 | _shouldIncludePredicate = shouldIncludePredicate; 23 | } 24 | 25 | private unsafe bool GetData() 26 | { 27 | // To enumerate files on a volume, use the FSCTL_ENUM_USN_DATA operation one or more times. 28 | // On the first call, set the starting point, the StartFileReferenceNumber member of the MFT_ENUM_DATA structure, to (DWORDLONG)0. 29 | var mftEnumData = new MFT_ENUM_DATA_V0 30 | { 31 | StartFileReferenceNumber = _nextStartFileId, 32 | LowUsn = 0, 33 | HighUsn = _highUsn 34 | }; 35 | var mftEnumDataSize = Marshal.SizeOf(mftEnumData); 36 | var mftDataBuffer = Marshal.AllocHGlobal(mftEnumDataSize); 37 | 38 | try 39 | { 40 | Marshal.StructureToPtr(mftEnumData, mftDataBuffer, true); 41 | 42 | var success = DeviceIoControl( 43 | _volumeRootHandle, 44 | FSCTL_ENUM_USN_DATA, 45 | mftDataBuffer, 46 | mftEnumDataSize, 47 | _buffer, 48 | _bufferLength, 49 | out _bytesRead, 50 | nint.Zero); 51 | 52 | if (!success) 53 | { 54 | var error = Marshal.GetLastWin32Error(); 55 | if (error != (int)Win32Error.ERROR_HANDLE_EOF) 56 | { 57 | throw new Win32Exception(error); 58 | } 59 | } 60 | 61 | return success; 62 | } 63 | finally 64 | { 65 | Marshal.FreeHGlobal(mftDataBuffer); 66 | } 67 | } 68 | 69 | protected override unsafe void FindNextEntry() 70 | { 71 | if (_record != null && _offset < _bytesRead) 72 | { 73 | _record = (USN_RECORD_V2*)((byte*)_record + _record->RecordLength); 74 | _offset += _record->RecordLength; 75 | return; 76 | } 77 | 78 | // We need read more data 79 | if (GetData()) 80 | { 81 | // Each call to FSCTL_ENUM_USN_DATA retrieves the starting point for the subsequent call as the first entry in the output buffer. 82 | _nextStartFileId = *(ulong*)_buffer; 83 | _offset = sizeof(ulong); 84 | if (_offset < _bytesRead) 85 | { 86 | _record = (USN_RECORD_V2*)(_buffer + _offset); 87 | _offset += _record->RecordLength; 88 | return; 89 | } 90 | } 91 | 92 | // EOF, no more records 93 | _record = default; 94 | } 95 | 96 | protected override bool ShouldIncludeEntry(UsnEntry entry) => 97 | _shouldIncludePredicate?.Invoke(entry) ?? true; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /UsnParser/Enumeration/UsnEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UsnParser.Native; 3 | 4 | namespace UsnParser.Enumeration 5 | { 6 | public unsafe class UsnEntry 7 | { 8 | public uint RecordLength { get; } 9 | 10 | public long USN { get; } 11 | 12 | public DateTimeOffset TimeStamp { get; set; } 13 | 14 | public ulong FileReferenceNumber { get; } 15 | 16 | public ulong ParentFileReferenceNumber { get; } 17 | 18 | public UsnReason Reason { get; } 19 | 20 | public UsnSource SourceInfo { get; } 21 | 22 | public uint SecurityId { get; } 23 | 24 | public string FileName { get; } 25 | 26 | private FileFlagsAndAttributes Attributes { get; } 27 | 28 | public bool IsFolder => (Attributes & FileFlagsAndAttributes.FILE_ATTRIBUTE_DIRECTORY) != 0; 29 | 30 | public bool IsHidden => (Attributes & FileFlagsAndAttributes.FILE_ATTRIBUTE_HIDDEN) != 0; 31 | 32 | public UsnEntry(USN_RECORD_V2* record) 33 | { 34 | RecordLength = record->RecordLength; 35 | FileReferenceNumber = record->FileReferenceNumber; 36 | ParentFileReferenceNumber = record->ParentFileReferenceNumber; 37 | USN = record->Usn; 38 | TimeStamp = record->TimeStamp.ToDateTimeOffset(); 39 | Reason = record->Reason; 40 | SourceInfo = record->SourceInfo; 41 | SecurityId = record->SecurityId; 42 | Attributes = record->FileAttributes; 43 | FileName = record->FileName.ToString(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /UsnParser/Extensions/ConsoleExtension.cs: -------------------------------------------------------------------------------- 1 | using McMaster.Extensions.CommandLineUtils; 2 | using System; 3 | using UsnParser.Enumeration; 4 | using UsnParser.Extensions; 5 | using UsnParser.Native; 6 | 7 | namespace UsnParser.Extensions 8 | { 9 | public static class ConsoleExtension 10 | { 11 | private static void WriteLine(this IConsole console, ConsoleColor color, string message) 12 | { 13 | console.ForegroundColor = color; 14 | console.WriteLine(message); 15 | console.ResetColor(); 16 | } 17 | 18 | public static void PrintError(this IConsole console, string message) 19 | { 20 | console.WriteLine(ConsoleColor.Red, message); 21 | } 22 | 23 | public static void PrintUsnJournalData(this IConsole console, USN_JOURNAL_DATA_V0 usnData) 24 | { 25 | console.WriteLine($"{"Journal ID",-20}: 0x{usnData.UsnJournalID:x16}"); 26 | console.WriteLine($"{"First USN",-20}: {usnData.FirstUsn}"); 27 | console.WriteLine($"{"Next USN",-20}: {usnData.NextUsn}"); 28 | console.WriteLine($"{"Lowest Valid USN",-20}: {usnData.LowestValidUsn}"); 29 | console.WriteLine($"{"Max USN",-20}: {usnData.MaxUsn}"); 30 | console.WriteLine($"{"Max Size",-20}: {usnData.MaximumSize}"); 31 | console.WriteLine($"{"Allocation Delta",-20}: {usnData.AllocationDelta}"); 32 | } 33 | 34 | public static void PrintUsnEntryBasic(this IConsole console, UsnJournal usnJournal, UsnEntry usnEntry) 35 | { 36 | console.WriteLine(); 37 | console.WriteLine($"{"Type",-20}: {(usnEntry.IsFolder ? "Directory" : "File")}"); 38 | if (usnJournal.TryGetPath(usnEntry, out var path)) 39 | { 40 | console.WriteLine($"{"Path",-20}: {path}"); 41 | } 42 | console.WriteLine($"{"File ID",-20}: 0x{usnEntry.FileReferenceNumber:x}"); 43 | console.WriteLine($"{"Parent File ID",-20}: 0x{usnEntry.ParentFileReferenceNumber:x}"); 44 | } 45 | 46 | public static void PrintUsnEntryFull(this IConsole console, UsnJournal usnJournal, UsnEntry usnEntry) 47 | { 48 | console.WriteLine(); 49 | console.WriteLine($"{"USN",-20}: {usnEntry.USN}"); 50 | console.WriteLine($"{"Type",-20}: {(usnEntry.IsFolder ? "Directory" : "File")}"); 51 | if (usnJournal.TryGetPath(usnEntry, out var path)) 52 | { 53 | console.WriteLine($"{"Path",-20}: {path}"); 54 | } 55 | 56 | console.WriteLine($"{"Timestamp",-20}: {usnEntry.TimeStamp.ToLocalTime()}"); 57 | 58 | console.WriteLine($"{"File ID",-20}: 0x{usnEntry.FileReferenceNumber:x}"); 59 | console.WriteLine($"{"Parent File ID",-20}: 0x{usnEntry.ParentFileReferenceNumber:x}"); 60 | 61 | var reason = usnEntry.Reason.ToString().Replace(',', '|'); 62 | console.WriteLine($"{"Reason",-20}: {reason}"); 63 | 64 | var sourceInfo = usnEntry.SourceInfo.ToString().Replace(',', '|'); 65 | console.WriteLine($"{"Source Info",-20}: {sourceInfo}"); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /UsnParser/FilterOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UsnParser 4 | { 5 | public class FilterOptions 6 | { 7 | public string? Keyword { get; } 8 | 9 | public bool FileOnly { get; } 10 | 11 | public bool DirectoryOnly { get; } 12 | 13 | public bool CaseSensitive { get; } 14 | 15 | public static FilterOptions Default { get; } = new FilterOptions(); 16 | 17 | private FilterOptions() { } 18 | 19 | public FilterOptions(string? keyword, bool fileOnly, bool directoryOnly, bool caseSensitive) 20 | { 21 | Keyword = keyword; 22 | FileOnly = fileOnly; 23 | DirectoryOnly = directoryOnly; 24 | CaseSensitive = caseSensitive; 25 | 26 | if (FileOnly && DirectoryOnly) 27 | throw new InvalidOperationException($"{nameof(FileOnly)} and {nameof(DirectoryOnly)} can't both be set to true!"); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /UsnParser/Native/ACCESS_MASK.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UsnParser.Native 4 | { 5 | [Flags] 6 | public enum ACCESS_MASK : uint 7 | { 8 | DELETE = 0x00010000, 9 | READ_CONTROL = 0x00020000, 10 | WRITE_DAC = 0x00040000, 11 | WRITE_OWNER = 0x00080000, 12 | SYNCHRONIZE = 0x00100000, 13 | STANDARD_RIGHTS_REQUIRED = 0x000F0000, 14 | STANDARD_RIGHTS_READ = 0x00020000, 15 | STANDARD_RIGHTS_WRITE = 0x00020000, 16 | STANDARD_RIGHTS_EXECUTE = 0x00020000, 17 | STANDARD_RIGHTS_ALL = 0x001F0000, 18 | SPECIFIC_RIGHTS_ALL = 0x0000FFFF, 19 | ACCESS_SYSTEM_SECURITY = 0x01000000, 20 | MAXIMUM_ALLOWED = 0x02000000, 21 | GENERIC_READ = 0x80000000, 22 | GENERIC_WRITE = 0x40000000, 23 | GENERIC_EXECUTE = 0x20000000, 24 | GENERIC_ALL = 0x10000000 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /UsnParser/Native/BY_HANDLE_FILE_INFORMATION.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Runtime.InteropServices.ComTypes; 3 | 4 | namespace UsnParser.Native 5 | { 6 | /// Contains information that the GetFileInformationByHandle function retrieves. 7 | [StructLayout(LayoutKind.Sequential)] 8 | public struct BY_HANDLE_FILE_INFORMATION 9 | { 10 | /// The file attributes. For possible values and their descriptions, see File Attribute Constants. 11 | public FileFlagsAndAttributes dwFileAttributes; 12 | 13 | /// 14 | /// A FILETIME structure that specifies when a file or directory is created. If the underlying file system does not support 15 | /// creation time, this member is zero (0). 16 | /// 17 | public FILETIME ftCreationTime; 18 | 19 | /// 20 | /// A FILETIME structure. For a file, the structure specifies the last time that a file is read from or written to. For a 21 | /// directory, the structure specifies when the directory is created. For both files and directories, the specified date is 22 | /// correct, but the time of day is always set to midnight. If the underlying file system does not support the last access time, 23 | /// this member is zero (0). 24 | /// 25 | public FILETIME ftLastAccessTime; 26 | 27 | /// 28 | /// A FILETIME structure. For a file, the structure specifies the last time that a file is written to. For a directory, the 29 | /// structure specifies when the directory is created. If the underlying file system does not support the last write time, this 30 | /// member is zero (0). 31 | /// 32 | public FILETIME ftLastWriteTime; 33 | 34 | /// The serial number of the volume that contains a file. 35 | public uint dwVolumeSerialNumber; 36 | 37 | /// The high-order part of the file size. 38 | public uint nFileSizeHigh; 39 | 40 | /// The low-order part of the file size. 41 | public uint nFileSizeLow; 42 | 43 | /// 44 | /// The number of links to this file. For the FAT file system this member is always 1. For the NTFS file system, it can be more 45 | /// than 1. 46 | /// 47 | public uint nNumberOfLinks; 48 | 49 | /// The high-order part of a unique identifier that is associated with a file. For more information, see nFileIndexLow. 50 | public uint nFileIndexHigh; 51 | 52 | /// 53 | /// The low-order part of a unique identifier that is associated with a file. 54 | /// 55 | /// The identifier (low and high parts) and the volume serial number uniquely identify a file on a single computer. To determine 56 | /// whether two open handles represent the same file, combine the identifier and the volume serial number for each file and 57 | /// compare them. 58 | /// 59 | /// 60 | /// The ReFS file system, introduced with Windows Server 2012, includes 128-bit file identifiers. To retrieve the 128-bit file 61 | /// identifier use the GetFileInformationByHandleEx function with FileIdInfo to retrieve the FILE_ID_INFO structure. The 64-bit 62 | /// identifier in this structure is not guaranteed to be unique on ReFS. 63 | /// 64 | /// 65 | public uint nFileIndexLow; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /UsnParser/Native/CREATE_USN_JOURNAL_DATA.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace UsnParser.Native 4 | { 5 | /// Contains information that describes an update sequence number (USN) change journal. 6 | /// For more information, see Creating, Modifying, and Deleting a Change Journal. 7 | // https://docs.microsoft.com/en-us/windows/win32/api/winioctl/ns-winioctl-create_usn_journal_data typedef struct { DWORDLONG 8 | // MaximumSize; DWORDLONG AllocationDelta; } CREATE_USN_JOURNAL_DATA, *PCREATE_USN_JOURNAL_DATA; 9 | [StructLayout(LayoutKind.Sequential)] 10 | public struct CREATE_USN_JOURNAL_DATA 11 | { 12 | /// 13 | /// The target maximum size that the NTFS file system allocates for the change journal, in bytes. 14 | /// 15 | /// The change journal can grow larger than this value, but it is then truncated at the next NTFS file system checkpoint to less 16 | /// than this value. 17 | /// 18 | /// 19 | public ulong MaximumSize; 20 | 21 | /// 22 | /// The size of memory allocation that is added to the end and removed from the beginning of the change journal, in bytes. 23 | /// 24 | /// The change journal can grow to more than the sum of the values of MaximumSize and AllocationDelta before being trimmed. 25 | /// 26 | /// 27 | public ulong AllocationDelta; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /UsnParser/Native/FILE_INFORMATION_CLASS.cs: -------------------------------------------------------------------------------- 1 | namespace UsnParser.Native 2 | { 3 | public enum FILE_INFORMATION_CLASS 4 | { 5 | FileDirectoryInformation = 1, 6 | FileFullDirectoryInformation = 2, 7 | FileBothDirectoryInformation = 3, 8 | FileBasicInformation = 4, 9 | FileStandardInformation = 5, 10 | FileInternalInformation = 6, 11 | FileEaInformation = 7, 12 | FileAccessInformation = 8, 13 | FileNameInformation = 9, 14 | FileRenameInformation = 10, 15 | FileLinkInformation = 11, 16 | FileNamesInformation = 12, 17 | FileDispositionInformation = 13, 18 | FilePositionInformation = 14, 19 | FileFullEaInformation = 15, 20 | FileModeInformation = 16, 21 | FileAlignmentInformation = 17, 22 | FileAllInformation = 18, 23 | FileAllocationInformation = 19, 24 | FileEndOfFileInformation = 20, 25 | FileAlternateNameInformation = 21, 26 | FileStreamInformation = 22, 27 | FilePipeInformation = 23, 28 | FilePipeLocalInformation = 24, 29 | FilePipeRemoteInformation = 25, 30 | FileMailslotQueryInformation = 26, 31 | FileMailslotSetInformation = 27, 32 | FileCompressionInformation = 28, 33 | FileObjectIdInformation = 29, 34 | FileCompletionInformation = 30, 35 | FileMoveClusterInformation = 31, 36 | FileQuotaInformation = 32, 37 | FileReparsePointInformation = 33, 38 | FileNetworkOpenInformation = 34, 39 | FileAttributeTagInformation = 35, 40 | FileTrackingInformation = 36, 41 | FileIdBothDirectoryInformation = 37, 42 | FileIdFullDirectoryInformation = 38, 43 | FileValidDataLengthInformation = 39, 44 | FileShortNameInformation = 40, 45 | FileHardLinkInformation = 46 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /UsnParser/Native/FILE_NAME_INFORMATION.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace UsnParser.Native 5 | { 6 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 7 | public struct FILE_NAME_INFORMATION 8 | { 9 | public uint FileNameLength; 10 | public char _fileName; 11 | public unsafe ReadOnlySpan FileName => MemoryMarshal.CreateReadOnlySpan(ref _fileName, (int)FileNameLength / sizeof(char)); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /UsnParser/Native/FileAccess.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UsnParser.Native 4 | { 5 | /// Enumerates the that may apply to files. 6 | /// These flags may be passed to CreateFile. 7 | [Flags] 8 | public enum FileAccess : uint 9 | { 10 | /// Read access. 11 | GENERIC_READ = ACCESS_MASK.GENERIC_READ, 12 | 13 | /// Write access. 14 | GENERIC_WRITE = ACCESS_MASK.GENERIC_WRITE, 15 | 16 | /// Execute access. 17 | GENERIC_EXECUTE = ACCESS_MASK.GENERIC_EXECUTE, 18 | 19 | /// All possible access rights. 20 | GENERIC_ALL = ACCESS_MASK.GENERIC_ALL, 21 | 22 | /// 23 | /// For a file object, the right to read the corresponding file data. For a directory object, the right to read the corresponding 24 | /// directory data. 25 | /// 26 | FILE_READ_DATA = 0x0001, // file & pipe 27 | 28 | /// For a directory, the right to list the contents of the directory. 29 | FILE_LIST_DIRECTORY = 0x0001, // directory 30 | 31 | /// 32 | /// For a file object, the right to write data to the file. For a directory object, the right to create a file in the directory ( ). 33 | /// 34 | FILE_WRITE_DATA = 0x0002, // file & pipe 35 | 36 | /// For a directory, the right to create a file in the directory. 37 | FILE_ADD_FILE = 0x0002, // directory 38 | 39 | /// 40 | /// For a file object, the right to append data to the file. (For local files, write operations will not overwrite existing data 41 | /// if this flag is specified without .) For a directory object, the right to create a subdirectory 42 | /// ( ). 43 | /// 44 | FILE_APPEND_DATA = 0x0004, // file 45 | 46 | /// For a directory, the right to create a subdirectory. 47 | FILE_ADD_SUBDIRECTORY = 0x0004, // directory 48 | 49 | /// For a named pipe, the right to create a pipe. 50 | FILE_CREATE_PIPE_INSTANCE = 0x0004, // named pipe 51 | 52 | /// The right to read extended file attributes. 53 | FILE_READ_EA = 0x0008, // file & directory 54 | 55 | /// The right to write extended file attributes. 56 | FILE_WRITE_EA = 0x0010, // file & directory 57 | 58 | /// 59 | /// For a native code file, the right to execute the file. This access right given to scripts may cause the script to be 60 | /// executable, depending on the script interpreter. 61 | /// 62 | FILE_EXECUTE = 0x0020, // file 63 | 64 | /// 65 | /// For a directory, the right to traverse the directory. By default, users are assigned the BYPASS_TRAVERSE_CHECKING privilege, 66 | /// which ignores the FILE_TRAVERSE access right. 67 | /// 68 | FILE_TRAVERSE = 0x0020, // directory 69 | 70 | /// For a directory, the right to delete a directory and all the files it contains, including read-only files. 71 | FILE_DELETE_CHILD = 0x0040, // directory 72 | 73 | /// The right to read file attributes. 74 | FILE_READ_ATTRIBUTES = 0x0080, // all 75 | 76 | /// The right to write file attributes. 77 | FILE_WRITE_ATTRIBUTES = 0x0100, // all 78 | 79 | /// 80 | SPECIFIC_RIGHTS_ALL = 0x00FFFF, 81 | 82 | /// 83 | FILE_ALL_ACCESS = ACCESS_MASK.STANDARD_RIGHTS_REQUIRED | ACCESS_MASK.SYNCHRONIZE | 0x1FF, 84 | 85 | /// 86 | FILE_GENERIC_READ = ACCESS_MASK.STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | ACCESS_MASK.SYNCHRONIZE, 87 | 88 | /// 89 | FILE_GENERIC_WRITE = ACCESS_MASK.STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | ACCESS_MASK.SYNCHRONIZE, 90 | 91 | /// 92 | FILE_GENERIC_EXECUTE = ACCESS_MASK.STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | ACCESS_MASK.SYNCHRONIZE, 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /UsnParser/Native/FileFlagsAndAttributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UsnParser.Native 4 | { 5 | /// 6 | /// File attributes are metadata values stored by the file system on disk and are used by the system and are available to developers via 7 | /// various file I/O APIs. 8 | /// 9 | [Flags] 10 | public enum FileFlagsAndAttributes : uint 11 | { 12 | /// 13 | /// A file that is read-only. Applications can read the file, but cannot write to it or delete it. This attribute is not honored on 14 | /// directories. For more information, see You cannot view or change the Read-only or the System attributes of folders in Windows Server 15 | /// 2003, in Windows XP, in Windows Vista or in Windows 7. 16 | /// 17 | FILE_ATTRIBUTE_READONLY = 0x00000001, 18 | 19 | /// The file or directory is hidden. It is not included in an ordinary directory listing. 20 | FILE_ATTRIBUTE_HIDDEN = 0x00000002, 21 | 22 | /// A file or directory that the operating system uses a part of, or uses exclusively. 23 | FILE_ATTRIBUTE_SYSTEM = 0x00000004, 24 | 25 | /// The handle that identifies a directory. 26 | FILE_ATTRIBUTE_DIRECTORY = 0x00000010, 27 | 28 | /// 29 | /// A file or directory that is an archive file or directory. Applications typically use this attribute to mark files for backup or 30 | /// removal . 31 | /// 32 | FILE_ATTRIBUTE_ARCHIVE = 0x00000020, 33 | 34 | /// This value is reserved for system use. 35 | FILE_ATTRIBUTE_DEVICE = 0x00000040, 36 | 37 | /// A file that does not have other attributes set. This attribute is valid only when used alone. 38 | FILE_ATTRIBUTE_NORMAL = 0x00000080, 39 | 40 | /// 41 | /// A file that is being used for temporary storage. File systems avoid writing data back to mass storage if sufficient cache memory is 42 | /// available, because typically, an application deletes a temporary file after the handle is closed. In that scenario, the system can 43 | /// entirely avoid writing the data. Otherwise, the data is written after the handle is closed. 44 | /// 45 | FILE_ATTRIBUTE_TEMPORARY = 0x00000100, 46 | 47 | /// A file that is a sparse file. 48 | FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200, 49 | 50 | /// A file or directory that has an associated reparse point, or a file that is a symbolic link. 51 | FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400, 52 | 53 | /// 54 | /// A file or directory that is compressed. For a file, all of the data in the file is compressed. For a directory, compression is the 55 | /// default for newly created files and subdirectories. 56 | /// 57 | FILE_ATTRIBUTE_COMPRESSED = 0x00000800, 58 | 59 | /// 60 | /// The data of a file is not available immediately. This attribute indicates that the file data is physically moved to offline storage. 61 | /// This attribute is used by Remote Storage, which is the hierarchical storage management software. Applications should not arbitrarily 62 | /// change this attribute. 63 | /// 64 | FILE_ATTRIBUTE_OFFLINE = 0x00001000, 65 | 66 | /// The file or directory is not to be indexed by the content indexing service. 67 | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000, 68 | 69 | /// 70 | /// A file or directory that is encrypted. For a file, all data streams in the file are encrypted. For a directory, encryption is the 71 | /// default for newly created files and subdirectories. 72 | /// 73 | FILE_ATTRIBUTE_ENCRYPTED = 0x00004000, 74 | 75 | /// 76 | /// The directory or user data stream is configured with integrity (only supported on ReFS volumes). It is not included in an ordinary 77 | /// directory listing. The integrity setting persists with the file if it's renamed. If a file is copied the destination file will have 78 | /// integrity set if either the source file or destination directory have integrity set. 79 | /// 80 | /// Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This flag is not 81 | /// supported until Windows Server 2012. 82 | /// 83 | /// 84 | FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x00008000, 85 | 86 | /// This value is reserved for system use. 87 | FILE_ATTRIBUTE_VIRTUAL = 0x00010000, 88 | 89 | /// 90 | /// The user data stream not to be read by the background data integrity scanner (AKA scrubber). When set on a directory it only provides 91 | /// inheritance. This flag is only supported on Storage Spaces and ReFS volumes. It is not included in an ordinary directory listing. 92 | /// 93 | /// Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This flag is not 94 | /// supported until Windows 8 and Windows Server 2012. 95 | /// 96 | /// 97 | FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x00020000, 98 | 99 | /// 100 | FILE_ATTRIBUTE_EA = 0x00040000, 101 | 102 | /// Used to prevent the file from being purged from local storage when running low on disk space. 103 | FILE_ATTRIBUTE_PINNED = 0x00080000, 104 | 105 | /// Indicate that the file is not stored locally. 106 | FILE_ATTRIBUTE_UNPINNED = 0x00100000, 107 | 108 | /// 109 | /// This attribute only appears in directory enumeration classes (FILE_DIRECTORY_INFORMATION, FILE_BOTH_DIR_INFORMATION, etc.). When this 110 | /// attribute is set, it means that the file or directory has no physical representation on the local system; the item is virtual. 111 | /// Opening the item will be more expensive than normal, e.g. it will cause at least some of it to be fetched from a remote store. 112 | /// 113 | FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x00040000, 114 | 115 | /// 116 | /// When this attribute is set, it means that the file or directory is not fully present locally. For a file that means that not all of 117 | /// its data is on local storage (e.g. it may be sparse with some data still in remote storage). For a directory it means that some of 118 | /// the directory contents are being virtualized from another location. Reading the file / enumerating the directory will be more 119 | /// expensive than normal, e.g. it will cause at least some of the file/directory content to be fetched from a remote store. Only 120 | /// kernel-mode callers can set this bit. 121 | /// 122 | FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x00400000, 123 | 124 | /// 125 | FILE_ATTRIBUTE_STRICTLY_SEQUENTIAL = 0x20000000, 126 | 127 | /// 128 | /// Write operations will not go through any intermediate cache, they will go directly to disk. 129 | /// For additional information, see the Caching Behavior section of this topic. 130 | /// 131 | FILE_FLAG_WRITE_THROUGH = 0x80000000, 132 | 133 | /// 134 | /// The file or device is being opened or created for asynchronous I/O. 135 | /// 136 | /// When subsequent I/O operations are completed on this handle, the event specified in the OVERLAPPED structure will be set to the 137 | /// signaled state. 138 | /// 139 | /// If this flag is specified, the file can be used for simultaneous read and write operations. 140 | /// 141 | /// If this flag is not specified, then I/O operations are serialized, even if the calls to the read and write functions specify an 142 | /// OVERLAPPED structure. 143 | /// 144 | /// 145 | /// For information about considerations when using a file handle created with this flag, see the Synchronous and Asynchronous I/O 146 | /// Handles section of this topic. 147 | /// 148 | /// 149 | FILE_FLAG_OVERLAPPED = 0x40000000, 150 | 151 | /// 152 | /// The file or device is being opened with no system caching for data reads and writes. This flag does not affect hard disk caching or 153 | /// memory mapped files. 154 | /// 155 | /// There are strict requirements for successfully working with files opened with CreateFile using the FILE_FLAG_NO_BUFFERING flag, for 156 | /// details see File Buffering. 157 | /// 158 | /// 159 | FILE_FLAG_NO_BUFFERING = 0x20000000, 160 | 161 | /// 162 | /// Access is intended to be random. The system can use this as a hint to optimize file caching. 163 | /// This flag has no effect if the file system does not support cached I/O and FILE_FLAG_NO_BUFFERING. 164 | /// For more information, see the Caching Behavior section of this topic. 165 | /// 166 | FILE_FLAG_RANDOM_ACCESS = 0x10000000, 167 | 168 | /// 169 | /// Access is intended to be sequential from beginning to end. The system can use this as a hint to optimize file caching. 170 | /// This flag should not be used if read-behind (that is, reverse scans) will be used. 171 | /// This flag has no effect if the file system does not support cached I/O and FILE_FLAG_NO_BUFFERING. 172 | /// For more information, see the Caching Behavior section of this topic. 173 | /// 174 | FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000, 175 | 176 | /// 177 | /// The file is to be deleted immediately after all of its handles are closed, which includes the specified handle and any other open or 178 | /// duplicated handles. 179 | /// If there are existing open handles to a file, the call fails unless they were all opened with the FILE_SHARE_DELETE share mode. 180 | /// Subsequent open requests for the file fail, unless the FILE_SHARE_DELETE share mode is specified. 181 | /// 182 | FILE_FLAG_DELETE_ON_CLOSE = 0x04000000, 183 | 184 | /// 185 | /// The file is being opened or created for a backup or restore operation. The system ensures that the calling process overrides file 186 | /// security checks when the process has SE_BACKUP_NAME and SE_RESTORE_NAME privileges. For more information, see Changing Privileges in 187 | /// a Token. 188 | /// 189 | /// You must set this flag to obtain a handle to a directory. A directory handle can be passed to some functions instead of a file 190 | /// handle. For more information, see the Remarks section. 191 | /// 192 | /// 193 | FILE_FLAG_BACKUP_SEMANTICS = 0x02000000, 194 | 195 | /// 196 | /// Access will occur according to POSIX rules. This includes allowing multiple files with names, differing only in case, for file 197 | /// systems that support that naming. Use care when using this option, because files created with this flag may not be accessible by 198 | /// applications that are written for MS-DOS or 16-bit Windows. 199 | /// 200 | FILE_FLAG_POSIX_SEMANTICS = 0x01000000, 201 | 202 | /// 203 | /// The file or device is being opened with session awareness. If this flag is not specified, then per-session devices (such as a device 204 | /// using RemoteFX USB Redirection) cannot be opened by processes running in session 0. This flag has no effect for callers not in 205 | /// session 0. This flag is supported only on server editions of Windows. 206 | /// Windows Server 2008 R2 and Windows Server 2008: This flag is not supported before Windows Server 2012. 207 | /// 208 | FILE_FLAG_SESSION_AWARE = 0x00800000, 209 | 210 | /// 211 | /// Normal reparse point processing will not occur; CreateFile will attempt to open the reparse point. When a file is opened, a file 212 | /// handle is returned, whether or not the filter that controls the reparse point is operational. 213 | /// This flag cannot be used with the CREATE_ALWAYS flag. 214 | /// If the file is not a reparse point, then this flag is ignored. 215 | /// 216 | FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000, 217 | 218 | /// 219 | /// The file data is requested, but it should continue to be located in remote storage. It should not be transported back to local 220 | /// storage. This flag is for use by remote storage systems. 221 | /// 222 | FILE_FLAG_OPEN_NO_RECALL = 0x00100000, 223 | 224 | /// 225 | /// If you attempt to create multiple instances of a pipe with this flag, creation of the first instance succeeds, but creation of the 226 | /// next instance fails with ERROR_ACCESS_DENIED. 227 | /// 228 | FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000, 229 | 230 | /// Impersonates a client at the Anonymous impersonation level. 231 | SECURITY_ANONYMOUS = 0x00000000, 232 | 233 | /// Impersonates a client at the Identification impersonation level. 234 | SECURITY_IDENTIFICATION = 0x00010000, 235 | 236 | /// 237 | /// Impersonate a client at the impersonation level. This is the default behavior if no other flags are specified along with the 238 | /// SECURITY_SQOS_PRESENT flag. 239 | /// 240 | SECURITY_IMPERSONATION = 0x00020000, 241 | 242 | /// Impersonates a client at the Delegation impersonation level. 243 | SECURITY_DELEGATION = 0x00030000, 244 | 245 | /// The security tracking mode is dynamic. If this flag is not specified, the security tracking mode is static. 246 | SECURITY_CONTEXT_TRACKING = 0x00040000, 247 | 248 | /// 249 | /// Only the enabled aspects of the client's security context are available to the server. If you do not specify this flag, all aspects 250 | /// of the client's security context are available. 251 | /// This allows the client to limit the groups and privileges that a server can use while impersonating the client. 252 | /// 253 | SECURITY_EFFECTIVE_ONLY = 0x00080000, 254 | 255 | /// Include to enable the other SECURITY_ flags. 256 | SECURITY_SQOS_PRESENT = 0x00100000, 257 | 258 | /// The specified volume is a compressed volume. 259 | FILE_VOLUME_IS_COMPRESSED = 0x00008000, 260 | 261 | /// The specified volume supports object identifiers. 262 | FILE_SUPPORTS_OBJECT_IDS = 0x00010000, 263 | 264 | /// The specified volume supports the Encrypted File System (EFS). For more information, see File Encryption. 265 | FILE_SUPPORTS_ENCRYPTION = 0x00020000, 266 | 267 | /// The specified volume supports named streams. 268 | FILE_NAMED_STREAMS = 0x00040000, 269 | 270 | /// The specified volume is read-only. 271 | FILE_READ_ONLY_VOLUME = 0x00080000, 272 | 273 | /// The specified volume supports a single sequential write. 274 | FILE_SEQUENTIAL_WRITE_ONCE = 0x00100000, 275 | 276 | /// The specified volume supports transactions. For more information, see About KTM. 277 | FILE_SUPPORTS_TRANSACTIONS = 0x00200000, 278 | 279 | /// 280 | /// The specified volume supports hard links. For more information, see Hard Links and Junctions. 281 | /// Windows Vista and Windows Server 2008: This value is not supported. 282 | /// 283 | FILE_SUPPORTS_HARD_LINKS = 0x00400000, 284 | 285 | /// 286 | /// The specified volume supports extended attributes. An extended attribute is a piece of application-specific metadata that an 287 | /// application can associate with a file and is not part of the file's data. 288 | /// Windows Vista and Windows Server 2008: This value is not supported. 289 | /// 290 | FILE_SUPPORTS_EXTENDED_ATTRIBUTES = 0x00800000, 291 | 292 | /// 293 | /// The file system supports open by FileID. For more information, see FILE_ID_BOTH_DIR_INFO. 294 | /// Windows Vista and Windows Server 2008: This value is not supported. 295 | /// 296 | FILE_SUPPORTS_OPEN_BY_FILE_ID = 0x01000000, 297 | 298 | /// 299 | /// The specified volume supports update sequence number (USN) journals. For more information, see Change Journal Records. 300 | /// Windows Vista and Windows Server 2008: This value is not supported. 301 | /// 302 | FILE_SUPPORTS_USN_JOURNAL = 0x02000000, 303 | 304 | /// The file system supports integrity streams. 305 | FILE_SUPPORTS_INTEGRITY_STREAMS = 0x04000000, 306 | 307 | /// 308 | /// The file system supports block cloning, that is, sharing logical clusters between files on the same volume. The file system 309 | /// reallocates on writes to shared clusters. 310 | /// 311 | FILE_SUPPORTS_BLOCK_REFCOUNTING = 0x08000000, 312 | 313 | /// 314 | /// The file system tracks whether each cluster of a file contains valid data (either from explicit file writes or automatic zeros) or 315 | /// invalid data (has not yet been written to or zeroed). File systems that use sparse valid data length (VDL) do not store a valid data 316 | /// length and do not require that valid data be contiguous within a file. 317 | /// 318 | FILE_SUPPORTS_SPARSE_VDL = 0x10000000, 319 | 320 | /// The specified volume is a direct access (DAX) volume. 321 | FILE_DAX_VOLUME = 0x20000000, 322 | 323 | /// The file system supports ghosting. 324 | FILE_SUPPORTS_GHOSTING = 0x40000000, 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /UsnParser/Native/IO_STATUS_BLOCK.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace UsnParser.Native 5 | { 6 | /// 7 | /// 8 | /// A driver sets an IRP's I/O status block to indicate the final status of an I/O request, before calling IoCompleteRequest for the IRP. 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// Unless a driver's dispatch routine completes an IRP with an error status value, the lowest-level driver in the chain frequently 14 | /// sets the IRP's I/O status block to the values that will be returned to the original requester of the I/O operation. 15 | /// 16 | /// 17 | /// The IoCompletion routines of higher-level drivers usually check the I/O status block in IRPs completed by lower drivers. By 18 | /// design, the I/O status block in an IRP is the only information passed back from the underlying device driver to all higher-level 19 | /// drivers' IoCompletion routines. 20 | /// 21 | /// 22 | /// The operating system implements support routines that write IO_STATUS_BLOCK values to caller-supplied output buffers. For 23 | /// example, see ZwOpenFile or NtOpenFile. These routines return status codes that might not match the status codes in the 24 | /// IO_STATUS_BLOCK structures. If one of these routines returns STATUS_PENDING, the caller should wait for the I/O operation 25 | /// to complete, and then check the status code in the IO_STATUS_BLOCK structure to determine the final status of the 26 | /// operation. If the routine returns a status code other than STATUS_PENDING, the caller should rely on this status code instead of 27 | /// the status code in the IO_STATUS_BLOCK structure. 28 | /// 29 | /// For more information, see I/O Status Blocks. 30 | /// 31 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_io_status_block typedef struct _IO_STATUS_BLOCK 32 | // { union { NTSTATUS Status; PVOID Pointer; } DUMMYUNIONNAME; ULONG_PTR Information; } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; 33 | [StructLayout(LayoutKind.Sequential)] 34 | public struct IO_STATUS_BLOCK 35 | { 36 | /// 37 | /// This is the completion status, either STATUS_SUCCESS if the requested operation was completed successfully or an 38 | /// informational, warning, or error STATUS_XXX value. For more information, see Using NTSTATUS values. 39 | /// 40 | public uint Status; 41 | 42 | /// 43 | /// This is set to a request-dependent value. For example, on successful completion of a transfer request, this is set to the 44 | /// number of bytes transferred. If a transfer request is completed with another STATUS_XXX, this member is set to zero. 45 | /// 46 | public IntPtr Information; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /UsnParser/Native/Kernel32.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System; 3 | using System.IO; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace UsnParser.Native 7 | { 8 | public static partial class Kernel32 9 | { 10 | internal const uint USN_REASON_MASK_ALL = 0xFFFFFFFF; 11 | 12 | internal const int MAX_PATH = 260; 13 | 14 | // CTL_CODE( DeviceType, Function, Method, Access ) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) 15 | private const uint FILE_DEVICE_FILE_SYSTEM = 0x00000009; 16 | 17 | private const uint METHOD_NEITHER = 3; 18 | private const uint METHOD_BUFFERED = 0; 19 | private const uint FILE_ANY_ACCESS = 0; 20 | 21 | // FSCTL_ENUM_USN_DATA = CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 44, METHOD_NEITHER, FILE_ANY_ACCESS) 22 | internal const uint FSCTL_ENUM_USN_DATA = (FILE_DEVICE_FILE_SYSTEM << 16) | (FILE_ANY_ACCESS << 14) | (44 << 2) | METHOD_NEITHER; 23 | 24 | // FSCTL_READ_USN_JOURNAL = CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 46, METHOD_NEITHER, FILE_ANY_ACCESS) 25 | internal const uint FSCTL_READ_USN_JOURNAL = (FILE_DEVICE_FILE_SYSTEM << 16) | (FILE_ANY_ACCESS << 14) | (46 << 2) | METHOD_NEITHER; 26 | 27 | // FSCTL_CREATE_USN_JOURNAL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 57, METHOD_NEITHER, FILE_ANY_ACCESS) 28 | internal const uint FSCTL_CREATE_USN_JOURNAL = (FILE_DEVICE_FILE_SYSTEM << 16) | (FILE_ANY_ACCESS << 14) | (57 << 2) | METHOD_NEITHER; 29 | 30 | // FSCTL_QUERY_USN_JOURNAL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 61, METHOD_BUFFERED, FILE_ANY_ACCESS) 31 | internal const uint FSCTL_QUERY_USN_JOURNAL = (FILE_DEVICE_FILE_SYSTEM << 16) | (FILE_ANY_ACCESS << 14) | (61 << 2) | METHOD_BUFFERED; 32 | 33 | // FSCTL_DELETE_USN_JOURNAL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 62, METHOD_BUFFERED, FILE_ANY_ACCESS) 34 | internal const uint FSCTL_DELETE_USN_JOURNAL = (FILE_DEVICE_FILE_SYSTEM << 16) | (FILE_ANY_ACCESS << 14) | (62 << 2) | METHOD_BUFFERED; 35 | 36 | 37 | /// 38 | /// The RtlZeroMemory routine fills a block of memory with zeros, given a pointer to the block and the length, in bytes, to be filled. 39 | /// 40 | /// A pointer to the memory block to be filled with zeros. 41 | /// The number of bytes to fill with zeros. 42 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/nf-wdm-rtlzeromemory 43 | [LibraryImport("kernel32.dll", EntryPoint = "RtlZeroMemory", SetLastError = false)] 44 | internal static partial void ZeroMemory(IntPtr Destination, int Length); 45 | 46 | /// 47 | /// Sends the control code 'dwIoControlCode' to the device driver specified by 'hDevice'. 48 | /// 49 | /// IntPtr handle to the device to receive 'dwIoControlCode 50 | /// Device IO Control Code to send 51 | /// Input buffer if required 52 | /// Size of input buffer 53 | /// Output buffer if required 54 | /// Size of output buffer 55 | /// Number of bytes returned 56 | /// Pointer to an 'OVERLAPPED' structure 57 | /// 58 | [LibraryImport("kernel32.dll", SetLastError = true)] 59 | [return: MarshalAs(UnmanagedType.Bool)] 60 | internal static partial bool DeviceIoControl( 61 | SafeFileHandle hDevice, 62 | uint dwIoControlCode, 63 | IntPtr lpInBuffer, 64 | int nInBufferSize, 65 | IntPtr lpOutBuffer, 66 | int nOutBufferSize, 67 | out uint lpBytesReturned, 68 | IntPtr lpOverlapped); 69 | 70 | /// 71 | /// Creates the file specified by 'lpFileName' with desired access, share mode, security attributes, 72 | /// creation disposition, flags and attributes. 73 | /// 74 | /// Fully qualified path to a file 75 | /// Requested access (write, read, read/write, none) 76 | /// Share mode (read, write, read/write, delete, all, none) 77 | /// IntPtr to a 'SECURITY_ATTRIBUTES' structure 78 | /// Action to take on file or device specified by 'lpFileName' (CREATE_NEW, 79 | /// CREATE_ALWAYS, OPEN_ALWAYS, OPEN_EXISTING, TRUNCATE_EXISTING) 80 | /// File or device attributes and flags (typically FILE_ATTRIBUTE_NORMAL) 81 | /// IntPtr to a valid handle to a template file with 'GENERIC_READ' access right 82 | /// IntPtr handle to the 'lpFileName' file or device or 'INVALID_HANDLE_VALUE' 83 | [LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "CreateFileW", StringMarshalling = StringMarshalling.Utf16)] 84 | internal static partial SafeFileHandle 85 | CreateFile( 86 | string lpFileName, 87 | FileAccess dwDesiredAccess, 88 | FileShare dwShareMode, 89 | IntPtr lpSecurityAttributes, 90 | FileMode dwCreationDisposition, 91 | FileFlagsAndAttributes dwFlagsAndAttributes, 92 | IntPtr hTemplateFile); 93 | 94 | /// 95 | /// Fills the 'BY_HANDLE_FILE_INFORMATION' structure for the file specified by 'hFile'. 96 | /// 97 | /// Fully qualified name of a file 98 | /// Out BY_HANDLE_FILE_INFORMATION argument 99 | /// 'true' if successful, otherwise 'false' 100 | [DllImport("kernel32.dll", SetLastError = true)] 101 | [return: MarshalAs(UnmanagedType.Bool)] 102 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "SYSLIB1054:Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time", Justification = "Workaround SYSLIB1051 error")] 103 | internal static extern bool GetFileInformationByHandle( 104 | SafeFileHandle hFile, 105 | out BY_HANDLE_FILE_INFORMATION lpFileInformation); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /UsnParser/Native/LongFileTime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UsnParser.Native 4 | { 5 | /// 6 | /// 100-nanosecond intervals (ticks) since January 1, 1601 (UTC). 7 | /// 8 | /// 9 | /// For NT times that are defined as longs (LARGE_INTEGER, etc.). 10 | /// Do NOT use for FILETIME unless you are POSITIVE it will fall on an 11 | /// 8 byte boundary. 12 | /// 13 | public struct LongFileTime 14 | { 15 | /// 16 | /// 100-nanosecond intervals (ticks) since January 1, 1601 (UTC). 17 | /// 18 | public long TicksSince1601; 19 | 20 | public DateTimeOffset ToDateTimeOffset() => new DateTimeOffset(DateTime.FromFileTimeUtc(TicksSince1601)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /UsnParser/Native/MFT_ENUM_DATA_V0.cs: -------------------------------------------------------------------------------- 1 | namespace UsnParser.Native 2 | { 3 | // 4 | // Summary: 5 | // Contains information defining the boundaries for and starting place of an enumeration 6 | // of update sequence number (USN) change journal records. It is used as the input 7 | // buffer for the FSCTL_ENUM_USN_DATA control code. Prior to Windows Server 2012 8 | // this structure was named MFT_ENUM_DATA. Use that name to compile with older SDKs 9 | // and compilers. 10 | public struct MFT_ENUM_DATA_V0 11 | { 12 | // 13 | // Summary: 14 | // The ordinal position within the files on the current volume at which the enumeration 15 | // is to begin. 16 | // The first call to FSCTL_ENUM_USN_DATA during an enumeration must have the StartFileReferenceNumber 17 | // member set to . Each call to FSCTL_ENUM_USN_DATA retrieves the starting point 18 | // for the subsequent call as the first entry in the output buffer. Subsequent calls 19 | // must be made with StartFileReferenceNumber set to this value. For more information, 20 | // see FSCTL_ENUM_USN_DATA. 21 | public ulong StartFileReferenceNumber; 22 | 23 | // 24 | // Summary: 25 | // The lower boundary of the range of USN values used to filter which records are 26 | // returned. Only records whose last change journal USN is between or equal to the 27 | // LowUsn and HighUsn member values are returned. 28 | public long LowUsn; 29 | 30 | // 31 | // Summary: 32 | // The upper boundary of the range of USN values used to filter which files are 33 | // returned. 34 | public long HighUsn; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /UsnParser/Native/NtFileCreateOptions.cs: -------------------------------------------------------------------------------- 1 | namespace UsnParser.Native 2 | { 3 | /// Specifies the options to apply when the driver creates or opens the file. 4 | public enum NtFileCreateOptions 5 | { 6 | /// 7 | /// The file is a directory. Compatible CreateOptions flags are FILE_SYNCHRONOUS_IO_ALERT, FILE_SYNCHRONOUS_IO_NONALERT, 8 | /// FILE_WRITE_THROUGH, FILE_OPEN_FOR_BACKUP_INTENT, and FILE_OPEN_BY_FILE_ID. The CreateDisposition parameter must be set to 9 | /// FILE_CREATE, FILE_OPEN, or FILE_OPEN_IF. 10 | /// 11 | FILE_DIRECTORY_FILE = 0x00000001, 12 | 13 | /// 14 | /// System services, file-system drivers, and drivers that write data to the file must actually transfer the data to the file 15 | /// before any requested write operation is considered complete. 16 | /// 17 | FILE_WRITE_THROUGH = 0x00000002, 18 | 19 | /// 20 | /// All access to the file will be sequential. 21 | /// 22 | FILE_SEQUENTIAL_ONLY = 0x00000004, 23 | 24 | /// 25 | /// The file cannot be cached or buffered in a driver's internal buffers. This flag is incompatible with the DesiredAccess 26 | /// parameter's FILE_APPEND_DATA flag. 27 | /// 28 | FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008, 29 | 30 | /// 31 | /// All operations on the file are performed synchronously. Any wait on behalf of the caller is subject to premature termination 32 | /// from alerts. This flag also causes the I/O system to maintain the file-position pointer. If this flag is set, the 33 | /// SYNCHRONIZE flag must be set in the DesiredAccess parameter. 34 | /// 35 | FILE_SYNCHRONOUS_IO_ALERT = 0x00000010, 36 | 37 | /// 38 | /// All operations on the file are performed synchronously. Waits in the system that synchronize I/O queuing and completion are 39 | /// not subject to alerts. This flag also causes the I/O system to maintain the file-position context. If this flag is set, the 40 | /// SYNCHRONIZE flag must be set in the DesiredAccess parameter. 41 | /// 42 | FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020, 43 | 44 | /// 45 | /// The file is a directory. The file object to open can represent a data file; a logical, virtual, or physical device; or a volume. 46 | /// 47 | FILE_NON_DIRECTORY_FILE = 0x00000040, 48 | 49 | /// 50 | /// Create a tree connection for this file in order to open it over the network. This flag is not used by device and 51 | /// intermediate drivers. 52 | /// 53 | FILE_CREATE_TREE_CONNECTION = 0x00000080, 54 | 55 | /// 56 | /// Complete this operation immediately with an alternate success code of STATUS_OPLOCK_BREAK_IN_PROGRESS if the target file is 57 | /// oplocked, rather than blocking the caller's thread. If the file is oplocked, another caller already has access to the file. 58 | /// This flag is not used by device and intermediate drivers. 59 | /// 60 | FILE_COMPLETE_IF_OPLOCKED = 0x00000100, 61 | 62 | /// 63 | /// If the extended attributes (EAs) for an existing file being opened indicate that the caller must understand EAs to properly 64 | /// interpret the file, NtCreateFile should return an error. This flag is irrelevant for device and intermediate drivers. 65 | /// 66 | FILE_NO_EA_KNOWLEDGE = 0x00000200, 67 | 68 | /// formerly known as FILE_OPEN_FOR_RECOVERY 69 | FILE_OPEN_REMOTE_INSTANCE = 0x00000400, 70 | 71 | /// 72 | /// Access to the file can be random, so no sequential read-ahead operations should be performed by file-system drivers or by 73 | /// the system. 74 | /// 75 | FILE_RANDOM_ACCESS = 0x00000800, 76 | 77 | /// 78 | /// The system deletes the file when the last handle to the file is passed to NtClose. If this flag is set, the DELETE flag must 79 | /// be set in the DesiredAccess parameter. 80 | /// 81 | FILE_DELETE_ON_CLOSE = 0x00001000, 82 | 83 | /// 84 | /// The file name that is specified by the ObjectAttributes parameter includes a binary 8-byte or 16-byte file reference number 85 | /// or object ID for the file, depending on the file system as shown below. Optionally, a device name followed by a backslash 86 | /// character may proceed these binary values. For example, a device name will have the following format. This number is 87 | /// assigned by and specific to the particular file system. 88 | /// 89 | FILE_OPEN_BY_FILE_ID = 0x00002000, 90 | 91 | /// 92 | /// The file is being opened for backup intent. Therefore, the system should check for certain access rights and grant the 93 | /// caller the appropriate access to the file—before checking the DesiredAccess parameter against the file's security 94 | /// descriptor. This flag not used by device and intermediate drivers. 95 | /// 96 | FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000, 97 | 98 | /// 99 | /// When a new file is created, the file MUST NOT be compressed, even if it is on a compressed volume. The flag MUST be ignored 100 | /// when opening an existing file. 101 | /// 102 | FILE_NO_COMPRESSION = 0x00008000, 103 | 104 | /// 105 | /// The file is being opened and an opportunistic lock (oplock) on the file is being requested as a single atomic operation. The 106 | /// file system checks for oplocks before it performs the create operation, and will fail the create with a return code of 107 | /// STATUS_CANNOT_BREAK_OPLOCK if the result would be to break an existing oplock. 108 | /// 109 | FILE_OPEN_REQUIRING_OPLOCK = 0x00010000, 110 | 111 | /// 112 | /// This flag allows an application to request a Filter opportunistic lock (oplock) to prevent other applications from getting 113 | /// share violations. If there are already open handles, the create request will fail with STATUS_OPLOCK_NOT_GRANTED. For more 114 | /// information, see the following Remarks section. 115 | /// 116 | FILE_RESERVE_OPFILTER = 0x00100000, 117 | 118 | /// 119 | /// Open a file with a reparse point and bypass normal reparse point processing for the file. For more information, see the 120 | /// following Remarks section. 121 | /// 122 | FILE_OPEN_REPARSE_POINT = 0x00200000, 123 | 124 | /// 125 | /// In a hierarchical storage management environment, this option requests that the file SHOULD NOT be recalled from tertiary 126 | /// storage such as tape. A file recall can take up to several minutes in a hierarchical storage management environment. The 127 | /// clients can specify this option to avoid such delays. 128 | /// 129 | FILE_OPEN_NO_RECALL = 0x00400000, 130 | 131 | /// Open file to query for free space. The client SHOULD set this to 0 and the server MUST ignore it. 132 | FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000, 133 | 134 | /// Undocumented. 135 | FILE_VALID_OPTION_FLAGS = 0x00ffffff, 136 | 137 | /// Undocumented. 138 | FILE_VALID_PIPE_OPTION_FLAGS = 0x00000032, 139 | 140 | /// Undocumented. 141 | FILE_VALID_MAILSLOT_OPTION_FLAGS = 0x00000032, 142 | 143 | /// Undocumented. 144 | FILE_VALID_SET_FLAGS = 0x00000036, 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /UsnParser/Native/NtFileMode.cs: -------------------------------------------------------------------------------- 1 | namespace UsnParser.Native 2 | { 3 | /// Specifies the action to perform if the file does or does not exist. 4 | public enum NtFileMode 5 | { 6 | /// Replaces the file if it exists. Creates the file if it doesn't exist. 7 | FILE_SUPERSEDE = 0x00000000, 8 | 9 | /// Opens the file if it exists. Returns an error if it doesn't exist. 10 | FILE_OPEN = 0x00000001, 11 | 12 | /// Returns an error if the file exists. Creates the file if it doesn't exist. 13 | FILE_CREATE = 0x00000002, 14 | 15 | /// Opens the file if it exists. Creates the file if it doesn't exist. 16 | FILE_OPEN_IF = 0x00000003, 17 | 18 | /// Open the file, and overwrite it if it exists. Returns an error if it doesn't exist. 19 | FILE_OVERWRITE = 0x00000004, 20 | 21 | /// Open the file, and overwrite it if it exists. Creates the file if it doesn't exist. 22 | FILE_OVERWRITE_IF = 0x00000005, 23 | 24 | /// Undocumented. 25 | FILE_MAXIMUM_DISPOSITION = 0x00000005 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /UsnParser/Native/Ntdll.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System; 3 | using System.IO; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace UsnParser.Native 7 | { 8 | public static partial class Ntdll 9 | { 10 | /// The operation completed successfully. 11 | public const int STATUS_SUCCESS = 0x00000000; 12 | 13 | /// {Access Denied} A process has requested access to an object but has not been granted those access rights. 14 | public const int STATUS_ACCESS_DENIED = unchecked((int)0xC0000022); 15 | 16 | /// The specified information record length does not match the length that is required for the specified information class. 17 | internal const int STATUS_INFO_LENGTH_MISMATCH = unchecked((int)0xC0000004); 18 | 19 | /// {Buffer Overflow} The data was too large to fit into the specified buffer. 20 | public const int STATUS_BUFFER_OVERFLOW = unchecked((int)0x80000005); 21 | 22 | /// 23 | /// The NtQueryInformationFile routine returns various kinds of information about a file object. 24 | /// 25 | /// Handle to a file object. The handle is created by a successful call to NtCreateFile or NtOpenFile, or to an equivalent file create or open routine. 26 | /// Pointer to an IO_STATUS_BLOCK structure that receives the final completion status and information about the operation. The Information member receives the number of bytes that this routine actually writes to the FileInformation buffer. 27 | /// Pointer to a caller-allocated buffer into which the routine writes the requested information about the file object. The FileInformationClass parameter specifies the type of information that the caller requests. 28 | /// The size, in bytes, of the buffer pointed to by FileInformation. 29 | /// Specifies the type of information to be returned about the file, in the buffer that FileInformation points to. 30 | /// 31 | [DllImport("ntdll.dll", SetLastError = true)] 32 | internal static extern int NtQueryInformationFile( 33 | SafeFileHandle fileHandle, 34 | in IO_STATUS_BLOCK ioStatusBlock, 35 | IntPtr fileInformation, 36 | uint length, 37 | FILE_INFORMATION_CLASS fileInformationClass); 38 | 39 | /// 40 | /// Creates a new file or directory, or opens an existing file, device, directory, or volume 41 | /// 42 | /// A pointer to a variable that receives the file handle if the call is successful (out) 43 | /// ACCESS_MASK value that expresses the type of access that the caller requires to the file or directory (in) 44 | /// A pointer to a structure already initialized with InitializeObjectAttributes (in) 45 | /// A pointer to a variable that receives the final completion status and information about the requested operation (out) 46 | /// The initial allocation size in bytes for the file (in)(optional) 47 | /// file attributes (in) 48 | /// type of share access that the caller would like to use in the file (in) 49 | /// what to do, depending on whether the file already exists (in) 50 | /// options to be applied when creating or opening the file (in) 51 | /// Pointer to an EA buffer used to pass extended attributes (in) 52 | /// Length of the EA buffer 53 | /// either STATUS_SUCCESS or an appropriate error status. If it returns an error status, the caller can find more information about the cause of the failure by checking the IoStatusBlock 54 | [LibraryImport("ntdll.dll", SetLastError = true)] 55 | internal static partial int NtCreateFile( 56 | out SafeFileHandle handle, 57 | FileAccess access, 58 | in OBJECT_ATTRIBUTES objectAttributes, 59 | out IO_STATUS_BLOCK ioStatusBlock, 60 | in long allocationSize, 61 | FileFlagsAndAttributes fileAttributes, 62 | FileShare shareAccess, 63 | NtFileMode createDisposition, 64 | NtFileCreateOptions createOptions, 65 | IntPtr eaBuffer, 66 | uint eaLength); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /UsnParser/Native/OBJECT_ATTRIBUTES.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace UsnParser.Native 5 | { 6 | // 7 | // Summary: 8 | // The OBJECT_ATTRIBUTES structure specifies attributes that can be applied to objects 9 | // or object handles by routines that create objects and/or return handles to objects. 10 | // 11 | // Remarks: 12 | // Use the InitializeObjectAttributes macro to initialize the members of the OBJECT_ATTRIBUTES 13 | // structure. Note that InitializeObjectAttributes initializes the SecurityQualityOfService 14 | // member to NULL. If you must specify a non- NULL value, set the SecurityQualityOfService 15 | // member after initialization. 16 | // To apply the attributes contained in this structure to an object or object handle, 17 | // pass a pointer to this structure to a routine that accesses objects or returns 18 | // object handles, such as ZwCreateFile or ZwCreateDirectoryObject. 19 | // All members of this structure are read-only. If a member of this structure is 20 | // a pointer, the object that this member points to is read-only as well. Read-only 21 | // members and objects can be used to acquire relevant information but must not 22 | // be modified. To set the members of this structure, use the InitializeObjectAttributes 23 | // macro. 24 | // Driver routines that run in a process context other than that of the system process 25 | // must set the OBJ_KERNEL_HANDLE flag for the Attributes member (by using the InitializeObjectAttributes 26 | // macro). This restricts the use of a handle opened for that object to processes 27 | // running only in kernel mode. Otherwise, the handle can be accessed by the process 28 | // in whose context the driver is running. 29 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 30 | public unsafe struct OBJECT_ATTRIBUTES 31 | { 32 | // 33 | // Summary: 34 | // The number of bytes of data contained in this structure. The InitializeObjectAttributes 35 | // macro sets this member to sizeof( OBJECT_ATTRIBUTES). 36 | public uint length; 37 | 38 | // 39 | // Summary: 40 | // Optional handle to the root object directory for the path name specified by the 41 | // ObjectName member. If RootDirectory is NULL, ObjectName must point to a fully 42 | // qualified object name that includes the full path to the target object. If RootDirectory 43 | // is non- NULL, ObjectName specifies an object name relative to the RootDirectory 44 | // directory. The RootDirectory handle can refer to a file system directory or an 45 | // object directory in the object manager namespace. 46 | public IntPtr rootDirectory; 47 | 48 | // 49 | // Summary: 50 | // Pointer to a Unicode string that contains the name of the object for which a 51 | // handle is to be opened. This must either be a fully qualified object name, or 52 | // a relative path name to the directory specified by the RootDirectory member. 53 | public UNICODE_STRING* objectName; 54 | 55 | // 56 | // Summary: 57 | // Bitmask of flags that specify object handle attributes. This member can contain 58 | // one or more of the flags in the following table. 59 | // Flag – Meaning – 60 | // OBJ_INHERIT – This handle can be inherited by child processes of the current 61 | // process. – 62 | // OBJ_PERMANENT – This flag only applies to objects that are named within the object 63 | // manager. By default, such objects are deleted when all open handles to them are 64 | // closed. If this flag is specified, the object is not deleted when all open handles 65 | // are closed. Drivers can use the ZwMakeTemporaryObject routine to make a permanent 66 | // object non-permanent. – 67 | // OBJ_EXCLUSIVE – If this flag is set and the OBJECT_ATTRIBUTES structure is passed 68 | // to a routine that creates an object, the object can be accessed exclusively. 69 | // That is, once a process opens such a handle to the object, no other processes 70 | // can open handles to this object. If this flag is set and the OBJECT_ATTRIBUTES 71 | // structure is passed to a routine that creates an object handle, the caller is 72 | // requesting exclusive access to the object for the process context that the handle 73 | // was created in. This request can be granted only if the OBJ_EXCLUSIVE flag was 74 | // set when the object was created. – 75 | // OBJ_CASE_INSENSITIVE – If this flag is specified, a case-insensitive comparison 76 | // is used when matching the name pointed to by the ObjectName member against the 77 | // names of existing objects. Otherwise, object names are compared using the default 78 | // system settings. – 79 | // OBJ_OPENIF – If this flag is specified, by using the object handle, to a routine 80 | // that creates objects and if that object already exists, the routine should open 81 | // that object. Otherwise, the routine creating the object returns an NTSTATUS code 82 | // of STATUS_OBJECT_NAME_COLLISION. – 83 | // OBJ_OPENLINK – If an object handle, with this flag set, is passed to a routine 84 | // that opens objects and if the object is a symbolic link object, the routine should 85 | // open the symbolic link object itself, rather than the object that the symbolic 86 | // link refers to (which is the default behavior). – 87 | // OBJ_KERNEL_HANDLE – The handle is created in system process context and can only 88 | // be accessed from kernel mode. – 89 | // OBJ_FORCE_ACCESS_CHECK – The routine that opens the handle should enforce all 90 | // access checks for the object, even if the handle is being opened in kernel mode. 91 | // – 92 | // OBJ_VALID_ATTRIBUTES – Reserved. – 93 | public uint attributes; 94 | 95 | // 96 | // Summary: 97 | // Specifies a security descriptor (SECURITY_DESCRIPTOR) for the object when the 98 | // object is created. If this member is NULL, the object will receive default security 99 | // settings. 100 | public IntPtr securityDescriptor; 101 | 102 | // 103 | // Summary: 104 | // Optional quality of service to be applied to the object when it is created. Used 105 | // to indicate the security impersonation level and context tracking mode (dynamic 106 | // or static). Currently, the InitializeObjectAttributes macro sets this member 107 | // to NULL. 108 | public IntPtr securityQualityOfService; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /UsnParser/Native/ObjectAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UsnParser.Native 4 | { 5 | /// 6 | /// Possible flags for the field. 7 | /// 8 | [Flags] 9 | public enum ObjectAttribute 10 | { 11 | /// 12 | /// This handle can be inherited by child processes of the current process. 13 | /// 14 | OBJ_INHERIT = 0x00000002, 15 | /// 16 | /// This flag only applies to objects that are named within the object manager. By default, such objects are deleted when all open handles to them are closed. If this flag is specified, the object is not deleted when all open handles are closed. Drivers can use the ZwMakeTemporaryObject routine to make a permanent object non-permanent. 17 | /// 18 | OBJ_PERMANENT = 0x00000010, 19 | /// 20 | /// If this flag is set and the structure is passed to a routine that creates an object, the object can be accessed exclusively. That is, once a process opens such a handle to the object, no other processes can open handles to this object. 21 | /// If this flag is set and the structure is passed to a routine that creates an object handle, the caller is requesting exclusive access to the object for the process context that the handle was created in. This request can be granted only if the OBJ_EXCLUSIVE flag was set when the object was created. 22 | /// 23 | OBJ_EXCLUSIVE = 0x00000020, 24 | /// 25 | /// If this flag is specified, a case-insensitive comparison is used when matching the name pointed to by the ObjectName member against the names of existing objects. Otherwise, object names are compared using the default system settings. 26 | /// 27 | OBJ_CASE_INSENSITIVE = 0x00000040, 28 | /// 29 | /// If this flag is specified, by using the object handle, to a routine that creates objects and if that object already exists, the routine should open that object. Otherwise, the routine creating the object returns an NTSTATUS code of STATUS_OBJECT_NAME_COLLISION. 30 | /// 31 | OBJ_OPENIF = 0x00000080, 32 | /// 33 | /// If an object handle, with this flag set, is passed to a routine that opens objects and if the object is a symbolic link object, the routine should open the symbolic link object itself, rather than the object that the symbolic link refers to (which is the default behavior). 34 | /// 35 | OBJ_OPENLINK = 0x00000100, 36 | /// 37 | /// The handle is created in system process context and can only be accessed from kernel mode. 38 | /// 39 | OBJ_KERNEL_HANDLE = 0x00000200, 40 | /// 41 | /// The routine that opens the handle should enforce all access checks for the object, even if the handle is being opened in kernel mode. 42 | /// 43 | OBJ_FORCE_ACCESS_CHECK = 0x00000400, 44 | /// 45 | /// The mask of all valid attributes. 46 | /// 47 | OBJ_VALID_ATTRIBUTES = 0x000007F2, 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /UsnParser/Native/READ_USN_JOURNAL_DATA_V0.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace UsnParser.Native 4 | { 5 | /// 6 | /// Contains information defining a set of update sequence number (USN) change journal records to return to the calling process. It 7 | /// is used by the FSCTL_QUERY_USN_JOURNAL and FSCTL_READ_USN_JOURNAL control codes. Prior to Windows 8 and Windows Server 2012 this 8 | /// structure was named READ_USN_JOURNAL_DATA. Use that name to compile with older SDKs and compilers. Windows Server 2012 9 | /// introduced READ_USN_JOURNAL_DATA_V1 to support 128-bit file identifiers used by ReFS. 10 | /// 11 | // https://docs.microsoft.com/en-us/windows/win32/api/winioctl/ns-winioctl-read_usn_journal_data_v0 typedef struct { USN StartUsn; 12 | // DWORD ReasonMask; DWORD ReturnOnlyOnClose; DWORDLONG Timeout; DWORDLONG BytesToWaitFor; DWORDLONG UsnJournalID; } 13 | // READ_USN_JOURNAL_DATA_V0, *PREAD_USN_JOURNAL_DATA_V0; 14 | [StructLayout(LayoutKind.Sequential, Pack = 8)] 15 | public struct READ_USN_JOURNAL_DATA_V0 16 | { 17 | /// 18 | /// The USN at which to begin reading the change journal. 19 | /// 20 | /// To start the read operation at the first record in the journal, set the StartUsn member to zero. Because a USN is 21 | /// contained in every journal record, the output buffer tells at which record the read operation actually started. 22 | /// 23 | /// To start the read operation at a specific record, set StartUsn to that record USN. 24 | /// 25 | /// If a nonzero USN is specified that is less than the first USN in the change journal, then an error occurs and the 26 | /// ERROR_JOURNAL_ENTRY_DELETED error code is returned. This code may indicate a case in which the specified USN is valid 27 | /// at one time but has since been deleted. 28 | /// 29 | /// 30 | /// For more information on navigating the change journal buffer returned in READ_USN_JOURNAL_DATA_V0, see Walking a 31 | /// Buffer of Change Journal Records. 32 | /// 33 | /// 34 | public long StartUsn; 35 | 36 | /// 37 | /// 38 | /// A mask of flags, each flag noting a change for which the file or directory has a record in the change journal. To be returned 39 | /// in a FSCTL_READ_USN_JOURNAL operation, a change journal record must have at least one of these flags set. 40 | /// 41 | /// The list of valid flags is as follows. Unused bits are reserved. 42 | /// 43 | /// 44 | /// Value 45 | /// Meaning 46 | /// 47 | /// 48 | /// USN_REASON_BASIC_INFO_CHANGE 0x00008000 49 | /// 50 | /// A user has either changed one or more file or directory attributes (such as the read-only, hidden, system, archive, or sparse 51 | /// attribute), or one or more time stamps. 52 | /// 53 | /// 54 | /// 55 | /// USN_REASON_CLOSE 0x80000000 56 | /// The file or directory is closed. 57 | /// 58 | /// 59 | /// USN_REASON_COMPRESSION_CHANGE 0x00020000 60 | /// The compression state of the file or directory is changed from or to compressed. 61 | /// 62 | /// 63 | /// USN_REASON_DATA_EXTEND 0x00000002 64 | /// The file or directory is added to. 65 | /// 66 | /// 67 | /// USN_REASON_DATA_OVERWRITE 0x00000001 68 | /// Data in the file or directory is overwritten. 69 | /// 70 | /// 71 | /// USN_REASON_DATA_TRUNCATION 0x00000004 72 | /// The file or directory is truncated. 73 | /// 74 | /// 75 | /// USN_REASON_EA_CHANGE 0x00000400 76 | /// 77 | /// The user makes a change to the file or directory extended attributes. These NTFS file system attributes are not accessible to 78 | /// Windows-based applications. 79 | /// 80 | /// 81 | /// 82 | /// USN_REASON_ENCRYPTION_CHANGE 0x00040000 83 | /// The file or directory is encrypted or decrypted. 84 | /// 85 | /// 86 | /// USN_REASON_FILE_CREATE 0x00000100 87 | /// The file or directory is created for the first time. 88 | /// 89 | /// 90 | /// USN_REASON_FILE_DELETE 0x00000200 91 | /// The file or directory is deleted. 92 | /// 93 | /// 94 | /// USN_REASON_HARD_LINK_CHANGE 0x00010000 95 | /// 96 | /// An NTFS file system hard link is added to or removed from the file or directory. An NTFS file system hard link, similar to a 97 | /// POSIX hard link, is one of several directory entries that see the same file or directory. 98 | /// 99 | /// 100 | /// 101 | /// USN_REASON_INDEXABLE_CHANGE 0x00004000 102 | /// 103 | /// A user changed the FILE_ATTRIBUTE_NOT_CONTENT_INDEXED attribute. That is, the user changed the file or directory from one 104 | /// that can be content indexed to one that cannot, or vice versa. (Content indexing permits rapid searching of data by building 105 | /// a database of selected content.) 106 | /// 107 | /// 108 | /// 109 | /// USN_REASON_NAMED_DATA_EXTEND 0x00000020 110 | /// One or more named data streams for the file were added to. 111 | /// 112 | /// 113 | /// USN_REASON_NAMED_DATA_OVERWRITE 0x00000010 114 | /// Data in one or more named data streams for the file is overwritten. 115 | /// 116 | /// 117 | /// USN_REASON_NAMED_DATA_TRUNCATION 0x00000040 118 | /// One or more named data streams for the file is truncated. 119 | /// 120 | /// 121 | /// USN_REASON_OBJECT_ID_CHANGE 0x00080000 122 | /// The object identifier of the file or directory is changed. 123 | /// 124 | /// 125 | /// USN_REASON_RENAME_NEW_NAME 0x00002000 126 | /// 127 | /// The file or directory is renamed, and the file name in the USN_RECORD_V2 or USN_RECORD_V3 structure holding this journal 128 | /// record is the new name. 129 | /// 130 | /// 131 | /// 132 | /// USN_REASON_RENAME_OLD_NAME 0x00001000 133 | /// 134 | /// The file or directory is renamed, and the file name in the USN_RECORD_V2 or USN_RECORD_V3 structure holding this journal 135 | /// record is the previous name. 136 | /// 137 | /// 138 | /// 139 | /// USN_REASON_REPARSE_POINT_CHANGE 0x00100000 140 | /// 141 | /// The reparse point contained in the file or directory is changed, or a reparse point is added to or deleted from the file or directory. 142 | /// 143 | /// 144 | /// 145 | /// USN_REASON_SECURITY_CHANGE 0x00000800 146 | /// A change is made in the access permissions to the file or directory. 147 | /// 148 | /// 149 | /// USN_REASON_STREAM_CHANGE 0x00200000 150 | /// A named stream is added to or removed from the file or directory, or a named stream is renamed. 151 | /// 152 | /// 153 | /// 154 | public uint ReasonMask; 155 | 156 | /// 157 | /// A value that specifies when to return change journal records. 158 | /// 159 | /// To receive notification when the final handle for the changed file or directory is closed, rather than at the time a change 160 | /// occurs, set ReturnOnlyOnClose to any nonzero value and specify the USN_REASON_CLOSE flag in the 161 | /// ReasonMask member. 162 | /// 163 | /// 164 | /// All changes indicated by ReasonMask flags eventually generate a call to the change journal software when the file is 165 | /// closed. If your DeviceIoControl call is waiting for the file to be closed, that call in turn will allow your 166 | /// DeviceIoControl call to return. In the event that a file or directory is not closed prior to a volume failure, 167 | /// operating system failure, or shutdown, a cleanup call to the change journal software occurs the next time the volume is 168 | /// mounted. The call occurs even if there is an intervening system restart. 169 | /// 170 | /// 171 | /// To receive notification the first time each change is logged, as well as at cleanup, set ReturnOnlyOnClose to zero. 172 | /// 173 | /// 174 | /// Whether ReturnOnlyOnClose is zero or nonzero, the records generated at cleanup log within the change journal all 175 | /// reasons for USN changes that occurred to the file or directory. Each time a final close operation occurs for an item, a USN 176 | /// close record is written to the change journal, and the ReasonMask flags for the item are all reset. 177 | /// 178 | /// 179 | /// For a file or directory for which no user data exists (for example, a mounted folder), the final close operation occurs when 180 | /// the CloseHandle function is called on the last user handle to the item. 181 | /// 182 | /// 183 | public uint ReturnOnlyOnClose; 184 | 185 | /// 186 | /// 187 | /// The time-out value, in seconds, used with the BytesToWaitFor member to tell the operating system what to do if the 188 | /// FSCTL_READ_USN_JOURNAL operation requests more data than exists in the change journal. 189 | /// 190 | /// 191 | /// If Timeout is zero and BytesToWaitFor is nonzero, and the FSCTL_READ_USN_JOURNAL operation call reaches the end 192 | /// of the change journal without finding data to return, FSCTL_READ_USN_JOURNAL waits until BytesToWaitFor bytes 193 | /// of unfiltered data have been added to the change journal and then retrieves the specified records. 194 | /// 195 | /// 196 | /// If Timeout is nonzero and BytesToWaitFor is nonzero, and the FSCTL_READ_USN_JOURNAL operation call reaches the 197 | /// end of the change journal without finding data to return, FSCTL_READ_USN_JOURNAL waits Timeout seconds and then 198 | /// attempts to return the specified records. After Timeout seconds, FSCTL_READ_USN_JOURNAL retrieves any records 199 | /// available within the specified range. 200 | /// 201 | /// 202 | /// In either case, after the time-out period any new data appended to the change journal is processed. If there are still no 203 | /// records to return from the specified set, the time-out period is repeated. In this mode, FSCTL_READ_USN_JOURNAL remains 204 | /// outstanding until at least one record is returned or I/O is canceled. 205 | /// 206 | /// 207 | /// If BytesToWaitFor is zero, then Timeout is ignored. Timeout is also ignored for asynchronously opened handles. 208 | /// 209 | /// 210 | public ulong Timeout; 211 | 212 | /// 213 | /// 214 | /// The number of bytes of unfiltered data added to the change journal. Use this value with Timeout to tell the operating 215 | /// system what to do if the FSCTL_READ_USN_JOURNAL operation requests more data than exists in the change journal. 216 | /// 217 | /// 218 | /// If BytesToWaitFor is zero, then Timeout is ignored. In this case, the FSCTL_READ_USN_JOURNAL operation always 219 | /// returns successfully when the end of the change journal file is encountered. It also retrieves the USN that should be used 220 | /// for the next FSCTL_READ_USN_JOURNAL operation. When the returned next USN is the same as the StartUsn supplied, 221 | /// there are no records available. The calling process should not use FSCTL_READ_USN_JOURNAL again immediately. 222 | /// 223 | /// 224 | /// Because the amount of data returned cannot be predicted when BytesToWaitFor is zero, you run a risk of overflowing the 225 | /// output buffer. To reduce this risk, specify a nonzero BytesToWaitFor value in repeated FSCTL_READ_USN_JOURNAL 226 | /// operations until all records in the change journal are exhausted. Then specify zero to await new records. 227 | /// 228 | /// 229 | /// Alternatively, use the lpBytesReturned parameter of DeviceIoControl in the FSCTL_READ_USN_JOURNAL operation call to determine 230 | /// the amount of data available, reallocate the output buffer (with room to spare for new records), and call 231 | /// DeviceIoControl again. 232 | /// 233 | /// 234 | public ulong BytesToWaitFor; 235 | 236 | /// 237 | /// The identifier for the instance of the journal that is current for the volume. 238 | /// 239 | /// The NTFS file system can miss putting events in the change journal if the change journal is stopped and restarted or deleted 240 | /// and re-created. If either of these events occurs, the NTFS file system gives the journal a new identifier. If the journal 241 | /// identifier does not agree with the current journal identifier, the call to DeviceIoControl fails and returns an appropriate 242 | /// error code. To retrieve the new journal identifier, call DeviceIoControl with the FSCTL_QUERY_USN_JOURNAL operation. 243 | /// 244 | /// 245 | public ulong UsnJournalID; 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /UsnParser/Native/UNICODE_STRING.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace UsnParser.Native 5 | { 6 | /// The UNICODE_STRING structure is used to define Unicode strings. 7 | /// 8 | /// 9 | /// The UNICODE_STRING structure is used to pass Unicode strings. Use RtlUnicodeStringInit or RtlUnicodeStringInitEx to 10 | /// initialize a UNICODE_STRING structure. 11 | /// 12 | /// If the string is null-terminated, Length does not include the trailing null character. 13 | /// 14 | /// The MaximumLength is used to indicate the length of Buffer so that if the string is passed to a conversion routine 15 | /// such as RtlAnsiStringToUnicodeString the returned string does not exceed the buffer size. 16 | /// 17 | /// 18 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wudfwdm/ns-wudfwdm-_unicode_string typedef struct 19 | // _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWCH Buffer; } UNICODE_STRING; 20 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 21 | public struct UNICODE_STRING 22 | { 23 | /// The length, in bytes, of the string stored in Buffer. 24 | public ushort Length; 25 | 26 | /// The length, in bytes, of Buffer. 27 | public ushort MaximumLength; 28 | 29 | /// Pointer to a wide-character string. 30 | public IntPtr Buffer; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /UsnParser/Native/USN_JOURNAL_DATA_V0.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace UsnParser.Native 4 | { 5 | /// 6 | /// Represents an update sequence number (USN) change journal, its records, and its capacity. This structure is the output buffer for 7 | /// the FSCTL_QUERY_USN_JOURNAL control code. Prior to Windows 8 and Windows Server 2012 this structure was named 8 | /// USN_JOURNAL_DATA. Use that name to compile with older SDKs and compilers. 9 | /// 10 | // https://docs.microsoft.com/en-us/windows/win32/api/winioctl/ns-winioctl-usn_journal_data_v0 typedef struct { DWORDLONG 11 | // UsnJournalID; USN FirstUsn; USN NextUsn; USN LowestValidUsn; USN MaxUsn; DWORDLONG MaximumSize; DWORDLONG AllocationDelta; } 12 | // USN_JOURNAL_DATA_V0, *PUSN_JOURNAL_DATA_V0; 13 | [StructLayout(LayoutKind.Sequential)] 14 | public struct USN_JOURNAL_DATA_V0 15 | { 16 | /// 17 | /// The current journal identifier. A journal is assigned a new identifier on creation and can be stamped with a new identifier 18 | /// in the course of its existence. The NTFS file system uses this identifier for an integrity check. 19 | /// 20 | public ulong UsnJournalID; 21 | 22 | /// The number of first record that can be read from the journal. 23 | public long FirstUsn; 24 | 25 | /// The number of next record to be written to the journal. 26 | public long NextUsn; 27 | 28 | /// 29 | /// The first record that was written into the journal for this journal instance. Enumerating the files or directories on a 30 | /// volume can return a USN lower than this value (in other words, a FirstUsn member value less than the 31 | /// LowestValidUsn member value). If it does, the journal has been stamped with a new identifier since the last USN was 32 | /// written. In this case, LowestValidUsn may indicate a discontinuity in the journal, in which changes to some or all 33 | /// files or directories on the volume may have occurred that are not recorded in the change journal. 34 | /// 35 | public long LowestValidUsn; 36 | 37 | /// 38 | /// The largest USN that the change journal supports. An administrator must delete the change journal as the value of 39 | /// NextUsn approaches this value. 40 | /// 41 | public long MaxUsn; 42 | 43 | /// 44 | /// The target maximum size for the change journal, in bytes. The change journal can grow larger than this value, but it is then 45 | /// truncated at the next NTFS file system checkpoint to less than this value. 46 | /// 47 | public ulong MaximumSize; 48 | 49 | /// 50 | /// The number of bytes of disk memory added to the end and removed from the beginning of the change journal each time memory is 51 | /// allocated or deallocated. In other words, allocation and deallocation take place in units of this size. An integer multiple 52 | /// of a cluster size is a reasonable value for this member. 53 | /// 54 | public ulong AllocationDelta; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /UsnParser/Native/USN_RECORD_V2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace UsnParser.Native 5 | { 6 | /// 7 | /// Contains the information for an update sequence number (USN) change journal version 2.0 record. Applications should not attempt 8 | /// to work with change journal versions earlier than 2.0. Prior to Windows 8 and Windows Server 2012 this structure was named 9 | /// USN_RECORD. Use that name to compile with older SDKs and compilers. 10 | /// 11 | /// 12 | /// 13 | /// In output buffers returned from DeviceIoControl operations that work with USN_RECORD_V2, all records are aligned on 64-bit 14 | /// boundaries from the start of the buffer. 15 | /// 16 | /// 17 | /// To provide a path for upward compatibility in change journal clients, Microsoft provides a major and minor version number of the 18 | /// change journal software in the USN_RECORD_V2 structure. Your code should examine these values, detect its own 19 | /// compatibility with the change journal software, and if necessary gracefully handle any incompatibility. 20 | /// 21 | /// 22 | /// A change in the minor version number indicates that the existing USN_RECORD_V2 structure members are still valid, but that 23 | /// new members may have been added between the penultimate member and the last, which is a variable-length string. 24 | /// 25 | /// 26 | /// To handle such a change gracefully, your code should not do any compile-time pointer arithmetic that relies on the location of 27 | /// the last member. For example, this makes the C code unreliable. Instead, rely on run-time calculations by using the 28 | /// RecordLength member. 29 | /// 30 | /// 31 | /// An increase in the major version number of the change journal software indicates that the USN_RECORD_V2 structure may have 32 | /// undergone major changes, and that the current definition may not be reliable. If your code detects a change in the major version 33 | /// number of the change journal software, it should not work with the change journal. 34 | /// 35 | /// For more information, see Creating, Modifying, and Deleting a Change Journal. 36 | /// 37 | // https://docs.microsoft.com/en-us/windows/win32/api/winioctl/ns-winioctl-usn_record_v2 typedef struct { DWORD RecordLength; WORD 38 | // MajorVersion; WORD MinorVersion; DWORDLONG FileReferenceNumber; DWORDLONG ParentFileReferenceNumber; USN Usn; LARGE_INTEGER 39 | // TimeStamp; DWORD Reason; DWORD SourceInfo; DWORD SecurityId; DWORD FileAttributes; WORD FileNameLength; WORD FileNameOffset; WCHAR 40 | // FileName[1]; } USN_RECORD_V2, *PUSN_RECORD_V2; 41 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 42 | public struct USN_RECORD_V2 43 | { 44 | /// 45 | /// The total length of a record, in bytes. 46 | /// 47 | /// Because USN_RECORD_V2 is a variable size, the RecordLength member should be used when calculating the address 48 | /// of the next record in an output buffer, for example, a buffer that is returned from operations for the DeviceIoControl 49 | /// function that work with USN_RECORD_V2. 50 | /// 51 | /// 52 | /// The size in bytes of any change journal record is at most the size of the USN_RECORD_V2 structure, plus 53 | /// MaximumComponentLength characters minus 1 (for the character declared in the structure) times the size of a wide character. 54 | /// The value of MaximumComponentLength may be determined by calling the GetVolumeInformation function. In C, you can determine a 55 | /// record size by using the following code example. 56 | /// 57 | /// 58 | /// To maintain compatibility across version changes of the change journal software, use a run-time calculation to determine the 59 | /// size of the 60 | /// 61 | /// USN_RECORD_V2 62 | /// structure. For more information about compatibility across version changes, see the Remarks section in this topic. 63 | /// 64 | public uint RecordLength; 65 | 66 | /// 67 | /// The major version number of the change journal software for this record. 68 | /// For example, if the change journal software is version 2.0, the major version number is 2. 69 | /// 70 | /// 71 | /// Value 72 | /// Meaning 73 | /// 74 | /// 75 | /// 2 76 | /// The structure is a USN_RECORD_V2 structure and the remainder of the structure should be parsed using that layout. 77 | /// 78 | /// 79 | /// 3 80 | /// The structure is a USN_RECORD_V3 structure and the remainder of the structure should be parsed using that layout. 81 | /// 82 | /// 83 | /// 4 84 | /// The structure is a USN_RECORD_V4 structure and the remainder of the structure should be parsed using that layout. 85 | /// 86 | /// 87 | /// 88 | public ushort MajorVersion; 89 | 90 | /// 91 | /// The minor version number of the change journal software for this record. For example, if the change journal software is 92 | /// version 2.0, the minor version number is zero. 93 | /// 94 | public ushort MinorVersion; 95 | 96 | /// 97 | /// The ordinal number of the file or directory for which this record notes changes. 98 | /// This is an arbitrarily assigned value that associates a journal record with a file. 99 | /// 100 | public ulong FileReferenceNumber; 101 | 102 | /// 103 | /// The ordinal number of the directory where the file or directory that is associated with this record is located. 104 | /// This is an arbitrarily assigned value that associates a journal record with a parent directory. 105 | /// 106 | public ulong ParentFileReferenceNumber; 107 | 108 | /// The USN of this record. 109 | public long Usn; 110 | 111 | /// The standard UTC time stamp (FILETIME) of this record, in 64-bit format. 112 | public LongFileTime TimeStamp; 113 | 114 | /// 115 | /// 116 | /// The flags that identify reasons for changes that have accumulated in this file or directory journal record since the file or 117 | /// directory opened. 118 | /// 119 | /// 120 | /// When a file or directory closes, then a final USN record is generated with the USN_REASON_CLOSE flag set. The next 121 | /// change (for example, after the next open operation or deletion) starts a new record with a new set of reason flags. 122 | /// 123 | /// 124 | /// A rename or move operation generates two USN records, one that records the old parent directory for the item, and one that 125 | /// records a new parent. 126 | /// 127 | /// The following table identifies the possible flags. 128 | /// Note Unused bits are reserved. 129 | /// 130 | /// 131 | /// Value 132 | /// Meaning 133 | /// 134 | /// 135 | /// USN_REASON_BASIC_INFO_CHANGE 0x00008000 136 | /// 137 | /// A user has either changed one or more file or directory attributes (for example, the read-only, hidden, system, archive, or 138 | /// sparse attribute), or one or more time stamps. 139 | /// 140 | /// 141 | /// 142 | /// USN_REASON_CLOSE 0x80000000 143 | /// The file or directory is closed. 144 | /// 145 | /// 146 | /// USN_REASON_COMPRESSION_CHANGE 0x00020000 147 | /// The compression state of the file or directory is changed from or to compressed. 148 | /// 149 | /// 150 | /// USN_REASON_DATA_EXTEND 0x00000002 151 | /// The file or directory is extended (added to). 152 | /// 153 | /// 154 | /// USN_REASON_DATA_OVERWRITE 0x00000001 155 | /// The data in the file or directory is overwritten. 156 | /// 157 | /// 158 | /// USN_REASON_DATA_TRUNCATION 0x00000004 159 | /// The file or directory is truncated. 160 | /// 161 | /// 162 | /// USN_REASON_EA_CHANGE 0x00000400 163 | /// 164 | /// The user made a change to the extended attributes of a file or directory. These NTFS file system attributes are not 165 | /// accessible to Windows-based applications. 166 | /// 167 | /// 168 | /// 169 | /// USN_REASON_ENCRYPTION_CHANGE 0x00040000 170 | /// The file or directory is encrypted or decrypted. 171 | /// 172 | /// 173 | /// USN_REASON_FILE_CREATE 0x00000100 174 | /// The file or directory is created for the first time. 175 | /// 176 | /// 177 | /// USN_REASON_FILE_DELETE 0x00000200 178 | /// The file or directory is deleted. 179 | /// 180 | /// 181 | /// USN_REASON_HARD_LINK_CHANGE 0x00010000 182 | /// 183 | /// An NTFS file system hard link is added to or removed from the file or directory. An NTFS file system hard link, similar to a 184 | /// POSIX hard link, is one of several directory entries that see the same file or directory. 185 | /// 186 | /// 187 | /// 188 | /// USN_REASON_INDEXABLE_CHANGE 0x00004000 189 | /// 190 | /// A user changes the FILE_ATTRIBUTE_NOT_CONTENT_INDEXED attribute. That is, the user changes the file or directory from one 191 | /// where content can be indexed to one where content cannot be indexed, or vice versa. Content indexing permits rapid searching 192 | /// of data by building a database of selected content. 193 | /// 194 | /// 195 | /// 196 | /// USN_REASON_INTEGRITY_CHANGE 0x00800000 197 | /// 198 | /// A user changed the state of the FILE_ATTRIBUTE_INTEGRITY_STREAM attribute for the given stream. On the ReFS file system, 199 | /// integrity streams maintain a checksum of all data for that stream, so that the contents of the file can be validated during 200 | /// read or write operations. 201 | /// 202 | /// 203 | /// 204 | /// USN_REASON_NAMED_DATA_EXTEND 0x00000020 205 | /// The one or more named data streams for a file are extended (added to). 206 | /// 207 | /// 208 | /// USN_REASON_NAMED_DATA_OVERWRITE 0x00000010 209 | /// The data in one or more named data streams for a file is overwritten. 210 | /// 211 | /// 212 | /// USN_REASON_NAMED_DATA_TRUNCATION 0x00000040 213 | /// The one or more named data streams for a file is truncated. 214 | /// 215 | /// 216 | /// USN_REASON_OBJECT_ID_CHANGE 0x00080000 217 | /// The object identifier of a file or directory is changed. 218 | /// 219 | /// 220 | /// USN_REASON_RENAME_NEW_NAME 0x00002000 221 | /// A file or directory is renamed, and the file name in the USN_RECORD_V2 structure is the new name. 222 | /// 223 | /// 224 | /// USN_REASON_RENAME_OLD_NAME 0x00001000 225 | /// The file or directory is renamed, and the file name in the USN_RECORD_V2 structure is the previous name. 226 | /// 227 | /// 228 | /// USN_REASON_REPARSE_POINT_CHANGE 0x00100000 229 | /// 230 | /// The reparse point that is contained in a file or directory is changed, or a reparse point is added to or deleted from a file 231 | /// or directory. 232 | /// 233 | /// 234 | /// 235 | /// USN_REASON_SECURITY_CHANGE 0x00000800 236 | /// A change is made in the access rights to a file or directory. 237 | /// 238 | /// 239 | /// USN_REASON_STREAM_CHANGE 0x00200000 240 | /// A named stream is added to or removed from a file, or a named stream is renamed. 241 | /// 242 | /// 243 | /// USN_REASON_TRANSACTED_CHANGE 0x00400000 244 | /// The given stream is modified through a TxF transaction. 245 | /// 246 | /// 247 | /// 248 | public UsnReason Reason; 249 | 250 | /// 251 | /// Additional information about the source of the change, set by the FSCTL_MARK_HANDLE of the DeviceIoControl operation. 252 | /// 253 | /// When a thread writes a new USN record, the source information flags in the prior record continues to be present only if the 254 | /// thread also sets those flags. Therefore, the source information structure allows applications to filter out USN records that 255 | /// are set only by a known source, for example, an antivirus filter. 256 | /// 257 | /// One of the two following values can be set. 258 | /// 259 | /// 260 | /// Value 261 | /// Meaning 262 | /// 263 | /// 264 | /// USN_SOURCE_AUXILIARY_DATA 0x00000002 265 | /// 266 | /// The operation adds a private data stream to a file or directory. An example might be a virus detector adding checksum 267 | /// information. As the virus detector modifies the item, the system generates USN records. USN_SOURCE_AUXILIARY_DATA indicates 268 | /// that the modifications did not change the application data. 269 | /// 270 | /// 271 | /// 272 | /// USN_SOURCE_DATA_MANAGEMENT 0x00000001 273 | /// 274 | /// The operation provides information about a change to the file or directory made by the operating system. A typical use is 275 | /// when the Remote Storage system moves data from external to local storage. Remote Storage is the hierarchical storage 276 | /// management software. Such a move usually at a minimum adds the USN_REASON_DATA_OVERWRITE flag to a USN record. However, the 277 | /// data has not changed from the user's point of view. By noting USN_SOURCE_DATA_MANAGEMENT in the SourceInfo member, you can 278 | /// determine that although a write operation is performed on the item, data has not changed. 279 | /// 280 | /// 281 | /// 282 | /// USN_SOURCE_REPLICATION_MANAGEMENT 0x00000004 283 | /// 284 | /// The operation is modifying a file to match the contents of the same file which exists in another member of the replica set. 285 | /// 286 | /// 287 | /// 288 | /// USN_SOURCE_CLIENT_REPLICATION_MANAGEMENT 0x00000008 289 | /// The operation is modifying a file on client systems to match the contents of the same file that exists in the cloud. 290 | /// 291 | /// 292 | /// 293 | public UsnSource SourceInfo; 294 | 295 | /// The unique security identifier assigned to the file or directory associated with this record. 296 | public uint SecurityId; 297 | 298 | /// 299 | /// The attributes for the file or directory associated with this record, as returned by the GetFileAttributes function. 300 | /// Attributes of streams associated with the file or directory are excluded. 301 | /// 302 | public FileFlagsAndAttributes FileAttributes; 303 | 304 | /// 305 | /// The length of the name of the file or directory associated with this record, in bytes. The FileName member contains 306 | /// this name. Use this member to determine file name length, rather than depending on a trailing '\0' to delimit the file name 307 | /// in FileName. 308 | /// 309 | public ushort FileNameLength; 310 | 311 | /// The offset of the FileName member from the beginning of the structure. 312 | public ushort FileNameOffset; 313 | 314 | /// 315 | /// 316 | /// The name of the file or directory associated with this record in Unicode format. This file or directory name is of variable length. 317 | /// 318 | /// 319 | /// When working with FileName, do not count on the file name that contains a trailing '\0' delimiter, but instead 320 | /// determine the length of the file name by using FileNameLength. 321 | /// 322 | /// 323 | /// Do not perform any compile-time pointer arithmetic using FileName. Instead, make necessary calculations at run time by 324 | /// using the value of the FileNameOffset member. Doing so helps make your code compatible with any future versions of USN_RECORD_V2. 325 | /// 326 | /// 327 | private char _fileName; 328 | 329 | public unsafe ReadOnlySpan FileName => MemoryMarshal.CreateReadOnlySpan(ref _fileName, FileNameLength / sizeof(char)); 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /UsnParser/Native/UsnReason.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UsnParser.Native 4 | { 5 | [Flags] 6 | public enum UsnReason : uint 7 | { 8 | NONE = 0x00000000, 9 | 10 | /// 11 | /// A user has either changed one or more file or directory attributes (for example, the read-only, hidden, system, archive, or 12 | /// sparse attribute), or one or more time stamps. 13 | /// 14 | BASIC_INFO_CHANGE = 0x00008000, 15 | 16 | /// The file or directory is closed. 17 | CLOSE = 0x80000000, 18 | 19 | /// The compression state of the file or directory is changed from or to compressed. 20 | COMPRESSION_CHANGE = 0x00020000, 21 | 22 | /// The file or directory is extended (added to). 23 | DATA_EXTEND = 0x00000002, 24 | 25 | /// The data in the file or directory is overwritten. 26 | DATA_OVERWRITE = 0x00000001, 27 | 28 | /// The file or directory is truncated. 29 | DATA_TRUNCATION = 0x00000004, 30 | 31 | /// 32 | /// The user made a change to the extended attributes of a file or directory. 33 | /// These NTFS file system attributes are not accessible to Windows-based applications. 34 | /// 35 | EA_CHANGE = 0x00000400, 36 | 37 | /// The file or directory is encrypted or decrypted. 38 | ENCRYPTION_CHANGE = 0x00040000, 39 | 40 | /// The file or directory is created for the first time. 41 | FILE_CREATE = 0x00000100, 42 | 43 | /// The file or directory is deleted. 44 | FILE_DELETE = 0x00000200, 45 | 46 | /// 47 | /// An NTFS file system hard link is added to or removed from the file or directory. 48 | /// 49 | /// An NTFS file system hard link, similar to a POSIX hard link, is one of several directory entries that see the same file or directory. 50 | /// 51 | /// 52 | HARD_LINK_CHANGE = 0x00010000, 53 | 54 | /// 55 | /// A user changes the FILE_ATTRIBUTE_NOT_CONTENT_INDEXED attribute. 56 | /// 57 | /// That is, the user changes the file or directory from one where content can be indexed to one where content cannot be indexed, 58 | /// or vice versa. Content indexing permits rapid searching of data by building a database of selected content. 59 | /// 60 | /// 61 | INDEXABLE_CHANGE = 0x00004000, 62 | 63 | /// 64 | /// A user changed the state of the FILE_ATTRIBUTE_INTEGRITY_STREAM attribute for the given stream. 65 | /// 66 | /// On the ReFS file system, integrity streams maintain a checksum of all data for that stream, so that the contents of the file 67 | /// can be validated during read or write operations. 68 | /// 69 | /// 70 | INTEGRITY_CHANGE = 0x00800000, 71 | 72 | /// The one or more named data streams for a file are extended (added to). 73 | NAMED_DATA_EXTEND = 0x00000020, 74 | 75 | /// The data in one or more named data streams for a file is overwritten. 76 | NAMED_DATA_OVERWRITE = 0x00000010, 77 | 78 | /// The one or more named data streams for a file is truncated. 79 | NAMED_DATA_TRUNCATION = 0x00000040, 80 | 81 | /// The object identifier of a file or directory is changed. 82 | OBJECT_ID_CHANGE = 0x00080000, 83 | 84 | /// A file or directory is renamed, and the file name in the USN_RECORD_V2 structure is the new name. 85 | RENAME_NEW_NAME = 0x00002000, 86 | 87 | /// The file or directory is renamed, and the file name in the USN_RECORD_V2 structure is the previous name. 88 | RENAME_OLD_NAME = 0x00001000, 89 | 90 | /// 91 | /// The reparse point that is contained in a file or directory is changed, or a reparse point is added to or deleted from a file 92 | /// or directory. 93 | /// 94 | REPARSE_POINT_CHANGE = 0x00100000, 95 | 96 | /// A change is made in the access rights to a file or directory. 97 | SECURITY_CHANGE = 0x00000800, 98 | 99 | /// A named stream is added to or removed from a file, or a named stream is renamed. 100 | STREAM_CHANGE = 0x00200000, 101 | 102 | /// The given stream is modified through a TxF transaction. 103 | TRANSACTED_CHANGE = 0x00400000, 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /UsnParser/Native/UsnSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UsnParser.Native 4 | { 5 | [Flags] 6 | public enum UsnSource : uint 7 | { 8 | NONE = 0x00000000, 9 | 10 | /// 11 | /// The operation adds a private data stream to a file or directory. 12 | /// 13 | /// An example might be a virus detector adding checksum information. As the virus detector modifies the item, the system 14 | /// generates USN records. USN_SOURCE_AUXILIARY_DATA indicates that the modifications did not change the application data. 15 | /// 16 | /// 17 | DATA_MANAGEMENT = 0x00000001, 18 | 19 | /// 20 | /// The operation provides information about a change to the file or directory made by the operating system. 21 | /// 22 | /// A typical use is when the Remote Storage system moves data from external to local storage. Remote Storage is the hierarchical 23 | /// storage management software. Such a move usually at a minimum adds the USN_REASON_DATA_OVERWRITE flag to a USN record. 24 | /// However, the data has not changed from the user's point of view. By noting USN_SOURCE_DATA_MANAGEMENT in the SourceInfo 25 | /// member, you can determine that although a write operation is performed on the item, data has not changed. 26 | /// 27 | /// 28 | AUXILIARY_DATA = 0x00000002, 29 | 30 | 31 | /// 32 | /// The operation is modifying a file to match the contents of the same file which exists in another member of the replica set. 33 | /// 34 | REPLICATION_MANAGEMENT = 0x00000004, 35 | 36 | /// 37 | /// The operation is modifying a file on client systems to match the contents of the same file that exists in the cloud. 38 | /// 39 | CLIENT_REPLICATION_MANAGEMENT = 0x00000008, 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /UsnParser/Native/Win32Error.cs: -------------------------------------------------------------------------------- 1 | namespace UsnParser.Native 2 | { 3 | public enum Win32Error 4 | { 5 | INVALID_HANDLE_VALUE = -1, 6 | ERROR_SUCCESS = 0, 7 | ERROR_INVALID_FUNCTION = 1, 8 | ERROR_FILE_NOT_FOUND = 2, 9 | ERROR_PATH_NOT_FOUND = 3, 10 | ERROR_TOO_MANY_OPEN_FILES = 4, 11 | ERROR_ACCESS_DENIED = 5, 12 | ERROR_INVALID_HANDLE = 6, 13 | ERROR_INVALID_DATA = 13, 14 | ERROR_HANDLE_EOF = 38, 15 | ERROR_NOT_SUPPORTED = 50, 16 | ERROR_INVALID_PARAMETER = 87, 17 | ERROR_JOURNAL_DELETE_IN_PROGRESS = 1178, 18 | ERROR_JOURNAL_NOT_ACTIVE = 1179, 19 | ERROR_JOURNAL_ENTRY_DELETED = 1181, 20 | ERROR_INVALID_USER_BUFFER = 1784 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /UsnParser/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.IO; 5 | using System.Reflection; 6 | using System.Security.Principal; 7 | using System.Threading; 8 | using McMaster.Extensions.CommandLineUtils; 9 | using UsnParser.Extensions; 10 | using UsnParser.Native; 11 | 12 | namespace UsnParser 13 | { 14 | [Command( 15 | Name = "UsnParser", 16 | FullName = "NTFS USN Journal parser", 17 | Description = "A command utility for NTFS to search the MFT & monitoring the changes of USN Journal.")] 18 | [VersionOptionFromMember(MemberName = nameof(GetVersion))] 19 | [Subcommand(typeof(MonitorCommand), typeof(SearchCommand), typeof(ReadCommand))] 20 | [HelpOption("-h|--help")] 21 | internal class UsnParser 22 | { 23 | public static int Main(string[] args) 24 | => CommandLineApplication.Execute(args); 25 | 26 | private static string GetVersion() => 27 | typeof(UsnParser).Assembly.GetCustomAttribute()?.InformationalVersion ?? "Unknown"; 28 | 29 | #pragma warning disable CA1822 // Mark members as static 30 | private int OnExecute(CommandLineApplication app) 31 | #pragma warning restore CA1822 // Mark members as static 32 | { 33 | // This shows help even if the --help option isn't specified 34 | app.ShowHelp(); 35 | return 1; 36 | } 37 | 38 | [HelpOption("-h|--help", Inherited = true)] 39 | private abstract class SubCommandBase 40 | { 41 | [Argument(0, Description = "Volume name, e.g. C: ")] 42 | [Required] 43 | public required string Volume { get; set; } 44 | 45 | [Option("-f|--filter", Description = "Filter the result with keyword, wildcards are permitted")] 46 | public string? Keyword { get; set; } 47 | 48 | [Option("-fo|--FileOnly", Description = "Only show the file entries")] 49 | public bool FileOnly { get; set; } 50 | 51 | [Option("-do|--DirectoryOnly", Description = "Only show the directory entries")] 52 | public bool DirectoryOnly { get; set; } 53 | 54 | [Option("--caseSensitive", Description = "Use case-sensitive matching, default is false")] 55 | public bool CaseSensitive { get; set; } 56 | 57 | protected CancellationToken _cancellationToken; 58 | 59 | protected readonly IConsole _console = PhysicalConsole.Singleton; 60 | 61 | protected FilterOptions _filterOptions = FilterOptions.Default; 62 | 63 | protected int OnExecute(CommandLineApplication app) 64 | { 65 | try 66 | { 67 | var cts = new CancellationTokenSource(); 68 | _cancellationToken = cts.Token; 69 | 70 | _console.CancelKeyPress += (o, e) => 71 | { 72 | _console.WriteLine("Ctrl+C is pressed, exiting..."); 73 | cts.Cancel(); 74 | }; 75 | 76 | if (!OperatingSystem.IsWindows()) 77 | { 78 | _console.PrintError($"This tool only support Windows, since it used NTFS specific features."); 79 | return -1; 80 | } 81 | 82 | var driveInfo = new DriveInfo(Volume); 83 | using var usnJournal = new UsnJournal(driveInfo); 84 | #if DEBUG 85 | _console.PrintUsnJournalData(usnJournal.JournalInfo); 86 | #endif 87 | 88 | _filterOptions = new FilterOptions(Keyword, FileOnly, DirectoryOnly, CaseSensitive); 89 | return Run(usnJournal); 90 | } 91 | catch (Exception ex) 92 | { 93 | _console.PrintError(ex.Message); 94 | 95 | if (ex is Win32Exception win32Ex && win32Ex.NativeErrorCode == (int)Win32Error.ERROR_ACCESS_DENIED && !HasAdministratorPrivilege()) 96 | { 97 | _console.PrintError($"You need system administrator privileges to access the USN journal of drive {Volume.ToUpper()}."); 98 | } 99 | 100 | return -1; 101 | } 102 | } 103 | 104 | protected abstract int Run(UsnJournal usnJournal); 105 | 106 | private static bool HasAdministratorPrivilege() 107 | { 108 | using var identity = WindowsIdentity.GetCurrent(); 109 | var principal = new WindowsPrincipal(identity); 110 | return principal.IsInRole(WindowsBuiltInRole.Administrator); 111 | } 112 | } 113 | 114 | [Command("monitor", Description = "Monitor real-time USN journal changes")] 115 | private class MonitorCommand : SubCommandBase 116 | { 117 | protected override int Run(UsnJournal usnJournal) 118 | { 119 | var usnEntries = usnJournal.MonitorLiveUsn(usnJournal.JournalInfo.UsnJournalID, usnJournal.JournalInfo.NextUsn, _filterOptions); 120 | foreach (var entry in usnEntries) 121 | { 122 | if (_cancellationToken.IsCancellationRequested) return -1; 123 | 124 | _console.PrintUsnEntryFull(usnJournal, entry); 125 | } 126 | return 0; 127 | } 128 | } 129 | 130 | [Command("search", Description = "Search the Master File Table")] 131 | private class SearchCommand : SubCommandBase 132 | { 133 | protected override int Run(UsnJournal usnJournal) 134 | { 135 | var usnEntries = usnJournal.EnumerateMasterFileTable(usnJournal.JournalInfo.NextUsn, _filterOptions); 136 | foreach (var entry in usnEntries) 137 | { 138 | if (_cancellationToken.IsCancellationRequested) return -1; 139 | 140 | _console.PrintUsnEntryBasic(usnJournal, entry); 141 | } 142 | return 0; 143 | } 144 | } 145 | 146 | [Command("read", Description = "Read history USN journal entries")] 147 | private class ReadCommand : SubCommandBase 148 | { 149 | protected override int Run(UsnJournal usnJournal) 150 | { 151 | var usnEntries = usnJournal.EnumerateUsnEntries(usnJournal.JournalInfo.UsnJournalID, _filterOptions); 152 | foreach (var entry in usnEntries) 153 | { 154 | if (_cancellationToken.IsCancellationRequested) return -1; 155 | 156 | _console.PrintUsnEntryFull(usnJournal, entry); 157 | } 158 | return 0; 159 | } 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /UsnParser/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "UsnParser": { 4 | "commandName": "Project", 5 | "commandLineArgs": "search D:" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /UsnParser/UsnJournal.cs: -------------------------------------------------------------------------------- 1 | using DotNet.Globbing; 2 | using McMaster.Extensions.CommandLineUtils; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.IO; 7 | using System.Runtime.InteropServices; 8 | using UsnParser.Native; 9 | using Microsoft.Win32.SafeHandles; 10 | using static UsnParser.Native.Kernel32; 11 | using static UsnParser.Native.Ntdll; 12 | using FileAccess = UsnParser.Native.FileAccess; 13 | using UsnParser.Enumeration; 14 | using static UsnParser.Enumeration.BaseEnumerable; 15 | using UsnParser.Cache; 16 | 17 | namespace UsnParser 18 | { 19 | public class UsnJournal : IDisposable 20 | { 21 | private readonly DriveInfo _driveInfo; 22 | private readonly SafeFileHandle _volumeRootHandle; 23 | private readonly LRUCache _lruCache; 24 | private readonly bool _isReFS; 25 | 26 | public string VolumeName { get; } 27 | 28 | public USN_JOURNAL_DATA_V0 JournalInfo { get; private set; } 29 | 30 | public UsnJournal(DriveInfo driveInfo) 31 | { 32 | _driveInfo = driveInfo; 33 | VolumeName = driveInfo.Name; 34 | 35 | var isNTFS = _driveInfo.DriveFormat.Equals("NTFS", StringComparison.OrdinalIgnoreCase); 36 | _isReFS = _driveInfo.DriveFormat.Equals("ReFs", StringComparison.OrdinalIgnoreCase); 37 | 38 | if (!(isNTFS || _isReFS)) 39 | { 40 | throw new Exception($"{_driveInfo.Name} is not an NTFS or ReFS volume, which does not support USN change journal."); 41 | } 42 | 43 | _volumeRootHandle = GetVolumeRootHandle(); 44 | _lruCache = new LRUCache(4096); 45 | Init(); 46 | } 47 | 48 | private void Init() 49 | { 50 | if (_volumeRootHandle.IsInvalid) 51 | throw new Win32Exception((int)Win32Error.ERROR_INVALID_HANDLE); 52 | 53 | try 54 | { 55 | JournalInfo = QueryUsnJournalInfo(); 56 | } 57 | catch (Win32Exception ex) 58 | { 59 | if (ex.NativeErrorCode == (int)Win32Error.ERROR_JOURNAL_NOT_ACTIVE) 60 | { 61 | var shouldCreate = Prompt.GetYesNo( 62 | $"The change journal of volume {VolumeName} is not active, active it now?", 63 | defaultAnswer: true); 64 | 65 | if (shouldCreate) 66 | { 67 | // Set default max size to 32MB, default allocation delta to 8MB. 68 | CreateUsnJournal(0x2000000, 0x800000); 69 | JournalInfo = QueryUsnJournalInfo(); 70 | return; 71 | } 72 | } 73 | 74 | throw; 75 | } 76 | } 77 | 78 | private unsafe void CreateUsnJournal(ulong maxSize, ulong allocationDelta) 79 | { 80 | var createData = new CREATE_USN_JOURNAL_DATA 81 | { 82 | MaximumSize = maxSize, 83 | AllocationDelta = allocationDelta 84 | }; 85 | 86 | var createDataSize = sizeof(CREATE_USN_JOURNAL_DATA); 87 | var createDataBuffer = Marshal.AllocHGlobal(createDataSize); 88 | try 89 | { 90 | Marshal.StructureToPtr(createData, createDataBuffer, true); 91 | var bSuccess = DeviceIoControl( 92 | _volumeRootHandle, 93 | FSCTL_CREATE_USN_JOURNAL, 94 | createDataBuffer, 95 | createDataSize, 96 | IntPtr.Zero, 97 | 0, 98 | out _, 99 | IntPtr.Zero); 100 | if (!bSuccess) 101 | { 102 | var lastError = Marshal.GetLastWin32Error(); 103 | throw new Win32Exception(lastError); 104 | } 105 | } 106 | finally 107 | { 108 | Marshal.FreeHGlobal(createDataBuffer); 109 | } 110 | } 111 | 112 | public IEnumerable EnumerateMasterFileTable(long highUsn, FilterOptions filterOptions) 113 | { 114 | // Note: 115 | // In ReFS there is no MFT and subsequently no MFT entries. 116 | // http://www.resilientfilesystem.co.uk/refs-master-file-table 117 | if (_isReFS) 118 | { 119 | throw new NotSupportedException($"The file system of drive {VolumeName} is ReFS, which does not have a MFT(Master File Table) to search on."); 120 | } 121 | 122 | var options = MasterFileTableEnumerationOptions.Default; 123 | return new MasterFileTableEnumerable(_volumeRootHandle, highUsn, options, Filter(filterOptions)); 124 | } 125 | 126 | private static FindPredicate Filter(FilterOptions filterOptions) 127 | { 128 | return usnEntry => 129 | { 130 | if (filterOptions.FileOnly && usnEntry.IsFolder) return false; 131 | if (filterOptions.DirectoryOnly && !usnEntry.IsFolder) return false; 132 | 133 | if (string.IsNullOrWhiteSpace(filterOptions.Keyword)) return true; 134 | 135 | var globOptions = new GlobOptions { Evaluation = { CaseInsensitive = !filterOptions.CaseSensitive } }; 136 | var glob = Glob.Parse(filterOptions.Keyword, globOptions); 137 | return glob.IsMatch(usnEntry.FileName); 138 | }; 139 | } 140 | 141 | public IEnumerable MonitorLiveUsn(ulong usnJournalId, long startUsn, FilterOptions filterOptions) 142 | { 143 | var options = new ChangeJournalEnumerationOptions 144 | { 145 | BytesToWaitFor = 1, 146 | Timeout = 0, 147 | ReturnOnlyOnClose = false, 148 | StartUsn = startUsn, 149 | }; 150 | return new ChangeJournalEnumerable(_volumeRootHandle, usnJournalId, options, Filter(filterOptions)); 151 | } 152 | 153 | public IEnumerable EnumerateUsnEntries(ulong usnJournalId, FilterOptions filterOptions) 154 | { 155 | var options = new ChangeJournalEnumerationOptions 156 | { 157 | BytesToWaitFor = 0, 158 | Timeout = 0, 159 | ReturnOnlyOnClose = false, 160 | StartUsn = 0, 161 | }; 162 | return new ChangeJournalEnumerable(_volumeRootHandle, usnJournalId, options, Filter(filterOptions)); 163 | } 164 | 165 | private SafeFileHandle GetVolumeRootHandle() 166 | { 167 | var vol = string.Concat(@"\\.\", _driveInfo.Name.TrimEnd('\\')); 168 | 169 | var rootHandle = CreateFile( 170 | vol, 171 | FileAccess.GENERIC_READ, 172 | FileShare.ReadWrite, 173 | default, 174 | FileMode.Open, 175 | 0, 176 | default 177 | ); 178 | 179 | if (rootHandle.IsInvalid) 180 | { 181 | throw new Win32Exception(Marshal.GetLastWin32Error()); 182 | } 183 | 184 | return rootHandle; 185 | } 186 | 187 | private unsafe USN_JOURNAL_DATA_V0 QueryUsnJournalInfo() 188 | { 189 | var journalDataSize = sizeof(USN_JOURNAL_DATA_V0); 190 | var journalDataPtr = Marshal.AllocHGlobal(journalDataSize); 191 | try 192 | { 193 | var bSuccess = DeviceIoControl( 194 | _volumeRootHandle, 195 | FSCTL_QUERY_USN_JOURNAL, 196 | IntPtr.Zero, 197 | 0, 198 | journalDataPtr, 199 | journalDataSize, 200 | out var _, 201 | IntPtr.Zero); 202 | 203 | if (!bSuccess) 204 | { 205 | var lastError = Marshal.GetLastWin32Error(); 206 | throw new Win32Exception(lastError); 207 | } 208 | 209 | return Marshal.PtrToStructure(journalDataPtr); 210 | } 211 | finally 212 | { 213 | Marshal.FreeHGlobal(journalDataPtr); 214 | } 215 | } 216 | 217 | public unsafe bool TryGetPath(UsnEntry entry, out string? path) 218 | { 219 | path = null; 220 | if (entry.ParentFileReferenceNumber == 0) return false; 221 | if (_lruCache.TryGet(entry.FileReferenceNumber, out path)) 222 | { 223 | return true; 224 | } 225 | 226 | if (_lruCache.TryGet(entry.ParentFileReferenceNumber, out var parentPath)) 227 | { 228 | path = Path.Join(parentPath, entry.FileName); 229 | _lruCache.Set(entry.FileReferenceNumber, path); 230 | return true; 231 | } 232 | 233 | var parentFileId = entry.ParentFileReferenceNumber; 234 | var unicodeString = new UNICODE_STRING 235 | { 236 | Length = sizeof(long), 237 | MaximumLength = sizeof(long), 238 | Buffer = new IntPtr(&parentFileId) 239 | }; 240 | var objAttributes = new OBJECT_ATTRIBUTES 241 | { 242 | length = (uint)sizeof(OBJECT_ATTRIBUTES), 243 | objectName = &unicodeString, 244 | rootDirectory = _volumeRootHandle.DangerousGetHandle(), 245 | attributes = (int)ObjectAttribute.OBJ_CASE_INSENSITIVE 246 | }; 247 | 248 | var status = NtCreateFile( 249 | out var fileHandle, 250 | FileAccess.GENERIC_READ, 251 | objAttributes, 252 | out var ioStatusBlock, 253 | 0, 254 | 0, 255 | FileShare.ReadWrite | FileShare.Delete, 256 | NtFileMode.FILE_OPEN, 257 | NtFileCreateOptions.FILE_OPEN_BY_FILE_ID | NtFileCreateOptions.FILE_OPEN_FOR_BACKUP_INTENT, 258 | IntPtr.Zero, 259 | 0); 260 | 261 | using (fileHandle) 262 | { 263 | if (status == STATUS_SUCCESS) 264 | { 265 | var pathBufferSize = MAX_PATH; 266 | while (true) 267 | { 268 | var pathBuffer = Marshal.AllocHGlobal(pathBufferSize); 269 | try 270 | { 271 | status = NtQueryInformationFile( 272 | fileHandle, 273 | ioStatusBlock, 274 | pathBuffer, 275 | (uint)pathBufferSize, 276 | FILE_INFORMATION_CLASS.FileNameInformation); 277 | if (status == STATUS_SUCCESS) 278 | { 279 | var nameInfo = (FILE_NAME_INFORMATION*)pathBuffer; 280 | 281 | parentPath = Path.Join(VolumeName.TrimEnd(Path.DirectorySeparatorChar), nameInfo->FileName.ToString()); 282 | _lruCache.Set(parentFileId, parentPath); 283 | 284 | path = Path.Join(parentPath, entry.FileName); 285 | if (entry.IsFolder) 286 | { 287 | _lruCache.Set(entry.FileReferenceNumber, path); 288 | } 289 | 290 | return true; 291 | } 292 | else if (status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_BUFFER_OVERFLOW) 293 | { 294 | // The buffer size is not large enough to contain the name information, 295 | // increase the buffer size by a factor of 2 then try again. 296 | pathBufferSize *= 2; 297 | } 298 | else 299 | { 300 | return false; 301 | } 302 | } 303 | finally 304 | { 305 | Marshal.FreeHGlobal(pathBuffer); 306 | } 307 | } 308 | } 309 | } 310 | 311 | return false; 312 | } 313 | 314 | #region IDisposable 315 | 316 | public void Dispose() 317 | { 318 | Dispose(true); 319 | GC.SuppressFinalize(this); 320 | } 321 | 322 | private void Dispose(bool disposing) 323 | { 324 | if (disposing) 325 | _volumeRootHandle.Dispose(); 326 | } 327 | 328 | #endregion 329 | } 330 | } 331 | 332 | -------------------------------------------------------------------------------- /UsnParser/UsnParser.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0-windows 6 | 0.2.1 7 | full 8 | true 9 | true 10 | enable 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /publish.ps1: -------------------------------------------------------------------------------- 1 | $rids = "win-x64" , "win-arm64" 2 | 3 | Push-Location .\UsnParser 4 | 5 | foreach ($rid in $rids) { 6 | dotnet publish -c release -r $rid -o ..\publish\$rid --self-contained true 7 | Remove-Item ..\publish\$rid\UsnParser.pdb -Force 8 | } 9 | 10 | Pop-Location 11 | 12 | New-Item -ItemType "directory" -Name "release" -Force 13 | 14 | foreach ($rid in $rids) { 15 | $path = ".\publish\$rid" 16 | 17 | $compress = @{ 18 | Path = $path 19 | CompressionLevel = "Optimal" 20 | DestinationPath = ".\release\UsnParser-$rid.zip" 21 | } 22 | 23 | Compress-Archive @compress -Force 24 | } --------------------------------------------------------------------------------