Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3469 lines
104 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. ViewSup.c
  5. Abstract:
  6. This module implements the Index management routines for NtOfs
  7. Author:
  8. Tom Miller [TomM] 5-Jan-1996
  9. Revision History:
  10. --*/
  11. #include "NtfsProc.h"
  12. #include "Index.h"
  13. //
  14. // The local debug trace level
  15. //
  16. #define Dbg (DEBUG_TRACE_VIEWSUP)
  17. //
  18. // Define a tag for general pool allocations from this module
  19. //
  20. #undef MODULE_POOL_TAG
  21. #define MODULE_POOL_TAG ('vFtN')
  22. //
  23. // Temporary definitions for test
  24. //
  25. BOOLEAN NtOfsDoIndexTest = TRUE;
  26. BOOLEAN NtOfsLeaveTestIndex = FALSE;
  27. extern ATTRIBUTE_DEFINITION_COLUMNS NtfsAttributeDefinitions[];
  28. //
  29. // Define a context for NtOfsReadRecords, which is primarily an IndexContext
  30. // and a copy of the last Key returned.
  31. //
  32. typedef struct _READ_CONTEXT {
  33. //
  34. // IndexContext (cursor) for the enumeration.
  35. //
  36. INDEX_CONTEXT IndexContext;
  37. //
  38. // The last key returned is allocated from paged pool. We have to
  39. // separately record how much is allocated, and how long the current
  40. // key is using, the latter being in the KeyLength field of IndexKey.
  41. // SmallKeyBuffer will store a small key in this structure without going
  42. // to pool.
  43. //
  44. INDEX_KEY LastReturnedKey;
  45. ULONG AllocatedKeyLength;
  46. ULONG SmallKeyBuffer[3];
  47. } READ_CONTEXT, *PREAD_CONTEXT;
  48. #ifdef ALLOC_PRAGMA
  49. #pragma alloc_text(PAGE, NtfsQueryViewIndex)
  50. #pragma alloc_text(PAGE, NtOfsCreateIndex)
  51. #pragma alloc_text(PAGE, NtOfsCloseIndex)
  52. #pragma alloc_text(PAGE, NtOfsDeleteIndex)
  53. #pragma alloc_text(PAGE, NtOfsFindRecord)
  54. #pragma alloc_text(PAGE, NtOfsAddRecords)
  55. #pragma alloc_text(PAGE, NtOfsDeleteRecords)
  56. #pragma alloc_text(PAGE, NtOfsUpdateRecord)
  57. #pragma alloc_text(PAGE, NtOfsReadRecords)
  58. #pragma alloc_text(PAGE, NtOfsFreeReadContext)
  59. #pragma alloc_text(PAGE, NtOfsFindLastRecord)
  60. #pragma alloc_text(PAGE, NtOfsCollateUlong)
  61. #pragma alloc_text(PAGE, NtOfsCollateUlongs)
  62. #pragma alloc_text(PAGE, NtOfsCollateUnicode)
  63. #pragma alloc_text(PAGE, NtOfsMatchAll)
  64. #pragma alloc_text(PAGE, NtOfsMatchUlongExact)
  65. #pragma alloc_text(PAGE, NtOfsMatchUlongsExact)
  66. #pragma alloc_text(PAGE, NtOfsMatchUnicodeExpression)
  67. #pragma alloc_text(PAGE, NtOfsMatchUnicodeString)
  68. #pragma alloc_text(PAGE, NtOfsCollateSid)
  69. #endif
  70. NTFSAPI
  71. NTSTATUS
  72. NtOfsCreateIndex (
  73. IN PIRP_CONTEXT IrpContext,
  74. IN PFCB Fcb,
  75. IN UNICODE_STRING Name,
  76. IN CREATE_OPTIONS CreateOptions,
  77. IN ULONG DeleteCollationData,
  78. IN ULONG CollationRule,
  79. IN PCOLLATION_FUNCTION CollationFunction,
  80. IN PVOID CollationData OPTIONAL,
  81. OUT PSCB *Scb
  82. )
  83. /*++
  84. Routine Description:
  85. This routine may be called to create / open a view index
  86. within a given file for a given CollationRule.
  87. Arguments:
  88. Fcb - File in which the index is to be created.
  89. Name - Name of the index for all related Scbs and attributes on disk.
  90. CreateOptions - Standard create flags.
  91. DeleteCollationData - Specifies 1 if the NtfsFreePool should be called
  92. for CollationData when no longer required, or 0
  93. if NtfsFreePool should never be called.
  94. CollationRule - A binary code to store in the index root to convey the
  95. collation function to ChkDsk. These rules are defined
  96. in ntfs.h, and must have a one-to-one correspondence with
  97. the CollationFunction below.
  98. CollationFunction - Function to be called to collate the index.
  99. CollationData - Data pointer to be passed to CollationFunction.
  100. Scb - Returns an Scb as handle for the index.
  101. Return Value:
  102. STATUS_OBJECT_NAME_COLLISION -- if CreateNew and index already exists
  103. STATUS_OBJECT_NAME_NOT_FOUND -- if OpenExisting and index does not exist
  104. --*/
  105. {
  106. ATTRIBUTE_ENUMERATION_CONTEXT LocalContext;
  107. BOOLEAN FoundAttribute;
  108. NTSTATUS Status = STATUS_SUCCESS;
  109. PBCB FileRecordBcb = NULL;
  110. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  111. LONGLONG FileRecordOffset;
  112. struct {
  113. INDEX_ROOT IndexRoot;
  114. INDEX_ENTRY EndEntry;
  115. } R;
  116. ASSERT_IRP_CONTEXT( IrpContext );
  117. PAGED_CODE();
  118. //
  119. // First we will initialize the Index Root structure which is the value
  120. // of the attribute we need to create.
  121. //
  122. RtlZeroMemory( &R, sizeof(R) );
  123. R.IndexRoot.CollationRule = CollationRule;
  124. R.IndexRoot.BytesPerIndexBuffer = NTOFS_VIEW_INDEX_BUFFER_SIZE;
  125. R.IndexRoot.BlocksPerIndexBuffer = (UCHAR)ClustersFromBytes( Fcb->Vcb,
  126. NTOFS_VIEW_INDEX_BUFFER_SIZE );
  127. if (NTOFS_VIEW_INDEX_BUFFER_SIZE < Fcb->Vcb->BytesPerCluster) {
  128. R.IndexRoot.BlocksPerIndexBuffer = NTOFS_VIEW_INDEX_BUFFER_SIZE / DEFAULT_INDEX_BLOCK_SIZE;
  129. }
  130. R.IndexRoot.IndexHeader.FirstIndexEntry = QuadAlign(sizeof(INDEX_HEADER));
  131. R.IndexRoot.IndexHeader.FirstFreeByte =
  132. R.IndexRoot.IndexHeader.BytesAvailable = QuadAlign(sizeof(INDEX_HEADER)) +
  133. QuadAlign(sizeof(INDEX_ENTRY));
  134. //
  135. // Now we need to put in the special End entry.
  136. //
  137. R.EndEntry.Length = sizeof(INDEX_ENTRY);
  138. SetFlag( R.EndEntry.Flags, INDEX_ENTRY_END );
  139. //
  140. // Now, just create the Index Root Attribute.
  141. //
  142. NtfsInitializeAttributeContext( &LocalContext );
  143. NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, 0 );
  144. try {
  145. //
  146. // First see if the index already exists, by searching for the root
  147. // attribute.
  148. //
  149. FoundAttribute = NtfsLookupAttributeByName( IrpContext,
  150. Fcb,
  151. &Fcb->FileReference,
  152. $INDEX_ROOT,
  153. &Name,
  154. NULL,
  155. TRUE,
  156. &LocalContext );
  157. //
  158. // If it is not there, and the CreateOptions allow, then let's create
  159. // the index root now. (First cleaning up the attribute context from
  160. // the lookup).
  161. //
  162. if (!FoundAttribute && (CreateOptions <= CREATE_OR_OPEN)) {
  163. NtfsCleanupAttributeContext( IrpContext, &LocalContext );
  164. NtfsCreateAttributeWithValue( IrpContext,
  165. Fcb,
  166. $INDEX_ROOT,
  167. &Name,
  168. &R,
  169. sizeof(R),
  170. 0,
  171. NULL,
  172. TRUE,
  173. &LocalContext );
  174. //
  175. // If the index is already there, and we were asked to create it, then
  176. // return an error.
  177. //
  178. } else if (FoundAttribute && (CreateOptions == CREATE_NEW)) {
  179. try_return( Status = STATUS_OBJECT_NAME_COLLISION );
  180. //
  181. // If the index is not there, and we were supposed to open existing, then
  182. // return an error.
  183. //
  184. } else if (!FoundAttribute && (CreateOptions == OPEN_EXISTING)) {
  185. try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND );
  186. }
  187. //
  188. // Otherwise create/find the Scb and reference it.
  189. //
  190. *Scb = NtfsCreateScb( IrpContext, Fcb, $INDEX_ALLOCATION, &Name, FALSE, NULL );
  191. SetFlag( (*Scb)->ScbState, SCB_STATE_VIEW_INDEX );
  192. (*Scb)->ScbType.Index.CollationFunction = CollationFunction;
  193. //
  194. // Handle the case where CollationData is to be deleted.
  195. //
  196. if (DeleteCollationData) {
  197. SetFlag((*Scb)->ScbState, SCB_STATE_DELETE_COLLATION_DATA);
  198. if ((*Scb)->ScbType.Index.CollationData != NULL) {
  199. NtfsFreePool(CollationData);
  200. } else {
  201. (*Scb)->ScbType.Index.CollationData = CollationData;
  202. }
  203. //
  204. // Otherwise just jam the pointer the caller passed.
  205. //
  206. } else {
  207. (*Scb)->ScbType.Index.CollationData = CollationData;
  208. }
  209. NtfsIncrementCloseCounts( *Scb, TRUE, FALSE );
  210. //
  211. // We have to set the view index present bit, so read it, save the
  212. // old data and set the flag here.
  213. //
  214. NtfsPinMftRecord( IrpContext,
  215. Fcb->Vcb,
  216. &Fcb->FileReference,
  217. FALSE,
  218. &FileRecordBcb,
  219. &FileRecord,
  220. &FileRecordOffset );
  221. //
  222. // If necessary, set the flag to indicate that this file will have
  223. // no unnamed data stream and any attempt to open this file without
  224. // specifying a named stream will fail, but without marking the
  225. // volume corrupt.
  226. //
  227. if (!FlagOn( FileRecord->Flags, FILE_VIEW_INDEX_PRESENT )) {
  228. //
  229. // We have to be very careful when using the InitialzeFileRecordSegment
  230. // log record. This action is applied unconditionally. DoAction doesn't
  231. // check the previous LSN in the page. It may be garbage on a newly initialized
  232. // file record. We log the entire file record to avoid the case where we
  233. // might overwrite a later Lsn with this earlier Lsn during restart.
  234. //
  235. //
  236. // Log the existing file record as the undo action.
  237. //
  238. FileRecord->Lsn = NtfsWriteLog( IrpContext,
  239. Fcb->Vcb->MftScb,
  240. FileRecordBcb,
  241. Noop,
  242. NULL,
  243. 0,
  244. InitializeFileRecordSegment,
  245. FileRecord,
  246. FileRecord->FirstFreeByte,
  247. FileRecordOffset,
  248. 0,
  249. 0,
  250. Fcb->Vcb->BytesPerFileRecordSegment );
  251. SetFlag( FileRecord->Flags, FILE_VIEW_INDEX_PRESENT );
  252. //
  253. // Log the new file record.
  254. //
  255. FileRecord->Lsn = NtfsWriteLog( IrpContext,
  256. Fcb->Vcb->MftScb,
  257. FileRecordBcb,
  258. InitializeFileRecordSegment,
  259. FileRecord,
  260. FileRecord->FirstFreeByte,
  261. Noop,
  262. NULL,
  263. 0,
  264. FileRecordOffset,
  265. 0,
  266. 0,
  267. Fcb->Vcb->BytesPerFileRecordSegment );
  268. }
  269. try_exit: NOTHING;
  270. } finally {
  271. NtfsCleanupAttributeContext( IrpContext, &LocalContext );
  272. NtfsUnpinBcb( IrpContext, &FileRecordBcb );
  273. NtfsReleaseFcb( IrpContext, Fcb );
  274. }
  275. return Status;
  276. }
  277. NTFSAPI
  278. VOID
  279. NtOfsCloseIndex (
  280. IN PIRP_CONTEXT IrpContext,
  281. IN PSCB Scb
  282. )
  283. /*++
  284. Routine Description:
  285. This routine may be called to close a previously returned handle on a view index.
  286. Arguments:
  287. Scb - Supplies an Scb as the previously returned handle for this index.
  288. Return Value:
  289. None.
  290. --*/
  291. {
  292. ASSERT( NtfsIsExclusiveFcb( Scb->Fcb ));
  293. NtfsDecrementCloseCounts( IrpContext, Scb, NULL, TRUE, FALSE, FALSE );
  294. }
  295. NTFSAPI
  296. VOID
  297. NtOfsDeleteIndex (
  298. IN PIRP_CONTEXT IrpContext,
  299. IN PFCB Fcb,
  300. IN PSCB Scb
  301. )
  302. /*++
  303. Routine Description:
  304. This routine may be called to delete an index.
  305. Arguments:
  306. Fcb - Supplies an Fcb as the previously returned object handle for the file
  307. Scb - Supplies an Scb as the previously returned handle for this index.
  308. Return Value:
  309. None (Deleting a nonexistant index is benign).
  310. --*/
  311. {
  312. ATTRIBUTE_ENUMERATION_CONTEXT LocalContext;
  313. ATTRIBUTE_TYPE_CODE AttributeTypeCode;
  314. BOOLEAN FoundAttribute;
  315. ASSERT_IRP_CONTEXT( IrpContext );
  316. ASSERT(($BITMAP - $INDEX_ALLOCATION) == ($INDEX_ALLOCATION - $INDEX_ROOT));
  317. PAGED_CODE();
  318. NtfsAcquireExclusiveScb( IrpContext, Scb );
  319. try {
  320. //
  321. // First see if there is some index allocation, and if so truncate it
  322. // away allowing this operation to be broken up.
  323. //
  324. NtfsInitializeAttributeContext( &LocalContext );
  325. if (NtfsLookupAttributeByName( IrpContext,
  326. Fcb,
  327. &Fcb->FileReference,
  328. $INDEX_ALLOCATION,
  329. &Scb->AttributeName,
  330. NULL,
  331. FALSE,
  332. &LocalContext )) {
  333. NtfsCreateInternalAttributeStream( IrpContext, Scb, TRUE, NULL );
  334. NtfsDeleteAllocation( IrpContext, NULL, Scb, 0, MAXLONGLONG, TRUE, TRUE );
  335. }
  336. NtfsCleanupAttributeContext( IrpContext, &LocalContext );
  337. for (AttributeTypeCode = $INDEX_ROOT;
  338. AttributeTypeCode <= $BITMAP;
  339. AttributeTypeCode += ($INDEX_ALLOCATION - $INDEX_ROOT)) {
  340. //
  341. // Initialize the attribute context on each trip through the loop.
  342. //
  343. NtfsInitializeAttributeContext( &LocalContext );
  344. //
  345. // First see if the index already exists, by searching for the root
  346. // attribute.
  347. //
  348. FoundAttribute = NtfsLookupAttributeByName( IrpContext,
  349. Fcb,
  350. &Fcb->FileReference,
  351. AttributeTypeCode,
  352. &Scb->AttributeName,
  353. NULL,
  354. TRUE,
  355. &LocalContext );
  356. //
  357. // Loop while we see the right records.
  358. //
  359. while (FoundAttribute) {
  360. NtfsDeleteAttributeRecord( IrpContext,
  361. Fcb,
  362. DELETE_LOG_OPERATION |
  363. DELETE_RELEASE_FILE_RECORD |
  364. DELETE_RELEASE_ALLOCATION,
  365. &LocalContext );
  366. FoundAttribute = NtfsLookupNextAttributeByName( IrpContext,
  367. Fcb,
  368. AttributeTypeCode,
  369. &Scb->AttributeName,
  370. TRUE,
  371. &LocalContext );
  372. }
  373. NtfsCleanupAttributeContext( IrpContext, &LocalContext );
  374. }
  375. SetFlag( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
  376. } finally {
  377. NtfsCleanupAttributeContext( IrpContext, &LocalContext );
  378. NtfsReleaseScb( IrpContext, Scb );
  379. }
  380. }
  381. NTFSAPI
  382. NTSTATUS
  383. NtOfsFindRecord (
  384. IN PIRP_CONTEXT IrpContext,
  385. IN PSCB Scb,
  386. IN PINDEX_KEY IndexKey,
  387. OUT PINDEX_ROW IndexRow,
  388. OUT PMAP_HANDLE MapHandle,
  389. IN OUT PQUICK_INDEX_HINT QuickIndexHint OPTIONAL
  390. )
  391. /*++
  392. Routine Description:
  393. This routine may be called to find the first occurrence of a key in an index,
  394. and return cached information which may can accelerate the update on the data
  395. for that key if the index buffer is not changed.
  396. Arguments:
  397. Scb - Supplies an Scb as the previously returned handle for this index.
  398. IndexKey - Supplies the key to find.
  399. IndexRow - Returns a description of the Key and Data *in place*, for read-only
  400. access, valid only until the Bcb is unpinned. (Neither key nor
  401. data may be modified in place!)
  402. MapHandle - Returns a map handle for accessing the key and data directly.
  403. QuickIndexHint - Supplies a previously returned hint, or all zeros on first use.
  404. Returns location information which may be held an arbitrary
  405. amount of time, which can accelerate a subsequent call to
  406. NtOfsUpdateRecord for the data in this key, iff changes to
  407. the index do not prohibit use of this hint.
  408. Return Value:
  409. STATUS_SUCCESS -- if operation was successful.
  410. STATUS_NO_MATCH -- if the specified key does not exist.
  411. --*/
  412. {
  413. INDEX_CONTEXT IndexContext;
  414. PINDEX_LOOKUP_STACK Sp;
  415. PINDEX_ENTRY IndexEntry;
  416. NTSTATUS Status;
  417. PQUICK_INDEX QuickIndex = (PQUICK_INDEX)QuickIndexHint;
  418. ASSERT_IRP_CONTEXT( IrpContext );
  419. ASSERT_SCB( Scb );
  420. PAGED_CODE();
  421. Status = STATUS_SUCCESS;
  422. NtfsInitializeIndexContext( &IndexContext );
  423. ASSERT_SHARED_SCB( Scb );
  424. try {
  425. //
  426. // Use the second location in the index context to perform the
  427. // read.
  428. //
  429. Sp =
  430. IndexContext.Current = IndexContext.Base + 1;
  431. //
  432. // If the index entry for this filename hasn't moved we can go
  433. // directly to the location in the buffer. For this to be the case the
  434. // following must be true.
  435. //
  436. // - The entry must already be in an index buffer (BufferOffset test)
  437. // - The index stream may not have been truncated (ChangeCount test)
  438. // - The Lsn in the page can't have changed
  439. //
  440. if (ARGUMENT_PRESENT( QuickIndexHint ) &&
  441. (QuickIndex->BufferOffset != 0) &&
  442. (QuickIndex->ChangeCount == Scb->ScbType.Index.ChangeCount)) {
  443. ReadIndexBuffer( IrpContext,
  444. Scb,
  445. QuickIndex->IndexBlock,
  446. FALSE,
  447. Sp );
  448. //
  449. // If the Lsn matches then we can use this buffer directly.
  450. //
  451. if (QuickIndex->CapturedLsn.QuadPart == Sp->CapturedLsn.QuadPart) {
  452. Sp->IndexEntry = (PINDEX_ENTRY) Add2Ptr( Sp->StartOfBuffer,
  453. QuickIndex->BufferOffset );
  454. //
  455. // Otherwise we need to reinitialize the index context and take
  456. // the long path below.
  457. //
  458. } else {
  459. NtfsReinitializeIndexContext( IrpContext, &IndexContext );
  460. }
  461. }
  462. //
  463. // If we did not get the index entry via the hint, get it now.
  464. //
  465. if (Sp->Bcb == NULL) {
  466. //
  467. // Position to first possible match.
  468. //
  469. FindFirstIndexEntry( IrpContext,
  470. Scb,
  471. IndexKey,
  472. &IndexContext );
  473. //
  474. // See if there is an actual match.
  475. //
  476. if (!FindNextIndexEntry( IrpContext,
  477. Scb,
  478. IndexKey,
  479. FALSE,
  480. FALSE,
  481. &IndexContext,
  482. FALSE,
  483. NULL )) {
  484. try_return( Status = STATUS_NO_MATCH );
  485. }
  486. }
  487. //
  488. // Basic consistency check
  489. //
  490. IndexEntry = IndexContext.Current->IndexEntry;
  491. if ((IndexEntry->DataOffset + IndexEntry->DataLength > IndexEntry->Length) ||
  492. (IndexEntry->AttributeLength + sizeof( INDEX_ENTRY ) > IndexEntry->Length)) {
  493. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  494. }
  495. //
  496. // If we found the key in the base, then return the Bcb from the
  497. // attribute context and return no hint (BufferOffset = 0).
  498. //
  499. if (IndexContext.Current == IndexContext.Base) {
  500. MapHandle->Buffer = NULL;
  501. MapHandle->Bcb = NtfsFoundBcb(&IndexContext.AttributeContext);
  502. NtfsFoundBcb(&IndexContext.AttributeContext) = NULL;
  503. if (ARGUMENT_PRESENT( QuickIndexHint )) {
  504. QuickIndex->BufferOffset = 0;
  505. }
  506. //
  507. // If we found the key in an index buffer, then return the Bcb from
  508. // the lookup stack, and record the hint for the caller.
  509. //
  510. } else {
  511. Sp = IndexContext.Current;
  512. MapHandle->Buffer = Sp->StartOfBuffer;
  513. MapHandle->Bcb = Sp->Bcb;
  514. Sp->Bcb = NULL;
  515. if (ARGUMENT_PRESENT( QuickIndexHint )) {
  516. QuickIndex->ChangeCount = Scb->ScbType.Index.ChangeCount;
  517. QuickIndex->BufferOffset = PtrOffset( Sp->StartOfBuffer, Sp->IndexEntry );
  518. QuickIndex->CapturedLsn = ((PINDEX_ALLOCATION_BUFFER) Sp->StartOfBuffer)->Lsn;
  519. QuickIndex->IndexBlock = ((PINDEX_ALLOCATION_BUFFER) Sp->StartOfBuffer)->ThisBlock;
  520. }
  521. }
  522. //
  523. // Complete the MapHandle to disallow pinning.
  524. //
  525. MapHandle->FileOffset = MAXLONGLONG;
  526. MapHandle->Length = MAXULONG;
  527. //
  528. // Return the IndexRow described directly in the buffer.
  529. //
  530. IndexRow->KeyPart.Key = (IndexEntry + 1);
  531. IndexRow->KeyPart.KeyLength = IndexEntry->AttributeLength;
  532. IndexRow->DataPart.Data = Add2Ptr( IndexEntry, IndexEntry->DataOffset );
  533. IndexRow->DataPart.DataLength = IndexEntry->DataLength;
  534. try_exit: NOTHING;
  535. } finally {
  536. NtfsCleanupIndexContext( IrpContext, &IndexContext );
  537. }
  538. return Status;
  539. }
  540. NTFSAPI
  541. NTSTATUS
  542. NtOfsFindLastRecord (
  543. IN PIRP_CONTEXT IrpContext,
  544. IN PSCB Scb,
  545. IN PINDEX_KEY MaxIndexKey,
  546. OUT PINDEX_ROW IndexRow,
  547. OUT PMAP_HANDLE MapHandle
  548. )
  549. /*++
  550. Routine Description:
  551. This routine may be called to find the highest key in an index.
  552. Arguments:
  553. Scb - Supplies an Scb as the previously returned handle for this index.
  554. MaxIndexKey - Supplies the maximum possible key value (such as MAXULONG, etc.),
  555. and this key must not actually be in use!
  556. IndexRow - Returns a description of the Key and Data *in place*, for read-only
  557. access, valid only until the Bcb is unpinned. (Neither key nor
  558. data may be modified in place!)
  559. MapHandle - Returns a map handle for accessing the key and data directly.
  560. Return Value:
  561. STATUS_SUCCESS -- if operation was successful.
  562. STATUS_NO_MATCH -- if the specified key does not exist (index is empty).
  563. --*/
  564. {
  565. INDEX_CONTEXT IndexContext;
  566. PINDEX_LOOKUP_STACK Sp;
  567. PINDEX_ENTRY IndexEntry, NextIndexEntry;
  568. NTSTATUS Status;
  569. ASSERT_IRP_CONTEXT( IrpContext );
  570. ASSERT_SCB( Scb );
  571. PAGED_CODE();
  572. Status = STATUS_SUCCESS;
  573. NtfsInitializeIndexContext( &IndexContext );
  574. NtfsAcquireSharedScb( IrpContext, Scb );
  575. try {
  576. //
  577. // Slide down the "right" side of the tree.
  578. //
  579. FindFirstIndexEntry( IrpContext,
  580. Scb,
  581. MaxIndexKey,
  582. &IndexContext );
  583. //
  584. // If this happens, the index must be empty.
  585. //
  586. Sp = IndexContext.Current;
  587. IndexEntry = NtfsFirstIndexEntry(Sp->IndexHeader);
  588. if (FlagOn(IndexEntry->Flags, INDEX_ENTRY_END)) {
  589. try_return( Status = STATUS_NO_MATCH );
  590. }
  591. //
  592. // If we found the key in the base, then return the Bcb from the
  593. // attribute context and return no hint (BufferOffset = 0).
  594. //
  595. if (IndexContext.Current == IndexContext.Base) {
  596. MapHandle->Bcb = NtfsFoundBcb(&IndexContext.AttributeContext);
  597. NtfsFoundBcb(&IndexContext.AttributeContext) = NULL;
  598. //
  599. // If we found the key in an index buffer, then return the Bcb from
  600. // the lookup stack, and record the hint for the caller.
  601. //
  602. } else {
  603. MapHandle->Bcb = Sp->Bcb;
  604. Sp->Bcb = NULL;
  605. }
  606. //
  607. // Complete the MapHandle to disallow pinning.
  608. //
  609. MapHandle->FileOffset = MAXLONGLONG;
  610. MapHandle->Length = MAXULONG;
  611. MapHandle->Buffer = NULL;
  612. //
  613. // Now rescan the last buffer to return the second to last index entry,
  614. // if there is one.
  615. //
  616. NextIndexEntry = IndexEntry;
  617. do {
  618. IndexEntry = NextIndexEntry;
  619. if (IndexEntry->Length == 0) {
  620. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  621. }
  622. NextIndexEntry = NtfsNextIndexEntry(IndexEntry);
  623. NtfsCheckIndexBound( NextIndexEntry, Sp->IndexHeader );
  624. } while (!FlagOn(NextIndexEntry->Flags, INDEX_ENTRY_END));
  625. //
  626. // Return the IndexRow described directly in the buffer.
  627. //
  628. IndexRow->KeyPart.Key = (IndexEntry + 1);
  629. IndexRow->KeyPart.KeyLength = IndexEntry->AttributeLength;
  630. IndexRow->DataPart.Data = Add2Ptr( IndexEntry, IndexEntry->DataOffset );
  631. IndexRow->DataPart.DataLength = IndexEntry->DataLength;
  632. try_exit: NOTHING;
  633. } finally {
  634. NtfsCleanupIndexContext( IrpContext, &IndexContext );
  635. NtfsReleaseScb( IrpContext, Scb );
  636. }
  637. return Status;
  638. }
  639. NTFSAPI
  640. VOID
  641. NtOfsAddRecords (
  642. IN PIRP_CONTEXT IrpContext,
  643. IN PSCB Scb,
  644. IN ULONG Count,
  645. IN PINDEX_ROW IndexRow,
  646. IN ULONG SequentialInsertMode
  647. )
  648. /*++
  649. Routine Description:
  650. This routine may be called to add one or more records to an index.
  651. If SequentialInsertMode is nonzero, this is a hint to the index package
  652. to keep all BTree buffers as full as possible, by splitting as close to
  653. the end of the buffer as possible. If specified as zero, random inserts
  654. are assumed, and buffers are always split in the middle for better balance.
  655. Arguments:
  656. Scb - Supplies an Scb as the previously returned handle for this index.
  657. Count - Supplies the number of records being added.
  658. IndexRow - Supplies an array of Count entries, containing the Keys and Data to add.
  659. SequentialInsertMode - If specified as nozero, the implementation may choose to
  660. split all index buffers at the end for maximum fill.
  661. Return Value:
  662. None.
  663. Raises:
  664. STATUS_DUPLICATE_NAME -- if the specified key already exists.
  665. --*/
  666. {
  667. INDEX_CONTEXT IndexContext;
  668. struct {
  669. INDEX_ENTRY IndexEntry;
  670. PVOID Key;
  671. PVOID Data;
  672. } IE;
  673. ULONG i;
  674. ASSERT_IRP_CONTEXT( IrpContext );
  675. ASSERT_SCB( Scb );
  676. UNREFERENCED_PARAMETER(SequentialInsertMode);
  677. PAGED_CODE();
  678. NtfsInitializeIndexContext( &IndexContext );
  679. NtfsAcquireExclusiveScb( IrpContext, Scb );
  680. try {
  681. //
  682. // Loop to add all entries
  683. //
  684. for (i = 0; i < Count; i++) {
  685. //
  686. // Position to first possible match.
  687. //
  688. FindFirstIndexEntry( IrpContext,
  689. Scb,
  690. &IndexRow->KeyPart,
  691. &IndexContext );
  692. //
  693. // See if there is an actual match.
  694. //
  695. if (FindNextIndexEntry( IrpContext,
  696. Scb,
  697. &IndexRow->KeyPart,
  698. FALSE,
  699. FALSE,
  700. &IndexContext,
  701. FALSE,
  702. NULL )) {
  703. NtfsRaiseStatus( IrpContext, STATUS_DUPLICATE_NAME, NULL, NULL );
  704. }
  705. //
  706. // Initialize the Index Entry in pointer form.
  707. //
  708. // Note that the final index entry ends up looking like this:
  709. //
  710. // (IndexEntry)(Key)(Data)
  711. //
  712. // where all fields are long-aligned and:
  713. //
  714. // Key is at IndexEntry + sizeof(INDEX_ENTRY), and of length AttributeLength
  715. // Data is at IndexEntry + DataOffset and of length DataLength
  716. //
  717. IE.IndexEntry.AttributeLength = (USHORT)IndexRow->KeyPart.KeyLength;
  718. IE.IndexEntry.DataOffset = (USHORT)(sizeof(INDEX_ENTRY) + LongAlign( IndexRow->KeyPart.KeyLength ));
  719. IE.IndexEntry.DataLength = (USHORT)IndexRow->DataPart.DataLength;
  720. IE.IndexEntry.ReservedForZero = 0;
  721. IE.IndexEntry.Length = (USHORT)(QuadAlign(IE.IndexEntry.DataOffset + IndexRow->DataPart.DataLength));
  722. IE.IndexEntry.Flags = INDEX_ENTRY_POINTER_FORM;
  723. IE.IndexEntry.Reserved = 0;
  724. IE.Key = IndexRow->KeyPart.Key;
  725. IE.Data = IndexRow->DataPart.Data;
  726. //
  727. // Now add it to the index. We can only add to a leaf, so force our
  728. // position back to the correct spot in a leaf first.
  729. //
  730. IndexContext.Current = IndexContext.Top;
  731. AddToIndex( IrpContext, Scb, (PINDEX_ENTRY)&IE, &IndexContext, NULL, FALSE );
  732. NtfsReinitializeIndexContext( IrpContext, &IndexContext );
  733. IndexRow += 1;
  734. }
  735. } finally {
  736. NtfsCleanupIndexContext( IrpContext, &IndexContext );
  737. NtfsReleaseScb( IrpContext, Scb );
  738. }
  739. }
  740. NTFSAPI
  741. VOID
  742. NtOfsDeleteRecords (
  743. IN PIRP_CONTEXT IrpContext,
  744. IN PSCB Scb,
  745. IN ULONG Count,
  746. IN PINDEX_KEY IndexKey
  747. )
  748. /*++
  749. Routine Description:
  750. This routine may be called to delete one or more records from an index.
  751. Arguments:
  752. Scb - Supplies an Scb as the previously returned handle for this index.
  753. Count - Supplies the number of records being deleted.
  754. IndexKey - Supplies an array of Count entries, containing the Keys to be deleted.
  755. Return Value:
  756. None. (This call is benign if any records do not exist.)
  757. --*/
  758. {
  759. INDEX_CONTEXT IndexContext;
  760. ULONG i;
  761. ASSERT_IRP_CONTEXT( IrpContext );
  762. ASSERT_SCB( Scb );
  763. PAGED_CODE();
  764. NtfsInitializeIndexContext( &IndexContext );
  765. NtfsAcquireExclusiveScb( IrpContext, Scb );
  766. try {
  767. //
  768. // Loop to add all entries
  769. //
  770. for (i = 0; i < Count; i++) {
  771. //
  772. // Position to first possible match.
  773. //
  774. FindFirstIndexEntry( IrpContext,
  775. Scb,
  776. IndexKey,
  777. &IndexContext );
  778. //
  779. // See if there is an actual match.
  780. //
  781. if (FindNextIndexEntry( IrpContext,
  782. Scb,
  783. IndexKey,
  784. FALSE,
  785. FALSE,
  786. &IndexContext,
  787. FALSE,
  788. NULL )) {
  789. //
  790. // Delete it.
  791. //
  792. DeleteFromIndex( IrpContext, Scb, &IndexContext );
  793. }
  794. NtfsReinitializeIndexContext( IrpContext, &IndexContext );
  795. IndexKey += 1;
  796. }
  797. } finally {
  798. NtfsCleanupIndexContext( IrpContext, &IndexContext );
  799. NtfsReleaseScb( IrpContext, Scb );
  800. }
  801. }
  802. NTFSAPI
  803. VOID
  804. NtOfsUpdateRecord (
  805. IN PIRP_CONTEXT IrpContext,
  806. IN PSCB Scb,
  807. IN ULONG Count,
  808. IN PINDEX_ROW IndexRow,
  809. IN OUT PQUICK_INDEX_HINT QuickIndexHint OPTIONAL,
  810. IN OUT PMAP_HANDLE MapHandle OPTIONAL
  811. )
  812. /*++
  813. Routine Description:
  814. This routine may be called to update the data portion of a record in an index.
  815. If QuickIndexHint is specified, then the update may occur by directly accessing
  816. the buffer containing the specified key, iff other changes to the index do not
  817. prevent that. If changes prevent the quick update, then the record is looked
  818. up by key in order to perform the data update.
  819. Arguments:
  820. Scb - Supplies an Scb as the previously returned handle for this index.
  821. Count - Supplies the count of updates described in IndexRow. For counts
  822. greater than 1, QuickIndexHint and MapHandle must not be supplied.
  823. IndexRow - Supplies the key to be updated and the new data for that key.
  824. QuickIndexHint - Supplies a optional quick index for this row returned from a previous
  825. call to NtOfsFindRecord, updated on return.
  826. MapHandle - Supplies an optional MapHandle to accompany the QuickIndex. If MapHandle
  827. is supplied, then the QuickIndexHint must be guaranteed valid. MapHandle
  828. is updated (pinned) on return.
  829. MapHandle is ignored if QuickIndexHint is not specified.
  830. Return Value:
  831. None.
  832. Raises:
  833. STATUS_INFO_LENGTH_MISMATCH -- if the specified data is a different length from the
  834. data in the key.
  835. STATUS_NO_MATCH -- if the specified key does not exist.
  836. --*/
  837. {
  838. INDEX_CONTEXT IndexContext;
  839. PQUICK_INDEX QuickIndex = (PQUICK_INDEX)QuickIndexHint;
  840. PVOID DataInIndex;
  841. PINDEX_ENTRY IndexEntry;
  842. PVCB Vcb = Scb->Vcb;
  843. PINDEX_LOOKUP_STACK Sp;
  844. PINDEX_ALLOCATION_BUFFER IndexBuffer;
  845. ASSERT_IRP_CONTEXT( IrpContext );
  846. ASSERT_SCB( Scb );
  847. ASSERT_SHARED_SCB( Scb );
  848. ASSERT(Count != 0);
  849. PAGED_CODE();
  850. NtfsInitializeIndexContext( &IndexContext );
  851. try {
  852. //
  853. // If the index entry for this filename hasn't moved we can go
  854. // directly to the location in the buffer. For this to be the case the
  855. // following must be true.
  856. //
  857. // - The entry must already be in an index buffer (BufferOffset test)
  858. // - The index stream may not have been truncated (ChangeCount test)
  859. // - The Lsn in the page can't have changed
  860. //
  861. if (ARGUMENT_PRESENT( QuickIndexHint ) &&
  862. (QuickIndex->BufferOffset != 0) &&
  863. (QuickIndex->ChangeCount == Scb->ScbType.Index.ChangeCount)) {
  864. ASSERT(Count == 1);
  865. //
  866. // Use the top location in the index context to perform the
  867. // read.
  868. //
  869. Sp = IndexContext.Base;
  870. //
  871. // If we have a MapHandle already, we do not need to read the
  872. // IndexBuffer.
  873. //
  874. if (ARGUMENT_PRESENT(MapHandle)) {
  875. IndexBuffer = MapHandle->Buffer;
  876. Sp->Bcb = MapHandle->Bcb;
  877. MapHandle->Bcb = NULL;
  878. Sp->CapturedLsn.QuadPart = QuickIndex->CapturedLsn.QuadPart;
  879. } else {
  880. ReadIndexBuffer( IrpContext,
  881. Scb,
  882. QuickIndex->IndexBlock,
  883. FALSE,
  884. Sp );
  885. IndexBuffer = Sp->StartOfBuffer;
  886. }
  887. //
  888. // If the Lsn matches then we can use this buffer directly.
  889. //
  890. if (QuickIndex->CapturedLsn.QuadPart == Sp->CapturedLsn.QuadPart) {
  891. IndexEntry = (PINDEX_ENTRY) Add2Ptr( IndexBuffer, QuickIndex->BufferOffset );
  892. if (IndexEntry->DataLength < IndexRow->DataPart.DataLength) {
  893. NtfsRaiseStatus( IrpContext, STATUS_INFO_LENGTH_MISMATCH, NULL, NULL );
  894. }
  895. DataInIndex = Add2Ptr( IndexEntry, IndexEntry->DataOffset );
  896. //
  897. // Pin the index buffer
  898. //
  899. NtfsPinMappedData( IrpContext,
  900. Scb,
  901. LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  902. Scb->ScbType.Index.BytesPerIndexBuffer,
  903. &Sp->Bcb );
  904. //
  905. // Write a log record to change our ParentIndexEntry.
  906. //
  907. //
  908. // Write the log record, but do not update the IndexBuffer Lsn,
  909. // since nothing moved and we don't want to force index contexts
  910. // to have to rescan.
  911. //
  912. // Indexbuffer->Lsn =
  913. //
  914. // ASSERT(Scb->ScbType.Index.ClustersPerIndexBuffer != 0);
  915. NtfsWriteLog( IrpContext,
  916. Scb,
  917. Sp->Bcb,
  918. UpdateRecordDataAllocation,
  919. IndexRow->DataPart.Data,
  920. IndexRow->DataPart.DataLength,
  921. UpdateRecordDataAllocation,
  922. DataInIndex,
  923. IndexRow->DataPart.DataLength,
  924. LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  925. 0,
  926. QuickIndex->BufferOffset,
  927. Scb->ScbType.Index.BytesPerIndexBuffer );
  928. //
  929. // Now call the Restart routine to do it.
  930. //
  931. NtOfsRestartUpdateDataInIndex( IndexEntry,
  932. IndexRow->DataPart.Data,
  933. IndexRow->DataPart.DataLength );
  934. //
  935. // If there is a MapHandle, we must update the Bcb pointer.
  936. //
  937. if (ARGUMENT_PRESENT(MapHandle)) {
  938. MapHandle->Bcb = Sp->Bcb;
  939. Sp->Bcb = NULL;
  940. }
  941. try_return( NOTHING );
  942. //
  943. // Otherwise we need to unpin the Bcb and take
  944. // the long path below.
  945. //
  946. } else {
  947. ASSERT(!ARGUMENT_PRESENT(MapHandle));
  948. NtfsUnpinBcb( IrpContext, &Sp->Bcb );
  949. }
  950. }
  951. //
  952. // Loop to apply all updates.
  953. //
  954. do {
  955. //
  956. // Position to first possible match.
  957. //
  958. FindFirstIndexEntry( IrpContext,
  959. Scb,
  960. &IndexRow->KeyPart,
  961. &IndexContext );
  962. //
  963. // See if there is an actual match.
  964. //
  965. if (FindNextIndexEntry( IrpContext,
  966. Scb,
  967. &IndexRow->KeyPart,
  968. FALSE,
  969. FALSE,
  970. &IndexContext,
  971. FALSE,
  972. NULL )) {
  973. //
  974. // Point to the index entry and the data within it.
  975. //
  976. IndexEntry = IndexContext.Current->IndexEntry;
  977. if (IndexEntry->DataLength < IndexRow->DataPart.DataLength) {
  978. NtfsRaiseStatus( IrpContext, STATUS_INFO_LENGTH_MISMATCH, NULL, NULL );
  979. }
  980. DataInIndex = Add2Ptr( IndexEntry, IndexEntry->DataOffset );
  981. //
  982. // Now pin the entry.
  983. //
  984. if (IndexContext.Current == IndexContext.Base) {
  985. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  986. PATTRIBUTE_RECORD_HEADER Attribute;
  987. PATTRIBUTE_ENUMERATION_CONTEXT Context = &IndexContext.AttributeContext;
  988. //
  989. // Pin the root
  990. //
  991. NtfsPinMappedAttribute( IrpContext,
  992. Vcb,
  993. Context );
  994. //
  995. // Write a log record to change our ParentIndexEntry.
  996. //
  997. FileRecord = NtfsContainingFileRecord(Context);
  998. Attribute = NtfsFoundAttribute(Context);
  999. //
  1000. // Write the log record, but do not update the FileRecord Lsn,
  1001. // since nothing moved and we don't want to force index contexts
  1002. // to have to rescan.
  1003. //
  1004. // FileRecord->Lsn =
  1005. //
  1006. NtfsWriteLog( IrpContext,
  1007. Vcb->MftScb,
  1008. NtfsFoundBcb(Context),
  1009. UpdateRecordDataRoot,
  1010. IndexRow->DataPart.Data,
  1011. IndexRow->DataPart.DataLength,
  1012. UpdateRecordDataRoot,
  1013. DataInIndex,
  1014. IndexRow->DataPart.DataLength,
  1015. NtfsMftOffset( Context ),
  1016. (ULONG)((PCHAR)Attribute - (PCHAR)FileRecord),
  1017. (ULONG)((PCHAR)IndexEntry - (PCHAR)Attribute),
  1018. Vcb->BytesPerFileRecordSegment );
  1019. if (ARGUMENT_PRESENT( QuickIndexHint )) {
  1020. ASSERT( Count == 1 );
  1021. QuickIndex->BufferOffset = 0;
  1022. }
  1023. } else {
  1024. PINDEX_ALLOCATION_BUFFER IndexBuffer;
  1025. Sp = IndexContext.Current;
  1026. IndexBuffer = (PINDEX_ALLOCATION_BUFFER)Sp->StartOfBuffer;
  1027. //
  1028. // Pin the index buffer
  1029. //
  1030. NtfsPinMappedData( IrpContext,
  1031. Scb,
  1032. LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  1033. Scb->ScbType.Index.BytesPerIndexBuffer,
  1034. &Sp->Bcb );
  1035. //
  1036. // Write a log record to change our ParentIndexEntry.
  1037. //
  1038. //
  1039. // Write the log record, but do not update the IndexBuffer Lsn,
  1040. // since nothing moved and we don't want to force index contexts
  1041. // to have to rescan.
  1042. //
  1043. // Indexbuffer->Lsn =
  1044. //
  1045. NtfsWriteLog( IrpContext,
  1046. Scb,
  1047. Sp->Bcb,
  1048. UpdateRecordDataAllocation,
  1049. IndexRow->DataPart.Data,
  1050. IndexRow->DataPart.DataLength,
  1051. UpdateRecordDataAllocation,
  1052. DataInIndex,
  1053. IndexRow->DataPart.DataLength,
  1054. LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
  1055. 0,
  1056. (ULONG)((PCHAR)Sp->IndexEntry - (PCHAR)IndexBuffer),
  1057. Scb->ScbType.Index.BytesPerIndexBuffer );
  1058. if (ARGUMENT_PRESENT( QuickIndexHint )) {
  1059. ASSERT( Count == 1 );
  1060. QuickIndex->ChangeCount = Scb->ScbType.Index.ChangeCount;
  1061. QuickIndex->BufferOffset = PtrOffset( Sp->StartOfBuffer, Sp->IndexEntry );
  1062. QuickIndex->CapturedLsn = ((PINDEX_ALLOCATION_BUFFER) Sp->StartOfBuffer)->Lsn;
  1063. QuickIndex->IndexBlock = ((PINDEX_ALLOCATION_BUFFER) Sp->StartOfBuffer)->ThisBlock;
  1064. }
  1065. }
  1066. //
  1067. // Now call the Restart routine to do it.
  1068. //
  1069. NtOfsRestartUpdateDataInIndex( IndexEntry,
  1070. IndexRow->DataPart.Data,
  1071. IndexRow->DataPart.DataLength );
  1072. //
  1073. // If the file name is not in the index, this is a bad file.
  1074. //
  1075. } else {
  1076. NtfsRaiseStatus( IrpContext, STATUS_NO_MATCH, NULL, NULL );
  1077. }
  1078. //
  1079. // Get ready for the next pass through.
  1080. //
  1081. NtfsReinitializeIndexContext( IrpContext, &IndexContext );
  1082. IndexRow += 1;
  1083. } while (--Count);
  1084. try_exit: NOTHING;
  1085. } finally {
  1086. NtfsCleanupIndexContext( IrpContext, &IndexContext );
  1087. }
  1088. }
  1089. NTFSAPI
  1090. NTSTATUS
  1091. NtOfsReadRecords (
  1092. IN PIRP_CONTEXT IrpContext,
  1093. IN PSCB Scb,
  1094. IN OUT PREAD_CONTEXT *ReadContext,
  1095. IN PINDEX_KEY IndexKey OPTIONAL,
  1096. IN PMATCH_FUNCTION MatchFunction,
  1097. IN PVOID MatchData,
  1098. IN OUT ULONG *Count,
  1099. OUT PINDEX_ROW Rows,
  1100. IN ULONG BufferLength,
  1101. OUT PVOID Buffer
  1102. )
  1103. /*++
  1104. Routine Description:
  1105. This routine may be called to enumerate rows in an index, in collated
  1106. order. It only returns records accepted by the match function.
  1107. IndexKey may be specified at any time to start a new search from IndexKey,
  1108. and IndexKey must be specified on the first call for a given IrpContext
  1109. (and *ReadContext must be NULL).
  1110. The read terminates when either *Count records have been returned, or
  1111. BufferLength has been exhausted, or there are no more matching records.
  1112. NtOfsReadRecords will seek to the appropriate point in the BTree (as defined
  1113. by the IndexKey or saved position and the CollateFunction) and begin calling
  1114. MatchFunction for each record. It continues doing this while MatchFunction
  1115. returns STATUS_SUCCESS. If MatchFunction returns STATUS_NO_MORE_MATCHES,
  1116. NtOfsReadRecords will cache this result and not call MatchFunction again until
  1117. called with a non-NULL IndexKey.
  1118. Note that this call is self-synchronized, such that successive calls to
  1119. the routine are guaranteed to make progress through the index and to return
  1120. items in Collation order, in spite of Add and Delete record calls being
  1121. interspersed with Read records calls.
  1122. Arguments:
  1123. Scb - Supplies an Scb as the previously returned handle for this index.
  1124. ReadContext - On the first call this must supply a pointer to NULL. On
  1125. return a pointer to a private context structure is returned,
  1126. which must then be supplied on all subsequent calls. This
  1127. structure must be eventually be freed via NtOfsFreeReadContext.
  1128. IndexKey - If specified, supplies the key from which the enumeration is to
  1129. start/resume. It must be specified on the first call when *ReadContext
  1130. is NULL.
  1131. MatchFunction - Supplies the MatchFunction to be called to determine which
  1132. rows to return.
  1133. MatchData - Supplies the MatchData to be specified on each call to the MatchFunction.
  1134. Count - Supplies the count of how many rows may be received, and returns the
  1135. number of rows actually being returned.
  1136. Rows - Returns the Count row descriptions.
  1137. BufferLength - Supplies the length of the buffer in bytes, into which the
  1138. row keys and data are copied upon return.
  1139. Buffer - Supplies the buffer into which the rows may be copied.
  1140. Return Value:
  1141. STATUS_SUCCESS -- if operation was successful.
  1142. STATUS_NO_MATCH -- if there is no match for the specified IndexKey.
  1143. STATUS_NO_MORE_MATCHES -- if a match is returned or previously returned,
  1144. but there are no more matches.
  1145. --*/
  1146. {
  1147. PINDEX_CONTEXT IndexContext;
  1148. PINDEX_ENTRY IndexEntry;
  1149. ULONG LengthToCopy;
  1150. BOOLEAN MustRestart;
  1151. ULONG BytesRemaining = BufferLength;
  1152. ULONG ReturnCount = 0;
  1153. NTSTATUS Status;
  1154. BOOLEAN NextFlag;
  1155. ASSERT_IRP_CONTEXT( IrpContext );
  1156. ASSERT_SCB( Scb );
  1157. PAGED_CODE();
  1158. //
  1159. // On the first lookup, their must be a key.
  1160. //
  1161. ASSERT((IndexKey != NULL) || (*ReadContext != NULL));
  1162. //
  1163. // Everything must be Ulong aligned and sized.
  1164. //
  1165. ASSERT(IsLongAligned(Buffer));
  1166. ASSERT(IsLongAligned(BufferLength));
  1167. Status = STATUS_SUCCESS;
  1168. NextFlag = FALSE;
  1169. //
  1170. // Pick up the IndexContext, allocating one if we need to.
  1171. //
  1172. if (*ReadContext == NULL) {
  1173. *ReadContext = NtfsAllocatePool(PagedPool, sizeof(READ_CONTEXT) );
  1174. NtfsInitializeIndexContext( &(*ReadContext)->IndexContext );
  1175. (*ReadContext)->LastReturnedKey.Key = &(*ReadContext)->SmallKeyBuffer[0];
  1176. (*ReadContext)->LastReturnedKey.KeyLength = 0;
  1177. (*ReadContext)->AllocatedKeyLength = sizeof(READ_CONTEXT) -
  1178. FIELD_OFFSET(READ_CONTEXT, SmallKeyBuffer[0]);
  1179. }
  1180. IndexContext = &(*ReadContext)->IndexContext;
  1181. //
  1182. // Store the MatchFunction and Data in the IndexContext, for the enumerations.
  1183. //
  1184. IndexContext->MatchFunction = MatchFunction;
  1185. IndexContext->MatchData = MatchData;
  1186. NtfsAcquireSharedScb( IrpContext, Scb );
  1187. try {
  1188. //
  1189. // If a Key was passed, position to the first possible match.
  1190. //
  1191. if (ARGUMENT_PRESENT(IndexKey)) {
  1192. FindFirstIndexEntry( IrpContext,
  1193. Scb,
  1194. IndexKey,
  1195. IndexContext );
  1196. //
  1197. // Otherwise return here if we hit the end of the matches last time.
  1198. //
  1199. } else if ((*ReadContext)->LastReturnedKey.KeyLength == 0) {
  1200. try_return( Status = STATUS_NO_MORE_MATCHES );
  1201. }
  1202. //
  1203. // Loop while we still have space to store rows.
  1204. //
  1205. while (ReturnCount <= *Count) {
  1206. //
  1207. // If we're already at the end, don't call FindNextIndexEntry again.
  1208. //
  1209. ASSERT(ARGUMENT_PRESENT(IndexKey) ||
  1210. ((*ReadContext)->LastReturnedKey.KeyLength != 0));
  1211. //
  1212. // See if there is an actual match.
  1213. //
  1214. if (!FindNextIndexEntry( IrpContext,
  1215. Scb,
  1216. NULL, // Not needed because of Match Function
  1217. TRUE,
  1218. FALSE,
  1219. IndexContext,
  1220. NextFlag,
  1221. &MustRestart )) {
  1222. //
  1223. // First handle the restart case by resuming from the last
  1224. // key returned, and skip that one.
  1225. //
  1226. if (MustRestart) {
  1227. ASSERT(!ARGUMENT_PRESENT(IndexKey));
  1228. NtfsReinitializeIndexContext( IrpContext, IndexContext );
  1229. FindFirstIndexEntry( IrpContext,
  1230. Scb,
  1231. &(*ReadContext)->LastReturnedKey,
  1232. IndexContext );
  1233. //
  1234. // Set NextFlag to TRUE, so we can go back and skip
  1235. // the key we resumed on.
  1236. //
  1237. NextFlag = TRUE;
  1238. continue;
  1239. }
  1240. //
  1241. // No (more) entries - remember that the enumeration is done.
  1242. //
  1243. (*ReadContext)->LastReturnedKey.KeyLength = 0;
  1244. //
  1245. // Return the appropriate code based on whether we have returned
  1246. // any matches yet or not.
  1247. //
  1248. if ((ReturnCount == 0) && ARGUMENT_PRESENT(IndexKey)) {
  1249. Status = STATUS_NO_MATCH;
  1250. } else {
  1251. Status = STATUS_NO_MORE_MATCHES;
  1252. }
  1253. try_return(Status);
  1254. }
  1255. //
  1256. // We always need to go one beyond the one we can return to keep
  1257. // all resume cases the same, so now is the time to get out if the
  1258. // count is finished.
  1259. //
  1260. if (ReturnCount == *Count) {
  1261. break;
  1262. }
  1263. //
  1264. // Now we must always move to the next.
  1265. //
  1266. NextFlag = TRUE;
  1267. //
  1268. // First try to copy the key.
  1269. //
  1270. IndexEntry = IndexContext->Current->IndexEntry;
  1271. LengthToCopy = IndexEntry->AttributeLength;
  1272. if (LengthToCopy > BytesRemaining) {
  1273. break;
  1274. }
  1275. RtlCopyMemory( Buffer, IndexEntry + 1, LengthToCopy );
  1276. Rows->KeyPart.Key = Buffer;
  1277. Rows->KeyPart.KeyLength = LengthToCopy;
  1278. LengthToCopy = LongAlign(LengthToCopy);
  1279. Buffer = Add2Ptr( Buffer, LengthToCopy );
  1280. BytesRemaining -= LengthToCopy;
  1281. //
  1282. // Now try to copy the data.
  1283. //
  1284. LengthToCopy = IndexEntry->DataLength;
  1285. if (LengthToCopy > BytesRemaining) {
  1286. break;
  1287. }
  1288. RtlCopyMemory( Buffer, Add2Ptr(IndexEntry, IndexEntry->DataOffset), LengthToCopy );
  1289. Rows->DataPart.Data = Buffer;
  1290. Rows->DataPart.DataLength = LengthToCopy;
  1291. LengthToCopy = LongAlign(LengthToCopy);
  1292. Buffer = Add2Ptr( Buffer, LengthToCopy );
  1293. BytesRemaining -= LengthToCopy;
  1294. //
  1295. // Capture this key before looping back.
  1296. //
  1297. // First see if there is enough space.
  1298. //
  1299. if (Rows->KeyPart.KeyLength > (*ReadContext)->AllocatedKeyLength) {
  1300. PVOID NewBuffer;
  1301. //
  1302. // Allocate a new buffer.
  1303. //
  1304. LengthToCopy = LongAlign(Rows->KeyPart.KeyLength + 16);
  1305. NewBuffer = NtfsAllocatePool(PagedPool, LengthToCopy );
  1306. //
  1307. // Delete old key buffer?
  1308. //
  1309. if ((*ReadContext)->LastReturnedKey.Key != &(*ReadContext)->SmallKeyBuffer[0]) {
  1310. NtfsFreePool( (*ReadContext)->LastReturnedKey.Key );
  1311. }
  1312. (*ReadContext)->LastReturnedKey.Key = NewBuffer;
  1313. (*ReadContext)->AllocatedKeyLength = LengthToCopy;
  1314. }
  1315. RtlCopyMemory( (*ReadContext)->LastReturnedKey.Key,
  1316. Rows->KeyPart.Key,
  1317. Rows->KeyPart.KeyLength );
  1318. (*ReadContext)->LastReturnedKey.KeyLength = Rows->KeyPart.KeyLength;
  1319. Rows += 1;
  1320. ReturnCount += 1;
  1321. }
  1322. try_exit: NOTHING;
  1323. } finally {
  1324. #ifdef BENL_DBG
  1325. ASSERT( (*ReadContext)->AllocatedKeyLength >= (*ReadContext)->LastReturnedKey.KeyLength );
  1326. #endif
  1327. NtfsReinitializeIndexContext( IrpContext, IndexContext );
  1328. NtfsReleaseScb( IrpContext, Scb );
  1329. }
  1330. *Count = ReturnCount;
  1331. //
  1332. // If we are already returning something, but we got an error, change it
  1333. // to success to return what we have. Then we may or may not get this error
  1334. // again anyway when we are called back. This loop is currently not designed
  1335. // to resume correctly in all cases if there are already items returned.
  1336. //
  1337. if (ReturnCount != 0) {
  1338. Status = STATUS_SUCCESS;
  1339. }
  1340. return Status;
  1341. }
  1342. NTFSAPI
  1343. VOID
  1344. NtOfsFreeReadContext (
  1345. IN PREAD_CONTEXT ReadContext
  1346. )
  1347. /*++
  1348. Routine Description:
  1349. This routine is called to free an ReadContext created by NtOfsReadRecords.
  1350. Arguments:
  1351. ReadContext - Supplies the context to free.
  1352. Return Value:
  1353. STATUS_SUCCESS -- if operation was successful.
  1354. --*/
  1355. {
  1356. PAGED_CODE();
  1357. if (ReadContext->LastReturnedKey.Key != NULL &&
  1358. ReadContext->LastReturnedKey.Key != &ReadContext->SmallKeyBuffer[0]) {
  1359. NtfsFreePool( ReadContext->LastReturnedKey.Key );
  1360. }
  1361. if (ReadContext->IndexContext.Base != ReadContext->IndexContext.LookupStack) {
  1362. NtfsFreePool( ReadContext->IndexContext.Base );
  1363. }
  1364. NtfsFreePool( ReadContext );
  1365. }
  1366. NTSTATUS
  1367. NtfsQueryViewIndex (
  1368. IN PIRP_CONTEXT IrpContext,
  1369. IN PIRP Irp,
  1370. IN PVCB Vcb,
  1371. IN PSCB Scb,
  1372. IN PCCB Ccb
  1373. )
  1374. /*++
  1375. Routine Description:
  1376. This routine performs the query view index operation. It is responsible
  1377. for either completing or enqueuing the input Irp.
  1378. Arguments:
  1379. Irp - Supplies the Irp to process
  1380. Vcb - Supplies its Vcb
  1381. Scb - Supplies its Scb
  1382. Ccb - Supplies its Ccb
  1383. Return Value:
  1384. NTSTATUS - The return status for the operation
  1385. --*/
  1386. {
  1387. NTSTATUS Status = STATUS_SUCCESS;
  1388. PIO_STACK_LOCATION IrpSp;
  1389. PUCHAR Buffer;
  1390. CLONG UserBufferLength;
  1391. ULONG BaseLength;
  1392. ULONG SidLength;
  1393. FILE_INFORMATION_CLASS FileInformationClass;
  1394. BOOLEAN RestartScan;
  1395. BOOLEAN ReturnSingleEntry;
  1396. BOOLEAN GotEntry;
  1397. BOOLEAN LastPass;
  1398. BOOLEAN FirstPass = TRUE;
  1399. ULONG NextEntry;
  1400. ULONG LastEntry;
  1401. ULONG VariableLength;
  1402. PVOID CurrentEntryBuffer = NULL;
  1403. PINDEX_KEY IndexKey;
  1404. ULONG IndexKeyLength = 0;
  1405. PREAD_CONTEXT ReadContext = NULL;
  1406. PFILE_OBJECTID_INFORMATION ObjIdInfoPtr;
  1407. PFILE_QUOTA_INFORMATION QuotaInfoPtr = NULL;
  1408. PQUOTA_USER_DATA QuotaUserData;
  1409. PFILE_REPARSE_POINT_INFORMATION ReparsePointInfoPtr;
  1410. BOOLEAN ScbAcquired = FALSE;
  1411. BOOLEAN CcbAcquired = FALSE;
  1412. BOOLEAN FirstQueryForThisCcb = FALSE;
  1413. BOOLEAN IndexKeyAllocated = FALSE;
  1414. BOOLEAN IndexKeyKeyAllocated = FALSE;
  1415. BOOLEAN AccessingUserBuffer = FALSE;
  1416. ULONG ReadRecordBuffer[20];
  1417. ULONG VariableBytesToCopy = 0;
  1418. BOOLEAN AnotherEntryWillFit = TRUE;
  1419. BOOLEAN AtEndOfIndex = FALSE;
  1420. PSTRING RestartKey = NULL;
  1421. ULONG BytesRemainingInBuffer;
  1422. NTSTATUS ReadRecordStatus;
  1423. ULONG Count;
  1424. INDEX_ROW IndexRow;
  1425. //
  1426. // We need to be certain that the scratch buffer is big enough.
  1427. //
  1428. ASSERT( sizeof(ReadRecordBuffer) >= sizeof(FILE_OBJECTID_INFORMATION) );
  1429. ASSERT( sizeof(ReadRecordBuffer) >= sizeof(FILE_QUOTA_INFORMATION) );
  1430. ASSERT( sizeof(ReadRecordBuffer) >= sizeof(FILE_REPARSE_POINT_INFORMATION) );
  1431. ASSERT_IRP_CONTEXT( IrpContext );
  1432. ASSERT_IRP( Irp );
  1433. ASSERT_VCB( Vcb );
  1434. ASSERT_CCB( Ccb );
  1435. ASSERT_SCB( Scb );
  1436. PAGED_CODE();
  1437. //
  1438. // Get the current Stack location
  1439. //
  1440. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  1441. DebugTrace( +1, Dbg, ("NtfsQueryViewIndex...\n") );
  1442. DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
  1443. DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
  1444. DebugTrace( 0, Dbg, (" ->Length = %08lx\n", IrpSp->Parameters.QueryDirectory.Length) );
  1445. DebugTrace( 0, Dbg, (" ->FileInformationClass = %08lx\n", IrpSp->Parameters.QueryDirectory.FileInformationClass) );
  1446. DebugTrace( 0, Dbg, (" ->SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer) );
  1447. DebugTrace( 0, Dbg, (" ->RestartScan = %08lx\n", FlagOn(IrpSp->Flags, SL_RESTART_SCAN)) );
  1448. DebugTrace( 0, Dbg, (" ->ReturnSingleEntry = %08lx\n", FlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY)) );
  1449. DebugTrace( 0, Dbg, ("Vcb = %08lx\n", Vcb) );
  1450. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  1451. DebugTrace( 0, Dbg, ("Ccb = %08lx\n", Ccb) );
  1452. //
  1453. // Because we probably need to do the I/O anyway we'll reject any request
  1454. // right now that cannot wait for I/O. We do not want to abort after
  1455. // processing a few index entries.
  1456. //
  1457. if (!FlagOn(IrpContext->State, IRP_CONTEXT_STATE_WAIT)) {
  1458. DebugTrace( 0, Dbg, ("Automatically enqueue Irp to Fsp\n") );
  1459. Status = NtfsPostRequest( IrpContext, Irp );
  1460. DebugTrace( -1, Dbg, ("NtfsQueryViewIndex -> %08lx\n", Status) );
  1461. return Status;
  1462. }
  1463. //
  1464. // Reference our input parameters to make things easier
  1465. //
  1466. UserBufferLength = IrpSp->Parameters.QueryDirectory.Length;
  1467. FileInformationClass = IrpSp->Parameters.QueryDirectory.FileInformationClass;
  1468. RestartKey = IrpSp->Parameters.QueryDirectory.FileName;
  1469. //
  1470. // Look in the Ccb to see the type of search.
  1471. //
  1472. RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN);
  1473. ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY);
  1474. //
  1475. // Determine the size of the constant part of the structure and make sure the Scb
  1476. // and info class are in agreement. There may be some security implications in
  1477. // letting a user treat, say, the reparse index as the object id index.
  1478. //
  1479. switch (FileInformationClass) {
  1480. case FileObjectIdInformation:
  1481. BaseLength = sizeof( FILE_OBJECTID_INFORMATION );
  1482. IndexKeyLength = OBJECT_ID_KEY_LENGTH;
  1483. if (Scb != Vcb->ObjectIdTableScb) {
  1484. Status = STATUS_INVALID_INFO_CLASS;
  1485. }
  1486. break;
  1487. case FileQuotaInformation:
  1488. BaseLength = sizeof( FILE_QUOTA_INFORMATION );
  1489. IndexKeyLength = sizeof( ULONG );
  1490. if (Scb != Vcb->QuotaTableScb) {
  1491. Status = STATUS_INVALID_INFO_CLASS;
  1492. }
  1493. break;
  1494. case FileReparsePointInformation:
  1495. BaseLength = sizeof( FILE_REPARSE_POINT_INFORMATION );
  1496. IndexKeyLength = sizeof( REPARSE_INDEX_KEY );
  1497. if (Scb != Vcb->ReparsePointTableScb) {
  1498. Status = STATUS_INVALID_INFO_CLASS;
  1499. }
  1500. break;
  1501. default:
  1502. Status = STATUS_INVALID_INFO_CLASS;
  1503. break;
  1504. }
  1505. if (Status != STATUS_SUCCESS) {
  1506. NtfsCompleteRequest( IrpContext, Irp, Status );
  1507. DebugTrace( -1, Dbg, ("NtfsQueryViewIndex -> %08lx\n", Status) );
  1508. return Status;
  1509. }
  1510. try {
  1511. //
  1512. // We only allow one active request in this handle at a time. If this is
  1513. // not a synchronous request then wait on the handle.
  1514. //
  1515. if (!FlagOn( IrpSp->FileObject->Flags, FO_SYNCHRONOUS_IO )) {
  1516. EOF_WAIT_BLOCK WaitBlock;
  1517. NtfsAcquireIndexCcb( Scb, Ccb, &WaitBlock );
  1518. CcbAcquired = TRUE;
  1519. }
  1520. //
  1521. // Initialize the value we use to start the index enumeration. Read carefully,
  1522. // we're working with both IndexKey and IndexKey->Key here.
  1523. //
  1524. IndexKey = NtfsAllocatePool( PagedPool, sizeof(INDEX_KEY) );
  1525. IndexKeyAllocated = TRUE;
  1526. IndexKey->KeyLength = IndexKeyLength;
  1527. IndexKey->Key = NtfsAllocatePool( PagedPool, IndexKeyLength );
  1528. IndexKeyKeyAllocated = TRUE;
  1529. //
  1530. // When we first come into this function, there are a few interesting
  1531. // cases we need to consider to get everything initialized correctly.
  1532. //
  1533. // 1 We were called with some value from which to (re)start the enumeration,
  1534. // i.e. the caller wants to start somewhere in the middle of the index.
  1535. //
  1536. // 2 This is the first time we've been called to enumerate this index with
  1537. // this Ccb, in which case we want to start from the beginning. This
  1538. // is substantially similar to the case where the caller has enumerated
  1539. // this index before, but wishes to restart the scan from the beginning of
  1540. // the index.
  1541. //
  1542. // 3 This is _not_ the first time we've been called to enumerate this index
  1543. // with this Ccb, and no restart key was specified, and the caller does
  1544. // not wish to restart the scan. In this case, we need to pick up where
  1545. // the last call left off.
  1546. //
  1547. if (RestartKey != NULL) {
  1548. DebugTrace( 0, Dbg, ("Restart key NOT null (case 1)\n") );
  1549. //
  1550. // If we have a leftover query buffer from a previous call, free
  1551. // it, since we're no longer interested in where it left us.
  1552. //
  1553. if (Ccb->QueryBuffer != NULL) {
  1554. ASSERT(FlagOn( Ccb->Flags, CCB_FLAG_READ_CONTEXT_ALLOCATED ));
  1555. NtOfsFreeReadContext( Ccb->QueryBuffer );
  1556. Ccb->QueryBuffer = NULL;
  1557. ClearFlag( Ccb->Flags, CCB_FLAG_READ_CONTEXT_ALLOCATED | CCB_FLAG_LAST_INDEX_ROW_RETURNED );
  1558. }
  1559. //
  1560. // Store the initial value from which to start the enumeration,
  1561. // being careful not to write beyond the size of our allocated buffer.
  1562. //
  1563. if (RestartKey->Length > IndexKeyLength) {
  1564. NtfsFreePool( IndexKey->Key );
  1565. IndexKeyKeyAllocated = FALSE;
  1566. IndexKey->Key = NtfsAllocatePool( PagedPool, RestartKey->Length );
  1567. IndexKeyKeyAllocated = TRUE;
  1568. }
  1569. //
  1570. // Copy the key, and store the length.
  1571. //
  1572. RtlCopyMemory( IndexKey->Key,
  1573. RestartKey->Buffer,
  1574. RestartKey->Length );
  1575. IndexKey->KeyLength = IndexKeyLength = RestartKey->Length;
  1576. } else if (RestartScan || (Ccb->QueryBuffer == NULL)) {
  1577. DebugTrace( 0, Dbg, ("RestartScan || Qb null (case 2)") );
  1578. //
  1579. // The restart scan case is similar to the case where we're called with a
  1580. // RestartKey in that we want to deallocate any leftover info in the Ccb.
  1581. // The only difference is that we don't have a key from which to restart
  1582. // so we just set the key back to the appropriate starting value. If
  1583. // the Ccb has no query buffer, then this is our first enumeration call
  1584. // since the handle was opened, and we need to start from scratch.
  1585. //
  1586. if (Ccb->QueryBuffer != NULL) {
  1587. DebugTrace( 0, Dbg, ("Qb NOT null\n") );
  1588. ASSERT(FlagOn( Ccb->Flags, CCB_FLAG_READ_CONTEXT_ALLOCATED ));
  1589. NtOfsFreeReadContext( Ccb->QueryBuffer );
  1590. Ccb->QueryBuffer = NULL;
  1591. ClearFlag( Ccb->Flags, CCB_FLAG_READ_CONTEXT_ALLOCATED | CCB_FLAG_LAST_INDEX_ROW_RETURNED );
  1592. } else {
  1593. DebugTrace( 0, Dbg, ("Qb null\n") );
  1594. FirstQueryForThisCcb = TRUE;
  1595. }
  1596. if (FileInformationClass == FileQuotaInformation) {
  1597. //
  1598. // In the quota case, we have some special requirements for the fist key,
  1599. // so we want to init it to handle the case where we haven't been called
  1600. // with a restart key.
  1601. //
  1602. *((PULONG) IndexKey->Key) = QUOTA_FISRT_USER_ID;
  1603. } else {
  1604. RtlZeroMemory( IndexKey->Key,
  1605. IndexKeyLength );
  1606. }
  1607. } else {
  1608. DebugTrace( 0, Dbg, ("Ccb->QueryBuffer NOT null (case 3)\n") );
  1609. ASSERT(Ccb->QueryBuffer != NULL);
  1610. ASSERT(FlagOn( Ccb->Flags, CCB_FLAG_READ_CONTEXT_ALLOCATED ));
  1611. //
  1612. // We have a leftover query buffer in the Ccb, and we were _not_
  1613. // called with a restart key, and we're not restarting the enumeration.
  1614. // Let's just pick up the enumeration where the Ccb's buffer tells us
  1615. // the last call left off.
  1616. //
  1617. ReadContext = Ccb->QueryBuffer;
  1618. //
  1619. // If the previous pass set the keylength to 0, it must have hit the
  1620. // end of the index. Check the CCB_FLAG_LAST_INDEX_ROW_RETURNED bit
  1621. // to see if we already made our special last pass through here to
  1622. // return the last row.
  1623. //
  1624. if (ReadContext->LastReturnedKey.KeyLength == 0) {
  1625. DebugTrace( 0, Dbg, ("LastReturnedKey had 0 length\n") );
  1626. if (FlagOn(Ccb->Flags, CCB_FLAG_LAST_INDEX_ROW_RETURNED)) {
  1627. //
  1628. // We're at the end of the index, and the last entry has already
  1629. // been returned to our caller. We're all done now.
  1630. //
  1631. try_return( Status = STATUS_NO_MORE_FILES );
  1632. } else {
  1633. //
  1634. // We're at the end of the index, but we have not yet returned the
  1635. // last entry to our caller. We can't break out yet.
  1636. //
  1637. AtEndOfIndex = TRUE;
  1638. //
  1639. // Remember that we are returning the last entry now.
  1640. //
  1641. SetFlag( Ccb->Flags, CCB_FLAG_LAST_INDEX_ROW_RETURNED );
  1642. //
  1643. // We need to set this to a nonzero value so NtOfsReadRecords
  1644. // will honor our request to read the last record.
  1645. //
  1646. if (IndexKeyLength > ReadContext->AllocatedKeyLength) {
  1647. ReadContext->LastReturnedKey.KeyLength = ReadContext->AllocatedKeyLength;
  1648. } else {
  1649. ReadContext->LastReturnedKey.KeyLength = IndexKeyLength;
  1650. }
  1651. }
  1652. } else if (ReadContext->LastReturnedKey.KeyLength > IndexKeyLength) {
  1653. //
  1654. // There's not enough room to store the initial value from which to
  1655. // start the enumeration. Free the buffer and get a bigger one.
  1656. //
  1657. NtfsFreePool( IndexKey->Key );
  1658. IndexKeyKeyAllocated = FALSE;
  1659. IndexKey->Key = NtfsAllocatePool( PagedPool, ReadContext->LastReturnedKey.KeyLength );
  1660. IndexKeyKeyAllocated = TRUE;
  1661. }
  1662. //
  1663. // Make sure we're either using the small key buffer, or we've allocated
  1664. // a buffer that's big enough.
  1665. //
  1666. ASSERT( (ReadContext->LastReturnedKey.Key == &ReadContext->SmallKeyBuffer[0]) ||
  1667. (ReadContext->LastReturnedKey.KeyLength <= ReadContext->AllocatedKeyLength) );
  1668. //
  1669. // Store the initial value from which to start the enumeration.
  1670. //
  1671. RtlCopyMemory( IndexKey->Key,
  1672. ReadContext->LastReturnedKey.Key,
  1673. ReadContext->LastReturnedKey.KeyLength );
  1674. IndexKeyLength = ReadContext->LastReturnedKey.KeyLength;
  1675. }
  1676. Irp->IoStatus.Information = 0;
  1677. //
  1678. // Acquire shared access to the Scb.
  1679. //
  1680. NtfsAcquireSharedScb( IrpContext, Scb );
  1681. ScbAcquired = TRUE;
  1682. //
  1683. // If the volume is no longer mounted, we should fail this
  1684. // request. Since we have the Scb shared now, we know that
  1685. // a dismount request can't sneak in.
  1686. //
  1687. if (FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) {
  1688. try_return( Status = STATUS_VOLUME_DISMOUNTED );
  1689. }
  1690. try {
  1691. //
  1692. // If we are in the Fsp now because we had to wait earlier,
  1693. // we must map the user buffer, otherwise we can use the
  1694. // user's buffer directly.
  1695. //
  1696. AccessingUserBuffer = TRUE;
  1697. Buffer = NtfsMapUserBuffer( Irp );
  1698. AccessingUserBuffer = FALSE;
  1699. //
  1700. // At this point we are about to enter our query loop. We have
  1701. // already decided if we need to call restart or continue when we
  1702. // go after an index entry. The variables LastEntry and NextEntry are
  1703. // used to index into the user buffer. LastEntry is the last entry
  1704. // we added to the user buffer, and NextEntry is the current
  1705. // one we're working on.
  1706. //
  1707. LastEntry = 0;
  1708. NextEntry = 0;
  1709. while (TRUE) {
  1710. DebugTrace( 0, Dbg, ("Top of Loop\n") );
  1711. DebugTrace( 0, Dbg, ("LastEntry = %08lx\n", LastEntry) );
  1712. DebugTrace( 0, Dbg, ("NextEntry = %08lx\n", NextEntry) );
  1713. //
  1714. // Check to see if we should quit the loop because we are only
  1715. // returning a single entry. We actually want to spin around
  1716. // the loop top twice so that our enumeration has us left off
  1717. // at the last entry we didn't return.
  1718. //
  1719. LastPass = (ReturnSingleEntry && !FirstPass);
  1720. //
  1721. // Be sure to pessimistically reinitialize these locals each
  1722. // time through the loop.
  1723. //
  1724. GotEntry = FALSE;
  1725. IndexRow.KeyPart.KeyLength = 0;
  1726. IndexRow.DataPart.DataLength = 0;
  1727. Count = 1;
  1728. //
  1729. // On the first pass for this IrpContext, we MUST take this code path
  1730. // with a null readcontext and a non-null IndexKey. See the comment
  1731. // where NtOfsReadRecords is implemented.
  1732. //
  1733. if (FirstPass) {
  1734. DebugTrace( 0, Dbg, ("First pass\n") );
  1735. ReadContext = NULL;
  1736. ReadRecordStatus = NtOfsReadRecords( IrpContext,
  1737. Scb,
  1738. &ReadContext,
  1739. IndexKey,
  1740. NtOfsMatchAll,
  1741. IndexKey,
  1742. &Count,
  1743. &IndexRow,
  1744. sizeof(ReadRecordBuffer),
  1745. &ReadRecordBuffer );
  1746. //
  1747. // We want to store the new ReadContext in the Ccb. Free
  1748. // anything leftover in the Ccb first.
  1749. //
  1750. if (Ccb->QueryBuffer != NULL) {
  1751. ASSERT(FlagOn( Ccb->Flags, CCB_FLAG_READ_CONTEXT_ALLOCATED ));
  1752. NtOfsFreeReadContext( Ccb->QueryBuffer );
  1753. }
  1754. Ccb->QueryBuffer = ReadContext;
  1755. SetFlag( Ccb->Flags, CCB_FLAG_READ_CONTEXT_ALLOCATED );
  1756. GotEntry = (NT_SUCCESS( ReadRecordStatus ) &&
  1757. (IndexRow.KeyPart.KeyLength != 0));
  1758. } else if ((!AtEndOfIndex)) {
  1759. DebugTrace( 0, Dbg, ("Nth pass\n") );
  1760. //
  1761. // We don't want to do this if the previous trip through the loop
  1762. // took us to the end of the index.
  1763. //
  1764. ReadContext = Ccb->QueryBuffer;
  1765. ASSERT(ReadContext != NULL);
  1766. //
  1767. // Lookup the next index entry and set ourselves
  1768. // up for subsequent iterations through the loop.
  1769. //
  1770. ReadRecordStatus = NtOfsReadRecords( IrpContext,
  1771. Scb,
  1772. &ReadContext,
  1773. NULL,
  1774. NtOfsMatchAll,
  1775. NULL,
  1776. &Count,
  1777. &IndexRow,
  1778. sizeof(ReadRecordBuffer),
  1779. &ReadRecordBuffer );
  1780. GotEntry = (NT_SUCCESS( ReadRecordStatus ) &&
  1781. (IndexRow.KeyPart.KeyLength != 0));
  1782. }
  1783. //
  1784. // If we only want to do the top part of the loop this time, get out here.
  1785. // For more info, see the comment above where we set LastPass. Basically,
  1786. // if we're just advancing our index pointer in the return single case,
  1787. // if we don't have room to return another entry in the return multiple
  1788. // case, or if we came in pointing to the end of the index and we've
  1789. // already made one pass to return that entry to the caller, we should
  1790. // get out now.
  1791. //
  1792. if (LastPass ||
  1793. !AnotherEntryWillFit ||
  1794. (AtEndOfIndex && !FirstPass)) {
  1795. DebugTrace( 0, Dbg, ("LastPass = %08lx\n", LastPass) );
  1796. DebugTrace( 0, Dbg, ("AnotherEntryWillFit = %08lx\n", AnotherEntryWillFit) );
  1797. DebugTrace( 0, Dbg, ("...breaking out\n") );
  1798. if ((FileInformationClass == FileQuotaInformation) &&
  1799. (QuotaInfoPtr != NULL)) {
  1800. //
  1801. // In the quota enumeration case, we need to zero this field
  1802. // to indicate the end of the list.
  1803. //
  1804. QuotaInfoPtr->NextEntryOffset = 0;
  1805. }
  1806. break;
  1807. }
  1808. //
  1809. // Now check to see if we actually got another index entry. If
  1810. // we didn't then we also need to check if we never got any
  1811. // or if we just ran out. If we just ran out then we break out
  1812. // of the main loop and finish the Irp after the loop.
  1813. //
  1814. if (!GotEntry) {
  1815. DebugTrace( 0, Dbg, ("GotEntry is FALSE\n") );
  1816. if (!FirstPass) {
  1817. if (FirstQueryForThisCcb) {
  1818. try_return( Status = STATUS_NO_SUCH_FILE );
  1819. }
  1820. try_return( Status = STATUS_NO_MORE_FILES );
  1821. }
  1822. break;
  1823. }
  1824. //
  1825. // If the previous pass set the keylength to 0, it must have hit the
  1826. // end of the index.
  1827. //
  1828. if (ReadContext->LastReturnedKey.KeyLength == 0) {
  1829. DebugTrace( 0, Dbg, ("LastReturnedKey had 0 length (mid-loop)\n") );
  1830. //
  1831. // We're at the end of the index, but we have not yet returned the
  1832. // last entry to our caller. We can't break out yet.
  1833. //
  1834. AtEndOfIndex = TRUE;
  1835. //
  1836. // Remember that we are returning the last entry now.
  1837. //
  1838. SetFlag( Ccb->Flags, CCB_FLAG_LAST_INDEX_ROW_RETURNED );
  1839. }
  1840. //
  1841. // Here are the rules concerning filling up the buffer:
  1842. //
  1843. // 1. The Io system guarantees that there will always be
  1844. // enough room for at least one base record.
  1845. //
  1846. // 2. If the full first record (including variable length data)
  1847. // cannot fit, as much of the data as possible is copied
  1848. // and STATUS_BUFFER_OVERFLOW is returned.
  1849. //
  1850. // 3. If a subsequent record cannot completely fit into the
  1851. // buffer, none of it (as in 0 bytes) is copied, and
  1852. // STATUS_SUCCESS is returned. A subsequent query will
  1853. // pick up with this record.
  1854. //
  1855. BytesRemainingInBuffer = UserBufferLength - NextEntry;
  1856. if ( (NextEntry != 0) &&
  1857. ( (BaseLength + VariableLength > BytesRemainingInBuffer) ||
  1858. (UserBufferLength < NextEntry) ) ) {
  1859. DebugTrace( 0, Dbg, ("Next entry won't fit\n") );
  1860. try_return( Status = STATUS_SUCCESS );
  1861. }
  1862. ASSERT( BytesRemainingInBuffer >= BaseLength );
  1863. //
  1864. // Zero the base part of the structure.
  1865. //
  1866. AccessingUserBuffer = TRUE;
  1867. RtlZeroMemory( &Buffer[NextEntry], BaseLength );
  1868. //
  1869. // Now we have an entry to return to our caller. We'll
  1870. // case on the type of information requested and fill up the
  1871. // user buffer if everything fits.
  1872. //
  1873. switch (FileInformationClass) {
  1874. case FileObjectIdInformation:
  1875. ObjIdInfoPtr = (PFILE_OBJECTID_INFORMATION) (&Buffer[NextEntry]);
  1876. if (IndexRow.DataPart.DataLength == sizeof(NTFS_OBJECTID_INFORMATION)) {
  1877. RtlCopyMemory( &ObjIdInfoPtr->FileReference,
  1878. &(((NTFS_OBJECTID_INFORMATION *) IndexRow.DataPart.Data)->FileSystemReference),
  1879. sizeof( LONGLONG ) );
  1880. RtlCopyMemory( &ObjIdInfoPtr->ExtendedInfo,
  1881. ((NTFS_OBJECTID_INFORMATION *) IndexRow.DataPart.Data)->ExtendedInfo,
  1882. OBJECT_ID_EXT_INFO_LENGTH );
  1883. } else {
  1884. ASSERTMSG( "Bad objectid index datalength", FALSE );
  1885. SetFlag( Vcb->ObjectIdState, VCB_OBJECT_ID_CORRUPT );
  1886. try_return( STATUS_NO_MORE_FILES );
  1887. }
  1888. if (IndexRow.KeyPart.KeyLength == IndexKeyLength) {
  1889. RtlCopyMemory( &ObjIdInfoPtr->ObjectId,
  1890. IndexRow.KeyPart.Key,
  1891. IndexRow.KeyPart.KeyLength );
  1892. } else {
  1893. ASSERTMSG( "Bad objectid index keylength", FALSE );
  1894. SetFlag( Vcb->ObjectIdState, VCB_OBJECT_ID_CORRUPT );
  1895. try_return( STATUS_NO_MORE_FILES );
  1896. }
  1897. //
  1898. // Object Ids have no variable length data, so we can skip
  1899. // over some of the tricky code below.
  1900. //
  1901. VariableLength = 0;
  1902. break;
  1903. case FileQuotaInformation:
  1904. QuotaInfoPtr = (PFILE_QUOTA_INFORMATION) (&Buffer[NextEntry]);
  1905. QuotaUserData = (PQUOTA_USER_DATA) IndexRow.DataPart.Data;
  1906. //
  1907. // Skip this entry if it has been deleted.
  1908. //
  1909. if (FlagOn( QuotaUserData->QuotaFlags, QUOTA_FLAG_ID_DELETED )) {
  1910. continue;
  1911. }
  1912. SidLength = IndexRow.DataPart.DataLength - SIZEOF_QUOTA_USER_DATA;
  1913. QuotaInfoPtr->ChangeTime.QuadPart = QuotaUserData->QuotaChangeTime;
  1914. QuotaInfoPtr->QuotaUsed.QuadPart = QuotaUserData->QuotaUsed;
  1915. QuotaInfoPtr->QuotaThreshold.QuadPart = QuotaUserData->QuotaThreshold;
  1916. QuotaInfoPtr->QuotaLimit.QuadPart = QuotaUserData->QuotaLimit;
  1917. QuotaInfoPtr->SidLength = SidLength;
  1918. RtlCopyMemory( &QuotaInfoPtr->Sid,
  1919. &QuotaUserData->QuotaSid,
  1920. SidLength );
  1921. QuotaInfoPtr->NextEntryOffset = QuadAlign( SidLength + SIZEOF_QUOTA_USER_DATA );
  1922. VariableLength = QuotaInfoPtr->SidLength;
  1923. break;
  1924. case FileReparsePointInformation:
  1925. ReparsePointInfoPtr = (PFILE_REPARSE_POINT_INFORMATION) (&Buffer[NextEntry]);
  1926. if (IndexRow.KeyPart.KeyLength == sizeof(REPARSE_INDEX_KEY)) {
  1927. ReparsePointInfoPtr->Tag = ((PREPARSE_INDEX_KEY) IndexRow.KeyPart.Key)->FileReparseTag;
  1928. ReparsePointInfoPtr->FileReference = ((PREPARSE_INDEX_KEY) IndexRow.KeyPart.Key)->FileId.QuadPart;
  1929. } else {
  1930. ASSERTMSG( "Bad reparse point index key length", FALSE );
  1931. }
  1932. //
  1933. // Reparse points have no variable length data, so we can skip
  1934. // over some of the tricky code below.
  1935. //
  1936. VariableLength = 0;
  1937. break;
  1938. default:
  1939. try_return( Status = STATUS_INVALID_INFO_CLASS );
  1940. }
  1941. if (VariableLength != 0) {
  1942. //
  1943. // Compute how many bytes we can copy. This should only be less
  1944. // than the variable length if we are only returning a single
  1945. // entry.
  1946. //
  1947. if (BytesRemainingInBuffer >= BaseLength + VariableLength) {
  1948. VariableBytesToCopy = VariableLength;
  1949. } else {
  1950. VariableBytesToCopy = BytesRemainingInBuffer - BaseLength;
  1951. if (FileInformationClass == FileQuotaInformation) {
  1952. //
  1953. // In the quota enumeration case, we need to zero this field
  1954. // to indicate the end of the list.
  1955. //
  1956. QuotaInfoPtr->NextEntryOffset = 0;
  1957. }
  1958. Status = STATUS_BUFFER_OVERFLOW;
  1959. }
  1960. } else {
  1961. VariableBytesToCopy = 0;
  1962. }
  1963. //
  1964. // Set up the previous next entry offset.
  1965. //
  1966. if (FileInformationClass == FileQuotaInformation) {
  1967. *((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry;
  1968. }
  1969. AccessingUserBuffer = FALSE;
  1970. //
  1971. // And indicate how much of the user buffer we have currently
  1972. // used up. We must compute this value before we long align
  1973. // ourselves for the next entry. This is the point where we
  1974. // quad-align the length of the previous entry.
  1975. //
  1976. Irp->IoStatus.Information = QuadAlign( Irp->IoStatus.Information) +
  1977. BaseLength + VariableBytesToCopy;
  1978. //
  1979. // If we weren't able to copy the whole index entry, then we bail here.
  1980. //
  1981. if ( !NT_SUCCESS( Status ) ) {
  1982. DebugTrace( 0, Dbg, ("Couldn't copy the whole index entry, exiting\n") );
  1983. try_return( Status );
  1984. }
  1985. //
  1986. // Set ourselves up for the next iteration.
  1987. //
  1988. LastEntry = NextEntry;
  1989. NextEntry += (ULONG)QuadAlign( BaseLength + VariableBytesToCopy );
  1990. FirstPass = FALSE;
  1991. //
  1992. // Determine whether we should be able to fit another entry
  1993. // in the user's buffer after this one.
  1994. //
  1995. AnotherEntryWillFit = ((NextEntry + BaseLength) <= UserBufferLength);
  1996. }
  1997. } except(EXCEPTION_EXECUTE_HANDLER) {
  1998. Status = GetExceptionCode();
  1999. DebugTrace( -1, Dbg, ("NtfsQueryViewIndex raising %08lx\n", Status) );
  2000. if (FsRtlIsNtstatusExpected( Status )) {
  2001. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  2002. } else {
  2003. ExRaiseStatus( AccessingUserBuffer ? STATUS_INVALID_USER_BUFFER : Status );
  2004. }
  2005. }
  2006. //
  2007. // At this point we've successfully filled up some of the buffer so
  2008. // now is the time to set our status to success.
  2009. //
  2010. Status = STATUS_SUCCESS;
  2011. try_exit:
  2012. //
  2013. // Abort transaction on error by raising.
  2014. //
  2015. NtfsCleanupTransaction( IrpContext, Status, FALSE );
  2016. //
  2017. // Set the last access flag in the Fcb if the caller
  2018. // didn't set it explicitly.
  2019. //
  2020. if (!FlagOn( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS_TIME ) &&
  2021. !FlagOn( NtfsData.Flags, NTFS_FLAGS_DISABLE_LAST_ACCESS )) {
  2022. NtfsGetCurrentTime( IrpContext, Scb->Fcb->CurrentLastAccess );
  2023. SetFlag( Scb->Fcb->InfoFlags, FCB_INFO_UPDATE_LAST_ACCESS );
  2024. }
  2025. } finally {
  2026. DebugUnwind( NtfsQueryViewIndex );
  2027. if (ScbAcquired) {
  2028. NtfsReleaseScb( IrpContext, Scb );
  2029. }
  2030. NtfsCleanupAfterEnumeration( IrpContext, Ccb );
  2031. if (CcbAcquired) {
  2032. NtfsReleaseIndexCcb( Scb, Ccb );
  2033. }
  2034. if (IndexKeyAllocated) {
  2035. if (IndexKeyKeyAllocated) {
  2036. NtfsFreePool( IndexKey->Key );
  2037. }
  2038. NtfsFreePool( IndexKey );
  2039. }
  2040. if (!AbnormalTermination()) {
  2041. NtfsCompleteRequest( IrpContext, Irp, Status );
  2042. }
  2043. }
  2044. //
  2045. // And return to our caller
  2046. //
  2047. DebugTrace( -1, Dbg, ("NtfsQueryViewIndex -> %08lx\n", Status) );
  2048. return Status;
  2049. }
  2050. /*++
  2051. Routine Descriptions:
  2052. Standard collation routines for creating simple indices.
  2053. Arguments:
  2054. Key1 - First key to compare.
  2055. Key2 - Second key to compare.
  2056. CollationData - Optional data to support the collation.
  2057. Return Value:
  2058. LessThan, EqualTo, or Greater than, for how Key1 compares
  2059. with Key2.
  2060. --*/
  2061. FSRTL_COMPARISON_RESULT
  2062. NtOfsCollateUlong (
  2063. IN PINDEX_KEY Key1,
  2064. IN PINDEX_KEY Key2,
  2065. IN PVOID CollationData
  2066. )
  2067. {
  2068. ULONG u1, u2;
  2069. UNREFERENCED_PARAMETER(CollationData);
  2070. ASSERT( Key1->KeyLength == 4 );
  2071. ASSERT( Key2->KeyLength == 4 );
  2072. u1 = *(PULONG)Key1->Key;
  2073. u2 = *(PULONG)Key2->Key;
  2074. if (u1 > u2) {
  2075. return GreaterThan;
  2076. } else if (u1 < u2) {
  2077. return LessThan;
  2078. }
  2079. return EqualTo;
  2080. }
  2081. FSRTL_COMPARISON_RESULT
  2082. NtOfsCollateUlongs (
  2083. IN PINDEX_KEY Key1,
  2084. IN PINDEX_KEY Key2,
  2085. IN PVOID CollationData
  2086. )
  2087. {
  2088. PULONG pu1, pu2;
  2089. ULONG count;
  2090. FSRTL_COMPARISON_RESULT result = EqualTo;
  2091. UNREFERENCED_PARAMETER(CollationData);
  2092. ASSERT( (Key1->KeyLength & 3) == 0 );
  2093. ASSERT( (Key2->KeyLength & 3) == 0 );
  2094. count = Key1->KeyLength;
  2095. if (count != Key2->KeyLength) {
  2096. result = LessThan;
  2097. if (count > Key2->KeyLength) {
  2098. count = Key2->KeyLength;
  2099. result = GreaterThan;
  2100. }
  2101. }
  2102. pu1 = (PULONG)Key1->Key;
  2103. pu2 = (PULONG)Key2->Key;
  2104. while (count > 0) {
  2105. if (*pu1 > *pu2) {
  2106. return GreaterThan;
  2107. } else if (*(pu1++) < *(pu2++)) {
  2108. return LessThan;
  2109. }
  2110. count -= 4;
  2111. }
  2112. return result;
  2113. }
  2114. FSRTL_COMPARISON_RESULT
  2115. NtOfsCollateSid (
  2116. IN PINDEX_KEY Key1,
  2117. IN PINDEX_KEY Key2,
  2118. IN PVOID CollationData
  2119. )
  2120. {
  2121. LONG Compare;
  2122. PAGED_CODE( );
  2123. UNREFERENCED_PARAMETER(CollationData);
  2124. //
  2125. // The length of a valid SID is imbedded in the data
  2126. // so the function will mismatch be for the data runs out.
  2127. //
  2128. Compare = memcmp( Key1->Key, Key2->Key, Key1->KeyLength );
  2129. if (Compare > 0) {
  2130. return GreaterThan;
  2131. } else if (Compare < 0) {
  2132. return LessThan;
  2133. }
  2134. return EqualTo;
  2135. }
  2136. FSRTL_COMPARISON_RESULT
  2137. NtOfsCollateUnicode (
  2138. IN PINDEX_KEY Key1,
  2139. IN PINDEX_KEY Key2,
  2140. IN PVOID CollationData
  2141. )
  2142. {
  2143. UNICODE_STRING String1, String2;
  2144. PAGED_CODE();
  2145. //
  2146. // Build the unicode strings and call namesup.
  2147. //
  2148. String1.Length =
  2149. String1.MaximumLength = (USHORT)Key1->KeyLength;
  2150. String1.Buffer = Key1->Key;
  2151. String2.Length =
  2152. String2.MaximumLength = (USHORT)Key2->KeyLength;
  2153. String2.Buffer = Key2->Key;
  2154. return NtfsCollateNames( ((PUPCASE_TABLE_AND_KEY)CollationData)->UpcaseTable,
  2155. ((PUPCASE_TABLE_AND_KEY)CollationData)->UpcaseTableSize,
  2156. &String1,
  2157. &String2,
  2158. LessThan,
  2159. TRUE );
  2160. }
  2161. /*++
  2162. Routine Descriptions:
  2163. Standard match routines for find / enumerate in simple indices.
  2164. Arguments:
  2165. IndexRow - Row to check for a match.
  2166. MatchData - Optional data for determining a match.
  2167. Return Value:
  2168. STATUS_SUCCESS if the IndexRow matches
  2169. STATUS_NO_MATCH if the IndexRow does not match, but the enumeration should
  2170. continue
  2171. STATUS_NO_MORE_MATCHES if the IndexRow does not match, and the enumeration
  2172. should terminate
  2173. --*/
  2174. NTSTATUS
  2175. NtOfsMatchAll (
  2176. IN PINDEX_ROW IndexRow,
  2177. IN OUT PVOID MatchData
  2178. )
  2179. {
  2180. UNREFERENCED_PARAMETER(IndexRow);
  2181. UNREFERENCED_PARAMETER(MatchData);
  2182. return STATUS_SUCCESS;
  2183. }
  2184. NTSTATUS
  2185. NtOfsMatchUlongExact (
  2186. IN PINDEX_ROW IndexRow,
  2187. IN OUT PVOID MatchData
  2188. )
  2189. {
  2190. ULONG u1, u2;
  2191. ASSERT( IndexRow->KeyPart.KeyLength == 4 );
  2192. u1 = *(PULONG)IndexRow->KeyPart.Key;
  2193. u2 = *(PULONG)((PINDEX_KEY)MatchData)->Key;
  2194. if (u1 == u2) {
  2195. return STATUS_SUCCESS;
  2196. } else if (u1 < u2) {
  2197. return STATUS_NO_MATCH;
  2198. }
  2199. return STATUS_NO_MORE_MATCHES;
  2200. }
  2201. NTSTATUS
  2202. NtOfsMatchUlongsExact (
  2203. IN PINDEX_ROW IndexRow,
  2204. IN OUT PVOID MatchData
  2205. )
  2206. {
  2207. PULONG pu1, pu2;
  2208. ULONG count;
  2209. NTSTATUS status = STATUS_SUCCESS;
  2210. ASSERT( (((PINDEX_KEY)MatchData)->KeyLength & 3) == 0 );
  2211. ASSERT( (IndexRow->KeyPart.KeyLength & 3) == 0 );
  2212. count = ((PINDEX_KEY)MatchData)->KeyLength;
  2213. if (count != IndexRow->KeyPart.KeyLength) {
  2214. status = STATUS_NO_MORE_MATCHES;
  2215. if (count > IndexRow->KeyPart.KeyLength) {
  2216. count = IndexRow->KeyPart.KeyLength;
  2217. status = STATUS_NO_MATCH;
  2218. }
  2219. }
  2220. pu1 = (PULONG)((PINDEX_KEY)MatchData)->Key;
  2221. pu2 = (PULONG)IndexRow->KeyPart.Key;
  2222. while (count > 0) {
  2223. if (*pu1 > *pu2) {
  2224. return STATUS_NO_MATCH;
  2225. } else if (*(pu1++) < *(pu2++)) {
  2226. return STATUS_NO_MORE_MATCHES;
  2227. }
  2228. count -= 4;
  2229. }
  2230. return status;
  2231. }
  2232. NTSTATUS
  2233. NtOfsMatchUnicodeExpression (
  2234. IN PINDEX_ROW IndexRow,
  2235. IN OUT PVOID MatchData
  2236. )
  2237. {
  2238. UNICODE_STRING MatchString, IndexString;
  2239. FSRTL_COMPARISON_RESULT BlindResult;
  2240. PUPCASE_TABLE_AND_KEY UpcaseTableAndKey = (PUPCASE_TABLE_AND_KEY)MatchData;
  2241. PAGED_CODE();
  2242. //
  2243. // Build the unicode strings and call namesup.
  2244. //
  2245. MatchString.Length =
  2246. MatchString.MaximumLength = (USHORT)UpcaseTableAndKey->Key.KeyLength;
  2247. MatchString.Buffer = UpcaseTableAndKey->Key.Key;
  2248. IndexString.Length =
  2249. IndexString.MaximumLength = (USHORT)IndexRow->KeyPart.KeyLength;
  2250. IndexString.Buffer = IndexRow->KeyPart.Key;
  2251. if (NtfsIsNameInExpression( UpcaseTableAndKey->UpcaseTable, &MatchString, &IndexString, TRUE )) {
  2252. return STATUS_SUCCESS;
  2253. } else if ((BlindResult = NtfsCollateNames(UpcaseTableAndKey->UpcaseTable,
  2254. UpcaseTableAndKey->UpcaseTableSize,
  2255. &MatchString,
  2256. &IndexString,
  2257. GreaterThan,
  2258. TRUE)) != LessThan) {
  2259. return STATUS_NO_MATCH;
  2260. } else {
  2261. return STATUS_NO_MORE_MATCHES;
  2262. }
  2263. }
  2264. NTSTATUS
  2265. NtOfsMatchUnicodeString (
  2266. IN PINDEX_ROW IndexRow,
  2267. IN OUT PVOID MatchData
  2268. )
  2269. {
  2270. UNICODE_STRING MatchString, IndexString;
  2271. FSRTL_COMPARISON_RESULT BlindResult;
  2272. PUPCASE_TABLE_AND_KEY UpcaseTableAndKey = (PUPCASE_TABLE_AND_KEY)MatchData;
  2273. PAGED_CODE();
  2274. //
  2275. // Build the unicode strings and call namesup.
  2276. //
  2277. MatchString.Length =
  2278. MatchString.MaximumLength = (USHORT)UpcaseTableAndKey->Key.KeyLength;
  2279. MatchString.Buffer = UpcaseTableAndKey->Key.Key;
  2280. IndexString.Length =
  2281. IndexString.MaximumLength = (USHORT)IndexRow->KeyPart.KeyLength;
  2282. IndexString.Buffer = IndexRow->KeyPart.Key;
  2283. if (NtfsAreNamesEqual( UpcaseTableAndKey->UpcaseTable, &MatchString, &IndexString, TRUE )) {
  2284. return STATUS_SUCCESS;
  2285. } else if ((BlindResult = NtfsCollateNames(UpcaseTableAndKey->UpcaseTable,
  2286. UpcaseTableAndKey->UpcaseTableSize,
  2287. &MatchString,
  2288. &IndexString,
  2289. GreaterThan,
  2290. TRUE)) != LessThan) {
  2291. return STATUS_NO_MATCH;
  2292. } else {
  2293. return STATUS_NO_MORE_MATCHES;
  2294. }
  2295. }
  2296. #ifdef TOMM
  2297. VOID
  2298. NtOfsIndexTest (
  2299. PIRP_CONTEXT IrpContext,
  2300. PFCB TestFcb
  2301. )
  2302. {
  2303. PSCB AdScb;
  2304. NTSTATUS Status;
  2305. ULONG i;
  2306. MAP_HANDLE MapHandle;
  2307. ULONG Count;
  2308. UPCASE_TABLE_AND_KEY UpcaseTableAndKey;
  2309. QUICK_INDEX_HINT QuickHint;
  2310. INDEX_KEY IndexKey;
  2311. INDEX_ROW IndexRow[6];
  2312. UCHAR Buffer[6*160];
  2313. PREAD_CONTEXT ReadContext = NULL;
  2314. UNICODE_STRING IndexName = CONSTANT_UNICODE_STRING( L"$Test" );
  2315. USHORT MaxKey = MAXUSHORT;
  2316. USHORT MinKey = 0;
  2317. DbgPrint("NtOfs Make NtOfsDoIndexTest FALSE to suppress test\n");
  2318. DbgPrint("NtOfs Make NtOfsLeaveTestIndex TRUE to leave test index\n");
  2319. DbgBreakPoint();
  2320. if (!NtOfsDoIndexTest) {
  2321. return;
  2322. }
  2323. NtOfsDoIndexTest = FALSE;
  2324. UpcaseTableAndKey.UpcaseTable = TestFcb->Vcb->UpcaseTable;
  2325. UpcaseTableAndKey.UpcaseTableSize = TestFcb->Vcb->UpcaseTableSize;
  2326. UpcaseTableAndKey.Key.Key = NULL;
  2327. UpcaseTableAndKey.Key.KeyLength = 0;
  2328. //
  2329. // Create Test Index
  2330. //
  2331. DbgPrint("NtOfs creating test index\n");
  2332. NtOfsCreateIndex( IrpContext,
  2333. TestFcb,
  2334. IndexName,
  2335. CREATE_NEW,
  2336. 0,
  2337. COLLATION_NTOFS_ULONG,
  2338. &NtOfsCollateUnicode,
  2339. &UpcaseTableAndKey,
  2340. &AdScb );
  2341. DbgPrint("NtOfs created Test Index Scb %08lx\n", AdScb);
  2342. //
  2343. // Access empty index
  2344. //
  2345. DbgPrint("NtOfs lookup last in empty index\n");
  2346. IndexKey.Key = &MaxKey;
  2347. IndexKey.KeyLength = sizeof(MaxKey);
  2348. Status = NtOfsFindLastRecord( IrpContext, AdScb, &IndexKey, &IndexRow[0], &MapHandle );
  2349. ASSERT(!NT_SUCCESS(Status));
  2350. //
  2351. // Add some keys!
  2352. //
  2353. DbgPrint("NtOfs adding keys to index\n");
  2354. for (i = 0; i < $EA/0x10; i++) {
  2355. IndexRow[0].KeyPart.Key = &NtfsAttributeDefinitions[i].AttributeName;
  2356. IndexRow[0].KeyPart.KeyLength = 0x80;
  2357. IndexRow[0].DataPart.Data = (PCHAR)IndexRow[0].KeyPart.Key + 0x80;
  2358. IndexRow[0].DataPart.DataLength = sizeof(ATTRIBUTE_DEFINITION_COLUMNS) - 0x84;
  2359. NtOfsAddRecords( IrpContext, AdScb, 1, &IndexRow[0], 0 );
  2360. }
  2361. //
  2362. // Now find the last key
  2363. //
  2364. DbgPrint("NtOfs checkin last key in index\n");
  2365. IndexKey.Key = &MaxKey;
  2366. IndexKey.KeyLength = sizeof(MaxKey);
  2367. Status = NtOfsFindLastRecord( IrpContext, AdScb, &IndexKey, &IndexRow[0], &MapHandle );
  2368. ASSERT(NT_SUCCESS(Status));
  2369. ASSERT(RtlCompareMemory(IndexRow[0].KeyPart.Key, L"$VOLUME_NAME", sizeof(L"$VOLUME_NAME") - sizeof( WCHAR )) ==
  2370. (sizeof(L"$VOLUME_NAME") - sizeof( WCHAR )));
  2371. NtOfsReleaseMap( IrpContext, &MapHandle );
  2372. //
  2373. // See if they are all there.
  2374. //
  2375. DbgPrint("NtOfs looking up all keys in index\n");
  2376. for (i = 0; i < $EA/0x10; i++) {
  2377. IndexKey.Key = &NtfsAttributeDefinitions[i].AttributeName;
  2378. IndexKey.KeyLength = 0x80;
  2379. Status = NtOfsFindRecord( IrpContext, AdScb, &IndexKey, &IndexRow[0], &MapHandle, NULL );
  2380. if (!NT_SUCCESS(Status)) {
  2381. DbgPrint("NtOfsIterationFailure with i = %08lx, Status = %08lx\n", i, Status);
  2382. }
  2383. NtOfsReleaseMap( IrpContext, &MapHandle );
  2384. }
  2385. //
  2386. // Now enumerate the entire index
  2387. //
  2388. IndexKey.Key = &MinKey;
  2389. IndexKey.KeyLength = sizeof(MinKey);
  2390. Count = 6;
  2391. DbgPrint("NtOfs enumerating index:\n\n");
  2392. while (NT_SUCCESS(Status = NtOfsReadRecords( IrpContext,
  2393. AdScb,
  2394. &ReadContext,
  2395. (ReadContext == NULL) ? &IndexKey : NULL,
  2396. &NtOfsMatchAll,
  2397. NULL,
  2398. &Count,
  2399. IndexRow,
  2400. sizeof(Buffer),
  2401. Buffer ))) {
  2402. for (i = 0; i < Count; i++) {
  2403. DbgPrint( "IndexKey = %ws, AttributeTypeCode = %lx\n",
  2404. IndexRow[i].KeyPart.Key,
  2405. *(PULONG)IndexRow[i].DataPart.Data );
  2406. }
  2407. DbgPrint( "\n" );
  2408. }
  2409. NtOfsFreeReadContext( ReadContext );
  2410. ReadContext = NULL;
  2411. //
  2412. // Loop to update all records.
  2413. //
  2414. DbgPrint("NtOfs updating up all keys in index\n");
  2415. for (i = 0; i < $EA/0x10; i++) {
  2416. IndexKey.Key = &NtfsAttributeDefinitions[i].AttributeName;
  2417. IndexKey.KeyLength = 0x80;
  2418. RtlZeroMemory( &QuickHint, sizeof(QUICK_INDEX_HINT) );
  2419. NtOfsFindRecord( IrpContext, AdScb, &IndexKey, &IndexRow[0], &MapHandle, &QuickHint );
  2420. //
  2421. // Copy and update the data.
  2422. //
  2423. RtlCopyMemory( Buffer, IndexRow[0].DataPart.Data, IndexRow[0].DataPart.DataLength );
  2424. *(PULONG)Buffer += 0x100;
  2425. IndexRow[0].DataPart.Data = Buffer;
  2426. //
  2427. // Perform update with all valid combinations of hint and map handle.
  2428. //
  2429. NtOfsUpdateRecord( IrpContext,
  2430. AdScb,
  2431. 1,
  2432. &IndexRow[0],
  2433. (i <= $FILE_NAME/0x10) ? NULL : &QuickHint,
  2434. (i < $INDEX_ROOT/0x10) ? NULL : &MapHandle );
  2435. NtOfsReleaseMap( IrpContext, &MapHandle );
  2436. }
  2437. //
  2438. // Now enumerate the entire index again to see the updates.
  2439. //
  2440. IndexKey.Key = &MinKey;
  2441. IndexKey.KeyLength = sizeof(MinKey);
  2442. Count = 6;
  2443. DbgPrint("NtOfs enumerating index after updates:\n\n");
  2444. while (NT_SUCCESS(Status = NtOfsReadRecords( IrpContext,
  2445. AdScb,
  2446. &ReadContext,
  2447. (ReadContext == NULL) ? &IndexKey : NULL,
  2448. &NtOfsMatchAll,
  2449. NULL,
  2450. &Count,
  2451. IndexRow,
  2452. sizeof(Buffer),
  2453. Buffer ))) {
  2454. for (i = 0; i < Count; i++) {
  2455. DbgPrint( "IndexKey = %ws, AttributeTypeCode = %lx\n",
  2456. IndexRow[i].KeyPart.Key,
  2457. *(PULONG)IndexRow[i].DataPart.Data );
  2458. }
  2459. DbgPrint( "\n" );
  2460. }
  2461. NtOfsFreeReadContext( ReadContext );
  2462. ReadContext = NULL;
  2463. //
  2464. // Now delete the keys
  2465. //
  2466. if (!NtOfsLeaveTestIndex) {
  2467. DbgPrint("NtOfs deleting all keys in index:\n\n");
  2468. for (i = 0; i < $EA/0x10; i++) {
  2469. IndexKey.Key = &NtfsAttributeDefinitions[i].AttributeName;
  2470. IndexKey.KeyLength = 0x80;
  2471. NtOfsDeleteRecords( IrpContext, AdScb, 1, &IndexKey );
  2472. }
  2473. //
  2474. // Access empty index
  2475. //
  2476. DbgPrint("NtOfs lookup last key in empty index:\n\n");
  2477. IndexKey.Key = &MaxKey;
  2478. IndexKey.KeyLength = sizeof(MaxKey);
  2479. Status = NtOfsFindLastRecord( IrpContext, AdScb, &IndexKey, &IndexRow[0], &MapHandle );
  2480. ASSERT(!NT_SUCCESS(Status));
  2481. DbgPrint("NtOfs deleting index:\n");
  2482. NtOfsDeleteIndex( IrpContext, TestFcb, AdScb );
  2483. }
  2484. DbgPrint("NtOfs closing index:\n");
  2485. NtOfsCloseIndex( IrpContext, AdScb );
  2486. DbgPrint("NtOfs test complete!\n\n");
  2487. return;
  2488. //
  2489. // Make sure these at least compile until we have some real callers.
  2490. //
  2491. {
  2492. MAP_HANDLE M;
  2493. PVOID B;
  2494. LONGLONG O;
  2495. ULONG L;
  2496. LSN Lsn;
  2497. NtOfsInitializeMapHandle( &M );
  2498. NtOfsMapAttribute( IrpContext, AdScb, O, L, &B, &M );
  2499. NtOfsPreparePinWrite( IrpContext, AdScb, O, L, &B, &M );
  2500. NtOfsPinRead( IrpContext, AdScb, O, L, &M );
  2501. NtOfsDirty( IrpContext, &M, &Lsn );
  2502. NtOfsReleaseMap( IrpContext, &M );
  2503. NtOfsPutData( IrpContext, AdScb, O, L, &B );
  2504. }
  2505. }
  2506. #endif TOMM