mirror of
https://github.com/winfsp/winfsp.git
synced 2025-04-24 17:32:29 -05:00
696 lines
22 KiB
C
696 lines
22 KiB
C
/**
|
|
* @file sys/read.c
|
|
*
|
|
* @copyright 2015-2022 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 software
|
|
* in accordance with the commercial license agreement provided in
|
|
* conjunction with the software. The terms and conditions of any such
|
|
* commercial license agreement shall govern, supersede, and render
|
|
* ineffective any application of the GPLv3 license to this software,
|
|
* notwithstanding of any reference thereto in the software or
|
|
* associated repository.
|
|
*/
|
|
|
|
#include <sys/driver.h>
|
|
|
|
FAST_IO_READ FspFastIoRead;
|
|
static NTSTATUS FspFsvolRead(
|
|
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
|
static NTSTATUS FspFsvolReadCached(
|
|
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
|
|
BOOLEAN CanWait);
|
|
static NTSTATUS FspFsvolReadNonCached(
|
|
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
|
|
BOOLEAN CanWait);
|
|
FSP_IOPREP_DISPATCH FspFsvolReadPrepare;
|
|
FSP_IOCMPL_DISPATCH FspFsvolReadComplete;
|
|
static FSP_IOP_REQUEST_FINI FspFsvolReadNonCachedRequestFini;
|
|
FSP_DRIVER_DISPATCH FspRead;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, FspFastIoRead)
|
|
#pragma alloc_text(PAGE, FspFsvolRead)
|
|
#pragma alloc_text(PAGE, FspFsvolReadCached)
|
|
#pragma alloc_text(PAGE, FspFsvolReadNonCached)
|
|
#pragma alloc_text(PAGE, FspFsvolReadPrepare)
|
|
#pragma alloc_text(PAGE, FspFsvolReadComplete)
|
|
#pragma alloc_text(PAGE, FspFsvolReadNonCachedRequestFini)
|
|
#pragma alloc_text(PAGE, FspRead)
|
|
#endif
|
|
|
|
enum
|
|
{
|
|
/* ReadNonCached */
|
|
RequestIrp = 0,
|
|
RequestCookie = 1,
|
|
RequestSafeMdl = 1,
|
|
RequestAddress = 2,
|
|
RequestProcess = 3,
|
|
};
|
|
FSP_FSCTL_STATIC_ASSERT(RequestCookie == RequestSafeMdl, "");
|
|
|
|
BOOLEAN FspFastIoRead(
|
|
PFILE_OBJECT FileObject,
|
|
PLARGE_INTEGER ByteOffset,
|
|
ULONG Length,
|
|
BOOLEAN CanWait,
|
|
ULONG Key,
|
|
PVOID UserBuffer,
|
|
PIO_STATUS_BLOCK IoStatus,
|
|
PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
FSP_ENTER_FIO(PAGED_CODE());
|
|
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
IoStatus->Information = 0;
|
|
|
|
if (!FspFileNodeIsValid(FileObject->FsContext))
|
|
{
|
|
IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
FSP_RETURN(Result = TRUE);
|
|
}
|
|
|
|
FSP_FILE_NODE* FileNode = FileObject->FsContext;
|
|
LARGE_INTEGER ReadOffset = *ByteOffset;
|
|
ULONG ReadLength = Length;
|
|
FSP_FSCTL_FILE_INFO FileInfo;
|
|
|
|
/* only regular files can be read */
|
|
if (FileNode->IsDirectory)
|
|
{
|
|
IoStatus->Status = STATUS_INVALID_PARAMETER;
|
|
FSP_RETURN(Result = TRUE);
|
|
}
|
|
|
|
/* do we have anything to read? */
|
|
if (0 == ReadLength)
|
|
FSP_RETURN(Result = TRUE);
|
|
|
|
/* does the file support caching? */
|
|
if (!FlagOn(FileObject->Flags, FO_CACHE_SUPPORTED))
|
|
FSP_RETURN(Result = FALSE);
|
|
|
|
/* try to acquire the FileNode Main shared */
|
|
Result = DEBUGTEST(90) &&
|
|
FspFileNodeTryAcquireSharedF(FileNode, FspFileNodeAcquireMain, CanWait);
|
|
if (!Result)
|
|
FSP_RETURN(Result = FALSE);
|
|
|
|
/* is the file actually cached? */
|
|
if (0 == FileObject->PrivateCacheMap)
|
|
{
|
|
FspFileNodeRelease(FileNode, Main);
|
|
FSP_RETURN(Result = FALSE);
|
|
}
|
|
|
|
/* does the file have oplocks or file locks? */
|
|
Result =
|
|
FsRtlOplockIsFastIoPossible(FspFileNodeAddrOfOplock(FileNode)) &&
|
|
!FsRtlAreThereCurrentFileLocks(&FileNode->FileLock);
|
|
if (!Result)
|
|
{
|
|
FspFileNodeRelease(FileNode, Main);
|
|
FSP_RETURN(Result = FALSE);
|
|
}
|
|
|
|
/* trim ReadLength; the cache manager does not tolerate reads beyond file size */
|
|
ASSERT(FspTimeoutInfinity32 ==
|
|
FspFsvolDeviceExtension(FsvolDeviceObject)->VolumeParams.FileInfoTimeout);
|
|
FspFileNodeGetFileInfo(FileNode, &FileInfo);
|
|
if ((UINT64)ReadOffset.QuadPart >= FileInfo.FileSize)
|
|
{
|
|
FspFileNodeRelease(FileNode, Main);
|
|
IoStatus->Status = STATUS_END_OF_FILE;
|
|
FSP_RETURN(Result = TRUE);
|
|
}
|
|
if ((UINT64)ReadLength > FileInfo.FileSize - ReadOffset.QuadPart)
|
|
ReadLength = (ULONG)(FileInfo.FileSize - ReadOffset.QuadPart);
|
|
|
|
NTSTATUS Result0 =
|
|
FspCcCopyRead(FileObject, &ReadOffset, ReadLength, CanWait, UserBuffer, IoStatus);
|
|
if (!NT_SUCCESS(Result0))
|
|
{
|
|
IoStatus->Status = Result0;
|
|
IoStatus->Information = 0;
|
|
FspFileNodeRelease(FileNode, Main);
|
|
FSP_RETURN(Result = TRUE);
|
|
}
|
|
Result = STATUS_SUCCESS == Result0;
|
|
|
|
SetFlag(FileObject->Flags, FO_FILE_FAST_IO_READ);
|
|
if (Result)
|
|
FileObject->CurrentByteOffset.QuadPart = ReadOffset.QuadPart + ReadLength;
|
|
|
|
FspFileNodeRelease(FileNode, Main);
|
|
|
|
FSP_LEAVE_FIO(
|
|
"FileObject=%p, UserBuffer=%p, CanWait=%d, "
|
|
"Key=%#lx, ByteOffset=%#lx:%#lx, Length=%ld",
|
|
FileObject, UserBuffer, CanWait,
|
|
Key, ByteOffset->HighPart, ByteOffset->LowPart, Length);
|
|
}
|
|
|
|
static NTSTATUS FspFsvolRead(
|
|
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
/* is this a valid FileObject? */
|
|
if (!FspFileNodeIsValid(IrpSp->FileObject->FsContext))
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
NTSTATUS Result;
|
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
|
FSP_FILE_NODE *FileNode = FileObject->FsContext;
|
|
|
|
/* is this an MDL complete request? */
|
|
if (FlagOn(IrpSp->MinorFunction, IRP_MN_COMPLETE))
|
|
{
|
|
Result = FspCcMdlReadComplete(FileObject, Irp->MdlAddress);
|
|
Irp->MdlAddress = 0;
|
|
return Result;
|
|
}
|
|
|
|
/* only regular files can be read */
|
|
if (FileNode->IsDirectory)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* do we have anything to read? */
|
|
if (0 == IrpSp->Parameters.Read.Length)
|
|
{
|
|
Irp->IoStatus.Information = 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* are we doing cached or non-cached I/O? */
|
|
if (FlagOn(FileObject->Flags, FO_CACHE_SUPPORTED) &&
|
|
!FlagOn(Irp->Flags, IRP_PAGING_IO | IRP_NOCACHE))
|
|
Result = FspFsvolReadCached(FsvolDeviceObject, Irp, IrpSp, IoIsOperationSynchronous(Irp));
|
|
else
|
|
Result = FspFsvolReadNonCached(FsvolDeviceObject, Irp, IrpSp, IoIsOperationSynchronous(Irp));
|
|
|
|
return Result;
|
|
}
|
|
|
|
static NTSTATUS FspFsvolReadCached(
|
|
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
|
|
BOOLEAN CanWait)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
/* assert: must be top-level IRP */
|
|
ASSERT(0 == FspIrpTopFlags(Irp));
|
|
|
|
NTSTATUS Result;
|
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
|
FSP_FILE_NODE *FileNode = FileObject->FsContext;
|
|
LARGE_INTEGER ReadOffset = IrpSp->Parameters.Read.ByteOffset;
|
|
ULONG ReadLength = IrpSp->Parameters.Read.Length;
|
|
BOOLEAN SynchronousIo = BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO);
|
|
FSP_FSCTL_FILE_INFO FileInfo;
|
|
CC_FILE_SIZES FileSizes;
|
|
BOOLEAN Success;
|
|
|
|
/* try to acquire the FileNode Main shared */
|
|
Success = DEBUGTEST(90) &&
|
|
FspFileNodeTryAcquireSharedF(FileNode, FspFileNodeAcquireMain, CanWait);
|
|
if (!Success)
|
|
return FspWqRepostIrpWorkItem(Irp, FspFsvolReadCached, 0);
|
|
|
|
/* perform oplock check */
|
|
Result = FspFileNodeOplockCheckAsync(
|
|
FileNode, FspFileNodeAcquireMain, FspFsvolReadCached,
|
|
Irp);
|
|
if (STATUS_PENDING == Result)
|
|
return Result;
|
|
if (!NT_SUCCESS(Result))
|
|
{
|
|
FspFileNodeRelease(FileNode, Main);
|
|
return Result;
|
|
}
|
|
|
|
/* check the file locks */
|
|
if (!FsRtlCheckLockForReadAccess(&FileNode->FileLock, Irp))
|
|
{
|
|
FspFileNodeRelease(FileNode, Main);
|
|
return STATUS_FILE_LOCK_CONFLICT;
|
|
}
|
|
|
|
/* trim ReadLength; the cache manager does not tolerate reads beyond file size */
|
|
ASSERT(FspTimeoutInfinity32 ==
|
|
FspFsvolDeviceExtension(FsvolDeviceObject)->VolumeParams.FileInfoTimeout);
|
|
FspFileNodeGetFileInfo(FileNode, &FileInfo);
|
|
if ((UINT64)ReadOffset.QuadPart >= FileInfo.FileSize)
|
|
{
|
|
FspFileNodeRelease(FileNode, Main);
|
|
return STATUS_END_OF_FILE;
|
|
}
|
|
if ((UINT64)ReadLength > FileInfo.FileSize - ReadOffset.QuadPart)
|
|
ReadLength = (ULONG)(FileInfo.FileSize - ReadOffset.QuadPart);
|
|
|
|
/* initialize cache if not already initialized! */
|
|
if (0 == FileObject->PrivateCacheMap)
|
|
{
|
|
FileSizes.AllocationSize.QuadPart = FileInfo.AllocationSize;
|
|
FileSizes.FileSize.QuadPart = FileInfo.FileSize;
|
|
FileSizes.ValidDataLength.QuadPart = MAXLONGLONG;
|
|
|
|
Result = FspCcInitializeCacheMap(FileObject, &FileSizes, FALSE,
|
|
&FspCacheManagerCallbacks, FileNode);
|
|
if (!NT_SUCCESS(Result))
|
|
{
|
|
FspFileNodeRelease(FileNode, Main);
|
|
return Result;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* From this point forward we must jump to the CLEANUP label on failure.
|
|
*/
|
|
|
|
/* are we using the copy or MDL interface? */
|
|
if (!FlagOn(IrpSp->MinorFunction, IRP_MN_MDL))
|
|
{
|
|
PVOID Buffer;
|
|
|
|
Buffer = 0 == Irp->MdlAddress ?
|
|
Irp->UserBuffer : MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
|
|
if (0 == Buffer)
|
|
{
|
|
Result = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
Result = FspCcCopyRead(FileObject, &ReadOffset, ReadLength, CanWait, Buffer,
|
|
&Irp->IoStatus);
|
|
if (!NT_SUCCESS(Result) || STATUS_PENDING == Result)
|
|
goto cleanup;
|
|
}
|
|
else
|
|
{
|
|
Result = FspCcMdlRead(FileObject, &ReadOffset, ReadLength, &Irp->MdlAddress,
|
|
&Irp->IoStatus);
|
|
if (!NT_SUCCESS(Result))
|
|
goto cleanup;
|
|
ASSERT(STATUS_PENDING != Result);
|
|
}
|
|
|
|
/* update the current file offset if synchronous I/O */
|
|
if (SynchronousIo)
|
|
FileObject->CurrentByteOffset.QuadPart = ReadOffset.QuadPart + Irp->IoStatus.Information;
|
|
|
|
FspFileNodeRelease(FileNode, Main);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
cleanup:
|
|
FspFileNodeRelease(FileNode, Main);
|
|
|
|
if (STATUS_PENDING == Result)
|
|
return FspWqRepostIrpWorkItem(Irp, FspFsvolReadCached, 0);
|
|
|
|
return Result;
|
|
}
|
|
|
|
static NTSTATUS FspFsvolReadNonCached(
|
|
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
|
|
BOOLEAN CanWait)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
/* assert: either a top-level IRP or Paging I/O */
|
|
ASSERT(0 == FspIrpTopFlags(Irp) || FlagOn(Irp->Flags, IRP_PAGING_IO));
|
|
|
|
NTSTATUS Result;
|
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
|
FSP_FILE_NODE *FileNode = FileObject->FsContext;
|
|
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
|
|
LARGE_INTEGER ReadOffset = IrpSp->Parameters.Read.ByteOffset;
|
|
ULONG ReadLength = IrpSp->Parameters.Read.Length;
|
|
ULONG ReadKey = IrpSp->Parameters.Read.Key;
|
|
BOOLEAN PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO);
|
|
FSP_FSCTL_FILE_INFO FileInfo;
|
|
FSP_FSCTL_TRANSACT_REQ *Request;
|
|
BOOLEAN Success;
|
|
|
|
ASSERT(FileNode == FileDesc->FileNode);
|
|
|
|
/* no MDL requests on the non-cached path */
|
|
if (FlagOn(IrpSp->MinorFunction, IRP_MN_MDL))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* probe and lock the user buffer */
|
|
Result = FspLockUserBuffer(Irp, ReadLength, IoWriteAccess);
|
|
if (!NT_SUCCESS(Result))
|
|
return Result;
|
|
|
|
/* acquire FileNode shared Full */
|
|
Success = DEBUGTEST(90) &&
|
|
FspFileNodeTryAcquireSharedF(FileNode, FspFileNodeAcquireFull, CanWait);
|
|
if (!Success)
|
|
return FspWqRepostIrpWorkItem(Irp, FspFsvolReadNonCached, 0);
|
|
|
|
/* perform oplock check */
|
|
if (!PagingIo)
|
|
{
|
|
Result = FspFileNodeOplockCheckAsync(
|
|
FileNode, FspFileNodeAcquireFull, FspFsvolReadNonCached,
|
|
Irp);
|
|
if (STATUS_PENDING == Result)
|
|
return Result;
|
|
if (!NT_SUCCESS(Result))
|
|
{
|
|
FspFileNodeRelease(FileNode, Full);
|
|
return Result;
|
|
}
|
|
}
|
|
|
|
/* check the file locks */
|
|
if (!PagingIo && !FsRtlCheckLockForReadAccess(&FileNode->FileLock, Irp))
|
|
{
|
|
FspFileNodeRelease(FileNode, Full);
|
|
return STATUS_FILE_LOCK_CONFLICT;
|
|
}
|
|
|
|
/* if this is a non-cached transfer on a cached file then flush the file */
|
|
if (!PagingIo && 0 != FileObject->SectionObjectPointer->DataSectionObject)
|
|
{
|
|
FspFileNodeRelease(FileNode, Full);
|
|
if (!CanWait)
|
|
return FspWqRepostIrpWorkItem(Irp, FspFsvolReadNonCached, 0);
|
|
|
|
/* need to acquire exclusive for flushing */
|
|
FspFileNodeAcquireExclusive(FileNode, Full);
|
|
Result = FspFileNodeFlushAndPurgeCache(FileNode,
|
|
IrpSp->Parameters.Read.ByteOffset.QuadPart,
|
|
IrpSp->Parameters.Read.Length,
|
|
FALSE);
|
|
FspFileNodeRelease(FileNode, Full);
|
|
if (!NT_SUCCESS(Result))
|
|
return Result;
|
|
|
|
FspFileNodeAcquireShared(FileNode, Full);
|
|
}
|
|
|
|
/* trim ReadLength during CreateProcess; resolve bugcheck for filesystem that reports incorrect size */
|
|
if (FileNode->Tls.CreateSection)
|
|
{
|
|
FspFileNodeGetFileInfo(FileNode, &FileInfo);
|
|
if ((UINT64)ReadOffset.QuadPart >= FileInfo.FileSize)
|
|
{
|
|
FspFileNodeRelease(FileNode, Full);
|
|
return STATUS_END_OF_FILE;
|
|
}
|
|
if ((UINT64)ReadLength > FileInfo.FileSize - ReadOffset.QuadPart)
|
|
ReadLength = (ULONG)(FileInfo.FileSize - ReadOffset.QuadPart);
|
|
}
|
|
|
|
Request = FspIrpRequest(Irp);
|
|
if (0 == Request)
|
|
{
|
|
/* create request */
|
|
Result = FspIopCreateRequestEx(Irp, 0, 0, FspFsvolReadNonCachedRequestFini, &Request);
|
|
if (!NT_SUCCESS(Result))
|
|
{
|
|
FspFileNodeRelease(FileNode, Full);
|
|
return Result;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* reuse existing request */
|
|
ASSERT(Request->Size == sizeof *Request);
|
|
ASSERT(Request->Hint == (UINT_PTR)Irp);
|
|
FspIopResetRequest(Request, FspFsvolReadNonCachedRequestFini);
|
|
RtlZeroMemory(&Request->Req,
|
|
sizeof *Request - FIELD_OFFSET(FSP_FSCTL_TRANSACT_REQ, Req));
|
|
}
|
|
|
|
Request->Kind = FspFsctlTransactReadKind;
|
|
Request->Req.Read.UserContext = FileNode->UserContext;
|
|
Request->Req.Read.UserContext2 = FileDesc->UserContext2;
|
|
Request->Req.Read.Offset = ReadOffset.QuadPart;
|
|
Request->Req.Read.Length = ReadLength;
|
|
Request->Req.Read.Key = ReadKey;
|
|
|
|
FspFileNodeSetOwner(FileNode, Full, Request);
|
|
FspIopRequestContext(Request, RequestIrp) = Irp;
|
|
|
|
FSP_STATISTICS *Statistics = FspFsvolDeviceStatistics(FsvolDeviceObject);
|
|
if (PagingIo)
|
|
{
|
|
FspStatisticsInc(Statistics, Base.UserFileReads);
|
|
FspStatisticsAdd(Statistics, Base.UserFileReadBytes, ReadLength);
|
|
FspStatisticsInc(Statistics, Base.UserDiskReads);
|
|
}
|
|
else
|
|
{
|
|
FspStatisticsInc(Statistics, Specific.NonCachedReads);
|
|
FspStatisticsAdd(Statistics, Specific.NonCachedReadBytes, ReadLength);
|
|
FspStatisticsInc(Statistics, Specific.NonCachedDiskReads);
|
|
}
|
|
|
|
return FSP_STATUS_IOQ_POST;
|
|
}
|
|
|
|
NTSTATUS FspFsvolReadPrepare(
|
|
PIRP Irp, FSP_FSCTL_TRANSACT_REQ *Request)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (FspReadIrpShouldUseProcessBuffer(Irp, Request->Req.Read.Length))
|
|
{
|
|
NTSTATUS Result;
|
|
PVOID Cookie;
|
|
PVOID Address;
|
|
PEPROCESS Process;
|
|
|
|
if (0 == MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority))
|
|
return STATUS_INSUFFICIENT_RESOURCES; /* something is seriously screwy! */
|
|
|
|
Result = FspProcessBufferAcquire(Request->Req.Read.Length, &Cookie, &Address);
|
|
if (!NT_SUCCESS(Result))
|
|
return Result;
|
|
|
|
/* get a pointer to the current process so that we can release the buffer later */
|
|
Process = PsGetCurrentProcess();
|
|
ObReferenceObject(Process);
|
|
|
|
Request->Req.Read.Address = (UINT64)(UINT_PTR)Address;
|
|
|
|
FspIopRequestContext(Request, RequestCookie) = (PVOID)((UINT_PTR)Cookie | 1);
|
|
FspIopRequestContext(Request, RequestAddress) = Address;
|
|
FspIopRequestContext(Request, RequestProcess) = Process;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
NTSTATUS Result;
|
|
FSP_SAFE_MDL *SafeMdl = 0;
|
|
PVOID Address;
|
|
PEPROCESS Process;
|
|
|
|
/* create a "safe" MDL if necessary */
|
|
if (!FspSafeMdlCheck(Irp->MdlAddress))
|
|
{
|
|
Result = FspSafeMdlCreate(Irp->MdlAddress, IoWriteAccess, &SafeMdl);
|
|
if (!NT_SUCCESS(Result))
|
|
return Result;
|
|
}
|
|
|
|
/* map the MDL into user-mode */
|
|
Result = FspMapLockedPagesInUserMode(
|
|
0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress, &Address, 0);
|
|
if (!NT_SUCCESS(Result))
|
|
{
|
|
if (0 != SafeMdl)
|
|
FspSafeMdlDelete(SafeMdl);
|
|
|
|
return Result;
|
|
}
|
|
|
|
/* get a pointer to the current process so that we can unmap the address later */
|
|
Process = PsGetCurrentProcess();
|
|
ObReferenceObject(Process);
|
|
|
|
Request->Req.Read.Address = (UINT64)(UINT_PTR)Address;
|
|
|
|
FspIopRequestContext(Request, RequestSafeMdl) = SafeMdl;
|
|
FspIopRequestContext(Request, RequestAddress) = Address;
|
|
FspIopRequestContext(Request, RequestProcess) = Process;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
NTSTATUS FspFsvolReadComplete(
|
|
PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response)
|
|
{
|
|
FSP_ENTER_IOC(PAGED_CODE());
|
|
|
|
if (!NT_SUCCESS(Response->IoStatus.Status))
|
|
{
|
|
Irp->IoStatus.Information = 0;
|
|
Result = Response->IoStatus.Status;
|
|
FSP_RETURN();
|
|
}
|
|
|
|
FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
|
|
|
|
if (Response->IoStatus.Information > Request->Req.Read.Length)
|
|
FSP_RETURN(Result = STATUS_INTERNAL_ERROR);
|
|
|
|
if ((UINT_PTR)FspIopRequestContext(Request, RequestCookie) & 1)
|
|
{
|
|
PVOID Address = FspIopRequestContext(Request, RequestAddress);
|
|
PVOID SystemAddress = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
|
|
|
|
ASSERT(0 != Address);
|
|
try
|
|
{
|
|
RtlCopyMemory(SystemAddress, Address, Response->IoStatus.Information);
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Result = GetExceptionCode();
|
|
Result = FsRtlIsNtstatusExpected(Result) ? STATUS_INVALID_USER_BUFFER : Result;
|
|
FSP_RETURN();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FSP_SAFE_MDL *SafeMdl = FspIopRequestContext(Request, RequestSafeMdl);
|
|
|
|
if (0 != SafeMdl)
|
|
FspSafeMdlCopyBack(SafeMdl);
|
|
}
|
|
|
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
|
LARGE_INTEGER ReadOffset = IrpSp->Parameters.Read.ByteOffset;
|
|
BOOLEAN PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO);
|
|
BOOLEAN SynchronousIo = BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO);
|
|
|
|
/* if we are top-level */
|
|
if (0 == FspIrpTopFlags(Irp))
|
|
{
|
|
/* update the current file offset if synchronous I/O (and not paging I/O) */
|
|
if (SynchronousIo && !PagingIo)
|
|
FileObject->CurrentByteOffset.QuadPart =
|
|
ReadOffset.QuadPart + Response->IoStatus.Information;
|
|
|
|
FspIopResetRequest(Request, 0);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(PagingIo);
|
|
FspIopResetRequest(Request, 0);
|
|
}
|
|
|
|
Irp->IoStatus.Information = Response->IoStatus.Information;
|
|
Result = STATUS_SUCCESS;
|
|
|
|
FSP_LEAVE_IOC(
|
|
"FileObject=%p, UserBuffer=%p, MdlAddress=%p, "
|
|
"Key=%#lx, ByteOffset=%#lx:%#lx, Length=%ld",
|
|
IrpSp->FileObject, Irp->UserBuffer, Irp->MdlAddress,
|
|
IrpSp->Parameters.Read.Key,
|
|
IrpSp->Parameters.Read.ByteOffset.HighPart, IrpSp->Parameters.Read.ByteOffset.LowPart,
|
|
IrpSp->Parameters.Read.Length);
|
|
}
|
|
|
|
static VOID FspFsvolReadNonCachedRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Context[4])
|
|
{
|
|
PAGED_CODE();
|
|
|
|
PIRP Irp = Context[RequestIrp];
|
|
|
|
if ((UINT_PTR)Context[RequestCookie] & 1)
|
|
{
|
|
PVOID Cookie = (PVOID)((UINT_PTR)Context[RequestCookie] & ~1);
|
|
PVOID Address = Context[RequestAddress];
|
|
PEPROCESS Process = Context[RequestProcess];
|
|
|
|
if (0 != Address)
|
|
{
|
|
KAPC_STATE ApcState;
|
|
BOOLEAN Attach;
|
|
|
|
ASSERT(0 != Process);
|
|
Attach = Process != PsGetCurrentProcess();
|
|
|
|
if (Attach)
|
|
KeStackAttachProcess(Process, &ApcState);
|
|
FspProcessBufferRelease(Cookie, Address);
|
|
if (Attach)
|
|
KeUnstackDetachProcess(&ApcState);
|
|
|
|
ObDereferenceObject(Process);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FSP_SAFE_MDL *SafeMdl = Context[RequestSafeMdl];
|
|
PVOID Address = Context[RequestAddress];
|
|
PEPROCESS Process = Context[RequestProcess];
|
|
|
|
if (0 != Address)
|
|
{
|
|
KAPC_STATE ApcState;
|
|
BOOLEAN Attach;
|
|
|
|
ASSERT(0 != Process);
|
|
Attach = Process != PsGetCurrentProcess();
|
|
|
|
if (Attach)
|
|
KeStackAttachProcess(Process, &ApcState);
|
|
MmUnmapLockedPages(Address, 0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress);
|
|
if (Attach)
|
|
KeUnstackDetachProcess(&ApcState);
|
|
|
|
ObDereferenceObject(Process);
|
|
}
|
|
|
|
if (0 != SafeMdl)
|
|
FspSafeMdlDelete(SafeMdl);
|
|
}
|
|
|
|
if (0 != Irp)
|
|
{
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
FSP_FILE_NODE *FileNode = IrpSp->FileObject->FsContext;
|
|
|
|
FspFileNodeReleaseOwner(FileNode, Full, Request);
|
|
}
|
|
}
|
|
|
|
NTSTATUS FspRead(
|
|
PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
|
{
|
|
FSP_ENTER_MJ(PAGED_CODE());
|
|
|
|
switch (FspDeviceExtension(DeviceObject)->Kind)
|
|
{
|
|
case FspFsvolDeviceExtensionKind:
|
|
FSP_RETURN(Result = FspFsvolRead(DeviceObject, Irp, IrpSp));
|
|
default:
|
|
FSP_RETURN(Result = STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
|
|
FSP_LEAVE_MJ(
|
|
"FileObject=%p, UserBuffer=%p, MdlAddress=%p, "
|
|
"Key=%#lx, ByteOffset=%#lx:%#lx, Length=%ld",
|
|
IrpSp->FileObject, Irp->UserBuffer, Irp->MdlAddress,
|
|
IrpSp->Parameters.Read.Key,
|
|
IrpSp->Parameters.Read.ByteOffset.HighPart, IrpSp->Parameters.Read.ByteOffset.LowPart,
|
|
IrpSp->Parameters.Read.Length);
|
|
}
|