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

Generated on Sat May 15 19:42:10 2004 for test by doxygen 1.3.7