8367 lines
233 KiB
C
8367 lines
233 KiB
C
/*++
|
||
|
||
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;
|
||
}
|
||
|
||
|
||
|