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