NT4/private/ntos/cntfs/strucsup.c
2020-09-30 17:12:29 +02:00

7384 lines
199 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
StrucSup.c
Abstract:
This module implements the Ntfs in-memory data structure manipulation
routines
Author:
Gary Kimura [GaryKi] 21-May-1991
Tom Miller [TomM] 9-Sep-1991
Revision History:
--*/
#include "NtfsProc.h"
//
//**** include this file for our quick hacked quota check in NtfsFreePool
//
//#include <pool.h>
//
// Temporarily reference our local attribute definitions
//
extern ATTRIBUTE_DEFINITION_COLUMNS NtfsAttributeDefinitions[];
//
// The debug trace level
//
#define Dbg (DEBUG_TRACE_STRUCSUP)
//
// Define a tag for general pool allocations from this module
//
#undef MODULE_POOL_TAG
#define MODULE_POOL_TAG ('sFtN')
//
// Define a structure to use when renaming or moving Lcb's so that
// all the allocation for new filenames will succeed before munging names.
// This new allocation can be for the filename attribute in an Lcb or the
// filename in a Ccb.
//
typedef struct _NEW_FILENAME {
//
// Ntfs structure which needs the allocation.
//
PVOID Structure;
PVOID NewAllocation;
} NEW_FILENAME;
typedef NEW_FILENAME *PNEW_FILENAME;
//
// This is just a macro to do a sanity check for duplicate scbs on an Fcb
//
//
// Local support routines
//
VOID
NtfsCheckScbForCache (
IN OUT PSCB Scb
);
BOOLEAN
NtfsRemoveScb (
IN PIRP_CONTEXT IrpContext,
IN PSCB Scb,
IN BOOLEAN CheckForAttributeTable,
OUT PBOOLEAN HeldByStream
);
BOOLEAN
NtfsPrepareFcbForRemoval (
IN PIRP_CONTEXT IrpContext,
IN PFCB Fcb,
IN PSCB StartingScb OPTIONAL,
IN BOOLEAN CheckForAttributeTable
);
VOID
NtfsTeardownFromLcb (
IN PIRP_CONTEXT IrpContext,
IN PVCB Vcb,
IN PFCB StartingFcb,
IN PLCB StartingLcb,
IN BOOLEAN CheckForAttributeTable,
IN BOOLEAN DontWaitForAcquire,
OUT PBOOLEAN RemovedStartingLcb,
OUT PBOOLEAN RemovedStartingFcb
);
//
// The following local routines are for manipulating the Fcb Table.
// The first three are generic table calls backs.
//
RTL_GENERIC_COMPARE_RESULTS
NtfsFcbTableCompare (
IN PRTL_GENERIC_TABLE FcbTable,
IN PVOID FirstStruct,
IN PVOID SecondStruct
);
//
// VOID
// NtfsInsertFcbTableEntry (
// IN PIRP_CONTEXT IrpContext,
// IN PVCB Vcb,
// IN PFCB Fcb,
// IN FILE_REFERENCE FileReference
// );
//
#define NtfsInsertFcbTableEntry(IC,V,F,FR) { \
FCB_TABLE_ELEMENT _Key; \
_Key.FileReference = (FR); \
_Key.Fcb = (F); \
(VOID) RtlInsertElementGenericTable( &(V)->FcbTable, \
&_Key, \
sizeof(FCB_TABLE_ELEMENT), \
NULL ); \
}
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NtfsBuildNormalizedName)
#pragma alloc_text(PAGE, NtfsCheckScbForCache)
#pragma alloc_text(PAGE, NtfsCombineLcbs)
#pragma alloc_text(PAGE, NtfsCreateCcb)
#pragma alloc_text(PAGE, NtfsCreateFcb)
#pragma alloc_text(PAGE, NtfsCreateFileLock)
#pragma alloc_text(PAGE, NtfsCreateLcb)
#pragma alloc_text(PAGE, NtfsCreatePrerestartScb)
#pragma alloc_text(PAGE, NtfsCreateRootFcb)
#pragma alloc_text(PAGE, NtfsCreateScb)
#pragma alloc_text(PAGE, NtfsDeleteCcb)
#pragma alloc_text(PAGE, NtfsDeleteFcb)
#pragma alloc_text(PAGE, NtfsDeleteLcb)
#pragma alloc_text(PAGE, NtfsDeleteScb)
#pragma alloc_text(PAGE, NtfsDeleteVcb)
#pragma alloc_text(PAGE, NtfsFcbTableCompare)
#pragma alloc_text(PAGE, NtfsGetNextFcbTableEntry)
#pragma alloc_text(PAGE, NtfsGetNextScb)
#pragma alloc_text(PAGE, NtfsInitializeVcb)
#pragma alloc_text(PAGE, NtfsLookupLcbByFlags)
#pragma alloc_text(PAGE, NtfsMoveLcb)
#pragma alloc_text(PAGE, NtfsRemoveScb)
#pragma alloc_text(PAGE, NtfsRenameLcb)
#pragma alloc_text(PAGE, NtfsTeardownStructures)
#pragma alloc_text(PAGE, NtfsUpdateNormalizedName)
#pragma alloc_text(PAGE, NtfsUpdateScbSnapshots)
#endif
VOID
NtfsInitializeVcb (
IN PIRP_CONTEXT IrpContext,
IN OUT PVCB Vcb,
IN PDEVICE_OBJECT TargetDeviceObject,
IN PVPB Vpb
)
/*++
Routine Description:
This routine initializes and inserts a new Vcb record into the in-memory
data structure. The Vcb record "hangs" off the end of the Volume device
object and must be allocated by our caller.
Arguments:
Vcb - Supplies the address of the Vcb record being initialized.
TargetDeviceObject - Supplies the address of the target device object to
associate with the Vcb record.
Vpb - Supplies the address of the Vpb to associate with the Vcb record.
Return Value:
None.
--*/
{
ULONG i;
ULONG NumberProcessors;
ASSERT_IRP_CONTEXT( IrpContext );
PAGED_CODE();
DebugTrace( +1, Dbg, ("NtfsInitializeVcb, Vcb = %08lx\n", Vcb) );
//
// First zero out the Vcb
//
RtlZeroMemory( Vcb, sizeof(VCB) );
//
// Set the node type code and size
//
Vcb->NodeTypeCode = NTFS_NTC_VCB;
Vcb->NodeByteSize = sizeof(VCB);
//
// Set the following Vcb flags before putting the Vcb in the
// Vcb queue. This will lock out checkpoints until the
// volume is mounted.
//
SetFlag( Vcb->CheckpointFlags,
VCB_CHECKPOINT_IN_PROGRESS |
VCB_LAST_CHECKPOINT_CLEAN);
//
// Insert this vcb record into the vcb queue off of the global data
// record
//
InsertTailList( &NtfsData.VcbQueue, &Vcb->VcbLinks );
//
// Set the target device object and vpb fields
//
Vcb->TargetDeviceObject = TargetDeviceObject;
Vcb->Vpb = Vpb;
//
// Set the state and condition fields. The removable media flag
// is set based on the real device's characteristics.
//
if (FlagOn(Vpb->RealDevice->Characteristics, FILE_REMOVABLE_MEDIA)) {
SetFlag( Vcb->VcbState, VCB_STATE_REMOVABLE_MEDIA );
}
SetFlag( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED );
//
// Initialize the synchronization objects in the Vcb.
//
ExInitializeResource( &Vcb->Resource );
ExInitializeFastMutex( &Vcb->FcbTableMutex );
ExInitializeFastMutex( &Vcb->FcbSecurityMutex );
ExInitializeFastMutex( &Vcb->CheckpointMutex );
KeInitializeEvent( &Vcb->CheckpointNotifyEvent, NotificationEvent, TRUE );
//
// Initialize the Fcb Table
//
RtlInitializeGenericTable( &Vcb->FcbTable,
NtfsFcbTableCompare,
NtfsAllocateFcbTableEntry,
NtfsFreeFcbTableEntry,
NULL );
//
// Initialize the list head and mutex for the dir notify Irps.
// Also the rename resource.
//
InitializeListHead( &Vcb->DirNotifyList );
FsRtlNotifyInitializeSync( &Vcb->NotifySync );
//
// Allocate and initialize struct array for performance data. This
// attempt to allocate could raise STATUS_INSUFFICIENT_RESOURCES.
//
NumberProcessors = **((PCHAR *)&KeNumberProcessors);
Vcb->Statistics = NtfsAllocatePool( NonPagedPool,
sizeof(FILESYSTEM_STATISTICS) * NumberProcessors );
RtlZeroMemory( Vcb->Statistics, sizeof(FILESYSTEM_STATISTICS) * NumberProcessors );
for (i = 0; i < NumberProcessors; i += 1) {
Vcb->Statistics[i].FileSystemType = FILESYSTEM_STATISTICS_TYPE_NTFS;
Vcb->Statistics[i].Version = 1;
}
//
// Initialize the property tunneling structure
//
FsRtlInitializeTunnelCache(&Vcb->Tunnel);
//
// And return to our caller
//
DebugTrace( -1, Dbg, ("NtfsInitializeVcb -> VOID\n") );
return;
}
VOID
NtfsDeleteVcb (
IN PIRP_CONTEXT IrpContext,
IN OUT PVCB *Vcb
)
/*++
Routine Description:
This routine removes the Vcb record from Ntfs's in-memory data
structures.
Arguments:
Vcb - Supplies the Vcb to be removed
Return Value:
None
--*/
{
PVOLUME_DEVICE_OBJECT VolDo;
BOOLEAN AcquiredFcb;
PSCB Scb;
PFCB Fcb;
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_VCB( *Vcb );
ASSERTMSG("Cannot delete Vcb ", !FlagOn((*Vcb)->VcbState, VCB_STATE_VOLUME_MOUNTED));
PAGED_CODE();
DebugTrace( +1, Dbg, ("NtfsDeleteVcb, *Vcb = %08lx\n", *Vcb) );
//
// Make sure that we can really delete the vcb
//
ASSERT( (*Vcb)->CloseCount == 0 );
#ifdef _CAIRO_
NtOfsPurgeSecurityCache( *Vcb );
#endif
//
// If the Vcb log file object is present then we need to
// dereference it and uninitialize it through the cache.
//
if ((*Vcb)->LogFileObject != NULL) {
CACHE_UNINITIALIZE_EVENT UninitializeCompleteEvent;
NTSTATUS WaitStatus;
KeInitializeEvent( &UninitializeCompleteEvent.Event,
SynchronizationEvent,
FALSE);
CcUninitializeCacheMap( (*Vcb)->LogFileObject,
&NtfsLarge0,
&UninitializeCompleteEvent );
//
// Now wait for the cache manager to finish purging the file.
// This will garentee that Mm gets the purge before we
// delete the Vcb.
//
WaitStatus = KeWaitForSingleObject( &UninitializeCompleteEvent.Event,
Executive,
KernelMode,
FALSE,
NULL);
ASSERT( NT_SUCCESS( WaitStatus ) );
(*Vcb)->LogFileObject->FsContext = NULL;
(*Vcb)->LogFileObject->FsContext2 = NULL;
ObDereferenceObject( (*Vcb)->LogFileObject );
(*Vcb)->LogFileObject = NULL;
}
//
// Uninitialize the Mcb's for the deallocated cluster Mcb's.
//
if ((*Vcb)->PriorDeallocatedClusters != NULL) {
FsRtlUninitializeLargeMcb( &(*Vcb)->PriorDeallocatedClusters->Mcb );
(*Vcb)->PriorDeallocatedClusters = NULL;
}
if ((*Vcb)->ActiveDeallocatedClusters != NULL) {
FsRtlUninitializeLargeMcb( &(*Vcb)->ActiveDeallocatedClusters->Mcb );
(*Vcb)->ActiveDeallocatedClusters = NULL;
}
//
// Clean up the Root Lcb if present.
//
if ((*Vcb)->RootLcb != NULL) {
//
// Cleanup the Lcb so the DeleteLcb routine won't look at any
// other structures.
//
InitializeListHead( &(*Vcb)->RootLcb->ScbLinks );
InitializeListHead( &(*Vcb)->RootLcb->FcbLinks );
ClearFlag( (*Vcb)->RootLcb->LcbState,
LCB_STATE_EXACT_CASE_IN_TREE | LCB_STATE_IGNORE_CASE_IN_TREE );
NtfsDeleteLcb( IrpContext, &(*Vcb)->RootLcb );
(*Vcb)->RootLcb = NULL;
}
//
// Make sure the Fcb table is completely emptied. It is possible that an occasional Fcb
// (along with its Scb) will not be deleted when the file object closes come in.
//
while (TRUE) {
PVOID RestartKey;
//
// Always reinitialize the search so we get the first element in the tree.
//
RestartKey = NULL;
NtfsAcquireFcbTable( IrpContext, *Vcb );
Fcb = NtfsGetNextFcbTableEntry( *Vcb, &RestartKey );
NtfsReleaseFcbTable( IrpContext, *Vcb );
if (Fcb == NULL) { break; }
while ((Scb = NtfsGetNextChildScb( Fcb, NULL )) != NULL) {
NtfsDeleteScb( IrpContext, &Scb );
}
NtfsAcquireFcbTable( IrpContext, *Vcb );
NtfsDeleteFcb( IrpContext, &Fcb, &AcquiredFcb );
}
//
// Free the upcase table and attribute definitions. The upcase
// table only gets freed if it is not the global table.
//
if (((*Vcb)->UpcaseTable != NULL) && ((*Vcb)->UpcaseTable != NtfsData.UpcaseTable)) {
NtfsFreePool( (*Vcb)->UpcaseTable );
}
(*Vcb)->UpcaseTable = NULL;
if (((*Vcb)->AttributeDefinitions != NULL) &&
((*Vcb)->AttributeDefinitions != NtfsAttributeDefinitions)) {
NtfsFreePool( (*Vcb)->AttributeDefinitions );
(*Vcb)->AttributeDefinitions = NULL;
}
//
// Free the device name string if present.
//
if ((*Vcb)->DeviceName.Buffer != NULL) {
NtfsFreePool( (*Vcb)->DeviceName.Buffer );
(*Vcb)->DeviceName.Buffer = NULL;
}
FsRtlNotifyUninitializeSync( &(*Vcb)->NotifySync );
//
// We will free the structure allocated for the Lfs handle.
//
LfsDeleteLogHandle( (*Vcb)->LogHandle );
(*Vcb)->LogHandle = NULL;
//
// Delete the vcb resource and also free the restart tables
//
NtfsFreeRestartTable( &(*Vcb)->OpenAttributeTable );
NtfsFreeRestartTable( &(*Vcb)->TransactionTable );
//
// The Vpb in the Vcb may be a temporary Vpb and we should free it here.
//
if (FlagOn( (*Vcb)->VcbState, VCB_STATE_TEMP_VPB )) {
NtfsFreePool( (*Vcb)->Vpb );
(*Vcb)->Vpb = NULL;
}
ExDeleteResource( &(*Vcb)->Resource );
//
// Delete the space used to store performance counters.
//
if ((*Vcb)->Statistics != NULL) {
NtfsFreePool( (*Vcb)->Statistics );
(*Vcb)->Statistics = NULL;
}
//
// Tear down the file property tunneling structure
//
FsRtlDeleteTunnelCache(&(*Vcb)->Tunnel);
#ifdef NTFS_CHECK_BITMAP
if ((*Vcb)->BitmapCopy != NULL) {
ULONG Count = 0;
while (Count < (*Vcb)->BitmapPages) {
if (((*Vcb)->BitmapCopy + Count)->Buffer != NULL) {
NtfsFreePool( ((*Vcb)->BitmapCopy + Count)->Buffer );
}
Count += 1;
}
NtfsFreePool( (*Vcb)->BitmapCopy );
(*Vcb)->BitmapCopy = NULL;
}
#endif
//
// Return the Vcb (i.e., the VolumeDeviceObject) to pool and null out
// the input pointer to be safe
//
VolDo = CONTAINING_RECORD(*Vcb, VOLUME_DEVICE_OBJECT, Vcb);
IoDeleteDevice( (PDEVICE_OBJECT)VolDo );
*Vcb = NULL;
//
// And return to our caller
//
DebugTrace( -1, Dbg, ("NtfsDeleteVcb -> VOID\n") );
return;
}
PFCB
NtfsCreateRootFcb (
IN PIRP_CONTEXT IrpContext,
IN PVCB Vcb
)
/*++
Routine Description:
This routine allocates, initializes, and inserts a new root FCB record
into the in memory data structure. It also creates the necessary Root LCB
record and inserts the root name into the prefix table.
Arguments:
Vcb - Supplies the Vcb to associate with the new root Fcb and Lcb
Return Value:
PFCB - returns pointer to the newly allocated root FCB.
--*/
{
PFCB RootFcb;
PLCB RootLcb;
//
// The following variables are only used for abnormal termination
//
PVOID UnwindStorage = NULL;
PERESOURCE UnwindResource = NULL;
PFAST_MUTEX UnwindFastMutex = NULL;
PAGED_CODE();
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_VCB( Vcb );
DebugTrace( +1, Dbg, ("NtfsCreateRootFcb, Vcb = %08lx\n", Vcb) );
try {
//
// Allocate a new fcb and zero it out. We use Fcb locally so we
// don't have to continually go through the Vcb
//
RootFcb =
UnwindStorage = (PFCB)ExAllocateFromPagedLookasideList( &NtfsFcbIndexLookasideList );
RtlZeroMemory( RootFcb, sizeof(FCB_INDEX) );
//
// Set the proper node type code and byte size
//
RootFcb->NodeTypeCode = NTFS_NTC_FCB;
RootFcb->NodeByteSize = sizeof(FCB);
SetFlag( RootFcb->FcbState, FCB_STATE_COMPOUND_INDEX );
//
// Initialize the Lcb queue and point back to our Vcb.
//
InitializeListHead( &RootFcb->LcbQueue );
RootFcb->Vcb = Vcb;
//
// File Reference
//
NtfsSetSegmentNumber( &RootFcb->FileReference,
0,
ROOT_FILE_NAME_INDEX_NUMBER );
RootFcb->FileReference.SequenceNumber = ROOT_FILE_NAME_INDEX_NUMBER;
//
// Initialize the Scb
//
InitializeListHead( &RootFcb->ScbQueue );
//
// Allocate and initialize the resource variable
//
UnwindResource = RootFcb->Resource = NtfsAllocateEresource();
//
// Allocate and initialize the Fcb fast mutex.
//
UnwindFastMutex =
RootFcb->FcbMutex = NtfsAllocatePool( NonPagedPool, sizeof( FAST_MUTEX ));
ExInitializeFastMutex( UnwindFastMutex );
//
// Insert this new fcb into the fcb table
//
NtfsInsertFcbTableEntry( IrpContext, Vcb, RootFcb, RootFcb->FileReference );
SetFlag( RootFcb->FcbState, FCB_STATE_IN_FCB_TABLE );
//
// Now insert this new root fcb into it proper position in the graph with a
// root lcb. First allocate an initialize the root lcb and then build the
// lcb/scb graph.
//
{
//
// Use the root Lcb within the Fcb.
//
RootLcb = Vcb->RootLcb = (PLCB) &((PFCB_INDEX) RootFcb)->Lcb;
RootLcb->NodeTypeCode = NTFS_NTC_LCB;
RootLcb->NodeByteSize = sizeof(LCB);
//
// Insert the root lcb into the Root Fcb's queue
//
InsertTailList( &RootFcb->LcbQueue, &RootLcb->FcbLinks );
RootLcb->Fcb = RootFcb;
//
// Use the embedded file name attribute.
//
RootLcb->FileNameAttr = (PFILE_NAME) &RootLcb->ParentDirectory;
RootLcb->FileNameAttr->ParentDirectory = RootFcb->FileReference;
RootLcb->FileNameAttr->FileNameLength = 1;
RootLcb->FileNameAttr->Flags = FILE_NAME_NTFS | FILE_NAME_DOS;
RootLcb->ExactCaseLink.LinkName.Buffer = (PWCHAR) &RootLcb->FileNameAttr->FileName;
RootLcb->IgnoreCaseLink.LinkName.Buffer = Add2Ptr( RootLcb->FileNameAttr,
NtfsFileNameSizeFromLength( 2 ));
RootLcb->ExactCaseLink.LinkName.MaximumLength =
RootLcb->ExactCaseLink.LinkName.Length =
RootLcb->IgnoreCaseLink.LinkName.MaximumLength =
RootLcb->IgnoreCaseLink.LinkName.Length = 2;
RootLcb->ExactCaseLink.LinkName.Buffer[0] =
RootLcb->IgnoreCaseLink.LinkName.Buffer[0] = L'\\';
SetFlag( RootLcb->FileNameAttr->Flags, FILE_NAME_NTFS | FILE_NAME_DOS );
//
// Initialize both the ccb.
//
InitializeListHead( &RootLcb->CcbQueue );
}
} finally {
DebugUnwind( NtfsCreateRootFcb );
if (AbnormalTermination()) {
if (UnwindResource) { NtfsFreeEresource( UnwindResource ); }
if (UnwindStorage) { NtfsFreePool( UnwindStorage ); }
if (UnwindFastMutex) { NtfsFreePool( UnwindFastMutex ); }
}
}
DebugTrace( -1, Dbg, ("NtfsCreateRootFcb -> %8lx\n", RootFcb) );
return RootFcb;
}
PFCB
NtfsCreateFcb (
IN PIRP_CONTEXT IrpContext,
IN PVCB Vcb,
IN FILE_REFERENCE FileReference,
IN BOOLEAN IsPagingFile,
IN BOOLEAN LargeFcb,
OUT PBOOLEAN ReturnedExistingFcb OPTIONAL
)
/*++
Routine Description:
This routine allocates and initializes a new Fcb record. The record
is not placed within the Fcb/Scb graph but is only inserted in the
FcbTable.
Arguments:
Vcb - Supplies the Vcb to associate the new FCB under.
FileReference - Supplies the file reference to use to identify the
Fcb with. We will search the Fcb table for any preexisting
Fcb's with the same file reference number.
IsPagingFile - Indicates if we are creating an FCB for a paging file
or some other type of file.
LargeFcb - Indicates if we should use the larger of the compound Fcb's.
ReturnedExistingFcb - Optionally indicates to the caller if the
returned Fcb already existed
Return Value:
PFCB - Returns a pointer to the newly allocated FCB
--*/
{
FCB_TABLE_ELEMENT Key;
PFCB_TABLE_ELEMENT Entry;
PFCB Fcb;
BOOLEAN LocalReturnedExistingFcb;
//
// The following variables are only used for abnormal termination
//
PVOID UnwindStorage = NULL;
PERESOURCE UnwindResource = NULL;
PFAST_MUTEX UnwindFastMutex = NULL;
PAGED_CODE();
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_VCB( Vcb );
DebugTrace( +1, Dbg, ("NtfsCreateFcb\n") );
if (!ARGUMENT_PRESENT(ReturnedExistingFcb)) { ReturnedExistingFcb = &LocalReturnedExistingFcb; }
//
// First search the FcbTable for a matching fcb
//
Key.FileReference = FileReference;
Fcb = NULL;
if ((Entry = RtlLookupElementGenericTable( &Vcb->FcbTable, &Key )) != NULL) {
Fcb = Entry->Fcb;
//
// It's possible that this Fcb has been deleted but in truncating and
// growing the Mft we are reusing some of the file references.
// If this file has been deleted but the Fcb is waiting around for
// closes, we will remove it from the Fcb table and create a new Fcb
// below.
//
if (FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED )) {
//
// Remove it from the Fcb table and remember to create an
// Fcb below.
//
NtfsDeleteFcbTableEntry( Fcb->Vcb,
Fcb->FileReference );
ClearFlag( Fcb->FcbState, FCB_STATE_IN_FCB_TABLE );
Fcb = NULL;
} else {
*ReturnedExistingFcb = TRUE;
}
}
//
// Now check if we have an Fcb.
//
if (Fcb == NULL) {
*ReturnedExistingFcb = FALSE;
try {
//
// Allocate a new FCB and zero it out.
//
if (IsPagingFile ||
NtfsSegmentNumber( &FileReference ) <= MASTER_FILE_TABLE2_NUMBER ||
NtfsSegmentNumber( &FileReference ) == BAD_CLUSTER_FILE_NUMBER ||
NtfsSegmentNumber( &FileReference ) == BIT_MAP_FILE_NUMBER) {
Fcb = UnwindStorage = NtfsAllocatePoolWithTag( NonPagedPool,
sizeof(FCB),
'fftN' );
RtlZeroMemory( Fcb, sizeof(FCB) );
if (IsPagingFile) {
SetFlag( Fcb->FcbState, FCB_STATE_PAGING_FILE );
//
// We don't want to dismount this volume now that
// we have a pagefile open on it.
//
SetFlag( Vcb->VcbState, VCB_STATE_DISALLOW_DISMOUNT );
}
SetFlag( Fcb->FcbState, FCB_STATE_NONPAGED );
} else {
if (LargeFcb) {
Fcb = UnwindStorage =
(PFCB)ExAllocateFromPagedLookasideList( &NtfsFcbIndexLookasideList );
RtlZeroMemory( Fcb, sizeof( FCB_INDEX ));
SetFlag( Fcb->FcbState, FCB_STATE_COMPOUND_INDEX );
} else {
Fcb = UnwindStorage =
(PFCB)ExAllocateFromPagedLookasideList( &NtfsFcbDataLookasideList );
RtlZeroMemory( Fcb, sizeof( FCB_DATA ));
SetFlag( Fcb->FcbState, FCB_STATE_COMPOUND_DATA );
}
}
//
// Set the proper node type code and byte size
//
Fcb->NodeTypeCode = NTFS_NTC_FCB;
Fcb->NodeByteSize = sizeof(FCB);
//
// Initialize the Lcb queue and point back to our Vcb, and indicate
// that we are a directory
//
InitializeListHead( &Fcb->LcbQueue );
Fcb->Vcb = Vcb;
//
// File Reference
//
Fcb->FileReference = FileReference;
//
// Initialize the Scb
//
InitializeListHead( &Fcb->ScbQueue );
//
// Allocate and initialize the resource variable
//
UnwindResource = Fcb->Resource = NtfsAllocateEresource();
//
// Allocate and initialize fast mutex for the Fcb.
//
UnwindFastMutex = Fcb->FcbMutex = NtfsAllocatePool( NonPagedPool, sizeof( FAST_MUTEX ));
ExInitializeFastMutex( UnwindFastMutex );
//
// Insert this new fcb into the fcb table
//
NtfsInsertFcbTableEntry( IrpContext, Vcb, Fcb, FileReference );
SetFlag( Fcb->FcbState, FCB_STATE_IN_FCB_TABLE );
//
// Set the flag to indicate if this is a system file.
//
if (NtfsSegmentNumber( &FileReference ) <= FIRST_USER_FILE_NUMBER) {
SetFlag( Fcb->FcbState, FCB_STATE_SYSTEM_FILE );
}
} finally {
DebugUnwind( NtfsCreateFcb );
if (AbnormalTermination()) {
if (UnwindFastMutex) { NtfsFreePool( UnwindFastMutex ); }
if (UnwindResource) { NtfsFreeEresource( UnwindResource ); }
if (UnwindStorage) { NtfsFreePool( UnwindStorage ); }
}
}
}
DebugTrace( -1, Dbg, ("NtfsCreateFcb -> %08lx\n", Fcb) );
return Fcb;
}
VOID
NtfsDeleteFcb (
IN PIRP_CONTEXT IrpContext,
IN OUT PFCB *Fcb,
OUT PBOOLEAN AcquiredFcbTable
)
/*++
Routine Description:
This routine deallocates and removes an FCB record from all Ntfs's in-memory
data structures. It assumes that it does not have anything Scb children nor
does it have any lcb edges going into it at the time of the call.
Arguments:
Fcb - Supplies the FCB to be removed
AcquiredFcbTable - Set to FALSE when this routine releases the
FcbTable.
Return Value:
None
--*/
{
PAGED_CODE();
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_FCB( *Fcb );
ASSERT( IsListEmpty(&(*Fcb)->ScbQueue) );
ASSERT( (NodeType(*Fcb) == NTFS_NTC_FCB) );
DebugTrace( +1, Dbg, ("NtfsDeleteFcb, *Fcb = %08lx\n", *Fcb) );
//
// First free any possible Scb snapshots.
//
NtfsFreeSnapshotsForFcb( IrpContext, *Fcb );
//
// This Fcb may be in the ExclusiveFcb list of the IrpContext.
// If it is (The Flink is not NULL), we remove it.
// And release the global resource.
//
if ((*Fcb)->ExclusiveFcbLinks.Flink != NULL) {
RemoveEntryList( &(*Fcb)->ExclusiveFcbLinks );
}
//
// Clear the IrpContext field for any request which may own the paging
// IO resource for this Fcb.
//
if (IrpContext->FcbWithPagingExclusive == *Fcb) {
IrpContext->FcbWithPagingExclusive = NULL;
} else if (IrpContext->TopLevelIrpContext->FcbWithPagingExclusive == *Fcb) {
IrpContext->TopLevelIrpContext->FcbWithPagingExclusive = NULL;
}
//
// Deallocate the resources protecting the Fcb
//
ASSERT((*Fcb)->Resource->NumberOfSharedWaiters == 0);
ASSERT((*Fcb)->Resource->NumberOfExclusiveWaiters == 0);
#ifdef NTFSDBG
//
// Either we own the FCB or nobody should own it. The extra acquire
// here does not matter since we will free the resource below.
//
ASSERT(ExAcquireResourceExclusiveLite( (*Fcb)->Resource, FALSE ));
#endif
NtfsFreeEresource( (*Fcb)->Resource );
if ( (*Fcb)->PagingIoResource != NULL ) {
if (IrpContext->FcbWithPagingExclusive == *Fcb) {
IrpContext->FcbWithPagingExclusive = NULL;
}
NtfsFreeEresource( (*Fcb)->PagingIoResource );
}
//
// Deallocate the fast mutex.
//
if ((*Fcb)->FcbMutex != NULL) {
NtfsFreePool( (*Fcb)->FcbMutex );
}
//
// Remove the fcb from the fcb table if present.
//
if (FlagOn( (*Fcb)->FcbState, FCB_STATE_IN_FCB_TABLE )) {
NtfsDeleteFcbTableEntry( (*Fcb)->Vcb, (*Fcb)->FileReference );
ClearFlag( (*Fcb)->FcbState, FCB_STATE_IN_FCB_TABLE );
}
NtfsReleaseFcbTable( IrpContext, (*Fcb)->Vcb );
*AcquiredFcbTable = FALSE;
//
// Dereference and possibly deallocate the security descriptor if present.
//
NtfsAcquireFcbSecurity( (*Fcb)->Vcb );
if ((*Fcb)->SharedSecurity != NULL) {
NtfsDereferenceSharedSecurity( *Fcb );
}
//
// Now check and free if we have a security descriptor for children.
//
if ((*Fcb)->ChildSharedSecurity != NULL) {
(*Fcb)->ChildSharedSecurity->ReferenceCount -= 1;
(*Fcb)->ChildSharedSecurity->ParentFcb = NULL;
//
// We can deallocate the structure if we are the last
// reference to this structure.
//
if ((*Fcb)->ChildSharedSecurity->ReferenceCount == 0) {
NtfsFreePool( (*Fcb)->ChildSharedSecurity );
}
(*Fcb)->ChildSharedSecurity = NULL;
}
NtfsReleaseFcbSecurity( (*Fcb)->Vcb );
#ifdef _CAIRO_
//
// Release the quota control block.
//
if (NtfsPerformQuotaOperation( *Fcb )) {
NtfsDereferenceQuotaControlBlock( (*Fcb)->Vcb, &(*Fcb)->QuotaControl );
}
#endif // _CAIRO_
//
// Deallocate the Fcb itself
//
if (FlagOn( (*Fcb)->FcbState, FCB_STATE_NONPAGED )) {
NtfsFreePool( *Fcb );
} else {
if (FlagOn( (*Fcb)->FcbState, FCB_STATE_COMPOUND_INDEX )) {
ExFreeToPagedLookasideList( &NtfsFcbIndexLookasideList, *Fcb );
} else {
ExFreeToPagedLookasideList( &NtfsFcbDataLookasideList, *Fcb );
}
}
//
// Zero out the input pointer
//
*Fcb = NULL;
//
// And return to our caller
//
DebugTrace( -1, Dbg, ("NtfsDeleteFcb -> VOID\n") );
return;
}
PFCB
NtfsGetNextFcbTableEntry (
IN PVCB Vcb,
IN PVOID *RestartKey
)
/*++
Routine Description:
This routine will enumerate through all of the fcb's for the given
vcb
Arguments:
Vcb - Supplies the Vcb used in this operation
RestartKey - This value is used by the table package to maintain
its position in the enumeration. It is initialized to NULL
for the first search.
Return Value:
PFCB - A pointer to the next fcb or NULL if the enumeration is
completed
--*/
{
PFCB Fcb;
PAGED_CODE();
Fcb = (PFCB) RtlEnumerateGenericTableWithoutSplaying( &Vcb->FcbTable, RestartKey );
if (Fcb != NULL) {
Fcb = ((PFCB_TABLE_ELEMENT)(Fcb))->Fcb;
}
return Fcb;
}
PSCB
NtfsCreateScb (
IN PIRP_CONTEXT IrpContext,
IN PFCB Fcb,
IN ATTRIBUTE_TYPE_CODE AttributeTypeCode,
IN PUNICODE_STRING AttributeName,
IN BOOLEAN ReturnExistingOnly,
OUT PBOOLEAN ReturnedExistingScb OPTIONAL
)
/*++
Routine Description:
This routine allocates, initializes, and inserts a new Scb record into
the in memory data structures, provided one does not already exist
with the identical attribute record.
Arguments:
Fcb - Supplies the Fcb to associate the new SCB under.
AttributeTypeCode - Supplies the attribute type code for the new Scb
AttributeName - Supplies the attribute name for the new Scb, with
AttributeName->Length == 0 if there is no name.
ReturnExistingOnly - If specified as TRUE then only an existing Scb
will be returned. If no matching Scb exists then NULL is returned.
ReturnedExistingScb - Indicates if this procedure found an existing
Scb with the identical attribute record (variable is set to TRUE)
or if this procedure needed to create a new Scb (variable is set to
FALSE).
Return Value:
PSCB - Returns a pointer to the newly allocated SCB or NULL if there is
no Scb and ReturnExistingOnly is TRUE.
--*/
{
PSCB Scb;
NODE_TYPE_CODE NodeTypeCode;
NODE_BYTE_SIZE NodeByteSize;
BOOLEAN LocalReturnedExistingScb;
BOOLEAN PagingIoResource = FALSE;
#ifdef SYSCACHE
BOOLEAN SyscacheFile = FALSE;
#endif
//
// The following variables are only used for abnormal termination
//
PVOID UnwindStorage[4] = { NULL, NULL, NULL, NULL };
POPLOCK UnwindOplock = NULL;
PNTFS_MCB UnwindMcb = NULL;
PLARGE_MCB UnwindAddedClustersMcb = NULL;
PLARGE_MCB UnwindRemovedClustersMcb = NULL;
BOOLEAN UnwindFromQueue = FALSE;
BOOLEAN Nonpaged = FALSE;
PAGED_CODE();
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_FCB( Fcb );
ASSERT( AttributeTypeCode >= $STANDARD_INFORMATION );
DebugTrace( +1, Dbg, ("NtfsCreateScb\n") );
if (!ARGUMENT_PRESENT(ReturnedExistingScb)) { ReturnedExistingScb = &LocalReturnedExistingScb; }
//
// Search the scb queue of the fcb looking for a matching
// attribute type code and attribute name
//
NtfsLockFcb( IrpContext, Fcb );
Scb = NULL;
while ((Scb = NtfsGetNextChildScb( Fcb, Scb )) != NULL) {
ASSERT_SCB( Scb );
//
// For every scb already in the fcb's queue check for a matching
// type code and name. If we find a match we return from this
// procedure right away.
//
if (!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED) &&
(AttributeTypeCode == Scb->AttributeTypeCode) &&
NtfsAreNamesEqual( IrpContext->Vcb->UpcaseTable, &Scb->AttributeName, AttributeName, FALSE )) {
*ReturnedExistingScb = TRUE;
NtfsUnlockFcb( IrpContext, Fcb );
if (NtfsIsExclusiveScb(Scb)) {
NtfsSnapshotScb( IrpContext, Scb );
}
DebugTrace( -1, Dbg, ("NtfsCreateScb -> %08lx\n", Scb) );
return Scb;
}
}
//
// If the user only wanted an existing Scb then return NULL.
//
if (ReturnExistingOnly) {
NtfsUnlockFcb( IrpContext, Fcb );
DebugTrace( -1, Dbg, ("NtfsCreateScb -> %08lx\n", NULL) );
return NULL;
}
//
// We didn't find it so we are not going to be returning an existing Scb
//
*ReturnedExistingScb = FALSE;
try {
//
// Decide the node type and size of the Scb. Also decide if it will be
// allocated from paged or non-paged pool.
//
if (AttributeTypeCode == $INDEX_ALLOCATION) {
if (NtfsSegmentNumber( &Fcb->FileReference ) == ROOT_FILE_NAME_INDEX_NUMBER) {
NodeTypeCode = NTFS_NTC_SCB_ROOT_INDEX;
} else {
NodeTypeCode = NTFS_NTC_SCB_INDEX;
}
NodeByteSize = SIZEOF_SCB_INDEX;
} else if (NtfsSegmentNumber( &Fcb->FileReference ) <= MASTER_FILE_TABLE2_NUMBER
&& (AttributeTypeCode == $DATA)) {
NodeTypeCode = NTFS_NTC_SCB_MFT;
NodeByteSize = SIZEOF_SCB_MFT;
} else {
NodeTypeCode = NTFS_NTC_SCB_DATA;
NodeByteSize = SIZEOF_SCB_DATA;
//
// If this is a user data stream then remember that we need
// a paging IO resource.
//
if (((NtfsSegmentNumber( &Fcb->FileReference ) == ROOT_FILE_NAME_INDEX_NUMBER) ||
(NtfsSegmentNumber( &Fcb->FileReference ) == VOLUME_DASD_NUMBER) ||
(NtfsSegmentNumber( &Fcb->FileReference ) == BIT_MAP_FILE_NUMBER) ||
#ifdef _CAIRO_
(NtfsSegmentNumber( &Fcb->FileReference ) == QUOTA_TABLE_NUMBER) ||
#endif
(NtfsSegmentNumber( &Fcb->FileReference ) >= FIRST_USER_FILE_NUMBER))
&& (NtfsIsTypeCodeUserData( AttributeTypeCode ) ||
(AttributeTypeCode == $EA) ||
(AttributeTypeCode >= $FIRST_USER_DEFINED_ATTRIBUTE))) {
//
// Remember that this stream needs a paging io resource.
//
PagingIoResource = TRUE;
}
}
//
// The scb will come from non-paged if the Fcb is non-paged or
// it is an attribute list.
//
if (FlagOn( Fcb->FcbState, FCB_STATE_NONPAGED ) ||
(AttributeTypeCode == $ATTRIBUTE_LIST)) {
Scb = UnwindStorage[0] = NtfsAllocatePoolWithTag( NonPagedPool, NodeByteSize, 'nftN' );
Nonpaged = TRUE;
} else if (AttributeTypeCode == $INDEX_ALLOCATION) {
//
// If the Fcb is an INDEX Fcb and the Scb is unused, then
// use that. Otherwise allocate from the lookaside list.
//
if (FlagOn( Fcb->FcbState, FCB_STATE_COMPOUND_INDEX ) &&
(SafeNodeType( &((PFCB_INDEX) Fcb)->Scb ) == 0)) {
Scb = (PSCB) &((PFCB_INDEX) Fcb)->Scb;
} else {
Scb = UnwindStorage[0] = (PSCB)NtfsAllocatePoolWithTag( PagedPool, SIZEOF_SCB_INDEX, 'SftN' );
}
} else {
//
// We can use the Scb field in the Fcb in all cases if it is
// unused. We will only use it for a data stream since
// it will have the longest life.
//
if ((AttributeTypeCode == $DATA) &&
(SafeNodeType( &((PFCB_INDEX) Fcb)->Scb ) == 0)) {
Scb = (PSCB) &((PFCB_INDEX) Fcb)->Scb;
} else {
Scb = UnwindStorage[0] = (PSCB)ExAllocateFromPagedLookasideList( &NtfsScbDataLookasideList );
}
#ifdef SYSCACHE
if (FsRtlIsSyscacheFile(IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)->FileObject)) {
SyscacheFile = TRUE;
}
#endif
}
//
// Store the Scb address and zero it out.
//
RtlZeroMemory( Scb, NodeByteSize );
#ifdef SYSCACHE
if (SyscacheFile) {
SetFlag( Scb->ScbState, SCB_STATE_SYSCACHE_FILE );
}
#endif
//
// Set the proper node type code and byte size
//
Scb->Header.NodeTypeCode = NodeTypeCode;
Scb->Header.NodeByteSize = NodeByteSize;
//
// Set a back pointer to the resource we will be using
//
Scb->Header.Resource = Fcb->Resource;
//
// Decide if we will be using the PagingIoResource
//
if (PagingIoResource) {
//
// Initialize it in the Fcb if it is not already there, and
// setup the pointer and flag in the Scb.
//
if (Fcb->PagingIoResource == NULL) {
Fcb->PagingIoResource = NtfsAllocateEresource();
}
Scb->Header.PagingIoResource = Fcb->PagingIoResource;
}
//
// Insert this Scb into our parents scb queue, and point back to
// our parent fcb, and vcb
//
InsertTailList( &Fcb->ScbQueue, &Scb->FcbLinks );
UnwindFromQueue = TRUE;
Scb->Fcb = Fcb;
Scb->Vcb = Fcb->Vcb;
//
// If the attribute name exists then allocate a buffer for the
// attribute name and iniitalize it.
//
if (AttributeName->Length != 0) {
//
// The typical case is the $I30 string. If this matches then
// point to a common string.
//
if ((AttributeName->Length == NtfsFileNameIndex.Length) &&
(RtlEqualMemory( AttributeName->Buffer,
NtfsFileNameIndex.Buffer,
AttributeName->Length ) )) {
Scb->AttributeName = NtfsFileNameIndex;
} else {
Scb->AttributeName.Length = AttributeName->Length;
Scb->AttributeName.MaximumLength = (USHORT)(AttributeName->Length + 2);
Scb->AttributeName.Buffer = UnwindStorage[1] = NtfsAllocatePool(PagedPool, AttributeName->Length + 2 );
RtlCopyMemory( Scb->AttributeName.Buffer, AttributeName->Buffer, AttributeName->Length );
Scb->AttributeName.Buffer[AttributeName->Length / 2] = L'\0';
}
}
//
// Set the attribute Type Code
//
Scb->AttributeTypeCode = AttributeTypeCode;
if (NtfsIsTypeCodeSubjectToQuota( AttributeTypeCode )){
SetFlag( Scb->ScbState, SCB_STATE_SUBJECT_TO_QUOTA );
}
//
// If this is an Mft Scb then initialize the cluster Mcb's.
//
if (NodeTypeCode == NTFS_NTC_SCB_MFT) {
FsRtlInitializeLargeMcb( &Scb->ScbType.Mft.AddedClusters, NonPagedPool );
UnwindAddedClustersMcb = &Scb->ScbType.Mft.AddedClusters;
FsRtlInitializeLargeMcb( &Scb->ScbType.Mft.RemovedClusters, NonPagedPool );
UnwindRemovedClustersMcb = &Scb->ScbType.Mft.RemovedClusters;
}
//
// Get the mutex for the Scb. We may be able to use the one in the Fcb.
// We can if the Scb is paged.
//
if (Nonpaged) {
SetFlag( Scb->ScbState, SCB_STATE_NONPAGED );
UnwindStorage[3] =
Scb->Header.FastMutex = NtfsAllocatePool( NonPagedPool, sizeof( FAST_MUTEX ));
ExInitializeFastMutex( Scb->Header.FastMutex );
} else {
Scb->Header.FastMutex = Fcb->FcbMutex;
}
//
// Allocate the Nonpaged portion of the Scb.
//
Scb->NonpagedScb =
UnwindStorage[2] = (PSCB_NONPAGED)ExAllocateFromNPagedLookasideList( &NtfsScbNonpagedLookasideList );
RtlZeroMemory( Scb->NonpagedScb, sizeof( SCB_NONPAGED ));
Scb->NonpagedScb->NodeTypeCode = NTFS_NTC_SCB_NONPAGED;
Scb->NonpagedScb->NodeByteSize = sizeof( SCB_NONPAGED );
Scb->NonpagedScb->Vcb = Scb->Vcb;
//
// Fill in the advanced fields
//
SetFlag( Scb->Header.Flags, FSRTL_FLAG_ADVANCED_HEADER );
Scb->Header.PendingEofAdvances = &Scb->EofListHead;
InitializeListHead( &Scb->EofListHead );
Scb->Header.SectionObjectPointers = &Scb->NonpagedScb->SegmentObject;
NtfsInitializeNtfsMcb( &Scb->Mcb,
&Scb->Header,
&Scb->McbStructs,
FlagOn( Scb->ScbState, SCB_STATE_NONPAGED )
? NonPagedPool : PagedPool);
UnwindMcb = &Scb->Mcb;
//
// Do that data stream specific initialization.
//
if (NodeTypeCode == NTFS_NTC_SCB_DATA) {
FsRtlInitializeOplock( &Scb->ScbType.Data.Oplock );
UnwindOplock = &Scb->ScbType.Data.Oplock;
} else {
//
// There is a deallocated queue for indexes and the Mft.
//
InitializeListHead( &Scb->ScbType.Index.RecentlyDeallocatedQueue );
//
// Initialize index-specific fields.
//
if (AttributeTypeCode == $INDEX_ALLOCATION) {
InitializeListHead( &Scb->ScbType.Index.LcbQueue );
}
}
//
// If this Scb should be marked as containing Lsn's or
// Update Sequence Arrays, do so now.
//
NtfsCheckScbForCache( Scb );
} finally {
DebugUnwind( NtfsCreateScb );
NtfsUnlockFcb( IrpContext, Fcb );
if (AbnormalTermination()) {
if (UnwindFromQueue) { RemoveEntryList( &Scb->FcbLinks ); }
if (UnwindMcb != NULL) { NtfsUninitializeNtfsMcb( UnwindMcb ); }
if (UnwindAddedClustersMcb != NULL) { FsRtlUninitializeLargeMcb( UnwindAddedClustersMcb ); }
if (UnwindRemovedClustersMcb != NULL) { FsRtlUninitializeLargeMcb( UnwindRemovedClustersMcb ); }
if (UnwindOplock != NULL) { FsRtlUninitializeOplock( UnwindOplock ); }
if (UnwindStorage[0]) { NtfsFreePool( UnwindStorage[0] );
} else if (Scb != NULL) { Scb->Header.NodeTypeCode = 0; }
if (UnwindStorage[1]) { NtfsFreePool( UnwindStorage[1] ); }
if (UnwindStorage[2]) { NtfsFreePool( UnwindStorage[2] ); }
if (UnwindStorage[3]) { NtfsFreePool( UnwindStorage[3] ); }
}
}
DebugTrace( -1, Dbg, ("NtfsCreateScb -> %08lx\n", Scb) );
return Scb;
}
PSCB
NtfsCreatePrerestartScb (
IN PIRP_CONTEXT IrpContext,
IN PVCB Vcb,
IN PFILE_REFERENCE FileReference,
IN ATTRIBUTE_TYPE_CODE AttributeTypeCode,
IN PUNICODE_STRING AttributeName OPTIONAL,
IN ULONG BytesPerIndexBuffer
)
/*++
Routine Description:
This routine allocates, initializes, and inserts a new Scb record into
the in memory data structures, provided one does not already exist
with the identical attribute record. It does this on the FcbTable
off of the Vcb. If necessary this routine will also create the fcb
if one does not already exist for the indicated file reference.
Arguments:
Vcb - Supplies the Vcb to associate the new SCB under.
FileReference - Supplies the file reference for the new SCB this is
used to identify/create a new lookaside Fcb.
AttributeTypeCode - Supplies the attribute type code for the new SCB
AttributeName - Supplies the optional attribute name of the SCB
BytesPerIndexBuffer - For index Scbs, this must specify the bytes per
index buffer.
Return Value:
PSCB - Returns a pointer to the newly allocated SCB
--*/
{
PSCB Scb;
PFCB Fcb;
NODE_TYPE_CODE NodeTypeCode;
NODE_BYTE_SIZE NodeByteSize;
PAGED_CODE();
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_VCB( Vcb );
ASSERT( AttributeTypeCode >= $STANDARD_INFORMATION );
DebugTrace( +1, Dbg, ("NtfsCreatePrerestartScb\n") );
//
// First make sure we have an Fcb of the proper file reference
// and indicate that it is from prerestart
//
Fcb = NtfsCreateFcb( IrpContext,
Vcb,
*FileReference,
FALSE,
TRUE,
NULL );
//
// Search the child scbs of this fcb for a matching Scb (based on
// attribute type code and attribute name) if one is not found then
// we'll create a new scb. When we exit the following loop if the
// scb pointer to not null then we've found a preexisting scb.
//
Scb = NULL;
while ((Scb = NtfsGetNextChildScb(Fcb, Scb)) != NULL) {
ASSERT_SCB( Scb );
//
// The the attribute type codes match and if supplied the name also
// matches then we got our scb
//
if (Scb->AttributeTypeCode == AttributeTypeCode) {
if (!ARGUMENT_PRESENT( AttributeName )) {
if (Scb->AttributeName.Length == 0) {
break;
}
} else if (AttributeName->Length == 0
&& Scb->AttributeName.Length == 0) {
break;
} else if (NtfsAreNamesEqual( IrpContext->Vcb->UpcaseTable,
AttributeName,
&Scb->AttributeName,
FALSE )) { // Ignore Case
break;
}
}
}
//
// If scb now null then we need to create a minimal scb. We always allocate
// these out of non-paged pool.
//
if (Scb == NULL) {
BOOLEAN ShareScb = FALSE;
//
// Allocate new scb and zero it out and set the node type code and byte size.
//
if (AttributeTypeCode == $INDEX_ALLOCATION) {
if (NtfsSegmentNumber( FileReference ) == ROOT_FILE_NAME_INDEX_NUMBER) {
NodeTypeCode = NTFS_NTC_SCB_ROOT_INDEX;
} else {
NodeTypeCode = NTFS_NTC_SCB_INDEX;
}
NodeByteSize = SIZEOF_SCB_INDEX;
} else if (NtfsSegmentNumber( FileReference ) <= MASTER_FILE_TABLE2_NUMBER
&& (AttributeTypeCode == $DATA)) {
NodeTypeCode = NTFS_NTC_SCB_MFT;
NodeByteSize = SIZEOF_SCB_MFT;
} else {
NodeTypeCode = NTFS_NTC_SCB_DATA;
NodeByteSize = SIZEOF_SCB_DATA;
}
Scb = NtfsAllocatePoolWithTag( NonPagedPool, NodeByteSize, 'tftN' );
RtlZeroMemory( Scb, NodeByteSize );
//
// Fill in the node type code and size.
//
Scb->Header.NodeTypeCode = NodeTypeCode;
Scb->Header.NodeByteSize = NodeByteSize;
//
// Show that all of the Scb's are from nonpaged pool.
//
SetFlag( Scb->ScbState, SCB_STATE_NONPAGED );
//
// Set a back pointer to the resource we will be using
//
Scb->Header.Resource = Fcb->Resource;
//
// Insert this scb into our parents scb queue and point back to our
// parent fcb and vcb
//
InsertTailList( &Fcb->ScbQueue, &Scb->FcbLinks );
Scb->Fcb = Fcb;
Scb->Vcb = Vcb;
//
// If the attribute name is present and the name length is greater than 0
// then allocate a buffer for the attribute name and initialize it.
//
if (ARGUMENT_PRESENT( AttributeName ) && (AttributeName->Length != 0)) {
//
// The typical case is the $I30 string. If this matches then
// point to a common string.
//
if ((AttributeName->Length == NtfsFileNameIndex.Length) &&
(RtlEqualMemory( AttributeName->Buffer,
NtfsFileNameIndex.Buffer,
AttributeName->Length ) )) {
Scb->AttributeName = NtfsFileNameIndex;
} else {
Scb->AttributeName.Length = AttributeName->Length;
Scb->AttributeName.MaximumLength = (USHORT)(AttributeName->Length + 2);
Scb->AttributeName.Buffer = NtfsAllocatePool(PagedPool, AttributeName->Length + 2 );
RtlCopyMemory( Scb->AttributeName.Buffer, AttributeName->Buffer, AttributeName->Length );
Scb->AttributeName.Buffer[AttributeName->Length/2] = L'\0';
}
}
//
// Set the attribute type code recently deallocated information structures.
//
Scb->AttributeTypeCode = AttributeTypeCode;
//
// If this is an Mft Scb then initialize the cluster Mcb's.
//
if (NodeTypeCode == NTFS_NTC_SCB_MFT) {
FsRtlInitializeLargeMcb( &Scb->ScbType.Mft.AddedClusters, NonPagedPool );
FsRtlInitializeLargeMcb( &Scb->ScbType.Mft.RemovedClusters, NonPagedPool );
}
Scb->NonpagedScb = (PSCB_NONPAGED)ExAllocateFromNPagedLookasideList( &NtfsScbNonpagedLookasideList );
RtlZeroMemory( Scb->NonpagedScb, sizeof( SCB_NONPAGED ));
Scb->NonpagedScb->NodeTypeCode = NTFS_NTC_SCB_NONPAGED;
Scb->NonpagedScb->NodeByteSize = sizeof( SCB_NONPAGED );
Scb->NonpagedScb->Vcb = Vcb;
//
// Fill in the advanced fields
//
SetFlag( Scb->Header.Flags, FSRTL_FLAG_ADVANCED_HEADER );
Scb->Header.PendingEofAdvances = &Scb->EofListHead;
InitializeListHead( &Scb->EofListHead );
Scb->Header.SectionObjectPointers = &Scb->NonpagedScb->SegmentObject;
Scb->Header.FastMutex = NtfsAllocatePool( NonPagedPool, sizeof( FAST_MUTEX ));
ExInitializeFastMutex( Scb->Header.FastMutex );
NtfsInitializeNtfsMcb( &Scb->Mcb, &Scb->Header, &Scb->McbStructs, NonPagedPool );
//
// Do that data stream specific initialization.
//
if (NodeTypeCode == NTFS_NTC_SCB_DATA) {
FsRtlInitializeOplock( &Scb->ScbType.Data.Oplock );
} else {
//
// There is a deallocated queue for indexes and the Mft.
//
InitializeListHead( &Scb->ScbType.Index.RecentlyDeallocatedQueue );
//
// Initialize index-specific fields.
//
if (AttributeTypeCode == $INDEX_ALLOCATION) {
Scb->ScbType.Index.BytesPerIndexBuffer = BytesPerIndexBuffer;
InitializeListHead( &Scb->ScbType.Index.LcbQueue );
}
}
//
// If this Scb should be marked as containing Lsn's or
// Update Sequence Arrays, do so now.
//
NtfsCheckScbForCache( Scb );
}
DebugTrace( -1, Dbg, ("NtfsCreatePrerestartScb -> %08lx\n", Scb) );
return Scb;
}
VOID
NtfsDeleteScb (
IN PIRP_CONTEXT IrpContext,
IN OUT PSCB *Scb
)
/*++
Routine Description:
This routine deallocates and removes an Scb record
from Ntfs's in-memory data structures. It assume that is does not have
any children lcb emanating from it.
Arguments:
Scb - Supplies the SCB to be removed
Return Value:
None.
--*/
{
PVCB Vcb;
PFCB Fcb;
POPEN_ATTRIBUTE_ENTRY AttributeEntry;
USHORT ThisNodeType;
PAGED_CODE();
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_SCB( *Scb );
ASSERT( (*Scb)->CleanupCount == 0 );
DebugTrace( +1, Dbg, ("NtfsDeleteScb, *Scb = %08lx\n", *Scb) );
Fcb = (*Scb)->Fcb;
Vcb = Fcb->Vcb;
RemoveEntryList( &(*Scb)->FcbLinks );
ThisNodeType = SafeNodeType( *Scb );
//
// If this is a bitmap Scb for a directory then make sure the record
// allocation structure is uninitialized. Otherwise we will leave a
// stale pointer for the record allocation package.
//
if (((*Scb)->AttributeTypeCode == $BITMAP) &&
IsDirectory( &Fcb->Info)) {
PLIST_ENTRY Links;
PSCB IndexAllocationScb;
Links = Fcb->ScbQueue.Flink;
while (Links != &Fcb->ScbQueue) {
IndexAllocationScb = CONTAINING_RECORD( Links, SCB, FcbLinks );
if (IndexAllocationScb->AttributeTypeCode == $INDEX_ALLOCATION) {
NtfsUninitializeRecordAllocation( IrpContext,
&IndexAllocationScb->ScbType.Index.RecordAllocationContext );
IndexAllocationScb->ScbType.Index.AllocationInitialized = FALSE;
break;
}
Links = Links->Flink;
}
}
//
// Delete the write mask, if one is being maintained.
//
#ifdef SYSCACHE
if ((ThisNodeType == NTFS_NTC_SCB_DATA) &&
((*Scb)->ScbType.Data.WriteMask != (PULONG)NULL)) {
NtfsFreePool((*Scb)->ScbType.Data.WriteMask);
}
#endif
//
// Mark our entry in the Open Attribute Table as free,
// although it will not be deleted until some future
// checkpoint. Log this change as well, as long as the
// log file is active.
//
if ((*Scb)->NonpagedScb->OpenAttributeTableIndex != 0) {
NtfsAcquireSharedRestartTable( &Vcb->OpenAttributeTable, TRUE );
AttributeEntry = GetRestartEntryFromIndex( &Vcb->OpenAttributeTable,
(*Scb)->NonpagedScb->OpenAttributeTableIndex );
AttributeEntry->Overlay.Scb = NULL;
NtfsReleaseRestartTable( &Vcb->OpenAttributeTable );
//
// "Steal" the name, and let it belong to the Open Attribute Table
// entry and deallocate it only during checkpoints.
//
(*Scb)->AttributeName.Buffer = NULL;
}
//
// Uninitialize the file lock and oplock variables if this
// a data Scb. For the index case make sure that the lcb queue
// is empty. If this is for an Mft Scb then uninitialize the
// allocation Mcb's.
//
NtfsUninitializeNtfsMcb( &(*Scb)->Mcb );
if (ThisNodeType == NTFS_NTC_SCB_DATA ) {
FsRtlUninitializeOplock( &(*Scb)->ScbType.Data.Oplock );
if ((*Scb)->ScbType.Data.FileLock != NULL) {
FsRtlUninitializeFileLock( (*Scb)->ScbType.Data.FileLock );
ExFreeToNPagedLookasideList( &NtfsFileLockLookasideList, (*Scb)->ScbType.Data.FileLock );
}
} else if (ThisNodeType != NTFS_NTC_SCB_MFT) {
ASSERT(IsListEmpty(&(*Scb)->ScbType.Index.LcbQueue));
if ((*Scb)->ScbType.Index.NormalizedName.Buffer != NULL) {
NtfsFreePool( (*Scb)->ScbType.Index.NormalizedName.Buffer );
(*Scb)->ScbType.Index.NormalizedName.Buffer = NULL;
}
} else {
FsRtlUninitializeLargeMcb( &(*Scb)->ScbType.Mft.AddedClusters );
FsRtlUninitializeLargeMcb( &(*Scb)->ScbType.Mft.RemovedClusters );
}
//
// Show there is no longer a snapshot Scb, if there is a snapshot.
// We rely on the snapshot package to correctly recognize the
// the case where the Scb field is gone.
//
if ((*Scb)->ScbSnapshot != NULL) {
(*Scb)->ScbSnapshot->Scb = NULL;
}
//
// Deallocate the fast mutex if not in the Fcb.
//
if ((*Scb)->Header.FastMutex != (*Scb)->Fcb->FcbMutex) {
NtfsFreePool( (*Scb)->Header.FastMutex );
}
//
// Deallocate the non-paged scb.
//
ExFreeToNPagedLookasideList( &NtfsScbNonpagedLookasideList, (*Scb)->NonpagedScb );
//
// Deallocate the attribute name and the scb itself
//
if (((*Scb)->AttributeName.Buffer != NULL) &&
((*Scb)->AttributeName.Buffer != NtfsFileNameIndexName)) {
NtfsFreePool( (*Scb)->AttributeName.Buffer );
DebugDoit( (*Scb)->AttributeName.Buffer = NULL );
}
#ifdef _CAIRO_
//
// See if CollationData is to be deleted.
//
if (FlagOn((*Scb)->ScbState, SCB_STATE_DELETE_COLLATION_DATA)) {
NtfsFreePool((*Scb)->ScbType.Index.CollationData);
}
#endif _CAIRO_
//
// Always directly free the Mft and non-paged Scb's.
//
if (FlagOn( (*Scb)->ScbState, SCB_STATE_NONPAGED ) ||
(ThisNodeType == NTFS_NTC_SCB_MFT)) {
NtfsFreePool( *Scb );
} else {
//
// Free any final reserved clusters for data Scb's.
//
if (ThisNodeType == NTFS_NTC_SCB_DATA) {
//
// Free any reserved clusters directly into the Vcb
//
if (((*Scb)->ScbType.Data.TotalReserved != 0) &&
FlagOn((*Scb)->ScbState, SCB_STATE_WRITE_ACCESS_SEEN)) {
#ifdef SYSCACHE
if (!FlagOn((*Scb)->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE)) {
DbgPrint( "Freeing final reserved clusters for Scb\n" );
}
#endif
NtfsFreeFinalReservedClusters( Vcb,
LlClustersFromBytes(Vcb, (*Scb)->ScbType.Data.TotalReserved) );
}
//
// Free the reserved bitmap if present.
//
if ((*Scb)->ScbType.Data.ReservedBitMap != NULL) {
NtfsFreePool( (*Scb)->ScbType.Data.ReservedBitMap );
}
}
//
// Now free the Scb itself.
//
// Check if this is an embedded Scb. This could be part of either an INDEX_FCB
// or a DATA_FCB. We depend on the fact that the Scb would be in the same
// location in either case.
//
if ((*Scb) == (PSCB) &((PFCB_DATA) (*Scb)->Fcb)->Scb) {
(*Scb)->Header.NodeTypeCode = 0;
} else if (SafeNodeType( *Scb ) == NTFS_NTC_SCB_DATA) {
ExFreeToPagedLookasideList( &NtfsScbDataLookasideList, *Scb );
} else {
NtfsFreePool( *Scb );
}
}
//
// Zero out the input pointer
//
*Scb = NULL;
//
// And return to our caller
//
DebugTrace( -1, Dbg, ("NtfsDeleteScb -> VOID\n") );
return;
}
VOID
NtfsUpdateNormalizedName (
IN PIRP_CONTEXT IrpContext,
IN PSCB ParentScb,
IN PSCB Scb,
IN PFILE_NAME FileName OPTIONAL,
IN BOOLEAN CheckBufferSizeOnly
)
/*++
Routine Description:
This routine is called to update the normalized name in an IndexScb.
This name will be the path from the root without any short name components.
This routine will append the given name if present provided this is not a
DOS only name. In any other case this routine will go to the disk to
find the name. This routine will handle the case where there is an existing buffer
and the data will fit, as well as the case where the buffer doesn't exist
or is too small.
Arguments:
ParentScb - Supplies the parent of the current Scb. The name for the target
scb is appended to the name in this Scb.
Scb - Supplies the target Scb to add the name to.
FileName - If present this is a filename attribute for this Scb. We check
that it is not a DOS-only name.
CheckBufferSizeOnly - Indicates that we don't want to change the name yet. Just
verify that the buffer is the correct size.
Return Value:
None
--*/
{
ATTRIBUTE_ENUMERATION_CONTEXT Context;
BOOLEAN CleanupContext = FALSE;
BOOLEAN Found;
ULONG Length;
PVOID NewBuffer = Scb->ScbType.Index.NormalizedName.Buffer;
PVOID OldBuffer = NULL;
PCHAR NextChar;
PAGED_CODE();
ASSERT( NodeType( Scb ) == NTFS_NTC_SCB_INDEX );
ASSERT( NodeType( ParentScb ) == NTFS_NTC_SCB_INDEX ||
NodeType( ParentScb ) == NTFS_NTC_SCB_ROOT_INDEX );
ASSERT( ParentScb->ScbType.Index.NormalizedName.Buffer != NULL );
//
// Use a try-finally to clean up the attribute context.
//
try {
//
// If the filename isn't present or is a DOS-only name then go to
// disk to find another name for this Scb.
//
if (!ARGUMENT_PRESENT( FileName ) ||
(FileName->Flags == FILE_NAME_DOS)) {
NtfsInitializeAttributeContext( &Context );
CleanupContext = TRUE;
//
// Walk through the names for this entry. There better
// be one which is not a DOS-only name.
//
Found = NtfsLookupAttributeByCode( IrpContext,
Scb->Fcb,
&Scb->Fcb->FileReference,
$FILE_NAME,
&Context );
while (Found) {
FileName = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &Context ));
if (FileName->Flags != FILE_NAME_DOS) {
break;
}
Found = NtfsLookupNextAttributeByCode( IrpContext,
Scb->Fcb,
$FILE_NAME,
&Context );
}
//
// We should have found the entry.
//
if (!Found) {
NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
}
}
//
// Now that we have the file name attribute allocate the paged pool
// for the name.
//
Length = ParentScb->ScbType.Index.NormalizedName.Length +
((FileName->FileNameLength + 1) * sizeof( WCHAR ));
//
// If the parent is the root then we don't need an extra separator.
//
if (ParentScb == ParentScb->Vcb->RootIndexScb) {
Length -= sizeof( WCHAR );
}
//
// If the current buffer is insufficient then allocate a new one.
//
if ((NewBuffer == NULL) ||
(Scb->ScbType.Index.NormalizedName.MaximumLength < Length)) {
OldBuffer = NewBuffer;
NewBuffer = NtfsAllocatePool( PagedPool, Length );
if (OldBuffer != NULL) {
RtlCopyMemory( NewBuffer,
OldBuffer,
Scb->ScbType.Index.NormalizedName.MaximumLength );
}
//
// Now swap the buffers.
//
Scb->ScbType.Index.NormalizedName.Buffer = NewBuffer;
Scb->ScbType.Index.NormalizedName.MaximumLength = (USHORT) Length;
}
//
// If we are setting the buffer sizes only then make sure we set the length
// to zero if there is nothing in the buffer.
//
if (CheckBufferSizeOnly) {
if (OldBuffer == NULL) {
Scb->ScbType.Index.NormalizedName.Length = 0;
}
} else {
Scb->ScbType.Index.NormalizedName.Length = (USHORT) Length;
NextChar = (PCHAR) Scb->ScbType.Index.NormalizedName.Buffer;
//
// Now copy the name in. Don't forget to add the separator if the parent isn't
// the root.
//
RtlCopyMemory( NextChar,
ParentScb->ScbType.Index.NormalizedName.Buffer,
ParentScb->ScbType.Index.NormalizedName.Length );
NextChar += ParentScb->ScbType.Index.NormalizedName.Length;
if (ParentScb != ParentScb->Vcb->RootIndexScb) {
*((PWCHAR) NextChar) = L'\\';
NextChar += sizeof( WCHAR );
}
//
// Now append this name to the parent name.
//
RtlCopyMemory( NextChar,
FileName->FileName,
FileName->FileNameLength * sizeof( WCHAR ));
}
if (OldBuffer != NULL) {
NtfsFreePool( OldBuffer );
}
} finally {
if (CleanupContext) {
NtfsCleanupAttributeContext( &Context );
}
}
return;
}
VOID
NtfsBuildNormalizedName (
IN PIRP_CONTEXT IrpContext,
IN PSCB Scb,
OUT PUNICODE_STRING PathName
)
/*++
Routine Description:
This routine is called to build a normalized name for an scb by looking
up the file name attributes up to the root directory.
Arguments:
Scb - Supplies the starting point.
Return Value:
None
--*/
{
PFCB ThisFcb = Scb->Fcb;
PFCB NextFcb;
PSCB NextScb;
PLCB NextLcb;
BOOLEAN AcquiredNextFcb = FALSE;
BOOLEAN AcquiredThisFcb = FALSE;
BOOLEAN AcquiredFcbTable = FALSE;
USHORT NewMaximumLength;
PWCHAR NewBuffer;
UNICODE_STRING NormalizedName;
UNICODE_STRING ComponentName;
BOOLEAN FoundEntry = TRUE;
BOOLEAN CleanupAttrContext = FALSE;
PFILE_NAME FileName;
ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
PAGED_CODE();
ASSERT_SCB( Scb );
ASSERT_SHARED_RESOURCE( &Scb->Vcb->Resource );
NormalizedName.Buffer = NULL;
NormalizedName.MaximumLength =
NormalizedName.Length = 0;
//
// Special case the root directory.
//
if (ThisFcb == ThisFcb->Vcb->RootIndexScb->Fcb) {
NormalizedName.Buffer = NtfsAllocatePool( PagedPool, sizeof( WCHAR ) );
NormalizedName.Buffer[0] = L'\\';
NormalizedName.MaximumLength =
NormalizedName.Length = sizeof( WCHAR );
*PathName = NormalizedName;
return;
}
//
// Use a try-finally to facilitate cleanup.
//
try {
while (TRUE) {
//
// Find a non-dos name for the current Scb. There better be one.
//
if (CleanupAttrContext) {
NtfsCleanupAttributeContext( &AttrContext );
}
NtfsInitializeAttributeContext( &AttrContext );
CleanupAttrContext = TRUE;
FoundEntry = NtfsLookupAttributeByCode( IrpContext,
ThisFcb,
&ThisFcb->FileReference,
$FILE_NAME,
&AttrContext );
while (FoundEntry) {
FileName = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
if (FileName->Flags != FILE_NAME_DOS ) { break; }
FoundEntry = NtfsLookupNextAttributeByCode( IrpContext,
ThisFcb,
$FILE_NAME,
&AttrContext );
}
if (!FoundEntry) {
NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, NextFcb );
}
//
// Add the name from the filename attribute to the buffer we are building up.
// We always allow space for the leadingseparator. If we need to grow the buffer then
// always round up to reduce the number of allocations we make.
//
NewMaximumLength = NormalizedName.Length +
((1 + FileName->FileNameLength) * sizeof( WCHAR ));
if (NormalizedName.MaximumLength < NewMaximumLength) {
//
// Round up to a pool block boundary, allow for the pool header as well.
//
NewMaximumLength = ((NewMaximumLength + 8 + 0x40 - 1) & ~(0x40 - 1)) - 8;
NewBuffer = NtfsAllocatePool( PagedPool, NewMaximumLength );
//
// Copy over the existing part of the name to the correct location.
//
if (NormalizedName.Length != 0) {
RtlCopyMemory( (NewBuffer + FileName->FileNameLength + 1),
NormalizedName.Buffer,
NormalizedName.Length );
NtfsFreePool( NormalizedName.Buffer );
}
NormalizedName.Buffer = NewBuffer;
NormalizedName.MaximumLength = NewMaximumLength;
} else {
//
// Move the existing data down in the buffer.
//
RtlMoveMemory( &NormalizedName.Buffer[FileName->FileNameLength + 1],
NormalizedName.Buffer,
NormalizedName.Length );
}
//
// Update the length of the normalized name.
//
NormalizedName.Length += (1 + FileName->FileNameLength) * sizeof( WCHAR );
//
// Now copy over the new component name along with a preceding backslash.
//
NormalizedName.Buffer[0] = L'\\';
RtlCopyMemory( &NormalizedName.Buffer[1],
FileName->FileName,
FileName->FileNameLength * sizeof( WCHAR ));
//
// Now get the parent for the current component. Acquire the Fcb for synchronization.
// We can either walk up the Lcb chain or look it up in the Fcb table. It
// must be for the same name as the file name since there is only one path
// up the tree for a directory.
//
if (!IsListEmpty( &ThisFcb->LcbQueue )) {
NextLcb = (PLCB) CONTAINING_RECORD( ThisFcb->LcbQueue.Flink, LCB, FcbLinks );
NextScb = NextLcb->Scb;
NextFcb = NextScb->Fcb;
NtfsAcquireExclusiveFcb( IrpContext, NextFcb, NULL, TRUE, FALSE );
AcquiredNextFcb = TRUE;
ASSERT( NtfsEqualMftRef( &FileName->ParentDirectory, &NextFcb->FileReference ));
} else {
NtfsAcquireFcbTable( IrpContext, Scb->Vcb );
AcquiredFcbTable = TRUE;
NextFcb = NtfsCreateFcb( IrpContext,
Scb->Vcb,
FileName->ParentDirectory,
FALSE,
TRUE,
NULL );
NextFcb->ReferenceCount -= 1;
//
// Try to do an unsafe acquire. Otherwise we must drop the Fcb table
// and acquire the Fcb and then reacquire the Fcb table.
//
if (!NtfsAcquireExclusiveFcb( IrpContext, NextFcb, NULL, TRUE, TRUE )) {
NtfsReleaseFcbTable( IrpContext, Scb->Vcb );
NtfsAcquireExclusiveFcb( IrpContext, NextFcb, NULL, TRUE, FALSE );
NtfsAcquireFcbTable( IrpContext, Scb->Vcb );
}
NextFcb->ReferenceCount -= 1;
NtfsReleaseFcbTable( IrpContext, Scb->Vcb );
AcquiredFcbTable = FALSE;
AcquiredNextFcb = TRUE;
NextScb = NtfsCreateScb( IrpContext,
NextFcb,
$INDEX_ALLOCATION,
&NtfsFileNameIndex,
FALSE,
NULL );
ComponentName.Buffer = FileName->FileName;
ComponentName.MaximumLength =
ComponentName.Length = FileName->FileNameLength * sizeof( WCHAR );
NextLcb = NtfsCreateLcb( IrpContext,
NextScb,
ThisFcb,
ComponentName,
FileName->Flags,
NULL );
}
//
// If we reach the root then exit. The preceding backslash is already stored.
//
if (NextScb == NextScb->Vcb->RootIndexScb) { break; }
//
// If we reach an Scb which has a normalized name then prepend it
// to the name we have built up and store it into the normalized
// name buffer and exit.
//
if ((NextScb->ScbType.Index.NormalizedName.Buffer != NULL) &&
(NextScb->ScbType.Index.NormalizedName.Length != 0)) {
//
// Compute the new maximum length value.
//
NewMaximumLength = NextScb->ScbType.Index.NormalizedName.Length + NormalizedName.Length;
//
// Allocate a new buffer if needed.
//
if (NewMaximumLength > NormalizedName.MaximumLength) {
NewBuffer = NtfsAllocatePool( PagedPool, NewMaximumLength );
RtlCopyMemory( Add2Ptr( NewBuffer, NextScb->ScbType.Index.NormalizedName.Length ),
NormalizedName.Buffer,
NormalizedName.Length );
NtfsFreePool( NormalizedName.Buffer );
NormalizedName.Buffer = NewBuffer;
NormalizedName.MaximumLength = NewMaximumLength;
} else {
RtlMoveMemory( Add2Ptr( NormalizedName.Buffer, NextScb->ScbType.Index.NormalizedName.Length ),
NormalizedName.Buffer,
NormalizedName.Length );
}
//
// Now copy over the name from the root to this point.
//
RtlCopyMemory( NormalizedName.Buffer,
NextScb->ScbType.Index.NormalizedName.Buffer,
NextScb->ScbType.Index.NormalizedName.Length );
NormalizedName.Length = NewMaximumLength;
break;
}
//
// Release the current Fcb and move up the tree.
//
if (AcquiredThisFcb) { NtfsReleaseFcb( IrpContext, ThisFcb ); }
ThisFcb = NextFcb;
AcquiredThisFcb = TRUE;
AcquiredNextFcb = FALSE;
}
//
// Now store the normalized name into the callers filename structure.
//
*PathName = NormalizedName;
NormalizedName.Buffer = NULL;
} finally {
if (AcquiredFcbTable) { NtfsReleaseFcbTable( IrpContext, Scb->Vcb ); }
if (AcquiredNextFcb) { NtfsReleaseFcb( IrpContext, NextFcb ); }
if (AcquiredThisFcb) { NtfsReleaseFcb( IrpContext, ThisFcb ); }
if (CleanupAttrContext) {
NtfsCleanupAttributeContext( &AttrContext );
}
if (NormalizedName.Buffer != NULL) {
NtfsFreePool( NormalizedName.Buffer );
}
}
return;
}
VOID
NtfsSnapshotScb (
IN PIRP_CONTEXT IrpContext,
IN PSCB Scb
)
/*++
Routine Description:
This routine snapshots necessary Scb data, such as the Scb file sizes,
so that they may be correctly restored if the caller's I/O request is
aborted for any reason. The restoring of these values and the freeing
of any pool involved is automatic.
Arguments:
Scb - Supplies the current Scb
Return Value:
None
--*/
{
PSCB_SNAPSHOT ScbSnapshot;
ASSERT_EXCLUSIVE_SCB(Scb);
ScbSnapshot = &IrpContext->ScbSnapshot;
//
// Only do the snapshot if the Scb is initialized, we have not done
// so already, and it is worth special-casing the bitmap, as it never changes!
//
if (FlagOn(Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED) &&
(Scb->ScbSnapshot == NULL) && (Scb != Scb->Vcb->BitmapScb)) {
//
// If the snapshot structure in the IrpContext is in use, then we have
// to allocate one and insert it in the list.
//
if (ScbSnapshot->SnapshotLinks.Flink != NULL) {
ScbSnapshot = (PSCB_SNAPSHOT)ExAllocateFromNPagedLookasideList( &NtfsScbSnapshotLookasideList );
InsertTailList( &IrpContext->ScbSnapshot.SnapshotLinks,
&ScbSnapshot->SnapshotLinks );
//
// Otherwise we will initialize the listhead to show that the structure
// in the IrpContext is in use.
//
} else {
InitializeListHead( &ScbSnapshot->SnapshotLinks );
}
//
// Snapshot the Scb values and point the Scb and snapshot structure
// at each other.
//
ScbSnapshot->AllocationSize = Scb->Header.AllocationSize.QuadPart;
if (FlagOn(Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT)) {
((ULONG)ScbSnapshot->AllocationSize) += 1;
}
ScbSnapshot->FileSize = Scb->Header.FileSize.QuadPart;
ScbSnapshot->ValidDataLength = Scb->Header.ValidDataLength.QuadPart;
ScbSnapshot->ValidDataToDisk = Scb->ValidDataToDisk;
ScbSnapshot->Scb = Scb;
ScbSnapshot->LowestModifiedVcn = MAXLONGLONG;
ScbSnapshot->HighestModifiedVcn = 0;
ScbSnapshot->TotalAllocated = Scb->TotalAllocated;
#ifdef _CAIRO_
ScbSnapshot->ScbState = FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT |
SCB_STATE_QUOTA_ENLARGED );
#endif // _CAIRO_
Scb->ScbSnapshot = ScbSnapshot;
//
// If this is the Mft Scb then initialize the cluster Mcb structures.
//
if (Scb == Scb->Vcb->MftScb) {
FsRtlTruncateLargeMcb( &Scb->ScbType.Mft.AddedClusters, 0 );
FsRtlTruncateLargeMcb( &Scb->ScbType.Mft.RemovedClusters, 0 );
Scb->ScbType.Mft.FreeRecordChange = 0;
Scb->ScbType.Mft.HoleRecordChange = 0;
}
}
}
VOID
NtfsUpdateScbSnapshots (
IN PIRP_CONTEXT IrpContext
)
/*++
Routine Description:
This routine may be called to update the snapshot values for all Scbs,
after completing a transaction checkpoint.
Arguments:
Return Value:
None
--*/
{
PSCB_SNAPSHOT ScbSnapshot;
PSCB Scb;
ASSERT(FIELD_OFFSET(SCB_SNAPSHOT, SnapshotLinks) == 0);
PAGED_CODE();
ScbSnapshot = &IrpContext->ScbSnapshot;
//
// There is no snapshot data to update if the Flink is still NULL.
//
if (ScbSnapshot->SnapshotLinks.Flink != NULL) {
//
// Loop to update first the Scb data from the snapshot in the
// IrpContext, and then 0 or more additional snapshots linked
// to the IrpContext.
//
do {
Scb = ScbSnapshot->Scb;
//
// Update the Scb values.
//
if (Scb != NULL) {
ScbSnapshot->AllocationSize = Scb->Header.AllocationSize.QuadPart;
if (FlagOn(Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT)) {
((ULONG)ScbSnapshot->AllocationSize) += 1;
}
//
// If this is the MftScb then clear out the added/removed
// cluster Mcbs.
//
if (Scb == Scb->Vcb->MftScb) {
FsRtlTruncateLargeMcb( &Scb->ScbType.Mft.AddedClusters, (LONGLONG)0 );
FsRtlTruncateLargeMcb( &Scb->ScbType.Mft.RemovedClusters, (LONGLONG)0 );
Scb->ScbType.Mft.FreeRecordChange = 0;
Scb->ScbType.Mft.HoleRecordChange = 0;
}
ScbSnapshot->FileSize = Scb->Header.FileSize.QuadPart;
ScbSnapshot->ValidDataLength = Scb->Header.ValidDataLength.QuadPart;
ScbSnapshot->ValidDataToDisk = Scb->ValidDataToDisk;
ScbSnapshot->TotalAllocated = Scb->TotalAllocated;
#ifdef _CAIRO_
ScbSnapshot->ScbState = FlagOn( Scb->ScbState,
SCB_STATE_ATTRIBUTE_RESIDENT |
SCB_STATE_QUOTA_ENLARGED );
#endif // _CAIRO_
}
ScbSnapshot = (PSCB_SNAPSHOT)ScbSnapshot->SnapshotLinks.Flink;
} while (ScbSnapshot != &IrpContext->ScbSnapshot);
}
}
VOID
NtfsRestoreScbSnapshots (
IN PIRP_CONTEXT IrpContext,
IN BOOLEAN Higher
)
/*++
Routine Description:
This routine restores snapshot Scb data in the event of an aborted request.
Arguments:
Higher - Specified as TRUE to restore only those Scb values which are
higher than current values. Specified as FALSE to restore
only those Scb values which are lower (or same!).
Return Value:
None
--*/
{
BOOLEAN UpdateCc;
PSCB_SNAPSHOT ScbSnapshot;
PSCB Scb;
PVCB Vcb = IrpContext->Vcb;
ASSERT(FIELD_OFFSET(SCB_SNAPSHOT, SnapshotLinks) == 0);
ScbSnapshot = &IrpContext->ScbSnapshot;
//
// There is no snapshot data to restore if the Flink is still NULL.
//
if (ScbSnapshot->SnapshotLinks.Flink != NULL) {
//
// Loop to retore first the Scb data from the snapshot in the
// IrpContext, and then 0 or more additional snapshots linked
// to the IrpContext.
//
do {
PSECTION_OBJECT_POINTERS SectionObjectPointer;
PFILE_OBJECT PseudoFileObject;
Scb = ScbSnapshot->Scb;
if (Scb == NULL) {
ScbSnapshot = (PSCB_SNAPSHOT)ScbSnapshot->SnapshotLinks.Flink;
continue;
}
//
// Increment the cleanup count so the Scb won't go away.
//
InterlockedIncrement( &Scb->CleanupCount );
//
// We update the Scb file size in the correct pass. We always do
// the extend/truncate pair.
//
// Only do sizes if our caller was changing these fields, which we can
// see from the Fcb PagingIo being acquired exclusive, the Scb is
// locked for IoAtEof, or else it is metadata.
//
// The one unusual case is where we are converting a stream to
// nonresident when this is not the stream for the request. We
// must restore the Scb for this case as well.
//
UpdateCc = FALSE;
if (FlagOn(Scb->ScbState, SCB_STATE_MODIFIED_NO_WRITE | SCB_STATE_CONVERT_UNDERWAY) ||
(Scb->Fcb == IrpContext->FcbWithPagingExclusive) ||
(Scb == (PSCB)IrpContext->FcbWithPagingExclusive)) {
//
// Proceed to restore all values which are in higher or not
// higher.
//
// Note that the low bit of the allocation size being set
// can only affect the tests if the sizes were equal anyway,
// i.e., sometimes we will execute this code unnecessarily,
// when the values did not change.
//
if (Higher == (ScbSnapshot->AllocationSize >=
Scb->Header.AllocationSize.QuadPart)) {
//
// If this is the maximize pass, we want to extend the cache section.
// In all cases we restore the allocation size in the Scb and
// recover the resident bit.
//
Scb->Header.AllocationSize.QuadPart = ScbSnapshot->AllocationSize;
if (FlagOn(Scb->Header.AllocationSize.LowPart, 1)) {
Scb->Header.AllocationSize.LowPart -= 1;
SetFlag(Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT);
} else {
ClearFlag(Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT);
}
//
// Calculate FastIoPossible
//
if (Scb->CompressionUnit != 0) {
NtfsAcquireFsrtlHeader( Scb );
Scb->Header.IsFastIoPossible = NtfsIsFastIoPossible( Scb );
NtfsReleaseFsrtlHeader( Scb );
}
}
NtfsAcquireFsrtlHeader( Scb );
if (Higher == (ScbSnapshot->FileSize >
Scb->Header.FileSize.QuadPart)) {
Scb->Header.FileSize.QuadPart = ScbSnapshot->FileSize;
//
// We really only need to update Cc if FileSize changes,
// since he does not look at ValidDataLength, and he
// only cares about successfully reached highwatermarks
// on AllocationSize (making section big enough).
//
// Note that setting this flag TRUE also implies we
// are correctly synchronized with FileSize!
//
UpdateCc = TRUE;
}
if (Higher == (ScbSnapshot->ValidDataLength >
Scb->Header.ValidDataLength.QuadPart)) {
Scb->Header.ValidDataLength.QuadPart = ScbSnapshot->ValidDataLength;
}
//
// If this is the unnamed data attribute, we have to update
// some Fcb fields for standard information as well.
//
if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
Scb->Fcb->Info.FileSize = Scb->Header.FileSize.QuadPart;
}
NtfsReleaseFsrtlHeader( Scb );
}
if (!Higher) {
Scb->ValidDataToDisk = ScbSnapshot->ValidDataToDisk;
//
// We always truncate the Mcb to the original allocation size.
// If the Mcb has shrunk beyond this, this becomes a noop.
// If the file is resident, then we will uninitialize
// and reinitialize the Mcb.
//
if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
//
// Remove all of the mappings in the Mcb.
//
NtfsUnloadNtfsMcbRange( &Scb->Mcb, (LONGLONG)0, MAXLONGLONG, FALSE, FALSE );
//
// If we attempted a convert a data attribute to non-
// resident and failed then need to nuke the pages in the
// section if this is not a user file. This is because for
// resident system attributes we always update the attribute
// directly and don't want to reference stale data in the
// section if we do a convert to non-resident later.
//
if ((Scb->AttributeTypeCode != $DATA) &&
(Scb->NonpagedScb->SegmentObject.SharedCacheMap != NULL)) {
if (!CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject,
NULL,
0,
FALSE )) {
ASSERTMSG( "Failed to purge Scb during restore\n", FALSE );
}
//
// If the attribute is for non-user data, then we
// want to modify this Scb so it won't be used again.
// Set the sizes to zero, mark it as being initialized
// and deleted and then change the attribute type code
// so we won't ever return it via NtfsCreateScb.
//
#ifdef _CAIRO_
if (!NtfsIsTypeCodeUserData( Scb->AttributeTypeCode )) {
#endif // _CAIRO_
NtfsAcquireFsrtlHeader( Scb );
Scb->Header.AllocationSize =
Scb->Header.FileSize =
Scb->Header.ValidDataLength = Li0;
NtfsReleaseFsrtlHeader( Scb );
Scb->ValidDataToDisk = 0;
SetFlag( Scb->ScbState,
SCB_STATE_FILE_SIZE_LOADED |
SCB_STATE_HEADER_INITIALIZED |
SCB_STATE_ATTRIBUTE_DELETED );
Scb->AttributeTypeCode = $UNUSED;
#ifdef _CAIRO_
}
#endif // _CAIRO_
}
//
// If we have modified this Mcb and want to back out any
// changes then truncate the Mcb. Don't do the Mft, because
// that is handled elsewhere.
//
} else if ((ScbSnapshot->LowestModifiedVcn != MAXLONGLONG) &&
(Scb != Vcb->MftScb)) {
//
// Truncate the Mcb.
//
NtfsUnloadNtfsMcbRange( &Scb->Mcb, ScbSnapshot->LowestModifiedVcn, ScbSnapshot->HighestModifiedVcn, FALSE, FALSE );
}
Scb->TotalAllocated = ScbSnapshot->TotalAllocated;
//
// If the compression unit is non-zero then set the flag in the
// common header for the Modified page writer.
//
ASSERT( (Scb->CompressionUnit == 0) ||
(Scb->AttributeTypeCode == $INDEX_ROOT) ||
NtfsIsTypeCodeCompressible( Scb->AttributeTypeCode ));
} else {
//
// Set the flag to indicate that we're performing a restore on this
// Scb. We don't want to write any new log records as a result of
// this operation other than the abort records.
//
SetFlag( Scb->ScbState, SCB_STATE_RESTORE_UNDERWAY );
}
#ifdef _CAIRO_
ClearFlag( Scb->ScbState, SCB_STATE_QUOTA_ENLARGED );
SetFlag( Scb->ScbState, FlagOn( ScbSnapshot->ScbState, SCB_STATE_QUOTA_ENLARGED ));
#endif // _CAIRO_
//
// Be sure to update Cache Manager. The interface here uses a file
// object but the routine itself only uses the section object pointers.
// We put a pointer to the segment object pointers on the stack and
// cast some prior value as a file object pointer.
//
PseudoFileObject = (PFILE_OBJECT) CONTAINING_RECORD( &SectionObjectPointer,
FILE_OBJECT,
SectionObjectPointer );
PseudoFileObject->SectionObjectPointer = &Scb->NonpagedScb->SegmentObject;
//
// Now tell the cache manager the sizes.
//
// If we fail in this call, we definitely want to charge on anyway.
// It should only fail if it tries to extend the section and cannot,
// in which case we do not care because we cannot need the extended
// part to the section anyway. (This is probably the very error that
// is causing us to clean up in the first place!)
//
// We don't need to make this call if the top level request is a
// paging Io write.
//
// We only do this if there is a shared cache map for this stream.
// Otherwise CC will cause a flush to happen which could screw up
// the transaction and abort logic.
//
if (UpdateCc && CcIsFileCached( PseudoFileObject ) &&
(IrpContext->OriginatingIrp == NULL ||
IrpContext->OriginatingIrp->Type != IO_TYPE_IRP ||
IrpContext->MajorFunction != IRP_MJ_WRITE ||
!FlagOn( IrpContext->OriginatingIrp->Flags, IRP_PAGING_IO ))) {
try {
CcSetFileSizes( PseudoFileObject,
(PCC_FILE_SIZES)&Scb->Header.AllocationSize );
} except(FsRtlIsNtstatusExpected(GetExceptionCode()) ?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH) {
NOTHING;
}
}
//
// If this is the unnamed data attribute, we have to update
// some Fcb fields for standard information as well.
//
if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
Scb->Fcb->Info.AllocatedLength = Scb->TotalAllocated;
}
//
// We always clear the Scb deleted flag and the deleted flag in the Fcb
// unless this was a create new file operation which failed. We recognize
// this by looking for the major Irp code in the IrpContext, and the
// deleted bit in the Fcb.
//
if (Scb->AttributeTypeCode != $UNUSED &&
(IrpContext->MajorFunction != IRP_MJ_CREATE ||
!FlagOn( Scb->Fcb->FcbState, FCB_STATE_FILE_DELETED ))) {
ClearFlag( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
ClearFlag( Scb->Fcb->FcbState, FCB_STATE_FILE_DELETED );
}
//
// Clear the flags in the Scb if this Scb is from a create
// that failed. We always clear our RESTORE_UNDERWAY flag.
//
// If this is an Index allocation or Mft bitmap, then we
// store MAXULONG in the record allocation context to indicate
// that we should reinitialize it.
//
if (!Higher) {
ClearFlag( Scb->ScbState, SCB_STATE_RESTORE_UNDERWAY );
if (FlagOn( Scb->ScbState, SCB_STATE_UNINITIALIZE_ON_RESTORE )) {
ClearFlag( Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED |
SCB_STATE_HEADER_INITIALIZED |
SCB_STATE_UNINITIALIZE_ON_RESTORE );
}
//
// If this is the MftScb we have several jobs to do.
//
// - Force the record allocation context to be reinitialized
// - Back out the changes to the Vcb->MftFreeRecords field
// - Back changes to the Vcb->MftHoleRecords field
// - Clear the flag indicating we allocated file record 15
// - Clear the flag indicating we reserved a record
// - Remove any clusters added to the Scb Mcb
// - Restore any clusters removed from the Scb Mcb
//
if (Scb == Vcb->MftScb) {
ULONG RunIndex;
VCN Vcn;
LCN Lcn;
LONGLONG Clusters;
Vcb->MftBitmapAllocationContext.CurrentBitmapSize = MAXULONG;
(LONG) Vcb->MftFreeRecords -= Scb->ScbType.Mft.FreeRecordChange;
(LONG) Vcb->MftHoleRecords -= Scb->ScbType.Mft.HoleRecordChange;
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_MFT_RECORD_15_USED )) {
ClearFlag( IrpContext->Flags, IRP_CONTEXT_MFT_RECORD_15_USED );
ClearFlag( Vcb->MftReserveFlags, VCB_MFT_RECORD_15_USED );
}
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_MFT_RECORD_RESERVED )) {
ClearFlag( IrpContext->Flags, IRP_CONTEXT_MFT_RECORD_RESERVED );
ClearFlag( Vcb->MftReserveFlags, VCB_MFT_RECORD_RESERVED );
Scb->ScbType.Mft.ReservedIndex = 0;
}
RunIndex = 0;
while (FsRtlGetNextLargeMcbEntry( &Scb->ScbType.Mft.AddedClusters,
RunIndex,
&Vcn,
&Lcn,
&Clusters )) {
if (Lcn != UNUSED_LCN) {
NtfsRemoveNtfsMcbEntry( &Scb->Mcb, Vcn, Clusters );
}
RunIndex += 1;
}
RunIndex = 0;
while (FsRtlGetNextLargeMcbEntry( &Scb->ScbType.Mft.RemovedClusters,
RunIndex,
&Vcn,
&Lcn,
&Clusters )) {
if (Lcn != UNUSED_LCN) {
NtfsAddNtfsMcbEntry( &Scb->Mcb, Vcn, Lcn, Clusters, FALSE );
}
RunIndex += 1;
}
} else if (Scb->AttributeTypeCode == $INDEX_ALLOCATION) {
Scb->ScbType.Index.RecordAllocationContext.CurrentBitmapSize = MAXULONG;
}
}
//
// Decrement the cleanup count to restore the previous value.
//
InterlockedDecrement( &Scb->CleanupCount );
ScbSnapshot = (PSCB_SNAPSHOT)ScbSnapshot->SnapshotLinks.Flink;
} while (ScbSnapshot != &IrpContext->ScbSnapshot);
}
}
VOID
NtfsFreeSnapshotsForFcb (
IN PIRP_CONTEXT IrpContext,
IN PFCB Fcb
)
/*++
Routine Description:
This routine restores snapshot Scb data in the event of an aborted request.
Arguments:
Fcb - Fcb for which all snapshots are to be freed, or NULL to free all
snapshots.
Return Value:
None
--*/
{
PSCB_SNAPSHOT ScbSnapshot;
ASSERT(FIELD_OFFSET(SCB_SNAPSHOT, SnapshotLinks) == 0);
ScbSnapshot = &IrpContext->ScbSnapshot;
//
// There is no snapshot data to free if the Flink is still NULL.
// We also don't free the snapshot if this isn't a top-level action.
//
if (ScbSnapshot->SnapshotLinks.Flink != NULL) {
//
// Loop to free first the Scb data from the snapshot in the
// IrpContext, and then 0 or more additional snapshots linked
// to the IrpContext.
//
do {
PSCB_SNAPSHOT NextScbSnapshot;
//
// Move to next snapshot before we delete the current one.
//
NextScbSnapshot = (PSCB_SNAPSHOT)ScbSnapshot->SnapshotLinks.Flink;
//
// We are now at a snapshot in the snapshot list. We skip
// over this entry if it has an Scb and the Fcb for that
// Scb does not match the input Fcb. If there is no
// input Fcb we always deal with this snapshot.
//
if (ScbSnapshot->Scb != NULL
&& Fcb != NULL
&& ScbSnapshot->Scb->Fcb != Fcb) {
ScbSnapshot = NextScbSnapshot;
continue;
}
//
// If there is an Scb, then clear its snapshot pointer.
// Always clear the UNINITIALIZE_ON_RESTORE flag, RESTORE_UNDERWAY and
// CONVERT_UNDERWAY flags.
//
if (ScbSnapshot->Scb != NULL) {
if (FlagOn( ScbSnapshot->Scb->ScbState,
SCB_STATE_UNINITIALIZE_ON_RESTORE | SCB_STATE_RESTORE_UNDERWAY | SCB_STATE_CONVERT_UNDERWAY )) {
NtfsAcquireFsrtlHeader( ScbSnapshot->Scb );
ClearFlag( ScbSnapshot->Scb->ScbState,
SCB_STATE_UNINITIALIZE_ON_RESTORE | SCB_STATE_RESTORE_UNDERWAY | SCB_STATE_CONVERT_UNDERWAY );
NtfsReleaseFsrtlHeader( ScbSnapshot->Scb );
}
ScbSnapshot->Scb->ScbSnapshot = NULL;
}
if (ScbSnapshot == &IrpContext->ScbSnapshot) {
IrpContext->ScbSnapshot.Scb = NULL;
//
// Else delete the snapshot structure
//
} else {
RemoveEntryList(&ScbSnapshot->SnapshotLinks);
ExFreeToNPagedLookasideList( &NtfsScbSnapshotLookasideList, ScbSnapshot );
}
ScbSnapshot = NextScbSnapshot;
} while (ScbSnapshot != &IrpContext->ScbSnapshot);
}
}
BOOLEAN
NtfsCreateFileLock (
IN PSCB Scb,
IN BOOLEAN RaiseOnError
)
/*++
Routine Description:
This routine is called to create and initialize a file lock structure.
A try-except is used to catch allocation failures if the caller doesn't
want the exception raised.
Arguments:
Scb - Supplies the Scb to attach the file lock to.
RaiseOnError - If TRUE then don't catch the allocation failure.
Return Value:
TRUE if the lock is allocated and initialized. FALSE if there is an
error and the caller didn't specify RaiseOnError.
--*/
{
PFILE_LOCK FileLock = NULL;
BOOLEAN Success = TRUE;
PAGED_CODE();
//
// Use a try-except to catch all errors.
//
try {
FileLock = (PFILE_LOCK)ExAllocateFromNPagedLookasideList( &NtfsFileLockLookasideList );
FsRtlInitializeFileLock( FileLock, NULL, NULL );
//
// Use the FsRtl header mutex to synchronize storing
// the lock structure, and only store it if no one
// else beat us.
//
NtfsAcquireFsrtlHeader(Scb);
if (Scb->ScbType.Data.FileLock == NULL) {
Scb->ScbType.Data.FileLock = FileLock;
FileLock = NULL;
}
NtfsReleaseFsrtlHeader(Scb);
} except( (!FsRtlIsNtstatusExpected( GetExceptionCode() ) || RaiseOnError)
? EXCEPTION_CONTINUE_SEARCH
: EXCEPTION_EXECUTE_HANDLER ) {
Success = FALSE;
}
if (FileLock != NULL) {
ExFreeToNPagedLookasideList( &NtfsFileLockLookasideList, FileLock );
}
return Success;
}
PSCB
NtfsGetNextScb (
IN PSCB Scb,
IN PSCB TerminationScb
)
/*++
Routine Description:
This routine is used to iterate through Scbs in a tree.
The rules are:
. If you have a child, go to it, else
. If you have a next sibling, go to it, else
. Go to your parent's next sibling.
If this routine is called with in invalid TerminationScb it will fail,
badly.
Arguments:
Scb - Supplies the current Scb
TerminationScb - The Scb at which the enumeration should (non-inclusively)
stop. Assumed to be a directory.
Return Value:
The next Scb in the enumeration, or NULL if Scb was the final one.
--*/
{
PSCB Results;
PAGED_CODE();
DebugTrace( +1, Dbg, ("NtfsGetNextScb, Scb = %08lx, TerminationScb = %08lx\n", Scb, TerminationScb) );
//
// If this is an index (i.e., not a file) and it has children then return
// the scb for the first child
//
// Scb
//
// / \.
// / \.
//
// ChildLcb
//
// |
// |
//
// ChildFcb
//
// / \.
// / \.
//
// Results
//
if (((SafeNodeType(Scb) == NTFS_NTC_SCB_INDEX) || (SafeNodeType(Scb) == NTFS_NTC_SCB_ROOT_INDEX))
&&
!IsListEmpty(&Scb->ScbType.Index.LcbQueue)) {
PLCB ChildLcb;
PFCB ChildFcb;
//
// locate the first lcb out of this scb and also the corresponding fcb
//
ChildLcb = NtfsGetNextChildLcb(Scb, NULL);
ChildFcb = ChildLcb->Fcb;
//
// Then as a bookkeeping means for ourselves we will move this
// lcb to the head of the fcb's lcb queue that way when we
// need to ask which link we went through to get here we will know
//
RemoveEntryList( &ChildLcb->FcbLinks );
InsertHeadList( &ChildFcb->LcbQueue, &ChildLcb->FcbLinks );
//
// And our return value is the first scb of this fcb
//
ASSERT( !IsListEmpty(&ChildFcb->ScbQueue) );
//
// Acquire and drop the Fcb in order to look at the Scb list.
//
ExAcquireResourceExclusive( ChildFcb->Resource, TRUE );
Results = NtfsGetNextChildScb( ChildFcb, NULL );
ExReleaseResource( ChildFcb->Resource );
//
// We could be processing an empty index
//
} else if ( Scb == TerminationScb ) {
Results = NULL;
} else {
PSCB SiblingScb;
PFCB ParentFcb;
PLCB ParentLcb;
PLCB SiblingLcb;
PFCB SiblingFcb;
//
// Acquire and drop the Fcb in order to look at the Scb list.
//
ExAcquireResourceExclusive( Scb->Fcb->Resource, TRUE );
SiblingScb = NtfsGetNextChildScb( Scb->Fcb, Scb );
ExReleaseResource( Scb->Fcb->Resource );
while (TRUE) {
//
// If there is a sibling scb to the input scb then return it
//
// Fcb
//
// / \.
// / \.
//
// Scb Sibling
// Scb
//
if (SiblingScb != NULL) {
Results = SiblingScb;
break;
}
//
// The scb doesn't have any more siblings. See if our fcb has a sibling
//
// S
//
// / \.
// / \.
//
// ParentLcb SiblingLcb
//
// | |
// | |
//
// ParentFcb SiblingFcb
//
// / / \.
// / / \.
//
// Scb Results
//
// It's possible that the SiblingFcb has already been traversed.
// Consider the case where there are multiple links between the
// same Scb and Fcb. We want to ignore this case or else face
// an infinite loop by moving the Lcb to the beginning of the
// Fcb queue and then later finding an Lcb that we have already
// traverse. We use the fact that we haven't modified the
// ordering of the Lcb off the parent Scb. When we find a
// candidate for the next Fcb, we walk backwards through the
// list of Lcb's off the Scb to make sure this is not a
// duplicate Fcb.
//
ParentFcb = Scb->Fcb;
ParentLcb = NtfsGetNextParentLcb(ParentFcb, NULL);
//
// Try to find a sibling Lcb which does not point to an Fcb
// we've already visited.
//
SiblingLcb = ParentLcb;
while ((SiblingLcb = NtfsGetNextChildLcb( ParentLcb->Scb, SiblingLcb)) != NULL) {
PLCB PrevChildLcb;
PFCB PotentialSiblingFcb;
//
// Now walk through the child Lcb's of the Scb which we have
// already visited.
//
PrevChildLcb = SiblingLcb;
PotentialSiblingFcb = SiblingLcb->Fcb;
//
// Skip this Lcb if the Fcb has no children.
//
if (IsListEmpty( &PotentialSiblingFcb->ScbQueue )) {
continue;
}
while ((PrevChildLcb = NtfsGetPrevChildLcb( ParentLcb->Scb, PrevChildLcb )) != NULL) {
//
// If the parent Fcb and the Fcb for this Lcb are the same,
// then we have already returned the Scb's for this Fcb.
//
if (PrevChildLcb->Fcb == PotentialSiblingFcb) {
break;
}
}
//
// If we don't have a PrevChildLcb, that means that we have a valid
// sibling Lcb. We will ignore any sibling Lcb's whose
// Fcb's don't have any Scb's.
//
if (PrevChildLcb == NULL) {
break;
}
}
if (SiblingLcb != NULL) {
SiblingFcb = SiblingLcb->Fcb;
//
// Then as a bookkeeping means for ourselves we will move this
// lcb to the head of the fcb's lcb queue that way when we
// need to ask which link we went through to get here we will know
//
RemoveEntryList( &SiblingLcb->FcbLinks );
InsertHeadList( &SiblingFcb->LcbQueue, &SiblingLcb->FcbLinks );
//
// And our return value is the first scb of this fcb
//
ASSERT( !IsListEmpty(&SiblingFcb->ScbQueue) );
//
// Acquire and drop the Fcb in order to look at the Scb list.
//
ExAcquireResourceExclusive( SiblingFcb->Resource, TRUE );
Results = NtfsGetNextChildScb( SiblingFcb, NULL );
ExReleaseResource( SiblingFcb->Resource );
break;
}
//
// The Fcb has no sibling so bounce up one and see if we
// have reached our termination scb yet
//
// NewScb
//
// /
// /
//
// ParentLcb
//
// |
// |
//
// ParentFcb
//
// /
// /
//
// Scb
//
//
Scb = ParentLcb->Scb;
if (Scb == TerminationScb) {
Results = NULL;
break;
}
//
// Acquire and drop the Fcb in order to look at the Scb list.
//
ExAcquireResourceExclusive( Scb->Fcb->Resource, TRUE );
SiblingScb = NtfsGetNextChildScb( Scb->Fcb, Scb );
ExReleaseResource( Scb->Fcb->Resource );
}
}
DebugTrace( -1, Dbg, ("NtfsGetNextScb -> %08lx\n", Results) );
return Results;
}
PLCB
NtfsCreateLcb (
IN PIRP_CONTEXT IrpContext,
IN PSCB Scb,
IN PFCB Fcb,
IN UNICODE_STRING LastComponentFileName,
IN UCHAR FileNameFlags,
OUT PBOOLEAN ReturnedExistingLcb OPTIONAL
)
/*++
Routine Description:
This routine allocates and creates a new lcb between an
existing scb and fcb. If a component of the exact
name already exists we return that one instead of creating
a new lcb
Arguments:
Scb - Supplies the parent scb to use
Fcb - Supplies the child fcb to use
LastComponentFileName - Supplies the last component of the
path that this link represents
FileNameFlags - Indicates if this is an NTFS, DOS or hard link
ReturnedExistingLcb - Optionally tells the caller if the
lcb returned already existed
Return Value:
LCB - returns a pointer the newly created lcb.
--*/
{
PLCB Lcb = NULL;
BOOLEAN LocalReturnedExistingLcb;
//
// The following variables are only used for abnormal termination
//
PVOID UnwindStorage[2] = { NULL, NULL };
PAGED_CODE();
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_SCB( Scb );
ASSERT_FCB( Fcb );
ASSERT(NodeType(Scb) != NTFS_NTC_SCB_DATA);
DebugTrace( +1, Dbg, ("NtfsCreateLcb...\n") );
if (!ARGUMENT_PRESENT(ReturnedExistingLcb)) { ReturnedExistingLcb = &LocalReturnedExistingLcb; }
//
// Search the lcb children of the input Scb to see if we have an Lcb that matches
// this one. We match if the Lcb points to the same fcb and the last component file name
// and flags match. We ignore any Lcb's that indicate links that have been
// removed.
//
Lcb = NULL;
while ((Lcb = NtfsGetNextParentLcb(Fcb, Lcb)) != NULL) {
ASSERT_LCB( Lcb );
if ((Scb == Lcb->Scb)
&&
(!FlagOn( Lcb->LcbState, LCB_STATE_LINK_IS_GONE ))
&&
(FileNameFlags == Lcb->FileNameAttr->Flags)
&&
(LastComponentFileName.Length == Lcb->ExactCaseLink.LinkName.Length)
&&
(RtlEqualMemory( LastComponentFileName.Buffer,
Lcb->ExactCaseLink.LinkName.Buffer,
LastComponentFileName.Length ) )) {
*ReturnedExistingLcb = TRUE;
DebugTrace( -1, Dbg, ("NtfsCreateLcb -> %08lx\n", Lcb) );
return Lcb;
}
}
*ReturnedExistingLcb = FALSE;
try {
UCHAR MaxNameLength;
//
// Allocate a new lcb, zero it out and set the node type information
// Check if we can allocate the Lcb out of a compound Fcb. Check here if
// we can use the embedded name as well.
//
if (FlagOn( Fcb->FcbState, FCB_STATE_COMPOUND_DATA) &&
(SafeNodeType( &((PFCB_DATA) Fcb)->Lcb ) == 0)) {
Lcb = (PLCB) &((PFCB_DATA) Fcb)->Lcb;
MaxNameLength = MAX_DATA_FILE_NAME;
} else if (FlagOn( Fcb->FcbState, FCB_STATE_COMPOUND_INDEX ) &&
(SafeNodeType( &((PFCB_INDEX) Fcb)->Lcb ) == 0)) {
Lcb = (PLCB) &((PFCB_INDEX) Fcb)->Lcb;
MaxNameLength = MAX_INDEX_FILE_NAME;
} else {
Lcb = UnwindStorage[0] = ExAllocateFromPagedLookasideList( &NtfsLcbLookasideList );
MaxNameLength = 0;
}
RtlZeroMemory( Lcb, sizeof(LCB) );
Lcb->NodeTypeCode = NTFS_NTC_LCB;
Lcb->NodeByteSize = sizeof(LCB);
//
// Check if we will have to allocate a separate filename attr.
//
if (MaxNameLength < (USHORT) (LastComponentFileName.Length / sizeof( WCHAR ))) {
//
// Allocate the last component part of the lcb and copy over the data.
// Check if there is space in the Fcb for this.
//
Lcb->FileNameAttr =
UnwindStorage[1] = NtfsAllocatePool(PagedPool, LastComponentFileName.Length +
NtfsFileNameSizeFromLength( LastComponentFileName.Length ));
MaxNameLength = LastComponentFileName.Length / sizeof( WCHAR );
} else {
Lcb->FileNameAttr = (PFILE_NAME) &Lcb->ParentDirectory;
}
Lcb->FileNameAttr->ParentDirectory = Scb->Fcb->FileReference;
Lcb->FileNameAttr->FileNameLength = (USHORT) LastComponentFileName.Length / sizeof( WCHAR );
Lcb->FileNameAttr->Flags = FileNameFlags;
Lcb->ExactCaseLink.LinkName.Buffer = (PWCHAR) &Lcb->FileNameAttr->FileName;
Lcb->IgnoreCaseLink.LinkName.Buffer = Add2Ptr( Lcb->FileNameAttr,
NtfsFileNameSizeFromLength( MaxNameLength * sizeof( WCHAR )));
Lcb->ExactCaseLink.LinkName.Length =
Lcb->IgnoreCaseLink.LinkName.Length = LastComponentFileName.Length;
Lcb->ExactCaseLink.LinkName.MaximumLength =
Lcb->IgnoreCaseLink.LinkName.MaximumLength = MaxNameLength * sizeof( WCHAR );
RtlCopyMemory( Lcb->ExactCaseLink.LinkName.Buffer,
LastComponentFileName.Buffer,
LastComponentFileName.Length );
RtlCopyMemory( Lcb->IgnoreCaseLink.LinkName.Buffer,
LastComponentFileName.Buffer,
LastComponentFileName.Length );
NtfsUpcaseName( IrpContext->Vcb->UpcaseTable,
IrpContext->Vcb->UpcaseTableSize,
&Lcb->IgnoreCaseLink.LinkName );
//
// Now put this Lcb into the queues for the scb and the fcb
//
InsertTailList( &Scb->ScbType.Index.LcbQueue, &Lcb->ScbLinks );
Lcb->Scb = Scb;
InsertTailList( &Fcb->LcbQueue, &Lcb->FcbLinks );
Lcb->Fcb = Fcb;
//
// Now initialize the ccb queue.
//
InitializeListHead( &Lcb->CcbQueue );
} finally {
DebugUnwind( NtfsCreateLcb );
if (AbnormalTermination()) {
if (UnwindStorage[0]) { NtfsFreePool( UnwindStorage[0] );
} else if (Lcb != NULL) { Lcb->NodeTypeCode = 0; }
if (UnwindStorage[1]) { NtfsFreePool( UnwindStorage[1] ); }
}
}
DebugTrace( -1, Dbg, ("NtfsCreateLcb -> %08lx\n", Lcb) );
return Lcb;
}
VOID
NtfsDeleteLcb (
IN PIRP_CONTEXT IrpContext,
IN OUT PLCB *Lcb
)
/*++
Routine Description:
This routine deallocated and removes the lcb record from Ntfs's in-memory
data structures. It assumes that the ccb queue is empty. We also assume
that this is not the root lcb that we are trying to delete.
Arguments:
Lcb - Supplise the Lcb to be removed
Return Value:
None.
--*/
{
PCCB Ccb;
PLIST_ENTRY Links;
PAGED_CODE();
ASSERT_IRP_CONTEXT( IrpContext );
DebugTrace( +1, Dbg, ("NtfsDeleteLcb, *Lcb = %08lx\n", *Lcb) );
//
// Get rid of any prefixes that might still be attached to us
//
NtfsRemovePrefix( (*Lcb) );
//
// Walk through the Ccb's for this link and clear the Lcb
// pointer. This can only be for Ccb's which there is no
// more user handle.
//
Links = (*Lcb)->CcbQueue.Flink;
while (Links != &(*Lcb)->CcbQueue) {
Ccb = CONTAINING_RECORD( Links,
CCB,
LcbLinks );
Links = Links->Flink;
NtfsUnlinkCcbFromLcb( IrpContext, Ccb );
}
//
//
// Now remove ourselves from our scb and fcb
//
RemoveEntryList( &(*Lcb)->ScbLinks );
RemoveEntryList( &(*Lcb)->FcbLinks );
//
// Free up the last component part and then free ourselves
//
if ((*Lcb)->FileNameAttr != (PFILE_NAME) &(*Lcb)->ParentDirectory) {
NtfsFreePool( (*Lcb)->FileNameAttr );
DebugDoit( (*Lcb)->FileNameAttr = NULL );
}
//
// Check if we are part of an embedded structure otherwise free back to the
// lookaside list
//
if (((*Lcb) == (PLCB) &((PFCB_DATA) (*Lcb)->Fcb)->Lcb) ||
((*Lcb) == (PLCB) &((PFCB_INDEX) (*Lcb)->Fcb)->Lcb)) {
(*Lcb)->NodeTypeCode = 0;
} else {
ExFreeToPagedLookasideList( &NtfsLcbLookasideList, *Lcb );
}
//
// And for safety sake null out the pointer
//
*Lcb = NULL;
DebugTrace( -1, Dbg, ("NtfsDeleteLcb -> VOID\n") );
return;
}
VOID
NtfsMoveLcb (
IN PIRP_CONTEXT IrpContext,
IN PLCB Lcb,
IN PSCB Scb,
IN PFCB Fcb,
IN PUNICODE_STRING TargetDirectoryName,
IN PUNICODE_STRING LastComponentName,
IN UCHAR FileNameFlags,
IN BOOLEAN CheckBufferSizeOnly
)
/*++
Routine Description:
This routine completely moves the input lcb to join different fcbs and
scbs. It hasIt uses the target directory
file object to supply the complete new name to use.
Arguments:
Lcb - Supplies the Lcb being moved.
Scb - Supplies the new parent scb
Fcb - Supplies the new child fcb
TargetDirectoryName - This is the path used to reach the new parent directory
for this Lcb. It will only be from the root.
LastComponentName - This is the last component name to store in this relocated Lcb.
FileNameFlags - Indicates if this is an NTFS, DOS or hard link
CheckBufferSizeOnly - If TRUE we just want to pass through and verify that
the buffer sizes of the various structures will be large enough for the
new name.
Return Value:
None.
--*/
{
PVCB Vcb = Scb->Vcb;
ULONG BytesNeeded;
PVOID NewAllocation;
PCHAR NextChar;
PSCB NextScb;
PSCB ChildScb;
PLCB TempLcb;
PCCB Ccb;
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_LCB( Lcb );
ASSERT_SCB( Scb );
ASSERT_FCB( Fcb );
ASSERT( NodeType( Scb ) != NTFS_NTC_SCB_DATA );
PAGED_CODE();
DebugTrace( +1, Dbg, ("NtfsMoveLcb, Lcb = %08lx\n", Lcb) );
//
// Use a try finally to cleanup any new allocation.
//
try {
//
// If we're not just checking sizes then remove entries from the prefix table
// and the normalized name for descendents of the current scb.
//
if (!CheckBufferSizeOnly) {
//
// Clear the index offset pointer so we will look this up again.
//
Lcb->QuickIndex.BufferOffset = 0;
//
// Get rid of any prefixes that might still be attached to us
//
NtfsRemovePrefix( Lcb );
//
// And then traverse the graph underneath our fcb and remove all prefixes
// also used there. For each child scb under the fcb we will traverse all of
// its descendant Scb children and for each lcb we encounter we will remove its prefixes.
//
ChildScb = NULL;
while ((ChildScb = NtfsGetNextChildScb( Lcb->Fcb, ChildScb )) != NULL) {
//
// Loop through his Lcb's and remove the prefix entries.
//
TempLcb = NULL;
while ((TempLcb = NtfsGetNextChildLcb(ChildScb, TempLcb)) != NULL) {
NtfsRemovePrefix( TempLcb );
}
//
// Now we have to descend into this Scb subtree, if it exists.
// Then remove the prefix entries on all of the links found.
//
NextScb = ChildScb;
while ((NextScb = NtfsGetNextScb( NextScb, ChildScb )) != NULL) {
//
// If this is an index Scb with a normalized name, then free
// the normalized name.
//
if ((SafeNodeType( NextScb ) == NTFS_NTC_SCB_INDEX) &&
(NextScb->ScbType.Index.NormalizedName.Buffer != NULL)) {
NtfsFreePool( NextScb->ScbType.Index.NormalizedName.Buffer );
NextScb->ScbType.Index.NormalizedName.Buffer = NULL;
NextScb->ScbType.Index.NormalizedName.Length = 0;
}
TempLcb = NULL;
while ((TempLcb = NtfsGetNextChildLcb(NextScb, TempLcb)) != NULL) {
NtfsRemovePrefix( TempLcb );
}
}
}
}
//
// Remember the number of bytes needed for the last component.
//
BytesNeeded = LastComponentName->Length;
//
// Check if we need to allocate a new filename attribute. If so allocate
// it and store it into the new allocation buffer.
//
if (Lcb->ExactCaseLink.LinkName.MaximumLength < BytesNeeded) {
NewAllocation = NtfsAllocatePool( PagedPool,
BytesNeeded + NtfsFileNameSizeFromLength( BytesNeeded ));
//
// Set up the existing names into the new buffer. That way if we have an allocation
// failure below with the Ccb's the Lcb is still in a valid state.
//
RtlCopyMemory( NewAllocation,
Lcb->FileNameAttr,
NtfsFileNameSizeFromLength( Lcb->ExactCaseLink.LinkName.MaximumLength ));
RtlCopyMemory( Add2Ptr( NewAllocation, NtfsFileNameSizeFromLength( BytesNeeded )),
Lcb->IgnoreCaseLink.LinkName.Buffer,
Lcb->IgnoreCaseLink.LinkName.MaximumLength );
if (Lcb->FileNameAttr != (PFILE_NAME) &Lcb->ParentDirectory) {
NtfsFreePool( Lcb->FileNameAttr );
}
Lcb->FileNameAttr = NewAllocation;
Lcb->ExactCaseLink.LinkName.MaximumLength =
Lcb->IgnoreCaseLink.LinkName.MaximumLength = (USHORT) BytesNeeded;
Lcb->ExactCaseLink.LinkName.Buffer = (PWCHAR) &Lcb->FileNameAttr->FileName;
Lcb->IgnoreCaseLink.LinkName.Buffer = Add2Ptr( Lcb->FileNameAttr,
NtfsFileNameSizeFromLength( BytesNeeded ));
}
//
// Compute the full length of the name for the CCB, assume we will need a
// separator.
//
BytesNeeded += (TargetDirectoryName->Length + sizeof( WCHAR ));
//
// Now for every ccb attached to us we need to check if we need a new
// filename buffer.
//
Ccb = NULL;
while ((Ccb = NtfsGetNextCcb(Lcb, Ccb)) != NULL) {
//
// Check if the name buffer currently in the Ccb is large enough.
//
if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_FILE_ID | CCB_FLAG_CLEANUP )) {
if (Ccb->FullFileName.MaximumLength < BytesNeeded) {
//
// Allocate a new file name buffer and copy the existing data back into it.
//
NewAllocation = NtfsAllocatePool( PagedPool, BytesNeeded );
RtlCopyMemory( NewAllocation,
Ccb->FullFileName.Buffer,
Ccb->FullFileName.Length );
if (FlagOn( Ccb->Flags, CCB_FLAG_ALLOCATED_FILE_NAME )) {
NtfsFreePool( Ccb->FullFileName.Buffer );
}
Ccb->FullFileName.Buffer = NewAllocation;
Ccb->FullFileName.MaximumLength = (USHORT) BytesNeeded;
SetFlag( Ccb->Flags, CCB_FLAG_ALLOCATED_FILE_NAME );
}
}
}
//
// Now update the Lcb with the new values if we are to rewrite the buffers.
//
if (!CheckBufferSizeOnly) {
Lcb->FileNameAttr->ParentDirectory = Scb->Fcb->FileReference;
Lcb->FileNameAttr->FileNameLength = (USHORT) LastComponentName->Length / sizeof( WCHAR );
Lcb->FileNameAttr->Flags = FileNameFlags;
Lcb->ExactCaseLink.LinkName.Length =
Lcb->IgnoreCaseLink.LinkName.Length = (USHORT) LastComponentName->Length;
RtlCopyMemory( Lcb->ExactCaseLink.LinkName.Buffer,
LastComponentName->Buffer,
LastComponentName->Length );
RtlCopyMemory( Lcb->IgnoreCaseLink.LinkName.Buffer,
LastComponentName->Buffer,
LastComponentName->Length );
NtfsUpcaseName( IrpContext->Vcb->UpcaseTable,
IrpContext->Vcb->UpcaseTableSize,
&Lcb->IgnoreCaseLink.LinkName );
//
// Now for every ccb attached to us we need to munge it file object name by
// copying over the entire new name
//
Ccb = NULL;
while ((Ccb = NtfsGetNextCcb(Lcb, Ccb)) != NULL) {
//
// We ignore any Ccb's which are associated with open by File Id
// file objects or their file objects have gone through cleanup.
//
if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_FILE_ID | CCB_FLAG_CLEANUP )) {
Ccb->FullFileName.Length = (USHORT) BytesNeeded;
NextChar = (PCHAR) Ccb->FullFileName.Buffer;
RtlCopyMemory( NextChar,
TargetDirectoryName->Buffer,
TargetDirectoryName->Length );
NextChar += TargetDirectoryName->Length;
if (TargetDirectoryName->Length != sizeof( WCHAR )) {
*((PWCHAR) NextChar) = L'\\';
NextChar += sizeof( WCHAR );
} else {
Ccb->FullFileName.Length -= sizeof( WCHAR );
}
RtlCopyMemory( NextChar,
LastComponentName->Buffer,
LastComponentName->Length );
Ccb->LastFileNameOffset = (USHORT) (Ccb->FullFileName.Length - LastComponentName->Length);
}
}
//
// Now dequeue ourselves from our old scb and fcb and put us in the
// new fcb and scb queues.
//
RemoveEntryList( &Lcb->ScbLinks );
RemoveEntryList( &Lcb->FcbLinks );
InsertTailList( &Scb->ScbType.Index.LcbQueue, &Lcb->ScbLinks );
Lcb->Scb = Scb;
InsertTailList( &Fcb->LcbQueue, &Lcb->FcbLinks );
Lcb->Fcb = Fcb;
}
} finally {
DebugTrace( -1, Dbg, ("NtfsMoveLcb -> VOID\n") );
}
//
// And return to our caller
//
return;
}
VOID
NtfsRenameLcb (
IN PIRP_CONTEXT IrpContext,
IN PLCB Lcb,
IN PUNICODE_STRING LastComponentFileName,
IN UCHAR FileNameFlags,
IN BOOLEAN CheckBufferSizeOnly
)
/*++
Routine Description:
This routine changes the last component name of the input lcb
It also walks through the opened ccb and munges their names and
also removes the lcb from the prefix table
Arguments:
Lcb - Supplies the Lcb being renamed
LastComponentFileName - Supplies the new last component to use
for the lcb name
FileNameFlags - Indicates if this is an NTFS, DOS or hard link
CheckBufferSizeOnly - If TRUE we just want to pass through and verify that
the buffer sizes of the various structures will be large enough for the
new name.
Return Value:
None.
--*/
{
PVCB Vcb = Lcb->Fcb->Vcb;
ULONG BytesNeeded;
PVOID NewAllocation;
PSCB ChildScb;
PLCB TempLcb;
PSCB NextScb;
PCCB Ccb;
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_LCB( Lcb );
PAGED_CODE();
//
// If we're not just checking sizes then remove entries from the prefix table
// and the normalized name for descendents of the current scb.
//
if (!CheckBufferSizeOnly) {
//
// Clear the index offset pointer so we will look this up again.
//
Lcb->QuickIndex.BufferOffset = 0;
//
// Get rid of any prefixes that might still be attached to us
//
NtfsRemovePrefix( Lcb );
//
// And then traverse the graph underneath our fcb and remove all prefixes
// also used there. For each child scb under the fcb we will traverse all of
// its descendant Scb children and for each lcb we encounter we will remove its prefixes.
//
ChildScb = NULL;
while ((ChildScb = NtfsGetNextChildScb( Lcb->Fcb, ChildScb )) != NULL) {
//
// Loop through his Lcb's and remove the prefix entries.
//
TempLcb = NULL;
while ((TempLcb = NtfsGetNextChildLcb(ChildScb, TempLcb)) != NULL) {
NtfsRemovePrefix( TempLcb );
}
//
// Now we have to descend into this Scb subtree, if it exists.
// Then remove the prefix entries on all of the links found.
//
NextScb = ChildScb;
while ((NextScb = NtfsGetNextScb(NextScb, ChildScb)) != NULL) {
//
// If this is an index Scb with a normalized name, then free
// the normalized name.
//
if ((SafeNodeType( NextScb ) == NTFS_NTC_SCB_INDEX) &&
(NextScb->ScbType.Index.NormalizedName.Buffer != NULL)) {
NtfsFreePool( NextScb->ScbType.Index.NormalizedName.Buffer );
NextScb->ScbType.Index.NormalizedName.Buffer = NULL;
NextScb->ScbType.Index.NormalizedName.Length = 0;
}
TempLcb = NULL;
while ((TempLcb = NtfsGetNextChildLcb(NextScb, TempLcb)) != NULL) {
NtfsRemovePrefix( TempLcb );
}
}
}
}
//
// Remember the number of bytes needed for the last component.
//
BytesNeeded = LastComponentFileName->Length;
//
// Check if we need to allocate a new filename attribute. If so allocate
// it and store it into the new allocation buffer.
//
if (Lcb->ExactCaseLink.LinkName.MaximumLength < BytesNeeded) {
NewAllocation = NtfsAllocatePool( PagedPool,
BytesNeeded + NtfsFileNameSizeFromLength( BytesNeeded ));
//
// Set up the existing names into the new buffer. That way if we have an allocation
// failure below with the Ccb's the Lcb is still in a valid state.
//
RtlCopyMemory( NewAllocation,
Lcb->FileNameAttr,
NtfsFileNameSizeFromLength( Lcb->ExactCaseLink.LinkName.MaximumLength ));
RtlCopyMemory( Add2Ptr( NewAllocation, NtfsFileNameSizeFromLength( BytesNeeded )),
Lcb->IgnoreCaseLink.LinkName.Buffer,
Lcb->IgnoreCaseLink.LinkName.MaximumLength );
if (Lcb->FileNameAttr != (PFILE_NAME) &Lcb->ParentDirectory) {
NtfsFreePool( Lcb->FileNameAttr );
}
Lcb->FileNameAttr = NewAllocation;
Lcb->ExactCaseLink.LinkName.MaximumLength =
Lcb->IgnoreCaseLink.LinkName.MaximumLength = (USHORT) BytesNeeded;
Lcb->ExactCaseLink.LinkName.Buffer = (PWCHAR) &Lcb->FileNameAttr->FileName;
Lcb->IgnoreCaseLink.LinkName.Buffer = Add2Ptr( Lcb->FileNameAttr,
NtfsFileNameSizeFromLength( BytesNeeded ));
}
//
// Now for every ccb attached to us we need to check if we need a new
// filename buffer.
//
Ccb = NULL;
while ((Ccb = NtfsGetNextCcb(Lcb, Ccb)) != NULL) {
//
// If the Ccb last component length is zero, this Ccb is for a
// file object that was opened by File Id. We won't to any
// work for the name in the fileobject for this. Otherwise we
// compute the length of the new name and see if we have enough space
//
if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_FILE_ID | CCB_FLAG_CLEANUP )) {
BytesNeeded = Ccb->LastFileNameOffset + LastComponentFileName->Length;
if ((ULONG)Ccb->FullFileName.MaximumLength < BytesNeeded) {
//
// Allocate a new file name buffer and copy the existing data back into it.
//
NewAllocation = NtfsAllocatePool( PagedPool, BytesNeeded );
RtlCopyMemory( NewAllocation,
Ccb->FullFileName.Buffer,
Ccb->FullFileName.Length );
if (FlagOn( Ccb->Flags, CCB_FLAG_ALLOCATED_FILE_NAME )) {
NtfsFreePool( Ccb->FullFileName.Buffer );
}
Ccb->FullFileName.Buffer = NewAllocation;
Ccb->FullFileName.MaximumLength = (USHORT) BytesNeeded;
SetFlag( Ccb->Flags, CCB_FLAG_ALLOCATED_FILE_NAME );
}
}
}
//
// Now update the Lcb and Ccb's with the new values if we are to rewrite the buffers.
//
if (!CheckBufferSizeOnly) {
BytesNeeded = LastComponentFileName->Length;
Lcb->FileNameAttr->FileNameLength = (USHORT) BytesNeeded / sizeof( WCHAR );
Lcb->FileNameAttr->Flags = FileNameFlags;
Lcb->ExactCaseLink.LinkName.Length =
Lcb->IgnoreCaseLink.LinkName.Length = (USHORT) LastComponentFileName->Length;
RtlCopyMemory( Lcb->ExactCaseLink.LinkName.Buffer,
LastComponentFileName->Buffer,
BytesNeeded );
RtlCopyMemory( Lcb->IgnoreCaseLink.LinkName.Buffer,
LastComponentFileName->Buffer,
BytesNeeded );
NtfsUpcaseName( IrpContext->Vcb->UpcaseTable,
IrpContext->Vcb->UpcaseTableSize,
&Lcb->IgnoreCaseLink.LinkName );
//
// Now for every ccb attached to us we need to munge it file object name by
// copying over the entire new name
//
Ccb = NULL;
while ((Ccb = NtfsGetNextCcb(Lcb, Ccb)) != NULL) {
//
// We ignore any Ccb's which are associated with open by File Id
// file objects. We also ignore any Ccb's which don't have a file
// object pointer.
//
if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_FILE_ID | CCB_FLAG_CLEANUP )) {
RtlCopyMemory( &Ccb->FullFileName.Buffer[ Ccb->LastFileNameOffset / sizeof( WCHAR ) ],
LastComponentFileName->Buffer,
BytesNeeded );
Ccb->FullFileName.Length = Ccb->LastFileNameOffset + (USHORT) BytesNeeded;
}
}
}
return;
}
VOID
NtfsCombineLcbs (
IN PIRP_CONTEXT IrpContext,
IN PLCB PrimaryLcb,
IN PLCB AuxLcb
)
/*++
Routine Description:
This routine is called for the case where we have multiple Lcb's for a
file which connect to the same Scb. We are performing a link rename
operation which causes the links to be combined and we need to
move all of the Ccb's to the same Lcb. This routine will be called only
after the names have been munged so that they are identical.
(i.e. call NtfsRenameLcb first)
Arguments:
PrimaryLcb - Supplies the Lcb to receive all the Ccb's and Pcb's.
AuxLcb - Supplies the Lcb to strip.
Return Value:
None.
--*/
{
PLIST_ENTRY Links;
PCCB NextCcb;
DebugTrace( +1, Dbg, ("NtfsCombineLcbs: Entered\n") );
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_LCB( PrimaryLcb );
ASSERT_LCB( AuxLcb );
PAGED_CODE();
//
// Move all of the Ccb's first.
//
for (Links = AuxLcb->CcbQueue.Flink;
Links != &AuxLcb->CcbQueue;
Links = AuxLcb->CcbQueue.Flink) {
NextCcb = CONTAINING_RECORD( Links, CCB, LcbLinks );
NtfsUnlinkCcbFromLcb( IrpContext, NextCcb );
NtfsLinkCcbToLcb( IrpContext, NextCcb, PrimaryLcb );
}
//
// Now do the prefix entries.
//
NtfsRemovePrefix( AuxLcb );
//
// Finally we need to transfer the unclean counts from the
// Lcb being merged to the primary Lcb.
//
PrimaryLcb->CleanupCount += AuxLcb->CleanupCount;
DebugTrace( -1, Dbg, ("NtfsCombineLcbs: Entered\n") );
return;
}
PLCB
NtfsLookupLcbByFlags (
IN PFCB Fcb,
IN UCHAR FileNameFlags
)
/*++
Routine Description:
This routine is called to find a split primary link by the file flag
only.
Arguments:
Fcb - This is the Fcb for the file.
FileNameFlags - This is the file flag to search for. We will return
a link which matches this exactly.
Return Value:
PLCB - The Lcb which has the desired flag, NULL otherwise.
--*/
{
PLCB Lcb;
PLIST_ENTRY Links;
PLCB ThisLcb;
PAGED_CODE();
DebugTrace( +1, Dbg, ("NtfsLookupLcbByFlags: Entered\n") );
Lcb = NULL;
//
// Walk through the Lcb's for the file, looking for an exact match.
//
for (Links = Fcb->LcbQueue.Flink; Links != &Fcb->LcbQueue; Links = Links->Flink) {
ThisLcb = CONTAINING_RECORD( Links, LCB, FcbLinks );
if (ThisLcb->FileNameAttr->Flags == FileNameFlags) {
Lcb = ThisLcb;
break;
}
}
DebugTrace( -1, Dbg, ("NtfsLookupLcbByFlags: Exit\n") );
return Lcb;
}
ULONG
NtfsLookupNameLengthViaLcb (
IN PFCB Fcb,
OUT PBOOLEAN LeadingBackslash
)
/*++
Routine Description:
This routine is called to find the length of the file name by walking
backwards through the Lcb links.
Arguments:
Fcb - This is the Fcb for the file.
LeadingBackslash - On return, indicates whether this chain begins with a
backslash.
Return Value:
ULONG This is the length of the bytes found in the Lcb chain.
--*/
{
ULONG NameLength;
DebugTrace( +1, Dbg, ("NtfsLookupNameLengthViaLcb: Entered\n") );
//
// Initialize the return values.
//
NameLength = 0;
*LeadingBackslash = FALSE;
//
// If there is no Lcb we are done.
//
if (!IsListEmpty( &Fcb->LcbQueue )) {
PLCB ThisLcb;
BOOLEAN FirstComponent;
//
// Walk up the list of Lcb's and count the name elements.
//
FirstComponent = TRUE;
ThisLcb = CONTAINING_RECORD( Fcb->LcbQueue.Flink,
LCB,
FcbLinks );
//
// Loop until we have reached the root or there are no more Lcb's.
//
while (TRUE) {
if (ThisLcb == Fcb->Vcb->RootLcb) {
NameLength += sizeof( WCHAR );
*LeadingBackslash = TRUE;
break;
}
//
// If this is not the first component, we add room for a separating
// forward slash.
//
if (!FirstComponent) {
NameLength += sizeof( WCHAR );
} else {
FirstComponent = FALSE;
}
NameLength += ThisLcb->ExactCaseLink.LinkName.Length;
//
// If the next Fcb has no Lcb we exit.
//
Fcb = ((PSCB) ThisLcb->Scb)->Fcb;
if (IsListEmpty( &Fcb->LcbQueue)) {
break;
}
ThisLcb = CONTAINING_RECORD( Fcb->LcbQueue.Flink,
LCB,
FcbLinks );
}
//
// If this is a system file we use the hard coded name.
//
} else if (NtfsSegmentNumber( &Fcb->FileReference ) <= UPCASE_TABLE_NUMBER) {
NameLength = NtfsSystemFiles[NtfsSegmentNumber( &Fcb->FileReference )].Length;
*LeadingBackslash = TRUE;
}
DebugTrace( -1, Dbg, ("NtfsLookupNameLengthViaLcb: Exit - %08lx\n", NameLength) );
return NameLength;
}
VOID
NtfsFileNameViaLcb (
IN PFCB Fcb,
IN PWCHAR FileName,
ULONG Length,
ULONG BytesToCopy
)
/*++
Routine Description:
This routine is called to fill a buffer with the generated filename. The name
is constructed by walking backwards through the Lcb chain from the current Fcb.
Arguments:
Fcb - This is the Fcb for the file.
FileName - This is the buffer to fill with the name.
Length - This is the length of the name. Already calculated by calling
NtfsLookupNameLengthViaLcb.
BytesToCopy - This indicates the number of bytes we are to copy. We drop
any characters out of the trailing Lcb's to only insert the beginning
of the path.
Return Value:
None.
--*/
{
ULONG BytesToDrop;
PWCHAR ThisName;
DebugTrace( +1, Dbg, ("NtfsFileNameViaLcb: Entered\n") );
//
// If there is no Lcb or there are no bytes to copy we are done.
//
if (BytesToCopy) {
if (!IsListEmpty( &Fcb->LcbQueue )) {
PLCB ThisLcb;
BOOLEAN FirstComponent;
//
// Walk up the list of Lcb's and count the name elements.
//
FirstComponent = TRUE;
ThisLcb = CONTAINING_RECORD( Fcb->LcbQueue.Flink,
LCB,
FcbLinks );
//
// Loop until we have reached the root or there are no more Lcb's.
//
while (TRUE) {
if (ThisLcb == Fcb->Vcb->RootLcb) {
*FileName = L'\\';
break;
}
//
// If this is not the first component, we add room for a separating
// forward slash.
//
if (!FirstComponent) {
Length -= sizeof( WCHAR );
ThisName = (PWCHAR) Add2Ptr( FileName,
Length );
if (Length < BytesToCopy) {
*ThisName = L'\\';
}
} else {
FirstComponent = FALSE;
}
//
// Length is current pointing just beyond where the next
// copy will end. If we are beyond the number of bytes to copy
// then we will truncate the copy.
//
if (Length > BytesToCopy) {
BytesToDrop = Length - BytesToCopy;
} else {
BytesToDrop = 0;
}
Length -= ThisLcb->ExactCaseLink.LinkName.Length;
ThisName = (PWCHAR) Add2Ptr( FileName,
Length );
//
// Only perform the copy if we are in the range of bytes to copy.
//
if (Length < BytesToCopy) {
RtlCopyMemory( ThisName,
ThisLcb->ExactCaseLink.LinkName.Buffer,
ThisLcb->ExactCaseLink.LinkName.Length - BytesToDrop );
}
//
// If the next Fcb has no Lcb we exit.
//
Fcb = ((PSCB) ThisLcb->Scb)->Fcb;
if (IsListEmpty( &Fcb->LcbQueue)) {
break;
}
ThisLcb = CONTAINING_RECORD( Fcb->LcbQueue.Flink,
LCB,
FcbLinks );
}
//
// If this is a system file, we use the hard coded name.
//
} else if (NtfsSegmentNumber(&Fcb->FileReference) <= UPCASE_TABLE_NUMBER) {
if (BytesToCopy > NtfsSystemFiles[NtfsSegmentNumber( &Fcb->FileReference )].Length) {
BytesToCopy = NtfsSystemFiles[NtfsSegmentNumber( &Fcb->FileReference )].Length;
}
RtlCopyMemory( FileName,
NtfsSystemFiles[NtfsSegmentNumber( &Fcb->FileReference )].Buffer,
BytesToCopy );
}
}
DebugTrace( -1, Dbg, ("NtfsFileNameViaLcb: Exit\n") );
return;
}
PCCB
NtfsCreateCcb (
IN PIRP_CONTEXT IrpContext,
IN PFCB Fcb,
IN BOOLEAN Indexed,
IN USHORT EaModificationCount,
IN ULONG Flags,
IN UNICODE_STRING FileName,
IN ULONG LastFileNameOffset
)
/*++
Routine Description:
This routine creates a new CCB record
Arguments:
Fcb - This is the Fcb for the file. We will check if we can allocate
the Ccb from an embedded structure.
Indexed - Indicates if we need an index Ccb.
EaModificationCount - This is the current modification count in the
Fcb for this file.
Flags - Informational flags for this Ccb.
FileName - Full path used to open this file.
LastFileNameOffset - Supplies the offset (in bytes) of the last component
for the name that the user is opening. If this is the root
directory it should denote "\" and all other ones should not
start with a backslash.
Return Value:
CCB - returns a pointer to the newly allocate CCB
--*/
{
PCCB Ccb;
PAGED_CODE();
ASSERT_IRP_CONTEXT( IrpContext );
DebugTrace( +1, Dbg, ("NtfsCreateCcb\n") );
//
// Allocate a new CCB Record. If the Fcb is nonpaged then we must allocate
// a non-paged ccb. Then test if we can allocate this out of the Fcb.
//
if (FlagOn( Fcb->FcbState, FCB_STATE_NONPAGED )) {
if (Indexed) {
Ccb = NtfsAllocatePoolWithTag( NonPagedPool, sizeof(CCB), 'CftN' );
} else {
Ccb = NtfsAllocatePoolWithTag( NonPagedPool, sizeof(CCB_DATA), 'cftN' );
}
} else if (FlagOn( Fcb->FcbState, FCB_STATE_COMPOUND_INDEX ) &&
(SafeNodeType( &((PFCB_INDEX) Fcb)->Ccb ) == 0)) {
Ccb = (PCCB) &((PFCB_INDEX) Fcb)->Ccb;
} else if (!Indexed &&
FlagOn( Fcb->FcbState, FCB_STATE_COMPOUND_DATA ) &&
(SafeNodeType( &((PFCB_DATA) Fcb)->Ccb ) == 0)) {
Ccb = (PCCB) &((PFCB_DATA) Fcb)->Ccb;
} else {
if (Indexed) {
Ccb = (PCCB)ExAllocateFromPagedLookasideList( &NtfsCcbLookasideList );
} else {
Ccb = (PCCB)ExAllocateFromPagedLookasideList( &NtfsCcbDataLookasideList );
}
}
//
// Zero and initialize the correct structure.
//
if (Indexed) {
RtlZeroMemory( Ccb, sizeof(CCB) );
//
// Set the proper node type code and node byte size
//
Ccb->NodeTypeCode = NTFS_NTC_CCB_INDEX;
Ccb->NodeByteSize = sizeof(CCB);
} else {
RtlZeroMemory( Ccb, sizeof(CCB_DATA) );
//
// Set the proper node type code and node byte size
//
Ccb->NodeTypeCode = NTFS_NTC_CCB_DATA;
Ccb->NodeByteSize = sizeof(CCB_DATA);
}
//
// Copy the Ea modification count.
//
Ccb->EaModificationCount = EaModificationCount;
//
// Copy the flags field
//
Ccb->Flags = Flags;
//
// Set the file object and last file name offset fields
//
Ccb->FullFileName = FileName;
Ccb->LastFileNameOffset = (USHORT)LastFileNameOffset;
//
// Initialize the Lcb queue.
//
InitializeListHead( &Ccb->LcbLinks );
#ifdef _CAIRO_
if (FlagOn( Fcb->Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_ENABLED )) {
//
// CAIROBUG: Consider doing this if VCB_QUOTA_TRACKING_REQUESTED
// is on too.
//
//
// Get the owner id of the calling thread. This must be done at
// create time since that is the only time the owner is valid.
//
Ccb->OwnerId = NtfsGetCallersUserId( IrpContext );
}
#endif _CAIRO_
DebugTrace( -1, Dbg, ("NtfsCreateCcb -> %08lx\n", Ccb) );
return Ccb;
}
VOID
NtfsDeleteCcb (
IN PIRP_CONTEXT IrpContext,
IN PFCB Fcb,
IN OUT PCCB *Ccb
)
/*++
Routine Description:
This routine deallocates the specified CCB record.
Arguments:
Fcb - This is the Fcb for the file. We will check if we can allocate
the Ccb from an embedded structure.
Ccb - Supplies the CCB to remove
Return Value:
None
--*/
{
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_CCB( *Ccb );
PAGED_CODE();
DebugTrace( +1, Dbg, ("NtfsDeleteCcb, Ccb = %08lx\n", Ccb) );
//
// Deallocate any structures the Ccb is pointing to. The following
// are only in index Ccb.
//
if (SafeNodeType( *Ccb ) == NTFS_NTC_CCB_INDEX) {
if ((*Ccb)->QueryBuffer != NULL) { NtfsFreePool( (*Ccb)->QueryBuffer ); }
if ((*Ccb)->IndexEntry != NULL) { NtfsFreePool( (*Ccb)->IndexEntry ); }
if ((*Ccb)->IndexContext != NULL) {
PINDEX_CONTEXT IndexContext;
if ((*Ccb)->IndexContext->Base != (*Ccb)->IndexContext->LookupStack) {
NtfsFreePool( (*Ccb)->IndexContext->Base );
}
//
// Copy the IndexContext pointer into the stack so we don't dereference the
// paged Ccb while holding a spinlock.
//
IndexContext = (*Ccb)->IndexContext;
ExFreeToPagedLookasideList( &NtfsIndexContextLookasideList, IndexContext );
}
}
if (FlagOn( (*Ccb)->Flags, CCB_FLAG_ALLOCATED_FILE_NAME )) {
NtfsFreePool( (*Ccb)->FullFileName.Buffer );
}
//
// Deallocate the Ccb simply clear the flag in the Ccb header.
//
if ((*Ccb == (PCCB) &((PFCB_DATA) Fcb)->Ccb) ||
(*Ccb == (PCCB) &((PFCB_INDEX) Fcb)->Ccb)) {
(*Ccb)->NodeTypeCode = 0;
} else {
if (SafeNodeType( *Ccb ) == NTFS_NTC_CCB_INDEX) {
ExFreeToPagedLookasideList( &NtfsCcbLookasideList, *Ccb );
} else {
ExFreeToPagedLookasideList( &NtfsCcbDataLookasideList, *Ccb );
}
}
//
// Zero out the input pointer
//
*Ccb = NULL;
//
// And return to our caller
//
DebugTrace( -1, Dbg, ("NtfsDeleteCcb -> VOID\n") );
return;
}
PIRP_CONTEXT
NtfsCreateIrpContext (
IN PIRP Irp OPTIONAL,
IN BOOLEAN Wait
)
/*++
Routine Description:
This routine creates a new IRP_CONTEXT record
Arguments:
Irp - Supplies the originating Irp. Won't be present if this is a defrag
operation.
Wait - Supplies the wait value to store in the context
Return Value:
PIRP_CONTEXT - returns a pointer to the newly allocate IRP_CONTEXT Record
--*/
{
PIRP_CONTEXT IrpContext = NULL;
PIO_STACK_LOCATION IrpSp;
PVCB Vcb = NULL;
BOOLEAN AllocateFromPool = FALSE;
ASSERT_OPTIONAL_IRP( Irp );
// DebugTrace( +1, Dbg, ("NtfsCreateIrpContext\n") );
if (ARGUMENT_PRESENT( Irp )) {
IrpSp = IoGetCurrentIrpStackLocation( Irp );
//
// If we were called with our file system device object instead of a
// volume device object and this is not a mount, the request is illegal.
//
if ((IrpSp->DeviceObject->Size == (USHORT)sizeof(DEVICE_OBJECT)) &&
(IrpSp->FileObject != NULL)) {
ExRaiseStatus( STATUS_INVALID_DEVICE_REQUEST );
}
}
//
// Allocate an IrpContext from zone if available, otherwise from
// non-paged pool.
//
DebugDoit( NtfsFsdEntryCount += 1);
IrpContext = (PIRP_CONTEXT)ExAllocateFromNPagedLookasideList( &NtfsIrpContextLookasideList );
RtlZeroMemory( IrpContext, sizeof(IRP_CONTEXT) );
//
// Set the proper node type code and node byte size
//
IrpContext->NodeTypeCode = NTFS_NTC_IRP_CONTEXT;
IrpContext->NodeByteSize = sizeof(IRP_CONTEXT);
//
// Set the originating Irp field
//
IrpContext->OriginatingIrp = Irp;
if (ARGUMENT_PRESENT( Irp )) {
//
// Copy RealDevice for workque algorithms, and also set WriteThrough
// if there is a file object.
//
if (IrpSp->FileObject != NULL) {
PVOLUME_DEVICE_OBJECT VolumeDeviceObject;
PFILE_OBJECT FileObject = IrpSp->FileObject;
IrpContext->RealDevice = FileObject->DeviceObject;
//
// Locate the volume device object and Vcb that we are trying to access
// so we can see if the request is WriteThrough. We ignore the
// write-through flag for close and cleanup.
//
VolumeDeviceObject = (PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject;
Vcb = &VolumeDeviceObject->Vcb;
if (IsFileWriteThrough( FileObject, Vcb )) {
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH);
}
//
// We would still like to find out the Vcb in all cases except for
// mount.
//
} else if (IrpSp->DeviceObject != NULL) {
Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb;
}
//
// Major/Minor Function codes
//
IrpContext->MajorFunction = IrpSp->MajorFunction;
IrpContext->MinorFunction = IrpSp->MinorFunction;
}
//
// Set the Vcb we found (or NULL).
//
IrpContext->Vcb = Vcb;
//
// Set the wait parameter
//
if (Wait) { SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); }
//
// Initialize the recently deallocated record queue and exclusive Scb queue
//
InitializeListHead( &IrpContext->RecentlyDeallocatedQueue );
InitializeListHead( &IrpContext->ExclusiveFcbList );
//
// Set up LogFull testing
//
DebugDoit( IrpContext->CurrentFailCount = IrpContext->NextFailCount = NtfsFailCheck );
//
// return and tell the caller
//
// DebugTrace( -1, Dbg, ("NtfsCreateIrpContext -> %08lx\n", IrpContext) );
return IrpContext;
}
VOID
NtfsDeleteIrpContext (
IN OUT PIRP_CONTEXT *IrpContext
)
/*++
Routine Description:
This routine deallocates and removes the specified IRP_CONTEXT record
from the Ntfs in memory data structures. It should only be called
by NtfsCompleteRequest.
Arguments:
IrpContext - Supplies the IRP_CONTEXT to remove
Return Value:
None
--*/
{
PFCB Fcb;
ASSERT_IRP_CONTEXT( *IrpContext );
// DebugTrace( +1, Dbg, ("NtfsDeleteIrpContext, *IrpContext = %08lx\n", *IrpContext) );
if (!IsListEmpty( &(*IrpContext)->RecentlyDeallocatedQueue )) {
NtfsDeallocateRecordsComplete( *IrpContext );
}
//
// Just in case we somehow get here with a transaction ID, clear
// it here so we do not loop forever.
//
ASSERT((*IrpContext)->TransactionId == 0);
(*IrpContext)->TransactionId = 0;
//
// Free any exclusive paging I/O resource, or IoAtEof condition,
// this field is overlayed, minimally in write.c.
//
Fcb = (*IrpContext)->FcbWithPagingExclusive;
if (Fcb != NULL) {
if (Fcb->NodeTypeCode == NTFS_NTC_FCB) {
NtfsReleasePagingIo((*IrpContext), Fcb );
} else {
FsRtlUnlockFsRtlHeader( (PFSRTL_ADVANCED_FCB_HEADER) Fcb );
(*IrpContext)->FcbWithPagingExclusive = NULL;
}
}
//
// Finally, now that we have written the forget record, we can free
// any exclusive Scbs that we have been holding.
//
while (!IsListEmpty(&(*IrpContext)->ExclusiveFcbList)) {
Fcb = (PFCB)CONTAINING_RECORD((*IrpContext)->ExclusiveFcbList.Flink,
FCB,
ExclusiveFcbLinks );
NtfsReleaseFcb( *IrpContext, Fcb );
}
//
// Go through and free any Scb's in the queue of shared Scb's for transactions.
//
if ((*IrpContext)->SharedScb != NULL) {
NtfsReleaseSharedResources( *IrpContext );
}
//
// Make sure there are no Scb snapshots left.
//
NtfsFreeSnapshotsForFcb( *IrpContext, NULL );
//
// If we can delete this Irp Context do so now.
//
if (!FlagOn( (*IrpContext)->Flags, IRP_CONTEXT_FLAG_DONT_DELETE )) {
//
// If there is an Io context pointer in the irp context and it is not
// on the stack, then free it.
//
if (FlagOn( (*IrpContext)->Flags, IRP_CONTEXT_FLAG_ALLOC_CONTEXT )
&& ((*IrpContext)->Union.NtfsIoContext != NULL)) {
ExFreeToNPagedLookasideList( &NtfsIoContextLookasideList, (*IrpContext)->Union.NtfsIoContext );
}
//
// If we have captured the subject context then free it now.
//
if (FlagOn( (*IrpContext)->Flags, IRP_CONTEXT_FLAG_ALLOC_SECURITY )
&& (*IrpContext)->Union.SubjectContext != NULL) {
SeReleaseSubjectContext( (*IrpContext)->Union.SubjectContext );
NtfsFreePool( (*IrpContext)->Union.SubjectContext );
}
//
// Return the IRP context record to the lookaside or to pool depending
// how much is currently in the lookaside
//
ExFreeToNPagedLookasideList( &NtfsIrpContextLookasideList, *IrpContext );
//
// Zero out the input pointer
//
*IrpContext = NULL;
} else {
//
// Set up the Irp Context for retry.
//
ClearFlag( (*IrpContext)->Flags, IRP_CONTEXT_FLAGS_CLEAR_ON_RETRY );
(*IrpContext)->DeallocatedClusters = 0;
(*IrpContext)->FreeClusterChange = 0;
}
//
// And return to our caller
//
// DebugTrace( -1, Dbg, ("NtfsDeleteIrpContext -> VOID\n") );
return;
}
VOID
NtfsTeardownStructures (
IN PIRP_CONTEXT IrpContext,
IN PVOID FcbOrScb,
IN PLCB Lcb OPTIONAL,
IN BOOLEAN CheckForAttributeTable,
IN BOOLEAN DontWaitForAcquire,
OUT PBOOLEAN RemovedFcb OPTIONAL
)
/*++
Routine Description:
This routine is called to start the teardown process on a node in
the Fcb/Scb tree. We will attempt to remove this node and then
move up the tree removing any nodes held by this node.
This routine deals with the case where a single node may be holding
multiple parents in memory. If we are passed an input Lcb we will
use that to walk up the tree. If the Vcb is held exclusively we
will try to trim any nodes that have no open files on them.
This routine takes the following steps:
Remove as many Scb's and file objects from the starting
Fcb.
If the Fcb can't go away but has multiple links then remove
whatever links possible. If we have the Vcb we can
do all of them but we will leave a single link behind
to optimize prefix lookups. Otherwise we will traverse the
single link we were given.
If the Fcb can go away then we should have the Vcb if there are
multiple links to remove. Otherwise we only remove the link
we were given if there are multiple links. In the single link
case just remove that link.
Arguments:
FcbOrScb - Supplies either an Fcb or an Scb as the start of the
teardown point. The Fcb for this element must be held exclusively.
Lcb - If specified, this is the path up the tree to perform the
teardown.
CheckForAttributeTable - Indicates that we should not teardown an
Scb which is in the attribute table. Instead we will attempt
to put an entry on the async close queue. This will be TRUE
if we may need the Scb to abort the current transaction.
DontWaitForAcquire - Indicates whether we should abort the teardown when
we can't acquire a parent. When called from some path where we may
hold the MftScb or another resource in another path up the tree.
RemovedFcb - Address to store TRUE if we delete the starting Fcb.
Return Value:
None
--*/
{
PSCB StartingScb = NULL;
PFCB Fcb;
BOOLEAN FcbCanBeRemoved;
BOOLEAN RemovedLcb;
BOOLEAN LocalRemovedFcb = FALSE;
PLIST_ENTRY Links;
PLIST_ENTRY NextLink;
PAGED_CODE();
//
// If this is a recursive call to TearDownStructures we return immediately
// doing no operation.
//
if (FlagOn( IrpContext->TopLevelIrpContext->Flags, IRP_CONTEXT_FLAG_IN_TEARDOWN )) {
DebugTrace( 0, Dbg, ("Recursive teardown call\n") );
DebugTrace( -1, Dbg, ("NtfsTeardownStructures -> VOID\n") );
return;
}
if (SafeNodeType(FcbOrScb) == NTFS_NTC_FCB) {
Fcb = FcbOrScb;
} else {
StartingScb = FcbOrScb;
FcbOrScb = Fcb = StartingScb->Fcb;
}
SetFlag( IrpContext->TopLevelIrpContext->Flags, IRP_CONTEXT_FLAG_IN_TEARDOWN );
//
// Use a try-finally to clear the top level irp field.
//
try {
//
// Use our local boolean if the caller didn't supply one.
//
if (!ARGUMENT_PRESENT( RemovedFcb )) {
RemovedFcb = &LocalRemovedFcb;
}
//
// Check this Fcb for removal. Remember if all of the Scb's
// and file objects are gone. We will try to remove the Fcb
// if the cleanup count is zero or if we are walking up
// one directory path of a mult-link file. If the Fcb has
// a non-zero cleanup count but the current Scb has a zero
// cleanup count then try to delete the Scb at the very least.
//
FcbCanBeRemoved = FALSE;
if (Fcb->CleanupCount == 0) {
FcbCanBeRemoved = NtfsPrepareFcbForRemoval( IrpContext,
Fcb,
StartingScb,
CheckForAttributeTable );
} else if (ARGUMENT_PRESENT( StartingScb ) &&
(StartingScb->CleanupCount == 0) &&
(StartingScb->AttributeTypeCode != $ATTRIBUTE_LIST)) {
NtfsRemoveScb( IrpContext, StartingScb, CheckForAttributeTable, &RemovedLcb );
}
//
// There is a single link (typical case) we either try to
// remove that link or we simply return.
//
if (Fcb->LcbQueue.Flink == Fcb->LcbQueue.Blink) {
if (FcbCanBeRemoved) {
NtfsTeardownFromLcb( IrpContext,
Fcb->Vcb,
Fcb,
CONTAINING_RECORD( Fcb->LcbQueue.Flink,
LCB,
FcbLinks ),
CheckForAttributeTable,
DontWaitForAcquire,
&RemovedLcb,
&LocalRemovedFcb );
}
try_return( NOTHING );
//
// If there are multiple links we will try to either remove
// them all or all but one (if the Fcb is not going away) if
// we own the Vcb. We will try to delete the one we were
// given otherwise.
//
} else {
//
// If we have the Vcb we will remove all if the Fcb can
// go away. Otherwise we will leave one.
//
if (NtfsIsExclusiveVcb( Fcb->Vcb )) {
Links = Fcb->LcbQueue.Flink;
while (TRUE) {
//
// Remember the next entry in case the current link
// goes away.
//
NextLink = Links->Flink;
RemovedLcb = FALSE;
NtfsTeardownFromLcb( IrpContext,
Fcb->Vcb,
Fcb,
CONTAINING_RECORD( Links, LCB, FcbLinks ),
CheckForAttributeTable,
FALSE,
&RemovedLcb,
&LocalRemovedFcb );
//
// If couldn't remove this link then munge the
// boolean indicating if the Fcb can be removed
// to make it appear we need to remove all of
// the Lcb's.
//
if (!RemovedLcb) {
FcbCanBeRemoved = TRUE;
}
//
// If the Fcb has been removed then we exit.
// If the next link is the beginning of the
// Lcb queue then we also exit.
// If the next link is the last entry and
// we want to leave a single entry then we
// exit.
//
if (LocalRemovedFcb ||
(NextLink == &Fcb->LcbQueue) ||
(!FcbCanBeRemoved &&
(NextLink->Flink == &Fcb->LcbQueue))) {
try_return( NOTHING );
}
//
// Move to the next link.
//
Links = NextLink;
}
//
// If we have an Lcb just move up that path.
//
} else if (ARGUMENT_PRESENT( Lcb )) {
NtfsTeardownFromLcb( IrpContext,
Fcb->Vcb,
Fcb,
Lcb,
CheckForAttributeTable,
DontWaitForAcquire,
&RemovedLcb,
&LocalRemovedFcb );
}
}
try_exit: NOTHING;
} finally {
DebugUnwind( NtfsTeardownStructures );
//
// If we removed the Fcb then set the caller's value now.
//
if (LocalRemovedFcb) {
*RemovedFcb = TRUE;
}
ClearFlag( IrpContext->TopLevelIrpContext->Flags, IRP_CONTEXT_FLAG_IN_TEARDOWN );
}
return;
}
VOID
NtfsIncrementCleanupCounts (
IN PSCB Scb,
IN PLCB Lcb OPTIONAL,
IN BOOLEAN NonCachedHandle
)
/*++
Routine Description:
This routine increments the cleanup counts for the associated data structures
Arguments:
Scb - Supplies the Scb used in this operation
Lcb - Optionally supplies the Lcb used in this operation
NonCachedHandle - Indicates this handle is for a user non-cached handle.
Return Value:
None.
--*/
{
PVCB Vcb = Scb->Vcb;
//
// This is really a pretty light weight procedure but having it be a procedure
// really helps in debugging the system and keeping track of who increments
// and decrements cleanup counts
//
if (ARGUMENT_PRESENT(Lcb)) { Lcb->CleanupCount += 1; }
InterlockedIncrement( &Scb->CleanupCount );
Scb->Fcb->CleanupCount += 1;
if (NonCachedHandle) {
Scb->NonCachedCleanupCount += 1;
}
InterlockedIncrement( &Vcb->CleanupCount );
return;
}
VOID
NtfsIncrementCloseCounts (
IN PSCB Scb,
IN BOOLEAN SystemFile,
IN BOOLEAN ReadOnly
)
/*++
Routine Description:
This routine increments the close counts for the associated data structures
Arguments:
Scb - Supplies the Scb used in this operation
SystemFile - Indicates if the Scb is for a system file (if so then
the Vcb system file close count in also incremented)
ReadOnly - Indicates if the Scb is opened readonly. (if so then the
Vcb Read Only close count is also incremented)
Return Value:
None.
--*/
{
PVCB Vcb = Scb->Vcb;
//
// This is really a pretty light weight procedure but having it be a procedure
// really helps in debugging the system and keeping track of who increments
// and decrements close counts
//
InterlockedIncrement( &Scb->CloseCount );
InterlockedIncrement( &Scb->Fcb->CloseCount );
InterlockedIncrement( &Vcb->CloseCount );
if (SystemFile) {
InterlockedIncrement( &Vcb->SystemFileCloseCount );
}
if (ReadOnly) {
InterlockedIncrement( &Vcb->ReadOnlyCloseCount );
}
//
// We will always clear the delay close flag in this routine.
//
ClearFlag( Scb->ScbState, SCB_STATE_DELAY_CLOSE );
return;
}
VOID
NtfsDecrementCleanupCounts (
IN PSCB Scb,
IN PLCB Lcb OPTIONAL,
IN BOOLEAN NonCachedHandle
)
/*++
Routine Description:
This procedure decrements the cleanup counts for the associated data structures
and if necessary it also start to cleanup associated internal attribute streams
Arguments:
Scb - Supplies the Scb used in this operation
Lcb - Optionally supplies the Lcb used in this operation
NonCachedHandle - Indicates this handle is for a user non-cached handle.
Return Value:
None.
--*/
{
PVCB Vcb = Scb->Vcb;
ASSERT_SCB( Scb );
ASSERT_FCB( Scb->Fcb );
ASSERT_VCB( Scb->Fcb->Vcb );
ASSERT_OPTIONAL_LCB( Lcb );
//
// First we decrement the appropriate cleanup counts
//
if (ARGUMENT_PRESENT(Lcb)) { Lcb->CleanupCount -= 1; }
InterlockedDecrement( &Scb->CleanupCount );
Scb->Fcb->CleanupCount -= 1;
if (NonCachedHandle) {
Scb->NonCachedCleanupCount -= 1;
}
InterlockedDecrement( &Vcb->CleanupCount );
//
// Now if the Fcb's cleanup count is zero that indicates that we are
// done with this Fcb from a user handle standpoint and we should
// now scan through all of the Scb's that are opened under this
// Fcb and shutdown any internal attributes streams we have open.
// For example, EAs and ACLs. We only need to do one. The domino effect
// will take of the rest.
//
if (Scb->Fcb->CleanupCount == 0) {
PSCB NextScb;
//
// Remember if we are dealing with a system file and return immediately.
//
if (NtfsSegmentNumber( &Scb->Fcb->FileReference ) < FIRST_USER_FILE_NUMBER &&
NtfsSegmentNumber( &Scb->Fcb->FileReference ) != ROOT_FILE_NAME_INDEX_NUMBER) {
return;
}
for (NextScb = CONTAINING_RECORD(Scb->Fcb->ScbQueue.Flink, SCB, FcbLinks);
&NextScb->FcbLinks != &Scb->Fcb->ScbQueue;
NextScb = CONTAINING_RECORD( NextScb->FcbLinks.Flink, SCB, FcbLinks )) {
//
// Skip the root index on the volume.
//
if (SafeNodeType( NextScb ) == NTFS_NTC_SCB_ROOT_INDEX) {
continue;
}
//
// If we have an index with children then break out.
//
if ((SafeNodeType( NextScb ) == NTFS_NTC_SCB_INDEX) &&
!IsListEmpty( &NextScb->ScbType.Index.LcbQueue )) {
break;
}
//
// If there is an internal stream then dereference it and get out.
//
if (NextScb->FileObject != NULL) {
NtfsDeleteInternalAttributeStream( NextScb,
(BOOLEAN) (Scb->Fcb->LinkCount == 0) );
break;
}
}
}
//
// And return to our caller
//
return;
}
VOID
NtfsDecrementCloseCounts (
IN PIRP_CONTEXT IrpContext,
IN PSCB Scb,
IN PLCB Lcb OPTIONAL,
IN BOOLEAN SystemFile,
IN BOOLEAN ReadOnly,
IN BOOLEAN DecrementCountsOnly
)
/*++
Routine Description:
This routine decrements the close counts for the associated data structures
and if necessary it will teardown structures that are no longer in use
Arguments:
Scb - Supplies the Scb used in this operation
Lcb - Used if calling teardown to know which path to take.
SystemFile - Indicates if the Scb is for a system file
ReadOnly - Indicates if the Scb was opened readonly
DecrementCountsOnly - Indicates if this operation should only modify the
count fields.
Return Value:
None.
--*/
{
PFCB Fcb = Scb->Fcb;
PVCB Vcb = Scb->Vcb;
ASSERT_SCB( Scb );
ASSERT_FCB( Fcb );
ASSERT_VCB( Fcb->Vcb );
//
// Decrement the close counts
//
InterlockedDecrement( &Scb->CloseCount );
InterlockedDecrement( &Fcb->CloseCount );
InterlockedDecrement( &Vcb->CloseCount );
if (SystemFile) {
InterlockedDecrement( &Vcb->SystemFileCloseCount );
}
if (ReadOnly) {
InterlockedDecrement( &Vcb->ReadOnlyCloseCount );
}
//
// Now if the scb's close count is zero then we are ready to tear
// it down
//
if (!DecrementCountsOnly) {
//
// We want to try to start a teardown from this Scb if
//
// - The close count is zero
//
// or the following are all true
//
// - The cleanup count is zero
// - There is a file object in the Scb
// - It is a data Scb or an empty index Scb
// - It is not an Ntfs system file
//
// The teardown will be noopted if this is a recursive call.
//
if (Scb->CloseCount == 0
||
(Scb->CleanupCount == 0
&& Scb->FileObject != NULL
&& (NtfsSegmentNumber( &Scb->Fcb->FileReference ) >= FIRST_USER_FILE_NUMBER)
&& ((SafeNodeType( Scb ) == NTFS_NTC_SCB_DATA)
|| (SafeNodeType( Scb ) == NTFS_NTC_SCB_MFT)
|| IsListEmpty( &Scb->ScbType.Index.LcbQueue )))) {
NtfsTeardownStructures( IrpContext,
Scb,
Lcb,
FALSE,
FALSE,
NULL );
}
}
//
// And return to our caller
//
return;
}
PERESOURCE
NtfsAllocateEresource (
)
{
KIRQL _SavedIrql;
PERESOURCE Eresource;
KeAcquireSpinLock( &NtfsData.StrucSupSpinLock, &_SavedIrql );
if (NtfsData.FreeEresourceSize > 0) {
Eresource = NtfsData.FreeEresourceArray[--NtfsData.FreeEresourceSize];
KeReleaseSpinLock( &NtfsData.StrucSupSpinLock, _SavedIrql );
} else {
KeReleaseSpinLock( &NtfsData.StrucSupSpinLock, _SavedIrql );
Eresource = NtfsAllocatePoolWithTag( NonPagedPool, sizeof(ERESOURCE), 'rftN' );
ExInitializeResource( Eresource );
NtfsData.FreeEresourceMiss += 1;
}
return Eresource;
}
VOID
NtfsFreeEresource (
IN PERESOURCE Eresource
)
{
KIRQL _SavedIrql;
//
// Do an unsafe test to see if we should put this on our list.
// We want to reinitialize this before adding to the list so
// we don't have a bunch of resources which appear to be held.
//
if (NtfsData.FreeEresourceSize < NtfsData.FreeEresourceTotal) {
ExReinitializeResourceLite( Eresource );
//
// Now acquire the spinlock and do a real test.
//
KeAcquireSpinLock( &NtfsData.StrucSupSpinLock, &_SavedIrql );
if (NtfsData.FreeEresourceSize < NtfsData.FreeEresourceTotal) {
NtfsData.FreeEresourceArray[NtfsData.FreeEresourceSize++] = Eresource;
KeReleaseSpinLock( &NtfsData.StrucSupSpinLock, _SavedIrql );
} else {
KeReleaseSpinLock( &NtfsData.StrucSupSpinLock, _SavedIrql );
ExDeleteResource( Eresource );
NtfsFreePool( Eresource );
}
} else {
ExDeleteResource( Eresource );
NtfsFreePool( Eresource );
}
return;
}
PVOID
NtfsAllocateFcbTableEntry (
IN PRTL_GENERIC_TABLE FcbTable,
IN CLONG ByteSize
)
/*++
Routine Description:
This is a generic table support routine to allocate memory
Arguments:
FcbTable - Supplies the generic table being used
ByteSize - Supplies the number of bytes to allocate
Return Value:
PVOID - Returns a pointer to the allocated data
--*/
{
KIRQL _SavedIrql;
PVOID FcbTableEntry;
UNREFERENCED_PARAMETER( FcbTable );
KeAcquireSpinLock( &NtfsData.StrucSupSpinLock, &_SavedIrql );
if (NtfsData.FreeFcbTableSize > 0) {
FcbTableEntry = NtfsData.FreeFcbTableArray[--NtfsData.FreeFcbTableSize];
KeReleaseSpinLock( &NtfsData.StrucSupSpinLock, _SavedIrql );
} else {
KeReleaseSpinLock( &NtfsData.StrucSupSpinLock, _SavedIrql );
FcbTableEntry = NtfsAllocatePool( PagedPool, ByteSize );
}
return FcbTableEntry;
}
VOID
NtfsFreeFcbTableEntry (
IN PRTL_GENERIC_TABLE FcbTable,
IN PVOID Buffer
)
/*++
Routine Description:
This is a generic table support routine that deallocates memory
Arguments:
FcbTable - Supplies the generic table being used
Buffer - Supplies the buffer being deallocated
Return Value:
None.
--*/
{
KIRQL _SavedIrql;
UNREFERENCED_PARAMETER( FcbTable );
KeAcquireSpinLock( &NtfsData.StrucSupSpinLock, &_SavedIrql );
if (NtfsData.FreeFcbTableSize < FREE_FCB_TABLE_SIZE) {
NtfsData.FreeFcbTableArray[NtfsData.FreeFcbTableSize++] = Buffer;
KeReleaseSpinLock( &NtfsData.StrucSupSpinLock, _SavedIrql );
} else {
KeReleaseSpinLock( &NtfsData.StrucSupSpinLock, _SavedIrql );
NtfsFreePool( Buffer );
}
return;
}
//
// Local support routine
//
VOID
NtfsCheckScbForCache (
IN OUT PSCB Scb
)
/*++
Routine Description:
This routine checks if the Scb has blocks contining
Lsn's or Update sequence arrays and set the appropriate
bit in the Scb state word.
The Scb is Update sequence aware if it the Data Attribute for the
Mft or the Data Attribute for the log file or any index allocation
stream.
The Lsn aware Scb's are the ones above without the Log file.
Arguments:
Scb - Supplies the current Scb
Return Value:
The next Scb in the enumeration, or NULL if Scb was the final one.
--*/
{
//
// Temporarily either sequence 0 or 1 is ok.
//
FILE_REFERENCE MftTemp = {0,0,1};
PAGED_CODE();
//
// Check for Update Sequence Array files first.
//
if ((Scb->AttributeTypeCode == $INDEX_ALLOCATION)
||
(Scb->AttributeTypeCode == $DATA
&& Scb->AttributeName.Length == 0
&& (NtfsEqualMftRef( &Scb->Fcb->FileReference, &MftFileReference )
|| NtfsEqualMftRef( &Scb->Fcb->FileReference, &MftTemp )
|| NtfsEqualMftRef( &Scb->Fcb->FileReference, &Mft2FileReference )
|| NtfsEqualMftRef( &Scb->Fcb->FileReference, &LogFileReference )))) {
SetFlag( Scb->ScbState, SCB_STATE_USA_PRESENT );
}
return;
}
//
// Local support routine.
//
BOOLEAN
NtfsRemoveScb (
IN PIRP_CONTEXT IrpContext,
IN PSCB Scb,
IN BOOLEAN CheckForAttributeTable,
OUT PBOOLEAN HeldByStream
)
/*++
Routine Description:
This routine will try to remove an Scb from the Fcb/Scb tree.
It deals with the case where we can make no attempt to remove
the Scb, the case where we start the process but can't complete
it, and finally the case where we remove the Scb entirely.
The following conditions prevent us from removing the Scb at all.
The open count is greater than 1.
It is the root directory.
It is an index Scb with no stream file and an outstanding close.
It is a data file with a non-zero close count.
We start the teardown under the following conditions.
It is an index with an open count of 1, and a stream file object.
We totally remove the Scb when the open count is zero.
Arguments:
Scb - Supplies the Scb to test
CheckForAttributeTable - Indicates that we don't want to remove this
Scb in this thread if it is in the open attribute table. We will
queue an async close in this case. This is to prevent us from
deleting an Scb which may be needed in the abort path.
HeldByStream - Indicates that this Scb was held by a stream file which
we started the close on.
Return Value:
BOOLEAN - TRUE if the Scb was removed, FALSE otherwise. We return FALSE for
the case where we start the process but don't finish.
--*/
{
BOOLEAN ScbRemoved;
ASSERT_SCB( Scb );
PAGED_CODE();
DebugTrace( +1, Dbg, ("NtfsRemoveScb: Entered\n") );
DebugTrace( 0, Dbg, ("Scb -> %08lx\n", Scb) );
ScbRemoved = FALSE;
*HeldByStream = FALSE;
//
// If the Scb is not the root Scb and the count is less than two,
// then this Scb is a candidate for removal.
//
if (SafeNodeType( Scb ) != NTFS_NTC_SCB_ROOT_INDEX
&& Scb->CleanupCount == 0) {
//
//
// If this is a data file or it is an index without children,
// we can get rid of the Scb if there are no children. If
// there is one open count and it is the file object, we
// can start the cleanup on the file object.
//
if ((SafeNodeType( Scb ) == NTFS_NTC_SCB_DATA) ||
(SafeNodeType( Scb ) == NTFS_NTC_SCB_MFT) ||
IsListEmpty( &Scb->ScbType.Index.LcbQueue )) {
//
// Check if we need to post a request to the async queue.
//
if (CheckForAttributeTable &&
(Scb->NonpagedScb->OpenAttributeTableIndex != 0)) {
NtfsAddScbToFspClose( IrpContext, Scb, FALSE );
} else {
if (Scb->CloseCount == 0) {
NtfsDeleteScb( IrpContext, &Scb );
ScbRemoved = TRUE;
//
// Else we know the open count is 1 or 2. If there is a stream
// file, we discard it (but not for the special system
// files) that get removed on dismount
//
} else if (((Scb->FileObject != NULL) || (Scb->Header.FileObjectC != NULL)) &&
(NtfsSegmentNumber( &Scb->Fcb->FileReference ) >= FIRST_USER_FILE_NUMBER)) {
NtfsDeleteInternalAttributeStream( Scb,
FALSE );
*HeldByStream = TRUE;
}
}
}
}
DebugTrace( -1, Dbg, ("NtfsRemoveScb: Exit -> %04x\n", ScbRemoved) );
return ScbRemoved;
}
//
// Local support routine
//
BOOLEAN
NtfsPrepareFcbForRemoval (
IN PIRP_CONTEXT IrpContext,
IN PFCB Fcb,
IN PSCB StartingScb OPTIONAL,
IN BOOLEAN CheckForAttributeTable
)
/*++
Routine Description:
This routine will attempt to prepare the Fcb for removal from the Fcb/Scb
tree. It will try to remove all of the Scb's and test finally if
all of the close count has gone to zero. NOTE the close count is incremented
by routines to reference this Fcb to keep it from being torn down. An empty
Scb list isn't enough to insure that the Fcb can be removed.
Arguments:
Fcb - This is the Fcb to remove.
StartingScb - This is the Scb to remove first.
CheckForAttributeTable - Indicates that we should not teardown an
Scb which is in the attribute table. Instead we will attempt
to put an entry on the async close queue. This will be TRUE
if we may need the Scb to abort the current transaction.
Return Value:
BOOLEAN - TRUE if the Fcb can be removed, FALSE otherwise.
--*/
{
PSCB Scb;
BOOLEAN HeldByStream;
PAGED_CODE();
//
// Try to remove each Scb in the Fcb queue.
//
while (TRUE) {
if (IsListEmpty( &Fcb->ScbQueue )) {
if (Fcb->CloseCount == 0) {
return TRUE;
} else {
return FALSE;
}
}
if (ARGUMENT_PRESENT( StartingScb )) {
Scb = StartingScb;
StartingScb = NULL;
} else {
Scb = CONTAINING_RECORD( Fcb->ScbQueue.Flink,
SCB,
FcbLinks );
}
//
// Try to remove this Scb. If the call to remove didn't succeed
// but the close count has gone to zero, it means that a recursive
// close was generated which removed a stream file. In that
// case we can delete the Scb now.
//
if (!NtfsRemoveScb( IrpContext, Scb, CheckForAttributeTable, &HeldByStream )) {
if (HeldByStream &&
Scb->CloseCount == 0) {
NtfsDeleteScb( IrpContext, &Scb );
//
// Return FALSE to indicate the Fcb can't go away.
//
} else {
return FALSE;
}
}
}
}
//
// Local support routine
//
VOID
NtfsTeardownFromLcb (
IN PIRP_CONTEXT IrpContext,
IN PVCB Vcb,
IN PFCB StartingFcb,
IN PLCB StartingLcb,
IN BOOLEAN CheckForAttributeTable,
IN BOOLEAN DontWaitForAcquire,
OUT PBOOLEAN RemovedStartingLcb,
OUT PBOOLEAN RemovedStartingFcb
)
/*++
Routine Description:
This routine is called to remove a link and continue moving up the
tree looking for more elements to remove. We will check that the
link is unreferenced. NOTE this Lcb must point up to a directory
so that other than our starting Lcb no Lcb we encounter will
have multiple parents.
Arguments:
Vcb - Vcb for this volume.
StartingFcb - This is the Fcb whose link we are trying to remove.
StartingLcb - This is the Lcb to walk up through. Note that
this may be a bogus pointer. It is only valid if there
is at least one Fcb in the queue.
CheckForAttributeTable - Indicates that we should not teardown an
Scb which is in the attribute table. Instead we will attempt
to put an entry on the async close queue. This will be TRUE
if we may need the Scb to abort the current transaction.
DontWaitForAcquire - Indicates whether we should abort the teardown when
we can't acquire a parent. When called from some path where we may
hold the MftScb or another resource in another path up the tree.
RemovedStartingLcb - Address to store TRUE if we remove the
starting Lcb.
RemovedStartingFcb - Address to store TRUE if we remove the
starting Fcb.
Return Value:
None
--*/
{
PSCB ParentScb;
BOOLEAN AcquiredParentScb = FALSE;
BOOLEAN AcquiredFcb = FALSE;
BOOLEAN LastAccessInFcb;
BOOLEAN FcbUpdateDuplicate;
BOOLEAN AcquiredFcbTable = FALSE;
PLCB Lcb;
PFCB Fcb = StartingFcb;
PAGED_CODE();
//
// Use a try-finally to free any resources held.
//
try {
if (!FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED | FCB_STATE_SYSTEM_FILE ) &&
!FlagOn( StartingLcb->LcbState, LCB_STATE_LINK_IS_GONE ) &&
(FlagOn( Fcb->Vcb->VcbState,
VCB_STATE_VOL_PURGE_IN_PROGRESS | VCB_STATE_VOLUME_MOUNTED ) == VCB_STATE_VOLUME_MOUNTED) &&
(IrpContext->TopLevelIrpContext->ExceptionStatus == STATUS_SUCCESS)) {
FcbUpdateDuplicate = TRUE;
} else {
FcbUpdateDuplicate = FALSE;
}
//
// Check if the correct last access is stored in the Fcb.
//
if (Fcb->CurrentLastAccess != Fcb->Info.LastAccessTime) {
LastAccessInFcb = FALSE;
} else {
LastAccessInFcb = TRUE;
}
while (TRUE) {
ParentScb = NULL;
//
// Look through all of the Lcb's for this Fcb.
//
while (!IsListEmpty( &Fcb->LcbQueue )) {
if (Fcb == StartingFcb) {
Lcb = StartingLcb;
} else {
Lcb = CONTAINING_RECORD( Fcb->LcbQueue.Flink,
LCB,
FcbLinks );
}
if (Lcb->CleanupCount != 0) {
try_return( NOTHING );
}
ParentScb = Lcb->Scb;
//
// Try to acquire the parent but check whether we
// should wait.
//
if (!NtfsAcquireExclusiveFcb( IrpContext,
ParentScb->Fcb,
ParentScb,
TRUE,
DontWaitForAcquire )) {
try_return( NOTHING );
}
if (FlagOn( ParentScb->ScbState, SCB_STATE_FILE_SIZE_LOADED )) {
NtfsSnapshotScb( IrpContext, ParentScb );
}
AcquiredParentScb = TRUE;
if (Lcb->ReferenceCount != 0) {
try_return( NOTHING );
}
//
// This Lcb may be removed. Check first if we need
// to update the duplicate information for this
// entry.
//
if (FcbUpdateDuplicate &&
(FlagOn( (Fcb->InfoFlags | Lcb->InfoFlags), FCB_INFO_DUPLICATE_FLAGS ) ||
!LastAccessInFcb)) {
if (!LastAccessInFcb) {
Fcb->Info.LastAccessTime = Fcb->CurrentLastAccess;
SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_LAST_ACCESS );
LastAccessInFcb = TRUE;
}
//
// Use a try-except, we ignore errors here.
//
try {
if (FlagOn( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO )) {
NtfsUpdateStandardInformation( IrpContext, Fcb );
}
NtfsUpdateDuplicateInfo( IrpContext, Fcb, Lcb, ParentScb );
NtfsUpdateLcbDuplicateInfo( Fcb, Lcb );
} except( FsRtlIsNtstatusExpected( GetExceptionCode() ) ?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH ) {
NOTHING;
}
Fcb->InfoFlags = 0;
ClearFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
}
//
// Now remove the Lcb. Remember if this is our original
// Lcb.
//
if (Lcb == StartingLcb) {
*RemovedStartingLcb = TRUE;
}
NtfsDeleteLcb( IrpContext, &Lcb );
//
// If this is the first Fcb then exit the loop.
//
if (Fcb == StartingFcb) {
break;
}
}
//
// If we get here it means we removed all of the Lcb's we
// could for the current Fcb. If the list is empty we
// can remove the Fcb itself.
//
if (IsListEmpty( &Fcb->LcbQueue )) {
//
// If this is a directory that was opened by Id it is
// possible that we still have an update to perform
// for the duplicate information and possibly for
// standard information.
//
if (FcbUpdateDuplicate &&
(!LastAccessInFcb || FlagOn( Fcb->InfoFlags, FCB_INFO_DUPLICATE_FLAGS ))) {
if (!LastAccessInFcb) {
Fcb->Info.LastAccessTime = Fcb->CurrentLastAccess;
}
//
// Use a try-except, we ignore errors here.
//
try {
NtfsUpdateStandardInformation( IrpContext, Fcb );
NtfsUpdateDuplicateInfo( IrpContext, Fcb, NULL, NULL );
} except( EXCEPTION_EXECUTE_HANDLER ) {
NOTHING;
}
Fcb->InfoFlags = 0;
ClearFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
}
//
// Our worst nightmare has come true. We had to create an Scb
// and a stream in order to write out the duplicate information.
// This will happen if we have a non-resident attribute list.
//
if (!IsListEmpty( &Fcb->ScbQueue)) {
PSCB Scb;
Scb = CONTAINING_RECORD( Fcb->ScbQueue.Flink,
SCB,
FcbLinks );
NtfsDeleteInternalAttributeStream( Scb, TRUE );
}
//
// If the list is now empty then check the reference count.
//
if (IsListEmpty( &Fcb->ScbQueue)) {
//
// Now we are ready to remove the current Fcb. We need to
// do a final check of the reference count to make sure
// it isn't being referenced in an open somewhere.
//
NtfsAcquireFcbTable( IrpContext, Vcb );
AcquiredFcbTable = TRUE;
if (Fcb->ReferenceCount == 0) {
if (Fcb == StartingFcb) {
*RemovedStartingFcb = TRUE;
}
NtfsDeleteFcb( IrpContext, &Fcb, &AcquiredFcbTable );
AcquiredFcb = FALSE;
} else {
NtfsReleaseFcbTable( IrpContext, Vcb );
AcquiredFcbTable = FALSE;
}
}
}
//
// Move to the Fcb for the ParentScb.
//
if (ParentScb == NULL) {
try_return( NOTHING );
}
Fcb = ParentScb->Fcb;
AcquiredFcb = TRUE;
AcquiredParentScb = FALSE;
//
// Check if this Fcb can be removed.
//
if (!NtfsPrepareFcbForRemoval( IrpContext, Fcb, NULL, CheckForAttributeTable )) {
try_return( NOTHING );
}
if (!FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED ) &&
(FlagOn( Fcb->Vcb->VcbState,
VCB_STATE_VOL_PURGE_IN_PROGRESS | VCB_STATE_VOLUME_MOUNTED ) == VCB_STATE_VOLUME_MOUNTED) &&
(IrpContext->TopLevelIrpContext->ExceptionStatus == STATUS_SUCCESS)) {
FcbUpdateDuplicate = TRUE;
} else {
FcbUpdateDuplicate = FALSE;
}
//
// Check if the last access time for this Fcb is out of date.
//
if (Fcb->CurrentLastAccess != Fcb->Info.LastAccessTime) {
LastAccessInFcb = FALSE;
} else {
LastAccessInFcb = TRUE;
}
}
try_exit: NOTHING;
} finally {
DebugUnwind( NtfsTeardownFromLcb );
if (AcquiredFcbTable) {
NtfsReleaseFcbTable( IrpContext, Vcb );
}
if (AcquiredFcb) {
NtfsReleaseFcb( IrpContext, Fcb );
}
if (AcquiredParentScb) {
NtfsReleaseScb( IrpContext, ParentScb );
}
}
return;
}
//
// Local support routine
//
RTL_GENERIC_COMPARE_RESULTS
NtfsFcbTableCompare (
IN PRTL_GENERIC_TABLE FcbTable,
IN PVOID FirstStruct,
IN PVOID SecondStruct
)
/*++
Routine Description:
This is a generic table support routine to compare two fcb table elements
Arguments:
FcbTable - Supplies the generic table being queried
FirstStruct - Supplies the first fcb table element to compare
SecondStruct - Supplies the second fcb table element to compare
Return Value:
RTL_GENERIC_COMPARE_RESULTS - The results of comparing the two
input structures
--*/
{
LONGLONG UNALIGNED *Key1 = (PLONGLONG) &((PFCB_TABLE_ELEMENT)FirstStruct)->FileReference;
LONGLONG UNALIGNED *Key2 = (PLONGLONG) &((PFCB_TABLE_ELEMENT)SecondStruct)->FileReference;
UNREFERENCED_PARAMETER( FcbTable );
PAGED_CODE();
//
// Use also the sequence number for all compares so file references in the
// fcb table are unique over time and space. If we want to ignore sequence
// numbers we can zero out the sequence number field, but then we will also
// need to delete the Fcbs from the table during cleanup and not when the
// fcb really gets deleted. Otherwise we cannot reuse file records.
//
if (*Key1 < *Key2) {
return GenericLessThan;
}
if (*Key1 > *Key2) {
return GenericGreaterThan;
}
return GenericEqual;
}