mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
1196 lines
35 KiB
C++
1196 lines
35 KiB
C++
////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
|
|
// All rights reserved
|
|
// This file was released under the GPLv2 on June 2015.
|
|
////////////////////////////////////////////////////////////////////
|
|
/*************************************************************************
|
|
*
|
|
* File: Fastio.cpp
|
|
*
|
|
* Module: UDF File System Driver (Kernel mode execution only)
|
|
*
|
|
* Description:
|
|
* Contains code to handle the various "fast-io" calls.
|
|
*
|
|
*************************************************************************/
|
|
|
|
#include "udffs.h"
|
|
|
|
// define the file specific bug-check id
|
|
#define UDF_BUG_CHECK_ID UDF_FILE_FAST_IO
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFFastIoCheckIfPossible()
|
|
*
|
|
* Description:
|
|
* To fast-io or not to fast-io, that is the question ...
|
|
* This routine helps the I/O Manager determine whether the FSD wishes
|
|
* to permit fast-io on a specific file stream.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: TRUE/FALSE
|
|
*
|
|
*************************************************************************/
|
|
BOOLEAN
|
|
NTAPI
|
|
UDFFastIoCheckIfPossible(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN BOOLEAN Wait,
|
|
IN ULONG LockKey,
|
|
IN BOOLEAN CheckForReadOperation,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
BOOLEAN ReturnedStatus = FALSE;
|
|
PtrUDFFCB Fcb = NULL;
|
|
PtrUDFCCB Ccb = NULL;
|
|
LARGE_INTEGER IoLength;
|
|
|
|
// Obtain a pointer to the FCB and CCB for the file stream.
|
|
Ccb = (PtrUDFCCB)(FileObject->FsContext2);
|
|
ASSERT(Ccb);
|
|
Fcb = Ccb->Fcb;
|
|
ASSERT(Fcb);
|
|
|
|
// Validate that this is a fast-IO request to a regular file.
|
|
// The UDF FSD for example, will not allow fast-IO requests
|
|
// to volume objects, or to directories.
|
|
if ((Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) ||
|
|
(Fcb->FCBFlags & UDF_FCB_DIRECTORY)) {
|
|
// This is not allowed.
|
|
IoStatus->Status = STATUS_INVALID_PARAMETER;
|
|
MmPrint((" UDFFastIoCheckIfPossible() TRUE, Failed\n"));
|
|
return FALSE;
|
|
}
|
|
/*
|
|
// back pressure for very smart and fast system cache ;)
|
|
if(Fcb->Vcb->VerifyCtx.ItemCount >= UDF_MAX_VERIFY_CACHE) {
|
|
AdPrint((" Verify queue overflow -> UDFFastIoCheckIfPossible() = FALSE\n"));
|
|
return FALSE;
|
|
}
|
|
*/
|
|
IoLength.QuadPart = Length;
|
|
|
|
// The FSD can determine the checks that it needs to perform.
|
|
// Typically, a FSD will check whether there exist any byte-range
|
|
// locks that would prevent a fast-IO operation from proceeding.
|
|
|
|
// ... (FSD specific checks go here).
|
|
|
|
if (CheckForReadOperation) {
|
|
// The following routine is exported by the FSRTL
|
|
// package and it returns TRUE if the read operation should be
|
|
// allowed to proceed based on the status of the current byte-range
|
|
// locks on the file stream. If we do not use the FSRTL package
|
|
// for byte-range locking support, then we must substitute our
|
|
// own checks over here.
|
|
ReturnedStatus = FsRtlFastCheckLockForRead(&(Fcb->NTRequiredFCB->FileLock),
|
|
FileOffset, &IoLength, LockKey, FileObject,
|
|
PsGetCurrentProcess());
|
|
} else {
|
|
// if(Fcb->Vcb->VCBFlags );
|
|
// This is a write request. Invoke the FSRTL byte-range lock package
|
|
// to see whether the write should be allowed to proceed.
|
|
ReturnedStatus = FsRtlFastCheckLockForWrite(&(Fcb->NTRequiredFCB->FileLock),
|
|
FileOffset, &IoLength, LockKey, FileObject,
|
|
PsGetCurrentProcess());
|
|
}
|
|
|
|
MmPrint((" UDFFastIoCheckIfPossible() %s\n", ReturnedStatus ? "TRUE" : "FALSE"));
|
|
return(ReturnedStatus);
|
|
// return FALSE;
|
|
|
|
} // end UDFFastIoCheckIfPossible()
|
|
|
|
/*
|
|
*/
|
|
FAST_IO_POSSIBLE
|
|
NTAPI
|
|
UDFIsFastIoPossible(
|
|
IN PtrUDFFCB Fcb
|
|
)
|
|
{
|
|
if( !(Fcb->Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) /*||
|
|
!FsRtlOplockIsFastIoPossible(&(Fcb->Oplock))*/ ) {
|
|
UDFPrint((" FastIoIsNotPossible\n"));
|
|
return FastIoIsNotPossible;
|
|
}
|
|
/*
|
|
// back pressure for very smart and fast system cache ;)
|
|
if(Fcb->Vcb->VerifyCtx.ItemCount >= UDF_MAX_VERIFY_CACHE) {
|
|
AdPrint((" Verify queue overflow -> UDFIsFastIoPossible() = FastIoIsNotPossible\n"));
|
|
return FastIoIsNotPossible;
|
|
}
|
|
*/
|
|
if(FsRtlAreThereCurrentFileLocks(&(Fcb->NTRequiredFCB->FileLock)) ) {
|
|
UDFPrint((" FastIoIsQuestionable\n"));
|
|
return FastIoIsQuestionable;
|
|
}
|
|
UDFPrint((" FastIoIsPossible\n"));
|
|
return FastIoIsPossible;
|
|
} // end UDFIsFastIoPossible()
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFFastIoQueryBasicInfo()
|
|
*
|
|
* Description:
|
|
* Bypass the traditional IRP method to perform a query basic
|
|
* information operation.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: TRUE/FALSE
|
|
*
|
|
*************************************************************************/
|
|
BOOLEAN
|
|
NTAPI
|
|
UDFFastIoQueryBasicInfo(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN BOOLEAN Wait,
|
|
OUT PFILE_BASIC_INFORMATION Buffer,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
BOOLEAN ReturnedStatus = FALSE; // fast i/o failed/not allowed
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
PtrUDFIrpContext PtrIrpContext = NULL;
|
|
LONG Length = sizeof(FILE_BASIC_INFORMATION);
|
|
PtrUDFFCB Fcb;
|
|
PtrUDFCCB Ccb;
|
|
PtrUDFNTRequiredFCB NtReqFcb = NULL;
|
|
BOOLEAN MainResourceAcquired = FALSE;
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
UDFPrint(("UDFFastIo \n"));
|
|
// if the file is already opended we can satisfy this request
|
|
// immediately 'cause all the data we need must be cached
|
|
_SEH2_TRY {
|
|
|
|
_SEH2_TRY {
|
|
|
|
// Get the FCB and CCB pointers.
|
|
Ccb = (PtrUDFCCB)(FileObject->FsContext2);
|
|
ASSERT(Ccb);
|
|
Fcb = Ccb->Fcb;
|
|
ASSERT(Fcb);
|
|
NtReqFcb = Fcb->NTRequiredFCB;
|
|
//Fcb->Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
|
|
|
|
if (!(Fcb->FCBFlags & UDF_FCB_PAGE_FILE)) {
|
|
// Acquire the MainResource shared.
|
|
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
|
|
if (!UDFAcquireResourceShared(&(NtReqFcb->MainResource), Wait)) {
|
|
try_return(RC = STATUS_CANT_WAIT);
|
|
}
|
|
MainResourceAcquired = TRUE;
|
|
}
|
|
|
|
ReturnedStatus =
|
|
((RC = UDFGetBasicInformation(FileObject, Fcb, Buffer, &Length)) == STATUS_SUCCESS);
|
|
|
|
} _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) {
|
|
|
|
RC = UDFExceptionHandler(PtrIrpContext, NULL);
|
|
|
|
UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
|
|
|
|
} _SEH2_END;
|
|
try_exit: NOTHING;
|
|
} _SEH2_FINALLY {
|
|
if (MainResourceAcquired) {
|
|
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
|
|
UDFReleaseResource(&(NtReqFcb->MainResource));
|
|
MainResourceAcquired = FALSE;
|
|
}
|
|
IoStatus->Status = RC;
|
|
if(ReturnedStatus) {
|
|
IoStatus->Information = sizeof(FILE_BASIC_INFORMATION);
|
|
} else {
|
|
IoStatus->Information = 0;
|
|
}
|
|
} _SEH2_END;
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return(ReturnedStatus);
|
|
} // end UDFFastIoQueryBasicInfo()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFFastIoQueryStdInfo()
|
|
*
|
|
* Description:
|
|
* Bypass the traditional IRP method to perform a query standard
|
|
* information operation.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: TRUE/FALSE
|
|
*
|
|
*************************************************************************/
|
|
BOOLEAN
|
|
NTAPI
|
|
UDFFastIoQueryStdInfo(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN BOOLEAN Wait,
|
|
OUT PFILE_STANDARD_INFORMATION Buffer,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
BOOLEAN ReturnedStatus = FALSE; // fast i/o failed/not allowed
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
PtrUDFIrpContext PtrIrpContext = NULL;
|
|
LONG Length = sizeof(FILE_STANDARD_INFORMATION);
|
|
PtrUDFFCB Fcb;
|
|
PtrUDFCCB Ccb;
|
|
// PtrUDFNTRequiredFCB NtReqFcb = NULL;
|
|
// BOOLEAN MainResourceAcquired = FALSE;
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
UDFPrint(("UDFFastIo \n"));
|
|
// if the file is already opended we can satisfy this request
|
|
// immediately 'cause all the data we need must be cached
|
|
_SEH2_TRY {
|
|
|
|
_SEH2_TRY {
|
|
|
|
// Get the FCB and CCB pointers.
|
|
Ccb = (PtrUDFCCB)(FileObject->FsContext2);
|
|
ASSERT(Ccb);
|
|
Fcb = Ccb->Fcb;
|
|
ASSERT(Fcb);
|
|
// NtReqFcb = Fcb->NTRequiredFCB;
|
|
//Fcb->Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
|
|
|
|
/*
|
|
if (!(Fcb->FCBFlags & UDF_FCB_PAGE_FILE)) {
|
|
// Acquire the MainResource shared.
|
|
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
|
|
if (!UDFAcquireResourceShared(&(NtReqFcb->MainResource), Wait)) {
|
|
try_return(RC = STATUS_CANT_WAIT);
|
|
}
|
|
MainResourceAcquired = TRUE;
|
|
}
|
|
*/
|
|
ReturnedStatus =
|
|
((RC = UDFGetStandardInformation(Fcb, Buffer, &Length)) == STATUS_SUCCESS);
|
|
|
|
} _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) {
|
|
|
|
RC = UDFExceptionHandler(PtrIrpContext, NULL);
|
|
|
|
UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
|
|
|
|
} _SEH2_END;
|
|
//try_exit: NOTHING;
|
|
} _SEH2_FINALLY {
|
|
/*
|
|
if (MainResourceAcquired) {
|
|
UDFReleaseResource(&(NtReqFcb->MainResource));
|
|
MainResourceAcquired = FALSE;
|
|
}
|
|
*/
|
|
IoStatus->Status = RC;
|
|
if(ReturnedStatus) {
|
|
IoStatus->Information = sizeof(FILE_STANDARD_INFORMATION);
|
|
} else {
|
|
IoStatus->Information = 0;
|
|
}
|
|
} _SEH2_END;
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return(ReturnedStatus);
|
|
} // end UDFFastIoQueryStdInfo()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFFastIoAcqCreateSec()
|
|
*
|
|
* Description:
|
|
* Not really a fast-io operation. Used by the VMM to acquire FSD resources
|
|
* before processing a file map (create section object) request.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: None (we must be prepared to handle VMM initiated calls)
|
|
*
|
|
*************************************************************************/
|
|
VOID
|
|
NTAPI
|
|
UDFFastIoAcqCreateSec(
|
|
IN PFILE_OBJECT FileObject
|
|
)
|
|
{
|
|
PtrUDFNTRequiredFCB NtReqFcb = (PtrUDFNTRequiredFCB)(FileObject->FsContext);
|
|
|
|
MmPrint((" AcqForCreateSection()\n"));
|
|
// Acquire the MainResource exclusively for the file stream
|
|
if(!ExIsResourceAcquiredExclusiveLite(&(NtReqFcb->MainResource)) ||
|
|
!ExIsResourceAcquiredExclusiveLite(&(NtReqFcb->PagingIoResource)) ) {
|
|
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
|
|
} else {
|
|
MmPrint((" already acquired\n"));
|
|
}
|
|
UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), TRUE);
|
|
|
|
// Although this is typically not required, the UDF FSD will
|
|
// also acquire the PagingIoResource exclusively at this time
|
|
// to conform with the resource acquisition described in the set
|
|
// file information routine. Once again though, we will probably
|
|
// not need to do this.
|
|
UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), TRUE);
|
|
NtReqFcb->AcqSectionCount++;
|
|
|
|
return;
|
|
} // end UDFFastIoAcqCreateSec()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFFastIoRelCreateSec()
|
|
*
|
|
* Description:
|
|
* Not really a fast-io operation. Used by the VMM to release FSD resources
|
|
* after processing a file map (create section object) request.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: None
|
|
*
|
|
*************************************************************************/
|
|
VOID
|
|
NTAPI
|
|
UDFFastIoRelCreateSec(
|
|
IN PFILE_OBJECT FileObject)
|
|
{
|
|
PtrUDFNTRequiredFCB NtReqFcb = (PtrUDFNTRequiredFCB)(FileObject->FsContext);
|
|
|
|
MmPrint((" RelFromCreateSection()\n"));
|
|
|
|
NtReqFcb->AcqSectionCount--;
|
|
// Release the PagingIoResource for the file stream
|
|
UDFReleaseResource(&(NtReqFcb->PagingIoResource));
|
|
|
|
// Release the MainResource for the file stream
|
|
UDFReleaseResource(&(NtReqFcb->MainResource));
|
|
|
|
return;
|
|
} // end UDFFastIoRelCreateSec()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFAcqLazyWrite()
|
|
*
|
|
* Description:
|
|
* Not really a fast-io operation. Used by the NT Cache Mgr to acquire FSD
|
|
* resources before performing a delayed write (write behind/lazy write)
|
|
* operation.
|
|
* NOTE: this function really must succeed since the Cache Manager will
|
|
* typically ignore failure and continue on ...
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: TRUE/FALSE (Cache Manager does not tolerate FALSE well)
|
|
*
|
|
*************************************************************************/
|
|
BOOLEAN NTAPI UDFAcqLazyWrite(
|
|
IN PVOID Context,
|
|
IN BOOLEAN Wait)
|
|
{
|
|
// The context is whatever we passed to the Cache Manager when invoking
|
|
// the CcInitializeCacheMaps() function. In the case of the UDF FSD
|
|
// implementation, this context is a pointer to the NT_REQ_FCB structure.
|
|
PtrUDFNTRequiredFCB NtReqFcb = (PtrUDFNTRequiredFCB)Context;
|
|
|
|
MmPrint((" UDFAcqLazyWrite()\n"));
|
|
|
|
// Acquire the PagingIoResource in the NT_REQ_FCB exclusively. Then, set the
|
|
// lazy-writer thread id in the NT_REQ_FCB structure for identification
|
|
// when an actual write request is received by the FSD.
|
|
// Note: The lazy-writer typically always supplies WAIT set to TRUE.
|
|
if (!UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), Wait))
|
|
return FALSE;
|
|
|
|
// Now, set the lazy-writer thread id.
|
|
ASSERT(!(NtReqFcb->LazyWriterThreadID));
|
|
NtReqFcb->LazyWriterThreadID = HandleToUlong(PsGetCurrentThreadId());
|
|
|
|
ASSERT(IoGetTopLevelIrp() == NULL);
|
|
IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP);
|
|
|
|
// If our FSD needs to perform some special preparations in anticipation
|
|
// of receving a lazy-writer request, do so now.
|
|
return TRUE;
|
|
} // end UDFAcqLazyWrite()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFRelLazyWrite()
|
|
*
|
|
* Description:
|
|
* Not really a fast-io operation. Used by the NT Cache Mgr to release FSD
|
|
* resources after performing a delayed write (write behind/lazy write)
|
|
* operation.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: None
|
|
*
|
|
*************************************************************************/
|
|
VOID
|
|
NTAPI
|
|
UDFRelLazyWrite(
|
|
IN PVOID Context)
|
|
{
|
|
// The context is whatever we passed to the Cache Manager when invoking
|
|
// the CcInitializeCacheMaps() function. In the case of the UDF FSD
|
|
// implementation, this context is a pointer to the NT_REQ_FCB structure.
|
|
PtrUDFNTRequiredFCB NtReqFcb = (PtrUDFNTRequiredFCB)Context;
|
|
|
|
MmPrint((" UDFRelLazyWrite()\n"));
|
|
|
|
// Remove the current thread-id from the NT_REQ_FCB
|
|
// and release the MainResource.
|
|
ASSERT((NtReqFcb->LazyWriterThreadID) == HandleToUlong(PsGetCurrentThreadId()));
|
|
NtReqFcb->LazyWriterThreadID = 0;
|
|
|
|
// Release the acquired resource.
|
|
UDFReleaseResource(&(NtReqFcb->PagingIoResource));
|
|
|
|
IoSetTopLevelIrp( NULL );
|
|
return;
|
|
} // end UDFRelLazyWrite()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFAcqReadAhead()
|
|
*
|
|
* Description:
|
|
* Not really a fast-io operation. Used by the NT Cache Mgr to acquire FSD
|
|
* resources before performing a read-ahead operation.
|
|
* NOTE: this function really must succeed since the Cache Manager will
|
|
* typically ignore failure and continue on ...
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: TRUE/FALSE (Cache Manager does not tolerate FALSE well)
|
|
*
|
|
*************************************************************************/
|
|
BOOLEAN
|
|
NTAPI
|
|
UDFAcqReadAhead(
|
|
IN PVOID Context,
|
|
IN BOOLEAN Wait
|
|
)
|
|
{
|
|
// The context is whatever we passed to the Cache Manager when invoking
|
|
// the CcInitializeCacheMaps() function. In the case of the UDF FSD
|
|
// implementation, this context is a pointer to the NT_REQ_FCB structure.
|
|
#define NtReqFcb ((PtrUDFNTRequiredFCB)Context)
|
|
|
|
MmPrint((" AcqForReadAhead()\n"));
|
|
|
|
// Acquire the MainResource in the NT_REQ_FCB shared.
|
|
// Note: The read-ahead thread typically always supplies WAIT set to TRUE.
|
|
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
|
|
if (!UDFAcquireResourceShared(&(NtReqFcb->MainResource), Wait))
|
|
return FALSE;
|
|
|
|
ASSERT(IoGetTopLevelIrp() == NULL);
|
|
IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP);
|
|
|
|
return TRUE;
|
|
#undef NtReqFcb
|
|
|
|
} // end UDFAcqReadAhead()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFRelReadAhead()
|
|
*
|
|
* Description:
|
|
* Not really a fast-io operation. Used by the NT Cache Mgr to release FSD
|
|
* resources after performing a read-ahead operation.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: None
|
|
*
|
|
*************************************************************************/
|
|
VOID
|
|
NTAPI
|
|
UDFRelReadAhead(
|
|
IN PVOID Context)
|
|
{
|
|
// The context is whatever we passed to the Cache Manager when invoking
|
|
// the CcInitializeCacheMaps() function. In the case of the UDF FSD
|
|
// implementation, this context is a pointer to the NT_REQ_FCB structure.
|
|
#define NtReqFcb ((PtrUDFNTRequiredFCB)Context)
|
|
|
|
MmPrint((" RelFromReadAhead()\n"));
|
|
|
|
// Release the acquired resource.
|
|
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
|
|
UDFReleaseResource(&(NtReqFcb->MainResource));
|
|
|
|
// Of course, the FSD should undo whatever else seems appropriate at this
|
|
// time.
|
|
IoSetTopLevelIrp( NULL );
|
|
|
|
return;
|
|
#undef NtReqFcb
|
|
} // end UDFRelReadAhead()
|
|
|
|
/* the remaining are only valid under NT Version 4.0 and later */
|
|
#if(_WIN32_WINNT >= 0x0400)
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFFastIoQueryNetInfo()
|
|
*
|
|
* Description:
|
|
* Get information requested by a redirector across the network. This call
|
|
* will originate from the LAN Manager server.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: TRUE/FALSE
|
|
*
|
|
*************************************************************************/
|
|
BOOLEAN
|
|
NTAPI
|
|
UDFFastIoQueryNetInfo(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN BOOLEAN Wait,
|
|
OUT PFILE_NETWORK_OPEN_INFORMATION Buffer,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
BOOLEAN ReturnedStatus = FALSE; // fast i/o failed/not allowed
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
PtrUDFIrpContext PtrIrpContext = NULL;
|
|
LONG Length = sizeof(FILE_NETWORK_OPEN_INFORMATION);
|
|
PtrUDFFCB Fcb;
|
|
PtrUDFCCB Ccb;
|
|
PtrUDFNTRequiredFCB NtReqFcb = NULL;
|
|
BOOLEAN MainResourceAcquired = FALSE;
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
UDFPrint(("UDFFastIo \n"));
|
|
// if the file is already opended we can satisfy this request
|
|
// immediately 'cause all the data we need must be cached
|
|
_SEH2_TRY {
|
|
|
|
_SEH2_TRY {
|
|
|
|
// Get the FCB and CCB pointers.
|
|
Ccb = (PtrUDFCCB)(FileObject->FsContext2);
|
|
ASSERT(Ccb);
|
|
Fcb = Ccb->Fcb;
|
|
ASSERT(Fcb);
|
|
NtReqFcb = Fcb->NTRequiredFCB;
|
|
//Fcb->Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
|
|
|
|
if (!(Fcb->FCBFlags & UDF_FCB_PAGE_FILE)) {
|
|
// Acquire the MainResource shared.
|
|
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
|
|
if (!UDFAcquireResourceShared(&(NtReqFcb->MainResource), Wait)) {
|
|
try_return(RC = STATUS_CANT_WAIT);
|
|
}
|
|
MainResourceAcquired = TRUE;
|
|
}
|
|
|
|
ReturnedStatus =
|
|
((RC = UDFGetNetworkInformation(Fcb, Buffer, &Length)) == STATUS_SUCCESS);
|
|
|
|
} _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) {
|
|
|
|
RC = UDFExceptionHandler(PtrIrpContext, NULL);
|
|
|
|
UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
|
|
|
|
} _SEH2_END;
|
|
try_exit: NOTHING;
|
|
} _SEH2_FINALLY {
|
|
if (MainResourceAcquired) {
|
|
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
|
|
UDFReleaseResource(&(NtReqFcb->MainResource));
|
|
MainResourceAcquired = FALSE;
|
|
}
|
|
IoStatus->Status = RC;
|
|
if(ReturnedStatus) {
|
|
IoStatus->Information = sizeof(FILE_NETWORK_OPEN_INFORMATION);
|
|
} else {
|
|
IoStatus->Information = 0;
|
|
}
|
|
} _SEH2_END;
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return(ReturnedStatus);
|
|
|
|
} // end UDFFastIoQueryNetInfo()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFFastIoMdlRead()
|
|
*
|
|
* Description:
|
|
* Bypass the traditional IRP method to perform a MDL read operation.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: TRUE/FALSE
|
|
*
|
|
*************************************************************************/
|
|
/*BOOLEAN UDFFastIoMdlRead(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN ULONG LockKey,
|
|
OUT PMDL* MdlChain,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
BOOLEAN ReturnedStatus = FALSE; // fast i/o failed/not allowed
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
PtrUDFIrpContext PtrIrpContext = NULL;
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
_SEH2_TRY {
|
|
|
|
_SEH2_TRY {
|
|
|
|
// See description in UDFFastIoRead() before filling-in the
|
|
// stub here.
|
|
NOTHING;
|
|
|
|
|
|
} __except (UDFExceptionFilter(PtrIrpContext, GetExceptionInformation())) {
|
|
|
|
RC = UDFExceptionHandler(PtrIrpContext, NULL);
|
|
|
|
UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
|
|
|
|
}
|
|
|
|
//try_exit: NOTHING;
|
|
|
|
} _SEH2_FINALLY {
|
|
|
|
}
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return(ReturnedStatus);
|
|
}*/
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFFastIoMdlReadComplete()
|
|
*
|
|
* Description:
|
|
* Bypass the traditional IRP method to inform the NT Cache Manager and the
|
|
* FSD that the caller no longer requires the data locked in the system cache
|
|
* or the MDL to stay around anymore ..
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: TRUE/FALSE
|
|
*
|
|
*************************************************************************/
|
|
/*BOOLEAN UDFFastIoMdlReadComplete(
|
|
IN PFILE_OBJECT FileObject,
|
|
OUT PMDL MdlChain,
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
BOOLEAN ReturnedStatus = FALSE; // fast i/o failed/not allowed
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
PtrUDFIrpContext PtrIrpContext = NULL;
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
_SEH2_TRY {
|
|
|
|
_SEH2_TRY {
|
|
|
|
// See description in UDFFastIoRead() before filling-in the
|
|
// stub here.
|
|
NOTHING;
|
|
|
|
} __except (UDFExceptionFilter(PtrIrpContext, GetExceptionInformation())) {
|
|
|
|
RC = UDFExceptionHandler(PtrIrpContext, NULL);
|
|
|
|
UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
|
|
|
|
}
|
|
|
|
//try_exit: NOTHING;
|
|
|
|
} _SEH2_FINALLY {
|
|
|
|
}
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return(ReturnedStatus);
|
|
}*/
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFFastIoPrepareMdlWrite()
|
|
*
|
|
* Description:
|
|
* Bypass the traditional IRP method to prepare for a MDL write operation.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: TRUE/FALSE
|
|
*
|
|
*************************************************************************/
|
|
/*BOOLEAN
|
|
UDFFastIoPrepareMdlWrite(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN ULONG LockKey,
|
|
OUT PMDL *MdlChain,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
BOOLEAN ReturnedStatus = FALSE; // fast i/o failed/not allowed
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
PtrUDFIrpContext PtrIrpContext = NULL;
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
_SEH2_TRY {
|
|
|
|
_SEH2_TRY {
|
|
|
|
// See description in UDFFastIoRead() before filling-in the
|
|
// stub here.
|
|
NOTHING;
|
|
|
|
} __except (UDFExceptionFilter(PtrIrpContext, GetExceptionInformation())) {
|
|
|
|
RC = UDFExceptionHandler(PtrIrpContext, NULL);
|
|
|
|
UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
|
|
|
|
}
|
|
|
|
//try_exit: NOTHING;
|
|
|
|
} _SEH2_FINALLY {
|
|
|
|
}
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return(ReturnedStatus);
|
|
}*/
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFFastIoMdlWriteComplete()
|
|
*
|
|
* Description:
|
|
* Bypass the traditional IRP method to inform the NT Cache Manager and the
|
|
* FSD that the caller has updated the contents of the MDL. This data can
|
|
* now be asynchronously written out to secondary storage by the Cache Mgr.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: TRUE/FALSE
|
|
*
|
|
*************************************************************************/
|
|
/*BOOLEAN UDFFastIoMdlWriteComplete(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
OUT PMDL MdlChain,
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
BOOLEAN ReturnedStatus = FALSE; // fast i/o failed/not allowed
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
PtrUDFIrpContext PtrIrpContext = NULL;
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
_SEH2_TRY {
|
|
|
|
_SEH2_TRY {
|
|
|
|
// See description in UDFFastIoRead() before filling-in the
|
|
// stub here.
|
|
NOTHING;
|
|
|
|
} __except (UDFExceptionFilter(PtrIrpContext, GetExceptionInformation())) {
|
|
|
|
RC = UDFExceptionHandler(PtrIrpContext, NULL);
|
|
|
|
UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
|
|
|
|
}
|
|
|
|
//try_exit: NOTHING;
|
|
|
|
} _SEH2_FINALLY {
|
|
|
|
}
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return(ReturnedStatus);
|
|
}*/
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFFastIoAcqModWrite()
|
|
*
|
|
* Description:
|
|
* Not really a fast-io operation. Used by the VMM to acquire FSD resources
|
|
* before initiating a write operation via the Modified Page/Block Writer.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: STATUS_SUCCESS/Error (__try not to return an error, will 'ya ? :-)
|
|
*
|
|
*************************************************************************/
|
|
NTSTATUS
|
|
NTAPI
|
|
UDFFastIoAcqModWrite(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER EndingOffset,
|
|
OUT PERESOURCE *ResourceToRelease,
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
MmPrint((" AcqModW %I64x\n", EndingOffset->QuadPart));
|
|
|
|
#define NtReqFcb ((PtrUDFNTRequiredFCB)(FileObject->FsContext))
|
|
|
|
// We must determine which resource(s) we would like to
|
|
// acquire at this time. We know that a write is imminent;
|
|
// we will probably therefore acquire appropriate resources
|
|
// exclusively.
|
|
|
|
// We must first get the FCB and CCB pointers from the file object
|
|
// that is passed in to this function (as an argument). Note that
|
|
// the ending offset (when examined in conjunction with current valid data
|
|
// length) may help us in determining the appropriate resource(s) to acquire.
|
|
|
|
// For example, if the ending offset is beyond current valid data length,
|
|
// We may decide to acquire *both* the MainResource and the PagingIoResource
|
|
// exclusively; otherwise, we may decide simply to acquire the PagingIoResource.
|
|
|
|
// Consult the text for more information on synchronization in FSDs.
|
|
|
|
// One final note; the VMM expects that we will return a pointer to
|
|
// the resource that we acquired (single return value). This pointer
|
|
// will be returned back to we in the release call (below).
|
|
|
|
if(UDFAcquireResourceShared(&(NtReqFcb->PagingIoResource), FALSE)) {
|
|
if(EndingOffset->QuadPart <= NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart) {
|
|
UDFReleaseResource(&(NtReqFcb->PagingIoResource));
|
|
RC = STATUS_CANT_WAIT;
|
|
} else {
|
|
NtReqFcb->AcqFlushCount++;
|
|
(*ResourceToRelease) = &(NtReqFcb->PagingIoResource);
|
|
MmPrint((" AcqModW OK\n"));
|
|
}
|
|
} else {
|
|
RC = STATUS_CANT_WAIT;
|
|
}
|
|
|
|
#undef NtReqFcb
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return RC;
|
|
} // end UDFFastIoAcqModWrite()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFFastIoRelModWrite()
|
|
*
|
|
* Description:
|
|
* Not really a fast-io operation. Used by the VMM to release FSD resources
|
|
* after processing a modified page/block write operation.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: STATUS_SUCCESS/Error (an error returned here is really not expected!)
|
|
*
|
|
*************************************************************************/
|
|
NTSTATUS
|
|
NTAPI
|
|
UDFFastIoRelModWrite(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PERESOURCE ResourceToRelease,
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
FsRtlEnterFileSystem();
|
|
|
|
MmPrint((" RelModW\n"));
|
|
|
|
#define NtReqFcb ((PtrUDFNTRequiredFCB)(FileObject->FsContext))
|
|
|
|
// The MPW has complete the write for modified pages and therefore
|
|
// wants us to release pre-acquired resource(s).
|
|
|
|
// We must undo here whatever it is that we did in the
|
|
// UDFFastIoAcqModWrite() call above.
|
|
|
|
NtReqFcb->AcqFlushCount--;
|
|
ASSERT(ResourceToRelease == &(NtReqFcb->PagingIoResource));
|
|
UDFReleaseResource(ResourceToRelease);
|
|
|
|
#undef NtReqFcb
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return(STATUS_SUCCESS);
|
|
} // end UDFFastIoRelModWrite()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFFastIoAcqCcFlush()
|
|
*
|
|
* Description:
|
|
* Not really a fast-io operation. Used by the NT Cache Mgr to acquire FSD
|
|
* resources before performing a CcFlush() operation on a specific file
|
|
* stream.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: STATUS_SUCCESS/Error
|
|
*
|
|
*************************************************************************/
|
|
NTSTATUS
|
|
NTAPI
|
|
UDFFastIoAcqCcFlush(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
// NTSTATUS RC = STATUS_SUCCESS;
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
MmPrint((" AcqCcFlush\n"));
|
|
|
|
// Acquire appropriate resources that will allow correct synchronization
|
|
// with a flush call (and avoid deadlock).
|
|
|
|
#define NtReqFcb ((PtrUDFNTRequiredFCB)(FileObject->FsContext))
|
|
|
|
// UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), TRUE);
|
|
UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), TRUE);
|
|
// ASSERT(!(NtReqFcb->AcqFlushCount));
|
|
NtReqFcb->AcqFlushCount++;
|
|
|
|
#undef NtReqFcb
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // end UDFFastIoAcqCcFlush()
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFFastIoRelCcFlush()
|
|
*
|
|
* Description:
|
|
* Not really a fast-io operation. Used by the NT Cache Mgr to acquire FSD
|
|
* resources before performing a CcFlush() operation on a specific file
|
|
* stream.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: STATUS_SUCCESS/Error
|
|
*
|
|
*************************************************************************/
|
|
NTSTATUS
|
|
NTAPI
|
|
UDFFastIoRelCcFlush(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
// NTSTATUS RC = STATUS_SUCCESS;
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
MmPrint((" RelCcFlush\n"));
|
|
|
|
#define NtReqFcb ((PtrUDFNTRequiredFCB)(FileObject->FsContext))
|
|
|
|
// Release resources acquired in UDFFastIoAcqCcFlush() above.
|
|
|
|
NtReqFcb->AcqFlushCount--;
|
|
UDFReleaseResource(&(NtReqFcb->PagingIoResource));
|
|
// UDFReleaseResource(&(NtReqFcb->MainResource));
|
|
|
|
#undef NtReqFcb
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // end UDFFastIoRelCcFlush()
|
|
|
|
|
|
/*BOOLEAN
|
|
UDFFastIoDeviceControl (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN BOOLEAN Wait,
|
|
IN PVOID InputBuffer OPTIONAL,
|
|
IN ULONG InputBufferLength,
|
|
OUT PVOID OutputBuffer OPTIONAL,
|
|
IN ULONG OutputBufferLength,
|
|
IN ULONG IoControlCode,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
switch(IoControlCode) {
|
|
case FSCTL_ALLOW_EXTENDED_DASD_IO: {
|
|
IoStatus->Information = 0;
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
case FSCTL_IS_VOLUME_MOUNTED: {
|
|
PtrUDFFCB Fcb;
|
|
PtrUDFCCB Ccb;
|
|
|
|
Ccb = (PtrUDFCCB)(FileObject->FsContext2);
|
|
Fcb = Ccb->Fcb;
|
|
|
|
if(Fcb &&
|
|
!(Fcb->Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) &&
|
|
!(Fcb->Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_LOCKED) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
IoStatus->Information = 0;
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}*/
|
|
|
|
#endif //_WIN32_WINNT >= 0x0400
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
UDFFastIoCopyWrite (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN BOOLEAN Wait,
|
|
IN ULONG LockKey,
|
|
IN PVOID Buffer,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
PtrUDFFCB Fcb = NULL;
|
|
PtrUDFCCB Ccb = NULL;
|
|
|
|
// Obtain a pointer to the FCB and CCB for the file stream.
|
|
Ccb = (PtrUDFCCB)(FileObject->FsContext2);
|
|
ASSERT(Ccb);
|
|
Fcb = Ccb->Fcb;
|
|
ASSERT(Fcb);
|
|
|
|
// back pressure for very smart and fast system cache ;)
|
|
if(Fcb->Vcb->VerifyCtx.QueuedCount ||
|
|
Fcb->Vcb->VerifyCtx.ItemCount >= UDF_MAX_VERIFY_CACHE) {
|
|
AdPrint((" Verify queue overflow -> UDFFastIoCopyWrite() = FALSE\n"));
|
|
return FALSE;
|
|
}
|
|
if(Fcb->NTRequiredFCB->SectionObject.DataSectionObject &&
|
|
Length >= 0x10000 &&
|
|
FileOffset->LowPart &&
|
|
!(FileOffset->LowPart & 0x00ffffff)) {
|
|
|
|
MmPrint((" no FastIo 16Mb\n"));
|
|
return FALSE;
|
|
}
|
|
return FsRtlCopyWrite(FileObject, FileOffset, Length, Wait, LockKey, Buffer, IoStatus, DeviceObject);
|
|
|
|
} // end UDFFastIoCopyWrite()
|