mirror of
https://github.com/reactos/reactos.git
synced 2024-08-20 02:13:29 +00:00
![Pierre Schweitzer](/assets/img/avatar_default.png)
This will avoid corruption when a file size is little grown and read afterwards. Up to now, FSD where reading 0es instead of expected data, causing corruption. This fixes MS FastFAT not being able to mount a FAT volume in ReactOS, corrupting the FAT. This also fixes the CcSetFileSizes kmtest tests. This is based on a patch by Thomas Faber. CORE-11819
422 lines
11 KiB
C
422 lines
11 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: ntoskrnl/cc/fs.c
|
|
* PURPOSE: Implements cache managers functions useful for File Systems
|
|
*
|
|
* PROGRAMMERS: Alex Ionescu
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* GLOBALS *****************************************************************/
|
|
|
|
NTSTATUS CcRosInternalFreeVacb(PROS_VACB Vacb);
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
LARGE_INTEGER
|
|
NTAPI
|
|
CcGetDirtyPages (
|
|
IN PVOID LogHandle,
|
|
IN PDIRTY_PAGE_ROUTINE DirtyPageRoutine,
|
|
IN PVOID Context1,
|
|
IN PVOID Context2)
|
|
{
|
|
LARGE_INTEGER i;
|
|
|
|
CCTRACE(CC_API_DEBUG, "LogHandle=%p DirtyPageRoutine=%p Context1=%p Context2=%p\n",
|
|
LogHandle, DirtyPageRoutine, Context1, Context2);
|
|
|
|
UNIMPLEMENTED;
|
|
i.QuadPart = 0;
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
PFILE_OBJECT
|
|
NTAPI
|
|
CcGetFileObjectFromBcb (
|
|
IN PVOID Bcb)
|
|
{
|
|
PINTERNAL_BCB iBcb = (PINTERNAL_BCB)Bcb;
|
|
|
|
CCTRACE(CC_API_DEBUG, "Bcb=%p\n", Bcb);
|
|
|
|
return iBcb->Vacb->SharedCacheMap->FileObject;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
LARGE_INTEGER
|
|
NTAPI
|
|
CcGetLsnForFileObject (
|
|
IN PFILE_OBJECT FileObject,
|
|
OUT PLARGE_INTEGER OldestLsn OPTIONAL)
|
|
{
|
|
LARGE_INTEGER i;
|
|
|
|
CCTRACE(CC_API_DEBUG, "FileObject=%p\n", FileObject);
|
|
|
|
UNIMPLEMENTED;
|
|
i.QuadPart = 0;
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
CcInitializeCacheMap (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PCC_FILE_SIZES FileSizes,
|
|
IN BOOLEAN PinAccess,
|
|
IN PCACHE_MANAGER_CALLBACKS CallBacks,
|
|
IN PVOID LazyWriterContext)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
ASSERT(FileObject);
|
|
ASSERT(FileSizes);
|
|
|
|
CCTRACE(CC_API_DEBUG, "FileObject=%p FileSizes=%p PinAccess=%d CallBacks=%p LazyWriterContext=%p\n",
|
|
FileObject, FileSizes, PinAccess, CallBacks, LazyWriterContext);
|
|
|
|
/* Call old ROS cache init function */
|
|
Status = CcRosInitializeFileCache(FileObject,
|
|
FileSizes,
|
|
PinAccess,
|
|
CallBacks,
|
|
LazyWriterContext);
|
|
if (!NT_SUCCESS(Status))
|
|
ExRaiseStatus(Status);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
CcIsThereDirtyData (
|
|
IN PVPB Vpb)
|
|
{
|
|
PROS_VACB Vacb;
|
|
PLIST_ENTRY Entry;
|
|
KIRQL oldIrql;
|
|
/* Assume no dirty data */
|
|
BOOLEAN Dirty = FALSE;
|
|
|
|
CCTRACE(CC_API_DEBUG, "Vpb=%p\n", Vpb);
|
|
|
|
oldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
|
|
|
|
/* Browse dirty VACBs */
|
|
for (Entry = DirtyVacbListHead.Flink; Entry != &DirtyVacbListHead; Entry = Entry->Flink)
|
|
{
|
|
Vacb = CONTAINING_RECORD(Entry, ROS_VACB, DirtyVacbListEntry);
|
|
/* Look for these associated with our volume */
|
|
if (Vacb->SharedCacheMap->FileObject->Vpb != Vpb)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* From now on, we are associated with our VPB */
|
|
|
|
/* Temporary files are not counted as dirty */
|
|
if (BooleanFlagOn(Vacb->SharedCacheMap->FileObject->Flags, FO_TEMPORARY_FILE))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* A single dirty VACB is enough to have dirty data */
|
|
if (Vacb->Dirty)
|
|
{
|
|
Dirty = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
KeReleaseQueuedSpinLock(LockQueueMasterLock, oldIrql);
|
|
|
|
return Dirty;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
CcPurgeCacheSection (
|
|
IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
|
|
IN PLARGE_INTEGER FileOffset OPTIONAL,
|
|
IN ULONG Length,
|
|
IN BOOLEAN UninitializeCacheMaps)
|
|
{
|
|
PROS_SHARED_CACHE_MAP SharedCacheMap;
|
|
LONGLONG StartOffset;
|
|
LONGLONG EndOffset;
|
|
LIST_ENTRY FreeList;
|
|
KIRQL OldIrql;
|
|
PLIST_ENTRY ListEntry;
|
|
PROS_VACB Vacb;
|
|
LONGLONG ViewEnd;
|
|
BOOLEAN Success;
|
|
|
|
CCTRACE(CC_API_DEBUG, "SectionObjectPointer=%p\n FileOffset=%p Length=%lu UninitializeCacheMaps=%d",
|
|
SectionObjectPointer, FileOffset, Length, UninitializeCacheMaps);
|
|
|
|
if (UninitializeCacheMaps)
|
|
{
|
|
DPRINT1("FIXME: CcPurgeCacheSection not uninitializing private cache maps\n");
|
|
}
|
|
|
|
SharedCacheMap = SectionObjectPointer->SharedCacheMap;
|
|
if (!SharedCacheMap)
|
|
return FALSE;
|
|
|
|
StartOffset = FileOffset != NULL ? FileOffset->QuadPart : 0;
|
|
if (Length == 0 || FileOffset == NULL)
|
|
{
|
|
EndOffset = MAXLONGLONG;
|
|
}
|
|
else
|
|
{
|
|
EndOffset = StartOffset + Length;
|
|
ASSERT(EndOffset > StartOffset);
|
|
}
|
|
|
|
InitializeListHead(&FreeList);
|
|
|
|
/* Assume success */
|
|
Success = TRUE;
|
|
|
|
OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
|
|
KeAcquireSpinLockAtDpcLevel(&SharedCacheMap->CacheMapLock);
|
|
ListEntry = SharedCacheMap->CacheMapVacbListHead.Flink;
|
|
while (ListEntry != &SharedCacheMap->CacheMapVacbListHead)
|
|
{
|
|
ULONG Refs;
|
|
|
|
Vacb = CONTAINING_RECORD(ListEntry, ROS_VACB, CacheMapVacbListEntry);
|
|
ListEntry = ListEntry->Flink;
|
|
|
|
/* Skip VACBs outside the range, or only partially in range */
|
|
if (Vacb->FileOffset.QuadPart < StartOffset)
|
|
{
|
|
continue;
|
|
}
|
|
ViewEnd = min(Vacb->FileOffset.QuadPart + VACB_MAPPING_GRANULARITY,
|
|
SharedCacheMap->SectionSize.QuadPart);
|
|
if (ViewEnd >= EndOffset)
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* Still in use, it cannot be purged, fail
|
|
* Allow one ref: VACB is supposed to be always 1-referenced
|
|
*/
|
|
Refs = CcRosVacbGetRefCount(Vacb);
|
|
if ((Refs > 1 && !Vacb->Dirty) ||
|
|
(Refs > 2 && Vacb->Dirty))
|
|
{
|
|
Success = FALSE;
|
|
break;
|
|
}
|
|
|
|
/* This VACB is in range, so unlink it and mark for free */
|
|
ASSERT(Refs == 1 || Vacb->Dirty);
|
|
RemoveEntryList(&Vacb->VacbLruListEntry);
|
|
InitializeListHead(&Vacb->VacbLruListEntry);
|
|
if (Vacb->Dirty)
|
|
{
|
|
CcRosUnmarkDirtyVacb(Vacb, FALSE);
|
|
}
|
|
RemoveEntryList(&Vacb->CacheMapVacbListEntry);
|
|
InsertHeadList(&FreeList, &Vacb->CacheMapVacbListEntry);
|
|
}
|
|
KeReleaseSpinLockFromDpcLevel(&SharedCacheMap->CacheMapLock);
|
|
KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
|
|
|
|
while (!IsListEmpty(&FreeList))
|
|
{
|
|
ULONG Refs;
|
|
|
|
Vacb = CONTAINING_RECORD(RemoveHeadList(&FreeList),
|
|
ROS_VACB,
|
|
CacheMapVacbListEntry);
|
|
InitializeListHead(&Vacb->CacheMapVacbListEntry);
|
|
Refs = CcRosVacbDecRefCount(Vacb);
|
|
ASSERT(Refs == 0);
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID NTAPI
|
|
CcSetFileSizes (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PCC_FILE_SIZES FileSizes)
|
|
{
|
|
KIRQL oldirql;
|
|
PROS_SHARED_CACHE_MAP SharedCacheMap;
|
|
|
|
CCTRACE(CC_API_DEBUG, "FileObject=%p FileSizes=%p\n",
|
|
FileObject, FileSizes);
|
|
|
|
DPRINT("CcSetFileSizes(FileObject 0x%p, FileSizes 0x%p)\n",
|
|
FileObject, FileSizes);
|
|
DPRINT("AllocationSize %I64d, FileSize %I64d, ValidDataLength %I64d\n",
|
|
FileSizes->AllocationSize.QuadPart,
|
|
FileSizes->FileSize.QuadPart,
|
|
FileSizes->ValidDataLength.QuadPart);
|
|
|
|
SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
|
|
|
|
/*
|
|
* It is valid to call this function on file objects that weren't
|
|
* initialized for caching. In this case it's simple no-op.
|
|
*/
|
|
if (SharedCacheMap == NULL)
|
|
return;
|
|
|
|
if (FileSizes->AllocationSize.QuadPart < SharedCacheMap->SectionSize.QuadPart)
|
|
{
|
|
CcPurgeCacheSection(FileObject->SectionObjectPointer,
|
|
&FileSizes->AllocationSize,
|
|
0,
|
|
FALSE);
|
|
}
|
|
else
|
|
{
|
|
PROS_VACB LastVacb;
|
|
|
|
/*
|
|
* If file (allocation) size has increased, then we need to check whether
|
|
* it just grows in a single VACB (the last one).
|
|
* If so, we must mark the VACB as invalid to trigger a read to the
|
|
* FSD at the next VACB usage, and thus avoid returning garbage
|
|
*/
|
|
|
|
/* Check for allocation size and the last VACB */
|
|
if (SharedCacheMap->SectionSize.QuadPart < FileSizes->AllocationSize.QuadPart &&
|
|
SharedCacheMap->SectionSize.QuadPart % VACB_MAPPING_GRANULARITY)
|
|
{
|
|
LastVacb = CcRosLookupVacb(SharedCacheMap,
|
|
SharedCacheMap->SectionSize.QuadPart);
|
|
if (LastVacb != NULL)
|
|
{
|
|
/* Mark it as invalid */
|
|
CcRosReleaseVacb(SharedCacheMap, LastVacb, LastVacb->Dirty ? LastVacb->Valid : FALSE, FALSE, FALSE);
|
|
}
|
|
}
|
|
|
|
/* Check for file size and the last VACB */
|
|
if (SharedCacheMap->FileSize.QuadPart < FileSizes->FileSize.QuadPart &&
|
|
SharedCacheMap->FileSize.QuadPart % VACB_MAPPING_GRANULARITY)
|
|
{
|
|
LastVacb = CcRosLookupVacb(SharedCacheMap,
|
|
SharedCacheMap->FileSize.QuadPart);
|
|
if (LastVacb != NULL)
|
|
{
|
|
/* Mark it as invalid */
|
|
CcRosReleaseVacb(SharedCacheMap, LastVacb, LastVacb->Dirty ? LastVacb->Valid : FALSE, FALSE, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldirql);
|
|
SharedCacheMap->SectionSize = FileSizes->AllocationSize;
|
|
SharedCacheMap->FileSize = FileSizes->FileSize;
|
|
KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldirql);
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
CcSetLogHandleForFile (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PVOID LogHandle,
|
|
IN PFLUSH_TO_LSN FlushToLsnRoutine)
|
|
{
|
|
CCTRACE(CC_API_DEBUG, "FileObject=%p LogHandle=%p FlushToLsnRoutine=%p\n",
|
|
FileObject, LogHandle, FlushToLsnRoutine);
|
|
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
CcUninitializeCacheMap (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER TruncateSize OPTIONAL,
|
|
IN PCACHE_UNINITIALIZE_EVENT UninitializeCompleteEvent OPTIONAL)
|
|
{
|
|
NTSTATUS Status;
|
|
PROS_SHARED_CACHE_MAP SharedCacheMap;
|
|
KIRQL OldIrql;
|
|
|
|
CCTRACE(CC_API_DEBUG, "FileObject=%p TruncateSize=%p UninitializeCompleteEvent=%p\n",
|
|
FileObject, TruncateSize, UninitializeCompleteEvent);
|
|
|
|
if (TruncateSize != NULL &&
|
|
FileObject->SectionObjectPointer->SharedCacheMap != NULL)
|
|
{
|
|
SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
|
|
KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &OldIrql);
|
|
if (SharedCacheMap->FileSize.QuadPart > TruncateSize->QuadPart)
|
|
{
|
|
SharedCacheMap->FileSize = *TruncateSize;
|
|
}
|
|
KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql);
|
|
CcPurgeCacheSection(FileObject->SectionObjectPointer,
|
|
TruncateSize,
|
|
0,
|
|
FALSE);
|
|
}
|
|
|
|
Status = CcRosReleaseFileCache(FileObject);
|
|
if (UninitializeCompleteEvent)
|
|
{
|
|
KeSetEvent(&UninitializeCompleteEvent->Event, IO_NO_INCREMENT, FALSE);
|
|
}
|
|
return NT_SUCCESS(Status);
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
CcGetFileSizes (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PCC_FILE_SIZES FileSizes)
|
|
{
|
|
PROS_SHARED_CACHE_MAP SharedCacheMap;
|
|
|
|
SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
|
|
|
|
if (!SharedCacheMap)
|
|
return FALSE;
|
|
|
|
FileSizes->AllocationSize = SharedCacheMap->SectionSize;
|
|
FileSizes->FileSize = FileSizes->ValidDataLength = SharedCacheMap->FileSize;
|
|
return TRUE;
|
|
}
|