You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1169 lines
34 KiB
1169 lines
34 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cmwrapr.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the source for wrapper routines called by the
|
|
hive code, which in turn call the appropriate NT routines.
|
|
|
|
Author:
|
|
|
|
Bryan M. Willman (bryanwi) 16-Dec-1991
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "cmp.h"
|
|
|
|
VOID
|
|
CmpUnmapCmViewSurroundingOffset(
|
|
IN PCMHIVE CmHive,
|
|
IN ULONG FileOffset
|
|
);
|
|
|
|
|
|
|
|
#ifdef CM_TRACK_QUOTA_LEAKS
|
|
BOOLEAN CmpTrackQuotaEnabled = FALSE;
|
|
LIST_ENTRY CmpTrackQuotaListHead;
|
|
FAST_MUTEX CmpQuotaLeaksMutex;
|
|
#endif
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg("PAGEDATA")
|
|
#endif
|
|
ULONG perftouchbuffer = 0;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,CmpAllocate)
|
|
#ifdef POOL_TAGGING
|
|
#pragma alloc_text(PAGE,CmpAllocateTag)
|
|
#endif
|
|
#pragma alloc_text(PAGE,CmpFree)
|
|
#pragma alloc_text(PAGE,CmpDoFileSetSize)
|
|
#pragma alloc_text(PAGE,CmpCreateEvent)
|
|
#pragma alloc_text(PAGE,CmpFileRead)
|
|
#pragma alloc_text(PAGE,CmpFileWrite)
|
|
#pragma alloc_text(PAGE,CmpFileFlush)
|
|
#pragma alloc_text(PAGE,CmpFileWriteThroughCache)
|
|
#endif
|
|
|
|
extern BOOLEAN CmpNoWrite;
|
|
|
|
|
|
//
|
|
// never read more than 64k, neither the filesystem nor some disk drivers
|
|
// like it much.
|
|
//
|
|
#define MAX_FILE_IO 0x10000
|
|
|
|
#define CmpIoFileRead 1
|
|
#define CmpIoFileWrite 2
|
|
#define CmpIoFileSetSize 3
|
|
#define CmpIoFileFlush 4
|
|
|
|
extern struct {
|
|
ULONG Action;
|
|
HANDLE Handle;
|
|
NTSTATUS Status;
|
|
} CmRegistryIODebug;
|
|
|
|
extern BOOLEAN CmpFlushOnLockRelease;
|
|
//
|
|
// Storage management
|
|
//
|
|
|
|
PVOID
|
|
CmpAllocate(
|
|
ULONG Size,
|
|
BOOLEAN UseForIo,
|
|
ULONG Tag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine makes more memory available to a hive.
|
|
|
|
It is environment specific.
|
|
|
|
Arguments:
|
|
|
|
Size - amount of space caller wants
|
|
|
|
UseForIo - TRUE if object allocated will be target of I/O,
|
|
FALSE if not.
|
|
|
|
Return Value:
|
|
|
|
NULL if failure, address of allocated block if not.
|
|
|
|
--*/
|
|
{
|
|
PVOID result;
|
|
ULONG pooltype;
|
|
#ifdef CM_TRACK_QUOTA_LEAKS
|
|
ULONG NewSize = Size;
|
|
ULONG RoundedSize = ROUND_UP(Size,sizeof(PVOID));
|
|
#endif
|
|
|
|
#if DBG
|
|
PVOID Caller;
|
|
PVOID CallerCaller;
|
|
RtlGetCallersAddress(&Caller, &CallerCaller);
|
|
#endif
|
|
|
|
if (CmpClaimGlobalQuota(Size) == FALSE) {
|
|
return NULL;
|
|
}
|
|
#ifdef CM_TRACK_QUOTA_LEAKS
|
|
if( CmpTrackQuotaEnabled ) {
|
|
NewSize = RoundedSize + sizeof(CM_QUOTA_LOG_ENTRY);
|
|
}
|
|
#endif
|
|
|
|
pooltype = (UseForIo) ? PagedPoolCacheAligned : PagedPool;
|
|
result = ExAllocatePoolWithTag(
|
|
pooltype,
|
|
#ifdef CM_TRACK_QUOTA_LEAKS
|
|
NewSize,
|
|
#else
|
|
Size,
|
|
#endif
|
|
Tag
|
|
);
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_POOL,"**CmpAllocate: allocate:%08lx, ", Size));
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_POOL,"type:%d, at:%08lx ", PagedPool, result));
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_POOL,"c:%p cc:%p\n", Caller, CallerCaller));
|
|
|
|
if (result == NULL) {
|
|
CmpReleaseGlobalQuota(Size);
|
|
}
|
|
#ifdef CM_TRACK_QUOTA_LEAKS
|
|
if( CmpTrackQuotaEnabled ) {
|
|
PCM_QUOTA_LOG_ENTRY QuotaEntry = (PCM_QUOTA_LOG_ENTRY)(((PUCHAR)result) + RoundedSize);
|
|
|
|
RtlWalkFrameChain(QuotaEntry->Stack,sizeof(QuotaEntry->Stack)/sizeof(PVOID),0);
|
|
ExAcquireFastMutexUnsafe(&CmpQuotaLeaksMutex);
|
|
InsertTailList( &CmpTrackQuotaListHead,
|
|
&(QuotaEntry->ListEntry)
|
|
);
|
|
ExReleaseFastMutexUnsafe(&CmpQuotaLeaksMutex);
|
|
QuotaEntry->Size = Size;
|
|
}
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
#ifdef POOL_TAGGING
|
|
PVOID
|
|
CmpAllocateTag(
|
|
ULONG Size,
|
|
BOOLEAN UseForIo,
|
|
ULONG Tag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine makes more memory available to a hive.
|
|
|
|
It is environment specific.
|
|
|
|
Arguments:
|
|
|
|
Size - amount of space caller wants
|
|
|
|
UseForIo - TRUE if object allocated will be target of I/O,
|
|
FALSE if not.
|
|
|
|
Return Value:
|
|
|
|
NULL if failure, address of allocated block if not.
|
|
|
|
--*/
|
|
{
|
|
PVOID result;
|
|
ULONG pooltype;
|
|
#ifdef CM_TRACK_QUOTA_LEAKS
|
|
ULONG NewSize = Size;
|
|
ULONG RoundedSize = ROUND_UP(Size,sizeof(PVOID));
|
|
#endif
|
|
|
|
#if DBG
|
|
PVOID Caller;
|
|
PVOID CallerCaller;
|
|
RtlGetCallersAddress(&Caller, &CallerCaller);
|
|
#endif
|
|
|
|
if (CmpClaimGlobalQuota(Size) == FALSE) {
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef CM_TRACK_QUOTA_LEAKS
|
|
if( CmpTrackQuotaEnabled ) {
|
|
NewSize = RoundedSize + sizeof(CM_QUOTA_LOG_ENTRY);
|
|
}
|
|
#endif
|
|
|
|
pooltype = (UseForIo) ? PagedPoolCacheAligned : PagedPool;
|
|
result = ExAllocatePoolWithTag(
|
|
pooltype,
|
|
#ifdef CM_TRACK_QUOTA_LEAKS
|
|
NewSize,
|
|
#else
|
|
Size,
|
|
#endif
|
|
Tag
|
|
);
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_POOL,"**CmpAllocate: allocate:%08lx, ", Size));
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_POOL,"type:%d, at:%08lx ", PagedPool, result));
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_POOL,"c:%p cc:%p\n", Caller, CallerCaller));
|
|
|
|
if (result == NULL) {
|
|
CmpReleaseGlobalQuota(Size);
|
|
}
|
|
|
|
#ifdef CM_TRACK_QUOTA_LEAKS
|
|
if( CmpTrackQuotaEnabled ) {
|
|
PCM_QUOTA_LOG_ENTRY QuotaEntry = (PCM_QUOTA_LOG_ENTRY)(((PUCHAR)result) + RoundedSize);
|
|
|
|
RtlWalkFrameChain(QuotaEntry->Stack,sizeof(QuotaEntry->Stack)/sizeof(PVOID),0);
|
|
ExAcquireFastMutexUnsafe(&CmpQuotaLeaksMutex);
|
|
InsertTailList( &CmpTrackQuotaListHead,
|
|
&(QuotaEntry->ListEntry)
|
|
);
|
|
ExReleaseFastMutexUnsafe(&CmpQuotaLeaksMutex);
|
|
QuotaEntry->Size = Size;
|
|
}
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
|
|
VOID
|
|
CmpFree(
|
|
PVOID MemoryBlock,
|
|
ULONG GlobalQuotaSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees memory that has been allocated by the registry.
|
|
|
|
It is environment specific
|
|
|
|
|
|
Arguments:
|
|
|
|
MemoryBlock - supplies address of memory object to free
|
|
|
|
GlobalQuotaSize - amount of global quota to release
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
{
|
|
#if DBG
|
|
PVOID Caller;
|
|
PVOID CallerCaller;
|
|
RtlGetCallersAddress(&Caller, &CallerCaller);
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_POOL,"**FREEING:%08lx c,cc:%p,%p\n", MemoryBlock, Caller, CallerCaller));
|
|
#endif
|
|
ASSERT(GlobalQuotaSize > 0);
|
|
CmpReleaseGlobalQuota(GlobalQuotaSize);
|
|
#ifdef CM_TRACK_QUOTA_LEAKS
|
|
if( CmpTrackQuotaEnabled ) {
|
|
ULONG RoundedSize = ROUND_UP(GlobalQuotaSize,sizeof(PVOID));
|
|
PCM_QUOTA_LOG_ENTRY QuotaEntry = (PCM_QUOTA_LOG_ENTRY)(((PUCHAR)MemoryBlock) + RoundedSize);
|
|
|
|
ASSERT( QuotaEntry->Size == GlobalQuotaSize );
|
|
|
|
ExAcquireFastMutexUnsafe(&CmpQuotaLeaksMutex);
|
|
RemoveEntryList(&(QuotaEntry->ListEntry) );
|
|
ExReleaseFastMutexUnsafe(&CmpQuotaLeaksMutex);
|
|
}
|
|
#endif
|
|
ExFreePool(MemoryBlock);
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CmpDoFileSetSize(
|
|
PHHIVE Hive,
|
|
ULONG FileType,
|
|
ULONG FileSize,
|
|
ULONG OldFileSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets the size of a file. It must not return until
|
|
the size is guaranteed.
|
|
|
|
It is environment specific.
|
|
|
|
Must be running in the context of the cmp worker thread.
|
|
|
|
Arguments:
|
|
|
|
Hive - Hive we are doing I/O for
|
|
|
|
FileType - which supporting file to use
|
|
|
|
FileSize - 32 bit value to set the file's size to
|
|
|
|
OldFileSize - old file size, in order to determine if this is a shrink;
|
|
- ignored if file type is not primary, or hive doesn't use
|
|
the mapped views technique
|
|
|
|
Return Value:
|
|
|
|
FALSE if failure
|
|
TRUE if success
|
|
|
|
--*/
|
|
{
|
|
PCMHIVE CmHive;
|
|
HANDLE FileHandle;
|
|
NTSTATUS Status;
|
|
FILE_END_OF_FILE_INFORMATION FileInfo;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
BOOLEAN oldFlag;
|
|
LARGE_INTEGER FileOffset; // where the mapping starts
|
|
|
|
ASSERT(FIELD_OFFSET(CMHIVE, Hive) == 0);
|
|
|
|
CmHive = (PCMHIVE)Hive;
|
|
FileHandle = CmHive->FileHandles[FileType];
|
|
if (FileHandle == NULL) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// disable hard error popups, to avoid self deadlock on bogus devices
|
|
//
|
|
oldFlag = IoSetThreadHardErrorMode(FALSE);
|
|
|
|
FileInfo.EndOfFile.HighPart = 0L;
|
|
if( FileType == HFILE_TYPE_PRIMARY ) {
|
|
FileInfo.EndOfFile.LowPart = ROUND_UP(FileSize, CM_FILE_GROW_INCREMENT);
|
|
} else {
|
|
FileInfo.EndOfFile.LowPart = FileSize;
|
|
}
|
|
|
|
ASSERT_PASSIVE_LEVEL();
|
|
|
|
Status = ZwSetInformationFile(
|
|
FileHandle,
|
|
&IoStatus,
|
|
(PVOID)&FileInfo,
|
|
sizeof(FILE_END_OF_FILE_INFORMATION),
|
|
FileEndOfFileInformation
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
ASSERT(IoStatus.Status == Status);
|
|
} else {
|
|
|
|
//
|
|
// set debugging info
|
|
//
|
|
CmRegistryIODebug.Action = CmpIoFileSetSize;
|
|
CmRegistryIODebug.Handle = FileHandle;
|
|
CmRegistryIODebug.Status = Status;
|
|
#ifndef _CM_LDR_
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpFileSetSize:\tHandle=%08lx OldLength = %08lx NewLength=%08lx \n",
|
|
FileHandle, OldFileSize, FileSize);
|
|
#endif //_CM_LDR_
|
|
if( (Status == STATUS_DISK_FULL) && ExIsResourceAcquiredExclusiveLite(&CmpRegistryLock) ) {
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"Disk is full while attempting to grow file %lx; will flush upon lock release\n",FileHandle);
|
|
CmpFlushOnLockRelease = TRUE;;
|
|
}
|
|
}
|
|
|
|
//
|
|
// restore hard error popups mode
|
|
//
|
|
IoSetThreadHardErrorMode(oldFlag);
|
|
|
|
|
|
//
|
|
// purge
|
|
//
|
|
if( HiveWritesThroughCache(Hive,FileType) && (OldFileSize > FileSize)) {
|
|
//
|
|
// first we have to unmap any possible mapped views in the last 256K window
|
|
// to avoid deadlock on CcWaitOnActiveCount inside CcPurgeCacheSection call below
|
|
//
|
|
ULONG Offset = FileSize & (~(_256K - 1));
|
|
//
|
|
// we are not allowed to shrink in shared mode.
|
|
//
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
|
|
while( Offset < OldFileSize ) {
|
|
CmpUnmapCmViewSurroundingOffset((PCMHIVE)Hive,Offset);
|
|
Offset += CM_VIEW_SIZE;
|
|
}
|
|
|
|
//
|
|
// we need to take extra precaution here and unmap the very last view too
|
|
//
|
|
//CmpUnmapCmViewSurroundingOffset((PCMHIVE)Hive,OldFileSize-HBLOCK_SIZE);
|
|
|
|
FileOffset.HighPart = 0;
|
|
FileOffset.LowPart = FileSize;
|
|
//
|
|
// This is a shrink; Inform cache manager of the change of the size
|
|
//
|
|
CcPurgeCacheSection( ((PCMHIVE)Hive)->FileObject->SectionObjectPointer, (PLARGE_INTEGER)(((ULONG_PTR)(&FileOffset)) + 1),
|
|
OldFileSize - FileSize, FALSE );
|
|
|
|
//
|
|
// Flush out this view to clear out the Cc dirty hints
|
|
//
|
|
CcFlushCache( ((PCMHIVE)Hive)->FileObject->SectionObjectPointer, (PLARGE_INTEGER)(((ULONG_PTR)(&FileOffset)) + 1),/*we are private writers*/
|
|
OldFileSize - FileSize,NULL);
|
|
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CmpCreateEvent(
|
|
IN EVENT_TYPE eventType,
|
|
OUT PHANDLE eventHandle,
|
|
OUT PKEVENT *event
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
OBJECT_ATTRIBUTES obja;
|
|
|
|
InitializeObjectAttributes( &obja, NULL, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL );
|
|
status = ZwCreateEvent(
|
|
eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
&obja,
|
|
eventType,
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
status = ObReferenceObjectByHandle(
|
|
*eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
KernelMode,
|
|
event,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ZwClose(*eventHandle);
|
|
return status;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
BOOLEAN
|
|
CmpFileRead (
|
|
PHHIVE Hive,
|
|
ULONG FileType,
|
|
PULONG FileOffset,
|
|
PVOID DataBuffer,
|
|
ULONG DataLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads in a buffer from a file.
|
|
|
|
It is environment specific.
|
|
|
|
NOTE: We assume the handle is opened for asynchronous access,
|
|
and that we, and not the IO system, are keeping the
|
|
offset pointer.
|
|
|
|
NOTE: Only 32bit offsets are supported, even though the underlying
|
|
IO system on NT supports 64 bit offsets.
|
|
|
|
Arguments:
|
|
|
|
Hive - Hive we are doing I/O for
|
|
|
|
FileType - which supporting file to use
|
|
|
|
FileOffset - pointer to variable providing 32bit offset on input,
|
|
and receiving new 32bit offset on output.
|
|
|
|
DataBuffer - pointer to buffer
|
|
|
|
DataLength - length of buffer
|
|
|
|
Return Value:
|
|
|
|
FALSE if failure
|
|
TRUE if success
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
LARGE_INTEGER Offset;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
PCMHIVE CmHive;
|
|
HANDLE FileHandle;
|
|
ULONG LengthToRead;
|
|
HANDLE eventHandle = NULL;
|
|
PKEVENT eventObject = NULL;
|
|
|
|
ASSERT(FIELD_OFFSET(CMHIVE, Hive) == 0);
|
|
CmHive = (PCMHIVE)Hive;
|
|
FileHandle = CmHive->FileHandles[FileType];
|
|
if (FileHandle == NULL) {
|
|
return TRUE;
|
|
}
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"CmpFileRead:\n"));
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"\tHandle=%08lx Offset=%08lx ", FileHandle, *FileOffset));
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"Buffer=%p Length=%08lx\n", DataBuffer, DataLength));
|
|
|
|
//
|
|
// Detect attempt to read off end of 2gig file (this should be irrelevent)
|
|
//
|
|
if ((0xffffffff - *FileOffset) < DataLength) {
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CmpFileRead: runoff\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
status = CmpCreateEvent(
|
|
SynchronizationEvent,
|
|
&eventHandle,
|
|
&eventObject);
|
|
if (!NT_SUCCESS(status))
|
|
return FALSE;
|
|
|
|
//
|
|
// We'd really like to just call the filesystems and have them do
|
|
// the right thing. But the filesystem will attempt to lock our
|
|
// entire buffer into memory, and that may fail for large requests.
|
|
// So we split our reads into 64k chunks and call the filesystem for
|
|
// each one.
|
|
//
|
|
ASSERT_PASSIVE_LEVEL();
|
|
while (DataLength > 0) {
|
|
|
|
//
|
|
// Convert ULONG to Large
|
|
//
|
|
Offset.LowPart = *FileOffset;
|
|
Offset.HighPart = 0L;
|
|
|
|
//
|
|
// trim request down if necessary.
|
|
//
|
|
if (DataLength > MAX_FILE_IO) {
|
|
LengthToRead = MAX_FILE_IO;
|
|
} else {
|
|
LengthToRead = DataLength;
|
|
}
|
|
|
|
status = ZwReadFile(
|
|
FileHandle,
|
|
eventHandle,
|
|
NULL, // apcroutine
|
|
NULL, // apccontext
|
|
&IoStatus,
|
|
DataBuffer,
|
|
LengthToRead,
|
|
&Offset,
|
|
NULL // key
|
|
);
|
|
|
|
if (STATUS_PENDING == status) {
|
|
status = KeWaitForSingleObject(eventObject, Executive,
|
|
KernelMode, FALSE, NULL);
|
|
ASSERT(STATUS_SUCCESS == status);
|
|
status = IoStatus.Status;
|
|
}
|
|
|
|
//
|
|
// adjust offsets
|
|
//
|
|
*FileOffset = Offset.LowPart + LengthToRead;
|
|
DataLength -= LengthToRead;
|
|
DataBuffer = (PVOID)((PCHAR)DataBuffer + LengthToRead);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
ASSERT(IoStatus.Status == status);
|
|
if (IoStatus.Information != LengthToRead) {
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CmpFileRead:\n\t"));
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"Failure1: status = %08lx ", status));
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"IoInformation = %08lx\n", IoStatus.Information));
|
|
ObDereferenceObject(eventObject);
|
|
ZwClose(eventHandle);
|
|
CmRegistryIODebug.Action = CmpIoFileRead;
|
|
CmRegistryIODebug.Handle = FileHandle;
|
|
#if defined(_WIN64)
|
|
CmRegistryIODebug.Status = (ULONG)IoStatus.Information - LengthToRead;
|
|
#else
|
|
CmRegistryIODebug.Status = (ULONG)&IoStatus;
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
//
|
|
// set debugging info
|
|
//
|
|
CmRegistryIODebug.Action = CmpIoFileRead;
|
|
CmRegistryIODebug.Handle = FileHandle;
|
|
CmRegistryIODebug.Status = status;
|
|
#ifndef _CM_LDR_
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpFileRead:\tFailure2: status = %08lx IoStatus = %08lx\n", status, IoStatus.Status);
|
|
#endif //_CM_LDR_
|
|
|
|
ObDereferenceObject(eventObject);
|
|
ZwClose(eventHandle);
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
ObDereferenceObject(eventObject);
|
|
ZwClose(eventHandle);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
CmpFileWriteThroughCache(
|
|
PHHIVE Hive,
|
|
ULONG FileType,
|
|
PCMP_OFFSET_ARRAY offsetArray,
|
|
ULONG offsetArrayCount
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is routine writes dirty ranges of data using Cc mapped views.
|
|
The benefit is that writes don't go through Cc Lazy Writer, so there
|
|
is no danger to be throttled or deffered.
|
|
|
|
It also flushes the cache for the written ranges, guaranteeing that
|
|
the data was commited to the disk upon return.
|
|
|
|
Arguments:
|
|
|
|
Hive - Hive we are doing I/O for
|
|
|
|
FileType - which supporting file to use
|
|
|
|
offsetArray - array of structures where each structure holds a 32bit offset
|
|
into the Hive file and pointer the a buffer written to that
|
|
file offset.
|
|
|
|
offsetArrayCount - number of elements in the offsetArray.
|
|
|
|
Return Value:
|
|
|
|
FALSE if failure
|
|
TRUE if success
|
|
|
|
Note:
|
|
|
|
This routine is intended to deal only with paged bins (i.e. bins crossing the
|
|
CM_VIEW_SIZE boundary or bins that were added after the last sync)
|
|
|
|
Assumption:
|
|
|
|
We work on the assumption that the data to be written at one iteration never spans
|
|
over the CM_VIEW_SIZE boundary. HvpFindNextDirtyBlock takes care of that !!!
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
PVOID DataBuffer;
|
|
ULONG DataLength;
|
|
ULONG FileOffset;
|
|
PCMHIVE CmHive;
|
|
PVOID Bcb;
|
|
PVOID FileBuffer;
|
|
LARGE_INTEGER Offset;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
|
|
ASSERT_PASSIVE_LEVEL();
|
|
|
|
#if !DBG
|
|
UNREFERENCED_PARAMETER (FileType);
|
|
#endif
|
|
|
|
CmHive = (PCMHIVE)CONTAINING_RECORD(Hive, CMHIVE, Hive);
|
|
|
|
ASSERT( ((FileType == HFILE_TYPE_EXTERNAL) && (CmHive->FileObject != NULL)) || HiveWritesThroughCache(Hive,FileType) );
|
|
|
|
//ASSERT( IsListEmpty(&(CmHive->PinViewListHead)) == TRUE);
|
|
//ASSERT( CmHive->PinnedViews == 0 );
|
|
|
|
Offset.HighPart = 0;
|
|
//
|
|
// iterate through the array of data
|
|
//
|
|
for(i=0;i<offsetArrayCount;i++) {
|
|
DataBuffer = offsetArray[i].DataBuffer;
|
|
DataLength = offsetArray[i].DataLength;
|
|
FileOffset = offsetArray[i].FileOffset;
|
|
//
|
|
// data should never span over CM_VIEW_SIZE boundary
|
|
//
|
|
ASSERT( (FileOffset & (~(CM_VIEW_SIZE - 1))) == ((FileOffset + DataLength - 1) & (~(CM_VIEW_SIZE - 1))) );
|
|
|
|
//
|
|
// unmap any possible mapped view that could overlapp with this range ; not needed !!!!
|
|
//
|
|
//CmpUnmapCmViewSurroundingOffset(CmHive,FileOffset);
|
|
|
|
//
|
|
// map and pin data
|
|
//
|
|
Offset.LowPart = FileOffset;
|
|
try {
|
|
if( !CcPinRead (CmHive->FileObject,&Offset,DataLength,PIN_WAIT,&Bcb,&FileBuffer) ) {
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpFileWriteThroughCache - could not pin read view i= %lu\n",i));
|
|
#if DBG
|
|
DbgBreakPoint();
|
|
#endif //DBG
|
|
return FALSE;
|
|
}
|
|
//
|
|
// copy data to pinned view; we need to do it inside try except, to protect against devices/volumes
|
|
// dismounting from under us.
|
|
//
|
|
RtlCopyMemory(FileBuffer,DataBuffer, DataLength);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
//
|
|
// in low-memory scenarios, CcPinRead throws a STATUS_INSUFFICIENT_RESOURCES
|
|
// We want to catch this and treat as a "not enough resources" problem,
|
|
// rather than letting it to surface the kernel call
|
|
//
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpFileWriteThroughCache : CcPinRead has raised :%08lx\n",GetExceptionCode()));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// dirty, unpin and flush
|
|
//
|
|
CcSetDirtyPinnedData (Bcb,NULL);
|
|
CcUnpinData( Bcb );
|
|
CcFlushCache (CmHive->FileObject->SectionObjectPointer,(PLARGE_INTEGER)(((ULONG_PTR)(&Offset)) + 1)/*we are private writers*/,DataLength,&IoStatus);
|
|
if(!NT_SUCCESS(IoStatus.Status) ) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
FAST_MUTEX CmpWriteLock; // used to synchronize access to the below;
|
|
// the only case we ned this is when NtSaveKey is called by different threads
|
|
// at the same time; all other calls to CmpFileWrite are made with the reg_lock
|
|
// held exclusively
|
|
CM_WRITE_BLOCK CmpWriteBlock;
|
|
|
|
BOOLEAN
|
|
CmpFileWrite(
|
|
PHHIVE Hive,
|
|
ULONG FileType,
|
|
PCMP_OFFSET_ARRAY offsetArray,
|
|
ULONG offsetArrayCount,
|
|
PULONG FileOffset
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine writes an array of buffers out to a file.
|
|
|
|
It is environment specific.
|
|
|
|
NOTE: We assume the handle is opened for asynchronous access,
|
|
and that we, and not the IO system, are keeping the
|
|
offset pointer.
|
|
|
|
NOTE: Only 32bit offsets are supported, even though the underlying
|
|
IO system on NT supports 64 bit offsets.
|
|
|
|
Arguments:
|
|
|
|
Hive - Hive we are doing I/O for
|
|
|
|
FileType - which supporting file to use
|
|
|
|
offsetArray - array of structures where each structure holds a 32bit offset
|
|
into the Hive file and pointer the a buffer written to that
|
|
file offset.
|
|
|
|
offsetArrayCount - number of elements in the offsetArray.
|
|
|
|
FileOffset - returns the file offset after the last write to the file.
|
|
|
|
Return Value:
|
|
|
|
FALSE if failure
|
|
TRUE if success
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
LARGE_INTEGER Offset;
|
|
PCMHIVE CmHive;
|
|
HANDLE FileHandle;
|
|
ULONG LengthToWrite;
|
|
LONG WaitBufferCount = 0;
|
|
LONG idx;
|
|
ULONG arrayCount = 0;
|
|
PVOID DataBuffer = NULL; // W4 only
|
|
ULONG DataLength;
|
|
BOOLEAN ret_val = TRUE;
|
|
|
|
if (CmpNoWrite) {
|
|
return TRUE;
|
|
}
|
|
|
|
ASSERT(FIELD_OFFSET(CMHIVE, Hive) == 0);
|
|
CmHive = (PCMHIVE)Hive;
|
|
FileHandle = CmHive->FileHandles[FileType];
|
|
if (FileHandle == NULL) {
|
|
return TRUE;
|
|
}
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"CmpFileWrite:\n"));
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"\tHandle=%08lx ", FileHandle));
|
|
|
|
//ASSERT( !HiveWritesThroughCache(Hive,FileType) );
|
|
|
|
ExAcquireFastMutexUnsafe(&CmpWriteLock);
|
|
|
|
for (idx = 0; idx < MAXIMUM_WAIT_OBJECTS; idx++) {
|
|
CmpWriteBlock.EventHandles[idx] = NULL;
|
|
#if DBG
|
|
CmpWriteBlock.EventObjects[idx] = NULL;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// decide whether we wait for IOs to complete or just issue them and
|
|
// rely on the CcFlushCache to do the job
|
|
//
|
|
|
|
// Bring pages being written into memory first to allow disk to write
|
|
// buffer contiguously.
|
|
for (idx = 0; (ULONG) idx < offsetArrayCount; idx++) {
|
|
char * start = offsetArray[idx].DataBuffer;
|
|
char * end = (char *) start + offsetArray[idx].DataLength;
|
|
while (start < end) {
|
|
// perftouchbuffer globally declared so that compiler won't try
|
|
// to remove it and this loop (if its smart enough?).
|
|
perftouchbuffer += (ULONG) *start;
|
|
start += PAGE_SIZE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We'd really like to just call the filesystems and have them do
|
|
// the right thing. But the filesystem will attempt to lock our
|
|
// entire buffer into memory, and that may fail for large requests.
|
|
// So we split our reads into 64k chunks and call the filesystem for
|
|
// each one.
|
|
//
|
|
ASSERT_PASSIVE_LEVEL();
|
|
arrayCount = 0;
|
|
DataLength = 0;
|
|
// This outer loop is hit more than once if the MAXIMUM_WAIT_OBJECTS limit
|
|
// is hit before the offset array is drained.
|
|
while (arrayCount < offsetArrayCount) {
|
|
WaitBufferCount = 0;
|
|
|
|
// This loop fills the wait buffer.
|
|
while ((arrayCount < offsetArrayCount) &&
|
|
(WaitBufferCount < MAXIMUM_WAIT_OBJECTS)) {
|
|
|
|
// If data length isn't zero than the wait buffer filled before the
|
|
// buffer in the last offsetArray element was sent to write file.
|
|
if (DataLength == 0) {
|
|
*FileOffset = offsetArray[arrayCount].FileOffset;
|
|
DataBuffer = offsetArray[arrayCount].DataBuffer;
|
|
DataLength = offsetArray[arrayCount].DataLength;
|
|
//
|
|
// Detect attempt to read off end of 2gig file
|
|
// (this should be irrelevent)
|
|
//
|
|
if ((0xffffffff - *FileOffset) < DataLength) {
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CmpFileWrite: runoff\n"));
|
|
status = STATUS_INVALID_PARAMETER_5;
|
|
goto Error_Exit;
|
|
}
|
|
}
|
|
// else still more to write out of last buffer.
|
|
|
|
while ((DataLength > 0) && (WaitBufferCount < MAXIMUM_WAIT_OBJECTS)) {
|
|
|
|
//
|
|
// Convert ULONG to Large
|
|
//
|
|
Offset.LowPart = *FileOffset;
|
|
Offset.HighPart = 0L;
|
|
|
|
//
|
|
// trim request down if necessary.
|
|
//
|
|
if (DataLength > MAX_FILE_IO) {
|
|
LengthToWrite = MAX_FILE_IO;
|
|
} else {
|
|
LengthToWrite = DataLength;
|
|
}
|
|
|
|
// Previously created events are reused.
|
|
if (CmpWriteBlock.EventHandles[WaitBufferCount] == NULL) {
|
|
status = CmpCreateEvent(SynchronizationEvent,
|
|
&(CmpWriteBlock.EventHandles[WaitBufferCount]),
|
|
&(CmpWriteBlock.EventObjects[WaitBufferCount]));
|
|
if (!NT_SUCCESS(status)) {
|
|
// Make sure we don't try to clean this up.
|
|
CmpWriteBlock.EventHandles[WaitBufferCount] = NULL;
|
|
goto Error_Exit;
|
|
}
|
|
CmpSetHandleProtection(CmpWriteBlock.EventHandles[WaitBufferCount],TRUE);
|
|
}
|
|
|
|
status = ZwWriteFile(FileHandle,
|
|
CmpWriteBlock.EventHandles[WaitBufferCount],
|
|
NULL, // apcroutine
|
|
NULL, // apccontext
|
|
&(CmpWriteBlock.IoStatus[WaitBufferCount]),
|
|
DataBuffer,
|
|
LengthToWrite,
|
|
&Offset,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Error_Exit;
|
|
}
|
|
|
|
WaitBufferCount++;
|
|
|
|
//
|
|
// adjust offsets
|
|
//
|
|
*FileOffset = Offset.LowPart + LengthToWrite;
|
|
DataLength -= LengthToWrite;
|
|
DataBuffer = (PVOID)((PCHAR)DataBuffer + LengthToWrite);
|
|
} // while (DataLength > 0 && WaitBufferCount < MAXIMUM_WAIT_OBJECTS)
|
|
|
|
arrayCount++;
|
|
|
|
} // while (arrayCount < offsetArrayCount &&
|
|
// WaitBufferCount < MAXIMUM_WAIT_OBJECTS)
|
|
|
|
status = KeWaitForMultipleObjects(WaitBufferCount,
|
|
CmpWriteBlock.EventObjects,
|
|
WaitAll,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL,
|
|
CmpWriteBlock.WaitBlockArray);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Error_Exit;
|
|
|
|
for (idx = 0; idx < WaitBufferCount; idx++) {
|
|
if (!NT_SUCCESS(CmpWriteBlock.IoStatus[idx].Status)) {
|
|
status = CmpWriteBlock.IoStatus[idx].Status;
|
|
ret_val = FALSE;
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
// There may still be more to do if the last element held a big buffer
|
|
// and the wait buffer filled before it was all sent to the file.
|
|
if (DataLength > 0) {
|
|
arrayCount--;
|
|
}
|
|
|
|
} // while (arrayCount < offsetArrayCount)
|
|
|
|
ret_val = TRUE;
|
|
|
|
goto Done;
|
|
Error_Exit:
|
|
//
|
|
// set debugging info
|
|
//
|
|
CmRegistryIODebug.Action = CmpIoFileWrite;
|
|
CmRegistryIODebug.Handle = FileHandle;
|
|
CmRegistryIODebug.Status = status;
|
|
#ifndef _CM_LDR_
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpFileWrite: error exiting %d\n", status);
|
|
#endif //_CM_LDR_
|
|
//
|
|
// if WaitBufferCount > 0 then we have successfully issued
|
|
// some I/Os, but not all of them. This is an error, but we
|
|
// cannot return from this routine until all the successfully
|
|
// issued I/Os have completed.
|
|
//
|
|
if (WaitBufferCount > 0) {
|
|
//
|
|
// only if we decided that we want to wait for the write to complete
|
|
// (log files and hives not using the mapped views technique)
|
|
//
|
|
status = KeWaitForMultipleObjects(WaitBufferCount,
|
|
CmpWriteBlock.EventObjects,
|
|
WaitAll,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL,
|
|
CmpWriteBlock.WaitBlockArray);
|
|
}
|
|
|
|
|
|
ret_val = FALSE;
|
|
Done:
|
|
idx = 0;
|
|
// Clean up open event handles and objects.
|
|
while ((idx < MAXIMUM_WAIT_OBJECTS) && (CmpWriteBlock.EventHandles[idx] != NULL)) {
|
|
ASSERT( CmpWriteBlock.EventObjects[idx] );
|
|
ObDereferenceObject(CmpWriteBlock.EventObjects[idx]);
|
|
CmCloseHandle(CmpWriteBlock.EventHandles[idx]);
|
|
idx++;
|
|
}
|
|
|
|
ExReleaseFastMutexUnsafe(&CmpWriteLock);
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CmpFileFlush (
|
|
PHHIVE Hive,
|
|
ULONG FileType,
|
|
PLARGE_INTEGER FileOffset,
|
|
ULONG Length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs a flush on a file handle.
|
|
|
|
Arguments:
|
|
|
|
Hive - Hive we are doing I/O for
|
|
|
|
FileType - which supporting file to use
|
|
|
|
FileOffset - If this parameter is supplied (not NULL), then only the
|
|
byte range specified by FileOffset and Length are flushed.
|
|
|
|
Length - Defines the length of the byte range to flush, starting at
|
|
FileOffset. This parameter is ignored if FileOffset is
|
|
specified as NULL.
|
|
|
|
|
|
Return Value:
|
|
|
|
FALSE if failure
|
|
TRUE if success
|
|
|
|
Note:
|
|
|
|
FileOffset and Length are only taken into account when FileType == HFILE_TYPE_PRIMARY
|
|
and the hive uses the mapped-views method.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
PCMHIVE CmHive;
|
|
HANDLE FileHandle;
|
|
|
|
ASSERT(FIELD_OFFSET(CMHIVE, Hive) == 0);
|
|
CmHive = (PCMHIVE)Hive;
|
|
FileHandle = CmHive->FileHandles[FileType];
|
|
if (FileHandle == NULL) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (CmpNoWrite) {
|
|
return TRUE;
|
|
}
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"CmpFileFlush:\n\tHandle = %08lx\n", FileHandle));
|
|
|
|
ASSERT_PASSIVE_LEVEL();
|
|
|
|
|
|
if( HiveWritesThroughCache(Hive,FileType) == TRUE ) {
|
|
//
|
|
// OK, we need to flush using CcFlushCache
|
|
//
|
|
CcFlushCache (CmHive->FileObject->SectionObjectPointer,(PLARGE_INTEGER)((ULONG_PTR)FileOffset + 1)/*we are private writers*/,Length,&IoStatus);
|
|
status = IoStatus.Status;
|
|
if( !NT_SUCCESS(status) ) {
|
|
goto Error;
|
|
}
|
|
}
|
|
//
|
|
// we have to do that regardless, to make sure the disk cache makes it to the disk.
|
|
//
|
|
status = ZwFlushBuffersFile(
|
|
FileHandle,
|
|
&IoStatus
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
ASSERT(IoStatus.Status == status);
|
|
return TRUE;
|
|
} else {
|
|
Error:
|
|
//
|
|
// set debugging info
|
|
//
|
|
CmRegistryIODebug.Action = CmpIoFileFlush;
|
|
CmRegistryIODebug.Handle = FileHandle;
|
|
CmRegistryIODebug.Status = status;
|
|
|
|
#ifndef _CM_LDR_
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpFileFlush:\tFailure1: status = %08lx IoStatus = %08lx\n",status,IoStatus.Status);
|
|
#endif //_CM_LDR_
|
|
|
|
#ifdef DRAGOSS_PRIVATE_DEBUG
|
|
DbgBreakPoint();
|
|
#endif //DRAGOSS_PRIVATE_DEBUG
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg()
|
|
#endif
|
|
|