123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 |
- /* Copyright (C) 2017-2020 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
- *
- * You can redistribute this program and/or modify it under the terms of
- * the GNU Lesser Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- */
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Runtime.InteropServices;
- using System.Threading;
- using Microsoft.Win32.SafeHandles;
- using Utilities;
- namespace SMBLibrary.Win32
- {
- [StructLayout(LayoutKind.Sequential)]
- public struct UNICODE_STRING : IDisposable
- {
- public ushort Length;
- public ushort MaximumLength;
- private IntPtr Buffer;
- public UNICODE_STRING(string value)
- {
- Length = (ushort)(value.Length * 2);
- MaximumLength = (ushort)(value.Length + 2);
- Buffer = Marshal.StringToHGlobalUni(value);
- }
- public void Dispose()
- {
- Marshal.FreeHGlobal(Buffer);
- Buffer = IntPtr.Zero;
- }
- public override string ToString()
- {
- return Marshal.PtrToStringUni(Buffer);
- }
- }
- [StructLayoutAttribute(LayoutKind.Sequential)]
- public struct OBJECT_ATTRIBUTES
- {
- public int Length;
- public IntPtr RootDirectory;
- public IntPtr ObjectName;
- public uint Attributes;
- public IntPtr SecurityDescriptor;
- public IntPtr SecurityQualityOfService;
- }
- [StructLayoutAttribute(LayoutKind.Sequential)]
- public struct IO_STATUS_BLOCK
- {
- public UInt32 Status;
- public IntPtr Information;
- }
- internal class PendingRequest
- {
- public IntPtr FileHandle;
- public uint ThreadID;
- public IO_STATUS_BLOCK IOStatusBlock;
- public bool Cleanup;
- }
- public class NTDirectoryFileSystem : INTFileStore
- {
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- 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);
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- private static extern NTStatus NtClose(IntPtr handle);
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- 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);
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- 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);
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- private static extern NTStatus NtFlushBuffersFile(IntPtr handle, out IO_STATUS_BLOCK ioStatusBlock);
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- 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);
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- private static extern NTStatus NtUnlockFile(IntPtr handle, out IO_STATUS_BLOCK ioStatusBlock, ref long byteOffset, ref long length, uint key);
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- 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);
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- private static extern NTStatus NtQueryInformationFile(IntPtr handle, out IO_STATUS_BLOCK ioStatusBlock, byte[] fileInformation, uint length, uint fileInformationClass);
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- private static extern NTStatus NtSetInformationFile(IntPtr handle, out IO_STATUS_BLOCK ioStatusBlock, byte[] fileInformation, uint length, uint fileInformationClass);
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- private static extern NTStatus NtQueryVolumeInformationFile(IntPtr handle, out IO_STATUS_BLOCK ioStatusBlock, byte[] fsInformation, uint length, uint fsInformationClass);
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- private static extern NTStatus NtSetVolumeInformationFile(IntPtr handle, out IO_STATUS_BLOCK ioStatusBlock, byte[] fsInformation, uint length, uint fsInformationClass);
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- private static extern NTStatus NtQuerySecurityObject(IntPtr handle, SecurityInformation securityInformation, byte[] securityDescriptor, uint length, out uint lengthNeeded);
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- private static extern NTStatus NtSetSecurityObject(IntPtr handle, SecurityInformation securityInformation, byte[] securityDescriptor);
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- 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);
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- 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);
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- private static extern NTStatus NtAlertThread(IntPtr threadHandle);
- // Available starting from Windows Vista.
- [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
- private static extern NTStatus NtCancelSynchronousIoFile(IntPtr threadHandle, IntPtr ioRequestToCancel, out IO_STATUS_BLOCK ioStatusBlock);
- private static readonly int QueryDirectoryBufferSize = 4096;
- private static readonly int FileInformationBufferSize = 8192;
- private static readonly int FileSystemInformationBufferSize = 4096;
- private DirectoryInfo m_directory;
- private PendingRequestCollection m_pendingRequests = new PendingRequestCollection();
- public NTDirectoryFileSystem(string path) : this(new DirectoryInfo(path))
- {
- }
- public NTDirectoryFileSystem(DirectoryInfo directory)
- {
- m_directory = directory;
- }
- private OBJECT_ATTRIBUTES InitializeObjectAttributes(UNICODE_STRING objectName)
- {
- OBJECT_ATTRIBUTES objectAttributes = new OBJECT_ATTRIBUTES();
- objectAttributes.RootDirectory = IntPtr.Zero;
- objectAttributes.ObjectName = Marshal.AllocHGlobal(Marshal.SizeOf(objectName));
- Marshal.StructureToPtr(objectName, objectAttributes.ObjectName, false);
- objectAttributes.SecurityDescriptor = IntPtr.Zero;
- objectAttributes.SecurityQualityOfService = IntPtr.Zero;
- objectAttributes.Length = Marshal.SizeOf(objectAttributes);
- return objectAttributes;
- }
- private NTStatus CreateFile(out IntPtr handle, out FileStatus fileStatus, string nativePath, AccessMask desiredAccess, long allocationSize, FileAttributes fileAttributes, ShareAccess shareAccess, CreateDisposition createDisposition, CreateOptions createOptions)
- {
- UNICODE_STRING objectName = new UNICODE_STRING(nativePath);
- OBJECT_ATTRIBUTES objectAttributes = InitializeObjectAttributes(objectName);
- IO_STATUS_BLOCK ioStatusBlock;
- NTStatus status = NtCreateFile(out handle, (uint)desiredAccess, ref objectAttributes, out ioStatusBlock, ref allocationSize, fileAttributes, shareAccess, createDisposition, createOptions, IntPtr.Zero, 0);
- fileStatus = (FileStatus)ioStatusBlock.Information;
- return status;
- }
- private string ToNativePath(string path)
- {
- if (!path.StartsWith(@"\"))
- {
- path = @"\" + path;
- }
- return @"\??\" + m_directory.FullName + path;
- }
- public NTStatus CreateFile(out object handle, out FileStatus fileStatus, string path, AccessMask desiredAccess, FileAttributes fileAttributes, ShareAccess shareAccess, CreateDisposition createDisposition, CreateOptions createOptions, SecurityContext securityContext)
- {
- IntPtr fileHandle;
- string nativePath = ToNativePath(path);
- // NtQueryDirectoryFile will return STATUS_PENDING if the directory handle was not opened with SYNCHRONIZE and FILE_SYNCHRONOUS_IO_ALERT or FILE_SYNCHRONOUS_IO_NONALERT.
- // 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).
- // Note: Sometimes a directory will be opened without specifying FILE_DIRECTORY_FILE.
- desiredAccess |= AccessMask.SYNCHRONIZE;
- createOptions &= ~CreateOptions.FILE_SYNCHRONOUS_IO_NONALERT;
- createOptions |= CreateOptions.FILE_SYNCHRONOUS_IO_ALERT;
- if ((createOptions & CreateOptions.FILE_NO_INTERMEDIATE_BUFFERING) > 0 &&
- ((FileAccessMask)desiredAccess & FileAccessMask.FILE_APPEND_DATA) > 0)
- {
- // FILE_NO_INTERMEDIATE_BUFFERING is incompatible with FILE_APPEND_DATA
- // [MS-SMB2] 3.3.5.9 suggests setting FILE_APPEND_DATA to zero in this case.
- desiredAccess = (AccessMask)((uint)desiredAccess & (uint)~FileAccessMask.FILE_APPEND_DATA);
- }
- NTStatus status = CreateFile(out fileHandle, out fileStatus, nativePath, desiredAccess, 0, fileAttributes, shareAccess, createDisposition, createOptions);
- handle = fileHandle;
- return status;
- }
- public NTStatus CloseFile(object handle)
- {
- // [MS-FSA] 2.1.5.4 The close operation has to complete any pending ChangeNotify request with STATUS_NOTIFY_CLEANUP.
- // - When closing a synchronous handle we must explicitly cancel any pending ChangeNotify request, otherwise the call to NtClose will hang.
- // We use request.Cleanup to tell that we should complete such ChangeNotify request with STATUS_NOTIFY_CLEANUP.
- // - When closing an asynchronous handle Windows will implicitly complete any pending ChangeNotify request with STATUS_NOTIFY_CLEANUP as required.
- List<PendingRequest> pendingRequests = m_pendingRequests.GetRequestsByHandle((IntPtr)handle);
- foreach (PendingRequest request in pendingRequests)
- {
- request.Cleanup = true;
- Cancel(request);
- }
- return NtClose((IntPtr)handle);
- }
- public NTStatus ReadFile(out byte[] data, object handle, long offset, int maxCount)
- {
- IO_STATUS_BLOCK ioStatusBlock;
- data = new byte[maxCount];
- NTStatus status = NtReadFile((IntPtr)handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out ioStatusBlock, data, (uint)maxCount, ref offset, IntPtr.Zero);
- if (status == NTStatus.STATUS_SUCCESS)
- {
- int bytesRead = (int)ioStatusBlock.Information;
- if (bytesRead < maxCount)
- {
- data = ByteReader.ReadBytes(data, 0, bytesRead);
- }
- }
- return status;
- }
- public NTStatus WriteFile(out int numberOfBytesWritten, object handle, long offset, byte[] data)
- {
- IO_STATUS_BLOCK ioStatusBlock;
- NTStatus status = NtWriteFile((IntPtr)handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out ioStatusBlock, data, (uint)data.Length, ref offset, IntPtr.Zero);
- if (status == NTStatus.STATUS_SUCCESS)
- {
- numberOfBytesWritten = (int)ioStatusBlock.Information;
- }
- else
- {
- numberOfBytesWritten = 0;
- }
- return status;
- }
- public NTStatus FlushFileBuffers(object handle)
- {
- IO_STATUS_BLOCK ioStatusBlock;
- return NtFlushBuffersFile((IntPtr)handle, out ioStatusBlock);
- }
- public NTStatus LockFile(object handle, long byteOffset, long length, bool exclusiveLock)
- {
- IO_STATUS_BLOCK ioStatusBlock;
- return NtLockFile((IntPtr)handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out ioStatusBlock, ref byteOffset, ref length, 0, true, exclusiveLock);
- }
- public NTStatus UnlockFile(object handle, long byteOffset, long length)
- {
- IO_STATUS_BLOCK ioStatusBlock;
- return NtUnlockFile((IntPtr)handle, out ioStatusBlock, ref byteOffset, ref length, 0);
- }
- public NTStatus QueryDirectory(out List<QueryDirectoryFileInformation> result, object handle, string fileName, FileInformationClass informationClass)
- {
- IO_STATUS_BLOCK ioStatusBlock;
- byte[] buffer = new byte[QueryDirectoryBufferSize];
- UNICODE_STRING fileNameStructure = new UNICODE_STRING(fileName);
- result = new List<QueryDirectoryFileInformation>();
- bool restartScan = true;
- while (true)
- {
- NTStatus status = NtQueryDirectoryFile((IntPtr)handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out ioStatusBlock, buffer, (uint)buffer.Length, (byte)informationClass, false, ref fileNameStructure, restartScan);
- if (status == NTStatus.STATUS_NO_MORE_FILES)
- {
- break;
- }
- else if (status != NTStatus.STATUS_SUCCESS)
- {
- return status;
- }
- int numberOfBytesWritten = (int)ioStatusBlock.Information;
- List<QueryDirectoryFileInformation> page = QueryDirectoryFileInformation.ReadFileInformationList(buffer, 0, informationClass);
- result.AddRange(page);
- restartScan = false;
- }
- fileNameStructure.Dispose();
- return NTStatus.STATUS_SUCCESS;
- }
- public NTStatus GetFileInformation(out FileInformation result, object handle, FileInformationClass informationClass)
- {
- IO_STATUS_BLOCK ioStatusBlock;
- byte[] buffer = new byte[FileInformationBufferSize];
- NTStatus status = NtQueryInformationFile((IntPtr)handle, out ioStatusBlock, buffer, (uint)buffer.Length, (uint)informationClass);
- if (status == NTStatus.STATUS_SUCCESS)
- {
- int numberOfBytesWritten = (int)ioStatusBlock.Information;
- buffer = ByteReader.ReadBytes(buffer, 0, numberOfBytesWritten);
- result = FileInformation.GetFileInformation(buffer, 0, informationClass);
- }
- else
- {
- result = null;
- }
- return status;
- }
- public NTStatus SetFileInformation(object handle, FileInformation information)
- {
- IO_STATUS_BLOCK ioStatusBlock;
- if (information is FileRenameInformationType2)
- {
- FileRenameInformationType2 fileRenameInformationRemote = (FileRenameInformationType2)information;
- if (ProcessHelper.Is64BitProcess)
- {
- // We should not modify the FileRenameInformationType2 instance we received - the caller may use it later.
- FileRenameInformationType2 fileRenameInformationLocal = new FileRenameInformationType2();
- fileRenameInformationLocal.ReplaceIfExists = fileRenameInformationRemote.ReplaceIfExists;
- fileRenameInformationLocal.FileName = ToNativePath(fileRenameInformationRemote.FileName);
- information = fileRenameInformationLocal;
- }
- else
- {
- // Note: WOW64 process should use FILE_RENAME_INFORMATION_TYPE_1.
- // Note: Server 2003 x64 has issues with using FILE_RENAME_INFORMATION under WOW64.
- FileRenameInformationType1 fileRenameInformationLocal = new FileRenameInformationType1();
- fileRenameInformationLocal.ReplaceIfExists = fileRenameInformationRemote.ReplaceIfExists;
- fileRenameInformationLocal.FileName = ToNativePath(fileRenameInformationRemote.FileName);
- information = fileRenameInformationLocal;
- }
- }
- else if (information is FileLinkInformationType2)
- {
- FileLinkInformationType2 fileLinkInformationRemote = (FileLinkInformationType2)information;
- if (ProcessHelper.Is64BitProcess)
- {
- FileRenameInformationType2 fileLinkInformationLocal = new FileRenameInformationType2();
- fileLinkInformationLocal.ReplaceIfExists = fileLinkInformationRemote.ReplaceIfExists;
- fileLinkInformationLocal.FileName = ToNativePath(fileLinkInformationRemote.FileName);
- information = fileLinkInformationRemote;
- }
- else
- {
- FileLinkInformationType1 fileLinkInformationLocal = new FileLinkInformationType1();
- fileLinkInformationLocal.ReplaceIfExists = fileLinkInformationRemote.ReplaceIfExists;
- fileLinkInformationLocal.FileName = ToNativePath(fileLinkInformationRemote.FileName);
- information = fileLinkInformationRemote;
- }
- }
- byte[] buffer = information.GetBytes();
- return NtSetInformationFile((IntPtr)handle, out ioStatusBlock, buffer, (uint)buffer.Length, (uint)information.FileInformationClass);
- }
- public NTStatus GetFileSystemInformation(out FileSystemInformation result, FileSystemInformationClass informationClass)
- {
- IO_STATUS_BLOCK ioStatusBlock;
- byte[] buffer = new byte[FileSystemInformationBufferSize];
- IntPtr volumeHandle;
- FileStatus fileStatus;
- string nativePath = @"\??\" + m_directory.FullName.Substring(0, 3);
- NTStatus status = CreateFile(out volumeHandle, out fileStatus, nativePath, AccessMask.GENERIC_READ, 0, (FileAttributes)0, ShareAccess.Read, CreateDisposition.FILE_OPEN, (CreateOptions)0);
- result = null;
- if (status != NTStatus.STATUS_SUCCESS)
- {
- return status;
- }
- status = NtQueryVolumeInformationFile((IntPtr)volumeHandle, out ioStatusBlock, buffer, (uint)buffer.Length, (uint)informationClass);
- CloseFile(volumeHandle);
- if (status == NTStatus.STATUS_SUCCESS)
- {
- int numberOfBytesWritten = (int)ioStatusBlock.Information;
- buffer = ByteReader.ReadBytes(buffer, 0, numberOfBytesWritten);
- result = FileSystemInformation.GetFileSystemInformation(buffer, 0, informationClass);
- }
- return status;
- }
- public NTStatus SetFileSystemInformation(FileSystemInformation information)
- {
- return NTStatus.STATUS_NOT_SUPPORTED;
- }
- public NTStatus GetSecurityInformation(out SecurityDescriptor result, object handle, SecurityInformation securityInformation)
- {
- result = null;
- return NTStatus.STATUS_INVALID_DEVICE_REQUEST;
- }
- public NTStatus SetSecurityInformation(object handle, SecurityInformation securityInformation, SecurityDescriptor securityDescriptor)
- {
- // [MS-FSA] If the object store does not implement security, the operation MUST be failed with STATUS_INVALID_DEVICE_REQUEST.
- return NTStatus.STATUS_INVALID_DEVICE_REQUEST;
- }
- public NTStatus NotifyChange(out object ioRequest, object handle, NotifyChangeFilter completionFilter, bool watchTree, int outputBufferSize, OnNotifyChangeCompleted onNotifyChangeCompleted, object context)
- {
- byte[] buffer = new byte[outputBufferSize];
- ManualResetEvent requestAddedEvent = new ManualResetEvent(false);
- PendingRequest request = new PendingRequest();
- Thread m_thread = new Thread(delegate()
- {
- request.FileHandle = (IntPtr)handle;
- request.ThreadID = ThreadingHelper.GetCurrentThreadId();
- m_pendingRequests.Add(request);
- // The request has been added, we can now return STATUS_PENDING.
- requestAddedEvent.Set();
- // 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.
- NTStatus status = NtNotifyChangeDirectoryFile((IntPtr)handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out request.IOStatusBlock, buffer, (uint)buffer.Length, completionFilter, watchTree);
- if (status == NTStatus.STATUS_SUCCESS)
- {
- int length = (int)request.IOStatusBlock.Information;
- buffer = ByteReader.ReadBytes(buffer, 0, length);
- }
- else
- {
- const NTStatus STATUS_ALERTED = (NTStatus)0x00000101;
- const NTStatus STATUS_OBJECT_TYPE_MISMATCH = (NTStatus)0xC0000024;
- buffer = new byte[0];
- if (status == STATUS_OBJECT_TYPE_MISMATCH)
- {
- status = NTStatus.STATUS_INVALID_HANDLE;
- }
- else if (status == STATUS_ALERTED)
- {
- status = NTStatus.STATUS_CANCELLED;
- }
- // If the handle is closing and we had to cancel a ChangeNotify request as part of a cleanup,
- // we return STATUS_NOTIFY_CLEANUP as specified in [MS-FSA] 2.1.5.4.
- if (status == NTStatus.STATUS_CANCELLED && request.Cleanup)
- {
- status = NTStatus.STATUS_NOTIFY_CLEANUP;
- }
- }
- onNotifyChangeCompleted(status, buffer, context);
- m_pendingRequests.Remove((IntPtr)handle, request.ThreadID);
- });
- m_thread.Start();
- // We must wait for the request to be added in order for Cancel to function properly.
- requestAddedEvent.WaitOne();
- ioRequest = request;
- return NTStatus.STATUS_PENDING;
- }
- public NTStatus Cancel(object ioRequest)
- {
- PendingRequest request = (PendingRequest)ioRequest;
- const uint THREAD_TERMINATE = 0x00000001;
- const uint THREAD_ALERT = 0x00000004;
- uint threadID = request.ThreadID;
- IntPtr threadHandle = ThreadingHelper.OpenThread(THREAD_TERMINATE | THREAD_ALERT, false, threadID);
- if (threadHandle == IntPtr.Zero)
- {
- Win32Error error = (Win32Error)Marshal.GetLastWin32Error();
- if (error == Win32Error.ERROR_INVALID_PARAMETER)
- {
- return NTStatus.STATUS_INVALID_HANDLE;
- }
- else
- {
- throw new Exception("OpenThread failed, Win32 error: " + error.ToString("D"));
- }
- }
- NTStatus status;
- if (Environment.OSVersion.Version.Major >= 6)
- {
- IO_STATUS_BLOCK ioStatusBlock;
- status = NtCancelSynchronousIoFile(threadHandle, IntPtr.Zero, out ioStatusBlock);
- }
- else
- {
- // The handle was opened for synchronous operation so NtNotifyChangeDirectoryFile is blocking.
- // We MUST use NtAlertThread to send a signal to stop the wait. The handle cannot be closed otherwise.
- // Note: The handle was opened with CreateOptions.FILE_SYNCHRONOUS_IO_ALERT as required.
- status = NtAlertThread(threadHandle);
- }
- ThreadingHelper.CloseHandle(threadHandle);
- m_pendingRequests.Remove(request.FileHandle, request.ThreadID);
- return status;
- }
- public NTStatus DeviceIOControl(object handle, uint ctlCode, byte[] input, out byte[] output, int maxOutputLength)
- {
- switch ((IoControlCode)ctlCode)
- {
- case IoControlCode.FSCTL_IS_PATHNAME_VALID:
- case IoControlCode.FSCTL_GET_COMPRESSION:
- case IoControlCode.FSCTL_GET_RETRIEVAL_POINTERS:
- case IoControlCode.FSCTL_SET_OBJECT_ID:
- case IoControlCode.FSCTL_GET_OBJECT_ID:
- case IoControlCode.FSCTL_DELETE_OBJECT_ID:
- case IoControlCode.FSCTL_SET_OBJECT_ID_EXTENDED:
- case IoControlCode.FSCTL_CREATE_OR_GET_OBJECT_ID:
- case IoControlCode.FSCTL_SET_SPARSE:
- case IoControlCode.FSCTL_READ_FILE_USN_DATA:
- case IoControlCode.FSCTL_SET_DEFECT_MANAGEMENT:
- case IoControlCode.FSCTL_SET_COMPRESSION:
- case IoControlCode.FSCTL_QUERY_SPARING_INFO:
- case IoControlCode.FSCTL_QUERY_ON_DISK_VOLUME_INFO:
- case IoControlCode.FSCTL_SET_ZERO_ON_DEALLOCATION:
- case IoControlCode.FSCTL_QUERY_FILE_REGIONS:
- case IoControlCode.FSCTL_QUERY_ALLOCATED_RANGES:
- case IoControlCode.FSCTL_SET_ZERO_DATA:
- {
- IO_STATUS_BLOCK ioStatusBlock;
- output = new byte[maxOutputLength];
- NTStatus status = NtFsControlFile((IntPtr)handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out ioStatusBlock, ctlCode, input, (uint)input.Length, output, (uint)maxOutputLength);
- if (status == NTStatus.STATUS_SUCCESS)
- {
- int numberOfBytesWritten = (int)ioStatusBlock.Information;
- output = ByteReader.ReadBytes(output, 0, numberOfBytesWritten);
- }
- return status;
- }
- default:
- {
- output = null;
- return NTStatus.STATUS_NOT_SUPPORTED;
- }
- }
- }
- }
- }
|