VALUE_SEARCH_RETURN_TYPE NTAPI CmpQueryKeyValueData(IN PCM_KEY_CONTROL_BLOCK Kcb, IN PCM_CACHED_VALUE *CachedValue, IN PCM_KEY_VALUE ValueKey, IN BOOLEAN ValueIsCached, IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, IN PVOID KeyValueInformation, IN ULONG Length, OUT PULONG ResultLength, OUT PNTSTATUS Status) { PKEY_VALUE_INFORMATION Info = (PKEY_VALUE_INFORMATION)KeyValueInformation; PCELL_DATA CellData; USHORT NameSize; ULONG Size, MinimumSize, SizeLeft, KeySize, AlignedData = 0, DataOffset; PVOID Buffer; BOOLEAN IsSmall, BufferAllocated = FALSE; HCELL_INDEX CellToRelease = HCELL_NIL; VALUE_SEARCH_RETURN_TYPE Result = SearchSuccess; /* Get the value data */ CellData = (PCELL_DATA)ValueKey; /* Check if the value is compressed */ if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME) { /* Get the compressed name size */ NameSize = CmpCompressedNameSize(CellData->u.KeyValue.Name, CellData->u.KeyValue.NameLength); } else { /* Get the real size */ NameSize = CellData->u.KeyValue.NameLength; } /* Check what kind of information the caller is requesting */ switch (KeyValueInformationClass) { /* Basic information */ case KeyValueBasicInformation: /* This is how much size we'll need */ Size = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name) + NameSize; /* This is the minimum we can work with */ MinimumSize = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name); /* Return the size we'd like, and assume success */ *ResultLength = Size; *Status = STATUS_SUCCESS; /* Check if the caller gave us below our minimum */ if (Length < MinimumSize) { /* Then we must fail */ *Status = STATUS_BUFFER_TOO_SMALL; break; } /* Fill out the basic information */ Info->KeyValueBasicInformation.TitleIndex = 0; Info->KeyValueBasicInformation.Type = CellData->u.KeyValue.Type; Info->KeyValueBasicInformation.NameLength = NameSize; /* Now only the name is left */ SizeLeft = Length - MinimumSize; Size = NameSize; /* Check if the remaining buffer is too small for the name */ if (SizeLeft < Size) { /* Copy only as much as can fit, and tell the caller */ Size = SizeLeft; *Status = STATUS_BUFFER_OVERFLOW; } /* Check if this is a compressed name */ if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME) { /* Copy as much as we can of the compressed name */ CmpCopyCompressedName(Info->KeyValueBasicInformation.Name, Size, CellData->u.KeyValue.Name, CellData->u.KeyValue.NameLength); } else { /* Copy as much as we can of the raw name */ RtlCopyMemory(Info->KeyValueBasicInformation.Name, CellData->u.KeyValue.Name, Size); } /* We're all done */ break; /* Full key information */ case KeyValueFullInformation: case KeyValueFullInformationAlign64: /* Check if this is a small key and compute key size */ IsSmall = CmpIsKeyValueSmall(&KeySize, CellData->u.KeyValue.DataLength); /* Calculate the total size required */ Size = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) + NameSize + KeySize; /* And this is the least we can work with */ MinimumSize = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name); /* Check if there's any key data */ if (KeySize > 0) { /* Calculate the data offset */ DataOffset = Size - KeySize; #ifdef _WIN64 /* On 64-bit, always align to 8 bytes */ AlignedData = ALIGN_UP(DataOffset, ULONGLONG); #else /* On 32-bit, align the offset to 4 or 8 bytes */ if (KeyValueInformationClass == KeyValueFullInformationAlign64) { AlignedData = ALIGN_UP(DataOffset, ULONGLONG); } else { AlignedData = ALIGN_UP(DataOffset, ULONG); } #endif /* If alignment was required, we'll need more space */ if (AlignedData > DataOffset) Size += (AlignedData-DataOffset); } /* Tell the caller the size we'll finally need, and set success */ *ResultLength = Size; *Status = STATUS_SUCCESS; /* Check if the caller is giving us too little */ if (Length < MinimumSize) { /* Then fail right now */ *Status = STATUS_BUFFER_TOO_SMALL; break; } /* Fill out the basic information */ Info->KeyValueFullInformation.TitleIndex = 0; Info->KeyValueFullInformation.Type = CellData->u.KeyValue.Type; Info->KeyValueFullInformation.DataLength = KeySize; Info->KeyValueFullInformation.NameLength = NameSize; /* Only the name is left now */ SizeLeft = Length - MinimumSize; Size = NameSize; /* Check if the name fits */ if (SizeLeft < Size) { /* It doesn't, truncate what we'll copy, and tell the caller */ Size = SizeLeft; *Status = STATUS_BUFFER_OVERFLOW; } /* Check if this key value is compressed */ if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME) { /* It is, copy the compressed name */ CmpCopyCompressedName(Info->KeyValueFullInformation.Name, Size, CellData->u.KeyValue.Name, CellData->u.KeyValue.NameLength); } else { /* It's not, copy the raw name */ RtlCopyMemory(Info->KeyValueFullInformation.Name, CellData->u.KeyValue.Name, Size); } /* Now check if the key had any data */ if (KeySize > 0) { /* Was it a small key? */ if (IsSmall) { /* Then the data is directly into the cell */ Buffer = &CellData->u.KeyValue.Data; } else { /* Otherwise, we must retrieve it from the value cache */ Result = CmpGetValueDataFromCache(Kcb, CachedValue, CellData, ValueIsCached, &Buffer, &BufferAllocated, &CellToRelease); if (Result != SearchSuccess) { /* We failed, nothing should be allocated */ ASSERT(Buffer == NULL); ASSERT(BufferAllocated == FALSE); *Status = STATUS_INSUFFICIENT_RESOURCES; } } /* Now that we know we truly have data, set its offset */ Info->KeyValueFullInformation.DataOffset = AlignedData; /* Only the data remains to be copied */ SizeLeft = (((LONG)Length - (LONG)AlignedData) < 0) ? 0 : (Length - AlignedData); Size = KeySize; /* Check if the caller has no space for it */ if (SizeLeft < Size) { /* Truncate what we'll copy, and tell the caller */ Size = SizeLeft; *Status = STATUS_BUFFER_OVERFLOW; } /* Sanity check */ ASSERT((IsSmall ? (Size <= CM_KEY_VALUE_SMALL) : TRUE)); /* Make sure we have a valid buffer */ if (Buffer) { /* Copy the data into the aligned offset */ RtlCopyMemory((PVOID)((ULONG_PTR)Info + AlignedData), Buffer, Size); } } else { /* We don't have any data, set the offset to -1, not 0! */ Info->KeyValueFullInformation.DataOffset = 0xFFFFFFFF; } /* We're done! */ break; /* Partial information requested (no name or alignment!) */ case KeyValuePartialInformation: /* Check if this is a small key and compute key size */ IsSmall = CmpIsKeyValueSmall(&KeySize, CellData->u.KeyValue.DataLength); /* Calculate the total size required */ Size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + KeySize; /* And this is the least we can work with */ MinimumSize = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data); /* Tell the caller the size we'll finally need, and set success */ *ResultLength = Size; *Status = STATUS_SUCCESS; /* Check if the caller is giving us too little */ if (Length < MinimumSize) { /* Then fail right now */ *Status = STATUS_BUFFER_TOO_SMALL; break; } /* Fill out the basic information */ Info->KeyValuePartialInformation.TitleIndex = 0; Info->KeyValuePartialInformation.Type = CellData->u.KeyValue.Type; Info->KeyValuePartialInformation.DataLength = KeySize; /* Now check if the key had any data */ if (KeySize > 0) { /* Was it a small key? */ if (IsSmall) { /* Then the data is directly into the cell */ Buffer = &CellData->u.KeyValue.Data; } else { /* Otherwise, we must retrieve it from the value cache */ Result = CmpGetValueDataFromCache(Kcb, CachedValue, CellData, ValueIsCached, &Buffer, &BufferAllocated, &CellToRelease); if (Result != SearchSuccess) { /* We failed, nothing should be allocated */ ASSERT(Buffer == NULL); ASSERT(BufferAllocated == FALSE); *Status = STATUS_INSUFFICIENT_RESOURCES; } } /* Only the data remains to be copied */ SizeLeft = Length - MinimumSize; Size = KeySize; /* Check if the caller has no space for it */ if (SizeLeft < Size) { /* Truncate what we'll copy, and tell the caller */ Size = SizeLeft; *Status = STATUS_BUFFER_OVERFLOW; } /* Sanity check */ ASSERT((IsSmall ? (Size <= CM_KEY_VALUE_SMALL) : TRUE)); /* Make sure we have a valid buffer */ if (Buffer) { /* Copy the data into the aligned offset */ RtlCopyMemory(Info->KeyValuePartialInformation.Data, Buffer, Size); } } /* We're done! */ break; /* Other information class */ default: /* We got some class that we don't support */ DPRINT1("Caller requested unknown class: %lx\n", KeyValueInformationClass); *Status = STATUS_INVALID_PARAMETER; break; } /* Return the search result as well */ return Result; }
VALUE_SEARCH_RETURN_TYPE NTAPI CmpCompareNewValueDataAgainstKCBCache(IN PCM_KEY_CONTROL_BLOCK Kcb, IN PUNICODE_STRING ValueName, IN ULONG Type, IN PVOID Data, IN ULONG DataSize) { VALUE_SEARCH_RETURN_TYPE SearchResult; PCM_KEY_NODE KeyNode; PCM_CACHED_VALUE *CachedValue; ULONG Index; PCM_KEY_VALUE Value; BOOLEAN ValueCached, BufferAllocated = FALSE; PVOID Buffer; HCELL_INDEX ValueCellToRelease = HCELL_NIL, CellToRelease = HCELL_NIL; BOOLEAN IsSmall; ULONG CompareResult; PAGED_CODE(); /* Check if this is a symlink */ if (Kcb->Flags & KEY_SYM_LINK) { /* We need the exclusive lock */ if (!(CmpIsKcbLockedExclusive(Kcb)) && !(CmpTryToConvertKcbSharedToExclusive(Kcb))) { /* We need the exclusive lock */ return SearchNeedExclusiveLock; } /* Otherwise, get the key node */ KeyNode = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive, Kcb->KeyCell); if (!KeyNode) return SearchFail; /* Cleanup the KCB cache */ CmpCleanUpKcbValueCache(Kcb); /* Sanity checks */ ASSERT(!(CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList))); ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)); /* Set the value cache */ Kcb->ValueCache.Count = KeyNode->ValueList.Count; Kcb->ValueCache.ValueList = KeyNode->ValueList.List; /* Release the cell */ HvReleaseCell(Kcb->KeyHive, Kcb->KeyCell); } /* Do the search */ SearchResult = CmpFindValueByNameFromCache(Kcb, ValueName, &CachedValue, &Index, &Value, &ValueCached, &ValueCellToRelease); if (SearchResult == SearchNeedExclusiveLock) { /* We need the exclusive lock */ ASSERT(!CmpIsKcbLockedExclusive(Kcb)); ASSERT(ValueCellToRelease == HCELL_NIL); ASSERT(Value == NULL); goto Quickie; } else if (SearchResult == SearchSuccess) { /* Sanity check */ ASSERT(Value); /* First of all, check if the key size and type matches */ if ((Type == Value->Type) && (DataSize == (Value->DataLength & ~CM_KEY_VALUE_SPECIAL_SIZE))) { /* Check if this is a small key */ IsSmall = (DataSize <= CM_KEY_VALUE_SMALL) ? TRUE: FALSE; if (IsSmall) { /* Compare against the data directly */ Buffer = &Value->Data; } else { /* Do a search */ SearchResult = CmpGetValueDataFromCache(Kcb, CachedValue, (PCELL_DATA)Value, ValueCached, &Buffer, &BufferAllocated, &CellToRelease); if (SearchResult != SearchSuccess) { /* Sanity checks */ ASSERT(Buffer == NULL); ASSERT(BufferAllocated == FALSE); goto Quickie; } } /* Now check the data size */ if (DataSize) { /* Do the compare */ CompareResult = RtlCompareMemory(Buffer, Data, DataSize & ~CM_KEY_VALUE_SPECIAL_SIZE); } else { /* It's equal */ CompareResult = 0; } /* Now check if the compare wasn't equal */ if (CompareResult != DataSize) SearchResult = SearchFail; } else { /* The length or type isn't equal */ SearchResult = SearchFail; } } Quickie: /* Release the value cell */ if (ValueCellToRelease) HvReleaseCell(Kcb->KeyHive, ValueCellToRelease); /* Free the buffer */ if (BufferAllocated) CmpFree(Buffer, 0); /* Free the cell */ if (CellToRelease) HvReleaseCell(Kcb->KeyHive, CellToRelease); /* Return the search result */ return SearchResult; }
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; }