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
= 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
= CONTAINING_RECORD(Entry
, TUNNEL_NODE_ENTRY
, TimerQueueEntry
);
128 FsRtlRemoveNodeFromTunnel(Cache
, CurEntry
, PoolList
, &Rebalance
);
134 FsRtlGetTunnelParameterValue(
135 IN PUNICODE_STRING ParameterName
,
138 UNICODE_STRING Root
= RTL_CONSTANT_STRING(L
"Registry\\Machine\\System\\CurrentControlSet\\Control\\FileSystem");
139 OBJECT_ATTRIBUTES ObjectAttributes
;
143 PKEY_VALUE_FULL_INFORMATION Info
;
145 /* initialize object attributes */
146 InitializeObjectAttributes(&ObjectAttributes
, &Root
, OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
148 /* open registry key */
149 Status
= ZwOpenKey(&hKey
, KEY_READ
, &ObjectAttributes
);
151 if (!NT_SUCCESS(Status
))
153 /* failed to open key */
157 /* query value size */
158 Status
= ZwQueryValueKey(hKey
, ParameterName
, KeyValueFullInformation
, NULL
, 0, &Length
);
160 if (Status
!= STATUS_BUFFER_TOO_SMALL
)
162 /* failed to query size */
167 /* allocate buffer */
168 Info
= ExAllocatePool(PagedPool
, Length
);
178 Status
= ZwQueryValueKey(hKey
, ParameterName
, KeyValueFullInformation
, NULL
, 0, &Length
);
180 if (NT_SUCCESS(Status
))
182 if (Info
->DataLength
)
185 *Value
= (ULONG
)((ULONG_PTR
)Info
+ Info
->DataOffset
);
199 FsRtlInitializeTunnels(VOID
)
202 UNICODE_STRING MaximumTunnelEntryAgeInSeconds
= RTL_CONSTANT_STRING(L
"MaximumTunnelEntryAgeInSeconds");
203 UNICODE_STRING MaximumTunnelEntries
= RTL_CONSTANT_STRING( L
"MaximumTunnelEntries");
206 if (MmIsThisAnNtAsSystem())
209 TunnelMaxEntries
= 1024;
212 /* check for custom override of max entries*/
213 FsRtlGetTunnelParameterValue(&MaximumTunnelEntries
, &TunnelMaxEntries
);
215 /* check for custom override of age*/
216 FsRtlGetTunnelParameterValue(&MaximumTunnelEntryAgeInSeconds
, &TunnelMaxAge
);
220 /* no age means no entries */
221 TunnelMaxEntries
= 0;
224 /* get max entries */
225 TunnelEntries
= TunnelMaxEntries
;
227 /* convert to ticks */
228 TunnelMaxAge
*= 10000000;
230 if(TunnelMaxEntries
<= 65535)
232 /* use max 256 entries */
233 TunnelEntries
= TunnelMaxEntries
/ 16;
236 if(!TunnelEntries
&& TunnelMaxEntries
)
238 /* max tunnel entries was too small */
239 TunnelEntries
= TunnelMaxEntries
+ 1;
242 if (TunnelEntries
> 0xFFFF)
244 /* max entries is 256 */
248 /* initialize look aside list */
249 ExInitializePagedLookasideList(&TunnelLookasideList
, NULL
, NULL
, 0, DEFAULT_ENTRY_SIZE
, 'TunL', TunnelEntries
);
253 FsRtlCompareNodeAndKey(
254 IN PTUNNEL_NODE_ENTRY CurEntry
,
255 IN ULONGLONG DirectoryKey
,
256 IN PUNICODE_STRING KeyString
)
258 PUNICODE_STRING String
;
261 if (DirectoryKey
> CurEntry
->DirectoryKey
)
265 else if (DirectoryKey
< CurEntry
->DirectoryKey
)
271 if (CurEntry
->Flags
& TUNNEL_FLAG_KEY_SHORT_NAME
)
273 /* use short name as key */
274 String
= &CurEntry
->ShortName
;
278 /* use long name as key */
279 String
= &CurEntry
->LongName
;
282 Ret
= RtlCompareUnicodeString(KeyString
, String
, TRUE
);
289 FsRtlEmptyFreePoolList(
290 IN PLIST_ENTRY PoolList
)
292 PLIST_ENTRY CurEntry
;
293 PTUNNEL_NODE_ENTRY CurNode
;
295 /* loop over all the entry */
296 while (!IsListEmpty(PoolList
))
298 /* and free them, one by one */
299 CurEntry
= RemoveHeadList(PoolList
);
300 CurNode
= CONTAINING_RECORD(CurEntry
, TUNNEL_NODE_ENTRY
, TimerQueueEntry
);
301 FsRtlFreeTunnelNode(CurNode
, 0);
305 /* PUBLIC FUNCTIONS **********************************************************/
308 * @name FsRtlAddToTunnelCache
316 * @param DirectoryKey
325 * @param KeyByShortName
341 FsRtlAddToTunnelCache(IN PTUNNEL Cache
,
342 IN ULONGLONG DirectoryKey
,
343 IN PUNICODE_STRING ShortName
,
344 IN PUNICODE_STRING LongName
,
345 IN BOOLEAN KeyByShortName
,
349 PTUNNEL_NODE_ENTRY NodeEntry
= NULL
;
350 PRTL_SPLAY_LINKS CurEntry
, LastEntry
;
353 BOOLEAN AllocatedFromPool
= FALSE
;
354 PUNICODE_STRING KeyString
;
359 /* check if tunnel cache is enabled */
360 if (!TunnelMaxEntries
)
362 /* entries are disabled */
366 /* initialize free pool list */
367 InitializeListHead(&PoolList
);
369 /* calculate node length */
370 Length
= sizeof(TUNNEL_NODE_ENTRY
);
373 Length
+= DataLength
;
377 /* add short name length */
378 Length
+= ShortName
->Length
;
383 /* add short name length */
384 Length
+= LongName
->Length
;
387 if (Length
<= DEFAULT_ENTRY_SIZE
)
389 /* get standard entry */
390 NodeEntry
= ExAllocateFromPagedLookasideList(&TunnelLookasideList
);
393 if (NodeEntry
== NULL
)
395 /* bigger than default entry or allocation failed */
396 NodeEntry
= ExAllocatePool(PagedPool
| POOL_COLD_ALLOCATION
, Length
);
397 /* check for success */
398 if (NodeEntry
== NULL
)
404 AllocatedFromPool
= TRUE
;
408 ExAcquireFastMutex(&Cache
->Mutex
);
410 /* now search cache for existing entries */
411 CurEntry
= Cache
->Cache
;
413 /* check which key should be used for search */
414 KeyString
= (KeyByShortName
? ShortName
: LongName
);
416 /* initialize last entry */
421 /* compare current node */
422 Result
= FsRtlCompareNodeAndKey((PTUNNEL_NODE_ENTRY
)CurEntry
, DirectoryKey
, KeyString
);
424 /* backup last entry */
425 LastEntry
= CurEntry
;
429 /* current directory key is bigger */
430 CurEntry
= CurEntry
->LeftChild
;
436 /* found equal entry */
440 /* current directory key is smaller */
441 CurEntry
= CurEntry
->RightChild
;
445 /* initialize node entry */
446 RtlInitializeSplayLinks(&NodeEntry
->SplayInfo
);
448 if (CurEntry
!= NULL
)
450 /* found existing item */
451 if (CurEntry
->LeftChild
)
454 RtlInsertAsLeftChild(NodeEntry
, CurEntry
->LeftChild
);
457 if (CurEntry
->RightChild
)
460 RtlInsertAsRightChild(NodeEntry
, CurEntry
->RightChild
);
463 if (CurEntry
->Parent
== CurEntry
)
465 /* cur entry was root */
466 Cache
->Cache
= (struct _RTL_SPLAY_LINKS
*)NodeEntry
;
470 /* update parent node */
471 if (RtlIsLeftChild(CurEntry
))
473 RtlInsertAsLeftChild(RtlParent(CurEntry
), NodeEntry
);
477 RtlInsertAsRightChild(RtlParent(CurEntry
), NodeEntry
);
482 RemoveEntryList(&((PTUNNEL_NODE_ENTRY
)CurEntry
)->TimerQueueEntry
);
484 /* free node entry */
485 FsRtlFreeTunnelNode((PTUNNEL_NODE_ENTRY
)CurEntry
, &PoolList
);
487 /* decrement node count */
492 if (LastEntry
== NULL
)
494 /* first entry in tunnel cache */
495 Cache
->Cache
= (struct _RTL_SPLAY_LINKS
*)NodeEntry
;
502 RtlInsertAsLeftChild(LastEntry
, NodeEntry
);
507 RtlInsertAsRightChild(LastEntry
, NodeEntry
);
512 /* initialize entry */
513 KeQuerySystemTime(&NodeEntry
->Time
);
515 NodeEntry
->DirectoryKey
= DirectoryKey
;
516 NodeEntry
->Flags
= (AllocatedFromPool
? TUNNEL_FLAG_POOL
: 0x0);
517 NodeEntry
->Flags
|= (KeyByShortName
? TUNNEL_FLAG_KEY_SHORT_NAME
: 0x0);
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
));
526 RtlMoveMemory(NodeEntry
->ShortName
.Buffer
, ShortName
->Buffer
, ShortName
->Length
);
530 NodeEntry
->ShortName
.Length
= NodeEntry
->ShortName
.MaximumLength
= 0;
531 NodeEntry
->ShortName
.Buffer
= NULL
;
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
);
541 RtlMoveMemory(NodeEntry
->LongName
.Buffer
, LongName
->Buffer
, LongName
->Length
);
545 NodeEntry
->LongName
.Length
= NodeEntry
->LongName
.MaximumLength
= 0;
546 NodeEntry
->LongName
.Buffer
= NULL
;
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
);
553 /* increment node count */
556 /* insert into list */
557 InsertTailList(&Cache
->TimerQueue
, &NodeEntry
->TimerQueueEntry
);
560 FsRtlPruneTunnelCache(Cache
, &PoolList
);
563 ExReleaseFastMutex(&Cache
->Mutex
);
566 FsRtlEmptyFreePoolList(&PoolList
);
570 * @name FsRtlDeleteKeyFromTunnelCache
578 * @param DirectoryKey
588 FsRtlDeleteKeyFromTunnelCache(IN PTUNNEL Cache
,
589 IN ULONGLONG DirectoryKey
)
591 BOOLEAN Rebalance
= TRUE
;
593 PTUNNEL_NODE_ENTRY CurNode
;
594 PRTL_SPLAY_LINKS CurEntry
, LastEntry
= NULL
, Successors
;
598 /* check if tunnel cache is enabled */
599 if (!TunnelMaxEntries
)
601 /* entries are disabled */
605 /* initialize free pool list */
606 InitializeListHead(&PoolList
);
609 ExAcquireFastMutex(&Cache
->Mutex
);
611 /* Look for the entry */
612 CurEntry
= Cache
->Cache
;
615 CurNode
= CONTAINING_RECORD(CurEntry
, TUNNEL_NODE_ENTRY
, SplayInfo
);
617 if (CurNode
->DirectoryKey
> DirectoryKey
)
619 /* current directory key is bigger */
620 CurEntry
= CurEntry
->LeftChild
;
622 else if (CurNode
->DirectoryKey
< DirectoryKey
)
624 /* if we have already found one suitable, break */
625 if (LastEntry
!= NULL
)
630 /* current directory key is smaller */
631 CurEntry
= CurEntry
->RightChild
;
635 /* save and look for another */
636 LastEntry
= CurEntry
;
637 CurEntry
= CurEntry
->LeftChild
;
642 if (LastEntry
== NULL
)
644 /* release tunnel lock */
645 ExReleaseFastMutex(&Cache
->Mutex
);
650 /* delete any matching key */
653 CurNode
= CONTAINING_RECORD(LastEntry
, TUNNEL_NODE_ENTRY
, SplayInfo
);
655 Successors
= RtlRealSuccessor(LastEntry
);
656 if (CurNode
->DirectoryKey
!= DirectoryKey
)
661 /* remove from tunnel */
662 FsRtlRemoveNodeFromTunnel(Cache
, CurNode
, &PoolList
, &Rebalance
);
663 LastEntry
= Successors
;
665 while (LastEntry
!= NULL
);
667 /* release tunnel lock */
668 ExReleaseFastMutex(&Cache
->Mutex
);
671 FsRtlEmptyFreePoolList(&PoolList
);
675 * @name FsRtlDeleteTunnelCache
690 FsRtlDeleteTunnelCache(IN PTUNNEL Cache
)
692 PLIST_ENTRY Entry
, NextEntry
;
693 PTUNNEL_NODE_ENTRY CurEntry
;
697 /* check if tunnel cache is enabled */
698 if (!TunnelMaxEntries
)
700 /* entries are disabled */
704 /* free all entries */
705 Entry
= Cache
->TimerQueue
.Flink
;
707 while(Entry
!= &Cache
->TimerQueue
)
710 CurEntry
= CONTAINING_RECORD(Entry
, TUNNEL_NODE_ENTRY
, TimerQueueEntry
);
713 NextEntry
= Entry
->Flink
;
715 /* remove entry from list */
716 RemoveEntryList(&CurEntry
->TimerQueueEntry
);
719 FsRtlFreeTunnelNode(CurEntry
, NULL
);
721 /* move to next entry */
727 Cache
->NumEntries
= 0;
728 InitializeListHead(&Cache
->TimerQueue
);
732 * @name FsRtlFindInTunnelCache
740 * @param DirectoryKey
749 * @param KeyByShortName
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
,
774 PTUNNEL_NODE_ENTRY CurEntry
;
781 /* check if tunnel cache is enabled */
782 if (!TunnelMaxEntries
)
784 /* entries are disabled */
788 /* initialize free pool list */
789 InitializeListHead(&PoolList
);
791 /* acquire tunnel lock */
792 ExAcquireFastMutex(&Cache
->Mutex
);
794 /* prune old entries */
795 FsRtlPruneTunnelCache(Cache
, &PoolList
);
797 /* now search cache for existing entries */
798 CurEntry
= (PTUNNEL_NODE_ENTRY
)Cache
->Cache
;
802 /* compare current node */
803 Result
= FsRtlCompareNodeAndKey(CurEntry
, DirectoryKey
, Name
);
807 /* current directory key is bigger */
808 CurEntry
= (PTUNNEL_NODE_ENTRY
)CurEntry
->SplayInfo
.LeftChild
;
814 /* found equal entry */
818 /* current directory key is smaller */
819 CurEntry
= (PTUNNEL_NODE_ENTRY
)CurEntry
->SplayInfo
.RightChild
;
823 if (CurEntry
!= NULL
)
827 /* copy short name */
828 RtlCopyUnicodeString(ShortName
, &CurEntry
->ShortName
);
831 if (LongName
->MaximumLength
< CurEntry
->LongName
.Length
)
833 /* buffer is too small */
834 LongName
->Buffer
= ExAllocatePool(PagedPool
, CurEntry
->LongName
.Length
);
835 if (LongName
->Buffer
)
837 LongName
->Length
= CurEntry
->LongName
.Length
;
838 LongName
->MaximumLength
= CurEntry
->LongName
.MaximumLength
;
839 RtlMoveMemory(LongName
->Buffer
, CurEntry
->LongName
.Buffer
, CurEntry
->LongName
.Length
);
844 /* buffer is big enough */
845 RtlCopyUnicodeString(LongName
, &CurEntry
->LongName
);
849 RtlMoveMemory(Data
, CurEntry
->Data
, CurEntry
->DataLength
);
852 *DataLength
= CurEntry
->DataLength
;
857 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
860 //Status = _SEH2_GetExceptionCode();
866 /* release tunnel lock */
867 ExReleaseFastMutex(&Cache
->Mutex
);
870 FsRtlEmptyFreePoolList(&PoolList
);
876 * @name FsRtlInitializeTunnelCache
891 FsRtlInitializeTunnelCache(IN PTUNNEL Cache
)
895 /* initialize mutex */
896 ExInitializeFastMutex(&Cache
->Mutex
);
898 /* initialize node tree */
901 /* initialize timer list */
902 InitializeListHead(&Cache
->TimerQueue
);
904 /* initialize node count */
905 Cache
->NumEntries
= 0;