Windows2000/private/ntos/config/cmwrapr.c
2020-09-30 17:12:32 +02:00

829 lines
20 KiB
C

/*++
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"
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,CmpFileRead)
#pragma alloc_text(PAGE,CmpFileWrite)
#pragma alloc_text(PAGE,CmpFileFlush)
#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;
// Storage management
PVOID
CmpAllocate(
ULONG Size,
BOOLEAN UseForIo
)
/*++
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;
#if DBG
PVOID Caller;
PVOID CallerCaller;
RtlGetCallersAddress(&Caller, &CallerCaller);
#endif
if (CmpClaimGlobalQuota(Size) == FALSE) {
return NULL;
}
pooltype = (UseForIo) ? PagedPoolCacheAligned : PagedPool;
result = ExAllocatePoolWithTag(pooltype, Size, CM_POOL_TAG);
#if DBG
CMLOG(CML_MINOR, CMS_POOL) {
KdPrint(("**CmpAllocate: allocate:%08lx, ", Size));
KdPrint(("type:%d, at:%08lx ", PagedPool, result));
KdPrint(("c:%08lx cc:%08lx\n", Caller, CallerCaller));
}
#endif
if (result == NULL) {
CmpReleaseGlobalQuota(Size);
}
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;
#if DBG
PVOID Caller;
PVOID CallerCaller;
RtlGetCallersAddress(&Caller, &CallerCaller);
#endif
if (CmpClaimGlobalQuota(Size) == FALSE) {
return NULL;
}
pooltype = (UseForIo) ? PagedPoolCacheAligned : PagedPool;
result = ExAllocatePoolWithTag(pooltype, Size, Tag);
#if DBG
CMLOG(CML_MINOR, CMS_POOL) {
KdPrint(("**CmpAllocate: allocate:%08lx, ", Size));
KdPrint(("type:%d, at:%08lx ", PagedPool, result));
KdPrint(("c:%08lx cc:%08lx\n", Caller, CallerCaller));
}
#endif
if (result == NULL) {
CmpReleaseGlobalQuota(Size);
}
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);
CMLOG(CML_MINOR, CMS_POOL) {
KdPrint(("**FREEING:%08lx c,cc:%08lx,%08lx\n", MemoryBlock, Caller, CallerCaller));
}
#endif
ASSERT(GlobalQuotaSize > 0);
CmpReleaseGlobalQuota(GlobalQuotaSize);
ExFreePool(MemoryBlock);
return;
}
NTSTATUS
CmpDoFileSetSize(
PHHIVE Hive,
ULONG FileType,
ULONG FileSize
)
/*++
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
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;
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;
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;
DbgPrint("CmpFileSetSize:\tHandle=%08lx NewLength=%08lx \n", FileHandle, FileSize);
}
// restore hard error popups mode
IoSetThreadHardErrorMode(oldFlag);
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;
}
CMLOG(CML_MAJOR, CMS_IO) {
KdPrint(("CmpFileRead:\n"));
KdPrint(("\tHandle=%08lx Offset=%08lx ", FileHandle, *FileOffset));
KdPrint(("Buffer=%08lx Length=%08lx\n", DataBuffer, DataLength));
}
// Detect attempt to read off end of 2gig file (this should be irrelevent)
if ((0xffffffff - *FileOffset) < DataLength) {
CMLOG(CML_MAJOR, CMS_IO_ERROR) KdPrint(("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;
(PUCHAR)DataBuffer += LengthToRead;
if (NT_SUCCESS(status)) {
ASSERT(IoStatus.Status == status);
if (IoStatus.Information != LengthToRead) {
CMLOG(CML_MAJOR, CMS_IO_ERROR) {
KdPrint(("CmpFileRead:\n\t"));
KdPrint(("Failure1: status = %08lx ", status));
KdPrint(("IoInformation = %08lx\n", IoStatus.Information));
}
ObDereferenceObject(eventObject);
ZwClose(eventHandle);
return FALSE;
}
} else {
// set debugging info
CmRegistryIODebug.Action = CmpIoFileRead;
CmRegistryIODebug.Handle = FileHandle;
CmRegistryIODebug.Status = status;
DbgPrint("CmpFileRead:\tFailure2: status = %08lx IoStatus = %08lx\n", status, IoStatus.Status);
ObDereferenceObject(eventObject);
ZwClose(eventHandle);
return FALSE;
}
}
ObDereferenceObject(eventObject);
ZwClose(eventHandle);
return TRUE;
}
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;
ULONG DataLength;
HANDLE eventHandles[MAXIMUM_WAIT_OBJECTS];
PKEVENT eventObjects[MAXIMUM_WAIT_OBJECTS];
KWAIT_BLOCK waitBlockArray[MAXIMUM_WAIT_OBJECTS];
IO_STATUS_BLOCK IoStatus[MAXIMUM_WAIT_OBJECTS];
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;
}
CMLOG(CML_MAJOR, CMS_IO) {
KdPrint(("CmpFileWrite:\n"));
KdPrint(("\tHandle=%08lx ", FileHandle));
}
for (idx = 0; idx < MAXIMUM_WAIT_OBJECTS; idx++) {
eventHandles[idx] = NULL;
}
// 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) {
CMLOG(CML_MAJOR, CMS_IO_ERROR) KdPrint(("CmpFileWrite: runoff\n"));
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 (eventHandles[WaitBufferCount] == NULL) {
status = CmpCreateEvent(SynchronizationEvent,
&eventHandles[WaitBufferCount],
&eventObjects[WaitBufferCount]);
if (!NT_SUCCESS(status)) {
// Make sure we don't try to clean this up.
eventHandles[WaitBufferCount] = NULL;
goto Error_Exit;
}
}
status = ZwWriteFile(FileHandle,
eventHandles[WaitBufferCount],
NULL, // apcroutine
NULL, // apccontext
&IoStatus[WaitBufferCount],
DataBuffer,
LengthToWrite,
&Offset,
NULL);
if (!NT_SUCCESS(status)) {
goto Error_Exit;
}
WaitBufferCount++;
// adjust offsets
*FileOffset = Offset.LowPart + LengthToWrite;
DataLength -= LengthToWrite;
(PUCHAR)DataBuffer += LengthToWrite;
} // while (DataLength > 0 && WaitBufferCount < MAXIMUM_WAIT_OBJECTS)
arrayCount++;
} // while (arrayCount < offsetArrayCount &&
// WaitBufferCount < MAXIMUM_WAIT_OBJECTS)
status = KeWaitForMultipleObjects(WaitBufferCount,
eventObjects,
WaitAll,
Executive,
KernelMode,
FALSE,
NULL,
waitBlockArray);
if (!NT_SUCCESS(status))
goto Error_Exit;
for (idx = 0; idx < WaitBufferCount; idx++) {
if (!NT_SUCCESS(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;
DbgPrint("CmpFileWrite: error exiting %d\n", status);
// 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) {
status = KeWaitForMultipleObjects(WaitBufferCount,
eventObjects,
WaitAll,
Executive,
KernelMode,
FALSE,
NULL,
waitBlockArray);
}
ret_val = FALSE;
Done:
idx = 0;
// Clean up open event handles and objects.
while ((idx < MAXIMUM_WAIT_OBJECTS) && (eventHandles[idx] != NULL)) {
ObDereferenceObject(eventObjects[idx]);
ZwClose(eventHandles[idx]);
idx++;
}
return ret_val;
}
BOOLEAN
CmpFileFlush (
PHHIVE Hive,
ULONG FileType
)
/*++
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
Return Value:
FALSE if failure
TRUE if success
--*/
{
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;
}
CMLOG(CML_MAJOR, CMS_IO) {
KdPrint(("CmpFileFlush:\n\tHandle = %08lx\n", FileHandle));
}
ASSERT_PASSIVE_LEVEL();
status = ZwFlushBuffersFile(
FileHandle,
&IoStatus
);
if (NT_SUCCESS(status)) {
ASSERT(IoStatus.Status == status);
return TRUE;
} else {
// set debugging info
CmRegistryIODebug.Action = CmpIoFileFlush;
CmRegistryIODebug.Handle = FileHandle;
CmRegistryIODebug.Status = status;
DbgPrint("CmpFileFlush:\tFailure1: status = %08lx IoStatus = %08lx\n",status,IoStatus.Status);
return FALSE;
}
return TRUE;
}