mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
520 lines
14 KiB
520 lines
14 KiB
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
rtlutils.cpp
|
|
|
|
Abstract:
|
|
|
|
Contains functions from ntdll on XP
|
|
that are not available on W2K.
|
|
|
|
History:
|
|
|
|
09/10/2001 rparsons Created
|
|
|
|
--*/
|
|
|
|
#include "rtlutils.h"
|
|
|
|
namespace ShimLib
|
|
{
|
|
|
|
#define IS_PATH_SEPARATOR_U(ch) (((ch) == L'\\') || ((ch) == L'/'))
|
|
|
|
extern const UNICODE_STRING RtlpDosDevicesPrefix = RTL_CONSTANT_STRING( L"\\??\\" );
|
|
extern const UNICODE_STRING RtlpDosDevicesUncPrefix = RTL_CONSTANT_STRING( L"\\??\\UNC\\" );
|
|
|
|
const UNICODE_STRING RtlpEmptyString = RTL_CONSTANT_STRING(L"");
|
|
|
|
//
|
|
// Taken from %SDXROOT%\public\sdk\inc\ntrtl.h
|
|
//
|
|
#if DBG
|
|
#undef ASSERT
|
|
#define ASSERT( exp ) \
|
|
((!(exp)) ? \
|
|
(RtlAssert( #exp, __FILE__, __LINE__, NULL ),FALSE) : \
|
|
TRUE)
|
|
#else
|
|
#undef ASSERT
|
|
#define ASSERT( exp ) ((void) 0)
|
|
#endif
|
|
|
|
#define DPFLTR_LEVEL_STATUS(x) ((NT_SUCCESS(x) \
|
|
|| (x) == STATUS_OBJECT_NAME_NOT_FOUND \
|
|
) \
|
|
? DPFLTR_TRACE_LEVEL : DPFLTR_ERROR_LEVEL)
|
|
|
|
|
|
|
|
|
|
//
|
|
// These functions were taken from:
|
|
// %SDXROOT%\base\ntdll\ldrinit.c
|
|
//
|
|
PVOID
|
|
ShimAllocateStringRoutine(
|
|
SIZE_T NumberOfBytes
|
|
)
|
|
{
|
|
return RtlAllocateHeap(RtlProcessHeap(), 0, NumberOfBytes);
|
|
}
|
|
|
|
VOID
|
|
ShimFreeStringRoutine(
|
|
PVOID Buffer
|
|
)
|
|
{
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Buffer);
|
|
}
|
|
|
|
//
|
|
// These functions were pulled from:
|
|
// %SDXROOT%\base\ntdll\curdir.c
|
|
//
|
|
|
|
RTL_PATH_TYPE
|
|
NTAPI
|
|
ShimDetermineDosPathNameType_Ustr(
|
|
IN PCUNICODE_STRING String
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function examines the Dos format file name and determines the
|
|
type of file name (i.e. UNC, DriveAbsolute, Current Directory
|
|
rooted, or Relative.)
|
|
|
|
Arguments:
|
|
|
|
DosFileName - Supplies the Dos format file name whose type is to be
|
|
determined.
|
|
|
|
Return Value:
|
|
|
|
RtlPathTypeUnknown - The path type can not be determined
|
|
|
|
RtlPathTypeUncAbsolute - The path specifies a Unc absolute path
|
|
in the format \\server-name\sharename\rest-of-path
|
|
|
|
RtlPathTypeLocalDevice - The path specifies a local device in the format
|
|
\\.\rest-of-path or \\?\rest-of-path. This can be used for any device
|
|
where the nt and Win32 names are the same. For example mailslots.
|
|
|
|
RtlPathTypeRootLocalDevice - The path specifies the root of the local
|
|
devices in the format \\. or \\?
|
|
|
|
RtlPathTypeDriveAbsolute - The path specifies a drive letter absolute
|
|
path in the form drive:\rest-of-path
|
|
|
|
RtlPathTypeDriveRelative - The path specifies a drive letter relative
|
|
path in the form drive:rest-of-path
|
|
|
|
RtlPathTypeRooted - The path is rooted relative to the current disk
|
|
designator (either Unc disk, or drive). The form is \rest-of-path.
|
|
|
|
RtlPathTypeRelative - The path is relative (i.e. not absolute or rooted).
|
|
|
|
--*/
|
|
|
|
{
|
|
RTL_PATH_TYPE ReturnValue;
|
|
const PCWSTR DosFileName = String->Buffer;
|
|
|
|
#define ENOUGH_CHARS(_cch) (String->Length >= ((_cch) * sizeof(WCHAR)))
|
|
|
|
if ( ENOUGH_CHARS(1) && IS_PATH_SEPARATOR_U(*DosFileName) ) {
|
|
if ( ENOUGH_CHARS(2) && IS_PATH_SEPARATOR_U(*(DosFileName+1)) ) {
|
|
if ( ENOUGH_CHARS(3) && (DosFileName[2] == '.' ||
|
|
DosFileName[2] == '?') ) {
|
|
|
|
if ( ENOUGH_CHARS(4) && IS_PATH_SEPARATOR_U(*(DosFileName+3)) ){
|
|
// "\\.\" or "\\?\"
|
|
ReturnValue = RtlPathTypeLocalDevice;
|
|
}
|
|
//
|
|
// Bogosity ahead, the code is confusing length and nuls,
|
|
// because it was copy/pasted from the PCWSTR version.
|
|
//
|
|
else if ( ENOUGH_CHARS(4) && (*(DosFileName+3)) == UNICODE_NULL ){
|
|
// "\\.\0" or \\?\0"
|
|
ReturnValue = RtlPathTypeRootLocalDevice;
|
|
}
|
|
else {
|
|
// "\\.x" or "\\." or "\\?x" or "\\?"
|
|
ReturnValue = RtlPathTypeUncAbsolute;
|
|
}
|
|
}
|
|
else {
|
|
// "\\x"
|
|
ReturnValue = RtlPathTypeUncAbsolute;
|
|
}
|
|
}
|
|
else {
|
|
// "\x"
|
|
ReturnValue = RtlPathTypeRooted;
|
|
}
|
|
}
|
|
//
|
|
// the "*DosFileName" is left over from the PCWSTR version
|
|
// Win32 and DOS don't allow embedded nuls and much code limits
|
|
// drive letters to strictly 7bit a-zA-Z so it's ok.
|
|
//
|
|
else if (ENOUGH_CHARS(2) && *DosFileName && *(DosFileName+1)==L':') {
|
|
if (ENOUGH_CHARS(3) && IS_PATH_SEPARATOR_U(*(DosFileName+2))) {
|
|
// "x:\"
|
|
ReturnValue = RtlPathTypeDriveAbsolute;
|
|
}
|
|
else {
|
|
// "c:x"
|
|
ReturnValue = RtlPathTypeDriveRelative;
|
|
}
|
|
}
|
|
else {
|
|
// "x", first char is not a slash / second char is not colon
|
|
ReturnValue = RtlPathTypeRelative;
|
|
}
|
|
return ReturnValue;
|
|
|
|
#undef ENOUGH_CHARS
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
ShimNtPathNameToDosPathName(
|
|
IN ULONG Flags,
|
|
IN OUT PRTL_UNICODE_STRING_BUFFER Path,
|
|
OUT ULONG* Disposition OPTIONAL,
|
|
IN OUT PWSTR* FilePart OPTIONAL
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
SIZE_T NtFilePartOffset = 0;
|
|
SIZE_T DosFilePartOffset = 0;
|
|
BOOLEAN Unc = FALSE;
|
|
const static UNICODE_STRING DosUncPrefix = RTL_CONSTANT_STRING(L"\\\\");
|
|
PCUNICODE_STRING NtPrefix = NULL;
|
|
PCUNICODE_STRING DosPrefix = NULL;
|
|
RTL_STRING_LENGTH_TYPE Cch = 0;
|
|
|
|
if (ARGUMENT_PRESENT(Disposition)) {
|
|
*Disposition = 0;
|
|
}
|
|
|
|
if ( !RTL_SOFT_VERIFY(Path != NULL)
|
|
|| !RTL_SOFT_VERIFY(Flags == 0)
|
|
) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(FilePart) && *FilePart != NULL) {
|
|
NtFilePartOffset = *FilePart - Path->String.Buffer;
|
|
if (!RTL_SOFT_VERIFY(NtFilePartOffset < RTL_STRING_GET_LENGTH_CHARS(&Path->String))
|
|
) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (RtlPrefixUnicodeString(RTL_CONST_CAST(PUNICODE_STRING)(&RtlpDosDevicesUncPrefix), &Path->String, TRUE)
|
|
) {
|
|
NtPrefix = &RtlpDosDevicesUncPrefix;
|
|
DosPrefix = &DosUncPrefix;
|
|
if (ARGUMENT_PRESENT(Disposition)) {
|
|
*Disposition = RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_UNC;
|
|
}
|
|
}
|
|
else if (RtlPrefixUnicodeString(RTL_CONST_CAST(PUNICODE_STRING)(&RtlpDosDevicesPrefix), &Path->String, TRUE)
|
|
) {
|
|
NtPrefix = &RtlpDosDevicesPrefix;
|
|
DosPrefix = &RtlpEmptyString;
|
|
if (ARGUMENT_PRESENT(Disposition)) {
|
|
*Disposition = RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_DRIVE;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// It is not recognizably an Nt path produced by RtlDosPathNameToNtPathName_U.
|
|
//
|
|
if (ARGUMENT_PRESENT(Disposition)) {
|
|
RTL_PATH_TYPE PathType = ShimDetermineDosPathNameType_Ustr(&Path->String);
|
|
switch (PathType) {
|
|
case RtlPathTypeUnknown:
|
|
case RtlPathTypeRooted: // NT paths are identified as this
|
|
*Disposition = RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_AMBIGUOUS;
|
|
break;
|
|
|
|
//
|
|
// "already" dospaths, but not gotten from this function, let's
|
|
// give a less good disposition
|
|
//
|
|
case RtlPathTypeDriveRelative:
|
|
case RtlPathTypeRelative:
|
|
*Disposition = RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_AMBIGUOUS;
|
|
break;
|
|
|
|
// these are pretty clearly dospaths already
|
|
case RtlPathTypeUncAbsolute:
|
|
case RtlPathTypeDriveAbsolute:
|
|
case RtlPathTypeLocalDevice: // "\\?\" or "\\.\" or "\\?\blah" or "\\.\blah"
|
|
case RtlPathTypeRootLocalDevice: // "\\?" or "\\."
|
|
*Disposition = RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_ALREADY_DOS;
|
|
break;
|
|
}
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
Cch =
|
|
RTL_STRING_GET_LENGTH_CHARS(&Path->String)
|
|
+ RTL_STRING_GET_LENGTH_CHARS(DosPrefix)
|
|
- RTL_STRING_GET_LENGTH_CHARS(NtPrefix);
|
|
|
|
Status =
|
|
ShimEnsureUnicodeStringBufferSizeChars(Path, Cch);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// overlapping buffer shuffle...careful.
|
|
//
|
|
RtlMoveMemory(
|
|
Path->String.Buffer + RTL_STRING_GET_LENGTH_CHARS(DosPrefix),
|
|
Path->String.Buffer + RTL_STRING_GET_LENGTH_CHARS(NtPrefix),
|
|
Path->String.Length - NtPrefix->Length
|
|
);
|
|
RtlMoveMemory(
|
|
Path->String.Buffer,
|
|
DosPrefix->Buffer,
|
|
DosPrefix->Length
|
|
);
|
|
Path->String.Length = Cch * sizeof(Path->String.Buffer[0]);
|
|
RTL_NUL_TERMINATE_STRING(&Path->String);
|
|
|
|
if (NtFilePartOffset != 0) {
|
|
// review/test..
|
|
*FilePart = Path->String.Buffer + (NtFilePartOffset - RTL_STRING_GET_LENGTH_CHARS(NtPrefix) + RTL_STRING_GET_LENGTH_CHARS(DosPrefix));
|
|
}
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
/* KdPrintEx((
|
|
DPFLTR_SXS_ID, DPFLTR_LEVEL_STATUS(Status),
|
|
"%s(%d):%s(%wZ): 0x%08lx\n", __FILE__, __LINE__, __FUNCTION__, Path, Status)); */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ShimValidateUnicodeString(
|
|
ULONG Flags,
|
|
const UNICODE_STRING *String
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
ASSERT(Flags == 0);
|
|
|
|
if (Flags != 0) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
if (String != NULL) {
|
|
if (((String->Length % 2) != 0) ||
|
|
((String->MaximumLength % 2) != 0) ||
|
|
(String->Length > String->MaximumLength)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
if (((String->Length != 0) ||
|
|
(String->MaximumLength != 0)) &&
|
|
(String->Buffer == NULL)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// This function was taken from:
|
|
// %SDXROOT%\base\ntos\rtl\nls.c
|
|
//
|
|
|
|
NTSTATUS
|
|
ShimDuplicateUnicodeString(
|
|
ULONG Flags,
|
|
PCUNICODE_STRING StringIn,
|
|
PUNICODE_STRING StringOut
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
USHORT Length = 0;
|
|
USHORT NewMaximumLength = 0;
|
|
PWSTR Buffer = NULL;
|
|
|
|
if (((Flags & ~(
|
|
RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE |
|
|
RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING)) != 0) ||
|
|
(StringOut == NULL)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
// It doesn't make sense to force allocation of a null string unless you
|
|
// want null termination.
|
|
if ((Flags & RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING) &&
|
|
!(Flags & RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
Status = ShimValidateUnicodeString(0, StringIn);
|
|
if (!NT_SUCCESS(Status))
|
|
goto Exit;
|
|
|
|
if (StringIn != NULL)
|
|
Length = StringIn->Length;
|
|
|
|
if ((Flags & RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE) &&
|
|
(Length == UNICODE_STRING_MAX_BYTES)) {
|
|
Status = STATUS_NAME_TOO_LONG;
|
|
goto Exit;
|
|
}
|
|
|
|
if (Flags & RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE)
|
|
NewMaximumLength = (USHORT) (Length + sizeof(WCHAR));
|
|
else
|
|
NewMaximumLength = Length;
|
|
|
|
// If it's a zero length string in, force the allocation length to zero
|
|
// unless the caller said that they want zero length strings allocated.
|
|
if (((Flags & RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING) == 0) &&
|
|
(Length == 0)) {
|
|
NewMaximumLength = 0;
|
|
}
|
|
|
|
if (NewMaximumLength != 0) {
|
|
Buffer = (PWSTR)(RtlAllocateStringRoutine)(NewMaximumLength);
|
|
if (Buffer == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
// If there's anything to copy, copy it. We explicitly test Length because
|
|
// StringIn could be a NULL pointer, so dereferencing it to get the Buffer
|
|
// pointer would access violate.
|
|
if (Length != 0) {
|
|
RtlCopyMemory(
|
|
Buffer,
|
|
StringIn->Buffer,
|
|
Length);
|
|
}
|
|
|
|
if (Flags & RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE) {
|
|
Buffer[Length / sizeof(WCHAR)] = L'\0';
|
|
}
|
|
}
|
|
|
|
StringOut->Buffer = Buffer;
|
|
StringOut->MaximumLength = NewMaximumLength;
|
|
StringOut->Length = Length;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// This function was pulled from:
|
|
// %SDXROOT%\base\ntdll\buffer.c
|
|
//
|
|
NTSTATUS
|
|
NTAPI
|
|
ShimpEnsureBufferSize(
|
|
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;
|
|
}
|
|
|
|
} // end of namespace ShimLib
|