test: tunnel.c Source File

Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

# tunnel.c

Go to the documentation of this file.

    00001 /*++
    00002
    00003 Copyright (c) 1995  Microsoft Corporation
    00004
    00005 Module Name:
    00006
    00007     Tunnel.c
    00008
    00009 Abstract:
    00010
    00011     The tunnel package provides a set of routines that allow compatibility
    00012     with applications that rely on filesystems being able to "hold onto"
    00013     file meta-info for a short period of time after deletion/renaming and
    00014     reinstantiating a new directory entry with that meta-info if a
    00015     create/rename occurs to cause a file of that name to appear again in a
    00016     short period of time.
    00017
    00018     Note that this violates POSIX rules. This package should not be used
    00019     on POSIX fileobjects, i.e. fileobjects that have case-sensitive names.
    00020
    00021     Entries are keyed by directory and one of the short/long names. An opaque
    00022     rock of information is also associated (create time, last write time, etc.).
    00023     This is expected to vary on a per-filesystem basis.
    00024
    00025     A TUNNEL variable should be initialized for every volume in the system
    00026     at mount time. Thereafter, each delete/rename-out should add to the tunnel
    00027     and each create/rename-in should read from the tunnel. Each directory
    00028     deletion should also notify the package so that all associated entries can
    00029     be flushed. The package is responsible for cleaning out aged entries.
    00030
    00031     Tunneled information is in the paged pool.
    00032
    00033     Concurrent access to the TUNNEL variable is controlled by this package.
    00034     Callers are responsible for synchronizing access to the FsRtlDeleteTunnelCache
    00035     call.
    00036
    00037     The functions provided in this package are as follows:
    00038
    00039       o  FsRtlInitializeTunnel - Initializes the TUNNEL package (called once per boot)
    00040
    00041       o  FsRtlInitializeTunnelCache - Initializes a TUNNEL structure (called once on mount)
    00042
    00043       o  FsRtlAddToTunnelCache - Adds a new key/value pair to the tunnel
    00044
    00045       o  FsRtlFindInTunnelCache - Finds and returns a key/value from the tunnel
    00046
    00047       o  FsRtlDeleteKeyFromTunnelCache - Deletes all entries with a given
    00048            directory key from the tunnel
    00049
    00050       o  FsRtlDeleteTunnelCache - Deletes a TUNNEL structure
    00051
    00052 Author:
    00053
    00054     Dan Lovinger     [DanLo]    8-Aug-1995
    00055
    00056 Revision History:
    00057
    00058 --*/
    00059
    00060 #include "FsRtlP.h"
    00061
    00062 #ifndef INLINE
    00063 #define INLINE __inline
    00064 #endif
    00065
    00066 //
    00067 //  Registry keys/values for controlling tunneling
    00068 //
    00069
    00070 #define TUNNEL_KEY_NAME           L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\FileSystem"
    00071 #define TUNNEL_AGE_VALUE_NAME     L"MaximumTunnelEntryAgeInSeconds"
    00072 #define TUNNEL_SIZE_VALUE_NAME    L"MaximumTunnelEntries"
    00073 #define KEY_WORK_AREA ((sizeof(KEY_VALUE_FULL_INFORMATION) + sizeof(ULONG)) + 64)
    00074
    00075 //
    00076 //  Tunnel expiration paramters (cached once at startup)
    00077 //
    00078
    00079 ULONG   TunnelMaxEntries;
    00080 ULONG   TunnelMaxAge;
    00081
    00082 //
    00083 //  We use a lookaside list to manage the common size tunnel entry. The common size
    00084 //  is contrived to be 128 bytes by adjusting the size we defer for the long name
    00085 //  to 16 characters, which is pretty reasonable. If we ever expect to get more than
    00086 //  a ULONGLONG data element or common names are observed to become larger, adjusting
    00087 //  this may be required.
    00088 //
    00089
    00090 PAGED_LOOKASIDE_LIST    TunnelLookasideList;
    00091 #define MAX_LOOKASIDE_DEPTH     256
    00092
    00093 #define LOOKASIDE_NODE_SIZE     ( sizeof(TUNNEL_NODE) +     \
    00094                                   sizeof(WCHAR)*(8+1+3) +   \
    00095                                   sizeof(WCHAR)*(16) +      \
    00096                                   sizeof(ULONGLONG) )
    00097
    00098 //
    00099 //  Flag bits in the TUNNEL_NODE
    00100 //
    00101
    00102 #define TUNNEL_FLAG_NON_LOOKASIDE    0x1
    00103 #define TUNNEL_FLAG_KEY_SHORT        0x2
    00104
    00105 //
    00106 //  A node of tunneled information in the cache
    00107 //
    00108 //  A TUNNEL is allocated in each VCB and initialized at mount time.
    00109 //
    00110 //  TUNNEL_NODES are then arranged off of the TUNNEL in a splay tree keyed
    00111 //  by DirKey ## Name, where Name is whichever of the names was removed from
    00112 //  the directory (short or long). Each node is also timestamped and inserted
    00113 //  into a timer queue for age expiration.
    00114 //
    00115
    00116 typedef struct {
    00117
    00118     //
    00119     //  Splay links in the Cache tree
    00120     //
    00121
    00122     RTL_SPLAY_LINKS      CacheLinks;
    00123
    00124     //
    00125     //  List links in the timer queue
    00126     //
    00127
    00128     LIST_ENTRY           ListLinks;
    00129
    00130     //
    00131     //  Time this entry was created (for constant time insert)
    00132     //
    00133
    00134     LARGE_INTEGER        CreateTime;
    00135
    00136     //
    00137     //  Directory these names are associated with
    00138     //
    00139
    00140     ULONGLONG            DirKey;
    00141
    00142     //
    00143     //  Flags for the entry
    00144     //
    00145
    00146     ULONG                Flags;
    00147
    00148     //
    00149     //  Long/Short names of the file
    00150     //
    00151
    00152     UNICODE_STRING       LongName;
    00153     UNICODE_STRING       ShortName;
    00154
    00155     //
    00156     //  Opaque tunneled data
    00157     //
    00158
    00159     PVOID                TunnelData;
    00160     ULONG                TunnelDataLength;
    00161
    00162 } TUNNEL_NODE, *PTUNNEL_NODE;
    00163
    00164 //
    00165 //  Internal utility functions
    00166 //
    00167
    00168 NTSTATUS
    00169 FsRtlGetTunnelParameterValue (
    00170     IN PUNICODE_STRING ValueName,
    00171     IN OUT PULONG Value);
    00172
    00173 VOID
    00174 FsRtlPruneTunnelCache (
    00175     IN PTUNNEL Cache,
    00176     IN OUT PLIST_ENTRY FreePoolList);
    00177
    00178 #ifdef ALLOC_PRAGMA
    00179 #pragma alloc_text(PAGE, FsRtlInitializeTunnels)
    00180 #pragma alloc_text(PAGE, FsRtlInitializeTunnelCache)
    00181 #pragma alloc_text(PAGE, FsRtlAddToTunnelCache)
    00182 #pragma alloc_text(PAGE, FsRtlFindInTunnelCache)
    00183 #pragma alloc_text(PAGE, FsRtlDeleteKeyFromTunnelCache)
    00184 #pragma alloc_text(PAGE, FsRtlDeleteTunnelCache)
    00185 #pragma alloc_text(PAGE, FsRtlPruneTunnelCache)
    00186 #pragma alloc_text(PAGE, FsRtlGetTunnelParameterValue)
    00187 #endif
    00188
    00189 //
    00190 //  Testing and usermode rig support. Define TUNNELTEST to get verbose debugger
    00191 //  output on various operations. Define USERTEST to transform the code into
    00192 //  a form which can be compiled in usermode for more efficient debugging.
    00193 //
    00194
    00195 #if defined(TUNNELTEST) || defined(KEYVIEW)
    00196 VOID DumpUnicodeString(UNICODE_STRING *s);
    00197 VOID DumpNode( TUNNEL_NODE *Node, ULONG Indent );
    00198 VOID DumpTunnel( TUNNEL *Tunnel );
    00199 #define DblHex64(a) (ULONG)((a >> 32) & 0xffffffff),(ULONG)(a & 0xffffffff)
    00200 #endif // TUNNELTEST
    00201
    00202 #ifdef USERTEST
    00203 #include <stdio.h>
    00204 #undef KeQuerySystemTime
    00205 #define KeQuerySystemTime NtQuerySystemTime
    00206 #undef ExInitializeFastMutex
    00207 #define ExInitializeFastMutex(arg)
    00208 #define ExAcquireFastMutex(arg)
    00209 #define ExReleaseFastMutex(arg)
    00210 #define DbgPrint printf
    00211 #undef PAGED_CODE
    00212 #define PAGED_CODE()
    00213 #endif
    00214
    00215
    00216 INLINE
    00217 LONG
    00218 FsRtlCompareNodeAndKey (
    00219     TUNNEL_NODE *Node,
    00220     ULONGLONG DirectoryKey,
    00221     PUNICODE_STRING Name
    00222     )
    00223 /*++
    00224
    00225 Routine Description:
    00226
    00227     Compare a tunnel node with a key/name pair
    00228
    00229 Arguments:
    00230
    00231     Node              - a tunnel node
    00232
    00233     DirectoryKey      - a key value
    00234
    00235     Name              - a filename
    00236
    00237 Return Value:
    00238
    00239     Signed comparison result
    00240
    00241 --*/
    00242
    00243 {
    00244     return  (Node->DirKey > DirectoryKey ?  1 :
    00245             (Node->DirKey < DirectoryKey ? -1 :
    00246             RtlCompareUnicodeString((FlagOn(Node->Flags, TUNNEL_FLAG_KEY_SHORT) ?
    00247                                         &Node->ShortName : &Node->LongName),
    00248                                     Name,
    00249                                     TRUE)));
    00250 }
    00251
    00252
    00253 INLINE
    00254 VOID
    00255 FsRtlFreeTunnelNode (
    00256     PTUNNEL_NODE Node,
    00257     PLIST_ENTRY FreePoolList OPTIONAL
    00258     )
    00259 /*++
    00260
    00261 Routine Description:
    00262
    00263     Free a node
    00264
    00265 Arguments:
    00266
    00267     Node            - a tunnel node to free
    00268
    00269     FreePoolList    - optional list to hold freeable pool memory
    00270
    00271 Return Value:
    00272
    00273     None
    00274
    00275 -*/
    00276 {
    00277     if (FreePoolList) {
    00278
    00279         InsertHeadList(FreePoolList, &Node->ListLinks);
    00280
    00281     } else {
    00282
    00283         if (FlagOn(Node->Flags, TUNNEL_FLAG_NON_LOOKASIDE)) {
    00284
    00285             ExFreePool(Node);
    00286
    00287         } else {
    00288
    00289             ExFreeToPagedLookasideList(&TunnelLookasideList, Node);
    00290         }
    00291     }
    00292 }
    00293
    00294
    00295 INLINE
    00296 VOID
    00297 FsRtlEmptyFreePoolList (
    00298     PLIST_ENTRY FreePoolList
    00299     )
    00300 /*++
    00301
    00302 Routine Description:
    00303
    00304     Free all pool memory that has been delayed onto a free list.
    00305
    00306 Arguments:
    00307
    00308     FreePoolList    - a list of freeable pool memory
    00309
    00310 Return Value:
    00311
    00312     None
    00313
    00314 -*/
    00315 {
    00316     PTUNNEL_NODE FreeNode;
    00317
    00318     while (!IsListEmpty(FreePoolList)) {
    00319
    00320         FreeNode = CONTAINING_RECORD(FreePoolList->Flink, TUNNEL_NODE, ListLinks);
    00321         RemoveEntryList(FreePoolList->Flink);
    00322
    00323         FsRtlFreeTunnelNode(FreeNode, NULL);
    00324     }
    00325 }
    00326
    00327
    00328 INLINE
    00329 VOID
    00330 FsRtlRemoveNodeFromTunnel (
    00331     IN PTUNNEL Cache,
    00332     IN PTUNNEL_NODE Node,
    00333     IN PLIST_ENTRY FreePoolList,
    00334     IN PBOOLEAN Splay OPTIONAL
    00335     )
    00336 /*++
    00337
    00338 Routine Description:
    00339
    00340     Performs the common work of deleting a node from a tunnel cache. Pool memory
    00341     is not deleted immediately but is saved aside on a list for deletion later
    00342     by the calling routine.
    00343
    00344 Arguments:
    00345
    00346     Cache - the tunnel cache the node is in
    00347
    00348     Node - the node being removed
    00349
    00350     FreePoolList - an initialized list to take the node if it was allocated from
    00351         pool
    00352
    00353     Splay - an optional flag to indicate whether the tree should be splayed on
    00354         the delete. Set to FALSE if splaying was performed.
    00355
    00356 Return Value:
    00357
    00358     None.
    00359
    00360 --*/
    00361 {
    00362     if (Splay && *Splay) {
    00363
    00364         Cache->Cache = RtlDelete(&Node->CacheLinks);
    00365
    00366         *Splay = FALSE;
    00367
    00368     } else {
    00369
    00370         RtlDeleteNoSplay(&Node->CacheLinks, &Cache->Cache);
    00371     }
    00372
    00373     RemoveEntryList(&Node->ListLinks);
    00374
    00375     Cache->NumEntries--;
    00376
    00377     FsRtlFreeTunnelNode(Node, FreePoolList);
    00378 }
    00379
    00380
    00381 VOID
    00382 FsRtlInitializeTunnels (
    00383     VOID
    00384     )
    00385 /*++
    00386
    00387 Routine Description:
    00388
    00389     Initializes the global part of the tunneling package.
    00390
    00391 Arguments:
    00392
    00393     None
    00394
    00395 Return Value:
    00396
    00397     None
    00398
    00399 --*/
    00400 {
    00401     UNICODE_STRING  ValueName;
    00402     USHORT          LookasideDepth;
    00403
    00404     PAGED_CODE();
    00405
    00406     if (MmIsThisAnNtAsSystem()) {
    00407
    00408         TunnelMaxEntries = 1024;
    00409
    00410     } else {
    00411
    00412         TunnelMaxEntries = 256;
    00413     }
    00414
    00415     TunnelMaxAge = 15;
    00416
    00417     //
    00418     //  Query our configurable parameters
    00419     //
    00420     //  Don't worry about failure in retrieving from the registry. We've gotten
    00421     //  this far so fall back on defaults even if there was a problem with resources.
    00422     //
    00423
    00424     ValueName.Buffer = TUNNEL_SIZE_VALUE_NAME;
    00425     ValueName.Length = sizeof(TUNNEL_SIZE_VALUE_NAME) - sizeof(WCHAR);
    00426     ValueName.MaximumLength = sizeof(TUNNEL_SIZE_VALUE_NAME);
    00427     (VOID) FsRtlGetTunnelParameterValue(&ValueName, &TunnelMaxEntries);
    00428
    00429     ValueName.Buffer = TUNNEL_AGE_VALUE_NAME;
    00430     ValueName.Length = sizeof(TUNNEL_AGE_VALUE_NAME) - sizeof(WCHAR);
    00431     ValueName.MaximumLength = sizeof(TUNNEL_AGE_VALUE_NAME);
    00432     (VOID) FsRtlGetTunnelParameterValue(&ValueName, &TunnelMaxAge);
    00433
    00434     if (TunnelMaxAge == 0) {
    00435
    00436         //
    00437         //  If the registry has been set so the timeout is zero, we should force
    00438         //  the number of entries to zero also. This preserves expectations and lets
    00439         //  us key off of max entries alone in performing the hard disabling of the
    00440         //  caching code.
    00441         //
    00442
    00443         TunnelMaxEntries = 0;
    00444     }
    00445
    00446     //
    00447     //  Convert from seconds to 10ths of msecs, the internal resolution
    00448     //
    00449
    00450     TunnelMaxAge *= 10000000;
    00451
    00452     //
    00453     //  Build the lookaside list for common node allocation
    00454     //
    00455
    00456     if (TunnelMaxEntries > MAXUSHORT) {
    00457
    00458         //
    00459         //  User is hinting a big need to us
    00460         //
    00461
    00462         LookasideDepth = MAX_LOOKASIDE_DEPTH;
    00463
    00464     } else {
    00465
    00466         LookasideDepth = ((USHORT)TunnelMaxEntries)/16;
    00467     }
    00468
    00469     if (LookasideDepth == 0 && TunnelMaxEntries) {
    00470
    00471         //
    00472         //  Miniscule number of entries allowed. Lookaside 'em all.
    00473         //
    00474
    00475         LookasideDepth = (USHORT)TunnelMaxEntries + 1;
    00476     }
    00477
    00478     if (LookasideDepth > MAX_LOOKASIDE_DEPTH) {
    00479
    00480         //
    00481         //  Finally, restrict the depth to something reasonable.
    00482         //
    00483
    00484         LookasideDepth = MAX_LOOKASIDE_DEPTH;
    00485     }
    00486
    00487     ExInitializePagedLookasideList( &TunnelLookasideList,
    00488                                     NULL,
    00489                                     NULL,
    00490                                     0,
    00491                                     LOOKASIDE_NODE_SIZE,
    00492                                     'LnuT',
    00493                                     LookasideDepth );
    00494
    00495     return;
    00496 }
    00497
    00498
    00499 //
    00500 //  *** SPEC
    00501 //
    00502 //    FsRtlInitializeTunnelCache - Initialize a tunneling cache for a volume
    00503 //
    00504 //    FsRtlInitializeTunnelCache will allocate a default cache (resizing policy is common
    00505 //    to all file systems) and initialize it to be empty.  File systems will store a pointer to
    00506 //    this cache in their per-volume structures.
    00507 //
    00508 //    Information is retained in the tunnel cache for a fixed period of time.  MarkZ would
    00509 //    assume that a value of 10 seconds would satisfy the vast majority of situations.  This
    00510 //    could be controlled by the registry or could be a compilation constant.
    00511 //
    00512 //  Change: W95 times out at 15 seconds. Would be a registry value initialized at tunnel
    00513 //  creation time, with a proposed default of 15 seconds.
    00514 //
    00515
    00516 VOID
    00517 FsRtlInitializeTunnelCache (
    00518     IN PTUNNEL Cache
    00519     )
    00520 /*++
    00521
    00522 Routine Description:
    00523
    00524     Initialize a new tunnel cache.
    00525
    00526 Arguments:
    00527
    00528     None
    00529
    00530 Return Value:
    00531
    00532     None
    00533
    00534 --*/
    00535 {
    00536     PAGED_CODE();
    00537
    00538     ExInitializeFastMutex(&Cache->Mutex);
    00539
    00540     Cache->Cache = NULL;
    00541     InitializeListHead(&Cache->TimerQueue);
    00542     Cache->NumEntries = 0;
    00543
    00544     return;
    00545 }
    00546
    00547
    00548 //
    00549 //  *** SPEC
    00550 //
    00551 //    FsRtlAddToTunnelCache - add information to a tunnel cache
    00552 //
    00553 //    FsRtlAddToTunnelCache is called by file systems when a name disappears from a
    00554 //    directory.  This typically occurs in both the delete and the rename paths.  When
    00555 //    a name is deleted, all information needed to be cached is extracted from the file
    00556 //    and passed in a single buffer.  This information is stored keyed by the directory key
    00557 //    (a ULONG that is unique to the directory) and the short-name of the file.
    00558 //
    00559 //    The caller is required to synchronize this call against FsRtlDeleteTunnelCache.
    00560 //
    00561 //    Arguments:
    00562 //        Cache        pointer to cache initialized by FsRtlInitializeTunnelCache
    00563 //        DirectoryKey    ULONG unique ID of the directory containing the deleted file
    00564 //        ShortName    UNICODE_STRING* short (8.3) name of the file
    00565 //        LongName    UNICODE_STRING* full name of the file
    00566 //        DataLength    ULONG length of data to be cached with these names
    00567 //        Data        VOID* data that will be cached.
    00568 //
    00569 //    It is acceptable for the Cache to ignore this request based upon memory constraints.
    00570 //
    00571 //  Change: W95 maintains 10 items in the tunnel cache. Since we are a potential server
    00572 //  this should be much higher. The max count would be initialized from the registry with
    00573 //  a proposed default of 1024. Adds which run into the limit would cause least recently
    00574 //  inserted recycling (i.e., off of the top of the timer queue).
    00575 //
    00576 //  Change: Key should be by the name removed, not neccesarily the short name. If a long name
    00577 //  is removed, it would be incorrect to miss the tunnel. Use KeyByShortName boolean to specify
    00578 //  which.
    00579 //
    00580 //  Change: Specify that Data, ShortName, and LongName are copied for storage.
    00581 //
    00582
    00583 VOID
    00584 FsRtlAddToTunnelCache (
    00585     IN PTUNNEL Cache,
    00586     IN ULONGLONG DirKey,
    00587     IN PUNICODE_STRING ShortName,
    00588     IN PUNICODE_STRING LongName,
    00589     IN BOOLEAN KeyByShortName,
    00590     IN ULONG DataLength,
    00591     IN PVOID Data
    00592     )
    00593 /*++
    00594
    00595 Routine Description:
    00596
    00597     Adds an entry to the tunnel cache keyed by
    00598
    00599         DirectoryKey ## (KeyByShortName ? ShortName : LongName)
    00600
    00601     ShortName, LongName, and Data are copied and stored in the tunnel. As a side
    00602     effect, if there are too many entries in the tunnel cache, this routine will
    00603     initiate expiration in the tunnel cache.
    00604
    00605 Arguments:
    00606
    00607     Cache - a tunnel cache initialized by FsRtlInitializeTunnelCache()
    00608
    00609     DirKey - the key value of the directory the name appeared in
    00610
    00611     ShortName - (optional if !KeyByShortName) the 8.3 name of the file
    00612
    00613     LongName - (optional if KeyByShortName) the long name of the file
    00614
    00615     KeyByShortName - specifies which name is keyed in the tunnel cache
    00616
    00617     DataLength - specifies the length of the opaque data segment (file
    00618     system specific) which contains the tunnelling information for this
    00619     file
    00620
    00621     Data - pointer to the opaque tunneling data segment
    00622
    00623 Return Value:
    00624
    00625     None
    00626
    00627 --*/
    00628 {
    00629     LONG Compare;
    00630     ULONG NodeSize;
    00631     PUNICODE_STRING NameKey;
    00632     PRTL_SPLAY_LINKS *Links;
    00633     LIST_ENTRY FreePoolList;
    00634
    00635     PTUNNEL_NODE Node = NULL;
    00636     PTUNNEL_NODE NewNode = NULL;
    00637     BOOLEAN FreeOldNode = FALSE;
    00638     BOOLEAN AllocatedFromPool = FALSE;
    00639
    00640     PAGED_CODE();
    00641
    00642     //
    00643     //  If MaxEntries is 0 then tunneling is disabled.
    00644     //
    00645
    00646     if (TunnelMaxEntries == 0) return;
    00647
    00648     InitializeListHead(&FreePoolList);
    00649
    00650     //
    00651     //  Grab a new node for this data
    00652     //
    00653
    00654     NodeSize = sizeof(TUNNEL_NODE) + ShortName->Length + LongName->Length + DataLength;
    00655
    00656     if (LOOKASIDE_NODE_SIZE >= NodeSize) {
    00657
    00658         NewNode = ExAllocateFromPagedLookasideList(&TunnelLookasideList);
    00659     }
    00660
    00661     if (NewNode == NULL) {
    00662
    00663         //
    00664         //  Data doesn't fit in lookaside nodes
    00665         //
    00666
    00667         NewNode = ExAllocatePoolWithTag(PagedPool, NodeSize, 'PnuT');
    00668
    00669         if (NewNode == NULL) {
    00670
    00671             //
    00672             //  Give up tunneling this entry
    00673             //
    00674
    00675             return;
    00676         }
    00677
    00678         AllocatedFromPool = TRUE;
    00679     }
    00680
    00681     //
    00682     //  Traverse the cache to find our insertion point
    00683     //
    00684
    00685     NameKey = (KeyByShortName ? ShortName : LongName);
    00686
    00687     ExAcquireFastMutex(&Cache->Mutex);
    00688
    00689     Links = &Cache->Cache;
    00690
    00691     while (*Links) {
    00692
    00693         Node = CONTAINING_RECORD(*Links, TUNNEL_NODE, CacheLinks);
    00694
    00695         Compare = FsRtlCompareNodeAndKey(Node, DirKey, NameKey);
    00696
    00697         if (Compare > 0) {
    00698
    00699             Links = &RtlLeftChild(&Node->CacheLinks);
    00700
    00701         } else {
    00702
    00703             if (Compare < 0) {
    00704
    00705                 Links = &RtlRightChild(&Node->CacheLinks);
    00706
    00707             } else {
    00708
    00709                 break;
    00710             }
    00711         }
    00712     }
    00713
    00714     //
    00715     //  Thread new data into the splay tree
    00716     //
    00717
    00718     RtlInitializeSplayLinks(&NewNode->CacheLinks);
    00719
    00720     if (Node) {
    00721
    00722         //
    00723         //  Not inserting first node in tree
    00724         //
    00725
    00726         if (*Links) {
    00727
    00728             //
    00729             //  Entry exists in the cache, so replace by swapping all splay links
    00730             //
    00731
    00732             RtlRightChild(&NewNode->CacheLinks) = RtlRightChild(*Links);
    00733             RtlLeftChild(&NewNode->CacheLinks) = RtlLeftChild(*Links);
    00734
    00735             if (RtlRightChild(*Links)) RtlParent(RtlRightChild(*Links)) = &NewNode->CacheLinks;
    00736             if (RtlLeftChild(*Links)) RtlParent(RtlLeftChild(*Links)) = &NewNode->CacheLinks;
    00737
    00738             if (!RtlIsRoot(*Links)) {
    00739
    00740                 //
    00741                 //  Change over the parent links. Note that we've messed with *Links now
    00742                 //  since it is pointing at the parent member.
    00743                 //
    00744
    00745                 RtlParent(&NewNode->CacheLinks) = RtlParent(*Links);
    00746
    00747                 if (RtlIsLeftChild(*Links)) {
    00748
    00749                     RtlLeftChild(RtlParent(*Links)) = &NewNode->CacheLinks;
    00750
    00751                 } else {
    00752
    00753                     RtlRightChild(RtlParent(*Links)) = &NewNode->CacheLinks;
    00754                 }
    00755
    00756             } else {
    00757
    00758                 //
    00759                 //  Set root of the cache
    00760                 //
    00761
    00762                 Cache->Cache = &NewNode->CacheLinks;
    00763             }
    00764
    00765             //
    00766             //  Free old node
    00767             //
    00768
    00769             RemoveEntryList(&Node->ListLinks);
    00770
    00771             FsRtlFreeTunnelNode(Node, &FreePoolList);
    00772
    00773             Cache->NumEntries--;
    00774
    00775         } else {
    00776
    00777             //
    00778             //  Simple insertion as a leaf
    00779             //
    00780
    00781             NewNode->CacheLinks.Parent = &Node->CacheLinks;
    00782             *Links = &NewNode->CacheLinks;
    00783         }
    00784
    00785     } else {
    00786
    00787         Cache->Cache = &NewNode->CacheLinks;
    00788     }
    00789
    00790     //
    00791     //  Thread onto the timer list
    00792     //
    00793
    00794     KeQuerySystemTime(&NewNode->CreateTime);
    00795     InsertTailList(&Cache->TimerQueue, &NewNode->ListLinks);
    00796
    00797     Cache->NumEntries++;
    00798
    00799     //
    00800     //  Stash tunneling information
    00801     //
    00802
    00803     NewNode->DirKey = DirKey;
    00804
    00805     if (KeyByShortName) {
    00806
    00807         NewNode->Flags = TUNNEL_FLAG_KEY_SHORT;
    00808
    00809     } else {
    00810
    00811         NewNode->Flags = 0;
    00812     }
    00813
    00814     //
    00815     //  Initialize the internal UNICODE_STRINGS to point at the buffer segments. For various
    00816     //  reasons (UNICODE APIs are incomplete, we're avoiding calling any allocate routine more
    00817     //  than once, UNICODE strings are not guaranteed to be null terminated) we have to do a lot
    00818     //  of this by hand.
    00819     //
    00820     //  The data is layed out like this in the allocated block:
    00821     //
    00822     //  -----------------------------------------------------------------------------------
    00823     //  | TUNNEL_NODE | Node->ShortName.Buffer | Node->LongName.Buffer | Node->TunnelData |
    00824     //  -----------------------------------------------------------------------------------
    00825     //
    00826
    00827     NewNode->ShortName.Buffer = (PWCHAR)((PCHAR)NewNode + sizeof(TUNNEL_NODE));
    00828     NewNode->LongName.Buffer = (PWCHAR)((PCHAR)NewNode + sizeof(TUNNEL_NODE) + ShortName->Length);
    00829
    00830     NewNode->ShortName.Length = NewNode->ShortName.MaximumLength = ShortName->Length;
    00831     NewNode->LongName.Length = NewNode->LongName.MaximumLength = LongName->Length;
    00832
    00833     if (ShortName->Length) {
    00834
    00835         RtlCopyMemory(NewNode->ShortName.Buffer, ShortName->Buffer, ShortName->Length);
    00836     }
    00837
    00838     if (LongName->Length) {
    00839
    00840         RtlCopyMemory(NewNode->LongName.Buffer, LongName->Buffer, LongName->Length);
    00841     }
    00842
    00843     NewNode->TunnelData = (PVOID)((PCHAR)NewNode + sizeof(TUNNEL_NODE) + ShortName->Length + LongName->Length);
    00844
    00845     NewNode->TunnelDataLength = DataLength;
    00846
    00847     RtlCopyMemory(NewNode->TunnelData, Data, DataLength);
    00848
    00849     if (AllocatedFromPool) {
    00850
    00851         SetFlag(NewNode->Flags, TUNNEL_FLAG_NON_LOOKASIDE);
    00852     }
    00853
    00854 #if defined(TUNNELTEST) || defined (KEYVIEW)
    00855     DbgPrint("FsRtlAddToTunnelCache:\n");
    00856     DumpNode(NewNode, 1);
    00857 #ifndef KEYVIEW
    00858     DumpTunnel(Cache);
    00859 #endif
    00860 #endif // TUNNELTEST
    00861
    00862     //
    00863     //  Clean out the cache, release, and then drop any pool memory we need to
    00864     //
    00865
    00866     FsRtlPruneTunnelCache(Cache, &FreePoolList);
    00867
    00868     ExReleaseFastMutex(&Cache->Mutex);
    00869
    00870     FsRtlEmptyFreePoolList(&FreePoolList);
    00871
    00872     return;
    00873 }
    00874
    00875
    00876 //
    00877 //  *** SPEC
    00878 //
    00879 //    FsRtlFindInTunnelCache - retrieve information from tunnel cache
    00880 //
    00881 //    FsRtlFindInTunnelCache consults the cache to see if an entry with the same
    00882 //    DirectoryKey and ShortName exist.  If so, it returns the data associated with the
    00883 //    cache entry.  The entry may or may not be freed from the cache.  Information that is
    00884 //    stale but not yet purged (older than the retention threshold but not yet cleaned out)
    00885 //    may be returned.
    00886 //
    00887 //    File systems call FsRtlFindInTunnel cache in the create path when a new file is
    00888 //    being created and in the rename path when a new name is appearing in a directory.
    00889 //
    00890 //    The caller is required to synchronize this call against FsRtlDeleteTunnelCache.
    00891 //
    00892 //    Arguments:
    00893 //        Cache        a tunnel cache initialized by FsRtlInitializeTunnelCache()
    00894 //        DirectoryKey    ULONG unique ID of the directory where a name is appearing
    00895 //        Name        UNICODE_STRING* name that is being created
    00896 //        DataLength     in length of buffer, out returned length of data found
    00897 //        Data        pointer to buffer
    00898 //
    00899 //    Returns:
    00900 //        TRUE iff a matching DirectoryKey/Name pair are found, FALSE otherwise
    00901 //
    00902 //  Change: Add out parameters ShortName and LongName to capture the file naming information.
    00903 //  Plus: this avoids the need for marshalling/unmarshalling steps for the current desired use of
    00904 //  this code since otherwise we'd have variable length unaligned structures to contain the
    00905 //  strings along with the other meta-info.
    00906 //  Minus: Possibly a bad precedent.
    00907 //
    00908 //  Change: spec reads "may or may not be freed from cache" on a hit. This complicates unwinding
    00909 //  from aborted operations. Data will not be freed on a hit, but will expire like normal entries.
    00910 //
    00911
    00912 BOOLEAN
    00913 FsRtlFindInTunnelCache (
    00914     IN PTUNNEL Cache,
    00915     IN ULONGLONG DirKey,
    00916     IN PUNICODE_STRING Name,
    00917     OUT PUNICODE_STRING ShortName,
    00918     OUT PUNICODE_STRING LongName,
    00919     IN OUT PULONG  DataLength,
    00920     OUT PVOID Data
    00921     )
    00922 /*++
    00923
    00924 Routine Description:
    00925
    00926     Looks up the key
    00927
    00928         DirKey ## Name
    00929
    00930     in the tunnel cache and removes it. As a side effect, this routine will initiate
    00931     expiration of the aged entries in the tunnel cache.
    00932
    00933 Arguments:
    00934
    00935     Cache - a tunnel cache initialized by FsRtlInitializeTunnelCache()
    00936
    00937     DirKey - the key value of the directory the name will appear in
    00938
    00939     Name - the name of the entry
    00940
    00941     ShortName - return string to hold the short name of the tunneled file. Must
    00942         already be allocated and large enough for max 8.3 name
    00943
    00944     LongName -  return string to hold the long name of the tunneled file. If
    00945         already allocated, may be grown if not large enough. Caller is
    00946         responsible for noticing this and freeing data regardless of return value.
    00947
    00948     DataLength - provides the length of the buffer avaliable to hold the
    00949         tunneling information, returns the size of the tunneled information
    00950         read out
    00951
    00952 Return Value:
    00953
    00954     Boolean true if found, false otherwise
    00955
    00956 --*/
    00957 {
    00958     PRTL_SPLAY_LINKS Links;
    00959     PTUNNEL_NODE Node;
    00960     LONG Compare;
    00961     LIST_ENTRY FreePoolList;
    00962
    00963     BOOLEAN Status = FALSE;
    00964
    00965     PAGED_CODE();
    00966
    00967     //
    00968     //  If MaxEntries is 0 then tunneling is disabled.
    00969     //
    00970
    00971     if (TunnelMaxEntries == 0) return FALSE;
    00972
    00973     InitializeListHead(&FreePoolList);
    00974
    00975 #ifdef KEYVIEW
    00976     DbgPrint("++\nSearching for %wZ , %08x%08x\n--\n", Name, DblHex64(DirKey));
    00977 #endif
    00978
    00979     ExAcquireFastMutex(&Cache->Mutex);
    00980
    00981     //
    00982     //  Expire aged entries first so we don't grab old data
    00983     //
    00984
    00985     FsRtlPruneTunnelCache(Cache, &FreePoolList);
    00986
    00987     Links = Cache->Cache;
    00988
    00989     while (Links) {
    00990
    00991         Node = CONTAINING_RECORD(Links, TUNNEL_NODE, CacheLinks);
    00992
    00993         Compare = FsRtlCompareNodeAndKey(Node, DirKey, Name);
    00994
    00995         if (Compare > 0) {
    00996
    00997             Links = RtlLeftChild(&Node->CacheLinks);
    00998
    00999         } else {
    01000
    01001             if (Compare < 0) {
    01002
    01003                 Links = RtlRightChild(&Node->CacheLinks);
    01004
    01005             } else {
    01006
    01007                 //
    01008                 //  Found tunneling information
    01009                 //
    01010
    01011 #if defined(TUNNELTEST) || defined(KEYVIEW)
    01012                 DbgPrint("FsRtlFindInTunnelCache:\n");
    01013                 DumpNode(Node, 1);
    01014 #ifndef KEYVIEW
    01015                 DumpTunnel(Cache);
    01016 #endif
    01017 #endif // TUNNELTEST
    01018
    01019                 break;
    01020             }
    01021         }
    01022     }
    01023
    01024     try {
    01025
    01026         if (Links) {
    01027
    01028             //
    01029             //  Copy node data into caller's area
    01030             //
    01031
    01032             ASSERT(ShortName->MaximumLength >= (8+1+3)*sizeof(WCHAR));
    01033             RtlCopyUnicodeString(ShortName, &Node->ShortName);
    01034
    01035             if (LongName->MaximumLength >= Node->LongName.Length) {
    01036
    01037                 RtlCopyUnicodeString(LongName, &Node->LongName);
    01038
    01039             } else {
    01040
    01041                 //
    01042                 //  Need to allocate more memory for the long name
    01043                 //
    01044
    01045                 LongName->Buffer = FsRtlAllocatePoolWithTag(PagedPool, Node->LongName.Length, '4nuT');
    01046                 LongName->Length = LongName->MaximumLength = Node->LongName.Length;
    01047
    01048                 RtlCopyMemory(LongName->Buffer, Node->LongName.Buffer, Node->LongName.Length);
    01049             }
    01050
    01051             ASSERT(*DataLength >= Node->TunnelDataLength);
    01052             RtlCopyMemory(Data, Node->TunnelData, Node->TunnelDataLength);
    01053             *DataLength = Node->TunnelDataLength;
    01054
    01055             Status = TRUE;
    01056         }
    01057
    01058     } finally {
    01059
    01060         ExReleaseFastMutex(&Cache->Mutex);
    01061
    01062         FsRtlEmptyFreePoolList(&FreePoolList);
    01063     }
    01064
    01065     return Status;
    01066 }
    01067
    01068
    01069 //
    01070 //  *** SPEC
    01071 //
    01072 //    FsRtlDeleteKeyFromTunnelCache - delete all cached information associated with
    01073 //    a DirectoryKey
    01074 //
    01075 //    When file systems delete a directory, all cached information relating to that directory
    01076 //    must be purged.  File systems call FsRtlDeleteKeyFromTunnelCache in the rmdir path.
    01077 //
    01078 //    The caller is required to synchronize this call against FsRtlDeleteTunnelCache.
    01079 //
    01080 //    Arguments:
    01081 //        Cache        a tunnel cache initialized by FsRtlInitializeTunnelCache()
    01082 //        DirectoryKey    ULONGLONG unique ID of the directory that is being deleted
    01083 //
    01084
    01085 VOID
    01086 FsRtlDeleteKeyFromTunnelCache (
    01087     IN PTUNNEL Cache,
    01088     IN ULONGLONG DirKey
    01089     )
    01090 /*++
    01091
    01092 Routine Description:
    01093
    01094     Deletes all entries in the cache associated with a specific directory
    01095
    01096 Arguments:
    01097
    01098     Cache - a tunnel cache initialized by FsRtlInitializeTunnelCache()
    01099
    01100     DirKey - the key value of the directory (presumeably being removed)
    01101
    01102 Return Value:
    01103
    01104     None
    01105
    01106 --*/
    01107 {
    01108     PRTL_SPLAY_LINKS Links;
    01109     PRTL_SPLAY_LINKS SuccessorLinks;
    01110     PTUNNEL_NODE Node;
    01111     LIST_ENTRY FreePoolList;
    01112
    01113     PRTL_SPLAY_LINKS LastLinks = NULL;
    01114     BOOLEAN Splay = TRUE;
    01115
    01116     PAGED_CODE();
    01117
    01118     //
    01119     //  If MaxEntries is 0 then tunneling is disabled.
    01120     //
    01121
    01122     if (TunnelMaxEntries == 0) return;
    01123
    01124     InitializeListHead(&FreePoolList);
    01125
    01126 #ifdef KEYVIEW
    01127     DbgPrint("++\nDeleting key %08x%08x\n--\n", DblHex64(DirKey));
    01128 #endif
    01129
    01130     ExAcquireFastMutex(&Cache->Mutex);
    01131
    01132     Links = Cache->Cache;
    01133
    01134     while (Links) {
    01135
    01136         Node = CONTAINING_RECORD(Links, TUNNEL_NODE, CacheLinks);
    01137
    01138         if (Node->DirKey > DirKey) {
    01139
    01140             //
    01141             //  All nodes to the right are bigger, go left
    01142             //
    01143
    01144             Links = RtlLeftChild(&Node->CacheLinks);
    01145
    01146         } else {
    01147
    01148             if (Node->DirKey < DirKey) {
    01149
    01150                 if (LastLinks) {
    01151
    01152                     //
    01153                     //  If we have previously seen a candidate node to delete
    01154                     //  and we have now gone too far left - we know where to start.
    01155                     //
    01156
    01157                     break;
    01158                 }
    01159
    01160                 Links = RtlRightChild(&Node->CacheLinks);
    01161
    01162             } else {
    01163
    01164                 //
    01165                 //  Node is a candidate to be deleted, but we might have more nodes
    01166                 //  to the left in the tree. Note this location and go on.
    01167                 //
    01168
    01169                 LastLinks = Links;
    01170                 Links = RtlLeftChild(&Node->CacheLinks);
    01171             }
    01172         }
    01173     }
    01174
    01175     for (Links = LastLinks;
    01176          Links;
    01177          Links = SuccessorLinks) {
    01178
    01179         SuccessorLinks = RtlRealSuccessor(Links);
    01180         Node = CONTAINING_RECORD(Links, TUNNEL_NODE, CacheLinks);
    01181
    01182         if (Node->DirKey != DirKey) {
    01183
    01184             //
    01185             //  Reached nodes which have a different key, so we're done
    01186             //
    01187
    01188             break;
    01189         }
    01190
    01191         FsRtlRemoveNodeFromTunnel(Cache, Node, &FreePoolList, &Splay);
    01192     }
    01193
    01194 #ifdef TUNNELTEST
    01195     DbgPrint("FsRtlDeleteKeyFromTunnelCache:\n");
    01196 #ifndef KEYVIEW
    01197     DumpTunnel(Cache);
    01198 #endif
    01199 #endif // TUNNELTEST
    01200
    01201     ExReleaseFastMutex(&Cache->Mutex);
    01202
    01203     //
    01204     //  Free delayed pool
    01205     //
    01206
    01207     FsRtlEmptyFreePoolList(&FreePoolList);
    01208
    01209     return;
    01210 }
    01211
    01212
    01213 //
    01214 //  *** SPEC
    01215 //
    01216 //    FsRtlDeleteTunnelCache - free a tunnel cache
    01217 //
    01218 //    FsRtlDeleteTunnelCache deletes all cached information.  The Cache is no longer
    01219 //    valid.
    01220 //
    01221 //    Arguments:
    01222 //        Cache        a tunnel cache initialized by FsRtlInitializeTunnelCache()
    01223 //
    01224
    01225 VOID
    01226 FsRtlDeleteTunnelCache (
    01227     IN PTUNNEL Cache
    01228     )
    01229 /*++
    01230
    01231 Routine Description:
    01232
    01233     Deletes a tunnel cache
    01234
    01235 Arguments:
    01236
    01237     Cache - the cache to delete, initialized by FsRtlInitializeTunnelCache()
    01238
    01239 Return Value:
    01240
    01241     None
    01242
    01243 --*/
    01244 {
    01245     PTUNNEL_NODE Node;
    01246     PLIST_ENTRY Link, Next;
    01247
    01248     PAGED_CODE();
    01249
    01250     //
    01251     //  If MaxEntries is 0 then tunneling is disabled.
    01252     //
    01253
    01254     if (TunnelMaxEntries == 0) return;
    01255
    01256     //
    01257     //  Zero out the cache and delete everything on the timer list
    01258     //
    01259
    01260     Cache->Cache = NULL;
    01261     Cache->NumEntries = 0;
    01262
    01263     for (Link = Cache->TimerQueue.Flink;
    01264          Link != &Cache->TimerQueue;
    01265          Link = Next) {
    01266
    01267         Next = Link->Flink;
    01268
    01269         Node = CONTAINING_RECORD(Link, TUNNEL_NODE, ListLinks);
    01270
    01271         FsRtlFreeTunnelNode(Node, NULL);
    01272     }
    01273
    01274     InitializeListHead(&Cache->TimerQueue);
    01275
    01276     return;
    01277 }
    01278
    01279
    01280 VOID
    01281 FsRtlPruneTunnelCache (
    01282     IN PTUNNEL Cache,
    01283     IN OUT PLIST_ENTRY FreePoolList
    01284     )
    01285 /*++
    01286
    01287 Routine Description:
    01288
    01289     Removes deadwood entries from the tunnel cache as defined by TunnelMaxAge and TunnelMaxEntries.
    01290     Pool memory is returned on a list for deletion by the calling routine at a time of
    01291     its choosing.
    01292
    01293     For performance reasons we don't want to force freeing of memory inside a mutex.
    01294
    01295 Arguments:
    01296
    01297     Cache - the tunnel cache to prune
    01298
    01299     FreePoolList - a list to queue pool memory on to
    01300
    01301 Return Value:
    01302
    01303     None
    01304 --*/
    01305 {
    01306     PTUNNEL_NODE Node;
    01307     LARGE_INTEGER ExpireTime;
    01308     LARGE_INTEGER CurrentTime;
    01309     BOOLEAN Splay = TRUE;
    01310
    01311     PAGED_CODE();
    01312
    01313     //
    01314     //  Calculate the age of the oldest entry we want to keep
    01315     //
    01316
    01317     KeQuerySystemTime(&CurrentTime);
    01318     ExpireTime.QuadPart = CurrentTime.QuadPart - TunnelMaxAge;
    01319
    01320     //
    01321     //  Expire old entries off of the timer queue.  We have to check
    01322     //  for future time because the clock may jump as a result of
    01323     //  hard clock change.  If we did not do this, a rogue entry
    01324     //  with a future time could sit at the top of the queue and
    01325     //  prevent entries from going away.
    01326     //
    01327
    01328     while (!IsListEmpty(&Cache->TimerQueue)) {
    01329
    01330         Node = CONTAINING_RECORD(Cache->TimerQueue.Flink, TUNNEL_NODE, ListLinks);
    01331
    01332         if (Node->CreateTime.QuadPart < ExpireTime.QuadPart ||
    01333             Node->CreateTime.QuadPart > CurrentTime.QuadPart) {
    01334
    01335 #if defined(TUNNELTEST) || defined(KEYVIEW)
    01336             DbgPrint("Expiring node %x (%ud%ud 1/10 msec too old)\n", Node, DblHex64(ExpireTime.QuadPart - Node->CreateTime.QuadPart));
    01337 #endif // TUNNELTEST
    01338
    01339             FsRtlRemoveNodeFromTunnel(Cache, Node, FreePoolList, &Splay);
    01340
    01341         } else {
    01342
    01343             //
    01344             //  No more nodes to be expired
    01345             //
    01346
    01347             break;
    01348         }
    01349     }
    01350
    01351     //
    01352     //  Remove entries until we're under the TunnelMaxEntries limit
    01353     //
    01354
    01355     while (Cache->NumEntries > TunnelMaxEntries) {
    01356
    01357         Node = CONTAINING_RECORD(Cache->TimerQueue.Flink, TUNNEL_NODE, ListLinks);
    01358
    01359 #if defined(TUNNELTEST) || defined(KEYVIEW)
    01360             DbgPrint("Dumping node %x (%d > %d)\n", Node, Cache->NumEntries, TunnelMaxEntries);
    01361 #endif // TUNNELTEST
    01362
    01363         FsRtlRemoveNodeFromTunnel(Cache, Node, FreePoolList, &Splay);
    01364     }
    01365
    01366     return;
    01367 }
    01368
    01369
    01370 NTSTATUS
    01371 FsRtlGetTunnelParameterValue (
    01372     IN PUNICODE_STRING ValueName,
    01373     IN OUT PULONG Value
    01374     )
    01375
    01376 /*++
    01377
    01378 Routine Description:
    01379
    01380     Given a unicode value name this routine will go into the registry
    01381     location for the Tunnel parameter information and get the
    01382     value.
    01383
    01384 Arguments:
    01385
    01386     ValueName - the unicode name for the registry value located in the
    01387                 double space configuration location of the registry.
    01388     Value   - a pointer to the ULONG for the result.
    01389
    01390 Return Value:
    01391
    01392     NTSTATUS
    01393
    01394     If STATUS_SUCCESSFUL is returned, the location *Value will be
    01395     updated with the DWORD value from the registry.  If any failing
    01396     status is returned, this value is untouched.
    01397
    01398 --*/
    01399
    01400 {
    01401     HANDLE Handle;
    01402     NTSTATUS Status;
    01403     ULONG RequestLength;
    01404     ULONG ResultLength;
    01405     UCHAR Buffer[KEY_WORK_AREA];
    01406     UNICODE_STRING KeyName;
    01407     OBJECT_ATTRIBUTES ObjectAttributes;
    01408     PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
    01409
    01410     KeyName.Buffer = TUNNEL_KEY_NAME;
    01411     KeyName.Length = sizeof(TUNNEL_KEY_NAME) - sizeof(WCHAR);
    01412     KeyName.MaximumLength = sizeof(TUNNEL_KEY_NAME);
    01413
    01414     InitializeObjectAttributes(&ObjectAttributes,
    01415                                &KeyName,
    01416                                OBJ_CASE_INSENSITIVE,
    01417                                NULL,
    01418                                NULL);
    01419
    01420     Status = ZwOpenKey(&Handle,
    01421                        KEY_READ,
    01422                        &ObjectAttributes);
    01423
    01424     if (!NT_SUCCESS(Status)) {
    01425
    01426         return Status;
    01427     }
    01428
    01429     RequestLength = KEY_WORK_AREA;
    01430
    01431     KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION)Buffer;
    01432
    01433     while (1) {
    01434
    01435         Status = ZwQueryValueKey(Handle,
    01436                                  ValueName,
    01437                                  KeyValueFullInformation,
    01438                                  KeyValueInformation,
    01439                                  RequestLength,
    01440                                  &ResultLength);
    01441
    01442         ASSERT( Status != STATUS_BUFFER_OVERFLOW );
    01443
    01444         if (Status == STATUS_BUFFER_OVERFLOW) {
    01445
    01446             //
    01447             // Try to get a buffer big enough.
    01448             //
    01449
    01450             if (KeyValueInformation != (PKEY_VALUE_FULL_INFORMATION)Buffer) {
    01451
    01452                 ExFreePool(KeyValueInformation);
    01453             }
    01454
    01455             RequestLength += 256;
    01456
    01457             KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION)
    01458                                   ExAllocatePoolWithTag(PagedPool,
    01459                                                         RequestLength,
    01460                                                         'KnuT');
    01461
    01462             if (!KeyValueInformation) {
    01463                 return STATUS_NO_MEMORY;
    01464             }
    01465
    01466         } else {
    01467
    01468             break;
    01469         }
    01470     }
    01471
    01472     ZwClose(Handle);
    01473
    01474     if (NT_SUCCESS(Status)) {
    01475
    01476         if (KeyValueInformation->DataLength != 0) {
    01477
    01478             PULONG DataPtr;
    01479
    01480             //
    01481             // Return contents to the caller.
    01482             //
    01483
    01484             DataPtr = (PULONG)
    01485               ((PUCHAR)KeyValueInformation + KeyValueInformation->DataOffset);
    01486             *Value = *DataPtr;
    01487
    01488         } else {
    01489
    01490             //
    01491             // Treat as if no value was found
    01492             //
    01493
    01494             Status = STATUS_OBJECT_NAME_NOT_FOUND;
    01495         }
    01496     }
    01497
    01498     if (KeyValueInformation != (PKEY_VALUE_FULL_INFORMATION)Buffer) {
    01499
    01500         ExFreePool(KeyValueInformation);
    01501     }
    01502
    01503     return Status;
    01504 }
    01505
    01506
    01507 #if defined(TUNNELTEST) || defined(KEYVIEW)
    01508
    01509 VOID
    01510 DumpTunnel (
    01511     PTUNNEL Tunnel
    01512     )
    01513 {
    01514     PRTL_SPLAY_LINKS SplayLinks, Ptr;
    01515     PTUNNEL_NODE Node;
    01516     PLIST_ENTRY Link;
    01517     ULONG Indent = 1, i;
    01518     ULONG EntryCount = 0;
    01519     BOOLEAN CountOff = FALSE;
    01520
    01521     DbgPrint("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
    01522
    01523     DbgPrint("NumEntries = %d\n", Tunnel->NumEntries);
    01524     DbgPrint("****** Cache Tree\n");
    01525
    01526     SplayLinks = Tunnel->Cache;
    01527
    01528     if (SplayLinks == NULL) {
    01529
    01530         goto end;
    01531     }
    01532
    01533     while (RtlLeftChild(SplayLinks) != NULL) {
    01534
    01535         SplayLinks = RtlLeftChild(SplayLinks);
    01536         Indent++;
    01537     }
    01538
    01539     while (SplayLinks) {
    01540
    01541         Node = CONTAINING_RECORD( SplayLinks, TUNNEL_NODE, CacheLinks );
    01542
    01543         EntryCount++;
    01544
    01545         DumpNode(Node, Indent);
    01546
    01547         Ptr = SplayLinks;
    01548
    01549         /*
    01550           first check to see if there is a right subtree to the input link
    01551           if there is then the real successor is the left most node in
    01552           the right subtree.  That is find and return P in the following diagram
    01553
    01554                       Links
    01555                          \
    01556                           .
    01557                          .
    01558                         .
    01559                        /
    01560                       P
    01561                        \
    01562         */
    01563
    01564         if ((Ptr = RtlRightChild(SplayLinks)) != NULL) {
    01565
    01566             Indent++;
    01567             while (RtlLeftChild(Ptr) != NULL) {
    01568
    01569                 Indent++;
    01570                 Ptr = RtlLeftChild(Ptr);
    01571             }
    01572
    01573             SplayLinks = Ptr;
    01574
    01575         } else {
    01576             /*
    01577               we do not have a right child so check to see if have a parent and if
    01578               so find the first ancestor that we are a left decendent of. That
    01579               is find and return P in the following diagram
    01580
    01581                                P
    01582                               /
    01583                              .
    01584                               .
    01585                                .
    01586                               Links
    01587             */
    01588
    01589             Ptr = SplayLinks;
    01590             while (RtlIsRightChild(Ptr)) {
    01591
    01592                 Indent--;
    01593                 Ptr = RtlParent(Ptr);
    01594             }
    01595
    01596             if (!RtlIsLeftChild(Ptr)) {
    01597
    01598                 //
    01599                 //  we do not have a real successor so we simply return
    01600                 //  NULL
    01601                 //
    01602                 SplayLinks = NULL;
    01603
    01604             } else {
    01605
    01606                 Indent--;
    01607                 SplayLinks = RtlParent(Ptr);
    01608             }
    01609         }
    01610     }
    01611
    01612     end:
    01613
    01614     if (CountOff = (EntryCount != Tunnel->NumEntries)) {
    01615
    01616         DbgPrint("!!!!!!!!!! Splay Tree Count Mismatch (%d != %d)\n", EntryCount, Tunnel->NumEntries);
    01617     }
    01618
    01619     EntryCount = 0;
    01620
    01621     DbgPrint("****** Timer Queue\n");
    01622
    01623     for (Link = Tunnel->TimerQueue.Flink;
    01624          Link != &Tunnel->TimerQueue;
    01625          Link = Link->Flink) {
    01626
    01627         Node = CONTAINING_RECORD( Link, TUNNEL_NODE, ListLinks );
    01628
    01629         EntryCount++;
    01630
    01631         DumpNode(Node, 1);
    01632     }
    01633
    01634     if (CountOff |= (EntryCount != Tunnel->NumEntries)) {
    01635
    01636         DbgPrint("!!!!!!!!!! Timer Queue Count Mismatch (%d != %d)\n", EntryCount, Tunnel->NumEntries);
    01637     }
    01638
    01639     ASSERT(!CountOff);
    01640
    01641     DbgPrint("------------------------------------------------------------------\n");
    01642 }
    01643
    01644 #define MAXINDENT  128
    01645 #define INDENTSTEP 3
    01646
    01647 VOID
    01648 DumpNode (
    01649     PTUNNEL_NODE Node,
    01650     ULONG Indent
    01651     )
    01652 {
    01653     ULONG i;
    01654     CHAR  SpaceBuf[MAXINDENT*INDENTSTEP + 1];
    01655
    01656     Indent--;
    01657     if (Indent > MAXINDENT) {
    01658         Indent = MAXINDENT;
    01659     }
    01660
    01661     //
    01662     //  DbgPrint is really expensive to iteratively call to do the indenting,
    01663     //  so just build up the indentation all at once on the stack.
    01664     //
    01665
    01666     RtlFillMemory(SpaceBuf, Indent*INDENTSTEP, ' ');
    01667     SpaceBuf[Indent*INDENTSTEP] = '\0';
    01668
    01669     DbgPrint("%sNode 0x%x  CreateTime = %08x%08x, DirKey = %08x%08x, Flags = %d\n",
    01670              SpaceBuf,
    01671              Node,
    01672              DblHex64(Node->CreateTime.QuadPart),
    01673              DblHex64(Node->DirKey),
    01674              Node->Flags );
    01675
    01676     DbgPrint("%sShort = %wZ, Long = %wZ\n", SpaceBuf,
    01677                                             &Node->ShortName,
    01678                                             &Node->LongName );
    01679
    01680     DbgPrint("%sP = %x, R = %x, L = %x\n", SpaceBuf,
    01681                                            RtlParent(&Node->CacheLinks),
    01682                                            RtlRightChild(&Node->CacheLinks),
    01683                                            RtlLeftChild(&Node->CacheLinks) );
    01684 }
    01685 #endif // TUNNELTEST
    01686