SMBLibrary/SMBLibrary.Win32/NTFileStore/NTDirectoryFileSystem.cs

526 lines
28 KiB
C#

/* 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;
}
}
}
}
}