/* * 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 #define NDEBUG #include /* 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; }