450 lines
11 KiB
C++
450 lines
11 KiB
C++
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
mftref.cxx
|
||
|
||
Abstract:
|
||
|
||
This module contains the member function definitions for
|
||
the NTFS_REFLECTED_MASTER_FILE_TABLE class. This class
|
||
models the backup copy of the Master File Table.
|
||
|
||
Author:
|
||
|
||
Bill McJohn (billmc) 13-June-91
|
||
|
||
Environment:
|
||
|
||
ULIB, User Mode
|
||
|
||
--*/
|
||
|
||
#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 "mftref.hxx"
|
||
#include "ifssys.hxx"
|
||
#include "numset.hxx"
|
||
#include "message.hxx"
|
||
#include "rtmsg.h"
|
||
|
||
DEFINE_EXPORTED_CONSTRUCTOR( NTFS_REFLECTED_MASTER_FILE_TABLE,
|
||
NTFS_FILE_RECORD_SEGMENT, UNTFS_EXPORT );
|
||
|
||
UNTFS_EXPORT
|
||
NTFS_REFLECTED_MASTER_FILE_TABLE::~NTFS_REFLECTED_MASTER_FILE_TABLE(
|
||
)
|
||
{
|
||
Destroy();
|
||
}
|
||
|
||
|
||
VOID
|
||
NTFS_REFLECTED_MASTER_FILE_TABLE::Construct(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Worker function for the construtor.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
}
|
||
|
||
|
||
VOID
|
||
NTFS_REFLECTED_MASTER_FILE_TABLE::Destroy(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Clean up an NTFS_MASTER_FILE_TABLE object in preparation for
|
||
destruction or reinitialization.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NTFS_REFLECTED_MASTER_FILE_TABLE::Initialize(
|
||
IN OUT PNTFS_MASTER_FILE_TABLE Mft
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method initializes a Master File Table Reflection object.
|
||
The only special knowledge that it adds to the File Record Segment
|
||
initialization is the location within the Master File Table of the
|
||
Master File Table Reflection.
|
||
|
||
Arguments:
|
||
|
||
Mft -- Supplies the volume MasterFile Table.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion
|
||
|
||
Notes:
|
||
|
||
This class is reinitializable.
|
||
|
||
|
||
--*/
|
||
{
|
||
return( NTFS_FILE_RECORD_SEGMENT::Initialize( MASTER_FILE_TABLE2_NUMBER,
|
||
Mft ) );
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NTFS_REFLECTED_MASTER_FILE_TABLE::Create(
|
||
IN PCSTANDARD_INFORMATION StandardInformation,
|
||
IN OUT PNTFS_BITMAP VolumeBitmap
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method formats a Master File Table Reflection File Record
|
||
Segment in memory (without writing it to disk).
|
||
|
||
Arguments:
|
||
|
||
StandardInformation -- supplies the standard information for the
|
||
file record segment.
|
||
VolumeBitmap -- supplies the bitmap for the volume on
|
||
which this object resides.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
--*/
|
||
{
|
||
NTFS_ATTRIBUTE DataAttribute;
|
||
NTFS_EXTENT_LIST Extents;
|
||
LCN FirstLcn;
|
||
BIG_INT Size;
|
||
ULONG ReflectedMftClusters;
|
||
ULONG cluster_size;
|
||
|
||
// Set this object up as a File Record Segment.
|
||
|
||
if( !NTFS_FILE_RECORD_SEGMENT::Create( StandardInformation ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
// The Master File Table Reflection has a data attribute whose value
|
||
// consists of REFLECTED_MFT_SEGMENTS file record segments. Create
|
||
// merely allocates space for these clusters, it does not write them.
|
||
|
||
cluster_size = QueryClusterFactor() * GetDrive()->QuerySectorSize();
|
||
|
||
ReflectedMftClusters = (REFLECTED_MFT_SEGMENTS * QuerySize() + (cluster_size-1))
|
||
/ cluster_size;
|
||
|
||
Size = ReflectedMftClusters * cluster_size;
|
||
|
||
if( !VolumeBitmap->AllocateClusters( (QueryVolumeSectors()/2)/
|
||
QueryClusterFactor(),
|
||
ReflectedMftClusters,
|
||
&FirstLcn ) ||
|
||
!Extents.Initialize( 0, 0 ) ||
|
||
!Extents.AddExtent( 0,
|
||
FirstLcn,
|
||
ReflectedMftClusters ) ||
|
||
!DataAttribute.Initialize( GetDrive(),
|
||
QueryClusterFactor(),
|
||
&Extents,
|
||
Size,
|
||
Size,
|
||
$DATA ) ||
|
||
!DataAttribute.InsertIntoFile( this, VolumeBitmap ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
NONVIRTUAL
|
||
BOOLEAN
|
||
NTFS_REFLECTED_MASTER_FILE_TABLE::VerifyAndFix(
|
||
IN PNTFS_ATTRIBUTE MftData,
|
||
IN OUT PNTFS_BITMAP VolumeBitmap,
|
||
IN OUT PNUMBER_SET BadClusters,
|
||
IN OUT PNTFS_INDEX_TREE RootIndex,
|
||
OUT PBOOLEAN Changes,
|
||
IN FIX_LEVEL FixLevel,
|
||
IN OUT PMESSAGE Message
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine ensures that this FRS's $DATA attribute is the
|
||
appropriate length (from 1 to 3 clusters). It also compares
|
||
the data in these clusters with the first clusters of the
|
||
$MftData attribute and prints a message if they are different.
|
||
|
||
This routine does not actually write out the contents of these
|
||
clusters because this is done by MFT_FILE::Flush()
|
||
|
||
Arguments:
|
||
|
||
MftData - Supplies the MFT $DATA attribute.
|
||
VolumeBitmap - Supplies the volume bitmap.
|
||
BadClusters - Supplies the list of bad clusters.
|
||
RootIndex - Supplies the root index.
|
||
Changes - Returns whether or not changes were made.
|
||
FixLevel - Supplies the CHKDSK fix level.
|
||
Message - Supplies an outlet for messages.
|
||
|
||
Return Value:
|
||
|
||
FALSE - Failure.
|
||
TRUE - Success.
|
||
|
||
--*/
|
||
{
|
||
HMEM mft_hmem, ref_hmem;
|
||
SECRUN mft_secrun, ref_secrun;
|
||
LCN mft_lcn, ref_lcn;
|
||
BIG_INT run_length;
|
||
ULONG num_clusters;
|
||
ULONG num_sectors;
|
||
ULONG num_bytes, bytes_written;
|
||
BOOLEAN need_write;
|
||
NTFS_ATTRIBUTE data_attribute;
|
||
NTFS_EXTENT_LIST extents;
|
||
BOOLEAN error;
|
||
|
||
|
||
*Changes = FALSE;
|
||
|
||
// First read in the original stuff.
|
||
|
||
num_sectors = (REFLECTED_MFT_SEGMENTS*QuerySize())/GetDrive()->QuerySectorSize();
|
||
num_bytes = num_sectors * GetDrive()->QuerySectorSize();
|
||
|
||
if (!MftData->QueryLcnFromVcn(0, &mft_lcn) ||
|
||
!mft_hmem.Initialize() ||
|
||
!mft_secrun.Initialize(&mft_hmem, GetDrive(),
|
||
mft_lcn*QueryClusterFactor(),
|
||
num_sectors) ||
|
||
!mft_secrun.Read()) {
|
||
|
||
Message->DisplayMsg(MSG_CHK_NO_MEMORY);
|
||
return FALSE;
|
||
}
|
||
|
||
need_write = FALSE;
|
||
|
||
|
||
// Query the $DATA attribute from this FRS.
|
||
|
||
if (!QueryAttribute(&data_attribute, &error, $DATA) ||
|
||
data_attribute.IsResident()) {
|
||
|
||
if (error) {
|
||
Message->DisplayMsg(MSG_CHK_NO_MEMORY);
|
||
return FALSE;
|
||
}
|
||
|
||
Message->LogMsg(MSG_CHKLOG_NTFS_MISSING_OR_RESIDENT_DATA_ATTR_IN_MFT_MIRROR);
|
||
|
||
Message->DisplayMsg(MSG_CHK_NTFS_CORRECTING_MFT_MIRROR);
|
||
|
||
need_write = TRUE;
|
||
|
||
if (!extents.Initialize(0, 0) ||
|
||
!data_attribute.Initialize(GetDrive(), QueryClusterFactor(),
|
||
&extents, 0, 0, $DATA)) {
|
||
|
||
Message->DisplayMsg(MSG_CHK_NTFS_CANT_FIX_MFT_MIRROR);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
|
||
// Make sure that the $DATA attribute is the right size.
|
||
|
||
error = FALSE;
|
||
|
||
if (need_write) {
|
||
error = TRUE;
|
||
} else if (!data_attribute.QueryLcnFromVcn(0, &ref_lcn, &run_length)) {
|
||
|
||
Message->LogMsg(MSG_CHKLOG_NTFS_UNABLE_TO_QUERY_LCN_FROM_VCN_FOR_MFT_MIRROR);
|
||
|
||
error = TRUE;
|
||
} else if (ref_lcn == LCN_NOT_PRESENT) {
|
||
|
||
Message->LogMsg(MSG_CHKLOG_NTFS_LCN_NOT_PRESENT_FOR_VCN_ZERO_OF_MFT_MIRROR);
|
||
|
||
error = TRUE;
|
||
} else if (run_length*QueryClusterFactor() < num_sectors) {
|
||
|
||
Message->LogMsg(MSG_CHKLOG_NTFS_DISCONTIGUOUS_MFT_MIRROR);
|
||
|
||
error = TRUE;
|
||
} else if (data_attribute.QueryValueLength() < num_bytes) {
|
||
|
||
Message->LogMsg(MSG_CHKLOG_NTFS_MFT_MIRROR_HAS_INVALID_VALUE_LENGTH,
|
||
"%I64x%x",
|
||
data_attribute.QueryValueLength().GetLargeInteger(),
|
||
num_bytes);
|
||
|
||
error = TRUE;
|
||
} else if (data_attribute.QueryValidDataLength() < num_bytes) {
|
||
|
||
Message->LogMsg(MSG_CHKLOG_NTFS_MFT_MIRROR_HAS_INVALID_DATA_LENGTH,
|
||
"%I64x%x",
|
||
data_attribute.QueryValidDataLength().GetLargeInteger(),
|
||
num_bytes);
|
||
|
||
error = TRUE;
|
||
} else if (!ref_hmem.Initialize()) {
|
||
|
||
error = TRUE;
|
||
} else if (!ref_secrun.Initialize(&ref_hmem, GetDrive(),
|
||
ref_lcn*QueryClusterFactor(),
|
||
num_sectors)) {
|
||
|
||
error = TRUE;
|
||
} else if (!ref_secrun.Read()) {
|
||
|
||
Message->LogMsg(MSG_CHKLOG_NTFS_UNABLE_TO_READ_MFT_MIRROR,
|
||
"%I64x%x",
|
||
ref_lcn.GetLargeInteger(),
|
||
num_sectors);
|
||
error = TRUE;
|
||
}
|
||
|
||
if (error) {
|
||
|
||
if (!need_write) {
|
||
Message->DisplayMsg(MSG_CHK_NTFS_CORRECTING_MFT_MIRROR);
|
||
}
|
||
|
||
need_write = TRUE;
|
||
|
||
if (data_attribute.QueryLcnFromVcn(0, &ref_lcn, &run_length) &&
|
||
ref_lcn != LCN_NOT_PRESENT &&
|
||
!BadClusters->Add(ref_lcn, run_length)) {
|
||
|
||
Message->DisplayMsg(MSG_CHK_NO_MEMORY);
|
||
return FALSE;
|
||
}
|
||
|
||
num_clusters = (num_sectors+QueryClusterFactor()-1)/QueryClusterFactor();
|
||
|
||
// the write below is just to change the valid data length and valid length
|
||
// to the correct value. The content we write is not of importance.
|
||
|
||
if (!data_attribute.Hotfix(0, num_clusters, VolumeBitmap,
|
||
BadClusters, TRUE) ||
|
||
!data_attribute.Write(NULL,
|
||
num_bytes,
|
||
0,
|
||
&bytes_written,
|
||
NULL)) {
|
||
|
||
Message->DisplayMsg(MSG_CHK_NTFS_CANT_FIX_MFT_MIRROR);
|
||
return FALSE;
|
||
}
|
||
|
||
} else if (memcmp(mft_hmem.GetBuf(), ref_hmem.GetBuf(),
|
||
REFLECTED_MFT_SEGMENTS*QuerySize())) {
|
||
|
||
Message->LogMsg(MSG_CHKLOG_NTFS_MFT_MIRROR_DIFFERENT_FROM_MFT);
|
||
|
||
if (!need_write) {
|
||
Message->DisplayMsg(MSG_CHK_NTFS_CORRECTING_MFT_MIRROR);
|
||
}
|
||
|
||
need_write = TRUE; // set the change status
|
||
}
|
||
|
||
if ((data_attribute.IsStorageModified() &&
|
||
!data_attribute.InsertIntoFile(this, VolumeBitmap)) ||
|
||
(need_write && FixLevel != CheckOnly &&
|
||
!Flush(VolumeBitmap, RootIndex))) {
|
||
|
||
Message->DisplayMsg(MSG_CHK_NTFS_CANT_FIX_MFT_MIRROR);
|
||
return FALSE;
|
||
}
|
||
|
||
*Changes = need_write;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
LCN
|
||
NTFS_REFLECTED_MASTER_FILE_TABLE::QueryFirstLcn(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
The LCN of the first cluster of the Master File Table
|
||
Reflection's $DATA attribute.
|
||
|
||
--*/
|
||
{
|
||
NTFS_ATTRIBUTE DataAttribute;
|
||
LCN Result = 0;
|
||
BOOLEAN Error;
|
||
|
||
if( !QueryAttribute( &DataAttribute, &Error, $DATA ) ||
|
||
!DataAttribute.QueryLcnFromVcn( 0, &Result ) ) {
|
||
|
||
Result = 0;
|
||
}
|
||
|
||
return Result;
|
||
}
|