xref: /reactos/ntoskrnl/fsrtl/tunnel.c (revision 02e84521)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/fsrtl/tunnel.c
5  * PURPOSE:         Provides the Tunnel Cache implementation for file system drivers.
6  * PROGRAMMERS:     Johannes Anderwald (johannes.anderwald@reactos.org)
7  *                  Pierre Schweitzer (pierre@reactos.org)
8  */
9 
10 /* INCLUDES ******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 typedef struct {
17     RTL_SPLAY_LINKS SplayInfo;
18     LIST_ENTRY TimerQueueEntry;
19     LARGE_INTEGER Time;
20     ULONGLONG DirectoryKey;
21     ULONG Flags;
22     UNICODE_STRING LongName;
23     UNICODE_STRING ShortName;
24     PVOID Data;
25     ULONG DataLength;
26 } TUNNEL_NODE_ENTRY, *PTUNNEL_NODE_ENTRY;
27 
28 ULONG TunnelMaxEntries = 256;
29 ULONG TunnelMaxAge = 15;
30 PAGED_LOOKASIDE_LIST TunnelLookasideList;
31 
32 #define DEFAULT_EXTRA_SIZE (72)
33 #define DEFAULT_ENTRY_SIZE (sizeof(TUNNEL_NODE_ENTRY) + DEFAULT_EXTRA_SIZE)
34 
35 #define TUNNEL_FLAG_POOL 0x2
36 #define TUNNEL_FLAG_KEY_SHORT_NAME 0x1
37 
38 VOID
39 FsRtlFreeTunnelNode(
40     IN PTUNNEL_NODE_ENTRY CurEntry,
41     IN PLIST_ENTRY PoolList OPTIONAL)
42 {
43     if (PoolList)
44     {
45         /* divert the linked list entry, it's not required anymore, but we need it */
46         InsertHeadList(PoolList, &CurEntry->TimerQueueEntry);
47         return;
48     }
49 
50     if (CurEntry->Flags & TUNNEL_FLAG_POOL)
51         ExFreePool(CurEntry);
52     else
53         ExFreeToPagedLookasideList(&TunnelLookasideList, CurEntry);
54 }
55 
56 VOID
57 FsRtlRemoveNodeFromTunnel(
58     IN PTUNNEL Cache,
59     IN PTUNNEL_NODE_ENTRY CurEntry,
60     IN PLIST_ENTRY PoolList,
61     OUT PBOOLEAN Rebalance)
62 {
63     /* delete entry and rebalance if required */
64     if (Rebalance && *Rebalance)
65     {
66         Cache->Cache = RtlDelete(&CurEntry->SplayInfo);
67         /* reset */
68         *Rebalance = FALSE;
69     }
70     else
71     {
72         RtlDeleteNoSplay(&CurEntry->SplayInfo, &Cache->Cache);
73     }
74 
75     /* remove entry */
76     RemoveEntryList(&CurEntry->TimerQueueEntry);
77 
78     /* free node entry */
79     FsRtlFreeTunnelNode(CurEntry, PoolList);
80 
81     /* decrement node count */
82     Cache->NumEntries--;
83 }
84 
85 VOID
86 FsRtlPruneTunnelCache(
87     IN PTUNNEL Cache,
88     IN PLIST_ENTRY PoolList)
89 {
90     PLIST_ENTRY Entry, NextEntry;
91     PTUNNEL_NODE_ENTRY CurEntry;
92     LARGE_INTEGER CurTime, OldTime;
93     BOOLEAN Rebalance = TRUE;
94     PAGED_CODE();
95 
96     /* query time */
97     KeQuerySystemTime(&CurTime);
98 
99     /* subtract maximum node age */
100     OldTime.QuadPart = CurTime.QuadPart - TunnelMaxAge;
101 
102     /* free all entries */
103     Entry = Cache->TimerQueue.Flink;
104 
105     while(Entry != &Cache->TimerQueue)
106     {
107         /* get node entry */
108         CurEntry = CONTAINING_RECORD(Entry, TUNNEL_NODE_ENTRY, TimerQueueEntry);
109 
110         /* get next entry */
111          NextEntry = Entry->Flink;
112 
113         /* prune if expired OR if in advance in time */
114         if (CurEntry->Time.QuadPart < OldTime.QuadPart ||
115             CurEntry->Time.QuadPart > CurTime.QuadPart)
116         {
117             FsRtlRemoveNodeFromTunnel(Cache, CurEntry, PoolList, &Rebalance);
118         }
119 
120         /* move to next entry */
121         Entry = NextEntry;
122     }
123 
124     /* If we have too many entries */
125     while (Cache->NumEntries > TunnelMaxEntries)
126     {
127         CurEntry = CONTAINING_RECORD(Entry, TUNNEL_NODE_ENTRY, TimerQueueEntry);
128         FsRtlRemoveNodeFromTunnel(Cache, CurEntry, PoolList, &Rebalance);
129     }
130 }
131 
132 INIT_FUNCTION
133 VOID
134 FsRtlGetTunnelParameterValue(
135     IN PUNICODE_STRING ParameterName,
136     OUT PULONG Value)
137 {
138     UNICODE_STRING Root = RTL_CONSTANT_STRING(L"Registry\\Machine\\System\\CurrentControlSet\\Control\\FileSystem");
139     OBJECT_ATTRIBUTES ObjectAttributes;
140     HANDLE hKey;
141     NTSTATUS Status;
142     ULONG Length;
143     PKEY_VALUE_FULL_INFORMATION Info;
144 
145     /* initialize object attributes */
146     InitializeObjectAttributes(&ObjectAttributes, &Root, OBJ_CASE_INSENSITIVE, NULL, NULL);
147 
148     /* open registry key */
149     Status = ZwOpenKey(&hKey, KEY_READ, &ObjectAttributes);
150 
151     if (!NT_SUCCESS(Status))
152     {
153         /* failed to open key */
154         return;
155     }
156 
157     /* query value size */
158     Status = ZwQueryValueKey(hKey, ParameterName, KeyValueFullInformation, NULL, 0, &Length);
159 
160     if (Status != STATUS_BUFFER_TOO_SMALL)
161     {
162         /* failed to query size */
163         ZwClose(hKey);
164         return;
165     }
166 
167     /* allocate buffer */
168     Info = ExAllocatePool(PagedPool, Length);
169 
170     if (!Info)
171     {
172          /* out of memory */
173         ZwClose(hKey);
174         return;
175     }
176 
177     /* query value */
178     Status = ZwQueryValueKey(hKey, ParameterName, KeyValueFullInformation, NULL, 0, &Length);
179 
180     if (NT_SUCCESS(Status))
181     {
182         if (Info->DataLength)
183         {
184             /* store result */
185             *Value = (ULONG)((ULONG_PTR)Info + Info->DataOffset);
186         }
187     }
188 
189     /* free buffer */
190     ExFreePool(Info);
191 
192     /* close key */
193     ZwClose(hKey);
194 }
195 
196 INIT_FUNCTION
197 VOID
198 NTAPI
199 FsRtlInitializeTunnels(VOID)
200 {
201     ULONG TunnelEntries;
202     UNICODE_STRING MaximumTunnelEntryAgeInSeconds = RTL_CONSTANT_STRING(L"MaximumTunnelEntryAgeInSeconds");
203     UNICODE_STRING MaximumTunnelEntries = RTL_CONSTANT_STRING( L"MaximumTunnelEntries");
204 
205     /* check for nt */
206     if (MmIsThisAnNtAsSystem())
207     {
208         /* default */
209         TunnelMaxEntries = 1024;
210     }
211 
212     /* check for custom override of max entries*/
213     FsRtlGetTunnelParameterValue(&MaximumTunnelEntries, &TunnelMaxEntries);
214 
215     /* check for custom override of age*/
216     FsRtlGetTunnelParameterValue(&MaximumTunnelEntryAgeInSeconds, &TunnelMaxAge);
217 
218     if (!TunnelMaxAge)
219     {
220         /* no age means no entries */
221         TunnelMaxEntries = 0;
222     }
223 
224     /* get max entries */
225     TunnelEntries = TunnelMaxEntries;
226 
227     /* convert to ticks */
228     TunnelMaxAge *= 10000000;
229 
230     if(TunnelMaxEntries <= 65535)
231     {
232         /* use max 256 entries */
233         TunnelEntries = TunnelMaxEntries / 16;
234     }
235 
236     if(!TunnelEntries && TunnelMaxEntries )
237     {
238         /* max tunnel entries was too small */
239         TunnelEntries = TunnelMaxEntries + 1;
240     }
241 
242     if (TunnelEntries > 0xFFFF)
243     {
244         /* max entries is 256 */
245         TunnelEntries = 256;
246     }
247 
248     /* initialize look aside list */
249     ExInitializePagedLookasideList(&TunnelLookasideList, NULL, NULL, 0, DEFAULT_ENTRY_SIZE, 'TunL', TunnelEntries);
250 }
251 
252 LONG
253 FsRtlCompareNodeAndKey(
254     IN PTUNNEL_NODE_ENTRY CurEntry,
255     IN ULONGLONG DirectoryKey,
256     IN PUNICODE_STRING KeyString)
257 {
258     PUNICODE_STRING String;
259     LONG Ret;
260 
261     if (DirectoryKey > CurEntry->DirectoryKey)
262     {
263         Ret = 1;
264     }
265     else if (DirectoryKey < CurEntry->DirectoryKey)
266     {
267         Ret = -1;
268     }
269     else
270     {
271         if (CurEntry->Flags & TUNNEL_FLAG_KEY_SHORT_NAME)
272         {
273             /* use short name as key */
274             String = &CurEntry->ShortName;
275         }
276         else
277         {
278             /* use long name as key */
279             String = &CurEntry->LongName;
280         }
281 
282         Ret = RtlCompareUnicodeString(KeyString, String, TRUE);
283     }
284 
285     return Ret;
286 }
287 
288 VOID
289 FsRtlEmptyFreePoolList(
290     IN PLIST_ENTRY PoolList)
291 {
292     PLIST_ENTRY CurEntry;
293     PTUNNEL_NODE_ENTRY CurNode;
294 
295     /* loop over all the entry */
296     while (!IsListEmpty(PoolList))
297     {
298         /* and free them, one by one */
299         CurEntry = RemoveHeadList(PoolList);
300         CurNode = CONTAINING_RECORD(CurEntry, TUNNEL_NODE_ENTRY, TimerQueueEntry);
301         FsRtlFreeTunnelNode(CurNode, 0);
302     }
303 }
304 
305 /* PUBLIC FUNCTIONS **********************************************************/
306 
307 /*++
308  * @name FsRtlAddToTunnelCache
309  * @implemented
310  *
311  * FILLME
312  *
313  * @param Cache
314  *        FILLME
315  *
316  * @param DirectoryKey
317  *        FILLME
318  *
319  * @param ShortName
320  *        FILLME
321  *
322  * @param LongName
323  *        FILLME
324  *
325  * @param KeyByShortName
326  *        FILLME
327  *
328  * @param DataLength
329  *        FILLME
330  *
331  * @param Data
332  *        FILLME
333  *
334  * @return None
335  *
336  * @remarks None
337  *
338  *--*/
339 VOID
340 NTAPI
341 FsRtlAddToTunnelCache(IN PTUNNEL Cache,
342                       IN ULONGLONG DirectoryKey,
343                       IN PUNICODE_STRING ShortName,
344                       IN PUNICODE_STRING LongName,
345                       IN BOOLEAN KeyByShortName,
346                       IN ULONG DataLength,
347                       IN PVOID Data)
348 {
349     PTUNNEL_NODE_ENTRY NodeEntry = NULL;
350     PRTL_SPLAY_LINKS CurEntry, LastEntry;
351     ULONG Length;
352     LONG Result = 0;
353     BOOLEAN AllocatedFromPool = FALSE;
354     PUNICODE_STRING KeyString;
355     LIST_ENTRY PoolList;
356 
357     PAGED_CODE();
358 
359     /* check if tunnel cache is enabled */
360     if (!TunnelMaxEntries)
361     {
362         /* entries are disabled */
363         return;
364     }
365 
366     /* initialize free pool list */
367     InitializeListHead(&PoolList);
368 
369     /* calculate node length */
370     Length = sizeof(TUNNEL_NODE_ENTRY);
371 
372     /* add data size */
373     Length += DataLength;
374 
375     if (ShortName)
376     {
377         /* add short name length */
378         Length += ShortName->Length;
379     }
380 
381     if (LongName)
382     {
383         /* add short name length */
384         Length += LongName->Length;
385     }
386 
387     if (Length <= DEFAULT_ENTRY_SIZE)
388     {
389         /* get standard entry */
390         NodeEntry = ExAllocateFromPagedLookasideList(&TunnelLookasideList);
391     }
392 
393     if (NodeEntry == NULL)
394     {
395         /* bigger than default entry or allocation failed */
396         NodeEntry = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, Length);
397         /* check for success */
398         if (NodeEntry == NULL)
399         {
400              /* out of memory */
401              return;
402         }
403 
404         AllocatedFromPool = TRUE;
405     }
406 
407     /* acquire lock */
408     ExAcquireFastMutex(&Cache->Mutex);
409 
410     /* now search cache for existing entries */
411     CurEntry = Cache->Cache;
412 
413     /* check which key should be used for search */
414     KeyString = (KeyByShortName ? ShortName : LongName);
415 
416     /* initialize last entry */
417     LastEntry = NULL;
418 
419     while(CurEntry)
420     {
421         /* compare current node */
422         Result = FsRtlCompareNodeAndKey((PTUNNEL_NODE_ENTRY)CurEntry, DirectoryKey, KeyString);
423 
424         /* backup last entry */
425         LastEntry = CurEntry;
426 
427         if (Result > 0)
428         {
429             /* current directory key is bigger */
430             CurEntry = CurEntry->LeftChild;
431         }
432         else
433         {
434             if (Result == 0)
435             {
436                 /* found equal entry */
437                 break;
438             }
439 
440             /* current directory key is smaller */
441             CurEntry = CurEntry->RightChild;
442         }
443     }
444 
445     /* initialize node entry */
446     RtlInitializeSplayLinks(&NodeEntry->SplayInfo);
447 
448     if (CurEntry != NULL)
449     {
450          /* found existing item */
451          if (CurEntry->LeftChild)
452          {
453              /* update parent */
454              RtlInsertAsLeftChild(NodeEntry, CurEntry->LeftChild);
455          }
456 
457          if (CurEntry->RightChild)
458          {
459              /* update parent */
460              RtlInsertAsRightChild(NodeEntry, CurEntry->RightChild);
461          }
462 
463          if (CurEntry->Parent == CurEntry)
464          {
465               /* cur entry was root */
466               Cache->Cache = (struct _RTL_SPLAY_LINKS*)NodeEntry;
467          }
468          else
469          {
470               /* update parent node */
471               if (RtlIsLeftChild(CurEntry))
472               {
473                   RtlInsertAsLeftChild(RtlParent(CurEntry), NodeEntry);
474               }
475               else
476               {
477                   RtlInsertAsRightChild(RtlParent(CurEntry), NodeEntry);
478               }
479          }
480 
481          /* remove entry */
482          RemoveEntryList(&((PTUNNEL_NODE_ENTRY)CurEntry)->TimerQueueEntry);
483 
484          /* free node entry */
485          FsRtlFreeTunnelNode((PTUNNEL_NODE_ENTRY)CurEntry, &PoolList);
486 
487          /* decrement node count */
488          Cache->NumEntries--;
489     }
490     else
491     {
492         if (LastEntry == NULL)
493         {
494             /* first entry in tunnel cache */
495             Cache->Cache = (struct _RTL_SPLAY_LINKS*)NodeEntry;
496         }
497         else
498         {
499             if (Result > 0)
500             {
501                 /* new left node */
502                 RtlInsertAsLeftChild(LastEntry, NodeEntry);
503             }
504             else
505             {
506                 /* new right node */
507                 RtlInsertAsRightChild(LastEntry, NodeEntry);
508             }
509         }
510     }
511 
512     /* initialize entry */
513     KeQuerySystemTime(&NodeEntry->Time);
514 
515     NodeEntry->DirectoryKey = DirectoryKey;
516     NodeEntry->Flags = (AllocatedFromPool ? TUNNEL_FLAG_POOL : 0x0);
517     NodeEntry->Flags |= (KeyByShortName ? TUNNEL_FLAG_KEY_SHORT_NAME : 0x0);
518 
519     if (ShortName)
520     {
521         /* copy short name */
522         NodeEntry->ShortName.Length = ShortName->Length;
523         NodeEntry->ShortName.MaximumLength = ShortName->Length;
524         NodeEntry->ShortName.Buffer = (LPWSTR)((ULONG_PTR)NodeEntry + sizeof(TUNNEL_NODE_ENTRY));
525 
526         RtlMoveMemory(NodeEntry->ShortName.Buffer, ShortName->Buffer, ShortName->Length);
527     }
528     else
529     {
530         NodeEntry->ShortName.Length = NodeEntry->ShortName.MaximumLength = 0;
531         NodeEntry->ShortName.Buffer = NULL;
532     }
533 
534     if (LongName)
535     {
536         /* copy long name */
537         NodeEntry->LongName.Length = LongName->Length;
538         NodeEntry->LongName.MaximumLength = LongName->Length;
539         NodeEntry->LongName.Buffer = (LPWSTR)((ULONG_PTR)NodeEntry + sizeof(TUNNEL_NODE_ENTRY) + NodeEntry->ShortName.Length);
540 
541         RtlMoveMemory(NodeEntry->LongName.Buffer, LongName->Buffer, LongName->Length);
542     }
543     else
544     {
545         NodeEntry->LongName.Length = NodeEntry->LongName.MaximumLength = 0;
546         NodeEntry->LongName.Buffer = NULL;
547     }
548 
549      NodeEntry->DataLength = DataLength;
550      NodeEntry->Data = (PVOID)((ULONG_PTR)NodeEntry + sizeof(TUNNEL_NODE_ENTRY) + NodeEntry->ShortName.Length + NodeEntry->LongName.Length);
551      RtlMoveMemory(NodeEntry->Data, Data, DataLength);
552 
553      /* increment node count */
554      Cache->NumEntries++;
555 
556      /* insert into list */
557      InsertTailList(&Cache->TimerQueue, &NodeEntry->TimerQueueEntry);
558 
559      /* prune cache */
560      FsRtlPruneTunnelCache(Cache, &PoolList);
561 
562      /* release lock */
563      ExReleaseFastMutex(&Cache->Mutex);
564 
565      /* free pool list */
566      FsRtlEmptyFreePoolList(&PoolList);
567 }
568 
569 /*++
570  * @name FsRtlDeleteKeyFromTunnelCache
571  * @implemented
572  *
573  * FILLME
574  *
575  * @param Cache
576  *        FILLME
577  *
578  * @param DirectoryKey
579  *        FILLME
580  *
581  * @return None
582  *
583  * @remarks None
584  *
585  *--*/
586 VOID
587 NTAPI
588 FsRtlDeleteKeyFromTunnelCache(IN PTUNNEL Cache,
589                               IN ULONGLONG DirectoryKey)
590 {
591     BOOLEAN Rebalance = TRUE;
592     LIST_ENTRY PoolList;
593     PTUNNEL_NODE_ENTRY CurNode;
594     PRTL_SPLAY_LINKS CurEntry, LastEntry = NULL, Successors;
595 
596     PAGED_CODE();
597 
598     /* check if tunnel cache is enabled */
599     if (!TunnelMaxEntries)
600     {
601         /* entries are disabled */
602         return;
603     }
604 
605     /* initialize free pool list */
606     InitializeListHead(&PoolList);
607 
608     /* acquire lock */
609     ExAcquireFastMutex(&Cache->Mutex);
610 
611     /* Look for the entry */
612     CurEntry = Cache->Cache;
613     while (CurEntry)
614     {
615         CurNode = CONTAINING_RECORD(CurEntry, TUNNEL_NODE_ENTRY, SplayInfo);
616 
617         if (CurNode->DirectoryKey > DirectoryKey)
618         {
619             /* current directory key is bigger */
620             CurEntry = CurEntry->LeftChild;
621         }
622         else if (CurNode->DirectoryKey < DirectoryKey)
623         {
624             /* if we have already found one suitable, break */
625             if (LastEntry != NULL)
626             {
627                 break;
628             }
629 
630             /* current directory key is smaller */
631             CurEntry = CurEntry->RightChild;
632         }
633         else
634         {
635             /* save and look for another */
636             LastEntry = CurEntry;
637             CurEntry = CurEntry->LeftChild;
638         }
639     }
640 
641     /* was it found? */
642     if (LastEntry == NULL)
643     {
644         /* release tunnel lock */
645         ExReleaseFastMutex(&Cache->Mutex);
646 
647         return;
648     }
649 
650     /* delete any matching key */
651     do
652     {
653         CurNode = CONTAINING_RECORD(LastEntry, TUNNEL_NODE_ENTRY, SplayInfo);
654 
655         Successors = RtlRealSuccessor(LastEntry);
656         if (CurNode->DirectoryKey != DirectoryKey)
657         {
658             break;
659         }
660 
661         /* remove from tunnel */
662         FsRtlRemoveNodeFromTunnel(Cache, CurNode, &PoolList, &Rebalance);
663         LastEntry = Successors;
664     }
665     while (LastEntry != NULL);
666 
667     /* release tunnel lock */
668     ExReleaseFastMutex(&Cache->Mutex);
669 
670     /* free pool */
671     FsRtlEmptyFreePoolList(&PoolList);
672 }
673 
674 /*++
675  * @name FsRtlDeleteTunnelCache
676  * @implemented
677  *
678  * FILLME
679  *
680  * @param Cache
681  *        FILLME
682  *
683  * @return None
684  *
685  * @remarks None
686  *
687  *--*/
688 VOID
689 NTAPI
690 FsRtlDeleteTunnelCache(IN PTUNNEL Cache)
691 {
692     PLIST_ENTRY Entry, NextEntry;
693     PTUNNEL_NODE_ENTRY CurEntry;
694 
695     PAGED_CODE();
696 
697     /* check if tunnel cache is enabled */
698     if (!TunnelMaxEntries)
699     {
700         /* entries are disabled */
701         return;
702     }
703 
704     /* free all entries */
705     Entry = Cache->TimerQueue.Flink;
706 
707     while(Entry != &Cache->TimerQueue)
708     {
709         /* get node entry */
710         CurEntry = CONTAINING_RECORD(Entry, TUNNEL_NODE_ENTRY, TimerQueueEntry);
711 
712         /* get next entry */
713          NextEntry = Entry->Flink;
714 
715         /* remove entry from list */
716         RemoveEntryList(&CurEntry->TimerQueueEntry);
717 
718         /* free entry */
719         FsRtlFreeTunnelNode(CurEntry, NULL);
720 
721         /* move to next entry */
722         Entry = NextEntry;
723     }
724 
725     /* reset object */
726     Cache->Cache = NULL;
727     Cache->NumEntries = 0;
728     InitializeListHead(&Cache->TimerQueue);
729 }
730 
731 /*++
732  * @name FsRtlFindInTunnelCache
733  * @implemented
734  *
735  * FILLME
736  *
737  * @param Cache
738  *        FILLME
739  *
740  * @param DirectoryKey
741  *        FILLME
742  *
743  * @param ShortName
744  *        FILLME
745  *
746  * @param LongName
747  *        FILLME
748  *
749  * @param KeyByShortName
750  *        FILLME
751  *
752  * @param DataLength
753  *        FILLME
754  *
755  * @param Data
756  *        FILLME
757  *
758  * @return None
759  *
760  * @remarks None
761  *
762  *--*/
763 BOOLEAN
764 NTAPI
765 FsRtlFindInTunnelCache(IN PTUNNEL Cache,
766                        IN ULONGLONG DirectoryKey,
767                        IN PUNICODE_STRING Name,
768                        OUT PUNICODE_STRING ShortName,
769                        OUT PUNICODE_STRING LongName,
770                        IN OUT PULONG DataLength,
771                        OUT PVOID Data)
772 {
773     BOOLEAN Ret = FALSE;
774     PTUNNEL_NODE_ENTRY CurEntry;
775     LIST_ENTRY PoolList;
776     //NTSTATUS Status;
777     LONG Result;
778 
779     PAGED_CODE();
780 
781     /* check if tunnel cache is enabled */
782     if (!TunnelMaxEntries)
783     {
784         /* entries are disabled */
785         return FALSE;
786     }
787 
788     /* initialize free pool list */
789     InitializeListHead(&PoolList);
790 
791     /* acquire tunnel lock */
792     ExAcquireFastMutex(&Cache->Mutex);
793 
794     /* prune old entries */
795     FsRtlPruneTunnelCache(Cache, &PoolList);
796 
797     /* now search cache for existing entries */
798     CurEntry = (PTUNNEL_NODE_ENTRY)Cache->Cache;
799 
800     while(CurEntry)
801     {
802         /* compare current node */
803         Result = FsRtlCompareNodeAndKey(CurEntry, DirectoryKey, Name);
804 
805         if (Result > 0)
806         {
807             /* current directory key is bigger */
808             CurEntry = (PTUNNEL_NODE_ENTRY)CurEntry->SplayInfo.LeftChild;
809         }
810         else
811         {
812             if (Result == 0)
813             {
814                 /* found equal entry */
815                 break;
816             }
817 
818             /* current directory key is smaller */
819             CurEntry = (PTUNNEL_NODE_ENTRY)CurEntry->SplayInfo.RightChild;
820         }
821     }
822 
823     if (CurEntry != NULL)
824     {
825         _SEH2_TRY
826         {
827             /* copy short name */
828             RtlCopyUnicodeString(ShortName, &CurEntry->ShortName);
829 
830             /* check size */
831             if (LongName->MaximumLength < CurEntry->LongName.Length)
832             {
833                 /* buffer is too small */
834                 LongName->Buffer = ExAllocatePool(PagedPool, CurEntry->LongName.Length);
835                 if (LongName->Buffer)
836                 {
837                     LongName->Length = CurEntry->LongName.Length;
838                     LongName->MaximumLength = CurEntry->LongName.MaximumLength;
839                     RtlMoveMemory(LongName->Buffer, CurEntry->LongName.Buffer, CurEntry->LongName.Length);
840                 }
841             }
842             else
843             {
844                 /* buffer is big enough */
845                 RtlCopyUnicodeString(LongName, &CurEntry->LongName);
846             }
847 
848             /* copy data */
849             RtlMoveMemory(Data, CurEntry->Data, CurEntry->DataLength);
850 
851             /* store size */
852             *DataLength = CurEntry->DataLength;
853 
854             /* done */
855             Ret = TRUE;
856         }
857         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
858         {
859             /* Get the status */
860             //Status = _SEH2_GetExceptionCode();
861         }
862         _SEH2_END;
863 
864     }
865 
866     /* release tunnel lock */
867     ExReleaseFastMutex(&Cache->Mutex);
868 
869     /* free pool */
870     FsRtlEmptyFreePoolList(&PoolList);
871 
872     return Ret;
873 }
874 
875 /*++
876  * @name FsRtlInitializeTunnelCache
877  * @implemented
878  *
879  * FILLME
880  *
881  * @param Cache
882  *        FILLME
883  *
884  * @return None
885  *
886  * @remarks None
887  *
888  *--*/
889 VOID
890 NTAPI
891 FsRtlInitializeTunnelCache(IN PTUNNEL Cache)
892 {
893     PAGED_CODE();
894 
895     /* initialize mutex */
896     ExInitializeFastMutex(&Cache->Mutex);
897 
898     /* initialize node tree */
899     Cache->Cache = NULL;
900 
901     /* initialize timer list */
902     InitializeListHead(&Cache->TimerQueue);
903 
904     /* initialize node count */
905     Cache->NumEntries = 0;
906 }
907 
908 /* EOF */
909