Source code of Windows XP (NT5)
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.

6938 lines
210 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. IndexSup.c
  5. Abstract:
  6. This module implements the Index management routines for Ntfs
  7. Author:
  8. Tom Miller [TomM] 14-Jul-1991
  9. Revision History:
  10. --*/
  11. #include "NtfsProc.h"
  12. #include "Index.h"
  13. //
  14. // This constant affects the logic in NtfsRetrieveOtherFileName.
  15. // If an index is greater than this size, then we retrieve the other
  16. // name by reading the file record. The number is arbitrary, but the
  17. // below value should normally kick in for directories of around 150
  18. // to 200 files, or fewer if the names are quite large.
  19. //
  20. #define MAX_INDEX_TO_SCAN_FOR_NAMES (0x10000)
  21. #if DBG
  22. BOOLEAN NtfsIndexChecks = TRUE;
  23. #endif
  24. #if DBG
  25. #define CheckRoot() { \
  26. if (NtfsIndexChecks) { \
  27. NtfsCheckIndexRoot(Scb->Vcb, \
  28. (PINDEX_ROOT)NtfsAttributeValue(Attribute), \
  29. Attribute->Form.Resident.ValueLength); \
  30. } \
  31. }
  32. #define CheckBuffer(IB) { \
  33. if (NtfsIndexChecks) { \
  34. NtfsCheckIndexBuffer(Scb, \
  35. (IB)); \
  36. } \
  37. }
  38. #else
  39. #define CheckRoot() {NOTHING;}
  40. #define CheckBuffer(IB) {NOTHING;}
  41. #endif
  42. #define BINARY_SEARCH_ENTRIES (128)
  43. //
  44. // Local debug trace level
  45. //
  46. #define Dbg (DEBUG_TRACE_INDEXSUP)
  47. //
  48. // Define a tag for general pool allocations from this module
  49. //
  50. #undef MODULE_POOL_TAG
  51. #define MODULE_POOL_TAG ('IFtN')
  52. #ifdef ALLOC_PRAGMA
  53. #pragma alloc_text(PAGE, NtfsReinitializeIndexContext)
  54. #pragma alloc_text(PAGE, NtfsGrowLookupStack)
  55. #pragma alloc_text(PAGE, AddToIndex)
  56. #pragma alloc_text(PAGE, BinarySearchIndex)
  57. #pragma alloc_text(PAGE, DeleteFromIndex)
  58. #pragma alloc_text(PAGE, DeleteIndexBuffer)
  59. #pragma alloc_text(PAGE, DeleteSimple)
  60. #pragma alloc_text(PAGE, FindFirstIndexEntry)
  61. #pragma alloc_text(PAGE, FindMoveableIndexRoot)
  62. #pragma alloc_text(PAGE, FindNextIndexEntry)
  63. #pragma alloc_text(PAGE, GetIndexBuffer)
  64. #pragma alloc_text(PAGE, InsertSimpleAllocation)
  65. #pragma alloc_text(PAGE, InsertSimpleRoot)
  66. #pragma alloc_text(PAGE, InsertWithBufferSplit)
  67. #pragma alloc_text(PAGE, NtfsAddIndexEntry)
  68. #pragma alloc_text(PAGE, NtfsCleanupAfterEnumeration)
  69. #pragma alloc_text(PAGE, NtfsCleanupIndexContext)
  70. #pragma alloc_text(PAGE, NtfsContinueIndexEnumeration)
  71. #pragma alloc_text(PAGE, NtfsCreateIndex)
  72. #pragma alloc_text(PAGE, NtfsDeleteIndex)
  73. #pragma alloc_text(PAGE, NtfsDeleteIndexEntry)
  74. #pragma alloc_text(PAGE, NtfsFindIndexEntry)
  75. #pragma alloc_text(PAGE, NtfsInitializeIndexContext)
  76. #pragma alloc_text(PAGE, NtfsIsIndexEmpty)
  77. #pragma alloc_text(PAGE, NtfsPushIndexRoot)
  78. #pragma alloc_text(PAGE, NtfsRestartDeleteSimpleAllocation)
  79. #pragma alloc_text(PAGE, NtfsRestartDeleteSimpleRoot)
  80. #pragma alloc_text(PAGE, NtfsRestartIndexEnumeration)
  81. #pragma alloc_text(PAGE, NtfsRestartInsertSimpleAllocation)
  82. #pragma alloc_text(PAGE, NtfsRestartInsertSimpleRoot)
  83. #pragma alloc_text(PAGE, NtfsRestartSetIndexBlock)
  84. #pragma alloc_text(PAGE, NtfsRestartUpdateFileName)
  85. #pragma alloc_text(PAGE, NtfsRestartWriteEndOfIndex)
  86. #pragma alloc_text(PAGE, NtfsRetrieveOtherFileName)
  87. #pragma alloc_text(PAGE, NtfsUpdateFileNameInIndex)
  88. #pragma alloc_text(PAGE, NtfsUpdateIndexScbFromAttribute)
  89. #pragma alloc_text(PAGE, PruneIndex)
  90. #pragma alloc_text(PAGE, PushIndexRoot)
  91. #pragma alloc_text(PAGE, ReadIndexBuffer)
  92. #pragma alloc_text(PAGE, NtOfsRestartUpdateDataInIndex)
  93. #endif
  94. VOID
  95. NtfsCreateIndex (
  96. IN PIRP_CONTEXT IrpContext,
  97. IN OUT PFCB Fcb,
  98. IN ATTRIBUTE_TYPE_CODE IndexedAttributeType,
  99. IN COLLATION_RULE CollationRule,
  100. IN ULONG BytesPerIndexBuffer,
  101. IN UCHAR BlocksPerIndexBuffer,
  102. IN PATTRIBUTE_ENUMERATION_CONTEXT Context OPTIONAL,
  103. IN USHORT AttributeFlags,
  104. IN BOOLEAN NewIndex,
  105. IN BOOLEAN LogIt
  106. )
  107. /*++
  108. Routine Description:
  109. This routine may be called to create (or reinitialize) an index
  110. within a given file over a given attribute. For example, to create
  111. a normal directory, an index over the FILE_NAME attribute is created
  112. within the desired (directory) file.
  113. Arguments:
  114. Fcb - File in which the index is to be created.
  115. IndexedAttributeType - Type code of attribute to be indexed.
  116. CollationRule - Collation Rule for this index.
  117. BytesPerIndexBuffer - Number of bytes in an index buffer.
  118. BlocksPerIndexBuffer - Number of contiguous blocks to allocate for each
  119. index buffer allocated from the index allocation.
  120. Context - If reinitializing an existing index, this context must
  121. currently describe the INDEX_ROOT attribute. Must be
  122. supplied if NewIndex is FALSE.
  123. NewIndex - Supplied as FALSE to reinitialize an existing index, or
  124. TRUE if creating a new index.
  125. LogIt - May be supplied as FALSE by Create or Cleanup when already
  126. logging the creation or deletion of an entire file record.
  127. Otherwise must be specified as TRUE to allow logging.
  128. Return Value:
  129. None
  130. --*/
  131. {
  132. UNICODE_STRING AttributeName;
  133. WCHAR NameBuffer[10];
  134. ATTRIBUTE_ENUMERATION_CONTEXT LocalContext;
  135. ULONG idx;
  136. struct {
  137. INDEX_ROOT IndexRoot;
  138. INDEX_ENTRY EndEntry;
  139. } R;
  140. ASSERT_IRP_CONTEXT( IrpContext );
  141. ASSERT( NewIndex || ARGUMENT_PRESENT(Context) );
  142. PAGED_CODE();
  143. DebugTrace( +1, Dbg, ("NtfsCreateIndex\n") );
  144. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  145. DebugTrace( 0, Dbg, ("CollationRule = %08lx\n", CollationRule) );
  146. DebugTrace( 0, Dbg, ("BytesPerIndexBuffer = %08lx\n", BytesPerIndexBuffer) );
  147. DebugTrace( 0, Dbg, ("BlocksPerIndexBuffer = %08lx\n", BlocksPerIndexBuffer) );
  148. DebugTrace( 0, Dbg, ("Context = %08lx\n", Context) );
  149. DebugTrace( 0, Dbg, ("NewIndex = %02lx\n", NewIndex) );
  150. DebugTrace( 0, Dbg, ("LogIt = %02lx\n", LogIt) );
  151. //
  152. // First we will initialize the Index Root structure which is the value
  153. // of the attribute we need to create. We initialize it with 0 free bytes,
  154. // which means the first insert will have to expand the record
  155. //
  156. RtlZeroMemory( &R, sizeof(INDEX_ROOT) + sizeof(INDEX_ENTRY) );
  157. R.IndexRoot.IndexedAttributeType = IndexedAttributeType;
  158. R.IndexRoot.CollationRule = CollationRule;
  159. R.IndexRoot.BytesPerIndexBuffer = BytesPerIndexBuffer;
  160. R.IndexRoot.BlocksPerIndexBuffer = BlocksPerIndexBuffer;
  161. R.IndexRoot.IndexHeader.FirstIndexEntry = QuadAlign(sizeof(INDEX_HEADER));
  162. R.IndexRoot.IndexHeader.FirstFreeByte =
  163. R.IndexRoot.IndexHeader.BytesAvailable = QuadAlign(sizeof(INDEX_HEADER)) +
  164. QuadAlign(sizeof(INDEX_ENTRY));
  165. //
  166. // Now we need to put in the special End entry.
  167. //
  168. R.EndEntry.Length = sizeof(INDEX_ENTRY);
  169. SetFlag( R.EndEntry.Flags, INDEX_ENTRY_END );
  170. //
  171. // Now calculate the name which will be used to name the Index Root and
  172. // Index Allocation attributes for this index. It is $Ixxx, where "xxx"
  173. // is the attribute number being indexed in hex with leading 0's suppressed.
  174. //
  175. if (NewIndex) {
  176. //
  177. // First, there are some illegal values for the attribute code being indexed.
  178. //
  179. ASSERT( IndexedAttributeType < 0x10000000 );
  180. ASSERT( IndexedAttributeType != $UNUSED );
  181. //
  182. // Initialize the attribute name.
  183. //
  184. NameBuffer[0] = (WCHAR)'$';
  185. NameBuffer[1] = (WCHAR)'I';
  186. idx = 2;
  187. //
  188. // Now shift a "marker" into the low order nibble, so we know when to stop
  189. // shifting below.
  190. //
  191. IndexedAttributeType = (IndexedAttributeType << 4) + 0xF;
  192. //
  193. // Outer loop strips leading 0's
  194. //
  195. while (TRUE) {
  196. if ((IndexedAttributeType & 0xF0000000) == 0) {
  197. IndexedAttributeType <<= 4;
  198. } else {
  199. //
  200. // The inner loop forms the name until the marker is in the high
  201. // nibble.
  202. //
  203. while (IndexedAttributeType != 0xF0000000) {
  204. NameBuffer[idx] = (WCHAR)(IndexedAttributeType / 0x10000000 + '0');
  205. idx += 1;
  206. IndexedAttributeType <<= 4;
  207. }
  208. NameBuffer[idx] = UNICODE_NULL;
  209. break;
  210. }
  211. }
  212. RtlInitUnicodeString( &AttributeName, NameBuffer );
  213. //
  214. // Now, just create the Index Root Attribute.
  215. //
  216. Context = &LocalContext;
  217. NtfsInitializeAttributeContext( Context );
  218. }
  219. try {
  220. if (NewIndex) {
  221. LONGLONG Delta = NtfsResidentStreamQuota( Fcb->Vcb );
  222. NtfsConditionallyUpdateQuota( IrpContext,
  223. Fcb,
  224. &Delta,
  225. LogIt,
  226. TRUE );
  227. NtfsCreateAttributeWithValue( IrpContext,
  228. Fcb,
  229. $INDEX_ROOT,
  230. &AttributeName,
  231. &R,
  232. sizeof( INDEX_ROOT ) + sizeof( INDEX_ENTRY ),
  233. AttributeFlags,
  234. NULL,
  235. LogIt,
  236. Context );
  237. } else {
  238. NtfsChangeAttributeValue( IrpContext,
  239. Fcb,
  240. 0,
  241. &R,
  242. sizeof( INDEX_ROOT ) + sizeof( INDEX_ENTRY ),
  243. TRUE,
  244. FALSE,
  245. FALSE,
  246. TRUE,
  247. Context );
  248. }
  249. } finally {
  250. DebugUnwind( NtfsCreateIndex );
  251. if (NewIndex) {
  252. NtfsCleanupAttributeContext( IrpContext, Context );
  253. }
  254. }
  255. DebugTrace( -1, Dbg, ("NtfsCreateIndex -> VOID\n") );
  256. return;
  257. }
  258. VOID
  259. NtfsUpdateIndexScbFromAttribute (
  260. IN PIRP_CONTEXT IrpContext,
  261. IN PSCB Scb,
  262. IN PATTRIBUTE_RECORD_HEADER IndexRootAttr,
  263. IN ULONG MustBeFileName
  264. )
  265. /*++
  266. Routine Description:
  267. This routine is called when an Index Scb needs initialization. Typically
  268. once in the life of the Scb. It will update the Scb out of the $INDEX_ROOT
  269. attribute.
  270. Arguments:
  271. Scb - Supplies the Scb for the index.
  272. IndexRootAttr - Supplies the $INDEX_ROOT attribute.
  273. MustBeFileName - Force this to be a filename. Mark the volume dirty if
  274. the attribute isn't currently marked as such but let processing continue.
  275. This is used to continue mounting a volume where either the root directory or
  276. the $Extend directory is incorrectly marked.
  277. Return Value:
  278. None
  279. --*/
  280. {
  281. PINDEX_ROOT IndexRoot = (PINDEX_ROOT) NtfsAttributeValue( IndexRootAttr );
  282. PAGED_CODE();
  283. //
  284. // Update the Scb out of the attribute.
  285. //
  286. SetFlag( Scb->AttributeFlags,
  287. IndexRootAttr->Flags & (ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_ENCRYPTED) );
  288. //
  289. // Capture the values out of the attribute. Note that we load the
  290. // BytesPerIndexBuffer last as a flag to indicate that the Scb is
  291. // loaded.
  292. //
  293. Scb->ScbType.Index.CollationRule = IndexRoot->CollationRule;
  294. Scb->ScbType.Index.BlocksPerIndexBuffer = IndexRoot->BlocksPerIndexBuffer;
  295. //
  296. // Check if we must collate on the file name.
  297. //
  298. if (MustBeFileName) {
  299. Scb->ScbType.Index.AttributeBeingIndexed = $FILE_NAME;
  300. } else if (!FlagOn( Scb->ScbState, SCB_STATE_VIEW_INDEX )) {
  301. Scb->ScbType.Index.AttributeBeingIndexed = IndexRoot->IndexedAttributeType;
  302. }
  303. //
  304. // If the type code is $FILE_NAME then make sure the collation type
  305. // is FILE_NAME.
  306. //
  307. if (Scb->ScbType.Index.AttributeBeingIndexed == $FILE_NAME) {
  308. if (IndexRoot->CollationRule != COLLATION_FILE_NAME) {
  309. ASSERTMSG( "Invalid collation rule", FALSE );
  310. ASSERT( !FlagOn( Scb->ScbState, SCB_STATE_VIEW_INDEX ));
  311. NtfsMarkVolumeDirty( IrpContext, Scb->Vcb, TRUE );
  312. Scb->ScbType.Index.CollationRule = COLLATION_FILE_NAME;
  313. }
  314. }
  315. //
  316. // Compute the shift count for this index.
  317. //
  318. if (IndexRoot->BytesPerIndexBuffer >= Scb->Vcb->BytesPerCluster) {
  319. Scb->ScbType.Index.IndexBlockByteShift = (UCHAR) Scb->Vcb->ClusterShift;
  320. } else {
  321. Scb->ScbType.Index.IndexBlockByteShift = DEFAULT_INDEX_BLOCK_BYTE_SHIFT;
  322. }
  323. Scb->ScbType.Index.BytesPerIndexBuffer = IndexRoot->BytesPerIndexBuffer;
  324. return;
  325. }
  326. BOOLEAN
  327. NtfsFindIndexEntry (
  328. IN PIRP_CONTEXT IrpContext,
  329. IN PSCB Scb,
  330. IN PVOID Value,
  331. IN BOOLEAN IgnoreCase,
  332. OUT PQUICK_INDEX QuickIndex OPTIONAL,
  333. OUT PBCB *Bcb,
  334. OUT PINDEX_ENTRY *IndexEntry,
  335. OUT PINDEX_CONTEXT IndexContext OPTIONAL
  336. )
  337. /*++
  338. Routine Description:
  339. This routine may be called to look up a given value in a given index
  340. and return the file reference of the indexed file record.
  341. Arguments:
  342. Scb - Supplies the Scb for the index.
  343. Value - Supplies a pointer to the value to lookup.
  344. IgnoreCase - For indices with collation rules where character case
  345. may be relevant, supplies whether character case is
  346. to be ignored. For example, if supplied as TRUE, then
  347. 'T' and 't' are treated as equivalent.
  348. QuickIndex - If specified, supplies a pointer to a quick lookup structure
  349. to be updated by this routine.
  350. Bcb - Returns a Bcb pointer which must be unpinned by the caller
  351. IndexEntry - Returns a pointer to the actual Index Entry, valid until
  352. the Bcb is unpinned.
  353. IndexContext - If specified then this is an initialized index context.
  354. It is used to insert a new entry later if this search
  355. doesn't find a match.
  356. Return Value:
  357. FALSE - if no match was found.
  358. TRUE - if a match was found and being returned in FileReference.
  359. --*/
  360. {
  361. PINDEX_CONTEXT LocalIndexContext;
  362. BOOLEAN Result = FALSE;
  363. ASSERT_IRP_CONTEXT( IrpContext );
  364. ASSERT_SCB( Scb );
  365. ASSERT_SHARED_SCB( Scb );
  366. PAGED_CODE();
  367. DebugTrace( +1, Dbg, ("NtfsFindIndexEntry\n") );
  368. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  369. DebugTrace( 0, Dbg, ("Value = %08lx\n", Value) );
  370. DebugTrace( 0, Dbg, ("IgnoreCase = %02lx\n", IgnoreCase) );
  371. //
  372. // Check if we need to initialize a local structure.
  373. //
  374. if (ARGUMENT_PRESENT( IndexContext )) {
  375. LocalIndexContext = IndexContext;
  376. } else {
  377. LocalIndexContext = NtfsAllocateFromStack( sizeof( INDEX_CONTEXT ));
  378. NtfsInitializeIndexContext( LocalIndexContext );
  379. }
  380. try {
  381. //
  382. // Position to first possible match.
  383. //
  384. FindFirstIndexEntry( IrpContext,
  385. Scb,
  386. Value,
  387. LocalIndexContext );
  388. //
  389. // We are doing a direct compare in FindNextIndexEntry below
  390. // so we don't have to upcase Value. The name compare routine
  391. // called later will upcase both.
  392. //
  393. if (FindNextIndexEntry( IrpContext,
  394. Scb,
  395. Value,
  396. FALSE,
  397. IgnoreCase,
  398. LocalIndexContext,
  399. FALSE,
  400. NULL )) {
  401. //
  402. // Return our outputs, clearing the Bcb so it won't get
  403. // unpinned.
  404. //
  405. *IndexEntry = LocalIndexContext->Current->IndexEntry;
  406. //
  407. // Now return the correct Bcb.
  408. //
  409. if (LocalIndexContext->Current == LocalIndexContext->Base) {
  410. *Bcb = NtfsFoundBcb(&LocalIndexContext->AttributeContext);
  411. NtfsFoundBcb(&LocalIndexContext->AttributeContext) = NULL;
  412. if (ARGUMENT_PRESENT( QuickIndex )) {
  413. QuickIndex->BufferOffset = 0;
  414. }
  415. } else {
  416. PINDEX_LOOKUP_STACK Sp = LocalIndexContext->Current;
  417. *Bcb = Sp->Bcb;
  418. Sp->Bcb = NULL;
  419. if (ARGUMENT_PRESENT( QuickIndex )) {
  420. QuickIndex->ChangeCount = Scb->ScbType.Index.ChangeCount;
  421. QuickIndex->BufferOffset = PtrOffset( Sp->StartOfBuffer, Sp->IndexEntry );
  422. QuickIndex->CapturedLsn = ((PINDEX_ALLOCATION_BUFFER) Sp->StartOfBuffer)->Lsn;
  423. QuickIndex->IndexBlock = ((PINDEX_ALLOCATION_BUFFER) Sp->StartOfBuffer)->ThisBlock;
  424. }
  425. }
  426. try_return(Result = TRUE);
  427. } else {
  428. try_return(Result = FALSE);
  429. }
  430. try_exit: NOTHING;
  431. } finally{
  432. DebugUnwind( NtfsFindIndexEntry );
  433. if (!ARGUMENT_PRESENT( IndexContext )) {
  434. NtfsCleanupIndexContext( IrpContext, LocalIndexContext );
  435. }
  436. }
  437. DebugTrace( 0, Dbg, ("Bcb < %08lx\n", *Bcb) );
  438. DebugTrace( 0, Dbg, ("IndexEntry < %08lx\n", *IndexEntry) );
  439. DebugTrace( -1, Dbg, ("NtfsFindIndexEntry -> %08lx\n", Result) );
  440. return Result;
  441. }
  442. VOID
  443. NtfsUpdateFileNameInIndex (
  444. IN PIRP_CONTEXT IrpContext,
  445. IN PSCB Scb,
  446. IN PFILE_NAME FileName,
  447. IN PDUPLICATED_INFORMATION Info,
  448. IN OUT PQUICK_INDEX QuickIndex OPTIONAL
  449. )
  450. /*++
  451. Routine Description:
  452. This routine may be called to look up a given value in a given index
  453. and pin it for modification.
  454. Arguments:
  455. Scb - Supplies the Scb for the index.
  456. FileName - Supplies a pointer to the file name to lookup.
  457. Info - Supplies a pointer to the information for the update
  458. QuickIndex - If present, this is the fast lookup information for this index entry.
  459. Return Value:
  460. None.
  461. --*/
  462. {
  463. INDEX_CONTEXT IndexContext;
  464. PINDEX_ENTRY IndexEntry;
  465. PFILE_NAME FileNameInIndex;
  466. PVCB Vcb = Scb->Vcb;
  467. PINDEX_LOOKUP_STACK Sp;
  468. PINDEX_ALLOCATION_BUFFER IndexBuffer;
  469. ASSERT_IRP_CONTEXT( IrpContext );
  470. ASSERT_SCB( Scb );
  471. PAGED_CODE();
  472. DebugTrace( +1, Dbg, ("NtfsUpdateFileNameInIndex\n") );
  473. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  474. DebugTrace( 0, Dbg, ("FileName = %08lx\n", FileName) );
  475. DebugTrace( 0, Dbg, ("Info = %08lx\n", Info) );
  476. NtfsInitializeIndexContext( &IndexContext );
  477. try {
  478. //
  479. // If the index entry for this filename hasn't moved we can go
  480. // directly to the location in the buffer. For this to be the case the
  481. // following must be true.
  482. //
  483. // - The entry must already be in an index buffer
  484. // - The index stream may not have been truncated
  485. // - The Lsn in the page can't have changed
  486. //
  487. if (ARGUMENT_PRESENT( QuickIndex ) &&
  488. QuickIndex->BufferOffset != 0 &&
  489. QuickIndex->ChangeCount == Scb->ScbType.Index.ChangeCount) {
  490. //
  491. // Use the top location in the index context to perform the
  492. // read.
  493. //
  494. Sp = IndexContext.Base;
  495. ReadIndexBuffer( IrpContext,
  496. Scb,
  497. QuickIndex->IndexBlock,
  498. FALSE,
  499. Sp );
  500. //
  501. // If the Lsn matches then we can use this buffer directly.
  502. //
  503. if (QuickIndex->CapturedLsn.QuadPart == Sp->CapturedLsn.QuadPart) {
  504. IndexBuffer = (PINDEX_ALLOCATION_BUFFER) Sp->StartOfBuffer;
  505. IndexEntry = (PINDEX_ENTRY) Add2Ptr( Sp->StartOfBuffer,
  506. QuickIndex->BufferOffset );
  507. FileNameInIndex = (PFILE_NAME)(IndexEntry + 1);
  508. //
  509. // Pin the index buffer
  510. //
  511. NtfsPinMappedData( IrpContext,
  512. Scb,
  513. LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  514. Scb->ScbType.Index.BytesPerIndexBuffer,
  515. &Sp->Bcb );
  516. //
  517. // Write a log record to change our ParentIndexEntry.
  518. //
  519. //
  520. // Write the log record, but do not update the IndexBuffer Lsn,
  521. // since nothing moved and we don't want to force index contexts
  522. // to have to rescan.
  523. //
  524. // Indexbuffer->Lsn =
  525. //
  526. NtfsWriteLog( IrpContext,
  527. Scb,
  528. Sp->Bcb,
  529. UpdateFileNameAllocation,
  530. Info,
  531. sizeof(DUPLICATED_INFORMATION),
  532. UpdateFileNameAllocation,
  533. &FileNameInIndex->Info,
  534. sizeof(DUPLICATED_INFORMATION),
  535. LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  536. 0,
  537. (ULONG)((PCHAR)IndexEntry - (PCHAR)IndexBuffer),
  538. Scb->ScbType.Index.BytesPerIndexBuffer );
  539. //
  540. // Now call the Restart routine to do it.
  541. //
  542. NtfsRestartUpdateFileName( IndexEntry,
  543. Info );
  544. try_return( NOTHING );
  545. //
  546. // Otherwise we need to reinitialize the index context and take
  547. // the long path below.
  548. //
  549. } else {
  550. NtfsReinitializeIndexContext( IrpContext, &IndexContext );
  551. }
  552. }
  553. //
  554. // Position to first possible match.
  555. //
  556. FindFirstIndexEntry( IrpContext,
  557. Scb,
  558. (PVOID)FileName,
  559. &IndexContext );
  560. //
  561. // See if there is an actual match.
  562. //
  563. if (FindNextIndexEntry( IrpContext,
  564. Scb,
  565. (PVOID)FileName,
  566. FALSE,
  567. FALSE,
  568. &IndexContext,
  569. FALSE,
  570. NULL )) {
  571. //
  572. // Point to the index entry and the file name within it.
  573. //
  574. IndexEntry = IndexContext.Current->IndexEntry;
  575. FileNameInIndex = (PFILE_NAME)(IndexEntry + 1);
  576. //
  577. // Now pin the entry.
  578. //
  579. if (IndexContext.Current == IndexContext.Base) {
  580. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  581. PATTRIBUTE_RECORD_HEADER Attribute;
  582. PATTRIBUTE_ENUMERATION_CONTEXT Context = &IndexContext.AttributeContext;
  583. //
  584. // Pin the root
  585. //
  586. NtfsPinMappedAttribute( IrpContext,
  587. Vcb,
  588. Context );
  589. //
  590. // Write a log record to change our ParentIndexEntry.
  591. //
  592. FileRecord = NtfsContainingFileRecord(Context);
  593. Attribute = NtfsFoundAttribute(Context);
  594. //
  595. // Write the log record, but do not update the FileRecord Lsn,
  596. // since nothing moved and we don't want to force index contexts
  597. // to have to rescan.
  598. //
  599. // FileRecord->Lsn =
  600. //
  601. NtfsWriteLog( IrpContext,
  602. Vcb->MftScb,
  603. NtfsFoundBcb(Context),
  604. UpdateFileNameRoot,
  605. Info,
  606. sizeof(DUPLICATED_INFORMATION),
  607. UpdateFileNameRoot,
  608. &FileNameInIndex->Info,
  609. sizeof(DUPLICATED_INFORMATION),
  610. NtfsMftOffset( Context ),
  611. (ULONG)((PCHAR)Attribute - (PCHAR)FileRecord),
  612. (ULONG)((PCHAR)IndexEntry - (PCHAR)Attribute),
  613. Vcb->BytesPerFileRecordSegment );
  614. if (ARGUMENT_PRESENT( QuickIndex )) {
  615. QuickIndex->BufferOffset = 0;
  616. }
  617. } else {
  618. PINDEX_ALLOCATION_BUFFER IndexBuffer;
  619. Sp = IndexContext.Current;
  620. IndexBuffer = (PINDEX_ALLOCATION_BUFFER)Sp->StartOfBuffer;
  621. //
  622. // Pin the index buffer
  623. //
  624. NtfsPinMappedData( IrpContext,
  625. Scb,
  626. LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  627. Scb->ScbType.Index.BytesPerIndexBuffer,
  628. &Sp->Bcb );
  629. //
  630. // Write a log record to change our ParentIndexEntry.
  631. //
  632. //
  633. // Write the log record, but do not update the IndexBuffer Lsn,
  634. // since nothing moved and we don't want to force index contexts
  635. // to have to rescan.
  636. //
  637. // Indexbuffer->Lsn =
  638. //
  639. NtfsWriteLog( IrpContext,
  640. Scb,
  641. Sp->Bcb,
  642. UpdateFileNameAllocation,
  643. Info,
  644. sizeof(DUPLICATED_INFORMATION),
  645. UpdateFileNameAllocation,
  646. &FileNameInIndex->Info,
  647. sizeof(DUPLICATED_INFORMATION),
  648. LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  649. 0,
  650. (ULONG)((PCHAR)Sp->IndexEntry - (PCHAR)IndexBuffer),
  651. Scb->ScbType.Index.BytesPerIndexBuffer );
  652. if (ARGUMENT_PRESENT( QuickIndex )) {
  653. QuickIndex->ChangeCount = Scb->ScbType.Index.ChangeCount;
  654. QuickIndex->BufferOffset = PtrOffset( Sp->StartOfBuffer, Sp->IndexEntry );
  655. QuickIndex->CapturedLsn = ((PINDEX_ALLOCATION_BUFFER) Sp->StartOfBuffer)->Lsn;
  656. QuickIndex->IndexBlock = ((PINDEX_ALLOCATION_BUFFER) Sp->StartOfBuffer)->ThisBlock;
  657. }
  658. }
  659. //
  660. // Now call the Restart routine to do it.
  661. //
  662. NtfsRestartUpdateFileName( IndexEntry,
  663. Info );
  664. //
  665. // If the file name is not in the index, this is a bad file.
  666. //
  667. } else {
  668. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  669. }
  670. try_exit: NOTHING;
  671. } finally{
  672. DebugUnwind( NtfsUpdateFileNameInIndex );
  673. NtfsCleanupIndexContext( IrpContext, &IndexContext );
  674. }
  675. DebugTrace( -1, Dbg, ("NtfsUpdateFileNameInIndex -> VOID\n") );
  676. return;
  677. }
  678. VOID
  679. NtfsAddIndexEntry (
  680. IN PIRP_CONTEXT IrpContext,
  681. IN PSCB Scb,
  682. IN PVOID Value,
  683. IN ULONG ValueLength,
  684. IN PFILE_REFERENCE FileReference,
  685. IN PINDEX_CONTEXT IndexContext OPTIONAL,
  686. OUT PQUICK_INDEX QuickIndex OPTIONAL
  687. )
  688. /*++
  689. Routine Description:
  690. This routine may be called to add an entry to an index. This routine
  691. always allows duplicates. If duplicates are not allowed, it is the
  692. caller's responsibility to detect and eliminate any duplicate before
  693. calling this routine.
  694. Arguments:
  695. Scb - Supplies the Scb for the index.
  696. Value - Supplies a pointer to the value to add to the index
  697. ValueLength - Supplies the length of the value in bytes.
  698. FileReference - Supplies the file reference to place with the index entry.
  699. QuickIndex - If specified we store the location of the index added.
  700. IndexContext - If specified, previous result of doing the lookup for the name in the index.
  701. Return Value:
  702. None
  703. --*/
  704. {
  705. INDEX_CONTEXT IndexContextStruct;
  706. PINDEX_CONTEXT LocalIndexContext;
  707. struct {
  708. INDEX_ENTRY IndexEntry;
  709. PVOID Value;
  710. PVOID MustBeNull;
  711. } IE;
  712. ASSERT_IRP_CONTEXT( IrpContext );
  713. ASSERT_SCB( Scb );
  714. ASSERT_EXCLUSIVE_SCB( Scb );
  715. ASSERT( (Scb->ScbType.Index.CollationRule != COLLATION_FILE_NAME) ||
  716. ( *(PLONGLONG)&((PFILE_NAME)Value)->ParentDirectory ==
  717. *(PLONGLONG)&Scb->Fcb->FileReference ) );
  718. PAGED_CODE();
  719. DebugTrace( +1, Dbg, ("NtfsAddIndexEntry\n") );
  720. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  721. DebugTrace( 0, Dbg, ("Value = %08lx\n", Value) );
  722. DebugTrace( 0, Dbg, ("ValueLength = %08lx\n", ValueLength) );
  723. DebugTrace( 0, Dbg, ("FileReference = %08lx\n", FileReference) );
  724. //
  725. // Remember if we are using the local or input IndexContext.
  726. //
  727. if (ARGUMENT_PRESENT( IndexContext )) {
  728. LocalIndexContext = IndexContext;
  729. } else {
  730. LocalIndexContext = &IndexContextStruct;
  731. NtfsInitializeIndexContext( LocalIndexContext );
  732. }
  733. try {
  734. //
  735. // Do the lookup again if we don't have a context.
  736. //
  737. if (!ARGUMENT_PRESENT( IndexContext )) {
  738. //
  739. // Position to first possible match.
  740. //
  741. FindFirstIndexEntry( IrpContext,
  742. Scb,
  743. Value,
  744. LocalIndexContext );
  745. //
  746. // See if there is an actual match.
  747. //
  748. if (FindNextIndexEntry( IrpContext,
  749. Scb,
  750. Value,
  751. FALSE,
  752. FALSE,
  753. LocalIndexContext,
  754. FALSE,
  755. NULL )) {
  756. ASSERTMSG( "NtfsAddIndexEntry already exists", FALSE );
  757. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  758. }
  759. }
  760. //
  761. // Initialize the Index Entry in pointer form.
  762. //
  763. IE.IndexEntry.FileReference = *FileReference;
  764. IE.IndexEntry.Length = (USHORT)(sizeof(INDEX_ENTRY) + QuadAlign(ValueLength));
  765. IE.IndexEntry.AttributeLength = (USHORT)ValueLength;
  766. IE.IndexEntry.Flags = INDEX_ENTRY_POINTER_FORM;
  767. IE.IndexEntry.Reserved = 0;
  768. IE.Value = Value;
  769. IE.MustBeNull = NULL;
  770. //
  771. // Now add it to the index. We can only add to a leaf, so force our
  772. // position back to the correct spot in a leaf first.
  773. //
  774. LocalIndexContext->Current = LocalIndexContext->Top;
  775. AddToIndex( IrpContext, Scb, (PINDEX_ENTRY)&IE, LocalIndexContext, QuickIndex, FALSE );
  776. } finally{
  777. DebugUnwind( NtfsAddIndexEntry );
  778. if (!ARGUMENT_PRESENT( IndexContext )) {
  779. NtfsCleanupIndexContext( IrpContext, LocalIndexContext );
  780. }
  781. }
  782. DebugTrace( -1, Dbg, ("NtfsAddIndexEntry -> VOID\n") );
  783. return;
  784. }
  785. VOID
  786. NtfsDeleteIndexEntry (
  787. IN PIRP_CONTEXT IrpContext,
  788. IN PSCB Scb,
  789. IN PVOID Value,
  790. IN PFILE_REFERENCE FileReference
  791. )
  792. /*++
  793. Routine Description:
  794. This routine may be called to delete a specified index entry. The
  795. first entry is removed which matches the value exactly (including in Case,
  796. if relevant) and the file reference.
  797. Arguments:
  798. Scb - Supplies the Scb for the index.
  799. Value - Supplies a pointer to the value to delete from the index.
  800. FileReference - Supplies the file reference of the index entry.
  801. Return Value:
  802. None
  803. --*/
  804. {
  805. INDEX_CONTEXT IndexContext;
  806. PINDEX_ENTRY IndexEntry;
  807. ASSERT_IRP_CONTEXT( IrpContext );
  808. ASSERT_SCB( Scb );
  809. ASSERT_EXCLUSIVE_SCB( Scb );
  810. PAGED_CODE();
  811. DebugTrace( +1, Dbg, ("NtfsDeleteIndexEntry\n") );
  812. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  813. DebugTrace( 0, Dbg, ("Value = %08lx\n", Value) );
  814. DebugTrace( 0, Dbg, ("FileReference = %08lx\n", FileReference) );
  815. NtfsInitializeIndexContext( &IndexContext );
  816. try {
  817. //
  818. // Position to first possible match.
  819. //
  820. FindFirstIndexEntry( IrpContext,
  821. Scb,
  822. Value,
  823. &IndexContext );
  824. //
  825. // See if there is an actual match.
  826. //
  827. if (!FindNextIndexEntry( IrpContext,
  828. Scb,
  829. Value,
  830. FALSE,
  831. FALSE,
  832. &IndexContext,
  833. FALSE,
  834. NULL )) {
  835. ASSERTMSG( "NtfsDeleteIndexEntry does not exist", FALSE );
  836. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  837. }
  838. //
  839. // Extract the found index entry pointer.
  840. //
  841. IndexEntry = IndexContext.Current->IndexEntry;
  842. //
  843. // If the file reference also matches, then this is the one we
  844. // are supposed to delete.
  845. //
  846. if (!NtfsEqualMftRef(&IndexEntry->FileReference, FileReference)) {
  847. ASSERTMSG( "NtfsDeleteIndexEntry unexpected file reference", FALSE );
  848. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  849. }
  850. DeleteFromIndex( IrpContext, Scb, &IndexContext );
  851. } finally{
  852. DebugUnwind( NtfsDeleteIndexEntry );
  853. NtfsCleanupIndexContext( IrpContext, &IndexContext );
  854. }
  855. DebugTrace( -1, Dbg, ("NtfsDeleteIndexEntry -> VOID\n") );
  856. return;
  857. }
  858. VOID
  859. NtfsPushIndexRoot (
  860. IN PIRP_CONTEXT IrpContext,
  861. IN PSCB Scb
  862. )
  863. /*++
  864. Routine Description:
  865. This routine may be called to "push" the index root, i.e., add another
  866. level to the BTree, to make more room in the file record.
  867. Arguments:
  868. Scb - Supplies the Scb for the index.
  869. Return Value:
  870. None
  871. --*/
  872. {
  873. INDEX_CONTEXT IndexContext;
  874. PINDEX_LOOKUP_STACK Sp;
  875. ASSERT_IRP_CONTEXT( IrpContext );
  876. ASSERT_SCB( Scb );
  877. ASSERT_EXCLUSIVE_SCB( Scb );
  878. PAGED_CODE();
  879. DebugTrace( +1, Dbg, ("NtfsPushIndexRoot\n") );
  880. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  881. NtfsInitializeIndexContext( &IndexContext );
  882. try {
  883. //
  884. // Position to first possible match.
  885. //
  886. FindFirstIndexEntry( IrpContext,
  887. Scb,
  888. NULL,
  889. &IndexContext );
  890. //
  891. // See if the stack will have to be grown to do the push
  892. //
  893. Sp = IndexContext.Top + 1;
  894. if (Sp >= IndexContext.Base + (ULONG)IndexContext.NumberEntries) {
  895. NtfsGrowLookupStack( Scb,
  896. &IndexContext,
  897. &Sp );
  898. }
  899. PushIndexRoot( IrpContext, Scb, &IndexContext );
  900. } finally{
  901. DebugUnwind( NtfsPushIndexRoot );
  902. NtfsCleanupIndexContext( IrpContext, &IndexContext );
  903. }
  904. DebugTrace( -1, Dbg, ("NtfsPushIndexRoot -> VOID\n") );
  905. return;
  906. }
  907. BOOLEAN
  908. NtfsRestartIndexEnumeration (
  909. IN PIRP_CONTEXT IrpContext,
  910. IN PCCB Ccb,
  911. IN PSCB Scb,
  912. IN PVOID Value,
  913. IN BOOLEAN IgnoreCase,
  914. IN BOOLEAN NextFlag,
  915. OUT PINDEX_ENTRY *IndexEntry,
  916. IN PFCB AcquiredFcb
  917. )
  918. /*++
  919. Routine Description:
  920. This routine may be called to start or restart an index enumeration,
  921. according to the parameters as described below. The first matching
  922. entry, if any, is returned by this call. Subsequent entries, if any,
  923. may be returned by subsequent calls to NtfsContinueIndexEnumeration.
  924. For each entry found, a pointer is returned to a copy of the entry, in
  925. dynamically allocated pool pointed to by the Ccb. Therefore, there is
  926. nothing for the caller to unpin.
  927. Note that the Value, ValueLength, and IgnoreCase parameters on the first
  928. call for a given Ccb fix what will be returned for this Ccb forever. A
  929. subsequent call to this routine may also specify these parameters, but
  930. in this case these parameters will be used for positioning only; all
  931. matches returned will continue to match the value and IgnoreCase flag
  932. specified on the first call for the Ccb.
  933. Note that all calls to this routine must be from within a try-finally,
  934. and the finally clause must include a call to NtfsCleanupAfterEnumeration.
  935. Arguments:
  936. Ccb - Pointer to the Ccb for this enumeration.
  937. Scb - Supplies the Scb for the index.
  938. Value - Pointer to the value containing the pattern which is to match
  939. all returns for enumerations on this Ccb.
  940. IgnoreCase - If FALSE, all returns will match the pattern value with
  941. exact case (if relevant). If TRUE, all returns will match
  942. the pattern value ignoring case. On a second or subsequent
  943. call for a Ccb, this flag may be specified differently just
  944. for positioning. For example, an IgnoreCase TRUE enumeration
  945. may be restarted at a previously returned value found by exact
  946. case match.
  947. NextFlag - FALSE if the first match of the enumeration is to be returned.
  948. TRUE if the next match after the first one is to be returned.
  949. IndexEntry - Returns a pointer to a copy of the index entry.
  950. AcquiredFcb - Supplies a pointer to an Fcb which has been preacquired to
  951. potentially aide NtfsRetrieveOtherFileName
  952. Return Value:
  953. FALSE - If no match is being returned, and the output pointer is undefined.
  954. TRUE - If a match is being returned.
  955. --*/
  956. {
  957. PINDEX_ENTRY FoundIndexEntry;
  958. INDEX_CONTEXT OtherContext;
  959. BOOLEAN WildCardsInExpression;
  960. BOOLEAN SynchronizationError;
  961. PWCH UpcaseTable = IrpContext->Vcb->UpcaseTable;
  962. PINDEX_CONTEXT IndexContext = NULL;
  963. BOOLEAN CleanupOtherContext = FALSE;
  964. BOOLEAN Result = FALSE;
  965. BOOLEAN ContextJustCreated = FALSE;
  966. PAGED_CODE();
  967. ASSERT_IRP_CONTEXT( IrpContext );
  968. ASSERT_CCB( Ccb );
  969. ASSERT_SCB( Scb );
  970. ASSERT_SHARED_SCB( Scb );
  971. ASSERT( ARGUMENT_PRESENT(Value) || (Ccb->IndexContext != NULL) );
  972. DebugTrace( +1, Dbg, ("NtfsRestartIndexEnumeration\n") );
  973. DebugTrace( 0, Dbg, ("Ccb = %08lx\n", Ccb) );
  974. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  975. DebugTrace( 0, Dbg, ("Value = %08lx\n", Value) );
  976. DebugTrace( 0, Dbg, ("IgnoreCase = %02lx\n", IgnoreCase) );
  977. DebugTrace( 0, Dbg, ("NextFlag = %02lx\n", NextFlag) );
  978. try {
  979. //
  980. // If the Ccb does not yet have an index context, then we must
  981. // allocate one and initialize this Context and the Ccb as well.
  982. //
  983. if (Ccb->IndexContext == NULL) {
  984. //
  985. // Allocate and initialize the index context.
  986. //
  987. Ccb->IndexContext = (PINDEX_CONTEXT)ExAllocateFromPagedLookasideList( &NtfsIndexContextLookasideList );
  988. NtfsInitializeIndexContext( Ccb->IndexContext );
  989. ContextJustCreated = TRUE;
  990. //
  991. // Capture the caller's IgnoreCase flag.
  992. //
  993. if (IgnoreCase) {
  994. SetFlag( Ccb->Flags, CCB_FLAG_IGNORE_CASE );
  995. }
  996. }
  997. //
  998. // Pick up the pointer to the index context, and save the current
  999. // change count from the Scb.
  1000. //
  1001. IndexContext = Ccb->IndexContext;
  1002. //
  1003. // The first step of enumeration is to position our IndexContext.
  1004. //
  1005. FindFirstIndexEntry( IrpContext,
  1006. Scb,
  1007. Value,
  1008. IndexContext );
  1009. //
  1010. // The following code only applies to file name indices.
  1011. //
  1012. if (!FlagOn( Scb->ScbState, SCB_STATE_VIEW_INDEX )) {
  1013. //
  1014. // Remember if there are wild cards.
  1015. //
  1016. if ((*NtfsContainsWildcards[Scb->ScbType.Index.CollationRule])
  1017. ( Value )) {
  1018. WildCardsInExpression = TRUE;
  1019. } else {
  1020. WildCardsInExpression = FALSE;
  1021. }
  1022. //
  1023. // If the operation is caseinsensitive, upcase the string.
  1024. //
  1025. if (IgnoreCase) {
  1026. (*NtfsUpcaseValue[Scb->ScbType.Index.CollationRule])
  1027. ( UpcaseTable,
  1028. IrpContext->Vcb->UpcaseTableSize,
  1029. Value );
  1030. }
  1031. } else {
  1032. //
  1033. // For view indices, it is implied that all searches
  1034. // are wildcard searches.
  1035. //
  1036. WildCardsInExpression = TRUE;
  1037. }
  1038. //
  1039. // If this is not actually the first call, then we have to
  1040. // position exactly to the Value specified, and set NextFlag
  1041. // correctly. The first call can either the initial call
  1042. // to query or the first call after a restart.
  1043. //
  1044. if (!ContextJustCreated && NextFlag) {
  1045. PIS_IN_EXPRESSION MatchRoutine;
  1046. PFILE_NAME NameInIndex;
  1047. BOOLEAN ItsThere;
  1048. //
  1049. // See if the specified value is actually there, because
  1050. // we are not allowed to resume from a Dos-only name.
  1051. //
  1052. ItsThere = FindNextIndexEntry( IrpContext,
  1053. Scb,
  1054. Value,
  1055. WildCardsInExpression,
  1056. IgnoreCase,
  1057. IndexContext,
  1058. FALSE,
  1059. NULL );
  1060. //
  1061. // We will set up pointers from our returns, but we must
  1062. // be careful only to use them if we found something.
  1063. //
  1064. FoundIndexEntry = IndexContext->Current->IndexEntry;
  1065. NameInIndex = (PFILE_NAME)(FoundIndexEntry + 1);
  1066. //
  1067. // Figure out which match routine to use.
  1068. //
  1069. if (FlagOn(Ccb->Flags, CCB_FLAG_WILDCARD_IN_EXPRESSION)) {
  1070. MatchRoutine = NtfsIsInExpression[COLLATION_FILE_NAME];
  1071. } else {
  1072. MatchRoutine = (PIS_IN_EXPRESSION)NtfsIsEqual[COLLATION_FILE_NAME];
  1073. }
  1074. //
  1075. // If we are trying to resume from a Ntfs-only or Dos-Only name, then
  1076. // we take action here. Do not do this on the internal
  1077. // call from NtfsContinueIndexEnumeration, which is the
  1078. // only one who would point at the index entry in the Ccb.
  1079. //
  1080. // We can think of this code this way. No matter what our search
  1081. // expression is, we traverse the index only one way. For each
  1082. // name we find, we will only return the file name once - either
  1083. // from an Ntfs-only match or from a Dos-only match if the Ntfs-only
  1084. // name does not match. Regardless of whether resuming from the
  1085. // Ntfs-Only or Dos-only name, we still can determine a unique
  1086. // position in the directory. That unique position is the Ntfs-only
  1087. // name if it matches the expression, or else the Dos-only name if
  1088. // it only matches. In the illegal case that neither matches, we
  1089. // arbitrarily resume from the Ntfs-only name.
  1090. //
  1091. // This code may be read aloud to the tune
  1092. // "While My Heart Gently Weeps"
  1093. //
  1094. if (ItsThere &&
  1095. (Value != (PVOID)(Ccb->IndexEntry + 1)) &&
  1096. (Scb->ScbType.Index.CollationRule == COLLATION_FILE_NAME) &&
  1097. //
  1098. // Is it a Dos-only or Ntfs-only name?
  1099. //
  1100. (BooleanFlagOn( NameInIndex->Flags, FILE_NAME_DOS ) !=
  1101. BooleanFlagOn( NameInIndex->Flags, FILE_NAME_NTFS )) &&
  1102. //
  1103. // Try to resume from the other name if he either gave
  1104. // us a Dos-only name, or he gave us an Ntfs-only name
  1105. // that does not fit in the search expression.
  1106. //
  1107. (FlagOn( NameInIndex->Flags, FILE_NAME_DOS ) ||
  1108. !(*MatchRoutine)( UpcaseTable,
  1109. Ccb->QueryBuffer,
  1110. FoundIndexEntry,
  1111. IgnoreCase ))) {
  1112. PFILE_NAME FileNameBuffer;
  1113. ULONG FileNameLength;
  1114. NtfsInitializeIndexContext( &OtherContext );
  1115. CleanupOtherContext = TRUE;
  1116. FileNameBuffer = NtfsRetrieveOtherFileName( IrpContext,
  1117. Ccb,
  1118. Scb,
  1119. FoundIndexEntry,
  1120. &OtherContext,
  1121. AcquiredFcb,
  1122. &SynchronizationError );
  1123. //
  1124. // We have to position to the long name and actually
  1125. // resume from there. To do this we have to cleanup and initialize
  1126. // the IndexContext in the Ccb, and lookup the long name we just
  1127. // found.
  1128. //
  1129. // If the other index entry is not there, there is some minor
  1130. // corruption going on, but we will just charge on in that event.
  1131. // Also, if the other index entry is there, but it does not match
  1132. // our expression, then we are supposed to resume from the short
  1133. // name, so we carry on.
  1134. //
  1135. ItsThere = (FileNameBuffer != NULL);
  1136. if (!ItsThere && SynchronizationError) {
  1137. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  1138. }
  1139. if (ItsThere &&
  1140. (FlagOn(Ccb->Flags, CCB_FLAG_WILDCARD_IN_EXPRESSION) ?
  1141. NtfsFileNameIsInExpression(UpcaseTable,
  1142. (PFILE_NAME)Ccb->QueryBuffer,
  1143. FileNameBuffer,
  1144. IgnoreCase) :
  1145. NtfsFileNameIsEqual(UpcaseTable,
  1146. (PFILE_NAME)Ccb->QueryBuffer,
  1147. FileNameBuffer,
  1148. IgnoreCase))) {
  1149. ULONG SizeOfFileName = FIELD_OFFSET( FILE_NAME, FileName );
  1150. NtfsReinitializeIndexContext( IrpContext, IndexContext );
  1151. //
  1152. // Extract a description of the file name from the found index
  1153. // entry.
  1154. //
  1155. FileNameLength = FileNameBuffer->FileNameLength * sizeof( WCHAR );
  1156. //
  1157. // Call FindFirst/FindNext to position our context to the corresponding
  1158. // long name.
  1159. //
  1160. FindFirstIndexEntry( IrpContext,
  1161. Scb,
  1162. (PVOID)FileNameBuffer,
  1163. IndexContext );
  1164. ItsThere = FindNextIndexEntry( IrpContext,
  1165. Scb,
  1166. (PVOID)FileNameBuffer,
  1167. FALSE,
  1168. FALSE,
  1169. IndexContext,
  1170. FALSE,
  1171. NULL );
  1172. if (!ItsThere) {
  1173. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  1174. }
  1175. }
  1176. }
  1177. //
  1178. // NextFlag should only remain TRUE, if the specified value
  1179. // is actually there, and NextFlag was specified as TRUE
  1180. // on input. In particular, it is important to make
  1181. // NextFlag FALSE if the specified value is not actually
  1182. // there. (Experience shows this behavior is important to
  1183. // implement "delnode" correctly for the Lan Manager Server!)
  1184. //
  1185. NextFlag = (BOOLEAN)(NextFlag && ItsThere);
  1186. //
  1187. // If we created the context then we need to remember if the
  1188. // expression has wildcards.
  1189. //
  1190. } else {
  1191. //
  1192. // We may not handle correctly an initial enumeration with
  1193. // NextFlag TRUE, because the context is initially sitting
  1194. // in the root. Dirctrl must always pass NextFlag FALSE
  1195. // on the initial enumeration.
  1196. //
  1197. ASSERT(!NextFlag);
  1198. //
  1199. // Remember if the value has wild cards.
  1200. //
  1201. if (WildCardsInExpression) {
  1202. SetFlag( Ccb->Flags, CCB_FLAG_WILDCARD_IN_EXPRESSION );
  1203. }
  1204. }
  1205. //
  1206. // Now we are correctly positioned. See if there is an actual
  1207. // match at our current position. If not, return FALSE.
  1208. //
  1209. // (Note, FindFirstIndexEntry always leaves us positioned in
  1210. // some leaf of the index, and it is the first FindNext that
  1211. // actually positions us to the first match.)
  1212. //
  1213. if (!FindNextIndexEntry( IrpContext,
  1214. Scb,
  1215. Ccb->QueryBuffer,
  1216. BooleanFlagOn( Ccb->Flags, CCB_FLAG_WILDCARD_IN_EXPRESSION ),
  1217. BooleanFlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE ),
  1218. IndexContext,
  1219. NextFlag,
  1220. NULL )) {
  1221. try_return( Result = FALSE );
  1222. }
  1223. //
  1224. // If we get here, then we have a match that we want to return.
  1225. // We always copy the complete IndexEntry away and pass a pointer
  1226. // back to the copy. See if our current buffer for the index entry
  1227. // is large enough.
  1228. //
  1229. FoundIndexEntry = IndexContext->Current->IndexEntry;
  1230. if (Ccb->IndexEntryLength < (ULONG)FoundIndexEntry->Length) {
  1231. //
  1232. // If there is a buffer currently allocated, deallocate it before
  1233. // allocating a larger one. (Clear Ccb fields in case we get an
  1234. // allocation error.)
  1235. //
  1236. if (Ccb->IndexEntry != NULL) {
  1237. NtfsFreePool( Ccb->IndexEntry );
  1238. Ccb->IndexEntry = NULL;
  1239. Ccb->IndexEntryLength = 0;
  1240. }
  1241. //
  1242. // Allocate a new buffer for the index entry we just found, with
  1243. // some "padding" in case the next match is larger.
  1244. //
  1245. Ccb->IndexEntry = (PINDEX_ENTRY)NtfsAllocatePool(PagedPool, (ULONG)FoundIndexEntry->Length + 16 );
  1246. Ccb->IndexEntryLength = (ULONG)FoundIndexEntry->Length + 16;
  1247. }
  1248. //
  1249. // Now, save away our copy of the IndexEntry, and return a pointer
  1250. // to it.
  1251. //
  1252. RtlMoveMemory( Ccb->IndexEntry,
  1253. FoundIndexEntry,
  1254. (ULONG)FoundIndexEntry->Length );
  1255. *IndexEntry = Ccb->IndexEntry;
  1256. try_return( Result = TRUE );
  1257. try_exit: NOTHING;
  1258. } finally {
  1259. DebugUnwind( NtfsRestartIndexEnumeration );
  1260. if (CleanupOtherContext) {
  1261. NtfsCleanupIndexContext( IrpContext, &OtherContext );
  1262. }
  1263. //
  1264. // If we died during the first call, then deallocate everything
  1265. // that we might have allocated.
  1266. //
  1267. if (AbnormalTermination() && ContextJustCreated) {
  1268. if (Ccb->IndexEntry != NULL) {
  1269. NtfsFreePool( Ccb->IndexEntry );
  1270. Ccb->IndexEntry = NULL;
  1271. }
  1272. if (Ccb->IndexContext != NULL) {
  1273. NtfsCleanupIndexContext( IrpContext, Ccb->IndexContext );
  1274. ExFreeToPagedLookasideList( &NtfsIndexContextLookasideList, Ccb->IndexContext );
  1275. Ccb->IndexContext = NULL;
  1276. }
  1277. }
  1278. //
  1279. // Remember if we are not returning anything, to save work later.
  1280. //
  1281. if (!Result && (Ccb->IndexEntry != NULL)) {
  1282. Ccb->IndexEntry->Length = 0;
  1283. }
  1284. }
  1285. DebugTrace( 0, Dbg, ("*IndexEntry < %08lx\n", *IndexEntry) );
  1286. DebugTrace( -1, Dbg, ("NtfsRestartIndexEnumeration -> %08lx\n", Result) );
  1287. return Result;
  1288. }
  1289. BOOLEAN
  1290. NtfsContinueIndexEnumeration (
  1291. IN PIRP_CONTEXT IrpContext,
  1292. IN PCCB Ccb,
  1293. IN PSCB Scb,
  1294. IN BOOLEAN NextFlag,
  1295. OUT PINDEX_ENTRY *IndexEntry
  1296. )
  1297. /*++
  1298. Routine Description:
  1299. This routine may be called to return again the last match on an active
  1300. enumeration, or to return the next match. Enumerations must always be
  1301. started or restarted via a call to NtfsRestartIndexEnumeration.
  1302. Note that all calls to this routine must be from within a try-finally,
  1303. and the finally clause must include a call to NtfsCleanupAfterEnumeration.
  1304. Arguments:
  1305. Ccb - Pointer to the Ccb for this enumeration.
  1306. Scb - Supplies the Scb for the index.
  1307. NextFlag - FALSE if the last returned match is to be returned again.
  1308. TRUE if the next match is to be returned.
  1309. IndexEntry - Returns a pointer to a copy of the index entry.
  1310. Return Value:
  1311. FALSE - If no match is being returned, and the output pointer is undefined.
  1312. TRUE - If a match is being returned.
  1313. --*/
  1314. {
  1315. PINDEX_ENTRY FoundIndexEntry;
  1316. PINDEX_CONTEXT IndexContext;
  1317. BOOLEAN MustRestart;
  1318. BOOLEAN Result = FALSE;
  1319. ASSERT_IRP_CONTEXT( IrpContext );
  1320. ASSERT_CCB( Ccb );
  1321. ASSERT_SCB( Scb );
  1322. ASSERT_SHARED_SCB( Scb );
  1323. PAGED_CODE();
  1324. DebugTrace( +1, Dbg, ("NtfsContinueIndexEnumeration\n") );
  1325. DebugTrace( 0, Dbg, ("Ccb = %08lx\n", Ccb) );
  1326. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  1327. DebugTrace( 0, Dbg, ("NextFlag = %02lx\n", NextFlag) );
  1328. //
  1329. // It seems many apps like to come back one more time and really get
  1330. // an error status, so if we did not return anything last time, we can
  1331. // get out now too.
  1332. //
  1333. // There also may be no index entry, in the case of an empty directory
  1334. // and dirctrl is cycling through with "." and "..".
  1335. //
  1336. if ((Ccb->IndexEntry == NULL) || (Ccb->IndexEntry->Length == 0)) {
  1337. DebugTrace( -1, Dbg, ("NtfsContinueIndexEnumeration -> FALSE\n") );
  1338. return FALSE;
  1339. }
  1340. IndexContext = Ccb->IndexContext;
  1341. try {
  1342. //
  1343. // Lookup the next match.
  1344. //
  1345. if (!FindNextIndexEntry( IrpContext,
  1346. Scb,
  1347. Ccb->QueryBuffer,
  1348. BooleanFlagOn( Ccb->Flags, CCB_FLAG_WILDCARD_IN_EXPRESSION ),
  1349. BooleanFlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE ),
  1350. IndexContext,
  1351. NextFlag,
  1352. &MustRestart )) {
  1353. //
  1354. // If he is saying we must restart, then that means something changed
  1355. // in our saved enumeration context across two file system calls.
  1356. // Reestablish our position in the tree by looking up the last entry
  1357. // we returned.
  1358. //
  1359. if (MustRestart) {
  1360. NtfsReinitializeIndexContext( IrpContext, Ccb->IndexContext );
  1361. try_return( Result = NtfsRestartIndexEnumeration( IrpContext,
  1362. Ccb,
  1363. Scb,
  1364. (PVOID)(Ccb->IndexEntry + 1),
  1365. FALSE,
  1366. NextFlag,
  1367. IndexEntry,
  1368. NULL ));
  1369. //
  1370. // Otherwise, there is nothing left to return.
  1371. //
  1372. } else {
  1373. try_return( Result = FALSE );
  1374. }
  1375. }
  1376. //
  1377. // If we get here, then we have a match that we want to return.
  1378. // We always copy the complete IndexEntry away and pass a pointer
  1379. // back to the copy. See if our current buffer for the index entry
  1380. // is large enough.
  1381. //
  1382. FoundIndexEntry = IndexContext->Current->IndexEntry;
  1383. if (Ccb->IndexEntryLength < (ULONG)FoundIndexEntry->Length) {
  1384. //
  1385. // If there is a buffer currently allocated, deallocate it before
  1386. // allocating a larger one.
  1387. //
  1388. if (Ccb->IndexEntry != NULL) {
  1389. NtfsFreePool( Ccb->IndexEntry );
  1390. Ccb->IndexEntry = NULL;
  1391. Ccb->IndexEntryLength = 0;
  1392. }
  1393. //
  1394. // Allocate a new buffer for the index entry we just found, with
  1395. // some "padding".
  1396. //
  1397. Ccb->IndexEntry = (PINDEX_ENTRY)NtfsAllocatePool(PagedPool, (ULONG)FoundIndexEntry->Length + 16 );
  1398. Ccb->IndexEntryLength = (ULONG)FoundIndexEntry->Length + 16;
  1399. }
  1400. //
  1401. // Now, save away our copy of the IndexEntry, and return a pointer
  1402. // to it.
  1403. //
  1404. RtlMoveMemory( Ccb->IndexEntry,
  1405. FoundIndexEntry,
  1406. (ULONG)FoundIndexEntry->Length );
  1407. *IndexEntry = Ccb->IndexEntry;
  1408. try_return( Result = TRUE );
  1409. try_exit: NOTHING;
  1410. } finally {
  1411. DebugUnwind( NtfsContinueIndexEnumeration );
  1412. //
  1413. // Remember if we are not returning anything, to save work later.
  1414. //
  1415. if (!Result && (Ccb->IndexEntry != NULL)) {
  1416. Ccb->IndexEntry->Length = 0;
  1417. }
  1418. }
  1419. DebugTrace( 0, Dbg, ("*IndexEntry < %08lx\n", *IndexEntry) );
  1420. DebugTrace( -1, Dbg, ("NtfsContinueIndexEnumeration -> %08lx\n", Result) );
  1421. return Result;
  1422. }
  1423. PFILE_NAME
  1424. NtfsRetrieveOtherFileName (
  1425. IN PIRP_CONTEXT IrpContext,
  1426. IN PCCB Ccb,
  1427. IN PSCB Scb,
  1428. IN PINDEX_ENTRY IndexEntry,
  1429. IN OUT PINDEX_CONTEXT OtherContext,
  1430. IN PFCB AcquiredFcb OPTIONAL,
  1431. OUT PBOOLEAN SynchronizationError
  1432. )
  1433. /*++
  1434. Routine Description:
  1435. This routine may be called to retrieve the other index entry for a given
  1436. index entry. I.e., for an input Ntfs-only index entry it will find the
  1437. Dos-only index entry for the same file referenced, or visa versa. This
  1438. is a routine which clearly is relevant only to file name indices, but it
  1439. is located here because it uses the Index Context in the Ccb.
  1440. The idea is that nearly always the other name for a given index entry will
  1441. be very near to the given name.
  1442. This routine first scans the leaf index buffer described by the index
  1443. context for the Dos name. If this fails, this routine attempts to look
  1444. the other name up in the index. Currently there will always be a Dos name,
  1445. however if one does not exist, we treat that as benign, and simply return
  1446. FALSE.
  1447. Arguments:
  1448. Ccb - Pointer to the Ccb for this enumeration.
  1449. Scb - Supplies the Scb for the index.
  1450. IndexEntry - Suppliess a pointer to an index entry, for which the Dos name
  1451. is to be retrieved.
  1452. OtherContext - Must be initialized on input, and subsequently cleaned up
  1453. by the caller after the information has been extracted from
  1454. the other index entry.
  1455. AcquiredFcb - An Fcb which has been acquired so that its file record may be
  1456. read
  1457. SynchronizationError - Returns TRUE if no file name is being returned because
  1458. of an error trying to acquire an Fcb to read its file
  1459. record.
  1460. Return Value:
  1461. Pointer to the other desired file name.
  1462. --*/
  1463. {
  1464. PINDEX_CONTEXT IndexContext;
  1465. PINDEX_HEADER IndexHeader;
  1466. PINDEX_ENTRY IndexTemp, IndexLast;
  1467. PINDEX_LOOKUP_STACK Top;
  1468. struct {
  1469. FILE_NAME FileName;
  1470. WCHAR NameBuffer[2];
  1471. }OtherFileName;
  1472. UNICODE_STRING OtherName;
  1473. USHORT OtherFlag;
  1474. PVCB Vcb = Scb->Vcb;
  1475. ASSERT_IRP_CONTEXT( IrpContext );
  1476. ASSERT_CCB( Ccb );
  1477. ASSERT_SCB( Scb );
  1478. ASSERT_SHARED_SCB( Scb );
  1479. PAGED_CODE();
  1480. DebugTrace( +1, Dbg, ("NtfsRetrieveOtherFileName\n") );
  1481. DebugTrace( 0, Dbg, ("Ccb = %08lx\n", Ccb) );
  1482. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  1483. *SynchronizationError = FALSE;
  1484. //
  1485. // Calculate the other name space flag.
  1486. //
  1487. OtherFlag = ((PFILE_NAME)(IndexEntry + 1))->Flags;
  1488. ClearFlag( OtherFlag, ~(FILE_NAME_NTFS | FILE_NAME_DOS) );
  1489. OtherFlag ^= FILE_NAME_NTFS | FILE_NAME_DOS;
  1490. ASSERT( (OtherFlag == FILE_NAME_NTFS) || (OtherFlag == FILE_NAME_DOS) );
  1491. //
  1492. // Point to the IndexContext in the Ccb.
  1493. //
  1494. IndexContext = Ccb->IndexContext;
  1495. //
  1496. // We can only scan the top of the index if it is safe
  1497. // to read it.
  1498. //
  1499. Top = IndexContext->Top;
  1500. if ((Top->Bcb != NULL) ||
  1501. (Top == IndexContext->Base) ||
  1502. ReadIndexBuffer(IrpContext, Scb, 0, TRUE, Top)) {
  1503. //
  1504. // Point to the first index entry in the index buffer at the bottom of
  1505. // the index.
  1506. //
  1507. IndexHeader = Top->IndexHeader;
  1508. IndexTemp = Add2Ptr(IndexHeader, IndexHeader->FirstIndexEntry);
  1509. IndexLast = Add2Ptr(IndexHeader, IndexHeader->FirstFreeByte);
  1510. //
  1511. // Now scan this buffer for a matching Dos name.
  1512. //
  1513. while (IndexTemp < IndexLast) {
  1514. //
  1515. // If we find an entry with the same file reference and the Dos flag
  1516. // set, then we can return it and get out.
  1517. //
  1518. if (NtfsEqualMftRef(&IndexTemp->FileReference, &IndexEntry->FileReference) &&
  1519. FlagOn(((PFILE_NAME)(IndexTemp + 1))->Flags, OtherFlag)) {
  1520. DebugTrace( -1, Dbg, ("NtfsRetrieveOtherFileName -> %08lx\n", IndexTemp) );
  1521. return (PFILE_NAME)(IndexTemp + 1);
  1522. }
  1523. IndexTemp = Add2Ptr(IndexTemp, IndexTemp->Length);
  1524. }
  1525. }
  1526. //
  1527. // If this is a pretty large directory, then it may be too expensive to
  1528. // scan for the other name in the directory. Note in the degenerate
  1529. // case, we have actually do a sequential scan of the entire directory,
  1530. // and if all the files in the directory start with the same 6 characters,
  1531. // which is unfortunately common, then even our "pie-wedge" scan reads
  1532. // the entire directory.
  1533. //
  1534. // Thus we will just try to go read the file record in this case to get
  1535. // the other name. This is complicated from a synchronization standpoint -
  1536. // if the file is open, we need to acquire it shared before we can read
  1537. // it to get the other name. Here is a summary of the strategy implemented
  1538. // primarily here and in dirctrl:
  1539. //
  1540. // 1. Read the file record, in an attempt to avoid a fault while
  1541. // being synchronized.
  1542. // 2. If the file reference we need to synchronize is the same as
  1543. // in the optional AcquiredFcb parameter, go ahead and find/return
  1544. // the other name.
  1545. // 3. Else, acquire the Fcb Table to try to look up the Fcb.
  1546. // 4. If there is no Fcb, hold the table while returning the name.
  1547. // 5. If there is an Fcb, try to acquire it shared with Wait = FALSE,
  1548. // and hold it while returning the name.
  1549. // 6. If we cannot get the Fcb, and AcquiredFcb was not specified, then
  1550. // store the File Reference we are trying to get and return
  1551. // *SynchronizationError = TRUE. Dirctrl must figure out how to
  1552. // call us back with the FcbAcquired, by forcing a resume of the
  1553. // enumeration.
  1554. // 7. If we could not get the Fcb and there *was* a different AcquiredFcb
  1555. // specified, then this is the only case where we give up and fall
  1556. // through to find the other name in the directory. This should be
  1557. // extremely rare, but if we try to return *SynchronizationError = TRUE,
  1558. // and force a resume, we could be unlucky and loop forever, essentially
  1559. // toggling between synchronizing on two Fcbs. Presumably this could
  1560. // only happen if we have some kind of dumb client who likes to back
  1561. // up a few files when he resumes.
  1562. //
  1563. if (Scb->Header.AllocationSize.QuadPart > MAX_INDEX_TO_SCAN_FOR_NAMES) {
  1564. FCB_TABLE_ELEMENT Key;
  1565. PFCB_TABLE_ELEMENT Entry;
  1566. PFCB FcbWeNeed;
  1567. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  1568. PATTRIBUTE_RECORD_HEADER Attribute;
  1569. BOOLEAN Synchronized = TRUE;
  1570. //
  1571. // Get the base file record active and valid before synchronizing.
  1572. // Don't verify it since we aren't syncrhonized
  1573. //
  1574. NtfsReadMftRecord( IrpContext,
  1575. Vcb,
  1576. &IndexEntry->FileReference,
  1577. FALSE,
  1578. &OtherContext->AttributeContext.FoundAttribute.Bcb,
  1579. &FileRecord,
  1580. NULL );
  1581. //
  1582. // If we are not synchronized with the correct Fcb, then try to
  1583. // synchronize.
  1584. //
  1585. if (!ARGUMENT_PRESENT(AcquiredFcb) ||
  1586. !NtfsEqualMftRef(&AcquiredFcb->FileReference, &IndexEntry->FileReference)) {
  1587. //
  1588. // Now look up the Fcb, and if it is there, reference it
  1589. // and remember it.
  1590. //
  1591. Key.FileReference = IndexEntry->FileReference;
  1592. NtfsAcquireFcbTable( IrpContext, Vcb );
  1593. Entry = RtlLookupElementGenericTable( &Vcb->FcbTable, &Key );
  1594. if (Entry != NULL) {
  1595. FcbWeNeed = Entry->Fcb;
  1596. //
  1597. // Now that it cannot go anywhere, try to acquire it.
  1598. //
  1599. Synchronized = NtfsAcquireResourceShared( IrpContext, FcbWeNeed, FALSE );
  1600. //
  1601. // If we manage to acquire it, then increment its reference count
  1602. // and remember it for subsequent cleanup.
  1603. //
  1604. if (Synchronized) {
  1605. FcbWeNeed->ReferenceCount += 1;
  1606. OtherContext->AcquiredFcb = FcbWeNeed;
  1607. }
  1608. NtfsReleaseFcbTable( IrpContext, Vcb );
  1609. } else {
  1610. SetFlag( OtherContext->Flags, INDX_CTX_FLAG_FCB_TABLE_ACQUIRED );
  1611. }
  1612. }
  1613. if (Synchronized) {
  1614. ULONG CorruptHint = 0;
  1615. if (!NtfsCheckFileRecord( Vcb, FileRecord, &IndexEntry->FileReference, &CorruptHint ) ||
  1616. (!FlagOn( FileRecord->Flags, FILE_RECORD_SEGMENT_IN_USE ))) {
  1617. if (FlagOn( OtherContext->Flags, INDX_CTX_FLAG_FCB_TABLE_ACQUIRED )) {
  1618. ClearFlag( OtherContext->Flags, INDX_CTX_FLAG_FCB_TABLE_ACQUIRED );
  1619. NtfsReleaseFcbTable( IrpContext, Vcb );
  1620. }
  1621. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, &IndexEntry->FileReference, NULL );
  1622. }
  1623. Attribute = (PATTRIBUTE_RECORD_HEADER)Add2Ptr(FileRecord, FileRecord->FirstAttributeOffset);
  1624. while (((PVOID)Attribute < Add2Ptr(FileRecord, FileRecord->FirstFreeByte)) &&
  1625. (Attribute->TypeCode <= $FILE_NAME)) {
  1626. if ((Attribute->TypeCode == $FILE_NAME) &&
  1627. FlagOn(((PFILE_NAME)NtfsAttributeValue(Attribute))->Flags, OtherFlag)) {
  1628. return (PFILE_NAME)NtfsAttributeValue(Attribute);
  1629. }
  1630. Attribute = NtfsGetNextRecord(Attribute);
  1631. }
  1632. } else if (!ARGUMENT_PRESENT(AcquiredFcb)) {
  1633. Ccb->FcbToAcquire.FileReference = IndexEntry->FileReference;
  1634. *SynchronizationError = TRUE;
  1635. return NULL;
  1636. }
  1637. //
  1638. // Cleanup from above before proceding.
  1639. //
  1640. NtfsReinitializeIndexContext( IrpContext, OtherContext );
  1641. }
  1642. //
  1643. // Well, we were unlucky, and did not find the other name yet, form
  1644. // a name to scan a range of the index.
  1645. //
  1646. RtlZeroMemory( &OtherFileName, sizeof(OtherFileName) );
  1647. OtherName.MaximumLength = 6;
  1648. OtherName.Buffer = (PWCHAR) &OtherFileName.FileName.FileName[0];
  1649. OtherName.Buffer[0] = ((PFILE_NAME)(IndexEntry + 1))->FileName[0];
  1650. //
  1651. // Name generation is complicated enough, that we are only going to
  1652. // guess the other name using the first two (special case is one)
  1653. // characters followed by *.
  1654. //
  1655. if (((PFILE_NAME)(IndexEntry + 1))->FileNameLength > 1) {
  1656. OtherName.Buffer[1] = ((PFILE_NAME)(IndexEntry + 1))->FileName[1];
  1657. OtherName.Buffer[2] = L'*';
  1658. OtherFileName.FileName.FileNameLength = 3;
  1659. OtherName.Length = 6;
  1660. } else {
  1661. OtherName.Buffer[1] = L'*';
  1662. OtherFileName.FileName.FileNameLength = 2;
  1663. OtherName.Length = 4;
  1664. }
  1665. //
  1666. // Now we think we have formed a pretty good name fairly tightly
  1667. // encompasses the range of possible Dos names we expect for the
  1668. // given Ntfs name. We will enumerate all of the files which match
  1669. // the pattern we have formed, and see if any of them are the Dos
  1670. // name for the same file. If this expression still doesn't work,
  1671. // (extremely unlikely), then we will substitute the pattern with
  1672. // "*" and make one final attempt.
  1673. //
  1674. // Note many names are the same in Dos and Ntfs, and for them this
  1675. // routine is never called. Of the ones that do have separate names,
  1676. // the pattern we formed above should really match it and will probably
  1677. // scan parts of the directory already in the cache. The last loop is
  1678. // a terrible sequential scan of the entire directory. It should never,
  1679. // never happen, but it is here so that in the worst case we do not
  1680. // break, we just take a bit longer.
  1681. //
  1682. while (TRUE) {
  1683. BOOLEAN NextFlag;
  1684. ULONG NameLength;
  1685. NameLength = sizeof(FILE_NAME) + OtherFileName.FileName.FileNameLength - 1;
  1686. //
  1687. // The first step of enumeration is to position our IndexContext.
  1688. //
  1689. FindFirstIndexEntry( IrpContext,
  1690. Scb,
  1691. &OtherFileName,
  1692. OtherContext );
  1693. NextFlag = FALSE;
  1694. //
  1695. // Now scan through all of the case insensitive matches.
  1696. // Upcase our name structure.
  1697. //
  1698. NtfsUpcaseName( Vcb->UpcaseTable, Vcb->UpcaseTableSize, &OtherName );
  1699. while (FindNextIndexEntry( IrpContext,
  1700. Scb,
  1701. &OtherFileName,
  1702. TRUE,
  1703. TRUE,
  1704. OtherContext,
  1705. NextFlag,
  1706. NULL )) {
  1707. IndexTemp = OtherContext->Current->IndexEntry;
  1708. //
  1709. // If we find an entry with the same file reference and the Dos flag
  1710. // set, then we can return it and get out.
  1711. //
  1712. if (NtfsEqualMftRef(&IndexTemp->FileReference, &IndexEntry->FileReference) &&
  1713. FlagOn(((PFILE_NAME)(IndexTemp + 1))->Flags, OtherFlag)) {
  1714. DebugTrace( -1, Dbg, ("NtfsRetrieveOtherFileName -> %08lx\n", IndexTemp) );
  1715. return (PFILE_NAME)(IndexTemp + 1);
  1716. }
  1717. NextFlag = TRUE;
  1718. }
  1719. //
  1720. // Give up if we have already scanned everything.
  1721. //
  1722. if ((OtherName.Buffer[0] == '*') && (OtherName.Length == 2)) {
  1723. break;
  1724. }
  1725. NtfsReinitializeIndexContext( IrpContext, OtherContext );
  1726. OtherName.Buffer[0] = '*';
  1727. OtherName.Length = 2;
  1728. OtherFileName.FileName.FileNameLength = 1;
  1729. }
  1730. return NULL;
  1731. }
  1732. VOID
  1733. NtfsCleanupAfterEnumeration (
  1734. IN PIRP_CONTEXT IrpContext,
  1735. IN PCCB Ccb
  1736. )
  1737. /*++
  1738. Routine Description:
  1739. A call to this routine must exist within the finally clause of any routine
  1740. which calls either NtfsRestartIndexEnumeration or NtfsContinueIndexEnumeration.
  1741. Arguments:
  1742. Ccb - Pointer to the Ccb for this enumeration.
  1743. Return Value:
  1744. None
  1745. --*/
  1746. {
  1747. PAGED_CODE();
  1748. if (Ccb->IndexContext != NULL) {
  1749. NtfsReinitializeIndexContext( IrpContext, Ccb->IndexContext );
  1750. }
  1751. }
  1752. BOOLEAN
  1753. NtfsIsIndexEmpty (
  1754. IN PIRP_CONTEXT IrpContext,
  1755. IN PATTRIBUTE_RECORD_HEADER Attribute
  1756. )
  1757. /*++
  1758. Routine Description:
  1759. This routine may be called to see if the specified index is empty.
  1760. Arguments:
  1761. Attribute - Pointer to the attribute record header of an INDEX_ROOT
  1762. attribute.
  1763. Return Value:
  1764. FALSE - if the index is not empty.
  1765. TRUE - if the index is empty.
  1766. --*/
  1767. {
  1768. PINDEX_ROOT IndexRoot;
  1769. PINDEX_ENTRY IndexEntry;
  1770. BOOLEAN Result;
  1771. ASSERT_IRP_CONTEXT( IrpContext );
  1772. PAGED_CODE();
  1773. DebugTrace( +1, Dbg, ("NtfsIsIndexEmpty\n") );
  1774. DebugTrace( 0, Dbg, ("Attribute = %08lx\n", Attribute) );
  1775. IndexRoot = (PINDEX_ROOT)NtfsAttributeValue( Attribute );
  1776. IndexEntry = NtfsFirstIndexEntry( &IndexRoot->IndexHeader );
  1777. Result = (BOOLEAN)(!FlagOn( IndexEntry->Flags, INDEX_ENTRY_NODE ) &&
  1778. FlagOn( IndexEntry->Flags, INDEX_ENTRY_END ));
  1779. DebugTrace( -1, Dbg, ("NtfsIsIndexEmpty -> %02lx\n", Result) );
  1780. return Result;
  1781. }
  1782. VOID
  1783. NtfsDeleteIndex (
  1784. IN PIRP_CONTEXT IrpContext,
  1785. IN PFCB Fcb,
  1786. IN PUNICODE_STRING AttributeName
  1787. )
  1788. /*++
  1789. Routine Description:
  1790. This routine may be called to delete the specified index. The index
  1791. must be empty.
  1792. NOTE: This routine is not required until we can separately create/delete
  1793. indices, therefore it is not implemented. Use NtfsDeleteFile
  1794. to delete a "directory" (or a normal file).
  1795. Arguments:
  1796. Fcb - Supplies the Fcb for the index.
  1797. AttributeName - Name of the index attributes: $Ixxx
  1798. Return Value:
  1799. None
  1800. --*/
  1801. {
  1802. ASSERT_IRP_CONTEXT( IrpContext );
  1803. ASSERT_FCB( Fcb );
  1804. UNREFERENCED_PARAMETER( IrpContext );
  1805. UNREFERENCED_PARAMETER( Fcb );
  1806. UNREFERENCED_PARAMETER( AttributeName );
  1807. PAGED_CODE();
  1808. DebugTrace( +1, Dbg, ("NtfsDeleteIndex\n") );
  1809. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  1810. DebugTrace( 0, Dbg, ("AttributeName = %08lx\n", AttributeName) );
  1811. DbgDoit( DbgPrint("NtfsDeleteIndex is not yet implemented\n"); DbgBreakPoint(); );
  1812. DebugTrace( -1, Dbg, ("NtfsDeleteIndex -> VOID\n") );
  1813. }
  1814. VOID
  1815. NtfsInitializeIndexContext (
  1816. OUT PINDEX_CONTEXT IndexContext
  1817. )
  1818. /*++
  1819. Routine Description:
  1820. This routine may be called to initialize the specified index context.
  1821. Arguments:
  1822. IndexContext - Index context to initialize.
  1823. Return Value:
  1824. None
  1825. --*/
  1826. {
  1827. PAGED_CODE();
  1828. RtlZeroMemory( IndexContext, sizeof(INDEX_CONTEXT) );
  1829. NtfsInitializeAttributeContext( &IndexContext->AttributeContext );
  1830. //
  1831. // Describe the resident lookup stack
  1832. //
  1833. IndexContext->Base = IndexContext->LookupStack;
  1834. IndexContext->NumberEntries = INDEX_LOOKUP_STACK_SIZE;
  1835. }
  1836. VOID
  1837. NtfsCleanupIndexContext (
  1838. IN PIRP_CONTEXT IrpContext,
  1839. OUT PINDEX_CONTEXT IndexContext
  1840. )
  1841. /*++
  1842. Routine Description:
  1843. This routine may be called to cleanup the specified index context,
  1844. typically during finally processing.
  1845. Arguments:
  1846. IndexContext - Index context to clean up.
  1847. Return Value:
  1848. None
  1849. --*/
  1850. {
  1851. ULONG i;
  1852. PAGED_CODE();
  1853. //
  1854. // Release the Fcb Table and/or an acquired Fcb.
  1855. //
  1856. if (FlagOn(IndexContext->Flags, INDX_CTX_FLAG_FCB_TABLE_ACQUIRED)) {
  1857. NtfsReleaseFcbTable( IrpContext, IrpContext->Vcb );
  1858. ClearFlag( IndexContext->Flags, INDX_CTX_FLAG_FCB_TABLE_ACQUIRED );
  1859. }
  1860. if (IndexContext->AcquiredFcb != NULL) {
  1861. NtfsAcquireFcbTable( IrpContext, IrpContext->Vcb );
  1862. ASSERT(IndexContext->AcquiredFcb->ReferenceCount > 0);
  1863. IndexContext->AcquiredFcb->ReferenceCount -= 1;
  1864. NtfsReleaseFcbTable( IrpContext, IrpContext->Vcb );
  1865. NtfsReleaseResource( IrpContext, IndexContext->AcquiredFcb );
  1866. IndexContext->AcquiredFcb = NULL;
  1867. }
  1868. for (i = 0; i < IndexContext->NumberEntries; i++) {
  1869. NtfsUnpinBcb( IrpContext, &IndexContext->Base[i].Bcb );
  1870. }
  1871. //
  1872. // See if we need to deallocate a lookup stack. Point to the embedded
  1873. // lookup stack if we deallocate to handle the case where cleanup is
  1874. // called twice in a row. We will have to zero the stack so the
  1875. // subsequent cleanup won't find any Bcb's in the stack.
  1876. //
  1877. if (IndexContext->Base != IndexContext->LookupStack) {
  1878. NtfsFreePool( IndexContext->Base );
  1879. }
  1880. NtfsCleanupAttributeContext( IrpContext, &IndexContext->AttributeContext );
  1881. }
  1882. VOID
  1883. NtfsReinitializeIndexContext (
  1884. IN PIRP_CONTEXT IrpContext,
  1885. OUT PINDEX_CONTEXT IndexContext
  1886. )
  1887. /*++
  1888. Routine Description:
  1889. This routine may be called to cleanup the specified index context,
  1890. and reinitialize it, preserving fields that should not be zeroed.
  1891. Arguments:
  1892. IndexContext - Index context to clean up.
  1893. Return Value:
  1894. None
  1895. --*/
  1896. {
  1897. ULONG i;
  1898. PAGED_CODE();
  1899. //
  1900. // Release the Fcb Table and/or an acquired Fcb.
  1901. //
  1902. if (FlagOn(IndexContext->Flags, INDX_CTX_FLAG_FCB_TABLE_ACQUIRED)) {
  1903. NtfsReleaseFcbTable( IrpContext, IrpContext->Vcb );
  1904. ClearFlag( IndexContext->Flags, INDX_CTX_FLAG_FCB_TABLE_ACQUIRED );
  1905. }
  1906. if (IndexContext->AcquiredFcb != NULL) {
  1907. NtfsAcquireFcbTable( IrpContext, IrpContext->Vcb );
  1908. ASSERT(IndexContext->AcquiredFcb->ReferenceCount > 0);
  1909. IndexContext->AcquiredFcb->ReferenceCount -= 1;
  1910. NtfsReleaseFcbTable( IrpContext, IrpContext->Vcb );
  1911. NtfsReleaseResource( IrpContext, IndexContext->AcquiredFcb );
  1912. IndexContext->AcquiredFcb = NULL;
  1913. }
  1914. for (i = 0; i < IndexContext->NumberEntries; i++) {
  1915. NtfsUnpinBcb( IrpContext, &IndexContext->Base[i].Bcb );
  1916. }
  1917. NtfsCleanupAttributeContext( IrpContext, &IndexContext->AttributeContext );
  1918. NtfsInitializeAttributeContext( &IndexContext->AttributeContext );
  1919. }
  1920. VOID
  1921. NtfsGrowLookupStack (
  1922. IN PSCB Scb,
  1923. IN OUT PINDEX_CONTEXT IndexContext,
  1924. IN PINDEX_LOOKUP_STACK *Sp
  1925. )
  1926. /*++
  1927. Routine Description:
  1928. This routine grows and index lookup stack to contain the specified number
  1929. of entries.
  1930. Arguments:
  1931. Scb - Scb for index
  1932. IndexContext - Index context to grow.
  1933. Sp - Caller's local stack pointer to be updated
  1934. Return Value:
  1935. None
  1936. --*/
  1937. {
  1938. PINDEX_LOOKUP_STACK NewLookupStack;
  1939. ULONG_PTR Relocation;
  1940. USHORT NumberEntries;
  1941. PAGED_CODE();
  1942. //
  1943. // Extract current index hint, if there is one.
  1944. //
  1945. NumberEntries = Scb->ScbType.Index.IndexDepthHint;
  1946. //
  1947. // If there was no hint, or it was too small, then
  1948. // calculate a new hint.
  1949. //
  1950. if (NumberEntries <= IndexContext->NumberEntries) {
  1951. Scb->ScbType.Index.IndexDepthHint =
  1952. NumberEntries = IndexContext->NumberEntries + 3;
  1953. }
  1954. //
  1955. // Allocate (may fail), initialize and copy over the old one.
  1956. //
  1957. NewLookupStack = NtfsAllocatePool( PagedPool, NumberEntries * sizeof(INDEX_LOOKUP_STACK) );
  1958. RtlZeroMemory( NewLookupStack, NumberEntries * sizeof(INDEX_LOOKUP_STACK) );
  1959. RtlCopyMemory( NewLookupStack,
  1960. IndexContext->Base,
  1961. IndexContext->NumberEntries * sizeof(INDEX_LOOKUP_STACK) );
  1962. //
  1963. // Free the old one unless we were using the local stack.
  1964. //
  1965. if (IndexContext->Base != IndexContext->LookupStack) {
  1966. NtfsFreePool( IndexContext->Base );
  1967. }
  1968. //
  1969. // Now relocate all pointers to the old stack
  1970. //
  1971. Relocation = ((PCHAR)NewLookupStack - (PCHAR)IndexContext->Base);
  1972. IndexContext->Current = (PINDEX_LOOKUP_STACK)((PCHAR)IndexContext->Current + Relocation);
  1973. IndexContext->Top = (PINDEX_LOOKUP_STACK)((PCHAR)IndexContext->Top + Relocation);
  1974. *Sp = (PINDEX_LOOKUP_STACK)((PCHAR)*Sp + Relocation);
  1975. //
  1976. // Finally update context to describe new stack
  1977. //
  1978. IndexContext->Base = NewLookupStack;
  1979. IndexContext->NumberEntries = NumberEntries;
  1980. }
  1981. BOOLEAN
  1982. ReadIndexBuffer (
  1983. IN PIRP_CONTEXT IrpContext,
  1984. IN PSCB Scb,
  1985. IN LONGLONG IndexBlock,
  1986. IN BOOLEAN Reread,
  1987. OUT PINDEX_LOOKUP_STACK Sp
  1988. )
  1989. /*++
  1990. Routine Description:
  1991. This routine reads the index buffer at the specified Vcn, and initializes
  1992. the stack pointer to describe it.
  1993. Arguments:
  1994. Scb - Supplies the Scb for the index.
  1995. IndexBlock - Supplies the index block of this index buffer, ignored if
  1996. Reread is TRUE.
  1997. Reread - Supplies TRUE if buffer is being reread, and the CapturedLsn
  1998. should be checked.
  1999. Sp - Returns a description of the index buffer read.
  2000. Return Value:
  2001. FALSE - if Reread was supplied as TRUE and the Lsn changed
  2002. TRUE - if the buffer was read successfully (or did not change)
  2003. --*/
  2004. {
  2005. PINDEX_ALLOCATION_BUFFER IndexBuffer;
  2006. PAGED_CODE();
  2007. DebugTrace( +1, Dbg, ("ReadIndexBuffer\n") );
  2008. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  2009. DebugTrace( 0, Dbg, ("Sp = %08lx\n", Sp) );
  2010. ASSERT(Sp->Bcb == NULL);
  2011. //
  2012. // If the attribute stream does not already exist, create it.
  2013. //
  2014. if (Scb->FileObject == NULL) {
  2015. NtfsCreateInternalAttributeStream( IrpContext,
  2016. Scb,
  2017. TRUE,
  2018. &NtfsInternalUseFile[DIRECTORY_FILE_NUMBER] );
  2019. }
  2020. //
  2021. // If Reread is TRUE, then convert the directory entry pointer in the
  2022. // buffer to an offset (to be relocated later) and overwrite the Lbn
  2023. // input parameter with the Lbn in the stack location.
  2024. //
  2025. if (Reread) {
  2026. Sp->IndexEntry = (PINDEX_ENTRY)((PCHAR)Sp->IndexEntry - (PCHAR)Sp->StartOfBuffer);
  2027. IndexBlock = Sp->IndexBlock;
  2028. }
  2029. Sp->IndexBlock = IndexBlock;
  2030. //
  2031. // The vcn better only have 32 bits, other wise the the test in NtfsMapStream
  2032. // may not catch this error.
  2033. //
  2034. if (((PLARGE_INTEGER) &IndexBlock)->HighPart != 0) {
  2035. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  2036. }
  2037. NtfsMapStream( IrpContext,
  2038. Scb,
  2039. LlBytesFromIndexBlocks( IndexBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  2040. Scb->ScbType.Index.BytesPerIndexBuffer,
  2041. &Sp->Bcb,
  2042. &Sp->StartOfBuffer );
  2043. IndexBuffer = (PINDEX_ALLOCATION_BUFFER)Sp->StartOfBuffer;
  2044. if (!NtfsCheckIndexBuffer( Scb, IndexBuffer ) ||
  2045. (IndexBuffer->ThisBlock != IndexBlock)) {
  2046. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  2047. }
  2048. Sp->IndexHeader = &IndexBuffer->IndexHeader;
  2049. if (Reread) {
  2050. if (IndexBuffer->Lsn.QuadPart != Sp->CapturedLsn.QuadPart) {
  2051. NtfsUnpinBcb( IrpContext, &Sp->Bcb );
  2052. DebugTrace( -1, Dbg, ("ReadIndexBuffer->TRUE\n") );
  2053. return FALSE;
  2054. }
  2055. Sp->IndexEntry = (PINDEX_ENTRY)((PCHAR)Sp->IndexEntry + ((PCHAR)Sp->StartOfBuffer - (PCHAR)NULL));
  2056. } else {
  2057. Sp->IndexEntry = NtfsFirstIndexEntry(Sp->IndexHeader);
  2058. Sp->CapturedLsn = IndexBuffer->Lsn;
  2059. }
  2060. DebugTrace( -1, Dbg, ("ReadIndexBuffer->VOID\n") );
  2061. return TRUE;
  2062. }
  2063. PINDEX_ALLOCATION_BUFFER
  2064. GetIndexBuffer (
  2065. IN PIRP_CONTEXT IrpContext,
  2066. IN PSCB Scb,
  2067. OUT PINDEX_LOOKUP_STACK Sp,
  2068. OUT PLONGLONG EndOfValidData
  2069. )
  2070. /*++
  2071. Routine Description:
  2072. This routine allocates and initializes an index buffer, and initializes
  2073. the stack pointer to describe it.
  2074. Arguments:
  2075. Scb - Supplies the Scb for the index.
  2076. Sp - Returns a description of the index buffer allocated.
  2077. EndOfValidData - This is the file offset of the end of the allocated buffer.
  2078. This is used to update the valid data length of the stream when the
  2079. block is written.
  2080. Return Value:
  2081. Pointer to the Index Buffer allocated.
  2082. --*/
  2083. {
  2084. PINDEX_ALLOCATION_BUFFER IndexBuffer;
  2085. ATTRIBUTE_ENUMERATION_CONTEXT BitMapContext;
  2086. ULONG RecordIndex;
  2087. LONGLONG BufferOffset;
  2088. PUSHORT UsaSequenceNumber;
  2089. PAGED_CODE();
  2090. DebugTrace( +1, Dbg, ("GetIndexBuffer\n") );
  2091. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  2092. DebugTrace( 0, Dbg, ("Sp = %08lx\n", Sp) );
  2093. //
  2094. // Initialize the BitMap attribute context and insure cleanup on the
  2095. // way out.
  2096. //
  2097. NtfsInitializeAttributeContext( &BitMapContext );
  2098. try {
  2099. //
  2100. // Lookup the BITMAP attribute.
  2101. //
  2102. if (!NtfsLookupAttributeByName( IrpContext,
  2103. Scb->Fcb,
  2104. &Scb->Fcb->FileReference,
  2105. $BITMAP,
  2106. &Scb->AttributeName,
  2107. NULL,
  2108. FALSE,
  2109. &BitMapContext )) {
  2110. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  2111. }
  2112. //
  2113. // If the attribute stream does not already exist, create it.
  2114. //
  2115. if (Scb->FileObject == NULL) {
  2116. NtfsCreateInternalAttributeStream( IrpContext,
  2117. Scb,
  2118. TRUE,
  2119. &NtfsInternalUseFile[DIRECTORY_FILE_NUMBER] );
  2120. }
  2121. //
  2122. // If the allocation for this index has not been initialized yet,
  2123. // then we must initialize it first.
  2124. //
  2125. if (!Scb->ScbType.Index.AllocationInitialized) {
  2126. ULONG ExtendGranularity = 1;
  2127. ULONG TruncateGranularity = 4;
  2128. if (Scb->ScbType.Index.BytesPerIndexBuffer < Scb->Vcb->BytesPerCluster) {
  2129. ExtendGranularity = Scb->Vcb->BytesPerCluster / Scb->ScbType.Index.BytesPerIndexBuffer;
  2130. if (ExtendGranularity > 4) {
  2131. TruncateGranularity = ExtendGranularity;
  2132. }
  2133. }
  2134. NtfsInitializeRecordAllocation( IrpContext,
  2135. Scb,
  2136. &BitMapContext,
  2137. Scb->ScbType.Index.BytesPerIndexBuffer,
  2138. ExtendGranularity,
  2139. TruncateGranularity,
  2140. &Scb->ScbType.Index.RecordAllocationContext );
  2141. Scb->ScbType.Index.AllocationInitialized = TRUE;
  2142. }
  2143. //
  2144. // Now allocate a record. We always "hint" from the front to keep the
  2145. // index compact.
  2146. //
  2147. RecordIndex = NtfsAllocateRecord( IrpContext,
  2148. &Scb->ScbType.Index.RecordAllocationContext,
  2149. &BitMapContext );
  2150. //
  2151. // Calculate the IndexBlock.
  2152. //
  2153. BufferOffset = Int32x32To64( RecordIndex, Scb->ScbType.Index.BytesPerIndexBuffer );
  2154. //
  2155. // Now remember the offset of the end of the added record.
  2156. //
  2157. *EndOfValidData = BufferOffset + Scb->ScbType.Index.BytesPerIndexBuffer;
  2158. //
  2159. // Now pin and zero the buffer, in order to initialize it.
  2160. //
  2161. NtfsPreparePinWriteStream( IrpContext,
  2162. Scb,
  2163. BufferOffset,
  2164. Scb->ScbType.Index.BytesPerIndexBuffer,
  2165. TRUE,
  2166. &Sp->Bcb,
  2167. (PVOID *)&IndexBuffer );
  2168. //
  2169. // Now we can fill in the lookup stack.
  2170. //
  2171. Sp->StartOfBuffer = (PVOID)IndexBuffer;
  2172. Sp->IndexHeader = &IndexBuffer->IndexHeader;
  2173. Sp->IndexEntry = (PINDEX_ENTRY)NULL;
  2174. //
  2175. // Initialize the Index Allocation Buffer and return.
  2176. //
  2177. *(PULONG)IndexBuffer->MultiSectorHeader.Signature = *(PULONG)IndexSignature;
  2178. IndexBuffer->MultiSectorHeader.UpdateSequenceArrayOffset =
  2179. (USHORT)FIELD_OFFSET( INDEX_ALLOCATION_BUFFER, UpdateSequenceArray );
  2180. IndexBuffer->MultiSectorHeader.UpdateSequenceArraySize =
  2181. (USHORT)UpdateSequenceArraySize( Scb->ScbType.Index.BytesPerIndexBuffer );
  2182. UsaSequenceNumber = Add2Ptr( IndexBuffer,
  2183. IndexBuffer->MultiSectorHeader.UpdateSequenceArrayOffset );
  2184. *UsaSequenceNumber = 1;
  2185. IndexBuffer->ThisBlock = RecordIndex * Scb->ScbType.Index.BlocksPerIndexBuffer;
  2186. IndexBuffer->IndexHeader.FirstIndexEntry =
  2187. IndexBuffer->IndexHeader.FirstFreeByte =
  2188. QuadAlign((ULONG)IndexBuffer->MultiSectorHeader.UpdateSequenceArrayOffset +
  2189. (ULONG)IndexBuffer->MultiSectorHeader.UpdateSequenceArraySize * sizeof(USHORT)) -
  2190. FIELD_OFFSET(INDEX_ALLOCATION_BUFFER, IndexHeader);;
  2191. IndexBuffer->IndexHeader.BytesAvailable =
  2192. Scb->ScbType.Index.BytesPerIndexBuffer -
  2193. FIELD_OFFSET(INDEX_ALLOCATION_BUFFER, IndexHeader);;
  2194. } finally {
  2195. DebugUnwind( GetIndexBuffer );
  2196. NtfsCleanupAttributeContext( IrpContext, &BitMapContext );
  2197. }
  2198. DebugTrace( -1, Dbg, ("GetIndexBuffer -> %08lx\n", IndexBuffer) );
  2199. return IndexBuffer;
  2200. }
  2201. VOID
  2202. DeleteIndexBuffer (
  2203. IN PIRP_CONTEXT IrpContext,
  2204. IN PSCB Scb,
  2205. IN VCN IndexBlockNumber
  2206. )
  2207. /*++
  2208. Routine Description:
  2209. This routine deletes the specified index buffer.
  2210. Arguments:
  2211. Scb - Supplies the Scb for the index.
  2212. IndexBuffer - Pointer to the index buffer to delete.
  2213. Return Value:
  2214. None.
  2215. --*/
  2216. {
  2217. ATTRIBUTE_ENUMERATION_CONTEXT BitMapContext;
  2218. LONGLONG RecordIndex;
  2219. PATTRIBUTE_RECORD_HEADER BitmapAttribute;
  2220. BOOLEAN AttributeWasResident = FALSE;
  2221. PAGED_CODE();
  2222. DebugTrace( +1, Dbg, ("DeleteIndexBuffer\n") );
  2223. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  2224. DebugTrace( 0, Dbg, ("IndexBlockNumber = %08lx\n", IndexBlockNumber) );
  2225. //
  2226. // Initialize the BitMap attribute context and insure cleanup on the
  2227. // way out.
  2228. //
  2229. NtfsInitializeAttributeContext( &BitMapContext );
  2230. try {
  2231. //
  2232. // Lookup the BITMAP attribute.
  2233. //
  2234. if (!NtfsLookupAttributeByName( IrpContext,
  2235. Scb->Fcb,
  2236. &Scb->Fcb->FileReference,
  2237. $BITMAP,
  2238. &Scb->AttributeName,
  2239. NULL,
  2240. FALSE,
  2241. &BitMapContext )) {
  2242. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  2243. }
  2244. //
  2245. // Remember if the bitmap attribute is currently resident,
  2246. // in case it changes.
  2247. //
  2248. BitmapAttribute = NtfsFoundAttribute(&BitMapContext);
  2249. AttributeWasResident = BitmapAttribute->FormCode == RESIDENT_FORM;
  2250. //
  2251. // If the allocation for this index has not been initialized yet,
  2252. // then we must initialize it first.
  2253. //
  2254. if (!Scb->ScbType.Index.AllocationInitialized) {
  2255. ULONG ExtendGranularity = 1;
  2256. ULONG TruncateGranularity = 4;
  2257. if (Scb->ScbType.Index.BytesPerIndexBuffer < Scb->Vcb->BytesPerCluster) {
  2258. ExtendGranularity = Scb->Vcb->BytesPerCluster / Scb->ScbType.Index.BytesPerIndexBuffer;
  2259. if (ExtendGranularity > 4) {
  2260. TruncateGranularity = ExtendGranularity;
  2261. }
  2262. }
  2263. NtfsInitializeRecordAllocation( IrpContext,
  2264. Scb,
  2265. &BitMapContext,
  2266. Scb->ScbType.Index.BytesPerIndexBuffer,
  2267. ExtendGranularity,
  2268. TruncateGranularity,
  2269. &Scb->ScbType.Index.RecordAllocationContext );
  2270. Scb->ScbType.Index.AllocationInitialized = TRUE;
  2271. }
  2272. //
  2273. // Calculate the record index for this buffer.
  2274. //
  2275. RecordIndex = IndexBlockNumber / Scb->ScbType.Index.BlocksPerIndexBuffer;
  2276. if (((PLARGE_INTEGER)&RecordIndex)->HighPart != 0) {
  2277. ASSERT( ((PLARGE_INTEGER)&RecordIndex)->HighPart == 0 );
  2278. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  2279. }
  2280. //
  2281. // Now deallocate the record.
  2282. //
  2283. NtfsDeallocateRecord( IrpContext,
  2284. &Scb->ScbType.Index.RecordAllocationContext,
  2285. (ULONG)RecordIndex,
  2286. &BitMapContext );
  2287. } finally {
  2288. DebugUnwind( DeleteIndexBuffer );
  2289. //
  2290. // In the extremely rare case that the bitmap attribute was resident
  2291. // and now became nonresident, we will uninitialize it here so that
  2292. // the next time we will find the bitmap Scb, etc.
  2293. //
  2294. if (AttributeWasResident) {
  2295. BitmapAttribute = NtfsFoundAttribute(&BitMapContext);
  2296. if (BitmapAttribute->FormCode != RESIDENT_FORM) {
  2297. NtfsUninitializeRecordAllocation( IrpContext,
  2298. &Scb->ScbType.Index.RecordAllocationContext );
  2299. Scb->ScbType.Index.AllocationInitialized = FALSE;
  2300. }
  2301. }
  2302. NtfsCleanupAttributeContext( IrpContext, &BitMapContext );
  2303. }
  2304. DebugTrace( -1, Dbg, ("DeleteIndexBuffer -> VOID\n") );
  2305. }
  2306. VOID
  2307. FindFirstIndexEntry (
  2308. IN PIRP_CONTEXT IrpContext,
  2309. IN PSCB Scb,
  2310. IN PVOID Value,
  2311. IN OUT PINDEX_CONTEXT IndexContext
  2312. )
  2313. /*++
  2314. Routine Description:
  2315. This routine finds the the first entry in a leaf buffer of an Index Btree
  2316. which could possibly be a match for the input value. Another way to state
  2317. this is that this routine establishes a position in the Btree from which a
  2318. tree walk should begin to find the desired value or all desired values which
  2319. match the input value specification.
  2320. As stated, the context on return will always describe a pointer in a leaf
  2321. buffer. This is occassionally inefficient in the 2% case where a specific
  2322. value is being looked up that happens to reside in an intermediate node buffer.
  2323. However, for index adds and deletes, this pointer is always very interesting.
  2324. For an add, this pointer always describes the exact spot at which the add should
  2325. occur (adds must always occur in leafs). For deletes, this pointer is either
  2326. to the exact index entry which is to be deleted, or else it points to the best
  2327. replacement for the target to delete, when the actual target is at an intermediate
  2328. spot in the tree.
  2329. So this routine descends from the root of the index to the correct leaf, doing
  2330. a binary search in each index buffer along the way (via an external routine).
  2331. Arguments:
  2332. Scb - Supplies the Scb for the index.
  2333. Value - Pointer to a value or value expression which should be used to position
  2334. the IndexContext, or NULL to just describe the root for pushing.
  2335. IndexContext - Address of the initialized IndexContext, to return the desired
  2336. position.
  2337. Return Value:
  2338. None.
  2339. --*/
  2340. {
  2341. PINDEX_LOOKUP_STACK Sp;
  2342. PATTRIBUTE_RECORD_HEADER Attribute;
  2343. PINDEX_ROOT IndexRoot;
  2344. PAGED_CODE();
  2345. DebugTrace( +1, Dbg, ("FindFirstIndexEntry\n") );
  2346. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  2347. DebugTrace( 0, Dbg, ("Value = %08lx\n", Value) );
  2348. DebugTrace( 0, Dbg, ("IndexContext = %08lx\n", IndexContext) );
  2349. //
  2350. // Lookup the attribute record from the Scb.
  2351. //
  2352. if (!NtfsLookupAttributeByName( IrpContext,
  2353. Scb->Fcb,
  2354. &Scb->Fcb->FileReference,
  2355. $INDEX_ROOT,
  2356. &Scb->AttributeName,
  2357. NULL,
  2358. FALSE,
  2359. &IndexContext->AttributeContext )) {
  2360. DebugTrace( -1, 0, ("FindFirstIndexEntry - Could *not* find attribute\n") );
  2361. NtfsRaiseStatus( IrpContext, STATUS_OBJECT_PATH_NOT_FOUND, NULL, NULL );
  2362. }
  2363. //
  2364. // Save Lsn of file record containing Index Root so that later
  2365. // we can tell if we need to re-find it.
  2366. //
  2367. IndexContext->IndexRootFileRecordLsn =
  2368. IndexContext->AttributeContext.FoundAttribute.FileRecord->Lsn;
  2369. //
  2370. // Now Initialize some local pointers and the rest of the context
  2371. //
  2372. Sp = IndexContext->Base;
  2373. Sp->StartOfBuffer = NtfsContainingFileRecord( &IndexContext->AttributeContext );
  2374. Sp->CapturedLsn = ((PFILE_RECORD_SEGMENT_HEADER)Sp->StartOfBuffer)->Lsn;
  2375. IndexContext->ScbChangeCount = Scb->ScbType.Index.ChangeCount;
  2376. IndexContext->OldAttribute =
  2377. Attribute = NtfsFoundAttribute( &IndexContext->AttributeContext );
  2378. IndexRoot = (PINDEX_ROOT)NtfsAttributeValue( Attribute );
  2379. Sp->IndexHeader = &IndexRoot->IndexHeader;
  2380. //
  2381. // The Index part of the Scb may not yet be initialized. If so, do it
  2382. // here.
  2383. //
  2384. if (Scb->ScbType.Index.BytesPerIndexBuffer == 0) {
  2385. NtfsUpdateIndexScbFromAttribute( IrpContext, Scb, Attribute, FALSE );
  2386. }
  2387. //
  2388. // If Value is not specified, this is a special call from NtfsPushIndexRoot.
  2389. //
  2390. if (!ARGUMENT_PRESENT(Value)) {
  2391. Sp->IndexEntry = NtfsFirstIndexEntry(Sp->IndexHeader);
  2392. IndexContext->Top =
  2393. IndexContext->Current = Sp;
  2394. DebugTrace( -1, 0, ("FindFirstIndexEntry - No Value specified\n") );
  2395. return;
  2396. }
  2397. //
  2398. // Loop through the Lookup stack as we descend the binary tree doing an
  2399. // IgnoreCase lookup of the specified value.
  2400. //
  2401. while (TRUE) {
  2402. //
  2403. // Binary search in the current buffer for the first entry <= value.
  2404. //
  2405. Sp->IndexEntry = BinarySearchIndex( IrpContext,
  2406. Scb,
  2407. Sp,
  2408. Value );
  2409. //
  2410. // If this entry is not a node, then we are done.
  2411. //
  2412. if (!FlagOn( Sp->IndexEntry->Flags, INDEX_ENTRY_NODE )) {
  2413. IndexContext->Top =
  2414. IndexContext->Current = Sp;
  2415. //
  2416. // Check for and mark corrupt if we find an empty leaf.
  2417. //
  2418. if ((Sp != IndexContext->Base)
  2419. &&
  2420. FlagOn(NtfsFirstIndexEntry(Sp->IndexHeader)->Flags, INDEX_ENTRY_END)) {
  2421. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, NULL );
  2422. }
  2423. DebugTrace( -1, Dbg, ("FindFirstIndexEntry -> VOID\n") );
  2424. return;
  2425. }
  2426. //
  2427. // Check for special case where we preemptively push the root.
  2428. // Otherwise we can find ourselves recursively in NtfsAllocateRecord
  2429. // and NtfsAddAllocation, etc., on a buffer split which needs to push
  2430. // the root to add to the index allocation.
  2431. //
  2432. // First off, we only need to check this the first time through
  2433. // the loop, and only if the caller has the Scb exclusive.
  2434. //
  2435. if ((Sp == IndexContext->Base) &&
  2436. NtfsIsExclusiveScb(Scb) &&
  2437. !FlagOn( Scb->Vcb->VcbState, VCB_STATE_VOL_PURGE_IN_PROGRESS)) {
  2438. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  2439. FileRecord = NtfsContainingFileRecord(&IndexContext->AttributeContext);
  2440. //
  2441. // Only do the push if there are not enough bytes to allocate a
  2442. // record, and the root is already a node with down pointers, and
  2443. // the root is "big enough to move".
  2444. //
  2445. // This means this path will typically only kick in with directories
  2446. // with at least 200 files or so!
  2447. //
  2448. if (((FileRecord->BytesAvailable - FileRecord->FirstFreeByte) < (sizeof( ATTRIBUTE_LIST_ENTRY ) - sizeof( WCHAR ) + Scb->AttributeName.Length)) &&
  2449. FlagOn(IndexRoot->IndexHeader.Flags, INDEX_NODE) &&
  2450. (Attribute->RecordLength >= Scb->Vcb->BigEnoughToMove)) {
  2451. //
  2452. // Check if we want to defer pushing the root.
  2453. //
  2454. if (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_FORCE_PUSH )) {
  2455. //
  2456. // Our insertion point will now also be pushed, so we
  2457. // have to increment the stack pointer.
  2458. //
  2459. Sp += 1;
  2460. if (Sp >= IndexContext->Base + (ULONG)IndexContext->NumberEntries) {
  2461. NtfsGrowLookupStack( Scb,
  2462. IndexContext,
  2463. &Sp );
  2464. }
  2465. PushIndexRoot( IrpContext, Scb, IndexContext );
  2466. } else {
  2467. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED_PUSH );
  2468. }
  2469. }
  2470. }
  2471. //
  2472. // If the lookup stack is exhausted, grow the lookup stack.
  2473. //
  2474. Sp += 1;
  2475. if (Sp >= IndexContext->Base + (ULONG)IndexContext->NumberEntries) {
  2476. NtfsGrowLookupStack( Scb,
  2477. IndexContext,
  2478. &Sp );
  2479. }
  2480. //
  2481. // Otherwise, read the index buffer pointed to by the current
  2482. // Index Entry.
  2483. //
  2484. ReadIndexBuffer( IrpContext,
  2485. Scb,
  2486. NtfsIndexEntryBlock((Sp-1)->IndexEntry),
  2487. FALSE,
  2488. Sp );
  2489. }
  2490. }
  2491. BOOLEAN
  2492. FindNextIndexEntry (
  2493. IN PIRP_CONTEXT IrpContext,
  2494. IN PSCB Scb,
  2495. IN PVOID Value,
  2496. IN BOOLEAN ValueContainsWildcards,
  2497. IN BOOLEAN IgnoreCase,
  2498. IN OUT PINDEX_CONTEXT IndexContext,
  2499. IN BOOLEAN NextFlag,
  2500. OUT PBOOLEAN MustRestart OPTIONAL
  2501. )
  2502. /*++
  2503. Routine Description:
  2504. This routine performs a pre-order traversal of an index, starting from the
  2505. current position described by the index context, looking for the next match
  2506. for the input value, or the first value that indicates no further matches are
  2507. possible. Pre-order refers to the fact that starting at any given index entry,
  2508. we visit any descendents of that index entry first before visiting the index
  2509. entry itself, since all descendent index entries are lexigraphically less than
  2510. their parent index entry. A visit to an index entry is defined as either
  2511. detecting that the given index entry is the special end entry, or else testing
  2512. whether the index entry is a match for the input value expression.
  2513. The traversal either terminates successfully (returning TRUE) or unsuccessfully
  2514. (returning FALSE). It terminates successfully if a match is found and being
  2515. returned; in this case FindNextIndexEntry may be called again to look for the
  2516. next match. It terminates unsuccessfully if either the End entry is encountered
  2517. in the index root, or if an entry is found which is lexigraphically greater than
  2518. the input value, when compared ignoring case (if relevant).
  2519. The traversal is driven like a state machine driven by the index context, as
  2520. initialized from the preceding call to FindFirstIndexEntry, or as left from the
  2521. last call to this routine. The traversal algorithm is explained in comments
  2522. below.
  2523. The caller may specify whether it wants the current match to be returned (or
  2524. returned again), as described by the current state of the index context. Or it
  2525. may specify (with NextFlag TRUE) that it wishes to get the next match. Even if
  2526. NextFlag is FALSE, the currently described index entry will not be returned if
  2527. it is not a match.
  2528. Arguments:
  2529. Scb - Supplies the Scb for the index.
  2530. Value - Pointer to a value or value expression which should be used to position
  2531. the IndexContext. This value is already upcased if we are doing
  2532. an IgnoreCase compare and the value contains wildcards. Otherwise
  2533. the direct compare routine will upcase both values.
  2534. ValueContainsWildCards - Indicates if the value expression contains wild
  2535. cards. We can do a direct compare if it
  2536. doesn't.
  2537. IgnoreCase - Specified as TRUE if a case-insensitive match is desired (if
  2538. relevant for the collation rule).
  2539. IndexContext - Address of the initialized IndexContext, to return the desired
  2540. position.
  2541. NextFlag - Specified as FALSE if the currently described entry should be returned
  2542. if it is a match, or TRUE if the next entry should first be considered
  2543. for a match.
  2544. MustRestart - If specified and returning FALSE, returns TRUE if enumeration must
  2545. be restarted.
  2546. Return Value:
  2547. FALSE - if no entry is being returned, and there are no more matches.
  2548. TRUE - if an entry is being returned, and the caller may wish to call for further
  2549. matches.
  2550. --*/
  2551. {
  2552. PINDEX_ENTRY IndexEntry;
  2553. PINDEX_LOOKUP_STACK Sp;
  2554. FSRTL_COMPARISON_RESULT BlindResult;
  2555. BOOLEAN LocalMustRestart;
  2556. PWCH UpcaseTable = IrpContext->Vcb->UpcaseTable;
  2557. ULONG UpcaseTableSize = IrpContext->Vcb->UpcaseTableSize;
  2558. PAGED_CODE();
  2559. DebugTrace( +1, Dbg, ("FindNextIndexEntry\n") );
  2560. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  2561. DebugTrace( 0, Dbg, ("Value = %08lx\n", Value) );
  2562. DebugTrace( 0, Dbg, ("IgnoreCase = %02lx\n", IgnoreCase) );
  2563. DebugTrace( 0, Dbg, ("IndexContext = %08lx\n", IndexContext) );
  2564. DebugTrace( 0, Dbg, ("NextFlag = %02lx\n", NextFlag) );
  2565. if (!ARGUMENT_PRESENT(MustRestart)) {
  2566. MustRestart = &LocalMustRestart;
  2567. }
  2568. *MustRestart = FALSE;
  2569. if (IndexContext->ScbChangeCount != Scb->ScbType.Index.ChangeCount) {
  2570. DebugTrace( -1, Dbg, ("FindNextIndexEntry -> FALSE (must restart)\n") );
  2571. *MustRestart = TRUE;
  2572. return FALSE;
  2573. }
  2574. Sp = IndexContext->Current;
  2575. //
  2576. // If there is no Bcb for the current buffer, then we are continuing
  2577. // an enumeration.
  2578. //
  2579. if (Sp->Bcb == NULL) {
  2580. //
  2581. // Index Root case.
  2582. //
  2583. if (Sp == IndexContext->Base) {
  2584. //
  2585. // Lookup the attribute record from the Scb.
  2586. //
  2587. FindMoveableIndexRoot( IrpContext, Scb, IndexContext );
  2588. //
  2589. // Get out if someone has changed the file record.
  2590. //
  2591. if (Sp->CapturedLsn.QuadPart !=
  2592. NtfsContainingFileRecord(&IndexContext->AttributeContext)->Lsn.QuadPart) {
  2593. DebugTrace( -1, Dbg, ("FindNextIndexEntry -> FALSE (must restart)\n") );
  2594. *MustRestart = TRUE;
  2595. return FALSE;
  2596. }
  2597. //
  2598. // Index Buffer case.
  2599. //
  2600. } else {
  2601. //
  2602. // If the index buffer is unpinned, then see if we can read it and if it is
  2603. // unchanged.
  2604. //
  2605. if (!ReadIndexBuffer( IrpContext, Scb, 0, TRUE, Sp )) {
  2606. DebugTrace( -1, Dbg, ("FindNextIndexEntry -> FALSE (must restart)\n") );
  2607. *MustRestart = TRUE;
  2608. return FALSE;
  2609. }
  2610. }
  2611. }
  2612. //
  2613. // Load up some locals.
  2614. //
  2615. IndexEntry = Sp->IndexEntry;
  2616. //
  2617. // Loop until we hit a non-end record which is case-insensitive
  2618. // lexicgraphically greater than the target string. We also pop
  2619. // out the middle if we encounter the end record in the Index Root.
  2620. //
  2621. do {
  2622. //
  2623. // We last left our hero potentially somewhere in the middle of the
  2624. // Btree. If he is asking for the next record, we advance one entry
  2625. // in the current Index Buffer. If we are in an intermediate
  2626. // Index Buffer (there is a Btree Vcn), then we must move down
  2627. // through the first record in each intermediate Buffer until we hit
  2628. // the first leaf buffer (no Btree Vcn).
  2629. //
  2630. if (NextFlag) {
  2631. LONGLONG IndexBlock;
  2632. if (IndexEntry->Length == 0) {
  2633. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  2634. }
  2635. Sp->IndexEntry =
  2636. IndexEntry = NtfsNextIndexEntry( IndexEntry );
  2637. NtfsCheckIndexBound( IndexEntry, Sp->IndexHeader );
  2638. while (FlagOn(IndexEntry->Flags, INDEX_ENTRY_NODE)) {
  2639. IndexBlock = NtfsIndexEntryBlock(IndexEntry);
  2640. Sp += 1;
  2641. //
  2642. // If the tree is balanced we cannot go too far here.
  2643. //
  2644. if (Sp >= IndexContext->Base + (ULONG)IndexContext->NumberEntries) {
  2645. ASSERT(Sp < IndexContext->Base + (ULONG)IndexContext->NumberEntries);
  2646. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  2647. }
  2648. NtfsUnpinBcb( IrpContext, &Sp->Bcb );
  2649. ReadIndexBuffer( IrpContext,
  2650. Scb,
  2651. IndexBlock,
  2652. FALSE,
  2653. Sp );
  2654. IndexEntry = Sp->IndexEntry;
  2655. NtfsCheckIndexBound( IndexEntry, Sp->IndexHeader );
  2656. }
  2657. //
  2658. // Check for and mark corrupt if we find an empty leaf.
  2659. //
  2660. if ((Sp != IndexContext->Base)
  2661. &&
  2662. FlagOn(NtfsFirstIndexEntry(Sp->IndexHeader)->Flags, INDEX_ENTRY_END)) {
  2663. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  2664. }
  2665. }
  2666. //
  2667. // We could be pointing to an end record, either because of
  2668. // FindFirstIndexEntry or because NextFlag was TRUE, and we
  2669. // bumped our pointer to an end record. At any rate, if so, we
  2670. // move up the tree, rereading as required, until we find an entry
  2671. // which is not an end record, or until we hit the end record in the
  2672. // root, which means we hit the end of the Index.
  2673. //
  2674. while (FlagOn(IndexEntry->Flags, INDEX_ENTRY_END)) {
  2675. if (Sp == IndexContext->Base) {
  2676. DebugTrace( -1, Dbg, ("FindNextIndexEntry -> FALSE (End of Index)\n") );
  2677. return FALSE;
  2678. }
  2679. Sp -= 1;
  2680. //
  2681. // If there is no Bcb for the current buffer, then we are continuing
  2682. // an enumeration.
  2683. //
  2684. if (Sp->Bcb == NULL) {
  2685. //
  2686. // Index Root case.
  2687. //
  2688. if (Sp == IndexContext->Base) {
  2689. //
  2690. // Lookup the attribute record from the Scb.
  2691. //
  2692. FindMoveableIndexRoot( IrpContext, Scb, IndexContext );
  2693. //
  2694. // Get out if someone has changed the file record.
  2695. //
  2696. if (Sp->CapturedLsn.QuadPart !=
  2697. NtfsContainingFileRecord(&IndexContext->AttributeContext)->Lsn.QuadPart) {
  2698. DebugTrace( -1, Dbg, ("FindNextIndexEntry -> FALSE (must restart)\n") );
  2699. *MustRestart = TRUE;
  2700. return FALSE;
  2701. }
  2702. //
  2703. // Index Buffer case.
  2704. //
  2705. } else {
  2706. //
  2707. // If the index buffer is unpinned, then see if we can read it and if it is
  2708. // unchanged.
  2709. //
  2710. if (!ReadIndexBuffer( IrpContext, Scb, 0, TRUE, Sp )) {
  2711. DebugTrace( -1, Dbg, ("FindNextIndexEntry -> FALSE (must restart)\n") );
  2712. *MustRestart = TRUE;
  2713. return FALSE;
  2714. }
  2715. }
  2716. }
  2717. IndexEntry = Sp->IndexEntry;
  2718. NtfsCheckIndexBound( IndexEntry, Sp->IndexHeader );
  2719. }
  2720. //
  2721. // For a view Index, we either need to call the MatchFunction in the Index
  2722. // Context (if ValueContainsWildCards is TRUE), or else we look for equality
  2723. // from the CollateFunction in the Scb.
  2724. //
  2725. if (FlagOn(Scb->ScbState, SCB_STATE_VIEW_INDEX)) {
  2726. INDEX_ROW IndexRow;
  2727. NTSTATUS Status;
  2728. IndexRow.KeyPart.Key = IndexEntry + 1;
  2729. IndexRow.KeyPart.KeyLength = IndexEntry->AttributeLength;
  2730. //
  2731. // Now, if ValueContainsWildcards is TRUE, then we are doing multiple
  2732. // returns via the match function (for NtOfsReadRecords).
  2733. //
  2734. if (ValueContainsWildcards) {
  2735. IndexRow.DataPart.Data = Add2Ptr( IndexEntry, IndexEntry->DataOffset );
  2736. IndexRow.DataPart.DataLength = IndexEntry->DataLength;
  2737. if ((Status = IndexContext->MatchFunction(&IndexRow,
  2738. IndexContext->MatchData)) == STATUS_SUCCESS) {
  2739. IndexContext->Current = Sp;
  2740. Sp->IndexEntry = IndexEntry;
  2741. return TRUE;
  2742. //
  2743. // Get out if no more matches.
  2744. //
  2745. } else if (Status == STATUS_NO_MORE_MATCHES) {
  2746. return FALSE;
  2747. }
  2748. BlindResult = GreaterThan;
  2749. //
  2750. // Otherwise, we are looking for an exact match via the CollateFunction.
  2751. //
  2752. } else {
  2753. if ((BlindResult =
  2754. Scb->ScbType.Index.CollationFunction((PINDEX_KEY)Value,
  2755. &IndexRow.KeyPart,
  2756. Scb->ScbType.Index.CollationData)) == EqualTo) {
  2757. IndexContext->Current = Sp;
  2758. Sp->IndexEntry = IndexEntry;
  2759. return TRUE;
  2760. }
  2761. }
  2762. //
  2763. // At this point, we have a real live entry that we have to check
  2764. // for a match. Describe its name, see if its a match, and return
  2765. // TRUE if it is.
  2766. //
  2767. } else if (ValueContainsWildcards) {
  2768. if ((*NtfsIsInExpression[Scb->ScbType.Index.CollationRule])
  2769. ( UpcaseTable,
  2770. Value,
  2771. IndexEntry,
  2772. IgnoreCase )) {
  2773. IndexContext->Current = Sp;
  2774. Sp->IndexEntry = IndexEntry;
  2775. DebugTrace( 0, Dbg, ("IndexEntry < %08lx\n", IndexEntry) );
  2776. DebugTrace( -1, Dbg, ("FindNextIndexEntry -> TRUE\n") );
  2777. return TRUE;
  2778. }
  2779. } else {
  2780. if ((*NtfsIsEqual[Scb->ScbType.Index.CollationRule])
  2781. ( UpcaseTable,
  2782. Value,
  2783. IndexEntry,
  2784. IgnoreCase )) {
  2785. IndexContext->Current = Sp;
  2786. Sp->IndexEntry = IndexEntry;
  2787. DebugTrace( 0, Dbg, ("IndexEntry < %08lx\n", IndexEntry) );
  2788. DebugTrace( -1, Dbg, ("FindNextIndexEntry -> TRUE\n") );
  2789. return TRUE;
  2790. }
  2791. }
  2792. //
  2793. // If we loop back up, we must set NextFlag to TRUE. We are
  2794. // currently on a valid non-end entry now. Before looping back,
  2795. // check to see if we are beyond end of Target string by doing
  2796. // a case blind compare (IgnoreCase == TRUE).
  2797. //
  2798. NextFlag = TRUE;
  2799. //
  2800. // For enumerations in view indices, keep going and only terminate
  2801. // on the MatchFunction (BlindResult was set to GreaterThan above).
  2802. // If it is not an enumeration (no wild cards), we already set BlindResult
  2803. // when we called the colation routine above.
  2804. //
  2805. if (!FlagOn(Scb->ScbState, SCB_STATE_VIEW_INDEX)) {
  2806. BlindResult = (*NtfsCompareValues[Scb->ScbType.Index.CollationRule])
  2807. ( UpcaseTable,
  2808. UpcaseTableSize,
  2809. Value,
  2810. IndexEntry,
  2811. GreaterThan,
  2812. TRUE );
  2813. }
  2814. //
  2815. // The following while clause is not as bad as it looks, and it will
  2816. // evaluate quickly for the IgnoreCase == TRUE case. We have
  2817. // already done an IgnoreCase compare above, and stored the result
  2818. // in BlindResult.
  2819. //
  2820. // If we are doing an IgnoreCase TRUE find, we should keep going if
  2821. // BlindResult is either GreaterThan or EqualTo.
  2822. //
  2823. // If we are doing an IgnoreCase FALSE scan, then also continue if
  2824. // BlindResult is GreaterThan, but if BlindResult is EqualTo, we
  2825. // can only proceed if we are also GreaterThan or EqualTo case
  2826. // sensitive (i.e. != LessThan). This means that the Compare Values
  2827. // call in the following expresssion will never occur in an IgnoreCase
  2828. // TRUE scan (Windows default).
  2829. //
  2830. } while ((BlindResult == GreaterThan)
  2831. ||
  2832. ((BlindResult == EqualTo)
  2833. &&
  2834. (IgnoreCase
  2835. ||
  2836. ((*NtfsCompareValues[Scb->ScbType.Index.CollationRule])
  2837. ( UpcaseTable,
  2838. UpcaseTableSize,
  2839. Value,
  2840. IndexEntry,
  2841. GreaterThan,
  2842. FALSE ) != LessThan))));
  2843. DebugTrace( -1, Dbg, ("FindNextIndexEntry -> FALSE (end of expression)\n") );
  2844. return FALSE;
  2845. }
  2846. //
  2847. // Internal routine
  2848. //
  2849. PATTRIBUTE_RECORD_HEADER
  2850. FindMoveableIndexRoot (
  2851. IN PIRP_CONTEXT IrpContext,
  2852. IN PSCB Scb,
  2853. IN OUT PINDEX_CONTEXT IndexContext
  2854. )
  2855. /*++
  2856. Routine Description:
  2857. This routine looks up the index root attribute again after it has potentially
  2858. moved. As a side effect it reloads any other fields in the index context that
  2859. could have changed, so callers should always call this routine first before
  2860. accessing the other values.
  2861. Arguments:
  2862. Scb - Scb for the index
  2863. IndexContext - The index context assumed to already be pointing to the
  2864. active search path
  2865. Return Value:
  2866. The address of the Index Root attribute record header.
  2867. --*/
  2868. {
  2869. PATTRIBUTE_RECORD_HEADER OldAttribute, Attribute;
  2870. PINDEX_ROOT IndexRoot;
  2871. PBCB SavedBcb;
  2872. BOOLEAN Found;
  2873. PAGED_CODE();
  2874. //
  2875. // Check to see if the captured Lsn in the IndexContext matches
  2876. // the one currently in the file record. If it does match, then
  2877. // we know that the Index Root cannot possibly have moved and that
  2878. // the information in IndexContext->AttributeContext is up-to-date.
  2879. //
  2880. if (
  2881. //
  2882. // There's a pointer to a file record
  2883. //
  2884. IndexContext->AttributeContext.FoundAttribute.FileRecord != NULL &&
  2885. //
  2886. // Quad parts of Lsn match
  2887. //
  2888. IndexContext->IndexRootFileRecordLsn.QuadPart ==
  2889. IndexContext->AttributeContext.FoundAttribute.FileRecord->Lsn.QuadPart) {
  2890. return NtfsFoundAttribute(&IndexContext->AttributeContext);
  2891. }
  2892. OldAttribute = IndexContext->OldAttribute;
  2893. //
  2894. // Temporarily save the Bcb for the index root, and unpin at the end,
  2895. // so that it does not get unexpectedly unmapped and remapped when our
  2896. // caller knows it can't actually move.
  2897. //
  2898. SavedBcb = IndexContext->AttributeContext.FoundAttribute.Bcb;
  2899. IndexContext->AttributeContext.FoundAttribute.Bcb = NULL;
  2900. NtfsCleanupAttributeContext( IrpContext, &IndexContext->AttributeContext );
  2901. NtfsInitializeAttributeContext( &IndexContext->AttributeContext );
  2902. try {
  2903. Found =
  2904. NtfsLookupAttributeByName( IrpContext,
  2905. Scb->Fcb,
  2906. &Scb->Fcb->FileReference,
  2907. $INDEX_ROOT,
  2908. &Scb->AttributeName,
  2909. NULL,
  2910. FALSE,
  2911. &IndexContext->AttributeContext );
  2912. ASSERT(Found);
  2913. //
  2914. // Now we have to reload our attribute pointer and point to the root
  2915. //
  2916. IndexContext->OldAttribute =
  2917. Attribute = NtfsFoundAttribute(&IndexContext->AttributeContext);
  2918. IndexRoot = (PINDEX_ROOT)NtfsAttributeValue(Attribute);
  2919. //
  2920. // Reload start of buffer and index header appropriately.
  2921. //
  2922. IndexContext->Base->StartOfBuffer =
  2923. (PVOID)NtfsContainingFileRecord(&IndexContext->AttributeContext);
  2924. IndexContext->Base->IndexHeader = &IndexRoot->IndexHeader;
  2925. //
  2926. // Relocate the index entry on the search path by moving its pointer the
  2927. // same number of bytes that the attribute moved.
  2928. //
  2929. IndexContext->Base->IndexEntry = (PINDEX_ENTRY)((PCHAR)IndexContext->Base->IndexEntry +
  2930. ((PCHAR)Attribute - (PCHAR)OldAttribute));
  2931. //
  2932. // Save Lsn of file record containing Index Root so that later
  2933. // we can tell if we need to re-find it.
  2934. //
  2935. IndexContext->IndexRootFileRecordLsn =
  2936. IndexContext->AttributeContext.FoundAttribute.FileRecord->Lsn;
  2937. } finally {
  2938. NtfsUnpinBcb( IrpContext, &SavedBcb );
  2939. }
  2940. return Attribute;
  2941. }
  2942. PINDEX_ENTRY
  2943. BinarySearchIndex (
  2944. IN PIRP_CONTEXT IrpContext,
  2945. IN PSCB Scb,
  2946. IN OUT PINDEX_LOOKUP_STACK Sp,
  2947. IN PVOID Value
  2948. )
  2949. /*++
  2950. Routine Description:
  2951. This routine does a binary search of the current index buffer, for the first entry
  2952. in the buffer which is lexigraphically less than or equal to the input value, when
  2953. compared case-insensitive (if relevant).
  2954. Arguments:
  2955. Scb - Supplies the Scb for the index.
  2956. Sp - Supplies a pointer to a lookup stack entry describing the current buffer.
  2957. Value - Pointer to a value or value expression which should be used to position
  2958. the IndexContext.
  2959. Return Value:
  2960. None.
  2961. --*/
  2962. {
  2963. PINDEX_HEADER IndexHeader;
  2964. PINDEX_ENTRY IndexTemp, IndexLast;
  2965. ULONG LowIndex, HighIndex, TryIndex;
  2966. PINDEX_ENTRY LocalEntries[BINARY_SEARCH_ENTRIES];
  2967. PINDEX_ENTRY *Table = LocalEntries;
  2968. ULONG SizeOfTable = BINARY_SEARCH_ENTRIES;
  2969. PAGED_CODE();
  2970. DebugTrace( +1, Dbg, ("BinarySearchIndex\n") );
  2971. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  2972. DebugTrace( 0, Dbg, ("Sp = %08lx\n", Sp) );
  2973. DebugTrace( 0, Dbg, ("Value = %08lx\n", Value) );
  2974. //
  2975. // Set up to fill in our binary search vector.
  2976. //
  2977. IndexHeader = Sp->IndexHeader;
  2978. IndexTemp = (PINDEX_ENTRY)((PCHAR)IndexHeader + IndexHeader->FirstIndexEntry);
  2979. IndexLast = (PINDEX_ENTRY)((PCHAR)IndexHeader + IndexHeader->FirstFreeByte);
  2980. //
  2981. // Check that there will be at least 1 entry in the index.
  2982. //
  2983. if (IndexHeader->FirstIndexEntry >= IndexHeader->FirstFreeByte) {
  2984. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  2985. }
  2986. //
  2987. // Fill in the binary search vector, first trying our local vector, and
  2988. // allocating a larger one if we need to.
  2989. //
  2990. HighIndex = 0;
  2991. while (TRUE) {
  2992. while (IndexTemp < IndexLast) {
  2993. //
  2994. // See if we are about to store off the end of the table. If
  2995. // so we will have to go allocate a bigger one.
  2996. //
  2997. if (HighIndex >= SizeOfTable) {
  2998. break;
  2999. }
  3000. Table[HighIndex] = IndexTemp;
  3001. //
  3002. // Check for a corrupt IndexEntry where the length is zero. Since
  3003. // Length is unsigned, we can't test for it being negative.
  3004. //
  3005. if (IndexTemp->Length == 0) {
  3006. if (Table != LocalEntries) {
  3007. NtfsFreePool( Table );
  3008. }
  3009. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  3010. }
  3011. IndexTemp = (PINDEX_ENTRY)((PCHAR)IndexTemp + IndexTemp->Length);
  3012. HighIndex += 1;
  3013. }
  3014. //
  3015. // If we got them all, then get out.
  3016. //
  3017. if (IndexTemp >= IndexLast) {
  3018. break;
  3019. }
  3020. if (Table != LocalEntries) {
  3021. ASSERT( Table != LocalEntries );
  3022. NtfsFreePool( Table );
  3023. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  3024. }
  3025. //
  3026. // Otherwise we have to allocate one. Calculate the worst case
  3027. // and allocate it.
  3028. //
  3029. SizeOfTable = (IndexHeader->BytesAvailable /
  3030. (sizeof(INDEX_ENTRY) + sizeof(LARGE_INTEGER))) + 2;
  3031. Table = (PINDEX_ENTRY *)NtfsAllocatePool(PagedPool, SizeOfTable * sizeof(PINDEX_ENTRY));
  3032. RtlMoveMemory( Table, LocalEntries, BINARY_SEARCH_ENTRIES * sizeof(PINDEX_ENTRY) );
  3033. }
  3034. //
  3035. // Now do a binary search of the buffer to find the lowest entry
  3036. // (ignoring case) that is <= to the search value. During the
  3037. // binary search, LowIndex is always maintained as the lowest
  3038. // possible Index Entry that is <=, and HighIndex is maintained as
  3039. // the highest possible Index that could be the first <= match.
  3040. // Thus the loop exits when LowIndex == HighIndex.
  3041. //
  3042. ASSERT(HighIndex);
  3043. HighIndex -= 1;
  3044. LowIndex = 0;
  3045. //
  3046. // For view indices, we collate via the CollationFunction in the Scb.
  3047. //
  3048. if (FlagOn(Scb->ScbState, SCB_STATE_VIEW_INDEX)) {
  3049. INDEX_KEY IndexKey;
  3050. while (LowIndex != HighIndex) {
  3051. TryIndex = LowIndex + (HighIndex - LowIndex) / 2;
  3052. IndexKey.Key = Table[TryIndex] + 1;
  3053. IndexKey.KeyLength = Table[TryIndex]->AttributeLength;
  3054. if (!FlagOn( Table[TryIndex]->Flags, INDEX_ENTRY_END )
  3055. &&
  3056. (Scb->ScbType.Index.CollationFunction((PINDEX_KEY)Value,
  3057. &IndexKey,
  3058. Scb->ScbType.Index.CollationData) == GreaterThan)) {
  3059. LowIndex = TryIndex + 1;
  3060. }
  3061. else {
  3062. HighIndex = TryIndex;
  3063. }
  3064. }
  3065. } else {
  3066. while (LowIndex != HighIndex) {
  3067. PWCH UpcaseTable = IrpContext->Vcb->UpcaseTable;
  3068. ULONG UpcaseTableSize = IrpContext->Vcb->UpcaseTableSize;
  3069. TryIndex = LowIndex + (HighIndex - LowIndex) / 2;
  3070. if (!FlagOn( Table[TryIndex]->Flags, INDEX_ENTRY_END )
  3071. &&
  3072. (*NtfsCompareValues[Scb->ScbType.Index.CollationRule])
  3073. ( UpcaseTable,
  3074. UpcaseTableSize,
  3075. Value,
  3076. Table[TryIndex],
  3077. LessThan,
  3078. TRUE ) == GreaterThan) {
  3079. LowIndex = TryIndex + 1;
  3080. }
  3081. else {
  3082. HighIndex = TryIndex;
  3083. }
  3084. }
  3085. }
  3086. //
  3087. // Capture the return pointer and free our binary search table.
  3088. //
  3089. IndexTemp = Table[LowIndex];
  3090. if (Table != LocalEntries) {
  3091. NtfsFreePool( Table );
  3092. }
  3093. //
  3094. // When we exit the loop, we have the answer.
  3095. //
  3096. DebugTrace( -1, Dbg, ("BinarySearchIndex -> %08lx\n", IndexTemp) );
  3097. return IndexTemp;
  3098. }
  3099. BOOLEAN
  3100. AddToIndex (
  3101. IN PIRP_CONTEXT IrpContext,
  3102. IN PSCB Scb,
  3103. IN PINDEX_ENTRY InsertIndexEntry,
  3104. IN OUT PINDEX_CONTEXT IndexContext,
  3105. OUT PQUICK_INDEX QuickIndex OPTIONAL,
  3106. IN BOOLEAN FindRoot
  3107. )
  3108. /*++
  3109. Routine Description:
  3110. This routine inserts an index entry into the Btree, possibly performing
  3111. one or more buffer splits as required.
  3112. The caller has positioned the index context to point to the correct
  3113. insertion point in a leaf buffer, via calls to FindFirstIndexEntry and
  3114. FindNextIndexEntry. The index context thus not only points to the
  3115. insertion point, but it also shows where index entries will have to be
  3116. promoted in the event of buffer splits.
  3117. This routine employs tail-end recursion, to eliminate the cost of nested
  3118. calls. For the first insert and all potential subsequent inserts due to
  3119. bucket splits, all work is completed at the current level in the Btree,
  3120. and then the InsertIndexEntry input parameter is reloaded (and the lookup
  3121. stack pointer is adjusted), before looping back in the while loop to do
  3122. any necessary insert at the next level in the tree.
  3123. Each pass through the loop dispatches to one of four routines to handle
  3124. the following cases:
  3125. Simple insert in the root
  3126. Push the root down (add one level to the tree) if the root is full
  3127. Simple insert in an index allocation buffer
  3128. Buffer split of a full index allocation buffer
  3129. Arguments:
  3130. Scb - Supplies the Scb for the index.
  3131. InsertIndexEntry - pointer to the index entry to insert.
  3132. IndexContext - Index context describing the path to the insertion point.
  3133. QuickIndex - If specified we store the location of the index added.
  3134. FindRoot - Supplies TRUE if the context cannot be trusted and we should find
  3135. the root first.
  3136. Return Value:
  3137. FALSE -- if the stack did not have to be pushed
  3138. TRUE -- if the stack was pushed
  3139. --*/
  3140. {
  3141. PFCB Fcb = Scb->Fcb;
  3142. PINDEX_LOOKUP_STACK Sp = IndexContext->Current;
  3143. BOOLEAN DeleteIt = FALSE;
  3144. BOOLEAN FirstPass = TRUE;
  3145. BOOLEAN StackWasPushed = FALSE;
  3146. BOOLEAN ReturnValue;
  3147. PAGED_CODE();
  3148. DebugTrace( +1, Dbg, ("AddToIndex\n") );
  3149. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  3150. DebugTrace( 0, Dbg, ("InsertIndexEntry = %08lx\n", InsertIndexEntry) );
  3151. DebugTrace( 0, Dbg, ("IndexContext = %08lx\n", IndexContext) );
  3152. try {
  3153. //
  3154. // This routine uses "tail-end" recursion, so we just want to keep looping
  3155. // until we do an insert (either in the Index Root or the Index Allocation)
  3156. // that does not require a split.
  3157. //
  3158. while (TRUE) {
  3159. IndexContext->Current = Sp;
  3160. //
  3161. // First see if this is an insert in the root or a leaf.
  3162. //
  3163. if (Sp == IndexContext->Base) {
  3164. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  3165. PATTRIBUTE_RECORD_HEADER Attribute;
  3166. FileRecord = (PFILE_RECORD_SEGMENT_HEADER)Sp->StartOfBuffer;
  3167. Attribute = NtfsFoundAttribute(&IndexContext->AttributeContext);
  3168. //
  3169. // If the caller is telling us we need to look up the root again,
  3170. // then do so.
  3171. //
  3172. if (FindRoot) {
  3173. Attribute = FindMoveableIndexRoot( IrpContext, Scb, IndexContext );
  3174. }
  3175. //
  3176. // Now see if there is enough room to do a simple insert, or if
  3177. // there is not enough room, see if we are small enough ourselves
  3178. // to demand the room be made anyway.
  3179. //
  3180. if ((InsertIndexEntry->Length <=
  3181. (USHORT) (FileRecord->BytesAvailable - FileRecord->FirstFreeByte))
  3182. ||
  3183. ((InsertIndexEntry->Length + Attribute->RecordLength) <
  3184. Scb->Vcb->BigEnoughToMove)) {
  3185. //
  3186. // If FALSE is returned, then the space was not allocated and
  3187. // we have too loop back and try again. Second time must work.
  3188. //
  3189. while (!NtfsChangeAttributeSize( IrpContext,
  3190. Fcb,
  3191. Attribute->RecordLength + InsertIndexEntry->Length,
  3192. &IndexContext->AttributeContext )) {
  3193. //
  3194. // Lookup the attribute again.
  3195. //
  3196. Attribute = FindMoveableIndexRoot( IrpContext, Scb, IndexContext );
  3197. }
  3198. InsertSimpleRoot( IrpContext,
  3199. Scb,
  3200. InsertIndexEntry,
  3201. IndexContext );
  3202. //
  3203. // If we have a quick index then store a buffer offset of zero
  3204. // to indicate the root index.
  3205. //
  3206. if (ARGUMENT_PRESENT( QuickIndex )) {
  3207. QuickIndex->BufferOffset = 0;
  3208. }
  3209. DebugTrace( -1, Dbg, ("AddToIndex -> VOID\n") );
  3210. ReturnValue = StackWasPushed;
  3211. leave;
  3212. //
  3213. // Otherwise we have to push the current root down a level into
  3214. // the allocation, and try our insert again by looping back.
  3215. //
  3216. } else {
  3217. //
  3218. // Our insertion point will now also be pushed, so we
  3219. // have to increment the stack pointer.
  3220. //
  3221. Sp += 1;
  3222. if (Sp >= IndexContext->Base + (ULONG)IndexContext->NumberEntries) {
  3223. NtfsGrowLookupStack( Scb,
  3224. IndexContext,
  3225. &Sp );
  3226. }
  3227. PushIndexRoot( IrpContext, Scb, IndexContext );
  3228. StackWasPushed = TRUE;
  3229. continue;
  3230. }
  3231. //
  3232. // Otherwise this insert is in the Index Allocation, not the Index
  3233. // Root.
  3234. //
  3235. } else {
  3236. //
  3237. // See if there is enough room to do a simple insert.
  3238. //
  3239. if (InsertIndexEntry->Length <=
  3240. (USHORT)(Sp->IndexHeader->BytesAvailable - Sp->IndexHeader->FirstFreeByte)) {
  3241. InsertSimpleAllocation( IrpContext,
  3242. Scb,
  3243. InsertIndexEntry,
  3244. Sp,
  3245. QuickIndex );
  3246. DebugTrace( -1, Dbg, ("AddToIndex -> VOID\n") );
  3247. ReturnValue = StackWasPushed;
  3248. leave;
  3249. //
  3250. // Otherwise, we have to do a buffer split in the allocation.
  3251. //
  3252. } else {
  3253. //
  3254. // Call this local routine to do the buffer split. It
  3255. // returns a pointer to a new entry to insert, which is
  3256. // allocated in the nonpaged pool.
  3257. //
  3258. PINDEX_ENTRY NewInsertIndexEntry;
  3259. NewInsertIndexEntry =
  3260. InsertWithBufferSplit( IrpContext,
  3261. Scb,
  3262. InsertIndexEntry,
  3263. IndexContext,
  3264. QuickIndex );
  3265. //
  3266. // Remove the old key being inserted if we've
  3267. // allocated it.
  3268. //
  3269. if (DeleteIt) {
  3270. NtfsFreePool( InsertIndexEntry );
  3271. }
  3272. //
  3273. // Clear the QuickIndex pointer so we don't corrupt the captured
  3274. // information.
  3275. //
  3276. QuickIndex = NULL;
  3277. //
  3278. // Now we have to delete this index entry, since it was dynamically
  3279. // allocated by InsertWithBufferSplit.
  3280. //
  3281. InsertIndexEntry = NewInsertIndexEntry;
  3282. DeleteIt = TRUE;
  3283. //
  3284. // The middle entry from the old buffer must now get
  3285. // inserted at the insertion point in its parent.
  3286. //
  3287. Sp -= 1;
  3288. continue;
  3289. }
  3290. }
  3291. }
  3292. } finally {
  3293. if (DeleteIt) {
  3294. NtfsFreePool( InsertIndexEntry );
  3295. }
  3296. }
  3297. return ReturnValue;
  3298. }
  3299. VOID
  3300. InsertSimpleRoot (
  3301. IN PIRP_CONTEXT IrpContext,
  3302. IN PSCB Scb,
  3303. IN PINDEX_ENTRY InsertIndexEntry,
  3304. IN OUT PINDEX_CONTEXT IndexContext
  3305. )
  3306. /*++
  3307. Routine Description:
  3308. This routine is called to do a simple insertion of a new index entry into the
  3309. root, when it is known that it will fit. It calls a routine common wiht
  3310. restart to do the insert, and then logs the insert.
  3311. Arguments:
  3312. Scb - Supplies the Scb for the index.
  3313. InsertIndexEntry - Pointer to the index entry to insert.
  3314. IndexContext - Index context describing the position in the root at which
  3315. the insert is to occur.
  3316. Return Value:
  3317. None
  3318. --*/
  3319. {
  3320. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  3321. PATTRIBUTE_RECORD_HEADER Attribute;
  3322. PINDEX_ENTRY BeforeIndexEntry;
  3323. PATTRIBUTE_ENUMERATION_CONTEXT Context = &IndexContext->AttributeContext;
  3324. PVCB Vcb;
  3325. BOOLEAN Inserted = FALSE;
  3326. PAGED_CODE();
  3327. Vcb = Scb->Vcb;
  3328. DebugTrace( +1, Dbg, ("InsertSimpleRoot\n") );
  3329. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  3330. DebugTrace( 0, Dbg, ("InsertIndexEntry = %08lx\n", InsertIndexEntry) );
  3331. DebugTrace( 0, Dbg, ("IndexContext = %08lx\n", IndexContext) );
  3332. try {
  3333. //
  3334. // Extract all of the updates required by the restart routine we
  3335. // will call.
  3336. //
  3337. FileRecord = NtfsContainingFileRecord( Context );
  3338. Attribute = NtfsFoundAttribute( Context );
  3339. BeforeIndexEntry = IndexContext->Base->IndexEntry;
  3340. //
  3341. // Pin the page
  3342. //
  3343. NtfsPinMappedAttribute( IrpContext, Vcb, Context );
  3344. //
  3345. // Call the same routine used by restart to actually apply the
  3346. // update.
  3347. //
  3348. NtfsRestartInsertSimpleRoot( IrpContext,
  3349. InsertIndexEntry,
  3350. FileRecord,
  3351. Attribute,
  3352. BeforeIndexEntry );
  3353. Inserted = TRUE;
  3354. CheckRoot();
  3355. //
  3356. // Now that the Index Entry is guaranteed to be in place, log
  3357. // this update. Note that the new record is now at the address
  3358. // we calculated in BeforeIndexEntry.
  3359. //
  3360. FileRecord->Lsn =
  3361. NtfsWriteLog( IrpContext,
  3362. Vcb->MftScb,
  3363. NtfsFoundBcb(Context),
  3364. AddIndexEntryRoot,
  3365. BeforeIndexEntry,
  3366. BeforeIndexEntry->Length,
  3367. DeleteIndexEntryRoot,
  3368. NULL,
  3369. 0,
  3370. NtfsMftOffset( Context ),
  3371. (ULONG)((PCHAR)Attribute - (PCHAR)FileRecord),
  3372. (ULONG)((PCHAR)BeforeIndexEntry - (PCHAR)Attribute),
  3373. Vcb->BytesPerFileRecordSegment );
  3374. } finally {
  3375. DebugUnwind( InsertSimpleRoot );
  3376. //
  3377. // If we failed after inserting the record, it must be because we failed to write the
  3378. // log record. If that happened, then the record will not be
  3379. // deleted by the transaction abort, so we have to do it here
  3380. // by hand.
  3381. //
  3382. if (AbnormalTermination() && Inserted) {
  3383. NtfsRestartDeleteSimpleRoot( IrpContext,
  3384. BeforeIndexEntry,
  3385. FileRecord,
  3386. Attribute );
  3387. }
  3388. }
  3389. DebugTrace( -1, Dbg, ("InsertSimpleRoot -> VOID\n") );
  3390. }
  3391. VOID
  3392. NtfsRestartInsertSimpleRoot (
  3393. IN PIRP_CONTEXT IrpContext,
  3394. IN PINDEX_ENTRY InsertIndexEntry,
  3395. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  3396. IN PATTRIBUTE_RECORD_HEADER Attribute,
  3397. IN PINDEX_ENTRY BeforeIndexEntry
  3398. )
  3399. /*++
  3400. Routine Description:
  3401. This is a restart routine used both in normal operation and during restart.
  3402. It is called to do a simple insertion of a new index entry into the
  3403. root, when it is known that it will fit. It does no logging.
  3404. Arguments:
  3405. InsertIndexEntry - Pointer to the index entry to insert.
  3406. FileRecord - Pointer to the file record in which the insert is to occur.
  3407. Attribute - Pointer to the attribute record header for the index root.
  3408. BeforeIndexEntry - Pointer to the index entry which is currently sitting
  3409. at the point at which the insert is to occur.
  3410. Return Value:
  3411. None
  3412. --*/
  3413. {
  3414. PINDEX_ROOT IndexRoot;
  3415. PINDEX_HEADER IndexHeader;
  3416. PAGED_CODE();
  3417. DebugTrace( +1, Dbg, ("NtfsRestartInsertSimpleRoot\n") );
  3418. DebugTrace( 0, Dbg, ("InsertIndexEntry = %08lx\n", InsertIndexEntry) );
  3419. DebugTrace( 0, Dbg, ("FileRecord = %08lx\n", FileRecord) );
  3420. DebugTrace( 0, Dbg, ("Attribute = %08lx\n", Attribute) );
  3421. DebugTrace( 0, Dbg, ("BeforeIndexEntry = %08lx\n", BeforeIndexEntry) );
  3422. //
  3423. // Form some pointers within the attribute value.
  3424. //
  3425. IndexRoot = (PINDEX_ROOT)NtfsAttributeValue(Attribute);
  3426. IndexHeader = &IndexRoot->IndexHeader;
  3427. //
  3428. // Grow the space for our attribute record as required.
  3429. //
  3430. NtfsRestartChangeAttributeSize( IrpContext,
  3431. FileRecord,
  3432. Attribute,
  3433. Attribute->RecordLength +
  3434. InsertIndexEntry->Length );
  3435. //
  3436. // Now move the tail end of the index to make room for the new entry.
  3437. //
  3438. RtlMoveMemory( (PCHAR)BeforeIndexEntry + InsertIndexEntry->Length,
  3439. BeforeIndexEntry,
  3440. ((PCHAR)IndexHeader + IndexHeader->FirstFreeByte) -
  3441. (PCHAR)BeforeIndexEntry );
  3442. //
  3443. // Move the new Index Entry into place. The index entry may either
  3444. // be a complete index entry, or it may be in pointer form.
  3445. //
  3446. if (FlagOn(InsertIndexEntry->Flags, INDEX_ENTRY_POINTER_FORM)) {
  3447. RtlMoveMemory( BeforeIndexEntry, InsertIndexEntry, sizeof(INDEX_ENTRY) );
  3448. RtlMoveMemory( (PVOID)(BeforeIndexEntry + 1),
  3449. *(PVOID *)(InsertIndexEntry + 1),
  3450. InsertIndexEntry->AttributeLength );
  3451. //
  3452. // In pointer form the Data Pointer follows the key pointer, but there is
  3453. // none for normal directory indices.
  3454. //
  3455. if (*(PVOID *)((PCHAR)InsertIndexEntry + sizeof(INDEX_ENTRY) + sizeof(PVOID)) != NULL) {
  3456. RtlMoveMemory( (PVOID)((PCHAR)BeforeIndexEntry + InsertIndexEntry->DataOffset),
  3457. *(PVOID *)((PCHAR)InsertIndexEntry + sizeof(INDEX_ENTRY) + sizeof(PVOID)),
  3458. InsertIndexEntry->DataLength );
  3459. }
  3460. ClearFlag( BeforeIndexEntry->Flags, INDEX_ENTRY_POINTER_FORM );
  3461. } else {
  3462. RtlMoveMemory( BeforeIndexEntry, InsertIndexEntry, InsertIndexEntry->Length );
  3463. }
  3464. //
  3465. // Update the index header by the space we grew by.
  3466. //
  3467. Attribute->Form.Resident.ValueLength += InsertIndexEntry->Length;
  3468. IndexHeader->FirstFreeByte += InsertIndexEntry->Length;
  3469. IndexHeader->BytesAvailable += InsertIndexEntry->Length;
  3470. DebugTrace( -1, Dbg, ("NtfsRestartInsertSimpleRoot -> VOID\n") );
  3471. }
  3472. VOID
  3473. PushIndexRoot (
  3474. IN PIRP_CONTEXT IrpContext,
  3475. IN PSCB Scb,
  3476. IN OUT PINDEX_CONTEXT IndexContext
  3477. )
  3478. /*++
  3479. Routine Description:
  3480. This routine is called to push the index root down a level, thus adding a
  3481. level to the Btree. If the Index Allocation and Bitmap attributes for this
  3482. index do not already exist, then they are created here prior to pushing the
  3483. root down. This routine performs the push down and logs the changes (either
  3484. directly or by calling routines which log their own changes).
  3485. Arguments:
  3486. Scb - Supplies the Scb for the index.
  3487. IndexContext - Index context describing the position in the root at which
  3488. the insert is to occur.
  3489. Return Value:
  3490. None
  3491. --*/
  3492. {
  3493. ATTRIBUTE_ENUMERATION_CONTEXT AllocationContext;
  3494. ATTRIBUTE_ENUMERATION_CONTEXT BitMapContext;
  3495. LARGE_MCB Mcb;
  3496. PINDEX_ALLOCATION_BUFFER IndexBuffer;
  3497. PINDEX_HEADER IndexHeaderR, IndexHeaderA;
  3498. PINDEX_LOOKUP_STACK Sp;
  3499. ULONG SizeToMove;
  3500. USHORT AttributeFlags;
  3501. LONGLONG EndOfValidData;
  3502. struct {
  3503. INDEX_ROOT IndexRoot;
  3504. INDEX_ENTRY IndexEntry;
  3505. LONGLONG IndexBlock;
  3506. } R;
  3507. PAGED_CODE();
  3508. DebugTrace( +1, Dbg, ("PushIndexRoot\n") );
  3509. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  3510. DebugTrace( 0, Dbg, ("IndexContext = %08lx\n", IndexContext) );
  3511. //
  3512. // Initialize everything (only Mcb can fail), then set up to cleanup
  3513. // on the way out.
  3514. //
  3515. RtlZeroMemory( &R, sizeof(R) );
  3516. FsRtlInitializeLargeMcb( &Mcb, NonPagedPool );
  3517. NtfsInitializeAttributeContext( &AllocationContext );
  3518. NtfsInitializeAttributeContext( &BitMapContext );
  3519. //
  3520. // Allocate a buffer to save away the current index root, as we will
  3521. // have to start by deleting it.
  3522. //
  3523. SizeToMove = IndexContext->Base->IndexHeader->FirstFreeByte;
  3524. IndexHeaderR = NtfsAllocatePool(PagedPool, SizeToMove );
  3525. try {
  3526. //
  3527. // Save away the current index root, then delete it. This should
  3528. // insure that we have enough space to create/extend the index allocation
  3529. // and bitmap attributes.
  3530. //
  3531. AttributeFlags = NtfsFoundAttribute(&IndexContext->AttributeContext)->Flags;
  3532. RtlMoveMemory( IndexHeaderR,
  3533. IndexContext->Base->IndexHeader,
  3534. SizeToMove );
  3535. NtfsDeleteAttributeRecord( IrpContext,
  3536. Scb->Fcb,
  3537. DELETE_LOG_OPERATION |
  3538. DELETE_RELEASE_FILE_RECORD |
  3539. DELETE_RELEASE_ALLOCATION,
  3540. &IndexContext->AttributeContext );
  3541. //
  3542. // If the IndexAllocation isn't there, then we have to create both
  3543. // the Index Allocation and Bitmap attributes.
  3544. //
  3545. if (!NtfsLookupAttributeByName( IrpContext,
  3546. Scb->Fcb,
  3547. &Scb->Fcb->FileReference,
  3548. $INDEX_ALLOCATION,
  3549. &Scb->AttributeName,
  3550. NULL,
  3551. FALSE,
  3552. &AllocationContext )) {
  3553. //
  3554. // Allocate the Index Allocation attribute. Always allocate at
  3555. // least one cluster.
  3556. //
  3557. EndOfValidData = Scb->ScbType.Index.BytesPerIndexBuffer;
  3558. if ((ULONG) EndOfValidData < Scb->Vcb->BytesPerCluster) {
  3559. EndOfValidData = Scb->Vcb->BytesPerCluster;
  3560. }
  3561. NtfsAllocateAttribute( IrpContext,
  3562. Scb,
  3563. $INDEX_ALLOCATION,
  3564. &Scb->AttributeName,
  3565. 0,
  3566. TRUE,
  3567. TRUE,
  3568. EndOfValidData,
  3569. NULL );
  3570. Scb->Header.AllocationSize.QuadPart = EndOfValidData;
  3571. SetFlag( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED );
  3572. //
  3573. // Now create the BitMap attribute.
  3574. //
  3575. NtfsCreateAttributeWithValue( IrpContext,
  3576. Scb->Fcb,
  3577. $BITMAP,
  3578. &Scb->AttributeName,
  3579. &Li0,
  3580. sizeof(LARGE_INTEGER),
  3581. 0,
  3582. NULL,
  3583. TRUE,
  3584. &BitMapContext );
  3585. }
  3586. //
  3587. // Take some pains here to preserve the IndexContext for the case that
  3588. // we are called from AddToIndex, when it is called from DeleteFromIndex,
  3589. // because we still need some of the stack then. The caller must have
  3590. // insured the stack is big enough for him. Move all but two entries,
  3591. // because we do not need to move the root, and we cannot move the last
  3592. // entry since it would go off the end of the structure!
  3593. //
  3594. ASSERT(IndexContext->NumberEntries > 2);
  3595. //
  3596. // Do an unpin on the entry that will be overwritten.
  3597. //
  3598. NtfsUnpinBcb( IrpContext, &IndexContext->Base[IndexContext->NumberEntries - 1].Bcb );
  3599. RtlMoveMemory( IndexContext->Base + 2,
  3600. IndexContext->Base + 1,
  3601. (IndexContext->NumberEntries - 2) * sizeof(INDEX_LOOKUP_STACK) );
  3602. //
  3603. // Now point our local pointer to where the root will be pushed to, and
  3604. // clear the Bcb pointer in the stack there, since it was copied above.
  3605. // Advance top and current because of the move.
  3606. //
  3607. Sp = IndexContext->Base + 1;
  3608. Sp->Bcb = NULL;
  3609. IndexContext->Top += 1;
  3610. IndexContext->Current += 1;
  3611. //
  3612. // Allocate a buffer to hold the pushed down entries.
  3613. //
  3614. IndexBuffer = GetIndexBuffer( IrpContext, Scb, Sp, &EndOfValidData );
  3615. //
  3616. // Point now to the new index header.
  3617. //
  3618. IndexHeaderA = Sp->IndexHeader;
  3619. //
  3620. // Now do the push down and fix up the IndexEntry pointer for
  3621. // the new buffer.
  3622. //
  3623. SizeToMove = IndexHeaderR->FirstFreeByte - IndexHeaderR->FirstIndexEntry;
  3624. RtlMoveMemory( NtfsFirstIndexEntry(IndexHeaderA),
  3625. NtfsFirstIndexEntry(IndexHeaderR),
  3626. SizeToMove );
  3627. Sp->IndexEntry = (PINDEX_ENTRY)((PCHAR)(Sp-1)->IndexEntry +
  3628. ((PCHAR)IndexHeaderA - (PCHAR)((Sp-1)->IndexHeader)) +
  3629. (IndexHeaderA->FirstIndexEntry -
  3630. IndexHeaderR->FirstIndexEntry));
  3631. IndexHeaderA->FirstFreeByte += SizeToMove;
  3632. IndexHeaderA->Flags = IndexHeaderR->Flags;
  3633. //
  3634. // Finally, log the pushed down buffer.
  3635. //
  3636. CheckBuffer(IndexBuffer);
  3637. IndexBuffer->Lsn =
  3638. NtfsWriteLog( IrpContext,
  3639. Scb,
  3640. Sp->Bcb,
  3641. UpdateNonresidentValue,
  3642. IndexBuffer,
  3643. FIELD_OFFSET( INDEX_ALLOCATION_BUFFER,IndexHeader ) +
  3644. IndexHeaderA->FirstFreeByte,
  3645. Noop,
  3646. NULL,
  3647. 0,
  3648. LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  3649. 0,
  3650. 0,
  3651. Scb->ScbType.Index.BytesPerIndexBuffer );
  3652. //
  3653. // Remember if we extended the valid data for this Scb.
  3654. //
  3655. if (EndOfValidData > Scb->Header.ValidDataLength.QuadPart) {
  3656. Scb->Header.ValidDataLength.QuadPart = EndOfValidData;
  3657. NtfsWriteFileSizes( IrpContext,
  3658. Scb,
  3659. &Scb->Header.ValidDataLength.QuadPart,
  3660. TRUE,
  3661. TRUE,
  3662. TRUE );
  3663. }
  3664. //
  3665. // Now initialize an image of the new root.
  3666. //
  3667. if (!FlagOn(Scb->ScbState, SCB_STATE_VIEW_INDEX)) {
  3668. R.IndexRoot.IndexedAttributeType = Scb->ScbType.Index.AttributeBeingIndexed;
  3669. } else {
  3670. R.IndexRoot.IndexedAttributeType = $UNUSED;
  3671. }
  3672. R.IndexRoot.CollationRule = (COLLATION_RULE)Scb->ScbType.Index.CollationRule;
  3673. R.IndexRoot.BytesPerIndexBuffer = Scb->ScbType.Index.BytesPerIndexBuffer;
  3674. R.IndexRoot.BlocksPerIndexBuffer = Scb->ScbType.Index.BlocksPerIndexBuffer;
  3675. R.IndexRoot.IndexHeader.FirstIndexEntry = (ULONG)((PCHAR)&R.IndexEntry -
  3676. (PCHAR)&R.IndexRoot.IndexHeader);
  3677. R.IndexRoot.IndexHeader.FirstFreeByte =
  3678. R.IndexRoot.IndexHeader.BytesAvailable = QuadAlign(sizeof(INDEX_HEADER)) +
  3679. QuadAlign(sizeof(INDEX_ENTRY)) +
  3680. sizeof(LONGLONG);
  3681. SetFlag( R.IndexRoot.IndexHeader.Flags, INDEX_NODE );
  3682. R.IndexEntry.Length = sizeof(INDEX_ENTRY) + sizeof(LONGLONG);
  3683. R.IndexEntry.Flags = INDEX_ENTRY_NODE | INDEX_ENTRY_END;
  3684. R.IndexBlock = IndexBuffer->ThisBlock;
  3685. //
  3686. // Now recreate the index root.
  3687. //
  3688. NtfsCleanupAttributeContext( IrpContext, &IndexContext->AttributeContext );
  3689. NtfsCreateAttributeWithValue( IrpContext,
  3690. Scb->Fcb,
  3691. $INDEX_ROOT,
  3692. &Scb->AttributeName,
  3693. (PVOID)&R,
  3694. sizeof(R),
  3695. AttributeFlags,
  3696. NULL,
  3697. TRUE,
  3698. &IndexContext->AttributeContext );
  3699. //
  3700. // We just pushed the index root, so let's find it again and
  3701. // fix up the caller's context. Note that he will try to
  3702. // recalculate the IndexEntry pointer, but we know that it
  3703. // has to change to point to the single entry in the new root.
  3704. //
  3705. FindMoveableIndexRoot( IrpContext, Scb, IndexContext );
  3706. (Sp-1)->IndexEntry = NtfsFirstIndexEntry((Sp-1)->IndexHeader);
  3707. } finally {
  3708. DebugUnwind( PushIndexRoot );
  3709. NtfsFreePool( IndexHeaderR );
  3710. FsRtlUninitializeLargeMcb( &Mcb );
  3711. NtfsCleanupAttributeContext( IrpContext, &AllocationContext );
  3712. NtfsCleanupAttributeContext( IrpContext, &BitMapContext );
  3713. }
  3714. DebugTrace( -1, Dbg, ("PushIndexRoot -> VOID\n") );
  3715. }
  3716. VOID
  3717. InsertSimpleAllocation (
  3718. IN PIRP_CONTEXT IrpContext,
  3719. IN PSCB Scb,
  3720. IN PINDEX_ENTRY InsertIndexEntry,
  3721. IN PINDEX_LOOKUP_STACK Sp,
  3722. OUT PQUICK_INDEX QuickIndex OPTIONAL
  3723. )
  3724. /*++
  3725. Routine Description:
  3726. This routine does a simple insert in an index buffer in the index
  3727. allocation. It calls a routine common with restart to do the insert,
  3728. and then it logs the change.
  3729. Arguments:
  3730. Scb - Supplies the Scb for the index.
  3731. InsertIndexEntry - Address of the index entry to be inserted.
  3732. Sp - Pointer to the lookup stack location describing the insertion
  3733. point.
  3734. QuickIndex - If specified we store the location of the index added.
  3735. Return Value:
  3736. None
  3737. --*/
  3738. {
  3739. PINDEX_ALLOCATION_BUFFER IndexBuffer;
  3740. PINDEX_ENTRY BeforeIndexEntry;
  3741. BOOLEAN Inserted = FALSE;
  3742. PAGED_CODE();
  3743. DebugTrace( +1, Dbg, ("InsertSimpleAllocation\n") );
  3744. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  3745. DebugTrace( 0, Dbg, ("InsertIndexEntry = %08lx\n", InsertIndexEntry) );
  3746. DebugTrace( 0, Dbg, ("Sp = %08lx\n", Sp) );
  3747. try {
  3748. //
  3749. // Extract all of the updates required by the restart routine we
  3750. // will call.
  3751. //
  3752. IndexBuffer = (PINDEX_ALLOCATION_BUFFER)Sp->StartOfBuffer;
  3753. BeforeIndexEntry = Sp->IndexEntry;
  3754. //
  3755. // Pin the page
  3756. //
  3757. NtfsPinMappedData( IrpContext,
  3758. Scb,
  3759. LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  3760. Scb->ScbType.Index.BytesPerIndexBuffer,
  3761. &Sp->Bcb );
  3762. //
  3763. // Call the same routine used by restart to actually apply the
  3764. // update.
  3765. //
  3766. NtfsRestartInsertSimpleAllocation( InsertIndexEntry,
  3767. IndexBuffer,
  3768. BeforeIndexEntry );
  3769. Inserted = TRUE;
  3770. CheckBuffer(IndexBuffer);
  3771. //
  3772. // Now that the Index Entry is guaranteed to be in place, log
  3773. // this update. Note that the new record is now at the address
  3774. // we calculated in BeforeIndexEntry.
  3775. //
  3776. IndexBuffer->Lsn =
  3777. NtfsWriteLog( IrpContext,
  3778. Scb,
  3779. Sp->Bcb,
  3780. AddIndexEntryAllocation,
  3781. BeforeIndexEntry,
  3782. BeforeIndexEntry->Length,
  3783. DeleteIndexEntryAllocation,
  3784. NULL,
  3785. 0,
  3786. LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  3787. 0,
  3788. (ULONG)((PCHAR)BeforeIndexEntry - (PCHAR)IndexBuffer),
  3789. Scb->ScbType.Index.BytesPerIndexBuffer );
  3790. //
  3791. // Update the quick index buffer if we have it.
  3792. //
  3793. if (ARGUMENT_PRESENT( QuickIndex )) {
  3794. QuickIndex->ChangeCount = Scb->ScbType.Index.ChangeCount;
  3795. QuickIndex->BufferOffset = PtrOffset( Sp->StartOfBuffer, Sp->IndexEntry );
  3796. QuickIndex->CapturedLsn = ((PINDEX_ALLOCATION_BUFFER) Sp->StartOfBuffer)->Lsn;
  3797. QuickIndex->IndexBlock = ((PINDEX_ALLOCATION_BUFFER) Sp->StartOfBuffer)->ThisBlock;
  3798. }
  3799. } finally {
  3800. DebugUnwind( InsertSimpleAllocation );
  3801. //
  3802. // If we failed and already inserted the item,
  3803. // it must be because we failed to write the log record. If that happened,
  3804. // then the record will not be deleted by the transaction abort,
  3805. // so we have to do it here by hand.
  3806. //
  3807. if (AbnormalTermination() && Inserted) {
  3808. NtfsRestartDeleteSimpleAllocation( BeforeIndexEntry,
  3809. IndexBuffer );
  3810. }
  3811. }
  3812. DebugTrace( -1, Dbg, ("InsertSimpleAllocation -> VOID\n") );
  3813. }
  3814. VOID
  3815. NtfsRestartInsertSimpleAllocation (
  3816. IN PINDEX_ENTRY InsertIndexEntry,
  3817. IN PINDEX_ALLOCATION_BUFFER IndexBuffer,
  3818. IN PINDEX_ENTRY BeforeIndexEntry
  3819. )
  3820. /*++
  3821. Routine Description:
  3822. This routine does a simple insert in an index buffer in the index
  3823. allocation. It performs this work either in the running system, or
  3824. when called by restart. It does no logging.
  3825. Arguments:
  3826. InsertIndexEntry - Address of the index entry to be inserted.
  3827. IndexBuffer - Pointer to the index buffer into which the insert is to
  3828. occur.
  3829. BeforeIndexEntry - Pointer to the index entry currently residing at
  3830. the insertion point.
  3831. Return Value:
  3832. None
  3833. --*/
  3834. {
  3835. PINDEX_HEADER IndexHeader;
  3836. PAGED_CODE();
  3837. DebugTrace( +1, Dbg, ("NtfsRestartInsertSimpleAllocation\n") );
  3838. DebugTrace( 0, Dbg, ("InsertIndexEntry = %08lx\n", InsertIndexEntry) );
  3839. DebugTrace( 0, Dbg, ("IndexBuffer = %08lx\n", IndexBuffer) );
  3840. DebugTrace( 0, Dbg, ("BeforeIndexEntry = %08lx\n", BeforeIndexEntry) );
  3841. //
  3842. // Form some pointers within the attribute value.
  3843. //
  3844. IndexHeader = &IndexBuffer->IndexHeader;
  3845. //
  3846. // Now move the tail end of the index to make room for the new entry.
  3847. //
  3848. RtlMoveMemory( (PCHAR)BeforeIndexEntry + InsertIndexEntry->Length,
  3849. BeforeIndexEntry,
  3850. ((PCHAR)IndexHeader + IndexHeader->FirstFreeByte) -
  3851. (PCHAR)BeforeIndexEntry );
  3852. //
  3853. // Move the new Index Entry into place. The index entry may either
  3854. // be a complete index entry, or it may be in pointer form.
  3855. //
  3856. if (FlagOn(InsertIndexEntry->Flags, INDEX_ENTRY_POINTER_FORM)) {
  3857. RtlMoveMemory( BeforeIndexEntry, InsertIndexEntry, sizeof(INDEX_ENTRY) );
  3858. RtlMoveMemory( (PVOID)(BeforeIndexEntry + 1),
  3859. *(PVOID *)(InsertIndexEntry + 1),
  3860. InsertIndexEntry->AttributeLength );
  3861. //
  3862. // In pointer form the Data Pointer follows the key pointer, but there is
  3863. // none for normal directory indices.
  3864. //
  3865. if (*(PVOID *)((PCHAR)InsertIndexEntry + sizeof(INDEX_ENTRY) + sizeof(PVOID)) != NULL) {
  3866. RtlMoveMemory( (PVOID)((PCHAR)BeforeIndexEntry + InsertIndexEntry->DataOffset),
  3867. *(PVOID *)((PCHAR)InsertIndexEntry + sizeof(INDEX_ENTRY) + sizeof(PVOID)),
  3868. InsertIndexEntry->DataLength );
  3869. }
  3870. ClearFlag( BeforeIndexEntry->Flags, INDEX_ENTRY_POINTER_FORM );
  3871. } else {
  3872. RtlMoveMemory( BeforeIndexEntry, InsertIndexEntry, InsertIndexEntry->Length );
  3873. }
  3874. //
  3875. // Update the index header by the space we grew by.
  3876. //
  3877. IndexHeader->FirstFreeByte += InsertIndexEntry->Length;
  3878. DebugTrace( -1, Dbg, ("NtfsRestartInsertSimpleAllocation -> VOID\n") );
  3879. }
  3880. PINDEX_ENTRY
  3881. InsertWithBufferSplit (
  3882. IN PIRP_CONTEXT IrpContext,
  3883. IN PSCB Scb,
  3884. IN PINDEX_ENTRY InsertIndexEntry,
  3885. IN OUT PINDEX_CONTEXT IndexContext,
  3886. OUT PQUICK_INDEX QuickIndex OPTIONAL
  3887. )
  3888. /*++
  3889. Routine Description:
  3890. This routine is called to perform an insert in the index allocation, when
  3891. it is known that a buffer split is necessary. It splits the buffer in
  3892. half, inserts the new entry in the appropriate half, fixes the Vcn pointer
  3893. in the current parent, and returns a pointer to a new entry which is being
  3894. promoted to insert at the next level up.
  3895. Arguments:
  3896. Scb - Supplies the Scb for the index.
  3897. InsertIndexEntry - Address of the index entry to be inserted.
  3898. IndexContext - Index context describing the position in the stack at which
  3899. the insert with split is to occur.
  3900. QuickIndex - If specified we store the location of the index added.
  3901. Return Value:
  3902. Pointer to the index entry which must now be inserted at the next level.
  3903. --*/
  3904. {
  3905. PINDEX_ALLOCATION_BUFFER IndexBuffer, IndexBuffer2;
  3906. PINDEX_HEADER IndexHeader, IndexHeader2;
  3907. PINDEX_ENTRY BeforeIndexEntry, MiddleIndexEntry, MovingIndexEntry;
  3908. PINDEX_ENTRY ReturnIndexEntry = NULL;
  3909. INDEX_LOOKUP_STACK Stack2;
  3910. PINDEX_LOOKUP_STACK Sp;
  3911. ULONG LengthToMove;
  3912. ULONG Buffer2Length;
  3913. LONGLONG EndOfValidData;
  3914. struct {
  3915. INDEX_ENTRY IndexEntry;
  3916. LONGLONG IndexBlock;
  3917. } NewEnd;
  3918. PVCB Vcb;
  3919. Vcb = Scb->Vcb;
  3920. PAGED_CODE();
  3921. DebugTrace( +1, Dbg, ("InsertWithBufferSplit\n") );
  3922. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  3923. DebugTrace( 0, Dbg, ("InsertIndexEntry = %08lx\n", InsertIndexEntry) );
  3924. DebugTrace( 0, Dbg, ("IndexContext = %08lx\n", IndexContext) );
  3925. Stack2.Bcb = NULL;
  3926. Sp = IndexContext->Current;
  3927. try {
  3928. //
  3929. // Extract all of the updates required by the restart routine we
  3930. // will call.
  3931. //
  3932. IndexBuffer = (PINDEX_ALLOCATION_BUFFER)Sp->StartOfBuffer;
  3933. IndexHeader = &IndexBuffer->IndexHeader;
  3934. BeforeIndexEntry = Sp->IndexEntry;
  3935. //
  3936. // Pin the page
  3937. //
  3938. NtfsPinMappedData( IrpContext,
  3939. Scb,
  3940. LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  3941. Scb->ScbType.Index.BytesPerIndexBuffer,
  3942. &Sp->Bcb );
  3943. //
  3944. // Allocate an index buffer to take the second half of the splitting
  3945. // one.
  3946. //
  3947. IndexBuffer2 = GetIndexBuffer( IrpContext,
  3948. Scb,
  3949. &Stack2,
  3950. &EndOfValidData );
  3951. IndexHeader2 = &IndexBuffer2->IndexHeader;
  3952. //
  3953. // Scan to find the middle index entry that we will promote to
  3954. // the next level up, and the next one after him.
  3955. //
  3956. MiddleIndexEntry = NtfsFirstIndexEntry(IndexHeader);
  3957. NtfsCheckIndexBound( MiddleIndexEntry, IndexHeader );
  3958. if (MiddleIndexEntry->Length == 0) {
  3959. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  3960. }
  3961. while (((ULONG)((PCHAR)MiddleIndexEntry - (PCHAR)IndexHeader) +
  3962. (ULONG)MiddleIndexEntry->Length) < IndexHeader->BytesAvailable / 2) {
  3963. MovingIndexEntry = MiddleIndexEntry;
  3964. MiddleIndexEntry = NtfsNextIndexEntry(MiddleIndexEntry);
  3965. NtfsCheckIndexBound( MiddleIndexEntry, IndexHeader );
  3966. if (MiddleIndexEntry->Length == 0) {
  3967. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  3968. }
  3969. }
  3970. //
  3971. // We found an entry to elevate but if the next entry is the end
  3972. // record we want to go back one entry.
  3973. //
  3974. if (FlagOn( NtfsNextIndexEntry(MiddleIndexEntry)->Flags, INDEX_ENTRY_END )) {
  3975. MiddleIndexEntry = MovingIndexEntry;
  3976. }
  3977. MovingIndexEntry = NtfsNextIndexEntry(MiddleIndexEntry);
  3978. NtfsCheckIndexBound( MovingIndexEntry, IndexHeader );
  3979. if (MovingIndexEntry->Length == 0) {
  3980. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  3981. }
  3982. //
  3983. // Allocate space to hold this middle entry, and copy it out.
  3984. //
  3985. ReturnIndexEntry = NtfsAllocatePool( NonPagedPool,
  3986. MiddleIndexEntry->Length +
  3987. sizeof(LONGLONG) );
  3988. RtlMoveMemory( ReturnIndexEntry,
  3989. MiddleIndexEntry,
  3990. MiddleIndexEntry->Length );
  3991. if (!FlagOn(ReturnIndexEntry->Flags, INDEX_ENTRY_NODE)) {
  3992. SetFlag( ReturnIndexEntry->Flags, INDEX_ENTRY_NODE );
  3993. ReturnIndexEntry->Length += sizeof(LONGLONG);
  3994. }
  3995. //
  3996. // Now move the second half of the splitting buffer over to the
  3997. // new one, and fix it up.
  3998. //
  3999. LengthToMove = IndexHeader->FirstFreeByte - (ULONG)((PCHAR)MovingIndexEntry -
  4000. (PCHAR)IndexHeader);
  4001. RtlMoveMemory( NtfsFirstIndexEntry(IndexHeader2),
  4002. MovingIndexEntry,
  4003. LengthToMove );
  4004. IndexHeader2->FirstFreeByte += LengthToMove;
  4005. IndexHeader2->Flags = IndexHeader->Flags;
  4006. //
  4007. // Now the new Index Buffer is done, so lets log its contents.
  4008. //
  4009. Buffer2Length = FIELD_OFFSET( INDEX_ALLOCATION_BUFFER,IndexHeader ) +
  4010. IndexHeader2->FirstFreeByte;
  4011. CheckBuffer(IndexBuffer2);
  4012. IndexBuffer2->Lsn =
  4013. NtfsWriteLog( IrpContext,
  4014. Scb,
  4015. Stack2.Bcb,
  4016. UpdateNonresidentValue,
  4017. IndexBuffer2,
  4018. Buffer2Length,
  4019. Noop,
  4020. NULL,
  4021. 0,
  4022. LlBytesFromIndexBlocks( IndexBuffer2->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  4023. 0,
  4024. 0,
  4025. Scb->ScbType.Index.BytesPerIndexBuffer );
  4026. //
  4027. // Remember if we extended the valid data for this Scb.
  4028. //
  4029. if (EndOfValidData > Scb->Header.ValidDataLength.QuadPart) {
  4030. Scb->Header.ValidDataLength.QuadPart = EndOfValidData;
  4031. NtfsWriteFileSizes( IrpContext,
  4032. Scb,
  4033. &Scb->Header.ValidDataLength.QuadPart,
  4034. TRUE,
  4035. TRUE,
  4036. TRUE );
  4037. }
  4038. //
  4039. // Now let's create the image of the new end record for the
  4040. // splitting index buffer.
  4041. //
  4042. RtlZeroMemory( &NewEnd.IndexEntry, sizeof(INDEX_ENTRY) );
  4043. NewEnd.IndexEntry.Length = sizeof(INDEX_ENTRY);
  4044. NewEnd.IndexEntry.Flags = INDEX_ENTRY_END;
  4045. if (FlagOn(MiddleIndexEntry->Flags, INDEX_ENTRY_NODE)) {
  4046. NewEnd.IndexEntry.Length += sizeof(LONGLONG);
  4047. SetFlag( NewEnd.IndexEntry.Flags, INDEX_ENTRY_NODE );
  4048. NewEnd.IndexBlock = NtfsIndexEntryBlock(MiddleIndexEntry);
  4049. }
  4050. //
  4051. // Write a log record to set the new end of the splitting buffer.
  4052. //
  4053. IndexBuffer->Lsn =
  4054. NtfsWriteLog( IrpContext,
  4055. Scb,
  4056. Sp->Bcb,
  4057. WriteEndOfIndexBuffer,
  4058. &NewEnd,
  4059. NewEnd.IndexEntry.Length,
  4060. WriteEndOfIndexBuffer,
  4061. MiddleIndexEntry,
  4062. MiddleIndexEntry->Length + LengthToMove,
  4063. LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  4064. 0,
  4065. (ULONG)((PCHAR)MiddleIndexEntry - (PCHAR)IndexBuffer),
  4066. Scb->ScbType.Index.BytesPerIndexBuffer );
  4067. //
  4068. // Now call the restart routine to write the new end of the index
  4069. // buffer.
  4070. //
  4071. NtfsRestartWriteEndOfIndex( IndexHeader,
  4072. MiddleIndexEntry,
  4073. (PINDEX_ENTRY)&NewEnd,
  4074. NewEnd.IndexEntry.Length );
  4075. CheckBuffer(IndexBuffer);
  4076. //
  4077. // Now that we are done splitting IndexBuffer and IndexBuffer2, we
  4078. // need to figure out which one our original entry was inserting into,
  4079. // and do the simple insert. Going into the first half is trivial,
  4080. // and follows:
  4081. //
  4082. if (BeforeIndexEntry < MovingIndexEntry) {
  4083. InsertSimpleAllocation( IrpContext, Scb, InsertIndexEntry, Sp, QuickIndex );
  4084. //
  4085. // If it is going into the second half, we just have to fix up the
  4086. // stack descriptor for the buffer we allocated, and do the insert
  4087. // there. To fix it up we just have to do a little arithmetic to
  4088. // find the insert position.
  4089. //
  4090. } else {
  4091. Stack2.IndexEntry = (PINDEX_ENTRY)((PCHAR)BeforeIndexEntry +
  4092. ((PCHAR)NtfsFirstIndexEntry(IndexHeader2) -
  4093. (PCHAR)MovingIndexEntry));
  4094. InsertSimpleAllocation( IrpContext,
  4095. Scb,
  4096. InsertIndexEntry,
  4097. &Stack2,
  4098. QuickIndex );
  4099. }
  4100. //
  4101. // Now we just have to set the correct Vcns in the two index entries
  4102. // that point to IndexBuffer and IndexBuffer2 after the split. The
  4103. // first one is easy; its Vcn goes into the IndexEntry we are
  4104. // returning with to insert in our parent. The second one we have
  4105. // have to fix up is the index entry pointing to the buffer that
  4106. // split, since it must now point to the new buffer. It should look
  4107. // like this:
  4108. //
  4109. // ParentIndexBuffer: ...(ReturnIndexEntry) (ParentIndexEntry)...
  4110. // | |
  4111. // | |
  4112. // V V
  4113. // IndexBuffer IndexBuffer2
  4114. //
  4115. NtfsSetIndexEntryBlock( ReturnIndexEntry, IndexBuffer->ThisBlock );
  4116. //
  4117. // Decrement our stack pointer to point to the stack entry describing
  4118. // our parent.
  4119. //
  4120. Sp -= 1;
  4121. //
  4122. // First handle the case where our parent is the Index Root.
  4123. //
  4124. if (Sp == IndexContext->Base) {
  4125. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  4126. PATTRIBUTE_RECORD_HEADER Attribute;
  4127. PATTRIBUTE_ENUMERATION_CONTEXT Context = &IndexContext->AttributeContext;
  4128. Attribute = FindMoveableIndexRoot( IrpContext, Scb, IndexContext );
  4129. //
  4130. // Pin the page
  4131. //
  4132. NtfsPinMappedAttribute( IrpContext, Vcb, Context );
  4133. //
  4134. // Write a log record to change our ParentIndexEntry.
  4135. //
  4136. FileRecord = NtfsContainingFileRecord(Context);
  4137. FileRecord->Lsn =
  4138. NtfsWriteLog( IrpContext,
  4139. Vcb->MftScb,
  4140. NtfsFoundBcb(Context),
  4141. SetIndexEntryVcnRoot,
  4142. &IndexBuffer2->ThisBlock,
  4143. sizeof(LONGLONG),
  4144. SetIndexEntryVcnRoot,
  4145. &IndexBuffer->ThisBlock,
  4146. sizeof(LONGLONG),
  4147. NtfsMftOffset( Context ),
  4148. (ULONG)((PCHAR)Attribute - (PCHAR)FileRecord),
  4149. (ULONG)((PCHAR)Sp->IndexEntry - (PCHAR)Attribute),
  4150. Vcb->BytesPerFileRecordSegment );
  4151. //
  4152. // Otherwise, our parent is also an Index Buffer.
  4153. //
  4154. } else {
  4155. PINDEX_ALLOCATION_BUFFER ParentIndexBuffer;
  4156. ParentIndexBuffer = (PINDEX_ALLOCATION_BUFFER)Sp->StartOfBuffer;
  4157. //
  4158. // Pin the page
  4159. //
  4160. NtfsPinMappedData( IrpContext,
  4161. Scb,
  4162. LlBytesFromIndexBlocks( ParentIndexBuffer->ThisBlock,
  4163. Scb->ScbType.Index.IndexBlockByteShift ),
  4164. Scb->ScbType.Index.BytesPerIndexBuffer,
  4165. &Sp->Bcb );
  4166. //
  4167. // Write a log record to change our ParentIndexEntry.
  4168. //
  4169. ParentIndexBuffer->Lsn =
  4170. NtfsWriteLog( IrpContext,
  4171. Scb,
  4172. Sp->Bcb,
  4173. SetIndexEntryVcnAllocation,
  4174. &IndexBuffer2->ThisBlock,
  4175. sizeof(LONGLONG),
  4176. SetIndexEntryVcnAllocation,
  4177. &IndexBuffer->ThisBlock,
  4178. sizeof(LONGLONG),
  4179. LlBytesFromIndexBlocks( ParentIndexBuffer->ThisBlock,
  4180. Scb->ScbType.Index.IndexBlockByteShift ),
  4181. 0,
  4182. (ULONG)((PCHAR)Sp->IndexEntry - (PCHAR)ParentIndexBuffer),
  4183. Scb->ScbType.Index.BytesPerIndexBuffer );
  4184. }
  4185. //
  4186. // Now call the Restart routine to do it.
  4187. //
  4188. NtfsRestartSetIndexBlock( Sp->IndexEntry,
  4189. IndexBuffer2->ThisBlock );
  4190. } finally {
  4191. DebugUnwind( InsertWithBufferSplit );
  4192. if (AbnormalTermination( )) {
  4193. if (ReturnIndexEntry != NULL) {
  4194. NtfsFreePool( ReturnIndexEntry );
  4195. }
  4196. }
  4197. NtfsUnpinBcb( IrpContext, &Stack2.Bcb );
  4198. }
  4199. DebugTrace( -1, Dbg, ("InsertWithBufferSplit -> VOID\n") );
  4200. return ReturnIndexEntry;
  4201. }
  4202. VOID
  4203. NtfsRestartWriteEndOfIndex (
  4204. IN PINDEX_HEADER IndexHeader,
  4205. IN PINDEX_ENTRY OverwriteIndexEntry,
  4206. IN PINDEX_ENTRY FirstNewIndexEntry,
  4207. IN ULONG Length
  4208. )
  4209. /*++
  4210. Routine Description:
  4211. This routine is used both in normal operation and at restart to
  4212. update the end of an index buffer, as one of the consequences of
  4213. a buffer split. Since it is called at restart, it does no logging.
  4214. Arguments:
  4215. IndexHeader - Supplies a pointer to the IndexHeader of the buffer
  4216. whose end is being rewritten.
  4217. OverWriteIndexEntry - Points to the index entry in the buffer at which
  4218. the overwrite of the end is to occur.
  4219. FirstNewIndexEntry - Points to the first entry in the buffer which is
  4220. to overwrite the end of the current buffer.
  4221. Length - Supplies the length of the index entries being written to the
  4222. end of the buffer, in bytes.
  4223. Return Value:
  4224. None
  4225. --*/
  4226. {
  4227. PAGED_CODE();
  4228. DebugTrace( +1, Dbg, ("NtfsRestartWriteEndOfIndex\n") );
  4229. DebugTrace( 0, Dbg, ("IndexHeader = %08lx\n", IndexHeader) );
  4230. DebugTrace( 0, Dbg, ("OverwriteIndexEntry = %08lx\n", OverwriteIndexEntry) );
  4231. DebugTrace( 0, Dbg, ("FirstNewIndexEntry = %08lx\n", FirstNewIndexEntry) );
  4232. DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) );
  4233. IndexHeader->FirstFreeByte = (ULONG)((PCHAR)OverwriteIndexEntry - (PCHAR)IndexHeader) +
  4234. Length;
  4235. RtlMoveMemory( OverwriteIndexEntry, FirstNewIndexEntry, Length );
  4236. DebugTrace( -1, Dbg, ("NtfsRestartWriteEndOfIndex -> VOID\n") );
  4237. }
  4238. VOID
  4239. NtfsRestartSetIndexBlock(
  4240. IN PINDEX_ENTRY IndexEntry,
  4241. IN LONGLONG IndexBlock
  4242. )
  4243. /*++
  4244. Routine Description:
  4245. This routine updates the IndexBlock in an index entry, for both normal operation and
  4246. restart. Therefore it does no logging.
  4247. Arguments:
  4248. IndexEntry - Supplies a pointer to the index entry whose Vcn is to be overwritten.
  4249. IndexBlock - The index block which is to be written to the index entry.
  4250. Return Value:
  4251. None
  4252. --*/
  4253. {
  4254. PAGED_CODE();
  4255. DebugTrace( +1, Dbg, ("NtfsRestartSetIndexBlock\n") );
  4256. DebugTrace( 0, Dbg, ("IndexEntry = %08lx\n", IndexEntry) );
  4257. DebugTrace( 0, Dbg, ("IndexBlock = %016I64x\n", IndexBlock) );
  4258. NtfsSetIndexEntryBlock( IndexEntry, IndexBlock );
  4259. DebugTrace( -1, Dbg, ("NtfsRestartSetIndexEntryBlock -> VOID\n") );
  4260. }
  4261. VOID
  4262. NtfsRestartUpdateFileName(
  4263. IN PINDEX_ENTRY IndexEntry,
  4264. IN PDUPLICATED_INFORMATION Info
  4265. )
  4266. /*++
  4267. Routine Description:
  4268. This routine updates the duplicated information in a file name index entry,
  4269. for both normal operation and restart. Therefore it does no logging.
  4270. Arguments:
  4271. IndexEntry - Supplies a pointer to the index entry whose Vcn is to be overwritten.
  4272. Info - Pointer to the duplicated information for the update.
  4273. Return Value:
  4274. None
  4275. --*/
  4276. {
  4277. PAGED_CODE();
  4278. DebugTrace( +1, Dbg, ("NtfsRestartUpdateFileName\n") );
  4279. DebugTrace( 0, Dbg, ("IndexEntry = %08lx\n", IndexEntry) );
  4280. DebugTrace( 0, Dbg, ("Info = %08lx\n", Info) );
  4281. RtlMoveMemory( &((PFILE_NAME)(IndexEntry + 1))->Info,
  4282. Info,
  4283. sizeof(DUPLICATED_INFORMATION) );
  4284. DebugTrace( -1, Dbg, ("NtfsRestartUpdateFileName -> VOID\n") );
  4285. }
  4286. VOID
  4287. DeleteFromIndex (
  4288. IN PIRP_CONTEXT IrpContext,
  4289. IN PSCB Scb,
  4290. IN OUT PINDEX_CONTEXT IndexContext
  4291. )
  4292. /*++
  4293. Routine Description:
  4294. This routine deletes an entry from an index, deleting any empty index buffers
  4295. as required.
  4296. The steps are as follows:
  4297. 1. If the entry to be deleted is not a leaf, then find a leaf entry to
  4298. delete which can be used to replace the entry we want to delete.
  4299. 2. Delete the desired index entry, or the replacement we found. If we
  4300. delete a replacement, remember it for reinsertion later, in step 4 or
  4301. 6 below.
  4302. 3. Now prune empty buffers from the tree, if any, starting with the buffer
  4303. we just deleted an entry from.
  4304. 4. If the original target was an intermediate entry, then delete it now,
  4305. and replace it with the entry we deleted in its place in 2 above. As
  4306. a special case, if all of the descendent buffers of the target went away,
  4307. then we do not have to replace it, so we hang onto the replacement for
  4308. reinsertion.
  4309. 5. When we pruned the index back, we may have stopped on an entry which is
  4310. now childless. If this is the case, then we have to delete this childless
  4311. entry and reinsert it in the next step. (If this is the case, then we
  4312. are guaranteed to not still have another entry to reinsert from 2 above!)
  4313. 6. Finally, at this point we may have an entry that needs to be reinserted
  4314. from either step 2 or 5 above. If so, we do this reinsert now, and we
  4315. are done.
  4316. Arguments:
  4317. Scb - Supplies the Scb for the index.
  4318. IndexContext - Describes the entry to delete, including the entire path through
  4319. it from root to leaf.
  4320. Return Value:
  4321. None
  4322. --*/
  4323. {
  4324. //
  4325. // It is possible that one or two Index Entries will have to be reinserted.
  4326. // However, we need to remember at most one at once.
  4327. //
  4328. PINDEX_ENTRY ReinsertEntry = NULL;
  4329. //
  4330. // A pointer to keep track of where we are in the Index Lookup Stack.
  4331. //
  4332. PINDEX_LOOKUP_STACK Sp = IndexContext->Current;
  4333. //
  4334. // Some Index entry pointers to remember the next entry to delete, and
  4335. // the original target if it is an intermediate node.
  4336. //
  4337. PINDEX_ENTRY TargetEntry = NULL;
  4338. PINDEX_ENTRY DeleteEntry;
  4339. //
  4340. // Two other Lookup Stack pointers we may have to remember.
  4341. //
  4342. PINDEX_LOOKUP_STACK TargetSp;
  4343. PINDEX_LOOKUP_STACK DeleteSp;
  4344. PAGED_CODE();
  4345. DebugTrace( +1, Dbg, ("DeleteFromIndex\n") );
  4346. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  4347. DebugTrace( 0, Dbg, ("IndexContext = %08lx\n", IndexContext) );
  4348. //
  4349. // Use try-finally to free pool on the way out.
  4350. //
  4351. try {
  4352. //
  4353. // Step 1.
  4354. //
  4355. // If we are supposed to delete an entry in an intermediate buffer,
  4356. // then we have to find in index entry lower in the tree to replace
  4357. // him. (In fact we will delete the lower entry first, and get around
  4358. // to deleting the one we really want to delete later after possibly
  4359. // pruning the tree back.) For right now just find the replacement
  4360. // to delete first, and save him away.
  4361. //
  4362. DeleteEntry = Sp->IndexEntry;
  4363. if (FlagOn(DeleteEntry->Flags, INDEX_ENTRY_NODE)) {
  4364. PINDEX_ALLOCATION_BUFFER IndexBuffer;
  4365. PINDEX_HEADER IndexHeader;
  4366. PINDEX_ENTRY NextEntry;
  4367. //
  4368. // Remember the real target we need to delete.
  4369. //
  4370. TargetEntry = DeleteEntry;
  4371. TargetSp = Sp;
  4372. //
  4373. // Go to the top of the stack (bottom of the index) and find the
  4374. // largest index entry in that buffer to be our replacement.
  4375. //
  4376. Sp =
  4377. IndexContext->Current = IndexContext->Top;
  4378. IndexBuffer = (PINDEX_ALLOCATION_BUFFER)Sp->StartOfBuffer;
  4379. IndexHeader = &IndexBuffer->IndexHeader;
  4380. NextEntry = NtfsFirstIndexEntry(IndexHeader);
  4381. NtfsCheckIndexBound( NextEntry, IndexHeader );
  4382. do {
  4383. DeleteEntry = NextEntry;
  4384. NextEntry = NtfsNextIndexEntry(NextEntry);
  4385. NtfsCheckIndexBound( NextEntry, IndexHeader );
  4386. if (NextEntry->Length == 0) {
  4387. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  4388. }
  4389. } while (!FlagOn(NextEntry->Flags, INDEX_ENTRY_END));
  4390. //
  4391. // Now we have to save this guy away because we will have to
  4392. // reinsert him later.
  4393. //
  4394. ReinsertEntry = (PINDEX_ENTRY)NtfsAllocatePool( NonPagedPool,
  4395. DeleteEntry->Length +
  4396. sizeof(LONGLONG) );
  4397. RtlMoveMemory( ReinsertEntry, DeleteEntry, DeleteEntry->Length );
  4398. }
  4399. //
  4400. // Step 2.
  4401. //
  4402. // Now it's time to delete either our target or replacement entry at
  4403. // DeleteEntry.
  4404. //
  4405. DeleteSimple( IrpContext, Scb, DeleteEntry, IndexContext );
  4406. DeleteEntry = NULL;
  4407. //
  4408. // Step 3.
  4409. //
  4410. // Now we need to see if the tree has to be "pruned" back some to
  4411. // eliminate any empty buffers. In the extreme case this routine
  4412. // returns the root to the state of being an empty directory. This
  4413. // routine returns a pointer to DeleteEntry if it leaves an entry
  4414. // in the tree somewhere which is pointing to a deleted buffer, and
  4415. // has to be reinserted elsewhere. We only have to prune if we are
  4416. // not currently in the root anyway.
  4417. //
  4418. // Remember the DeleteSp, which is where PruneIndex left the stack
  4419. // pointer.
  4420. //
  4421. if (Sp != IndexContext->Base) {
  4422. PruneIndex( IrpContext, Scb, IndexContext, &DeleteEntry );
  4423. DeleteSp = IndexContext->Current;
  4424. }
  4425. //
  4426. // Step 4.
  4427. //
  4428. // Now we have deleted someone, and possibly pruned the tree back.
  4429. // It is time to see if our original target has not yet been deleted
  4430. // yet and deal with that. First we just delete it, then we see if
  4431. // we really need to replace it. If the whole tree under it went
  4432. // empty (TargetEntry == DeleteEntry), then we do not have to replace
  4433. // it and we will reinsert its replacement below. Otherwise, do the
  4434. // replace now.
  4435. //
  4436. if (TargetEntry != NULL) {
  4437. LONGLONG SavedBlock;
  4438. //
  4439. // Reload in case root moved
  4440. //
  4441. if (TargetSp == IndexContext->Base) {
  4442. TargetEntry = TargetSp->IndexEntry;
  4443. }
  4444. //
  4445. // Save the Vcn in case we need it for the reinsert.
  4446. //
  4447. SavedBlock = NtfsIndexEntryBlock(TargetEntry);
  4448. //
  4449. // Delete it.
  4450. //
  4451. IndexContext->Current = TargetSp;
  4452. DeleteSimple( IrpContext, Scb, TargetEntry, IndexContext );
  4453. //
  4454. // See if this is exactly the same guy who went childless anyway
  4455. // when we pruned the tree. If not replace him now.
  4456. //
  4457. if (TargetEntry != DeleteEntry) {
  4458. //
  4459. // We know the replacement entry was a leaf, so give him the
  4460. // block number now.
  4461. //
  4462. SetFlag( ReinsertEntry->Flags, INDEX_ENTRY_NODE );
  4463. ReinsertEntry->Length += sizeof(LONGLONG);
  4464. NtfsSetIndexEntryBlock( ReinsertEntry, SavedBlock );
  4465. //
  4466. // Now we are all set up to just call our local routine to
  4467. // go insert our replacement. If the stack gets pushed,
  4468. // we have to increment our DeleteSp.
  4469. //
  4470. if (AddToIndex( IrpContext, Scb, ReinsertEntry, IndexContext, NULL, TRUE )) {
  4471. DeleteSp += 1;
  4472. }
  4473. //
  4474. // We may need to save someone else away below, but it could
  4475. // be a different size anyway, so let's just delete the
  4476. // current ReinsertEntry now.
  4477. //
  4478. NtfsFreePool( ReinsertEntry );
  4479. ReinsertEntry = NULL;
  4480. //
  4481. // Otherwise, we just deleted the same index entry who went
  4482. // childless during pruning, so clear our pointer to show that we
  4483. // so not have to deal with him later.
  4484. //
  4485. } else {
  4486. DeleteEntry = NULL;
  4487. }
  4488. }
  4489. //
  4490. // Step 5.
  4491. //
  4492. // Now there may still be a childless entry to deal with after the
  4493. // pruning above, if it did not turn out to be the guy we were deleting
  4494. // anyway. Note that if we have to do this delete, the ReinsertEntry
  4495. // pointer is guaranteed to be NULL. If our original target was not
  4496. // an intermediate node, then we never allocated one to begin with.
  4497. // Otherwise we passed through the preceding block of code, and either
  4498. // cleared ReinsertEntry or DeleteEntry.
  4499. //
  4500. if (DeleteEntry != NULL) {
  4501. ASSERT( ReinsertEntry == NULL );
  4502. //
  4503. // Now we have to save this guy away because we will have to
  4504. // reinsert him later.
  4505. //
  4506. ReinsertEntry = (PINDEX_ENTRY)NtfsAllocatePool( NonPagedPool,
  4507. DeleteEntry->Length );
  4508. RtlMoveMemory( ReinsertEntry, DeleteEntry, DeleteEntry->Length );
  4509. //
  4510. // We know the guy we are saving is an intermediate node, and that
  4511. // we no longer need his Vcn, since we deleted that buffer. Make
  4512. // the guy a leaf entry now. (We don't actually care about the
  4513. // Vcn, but we do this cleanup here in case the interface to
  4514. // NtfsAddIndexEntry were to change to take an initialized
  4515. // index entry.)
  4516. //
  4517. ClearFlag( ReinsertEntry->Flags, INDEX_ENTRY_NODE );
  4518. ReinsertEntry->Length -= sizeof(LONGLONG);
  4519. //
  4520. // Delete it.
  4521. //
  4522. IndexContext->Current = DeleteSp;
  4523. DeleteSimple( IrpContext, Scb, DeleteEntry, IndexContext );
  4524. }
  4525. //
  4526. // Step 6.
  4527. //
  4528. // Finally, we may have someone to reinsert now. This will either be
  4529. // someone we deleted as a replacement for our actual target, and then
  4530. // found out we did not need, or it could be just some entry that we
  4531. // had to delete just above, because the index buffers below him went
  4532. // empty and got deleted.
  4533. //
  4534. // In any case, we can no longer use the IndexContext we were called
  4535. // with because it no longer indicates where the replacement goes. We
  4536. // solve this by calling our top most external entry to do the insert.
  4537. //
  4538. if (ReinsertEntry != NULL) {
  4539. if (!FlagOn(Scb->ScbState, SCB_STATE_VIEW_INDEX)) {
  4540. NtfsAddIndexEntry( IrpContext,
  4541. Scb,
  4542. (PVOID)(ReinsertEntry + 1),
  4543. NtfsFileNameSize((PFILE_NAME)(ReinsertEntry + 1)),
  4544. &ReinsertEntry->FileReference,
  4545. NULL,
  4546. NULL );
  4547. } else {
  4548. INDEX_ROW IndexRow;
  4549. IndexRow.KeyPart.Key = ReinsertEntry + 1;
  4550. IndexRow.KeyPart.KeyLength = ReinsertEntry->AttributeLength;
  4551. IndexRow.DataPart.Data = Add2Ptr(ReinsertEntry, ReinsertEntry->DataOffset);
  4552. IndexRow.DataPart.DataLength = ReinsertEntry->DataLength;
  4553. NtOfsAddRecords( IrpContext,
  4554. Scb,
  4555. 1,
  4556. &IndexRow,
  4557. FALSE );
  4558. }
  4559. }
  4560. //
  4561. // Use the finally clause to free a potential ReinsertEntry we may
  4562. // have allocated.
  4563. //
  4564. } finally {
  4565. DebugUnwind( DeleteFromIndex );
  4566. if (ReinsertEntry != NULL) {
  4567. NtfsFreePool( ReinsertEntry );
  4568. }
  4569. }
  4570. DebugTrace( -1, Dbg, ("DeleteFromIndex -> VOID\n") );
  4571. }
  4572. VOID
  4573. DeleteSimple (
  4574. IN PIRP_CONTEXT IrpContext,
  4575. IN PSCB Scb,
  4576. IN PINDEX_ENTRY IndexEntry,
  4577. IN OUT PINDEX_CONTEXT IndexContext
  4578. )
  4579. /*++
  4580. Routine Description:
  4581. This routine does a simple insertion of an index entry, from either the
  4582. root or from an index allocation buffer. It writes the appropriate log
  4583. record first and then calls a routine in common with restart.
  4584. Arguments:
  4585. Scb - Supplies the Scb for the index.
  4586. IndexEntry - Points to the index entry to delete.
  4587. IndexContext - Describes the path to the index entry we are deleting.
  4588. Return Value:
  4589. None
  4590. --*/
  4591. {
  4592. PVCB Vcb = Scb->Vcb;
  4593. PINDEX_LOOKUP_STACK Sp = IndexContext->Current;
  4594. PAGED_CODE();
  4595. DebugTrace( +1, Dbg, ("DeleteSimple\n") );
  4596. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  4597. DebugTrace( 0, Dbg, ("IndexEntry = %08lx\n", IndexEntry) );
  4598. DebugTrace( 0, Dbg, ("IndexContext = %08lx\n", IndexContext) );
  4599. //
  4600. // Our caller never checks if he is deleting in the root or in the
  4601. // index allocation, so the first thing we do is check that.
  4602. //
  4603. // First we will handle the root case.
  4604. //
  4605. if (Sp == IndexContext->Base) {
  4606. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  4607. PATTRIBUTE_RECORD_HEADER Attribute;
  4608. PATTRIBUTE_ENUMERATION_CONTEXT Context;
  4609. //
  4610. // Initialize pointers for the root case.
  4611. //
  4612. Attribute = FindMoveableIndexRoot( IrpContext, Scb, IndexContext );
  4613. Context = &IndexContext->AttributeContext;
  4614. FileRecord = NtfsContainingFileRecord( Context );
  4615. //
  4616. // Pin the page before we start to modify it.
  4617. //
  4618. NtfsPinMappedAttribute( IrpContext, Vcb, Context );
  4619. //
  4620. // Write the log record first while we can still see the attribute
  4621. // we are going to delete.
  4622. //
  4623. FileRecord->Lsn =
  4624. NtfsWriteLog( IrpContext,
  4625. Vcb->MftScb,
  4626. NtfsFoundBcb(Context),
  4627. DeleteIndexEntryRoot,
  4628. NULL,
  4629. 0,
  4630. AddIndexEntryRoot,
  4631. IndexEntry,
  4632. IndexEntry->Length,
  4633. NtfsMftOffset( Context ),
  4634. (ULONG)((PCHAR)Attribute - (PCHAR)FileRecord),
  4635. (ULONG)((PCHAR)IndexEntry - (PCHAR)Attribute),
  4636. Vcb->BytesPerFileRecordSegment );
  4637. //
  4638. // Now call the same routine as Restart to actually delete it.
  4639. //
  4640. NtfsRestartDeleteSimpleRoot( IrpContext, IndexEntry, FileRecord, Attribute );
  4641. CheckRoot();
  4642. //
  4643. // Otherwise we are deleting in the index allocation, so do that here.
  4644. //
  4645. } else {
  4646. PINDEX_ALLOCATION_BUFFER IndexBuffer;
  4647. //
  4648. // Get the Index Buffer pointer from the stack.
  4649. //
  4650. IndexBuffer = (PINDEX_ALLOCATION_BUFFER)Sp->StartOfBuffer;
  4651. //
  4652. // Pin the page before we start to modify it.
  4653. //
  4654. NtfsPinMappedData( IrpContext,
  4655. Scb,
  4656. LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  4657. Scb->ScbType.Index.BytesPerIndexBuffer,
  4658. &Sp->Bcb );
  4659. //
  4660. // Write the log record now while the entry we are deleting is still
  4661. // there.
  4662. //
  4663. IndexBuffer->Lsn =
  4664. NtfsWriteLog( IrpContext,
  4665. Scb,
  4666. Sp->Bcb,
  4667. DeleteIndexEntryAllocation,
  4668. NULL,
  4669. 0,
  4670. AddIndexEntryAllocation,
  4671. IndexEntry,
  4672. IndexEntry->Length,
  4673. LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  4674. 0,
  4675. (ULONG)((PCHAR)IndexEntry - (PCHAR)IndexBuffer),
  4676. Scb->ScbType.Index.BytesPerIndexBuffer );
  4677. //
  4678. // Now call the same routine as Restart to delete the entry.
  4679. //
  4680. NtfsRestartDeleteSimpleAllocation( IndexEntry, IndexBuffer );
  4681. CheckBuffer(IndexBuffer);
  4682. }
  4683. DebugTrace( -1, Dbg, ("DeleteSimple -> VOID\n") );
  4684. }
  4685. VOID
  4686. NtfsRestartDeleteSimpleRoot (
  4687. IN PIRP_CONTEXT IrpContext,
  4688. IN PINDEX_ENTRY IndexEntry,
  4689. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  4690. IN PATTRIBUTE_RECORD_HEADER Attribute
  4691. )
  4692. /*++
  4693. Routine Description:
  4694. This is a restart routine which does a simple deletion of an index entry
  4695. from the Index Root, without logging. It is also used at run time.
  4696. Arguments:
  4697. IndexEntry - Points to the index entry to delete.
  4698. FileRecord - Points to the file record in which the index root resides.
  4699. Attribute - Points to the attribute record header for the index root.
  4700. Return Value:
  4701. None
  4702. --*/
  4703. {
  4704. PINDEX_ROOT IndexRoot;
  4705. PINDEX_HEADER IndexHeader;
  4706. PINDEX_ENTRY EntryAfter;
  4707. ULONG SavedLength;
  4708. PAGED_CODE();
  4709. DebugTrace( +1, Dbg, ("NtfsRestartDeleteSimpleRoot\n") );
  4710. DebugTrace( 0, Dbg, ("IndexEntry = %08lx\n", IndexEntry) );
  4711. DebugTrace( 0, Dbg, ("FileRecord = %08lx\n", FileRecord) );
  4712. DebugTrace( 0, Dbg, ("Attribute = %08lx\n", Attribute) );
  4713. //
  4714. // Form some pointers within the attribute value.
  4715. //
  4716. IndexRoot = (PINDEX_ROOT)NtfsAttributeValue(Attribute);
  4717. IndexHeader = &IndexRoot->IndexHeader;
  4718. SavedLength = (ULONG)IndexEntry->Length;
  4719. EntryAfter = NtfsNextIndexEntry(IndexEntry);
  4720. //
  4721. // Now move the tail end of the index to make room for the new entry.
  4722. //
  4723. RtlMoveMemory( IndexEntry,
  4724. EntryAfter,
  4725. ((PCHAR)IndexHeader + IndexHeader->FirstFreeByte) -
  4726. (PCHAR)EntryAfter );
  4727. //
  4728. // Update the index header by the space we grew by.
  4729. //
  4730. Attribute->Form.Resident.ValueLength -= SavedLength;
  4731. IndexHeader->FirstFreeByte -= SavedLength;
  4732. IndexHeader->BytesAvailable -= SavedLength;
  4733. //
  4734. // Now shrink the attribute record.
  4735. //
  4736. NtfsRestartChangeAttributeSize( IrpContext,
  4737. FileRecord,
  4738. Attribute,
  4739. Attribute->RecordLength - SavedLength );
  4740. DebugTrace( -1, Dbg, ("NtfsRestartDeleteSimpleRoot -> VOID\n") );
  4741. }
  4742. VOID
  4743. NtfsRestartDeleteSimpleAllocation (
  4744. IN PINDEX_ENTRY IndexEntry,
  4745. IN PINDEX_ALLOCATION_BUFFER IndexBuffer
  4746. )
  4747. /*++
  4748. Routine Description:
  4749. This is a restart routine which does a simple deletion of an index entry
  4750. from an index allocation buffer, without logging. It is also used at run time.
  4751. Arguments:
  4752. IndexEntry - Points to the index entry to delete.
  4753. IndexBuffer - Pointer to the index allocation buffer in which the delete is to
  4754. occur.
  4755. Return Value:
  4756. None
  4757. --*/
  4758. {
  4759. PINDEX_HEADER IndexHeader;
  4760. PINDEX_ENTRY EntryAfter;
  4761. ULONG SavedLength;
  4762. PAGED_CODE();
  4763. DebugTrace( +1, Dbg, ("NtfsRestartDeleteSimpleAllocation\n") );
  4764. DebugTrace( 0, Dbg, ("IndexEntry = %08lx\n", IndexEntry) );
  4765. DebugTrace( 0, Dbg, ("IndexBuffer = %08lx\n", IndexBuffer) );
  4766. //
  4767. // Form some pointers within the attribute value.
  4768. //
  4769. IndexHeader = &IndexBuffer->IndexHeader;
  4770. EntryAfter = NtfsNextIndexEntry(IndexEntry);
  4771. SavedLength = (ULONG)IndexEntry->Length;
  4772. //
  4773. // Now move the tail end of the index to make room for the new entry.
  4774. //
  4775. RtlMoveMemory( IndexEntry,
  4776. EntryAfter,
  4777. ((PCHAR)IndexHeader + IndexHeader->FirstFreeByte) -
  4778. (PCHAR)EntryAfter );
  4779. //
  4780. // Update the index header by the space we grew by.
  4781. //
  4782. IndexHeader->FirstFreeByte -= SavedLength;
  4783. DebugTrace( -1, Dbg, ("NtfsRestartDeleteSimpleAllocation -> VOID\n") );
  4784. }
  4785. VOID
  4786. PruneIndex (
  4787. IN PIRP_CONTEXT IrpContext,
  4788. IN PSCB Scb,
  4789. IN OUT PINDEX_CONTEXT IndexContext,
  4790. OUT PINDEX_ENTRY *DeleteEntry
  4791. )
  4792. /*++
  4793. Routine Description:
  4794. This routine checks if any index buffers need to be deallocated, as the result
  4795. of just having deleted an entry. The logic of the main loop is described in
  4796. detail below. All changes are logged.
  4797. Arguments:
  4798. Scb - Supplies the Scb of the index.
  4799. IndexContext - describes the path to the index buffer in which the delete just
  4800. occured.
  4801. DeleteEntry - Returns a pointer to an entry which must be deleted, because all
  4802. of the buffers below it were deleted.
  4803. Return Value:
  4804. None
  4805. --*/
  4806. {
  4807. PATTRIBUTE_ENUMERATION_CONTEXT Context;
  4808. PINDEX_ALLOCATION_BUFFER IndexBuffer;
  4809. PINDEX_HEADER IndexHeader;
  4810. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  4811. PATTRIBUTE_RECORD_HEADER Attribute = NULL;
  4812. PINDEX_LOOKUP_STACK Sp = IndexContext->Current;
  4813. PVCB Vcb = Scb->Vcb;
  4814. PAGED_CODE();
  4815. DebugTrace( +1, Dbg, ("PruneIndex\n") );
  4816. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  4817. DebugTrace( 0, Dbg, ("IndexContext = %08lx\n", IndexContext) );
  4818. DebugTrace( 0, Dbg, ("DeleteEntry = %08lx\n", DeleteEntry) );
  4819. //
  4820. // We do not allow ourselves to be called if the index has no
  4821. // allocation.
  4822. //
  4823. ASSERT( Sp != IndexContext->Base );
  4824. IndexBuffer = (PINDEX_ALLOCATION_BUFFER)Sp->StartOfBuffer;
  4825. IndexHeader = &IndexBuffer->IndexHeader;
  4826. //
  4827. // Initialize pointers for the root.
  4828. //
  4829. Context = &IndexContext->AttributeContext;
  4830. //
  4831. // Assume returning NULL.
  4832. //
  4833. *DeleteEntry = NULL;
  4834. //
  4835. // A pruning we will go...
  4836. //
  4837. while (TRUE) {
  4838. //
  4839. // The Index Buffer is empty if its first entry is the end record.
  4840. // If so, delete it, otherwise get out.
  4841. //
  4842. if (FlagOn(NtfsFirstIndexEntry(IndexHeader)->Flags, INDEX_ENTRY_END)) {
  4843. VCN IndexBlockNumber = IndexBuffer->ThisBlock;
  4844. NtfsUnpinBcb( IrpContext, &Sp->Bcb );
  4845. DeleteIndexBuffer( IrpContext, Scb, IndexBlockNumber );
  4846. } else {
  4847. break;
  4848. }
  4849. //
  4850. // We just deleted an Index Buffer, so we have to go up a level
  4851. // in the stack and take care of the Entry that was pointing to it.
  4852. // There are these cases:
  4853. //
  4854. // 1. If the Entry pointing to the one we deleted is not
  4855. // an End Entry, then we will remember its address in
  4856. // *DeleteEntry to cause it to be deleted and reinserted
  4857. // later.
  4858. // 2. If the Entry pointing to the one we deleted is an
  4859. // End Entry, and it is not the Index Root, then we
  4860. // cannot delete the End Entry, so we get the Vcn
  4861. // from the entry before the End, store it in the End
  4862. // record, and make the Entry before the End record
  4863. // the one returned in *DeleteEntry.
  4864. // 3. If the current Index Buffer has gone empty, and it is
  4865. // the index root, then we have an Index just gone
  4866. // empty. We have to catch this special case and
  4867. // transition the Index root back to an empty leaf by
  4868. // by calling NtfsCreateIndex to reinitialize it.
  4869. // 4. If there is no Entry before the end record, then the
  4870. // current Index Buffer is empty. If it is not the
  4871. // root, we just let ourselves loop back and delete the
  4872. // empty buffer in the while statement above.
  4873. //
  4874. Sp -= 1;
  4875. //
  4876. // When we get back to the root, look it up again because it may
  4877. // have moved.
  4878. //
  4879. if (Sp == IndexContext->Base) {
  4880. Attribute = FindMoveableIndexRoot( IrpContext, Scb, IndexContext );
  4881. }
  4882. IndexHeader = Sp->IndexHeader;
  4883. IndexBuffer = (PINDEX_ALLOCATION_BUFFER)Sp->StartOfBuffer;
  4884. //
  4885. // Remember potential entry to delete.
  4886. //
  4887. IndexContext->Current = Sp;
  4888. *DeleteEntry = Sp->IndexEntry;
  4889. //
  4890. // If the current delete entry is not an end entry, then we have
  4891. // Case 1 above, and we can break out.
  4892. //
  4893. if (!FlagOn((*DeleteEntry)->Flags, INDEX_ENTRY_END)) {
  4894. break;
  4895. }
  4896. //
  4897. // If we are pointing to the end record, but it is not the first in
  4898. // the buffer, then we have Case 2. We need to find the predecessor
  4899. // index entry, choose it for deletion, and copy its Vcn to the end
  4900. // record.
  4901. //
  4902. if (*DeleteEntry != NtfsFirstIndexEntry(IndexHeader)) {
  4903. PINDEX_ENTRY NextEntry;
  4904. NextEntry = NtfsFirstIndexEntry(IndexHeader);
  4905. NtfsCheckIndexBound( NextEntry, IndexHeader );
  4906. do {
  4907. *DeleteEntry = NextEntry;
  4908. NextEntry = NtfsNextIndexEntry(NextEntry);
  4909. NtfsCheckIndexBound( NextEntry, IndexHeader );
  4910. if (NextEntry->Length == 0) {
  4911. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  4912. }
  4913. } while (!FlagOn(NextEntry->Flags, INDEX_ENTRY_END));
  4914. //
  4915. // First handle the case where our parent is the Index Root.
  4916. //
  4917. if (Sp == IndexContext->Base) {
  4918. //
  4919. // Pin the page
  4920. //
  4921. NtfsPinMappedAttribute( IrpContext, Vcb, Context );
  4922. //
  4923. // Write a log record to change our ParentIndexEntry.
  4924. //
  4925. FileRecord = NtfsContainingFileRecord(Context);
  4926. FileRecord->Lsn =
  4927. NtfsWriteLog( IrpContext,
  4928. Vcb->MftScb,
  4929. NtfsFoundBcb(Context),
  4930. SetIndexEntryVcnRoot,
  4931. &NtfsIndexEntryBlock(*DeleteEntry),
  4932. sizeof(LONGLONG),
  4933. SetIndexEntryVcnRoot,
  4934. &NtfsIndexEntryBlock(NextEntry),
  4935. sizeof(LONGLONG),
  4936. NtfsMftOffset( Context ),
  4937. (ULONG)((PCHAR)Attribute - (PCHAR)FileRecord),
  4938. (ULONG)((PCHAR)NextEntry - (PCHAR)Attribute),
  4939. Vcb->BytesPerFileRecordSegment );
  4940. //
  4941. // Otherwise, our parent is also an Index Buffer.
  4942. //
  4943. } else {
  4944. //
  4945. // Pin the page
  4946. //
  4947. NtfsPinMappedData( IrpContext,
  4948. Scb,
  4949. LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  4950. Scb->ScbType.Index.BytesPerIndexBuffer,
  4951. &Sp->Bcb );
  4952. //
  4953. // Write a log record to change our ParentIndexEntry.
  4954. //
  4955. IndexBuffer->Lsn =
  4956. NtfsWriteLog( IrpContext,
  4957. Scb,
  4958. Sp->Bcb,
  4959. SetIndexEntryVcnAllocation,
  4960. &NtfsIndexEntryBlock(*DeleteEntry),
  4961. sizeof(LONGLONG),
  4962. SetIndexEntryVcnAllocation,
  4963. &NtfsIndexEntryBlock(NextEntry),
  4964. sizeof(LONGLONG),
  4965. LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  4966. 0,
  4967. (ULONG)((PCHAR)NextEntry - (PCHAR)IndexBuffer),
  4968. Scb->ScbType.Index.BytesPerIndexBuffer );
  4969. }
  4970. //
  4971. // Now call the Restart routine to do it.
  4972. //
  4973. NtfsRestartSetIndexBlock( NextEntry,
  4974. NtfsIndexEntryBlock(*DeleteEntry) );
  4975. break;
  4976. //
  4977. // Otherwise we are looking at an empty buffer. If it is the root
  4978. // then we have Case 3. We are returning an IndexRoot to the
  4979. // empty leaf case by reinitializing it.
  4980. //
  4981. } else if (Sp == IndexContext->Base) {
  4982. ATTRIBUTE_TYPE_CODE IndexedAttributeType = $UNUSED;
  4983. if (!FlagOn(Scb->ScbState, SCB_STATE_VIEW_INDEX)) {
  4984. IndexedAttributeType = Scb->ScbType.Index.AttributeBeingIndexed;
  4985. }
  4986. NtfsCreateIndex( IrpContext,
  4987. Scb->Fcb,
  4988. IndexedAttributeType,
  4989. (COLLATION_RULE)Scb->ScbType.Index.CollationRule,
  4990. Scb->ScbType.Index.BytesPerIndexBuffer,
  4991. Scb->ScbType.Index.BlocksPerIndexBuffer,
  4992. Context,
  4993. Scb->AttributeFlags,
  4994. FALSE,
  4995. TRUE );
  4996. //
  4997. // Nobody should use this context anymore, so set to crash
  4998. // if they try to use this index entry pointer.
  4999. //
  5000. IndexContext->OldAttribute = NtfsFoundAttribute(Context);
  5001. IndexContext->Base->IndexEntry = (PINDEX_ENTRY)NULL;
  5002. //
  5003. // In this case our caller has nothing to delete.
  5004. //
  5005. *DeleteEntry = NULL;
  5006. break;
  5007. //
  5008. // Otherwise, this is just some intermediate empty buffer, which
  5009. // is Case 4. Just continue back and keep on pruning.
  5010. //
  5011. } else {
  5012. continue;
  5013. }
  5014. }
  5015. //
  5016. // If it looks like we did some work, and did not already find the root again,
  5017. // then make sure the stack is correct for return.
  5018. //
  5019. if ((*DeleteEntry != NULL) && (Attribute == NULL)) {
  5020. FindMoveableIndexRoot( IrpContext, Scb, IndexContext );
  5021. }
  5022. DebugTrace( -1, Dbg, ("PruneIndex -> VOID\n") );
  5023. }
  5024. VOID
  5025. NtOfsRestartUpdateDataInIndex(
  5026. IN PINDEX_ENTRY IndexEntry,
  5027. IN PVOID IndexData,
  5028. IN ULONG Length )
  5029. /*++
  5030. Routine Description:
  5031. This is the restart routine used to apply updates to the data in a row,
  5032. both in run time and at restart.
  5033. Arguments:
  5034. IndexEntry - Supplies a pointer to the IndexEntry to be updated.
  5035. IndexData - Supplies the data for the update.
  5036. Return Value:
  5037. None.
  5038. --*/
  5039. {
  5040. PAGED_CODE();
  5041. RtlMoveMemory( Add2Ptr(IndexEntry, IndexEntry->DataOffset),
  5042. IndexData,
  5043. Length );
  5044. }