6427 lines
163 KiB
C
6427 lines
163 KiB
C
/*++
|
||
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
FsCtrl.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the File System Control routines for Fat called
|
||
by the dispatch driver.
|
||
|
||
Author:
|
||
|
||
Gary Kimura [GaryKi] 28-Dec-1989
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "FatProcs.h"
|
||
|
||
//
|
||
// The Bug check file id for this module
|
||
//
|
||
|
||
#define BugCheckFileId (FAT_BUG_CHECK_FSCTRL)
|
||
|
||
//
|
||
// The local debug trace level
|
||
//
|
||
|
||
#define Dbg (DEBUG_TRACE_FSCTRL)
|
||
|
||
//
|
||
// Local procedure prototypes
|
||
//
|
||
|
||
NTSTATUS
|
||
FatMountVolume (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDEVICE_OBJECT TargetDeviceObject,
|
||
IN PVPB Vpb,
|
||
IN PDSCB Dcsb OPTIONAL
|
||
);
|
||
|
||
NTSTATUS
|
||
FatVerifyVolume (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
BOOLEAN
|
||
FatIsBootSectorFat (
|
||
IN PPACKED_BOOT_SECTOR BootSector
|
||
);
|
||
|
||
NTSTATUS
|
||
FatGetPartitionInfo(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDEVICE_OBJECT TargetDeviceObject,
|
||
IN PPARTITION_INFORMATION PartitionInformation
|
||
);
|
||
|
||
BOOLEAN
|
||
FatIsMediaWriteProtected (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDEVICE_OBJECT TargetDeviceObject
|
||
);
|
||
|
||
NTSTATUS
|
||
FatUserFsCtrl (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FatOplockRequest (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FatLockVolume (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FatUnlockVolume (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FatDismountVolume (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FatDirtyVolume (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FatIsVolumeMounted (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FatIsPathnameValid (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FatInvalidateVolumes (
|
||
IN PIRP Irp
|
||
);
|
||
|
||
BOOLEAN
|
||
FatPerformVerifyDiskRead (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN PVOID Buffer,
|
||
IN LBO Lbo,
|
||
IN ULONG NumberOfBytesToRead,
|
||
IN BOOLEAN ReturnOnError
|
||
);
|
||
|
||
NTSTATUS
|
||
FatMountDblsVolume (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FatAutoMountDblsVolume (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVPB Vpb
|
||
);
|
||
|
||
BOOLEAN
|
||
FatIsAutoMountEnabled (
|
||
IN PIRP_CONTEXT IrpContext
|
||
);
|
||
|
||
NTSTATUS
|
||
FatQueryRetrievalPointers (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FatQueryBpb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FatGetStatistics (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FatAllowExtendedDasdIo (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
#define DOUBLE_SPACE_KEY_NAME L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\DoubleSpace"
|
||
#define DOUBLE_SPACE_VALUE_NAME L"AutomountRemovable"
|
||
|
||
#define KEY_WORK_AREA ((sizeof(KEY_VALUE_FULL_INFORMATION) + \
|
||
sizeof(DOUBLE_SPACE_VALUE_NAME) + \
|
||
sizeof(ULONG)) + 64)
|
||
NTSTATUS
|
||
FatGetDoubleSpaceConfigurationValue(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PUNICODE_STRING ValueName,
|
||
IN OUT PULONG Value
|
||
);
|
||
|
||
//
|
||
// Local support routine prototypes
|
||
//
|
||
|
||
NTSTATUS
|
||
FatGetVolumeBitmap (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FatGetRetrievalPointers (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FatMoveFile (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
VOID
|
||
FatComputeMoveFileSplicePoints (
|
||
PIRP_CONTEXT IrpContext,
|
||
PFCB FcbOrDcb,
|
||
ULONG FileOffset,
|
||
ULONG TargetCluster,
|
||
ULONG BytesToReallocate,
|
||
PULONG FirstSpliceSourceCluster,
|
||
PULONG FirstSpliceTargetCluster,
|
||
PULONG SecondSpliceSourceCluster,
|
||
PULONG SecondSpliceTargetCluster,
|
||
PMCB SourceMcb
|
||
);
|
||
|
||
VOID
|
||
FatComputeMoveFileParameter (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB FcbOrDcb,
|
||
IN ULONG FileOffset,
|
||
IN OUT PULONG ByteCount,
|
||
OUT PULONG BytesToReallocate,
|
||
OUT PULONG BytesToWrite
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, FatCommonFileSystemControl)
|
||
#pragma alloc_text(PAGE, FatDirtyVolume)
|
||
#pragma alloc_text(PAGE, FatFsdFileSystemControl)
|
||
#pragma alloc_text(PAGE, FatGetPartitionInfo)
|
||
#pragma alloc_text(PAGE, FatIsMediaWriteProtected)
|
||
#pragma alloc_text(PAGE, FatIsBootSectorFat)
|
||
#pragma alloc_text(PAGE, FatIsPathnameValid)
|
||
#pragma alloc_text(PAGE, FatIsVolumeMounted)
|
||
#pragma alloc_text(PAGE, FatMountVolume)
|
||
#pragma alloc_text(PAGE, FatOplockRequest)
|
||
#pragma alloc_text(PAGE, FatPerformVerifyDiskRead)
|
||
#pragma alloc_text(PAGE, FatUserFsCtrl)
|
||
#pragma alloc_text(PAGE, FatVerifyVolume)
|
||
#pragma alloc_text(PAGE, FatQueryRetrievalPointers)
|
||
#pragma alloc_text(PAGE, FatDismountVolume)
|
||
#pragma alloc_text(PAGE, FatQueryBpb)
|
||
#pragma alloc_text(PAGE, FatInvalidateVolumes)
|
||
#pragma alloc_text(PAGE, FatGetStatistics)
|
||
#ifdef WE_WON_ON_APPEAL
|
||
#pragma alloc_text(PAGE, FatMountDblsVolume)
|
||
#pragma alloc_text(PAGE, FatAutoMountDblsVolume)
|
||
#endif // WE_WON_ON_APPEAL
|
||
#pragma alloc_text(PAGE, FatGetVolumeBitmap)
|
||
#pragma alloc_text(PAGE, FatGetRetrievalPointers)
|
||
#pragma alloc_text(PAGE, FatMoveFile)
|
||
#pragma alloc_text(PAGE, FatComputeMoveFileSplicePoints)
|
||
#pragma alloc_text(PAGE, FatComputeMoveFileParameter)
|
||
#pragma alloc_text(PAGE, FatAllowExtendedDasdIo)
|
||
#endif
|
||
|
||
BOOLEAN FatMoveFileDebug = 0;
|
||
|
||
|
||
NTSTATUS
|
||
FatFsdFileSystemControl (
|
||
IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine implements the FSD part of FileSystem control operations
|
||
|
||
Arguments:
|
||
|
||
VolumeDeviceObject - Supplies the volume device object where the
|
||
file exists
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The FSD status for the IRP
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN Wait;
|
||
NTSTATUS Status;
|
||
PIRP_CONTEXT IrpContext = NULL;
|
||
|
||
BOOLEAN TopLevel;
|
||
|
||
DebugTrace(+1, Dbg, "FatFsdFileSystemControl\n", 0);
|
||
|
||
//
|
||
// Call the common FileSystem Control routine, with blocking allowed if
|
||
// synchronous. This opeation needs to special case the mount
|
||
// and verify suboperations because we know they are allowed to block.
|
||
// We identify these suboperations by looking at the file object field
|
||
// and seeing if its null.
|
||
//
|
||
|
||
if (IoGetCurrentIrpStackLocation(Irp)->FileObject == NULL) {
|
||
|
||
Wait = TRUE;
|
||
|
||
} else {
|
||
|
||
Wait = CanFsdWait( Irp );
|
||
}
|
||
|
||
FsRtlEnterFileSystem();
|
||
|
||
TopLevel = FatIsIrpTopLevel( Irp );
|
||
|
||
try {
|
||
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
//
|
||
// We need to made a special check here for the InvalidateVolumes
|
||
// FSCTL as that comes in with a FileSystem device object instead
|
||
// of a volume device object.
|
||
//
|
||
|
||
if ((IrpSp->DeviceObject->Size == (USHORT)sizeof(DEVICE_OBJECT)) &&
|
||
(IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
|
||
(IrpSp->MinorFunction == IRP_MN_USER_FS_REQUEST) &&
|
||
(IrpSp->Parameters.FileSystemControl.FsControlCode ==
|
||
FSCTL_INVALIDATE_VOLUMES)) {
|
||
|
||
Status = FatInvalidateVolumes( Irp );
|
||
|
||
} else {
|
||
|
||
IrpContext = FatCreateIrpContext( Irp, Wait );
|
||
|
||
Status = FatCommonFileSystemControl( IrpContext, Irp );
|
||
}
|
||
|
||
} except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
|
||
|
||
//
|
||
// We had some trouble trying to perform the requested
|
||
// operation, so we'll abort the I/O request with
|
||
// the error status that we get back from the
|
||
// execption code
|
||
//
|
||
|
||
Status = FatProcessException( IrpContext, Irp, GetExceptionCode() );
|
||
}
|
||
|
||
if (TopLevel) { IoSetTopLevelIrp( NULL ); }
|
||
|
||
FsRtlExitFileSystem();
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace(-1, Dbg, "FatFsdFileSystemControl -> %08lx\n", Status);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
FatCommonFileSystemControl (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common routine for doing FileSystem control operations called
|
||
by both the fsd and fsp threads
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp to process
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
//
|
||
// Get a pointer to the current Irp stack location
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "FatCommonFileSystemControl\n", 0);
|
||
DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp);
|
||
DebugTrace( 0, Dbg, "MinorFunction = %08lx\n", IrpSp->MinorFunction);
|
||
|
||
//
|
||
// We know this is a file system control so we'll case on the
|
||
// minor function, and call a internal worker routine to complete
|
||
// the irp.
|
||
//
|
||
|
||
switch (IrpSp->MinorFunction) {
|
||
|
||
case IRP_MN_USER_FS_REQUEST:
|
||
|
||
Status = FatUserFsCtrl( IrpContext, Irp );
|
||
break;
|
||
|
||
case IRP_MN_MOUNT_VOLUME:
|
||
|
||
Status = FatMountVolume( IrpContext,
|
||
IrpSp->Parameters.MountVolume.DeviceObject,
|
||
IrpSp->Parameters.MountVolume.Vpb,
|
||
NULL );
|
||
|
||
#ifdef WE_WON_ON_APPEAL
|
||
//
|
||
// If automount is enabled and this is a floppy, then attemp an
|
||
// automount. If something goes wrong, we ignore the error.
|
||
//
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
PVCB Vcb;
|
||
|
||
Vcb = &((PVOLUME_DEVICE_OBJECT)
|
||
IrpSp->Parameters.MountVolume.Vpb->DeviceObject)->Vcb;
|
||
|
||
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) &&
|
||
FatIsAutoMountEnabled(IrpContext)) {
|
||
|
||
try {
|
||
|
||
FatAutoMountDblsVolume( IrpContext,
|
||
IrpSp->Parameters.MountVolume.Vpb );
|
||
|
||
} except( FatExceptionFilter( IrpContext, GetExceptionInformation() ) ) {
|
||
|
||
NOTHING;
|
||
}
|
||
}
|
||
}
|
||
#endif // WE_WON_ON_APPEAL
|
||
|
||
//
|
||
// Complete the request.
|
||
//
|
||
// We do this here because FatMountVolume can be called recursively,
|
||
// but the Irp is only to be completed once.
|
||
//
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
|
||
break;
|
||
|
||
case IRP_MN_VERIFY_VOLUME:
|
||
|
||
#ifdef WE_WON_ON_APPEAL
|
||
//
|
||
// If we got a request to verify a compressed volume change it to
|
||
// the host volume. We will verify all compressed children as well.
|
||
//
|
||
|
||
{
|
||
PVOLUME_DEVICE_OBJECT VolDo;
|
||
PVPB Vpb;
|
||
PVCB Vcb;
|
||
|
||
VolDo = (PVOLUME_DEVICE_OBJECT)IrpSp->Parameters.VerifyVolume.DeviceObject;
|
||
Vpb = IrpSp->Parameters.VerifyVolume.Vpb;
|
||
Vcb = &VolDo->Vcb;
|
||
|
||
if (Vcb->Dscb) {
|
||
|
||
Vcb = Vcb->Dscb->ParentVcb;
|
||
|
||
IrpSp->Parameters.VerifyVolume.Vpb = Vcb->Vpb;
|
||
IrpSp->Parameters.VerifyVolume.DeviceObject =
|
||
&CONTAINING_RECORD( Vcb,
|
||
VOLUME_DEVICE_OBJECT,
|
||
Vcb )->DeviceObject;
|
||
}
|
||
}
|
||
#endif // WE_WON_ON_APPEAL
|
||
|
||
Status = FatVerifyVolume( IrpContext, Irp );
|
||
break;
|
||
|
||
default:
|
||
|
||
DebugTrace( 0, Dbg, "Invalid FS Control Minor Function %08lx\n", IrpSp->MinorFunction);
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
|
||
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
break;
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatCommonFileSystemControl -> %08lx\n", Status);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatMountVolume (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDEVICE_OBJECT TargetDeviceObject,
|
||
IN PVPB Vpb,
|
||
IN PDSCB Dscb OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine performs the mount volume operation. It is responsible for
|
||
either completing of enqueuing the input Irp.
|
||
|
||
Its job is to verify that the volume denoted in the IRP is a Fat volume,
|
||
and create the VCB and root DCB structures. The algorithm it uses is
|
||
essentially as follows:
|
||
|
||
1. Create a new Vcb Structure, and initialize it enough to do cached
|
||
volume file I/O.
|
||
|
||
2. Read the disk and check if it is a Fat volume.
|
||
|
||
3. If it is not a Fat volume then free the cached volume file, delete
|
||
the VCB, and complete the IRP with STATUS_UNRECOGNIZED_VOLUME
|
||
|
||
4. Check if the volume was previously mounted and if it was then do a
|
||
remount operation. This involves reinitializing the cached volume
|
||
file, checking the dirty bit, resetting up the allocation support,
|
||
deleting the VCB, hooking in the old VCB, and completing the IRP.
|
||
|
||
5. Otherwise create a root DCB, create Fsp threads as necessary, and
|
||
complete the IRP.
|
||
|
||
Arguments:
|
||
|
||
TargetDeviceObject - This is where we send all of our requests.
|
||
|
||
Vpb - This gives us additional information needed to complete the mount.
|
||
|
||
Dscb - If present, this indicates that we are attempting to mount a
|
||
double space "volume" that actually lives on another volume. Putting
|
||
this parameter in the Vcb->Dscb will cause non-cached reads to be
|
||
appropriately directed.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PULONG Fat;
|
||
PBCB BootBcb;
|
||
PPACKED_BOOT_SECTOR BootSector;
|
||
|
||
PBCB DirentBcb;
|
||
PDIRENT Dirent;
|
||
ULONG ByteOffset;
|
||
|
||
BOOLEAN MountNewVolume = FALSE;
|
||
|
||
BOOLEAN WeClearedVerifyRequiredBit = FALSE;
|
||
|
||
PDEVICE_OBJECT RealDevice;
|
||
PVOLUME_DEVICE_OBJECT VolDo = NULL;
|
||
PVCB Vcb = NULL;
|
||
|
||
PLIST_ENTRY Links;
|
||
|
||
DebugTrace(+1, Dbg, "FatMountVolume\n", 0);
|
||
DebugTrace( 0, Dbg, "DeviceObject = %08lx\n", DeviceObject);
|
||
DebugTrace( 0, Dbg, "Vpb = %08lx\n", Vpb);
|
||
|
||
ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
|
||
|
||
//
|
||
// Ping the volume with a partition query to make Jeff happy.
|
||
//
|
||
|
||
{
|
||
PARTITION_INFORMATION PartitionInformation;
|
||
|
||
(VOID)FatGetPartitionInfo( IrpContext,
|
||
TargetDeviceObject,
|
||
&PartitionInformation );
|
||
}
|
||
|
||
//
|
||
// Make sure we can wait.
|
||
//
|
||
|
||
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
|
||
|
||
//
|
||
// Initialize the Bcbs and our final state so that the termination
|
||
// handlers will know what to free or unpin
|
||
//
|
||
|
||
Fat = NULL;
|
||
BootBcb = NULL;
|
||
DirentBcb = NULL;
|
||
|
||
Vcb = NULL;
|
||
VolDo = NULL;
|
||
MountNewVolume = FALSE;
|
||
|
||
try {
|
||
|
||
BOOLEAN DoARemount = FALSE;
|
||
|
||
PVCB OldVcb;
|
||
PVPB OldVpb;
|
||
|
||
//
|
||
// Synchronize with FatCheckForDismount(), which modifies the vpb.
|
||
//
|
||
|
||
(VOID)FatAcquireExclusiveGlobal( IrpContext );
|
||
|
||
//
|
||
// Create a new volume device object. This will have the Vcb
|
||
// hanging off of its end, and set its alignment requirement
|
||
// from the device we talk to.
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status = IoCreateDevice( FatData.DriverObject,
|
||
sizeof(VOLUME_DEVICE_OBJECT) - sizeof(DEVICE_OBJECT),
|
||
NULL,
|
||
FILE_DEVICE_DISK_FILE_SYSTEM,
|
||
0,
|
||
FALSE,
|
||
(PDEVICE_OBJECT *)&VolDo))) {
|
||
|
||
try_return( Status );
|
||
}
|
||
|
||
#ifdef _PNP_POWER_
|
||
//
|
||
// This driver doesn't talk directly to a device, and (at the moment)
|
||
// isn't otherwise concerned about power management.
|
||
//
|
||
|
||
VolDo->DeviceObject.DeviceObjectExtension->PowerControlNeeded = FALSE;
|
||
#endif
|
||
|
||
//
|
||
// Our alignment requirement is the larger of the processor alignment requirement
|
||
// already in the volume device object and that in the TargetDeviceObject
|
||
//
|
||
|
||
if (TargetDeviceObject->AlignmentRequirement > VolDo->DeviceObject.AlignmentRequirement) {
|
||
|
||
VolDo->DeviceObject.AlignmentRequirement = TargetDeviceObject->AlignmentRequirement;
|
||
}
|
||
|
||
//
|
||
// Initialize the overflow queue for the volume
|
||
//
|
||
|
||
VolDo->OverflowQueueCount = 0;
|
||
InitializeListHead( &VolDo->OverflowQueue );
|
||
|
||
VolDo->PostedRequestCount = 0;
|
||
KeInitializeSpinLock( &VolDo->OverflowQueueSpinLock );
|
||
|
||
//
|
||
// Indicate that this device object is now completely initialized
|
||
//
|
||
|
||
ClearFlag(VolDo->DeviceObject.Flags, DO_DEVICE_INITIALIZING);
|
||
|
||
//
|
||
// Now Before we can initialize the Vcb we need to set up the device
|
||
// object field in the Vpb to point to our new volume device object.
|
||
// This is needed when we create the virtual volume file's file object
|
||
// in initialize vcb.
|
||
//
|
||
|
||
Vpb->DeviceObject = (PDEVICE_OBJECT)VolDo;
|
||
|
||
//
|
||
// If the real device needs verification, temporarily clear the
|
||
// field.
|
||
//
|
||
|
||
RealDevice = Vpb->RealDevice;
|
||
|
||
if ( FlagOn(RealDevice->Flags, DO_VERIFY_VOLUME) ) {
|
||
|
||
ClearFlag(RealDevice->Flags, DO_VERIFY_VOLUME);
|
||
|
||
WeClearedVerifyRequiredBit = TRUE;
|
||
}
|
||
|
||
//
|
||
// Initialize the new vcb
|
||
//
|
||
|
||
FatInitializeVcb( IrpContext, &VolDo->Vcb, TargetDeviceObject, Vpb, Dscb );
|
||
|
||
//
|
||
// Get a reference to the Vcb hanging off the end of the device object
|
||
//
|
||
|
||
Vcb = &VolDo->Vcb;
|
||
|
||
//
|
||
// We must initialize the stack size in our device object before
|
||
// the following reads, because the I/O system has not done it yet.
|
||
//
|
||
|
||
Vpb->DeviceObject->StackSize = (CCHAR)(TargetDeviceObject->StackSize + 1);
|
||
|
||
//
|
||
// Read in the boot sector, and have the read be the minumum size
|
||
// needed. We know we can wait.
|
||
//
|
||
|
||
FatReadVolumeFile( IrpContext,
|
||
Vcb,
|
||
0, // Starting Byte
|
||
sizeof(PACKED_BOOT_SECTOR),
|
||
&BootBcb,
|
||
(PVOID *)&BootSector );
|
||
|
||
//
|
||
// Call a routine to check the boot sector to see if it is fat
|
||
//
|
||
|
||
if (!FatIsBootSectorFat( BootSector )) {
|
||
|
||
DebugTrace(0, Dbg, "Not a Fat Volume\n", 0);
|
||
|
||
//
|
||
// Complete the request and return to our caller
|
||
//
|
||
|
||
try_return( Status = STATUS_UNRECOGNIZED_VOLUME );
|
||
}
|
||
|
||
//
|
||
// Stash a copy of the first 0x24 bytes
|
||
//
|
||
|
||
RtlCopyMemory( Vcb->First0x24BytesOfBootSector,
|
||
BootSector,
|
||
0x24 );
|
||
|
||
//
|
||
// Verify that all the Fats are REALLY FATs
|
||
//
|
||
|
||
if (!FatData.FujitsuFMR) {
|
||
|
||
UCHAR i;
|
||
ULONG BytesPerSector;
|
||
ULONG BytesPerFat;
|
||
ULONG FirstFatOffset;
|
||
PPACKED_BIOS_PARAMETER_BLOCK Bpb = &BootSector->PackedBpb;
|
||
|
||
BytesPerSector = Bpb->BytesPerSector[0] +
|
||
Bpb->BytesPerSector[1]*0x100;
|
||
|
||
BytesPerFat = ( Bpb->SectorsPerFat[0] +
|
||
Bpb->SectorsPerFat[1]*0x100 )
|
||
* BytesPerSector;
|
||
|
||
|
||
FirstFatOffset = ( Bpb->ReservedSectors[0] +
|
||
Bpb->ReservedSectors[1]*0x100 )
|
||
* BytesPerSector;
|
||
|
||
Fat = FsRtlAllocatePool( NonPagedPoolCacheAligned,
|
||
ROUND_TO_PAGES( BytesPerSector ));
|
||
|
||
|
||
for (i=0; i < Bpb->Fats[0]; i++) {
|
||
|
||
(VOID)FatPerformVerifyDiskRead( IrpContext,
|
||
Vcb,
|
||
Fat,
|
||
FirstFatOffset + i*BytesPerFat,
|
||
BytesPerSector,
|
||
FALSE );
|
||
|
||
|
||
//
|
||
// Make sure the media byte is 0xf0-0xff and that the
|
||
// next two bytes are 0xFF (16 bit fat get a freebe byte).
|
||
//
|
||
|
||
if ((Fat[0] & 0xfffff0) != 0xfffff0) {
|
||
|
||
try_return( Status = STATUS_UNRECOGNIZED_VOLUME );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Set the bytes per sector in our device object.
|
||
//
|
||
|
||
VolDo->DeviceObject.SectorSize = (USHORT)BytesPerSector;
|
||
|
||
} else {
|
||
|
||
VolDo->DeviceObject.SectorSize =
|
||
BootSector->PackedBpb.BytesPerSector[0] +
|
||
BootSector->PackedBpb.BytesPerSector[1]*0x100;
|
||
}
|
||
|
||
//
|
||
// This is a fat volume, so extract the bpb, serial number. The
|
||
// label we'll get later after we've created the root dcb.
|
||
//
|
||
// Note that the way data caching is done, we set neither the
|
||
// direct I/O or Buffered I/O bit in the device object flags.
|
||
//
|
||
|
||
FatUnpackBios( &Vcb->Bpb, &BootSector->PackedBpb );
|
||
if (Vcb->Bpb.Sectors != 0) { Vcb->Bpb.LargeSectors = 0; }
|
||
|
||
CopyUchar4( &Vpb->SerialNumber, BootSector->Id );
|
||
|
||
//
|
||
// Now unpin the boot sector, so when we set up allocation eveything
|
||
// works.
|
||
//
|
||
|
||
FatUnpinBcb( IrpContext, BootBcb );
|
||
|
||
//
|
||
// Compute a number of fields for Vcb.AllocationSupport
|
||
//
|
||
|
||
FatSetupAllocationSupport( IrpContext, Vcb );
|
||
|
||
//
|
||
// Create a root Dcb so we can read in the volume label
|
||
//
|
||
|
||
(VOID)FatCreateRootDcb( IrpContext, Vcb );
|
||
|
||
FatLocateVolumeLabel( IrpContext,
|
||
Vcb,
|
||
&Dirent,
|
||
&DirentBcb,
|
||
&ByteOffset );
|
||
|
||
if (Dirent != NULL) {
|
||
|
||
OEM_STRING OemString;
|
||
UNICODE_STRING UnicodeString;
|
||
|
||
//
|
||
// Compute the length of the volume name
|
||
//
|
||
|
||
OemString.Buffer = &Dirent->FileName[0];
|
||
OemString.MaximumLength = 11;
|
||
|
||
for ( OemString.Length = 11;
|
||
OemString.Length > 0;
|
||
OemString.Length -= 1) {
|
||
|
||
if ( (Dirent->FileName[OemString.Length-1] != 0x00) &&
|
||
(Dirent->FileName[OemString.Length-1] != 0x20) ) { break; }
|
||
}
|
||
|
||
UnicodeString.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH;
|
||
UnicodeString.Buffer = &Vcb->Vpb->VolumeLabel[0];
|
||
|
||
Status = RtlOemStringToCountedUnicodeString( &UnicodeString,
|
||
&OemString,
|
||
FALSE );
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
|
||
try_return( Status );
|
||
}
|
||
|
||
Vpb->VolumeLabelLength = UnicodeString.Length;
|
||
|
||
} else {
|
||
|
||
Vpb->VolumeLabelLength = 0;
|
||
}
|
||
|
||
Vcb->ChangeCount = 0;
|
||
|
||
//
|
||
// Now scan the list of previously mounted volumes and compare
|
||
// serial numbers and volume labels off not currently mounted
|
||
// volumes to see if we have a match.
|
||
//
|
||
// Note we never attempt a remount of a DoubleSpace volume.
|
||
//
|
||
|
||
if (Dscb == NULL) {
|
||
|
||
for (Links = FatData.VcbQueue.Flink;
|
||
Links != &FatData.VcbQueue;
|
||
Links = Links->Flink) {
|
||
|
||
OldVcb = CONTAINING_RECORD( Links, VCB, VcbLinks );
|
||
OldVpb = OldVcb->Vpb;
|
||
|
||
//
|
||
// Skip over ourselves since we're already in the VcbQueue
|
||
//
|
||
|
||
if (OldVpb == Vpb) { continue; }
|
||
|
||
//
|
||
// Ship DoubleSpace volumes.
|
||
//
|
||
|
||
if (OldVcb->Dscb != NULL) { continue; }
|
||
|
||
//
|
||
// Check for a match:
|
||
//
|
||
// Serial Number, VolumeLabel and Bpb must all be the same.
|
||
// Also the volume must have failed a verify before (ie.
|
||
// VolumeNotMounted), and it must be in the same physical
|
||
// drive than it was mounted in before.
|
||
//
|
||
|
||
if ( (OldVpb->SerialNumber == Vpb->SerialNumber) &&
|
||
(OldVcb->VcbCondition == VcbNotMounted) &&
|
||
(OldVpb->RealDevice == RealDevice) &&
|
||
(OldVpb->VolumeLabelLength == Vpb->VolumeLabelLength) &&
|
||
(RtlEqualMemory(&OldVpb->VolumeLabel[0],
|
||
&Vpb->VolumeLabel[0],
|
||
Vpb->VolumeLabelLength)) &&
|
||
(RtlEqualMemory(&OldVcb->Bpb,
|
||
&Vcb->Bpb,
|
||
sizeof(BIOS_PARAMETER_BLOCK))) ) {
|
||
|
||
DoARemount = TRUE;
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( DoARemount ) {
|
||
|
||
PVPB *IrpVpb;
|
||
|
||
DebugTrace(0, Dbg, "Doing a remount\n", 0);
|
||
DebugTrace(0, Dbg, "Vcb = %08lx\n", Vcb);
|
||
DebugTrace(0, Dbg, "Vpb = %08lx\n", Vpb);
|
||
DebugTrace(0, Dbg, "OldVcb = %08lx\n", OldVcb);
|
||
DebugTrace(0, Dbg, "OldVpb = %08lx\n", OldVpb);
|
||
|
||
//
|
||
// This is a remount, so link the old vpb in place
|
||
// of the new vpb and release the new vpb and the extra
|
||
// volume device object we created earlier.
|
||
//
|
||
|
||
OldVpb->RealDevice = Vpb->RealDevice;
|
||
OldVpb->RealDevice->Vpb = OldVpb;
|
||
OldVcb->TargetDeviceObject = TargetDeviceObject;
|
||
OldVcb->VcbCondition = VcbGood;
|
||
|
||
|
||
//
|
||
// Delete the extra new vpb, and make sure we don't use it again.
|
||
//
|
||
// Also if this is the Vpb referenced in the original Irp, set
|
||
// that reference back to the old VPB.
|
||
//
|
||
|
||
IrpVpb = &IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)->Parameters.MountVolume.Vpb;
|
||
|
||
if (*IrpVpb == Vpb) {
|
||
|
||
*IrpVpb = OldVpb;
|
||
}
|
||
|
||
ExFreePool( Vpb );
|
||
Vpb = NULL;
|
||
|
||
//
|
||
// Make sure the remaining stream files are orphaned.
|
||
//
|
||
|
||
Vcb->VirtualVolumeFile->Vpb = NULL;
|
||
Vcb->RootDcb->Specific.Dcb.DirectoryFile->Vpb = NULL;
|
||
|
||
//
|
||
// Reinitialize the volume file cache and allocation support.
|
||
//
|
||
|
||
{
|
||
CC_FILE_SIZES FileSizes;
|
||
|
||
FileSizes.AllocationSize.QuadPart =
|
||
FileSizes.FileSize.QuadPart = ( 0x40000 + 0x1000 );
|
||
FileSizes.ValidDataLength = FatMaxLarge;
|
||
|
||
DebugTrace(0, Dbg, "Truncate and reinitialize the volume file\n", 0);
|
||
|
||
CcInitializeCacheMap( OldVcb->VirtualVolumeFile,
|
||
&FileSizes,
|
||
TRUE,
|
||
&FatData.CacheManagerNoOpCallbacks,
|
||
Vcb );
|
||
|
||
//
|
||
// Redo the allocation support
|
||
//
|
||
|
||
FatSetupAllocationSupport( IrpContext, OldVcb );
|
||
|
||
//
|
||
// Get the state of the dirty bit.
|
||
//
|
||
|
||
FatCheckDirtyBit( IrpContext, OldVcb );
|
||
|
||
//
|
||
// Check for write protected media.
|
||
//
|
||
|
||
if (FatIsMediaWriteProtected(IrpContext, TargetDeviceObject)) {
|
||
|
||
SetFlag( OldVcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
|
||
|
||
} else {
|
||
|
||
ClearFlag( OldVcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Complete the request and return to our caller
|
||
//
|
||
|
||
try_return( Status = STATUS_SUCCESS );
|
||
}
|
||
|
||
DebugTrace(0, Dbg, "Mount a new volume\n", 0);
|
||
|
||
//
|
||
// This is a new mount
|
||
//
|
||
// Create a blank ea data file fcb.
|
||
//
|
||
|
||
{
|
||
DIRENT TempDirent;
|
||
PFCB EaFcb;
|
||
|
||
RtlZeroMemory( &TempDirent, sizeof(DIRENT) );
|
||
RtlCopyMemory( &TempDirent.FileName[0], "EA DATA SF", 11 );
|
||
|
||
EaFcb = FatCreateFcb( IrpContext,
|
||
Vcb,
|
||
Vcb->RootDcb,
|
||
0,
|
||
0,
|
||
&TempDirent,
|
||
NULL,
|
||
FALSE );
|
||
|
||
//
|
||
// Deny anybody who trys to open the file.
|
||
//
|
||
|
||
SetFlag( EaFcb->FcbState, FCB_STATE_SYSTEM_FILE );
|
||
|
||
//
|
||
// For the EaFcb we use the normal resource for the paging io
|
||
// resource. The blocks lazy writes while we are messing
|
||
// with its innards.
|
||
//
|
||
|
||
EaFcb->Header.PagingIoResource =
|
||
EaFcb->Header.Resource;
|
||
|
||
Vcb->EaFcb = EaFcb;
|
||
}
|
||
|
||
//
|
||
// Get the state of the dirty bit.
|
||
//
|
||
|
||
FatCheckDirtyBit( IrpContext, Vcb );
|
||
|
||
//
|
||
// Check for write protected media.
|
||
//
|
||
|
||
if (FatIsMediaWriteProtected(IrpContext, TargetDeviceObject)) {
|
||
|
||
SetFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
|
||
|
||
} else {
|
||
|
||
ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
|
||
}
|
||
|
||
//
|
||
// Lock volume in drive if we just mounted the boot drive.
|
||
//
|
||
|
||
if (FlagOn(RealDevice->Flags, DO_SYSTEM_BOOT_PARTITION) &&
|
||
FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) {
|
||
|
||
SetFlag(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE);
|
||
|
||
FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE );
|
||
}
|
||
|
||
//
|
||
// Indicate to our termination handler that we have mounted
|
||
// a new volume.
|
||
//
|
||
|
||
MountNewVolume = TRUE;
|
||
|
||
//
|
||
// Complete the request
|
||
//
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
try_exit: NOTHING;
|
||
|
||
} finally {
|
||
|
||
DebugUnwind( FatMountVolume );
|
||
|
||
FatUnpinBcb( IrpContext, BootBcb );
|
||
FatUnpinBcb( IrpContext, DirentBcb );
|
||
|
||
if ( Fat != NULL ) {
|
||
|
||
ExFreePool( Fat );
|
||
}
|
||
|
||
//
|
||
// Check if a volume was mounted. If not then we need to
|
||
// mark the Vpb not mounted again and delete the volume.
|
||
//
|
||
|
||
if ( !MountNewVolume ) {
|
||
|
||
if ( Vpb != NULL ) {
|
||
|
||
Vpb->DeviceObject = NULL;
|
||
}
|
||
|
||
if ( Vcb != NULL ) {
|
||
|
||
FatDeleteVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
if ( VolDo != NULL ) {
|
||
|
||
IoDeleteDevice( &VolDo->DeviceObject );
|
||
}
|
||
}
|
||
|
||
if ( WeClearedVerifyRequiredBit == TRUE ) {
|
||
|
||
SetFlag(RealDevice->Flags, DO_VERIFY_VOLUME);
|
||
}
|
||
|
||
FatReleaseGlobal( IrpContext );
|
||
|
||
DebugTrace(-1, Dbg, "FatMountVolume -> %08lx\n", Status);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatVerifyVolume (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine performs the verify volume operation by checking the volume
|
||
label and serial number physically on the media with the the Vcb
|
||
currently claiming to have the volume mounted. It is responsible for
|
||
either completing or enqueuing the input Irp.
|
||
|
||
Regardless of whether the verify operation succeeds, the following
|
||
operations are performed:
|
||
|
||
- Set Vcb->VirtualEaFile back to its virgin state.
|
||
- Purge all cached data (flushing first if verify succeeds)
|
||
- Mark all Fcbs as needing verification
|
||
|
||
If the volumes verifies correctly we also must:
|
||
|
||
- Check the volume dirty bit.
|
||
- Reinitialize the allocation support
|
||
- Flush any dirty data
|
||
|
||
If the volume verify fails, it may never be mounted again. If it is
|
||
mounted again, it will happen as a remount operation. In preparation
|
||
for that, and to leave the volume in a state that can be "lazy deleted"
|
||
the following operations are performed:
|
||
|
||
- Set the Vcb condition to VcbNotMounted
|
||
- Uninitialize the volume file cachemap
|
||
- Tear down the allocation support
|
||
|
||
In the case of an abnormal termination we haven't determined the state
|
||
of the volume, so we set the Device Object as needing verification again.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp to process
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - If the verify operation completes, it will return either
|
||
STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly. If an IO or
|
||
other error is encountered, that status will be returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PDIRENT RootDirectory = NULL;
|
||
PPACKED_BOOT_SECTOR BootSector = NULL;
|
||
|
||
BIOS_PARAMETER_BLOCK Bpb;
|
||
|
||
PVOLUME_DEVICE_OBJECT VolDo;
|
||
PVCB Vcb;
|
||
PVPB Vpb;
|
||
|
||
ULONG SectorSize;
|
||
|
||
BOOLEAN ClearVerify;
|
||
|
||
//
|
||
// Get the current Irp stack location
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "FatVerifyVolume\n", 0);
|
||
DebugTrace( 0, Dbg, "DeviceObject = %08lx\n", IrpSp->Parameters.VerifyVolume.DeviceObject);
|
||
DebugTrace( 0, Dbg, "Vpb = %08lx\n", IrpSp->Parameters.VerifyVolume.Vpb);
|
||
|
||
//
|
||
// Save some references to make our life a little easier
|
||
//
|
||
|
||
VolDo = (PVOLUME_DEVICE_OBJECT)IrpSp->Parameters.VerifyVolume.DeviceObject;
|
||
|
||
Vpb = IrpSp->Parameters.VerifyVolume.Vpb;
|
||
Vcb = &VolDo->Vcb;
|
||
|
||
//
|
||
// If we cannot wait then enqueue the irp to the fsp and
|
||
// return the status to our caller.
|
||
//
|
||
|
||
if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
|
||
|
||
DebugTrace(0, Dbg, "Cannot wait for verify.\n", 0);
|
||
|
||
Status = FatFsdPostRequest( IrpContext, Irp );
|
||
|
||
DebugTrace(-1, Dbg, "FatVerifyVolume -> %08lx\n", Status );
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// We are serialized at this point allowing only one thread to
|
||
// actually perform the verify operation. Any others will just
|
||
// wait and then no-op when checking if the volume still needs
|
||
// verification.
|
||
//
|
||
|
||
(VOID)FatAcquireExclusiveGlobal( IrpContext );
|
||
(VOID)FatAcquireExclusiveVcb( IrpContext, Vcb );
|
||
|
||
try {
|
||
|
||
BOOLEAN AllowRawMount = BooleanFlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT );
|
||
|
||
#ifdef WE_WON_ON_APPEAL
|
||
PLIST_ENTRY Links;
|
||
#endif // WE_WON_ON_APPEAL
|
||
|
||
//
|
||
// Check if the real device still needs to be verified. If it doesn't
|
||
// then obviously someone beat us here and already did the work
|
||
// so complete the verify irp with success. Otherwise reenable
|
||
// the real device and get to work.
|
||
//
|
||
|
||
if (!FlagOn(Vpb->RealDevice->Flags, DO_VERIFY_VOLUME)) {
|
||
|
||
DebugTrace(0, Dbg, "RealDevice has already been verified\n", 0);
|
||
|
||
try_return( Status = STATUS_SUCCESS );
|
||
}
|
||
|
||
//
|
||
// If we are a DoubleSpace partition, and our host is in a
|
||
// VcbNotMounted condition, then bail immediately.
|
||
//
|
||
|
||
if ((Vcb->Dscb != NULL) &&
|
||
(Vcb->Dscb->ParentVcb->VcbCondition == VcbNotMounted)) {
|
||
|
||
try_return( Status = STATUS_WRONG_VOLUME );
|
||
}
|
||
|
||
//
|
||
// Mark ourselves as verifying this volume so that recursive I/Os
|
||
// will be able to complete.
|
||
//
|
||
|
||
ASSERT( Vcb->VerifyThread == NULL );
|
||
|
||
Vcb->VerifyThread = KeGetCurrentThread();
|
||
|
||
//
|
||
// Ping the volume with a partition query to make Jeff happy.
|
||
//
|
||
|
||
{
|
||
PARTITION_INFORMATION PartitionInformation;
|
||
|
||
(VOID)FatGetPartitionInfo( IrpContext,
|
||
Vcb->TargetDeviceObject,
|
||
&PartitionInformation );
|
||
}
|
||
|
||
//
|
||
// Read in the boot sector
|
||
//
|
||
|
||
SectorSize = (ULONG)Vcb->Bpb.BytesPerSector;
|
||
|
||
BootSector = FsRtlAllocatePool(NonPagedPoolCacheAligned,
|
||
ROUND_TO_PAGES( SectorSize ));
|
||
|
||
//
|
||
// If this verify is on behalf of a DASD open, allow a RAW mount.
|
||
//
|
||
|
||
if (!FatPerformVerifyDiskRead( IrpContext,
|
||
Vcb,
|
||
BootSector,
|
||
0,
|
||
SectorSize,
|
||
AllowRawMount )) {
|
||
|
||
try_return( Status = STATUS_WRONG_VOLUME );
|
||
}
|
||
|
||
//
|
||
// Call a routine to check the boot sector to see if it is fat.
|
||
// If it is not fat then mark the vcb as not mounted tell our
|
||
// caller its the wrong volume
|
||
//
|
||
|
||
if (!FatIsBootSectorFat( BootSector )) {
|
||
|
||
DebugTrace(0, Dbg, "Not a Fat Volume\n", 0);
|
||
|
||
try_return( Status = STATUS_WRONG_VOLUME );
|
||
}
|
||
|
||
//
|
||
// This is a fat volume, so extract serial number and see if it is
|
||
// ours.
|
||
//
|
||
|
||
{
|
||
ULONG SerialNumber;
|
||
|
||
CopyUchar4( &SerialNumber, BootSector->Id );
|
||
|
||
if (SerialNumber != Vpb->SerialNumber) {
|
||
|
||
DebugTrace(0, Dbg, "Not our serial number\n", 0);
|
||
|
||
try_return( Status = STATUS_WRONG_VOLUME );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Make sure the Bpbs are not different. We have to zero out our
|
||
// stack version of the Bpb since unpacking leaves holes.
|
||
//
|
||
|
||
RtlZeroMemory( &Bpb, sizeof(BIOS_PARAMETER_BLOCK) );
|
||
|
||
FatUnpackBios( &Bpb, &BootSector->PackedBpb );
|
||
if (Bpb.Sectors != 0) { Bpb.LargeSectors = 0; }
|
||
|
||
if ( !RtlEqualMemory( &Bpb,
|
||
&Vcb->Bpb,
|
||
sizeof(BIOS_PARAMETER_BLOCK) ) ) {
|
||
|
||
DebugTrace(0, Dbg, "Bpb is different\n", 0);
|
||
|
||
try_return( Status = STATUS_WRONG_VOLUME );
|
||
}
|
||
|
||
RootDirectory = FsRtlAllocatePool( NonPagedPoolCacheAligned,
|
||
ROUND_TO_PAGES( FatRootDirectorySize( &Bpb )));
|
||
|
||
if (!FatPerformVerifyDiskRead( IrpContext,
|
||
Vcb,
|
||
RootDirectory,
|
||
FatRootDirectoryLbo( &Bpb ),
|
||
FatRootDirectorySize( &Bpb ),
|
||
AllowRawMount )) {
|
||
|
||
try_return( Status = STATUS_WRONG_VOLUME );
|
||
}
|
||
|
||
//
|
||
// Check the volume label. We do this by trying to locate the
|
||
// volume label, making two strings one for the saved volume label
|
||
// and the other for the new volume label and then we compare the
|
||
// two labels.
|
||
//
|
||
|
||
{
|
||
WCHAR UnicodeBuffer[11];
|
||
|
||
PDIRENT Dirent;
|
||
PDIRENT TerminationDirent;
|
||
|
||
ULONG VolumeLabelLength;
|
||
|
||
Dirent = RootDirectory;
|
||
|
||
TerminationDirent = Dirent +
|
||
FatRootDirectorySize( &Bpb ) / sizeof(DIRENT);
|
||
|
||
while ( Dirent < TerminationDirent ) {
|
||
|
||
if ( Dirent->FileName[0] == FAT_DIRENT_NEVER_USED ) {
|
||
|
||
DebugTrace( 0, Dbg, "Volume label not found.\n", 0);
|
||
Dirent = TerminationDirent;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If the entry is the non-deleted volume label break from the loop.
|
||
//
|
||
// Note that all out parameters are already correctly set.
|
||
//
|
||
|
||
if (((Dirent->Attributes & ~FAT_DIRENT_ATTR_ARCHIVE) ==
|
||
FAT_DIRENT_ATTR_VOLUME_ID) &&
|
||
(Dirent->FileName[0] != FAT_DIRENT_DELETED)) {
|
||
|
||
break;
|
||
}
|
||
|
||
Dirent += 1;
|
||
}
|
||
|
||
if ( Dirent < TerminationDirent ) {
|
||
|
||
OEM_STRING OemString;
|
||
UNICODE_STRING UnicodeString;
|
||
|
||
//
|
||
// Compute the length of the volume name
|
||
//
|
||
|
||
OemString.Buffer = &Dirent->FileName[0];
|
||
OemString.MaximumLength = 11;
|
||
|
||
for ( OemString.Length = 11;
|
||
OemString.Length > 0;
|
||
OemString.Length -= 1) {
|
||
|
||
if ( (Dirent->FileName[OemString.Length-1] != 0x00) &&
|
||
(Dirent->FileName[OemString.Length-1] != 0x20) ) { break; }
|
||
}
|
||
|
||
UnicodeString.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH;
|
||
UnicodeString.Buffer = &UnicodeBuffer[0];
|
||
|
||
Status = RtlOemStringToCountedUnicodeString( &UnicodeString,
|
||
&OemString,
|
||
FALSE );
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
|
||
try_return( Status );
|
||
}
|
||
|
||
VolumeLabelLength = UnicodeString.Length;
|
||
|
||
} else {
|
||
|
||
VolumeLabelLength = 0;
|
||
}
|
||
|
||
if ( (VolumeLabelLength != (ULONG)Vpb->VolumeLabelLength) ||
|
||
(!RtlEqualMemory(&UnicodeBuffer[0],
|
||
&Vpb->VolumeLabel[0],
|
||
VolumeLabelLength)) ) {
|
||
|
||
DebugTrace(0, Dbg, "Wrong volume label\n", 0);
|
||
|
||
try_return( Status = STATUS_WRONG_VOLUME );
|
||
}
|
||
}
|
||
|
||
try_exit: NOTHING;
|
||
|
||
//
|
||
// Note that we have previously acquired the Vcb to serialize
|
||
// the EA file stuff the marking all the Fcbs as NeedToBeVerified.
|
||
//
|
||
// Put the Ea file back in a virgin state.
|
||
//
|
||
|
||
if (Vcb->VirtualEaFile != NULL) {
|
||
|
||
PFILE_OBJECT EaFileObject;
|
||
|
||
EaFileObject = Vcb->VirtualEaFile;
|
||
|
||
if ( Status == STATUS_SUCCESS ) {
|
||
|
||
CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
|
||
}
|
||
|
||
Vcb->VirtualEaFile = NULL;
|
||
|
||
//
|
||
// Empty the Mcb for the Ea file.
|
||
//
|
||
|
||
FsRtlRemoveMcbEntry( &Vcb->EaFcb->Mcb, 0, 0xFFFFFFFF );
|
||
|
||
//
|
||
// Set the file object type to unopened file object
|
||
// and dereference it.
|
||
//
|
||
|
||
FatSetFileObject( EaFileObject,
|
||
UnopenedFileObject,
|
||
NULL,
|
||
NULL );
|
||
|
||
FatSyncUninitializeCacheMap( IrpContext, EaFileObject );
|
||
|
||
ObDereferenceObject( EaFileObject );
|
||
}
|
||
|
||
//
|
||
// Mark all Fcbs as needing verification.
|
||
//
|
||
|
||
FatMarkFcbCondition(IrpContext, Vcb->RootDcb, FcbNeedsToBeVerified);
|
||
|
||
//
|
||
// If the verify didn't succeed, get the volume ready for a
|
||
// remount or eventual deletion.
|
||
//
|
||
|
||
if (Vcb->VcbCondition == VcbNotMounted) {
|
||
|
||
//
|
||
// If the volume was already in an unmounted state, just bail
|
||
// and make sure we return STATUS_WRONG_VOLUME.
|
||
//
|
||
|
||
Status = STATUS_WRONG_VOLUME;
|
||
|
||
ClearVerify = FALSE;
|
||
|
||
NOTHING;
|
||
|
||
} else if ( Status == STATUS_WRONG_VOLUME ) {
|
||
|
||
//
|
||
// Get rid of any cached data, without flushing
|
||
//
|
||
|
||
FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, FALSE );
|
||
|
||
//
|
||
// Uninitialize the volume file cache map. Note that we cannot
|
||
// do a "FatSyncUninit" because of deadlock problems. However,
|
||
// since this FileObject is referenced by us, and thus included
|
||
// in the Vpb residual count, it is OK to do a normal CcUninit.
|
||
//
|
||
|
||
CcUninitializeCacheMap( Vcb->VirtualVolumeFile,
|
||
&FatLargeZero,
|
||
NULL );
|
||
|
||
FatTearDownAllocationSupport( IrpContext, Vcb );
|
||
|
||
Vcb->VcbCondition = VcbNotMounted;
|
||
|
||
ClearVerify = TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Get rid of any cached data, flushing first.
|
||
//
|
||
|
||
FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, TRUE );
|
||
|
||
//
|
||
// Flush and Purge the volume file.
|
||
//
|
||
|
||
(VOID)FatFlushFat( IrpContext, Vcb );
|
||
CcPurgeCacheSection( &Vcb->SectionObjectPointers, NULL, 0, FALSE );
|
||
|
||
//
|
||
// Redo the allocation support with newly paged stuff.
|
||
//
|
||
|
||
FatTearDownAllocationSupport( IrpContext, Vcb );
|
||
|
||
FatSetupAllocationSupport( IrpContext, Vcb );
|
||
|
||
FatCheckDirtyBit( IrpContext, Vcb );
|
||
|
||
//
|
||
// Check for write protected media.
|
||
//
|
||
|
||
if (FatIsMediaWriteProtected(IrpContext, Vcb->TargetDeviceObject)) {
|
||
|
||
SetFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
|
||
|
||
} else {
|
||
|
||
ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
|
||
}
|
||
|
||
ClearVerify = TRUE;
|
||
}
|
||
|
||
#ifdef WE_WON_ON_APPEAL
|
||
//
|
||
// Now try to find any other volumes leaching off of this
|
||
// real device object, and verify them as well.
|
||
//
|
||
|
||
for (Links = Vcb->ParentDscbLinks.Flink;
|
||
Links != &Vcb->ParentDscbLinks;
|
||
Links = Links->Flink) {
|
||
|
||
PVCB ChildVcb;
|
||
PVPB ChildVpb;
|
||
|
||
ChildVcb = CONTAINING_RECORD( Links, DSCB, ChildDscbLinks )->Vcb;
|
||
ChildVpb = ChildVcb->Vpb;
|
||
|
||
ASSERT( ChildVpb->RealDevice == Vcb->Vpb->RealDevice );
|
||
|
||
//
|
||
// Now dummy up our Irp and try to verify the DoubleSpace volume.
|
||
//
|
||
|
||
IrpSp->Parameters.VerifyVolume.DeviceObject =
|
||
&CONTAINING_RECORD( ChildVcb,
|
||
VOLUME_DEVICE_OBJECT,
|
||
Vcb )->DeviceObject;
|
||
|
||
IrpSp->Parameters.VerifyVolume.Vpb = ChildVpb;
|
||
|
||
try {
|
||
|
||
FatVerifyVolume( IrpContext, Irp );
|
||
|
||
} except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
|
||
|
||
NOTHING;
|
||
}
|
||
}
|
||
|
||
#endif // WE_WON_ON_APPEAL
|
||
|
||
if (ClearVerify) {
|
||
|
||
//
|
||
// Mark the device as no longer needing verification.
|
||
//
|
||
|
||
ClearFlag( Vpb->RealDevice->Flags, DO_VERIFY_VOLUME );
|
||
}
|
||
|
||
|
||
} finally {
|
||
|
||
DebugUnwind( FatVerifyVolume );
|
||
|
||
//
|
||
// Free any buffer we may have allocated
|
||
//
|
||
|
||
if ( BootSector != NULL ) { ExFreePool( BootSector ); }
|
||
if ( RootDirectory != NULL ) { ExFreePool( RootDirectory ); }
|
||
|
||
//
|
||
// Show that we are done with this volume.
|
||
//
|
||
|
||
Vcb->VerifyThread = NULL;
|
||
|
||
FatReleaseVcb( IrpContext, Vcb );
|
||
FatReleaseGlobal( IrpContext );
|
||
|
||
//
|
||
// If this was not an abnormal termination, complete the irp.
|
||
//
|
||
|
||
if (!AbnormalTermination() && (Vcb->Dscb == NULL)) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatVerifyVolume -> %08lx\n", Status);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
BOOLEAN
|
||
FatIsBootSectorFat (
|
||
IN PPACKED_BOOT_SECTOR BootSector
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks if the boot sector is for a fat file volume.
|
||
|
||
Arguments:
|
||
|
||
BootSector - Supplies the packed boot sector to check
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if the volume is Fat and FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN Result;
|
||
BIOS_PARAMETER_BLOCK Bpb;
|
||
|
||
DebugTrace(+1, Dbg, "FatIsBootSectorFat, BootSector = %08lx\n", BootSector);
|
||
|
||
//
|
||
// The result is true unless we decide that it should be false
|
||
//
|
||
|
||
Result = TRUE;
|
||
|
||
//
|
||
// Unpack the bios and then test everything
|
||
//
|
||
|
||
FatUnpackBios( &Bpb, &BootSector->PackedBpb );
|
||
if (Bpb.Sectors != 0) { Bpb.LargeSectors = 0; }
|
||
|
||
if ((BootSector->Jump[0] != 0xe9) &&
|
||
(BootSector->Jump[0] != 0xeb) &&
|
||
(BootSector->Jump[0] != 0x49)) {
|
||
|
||
Result = FALSE;
|
||
|
||
} else if ((Bpb.BytesPerSector != 128) &&
|
||
(Bpb.BytesPerSector != 256) &&
|
||
(Bpb.BytesPerSector != 512) &&
|
||
(Bpb.BytesPerSector != 1024) &&
|
||
(Bpb.BytesPerSector != 2048) &&
|
||
(Bpb.BytesPerSector != 4096)) {
|
||
|
||
Result = FALSE;
|
||
|
||
} else if ((Bpb.SectorsPerCluster != 1) &&
|
||
(Bpb.SectorsPerCluster != 2) &&
|
||
(Bpb.SectorsPerCluster != 4) &&
|
||
(Bpb.SectorsPerCluster != 8) &&
|
||
(Bpb.SectorsPerCluster != 16) &&
|
||
(Bpb.SectorsPerCluster != 32) &&
|
||
(Bpb.SectorsPerCluster != 64) &&
|
||
(Bpb.SectorsPerCluster != 128)) {
|
||
|
||
Result = FALSE;
|
||
|
||
} else if (Bpb.ReservedSectors == 0) {
|
||
|
||
Result = FALSE;
|
||
|
||
} else if (Bpb.Fats == 0) {
|
||
|
||
Result = FALSE;
|
||
|
||
} else if (Bpb.RootEntries == 0) {
|
||
|
||
Result = FALSE;
|
||
|
||
//
|
||
// Prior to DOS 3.2 might contains value in both of Sectors and
|
||
// Sectors Large.
|
||
//
|
||
|
||
} else if ((Bpb.Sectors == 0) && (Bpb.LargeSectors == 0)) {
|
||
|
||
Result = FALSE;
|
||
|
||
} else if (Bpb.SectorsPerFat == 0) {
|
||
|
||
Result = FALSE;
|
||
|
||
} else if ((Bpb.Media != 0xf0) &&
|
||
(Bpb.Media != 0xf8) &&
|
||
(Bpb.Media != 0xf9) &&
|
||
(Bpb.Media != 0xfb) &&
|
||
(Bpb.Media != 0xfc) &&
|
||
(Bpb.Media != 0xfd) &&
|
||
(Bpb.Media != 0xfe) &&
|
||
(Bpb.Media != 0xff) &&
|
||
(!FatData.FujitsuFMR || ((Bpb.Media != 0x00) &&
|
||
(Bpb.Media != 0x01) &&
|
||
(Bpb.Media != 0xfa)))) {
|
||
|
||
Result = FALSE;
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatIsBootSectorFat -> %08lx\n", Result);
|
||
|
||
return Result;
|
||
}
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatGetPartitionInfo (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDEVICE_OBJECT TargetDeviceObject,
|
||
IN PPARTITION_INFORMATION PartitionInformation
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used for querying the partition information.
|
||
|
||
Arguments:
|
||
|
||
TargetDeviceObject - The target of the query
|
||
|
||
PartitionInformation - Receives the result of the query
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
PIRP Irp;
|
||
KEVENT Event;
|
||
NTSTATUS Status;
|
||
IO_STATUS_BLOCK Iosb;
|
||
|
||
//
|
||
// Query the partition table
|
||
//
|
||
|
||
KeInitializeEvent( &Event, NotificationEvent, FALSE );
|
||
|
||
Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_GET_PARTITION_INFO,
|
||
TargetDeviceObject,
|
||
NULL,
|
||
0,
|
||
PartitionInformation,
|
||
sizeof(PARTITION_INFORMATION),
|
||
FALSE,
|
||
&Event,
|
||
&Iosb );
|
||
|
||
if ( Irp == NULL ) {
|
||
|
||
FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
Status = IoCallDriver( TargetDeviceObject, Irp );
|
||
|
||
if ( Status == STATUS_PENDING ) {
|
||
|
||
(VOID) KeWaitForSingleObject( &Event,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
(PLARGE_INTEGER)NULL );
|
||
|
||
Status = Iosb.Status;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
BOOLEAN
|
||
FatIsMediaWriteProtected (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDEVICE_OBJECT TargetDeviceObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines if the target media is write protected.
|
||
|
||
Arguments:
|
||
|
||
TargetDeviceObject - The target of the query
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
PIRP Irp;
|
||
KEVENT Event;
|
||
NTSTATUS Status;
|
||
IO_STATUS_BLOCK Iosb;
|
||
|
||
//
|
||
// Query the partition table
|
||
//
|
||
|
||
KeInitializeEvent( &Event, NotificationEvent, FALSE );
|
||
|
||
//
|
||
// See if the media is write protected. On success or any kind
|
||
// of error (possibly illegal device function), assume it is
|
||
// writeable, and only complain if he tells us he is write protected.
|
||
//
|
||
|
||
Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_IS_WRITABLE,
|
||
TargetDeviceObject,
|
||
NULL,
|
||
0,
|
||
NULL,
|
||
0,
|
||
FALSE,
|
||
&Event,
|
||
&Iosb );
|
||
|
||
//
|
||
// Just return FALSE in the unlikely event we couldn't allocate an Irp.
|
||
//
|
||
|
||
if ( Irp == NULL ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME );
|
||
|
||
Status = IoCallDriver( TargetDeviceObject, Irp );
|
||
|
||
if ( Status == STATUS_PENDING ) {
|
||
|
||
(VOID) KeWaitForSingleObject( &Event,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
(PLARGE_INTEGER)NULL );
|
||
|
||
Status = Iosb.Status;
|
||
}
|
||
|
||
return (BOOLEAN)(Status == STATUS_MEDIA_WRITE_PROTECTED);
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatUserFsCtrl (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common routine for implementing the user's requests made
|
||
through NtFsControlFile.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
ULONG FsControlCode;
|
||
|
||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
//
|
||
// Save some references to make our life a little easier
|
||
//
|
||
|
||
FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
|
||
|
||
DebugTrace(+1, Dbg, "FatUserFsCtrl...\n", 0);
|
||
DebugTrace( 0, Dbg, "FsControlCode = %08lx\n", FsControlCode);
|
||
|
||
//
|
||
// Case on the control code.
|
||
//
|
||
|
||
switch ( FsControlCode ) {
|
||
|
||
case FSCTL_REQUEST_OPLOCK_LEVEL_1:
|
||
case FSCTL_REQUEST_OPLOCK_LEVEL_2:
|
||
case FSCTL_REQUEST_BATCH_OPLOCK:
|
||
case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
|
||
case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
|
||
case FSCTL_OPLOCK_BREAK_NOTIFY:
|
||
case FSCTL_OPLOCK_BREAK_ACK_NO_2:
|
||
|
||
Status = FatOplockRequest( IrpContext, Irp );
|
||
break;
|
||
|
||
case FSCTL_LOCK_VOLUME:
|
||
|
||
Status = FatLockVolume( IrpContext, Irp );
|
||
break;
|
||
|
||
case FSCTL_UNLOCK_VOLUME:
|
||
|
||
Status = FatUnlockVolume( IrpContext, Irp );
|
||
break;
|
||
|
||
case FSCTL_DISMOUNT_VOLUME:
|
||
|
||
Status = FatDismountVolume( IrpContext, Irp );
|
||
break;
|
||
|
||
case FSCTL_MARK_VOLUME_DIRTY:
|
||
|
||
Status = FatDirtyVolume( IrpContext, Irp );
|
||
break;
|
||
|
||
case FSCTL_IS_VOLUME_MOUNTED:
|
||
|
||
Status = FatIsVolumeMounted( IrpContext, Irp );
|
||
break;
|
||
|
||
case FSCTL_IS_PATHNAME_VALID:
|
||
Status = FatIsPathnameValid( IrpContext, Irp );
|
||
break;
|
||
|
||
#ifdef WE_WON_ON_APPEAL
|
||
|
||
case FSCTL_MOUNT_DBLS_VOLUME:
|
||
Status = FatMountDblsVolume( IrpContext, Irp );
|
||
break;
|
||
|
||
#endif // WE_WON_ON_APPEAL
|
||
|
||
case FSCTL_QUERY_RETRIEVAL_POINTERS:
|
||
Status = FatQueryRetrievalPointers( IrpContext, Irp );
|
||
break;
|
||
|
||
case FSCTL_QUERY_FAT_BPB:
|
||
Status = FatQueryBpb( IrpContext, Irp );
|
||
break;
|
||
|
||
case FSCTL_FILESYSTEM_GET_STATISTICS:
|
||
Status = FatGetStatistics( IrpContext, Irp );
|
||
break;
|
||
|
||
case FSCTL_GET_VOLUME_BITMAP:
|
||
Status = FatGetVolumeBitmap( IrpContext, Irp );
|
||
break;
|
||
|
||
case FSCTL_GET_RETRIEVAL_POINTERS:
|
||
Status = FatGetRetrievalPointers( IrpContext, Irp );
|
||
break;
|
||
|
||
case FSCTL_MOVE_FILE:
|
||
Status = FatMoveFile( IrpContext, Irp );
|
||
break;
|
||
|
||
case FSCTL_ALLOW_EXTENDED_DASD_IO:
|
||
Status = FatAllowExtendedDasdIo( IrpContext, Irp );
|
||
break;
|
||
|
||
default :
|
||
|
||
DebugTrace(0, Dbg, "Invalid control code -> %08lx\n", FsControlCode );
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
|
||
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
break;
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatUserFsCtrl -> %08lx\n", Status );
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatOplockRequest (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common routine to handle oplock requests made via the
|
||
NtFsControlFile call.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
ULONG FsControlCode;
|
||
PFCB Fcb;
|
||
PVCB Vcb;
|
||
PCCB Ccb;
|
||
|
||
ULONG OplockCount;
|
||
|
||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
BOOLEAN AcquiredVcb = FALSE;
|
||
|
||
//
|
||
// Save some references to make our life a little easier
|
||
//
|
||
|
||
FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
|
||
|
||
DebugTrace(+1, Dbg, "FatOplockRequest...\n", 0);
|
||
DebugTrace( 0, Dbg, "FsControlCode = %08lx\n", FsControlCode);
|
||
|
||
//
|
||
// We only permit oplock requests on files.
|
||
//
|
||
|
||
if ( FatDecodeFileObject( IrpSp->FileObject,
|
||
&Vcb,
|
||
&Fcb,
|
||
&Ccb ) != UserFileOpen ) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
DebugTrace(-1, Dbg, "FatOplockRequest -> STATUS_INVALID_PARAMETER\n", 0);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Switch on the function control code. We grab the Fcb exclusively
|
||
// for oplock requests, shared for oplock break acknowledgement.
|
||
//
|
||
|
||
switch ( FsControlCode ) {
|
||
|
||
case FSCTL_REQUEST_OPLOCK_LEVEL_1:
|
||
case FSCTL_REQUEST_OPLOCK_LEVEL_2:
|
||
case FSCTL_REQUEST_BATCH_OPLOCK:
|
||
|
||
if ( !FatAcquireSharedVcb( IrpContext, Fcb->Vcb )) {
|
||
|
||
//
|
||
// If we can't acquire the Vcb, then this is an invalid
|
||
// operation since we can't post Oplock requests.
|
||
//
|
||
|
||
DebugTrace(0, Dbg, "Cannot acquire exclusive Vcb\n", 0)
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_OPLOCK_NOT_GRANTED );
|
||
DebugTrace(-1, Dbg, "FatOplockRequest -> STATUS_OPLOCK_NOT_GRANTED\n", 0);
|
||
return STATUS_OPLOCK_NOT_GRANTED;
|
||
}
|
||
|
||
AcquiredVcb = TRUE;
|
||
|
||
//
|
||
// We set the wait parameter in the IrpContext to FALSE. If this
|
||
// request can't grab the Fcb and we are in the Fsp thread, then
|
||
// we fail this request.
|
||
//
|
||
|
||
ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
|
||
|
||
if ( !FatAcquireExclusiveFcb( IrpContext, Fcb )) {
|
||
|
||
DebugTrace(0, Dbg, "Cannot acquire exclusive Fcb\n", 0);
|
||
|
||
FatReleaseVcb( IrpContext, Fcb->Vcb );
|
||
|
||
//
|
||
// We fail this request.
|
||
//
|
||
|
||
Status = STATUS_OPLOCK_NOT_GRANTED;
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
|
||
DebugTrace(-1, Dbg, "FatOplockRequest -> %08lx\n", Status );
|
||
return Status;
|
||
}
|
||
|
||
if (FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2) {
|
||
|
||
OplockCount = (ULONG) FsRtlAreThereCurrentFileLocks( &Fcb->Specific.Fcb.FileLock );
|
||
|
||
} else {
|
||
|
||
OplockCount = Fcb->UncleanCount;
|
||
}
|
||
|
||
break;
|
||
|
||
case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
|
||
case FSCTL_OPBATCH_ACK_CLOSE_PENDING :
|
||
case FSCTL_OPLOCK_BREAK_NOTIFY:
|
||
case FSCTL_OPLOCK_BREAK_ACK_NO_2:
|
||
|
||
if ( !FatAcquireSharedFcb( IrpContext, Fcb )) {
|
||
|
||
DebugTrace(0, Dbg, "Cannot acquire shared Fcb\n", 0);
|
||
|
||
Status = FatFsdPostRequest( IrpContext, Irp );
|
||
|
||
DebugTrace(-1, Dbg, "FatOplockRequest -> %08lx\n", Status );
|
||
return Status;
|
||
}
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
FatBugCheck( FsControlCode, 0, 0 );
|
||
}
|
||
|
||
//
|
||
// Use a try finally to free the Fcb.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Call the FsRtl routine to grant/acknowledge oplock.
|
||
//
|
||
|
||
Status = FsRtlOplockFsctrl( &Fcb->Specific.Fcb.Oplock,
|
||
Irp,
|
||
OplockCount );
|
||
|
||
//
|
||
// Set the flag indicating if Fast I/O is possible
|
||
//
|
||
|
||
Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb );
|
||
|
||
} finally {
|
||
|
||
DebugUnwind( FatOplockRequest );
|
||
|
||
//
|
||
// Release all of our resources
|
||
//
|
||
|
||
if (AcquiredVcb) {
|
||
|
||
FatReleaseVcb( IrpContext, Fcb->Vcb );
|
||
}
|
||
|
||
FatReleaseFcb( IrpContext, Fcb );
|
||
|
||
if (!AbnormalTermination()) {
|
||
|
||
FatCompleteRequest( IrpContext, FatNull, 0 );
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatOplockRequest -> %08lx\n", Status );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatLockVolume (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine performs the lock volume operation. It is responsible for
|
||
either completing of enqueuing the input Irp.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp to process
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
KIRQL SavedIrql;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PVCB Vcb;
|
||
PFCB Fcb;
|
||
PCCB Ccb;
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "FatLockVolume...\n", 0);
|
||
|
||
//
|
||
// Decode the file object, the only type of opens we accept are
|
||
// user volume opens.
|
||
//
|
||
|
||
if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
|
||
DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Acquire exclusive access to the Vcb and enqueue the Irp if we
|
||
// didn't get access.
|
||
//
|
||
|
||
if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {
|
||
|
||
DebugTrace( 0, Dbg, "Cannot acquire Vcb\n", 0);
|
||
|
||
Status = FatFsdPostRequest( IrpContext, Irp );
|
||
|
||
DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", Status);
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// If there are any open handles, this will fail.
|
||
//
|
||
|
||
if (!FatIsHandleCountZero( IrpContext, Vcb )) {
|
||
|
||
FatReleaseVcb( IrpContext, Vcb );
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED );
|
||
|
||
DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", STATUS_ACCESS_DENIED);
|
||
return STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
try {
|
||
|
||
//
|
||
// Force Mm to get rid of its referenced file objects.
|
||
//
|
||
|
||
FatFlushFat( IrpContext, Vcb );
|
||
|
||
FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, TRUE );
|
||
|
||
if (Vcb->VirtualEaFile != NULL) {
|
||
|
||
PFILE_OBJECT EaFileObject;
|
||
|
||
EaFileObject = Vcb->VirtualEaFile;
|
||
|
||
CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
|
||
|
||
Vcb->VirtualEaFile = NULL;
|
||
|
||
//
|
||
// Empty the Mcb for the Ea file.
|
||
//
|
||
|
||
FsRtlRemoveMcbEntry( &Vcb->EaFcb->Mcb, 0, 0xFFFFFFFF );
|
||
|
||
//
|
||
// Set the file object type to unopened file object
|
||
// and dereference it.
|
||
//
|
||
|
||
FatSetFileObject( EaFileObject,
|
||
UnopenedFileObject,
|
||
NULL,
|
||
NULL );
|
||
|
||
FatSyncUninitializeCacheMap( IrpContext, EaFileObject );
|
||
|
||
ObDereferenceObject( EaFileObject );
|
||
}
|
||
|
||
} finally {
|
||
|
||
FatReleaseVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
//
|
||
// Check if the Vcb is already locked, or if the open file count
|
||
// is greater than 1 (which implies that someone else also is
|
||
// currently using the volume, or a file on the volume).
|
||
//
|
||
|
||
IoAcquireVpbSpinLock( &SavedIrql );
|
||
|
||
if (!FlagOn(Vcb->Vpb->Flags, VPB_LOCKED) &&
|
||
(Vcb->Vpb->ReferenceCount == 3)) {
|
||
|
||
SetFlag(Vcb->Vpb->Flags, VPB_LOCKED);
|
||
|
||
SetFlag(Vcb->VcbState, VCB_STATE_FLAG_LOCKED);
|
||
Vcb->FileObjectWithVcbLocked = IrpSp->FileObject;
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
Status = STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
IoReleaseVpbSpinLock( SavedIrql );
|
||
|
||
//
|
||
// If we successully locked the volume, see if it is clean now.
|
||
//
|
||
|
||
if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ) &&
|
||
!FlagOn( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ) &&
|
||
!CcIsThereDirtyData(Vcb->Vpb)) {
|
||
|
||
FatMarkVolumeClean( IrpContext, Vcb );
|
||
ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY );
|
||
}
|
||
|
||
ASSERT( !NT_SUCCESS(Status) || (Vcb->OpenFileCount == 1) );
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
|
||
DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", Status);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatUnlockVolume (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine performs the unlock volume operation. It is responsible for
|
||
either completing of enqueuing the input Irp.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp to process
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
KIRQL SavedIrql;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PVCB Vcb;
|
||
PFCB Fcb;
|
||
PCCB Ccb;
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "FatUnlockVolume...\n", 0);
|
||
|
||
//
|
||
// Decode the file object, the only type of opens we accept are
|
||
// user volume opens.
|
||
//
|
||
|
||
if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
|
||
DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
IoAcquireVpbSpinLock( &SavedIrql );
|
||
|
||
if (FlagOn(Vcb->Vpb->Flags, VPB_LOCKED)) {
|
||
|
||
//
|
||
// Unlock the volume and complete the Irp
|
||
//
|
||
|
||
ClearFlag( Vcb->Vpb->Flags, VPB_LOCKED );
|
||
|
||
ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_LOCKED );
|
||
Vcb->FileObjectWithVcbLocked = NULL;
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
Status = STATUS_NOT_LOCKED;
|
||
}
|
||
|
||
IoReleaseVpbSpinLock( SavedIrql );
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
|
||
DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", Status);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatDismountVolume (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine performs the dismount volume operation. It is responsible for
|
||
either completing of enqueuing the input Irp.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp to process
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PVCB Vcb;
|
||
PFCB Fcb;
|
||
PCCB Ccb;
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "FatDismountVolume...\n", 0);
|
||
|
||
//
|
||
// Decode the file object, the only type of opens we accept are
|
||
// user volume opens.
|
||
//
|
||
|
||
if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
|
||
DebugTrace(-1, Dbg, "FatDismountVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// If the volume was not locked, fail the request.
|
||
//
|
||
|
||
if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED) ||
|
||
(Vcb->OpenFileCount != 1)) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_NOT_LOCKED );
|
||
|
||
DebugTrace(-1, Dbg, "FatDismountVolume -> %08lx\n", STATUS_NOT_LOCKED);
|
||
return STATUS_NOT_LOCKED;
|
||
}
|
||
|
||
//
|
||
// If this is an automounted compressed volume, no-op this request.
|
||
//
|
||
|
||
if (Vcb->Dscb && (Vcb->CurrentDevice == Vcb->Vpb->RealDevice)) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
|
||
|
||
DebugTrace(-1, Dbg, "FatDismountVolume -> %08lx\n", STATUS_SUCCESS);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
ASSERT( Vcb->OpenFileCount == 1 );
|
||
|
||
//
|
||
// First, tell the device to flush anything from its buffers.
|
||
//
|
||
|
||
(VOID)FatHijackIrpAndFlushDevice( IrpContext, Irp, Vcb->TargetDeviceObject );
|
||
|
||
//
|
||
// Get rid of any cached data, without flushing
|
||
//
|
||
|
||
FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, FALSE );
|
||
|
||
//
|
||
// Uninitialize the volume file cache map. Note that we cannot
|
||
// do a "FatSyncUninit" because of deadlock problems. However,
|
||
// since this FileObject is referenced by us, and thus included
|
||
// in the Vpb residual count, it is OK to do a normal CcUninit.
|
||
//
|
||
|
||
CcUninitializeCacheMap( Vcb->VirtualVolumeFile,
|
||
&FatLargeZero,
|
||
NULL );
|
||
|
||
FatTearDownAllocationSupport( IrpContext, Vcb );
|
||
|
||
Vcb->VcbCondition = VcbNotMounted;
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
|
||
|
||
DebugTrace(-1, Dbg, "FatDismountVolume -> %08lx\n", STATUS_SUCCESS);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatDirtyVolume (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine marks the volume as dirty.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp to process
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PVCB Vcb;
|
||
PFCB Fcb;
|
||
PCCB Ccb;
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "FatDirtyVolume...\n", 0);
|
||
|
||
//
|
||
// Decode the file object, the only type of opens we accept are
|
||
// user volume opens.
|
||
//
|
||
|
||
if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
|
||
DebugTrace(-1, Dbg, "FatDirtyVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY );
|
||
|
||
FatMarkVolumeDirty( IrpContext, Vcb, FALSE );
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
|
||
|
||
DebugTrace(-1, Dbg, "FatDirtyVolume -> STATUS_SUCCESS\n", 0);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatIsVolumeMounted (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines if a volume is currently mounted.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp to process
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PVCB Vcb = NULL;
|
||
PFCB Fcb;
|
||
PCCB Ccb;
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
DebugTrace(+1, Dbg, "FatIsVolumeMounted...\n", 0);
|
||
|
||
//
|
||
// Decode the file object.
|
||
//
|
||
|
||
(VOID)FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb );
|
||
|
||
ASSERT( Vcb != NULL );
|
||
|
||
//
|
||
// Disable PopUps, we want to return any error.
|
||
//
|
||
|
||
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS);
|
||
|
||
//
|
||
// Verify the Vcb.
|
||
//
|
||
|
||
FatVerifyVcb( IrpContext, Vcb );
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
|
||
DebugTrace(-1, Dbg, "FatIsVolumeMounted -> %08lx\n", Status);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatIsPathnameValid (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines if pathname is a valid FAT pathname.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp to process
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PPATHNAME_BUFFER PathnameBuffer;
|
||
UNICODE_STRING PathName;
|
||
OEM_STRING DbcsName;
|
||
|
||
UCHAR Buffer[128];
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "FatIsPathnameValid...\n", 0);
|
||
|
||
//
|
||
// Extract the pathname and convert the path to DBCS
|
||
//
|
||
|
||
PathnameBuffer = (PPATHNAME_BUFFER)Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
PathName.Buffer = PathnameBuffer->Name;
|
||
PathName.Length = (USHORT)PathnameBuffer->PathNameLength;
|
||
|
||
//
|
||
// Check for an invalid buffer
|
||
//
|
||
|
||
if (FIELD_OFFSET(PATHNAME_BUFFER, Name[0]) + PathnameBuffer->PathNameLength >
|
||
IrpSp->Parameters.FileSystemControl.InputBufferLength) {
|
||
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
|
||
DebugTrace(-1, Dbg, "FatIsPathnameValid -> %08lx\n", Status);
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// First try to convert using our stack buffer, and allocate one if that
|
||
// doesn't work.
|
||
//
|
||
|
||
DbcsName.Buffer = &Buffer[0];
|
||
DbcsName.Length = 0;
|
||
DbcsName.MaximumLength = 128;
|
||
|
||
Status = RtlUnicodeStringToCountedOemString( &DbcsName, &PathName, FALSE );
|
||
|
||
if (Status == STATUS_BUFFER_OVERFLOW) {
|
||
|
||
DbcsName.Buffer = &Buffer[0];
|
||
DbcsName.Length = 0;
|
||
DbcsName.MaximumLength = 128;
|
||
|
||
Status = RtlUnicodeStringToCountedOemString( &DbcsName, &PathName, TRUE );
|
||
}
|
||
|
||
if ( !NT_SUCCESS( Status) ) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
|
||
DebugTrace(-1, Dbg, "FatIsPathnameValid -> %08lx\n", Status);
|
||
|
||
return Status;
|
||
}
|
||
|
||
Status = FatIsNameValid(IrpContext, DbcsName, FALSE, TRUE, TRUE ) ?
|
||
STATUS_SUCCESS : STATUS_OBJECT_NAME_INVALID;
|
||
|
||
if (DbcsName.Buffer != &Buffer[0]) {
|
||
|
||
RtlFreeOemString( &DbcsName );
|
||
}
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
|
||
DebugTrace(-1, Dbg, "FatIsPathnameValid -> %08lx\n", Status);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatQueryBpb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine simply returns the first 0x24 bytes of sector 0.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp to process
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PVCB Vcb;
|
||
|
||
PFSCTL_QUERY_FAT_BPB_BUFFER BpbBuffer;
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "FatIsPathnameValid...\n", 0);
|
||
|
||
//
|
||
// Extract the buffer
|
||
//
|
||
|
||
BpbBuffer = (PFSCTL_QUERY_FAT_BPB_BUFFER)Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
//
|
||
// Make sure the buffer is big enough.
|
||
//
|
||
|
||
if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < 0x24) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
|
||
|
||
DebugTrace(-1, Dbg, "FatIsPathnameValid -> %08lx\n", STATUS_BUFFER_TOO_SMALL );
|
||
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
//
|
||
// Get the Vcb.
|
||
//
|
||
|
||
Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb;
|
||
|
||
//
|
||
// Fill in the output buffer
|
||
//
|
||
|
||
RtlCopyMemory( BpbBuffer->First0x24BytesOfBootSector,
|
||
Vcb->First0x24BytesOfBootSector,
|
||
0x24 );
|
||
|
||
Irp->IoStatus.Information = 0x24;
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
|
||
|
||
DebugTrace(-1, Dbg, "FatIsPathnameValid -> %08lx\n", STATUS_SUCCESS);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatInvalidateVolumes (
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine searches for all the volumes mounted on the same real device
|
||
of the current DASD handle, and marks them all bad. The only operation
|
||
that can be done on such handles is cleanup and close.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp to process
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
IRP_CONTEXT IrpContext;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0};
|
||
|
||
HANDLE Handle;
|
||
|
||
PVPB NewVpb;
|
||
|
||
PLIST_ENTRY Links;
|
||
|
||
PFILE_OBJECT FileToMarkBad;
|
||
PDEVICE_OBJECT DeviceToMarkBad;
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "FatInvalidateVolumes...\n", 0);
|
||
|
||
//
|
||
// Check for the correct security access.
|
||
// The caller must have the SeTcbPrivilege.
|
||
//
|
||
|
||
if (!SeSinglePrivilegeCheck(TcbPrivilege, Irp->RequestorMode)) {
|
||
|
||
FatCompleteRequest( FatNull, Irp, STATUS_PRIVILEGE_NOT_HELD );
|
||
|
||
DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", STATUS_PRIVILEGE_NOT_HELD);
|
||
return STATUS_PRIVILEGE_NOT_HELD;
|
||
}
|
||
|
||
//
|
||
// Try to get a pointer to the device object from the handle passed in.
|
||
//
|
||
|
||
if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE)) {
|
||
|
||
FatCompleteRequest( FatNull, Irp, STATUS_INVALID_PARAMETER );
|
||
|
||
DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", STATUS_INVALID_PARAMETER);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
Handle = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
Status = ObReferenceObjectByHandle( Handle,
|
||
0,
|
||
*IoFileObjectType,
|
||
KernelMode,
|
||
&FileToMarkBad,
|
||
NULL );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
FatCompleteRequest( FatNull, Irp, Status );
|
||
|
||
DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", Status);
|
||
return Status;
|
||
|
||
} else {
|
||
|
||
//
|
||
// We only needed the pointer, not a reference.
|
||
//
|
||
|
||
ObDereferenceObject( FileToMarkBad );
|
||
|
||
//
|
||
// Grab the DeviceObject from the FileObject.
|
||
//
|
||
|
||
DeviceToMarkBad = FileToMarkBad->DeviceObject;
|
||
}
|
||
|
||
//
|
||
// Create a new Vpb for this device so that any new opens will mount
|
||
// a new volume.
|
||
//
|
||
|
||
NewVpb = ExAllocatePoolWithTag( NonPagedPoolMustSucceed,
|
||
sizeof( VPB ),
|
||
' bpV' );
|
||
RtlZeroMemory( NewVpb, sizeof( VPB ) );
|
||
NewVpb->Type = IO_TYPE_VPB;
|
||
NewVpb->Size = sizeof( VPB );
|
||
NewVpb->RealDevice = DeviceToMarkBad;
|
||
|
||
RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );
|
||
|
||
SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT );
|
||
IrpContext.MajorFunction = IrpSp->MajorFunction;
|
||
IrpContext.MinorFunction = IrpSp->MinorFunction;
|
||
|
||
FatAcquireExclusiveGlobal( &IrpContext );
|
||
|
||
//
|
||
// Nothing can go wrong now.
|
||
//
|
||
|
||
DeviceToMarkBad->Vpb = NewVpb;
|
||
|
||
//
|
||
// First acquire the FatData resource shared, then walk through all the
|
||
// mounted VCBs looking for candidates to mark BAD.
|
||
//
|
||
// On volumes we mark bad, check for dismount possibility (which is
|
||
// why we have to get the next link early).
|
||
//
|
||
|
||
Links = FatData.VcbQueue.Flink;
|
||
|
||
while (Links != &FatData.VcbQueue) {
|
||
|
||
PVCB ExistingVcb;
|
||
|
||
ExistingVcb = CONTAINING_RECORD(Links, VCB, VcbLinks);
|
||
|
||
Links = Links->Flink;
|
||
|
||
//
|
||
// If we get a match, mark the volume Bad, and also check to
|
||
// see if the volume should go away.
|
||
//
|
||
|
||
if (ExistingVcb->Vpb->RealDevice == DeviceToMarkBad) {
|
||
|
||
PVPB Vpb;
|
||
|
||
//
|
||
// Here we acquire the Vcb exclusive and try to purge
|
||
// all the open files. The idea is to have as little as
|
||
// possible stale data visible and to hasten the volume
|
||
// going away.
|
||
//
|
||
|
||
(VOID)FatAcquireExclusiveVcb( &IrpContext, ExistingVcb );
|
||
|
||
ExistingVcb->VcbCondition = VcbBad;
|
||
|
||
FatMarkFcbCondition( &IrpContext, ExistingVcb->RootDcb, FcbBad );
|
||
|
||
FatPurgeReferencedFileObjects( &IrpContext,
|
||
ExistingVcb->RootDcb,
|
||
FALSE );
|
||
|
||
//
|
||
// If the volume was not deleted, drop the resource.
|
||
//
|
||
|
||
if (Links->Blink == &ExistingVcb->VcbLinks) {
|
||
|
||
FatReleaseVcb( &IrpContext, ExistingVcb );
|
||
|
||
//
|
||
// If the volume does go away now, then we have to free
|
||
// up the Vpb as nobody else will.
|
||
//
|
||
|
||
Vpb = ExistingVcb->Vpb;
|
||
|
||
if (FatCheckForDismount( &IrpContext, ExistingVcb )) {
|
||
|
||
ExFreePool( Vpb );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
FatReleaseGlobal( &IrpContext );
|
||
|
||
FatCompleteRequest( FatNull, Irp, STATUS_SUCCESS );
|
||
|
||
DebugTrace(-1, Dbg, "FatInvalidateVolumes -> STATUS_SUCCESS\n", 0);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Local Support routine
|
||
//
|
||
|
||
BOOLEAN
|
||
FatPerformVerifyDiskRead (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN PVOID Buffer,
|
||
IN LBO Lbo,
|
||
IN ULONG NumberOfBytesToRead,
|
||
IN BOOLEAN ReturnOnError
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to read in a range of bytes from the disk. It
|
||
bypasses all of the caching and regular I/O logic, and builds and issues
|
||
the requests itself. It does this operation overriding the verify
|
||
volume flag in the device object.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Supplies the target device object or double space structure for
|
||
this operation.
|
||
|
||
Buffer - Supplies the buffer that will recieve the results of this operation
|
||
|
||
Lbo - Supplies the byte offset of where to start reading
|
||
|
||
NumberOfBytesToRead - Supplies the number of bytes to read, this must
|
||
be in multiple of bytes units acceptable to the disk driver.
|
||
|
||
ReturnOnError - Indicates that we should return on an error, instead
|
||
of raising.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if the operation succeded, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
KEVENT Event;
|
||
PIRP Irp;
|
||
LARGE_INTEGER ByteOffset;
|
||
NTSTATUS Status;
|
||
IO_STATUS_BLOCK Iosb;
|
||
|
||
DebugTrace(0, Dbg, "FatPerformVerifyDiskRead, Lbo = %08lx\n", Lbo );
|
||
|
||
//
|
||
// Initialize the event we're going to use
|
||
//
|
||
|
||
KeInitializeEvent( &Event, NotificationEvent, FALSE );
|
||
|
||
//
|
||
// Build the irp for the operation and also set the overrride flag
|
||
//
|
||
|
||
ByteOffset.QuadPart = Lbo;
|
||
|
||
Irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ,
|
||
Vcb->TargetDeviceObject,
|
||
Buffer,
|
||
NumberOfBytesToRead,
|
||
&ByteOffset,
|
||
&Event,
|
||
&Iosb );
|
||
|
||
if ( Irp == NULL ) {
|
||
|
||
FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME );
|
||
|
||
//
|
||
// Call the device to do the read and wait for it to finish.
|
||
//
|
||
// If we were called with a Vcb->Dscb, then use that.
|
||
//
|
||
|
||
#ifdef WE_WON_ON_APPEAL
|
||
Status = (Vcb->Dscb != NULL) ?
|
||
FatLowLevelDblsReadWrite( IrpContext, Irp, Vcb ) :
|
||
IoCallDriver( Vcb->TargetDeviceObject, Irp );
|
||
#else
|
||
Status = IoCallDriver( Vcb->TargetDeviceObject, Irp );
|
||
#endif // WE_WON_ON_APPEAL
|
||
|
||
if (Status == STATUS_PENDING) {
|
||
|
||
(VOID)KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL );
|
||
|
||
Status = Iosb.Status;
|
||
}
|
||
|
||
ASSERT(Status != STATUS_VERIFY_REQUIRED);
|
||
|
||
//
|
||
// Special case this error code because this probably means we used
|
||
// the wrong sector size and we want to reject STATUS_WRONG_VOLUME.
|
||
//
|
||
|
||
if (Status == STATUS_INVALID_PARAMETER) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// If it doesn't succeed then either return or raise the error.
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
if (ReturnOnError) {
|
||
|
||
return FALSE;
|
||
|
||
} else {
|
||
|
||
FatNormalizeAndRaiseStatus( IrpContext, Status );
|
||
}
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
#ifdef WE_WON_ON_APPEAL
|
||
|
||
|
||
//
|
||
// Local Support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatMountDblsVolume (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks for the existence of a double space file. If it
|
||
finds one, it attempts a mount.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the volume to attemp a double space mount on.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The result of the operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG Offset;
|
||
|
||
PBCB Bcb = NULL;
|
||
PDIRENT Dirent = NULL;
|
||
PDSCB Dscb = NULL;
|
||
PFCB CvfFcb = NULL;
|
||
PFILE_OBJECT Cvf = NULL;
|
||
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
||
|
||
OEM_STRING FileName = {0, 0, NULL};
|
||
UNICODE_STRING UnicodeFileName;
|
||
UNICODE_STRING HostName;
|
||
UNICODE_STRING NewName;
|
||
|
||
POBJECT_NAME_INFORMATION ObjectName = NULL;
|
||
|
||
PFILE_MOUNT_DBLS_BUFFER Buffer;
|
||
|
||
PVPB HostVpb;
|
||
PVPB NewVpb;
|
||
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
TYPE_OF_OPEN TypeOfOpen;
|
||
PVCB HostVcb;
|
||
PFCB Fcb;
|
||
PCCB Ccb;
|
||
|
||
PDEVICE_OBJECT HostDevice;
|
||
PDEVICE_OBJECT NewDevice = NULL;
|
||
|
||
ULONG HostNameLength;
|
||
ULONG DontCare;
|
||
|
||
PVPB CreatedVpb = NULL;
|
||
PVPB OldVpb = NULL;
|
||
PDEVICE_OBJECT CreatedDevice = NULL;
|
||
|
||
//
|
||
// Get a pointer to the current Irp stack location and HostVcb
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
//
|
||
// Decode the file object
|
||
//
|
||
|
||
TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &HostVcb, &Fcb, &Ccb );
|
||
|
||
Buffer = (PFILE_MOUNT_DBLS_BUFFER)Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
//
|
||
// Check for an invalid buffer
|
||
//
|
||
|
||
if (FIELD_OFFSET(FILE_MOUNT_DBLS_BUFFER, CvfName[0]) + Buffer->CvfNameLength >
|
||
IrpSp->Parameters.FileSystemControl.InputBufferLength) {
|
||
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
|
||
DebugTrace(-1, Dbg, "FatIsPathnameValid -> %08lx\n", Status);
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Acquire exclusive access to the Vcb and enqueue the Irp if we didn't
|
||
// get access
|
||
//
|
||
|
||
if (!FatAcquireExclusiveVcb( IrpContext, HostVcb )) {
|
||
|
||
DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0);
|
||
|
||
Status = FatFsdPostRequest( IrpContext, Irp );
|
||
|
||
DebugTrace(-1, Dbg, "FatCommonSetVolumeInfo -> %08lx\n", Status );
|
||
return Status;
|
||
}
|
||
|
||
try {
|
||
|
||
//
|
||
// Make sure the vcb is in a usable condition. This will raise
|
||
// and error condition if the volume is unusable
|
||
//
|
||
|
||
FatVerifyVcb( IrpContext, HostVcb );
|
||
|
||
//
|
||
// If the Vcb is locked then we cannot perform this mount
|
||
//
|
||
|
||
if (FlagOn(HostVcb->VcbState, VCB_STATE_FLAG_LOCKED)) {
|
||
|
||
DebugTrace(0, Dbg, "Volume is locked\n", 0);
|
||
|
||
try_return( Status = STATUS_ACCESS_DENIED );
|
||
}
|
||
|
||
//
|
||
// If this is removeable media, only a single mounted DBLS volume
|
||
// is allowed.
|
||
//
|
||
|
||
if (FlagOn(HostVcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) &&
|
||
!IsListEmpty(&HostVcb->ParentDscbLinks)) {
|
||
|
||
try_return( Status = STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
//
|
||
// Convert the UNICODE name to a Oem upcased name.
|
||
//
|
||
|
||
UnicodeFileName.Length =
|
||
UnicodeFileName.MaximumLength = (USHORT)Buffer->CvfNameLength;
|
||
UnicodeFileName.Buffer = &Buffer->CvfName[0];
|
||
|
||
Status = FatUpcaseUnicodeStringToCountedOemString( &FileName,
|
||
&UnicodeFileName,
|
||
TRUE );
|
||
|
||
|
||
if (!NT_SUCCESS( Status )) { try_return( Status ); }
|
||
|
||
//
|
||
// Make sure the name is a valid single componant fat name.
|
||
//
|
||
|
||
if (!FatIsNameValid( IrpContext, FileName, FALSE, FALSE, FALSE )) {
|
||
|
||
try_return( Status = STATUS_OBJECT_NAME_INVALID );
|
||
}
|
||
|
||
//
|
||
// See if there is already an Fcb for this name. If so try to
|
||
// make it go away. If it still doesn't, then we can't mount it.
|
||
//
|
||
|
||
Fcb = FatFindFcb( IrpContext,
|
||
&HostVcb->RootDcb->Specific.Dcb.RootOemNode,
|
||
&FileName );
|
||
|
||
if (Fcb != NULL) {
|
||
|
||
FatForceCacheMiss( IrpContext, Fcb, TRUE );
|
||
|
||
Fcb = FatFindFcb( IrpContext,
|
||
&HostVcb->RootDcb->Specific.Dcb.RootOemNode,
|
||
&FileName );
|
||
|
||
if (Fcb != NULL) {
|
||
|
||
try_return( Status = STATUS_SHARING_VIOLATION );
|
||
}
|
||
}
|
||
|
||
//
|
||
// No Fcb exists for this name, so see if there is a dirent.
|
||
//
|
||
|
||
FatLocateSimpleOemDirent( IrpContext,
|
||
HostVcb->RootDcb,
|
||
&FileName,
|
||
&Dirent,
|
||
&Bcb,
|
||
&Offset );
|
||
|
||
//
|
||
// If we couldn't find the Cvf, no dice.
|
||
//
|
||
|
||
if (Dirent == NULL) {
|
||
|
||
try_return( Status = STATUS_UNRECOGNIZED_VOLUME );
|
||
}
|
||
|
||
//
|
||
// We found one, good. Now for the real guts of the operation.
|
||
//
|
||
// Create the Cvf Fcb.
|
||
//
|
||
|
||
CvfFcb = FatCreateFcb( IrpContext,
|
||
HostVcb,
|
||
HostVcb->RootDcb,
|
||
Offset,
|
||
Offset,
|
||
Dirent,
|
||
NULL,
|
||
FALSE );
|
||
|
||
SetFlag( CvfFcb->FcbState, FCB_STATE_COMPRESSED_VOLUME_FILE );
|
||
|
||
//
|
||
// Deny anybody who trys to open the file.
|
||
//
|
||
|
||
SetFlag( CvfFcb->FcbState, FCB_STATE_SYSTEM_FILE );
|
||
|
||
//
|
||
// Set up the share access so that no other opens will be
|
||
// allowed to this file.
|
||
//
|
||
|
||
Cvf = IoCreateStreamFileObject( NULL, HostVcb->Vpb->RealDevice );
|
||
|
||
IoSetShareAccess( FILE_READ_DATA | FILE_WRITE_DATA | DELETE,
|
||
0,
|
||
Cvf,
|
||
&CvfFcb->ShareAccess );
|
||
|
||
FatSetFileObject( Cvf,
|
||
EaFile,
|
||
CvfFcb,
|
||
NULL );
|
||
|
||
Cvf->SectionObjectPointer = &CvfFcb->NonPaged->SectionObjectPointers;
|
||
|
||
//
|
||
// Now attempt a double space pre-mount. If there are any
|
||
// problems this routine will raise.
|
||
//
|
||
|
||
FatDblsPreMount( IrpContext,
|
||
&Dscb,
|
||
Cvf,
|
||
Dirent->FileSize );
|
||
|
||
//
|
||
// OK, it looks like a DBLS volume, so go ahead and continue.
|
||
// First we construct the wanna-be real device object by cloning
|
||
// all the parameters on the host real device object.
|
||
//
|
||
// The name is the host name + '.' + CvfFileName. For instance
|
||
// if the host device is \Nt\Device\HardDisk0\Partition1 and the
|
||
// Cvf name is DBLSPACE.000, then the wanna-be device object's
|
||
// name is \Nt\Device\HardDisk0\Partition1.DBLSPACE.000
|
||
//
|
||
|
||
HostDevice = HostVcb->Vpb->RealDevice;
|
||
|
||
ObQueryNameString( HostDevice, NULL, 0, &HostNameLength );
|
||
|
||
ObjectName = FsRtlAllocatePool( PagedPool,
|
||
HostNameLength +
|
||
sizeof(WCHAR) +
|
||
UnicodeFileName.Length );
|
||
|
||
if (!NT_SUCCESS( Status = ObQueryNameString( HostDevice,
|
||
ObjectName,
|
||
HostNameLength,
|
||
&DontCare ) )) {
|
||
|
||
try_return( Status );
|
||
}
|
||
|
||
ASSERT( HostNameLength == DontCare );
|
||
|
||
HostName = ObjectName->Name;
|
||
|
||
NewName.Length =
|
||
NewName.MaximumLength = HostName.Length +
|
||
sizeof(WCHAR) +
|
||
UnicodeFileName.Length;
|
||
NewName.Buffer = HostName.Buffer;
|
||
|
||
NewName.Buffer[HostName.Length/sizeof(WCHAR)] = L'.';
|
||
|
||
RtlCopyMemory( &NewName.Buffer[HostName.Length/sizeof(WCHAR) + 1],
|
||
UnicodeFileName.Buffer,
|
||
UnicodeFileName.Length );
|
||
|
||
//
|
||
// Go ahead and try to create the device.
|
||
//
|
||
|
||
Status = IoCreateDevice( HostDevice->DriverObject,
|
||
0,
|
||
&NewName,
|
||
HostDevice->DeviceType,
|
||
HostDevice->Characteristics,
|
||
BooleanFlagOn(HostDevice->Flags, DO_EXCLUSIVE),
|
||
&NewDevice );
|
||
|
||
CreatedDevice = NewDevice;
|
||
|
||
#ifdef _PNP_POWER_
|
||
if (NT_SUCCESS(Status)) {
|
||
//
|
||
// This driver doesn't talk directly to a device, and (at the moment)
|
||
// isn't otherwise concerned about power management.
|
||
//
|
||
|
||
NewDevice->DeviceObjectExtension->PowerControlNeeded = FALSE;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// We got a colision, so there must be another DeviceObject with
|
||
// the same name.
|
||
//
|
||
|
||
if (Status == STATUS_OBJECT_NAME_COLLISION) {
|
||
|
||
//ASSERT(FlagOn(HostVcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA));
|
||
|
||
NewDevice = (PDEVICE_OBJECT)HostDevice->ActiveThreadCount;
|
||
|
||
//
|
||
// If we get here without the NewDevice set, then this is
|
||
// not removeable media, ie. not auto-mount.
|
||
//
|
||
|
||
if (NewDevice == NULL) {
|
||
|
||
try_return( Status );
|
||
}
|
||
|
||
ASSERT( NewDevice );
|
||
|
||
//
|
||
// If a volume is currently mounted on this Vpb, we have got to
|
||
// create a new Vpb.
|
||
//
|
||
|
||
if (FlagOn(NewDevice->Vpb->Flags, VPB_MOUNTED)) {
|
||
|
||
CreatedVpb = FsRtlAllocatePool( NonPagedPool, sizeof(VPB) );
|
||
OldVpb = NewDevice->Vpb;
|
||
|
||
RtlZeroMemory( CreatedVpb, sizeof( VPB ) );
|
||
CreatedVpb->Type = IO_TYPE_VPB;
|
||
CreatedVpb->Size = sizeof( VPB );
|
||
CreatedVpb->RealDevice = OldVpb->RealDevice;
|
||
NewDevice->Vpb = CreatedVpb;
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
DbgBreakPoint();
|
||
|
||
try_return( Status );
|
||
}
|
||
|
||
|
||
//
|
||
// Setting the below flag will cause the device object reference
|
||
// count to be decremented BEFORE calling our close routine.
|
||
//
|
||
|
||
SetFlag(NewDevice->Flags, DO_NEVER_LAST_DEVICE);
|
||
|
||
Dscb->NewDevice = NewDevice;
|
||
|
||
//
|
||
// Cool. Now we are going to trick everybody who get the real
|
||
// device from the Vpb to actually get the really "real" device.
|
||
//
|
||
|
||
NewVpb = NewDevice->Vpb;
|
||
HostVpb = HostVcb->Vpb;
|
||
|
||
NewVpb->RealDevice = HostVpb->RealDevice;
|
||
|
||
//
|
||
// At this point we go for a full fledged mount!
|
||
//
|
||
|
||
Status = FatMountVolume( IrpContext,
|
||
HostVcb->TargetDeviceObject,
|
||
NewVpb,
|
||
Dscb );
|
||
|
||
if (!NT_SUCCESS( Status )) { try_return( Status ); }
|
||
|
||
//
|
||
// Way radical dude, it worked. Add the few finishing touches
|
||
// that the Io System usually does, and we're ready to party.
|
||
//
|
||
|
||
NewVpb->Flags = VPB_MOUNTED;
|
||
NewVpb->DeviceObject->StackSize = HostVpb->DeviceObject->StackSize;
|
||
|
||
if (OldVpb) {
|
||
|
||
ClearFlag( OldVpb->Flags, VPB_PERSISTENT );
|
||
}
|
||
|
||
ClearFlag(NewDevice->Flags, DO_DEVICE_INITIALIZING);
|
||
|
||
if (FlagOn(HostVcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) {
|
||
|
||
HostDevice->ActiveThreadCount = (ULONG)NewDevice;
|
||
}
|
||
|
||
//
|
||
// Finally, register this Dscb with Vcb.
|
||
//
|
||
|
||
InsertTailList( &HostVcb->ParentDscbLinks, &Dscb->ChildDscbLinks );
|
||
Dscb->ParentVcb = HostVcb;
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
//
|
||
// If the anything above was not successful, backout eveything.
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status) || AbnormalTermination()) {
|
||
|
||
if (CreatedVpb) {
|
||
|
||
ExFreePool( CreatedVpb );
|
||
NewDevice->Vpb = OldVpb;
|
||
}
|
||
|
||
if (CreatedDevice) {
|
||
|
||
ExFreePool( CreatedDevice->Vpb );
|
||
IoDeleteDevice( CreatedDevice );
|
||
}
|
||
|
||
if (Dscb) {
|
||
|
||
//
|
||
// Cleanup the cache map of the cvf file object.
|
||
//
|
||
|
||
FatSyncUninitializeCacheMap( IrpContext, Dscb->CvfFileObject );
|
||
|
||
#ifdef DOUBLE_SPACE_WRITE
|
||
|
||
//
|
||
// Delete the resource
|
||
//
|
||
|
||
FatDeleteResource( Dscb->Resource );
|
||
|
||
ExFreePool( Dscb->Resource );
|
||
|
||
#endif // DOUBLE_SPACE_WRITE
|
||
|
||
//
|
||
// And free the pool.
|
||
//
|
||
|
||
ExFreePool( Dscb );
|
||
}
|
||
|
||
if (Cvf) {
|
||
|
||
FatSetFileObject( Cvf, UnopenedFileObject, NULL, NULL );
|
||
ObDereferenceObject( Cvf );
|
||
}
|
||
|
||
if (CvfFcb) {
|
||
|
||
FatDeleteFcb( IrpContext, CvfFcb );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Always unpin the Bcb, free some pool, and release the resource.
|
||
//
|
||
|
||
if (ObjectName != NULL) { ExFreePool( ObjectName ); }
|
||
|
||
if (Bcb != NULL) { FatUnpinBcb( IrpContext, Bcb ); }
|
||
|
||
FatFreeOemString( &FileName );
|
||
|
||
FatReleaseVcb( IrpContext, HostVcb );
|
||
|
||
//
|
||
// If we aren't raising out of here, complete the request.
|
||
//
|
||
|
||
if (!AbnormalTermination()) {
|
||
|
||
FatCompleteRequest(IrpContext, Irp, Status);
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Local Support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatAutoMountDblsVolume (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVPB HostVpb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks for the existence of a double space file. If it
|
||
finds one, it attempts a mount.
|
||
|
||
Arguments:
|
||
|
||
HostVpb - Supplies the volume to attemp a double space mount on.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The result of the operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG Offset;
|
||
|
||
PBCB Bcb = NULL;
|
||
PDIRENT Dirent = NULL;
|
||
PDSCB Dscb = NULL;
|
||
PFCB CvfFcb = NULL;
|
||
PFILE_OBJECT Cvf = NULL;
|
||
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
||
|
||
OEM_STRING FileName = {12, 12, "DBLSPACE.000"};
|
||
UNICODE_STRING HostName;
|
||
UNICODE_STRING NewName;
|
||
|
||
POBJECT_NAME_INFORMATION ObjectName = NULL;
|
||
|
||
PVPB NewVpb;
|
||
|
||
PVCB HostVcb;
|
||
PVCB NewVcb;
|
||
|
||
PDEVICE_OBJECT HostDevice;
|
||
PDEVICE_OBJECT NewDevice = NULL;
|
||
|
||
ULONG HostNameLength;
|
||
ULONG DontCare;
|
||
|
||
PVPB CreatedVpb = NULL;
|
||
PVPB OldVpb = NULL;
|
||
PDEVICE_OBJECT CreatedDevice = NULL;
|
||
|
||
HostVcb = &((PVOLUME_DEVICE_OBJECT)HostVpb->DeviceObject)->Vcb;
|
||
|
||
try {
|
||
|
||
//
|
||
// No Fcb exists for this name, so see if there is a dirent.
|
||
//
|
||
|
||
FatLocateSimpleOemDirent( IrpContext,
|
||
HostVcb->RootDcb,
|
||
&FileName,
|
||
&Dirent,
|
||
&Bcb,
|
||
&Offset );
|
||
|
||
//
|
||
// If we couldn't find the Cvf, no dice.
|
||
//
|
||
|
||
if (Dirent == NULL) {
|
||
|
||
try_return( Status = STATUS_UNRECOGNIZED_VOLUME );
|
||
}
|
||
|
||
//
|
||
// We found one, good. Now for the real guts of the operation.
|
||
//
|
||
// Create the Cvf Fcb.
|
||
//
|
||
|
||
CvfFcb = FatCreateFcb( IrpContext,
|
||
HostVcb,
|
||
HostVcb->RootDcb,
|
||
Offset,
|
||
Offset,
|
||
Dirent,
|
||
NULL,
|
||
FALSE );
|
||
|
||
SetFlag( CvfFcb->FcbState, FCB_STATE_COMPRESSED_VOLUME_FILE );
|
||
|
||
//
|
||
// Deny anybody who trys to open the file.
|
||
//
|
||
|
||
SetFlag( CvfFcb->FcbState, FCB_STATE_SYSTEM_FILE );
|
||
|
||
//
|
||
// Set up the share access so that no other opens will be
|
||
// allowed to this file.
|
||
//
|
||
|
||
Cvf = IoCreateStreamFileObject( NULL, HostVcb->Vpb->RealDevice );
|
||
|
||
IoSetShareAccess( FILE_READ_DATA | FILE_WRITE_DATA | DELETE,
|
||
0,
|
||
Cvf,
|
||
&CvfFcb->ShareAccess );
|
||
|
||
FatSetFileObject( Cvf,
|
||
EaFile,
|
||
CvfFcb,
|
||
NULL );
|
||
|
||
Cvf->SectionObjectPointer = &CvfFcb->NonPaged->SectionObjectPointers;
|
||
|
||
//
|
||
// Now attempt a double space pre-mount. If there are any
|
||
// problems this routine will raise.
|
||
//
|
||
|
||
FatDblsPreMount( IrpContext,
|
||
&Dscb,
|
||
Cvf,
|
||
Dirent->FileSize );
|
||
|
||
//
|
||
// OK, it looks like a DBLS volume, so go ahead and continue.
|
||
// Since this is the automount case, we create a new real device
|
||
// to hold the host volume.
|
||
//
|
||
|
||
HostDevice = HostVcb->Vpb->RealDevice;
|
||
|
||
ObQueryNameString( HostDevice, NULL, 0, &HostNameLength );
|
||
|
||
ObjectName = FsRtlAllocatePool( PagedPool,
|
||
HostNameLength +
|
||
5 * sizeof(WCHAR) );
|
||
|
||
if (!NT_SUCCESS( Status = ObQueryNameString( HostDevice,
|
||
ObjectName,
|
||
HostNameLength,
|
||
&DontCare ) )) {
|
||
|
||
try_return( Status );
|
||
}
|
||
|
||
ASSERT( HostNameLength == DontCare );
|
||
|
||
HostName = ObjectName->Name;
|
||
|
||
NewName.Length =
|
||
NewName.MaximumLength = HostName.Length +
|
||
5 * sizeof(WCHAR);
|
||
NewName.Buffer = HostName.Buffer;
|
||
|
||
RtlCopyMemory( &NewName.Buffer[HostName.Length/sizeof(WCHAR)],
|
||
L".Host",
|
||
5 * sizeof(WCHAR) );
|
||
|
||
//
|
||
// Go ahead and try to create the device.
|
||
//
|
||
|
||
Status = IoCreateDevice( HostDevice->DriverObject,
|
||
0,
|
||
&NewName,
|
||
HostDevice->DeviceType,
|
||
HostDevice->Characteristics,
|
||
BooleanFlagOn(HostDevice->Flags, DO_EXCLUSIVE),
|
||
&NewDevice );
|
||
|
||
CreatedDevice = NewDevice;
|
||
|
||
#ifdef _PNP_POWER_
|
||
if (NT_SUCCESS(Status)) {
|
||
//
|
||
// This driver doesn't talk directly to a device, and (at the moment)
|
||
// isn't otherwise concerned about power management.
|
||
//
|
||
|
||
NewDevice->DeviceObjectExtension->PowerControlNeeded = FALSE;
|
||
}
|
||
#endif
|
||
|
||
if (Status == STATUS_OBJECT_NAME_COLLISION) {
|
||
|
||
NewDevice = (PDEVICE_OBJECT)HostDevice->ActiveThreadCount;
|
||
|
||
//
|
||
// If we get here without the NewDevice set, then this is
|
||
// not removeable media, ie. not auto-mount.
|
||
//
|
||
|
||
if (NewDevice == NULL) {
|
||
|
||
try_return( Status );
|
||
}
|
||
|
||
ASSERT( NewDevice );
|
||
|
||
//
|
||
// If a volume is currently mounted on this Vpb, we have got to
|
||
// create a new Vpb.
|
||
//
|
||
|
||
if (FlagOn(NewDevice->Vpb->Flags, VPB_MOUNTED)) {
|
||
|
||
CreatedVpb = FsRtlAllocatePool( NonPagedPool, sizeof(VPB) );
|
||
OldVpb = NewDevice->Vpb;
|
||
|
||
RtlZeroMemory( CreatedVpb, sizeof( VPB ) );
|
||
CreatedVpb->Type = IO_TYPE_VPB;
|
||
CreatedVpb->Size = sizeof( VPB );
|
||
CreatedVpb->RealDevice = OldVpb->RealDevice;
|
||
NewDevice->Vpb = CreatedVpb;
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
DbgBreakPoint();
|
||
|
||
try_return( Status );
|
||
}
|
||
|
||
ASSERT( NewDevice->Vpb->ReferenceCount == 0 );
|
||
|
||
//
|
||
// Setting the below flag will cause the device object reference
|
||
// count to be decremented BEFORE calling our close routine.
|
||
//
|
||
|
||
SetFlag(NewDevice->Flags, DO_NEVER_LAST_DEVICE);
|
||
|
||
Dscb->NewDevice = NewDevice;
|
||
|
||
//
|
||
// Cool. Now we are going to trick everybody who get the real
|
||
// device from the Vpb to actually get the really "real" device.
|
||
//
|
||
|
||
NewVpb = NewDevice->Vpb;
|
||
HostVpb = HostVcb->Vpb;
|
||
|
||
NewVpb->RealDevice = HostVpb->RealDevice;
|
||
|
||
//
|
||
// At this point we go for a full fledged mount!
|
||
//
|
||
|
||
Status = FatMountVolume( IrpContext,
|
||
HostVcb->TargetDeviceObject,
|
||
NewVpb,
|
||
Dscb );
|
||
|
||
if (!NT_SUCCESS( Status )) { try_return( Status ); }
|
||
|
||
//
|
||
// Way radical dude, it worked. Add the few finishing touches
|
||
// that the Io System usually does, and we're ready to party.
|
||
//
|
||
|
||
NewVcb = &((PVOLUME_DEVICE_OBJECT)NewVpb->DeviceObject)->Vcb;
|
||
|
||
HostVpb->Flags = VPB_MOUNTED | VPB_PERSISTENT;
|
||
HostVpb->DeviceObject->StackSize = HostVpb->DeviceObject->StackSize;
|
||
|
||
if (OldVpb) {
|
||
|
||
ClearFlag( OldVpb->Flags, VPB_PERSISTENT );
|
||
}
|
||
|
||
ClearFlag(NewDevice->Flags, DO_DEVICE_INITIALIZING);
|
||
HostDevice->ActiveThreadCount = (ULONG)NewDevice;
|
||
|
||
//
|
||
// Register this Dscb with Vcb.
|
||
//
|
||
|
||
InsertTailList( &HostVcb->ParentDscbLinks, &Dscb->ChildDscbLinks );
|
||
Dscb->ParentVcb = HostVcb;
|
||
|
||
//
|
||
// Now we swap Vpb->Device pointers so that the new compressed
|
||
// volume is the default. On a failed verify, these will be
|
||
// swapped back.
|
||
//
|
||
|
||
HostDevice->Vpb = NewVpb;
|
||
NewDevice->Vpb = HostVpb;
|
||
|
||
HostDevice->ReferenceCount -= HostVpb->ReferenceCount;
|
||
HostDevice->ReferenceCount += NewVpb->ReferenceCount;
|
||
|
||
NewDevice->ReferenceCount -= NewVpb->ReferenceCount;
|
||
NewDevice->ReferenceCount += HostVpb->ReferenceCount;
|
||
|
||
HostVcb->CurrentDevice = NewDevice;
|
||
NewVcb->CurrentDevice = HostDevice;
|
||
|
||
//
|
||
// Now exactly five stream files (3 on the host and 2 on the new
|
||
// volume) were created. We have to go and fix all the
|
||
// FileObject->DeviceObject fields so that the correct count
|
||
// is decremented when the file object is closed.
|
||
//
|
||
|
||
ASSERT( HostVcb->VirtualVolumeFile->DeviceObject == HostDevice );
|
||
ASSERT( HostVcb->RootDcb->Specific.Dcb.DirectoryFile->DeviceObject == HostDevice );
|
||
|
||
ASSERT( NewVcb->VirtualVolumeFile->DeviceObject == NewDevice );
|
||
ASSERT( NewVcb->RootDcb->Specific.Dcb.DirectoryFile->DeviceObject == NewDevice );
|
||
|
||
ASSERT( NewVcb->Dscb->CvfFileObject->DeviceObject == HostDevice );
|
||
|
||
HostVcb->VirtualVolumeFile->DeviceObject = NewDevice;
|
||
HostVcb->RootDcb->Specific.Dcb.DirectoryFile->DeviceObject = NewDevice;
|
||
|
||
NewVcb->VirtualVolumeFile->DeviceObject = HostDevice;
|
||
NewVcb->RootDcb->Specific.Dcb.DirectoryFile->DeviceObject = HostDevice;
|
||
|
||
NewVcb->Dscb->CvfFileObject->DeviceObject = NewDevice;
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
//
|
||
// If the anything above was not successful, backout eveything.
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status) || AbnormalTermination()) {
|
||
|
||
if (CreatedVpb) {
|
||
|
||
ExFreePool( CreatedVpb );
|
||
NewDevice->Vpb = OldVpb;
|
||
}
|
||
|
||
if (CreatedDevice) {
|
||
|
||
ExFreePool( CreatedDevice->Vpb );
|
||
IoDeleteDevice( CreatedDevice );
|
||
}
|
||
|
||
if (Dscb) {
|
||
|
||
//
|
||
// Cleanup the cache map of the cvf file object.
|
||
//
|
||
|
||
FatSyncUninitializeCacheMap( IrpContext, Dscb->CvfFileObject );
|
||
|
||
#ifdef DOUBLE_SPACE_WRITE
|
||
|
||
//
|
||
// Delete the resource
|
||
//
|
||
|
||
FatDeleteResource( Dscb->Resource );
|
||
|
||
ExFreePool( Dscb->Resource );
|
||
|
||
#endif // DOUBLE_SPACE_WRITE
|
||
|
||
//
|
||
// And free the pool.
|
||
//
|
||
|
||
ExFreePool( Dscb );
|
||
}
|
||
|
||
if (Cvf) {
|
||
|
||
FatSetFileObject( Cvf, UnopenedFileObject, NULL, NULL );
|
||
ObDereferenceObject( Cvf );
|
||
}
|
||
|
||
if (CvfFcb) {
|
||
|
||
FatDeleteFcb( IrpContext, CvfFcb );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Always unpin the Bcb, free some pool, and release the resource.
|
||
//
|
||
|
||
if (ObjectName != NULL) { ExFreePool( ObjectName ); }
|
||
|
||
if (Bcb != NULL) { FatUnpinBcb( IrpContext, Bcb ); }
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Local Support routine
|
||
//
|
||
|
||
BOOLEAN
|
||
FatIsAutoMountEnabled (
|
||
IN PIRP_CONTEXT IrpContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine reads the registry and determines if automounting of
|
||
removable media is currently enabled.
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE is enabled, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
ULONG Value;
|
||
UNICODE_STRING ValueName;
|
||
|
||
ValueName.Buffer = DOUBLE_SPACE_VALUE_NAME;
|
||
ValueName.Length = sizeof(DOUBLE_SPACE_VALUE_NAME) - sizeof(WCHAR);
|
||
ValueName.MaximumLength = sizeof(DOUBLE_SPACE_VALUE_NAME);
|
||
|
||
Status = FatGetDoubleSpaceConfigurationValue( IrpContext,
|
||
&ValueName,
|
||
&Value );
|
||
|
||
if (NT_SUCCESS(Status) && FlagOn(Value, 1)) {
|
||
|
||
return TRUE;
|
||
|
||
} else {
|
||
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatGetDoubleSpaceConfigurationValue(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PUNICODE_STRING ValueName,
|
||
IN OUT PULONG Value
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given a unicode value name this routine will go into the registry
|
||
location for double space configuation information and get the
|
||
value.
|
||
|
||
Arguments:
|
||
|
||
ValueName - the unicode name for the registry value located in the
|
||
double space configuration location of the registry.
|
||
Value - a pointer to the ULONG for the result.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
If STATUS_SUCCESSFUL is returned, the location *Value will be
|
||
updated with the DWORD value from the registry. If any failing
|
||
status is returned, this value is untouched.
|
||
|
||
--*/
|
||
|
||
{
|
||
HANDLE Handle;
|
||
NTSTATUS Status;
|
||
ULONG RequestLength;
|
||
ULONG ResultLength;
|
||
UCHAR Buffer[KEY_WORK_AREA];
|
||
UNICODE_STRING KeyName;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
|
||
|
||
KeyName.Buffer = DOUBLE_SPACE_KEY_NAME;
|
||
KeyName.Length = sizeof(DOUBLE_SPACE_KEY_NAME) - sizeof(WCHAR);
|
||
KeyName.MaximumLength = sizeof(DOUBLE_SPACE_KEY_NAME);
|
||
|
||
|
||
InitializeObjectAttributes(&ObjectAttributes,
|
||
&KeyName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL);
|
||
|
||
Status = ZwOpenKey(&Handle,
|
||
KEY_READ,
|
||
&ObjectAttributes);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
RequestLength = KEY_WORK_AREA;
|
||
|
||
KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION)Buffer;
|
||
|
||
while (1) {
|
||
|
||
Status = ZwQueryValueKey(Handle,
|
||
ValueName,
|
||
KeyValueFullInformation,
|
||
KeyValueInformation,
|
||
RequestLength,
|
||
&ResultLength);
|
||
|
||
ASSERT( Status != STATUS_BUFFER_OVERFLOW );
|
||
|
||
if (Status == STATUS_BUFFER_OVERFLOW) {
|
||
|
||
//
|
||
// Try to get a buffer big enough.
|
||
//
|
||
|
||
if (KeyValueInformation != (PKEY_VALUE_FULL_INFORMATION)Buffer) {
|
||
|
||
ExFreePool(KeyValueInformation);
|
||
}
|
||
|
||
RequestLength += 256;
|
||
|
||
KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION)
|
||
ExAllocatePoolWithTag(PagedPool,
|
||
RequestLength,
|
||
' taF');
|
||
|
||
if (!KeyValueInformation) {
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
} else {
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
ZwClose(Handle);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
if (KeyValueInformation->DataLength != 0) {
|
||
|
||
PULONG DataPtr;
|
||
|
||
//
|
||
// Return contents to the caller.
|
||
//
|
||
|
||
DataPtr = (PULONG)
|
||
((PUCHAR)KeyValueInformation + KeyValueInformation->DataOffset);
|
||
*Value = *DataPtr;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Treat as if no value was found
|
||
//
|
||
|
||
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
||
}
|
||
}
|
||
|
||
if (KeyValueInformation != (PKEY_VALUE_FULL_INFORMATION)Buffer) {
|
||
|
||
ExFreePool(KeyValueInformation);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
#endif // WE_WON_ON_APPEAL
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatQueryRetrievalPointers (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine performs the query retrieval pointers operation.
|
||
It returns the retrieval pointers for the specified input
|
||
file from the start of the file to the request map size specified
|
||
in the input buffer.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp to process
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PVCB Vcb;
|
||
PFCB Fcb;
|
||
PCCB Ccb;
|
||
|
||
PLARGE_INTEGER RequestedMapSize;
|
||
PLARGE_INTEGER *MappingPairs;
|
||
|
||
ULONG Index;
|
||
ULONG i;
|
||
ULONG SectorCount;
|
||
ULONG Lbo;
|
||
ULONG Vbo;
|
||
ULONG MapSize;
|
||
|
||
//
|
||
// Only Kernel mode clients may query retrieval pointer information about
|
||
// a file, and then only the paging file. Ensure that this is the case
|
||
// for this caller.
|
||
//
|
||
|
||
if ( Irp->RequestorMode != KernelMode ) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Get the current stack location
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
//
|
||
// Decode the file object and ensure that it is the paging file
|
||
//
|
||
//
|
||
|
||
(VOID)FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb );
|
||
|
||
if ( !FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) ) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Extract the input and output buffer information. The input contains
|
||
// the requested size of the mappings in terms of VBO. The output
|
||
// parameter will receive a pointer to nonpaged pool where the mapping
|
||
// pairs are stored.
|
||
//
|
||
|
||
ASSERT( IrpSp->Parameters.FileSystemControl.InputBufferLength == sizeof(LARGE_INTEGER) );
|
||
ASSERT( IrpSp->Parameters.FileSystemControl.OutputBufferLength == sizeof(PVOID) );
|
||
|
||
RequestedMapSize = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
|
||
MappingPairs = Irp->UserBuffer;
|
||
|
||
//
|
||
// Acquire exclusive access to the Fcb
|
||
//
|
||
|
||
if (!FatAcquireExclusiveFcb( IrpContext, Fcb )) {
|
||
|
||
return FatFsdPostRequest( IrpContext, Irp );
|
||
}
|
||
|
||
try {
|
||
|
||
//
|
||
// Check if the mapping the caller requested is too large
|
||
//
|
||
|
||
if ((*RequestedMapSize).QuadPart > Fcb->Header.FileSize.QuadPart) {
|
||
|
||
try_return( Status = STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
//
|
||
// Now get the index for the mcb entry that will contain the
|
||
// callers request and allocate enough pool to hold the
|
||
// output mapping pairs
|
||
//
|
||
|
||
(VOID)FsRtlLookupMcbEntry( &Fcb->Mcb, RequestedMapSize->LowPart - 1, &Lbo, NULL, &Index );
|
||
|
||
*MappingPairs = FsRtlAllocatePool( NonPagedPool, (Index + 2) * (2 * sizeof(LARGE_INTEGER)) );
|
||
|
||
//
|
||
// Now copy over the mapping pairs from the mcb
|
||
// to the output buffer. We store in [sector count, lbo]
|
||
// mapping pairs and end with a zero sector count.
|
||
//
|
||
|
||
MapSize = RequestedMapSize->LowPart;
|
||
|
||
for (i = 0; i <= Index; i += 1) {
|
||
|
||
(VOID)FsRtlGetNextMcbEntry( &Fcb->Mcb, i, &Vbo, &Lbo, &SectorCount );
|
||
|
||
if (SectorCount > MapSize) {
|
||
SectorCount = MapSize;
|
||
}
|
||
|
||
(*MappingPairs)[ i*2 + 0 ].QuadPart = SectorCount;
|
||
(*MappingPairs)[ i*2 + 1 ].QuadPart = Lbo;
|
||
|
||
MapSize -= SectorCount;
|
||
}
|
||
|
||
(*MappingPairs)[ i*2 + 0 ].QuadPart = 0;
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
DebugUnwind( FatQueryRetrievalPointers );
|
||
|
||
//
|
||
// Release all of our resources
|
||
//
|
||
|
||
FatReleaseFcb( IrpContext, Fcb );
|
||
|
||
//
|
||
// If this is an abnormal termination then undo our work, otherwise
|
||
// complete the irp
|
||
//
|
||
|
||
if (!AbnormalTermination()) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatGetStatistics (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns the filesystem performance counters from the
|
||
appropriate VCB.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp to process
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PVCB Vcb;
|
||
|
||
PFILESYSTEM_STATISTICS Stats;
|
||
ULONG StatsSize;
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "FatGetStatistics...\n", 0);
|
||
|
||
//
|
||
// Extract the buffer
|
||
//
|
||
|
||
Stats = (PFILESYSTEM_STATISTICS)Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
//
|
||
// Make sure the buffer is big enough.
|
||
//
|
||
|
||
StatsSize = sizeof(FILESYSTEM_STATISTICS) * *KeNumberProcessors;
|
||
|
||
if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < StatsSize) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
|
||
|
||
DebugTrace(-1, Dbg, "FatGetStatistics -> %08lx\n", STATUS_BUFFER_TOO_SMALL );
|
||
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
//
|
||
// Get the Vcb.
|
||
//
|
||
|
||
Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb;
|
||
|
||
//
|
||
// Fill in the output buffer
|
||
//
|
||
|
||
RtlCopyMemory( Stats, Vcb->Statistics, StatsSize );
|
||
|
||
Irp->IoStatus.Information = StatsSize;
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
|
||
|
||
DebugTrace(-1, Dbg, "FatGetStatistics -> %08lx\n", STATUS_SUCCESS);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatGetVolumeBitmap(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns the volume allocation bitmap.
|
||
|
||
Input = the STARTING_LCN_INPUT_BUFFER data structure is passed in
|
||
through the input buffer.
|
||
Output = the VOLUME_BITMAP_BUFFER data structure is returned through
|
||
the output buffer.
|
||
|
||
We return as much as the user buffer allows starting the specified input
|
||
LCN (trucated to a byte). If there is no input buffer, we start at zero.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp being processed.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PVCB Vcb;
|
||
PFCB Fcb;
|
||
PCCB Ccb;
|
||
|
||
ULONG BytesToCopy;
|
||
ULONG TotalClusters;
|
||
ULONG DesiredClusters;
|
||
ULONG StartingCluster;
|
||
ULONG InputBufferLength;
|
||
ULONG OutputBufferLength;
|
||
LARGE_INTEGER StartingLcn;
|
||
PVOLUME_BITMAP_BUFFER OutputBuffer;
|
||
|
||
//
|
||
// Get the current Irp stack location and save some references.
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "FatGetVolumeBitmap, FsControlCode = %08lx\n",
|
||
IrpSp->Parameters.FileSystemControl.FsControlCode);
|
||
|
||
//
|
||
// Extract and decode the file object and check for type of open.
|
||
//
|
||
|
||
if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
|
||
OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
|
||
|
||
OutputBuffer = (PVOLUME_BITMAP_BUFFER)FatMapUserBuffer( IrpContext, Irp );
|
||
|
||
//
|
||
// Check for a minimum length on the input and output buffers.
|
||
//
|
||
|
||
if ((InputBufferLength < sizeof(STARTING_LCN_INPUT_BUFFER)) ||
|
||
(OutputBufferLength < sizeof(VOLUME_BITMAP_BUFFER))) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
//
|
||
// Check if a starting cluster was specified.
|
||
//
|
||
|
||
TotalClusters = Vcb->AllocationSupport.NumberOfClusters;
|
||
|
||
//
|
||
// Check for a valid buffers
|
||
//
|
||
|
||
try {
|
||
|
||
ProbeForRead( IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
|
||
InputBufferLength,
|
||
sizeof(UCHAR) );
|
||
|
||
ProbeForWrite( OutputBuffer, OutputBufferLength, sizeof(UCHAR) );
|
||
|
||
StartingLcn = ((PSTARTING_LCN_INPUT_BUFFER)IrpSp->Parameters.FileSystemControl.Type3InputBuffer)->StartingLcn;
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
Status = GetExceptionCode();
|
||
|
||
FatRaiseStatus( IrpContext,
|
||
FsRtlIsNtstatusExpected(Status) ?
|
||
Status : STATUS_INVALID_USER_BUFFER );
|
||
}
|
||
|
||
if (StartingLcn.HighPart || StartingLcn.LowPart >= TotalClusters) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
return STATUS_INVALID_PARAMETER;
|
||
|
||
} else {
|
||
|
||
StartingCluster = StartingLcn.LowPart & ~7;
|
||
}
|
||
|
||
//
|
||
// Acquire exclusive access to the Vcb and enqueue the Irp if we
|
||
// didn't get access.
|
||
//
|
||
|
||
if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {
|
||
|
||
DebugTrace( 0, Dbg, "Cannot acquire Vcb\n", 0);
|
||
|
||
Status = FatFsdPostRequest( IrpContext, Irp );
|
||
|
||
DebugTrace(-1, Dbg, "FatGetVolumeBitmap -> %08lx\n", Status);
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Only return what will fit in the user buffer.
|
||
//
|
||
|
||
OutputBufferLength -= FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer);
|
||
DesiredClusters = TotalClusters - StartingCluster;
|
||
|
||
if (OutputBufferLength < (DesiredClusters + 7) / 8) {
|
||
|
||
BytesToCopy = OutputBufferLength;
|
||
Status = STATUS_BUFFER_OVERFLOW;
|
||
|
||
} else {
|
||
|
||
BytesToCopy = (DesiredClusters + 7) / 8;
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
try {
|
||
|
||
//
|
||
// Fill in the fixed part of the output buffer
|
||
//
|
||
|
||
OutputBuffer->StartingLcn.QuadPart = StartingCluster;
|
||
OutputBuffer->BitmapSize.QuadPart = DesiredClusters;
|
||
|
||
//
|
||
// Now copy the volume bitmap into the user buffer.
|
||
//
|
||
|
||
ASSERT( Vcb->FreeClusterBitMap.Buffer != NULL );
|
||
|
||
RtlCopyMemory( &OutputBuffer->Buffer[0],
|
||
(PUCHAR)Vcb->FreeClusterBitMap.Buffer + StartingCluster/8,
|
||
BytesToCopy );
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
Status = GetExceptionCode();
|
||
|
||
FatRaiseStatus( IrpContext,
|
||
FsRtlIsNtstatusExpected(Status) ?
|
||
Status : STATUS_INVALID_USER_BUFFER );
|
||
}
|
||
|
||
Irp->IoStatus.Information = FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer) +
|
||
BytesToCopy;
|
||
|
||
FatReleaseVcb( IrpContext, Vcb );
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
|
||
DebugTrace(-1, Dbg, "FatGetVolumeBitmap -> VOID\n", 0);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatGetRetrievalPointers (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine scans the MCB and builds an extent list. The first run in
|
||
the output extent list will start at the begining of the contiguous
|
||
run specified by the input parameter.
|
||
|
||
Input = STARTING_VCN_INPUT_BUFFER;
|
||
Output = RETRIEVAL_POINTERS_BUFFER.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp being processed.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PVCB Vcb;
|
||
PFCB FcbOrDcb;
|
||
PCCB Ccb;
|
||
TYPE_OF_OPEN TypeOfOpen;
|
||
|
||
ULONG Index;
|
||
ULONG ClusterShift;
|
||
ULONG AllocationSize;
|
||
|
||
ULONG Run;
|
||
ULONG RunCount;
|
||
ULONG StartingRun;
|
||
LARGE_INTEGER StartingVcn;
|
||
|
||
ULONG InputBufferLength;
|
||
ULONG OutputBufferLength;
|
||
|
||
PRETRIEVAL_POINTERS_BUFFER OutputBuffer;
|
||
|
||
//
|
||
// Get the current Irp stack location and save some references.
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "FatGetRetrievalPointers, FsControlCode = %08lx\n",
|
||
IrpSp->Parameters.FileSystemControl.FsControlCode);
|
||
|
||
//
|
||
// Extract and decode the file object and check for type of open.
|
||
//
|
||
|
||
TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &FcbOrDcb, &Ccb );
|
||
|
||
if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen)) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Get the input aand output buffer lengths and pointers.
|
||
// Initialize some variables.
|
||
//
|
||
|
||
InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
|
||
OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
|
||
|
||
OutputBuffer = (PRETRIEVAL_POINTERS_BUFFER)FatMapUserBuffer( IrpContext, Irp );
|
||
|
||
//
|
||
// Check for a minimum length on the input and ouput buffers.
|
||
//
|
||
|
||
if ((InputBufferLength < sizeof(STARTING_VCN_INPUT_BUFFER)) ||
|
||
(OutputBufferLength < sizeof(RETRIEVAL_POINTERS_BUFFER))) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
//
|
||
// Acquire exclusive access to the Fcb and enqueue the Irp if we
|
||
// didn't get access.
|
||
//
|
||
|
||
if (!FatAcquireExclusiveFcb( IrpContext, FcbOrDcb )) {
|
||
|
||
DebugTrace( 0, Dbg, "Cannot acquire Vcb\n", 0);
|
||
|
||
Status = FatFsdPostRequest( IrpContext, Irp );
|
||
|
||
DebugTrace(-1, Dbg, "FatGetRetrievalPointers -> %08lx\n", Status);
|
||
return Status;
|
||
}
|
||
|
||
try {
|
||
|
||
//
|
||
// If we haven't yet set the correct AllocationSize, do so.
|
||
//
|
||
|
||
if (FcbOrDcb->Header.AllocationSize.LowPart == 0xffffffff) {
|
||
|
||
FatLookupFileAllocationSize( IrpContext, FcbOrDcb );
|
||
|
||
//
|
||
// If this is a non-root directory, we have a bit more to
|
||
// do since it has not gone through FatOpenDirectoryFile().
|
||
//
|
||
|
||
if (NodeType(FcbOrDcb) == FAT_NTC_DCB) {
|
||
|
||
FcbOrDcb->Header.FileSize.LowPart =
|
||
FcbOrDcb->Header.AllocationSize.LowPart;
|
||
|
||
//
|
||
// Setup the Bitmap buffer.
|
||
//
|
||
|
||
FatCheckFreeDirentBitmap( IrpContext, FcbOrDcb );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Check if a starting cluster was specified.
|
||
//
|
||
|
||
ClusterShift = Vcb->AllocationSupport.LogOfBytesPerCluster;
|
||
AllocationSize = FcbOrDcb->Header.AllocationSize.LowPart;
|
||
|
||
try {
|
||
|
||
ProbeForRead( IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
|
||
InputBufferLength,
|
||
sizeof(UCHAR) );
|
||
|
||
ProbeForWrite( OutputBuffer, OutputBufferLength, sizeof(UCHAR) );
|
||
|
||
StartingVcn = ((PSTARTING_VCN_INPUT_BUFFER)IrpSp->Parameters.FileSystemControl.Type3InputBuffer)->StartingVcn;
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
Status = GetExceptionCode();
|
||
|
||
FatRaiseStatus( IrpContext,
|
||
FsRtlIsNtstatusExpected(Status) ?
|
||
Status : STATUS_INVALID_USER_BUFFER );
|
||
}
|
||
|
||
if (StartingVcn.HighPart ||
|
||
StartingVcn.LowPart >= (AllocationSize >> ClusterShift)) {
|
||
|
||
try_return( Status = STATUS_END_OF_FILE );
|
||
|
||
} else {
|
||
|
||
//
|
||
// If we don't find the run, something is very wrong.
|
||
//
|
||
|
||
LBO Lbo;
|
||
|
||
if (!FsRtlLookupMcbEntry( &FcbOrDcb->Mcb,
|
||
StartingVcn.LowPart << ClusterShift,
|
||
&Lbo,
|
||
NULL,
|
||
&StartingRun)) {
|
||
|
||
FatBugCheck( (ULONG)FcbOrDcb, (ULONG)&FcbOrDcb->Mcb, StartingVcn.LowPart );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now go fill in the ouput buffer with run information
|
||
//
|
||
|
||
RunCount = FsRtlNumberOfRunsInMcb(&FcbOrDcb->Mcb);
|
||
|
||
for (Index = 0, Run = StartingRun; Run < RunCount; Index++, Run++) {
|
||
|
||
ULONG Vcn;
|
||
LBO Lbo;
|
||
ULONG ByteLength;
|
||
|
||
//
|
||
// Check for an exhausted output buffer.
|
||
//
|
||
|
||
if (FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[Index+1]) > OutputBufferLength) {
|
||
|
||
//
|
||
// We've run out of space, so we won't be storing as many runs to the
|
||
// user's buffer as we had originally planned. We need to return the
|
||
// number of runs that we did have room for.
|
||
//
|
||
|
||
try {
|
||
|
||
OutputBuffer->ExtentCount = Index;
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
Status = GetExceptionCode();
|
||
|
||
FatRaiseStatus( IrpContext,
|
||
FsRtlIsNtstatusExpected(Status) ?
|
||
Status : STATUS_INVALID_USER_BUFFER );
|
||
}
|
||
|
||
Irp->IoStatus.Information = FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[Index]);
|
||
try_return( Status = STATUS_BUFFER_OVERFLOW );
|
||
}
|
||
|
||
//
|
||
// Get the extent. If it's not there or malformed, something is very wrong.
|
||
//
|
||
|
||
if (!FsRtlGetNextMcbEntry(&FcbOrDcb->Mcb, Run, &Vcn, &Lbo, &ByteLength)) {
|
||
FatBugCheck( (ULONG)FcbOrDcb, (ULONG)&FcbOrDcb->Mcb, Run );
|
||
}
|
||
|
||
//
|
||
// Fill in the next array element.
|
||
//
|
||
|
||
try {
|
||
|
||
OutputBuffer->Extents[Index].NextVcn.QuadPart = (Vcn + ByteLength) >> ClusterShift;
|
||
OutputBuffer->Extents[Index].Lcn.QuadPart = FatGetIndexFromLbo( Vcb, Lbo ) - 2;
|
||
|
||
//
|
||
// If this is the first run, fill in the starting Vcn
|
||
//
|
||
|
||
if (Index == 0) {
|
||
OutputBuffer->ExtentCount = RunCount - StartingRun;
|
||
OutputBuffer->StartingVcn.QuadPart = Vcn >> ClusterShift;
|
||
}
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
Status = GetExceptionCode();
|
||
|
||
FatRaiseStatus( IrpContext,
|
||
FsRtlIsNtstatusExpected(Status) ?
|
||
Status : STATUS_INVALID_USER_BUFFER );
|
||
}
|
||
}
|
||
|
||
//
|
||
// We successfully retrieved extent info to the end of the allocation.
|
||
//
|
||
|
||
Irp->IoStatus.Information = FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[Index]);
|
||
Status = STATUS_SUCCESS;
|
||
|
||
try_exit: NOTHING;
|
||
|
||
} finally {
|
||
|
||
DebugUnwind( FatGetRetrievalPointers );
|
||
|
||
//
|
||
// Release resources
|
||
//
|
||
|
||
FatReleaseFcb( IrpContext, FcbOrDcb );
|
||
|
||
//
|
||
// If nothing raised then complete the irp.
|
||
//
|
||
|
||
if (!AbnormalTermination()) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatGetRetrievalPointers -> VOID\n", 0);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FatMoveFile (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The major parts of the following routine were extracted from NtfsSetCompression. This
|
||
routine moves a file to the requested Starting Lcn from Starting Vcn for the length
|
||
of cluster count. These values are passed in through the the input buffer as a MOVE_DATA
|
||
structure.
|
||
|
||
The call must be made with a DASD handle. The file to move is passed in as a
|
||
parameter.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp being processed.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PFILE_OBJECT FileObject;
|
||
TYPE_OF_OPEN TypeOfOpen;
|
||
PVCB Vcb;
|
||
PFCB FcbOrDcb;
|
||
PCCB Ccb;
|
||
|
||
ULONG InputBufferLength;
|
||
PMOVE_FILE_DATA InputBuffer;
|
||
|
||
ULONG ClusterShift;
|
||
ULONG MaxClusters;
|
||
|
||
ULONG FileOffset;
|
||
LARGE_INTEGER LargeFileOffset;
|
||
|
||
ULONG TargetLbo;
|
||
ULONG TargetCluster;
|
||
LARGE_INTEGER LargeTargetLbo;
|
||
|
||
ULONG ByteCount;
|
||
ULONG BytesToWrite;
|
||
ULONG BytesToReallocate;
|
||
ULONG TargetAllocation;
|
||
|
||
ULONG FirstSpliceSourceCluster;
|
||
ULONG FirstSpliceTargetCluster;
|
||
ULONG SecondSpliceSourceCluster;
|
||
ULONG SecondSpliceTargetCluster;
|
||
|
||
MCB SourceMcb;
|
||
MCB TargetMcb;
|
||
|
||
KEVENT StackEvent;
|
||
|
||
PBCB Bcb = NULL;
|
||
PMDL Mdl = NULL;
|
||
PVOID Buffer;
|
||
|
||
BOOLEAN SourceMcbInitialized = FALSE;
|
||
BOOLEAN TargetMcbInitialized = FALSE;
|
||
BOOLEAN CacheMapInitialized = FALSE;
|
||
|
||
BOOLEAN FcbAcquired = FALSE;
|
||
BOOLEAN LockedPages = FALSE;
|
||
BOOLEAN EventArmed = FALSE;
|
||
BOOLEAN DiskSpaceAllocated = FALSE;
|
||
|
||
PDIRENT Dirent;
|
||
PBCB DirentBcb = NULL;
|
||
|
||
//
|
||
// Get the current Irp stack location and save some references.
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "FatMoveFile, FsControlCode = %08lx\n",
|
||
IrpSp->Parameters.FileSystemControl.FsControlCode);
|
||
|
||
//
|
||
// Extract and decode the file object and check for type of open.
|
||
//
|
||
|
||
if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &FcbOrDcb, &Ccb ) != UserVolumeOpen) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
|
||
DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
|
||
InputBuffer = (PMOVE_FILE_DATA)Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
//
|
||
// Do a quick check on the input buffer.
|
||
//
|
||
|
||
MaxClusters = Vcb->AllocationSupport.NumberOfClusters;
|
||
TargetCluster = InputBuffer->StartingLcn.LowPart + 2;
|
||
|
||
if (InputBufferLength < sizeof(MOVE_FILE_DATA)) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
if (InputBuffer->StartingVcn.HighPart ||
|
||
InputBuffer->StartingLcn.HighPart ||
|
||
(TargetCluster + InputBuffer->ClusterCount < TargetCluster) ||
|
||
(TargetCluster + InputBuffer->ClusterCount > MaxClusters + 2) ||
|
||
(InputBuffer->StartingVcn.LowPart >= MaxClusters)) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
|
||
DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Try to get a pointer to the file object from the handle passed in.
|
||
//
|
||
|
||
Status = ObReferenceObjectByHandle( InputBuffer->FileHandle,
|
||
0,
|
||
*IoFileObjectType,
|
||
Irp->RequestorMode,
|
||
&FileObject,
|
||
NULL );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
|
||
DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", Status);
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// We only needed the pointer, not a reference.
|
||
//
|
||
|
||
ObDereferenceObject( FileObject );
|
||
|
||
//
|
||
// Check that this file object is opened on the same volume as the
|
||
// DASD handle used to call this routine.
|
||
//
|
||
|
||
if (FileObject->Vpb != Vcb->Vpb) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
|
||
DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Extract and decode the file object and check for type of open.
|
||
//
|
||
|
||
TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &FcbOrDcb, &Ccb );
|
||
|
||
if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen)) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
|
||
DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// If this is a directory, verify that it's not the root and that we
|
||
// are not trying to move the first cluster. We cannot move the first
|
||
// cluster because sub-directories have this cluster number in them
|
||
// and there is no safe way to simultaneously update them all.
|
||
//
|
||
|
||
if ((TypeOfOpen == UserDirectoryOpen) &&
|
||
((NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB) || (InputBuffer->StartingVcn.QuadPart == 0))) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
|
||
DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
ClusterShift = Vcb->AllocationSupport.LogOfBytesPerCluster;
|
||
|
||
try {
|
||
|
||
//
|
||
// Initialize our state variables and the event.
|
||
//
|
||
|
||
FileOffset = InputBuffer->StartingVcn.LowPart << ClusterShift;
|
||
LargeFileOffset.QuadPart = FileOffset;
|
||
|
||
ByteCount = InputBuffer->ClusterCount << ClusterShift;
|
||
|
||
TargetLbo = FatGetLboFromIndex( Vcb, TargetCluster );
|
||
LargeTargetLbo.QuadPart = TargetLbo;
|
||
|
||
//
|
||
// Do a quick check on parameters here
|
||
//
|
||
|
||
if (FileOffset + ByteCount < FileOffset) {
|
||
|
||
try_return( Status = STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
KeInitializeEvent( &StackEvent, NotificationEvent, FALSE );
|
||
|
||
//
|
||
// Initialize two MCBs we will be using
|
||
//
|
||
|
||
FsRtlInitializeMcb( &SourceMcb, PagedPool );
|
||
SourceMcbInitialized = TRUE;
|
||
|
||
FsRtlInitializeMcb( &TargetMcb, PagedPool );
|
||
TargetMcbInitialized = TRUE;
|
||
|
||
//
|
||
// Force WAIT to true
|
||
//
|
||
|
||
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
|
||
|
||
while (ByteCount) {
|
||
|
||
VBO TempVbo;
|
||
LBO TempLbo;
|
||
ULONG TempByteCount;
|
||
|
||
//
|
||
// We must throttle our writes.
|
||
//
|
||
|
||
CcCanIWrite( FileObject, 0x40000, TRUE, FALSE );
|
||
|
||
//
|
||
// Aqcuire file resource exclusive to freeze FileSize and block
|
||
// user non-cached I/O.
|
||
//
|
||
|
||
(VOID)FatAcquireExclusiveFcb( IrpContext, FcbOrDcb );
|
||
FcbAcquired = TRUE;
|
||
|
||
//
|
||
// Analyzes the range of file allocation we are moving
|
||
// and determines the actual amount of allocation to be
|
||
// moved and how much needs to be written. In addition
|
||
// it guarantees that the Mcb in the file is large enough
|
||
// so that later MCB operations cannot fail.
|
||
//
|
||
|
||
FatComputeMoveFileParameter( IrpContext,
|
||
FcbOrDcb,
|
||
FileOffset,
|
||
&ByteCount,
|
||
&BytesToReallocate,
|
||
&BytesToWrite );
|
||
|
||
//
|
||
// If ByteCount comes back zero, break here.
|
||
//
|
||
|
||
if (ByteCount == 0) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// At this point (before actually doing anything with the disk
|
||
// meta data), calculate the FAT splice clusters and build an
|
||
// MCB describing the space to be deallocated.
|
||
//
|
||
|
||
FatComputeMoveFileSplicePoints( IrpContext,
|
||
FcbOrDcb,
|
||
FileOffset,
|
||
TargetCluster,
|
||
BytesToReallocate,
|
||
&FirstSpliceSourceCluster,
|
||
&FirstSpliceTargetCluster,
|
||
&SecondSpliceSourceCluster,
|
||
&SecondSpliceTargetCluster,
|
||
&SourceMcb );
|
||
|
||
//
|
||
// Now attempt to allocate the new disk storage using the
|
||
// Target Lcn as a hint.
|
||
//
|
||
|
||
TempByteCount = BytesToReallocate;
|
||
FatAllocateDiskSpace( IrpContext,
|
||
Vcb,
|
||
TargetCluster,
|
||
&TempByteCount,
|
||
&TargetMcb );
|
||
|
||
DiskSpaceAllocated = TRUE;
|
||
|
||
//
|
||
// If we didn't get EXACTLY what we wanted, return immediately.
|
||
//
|
||
|
||
if ((FsRtlNumberOfRunsInMcb(&TargetMcb) != 1) ||
|
||
!FsRtlGetNextMcbEntry(&TargetMcb, 0, &TempVbo, &TempLbo, &TempByteCount) ||
|
||
(FatGetIndexFromLbo(Vcb, TempLbo) != TargetCluster) ||
|
||
(TempByteCount != BytesToReallocate)) {
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// We are going to attempt a move, note it.
|
||
//
|
||
|
||
if (FatMoveFileDebug) {
|
||
DbgPrint("%lx: Vcn 0x%lx, Lcn 0x%lx, Count 0x%lx.\n",
|
||
PsGetCurrentThread(),
|
||
FileOffset >> ClusterShift,
|
||
TargetCluster,
|
||
BytesToReallocate >> ClusterShift );
|
||
}
|
||
|
||
//
|
||
// Now attempt to commit the new allocation to disk. If this
|
||
// raises, the allocation will be deallocated.
|
||
//
|
||
|
||
FatFlushFatEntries( IrpContext,
|
||
Vcb,
|
||
TargetCluster,
|
||
BytesToReallocate >> ClusterShift );
|
||
|
||
//
|
||
// If we are going to write, we have to lock the pages down BEFORE
|
||
// closing off the paging I/O path to avoid a deadlock from
|
||
// colided page faults.
|
||
//
|
||
|
||
if (BytesToWrite) {
|
||
|
||
//
|
||
// If a shared cache map is not initialized, do so.
|
||
//
|
||
|
||
if (FileObject->SectionObjectPointer->SharedCacheMap == NULL ) {
|
||
|
||
CC_FILE_SIZES TempSizes;
|
||
|
||
//
|
||
// Indicate that valid data length tracking and callbacks are not desired.
|
||
//
|
||
|
||
TempSizes = *(PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize;
|
||
|
||
TempSizes.ValidDataLength = FatMaxLarge;
|
||
|
||
CcInitializeCacheMap( FileObject,
|
||
(PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize,
|
||
TRUE,
|
||
&FatData.CacheManagerNoOpCallbacks,
|
||
FcbOrDcb );
|
||
|
||
CacheMapInitialized = TRUE;
|
||
}
|
||
|
||
//
|
||
// Map the next range of the file.
|
||
//
|
||
|
||
CcMapData( FileObject, &LargeFileOffset, BytesToWrite, TRUE, &Bcb, &Buffer );
|
||
|
||
//
|
||
// Now attempt to allocate an Mdl to describe the mapped data.
|
||
//
|
||
|
||
Mdl = IoAllocateMdl( Buffer, (ULONG)BytesToWrite, FALSE, FALSE, NULL );
|
||
|
||
if (Mdl == NULL) {
|
||
FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
//
|
||
// Lock the data into memory so that we can safely reallocate the
|
||
// space.
|
||
//
|
||
|
||
MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
|
||
LockedPages = TRUE;
|
||
}
|
||
|
||
//
|
||
// Aqcuire both resources exclusive now, guaranteeing that NOBODY
|
||
// is in either the read or write paths.
|
||
//
|
||
|
||
ExAcquireResourceExclusive( FcbOrDcb->Header.PagingIoResource, TRUE );
|
||
|
||
//
|
||
// This is the first part of some tricky synchronization.
|
||
//
|
||
// Set the Event pointer in the FCB. Any paging I/O will block on
|
||
// this event (if set in FCB) after acquiring the PagingIo resource.
|
||
//
|
||
// This is how I keep ALL I/O out of this path without holding the
|
||
// PagingIo resource exclusive for an extended time.
|
||
//
|
||
|
||
FcbOrDcb->MoveFileEvent = &StackEvent;
|
||
EventArmed = TRUE;
|
||
|
||
ExReleaseResource( FcbOrDcb->Header.PagingIoResource );
|
||
|
||
//
|
||
// Now write out the data, but only if we have to. We don't have
|
||
// to copy any file data if the range being reallocated is wholly
|
||
// beyond valid data length.
|
||
//
|
||
|
||
if (BytesToWrite) {
|
||
|
||
PIRP IoIrp;
|
||
KEVENT IoEvent;
|
||
IO_STATUS_BLOCK Iosb;
|
||
|
||
KeInitializeEvent( &IoEvent, NotificationEvent, FALSE );
|
||
|
||
IoIrp = IoBuildSynchronousFsdRequest( IRP_MJ_WRITE,
|
||
Vcb->TargetDeviceObject,
|
||
Buffer,
|
||
BytesToWrite,
|
||
&LargeTargetLbo,
|
||
&IoEvent,
|
||
&Iosb );
|
||
|
||
if (!IoIrp) {
|
||
FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
//
|
||
// Set a flag indicating that we want to write through any
|
||
// cache on the controller. This eliminates the need for
|
||
// an explicit flush-device after the write.
|
||
//
|
||
|
||
SetFlag( IoGetNextIrpStackLocation(IoIrp)->Flags, SL_WRITE_THROUGH );
|
||
|
||
Status = IoCallDriver( Vcb->TargetDeviceObject, IoIrp );
|
||
|
||
if (Status == STATUS_PENDING) {
|
||
(VOID)KeWaitForSingleObject( &IoEvent, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL );
|
||
Status = Iosb.Status;
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
FatNormalizeAndRaiseStatus( IrpContext, Status );
|
||
}
|
||
|
||
//
|
||
// Now we can get rid of this Mdl.
|
||
//
|
||
|
||
MmUnlockPages( Mdl );
|
||
LockedPages = FALSE;
|
||
IoFreeMdl( Mdl );
|
||
Mdl = NULL;
|
||
|
||
//
|
||
// Now we can safely unpin.
|
||
//
|
||
|
||
CcUnpinData( Bcb );
|
||
Bcb = NULL;
|
||
}
|
||
|
||
//
|
||
// Now that the file data has been moved successfully, we'll go
|
||
// to fix up the links in the FAT table and perhaps change the
|
||
// entry in the parent directory.
|
||
//
|
||
// First we'll do the second splice and commit it. At that point,
|
||
// while the volume is in an inconsistent state, the file is
|
||
// still OK.
|
||
//
|
||
|
||
FatSetFatEntry( IrpContext,
|
||
Vcb,
|
||
SecondSpliceSourceCluster,
|
||
(FAT_ENTRY)SecondSpliceTargetCluster );
|
||
|
||
FatFlushFatEntries( IrpContext, Vcb, SecondSpliceSourceCluster, 1 );
|
||
|
||
//
|
||
// Now do the first splice OR update the dirent in the parent
|
||
// and flush the respective object. After this flush the file
|
||
// now points to the new allocation.
|
||
//
|
||
|
||
if (FirstSpliceSourceCluster == 0) {
|
||
|
||
//
|
||
// We are moving the first cluster of the file, so we need
|
||
// to update our parent directory.
|
||
//
|
||
|
||
FatGetDirentFromFcbOrDcb( IrpContext, FcbOrDcb, &Dirent, &DirentBcb );
|
||
Dirent->FirstClusterOfFile = FirstSpliceTargetCluster;
|
||
|
||
FatSetDirtyBcb( IrpContext, DirentBcb, Vcb );
|
||
|
||
FatUnpinBcb( IrpContext, DirentBcb );
|
||
DirentBcb = NULL;
|
||
|
||
FatFlushDirentForFile( IrpContext, FcbOrDcb );
|
||
|
||
FcbOrDcb->FirstClusterOfFile = FirstSpliceTargetCluster;
|
||
|
||
} else {
|
||
|
||
FatSetFatEntry( IrpContext,
|
||
Vcb,
|
||
FirstSpliceSourceCluster,
|
||
(FAT_ENTRY)FirstSpliceTargetCluster );
|
||
|
||
FatFlushFatEntries( IrpContext, Vcb, FirstSpliceSourceCluster, 1 );
|
||
}
|
||
|
||
//
|
||
// This was successfully committed. We no longer want to free
|
||
// this allocation on error.
|
||
//
|
||
|
||
DiskSpaceAllocated = FALSE;
|
||
|
||
//
|
||
// Now we just have to free the orphaned space. We don't have
|
||
// to commit this right now as the integrity of the file doesn't
|
||
// depend on it.
|
||
//
|
||
|
||
FatDeallocateDiskSpace( IrpContext, Vcb, &SourceMcb );
|
||
|
||
FatUnpinRepinnedBcbs( IrpContext );
|
||
|
||
Status = FatHijackIrpAndFlushDevice( IrpContext,
|
||
Irp,
|
||
Vcb->TargetDeviceObject );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
FatNormalizeAndRaiseStatus( IrpContext, Status );
|
||
}
|
||
|
||
//
|
||
// Finally we must replace the old MCB extent information with
|
||
// the new. If this fails from pool allocation, we fix it in
|
||
// the finally clause by resetting the file's Mcb.
|
||
//
|
||
|
||
FsRtlRemoveMcbEntry( &FcbOrDcb->Mcb,
|
||
FileOffset,
|
||
BytesToReallocate );
|
||
|
||
FsRtlAddMcbEntry( &FcbOrDcb->Mcb,
|
||
FileOffset,
|
||
TargetLbo,
|
||
BytesToReallocate );
|
||
|
||
//
|
||
// Now this is the second part of the tricky synchronization.
|
||
//
|
||
// We drop the paging I/O here and signal the notification
|
||
// event which allows all waiters (present or future) to proceed.
|
||
// Then we block again on the PagingIo exclusive. When
|
||
// we have it, we again know that there can be nobody in the
|
||
// read/write path and thus nobody touching the event, so we
|
||
// NULL the pointer to it and then drop the PagingIo resource.
|
||
//
|
||
// This combined with our synchronization before the write above
|
||
// guarantees that while we were moving the allocation, there
|
||
// was no other I/O to this file and because we do not hold
|
||
// the paging resource across a flush, we are not exposed to
|
||
// a deadlock.
|
||
//
|
||
|
||
KeSetEvent( &StackEvent, 0, FALSE );
|
||
|
||
ExAcquireResourceExclusive( FcbOrDcb->Header.PagingIoResource, TRUE );
|
||
|
||
FcbOrDcb->MoveFileEvent = NULL;
|
||
EventArmed = FALSE;
|
||
|
||
ExReleaseResource( FcbOrDcb->Header.PagingIoResource );
|
||
|
||
//
|
||
// Release the resources and let anyone else access the file before
|
||
// looping back.
|
||
//
|
||
|
||
FatReleaseFcb( IrpContext, FcbOrDcb );
|
||
FcbAcquired = FALSE;
|
||
|
||
//
|
||
// Advance the state variables.
|
||
//
|
||
|
||
TargetCluster += BytesToReallocate >> ClusterShift;
|
||
|
||
FileOffset += BytesToReallocate;
|
||
TargetLbo += BytesToReallocate;
|
||
ByteCount -= BytesToReallocate;
|
||
|
||
LargeFileOffset.LowPart += BytesToReallocate;
|
||
LargeTargetLbo.LowPart += BytesToReallocate;
|
||
|
||
//
|
||
// Clear the two Mcbs
|
||
//
|
||
|
||
FsRtlRemoveMcbEntry( &SourceMcb, 0, 0xFFFFFFFF );
|
||
FsRtlRemoveMcbEntry( &TargetMcb, 0, 0xFFFFFFFF );
|
||
|
||
//
|
||
// Make the event blockable again.
|
||
//
|
||
|
||
KeClearEvent( &StackEvent );
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
try_exit: NOTHING;
|
||
|
||
} finally {
|
||
|
||
DebugUnwind( FatMoveFile );
|
||
|
||
//
|
||
// Cleanup the Mdl, Bcb, and cache map as appropriate.
|
||
//
|
||
|
||
if (Mdl != NULL) {
|
||
ASSERT(AbnormalTermination());
|
||
if (LockedPages) {
|
||
MmUnlockPages( Mdl );
|
||
}
|
||
IoFreeMdl( Mdl );
|
||
}
|
||
|
||
if (Bcb != NULL) {
|
||
ASSERT(AbnormalTermination());
|
||
CcUnpinData( Bcb );
|
||
}
|
||
|
||
if (CacheMapInitialized) {
|
||
CcUninitializeCacheMap( FileObject, NULL, NULL );
|
||
}
|
||
|
||
//
|
||
// If we have some new allocation hanging around, remove it. The
|
||
// pages needed to do this are guaranteed to be resident because
|
||
// we have already repinned them.
|
||
//
|
||
|
||
if (DiskSpaceAllocated) {
|
||
FatDeallocateDiskSpace( IrpContext, Vcb, &TargetMcb );
|
||
FatUnpinRepinnedBcbs( IrpContext );
|
||
}
|
||
|
||
//
|
||
// Check on the directory Bcb
|
||
//
|
||
|
||
if (DirentBcb != NULL) {
|
||
FatUnpinBcb( IrpContext, DirentBcb );
|
||
}
|
||
|
||
//
|
||
// Uninitialize our MCBs
|
||
//
|
||
|
||
if (SourceMcbInitialized) {
|
||
FsRtlUninitializeMcb( &SourceMcb );
|
||
}
|
||
|
||
if (TargetMcbInitialized) {
|
||
FsRtlUninitializeMcb( &TargetMcb );
|
||
}
|
||
|
||
//
|
||
// If we broke out of the loop with the Event armed, defuse it
|
||
// in the same way we do it after a write.
|
||
//
|
||
|
||
if (EventArmed) {
|
||
KeSetEvent( &StackEvent, 0, FALSE );
|
||
ExAcquireResourceExclusive( FcbOrDcb->Header.PagingIoResource, TRUE );
|
||
FcbOrDcb->MoveFileEvent = NULL;
|
||
ExReleaseResource( FcbOrDcb->Header.PagingIoResource );
|
||
}
|
||
|
||
//
|
||
// If this is an abnormal termination then presumably something
|
||
// bad happened. Set the Allocation size to unknown and clear
|
||
// the Mcb, but only if we still own the Fcb.
|
||
//
|
||
|
||
if (AbnormalTermination() && FcbAcquired) {
|
||
|
||
FcbOrDcb->Header.AllocationSize.LowPart = 0xffffffff;
|
||
FsRtlRemoveMcbEntry( &FcbOrDcb->Mcb, 0, 0xFFFFFFFF );
|
||
}
|
||
|
||
//
|
||
// Finally release the main file resource.
|
||
//
|
||
|
||
if (FcbAcquired) {
|
||
FatReleaseFcb( IrpContext, FcbOrDcb );
|
||
}
|
||
|
||
//
|
||
// Complete the irp if we terminated normally.
|
||
//
|
||
|
||
if (!AbnormalTermination()) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
VOID
|
||
FatComputeMoveFileParameter (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB FcbOrDcb,
|
||
IN ULONG FileOffset,
|
||
IN OUT PULONG ByteCount,
|
||
OUT PULONG BytesToReallocate,
|
||
OUT PULONG BytesToWrite
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is a helper routine for FatMoveFile that analyses the range of
|
||
file allocation we are moving and determines the actual amount
|
||
of allocation to be moved and how much needs to be written.
|
||
|
||
Arguments:
|
||
|
||
FcbOrDcb - Supplies the file and its various sizes.
|
||
|
||
FileOffset - Supplies the beginning Vbo of the reallocation zone.
|
||
|
||
ByteCount - Supplies the request length to reallocate. This will
|
||
be bounded by allocation size on return.
|
||
|
||
BytesToReallocate - Receives ByteCount bounded by the file allocation size
|
||
and a 0x40000 boundry.
|
||
|
||
BytesToWrite - Receives BytesToReallocate bounded by ValidDataLength.
|
||
|
||
Return Value:
|
||
|
||
VOID
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG ClusterSize;
|
||
|
||
ULONG AllocationSize;
|
||
ULONG ValidDataLength;
|
||
ULONG ClusterAlignedVDL;
|
||
|
||
//
|
||
// If we haven't yet set the correct AllocationSize, do so.
|
||
//
|
||
|
||
if (FcbOrDcb->Header.AllocationSize.LowPart == 0xffffffff) {
|
||
|
||
FatLookupFileAllocationSize( IrpContext, FcbOrDcb );
|
||
|
||
//
|
||
// If this is a non-root directory, we have a bit more to
|
||
// do since it has not gone through FatOpenDirectoryFile().
|
||
//
|
||
|
||
if (NodeType(FcbOrDcb) == FAT_NTC_DCB) {
|
||
|
||
FcbOrDcb->Header.FileSize.LowPart =
|
||
FcbOrDcb->Header.AllocationSize.LowPart;
|
||
|
||
//
|
||
// Setup the Bitmap buffer.
|
||
//
|
||
|
||
FatCheckFreeDirentBitmap( IrpContext, FcbOrDcb );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the number of bytes left to write and ensure that it does
|
||
// not extend beyond allocation size. We return here if FileOffset
|
||
// is beyond AllocationSize which can happn on a truncation.
|
||
//
|
||
|
||
AllocationSize = FcbOrDcb->Header.AllocationSize.LowPart;
|
||
ValidDataLength = FcbOrDcb->Header.ValidDataLength.LowPart;
|
||
|
||
if (FileOffset + *ByteCount > AllocationSize) {
|
||
|
||
if (FileOffset >= AllocationSize) {
|
||
*ByteCount = 0;
|
||
*BytesToReallocate = 0;
|
||
*BytesToWrite = 0;
|
||
|
||
return;
|
||
}
|
||
|
||
*ByteCount = AllocationSize - FileOffset;
|
||
}
|
||
|
||
//
|
||
// If there is more than our max, then reduce the byte count for this
|
||
// pass to our maximum. We must also align the file offset to a 0x40000
|
||
// byte boundary.
|
||
//
|
||
|
||
if ((FileOffset & 0x3ffff) + *ByteCount > 0x40000) {
|
||
|
||
*BytesToReallocate = 0x40000 - (FileOffset & 0x3ffff);
|
||
|
||
} else {
|
||
|
||
*BytesToReallocate = *ByteCount;
|
||
}
|
||
|
||
//
|
||
// We may be able to skip some (or all) of the write
|
||
// if allocation size is significantly greater than valid data length.
|
||
//
|
||
|
||
ClusterSize = 1 << FcbOrDcb->Vcb->AllocationSupport.LogOfBytesPerCluster;
|
||
|
||
ClusterAlignedVDL = (ValidDataLength + (ClusterSize - 1)) & ~(ClusterSize - 1);
|
||
|
||
if ((NodeType(FcbOrDcb) == FAT_NTC_FCB) &&
|
||
(FileOffset + *BytesToReallocate > ClusterAlignedVDL)) {
|
||
|
||
if (FileOffset > ClusterAlignedVDL) {
|
||
|
||
*BytesToWrite = 0;
|
||
|
||
} else {
|
||
|
||
*BytesToWrite = ClusterAlignedVDL - FileOffset;
|
||
}
|
||
|
||
} else {
|
||
|
||
*BytesToWrite = *BytesToReallocate;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
VOID
|
||
FatComputeMoveFileSplicePoints (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB FcbOrDcb,
|
||
IN ULONG FileOffset,
|
||
IN ULONG TargetCluster,
|
||
IN ULONG BytesToReallocate,
|
||
OUT PULONG FirstSpliceSourceCluster,
|
||
OUT PULONG FirstSpliceTargetCluster,
|
||
OUT PULONG SecondSpliceSourceCluster,
|
||
OUT PULONG SecondSpliceTargetCluster,
|
||
IN OUT PMCB SourceMcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is a helper routine for FatMoveFile that analyzes the range of
|
||
file allocation we are moving and generates the splice points in the
|
||
FAT table.
|
||
|
||
Arguments:
|
||
|
||
FcbOrDcb - Supplies the file and thus Mcb.
|
||
|
||
FileOffset - Supplies the beginning Vbo of the reallocation zone.
|
||
|
||
TargetCluster - Supplies the beginning cluster of the reallocation target.
|
||
|
||
BytesToReallocate - Suppies the length of the reallocation zone.
|
||
|
||
FirstSpliceSourceCluster - Receives the last cluster in previous allocation
|
||
or zero if we are reallocating from VBO 0.
|
||
|
||
FirstSpliceTargetCluster - Receives the target cluster (i.e. new allocation)
|
||
|
||
SecondSpliceSourceCluster - Receives the final target cluster.
|
||
|
||
SecondSpliceTargetCluster - Receives the first cluster of the remaining
|
||
source allocation or FAT_CLUSTER_LAST if the reallocation zone
|
||
extends to the end of the file.
|
||
|
||
SourceMcb - This supplies an MCB that will be filled in with run
|
||
information describing the file allocation being replaced. The Mcb
|
||
must be initialized by the caller.
|
||
|
||
Return Value:
|
||
|
||
VOID
|
||
|
||
--*/
|
||
|
||
{
|
||
VBO SourceVbo;
|
||
LBO SourceLbo;
|
||
ULONG SourceIndex;
|
||
ULONG SourceBytesInRun;
|
||
ULONG SourceBytesRemaining;
|
||
|
||
ULONG SourceMcbVbo;
|
||
ULONG SourceMcbBytesInRun;
|
||
|
||
PVCB Vcb;
|
||
|
||
Vcb = FcbOrDcb->Vcb;
|
||
|
||
//
|
||
// Get information on the final cluster in the previous allocation and
|
||
// prepare to enumerate it in the follow loop.
|
||
//
|
||
|
||
if (FileOffset == 0) {
|
||
|
||
SourceIndex = 0;
|
||
*FirstSpliceSourceCluster = 0;
|
||
FsRtlGetNextMcbEntry( &FcbOrDcb->Mcb,
|
||
0,
|
||
&SourceVbo,
|
||
&SourceLbo,
|
||
&SourceBytesInRun );
|
||
|
||
} else {
|
||
|
||
FsRtlLookupMcbEntry( &FcbOrDcb->Mcb,
|
||
FileOffset-1,
|
||
&SourceLbo,
|
||
&SourceBytesInRun,
|
||
&SourceIndex);
|
||
|
||
*FirstSpliceSourceCluster = FatGetIndexFromLbo( Vcb, SourceLbo );
|
||
|
||
if (SourceBytesInRun == 1) {
|
||
|
||
SourceIndex += 1;
|
||
FsRtlGetNextMcbEntry( &FcbOrDcb->Mcb,
|
||
SourceIndex,
|
||
&SourceVbo,
|
||
&SourceLbo,
|
||
&SourceBytesInRun);
|
||
|
||
} else {
|
||
|
||
SourceVbo = FileOffset;
|
||
SourceLbo += 1;
|
||
SourceBytesInRun -= 1;
|
||
}
|
||
}
|
||
|
||
//
|
||
// At this point the variables:
|
||
//
|
||
// - SourceIndex - SourceLbo - SourceBytesInRun -
|
||
//
|
||
// all correctly decribe the allocation to be removed. In the loop
|
||
// below we will start here and continue enumerating the Mcb runs
|
||
// until we are finished with the allocation to be relocated.
|
||
//
|
||
|
||
*FirstSpliceTargetCluster = TargetCluster;
|
||
|
||
*SecondSpliceSourceCluster =
|
||
*FirstSpliceTargetCluster +
|
||
(BytesToReallocate >> Vcb->AllocationSupport.LogOfBytesPerCluster) - 1;
|
||
|
||
for (SourceBytesRemaining = BytesToReallocate, SourceMcbVbo = 0;
|
||
|
||
SourceBytesRemaining > 0;
|
||
|
||
SourceIndex += 1,
|
||
SourceBytesRemaining -= SourceMcbBytesInRun,
|
||
SourceMcbVbo += SourceMcbBytesInRun) {
|
||
|
||
if (SourceMcbVbo != 0) {
|
||
FsRtlGetNextMcbEntry( &FcbOrDcb->Mcb,
|
||
SourceIndex,
|
||
&SourceVbo,
|
||
&SourceLbo,
|
||
&SourceBytesInRun );
|
||
}
|
||
|
||
ASSERT( SourceVbo == SourceMcbVbo + FileOffset );
|
||
|
||
SourceMcbBytesInRun =
|
||
SourceBytesInRun < SourceBytesRemaining ?
|
||
SourceBytesInRun : SourceBytesRemaining;
|
||
|
||
FsRtlAddMcbEntry( SourceMcb,
|
||
SourceMcbVbo,
|
||
SourceLbo,
|
||
SourceMcbBytesInRun );
|
||
}
|
||
|
||
//
|
||
// Now compute the cluster of the target of the second
|
||
// splice. If the final run in the above loop was
|
||
// more than we needed, then we can just do arithmetic,
|
||
// otherwise we have to look up the next run.
|
||
//
|
||
|
||
if (SourceMcbBytesInRun < SourceBytesInRun) {
|
||
|
||
*SecondSpliceTargetCluster =
|
||
FatGetIndexFromLbo( Vcb, SourceLbo + SourceMcbBytesInRun );
|
||
|
||
} else {
|
||
|
||
if (FsRtlGetNextMcbEntry( &FcbOrDcb->Mcb,
|
||
SourceIndex,
|
||
&SourceVbo,
|
||
&SourceLbo,
|
||
&SourceBytesInRun )) {
|
||
|
||
*SecondSpliceTargetCluster = FatGetIndexFromLbo( Vcb, SourceLbo );
|
||
|
||
} else {
|
||
|
||
*SecondSpliceTargetCluster = FAT_CLUSTER_LAST;
|
||
}
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
FatAllowExtendedDasdIo(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine marks the CCB to indicate that the handle
|
||
may be used to read past the end of the volume file. The
|
||
handle must be a dasd handle.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp being processed.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation.
|
||
|
||
--*/
|
||
{
|
||
PIO_STACK_LOCATION IrpSp;
|
||
PVCB Vcb;
|
||
PFCB Fcb;
|
||
PCCB Ccb;
|
||
|
||
//
|
||
// Get the current Irp stack location and save some references.
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
//
|
||
// Extract and decode the file object and check for type of open.
|
||
//
|
||
|
||
if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
SetFlag( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO );
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
|
||
return STATUS_SUCCESS;
|
||
}
|