2402 lines
53 KiB
C
2402 lines
53 KiB
C
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Prefix.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the prefix table utility. The two structures
|
|
used in a prefix table are the PREFIX_TABLE and PREFIX_TABLE_ENTRY.
|
|
Each table has one prefix table and multiple prefix table entries
|
|
corresponding to each prefix stored in the table.
|
|
|
|
A prefix table is a list of prefix trees, where each tree contains
|
|
the prefixes corresponding to a particular name length (i.e., all
|
|
prefixes of length 1 are stored in one tree, prefixes of length 2
|
|
are stored in another tree, and so forth). A prefixes name length
|
|
is the number of separate names that appear in the string, and not
|
|
the number of characters in the string (e.g., Length("\alpha\beta") = 2).
|
|
|
|
The elements of each tree are ordered lexicalgraphically (case blind)
|
|
using a splay tree data structure. If two or more prefixes are identical
|
|
except for case then one of the corresponding table entries is actually
|
|
in the tree, while the other entries are in a circular linked list joined
|
|
with the tree member.
|
|
|
|
Author:
|
|
|
|
Gary Kimura [GaryKi] 3-Aug-1989
|
|
|
|
Environment:
|
|
|
|
Pure utility routine
|
|
|
|
Revision History:
|
|
|
|
08-Mar-1993 JulieB Moved Upcase Macro to ntrtlp.h.
|
|
|
|
--*/
|
|
|
|
#include "ntrtlp.h"
|
|
|
|
|
|
// Local procedures and types used only in this package
|
|
|
|
|
|
typedef enum _COMPARISON {
|
|
IsLessThan,
|
|
IsPrefix,
|
|
IsEqual,
|
|
IsGreaterThan
|
|
} COMPARISON;
|
|
|
|
CLONG
|
|
ComputeNameLength(
|
|
IN PSTRING Name
|
|
);
|
|
|
|
COMPARISON
|
|
CompareNamesCaseSensitive (
|
|
IN PSTRING Prefix,
|
|
IN PSTRING Name
|
|
);
|
|
|
|
CLONG
|
|
ComputeUnicodeNameLength(
|
|
IN PUNICODE_STRING Name
|
|
);
|
|
|
|
COMPARISON
|
|
CompareUnicodeStrings (
|
|
IN PUNICODE_STRING Prefix,
|
|
IN PUNICODE_STRING Name,
|
|
IN ULONG CaseInsensitiveIndex
|
|
);
|
|
|
|
#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
|
|
#pragma alloc_text(PAGE,ComputeNameLength)
|
|
#pragma alloc_text(PAGE,CompareNamesCaseSensitive)
|
|
#pragma alloc_text(PAGE,PfxInitialize)
|
|
#pragma alloc_text(PAGE,PfxInsertPrefix)
|
|
#pragma alloc_text(PAGE,PfxRemovePrefix)
|
|
#pragma alloc_text(PAGE,PfxFindPrefix)
|
|
#pragma alloc_text(PAGE,ComputeUnicodeNameLength)
|
|
#pragma alloc_text(PAGE,CompareUnicodeStrings)
|
|
#pragma alloc_text(PAGE,RtlInitializeUnicodePrefix)
|
|
#pragma alloc_text(PAGE,RtlInsertUnicodePrefix)
|
|
#pragma alloc_text(PAGE,RtlRemoveUnicodePrefix)
|
|
#pragma alloc_text(PAGE,RtlFindUnicodePrefix)
|
|
#pragma alloc_text(PAGE,RtlNextUnicodePrefix)
|
|
#endif
|
|
|
|
|
|
|
|
// The node type codes for the prefix data structures
|
|
|
|
|
|
#define RTL_NTC_PREFIX_TABLE ((CSHORT)0x0200)
|
|
#define RTL_NTC_ROOT ((CSHORT)0x0201)
|
|
#define RTL_NTC_INTERNAL ((CSHORT)0x0202)
|
|
|
|
|
|
VOID
|
|
PfxInitialize (
|
|
IN PPREFIX_TABLE PrefixTable
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes a prefix table record to the empty state.
|
|
|
|
Arguments:
|
|
|
|
PrefixTable - Supplies the prefix table being initialized
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
RTL_PAGED_CODE();
|
|
|
|
PrefixTable->NodeTypeCode = RTL_NTC_PREFIX_TABLE;
|
|
|
|
PrefixTable->NameLength = 0;
|
|
|
|
PrefixTable->NextPrefixTree = (PPREFIX_TABLE_ENTRY)PrefixTable;
|
|
|
|
|
|
// return to our caller
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
PfxInsertPrefix (
|
|
IN PPREFIX_TABLE PrefixTable,
|
|
IN PSTRING Prefix,
|
|
IN PPREFIX_TABLE_ENTRY PrefixTableEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine inserts a new prefix into the specified prefix table
|
|
|
|
Arguments:
|
|
|
|
PrefixTable - Supplies the target prefix table
|
|
|
|
Prefix - Supplies the string to be inserted in the prefix table
|
|
|
|
PrefixTableEntry - Supplies the entry to use to insert the prefix
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if the Prefix is not already in the table, and FALSE
|
|
otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG PrefixNameLength;
|
|
|
|
PPREFIX_TABLE_ENTRY PreviousTree;
|
|
PPREFIX_TABLE_ENTRY CurrentTree;
|
|
PPREFIX_TABLE_ENTRY NextTree;
|
|
|
|
PPREFIX_TABLE_ENTRY Node;
|
|
|
|
COMPARISON Comparison;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
|
|
// Determine the name length of the input string
|
|
|
|
|
|
PrefixNameLength = ComputeNameLength(Prefix);
|
|
|
|
|
|
// Setup parts of the prefix table entry that we will always need
|
|
|
|
|
|
PrefixTableEntry->NameLength = (CSHORT)PrefixNameLength;
|
|
PrefixTableEntry->Prefix = Prefix;
|
|
|
|
RtlInitializeSplayLinks(&PrefixTableEntry->Links);
|
|
|
|
|
|
// find the corresponding tree, or find where the tree should go
|
|
|
|
|
|
PreviousTree = (PPREFIX_TABLE_ENTRY)PrefixTable;
|
|
CurrentTree = PreviousTree->NextPrefixTree;
|
|
|
|
while (CurrentTree->NameLength > (CSHORT)PrefixNameLength) {
|
|
|
|
PreviousTree = CurrentTree;
|
|
CurrentTree = CurrentTree->NextPrefixTree;
|
|
|
|
}
|
|
|
|
|
|
// If the name length of the current tree is not equal to the
|
|
// prefix name length then the tree does not exist and we need
|
|
// to make a new tree node.
|
|
|
|
|
|
if (CurrentTree->NameLength != (CSHORT)PrefixNameLength) {
|
|
|
|
|
|
// Insert the new prefix entry to the list between
|
|
// previous and current tree
|
|
|
|
|
|
PreviousTree->NextPrefixTree = PrefixTableEntry;
|
|
PrefixTableEntry->NextPrefixTree = CurrentTree;
|
|
|
|
|
|
// And set the node type code
|
|
|
|
|
|
PrefixTableEntry->NodeTypeCode = RTL_NTC_ROOT;
|
|
|
|
|
|
// And tell our caller everything worked fine
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
// The tree does exist so now search the tree for our
|
|
// position in it. We only exit the loop if we've inserted
|
|
// a new node, and node is left is left pointing to the
|
|
// tree position
|
|
|
|
|
|
Node = CurrentTree;
|
|
|
|
while (TRUE) {
|
|
|
|
|
|
// Compare the prefix in the tree with the prefix we want
|
|
// to insert
|
|
|
|
|
|
Comparison = CompareNamesCaseSensitive(Node->Prefix, Prefix);
|
|
|
|
|
|
// If we do match case sensitive then we cannot add
|
|
// this prefix so we return false. Note this is the
|
|
// only condition where we return false
|
|
|
|
|
|
if (Comparison == IsEqual) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// If the tree prefix is greater than the new prefix then
|
|
// we go down the left subtree
|
|
|
|
|
|
if (Comparison == IsGreaterThan) {
|
|
|
|
|
|
// We want to go down the left subtree, first check to see
|
|
// if we have a left subtree
|
|
|
|
|
|
if (RtlLeftChild(&Node->Links) == NULL) {
|
|
|
|
|
|
// there isn't a left child so we insert ourselves as the
|
|
// new left child
|
|
|
|
|
|
PrefixTableEntry->NodeTypeCode = RTL_NTC_INTERNAL;
|
|
PrefixTableEntry->NextPrefixTree = NULL;
|
|
|
|
RtlInsertAsLeftChild(&Node->Links, &PrefixTableEntry->Links);
|
|
|
|
|
|
// and exit the while loop
|
|
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
|
|
// there is a left child so simply go down that path, and
|
|
// go back to the top of the loop
|
|
|
|
|
|
Node = CONTAINING_RECORD( RtlLeftChild(&Node->Links),
|
|
PREFIX_TABLE_ENTRY,
|
|
Links );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
|
|
// The tree prefix is either less than or a proper prefix
|
|
// of the new string. We treat both cases a less than when
|
|
// we do insert. So we want to go down the right subtree,
|
|
// first check to see if we have a right subtree
|
|
|
|
|
|
if (RtlRightChild(&Node->Links) == NULL) {
|
|
|
|
|
|
// These isn't a right child so we insert ourselves as the
|
|
// new right child
|
|
|
|
|
|
PrefixTableEntry->NodeTypeCode = RTL_NTC_INTERNAL;
|
|
PrefixTableEntry->NextPrefixTree = NULL;
|
|
|
|
RtlInsertAsRightChild(&Node->Links, &PrefixTableEntry->Links);
|
|
|
|
|
|
// and exit the while loop
|
|
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
|
|
// there is a right child so simply go down that path, and
|
|
// go back to the top of the loop
|
|
|
|
|
|
Node = CONTAINING_RECORD( RtlRightChild(&Node->Links),
|
|
PREFIX_TABLE_ENTRY,
|
|
Links );
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// Now that we've inserted the new node we can splay the tree.
|
|
// To do this we need to remember how we find this tree in the root
|
|
// tree list, set the root to be an internal, splay, the tree, and
|
|
// then setup the new root node. Note: we cannot splay the prefix table
|
|
// entry because it might be a case match node so we only splay
|
|
// the Node variable, which for case match insertions is the
|
|
// internal node for the case match and for non-case match insertions
|
|
// the Node variable is the parent node.
|
|
|
|
|
|
|
|
// Save a pointer to the next tree, we already have the previous tree
|
|
|
|
|
|
NextTree = CurrentTree->NextPrefixTree;
|
|
|
|
|
|
// Reset the current root to be an internal node
|
|
|
|
|
|
CurrentTree->NodeTypeCode = RTL_NTC_INTERNAL;
|
|
CurrentTree->NextPrefixTree = NULL;
|
|
|
|
|
|
// Splay the tree and get the root
|
|
|
|
|
|
Node = CONTAINING_RECORD(RtlSplay(&Node->Links), PREFIX_TABLE_ENTRY, Links);
|
|
|
|
|
|
// Set the new root's node type code and make it part of the
|
|
// root tree list
|
|
|
|
|
|
Node->NodeTypeCode = RTL_NTC_ROOT;
|
|
PreviousTree->NextPrefixTree = Node;
|
|
Node->NextPrefixTree = NextTree;
|
|
|
|
|
|
// tell our caller everything worked fine
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
PfxRemovePrefix (
|
|
IN PPREFIX_TABLE PrefixTable,
|
|
IN PPREFIX_TABLE_ENTRY PrefixTableEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine removes the indicated prefix table entry from
|
|
the prefix table
|
|
|
|
Arguments:
|
|
|
|
PrefixTable - Supplies the prefix table affected
|
|
|
|
PrefixTableEntry - Supplies the prefix entry to remove
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PRTL_SPLAY_LINKS Links;
|
|
|
|
PPREFIX_TABLE_ENTRY Root;
|
|
PPREFIX_TABLE_ENTRY NewRoot;
|
|
|
|
PPREFIX_TABLE_ENTRY PreviousTree;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
|
|
// case on the type of node that we are trying to delete
|
|
|
|
|
|
switch (PrefixTableEntry->NodeTypeCode) {
|
|
|
|
case RTL_NTC_INTERNAL:
|
|
case RTL_NTC_ROOT:
|
|
|
|
|
|
// The node is internal or root node so we need to delete it from
|
|
// the tree, but first find the root of the tree
|
|
|
|
|
|
Links = &PrefixTableEntry->Links;
|
|
|
|
while (!RtlIsRoot(Links)) {
|
|
|
|
Links = RtlParent(Links);
|
|
}
|
|
|
|
Root = CONTAINING_RECORD( Links, PREFIX_TABLE_ENTRY, Links );
|
|
|
|
|
|
// Now delete the node
|
|
|
|
|
|
Links = RtlDelete(&PrefixTableEntry->Links);
|
|
|
|
|
|
// Now see if the tree is deleted
|
|
|
|
|
|
if (Links == NULL) {
|
|
|
|
|
|
// The tree is now empty so remove this tree from
|
|
// the tree list, by first finding the previous tree that
|
|
// references us
|
|
|
|
|
|
PreviousTree = Root->NextPrefixTree;
|
|
|
|
while ( PreviousTree->NextPrefixTree != Root ) {
|
|
|
|
PreviousTree = PreviousTree->NextPrefixTree;
|
|
}
|
|
|
|
|
|
// We've located the previous tree so now just have it
|
|
// point around the deleted node
|
|
|
|
|
|
PreviousTree->NextPrefixTree = Root->NextPrefixTree;
|
|
|
|
|
|
// and return the our caller
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// The tree is not deleted but see if we changed roots
|
|
|
|
|
|
if (&Root->Links != Links) {
|
|
|
|
|
|
// Get a pointer to the new root
|
|
|
|
|
|
NewRoot = CONTAINING_RECORD(Links, PREFIX_TABLE_ENTRY, Links);
|
|
|
|
|
|
// We changed root so we better need to make the new
|
|
// root part of the prefix data structure, by
|
|
// first finding the previous tree that
|
|
// references us
|
|
|
|
|
|
PreviousTree = Root->NextPrefixTree;
|
|
|
|
while ( PreviousTree->NextPrefixTree != Root ) {
|
|
|
|
PreviousTree = PreviousTree->NextPrefixTree;
|
|
}
|
|
|
|
|
|
// Set the new root
|
|
|
|
|
|
NewRoot->NodeTypeCode = RTL_NTC_ROOT;
|
|
|
|
PreviousTree->NextPrefixTree = NewRoot;
|
|
NewRoot->NextPrefixTree = Root->NextPrefixTree;
|
|
|
|
|
|
// Set the old root to be an internal node
|
|
|
|
|
|
Root->NodeTypeCode = RTL_NTC_INTERNAL;
|
|
|
|
Root->NextPrefixTree = NULL;
|
|
|
|
|
|
// And return to our caller
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// We didn't change roots so everything is fine and we can
|
|
// simply return to our caller
|
|
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
|
|
// If we get here then there was an error and the node type
|
|
// code is unknown
|
|
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
PPREFIX_TABLE_ENTRY
|
|
PfxFindPrefix (
|
|
IN PPREFIX_TABLE PrefixTable,
|
|
IN PSTRING FullName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds if a full name has a prefix in a prefix table.
|
|
It returns a pointer to the largest proper prefix found if one exists.
|
|
|
|
Arguments:
|
|
|
|
PrefixTable - Supplies the prefix table to search
|
|
|
|
FullString - Supplies the name to search for
|
|
|
|
Return Value:
|
|
|
|
PPREFIX_TABLE_ENTRY - a pointer to the longest prefix found if one
|
|
exists, and NULL otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
CLONG NameLength;
|
|
|
|
PPREFIX_TABLE_ENTRY PreviousTree;
|
|
PPREFIX_TABLE_ENTRY CurrentTree;
|
|
PPREFIX_TABLE_ENTRY NextTree;
|
|
|
|
PRTL_SPLAY_LINKS Links;
|
|
|
|
PPREFIX_TABLE_ENTRY Node;
|
|
|
|
COMPARISON Comparison;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
|
|
// Determine the name length of the input string
|
|
|
|
|
|
NameLength = ComputeNameLength(FullName);
|
|
|
|
|
|
// Locate the first tree that can contain a prefix
|
|
|
|
|
|
PreviousTree = (PPREFIX_TABLE_ENTRY)PrefixTable;
|
|
CurrentTree = PreviousTree->NextPrefixTree;
|
|
|
|
while (CurrentTree->NameLength > (CSHORT)NameLength) {
|
|
|
|
PreviousTree = CurrentTree;
|
|
CurrentTree = CurrentTree->NextPrefixTree;
|
|
}
|
|
|
|
|
|
// Now search for a prefix until we find one or until we exhaust
|
|
// the prefix trees
|
|
|
|
|
|
while (CurrentTree->NameLength > 0) {
|
|
|
|
Links = &CurrentTree->Links;
|
|
|
|
while (Links != NULL) {
|
|
|
|
Node = CONTAINING_RECORD(Links, PREFIX_TABLE_ENTRY, Links);
|
|
|
|
|
|
// Compare the prefix in the tree with the full name
|
|
|
|
|
|
Comparison = CompareNamesCaseSensitive(Node->Prefix, FullName);
|
|
|
|
|
|
// See if they don't match
|
|
|
|
|
|
if (Comparison == IsGreaterThan) {
|
|
|
|
|
|
// The prefix is greater than the full name
|
|
// so we go down the left child
|
|
|
|
|
|
Links = RtlLeftChild(Links);
|
|
|
|
|
|
// And continue searching down this tree
|
|
|
|
|
|
} else if (Comparison == IsLessThan) {
|
|
|
|
|
|
// The prefix is less than the full name
|
|
// so we go down the right child
|
|
|
|
|
|
Links = RtlRightChild(Links);
|
|
|
|
|
|
// And continue searching down this tree
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// We found it.
|
|
|
|
// Now that we've located the node we can splay the tree.
|
|
// To do this we need to remember how we find this tree in the root
|
|
// tree list, set the root to be an internal, splay, the tree, and
|
|
// then setup the new root node.
|
|
|
|
|
|
if (Node->NodeTypeCode == RTL_NTC_INTERNAL) {
|
|
|
|
//DbgPrint("PrefixTable = %08lx\n", PrefixTable);
|
|
//DbgPrint("Node = %08lx\n", Node);
|
|
//DbgPrint("CurrentTree = %08lx\n", CurrentTree);
|
|
//DbgPrint("PreviousTree = %08lx\n", PreviousTree);
|
|
//DbgBreakPoint();
|
|
|
|
|
|
// Save a pointer to the next tree, we already have the previous tree
|
|
|
|
|
|
NextTree = CurrentTree->NextPrefixTree;
|
|
|
|
|
|
// Reset the current root to be an internal node
|
|
|
|
|
|
CurrentTree->NodeTypeCode = RTL_NTC_INTERNAL;
|
|
CurrentTree->NextPrefixTree = NULL;
|
|
|
|
|
|
// Splay the tree and get the root
|
|
|
|
|
|
Node = CONTAINING_RECORD(RtlSplay(&Node->Links), PREFIX_TABLE_ENTRY, Links);
|
|
|
|
|
|
// Set the new root's node type code and make it part of the
|
|
// root tree list
|
|
|
|
|
|
Node->NodeTypeCode = RTL_NTC_ROOT;
|
|
PreviousTree->NextPrefixTree = Node;
|
|
Node->NextPrefixTree = NextTree;
|
|
}
|
|
|
|
return Node;
|
|
}
|
|
}
|
|
|
|
|
|
// This tree is done so now find the next tree
|
|
|
|
|
|
PreviousTree = CurrentTree;
|
|
CurrentTree = CurrentTree->NextPrefixTree;
|
|
}
|
|
|
|
|
|
// We sesarched everywhere and didn't find a prefix so tell the
|
|
// caller none was found
|
|
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
CLONG
|
|
ComputeNameLength(
|
|
IN PSTRING Name
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine counts the number of names appearing in the input string.
|
|
It does this by simply counting the number of backslashes in the string.
|
|
To handle ill-formed names (i.e., names that do not contain a backslash)
|
|
this routine really returns the number of backslashes plus 1.
|
|
|
|
Arguments:
|
|
|
|
Name - Supplies the input name to examine
|
|
|
|
Returns Value:
|
|
|
|
CLONG - the number of names in the input string
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG NameLength;
|
|
ULONG i;
|
|
ULONG Count;
|
|
|
|
extern PUSHORT NlsLeadByteInfo; // Lead byte info. for ACP ( nlsxlat.c )
|
|
extern BOOLEAN NlsMbCodePageTag;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
|
|
// Save the name length, this should make the compiler be able to
|
|
// optimize not having to reload the length each time
|
|
|
|
|
|
NameLength = Name->Length - 1;
|
|
|
|
|
|
// Now loop through the input string counting back slashes
|
|
|
|
|
|
if (NlsMbCodePageTag) {
|
|
|
|
|
|
// ComputeNameLength() skip DBCS character when counting '\'
|
|
|
|
|
|
for (i = 0, Count = 1; i < NameLength; ) {
|
|
|
|
if (NlsLeadByteInfo[(UCHAR)Name->Buffer[i]]) {
|
|
|
|
i += 2;
|
|
|
|
} else {
|
|
|
|
if (Name->Buffer[i] == '\\') {
|
|
|
|
Count += 1;
|
|
}
|
|
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
for (i = 0, Count = 1; i < NameLength; i += 1) {
|
|
|
|
|
|
// check for a back slash
|
|
|
|
|
|
if (Name->Buffer[i] == '\\') {
|
|
|
|
Count += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// return the number of back slashes we found
|
|
|
|
|
|
//DbgPrint("ComputeNameLength(%s) = %x\n", Name->Buffer, Count);
|
|
|
|
return Count;
|
|
}
|
|
|
|
|
|
COMPARISON
|
|
CompareNamesCaseSensitive (
|
|
IN PSTRING Prefix,
|
|
IN PSTRING Name
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes a prefix string and a full name string and determines
|
|
if the prefix string is a proper prefix of the name string (case sensitive)
|
|
|
|
Arguments:
|
|
|
|
Prefix - Supplies the input prefix string
|
|
|
|
Name - Supplies the full name input string
|
|
|
|
Return Value:
|
|
|
|
COMPARISON - returns
|
|
|
|
IsLessThan if Prefix < Name lexicalgraphically,
|
|
IsPrefix if Prefix is a proper prefix of Name
|
|
IsEqual if Prefix is equal to Name, and
|
|
IsGreaterThan if Prefix > Name lexicalgraphically
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG PrefixLength;
|
|
ULONG NameLength;
|
|
ULONG MinLength;
|
|
ULONG i;
|
|
|
|
UCHAR PrefixChar;
|
|
UCHAR NameChar;
|
|
|
|
extern PUSHORT NlsLeadByteInfo; // Lead byte info. for ACP ( nlsxlat.c )
|
|
extern BOOLEAN NlsMbCodePageTag;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
//DbgPrint("CompareNamesCaseSensitive(\"%s\", \"%s\") = ", Prefix->Buffer, Name->Buffer);
|
|
|
|
|
|
// Save the length of the prefix and name string, this should allow
|
|
// the compiler to not need to reload the length through a pointer every
|
|
// time we need their values
|
|
|
|
|
|
PrefixLength = Prefix->Length;
|
|
NameLength = Name->Length;
|
|
|
|
|
|
// Special case the situation where the prefix string is simply "\" and
|
|
// the name starts with an "\"
|
|
|
|
|
|
if ((Prefix->Length == 1) && (Prefix->Buffer[0] == '\\') &&
|
|
(Name->Length > 1) && (Name->Buffer[0] == '\\')) {
|
|
//DbgPrint("IsPrefix\n");
|
|
return IsPrefix;
|
|
}
|
|
|
|
|
|
// Figure out the minimum of the two lengths
|
|
|
|
|
|
MinLength = (PrefixLength < NameLength ? PrefixLength : NameLength);
|
|
|
|
|
|
// Loop through looking at all of the characters in both strings
|
|
// testing for equalilty, less than, and greater than
|
|
|
|
|
|
i = (ULONG) RtlCompareMemory( &Prefix->Buffer[0], &Name->Buffer[0], MinLength );
|
|
|
|
if (i < MinLength) {
|
|
|
|
UCHAR c;
|
|
|
|
|
|
// Get both characters to examine and keep their case
|
|
|
|
|
|
PrefixChar = ((c = Prefix->Buffer[i]) == '\\' ? (CHAR)0 : c);
|
|
NameChar = ((c = Name->Buffer[i]) == '\\' ? (CHAR)0 : c);
|
|
|
|
|
|
// Unfortunately life is not so easy in DBCS land.
|
|
|
|
|
|
if (NlsMbCodePageTag) {
|
|
|
|
|
|
// CompareNamesCaseSensitive(): check backslash in trailing bytes
|
|
|
|
|
|
if (Prefix->Buffer[i] == '\\') {
|
|
|
|
ULONG j;
|
|
extern PUSHORT NlsLeadByteInfo; // Lead byte info. for ACP ( nlsxlat.c )
|
|
|
|
for (j = 0; j < i;) {
|
|
|
|
j += NlsLeadByteInfo[(UCHAR)Prefix->Buffer[j]] ? 2 : 1;
|
|
}
|
|
|
|
if (j != i) {
|
|
|
|
PrefixChar = '\\';
|
|
//DbgPrint("RTL:CompareNamesCaseSensitive encountered a fake backslash!\n");
|
|
}
|
|
}
|
|
|
|
if (Name->Buffer[i] == '\\') {
|
|
|
|
ULONG j;
|
|
extern PUSHORT NlsLeadByteInfo; // Lead byte info. for ACP ( nlsxlat.c )
|
|
|
|
for (j = 0; j < i;) {
|
|
|
|
j += NlsLeadByteInfo[(UCHAR)Name->Buffer[j]] ? 2 : 1;
|
|
}
|
|
|
|
if (j != i) {
|
|
|
|
NameChar = '\\';
|
|
//DbgPrint("RTL:CompareNamesCaseSensitive encountered a fake backslash!\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Now compare the characters
|
|
|
|
|
|
if (PrefixChar < NameChar) {
|
|
|
|
return IsLessThan;
|
|
|
|
} else if (PrefixChar > NameChar) {
|
|
|
|
return IsGreaterThan;
|
|
}
|
|
}
|
|
|
|
|
|
// They match upto the minimum length so now figure out the largest string
|
|
// and see if one is a proper prefix of the other
|
|
|
|
|
|
if (PrefixLength < NameLength) {
|
|
|
|
|
|
// The prefix string is shorter so if it is a proper prefix we
|
|
// return prefix otherwise we return less than (e.g., "\a" < "\ab")
|
|
|
|
|
|
if (Name->Buffer[PrefixLength] == '\\') {
|
|
|
|
return IsPrefix;
|
|
|
|
} else {
|
|
|
|
return IsLessThan;
|
|
}
|
|
|
|
} else if (PrefixLength > NameLength) {
|
|
|
|
|
|
// The Prefix string is longer so we say that the prefix is
|
|
// greater than the name (e.g., "\ab" > "\a")
|
|
|
|
|
|
return IsGreaterThan;
|
|
|
|
} else {
|
|
|
|
|
|
// They lengths are equal so the strings are equal
|
|
|
|
|
|
return IsEqual;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// The node type codes for the prefix data structures
|
|
|
|
|
|
#define RTL_NTC_UNICODE_PREFIX_TABLE ((CSHORT)0x0800)
|
|
#define RTL_NTC_UNICODE_ROOT ((CSHORT)0x0801)
|
|
#define RTL_NTC_UNICODE_INTERNAL ((CSHORT)0x0802)
|
|
#define RTL_NTC_UNICODE_CASE_MATCH ((CSHORT)0x0803)
|
|
|
|
|
|
VOID
|
|
RtlInitializeUnicodePrefix (
|
|
IN PUNICODE_PREFIX_TABLE PrefixTable
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes a unicode prefix table record to the empty state.
|
|
|
|
Arguments:
|
|
|
|
PrefixTable - Supplies the prefix table being initialized
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
RTL_PAGED_CODE();
|
|
|
|
PrefixTable->NodeTypeCode = RTL_NTC_UNICODE_PREFIX_TABLE;
|
|
PrefixTable->NameLength = 0;
|
|
PrefixTable->NextPrefixTree = (PUNICODE_PREFIX_TABLE_ENTRY)PrefixTable;
|
|
PrefixTable->LastNextEntry = NULL;
|
|
|
|
|
|
// return to our caller
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
RtlInsertUnicodePrefix (
|
|
IN PUNICODE_PREFIX_TABLE PrefixTable,
|
|
IN PUNICODE_STRING Prefix,
|
|
IN PUNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine inserts a new unicode prefix into the specified prefix table
|
|
|
|
Arguments:
|
|
|
|
PrefixTable - Supplies the target prefix table
|
|
|
|
Prefix - Supplies the string to be inserted in the prefix table
|
|
|
|
PrefixTableEntry - Supplies the entry to use to insert the prefix
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if the Prefix is not already in the table, and FALSE
|
|
otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG PrefixNameLength;
|
|
|
|
PUNICODE_PREFIX_TABLE_ENTRY PreviousTree;
|
|
PUNICODE_PREFIX_TABLE_ENTRY CurrentTree;
|
|
PUNICODE_PREFIX_TABLE_ENTRY NextTree;
|
|
|
|
PUNICODE_PREFIX_TABLE_ENTRY Node;
|
|
|
|
COMPARISON Comparison;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
|
|
// Determine the name length of the input string
|
|
|
|
|
|
PrefixNameLength = ComputeUnicodeNameLength(Prefix);
|
|
|
|
|
|
// Setup parts of the prefix table entry that we will always need
|
|
|
|
|
|
PrefixTableEntry->NameLength = (CSHORT)PrefixNameLength;
|
|
PrefixTableEntry->Prefix = Prefix;
|
|
|
|
RtlInitializeSplayLinks(&PrefixTableEntry->Links);
|
|
|
|
|
|
// find the corresponding tree, or find where the tree should go
|
|
|
|
|
|
PreviousTree = (PUNICODE_PREFIX_TABLE_ENTRY)PrefixTable;
|
|
CurrentTree = PreviousTree->NextPrefixTree;
|
|
|
|
while (CurrentTree->NameLength > (CSHORT)PrefixNameLength) {
|
|
|
|
PreviousTree = CurrentTree;
|
|
CurrentTree = CurrentTree->NextPrefixTree;
|
|
}
|
|
|
|
|
|
// If the name length of the current tree is not equal to the
|
|
// prefix name length then the tree does not exist and we need
|
|
// to make a new tree node.
|
|
|
|
|
|
if (CurrentTree->NameLength != (CSHORT)PrefixNameLength) {
|
|
|
|
|
|
// Insert the new prefix entry to the list between
|
|
// previous and current tree
|
|
|
|
|
|
PreviousTree->NextPrefixTree = PrefixTableEntry;
|
|
PrefixTableEntry->NextPrefixTree = CurrentTree;
|
|
|
|
|
|
// And set the node type code, case match for the root tree node
|
|
|
|
|
|
PrefixTableEntry->NodeTypeCode = RTL_NTC_UNICODE_ROOT;
|
|
PrefixTableEntry->CaseMatch = PrefixTableEntry;
|
|
|
|
|
|
// And tell our caller everything worked fine
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// The tree does exist so now search the tree for our
|
|
// position in it. We only exit the loop if we've inserted
|
|
// a new node, and node is left is left pointing to the
|
|
// tree position
|
|
|
|
|
|
Node = CurrentTree;
|
|
|
|
while (TRUE) {
|
|
|
|
|
|
// Compare the prefix in the tree with the prefix we want
|
|
// to insert. Do the compare case blind
|
|
|
|
|
|
Comparison = CompareUnicodeStrings(Node->Prefix, Prefix, 0);
|
|
|
|
|
|
// If they are equal then this node gets added as a case
|
|
// match, provided it doesn't case sensitive match anyone
|
|
|
|
|
|
if (Comparison == IsEqual) {
|
|
|
|
PUNICODE_PREFIX_TABLE_ENTRY Next;
|
|
|
|
|
|
// Loop through the case match list checking to see if we
|
|
// match case sensitive with anyone. Get the first node
|
|
|
|
|
|
Next = Node;
|
|
|
|
|
|
// And loop checking each node until we're back to where
|
|
// we started
|
|
|
|
|
|
do {
|
|
|
|
|
|
// If we do match case sensitive then we cannot add
|
|
// this prefix so we return false. Note this is the
|
|
// only condition where we return false
|
|
|
|
|
|
if (CompareUnicodeStrings(Next->Prefix, Prefix, MAXULONG) == IsEqual) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// Get the next node in the case match list
|
|
|
|
|
|
Next = Next->CaseMatch;
|
|
|
|
|
|
// And continue looping until we're back where we started
|
|
|
|
|
|
} while ( Next != Node );
|
|
|
|
|
|
// We've searched the case match and didn't find an exact match
|
|
// so we can insert this node in the case match list
|
|
|
|
|
|
PrefixTableEntry->NodeTypeCode = RTL_NTC_UNICODE_CASE_MATCH;
|
|
PrefixTableEntry->NextPrefixTree = NULL;
|
|
|
|
PrefixTableEntry->CaseMatch = Node->CaseMatch;
|
|
Node->CaseMatch = PrefixTableEntry;
|
|
|
|
|
|
// And exit out of the while loop
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
// If the tree prefix is greater than the new prefix then
|
|
// we go down the left subtree
|
|
|
|
|
|
if (Comparison == IsGreaterThan) {
|
|
|
|
|
|
// We want to go down the left subtree, first check to see
|
|
// if we have a left subtree
|
|
|
|
|
|
if (RtlLeftChild(&Node->Links) == NULL) {
|
|
|
|
|
|
// there isn't a left child so we insert ourselves as the
|
|
// new left child
|
|
|
|
|
|
PrefixTableEntry->NodeTypeCode = RTL_NTC_UNICODE_INTERNAL;
|
|
PrefixTableEntry->NextPrefixTree = NULL;
|
|
PrefixTableEntry->CaseMatch = PrefixTableEntry;
|
|
|
|
RtlInsertAsLeftChild(&Node->Links, &PrefixTableEntry->Links);
|
|
|
|
|
|
// and exit the while loop
|
|
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
|
|
// there is a left child so simply go down that path, and
|
|
// go back to the top of the loop
|
|
|
|
|
|
Node = CONTAINING_RECORD( RtlLeftChild(&Node->Links),
|
|
UNICODE_PREFIX_TABLE_ENTRY,
|
|
Links );
|
|
}
|
|
|
|
} else {
|
|
|
|
|
|
// The tree prefix is either less than or a proper prefix
|
|
// of the new string. We treat both cases a less than when
|
|
// we do insert. So we want to go down the right subtree,
|
|
// first check to see if we have a right subtree
|
|
|
|
|
|
if (RtlRightChild(&Node->Links) == NULL) {
|
|
|
|
|
|
// These isn't a right child so we insert ourselves as the
|
|
// new right child
|
|
|
|
|
|
PrefixTableEntry->NodeTypeCode = RTL_NTC_UNICODE_INTERNAL;
|
|
PrefixTableEntry->NextPrefixTree = NULL;
|
|
PrefixTableEntry->CaseMatch = PrefixTableEntry;
|
|
|
|
RtlInsertAsRightChild(&Node->Links, &PrefixTableEntry->Links);
|
|
|
|
|
|
// and exit the while loop
|
|
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
|
|
// there is a right child so simply go down that path, and
|
|
// go back to the top of the loop
|
|
|
|
|
|
Node = CONTAINING_RECORD( RtlRightChild(&Node->Links),
|
|
UNICODE_PREFIX_TABLE_ENTRY,
|
|
Links );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Now that we've inserted the new node we can splay the tree.
|
|
// To do this we need to remember how we find this tree in the root
|
|
// tree list, set the root to be an internal, splay, the tree, and
|
|
// then setup the new root node. Note: we cannot splay the prefix table
|
|
// entry because it might be a case match node so we only splay
|
|
// the Node variable, which for case match insertions is the
|
|
// internal node for the case match and for non-case match insertions
|
|
// the Node variable is the parent node.
|
|
|
|
|
|
|
|
// Save a pointer to the next tree, we already have the previous tree
|
|
|
|
|
|
NextTree = CurrentTree->NextPrefixTree;
|
|
|
|
|
|
// Reset the current root to be an internal node
|
|
|
|
|
|
CurrentTree->NodeTypeCode = RTL_NTC_UNICODE_INTERNAL;
|
|
CurrentTree->NextPrefixTree = NULL;
|
|
|
|
|
|
// Splay the tree and get the root
|
|
|
|
|
|
Node = CONTAINING_RECORD(RtlSplay(&Node->Links), UNICODE_PREFIX_TABLE_ENTRY, Links);
|
|
|
|
|
|
// Set the new root's node type code and make it part of the
|
|
// root tree list
|
|
|
|
|
|
Node->NodeTypeCode = RTL_NTC_UNICODE_ROOT;
|
|
PreviousTree->NextPrefixTree = Node;
|
|
Node->NextPrefixTree = NextTree;
|
|
|
|
|
|
// tell our caller everything worked fine
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
RtlRemoveUnicodePrefix (
|
|
IN PUNICODE_PREFIX_TABLE PrefixTable,
|
|
IN PUNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine removes the indicated prefix table entry from
|
|
the prefix table
|
|
|
|
Arguments:
|
|
|
|
PrefixTable - Supplies the prefix table affected
|
|
|
|
PrefixTableEntry - Supplies the prefix entry to remove
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PUNICODE_PREFIX_TABLE_ENTRY PreviousCaseMatch;
|
|
|
|
PRTL_SPLAY_LINKS Links;
|
|
|
|
PUNICODE_PREFIX_TABLE_ENTRY Root;
|
|
PUNICODE_PREFIX_TABLE_ENTRY NewRoot;
|
|
|
|
PUNICODE_PREFIX_TABLE_ENTRY PreviousTree;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
|
|
// Wipe out the next last entry field of the prefix table
|
|
|
|
|
|
PrefixTable->LastNextEntry = NULL;
|
|
|
|
|
|
// case on the type of node that we are trying to delete
|
|
|
|
|
|
switch (PrefixTableEntry->NodeTypeCode) {
|
|
|
|
case RTL_NTC_UNICODE_CASE_MATCH:
|
|
|
|
|
|
// The prefix entry is a case match record so
|
|
// we only need to remove it from the case match list.
|
|
// Locate the previous PrefixTableEntry that reference this
|
|
// case match record
|
|
|
|
|
|
PreviousCaseMatch = PrefixTableEntry->CaseMatch;
|
|
|
|
while ( PreviousCaseMatch->CaseMatch != PrefixTableEntry ) {
|
|
|
|
PreviousCaseMatch = PreviousCaseMatch->CaseMatch;
|
|
}
|
|
|
|
|
|
// Now that we have the previous record just have it point
|
|
// around the case match that is being deleted
|
|
|
|
|
|
PreviousCaseMatch->CaseMatch = PrefixTableEntry->CaseMatch;
|
|
|
|
|
|
// And return to our caller
|
|
|
|
|
|
return;
|
|
|
|
case RTL_NTC_UNICODE_INTERNAL:
|
|
case RTL_NTC_UNICODE_ROOT:
|
|
|
|
|
|
// The prefix entry is an internal/root node so check to see if it
|
|
// has any case match nodes with it
|
|
|
|
|
|
if (PrefixTableEntry->CaseMatch != PrefixTableEntry) {
|
|
|
|
|
|
// There is at least one case match that goes with this
|
|
// node, so we need to make the next case match the
|
|
// new node and remove this node.
|
|
// Locate the previous prefix table entry that references this
|
|
// case match record
|
|
|
|
|
|
PreviousCaseMatch = PrefixTableEntry->CaseMatch;
|
|
|
|
while ( PreviousCaseMatch->CaseMatch != PrefixTableEntry ) {
|
|
|
|
PreviousCaseMatch = PreviousCaseMatch->CaseMatch;
|
|
}
|
|
|
|
|
|
// Now that we have the previous record just have it point
|
|
// around the node being deleted
|
|
|
|
|
|
PreviousCaseMatch->CaseMatch = PrefixTableEntry->CaseMatch;
|
|
|
|
|
|
// Now make the previous case match in the new node
|
|
|
|
|
|
PreviousCaseMatch->NodeTypeCode = PrefixTableEntry->NodeTypeCode;
|
|
PreviousCaseMatch->NextPrefixTree = PrefixTableEntry->NextPrefixTree;
|
|
PreviousCaseMatch->Links = PrefixTableEntry->Links;
|
|
|
|
|
|
// Now take care of the back pointers to this new internal
|
|
// node in the splay tree, first do the parent's pointer to us.
|
|
|
|
|
|
if (RtlIsRoot(&PrefixTableEntry->Links)) {
|
|
|
|
|
|
// This is the root so make this new node the root
|
|
|
|
|
|
PreviousCaseMatch->Links.Parent = &PreviousCaseMatch->Links;
|
|
|
|
|
|
// Fix up the root tree list, by first finding the previous
|
|
// pointer to us
|
|
|
|
PreviousTree = PrefixTableEntry->NextPrefixTree;
|
|
|
|
while ( PreviousTree->NextPrefixTree != PrefixTableEntry ) {
|
|
|
|
PreviousTree = PreviousTree->NextPrefixTree;
|
|
}
|
|
|
|
|
|
// We've located the previous tree so now have the previous
|
|
// tree point to our new root
|
|
|
|
|
|
PreviousTree->NextPrefixTree = PreviousCaseMatch;
|
|
|
|
} else if (RtlIsLeftChild(&PrefixTableEntry->Links)) {
|
|
|
|
|
|
// The node was the left child so make the new node the
|
|
// left child
|
|
|
|
|
|
RtlParent(&PrefixTableEntry->Links)->LeftChild = &PreviousCaseMatch->Links;
|
|
|
|
} else {
|
|
|
|
|
|
// The node was the right child so make the new node the
|
|
// right child
|
|
|
|
|
|
RtlParent(&PrefixTableEntry->Links)->RightChild = &PreviousCaseMatch->Links;
|
|
}
|
|
|
|
|
|
// Now update the parent pointer for our new children
|
|
|
|
|
|
if (RtlLeftChild(&PreviousCaseMatch->Links) != NULL) {
|
|
|
|
RtlLeftChild(&PreviousCaseMatch->Links)->Parent = &PreviousCaseMatch->Links;
|
|
}
|
|
|
|
if (RtlRightChild(&PreviousCaseMatch->Links) != NULL) {
|
|
|
|
RtlRightChild(&PreviousCaseMatch->Links)->Parent = &PreviousCaseMatch->Links;
|
|
}
|
|
|
|
|
|
// And return to our caller
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// The node is internal or root node and does not have any case match
|
|
// nodes so we need to delete it from the tree, but first find
|
|
// the root of the tree
|
|
|
|
|
|
Links = &PrefixTableEntry->Links;
|
|
|
|
while (!RtlIsRoot(Links)) {
|
|
|
|
Links = RtlParent(Links);
|
|
}
|
|
|
|
Root = CONTAINING_RECORD( Links, UNICODE_PREFIX_TABLE_ENTRY, Links );
|
|
|
|
|
|
// Now delete the node
|
|
|
|
|
|
Links = RtlDelete(&PrefixTableEntry->Links);
|
|
|
|
|
|
// Now see if the tree is deleted
|
|
|
|
|
|
if (Links == NULL) {
|
|
|
|
|
|
// The tree is now empty so remove this tree from
|
|
// the tree list, by first finding the previous tree that
|
|
// references us
|
|
|
|
|
|
PreviousTree = Root->NextPrefixTree;
|
|
|
|
while ( PreviousTree->NextPrefixTree != Root ) {
|
|
|
|
PreviousTree = PreviousTree->NextPrefixTree;
|
|
}
|
|
|
|
|
|
// We've located the previous tree so now just have it
|
|
// point around the deleted node
|
|
|
|
|
|
PreviousTree->NextPrefixTree = Root->NextPrefixTree;
|
|
|
|
|
|
// and return the our caller
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// The tree is not deleted but see if we changed roots
|
|
|
|
|
|
if (&Root->Links != Links) {
|
|
|
|
|
|
// Get a pointer to the new root
|
|
|
|
|
|
NewRoot = CONTAINING_RECORD(Links, UNICODE_PREFIX_TABLE_ENTRY, Links);
|
|
|
|
|
|
// We changed root so we better need to make the new
|
|
// root part of the prefix data structure, by
|
|
// first finding the previous tree that
|
|
// references us
|
|
|
|
|
|
PreviousTree = Root->NextPrefixTree;
|
|
|
|
while ( PreviousTree->NextPrefixTree != Root ) {
|
|
|
|
PreviousTree = PreviousTree->NextPrefixTree;
|
|
}
|
|
|
|
|
|
// Set the new root
|
|
|
|
|
|
NewRoot->NodeTypeCode = RTL_NTC_UNICODE_ROOT;
|
|
|
|
PreviousTree->NextPrefixTree = NewRoot;
|
|
NewRoot->NextPrefixTree = Root->NextPrefixTree;
|
|
|
|
|
|
// Set the old root to be an internal node
|
|
|
|
|
|
Root->NodeTypeCode = RTL_NTC_UNICODE_INTERNAL;
|
|
|
|
Root->NextPrefixTree = NULL;
|
|
|
|
|
|
// And return to our caller
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// We didn't change roots so everything is fine and we can
|
|
// simply return to our caller
|
|
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
|
|
// If we get here then there was an error and the node type
|
|
// code is unknown
|
|
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
PUNICODE_PREFIX_TABLE_ENTRY
|
|
RtlFindUnicodePrefix (
|
|
IN PUNICODE_PREFIX_TABLE PrefixTable,
|
|
IN PUNICODE_STRING FullName,
|
|
IN ULONG CaseInsensitiveIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds if a full name has a prefix in a prefix table.
|
|
It returns a pointer to the largest proper prefix found if one exists.
|
|
|
|
Arguments:
|
|
|
|
PrefixTable - Supplies the prefix table to search
|
|
|
|
FullString - Supplies the name to search for
|
|
|
|
CaseInsensitiveIndex - Indicates the wchar index at which to do a case
|
|
insensitive search. All characters before the index are searched
|
|
case sensitive and all characters at and after the index are searched
|
|
insensitive.
|
|
|
|
Return Value:
|
|
|
|
PPREFIX_TABLE_ENTRY - a pointer to the longest prefix found if one
|
|
exists, and NULL otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
CLONG NameLength;
|
|
|
|
PUNICODE_PREFIX_TABLE_ENTRY PreviousTree;
|
|
PUNICODE_PREFIX_TABLE_ENTRY CurrentTree;
|
|
PUNICODE_PREFIX_TABLE_ENTRY NextTree;
|
|
|
|
PRTL_SPLAY_LINKS Links;
|
|
|
|
PUNICODE_PREFIX_TABLE_ENTRY Node;
|
|
PUNICODE_PREFIX_TABLE_ENTRY Next;
|
|
|
|
COMPARISON Comparison;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
|
|
// Determine the name length of the input string
|
|
|
|
|
|
NameLength = ComputeUnicodeNameLength(FullName);
|
|
|
|
|
|
// Locate the first tree that can contain a prefix
|
|
|
|
|
|
PreviousTree = (PUNICODE_PREFIX_TABLE_ENTRY)PrefixTable;
|
|
CurrentTree = PreviousTree->NextPrefixTree;
|
|
|
|
while (CurrentTree->NameLength > (CSHORT)NameLength) {
|
|
|
|
PreviousTree = CurrentTree;
|
|
CurrentTree = CurrentTree->NextPrefixTree;
|
|
}
|
|
|
|
|
|
// Now search for a prefix until we find one or until we exhaust
|
|
// the prefix trees
|
|
|
|
|
|
while (CurrentTree->NameLength > 0) {
|
|
|
|
Links = &CurrentTree->Links;
|
|
|
|
while (Links != NULL) {
|
|
|
|
Node = CONTAINING_RECORD(Links, UNICODE_PREFIX_TABLE_ENTRY, Links);
|
|
|
|
|
|
// Compare the prefix in the tree with the full name, do the
|
|
// compare case blind
|
|
|
|
|
|
Comparison = CompareUnicodeStrings(Node->Prefix, FullName, 0);
|
|
|
|
|
|
// See if they don't match
|
|
|
|
|
|
if (Comparison == IsGreaterThan) {
|
|
|
|
|
|
// The prefix is greater than the full name
|
|
// so we go down the left child
|
|
|
|
|
|
Links = RtlLeftChild(Links);
|
|
|
|
|
|
// And continue searching down this tree
|
|
|
|
|
|
} else if (Comparison == IsLessThan) {
|
|
|
|
|
|
// The prefix is less than the full name
|
|
// so we go down the right child
|
|
|
|
|
|
Links = RtlRightChild(Links);
|
|
|
|
|
|
// And continue searching down this tree
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// We have either a prefix or a match either way
|
|
// we need to check if we should do case sensitive
|
|
// seearches
|
|
|
|
|
|
if (CaseInsensitiveIndex == 0) {
|
|
|
|
|
|
// The caller wants case insensitive so we'll
|
|
// return the first one we found
|
|
|
|
// Now that we've located the node we can splay the tree.
|
|
// To do this we need to remember how we find this tree in the root
|
|
// tree list, set the root to be an internal, splay, the tree, and
|
|
// then setup the new root node.
|
|
|
|
|
|
if (Node->NodeTypeCode == RTL_NTC_UNICODE_INTERNAL) {
|
|
|
|
//DbgPrint("PrefixTable = %08lx\n", PrefixTable);
|
|
//DbgPrint("Node = %08lx\n", Node);
|
|
//DbgPrint("CurrentTree = %08lx\n", CurrentTree);
|
|
//DbgPrint("PreviousTree = %08lx\n", PreviousTree);
|
|
//DbgBreakPoint();
|
|
|
|
|
|
// Save a pointer to the next tree, we already have the previous tree
|
|
|
|
|
|
NextTree = CurrentTree->NextPrefixTree;
|
|
|
|
|
|
// Reset the current root to be an internal node
|
|
|
|
|
|
CurrentTree->NodeTypeCode = RTL_NTC_UNICODE_INTERNAL;
|
|
CurrentTree->NextPrefixTree = NULL;
|
|
|
|
|
|
// Splay the tree and get the root
|
|
|
|
|
|
Node = CONTAINING_RECORD(RtlSplay(&Node->Links), UNICODE_PREFIX_TABLE_ENTRY, Links);
|
|
|
|
|
|
// Set the new root's node type code and make it part of the
|
|
// root tree list
|
|
|
|
|
|
Node->NodeTypeCode = RTL_NTC_UNICODE_ROOT;
|
|
PreviousTree->NextPrefixTree = Node;
|
|
Node->NextPrefixTree = NextTree;
|
|
}
|
|
|
|
|
|
// Now return the root to our caller
|
|
|
|
|
|
return Node;
|
|
}
|
|
|
|
|
|
// The caller wants an exact match so search the case match
|
|
// until we find a complete match. Get the first node
|
|
|
|
|
|
Next = Node;
|
|
|
|
|
|
// Loop through the case match list checking to see if we
|
|
// match case sensitive with anyone.
|
|
|
|
|
|
do {
|
|
|
|
|
|
// If we do match case sensitive then we found one
|
|
// and we return it to our caller
|
|
|
|
|
|
Comparison = CompareUnicodeStrings( Next->Prefix,
|
|
FullName,
|
|
CaseInsensitiveIndex );
|
|
|
|
if ((Comparison == IsEqual) || (Comparison == IsPrefix)) {
|
|
|
|
|
|
// We found a good one, so return it to our caller
|
|
|
|
|
|
return Next;
|
|
}
|
|
|
|
|
|
// Get the next case match record
|
|
|
|
|
|
Next = Next->CaseMatch;
|
|
|
|
|
|
// And continue the loop until we reach the original
|
|
// node again
|
|
|
|
|
|
} while ( Next != Node );
|
|
|
|
|
|
// We found a case blind prefix but the caller wants
|
|
// case sensitive and we weren't able to find one of those
|
|
// so we need to go on to the next tree, by breaking out
|
|
// of the inner while-loop
|
|
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// This tree is done so now find the next tree
|
|
|
|
|
|
PreviousTree = CurrentTree;
|
|
CurrentTree = CurrentTree->NextPrefixTree;
|
|
}
|
|
|
|
|
|
// We sesarched everywhere and didn't find a prefix so tell the
|
|
// caller none was found
|
|
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PUNICODE_PREFIX_TABLE_ENTRY
|
|
RtlNextUnicodePrefix (
|
|
IN PUNICODE_PREFIX_TABLE PrefixTable,
|
|
IN BOOLEAN Restart
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the next prefix entry stored in the prefix table
|
|
|
|
Arguments:
|
|
|
|
PrefixTable - Supplies the prefix table to enumerate
|
|
|
|
Restart - Indicates if the enumeration should start over
|
|
|
|
Return Value:
|
|
|
|
PPREFIX_TABLE_ENTRY - A pointer to the next prefix table entry if
|
|
one exists otherwise NULL
|
|
|
|
--*/
|
|
|
|
{
|
|
PUNICODE_PREFIX_TABLE_ENTRY Node;
|
|
|
|
PRTL_SPLAY_LINKS Links;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
|
|
// See if we are restarting the sequence
|
|
|
|
|
|
if (Restart || (PrefixTable->LastNextEntry == NULL)) {
|
|
|
|
|
|
// we are restarting the sequence so locate the first entry
|
|
// in the first tree
|
|
|
|
|
|
Node = PrefixTable->NextPrefixTree;
|
|
|
|
|
|
// Make sure we've pointing at a prefix tree
|
|
|
|
|
|
if (Node->NodeTypeCode == RTL_NTC_UNICODE_PREFIX_TABLE) {
|
|
|
|
|
|
// No we aren't so the table must be empty
|
|
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// Find the first node in the tree
|
|
|
|
|
|
Links = &Node->Links;
|
|
|
|
while (RtlLeftChild(Links) != NULL) {
|
|
|
|
Links = RtlLeftChild(Links);
|
|
}
|
|
|
|
|
|
// Set it as our the node we're returning
|
|
|
|
|
|
Node = CONTAINING_RECORD( Links, UNICODE_PREFIX_TABLE_ENTRY, Links);
|
|
|
|
} else if (PrefixTable->LastNextEntry->CaseMatch->NodeTypeCode == RTL_NTC_UNICODE_CASE_MATCH) {
|
|
|
|
|
|
// The last node has a case match that we should be returning
|
|
// this time around
|
|
|
|
|
|
Node = PrefixTable->LastNextEntry->CaseMatch;
|
|
|
|
} else {
|
|
|
|
|
|
// Move over the last node returned by the case match link, this
|
|
// will enable us to finish off the last case match node if there
|
|
// was one, and go to the next internal/root node. If this node
|
|
// does not have a case match then we simply circle back to ourselves
|
|
|
|
|
|
Node = PrefixTable->LastNextEntry->CaseMatch;
|
|
|
|
|
|
// Find the successor for the last node we returned
|
|
|
|
|
|
Links = RtlRealSuccessor(&Node->Links);
|
|
|
|
|
|
// If links is null then we've exhausted this tree and need to
|
|
// the the next tree to use
|
|
|
|
|
|
if (Links == NULL) {
|
|
|
|
Links = &PrefixTable->LastNextEntry->Links;
|
|
|
|
while (!RtlIsRoot(Links)) {
|
|
|
|
Links = RtlParent(Links);
|
|
}
|
|
|
|
Node = CONTAINING_RECORD(Links, UNICODE_PREFIX_TABLE_ENTRY, Links);
|
|
|
|
|
|
// Now we've found the root see if there is another
|
|
// tree to enumerate
|
|
|
|
|
|
Node = Node->NextPrefixTree;
|
|
|
|
if (Node->NameLength <= 0) {
|
|
|
|
|
|
// We've run out of tree so tell our caller there
|
|
// are no more
|
|
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// We have another tree to go down
|
|
|
|
|
|
Links = &Node->Links;
|
|
|
|
while (RtlLeftChild(Links) != NULL) {
|
|
|
|
Links = RtlLeftChild(Links);
|
|
}
|
|
}
|
|
|
|
|
|
// Set it as our the node we're returning
|
|
|
|
|
|
Node = CONTAINING_RECORD( Links, UNICODE_PREFIX_TABLE_ENTRY, Links);
|
|
}
|
|
|
|
|
|
// Save node as the last next entry
|
|
|
|
|
|
PrefixTable->LastNextEntry = Node;
|
|
|
|
|
|
// And return this entry to our caller
|
|
|
|
|
|
return Node;
|
|
}
|
|
|
|
|
|
CLONG
|
|
ComputeUnicodeNameLength(
|
|
IN PUNICODE_STRING Name
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine counts the number of names appearing in the input string.
|
|
It does this by simply counting the number of backslashes in the string.
|
|
To handle ill-formed names (i.e., names that do not contain a backslash)
|
|
this routine really returns the number of backslashes plus 1.
|
|
|
|
Arguments:
|
|
|
|
Name - Supplies the input name to examine
|
|
|
|
Returns Value:
|
|
|
|
CLONG - the number of names in the input string
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR UnicodeBackSlash = '\\';
|
|
ULONG NameLength;
|
|
ULONG i;
|
|
ULONG Count;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
|
|
// Save the name length, this should make the compiler be able to
|
|
// optimize not having to reload the length each time
|
|
|
|
|
|
NameLength = (ULONG)Name->Length/2;
|
|
|
|
|
|
// Now loop through the input string counting back slashes
|
|
|
|
|
|
for (i = 0, Count = 1; i < (ULONG)NameLength - 1; i += 1) {
|
|
|
|
|
|
// check for a back slash
|
|
|
|
|
|
if (Name->Buffer[i] == UnicodeBackSlash) {
|
|
|
|
Count += 1;
|
|
}
|
|
}
|
|
|
|
|
|
// return the number of back slashes we found
|
|
|
|
|
|
//DbgPrint("ComputeUnicodeNameLength(%Z) = %x\n", Name, Count);
|
|
|
|
return Count;
|
|
}
|
|
|
|
|
|
COMPARISON
|
|
CompareUnicodeStrings (
|
|
IN PUNICODE_STRING Prefix,
|
|
IN PUNICODE_STRING Name,
|
|
IN ULONG CaseInsensitiveIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes a prefix string and a full name string and determines
|
|
if the prefix string is a proper prefix of the name string (case sensitive)
|
|
|
|
Arguments:
|
|
|
|
Prefix - Supplies the input prefix string
|
|
|
|
Name - Supplies the full name input string
|
|
|
|
CaseInsensitiveIndex - Indicates the wchar index at which to do a case
|
|
insensitive search. All characters before the index are searched
|
|
case sensitive and all characters at and after the index are searched
|
|
|
|
Return Value:
|
|
|
|
COMPARISON - returns
|
|
|
|
IsLessThan if Prefix < Name lexicalgraphically,
|
|
IsPrefix if Prefix is a proper prefix of Name
|
|
IsEqual if Prefix is equal to Name, and
|
|
IsGreaterThan if Prefix > Name lexicalgraphically
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR UnicodeBackSlash = '\\';
|
|
ULONG PrefixLength;
|
|
ULONG NameLength;
|
|
ULONG MinLength;
|
|
ULONG i;
|
|
|
|
WCHAR PrefixChar;
|
|
WCHAR NameChar;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
//DbgPrint("CompareUnicodeStrings(\"%Z\", \"%Z\") = ", Prefix, Name);
|
|
|
|
|
|
// Save the length of the prefix and name string, this should allow
|
|
// the compiler to not need to reload the length through a pointer every
|
|
// time we need their values
|
|
|
|
|
|
PrefixLength = (ULONG)Prefix->Length/2;
|
|
NameLength = (ULONG)Name->Length/2;
|
|
|
|
|
|
// Special case the situation where the prefix string is simply "\" and
|
|
// the name starts with an "\"
|
|
|
|
|
|
if ((PrefixLength == 1) && (Prefix->Buffer[0] == UnicodeBackSlash) &&
|
|
(NameLength > 1) && (Name->Buffer[0] == UnicodeBackSlash)) {
|
|
//DbgPrint("IsPrefix\n");
|
|
return IsPrefix;
|
|
}
|
|
|
|
|
|
// Figure out the minimum of the two lengths
|
|
|
|
|
|
MinLength = (PrefixLength < NameLength ? PrefixLength : NameLength);
|
|
|
|
|
|
// Loop through looking at all of the characters in both strings
|
|
// testing for equalilty. First to the CaseSensitive part, then the
|
|
// CaseInsensitive part.
|
|
|
|
|
|
if (CaseInsensitiveIndex > MinLength) {
|
|
|
|
CaseInsensitiveIndex = MinLength;
|
|
}
|
|
|
|
|
|
// CaseSensitive compare
|
|
|
|
|
|
for (i = 0; i < CaseInsensitiveIndex; i += 1) {
|
|
|
|
PrefixChar = Prefix->Buffer[i];
|
|
NameChar = Name->Buffer[i];
|
|
|
|
if (PrefixChar != NameChar) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// If we didn't break out of the above loop, do the
|
|
// CaseInsensitive compare.
|
|
|
|
|
|
if (i == CaseInsensitiveIndex) {
|
|
|
|
WCHAR *s1 = &Prefix->Buffer[i];
|
|
WCHAR *s2 = &Name->Buffer[i];
|
|
|
|
for (; i < MinLength; i += 1) {
|
|
|
|
PrefixChar = *s1++;
|
|
NameChar = *s2++;
|
|
|
|
if (PrefixChar != NameChar) {
|
|
|
|
PrefixChar = NLS_UPCASE(PrefixChar);
|
|
NameChar = NLS_UPCASE(NameChar);
|
|
|
|
if (PrefixChar != NameChar) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// If we broke out of the above loop because of a mismatch, determine
|
|
// the result of the comparison.
|
|
|
|
|
|
if (i < MinLength) {
|
|
|
|
|
|
// We also need to treat "\" as less than all other characters, so
|
|
// if the char is a "\" we'll drop it down to a value of zero.
|
|
|
|
|
|
if (PrefixChar == UnicodeBackSlash) {
|
|
|
|
return IsLessThan;
|
|
}
|
|
|
|
if (NameChar == UnicodeBackSlash) {
|
|
|
|
return IsGreaterThan;
|
|
}
|
|
|
|
|
|
// Now compare the characters
|
|
|
|
|
|
if (PrefixChar < NameChar) {
|
|
|
|
return IsLessThan;
|
|
|
|
} else if (PrefixChar > NameChar) {
|
|
|
|
return IsGreaterThan;
|
|
}
|
|
}
|
|
|
|
|
|
// They match upto the minimum length so now figure out the largest string
|
|
// and see if one is a proper prefix of the other
|
|
|
|
|
|
if (PrefixLength < NameLength) {
|
|
|
|
|
|
// The prefix string is shorter so if it is a proper prefix we
|
|
// return prefix otherwise we return less than (e.g., "\a" < "\ab")
|
|
|
|
|
|
if (Name->Buffer[PrefixLength] == UnicodeBackSlash) {
|
|
|
|
//DbgPrint("IsPrefix\n");
|
|
|
|
return IsPrefix;
|
|
|
|
} else {
|
|
|
|
//DbgPrint("IsLessThan\n");
|
|
|
|
return IsLessThan;
|
|
}
|
|
|
|
} else if (PrefixLength > NameLength) {
|
|
|
|
|
|
// The Prefix string is longer so we say that the prefix is
|
|
// greater than the name (e.g., "\ab" > "\a")
|
|
|
|
|
|
//DbgPrint("IsGreaterThan\n");
|
|
|
|
return IsGreaterThan;
|
|
|
|
} else {
|
|
|
|
|
|
// They lengths are equal so the strings are equal
|
|
|
|
|
|
//DbgPrint("IsEqual\n");
|
|
|
|
return IsEqual;
|
|
}
|
|
}
|
|
|