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

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