reactos/sdk/lib/drivers/copysup/copysup.c

692 lines
22 KiB
C

/*
* ReactOS kernel
* Copyright (C) 2017 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: sdk/lib/drivers/copysup/copysup.c
* PURPOSE: CopySup library
* PROGRAMMER: Pierre Schweitzer (pierre@reactos.org)
*/
/* INCLUDES *****************************************************************/
#include "copysup.h"
#include <pseh/pseh2.h>
#define NDEBUG
#include <debug.h>
/* FUNCTIONS ****************************************************************/
/*
* @implemented
*/
BOOLEAN
FsRtlCopyRead2(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN BOOLEAN Wait,
IN ULONG LockKey,
OUT PVOID Buffer,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject,
IN PVOID TopLevelContext)
{
BOOLEAN Ret;
ULONG PageCount;
LARGE_INTEGER FinalOffset;
PFSRTL_COMMON_FCB_HEADER Fcb;
PFAST_IO_DISPATCH FastIoDispatch;
PDEVICE_OBJECT RelatedDeviceObject;
PAGED_CODE();
Ret = TRUE;
PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(FileOffset, Length);
/* Null-length read is always OK */
if (Length == 0)
{
IoStatus->Information = 0;
IoStatus->Status = STATUS_SUCCESS;
return TRUE;
}
/* Check we don't overflow */
FinalOffset.QuadPart = FileOffset->QuadPart + Length;
if (FinalOffset.QuadPart <= 0)
{
return FALSE;
}
/* Get the FCB (at least, its header) */
Fcb = FileObject->FsContext;
FsRtlEnterFileSystem();
/* Acquire its resource (shared) */
if (Wait)
{
ExAcquireResourceSharedLite(Fcb->Resource, TRUE);
}
else
{
if (!ExAcquireResourceSharedLite(Fcb->Resource, FALSE))
{
Ret = FALSE;
goto CriticalSection;
}
}
/* If cache wasn't initialized, or FastIO isn't possible, fail */
if (FileObject->PrivateCacheMap == NULL || Fcb->IsFastIoPossible == FastIoIsNotPossible)
{
Ret = FALSE;
goto Resource;
}
/* If FastIO is questionable, then, question! */
if (Fcb->IsFastIoPossible == FastIoIsQuestionable)
{
RelatedDeviceObject = IoGetRelatedDeviceObject(FileObject);
FastIoDispatch = RelatedDeviceObject->DriverObject->FastIoDispatch;
ASSERT(FastIoDispatch != NULL);
ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL);
/* If it's not possible, then fail */
if (!FastIoDispatch->FastIoCheckIfPossible(FileObject, FileOffset, Length,
Wait, LockKey, TRUE, IoStatus, RelatedDeviceObject))
{
Ret = FALSE;
goto Resource;
}
}
/* If we get beyond file end... */
if (FinalOffset.QuadPart > Fcb->FileSize.QuadPart)
{
/* Fail if the offset was already beyond file end */
if (FileOffset->QuadPart >= Fcb->FileSize.QuadPart)
{
IoStatus->Information = 0;
IoStatus->Status = STATUS_END_OF_FILE;
goto Resource;
}
/* Otherwise, just fix read length */
Length = (ULONG)(Fcb->FileSize.QuadPart - FileOffset->QuadPart);
}
/* Set caller provided context as TLI */
IoSetTopLevelIrp(TopLevelContext);
_SEH2_TRY
{
/* If we cannot wait, or if file is bigger than 4GB */
if (!Wait || (FinalOffset.HighPart | Fcb->FileSize.HighPart) != 0)
{
/* Forward to Cc */
Ret = CcCopyRead(FileObject, FileOffset, Length, Wait, Buffer, IoStatus);
SetFlag(FileObject->Flags, FO_FILE_FAST_IO_READ);
/* Validate output */
ASSERT(!Ret || (IoStatus->Status == STATUS_END_OF_FILE) || (((ULONGLONG)FileOffset->QuadPart + IoStatus->Information) <= (ULONGLONG)Fcb->FileSize.QuadPart));
}
else
{
/* Forward to Cc */
CcFastCopyRead(FileObject, FileOffset->LowPart, Length, PageCount, Buffer, IoStatus);
SetFlag(FileObject->Flags, FO_FILE_FAST_IO_READ);
/* Validate output */
ASSERT((IoStatus->Status == STATUS_END_OF_FILE) || ((FileOffset->LowPart + IoStatus->Information) <= Fcb->FileSize.LowPart));
}
/* If read was successful, update the byte offset in the FO */
if (Ret)
{
FileObject->CurrentByteOffset.QuadPart = FileOffset->QuadPart + IoStatus->Information;
}
}
_SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
Ret = FALSE;
}
_SEH2_END;
/* Reset TLI */
IoSetTopLevelIrp(NULL);
Resource:
ExReleaseResourceLite(Fcb->Resource);
CriticalSection:
FsRtlExitFileSystem();
return Ret;
}
/*
* @implemented
*/
BOOLEAN
FsRtlCopyWrite2(
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,
IN PVOID TopLevelContext)
{
IO_STATUS_BLOCK LocalIoStatus;
PFSRTL_ADVANCED_FCB_HEADER Fcb;
BOOLEAN WriteToEof, AcquiredShared, FileSizeChanged, Ret;
LARGE_INTEGER WriteOffset, LastOffset, InitialFileSize, InitialValidDataLength;
PAGED_CODE();
/* First, check whether we're writing to EOF */
WriteToEof = ((FileOffset->LowPart == FILE_WRITE_TO_END_OF_FILE) &&
(FileOffset->HighPart == -1));
/* If Cc says we cannot write, fail now */
if (!CcCanIWrite(FileObject, Length, Wait, FALSE))
{
return FALSE;
}
/* Write through means no cache */
if (BooleanFlagOn(FileObject->Flags, FO_WRITE_THROUGH))
{
return FALSE;
}
/* If write is > 64Kb, don't use FastIO */
if (!CcCopyWriteWontFlush(FileObject, FileOffset, Length))
{
return FALSE;
}
/* Initialize the IO_STATUS_BLOCK */
IoStatus->Status = STATUS_SUCCESS;
IoStatus->Information = Length;
/* No length, it's already written! */
if (Length == 0)
{
return TRUE;
}
AcquiredShared = FALSE;
FileSizeChanged = FALSE;
Fcb = FileObject->FsContext;
FsRtlEnterFileSystem();
/* If we cannot wait, or deal with files bigger then 4GB */
if (!Wait || (Fcb->AllocationSize.HighPart != 0))
{
/* If we're to extend the file, then, acquire exclusively */
if (WriteToEof || FileOffset->QuadPart + Length > Fcb->ValidDataLength.QuadPart)
{
if (!ExAcquireResourceExclusiveLite(Fcb->Resource, Wait))
{
FsRtlExitFileSystem();
return FALSE;
}
}
/* Otherwise, a shared lock is enough */
else
{
if (!ExAcquireResourceSharedLite(Fcb->Resource, Wait))
{
FsRtlExitFileSystem();
return FALSE;
}
AcquiredShared = TRUE;
}
/* Get first write offset, and last */
if (WriteToEof)
{
WriteOffset.QuadPart = Fcb->FileSize.QuadPart;
LastOffset.QuadPart = WriteOffset.QuadPart + Length;
}
else
{
WriteOffset.QuadPart = FileOffset->QuadPart;
LastOffset.QuadPart = WriteOffset.QuadPart + Length;
}
/* If cache wasn't initialized, fail */
if (FileObject->PrivateCacheMap == NULL ||
Fcb->IsFastIoPossible == FastIoIsNotPossible)
{
ExReleaseResourceLite(Fcb->Resource);
FsRtlExitFileSystem();
return FALSE;
}
/* If we're to write beyond allocation size, it's no go,
* same is we create a hole bigger than 8kb
*/
if ((Fcb->ValidDataLength.QuadPart + 0x2000 <= WriteOffset.QuadPart) ||
(Length > MAXLONGLONG - WriteOffset.QuadPart) ||
(Fcb->AllocationSize.QuadPart < LastOffset.QuadPart))
{
ExReleaseResourceLite(Fcb->Resource);
FsRtlExitFileSystem();
return FALSE;
}
/* If we have to extend the VDL, shared lock isn't enough */
if (AcquiredShared && LastOffset.QuadPart > Fcb->ValidDataLength.QuadPart)
{
/* So release, and attempt to acquire exclusively */
ExReleaseResourceLite(Fcb->Resource);
if (!ExAcquireResourceExclusiveLite(Fcb->Resource, Wait))
{
FsRtlExitFileSystem();
return FALSE;
}
/* Get again EOF, in case file size changed in between */
if (WriteToEof)
{
WriteOffset.QuadPart = Fcb->FileSize.QuadPart;
LastOffset.QuadPart = WriteOffset.QuadPart + Length;
}
/* Make sure caching is still enabled */
if (FileObject->PrivateCacheMap == NULL ||
Fcb->IsFastIoPossible == FastIoIsNotPossible)
{
ExReleaseResourceLite(Fcb->Resource);
FsRtlExitFileSystem();
return FALSE;
}
/* And that we're not writting beyond allocation size */
if (Fcb->AllocationSize.QuadPart < LastOffset.QuadPart)
{
ExReleaseResourceLite(Fcb->Resource);
FsRtlExitFileSystem();
return FALSE;
}
}
/* If FastIO is questionable, then question */
if (Fcb->IsFastIoPossible == FastIoIsQuestionable)
{
PFAST_IO_DISPATCH FastIoDispatch;
PDEVICE_OBJECT RelatedDeviceObject;
RelatedDeviceObject = IoGetRelatedDeviceObject(FileObject);
FastIoDispatch = RelatedDeviceObject->DriverObject->FastIoDispatch;
ASSERT(FastIoDispatch != NULL);
ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL);
if (!FastIoDispatch->FastIoCheckIfPossible(FileObject,
&WriteOffset,
Length, Wait, LockKey,
FALSE, &LocalIoStatus,
RelatedDeviceObject))
{
ExReleaseResourceLite(Fcb->Resource);
FsRtlExitFileSystem();
return FALSE;
}
}
/* If we write beyond EOF, then, save previous sizes (in case of failure)
* and update file size, to allow writing
*/
if (LastOffset.QuadPart > Fcb->FileSize.QuadPart)
{
FileSizeChanged = TRUE;
InitialFileSize.QuadPart = Fcb->FileSize.QuadPart;
InitialValidDataLength.QuadPart = Fcb->ValidDataLength.QuadPart;
if (LastOffset.HighPart != Fcb->FileSize.HighPart &&
Fcb->PagingIoResource != NULL)
{
ExAcquireResourceExclusiveLite(Fcb->PagingIoResource, TRUE);
Fcb->FileSize.QuadPart = LastOffset.QuadPart;
ExReleaseResourceLite(Fcb->PagingIoResource);
}
else
{
Fcb->FileSize.QuadPart = LastOffset.QuadPart;
}
}
/* Set caller provided context as top level IRP */
IoSetTopLevelIrp(TopLevelContext);
Ret = TRUE;
/* And perform the writing */
_SEH2_TRY
{
/* Check whether we've to create a hole first */
if (LastOffset.QuadPart > Fcb->ValidDataLength.QuadPart)
{
Ret = CcZeroData(FileObject, &Fcb->ValidDataLength,
&WriteOffset, Wait);
}
/* If not needed, or if it worked, write data */
if (Ret)
{
Ret = CcCopyWrite(FileObject, &WriteOffset,
Length, Wait, Buffer);
}
}
_SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH)
{
Ret = FALSE;
}
_SEH2_END;
/* Restore top level IRP */
IoSetTopLevelIrp(NULL);
/* If writing succeed */
if (Ret)
{
/* If we wrote beyond VDL, update it */
if (LastOffset.QuadPart > Fcb->ValidDataLength.QuadPart)
{
if (LastOffset.HighPart != Fcb->ValidDataLength.HighPart &&
Fcb->PagingIoResource != NULL)
{
ExAcquireResourceExclusiveLite(Fcb->PagingIoResource, TRUE);
Fcb->ValidDataLength.QuadPart = LastOffset.QuadPart;
ExReleaseResourceLite(Fcb->PagingIoResource);
}
else
{
Fcb->ValidDataLength.QuadPart = LastOffset.QuadPart;
}
}
/* File was obviously modified */
SetFlag(FileObject->Flags, FO_FILE_MODIFIED);
/* And if we increased it, modify size in Cc and update FO */
if (FileSizeChanged)
{
(*CcGetFileSizePointer(FileObject)).QuadPart = LastOffset.QuadPart;
SetFlag(FileObject->Flags, FO_FILE_SIZE_CHANGED);
}
/* Update offset */
FileObject->CurrentByteOffset.QuadPart = WriteOffset.QuadPart + Length;
}
else
{
/* We failed, we need to restore previous sizes */
if (FileSizeChanged)
{
if (Fcb->PagingIoResource != NULL)
{
ExAcquireResourceExclusiveLite(Fcb->PagingIoResource, TRUE);
Fcb->FileSize.QuadPart = InitialFileSize.QuadPart;
Fcb->ValidDataLength.QuadPart = InitialValidDataLength.QuadPart;
ExReleaseResourceLite(Fcb->PagingIoResource);
}
else
{
Fcb->FileSize.QuadPart = InitialFileSize.QuadPart;
Fcb->ValidDataLength.QuadPart = InitialValidDataLength.QuadPart;
}
}
}
}
else
{
BOOLEAN AboveFour;
WriteOffset.HighPart = 0;
LastOffset.HighPart = 0;
/* If we're to extend the file, then, acquire exclusively
* Here, easy stuff, we know we can wait, no return to check!
*/
if (WriteToEof || FileOffset->QuadPart + Length > Fcb->ValidDataLength.QuadPart)
{
ExAcquireResourceExclusiveLite(Fcb->Resource, TRUE);
}
/* Otherwise, a shared lock is enough */
else
{
ExAcquireResourceSharedLite(Fcb->Resource, TRUE);
AcquiredShared = TRUE;
}
/* Get first write offset, and last
* Also check whether our writing will bring us
* beyond the 4GB
*/
if (WriteToEof)
{
WriteOffset.LowPart = Fcb->FileSize.LowPart;
LastOffset.LowPart = WriteOffset.LowPart + Length;
AboveFour = (LastOffset.LowPart < Fcb->FileSize.LowPart);
}
else
{
WriteOffset.LowPart = FileOffset->LowPart;
LastOffset.LowPart = WriteOffset.LowPart + Length;
AboveFour = (LastOffset.LowPart < FileOffset->LowPart) ||
(FileOffset->HighPart != 0);
}
/* If cache wasn't initialized, fail */
if (FileObject->PrivateCacheMap == NULL ||
Fcb->IsFastIoPossible == FastIoIsNotPossible)
{
ExReleaseResourceLite(Fcb->Resource);
FsRtlExitFileSystem();
return FALSE;
}
/* If we're to write beyond allocation size, it's no go,
* same is we create a hole bigger than 8kb
* same if we end writing beyond 4GB
*/
if ((Fcb->AllocationSize.LowPart < LastOffset.LowPart) ||
(WriteOffset.LowPart >= Fcb->ValidDataLength.LowPart + 0x2000) ||
AboveFour)
{
ExReleaseResourceLite(Fcb->Resource);
FsRtlExitFileSystem();
return FALSE;
}
/* If we have to extend the VDL, shared lock isn't enough */
if (AcquiredShared && LastOffset.LowPart > Fcb->ValidDataLength.LowPart)
{
/* So release, and acquire exclusively */
ExReleaseResourceLite(Fcb->Resource);
ExAcquireResourceExclusiveLite(Fcb->Resource, TRUE);
/* Get again EOF, in case file size changed in between and
* recheck we won't go beyond 4GB
*/
if (WriteToEof)
{
WriteOffset.LowPart = Fcb->FileSize.LowPart;
LastOffset.LowPart = WriteOffset.LowPart + Length;
AboveFour = (LastOffset.LowPart < Fcb->FileSize.LowPart);
}
/* Make sure caching is still enabled */
if (FileObject->PrivateCacheMap == NULL ||
Fcb->IsFastIoPossible == FastIoIsNotPossible)
{
ExReleaseResourceLite(Fcb->Resource);
FsRtlExitFileSystem();
return FALSE;
}
/* And that we're not writting beyond allocation size
* and that we're not going above 4GB
*/
if ((Fcb->AllocationSize.LowPart < LastOffset.LowPart) ||
(Fcb->AllocationSize.HighPart != 0) || AboveFour)
{
ExReleaseResourceLite(Fcb->Resource);
FsRtlExitFileSystem();
return FALSE;
}
}
/* If FastIO is questionable, then question */
if (Fcb->IsFastIoPossible == FastIoIsQuestionable)
{
PFAST_IO_DISPATCH FastIoDispatch;
PDEVICE_OBJECT RelatedDeviceObject;
RelatedDeviceObject = IoGetRelatedDeviceObject(FileObject);
FastIoDispatch = RelatedDeviceObject->DriverObject->FastIoDispatch;
ASSERT(FastIoDispatch != NULL);
ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL);
if (!FastIoDispatch->FastIoCheckIfPossible(FileObject,
&WriteOffset,
Length, Wait, LockKey,
FALSE, &LocalIoStatus,
RelatedDeviceObject))
{
ExReleaseResourceLite(Fcb->Resource);
FsRtlExitFileSystem();
return FALSE;
}
}
/* If we write beyond EOF, then, save previous sizes (in case of failure)
* and update file size, to allow writing
*/
if (LastOffset.LowPart > Fcb->FileSize.LowPart)
{
FileSizeChanged = TRUE;
InitialFileSize.LowPart = Fcb->FileSize.LowPart;
InitialValidDataLength.LowPart = Fcb->ValidDataLength.LowPart;
Fcb->FileSize.LowPart = LastOffset.LowPart;
}
/* Set caller provided context as top level IRP */
IoSetTopLevelIrp(TopLevelContext);
Ret = TRUE;
/* And perform the writing */
_SEH2_TRY
{
/* Check whether we've to create a hole first -
* it cannot fail, we can wait
*/
if (LastOffset.LowPart > Fcb->ValidDataLength.LowPart)
{
CcZeroData(FileObject, &Fcb->ValidDataLength, &WriteOffset, TRUE);
}
/* Write data */
CcFastCopyWrite(FileObject, WriteOffset.LowPart, Length, Buffer);
}
_SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH)
{
Ret = FALSE;
}
_SEH2_END;
/* Restore top level IRP */
IoSetTopLevelIrp(NULL);
/* If writing succeed */
if (Ret)
{
/* If we wrote beyond VDL, update it */
if (LastOffset.LowPart > Fcb->ValidDataLength.LowPart)
{
Fcb->ValidDataLength.LowPart = LastOffset.LowPart;
}
/* File was obviously modified */
SetFlag(FileObject->Flags, FO_FILE_MODIFIED);
/* And if we increased it, modify size in Cc and update FO */
if (FileSizeChanged)
{
(*CcGetFileSizePointer(FileObject)).LowPart = LastOffset.LowPart;
SetFlag(FileObject->Flags, FO_FILE_SIZE_CHANGED);
}
/* Update offset - we're still below 4GB, so high part must be 0 */
FileObject->CurrentByteOffset.LowPart = WriteOffset.LowPart + Length;
FileObject->CurrentByteOffset.HighPart = 0;
}
else
{
/* We failed, we need to restore previous sizes */
if (FileSizeChanged)
{
if (Fcb->PagingIoResource != NULL)
{
ExAcquireResourceExclusiveLite(Fcb->PagingIoResource, TRUE);
Fcb->FileSize.LowPart = InitialFileSize.LowPart;
Fcb->ValidDataLength.LowPart = InitialValidDataLength.LowPart;
ExReleaseResourceLite(Fcb->PagingIoResource);
}
else
{
Fcb->FileSize.LowPart = InitialFileSize.LowPart;
Fcb->ValidDataLength.LowPart = InitialValidDataLength.LowPart;
}
}
}
}
/* Release our resource and leave */
ExReleaseResourceLite(Fcb->Resource);
FsRtlExitFileSystem();
return Ret;
}