3705 lines
100 KiB
C
3705 lines
100 KiB
C
/*++
|
||
|
||
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)
|
||
|
||
//
|
||
// Debugging control variables
|
||
//
|
||
|
||
PUCHAR NtfsPageInAddress = NULL;
|
||
LONGLONG NtfsMapOffset = -1;
|
||
|
||
//
|
||
// 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) { \
|
||
PFILE_SYSTEM_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;
|
||
|
||
//
|
||
// Mutex to synchronize creation of stream files.
|
||
//
|
||
|
||
KMUTANT StreamFileCreationMutex;
|
||
|
||
//
|
||
// Notification event for creation of encrypted files.
|
||
//
|
||
|
||
KEVENT NtfsEncryptionPendingEvent;
|
||
#ifdef KEITHKA
|
||
ULONG EncryptionPendingCount = 0;
|
||
#endif
|
||
|
||
//
|
||
// 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};
|
||
LARGE_INTEGER NtfsLargeMax = {0xffffffff,0x7fffffff};
|
||
LARGE_INTEGER NtfsLargeEof = {0xffffffff,0xffffffff};
|
||
|
||
//
|
||
// The granularity for updating last access times
|
||
//
|
||
|
||
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 NtfsIoContextLookasideList;
|
||
NPAGED_LOOKASIDE_LIST NtfsIrpContextLookasideList;
|
||
NPAGED_LOOKASIDE_LIST NtfsKeventLookasideList;
|
||
NPAGED_LOOKASIDE_LIST NtfsScbNonpagedLookasideList;
|
||
NPAGED_LOOKASIDE_LIST NtfsScbSnapshotLookasideList;
|
||
NPAGED_LOOKASIDE_LIST NtfsCompressSyncLookasideList;
|
||
|
||
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.
|
||
//
|
||
|
||
const UNICODE_STRING NtfsFileNameIndex = CONSTANT_UNICODE_STRING( L"$I30" );
|
||
|
||
//
|
||
// This is the string for the attribute code for index allocation.
|
||
// $INDEX_ALLOCATION.
|
||
//
|
||
|
||
const UNICODE_STRING NtfsIndexAllocation = CONSTANT_UNICODE_STRING( L"$INDEX_ALLOCATION" );
|
||
|
||
//
|
||
// This is the string for the data attribute, $DATA.
|
||
//
|
||
|
||
const UNICODE_STRING NtfsDataString = CONSTANT_UNICODE_STRING( L"$DATA" );
|
||
|
||
//
|
||
// This is the string for the bitmap attribute
|
||
//
|
||
|
||
const UNICODE_STRING NtfsBitmapString = CONSTANT_UNICODE_STRING( L"$BITMAP" );
|
||
|
||
//
|
||
// This is the string for the attribute list attribute
|
||
//
|
||
|
||
const UNICODE_STRING NtfsAttrListString = CONSTANT_UNICODE_STRING( L"$ATTRIBUTE_LIST" );
|
||
|
||
//
|
||
// This is the string for the attribute list attribute
|
||
//
|
||
|
||
const UNICODE_STRING NtfsReparsePointString = CONSTANT_UNICODE_STRING( L"$REPARSE_POINT" );
|
||
|
||
//
|
||
// These strings are used as the Scb->AttributeName for
|
||
// user-opened general indices. Declaring them here avoids
|
||
// having to marshal allocating & freeing them.
|
||
//
|
||
|
||
const UNICODE_STRING NtfsObjId = CONSTANT_UNICODE_STRING( L"$O" );
|
||
const UNICODE_STRING NtfsQuota = CONSTANT_UNICODE_STRING( L"$Q" );
|
||
|
||
//
|
||
// The following is the name of the data stream for the Usn journal.
|
||
//
|
||
|
||
const UNICODE_STRING JournalStreamName = CONSTANT_UNICODE_STRING( L"$J" );
|
||
|
||
//
|
||
// These are the strings for the files in the extend directory.
|
||
//
|
||
|
||
const UNICODE_STRING NtfsExtendName = CONSTANT_UNICODE_STRING( L"$Extend" );
|
||
const UNICODE_STRING NtfsUsnJrnlName = CONSTANT_UNICODE_STRING( L"$UsnJrnl" );
|
||
const UNICODE_STRING NtfsQuotaName = CONSTANT_UNICODE_STRING( L"$Quota" );
|
||
const UNICODE_STRING NtfsObjectIdName = CONSTANT_UNICODE_STRING( L"$ObjId" );
|
||
const UNICODE_STRING NtfsMountTableName = CONSTANT_UNICODE_STRING( L"$Reparse" );
|
||
|
||
//
|
||
// This strings are used for informational popups.
|
||
//
|
||
|
||
const 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"\\$Secure" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$UpCase" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$Extend" ),
|
||
};
|
||
|
||
const UNICODE_STRING NtfsInternalUseFile[] = {
|
||
CONSTANT_UNICODE_STRING( L"\\$ChangeAttributeValue" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$ChangeAttributeValue2" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$CommonCleanup" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$ConvertToNonresident" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$CreateNonresidentWithValue" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$DeallocateRecord" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$DeleteAllocationFromRecord" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$Directory" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$InitializeRecordAllocation" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$MapAttributeValue" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$NonCachedIo" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$PerformHotFix" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$PrepareToShrinkFileSize" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$ReplaceAttribute" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$ReplaceAttribute2" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$SetAllocationInfo" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$SetEndOfFileInfo" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$ZeroRangeInStream" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$ZeroRangeInStream2" ),
|
||
CONSTANT_UNICODE_STRING( L"\\$ZeroRangeInStream3" ),
|
||
};
|
||
|
||
const UNICODE_STRING NtfsUnknownFile =
|
||
CONSTANT_UNICODE_STRING( L"\\????" );
|
||
|
||
const UNICODE_STRING NtfsRootIndexString =
|
||
CONSTANT_UNICODE_STRING( L"." );
|
||
|
||
//
|
||
// This is the empty string. This can be used to pass a string with
|
||
// no length.
|
||
//
|
||
|
||
const UNICODE_STRING NtfsEmptyString =
|
||
CONSTANT_UNICODE_STRING( L"" );
|
||
|
||
//
|
||
// The following file references are used to identify system files.
|
||
//
|
||
|
||
const FILE_REFERENCE MftFileReference = { MASTER_FILE_TABLE_NUMBER, 0, MASTER_FILE_TABLE_NUMBER };
|
||
const FILE_REFERENCE Mft2FileReference = { MASTER_FILE_TABLE2_NUMBER, 0, MASTER_FILE_TABLE2_NUMBER };
|
||
const FILE_REFERENCE LogFileReference = { LOG_FILE_NUMBER, 0, LOG_FILE_NUMBER };
|
||
const FILE_REFERENCE VolumeFileReference = { VOLUME_DASD_NUMBER, 0, VOLUME_DASD_NUMBER };
|
||
const FILE_REFERENCE AttrDefFileReference = { ATTRIBUTE_DEF_TABLE_NUMBER, 0, ATTRIBUTE_DEF_TABLE_NUMBER };
|
||
const FILE_REFERENCE RootIndexFileReference = { ROOT_FILE_NAME_INDEX_NUMBER, 0, ROOT_FILE_NAME_INDEX_NUMBER };
|
||
const FILE_REFERENCE BitmapFileReference = { BIT_MAP_FILE_NUMBER, 0, BIT_MAP_FILE_NUMBER };
|
||
const FILE_REFERENCE BootFileReference = { BOOT_FILE_NUMBER, 0, BOOT_FILE_NUMBER };
|
||
const FILE_REFERENCE ExtendFileReference = { EXTEND_NUMBER, 0, EXTEND_NUMBER };
|
||
const FILE_REFERENCE FirstUserFileReference = { FIRST_USER_FILE_NUMBER, 0, 0 };
|
||
|
||
//
|
||
// The following are used to determine what level of protection to attach
|
||
// to system files and attributes.
|
||
//
|
||
|
||
BOOLEAN NtfsProtectSystemFiles = TRUE;
|
||
BOOLEAN NtfsProtectSystemAttributes = TRUE;
|
||
|
||
//
|
||
// The following is used to indicate the multiplier value for the Mft zone.
|
||
//
|
||
|
||
ULONG NtfsMftZoneMultiplier;
|
||
|
||
//
|
||
// Debug code for finding corruption.
|
||
//
|
||
|
||
#if (DBG || defined( NTFS_FREE_ASSERTS ))
|
||
BOOLEAN NtfsBreakOnCorrupt = TRUE;
|
||
#else
|
||
BOOLEAN NtfsBreakOnCorrupt = FALSE;
|
||
#endif
|
||
|
||
//#endif
|
||
|
||
//
|
||
// Enable compression on the wire.
|
||
//
|
||
|
||
BOOLEAN NtfsEnableCompressedIO = FALSE;
|
||
|
||
//
|
||
// FsRtl fast I/O call backs
|
||
//
|
||
|
||
FAST_IO_DISPATCH NtfsFastIoDispatch;
|
||
|
||
#ifdef BRIANDBG
|
||
ULONG NtfsIgnoreReserved = FALSE;
|
||
#endif
|
||
|
||
#ifdef NTFS_LOG_FULL_TEST
|
||
LONG NtfsFailCheck = 0;
|
||
LONG NtfsFailFrequency = 0;
|
||
LONG NtfsPeriodicFail = 0;
|
||
#endif
|
||
|
||
#ifdef NTFSDBG
|
||
|
||
LONG NtfsDebugTraceLevel = DEBUG_TRACE_ERROR;
|
||
LONG NtfsDebugTraceIndent = 0;
|
||
|
||
ULONG NtfsFsdEntryCount = 0;
|
||
ULONG NtfsFspEntryCount = 0;
|
||
ULONG NtfsIoCallDriverCount = 0;
|
||
LONG NtfsReturnStatusFilter = 0xf0ffffffL; // just an non-existent error code
|
||
|
||
#endif // NTFSDBG
|
||
|
||
#ifdef SYSCACHE_DEBUG
|
||
ULONG NtfsSyscacheTrackingActive = 1;
|
||
#endif
|
||
|
||
//
|
||
// Default restart version.
|
||
//
|
||
|
||
#ifdef _WIN64
|
||
ULONG NtfsDefaultRestartVersion = 1;
|
||
#else
|
||
ULONG NtfsDefaultRestartVersion = 0;
|
||
#endif
|
||
|
||
//
|
||
// Performance statistics
|
||
//
|
||
|
||
ULONG NtfsMaxDelayedCloseCount;
|
||
ULONG NtfsMinDelayedCloseCount;
|
||
ULONG NtfsThrottleCreates;
|
||
ULONG NtfsFailedHandedOffPagingFileOps = 0;
|
||
ULONG NtfsFailedPagingFileOps = 0;
|
||
ULONG NtfsFailedAborts = 0;
|
||
ULONG NtfsFailedPagingReads = 0;
|
||
ULONG NtfsFailedHandedOffPagingReads = 0;
|
||
ULONG NtfsFailedLfsRestart = 0;
|
||
|
||
ULONG NtfsCleanCheckpoints = 0;
|
||
ULONG NtfsPostRequests = 0;
|
||
|
||
const UCHAR BaadSignature[4] = {'B', 'A', 'A', 'D'};
|
||
const UCHAR IndexSignature[4] = {'I', 'N', 'D', 'X'};
|
||
const UCHAR FileSignature[4] = {'F', 'I', 'L', 'E'};
|
||
const UCHAR HoleSignature[4] = {'H', 'O', 'L', 'E'};
|
||
const 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
|
||
|
||
FAST_MUTEX NtfsScavengerLock;
|
||
PIRP_CONTEXT NtfsScavengerWorkList;
|
||
BOOLEAN NtfsScavengerRunning;
|
||
ULONGLONG NtfsMaxQuotaNotifyRate = MIN_QUOTA_NOTIFY_TIME;
|
||
ULONG NtfsAsyncPostThreshold;
|
||
|
||
UCHAR NtfsZeroExtendedInfo[48];
|
||
|
||
typedef struct _VOLUME_ERROR_PACKET {
|
||
NTSTATUS Status;
|
||
UNICODE_STRING FileName;
|
||
PKTHREAD Thread;
|
||
} VOLUME_ERROR_PACKET, *PVOLUME_ERROR_PACKET;
|
||
|
||
#ifdef NTFS_RWC_DEBUG
|
||
//
|
||
// Range to include in COW checks.
|
||
//
|
||
|
||
LONGLONG NtfsRWCLowThreshold = 0;
|
||
LONGLONG NtfsRWCHighThreshold = 0x7fffffffffffffff;
|
||
#endif
|
||
|
||
VOID
|
||
NtfsResolveVolumeAndRaiseErrorSpecial (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN OUT PVOID Context
|
||
);
|
||
|
||
NTSTATUS
|
||
NtfsFsdDispatchSwitch (
|
||
IN PIRP_CONTEXT StackIrpContext OPTIONAL,
|
||
IN PIRP Irp,
|
||
IN BOOLEAN Wait
|
||
);
|
||
|
||
//
|
||
// Locals used to track specific failures.
|
||
//
|
||
|
||
BOOLEAN NtfsTestStatus = FALSE;
|
||
BOOLEAN NtfsTestFilter = FALSE;
|
||
NTSTATUS NtfsTestStatusCode = STATUS_SUCCESS;
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, NtfsFastIoCheckIfPossible)
|
||
#pragma alloc_text(PAGE, NtfsFastQueryBasicInfo)
|
||
#pragma alloc_text(PAGE, NtfsFastQueryStdInfo)
|
||
#pragma alloc_text(PAGE, NtfsFastQueryNetworkOpenInfo)
|
||
#pragma alloc_text(PAGE, NtfsFastIoQueryCompressionInfo)
|
||
#pragma alloc_text(PAGE, NtfsFastIoQueryCompressedSize)
|
||
#pragma alloc_text(PAGE, NtfsFsdDispatch)
|
||
#pragma alloc_text(PAGE, NtfsFsdDispatchWait)
|
||
#pragma alloc_text(PAGE, NtfsFsdDispatchSwitch)
|
||
#pragma alloc_text(PAGE, NtfsResolveVolumeAndRaiseErrorSpecial)
|
||
#endif
|
||
|
||
//
|
||
// Internal support routines
|
||
//
|
||
|
||
LONG
|
||
NtfsProcessExceptionFilter (
|
||
IN PEXCEPTION_POINTERS ExceptionPointer
|
||
)
|
||
{
|
||
UNREFERENCED_PARAMETER( ExceptionPointer );
|
||
|
||
ASSERT( !NtfsBreakOnCorrupt ||
|
||
(ExceptionPointer->ExceptionRecord->ExceptionCode != STATUS_LOG_FILE_FULL) );
|
||
|
||
#ifndef LFS_CLUSTER_CHECK
|
||
ASSERT( !NtfsBreakOnCorrupt ||
|
||
NT_SUCCESS( ExceptionPointer->ExceptionRecord->ExceptionCode ));
|
||
#endif
|
||
|
||
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
|
||
NtfsCorruptionBreakPointTest (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN ULONG ExceptionCode
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The corruption breakpoint routine. Breakin when NtfsBreakOnCorrupt is set
|
||
and we encounter corruption. This is currently active only on checked builds.
|
||
|
||
Arguments:
|
||
|
||
ExceptionCode - the exception that occured
|
||
|
||
Return Value:
|
||
|
||
none
|
||
|
||
--*/
|
||
|
||
{
|
||
if (NtfsBreakOnCorrupt &&
|
||
((ExceptionCode == STATUS_FILE_CORRUPT_ERROR) ||
|
||
(ExceptionCode == STATUS_DISK_CORRUPT_ERROR))) {
|
||
|
||
if (*KdDebuggerEnabled) {
|
||
DbgPrint("*******************************************\n");
|
||
DbgPrint("NTFS detected corruption on your volume\n");
|
||
DbgPrint("IrpContext=0x%08x, VCB=0x%08x\n",IrpContext,IrpContext->Vcb);
|
||
DbgPrint("Send email to NTFSDEV\n");
|
||
DbgPrint("*******************************************\n");
|
||
DbgBreakPoint();
|
||
|
||
while (NtfsPageInAddress) {
|
||
|
||
volatile CHAR test;
|
||
|
||
if (NtfsMapOffset != -1) {
|
||
|
||
PBCB Bcb;
|
||
PVOID Buffer;
|
||
|
||
if ((NtfsMapOffset > 0) && (NtfsMapOffset < ((PSCB)(IrpContext->Vcb->LogFileObject->FsContext))->Header.FileSize.QuadPart)) {
|
||
CcMapData( IrpContext->Vcb->LogFileObject, (PLARGE_INTEGER)&NtfsMapOffset, PAGE_SIZE, TRUE, &Bcb, &Buffer );
|
||
} else {
|
||
KdPrint(( "Offset out of range to be mapped: 0x%I64x logfilesize: %I64x\n", NtfsMapOffset, ((PSCB)(IrpContext->Vcb->LogFileObject->FsContext))->Header.FileSize.QuadPart ));
|
||
}
|
||
|
||
}
|
||
|
||
test = *NtfsPageInAddress;
|
||
DbgBreakPoint();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
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) ||
|
||
(Status == STATUS_FILE_CORRUPT_ERROR) ||
|
||
(Status == STATUS_EA_CORRUPT_ERROR)) {
|
||
|
||
//
|
||
// Let's not bugcheck just because of logfile corruption during
|
||
// restart when mounting volume. Just mark the volume dirty.
|
||
// Autochk should be able to fix it.
|
||
//
|
||
|
||
if ((IrpContext->Vcb != NULL) &&
|
||
(IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
|
||
(IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) &&
|
||
FlagOn( IrpContext->Vcb->Vpb->RealDevice->Flags, DO_SYSTEM_BOOT_PARTITION ) &&
|
||
!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS )) {
|
||
|
||
NtfsBugCheck( (ULONG_PTR)IrpContext, (ULONG_PTR)Status, 0 );
|
||
}
|
||
|
||
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 in the write path.
|
||
//
|
||
|
||
if ((Status == STATUS_QUOTA_EXCEEDED) &&
|
||
(IrpContext->MajorFunction == IRP_MJ_WRITE) &&
|
||
(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) );
|
||
|
||
//
|
||
// Check if this status is the status we are watching for.
|
||
//
|
||
|
||
if (NtfsTestFilter && (NtfsTestStatusCode == ExceptionCode)) {
|
||
|
||
NtfsTestStatusProc();
|
||
}
|
||
|
||
#if (DBG || defined( NTFS_FREE_ASSERTS ))
|
||
//
|
||
// We should not raise insufficient resources during paging file reads
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( IrpContext ) && (IrpContext->OriginatingIrp != NULL)) {
|
||
|
||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );
|
||
PSCB Scb = NULL;
|
||
|
||
if (IrpSp && (IrpSp->FileObject != NULL)) {
|
||
Scb = (PSCB)IrpSp->FileObject->FsContext;
|
||
}
|
||
|
||
ASSERT( (IrpContext->MajorFunction != IRP_MJ_READ) ||
|
||
(ExceptionCode != STATUS_INSUFFICIENT_RESOURCES) ||
|
||
(Scb == NULL) ||
|
||
(!FlagOn( Scb->Fcb->FcbState, FCB_STATE_PAGING_FILE )) );
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// 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 = (NTSTATUS) 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_PTR)ExceptionPointer->ExceptionRecord,
|
||
(ULONG_PTR)ExceptionPointer->ContextRecord,
|
||
(ULONG_PTR)ExceptionPointer->ExceptionRecord->ExceptionAddress );
|
||
}
|
||
|
||
return EXCEPTION_EXECUTE_HANDLER;
|
||
}
|
||
|
||
//
|
||
// Assert if we have caught an exception beyond a point at which we assumed we
|
||
// wouldn't raise anymore
|
||
//
|
||
|
||
ASSERT( !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_NO_FAILURES_EXPECTED ) );
|
||
|
||
NtfsCorruptionBreakPointTest( IrpContext, ExceptionCode );
|
||
|
||
//
|
||
// 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->State, IRP_CONTEXT_STATE_WAIT )) {
|
||
|
||
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST );
|
||
}
|
||
|
||
SetFlag(IrpContext->State, IRP_CONTEXT_STATE_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 )) {
|
||
|
||
#ifdef PERF_STATS
|
||
if (ExceptionCode == STATUS_LOG_FILE_FULL) {
|
||
IrpContext->LogFullReason = LF_LOG_SPACE;
|
||
}
|
||
#endif
|
||
|
||
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->MajorFunction == IRP_MJ_WRITE) &&
|
||
(IrpContext->OriginatingIrp != NULL) &&
|
||
FlagOn( IrpContext->OriginatingIrp->Flags, IRP_PAGING_IO )) {
|
||
|
||
ExceptionCode = STATUS_FILE_LOCK_CONFLICT;
|
||
}
|
||
|
||
IrpContext->ExceptionStatus = ExceptionCode;
|
||
|
||
} else {
|
||
|
||
NtfsBugCheck( (ULONG_PTR)ExceptionPointer->ExceptionRecord,
|
||
(ULONG_PTR)ExceptionPointer->ContextRecord,
|
||
(ULONG_PTR)ExceptionPointer->ExceptionRecord->ExceptionAddress );
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// We raised this code explicitly ourselves, so it had better be
|
||
// expected.
|
||
//
|
||
|
||
ASSERT( ExceptionCode == IrpContext->ExceptionStatus );
|
||
ASSERT( FsRtlIsNtstatusExpected( ExceptionCode ) );
|
||
}
|
||
|
||
#ifdef LFS_CLUSTER_CHECK
|
||
ASSERT( !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_DISMOUNT_LOG_FLUSH ) ||
|
||
((IrpContext->ExceptionStatus != STATUS_NO_SUCH_DEVICE) &&
|
||
(IrpContext->ExceptionStatus != STATUS_DEVICE_BUSY) &&
|
||
(IrpContext->ExceptionStatus != STATUS_DEVICE_OFF_LINE) ));
|
||
#endif
|
||
|
||
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;
|
||
IrpContext->Vcb->LogFileFullCount += 1;
|
||
}
|
||
|
||
return EXCEPTION_EXECUTE_HANDLER;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtfsProcessException (
|
||
IN PIRP_CONTEXT IrpContext OPTIONAL,
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
PVCB Vcb;
|
||
PIRP_CONTEXT PostIrpContext = NULL;
|
||
BOOLEAN Retry = FALSE;
|
||
PUSN_FCB ThisUsn, LastUsn;
|
||
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) {
|
||
|
||
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.
|
||
//
|
||
|
||
Vcb = IrpContext->Vcb;
|
||
CollectExceptionStats( 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_MN_COMPLETE ) == IRP_MN_MDL) &&
|
||
(Irp->MdlAddress != NULL)) {
|
||
|
||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
|
||
CcMdlWriteAbort( IrpSp->FileObject, 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 (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.
|
||
//
|
||
|
||
NtfsAcquireResourceExclusive( IrpContext, Vcb->BitmapScb, TRUE );
|
||
|
||
//
|
||
// Restore the free cluster count in the Vcb.
|
||
//
|
||
|
||
Vcb->FreeClusters -= IrpContext->FreeClusterChange;
|
||
|
||
ReleaseBitmap = TRUE;
|
||
}
|
||
|
||
//
|
||
// If we are aborting a transaction, then it is important to clear out the
|
||
// Usn reasons, so we do not try to write a Usn Journal record for
|
||
// somthing that did not happen! Worse yet if we get a log file full
|
||
// we fail the abort, which is not allowed.
|
||
//
|
||
// First, reset the bits in the Fcb, so we will not fail to allow posting
|
||
// and writing these bits later. Note that all the reversible changes are
|
||
// done with the Fcb exclusive, and they are actually backed out anyway.
|
||
// All the nonreversible ones (only unnamed and named data overwrite) are
|
||
// forced out first anyway before the data is actually modified.
|
||
//
|
||
|
||
ThisUsn = &IrpContext->Usn;
|
||
do {
|
||
|
||
PFCB UsnFcb;
|
||
|
||
if (ThisUsn->CurrentUsnFcb != NULL) {
|
||
|
||
UsnFcb = ThisUsn->CurrentUsnFcb;
|
||
|
||
NtfsLockFcb( IrpContext, UsnFcb );
|
||
|
||
//
|
||
// We may hold nothing here (write path) so we have to retest for the usn
|
||
// after locking it down in case of the deleteusnjournal worker
|
||
//
|
||
|
||
if (UsnFcb->FcbUsnRecord != NULL) {
|
||
|
||
//
|
||
// If any rename flags are part of the new reasons then
|
||
// make sure to look the name up again.
|
||
//
|
||
|
||
if (FlagOn( ThisUsn->NewReasons,
|
||
USN_REASON_RENAME_NEW_NAME | USN_REASON_RENAME_OLD_NAME )) {
|
||
|
||
ClearFlag( UsnFcb->FcbState, FCB_STATE_VALID_USN_NAME );
|
||
}
|
||
|
||
//
|
||
// Now restore the reason and source info fields.
|
||
//
|
||
|
||
ClearFlag( UsnFcb->FcbUsnRecord->UsnRecord.Reason,
|
||
ThisUsn->NewReasons );
|
||
if (UsnFcb->FcbUsnRecord->UsnRecord.Reason == 0) {
|
||
|
||
UsnFcb->FcbUsnRecord->UsnRecord.SourceInfo = 0;
|
||
|
||
} else {
|
||
|
||
SetFlag( UsnFcb->FcbUsnRecord->UsnRecord.SourceInfo,
|
||
ThisUsn->RemovedSourceInfo );
|
||
}
|
||
|
||
//
|
||
// Restore the old fcb state - if we attempted to grow std info
|
||
//
|
||
|
||
if (FlagOn( ThisUsn->UsnFcbFlags, USN_FCB_FLAG_NEW_FCB_STATE )) {
|
||
|
||
UsnFcb->FcbState = ThisUsn->OldFcbState;
|
||
ClearFlag( ThisUsn->UsnFcbFlags, USN_FCB_FLAG_NEW_FCB_STATE );
|
||
}
|
||
}
|
||
|
||
NtfsUnlockFcb( IrpContext, UsnFcb );
|
||
|
||
//
|
||
// Zero out the structure.
|
||
//
|
||
|
||
ThisUsn->CurrentUsnFcb = NULL;
|
||
ThisUsn->NewReasons = 0;
|
||
ThisUsn->RemovedSourceInfo = 0;
|
||
ThisUsn->UsnFcbFlags = 0;
|
||
|
||
if (ThisUsn != &IrpContext->Usn) {
|
||
|
||
LastUsn->NextUsnFcb = ThisUsn->NextUsnFcb;
|
||
NtfsFreePool( ThisUsn );
|
||
ThisUsn = LastUsn;
|
||
}
|
||
}
|
||
|
||
if (ThisUsn->NextUsnFcb == NULL) {
|
||
break;
|
||
}
|
||
|
||
LastUsn = ThisUsn;
|
||
ThisUsn = ThisUsn->NextUsnFcb;
|
||
|
||
} while (TRUE);
|
||
|
||
//
|
||
// Only abort the transaction if the volume is still mounted.
|
||
//
|
||
|
||
if (FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
|
||
|
||
NtfsAbortTransaction( IrpContext, Vcb, NULL );
|
||
}
|
||
|
||
if (ReleaseBitmap) {
|
||
|
||
NtfsReleaseResource( IrpContext, Vcb->BitmapScb );
|
||
ReleaseBitmap = FALSE;
|
||
}
|
||
|
||
NtfsRestoreScbSnapshots( IrpContext, FALSE );
|
||
|
||
NtfsAcquireCheckpoint( IrpContext, Vcb );
|
||
SetFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_ENABLED );
|
||
NtfsReleaseCheckpoint( IrpContext, Vcb );
|
||
|
||
//
|
||
// It is a rare path where the user is extending a volume and has hit
|
||
// an exception. In that case we need to roll back the total clusters
|
||
// in the Vcb. We detect this case is possible by comparing the
|
||
// snapshot value for total clusters in the Vcb.
|
||
//
|
||
|
||
if (Vcb->TotalClusters != Vcb->PreviousTotalClusters) {
|
||
|
||
//
|
||
// Someone is changing this value but is it us.
|
||
//
|
||
|
||
if ((Vcb->BitmapScb != NULL) &&
|
||
NtfsIsExclusiveScb( Vcb->BitmapScb )) {
|
||
|
||
Vcb->TotalClusters = Vcb->PreviousTotalClusters;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Exceptions at this point are pretty bad, we failed to undo everything.
|
||
//
|
||
|
||
} except(NtfsProcessExceptionFilter( GetExceptionInformation() )) {
|
||
|
||
PSCB_SNAPSHOT ScbSnapshot;
|
||
PSCB NextScb;
|
||
PTRANSACTION_ENTRY TransactionEntry;
|
||
|
||
//
|
||
// Update counter
|
||
//
|
||
|
||
NtfsFailedAborts += 1;
|
||
|
||
//
|
||
// 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( Vcb->VcbState, VCB_STATE_RELOAD_FREE_CLUSTERS );
|
||
NtfsReleaseResource( IrpContext, Vcb->BitmapScb );
|
||
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;
|
||
|
||
|
||
//
|
||
// 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;
|
||
}
|
||
|
||
//
|
||
// Unload all information from memory and force future references to resynch to disk
|
||
// for all regular files. For system files / paging files truncate all sizes
|
||
// and unload based on the snapshot
|
||
//
|
||
|
||
if (!FlagOn( NextScb->Fcb->FcbState, FCB_STATE_SYSTEM_FILE | FCB_STATE_PAGING_FILE )) {
|
||
|
||
ClearFlag( NextScb->ScbState, SCB_STATE_HEADER_INITIALIZED | SCB_STATE_FILE_SIZE_LOADED );
|
||
NextScb->Header.AllocationSize.QuadPart =
|
||
NextScb->Header.FileSize.QuadPart =
|
||
NextScb->Header.ValidDataLength.QuadPart = 0;
|
||
|
||
//
|
||
// Remove all of the mappings in the Mcb.
|
||
//
|
||
|
||
NtfsUnloadNtfsMcbRange( &NextScb->Mcb, (LONGLONG)0, MAXLONGLONG, FALSE, FALSE );
|
||
|
||
//
|
||
// If there is any caching tear it down so that we reinit it with values off disk
|
||
//
|
||
|
||
if (NextScb->FileObject) {
|
||
NtfsDeleteInternalAttributeStream( NextScb, TRUE, FALSE );
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Go through each of the sizes and use the lower value for system files.
|
||
//
|
||
|
||
if (ScbSnapshot->AllocationSize < NextScb->Header.AllocationSize.QuadPart) {
|
||
|
||
NextScb->Header.AllocationSize.QuadPart = ScbSnapshot->AllocationSize;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
NtfsUnloadNtfsMcbRange( &NextScb->Mcb,
|
||
Int64ShraMod32(NextScb->Header.AllocationSize.QuadPart, NextScb->Vcb->ClusterShift),
|
||
MAXLONGLONG,
|
||
TRUE,
|
||
FALSE );
|
||
|
||
//
|
||
// For the mft set the record allocation context size so that it
|
||
// will be reininitialized the next time its used
|
||
//
|
||
|
||
if (NextScb->Header.NodeTypeCode == NTFS_NTC_SCB_MFT) {
|
||
|
||
NextScb->ScbType.Mft.RecordAllocationContext.CurrentBitmapSize = MAXULONG;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Update the FastIoField.
|
||
//
|
||
|
||
NtfsAcquireFsrtlHeader( NextScb );
|
||
NextScb->Header.IsFastIoPossible = FastIoIsNotPossible;
|
||
NtfsReleaseFsrtlHeader( NextScb );
|
||
|
||
ScbSnapshot = (PSCB_SNAPSHOT)ScbSnapshot->SnapshotLinks.Flink;
|
||
|
||
} while (ScbSnapshot != &IrpContext->ScbSnapshot);
|
||
|
||
//
|
||
// It is a rare path where the user is extending a volume and has hit
|
||
// an exception. In that case we need to roll back the total clusters
|
||
// in the Vcb. We detect this case is possible by comparing the
|
||
// snapshot value for total clusters in the Vcb.
|
||
//
|
||
|
||
if ((Vcb != NULL) && (Vcb->TotalClusters != Vcb->PreviousTotalClusters)) {
|
||
|
||
//
|
||
// Someone is changing this value but is it us.
|
||
//
|
||
|
||
if ((Vcb->BitmapScb != NULL) &&
|
||
NtfsIsExclusiveScb( Vcb->BitmapScb )) {
|
||
|
||
Vcb->TotalClusters = Vcb->PreviousTotalClusters;
|
||
}
|
||
}
|
||
|
||
//ASSERTMSG( "***Failed to abort transaction, volume is corrupt", FALSE );
|
||
|
||
//
|
||
// If this is part of a transaction delete the transaction and then
|
||
// clear the transaction Id in the IrpContext to make sure we don't
|
||
// try to write any log records in the complete request.
|
||
//
|
||
|
||
if (IrpContext->TransactionId) {
|
||
|
||
NtfsAcquireExclusiveRestartTable( &Vcb->TransactionTable,
|
||
TRUE );
|
||
|
||
TransactionEntry = (PTRANSACTION_ENTRY)GetRestartEntryFromIndex(
|
||
&Vcb->TransactionTable,
|
||
IrpContext->TransactionId );
|
||
|
||
//
|
||
// We may leak a little reservation here - that will be recoverved at
|
||
// clean checkpoints
|
||
//
|
||
|
||
NtfsFreeRestartTableIndex( &Vcb->TransactionTable,
|
||
IrpContext->TransactionId );
|
||
|
||
//
|
||
// Mark that there is no transaction for the irp and signal
|
||
// any waiters if there are no transactions left
|
||
//
|
||
|
||
if (Vcb->TransactionTable.Table->NumberAllocated == 0) {
|
||
|
||
KeSetEvent( &Vcb->TransactionsDoneEvent, 0, FALSE );
|
||
}
|
||
|
||
NtfsReleaseRestartTable( &Vcb->TransactionTable );
|
||
|
||
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 )) {
|
||
|
||
//
|
||
// Tracking where a recursive error occurs during a file rename
|
||
//
|
||
|
||
ASSERT( (IrpContext->TopLevelIrpContext->MajorFunction != IRP_MJ_SET_INFORMATION) ||
|
||
(IoGetCurrentIrpStackLocation( IrpContext->TopLevelIrpContext->OriginatingIrp )->Parameters.SetFile.FileInformationClass != FileRenameInformation) );
|
||
|
||
if (ExceptionCode == STATUS_FILE_LOCK_CONFLICT) {
|
||
|
||
IrpContext->TopLevelIrpContext->ExceptionStatus = STATUS_CANT_WAIT;
|
||
|
||
} else {
|
||
|
||
IrpContext->TopLevelIrpContext->ExceptionStatus = ExceptionCode;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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 we are top level, we will either post it or retry. Also, make
|
||
// sure we always take this path for close, because we should never delete
|
||
// his IrpContext.
|
||
//
|
||
|
||
if (NtfsIsTopLevelRequest( IrpContext ) || (IrpContext->MajorFunction == IRP_MJ_CLOSE)) {
|
||
|
||
//
|
||
// 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 {
|
||
|
||
if (ExceptionCode == STATUS_LOG_FILE_FULL) {
|
||
|
||
//
|
||
// Increment the unhandled logfile full count and restart area at this
|
||
// point for tracking with dummy checkpoints
|
||
//
|
||
|
||
Vcb->UnhandledLogFileFullCount += 1;
|
||
|
||
//
|
||
// Unsafe test of value before recording the current restart area
|
||
// if this is the first logfull. We'd prefer an extra comparison
|
||
// over an interlocked operation.
|
||
//
|
||
|
||
if (Vcb->LastRestartAreaAtNonTopLevelLogFull.QuadPart == Li0.QuadPart) {
|
||
InterlockedCompareExchange64( &Vcb->LastRestartAreaAtNonTopLevelLogFull.QuadPart,
|
||
Vcb->LastRestartArea.QuadPart,
|
||
Li0.QuadPart );
|
||
|
||
}
|
||
|
||
//
|
||
// 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 ((IrpContext->TopLevelIrpContext == IrpContext) &&
|
||
!FlagOn( Vcb->CheckpointFlags, VCB_DUMMY_CHECKPOINT_POSTED ) &&
|
||
!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ONLY_SYNCH_CHECKPOINT )) {
|
||
|
||
//
|
||
// If this is the lazy writer then we will just set a flag
|
||
// in the top level field to signal ourselves to perform
|
||
// the clean checkpoint when the cache manager releases
|
||
// the Scb.
|
||
//
|
||
|
||
if (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_LAZY_WRITE ) &&
|
||
(NtfsGetTopLevelContext()->SavedTopLevelIrp == (PIRP) FSRTL_CACHE_TOP_LEVEL_IRP)) {
|
||
|
||
SetFlag( (ULONG_PTR) NtfsGetTopLevelContext()->SavedTopLevelIrp, 0x80000000 );
|
||
|
||
} else if (!FlagOn( 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 = NULL;
|
||
NtfsInitializeIrpContext( NULL, TRUE, &PostIrpContext );
|
||
PostIrpContext->Vcb = Vcb;
|
||
PostIrpContext->LastRestartArea = PostIrpContext->Vcb->LastRestartArea;
|
||
|
||
#ifdef PERF_STATS
|
||
PostIrpContext->LogFullReason = IrpContext->LogFullReason;
|
||
#endif
|
||
|
||
NtfsAcquireCheckpoint( IrpContext, Vcb );
|
||
if (!FlagOn( Vcb->CheckpointFlags, VCB_DUMMY_CHECKPOINT_POSTED )) {
|
||
SetFlag( Vcb->CheckpointFlags, VCB_DUMMY_CHECKPOINT_POSTED );
|
||
} else {
|
||
NtfsCleanupIrpContext( PostIrpContext, FALSE );
|
||
PostIrpContext = NULL;
|
||
}
|
||
NtfsReleaseCheckpoint( 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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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) {
|
||
|
||
//
|
||
// Cleanup but don't delete the IrpContext. Don't delete the Irp.
|
||
//
|
||
|
||
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DONT_DELETE );
|
||
Irp = NULL;
|
||
|
||
//
|
||
// Clear the status code in the IrpContext, because we'll be retrying.
|
||
//
|
||
|
||
IrpContext->ExceptionStatus = 0;
|
||
|
||
//
|
||
// If this is a create then sometimes we want to complete the Irp. Otherwise
|
||
// save the Irp. Save the Irp by clearing it here.
|
||
//
|
||
|
||
} else if ((IrpContext != NULL) &&
|
||
!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_IN_FSP ) &&
|
||
FlagOn( IrpContext->State, IRP_CONTEXT_STATE_EFS_CREATE )) {
|
||
|
||
Irp = NULL;
|
||
}
|
||
|
||
//
|
||
// If this is an Mdl write complete request which we are completing,
|
||
// then take care of the Mdl here so that things get cleaned up properly.
|
||
// Cc owns the mdl and needs to clean it up rather than i/o. For example
|
||
// it may be unlocked already. Note: if there is still an irp there must
|
||
// still also be an irpcontext
|
||
//
|
||
|
||
if ((Irp != NULL) &&
|
||
(IrpContext->MajorFunction == IRP_MJ_WRITE) &&
|
||
(FlagOn( IrpContext->MinorFunction, IRP_MN_MDL | IRP_MN_COMPLETE ) == (IRP_MN_MDL | IRP_MN_COMPLETE)) &&
|
||
(Irp->MdlAddress != NULL)) {
|
||
|
||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
|
||
CcMdlWriteAbort( IrpSp->FileObject, Irp->MdlAddress );
|
||
Irp->MdlAddress = NULL;
|
||
}
|
||
|
||
NtfsCompleteRequest( IrpContext, Irp, 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:
|
||
|
||
IrpContext - Supplies the IrpContext being completed
|
||
|
||
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 we have posted any Usn journal changes, then they better be written,
|
||
// because commit is not supposed to fail for log file full.
|
||
//
|
||
|
||
ASSERT( (IrpContext->Usn.NewReasons == 0) &&
|
||
(IrpContext->Usn.RemovedSourceInfo == 0) );
|
||
|
||
if (IrpContext->TransactionId != 0) {
|
||
NtfsCommitCurrentTransaction( IrpContext );
|
||
}
|
||
|
||
//
|
||
// Always store the status in the top level Irp Context unless
|
||
// there is already an error code.
|
||
//
|
||
|
||
if ((IrpContext != IrpContext->TopLevelIrpContext) &&
|
||
NT_SUCCESS( IrpContext->TopLevelIrpContext->ExceptionStatus )) {
|
||
|
||
IrpContext->TopLevelIrpContext->ExceptionStatus = Status;
|
||
}
|
||
|
||
NtfsCleanupIrpContext( IrpContext, TRUE );
|
||
}
|
||
|
||
//
|
||
// If we have an Irp then complete the irp.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( Irp )) {
|
||
|
||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
PSCB Scb = NULL;
|
||
|
||
if (IrpSp->FileObject) {
|
||
Scb = (PSCB) IrpSp->FileObject->FsContext;
|
||
}
|
||
|
||
ASSERT_IRP( Irp );
|
||
|
||
if (NT_ERROR( Status ) &&
|
||
FlagOn( Irp->Flags, IRP_INPUT_OPERATION )) {
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
}
|
||
|
||
Irp->IoStatus.Status = Status;
|
||
|
||
#ifdef NTFS_RWC_DEBUG
|
||
ASSERT( (Status != STATUS_FILE_LOCK_CONFLICT) ||
|
||
(IrpSp->MajorFunction != IRP_MJ_READ) ||
|
||
!FlagOn( Irp->Flags, IRP_PAGING_IO ));
|
||
#endif
|
||
|
||
//
|
||
// Update counter for any failed paging file reads or writes
|
||
//
|
||
|
||
if (((IrpSp->MajorFunction == IRP_MJ_READ) ||
|
||
(IrpSp->MajorFunction == IRP_MJ_WRITE)) &&
|
||
|
||
(Status == STATUS_INSUFFICIENT_RESOURCES) &&
|
||
|
||
(Scb != NULL) &&
|
||
|
||
FlagOn( Scb->Fcb->FcbState, FCB_STATE_PAGING_FILE)) {
|
||
|
||
ASSERTMSG( "NTFS: Failed pagingfile read for insufficient resources\n", FALSE );
|
||
|
||
NtfsFailedPagingFileOps++;
|
||
}
|
||
|
||
//
|
||
// Update counter for any failed paging reads
|
||
//
|
||
|
||
if ((IrpSp->MajorFunction == IRP_MJ_READ) &&
|
||
(Status == STATUS_INSUFFICIENT_RESOURCES) &&
|
||
FlagOn( Irp->Flags, IRP_PAGING_IO )) {
|
||
|
||
NtfsFailedPagingReads++;
|
||
}
|
||
|
||
ASSERT( (IrpSp->MajorFunction != IRP_MJ_READ) ||
|
||
(Status != STATUS_INSUFFICIENT_RESOURCES) ||
|
||
(Scb == NULL) ||
|
||
(!FlagOn( Scb->Fcb->FcbState, FCB_STATE_PAGING_FILE )) );
|
||
|
||
//
|
||
// We should never return STATUS_CANT_WAIT on a create.
|
||
//
|
||
|
||
ASSERT( (Status != STATUS_CANT_WAIT ) ||
|
||
(IrpSp->MajorFunction != IRP_MJ_CREATE) );
|
||
|
||
#ifdef LFS_CLUSTER_CHECK
|
||
|
||
ASSERT( (IrpSp->MajorFunction != IRP_MJ_FILE_SYSTEM_CONTROL) ||
|
||
(IrpSp->MinorFunction != IRP_MN_USER_FS_REQUEST) ||
|
||
(IrpSp->Parameters.FileSystemControl.FsControlCode != FSCTL_DISMOUNT_VOLUME) ||
|
||
((Status != STATUS_NO_SUCH_DEVICE) &&
|
||
(Status != STATUS_DEVICE_BUSY) &&
|
||
(Status != STATUS_DEVICE_OFF_LINE)) );
|
||
#endif
|
||
//
|
||
// Check if this status is the status we are watching for.
|
||
//
|
||
|
||
if (NtfsTestStatus && (NtfsTestStatusCode == Status)) {
|
||
|
||
NtfsTestStatusProc();
|
||
}
|
||
|
||
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
|
||
}
|
||
|
||
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;
|
||
PFCB Fcb;
|
||
|
||
LARGE_INTEGER LargeLength;
|
||
ULONG Extend, Overwrite;
|
||
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
UNREFERENCED_PARAMETER( IoStatus );
|
||
UNREFERENCED_PARAMETER( Wait );
|
||
|
||
PAGED_CODE();
|
||
|
||
#ifdef NTFS_NO_FASTIO
|
||
|
||
UNREFERENCED_PARAMETER( FileObject );
|
||
UNREFERENCED_PARAMETER( FileOffset );
|
||
UNREFERENCED_PARAMETER( Length );
|
||
UNREFERENCED_PARAMETER( LockKey );
|
||
UNREFERENCED_PARAMETER( CheckForReadOperation );
|
||
|
||
return FALSE;
|
||
|
||
#endif
|
||
|
||
//
|
||
// Decode the file object to get our fcb, the only one we want
|
||
// to deal with is a UserFileOpen
|
||
//
|
||
|
||
#ifdef COMPRESS_ON_WIRE
|
||
if (((Scb = NtfsFastDecodeUserFileOpen( FileObject )) == NULL) ||
|
||
((Scb->NonpagedScb->SegmentObjectC.DataSectionObject != NULL) && (Scb->Header.FileObjectC == NULL))) {
|
||
|
||
return FALSE;
|
||
}
|
||
#else
|
||
if ((Scb = NtfsFastDecodeUserFileOpen( FileObject )) == NULL) {
|
||
|
||
return FALSE;
|
||
}
|
||
#endif
|
||
|
||
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 {
|
||
|
||
ULONG Reason = 0;
|
||
|
||
Overwrite = (FileOffset->QuadPart < Scb->Header.FileSize.QuadPart);
|
||
Extend = ((FileOffset->QuadPart + Length) > Scb->Header.FileSize.QuadPart);
|
||
|
||
if ((Scb->ScbType.Data.FileLock == NULL) ||
|
||
FsRtlFastCheckLockForWrite( Scb->ScbType.Data.FileLock,
|
||
FileOffset,
|
||
&LargeLength,
|
||
LockKey,
|
||
FileObject,
|
||
PsGetCurrentProcess() )) {
|
||
|
||
//
|
||
// Make sure we don't have to post a Usn change.
|
||
//
|
||
|
||
Fcb = Scb->Fcb;
|
||
NtfsLockFcb( NULL, Fcb );
|
||
if (Fcb->FcbUsnRecord != NULL) {
|
||
Reason = Fcb->FcbUsnRecord->UsnRecord.Reason;
|
||
}
|
||
NtfsUnlockFcb( NULL, Fcb );
|
||
|
||
if (((Scb->AttributeName.Length != 0) ?
|
||
((!Overwrite || FlagOn(Reason, USN_REASON_NAMED_DATA_OVERWRITE)) &&
|
||
(!Extend || FlagOn(Reason, USN_REASON_NAMED_DATA_EXTEND))) :
|
||
((!Overwrite || FlagOn(Reason, USN_REASON_DATA_OVERWRITE)) &&
|
||
(!Extend || FlagOn(Reason, USN_REASON_DATA_EXTEND)))) &&
|
||
|
||
//
|
||
// If the file is compressed, reserve clusters for it.
|
||
//
|
||
|
||
((Scb->CompressionUnit == 0) ||
|
||
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;
|
||
|
||
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.State, IRP_CONTEXT_STATE_WAIT);
|
||
} else {
|
||
ClearFlag(IrpContext.State, IRP_CONTEXT_STATE_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:
|
||
case UserDirectoryOpen:
|
||
case StreamFileOpen:
|
||
|
||
if (ExAcquireResourceSharedLite( Fcb->Resource, Wait )) {
|
||
|
||
FcbAcquired = TRUE;
|
||
|
||
if (FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED ) ||
|
||
FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) {
|
||
|
||
leave;
|
||
}
|
||
|
||
} else {
|
||
|
||
leave;
|
||
}
|
||
|
||
NtfsFillBasicInfo( Buffer, Scb );
|
||
Results = TRUE;
|
||
|
||
IoStatus->Information = sizeof(FILE_BASIC_INFORMATION);
|
||
|
||
IoStatus->Status = STATUS_SUCCESS;
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
NOTHING;
|
||
}
|
||
|
||
} finally {
|
||
|
||
if (FcbAcquired) { ExReleaseResourceLite( Fcb->Resource ); }
|
||
|
||
FsRtlExitFileSystem();
|
||
}
|
||
|
||
//
|
||
// Return to our caller
|
||
//
|
||
|
||
return Results;
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
}
|
||
|
||
|
||
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;
|
||
|
||
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.State, IRP_CONTEXT_STATE_WAIT);
|
||
} else {
|
||
ClearFlag(IrpContext.State, IRP_CONTEXT_STATE_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:
|
||
case UserDirectoryOpen:
|
||
case StreamFileOpen:
|
||
|
||
if (Scb->Header.PagingIoResource != NULL) {
|
||
ExAcquireResourceSharedLite( Scb->Header.PagingIoResource, TRUE );
|
||
}
|
||
|
||
FsRtlLockFsRtlHeader( &Scb->Header );
|
||
FsRtlHeaderLocked = TRUE;
|
||
|
||
if (ExAcquireResourceSharedLite( Fcb->Resource, Wait )) {
|
||
|
||
FcbAcquired = TRUE;
|
||
|
||
if (FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED ) ||
|
||
FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) {
|
||
|
||
leave;
|
||
}
|
||
|
||
} else {
|
||
|
||
leave;
|
||
}
|
||
|
||
//
|
||
// 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 {
|
||
|
||
NtfsFillStandardInfo( Buffer, Scb, Ccb );
|
||
|
||
IoStatus->Information = sizeof(FILE_STANDARD_INFORMATION);
|
||
|
||
IoStatus->Status = STATUS_SUCCESS;
|
||
|
||
Results = TRUE;
|
||
}
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
NOTHING;
|
||
}
|
||
|
||
} finally {
|
||
|
||
if (FcbAcquired) { ExReleaseResourceLite( Fcb->Resource ); }
|
||
|
||
if (FsRtlHeaderLocked) {
|
||
FsRtlUnlockFsRtlHeader( &Scb->Header );
|
||
if (Scb->Header.PagingIoResource != NULL) {
|
||
ExReleaseResourceLite( Scb->Header.PagingIoResource );
|
||
}
|
||
}
|
||
|
||
FsRtlExitFileSystem();
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
return Results;
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
}
|
||
|
||
|
||
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;
|
||
|
||
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.State, IRP_CONTEXT_STATE_WAIT);
|
||
} else {
|
||
ClearFlag(IrpContext.State, IRP_CONTEXT_STATE_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:
|
||
case UserDirectoryOpen:
|
||
case StreamFileOpen:
|
||
|
||
if (ExAcquireResourceSharedLite( Fcb->Resource, Wait )) {
|
||
|
||
FcbAcquired = TRUE;
|
||
|
||
if (FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED ) ||
|
||
FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED ) ||
|
||
(!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED) &&
|
||
(Scb->AttributeTypeCode != $INDEX_ALLOCATION))) {
|
||
|
||
leave;
|
||
}
|
||
|
||
} else {
|
||
|
||
leave;
|
||
}
|
||
|
||
NtfsFillNetworkOpenInfo( Buffer, Scb );
|
||
IoStatus->Information = sizeof(FILE_NETWORK_OPEN_INFORMATION);
|
||
|
||
IoStatus->Status = STATUS_SUCCESS;
|
||
|
||
Results = TRUE;
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
NOTHING;
|
||
}
|
||
|
||
} finally {
|
||
|
||
if (FcbAcquired) { ExReleaseResourceLite( Fcb->Resource ); }
|
||
|
||
FsRtlExitFileSystem();
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
return Results;
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
}
|
||
|
||
|
||
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.State, IRP_CONTEXT_STATE_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);
|
||
|
||
if (TypeOfOpen == UnopenedFileObject) {
|
||
|
||
ExRaiseStatus( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
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.State, IRP_CONTEXT_STATE_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;
|
||
}
|
||
}
|
||
|
||
|
||
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 = 0;
|
||
|
||
PFILE_OBJECT FileObject;
|
||
|
||
WCHAR *NewBuffer = NULL;
|
||
|
||
PIRP Irp = NULL;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PUNICODE_STRING FileName = NULL;
|
||
PUNICODE_STRING RelatedFileName = NULL;
|
||
|
||
BOOLEAN UseLcb = FALSE;
|
||
PVOLUME_ERROR_PACKET VolumeErrorPacket = NULL;
|
||
ULONG OldCount;
|
||
|
||
//
|
||
// Return if there is no originating Irp, for example when originating
|
||
// from NtfsPerformHotFix or if the originating irp type doesn't match an irp
|
||
//
|
||
|
||
if ((IrpContext->OriginatingIrp == NULL) ||
|
||
(IrpContext->OriginatingIrp->Type != IO_TYPE_IRP)) {
|
||
return;
|
||
}
|
||
|
||
Irp = IrpContext->OriginatingIrp;
|
||
IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );
|
||
FileObject = IrpSp->FileObject;
|
||
|
||
//
|
||
// 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 assume the error occurred in a system file.
|
||
// if its fileref is outside of this range default to $MFT
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT( Fcb )) {
|
||
|
||
if (ARGUMENT_PRESENT( FileReference )) {
|
||
if (NtfsSegmentNumber( FileReference ) <= UPCASE_TABLE_NUMBER) {
|
||
FileName = (PUNICODE_STRING)(&(NtfsSystemFiles[NtfsSegmentNumber( FileReference )]));
|
||
} else {
|
||
FileName = (PUNICODE_STRING)(&(NtfsSystemFiles[0]));
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the name has an Lcb, we will contruct a name with a chain of Lcb's.
|
||
//
|
||
|
||
} else if (!IsListEmpty( &Fcb->LcbQueue )) {
|
||
|
||
UseLcb = TRUE;
|
||
|
||
//
|
||
// Check if this is a system file.
|
||
//
|
||
|
||
} else if (NtfsSegmentNumber( &Fcb->FileReference ) < FIRST_USER_FILE_NUMBER) {
|
||
|
||
|
||
if (NtfsSegmentNumber( &Fcb->FileReference ) <= UPCASE_TABLE_NUMBER) {
|
||
FileName = (PUNICODE_STRING)(&(NtfsSystemFiles[NtfsSegmentNumber( &Fcb->FileReference )]));
|
||
} else {
|
||
FileName = (PUNICODE_STRING)(&(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
|
||
&& FlagOn( IrpSp->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID ))
|
||
|| (FileObject->FileName.Length == 0
|
||
&& (FileObject->RelatedFileObject == NULL
|
||
|| IrpContext->MajorFunction != IRP_MJ_CREATE))) {
|
||
|
||
FileName = (PUNICODE_STRING)&(NtfsUnknownFile);
|
||
//
|
||
// If there is a valid name in the file object we use that.
|
||
//
|
||
|
||
} else if ((FileObject->FileName.Length != 0) &&
|
||
(FileObject->FileName.Buffer[0] == L'\\')) {
|
||
|
||
FileName = &(FileObject->FileName);
|
||
//
|
||
// We have to construct the name from filename + related fileobject.
|
||
//
|
||
|
||
} else {
|
||
|
||
if (FileObject->FileName.Length != 0) {
|
||
FileName = &(FileObject->FileName);
|
||
}
|
||
|
||
if ((FileObject->RelatedFileObject) &&
|
||
(FileObject->RelatedFileObject->FileName.Length != 0)) {
|
||
RelatedFileName = &(FileObject->RelatedFileObject->FileName);
|
||
}
|
||
}
|
||
|
||
if (FileName) {
|
||
NameLength += FileName->Length;
|
||
}
|
||
|
||
if (RelatedFileName) {
|
||
NameLength += RelatedFileName->Length;
|
||
}
|
||
|
||
if (UseLcb) {
|
||
BOOLEAN LeadingBackslash;
|
||
NameLength += NtfsLookupNameLengthViaLcb( Fcb, &LeadingBackslash );
|
||
}
|
||
|
||
//
|
||
// Either append what info we found or default to the volume label
|
||
//
|
||
|
||
if (NameLength > 0) {
|
||
|
||
NewBuffer = NtfsAllocatePool(PagedPool, NameLength );
|
||
Name.Buffer = NewBuffer;
|
||
|
||
//
|
||
// For super long names truncate buffer size to 64k.
|
||
// RtlAppendUnicodeString handles the rest of the work
|
||
//
|
||
|
||
if (NameLength > 0xFFFF) {
|
||
NameLength = 0xFFFF;
|
||
}
|
||
|
||
Name.MaximumLength = (USHORT) NameLength;
|
||
Name.Length = 0;
|
||
|
||
if (RelatedFileName) {
|
||
RtlAppendUnicodeStringToString( &Name, RelatedFileName );
|
||
}
|
||
|
||
if (FileName) {
|
||
RtlAppendUnicodeStringToString( &Name, FileName );
|
||
}
|
||
|
||
if (UseLcb) {
|
||
NtfsFileNameViaLcb( Fcb, NewBuffer, NameLength, NameLength);
|
||
Name.Length = (USHORT) NameLength;
|
||
}
|
||
|
||
} else {
|
||
|
||
Name.Length = Name.MaximumLength = 0;
|
||
Name.Buffer = NULL;
|
||
}
|
||
|
||
//
|
||
// Only allow 1 post to resolve the volume name to occur at a time
|
||
//
|
||
|
||
OldCount = InterlockedCompareExchange( &(NtfsData.VolumeNameLookupsInProgress), 1, 0 );
|
||
if (OldCount == 0) {
|
||
|
||
VolumeErrorPacket = NtfsAllocatePool( PagedPool, sizeof( VOLUME_ERROR_PACKET ) );
|
||
VolumeErrorPacket->Status = Status;
|
||
VolumeErrorPacket->Thread = Thread;
|
||
RtlCopyMemory( &(VolumeErrorPacket->FileName), &Name, sizeof( UNICODE_STRING ) );
|
||
|
||
//
|
||
// Reference the thread to keep it around during the resolveandpost
|
||
//
|
||
|
||
if (Thread) {
|
||
ObReferenceObject( Thread );
|
||
}
|
||
|
||
//
|
||
// Now post to generate the popup. After posting ResolveVolume will free the newbuffer
|
||
//
|
||
|
||
NtfsPostSpecial( IrpContext, IrpContext->Vcb, NtfsResolveVolumeAndRaiseErrorSpecial, VolumeErrorPacket );
|
||
NewBuffer = NULL;
|
||
VolumeErrorPacket = NULL;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Lets use what we have
|
||
//
|
||
|
||
IoRaiseInformationalHardError( Status, &Name, Thread );
|
||
}
|
||
|
||
} finally {
|
||
|
||
//
|
||
// Cleanup any remaining buffers we still own
|
||
//
|
||
|
||
if (NewBuffer) {
|
||
NtfsFreePool( NewBuffer );
|
||
}
|
||
|
||
if (VolumeErrorPacket) {
|
||
|
||
if (VolumeErrorPacket->Thread) {
|
||
ObDereferenceObject( VolumeErrorPacket->Thread );
|
||
}
|
||
NtfsFreePool( VolumeErrorPacket );
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsResolveVolumeAndRaiseErrorSpecial (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN OUT PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Resolve Vcb's win32 devicename and raise an io hard error. This is done in
|
||
a separate thread in order to have enough stack to re-enter the filesys if necc.
|
||
Also because we may reenter. Starting from here means we own no resources other than
|
||
having inc'ed the close count on the underlying vcb to prevent its going away
|
||
|
||
Arguments:
|
||
|
||
IrpContext - IrpContext containing vcb we're interested in
|
||
|
||
Context - String to append to volume win32 name
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
UNICODE_STRING VolumeName;
|
||
NTSTATUS Status;
|
||
PVOLUME_ERROR_PACKET VolumeErrorPacket;
|
||
UNICODE_STRING FullName;
|
||
WCHAR *NewBuffer = NULL;
|
||
ULONG NameLength;
|
||
|
||
ASSERT( Context != NULL );
|
||
ASSERT( IrpContext->Vcb->NodeTypeCode == NTFS_NTC_VCB );
|
||
ASSERT( IrpContext->Vcb->Vpb->RealDevice != NULL );
|
||
|
||
VolumeErrorPacket = (PVOLUME_ERROR_PACKET) Context;
|
||
VolumeName.Length = 0;
|
||
VolumeName.Buffer = NULL;
|
||
|
||
try {
|
||
|
||
//
|
||
// Only use the target device if we haven't stopped it and deref'ed it
|
||
//
|
||
|
||
Status = IoVolumeDeviceToDosName( IrpContext->Vcb->TargetDeviceObject, &VolumeName );
|
||
ASSERT( STATUS_SUCCESS == Status );
|
||
|
||
NameLength = VolumeName.Length + VolumeErrorPacket->FileName.Length;
|
||
|
||
if (NameLength > 0) {
|
||
NewBuffer = NtfsAllocatePool( PagedPool, NameLength );
|
||
FullName.Buffer = NewBuffer;
|
||
|
||
//
|
||
// For super long names truncate buffer size to 64k.
|
||
// RtlAppendUnicodeString handles the rest of the work
|
||
//
|
||
|
||
if (NameLength > 0xFFFF) {
|
||
NameLength = 0xFFFF;
|
||
}
|
||
FullName.MaximumLength = (USHORT) NameLength;
|
||
FullName.Length = 0;
|
||
if (VolumeName.Length) {
|
||
RtlCopyUnicodeString( &FullName, &VolumeName );
|
||
}
|
||
if (VolumeErrorPacket->FileName.Length) {
|
||
RtlAppendUnicodeStringToString( &FullName, &(VolumeErrorPacket->FileName) );
|
||
}
|
||
} else {
|
||
|
||
FullName.MaximumLength = FullName.Length = IrpContext->Vcb->Vpb->VolumeLabelLength;
|
||
FullName.Buffer = (PWCHAR) IrpContext->Vcb->Vpb->VolumeLabel;
|
||
}
|
||
|
||
//
|
||
// Now generate a popup.
|
||
//
|
||
|
||
IoRaiseInformationalHardError( VolumeErrorPacket->Status, &FullName, VolumeErrorPacket->Thread );
|
||
|
||
} finally {
|
||
|
||
//
|
||
// Indicate we're done and other lookups can occur
|
||
//
|
||
|
||
InterlockedDecrement( &(NtfsData.VolumeNameLookupsInProgress) );
|
||
|
||
//
|
||
// deref the thread
|
||
//
|
||
|
||
if (VolumeErrorPacket->Thread) {
|
||
ObDereferenceObject( VolumeErrorPacket->Thread );
|
||
}
|
||
|
||
if (NewBuffer != NULL) {
|
||
NtfsFreePool( NewBuffer );
|
||
}
|
||
|
||
if (VolumeName.Buffer != NULL) {
|
||
NtfsFreePool( VolumeName.Buffer );
|
||
}
|
||
|
||
if (VolumeErrorPacket->FileName.Buffer != NULL) {
|
||
NtfsFreePool( VolumeErrorPacket->FileName.Buffer );
|
||
}
|
||
|
||
if (Context != NULL) {
|
||
NtfsFreePool( VolumeErrorPacket );
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
PTOP_LEVEL_CONTEXT
|
||
NtfsInitializeTopLevelIrp (
|
||
IN PTOP_LEVEL_CONTEXT TopLevelContext,
|
||
IN BOOLEAN ForceTopLevel,
|
||
IN BOOLEAN SetTopLevel
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to initializethe top level context to be used 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 which is to be stored in the local storage and associated with the
|
||
IrpContext for this request. The return value may be the existing stack location
|
||
or a new one for a recursive request. If we will use the new one we will initialize
|
||
it but let our caller actually put it on the stack when the IrpContext is initialized.
|
||
The ThreadIrpContext field in the TopLevelContext indicates if this is already on
|
||
the stack. A NULL value indicates that this is not on the stack yet.
|
||
|
||
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 except it won't be stored on the stack and wont'
|
||
have an IrpContext field.
|
||
|
||
--*/
|
||
|
||
{
|
||
PTOP_LEVEL_CONTEXT CurrentTopLevelContext;
|
||
ULONG_PTR StackBottom;
|
||
ULONG_PTR 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_PTR) CurrentTopLevelContext <= StackBottom - sizeof( TOP_LEVEL_CONTEXT )) &&
|
||
((ULONG_PTR) CurrentTopLevelContext >= StackTop) &&
|
||
!FlagOn( (ULONG_PTR) 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_PTR) CurrentTopLevelContext <= FSRTL_MAX_TOP_LEVEL_IRP_FLAG) {
|
||
|
||
TopLevelRequest = FALSE;
|
||
|
||
} else if (ValidCurrentTopLevel &&
|
||
!FlagOn( CurrentTopLevelContext->ThreadIrpContext->Flags,
|
||
IRP_CONTEXT_FLAG_CALL_SELF )) {
|
||
|
||
TopLevelRequest = FALSE;
|
||
TopLevelNtfs = FALSE;
|
||
|
||
//
|
||
// Handle the case where we have returned FILE_LOCK_CONFLICT to CC and
|
||
// want to perform a clean checkpoint when releasing the resource.
|
||
//
|
||
|
||
} else if ((ULONG_PTR) CurrentTopLevelContext == (0x80000000 | FSRTL_CACHE_TOP_LEVEL_IRP)) {
|
||
|
||
TopLevelRequest = FALSE;
|
||
}
|
||
|
||
//
|
||
// If we are the top level ntfs then initialize the caller's structure.
|
||
// Leave the Ntfs signature and ThreadIrpContext NULL to indicate this is
|
||
// not in the stack yet.
|
||
//
|
||
|
||
if (TopLevelNtfs) {
|
||
|
||
TopLevelContext->Ntfs = 0;
|
||
TopLevelContext->SavedTopLevelIrp = (PIRP) CurrentTopLevelContext;
|
||
TopLevelContext->ThreadIrpContext = 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;
|
||
}
|
||
|
||
return TopLevelContext;
|
||
}
|
||
|
||
return CurrentTopLevelContext;
|
||
}
|
||
|
||
|
||
//
|
||
// Non-paged routines to set up and tear down Irps for cancel.
|
||
//
|
||
|
||
BOOLEAN
|
||
NtfsSetCancelRoutine (
|
||
IN PIRP Irp,
|
||
IN PDRIVER_CANCEL CancelRoutine,
|
||
IN ULONG_PTR IrpInformation,
|
||
IN ULONG Async
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to set up an Irp for cancel. We will set the cancel routine
|
||
and initialize the Irp information we use during cancel.
|
||
|
||
Arguments:
|
||
|
||
Irp - This is the Irp we need to set up for cancel.
|
||
|
||
CancelRoutine - This is the cancel routine for this irp.
|
||
|
||
IrpInformation - This is the context information to store in the irp
|
||
for the cancel routine.
|
||
|
||
Async - Indicates if this request is synchronous or asynchronous.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if we initialized the Irp, FALSE if the Irp has already
|
||
been marked cancelled. It will be marked cancelled if the user
|
||
has cancelled the irp before we could put it in the queue.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL Irql;
|
||
|
||
//
|
||
// Assume that the Irp has not been cancelled.
|
||
//
|
||
|
||
IoAcquireCancelSpinLock( &Irql );
|
||
if (!Irp->Cancel) {
|
||
|
||
Irp->IoStatus.Information = (ULONG_PTR) IrpInformation;
|
||
|
||
IoSetCancelRoutine( Irp, CancelRoutine );
|
||
IoReleaseCancelSpinLock( Irql );
|
||
|
||
if (Async) {
|
||
|
||
IoMarkIrpPending( Irp );
|
||
}
|
||
|
||
return TRUE;
|
||
|
||
} else {
|
||
|
||
IoReleaseCancelSpinLock( Irql );
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
BOOLEAN
|
||
NtfsClearCancelRoutine (
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to clear an Irp from cancel. It is called when Ntfs is
|
||
internally ready to continue processing the Irp. We need to know if cancel
|
||
has already been called on this Irp. In that case we allow the cancel routine
|
||
to complete the Irp.
|
||
|
||
Arguments:
|
||
|
||
Irp - This is the Irp we want to process further.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if we can proceed with processing the Irp, FALSE if the cancel
|
||
routine will process the Irp.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL Irql;
|
||
|
||
IoAcquireCancelSpinLock( &Irql );
|
||
|
||
//
|
||
// Check if the cancel routine has been called.
|
||
//
|
||
|
||
if (IoSetCancelRoutine( Irp, NULL ) == NULL) {
|
||
|
||
//
|
||
// Let our cancel routine handle the Irp.
|
||
//
|
||
|
||
IoReleaseCancelSpinLock( Irql );
|
||
return FALSE;
|
||
|
||
} else {
|
||
|
||
IoReleaseCancelSpinLock( Irql );
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtfsFsdDispatchWait (
|
||
IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the driver entry to most of the NTFS Fsd dispatch points.
|
||
IrpContext is initialized on the stack and passed from here.
|
||
|
||
Arguments:
|
||
|
||
VolumeDeviceObject - Supplies the volume device object for this request
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The FSD status for the IRP
|
||
|
||
--*/
|
||
|
||
{
|
||
IRP_CONTEXT LocalIrpContext;
|
||
NTSTATUS Status;
|
||
|
||
Status = NtfsFsdDispatchSwitch( &LocalIrpContext, Irp, TRUE );
|
||
|
||
//
|
||
// If we ever catch ourselves using an IrpContext of this
|
||
// type, we know we are doing something wrong.
|
||
//
|
||
|
||
LocalIrpContext.NodeTypeCode = (NODE_TYPE_CODE)-1;
|
||
|
||
return Status;
|
||
|
||
UNREFERENCED_PARAMETER( VolumeDeviceObject );
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtfsFsdDispatch (
|
||
IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the driver entry to NTFS Fsd dispatch IRPs that may
|
||
or may not be synchronous.
|
||
|
||
Arguments:
|
||
|
||
VolumeDeviceObject - Supplies the volume device object for this request
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The FSD status for the IRP
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
//
|
||
// We'd rather create the IrpContext on the stack.
|
||
//
|
||
|
||
if (CanFsdWait( Irp )) {
|
||
|
||
return NtfsFsdDispatchWait( VolumeDeviceObject, Irp );
|
||
|
||
} else {
|
||
|
||
return NtfsFsdDispatchSwitch( NULL, Irp, FALSE );
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
NTSTATUS
|
||
NtfsFsdDispatchSwitch (
|
||
IN PIRP_CONTEXT StackIrpContext OPTIONAL,
|
||
IN PIRP Irp,
|
||
IN BOOLEAN Wait
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common switch for all the FsdEntry points
|
||
that don't need special pre-processing. This simply initializes
|
||
the IrpContext and calls the 'Common*' code.
|
||
|
||
Arguments:
|
||
|
||
VolumeDeviceObject - Supplies the volume device object for this request
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
Wait - Can this request be posted or not?
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The FSD status for the IRP
|
||
|
||
--*/
|
||
|
||
{
|
||
TOP_LEVEL_CONTEXT TopLevelContext;
|
||
PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
|
||
PIRP_CONTEXT IrpContext = NULL;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
ASSERT_IRP( Irp );
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, ("NtfsFsdDispatch\n") );
|
||
|
||
//
|
||
// Call the common query Information routine
|
||
//
|
||
|
||
FsRtlEnterFileSystem();
|
||
|
||
//
|
||
// Always make these requests look top level.
|
||
//
|
||
|
||
ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, FALSE, FALSE );
|
||
|
||
do {
|
||
|
||
try {
|
||
|
||
//
|
||
// We are either initiating this request or retrying it.
|
||
//
|
||
|
||
if (IrpContext == NULL) {
|
||
|
||
//
|
||
// The optional IrpContext could reside on the caller's stack.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( StackIrpContext )) {
|
||
|
||
IrpContext = StackIrpContext;
|
||
}
|
||
|
||
NtfsInitializeIrpContext( Irp, Wait, &IrpContext );
|
||
|
||
//
|
||
// Initialize the thread top level structure, if needed.
|
||
//
|
||
|
||
NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
|
||
|
||
} else if (Status == STATUS_LOG_FILE_FULL) {
|
||
|
||
NtfsCheckpointForLogFileFull( IrpContext );
|
||
}
|
||
|
||
|
||
switch (IrpContext->MajorFunction) {
|
||
|
||
case IRP_MJ_QUERY_EA:
|
||
|
||
Status = NtfsCommonQueryEa( IrpContext, Irp );
|
||
break;
|
||
|
||
case IRP_MJ_SET_EA:
|
||
|
||
Status = NtfsCommonSetEa( IrpContext, Irp );
|
||
break;
|
||
|
||
case IRP_MJ_QUERY_QUOTA:
|
||
|
||
Status = NtfsCommonQueryQuota( IrpContext, Irp );
|
||
break;
|
||
|
||
case IRP_MJ_SET_QUOTA:
|
||
|
||
Status = NtfsCommonSetQuota( IrpContext, Irp );
|
||
break;
|
||
|
||
case IRP_MJ_DEVICE_CONTROL:
|
||
|
||
Status = NtfsCommonDeviceControl( IrpContext, Irp );
|
||
break;
|
||
|
||
case IRP_MJ_QUERY_INFORMATION:
|
||
|
||
Status = NtfsCommonQueryInformation( IrpContext, Irp );
|
||
break;
|
||
|
||
case IRP_MJ_QUERY_SECURITY:
|
||
|
||
Status = NtfsCommonQuerySecurityInfo( IrpContext, Irp );
|
||
break;
|
||
|
||
case IRP_MJ_SET_SECURITY:
|
||
|
||
Status = NtfsCommonSetSecurityInfo( IrpContext, Irp );
|
||
break;
|
||
|
||
case IRP_MJ_QUERY_VOLUME_INFORMATION:
|
||
|
||
Status = NtfsCommonQueryVolumeInfo( IrpContext, Irp );
|
||
break;
|
||
|
||
case IRP_MJ_SET_VOLUME_INFORMATION:
|
||
|
||
Status = NtfsCommonSetVolumeInfo( IrpContext, Irp );
|
||
break;
|
||
|
||
default:
|
||
|
||
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
NtfsCompleteRequest( IrpContext, Irp, Status );
|
||
ASSERT(FALSE);
|
||
break;
|
||
}
|
||
|
||
break;
|
||
|
||
} except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
|
||
|
||
//
|
||
// We had some trouble trying to perform the requested
|
||
// operation, so we'll abort the I/O request with
|
||
// the error status that we get back from the
|
||
// execption code
|
||
//
|
||
|
||
Status = NtfsProcessException( IrpContext, Irp, GetExceptionCode() );
|
||
}
|
||
|
||
} while (Status == STATUS_CANT_WAIT ||
|
||
Status == STATUS_LOG_FILE_FULL);
|
||
|
||
ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
|
||
FsRtlExitFileSystem();
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsFsdDispatch -> %08lx\n", Status) );
|
||
|
||
return Status;
|
||
}
|
||
|
||
#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 9
|
||
|
||
typedef struct _BACKTRACE
|
||
{
|
||
ULONG State;
|
||
ULONG Size;
|
||
PVOID Allocate[BACKTRACE_DEPTH];
|
||
PVOID Free[BACKTRACE_DEPTH];
|
||
} BACKTRACE, *PBACKTRACE;
|
||
|
||
|
||
#define STATE_ALLOCATED 'M'
|
||
#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))
|
||
|
||
ULONG NtfsDebugTotalPoolAllocated = 0;
|
||
ULONG NtfsDebugCountAllocated = 0;
|
||
ULONG NtfsDebugSnapshotTotal = 0;
|
||
ULONG NtfsDebugSnapshotCount = 0;
|
||
|
||
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) );
|
||
if (RtlCaptureStackBackTrace( 0, BACKTRACE_DEPTH, BackTrace->Allocate, &Ignore ) == 0)
|
||
BackTrace->Allocate[0] = (PVOID)-1;
|
||
|
||
BackTrace->State = STATE_ALLOCATED;
|
||
BackTrace->Size = Length;
|
||
|
||
NtfsDebugCountAllocated++;
|
||
NtfsDebugTotalPoolAllocated += Length;
|
||
|
||
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) );
|
||
if (RtlCaptureStackBackTrace( 0, BACKTRACE_DEPTH, BackTrace->Allocate, &Ignore ) == 0)
|
||
BackTrace->Allocate[0] = (PVOID)-1;
|
||
|
||
BackTrace->State = STATE_ALLOCATED;
|
||
BackTrace->Size = Length;
|
||
|
||
NtfsDebugCountAllocated++;
|
||
NtfsDebugTotalPoolAllocated += Length;
|
||
|
||
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( );
|
||
}
|
||
|
||
if (RtlCaptureStackBackTrace( 0, BACKTRACE_DEPTH, BackTrace->Free, &Ignore ) == 0)
|
||
BackTrace->Free[0] = (PVOID)-1;
|
||
|
||
BackTrace->State = STATE_FREE;
|
||
|
||
NtfsDebugCountAllocated--;
|
||
NtfsDebugTotalPoolAllocated -= BackTrace->Size;
|
||
|
||
ExFreePool( BackTrace );
|
||
}
|
||
}
|
||
|
||
VOID
|
||
NtfsDebugHeapDump (
|
||
PUNICODE_STRING UnicodeString )
|
||
{
|
||
|
||
UNREFERENCED_PARAMETER( UnicodeString );
|
||
|
||
DbgPrint( "Cumulative %8x bytes in %8x blocks\n",
|
||
NtfsDebugTotalPoolAllocated, NtfsDebugCountAllocated );
|
||
DbgPrint( "Snapshot %8x bytes in %8x blocks\n",
|
||
NtfsDebugTotalPoolAllocated - NtfsDebugSnapshotTotal,
|
||
NtfsDebugCountAllocated - NtfsDebugSnapshotCount );
|
||
|
||
}
|
||
|
||
#endif
|