mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
749 lines
19 KiB
749 lines
19 KiB
/*++
|
|
|
|
Copyright (c) 1991-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
mftfile.hxx
|
|
|
|
Abstract:
|
|
|
|
This module contains the member function definitions for the
|
|
NTFS_MFT_FILE class.
|
|
|
|
Author:
|
|
|
|
Bill McJohn (billmc) 22-June-91
|
|
|
|
Environment:
|
|
|
|
ULIB, User Mode
|
|
|
|
Notes:
|
|
|
|
The MFT and the Volume Bitmap:
|
|
|
|
The Master File Table needs the bitmap to extend itself. The
|
|
volume bitmap can be passed in upon initialization, or it can
|
|
be supplied (using SetVolumeBitmap) at any time. However,
|
|
until it is supplied, the Master File Table is unable to grow
|
|
itself.
|
|
|
|
--*/
|
|
|
|
#include <pch.cxx>
|
|
|
|
#define _NTAPI_ULIB_
|
|
#define _UNTFS_MEMBER_
|
|
|
|
#include "ulib.hxx"
|
|
#include "error.hxx"
|
|
#include "untfs.hxx"
|
|
|
|
#include "drive.hxx"
|
|
#include "attrib.hxx"
|
|
#include "ntfsbit.hxx"
|
|
#include "mftfile.hxx"
|
|
#include "clusrun.hxx"
|
|
#include "cmem.hxx"
|
|
#include "indxtree.hxx"
|
|
|
|
#define LOGFILE_PLACEMENT_V1 1
|
|
|
|
DEFINE_EXPORTED_CONSTRUCTOR( NTFS_MFT_FILE, NTFS_FILE_RECORD_SEGMENT, UNTFS_EXPORT );
|
|
|
|
UNTFS_EXPORT
|
|
NTFS_MFT_FILE::~NTFS_MFT_FILE(
|
|
)
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
|
|
VOID
|
|
NTFS_MFT_FILE::Construct(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Worker function for the construtor.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
_FirstLcn = 0;
|
|
_VolumeBitmap = NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
NTFS_MFT_FILE::Destroy(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Clean up an NTFS_MFT_FILE object in preparation for
|
|
destruction or reinitialization.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
_FirstLcn = 0;
|
|
_VolumeBitmap = NULL;
|
|
}
|
|
|
|
|
|
UNTFS_EXPORT
|
|
BOOLEAN
|
|
NTFS_MFT_FILE::Initialize(
|
|
IN OUT PLOG_IO_DP_DRIVE Drive,
|
|
IN LCN Lcn,
|
|
IN ULONG ClusterFactor,
|
|
IN ULONG FrsSize,
|
|
IN BIG_INT VolumeSectors,
|
|
IN OUT PNTFS_BITMAP VolumeBitmap,
|
|
IN PNTFS_UPCASE_TABLE UpcaseTable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize an NTFS_MFT_FILE object.
|
|
|
|
Arguments:
|
|
|
|
Drive -- supplies the Drive on which the file table resides
|
|
Lcn -- supplies the logical cluster number of the master
|
|
file table entry which describes the master file
|
|
table itself.
|
|
ClusterFactor -- supplies the number of sectors per cluster.
|
|
FrsSize -- supplies the number of bytes per File Record
|
|
Segment in this MFT.
|
|
VolumeSectors -- supplies the number of volume sectors.
|
|
VolumeBitmap -- supplies the bitmap for the volume. This parameter
|
|
may be NULL.
|
|
|
|
Return Value:
|
|
|
|
TRUE upon successful completion.
|
|
|
|
--*/
|
|
{
|
|
ULONG MirroredClusters;
|
|
ULONG ClusterSize;
|
|
|
|
Destroy();
|
|
|
|
DebugPtrAssert( Drive );
|
|
|
|
_FirstLcn = Lcn;
|
|
_VolumeBitmap = VolumeBitmap;
|
|
|
|
ClusterSize = Drive->QuerySectorSize() * ClusterFactor;
|
|
|
|
MirroredClusters = (REFLECTED_MFT_SEGMENTS * FrsSize + (ClusterSize - 1))
|
|
/ ClusterSize;
|
|
|
|
if( !_MirrorMem.Initialize() ||
|
|
!_MirrorClusterRun.Initialize( &_MirrorMem,
|
|
Drive,
|
|
0,
|
|
ClusterFactor,
|
|
MirroredClusters ) ) {
|
|
|
|
DebugPrint( "Can't initialize MFT helper cluster run.\n" );
|
|
Destroy();
|
|
return FALSE;
|
|
}
|
|
|
|
if (!_Mft.Initialize(&_DataAttribute, &_MftBitmap, VolumeBitmap,
|
|
UpcaseTable, ClusterFactor, FrsSize,
|
|
Drive->QuerySectorSize(), VolumeSectors)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
_Mft.DisableMethods();
|
|
|
|
|
|
if (!NTFS_FILE_RECORD_SEGMENT::Initialize(Drive,
|
|
Lcn,
|
|
&_Mft) ) {
|
|
|
|
Destroy();
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
NTFS_MFT_FILE::Create(
|
|
IN ULONG InitialSize,
|
|
IN PCSTANDARD_INFORMATION StandardInformation,
|
|
IN OUT PNTFS_BITMAP VolumeBitmap
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a new Master File Table for the volume.
|
|
|
|
Arguments:
|
|
|
|
InitialSize -- supplies the number of clusters to allocate
|
|
to the MFT we create.
|
|
StandardInformation -- supplies a standard information structure for
|
|
the MFT's File Record Segment.
|
|
VolumeBitmap -- supplies the bitmap for the volume.
|
|
|
|
Return Value:
|
|
|
|
TRUE upon successful completion.
|
|
|
|
Notes:
|
|
|
|
The caller must first allocate a run of InitialSize clusters
|
|
from the bitmap, and initialize the NTFS_MFT_FILE object
|
|
with the starting cluster of that run.
|
|
|
|
--*/
|
|
{
|
|
NTFS_EXTENT_LIST Extents;
|
|
NTFS_ATTRIBUTE MftBitmapAttribute;
|
|
LCN FirstLcnInMftBitmap;
|
|
ULONG ClustersInMftBitmap;
|
|
ULONG MftBitmapSize;
|
|
ULONG ClusterSize;
|
|
ULONG MftClusters;
|
|
|
|
_Mft.DisableMethods();
|
|
|
|
if( InitialSize < FIRST_USER_FILE_NUMBER ) {
|
|
|
|
DebugPrint( "MFT Initial Size is too small.\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// Set this object up as a File Record Segment:
|
|
|
|
if( !NTFS_FILE_RECORD_SEGMENT::Create( StandardInformation ) ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// OK, set up the data attribute with the disk space supplied
|
|
// by the caller.
|
|
|
|
ClusterSize = GetDrive()->QuerySectorSize() * QueryClusterFactor();
|
|
|
|
MftClusters = (InitialSize * QuerySize() + (ClusterSize - 1)) / ClusterSize;
|
|
|
|
if( !Extents.Initialize( 0, 0 ) ||
|
|
!Extents.AddExtent( 0,
|
|
_FirstLcn,
|
|
MftClusters ) ||
|
|
!_DataAttribute.Initialize( GetDrive(),
|
|
QueryClusterFactor(),
|
|
&Extents,
|
|
InitialSize * QuerySize(),
|
|
InitialSize * QuerySize(),
|
|
$DATA ) ||
|
|
!_DataAttribute.Fill(0, 0) ||
|
|
!_DataAttribute.InsertIntoFile( this,
|
|
VolumeBitmap ) ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Create an MFT Bitmap attribute. Allocate a run on disk to
|
|
// hold its initial size, and use that to set up a non-resident
|
|
// attribute to hold it. The initial size is at least 8k (to
|
|
// allow some space for future growth.)
|
|
//
|
|
|
|
MftBitmapSize = (InitialSize + 7)/ 8;
|
|
|
|
ClustersInMftBitmap = max((MftBitmapSize + (ClusterSize - 1))/ClusterSize,
|
|
/* MFT_BITMAP_INITIAL_SIZE/ClusterSize */, 0);
|
|
|
|
if( !VolumeBitmap->AllocateClusters(
|
|
#if LOGFILE_PLACEMENT_V1 // initial location of MFT Bitmap
|
|
_FirstLcn - ClustersInMftBitmap,
|
|
#else
|
|
1,
|
|
#endif
|
|
ClustersInMftBitmap,
|
|
&FirstLcnInMftBitmap,
|
|
1 ) ||
|
|
!Extents.Initialize( 0, 0 ) ||
|
|
!Extents.AddExtent( 0,
|
|
FirstLcnInMftBitmap,
|
|
ClustersInMftBitmap ) ||
|
|
!MftBitmapAttribute.Initialize( GetDrive(),
|
|
QueryClusterFactor(),
|
|
&Extents,
|
|
MftBitmapSize,
|
|
/* value length */
|
|
/* ClustersInMftBitmap * ClusterSize, */
|
|
MftBitmapSize, /* valid length */
|
|
$BITMAP ) ||
|
|
!MftBitmapAttribute.InsertIntoFile( this,
|
|
VolumeBitmap ) ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Create the MFT Bitmap. Note that it is growable.
|
|
|
|
if( !_MftBitmap.Initialize( InitialSize, TRUE ) ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Mark the system files as in use. Note that we've already
|
|
// checked that InitialSize is at least FIRST_USER_FILE_NUMBER.
|
|
|
|
_MftBitmap.SetAllocated( 0, FIRST_USER_FILE_NUMBER );
|
|
|
|
_Mft.EnableMethods();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
UNTFS_EXPORT
|
|
BOOLEAN
|
|
NTFS_MFT_FILE::Read(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads this FRS for the MFT and then proceeds
|
|
to read the MFT bitmap. If all goes well, the internal
|
|
data attribute and MFT bitmap will be initialized.
|
|
|
|
This method will return TRUE if and only if the base FRS for
|
|
this MFT is correctly read in. The MFT allocation methods will
|
|
be enabled by this method if and only if MFT bitmap and the
|
|
MFT data attribute are properly read in and initialized.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
FALSE - Failure.
|
|
TRUE - Success.
|
|
|
|
--*/
|
|
{
|
|
NTFS_ATTRIBUTE MftBitmapAttribute;
|
|
BIG_INT DataAttributeWrittenLength;
|
|
BIG_INT DataAttributeAllocatedLength;
|
|
BIG_INT NumberOfAllocatedFileRecordSegments,
|
|
NumberOfWrittenFileRecordSegments;
|
|
ULONG RequiredBitsInBitmap, PreferredBitsInBitmap;
|
|
BOOLEAN Error;
|
|
|
|
_Mft.DisableMethods();
|
|
|
|
if (!NTFS_FILE_RECORD_SEGMENT::Read()) {
|
|
return FALSE;
|
|
}
|
|
|
|
_Mft.EnableMethods();
|
|
|
|
// Make sure that we have the Data Attribute to play with.
|
|
|
|
if (!QueryAttribute(&_DataAttribute, &Error, $DATA)) {
|
|
|
|
_Mft.DisableMethods();
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// The length of the bitmap depends on the allocated length of
|
|
// the data attribute.
|
|
|
|
_DataAttribute.QueryValueLength( &DataAttributeWrittenLength,
|
|
&DataAttributeAllocatedLength );
|
|
|
|
// A quick sanity check:
|
|
//
|
|
if( CompareGT(DataAttributeWrittenLength, DataAttributeAllocatedLength) ) {
|
|
|
|
DebugAbort( "UNTFS: MFT Data attribute is corrupt.\n" );
|
|
_Mft.DisableMethods();
|
|
return TRUE;
|
|
}
|
|
|
|
NumberOfWrittenFileRecordSegments = DataAttributeWrittenLength / QuerySize();
|
|
NumberOfAllocatedFileRecordSegments = DataAttributeAllocatedLength / QuerySize();
|
|
|
|
DebugAssert( NumberOfWrittenFileRecordSegments.GetHighPart() == 0 );
|
|
DebugAssert( NumberOfAllocatedFileRecordSegments.GetHighPart() == 0 );
|
|
|
|
RequiredBitsInBitmap = NumberOfWrittenFileRecordSegments.GetLowPart();
|
|
PreferredBitsInBitmap = NumberOfAllocatedFileRecordSegments.GetLowPart();
|
|
|
|
// Create a bitmap, and get the MFT bitmap attribute through
|
|
// which we read it, and read the bitmap.
|
|
|
|
if( !_MftBitmap.Initialize( RequiredBitsInBitmap, TRUE ) ||
|
|
!QueryAttribute( &MftBitmapAttribute, &Error, $BITMAP ) ||
|
|
!_MftBitmap.Read( &MftBitmapAttribute ) ||
|
|
!_MftBitmap.Resize( PreferredBitsInBitmap ) ) {
|
|
|
|
DebugAbort( "Cannot read MFT Bitmap.\n" );
|
|
_Mft.DisableMethods();
|
|
return TRUE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
UNTFS_EXPORT
|
|
BOOLEAN
|
|
NTFS_MFT_FILE::Flush(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method flushes the MFT--re-inserts the DATA attribute (if
|
|
necessary); writes the MFT bitmap, and writes the MFT's own
|
|
File Record Segment.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE upon successful completion.
|
|
|
|
Notes:
|
|
|
|
This method will also write the volume bitmap and the mft mirror.
|
|
It will resize the $DATA attributes on the bitmap and mirror files
|
|
and write those FRS's, if necessary.
|
|
|
|
--*/
|
|
{
|
|
NTFS_BITMAP_FILE BitmapFile;
|
|
NTFS_REFLECTED_MASTER_FILE_TABLE MirrorFile;
|
|
NTFS_INDEX_TREE RootIndex;
|
|
NTFS_FILE_RECORD_SEGMENT RootIndexFrs;
|
|
DSTRING FileNameIndexName;
|
|
|
|
NTFS_ATTRIBUTE MftBitmapAttribute,
|
|
MirrorDataAttribute,
|
|
VolumeBitmapAttribute;
|
|
|
|
LCN FirstMirrorLcn;
|
|
BIG_INT OldValidLength;
|
|
BOOLEAN Error;
|
|
|
|
if( !_Mft.AreMethodsEnabled() ) {
|
|
|
|
DebugAbort( "Tried to flush the MFT before enabling it.\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// Ensure that the bitmap file and mirror file's $DATA attributes
|
|
// are the correct sizes. This will later allow us to write these
|
|
// two constructs without affecting their respective FRS's.
|
|
|
|
if( !BitmapFile.Initialize( &_Mft ) ||
|
|
!BitmapFile.Read() ||
|
|
!BitmapFile.QueryAttribute( &VolumeBitmapAttribute,
|
|
&Error,
|
|
$DATA ) ||
|
|
!_VolumeBitmap->CheckAttributeSize( &VolumeBitmapAttribute,
|
|
_VolumeBitmap ) ||
|
|
!MirrorFile.Initialize( &_Mft ) ||
|
|
!MirrorFile.Read() ||
|
|
!MirrorFile.QueryAttribute( &MirrorDataAttribute,
|
|
&Error,
|
|
$DATA ) ||
|
|
!CheckMirrorSize( &MirrorDataAttribute,
|
|
TRUE,
|
|
_VolumeBitmap,
|
|
&FirstMirrorLcn ) ) {
|
|
|
|
DebugPrint( "Cannot check size of bitmap & mirror attributes.\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if( VolumeBitmapAttribute.IsStorageModified() &&
|
|
( !VolumeBitmapAttribute.InsertIntoFile( &BitmapFile,
|
|
_VolumeBitmap ) ||
|
|
!BitmapFile.Flush( _VolumeBitmap ) ) ) {
|
|
|
|
DebugPrint( "Cannot save volume bitmap attribute.\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
if( MirrorDataAttribute.IsStorageModified() &&
|
|
( !MirrorDataAttribute.InsertIntoFile( &MirrorFile, _VolumeBitmap ) ||
|
|
!MirrorFile.Flush( _VolumeBitmap ) ) ) {
|
|
|
|
DebugPrint( "Cannot save MFT mirror data attribute.\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
// Fetch the root index from its FRS.
|
|
//
|
|
if( !RootIndexFrs.Initialize( ROOT_FILE_NAME_INDEX_NUMBER, this ) ||
|
|
!RootIndexFrs.Read() ||
|
|
!FileNameIndexName.Initialize( FileNameIndexNameData ) ||
|
|
!RootIndex.Initialize( GetDrive(),
|
|
QueryClusterFactor(),
|
|
_VolumeBitmap,
|
|
GetUpcaseTable(),
|
|
QuerySize()/2,
|
|
&RootIndexFrs,
|
|
&FileNameIndexName ) ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Fetch the MFT Bitmap attribute.
|
|
//
|
|
if( !QueryAttribute( &MftBitmapAttribute, &Error, $BITMAP ) ) {
|
|
|
|
DebugPrintTrace(( "UNTFS: Cannot fetch MFT bitmap attribute.\n" ));
|
|
return FALSE;
|
|
}
|
|
|
|
// Write the bitmap once to make it the right size. If
|
|
// it grows while we're saving the MFT, we'll have to
|
|
// write it again.
|
|
//
|
|
if( !_MftBitmap.Write( &MftBitmapAttribute, _VolumeBitmap ) ) {
|
|
|
|
DebugPrintTrace(( "UNTFS: Cannot write the MFT bitmap.\n" ));
|
|
return FALSE;
|
|
}
|
|
|
|
do {
|
|
|
|
// Note that inserting an attribute into a file resets the
|
|
// attribute's StorageModified flag.
|
|
//
|
|
if( MftBitmapAttribute.IsStorageModified() &&
|
|
!MftBitmapAttribute.InsertIntoFile( this, NULL ) ) {
|
|
|
|
DebugPrint( "UNTFS: Cannot save MFT bitmap attribute.\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
// Remember the bitmap size (which is equal to the number
|
|
// of FRS's in the MFT).
|
|
//
|
|
OldValidLength = _DataAttribute.QueryValidDataLength();
|
|
|
|
// Save the data attribute.
|
|
//
|
|
if( _DataAttribute.IsStorageModified() &&
|
|
!_DataAttribute.InsertIntoFile(this, NULL) ) {
|
|
|
|
DebugAbort( "UNTFS: Cannot save MFT's data attribute.\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
// Flush this FRS.
|
|
//
|
|
if( !NTFS_FILE_RECORD_SEGMENT::Flush( _VolumeBitmap, &RootIndex) ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Write the bitmap again, in case it changed.
|
|
//
|
|
if( !_MftBitmap.Write( &MftBitmapAttribute, _VolumeBitmap ) ) {
|
|
|
|
DebugPrintTrace(( "UNTFS: Cannot write the MFT bitmap.\n" ));
|
|
return FALSE;
|
|
}
|
|
|
|
// If the MFT's Valid Data Length changed while we were
|
|
// saving the data attribute and bitmap, we have to go
|
|
// through this loop again.
|
|
|
|
} while( OldValidLength != _DataAttribute.QueryValidDataLength() );
|
|
|
|
// Save the root index:
|
|
//
|
|
if( !RootIndex.Save( &RootIndexFrs ) ||
|
|
!RootIndexFrs.Flush( _VolumeBitmap ) ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if( !_VolumeBitmap->Write( &VolumeBitmapAttribute, NULL ) ||
|
|
!WriteMirror( &MirrorDataAttribute ) ){
|
|
|
|
DebugPrint( "Failed write of MFT Mirror or volume bitmap.\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
NTFS_MFT_FILE::CheckMirrorSize(
|
|
IN OUT PNTFS_ATTRIBUTE MirrorDataAttribute,
|
|
IN BOOLEAN Fix,
|
|
IN OUT PNTFS_BITMAP VolumeBitmap,
|
|
OUT PLCN FirstLcn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method checks that the MFT Mirror $DATA attribute is the
|
|
correct size and contiguous. It can also be used to check these
|
|
restrictions.
|
|
|
|
Arguments:
|
|
|
|
MirrorDataAttribut -- Supplies the MFT Mirror's $DATA attribute.
|
|
Fix -- Supplies a flag which indicates that the
|
|
attribute should be reallocated if it is
|
|
the wrong size or not contiguous.
|
|
VolumeBitmap -- Supplies the volume bitmap (only required
|
|
if Fix is TRUE).
|
|
FirstLcn -- Receives the starting LCN of the mirror.
|
|
|
|
Return Value:
|
|
|
|
TRUE upon successful completion.
|
|
|
|
--*/
|
|
{
|
|
BIG_INT RunLength;
|
|
LCN NewStartingLcn;
|
|
ULONG MirroredClusters;
|
|
ULONG ClusterSize;
|
|
|
|
ClusterSize = QueryClusterFactor() * _Mft.QuerySectorSize();
|
|
|
|
MirroredClusters = (REFLECTED_MFT_SEGMENTS * QuerySize() + (ClusterSize - 1))
|
|
/ ClusterSize;
|
|
|
|
if( MirrorDataAttribute->QueryLcnFromVcn( 0, FirstLcn, &RunLength ) &&
|
|
*FirstLcn != 0 &&
|
|
*FirstLcn != LCN_NOT_PRESENT &&
|
|
RunLength >= MirroredClusters ) {
|
|
|
|
// Everything is perfect.
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Something is not perfect.
|
|
|
|
if( Fix &&
|
|
VolumeBitmap->AllocateClusters( QueryVolumeSectors()/
|
|
QueryClusterFactor()/
|
|
2,
|
|
MirroredClusters,
|
|
&NewStartingLcn ) &&
|
|
MirrorDataAttribute->Resize( 0, VolumeBitmap ) &&
|
|
MirrorDataAttribute->AddExtent( 0,
|
|
NewStartingLcn,
|
|
MirroredClusters ) ) {
|
|
|
|
// It was broken, but now it's perfect.
|
|
|
|
*FirstLcn = NewStartingLcn;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
NTFS_MFT_FILE::WriteMirror(
|
|
IN OUT PNTFS_ATTRIBUTE MirrorDataAttribute
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method writes the MFT Mirror. Note that it will fail if
|
|
the mirror's $DATA attribute is not the correct size or is
|
|
not contiguous.
|
|
|
|
Arguments:
|
|
|
|
MirrorDataAttribute -- Supplies the MFT Mirror's $DATA attribute.
|
|
|
|
Return Value:
|
|
|
|
TRUE upon successful completion.
|
|
|
|
Notes:
|
|
|
|
This method copies whatever is _on disk_ in the MFT's data attribute
|
|
to the mirror's data attribute. Therefore, it should only be called
|
|
after the MFT itself has been written.
|
|
|
|
--*/
|
|
{
|
|
LCN FirstMirrorLcn;
|
|
|
|
if( !CheckMirrorSize( MirrorDataAttribute,
|
|
FALSE,
|
|
NULL,
|
|
&FirstMirrorLcn ) ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
_MirrorClusterRun.Relocate( _FirstLcn );
|
|
|
|
if( !_MirrorClusterRun.Read() ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
_MirrorClusterRun.Relocate( FirstMirrorLcn );
|
|
|
|
if( !_MirrorClusterRun.Write() ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|