3 Copyright (c) 1989-2000 Microsoft Corporation
11 This module implements the Cdfs Prefix support routines
19 // The Bug check file id for this module
22 #define BugCheckFileId (CDFS_BUG_CHECK_PREFXSUP)
25 // Local support routines.
30 _In_ PIRP_CONTEXT IrpContext
,
31 _In_ PRTL_SPLAY_LINKS
*RootNode
,
32 _In_ PUNICODE_STRING Name
37 _In_ PIRP_CONTEXT IrpContext
,
38 _Inout_ PRTL_SPLAY_LINKS
*RootNode
,
39 _In_ PNAME_LINK NameLink
43 #pragma alloc_text(PAGE, CdFindNameLink)
44 #pragma alloc_text(PAGE, CdFindPrefix)
45 #pragma alloc_text(PAGE, CdInsertNameLink)
46 #pragma alloc_text(PAGE, CdInsertPrefix)
47 #pragma alloc_text(PAGE, CdRemovePrefix)
53 _In_ PIRP_CONTEXT IrpContext
,
56 _In_ BOOLEAN IgnoreCase
,
57 _In_ BOOLEAN ShortNameMatch
,
58 _Inout_ PFCB ParentFcb
65 This routine inserts the names in the given Lcb into the links for the
70 Fcb - This is the Fcb whose name is being inserted into the tree.
72 Name - This is the name for the component. The IgnoreCase flag tells
73 us which entry this belongs to.
75 IgnoreCase - Indicates if we should insert the case-insensitive name.
77 ShortNameMatch - Indicates if this is the short name.
79 ParentFcb - This is the ParentFcb. The prefix tree is attached to this.
90 PPREFIX_ENTRY PrefixEntry
;
91 PRTL_SPLAY_LINKS
*TreeRoot
;
98 // Check if we need to allocate a prefix entry for the short name.
99 // If we can't allocate one then fail quietly. We don't have to
103 PrefixEntry
= &Fcb
->FileNamePrefix
;
105 if (ShortNameMatch
) {
107 if (Fcb
->ShortNamePrefix
== NULL
) {
109 Fcb
->ShortNamePrefix
= ExAllocatePoolWithTag( CdPagedPool
,
110 sizeof( PREFIX_ENTRY
),
113 if (Fcb
->ShortNamePrefix
== NULL
) { return; }
115 RtlZeroMemory( Fcb
->ShortNamePrefix
, sizeof( PREFIX_ENTRY
));
118 PrefixEntry
= Fcb
->ShortNamePrefix
;
122 // Capture the local variables for the separate cases.
127 PrefixFlags
= PREFIX_FLAG_IGNORE_CASE_IN_TREE
;
128 NameLink
= &PrefixEntry
->IgnoreCaseName
;
129 TreeRoot
= &ParentFcb
->IgnoreCaseRoot
;
133 PrefixFlags
= PREFIX_FLAG_EXACT_CASE_IN_TREE
;
134 NameLink
= &PrefixEntry
->ExactCaseName
;
135 TreeRoot
= &ParentFcb
->ExactCaseRoot
;
139 // If neither name is in the tree then check whether we have a buffer for this
143 if (!FlagOn( PrefixEntry
->PrefixFlags
,
144 PREFIX_FLAG_EXACT_CASE_IN_TREE
| PREFIX_FLAG_IGNORE_CASE_IN_TREE
)) {
147 // Allocate a new buffer if the embedded buffer is too small.
150 NameBuffer
= PrefixEntry
->FileNameBuffer
;
152 if (Name
->FileName
.Length
> BYTE_COUNT_EMBEDDED_NAME
) {
154 NameBuffer
= ExAllocatePoolWithTag( CdPagedPool
,
155 Name
->FileName
.Length
* 2,
159 // Exit if no name buffer.
162 if (NameBuffer
== NULL
) { return; }
166 // Split the buffer and fill in the separate components.
169 PrefixEntry
->ExactCaseName
.FileName
.Buffer
= NameBuffer
;
170 PrefixEntry
->IgnoreCaseName
.FileName
.Buffer
= Add2Ptr( NameBuffer
,
171 Name
->FileName
.Length
,
174 PrefixEntry
->IgnoreCaseName
.FileName
.MaximumLength
=
175 PrefixEntry
->IgnoreCaseName
.FileName
.Length
=
176 PrefixEntry
->ExactCaseName
.FileName
.MaximumLength
=
177 PrefixEntry
->ExactCaseName
.FileName
.Length
= Name
->FileName
.Length
;
181 // Only insert the name if not already present.
184 if (!FlagOn( PrefixEntry
->PrefixFlags
, PrefixFlags
)) {
187 // Initialize the name in the prefix entry.
190 RtlCopyMemory( NameLink
->FileName
.Buffer
,
191 Name
->FileName
.Buffer
,
192 Name
->FileName
.Length
);
194 CdInsertNameLink( IrpContext
,
198 PrefixEntry
->Fcb
= Fcb
;
199 SetFlag( PrefixEntry
->PrefixFlags
, PrefixFlags
);
208 _In_ PIRP_CONTEXT IrpContext
,
216 This routine is called to remove all of the previx entries of a
217 given Fcb from its parent Fcb.
221 Fcb - Fcb whose entries are to be removed.
232 UNREFERENCED_PARAMETER( IrpContext
);
235 // Start with the short name prefix entry.
238 if (Fcb
->ShortNamePrefix
!= NULL
) {
240 if (FlagOn( Fcb
->ShortNamePrefix
->PrefixFlags
, PREFIX_FLAG_IGNORE_CASE_IN_TREE
)) {
242 Fcb
->ParentFcb
->IgnoreCaseRoot
= RtlDelete( &Fcb
->ShortNamePrefix
->IgnoreCaseName
.Links
);
245 if (FlagOn( Fcb
->ShortNamePrefix
->PrefixFlags
, PREFIX_FLAG_EXACT_CASE_IN_TREE
)) {
247 Fcb
->ParentFcb
->ExactCaseRoot
= RtlDelete( &Fcb
->ShortNamePrefix
->ExactCaseName
.Links
);
250 ClearFlag( Fcb
->ShortNamePrefix
->PrefixFlags
,
251 PREFIX_FLAG_IGNORE_CASE_IN_TREE
| PREFIX_FLAG_EXACT_CASE_IN_TREE
);
255 // Now do the long name prefix entries.
258 if (FlagOn( Fcb
->FileNamePrefix
.PrefixFlags
, PREFIX_FLAG_IGNORE_CASE_IN_TREE
)) {
260 Fcb
->ParentFcb
->IgnoreCaseRoot
= RtlDelete( &Fcb
->FileNamePrefix
.IgnoreCaseName
.Links
);
263 if (FlagOn( Fcb
->FileNamePrefix
.PrefixFlags
, PREFIX_FLAG_EXACT_CASE_IN_TREE
)) {
265 Fcb
->ParentFcb
->ExactCaseRoot
= RtlDelete( &Fcb
->FileNamePrefix
.ExactCaseName
.Links
);
268 ClearFlag( Fcb
->FileNamePrefix
.PrefixFlags
,
269 PREFIX_FLAG_IGNORE_CASE_IN_TREE
| PREFIX_FLAG_EXACT_CASE_IN_TREE
);
272 // Deallocate any buffer we may have allocated.
275 if ((Fcb
->FileNamePrefix
.ExactCaseName
.FileName
.Buffer
!= (PWCHAR
) &Fcb
->FileNamePrefix
.FileNameBuffer
) &&
276 (Fcb
->FileNamePrefix
.ExactCaseName
.FileName
.Buffer
!= NULL
)) {
278 CdFreePool( &Fcb
->FileNamePrefix
.ExactCaseName
.FileName
.Buffer
);
279 Fcb
->FileNamePrefix
.ExactCaseName
.FileName
.Buffer
= NULL
;
287 _Requires_lock_held_(_Global_critical_region_
)
290 _In_ PIRP_CONTEXT IrpContext
,
291 _Inout_ PFCB
*CurrentFcb
,
292 _Inout_ PUNICODE_STRING RemainingName
,
293 _In_ BOOLEAN IgnoreCase
300 This routine begins from the given CurrentFcb and walks through all of
301 components of the name looking for the longest match in the prefix
302 splay trees. The search is relative to the starting Fcb so the
303 full name may not begin with a '\'. On return this routine will
304 update Current Fcb with the lowest point it has travelled in the
305 tree. It will also hold only that resource on return and it must
310 CurrentFcb - Address to store the lowest Fcb we find on this search.
311 On return we will have acquired this Fcb. On entry this is the
314 RemainingName - Supplies a buffer to store the exact case of the name being
315 searched for. Initially will contain the upcase name based on the
318 IgnoreCase - Indicates if we are doing a case-insensitive compare.
327 UNICODE_STRING LocalRemainingName
;
329 UNICODE_STRING FinalName
;
332 PPREFIX_ENTRY PrefixEntry
;
337 // Make a local copy of the input strings.
340 LocalRemainingName
= *RemainingName
;
343 // Loop until we find the longest matching prefix.
349 // If there are no characters left or we are not at an IndexFcb then
350 // return immediately.
353 if ((LocalRemainingName
.Length
== 0) ||
354 (SafeNodeType( *CurrentFcb
) != CDFS_NTC_FCB_INDEX
)) {
360 // Split off the next component from the name.
363 CdDissectName( IrpContext
,
368 // Check if this name is in the splay tree for this Scb.
373 NameLink
= CdFindNameLink( IrpContext
,
374 &(*CurrentFcb
)->IgnoreCaseRoot
,
378 // Get the prefix entry from this NameLink. Don't access any
379 // fields within it until we verify we have a name link.
382 PrefixEntry
= (PPREFIX_ENTRY
) CONTAINING_RECORD( NameLink
,
388 NameLink
= CdFindNameLink( IrpContext
,
389 &(*CurrentFcb
)->ExactCaseRoot
,
392 PrefixEntry
= (PPREFIX_ENTRY
) CONTAINING_RECORD( NameLink
,
398 // If we didn't find a match then exit.
401 if (NameLink
== NULL
) { return; }
404 // If this is a case-insensitive match then copy the exact case of the name into
410 RtlCopyMemory( FinalName
.Buffer
,
411 PrefixEntry
->ExactCaseName
.FileName
.Buffer
,
412 PrefixEntry
->ExactCaseName
.FileName
.Length
);
416 // Update the caller's remaining name string to reflect the fact that we found
420 *RemainingName
= LocalRemainingName
;
423 // Move down to the next component in the tree. Acquire without waiting.
424 // If this fails then lock the Fcb to reference this Fcb and then drop
425 // the parent and acquire the child.
428 if (!CdAcquireFcbExclusive( IrpContext
, PrefixEntry
->Fcb
, TRUE
)) {
431 // If we can't wait then raise CANT_WAIT.
434 if (!FlagOn( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
)) {
436 CdRaiseStatus( IrpContext
, STATUS_CANT_WAIT
);
439 CdLockVcb( IrpContext
, IrpContext
->Vcb
);
440 PrefixEntry
->Fcb
->FcbReference
+= 1;
441 CdUnlockVcb( IrpContext
, IrpContext
->Vcb
);
443 CdReleaseFcb( IrpContext
, *CurrentFcb
);
444 CdAcquireFcbExclusive( IrpContext
, PrefixEntry
->Fcb
, FALSE
);
446 CdLockVcb( IrpContext
, IrpContext
->Vcb
);
447 PrefixEntry
->Fcb
->FcbReference
-= 1;
448 CdUnlockVcb( IrpContext
, IrpContext
->Vcb
);
452 CdReleaseFcb( IrpContext
, *CurrentFcb
);
455 *CurrentFcb
= PrefixEntry
->Fcb
;
461 // Local support routine
466 _In_ PIRP_CONTEXT IrpContext
,
467 _In_ PRTL_SPLAY_LINKS
*RootNode
,
468 _In_ PUNICODE_STRING Name
475 This routine searches through a splay link tree looking for a match for the
476 input name. If we find the corresponding name we will rebalance the
481 RootNode - Supplies the parent to search.
483 Name - This is the name to search for. Note if we are doing a case
484 insensitive search the name would have been upcased already.
488 PNAME_LINK - The name link found or NULL if there is no match.
493 FSRTL_COMPARISON_RESULT Comparison
;
495 PRTL_SPLAY_LINKS Links
;
501 while (Links
!= NULL
) {
503 Node
= CONTAINING_RECORD( Links
, NAME_LINK
, Links
);
506 // Compare the prefix in the tree with the full name
509 Comparison
= CdFullCompareNames( IrpContext
, &Node
->FileName
, Name
);
512 // See if they don't match
515 if (Comparison
== GreaterThan
) {
518 // The prefix is greater than the full name
519 // so we go down the left child
522 Links
= RtlLeftChild( Links
);
525 // And continue searching down this tree
528 } else if (Comparison
== LessThan
) {
531 // The prefix is less than the full name
532 // so we go down the right child
535 Links
= RtlRightChild( Links
);
538 // And continue searching down this tree
546 // Splay the tree and save the new root.
549 *RootNode
= RtlSplay( Links
);
556 // We didn't find the Link.
564 // Local support routine
569 _In_ PIRP_CONTEXT IrpContext
,
570 _Inout_ PRTL_SPLAY_LINKS
*RootNode
,
571 _In_ PNAME_LINK NameLink
578 This routine will insert a name in the splay tree pointed to
581 The name could already exist in this tree for a case-insensitive tree.
582 In that case we simply return FALSE and do nothing.
586 RootNode - Supplies a pointer to the table.
588 NameLink - Contains the new link to enter.
592 BOOLEAN - TRUE if the name is inserted, FALSE otherwise.
597 FSRTL_COMPARISON_RESULT Comparison
;
602 RtlInitializeSplayLinks( &NameLink
->Links
);
605 // If we are the first entry in the tree, just become the root.
608 if (*RootNode
== NULL
) {
610 *RootNode
= &NameLink
->Links
;
615 Node
= CONTAINING_RECORD( *RootNode
, NAME_LINK
, Links
);
620 // Compare the prefix in the tree with the prefix we want
624 Comparison
= CdFullCompareNames( IrpContext
, &Node
->FileName
, &NameLink
->FileName
);
627 // If we found the entry, return immediately.
630 if (Comparison
== EqualTo
) { return FALSE
; }
633 // If the tree prefix is greater than the new prefix then
634 // we go down the left subtree
637 if (Comparison
== GreaterThan
) {
640 // We want to go down the left subtree, first check to see
641 // if we have a left subtree
644 if (RtlLeftChild( &Node
->Links
) == NULL
) {
647 // there isn't a left child so we insert ourselves as the
651 RtlInsertAsLeftChild( &Node
->Links
, &NameLink
->Links
);
654 // and exit the while loop
662 // there is a left child so simply go down that path, and
663 // go back to the top of the loop
666 Node
= CONTAINING_RECORD( RtlLeftChild( &Node
->Links
),
674 // The tree prefix is either less than or a proper prefix
675 // of the new string. We treat both cases as less than when
676 // we do insert. So we want to go down the right subtree,
677 // first check to see if we have a right subtree
680 if (RtlRightChild( &Node
->Links
) == NULL
) {
683 // These isn't a right child so we insert ourselves as the
687 RtlInsertAsRightChild( &Node
->Links
, &NameLink
->Links
);
690 // and exit the while loop
698 // there is a right child so simply go down that path, and
699 // go back to the top of the loop
702 Node
= CONTAINING_RECORD( RtlRightChild( &Node
->Links
),