windows-nt/Source/XPSP1/NT/base/ntdll/buffer.c
2020-09-26 16:20:57 +08:00

709 lines
18 KiB
C

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
buffer.c
Abstract:
The module implements a buffer in the style popularized by
Michael J. Grier (MGrier), where some amount (like MAX_PATH)
of storage is preallocated (like on the stack) and if the storage
needs grow beyond the preallocated size, the heap is used.
Author:
Jay Krell (a-JayK) June 2000
Environment:
User Mode or Kernel Mode (but don't preallocate much on the stack in kernel mode)
Revision History:
--*/
#include "ntos.h"
#include "nt.h"
#include "ntrtl.h"
#include "nturtl.h"
#include <limits.h>
NTSTATUS
NTAPI
RtlpEnsureBufferSize(
IN ULONG Flags,
IN OUT PRTL_BUFFER Buffer,
IN SIZE_T Size
)
/*++
Routine Description:
This function ensures Buffer can hold Size bytes, or returns
an error. It either bumps Buffer->Size closer to Buffer->StaticSize,
or heap allocates.
Arguments:
Buffer - a Buffer object, see also RtlInitBuffer.
Size - the number of bytes the caller wishes to store in Buffer->Buffer.
Return Value:
STATUS_SUCCESS
STATUS_NO_MEMORY
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PUCHAR Temp = NULL;
if ((Flags & ~(RTL_ENSURE_BUFFER_SIZE_NO_COPY)) != 0) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (Buffer == NULL) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (Size <= Buffer->Size) {
Status = STATUS_SUCCESS;
goto Exit;
}
// Size <= Buffer->StaticSize does not imply static allocation, it
// could be heap allocation that the client poked smaller.
if (Buffer->Buffer == Buffer->StaticBuffer && Size <= Buffer->StaticSize) {
Buffer->Size = Size;
Status = STATUS_SUCCESS;
goto Exit;
}
//
// The realloc case was messed up in Whistler, and got removed.
// Put it back in Blackcomb.
//
Temp = (PUCHAR)RtlAllocateStringRoutine(Size);
if (Temp == NULL) {
Status = STATUS_NO_MEMORY;
goto Exit;
}
if ((Flags & RTL_ENSURE_BUFFER_SIZE_NO_COPY) == 0) {
RtlCopyMemory(Temp, Buffer->Buffer, Buffer->Size);
}
if (RTLP_BUFFER_IS_HEAP_ALLOCATED(Buffer)) {
RtlFreeStringRoutine(Buffer->Buffer);
Buffer->Buffer = NULL;
}
ASSERT(Temp != NULL);
Buffer->Buffer = Temp;
Buffer->Size = Size;
Status = STATUS_SUCCESS;
Exit:
return Status;
}
NTSTATUS
NTAPI
RtlMultiAppendUnicodeStringBuffer(
OUT PRTL_UNICODE_STRING_BUFFER Destination,
IN ULONG NumberOfSources,
IN const UNICODE_STRING* SourceArray
)
/*++
Routine Description:
Arguments:
Destination -
NumberOfSources -
SourceArray -
Return Value:
STATUS_SUCCESS
STATUS_NO_MEMORY
STATUS_NAME_TOO_LONG
--*/
{
SIZE_T Length = 0;
ULONG i = 0;
NTSTATUS Status = STATUS_SUCCESS;
const SIZE_T CharSize = sizeof(*Destination->String.Buffer);
const ULONG OriginalDestinationLength = Destination->String.Length;
Length = OriginalDestinationLength;
for (i = 0 ; i != NumberOfSources ; ++i) {
Length += SourceArray[i].Length;
}
Length += CharSize;
if (Length > MAX_UNICODE_STRING_MAXLENGTH) {
return STATUS_NAME_TOO_LONG;
}
Status = RtlEnsureBufferSize(0, &Destination->ByteBuffer, Length);
if (!NT_SUCCESS(Status)) {
return Status;
}
Destination->String.MaximumLength = (USHORT)Length;
Destination->String.Length = (USHORT)(Length - CharSize);
Destination->String.Buffer = (PWSTR)Destination->ByteBuffer.Buffer;
Length = OriginalDestinationLength;
for (i = 0 ; i != NumberOfSources ; ++i) {
RtlMoveMemory(
Destination->String.Buffer + Length / CharSize,
SourceArray[i].Buffer,
SourceArray[i].Length);
Length += SourceArray[i].Length;
}
Destination->String.Buffer[Length / CharSize] = 0;
return STATUS_SUCCESS;
}
#if 0 // not yet unused
NTSTATUS
NTAPI
RtlPrependStringToUnicodeStringBuffer(
IN ULONG Flags,
IN OUT PRTL_UNICODE_STRING_BUFFER UnicodeStringBuffer,
IN PCUNICODE_STRING UnicodeString
)
/*++
Routine Description:
Insert a string at the beginning of a unicode string buffer.
This should be equivalent to RtlInsertStringIntoUnicodeStringBuffer(0).
Arguments:
Flags - 0, room for future binary compatible expansion
Buffer - buffer to change
Length - number of chars to keep
Return Value:
STATUS_SUCCESS
STATUS_INVALID_PARAMETER
STATUS_NAME_TOO_LONG
--*/
{
//
// This could be sped up. It does an extra copy of the buffer's
// existing contents when the buffer needs to grow.
//
NTSTATUS Status = STATUS_SUCCESS;
if (Flags != 0) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (UnicodeStringBuffer == NULL) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (UnicodeString == NULL || UnicodeString->Length == 0) {
Status = STATUS_SUCCESS;
goto Exit;
}
Status =
RtlEnsureUnicodeStringBufferSizeChars(
UnicodeStringBuffer,
RTL_STRING_GET_LENGTH_CHARS(&UnicodeStringBuffer->String)
+ RTL_STRING_GET_LENGTH_CHARS(UnicodeString)
);
if (!NT_SUCCESS(Status))
goto Exit;
RtlMoveMemory(
UnicodeStringBuffer->String.Buffer + RTL_STRING_GET_LENGTH_CHARS(UnicodeString),
UnicodeStringBuffer->String.Buffer,
UnicodeStringBuffer->String.Length + sizeof(WCHAR)
);
RtlMoveMemory(
UnicodeStringBuffer->String.Buffer
UnicodeString->Buffer,
UnicodeString->Length
);
UnicodeStringBuffer->String.Length += UnicodeString->Length;
Status = STATUS_SUCCESS;
Exit:
return Status;
}
#endif
#if 0 // not yet unused
NTSTATUS
NTAPI
RtlUnicodeStringBufferRight(
IN ULONG Flags,
IN OUT PRTL_UNICODE_STRING_BUFFER Buffer,
IN ULONG Length
)
/*++
Routine Description:
This function replaces a unicode string buffer with characters
taken from its right. This requires a copy. In the future
we should allow
RTL_UNICODE_STRING_BUFFER.UnicodeString.Buffer
!= RTL_UNICODE_STRING_BUFFER.ByteBuffer.Buffer
so this can be fast. Likewise for Mid.
Arguments:
Flags - 0, room for future binary compatible expansion
Buffer - buffer to change
Length - number of chars to keep
Return Value:
STATUS_SUCCESS
STATUS_INVALID_PARAMETER
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
const PUNICODE_STRING String = (Buffer == NULL ? NULL : &Buffer->String);
const ULONG CurrentLengthChars = (String == NULL ? 0 : RTL_STRING_GET_LENGTH_CHARS(String));
if (Flags != 0) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (Buffer == NULL) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (Length >= CurrentLengthChars) {
Status = STATUS_SUCCESS;
goto Exit;
}
RtlMoveMemory(
String->Buffer,
String->Buffer + CurrentLengthChars - Length,
Length * sizeof(String->Buffer[0])
);
RTL_STRING_SET_LENGTH_CHARS_UNSAFE(String, Length);
RTL_STRING_NUL_TERMINATE(String);
Status = STATUS_SUCCESS;
Exit:
return Status;
}
#endif
#if 0 // not yet unused
NTSTATUS
NTAPI
RtlUnicodeStringBufferLeft(
IN ULONG Flags,
IN OUT PRTL_UNICODE_STRING_BUFFER Buffer,
IN ULONG Length
)
/*++
Routine Description:
This function replaces a unicode string buffer with characters
taken from its left. This is fast.
Arguments:
Flags - 0, room for future binary compatible expansion
Buffer - buffer to change
Length - number of chars to keep
Return Value:
STATUS_SUCCESS
STATUS_INVALID_PARAMETER
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
const PUNICODE_STRING String = (Buffer == NULL ? NULL : &Buffer->String);
const ULONG CurrentLengthChars = (String == NULL ? 0 : RTL_STRING_GET_LENGTH_CHARS(String));
if (Flags != 0) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (Buffer == NULL) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (Length >= CurrentLengthChars) {
Status = STATUS_SUCCESS;
goto Exit;
}
RTL_STRING_SET_LENGTH_CHARS_UNSAFE(String, Length);
RTL_STRING_NUL_TERMINATE(String);
Status = STATUS_SUCCESS;
Exit:
return Status;
}
#endif
#if 0 // not yet unused
NTSTATUS
NTAPI
RtlUnicodeStringBufferMid(
IN ULONG Flags,
IN OUT PRTL_UNICODE_STRING_BUFFER Buffer,
IN ULONG Offset,
IN ULONG Length
)
/*++
Routine Description:
This function replaces a unicode string buffer with characters
taken from its "middle", as defined by an offset
from the start and length, both in chars.
Arguments:
Flags - 0, room for future binary compatible expansion
Buffer - buffer to change
Offset - offset to keep chars from
Length - number of chars to keep
Return Value:
STATUS_SUCCESS
STATUS_INVALID_PARAMETER
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
const PUNICODE_STRING String = (Buffer == NULL ? NULL : &Buffer->String);
const ULONG CurrentLengthChars = (String == NULL ? 0 : RTL_STRING_GET_LENGTH_CHARS(String));
if (Flags != 0) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (Buffer == NULL) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (Offset >= CurrentLengthChars) {
Offset = 0;
Length = 0;
} else if (Offset + Length >= CurrentLengthChars) {
Length = CurrentLengthChars - Offset;
}
ASSERT(Offset < CurrentLengthChars);
ASSERT(Length <= CurrentLengthChars);
ASSERT(Offset + Length <= CurrentLengthChars);
if (Length != 0 && Offset != 0 && Length != CurrentLengthChars) {
RtlMoveMemory(
String->Buffer,
String->Buffer + Offset,
Length * sizeof(String->Buffer[0])
);
}
RTL_STRING_SET_LENGTH_CHARS_UNSAFE(String, Length);
RTL_STRING_NUL_TERMINATE(String);
Status = STATUS_SUCCESS;
Exit:
return Status;
}
#endif
#if 0 // not yet unused
NTSTATUS
NTAPI
RtlInsertStringIntoUnicodeStringBuffer(
IN ULONG Flags,
IN OUT PRTL_UNICODE_STRING_BUFFER UnicodeStringBuffer,
IN ULONG Offset,
IN PCUNICODE_STRING InsertString
)
/*++
Routine Description:
This function insert a string into a unicode string buffer at
a specified offset, growing the buffer as necessary to fit,
and even handling the aliased case where the string and buffer overlap.
Arguments:
Flags - 0, the ever popular "room for future binary compatible expansion"
UnicodeStringBuffer - buffer to insert string into
Offset - offset to insert the string at
InsertString - string to insert into UnicodeStringBuffer
Return Value:
STATUS_SUCCESS
STATUS_INVALID_PARAMETER
STATUS_NO_MEMORY
STATUS_NAME_TOO_LONG
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
typedef WCHAR TChar;
const PUNICODE_STRING String = (UnicodeStringBuffer == NULL ? NULL : &UnicodeStringBuffer->String);
const ULONG CurrentLengthChars = (String == NULL ? 0 : RTL_STRING_GET_LENGTH_CHARS(String));
const ULONG InsertStringLengthChars = (InsertString == NULL ? 0 : RTL_STRING_GET_LENGTH_CHARS(InsertString));
const ULONG NewLengthChars = CurrentLengthChars + InsertStringLengthChars;
const ULONG NewLengthBytes = (CurrentLengthChars + InsertStringLengthChars) * sizeof(TChar);
RTL_UNICODE_STRING_BUFFER AliasBuffer = { 0 };
BOOLEAN Alias = FALSE;
ULONG i = 0;
if (Flags != 0) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (UnicodeStringBuffer == NULL) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (InsertString == NULL || InsertString->Length == 0) {
Status = STATUS_SUCCESS;
goto Exit;
}
if (Offset >= CurrentLengthChars) {
Offset = CurrentLengthChars;
}
//
// Check for aliasing. This check is overly cautious.
//
if (InsertString->Buffer >= String->Buffer && InsertString->Buffer < String->Buffer + CurrentLengthChars) {
Alias = TRUE;
}
else if (String->Buffer >= InsertString->Buffer && String->Buffer < InsertString->Buffer + InsertStringLengthChars) {
Alias = TRUE;
}
if (Alias) {
RtlInitUnicodeStringBuffer(&AliasBuffer, NULL, 0);
Status = RtlAssignUnicodeStringBuffer(&AliasBuffer, InsertString);
if (!NT_SUCCESS(Status))
goto Exit;
InsertString = &AliasBuffer->String;
}
Status = RtlEnsureUnicodeBufferSizeChars(UnicodeStringBuffer, NewLength);
if (!NT_SUCCESS(Status))
goto Exit;
RtlMoveMemory(String->Buffer + Offset + InsertStringLengthChars, String->Buffer + Offset, (CurrentLengthChars - Offset) * sizeof(TChar));
RtlMoveMemory(String->Buffer + Offset, InsertString->Insert, InsertStringLengthChars * sizeof(TChar));
RTL_STRING_SET_LENGTH_CHARS_UNSAFE(String, NewLength);
RTL_STRING_NUL_TERMINATE(String);
Status = STATUS_SUCCESS;
Exit:
RtlFreeUnicodeStringBuffer(&AliasBuffer);
return Status;
}
#endif
#if 0 // not yet unused
NTSTATUS
NTAPI
RtlBufferTakeValue(
IN ULONG Flags,
IN OUT PRTL_BUFFER DestinationBuffer,
IN OUT PRTL_BUFFER SourceBuffer
)
/*++
Routine Description:
This function copies the value of one RTL_BUFFER to another
and frees the source, in one step. If source is heap allocated, this enables
the optimization of not doing a RtlMoveMemory, just moving the pointers and sizes.
Arguments:
Flags - 0
DestinationBuffer - ends up holding source's value
SourceBuffer - ends up freed
Return Value:
STATUS_SUCCESS
STATUS_INVALID_PARAMETER
STATUS_NO_MEMORY
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
typedef WCHAR TChar;
if (Flags != 0) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (DestinationBuffer == NULL) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (SourceBuffer == NULL) {
RtlFreeBuffer(DestinationBuffer);
Status = STATUS_SUCCESS;
goto Exit;
}
if (RTLP_BUFFER_IS_HEAP_ALLOCATED(SourceBuffer)
&& SourceBuffer->ReservedForIMalloc == DestinationBuffer->ReservedForIMalloc
) {
DestinationBuffer->Size = SourceBuffer->Size;
DestinationBuffer->Buffer = SourceBuffer->Buffer;
SourceBuffer->Buffer = SourceBuffer->StaticBuffer;
SourceBuffer->Size = SourceBuffer->StaticSize;
goto Exit;
}
Status = RtlEnsureBufferSize(RTL_ENSURE_BUFFER_SIZE_NO_COPY, DestinationBuffer, SourceBuffer->Size);
if (!NT_SUCCESS(Status))
goto Exit;
RtlMoveMemory(DestinationBuffer->Buffer, SourceBuffer->Buffer, SourceBuffer->Size);
RtlFreeBuffer(SourceBuffer);
Status = STATUS_SUCCESS;
Exit:
return Status;
}
#endif
#if 0 // not yet unused
NTSTATUS
NTAPI
RtlValidateBuffer(
IN ULONG Flags,
IN CONST RTL_BUFFER* Buffer
)
/*++
Routine Description:
This function performs some sanity checking on the buffer.
Arguments:
Flags - 0
Buffer - the buffer to check
Return Value:
STATUS_SUCCESS - the buffer is aok
STATUS_INVALID_PARAMETER - the buffer is not good
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
if (Flags != 0) {
Status = STATUS_INVALID_PARAMETER
goto Exit;
}
if (Buffer == NULL) {
Status = STATUS_SUCCESS;
goto Exit;
}
Status = STATUS_INVALID_PARAMETER;
if (!RTL_IMPLIES(Buffer->Buffer == Buffer->StaticBuffer, Buffer->Size <= Buffer->StaticSize))
goto Exit;
Status = STATUS_SUCCESS;
Exit:
return Status;
}
#endif
#if 0 // not yet unused
NTSTATUS
NTAPI
RtlValidateUnicodeStringBuffer(
IN ULONG Flags,
IN CONST RTL_UNICODE_STRING_BUFFER* UnicodeStringBuffer
)
/*++
Routine Description:
This function performs some sanity checking on the buffer.
Arguments:
Flags - 0
UnicodeStringBuffer - the buffer to check
Return Value:
STATUS_SUCCESS - the buffer is aok
STATUS_INVALID_PARAMETER - the buffer is not good
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
if (Flags != 0) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (UnicodeStringBuffer == NULL) {
Status = STATUS_SUCCESS;
goto Exit;
}
if (!RTL_VERIFY(NT_SUCCESS(Status = RtlValidateUnicodeString(&UnicodeStringBuffer->String))))
goto Exit;
if (!RTL_VERIFY(NT_SUCCESS(Status = RtlValidateBuffer(&UnicodeStringBuffer->Buffer))))
goto Exit;
Status = STATUS_INVALID_PARAMETER;
if (!RTL_VERIFY(UnicodeStringBuffer->String.Length < UnicodeStringBuffer->ByteBuffer.Size))
goto Exit;
if (!RTL_VERIFY(UnicodeStringBuffer->String.MaximumLength >= UnicodeStringBuffer->String.Length))
goto Exit;
if (!RTL_VERIFY(UnicodeStringBuffer->String.MaximumLength == UnicodeStringBuffer->ByteBuffer.Size))
goto Exit;
if (!RTL_VERIFY(UnicodeStringBuffer->String.Buffer == UnicodeStringBuffer->ByteBuffer.Buffer))
goto Exit;
if (!RTL_VERIFY(UnicodeStringBuffer->String.MaximumLength != 0))
goto Exit;
Status = STATUS_SUCCESS;
Exit:
return Status;
}
#endif
#if 0
static void test()
{
RTL_BUFFER Buffer = { 0 };
UCHAR chars[260 * sizeof(WCHAR)];
RtlInitBuffer(&Buffer, chars, sizeof(chars));
RtlEnsureBufferSize(0, &Buffer, 1024 * sizeof(WCHAR));
RtlFreeBuffer(&Buffer);
}
#endif