Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1439 lines
37 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. HashSup.c
  5. Abstract:
  6. This module implements the Ntfs hasing support routines
  7. Author:
  8. Chris Davis [CDavis] 2-May-1997
  9. Brian Andrew [BrianAn] 29-Dec-1998
  10. Revision History:
  11. --*/
  12. #include "NtfsProc.h"
  13. //
  14. // The Bug check file id for this module
  15. //
  16. #define BugCheckFileId (NTFS_BUG_CHECK_HASHSUP)
  17. //
  18. // The debug trace level for this module
  19. //
  20. #define Dbg (DEBUG_TRACE_HASHSUP)
  21. /*
  22. Here are 10 primes slightly greater than 10^9 which may come in handy
  23. 1000000007, 1000000009, 1000000021, 1000000033, 1000000087,
  24. 1000000093, 1000000097, 1000000103, 1000000123, 1000000181
  25. */
  26. //
  27. // Local definitions
  28. //
  29. //
  30. // Hash value is modula this value.
  31. //
  32. #define HASH_PRIME (1048583)
  33. //
  34. // Bucket depth before starting to split.
  35. //
  36. #ifdef NTFS_HASH_DATA
  37. #define HASH_MAX_BUCKET_DEPTH (7)
  38. ULONG NtfsInsertHashCount = 0;
  39. BOOLEAN NtfsFillHistogram = FALSE;
  40. VOID
  41. NtfsFillHashHistogram (
  42. PNTFS_HASH_TABLE Table
  43. );
  44. #else
  45. #define HASH_MAX_BUCKET_DEPTH (5)
  46. #endif
  47. //
  48. // VOID
  49. // NtfsHashBucketFromHash (
  50. // IN PNTFS_HASH_TABLE Table,
  51. // IN ULONG Hash,
  52. // OUT PULONG Index
  53. // );
  54. //
  55. #define NtfsHashBucketFromHash(T,H,PI) { \
  56. *(PI) = (H) & ((T)->MaxBucket - 1); \
  57. if (*(PI) < (T)->SplitPoint) { \
  58. *(PI) = (H) & ((2 * (T)->MaxBucket) - 1); \
  59. } \
  60. }
  61. //
  62. // VOID
  63. // NtfsGetHashSegmentAndIndex (
  64. // IN ULONG HashBucket,
  65. // IN PULONG HashSegment,
  66. // IN PULONG HashIndex
  67. // );
  68. //
  69. #define NtfsGetHashSegmentAndIndex(B,S,I) { \
  70. *(S) = (B) >> HASH_INDEX_SHIFT; \
  71. *(I) = (B) & (HASH_MAX_INDEX_COUNT - 1);\
  72. }
  73. //
  74. // Local procedures
  75. //
  76. VOID
  77. NtfsInitHashSegment (
  78. IN OUT PNTFS_HASH_TABLE Table,
  79. IN ULONG SegmentIndex
  80. );
  81. PNTFS_HASH_ENTRY
  82. NtfsLookupHashEntry (
  83. IN PNTFS_HASH_TABLE Table,
  84. IN ULONG FullNameLength,
  85. IN ULONG HashValue,
  86. IN PNTFS_HASH_ENTRY CurrentEntry OPTIONAL
  87. );
  88. BOOLEAN
  89. NtfsAreHashNamesEqual (
  90. IN PSCB StartingScb,
  91. IN PLCB HashLcb,
  92. IN PUNICODE_STRING RelativeName
  93. );
  94. VOID
  95. NtfsExpandHashTable (
  96. IN OUT PNTFS_HASH_TABLE Table
  97. );
  98. #ifdef ALLOC_PRAGMA
  99. #pragma alloc_text(PAGE, NtfsAreHashNamesEqual)
  100. #pragma alloc_text(PAGE, NtfsExpandHashTable)
  101. #pragma alloc_text(PAGE, NtfsFindPrefixHashEntry)
  102. #pragma alloc_text(PAGE, NtfsInitHashSegment)
  103. #pragma alloc_text(PAGE, NtfsInitializeHashTable)
  104. #pragma alloc_text(PAGE, NtfsInsertHashEntry)
  105. #pragma alloc_text(PAGE, NtfsLookupHashEntry)
  106. #pragma alloc_text(PAGE, NtfsRemoveHashEntry)
  107. #pragma alloc_text(PAGE, NtfsUninitializeHashTable)
  108. #endif
  109. VOID
  110. NtfsInitializeHashTable (
  111. IN OUT PNTFS_HASH_TABLE Table
  112. )
  113. /*++
  114. Routine Description:
  115. This routine is called to initialize the hash table. We set this up with a single
  116. hash segment. May raise due to InitHashSegment
  117. Arguments:
  118. Table - Hash table to initialize.
  119. Return Value:
  120. None
  121. --*/
  122. {
  123. PAGED_CODE();
  124. RtlZeroMemory( Table, sizeof( NTFS_HASH_TABLE ));
  125. NtfsInitHashSegment( Table, 0 );
  126. Table->MaxBucket = HASH_MAX_INDEX_COUNT;
  127. return;
  128. }
  129. VOID
  130. NtfsUninitializeHashTable (
  131. IN OUT PNTFS_HASH_TABLE Table
  132. )
  133. /*++
  134. Routine Description:
  135. This routine will uninitialize the hash table. Note that all of the buckets should be
  136. empty.
  137. Arguments:
  138. Table - Hash table.
  139. Return Value:
  140. None
  141. --*/
  142. {
  143. PNTFS_HASH_SEGMENT *ThisSegment;
  144. PNTFS_HASH_SEGMENT *LastSegment;
  145. PAGED_CODE();
  146. //
  147. // Walk through the array of hash segments.
  148. //
  149. ThisSegment = &Table->HashSegments[0];
  150. LastSegment = &Table->HashSegments[HASH_MAX_SEGMENT_COUNT - 1];
  151. while (*ThisSegment != NULL) {
  152. NtfsFreePool( *ThisSegment );
  153. *ThisSegment = NULL;
  154. if (ThisSegment == LastSegment) { break; }
  155. ThisSegment += 1;
  156. }
  157. return;
  158. }
  159. PLCB
  160. NtfsFindPrefixHashEntry (
  161. IN PIRP_CONTEXT IrpContext,
  162. IN PNTFS_HASH_TABLE Table,
  163. IN PSCB ParentScb,
  164. OUT PULONG CreateFlags,
  165. IN OUT PFCB *CurrentFcb,
  166. OUT PULONG FileHashValue,
  167. OUT PULONG FileNameLength,
  168. OUT PULONG ParentHashValue,
  169. OUT PULONG ParentNameLength,
  170. IN OUT PUNICODE_STRING RemainingName
  171. )
  172. /*++
  173. Routine Description:
  174. This routine is called to look for a match in the hash table for the given
  175. starting Scb and remaining name. We will first look for the full name, if
  176. we don't find a match on that we will check for a matching parent string.
  177. Arguments:
  178. Table - Hash table to process.
  179. ParentScb - The name search begins from this directory. The Scb is
  180. initially acquired and its Fcb is stored in *CurrentFcb.
  181. OwnParentScb - Boolean which indicates if this thread owns the parent Scb.
  182. CurrentFcb - Points to the last Fcb acquired. If we need to perform a
  183. teardown it will begin from this point.
  184. FileHashValue - Address to store the hash value for the input string. Applies to
  185. the full string and starting Scb even if a match wasn't found.
  186. FileNameLength - Location to store the length of the relative name which
  187. matches the hash value generated above. If we didn't generate a hash
  188. then we will return a 0 for the length.
  189. ParentHashValue - Address to store the hash value for the parent of the input string.
  190. Applies to parent of the full string and starting Scb even if a match wasn't found
  191. for the full string.
  192. ParentNameLength - Location to store the length of the parent of the full name.
  193. It corresponds to the parent hash generated above. Note that our caller
  194. will have to check the remaining name on return to know if the parent hash
  195. was computed. If we didn't generate a hash for the parent above then
  196. we will return 0 for the length.
  197. RemainingName - Name relative to the StartingScb above, on return it will be
  198. the unmatched portion of the name.
  199. Return Value:
  200. PLCB - Pointer to the Lcb found in the hash lookup or FALSE if no Lcb is found.
  201. If an Lcb is found then we will own the Fcb for it exclusively on return.
  202. --*/
  203. {
  204. PVCB Vcb = ParentScb->Vcb;
  205. PNTFS_HASH_ENTRY FoundEntry;
  206. PLCB FoundLcb = NULL;
  207. UNICODE_STRING TempName;
  208. WCHAR Separator = L'\\';
  209. ULONG RemainingNameLength;
  210. PWCHAR RemainingNameBuffer;
  211. PWCHAR NextChar;
  212. PAGED_CODE();
  213. ASSERT( RemainingName->Length != 0 );
  214. ASSERT( RemainingName->Buffer[0] != L'\\' );
  215. ASSERT( RemainingName->Buffer[0] != L':' );
  216. ASSERT( ParentScb->AttributeTypeCode == $INDEX_ALLOCATION );
  217. //
  218. // Compute the hash for the file before acquiring the hash table.
  219. //
  220. *ParentHashValue = *FileHashValue = ParentScb->ScbType.Index.HashValue;
  221. //
  222. // Check whether we need to generate the separator.
  223. //
  224. if (ParentScb != Vcb->RootIndexScb) {
  225. NtfsConvertNameToHash( &Separator, sizeof( WCHAR ), Vcb->UpcaseTable, FileHashValue );
  226. }
  227. //
  228. // Generate the hash for the file name.
  229. //
  230. NtfsConvertNameToHash( RemainingName->Buffer,
  231. RemainingName->Length,
  232. Vcb->UpcaseTable,
  233. FileHashValue );
  234. *FileHashValue = NtfsGenerateHashFromUlong( *FileHashValue );
  235. NtfsAcquireHashTable( Vcb );
  236. //
  237. // Generate the hash value based on the starting Scb and the string.
  238. // Return immediately if there is no parent name.
  239. //
  240. if (ParentScb->ScbType.Index.NormalizedName.Length == 0) {
  241. NtfsReleaseHashTable( Vcb );
  242. return NULL;
  243. }
  244. *ParentNameLength = *FileNameLength = ParentScb->ScbType.Index.NormalizedName.Length;
  245. *FileNameLength += RemainingName->Length;
  246. #ifdef NTFS_HASH_DATA
  247. Table->HashLookupCount += 1;
  248. #endif
  249. //
  250. // Check whether to include a separator.
  251. //
  252. if (ParentScb != Vcb->RootIndexScb) {
  253. *FileNameLength += sizeof( WCHAR );
  254. }
  255. //
  256. // Loop looking for a match on the hash value and then verify the name.
  257. //
  258. FoundEntry = NULL;
  259. while ((FoundEntry = NtfsLookupHashEntry( Table,
  260. *FileNameLength,
  261. *FileHashValue,
  262. FoundEntry )) != NULL) {
  263. //
  264. // If we have a match then verify the name strings.
  265. //
  266. if (NtfsAreHashNamesEqual( ParentScb,
  267. FoundEntry->HashLcb,
  268. RemainingName )) {
  269. //
  270. // The name string match. Adjust the input remaining name to
  271. // show there is no name left to process.
  272. //
  273. FoundLcb = FoundEntry->HashLcb;
  274. //
  275. // Move to the end of the input string.
  276. //
  277. RemainingNameLength = 0;
  278. RemainingNameBuffer = Add2Ptr( RemainingName->Buffer,
  279. RemainingName->Length );
  280. //
  281. // Show that we never generated a parent hash. No need to
  282. // remember the file hash in this case either.
  283. //
  284. #ifdef NTFS_HASH_DATA
  285. Table->FileMatchCount += 1;
  286. #endif
  287. *ParentNameLength = 0;
  288. *FileNameLength = 0;
  289. break;
  290. }
  291. }
  292. //
  293. // If we don't have a match then let's look at a possible parent string.
  294. //
  295. if (FoundLcb == NULL) {
  296. //
  297. // Search backwards for a '\'. If it is a '\' then do the
  298. // same search for a match on the string based on the parent.
  299. //
  300. TempName.Length = RemainingName->Length;
  301. NextChar = &RemainingName->Buffer[ (TempName.Length - sizeof( WCHAR )) / sizeof( WCHAR ) ];
  302. while (TRUE) {
  303. //
  304. // Break out if no separator is found.
  305. //
  306. if (TempName.Length == 0) {
  307. *ParentNameLength = 0;
  308. break;
  309. }
  310. if (*NextChar == L'\\') {
  311. //
  312. // We found the separator. Back up one more character to step over
  313. // the '\' character and then complete a hash for the parent.
  314. //
  315. TempName.Buffer = RemainingName->Buffer;
  316. TempName.Length -= sizeof( WCHAR );
  317. TempName.MaximumLength = TempName.Length;
  318. //
  319. // Drop the mutex while we compute the hash.
  320. //
  321. NtfsReleaseHashTable( Vcb );
  322. if (ParentScb != Vcb->RootIndexScb) {
  323. NtfsConvertNameToHash( &Separator, sizeof( WCHAR ), Vcb->UpcaseTable, ParentHashValue );
  324. *ParentNameLength += sizeof( WCHAR );
  325. }
  326. NtfsConvertNameToHash( TempName.Buffer,
  327. TempName.Length,
  328. Vcb->UpcaseTable,
  329. ParentHashValue );
  330. *ParentHashValue = NtfsGenerateHashFromUlong( *ParentHashValue );
  331. *ParentNameLength += TempName.Length;
  332. NtfsAcquireHashTable( Vcb );
  333. FoundEntry = NULL;
  334. while ((FoundEntry = NtfsLookupHashEntry( Table,
  335. *ParentNameLength,
  336. *ParentHashValue,
  337. FoundEntry )) != NULL) {
  338. //
  339. // If we have a match then verify the name strings.
  340. //
  341. if (NtfsAreHashNamesEqual( ParentScb,
  342. FoundEntry->HashLcb,
  343. &TempName )) {
  344. //
  345. // The name string match. Adjust the remaining name to
  346. // swallow the parent string found.
  347. //
  348. FoundLcb = FoundEntry->HashLcb;
  349. RemainingNameLength = RemainingName->Length - (TempName.Length + sizeof( WCHAR ));
  350. RemainingNameBuffer = Add2Ptr( RemainingName->Buffer,
  351. TempName.Length + sizeof( WCHAR ));
  352. #ifdef NTFS_HASH_DATA
  353. Table->ParentMatchCount += 1;
  354. #endif
  355. *ParentNameLength = 0;
  356. break;
  357. }
  358. }
  359. //
  360. // No match found. Break out in any case.
  361. //
  362. break;
  363. }
  364. TempName.Length -= sizeof( WCHAR );
  365. NextChar -= 1;
  366. }
  367. }
  368. //
  369. // We now have the Lcb to return. We need to carefully acquire the Fcb for this Lcb.
  370. // We can't acquire it while waiting because of deadlock possibilities.
  371. //
  372. if (FoundLcb != NULL) {
  373. UCHAR LcbFlags;
  374. BOOLEAN CreateNewLcb = FALSE;
  375. ULONG RemainingNameOffset;
  376. //
  377. // While we own the hash table it will be safe to copy the exact case of the
  378. // names over to our input buffer. We will work our way backwards through the
  379. // remaining name passed to us.
  380. //
  381. RemainingNameOffset = RemainingNameLength + FoundLcb->ExactCaseLink.LinkName.Length;
  382. //
  383. // If this was a match on the parent then step back over the '\'.
  384. // We know there must be a separator.
  385. //
  386. if (RemainingNameLength != 0) {
  387. RemainingNameOffset += sizeof( WCHAR );
  388. }
  389. //
  390. // Now back up the length of the name in the Lcb. Save this location in
  391. // case we have to look up the Lcb again.
  392. //
  393. TempName.Buffer = Add2Ptr( RemainingName->Buffer,
  394. RemainingName->Length - RemainingNameOffset );
  395. TempName.MaximumLength = TempName.Length = FoundLcb->ExactCaseLink.LinkName.Length;
  396. RtlCopyMemory( TempName.Buffer,
  397. FoundLcb->ExactCaseLink.LinkName.Buffer,
  398. FoundLcb->ExactCaseLink.LinkName.Length );
  399. //
  400. // Now the balance of the name which is part of the Lcb->Scb parent name.
  401. //
  402. if (RemainingNameOffset != RemainingName->Length) {
  403. //
  404. // There are prior components in our input string. We want to back up
  405. // over the preceding backslash and then copy over the relevant portion
  406. // of the normalized name.
  407. //
  408. RemainingNameOffset = RemainingName->Length - (RemainingNameOffset + sizeof( WCHAR ));
  409. RtlCopyMemory( RemainingName->Buffer,
  410. Add2Ptr( FoundLcb->Scb->ScbType.Index.NormalizedName.Buffer,
  411. FoundLcb->Scb->ScbType.Index.NormalizedName.Length - RemainingNameOffset ),
  412. RemainingNameOffset );
  413. }
  414. if (!NtfsAcquireFcbWithPaging( IrpContext, FoundLcb->Fcb, ACQUIRE_DONT_WAIT )) {
  415. PFCB ThisFcb = FoundLcb->Fcb;
  416. PFCB ParentFcb = FoundLcb->Scb->Fcb;
  417. PSCB ThisScb;
  418. //
  419. // Remember the current Lcb flags.
  420. //
  421. LcbFlags = FoundLcb->FileNameAttr->Flags;
  422. //
  423. // Acquire the Fcb table and reference the Fcb. Then release the hash table, Fcb table
  424. // and ParentScb. We should now be able to acquire the Fcb. Reacquire the Fcb table
  425. // to clean up the Fcb reference count. Finally verify that the Lcb is still in the
  426. // hash table (requires another lookup).
  427. //
  428. NtfsAcquireFcbTable( IrpContext, Vcb );
  429. ThisFcb->ReferenceCount += 1;
  430. ParentFcb->ReferenceCount += 1;
  431. NtfsReleaseFcbTable( IrpContext, Vcb );
  432. NtfsReleaseScb( IrpContext, ParentScb );
  433. ClearFlag( *CreateFlags, CREATE_FLAG_SHARED_PARENT_FCB );
  434. *CurrentFcb = NULL;
  435. NtfsReleaseHashTable( Vcb );
  436. NtfsAcquireFcbWithPaging( IrpContext, ThisFcb, 0 );
  437. *CurrentFcb = ThisFcb;
  438. NtfsAcquireSharedFcb( IrpContext, ParentFcb, NULL, 0 );
  439. NtfsAcquireFcbTable( IrpContext, Vcb );
  440. ThisFcb->ReferenceCount -= 1;
  441. ParentFcb->ReferenceCount -= 1;
  442. NtfsReleaseFcbTable( IrpContext, Vcb );
  443. //
  444. // Now look for an existing Scb and Lcb.
  445. //
  446. ThisScb = NtfsCreateScb( IrpContext,
  447. ParentFcb,
  448. $INDEX_ALLOCATION,
  449. &NtfsFileNameIndex,
  450. TRUE,
  451. NULL );
  452. if (ThisScb == NULL) {
  453. #ifdef NTFS_HASH_DATA
  454. Table->CreateScbFails += 1;
  455. #endif
  456. NtfsReleaseFcb( IrpContext, ParentFcb );
  457. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  458. }
  459. FoundLcb = NtfsCreateLcb( IrpContext,
  460. ThisScb,
  461. ThisFcb,
  462. TempName,
  463. LcbFlags,
  464. &CreateNewLcb );
  465. NtfsReleaseFcb( IrpContext, ParentFcb );
  466. //
  467. // If this wasn't an existing Lcb then reacquire the starting Scb.
  468. // This is the rare case so raise CANT_WAIT and retry.
  469. //
  470. if (FoundLcb == NULL) {
  471. #ifdef NTFS_HASH_DATA
  472. Table->CreateLcbFails += 1;
  473. #endif
  474. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  475. }
  476. //
  477. // Release the starting Scb and remember our current Fcb.
  478. //
  479. } else {
  480. NtfsReleaseHashTable( Vcb );
  481. NtfsReleaseScb( IrpContext, ParentScb );
  482. ClearFlag( *CreateFlags, CREATE_FLAG_SHARED_PARENT_FCB );
  483. *CurrentFcb = FoundLcb->Fcb;
  484. }
  485. //
  486. // If we still have the Lcb then update the remaining name string.
  487. //
  488. if (FoundLcb != NULL) {
  489. RemainingName->Length = (USHORT) RemainingNameLength;
  490. RemainingName->Buffer = RemainingNameBuffer;
  491. }
  492. } else {
  493. NtfsReleaseHashTable( Vcb );
  494. }
  495. return FoundLcb;
  496. }
  497. VOID
  498. NtfsInsertHashEntry (
  499. IN PNTFS_HASH_TABLE Table,
  500. IN PLCB HashLcb,
  501. IN ULONG NameLength,
  502. IN ULONG HashValue
  503. )
  504. /*++
  505. Routine Description:
  506. This routine will insert a entry into the hash table. May raise due to
  507. memory allocation.
  508. Arguments:
  509. Table - Hash table.
  510. HashLcb - Final target of the hash operation.
  511. NameLength - Full path used to reach the hash value.
  512. HashValue - Hash value to insert.
  513. Return Value:
  514. None
  515. --*/
  516. {
  517. PNTFS_HASH_ENTRY NewHashEntry;
  518. ULONG Segment;
  519. ULONG Index;
  520. ULONG Bucket;
  521. PAGED_CODE();
  522. //
  523. // Allocate and initialize the hash entry. Nothing to do if unsuccessful.
  524. //
  525. NewHashEntry = NtfsAllocatePoolNoRaise( PagedPool, sizeof( NTFS_HASH_ENTRY ));
  526. if (NewHashEntry == NULL) {
  527. return;
  528. }
  529. NewHashEntry->HashValue = HashValue;
  530. NewHashEntry->FullNameLength = NameLength;
  531. NewHashEntry->HashLcb = HashLcb;
  532. //
  533. // Find the bucket to insert into and then do the insertion.
  534. //
  535. NtfsAcquireHashTable( HashLcb->Fcb->Vcb );
  536. //
  537. // Continue the process of growing the table if needed.
  538. //
  539. if (Table->TableState == TABLE_STATE_EXPANDING) {
  540. NtfsExpandHashTable( Table );
  541. }
  542. NtfsHashBucketFromHash( Table, HashValue, &Bucket );
  543. NtfsGetHashSegmentAndIndex( Bucket, &Segment, &Index );
  544. NewHashEntry->NextEntry = (*Table->HashSegments[ Segment ])[ Index ];
  545. (*Table->HashSegments[ Segment ])[ Index ] = NewHashEntry;
  546. #ifdef NTFS_HASH_DATA
  547. NtfsInsertHashCount += 1;
  548. if (!FlagOn( NtfsInsertHashCount, 0xff ) && NtfsFillHistogram) {
  549. NtfsFillHashHistogram( Table );
  550. }
  551. #endif
  552. NtfsReleaseHashTable( HashLcb->Fcb->Vcb );
  553. //
  554. // Remember that we've inserted a hash.
  555. //
  556. HashLcb->HashValue = HashValue;
  557. SetFlag( HashLcb->LcbState, LCB_STATE_VALID_HASH_VALUE );
  558. return;
  559. }
  560. VOID
  561. NtfsRemoveHashEntry (
  562. IN PNTFS_HASH_TABLE Table,
  563. IN PLCB HashLcb
  564. )
  565. /*++
  566. Routine Description:
  567. This routine will remove all entries with a given hash value for the given Lcb.
  568. Arguments:
  569. Table - Hash table.
  570. HashLcb - Final target of the hash operation.
  571. Return Value:
  572. None
  573. --*/
  574. {
  575. PNTFS_HASH_ENTRY *NextHashEntry;
  576. PNTFS_HASH_ENTRY CurrentEntry;
  577. ULONG Segment;
  578. ULONG Index;
  579. ULONG Bucket;
  580. ULONG BucketDepth = 0;
  581. PAGED_CODE();
  582. NtfsAcquireHashTable( HashLcb->Fcb->Vcb );
  583. //
  584. // Find the bucket to remove from and then search for this hash value.
  585. //
  586. NtfsHashBucketFromHash( Table, HashLcb->HashValue, &Bucket );
  587. NtfsGetHashSegmentAndIndex( Bucket, &Segment, &Index );
  588. //
  589. // Get the address of the first entry.
  590. //
  591. NextHashEntry = (PNTFS_HASH_ENTRY *) &(*Table->HashSegments[ Segment ])[ Index ];
  592. while (*NextHashEntry != NULL) {
  593. //
  594. // Look for a match entry.
  595. //
  596. if (((*NextHashEntry)->HashValue == HashLcb->HashValue) &&
  597. ((*NextHashEntry)->HashLcb == HashLcb)) {
  598. CurrentEntry = *NextHashEntry;
  599. *NextHashEntry = CurrentEntry->NextEntry;
  600. NtfsFreePool( CurrentEntry );
  601. //
  602. // Move to the next entry but remember the depth of the bucket.
  603. //
  604. } else {
  605. NextHashEntry = &(*NextHashEntry)->NextEntry;
  606. BucketDepth += 1;
  607. }
  608. }
  609. //
  610. // Check if the bucket depth is greater than our max.
  611. //
  612. if ((BucketDepth > HASH_MAX_BUCKET_DEPTH) &&
  613. (Table->TableState == TABLE_STATE_STABLE) &&
  614. (Table->MaxBucket < HASH_MAX_BUCKET_COUNT)) {
  615. ASSERT( Table->SplitPoint == 0 );
  616. Table->TableState = TABLE_STATE_EXPANDING;
  617. }
  618. NtfsReleaseHashTable( HashLcb->Fcb->Vcb );
  619. HashLcb->HashValue = 0;
  620. ClearFlag( HashLcb->LcbState, LCB_STATE_VALID_HASH_VALUE );
  621. return;
  622. }
  623. //
  624. // Local support routine
  625. //
  626. VOID
  627. NtfsInitHashSegment (
  628. IN OUT PNTFS_HASH_TABLE Table,
  629. IN ULONG SegmentIndex
  630. )
  631. /*++
  632. Routine Description:
  633. This routine allocates and initializes a new segment in the segment array.
  634. It may raise out of resources.
  635. Arguments:
  636. Table - Table with an entry to initialize.
  637. SegmentIndex - Index to be initialized.
  638. Return Value:
  639. None
  640. --*/
  641. {
  642. PAGED_CODE();
  643. Table->HashSegments[ SegmentIndex ] = NtfsAllocatePool( PagedPool, sizeof( NTFS_HASH_SEGMENT ));
  644. RtlZeroMemory( Table->HashSegments[ SegmentIndex ], sizeof( NTFS_HASH_SEGMENT ));
  645. return;
  646. }
  647. //
  648. // Local support routine
  649. //
  650. PNTFS_HASH_ENTRY
  651. NtfsLookupHashEntry (
  652. IN PNTFS_HASH_TABLE Table,
  653. IN ULONG FullNameLength,
  654. IN ULONG HashValue,
  655. IN PNTFS_HASH_ENTRY CurrentEntry OPTIONAL
  656. )
  657. /*++
  658. Routine Description:
  659. This routine looks up a match in the hash table for a given hash value.
  660. The entry is uniquely indentified by the hash value, and the full name length.
  661. This routine also takes a pointer to a hash entry for the case where we are
  662. resuming a search for the same hash value.
  663. If the target bucket has more than our optimal number of entries then set
  664. the state of the table to grow the number of buckets.
  665. Arguments:
  666. Table - Hash table to search
  667. FullNameLength - Number of bytes in the name relative to the root.
  668. HashValue - Precomputed hash value.
  669. CurrentEntry - NULL if this is the first search for this hash entry. Otherwise
  670. it is the last entry returned.
  671. Return Value:
  672. PNTFS_HASH_ENTRY - this is NULL if no match was found. Otherwise it points it to
  673. a hash entry which matches the input value. NOTE - the caller must then verify
  674. the name strings.
  675. --*/
  676. {
  677. ULONG ChainDepth = 0;
  678. PNTFS_HASH_ENTRY NextEntry;
  679. ULONG HashBucket;
  680. ULONG HashSegment;
  681. ULONG HashIndex;
  682. PAGED_CODE();
  683. //
  684. // If we weren't passed an initial hash entry then look up the start of
  685. // the chain for the bucket containing this hash value.
  686. //
  687. if (!ARGUMENT_PRESENT( CurrentEntry )) {
  688. //
  689. // Find the bucket by computing the segment and index to look in.
  690. //
  691. NtfsHashBucketFromHash( Table, HashValue, &HashBucket );
  692. NtfsGetHashSegmentAndIndex( HashBucket, &HashSegment, &HashIndex );
  693. //
  694. // Get the first entry in the bucket.
  695. //
  696. NextEntry = (*Table->HashSegments[ HashSegment ])[ HashIndex ];
  697. //
  698. // Otherwise we use the next entry in the chain.
  699. //
  700. } else {
  701. NextEntry = CurrentEntry->NextEntry;
  702. }
  703. //
  704. // Walk down the chain looking for a match. Keep track of the depth
  705. // of the chain in case we need to grow the table.
  706. //
  707. while (NextEntry != NULL) {
  708. ChainDepth += 1;
  709. if ((NextEntry->HashValue == HashValue) &&
  710. (NextEntry->FullNameLength == FullNameLength)) {
  711. break;
  712. }
  713. NextEntry = NextEntry->NextEntry;
  714. }
  715. //
  716. // If the depth is greater than our optimal value then mark the table
  717. // for expansion. The table may already be growing or at its maximum
  718. // value.
  719. //
  720. if ((ChainDepth > HASH_MAX_BUCKET_DEPTH) &&
  721. (Table->TableState == TABLE_STATE_STABLE) &&
  722. (Table->MaxBucket < HASH_MAX_BUCKET_COUNT)) {
  723. ASSERT( Table->SplitPoint == 0 );
  724. Table->TableState = TABLE_STATE_EXPANDING;
  725. }
  726. //
  727. // Return the value if found.
  728. //
  729. return NextEntry;
  730. }
  731. //
  732. // Local support routine
  733. //
  734. BOOLEAN
  735. NtfsAreHashNamesEqual (
  736. IN PSCB StartingScb,
  737. IN PLCB HashLcb,
  738. IN PUNICODE_STRING RelativeName
  739. )
  740. /*++
  741. Routine Description:
  742. This routine is called to verify that the match found in the hash table has the
  743. same name as the input string.
  744. Arguments:
  745. StartingScb - The name search begins from this directory. It is not
  746. necessarily the parent of the file being opened.
  747. HashLcb - This is the Lcb found in the hash table. This Lcb points
  748. directly to the full string matched.
  749. StartingName - This is the name we need to match. It is 1 OR MORE of the
  750. final components of the name.
  751. Return Value:
  752. None
  753. --*/
  754. {
  755. PUNICODE_STRING StartingScbName;
  756. PUNICODE_STRING HashScbName;
  757. UNICODE_STRING RemainingHashScbName;
  758. UNICODE_STRING RemainingRelativeName;
  759. USHORT SeparatorBias = 0;
  760. PAGED_CODE();
  761. //
  762. // Start by verifying that there is a '\' separator in the correct positions.
  763. // There must be a separator in the Relative name prior to the last component.
  764. // There should also be a separator in the normalized name of the Scb in the
  765. // HashLcb where the StartingScb ends.
  766. //
  767. //
  768. // If the HashLcb Scb is not the StartingScb then there must be a separator
  769. // where the StartingScb string ends.
  770. //
  771. StartingScbName = &StartingScb->ScbType.Index.NormalizedName;
  772. HashScbName = &HashLcb->Scb->ScbType.Index.NormalizedName;
  773. //
  774. // If there is no normalized name in this Scb then get out.
  775. //
  776. if (HashScbName->Length == 0) {
  777. return FALSE;
  778. }
  779. if (StartingScb != HashLcb->Scb) {
  780. //
  781. // Also get out if name in the StartingScb is longer than the one in the
  782. // HashScb. Obviously there is no match if the last component of the
  783. // HashScb is longer than the last one or more components in
  784. // the input name. We can use >= as the test because if the lengths
  785. // match but they aren't the same Scb then there can be no match either.
  786. //
  787. if (StartingScbName->Length >= HashScbName->Length) {
  788. return FALSE;
  789. }
  790. //
  791. // Check for the separator provided the starting Scb is not the root.
  792. //
  793. if (StartingScb != StartingScb->Vcb->RootIndexScb) {
  794. if (HashScbName->Buffer[ StartingScbName->Length / sizeof( WCHAR ) ] != L'\\') {
  795. return FALSE;
  796. //
  797. // Make sure the StartingScbName and the first part of the HashScbName
  798. // match. If not, this is definitely not the right hash entry.
  799. //
  800. } else {
  801. RemainingHashScbName.Buffer = HashScbName->Buffer;
  802. RemainingHashScbName.MaximumLength =
  803. RemainingHashScbName.Length = StartingScbName->Length;
  804. //
  805. // OK to do a direct memory compare here because both name fragments
  806. // are in the normalized form (exactly as on disk).
  807. //
  808. if (!NtfsAreNamesEqual( StartingScb->Vcb->UpcaseTable,
  809. StartingScbName,
  810. &RemainingHashScbName,
  811. FALSE )) {
  812. return FALSE;
  813. }
  814. }
  815. SeparatorBias = sizeof( WCHAR );
  816. }
  817. //
  818. // Set up a unicode string for the remaining portion of the hash scb name.
  819. //
  820. RemainingHashScbName.Buffer = Add2Ptr( HashScbName->Buffer,
  821. StartingScbName->Length + SeparatorBias );
  822. RemainingHashScbName.MaximumLength =
  823. RemainingHashScbName.Length = HashScbName->Length - (StartingScbName->Length + SeparatorBias);
  824. }
  825. RemainingRelativeName.MaximumLength =
  826. RemainingRelativeName.Length = HashLcb->IgnoreCaseLink.LinkName.Length;
  827. RemainingRelativeName.Buffer = Add2Ptr( RelativeName->Buffer,
  828. RelativeName->Length - RemainingRelativeName.Length );
  829. //
  830. // Check for a separator between the last component of relative name and its parent.
  831. // Verify the parent portion actually exists.
  832. //
  833. if (RemainingRelativeName.Length != RelativeName->Length) {
  834. if (*(RemainingRelativeName.Buffer - 1) != L'\\') {
  835. return FALSE;
  836. }
  837. }
  838. //
  839. // Now verify that the tail of the name matches the name in the Lcb.
  840. //
  841. // OK to do a direct memory compare here because both name fragments
  842. // are already upcased.
  843. //
  844. //
  845. if (!NtfsAreNamesEqual( StartingScb->Vcb->UpcaseTable,
  846. &HashLcb->IgnoreCaseLink.LinkName,
  847. &RemainingRelativeName,
  848. FALSE )) {
  849. return FALSE;
  850. }
  851. //
  852. // It is possible that the StartingScb matches the Scb in the HashLcb. If it doesn't
  853. // then verify the other names in the name string.
  854. //
  855. if (StartingScb != HashLcb->Scb) {
  856. RemainingRelativeName.MaximumLength =
  857. RemainingRelativeName.Length = RemainingHashScbName.Length;
  858. RemainingRelativeName.Buffer = RelativeName->Buffer;
  859. //
  860. // We must to a case-insensitive compare here because the
  861. // HashScbName is in normalized form but the RemainingRelativeName
  862. // is already upcased.
  863. //
  864. if (!NtfsAreNamesEqual( StartingScb->Vcb->UpcaseTable,
  865. &RemainingHashScbName,
  866. &RemainingRelativeName,
  867. TRUE )) {
  868. return FALSE;
  869. }
  870. }
  871. return TRUE;
  872. }
  873. //
  874. // Local support routines
  875. //
  876. VOID
  877. NtfsExpandHashTable(
  878. IN OUT PNTFS_HASH_TABLE Table
  879. )
  880. /*++
  881. Routine Description:
  882. This routine is called to add a single bucket to the hash table. If we are at the
  883. last bucket then set the hash table state to stable.
  884. Arguments:
  885. Table - Hash table to add a bucket to.
  886. Return Value:
  887. None
  888. --*/
  889. {
  890. PNTFS_HASH_ENTRY *CurrentOldEntry;
  891. PNTFS_HASH_ENTRY *CurrentNewEntry;
  892. PNTFS_HASH_ENTRY CurrentEntry;
  893. ULONG OldSegment;
  894. ULONG OldIndex;
  895. ULONG NewSegment;
  896. ULONG NewIndex;
  897. ULONG NextBucket;
  898. PAGED_CODE();
  899. //
  900. // Are we already at the maximum then return.
  901. //
  902. if (Table->MaxBucket == HASH_MAX_BUCKET_COUNT) {
  903. Table->TableState = TABLE_STATE_STABLE;
  904. return;
  905. }
  906. //
  907. // If we have completed the split then set the state to stable and quit.
  908. //
  909. if (Table->MaxBucket == Table->SplitPoint) {
  910. Table->TableState = TABLE_STATE_STABLE;
  911. Table->MaxBucket *= 2;
  912. Table->SplitPoint = 0;
  913. return;
  914. }
  915. //
  916. // Check if we need allocate a new segment.
  917. //
  918. if (!FlagOn( Table->SplitPoint, (HASH_MAX_INDEX_COUNT - 1))) {
  919. //
  920. // If we can't allocate a new hash segment leave the table in its
  921. // old state and return - we can still use it as is
  922. //
  923. try {
  924. NtfsInitHashSegment( Table, (Table->MaxBucket + Table->SplitPoint) >> HASH_INDEX_SHIFT );
  925. } except( (GetExceptionCode() == STATUS_INSUFFICIENT_RESOURCES) ?
  926. EXCEPTION_EXECUTE_HANDLER :
  927. EXCEPTION_CONTINUE_SEARCH ) {
  928. return;
  929. }
  930. }
  931. //
  932. // Now perform the split on the next bucket.
  933. //
  934. NtfsGetHashSegmentAndIndex( Table->SplitPoint, &OldSegment, &OldIndex );
  935. NtfsGetHashSegmentAndIndex( Table->MaxBucket + Table->SplitPoint, &NewSegment, &NewIndex );
  936. CurrentOldEntry = (PNTFS_HASH_ENTRY *) &(*Table->HashSegments[ OldSegment ])[ OldIndex ];
  937. CurrentNewEntry = (PNTFS_HASH_ENTRY *) &(*Table->HashSegments[ NewSegment ])[ NewIndex ];
  938. Table->SplitPoint += 1;
  939. while (*CurrentOldEntry != NULL) {
  940. NtfsHashBucketFromHash( Table, (*CurrentOldEntry)->HashValue, &NextBucket );
  941. //
  942. // The entry belongs in the new bucket. Take it out of the existing
  943. // bucket and insert it at the head of the new bucket.
  944. //
  945. if (NextBucket >= Table->MaxBucket) {
  946. ASSERT( NextBucket == (Table->MaxBucket + Table->SplitPoint - 1) );
  947. CurrentEntry = *CurrentOldEntry;
  948. *CurrentOldEntry = CurrentEntry->NextEntry;
  949. CurrentEntry->NextEntry = *CurrentNewEntry;
  950. *CurrentNewEntry = CurrentEntry;
  951. //
  952. // Move to the next entry in the existing bucket.
  953. //
  954. } else {
  955. CurrentOldEntry = &(*CurrentOldEntry)->NextEntry;
  956. }
  957. }
  958. return;
  959. }
  960. #ifdef NTFS_HASH_DATA
  961. VOID
  962. NtfsFillHashHistogram (
  963. PNTFS_HASH_TABLE Table
  964. )
  965. {
  966. ULONG CurrentBucket = 0;
  967. ULONG Segment;
  968. ULONG Index;
  969. PNTFS_HASH_ENTRY NextEntry;
  970. ULONG Count;
  971. //
  972. // Zero the current histogram.
  973. //
  974. RtlZeroMemory( Table->Histogram, sizeof( Table->Histogram ));
  975. RtlZeroMemory( Table->ExtendedHistogram, sizeof( Table->ExtendedHistogram ));
  976. //
  977. // Walk through all of the buckets in use.
  978. //
  979. while (CurrentBucket < Table->MaxBucket + Table->SplitPoint) {
  980. Count = 0;
  981. NtfsGetHashSegmentAndIndex( CurrentBucket, &Segment, &Index );
  982. NextEntry = (*Table->HashSegments[ Segment ])[ Index ];
  983. //
  984. // Count the number of entries in each bucket.
  985. //
  986. while (NextEntry != NULL) {
  987. Count += 1;
  988. NextEntry = NextEntry->NextEntry;
  989. }
  990. //
  991. // Store it into the first histogram set if count is less than 16.
  992. //
  993. if (Count < 16) {
  994. Table->Histogram[Count] += 1;
  995. //
  996. // Store it in the last bucket if larger than our max.
  997. //
  998. } else if (Count >= 32) {
  999. Table->ExtendedHistogram[15] += 1;
  1000. //
  1001. // Otherwise store it into the extended histogram.
  1002. //
  1003. } else {
  1004. Table->ExtendedHistogram[(Count - 16) / 2] += 1;
  1005. }
  1006. CurrentBucket += 1;
  1007. }
  1008. }
  1009. #endif