/*++ 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