/*++ 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 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; #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 ); 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); } 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 ); 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); } 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); 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 bellow // 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; (PUCHAR)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); 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; NTSTATUS Status; PVOID Bcb; PVOID FileBuffer; LARGE_INTEGER Offset; IO_STATUS_BLOCK IoStatus; ASSERT_PASSIVE_LEVEL(); 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;iFileObject,&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 syncronize access to the bellow; // 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; 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")); 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; } } 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; (PUCHAR)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]); ZwClose(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; } else { // // hive loaded into paged pool; or not primary // 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; #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; } return TRUE; }