anshdadhich commited on
Commit
6eb91a5
·
verified ·
1 Parent(s): 48fb3f0

Upload FastSeekWpf/Core/MftReader.cs

Browse files
Files changed (1) hide show
  1. FastSeekWpf/Core/MftReader.cs +63 -13
FastSeekWpf/Core/MftReader.cs CHANGED
@@ -1,6 +1,7 @@
1
  using System;
2
  using System.Buffers.Binary;
3
  using System.Collections.Generic;
 
4
  using System.IO;
5
  using System.Runtime.InteropServices;
6
  using FastSeekWpf.NativeInterop;
@@ -31,13 +32,17 @@ public class MftReader : IDisposable
31
  IntPtr.Zero);
32
 
33
  if (_handle == new IntPtr(-1))
34
- throw new IOException($"Failed to open drive {drive.Letter}:");
 
 
 
35
  }
36
 
37
- public ScanResult? ScanDirect()
38
  {
39
  int? recordSize = ReadMftRecordSize();
40
- if (recordSize == null) return null;
 
41
 
42
  string mftPath = $"{_drive.Root}$MFT";
43
  IntPtr mftHandle = Win32Api.CreateFileW(
@@ -50,7 +55,10 @@ public class MftReader : IDisposable
50
  IntPtr.Zero);
51
 
52
  if (mftHandle == new IntPtr(-1))
53
- return null;
 
 
 
54
 
55
  try
56
  {
@@ -59,12 +67,19 @@ public class MftReader : IDisposable
59
  byte[] buffer = new byte[DirectBuf];
60
  ulong mftIndex = 0;
61
  int leftover = 0;
 
62
 
63
  while (true)
64
  {
65
  bool ok = Win32Api.ReadFile(mftHandle, buffer, (uint)(buffer.Length - leftover), out uint bytesRead, IntPtr.Zero);
66
- if (!ok || bytesRead == 0) break;
 
 
 
 
 
67
 
 
68
  int total = leftover + (int)bytesRead;
69
  int offset = 0;
70
 
@@ -111,8 +126,10 @@ public class MftReader : IDisposable
111
 
112
  try
113
  {
 
114
  while (true)
115
  {
 
116
  IntPtr bufferPtr = Marshal.AllocHGlobal(buffer.Length);
117
  try
118
  {
@@ -126,8 +143,12 @@ public class MftReader : IDisposable
126
  if (!ok)
127
  {
128
  int error = Marshal.GetLastWin32Error();
129
- if ((uint)error == 0x26) break;
130
- break;
 
 
 
 
131
  }
132
 
133
  if (bytesReturned <= 8) break;
@@ -171,36 +192,53 @@ public class MftReader : IDisposable
171
  Marshal.FreeHGlobal(bufferPtr);
172
  }
173
  }
 
 
174
  }
175
  finally
176
  {
177
  Marshal.FreeHGlobal(enumDataPtr);
178
  }
179
-
180
- return new ScanResult(records, nameData);
181
  }
182
 
183
  private int? ReadMftRecordSize()
184
  {
185
  bool ok = Win32Api.SetFilePointerEx(_handle, 0, out _, 0);
186
- if (!ok) return null;
 
 
 
 
 
187
 
188
  byte[] boot = new byte[512];
189
  ok = Win32Api.ReadFile(_handle, boot, 512, out uint br, IntPtr.Zero);
190
- if (!ok || br < 512) return null;
 
 
 
 
 
191
 
192
  if (boot[3] != 'N' || boot[4] != 'T' || boot[5] != 'F' || boot[6] != 'S')
 
 
193
  return null;
 
194
 
195
  ushort bytesPerSector = BinaryPrimitives.ReadUInt16LittleEndian(new ReadOnlySpan<byte>(boot, 0x0B, 2));
196
  byte sectorsPerCluster = boot[0x0D];
197
  int clusterSize = bytesPerSector * sectorsPerCluster;
198
 
199
  sbyte raw = (sbyte)boot[0x40];
 
200
  if (raw > 0)
201
- return raw * clusterSize;
202
  else
203
- return 1 << (-raw);
 
 
 
204
  }
205
 
206
  private static bool ApplyFixup(ReadOnlySpan<byte> record, int recordSize)
@@ -315,6 +353,18 @@ public class MftReader : IDisposable
315
  }
316
  }
317
 
 
 
 
 
 
 
 
 
 
 
 
 
318
  public void Dispose()
319
  {
320
  if (!_disposed)
 
1
  using System;
2
  using System.Buffers.Binary;
3
  using System.Collections.Generic;
4
+ using System.ComponentModel;
5
  using System.IO;
6
  using System.Runtime.InteropServices;
7
  using FastSeekWpf.NativeInterop;
 
32
  IntPtr.Zero);
33
 
34
  if (_handle == new IntPtr(-1))
35
+ {
36
+ int err = Marshal.GetLastWin32Error();
37
+ throw new IOException($"Failed to open drive {drive.Letter}: {Win32Error(err)}");
38
+ }
39
  }
40
 
