mirror of
https://github.com/winfsp/winfsp.git
synced 2025-04-23 00:43:00 -05:00
774 lines
28 KiB
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);
|
|
}
|