summaryrefslogtreecommitdiffstats
path: root/private/ntos/cntfs/ntfsdata.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/cntfs/ntfsdata.c')
-rw-r--r--private/ntos/cntfs/ntfsdata.c2607
1 files changed, 2607 insertions, 0 deletions
diff --git a/private/ntos/cntfs/ntfsdata.c b/private/ntos/cntfs/ntfsdata.c
new file mode 100644
index 000000000..fb53a0fa0
--- /dev/null
+++ b/private/ntos/cntfs/ntfsdata.c
@@ -0,0 +1,2607 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ NtfsData.c
+
+Abstract:
+
+ This module declares the global data used by the Ntfs file system.
+
+Author:
+
+ Gary Kimura [GaryKi] 21-May-1991
+
+Revision History:
+
+--*/
+
+#include "NtfsProc.h"
+
+//
+// The Bug check file id for this module
+//
+
+#define BugCheckFileId (NTFS_BUG_CHECK_NTFSDATA)
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CATCH_EXCEPTIONS)
+
+//
+// Define a tag for general pool allocations from this module
+//
+
+#undef MODULE_POOL_TAG
+#define MODULE_POOL_TAG ('NFtN')
+
+#define CollectExceptionStats(VCB,EXCEPTION_CODE) { \
+ if ((VCB) != NULL) { \
+ PFILESYSTEM_STATISTICS FsStat = &(VCB)->Statistics[KeGetCurrentProcessorNumber()]; \
+ if ((EXCEPTION_CODE) == STATUS_LOG_FILE_FULL) { \
+ FsStat->Ntfs.LogFileFullExceptions += 1; \
+ } else { \
+ FsStat->Ntfs.OtherExceptions += 1; \
+ } \
+ } \
+}
+
+//
+// The global fsd data record
+//
+
+NTFS_DATA NtfsData;
+
+//
+// Semaphore to synchronize creation of stream files.
+//
+
+FAST_MUTEX StreamFileCreationFastMutex;
+
+//
+// A mutex and queue of NTFS MCBS that will be freed
+// if we reach over a certain threshold
+//
+
+FAST_MUTEX NtfsMcbFastMutex;
+LIST_ENTRY NtfsMcbLruQueue;
+
+ULONG NtfsMcbHighWaterMark;
+ULONG NtfsMcbLowWaterMark;
+ULONG NtfsMcbCurrentLevel;
+
+BOOLEAN NtfsMcbCleanupInProgress;
+WORK_QUEUE_ITEM NtfsMcbWorkItem;
+
+//
+// The global large integer constants
+//
+
+LARGE_INTEGER NtfsLarge0 = {0x00000000,0x00000000};
+LARGE_INTEGER NtfsLarge1 = {0x00000001,0x00000000};
+
+LONGLONG NtfsLastAccess;
+
+//
+// The following fields are used to allocate nonpaged structures
+// using a lookaside list, and other fixed sized structures from a
+// small cache.
+//
+
+NPAGED_LOOKASIDE_LIST NtfsFileLockLookasideList;
+NPAGED_LOOKASIDE_LIST NtfsIoContextLookasideList;
+NPAGED_LOOKASIDE_LIST NtfsIrpContextLookasideList;
+NPAGED_LOOKASIDE_LIST NtfsKeventLookasideList;
+NPAGED_LOOKASIDE_LIST NtfsScbNonpagedLookasideList;
+NPAGED_LOOKASIDE_LIST NtfsScbSnapshotLookasideList;
+
+PAGED_LOOKASIDE_LIST NtfsCcbLookasideList;
+PAGED_LOOKASIDE_LIST NtfsCcbDataLookasideList;
+PAGED_LOOKASIDE_LIST NtfsDeallocatedRecordsLookasideList;
+PAGED_LOOKASIDE_LIST NtfsFcbDataLookasideList;
+PAGED_LOOKASIDE_LIST NtfsFcbIndexLookasideList;
+PAGED_LOOKASIDE_LIST NtfsIndexContextLookasideList;
+PAGED_LOOKASIDE_LIST NtfsLcbLookasideList;
+PAGED_LOOKASIDE_LIST NtfsNukemLookasideList;
+PAGED_LOOKASIDE_LIST NtfsScbDataLookasideList;
+
+//
+// Useful constant Unicode strings.
+//
+
+//
+// This is the string for the name of the index allocation attributes.
+//
+
+UNICODE_STRING NtfsFileNameIndex;
+WCHAR NtfsFileNameIndexName[] = { '$', 'I','0' + $FILE_NAME/0x10, '0' + $FILE_NAME%0x10, '\0' };
+
+//
+// This is the string for the attribute code for index allocation.
+// $INDEX_ALLOCATION.
+//
+
+UNICODE_STRING NtfsIndexAllocation =
+ CONSTANT_UNICODE_STRING( L"$INDEX_ALLOCATION" );
+
+//
+// This is the string for the data attribute, $DATA.
+//
+
+UNICODE_STRING NtfsDataString =
+ CONSTANT_UNICODE_STRING( L"$DATA" );
+
+//
+// This strings are used for informational popups.
+//
+
+UNICODE_STRING NtfsSystemFiles[] = {
+
+ CONSTANT_UNICODE_STRING( L"\\$Mft" ),
+ CONSTANT_UNICODE_STRING( L"\\$MftMirr" ),
+ CONSTANT_UNICODE_STRING( L"\\$LogFile" ),
+ CONSTANT_UNICODE_STRING( L"\\$Volume" ),
+ CONSTANT_UNICODE_STRING( L"\\$AttrDef" ),
+ CONSTANT_UNICODE_STRING( L"\\" ),
+ CONSTANT_UNICODE_STRING( L"\\$BitMap" ),
+ CONSTANT_UNICODE_STRING( L"\\$Boot" ),
+ CONSTANT_UNICODE_STRING( L"\\$BadClus" ),
+ CONSTANT_UNICODE_STRING( L"\\$Quota" ),
+ CONSTANT_UNICODE_STRING( L"\\$UpCase" ),
+};
+
+UNICODE_STRING NtfsUnknownFile =
+ CONSTANT_UNICODE_STRING( L"\\????" );
+
+UNICODE_STRING NtfsRootIndexString =
+ CONSTANT_UNICODE_STRING( L"." );
+
+//
+// This is the empty string. This can be used to pass a string with
+// no length.
+//
+
+UNICODE_STRING NtfsEmptyString = { 0, 0, NULL };
+
+//
+// The following file references are used to identify system files.
+//
+
+FILE_REFERENCE MftFileReference = { MASTER_FILE_TABLE_NUMBER, 0, MASTER_FILE_TABLE_NUMBER };
+FILE_REFERENCE Mft2FileReference = { MASTER_FILE_TABLE2_NUMBER, 0, MASTER_FILE_TABLE2_NUMBER };
+FILE_REFERENCE LogFileReference = { LOG_FILE_NUMBER, 0, LOG_FILE_NUMBER };
+FILE_REFERENCE VolumeFileReference = { VOLUME_DASD_NUMBER, 0, VOLUME_DASD_NUMBER };
+FILE_REFERENCE RootIndexFileReference = { ROOT_FILE_NAME_INDEX_NUMBER, 0, ROOT_FILE_NAME_INDEX_NUMBER };
+FILE_REFERENCE BitmapFileReference = { BIT_MAP_FILE_NUMBER, 0, BIT_MAP_FILE_NUMBER };
+FILE_REFERENCE FirstUserFileReference = { FIRST_USER_FILE_NUMBER, 0, 0 };
+FILE_REFERENCE BootFileReference = { BOOT_FILE_NUMBER, 0, BOOT_FILE_NUMBER };
+
+//
+// The following are used to determine what level of protection to attach
+// to system files and attributes.
+//
+
+BOOLEAN NtfsProtectSystemFiles = TRUE;
+BOOLEAN NtfsProtectSystemAttributes = TRUE;
+
+//
+// FsRtl fast I/O call backs
+//
+
+FAST_IO_DISPATCH NtfsFastIoDispatch;
+
+#ifdef NTFSDBG
+
+LONG NtfsDebugTraceLevel = DEBUG_TRACE_ERROR;
+LONG NtfsDebugTraceIndent = 0;
+LONG NtfsFailCheck = 0;
+
+ULONG NtfsFsdEntryCount = 0;
+ULONG NtfsFspEntryCount = 0;
+ULONG NtfsIoCallDriverCount = 0;
+
+#endif // NTFSDBG
+
+//
+// Performance statistics
+//
+
+ULONG NtfsMaxDelayedCloseCount;
+ULONG NtfsMinDelayedCloseCount;
+
+ULONG NtfsCleanCheckpoints = 0;
+ULONG NtfsPostRequests = 0;
+
+UCHAR BaadSignature[4] = {'B', 'A', 'A', 'D'};
+UCHAR IndexSignature[4] = {'I', 'N', 'D', 'X'};
+UCHAR FileSignature[4] = {'F', 'I', 'L', 'E'};
+UCHAR HoleSignature[4] = {'H', 'O', 'L', 'E'};
+UCHAR ChkdskSignature[4] = {'C', 'H', 'K', 'D'};
+
+//
+// Large Reserved Buffer Context
+//
+
+ULONG NtfsReservedInUse = 0;
+PVOID NtfsReserved1 = NULL;
+PVOID NtfsReserved2 = NULL;
+ULONG NtfsReserved2Count = 0;
+PVOID NtfsReserved3 = NULL;
+PVOID NtfsReserved1Thread = NULL;
+PVOID NtfsReserved2Thread = NULL;
+PVOID NtfsReserved3Thread = NULL;
+PFCB NtfsReserved12Fcb = NULL;
+PFCB NtfsReserved3Fcb = NULL;
+PVOID NtfsReservedBufferThread = NULL;
+BOOLEAN NtfsBufferAllocationFailure = FALSE;
+FAST_MUTEX NtfsReservedBufferMutex;
+ERESOURCE NtfsReservedBufferResource;
+LARGE_INTEGER NtfsShortDelay = {(ULONG)-100000, -1}; // 10 milliseconds
+
+#ifdef _CAIRO_
+FAST_MUTEX NtfsScavengerLock;
+PIRP_CONTEXT NtfsScavengerWorkList;
+BOOLEAN NtfsScavengerRunning;
+#endif // _CAIRO_
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, NtfsFastIoCheckIfPossible)
+#pragma alloc_text(PAGE, NtfsFastQueryBasicInfo)
+#pragma alloc_text(PAGE, NtfsFastQueryStdInfo)
+#pragma alloc_text(PAGE, NtfsFastQueryNetworkOpenInfo)
+#ifdef _CAIRO_
+#pragma alloc_text(PAGE, NtfsFastIoQueryCompressionInfo)
+#pragma alloc_text(PAGE, NtfsFastIoQueryCompressedSize)
+#endif _CAIRO_
+#endif
+
+//
+// Internal support routines
+//
+
+LONG
+NtfsProcessExceptionFilter (
+ IN PEXCEPTION_POINTERS ExceptionPointer
+ )
+{
+ UNREFERENCED_PARAMETER( ExceptionPointer );
+
+ ASSERT( NT_SUCCESS( ExceptionPointer->ExceptionRecord->ExceptionCode ));
+
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+ULONG
+NtfsRaiseStatusFunction (
+ IN PIRP_CONTEXT IrpContext,
+ IN NTSTATUS Status
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is only required by the NtfsDecodeFileObject macro. It is
+ a function wrapper around NtfsRaiseStatus.
+
+Arguments:
+
+ Status - Status to raise
+
+Return Value:
+
+ 0 - but no one will see it!
+
+--*/
+
+{
+ NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
+ return 0;
+}
+
+
+
+VOID
+NtfsRaiseStatus (
+ IN PIRP_CONTEXT IrpContext,
+ IN NTSTATUS Status,
+ IN PFILE_REFERENCE FileReference OPTIONAL,
+ IN PFCB Fcb OPTIONAL
+ )
+
+{
+ //
+ // If the caller is declaring corruption, then let's mark the
+ // the volume corrupt appropriately, and maybe generate a popup.
+ //
+
+ if (Status == STATUS_DISK_CORRUPT_ERROR) {
+
+ NtfsPostVcbIsCorrupt( IrpContext, Status, FileReference, Fcb );
+
+ } else if ((Status == STATUS_FILE_CORRUPT_ERROR) ||
+ (Status == STATUS_EA_CORRUPT_ERROR)) {
+
+ NtfsPostVcbIsCorrupt( IrpContext, Status, FileReference, Fcb );
+ }
+
+ //
+ // Set a flag to indicate that we raised this status code and store
+ // it in the IrpContext.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_RAISED_STATUS );
+
+ if (NT_SUCCESS( IrpContext->ExceptionStatus )) {
+
+ //
+ // If this is a paging io request and we got a Quota Exceeded error
+ // then translate the status to FILE_LOCK_CONFLICT so that this
+ // is a retryable condition.
+ //
+
+ if ((Status == STATUS_QUOTA_EXCEEDED) &&
+ (IrpContext->OriginatingIrp != NULL) &&
+ (FlagOn( IrpContext->OriginatingIrp->Flags, IRP_PAGING_IO ))) {
+
+ Status = STATUS_FILE_LOCK_CONFLICT;
+ }
+
+ IrpContext->ExceptionStatus = Status;
+ }
+
+ //
+ // Now finally raise the status, and make sure we do not come back.
+ //
+
+ ExRaiseStatus( IrpContext->ExceptionStatus );
+}
+
+
+LONG
+NtfsExceptionFilter (
+ IN PIRP_CONTEXT IrpContext OPTIONAL,
+ IN PEXCEPTION_POINTERS ExceptionPointer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to decide if we should or should not handle
+ an exception status that is being raised. It inserts the status
+ into the IrpContext and either indicates that we should handle
+ the exception or bug check the system.
+
+Arguments:
+
+ ExceptionPointer - Supplies the exception record to being checked.
+
+Return Value:
+
+ ULONG - returns EXCEPTION_EXECUTE_HANDLER or bugchecks
+
+--*/
+
+{
+ NTSTATUS ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionCode;
+
+ ASSERT_OPTIONAL_IRP_CONTEXT( IrpContext );
+
+ DebugTrace( 0, DEBUG_TRACE_UNWIND, ("NtfsExceptionFilter %X\n", ExceptionCode) );
+
+ //
+ // If the exception is an in page error, then get the real I/O error code
+ // from the exception record
+ //
+
+ if ((ExceptionCode == STATUS_IN_PAGE_ERROR) &&
+ (ExceptionPointer->ExceptionRecord->NumberParameters >= 3)) {
+
+ ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionInformation[2];
+
+ //
+ // If we got FILE_LOCK_CONFLICT from a paging request then change it
+ // to STATUS_CANT_WAIT. This means that we couldn't wait for a
+ // reserved buffer or some other retryable condition. In the write
+ // case the correct error is already in the IrpContext. The read
+ // case doesn't pass the error back via the top-level irp context
+ // however.
+ //
+
+ if (ExceptionCode == STATUS_FILE_LOCK_CONFLICT) {
+
+ ExceptionCode = STATUS_CANT_WAIT;
+ }
+ }
+
+ //
+ // If there is not an irp context, we must have had insufficient resources
+ //
+
+ if (!ARGUMENT_PRESENT(IrpContext)) {
+
+ //
+ // Check whether this is a fatal error and bug check if so.
+ // Typically the only error is insufficient resources but
+ // it is possible that pool has been corrupted.
+ //
+
+ if (!FsRtlIsNtstatusExpected( ExceptionCode )) {
+
+ NtfsBugCheck( (ULONG)ExceptionPointer->ExceptionRecord,
+ (ULONG)ExceptionPointer->ContextRecord,
+ (ULONG)ExceptionPointer->ExceptionRecord->ExceptionAddress );
+ }
+
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+
+#ifdef NTFS_RESTART
+ ASSERT( (ExceptionCode != STATUS_FILE_CORRUPT_ERROR) &&
+ (ExceptionCode != STATUS_DISK_CORRUPT_ERROR) );
+#endif
+
+ //
+ // When processing any exceptions we always can wait. Remember the
+ // current state of the wait flag so we can restore while processing
+ // the exception.
+ //
+
+ if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
+
+ SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST );
+ }
+
+ SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
+
+ //
+ // If someone got STATUS_LOG_FILE_FULL or STATUS_CANT_WAIT, let's
+ // handle that. Note any other error that also happens will
+ // probably not go away and will just reoccur. If it does go
+ // away, that's ok too.
+ //
+
+ if (IrpContext->TopLevelIrpContext == IrpContext) {
+
+ if ((IrpContext->ExceptionStatus == STATUS_LOG_FILE_FULL) ||
+ (IrpContext->ExceptionStatus == STATUS_CANT_WAIT)) {
+
+ ExceptionCode = IrpContext->ExceptionStatus;
+ }
+ }
+
+ //
+ // If we didn't raise this status code then we need to check if
+ // we should handle this exception.
+ //
+
+ if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_RAISED_STATUS )) {
+
+ if (FsRtlIsNtstatusExpected( ExceptionCode )) {
+
+ //
+ // If we got an allocation failure doing paging Io then convert
+ // this to FILE_LOCK_CONFLICT.
+ //
+
+ if ((ExceptionCode == STATUS_QUOTA_EXCEEDED) &&
+ (IrpContext->OriginatingIrp != NULL) &&
+ (FlagOn( IrpContext->OriginatingIrp->Flags, IRP_PAGING_IO ))) {
+
+ ExceptionCode = STATUS_FILE_LOCK_CONFLICT;
+ }
+
+ IrpContext->ExceptionStatus = ExceptionCode;
+
+ } else {
+
+ NtfsBugCheck( (ULONG)ExceptionPointer->ExceptionRecord,
+ (ULONG)ExceptionPointer->ContextRecord,
+ (ULONG)ExceptionPointer->ExceptionRecord->ExceptionAddress );
+ }
+
+ } else {
+
+ //
+ // We raised this code explicitly ourselves, so it had better be
+ // expected.
+ //
+
+ ASSERT( ExceptionCode == IrpContext->ExceptionStatus );
+ ASSERT( FsRtlIsNtstatusExpected( ExceptionCode ) );
+ }
+
+ ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_RAISED_STATUS );
+
+ //
+ // If the exception code is log file full, then remember the current
+ // RestartAreaLsn in the Vcb, so we can see if we are the ones to flush
+ // the log file later. Note, this does not have to be synchronized,
+ // because we are just using it to arbitrate who must do the flush, but
+ // eventually someone will anyway.
+ //
+
+ if (ExceptionCode == STATUS_LOG_FILE_FULL) {
+
+ IrpContext->TopLevelIrpContext->LastRestartArea = IrpContext->Vcb->LastRestartArea;
+ }
+
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+
+NTSTATUS
+NtfsProcessException (
+ IN PIRP_CONTEXT IrpContext,
+ IN PIRP Irp OPTIONAL,
+ IN NTSTATUS ExceptionCode
+ )
+
+/*++
+
+Routine Description:
+
+ This routine process an exception. It either completes the request
+ with the saved exception status or it sends the request off to the Fsp
+
+Arguments:
+
+ Irp - Supplies the Irp being processed
+
+ ExceptionCode - Supplies the normalized exception status being handled
+
+Return Value:
+
+ NTSTATUS - Returns the results of either posting the Irp or the
+ saved completion status.
+
+--*/
+
+{
+ BOOLEAN TopLevelRequest;
+ PIRP_CONTEXT PostIrpContext = NULL;
+ BOOLEAN Retry = FALSE;
+
+ BOOLEAN ReleaseBitmap = FALSE;
+
+ ASSERT_OPTIONAL_IRP_CONTEXT( IrpContext );
+ ASSERT_OPTIONAL_IRP( Irp );
+
+ DebugTrace( 0, Dbg, ("NtfsProcessException\n") );
+
+ //
+ // If there is not an irp context, we must have had insufficient resources
+ //
+
+ if (IrpContext == NULL) {
+
+ if (ARGUMENT_PRESENT( Irp )) {
+
+ NtfsCompleteRequest( NULL, &Irp, ExceptionCode );
+ }
+
+ return ExceptionCode;
+ }
+
+ //
+ // Get the real exception status from the Irp Context.
+ //
+
+ ExceptionCode = IrpContext->ExceptionStatus;
+
+ //
+ // All errors which could possibly have started a transaction must go
+ // through here. Abort the transaction.
+ //
+
+ //
+ // Increment the appropriate performance counters.
+ //
+
+ CollectExceptionStats( IrpContext->Vcb, ExceptionCode );
+
+ try {
+
+ //
+ // If this is an Mdl write request, then take care of the Mdl
+ // here so that things get cleaned up properly, and in the
+ // case of log file full we will just create a new Mdl. By
+ // getting rid of this Mdl now, the pages will not be locked
+ // if we try to truncate the file while restoring snapshots.
+ //
+
+ if ((IrpContext->MajorFunction == IRP_MJ_WRITE) &&
+ FlagOn(IrpContext->MinorFunction, IRP_MN_MDL) &&
+ (Irp->MdlAddress != NULL)) {
+
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ CcMdlWriteComplete( IrpSp->FileObject,
+ &IrpSp->Parameters.Write.ByteOffset,
+ Irp->MdlAddress );
+
+ Irp->MdlAddress = NULL;
+ }
+
+ //
+ // On a failed mount this value will be NULL. Don't perform the
+ // abort in that case or we will fail when looking at the Vcb
+ // in the Irp COntext.
+ //
+
+ if (IrpContext->Vcb != NULL) {
+
+ //
+ // To make sure that we can access all of our streams correctly,
+ // we first restore all of the higher sizes before aborting the
+ // transaction. Then we restore all of the lower sizes after
+ // the abort, so that all Scbs are finally restored.
+ //
+
+ NtfsRestoreScbSnapshots( IrpContext, TRUE );
+
+ //
+ // If we modified the volume bitmap during this transaction we
+ // want to acquire it and hold it throughout the abort process.
+ // Otherwise this abort could constantly be setting the rescan
+ // bitmap flag at the same time as some interleaved transaction
+ // is performing bitmap operations and we will thrash performing
+ // bitmap scans.
+ //
+
+ if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_MODIFIED_BITMAP ) &&
+ (IrpContext->TransactionId != 0)) {
+
+ //
+ // Acquire the resource and remember we need to release it.
+ //
+
+ ExAcquireResourceExclusive( IrpContext->Vcb->BitmapScb->Header.Resource,
+ TRUE );
+
+ //
+ // Restore the free cluster count in the Vcb.
+ //
+
+ IrpContext->Vcb->FreeClusters -= IrpContext->FreeClusterChange;
+
+ ReleaseBitmap = TRUE;
+ }
+
+ NtfsAbortTransaction( IrpContext, IrpContext->Vcb, NULL );
+
+ if (ReleaseBitmap) {
+
+ ExReleaseResource( IrpContext->Vcb->BitmapScb->Header.Resource );
+ ReleaseBitmap = FALSE;
+ }
+
+ NtfsRestoreScbSnapshots( IrpContext, FALSE );
+
+ NtfsAcquireCheckpoint( IrpContext, IrpContext->Vcb );
+ SetFlag( IrpContext->Vcb->MftDefragState, VCB_MFT_DEFRAG_ENABLED );
+ NtfsReleaseCheckpoint( IrpContext, IrpContext->Vcb );
+ }
+
+ //
+ // Exceptions at this point are pretty bad, we failed to undo everything.
+ //
+
+ } except(NtfsProcessExceptionFilter( GetExceptionInformation() )) {
+
+ PSCB_SNAPSHOT ScbSnapshot;
+ PSCB NextScb;
+
+ //
+ // If we get an exception doing this then things are in really bad
+ // shape but we still don't want to bugcheck the system so we
+ // need to protect ourselves
+ //
+
+ try {
+
+ NtfsPostVcbIsCorrupt( IrpContext, 0, NULL, NULL );
+
+ } except(NtfsProcessExceptionFilter( GetExceptionInformation() )) {
+
+ NOTHING;
+ }
+
+ if (ReleaseBitmap) {
+
+ //
+ // Since we had an unexpected failure and we know that
+ // we have modified the bitmap we need to do a complete
+ // scan to accurately know the free cluster count.
+ //
+
+ SetFlag( IrpContext->Vcb->VcbState, VCB_STATE_RELOAD_FREE_CLUSTERS );
+ ExReleaseResource( IrpContext->Vcb->BitmapScb->Header.Resource );
+ ReleaseBitmap = FALSE;
+ }
+
+ //
+ // We have taken all the steps possible to cleanup the current
+ // transaction and it has failed. Any of the Scb's involved in
+ // this transaction could now be out of ssync with the on-disk
+ // structures. We can't go to disk to restore this so we will
+ // clean up the in-memory structures as best we can so that the
+ // system won't crash.
+ //
+ // We will go through the Scb snapshot list and knock down the
+ // sizes to the lower of the two values. We will also truncate
+ // the Mcb to that allocation. If this is a normal data stream
+ // we will actually empty the Mcb.
+ //
+
+ 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 {
+
+ NextScb = ScbSnapshot->Scb;
+
+ if (NextScb == NULL) {
+
+ ScbSnapshot = (PSCB_SNAPSHOT)ScbSnapshot->SnapshotLinks.Flink;
+ continue;
+ }
+
+ //
+ // Go through each of the sizes and use the lower value.
+ //
+
+ if (ScbSnapshot->AllocationSize < NextScb->Header.AllocationSize.QuadPart) {
+
+ NextScb->Header.AllocationSize.QuadPart = ScbSnapshot->AllocationSize;
+ }
+
+ if (FlagOn(NextScb->Header.AllocationSize.LowPart, 1)) {
+
+ NextScb->Header.AllocationSize.LowPart -= 1;
+ SetFlag(NextScb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT);
+
+ } else {
+
+ ClearFlag(NextScb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT);
+ }
+
+ //
+ // Update the FastIoField.
+ //
+
+ NtfsAcquireFsrtlHeader( NextScb );
+ NextScb->Header.IsFastIoPossible = NtfsIsFastIoPossible( NextScb );
+ NtfsReleaseFsrtlHeader( NextScb );
+
+ if (ScbSnapshot->FileSize < NextScb->Header.FileSize.QuadPart) {
+
+ NextScb->Header.FileSize.QuadPart = ScbSnapshot->FileSize;
+ }
+
+ if (ScbSnapshot->ValidDataLength < NextScb->Header.ValidDataLength.QuadPart) {
+
+ NextScb->Header.ValidDataLength.QuadPart = ScbSnapshot->ValidDataLength;
+ }
+
+ //
+ // Truncate the Mcb to 0 for user data streams and to the
+ // allocation size for other streams.
+ //
+
+ if (NtfsIsTypeCodeUserData( NextScb->AttributeTypeCode ) &&
+ !FlagOn( NextScb->Fcb->FcbState, FCB_STATE_PAGING_FILE ) &&
+ (NtfsSegmentNumber( &NextScb->Fcb->FileReference ) >= FIRST_USER_FILE_NUMBER)) {
+
+ NtfsUnloadNtfsMcbRange( &NextScb->Mcb, (LONGLONG) 0, MAXLONGLONG, FALSE, FALSE );
+
+ } else {
+
+ NtfsUnloadNtfsMcbRange( &NextScb->Mcb,
+ Int64ShraMod32(NextScb->Header.AllocationSize.QuadPart, NextScb->Vcb->ClusterShift),
+ MAXLONGLONG,
+ FALSE,
+ FALSE );
+ }
+
+ ScbSnapshot = (PSCB_SNAPSHOT)ScbSnapshot->SnapshotLinks.Flink;
+
+ } while (ScbSnapshot != &IrpContext->ScbSnapshot);
+ }
+
+ //ASSERTMSG( "***Failed to abort transaction, volume is corrupt", FALSE );
+
+ //
+ // Clear the transaction Id in the IrpContext to make sure we don't
+ // try to write any log records in the complete request.
+ //
+
+ IrpContext->TransactionId = 0;
+ }
+
+ //
+ // If this isn't the top-level request then make sure to pass the real
+ // error back to the top level.
+ //
+
+ if (IrpContext != IrpContext->TopLevelIrpContext) {
+
+ //
+ // Make sure this error is returned to the top level guy.
+ // If the status is FILE_LOCK_CONFLICT then we are using this
+ // value to stop some lower level request. Convert it to
+ // STATUS_CANT_WAIT so the top-level request will retry.
+ //
+
+ if (NT_SUCCESS( IrpContext->TopLevelIrpContext->ExceptionStatus )) {
+
+ if (ExceptionCode == STATUS_FILE_LOCK_CONFLICT) {
+
+ IrpContext->TopLevelIrpContext->ExceptionStatus = STATUS_CANT_WAIT;
+
+ } else {
+
+ IrpContext->TopLevelIrpContext->ExceptionStatus = ExceptionCode;
+ }
+ }
+ }
+
+ //
+ // If the status is cant wait then send the request off to the fsp.
+ //
+
+ TopLevelRequest = NtfsIsTopLevelRequest( IrpContext );
+
+ //
+ // We want to look at the LOG_FILE_FULL or CANT_WAIT cases and consider
+ // if we want to post the request. We only post requests at the top
+ // level.
+ //
+
+ if (ExceptionCode == STATUS_LOG_FILE_FULL ||
+ ExceptionCode == STATUS_CANT_WAIT) {
+
+ if (ARGUMENT_PRESENT( Irp )) {
+
+ //
+ // If we are top level, we will either post it or retry.
+ //
+
+ if (TopLevelRequest) {
+
+ //
+ // See if we are supposed to post the request.
+ //
+
+ if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST )) {
+
+ PostIrpContext = IrpContext;
+
+ //
+ // Otherwise we will retry this request in the original thread.
+ //
+
+ } else {
+
+ Retry = TRUE;
+ }
+
+ //
+ // Otherwise we will complete the request, see if there is any
+ // related processing to do.
+ //
+
+ } else {
+
+ //
+ // We are the top level Ntfs call. If we are processing a
+ // LOG_FILE_FULL condition then there may be no one above us
+ // who can do the checkpoint. Go ahead and fire off a dummy
+ // request. Do an unsafe test on the flag since it won't hurt
+ // to generate an occasional additional request.
+ //
+
+ if ((ExceptionCode == STATUS_LOG_FILE_FULL) &&
+ (IrpContext->TopLevelIrpContext == IrpContext) &&
+ !FlagOn( IrpContext->Vcb->CheckpointFlags, VCB_DUMMY_CHECKPOINT_POSTED )) {
+
+ //
+ // Create a dummy IrpContext but protect this request with
+ // a try-except to catch any allocation failures.
+ //
+
+ try {
+
+ PostIrpContext = NtfsCreateIrpContext( NULL, TRUE );
+ PostIrpContext->Vcb = IrpContext->Vcb;
+ PostIrpContext->LastRestartArea = PostIrpContext->Vcb->LastRestartArea;
+
+ NtfsAcquireCheckpoint( IrpContext, IrpContext->Vcb );
+ SetFlag( IrpContext->Vcb->CheckpointFlags, VCB_DUMMY_CHECKPOINT_POSTED );
+ NtfsReleaseCheckpoint( IrpContext, IrpContext->Vcb );
+
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+
+ NOTHING;
+ }
+ }
+
+ //
+ // If this is a paging write and we are not the top level
+ // request then we need to return STATUS_FILE_LOCk_CONFLICT
+ // to make MM happy (and keep the pages dirty) and to
+ // prevent this request from retrying the request.
+ //
+
+ ExceptionCode = STATUS_FILE_LOCK_CONFLICT;
+ }
+ }
+ }
+
+ if (PostIrpContext) {
+
+ NTSTATUS PostStatus;
+
+ //
+ // Clear the current error code.
+ //
+
+ PostIrpContext->ExceptionStatus = 0;
+
+ //
+ // We need a try-except in case the Lock buffer call fails.
+ //
+
+ try {
+
+ PostStatus = NtfsPostRequest( PostIrpContext, PostIrpContext->OriginatingIrp );
+
+ //
+ // If we posted the original request we don't have any
+ // completion work to do.
+ //
+
+ if (PostIrpContext == IrpContext) {
+
+ Irp = NULL;
+ IrpContext = NULL;
+ ExceptionCode = PostStatus;
+ }
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ //
+ // If we don't have an error in the IrpContext then
+ // generate a generic IO error. We can't use the
+ // original status code if either LOG_FILE_FULL or
+ // CANT_WAIT. We would complete the Irp yet retry the
+ // request.
+ //
+
+ if (IrpContext == PostIrpContext) {
+
+ if (PostIrpContext->ExceptionStatus == 0) {
+
+ if ((ExceptionCode == STATUS_LOG_FILE_FULL) ||
+ (ExceptionCode == STATUS_CANT_WAIT)) {
+
+ ExceptionCode = STATUS_UNEXPECTED_IO_ERROR;
+ }
+
+ } else {
+
+ ExceptionCode = PostIrpContext->ExceptionStatus;
+ }
+ }
+ }
+ }
+
+ //
+ // We have the Irp. We either need to complete this request or allow
+ // the top level thread to retry.
+ //
+
+ if (ARGUMENT_PRESENT(Irp)) {
+
+ //
+ // If this is a top level Ntfs request and we still have the Irp
+ // it means we will be retrying the request. In that case
+ // mark the Irp Context so it doesn't go away.
+ //
+
+ if (Retry) {
+
+ SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DONT_DELETE );
+ NtfsCompleteRequest( &IrpContext, NULL, ExceptionCode );
+
+ //
+ // Clear the status code in the Irp Context.
+ //
+
+ IrpContext->ExceptionStatus = 0;
+
+ } else {
+
+ NtfsCompleteRequest( &IrpContext, &Irp, ExceptionCode );
+ }
+
+ } else if (IrpContext != NULL) {
+
+ NtfsCompleteRequest( &IrpContext, NULL, ExceptionCode );
+ }
+
+ return ExceptionCode;
+}
+
+
+VOID
+NtfsCompleteRequest (
+ IN OUT PIRP_CONTEXT *IrpContext OPTIONAL,
+ IN OUT PIRP *Irp OPTIONAL,
+ IN NTSTATUS Status
+ )
+
+/*++
+
+Routine Description:
+
+ This routine completes an IRP and deallocates the IrpContext
+
+Arguments:
+
+ Irp - Supplies the Irp being processed
+
+ Status - Supplies the status to complete the Irp with
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // If we have an Irp Context then unpin all of the repinned bcbs
+ // we might have collected, and delete the Irp context. Delete Irp
+ // Context will zero out our pointer for us.
+ //
+
+ if (ARGUMENT_PRESENT(IrpContext)) {
+
+ ASSERT_IRP_CONTEXT( *IrpContext );
+
+ if ((*IrpContext)->TransactionId != 0) {
+ NtfsCommitCurrentTransaction( *IrpContext );
+ }
+
+ (*IrpContext)->ExceptionStatus = Status;
+
+ //
+ // Always store the status in the top level Irp Context unless
+ // there is already an error code.
+ //
+
+ if (NT_SUCCESS( (*IrpContext)->TopLevelIrpContext->ExceptionStatus )) {
+ (*IrpContext)->TopLevelIrpContext->ExceptionStatus = Status;
+ }
+
+ NtfsDeleteIrpContext( IrpContext );
+ }
+
+ //
+ // If we have an Irp then complete the irp.
+ //
+
+ if (ARGUMENT_PRESENT( Irp )) {
+
+ PIO_STACK_LOCATION IrpSp;
+
+ ASSERT_IRP( *Irp );
+
+ if (NT_ERROR( Status ) &&
+ FlagOn( (*Irp)->Flags, IRP_INPUT_OPERATION )) {
+
+ (*Irp)->IoStatus.Information = 0;
+ }
+
+ IrpSp = IoGetCurrentIrpStackLocation( *Irp );
+
+ (*Irp)->IoStatus.Status = Status;
+
+ IoCompleteRequest( *Irp, IO_DISK_INCREMENT );
+
+ //
+ // Zero out our input pointer
+ //
+
+ *Irp = NULL;
+ }
+
+ return;
+}
+
+
+BOOLEAN
+NtfsFastIoCheckIfPossible (
+ IN PFILE_OBJECT FileObject,
+ IN PLARGE_INTEGER FileOffset,
+ IN ULONG Length,
+ IN BOOLEAN Wait,
+ IN ULONG LockKey,
+ IN BOOLEAN CheckForReadOperation,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ )
+
+/*++
+
+Routine Description:
+
+ This routine checks if fast i/o is possible for a read/write operation
+
+Arguments:
+
+ FileObject - Supplies the file object used in the query
+
+ FileOffset - Supplies the starting byte offset for the read/write operation
+
+ Length - Supplies the length, in bytes, of the read/write operation
+
+ Wait - Indicates if we can wait
+
+ LockKey - Supplies the lock key
+
+ CheckForReadOperation - Indicates if this is a check for a read or write
+ operation
+
+ IoStatus - Receives the status of the operation if our return value is
+ FastIoReturnError
+
+Return Value:
+
+ BOOLEAN - TRUE if fast I/O is possible and FALSE if the caller needs
+ to take the long route
+
+--*/
+
+{
+ PSCB Scb;
+
+ LARGE_INTEGER LargeLength;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+ UNREFERENCED_PARAMETER( IoStatus );
+ UNREFERENCED_PARAMETER( Wait );
+
+ PAGED_CODE();
+
+ //
+ // Decode the file object to get our fcb, the only one we want
+ // to deal with is a UserFileOpen
+ //
+
+ if ((Scb = NtfsFastDecodeUserFileOpen( FileObject )) == NULL) {
+
+ return FALSE;
+ }
+
+ LargeLength = RtlConvertUlongToLargeInteger( Length );
+
+ //
+ // Based on whether this is a read or write operation we call
+ // fsrtl check for read/write
+ //
+
+ if (CheckForReadOperation) {
+
+ if (Scb->ScbType.Data.FileLock == NULL
+ || FsRtlFastCheckLockForRead( Scb->ScbType.Data.FileLock,
+ FileOffset,
+ &LargeLength,
+ LockKey,
+ FileObject,
+ PsGetCurrentProcess() )) {
+
+ return TRUE;
+ }
+
+ } else {
+
+ if ((Scb->ScbType.Data.FileLock == NULL
+ || FsRtlFastCheckLockForWrite( Scb->ScbType.Data.FileLock,
+ FileOffset,
+ &LargeLength,
+ LockKey,
+ FileObject,
+ PsGetCurrentProcess() ))
+
+ &&
+
+ (!FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK ) ||
+ NtfsReserveClusters( NULL, Scb, FileOffset->QuadPart, Length))) {
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+BOOLEAN
+NtfsFastQueryBasicInfo (
+ IN PFILE_OBJECT FileObject,
+ IN BOOLEAN Wait,
+ IN OUT PFILE_BASIC_INFORMATION Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is for the fast query call for basic file information.
+
+Arguments:
+
+ FileObject - Supplies the file object used in this operation
+
+ Wait - Indicates if we are allowed to wait for the information
+
+ Buffer - Supplies the output buffer to receive the basic information
+
+ IoStatus - Receives the final status of the operation
+
+Return Value:
+
+ BOOLEAN _ TRUE if the operation is successful and FALSE if the caller
+ needs to take the long route.
+
+--*/
+
+{
+ BOOLEAN Results = FALSE;
+ IRP_CONTEXT IrpContext;
+
+ TYPE_OF_OPEN TypeOfOpen;
+ PVCB Vcb;
+ PFCB Fcb;
+ PSCB Scb;
+ PCCB Ccb;
+
+ BOOLEAN FcbAcquired = FALSE;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+
+ PAGED_CODE();
+
+ //
+ // Prepare the dummy irp context
+ //
+
+ RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );
+ IrpContext.NodeTypeCode = NTFS_NTC_IRP_CONTEXT;
+ IrpContext.NodeByteSize = sizeof(IRP_CONTEXT);
+ if (Wait) {
+ SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT);
+ } else {
+ ClearFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT);
+ }
+
+ //
+ // Determine the type of open for the input file object. The callee really
+ // ignores the irp context for us.
+ //
+
+ TypeOfOpen = NtfsDecodeFileObject( &IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, FALSE );
+
+ FsRtlEnterFileSystem();
+
+ try {
+
+ if (ExAcquireResourceShared( Fcb->Resource, Wait )) {
+
+ FcbAcquired = TRUE;
+
+ if (FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED ) ||
+ !FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
+
+ try_return( NOTHING );
+ }
+
+ } else {
+
+ try_return( NOTHING );
+ }
+
+ switch (TypeOfOpen) {
+
+ case UserFileOpen:
+#ifdef _CAIRO_
+ case UserPropertySetOpen:
+#endif // _CAIRO_
+ case UserDirectoryOpen:
+ case StreamFileOpen:
+
+ //
+ // Fill in the basic information fields
+ //
+
+ Buffer->CreationTime.QuadPart = Fcb->Info.CreationTime;
+ Buffer->LastWriteTime.QuadPart = Fcb->Info.LastModificationTime;
+ Buffer->ChangeTime.QuadPart = Fcb->Info.LastChangeTime;
+
+ Buffer->LastAccessTime.QuadPart = Fcb->CurrentLastAccess;
+
+ Buffer->FileAttributes = Fcb->Info.FileAttributes;
+
+ ClearFlag( Buffer->FileAttributes,
+ ~FILE_ATTRIBUTE_VALID_FLAGS
+ | FILE_ATTRIBUTE_TEMPORARY );
+
+ if (IsDirectory( &Fcb->Info )) {
+
+ SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_DIRECTORY );
+ }
+
+ //
+ // If this is not the main stream on the file then use the stream based
+ // compressed bit.
+ //
+
+ if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE )) {
+
+ if (Scb->CompressionUnit != 0) {
+
+ SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
+
+ } else {
+
+ ClearFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
+ }
+ }
+
+ //
+ // Set the temporary flag if set in the Scb.
+ //
+
+ if (FlagOn( Scb->ScbState, SCB_STATE_TEMPORARY )) {
+
+ SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
+ }
+
+ //
+ // If there are no flags set then explicitly set the NORMAL flag.
+ //
+
+ if (Buffer->FileAttributes == 0) {
+
+ Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
+ }
+
+ Results = TRUE;
+
+ IoStatus->Information = sizeof(FILE_BASIC_INFORMATION);
+
+ IoStatus->Status = STATUS_SUCCESS;
+
+ break;
+
+ default:
+
+ NOTHING;
+ }
+
+ try_exit: NOTHING;
+ } finally {
+
+ if (FcbAcquired) { ExReleaseResource( Fcb->Resource ); }
+
+ FsRtlExitFileSystem();
+ }
+
+ //
+ // Return to our caller
+ //
+
+ return Results;
+}
+
+
+BOOLEAN
+NtfsFastQueryStdInfo (
+ IN PFILE_OBJECT FileObject,
+ IN BOOLEAN Wait,
+ IN OUT PFILE_STANDARD_INFORMATION Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is for the fast query call for standard file information.
+
+Arguments:
+
+ FileObject - Supplies the file object used in this operation
+
+ Wait - Indicates if we are allowed to wait for the information
+
+ Buffer - Supplies the output buffer to receive the basic information
+
+ IoStatus - Receives the final status of the operation
+
+Return Value:
+
+ BOOLEAN _ TRUE if the operation is successful and FALSE if the caller
+ needs to take the long route.
+
+--*/
+
+{
+ BOOLEAN Results = FALSE;
+ IRP_CONTEXT IrpContext;
+
+ TYPE_OF_OPEN TypeOfOpen;
+ PVCB Vcb;
+ PFCB Fcb;
+ PSCB Scb;
+ PCCB Ccb;
+
+ BOOLEAN FcbAcquired = FALSE;
+ BOOLEAN FsRtlHeaderLocked = FALSE;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+
+ PAGED_CODE();
+
+ //
+ // Prepare the dummy irp context
+ //
+
+ RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );
+ IrpContext.NodeTypeCode = NTFS_NTC_IRP_CONTEXT;
+ IrpContext.NodeByteSize = sizeof(IRP_CONTEXT);
+ if (Wait) {
+ SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT);
+ } else {
+ ClearFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT);
+ }
+
+ //
+ // Determine the type of open for the input file object. The callee really
+ // ignores the irp context for us.
+ //
+
+ TypeOfOpen = NtfsDecodeFileObject( &IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, FALSE );
+
+ FsRtlEnterFileSystem();
+
+ try {
+
+ switch (TypeOfOpen) {
+
+ case UserFileOpen:
+#ifdef _CAIRO_
+ case UserPropertySetOpen:
+#endif // _CAIRO_
+ case UserDirectoryOpen:
+ case StreamFileOpen:
+
+ if (Scb->Header.PagingIoResource != NULL) {
+ ExAcquireResourceShared( Scb->Header.PagingIoResource, TRUE );
+ }
+
+ FsRtlLockFsRtlHeader( &Scb->Header );
+ FsRtlHeaderLocked = TRUE;
+
+ if (ExAcquireResourceShared( Fcb->Resource, Wait )) {
+
+ FcbAcquired = TRUE;
+
+ if (FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED ) ||
+ !FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
+
+ try_return( NOTHING );
+ }
+
+ } else {
+
+ try_return( NOTHING );
+ }
+
+ //
+ // Fill in the standard information fields. If the
+ // Scb is not initialized then take the long route
+ //
+
+ if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED) &&
+ (Scb->AttributeTypeCode != $INDEX_ALLOCATION)) {
+
+ NOTHING;
+
+ } else {
+
+ Buffer->AllocationSize.QuadPart = Scb->TotalAllocated;
+ Buffer->EndOfFile = Scb->Header.FileSize;
+ Buffer->NumberOfLinks = Fcb->LinkCount;
+
+ if (Ccb != NULL) {
+
+ if (FlagOn(Ccb->Flags, CCB_FLAG_OPEN_AS_FILE)) {
+
+ if (Scb->Fcb->LinkCount == 0 ||
+ (Ccb->Lcb != NULL &&
+ FlagOn( Ccb->Lcb->LcbState, LCB_STATE_DELETE_ON_CLOSE ))) {
+
+ Buffer->DeletePending = TRUE;
+ }
+
+ } else {
+
+ Buffer->DeletePending = BooleanFlagOn( Scb->ScbState, SCB_STATE_DELETE_ON_CLOSE );
+ }
+ }
+
+ Buffer->Directory = BooleanIsDirectory( &Fcb->Info );
+
+ IoStatus->Information = sizeof(FILE_STANDARD_INFORMATION);
+
+ IoStatus->Status = STATUS_SUCCESS;
+
+ Results = TRUE;
+ }
+
+ break;
+
+ default:
+
+ NOTHING;
+ }
+
+ try_exit: NOTHING;
+ } finally {
+
+ if (FcbAcquired) { ExReleaseResource( Fcb->Resource ); }
+
+ if (FsRtlHeaderLocked) {
+ FsRtlUnlockFsRtlHeader( &Scb->Header );
+ if (Scb->Header.PagingIoResource != NULL) {
+ ExReleaseResource( Scb->Header.PagingIoResource );
+ }
+ }
+
+ FsRtlExitFileSystem();
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return Results;
+}
+
+
+BOOLEAN
+NtfsFastQueryNetworkOpenInfo (
+ IN PFILE_OBJECT FileObject,
+ IN BOOLEAN Wait,
+ OUT PFILE_NETWORK_OPEN_INFORMATION Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is for the fast query network open call.
+
+Arguments:
+
+ FileObject - Supplies the file object used in this operation
+
+ Wait - Indicates if we are allowed to wait for the information
+
+ Buffer - Supplies the output buffer to receive the information
+
+ IoStatus - Receives the final status of the operation
+
+Return Value:
+
+ BOOLEAN _ TRUE if the operation is successful and FALSE if the caller
+ needs to take the long route.
+
+--*/
+
+{
+ BOOLEAN Results = FALSE;
+ IRP_CONTEXT IrpContext;
+
+ TYPE_OF_OPEN TypeOfOpen;
+ PVCB Vcb;
+ PFCB Fcb;
+ PSCB Scb;
+ PCCB Ccb;
+
+ BOOLEAN FcbAcquired = FALSE;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+
+ PAGED_CODE();
+
+ //
+ // Prepare the dummy irp context
+ //
+
+ RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );
+ IrpContext.NodeTypeCode = NTFS_NTC_IRP_CONTEXT;
+ IrpContext.NodeByteSize = sizeof(IRP_CONTEXT);
+ if (Wait) {
+ SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT);
+ } else {
+ ClearFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT);
+ }
+
+ //
+ // Determine the type of open for the input file object. The callee really
+ // ignores the irp context for us.
+ //
+
+ TypeOfOpen = NtfsDecodeFileObject( &IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, FALSE );
+
+ FsRtlEnterFileSystem();
+
+ try {
+
+ if (ExAcquireResourceShared( Fcb->Resource, Wait )) {
+
+ FcbAcquired = TRUE;
+
+ if (FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED ) ||
+ !FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED ) ||
+ (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED) &&
+ (Scb->AttributeTypeCode != $INDEX_ALLOCATION))) {
+
+ try_return( NOTHING );
+ }
+
+ } else {
+
+ try_return( NOTHING );
+ }
+
+ switch (TypeOfOpen) {
+
+ case UserFileOpen:
+#ifdef _CAIRO_
+ case UserPropertySetOpen:
+#endif // _CAIRO_
+ case UserDirectoryOpen:
+ case StreamFileOpen:
+
+ //
+ // Fill in the basic information fields
+ //
+
+ Buffer->CreationTime.QuadPart = Fcb->Info.CreationTime;
+ Buffer->LastWriteTime.QuadPart = Fcb->Info.LastModificationTime;
+ Buffer->ChangeTime.QuadPart = Fcb->Info.LastChangeTime;
+
+ Buffer->LastAccessTime.QuadPart = Fcb->CurrentLastAccess;
+
+ Buffer->FileAttributes = Fcb->Info.FileAttributes;
+
+ ClearFlag( Buffer->FileAttributes,
+ ~FILE_ATTRIBUTE_VALID_FLAGS | FILE_ATTRIBUTE_TEMPORARY );
+
+ if (Scb->AttributeTypeCode == $INDEX_ALLOCATION) {
+
+ if (IsDirectory( &Fcb->Info )) {
+
+ SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_DIRECTORY );
+
+ //
+ // If this is not the main stream then copy the compression
+ // value from this Scb.
+ //
+
+ } else if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
+
+ SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
+
+ } else {
+
+ ClearFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
+ }
+
+ Buffer->AllocationSize.QuadPart =
+ Buffer->EndOfFile.QuadPart = 0;
+
+ //
+ // Return a non-zero size only for data streams.
+ //
+
+ } else {
+
+ Buffer->AllocationSize.QuadPart = Scb->TotalAllocated;
+ Buffer->EndOfFile = Scb->Header.FileSize;
+
+ //
+ // If not the unnamed data stream then use the Scb
+ // compression value.
+ //
+
+ if (!FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
+
+ if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
+
+ SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
+
+ } else {
+
+ ClearFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
+ }
+ }
+ }
+
+ //
+ // Set the temporary flag if set in the Scb.
+ //
+
+ if (FlagOn( Scb->ScbState, SCB_STATE_TEMPORARY )) {
+
+ SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
+ }
+
+ //
+ // If there are no flags set then explicitly set the NORMAL flag.
+ //
+
+ if (Buffer->FileAttributes == 0) {
+
+ Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
+ }
+
+ IoStatus->Information = sizeof(FILE_NETWORK_OPEN_INFORMATION);
+
+ IoStatus->Status = STATUS_SUCCESS;
+
+ Results = TRUE;
+
+ break;
+
+ default:
+
+ NOTHING;
+ }
+
+ try_exit: NOTHING;
+ } finally {
+
+ if (FcbAcquired) { ExReleaseResource( Fcb->Resource ); }
+
+ FsRtlExitFileSystem();
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return Results;
+}
+
+
+#ifdef _CAIRO_
+VOID
+NtfsFastIoQueryCompressionInfo (
+ IN PFILE_OBJECT FileObject,
+ OUT PFILE_COMPRESSION_INFORMATION Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is a fast call for returning the comprssion information
+ for a file. It assumes that the caller has an exception handler and
+ will treat exceptions as an error. Therefore, this routine only uses
+ a finally clause to cleanup any resources, and it does not worry about
+ returning errors in the IoStatus.
+
+Arguments:
+
+ FileObject - FileObject for the file on which the compressed information
+ is desired.
+
+ Buffer - Buffer to receive the compressed data information (as defined
+ in ntioapi.h)
+
+ IoStatus - Returns STATUS_SUCCESS and the size of the information being
+ returned.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ IRP_CONTEXT IrpContext;
+
+ TYPE_OF_OPEN TypeOfOpen;
+ PVCB Vcb;
+ PFCB Fcb;
+ PSCB Scb;
+ PCCB Ccb;
+
+ BOOLEAN ScbAcquired = FALSE;
+
+ PAGED_CODE();
+
+ //
+ // Prepare the dummy irp context
+ //
+
+ RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );
+ IrpContext.NodeTypeCode = NTFS_NTC_IRP_CONTEXT;
+ IrpContext.NodeByteSize = sizeof(IRP_CONTEXT);
+ SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT);
+
+ //
+ // Assume success (otherwise caller should see the exception)
+ //
+
+ IoStatus->Status = STATUS_SUCCESS;
+ IoStatus->Information = sizeof(FILE_COMPRESSION_INFORMATION);
+
+ //
+ // Determine the type of open for the input file object. The callee really
+ // ignores the irp context for us.
+ //
+
+ TypeOfOpen = NtfsDecodeFileObject( &IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, FALSE);
+
+ FsRtlEnterFileSystem();
+
+ try {
+
+ NtfsAcquireSharedScb( &IrpContext, Scb );
+ ScbAcquired = TRUE;
+
+ //
+ // Now return the compressed data information.
+ //
+
+ Buffer->CompressedFileSize.QuadPart = Scb->TotalAllocated;
+ Buffer->CompressionFormat = (USHORT)(Scb->AttributeFlags & ATTRIBUTE_FLAG_COMPRESSION_MASK);
+ if (Buffer->CompressionFormat != 0) {
+ Buffer->CompressionFormat += 1;
+ }
+ Buffer->CompressionUnitShift = (UCHAR)(Scb->CompressionUnitShift + Vcb->ClusterShift);
+ Buffer->ChunkShift = NTFS_CHUNK_SHIFT;
+ Buffer->ClusterShift = (UCHAR)Vcb->ClusterShift;
+ Buffer->Reserved[0] = Buffer->Reserved[1] = Buffer->Reserved[2] = 0;
+
+ } finally {
+
+ if (ScbAcquired) {NtfsReleaseScb( &IrpContext, Scb );}
+ FsRtlExitFileSystem();
+ }
+}
+
+
+VOID
+NtfsFastIoQueryCompressedSize (
+ IN PFILE_OBJECT FileObject,
+ IN PLARGE_INTEGER FileOffset,
+ OUT PULONG CompressedSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is a fast call for returning the the size of a specified
+ compression unit. It assumes that the caller has an exception handler and
+ will treat exceptions as an error. Therefore, this routine does not even
+ have a finally clause, since it does not acquire any resources directly.
+
+Arguments:
+
+ FileObject - FileObject for the file on which the compressed information
+ is desired.
+
+ FileOffset - FileOffset for a compression unit for which the allocated size
+ is desired.
+
+ CompressedSize - Returns the allocated size of the compression unit.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ IRP_CONTEXT IrpContext;
+
+ TYPE_OF_OPEN TypeOfOpen;
+ PVCB Vcb;
+ PFCB Fcb;
+ PSCB Scb;
+ PCCB Ccb;
+
+ VCN Vcn;
+ LCN Lcn;
+ LONGLONG SizeInBytes;
+ LONGLONG ClusterCount = 0;
+
+ PAGED_CODE();
+
+ //
+ // Prepare the dummy irp context
+ //
+
+ RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );
+ IrpContext.NodeTypeCode = NTFS_NTC_IRP_CONTEXT;
+ IrpContext.NodeByteSize = sizeof(IRP_CONTEXT);
+ SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT);
+
+ //
+ // Determine the type of open for the input file object. The callee really
+ // ignores the irp context for us.
+ //
+
+ TypeOfOpen = NtfsDecodeFileObject( &IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, FALSE);
+ IrpContext.Vcb = Vcb;
+
+ ASSERT(Scb->CompressionUnit != 0);
+ ASSERT((FileOffset->QuadPart & (Scb->CompressionUnit - 1)) == 0);
+
+ //
+ // Calculate the Vcn the caller wants, and initialize our output.
+ //
+
+ Vcn = LlClustersFromBytes( Vcb, FileOffset->QuadPart );
+ *CompressedSize = 0;
+
+ //
+ // Loop as long as we are looking up allocated Vcns.
+ //
+
+ while (NtfsLookupAllocation(&IrpContext, Scb, Vcn, &Lcn, &ClusterCount, NULL, NULL)) {
+
+ SizeInBytes = LlBytesFromClusters( Vcb, ClusterCount );
+
+ //
+ // If this allocated run goes beyond the end of the compresion unit, then
+ // we know it is fully allocated.
+ //
+
+ if ((SizeInBytes + *CompressedSize) > Scb->CompressionUnit) {
+ *CompressedSize = Scb->CompressionUnit;
+ break;
+ }
+
+ *CompressedSize += (ULONG)SizeInBytes;
+ Vcn += ClusterCount;
+ }
+}
+#endif _CAIRO_
+
+
+VOID
+NtfsRaiseInformationHardError (
+ IN PIRP_CONTEXT IrpContext,
+ IN NTSTATUS Status,
+ IN PFILE_REFERENCE FileReference OPTIONAL,
+ IN PFCB Fcb OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to generate a popup in the event a corrupt file
+ or disk is encountered. The main purpose of the routine is to find
+ a name to pass to the popup package. If there is no Fcb we will take
+ the volume name out of the Vcb. If the Fcb has an Lcb in its Lcb list,
+ we will construct the name by walking backwards through the Lcb's.
+ If the Fcb has no Lcb but represents a system file, we will return
+ a default system string. If the Fcb represents a user file, but we
+ have no Lcb, we will use the name in the file object for the current
+ request.
+
+Arguments:
+
+ Status - Error status.
+
+ FileReference - File reference being accessed in Mft when error occurred.
+
+ Fcb - If specified, this is the Fcb being used when the error was encountered.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ FCB_TABLE_ELEMENT Key;
+ PFCB_TABLE_ELEMENT Entry = NULL;
+
+ PKTHREAD Thread;
+ UNICODE_STRING Name;
+ ULONG NameLength;
+
+ PFILE_OBJECT FileObject;
+
+ WCHAR *NewBuffer;
+
+ PIRP Irp = NULL;
+ PIO_STACK_LOCATION IrpSp;
+
+ //
+ // Return if there is no originating Irp, for example when originating
+ // from NtfsPerformHotFix.
+ //
+
+ if (IrpContext->OriginatingIrp == NULL) {
+ return;
+ }
+
+ if (IrpContext->OriginatingIrp->Type == IO_TYPE_IRP) {
+
+ Irp = IrpContext->OriginatingIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );
+ FileObject = IrpSp->FileObject;
+
+ } else {
+
+ return;
+ }
+
+ NewBuffer = NULL;
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // If the Fcb isn't specified and the file reference is, then
+ // try to get the Fcb from the Fcb table.
+ //
+
+ if (!ARGUMENT_PRESENT( Fcb )
+ && ARGUMENT_PRESENT( FileReference )) {
+
+ Key.FileReference = *FileReference;
+
+ NtfsAcquireFcbTable( IrpContext, IrpContext->Vcb );
+ Entry = RtlLookupElementGenericTable( &IrpContext->Vcb->FcbTable,
+ &Key );
+ NtfsReleaseFcbTable( IrpContext, IrpContext->Vcb );
+
+ if (Entry != NULL) {
+
+ Fcb = Entry->Fcb;
+ }
+ }
+
+ if (Irp == NULL ||
+ IoIsSystemThread( IrpContext->OriginatingIrp->Tail.Overlay.Thread )) {
+
+ Thread = NULL;
+
+ } else {
+
+ Thread = (PKTHREAD)IrpContext->OriginatingIrp->Tail.Overlay.Thread;
+ }
+
+ //
+ // If there is no Fcb and no file reference we use the name in the
+ // Vpb for the volume. If there is a file reference then assume
+ // the error occurred in a system file.
+ //
+
+ if (!ARGUMENT_PRESENT( Fcb )) {
+
+ if (!ARGUMENT_PRESENT( FileReference )) {
+
+ Name.MaximumLength = Name.Length = IrpContext->Vcb->Vpb->VolumeLabelLength;
+ Name.Buffer = (PWCHAR) IrpContext->Vcb->Vpb->VolumeLabel;
+
+ } else if (NtfsSegmentNumber( FileReference ) <= UPCASE_TABLE_NUMBER) {
+
+ Name = NtfsSystemFiles[NtfsSegmentNumber( FileReference )];
+
+ } else {
+
+ Name = NtfsSystemFiles[0];
+ }
+
+ //
+ // If the name has an Lcb, we contruct a name with a chain of Lcb's.
+ //
+
+ } else if (!IsListEmpty( &Fcb->LcbQueue )) {
+
+ BOOLEAN LeadingBackslash;
+
+ //
+ // Get the length of the list.
+ //
+
+ NameLength = NtfsLookupNameLengthViaLcb( Fcb, &LeadingBackslash );
+
+ //
+ // We now know the length of the name. Allocate and fill this buffer.
+ //
+
+ NewBuffer = NtfsAllocatePool(PagedPool, NameLength );
+
+ Name.MaximumLength = Name.Length = (USHORT) NameLength;
+ Name.Buffer = NewBuffer;
+
+ //
+ // Now insert the name.
+ //
+
+ NtfsFileNameViaLcb( Fcb, NewBuffer, NameLength, NameLength );
+
+ //
+ // Check if this is a system file.
+ //
+
+ } else if (NtfsSegmentNumber( &Fcb->FileReference ) < FIRST_USER_FILE_NUMBER) {
+
+ if (NtfsSegmentNumber( &Fcb->FileReference ) <= UPCASE_TABLE_NUMBER) {
+
+ Name = NtfsSystemFiles[NtfsSegmentNumber( &Fcb->FileReference )];
+
+ } else {
+
+ Name = NtfsSystemFiles[0];
+ }
+
+ //
+ // In this case we contruct a name out of the file objects in the
+ // Originating Irp. If there is no file object or file object buffer
+ // we generate an unknown file message.
+ //
+
+ } else if (FileObject == NULL
+ || (IrpContext->MajorFunction == IRP_MJ_CREATE
+ && BooleanFlagOn( IrpSp->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID ))
+ || (FileObject->FileName.Length == 0
+ && (FileObject->RelatedFileObject == NULL
+ || IrpContext->MajorFunction != IRP_MJ_CREATE))) {
+
+ Name = NtfsUnknownFile;
+
+ //
+ // If there is a valid name in the file object we use that.
+ //
+
+ } else if ((FileObject->FileName.MaximumLength != 0) &&
+ (FileObject->FileName.Length != 0) &&
+ (FileObject->FileName.Buffer[0] == L'\\')) {
+
+ Name = FileObject->FileName;
+
+ //
+ // We have to construct the name.
+ //
+
+ } else {
+
+ if ((FileObject->FileName.MaximumLength != 0) &&
+ (FileObject->FileName.Length != 0)) {
+
+ NameLength = FileObject->FileName.Length;
+
+ if (((PFILE_OBJECT) FileObject->RelatedFileObject)->FileName.Length != 2) {
+
+ NameLength += 2;
+ }
+
+ } else {
+
+ NameLength = 0;
+ }
+
+ NameLength += ((PFILE_OBJECT) FileObject->RelatedFileObject)->FileName.Length;
+
+ NewBuffer = NtfsAllocatePool(PagedPool, NameLength );
+
+ Name.MaximumLength = Name.Length = (USHORT) NameLength;
+ Name.Buffer = NewBuffer;
+
+ if (FileObject->FileName.Length != 0) {
+
+ NameLength -= FileObject->FileName.Length;
+
+ RtlCopyMemory( Add2Ptr( NewBuffer, NameLength ),
+ FileObject->FileName.Buffer,
+ FileObject->FileName.Length );
+
+ NameLength -= sizeof( WCHAR );
+
+ *((PWCHAR) Add2Ptr( NewBuffer, NameLength )) = L'\\';
+ }
+
+ if (NameLength != 0) {
+
+ FileObject = (PFILE_OBJECT) FileObject->RelatedFileObject;
+
+ RtlCopyMemory( NewBuffer,
+ FileObject->FileName.Buffer,
+ FileObject->FileName.Length );
+ }
+ }
+
+ //
+ // Now generate a popup.
+ //
+
+ IoRaiseInformationalHardError( Status, &Name, Thread );
+
+ } finally {
+
+ if (NewBuffer != NULL) {
+
+ NtfsFreePool( NewBuffer );
+ }
+ }
+
+ return;
+}
+
+
+PTOP_LEVEL_CONTEXT
+NtfsSetTopLevelIrp (
+ IN PTOP_LEVEL_CONTEXT TopLevelContext,
+ IN BOOLEAN ForceTopLevel,
+ IN BOOLEAN SetTopLevel
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to set up the top level context in the thread local
+ storage. Ntfs always puts its own context in this location and restores
+ the previous value on exit. This routine will determine if this request is
+ top level and top level ntfs. It will return a pointer to the top level ntfs
+ context stored in the thread local storage on return.
+
+Arguments:
+
+ TopLevelContext - This is the local top level context for our caller.
+
+ ForceTopLevel - Always use the input top level context.
+
+ SetTopLevel - Only applies if the ForceTopLevel value is TRUE. Indicates
+ if we should make this look like the top level request.
+
+Return Value:
+
+ PTOP_LEVEL_CONTEXT - Pointer to the top level ntfs context for this thread.
+ It may be the same as passed in by the caller. In that case the fields
+ will be initialized.
+
+--*/
+
+{
+ PTOP_LEVEL_CONTEXT CurrentTopLevelContext;
+ ULONG StackBottom;
+ ULONG StackTop;
+ BOOLEAN TopLevelRequest = TRUE;
+ BOOLEAN TopLevelNtfs = TRUE;
+
+ BOOLEAN ValidCurrentTopLevel = FALSE;
+
+ //
+ // Get the current value out of the thread local storage. If it is a zero
+ // value or not a pointer to a valid ntfs top level context or a valid
+ // Fsrtl value then we are the top level request.
+ //
+
+ CurrentTopLevelContext = NtfsGetTopLevelContext();
+
+ //
+ // Check if this is a valid Ntfs top level context.
+ //
+
+ IoGetStackLimits( &StackTop, &StackBottom);
+
+ if (((ULONG) CurrentTopLevelContext <= StackBottom - sizeof( TOP_LEVEL_CONTEXT )) &&
+ ((ULONG) CurrentTopLevelContext >= StackTop) &&
+ !FlagOn( (ULONG) CurrentTopLevelContext, 0x3 ) &&
+ (CurrentTopLevelContext->Ntfs == 0x5346544e)) {
+
+ ValidCurrentTopLevel = TRUE;
+ }
+
+ //
+ // If we are to force this request to be top level then set the
+ // TopLevelRequest flag according to the SetTopLevel input.
+ //
+
+ if (ForceTopLevel) {
+
+ TopLevelRequest = SetTopLevel;
+
+ //
+ // If the value is NULL then we are top level everything.
+ //
+
+ } else if (CurrentTopLevelContext == NULL) {
+
+ NOTHING;
+
+ //
+ // If this has one of the Fsrtl magic numbers then we were called from
+ // either the fast io path or the mm paging io path.
+ //
+
+ } else if ((ULONG) CurrentTopLevelContext <= FSRTL_MAX_TOP_LEVEL_IRP_FLAG) {
+
+ TopLevelRequest = FALSE;
+
+ } else if (ValidCurrentTopLevel &&
+ !FlagOn( CurrentTopLevelContext->TopLevelIrpContext->Flags,
+ IRP_CONTEXT_FLAG_CALL_SELF )) {
+
+ TopLevelRequest = FALSE;
+ TopLevelNtfs = FALSE;
+ }
+
+ //
+ // If we are the top level ntfs then initialize the caller's structure
+ // and store it in the thread local storage.
+ //
+
+ if (TopLevelNtfs) {
+
+ TopLevelContext->Ntfs = 0x5346544e;
+ TopLevelContext->SavedTopLevelIrp = (PIRP) CurrentTopLevelContext;
+ TopLevelContext->TopLevelIrpContext = NULL;
+ TopLevelContext->TopLevelRequest = TopLevelRequest;
+
+ if (ValidCurrentTopLevel) {
+
+ TopLevelContext->VboBeingHotFixed = CurrentTopLevelContext->VboBeingHotFixed;
+ TopLevelContext->ScbBeingHotFixed = CurrentTopLevelContext->ScbBeingHotFixed;
+ TopLevelContext->ValidSavedTopLevel = TRUE;
+ TopLevelContext->OverflowReadThread = CurrentTopLevelContext->OverflowReadThread;
+
+ } else {
+
+ TopLevelContext->VboBeingHotFixed = 0;
+ TopLevelContext->ScbBeingHotFixed = NULL;
+ TopLevelContext->ValidSavedTopLevel = FALSE;
+ TopLevelContext->OverflowReadThread = FALSE;
+ }
+
+ IoSetTopLevelIrp( (PIRP) TopLevelContext );
+ return TopLevelContext;
+ }
+
+ return CurrentTopLevelContext;
+}
+
+#ifdef NTFS_CHECK_BITMAP
+BOOLEAN NtfsForceBitmapBugcheck = FALSE;
+BOOLEAN NtfsDisableBitmapCheck = FALSE;
+
+VOID
+NtfsBadBitmapCopy (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BadBit,
+ IN ULONG Length
+ )
+{
+ if (!NtfsDisableBitmapCheck) {
+
+ DbgPrint("%s:%d %s\n",__FILE__,__LINE__,"Invalid bitmap");
+ DbgBreakPoint();
+
+ if (!NtfsDisableBitmapCheck && NtfsForceBitmapBugcheck) {
+
+ KeBugCheckEx( NTFS_FILE_SYSTEM, (ULONG) IrpContext, BadBit, Length, 0 );
+ }
+ }
+ return;
+}
+
+BOOLEAN
+NtfsCheckBitmap (
+ IN PVCB Vcb,
+ IN ULONG Lcn,
+ IN ULONG Count,
+ IN BOOLEAN Set
+ )
+{
+ ULONG BitmapPage;
+ ULONG LastBitmapPage;
+ ULONG BitOffset;
+ ULONG BitsThisPage;
+ BOOLEAN Valid = FALSE;
+
+ BitmapPage = Lcn / (PAGE_SIZE * 8);
+ LastBitmapPage = (Lcn + Count + (PAGE_SIZE * 8) - 1) / (PAGE_SIZE * 8);
+ BitOffset = Lcn & ((PAGE_SIZE * 8) - 1);
+
+ if (LastBitmapPage > Vcb->BitmapPages) {
+
+ return Valid;
+ }
+
+ do {
+
+ BitsThisPage = Count;
+
+ if (BitOffset + Count > (PAGE_SIZE * 8)) {
+
+ BitsThisPage = (PAGE_SIZE * 8) - BitOffset;
+ }
+
+ if (Set) {
+
+ Valid = RtlAreBitsSet( Vcb->BitmapCopy + BitmapPage,
+ BitOffset,
+ BitsThisPage );
+
+ } else {
+
+ Valid = RtlAreBitsClear( Vcb->BitmapCopy + BitmapPage,
+ BitOffset,
+ BitsThisPage );
+ }
+
+ BitOffset = 0;
+ Count -= BitsThisPage;
+ BitmapPage += 1;
+
+ } while (Valid && (BitmapPage < LastBitmapPage));
+
+ if (Count != 0) {
+
+ Valid = FALSE;
+ }
+
+ return Valid;
+}
+#endif
+
+//
+// Debugging support routines used for pool verification. Alas, this works only
+// on checked X86.
+//
+
+#if DBG && i386 && defined (NTFSPOOLCHECK)
+//
+// Number of backtrace items retrieved on X86
+
+
+#define BACKTRACE_DEPTH 8
+
+typedef struct _BACKTRACE
+{
+ ULONG State;
+ ULONG Pad;
+ PVOID Allocate[BACKTRACE_DEPTH];
+ PVOID Free[BACKTRACE_DEPTH];
+} BACKTRACE, *PBACKTRACE;
+
+
+#define STATE_ALLOCATED 'M'
+#define STATE_LOOKASIDE 'J'
+#define STATE_FREE 'Z'
+
+//
+// WARNING! The following depends on pool allocations being either
+// 0 mod PAGE_SIZE (for large blocks)
+// or 8 mod 0x20 (for all other requests)
+//
+
+#define PAGE_ALIGNED(pv) (((ULONG)(pv) & (PAGE_SIZE - 1)) == 0)
+#define IsKernelPoolBlock(pv) (PAGE_ALIGNED(pv) || (((ULONG)(pv) % 0x20) == 8))
+
+PVOID
+NtfsDebugAllocatePoolWithTagNoRaise (
+ POOL_TYPE Pool,
+ ULONG Length,
+ ULONG Tag)
+{
+ ULONG Ignore;
+ PBACKTRACE BackTrace =
+ ExAllocatePoolWithTag( Pool, Length + sizeof (BACKTRACE), Tag );
+
+ if (PAGE_ALIGNED(BackTrace))
+ {
+ return BackTrace;
+ }
+
+ RtlZeroMemory( BackTrace, sizeof (BACKTRACE) );
+ RtlCaptureStackBackTrace( 0, BACKTRACE_DEPTH, BackTrace->Allocate, &Ignore );
+
+ BackTrace->State = STATE_ALLOCATED;
+
+ return BackTrace + 1;
+}
+
+PVOID
+NtfsDebugAllocatePoolWithTag (
+ POOL_TYPE Pool,
+ ULONG Length,
+ ULONG Tag)
+{
+ ULONG Ignore;
+ PBACKTRACE BackTrace =
+ FsRtlAllocatePoolWithTag( Pool, Length + sizeof (BACKTRACE), Tag );
+
+ if (PAGE_ALIGNED(BackTrace))
+ {
+ return BackTrace;
+ }
+
+ RtlZeroMemory( BackTrace, sizeof (BACKTRACE) );
+ RtlCaptureStackBackTrace( 0, BACKTRACE_DEPTH, BackTrace->Allocate, &Ignore );
+
+ BackTrace->State = STATE_ALLOCATED;
+
+ return BackTrace + 1;
+}
+
+VOID
+NtfsDebugFreePool (
+ PVOID pv)
+{
+ if (IsKernelPoolBlock( pv ))
+ {
+ ExFreePool( pv );
+ }
+ else
+ {
+ ULONG Ignore;
+ PBACKTRACE BackTrace = (PBACKTRACE)pv - 1;
+
+ if (BackTrace->State != STATE_ALLOCATED)
+ {
+ DbgBreakPoint( );
+ }
+
+ RtlCaptureStackBackTrace( 0, BACKTRACE_DEPTH, BackTrace->Free, &Ignore );
+
+ BackTrace->State = STATE_FREE;
+
+ ExFreePool( BackTrace );
+ }
+}
+
+#endif