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