winfsp/src/sys/fsctl.c

774 lines
28 KiB
C

/**
* @file sys/fsctl.c
*
* @copyright 2015-2016 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
*
* You can redistribute it and/or modify it under the terms of the GNU
* General Public License version 3 as published by the Free Software
* Foundation.
*
* Licensees holding a valid commercial license may use this file in
* accordance with the commercial license agreement provided with the
* software.
*/
#include <sys/driver.h>
static NTSTATUS FspFsctlFileSystemControl(
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
static NTSTATUS FspFsvolFileSystemControlReparsePoint(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
BOOLEAN IsWrite);
static NTSTATUS FspFsvolFileSystemControlReparsePointComplete(
PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response,
BOOLEAN IsWrite);
static NTSTATUS FspFsvolFileSystemControlOplock(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
static IO_COMPLETION_ROUTINE FspFsvolFileSystemControlOplockCompletion;
static WORKER_THREAD_ROUTINE FspFsvolFileSystemControlOplockCompletionWork;
static NTSTATUS FspFsvolFileSystemControlQueryPersistentVolumeState(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
static NTSTATUS FspFsvolFileSystemControlGetStatistics(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
static NTSTATUS FspFsvolFileSystemControlGetRetrievalPointers(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
static NTSTATUS FspFsvolFileSystemControl(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
FSP_IOCMPL_DISPATCH FspFsvolFileSystemControlComplete;
static FSP_IOP_REQUEST_FINI FspFsvolFileSystemControlRequestFini;
FSP_DRIVER_DISPATCH FspFileSystemControl;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FspFsctlFileSystemControl)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlReparsePoint)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlReparsePointComplete)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlOplock)
// !#pragma alloc_text(PAGE, FspFsvolFileSystemControlOplockCompletion)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlOplockCompletionWork)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlQueryPersistentVolumeState)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlGetStatistics)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlGetRetrievalPointers)
#pragma alloc_text(PAGE, FspFsvolFileSystemControl)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlComplete)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlRequestFini)
#pragma alloc_text(PAGE, FspFileSystemControl)
#endif
enum
{
RequestFileNode = 0,
};
static NTSTATUS FspFsctlFileSystemControl(
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
NTSTATUS Result = STATUS_INVALID_DEVICE_REQUEST;
switch (IrpSp->MinorFunction)
{
case IRP_MN_USER_FS_REQUEST:
switch (IrpSp->Parameters.FileSystemControl.FsControlCode)
{
case FSP_FSCTL_VOLUME_NAME:
if (0 != IrpSp->FileObject->FsContext2)
Result = FspVolumeGetName(FsctlDeviceObject, Irp, IrpSp);
break;
case FSP_FSCTL_VOLUME_LIST:
Result = FspVolumeGetNameList(FsctlDeviceObject, Irp, IrpSp);
break;
case FSP_FSCTL_TRANSACT:
case FSP_FSCTL_TRANSACT_BATCH:
if (0 != IrpSp->FileObject->FsContext2)
Result = FspVolumeTransact(FsctlDeviceObject, Irp, IrpSp);
break;
case FSP_FSCTL_STOP:
if (0 != IrpSp->FileObject->FsContext2)
Result = FspVolumeStop(FsctlDeviceObject, Irp, IrpSp);
break;
}
break;
case IRP_MN_MOUNT_VOLUME:
Result = FspVolumeMount(FsctlDeviceObject, Irp, IrpSp);
break;
}
return Result;
}
static NTSTATUS FspFsvolFileSystemControlReparsePoint(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
BOOLEAN IsWrite)
{
PAGED_CODE();
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
PFILE_OBJECT FileObject = IrpSp->FileObject;
/* do we support reparse points? */
if (!FsvolDeviceExtension->VolumeParams.ReparsePoints)
return STATUS_INVALID_DEVICE_REQUEST;
/* is this a valid FileObject? */
if (!FspFileNodeIsValid(FileObject->FsContext))
return STATUS_INVALID_PARAMETER;
NTSTATUS Result;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
ULONG FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
PVOID InputBuffer = Irp->AssociatedIrp.SystemBuffer;
PVOID OutputBuffer = Irp->UserBuffer;
ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
PREPARSE_DATA_BUFFER ReparseData;
PWSTR ReparseTargetPath;
USHORT ReparseTargetPathLength;
UINT16 TargetOnFileSystem = 0;
FSP_FSCTL_TRANSACT_REQ *Request;
ASSERT(FileNode == FileDesc->FileNode);
if (IsWrite)
{
if (0 == InputBuffer || 0 == InputBufferLength)
return STATUS_INVALID_BUFFER_SIZE;
if (0 != OutputBufferLength)
return STATUS_INVALID_PARAMETER;
if (FSP_FSCTL_TRANSACT_REQ_BUFFER_SIZEMAX - (FileNode->FileName.Length + sizeof(WCHAR)) <
InputBufferLength)
return STATUS_IO_REPARSE_DATA_INVALID;
Result = FsRtlValidateReparsePointBuffer(InputBufferLength, InputBuffer);
if (!NT_SUCCESS(Result))
return Result;
/* NTFS seems to require one of these rights to allow FSCTL_SET_REPARSE_POINT */
if (!FlagOn(FileDesc->GrantedAccess,
FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES))
return STATUS_ACCESS_DENIED;
ReparseData = (PREPARSE_DATA_BUFFER)InputBuffer;
if (IO_REPARSE_TAG_SYMLINK == ReparseData->ReparseTag)
{
/* NTFS severely limits symbolic links; we will not do that unless our file system asks */
if (FsvolDeviceExtension->VolumeParams.ReparsePointsAccessCheck)
{
if (KernelMode != Irp->RequestorMode &&
!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_CREATE_SYMBOLIC_LINK_PRIVILEGE),
UserMode))
return STATUS_PRIVILEGE_NOT_HELD;
}
ReparseTargetPath = ReparseData->SymbolicLinkReparseBuffer.PathBuffer +
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
ReparseTargetPathLength = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
/* is this an absolute path? determine if target resides on same device as link */
if (!FlagOn(ReparseData->SymbolicLinkReparseBuffer.Flags, SYMLINK_FLAG_RELATIVE) &&
ReparseTargetPathLength >= sizeof(WCHAR) && L'\\' == ReparseTargetPath[0])
{
UNICODE_STRING TargetObjectName;
PDEVICE_OBJECT TargetDeviceObject;
PFILE_OBJECT TargetFileObject;
ULONG TargetFileNameIndex;
ULONG32 TargetProviderId;
FSRTL_MUP_PROVIDER_INFO_LEVEL_1 ProviderInfo;
ULONG ProviderInfoSize;
TargetObjectName.Length = TargetObjectName.MaximumLength = ReparseTargetPathLength;
TargetObjectName.Buffer = ReparseTargetPath;
/* get a pointer to the target device */
Result = FspGetDeviceObjectPointer(&TargetObjectName, FILE_READ_DATA,
&TargetFileNameIndex, &TargetFileObject, &TargetDeviceObject);
if (!NT_SUCCESS(Result))
goto target_check_exit;
/* is the target device the same as ours? */
if (TargetFileNameIndex < ReparseTargetPathLength &&
IoGetRelatedDeviceObject(FileObject) == TargetDeviceObject)
{
if (0 == FsvolDeviceExtension->VolumePrefix.Length)
/* not going thru MUP: DONE! */
TargetOnFileSystem = (UINT16)TargetFileNameIndex;
else
{
/* going thru MUP cases: \Device\Volume{GUID} and \??\UNC\{VolumePrefix} */
ProviderInfoSize = sizeof ProviderInfo;
Result = FsRtlMupGetProviderInfoFromFileObject(TargetFileObject, 1,
&ProviderInfo, &ProviderInfoSize);
if (NT_SUCCESS(Result))
{
/* case \Device\Volume{GUID}: is the targer provider id same as ours? */
TargetProviderId = ProviderInfo.ProviderId;
ProviderInfoSize = sizeof ProviderInfo;
Result = FsRtlMupGetProviderInfoFromFileObject(FileObject, 1,
&ProviderInfo, &ProviderInfoSize);
if (!NT_SUCCESS(Result))
goto target_check_exit;
if (ProviderInfo.ProviderId == TargetProviderId)
TargetOnFileSystem = (UINT16)TargetFileNameIndex;
}
else
{
/* case \??\UNC\{VolumePrefix}: is the target volume prefix same as ours? */
TargetObjectName.Length = TargetObjectName.MaximumLength =
FsvolDeviceExtension->VolumePrefix.Length;
TargetObjectName.Buffer = ReparseTargetPath +
TargetFileNameIndex / sizeof(WCHAR);
TargetFileNameIndex += FsvolDeviceExtension->VolumePrefix.Length;
if (TargetFileNameIndex < ReparseTargetPathLength &&
FspFsvolDeviceVolumePrefixInString(FsvolDeviceObject, &TargetObjectName))
TargetOnFileSystem = (UINT16)TargetFileNameIndex;
}
}
}
ObDereferenceObject(TargetFileObject);
target_check_exit:
;
}
}
FspFileNodeAcquireExclusive(FileNode, Full);
}
else
{
if (0 != InputBufferLength)
return STATUS_INVALID_PARAMETER;
if (0 == OutputBuffer || 0 == OutputBufferLength)
return STATUS_INVALID_USER_BUFFER;
/*
* NtFsControlFile (IopXxxControlFile) will setup Irp->AssociatedIrp.SystemBuffer
* with enough space for either InputBufferLength or OutputBufferLength. There is
* no need to call FspBufferUserBuffer ourselves.
*/
FspFileNodeAcquireShared(FileNode, Full);
}
Result = FspIopCreateRequestEx(Irp, &FileNode->FileName, IsWrite ? InputBufferLength : 0,
FspFsvolFileSystemControlRequestFini, &Request);
if (!NT_SUCCESS(Result))
{
FspFileNodeRelease(FileNode, Full);
return Result;
}
Request->Kind = FspFsctlTransactFileSystemControlKind;
Request->Req.FileSystemControl.UserContext = FileNode->UserContext;
Request->Req.FileSystemControl.UserContext2 = FileDesc->UserContext2;
Request->Req.FileSystemControl.FsControlCode = FsControlCode;
if (IsWrite)
{
Request->Req.FileSystemControl.Buffer.Offset = Request->FileName.Size;
Request->Req.FileSystemControl.Buffer.Size = (UINT16)InputBufferLength;
RtlCopyMemory(Request->Buffer + Request->Req.FileSystemControl.Buffer.Offset,
InputBuffer, InputBufferLength);
Request->Req.FileSystemControl.TargetOnFileSystem = TargetOnFileSystem;
}
FspFileNodeSetOwner(FileNode, Full, Request);
FspIopRequestContext(Request, RequestFileNode) = FileNode;
return FSP_STATUS_IOQ_POST;
}
static NTSTATUS FspFsvolFileSystemControlReparsePointComplete(
PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response,
BOOLEAN IsWrite)
{
PAGED_CODE();
if (IsWrite)
{
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = IrpSp->FileObject;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
FSP_FILE_DESC *FileDesc = IrpSp->FileObject->FsContext2;
ASSERT(FileNode == FileDesc->FileNode);
FspFileNodeInvalidateFileInfo(FileNode);
FileDesc->DidSetReparsePoint = TRUE;
FileDesc->DidSetMetadata = TRUE;
return STATUS_SUCCESS;
}
NTSTATUS Result;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PVOID OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
if (Response->Buffer + Response->Rsp.FileSystemControl.Buffer.Offset +
Response->Rsp.FileSystemControl.Buffer.Size > (PUINT8)Response + Response->Size)
return STATUS_IO_REPARSE_DATA_INVALID;
Result = FsRtlValidateReparsePointBuffer(Response->Rsp.FileSystemControl.Buffer.Size,
(PVOID)(Response->Buffer + Response->Rsp.FileSystemControl.Buffer.Offset));
if (!NT_SUCCESS(Result))
return Result;
if (Response->Rsp.FileSystemControl.Buffer.Size > OutputBufferLength)
return STATUS_BUFFER_TOO_SMALL;
RtlCopyMemory(OutputBuffer, Response->Buffer + Response->Rsp.FileSystemControl.Buffer.Offset,
Response->Rsp.FileSystemControl.Buffer.Size);
Irp->IoStatus.Information = Response->Rsp.FileSystemControl.Buffer.Size;
return STATUS_SUCCESS;
}
typedef struct
{
PDEVICE_OBJECT FsvolDeviceObject;
WORK_QUEUE_ITEM WorkItem;
} FSP_FSVOL_FILESYSTEM_CONTROL_OPLOCK_COMPLETION_CONTEXT;
static NTSTATUS FspFsvolFileSystemControlOplock(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
PFILE_OBJECT FileObject = IrpSp->FileObject;
/* is this a valid FileObject? */
if (!FspFileNodeIsValid(FileObject->FsContext))
return STATUS_INVALID_DEVICE_REQUEST;
NTSTATUS Result;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
ULONG FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
PVOID InputBuffer = Irp->AssociatedIrp.SystemBuffer;
ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
ULONG OplockCount;
FSP_FSVOL_FILESYSTEM_CONTROL_OPLOCK_COMPLETION_CONTEXT *CompletionContext;
if (FileNode->IsDirectory)
return STATUS_INVALID_PARAMETER;
/*
* As per FastFat:
*
* We grab the Fcb exclusively for oplock requests, shared for oplock
* break acknowledgement.
*/
switch (FsControlCode)
{
case FSCTL_REQUEST_OPLOCK:
if (sizeof(REQUEST_OPLOCK_INPUT_BUFFER) > InputBufferLength ||
sizeof(REQUEST_OPLOCK_OUTPUT_BUFFER) > OutputBufferLength)
return STATUS_BUFFER_TOO_SMALL;
if (FlagOn(((PREQUEST_OPLOCK_INPUT_BUFFER)InputBuffer)->Flags,
REQUEST_OPLOCK_INPUT_FLAG_REQUEST))
goto exclusive;
if (FlagOn(((PREQUEST_OPLOCK_INPUT_BUFFER)InputBuffer)->Flags,
REQUEST_OPLOCK_INPUT_FLAG_ACK))
goto shared;
/* one of REQUEST_OPLOCK_INPUT_FLAG_REQUEST or REQUEST_OPLOCK_INPUT_FLAG_ACK required */
return STATUS_INVALID_PARAMETER;
case FSCTL_REQUEST_OPLOCK_LEVEL_1:
case FSCTL_REQUEST_OPLOCK_LEVEL_2:
case FSCTL_REQUEST_BATCH_OPLOCK:
case FSCTL_REQUEST_FILTER_OPLOCK:
exclusive:
FspFileNodeAcquireExclusive(FileNode, Main);
if (!FsRtlOplockIsSharedRequest(Irp))
{
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
OplockCount = FileNode->HandleCount;
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
}
else
OplockCount = FsRtlAreThereCurrentOrInProgressFileLocks(&FileNode->FileLock);
break;
case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
case FSCTL_OPLOCK_BREAK_NOTIFY:
case FSCTL_OPLOCK_BREAK_ACK_NO_2:
shared:
FspFileNodeAcquireShared(FileNode, Main);
OplockCount = 0;
break;
default:
ASSERT(0);
return STATUS_INVALID_DEVICE_REQUEST;
}
/*
* The FileNode is acquired exclusive or shared.
* Make sure to release it before exiting!
*/
if (FSCTL_REQUEST_FILTER_OPLOCK == FsControlCode ||
FSCTL_REQUEST_BATCH_OPLOCK == FsControlCode ||
(FSCTL_REQUEST_OPLOCK == FsControlCode &&
FlagOn(((PREQUEST_OPLOCK_INPUT_BUFFER)InputBuffer)->RequestedOplockLevel,
OPLOCK_LEVEL_CACHE_HANDLE)))
{
BOOLEAN DeletePending;
DeletePending = 0 != FileNode->DeletePending;
MemoryBarrier();
if (DeletePending)
{
Result = STATUS_DELETE_PENDING;
goto unlock_exit;
}
}
/*
* This IRP will be completed by the FSRTL package and therefore
* we will have no chance to do our normal IRP completion processing.
* Hook the IRP completion and perform the IRP completion processing
* there.
*/
CompletionContext = FspAllocNonPaged(sizeof *CompletionContext);
if (0 == CompletionContext)
{
Result = STATUS_INSUFFICIENT_RESOURCES;
goto unlock_exit;
}
CompletionContext->FsvolDeviceObject = FsvolDeviceObject;
ExInitializeWorkItem(&CompletionContext->WorkItem,
FspFsvolFileSystemControlOplockCompletionWork, CompletionContext);
Result = FspIrpHook(Irp, FspFsvolFileSystemControlOplockCompletion, CompletionContext);
if (!NT_SUCCESS(Result))
{
FspFree(CompletionContext);
goto unlock_exit;
}
/*
* FspOplockFsctrl takes ownership of the IRP under all circumstances.
*/
IoSetTopLevelIrp(0);
Result = FspFileNodeOplockFsctl(FileNode, Irp, OplockCount);
FspFileNodeRelease(FileNode, Main);
return Result | FSP_STATUS_IGNORE_BIT;
unlock_exit:
FspFileNodeRelease(FileNode, Main);
return Result;
}
static NTSTATUS FspFsvolFileSystemControlOplockCompletion(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context)
{
// !PAGED_CODE();
FSP_FSVOL_FILESYSTEM_CONTROL_OPLOCK_COMPLETION_CONTEXT *CompletionContext =
FspIrpHookContext(Context);
ExQueueWorkItem(&CompletionContext->WorkItem, DelayedWorkQueue);
return FspIrpHookNext(DeviceObject, Irp, Context);
}
static VOID FspFsvolFileSystemControlOplockCompletionWork(PVOID Context)
{
PAGED_CODE();
FSP_FSVOL_FILESYSTEM_CONTROL_OPLOCK_COMPLETION_CONTEXT *CompletionContext = Context;
FspDeviceDereference(CompletionContext->FsvolDeviceObject);
FspFree(CompletionContext);
}
static NTSTATUS FspFsvolFileSystemControlQueryPersistentVolumeState(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
PVOID Buffer = Irp->AssociatedIrp.SystemBuffer;
ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
PFILE_FS_PERSISTENT_VOLUME_INFORMATION Info;
if (0 == Buffer)
return STATUS_INVALID_PARAMETER;
if (sizeof(FILE_FS_PERSISTENT_VOLUME_INFORMATION) > InputBufferLength ||
sizeof(FILE_FS_PERSISTENT_VOLUME_INFORMATION) > OutputBufferLength)
return STATUS_BUFFER_TOO_SMALL;
Info = Buffer;
if (1 != Info->Version ||
!FlagOn(Info->FlagMask, PERSISTENT_VOLUME_STATE_SHORT_NAME_CREATION_DISABLED))
return STATUS_INVALID_PARAMETER;
RtlZeroMemory(Info, sizeof(FILE_FS_PERSISTENT_VOLUME_INFORMATION));
Info->VolumeFlags = PERSISTENT_VOLUME_STATE_SHORT_NAME_CREATION_DISABLED;
Irp->IoStatus.Information = sizeof(FILE_FS_PERSISTENT_VOLUME_INFORMATION);
return STATUS_SUCCESS;
}
static NTSTATUS FspFsvolFileSystemControlGetStatistics(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
NTSTATUS Result;
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
PVOID Buffer = Irp->AssociatedIrp.SystemBuffer;
ULONG Length = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
Result = FspStatisticsCopy(FsvolDeviceExtension->Statistics, Buffer, &Length);
Irp->IoStatus.Information = Length;
return Result;
}
static NTSTATUS FspFsvolFileSystemControlGetRetrievalPointers(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
/*
* FSCTL_GET_RETRIEVAL_POINTERS is normally used for defragmentation support,
* which WinFsp does NOT support. However some tools (notably IFSTEST) use it
* to determine whether files are "resident" or "non-resident" which is an NTFS
* concept. To support such tools we respond in a manner that indicates that
* WinFsp files are always non-resident.
*/
PAGED_CODE();
PFILE_OBJECT FileObject = IrpSp->FileObject;
/* is this a valid FileObject? */
if (!FspFileNodeIsValid(FileObject->FsContext))
return STATUS_INVALID_DEVICE_REQUEST;
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
FSP_FILE_NODE *FileNode = FileObject->FsContext;
PSTARTING_VCN_INPUT_BUFFER InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
PRETRIEVAL_POINTERS_BUFFER OutputBuffer = Irp->UserBuffer;
ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
STARTING_VCN_INPUT_BUFFER StartingVcn;
RETRIEVAL_POINTERS_BUFFER RetrievalPointers;
UINT64 AllocationUnit;
if (0 == InputBuffer || 0 == OutputBuffer)
return STATUS_INVALID_PARAMETER;
if (sizeof(STARTING_VCN_INPUT_BUFFER) > InputBufferLength ||
sizeof(RETRIEVAL_POINTERS_BUFFER) > OutputBufferLength)
return STATUS_BUFFER_TOO_SMALL;
if (UserMode == Irp->RequestorMode)
{
try
{
ProbeForRead(InputBuffer, InputBufferLength, sizeof(UCHAR)/*FastFat*/);
StartingVcn = *InputBuffer;
}
except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
else
StartingVcn = *InputBuffer;
RetrievalPointers.ExtentCount = 1;
RetrievalPointers.StartingVcn.QuadPart = 0;
RetrievalPointers.Extents[0].NextVcn.QuadPart = 0;
RetrievalPointers.Extents[0].Lcn.QuadPart = -1LL;
AllocationUnit = FsvolDeviceExtension->VolumeParams.SectorSize *
FsvolDeviceExtension->VolumeParams.SectorsPerAllocationUnit;
FspFileNodeAcquireShared(FileNode, Main);
RetrievalPointers.Extents[0].NextVcn.QuadPart =
FileNode->Header.AllocationSize.QuadPart / AllocationUnit;
FspFileNodeRelease(FileNode, Main);
if (StartingVcn.StartingVcn.QuadPart > RetrievalPointers.Extents[0].NextVcn.QuadPart)
return STATUS_END_OF_FILE;
if (UserMode == Irp->RequestorMode)
{
try
{
ProbeForWrite(OutputBuffer, OutputBufferLength, sizeof(UCHAR)/*FastFat*/);
*OutputBuffer = RetrievalPointers;
}
except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
else
*OutputBuffer = RetrievalPointers;
Irp->IoStatus.Information = sizeof RetrievalPointers;
return STATUS_SUCCESS;
}
static NTSTATUS FspFsvolFileSystemControl(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
NTSTATUS Result = STATUS_INVALID_DEVICE_REQUEST;
switch (IrpSp->MinorFunction)
{
case IRP_MN_USER_FS_REQUEST:
switch (IrpSp->Parameters.FileSystemControl.FsControlCode)
{
case FSP_FSCTL_WORK:
case FSP_FSCTL_WORK_BEST_EFFORT:
Result = FspVolumeWork(FsvolDeviceObject, Irp, IrpSp);
break;
case FSCTL_GET_REPARSE_POINT:
Result = FspFsvolFileSystemControlReparsePoint(FsvolDeviceObject, Irp, IrpSp, FALSE);
break;
case FSCTL_SET_REPARSE_POINT:
case FSCTL_DELETE_REPARSE_POINT:
Result = FspFsvolFileSystemControlReparsePoint(FsvolDeviceObject, Irp, IrpSp, TRUE);
break;
case FSCTL_REQUEST_OPLOCK_LEVEL_1:
case FSCTL_REQUEST_OPLOCK_LEVEL_2:
case FSCTL_REQUEST_BATCH_OPLOCK:
case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
case FSCTL_OPLOCK_BREAK_NOTIFY:
case FSCTL_OPLOCK_BREAK_ACK_NO_2:
case FSCTL_REQUEST_FILTER_OPLOCK:
case FSCTL_REQUEST_OPLOCK:
Result = FspFsvolFileSystemControlOplock(FsvolDeviceObject, Irp, IrpSp);
break;
case FSCTL_QUERY_PERSISTENT_VOLUME_STATE:
Result = FspFsvolFileSystemControlQueryPersistentVolumeState(FsvolDeviceObject, Irp, IrpSp);
break;
case FSCTL_FILESYSTEM_GET_STATISTICS:
Result = FspFsvolFileSystemControlGetStatistics(FsvolDeviceObject, Irp, IrpSp);
break;
case FSCTL_GET_RETRIEVAL_POINTERS:
Result = FspFsvolFileSystemControlGetRetrievalPointers(FsvolDeviceObject, Irp, IrpSp);
break;
}
break;
}
return Result;
}
NTSTATUS FspFsvolFileSystemControlComplete(
PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response)
{
FSP_ENTER_IOC(PAGED_CODE());
/* exit now if we do not have a FileObject (FSP_FSCTL_WORK*) */
if (0 == IrpSp->FileObject)
FSP_RETURN();
if (!NT_SUCCESS(Response->IoStatus.Status))
{
Irp->IoStatus.Information = 0;
Result = Response->IoStatus.Status;
FSP_RETURN();
}
PFILE_OBJECT FileObject = IrpSp->FileObject;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
Result = STATUS_INVALID_DEVICE_REQUEST;
switch (IrpSp->MinorFunction)
{
case IRP_MN_USER_FS_REQUEST:
switch (IrpSp->Parameters.FileSystemControl.FsControlCode)
{
case FSCTL_GET_REPARSE_POINT:
Result = FspFsvolFileSystemControlReparsePointComplete(Irp, Response, FALSE);
break;
case FSCTL_SET_REPARSE_POINT:
case FSCTL_DELETE_REPARSE_POINT:
Result = FspFsvolFileSystemControlReparsePointComplete(Irp, Response, TRUE);
break;
}
break;
}
ASSERT(STATUS_INVALID_DEVICE_REQUEST != Result);
FspIopRequestContext(Request, RequestFileNode) = 0;
FspFileNodeReleaseOwner(FileNode, Full, Request);
FSP_LEAVE_IOC(
"%s%sFileObject=%p",
IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction ?
IoctlCodeSym(IrpSp->Parameters.FileSystemControl.FsControlCode) : "",
IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction ? ", " : "",
IrpSp->FileObject);
}
static VOID FspFsvolFileSystemControlRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Context[4])
{
PAGED_CODE();
FSP_FILE_NODE *FileNode = Context[RequestFileNode];
if (0 != FileNode)
FspFileNodeReleaseOwner(FileNode, Full, Request);
}
NTSTATUS FspFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
FSP_ENTER_MJ(PAGED_CODE());
switch (FspDeviceExtension(DeviceObject)->Kind)
{
case FspFsvolDeviceExtensionKind:
FSP_RETURN(Result = FspFsvolFileSystemControl(DeviceObject, Irp, IrpSp));
case FspFsctlDeviceExtensionKind:
FSP_RETURN(Result = FspFsctlFileSystemControl(DeviceObject, Irp, IrpSp));
default:
FSP_RETURN(Result = STATUS_INVALID_DEVICE_REQUEST);
}
FSP_LEAVE_MJ(
"%s%sFileObject=%p",
IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction ?
IoctlCodeSym(IrpSp->Parameters.FileSystemControl.FsControlCode) : "",
IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction ? ", " : "",
IrpSp->FileObject);
}