41
+ public ScanResult ScanDirect()
42
  {
43
  int? recordSize = ReadMftRecordSize();
44
+ if (recordSize == null)
45
+ throw new IOException("Could not read boot sector / determine MFT record size.");
46
 
47
  string mftPath = $"{_drive.Root}$MFT";
48
  IntPtr mftHandle = Win32Api.CreateFileW(
 
55
  IntPtr.Zero);
56
 
57
  if (mftHandle == new IntPtr(-1))
58
+ {
59
+ int err = Marshal.GetLastWin32Error();
60
+ throw new IOException($"Could not open {mftPath}: {Win32Error(err)}");
61
+ }
62
 
63
  try
64
  {
 
67
  byte[] buffer = new byte[DirectBuf];
68
  ulong mftIndex = 0;
69
  int leftover = 0;
70
+ long totalBytesRead = 0;
71
 
72
  while (true)
73
  {
74
  bool ok = Win32Api.ReadFile(mftHandle, buffer, (uint)(buffer.Length - leftover), out uint bytesRead, IntPtr.Zero);
75
+ if (!ok)
76
+ {
77
+ int err = Marshal.GetLastWin32Error();
78
+ throw new IOException($"ReadFile({mftPath}) failed at offset {totalBytesRead}: {Win32Error(err)}");
79
+ }
80
+ if (bytesRead == 0) break;
81
 
82
+ totalBytesRead += bytesRead;
83
  int total = leftover + (int)bytesRead;
84
  int offset = 0;
85
 
 
126
 
127
  try
128
  {
129
+ int iterations = 0;
130
  while (true)
131
  {
132
+ iterations++;
133
  IntPtr bufferPtr = Marshal.AllocHGlobal(buffer.Length);
134
  try
135
  {
 
143
  if (!ok)
144
  {
145
  int error = Marshal.GetLastWin32Error();
146
+ if ((uint)error == 0x26) // ERROR_HANDLE_EOF — normal end
147
+ break;
148
+
149
+ throw new IOException(
150
+ $"FSCTL_ENUM_USN_DATA failed (iter {iterations}): {Win32Error(error)}. " +
151
+ $"StartFileReferenceNumber={enumData.StartFileReferenceNumber}");
152
  }
153
 
154
  if (bytesReturned <= 8) break;
 
192
  Marshal.FreeHGlobal(bufferPtr);
193
  }
194
  }
195
+
196
+ return new ScanResult(records, nameData);
197
  }
198
  finally
199
  {
200
  Marshal.FreeHGlobal(enumDataPtr);
201
  }
 
 
202
  }
203
 
204
  private int? ReadMftRecordSize()
205
  {
206
  bool ok = Win32Api.SetFilePointerEx(_handle, 0, out _, 0);
207
+ if (!ok)
208
+ {
209
+ int err = Marshal.GetLastWin32Error();
210
+ Logger.Log($"ReadMftRecordSize: SetFilePointerEx failed: {Win32Error(err)}");
211
+ return null;
212
+ }
213
 
214
  byte[] boot = new byte[512];
215
  ok = Win32Api.ReadFile(_handle, boot, 512, out uint br, IntPtr.Zero);
216
+ if (!ok || br < 512)
217
+ {
218
+ int err = Marshal.GetLastWin32Error();
219
+ Logger.Log($"ReadMftRecordSize: ReadFile failed (ok={ok}, read={br}): {Win32Error(err)}");
220
+ return null;
221
+ }
222
 
223
  if (boot[3] != 'N' || boot[4] != 'T' || boot[5] != 'F' || boot[6] != 'S')
224
+ {
225
+ Logger.Log("ReadMftRecordSize: Not an NTFS boot sector (missing 'NTFS' signature)");
226
  return null;
227
+ }
228
 
229
  ushort bytesPerSector = BinaryPrimitives.ReadUInt16LittleEndian(new ReadOnlySpan<byte>(boot, 0x0B, 2));
230
  byte sectorsPerCluster = boot[0x0D];
231
  int clusterSize = bytesPerSector * sectorsPerCluster;
232
 
233
  sbyte raw = (sbyte)boot[0x40];
234
+ int recordSize;
235
  if (raw > 0)
236
+ recordSize = raw * clusterSize;
237
  else
238
+ recordSize = 1 << (-raw);
239
+
240
+ Logger.Log($"ReadMftRecordSize: bytesPerSector={bytesPerSector}, sectorsPerCluster={sectorsPerCluster}, clusterSize={clusterSize}, raw={raw}, recordSize={recordSize}");
241
+ return recordSize;
242
  }
243
 
244
  private static bool ApplyFixup(ReadOnlySpan<byte> record, int recordSize)
 
353
  }
354
  }
355
 
356
+ private static string Win32Error(int code)
357
+ {
358
+ try
359
+ {
360
+ return new Win32Exception(code).Message;
361
+ }
362
+ catch
363
+ {
364
+ return $"error {code} (0x{code:X})";
365
+ }
366
+ }
367
+
368
  public void Dispose()
369
  {
370
  if (!_disposed)