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

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