1403 lines
32 KiB
C
1403 lines
32 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
FatData.c
|
||
|
||
Abstract:
|
||
|
||
This module declares the global data used by the Fat file system.
|
||
|
||
// @@BEGIN_DDKSPLIT
|
||
|
||
Author:
|
||
|
||
Gary Kimura [GaryKi] 28-Dec-1989
|
||
|
||
Revision History:
|
||
|
||
// @@END_DDKSPLIT
|
||
|
||
--*/
|
||
|
||
#include "FatProcs.h"
|
||
|
||
//
|
||
// The Bug check file id for this module
|
||
//
|
||
|
||
#define BugCheckFileId (FAT_BUG_CHECK_FATDATA)
|
||
|
||
//
|
||
// The debug trace level
|
||
//
|
||
|
||
#define Dbg (DEBUG_TRACE_CATCH_EXCEPTIONS)
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
|
||
#if DBG
|
||
#pragma alloc_text(PAGE, FatBugCheckExceptionFilter)
|
||
#endif
|
||
|
||
#pragma alloc_text(PAGE, FatCompleteRequest_Real)
|
||
#pragma alloc_text(PAGE, FatFastIoCheckIfPossible)
|
||
#pragma alloc_text(PAGE, FatFastQueryBasicInfo)
|
||
#pragma alloc_text(PAGE, FatFastQueryNetworkOpenInfo)
|
||
#pragma alloc_text(PAGE, FatFastQueryStdInfo)
|
||
#pragma alloc_text(PAGE, FatIsIrpTopLevel)
|
||
#pragma alloc_text(PAGE, FatPopUpFileCorrupt)
|
||
#pragma alloc_text(PAGE, FatProcessException)
|
||
#endif
|
||
|
||
|
||
//
|
||
// The global fsd data record, and zero large integer
|
||
//
|
||
|
||
FAT_DATA FatData;
|
||
|
||
PDEVICE_OBJECT FatDiskFileSystemDeviceObject;
|
||
PDEVICE_OBJECT FatCdromFileSystemDeviceObject;
|
||
|
||
LARGE_INTEGER FatLargeZero = {0,0};
|
||
LARGE_INTEGER FatMaxLarge = {MAXULONG,MAXLONG};
|
||
|
||
LARGE_INTEGER Fat30Milliseconds = {(ULONG)(-30 * 1000 * 10), -1};
|
||
LARGE_INTEGER Fat100Milliseconds = {(ULONG)(-30 * 1000 * 10), -1};
|
||
LARGE_INTEGER FatOneDay = {0x2a69c000, 0xc9};
|
||
LARGE_INTEGER FatJanOne1980 = {0xe1d58000,0x01a8e79f};
|
||
LARGE_INTEGER FatDecThirtyOne1979 = {0xb76bc000,0x01a8e6d6};
|
||
|
||
FAT_TIME_STAMP FatTimeJanOne1980 = {{0,0,0},{1,1,0}};
|
||
|
||
LARGE_INTEGER FatMagic10000 = {0xe219652c, 0xd1b71758};
|
||
LARGE_INTEGER FatMagic86400000 = {0xfa67b90e, 0xc6d750eb};
|
||
|
||
FAST_IO_DISPATCH FatFastIoDispatch;
|
||
|
||
//
|
||
// Our lookaside lists.
|
||
//
|
||
|
||
NPAGED_LOOKASIDE_LIST FatIrpContextLookasideList;
|
||
NPAGED_LOOKASIDE_LIST FatNonPagedFcbLookasideList;
|
||
NPAGED_LOOKASIDE_LIST FatEResourceLookasideList;
|
||
|
||
SLIST_HEADER FatCloseContextSList;
|
||
|
||
//
|
||
// Synchronization for the close queue
|
||
//
|
||
|
||
FAST_MUTEX FatCloseQueueMutex;
|
||
|
||
//
|
||
// Reserve MDL for paging file operations.
|
||
//
|
||
|
||
PMDL FatReserveMdl = NULL;
|
||
KEVENT FatReserveEvent;
|
||
|
||
#ifdef FASTFATDBG
|
||
|
||
LONG FatDebugTraceLevel = 0x00000009;
|
||
LONG FatDebugTraceIndent = 0;
|
||
|
||
ULONG FatFsdEntryCount = 0;
|
||
ULONG FatFspEntryCount = 0;
|
||
ULONG FatIoCallDriverCount = 0;
|
||
|
||
LONG FatPerformanceTimerLevel = 0x00000000;
|
||
|
||
ULONG FatTotalTicks[32] = { 0 };
|
||
|
||
//
|
||
// I need this because C can't support conditional compilation within
|
||
// a macro.
|
||
//
|
||
|
||
PVOID FatNull = NULL;
|
||
|
||
#endif // FASTFATDBG
|
||
|
||
#if DBG
|
||
|
||
NTSTATUS FatAssertNotStatus = STATUS_SUCCESS;
|
||
BOOLEAN FatTestRaisedStatus = FALSE;
|
||
|
||
#endif
|
||
|
||
|
||
#if DBG
|
||
ULONG
|
||
FatBugCheckExceptionFilter (
|
||
IN PEXCEPTION_POINTERS ExceptionPointer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
An exception filter which acts as an assert that the exception should
|
||
never occur.
|
||
|
||
This is only valid on debug builds, we don't want the overhead on retail.
|
||
|
||
Arguments:
|
||
|
||
ExceptionPointers - The result of GetExceptionInformation() in the context
|
||
of the exception.
|
||
|
||
Return Value:
|
||
|
||
Bugchecks.
|
||
|
||
--*/
|
||
|
||
{
|
||
FatBugCheck( (ULONG_PTR)ExceptionPointer->ExceptionRecord,
|
||
(ULONG_PTR)ExceptionPointer->ContextRecord,
|
||
(ULONG_PTR)ExceptionPointer->ExceptionRecord->ExceptionAddress );
|
||
|
||
return EXCEPTION_EXECUTE_HANDLER;
|
||
}
|
||
#endif
|
||
|
||
|
||
ULONG
|
||
FatExceptionFilter (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
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:
|
||
|
||
ExceptionPointers - The result of GetExceptionInformation() in the context
|
||
of the exception.
|
||
|
||
Return Value:
|
||
|
||
ULONG - returns EXCEPTION_EXECUTE_HANDLER or bugchecks
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS ExceptionCode;
|
||
|
||
ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionCode;
|
||
DebugTrace(0, DEBUG_TRACE_UNWIND, "FatExceptionFilter %X\n", ExceptionCode);
|
||
DebugDump("FatExceptionFilter\n", Dbg, NULL );
|
||
|
||
//
|
||
// If the exception is STATUS_IN_PAGE_ERROR, get the I/O error code
|
||
// from the exception record.
|
||
//
|
||
|
||
if (ExceptionCode == STATUS_IN_PAGE_ERROR) {
|
||
if (ExceptionPointer->ExceptionRecord->NumberParameters >= 3) {
|
||
ExceptionCode = (NTSTATUS)ExceptionPointer->ExceptionRecord->ExceptionInformation[2];
|
||
}
|
||
}
|
||
|
||
//
|
||
// If there is not an irp context, we must have had insufficient resources.
|
||
//
|
||
|
||
if ( !ARGUMENT_PRESENT( IrpContext ) ) {
|
||
|
||
if (!FsRtlIsNtstatusExpected( ExceptionCode )) {
|
||
|
||
FatBugCheck( (ULONG_PTR)ExceptionPointer->ExceptionRecord,
|
||
(ULONG_PTR)ExceptionPointer->ContextRecord,
|
||
(ULONG_PTR)ExceptionPointer->ExceptionRecord->ExceptionAddress );
|
||
}
|
||
|
||
return EXCEPTION_EXECUTE_HANDLER;
|
||
}
|
||
|
||
//
|
||
// For the purposes of processing this exception, let's mark this
|
||
// request as being able to wait and disable write through if we
|
||
// aren't posting it.
|
||
//
|
||
|
||
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
|
||
|
||
if ( (ExceptionCode != STATUS_CANT_WAIT) &&
|
||
(ExceptionCode != STATUS_VERIFY_REQUIRED) ) {
|
||
|
||
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH);
|
||
}
|
||
|
||
if ( IrpContext->ExceptionStatus == 0 ) {
|
||
|
||
if (FsRtlIsNtstatusExpected( ExceptionCode )) {
|
||
|
||
IrpContext->ExceptionStatus = ExceptionCode;
|
||
|
||
return EXCEPTION_EXECUTE_HANDLER;
|
||
|
||
} else {
|
||
|
||
FatBugCheck( (ULONG_PTR)ExceptionPointer->ExceptionRecord,
|
||
(ULONG_PTR)ExceptionPointer->ContextRecord,
|
||
(ULONG_PTR)ExceptionPointer->ExceptionRecord->ExceptionAddress );
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// We raised this code explicitly ourselves, so it had better be
|
||
// expected.
|
||
//
|
||
|
||
ASSERT( IrpContext->ExceptionStatus == ExceptionCode );
|
||
ASSERT( FsRtlIsNtstatusExpected( ExceptionCode ) );
|
||
}
|
||
|
||
return EXCEPTION_EXECUTE_HANDLER;
|
||
}
|
||
|
||
NTSTATUS
|
||
FatProcessException (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp,
|
||
IN NTSTATUS ExceptionCode
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine process an exception. It either completes the request
|
||
with the saved exception status or it sends it off to IoRaiseHardError()
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
ExceptionCode - Supplies the normalized exception status being handled
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Returns the results of either posting the Irp or the
|
||
saved completion status.
|
||
|
||
--*/
|
||
|
||
{
|
||
PVCB Vcb;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
FAT_VOLUME_STATE TransitionState = VolumeDirty;
|
||
ULONG SavedFlags;
|
||
|
||
DebugTrace(0, Dbg, "FatProcessException\n", 0);
|
||
|
||
//
|
||
// If there is not an irp context, we must have had insufficient resources.
|
||
//
|
||
|
||
if ( !ARGUMENT_PRESENT( IrpContext ) ) {
|
||
|
||
FatCompleteRequest( FatNull, Irp, ExceptionCode );
|
||
|
||
return ExceptionCode;
|
||
}
|
||
|
||
//
|
||
// Get the real exception status from IrpContext->ExceptionStatus, and
|
||
// reset it.
|
||
//
|
||
|
||
ExceptionCode = IrpContext->ExceptionStatus;
|
||
FatResetExceptionState( IrpContext );
|
||
|
||
//
|
||
// If we are going to post the request, we may have to lock down the
|
||
// user's buffer, so do it here in a try except so that we failed the
|
||
// request if the LockPages fails.
|
||
//
|
||
// Also unpin any repinned Bcbs, protected by the try {} except {} filter.
|
||
//
|
||
|
||
try {
|
||
|
||
SavedFlags = IrpContext->Flags;
|
||
|
||
//
|
||
// Make sure we don't try to write through Bcbs
|
||
//
|
||
|
||
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH);
|
||
|
||
FatUnpinRepinnedBcbs( IrpContext );
|
||
|
||
IrpContext->Flags = SavedFlags;
|
||
|
||
//
|
||
// If we will have to post the request, do it here. Note
|
||
// that the last thing FatPrePostIrp() does is mark the Irp pending,
|
||
// so it is critical that we actually return PENDING. Nothing
|
||
// from this point to return can fail, so we are OK.
|
||
//
|
||
// We cannot do a verify operations at APC level because we
|
||
// have to wait for Io operations to complete.
|
||
//
|
||
|
||
if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL) &&
|
||
(((ExceptionCode == STATUS_VERIFY_REQUIRED) && (KeGetCurrentIrql() >= APC_LEVEL)) ||
|
||
(ExceptionCode == STATUS_CANT_WAIT))) {
|
||
|
||
ExceptionCode = FatFsdPostRequest( IrpContext, Irp );
|
||
}
|
||
|
||
} except( FatExceptionFilter( IrpContext, GetExceptionInformation() ) ) {
|
||
|
||
ExceptionCode = IrpContext->ExceptionStatus;
|
||
IrpContext->ExceptionStatus = 0;
|
||
|
||
IrpContext->Flags = SavedFlags;
|
||
}
|
||
|
||
//
|
||
// If we posted the request, just return here.
|
||
//
|
||
|
||
if (ExceptionCode == STATUS_PENDING) {
|
||
|
||
return ExceptionCode;
|
||
}
|
||
|
||
Irp->IoStatus.Status = ExceptionCode;
|
||
|
||
//
|
||
// If this request is not a "top-level" irp, just complete it.
|
||
//
|
||
|
||
if (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL)) {
|
||
|
||
//
|
||
// If there is a cache operation above us, commute verify
|
||
// to a lock conflict. This will cause retries so that
|
||
// we have a chance of getting through without needing
|
||
// to return an unaesthetic error for the operation.
|
||
//
|
||
|
||
if (IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP &&
|
||
ExceptionCode == STATUS_VERIFY_REQUIRED) {
|
||
|
||
ExceptionCode = STATUS_FILE_LOCK_CONFLICT;
|
||
}
|
||
|
||
FatCompleteRequest( IrpContext, Irp, ExceptionCode );
|
||
|
||
return ExceptionCode;
|
||
}
|
||
|
||
if (IoIsErrorUserInduced(ExceptionCode)) {
|
||
|
||
//
|
||
// Check for the various error conditions that can be caused by,
|
||
// and possibly resolved by the user.
|
||
//
|
||
|
||
if (ExceptionCode == STATUS_VERIFY_REQUIRED) {
|
||
|
||
PDEVICE_OBJECT Device;
|
||
|
||
DebugTrace(0, Dbg, "Perform Verify Operation\n", 0);
|
||
|
||
//
|
||
// Now we are at the top level file system entry point.
|
||
//
|
||
// Grab the device to verify from the thread local storage
|
||
// and stick it in the information field for transportation
|
||
// to the fsp. We also clear the field at this time.
|
||
//
|
||
|
||
Device = IoGetDeviceToVerify( Irp->Tail.Overlay.Thread );
|
||
IoSetDeviceToVerify( Irp->Tail.Overlay.Thread, NULL );
|
||
|
||
if ( Device == NULL ) {
|
||
|
||
Device = IoGetDeviceToVerify( PsGetCurrentThread() );
|
||
IoSetDeviceToVerify( PsGetCurrentThread(), NULL );
|
||
|
||
ASSERT( Device != NULL );
|
||
}
|
||
|
||
//
|
||
// Let's not BugCheck just because the driver messed up.
|
||
//
|
||
|
||
if (Device == NULL) {
|
||
|
||
ExceptionCode = STATUS_DRIVER_INTERNAL_ERROR;
|
||
|
||
FatCompleteRequest( IrpContext, Irp, ExceptionCode );
|
||
|
||
return ExceptionCode;
|
||
}
|
||
|
||
//
|
||
// FatPerformVerify() will do the right thing with the Irp.
|
||
|
||
return FatPerformVerify( IrpContext, Irp, Device );
|
||
}
|
||
|
||
//
|
||
// The other user induced conditions generate an error unless
|
||
// they have been disabled for this request.
|
||
//
|
||
|
||
if (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS)) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, ExceptionCode );
|
||
|
||
return ExceptionCode;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Generate a pop-up
|
||
//
|
||
|
||
PDEVICE_OBJECT RealDevice;
|
||
PVPB Vpb;
|
||
PETHREAD Thread;
|
||
|
||
if (IoGetCurrentIrpStackLocation(Irp)->FileObject != NULL) {
|
||
|
||
Vpb = IoGetCurrentIrpStackLocation(Irp)->FileObject->Vpb;
|
||
|
||
} else {
|
||
|
||
Vpb = NULL;
|
||
}
|
||
|
||
//
|
||
// The device to verify is either in my thread local storage
|
||
// or that of the thread that owns the Irp.
|
||
//
|
||
|
||
Thread = Irp->Tail.Overlay.Thread;
|
||
RealDevice = IoGetDeviceToVerify( Thread );
|
||
|
||
if ( RealDevice == NULL ) {
|
||
|
||
Thread = PsGetCurrentThread();
|
||
RealDevice = IoGetDeviceToVerify( Thread );
|
||
|
||
ASSERT( RealDevice != NULL );
|
||
}
|
||
|
||
//
|
||
// Let's not BugCheck just because the driver messed up.
|
||
//
|
||
|
||
if (RealDevice == NULL) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, ExceptionCode );
|
||
|
||
return ExceptionCode;
|
||
}
|
||
|
||
//
|
||
// This routine actually causes the pop-up. It usually
|
||
// does this by queuing an APC to the callers thread,
|
||
// but in some cases it will complete the request immediately,
|
||
// so it is very important to IoMarkIrpPending() first.
|
||
//
|
||
|
||
IoMarkIrpPending( Irp );
|
||
IoRaiseHardError( Irp, Vpb, RealDevice );
|
||
|
||
//
|
||
// We will be handing control back to the caller here, so
|
||
// reset the saved device object.
|
||
//
|
||
|
||
IoSetDeviceToVerify( Thread, NULL );
|
||
|
||
//
|
||
// The Irp will be completed by Io or resubmitted. In either
|
||
// case we must clean up the IrpContext here.
|
||
//
|
||
|
||
FatDeleteIrpContext( IrpContext );
|
||
return STATUS_PENDING;
|
||
}
|
||
}
|
||
|
||
//
|
||
// This is just a run of the mill error. If is a STATUS that we
|
||
// raised ourselves, and the information would be use for the
|
||
// user, raise an informational pop-up.
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
Vcb = IrpContext->Vcb;
|
||
|
||
//
|
||
// Now, if the Vcb is unknown to us this means that the error was raised
|
||
// in the process of a mount and before we even had a chance to build
|
||
// a full Vcb - and was really handled there.
|
||
//
|
||
|
||
if (Vcb != NULL) {
|
||
|
||
if ( !FatDeviceIsFatFsdo( IrpSp->DeviceObject) &&
|
||
!NT_SUCCESS(ExceptionCode) &&
|
||
!FsRtlIsTotalDeviceFailure(ExceptionCode) ) {
|
||
|
||
TransitionState = VolumeDirtyWithSurfaceTest;
|
||
}
|
||
|
||
//
|
||
// If this was a STATUS_FILE_CORRUPT or similar error indicating some
|
||
// nastiness out on the media, then mark the volume permanently dirty.
|
||
//
|
||
|
||
if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS) &&
|
||
( TransitionState == VolumeDirtyWithSurfaceTest ||
|
||
(ExceptionCode == STATUS_FILE_CORRUPT_ERROR) ||
|
||
(ExceptionCode == STATUS_DISK_CORRUPT_ERROR) ||
|
||
(ExceptionCode == STATUS_EA_CORRUPT_ERROR) ||
|
||
(ExceptionCode == STATUS_INVALID_EA_NAME) ||
|
||
(ExceptionCode == STATUS_EA_LIST_INCONSISTENT) ||
|
||
(ExceptionCode == STATUS_NO_EAS_ON_FILE) )) {
|
||
|
||
ASSERT( NodeType(Vcb) == FAT_NTC_VCB );
|
||
|
||
SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY );
|
||
|
||
//
|
||
// Do the "dirty" work, ignoring any error.
|
||
//
|
||
|
||
try {
|
||
|
||
FatMarkVolume( IrpContext, Vcb, TransitionState );
|
||
|
||
} except( FatExceptionFilter( IrpContext, GetExceptionInformation() ) ) {
|
||
|
||
NOTHING;
|
||
}
|
||
}
|
||
}
|
||
|
||
FatCompleteRequest( IrpContext, Irp, ExceptionCode );
|
||
|
||
return ExceptionCode;
|
||
}
|
||
|
||
|
||
VOID
|
||
FatCompleteRequest_Real (
|
||
IN PIRP_CONTEXT IrpContext OPTIONAL,
|
||
IN PIRP Irp OPTIONAL,
|
||
IN NTSTATUS Status
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine completes a Irp
|
||
|
||
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.
|
||
//
|
||
|
||
if (IrpContext != NULL) {
|
||
|
||
ASSERT( IrpContext->Repinned.Bcb[0] == NULL );
|
||
|
||
FatUnpinRepinnedBcbs( IrpContext );
|
||
}
|
||
|
||
//
|
||
// Delete the Irp context before completing the IRP so if
|
||
// we run into some of the asserts, we can still backtrack
|
||
// through the IRP.
|
||
//
|
||
|
||
if (IrpContext != NULL) {
|
||
|
||
FatDeleteIrpContext( IrpContext );
|
||
}
|
||
|
||
//
|
||
// If we have an Irp then complete the irp.
|
||
//
|
||
|
||
if (Irp != NULL) {
|
||
|
||
//
|
||
// We got an error, so zero out the information field before
|
||
// completing the request if this was an input operation.
|
||
// Otherwise IopCompleteRequest will try to copy to the user's buffer.
|
||
//
|
||
|
||
if ( NT_ERROR(Status) &&
|
||
FlagOn(Irp->Flags, IRP_INPUT_OPERATION) ) {
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
}
|
||
|
||
Irp->IoStatus.Status = Status;
|
||
|
||
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
BOOLEAN
|
||
FatIsIrpTopLevel (
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine detects if an Irp is the Top level requestor, ie. if it os OK
|
||
to do a verify or pop-up now. If TRUE is returned, then no file system
|
||
resources are held above us.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
Status - Supplies the status to complete the Irp with
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
if ( IoGetTopLevelIrp() == NULL ) {
|
||
|
||
IoSetTopLevelIrp( Irp );
|
||
|
||
return TRUE;
|
||
|
||
} else {
|
||
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
FatFastIoCheckIfPossible (
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
PVCB Vcb;
|
||
PFCB Fcb;
|
||
PCCB Ccb;
|
||
|
||
LARGE_INTEGER LargeLength;
|
||
|
||
//
|
||
// Decode the file object to get our fcb, the only one we want
|
||
// to deal with is a UserFileOpen
|
||
//
|
||
|
||
if (FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ) != UserFileOpen) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
LargeLength.QuadPart = Length;
|
||
|
||
//
|
||
// Based on whether this is a read or write operation we call
|
||
// fsrtl check for read/write
|
||
//
|
||
|
||
if (CheckForReadOperation) {
|
||
|
||
if (FsRtlFastCheckLockForRead( &Fcb->Specific.Fcb.FileLock,
|
||
FileOffset,
|
||
&LargeLength,
|
||
LockKey,
|
||
FileObject,
|
||
PsGetCurrentProcess() )) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Also check for a write-protected volume here.
|
||
//
|
||
|
||
if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED) &&
|
||
FsRtlFastCheckLockForWrite( &Fcb->Specific.Fcb.FileLock,
|
||
FileOffset,
|
||
&LargeLength,
|
||
LockKey,
|
||
FileObject,
|
||
PsGetCurrentProcess() )) {
|
||
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
FatFastQueryBasicInfo (
|
||
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 succeeded 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;
|
||
PCCB Ccb;
|
||
|
||
BOOLEAN FcbAcquired = FALSE;
|
||
|
||
//
|
||
// Prepare the dummy irp context
|
||
//
|
||
|
||
RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );
|
||
IrpContext.NodeTypeCode = FAT_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 and only accept
|
||
// the user file or directory open
|
||
//
|
||
|
||
TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
|
||
|
||
if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen)) {
|
||
|
||
return Results;
|
||
}
|
||
|
||
FsRtlEnterFileSystem();
|
||
|
||
//
|
||
// Get access to the Fcb but only if it is not the paging file
|
||
//
|
||
|
||
if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
|
||
|
||
if (!ExAcquireResourceSharedLite( Fcb->Header.Resource, Wait )) {
|
||
|
||
FsRtlExitFileSystem();
|
||
return Results;
|
||
}
|
||
|
||
FcbAcquired = TRUE;
|
||
}
|
||
|
||
try {
|
||
|
||
//
|
||
// If the Fcb is not in a good state, return FALSE.
|
||
//
|
||
|
||
if (Fcb->FcbCondition != FcbGood) {
|
||
|
||
try_return( Results );
|
||
}
|
||
|
||
Buffer->FileAttributes = 0;
|
||
|
||
//
|
||
// If the fcb is not the root dcb then we will fill in the
|
||
// buffer otherwise it is all setup for us.
|
||
//
|
||
|
||
if (NodeType(Fcb) != FAT_NTC_ROOT_DCB) {
|
||
|
||
//
|
||
// Extract the data and fill in the non zero fields of the output
|
||
// buffer
|
||
//
|
||
|
||
Buffer->LastWriteTime = Fcb->LastWriteTime;
|
||
Buffer->CreationTime = Fcb->CreationTime;
|
||
Buffer->LastAccessTime = Fcb->LastAccessTime;
|
||
|
||
//
|
||
// Zero out the field we don't support.
|
||
//
|
||
|
||
Buffer->ChangeTime.QuadPart = 0;
|
||
Buffer->FileAttributes = Fcb->DirentFatFlags;
|
||
|
||
} else {
|
||
|
||
Buffer->LastWriteTime.QuadPart = 0;
|
||
Buffer->CreationTime.QuadPart = 0;
|
||
Buffer->LastAccessTime.QuadPart = 0;
|
||
Buffer->ChangeTime.QuadPart = 0;
|
||
|
||
Buffer->FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
|
||
}
|
||
|
||
//
|
||
// If the temporary flag is set, then set it in the buffer.
|
||
//
|
||
|
||
if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) {
|
||
|
||
SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
|
||
}
|
||
|
||
//
|
||
// If no attributes were set, set the normal bit.
|
||
//
|
||
|
||
if (Buffer->FileAttributes == 0) {
|
||
|
||
Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
|
||
}
|
||
|
||
IoStatus->Status = STATUS_SUCCESS;
|
||
IoStatus->Information = sizeof(FILE_BASIC_INFORMATION);
|
||
|
||
Results = TRUE;
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
if (FcbAcquired) { ExReleaseResourceLite( Fcb->Header.Resource ); }
|
||
|
||
FsRtlExitFileSystem();
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
return Results;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
FatFastQueryStdInfo (
|
||
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 succeeded 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;
|
||
PCCB Ccb;
|
||
|
||
BOOLEAN FcbAcquired = FALSE;
|
||
|
||
//
|
||
// Prepare the dummy irp context
|
||
//
|
||
|
||
RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );
|
||
IrpContext.NodeTypeCode = FAT_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 and only accept
|
||
// the user file or directory open
|
||
//
|
||
|
||
TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
|
||
|
||
if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen)) {
|
||
|
||
return Results;
|
||
}
|
||
|
||
//
|
||
// Get access to the Fcb but only if it is not the paging file
|
||
//
|
||
|
||
FsRtlEnterFileSystem();
|
||
|
||
if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
|
||
|
||
if (!ExAcquireResourceSharedLite( Fcb->Header.Resource, Wait )) {
|
||
|
||
FsRtlExitFileSystem();
|
||
return Results;
|
||
}
|
||
|
||
FcbAcquired = TRUE;
|
||
}
|
||
|
||
try {
|
||
|
||
//
|
||
// If the Fcb is not in a good state, return FALSE.
|
||
//
|
||
|
||
if (Fcb->FcbCondition != FcbGood) {
|
||
|
||
try_return( Results );
|
||
}
|
||
|
||
Buffer->NumberOfLinks = 1;
|
||
Buffer->DeletePending = BooleanFlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
|
||
|
||
//
|
||
// Case on whether this is a file or a directory, and extract
|
||
// the information and fill in the fcb/dcb specific parts
|
||
// of the output buffer.
|
||
//
|
||
|
||
if (NodeType(Fcb) == FAT_NTC_FCB) {
|
||
|
||
//
|
||
// If we don't alread know the allocation size, we cannot look
|
||
// it up in the fast path.
|
||
//
|
||
|
||
if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
|
||
|
||
try_return( Results );
|
||
}
|
||
|
||
Buffer->AllocationSize = Fcb->Header.AllocationSize;
|
||
Buffer->EndOfFile = Fcb->Header.FileSize;
|
||
|
||
Buffer->Directory = FALSE;
|
||
|
||
} else {
|
||
|
||
Buffer->AllocationSize = FatLargeZero;
|
||
Buffer->EndOfFile = FatLargeZero;
|
||
|
||
Buffer->Directory = TRUE;
|
||
}
|
||
|
||
IoStatus->Status = STATUS_SUCCESS;
|
||
IoStatus->Information = sizeof(FILE_STANDARD_INFORMATION);
|
||
|
||
Results = TRUE;
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
if (FcbAcquired) { ExReleaseResourceLite( Fcb->Header.Resource ); }
|
||
|
||
FsRtlExitFileSystem();
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
return Results;
|
||
}
|
||
|
||
BOOLEAN
|
||
FatFastQueryNetworkOpenInfo (
|
||
IN PFILE_OBJECT FileObject,
|
||
IN BOOLEAN Wait,
|
||
IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is for the fast query call for network open 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 information
|
||
|
||
IoStatus - Receives the final status of the operation
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if the operation succeeded 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;
|
||
PCCB Ccb;
|
||
|
||
BOOLEAN FcbAcquired = FALSE;
|
||
|
||
//
|
||
// Prepare the dummy irp context
|
||
//
|
||
|
||
RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );
|
||
IrpContext.NodeTypeCode = FAT_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 and only accept
|
||
// the user file or directory open
|
||
//
|
||
|
||
TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
|
||
|
||
if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen)) {
|
||
|
||
return Results;
|
||
}
|
||
|
||
FsRtlEnterFileSystem();
|
||
|
||
//
|
||
// Get access to the Fcb but only if it is not the paging file
|
||
//
|
||
|
||
if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
|
||
|
||
if (!ExAcquireResourceSharedLite( Fcb->Header.Resource, Wait )) {
|
||
|
||
FsRtlExitFileSystem();
|
||
return Results;
|
||
}
|
||
|
||
FcbAcquired = TRUE;
|
||
}
|
||
|
||
try {
|
||
|
||
//
|
||
// If the Fcb is not in a good state, return FALSE.
|
||
//
|
||
|
||
if (Fcb->FcbCondition != FcbGood) {
|
||
|
||
try_return( Results );
|
||
}
|
||
|
||
//
|
||
// Extract the data and fill in the non zero fields of the output
|
||
// buffer
|
||
//
|
||
|
||
//
|
||
// Default the field we don't support to a reasonable value.
|
||
//
|
||
|
||
ExLocalTimeToSystemTime( &FatJanOne1980,
|
||
&Buffer->ChangeTime );
|
||
|
||
if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) {
|
||
|
||
//
|
||
// Reuse the default for the root dir.
|
||
//
|
||
|
||
Buffer->CreationTime =
|
||
Buffer->LastAccessTime =
|
||
Buffer->LastWriteTime = Buffer->ChangeTime;
|
||
|
||
} else {
|
||
|
||
Buffer->LastWriteTime = Fcb->LastWriteTime;
|
||
Buffer->CreationTime = Fcb->CreationTime;
|
||
Buffer->LastAccessTime = Fcb->LastAccessTime;
|
||
}
|
||
|
||
Buffer->FileAttributes = Fcb->DirentFatFlags;
|
||
|
||
//
|
||
// If the temporary flag is set, then set it in the buffer.
|
||
//
|
||
|
||
if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) {
|
||
|
||
SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
|
||
}
|
||
|
||
//
|
||
// If no attributes were set, set the normal bit.
|
||
//
|
||
|
||
if (Buffer->FileAttributes == 0) {
|
||
|
||
Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
|
||
}
|
||
|
||
if (NodeType(Fcb) == FAT_NTC_FCB) {
|
||
|
||
//
|
||
// If we don't already know the allocation size, we cannot
|
||
// lock it up in the fast path.
|
||
//
|
||
|
||
if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
|
||
|
||
try_return( Results );
|
||
}
|
||
|
||
Buffer->AllocationSize = Fcb->Header.AllocationSize;
|
||
Buffer->EndOfFile = Fcb->Header.FileSize;
|
||
|
||
} else {
|
||
|
||
Buffer->AllocationSize = FatLargeZero;
|
||
Buffer->EndOfFile = FatLargeZero;
|
||
}
|
||
|
||
IoStatus->Status = STATUS_SUCCESS;
|
||
IoStatus->Information = sizeof(FILE_NETWORK_OPEN_INFORMATION);
|
||
|
||
Results = TRUE;
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
if (FcbAcquired) { ExReleaseResourceLite( Fcb->Header.Resource ); }
|
||
|
||
FsRtlExitFileSystem();
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
return Results;
|
||
}
|
||
|
||
VOID
|
||
FatPopUpFileCorrupt (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The Following routine makes an informational popup that the file
|
||
is corrupt.
|
||
|
||
Arguments:
|
||
|
||
Fcb - The file that is corrupt.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PKTHREAD Thread;
|
||
|
||
//
|
||
// Disable the popup on the root directory. It is important not
|
||
// to generate them on objects which are part of the mount process.
|
||
//
|
||
|
||
if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) {
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Got to grab the full filename now.
|
||
//
|
||
|
||
if (Fcb->FullFileName.Buffer == NULL) {
|
||
|
||
FatSetFullFileNameInFcb( IrpContext, Fcb );
|
||
}
|
||
|
||
//
|
||
// We never want to block a system thread waiting for the user to
|
||
// press OK.
|
||
//
|
||
|
||
if (IoIsSystemThread(IrpContext->OriginatingIrp->Tail.Overlay.Thread)) {
|
||
|
||
Thread = NULL;
|
||
|
||
} else {
|
||
|
||
Thread = IrpContext->OriginatingIrp->Tail.Overlay.Thread;
|
||
}
|
||
|
||
IoRaiseInformationalHardError( STATUS_FILE_CORRUPT_ERROR,
|
||
&Fcb->FullFileName,
|
||
Thread);
|
||
}
|