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
;
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 /* bigger than default entry */
390 NodeEntry
= ExAllocatePool(NonPagedPool
, Length
);
391 AllocatedFromPool
= TRUE
;
395 /* get standard entry */
396 NodeEntry
= ExAllocateFromPagedLookasideList(&TunnelLookasideList
);
399 /* check for success */
407 ExAcquireFastMutex(&Cache
->Mutex
);
409 /* now search cache for existing entries */
410 CurEntry
= Cache
->Cache
;
412 /* check which key should be used for search */
413 KeyString
= (KeyByShortName
? ShortName
: LongName
);
415 /* initialize last entry */
420 /* compare current node */
421 Result
= FsRtlCompareNodeAndKey((PTUNNEL_NODE_ENTRY
)CurEntry
, DirectoryKey
, KeyString
);
423 /* backup last entry */
424 LastEntry
= CurEntry
;
428 /* current directory key is bigger */
429 CurEntry
= CurEntry
->LeftChild
;
435 /* found equal entry */
439 /* current directory key is smaller */
440 CurEntry
= CurEntry
->RightChild
;
444 /* initialize node entry */
445 RtlInitializeSplayLinks(&NodeEntry
->SplayInfo
);
447 if (CurEntry
!= NULL
)
449 /* found existing item */
450 if (CurEntry
->LeftChild
)
453 RtlInsertAsLeftChild(NodeEntry
, CurEntry
->LeftChild
);
456 if (CurEntry
->RightChild
)
459 RtlInsertAsRightChild(NodeEntry
, CurEntry
->RightChild
);
462 if (CurEntry
->Parent
== CurEntry
)
464 /* cur entry was root */
465 Cache
->Cache
= (struct _RTL_SPLAY_LINKS
*)NodeEntry
;
469 /* update parent node */
470 if (RtlIsLeftChild(CurEntry
))
472 RtlInsertAsLeftChild(RtlParent(CurEntry
), NodeEntry
);
476 RtlInsertAsRightChild(RtlParent(CurEntry
), NodeEntry
);
481 RemoveEntryList(&((PTUNNEL_NODE_ENTRY
)CurEntry
)->TimerQueueEntry
);
483 /* free node entry */
484 FsRtlFreeTunnelNode((PTUNNEL_NODE_ENTRY
)CurEntry
, &PoolList
);
486 /* decrement node count */
491 if (LastEntry
== NULL
)
493 /* first entry in tunnel cache */
494 Cache
->Cache
= (struct _RTL_SPLAY_LINKS
*)NodeEntry
;
501 RtlInsertAsLeftChild(LastEntry
, NodeEntry
);
506 RtlInsertAsRightChild(LastEntry
, NodeEntry
);
511 /* initialize entry */
512 KeQuerySystemTime(&NodeEntry
->Time
);
514 NodeEntry
->DirectoryKey
= DirectoryKey
;
515 NodeEntry
->Flags
= (AllocatedFromPool
? TUNNEL_FLAG_POOL
: 0x0);
516 NodeEntry
->Flags
|= (KeyByShortName
? TUNNEL_FLAG_KEY_SHORT_NAME
: 0x0);
520 /* copy short name */
521 NodeEntry
->ShortName
.Length
= ShortName
->Length
;
522 NodeEntry
->ShortName
.MaximumLength
= ShortName
->Length
;
523 NodeEntry
->ShortName
.Buffer
= (LPWSTR
)((ULONG_PTR
)NodeEntry
+ sizeof(TUNNEL_NODE_ENTRY
));
525 RtlMoveMemory(NodeEntry
->ShortName
.Buffer
, ShortName
->Buffer
, ShortName
->Length
);
529 NodeEntry
->ShortName
.Length
= NodeEntry
->ShortName
.MaximumLength
= 0;
530 NodeEntry
->ShortName
.Buffer
= NULL
;
536 NodeEntry
->LongName
.Length
= LongName
->Length
;
537 NodeEntry
->LongName
.MaximumLength
= LongName
->Length
;
538 NodeEntry
->LongName
.Buffer
= (LPWSTR
)((ULONG_PTR
)NodeEntry
+ sizeof(TUNNEL_NODE_ENTRY
) + NodeEntry
->ShortName
.Length
);
540 RtlMoveMemory(NodeEntry
->LongName
.Buffer
, LongName
->Buffer
, LongName
->Length
);
544 NodeEntry
->LongName
.Length
= NodeEntry
->LongName
.MaximumLength
= 0;
545 NodeEntry
->LongName
.Buffer
= NULL
;
548 NodeEntry
->DataLength
= DataLength
;
549 NodeEntry
->Data
= (PVOID
)((ULONG_PTR
)NodeEntry
+ sizeof(TUNNEL_NODE_ENTRY
) + NodeEntry
->ShortName
.Length
+ NodeEntry
->LongName
.Length
);
550 RtlMoveMemory(NodeEntry
->Data
, Data
, DataLength
);
552 /* increment node count */
555 /* insert into list */
556 InsertTailList(&Cache
->TimerQueue
, &NodeEntry
->TimerQueueEntry
);
559 FsRtlPruneTunnelCache(Cache
, &PoolList
);
562 ExReleaseFastMutex(&Cache
->Mutex
);
565 FsRtlEmptyFreePoolList(&PoolList
);
569 * @name FsRtlDeleteKeyFromTunnelCache
577 * @param DirectoryKey
587 FsRtlDeleteKeyFromTunnelCache(IN PTUNNEL Cache
,
588 IN ULONGLONG DirectoryKey
)
590 BOOLEAN Rebalance
= TRUE
;
592 PTUNNEL_NODE_ENTRY CurNode
;
593 PRTL_SPLAY_LINKS CurEntry
, LastEntry
= NULL
, Successors
;
597 /* check if tunnel cache is enabled */
598 if (!TunnelMaxEntries
)
600 /* entries are disabled */
604 /* initialize free pool list */
605 InitializeListHead(&PoolList
);
608 ExAcquireFastMutex(&Cache
->Mutex
);
610 /* Look for the entry */
611 CurEntry
= Cache
->Cache
;
614 CurNode
= CONTAINING_RECORD(CurEntry
, TUNNEL_NODE_ENTRY
, SplayInfo
);
616 if (CurNode
->DirectoryKey
> DirectoryKey
)
618 /* current directory key is bigger */
619 CurEntry
= CurEntry
->LeftChild
;
621 else if (CurNode
->DirectoryKey
< DirectoryKey
)
623 /* if we have already found one suitable, break */
624 if (LastEntry
!= NULL
)
629 /* current directory key is smaller */
630 CurEntry
= CurEntry
->RightChild
;
634 /* save and look for another */
635 LastEntry
= CurEntry
;
636 CurEntry
= CurEntry
->LeftChild
;
641 if (LastEntry
== NULL
)
643 /* release tunnel lock */
644 ExReleaseFastMutex(&Cache
->Mutex
);
649 /* delete any matching key */
652 CurNode
= CONTAINING_RECORD(LastEntry
, TUNNEL_NODE_ENTRY
, SplayInfo
);
654 Successors
= RtlRealSuccessor(LastEntry
);
655 if (CurNode
->DirectoryKey
!= DirectoryKey
)
660 /* remove from tunnel */
661 FsRtlRemoveNodeFromTunnel(Cache
, CurNode
, &PoolList
, &Rebalance
);
662 LastEntry
= Successors
;
664 while (LastEntry
!= NULL
);
666 /* release tunnel lock */
667 ExReleaseFastMutex(&Cache
->Mutex
);
670 FsRtlEmptyFreePoolList(&PoolList
);
674 * @name FsRtlDeleteTunnelCache
689 FsRtlDeleteTunnelCache(IN PTUNNEL Cache
)
691 PLIST_ENTRY Entry
, NextEntry
;
692 PTUNNEL_NODE_ENTRY CurEntry
;
696 /* check if tunnel cache is enabled */
697 if (!TunnelMaxEntries
)
699 /* entries are disabled */
703 /* free all entries */
704 Entry
= Cache
->TimerQueue
.Flink
;
706 while(Entry
!= &Cache
->TimerQueue
)
709 CurEntry
= CONTAINING_RECORD(Entry
, TUNNEL_NODE_ENTRY
, TimerQueueEntry
);
712 NextEntry
= Entry
->Flink
;
714 /* remove entry from list */
715 RemoveEntryList(&CurEntry
->TimerQueueEntry
);
718 FsRtlFreeTunnelNode(CurEntry
, NULL
);
720 /* move to next entry */
726 Cache
->NumEntries
= 0;
727 InitializeListHead(&Cache
->TimerQueue
);
731 * @name FsRtlFindInTunnelCache
739 * @param DirectoryKey
748 * @param KeyByShortName
764 FsRtlFindInTunnelCache(IN PTUNNEL Cache
,
765 IN ULONGLONG DirectoryKey
,
766 IN PUNICODE_STRING Name
,
767 OUT PUNICODE_STRING ShortName
,
768 OUT PUNICODE_STRING LongName
,
769 IN OUT PULONG DataLength
,
773 PTUNNEL_NODE_ENTRY CurEntry
;
780 /* check if tunnel cache is enabled */
781 if (!TunnelMaxEntries
)
783 /* entries are disabled */
787 /* initialize free pool list */
788 InitializeListHead(&PoolList
);
790 /* acquire tunnel lock */
791 ExAcquireFastMutex(&Cache
->Mutex
);
793 /* prune old entries */
794 FsRtlPruneTunnelCache(Cache
, &PoolList
);
796 /* now search cache for existing entries */
797 CurEntry
= (PTUNNEL_NODE_ENTRY
)Cache
->Cache
;
801 /* compare current node */
802 Result
= FsRtlCompareNodeAndKey(CurEntry
, DirectoryKey
, Name
);
806 /* current directory key is bigger */
807 CurEntry
= (PTUNNEL_NODE_ENTRY
)CurEntry
->SplayInfo
.LeftChild
;
813 /* found equal entry */
817 /* current directory key is smaller */
818 CurEntry
= (PTUNNEL_NODE_ENTRY
)CurEntry
->SplayInfo
.RightChild
;
822 if (CurEntry
!= NULL
)
826 /* copy short name */
827 RtlCopyUnicodeString(ShortName
, &CurEntry
->ShortName
);
830 if (LongName
->MaximumLength
< CurEntry
->LongName
.Length
)
832 /* buffer is too small */
833 LongName
->Buffer
= ExAllocatePool(PagedPool
, CurEntry
->LongName
.Length
);
834 if (LongName
->Buffer
)
836 LongName
->Length
= CurEntry
->LongName
.Length
;
837 LongName
->MaximumLength
= CurEntry
->LongName
.MaximumLength
;
838 RtlMoveMemory(LongName
->Buffer
, CurEntry
->LongName
.Buffer
, CurEntry
->LongName
.Length
);
843 /* buffer is big enough */
844 RtlCopyUnicodeString(LongName
, &CurEntry
->LongName
);
848 RtlMoveMemory(Data
, CurEntry
->Data
, CurEntry
->DataLength
);
851 *DataLength
= CurEntry
->DataLength
;
856 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
859 //Status = _SEH2_GetExceptionCode();
865 /* release tunnel lock */
866 ExReleaseFastMutex(&Cache
->Mutex
);
869 FsRtlEmptyFreePoolList(&PoolList
);
875 * @name FsRtlInitializeTunnelCache
890 FsRtlInitializeTunnelCache(IN PTUNNEL Cache
)
894 /* initialize mutex */
895 ExInitializeFastMutex(&Cache
->Mutex
);
897 /* initialize node tree */
900 /* initialize timer list */
901 InitializeListHead(&Cache
->TimerQueue
);
903 /* initialize node count */
904 Cache
->NumEntries
= 0;