#include "copysup.h"
#include <pseh/pseh2.h>
+#define NDEBUG
+#include <debug.h>
/* FUNCTIONS ****************************************************************/
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 = (((LARGE_INTEGER)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;
+ C_ASSERT(sizeof(LARGE_INTEGER) == sizeof(LONGLONG));
+
+ 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;
+}