├── .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 | }
--------------------------------------------------------------------------------