NTDirectoryFileSystem.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. /* Copyright (C) 2017-2020 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
  2. *
  3. * You can redistribute this program and/or modify it under the terms of
  4. * the GNU Lesser Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. */
  7. using System;
  8. using System.Collections.Generic;
  9. using System.IO;
  10. using System.Runtime.InteropServices;
  11. using System.Threading;
  12. using Microsoft.Win32.SafeHandles;
  13. using Utilities;
  14. namespace SMBLibrary.Win32
  15. {
  16. [StructLayout(LayoutKind.Sequential)]
  17. public struct UNICODE_STRING : IDisposable
  18. {
  19. public ushort Length;
  20. public ushort MaximumLength;
  21. private IntPtr Buffer;
  22. public UNICODE_STRING(string value)
  23. {
  24. Length = (ushort)(value.Length * 2);
  25. MaximumLength = (ushort)(value.Length + 2);
  26. Buffer = Marshal.StringToHGlobalUni(value);
  27. }
  28. public void Dispose()
  29. {
  30. Marshal.FreeHGlobal(Buffer);
  31. Buffer = IntPtr.Zero;
  32. }
  33. public override string ToString()
  34. {
  35. return Marshal.PtrToStringUni(Buffer);
  36. }
  37. }
  38. [StructLayoutAttribute(LayoutKind.Sequential)]
  39. public struct OBJECT_ATTRIBUTES
  40. {
  41. public int Length;
  42. public IntPtr RootDirectory;
  43. public IntPtr ObjectName;
  44. public uint Attributes;
  45. public IntPtr SecurityDescriptor;
  46. public IntPtr SecurityQualityOfService;
  47. }
  48. [StructLayoutAttribute(LayoutKind.Sequential)]
  49. public struct IO_STATUS_BLOCK
  50. {
  51. public UInt32 Status;
  52. public IntPtr Information;
  53. }
  54. internal class PendingRequest
  55. {
  56. public IntPtr FileHandle;
  57. public uint ThreadID;
  58. public IO_STATUS_BLOCK IOStatusBlock;
  59. public bool Cleanup;
  60. }
  61. public class NTDirectoryFileSystem : INTFileStore
  62. {
  63. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  64. private static extern NTStatus NtCreateFile(out IntPtr handle, uint desiredAccess, ref OBJECT_ATTRIBUTES objectAttributes, out IO_STATUS_BLOCK ioStatusBlock, ref long allocationSize, FileAttributes fileAttributes, ShareAccess shareAccess, CreateDisposition createDisposition, CreateOptions createOptions, IntPtr eaBuffer, uint eaLength);
  65. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  66. private static extern NTStatus NtClose(IntPtr handle);
  67. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  68. private static extern NTStatus NtReadFile(IntPtr handle, IntPtr evt, IntPtr apcRoutine, IntPtr apcContext, out IO_STATUS_BLOCK ioStatusBlock, byte[] buffer, uint length, ref long byteOffset, IntPtr key);
  69. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  70. private static extern NTStatus NtWriteFile(IntPtr handle, IntPtr evt, IntPtr apcRoutine, IntPtr apcContext, out IO_STATUS_BLOCK ioStatusBlock, byte[] buffer, uint length, ref long byteOffset, IntPtr key);
  71. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  72. private static extern NTStatus NtFlushBuffersFile(IntPtr handle, out IO_STATUS_BLOCK ioStatusBlock);
  73. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  74. private static extern NTStatus NtLockFile(IntPtr handle, IntPtr evt, IntPtr apcRoutine, IntPtr apcContext, out IO_STATUS_BLOCK ioStatusBlock, ref long byteOffset, ref long length, uint key, bool failImmediately, bool exclusiveLock);
  75. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  76. private static extern NTStatus NtUnlockFile(IntPtr handle, out IO_STATUS_BLOCK ioStatusBlock, ref long byteOffset, ref long length, uint key);
  77. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  78. private static extern NTStatus NtQueryDirectoryFile(IntPtr handle, IntPtr evt, IntPtr apcRoutine, IntPtr apcContext, out IO_STATUS_BLOCK ioStatusBlock, byte[] fileInformation, uint length, uint fileInformationClass, bool returnSingleEntry, ref UNICODE_STRING fileName, bool restartScan);
  79. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  80. private static extern NTStatus NtQueryInformationFile(IntPtr handle, out IO_STATUS_BLOCK ioStatusBlock, byte[] fileInformation, uint length, uint fileInformationClass);
  81. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  82. private static extern NTStatus NtSetInformationFile(IntPtr handle, out IO_STATUS_BLOCK ioStatusBlock, byte[] fileInformation, uint length, uint fileInformationClass);
  83. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  84. private static extern NTStatus NtQueryVolumeInformationFile(IntPtr handle, out IO_STATUS_BLOCK ioStatusBlock, byte[] fsInformation, uint length, uint fsInformationClass);
  85. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  86. private static extern NTStatus NtSetVolumeInformationFile(IntPtr handle, out IO_STATUS_BLOCK ioStatusBlock, byte[] fsInformation, uint length, uint fsInformationClass);
  87. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  88. private static extern NTStatus NtQuerySecurityObject(IntPtr handle, SecurityInformation securityInformation, byte[] securityDescriptor, uint length, out uint lengthNeeded);
  89. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  90. private static extern NTStatus NtSetSecurityObject(IntPtr handle, SecurityInformation securityInformation, byte[] securityDescriptor);
  91. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  92. private static extern NTStatus NtNotifyChangeDirectoryFile(IntPtr handle, IntPtr evt, IntPtr apcRoutine, IntPtr apcContext, out IO_STATUS_BLOCK ioStatusBlock, byte[] buffer, uint bufferSize, NotifyChangeFilter completionFilter, bool watchTree);
  93. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  94. private static extern NTStatus NtFsControlFile(IntPtr handle, IntPtr evt, IntPtr apcRoutine, IntPtr apcContext, out IO_STATUS_BLOCK ioStatusBlock, uint ioControlCode, byte[] inputBuffer, uint inputBufferLength, byte[] outputBuffer, uint outputBufferLength);
  95. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  96. private static extern NTStatus NtAlertThread(IntPtr threadHandle);
  97. // Available starting from Windows Vista.
  98. [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
  99. private static extern NTStatus NtCancelSynchronousIoFile(IntPtr threadHandle, IntPtr ioRequestToCancel, out IO_STATUS_BLOCK ioStatusBlock);
  100. private static readonly int QueryDirectoryBufferSize = 4096;
  101. private static readonly int FileInformationBufferSize = 8192;
  102. private static readonly int FileSystemInformationBufferSize = 4096;
  103. private DirectoryInfo m_directory;
  104. private PendingRequestCollection m_pendingRequests = new PendingRequestCollection();
  105. public NTDirectoryFileSystem(string path) : this(new DirectoryInfo(path))
  106. {
  107. }
  108. public NTDirectoryFileSystem(DirectoryInfo directory)
  109. {
  110. m_directory = directory;
  111. }
  112. private OBJECT_ATTRIBUTES InitializeObjectAttributes(UNICODE_STRING objectName)
  113. {
  114. OBJECT_ATTRIBUTES objectAttributes = new OBJECT_ATTRIBUTES();
  115. objectAttributes.RootDirectory = IntPtr.Zero;
  116. objectAttributes.ObjectName = Marshal.AllocHGlobal(Marshal.SizeOf(objectName));
  117. Marshal.StructureToPtr(objectName, objectAttributes.ObjectName, false);
  118. objectAttributes.SecurityDescriptor = IntPtr.Zero;
  119. objectAttributes.SecurityQualityOfService = IntPtr.Zero;
  120. objectAttributes.Length = Marshal.SizeOf(objectAttributes);
  121. return objectAttributes;
  122. }
  123. private NTStatus CreateFile(out IntPtr handle, out FileStatus fileStatus, string nativePath, AccessMask desiredAccess, long allocationSize, FileAttributes fileAttributes, ShareAccess shareAccess, CreateDisposition createDisposition, CreateOptions createOptions)
  124. {
  125. UNICODE_STRING objectName = new UNICODE_STRING(nativePath);
  126. OBJECT_ATTRIBUTES objectAttributes = InitializeObjectAttributes(objectName);
  127. IO_STATUS_BLOCK ioStatusBlock;
  128. NTStatus status = NtCreateFile(out handle, (uint)desiredAccess, ref objectAttributes, out ioStatusBlock, ref allocationSize, fileAttributes, shareAccess, createDisposition, createOptions, IntPtr.Zero, 0);
  129. fileStatus = (FileStatus)ioStatusBlock.Information;
  130. return status;
  131. }
  132. private string ToNativePath(string path)
  133. {
  134. if (!path.StartsWith(@"\"))
  135. {
  136. path = @"\" + path;
  137. }
  138. return @"\??\" + m_directory.FullName + path;
  139. }
  140. public NTStatus CreateFile(out object handle, out FileStatus fileStatus, string path, AccessMask desiredAccess, FileAttributes fileAttributes, ShareAccess shareAccess, CreateDisposition createDisposition, CreateOptions createOptions, SecurityContext securityContext)
  141. {
  142. IntPtr fileHandle;
  143. string nativePath = ToNativePath(path);
  144. // NtQueryDirectoryFile will return STATUS_PENDING if the directory handle was not opened with SYNCHRONIZE and FILE_SYNCHRONOUS_IO_ALERT or FILE_SYNCHRONOUS_IO_NONALERT.
  145. // Our usage of NtNotifyChangeDirectoryFile assumes the directory handle is opened with SYNCHRONIZE and FILE_SYNCHRONOUS_IO_ALERT (or FILE_SYNCHRONOUS_IO_NONALERT starting from Windows Vista).
  146. // Note: Sometimes a directory will be opened without specifying FILE_DIRECTORY_FILE.
  147. desiredAccess |= AccessMask.SYNCHRONIZE;
  148. createOptions &= ~CreateOptions.FILE_SYNCHRONOUS_IO_NONALERT;
  149. createOptions |= CreateOptions.FILE_SYNCHRONOUS_IO_ALERT;
  150. if ((createOptions & CreateOptions.FILE_NO_INTERMEDIATE_BUFFERING) > 0 &&
  151. ((FileAccessMask)desiredAccess & FileAccessMask.FILE_APPEND_DATA) > 0)
  152. {
  153. // FILE_NO_INTERMEDIATE_BUFFERING is incompatible with FILE_APPEND_DATA
  154. // [MS-SMB2] 3.3.5.9 suggests setting FILE_APPEND_DATA to zero in this case.
  155. desiredAccess = (AccessMask)((uint)desiredAccess & (uint)~FileAccessMask.FILE_APPEND_DATA);
  156. }
  157. NTStatus status = CreateFile(out fileHandle, out fileStatus, nativePath, desiredAccess, 0, fileAttributes, shareAccess, createDisposition, createOptions);
  158. handle = fileHandle;
  159. return status;
  160. }
  161. public NTStatus CloseFile(object handle)
  162. {
  163. // [MS-FSA] 2.1.5.4 The close operation has to complete any pending ChangeNotify request with STATUS_NOTIFY_CLEANUP.
  164. // - When closing a synchronous handle we must explicitly cancel any pending ChangeNotify request, otherwise the call to NtClose will hang.
  165. // We use request.Cleanup to tell that we should complete such ChangeNotify request with STATUS_NOTIFY_CLEANUP.
  166. // - When closing an asynchronous handle Windows will implicitly complete any pending ChangeNotify request with STATUS_NOTIFY_CLEANUP as required.
  167. List<PendingRequest> pendingRequests = m_pendingRequests.GetRequestsByHandle((IntPtr)handle);
  168. foreach (PendingRequest request in pendingRequests)
  169. {
  170. request.Cleanup = true;
  171. Cancel(request);
  172. }
  173. return NtClose((IntPtr)handle);
  174. }
  175. public NTStatus ReadFile(out byte[] data, object handle, long offset, int maxCount)
  176. {
  177. IO_STATUS_BLOCK ioStatusBlock;
  178. data = new byte[maxCount];
  179. NTStatus status = NtReadFile((IntPtr)handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out ioStatusBlock, data, (uint)maxCount, ref offset, IntPtr.Zero);
  180. if (status == NTStatus.STATUS_SUCCESS)
  181. {
  182. int bytesRead = (int)ioStatusBlock.Information;
  183. if (bytesRead < maxCount)
  184. {
  185. data = ByteReader.ReadBytes(data, 0, bytesRead);
  186. }
  187. }
  188. return status;
  189. }
  190. public NTStatus WriteFile(out int numberOfBytesWritten, object handle, long offset, byte[] data)
  191. {
  192. IO_STATUS_BLOCK ioStatusBlock;
  193. NTStatus status = NtWriteFile((IntPtr)handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out ioStatusBlock, data, (uint)data.Length, ref offset, IntPtr.Zero);
  194. if (status == NTStatus.STATUS_SUCCESS)
  195. {
  196. numberOfBytesWritten = (int)ioStatusBlock.Information;
  197. }
  198. else
  199. {
  200. numberOfBytesWritten = 0;
  201. }
  202. return status;
  203. }
  204. public NTStatus FlushFileBuffers(object handle)
  205. {
  206. IO_STATUS_BLOCK ioStatusBlock;
  207. return NtFlushBuffersFile((IntPtr)handle, out ioStatusBlock);
  208. }
  209. public NTStatus LockFile(object handle, long byteOffset, long length, bool exclusiveLock)
  210. {
  211. IO_STATUS_BLOCK ioStatusBlock;
  212. return NtLockFile((IntPtr)handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out ioStatusBlock, ref byteOffset, ref length, 0, true, exclusiveLock);
  213. }
  214. public NTStatus UnlockFile(object handle, long byteOffset, long length)
  215. {
  216. IO_STATUS_BLOCK ioStatusBlock;
  217. return NtUnlockFile((IntPtr)handle, out ioStatusBlock, ref byteOffset, ref length, 0);
  218. }
  219. public NTStatus QueryDirectory(out List<QueryDirectoryFileInformation> result, object handle, string fileName, FileInformationClass informationClass)
  220. {
  221. IO_STATUS_BLOCK ioStatusBlock;
  222. byte[] buffer = new byte[QueryDirectoryBufferSize];
  223. UNICODE_STRING fileNameStructure = new UNICODE_STRING(fileName);
  224. result = new List<QueryDirectoryFileInformation>();
  225. bool restartScan = true;
  226. while (true)
  227. {
  228. NTStatus status = NtQueryDirectoryFile((IntPtr)handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out ioStatusBlock, buffer, (uint)buffer.Length, (byte)informationClass, false, ref fileNameStructure, restartScan);
  229. if (status == NTStatus.STATUS_NO_MORE_FILES)
  230. {
  231. break;
  232. }
  233. else if (status != NTStatus.STATUS_SUCCESS)
  234. {
  235. return status;
  236. }
  237. int numberOfBytesWritten = (int)ioStatusBlock.Information;
  238. List<QueryDirectoryFileInformation> page = QueryDirectoryFileInformation.ReadFileInformationList(buffer, 0, informationClass);
  239. result.AddRange(page);
  240. restartScan = false;
  241. }
  242. fileNameStructure.Dispose();
  243. return NTStatus.STATUS_SUCCESS;
  244. }
  245. public NTStatus GetFileInformation(out FileInformation result, object handle, FileInformationClass informationClass)
  246. {
  247. IO_STATUS_BLOCK ioStatusBlock;
  248. byte[] buffer = new byte[FileInformationBufferSize];
  249. NTStatus status = NtQueryInformationFile((IntPtr)handle, out ioStatusBlock, buffer, (uint)buffer.Length, (uint)informationClass);
  250. if (status == NTStatus.STATUS_SUCCESS)
  251. {
  252. int numberOfBytesWritten = (int)ioStatusBlock.Information;
  253. buffer = ByteReader.ReadBytes(buffer, 0, numberOfBytesWritten);
  254. result = FileInformation.GetFileInformation(buffer, 0, informationClass);
  255. }
  256. else
  257. {
  258. result = null;
  259. }
  260. return status;
  261. }
  262. public NTStatus SetFileInformation(object handle, FileInformation information)
  263. {
  264. IO_STATUS_BLOCK ioStatusBlock;
  265. if (information is FileRenameInformationType2)
  266. {
  267. FileRenameInformationType2 fileRenameInformationRemote = (FileRenameInformationType2)information;
  268. if (ProcessHelper.Is64BitProcess)
  269. {
  270. // We should not modify the FileRenameInformationType2 instance we received - the caller may use it later.
  271. FileRenameInformationType2 fileRenameInformationLocal = new FileRenameInformationType2();
  272. fileRenameInformationLocal.ReplaceIfExists = fileRenameInformationRemote.ReplaceIfExists;
  273. fileRenameInformationLocal.FileName = ToNativePath(fileRenameInformationRemote.FileName);
  274. information = fileRenameInformationLocal;
  275. }
  276. else
  277. {
  278. // Note: WOW64 process should use FILE_RENAME_INFORMATION_TYPE_1.
  279. // Note: Server 2003 x64 has issues with using FILE_RENAME_INFORMATION under WOW64.
  280. FileRenameInformationType1 fileRenameInformationLocal = new FileRenameInformationType1();
  281. fileRenameInformationLocal.ReplaceIfExists = fileRenameInformationRemote.ReplaceIfExists;
  282. fileRenameInformationLocal.FileName = ToNativePath(fileRenameInformationRemote.FileName);
  283. information = fileRenameInformationLocal;
  284. }
  285. }
  286. else if (information is FileLinkInformationType2)
  287. {
  288. FileLinkInformationType2 fileLinkInformationRemote = (FileLinkInformationType2)information;
  289. if (ProcessHelper.Is64BitProcess)
  290. {
  291. FileRenameInformationType2 fileLinkInformationLocal = new FileRenameInformationType2();
  292. fileLinkInformationLocal.ReplaceIfExists = fileLinkInformationRemote.ReplaceIfExists;
  293. fileLinkInformationLocal.FileName = ToNativePath(fileLinkInformationRemote.FileName);
  294. information = fileLinkInformationRemote;
  295. }
  296. else
  297. {
  298. FileLinkInformationType1 fileLinkInformationLocal = new FileLinkInformationType1();
  299. fileLinkInformationLocal.ReplaceIfExists = fileLinkInformationRemote.ReplaceIfExists;
  300. fileLinkInformationLocal.FileName = ToNativePath(fileLinkInformationRemote.FileName);
  301. information = fileLinkInformationRemote;
  302. }
  303. }
  304. byte[] buffer = information.GetBytes();
  305. return NtSetInformationFile((IntPtr)handle, out ioStatusBlock, buffer, (uint)buffer.Length, (uint)information.FileInformationClass);
  306. }
  307. public NTStatus GetFileSystemInformation(out FileSystemInformation result, FileSystemInformationClass informationClass)
  308. {
  309. IO_STATUS_BLOCK ioStatusBlock;
  310. byte[] buffer = new byte[FileSystemInformationBufferSize];
  311. IntPtr volumeHandle;
  312. FileStatus fileStatus;
  313. string nativePath = @"\??\" + m_directory.FullName.Substring(0, 3);
  314. NTStatus status = CreateFile(out volumeHandle, out fileStatus, nativePath, AccessMask.GENERIC_READ, 0, (FileAttributes)0, ShareAccess.Read, CreateDisposition.FILE_OPEN, (CreateOptions)0);
  315. result = null;
  316. if (status != NTStatus.STATUS_SUCCESS)
  317. {
  318. return status;
  319. }
  320. status = NtQueryVolumeInformationFile((IntPtr)volumeHandle, out ioStatusBlock, buffer, (uint)buffer.Length, (uint)informationClass);
  321. CloseFile(volumeHandle);
  322. if (status == NTStatus.STATUS_SUCCESS)
  323. {
  324. int numberOfBytesWritten = (int)ioStatusBlock.Information;
  325. buffer = ByteReader.ReadBytes(buffer, 0, numberOfBytesWritten);
  326. result = FileSystemInformation.GetFileSystemInformation(buffer, 0, informationClass);
  327. }
  328. return status;
  329. }
  330. public NTStatus SetFileSystemInformation(FileSystemInformation information)
  331. {
  332. return NTStatus.STATUS_NOT_SUPPORTED;
  333. }
  334. public NTStatus GetSecurityInformation(out SecurityDescriptor result, object handle, SecurityInformation securityInformation)
  335. {
  336. result = null;
  337. return NTStatus.STATUS_INVALID_DEVICE_REQUEST;
  338. }
  339. public NTStatus SetSecurityInformation(object handle, SecurityInformation securityInformation, SecurityDescriptor securityDescriptor)
  340. {
  341. // [MS-FSA] If the object store does not implement security, the operation MUST be failed with STATUS_INVALID_DEVICE_REQUEST.
  342. return NTStatus.STATUS_INVALID_DEVICE_REQUEST;
  343. }
  344. public NTStatus NotifyChange(out object ioRequest, object handle, NotifyChangeFilter completionFilter, bool watchTree, int outputBufferSize, OnNotifyChangeCompleted onNotifyChangeCompleted, object context)
  345. {
  346. byte[] buffer = new byte[outputBufferSize];
  347. ManualResetEvent requestAddedEvent = new ManualResetEvent(false);
  348. PendingRequest request = new PendingRequest();
  349. Thread m_thread = new Thread(delegate()
  350. {
  351. request.FileHandle = (IntPtr)handle;
  352. request.ThreadID = ThreadingHelper.GetCurrentThreadId();
  353. m_pendingRequests.Add(request);
  354. // The request has been added, we can now return STATUS_PENDING.
  355. requestAddedEvent.Set();
  356. // There is a possibility of race condition if the caller will wait for STATUS_PENDING and then immediate call Cancel, but this scenario is very unlikely.
  357. NTStatus status = NtNotifyChangeDirectoryFile((IntPtr)handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out request.IOStatusBlock, buffer, (uint)buffer.Length, completionFilter, watchTree);
  358. if (status == NTStatus.STATUS_SUCCESS)
  359. {
  360. int length = (int)request.IOStatusBlock.Information;
  361. buffer = ByteReader.ReadBytes(buffer, 0, length);
  362. }
  363. else
  364. {
  365. const NTStatus STATUS_ALERTED = (NTStatus)0x00000101;
  366. const NTStatus STATUS_OBJECT_TYPE_MISMATCH = (NTStatus)0xC0000024;
  367. buffer = new byte[0];
  368. if (status == STATUS_OBJECT_TYPE_MISMATCH)
  369. {
  370. status = NTStatus.STATUS_INVALID_HANDLE;
  371. }
  372. else if (status == STATUS_ALERTED)
  373. {
  374. status = NTStatus.STATUS_CANCELLED;
  375. }
  376. // If the handle is closing and we had to cancel a ChangeNotify request as part of a cleanup,
  377. // we return STATUS_NOTIFY_CLEANUP as specified in [MS-FSA] 2.1.5.4.
  378. if (status == NTStatus.STATUS_CANCELLED && request.Cleanup)
  379. {
  380. status = NTStatus.STATUS_NOTIFY_CLEANUP;
  381. }
  382. }
  383. onNotifyChangeCompleted(status, buffer, context);
  384. m_pendingRequests.Remove((IntPtr)handle, request.ThreadID);
  385. });
  386. m_thread.Start();
  387. // We must wait for the request to be added in order for Cancel to function properly.
  388. requestAddedEvent.WaitOne();
  389. ioRequest = request;
  390. return NTStatus.STATUS_PENDING;
  391. }
  392. public NTStatus Cancel(object ioRequest)
  393. {
  394. PendingRequest request = (PendingRequest)ioRequest;
  395. const uint THREAD_TERMINATE = 0x00000001;
  396. const uint THREAD_ALERT = 0x00000004;
  397. uint threadID = request.ThreadID;
  398. IntPtr threadHandle = ThreadingHelper.OpenThread(THREAD_TERMINATE | THREAD_ALERT, false, threadID);
  399. if (threadHandle == IntPtr.Zero)
  400. {
  401. Win32Error error = (Win32Error)Marshal.GetLastWin32Error();
  402. if (error == Win32Error.ERROR_INVALID_PARAMETER)
  403. {
  404. return NTStatus.STATUS_INVALID_HANDLE;
  405. }
  406. else
  407. {
  408. throw new Exception("OpenThread failed, Win32 error: " + error.ToString("D"));
  409. }
  410. }
  411. NTStatus status;
  412. if (Environment.OSVersion.Version.Major >= 6)
  413. {
  414. IO_STATUS_BLOCK ioStatusBlock;
  415. status = NtCancelSynchronousIoFile(threadHandle, IntPtr.Zero, out ioStatusBlock);
  416. }
  417. else
  418. {
  419. // The handle was opened for synchronous operation so NtNotifyChangeDirectoryFile is blocking.
  420. // We MUST use NtAlertThread to send a signal to stop the wait. The handle cannot be closed otherwise.
  421. // Note: The handle was opened with CreateOptions.FILE_SYNCHRONOUS_IO_ALERT as required.
  422. status = NtAlertThread(threadHandle);
  423. }
  424. ThreadingHelper.CloseHandle(threadHandle);
  425. m_pendingRequests.Remove(request.FileHandle, request.ThreadID);
  426. return status;
  427. }
  428. public NTStatus DeviceIOControl(object handle, uint ctlCode, byte[] input, out byte[] output, int maxOutputLength)
  429. {
  430. switch ((IoControlCode)ctlCode)
  431. {
  432. case IoControlCode.FSCTL_IS_PATHNAME_VALID:
  433. case IoControlCode.FSCTL_GET_COMPRESSION:
  434. case IoControlCode.FSCTL_GET_RETRIEVAL_POINTERS:
  435. case IoControlCode.FSCTL_SET_OBJECT_ID:
  436. case IoControlCode.FSCTL_GET_OBJECT_ID:
  437. case IoControlCode.FSCTL_DELETE_OBJECT_ID:
  438. case IoControlCode.FSCTL_SET_OBJECT_ID_EXTENDED:
  439. case IoControlCode.FSCTL_CREATE_OR_GET_OBJECT_ID:
  440. case IoControlCode.FSCTL_SET_SPARSE:
  441. case IoControlCode.FSCTL_READ_FILE_USN_DATA:
  442. case IoControlCode.FSCTL_SET_DEFECT_MANAGEMENT:
  443. case IoControlCode.FSCTL_SET_COMPRESSION:
  444. case IoControlCode.FSCTL_QUERY_SPARING_INFO:
  445. case IoControlCode.FSCTL_QUERY_ON_DISK_VOLUME_INFO:
  446. case IoControlCode.FSCTL_SET_ZERO_ON_DEALLOCATION:
  447. case IoControlCode.FSCTL_QUERY_FILE_REGIONS:
  448. case IoControlCode.FSCTL_QUERY_ALLOCATED_RANGES:
  449. case IoControlCode.FSCTL_SET_ZERO_DATA:
  450. {
  451. IO_STATUS_BLOCK ioStatusBlock;
  452. output = new byte[maxOutputLength];
  453. NTStatus status = NtFsControlFile((IntPtr)handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out ioStatusBlock, ctlCode, input, (uint)input.Length, output, (uint)maxOutputLength);
  454. if (status == NTStatus.STATUS_SUCCESS)
  455. {
  456. int numberOfBytesWritten = (int)ioStatusBlock.Information;
  457. output = ByteReader.ReadBytes(output, 0, numberOfBytesWritten);
  458. }
  459. return status;
  460. }
  461. default:
  462. {
  463. output = null;
  464. return NTStatus.STATUS_NOT_SUPPORTED;
  465. }
  466. }
  467. }
  468. }
  469. }