2020-09-30 17:12:29 +02:00

8367 lines
233 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
FsCtrl.c
Abstract:
This module implements the File System Control routines for Ntfs called
by the dispatch driver.
Author:
Gary Kimura [GaryKi] 29-Aug-1991
Revision History:
--*/
#include "NtfsProc.h"
#ifdef NTFS_CHECK_BITMAP
BOOLEAN NtfsCopyBitmap = TRUE;
#endif
#ifdef _CAIRO_
VOID
NtOfsIndexTest (
PIRP_CONTEXT IrpContext,
PFCB TestFcb
);
#endif _CAIRO_
//
// Temporarily reference our local attribute definitions
//
extern ATTRIBUTE_DEFINITION_COLUMNS NtfsAttributeDefinitions[];
//
//**** The following variable is only temporary and is used to disable NTFS
//**** from mounting any volumes
//
BOOLEAN NtfsDisable = FALSE;
#ifdef _CAIRO_
//
// ***** The following is used to determine whether to update to version 2.0.
// ***** We don't want to do this until chkdsk will check the volume.
//
BOOLEAN NtfsUpdateTo20 = FALSE;
#endif
//
// The following is used to determine when to move to compressed files.
//
BOOLEAN NtfsDefragMftEnabled = FALSE;
//
// The Bug check file id for this module
//
#define BugCheckFileId (NTFS_BUG_CHECK_FSCTRL)
//
// The local debug trace level
//
#define Dbg (DEBUG_TRACE_FSCTRL)
//
// Define a tag for general pool allocations from this module
//
#undef MODULE_POOL_TAG
#define MODULE_POOL_TAG ('fFtN')
//
// Local procedure prototypes
//
NTSTATUS
NtfsMountVolume (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsVerifyVolume (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsUserFsRequest (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsOplockRequest (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsLockVolume (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsUnlockVolume (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsDismountVolume (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsDirtyVolume (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
BOOLEAN
NtfsGetDiskGeometry (
IN PIRP_CONTEXT IrpContext,
IN PDEVICE_OBJECT DeviceObjectWeTalkTo,
IN PDISK_GEOMETRY DiskGeometry,
IN PPARTITION_INFORMATION PartitionInfo
);
VOID
NtfsReadBootSector (
IN PIRP_CONTEXT IrpContext,
IN PVCB Vcb,
OUT PSCB *BootScb,
OUT PBCB *BootBcb,
OUT PVOID *BootSector
);
BOOLEAN
NtfsIsBootSectorNtfs (
IN PPACKED_BOOT_SECTOR BootSector,
IN PVCB Vcb
);
VOID
NtfsGetVolumeLabel (
IN PIRP_CONTEXT IrpContext,
IN PVPB Vpb OPTIONAL,
IN PVCB Vcb
);
VOID
NtfsSetAndGetVolumeTimes (
IN PIRP_CONTEXT IrpContext,
IN PVCB Vcb,
IN BOOLEAN MarkDirty
);
VOID
NtfsOpenSystemFile (
IN PIRP_CONTEXT IrpContext,
IN OUT PSCB *Scb,
IN PVCB Vcb,
IN ULONG FileNumber,
IN LONGLONG Size,
IN ATTRIBUTE_TYPE_CODE AttributeTypeCode,
IN BOOLEAN ModifiedNoWrite
);
VOID
NtfsOpenRootDirectory (
IN PIRP_CONTEXT IrpContext,
IN PVCB Vcb
);
NTSTATUS
NtfsQueryRetrievalPointers (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsGetCompression (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
VOID
NtfsChangeAttributeCompression (
IN PIRP_CONTEXT IrpContext,
IN PSCB Scb,
IN PVCB Vcb,
IN PCCB Ccb,
IN USHORT CompressionState
);
NTSTATUS
NtfsSetCompression (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsReadCompression (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsWriteCompression (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsMarkAsSystemHive (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsGetStatistics (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
#define NtfsMapPageInBitmap(A,B,C,D,E,F) NtfsMapOrPinPageInBitmap(A,B,C,D,E,F,FALSE)
#define NtfsPinPageInBitmap(A,B,C,D,E,F) NtfsMapOrPinPageInBitmap(A,B,C,D,E,F,TRUE)
VOID
NtfsMapOrPinPageInBitmap (
IN PIRP_CONTEXT IrpContext,
IN PVCB Vcb,
IN LCN Lcn,
OUT PLCN StartingLcn,
IN OUT PRTL_BITMAP Bitmap,
OUT PBCB *BitmapBcb,
IN BOOLEAN AlsoPinData
);
BOOLEAN
NtfsAddRecentlyDeallocated (
IN PVCB Vcb,
IN LCN Lcn,
IN OUT PRTL_BITMAP Bitmap
);
#define BYTES_PER_PAGE (PAGE_SIZE)
#define BITS_PER_PAGE (BYTES_PER_PAGE * 8)
NTSTATUS
NtfsGetVolumeData (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsGetVolumeBitmap (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsGetRetrievalPointers (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsGetMftRecord (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsMoveFile (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsIsVolumeDirty (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
NTSTATUS
NtfsSetExtendedDasdIo (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NtfsCommonFileSystemControl)
#pragma alloc_text(PAGE, NtfsDirtyVolume)
#pragma alloc_text(PAGE, NtfsDismountVolume)
#pragma alloc_text(PAGE, NtfsFsdFileSystemControl)
#pragma alloc_text(PAGE, NtfsGetDiskGeometry)
#pragma alloc_text(PAGE, NtfsGetVolumeLabel)
#pragma alloc_text(PAGE, NtfsIsBootSectorNtfs)
#pragma alloc_text(PAGE, NtfsIsVolumeDirty)
#pragma alloc_text(PAGE, NtfsLockVolume)
#pragma alloc_text(PAGE, NtfsMarkAsSystemHive)
#pragma alloc_text(PAGE, NtfsMountVolume)
#pragma alloc_text(PAGE, NtfsOpenRootDirectory)
#pragma alloc_text(PAGE, NtfsOpenSystemFile)
#pragma alloc_text(PAGE, NtfsOplockRequest)
#pragma alloc_text(PAGE, NtfsReadBootSector)
#pragma alloc_text(PAGE, NtfsSetAndGetVolumeTimes)
#pragma alloc_text(PAGE, NtfsSetTotalAllocatedField)
#pragma alloc_text(PAGE, NtfsUnlockVolume)
#pragma alloc_text(PAGE, NtfsUserFsRequest)
#pragma alloc_text(PAGE, NtfsVerifyVolume)
#pragma alloc_text(PAGE, NtfsQueryRetrievalPointers)
#pragma alloc_text(PAGE, NtfsGetCompression)
#pragma alloc_text(PAGE, NtfsSetCompression)
#pragma alloc_text(PAGE, NtfsReadCompression)
#pragma alloc_text(PAGE, NtfsWriteCompression)
#pragma alloc_text(PAGE, NtfsGetStatistics)
#pragma alloc_text(PAGE, NtfsGetVolumeData)
#pragma alloc_text(PAGE, NtfsGetVolumeBitmap)
#pragma alloc_text(PAGE, NtfsGetRetrievalPointers)
#pragma alloc_text(PAGE, NtfsGetMftRecord)
#pragma alloc_text(PAGE, NtfsMoveFile)
#pragma alloc_text(PAGE, NtfsSetExtendedDasdIo)
#endif
NTSTATUS
NtfsFsdFileSystemControl (
IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine implements the FSD part of File System Control.
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
--*/
{
TOP_LEVEL_CONTEXT TopLevelContext;
PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
BOOLEAN Wait;
BOOLEAN Retry = FALSE;
NTSTATUS Status = STATUS_SUCCESS;
PIRP_CONTEXT IrpContext = NULL;
PIO_STACK_LOCATION IrpSp;
ASSERT_IRP( Irp );
UNREFERENCED_PARAMETER( VolumeDeviceObject );
PAGED_CODE();
DebugTrace( +1, Dbg, ("NtfsFsdFileSystemControl\n") );
//
// Call the common File System 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();
ThreadTopLevelContext = NtfsSetTopLevelIrp( &TopLevelContext, FALSE, FALSE );
do {
try {
//
// We are either initiating this request or retrying it.
//
if (IrpContext == NULL) {
IrpContext = NtfsCreateIrpContext( Irp, Wait );
NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
} else if (Status == STATUS_LOG_FILE_FULL) {
Retry = TRUE;
NtfsCheckpointForLogFileFull( IrpContext );
}
IrpSp = IoGetCurrentIrpStackLocation(Irp);
if (IrpSp->MinorFunction == IRP_MN_MOUNT_VOLUME) {
Status = NtfsPostRequest( IrpContext, Irp );
} else {
//
// The SetCompression control is a long-winded function that has
// to rewrite the entire stream, and has to tolerate log file full
// conditions. If this is the first pass through we initialize some
// fields in the NextIrpSp to allow us to resume the set compression
// operation.
//
// David Goebel 1/3/96: Changed to next stack location so that we
// don't wipe out buffer length values. These Irps are never
// dispatched, so the next stack location will not be disturbed.
//
if ((IrpSp->MinorFunction == IRP_MN_USER_FS_REQUEST) &&
((IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_SET_COMPRESSION) ||
(IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_MOVE_FILE))) {
if (!Retry) {
PIO_STACK_LOCATION NextIrpSp;
NextIrpSp = IoGetNextIrpStackLocation( Irp );
NextIrpSp->Parameters.FileSystemControl.OutputBufferLength = MAXULONG;
NextIrpSp->Parameters.FileSystemControl.InputBufferLength = MAXULONG;
}
}
Status = NtfsCommonFileSystemControl( IrpContext, Irp );
}
break;
} except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
//
// We had some trouble trying to perform the requested
// operation, so we'll abort the I/O request with
// the error status that we get back from the
// execption code
//
Status = NtfsProcessException( IrpContext, Irp, GetExceptionCode() );
}
} while (Status == STATUS_CANT_WAIT ||
Status == STATUS_LOG_FILE_FULL);
if (ThreadTopLevelContext == &TopLevelContext) {
NtfsRestoreTopLevelIrp( ThreadTopLevelContext );
}
FsRtlExitFileSystem();
//
// And return to our caller
//
DebugTrace( -1, Dbg, ("NtfsFsdFileSystemControl -> %08lx\n", Status) );
return Status;
}
NTSTATUS
NtfsCommonFileSystemControl (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
This is the common routine for File System Control 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;
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_IRP( Irp );
PAGED_CODE();
//
// Get the current Irp stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace( +1, Dbg, ("NtfsCommonFileSystemControl\n") );
DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
//
// 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_MOUNT_VOLUME:
Status = NtfsMountVolume( IrpContext, Irp );
break;
case IRP_MN_USER_FS_REQUEST:
Status = NtfsUserFsRequest( IrpContext, Irp );
break;
default:
DebugTrace( 0, Dbg, ("Invalid Minor Function %08lx\n", IrpSp->MinorFunction) );
NtfsCompleteRequest( &IrpContext, &Irp, Status = STATUS_INVALID_DEVICE_REQUEST );
break;
}
//
// And return to our caller
//
DebugTrace( -1, Dbg, ("NtfsCommonFileSystemControl -> %08lx\n", Status) );
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NtfsMountVolume (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
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 an NTFS volume,
and create the VCB and root SCB/FCB 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 an NTFS volume.
3. If it is not an NTFS 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 freeing the cached volume file,
delete the VCB, hook in the old VCB, and complete the IRP.
5. Otherwise create a root SCB, recover the volume, create Fsp threads
as necessary, and complete the IRP.
Arguments:
Irp - Supplies the Irp to process
Return Value:
NTSTATUS - The return status for the operation
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
PATTRIBUTE_RECORD_HEADER Attribute;
PDEVICE_OBJECT DeviceObjectWeTalkTo;
PVPB Vpb;
PVOLUME_DEVICE_OBJECT VolDo;
PVCB Vcb;
PBCB BootBcb = NULL;
PPACKED_BOOT_SECTOR BootSector;
PSCB BootScb = NULL;
#ifdef _CAIRO_
PSCB QuotaDataScb = NULL;
#endif // _CAIRO_
POBJECT_NAME_INFORMATION DeviceObjectName = NULL;
ULONG DeviceObjectNameLength;
PBCB Bcbs[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
ULONG FirstNonMirroredCluster;
ULONG MirroredMftRange;
ULONG i;
IO_STATUS_BLOCK IoStatus;
BOOLEAN UpdatesApplied;
BOOLEAN VcbAcquired = FALSE;
BOOLEAN MountFailed = TRUE;
BOOLEAN CloseAttributes = FALSE;
BOOLEAN UpdateVersion = FALSE;
BOOLEAN WriteProtected;
LONGLONG LlTemp1;
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_IRP( Irp );
PAGED_CODE();
//
//**** The following code is only temporary and is used to disable NTFS
//**** from mounting any volumes
//
if (NtfsDisable) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_UNRECOGNIZED_VOLUME );
return STATUS_UNRECOGNIZED_VOLUME;
}
//
// Reject floppies
//
if (FlagOn( IoGetCurrentIrpStackLocation(Irp)->
Parameters.MountVolume.Vpb->
RealDevice->Characteristics, FILE_FLOPPY_DISKETTE ) ) {
Irp->IoStatus.Information = 0;
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_UNRECOGNIZED_VOLUME );
return STATUS_UNRECOGNIZED_VOLUME;
}
//
// Get the current Irp stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace( +1, Dbg, ("NtfsMountVolume\n") );
//
// Save some references to make our life a little easier
//
DeviceObjectWeTalkTo = IrpSp->Parameters.MountVolume.DeviceObject;
Vpb = IrpSp->Parameters.MountVolume.Vpb;
ClearFlag( Vpb->RealDevice->Flags, DO_VERIFY_VOLUME );
//
// Acquire exclusive global access
//
NtfsAcquireExclusiveGlobal( IrpContext );
//
// Now is a convenient time to look through the queue of Vcb's to see if there
// are any which can be deleted.
//
try {
PLIST_ENTRY Links;
for (Links = NtfsData.VcbQueue.Flink;
Links != &NtfsData.VcbQueue;
Links = Links->Flink) {
Vcb = CONTAINING_RECORD( Links, VCB, VcbLinks );
if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED ) &&
(Vcb->CloseCount == 0) &&
FlagOn( Vcb->VcbState, VCB_STATE_PERFORMED_DISMOUNT ) &&
(Vcb->LogFileObject != NULL)) {
//
// Now we can check to see if we should perform the teardown
// on this Vcb. The release Vcb routine below can do all of
// the checks correctly. Make this appear to from a close
// call since there is no special biasing for this case.
//
IrpContext->Vcb = Vcb;
NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
if (!FlagOn( Vcb->VcbState, VCB_STATE_DELETE_UNDERWAY )) {
NtfsReleaseGlobal( IrpContext );
NtfsReleaseVcbCheckDelete( IrpContext,
Vcb,
IRP_MJ_CLOSE,
NULL );
//
// Only do one since we have lost our place in the Vcb list.
//
NtfsAcquireExclusiveGlobal( IrpContext );
break;
} else {
NtfsReleaseVcb( IrpContext, Vcb );
}
}
}
} except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
//
// Make sure we own the global resource for mount. We can only raise above
// in the DeleteVcb path when we don't hold the resource.
//
NtfsAcquireExclusiveGlobal( IrpContext );
}
Vcb = NULL;
try {
PFILE_RECORD_SEGMENT_HEADER MftBuffer;
PVOID Mft2Buffer;
//
// 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( NtfsData.DriverObject,
sizeof(VOLUME_DEVICE_OBJECT) - sizeof(DEVICE_OBJECT),
NULL,
FILE_DEVICE_DISK_FILE_SYSTEM,
0,
FALSE,
(PDEVICE_OBJECT *)&VolDo))) {
try_return( Status );
}
//
// Our alignment requirement is the larger of the processor alignment requirement
// already in the volume device object and that in the DeviceObjectWeTalkTo
//
if (DeviceObjectWeTalkTo->AlignmentRequirement > VolDo->DeviceObject.AlignmentRequirement) {
VolDo->DeviceObject.AlignmentRequirement = DeviceObjectWeTalkTo->AlignmentRequirement;
}
ClearFlag( VolDo->DeviceObject.Flags, DO_DEVICE_INITIALIZING );
//
// Add one more to the stack size requirements for our device
//
VolDo->DeviceObject.StackSize = DeviceObjectWeTalkTo->StackSize + 1;
//
// Initialize the overflow queue for the volume
//
VolDo->OverflowQueueCount = 0;
InitializeListHead( &VolDo->OverflowQueue );
//
// Get a reference to the Vcb hanging off the end of the volume device object
// we just created
//
IrpContext->Vcb = Vcb = &VolDo->Vcb;
//
// Set the device object field in the vpb to point to our new volume device
// object
//
Vpb->DeviceObject = (PDEVICE_OBJECT)VolDo;
//
// Initialize the Vcb. Set checkpoint
// in progress (to prevent a real checkpoint from occuring until we
// are done).
//
NtfsInitializeVcb( IrpContext, Vcb, DeviceObjectWeTalkTo, Vpb );
NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
VcbAcquired= TRUE;
//
// Query the device we talk to for this geometry and setup enough of the
// vcb to read in the boot sectors. This is a temporary setup until
// we've read in the actual boot sector and got the real cluster factor.
//
{
DISK_GEOMETRY DiskGeometry;
PARTITION_INFORMATION PartitionInfo;
WriteProtected = NtfsGetDiskGeometry( IrpContext,
DeviceObjectWeTalkTo,
&DiskGeometry,
&PartitionInfo );
//
// If the sector size is greater than the page size, it is probably
// a bogus return, but we cannot use the device.
//
if (DiskGeometry.BytesPerSector > PAGE_SIZE) {
NtfsRaiseStatus( IrpContext, STATUS_BAD_DEVICE_TYPE, NULL, NULL );
}
Vcb->BytesPerSector = DiskGeometry.BytesPerSector;
Vcb->BytesPerCluster = Vcb->BytesPerSector;
Vcb->NumberSectors = PartitionInfo.PartitionLength.QuadPart / DiskGeometry.BytesPerSector;
//
// Fail the mount if the number of sectors is less than 16. Otherwise our mount logic
// won't work.
//
if (Vcb->NumberSectors <= 0x10) {
try_return( Status = STATUS_UNRECOGNIZED_VOLUME );
}
Vcb->ClusterMask = Vcb->BytesPerCluster - 1;
Vcb->InverseClusterMask = ~Vcb->ClusterMask;
for (Vcb->ClusterShift = 0, i = Vcb->BytesPerCluster; i > 1; i = i / 2) {
Vcb->ClusterShift += 1;
}
Vcb->ClustersPerPage = PAGE_SIZE >> Vcb->ClusterShift;
//
// Set the sector size in our device object.
//
VolDo->DeviceObject.SectorSize = (USHORT) Vcb->BytesPerSector;
}
//
// Read in the Boot sector, or spare boot sector, on exit of this try
// body we will have set bootbcb and bootsector.
//
NtfsReadBootSector( IrpContext, Vcb, &BootScb, &BootBcb, (PVOID *)&BootSector );
//
// Check if this is an NTFS volume
//
if (!NtfsIsBootSectorNtfs( BootSector, Vcb )) {
DebugTrace( 0, Dbg, ("Not an NTFS volume\n") );
try_return( Status = STATUS_UNRECOGNIZED_VOLUME );
}
//
// Not return write protected if the drive is really Ntfs.
//
if (WriteProtected) {
DebugTrace( 0, Dbg, ("Write protected volume\n") );
try_return( Status = STATUS_MEDIA_WRITE_PROTECTED );
}
//
// Now that we have a real boot sector on a real NTFS volume we can
// really set the proper Vcb fields.
//
{
BIOS_PARAMETER_BLOCK Bpb;
NtfsUnpackBios( &Bpb, &BootSector->PackedBpb );
Vcb->BytesPerSector = Bpb.BytesPerSector;
Vcb->BytesPerCluster = Bpb.BytesPerSector * Bpb.SectorsPerCluster;
Vcb->NumberSectors = BootSector->NumberSectors;
Vcb->MftStartLcn = BootSector->MftStartLcn;
Vcb->Mft2StartLcn = BootSector->Mft2StartLcn;
Vcb->ClusterMask = Vcb->BytesPerCluster - 1;
Vcb->InverseClusterMask = ~Vcb->ClusterMask;
for (Vcb->ClusterShift = 0, i = Vcb->BytesPerCluster; i > 1; i = i / 2) {
Vcb->ClusterShift += 1;
}
//
// If the cluster size is greater than the page size then set this value to 1.
//
Vcb->ClustersPerPage = PAGE_SIZE >> Vcb->ClusterShift;
if (Vcb->ClustersPerPage == 0) {
Vcb->ClustersPerPage = 1;
}
//
// File records can be smaller, equal or larger than the cluster size. Initialize
// both ClustersPerFileRecordSegment and FileRecordsPerCluster.
//
// If the value in the boot sector is positive then it signifies the
// clusters/structure. If negative then it signifies the shift value
// to obtain the structure size.
//
if (BootSector->ClustersPerFileRecordSegment < 0) {
Vcb->BytesPerFileRecordSegment = 1 << (-1 * BootSector->ClustersPerFileRecordSegment);
//
// Initialize the other Mft/Cluster relationship numbers in the Vcb
// based on whether the clusters are larger or smaller than file
// records.
//
if (Vcb->BytesPerFileRecordSegment < Vcb->BytesPerCluster) {
Vcb->FileRecordsPerCluster = Vcb->BytesPerCluster / Vcb->BytesPerFileRecordSegment;
} else {
Vcb->ClustersPerFileRecordSegment = Vcb->BytesPerFileRecordSegment / Vcb->BytesPerCluster;
}
} else {
Vcb->BytesPerFileRecordSegment = BytesFromClusters( Vcb, BootSector->ClustersPerFileRecordSegment );
Vcb->ClustersPerFileRecordSegment = BootSector->ClustersPerFileRecordSegment;
}
for (Vcb->MftShift = 0, i = Vcb->BytesPerFileRecordSegment; i > 1; i = i / 2) {
Vcb->MftShift += 1;
}
//
// We want to shift between file records and clusters regardless of which is larger.
// Compute the shift value here. Anyone using this value will have to know which
// way to shift.
//
Vcb->MftToClusterShift = Vcb->MftShift - Vcb->ClusterShift;
if (Vcb->ClustersPerFileRecordSegment == 0) {
Vcb->MftToClusterShift = Vcb->ClusterShift - Vcb->MftShift;
}
//
// Compute the default index allocation buffer size.
//
if (BootSector->DefaultClustersPerIndexAllocationBuffer < 0) {
Vcb->DefaultBytesPerIndexAllocationBuffer = 1 << (-1 * BootSector->DefaultClustersPerIndexAllocationBuffer);
//
// Determine whether the index allocation buffer is larger/smaller
// than the cluster size to determine the block size.
//
if (Vcb->DefaultBytesPerIndexAllocationBuffer < Vcb->BytesPerCluster) {
Vcb->DefaultBlocksPerIndexAllocationBuffer = Vcb->DefaultBytesPerIndexAllocationBuffer / DEFAULT_INDEX_BLOCK_SIZE;
} else {
Vcb->DefaultBlocksPerIndexAllocationBuffer = Vcb->DefaultBytesPerIndexAllocationBuffer / Vcb->BytesPerCluster;
}
} else {
Vcb->DefaultBlocksPerIndexAllocationBuffer = BootSector->DefaultClustersPerIndexAllocationBuffer;
Vcb->DefaultBytesPerIndexAllocationBuffer = BytesFromClusters( Vcb, Vcb->DefaultBlocksPerIndexAllocationBuffer );
}
//
// Now compute our volume specific constants that are stored in
// the Vcb. The total number of clusters is:
//
// (NumberSectors * BytesPerSector) / BytesPerCluster
//
Vcb->TotalClusters = LlClustersFromBytesTruncate( Vcb,
Vcb->NumberSectors * Vcb->BytesPerSector );
//
// Compute the attribute flags mask for this volume for this volume.
//
Vcb->AttributeFlagsMask = 0xffff;
if (Vcb->BytesPerCluster > 0x1000) {
ClearFlag( Vcb->AttributeFlagsMask, ATTRIBUTE_FLAG_COMPRESSION_MASK );
}
//
// For now, an attribute is considered "moveable" if it is at
// least 5/16 of the file record. This constant should only
// be changed i conjunction with the MAX_MOVEABLE_ATTRIBUTES
// constant. (The product of the two should be a little less
// than or equal to 1.)
//
Vcb->BigEnoughToMove = Vcb->BytesPerFileRecordSegment * 5 / 16;
//
// Set the serial number in the Vcb
//
Vcb->VolumeSerialNumber = BootSector->SerialNumber;
Vpb->SerialNumber = ((ULONG)BootSector->SerialNumber);
}
//
// Initialize recovery state.
//
NtfsInitializeRestartTable( sizeof(OPEN_ATTRIBUTE_ENTRY),
INITIAL_NUMBER_ATTRIBUTES,
&Vcb->OpenAttributeTable );
NtfsInitializeRestartTable( sizeof(TRANSACTION_ENTRY),
INITIAL_NUMBER_TRANSACTIONS,
&Vcb->TransactionTable );
//
// Now start preparing to restart the volume.
//
//
// Create the Mft and Log File Scbs and prepare to read them.
// The Mft and mirror length will be the first 4 file records or
// the first cluster.
//
FirstNonMirroredCluster = ClustersFromBytes( Vcb, 4 * Vcb->BytesPerFileRecordSegment );
MirroredMftRange = 4 * Vcb->BytesPerFileRecordSegment;
if (MirroredMftRange < Vcb->BytesPerCluster) {
MirroredMftRange = Vcb->BytesPerCluster;
}
NtfsOpenSystemFile( IrpContext,
&Vcb->MftScb,
Vcb,
MASTER_FILE_TABLE_NUMBER,
MirroredMftRange,
$DATA,
TRUE );
CcSetAdditionalCacheAttributes( Vcb->MftScb->FileObject, TRUE, TRUE );
LlTemp1 = FirstNonMirroredCluster;
(VOID)NtfsAddNtfsMcbEntry( &Vcb->MftScb->Mcb,
(LONGLONG)0,
Vcb->MftStartLcn,
(LONGLONG)FirstNonMirroredCluster,
FALSE );
//
// Now the same for Mft2
//
NtfsOpenSystemFile( IrpContext,
&Vcb->Mft2Scb,
Vcb,
MASTER_FILE_TABLE2_NUMBER,
MirroredMftRange,
$DATA,
TRUE );
CcSetAdditionalCacheAttributes( Vcb->Mft2Scb->FileObject, TRUE, TRUE );
(VOID)NtfsAddNtfsMcbEntry( &Vcb->Mft2Scb->Mcb,
(LONGLONG)0,
Vcb->Mft2StartLcn,
(LONGLONG)FirstNonMirroredCluster,
FALSE );
//
// Create the dasd system file, we do it here because we need to dummy
// up the mcb for it, and that way everything else in NTFS won't need
// to know that it is a special file. We need to do this after
// cluster allocation initialization because that computes the total
// clusters on the volume. Also for verification purposes we will
// set and get the times off of the volume.
//
// Open it now before the Log File, because that is the first time
// anyone may want to mark the volume corrupt.
//
NtfsOpenSystemFile( IrpContext,
&Vcb->VolumeDasdScb,
Vcb,
VOLUME_DASD_NUMBER,
LlBytesFromClusters( Vcb, Vcb->TotalClusters ),
$DATA,
FALSE );
(VOID)NtfsAddNtfsMcbEntry( &Vcb->VolumeDasdScb->Mcb,
(LONGLONG)0,
(LONGLONG)0,
Vcb->TotalClusters,
FALSE );
SetFlag( Vcb->VolumeDasdScb->Fcb->FcbState, FCB_STATE_DUP_INITIALIZED );
Vcb->VolumeDasdScb->Fcb->LinkCount =
Vcb->VolumeDasdScb->Fcb->TotalLinks = 1;
//
// We want to read the first four record segments of each of these
// files. We do this so that we don't have a cache miss when we
// look up the real allocation below.
//
for (i = 0; i < 4; i++) {
FILE_REFERENCE FileReference;
PATTRIBUTE_RECORD_HEADER FirstAttribute;
NtfsSetSegmentNumber( &FileReference, 0, i );
FileReference.SequenceNumber = (USHORT)i;
NtfsReadFileRecord( IrpContext,
Vcb,
&FileReference,
&Bcbs[i*2],
&MftBuffer,
&FirstAttribute,
NULL );
//
// If any of these file records are bad then
// fail the mount.
//
if (!NtfsCheckFileRecord( IrpContext, Vcb, MftBuffer )) {
try_return( Status = STATUS_DISK_CORRUPT_ERROR );
}
NtfsMapStream( IrpContext,
Vcb->Mft2Scb,
(LONGLONG)i,
Vcb->BytesPerFileRecordSegment,
&Bcbs[i*2 + 1],
&Mft2Buffer );
}
//
// The last file record was the Volume Dasd, so check the version number.
//
Attribute = NtfsFirstAttribute(MftBuffer);
while (TRUE) {
Attribute = NtfsGetNextRecord(Attribute);
if (Attribute->TypeCode == $VOLUME_INFORMATION) {
PVOLUME_INFORMATION VolumeInformation;
VolumeInformation = (PVOLUME_INFORMATION)NtfsAttributeValue(Attribute);
if (VolumeInformation->MajorVersion != 2) {
if (VolumeInformation->MajorVersion != 1) {
NtfsRaiseStatus( IrpContext, STATUS_WRONG_VOLUME, NULL, NULL );
}
#ifdef _CAIRO_
if (NtfsUpdateTo20) {
UpdateVersion = TRUE;
}
#else
if (VolumeInformation->MinorVersion <= 1) {
UpdateVersion = TRUE;
} else if (NtfsDefragMftEnabled) {
SetFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_PERMITTED );
}
#endif
} else if (NtfsDefragMftEnabled) {
SetFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_PERMITTED );
}
break;
}
if (Attribute->TypeCode == $END) {
NtfsRaiseStatus( IrpContext, STATUS_WRONG_VOLUME, NULL, NULL );
}
}
//
// Create the log file Scb and really look up its size.
//
NtfsOpenSystemFile( IrpContext,
&Vcb->LogFileScb,
Vcb,
LOG_FILE_NUMBER,
0,
$DATA,
TRUE );
Vcb->LogFileObject = Vcb->LogFileScb->FileObject;
CcSetAdditionalCacheAttributes( Vcb->LogFileScb->FileObject, TRUE, TRUE );
//
// Lookup the log file mapping now, since we will not go to the
// disk for allocation information any more once we set restart
// in progress.
//
(VOID)NtfsPreloadAllocation( IrpContext, Vcb->LogFileScb, 0, MAXLONGLONG );
//
// Now we have to unpin everything before restart, because it generally
// has to uninitialize everything.
//
NtfsUnpinBcb( &BootBcb );
for (i = 0; i < 8; i++) {
NtfsUnpinBcb( &Bcbs[i] );
}
//
// Purge the Mft, since we only read the first four file
// records, not necessarily an entire page!
//
CcPurgeCacheSection( &Vcb->MftScb->NonpagedScb->SegmentObject, NULL, 0, FALSE );
//
// Now start up the log file and perform Restart. This calls will
// unpin and remap the Mft Bcb's. The MftBuffer variables above
// may no longer point to the correct range of bytes. This is OK
// if they are never referenced.
//
// Put a try-except around this to catch any restart failures.
// This is important in order to allow us to limp along until
// autochk gets a chance to run.
//
// We set restart in progress first, to prevent us from looking up any
// more run information (now that we know where the log file is at!)
//
SetFlag(Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS);
try {
Status = STATUS_SUCCESS;
NtfsStartLogFile( Vcb->LogFileScb,
Vcb );
//
// We call the cache manager again with the stream files for the Mft and
// Mft mirror as we didn't have a log handle for the first call.
//
CcSetLogHandleForFile( Vcb->MftScb->FileObject,
Vcb->LogHandle,
&LfsFlushToLsn );
CcSetLogHandleForFile( Vcb->Mft2Scb->FileObject,
Vcb->LogHandle,
&LfsFlushToLsn );
CloseAttributes = TRUE;
UpdatesApplied = NtfsRestartVolume( IrpContext, Vcb );
//
// For right now, we will charge ahead with a dirty volume, no
// matter what the exception was. Later we will have to be
// defensive and use a filter.
//
} except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
Status = GetExceptionCode();
//
// If the error is STATUS_LOG_FILE_FULL then it means that
// we couldn't complete the restart. Mark the volume dirty in
// this case. Don't return this error code.
//
if (Status == STATUS_LOG_FILE_FULL) {
Status = STATUS_DISK_CORRUPT_ERROR;
IrpContext->ExceptionStatus = STATUS_DISK_CORRUPT_ERROR;
}
}
if (!NT_SUCCESS(Status)) {
LONGLONG VolumeDasdOffset;
NtfsSetAndGetVolumeTimes( IrpContext, Vcb, TRUE );
//
// Now flush it out, so chkdsk can see it with Dasd.
// Clear the error in the IrpContext so that this
// flush will succeed. Otherwise CommonWrite will
// return FILE_LOCK_CONFLICT.
//
IrpContext->ExceptionStatus = STATUS_SUCCESS;
VolumeDasdOffset = VOLUME_DASD_NUMBER << Vcb->MftShift;
CcFlushCache( &Vcb->MftScb->NonpagedScb->SegmentObject,
(PLARGE_INTEGER)&VolumeDasdOffset,
Vcb->BytesPerFileRecordSegment,
NULL );
try_return( Status );
}
//
// Now flush the Mft copies, because we are going to shut the real
// one down and reopen it for real.
//
CcFlushCache( &Vcb->Mft2Scb->NonpagedScb->SegmentObject, NULL, 0, &IoStatus );
if (NT_SUCCESS(IoStatus.Status)) {
CcFlushCache( &Vcb->MftScb->NonpagedScb->SegmentObject, NULL, 0, &IoStatus );
}
if (!NT_SUCCESS(IoStatus.Status)) {
NtfsNormalizeAndRaiseStatus( IrpContext,
IoStatus.Status,
STATUS_UNEXPECTED_IO_ERROR );
}
//
// Show that the restart is complete, and it is safe to go to
// the disk for the Mft allocation.
//
ClearFlag(Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS);
//
// Set the Mft sizes back down to the part which is guaranteed to
// be contiguous for now. Important on large page size systems!
//
Vcb->MftScb->Header.AllocationSize.QuadPart =
Vcb->MftScb->Header.FileSize.QuadPart =
Vcb->MftScb->Header.ValidDataLength.QuadPart = FirstNonMirroredCluster << Vcb->ClusterShift;
//
// Pin the first four file records
//
for (i = 0; i < 4; i++) {
NtfsPinStream( IrpContext,
Vcb->MftScb,
(LONGLONG)(i << Vcb->MftShift),
Vcb->BytesPerFileRecordSegment,
&Bcbs[i*2],
(PVOID *)&MftBuffer );
//
// Implement the one-time conversion of the Sequence Number
// for the Mft's own file record from 0 to 1.
//
if (i == 0) {
if (MftBuffer->SequenceNumber != 1) {
NtfsPostVcbIsCorrupt( (PVOID)Vcb, 0, NULL, NULL );
}
}
NtfsPinStream( IrpContext,
Vcb->Mft2Scb,
(LONGLONG)(i << Vcb->MftShift),
Vcb->BytesPerFileRecordSegment,
&Bcbs[i*2 + 1],
&Mft2Buffer );
}
//
// Now we need to uninitialize and purge the Mft and Mft2. This is
// because we could have only a partially filled page at the end, and
// we need to do real reads of whole pages now.
//
//
// Uninitialize and reinitialize the large mcbs so that we can reload
// it from the File Record.
//
NtfsUnloadNtfsMcbRange( &Vcb->MftScb->Mcb, (LONGLONG) 0, MAXLONGLONG, TRUE, FALSE );
NtfsUnloadNtfsMcbRange( &Vcb->Mft2Scb->Mcb, (LONGLONG) 0, MAXLONGLONG, TRUE, FALSE );
//
// Mark both of them as uninitialized.
//
ClearFlag( Vcb->MftScb->ScbState, SCB_STATE_HEADER_INITIALIZED |
SCB_STATE_FILE_SIZE_LOADED );
ClearFlag( Vcb->Mft2Scb->ScbState, SCB_STATE_HEADER_INITIALIZED |
SCB_STATE_FILE_SIZE_LOADED );
//
// Now load up the real allocation from just the first file record.
//
if (Vcb->FileRecordsPerCluster == 0) {
NtfsPreloadAllocation( IrpContext,
Vcb->MftScb,
0,
(FIRST_USER_FILE_NUMBER - 1) << Vcb->MftToClusterShift );
} else {
NtfsPreloadAllocation( IrpContext,
Vcb->MftScb,
0,
(FIRST_USER_FILE_NUMBER - 1) >> Vcb->MftToClusterShift );
}
NtfsPreloadAllocation( IrpContext, Vcb->Mft2Scb, 0, MAXLONGLONG );
//
// We update the Mft and the Mft mirror before we delete the current
// stream file for the Mft. We know we can read the true attributes
// for the Mft and the Mirror because we initialized their sizes
// above through the first few records in the Mft.
//
NtfsUpdateScbFromAttribute( IrpContext, Vcb->MftScb, NULL );
NtfsUpdateScbFromAttribute( IrpContext, Vcb->Mft2Scb, NULL );
//
// Unpin the Bcb's for the Mft files before uninitializing.
//
for (i = 0; i < 8; i++) {
NtfsUnpinBcb( &Bcbs[i] );
}
//
// Now close and purge the Mft, and recreate its stream so that
// the Mft is in a normal state, and we can close the rest of
// the attributes from restart. We need to bump the close count
// to keep the scb around while we do this little bit of trickery
//
{
Vcb->MftScb->CloseCount += 1;
NtfsDeleteInternalAttributeStream( Vcb->MftScb, TRUE );
NtfsCreateInternalAttributeStream( IrpContext, Vcb->MftScb, FALSE );
//
// Tell the cache manager the file sizes for the MFT. It is possible
// that the shared cache map did not go away on the DeleteInternalAttributeStream
// call above. In that case the Cache Manager has the file sizes from
// restart.
//
CcSetFileSizes( Vcb->MftScb->FileObject,
(PCC_FILE_SIZES) &Vcb->MftScb->Header.AllocationSize );
CcSetAdditionalCacheAttributes( Vcb->MftScb->FileObject, TRUE, FALSE );
Vcb->MftScb->CloseCount -= 1;
}
//
// We want to read all of the file records for the Mft to put
// its complete mapping into the Mcb.
//
NtfsPreloadAllocation( IrpContext, Vcb->MftScb, 0, MAXLONGLONG );
//
// Close the boot file (get rid of it because we do not know its proper
// size, and the Scb may be inconsistent).
//
NtfsDeleteInternalAttributeStream( BootScb, TRUE );
BootScb = NULL;
//
// Closing the attributes from restart has to occur here after
// the Mft is clean, because flushing these files will cause
// file size updates to occur, etc.
//
Status = NtfsCloseAttributesFromRestart( IrpContext, Vcb );
CloseAttributes = FALSE;
if (!NT_SUCCESS( Status )) {
NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
}
NtfsAcquireCheckpoint( IrpContext, Vcb );
//
// Show that it is ok to checkpoint now.
//
ClearFlag(Vcb->CheckpointFlags, VCB_CHECKPOINT_IN_PROGRESS);
//
// Clear the flag indicating that we won't defrag the volume.
//
ClearFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_ENABLED );
NtfsReleaseCheckpoint( IrpContext, Vcb );
//
// We always need to write a checkpoint record so that we have
// a checkpoint on the disk before we modify any files.
//
NtfsCheckpointVolume( IrpContext,
Vcb,
FALSE,
UpdatesApplied,
UpdatesApplied,
Vcb->LastRestartArea );
//
// Now set the defrag enabled flag.
//
NtfsAcquireCheckpoint( IrpContext, Vcb );
SetFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_ENABLED );
NtfsReleaseCheckpoint( IrpContext, Vcb );
//
// Open the Root Directory.
//
NtfsOpenRootDirectory( IrpContext, Vcb );
/* Format is using wrong attribute definitions
//
// At this point we are ready to use the volume normally. We could
// open the remaining system files by name, but for now we will go
// ahead and open them by file number.
//
NtfsOpenSystemFile( IrpContext,
&Vcb->AttributeDefTableScb,
Vcb,
ATTRIBUTE_DEF_TABLE_NUMBER,
0,
$DATA,
FALSE );
//
// Read in the attribute definitions.
//
{
IO_STATUS_BLOCK IoStatus;
PSCB Scb = Vcb->AttributeDefTableScb;
if ((Scb->Header.FileSize.HighPart != 0) || (Scb->Header.FileSize.LowPart == 0)) {
NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
}
Vcb->AttributeDefinitions = NtfsAllocatePool(PagedPool, Scb->Header.FileSize.LowPart );
CcCopyRead( Scb->FileObject,
&Li0,
Scb->Header.FileSize.LowPart,
TRUE,
Vcb->AttributeDefinitions,
&IoStatus );
if (!NT_SUCCESS(IoStatus.Status)) {
NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
}
}
*/
//
// Just point to our own attribute definitions for now.
//
Vcb->AttributeDefinitions = NtfsAttributeDefinitions;
//
// Open the upcase table.
//
NtfsOpenSystemFile( IrpContext,
&Vcb->UpcaseTableScb,
Vcb,
UPCASE_TABLE_NUMBER,
0,
$DATA,
FALSE );
//
// Read in the upcase table.
//
{
IO_STATUS_BLOCK IoStatus;
PSCB Scb = Vcb->UpcaseTableScb;
if ((Scb->Header.FileSize.HighPart != 0) || (Scb->Header.FileSize.LowPart < 512)) {
NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
}
Vcb->UpcaseTable = NtfsAllocatePool(PagedPool, Scb->Header.FileSize.LowPart );
Vcb->UpcaseTableSize = Scb->Header.FileSize.LowPart / 2;
CcCopyRead( Scb->FileObject,
&Li0,
Scb->Header.FileSize.LowPart,
TRUE,
Vcb->UpcaseTable,
&IoStatus );
if (!NT_SUCCESS(IoStatus.Status)) {
NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
}
//
// If we do not have a global upcase table yet then make this one the global one
//
if (NtfsData.UpcaseTable == NULL) {
NtfsData.UpcaseTable = Vcb->UpcaseTable;
NtfsData.UpcaseTableSize = Vcb->UpcaseTableSize;
//
// Otherwise if this one perfectly matches the global upcase table then throw
// this one back and use the global one
//
} else if ((NtfsData.UpcaseTableSize == Vcb->UpcaseTableSize)
&&
(RtlCompareMemory( NtfsData.UpcaseTable,
Vcb->UpcaseTable,
Vcb->UpcaseTableSize) == Vcb->UpcaseTableSize)) {
ExFreePool( Vcb->UpcaseTable );
Vcb->UpcaseTable = NtfsData.UpcaseTable;
}
}
NtfsOpenSystemFile( IrpContext,
&Vcb->BitmapScb,
Vcb,
BIT_MAP_FILE_NUMBER,
0,
$DATA,
TRUE );
NtfsOpenSystemFile( IrpContext,
&Vcb->BadClusterFileScb,
Vcb,
BAD_CLUSTER_FILE_NUMBER,
0,
$DATA,
TRUE );
NtfsOpenSystemFile( IrpContext,
&Vcb->MftBitmapScb,
Vcb,
MASTER_FILE_TABLE_NUMBER,
0,
$BITMAP,
TRUE );
//
// Initialize the bitmap support
//
NtfsInitializeClusterAllocation( IrpContext, Vcb );
NtfsSetAndGetVolumeTimes( IrpContext, Vcb, FALSE );
//
// Initialize the Mft record allocation
//
{
ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
BOOLEAN FoundAttribute;
ULONG ExtendGranularity;
//
// Lookup the bitmap allocation for the Mft file.
//
NtfsInitializeAttributeContext( &AttrContext );
//
// Use a try finally to cleanup the attribute context.
//
try {
//
// CODENOTE Is the Mft Fcb fully initialized at this point??
//
FoundAttribute = NtfsLookupAttributeByCode( IrpContext,
Vcb->MftScb->Fcb,
&Vcb->MftScb->Fcb->FileReference,
$BITMAP,
&AttrContext );
//
// Error if we don't find the bitmap
//
if (!FoundAttribute) {
DebugTrace( 0, 0, ("Couldn't find bitmap attribute for Mft\n") );
NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
}
//
// If there is no file object for the Mft Scb, we create it now.
//
if (Vcb->MftScb->FileObject == NULL) {
NtfsCreateInternalAttributeStream( IrpContext, Vcb->MftScb, TRUE );
}
//
// TEMPCODE We need a better way to determine the optimal
// truncate and extend granularity.
//
ExtendGranularity = MFT_EXTEND_GRANULARITY;
if ((ExtendGranularity * Vcb->BytesPerFileRecordSegment) < Vcb->BytesPerCluster) {
ExtendGranularity = Vcb->FileRecordsPerCluster;
}
NtfsInitializeRecordAllocation( IrpContext,
Vcb->MftScb,
&AttrContext,
Vcb->BytesPerFileRecordSegment,
ExtendGranularity,
ExtendGranularity,
&Vcb->MftBitmapAllocationContext );
} finally {
NtfsCleanupAttributeContext( &AttrContext );
}
}
//
// Get the serial number and volume label for the volume
//
NtfsGetVolumeLabel( IrpContext, Vpb, Vcb );
//
// Get the Device Name for this volume.
//
Status = ObQueryNameString( Vpb->RealDevice,
NULL,
0,
&DeviceObjectNameLength );
ASSERT( Status != STATUS_SUCCESS);
//
// Unlike the rest of the system, ObQueryNameString returns
// STATUS_INFO_LENGTH_MISMATCH instead of STATUS_BUFFER_TOO_SMALL when
// passed too small a buffer.
//
// We expect to get this error here. Anything else we can't handle.
//
if (Status == STATUS_INFO_LENGTH_MISMATCH) {
DeviceObjectName = NtfsAllocatePool( PagedPool, DeviceObjectNameLength );
Status = ObQueryNameString( Vpb->RealDevice,
DeviceObjectName,
DeviceObjectNameLength,
&DeviceObjectNameLength );
}
if (!NT_SUCCESS( Status )) {
try_return( NOTHING );
}
//
// Now that we are successfully mounting, let us see if we should
// enable balanced reads.
//
if (!FlagOn(Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED_DIRTY)) {
FsRtlBalanceReads( DeviceObjectWeTalkTo );
}
ASSERT( DeviceObjectName->Name.Length != 0 );
Vcb->DeviceName.MaximumLength =
Vcb->DeviceName.Length = DeviceObjectName->Name.Length;
Vcb->DeviceName.Buffer = NtfsAllocatePool( PagedPool, DeviceObjectName->Name.Length );
RtlCopyMemory( Vcb->DeviceName.Buffer,
DeviceObjectName->Name.Buffer,
DeviceObjectName->Name.Length );
//
// We have now mounted this volume. At this time we will update
// the version number if required and check the log file size.
//
if (UpdateVersion) {
#ifdef _CAIRO_
if ((Vpb->VolumeLabelLength == 18) &&
(RtlEqualMemory(Vpb->VolumeLabel, L"$DeadMeat", 18))) {
NtfsUpdateVersionNumber( IrpContext,
Vcb,
2,
0 );
//
// Now enable defragging.
//
if (NtfsDefragMftEnabled) {
NtfsAcquireCheckpoint( IrpContext, Vcb );
SetFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_PERMITTED );
NtfsReleaseCheckpoint( IrpContext, Vcb );
}
}
#else
NtfsUpdateVersionNumber( IrpContext,
Vcb,
1,
2 );
//
// Now enable defragging.
//
if (NtfsDefragMftEnabled) {
NtfsAcquireCheckpoint( IrpContext, Vcb );
SetFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_PERMITTED );
NtfsReleaseCheckpoint( IrpContext, Vcb );
}
#endif
}
//
// Now we want to initialize the remaining defrag status values.
//
Vcb->MftHoleGranularity = MFT_HOLE_GRANULARITY;
Vcb->MftClustersPerHole = Vcb->MftHoleGranularity << Vcb->MftToClusterShift;
if (MFT_HOLE_GRANULARITY < Vcb->FileRecordsPerCluster) {
Vcb->MftHoleGranularity = Vcb->FileRecordsPerCluster;
Vcb->MftClustersPerHole = 1;
}
Vcb->MftHoleMask = Vcb->MftHoleGranularity - 1;
Vcb->MftHoleInverseMask = ~(Vcb->MftHoleMask);
Vcb->MftHoleClusterMask = Vcb->MftClustersPerHole - 1;
Vcb->MftHoleClusterInverseMask = ~(Vcb->MftHoleClusterMask);
//
// Our maximum reserved Mft space is 0x140, we will try to
// get an extra 40 bytes if possible.
//
Vcb->MftReserved = Vcb->BytesPerFileRecordSegment / 8;
if (Vcb->MftReserved > 0x140) {
Vcb->MftReserved = 0x140;
}
Vcb->MftCushion = Vcb->MftReserved - 0x20;
NtfsScanMftBitmap( IrpContext, Vcb );
#ifdef NTFS_CHECK_BITMAP
{
ULONG BitmapSize;
ULONG Count;
BitmapSize = Vcb->BitmapScb->Header.FileSize.LowPart;
//
// Allocate a buffer for the bitmap copy and each individual bitmap.
//
Vcb->BitmapPages = (BitmapSize + PAGE_SIZE - 1) / PAGE_SIZE;
Vcb->BitmapCopy = NtfsAllocatePool(PagedPool, Vcb->BitmapPages * sizeof( RTL_BITMAP ));
RtlZeroMemory( Vcb->BitmapCopy, Vcb->BitmapPages * sizeof( RTL_BITMAP ));
//
// Now get a buffer for each page.
//
for (Count = 0; Count < Vcb->BitmapPages; Count += 1) {
(Vcb->BitmapCopy + Count)->Buffer = NtfsAllocatePool(PagedPool, PAGE_SIZE );
RtlInitializeBitMap( Vcb->BitmapCopy + Count, (Vcb->BitmapCopy + Count)->Buffer, PAGE_SIZE * 8 );
}
if (NtfsCopyBitmap) {
PUCHAR NextPage;
PBCB BitmapBcb = NULL;
ULONG BytesToCopy;
LONGLONG FileOffset = 0;
Count = 0;
while (BitmapSize) {
BytesToCopy = PAGE_SIZE;
if (BytesToCopy > BitmapSize) {
BytesToCopy = BitmapSize;
}
NtfsUnpinBcb( &BitmapBcb );
NtfsMapStream( IrpContext, Vcb->BitmapScb, FileOffset, BytesToCopy, &BitmapBcb, &NextPage );
RtlCopyMemory( (Vcb->BitmapCopy + Count)->Buffer,
NextPage,
BytesToCopy );
BitmapSize -= BytesToCopy;
FileOffset += BytesToCopy;
Count += 1;
}
NtfsUnpinBcb( &BitmapBcb );
//
// Otherwise we will want to scan the entire Mft and compare the mapping pairs
// with the current volume bitmap.
//
}
}
#endif
#ifdef _CAIRO_
if ((Vpb->VolumeLabelLength == 18) &&
(RtlCompareMemory(Vpb->VolumeLabel, L"$DeadMeat", 18) == 18)) {
//
// Open the Quota object. At present, the quota object contains:
//
// 1. Security info
// 2. Quota info
//
// BUGBUG: This is the default data stream on the quota table. This
// should be changed when we get NtOfsCreateAttribute becomes
// available.
//
NtfsOpenSystemFile( IrpContext,
&QuotaDataScb,
Vcb,
QUOTA_TABLE_NUMBER,
0,
$DATA,
TRUE );
//
// Enable quota tracking.
//
NtfsInitializeQuotaIndex( IrpContext,
QuotaDataScb->Fcb,
Vcb );
//
// Enable security index
//
NtfsInitializeSecurity( IrpContext, Vcb, QuotaDataScb->Fcb );
#ifdef TOMM
// NtOfsIndexTest( IrpContext, Vcb->SecurityDescriptorStream->Fcb );
#endif TOMM
}
#endif _CAIRO_
NtfsCleanupTransaction( IrpContext, STATUS_SUCCESS, FALSE );
//
//
// Set our return status and say that the mount succeeded
//
Status = STATUS_SUCCESS;
MountFailed = FALSE;
try_exit: NOTHING;
} finally {
DebugUnwind( NtfsMountVolume );
NtfsUnpinBcb( &BootBcb );
if (DeviceObjectName != NULL) {
NtfsFreePool( DeviceObjectName );
}
if (CloseAttributes) { NtfsCloseAttributesFromRestart( IrpContext, Vcb ); }
for (i = 0; i < 8; i++) { NtfsUnpinBcb( &Bcbs[i] ); }
if (BootScb != NULL) { NtfsDeleteInternalAttributeStream( BootScb, TRUE ); }
if (Vcb != NULL) {
if (Vcb->MftScb != NULL) { NtfsReleaseScb( IrpContext, Vcb->MftScb ); }
if (Vcb->Mft2Scb != NULL) { NtfsReleaseScb( IrpContext, Vcb->Mft2Scb ); }
if (Vcb->LogFileScb != NULL) { NtfsReleaseScb( IrpContext, Vcb->LogFileScb ); }
if (Vcb->VolumeDasdScb != NULL) { NtfsReleaseScb( IrpContext, Vcb->VolumeDasdScb ); }
if (Vcb->AttributeDefTableScb != NULL) { NtfsReleaseScb( IrpContext, Vcb->AttributeDefTableScb );
NtfsDeleteInternalAttributeStream( Vcb->AttributeDefTableScb, TRUE );
Vcb->AttributeDefTableScb = NULL;}
if (Vcb->UpcaseTableScb != NULL) { NtfsReleaseScb( IrpContext, Vcb->UpcaseTableScb );
NtfsDeleteInternalAttributeStream( Vcb->UpcaseTableScb, TRUE );
Vcb->UpcaseTableScb = NULL;}
if (Vcb->RootIndexScb != NULL) { NtfsReleaseScb( IrpContext, Vcb->RootIndexScb ); }
if (Vcb->BitmapScb != NULL) { NtfsReleaseScb( IrpContext, Vcb->BitmapScb ); }
if (Vcb->BadClusterFileScb != NULL) { NtfsReleaseScb( IrpContext, Vcb->BadClusterFileScb ); }
if (Vcb->MftBitmapScb != NULL) { NtfsReleaseScb( IrpContext, Vcb->MftBitmapScb ); }
#ifdef _CAIRO_
//
// Drop the security data
//
if (Vcb->SecurityDescriptorStream != NULL) { NtfsReleaseScb( IrpContext, Vcb->SecurityDescriptorStream ); }
if (QuotaDataScb != NULL) {
NtfsReleaseScb( IrpContext, QuotaDataScb );
NtfsDeleteInternalAttributeStream( QuotaDataScb, TRUE );
}
#endif _CAIRO_
if (MountFailed) {
NtfsPerformDismountOnVcb( IrpContext, Vcb, TRUE );
//
// On abnormal termination, someone will try to abort a transaction on
// this Vcb if we do not clear these fields.
//
IrpContext->TransactionId = 0;
IrpContext->Vcb = NULL;
}
}
if (VcbAcquired) {
NtfsReleaseVcbCheckDelete( IrpContext, Vcb, IRP_MJ_FILE_SYSTEM_CONTROL, NULL );
}
NtfsReleaseGlobal( IrpContext );
if (!AbnormalTermination()) {
NtfsCompleteRequest( &IrpContext, &Irp, Status );
}
}
DebugTrace( -1, Dbg, ("NtfsMountVolume -> %08lx\n", Status) );
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NtfsVerifyVolume (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
This routine performs the verify 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;
PIO_STACK_LOCATION IrpSp;
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_IRP( Irp );
PAGED_CODE();
//
// Get the current Irp stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace( +1, Dbg, ("NtfsVerifyVolume\n") );
//
// Do nothing for now
//
KdPrint(("NtfsVerifyVolume is not yet implemented\n")); //**** DbgBreakPoint();
NtfsCompleteRequest( &IrpContext, &Irp, Status = STATUS_NOT_IMPLEMENTED );
//
// And return to our caller
//
DebugTrace( -1, Dbg, ("NtfsVerifyVolume -> %08lx\n", Status) );
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NtfsUserFsRequest (
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
Wait - Indicates if the thread can block for a resource or I/O
Return Value:
NTSTATUS - The return status for the operation
--*/
{
NTSTATUS Status;
ULONG FsControlCode;
PIO_STACK_LOCATION IrpSp;
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_IRP( Irp );
PAGED_CODE();
//
// Get the current Irp stack location, and save some references
// to make our life a little easier.
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
DebugTrace( +1, Dbg, ("NtfsUserFsCtrl, 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_REQUEST_FILTER_OPLOCK:
case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
case FSCTL_OPLOCK_BREAK_NOTIFY:
case FSCTL_OPBATCH_ACK_CLOSE_PENDING :
case FSCTL_OPLOCK_BREAK_ACK_NO_2:
Status = NtfsOplockRequest( IrpContext, Irp );
break;
case FSCTL_LOCK_VOLUME:
Status = NtfsLockVolume( IrpContext, Irp );
break;
case FSCTL_UNLOCK_VOLUME:
Status = NtfsUnlockVolume( IrpContext, Irp );
break;
case FSCTL_DISMOUNT_VOLUME:
Status = NtfsDismountVolume( IrpContext, Irp );
break;
case FSCTL_MARK_VOLUME_DIRTY:
Status = NtfsDirtyVolume( IrpContext, Irp );
break;
case FSCTL_IS_PATHNAME_VALID:
//
// All names are potentially valid NTFS names
//
NtfsCompleteRequest( &IrpContext, &Irp, Status = STATUS_SUCCESS );
break;
case FSCTL_QUERY_RETRIEVAL_POINTERS:
Status = NtfsQueryRetrievalPointers( IrpContext, Irp );
break;
case FSCTL_GET_COMPRESSION:
Status = NtfsGetCompression( IrpContext, Irp );
break;
case FSCTL_SET_COMPRESSION:
//
// Post this request if we can't wait.
//
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
Status = NtfsPostRequest( IrpContext, Irp );
} else {
Status = NtfsSetCompression( IrpContext, Irp );
}
break;
case FSCTL_READ_COMPRESSION:
Status = NtfsReadCompression( IrpContext, Irp );
break;
case FSCTL_WRITE_COMPRESSION:
Status = NtfsWriteCompression( IrpContext, Irp );
break;
case FSCTL_MARK_AS_SYSTEM_HIVE:
Status = NtfsMarkAsSystemHive( IrpContext, Irp );
break;
case FSCTL_FILESYSTEM_GET_STATISTICS:
Status = NtfsGetStatistics( IrpContext, Irp );
break;
case FSCTL_GET_NTFS_VOLUME_DATA:
Status = NtfsGetVolumeData( IrpContext, Irp );
break;
case FSCTL_GET_VOLUME_BITMAP:
Status = NtfsGetVolumeBitmap( IrpContext, Irp );
break;
case FSCTL_GET_RETRIEVAL_POINTERS:
Status = NtfsGetRetrievalPointers( IrpContext, Irp );
break;
case FSCTL_GET_NTFS_FILE_RECORD:
Status = NtfsGetMftRecord( IrpContext, Irp );
break;
case FSCTL_MOVE_FILE:
//
// Always make this synchronous for MoveFile
//
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
Status = NtfsMoveFile( IrpContext, Irp );
break;
case FSCTL_IS_VOLUME_DIRTY:
Status = NtfsIsVolumeDirty( IrpContext, Irp );
break;
case FSCTL_ALLOW_EXTENDED_DASD_IO :
Status = NtfsSetExtendedDasdIo( IrpContext, Irp );
break;
default :
//
// Core Ntfs does not understand this FsCtl. We poll the loadable
// portions to see if they can process it.
//
#ifdef _CAIRO_
if (NtfsData.ViewCallBackTable != NULL) {
PVCB Vcb;
PFCB Fcb;
PSCB Scb;
PCCB Ccb;
ULONG OutputBufferLength;
NtfsDecodeFileObject( IrpContext,
IrpSp->FileObject,
&Vcb,
&Fcb,
&Scb,
&Ccb,
TRUE );
OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
Status = NtfsData.ViewCallBackTable->ViewFileSystemControl(
IrpContext,
Fcb,
Scb,
FsControlCode,
IrpSp->Parameters.FileSystemControl.InputBufferLength,
IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
&OutputBufferLength,
Irp->UserBuffer );
//
// If the request succeeded or was recognized in some way by Views
// then complete the request and return the buffer length
//
if (NT_SUCCESS( Status )) {
Irp->IoStatus.Information = OutputBufferLength;
NtfsCompleteRequest( &IrpContext, &Irp, Status);
break;
} else if (Status != STATUS_INVALID_DEVICE_REQUEST) {
NtfsCompleteRequest( &IrpContext, &Irp, Status);
break;
}
}
#endif // _CAIRO_
DebugTrace( 0, Dbg, ("Invalid control code -> %08lx\n", FsControlCode) );
NtfsCompleteRequest( &IrpContext, &Irp, Status = STATUS_INVALID_PARAMETER );
break;
}
//
// And return to our caller
//
DebugTrace( -1, Dbg, ("NtfsUserFsRequest -> %08lx\n", Status) );
return Status;
}
//
// Local support routine
//
NTSTATUS
NtfsOplockRequest (
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;
PIO_STACK_LOCATION IrpSp;
ULONG FsControlCode;
ULONG OplockCount = 0;
PFILE_OBJECT FileObject;
TYPE_OF_OPEN TypeOfOpen;
PVCB Vcb;
PFCB Fcb;
PSCB Scb;
PCCB Ccb;
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_IRP( Irp );
PAGED_CODE();
//
// Get the current Irp stack location, and save some reference to
// make life easier
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
DebugTrace( +1, Dbg, ("NtfsOplockRequest, FsControlCode = %08lx\n", FsControlCode) );
//
// Extract and decode the file object
//
FileObject = IrpSp->FileObject;
TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
//
// We only permit oplock requests on files.
//
if (TypeOfOpen != UserFileOpen) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
DebugTrace( -1, Dbg, ("NtfsOplockRequest -> STATUS_INVALID_PARAMETER\n") );
return STATUS_INVALID_PARAMETER;
}
//
// We jam Wait to TRUE in the IrpContext. This prevents us from returning
// STATUS_PENDING if we can't acquire the file. The caller would
// interpret that as having acquired an oplock.
//
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
//
// 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_BATCH_OPLOCK:
case FSCTL_REQUEST_FILTER_OPLOCK:
case FSCTL_REQUEST_OPLOCK_LEVEL_2:
NtfsAcquireExclusiveFcb( IrpContext, Fcb, Scb, FALSE, FALSE );
if (FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2) {
if (Scb->ScbType.Data.FileLock != NULL) {
OplockCount = (ULONG) FsRtlAreThereCurrentFileLocks( Scb->ScbType.Data.FileLock );
}
} else {
OplockCount = Scb->CleanupCount;
}
break;
case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
case FSCTL_OPBATCH_ACK_CLOSE_PENDING :
case FSCTL_OPLOCK_BREAK_NOTIFY:
case FSCTL_OPLOCK_BREAK_ACK_NO_2:
NtfsAcquireSharedFcb( IrpContext, Fcb, Scb, FALSE );
break;
default:
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
DebugTrace( -1, Dbg, ("NtfsOplockRequest -> STATUS_INVALID_PARAMETER\n") );
return STATUS_INVALID_PARAMETER;
}
//
// Use a try finally to free the Fcb.
//
try {
//
// Call the FsRtl routine to grant/acknowledge oplock.
//
Status = FsRtlOplockFsctrl( &Scb->ScbType.Data.Oplock,
Irp,
OplockCount );
//
// Set the flag indicating if Fast I/O is possible
//
NtfsAcquireFsrtlHeader( Scb );
Scb->Header.IsFastIoPossible = NtfsIsFastIoPossible( Scb );
NtfsReleaseFsrtlHeader( Scb );
} finally {
DebugUnwind( NtfsOplockRequest );
//
// Release all of our resources
//
NtfsReleaseFcb( IrpContext, Fcb );
//
// If this is not an abnormal termination then complete the irp
//
if (!AbnormalTermination()) {
NtfsCompleteRequest( &IrpContext, NULL, 0 );
}
DebugTrace( -1, Dbg, ("NtfsOplockRequest -> %08lx\n", Status) );
}
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NtfsLockVolume (
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;
PIO_STACK_LOCATION IrpSp;
PFILE_OBJECT FileObject;
TYPE_OF_OPEN TypeOfOpen;
BOOLEAN VcbAcquired = FALSE;
PVCB Vcb;
PFCB Fcb;
PSCB Scb;
PCCB Ccb;
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_IRP( Irp );
PAGED_CODE();
//
// Get the current Irp stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace( +1, Dbg, ("NtfsLockVolume...\n") );
//
// Extract and decode the file object, and only permit user volume opens
//
FileObject = IrpSp->FileObject;
TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
if (TypeOfOpen != UserVolumeOpen) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
DebugTrace( -1, Dbg, ("NtfsLockVolume -> %08lx\n", STATUS_INVALID_PARAMETER) );
return STATUS_INVALID_PARAMETER;
}
//
// Acquire exclusive access to the Ntfs global.
//
NtfsAcquireExclusiveGlobal( IrpContext );
try {
NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
VcbAcquired = TRUE;
//
// 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). We also fail
// this request if the volume has already gone through the dismount
// vcb process.
//
if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED ) ||
(Vcb->CleanupCount > 1)) {
DebugTrace( 0, Dbg, ("Volume is currently in use\n") );
Status = STATUS_ACCESS_DENIED;
//
// If the volume is already locked then it might have been the result of an
// exclusive DASD open. Allow that user to explictly lock the volume.
//
} else if (FlagOn( Vcb->VcbState, VCB_STATE_LOCKED )) {
if (FlagOn( Vcb->VcbState, VCB_STATE_EXPLICIT_LOCK )) {
DebugTrace( 0, Dbg, ("User has already locked volume\n") );
Status = STATUS_ACCESS_DENIED;
} else {
SetFlag( Vcb->VcbState, VCB_STATE_EXPLICIT_LOCK );
Status = STATUS_SUCCESS;
}
//
// We can take this path if the volume has already been locked via
// create but has not taken the PerformDismountOnVcb path. We checked
// for this above by looking at the VOLUME_MOUNTED flag in the Vcb.
//
} else {
//
// There better be system files objects only at this point.
//
if (!NT_SUCCESS( NtfsFlushVolume( IrpContext, Vcb, TRUE, TRUE, TRUE, FALSE ))
|| Vcb->CloseCount - Vcb->SystemFileCloseCount > 1) {
DebugTrace( 0, Dbg, ("Volume has user file objects\n") );
Status = STATUS_ACCESS_DENIED;
} else {
//
// We don't really want to do all of the perform dismount here because
// that will cause us to remount a new volume before we're ready.
// At this time we only want to stop the log file and close up our
// internal attribute streams. When the user (i.e., chkdsk) does an
// unlock then we'll finish up with the dismount call
//
NtfsPerformDismountOnVcb( IrpContext, Vcb, FALSE );
SetFlag( Vcb->VcbState, VCB_STATE_LOCKED | VCB_STATE_EXPLICIT_LOCK );
Vcb->FileObjectWithVcbLocked = (PFILE_OBJECT)(((ULONG)IrpSp->FileObject) + 1);
Status = STATUS_SUCCESS;
}
}
} finally {
DebugUnwind( NtfsLockVolume );
if (VcbAcquired) {
NtfsReleaseVcb( IrpContext, Vcb );
}
//
// Release all of our resources
//
NtfsReleaseGlobal( IrpContext );
//
// If this is an abnormal termination then undo our work, otherwise
// complete the irp
//
if (!AbnormalTermination()) {
NtfsCompleteRequest( &IrpContext, &Irp, Status );
}
DebugTrace( -1, Dbg, ("NtfsLockVolume -> %08lx\n", Status) );
}
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NtfsUnlockVolume (
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;
PIO_STACK_LOCATION IrpSp;
PFILE_OBJECT FileObject;
TYPE_OF_OPEN TypeOfOpen;
PVCB Vcb;
PFCB Fcb;
PSCB Scb;
PCCB Ccb;
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_IRP( Irp );
PAGED_CODE();
//
// Get the current Irp stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace( +1, Dbg, ("NtfsUnlockVolume...\n") );
//
// Extract and decode the file object, and only permit user volume opens
//
FileObject = IrpSp->FileObject;
TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
if (TypeOfOpen != UserVolumeOpen) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
DebugTrace( -1, Dbg, ("NtfsLockVolume -> %08lx\n", STATUS_INVALID_PARAMETER) );
return STATUS_INVALID_PARAMETER;
}
//
// Acquire exclusive access to the Vcb
//
NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
try {
if (FlagOn( Vcb->VcbState, VCB_STATE_EXPLICIT_LOCK )) {
NtfsPerformDismountOnVcb( IrpContext, Vcb, TRUE );
//
// Unlock the volume and complete the Irp
//
ClearFlag( Vcb->VcbState, VCB_STATE_LOCKED | VCB_STATE_EXPLICIT_LOCK );
Vcb->FileObjectWithVcbLocked = NULL;
Status = STATUS_SUCCESS;
#ifdef _CAIRO_
//
// If the quota tracking has been requested and the quotas need to be
// repaired then try to repair them now.
//
if (FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_REQUESTED ) &&
FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_OUT_OF_DATE |
QUOTA_FLAG_CORRUPT |
QUOTA_FLAG_PENDING_DELETES )) {
NtfsPostRepairQuotaIndex( IrpContext, Vcb );
}
#endif // _CAIRO_
} else {
Status = STATUS_NOT_LOCKED;
}
} finally {
DebugUnwind( NtfsUnlockVolume );
//
// Release all of our resources
//
NtfsReleaseVcb( IrpContext, Vcb );
//
// If this is an abnormal termination then undo our work, otherwise
// complete the irp
//
if (!AbnormalTermination()) {
NtfsCompleteRequest( &IrpContext, &Irp, Status );
}
DebugTrace( -1, Dbg, ("NtfsUnlockVolume -> %08lx\n", Status) );
}
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NtfsDismountVolume (
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
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
BOOLEAN VcbAcquired = FALSE;
PFILE_OBJECT FileObject;
TYPE_OF_OPEN TypeOfOpen;
PVCB Vcb;
PFCB Fcb;
PSCB Scb;
PCCB Ccb;
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_IRP( Irp );
PAGED_CODE();
//
// Get the current Irp stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace( +1, Dbg, ("NtfsDismountVolume...\n") );
//
// Extract and decode the file object, and only permit user volume opens
//
FileObject = IrpSp->FileObject;
TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
if (TypeOfOpen != UserVolumeOpen) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
DebugTrace( -1, Dbg, ("NtfsLockVolume -> %08lx\n", STATUS_INVALID_PARAMETER) );
return STATUS_INVALID_PARAMETER;
}
//
// Acquire exclusive access to the global resource. We want to
// prevent checkpointing from running on this volume.
//
NtfsAcquireExclusiveGlobal( IrpContext );
try {
//
// Acquire the Vcb exclusively.
//
NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
VcbAcquired = TRUE;
//
// If there's a pagefile on this volume, or if this is the
// system volume, don't do the dismount.
//
if (FlagOn(Vcb->VcbState, VCB_STATE_DISALLOW_DISMOUNT) &&
!FlagOn(Vcb->VcbState, VCB_STATE_LOCKED)) {
try_return( Status = STATUS_ACCESS_DENIED );
}
//
// We will ignore this request if this is a dismount with only readonly files
// opened. To decide if there are only readonly user files opened we will
// check if the readonly count equals the close count for user files minus the one
// for the handle with the volume locked
//
if (FlagOn(Vcb->VcbState, VCB_STATE_LOCKED) &&
(Vcb->ReadOnlyCloseCount == ((Vcb->CloseCount - Vcb->SystemFileCloseCount) - 1))) {
DebugTrace( 0, Dbg, ("Volume has readonly files opened\n") );
Status = STATUS_SUCCESS;
} else {
//
// Get as many cached writes out to disk as we can and mark
// all the streams for dismount.
//
NtfsFlushVolume( IrpContext, Vcb, TRUE, TRUE, TRUE, TRUE );
//
// Actually do the dismount.
//
NtfsPerformDismountOnVcb( IrpContext, Vcb, TRUE );
//
// Set this flag to prevent the volume from being accessed
// via checkpointing.
//
SetFlag( Vcb->CheckpointFlags, VCB_CHECKPOINT_IN_PROGRESS );
//
// Abort transaction on error by raising.
//
Status = STATUS_SUCCESS;
NtfsCleanupTransaction( IrpContext, Status, FALSE );
SetFlag( Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME );
}
try_exit: NOTHING;
} finally {
DebugUnwind( NtfsDismountVolume );
//
// Release all of our resources
//
if (VcbAcquired) {
NtfsReleaseVcb( IrpContext, Vcb );
}
NtfsReleaseGlobal( IrpContext );
//
// If this is an abnormal termination then undo our work, otherwise
// complete the irp
//
if (!AbnormalTermination()) {
NtfsCompleteRequest( &IrpContext, &Irp, Status );
}
DebugTrace( -1, Dbg, ("NtfsDismountVolume -> %08lx\n", Status) );
}
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NtfsDirtyVolume (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
This routine marks the specified volume dirty.
Arguments:
Irp - Supplies the Irp to process
Return Value:
NTSTATUS - The return status for the operation
--*/
{
PIO_STACK_LOCATION IrpSp;
NTSTATUS Status = STATUS_SUCCESS;
PFILE_OBJECT FileObject;
TYPE_OF_OPEN TypeOfOpen;
PVCB Vcb;
PFCB Fcb;
PSCB Scb;
PCCB Ccb;
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_IRP( Irp );
PAGED_CODE();
//
// Get the current Irp stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace( +1, Dbg, ("NtfsDirtyVolume...\n") );
//
// Extract and decode the file object, and only permit user volume opens
//
FileObject = IrpSp->FileObject;
TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
if (TypeOfOpen != UserVolumeOpen) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
DebugTrace( -1, Dbg, ("NtfsDirtyVolume -> %08lx\n", STATUS_INVALID_PARAMETER) );
return STATUS_INVALID_PARAMETER;
}
//
// Fail this request if the volume is not mounted.
//
if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
Status = STATUS_VOLUME_DISMOUNTED;
} else {
NtfsPostVcbIsCorrupt( IrpContext, 0, NULL, NULL );
}
NtfsCompleteRequest( &IrpContext, &Irp, Status );
DebugTrace( -1, Dbg, ("NtfsDirtyVolume -> STATUS_SUCCESS\n") );
return Status;
}
//
// Local support routine
//
BOOLEAN
NtfsGetDiskGeometry (
IN PIRP_CONTEXT IrpContext,
IN PDEVICE_OBJECT RealDevice,
IN PDISK_GEOMETRY DiskGeometry,
IN PPARTITION_INFORMATION PartitionInfo
)
/*++
Routine Description:
This procedure gets the disk geometry of the specified device
Arguments:
RealDevice - Supplies the real device that is being queried
DiskGeometry - Receives the disk geometry
PartitionInfo - Receives the partition information
Return Value:
BOOLEAN - TRUE if the media is write protected, FALSE otherwise
--*/
{
NTSTATUS Status;
ULONG i;
PREVENT_MEDIA_REMOVAL Prevent;
BOOLEAN WriteProtected = FALSE;
PAGED_CODE();
DebugTrace( +1, Dbg, ("NtfsGetDiskGeometry:\n") );
DebugTrace( 0, Dbg, ("RealDevice = %08lx\n", RealDevice) );
DebugTrace( 0, Dbg, ("DiskGeometry = %08lx\n", DiskGeometry) );
//
// Attempt to lock any removable media, ignoring status.
//
Prevent.PreventMediaRemoval = TRUE;
(PVOID)NtfsDeviceIoControl( IrpContext,
RealDevice,
IOCTL_DISK_MEDIA_REMOVAL,
&Prevent,
sizeof(PREVENT_MEDIA_REMOVAL),
NULL,
0 );
//
// 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.
//
Status = NtfsDeviceIoControl( IrpContext,
RealDevice,
IOCTL_DISK_IS_WRITABLE,
NULL,
0,
NULL,
0 );
//
// Remember if the media is write protected but don't raise the error now.
// If the volume is not Ntfs then let another filesystem try.
//
if (Status == STATUS_MEDIA_WRITE_PROTECTED) {
WriteProtected = TRUE;
Status = STATUS_SUCCESS;
}
for (i = 0; i < 2; i++) {
if (i == 0) {
Status = NtfsDeviceIoControl( IrpContext,
RealDevice,
IOCTL_DISK_GET_DRIVE_GEOMETRY,
NULL,
0,
DiskGeometry,
sizeof(DISK_GEOMETRY) );
} else {
Status = NtfsDeviceIoControl( IrpContext,
RealDevice,
IOCTL_DISK_GET_PARTITION_INFO,
NULL,
0,
PartitionInfo,
sizeof(PARTITION_INFORMATION) );
}
if (!NT_SUCCESS(Status)) {
NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
}
}
DebugTrace( -1, Dbg, ("NtfsGetDiskGeometry->VOID\n") );
return WriteProtected;
}
NTSTATUS
NtfsDeviceIoControl (
IN PIRP_CONTEXT IrpContext,
IN PDEVICE_OBJECT DeviceObject,
IN ULONG IoCtl,
IN PVOID InputBuffer OPTIONAL,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer OPTIONAL,
IN ULONG OutputBufferLength
)
/*++
Routine Description:
This procedure issues an Ioctl to the lower device, and waits
for the answer.
Arguments:
DeviceObject - Supplies the device to issue the request to
IoCtl - Gives the IoCtl to be used
XxBuffer - Gives the buffer pointer for the ioctl, if any
XxBufferLength - Gives the length of the buffer, if any
Return Value:
None.
--*/
{
PIRP Irp;
KEVENT Event;
IO_STATUS_BLOCK Iosb;
NTSTATUS Status;
KeInitializeEvent( &Event, NotificationEvent, FALSE );
Irp = IoBuildDeviceIoControlRequest( IoCtl,
DeviceObject,
InputBuffer,
InputBufferLength,
OutputBuffer,
OutputBufferLength,
FALSE,
&Event,
&Iosb );
if (Irp == NULL) {
NtfsRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES, NULL, NULL );
}
Status = IoCallDriver( DeviceObject, Irp );
if (Status == STATUS_PENDING) {
(VOID)KeWaitForSingleObject( &Event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER)NULL );
Status = Iosb.Status;
}
return Status;
}
//
// Local support routine
//
VOID
NtfsReadBootSector (
IN PIRP_CONTEXT IrpContext,
IN PVCB Vcb,
OUT PSCB *BootScb,
OUT PBCB *BootBcb,
OUT PVOID *BootSector
)
/*++
Routine Description:
This routine reads and returns a pointer to the boot sector for the volume.
Volumes formatted under 3.51 and earlier will have a boot sector at sector
0 and another halfway through the disk. Volumes formatted with NT 4.0
will have a boot sector at the end of the disk, in the sector beyond the
stated size of the volume in the boot sector. When this call is made the
Vcb has the sector count from the device driver so we subtract one to find
the last sector.
Arguments:
Vcb - Supplies the Vcb for the operation
BootScb - Receives the Scb for the boot file
BootBcb - Receives the bcb for the boot sector
BootSector - Receives a pointer to the boot sector
Return Value:
None.
--*/
{
PSCB Scb = NULL;
BOOLEAN Error = FALSE;
FILE_REFERENCE FileReference = { BOOT_FILE_NUMBER, 0, BOOT_FILE_NUMBER };
PAGED_CODE();
DebugTrace( +1, Dbg, ("NtfsReadBootSector:\n") );
DebugTrace( 0, Dbg, ("Vcb = %08lx\n", Vcb) );
//
// Create a temporary scb for reading in the boot sector and initialize the
// mcb for it.
//
Scb = NtfsCreatePrerestartScb( IrpContext,
Vcb,
&FileReference,
$DATA,
NULL,
0 );
*BootScb = Scb;
Scb->Header.AllocationSize.QuadPart =
Scb->Header.FileSize.QuadPart =
Scb->Header.ValidDataLength.QuadPart = (PAGE_SIZE * 2) + Vcb->BytesPerSector;
//
// We don't want to look up the size for this Scb.
//
NtfsCreateInternalAttributeStream( IrpContext, Scb, FALSE );
SetFlag( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED );
(VOID)NtfsAddNtfsMcbEntry( &Scb->Mcb,
(LONGLONG)0,
(LONGLONG)0,
(LONGLONG)Vcb->ClustersPerPage,
FALSE );
(VOID)NtfsAddNtfsMcbEntry( &Scb->Mcb,
(LONGLONG)Vcb->ClustersPerPage,
Vcb->NumberSectors >> 1,
(LONGLONG)Vcb->ClustersPerPage,
FALSE );
(VOID)NtfsAddNtfsMcbEntry( &Scb->Mcb,
Int64ShllMod32( (LONGLONG) Vcb->ClustersPerPage, 1 ),
Vcb->NumberSectors - 1,
1,
FALSE );
//
// Try reading in the first boot sector
//
try {
NtfsMapStream( IrpContext,
Scb,
(LONGLONG)0,
Vcb->BytesPerSector,
BootBcb,
BootSector );
//
// If we got an exception trying to read the first boot sector,
// then handle the exception by trying to read the second boot
// sector. If that faults too, then we just allow ourselves to
// unwind and return the error.
//
} except (FsRtlIsNtstatusExpected(GetExceptionCode()) ?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH) {
Error = TRUE;
}
//
// Get out if we didn't get an error. Otherwise try the middle sector.
// We want to read this next because we know that 4.0 format will clear
// this before writing the last sector. Otherwise we could see a
// stale boot sector in the last sector even though a 3.51 format was
// the last to run.
//
if (!Error) { return; }
Error = FALSE;
try {
NtfsMapStream( IrpContext,
Scb,
(LONGLONG)PAGE_SIZE,
Vcb->BytesPerSector,
BootBcb,
BootSector );
//
// Ignore this sector if not Ntfs. This could be the case for
// a bad sector 0 on a FAT volume.
//
if (!NtfsIsBootSectorNtfs( *BootSector, Vcb )) {
NtfsUnpinBcb( BootBcb );
Error = TRUE;
}
//
// If we got an exception trying to read the first boot sector,
// then handle the exception by trying to read the second boot
// sector. If that faults too, then we just allow ourselves to
// unwind and return the error.
//
} except (FsRtlIsNtstatusExpected(GetExceptionCode()) ?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH) {
Error = TRUE;
}
//
// Get out if we didn't get an error. Otherwise try the middle sector.
//
if (!Error) { return; }
NtfsMapStream( IrpContext,
Scb,
(LONGLONG) (PAGE_SIZE * 2),
Vcb->BytesPerSector,
BootBcb,
BootSector );
//
// Clear the header flag in the Scb.
//
ClearFlag( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED );
//
// And return to our caller
//
DebugTrace( 0, Dbg, ("BootScb > %08lx\n", *BootScb) );
DebugTrace( 0, Dbg, ("BootBcb > %08lx\n", *BootBcb) );
DebugTrace( 0, Dbg, ("BootSector > %08lx\n", *BootSector) );
DebugTrace( -1, Dbg, ("NtfsReadBootSector->VOID\n") );
return;
}
//
// Local support routine
//
//
// First define a local macro to number the tests for the debug case.
//
#ifdef NTFSDBG
#define NextTest ++CheckNumber &&
#else
#define NextTest TRUE &&
#endif
BOOLEAN
NtfsIsBootSectorNtfs (
IN PPACKED_BOOT_SECTOR BootSector,
IN PVCB Vcb
)
/*++
Routine Description:
This routine checks the boot sector to determine if it is an NTFS partition.
The Vcb must alread be initialized from the device object to contain the
parts of the device geometry we care about here: bytes per sector and
total number of sectors in the partition.
Arguments:
BootSector - Pointer to the boot sector which has been read in.
Vcb - Pointer to a Vcb which has been initialized with sector size and
number of sectors on the partition.
Return Value:
FALSE - If the boot sector is not for Ntfs.
TRUE - If the boot sector is for Ntfs.
--*/
{
#ifdef NTFSDBG
ULONG CheckNumber = 0;
#endif
PULONG l;
ULONG Checksum = 0;
PAGED_CODE();
DebugTrace( +1, Dbg, ("NtfsIsBootSectorNtfs\n") );
DebugTrace( 0, Dbg, ("BootSector = %08lx\n", BootSector) );
//
// First calculate the boot sector checksum
//
for (l = (PULONG)BootSector; l < (PULONG)&BootSector->Checksum; l++) {
Checksum += *l;
}
//
// Now perform all the checks, starting with the Name and Checksum.
// The remaining checks should be obvious, including some fields which
// must be 0 and other fields which must be a small power of 2.
//
if (NextTest
(BootSector->Oem[0] == 'N') &&
(BootSector->Oem[1] == 'T') &&
(BootSector->Oem[2] == 'F') &&
(BootSector->Oem[3] == 'S') &&
(BootSector->Oem[4] == ' ') &&
(BootSector->Oem[5] == ' ') &&
(BootSector->Oem[6] == ' ') &&
(BootSector->Oem[7] == ' ')
&&
// NextTest
// (BootSector->Checksum == Checksum)
//
// &&
//
// Check number of bytes per sector. The low order byte of this
// number must be zero (smallest sector size = 0x100) and the
// high order byte shifted must equal the bytes per sector gotten
// from the device and stored in the Vcb. And just to be sure,
// sector size must be less than page size.
//
NextTest
(BootSector->PackedBpb.BytesPerSector[0] == 0)
&&
NextTest
((ULONG)(BootSector->PackedBpb.BytesPerSector[1] << 8) == Vcb->BytesPerSector)
&&
NextTest
(BootSector->PackedBpb.BytesPerSector[1] << 8 <= PAGE_SIZE)
&&
//
// Sectors per cluster must be a power of 2.
//
NextTest
((BootSector->PackedBpb.SectorsPerCluster[0] == 0x1) ||
(BootSector->PackedBpb.SectorsPerCluster[0] == 0x2) ||
(BootSector->PackedBpb.SectorsPerCluster[0] == 0x4) ||
(BootSector->PackedBpb.SectorsPerCluster[0] == 0x8) ||
(BootSector->PackedBpb.SectorsPerCluster[0] == 0x10) ||
(BootSector->PackedBpb.SectorsPerCluster[0] == 0x20) ||
(BootSector->PackedBpb.SectorsPerCluster[0] == 0x40) ||
(BootSector->PackedBpb.SectorsPerCluster[0] == 0x80))
&&
//
// These fields must all be zero. For both Fat and HPFS, some of
// these fields must be nonzero.
//
NextTest
(BootSector->PackedBpb.ReservedSectors[0] == 0) &&
(BootSector->PackedBpb.ReservedSectors[1] == 0) &&
(BootSector->PackedBpb.Fats[0] == 0) &&
(BootSector->PackedBpb.RootEntries[0] == 0) &&
(BootSector->PackedBpb.RootEntries[1] == 0) &&
(BootSector->PackedBpb.Sectors[0] == 0) &&
(BootSector->PackedBpb.Sectors[1] == 0) &&
(BootSector->PackedBpb.SectorsPerFat[0] == 0) &&
(BootSector->PackedBpb.SectorsPerFat[1] == 0) &&
// (BootSector->PackedBpb.HiddenSectors[0] == 0) &&
// (BootSector->PackedBpb.HiddenSectors[1] == 0) &&
// (BootSector->PackedBpb.HiddenSectors[2] == 0) &&
// (BootSector->PackedBpb.HiddenSectors[3] == 0) &&
(BootSector->PackedBpb.LargeSectors[0] == 0) &&
(BootSector->PackedBpb.LargeSectors[1] == 0) &&
(BootSector->PackedBpb.LargeSectors[2] == 0) &&
(BootSector->PackedBpb.LargeSectors[3] == 0)
&&
//
// Number of Sectors cannot be greater than the number of sectors
// on the partition.
//
NextTest
(BootSector->NumberSectors <= Vcb->NumberSectors)
&&
//
// Check that both Lcn values are for sectors within the partition.
//
NextTest
((BootSector->MftStartLcn * BootSector->PackedBpb.SectorsPerCluster[0]) <=
Vcb->NumberSectors)
&&
NextTest
((BootSector->Mft2StartLcn * BootSector->PackedBpb.SectorsPerCluster[0]) <=
Vcb->NumberSectors)
&&
//
// Clusters per file record segment and default clusters for Index
// Allocation Buffers must be a power of 2. A zero indicates that the
// size of these structures is the default size.
//
NextTest
(((BootSector->ClustersPerFileRecordSegment >= -31) &&
(BootSector->ClustersPerFileRecordSegment <= -9)) ||
(BootSector->ClustersPerFileRecordSegment == 0x1) ||
(BootSector->ClustersPerFileRecordSegment == 0x2) ||
(BootSector->ClustersPerFileRecordSegment == 0x4) ||
(BootSector->ClustersPerFileRecordSegment == 0x8) ||
(BootSector->ClustersPerFileRecordSegment == 0x10) ||
(BootSector->ClustersPerFileRecordSegment == 0x20) ||
(BootSector->ClustersPerFileRecordSegment == 0x40))
&&
NextTest
(((BootSector->DefaultClustersPerIndexAllocationBuffer >= -31) &&
(BootSector->DefaultClustersPerIndexAllocationBuffer <= -9)) ||
(BootSector->DefaultClustersPerIndexAllocationBuffer == 0x1) ||
(BootSector->DefaultClustersPerIndexAllocationBuffer == 0x2) ||
(BootSector->DefaultClustersPerIndexAllocationBuffer == 0x4) ||
(BootSector->DefaultClustersPerIndexAllocationBuffer == 0x8) ||
(BootSector->DefaultClustersPerIndexAllocationBuffer == 0x10) ||
(BootSector->DefaultClustersPerIndexAllocationBuffer == 0x20) ||
(BootSector->DefaultClustersPerIndexAllocationBuffer == 0x40))) {
DebugTrace( -1, Dbg, ("NtfsIsBootSectorNtfs->TRUE\n") );
return TRUE;
} else {
//
// If a check failed, print its check number with Debug Trace.
//
DebugTrace( 0, Dbg, ("Boot Sector failed test number %08lx\n", CheckNumber) );
DebugTrace( -1, Dbg, ("NtfsIsBootSectorNtfs->FALSE\n") );
return FALSE;
}
}
//
// Local support routine
//
VOID
NtfsGetVolumeLabel (
IN PIRP_CONTEXT IrpContext,
IN PVPB Vpb OPTIONAL,
IN PVCB Vcb
)
/*++
Routine Description:
This routine gets the serial number and volume label for an NTFS volume
Arguments:
Vpb - Supplies the Vpb for the volume. The Vpb will receive a copy of
the volume label and serial number, if a Vpb is specified.
Vcb - Supplies the Vcb for the operation.
Return Value:
None.
--*/
{
ATTRIBUTE_ENUMERATION_CONTEXT AttributeContext;
PVOLUME_INFORMATION VolumeInformation;
PAGED_CODE();
DebugTrace( 0, Dbg, ("NtfsGetVolumeLabel...\n") );
//
// We read in the volume label attribute to get the volume label.
//
try {
if (ARGUMENT_PRESENT(Vpb)) {
NtfsInitializeAttributeContext( &AttributeContext );
if (NtfsLookupAttributeByCode( IrpContext,
Vcb->VolumeDasdScb->Fcb,
&Vcb->VolumeDasdScb->Fcb->FileReference,
$VOLUME_NAME,
&AttributeContext )) {
Vpb->VolumeLabelLength = (USHORT)
NtfsFoundAttribute( &AttributeContext )->Form.Resident.ValueLength;
if ( Vpb->VolumeLabelLength > MAXIMUM_VOLUME_LABEL_LENGTH) {
Vpb->VolumeLabelLength = MAXIMUM_VOLUME_LABEL_LENGTH;
}
RtlCopyMemory( &Vpb->VolumeLabel[0],
NtfsAttributeValue( NtfsFoundAttribute( &AttributeContext ) ),
Vpb->VolumeLabelLength );
} else {
Vpb->VolumeLabelLength = 0;
}
NtfsCleanupAttributeContext( &AttributeContext );
}
NtfsInitializeAttributeContext( &AttributeContext );
//
// Remember if the volume is dirty when we are mounting it.
//
if (NtfsLookupAttributeByCode( IrpContext,
Vcb->VolumeDasdScb->Fcb,
&Vcb->VolumeDasdScb->Fcb->FileReference,
$VOLUME_INFORMATION,
&AttributeContext )) {
VolumeInformation =
(PVOLUME_INFORMATION)NtfsAttributeValue( NtfsFoundAttribute( &AttributeContext ));
if (FlagOn(VolumeInformation->VolumeFlags, VOLUME_DIRTY)) {
SetFlag( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED_DIRTY );
} else {
ClearFlag( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED_DIRTY );
}
}
} finally {
DebugUnwind( NtfsGetVolumeLabel );
NtfsCleanupAttributeContext( &AttributeContext );
}
//
// And return to our caller
//
return;
}
//
// Local support routine
//
VOID
NtfsSetAndGetVolumeTimes (
IN PIRP_CONTEXT IrpContext,
IN PVCB Vcb,
IN BOOLEAN MarkDirty
)
/*++
Routine Description:
This routine reads in the volume times from the standard information attribute
of the volume file and also updates the access time to be the current
time
Arguments:
Vcb - Supplies the vcb for the operation.
MarkDirty - Supplies TRUE if volume is to be marked dirty
Return Value:
None.
--*/
{
ATTRIBUTE_ENUMERATION_CONTEXT AttributeContext;
PSTANDARD_INFORMATION StandardInformation;
LONGLONG MountTime;
PAGED_CODE();
DebugTrace( 0, Dbg, ("NtfsSetAndGetVolumeTimes...\n") );
try {
//
// Lookup the standard information attribute of the dasd file
//
NtfsInitializeAttributeContext( &AttributeContext );
if (!NtfsLookupAttributeByCode( IrpContext,
Vcb->VolumeDasdScb->Fcb,
&Vcb->VolumeDasdScb->Fcb->FileReference,
$STANDARD_INFORMATION,
&AttributeContext )) {
NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
}
StandardInformation = (PSTANDARD_INFORMATION)NtfsAttributeValue( NtfsFoundAttribute( &AttributeContext ));
//
// Get the current time and make sure it differs from the time stored
// in last access time and then store the new last access time
//
NtfsGetCurrentTime( IrpContext, MountTime );
if (MountTime == StandardInformation->LastAccessTime) {
MountTime = MountTime + 1;
}
//****
//**** Hold back on the update for now.
//****
//**** NtfsChangeAttributeValue( IrpContext,
//**** Vcb->VolumeDasdScb->Fcb,
//**** FIELD_OFFSET(STANDARD_INFORMATION, LastAccessTime),
//**** &MountTime,
//**** sizeof(MountTime),
//**** FALSE,
//**** FALSE,
//**** &AttributeContext );
//
// Now save all the time fields in our vcb
//
Vcb->VolumeCreationTime = StandardInformation->CreationTime;
Vcb->VolumeLastModificationTime = StandardInformation->LastModificationTime;
Vcb->VolumeLastChangeTime = StandardInformation->LastChangeTime;
Vcb->VolumeLastAccessTime = StandardInformation->LastAccessTime; //****Also hold back = MountTime;
NtfsCleanupAttributeContext( &AttributeContext );
//
// If the volume was mounted dirty, then set the dirty bit here.
//
if (MarkDirty) {
NtfsMarkVolumeDirty( IrpContext, Vcb );
}
} finally {
NtfsCleanupAttributeContext( &AttributeContext );
}
//
// And return to our caller
//
return;
}
//
// Local support routine
//
VOID
NtfsOpenSystemFile (
IN PIRP_CONTEXT IrpContext,
IN OUT PSCB *Scb,
IN PVCB Vcb,
IN ULONG FileNumber,
IN LONGLONG Size,
IN ATTRIBUTE_TYPE_CODE AttributeTypeCode,
IN BOOLEAN ModifiedNoWrite
)
/*++
Routine Description:
This routine is called to open one of the system files by its file number
during the mount process. An initial allocation is looked up for the file,
unless the optional initial size is specified (in which case this size is
used).
Parameters:
Scb - Pointer to where the Scb pointer is to be stored. If Scb pointer
pointed to is NULL, then a PreRestart Scb is created, otherwise the
existing Scb is used and only the stream file is set up.
FileNumber - Number of the system file to open.
Size - If nonzero, this size is used as the initial size, rather
than consulting the file record in the Mft.
AttributeTypeCode - Supplies the attribute to open, e.g., $DATA or $BITMAP
ModifiedNoWrite - Indicates if the Memory Manager is not to write this
attribute to disk. Applies to streams under transaction
control.
Return Value:
None.
--*/
{
FILE_REFERENCE FileReference;
UNICODE_STRING $BadName;
PUNICODE_STRING AttributeName = NULL;
PAGED_CODE();
DebugTrace( +1, Dbg, ("NtfsOpenSystemFile:\n") );
DebugTrace( 0, Dbg, ("*Scb = %08lx\n", *Scb) );
DebugTrace( 0, Dbg, ("FileNumber = %08lx\n", FileNumber) );
DebugTrace( 0, Dbg, ("ModifiedNoWrite = %04x\n", ModifiedNoWrite) );
//
// The Bad Cluster data attribute has a name.
//
if (FileNumber == BAD_CLUSTER_FILE_NUMBER) {
RtlInitUnicodeString( &$BadName, L"$Bad" );
AttributeName = &$BadName;
}
//
// If the Scb does not already exist, create it.
//
if (*Scb == NULL) {
NtfsSetSegmentNumber( &FileReference, 0, FileNumber );
FileReference.SequenceNumber = (FileNumber == 0 ? 1 : (USHORT)FileNumber);
//
// Create the Scb.
//
*Scb = NtfsCreatePrerestartScb( IrpContext,
Vcb,
&FileReference,
AttributeTypeCode,
AttributeName,
0 );
NtfsAcquireExclusiveScb( IrpContext, *Scb );
}
//
// Set the modified-no-write bit in the Scb if necessary.
//
if (ModifiedNoWrite) {
SetFlag( (*Scb)->ScbState, SCB_STATE_MODIFIED_NO_WRITE );
}
//
// Lookup the file sizes.
//
if (Size == 0) {
NtfsUpdateScbFromAttribute( IrpContext, *Scb, NULL );
//
// Otherwise, just set the size we were given.
//
} else {
(*Scb)->Header.FileSize.QuadPart =
(*Scb)->Header.ValidDataLength.QuadPart = Size;
(*Scb)->Header.AllocationSize.QuadPart = LlClustersFromBytes( Vcb, Size );
(*Scb)->Header.AllocationSize.QuadPart = LlBytesFromClusters( Vcb,
(*Scb)->Header.AllocationSize.QuadPart );
SetFlag( (*Scb)->ScbState, SCB_STATE_HEADER_INITIALIZED );
}
//
// Finally, create the stream, if not already there.
// And check if we should increment the counters
// If this is the volume file or the bad cluster file, we only increment the counts.
//
if ((FileNumber == VOLUME_DASD_NUMBER) ||
(FileNumber == BAD_CLUSTER_FILE_NUMBER)) {
if ((*Scb)->FileObject == 0) {
NtfsIncrementCloseCounts( *Scb, TRUE, FALSE );
(*Scb)->FileObject = (PFILE_OBJECT) 1;
}
} else {
NtfsCreateInternalAttributeStream( IrpContext, *Scb, TRUE );
}
DebugTrace( 0, Dbg, ("*Scb > %08lx\n", *Scb) );
DebugTrace( -1, Dbg, ("NtfsOpenSystemFile -> VOID\n") );
return;
}
//
// Local support routine
//
VOID
NtfsOpenRootDirectory (
IN PIRP_CONTEXT IrpContext,
IN PVCB Vcb
)
/*++
Routine Description:
This routine opens the root directory by file number, and fills in the
related pointers in the Vcb.
Arguments:
Vcb - Pointer to the Vcb for the volume
Return Value:
None.
--*/
{
PFCB RootFcb;
ATTRIBUTE_ENUMERATION_CONTEXT Context;
FILE_REFERENCE FileReference;
BOOLEAN MustBeFalse;
PAGED_CODE();
//
// Put special code here to do initial open of Root Index.
//
RootFcb = NtfsCreateRootFcb( IrpContext, Vcb );
NtfsSetSegmentNumber( &FileReference, 0, ROOT_FILE_NAME_INDEX_NUMBER );
FileReference.SequenceNumber = ROOT_FILE_NAME_INDEX_NUMBER;
//
// Now create its Scb and acquire it exclusive.
//
Vcb->RootIndexScb = NtfsCreateScb( IrpContext,
RootFcb,
$INDEX_ALLOCATION,
&NtfsFileNameIndex,
FALSE,
&MustBeFalse );
//
// Now allocate a buffer to hold the normalized name for the root.
//
Vcb->RootIndexScb->ScbType.Index.NormalizedName.Buffer = NtfsAllocatePool(PagedPool, 2 );
Vcb->RootIndexScb->ScbType.Index.NormalizedName.MaximumLength =
Vcb->RootIndexScb->ScbType.Index.NormalizedName.Length = 2;
Vcb->RootIndexScb->ScbType.Index.NormalizedName.Buffer[0] = '\\';
NtfsAcquireExclusiveScb( IrpContext, Vcb->RootIndexScb );
//
// Lookup the attribute and it better be there
//
NtfsInitializeAttributeContext( &Context );
//
// Use a try-finally to facilitate cleanup.
//
try {
if (!NtfsLookupAttributeByCode( IrpContext,
RootFcb,
&FileReference,
$INDEX_ROOT,
&Context ) ) {
NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
}
//
// We need to update the duplicated information in the
// Fcb.
NtfsUpdateFcbInfoFromDisk( IrpContext, TRUE, RootFcb, NULL, NULL );
} finally {
NtfsCleanupAttributeContext( &Context );
}
return;
}
//
// Local Support Routine
//
NTSTATUS
NtfsQueryRetrievalPointers (
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;
PSCB Scb;
PCCB Ccb;
PLONGLONG RequestedMapSize;
PLONGLONG *MappingPairs;
PVOID RangePtr;
ULONG Index;
ULONG i;
LONGLONG SectorCount;
LONGLONG Lbo;
LONGLONG Vbo;
LONGLONG Vcn;
LONGLONG 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 and 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.
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
ASSERT( IrpSp->Parameters.FileSystemControl.InputBufferLength == sizeof(LARGE_INTEGER) );
ASSERT( IrpSp->Parameters.FileSystemControl.OutputBufferLength == sizeof(PVOID) );
RequestedMapSize = (PLONGLONG)IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
MappingPairs = (PLONGLONG *)Irp->UserBuffer;
//
// Decode the file object and assert that it is the paging file
//
//
(VOID)NtfsDecodeFileObject( IrpContext, IrpSp->FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
if (!FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE)) {
return STATUS_INVALID_PARAMETER;
}
//
// Acquire exclusive access to the Scb
//
NtfsAcquireExclusiveScb( IrpContext, Scb );
try {
//
// Check if the mapping the caller requested is too large
//
if (*RequestedMapSize > Scb->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.
//
//
// Compute the Vcn which contains the byte just before the offset size
// passed in.
//
MapSize = *RequestedMapSize - 1;
if (*RequestedMapSize == 0) {
Index = 0;
} else {
Vcn = Int64ShraMod32( MapSize, Vcb->ClusterShift );
(VOID)NtfsLookupNtfsMcbEntry( &Scb->Mcb, Vcn, NULL, NULL, NULL, NULL, &RangePtr, &Index );
}
*MappingPairs = NtfsAllocatePool( 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;
i = 0;
if (MapSize != 0) {
for (; i <= Index; i += 1) {
(VOID)NtfsGetNextNtfsMcbEntry( &Scb->Mcb, &RangePtr, i, &Vbo, &Lbo, &SectorCount );
SectorCount = LlBytesFromClusters( Vcb, SectorCount );
if (SectorCount > MapSize) {
SectorCount = MapSize;
}
(*MappingPairs)[ i*2 + 0 ] = SectorCount;
(*MappingPairs)[ i*2 + 1 ] = LlBytesFromClusters( Vcb, Lbo );
MapSize = MapSize - SectorCount;
}
}
(*MappingPairs)[ i*2 + 0 ] = 0;
Status = STATUS_SUCCESS;
try_exit: NOTHING;
} finally {
DebugUnwind( NtfsQueryRetrievalPointers );
//
// Release all of our resources
//
NtfsReleaseScb( IrpContext, Scb );
//
// If this is an abnormal termination then undo our work, otherwise
// complete the irp
//
if (!AbnormalTermination()) {
NtfsCompleteRequest( &IrpContext, &Irp, Status );
}
}
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NtfsGetCompression (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
This routine returns the compression state of the opened file/directory
Arguments:
Irp - Supplies the Irp to process
Return Value:
NTSTATUS - The return status for the operation
--*/
{
PIO_STACK_LOCATION IrpSp;
TYPE_OF_OPEN TypeOfOpen;
PVCB Vcb;
PFCB Fcb;
PSCB Scb;
PCCB Ccb;
PUSHORT CompressionState;
//
// Get the current stack location and extract the output
// buffer information. The output parameter will receive
// the compressed state of the file/directory.
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
//
// Get a pointer to the output buffer. Look at the system buffer field in th
// irp first. Then the Irp Mdl.
//
if (Irp->AssociatedIrp.SystemBuffer != NULL) {
CompressionState = Irp->AssociatedIrp.SystemBuffer;
} else if (Irp->MdlAddress != NULL) {
CompressionState = MmGetSystemAddressForMdl( Irp->MdlAddress );
} else {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_USER_BUFFER );
return STATUS_INVALID_USER_BUFFER;
}
//
// Make sure the output buffer is large enough and then initialize
// the answer to be that the file isn't compressed
//
if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(USHORT)) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
*CompressionState = 0;
//
// Decode the file object
//
TypeOfOpen = NtfsDecodeFileObject( IrpContext, IrpSp->FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
if ((TypeOfOpen != UserFileOpen) &&
#ifdef _CAIRO_
(TypeOfOpen != UserPropertySetOpen) &&
#endif // _CAIRO_
(TypeOfOpen != UserDirectoryOpen)) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
//
// Acquire shared access to the Scb
//
NtfsAcquireSharedScb( IrpContext, Scb );
//
// If this is the index allocation Scb and it has not been initialized then
// lookup the index root and perform the initialization.
//
if ((Scb->AttributeTypeCode == $INDEX_ALLOCATION) &&
(Scb->ScbType.Index.BytesPerIndexBuffer == 0)) {
ATTRIBUTE_ENUMERATION_CONTEXT Context;
NtfsInitializeAttributeContext( &Context );
//
// Use a try-finally to perform cleanup.
//
try {
if (!NtfsLookupAttributeByName( IrpContext,
Scb->Fcb,
&Scb->Fcb->FileReference,
$INDEX_ROOT,
&Scb->AttributeName,
NULL,
FALSE,
&Context )) {
NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
}
NtfsUpdateIndexScbFromAttribute( Scb,
NtfsFoundAttribute( &Context ));
} finally {
NtfsCleanupAttributeContext( &Context );
if (AbnormalTermination()) { NtfsReleaseScb( IrpContext, Scb ); }
}
}
//
// Return the compression state and the size of the returned data.
//
*CompressionState = (USHORT)(Scb->AttributeFlags & ATTRIBUTE_FLAG_COMPRESSION_MASK);
if (*CompressionState != 0) {
*CompressionState += 1;
}
Irp->IoStatus.Information = sizeof( USHORT );
//
// Release all of our resources
//
NtfsReleaseScb( IrpContext, Scb );
//
// If this is an abnormal termination then undo our work, otherwise
// complete the irp
//
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_SUCCESS );
return STATUS_SUCCESS;
}
//
// Local Support Routine
//
VOID
NtfsChangeAttributeCompression (
IN PIRP_CONTEXT IrpContext,
IN PSCB Scb,
IN PVCB Vcb,
IN PCCB Ccb,
IN USHORT CompressionState
)
/*++
Routine Description:
This routine changes the compression state of an attribute on disk,
from not compressed to compressed, or visa versa.
To turn compression off, the caller must already have the Scb acquired
exclusive, and guarantee that the entire file is not compressed.
Arguments:
Scb - Scb for affected stream
Vcb - Vcb for volume
Ccb - Ccb for the open handle
CompressionState - 0 for no compression or nonzero for Rtl compression code - 1
Return Value:
None.
--*/
{
ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
ATTRIBUTE_RECORD_HEADER NewAttribute;
PATTRIBUTE_RECORD_HEADER Attribute;
ULONG AttributeSizeChange;
ULONG OriginalFileAttributes;
UCHAR OriginalHeaderFlags;
UCHAR OriginalCompressionUnitShift;
ULONG OriginalCompressionUnit;
PFCB Fcb = Scb->Fcb;
LONGLONG ByteCount;
ULONG NewCompressionUnit;
UCHAR NewCompressionUnitShift;
//
// Prepare to lookup and change attribute.
//
NtfsInitializeAttributeContext( &AttrContext );
ASSERT( (Scb->Header.PagingIoResource == NULL) ||
(IrpContext->FcbWithPagingExclusive == Fcb) ||
(IrpContext->FcbWithPagingExclusive == (PFCB) Scb) );
NtfsAcquireExclusiveScb( IrpContext, Scb );
OriginalFileAttributes = Fcb->Info.FileAttributes;
OriginalHeaderFlags = Scb->Header.Flags;
OriginalCompressionUnitShift = Scb->CompressionUnitShift;
OriginalCompressionUnit = Scb->CompressionUnit;
try {
//
// Lookup the attribute and pin it so that we can modify it.
//
if ((Scb->Header.NodeTypeCode == NTFS_NTC_SCB_INDEX) ||
(Scb->Header.NodeTypeCode == NTFS_NTC_SCB_ROOT_INDEX)) {
//
// Lookup the attribute record from the Scb.
//
if (!NtfsLookupAttributeByName( IrpContext,
Fcb,
&Fcb->FileReference,
$INDEX_ROOT,
&Scb->AttributeName,
NULL,
FALSE,
&AttrContext )) {
NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, NULL );
}
} else {
NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
}
NtfsPinMappedAttribute( IrpContext, Vcb, &AttrContext );
Attribute = NtfsFoundAttribute( &AttrContext );
if ((CompressionState != 0) && !NtfsIsAttributeResident(Attribute)) {
LONGLONG Temp;
ULONG CompressionUnitInClusters;
//
// If we are turning compression on, then we need to fill out the
// allocation of the compression unit containing file size, or else
// it will be interpreted as compressed when we fault it in. This
// is peanuts compared to the dual copies of clusters we keep around
// in the loop below when we rewrite the file.
//
CompressionUnitInClusters =
ClustersFromBytes( Vcb, Vcb->BytesPerCluster << NTFS_CLUSTERS_PER_COMPRESSION );
Temp = LlClustersFromBytes(Vcb, Scb->Header.AllocationSize.QuadPart);
//
// If FileSize is not already at a cluster boundary, then add
// allocation.
//
if ((ULONG)Temp & (CompressionUnitInClusters - 1)) {
NtfsAddAllocation( IrpContext,
NULL,
Scb,
Temp,
CompressionUnitInClusters - ((ULONG)Temp & (CompressionUnitInClusters - 1)),
FALSE );
if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
Scb->Fcb->Info.AllocatedLength = Scb->TotalAllocated;
SetFlag( Scb->Fcb->InfoFlags, FCB_INFO_CHANGED_ALLOC_SIZE );
}
NtfsWriteFileSizes( IrpContext,
Scb,
&Scb->Header.ValidDataLength.QuadPart,
FALSE,
TRUE );
//
// The attribute may have moved. We will cleanup the attribute
// context and look it up again.
//
NtfsCleanupAttributeContext( &AttrContext );
NtfsInitializeAttributeContext( &AttrContext );
NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
NtfsPinMappedAttribute( IrpContext, Vcb, &AttrContext );
Attribute = NtfsFoundAttribute( &AttrContext );
}
}
//
// If the attribute is resident, copy it here and remember its
// header size.
//
if (NtfsIsAttributeResident(Attribute)) {
RtlCopyMemory( &NewAttribute, Attribute, SIZEOF_RESIDENT_ATTRIBUTE_HEADER );
AttributeSizeChange = SIZEOF_RESIDENT_ATTRIBUTE_HEADER;
//
// Set the correct compression unit but only for data streams. We
// don't want to change this value for the Index Root.
//
if (NtfsIsTypeCodeCompressible( Attribute->TypeCode )) {
if (CompressionState != 0) {
NewCompressionUnit = BytesFromClusters( Scb->Vcb, 1 << NTFS_CLUSTERS_PER_COMPRESSION );
NewCompressionUnitShift = NTFS_CLUSTERS_PER_COMPRESSION;
} else {
NewCompressionUnit = 0;
NewCompressionUnitShift = 0;
}
} else {
NewCompressionUnit = Scb->CompressionUnit;
NewCompressionUnitShift = Scb->CompressionUnitShift;
}
//
// Else if it is nonresident, copy it here, set the compression parameter,
// and remember its size.
//
} else {
AttributeSizeChange = Attribute->Form.Nonresident.MappingPairsOffset;
if (Attribute->NameOffset != 0) {
AttributeSizeChange = Attribute->NameOffset;
}
RtlCopyMemory( &NewAttribute, Attribute, AttributeSizeChange );
if (CompressionState != 0) {
NewAttribute.Form.Nonresident.CompressionUnit = NTFS_CLUSTERS_PER_COMPRESSION;
NewCompressionUnit = Vcb->BytesPerCluster << NTFS_CLUSTERS_PER_COMPRESSION;
NewCompressionUnitShift = NTFS_CLUSTERS_PER_COMPRESSION;
} else {
NewAttribute.Form.Nonresident.CompressionUnit = 0;
NewCompressionUnit = 0;
NewCompressionUnitShift = 0;
}
ASSERT(NewCompressionUnit == 0
|| Scb->AttributeTypeCode == $INDEX_ROOT
|| NtfsIsTypeCodeCompressible( Scb->AttributeTypeCode ));
}
//
// Turn compression on/off
//
NewAttribute.Flags = CompressionState;
//
// Now, log the changed attribute.
//
(VOID)NtfsWriteLog( IrpContext,
Vcb->MftScb,
NtfsFoundBcb(&AttrContext),
UpdateResidentValue,
&NewAttribute,
AttributeSizeChange,
UpdateResidentValue,
Attribute,
AttributeSizeChange,
NtfsMftOffset( &AttrContext ),
PtrOffset(NtfsContainingFileRecord(&AttrContext), Attribute),
0,
Vcb->BytesPerFileRecordSegment );
//
// Change the attribute by calling the same routine called at restart.
//
NtfsRestartChangeValue( IrpContext,
NtfsContainingFileRecord(&AttrContext),
PtrOffset(NtfsContainingFileRecord(&AttrContext), Attribute),
0,
&NewAttribute,
AttributeSizeChange,
FALSE );
//
// If this is the main stream for a file we want to change the file attribute
// for this stream in both the standard information and duplicate
// information structure.
//
if (FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE )) {
if (CompressionState != 0) {
SetFlag( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
} else {
ClearFlag( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
}
SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_FILE_ATTR );
SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
}
//
// Now lets add or remove the total allocated field in the attribute
// header.
//
NtfsSetTotalAllocatedField( IrpContext, Scb, CompressionState );
//
// At this point we will change the compression unit in the Scb.
//
Scb->CompressionUnit = NewCompressionUnit;
Scb->CompressionUnitShift = NewCompressionUnitShift;
//
// Make sure we can reserve enough clusters to start the compress
// operation.
//
if ((CompressionState != 0) && !NtfsIsAttributeResident(Attribute)) {
ByteCount = Scb->Header.FileSize.QuadPart;
if (ByteCount > 0x40000) {
ByteCount = 0x40000;
}
if (!NtfsReserveClusters( IrpContext, Scb, 0, (ULONG) ByteCount )) {
NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, NULL );
}
}
if (FlagOn( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO )) {
NtfsUpdateStandardInformation( IrpContext, Fcb );
ClearFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
}
//
// Checkpoint the transaction now to secure this change.
//
NtfsCheckpointCurrentTransaction( IrpContext );
//
// Update the FastIoField.
//
NtfsAcquireFsrtlHeader( Scb );
Scb->Header.IsFastIoPossible = NtfsIsFastIoPossible( Scb );
NtfsReleaseFsrtlHeader( Scb );
//
// Cleanup on the way out.
//
} finally {
NtfsCleanupAttributeContext( &AttrContext );
//
// If this requests aborts then we want to back out any changes to the
// in-memory structures.
//
if (AbnormalTermination()) {
Fcb->Info.FileAttributes = OriginalFileAttributes;
SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
Scb->Header.Flags = OriginalHeaderFlags;
Scb->CompressionUnitShift = OriginalCompressionUnitShift;
Scb->CompressionUnit = OriginalCompressionUnit;
}
NtfsReleaseScb( IrpContext, Scb );
}
}
//
// Local Support Routine
//
NTSTATUS
NtfsSetCompression (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
This routine compresses or decompresses an entire stream in place,
by walking through the stream and forcing it to be written with the
new compression parameters. As it writes the stream it sets a flag
in the Scb to tell NtfsCommonWrite to delete all allocation at the
outset, to force the space to be reallocated.
Arguments:
Irp - Irp describing the compress or decompress change.
Return Value:
NSTATUS - Status of the request.
--*/
{
PIO_STACK_LOCATION IrpSp;
PIO_STACK_LOCATION NextIrpSp;
TYPE_OF_OPEN TypeOfOpen;
PVCB Vcb;
PFCB Fcb;
PSCB Scb;
PCCB Ccb;
PUSHORT CompressionStatePtr;
PFILE_OBJECT FileObject;
LONGLONG FileOffset;
LONGLONG ByteCount;
PVOID Buffer;
BOOLEAN UserMappedFile;
PBCB Bcb = NULL;
USHORT CompressionState = 0;
PMDL Mdl = NULL;
BOOLEAN ScbAcquired = FALSE;
BOOLEAN PagingIoAcquired = FALSE;
BOOLEAN LockedPages = FALSE;
BOOLEAN FsRtlHeaderLocked = FALSE;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
//
// Get the current stack location and extract the output
// buffer information. The output parameter will receive
// the compressed state of the file/directory.
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
NextIrpSp = IoGetNextIrpStackLocation( Irp );
FileObject = IrpSp->FileObject;
CompressionStatePtr = Irp->AssociatedIrp.SystemBuffer;
//
// Make sure the input buffer is big enough
//
if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(USHORT)) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
//
// Decode the file object
//
TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
//
// See if we are compressing, and only accept the default case or
// lznt1.
//
if (*CompressionStatePtr != 0) {
if ((*CompressionStatePtr == COMPRESSION_FORMAT_DEFAULT) ||
(*CompressionStatePtr == COMPRESSION_FORMAT_LZNT1)) {
CompressionState = COMPRESSION_FORMAT_LZNT1 - 1;
//
// Check that we can compress on this volume.
//
if (!FlagOn( Vcb->AttributeFlagsMask, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_DEVICE_REQUEST );
return STATUS_INVALID_DEVICE_REQUEST;
}
} else {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
}
if (((TypeOfOpen != UserFileOpen) &&
#ifdef _CAIRO_
(TypeOfOpen != UserPropertySetOpen) &&
#endif // _CAIRO_
(TypeOfOpen != UserDirectoryOpen)) ||
FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE)) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
try {
//
// We now want to acquire the Scb to check if we can continue.
//
if (Scb->Header.PagingIoResource != NULL) {
NtfsAcquireExclusivePagingIo( IrpContext, Fcb );
PagingIoAcquired = TRUE;
}
NtfsAcquireExclusiveScb( IrpContext, Scb );
ScbAcquired = TRUE;
if (FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) {
NtfsRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED, NULL, NULL );
}
//
// Handle the simple directory case here.
//
if ((Scb->Header.NodeTypeCode == NTFS_NTC_SCB_INDEX) ||
(Scb->Header.NodeTypeCode == NTFS_NTC_SCB_ROOT_INDEX)) {
NtfsChangeAttributeCompression( IrpContext, Scb, Vcb, Ccb, CompressionState );
if (CompressionState != 0) {
Scb->AttributeFlags = (USHORT)((Scb->AttributeFlags & ~ATTRIBUTE_FLAG_COMPRESSION_MASK) |
CompressionState);
SetFlag( Scb->ScbState, SCB_STATE_COMPRESSED );
} else {
Scb->AttributeFlags = (USHORT)(Scb->AttributeFlags & ~ATTRIBUTE_FLAG_COMPRESSION_MASK);
ClearFlag( Scb->ScbState, SCB_STATE_COMPRESSED );
}
try_return(Status = STATUS_SUCCESS);
}
if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
}
//
// Set the WRITE_ACCESS_SEEN flag so that we will enforce the
// reservation strategy.
//
if (!FlagOn( Scb->ScbState, SCB_STATE_WRITE_ACCESS_SEEN )) {
LONGLONG ClusterCount;
NtfsAcquireReservedClusters( Vcb );
//
// Does this Scb have reserved space that causes us to exceed the free
// space on the volume?
//
ClusterCount = LlClustersFromBytesTruncate( Vcb, Scb->ScbType.Data.TotalReserved );
if ((Scb->ScbType.Data.TotalReserved != 0) &&
((ClusterCount + Vcb->TotalReserved) > Vcb->FreeClusters)) {
NtfsReleaseReservedClusters( Vcb );
try_return( Status = STATUS_DISK_FULL );
}
//
// Otherwise tally in the reserved space now for this Scb, and
// remember that we have seen write access.
//
Vcb->TotalReserved += ClusterCount;
SetFlag( Scb->ScbState, SCB_STATE_WRITE_ACCESS_SEEN );
NtfsReleaseReservedClusters( Vcb );
}
//
// If this is the first pass through SetCompression we need to set this
// request up as the top-level change compression operation. This means
// setting the REALLOCATE_ON_WRITE flag, changing the attribute state
// and putting the SCB_STATE_COMPRESSED flag in the correct state.
//
if (NextIrpSp->Parameters.FileSystemControl.OutputBufferLength == MAXULONG) {
//
// If the REALLOCATE_ON_WRITE flag is set it means that someone is
// already changing the compression state. Return STATUS_SUCCESS in
// that case.
//
if (FlagOn( Scb->ScbState, SCB_STATE_REALLOCATE_ON_WRITE )) {
try_return( Status = STATUS_SUCCESS );
}
//
// If we are turning off compression and the file is uncompressed then
// we can just get out.
//
if ((CompressionState == 0) && ((Scb->AttributeFlags & ATTRIBUTE_FLAG_COMPRESSION_MASK) == 0)) {
try_return( Status = STATUS_SUCCESS );
}
//
// If we are compressing, change the compressed state now.
//
if (CompressionState != 0) {
NtfsChangeAttributeCompression( IrpContext, Scb, Vcb, Ccb, CompressionState );
Scb->AttributeFlags = (USHORT)((Scb->AttributeFlags & ~ATTRIBUTE_FLAG_COMPRESSION_MASK) |
CompressionState);
SetFlag( Scb->ScbState, SCB_STATE_COMPRESSED );
//
// Otherwise, we must clear the compress flag in the Scb to
// start writing decompressed.
//
} else {
ClearFlag( Scb->ScbState, SCB_STATE_COMPRESSED );
}
//
// Set ourselves up as the top level request.
//
SetFlag( Scb->ScbState, SCB_STATE_REALLOCATE_ON_WRITE );
NextIrpSp->Parameters.FileSystemControl.OutputBufferLength = 0;
NextIrpSp->Parameters.FileSystemControl.InputBufferLength = 0;
//
// If we are turning off compression and the file is uncompressed then
// we can just get out. Even if we raised while decompressing. If
// the state is now uncompressed then we have committed the change.
//
} else if ((CompressionState == 0) &&
((Scb->AttributeFlags & ATTRIBUTE_FLAG_COMPRESSION_MASK) == 0)) {
try_return( Status = STATUS_SUCCESS );
}
//
// In the Fsd entry we clear the following two parameter fields in the Irp,
// and then we update them to our current position on all abnormal terminations.
// That way if we get a log file full, we only have to resume where we left
// off.
//
((PLARGE_INTEGER)&FileOffset)->LowPart = NextIrpSp->Parameters.FileSystemControl.OutputBufferLength;
((PLARGE_INTEGER)&FileOffset)->HighPart = NextIrpSp->Parameters.FileSystemControl.InputBufferLength;
//
// If the stream is resident there is no need rewrite any of the data.
//
if (!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
//
// Release all of the files held by this Irp Context. The Mft
// may have been grabbed to make space for the TotalAllocated field.
//
while (!IsListEmpty( &IrpContext->ExclusiveFcbList )) {
NtfsReleaseFcb( IrpContext,
(PFCB)CONTAINING_RECORD( IrpContext->ExclusiveFcbList.Flink,
FCB,
ExclusiveFcbLinks ));
}
//
// Go through and free any Scb's in the queue of shared Scb's
// for transactions.
//
if (IrpContext->SharedScb != NULL) {
NtfsReleaseSharedResources( IrpContext );
}
ScbAcquired = FALSE;
ASSERT(IrpContext->TransactionId == 0);
NtfsReleasePagingIo( IrpContext, Fcb );
PagingIoAcquired = FALSE;
while (TRUE) {
//
// We must throttle our writes.
//
CcCanIWrite( FileObject, 0x40000, TRUE, FALSE );
//
// Lock the FsRtl header so we can freeze FileSize.
// Acquire paging io exclusive if uncompressing so
// we can guarantee that all of the pages get written
// before we mark the file as uncompressed. Otherwise a
// a competing LazyWrite in a range may block after
// going through Mm and Mm will report to this routine
// that the flush has occurred.
//
if (CompressionState == 0) {
ExAcquireResourceExclusive( Scb->Header.PagingIoResource, TRUE );
} else {
ExAcquireResourceShared( Scb->Header.PagingIoResource, TRUE );
}
FsRtlLockFsRtlHeader( &Scb->Header );
IrpContext->FcbWithPagingExclusive = (PFCB) Scb;
FsRtlHeaderLocked = TRUE;
//
// Jump out right here if the attribute is resident.
//
if (FlagOn(Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT)) {
break;
}
//
// Calculate the bytes left in the file to write.
//
ByteCount = Scb->Header.FileSize.QuadPart - FileOffset;
//
// This is how we exit, seeing that we have finally rewritten
// everything. It is possible that the file was truncated
// between passes through this loop so we test for 0 bytes or
// a negative value.
//
// Note that we exit with the Scb still acquired,
// so that we can reliably turn compression off.
//
if (ByteCount <= 0) {
break;
}
//
// 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 (((ULONG)FileOffset & 0x3ffff) + ByteCount > 0x40000) {
ByteCount = 0x40000 - ((ULONG)FileOffset & 0x3ffff);
}
//
// Make sure there are enough available clusters in the range
// we want to rewrite.
//
if (!NtfsReserveClusters( IrpContext, Scb, FileOffset, (ULONG) ByteCount )) {
//
// If this transaction has already deallocated clusters
// then raise log file full to allow those to become
// available.
//
if (IrpContext->DeallocatedClusters != 0) {
NtfsRaiseStatus( IrpContext, STATUS_LOG_FILE_FULL, NULL, NULL );
//
// Otherwise there is insufficient space to guarantee
// we can perform the compression operation.
//
} else {
NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, NULL );
}
}
//
// See if we have to create an internal attribute stream. We do
// it in the loop, because the Scb must be acquired.
//
if (Scb->FileObject == NULL) {
NtfsCreateInternalAttributeStream( IrpContext, Scb, FALSE );
}
//
// Map the next range of the file, and make the pages dirty.
//
CcMapData( Scb->FileObject, (PLARGE_INTEGER)&FileOffset, (ULONG)ByteCount, TRUE, &Bcb, &Buffer );
//
// Now attempt to allocate an Mdl to describe the mapped data.
//
Mdl = IoAllocateMdl( Buffer, (ULONG)ByteCount, FALSE, FALSE, NULL );
if (Mdl == NULL) {
DebugTrace( 0, 0, ("Failed to allocate Mdl\n") );
NtfsRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES, NULL, NULL );
}
//
// Lock the data into memory so that we can safely reallocate the
// space. Don't tell Mm here that we plan to write it, as he sets
// dirty now and at the unlock below if we do.
//
MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
LockedPages = TRUE;
#ifdef SYSCACHE
//
// Clear write mask before the flush
//
{
PULONG WriteMask;
ULONG Len;
ULONG Off = (ULONG)FileOffset;
WriteMask = Scb->ScbType.Data.WriteMask;
if (WriteMask == NULL) {
WriteMask = NtfsAllocatePool( NonPagedPool, (((0x2000000) / PAGE_SIZE) / 8) );
Scb->ScbType.Data.WriteMask = WriteMask;
RtlZeroMemory(WriteMask, (((0x2000000) / PAGE_SIZE) / 8));
}
if (Off < 0x2000000) {
Len = (ULONG)ByteCount;
if ((Off + Len) > 0x2000000) {
Len = 0x2000000 - Off;
}
while (Len != 0) {
WriteMask[(Off / PAGE_SIZE)/32] &= ~(1 << ((Off / PAGE_SIZE) % 32));
Off += PAGE_SIZE;
if (Len <= PAGE_SIZE) {
break;
}
Len -= PAGE_SIZE;
}
}
#endif
//
// Mark the address range modified so that the flush will
// flush.
//
MmSetAddressRangeModified( Buffer, (ULONG)ByteCount );
UserMappedFile = FALSE;
ExAcquireFastMutex( Scb->Header.FastMutex );
if (FlagOn( Scb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE )) {
UserMappedFile = TRUE;
}
//
// Tell Cc there is a user-mapped file so he will really flush.
//
SetFlag( Scb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE );
ExReleaseFastMutex( Scb->Header.FastMutex );
//
// Now flush these pages.
//
Irp->IoStatus.Status = NtfsFlushUserStream( IrpContext,
Scb,
&FileOffset,
(ULONG)ByteCount );
//
// Restore the FsRtl flag if there is no user mapped file.
// This is correctly synchronized, since we have the file
// exclusive, and Mm always has to query the file size before
// creating a user-mapped section and calling Cc to set
// the FsRtl flag.
//
if (!UserMappedFile) {
ExAcquireFastMutex( Scb->Header.FastMutex );
ClearFlag( Scb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE );
ExReleaseFastMutex( Scb->Header.FastMutex );
}
//
// On error get out.
//
NtfsNormalizeAndCleanupTransaction( IrpContext,
&Irp->IoStatus.Status,
TRUE,
STATUS_UNEXPECTED_IO_ERROR );
#ifdef SYSCACHE
//
// Verify writes occurred after the flush
//
Off = (ULONG)FileOffset;
WriteMask = Scb->ScbType.Data.WriteMask;
if (Off < 0x2000000) {
Len = (ULONG)ByteCount;
if ((Off + Len) > 0x2000000) {
Len = 0x2000000 - Off;
}
while (Len != 0) {
ASSERT(WriteMask[(Off / PAGE_SIZE)/32] & (1 << ((Off / PAGE_SIZE) % 32)));
Off += PAGE_SIZE;
if (Len <= PAGE_SIZE) {
break;
}
Len -= PAGE_SIZE;
}
}
}
#endif
//
// Now we can get rid of this Mdl.
//
MmUnlockPages( Mdl );
LockedPages = FALSE;
IoFreeMdl( Mdl );
Mdl = NULL;
//
// Now we can safely unpin and release the Scb for a while.
// (Got to let those checkpoints through!)
//
CcUnpinData( Bcb );
Bcb = NULL;
//
// Release any remaing reserved clusters in this range.
//
NtfsFreeReservedClusters( Scb, FileOffset, (ULONG) ByteCount );
//
// Advance the FileOffset.
//
FileOffset += ByteCount;
//
// If we hit the end of the file then exit while holding the
// resource so we can turn compression off.
//
if (FileOffset == Scb->Header.FileSize.QuadPart) {
break;
}
//
// Unlock the header an let anyone else access the file before
// looping back.
//
FsRtlUnlockFsRtlHeader( &Scb->Header );
ExReleaseResource( Scb->Header.PagingIoResource );
IrpContext->FcbWithPagingExclusive = NULL;
FsRtlHeaderLocked = FALSE;
}
}
//
// We have finished the conversion. Now is the time to turn compression
// off. Note that the compression flag in the Scb is already off.
//
if (CompressionState == 0) {
VCN StartingCluster;
VCN EndingCluster;
//
// The paging Io resource may already be acquired.
//
if (!PagingIoAcquired && !FsRtlHeaderLocked) {
if (Scb->Header.PagingIoResource != NULL) {
NtfsAcquireExclusivePagingIo( IrpContext, Fcb );
PagingIoAcquired = TRUE;
}
}
if (!ScbAcquired) {
NtfsAcquireExclusiveScb( IrpContext, Scb );
ScbAcquired = TRUE;
}
NtfsChangeAttributeCompression( IrpContext, Scb, Vcb, Ccb, 0 );
Scb->AttributeFlags &= (USHORT)~ATTRIBUTE_FLAG_COMPRESSION_MASK;
//
// If we are decompressing then make sure to release any holes at the end of
// the allocation.
//
if (!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
//
// Truncate any extra allocation beyond file size.
//
StartingCluster = LlClustersFromBytes( Vcb, Scb->Header.FileSize.QuadPart );
EndingCluster = Int64ShraMod32(Scb->Header.AllocationSize.QuadPart, Vcb->ClusterShift);
if (StartingCluster < EndingCluster) {
#ifdef _CAIRO_
if (NtfsPerformQuotaOperation( Fcb ) &&
!FlagOn( Scb->ScbState, SCB_STATE_QUOTA_ENLARGED)) {
//
// This can occur if the file handle gets closed
// while the file is being decompressed.
//
ASSERT( Scb->CleanupCount == 0 );
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_QUOTA_DISABLE )
}
#endif // _CAIRO_
NtfsDeleteAllocation( IrpContext,
IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp )->FileObject,
Scb,
StartingCluster,
MAXLONGLONG,
TRUE,
TRUE );
//
// If this is the unnamed data stream then we need to update
// the total allocated size.
//
if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
Scb->Fcb->Info.AllocatedLength = Scb->TotalAllocated;
SetFlag( Scb->Fcb->InfoFlags, FCB_INFO_CHANGED_ALLOC_SIZE );
}
}
//
// We don't want to leave any reserved clusters if we went
// to uncompressed.
//
if (Scb->ScbType.Data.ReservedBitMap != NULL) {
NtfsFreePool( Scb->ScbType.Data.ReservedBitMap );
Scb->ScbType.Data.ReservedBitMap = NULL;
}
NtfsFreeFinalReservedClusters( Vcb,
LlClustersFromBytesTruncate( Vcb, Scb->ScbType.Data.TotalReserved ));
Scb->ScbType.Data.TotalReserved = 0;
//
// Make sure there are no holes in the Mcb.
//
#ifdef SYSCACHE
{
LCN NextLcn;
LONGLONG ClusterCount;
PVOID RangePtr;
ULONG Index;
VCN LastStart = 0;
LONGLONG LastCount = 0;
//
// There better not be any holes in the Mcb at this point.
//
RangePtr = (PVOID) 1;
Index = 0;
while (NtfsGetSequentialMcbEntry( &Scb->Mcb,
&RangePtr,
Index,
&StartingCluster,
&NextLcn,
&ClusterCount )) {
LastStart = StartingCluster;
LastCount = ClusterCount;
Index += 1;
ASSERT( NextLcn != UNUSED_LCN );
}
NextLcn = LlBytesFromClusters( Vcb, LastStart + LastCount );
ASSERT( NextLcn == Scb->Header.AllocationSize.QuadPart );
}
#endif
}
//
// Now clear the REALLOCATE_ON_WRITE flag while holding both resources.
//
ClearFlag( Scb->ScbState, SCB_STATE_REALLOCATE_ON_WRITE );
}
//
// Unlock the header if we locked it.
//
if (FsRtlHeaderLocked) {
FsRtlUnlockFsRtlHeader( &Scb->Header );
ExReleaseResource( Scb->Header.PagingIoResource );
IrpContext->FcbWithPagingExclusive = NULL;
FsRtlHeaderLocked = FALSE;
}
Status = STATUS_SUCCESS;
try_exit: NOTHING;
//
// Now clear the reallocate flag in the Scb if we set it.
//
if (NextIrpSp->Parameters.FileSystemControl.OutputBufferLength != MAXULONG) {
ClearFlag( Scb->ScbState, SCB_STATE_REALLOCATE_ON_WRITE );
}
} finally {
DebugUnwind( NtfsSetCompression );
//
// Cleanup the Mdl if we died with one.
//
if (Mdl != NULL) {
if (LockedPages) {
MmUnlockPages( Mdl );
}
IoFreeMdl( Mdl );
}
if (Bcb != NULL) {
CcUnpinData( Bcb );
}
ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_QUOTA_DISABLE )
//
// If this is an abnormal termination then undo our work, otherwise
// complete the irp
//
if (!AbnormalTermination()) {
if (ScbAcquired) {
NtfsReleaseScb( IrpContext, Scb );
}
NtfsCompleteRequest( &IrpContext, &Irp, Status );
//
// Otherwise, set to restart from the current file position, assuming
// this may be a log file full.
//
} else {
//
// If we have started the transformation and are in the exception path
// we are either going to continue the operation after a clean
// checkpoint or we are done.
//
if (NextIrpSp->Parameters.FileSystemControl.OutputBufferLength != MAXULONG) {
//
// If we are continuing the operation, save the current file offset.
//
if (IrpContext->ExceptionStatus == STATUS_LOG_FILE_FULL ||
IrpContext->ExceptionStatus == STATUS_CANT_WAIT) {
NextIrpSp->Parameters.FileSystemControl.OutputBufferLength = (ULONG)FileOffset;
NextIrpSp->Parameters.FileSystemControl.InputBufferLength = ((PLARGE_INTEGER)&FileOffset)->HighPart;
//
// Otherwise clear the REALLOCATE_ON_WRITE flag and set the
// COMPRESSED flag.
//
} else {
ClearFlag( Scb->ScbState, SCB_STATE_REALLOCATE_ON_WRITE );
SetFlag( Scb->ScbState, SCB_STATE_COMPRESSED );
}
}
if (ScbAcquired) {
NtfsReleaseScb( IrpContext, Scb );
}
//
// We may have one or the other of these conditions to clean up.
//
if (FsRtlHeaderLocked) {
ExReleaseResource( Scb->Header.PagingIoResource );
}
}
}
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NtfsReadCompression (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_NOT_IMPLEMENTED );
return STATUS_NOT_IMPLEMENTED;
}
//
// Local Support Routine
//
NTSTATUS
NtfsWriteCompression (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_NOT_IMPLEMENTED );
return STATUS_NOT_IMPLEMENTED;
}
//
// Local Support Routine
//
NTSTATUS
NtfsMarkAsSystemHive (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the registry to identify the registry handles. We
will mark this in the Ccb and use it during FlushBuffers to know to do a
careful flush.
Arguments:
Irp - Supplies the Irp being processed.
Return Value:
NTSTATUS - The return status for the operation
--*/
{
PIO_STACK_LOCATION IrpSp;
TYPE_OF_OPEN TypeOfOpen;
PVCB Vcb;
PFCB Fcb;
PSCB Scb;
PCCB Ccb;
PAGED_CODE();
//
// Extract and decode the file object
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
TypeOfOpen = NtfsDecodeFileObject( IrpContext, IrpSp->FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
//
// We only permit this request on files and we must be called from kernel mode.
//
if (Irp->RequestorMode != KernelMode ||
TypeOfOpen != UserFileOpen) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
DebugTrace( -1, Dbg, ("NtfsOplockRequest -> STATUS_INVALID_PARAMETER\n") );
return STATUS_INVALID_PARAMETER;
}
//
// Now acquire the file and mark the Ccb and return SUCCESS.
//
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
NtfsAcquireExclusiveScb( IrpContext, Scb );
SetFlag( Ccb->Flags, CCB_FLAG_SYSTEM_HIVE );
NtfsReleaseScb( IrpContext, Scb );
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_SUCCESS );
return STATUS_SUCCESS;
}
//
// Local Support Routine
//
NTSTATUS
NtfsGetStatistics (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
This routine returns the filesystem performance counters for the
volume referred to.
Arguments:
Irp - Supplies the Irp being processed.
Return Value:
NTSTATUS - The return status for the operation
--*/
{
PIO_STACK_LOCATION IrpSp;
TYPE_OF_OPEN TypeOfOpen;
PVCB Vcb;
PFCB Fcb;
PSCB Scb;
PCCB Ccb;
PFILESYSTEM_STATISTICS Stats;
ULONG StatsSize;
PAGED_CODE();
//
// Get the current stack location and extract the output
// buffer information. The output parameter will receive
// the performance counters.
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
//
// Get a pointer to the output buffer. Look at the system buffer field
// in the irp first. Then the Irp Mdl.
//
if (Irp->AssociatedIrp.SystemBuffer != NULL) {
Stats = Irp->AssociatedIrp.SystemBuffer;
} else if (Irp->MdlAddress != NULL) {
Stats = MmGetSystemAddressForMdl( Irp->MdlAddress );
} else {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_USER_BUFFER );
return STATUS_INVALID_USER_BUFFER;
}
//
// Make sure the output buffer is large enough.
//
StatsSize = sizeof(FILESYSTEM_STATISTICS) * **((PCHAR *)&KeNumberProcessors);
if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < StatsSize) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_BUFFER_TOO_SMALL );
return STATUS_BUFFER_TOO_SMALL;
}
//
// Decode the file object
//
TypeOfOpen = NtfsDecodeFileObject( IrpContext, IrpSp->FileObject, &Vcb,
&Fcb, &Scb, &Ccb, TRUE );
RtlCopyMemory( Stats, Vcb->Statistics, StatsSize );
Irp->IoStatus.Information = StatsSize;
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_SUCCESS );
return STATUS_SUCCESS;
}
//
// Local Support Routine
//
NTSTATUS
NtfsGetVolumeData (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
Returns a filled in VOLUME_DATA structure in the user output buffer.
Arguments:
Irp - Supplies the Irp being processed.
Return Value:
NTSTATUS - The return status for the operation.
--*/
{
PIO_STACK_LOCATION IrpSp;
ULONG FsControlCode;
PFILE_OBJECT FileObject;
TYPE_OF_OPEN TypeOfOpen;
PVCB Vcb;
PFCB Fcb;
PSCB Scb;
PCCB Ccb;
PNTFS_VOLUME_DATA_BUFFER VolumeData;
ULONG VolumeDataLength;
//
// Get the current Irp stack location and save some references.
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
DebugTrace( +1, Dbg, ("NtfsGetVolumeData, FsControlCode = %08lx\n", FsControlCode) );
//
// Extract and decode the file object and check for type of open.
//
FileObject = IrpSp->FileObject;
TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
if (TypeOfOpen != UserVolumeOpen) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
//
// Make sure the volume is still mounted.
//
if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_VOLUME_DISMOUNTED );
return STATUS_VOLUME_DISMOUNTED;
}
//
// Get the output buffer length and pointer.
//
VolumeDataLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
VolumeData = (PNTFS_VOLUME_DATA_BUFFER)Irp->AssociatedIrp.SystemBuffer;
//
// Check for a minimum length on the ouput buffer.
//
if (VolumeDataLength < sizeof(NTFS_VOLUME_DATA_BUFFER)) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_BUFFER_TOO_SMALL );
return STATUS_BUFFER_TOO_SMALL;
}
try {
//
// Acquire the volume bitmap and fill in the volume data structure.
//
NtfsAcquireExclusiveScb( IrpContext, Vcb->BitmapScb );
VolumeData->VolumeSerialNumber.QuadPart = Vcb->VolumeSerialNumber;
VolumeData->NumberSectors.QuadPart = Vcb->NumberSectors;
VolumeData->TotalClusters.QuadPart = Vcb->TotalClusters;
VolumeData->FreeClusters.QuadPart = Vcb->FreeClusters;
VolumeData->TotalReserved.QuadPart = Vcb->TotalReserved;
VolumeData->BytesPerSector = Vcb->BytesPerSector;
VolumeData->BytesPerCluster = Vcb->BytesPerCluster;
VolumeData->BytesPerFileRecordSegment = Vcb->BytesPerFileRecordSegment;
VolumeData->ClustersPerFileRecordSegment = Vcb->ClustersPerFileRecordSegment;
VolumeData->MftValidDataLength = Vcb->MftScb->Header.ValidDataLength;
VolumeData->MftStartLcn.QuadPart = Vcb->MftStartLcn;
VolumeData->Mft2StartLcn.QuadPart = Vcb->Mft2StartLcn;
VolumeData->MftZoneStart.QuadPart = Vcb->MftZoneStart;
VolumeData->MftZoneEnd.QuadPart = Vcb->MftZoneEnd;
} finally {
NtfsReleaseScb( IrpContext, Vcb->BitmapScb );
}
//
// If nothing raised then complete the irp.
//
Irp->IoStatus.Information = sizeof(NTFS_VOLUME_DATA_BUFFER);
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_SUCCESS );
DebugTrace( -1, Dbg, ("NtfsGetVolumeData -> VOID\n") );
return STATUS_SUCCESS;
}
//
// Local Support Routine
//
NTSTATUS
NtfsGetVolumeBitmap(
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
This routine scans volume bitmap and returns the requested range.
Input = the GET_BITMAP data structure is passed in through the input buffer.
Output = the VOLUME_BITMAP data structure is returned through the output buffer.
Arguments:
Irp - Supplies the Irp being processed.
Return Value:
NTSTATUS - The return status for the operation.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PIO_STACK_LOCATION IrpSp;
ULONG FsControlCode;
PFILE_OBJECT FileObject;
TYPE_OF_OPEN TypeOfOpen;
PVCB Vcb;
PFCB Fcb;
PSCB Scb;
PCCB Ccb;
PSTARTING_LCN_INPUT_BUFFER GetBitmap;
ULONG GetBitmapLength;
PVOLUME_BITMAP_BUFFER VolumeBitmap;
ULONG VolumeBitmapLength;
ULONG BitsWritten;
LCN Lcn;
LCN StartingLcn;
RTL_BITMAP Bitmap;
PBCB BitmapBcb = NULL;
BOOLEAN StuffAdded = FALSE;
//
// Get the current Irp stack location and save some references.
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
DebugTrace( +1, Dbg, ("NtfsGetVolumeBitmap, FsControlCode = %08lx\n", FsControlCode) );
//
// Extract and decode the file object and check for type of open.
//
FileObject = IrpSp->FileObject;
TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
if (TypeOfOpen != UserVolumeOpen) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
//
// Make sure the volume is still mounted.
//
if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_VOLUME_DISMOUNTED );
return STATUS_VOLUME_DISMOUNTED;
}
//
// Get the input & output buffer lengths and pointers.
//
GetBitmapLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
GetBitmap = (PSTARTING_LCN_INPUT_BUFFER)IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
VolumeBitmapLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
VolumeBitmap = (PVOLUME_BITMAP_BUFFER)NtfsMapUserBuffer( Irp );
//
// Check for a minimum length on the input and ouput buffers.
//
if ((GetBitmapLength < sizeof(STARTING_LCN_INPUT_BUFFER)) ||
(VolumeBitmapLength < sizeof(VOLUME_BITMAP_BUFFER))) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_BUFFER_TOO_SMALL );
return STATUS_BUFFER_TOO_SMALL;
}
//
// Probe the user's buffers.
//
try {
ProbeForRead( GetBitmap, GetBitmapLength, sizeof(UCHAR) );
ProbeForWrite( VolumeBitmap, VolumeBitmapLength, sizeof(UCHAR) );
StartingLcn = GetBitmap->StartingLcn.QuadPart;
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
NtfsRaiseStatus( IrpContext,
FsRtlIsNtstatusExpected(Status) ?
Status : STATUS_INVALID_USER_BUFFER,
NULL, NULL);
}
try {
//
// Acquire the volume bitmap and check for a valid requested Lcn.
//
NtfsAcquireSharedScb( IrpContext, Vcb->BitmapScb );
if (StartingLcn >= Vcb->TotalClusters) {
NtfsRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER, NULL, NULL );
}
//
// Read in the volume bitmap page by page and copy it into the UserBuffer.
//
VolumeBitmapLength -= FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer);
for (Lcn = StartingLcn, BitsWritten = 0;
Lcn < Vcb->TotalClusters;
Lcn = Lcn + Bitmap.SizeOfBitMap) {
ULONG BytesToCopy;
//
// Read in the bitmap page and make sure that we haven't messed up the math.
//
if (StuffAdded) { NtfsFreePool( Bitmap.Buffer ); StuffAdded = FALSE; }
NtfsUnpinBcb( &BitmapBcb );
NtfsMapPageInBitmap( IrpContext, Vcb, Lcn, &Lcn, &Bitmap, &BitmapBcb );
//
// If this is first itteration, update StartingLcn with actual
// starting cluster returned.
//
if (BitsWritten == 0) {
StartingLcn = Lcn;
}
//
// Check to see if we have enough user buffer. If have some but
// not enough, copy what we can and return STATUS_BUFFER_OVERFLOW.
// If we are down to 0 (i.e. previous itteration used all the
// buffer), break right now.
//
BytesToCopy = (Bitmap.SizeOfBitMap + 7) / 8;
if (BytesToCopy > VolumeBitmapLength) {
BytesToCopy = VolumeBitmapLength;
Status = STATUS_BUFFER_OVERFLOW;
if (BytesToCopy == 0) {
break;
}
}
//
// Now bias the bitmap with the RecentlyDeallocatedMcb and copy it into the UserBuffer.
//
StuffAdded = NtfsAddRecentlyDeallocated( Vcb, Lcn, &Bitmap );
try {
RtlCopyMemory(&VolumeBitmap->Buffer[BitsWritten / 8], Bitmap.Buffer, BytesToCopy);
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
NtfsRaiseStatus( IrpContext,
FsRtlIsNtstatusExpected(Status) ?
Status : STATUS_INVALID_USER_BUFFER,
NULL, NULL );
}
//
// If this was an overflow, bump up bits written and continue
//
if (Status != STATUS_BUFFER_OVERFLOW) {
BitsWritten += Bitmap.SizeOfBitMap;
VolumeBitmapLength -= BytesToCopy;
} else {
BitsWritten += BytesToCopy * 8;
break;
}
}
try {
VolumeBitmap->StartingLcn.QuadPart = StartingLcn;
VolumeBitmap->BitmapSize.QuadPart = Vcb->TotalClusters - StartingLcn;
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
NtfsRaiseStatus( IrpContext,
FsRtlIsNtstatusExpected(Status) ?
Status : STATUS_INVALID_USER_BUFFER,
NULL, NULL );
}
Irp->IoStatus.Information =
FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer) + (BitsWritten + 7) / 8;
} finally {
DebugUnwind( NtfsGetVolumeBitmap );
if (StuffAdded) { NtfsFreePool( Bitmap.Buffer ); }
NtfsUnpinBcb( &BitmapBcb );
NtfsReleaseScb( IrpContext, Vcb->BitmapScb );
}
//
// If nothing raised then complete the irp.
//
NtfsCompleteRequest( &IrpContext, &Irp, Status );
DebugTrace( -1, Dbg, ("NtfsGetVolumeBitmap -> VOID\n") );
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NtfsGetRetrievalPointers (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
This routine scans the array of MCBs for the given SCB 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;
TYPE_OF_OPEN TypeOfOpen;
PVCB Vcb;
PFCB Fcb;
PSCB Scb;
PCCB Ccb;
VCN Vcn;
VCN LastVcnInFile;
LCN Lcn;
LONGLONG ClusterCount;
LONGLONG CountFromStartingVcn;
LONGLONG StartingVcn;
ULONG FileRunIndex;
ULONG RangeRunIndex;
ULONG InputBufferLength;
ULONG OutputBufferLength;
PVOID RangePtr;
PRETRIEVAL_POINTERS_BUFFER OutputBuffer;
BOOLEAN AccessingUserBuffer;
//
// Get the current Irp stack location and save some references.
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace( +1, Dbg, ("NtfsGetRetrievalPointers\n") );
//
// Extract and decode the file object and check for type of open.
// If we ever decide to support UserDirectoryOpen also, make sure
// to check for Scb->AttributeTypeCode != $INDEX_ALLOCATION when
// checking whether the Scb header is initialized. Otherwise we'll
// have trouble with phantom Scbs created for small directories.
//
TypeOfOpen = NtfsDecodeFileObject( IrpContext, IrpSp->FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
if (TypeOfOpen != UserFileOpen) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
//
// Get the input and output buffer lengths and pointers.
// Initialize some variables.
//
InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
OutputBuffer = (PRETRIEVAL_POINTERS_BUFFER)NtfsMapUserBuffer( Irp );
//
// Check for a minimum length on the input and ouput buffers.
//
if ((InputBufferLength < sizeof(STARTING_VCN_INPUT_BUFFER)) ||
(OutputBufferLength < sizeof(RETRIEVAL_POINTERS_BUFFER))) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_BUFFER_TOO_SMALL );
return STATUS_BUFFER_TOO_SMALL;
}
//
// Acquire shared access to the Scb. We don't want other threads
// to extend or move the file while we're trying to return the
// retrieval pointers for it.
//
NtfsAcquireSharedScb( IrpContext, Scb );
try {
//
// Check if a starting cluster was specified.
//
LastVcnInFile = LlClustersFromBytesTruncate( Vcb, Scb->Header.AllocationSize.QuadPart ) - 1;
//
// There are three separate places inside this try/except where we
// access the user-supplied buffer. We want to handle exceptions
// differently if they happen while we are trying to access the user
// buffer than if they happen elsewhere in the try/except. We set
// this boolean immediately before touching the user buffer, and
// clear it immediately after.
//
AccessingUserBuffer = FALSE;
try {
AccessingUserBuffer = TRUE;
ProbeForRead( IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
InputBufferLength,
sizeof(UCHAR) );
ProbeForWrite( OutputBuffer, OutputBufferLength, sizeof(UCHAR) );
StartingVcn = ((PSTARTING_VCN_INPUT_BUFFER)IrpSp->Parameters.FileSystemControl.Type3InputBuffer)->StartingVcn.QuadPart;
//
// While we have AccessingUserBuffer set to TRUE, let's initialize the
// extentcount. We increment this for each run in the mcb, so we need
// to initialize it outside the main do while loop.
//
OutputBuffer->ExtentCount = 0;
AccessingUserBuffer = FALSE;
//
// If the Scb is uninitialized, we initialize it now.
//
if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
}
//
// If the data attribute is resident (typically for a small file),
// it is not safe to call NtfsPreloadAllocation. There won't be
// any runs, and we've already set ExtentCount to 0. The best
// thing to do now is leave before somebody gets hurt.
//
if (FlagOn(Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT)) {
try_return( Status = STATUS_SUCCESS );
}
if (StartingVcn > LastVcnInFile) {
//
// It's possible that the Vcn we were given is past the end of the file.
//
try_return( Status = STATUS_END_OF_FILE );
} else {
//
// We need to call NtfsPreloadAllocation to make sure all the
// ranges in this NtfsMcb are loaded.
//
NtfsPreloadAllocation( IrpContext,
Scb,
StartingVcn,
LastVcnInFile );
//
// Decide which Mcb contains the starting Vcn.
//
(VOID)NtfsLookupNtfsMcbEntry( &Scb->Mcb,
StartingVcn,
NULL,
&CountFromStartingVcn,
&Lcn,
&ClusterCount,
&RangePtr,
&RangeRunIndex );
}
//
// Fill in the Vcn where the run containing StartingVcn truly starts.
//
OutputBuffer->StartingVcn.QuadPart = Vcn = StartingVcn - (ClusterCount - CountFromStartingVcn);
//
// FileRunIndex is the index of a given run within an entire
// file, as opposed to RangeRunIndex which is the index of a
// given run within its range. RangeRunIndex is reset to 0 for
// each range, where FileRunIndex is set to 0 once out here.
//
FileRunIndex = 0;
do {
//
// 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.
//
//
// Check for an exhausted output buffer.
//
if ((ULONG)FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[FileRunIndex+1]) > OutputBufferLength) {
//
// We know that we're out of room in the output buffer, so we won't be looking up
// any more runs. ExtentCount currently reflects how many runs we stored in the
// user buffer, so we can safely quit. There are indeed ExtentCount extents stored
// in the array, and returning STATUS_BUFFER_OVERFLOW informs our caller that we
// didn't have enough room to return all the runs.
//
Irp->IoStatus.Information = FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[FileRunIndex]);
try_return( Status = STATUS_BUFFER_OVERFLOW );
}
//
// Here's the interesting part -- we fill in the next array element in the ouput buffer
// with the current run's information.
//
AccessingUserBuffer = TRUE;
OutputBuffer->Extents[FileRunIndex].NextVcn.QuadPart = Vcn + ClusterCount;
OutputBuffer->Extents[FileRunIndex].Lcn.QuadPart = Lcn;
OutputBuffer->ExtentCount += 1;
AccessingUserBuffer = FALSE;
FileRunIndex += 1;
RangeRunIndex += 1;
} while (NtfsGetSequentialMcbEntry( &Scb->Mcb, &RangePtr, RangeRunIndex, &Vcn, &Lcn, &ClusterCount));
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
NtfsRaiseStatus( IrpContext,
((FsRtlIsNtstatusExpected(Status) || !AccessingUserBuffer) ? Status : STATUS_INVALID_USER_BUFFER),
NULL,
NULL );
}
//
// We successfully retrieved extent info to the end of the allocation.
//
Irp->IoStatus.Information = FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[FileRunIndex]);
Status = STATUS_SUCCESS;
try_exit: NOTHING;
} finally {
DebugUnwind( NtfsGetRetrievalPointers );
//
// Release resources.
//
NtfsReleaseScb( IrpContext, Scb );
//
// If nothing raised then complete the irp.
//
if (!AbnormalTermination()) {
NtfsCompleteRequest( &IrpContext, &Irp, Status );
}
DebugTrace( -1, Dbg, ("NtfsGetRetrievalPointers -> VOID\n") );
}
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NtfsGetMftRecord (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
This routine returns a copy of the requested File Record Segment. A
hint File Reference Number is passed in. If the hint File Record
Segment is "not in use" then the MFT bitmap is scanned backwards
from the hint until an "in use" File Record Segment is found. This
File Record Segment is then returned along with the identifying File Reference Number.
Input = the LONGLONG File Reference Number is passed in through the input buffer.
Output = the FILE_RECORD data structure is returned through the output buffer.
Arguments:
Irp - Supplies the Irp being processed.
Return Value:
NTSTATUS - The return status for the operation.
--*/
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PIO_STACK_LOCATION IrpSp;
ULONG FsControlCode;
PFILE_OBJECT FileObject;
TYPE_OF_OPEN TypeOfOpen;
PVCB Vcb;
PFCB Fcb;
PSCB Scb;
PCCB Ccb;
PNTFS_FILE_RECORD_INPUT_BUFFER GetFileRecord;
ULONG GetFileRecordLength;
PNTFS_FILE_RECORD_OUTPUT_BUFFER FileRecord;
ULONG FileRecordLength;
ULONG FileReferenceNumber;
PFILE_RECORD_SEGMENT_HEADER MftBuffer;
PBCB Bcb = NULL;
PBCB BitmapBcb = NULL;
BOOLEAN AcquiredMft = FALSE;
RTL_BITMAP Bitmap;
LONG BaseIndex;
LONG Index;
ULONG BitmapSize;
VCN Vcn = 0;
LONGLONG StartingByte;
PUCHAR BitmapBuffer;
ULONG SizeToMap;
ULONG BytesToCopy;
extern
LONG
NtfsReadMftExceptionFilter (
IN PIRP_CONTEXT IrpContext OPTIONAL,
IN PEXCEPTION_POINTERS ExceptionPointer,
IN NTSTATUS Status
);
//
// Get the current Irp stack location and save some references.
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
DebugTrace( +1, Dbg, ("NtfsGetMftRecord, FsControlCode = %08lx\n", FsControlCode) );
//
// Extract and decode the file object and check for type of open.
//
FileObject = IrpSp->FileObject;
TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
if (TypeOfOpen != UserVolumeOpen) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
//
// Make sure the volume is still mounted.
//
if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_VOLUME_DISMOUNTED );
return STATUS_VOLUME_DISMOUNTED;
}
//
// Get the input & output buffer lengths and pointers.
//
GetFileRecordLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
GetFileRecord = (PNTFS_FILE_RECORD_INPUT_BUFFER)Irp->AssociatedIrp.SystemBuffer;
FileRecordLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
FileRecord = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)Irp->AssociatedIrp.SystemBuffer;;
//
// Check for a minimum length on the input and ouput buffers.
//
if ((GetFileRecordLength < sizeof(NTFS_FILE_RECORD_INPUT_BUFFER)) ||
(FileRecordLength < sizeof(NTFS_FILE_RECORD_OUTPUT_BUFFER))) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_BUFFER_TOO_SMALL );
return STATUS_BUFFER_TOO_SMALL;
}
FileRecordLength -= FIELD_OFFSET(NTFS_FILE_RECORD_OUTPUT_BUFFER, FileRecordBuffer);
FileReferenceNumber = GetFileRecord->FileReferenceNumber.LowPart;
try {
LONGLONG ValidDataLength;
//
// Synchronize the lookup by acquiring the Mft.
//
NtfsAcquireSharedScb( IrpContext, Vcb->MftScb );
AcquiredMft = TRUE;
//
// Raise if the File Reference Number is not within the MFT valid data length.
//
ValidDataLength = Vcb->MftScb->Header.ValidDataLength.QuadPart;
if (FileReferenceNumber > (ValidDataLength / Vcb->BytesPerFileRecordSegment)) {
NtfsRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER, NULL, NULL );
}
//
// Fill in the record size and determine how much of it we can copy.
//
FileRecord->FileRecordLength = Vcb->BytesPerFileRecordSegment;
if (FileRecordLength >= Vcb->BytesPerFileRecordSegment) {
BytesToCopy = Vcb->BytesPerFileRecordSegment;
Status = STATUS_SUCCESS;
} else {
BytesToCopy = FileRecordLength;
Status = STATUS_BUFFER_OVERFLOW;
}
//
// If it is the MFT file record then just get it and we are done.
//
if (FileReferenceNumber == 0) {
try {
NtfsMapStream( IrpContext,
Vcb->MftScb,
0,
Vcb->BytesPerFileRecordSegment,
&Bcb,
(PVOID *)&MftBuffer );
} except (NtfsReadMftExceptionFilter( IrpContext,
GetExceptionInformation(),
GetExceptionCode() )) {
NtfsMapStream( IrpContext,
Vcb->Mft2Scb,
0,
Vcb->BytesPerFileRecordSegment,
&Bcb,
(PVOID *)&MftBuffer );
}
//
// Return the File Reference Number and the File Record.
//
RtlCopyMemory(FileRecord->FileRecordBuffer, MftBuffer, BytesToCopy);
FileRecord->FileReferenceNumber.QuadPart = 0;
try_return(Status);
}
//
// Scan through the MFT Bitmap to find an "in use" file.
//
while (FileReferenceNumber > 0) {
//
// Compute some values for the bitmap, convert the index to the offset of
// this page and get the base index for the File Reference number.
//
Index = FileReferenceNumber;
BitmapSize = (Index + 7) / 8;
Index = Index & (BITS_PER_PAGE - 1);
BaseIndex = FileReferenceNumber - Index;
//
// Set the Vcn count to the full size of the bitmap and move to the beginning
// of this page.
//
((ULONG)Vcn) = ClustersFromBytes(Vcb, ROUND_TO_PAGES(BitmapSize));
((ULONG)Vcn) = (ULONG)Vcn - Vcb->ClustersPerPage;
//
// Calculate the number of bytes to map in the current page.
//
SizeToMap = BitmapSize - BytesFromClusters(Vcb, ((ULONG)Vcn));
if(SizeToMap > BYTES_PER_PAGE) {
SizeToMap = BYTES_PER_PAGE;
}
//
// Initialize the bitmap for this page.
//
StartingByte = LlBytesFromClusters(Vcb, Vcn);
NtfsMapStream( IrpContext,
Vcb->MftBitmapScb,
StartingByte,
SizeToMap,
&BitmapBcb,
&BitmapBuffer );
RtlInitializeBitMap(&Bitmap, (PULONG)BitmapBuffer, SizeToMap * 8);
//
// Scan thru this page for an "in use" File Record.
//
for (; Index >= 0; Index --) {
if (RtlCheckBit(&Bitmap, Index)) {
//
// Found one "in use" on this page so get it and we are done.
//
try {
NtfsMapStream( IrpContext,
Vcb->MftScb,
Int64ShllMod32(BaseIndex + Index, Vcb->MftShift),
Vcb->BytesPerFileRecordSegment,
&Bcb,
(PVOID *)&MftBuffer );
} except (NtfsReadMftExceptionFilter( IrpContext,
GetExceptionInformation(),
GetExceptionCode() )) {
NtfsMapStream( IrpContext,
Vcb->Mft2Scb,
Int64ShllMod32(BaseIndex + Index, Vcb->MftShift),
Vcb->BytesPerFileRecordSegment,
&Bcb,
(PVOID *)&MftBuffer );
}
//
// Return the File Reference Number and the File Record.
//
RtlCopyMemory(FileRecord->FileRecordBuffer, MftBuffer, BytesToCopy);
FileRecord->FileReferenceNumber.QuadPart = BaseIndex + Index;
try_return(Status);
}
}
//
// Cleanup for next time through and decrement the File Reference Number.
//
NtfsUnpinBcb(&BitmapBcb);
FileReferenceNumber = BaseIndex - 1;
}
try_exit: NOTHING;
Irp->IoStatus.Information =
FIELD_OFFSET(NTFS_FILE_RECORD_OUTPUT_BUFFER, FileRecordBuffer) +
BytesToCopy;
} finally {
//
// Release resources and exit.
//
NtfsUnpinBcb(&BitmapBcb);
NtfsUnpinBcb(&Bcb);
if (AcquiredMft) {
NtfsReleaseScb( IrpContext, Vcb->MftScb );
}
DebugTrace( -1, Dbg, ("NtfsGetMftRecord: Exit\n") );
}
//
// If nothing raised then complete the Irp.
//
NtfsCompleteRequest( &IrpContext, &Irp, Status );
DebugTrace( -1, Dbg, ("NtfsGetMftRecord -> VOID\n") );
return Status;
}
//
// Local Support Routine
//
NTSTATUS
NtfsMoveFile (
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. Note that the Vcn and cluster count must be a factor of 16 the compression
chunk.
Arguments:
Irp - Supplies the Irp being processed.
Return Value:
NTSTATUS - The return status for the operation.
--*/
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PIO_STACK_LOCATION IrpSp;
PIO_STACK_LOCATION NextIrpSp;
ULONG FsControlCode;
PFILE_OBJECT FileObject;
TYPE_OF_OPEN TypeOfOpen;
PVCB Vcb;
PFCB Fcb;
PSCB Scb;
PCCB Ccb;
MOVE_FILE_DATA StackMoveData;
PMOVE_FILE_DATA MoveData;
LONGLONG FileOffset;
LONGLONG ByteCount;
PVOID Buffer;
BOOLEAN UserMappedFile;
PBCB Bcb = NULL;
USHORT CompressionState = 0;
PMDL Mdl = NULL;
BOOLEAN ScbAcquired = FALSE;
BOOLEAN PagingIoAcquired = FALSE;
BOOLEAN LockedPages = FALSE;
BOOLEAN FsRtlHeaderLocked = FALSE;
ULONG ScbCompressionUnitSave;
USHORT ScbAttributeFlagsSave;
UCHAR ScbCompressionUnitShiftSave;
ULONG CompressionUnitSize;
extern POBJECT_TYPE *IoFileObjectType;
//
// We should never be in the FSP for this. Otherwise the user handle
// is invalid.
//
ASSERT( !FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_IN_FSP ));
//
// Get the current Irp stack location and save some references.
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
NextIrpSp = IoGetNextIrpStackLocation( Irp );
FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
DebugTrace( +1, Dbg, ("NtfsMoveFile, FsControlCode = %08lx\n", FsControlCode) );
//
// Extract and decode the file object and check for type of open.
//
FileObject = IrpSp->FileObject;
TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
if (TypeOfOpen != UserVolumeOpen) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
//
// Can't defrag on clusters larger than 4K.
//
if (Vcb->BytesPerCluster > 0x1000) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_DEVICE_REQUEST );
return STATUS_INVALID_DEVICE_REQUEST;
}
//
// Get the input buffer pointer and check its length.
//
if (IrpSp->Parameters.FileSystemControl.InputBufferLength <
sizeof(MOVE_FILE_DATA)) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_BUFFER_TOO_SMALL );
return STATUS_BUFFER_TOO_SMALL;
}
//
// This routine modifies the input buffer, so just to be a good
// safe citizen, copy it to our stack.
//
RtlCopyMemory( &StackMoveData,
Irp->AssociatedIrp.SystemBuffer,
sizeof( MOVE_FILE_DATA ));
MoveData = &StackMoveData;
//
// Try to get a pointer to the file object from the handle passed in.
//
Status = ObReferenceObjectByHandle( MoveData->FileHandle,
0,
*IoFileObjectType,
Irp->RequestorMode,
&FileObject,
NULL );
if (!NT_SUCCESS(Status)) {
NtfsCompleteRequest( &IrpContext, &Irp, 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) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
//
// Now decode this FileObject.
//
TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
if ((TypeOfOpen != UserFileOpen) ||
FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE)) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
try {
//
// We now want to acquire the Scb to check if we can continue.
//
if (Scb->Header.PagingIoResource != NULL) {
NtfsAcquireExclusivePagingIo( IrpContext, Fcb );
PagingIoAcquired = TRUE;
}
NtfsAcquireExclusiveScb( IrpContext, Scb );
ScbAcquired = TRUE;
if (FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) {
try_return( Status = STATUS_VOLUME_DISMOUNTED );
}
if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
}
//
// Set the WRITE_ACCESS_SEEN flag so that we will enforce the
// reservation strategy.
//
if (!FlagOn( Scb->ScbState, SCB_STATE_WRITE_ACCESS_SEEN )) {
LONGLONG ClusterCount;
NtfsAcquireReservedClusters( Vcb );
//
// Does this Scb have reserved space that causes us to exceed the free
// space on the volume?
//
ClusterCount = LlClustersFromBytesTruncate( Vcb, Scb->ScbType.Data.TotalReserved );
if ((Scb->ScbType.Data.TotalReserved != 0) &&
((ClusterCount + Vcb->TotalReserved) > Vcb->FreeClusters)) {
NtfsReleaseReservedClusters( Vcb );
try_return( Status = STATUS_DISK_FULL );
}
//
// Otherwise tally in the reserved space now for this Scb, and
// remember that we have seen write access.
//
Vcb->TotalReserved += ClusterCount;
SetFlag( Scb->ScbState, SCB_STATE_WRITE_ACCESS_SEEN );
NtfsReleaseReservedClusters( Vcb );
}
//
// Save some parameters from the Scb, we need to restore these later.
//
ScbCompressionUnitShiftSave = Scb->CompressionUnitShift;
ScbCompressionUnitSave = Scb->CompressionUnit;
ScbAttributeFlagsSave = Scb->AttributeFlags;
CompressionState = COMPRESSION_FORMAT_LZNT1 - 1;
//
// If this is the first pass through NtfsMoveFile we need to set this
// request up as the top-level operation. This means
// setting the REALLOCATE_ON_WRITE flag, changing the attribute state
// and putting the SCB_STATE_COMPRESSED flag in the correct state.
//
if (NextIrpSp->Parameters.FileSystemControl.OutputBufferLength == MAXULONG) {
//
// If the REALLOCATE_ON_WRITE flag is set it means that someone is
// already changing the compression state, so get out now.
//
if (FlagOn( Scb->ScbState, SCB_STATE_REALLOCATE_ON_WRITE )) {
try_return( Status = STATUS_UNSUCCESSFUL );
}
//
// Set ourselves up as the top level request and get the requested file
// offset from MoveData.
//
SetFlag( Scb->ScbState, SCB_STATE_REALLOCATE_ON_WRITE );
FileOffset = LlBytesFromClusters( Vcb, MoveData->StartingVcn.QuadPart );
NextIrpSp->Parameters.FileSystemControl.OutputBufferLength = (ULONG)FileOffset;
NextIrpSp->Parameters.FileSystemControl.InputBufferLength = ((PLARGE_INTEGER)&FileOffset)->HighPart;
}
//
// In the Fsd entry we clear the following two parameter fields in the Irp,
// and then we update them to our current position on all abnormal terminations.
// That way if we get a log file full, we only have to resume where we left
// off. We do pad the defrag range to compression unit boundaries since that
// is the granularity we do our defragging.
//
CompressionUnitSize = Vcb->BytesPerCluster << NTFS_CLUSTERS_PER_COMPRESSION;
((PLARGE_INTEGER)&FileOffset)->LowPart = NextIrpSp->Parameters.FileSystemControl.OutputBufferLength;
((PLARGE_INTEGER)&FileOffset)->HighPart = NextIrpSp->Parameters.FileSystemControl.InputBufferLength;
MoveData->ClusterCount += ClustersFromBytes( Vcb, ((ULONG) FileOffset & (CompressionUnitSize - 1)));
MoveData->ClusterCount += ((1 << NTFS_CLUSTERS_PER_COMPRESSION) - 1);
MoveData->ClusterCount &= ~((1 << NTFS_CLUSTERS_PER_COMPRESSION) - 1);
((PLARGE_INTEGER) &FileOffset)->LowPart &= ~(CompressionUnitSize - 1);
//
// If the stream is resident there is no need rewrite any of the data.
//
if (!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
#ifdef _CAIRO_
//
// Expand quota to the expected state.
//
NtfsExpandQuotaToAllocationSize( IrpContext, Scb );
NtfsReleaseScb( IrpContext, Scb );
ScbAcquired = FALSE;
NtfsReleasePagingIo( IrpContext, Fcb );
PagingIoAcquired = FALSE;
if (IrpContext->TransactionId != 0) {
//
// Complete the request which commits the pending
// transaction if there is one and releases of the
// acquired resources. The IrpContext will not
// be deleted because the no delete flag is set.
//
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DONT_DELETE );
NtfsCompleteRequest( &IrpContext, NULL, STATUS_SUCCESS );
}
#else
NtfsReleaseScb( IrpContext, Scb );
ScbAcquired = FALSE;
ASSERT(IrpContext->TransactionId == 0);
NtfsReleasePagingIo( IrpContext, Fcb );
PagingIoAcquired = FALSE;
#endif // _CAIRO_
while (TRUE) {
//
// We must throttle our writes.
//
CcCanIWrite( FileObject, 0x40000, TRUE, FALSE );
//
// Lock the FsRtl header so we can freeze FileSize.
//
ExAcquireResourceExclusive( Scb->Header.PagingIoResource, TRUE );
FsRtlLockFsRtlHeader( &Scb->Header );
IrpContext->FcbWithPagingExclusive = (PFCB) Scb;
FsRtlHeaderLocked = TRUE;
//
// Jump out right here if the attribute is resident or we
// are beyond the end of the file.
//
if (FlagOn(Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT) ||
(FileOffset >= Scb->Header.FileSize.QuadPart)) {
break;
}
//
// Get the number of bytes left to write.
//
ByteCount = LlBytesFromClusters( Vcb, MoveData->ClusterCount );
//
// Make sure we don't go past the end of the file.
//
if (ByteCount + FileOffset > Scb->Header.FileSize.QuadPart) {
ByteCount = Scb->Header.FileSize.QuadPart - FileOffset;
}
//
// This is how we exit, seeing that we have finally rewritten
// everything. It is possible that the file was truncated
// between passes through this loop so we test for 0 bytes or
// a negative value.
//
// Note that we exit with the Scb still acquired,
// so that we can reliably turn compression off.
//
if (ByteCount <= 0) {
break;
}
//
// 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 (((ULONG)FileOffset & 0x3ffff) + ByteCount > 0x40000) {
ByteCount = 0x40000 - ((ULONG)FileOffset & 0x3ffff);
}
//
// Also remember if we need to add allocation to round the allocation
// size to a compression unit. We need to do this now so that the
// Scb and on-disk allocation sizes will stay in sync. This should
// already be true for compressed files.
//
if ((ScbCompressionUnitSave == 0) &&
((FileOffset + ByteCount + CompressionUnitSize - 1) > Scb->Header.AllocationSize.QuadPart)) {
LONGLONG NewAllocationSize;
NewAllocationSize = FileOffset + ByteCount + CompressionUnitSize - 1;
((PLARGE_INTEGER) &NewAllocationSize)->LowPart &= ~(CompressionUnitSize - 1);
//
// Check again now that we have the exact needed value for allocation
// size.
//
if (NewAllocationSize > Scb->Header.AllocationSize.QuadPart) {
NtfsAcquireExclusiveScb( IrpContext, Scb );
ScbAcquired = TRUE;
NtfsAddAllocation( IrpContext,
NULL,
Scb,
LlClustersFromBytes( Vcb,
Scb->Header.AllocationSize.QuadPart ),
LlClustersFromBytes( Vcb,
NewAllocationSize -
Scb->Header.AllocationSize.QuadPart ),
FALSE );
if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
Scb->Fcb->Info.AllocatedLength = Scb->TotalAllocated;
SetFlag( Scb->Fcb->InfoFlags, FCB_INFO_CHANGED_ALLOC_SIZE );
}
SetFlag( Scb->ScbState, SCB_STATE_TRUNCATE_ON_CLOSE );
NtfsCheckpointCurrentTransaction( IrpContext );
//
// Release everything at this point.
//
if (IrpContext->SharedScb != NULL) {
NtfsReleaseSharedResources( IrpContext );
}
while (!IsListEmpty( &IrpContext->ExclusiveFcbList )) {
NtfsReleaseFcb( IrpContext,
(PFCB)CONTAINING_RECORD( IrpContext->ExclusiveFcbList.Flink,
FCB,
ExclusiveFcbLinks ));
}
ScbAcquired = FALSE;
}
}
//
// Get the pointer to the MOVE_DATA structure in the SCB and set some flags in the Scb.
// These are required in order to ensure a proper path for moving the file
// even though we are not compressing or decompressing.
//
// Acquire and drop the file resource in order to make this change. Otherwise
// a user paging read which acquires the main resource could see an
// inconsistent picture of this data.
//
ExAcquireResourceExclusive( Scb->Header.Resource, TRUE );
Scb->Union.MoveData = MoveData;
Scb->CompressionUnitShift = NTFS_CLUSTERS_PER_COMPRESSION;
Scb->CompressionUnit = CompressionUnitSize;
Scb->AttributeFlags = (USHORT)((Scb->AttributeFlags & ~ATTRIBUTE_FLAG_COMPRESSION_MASK) | CompressionState);
ExReleaseResource( Scb->Header.Resource );
//
// Make sure there are enough available clusters in the range
// we want to rewrite.
//
if (!NtfsReserveClusters( IrpContext, Scb, FileOffset, (ULONG) ByteCount )) {
//
// If this transaction has already deallocated clusters
// then raise log file full to allow those to become
// available.
//
if (IrpContext->DeallocatedClusters != 0) {
NtfsRaiseStatus( IrpContext, STATUS_LOG_FILE_FULL, NULL, NULL );
//
// Otherwise there is insufficient space to guarantee
// we can perform the compression operation.
//
} else {
NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, NULL );
}
}
//
// See if we have to create an internal attribute stream. We do
// it in the loop, because the Scb must be acquired.
//
if (Scb->FileObject == NULL) {
NtfsCreateInternalAttributeStream( IrpContext, Scb, FALSE );
}
//
// Map the next range of the file, and make the pages dirty.
//
CcMapData( Scb->FileObject, (PLARGE_INTEGER)&FileOffset, (ULONG)ByteCount, TRUE, &Bcb, &Buffer );
//
// Now attempt to allocate an Mdl to describe the mapped data.
//
Mdl = IoAllocateMdl( Buffer, (ULONG)ByteCount, FALSE, FALSE, NULL );
if (Mdl == NULL) {
DebugTrace( 0, 0, ("Failed to allocate Mdl\n") );
NtfsRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES, NULL, NULL );
}
//
// Lock the data into memory so that we can safely reallocate the
// space. Don't tell Mm here that we plan to write it, as he sets
// dirty now and at the unlock below if we do.
//
MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
LockedPages = TRUE;
//
// Mark the address range modified so that the flush will
// flush.
//
MmSetAddressRangeModified( Buffer, (ULONG)ByteCount );
UserMappedFile = FALSE;
ExAcquireFastMutex( Scb->Header.FastMutex );
if (FlagOn( Scb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE )) {
UserMappedFile = TRUE;
}
//
// Tell Cc there is a user-mapped file so he will really flush.
//
SetFlag( Scb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE );
ExReleaseFastMutex( Scb->Header.FastMutex );
//
// Now flush these pages.
//
Irp->IoStatus.Status = NtfsFlushUserStream( IrpContext,
Scb,
&FileOffset,
(ULONG)ByteCount );
//
// Restore the FsRtl flag if there is no user mapped file.
// This is correctly synchronized, since we have the file
// exclusive, and Mm always has to query the file size before
// creating a user-mapped section and calling Cc to set
// the FsRtl flag.
//
if (!UserMappedFile) {
ExAcquireFastMutex( Scb->Header.FastMutex );
ClearFlag( Scb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE );
ExReleaseFastMutex( Scb->Header.FastMutex );
}
//
// On error get out.
//
NtfsNormalizeAndCleanupTransaction( IrpContext,
&Irp->IoStatus.Status,
TRUE,
STATUS_UNEXPECTED_IO_ERROR );
//
// Now we can get rid of this Mdl.
//
MmUnlockPages( Mdl );
LockedPages = FALSE;
IoFreeMdl( Mdl );
Mdl = NULL;
//
// Now we can safely unpin and release the Scb for a while.
// (Got to let those checkpoints through!)
//
CcUnpinData( Bcb );
Bcb = NULL;
//
// Release any remaing reserved clusters in this range.
//
NtfsFreeReservedClusters( Scb, FileOffset, (ULONG) ByteCount );
//
// Save the ClusterCount in the Scb
//
MoveData->ClusterCount -= ClustersFromBytes( Vcb, (ULONG) ByteCount );
//
// Zero the pointer to the MOVE_DATA structure in the SCB and restore
// the flags in the Scb. If the file is uncompressed then make
// sure to release any remaining reserved clusters.
//
ExAcquireResourceExclusive( Scb->Header.Resource, TRUE );
if (ScbCompressionUnitSave == 0) {
if (Scb->ScbType.Data.ReservedBitMap != NULL) {
NtfsFreePool( Scb->ScbType.Data.ReservedBitMap );
Scb->ScbType.Data.ReservedBitMap = NULL;
}
NtfsFreeFinalReservedClusters( Vcb,
LlClustersFromBytesTruncate( Vcb, Scb->ScbType.Data.TotalReserved ));
Scb->ScbType.Data.TotalReserved = 0;
}
Scb->Union.MoveData = NULL;
Scb->CompressionUnit = ScbCompressionUnitSave;
Scb->CompressionUnitShift = ScbCompressionUnitShiftSave;
Scb->AttributeFlags = ScbAttributeFlagsSave;
ExReleaseResource( Scb->Header.Resource );
//
// Unlock the header and let anyone else access the file before
// looping back.
//
FsRtlUnlockFsRtlHeader( &Scb->Header );
ExReleaseResource( Scb->Header.PagingIoResource );
IrpContext->FcbWithPagingExclusive = NULL;
FsRtlHeaderLocked = FALSE;
//
// If we hit the end of the file then exit while holding the
// resource so we can turn compression off.
//
if (((ULONG) ByteCount & 0x3ffff) != 0) {
break;
}
//
// Advance the FileOffset.
//
FileOffset += ByteCount;
//
// Update the user's MoveData structure for the next pass in
// case we get a log file full.
//
RtlCopyMemory( Irp->AssociatedIrp.SystemBuffer,
&StackMoveData,
sizeof( MOVE_FILE_DATA ));
}
//
// See if we broke out of the loop with the header locked.
//
if (FsRtlHeaderLocked) {
//
// Zero the pointer to the MOVE_DATA structure in the SCB and restore
// the flags in the Scb. If the file is uncompressed then make
// sure to release any remaining reserved clusters.
//
ExAcquireResourceExclusive( Scb->Header.Resource, TRUE );
if (ScbCompressionUnitSave == 0) {
if (Scb->ScbType.Data.ReservedBitMap != NULL) {
NtfsFreePool( Scb->ScbType.Data.ReservedBitMap );
Scb->ScbType.Data.ReservedBitMap = NULL;
}
NtfsFreeFinalReservedClusters( Vcb,
LlClustersFromBytesTruncate( Vcb, Scb->ScbType.Data.TotalReserved ));
Scb->ScbType.Data.TotalReserved = 0;
}
Scb->Union.MoveData = NULL;
Scb->CompressionUnit = ScbCompressionUnitSave;
Scb->CompressionUnitShift = ScbCompressionUnitShiftSave;
Scb->AttributeFlags = ScbAttributeFlagsSave;
ExReleaseResource( Scb->Header.Resource );
FsRtlUnlockFsRtlHeader( &Scb->Header );
ExReleaseResource( Scb->Header.PagingIoResource );
IrpContext->FcbWithPagingExclusive = NULL;
FsRtlHeaderLocked = FALSE;
}
}
Status = STATUS_SUCCESS;
try_exit: NOTHING;
//
// Now clear the reallocate flag in the Scb if we set it.
//
if (NextIrpSp->Parameters.FileSystemControl.OutputBufferLength != MAXULONG) {
ExAcquireResourceExclusive( Scb->Header.Resource, TRUE );
ClearFlag( Scb->ScbState, SCB_STATE_REALLOCATE_ON_WRITE );
ExReleaseResource( Scb->Header.Resource );
}
} finally {
DebugUnwind( NtfsMovesFile );
//
// Cleanup the Mdl if we died with one.
//
if (Mdl != NULL) {
if (LockedPages) { MmUnlockPages( Mdl ); }
IoFreeMdl( Mdl );
}
if (Bcb != NULL) {
CcUnpinData( Bcb );
}
//
// Restore the Scb flags if we haven't already done so.
//
if (FsRtlHeaderLocked) {
ExAcquireResourceExclusive( Scb->Header.Resource, TRUE );
if (ScbCompressionUnitSave == 0) {
if (Scb->ScbType.Data.ReservedBitMap != NULL) {
NtfsFreePool( Scb->ScbType.Data.ReservedBitMap );
Scb->ScbType.Data.ReservedBitMap = NULL;
}
NtfsFreeFinalReservedClusters( Vcb,
LlClustersFromBytesTruncate( Vcb, Scb->ScbType.Data.TotalReserved ));
Scb->ScbType.Data.TotalReserved = 0;
}
Scb->Union.MoveData = NULL;
Scb->CompressionUnit = ScbCompressionUnitSave;
Scb->CompressionUnitShift = ScbCompressionUnitShiftSave;
Scb->AttributeFlags = ScbAttributeFlagsSave;
ExReleaseResource( Scb->Header.Resource );
FsRtlUnlockFsRtlHeader( &Scb->Header );
}
//
// If this is an abnormal termination then undo our work, otherwise
// complete the irp
//
if (!AbnormalTermination()) {
if (ScbAcquired) {
NtfsReleaseScb( IrpContext, Scb );
}
NtfsCompleteRequest( &IrpContext, &Irp, Status );
//
// Otherwise, set to restart from the current file position, assuming
// this may be a log file full.
//
} else {
//
// If we have started the transformation and are in the exception path
// we are either going to continue the operation after a clean
// checkpoint or we are done.
//
if (NextIrpSp->Parameters.FileSystemControl.OutputBufferLength != MAXULONG) {
//
// If we are continuing the operation, save the current file offset.
//
if (IrpContext->ExceptionStatus == STATUS_LOG_FILE_FULL ||
IrpContext->ExceptionStatus == STATUS_CANT_WAIT) {
NextIrpSp->Parameters.FileSystemControl.OutputBufferLength = (ULONG)FileOffset;
NextIrpSp->Parameters.FileSystemControl.InputBufferLength = ((PLARGE_INTEGER)&FileOffset)->HighPart;
//
// Otherwise clear the REALLOCATE_ON_WRITE flag.
//
} else {
ClearFlag( Scb->ScbState, SCB_STATE_REALLOCATE_ON_WRITE );
}
}
if (ScbAcquired) {
NtfsReleaseScb( IrpContext, Scb );
}
//
// We may have one or the other of these conditions to clean up.
//
if (FsRtlHeaderLocked) {
ExReleaseResource( Scb->Header.PagingIoResource );
}
}
}
return Status;
}
//
// Local support routine
//
NTSTATUS
NtfsIsVolumeDirty (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
This routine returns the dirty state of the volume.
Arguments:
Irp - Supplies the Irp to process
Return Value:
NTSTATUS - The return status for the operation
--*/
{
PIO_STACK_LOCATION IrpSp;
TYPE_OF_OPEN TypeOfOpen;
PVCB Vcb;
PFCB Fcb;
PSCB Scb;
PCCB Ccb;
PULONG VolumeState;
PVOLUME_INFORMATION VolumeInfo;
ATTRIBUTE_ENUMERATION_CONTEXT Context;
//
// Get the current stack location and extract the output
// buffer information. The output parameter will receive
// the compressed state of the file/directory.
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
//
// Get a pointer to the output buffer. Look at the system buffer field in th
// irp first. Then the Irp Mdl.
//
if (Irp->AssociatedIrp.SystemBuffer != NULL) {
VolumeState = Irp->AssociatedIrp.SystemBuffer;
} else if (Irp->MdlAddress != NULL) {
VolumeState = MmGetSystemAddressForMdl( Irp->MdlAddress );
} else {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_USER_BUFFER );
return STATUS_INVALID_USER_BUFFER;
}
//
// Make sure the output buffer is large enough and then initialize
// the answer to be that the volume isn't corrupt.
//
if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG)) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
*VolumeState = 0;
//
// Decode the file object
//
TypeOfOpen = NtfsDecodeFileObject( IrpContext, IrpSp->FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
if (TypeOfOpen != UserVolumeOpen) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
//
// Acquire the Scb shared.
//
NtfsAcquireSharedScb( IrpContext, Scb );
//
// Make sure the volume is still mounted.
//
if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
NtfsReleaseScb( IrpContext, Scb );
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_VOLUME_DISMOUNTED );
return STATUS_VOLUME_DISMOUNTED;
}
//
// Look up the VOLUME_INFORMATION attribute.
//
NtfsInitializeAttributeContext( &Context );
//
// Use a try-finally to perform cleanup.
//
try {
if (!NtfsLookupAttributeByCode( IrpContext,
Fcb,
&Fcb->FileReference,
$VOLUME_INFORMATION,
&Context )) {
NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
}
//
// Return the volume state and the size of the returned data.
//
VolumeInfo = (PVOLUME_INFORMATION) NtfsAttributeValue( NtfsFoundAttribute( &Context ));
*VolumeState = VolumeInfo->VolumeFlags & VOLUME_DIRTY;
Irp->IoStatus.Information = sizeof( ULONG );
} finally {
NtfsReleaseScb( IrpContext, Scb );
NtfsCleanupAttributeContext( &Context );
DebugUnwind( NtfsIsVolumeDirty );
}
//
// If this is an abnormal termination then undo our work, otherwise
// complete the irp
//
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_SUCCESS );
return STATUS_SUCCESS;
}
//
// Local support routine
//
NTSTATUS
NtfsSetExtendedDasdIo (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
This routine will mark a Dasd handle to perform IO outside the logical bounds of
the partition. Any subsequent IO will be passed to the driver which can either
complete it or return an error.
Arguments:
Irp - Supplies the Irp to process
Return Value:
NTSTATUS - The return status for the operation
--*/
{
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
TYPE_OF_OPEN TypeOfOpen;
PVCB Vcb;
PFCB Fcb;
PSCB Scb;
PCCB Ccb;
PAGED_CODE();
//
// Decode the file object
//
TypeOfOpen = NtfsDecodeFileObject( IrpContext, IrpSp->FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
//
// Make sure this is a volume open.
//
if (TypeOfOpen != UserVolumeOpen) {
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
//
// Mark the Ccb for extended Io and return.
//
SetFlag( Ccb->Flags, CCB_FLAG_ALLOW_XTENDED_DASD_IO );
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_SUCCESS );
return STATUS_SUCCESS;
}