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; }
VOID HvpFillFileName( PHBASE_BLOCK BaseBlock, PUNICODE_STRING FileName ) /*++ Routine Description: Zero out the filename portion of the base block. If FileName is not NULL, copy last 64 bytes into name tail field of base block Arguments: BaseBlock - supplies pointer to a base block FileName - supplies pointer to a unicode STRING Return Value: None. --*/ { ULONG offset; ULONG length; PUCHAR sptr; CmKdPrintEx((DPFLTR_CONFIG_ID,CML_HIVE,"HvpFillFileName: %wZ\n", FileName)); RtlZeroMemory((PVOID)&(BaseBlock->FileName[0]), HBASE_NAME_ALLOC); if (FileName == NULL) { return; } // // Account for 0 at the end, so we have nice debug spews // if (FileName->Length < HBASE_NAME_ALLOC) { offset = 0; length = FileName->Length; } else { offset = FileName->Length - HBASE_NAME_ALLOC + sizeof(WCHAR); length = HBASE_NAME_ALLOC - sizeof(WCHAR); } sptr = (PUCHAR)&(FileName->Buffer[0]); RtlCopyMemory( (PVOID)&(BaseBlock->FileName[0]), (PVOID)&(sptr[offset]), length ); }
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; }
VOID HvFreeHive( PHHIVE Hive ) /*++ Routine Description: Free all of the pieces of a hive. Arguments: Hive - supplies a pointer to hive control structure for hive to free. this structure itself will NOT be freed, but everything it points to will. Return Value: NONE. --*/ { PHMAP_DIRECTORY Dir; PHMAP_ENTRY Me; HCELL_INDEX Address; ULONG Type; ULONG Length; PHBIN Bin; ULONG Tables; PFREE_HBIN FreeBin; ASSERT(Hive->Flat == FALSE); ASSERT(Hive->ReadOnly == FALSE); ASSERT(Stable == 0); ASSERT(Volatile == 1); CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"HvFreeHive(%ws) :\n", Hive->BaseBlock->FileName)); // // Iterate through both types of storage // for (Type = 0; Type <= Volatile; Type++) { Address = HCELL_TYPE_MASK * Type; Length = Hive->Storage[Type].Length + (HCELL_TYPE_MASK * Type); if( Hive->Storage[Type].Map && (Length > (HCELL_TYPE_MASK * Type)) ) { // // Sweep through bin set // do { Me = HvpGetCellMap(Hive, Address); VALIDATE_CELL_MAP(__LINE__,Me,Hive,Address); if (Me->BinAddress & HMAP_DISCARDABLE) { // // hbin is either discarded or discardable, check the tombstone // FreeBin = (PFREE_HBIN)Me->BlockAddress; Address += FreeBin->Size; if (FreeBin->Flags & FREE_HBIN_DISCARDABLE) { CmpFree((PHBIN)HBIN_BASE(Me->BinAddress), FreeBin->Size); } else if(Me->BinAddress & HMAP_INPAGEDPOOL) { // // The bin has been freed, but quota is still charged. // Since the hive is being freed, the quota must be // returned here. // CmpReleaseGlobalQuota(FreeBin->Size); } CmpFree(FreeBin, sizeof(FREE_HBIN)); } else { if( Me->BinAddress & HMAP_INPAGEDPOOL ) { ASSERT( Me->BinAddress & HMAP_INPAGEDPOOL ); Bin = (PHBIN)HBIN_BASE(Me->BinAddress); Address += HvpGetBinMemAlloc(Hive,Bin,Type); #if DBG if( Type == Stable ) { CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"HvFreeHive: BinAddress = 0x%p\t Size = 0x%lx\n", Bin, HvpGetBinMemAlloc(Hive,Bin,Type))); } #endif // // free the actual bin only if it is allocated from paged pool // if(HvpGetBinMemAlloc(Hive,Bin,Type)) { CmpFree(Bin, HvpGetBinMemAlloc(Hive,Bin,Type)); } } else { // // bin was mapped into view; advance carefully // Address += HBLOCK_SIZE; } } } while (Address < Length); // // Free map table storage // ASSERT(Hive->Storage[Type].Length != (HCELL_TYPE_MASK * Type)); Tables = (((Hive->Storage[Type].Length) / HBLOCK_SIZE)-1) / HTABLE_SLOTS; Dir = Hive->Storage[Type].Map; HvpFreeMap(Hive, Dir, 0, Tables); if (Tables > 0) { CmpFree(Hive->Storage[Type].Map, sizeof(HMAP_DIRECTORY)); // free dir if it exists } } Hive->Storage[Type].Length = 0; } CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"\n")); // // Free the base block // (Hive->Free)(Hive->BaseBlock,Hive->BaseBlockAlloc); Hive->BaseBlock = NULL; // // Free the dirty vector // if (Hive->DirtyVector.Buffer != NULL) { CmpFree((PVOID)(Hive->DirtyVector.Buffer), Hive->DirtyAlloc); } HvpFreeHiveFreeDisplay(Hive); LogHiveFree(Hive); return; }
NTSTATUS HvInitializeHive( PHHIVE Hive, ULONG OperationType, ULONG HiveFlags, ULONG FileType, PVOID HiveData OPTIONAL, PALLOCATE_ROUTINE AllocateRoutine, PFREE_ROUTINE FreeRoutine, PFILE_SET_SIZE_ROUTINE FileSetSizeRoutine, PFILE_WRITE_ROUTINE FileWriteRoutine, PFILE_READ_ROUTINE FileReadRoutine, PFILE_FLUSH_ROUTINE FileFlushRoutine, ULONG Cluster, PUNICODE_STRING FileName OPTIONAL ) /*++ Routine Description: Initialize a hive. Core HHive fields are always initializeed. File calls WILL be made BEFORE this call returns. Caller is expected to create/open files and store file handles in a way that can be derived from the hive pointer. Three kinds of initialization can be done, selected by OperationType: HINIT_CREATE Create a new hive from scratch. Will have 0 storage. [Used to do things like create HARDWARE hive and for parts of SaveKey and RestoreKey] HINIT_MEMORY_INPLACE Build a hive control structure which allows read only access to a contiguous in-memory image of a hive. No part of the image will be copied, but a map will be made. [Used by osloader.] HINIT_FLAT Support very limited (read-only, no checking code) operation against a hive image. HINIT_MEMORY Create a new hive, using a hive image already in memory, at address supplied by pointer HiveData. The data will be copied. Caller is expected to free HiveData. [Used for SYSTEM hive] HINIT_FILE Create a hive, reading its data from a file. Recovery processing via log file will be done if a log is available. If a log is recovered, flush and clear operation will proceed. HINIT_MAPFILE Create a hive, reading its data from a file. Data reading is done by mapping views of the file in the system cache. NOTE: The HHive is not a completely opaque structure, because it is really only used by a limited set of code. Do not assume that only this routine sets all of these values. Arguments: Hive - supplies a pointer to hive control structure to be initialized to describe this hive. OperationType - specifies whether to create a new hive from scratch, from a memory image, or by reading a file from disk. HiveFlags - HIVE_VOLATILE - Entire hive is to be volatile, regardless of the types of cells allocated HIVE_NO_LAZY_FLUSH - Data in this hive is never written to disk except by an explicit FlushKey FileType - HFILE_TYPE_*, HFILE_TYPE_LOG set up for logging support respectively. HiveData - if present, supplies a pointer to an in memory image of from which to init the hive. Only useful when OperationType is set to HINIT_MEMORY. AllocateRoutine - supplies a pointer to routine called to allocate memory. WILL be called before this routine returns. FreeRoutine - supplies a pointer to routine called to free memory. CAN be called before this routine returns. FileSetSizeRoutine - supplies a pointer to a routine used to set the size of a file. CAN be called before this routine returns. FileWriteRoutine - supplies a pointer to routine called to write memory to a file. FileReadRoutine - supplies a pointer to routine called to read from a file into memory. CAN be called before this routine returns. FileFlushRoutine - supplies a pointer to routine called to flush a file. Cluster - clustering factor in HSECTOR_SIZE units. (i.e. Size of physical sector in media / HSECTOR_SIZE. 1 for 512 byte physical sectors (or smaller), 2 for 1024, 4 for 2048, etc. (Numbers greater than 8 won't work.) FileName - some path like "...\system32\config\system", last 32 or so characters will be copied into baseblock (and thus to disk) as a debugging aid. May be null. Return Value: NTSTATUS code. --*/ { BOOLEAN UseForIo; PHBASE_BLOCK BaseBlock = NULL; NTSTATUS Status; ULONG i; ULONG Alignment; CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INIT,"HvInitializeHive:\n")); CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INIT,"\tHive=%p\n", Hive)); // // reject invalid parameter combinations // if ( (! ARGUMENT_PRESENT(HiveData)) && ((OperationType == HINIT_MEMORY) || (OperationType == HINIT_FLAT) || (OperationType == HINIT_MEMORY_INPLACE)) ) { return STATUS_INVALID_PARAMETER; } if ( ! ((OperationType == HINIT_CREATE) || (OperationType == HINIT_MEMORY) || (OperationType == HINIT_MEMORY_INPLACE) || (OperationType == HINIT_FLAT) || (OperationType == HINIT_FILE) || (OperationType == HINIT_MAPFILE)) ) { return STATUS_INVALID_PARAMETER; } // // static and global control values // Hive->Signature = HHIVE_SIGNATURE; Hive->Allocate = AllocateRoutine; Hive->Free = FreeRoutine; Hive->FileSetSize = FileSetSizeRoutine; Hive->FileWrite = FileWriteRoutine; Hive->FileRead = FileReadRoutine; Hive->FileFlush = FileFlushRoutine; Hive->Log = (BOOLEAN)((FileType == HFILE_TYPE_LOG) ? TRUE : FALSE); if (Hive->Log && (HiveFlags & HIVE_VOLATILE)) { return STATUS_INVALID_PARAMETER; } Hive->HiveFlags = HiveFlags; if ((Cluster == 0) || (Cluster > HSECTOR_COUNT)) { return STATUS_INVALID_PARAMETER; } Hive->Cluster = Cluster; Hive->RefreshCount = 0; Hive->StorageTypeCount = HTYPE_COUNT; Hive->Storage[Volatile].Length = 0; Hive->Storage[Volatile].Map = NULL; Hive->Storage[Volatile].SmallDir = NULL; Hive->Storage[Volatile].Guard = (ULONG)-1; Hive->Storage[Volatile].FreeSummary = 0; InitializeListHead(&Hive->Storage[Volatile].FreeBins); for (i = 0; i < HHIVE_FREE_DISPLAY_SIZE; i++) { RtlInitializeBitMap(&(Hive->Storage[Volatile].FreeDisplay[i].Display), NULL, 0); Hive->Storage[Volatile].FreeDisplay[i].RealVectorSize = 0; } Hive->Storage[Stable].Length = 0; Hive->Storage[Stable].Map = NULL; Hive->Storage[Stable].SmallDir = NULL; Hive->Storage[Stable].Guard = (ULONG)-1; Hive->Storage[Stable].FreeSummary = 0; InitializeListHead(&Hive->Storage[Stable].FreeBins); for (i = 0; i < HHIVE_FREE_DISPLAY_SIZE; i++) { RtlInitializeBitMap(&(Hive->Storage[Stable].FreeDisplay[i].Display), NULL, 0); Hive->Storage[Stable].FreeDisplay[i].RealVectorSize = 0; } RtlInitializeBitMap(&(Hive->DirtyVector), NULL, 0); Hive->DirtyCount = 0; Hive->DirtyAlloc = 0; Hive->DirtyFlag = FALSE; Hive->LogSize = 0; Hive->BaseBlockAlloc = sizeof(HBASE_BLOCK); Hive->GetCellRoutine = HvpGetCellPaged; Hive->ReleaseCellRoutine = NULL; Hive->Flat = FALSE; Hive->ReadOnly = FALSE; UseForIo = (BOOLEAN)!(Hive->HiveFlags & HIVE_VOLATILE); // // new create case // if (OperationType == HINIT_CREATE) { BaseBlock = (PHBASE_BLOCK)((Hive->Allocate)(Hive->BaseBlockAlloc, UseForIo,CM_FIND_LEAK_TAG11)); if (BaseBlock == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // // Make sure the buffer we got back is cluster-aligned. If not, try // harder to get an aligned buffer. // Alignment = Cluster * HSECTOR_SIZE - 1; if (((ULONG_PTR)BaseBlock & Alignment) != 0) { (Hive->Free)(BaseBlock, Hive->BaseBlockAlloc); BaseBlock = (PHBASE_BLOCK)((Hive->Allocate)(PAGE_SIZE, TRUE,CM_FIND_LEAK_TAG12)); if (BaseBlock == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } Hive->BaseBlockAlloc = PAGE_SIZE; } BaseBlock->Signature = HBASE_BLOCK_SIGNATURE; BaseBlock->Sequence1 = 1; BaseBlock->Sequence2 = 1; BaseBlock->TimeStamp.HighPart = 0; BaseBlock->TimeStamp.LowPart = 0; BaseBlock->Major = HSYS_MAJOR; BaseBlock->Minor = HSYS_MINOR; BaseBlock->Type = HFILE_TYPE_PRIMARY; BaseBlock->Format = HBASE_FORMAT_MEMORY; BaseBlock->RootCell = HCELL_NIL; BaseBlock->Length = 0; BaseBlock->Cluster = Cluster; BaseBlock->CheckSum = 0; HvpFillFileName(BaseBlock, FileName); Hive->BaseBlock = BaseBlock; Hive->Version = HSYS_MINOR; Hive->BaseBlock->BootType = 0; return STATUS_SUCCESS; } // // flat image case // if (OperationType == HINIT_FLAT) { Hive->BaseBlock = (PHBASE_BLOCK)HiveData; Hive->Version = Hive->BaseBlock->Minor; Hive->Flat = TRUE; Hive->ReadOnly = TRUE; Hive->GetCellRoutine = HvpGetCellFlat; Hive->Storage[Stable].Length = Hive->BaseBlock->Length; Hive->StorageTypeCount = 1; Hive->BaseBlock->BootType = 0; return STATUS_SUCCESS; } // // readonly image case // if (OperationType == HINIT_MEMORY_INPLACE) { BaseBlock = (PHBASE_BLOCK)HiveData; if ( (BaseBlock->Signature != HBASE_BLOCK_SIGNATURE) || (BaseBlock->Type != HFILE_TYPE_PRIMARY) || (BaseBlock->Major != HSYS_MAJOR) || (BaseBlock->Minor > HSYS_MINOR_SUPPORTED) || (BaseBlock->Format != HBASE_FORMAT_MEMORY) || (BaseBlock->Sequence1 != BaseBlock->Sequence2) || (HvpHeaderCheckSum(BaseBlock) != (BaseBlock->CheckSum)) ) { return STATUS_REGISTRY_CORRUPT; } Hive->BaseBlock = BaseBlock; Hive->Version = BaseBlock->Minor; Hive->ReadOnly = TRUE; Hive->StorageTypeCount = 1; Hive->BaseBlock->BootType = 0; Status = HvpAdjustHiveFreeDisplay(Hive,BaseBlock->Length,Stable); if( !NT_SUCCESS(Status) ) { return Status; } if ( !NT_SUCCESS(HvpBuildMap( Hive, (PUCHAR)HiveData + HBLOCK_SIZE ))) { return STATUS_REGISTRY_CORRUPT; } return(STATUS_SUCCESS); } // // memory copy case // if (OperationType == HINIT_MEMORY) { BaseBlock = (PHBASE_BLOCK)HiveData; if ( (BaseBlock->Signature != HBASE_BLOCK_SIGNATURE) || (BaseBlock->Type != HFILE_TYPE_PRIMARY) || (BaseBlock->Format != HBASE_FORMAT_MEMORY) || (BaseBlock->Major != HSYS_MAJOR) || (BaseBlock->Minor > HSYS_MINOR_SUPPORTED) || (HvpHeaderCheckSum(BaseBlock) != (BaseBlock->CheckSum)) ) { return STATUS_REGISTRY_CORRUPT; } Hive->BaseBlock = (PHBASE_BLOCK)((Hive->Allocate)(Hive->BaseBlockAlloc, UseForIo,CM_FIND_LEAK_TAG13)); if (Hive->BaseBlock==NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } // // Make sure the buffer we got back is cluster-aligned. If not, try // harder to get an aligned buffer. // Alignment = Cluster * HSECTOR_SIZE - 1; if (((ULONG_PTR)Hive->BaseBlock & Alignment) != 0) { (Hive->Free)(Hive->BaseBlock, Hive->BaseBlockAlloc); Hive->BaseBlock = (PHBASE_BLOCK)((Hive->Allocate)(PAGE_SIZE, TRUE,CM_FIND_LEAK_TAG14)); if (Hive->BaseBlock == NULL) { return (STATUS_INSUFFICIENT_RESOURCES); } Hive->BaseBlockAlloc = PAGE_SIZE; } RtlCopyMemory(Hive->BaseBlock, BaseBlock, HSECTOR_SIZE); Hive->BaseBlock->BootRecover = BaseBlock->BootRecover; Hive->BaseBlock->BootType = BaseBlock->BootType; Hive->Version = Hive->BaseBlock->Minor; Status = HvpAdjustHiveFreeDisplay(Hive,BaseBlock->Length,Stable); if( !NT_SUCCESS(Status) ) { (Hive->Free)(Hive->BaseBlock, Hive->BaseBlockAlloc); Hive->BaseBlock = NULL; return Status; } if ( !NT_SUCCESS(HvpBuildMapAndCopy(Hive, (PUCHAR)HiveData + HBLOCK_SIZE))) { (Hive->Free)(Hive->BaseBlock, Hive->BaseBlockAlloc); Hive->BaseBlock = NULL; return STATUS_REGISTRY_CORRUPT; } HvpFillFileName(Hive->BaseBlock, FileName); return(STATUS_SUCCESS); } // // file read case // if (OperationType == HINIT_FILE) { CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"HvInitializeHive(%wZ,HINIT_FILE) :\n", FileName)); // // get the file image (possible recovered via log) into memory // Status = HvLoadHive(Hive); if ((Status != STATUS_SUCCESS) && (Status != STATUS_REGISTRY_RECOVERED)) { return Status; } CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"\n")); if (Status == STATUS_REGISTRY_RECOVERED) { // // We have a good hive, with a log, and a dirty map, // all set up. Only problem is that we need to flush // the file so the log can be cleared and new writes // posted against the hive. Since we know we have // a good log in hand, we just write the hive image. // if ( ! HvpDoWriteHive(Hive, HFILE_TYPE_PRIMARY)) { // // Clean up the bins already allocated // HvpFreeAllocatedBins( Hive ); return STATUS_REGISTRY_IO_FAILED; } // // If we get here, we have recovered the hive, and // written it out to disk correctly. So we clear // the log here. // RtlClearAllBits(&(Hive->DirtyVector)); Hive->DirtyCount = 0; (Hive->FileSetSize)(Hive, HFILE_TYPE_LOG, 0,0); Hive->LogSize = 0; } // // slam debug name data into base block // HvpFillFileName(Hive->BaseBlock, FileName); return STATUS_SUCCESS; } // // file map case // if (OperationType == HINIT_MAPFILE) { Hive->GetCellRoutine = HvpGetCellMapped; Hive->ReleaseCellRoutine = HvpReleaseCellMapped; CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"HvInitializeHive(%wZ,HINIT_MAPFILE) :\n", FileName)); Status = HvMapHive(Hive); if ((Status != STATUS_SUCCESS) && (Status != STATUS_REGISTRY_RECOVERED)) { return Status; } CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"\n")); if (Status == STATUS_REGISTRY_RECOVERED) { // // We have a good hive, with a log, and a dirty map, // all set up. Only problem is that we need to flush // the file so the log can be cleared and new writes // posted against the hive. Since we know we have // a good log in hand, we just write the hive image. // if ( ! HvpDoWriteHive(Hive, HFILE_TYPE_PRIMARY)) { // // Clean up the bins already allocated // HvpFreeAllocatedBins( Hive ); return STATUS_REGISTRY_IO_FAILED; } // // If we get here, we have recovered the hive, and // written it out to disk correctly. So we clear // the log here. // RtlClearAllBits(&(Hive->DirtyVector)); Hive->DirtyCount = 0; (Hive->FileSetSize)(Hive, HFILE_TYPE_LOG, 0,0); Hive->LogSize = 0; } // // slam debug name data into base block // HvpFillFileName(Hive->BaseBlock, FileName); return STATUS_SUCCESS; } return STATUS_INVALID_PARAMETER; }
VALUE_SEARCH_RETURN_TYPE CmpFindValueByNameFromCache( IN PCM_KEY_CONTROL_BLOCK KeyControlBlock, IN PUNICODE_STRING Name, OUT PPCM_CACHED_VALUE *ContainingList, OUT ULONG *Index, OUT PCM_KEY_VALUE *Value, OUT BOOLEAN *ValueCached, OUT PHCELL_INDEX CellToRelease ) /*++ Routine Description: Find a value node given a value list array and a value name. It sequentially walk through each value node to look for a match. If the array and value nodes touched are not already cached, cache them. Arguments: Hive - pointer to hive control structure for hive of interest ChildList - pointer/index to the Value Index array Name - name of value to find ContainlingList - The address of the entry that will receive the found cached value. Index - pointer to variable to receive index for child ValueCached - Indicate if the value node is cached. Return Value: HCELL_INDEX for the found cell HCELL_NIL if not found Notes: New hives (Minor >= 4) have ValueList sorted; this implies ValueCache is sorted too; So, we can do a binary search here! --*/ { UNICODE_STRING Candidate; LONG Result; PCELL_DATA List; BOOLEAN IndexCached; ULONG Current; HCELL_INDEX ValueListToRelease = HCELL_NIL; ULONG HashKey = 0; PHHIVE Hive = KeyControlBlock->KeyHive; PCACHED_CHILD_LIST ChildList = &(KeyControlBlock->ValueCache); VALUE_SEARCH_RETURN_TYPE ret = SearchFail; ASSERT_KCB_LOCKED(KeyControlBlock); *CellToRelease = HCELL_NIL; *Value = NULL; if (ChildList->Count != 0) { ret = CmpGetValueListFromCache(KeyControlBlock,&List, &IndexCached,&ValueListToRelease); if( ret != SearchSuccess ) { // // retry with exclusive lock, since we need to update the cache // or fail altogether // ASSERT( (ret == SearchFail) || (CmpIsKCBLockedExclusive(KeyControlBlock) == FALSE) ); ASSERT( ValueListToRelease == HCELL_NIL ); return ret; } if( IndexCached ) { try { HashKey = CmpComputeHashKey(0,Name #if DBG , TRUE #endif ); } except (EXCEPTION_EXECUTE_HANDLER) { ret = SearchFail; goto Exit; } } // // old plain hive; simulate a for // Current = 0; while( TRUE ) { if( *CellToRelease != HCELL_NIL ) { HvReleaseCell(Hive,*CellToRelease); *CellToRelease = HCELL_NIL; } ret = CmpGetValueKeyFromCache(KeyControlBlock, List, Current, ContainingList, Value, IndexCached, ValueCached, CellToRelease); if( ret != SearchSuccess ) { // // retry with exclusive lock, since we need to update the cache // or fail altogether // ASSERT( (ret == SearchFail) || (CmpIsKCBLockedExclusive(KeyControlBlock) == FALSE) ); ASSERT( *CellToRelease == HCELL_NIL ); return ret; } // // only compare names when hash matches. // if( IndexCached && (*ValueCached) && (HashKey != ((PCM_CACHED_VALUE)((CMP_GET_CACHED_ADDRESS(**ContainingList))))->HashKey) ) { // // no hash match; skip it // #if DBG try { // // Name has user-mode buffer. // if ((*Value)->Flags & VALUE_COMP_NAME) { Result = CmpCompareCompressedName( Name, (*Value)->Name, (*Value)->NameLength, 0); } else { Candidate.Length = (*Value)->NameLength; Candidate.MaximumLength = Candidate.Length; Candidate.Buffer = (*Value)->Name; Result = RtlCompareUnicodeString( Name, &Candidate, TRUE); } } except (EXCEPTION_EXECUTE_HANDLER) { CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"CmpFindValueByNameFromCache: code:%08lx\n", GetExceptionCode())); // // the caller will bail out. Some ,alicious caller altered the Name buffer since we probed it. // *Value = NULL; ret = SearchFail; goto Exit; } ASSERT( Result != 0 ); #endif Result = 1; } else { try { // // Name has user-mode buffer. // if( (*Value)->Flags & VALUE_COMP_NAME) { Result = CmpCompareCompressedName( Name, (*Value)->Name, (*Value)->NameLength, 0); } else { Candidate.Length = (*Value)->NameLength; Candidate.MaximumLength = Candidate.Length; Candidate.Buffer = (*Value)->Name; Result = RtlCompareUnicodeString( Name, &Candidate, TRUE); } } except (EXCEPTION_EXECUTE_HANDLER) { CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"CmpFindValueByNameFromCache: code:%08lx\n", GetExceptionCode())); // // the caller will bail out. Some ,alicious caller altered the Name buffer since we probed it. // *Value = NULL; ret = SearchFail; goto Exit; } } if (Result == 0) { // // Success, fill the index, return data to caller and exit // *Index = Current; ret = SearchSuccess; goto Exit; } // // compute the next index to try: old'n plain hive; go on // Current++; if( Current == ChildList->Count ) { // // we've reached the end of the list; nicely return // (*Value) = NULL; ret = SearchFail; goto Exit; } } // while(TRUE)
VOID CmpDeleteKeyObject( IN PVOID Object ) /*++ Routine Description: This routine interfaces to the NT Object Manager. It is invoked when the last reference to a particular Key object (or Key Root object) is destroyed. If the Key object going away holds the last reference to the extension it is associated with, that extension is destroyed. Arguments: Object - supplies a pointer to a KeyRoot or Key, thus -> KEY_BODY. Return Value: NONE. --*/ { PCM_KEY_CONTROL_BLOCK KeyControlBlock; PCM_KEY_BODY KeyBody; PCMHIVE CmHive = NULL; BOOLEAN DoUnloadCheck = FALSE; CM_PAGED_CODE(); CmKdPrintEx((DPFLTR_CONFIG_ID,CML_FLOW,"CmpDeleteKeyObject: Object = %p\n", Object)); // // HandleClose callback // if ( CmAreCallbacksRegistered() ) { REG_KEY_HANDLE_CLOSE_INFORMATION KeyHandleCloseInfo; KeyHandleCloseInfo.Object = Object; CmpCallCallBacks(RegNtPreKeyHandleClose,&KeyHandleCloseInfo,TRUE,RegNtPostKeyHandleClose,Object); } KeyBody = (PCM_KEY_BODY)Object; BEGIN_LOCK_CHECKPOINT; CmpLockRegistry(); if (KeyBody->Type==KEY_BODY_TYPE) { KeyControlBlock = KeyBody->KeyControlBlock; // // the keybody should be initialized; when kcb is null, something went wrong // between the creation and the dereferenciation of the object // if( KeyControlBlock != NULL ) { // // Clean up any outstanding notifies attached to the KeyBody // CmpFlushNotify(KeyBody,FALSE); // // Remove our reference to the KeyControlBlock, clean it up, perform any // pend-till-final-close operations. // // NOTE: Delete notification is seen at the parent of the deleted key, // not the deleted key itself. If any notify was outstanding on // this key, it was cleared away above us. Only parent/ancestor // keys will see the report. // // // The dereference will free the KeyControlBlock. If the key was deleted, it // has already been removed from the hash table, and relevant notifications // posted then as well. All we are doing is freeing the tombstone. // // If it was not deleted, we're both cutting the kcb out of // the kcb list/tree, AND freeing its storage. // // // Replace this with the definition so we avoid dropping and reacquiring the lock DelistKeyBodyFromKCB(KeyBody,FALSE); // // take additional precaution in the case the hive has been unloaded and this is the root // if( !KeyControlBlock->Delete ) { CmHive = (PCMHIVE)CONTAINING_RECORD(KeyControlBlock->KeyHive, CMHIVE, Hive); if( IsHiveFrozen(CmHive) ) { // // unload is pending for this hive; // DoUnloadCheck = TRUE; } } CmpDelayDerefKeyControlBlock(KeyControlBlock); } } else { // // This must be a predefined handle // some sanity asserts // KeyControlBlock = KeyBody->KeyControlBlock; ASSERT( KeyBody->Type®_PREDEF_HANDLE_MASK); ASSERT( KeyControlBlock->Flags&KEY_PREDEF_HANDLE ); if( KeyControlBlock != NULL ) { CmHive = (PCMHIVE)CONTAINING_RECORD(KeyControlBlock->KeyHive, CMHIVE, Hive); if( IsHiveFrozen(CmHive) ) { // // unload is pending for this hive; we shouldn't put the kcb in the delay // close table // DoUnloadCheck = TRUE; } CmpDereferenceKeyControlBlock(KeyControlBlock); } } // // if a handle inside a frozen hive has been closed, we may need to unload the hive // if( DoUnloadCheck == TRUE ) { CmpDoQueueLateUnloadWorker(CmHive); } CmpUnlockRegistry(); END_LOCK_CHECKPOINT; // // just a notification; disregard the return status // CmPostCallbackNotification(RegNtPostKeyHandleClose,NULL,STATUS_SUCCESS); return; }
VALUE_SEARCH_RETURN_TYPE CmpQueryKeyValueData( PCM_KEY_CONTROL_BLOCK KeyControlBlock, PPCM_CACHED_VALUE ContainingList, PCM_KEY_VALUE ValueKey, BOOLEAN ValueCached, KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, PVOID KeyValueInformation, ULONG Length, PULONG ResultLength, NTSTATUS *status ) /*++ Routine Description: Do the actual copy of data for a key value into caller's buffer. If KeyValueInformation is not long enough to hold all requested data, STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be set to the number of bytes actually required. Arguments: Hive - supplies a pointer to the hive control structure for the hive Cell - supplies index of node to whose sub keys are to be found KeyValueInformationClass - Specifies the type of information returned in KeyValueInformation. One of the following types: KeyValueInformation -Supplies pointer to buffer to receive the data. Length - Length of KeyInformation in bytes. ResultLength - Number of bytes actually written into KeyInformation. Return Value: NTSTATUS --*/ { PKEY_VALUE_INFORMATION pbuffer; PCELL_DATA pcell; LONG leftlength; ULONG requiredlength; ULONG minimumlength; ULONG offset; ULONG base; ULONG realsize; PUCHAR datapointer; BOOLEAN small; USHORT NameLength; BOOLEAN BufferAllocated = FALSE; HCELL_INDEX CellToRelease = HCELL_NIL; PHHIVE Hive; VALUE_SEARCH_RETURN_TYPE SearchValue = SearchSuccess; Hive = KeyControlBlock->KeyHive; pbuffer = (PKEY_VALUE_INFORMATION)KeyValueInformation; pcell = (PCELL_DATA) ValueKey; NameLength = CmpValueNameLen(&pcell->u.KeyValue); switch (KeyValueInformationClass) { case KeyValueBasicInformation: // // TitleIndex, Type, NameLength, Name // requiredlength = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name) + NameLength; minimumlength = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name); *ResultLength = requiredlength; *status = STATUS_SUCCESS; if (Length < minimumlength) { *status = STATUS_BUFFER_TOO_SMALL; } else { pbuffer->KeyValueBasicInformation.TitleIndex = 0; pbuffer->KeyValueBasicInformation.Type = pcell->u.KeyValue.Type; pbuffer->KeyValueBasicInformation.NameLength = NameLength; leftlength = Length - minimumlength; requiredlength = NameLength; if (leftlength < (LONG)requiredlength) { requiredlength = leftlength; *status = STATUS_BUFFER_OVERFLOW; } if (pcell->u.KeyValue.Flags & VALUE_COMP_NAME) { CmpCopyCompressedName(pbuffer->KeyValueBasicInformation.Name, requiredlength, pcell->u.KeyValue.Name, pcell->u.KeyValue.NameLength); } else { RtlCopyMemory(&(pbuffer->KeyValueBasicInformation.Name[0]), &(pcell->u.KeyValue.Name[0]), requiredlength); } } break; case KeyValueFullInformation: case KeyValueFullInformationAlign64: // // TitleIndex, Type, DataOffset, DataLength, NameLength, // Name, Data // small = CmpIsHKeyValueSmall(realsize, pcell->u.KeyValue.DataLength); requiredlength = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) + NameLength + realsize; minimumlength = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name); offset = 0; if (realsize > 0) { base = requiredlength - realsize; #if defined(_WIN64) offset = ALIGN_OFFSET64(base); #else if (KeyValueInformationClass == KeyValueFullInformationAlign64) { offset = ALIGN_OFFSET64(base); } else { offset = ALIGN_OFFSET(base); } #endif if (offset > base) { requiredlength += (offset - base); } #if DBG && defined(_WIN64) // // Some clients will have passed in a structure that they "know" // will be exactly the right size. The fact that alignment // has changed on NT64 may cause these clients to have problems. // // The solution is to fix the client, but print out some debug // spew here if it looks like this is the case. This problem // isn't particularly easy to spot from the client end. // if((KeyValueInformationClass == KeyValueFullInformation) && (Length != minimumlength) && (requiredlength > Length) && ((requiredlength - Length) <= (ALIGN_OFFSET64(base) - ALIGN_OFFSET(base)))) { CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"ntos/config-64 KeyValueFullInformation: " "Possible client buffer size problem.\n")); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL," Required size = %d\n", requiredlength)); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL," Supplied size = %d\n", Length)); } #endif } *ResultLength = requiredlength; *status = STATUS_SUCCESS; if (Length < minimumlength) { *status = STATUS_BUFFER_TOO_SMALL; } else { pbuffer->KeyValueFullInformation.TitleIndex = 0; pbuffer->KeyValueFullInformation.Type = pcell->u.KeyValue.Type; pbuffer->KeyValueFullInformation.DataLength = realsize; pbuffer->KeyValueFullInformation.NameLength = NameLength; leftlength = Length - minimumlength; requiredlength = NameLength; if (leftlength < (LONG)requiredlength) { requiredlength = leftlength; *status = STATUS_BUFFER_OVERFLOW; } if (pcell->u.KeyValue.Flags & VALUE_COMP_NAME) { CmpCopyCompressedName(pbuffer->KeyValueFullInformation.Name, requiredlength, pcell->u.KeyValue.Name, pcell->u.KeyValue.NameLength); } else { RtlCopyMemory( &(pbuffer->KeyValueFullInformation.Name[0]), &(pcell->u.KeyValue.Name[0]), requiredlength ); } if (realsize > 0) { if (small == TRUE) { datapointer = (PUCHAR)(&(pcell->u.KeyValue.Data)); } else { SearchValue = CmpGetValueDataFromCache(KeyControlBlock, ContainingList, pcell, ValueCached,&datapointer,&BufferAllocated,&CellToRelease); if( SearchValue != SearchSuccess ) { ASSERT( datapointer == NULL ); ASSERT( BufferAllocated == FALSE ); *status = STATUS_INSUFFICIENT_RESOURCES; } } pbuffer->KeyValueFullInformation.DataOffset = offset; leftlength = (((LONG)Length - (LONG)offset) < 0) ? 0 : Length - offset; requiredlength = realsize; if (leftlength < (LONG)requiredlength) { requiredlength = leftlength; *status = STATUS_BUFFER_OVERFLOW; } ASSERT((small ? (requiredlength <= CM_KEY_VALUE_SMALL) : TRUE)); if( datapointer != NULL ) { try { RtlCopyMemory( ((PUCHAR)pbuffer + offset), datapointer, requiredlength ); } finally { if( BufferAllocated == TRUE ) { ExFreePool(datapointer); } if( CellToRelease != HCELL_NIL ) { HvReleaseCell(Hive,CellToRelease); } } } } else { pbuffer->KeyValueFullInformation.DataOffset = (ULONG)-1; } } break; case KeyValuePartialInformation: // // TitleIndex, Type, DataLength, Data // small = CmpIsHKeyValueSmall(realsize, pcell->u.KeyValue.DataLength); requiredlength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + realsize; minimumlength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data); *ResultLength = requiredlength; *status = STATUS_SUCCESS; if (Length < minimumlength) { *status = STATUS_BUFFER_TOO_SMALL; } else { pbuffer->KeyValuePartialInformation.TitleIndex = 0; pbuffer->KeyValuePartialInformation.Type = pcell->u.KeyValue.Type; pbuffer->KeyValuePartialInformation.DataLength = realsize; leftlength = Length - minimumlength; requiredlength = realsize; if (leftlength < (LONG)requiredlength) { requiredlength = leftlength; *status = STATUS_BUFFER_OVERFLOW; } if (realsize > 0) { if (small == TRUE) { datapointer = (PUCHAR)(&(pcell->u.KeyValue.Data)); } else { SearchValue = CmpGetValueDataFromCache(KeyControlBlock, ContainingList, pcell, ValueCached,&datapointer,&BufferAllocated,&CellToRelease); if( SearchValue != SearchSuccess ) { ASSERT( datapointer == NULL ); ASSERT( BufferAllocated == FALSE ); *status = STATUS_INSUFFICIENT_RESOURCES; } } ASSERT((small ? (requiredlength <= CM_KEY_VALUE_SMALL) : TRUE)); if( datapointer != NULL ) { try { RtlCopyMemory((PUCHAR)&(pbuffer->KeyValuePartialInformation.Data[0]), datapointer, requiredlength); } finally { if( BufferAllocated == TRUE ) { ExFreePool(datapointer); } if(CellToRelease != HCELL_NIL) { HvReleaseCell(Hive,CellToRelease); } } } } } break; case KeyValuePartialInformationAlign64: // // TitleIndex, Type, DataLength, Data // small = CmpIsHKeyValueSmall(realsize, pcell->u.KeyValue.DataLength); requiredlength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, Data) + realsize; minimumlength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, Data); *ResultLength = requiredlength; *status = STATUS_SUCCESS; if (Length < minimumlength) { *status = STATUS_BUFFER_TOO_SMALL; } else { pbuffer->KeyValuePartialInformationAlign64.Type = pcell->u.KeyValue.Type; pbuffer->KeyValuePartialInformationAlign64.DataLength = realsize; leftlength = Length - minimumlength; requiredlength = realsize; if (leftlength < (LONG)requiredlength) { requiredlength = leftlength; *status = STATUS_BUFFER_OVERFLOW; } if (realsize > 0) { if (small == TRUE) { datapointer = (PUCHAR)(&(pcell->u.KeyValue.Data)); } else { SearchValue = CmpGetValueDataFromCache(KeyControlBlock, ContainingList, pcell, ValueCached,&datapointer,&BufferAllocated,&CellToRelease); if( SearchValue != SearchSuccess ) { ASSERT( datapointer == NULL ); ASSERT( BufferAllocated == FALSE ); *status = STATUS_INSUFFICIENT_RESOURCES; } } ASSERT((small ? (requiredlength <= CM_KEY_VALUE_SMALL) : TRUE)); if( datapointer != NULL ) { try { RtlCopyMemory((PUCHAR)&(pbuffer->KeyValuePartialInformationAlign64.Data[0]), datapointer, requiredlength); } finally { if( BufferAllocated == TRUE ) { ExFreePool(datapointer); } if(CellToRelease != HCELL_NIL) { HvReleaseCell(Hive,CellToRelease); } } } } } break; default: *status = STATUS_INVALID_PARAMETER; break; }
NTSTATUS CmpQueryKeyDataFromCache( PCM_KEY_CONTROL_BLOCK Kcb, KEY_INFORMATION_CLASS KeyInformationClass, PVOID KeyInformation, ULONG Length, PULONG ResultLength ) /*++ Routine Description: Do the actual copy of data for a key into caller's buffer. If KeyInformation is not long enough to hold all requested data, STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be set to the number of bytes actually required. Works only for the information cached into kcb. I.e. KeyBasicInformation and KeyCachedInfo Arguments: Kcb - Supplies pointer to the kcb to be queried KeyInformationClass - Specifies the type of information returned in Buffer. One of the following types: KeyBasicInformation - return last write time, title index, and name. (see KEY_BASIC_INFORMATION structure) KeyCachedInformation - return last write time, title index, name .... (see KEY_CACHED_INFORMATION structure) KeyInformation -Supplies pointer to buffer to receive the data. Length - Length of KeyInformation in bytes. ResultLength - Number of bytes actually written into KeyInformation. Return Value: NTSTATUS --*/ { NTSTATUS status; PKEY_INFORMATION pbuffer; ULONG requiredlength; USHORT NameLength; PCM_KEY_NODE Node; // this is to be used only in case of cache incoherency CM_PAGED_CODE(); // // we cannot afford to return the kcb NameBlock as the key name // for KeyBasicInformation as there are lots of callers expecting // the name to be case-sensitive; KeyCachedInformation is new // and used only by the Win32 layer, which is not case sensitive // Note: future clients of KeyCachedInformation must be made aware // that name is NOT case-sensitive // ASSERT( KeyInformationClass == KeyCachedInformation ); // // we are going to need the nameblock; if it is NULL, bail out // if( Kcb->NameBlock == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; } pbuffer = (PKEY_INFORMATION)KeyInformation; if (Kcb->NameBlock->Compressed) { NameLength = CmpCompressedNameSize(Kcb->NameBlock->Name,Kcb->NameBlock->NameLength); } else { NameLength = Kcb->NameBlock->NameLength; } // Assume success status = STATUS_SUCCESS; switch (KeyInformationClass) { case KeyCachedInformation: // // LastWriteTime, TitleIndex, // SubKeys, MaxNameLen, Values, MaxValueNameLen, // MaxValueDataLen, Name // requiredlength = sizeof(KEY_CACHED_INFORMATION); *ResultLength = requiredlength; if (Length < requiredlength) { status = STATUS_BUFFER_TOO_SMALL; } else { pbuffer->KeyCachedInformation.LastWriteTime = Kcb->KcbLastWriteTime; pbuffer->KeyCachedInformation.TitleIndex = 0; pbuffer->KeyCachedInformation.NameLength = NameLength; pbuffer->KeyCachedInformation.Values = Kcb->ValueCache.Count; pbuffer->KeyCachedInformation.MaxNameLen = Kcb->KcbMaxNameLen; pbuffer->KeyCachedInformation.MaxValueNameLen = Kcb->KcbMaxValueNameLen; pbuffer->KeyCachedInformation.MaxValueDataLen = Kcb->KcbMaxValueDataLen; if( !(Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO) ) { // there is some cached info if( Kcb->ExtFlags & CM_KCB_NO_SUBKEY ) { pbuffer->KeyCachedInformation.SubKeys = 0; } else if( Kcb->ExtFlags & CM_KCB_SUBKEY_ONE ) { pbuffer->KeyCachedInformation.SubKeys = 1; } else if( Kcb->ExtFlags & CM_KCB_SUBKEY_HINT ) { pbuffer->KeyCachedInformation.SubKeys = Kcb->IndexHint->Count; } else { pbuffer->KeyCachedInformation.SubKeys = Kcb->SubKeyCount; } } else { // // kcb cache is not coherent; get the info from knode // CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"Kcb cache incoherency detected, kcb = %p\n",Kcb)); Node = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive,Kcb->KeyCell); if( Node == NULL ) { // // couldn't map view for this cell // status = STATUS_INSUFFICIENT_RESOURCES; break; } pbuffer->KeyCachedInformation.SubKeys = Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]; HvReleaseCell(Kcb->KeyHive,Kcb->KeyCell); } } break; default: status = STATUS_INVALID_PARAMETER; break; } return status; }
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; #if DBG DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpFileFlush:\tFailure1: status = %08lx IoStatus = %08lx\n",status,IoStatus.Status); #endif return FALSE; } }
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; PCM_WRITE_BLOCK WriteBlock = NULL; 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)); // // 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. try { 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; } } } except (EXCEPTION_EXECUTE_HANDLER) { // // we might get STATUS_IN_PAGE_ERROR // CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpFileWrite has raised :%08lx\n",GetExceptionCode())); return FALSE; } WriteBlock = (PCM_WRITE_BLOCK)ExAllocatePool(NonPagedPool,sizeof(CM_WRITE_BLOCK)); if( WriteBlock == NULL ) { return FALSE; } for (idx = 0; idx < MAXIMUM_WAIT_OBJECTS; idx++) { WriteBlock->EventHandles[idx] = NULL; #if DBG WriteBlock->EventObjects[idx] = NULL; #endif } // // 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 irrelevant) // 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 (WriteBlock->EventHandles[WaitBufferCount] == NULL) { status = CmpCreateEvent(SynchronizationEvent, &(WriteBlock->EventHandles[WaitBufferCount]), &(WriteBlock->EventObjects[WaitBufferCount])); if (!NT_SUCCESS(status)) { // Make sure we don't try to clean this up. WriteBlock->EventHandles[WaitBufferCount] = NULL; goto Error_Exit; } CmpSetHandleProtection(WriteBlock->EventHandles[WaitBufferCount],TRUE); } status = ZwWriteFile(FileHandle, WriteBlock->EventHandles[WaitBufferCount], NULL, // apcroutine NULL, // apccontext &(WriteBlock->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, WriteBlock->EventObjects, WaitAll, Executive, KernelMode, FALSE, NULL, WriteBlock->WaitBlockArray); if (!NT_SUCCESS(status)) goto Error_Exit; for (idx = 0; idx < WaitBufferCount; idx++) { if (!NT_SUCCESS(WriteBlock->IoStatus[idx].Status)) { status = WriteBlock->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; #if DBG DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpFileWrite: error exiting %d\n", status); #endif // // 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, WriteBlock->EventObjects, WaitAll, Executive, KernelMode, FALSE, NULL, WriteBlock->WaitBlockArray); } ret_val = FALSE; Done: idx = 0; // Clean up open event handles and objects. while ((idx < MAXIMUM_WAIT_OBJECTS) && (WriteBlock->EventHandles[idx] != NULL)) { ASSERT( WriteBlock->EventObjects[idx] ); ObDereferenceObject(WriteBlock->EventObjects[idx]); CmCloseHandle(WriteBlock->EventHandles[idx]); idx++; } if( WriteBlock != NULL ) { ExFreePool(WriteBlock); } return ret_val; }
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 deferred. 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) ); 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 overlap with this range ; not needed !!!! // // // 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; }
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 irrelevant) // 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; #if DBG DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpFileRead:\tFailure2: status = %08lx IoStatus = %08lx\n", status, IoStatus.Status); #endif ObDereferenceObject(eventObject); ZwClose(eventHandle); return FALSE; } } ObDereferenceObject(eventObject); ZwClose(eventHandle); return TRUE; }