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)
10 /* INCLUDES ******************************************************************/
17 RTL_SPLAY_LINKS SplayInfo
;
18 LIST_ENTRY TimerQueueEntry
;
20 ULONGLONG DirectoryKey
;
22 UNICODE_STRING LongName
;
23 UNICODE_STRING ShortName
;
26 } TUNNEL_NODE_ENTRY
, *PTUNNEL_NODE_ENTRY
;
28 ULONG TunnelMaxEntries
= 256;
29 ULONG TunnelMaxAge
= 15;
30 PAGED_LOOKASIDE_LIST TunnelLookasideList
;
32 #define DEFAULT_EXTRA_SIZE (72)
33 #define DEFAULT_ENTRY_SIZE (sizeof(TUNNEL_NODE_ENTRY) + DEFAULT_EXTRA_SIZE)
35 #define TUNNEL_FLAG_POOL 0x2
36 #define TUNNEL_FLAG_KEY_SHORT_NAME 0x1
40 IN PTUNNEL_NODE_ENTRY CurEntry
,
41 IN PLIST_ENTRY PoolList OPTIONAL
)
45 /* divert the linked list entry, it's not required anymore, but we need it */
46 InsertHeadList(PoolList
, &CurEntry
->TimerQueueEntry
);
50 if (CurEntry
->Flags
& TUNNEL_FLAG_POOL
)
53 ExFreeToPagedLookasideList(&TunnelLookasideList
, CurEntry
);
57 FsRtlRemoveNodeFromTunnel(
59 IN PTUNNEL_NODE_ENTRY CurEntry
,
60 IN PLIST_ENTRY PoolList
,
61 OUT PBOOLEAN Rebalance
)
63 /* delete entry and rebalance if required */
64 if (Rebalance
&& *Rebalance
)
66 Cache
->Cache
= RtlDelete(&CurEntry
->SplayInfo
);
72 RtlDeleteNoSplay(&CurEntry
->SplayInfo
, &Cache
->Cache
);
76 RemoveEntryList(&CurEntry
->TimerQueueEntry
);
79 FsRtlFreeTunnelNode(CurEntry
, PoolList
);
81 /* decrement node count */
86 FsRtlPruneTunnelCache(
88 IN PLIST_ENTRY PoolList
)
90 PLIST_ENTRY Entry
, NextEntry
;
91 PTUNNEL_NODE_ENTRY CurEntry
;
92 LARGE_INTEGER CurTime
, OldTime
;
93 BOOLEAN Rebalance
= TRUE
;
97 KeQuerySystemTime(&CurTime
);
99 /* subtract maximum node age */
100 OldTime
.QuadPart
= CurTime
.QuadPart
- TunnelMaxAge
;
102 /* free all entries */
103 Entry
= Cache
->TimerQueue
.Flink
;
105 while(Entry
!= &Cache
->TimerQueue
)
108 CurEntry
= (PTUNNEL_NODE_ENTRY
)CONTAINING_RECORD(Entry
, TUNNEL_NODE_ENTRY
, TimerQueueEntry
);
111 NextEntry
= Entry
->Flink
;
113 /* prune if expired OR if in advance in time */
114 if (CurEntry
->Time
.QuadPart
< OldTime
.QuadPart
||
115 CurEntry
->Time
.QuadPart
> CurTime
.QuadPart
)
117 FsRtlRemoveNodeFromTunnel(Cache
, CurEntry
, PoolList
, &Rebalance
);
120 /* move to next entry */
124 /* If we have too many entries */
125 while (Cache
->NumEntries
> TunnelMaxEntries
)
127 CurEntry
= (PTUNNEL_NODE_ENTRY
)CONTAINING_RECORD(Entry
, TUNNEL_NODE_ENTRY
, TimerQueueEntry
);
128 FsRtlRemoveNodeFromTunnel(Cache
, CurEntry
, PoolList
, &Rebalance
);
133 FsRtlGetTunnelParameterValue(
134 IN PUNICODE_STRING ParameterName
,
137 UNICODE_STRING Root
= RTL_CONSTANT_STRING(L
"Registry\\Machine\\System\\CurrentControlSet\\Control\\FileSystem");
138 OBJECT_ATTRIBUTES ObjectAttributes
;
142 PKEY_VALUE_FULL_INFORMATION Info
;
144 /* initialize object attributes */
145 InitializeObjectAttributes(&ObjectAttributes
, &Root
, OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
147 /* open registry key */
148 Status
= ZwOpenKey(&hKey
, KEY_READ
, &ObjectAttributes
);
150 if (!NT_SUCCESS(Status
))
152 /* failed to open key */
156 /* query value size */
157 Status
= ZwQueryValueKey(hKey
, ParameterName
, KeyValueFullInformation
, NULL
, 0, &Length
);
159 if (Status
!= STATUS_BUFFER_TOO_SMALL
)
161 /* failed to query size */
166 /* allocate buffer */
167 Info
= ExAllocatePool(PagedPool
, Length
);
177 Status
= ZwQueryValueKey(hKey
, ParameterName
, KeyValueFullInformation
, NULL
, 0, &Length
);
179 if (NT_SUCCESS(Status
))
181 if (Info
->DataLength
)
184 *Value
= (ULONG
)((ULONG_PTR
)Info
+ Info
->DataOffset
);
197 FsRtlInitializeTunnels()
200 UNICODE_STRING MaximumTunnelEntryAgeInSeconds
= RTL_CONSTANT_STRING(L
"MaximumTunnelEntryAgeInSeconds");
201 UNICODE_STRING MaximumTunnelEntries
= RTL_CONSTANT_STRING( L
"MaximumTunnelEntries");
204 if (MmIsThisAnNtAsSystem())
207 TunnelMaxEntries
= 1024;
210 /* check for custom override of max entries*/
211 FsRtlGetTunnelParameterValue(&MaximumTunnelEntries
, &TunnelMaxEntries
);
213 /* check for custom override of age*/
214 FsRtlGetTunnelParameterValue(&MaximumTunnelEntryAgeInSeconds
, &TunnelMaxAge
);
218 /* no age means no entries */
219 TunnelMaxEntries
= 0;
222 /* get max entries */
223 TunnelEntries
= TunnelMaxEntries
;
225 /* convert to ticks */
226 TunnelMaxAge
*= 10000000;
228 if(TunnelMaxEntries
<= 65535)
230 /* use max 256 entries */
231 TunnelEntries
= TunnelMaxEntries
/ 16;
234 if(!TunnelEntries
&& TunnelMaxEntries
)
236 /* max tunnel entries was too small */
237 TunnelEntries
= TunnelMaxEntries
+ 1;
240 if (TunnelEntries
> 0xFFFF)
242 /* max entries is 256 */
246 /* initialize look aside list */
247 ExInitializePagedLookasideList(&TunnelLookasideList
, NULL
, NULL
, 0, DEFAULT_ENTRY_SIZE
, 'TunL', TunnelEntries
);
251 FsRtlCompareNodeAndKey(
252 IN PTUNNEL_NODE_ENTRY CurEntry
,
253 IN ULONGLONG DirectoryKey
,
254 IN PUNICODE_STRING KeyString
)
256 PUNICODE_STRING String
;
259 if (DirectoryKey
> CurEntry
->DirectoryKey
)
263 else if (DirectoryKey
< CurEntry
->DirectoryKey
)
269 if (CurEntry
->Flags
& TUNNEL_FLAG_KEY_SHORT_NAME
)
271 /* use short name as key */
272 String
= &CurEntry
->ShortName
;
276 /* use long name as key */
277 String
= &CurEntry
->LongName
;
280 Ret
= RtlCompareUnicodeString(KeyString
, String
, TRUE
);
287 FsRtlEmptyFreePoolList(
288 IN PLIST_ENTRY PoolList
)
290 PLIST_ENTRY CurEntry
;
291 PTUNNEL_NODE_ENTRY CurNode
;
293 /* loop over all the entry */
294 while (!IsListEmpty(PoolList
))
296 /* and free them, one by one */
297 CurEntry
= RemoveHeadList(PoolList
);
298 CurNode
= (PTUNNEL_NODE_ENTRY
)CONTAINING_RECORD(CurEntry
, TUNNEL_NODE_ENTRY
, TimerQueueEntry
);
299 FsRtlFreeTunnelNode(CurNode
, 0);
303 /* PUBLIC FUNCTIONS **********************************************************/
306 * @name FsRtlAddToTunnelCache
314 * @param DirectoryKey
323 * @param KeyByShortName
339 FsRtlAddToTunnelCache(IN PTUNNEL Cache
,
340 IN ULONGLONG DirectoryKey
,
341 IN PUNICODE_STRING ShortName
,
342 IN PUNICODE_STRING LongName
,
343 IN BOOLEAN KeyByShortName
,
347 PTUNNEL_NODE_ENTRY NodeEntry
;
348 PRTL_SPLAY_LINKS CurEntry
, LastEntry
;
351 BOOLEAN AllocatedFromPool
= FALSE
;
352 PUNICODE_STRING KeyString
;
357 /* check if tunnel cache is enabled */
358 if (!TunnelMaxEntries
)
360 /* entries are disabled */
364 /* initialize free pool list */
365 InitializeListHead(&PoolList
);
367 /* calculate node length */
368 Length
= sizeof(TUNNEL_NODE_ENTRY
);
371 Length
+= DataLength
;
375 /* add short name length */
376 Length
+= ShortName
->Length
;
381 /* add short name length */
382 Length
+= LongName
->Length
;
385 if (Length
> DEFAULT_ENTRY_SIZE
)
387 /* bigger than default entry */
388 NodeEntry
= ExAllocatePool(NonPagedPool
, Length
);
389 AllocatedFromPool
= TRUE
;
393 /* get standard entry */
394 NodeEntry
= ExAllocateFromPagedLookasideList(&TunnelLookasideList
);
397 /* check for success */
405 ExAcquireFastMutex(&Cache
->Mutex
);
407 /* now search cache for existing entries */
408 CurEntry
= Cache
->Cache
;
410 /* check which key should be used for search */
411 KeyString
= (KeyByShortName
? ShortName
: LongName
);
413 /* initialize last entry */
418 /* compare current node */
419 Result
= FsRtlCompareNodeAndKey((PTUNNEL_NODE_ENTRY
)CurEntry
, DirectoryKey
, KeyString
);
421 /* backup last entry */
422 LastEntry
= CurEntry
;
426 /* current directory key is bigger */
427 CurEntry
= CurEntry
->LeftChild
;
433 /* found equal entry */
437 /* current directory key is smaller */
438 CurEntry
= CurEntry
->RightChild
;
442 /* initialize node entry */
443 RtlInitializeSplayLinks(&NodeEntry
->SplayInfo
);
445 if (CurEntry
!= NULL
)
447 /* found existing item */
448 if (CurEntry
->LeftChild
)
451 RtlInsertAsLeftChild(NodeEntry
, CurEntry
->LeftChild
);
454 if (CurEntry
->RightChild
)
457 RtlInsertAsRightChild(NodeEntry
, CurEntry
->RightChild
);
460 if (CurEntry
->Parent
== CurEntry
)
462 /* cur entry was root */
463 Cache
->Cache
= (struct _RTL_SPLAY_LINKS
*)NodeEntry
;
467 /* update parent node */
468 if (LastEntry
->LeftChild
== CurEntry
)
470 RtlInsertAsLeftChild(LastEntry
, NodeEntry
);
474 RtlInsertAsRightChild(LastEntry
, NodeEntry
);
479 RemoveEntryList(&((PTUNNEL_NODE_ENTRY
)LastEntry
)->TimerQueueEntry
);
481 /* free node entry */
482 FsRtlFreeTunnelNode((PTUNNEL_NODE_ENTRY
)LastEntry
, &PoolList
);
484 /* decrement node count */
489 if (LastEntry
== NULL
)
491 /* first entry in tunnel cache */
492 Cache
->Cache
= (struct _RTL_SPLAY_LINKS
*)NodeEntry
;
499 RtlInsertAsLeftChild(LastEntry
, NodeEntry
);
504 RtlInsertAsRightChild(LastEntry
, NodeEntry
);
509 /* initialize entry */
510 KeQuerySystemTime(&NodeEntry
->Time
);
512 NodeEntry
->DirectoryKey
= DirectoryKey
;
513 NodeEntry
->Flags
= (AllocatedFromPool
? TUNNEL_FLAG_POOL
: 0x0);
514 NodeEntry
->Flags
|= (KeyByShortName
? TUNNEL_FLAG_KEY_SHORT_NAME
: 0x0);
518 /* copy short name */
519 NodeEntry
->ShortName
.Length
= ShortName
->Length
;
520 NodeEntry
->ShortName
.MaximumLength
= ShortName
->Length
;
521 NodeEntry
->ShortName
.Buffer
= (LPWSTR
)((ULONG_PTR
)NodeEntry
+ sizeof(TUNNEL_NODE_ENTRY
));
523 RtlMoveMemory(NodeEntry
->ShortName
.Buffer
, ShortName
->Buffer
, ShortName
->Length
);
527 NodeEntry
->ShortName
.Length
= NodeEntry
->ShortName
.MaximumLength
= 0;
528 NodeEntry
->ShortName
.Buffer
= NULL
;
534 NodeEntry
->LongName
.Length
= LongName
->Length
;
535 NodeEntry
->LongName
.MaximumLength
= LongName
->Length
;
536 NodeEntry
->LongName
.Buffer
= (LPWSTR
)((ULONG_PTR
)NodeEntry
+ sizeof(TUNNEL_NODE_ENTRY
) + NodeEntry
->ShortName
.Length
);
538 RtlMoveMemory(NodeEntry
->LongName
.Buffer
, LongName
->Buffer
, LongName
->Length
);
542 NodeEntry
->LongName
.Length
= NodeEntry
->LongName
.MaximumLength
= 0;
543 NodeEntry
->LongName
.Buffer
= NULL
;
546 NodeEntry
->DataLength
= DataLength
;
547 NodeEntry
->Data
= (PVOID
)((ULONG_PTR
)NodeEntry
+ sizeof(TUNNEL_NODE_ENTRY
) + NodeEntry
->ShortName
.Length
+ NodeEntry
->LongName
.Length
);
548 RtlMoveMemory(NodeEntry
->Data
, Data
, DataLength
);
550 /* increment node count */
553 /* insert into list */
554 InsertTailList(&Cache
->TimerQueue
, &NodeEntry
->TimerQueueEntry
);
557 FsRtlPruneTunnelCache(Cache
, &PoolList
);
560 ExReleaseFastMutex(&Cache
->Mutex
);
563 FsRtlEmptyFreePoolList(&PoolList
);
567 * @name FsRtlDeleteKeyFromTunnelCache
575 * @param DirectoryKey
585 FsRtlDeleteKeyFromTunnelCache(IN PTUNNEL Cache
,
586 IN ULONGLONG DirectoryKey
)
588 BOOLEAN Rebalance
= TRUE
;
590 PTUNNEL_NODE_ENTRY CurNode
;
591 PRTL_SPLAY_LINKS CurEntry
, LastEntry
= NULL
, Successors
;
595 /* check if tunnel cache is enabled */
596 if (!TunnelMaxEntries
)
598 /* entries are disabled */
602 /* initialize free pool list */
603 InitializeListHead(&PoolList
);
606 ExAcquireFastMutex(&Cache
->Mutex
);
608 /* Look for the entry */
609 CurEntry
= Cache
->Cache
;
612 CurNode
= (PTUNNEL_NODE_ENTRY
)CONTAINING_RECORD(CurEntry
, TUNNEL_NODE_ENTRY
, SplayInfo
);
614 if (CurNode
->DirectoryKey
> DirectoryKey
)
616 /* current directory key is bigger */
617 CurEntry
= CurEntry
->LeftChild
;
619 else if (CurNode
->DirectoryKey
< DirectoryKey
)
621 /* if we have already found one suitable, break */
622 if (LastEntry
!= NULL
)
627 /* current directory key is smaller */
628 CurEntry
= CurEntry
->RightChild
;
632 /* save and look for another */
633 LastEntry
= CurEntry
;
634 CurEntry
= CurEntry
->LeftChild
;
639 if (LastEntry
== NULL
)
641 /* release tunnel lock */
642 ExReleaseFastMutex(&Cache
->Mutex
);
647 /* delete any matching key */
650 CurNode
= (PTUNNEL_NODE_ENTRY
)CONTAINING_RECORD(LastEntry
, TUNNEL_NODE_ENTRY
, SplayInfo
);
652 Successors
= RtlRealSuccessor(LastEntry
);
653 if (CurNode
->DirectoryKey
!= DirectoryKey
)
658 /* remove from tunnel */
659 FsRtlRemoveNodeFromTunnel(Cache
, CurNode
, &PoolList
, &Rebalance
);
660 LastEntry
= Successors
;
662 while (LastEntry
!= NULL
);
664 /* release tunnel lock */
665 ExReleaseFastMutex(&Cache
->Mutex
);
668 FsRtlEmptyFreePoolList(&PoolList
);
672 * @name FsRtlDeleteTunnelCache
687 FsRtlDeleteTunnelCache(IN PTUNNEL Cache
)
689 PLIST_ENTRY Entry
, NextEntry
;
690 PTUNNEL_NODE_ENTRY CurEntry
;
694 /* check if tunnel cache is enabled */
695 if (!TunnelMaxEntries
)
697 /* entries are disabled */
701 /* free all entries */
702 Entry
= Cache
->TimerQueue
.Flink
;
704 while(Entry
!= &Cache
->TimerQueue
)
707 CurEntry
= (PTUNNEL_NODE_ENTRY
)CONTAINING_RECORD(Entry
, TUNNEL_NODE_ENTRY
, TimerQueueEntry
);
710 NextEntry
= Entry
->Flink
;
712 /* remove entry from list */
713 RemoveEntryList(&CurEntry
->TimerQueueEntry
);
716 FsRtlFreeTunnelNode(CurEntry
, NULL
);
718 /* move to next entry */
724 Cache
->NumEntries
= 0;
725 InitializeListHead(&Cache
->TimerQueue
);
729 * @name FsRtlFindInTunnelCache
737 * @param DirectoryKey
746 * @param KeyByShortName
762 FsRtlFindInTunnelCache(IN PTUNNEL Cache
,
763 IN ULONGLONG DirectoryKey
,
764 IN PUNICODE_STRING Name
,
765 OUT PUNICODE_STRING ShortName
,
766 OUT PUNICODE_STRING LongName
,
767 IN OUT PULONG DataLength
,
771 PTUNNEL_NODE_ENTRY CurEntry
;
778 /* check if tunnel cache is enabled */
779 if (!TunnelMaxEntries
)
781 /* entries are disabled */
785 /* initialize free pool list */
786 InitializeListHead(&PoolList
);
788 /* acquire tunnel lock */
789 ExAcquireFastMutex(&Cache
->Mutex
);
791 /* prune old entries */
792 FsRtlPruneTunnelCache(Cache
, &PoolList
);
794 /* now search cache for existing entries */
795 CurEntry
= (PTUNNEL_NODE_ENTRY
)Cache
->Cache
;
799 /* compare current node */
800 Result
= FsRtlCompareNodeAndKey(CurEntry
, DirectoryKey
, Name
);
804 /* current directory key is bigger */
805 CurEntry
= (PTUNNEL_NODE_ENTRY
)CurEntry
->SplayInfo
.LeftChild
;
811 /* found equal entry */
815 /* current directory key is smaller */
816 CurEntry
= (PTUNNEL_NODE_ENTRY
)CurEntry
->SplayInfo
.RightChild
;
820 if (CurEntry
!= NULL
)
824 /* copy short name */
825 RtlCopyUnicodeString(ShortName
, &CurEntry
->ShortName
);
828 if (LongName
->MaximumLength
< CurEntry
->LongName
.Length
)
830 /* buffer is too small */
831 LongName
->Buffer
= ExAllocatePool(PagedPool
, CurEntry
->LongName
.Length
);
832 if (LongName
->Buffer
)
834 LongName
->Length
= CurEntry
->LongName
.Length
;
835 LongName
->MaximumLength
= CurEntry
->LongName
.MaximumLength
;
836 RtlMoveMemory(LongName
->Buffer
, CurEntry
->LongName
.Buffer
, CurEntry
->LongName
.Length
);
841 /* buffer is big enough */
842 RtlCopyUnicodeString(LongName
, &CurEntry
->LongName
);
846 RtlMoveMemory(Data
, CurEntry
->Data
, CurEntry
->DataLength
);
849 *DataLength
= CurEntry
->DataLength
;
854 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
857 //Status = _SEH2_GetExceptionCode();
863 /* release tunnel lock */
864 ExReleaseFastMutex(&Cache
->Mutex
);
867 FsRtlEmptyFreePoolList(&PoolList
);
873 * @name FsRtlInitializeTunnelCache
888 FsRtlInitializeTunnelCache(IN PTUNNEL Cache
)
892 /* initialize mutex */
893 ExInitializeFastMutex(&Cache
->Mutex
);
895 /* initialize node tree */
898 /* initialize timer list */
899 InitializeListHead(&Cache
->TimerQueue
);
901 /* initialize node count */
902 Cache
->NumEntries
= 0;