2294 lines
65 KiB
C
2294 lines
65 KiB
C
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
ViewSup.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the Index management routines for NtOfs
|
||
|
||
Author:
|
||
|
||
Tom Miller [TomM] 5-Jan-1996
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "NtfsProc.h"
|
||
#include "Index.h"
|
||
|
||
//
|
||
// Define a tag for general pool allocations from this module
|
||
//
|
||
|
||
#undef MODULE_POOL_TAG
|
||
#define MODULE_POOL_TAG ('vFtN')
|
||
|
||
//
|
||
// Temporary definitions for test
|
||
//
|
||
|
||
BOOLEAN NtOfsDoIndexTest = TRUE;
|
||
BOOLEAN NtOfsLeaveTestIndex = FALSE;
|
||
extern ATTRIBUTE_DEFINITION_COLUMNS NtfsAttributeDefinitions[];
|
||
|
||
//
|
||
// Define a context for NtOfsReadRecords, which is primarily an IndexContext
|
||
// and a copy of the last Key returned.
|
||
//
|
||
|
||
typedef struct _READ_CONTEXT {
|
||
|
||
//
|
||
// IndexContext (cursor) for the enumeration.
|
||
//
|
||
|
||
INDEX_CONTEXT IndexContext;
|
||
|
||
//
|
||
// The last key returned is allocated from paged pool. We have to
|
||
// separately record how much is allocated, and how long the current
|
||
// key is using, the latter being in the KeyLength field of IndexKey.
|
||
// SmallKeyBuffer will store a small key in this structure without going
|
||
// to pool.
|
||
//
|
||
|
||
INDEX_KEY LastReturnedKey;
|
||
ULONG AllocatedKeyLength;
|
||
ULONG SmallKeyBuffer[3];
|
||
|
||
} READ_CONTEXT, *PREAD_CONTEXT;
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, NtOfsCreateIndex)
|
||
#pragma alloc_text(PAGE, NtOfsCloseIndex)
|
||
#pragma alloc_text(PAGE, NtOfsDeleteIndex)
|
||
#pragma alloc_text(PAGE, NtOfsFindRecord)
|
||
#pragma alloc_text(PAGE, NtOfsAddRecords)
|
||
#pragma alloc_text(PAGE, NtOfsDeleteRecords)
|
||
#pragma alloc_text(PAGE, NtOfsUpdateRecord)
|
||
#pragma alloc_text(PAGE, NtOfsReadRecords)
|
||
#pragma alloc_text(PAGE, NtOfsFreeReadContext)
|
||
#pragma alloc_text(PAGE, NtOfsCollateUlong)
|
||
#pragma alloc_text(PAGE, NtOfsCollateUnicode)
|
||
#pragma alloc_text(PAGE, NtOfsMatchAll)
|
||
#pragma alloc_text(PAGE, NtOfsMatchUlongExact)
|
||
#pragma alloc_text(PAGE, NtOfsMatchUnicodeExpression)
|
||
#pragma alloc_text(PAGE, NtOfsMatchUnicodeString)
|
||
#endif
|
||
|
||
|
||
NTFSAPI
|
||
NTSTATUS
|
||
NtOfsCreateIndex (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN UNICODE_STRING Name,
|
||
IN CREATE_OPTIONS CreateOptions,
|
||
IN ULONG DeleteCollationData,
|
||
IN PCOLLATION_FUNCTION CollationFunction,
|
||
IN PVOID CollationData OPTIONAL,
|
||
OUT PSCB *Scb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine may be called to create / open a view index
|
||
within a given file for a given CollationRule.
|
||
|
||
Arguments:
|
||
|
||
Fcb - File in which the index is to be created.
|
||
|
||
Name - Name of the index for all related Scbs and attributes on disk.
|
||
|
||
CreateOptions - Standard create flags.
|
||
|
||
DeleteCollationData - Specifies 1 if the NtfsFreePool should be called
|
||
for CollationData when no longer required, or 0
|
||
if NtfsFreePool should never be called.
|
||
|
||
CollationFunction - Function to be called to collate the index.
|
||
|
||
CollationData - Data pointer to be passed to CollationFunction.
|
||
|
||
Scb - Returns an Scb as handle for the index.
|
||
|
||
Return Value:
|
||
|
||
STATUS_OBJECT_NAME_COLLISION -- if CreateNew and index already exists
|
||
STATUS_OBJECT_NAME_NOT_FOUND -- if OpenExisting and index does not exist
|
||
|
||
--*/
|
||
|
||
{
|
||
ATTRIBUTE_ENUMERATION_CONTEXT LocalContext;
|
||
ULONG idx;
|
||
BOOLEAN FoundAttribute;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
struct {
|
||
INDEX_ROOT IndexRoot;
|
||
INDEX_ENTRY EndEntry;
|
||
} R;
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// First we will initialize the Index Root structure which is the value
|
||
// of the attribute we need to create.
|
||
//
|
||
|
||
RtlZeroMemory( &R, sizeof(R) );
|
||
|
||
R.IndexRoot.BytesPerIndexBuffer = NTOFS_VIEW_INDEX_BUFFER_SIZE;
|
||
|
||
R.IndexRoot.BlocksPerIndexBuffer = (UCHAR)ClustersFromBytes( Fcb->Vcb,
|
||
NTOFS_VIEW_INDEX_BUFFER_SIZE );
|
||
|
||
if (NTOFS_VIEW_INDEX_BUFFER_SIZE < Fcb->Vcb->BytesPerCluster) {
|
||
|
||
R.IndexRoot.BlocksPerIndexBuffer = NTOFS_VIEW_INDEX_BUFFER_SIZE / DEFAULT_INDEX_BLOCK_SIZE;
|
||
}
|
||
|
||
R.IndexRoot.IndexHeader.FirstIndexEntry = QuadAlign(sizeof(INDEX_HEADER));
|
||
R.IndexRoot.IndexHeader.FirstFreeByte =
|
||
R.IndexRoot.IndexHeader.BytesAvailable = QuadAlign(sizeof(INDEX_HEADER)) +
|
||
QuadAlign(sizeof(INDEX_ENTRY));
|
||
|
||
//
|
||
// Now we need to put in the special End entry.
|
||
//
|
||
|
||
R.EndEntry.Length = sizeof(INDEX_ENTRY);
|
||
SetFlag( R.EndEntry.Flags, INDEX_ENTRY_END );
|
||
|
||
|
||
//
|
||
// Now, just create the Index Root Attribute.
|
||
//
|
||
|
||
NtfsInitializeAttributeContext( &LocalContext );
|
||
|
||
NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, FALSE, FALSE );
|
||
|
||
try {
|
||
|
||
//
|
||
// First see if the index already exists, by searching for the root
|
||
// attribute.
|
||
//
|
||
|
||
FoundAttribute = NtfsLookupAttributeByName( IrpContext,
|
||
Fcb,
|
||
&Fcb->FileReference,
|
||
$INDEX_ROOT,
|
||
&Name,
|
||
NULL,
|
||
TRUE,
|
||
&LocalContext );
|
||
|
||
//
|
||
// If it is not there, and the CreateOptions allow, then let's create
|
||
// the index root now. (First cleaning up the attribute context from
|
||
// the lookup).
|
||
//
|
||
|
||
if (!FoundAttribute && (CreateOptions <= CREATE_OR_OPEN)) {
|
||
|
||
NtfsCleanupAttributeContext( &LocalContext );
|
||
|
||
NtfsCreateAttributeWithValue( IrpContext,
|
||
Fcb,
|
||
$INDEX_ROOT,
|
||
&Name,
|
||
&R,
|
||
sizeof(R),
|
||
0,
|
||
NULL,
|
||
TRUE,
|
||
&LocalContext );
|
||
|
||
//
|
||
// If the index is already there, and we were asked to create it, then
|
||
// return an error.
|
||
//
|
||
|
||
} else if (FoundAttribute && (CreateOptions == CREATE_NEW)) {
|
||
|
||
try_return( Status = STATUS_OBJECT_NAME_COLLISION );
|
||
|
||
//
|
||
// If the index is not there, and we were supposed to open existing, then
|
||
// return an error.
|
||
//
|
||
|
||
} else if (!FoundAttribute && (CreateOptions == OPEN_EXISTING)) {
|
||
|
||
try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND );
|
||
}
|
||
|
||
//
|
||
// Otherwise create/find the Scb and reference it.
|
||
//
|
||
|
||
*Scb = NtfsCreateScb( IrpContext, Fcb, $INDEX_ALLOCATION, &Name, FALSE, NULL );
|
||
SetFlag( (*Scb)->ScbState, SCB_STATE_VIEW_INDEX );
|
||
(*Scb)->ScbType.Index.CollationFunction = CollationFunction;
|
||
|
||
//
|
||
// Handle the case where CollationData is to be deleted.
|
||
//
|
||
|
||
if (DeleteCollationData) {
|
||
SetFlag((*Scb)->ScbState, SCB_STATE_DELETE_COLLATION_DATA);
|
||
if ((*Scb)->ScbType.Index.CollationData != NULL) {
|
||
NtfsFreePool(CollationData);
|
||
} else {
|
||
(*Scb)->ScbType.Index.CollationData = CollationData;
|
||
}
|
||
|
||
//
|
||
// Otherwise just jam the pointer the caller passed.
|
||
//
|
||
|
||
} else {
|
||
(*Scb)->ScbType.Index.CollationData = CollationData;
|
||
}
|
||
|
||
NtfsIncrementCloseCounts( *Scb, TRUE, FALSE );
|
||
|
||
try_exit: NOTHING;
|
||
|
||
} finally {
|
||
NtfsCleanupAttributeContext( &LocalContext );
|
||
|
||
NtfsReleaseFcb( IrpContext, Fcb );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTFSAPI
|
||
VOID
|
||
NtOfsCloseIndex (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PSCB Scb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine may be called to close a previously returned handle on a view index.
|
||
|
||
Arguments:
|
||
|
||
Scb - Supplies an Scb as the previously returned handle for this index.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NtfsAcquireExclusiveFcb( IrpContext, Scb->Fcb, NULL, TRUE, FALSE );
|
||
NtfsDecrementCloseCounts( IrpContext, Scb, NULL, TRUE, FALSE, FALSE );
|
||
NtfsReleaseFcb( IrpContext, Scb->Fcb );
|
||
}
|
||
|
||
|
||
NTFSAPI
|
||
VOID
|
||
NtOfsDeleteIndex (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PSCB Scb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine may be called to delete an index.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Supplies an Fcb as the previously returned object handle for the file
|
||
|
||
Scb - Supplies an Scb as the previously returned handle for this index.
|
||
|
||
Return Value:
|
||
|
||
None (Deleting a nonexistant index is benign).
|
||
|
||
--*/
|
||
|
||
{
|
||
ATTRIBUTE_ENUMERATION_CONTEXT LocalContext;
|
||
ATTRIBUTE_TYPE_CODE AttributeTypeCode;
|
||
BOOLEAN FoundAttribute;
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
ASSERT(($BITMAP - $INDEX_ALLOCATION) == ($INDEX_ALLOCATION - $INDEX_ROOT));
|
||
|
||
PAGED_CODE();
|
||
|
||
NtfsAcquireExclusiveScb( IrpContext, Scb );
|
||
|
||
try {
|
||
|
||
//
|
||
// First see if there is some index allocation, and if so truncate it
|
||
// away allowing this operation to be broken up.
|
||
//
|
||
|
||
NtfsInitializeAttributeContext( &LocalContext );
|
||
|
||
if (NtfsLookupAttributeByName( IrpContext,
|
||
Fcb,
|
||
&Fcb->FileReference,
|
||
$INDEX_ALLOCATION,
|
||
&Scb->AttributeName,
|
||
NULL,
|
||
FALSE,
|
||
&LocalContext )) {
|
||
|
||
NtfsCreateInternalAttributeStream( IrpContext, Scb, TRUE );
|
||
|
||
NtfsDeleteAllocation( IrpContext, NULL, Scb, 0, MAXLONGLONG, TRUE, TRUE );
|
||
}
|
||
|
||
NtfsCleanupAttributeContext( &LocalContext );
|
||
|
||
for (AttributeTypeCode = $INDEX_ROOT;
|
||
AttributeTypeCode <= $BITMAP;
|
||
AttributeTypeCode += ($INDEX_ALLOCATION - $INDEX_ROOT)) {
|
||
|
||
//
|
||
// Initialize the attribute context on each trip through the loop.
|
||
//
|
||
|
||
NtfsInitializeAttributeContext( &LocalContext );
|
||
|
||
//
|
||
// First see if the index already exists, by searching for the root
|
||
// attribute.
|
||
//
|
||
|
||
FoundAttribute = NtfsLookupAttributeByName( IrpContext,
|
||
Fcb,
|
||
&Fcb->FileReference,
|
||
AttributeTypeCode,
|
||
&Scb->AttributeName,
|
||
NULL,
|
||
TRUE,
|
||
&LocalContext );
|
||
|
||
//
|
||
// Loop while we see the right records.
|
||
//
|
||
|
||
while (FoundAttribute) {
|
||
|
||
NtfsDeleteAttributeRecord( IrpContext, Fcb, TRUE, FALSE, &LocalContext );
|
||
|
||
FoundAttribute = NtfsLookupNextAttributeByName( IrpContext,
|
||
Fcb,
|
||
AttributeTypeCode,
|
||
&Scb->AttributeName,
|
||
TRUE,
|
||
&LocalContext );
|
||
}
|
||
|
||
NtfsCleanupAttributeContext( &LocalContext );
|
||
}
|
||
|
||
SetFlag( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
|
||
|
||
} finally {
|
||
|
||
NtfsCleanupAttributeContext( &LocalContext );
|
||
|
||
NtfsReleaseScb( IrpContext, Scb );
|
||
}
|
||
}
|
||
|
||
|
||
NTFSAPI
|
||
NTSTATUS
|
||
NtOfsFindRecord (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PSCB Scb,
|
||
IN PINDEX_KEY IndexKey,
|
||
OUT PINDEX_ROW IndexRow,
|
||
OUT PMAP_HANDLE MapHandle,
|
||
IN OUT PQUICK_INDEX_HINT QuickIndexHint OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine may be called to find the first occurrence of a key in an index,
|
||
and return cached information which may can accelerate the update on the data
|
||
for that key if the index buffer is not changed.
|
||
|
||
Arguments:
|
||
|
||
Scb - Supplies an Scb as the previously returned handle for this index.
|
||
|
||
IndexKey - Supplies the key to find.
|
||
|
||
IndexRow - Returns a description of the Key and Data *in place*, for read-only
|
||
access, valid only until the Bcb is unpinned. (Neither key nor
|
||
data may be modified in place!)
|
||
|
||
MapHandle - Returns a map handle for accessing the key and data directly.
|
||
|
||
QuickIndexHint - Supplies a previously returned hint, or all zeros on first use.
|
||
Returns location information which may be held an arbitrary
|
||
amount of time, which can accelerate a subsequent call to
|
||
NtOfsUpdateRecord for the data in this key, iff changes to
|
||
the index do not prohibit use of this hint.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS -- if operation was successful.
|
||
STATUS_NO_MATCH -- if the specified key does not exist.
|
||
|
||
--*/
|
||
|
||
{
|
||
INDEX_CONTEXT IndexContext;
|
||
PINDEX_LOOKUP_STACK Sp;
|
||
PINDEX_ENTRY IndexEntry;
|
||
NTSTATUS Status;
|
||
PQUICK_INDEX QuickIndex = (PQUICK_INDEX)QuickIndexHint;
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_SCB( Scb );
|
||
|
||
PAGED_CODE();
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
NtfsInitializeIndexContext( &IndexContext );
|
||
|
||
ASSERT_SHARED_SCB( Scb );
|
||
|
||
try {
|
||
|
||
//
|
||
// Use the second location in the index context to perform the
|
||
// read.
|
||
//
|
||
|
||
Sp =
|
||
IndexContext.Current = IndexContext.Base + 1;
|
||
|
||
//
|
||
// If the index entry for this filename hasn't moved we can go
|
||
// directly to the location in the buffer. For this to be the case the
|
||
// following must be true.
|
||
//
|
||
// - The entry must already be in an index buffer (BufferOffset test)
|
||
// - The index stream may not have been truncated (ChangeCount test)
|
||
// - The Lsn in the page can't have changed
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( QuickIndexHint ) &&
|
||
(QuickIndex->BufferOffset != 0) &&
|
||
(QuickIndex->ChangeCount == Scb->ScbType.Index.ChangeCount)) {
|
||
|
||
ReadIndexBuffer( IrpContext,
|
||
Scb,
|
||
QuickIndex->IndexBlock,
|
||
FALSE,
|
||
Sp );
|
||
|
||
//
|
||
// If the Lsn matches then we can use this buffer directly.
|
||
//
|
||
|
||
if (QuickIndex->CapturedLsn.QuadPart == Sp->CapturedLsn.QuadPart) {
|
||
|
||
Sp->IndexEntry = (PINDEX_ENTRY) Add2Ptr( Sp->StartOfBuffer,
|
||
QuickIndex->BufferOffset );
|
||
|
||
//
|
||
// Otherwise we need to reinitialize the index context and take
|
||
// the long path below.
|
||
//
|
||
|
||
} else {
|
||
|
||
NtfsCleanupIndexContext( IrpContext, &IndexContext );
|
||
NtfsInitializeIndexContext( &IndexContext );
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we did not get the index entry via the hint, get it now.
|
||
//
|
||
|
||
if (Sp->Bcb == NULL) {
|
||
|
||
//
|
||
// Position to first possible match.
|
||
//
|
||
|
||
FindFirstIndexEntry( IrpContext,
|
||
Scb,
|
||
IndexKey,
|
||
&IndexContext );
|
||
|
||
//
|
||
// See if there is an actual match.
|
||
//
|
||
|
||
if (!FindNextIndexEntry( IrpContext,
|
||
Scb,
|
||
IndexKey,
|
||
FALSE,
|
||
FALSE,
|
||
&IndexContext,
|
||
FALSE,
|
||
NULL )) {
|
||
|
||
try_return( Status = STATUS_NO_MATCH );
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we found the key in the base, then return the Bcb from the
|
||
// attribute context and return no hint (BufferOffset = 0).
|
||
//
|
||
|
||
if (IndexContext.Current == IndexContext.Base) {
|
||
|
||
MapHandle->Buffer = NULL;
|
||
MapHandle->Bcb = NtfsFoundBcb(&IndexContext.AttributeContext);
|
||
NtfsFoundBcb(&IndexContext.AttributeContext) = NULL;
|
||
|
||
if (ARGUMENT_PRESENT( QuickIndexHint )) {
|
||
QuickIndex->BufferOffset = 0;
|
||
}
|
||
|
||
//
|
||
// If we found the key in an index buffer, then return the Bcb from
|
||
// the lookup stack, and record the hint for the caller.
|
||
//
|
||
|
||
} else {
|
||
|
||
Sp = IndexContext.Current;
|
||
|
||
MapHandle->Buffer = Sp->StartOfBuffer;
|
||
MapHandle->Bcb = Sp->Bcb;
|
||
Sp->Bcb = NULL;
|
||
|
||
if (ARGUMENT_PRESENT( QuickIndexHint )) {
|
||
QuickIndex->ChangeCount = Scb->ScbType.Index.ChangeCount;
|
||
QuickIndex->BufferOffset = PtrOffset( Sp->StartOfBuffer, Sp->IndexEntry );
|
||
QuickIndex->CapturedLsn = ((PINDEX_ALLOCATION_BUFFER) Sp->StartOfBuffer)->Lsn;
|
||
QuickIndex->IndexBlock = ((PINDEX_ALLOCATION_BUFFER) Sp->StartOfBuffer)->ThisBlock;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Complete the MapHandle to disallow pinning.
|
||
//
|
||
|
||
MapHandle->FileOffset = MAXLONGLONG;
|
||
MapHandle->Length = MAXULONG;
|
||
|
||
//
|
||
// Return the IndexRow described directly in the buffer.
|
||
//
|
||
|
||
IndexEntry = IndexContext.Current->IndexEntry;
|
||
IndexRow->KeyPart.Key = (IndexEntry + 1);
|
||
IndexRow->KeyPart.KeyLength = IndexEntry->AttributeLength;
|
||
IndexRow->DataPart.Data = Add2Ptr( IndexEntry, IndexEntry->DataOffset );
|
||
IndexRow->DataPart.DataLength = IndexEntry->DataLength;
|
||
|
||
try_exit: NOTHING;
|
||
|
||
} finally {
|
||
|
||
NtfsCleanupIndexContext( IrpContext, &IndexContext );
|
||
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTFSAPI
|
||
NTSTATUS
|
||
NtOfsFindLastRecord (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PSCB Scb,
|
||
IN PINDEX_KEY MaxIndexKey,
|
||
OUT PINDEX_ROW IndexRow,
|
||
OUT PMAP_HANDLE MapHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine may be called to find the highest key in an index.
|
||
|
||
Arguments:
|
||
|
||
Scb - Supplies an Scb as the previously returned handle for this index.
|
||
|
||
MaxIndexKey - Supplies the maximum possible key value (such as MAXULONG, etc.),
|
||
and this key must not actually be in use!
|
||
|
||
IndexRow - Returns a description of the Key and Data *in place*, for read-only
|
||
access, valid only until the Bcb is unpinned. (Neither key nor
|
||
data may be modified in place!)
|
||
|
||
MapHandle - Returns a map handle for accessing the key and data directly.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS -- if operation was successful.
|
||
STATUS_NO_MATCH -- if the specified key does not exist (index is empty).
|
||
|
||
--*/
|
||
|
||
{
|
||
INDEX_CONTEXT IndexContext;
|
||
PINDEX_LOOKUP_STACK Sp;
|
||
PINDEX_ENTRY IndexEntry, NextIndexEntry;
|
||
NTSTATUS Status;
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_SCB( Scb );
|
||
|
||
PAGED_CODE();
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
NtfsInitializeIndexContext( &IndexContext );
|
||
|
||
NtfsAcquireSharedScb( IrpContext, Scb );
|
||
|
||
try {
|
||
|
||
//
|
||
// Slide down the "right" side of the tree.
|
||
//
|
||
|
||
FindFirstIndexEntry( IrpContext,
|
||
Scb,
|
||
MaxIndexKey,
|
||
&IndexContext );
|
||
|
||
//
|
||
// If this happens, the index must be empty.
|
||
//
|
||
|
||
Sp = IndexContext.Current;
|
||
IndexEntry = NtfsFirstIndexEntry(Sp->IndexHeader);
|
||
if (FlagOn(IndexEntry->Flags, INDEX_ENTRY_END)) {
|
||
try_return( Status = STATUS_NO_MATCH );
|
||
}
|
||
|
||
//
|
||
// If we found the key in the base, then return the Bcb from the
|
||
// attribute context and return no hint (BufferOffset = 0).
|
||
//
|
||
|
||
if (IndexContext.Current == IndexContext.Base) {
|
||
|
||
MapHandle->Bcb = NtfsFoundBcb(&IndexContext.AttributeContext);
|
||
NtfsFoundBcb(&IndexContext.AttributeContext) = NULL;
|
||
|
||
//
|
||
// If we found the key in an index buffer, then return the Bcb from
|
||
// the lookup stack, and record the hint for the caller.
|
||
//
|
||
|
||
} else {
|
||
|
||
|
||
MapHandle->Bcb = Sp->Bcb;
|
||
Sp->Bcb = NULL;
|
||
}
|
||
|
||
//
|
||
// Complete the MapHandle to disallow pinning.
|
||
//
|
||
|
||
MapHandle->FileOffset = MAXLONGLONG;
|
||
MapHandle->Length = MAXULONG;
|
||
MapHandle->Buffer = NULL;
|
||
|
||
//
|
||
// Now rescan the last buffer to return the second to last index entry,
|
||
// if there is one.
|
||
//
|
||
|
||
NextIndexEntry = IndexEntry;
|
||
do {
|
||
IndexEntry = NextIndexEntry;
|
||
NextIndexEntry = NtfsNextIndexEntry(IndexEntry);
|
||
} while (!FlagOn(NextIndexEntry->Flags, INDEX_ENTRY_END));
|
||
|
||
//
|
||
// Return the IndexRow described directly in the buffer.
|
||
//
|
||
|
||
IndexRow->KeyPart.Key = (IndexEntry + 1);
|
||
IndexRow->KeyPart.KeyLength = IndexEntry->AttributeLength;
|
||
IndexRow->DataPart.Data = Add2Ptr( IndexEntry, IndexEntry->DataOffset );
|
||
IndexRow->DataPart.DataLength = IndexEntry->DataLength;
|
||
|
||
try_exit: NOTHING;
|
||
|
||
} finally {
|
||
|
||
NtfsCleanupIndexContext( IrpContext, &IndexContext );
|
||
|
||
NtfsReleaseScb( IrpContext, Scb );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTFSAPI
|
||
VOID
|
||
NtOfsAddRecords (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PSCB Scb,
|
||
IN ULONG Count,
|
||
IN PINDEX_ROW IndexRow,
|
||
IN ULONG SequentialInsertMode
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine may be called to add one or more records to an index.
|
||
|
||
If SequentialInsertMode is nonzero, this is a hint to the index package
|
||
to keep all BTree buffers as full as possible, by splitting as close to
|
||
the end of the buffer as possible. If specified as zero, random inserts
|
||
are assumed, and buffers are always split in the middle for better balance.
|
||
|
||
|
||
Arguments:
|
||
|
||
Scb - Supplies an Scb as the previously returned handle for this index.
|
||
|
||
Count - Supplies the number of records being added.
|
||
|
||
IndexRow - Supplies an array of Count entries, containing the Keys and Data to add.
|
||
|
||
SequentialInsertMode - If specified as nozero, the implementation may choose to
|
||
split all index buffers at the end for maximum fill.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Raises:
|
||
|
||
STATUS_DUPLICATE_NAME -- if the specified key already exists.
|
||
|
||
--*/
|
||
|
||
{
|
||
INDEX_CONTEXT IndexContext;
|
||
struct {
|
||
INDEX_ENTRY IndexEntry;
|
||
PVOID Key;
|
||
PVOID Data;
|
||
} IE;
|
||
ULONG i;
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_SCB( Scb );
|
||
|
||
UNREFERENCED_PARAMETER(SequentialInsertMode);
|
||
|
||
PAGED_CODE();
|
||
|
||
NtfsAcquireExclusiveScb( IrpContext, Scb );
|
||
|
||
try {
|
||
|
||
//
|
||
// Loop to add all entries
|
||
//
|
||
|
||
for (i = 0; i < Count; i++) {
|
||
|
||
NtfsInitializeIndexContext( &IndexContext );
|
||
|
||
//
|
||
// Position to first possible match.
|
||
//
|
||
|
||
FindFirstIndexEntry( IrpContext,
|
||
Scb,
|
||
&IndexRow->KeyPart,
|
||
&IndexContext );
|
||
|
||
//
|
||
// See if there is an actual match.
|
||
//
|
||
|
||
if (FindNextIndexEntry( IrpContext,
|
||
Scb,
|
||
&IndexRow->KeyPart,
|
||
FALSE,
|
||
FALSE,
|
||
&IndexContext,
|
||
FALSE,
|
||
NULL )) {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_DUPLICATE_NAME, NULL, NULL );
|
||
}
|
||
|
||
//
|
||
// Initialize the Index Entry in pointer form.
|
||
//
|
||
// Note that the final index entry ends up looking like this:
|
||
//
|
||
// (IndexEntry)(Key)(Data)
|
||
//
|
||
// where all fields are long-aligned and:
|
||
//
|
||
// Key is at IndexEntry + sizeof(INDEX_ENTRY), and of length AttributeLength
|
||
// Data is at IndexEntry + DataOffset and of length DataLength
|
||
//
|
||
|
||
IE.IndexEntry.AttributeLength = (USHORT)IndexRow->KeyPart.KeyLength;
|
||
|
||
IE.IndexEntry.DataOffset = (USHORT)(sizeof(INDEX_ENTRY) +
|
||
LongAlign(IndexRow->KeyPart.KeyLength));
|
||
|
||
IE.IndexEntry.DataLength = (USHORT)IndexRow->DataPart.DataLength;
|
||
IE.IndexEntry.ReservedForZero = 0;
|
||
|
||
IE.IndexEntry.Length = (USHORT)(QuadAlign(IE.IndexEntry.DataOffset +
|
||
IndexRow->DataPart.DataLength));
|
||
|
||
IE.IndexEntry.Flags = INDEX_ENTRY_POINTER_FORM;
|
||
IE.IndexEntry.Reserved = 0;
|
||
IE.Key = IndexRow->KeyPart.Key;
|
||
IE.Data = IndexRow->DataPart.Data;
|
||
|
||
//
|
||
// Now add it to the index. We can only add to a leaf, so force our
|
||
// position back to the correct spot in a leaf first.
|
||
//
|
||
|
||
IndexContext.Current = IndexContext.Top;
|
||
AddToIndex( IrpContext, Scb, (PINDEX_ENTRY)&IE, &IndexContext, NULL );
|
||
NtfsCleanupIndexContext( IrpContext, &IndexContext );
|
||
IndexRow += 1;
|
||
}
|
||
|
||
} finally {
|
||
|
||
NtfsCleanupIndexContext( IrpContext, &IndexContext );
|
||
|
||
NtfsReleaseScb( IrpContext, Scb );
|
||
}
|
||
}
|
||
|
||
|
||
NTFSAPI
|
||
VOID
|
||
NtOfsDeleteRecords (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PSCB Scb,
|
||
IN ULONG Count,
|
||
IN PINDEX_KEY IndexKey
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine may be called to delete one or more records from an index.
|
||
|
||
Arguments:
|
||
|
||
Scb - Supplies an Scb as the previously returned handle for this index.
|
||
|
||
Count - Supplies the number of records being deleted.
|
||
|
||
IndexKey - Supplies an array of Count entries, containing the Keys to be deleted.
|
||
|
||
Return Value:
|
||
|
||
None. (This call is benign if any records do not exist.)
|
||
|
||
--*/
|
||
|
||
{
|
||
INDEX_CONTEXT IndexContext;
|
||
ULONG i;
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_SCB( Scb );
|
||
|
||
PAGED_CODE();
|
||
|
||
NtfsAcquireExclusiveScb( IrpContext, Scb );
|
||
|
||
try {
|
||
|
||
//
|
||
// Loop to add all entries
|
||
//
|
||
|
||
for (i = 0; i < Count; i++) {
|
||
|
||
NtfsInitializeIndexContext( &IndexContext );
|
||
|
||
//
|
||
// Position to first possible match.
|
||
//
|
||
|
||
FindFirstIndexEntry( IrpContext,
|
||
Scb,
|
||
IndexKey,
|
||
&IndexContext );
|
||
|
||
//
|
||
// See if there is an actual match.
|
||
//
|
||
|
||
if (FindNextIndexEntry( IrpContext,
|
||
Scb,
|
||
IndexKey,
|
||
FALSE,
|
||
FALSE,
|
||
&IndexContext,
|
||
FALSE,
|
||
NULL )) {
|
||
|
||
//
|
||
// Delete it.
|
||
//
|
||
|
||
DeleteFromIndex( IrpContext, Scb, &IndexContext );
|
||
}
|
||
|
||
NtfsCleanupIndexContext( IrpContext, &IndexContext );
|
||
IndexKey += 1;
|
||
}
|
||
|
||
} finally {
|
||
|
||
NtfsCleanupIndexContext( IrpContext, &IndexContext );
|
||
|
||
NtfsReleaseScb( IrpContext, Scb );
|
||
}
|
||
}
|
||
|
||
|
||
NTFSAPI
|
||
VOID
|
||
NtOfsUpdateRecord (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PSCB Scb,
|
||
IN ULONG Count,
|
||
IN PINDEX_ROW IndexRow,
|
||
IN OUT PQUICK_INDEX_HINT QuickIndexHint OPTIONAL,
|
||
IN OUT PMAP_HANDLE MapHandle OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine may be called to update the data portion of a record in an index.
|
||
|
||
If QuickIndexHint is specified, then the update may occur by directly accessing
|
||
the buffer containing the specified key, iff other changes to the index do not
|
||
prevent that. If changes prevent the quick update, then the record is looked
|
||
up by key in order to perform the data update.
|
||
|
||
Arguments:
|
||
|
||
Scb - Supplies an Scb as the previously returned handle for this index.
|
||
|
||
Count - Supplies the count of updates described in IndexRow. For counts
|
||
greater than 1, QuickIndexHint and MapHandle must not be supplied.
|
||
|
||
IndexRow - Supplies the key to be updated and the new data for that key.
|
||
|
||
QuickIndexHint - Supplies a optional quick index for this row returned from a previous
|
||
call to NtOfsFindRecord, updated on return.
|
||
|
||
MapHandle - Supplies an optional MapHandle to accompany the QuickIndex. If MapHandle
|
||
is supplied, then the QuickIndexHint must be guaranteed valid. MapHandle
|
||
is updated (pinned) on return.
|
||
|
||
MapHandle is ignored if QuickIndexHint is not specified.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Raises:
|
||
|
||
STATUS_INFO_LENGTH_MISMATCH -- if the specified data is a different length from the
|
||
data in the key.
|
||
STATUS_NO_MATCH -- if the specified key does not exist.
|
||
|
||
--*/
|
||
|
||
{
|
||
INDEX_CONTEXT IndexContext;
|
||
PQUICK_INDEX QuickIndex = (PQUICK_INDEX)QuickIndexHint;
|
||
PVOID DataInIndex;
|
||
PINDEX_ENTRY IndexEntry;
|
||
PVCB Vcb = Scb->Vcb;
|
||
PINDEX_LOOKUP_STACK Sp;
|
||
PINDEX_ALLOCATION_BUFFER IndexBuffer;
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_SCB( Scb );
|
||
ASSERT_SHARED_SCB( Scb );
|
||
|
||
ASSERT(Count != 0);
|
||
|
||
PAGED_CODE();
|
||
|
||
try {
|
||
|
||
//
|
||
// If the index entry for this filename hasn't moved we can go
|
||
// directly to the location in the buffer. For this to be the case the
|
||
// following must be true.
|
||
//
|
||
// - The entry must already be in an index buffer (BufferOffset test)
|
||
// - The index stream may not have been truncated (ChangeCount test)
|
||
// - The Lsn in the page can't have changed
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( QuickIndexHint ) &&
|
||
(QuickIndex->BufferOffset != 0) &&
|
||
(QuickIndex->ChangeCount == Scb->ScbType.Index.ChangeCount)) {
|
||
|
||
ASSERT(Count == 1);
|
||
|
||
NtfsInitializeIndexContext( &IndexContext );
|
||
|
||
//
|
||
// Use the top location in the index context to perform the
|
||
// read.
|
||
//
|
||
|
||
Sp = IndexContext.Base;
|
||
|
||
//
|
||
// If we have a MapHandle already, we do not need to read the
|
||
// IndexBuffer.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(MapHandle)) {
|
||
|
||
IndexBuffer = MapHandle->Buffer;
|
||
Sp->Bcb = MapHandle->Bcb;
|
||
MapHandle->Bcb = NULL;
|
||
Sp->CapturedLsn.QuadPart = QuickIndex->CapturedLsn.QuadPart;
|
||
|
||
} else {
|
||
|
||
ReadIndexBuffer( IrpContext,
|
||
Scb,
|
||
QuickIndex->IndexBlock,
|
||
FALSE,
|
||
Sp );
|
||
|
||
IndexBuffer = Sp->StartOfBuffer;
|
||
}
|
||
|
||
//
|
||
// If the Lsn matches then we can use this buffer directly.
|
||
//
|
||
|
||
if (QuickIndex->CapturedLsn.QuadPart == Sp->CapturedLsn.QuadPart) {
|
||
|
||
IndexEntry = (PINDEX_ENTRY) Add2Ptr( IndexBuffer, QuickIndex->BufferOffset );
|
||
|
||
if (IndexEntry->DataLength < IndexRow->DataPart.DataLength) {
|
||
NtfsRaiseStatus( IrpContext, STATUS_INFO_LENGTH_MISMATCH, NULL, NULL );
|
||
}
|
||
|
||
DataInIndex = Add2Ptr( IndexEntry, IndexEntry->DataOffset );
|
||
|
||
//
|
||
// Pin the index buffer
|
||
//
|
||
|
||
NtfsPinMappedData( IrpContext,
|
||
Scb,
|
||
LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
|
||
Scb->ScbType.Index.BytesPerIndexBuffer,
|
||
&Sp->Bcb );
|
||
|
||
//
|
||
// Write a log record to change our ParentIndexEntry.
|
||
//
|
||
|
||
//
|
||
// Write the log record, but do not update the IndexBuffer Lsn,
|
||
// since nothing moved and we don't want to force index contexts
|
||
// to have to rescan.
|
||
//
|
||
// Indexbuffer->Lsn =
|
||
//
|
||
|
||
// ASSERT(Scb->ScbType.Index.ClustersPerIndexBuffer != 0);
|
||
|
||
NtfsWriteLog( IrpContext,
|
||
Scb,
|
||
Sp->Bcb,
|
||
UpdateRecordDataAllocation,
|
||
IndexRow->DataPart.Data,
|
||
IndexRow->DataPart.DataLength,
|
||
UpdateRecordDataAllocation,
|
||
DataInIndex,
|
||
IndexRow->DataPart.DataLength,
|
||
LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
|
||
0,
|
||
QuickIndex->BufferOffset,
|
||
Scb->ScbType.Index.BytesPerIndexBuffer );
|
||
|
||
//
|
||
// Now call the Restart routine to do it.
|
||
//
|
||
|
||
NtOfsRestartUpdateDataInIndex( IndexEntry,
|
||
IndexRow->DataPart.Data,
|
||
IndexRow->DataPart.DataLength );
|
||
|
||
//
|
||
// If there is a MapHandle, we must update the Bcb pointer.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(MapHandle)) {
|
||
|
||
MapHandle->Bcb = Sp->Bcb;
|
||
Sp->Bcb = NULL;
|
||
}
|
||
|
||
try_return( NOTHING );
|
||
|
||
//
|
||
// Otherwise we need to unpin the Bcb and take
|
||
// the long path below.
|
||
//
|
||
|
||
} else {
|
||
|
||
ASSERT(!ARGUMENT_PRESENT(MapHandle));
|
||
NtfsUnpinBcb( &Sp->Bcb );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Loop to apply all updates.
|
||
//
|
||
|
||
do {
|
||
|
||
NtfsInitializeIndexContext( &IndexContext );
|
||
|
||
//
|
||
// Position to first possible match.
|
||
//
|
||
|
||
FindFirstIndexEntry( IrpContext,
|
||
Scb,
|
||
&IndexRow->KeyPart,
|
||
&IndexContext );
|
||
|
||
//
|
||
// See if there is an actual match.
|
||
//
|
||
|
||
if (FindNextIndexEntry( IrpContext,
|
||
Scb,
|
||
&IndexRow->KeyPart,
|
||
FALSE,
|
||
FALSE,
|
||
&IndexContext,
|
||
FALSE,
|
||
NULL )) {
|
||
|
||
//
|
||
// Point to the index entry and the data within it.
|
||
//
|
||
|
||
IndexEntry = IndexContext.Current->IndexEntry;
|
||
|
||
if (IndexEntry->DataLength < IndexRow->DataPart.DataLength) {
|
||
NtfsRaiseStatus( IrpContext, STATUS_INFO_LENGTH_MISMATCH, NULL, NULL );
|
||
}
|
||
|
||
DataInIndex = Add2Ptr( IndexEntry, IndexEntry->DataOffset );
|
||
|
||
//
|
||
// Now pin the entry.
|
||
//
|
||
|
||
if (IndexContext.Current == IndexContext.Base) {
|
||
|
||
PFILE_RECORD_SEGMENT_HEADER FileRecord;
|
||
PATTRIBUTE_RECORD_HEADER Attribute;
|
||
PATTRIBUTE_ENUMERATION_CONTEXT Context = &IndexContext.AttributeContext;
|
||
|
||
//
|
||
// Pin the root
|
||
//
|
||
|
||
NtfsPinMappedAttribute( IrpContext,
|
||
Vcb,
|
||
Context );
|
||
|
||
//
|
||
// Write a log record to change our ParentIndexEntry.
|
||
//
|
||
|
||
FileRecord = NtfsContainingFileRecord(Context);
|
||
Attribute = NtfsFoundAttribute(Context);
|
||
|
||
//
|
||
// Write the log record, but do not update the FileRecord Lsn,
|
||
// since nothing moved and we don't want to force index contexts
|
||
// to have to rescan.
|
||
//
|
||
// FileRecord->Lsn =
|
||
//
|
||
|
||
NtfsWriteLog( IrpContext,
|
||
Vcb->MftScb,
|
||
NtfsFoundBcb(Context),
|
||
UpdateRecordDataRoot,
|
||
IndexRow->DataPart.Data,
|
||
IndexRow->DataPart.DataLength,
|
||
UpdateRecordDataRoot,
|
||
DataInIndex,
|
||
IndexRow->DataPart.DataLength,
|
||
NtfsMftOffset( Context ),
|
||
(PCHAR)Attribute - (PCHAR)FileRecord,
|
||
(PCHAR)IndexEntry - (PCHAR)Attribute,
|
||
Vcb->BytesPerFileRecordSegment );
|
||
|
||
if (ARGUMENT_PRESENT( QuickIndexHint )) {
|
||
|
||
ASSERT( Count == 1 );
|
||
QuickIndex->BufferOffset = 0;
|
||
}
|
||
|
||
} else {
|
||
|
||
PINDEX_ALLOCATION_BUFFER IndexBuffer;
|
||
|
||
Sp = IndexContext.Current;
|
||
IndexBuffer = (PINDEX_ALLOCATION_BUFFER)Sp->StartOfBuffer;
|
||
|
||
//
|
||
// Pin the index buffer
|
||
//
|
||
|
||
NtfsPinMappedData( IrpContext,
|
||
Scb,
|
||
LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
|
||
Scb->ScbType.Index.BytesPerIndexBuffer,
|
||
&Sp->Bcb );
|
||
|
||
//
|
||
// Write a log record to change our ParentIndexEntry.
|
||
//
|
||
|
||
//
|
||
// Write the log record, but do not update the IndexBuffer Lsn,
|
||
// since nothing moved and we don't want to force index contexts
|
||
// to have to rescan.
|
||
//
|
||
// Indexbuffer->Lsn =
|
||
//
|
||
|
||
NtfsWriteLog( IrpContext,
|
||
Scb,
|
||
Sp->Bcb,
|
||
UpdateRecordDataAllocation,
|
||
IndexRow->DataPart.Data,
|
||
IndexRow->DataPart.DataLength,
|
||
UpdateRecordDataAllocation,
|
||
DataInIndex,
|
||
IndexRow->DataPart.DataLength,
|
||
LlBytesFromIndexBlocks( IndexBuffer->ThisBlock, Scb->ScbType.Index.IndexBlockByteShift ),
|
||
0,
|
||
(PCHAR)Sp->IndexEntry - (PCHAR)IndexBuffer,
|
||
Scb->ScbType.Index.BytesPerIndexBuffer );
|
||
|
||
if (ARGUMENT_PRESENT( QuickIndexHint )) {
|
||
|
||
ASSERT( Count == 1 );
|
||
QuickIndex->ChangeCount = Scb->ScbType.Index.ChangeCount;
|
||
QuickIndex->BufferOffset = PtrOffset( Sp->StartOfBuffer, Sp->IndexEntry );
|
||
QuickIndex->CapturedLsn = ((PINDEX_ALLOCATION_BUFFER) Sp->StartOfBuffer)->Lsn;
|
||
QuickIndex->IndexBlock = ((PINDEX_ALLOCATION_BUFFER) Sp->StartOfBuffer)->ThisBlock;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now call the Restart routine to do it.
|
||
//
|
||
|
||
NtOfsRestartUpdateDataInIndex( IndexEntry,
|
||
IndexRow->DataPart.Data,
|
||
IndexRow->DataPart.DataLength );
|
||
|
||
//
|
||
// If the file name is not in the index, this is a bad file.
|
||
//
|
||
|
||
} else {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_NO_MATCH, NULL, NULL );
|
||
}
|
||
|
||
//
|
||
// Get ready for the next pass through.
|
||
//
|
||
|
||
NtfsCleanupIndexContext( IrpContext, &IndexContext );
|
||
IndexRow += 1;
|
||
|
||
} while (--Count);
|
||
|
||
try_exit: NOTHING;
|
||
|
||
} finally {
|
||
|
||
NtfsCleanupIndexContext( IrpContext, &IndexContext );
|
||
|
||
}
|
||
}
|
||
|
||
|
||
NTFSAPI
|
||
NTSTATUS
|
||
NtOfsReadRecords (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PSCB Scb,
|
||
IN OUT PREAD_CONTEXT *ReadContext,
|
||
IN PINDEX_KEY IndexKey OPTIONAL,
|
||
IN PMATCH_FUNCTION MatchFunction,
|
||
IN PVOID MatchData,
|
||
IN OUT ULONG *Count,
|
||
OUT PINDEX_ROW Rows,
|
||
IN ULONG BufferLength,
|
||
OUT PVOID Buffer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine may be called to enumerate rows in an index, in collated
|
||
order. It only returns records accepted by the match function.
|
||
|
||
IndexKey may be specified at any time to start a new search from IndexKey,
|
||
and IndexKey must be specified on the first call for a given IrpContext
|
||
(and *ReadContext must be NULL).
|
||
|
||
The read terminates when either *Count records have been returned, or
|
||
BufferLength has been exhausted, or there are no more matching records.
|
||
|
||
NtOfsReadRecords will seek to the appropriate point in the BTree (as defined
|
||
by the IndexKey or saved position and the CollateFunction) and begin calling
|
||
MatchFunction for each record. It continues doing this while MatchFunction
|
||
returns STATUS_SUCCESS. If MatchFunction returns STATUS_NO_MORE_MATCHES,
|
||
NtOfsReadRecords will cache this result and not call MatchFunction again until
|
||
called with a non-NULL IndexKey.
|
||
|
||
Note that this call is self-synchronized, such that successive calls to
|
||
the routine are guaranteed to make progress through the index and to return
|
||
items in Collation order, in spite of Add and Delete record calls being
|
||
interspersed with Read records calls.
|
||
|
||
Arguments:
|
||
|
||
Scb - Supplies an Scb as the previously returned handle for this index.
|
||
|
||
ReadContext - On the first call this must supply a pointer to NULL. On
|
||
return a pointer to a private context structure is returned,
|
||
which must then be supplied on all subsequent calls. This
|
||
structure must be eventually be freed via NtOfsFreeReadContext.
|
||
|
||
IndexKey - If specified, supplies the key from which the enumeration is to
|
||
start/resume. It must be specified on the first call when *ReadContext
|
||
is NULL.
|
||
|
||
MatchFunction - Supplies the MatchFunction to be called to determine which
|
||
rows to return.
|
||
|
||
MatchData - Supplies the MatchData to be specified on each call to the MatchFunction.
|
||
|
||
Count - Supplies the count of how many rows may be received, and returns the
|
||
number of rows actually being returned.
|
||
|
||
Rows - Returns the Count row descriptions.
|
||
|
||
BufferLength - Supplies the length of the buffer in bytes, into which the
|
||
row keys and data are copied upon return.
|
||
|
||
Buffer - Supplies the buffer into which the rows may be copied.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS -- if operation was successful.
|
||
STATUS_NO_MATCH -- if there is no match for the specified IndexKey.
|
||
STATUS_NO_MORE_MATCHES -- if a match is returned or previously returned,
|
||
but there are no more matches.
|
||
|
||
--*/
|
||
|
||
{
|
||
PINDEX_CONTEXT IndexContext;
|
||
PINDEX_ENTRY IndexEntry;
|
||
ULONG LengthToCopy;
|
||
BOOLEAN MustRestart;
|
||
ULONG BytesRemaining = BufferLength;
|
||
ULONG ReturnCount = 0;
|
||
NTSTATUS Status;
|
||
BOOLEAN NextFlag;
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_SCB( Scb );
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// On the first lookup, their must be a key.
|
||
//
|
||
|
||
ASSERT((IndexKey != NULL) || (*ReadContext != NULL));
|
||
|
||
//
|
||
// Everything must be Ulong aligned and sized.
|
||
//
|
||
|
||
ASSERT(((ULONG)Buffer & (sizeof(ULONG) - 1)) == 0);
|
||
ASSERT((BufferLength & (sizeof(ULONG) - 1)) == 0);
|
||
|
||
Status = STATUS_SUCCESS;
|
||
NextFlag = FALSE;
|
||
|
||
//
|
||
// Pick up the IndexContext, allocating one if we need to.
|
||
//
|
||
|
||
if (*ReadContext == NULL) {
|
||
*ReadContext = NtfsAllocatePool(PagedPool, sizeof(READ_CONTEXT) );
|
||
NtfsInitializeIndexContext( &(*ReadContext)->IndexContext );
|
||
(*ReadContext)->LastReturnedKey.Key = &(*ReadContext)->SmallKeyBuffer[0];
|
||
(*ReadContext)->LastReturnedKey.KeyLength = 0;
|
||
(*ReadContext)->AllocatedKeyLength = sizeof(READ_CONTEXT) -
|
||
FIELD_OFFSET(READ_CONTEXT, SmallKeyBuffer[0]);
|
||
}
|
||
|
||
IndexContext = &(*ReadContext)->IndexContext;
|
||
|
||
//
|
||
// Store the MatchFunction and Data in the IndexContext, for the enumerations.
|
||
//
|
||
|
||
IndexContext->MatchFunction = MatchFunction;
|
||
IndexContext->MatchData = MatchData;
|
||
|
||
NtfsAcquireSharedScb( IrpContext, Scb );
|
||
|
||
try {
|
||
|
||
//
|
||
// If a Key was passed, position to the first possible match.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(IndexKey)) {
|
||
|
||
FindFirstIndexEntry( IrpContext,
|
||
Scb,
|
||
IndexKey,
|
||
IndexContext );
|
||
|
||
//
|
||
// Otherwise return here if we hit the end of the matches last time.
|
||
//
|
||
|
||
} else if ((*ReadContext)->LastReturnedKey.KeyLength == 0) {
|
||
|
||
try_return( Status = STATUS_NO_MORE_MATCHES );
|
||
}
|
||
|
||
//
|
||
// Loop while we still have space to store rows.
|
||
//
|
||
|
||
while (ReturnCount <= *Count) {
|
||
|
||
//
|
||
// See if there is an actual match.
|
||
//
|
||
|
||
if (!FindNextIndexEntry( IrpContext,
|
||
Scb,
|
||
NULL, // Not needed because of Match Function
|
||
TRUE,
|
||
FALSE,
|
||
IndexContext,
|
||
NextFlag,
|
||
&MustRestart )) {
|
||
|
||
//
|
||
// First handle the restart case by resuming from the last
|
||
// key returned, and skip that one.
|
||
//
|
||
|
||
if (MustRestart) {
|
||
|
||
ASSERT(!ARGUMENT_PRESENT(IndexKey));
|
||
|
||
NtfsReinitializeIndexContext( IrpContext, IndexContext );
|
||
|
||
FindFirstIndexEntry( IrpContext,
|
||
Scb,
|
||
&(*ReadContext)->LastReturnedKey,
|
||
IndexContext );
|
||
|
||
//
|
||
// Set NextFlag to TRUE, so we can go back and skip
|
||
// the key we resumed on.
|
||
//
|
||
|
||
NextFlag = TRUE;
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// No (more) entries - remember that the enumeration is done.
|
||
//
|
||
|
||
(*ReadContext)->LastReturnedKey.KeyLength = 0;
|
||
|
||
//
|
||
// Return the appropriate code based on whether we have returned
|
||
// any matches yet or not.
|
||
//
|
||
|
||
if ((ReturnCount == 0) && ARGUMENT_PRESENT(IndexKey)) {
|
||
Status = STATUS_NO_MATCH;
|
||
} else {
|
||
Status = STATUS_NO_MORE_MATCHES;
|
||
}
|
||
|
||
try_return(Status);
|
||
}
|
||
|
||
//
|
||
// We always need to go one beyond the one we can return to keep
|
||
// all resume cases the same, so now is the time to get out if the
|
||
// count is finished.
|
||
//
|
||
|
||
if (ReturnCount == *Count) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Now we must always move to the next.
|
||
//
|
||
|
||
NextFlag = TRUE;
|
||
|
||
//
|
||
// First try to copy the key.
|
||
//
|
||
|
||
IndexEntry = IndexContext->Current->IndexEntry;
|
||
|
||
LengthToCopy = IndexEntry->AttributeLength;
|
||
if (LengthToCopy > BytesRemaining) {
|
||
break;
|
||
}
|
||
|
||
RtlCopyMemory( Buffer, IndexEntry + 1, LengthToCopy );
|
||
Rows->KeyPart.Key = Buffer;
|
||
Rows->KeyPart.KeyLength = LengthToCopy;
|
||
LengthToCopy = LongAlign(LengthToCopy);
|
||
Buffer = Add2Ptr( Buffer, LengthToCopy );
|
||
BytesRemaining -= LengthToCopy;
|
||
|
||
//
|
||
// Now try to copy the data.
|
||
//
|
||
|
||
LengthToCopy = IndexEntry->DataLength;
|
||
if (LengthToCopy > BytesRemaining) {
|
||
break;
|
||
}
|
||
|
||
RtlCopyMemory( Buffer, Add2Ptr(IndexEntry, IndexEntry->DataOffset), LengthToCopy );
|
||
Rows->DataPart.Data = Buffer;
|
||
Rows->DataPart.DataLength = LengthToCopy;
|
||
LengthToCopy = LongAlign(LengthToCopy);
|
||
Buffer = Add2Ptr( Buffer, LengthToCopy );
|
||
BytesRemaining -= LengthToCopy;
|
||
|
||
//
|
||
// Capture this key before looping back.
|
||
//
|
||
// First see if there is enough space.
|
||
//
|
||
|
||
if (Rows->KeyPart.KeyLength > (*ReadContext)->AllocatedKeyLength) {
|
||
|
||
PVOID NewBuffer;
|
||
|
||
//
|
||
// Allocate a new buffer.
|
||
//
|
||
|
||
LengthToCopy = LongAlign(Rows->KeyPart.KeyLength + 16);
|
||
NewBuffer = NtfsAllocatePool(PagedPool, LengthToCopy );
|
||
|
||
//
|
||
// Delete old key buffer?
|
||
//
|
||
|
||
if ((*ReadContext)->LastReturnedKey.Key != &(*ReadContext)->SmallKeyBuffer[0]) {
|
||
NtfsFreePool( (*ReadContext)->LastReturnedKey.Key );
|
||
}
|
||
|
||
(*ReadContext)->LastReturnedKey.Key = NewBuffer;
|
||
(*ReadContext)->AllocatedKeyLength = LengthToCopy;
|
||
}
|
||
|
||
RtlCopyMemory( (*ReadContext)->LastReturnedKey.Key,
|
||
Rows->KeyPart.Key,
|
||
Rows->KeyPart.KeyLength );
|
||
|
||
(*ReadContext)->LastReturnedKey.KeyLength = Rows->KeyPart.KeyLength;
|
||
|
||
Rows += 1;
|
||
ReturnCount += 1;
|
||
}
|
||
|
||
try_exit: NOTHING;
|
||
|
||
} finally {
|
||
|
||
NtfsCleanupIndexContext( IrpContext, IndexContext );
|
||
|
||
NtfsReleaseScb( IrpContext, Scb );
|
||
}
|
||
|
||
*Count = ReturnCount;
|
||
|
||
//
|
||
// If we are already returning something, but we got an error, change it
|
||
// to success to return what we have. Then we may or may not get this error
|
||
// again anyway when we are called back. This loop is currently not designed
|
||
// to resume correctly in all cases if there are already items returned.
|
||
//
|
||
|
||
if (ReturnCount != 0) {
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTFSAPI
|
||
VOID
|
||
NtOfsFreeReadContext (
|
||
IN PREAD_CONTEXT ReadContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to free an ReadContext created by NtOfsReadRecords.
|
||
|
||
Arguments:
|
||
|
||
ReadContext - Supplies the context to free.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS -- if operation was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
if (ReadContext->LastReturnedKey.Key != NULL &&
|
||
ReadContext->LastReturnedKey.Key != &ReadContext->SmallKeyBuffer[0]) {
|
||
NtfsFreePool( ReadContext->LastReturnedKey.Key );
|
||
}
|
||
NtfsFreePool( ReadContext );
|
||
}
|
||
|
||
|
||
/*++
|
||
|
||
Routine Descriptions:
|
||
|
||
Standard collation routines for creating simple indices.
|
||
|
||
Arguments:
|
||
|
||
Key1 - First key to compare.
|
||
|
||
Key2 - Second key to compare.
|
||
|
||
CollationData - Optional data to support the collation.
|
||
|
||
Return Value:
|
||
|
||
LessThan, EqualTo, or Greater than, for how Key1 compares
|
||
with Key2.
|
||
|
||
--*/
|
||
|
||
FSRTL_COMPARISON_RESULT
|
||
NtOfsCollateUlong (
|
||
IN PINDEX_KEY Key1,
|
||
IN PINDEX_KEY Key2,
|
||
IN PVOID CollationData
|
||
)
|
||
|
||
{
|
||
ULONG u1, u2;
|
||
|
||
UNREFERENCED_PARAMETER(CollationData);
|
||
|
||
ASSERT( Key1->KeyLength == 4 );
|
||
ASSERT( Key2->KeyLength == 4 );
|
||
|
||
u1 = *(PULONG)Key1->Key;
|
||
u2 = *(PULONG)Key2->Key;
|
||
|
||
if (u1 > u2) {
|
||
return GreaterThan;
|
||
} else if (u1 < u2) {
|
||
return LessThan;
|
||
}
|
||
return EqualTo;
|
||
}
|
||
|
||
FSRTL_COMPARISON_RESULT
|
||
NtOfsCollateSid (
|
||
IN PINDEX_KEY Key1,
|
||
IN PINDEX_KEY Key2,
|
||
IN PVOID CollationData
|
||
)
|
||
|
||
{
|
||
LONG Compare;
|
||
|
||
UNREFERENCED_PARAMETER(CollationData);
|
||
|
||
//
|
||
// The length of a valid SID is imbedded in the data
|
||
// so the function will mismatch be for the data runs out.
|
||
//
|
||
|
||
Compare = memcmp( Key1->Key, Key2->Key, Key1->KeyLength );
|
||
|
||
if (Compare > 0) {
|
||
return GreaterThan;
|
||
} else if (Compare < 0) {
|
||
return LessThan;
|
||
}
|
||
|
||
return EqualTo;
|
||
}
|
||
|
||
FSRTL_COMPARISON_RESULT
|
||
NtOfsCollateUnicode (
|
||
IN PINDEX_KEY Key1,
|
||
IN PINDEX_KEY Key2,
|
||
IN PVOID CollationData
|
||
)
|
||
|
||
{
|
||
UNICODE_STRING String1, String2;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Build the unicode strings and call namesup.
|
||
//
|
||
|
||
String1.Length =
|
||
String1.MaximumLength = (USHORT)Key1->KeyLength;
|
||
String1.Buffer = Key1->Key;
|
||
|
||
String2.Length =
|
||
String2.MaximumLength = (USHORT)Key2->KeyLength;
|
||
String2.Buffer = Key2->Key;
|
||
|
||
return NtfsCollateNames( ((PUPCASE_TABLE_AND_KEY)CollationData)->UpcaseTable,
|
||
((PUPCASE_TABLE_AND_KEY)CollationData)->UpcaseTableSize,
|
||
&String1,
|
||
&String2,
|
||
LessThan,
|
||
TRUE );
|
||
}
|
||
|
||
|
||
/*++
|
||
|
||
Routine Descriptions:
|
||
|
||
Standard match routines for find / enumerate in simple indices.
|
||
|
||
Arguments:
|
||
|
||
IndexRow - Row to check for a match.
|
||
|
||
MatchData - Optional data for determining a match.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if the IndexRow matches
|
||
STATUS_NO_MATCH if the IndexRow does not match, but the enumeration should
|
||
continue
|
||
STATUS_NO_MORE_MATCHES if the IndexRow does not match, and the enumeration
|
||
should terminate
|
||
|
||
--*/
|
||
|
||
NTSTATUS
|
||
NtOfsMatchAll (
|
||
IN PINDEX_ROW IndexRow,
|
||
IN OUT PVOID MatchData
|
||
)
|
||
|
||
{
|
||
UNREFERENCED_PARAMETER(IndexRow);
|
||
UNREFERENCED_PARAMETER(MatchData);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
NtOfsMatchUlongExact (
|
||
IN PINDEX_ROW IndexRow,
|
||
IN OUT PVOID MatchData
|
||
)
|
||
|
||
{
|
||
ULONG u1, u2;
|
||
|
||
ASSERT( IndexRow->KeyPart.KeyLength == 4 );
|
||
|
||
u1 = *(PULONG)IndexRow->KeyPart.Key;
|
||
u2 = *(PULONG)((PINDEX_KEY)MatchData)->Key;
|
||
|
||
if (u1 == u2) {
|
||
return STATUS_SUCCESS;
|
||
} else if (u1 < u2) {
|
||
return STATUS_NO_MATCH;
|
||
}
|
||
return STATUS_NO_MORE_MATCHES;
|
||
}
|
||
|
||
NTSTATUS
|
||
NtOfsMatchUnicodeExpression (
|
||
IN PINDEX_ROW IndexRow,
|
||
IN OUT PVOID MatchData
|
||
)
|
||
|
||
{
|
||
UNICODE_STRING MatchString, IndexString;
|
||
FSRTL_COMPARISON_RESULT BlindResult;
|
||
PUPCASE_TABLE_AND_KEY UpcaseTableAndKey = (PUPCASE_TABLE_AND_KEY)MatchData;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Build the unicode strings and call namesup.
|
||
//
|
||
|
||
MatchString.Length =
|
||
MatchString.MaximumLength = (USHORT)UpcaseTableAndKey->Key.KeyLength;
|
||
MatchString.Buffer = UpcaseTableAndKey->Key.Key;
|
||
|
||
IndexString.Length =
|
||
IndexString.MaximumLength = (USHORT)IndexRow->KeyPart.KeyLength;
|
||
IndexString.Buffer = IndexRow->KeyPart.Key;
|
||
|
||
if (NtfsIsNameInExpression( UpcaseTableAndKey->UpcaseTable, &MatchString, &IndexString, TRUE )) {
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} else if ((BlindResult = NtfsCollateNames(UpcaseTableAndKey->UpcaseTable,
|
||
UpcaseTableAndKey->UpcaseTableSize,
|
||
&MatchString,
|
||
&IndexString,
|
||
GreaterThan,
|
||
TRUE)) != LessThan) {
|
||
|
||
return STATUS_NO_MATCH;
|
||
|
||
} else {
|
||
|
||
return STATUS_NO_MORE_MATCHES;
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
NtOfsMatchUnicodeString (
|
||
IN PINDEX_ROW IndexRow,
|
||
IN OUT PVOID MatchData
|
||
)
|
||
|
||
{
|
||
UNICODE_STRING MatchString, IndexString;
|
||
FSRTL_COMPARISON_RESULT BlindResult;
|
||
PUPCASE_TABLE_AND_KEY UpcaseTableAndKey = (PUPCASE_TABLE_AND_KEY)MatchData;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Build the unicode strings and call namesup.
|
||
//
|
||
|
||
MatchString.Length =
|
||
MatchString.MaximumLength = (USHORT)UpcaseTableAndKey->Key.KeyLength;
|
||
MatchString.Buffer = UpcaseTableAndKey->Key.Key;
|
||
|
||
IndexString.Length =
|
||
IndexString.MaximumLength = (USHORT)IndexRow->KeyPart.KeyLength;
|
||
IndexString.Buffer = IndexRow->KeyPart.Key;
|
||
|
||
if (NtfsAreNamesEqual( UpcaseTableAndKey->UpcaseTable, &MatchString, &IndexString, TRUE )) {
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} else if ((BlindResult = NtfsCollateNames(UpcaseTableAndKey->UpcaseTable,
|
||
UpcaseTableAndKey->UpcaseTableSize,
|
||
&MatchString,
|
||
&IndexString,
|
||
GreaterThan,
|
||
TRUE)) != LessThan) {
|
||
|
||
return STATUS_NO_MATCH;
|
||
|
||
} else {
|
||
|
||
return STATUS_NO_MORE_MATCHES;
|
||
}
|
||
}
|
||
|
||
|
||
#ifdef TOMM
|
||
VOID
|
||
NtOfsIndexTest (
|
||
PIRP_CONTEXT IrpContext,
|
||
PFCB TestFcb
|
||
)
|
||
|
||
{
|
||
PSCB AdScb;
|
||
NTSTATUS Status;
|
||
ULONG i;
|
||
MAP_HANDLE MapHandle;
|
||
ULONG Count;
|
||
UPCASE_TABLE_AND_KEY UpcaseTableAndKey;
|
||
QUICK_INDEX_HINT QuickHint;
|
||
INDEX_KEY IndexKey;
|
||
INDEX_ROW IndexRow[6];
|
||
UCHAR Buffer[6*160];
|
||
PREAD_CONTEXT ReadContext = NULL;
|
||
UNICODE_STRING IndexName = {sizeof(L"$Test") - 2, sizeof(L"$Test") - 2, L"$Test"};
|
||
USHORT MaxKey = MAXUSHORT;
|
||
USHORT MinKey = 0;
|
||
|
||
DbgPrint("NtOfs Make NtOfsDoIndexTest FALSE to suppress test\n");
|
||
DbgPrint("NtOfs Make NtOfsLeaveTestIndex TRUE to leave test index\n");
|
||
|
||
DbgBreakPoint();
|
||
|
||
if (!NtOfsDoIndexTest) {
|
||
return;
|
||
}
|
||
NtOfsDoIndexTest = FALSE;
|
||
|
||
UpcaseTableAndKey.UpcaseTable = TestFcb->Vcb->UpcaseTable;
|
||
UpcaseTableAndKey.UpcaseTableSize = TestFcb->Vcb->UpcaseTableSize;
|
||
UpcaseTableAndKey.Key.Key = NULL;
|
||
UpcaseTableAndKey.Key.KeyLength = 0;
|
||
|
||
//
|
||
// Create Test Index
|
||
//
|
||
|
||
DbgPrint("NtOfs creating test index\n");
|
||
NtOfsCreateIndex( IrpContext,
|
||
TestFcb,
|
||
IndexName,
|
||
CREATE_NEW,
|
||
0,
|
||
&NtOfsCollateUnicode,
|
||
&UpcaseTableAndKey,
|
||
&AdScb );
|
||
|
||
DbgPrint("NtOfs created Test Index Scb %08lx\n", AdScb);
|
||
|
||
//
|
||
// Access empty index
|
||
//
|
||
|
||
DbgPrint("NtOfs lookup last in empty index\n");
|
||
IndexKey.Key = &MaxKey;
|
||
IndexKey.KeyLength = sizeof(MaxKey);
|
||
Status = NtOfsFindLastRecord( IrpContext, AdScb, &IndexKey, &IndexRow[0], &MapHandle );
|
||
|
||
ASSERT(!NT_SUCCESS(Status));
|
||
|
||
//
|
||
// Add some keys!
|
||
//
|
||
|
||
DbgPrint("NtOfs adding keys to index\n");
|
||
for (i = 0; i < $EA/0x10; i++) {
|
||
|
||
IndexRow[0].KeyPart.Key = &NtfsAttributeDefinitions[i].AttributeName;
|
||
IndexRow[0].KeyPart.KeyLength = 0x80;
|
||
IndexRow[0].DataPart.Data = (PCHAR)IndexRow[0].KeyPart.Key + 0x80;
|
||
IndexRow[0].DataPart.DataLength = sizeof(ATTRIBUTE_DEFINITION_COLUMNS) - 0x84;
|
||
|
||
NtOfsAddRecords( IrpContext, AdScb, 1, &IndexRow[0], 0 );
|
||
}
|
||
|
||
//
|
||
// Now find the last key
|
||
//
|
||
|
||
DbgPrint("NtOfs checkin last key in index\n");
|
||
IndexKey.Key = &MaxKey;
|
||
IndexKey.KeyLength = sizeof(MaxKey);
|
||
Status = NtOfsFindLastRecord( IrpContext, AdScb, &IndexKey, &IndexRow[0], &MapHandle );
|
||
|
||
ASSERT(NT_SUCCESS(Status));
|
||
ASSERT(RtlCompareMemory(IndexRow[0].KeyPart.Key, L"$VOLUME_NAME", sizeof(L"$VOLUME_NAME") - 2) ==
|
||
(sizeof(L"$VOLUME_NAME") - 2));
|
||
|
||
NtOfsReleaseMap( IrpContext, &MapHandle );
|
||
|
||
//
|
||
// See if they are all there.
|
||
//
|
||
|
||
DbgPrint("NtOfs looking up all keys in index\n");
|
||
for (i = 0; i < $EA/0x10; i++) {
|
||
|
||
IndexKey.Key = &NtfsAttributeDefinitions[i].AttributeName;
|
||
IndexKey.KeyLength = 0x80;
|
||
|
||
Status = NtOfsFindRecord( IrpContext, AdScb, &IndexKey, &IndexRow[0], &MapHandle, NULL );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
DbgPrint("NtOfsIterationFailure with i = %08lx, Status = %08lx\n", i, Status);
|
||
}
|
||
|
||
NtOfsReleaseMap( IrpContext, &MapHandle );
|
||
}
|
||
|
||
//
|
||
// Now enumerate the entire index
|
||
//
|
||
|
||
IndexKey.Key = &MinKey;
|
||
IndexKey.KeyLength = sizeof(MinKey);
|
||
Count = 6;
|
||
|
||
DbgPrint("NtOfs enumerating index:\n\n");
|
||
while (NT_SUCCESS(Status = NtOfsReadRecords( IrpContext,
|
||
AdScb,
|
||
&ReadContext,
|
||
(ReadContext == NULL) ? &IndexKey : NULL,
|
||
&NtOfsMatchAll,
|
||
NULL,
|
||
&Count,
|
||
IndexRow,
|
||
sizeof(Buffer),
|
||
Buffer ))) {
|
||
|
||
for (i = 0; i < Count; i++) {
|
||
DbgPrint( "IndexKey = %ws, AttributeTypeCode = %lx\n",
|
||
IndexRow[i].KeyPart.Key,
|
||
*(PULONG)IndexRow[i].DataPart.Data );
|
||
}
|
||
DbgPrint( "\n" );
|
||
}
|
||
|
||
NtOfsFreeReadContext( ReadContext );
|
||
ReadContext = NULL;
|
||
|
||
//
|
||
// Loop to update all records.
|
||
//
|
||
|
||
DbgPrint("NtOfs updating up all keys in index\n");
|
||
for (i = 0; i < $EA/0x10; i++) {
|
||
|
||
IndexKey.Key = &NtfsAttributeDefinitions[i].AttributeName;
|
||
IndexKey.KeyLength = 0x80;
|
||
|
||
RtlZeroMemory( &QuickHint, sizeof(QUICK_INDEX_HINT) );
|
||
|
||
NtOfsFindRecord( IrpContext, AdScb, &IndexKey, &IndexRow[0], &MapHandle, &QuickHint );
|
||
|
||
//
|
||
// Copy and update the data.
|
||
//
|
||
|
||
RtlCopyMemory( Buffer, IndexRow[0].DataPart.Data, IndexRow[0].DataPart.DataLength );
|
||
*(PULONG)Buffer += 0x100;
|
||
IndexRow[0].DataPart.Data = Buffer;
|
||
|
||
//
|
||
// Perform update with all valid combinations of hint and map handle.
|
||
//
|
||
|
||
NtOfsUpdateRecord( IrpContext,
|
||
AdScb,
|
||
1,
|
||
&IndexRow[0],
|
||
(i <= $FILE_NAME/0x10) ? NULL : &QuickHint,
|
||
(i < $INDEX_ROOT/0x10) ? NULL : &MapHandle );
|
||
|
||
NtOfsReleaseMap( IrpContext, &MapHandle );
|
||
}
|
||
|
||
//
|
||
// Now enumerate the entire index again to see the updates.
|
||
//
|
||
|
||
IndexKey.Key = &MinKey;
|
||
IndexKey.KeyLength = sizeof(MinKey);
|
||
Count = 6;
|
||
|
||
DbgPrint("NtOfs enumerating index after updates:\n\n");
|
||
while (NT_SUCCESS(Status = NtOfsReadRecords( IrpContext,
|
||
AdScb,
|
||
&ReadContext,
|
||
(ReadContext == NULL) ? &IndexKey : NULL,
|
||
&NtOfsMatchAll,
|
||
NULL,
|
||
&Count,
|
||
IndexRow,
|
||
sizeof(Buffer),
|
||
Buffer ))) {
|
||
|
||
for (i = 0; i < Count; i++) {
|
||
DbgPrint( "IndexKey = %ws, AttributeTypeCode = %lx\n",
|
||
IndexRow[i].KeyPart.Key,
|
||
*(PULONG)IndexRow[i].DataPart.Data );
|
||
}
|
||
DbgPrint( "\n" );
|
||
}
|
||
|
||
NtOfsFreeReadContext( ReadContext );
|
||
ReadContext = NULL;
|
||
|
||
|
||
//
|
||
// Now delete the keys
|
||
//
|
||
|
||
if (!NtOfsLeaveTestIndex) {
|
||
|
||
DbgPrint("NtOfs deleting all keys in index:\n\n");
|
||
for (i = 0; i < $EA/0x10; i++) {
|
||
|
||
IndexKey.Key = &NtfsAttributeDefinitions[i].AttributeName;
|
||
IndexKey.KeyLength = 0x80;
|
||
|
||
NtOfsDeleteRecords( IrpContext, AdScb, 1, &IndexKey );
|
||
}
|
||
|
||
//
|
||
// Access empty index
|
||
//
|
||
|
||
DbgPrint("NtOfs lookup last key in empty index:\n\n");
|
||
IndexKey.Key = &MaxKey;
|
||
IndexKey.KeyLength = sizeof(MaxKey);
|
||
Status = NtOfsFindLastRecord( IrpContext, AdScb, &IndexKey, &IndexRow[0], &MapHandle );
|
||
|
||
ASSERT(!NT_SUCCESS(Status));
|
||
|
||
DbgPrint("NtOfs deleting index:\n");
|
||
NtOfsDeleteIndex( IrpContext, TestFcb, AdScb );
|
||
}
|
||
|
||
DbgPrint("NtOfs closing index:\n");
|
||
NtOfsCloseIndex( IrpContext, AdScb );
|
||
|
||
DbgPrint("NtOfs test complete!\n\n");
|
||
|
||
return;
|
||
|
||
//
|
||
// Make sure these at least compile until we have some real callers.
|
||
//
|
||
|
||
{
|
||
MAP_HANDLE M;
|
||
PVOID B;
|
||
LONGLONG O;
|
||
ULONG L;
|
||
LSN Lsn;
|
||
|
||
NtOfsInitializeMapHandle( &M );
|
||
NtOfsMapAttribute( IrpContext, AdScb, O, L, &B, &M );
|
||
NtOfsPreparePinWrite( IrpContext, AdScb, O, L, &B, &M );
|
||
NtOfsPinRead( IrpContext, AdScb, O, L, &M );
|
||
NtOfsDirty( IrpContext, &M, &Lsn );
|
||
NtOfsReleaseMap( IrpContext, &M );
|
||
NtOfsPutData( IrpContext, AdScb, O, L, &B );
|
||
|
||
}
|
||
}
|
||
|
||
#endif TOMM
|