513 lines
11 KiB
C
513 lines
11 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
PrefxSup.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the Fat Name lookup Suport routines
|
||
|
||
// @@BEGIN_DDKSPLIT
|
||
|
||
Author:
|
||
|
||
David Goebel [DavidGoe] 31-Jan-1994
|
||
|
||
Revision History:
|
||
|
||
// @@END_DDKSPLIT
|
||
|
||
--*/
|
||
|
||
#include "FatProcs.h"
|
||
|
||
//
|
||
// The Bug check file id for this module
|
||
//
|
||
|
||
#define BugCheckFileId (FAT_BUG_CHECK_SPLAYSUP)
|
||
|
||
//
|
||
// The debug trace level for this module
|
||
//
|
||
|
||
#define Dbg (DEBUG_TRACE_SPLAYSUP)
|
||
|
||
//
|
||
// Local procedures and types used only in this package
|
||
//
|
||
|
||
typedef enum _COMPARISON {
|
||
IsLessThan,
|
||
IsGreaterThan,
|
||
IsEqual
|
||
} COMPARISON;
|
||
|
||
COMPARISON
|
||
FatCompareNames (
|
||
IN PSTRING NameA,
|
||
IN PSTRING NameB
|
||
);
|
||
|
||
//
|
||
// Do a macro here to check for a common case.
|
||
//
|
||
|
||
#define CompareNames(NAMEA,NAMEB) ( \
|
||
*(PUCHAR)(NAMEA)->Buffer != *(PUCHAR)(NAMEB)->Buffer ? \
|
||
*(PUCHAR)(NAMEA)->Buffer < *(PUCHAR)(NAMEB)->Buffer ? \
|
||
IsLessThan : IsGreaterThan : \
|
||
FatCompareNames((PSTRING)(NAMEA), (PSTRING)(NAMEB)) \
|
||
)
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, FatInsertName)
|
||
#pragma alloc_text(PAGE, FatRemoveNames)
|
||
#pragma alloc_text(PAGE, FatFindFcb)
|
||
#pragma alloc_text(PAGE, FatCompareNames)
|
||
#endif
|
||
|
||
|
||
VOID
|
||
FatInsertName (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PRTL_SPLAY_LINKS *RootNode,
|
||
IN PFILE_NAME_NODE Name
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will insert a name in the splay tree pointed to
|
||
by RootNode.
|
||
|
||
The name must not already exist in the splay tree.
|
||
|
||
Arguments:
|
||
|
||
RootNode - Supplies a pointer to the table.
|
||
|
||
Name - Contains the New name to enter.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
COMPARISON Comparison;
|
||
PFILE_NAME_NODE Node;
|
||
|
||
RtlInitializeSplayLinks(&Name->Links);
|
||
|
||
//
|
||
// If we are the first entry in the tree, just become the root.
|
||
//
|
||
|
||
if (*RootNode == NULL) {
|
||
|
||
*RootNode = &Name->Links;
|
||
|
||
return;
|
||
}
|
||
|
||
Restart:
|
||
|
||
Node = CONTAINING_RECORD( *RootNode, FILE_NAME_NODE, Links );
|
||
|
||
while (TRUE) {
|
||
|
||
//
|
||
// Compare the prefix in the tree with the prefix we want
|
||
// to insert. Note that Oem here doesn't mean anything.
|
||
//
|
||
|
||
Comparison = CompareNames(&Node->Name.Oem, &Name->Name.Oem);
|
||
|
||
//
|
||
// We should never find the name in the table already.
|
||
//
|
||
|
||
if (Comparison == IsEqual) {
|
||
|
||
//
|
||
// Almost. If the removable media was taken to another machine and
|
||
// back, and we have something like:
|
||
//
|
||
// Old: foobar~1 / foobarbaz
|
||
// New: foobar~1 / foobarbazbaz
|
||
//
|
||
// but a handle was kept open to foobarbaz so we couldn't purge
|
||
// away the Fcb in the verify path ... opening foobarbazbaz will
|
||
// try to insert a duplicate shortname. Bang!
|
||
//
|
||
// Invalidate it and the horse it came in on. This new one wins.
|
||
// The old one is gone. Only if the old one is in normal state
|
||
// do we really have a problem.
|
||
//
|
||
|
||
if (Node->Fcb->FcbState == FcbGood) {
|
||
|
||
FatBugCheck( (ULONG_PTR)*RootNode, (ULONG_PTR)Name, (ULONG_PTR)Node );
|
||
}
|
||
|
||
//
|
||
// Note, once we zap the prefix links we need to restart our walk
|
||
// of the tree.
|
||
//
|
||
|
||
FatMarkFcbCondition( IrpContext, Node->Fcb, FcbBad, TRUE );
|
||
FatRemoveNames( IrpContext, Node->Fcb );
|
||
|
||
goto Restart;
|
||
}
|
||
|
||
//
|
||
// 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
|
||
//
|
||
|
||
RtlInsertAsLeftChild(&Node->Links, &Name->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),
|
||
FILE_NAME_NODE,
|
||
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
|
||
//
|
||
|
||
RtlInsertAsRightChild(&Node->Links, &Name->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),
|
||
FILE_NAME_NODE,
|
||
Links );
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
FatRemoveNames (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will remove the short name and any long names associated
|
||
with the files from their repsective splay tree.
|
||
|
||
Arguments:
|
||
|
||
Name - Supplies the Fcb to process.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDCB Parent;
|
||
PRTL_SPLAY_LINKS NewRoot;
|
||
|
||
Parent = Fcb->ParentDcb;
|
||
|
||
//
|
||
// We used to assert this condition, but it really isn't good. If
|
||
// someone rapidly renames a directory multiple times and we can't
|
||
// flush the lower fcbs fast enough (that didn't go away synch.)
|
||
// well, well hit some of them again.
|
||
//
|
||
// ASSERT( FlagOn( Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE ));
|
||
//
|
||
|
||
if (FlagOn( Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE )) {
|
||
|
||
//
|
||
// Delete the node short name.
|
||
//
|
||
|
||
NewRoot = RtlDelete(&Fcb->ShortName.Links);
|
||
|
||
Parent->Specific.Dcb.RootOemNode = NewRoot;
|
||
|
||
//
|
||
// Now check for the presence of long name and delete it.
|
||
//
|
||
|
||
if (FlagOn( Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME )) {
|
||
|
||
NewRoot = RtlDelete(&Fcb->LongName.Oem.Links);
|
||
|
||
Parent->Specific.Dcb.RootOemNode = NewRoot;
|
||
|
||
RtlFreeOemString( &Fcb->LongName.Oem.Name.Oem );
|
||
|
||
ClearFlag( Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME );
|
||
}
|
||
|
||
if (FlagOn( Fcb->FcbState, FCB_STATE_HAS_UNICODE_LONG_NAME )) {
|
||
|
||
NewRoot = RtlDelete(&Fcb->LongName.Unicode.Links);
|
||
|
||
Parent->Specific.Dcb.RootUnicodeNode = NewRoot;
|
||
|
||
RtlFreeUnicodeString( &Fcb->LongName.Unicode.Name.Unicode );
|
||
|
||
ClearFlag( Fcb->FcbState, FCB_STATE_HAS_UNICODE_LONG_NAME );
|
||
}
|
||
|
||
ClearFlag( Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
PFCB
|
||
FatFindFcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN OUT PRTL_SPLAY_LINKS *RootNode,
|
||
IN PSTRING Name,
|
||
OUT PBOOLEAN FileNameDos OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine searches either the Oem or Unicode splay tree looking
|
||
for an Fcb with the specified name. In the case the Fcb is found,
|
||
rebalance the tree.
|
||
|
||
Arguments:
|
||
|
||
RootNode - Supplies the parent to search.
|
||
|
||
Name - If present, search the Oem tree.
|
||
|
||
UnicodeName - If present, search the Unicode tree.
|
||
|
||
Return Value:
|
||
|
||
PFCB - The Fcb, or NULL if none was found.
|
||
|
||
--*/
|
||
|
||
{
|
||
COMPARISON Comparison;
|
||
PFILE_NAME_NODE Node;
|
||
PRTL_SPLAY_LINKS Links;
|
||
|
||
Links = *RootNode;
|
||
|
||
while (Links != NULL) {
|
||
|
||
Node = CONTAINING_RECORD(Links, FILE_NAME_NODE, Links);
|
||
|
||
//
|
||
// Compare the prefix in the tree with the full name
|
||
//
|
||
|
||
Comparison = CompareNames(&Node->Name.Oem, Name);
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
// Splay the tree and save the new root.
|
||
//
|
||
|
||
*RootNode = RtlSplay(Links);
|
||
|
||
//
|
||
// Tell the caller what kind of name we hit
|
||
//
|
||
|
||
if (FileNameDos) {
|
||
|
||
*FileNameDos = Node->FileNameDos;
|
||
}
|
||
|
||
return Node->Fcb;
|
||
}
|
||
}
|
||
|
||
//
|
||
// We didn't find the Fcb.
|
||
//
|
||
|
||
return NULL;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
COMPARISON
|
||
FatCompareNames (
|
||
IN PSTRING NameA,
|
||
IN PSTRING NameB
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function compares two names as fast as possible. Note that since
|
||
this comparison is case sensitive, I neither know nor case if the names
|
||
are UNICODE or OEM. All that is important is that the result is
|
||
deterministic.
|
||
|
||
Arguments:
|
||
|
||
NameA & NameB - The names to compare.
|
||
|
||
Return Value:
|
||
|
||
COMPARISON - returns
|
||
|
||
IsLessThan if NameA < NameB lexicalgraphically,
|
||
IsGreaterThan if NameA > NameB lexicalgraphically,
|
||
IsEqual if NameA is equal to NameB
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
ULONG MinLength;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Figure out the minimum of the two lengths
|
||
//
|
||
|
||
MinLength = NameA->Length < NameB->Length ? NameA->Length :
|
||
NameB->Length;
|
||
|
||
//
|
||
// Loop through looking at all of the characters in both strings
|
||
// testing for equalilty, less than, and greater than
|
||
//
|
||
|
||
i = (ULONG)RtlCompareMemory( NameA->Buffer, NameB->Buffer, MinLength );
|
||
|
||
|
||
if (i < MinLength) {
|
||
|
||
return NameA->Buffer[i] < NameB->Buffer[i] ? IsLessThan :
|
||
IsGreaterThan;
|
||
}
|
||
|
||
if (NameA->Length < NameB->Length) {
|
||
|
||
return IsLessThan;
|
||
}
|
||
|
||
if (NameA->Length > NameB->Length) {
|
||
|
||
return IsGreaterThan;
|
||
}
|
||
|
||
return IsEqual;
|
||
}
|