mirror of
https://github.com/TalAloni/SMBLibrary.git
synced 2025-08-02 22:11:50 +02:00
Added NTDirectoryFileSystem implementation for passsing-through IO requests to the local NT storage subsystem
This commit is contained in:
parent
5f90217d21
commit
9902b91eff
6 changed files with 593 additions and 0 deletions
|
@ -7,6 +7,7 @@ namespace SMBLibrary
|
|||
ERROR_SUCCESS = 0x0000,
|
||||
ERROR_ACCESS_DENIED = 0x0005,
|
||||
ERROR_SHARING_VIOLATION = 0x0020,
|
||||
ERROR_INVALID_PARAMETER = 0x0057,
|
||||
ERROR_DISK_FULL = 0x0070,
|
||||
ERROR_DIR_NOT_EMPTY = 0x0091,
|
||||
ERROR_ALREADY_EXISTS = 0x00B7,
|
||||
|
|
|
@ -551,12 +551,16 @@
|
|||
<Compile Include="Tests\SMB2SigningTests.cs" />
|
||||
<Compile Include="Utilities\LogEntry.cs" />
|
||||
<Compile Include="Utilities\SocketUtils.cs" />
|
||||
<Compile Include="Win32\NTFileStore\NTDirectoryFileSystem.cs" />
|
||||
<Compile Include="Win32\NTFileStore\PendingRequestCollection.cs" />
|
||||
<Compile Include="Win32\ProcessHelper.cs" />
|
||||
<Compile Include="Win32\Security\IntegratedNTLMAuthenticationProvider.cs" />
|
||||
<Compile Include="Win32\Security\LoginAPI.cs" />
|
||||
<Compile Include="Win32\Security\NetworkAPI.cs" />
|
||||
<Compile Include="Win32\Security\SSPIHelper.cs" />
|
||||
<Compile Include="Win32\Security\Structures\SecBuffer.cs" />
|
||||
<Compile Include="Win32\Security\Structures\SecBufferDesc.cs" />
|
||||
<Compile Include="Win32\ThreadingHelper.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Readme.txt" />
|
||||
|
|
416
SMBLibrary/Win32/NTFileStore/NTDirectoryFileSystem.cs
Normal file
416
SMBLibrary/Win32/NTFileStore/NTDirectoryFileSystem.cs
Normal file
|
@ -0,0 +1,416 @@
|
|||
/* Copyright (C) 2017 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 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 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 NtAlertThread(IntPtr threadHandle);
|
||||
|
||||
// Available starting from Windows Vista.
|
||||
[DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
|
||||
private static extern NTStatus NtCancelSynchronousIoFile(IntPtr threadHandle, ref IO_STATUS_BLOCK ioRequestToCancel, out IO_STATUS_BLOCK ioStatusBlock);
|
||||
|
||||
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.Directory |= DirectoryAccessMask.SYNCHRONIZE;
|
||||
createOptions &= ~CreateOptions.FILE_SYNCHRONOUS_IO_NONALERT;
|
||||
createOptions |= CreateOptions.FILE_SYNCHRONOUS_IO_ALERT;
|
||||
|
||||
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 QueryDirectory(out List<QueryDirectoryFileInformation> result, object handle, string fileName, FileInformationClass informationClass)
|
||||
{
|
||||
IO_STATUS_BLOCK ioStatusBlock;
|
||||
byte[] buffer = new byte[4096];
|
||||
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;
|
||||
}
|
||||
restartScan = false;
|
||||
List<QueryDirectoryFileInformation> page = QueryDirectoryFileInformation.ReadFileInformationList(buffer, 0, informationClass);
|
||||
result.AddRange(page);
|
||||
}
|
||||
fileNameStructure.Dispose();
|
||||
return NTStatus.STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
public NTStatus GetFileInformation(out FileInformation result, object handle, FileInformationClass informationClass)
|
||||
{
|
||||
IO_STATUS_BLOCK ioStatusBlock;
|
||||
byte[] buffer = new byte[8192];
|
||||
NTStatus status = NtQueryInformationFile((IntPtr)handle, out ioStatusBlock, buffer, (uint)buffer.Length, (uint)informationClass);
|
||||
if (status == NTStatus.STATUS_SUCCESS)
|
||||
{
|
||||
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 fileRenameInformation2 = (FileRenameInformationType2)information;
|
||||
fileRenameInformation2.FileName = ToNativePath(fileRenameInformation2.FileName);
|
||||
|
||||
// Note: WOW64 process should use FILE_RENAME_INFORMATION_TYPE_1.
|
||||
// Note: Server 2003 x64 has issues with using FILE_RENAME_INFORMATION under WOW64.
|
||||
if (!ProcessHelper.Is64BitProcess)
|
||||
{
|
||||
FileRenameInformationType1 fileRenameInformation1 = new FileRenameInformationType1();
|
||||
fileRenameInformation1.ReplaceIfExists = fileRenameInformation2.ReplaceIfExists;
|
||||
fileRenameInformation1.FileName = fileRenameInformation2.FileName;
|
||||
information = fileRenameInformation1;
|
||||
}
|
||||
}
|
||||
else if (information is FileLinkInformationType2)
|
||||
{
|
||||
FileLinkInformationType2 fileLinkInformation2 = (FileLinkInformationType2)information;
|
||||
fileLinkInformation2.FileName = ToNativePath(fileLinkInformation2.FileName);
|
||||
|
||||
if (!ProcessHelper.Is64BitProcess)
|
||||
{
|
||||
FileLinkInformationType1 fileLinkInformation1 = new FileLinkInformationType1();
|
||||
fileLinkInformation1.ReplaceIfExists = fileLinkInformation2.ReplaceIfExists;
|
||||
fileLinkInformation1.FileName = fileLinkInformation2.FileName;
|
||||
information = fileLinkInformation1;
|
||||
}
|
||||
}
|
||||
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[4096];
|
||||
IntPtr volumeHandle;
|
||||
FileStatus fileStatus;
|
||||
string nativePath = @"\??\" + m_directory.FullName.Substring(0, 3);
|
||||
NTStatus status = CreateFile(out volumeHandle, out fileStatus, nativePath, DirectoryAccessMask.GENERIC_READ, 0, (FileAttributes)0, ShareAccess.FILE_SHARE_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)
|
||||
{
|
||||
result = FileSystemInformation.GetFileSystemInformation(buffer, 0, informationClass);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
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();
|
||||
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, ref request.IOStatusBlock, 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)
|
||||
{
|
||||
output = null;
|
||||
return NTStatus.STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
}
|
72
SMBLibrary/Win32/NTFileStore/PendingRequestCollection.cs
Normal file
72
SMBLibrary/Win32/NTFileStore/PendingRequestCollection.cs
Normal file
|
@ -0,0 +1,72 @@
|
|||
/* Copyright (C) 2017 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.Text;
|
||||
|
||||
namespace SMBLibrary.Win32
|
||||
{
|
||||
internal class PendingRequestCollection
|
||||
{
|
||||
private Dictionary<IntPtr, List<PendingRequest>> m_handleToNotifyChangeRequests = new Dictionary<IntPtr, List<PendingRequest>>();
|
||||
|
||||
public void Add(PendingRequest request)
|
||||
{
|
||||
lock (m_handleToNotifyChangeRequests)
|
||||
{
|
||||
List<PendingRequest> pendingRequests;
|
||||
bool containsKey = m_handleToNotifyChangeRequests.TryGetValue(request.FileHandle, out pendingRequests);
|
||||
if (containsKey)
|
||||
{
|
||||
pendingRequests.Add(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingRequests = new List<PendingRequest>();
|
||||
pendingRequests.Add(request);
|
||||
m_handleToNotifyChangeRequests.Add(request.FileHandle, pendingRequests);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(IntPtr handle, uint threadID)
|
||||
{
|
||||
lock (m_handleToNotifyChangeRequests)
|
||||
{
|
||||
List<PendingRequest> pendingRequests;
|
||||
bool containsKey = m_handleToNotifyChangeRequests.TryGetValue(handle, out pendingRequests);
|
||||
if (containsKey)
|
||||
{
|
||||
for (int index = 0; index < pendingRequests.Count; index++)
|
||||
{
|
||||
if (pendingRequests[index].ThreadID == threadID)
|
||||
{
|
||||
pendingRequests.RemoveAt(index);
|
||||
index--;
|
||||
}
|
||||
}
|
||||
|
||||
if (pendingRequests.Count == 0)
|
||||
{
|
||||
m_handleToNotifyChangeRequests.Remove(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<PendingRequest> GetRequestsByHandle(IntPtr handle)
|
||||
{
|
||||
List<PendingRequest> pendingRequests;
|
||||
bool containsKey = m_handleToNotifyChangeRequests.TryGetValue((IntPtr)handle, out pendingRequests);
|
||||
if (containsKey)
|
||||
{
|
||||
return new List<PendingRequest>(pendingRequests);
|
||||
}
|
||||
return new List<PendingRequest>();
|
||||
}
|
||||
}
|
||||
}
|
76
SMBLibrary/Win32/ProcessHelper.cs
Normal file
76
SMBLibrary/Win32/ProcessHelper.cs
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* Copyright (C) 2017 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.Runtime.InteropServices;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace SMBLibrary.Win32
|
||||
{
|
||||
public class ProcessHelper
|
||||
{
|
||||
private static bool? m_is64BitProcess;
|
||||
private static bool? m_isWow64Process;
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool IsWow64Process(
|
||||
[In] IntPtr hProcess,
|
||||
[Out] out bool wow64Process
|
||||
);
|
||||
|
||||
public static bool IsWow64Process(Process process)
|
||||
{
|
||||
if ((Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor >= 1) ||
|
||||
Environment.OSVersion.Version.Major >= 6)
|
||||
{
|
||||
bool retVal;
|
||||
if (!IsWow64Process(process.Handle, out retVal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsWow64Process()
|
||||
{
|
||||
if (!m_isWow64Process.HasValue)
|
||||
{
|
||||
using (Process process = Process.GetCurrentProcess())
|
||||
{
|
||||
m_isWow64Process = IsWow64Process(process);
|
||||
}
|
||||
}
|
||||
return m_isWow64Process.Value;
|
||||
}
|
||||
|
||||
public static bool Is64BitProcess
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!m_is64BitProcess.HasValue)
|
||||
{
|
||||
m_is64BitProcess = (IntPtr.Size == 8);
|
||||
}
|
||||
return m_is64BitProcess.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Is64BitOperatingSystem
|
||||
{
|
||||
get
|
||||
{
|
||||
return Is64BitProcess || IsWow64Process();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
SMBLibrary/Win32/ThreadingHelper.cs
Normal file
24
SMBLibrary/Win32/ThreadingHelper.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
/* Copyright (C) 2017 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.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace SMBLibrary.Win32
|
||||
{
|
||||
public class ThreadingHelper
|
||||
{
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern uint GetCurrentThreadId();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr OpenThread(uint desiredAccess, bool inheritHandle, uint threadId);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern bool CloseHandle(IntPtr handle);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue