1042 lines
27 KiB
C++
1042 lines
27 KiB
C++
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991-2001 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
logfile.cxx
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains the member function definitions for
|
|||
|
the NTFS_LOG_FILE class.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Bill McJohn (billmc) 05-May-1992
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
ULIB, User Mode
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include <pch.cxx>
|
|||
|
|
|||
|
#define _NTAPI_ULIB_
|
|||
|
#define _UNTFS_MEMBER_
|
|||
|
|
|||
|
#include "ulib.hxx"
|
|||
|
#include "untfs.hxx"
|
|||
|
|
|||
|
#include "ifssys.hxx"
|
|||
|
#include "drive.hxx"
|
|||
|
#include "attrib.hxx"
|
|||
|
#include "logfile.hxx"
|
|||
|
|
|||
|
#include "message.hxx"
|
|||
|
#include "rtmsg.h"
|
|||
|
|
|||
|
DEFINE_EXPORTED_CONSTRUCTOR( NTFS_LOG_FILE, NTFS_FILE_RECORD_SEGMENT, UNTFS_EXPORT );
|
|||
|
|
|||
|
#define LOGFILE_PLACEMENT_V1 1
|
|||
|
|
|||
|
//
|
|||
|
// These constants are used to determine the default log file size.
|
|||
|
//
|
|||
|
|
|||
|
#define PrimaryLogFileGrowthRate 100 /* 1% of volume size */
|
|||
|
#define SecondaryLogFileGrowthRate 200 /* 0.5% of volume size */
|
|||
|
|
|||
|
#define MaximumVolumeSizeBeforeSlowingDownLogFileGrowthRate (400UL*1024*1024) // 400 MB
|
|||
|
|
|||
|
#define MaximumLogFileSize MAXULONG /* ~ 4 GB */
|
|||
|
#define MaximumInitialLogFileSize 0x4000000 /* 64 MB */
|
|||
|
#define MinimumLogFileSize 0x200000 /* 2 MB */
|
|||
|
#define LogFileAlignmentMask 0x3FFF
|
|||
|
|
|||
|
VOID
|
|||
|
NTFS_LOG_FILE::Construct(
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Worker function for the constructor.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
UNTFS_EXPORT
|
|||
|
NTFS_LOG_FILE::~NTFS_LOG_FILE(
|
|||
|
)
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
UNTFS_EXPORT
|
|||
|
BOOLEAN
|
|||
|
NTFS_LOG_FILE::Initialize(
|
|||
|
IN OUT PNTFS_MASTER_FILE_TABLE Mft
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This method initializes the log file object.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Mft -- Supplies the volume MasterFileTable.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE upon successful completion.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
return( NTFS_FILE_RECORD_SEGMENT::Initialize( LOG_FILE_NUMBER,
|
|||
|
Mft ) );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
NTFS_LOG_FILE::Create(
|
|||
|
IN PCSTANDARD_INFORMATION StandardInformation,
|
|||
|
IN LCN NearLcn,
|
|||
|
IN ULONG InitialSize,
|
|||
|
IN OUT PNTFS_BITMAP VolumeBitmap
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This method creates the Log File. It allocates space for
|
|||
|
the $DATA attribute, fills it with LogFileFillCharacter
|
|||
|
(defined in logfile.hxx).
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
StandardInformation -- Supplies the standard file information.
|
|||
|
NearLcn -- Supplies an LCN near where the $DATA
|
|||
|
attribute should be located.
|
|||
|
InitialSize -- Supplies the initial size of the $DATA
|
|||
|
attribute. If the client passes in
|
|||
|
zero, this routine will choose a default
|
|||
|
size.
|
|||
|
VolumeBitmap -- Supplies the volume bitmap.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE upon successful completion.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
// If the client passed in zero for initial size, calculate
|
|||
|
// the default initial size.
|
|||
|
//
|
|||
|
if( InitialSize == 0 ) {
|
|||
|
|
|||
|
InitialSize = QueryDefaultSize( GetDrive(), QueryVolumeSectors() );
|
|||
|
}
|
|||
|
|
|||
|
// Create the FRS and add the data attribute.
|
|||
|
//
|
|||
|
if( !NTFS_FILE_RECORD_SEGMENT::Create( StandardInformation ) ||
|
|||
|
!CreateDataAttribute( NearLcn, InitialSize, VolumeBitmap )
|
|||
|
) {
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (IsAttributePresent($ATTRIBUTE_LIST, NULL, TRUE)) {
|
|||
|
DebugPrintTrace(("UNTFS: Data attribute of logfile is too fragmented that an attribute list is formed\n"));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
UNTFS_EXPORT
|
|||
|
BOOLEAN
|
|||
|
NTFS_LOG_FILE::CreateDataAttribute(
|
|||
|
IN LCN NearLcn,
|
|||
|
IN ULONG InitialSize OPTIONAL,
|
|||
|
IN OUT PNTFS_BITMAP VolumeBitmap
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This methods creates the log file's $DATA attribute.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
NearLcn -- Supplies an LCN near where the $DATA
|
|||
|
attribute should be located.
|
|||
|
InitialSize -- Supplies the initial size of the $DATA
|
|||
|
attribute. If the client passes in
|
|||
|
zero, this routine will choose a default
|
|||
|
size.
|
|||
|
VolumeBitmap -- Supplies the volume bitmap.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE upon successful completion.
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
This routine may not be multithread safe as it is calling
|
|||
|
SetNextAlloc() to be used by AddDataAttribute().
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ULONG ClusterSize, ClustersInData;
|
|||
|
|
|||
|
// If the client passed in zero for initial size, calculate
|
|||
|
// the default initial size.
|
|||
|
//
|
|||
|
if( InitialSize == 0 ) {
|
|||
|
|
|||
|
InitialSize = QueryDefaultSize( GetDrive(), QueryVolumeSectors() );
|
|||
|
}
|
|||
|
|
|||
|
// Make sure that the file size is a multiple of cluster size:
|
|||
|
//
|
|||
|
ClusterSize = QueryClusterFactor() * GetDrive()->QuerySectorSize();
|
|||
|
|
|||
|
if( InitialSize % ClusterSize ) {
|
|||
|
|
|||
|
ClustersInData = InitialSize / ClusterSize + 1;
|
|||
|
InitialSize = ClustersInData * ClusterSize;
|
|||
|
}
|
|||
|
#if LOGFILE_PLACEMENT_V1
|
|||
|
|
|||
|
else {
|
|||
|
ClustersInData = InitialSize / ClusterSize;
|
|||
|
}
|
|||
|
|
|||
|
if (NearLcn != 0) {
|
|||
|
VolumeBitmap->SetNextAlloc(NearLcn - ClustersInData);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
// Add the data attribute.
|
|||
|
//
|
|||
|
return( AddDataAttribute( InitialSize,
|
|||
|
VolumeBitmap,
|
|||
|
TRUE,
|
|||
|
LogFileFillCharacter ) );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
NTFS_LOG_FILE::MarkVolumeChecked(
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This method sets the signature in the log file to indicate
|
|||
|
that the volume has been checked. This signature supports
|
|||
|
version 1.0 logfiles--ie. does not write the signature at
|
|||
|
the beginning of the second page, and does not record the
|
|||
|
greatest LSN.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE upon successful completion.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
LSN NullLsn;
|
|||
|
|
|||
|
NullLsn.LowPart = 0;
|
|||
|
NullLsn.HighPart = 0;
|
|||
|
|
|||
|
return( MarkVolumeChecked( FALSE, NullLsn ) );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
NTFS_LOG_FILE::MarkVolumeChecked(
|
|||
|
BOOLEAN WriteSecondPage,
|
|||
|
LSN GreatestLsn
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This method sets the signature in the log file to indicate
|
|||
|
that the volume has been checked.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
WriteSecondPage -- Supplies a flag which, if TRUE, indicates
|
|||
|
that the checked signature should also be
|
|||
|
written at the beginning of the second page
|
|||
|
of the file, and the greatest LSN on the
|
|||
|
volume should be recorded.
|
|||
|
|
|||
|
GreatestLsn -- Supplies the greatest LSN encountered on
|
|||
|
the volume. Ignored if WriteSecondPage is
|
|||
|
FALSE.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE upon successful completion.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTFS_ATTRIBUTE DataAttribute;
|
|||
|
UCHAR Signature[LogFileSignatureLength];
|
|||
|
LSN SignatureAndLsn[2];
|
|||
|
ULONG BytesTransferred;
|
|||
|
BOOLEAN Error;
|
|||
|
ULONG i, PageSize;
|
|||
|
|
|||
|
// Fetch the data attribute:
|
|||
|
//
|
|||
|
if( !QueryAttribute( &DataAttribute, &Error, $DATA ) ) {
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
// If the data attribute is resident, the volume is corrupt:
|
|||
|
//
|
|||
|
if( DataAttribute.IsResident() ) {
|
|||
|
|
|||
|
DbgPrint( "UNTFS: Log File $DATA attribute is resident.\n" );
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
// Read the old signature--it's at offset zero in the $DATA
|
|||
|
// attribute, with a length of LogFileSignatureLength bytes.
|
|||
|
//
|
|||
|
if( !DataAttribute.Read( Signature,
|
|||
|
0,
|
|||
|
LogFileSignatureLength,
|
|||
|
&BytesTransferred ) ||
|
|||
|
BytesTransferred != LogFileSignatureLength ) {
|
|||
|
|
|||
|
DbgPrint( "UNTFS: Can't read log file signature.\n" );
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if( !WriteSecondPage ) {
|
|||
|
|
|||
|
DebugAssert(FALSE);
|
|||
|
|
|||
|
// The client just wants the first signature.
|
|||
|
//
|
|||
|
memcpy( Signature,
|
|||
|
LOG_FILE_SIGNATURE_CHECKED,
|
|||
|
LogFileSignatureLength );
|
|||
|
|
|||
|
if( !DataAttribute.Write( Signature,
|
|||
|
0,
|
|||
|
LogFileSignatureLength,
|
|||
|
&BytesTransferred,
|
|||
|
NULL ) ||
|
|||
|
BytesTransferred != LogFileSignatureLength ) {
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
// The client wants us to write the signature and LSN at
|
|||
|
// the beginning of the first two pages.
|
|||
|
//
|
|||
|
PageSize = IFS_SYSTEM::QueryPageSize();
|
|||
|
|
|||
|
if( PageSize == 0 ||
|
|||
|
CompareLT(DataAttribute.QueryValidDataLength(),
|
|||
|
PageSize + sizeof( SignatureAndLsn )) ) {
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
memset( SignatureAndLsn, 0, sizeof(SignatureAndLsn) );
|
|||
|
|
|||
|
memcpy( SignatureAndLsn,
|
|||
|
LOG_FILE_SIGNATURE_CHECKED,
|
|||
|
LogFileSignatureLength );
|
|||
|
|
|||
|
SignatureAndLsn[1] = GreatestLsn;
|
|||
|
|
|||
|
for( i = 0; i < 2; i++ ) {
|
|||
|
|
|||
|
if( !DataAttribute.Write( SignatureAndLsn,
|
|||
|
PageSize * i,
|
|||
|
sizeof( SignatureAndLsn ),
|
|||
|
&BytesTransferred,
|
|||
|
NULL ) ||
|
|||
|
BytesTransferred != sizeof( SignatureAndLsn ) ) {
|
|||
|
|
|||
|
DebugPrintTrace(("UNTFS: Unable to write out logfile signature & lsn\n"));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Since we didn't modify the storage of the attribute, we don't
|
|||
|
// need to save it.
|
|||
|
//
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
NTFS_LOG_FILE::Reset(
|
|||
|
IN OUT PMESSAGE Message
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This method resets the Log File by filling it with
|
|||
|
the LogFileFillCharacter (0xFF).
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Message -- Supplies an outlet for messages.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE upon successful completion.
|
|||
|
|
|||
|
Note that, since the Log File's $DATA attribute is always
|
|||
|
non-resident and is never sparse, resetting the log file
|
|||
|
does not change the data attribute's Attribute Record or
|
|||
|
the Log File's File Record Segment
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTFS_ATTRIBUTE DataAttribute;
|
|||
|
BOOLEAN Error;
|
|||
|
|
|||
|
Message->DisplayMsg( MSG_CHK_NTFS_RESETTING_LOG_FILE );
|
|||
|
|
|||
|
if( !QueryAttribute( &DataAttribute, &Error, $DATA ) ||
|
|||
|
!DataAttribute.Fill( 0, LogFileFillCharacter ) ) {
|
|||
|
|
|||
|
Message->DisplayMsg( MSG_CHK_NO_MEMORY );
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
NTFS_LOG_FILE::Resize(
|
|||
|
IN BIG_INT NewSize,
|
|||
|
IN OUT PNTFS_BITMAP VolumeBitmap,
|
|||
|
IN BOOLEAN GetWhatYouCan,
|
|||
|
OUT PBOOLEAN Changed,
|
|||
|
OUT PBOOLEAN LogFileGrew,
|
|||
|
IN OUT PMESSAGE Message
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This method resizes an existing log file. It does not change
|
|||
|
the value of the remaining contents.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
NewSize -- Supplies the new size of the log file's data
|
|||
|
attribute, in bytes. Zero means resize to the
|
|||
|
default size.
|
|||
|
VolumeBitmap -- Supplies the bitmap for the volume on which
|
|||
|
the log file resides.
|
|||
|
GetWhatYouCan -- Supplies a flag that indicates the method
|
|||
|
should allocate as much of the requested
|
|||
|
space as possible; if this value is FALSE,
|
|||
|
this method will fail if it cannot make the
|
|||
|
log file the requested size.
|
|||
|
Changed -- Receives TRUE if the log file's size was changed
|
|||
|
by this operation.
|
|||
|
LogFileGrew -- Receives TRUE if the log file was made larger
|
|||
|
by this operation.
|
|||
|
Message -- Supplies an outlet for messages.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTFS_ATTRIBUTE DataAttribute;
|
|||
|
BIG_INT OldSize;
|
|||
|
BOOLEAN Error;
|
|||
|
|
|||
|
if (NewSize == 0) {
|
|||
|
|
|||
|
NewSize = QueryDefaultSize( GetDrive(), QueryVolumeSectors() );
|
|||
|
}
|
|||
|
|
|||
|
if (!QueryAttribute( &DataAttribute, &Error, $DATA )) {
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (NewSize == DataAttribute.QueryValueLength()) {
|
|||
|
|
|||
|
*Changed = FALSE;
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if (IsAttributeListPresent()) {
|
|||
|
Message->DisplayMsg( MSG_CHK_NTFS_RESIZING_LOG_FILE_FAILED_DUE_TO_ATTR_LIST_PRESENT );
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
Message->DisplayMsg( MSG_CHK_NTFS_RESIZING_LOG_FILE );
|
|||
|
|
|||
|
OldSize = DataAttribute.QueryValueLength();
|
|||
|
|
|||
|
*LogFileGrew = (NewSize > OldSize);
|
|||
|
|
|||
|
|
|||
|
#if 0 // fragment the disk for debugging purpose
|
|||
|
ULONG i;
|
|||
|
for (i = 0; i < 0x1f400*8; i += 2) {
|
|||
|
if (VolumeBitmap->IsFree(i, 1))
|
|||
|
VolumeBitmap->SetAllocated(i, 1);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
if( !DataAttribute.Resize( NewSize, VolumeBitmap ) ||
|
|||
|
!DataAttribute.Fill( OldSize, LogFileFillCharacter ) ||
|
|||
|
!DataAttribute.InsertIntoFile( this, VolumeBitmap ) ) {
|
|||
|
|
|||
|
*Changed = FALSE;
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (IsAttributeListPresent()) {
|
|||
|
|
|||
|
Message->DisplayMsg( MSG_CHK_NTFS_RESIZING_LOG_FILE_FAILED_DUE_TO_ATTR_LIST );
|
|||
|
|
|||
|
if (!DataAttribute.Resize( OldSize, VolumeBitmap ) ||
|
|||
|
!DataAttribute.InsertIntoFile( this, VolumeBitmap ) ||
|
|||
|
!PurgeAttributeList()) {
|
|||
|
|
|||
|
*Changed = FALSE;
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
*Changed = TRUE;
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
NTFS_LOG_FILE::VerifyAndFix(
|
|||
|
IN OUT PNTFS_BITMAP VolumeBitmap,
|
|||
|
IN OUT PNTFS_INDEX_TREE RootIndex,
|
|||
|
OUT PBOOLEAN Changes,
|
|||
|
IN OUT PNTFS_CHKDSK_REPORT ChkdskReport,
|
|||
|
IN FIX_LEVEL FixLevel,
|
|||
|
IN BOOLEAN Resize,
|
|||
|
IN ULONG LogFileSize,
|
|||
|
IN OUT PNUMBER_SET BadClusters,
|
|||
|
IN OUT PMESSAGE Message
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine ensures the validity of the log file; it should have
|
|||
|
a valid file name and standard information, and its size should be
|
|||
|
within reasonable limits.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
VolumeBitmap - Supplies the volume bitmap.
|
|||
|
RootIndex - Supplies the root index.
|
|||
|
Changes - Returns whether or not changes were made.
|
|||
|
ChkdskReport - Supplies the current chkdsk report.
|
|||
|
FixLevel - Supplies the fix up level.
|
|||
|
Resize - Supplies a flag indicating whether the log file
|
|||
|
should be resized.
|
|||
|
LogFileSize - If Resize is set, then LogFileSize supplies the
|
|||
|
new size of the logfile. If zero, the logfile will be
|
|||
|
resized to the default size.
|
|||
|
BadClusters - Supplies the current list of bad clusters.
|
|||
|
Message - Supplies an outlet for messages.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
FALSE - Failure.
|
|||
|
TRUE - Success.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTFS_ATTRIBUTE data_attribute;
|
|||
|
BIG_INT old_size;
|
|||
|
BOOLEAN error, has_external;
|
|||
|
ULONG default_size, max_size;
|
|||
|
BIG_INT freeSectorSize;
|
|||
|
BIG_INT bytes_recovered;
|
|||
|
BIG_INT bad_clusters_count;
|
|||
|
BOOLEAN bad_clusters_found;
|
|||
|
|
|||
|
// The logfile should not have an attribute list, and the data
|
|||
|
// attribute should be non-resident and of a reasonable size.
|
|||
|
//
|
|||
|
|
|||
|
error = *Changes = has_external = FALSE;
|
|||
|
|
|||
|
if (QueryAttribute(&data_attribute, &error, $DATA) &&
|
|||
|
!data_attribute.IsResident()) {
|
|||
|
|
|||
|
// If the log file has an attribute list, resize the
|
|||
|
// data attribute to zero and recreate it to force
|
|||
|
// it to be non-external.
|
|||
|
//
|
|||
|
|
|||
|
old_size = data_attribute.QueryValueLength();
|
|||
|
|
|||
|
default_size = QueryDefaultSize(GetDrive(), QueryVolumeSectors());
|
|||
|
max_size = QueryMaximumSize(GetDrive(), QueryVolumeSectors());
|
|||
|
|
|||
|
DebugAssert(data_attribute.QueryValueLength().GetHighPart() == 0);
|
|||
|
|
|||
|
if (Resize) {
|
|||
|
if (LogFileSize > max_size) {
|
|||
|
|
|||
|
Message->DisplayMsg( MSG_CHK_NTFS_SPECIFIED_LOGFILE_SIZE_TOO_BIG );
|
|||
|
ChkdskReport->BytesLogFile = old_size;
|
|||
|
return TRUE;
|
|||
|
|
|||
|
} else if (LogFileSize < MinimumLogFileSize) {
|
|||
|
|
|||
|
Message->DisplayMsg( MSG_CHK_NTFS_SPECIFIED_LOGFILE_SIZE_TOO_SMALL );
|
|||
|
ChkdskReport->BytesLogFile = old_size;
|
|||
|
return TRUE;
|
|||
|
|
|||
|
} else if (LogFileSize > old_size) {
|
|||
|
|
|||
|
freeSectorSize = VolumeBitmap->QueryFreeClusters() * QueryClusterFactor();
|
|||
|
if (((LogFileSize-old_size-1)/GetDrive()->QuerySectorSize()+1) > freeSectorSize) {
|
|||
|
Message->DisplayMsg(MSG_CHK_NTFS_OUT_OF_SPACE_FOR_SPECIFIED_LOGFILE_SIZE);
|
|||
|
ChkdskReport->BytesLogFile = old_size;
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
LogFileSize = default_size;
|
|||
|
if (old_size < MinimumLogFileSize) {
|
|||
|
|
|||
|
Resize = TRUE;
|
|||
|
freeSectorSize = VolumeBitmap->QueryFreeClusters() * QueryClusterFactor();
|
|||
|
if (((LogFileSize-old_size-1)/GetDrive()->QuerySectorSize()+1) > freeSectorSize) {
|
|||
|
Message->DisplayMsg(MSG_CHK_NTFS_OUT_OF_SPACE_TO_ENLARGE_LOGFILE_TO_DEFAULT_SIZE);
|
|||
|
ChkdskReport->BytesLogFile = old_size;
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
} else if (old_size > max_size) {
|
|||
|
|
|||
|
Resize = TRUE;
|
|||
|
Message->DisplayMsg(MSG_CHK_NTFS_LOGFILE_SIZE_TOO_BIG);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
bad_clusters_count = BadClusters->QueryCardinality();
|
|||
|
|
|||
|
if (!data_attribute.RecoverAttribute(VolumeBitmap,
|
|||
|
BadClusters,
|
|||
|
&bytes_recovered)) {
|
|||
|
Message->DisplayMsg( MSG_CHK_NO_MEMORY );
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
bad_clusters_found = (BadClusters->QueryCardinality() != bad_clusters_count);
|
|||
|
|
|||
|
if (IsAttributePresent($ATTRIBUTE_LIST, NULL, TRUE)) {
|
|||
|
|
|||
|
Message->DisplayMsg(MSG_CHK_NTFS_ATTR_LIST_IN_LOG_FILE);
|
|||
|
|
|||
|
*Changes = TRUE;
|
|||
|
|
|||
|
has_external = TRUE;
|
|||
|
|
|||
|
bad_clusters_found = FALSE;
|
|||
|
|
|||
|
if (FixLevel != CheckOnly &&
|
|||
|
(!data_attribute.Resize(0, VolumeBitmap) ||
|
|||
|
!data_attribute.InsertIntoFile(this, VolumeBitmap) ||
|
|||
|
!Flush(VolumeBitmap, RootIndex) ||
|
|||
|
!PurgeAttributeList() ||
|
|||
|
!Flush(VolumeBitmap, RootIndex))) {
|
|||
|
|
|||
|
// The log file is corrupt, and we can't fix it.
|
|||
|
//
|
|||
|
|
|||
|
Message->DisplayMsg(MSG_CHK_NTFS_CANT_FIX_LOG_FILE);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (has_external || Resize || bad_clusters_found) {
|
|||
|
|
|||
|
// The data attribute's size is out-of-bounds. Resize it to
|
|||
|
// the default size.
|
|||
|
//
|
|||
|
|
|||
|
*Changes = TRUE;
|
|||
|
|
|||
|
if (Resize) {
|
|||
|
Message->DisplayMsg(MSG_CHK_NTFS_RESIZING_LOG_FILE);
|
|||
|
}
|
|||
|
|
|||
|
if (bad_clusters_found) {
|
|||
|
Message->DisplayMsg(MSG_CHK_NTFS_BAD_CLUSTERS_IN_LOG_FILE);
|
|||
|
}
|
|||
|
|
|||
|
if (FixLevel != CheckOnly) {
|
|||
|
|
|||
|
#if 0 // fragment the disk for debugging purpose
|
|||
|
ULONG i;
|
|||
|
for (i = 0; i < 0x1f400*8; i += 2) {
|
|||
|
if (VolumeBitmap->IsFree(i, 1))
|
|||
|
VolumeBitmap->SetAllocated(i, 1);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
if (!data_attribute.Resize(LogFileSize, VolumeBitmap) ||
|
|||
|
!data_attribute.Fill(0, LogFileFillCharacter) ||
|
|||
|
!data_attribute.InsertIntoFile(this, VolumeBitmap) ||
|
|||
|
!Flush(VolumeBitmap, RootIndex)) {
|
|||
|
|
|||
|
if (has_external) {
|
|||
|
|
|||
|
// The log file is corrupt, and we can't fix it.
|
|||
|
//
|
|||
|
|
|||
|
Message->DisplayMsg(MSG_CHK_NTFS_CANT_FIX_LOG_FILE);
|
|||
|
return FALSE;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
// Print a warning message, but still return success.
|
|||
|
//
|
|||
|
|
|||
|
Message->DisplayMsg(MSG_CHK_NTFS_RESIZING_LOG_FILE_FAILED);
|
|||
|
|
|||
|
ChkdskReport->BytesLogFile = data_attribute.QueryValueLength();
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (IsAttributeListPresent()) {
|
|||
|
|
|||
|
if (old_size < MinimumLogFileSize)
|
|||
|
old_size = MinimumLogFileSize;
|
|||
|
|
|||
|
Message->DisplayMsg( MSG_CHK_NTFS_RESIZING_LOG_FILE_FAILED_DUE_TO_ATTR_LIST );
|
|||
|
|
|||
|
while (IsAttributeListPresent()) {
|
|||
|
|
|||
|
if (0 == old_size) {
|
|||
|
Message->DisplayMsg(MSG_CHK_NTFS_CANT_FIX_LOG_FILE);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (!data_attribute.Resize( 0, VolumeBitmap ) ||
|
|||
|
!data_attribute.InsertIntoFile( this, VolumeBitmap ) ||
|
|||
|
!Flush(VolumeBitmap) ||
|
|||
|
!PurgeAttributeList() ||
|
|||
|
!data_attribute.Resize( old_size, VolumeBitmap ) ||
|
|||
|
!data_attribute.Fill( old_size - 1, LogFileFillCharacter ) ||
|
|||
|
!data_attribute.InsertIntoFile( this, VolumeBitmap )) {
|
|||
|
|
|||
|
Message->DisplayMsg(MSG_CHK_NTFS_CANT_FIX_LOG_FILE);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
old_size = old_size / 2;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (!data_attribute.Fill(0, LogFileFillCharacter) ||
|
|||
|
!Flush(VolumeBitmap, RootIndex)) {
|
|||
|
|
|||
|
Message->DisplayMsg(MSG_CHK_NTFS_CANT_FIX_LOG_FILE);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ChkdskReport->BytesLogFile = data_attribute.QueryValueLength();
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if (error) {
|
|||
|
Message->DisplayMsg(MSG_CHK_NO_MEMORY);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
Message->LogMsg(MSG_CHKLOG_NTFS_MISSING_OR_RESIDENT_DATA_ATTR_IN_LOG_FILE);
|
|||
|
|
|||
|
*Changes = TRUE;
|
|||
|
|
|||
|
Message->DisplayMsg(MSG_CHK_NTFS_CORRECTING_LOG_FILE);
|
|||
|
|
|||
|
// Recreate the $DATA attribute.
|
|||
|
//
|
|||
|
|
|||
|
if (FixLevel != CheckOnly) {
|
|||
|
|
|||
|
// NTRAID#91401-2000/03/07 - danielch - Potential attribute list can be created here
|
|||
|
|
|||
|
if (!CreateDataAttribute(0, 0, VolumeBitmap) ||
|
|||
|
!Flush(VolumeBitmap, RootIndex)) {
|
|||
|
|
|||
|
Message->DisplayMsg(MSG_CHK_NTFS_CANT_FIX_LOG_FILE);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (QueryAttribute(&data_attribute, &error, $DATA)) {
|
|||
|
|
|||
|
ChkdskReport->BytesLogFile = data_attribute.QueryValueLength();
|
|||
|
} else {
|
|||
|
|
|||
|
ChkdskReport->BytesLogFile = 0;
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
ULONG
|
|||
|
NTFS_LOG_FILE::QueryDefaultSize(
|
|||
|
IN PCDP_DRIVE Drive,
|
|||
|
IN BIG_INT VolumeSectors
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This method returns the appropriate default log file size
|
|||
|
for the specified drive.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Drive - Supplies the drive under consideration.
|
|||
|
VolumeSectors - Supplies the number of volume sectors.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The appropriate default log file size for the drive.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
BIG_INT InitialSize, VolumeSize;
|
|||
|
ULONG FinalSize;
|
|||
|
|
|||
|
if (VolumeSectors.GetHighPart() != 0) {
|
|||
|
|
|||
|
FinalSize = MaximumInitialLogFileSize;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
VolumeSize = VolumeSectors * Drive->QuerySectorSize();
|
|||
|
|
|||
|
if (VolumeSize <= (400UL * 1024UL * 1024UL)) {
|
|||
|
|
|||
|
InitialSize = (VolumeSize / PrimaryLogFileGrowthRate);
|
|||
|
|
|||
|
if (InitialSize < MinimumLogFileSize)
|
|||
|
InitialSize = MinimumLogFileSize;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
VolumeSize = VolumeSize - MaximumVolumeSizeBeforeSlowingDownLogFileGrowthRate;
|
|||
|
|
|||
|
InitialSize = VolumeSize/SecondaryLogFileGrowthRate +
|
|||
|
MaximumVolumeSizeBeforeSlowingDownLogFileGrowthRate/
|
|||
|
PrimaryLogFileGrowthRate;
|
|||
|
|
|||
|
if (InitialSize > MaximumInitialLogFileSize)
|
|||
|
InitialSize = MaximumInitialLogFileSize;
|
|||
|
}
|
|||
|
|
|||
|
FinalSize = (InitialSize + LogFileAlignmentMask).GetLowPart() & ~LogFileAlignmentMask;
|
|||
|
}
|
|||
|
|
|||
|
return FinalSize;
|
|||
|
}
|
|||
|
|
|||
|
ULONG
|
|||
|
NTFS_LOG_FILE::QueryMinimumSize(
|
|||
|
IN PCDP_DRIVE Drive,
|
|||
|
IN BIG_INT VolumeSectors
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This method returns the minimum log file size
|
|||
|
for the specified drive.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Drive - Supplies the drive under consideration.
|
|||
|
VolumeSectors - Supplies the number of volume sectors.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The minimum log file size for the drive.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UNREFERENCED_PARAMETER(Drive);
|
|||
|
UNREFERENCED_PARAMETER(VolumeSectors);
|
|||
|
|
|||
|
return MinimumLogFileSize;
|
|||
|
}
|
|||
|
|
|||
|
ULONG
|
|||
|
NTFS_LOG_FILE::QueryMaximumSize(
|
|||
|
IN PCDP_DRIVE Drive,
|
|||
|
IN BIG_INT VolumeSectors
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This method returns the maximum log file size
|
|||
|
for the specified drive.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Drive - Supplies the drive under consideration.
|
|||
|
VolumeSectors - Supplies the number of volume sectors.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The maximum log file size for the drive.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UNREFERENCED_PARAMETER(Drive);
|
|||
|
UNREFERENCED_PARAMETER(VolumeSectors);
|
|||
|
|
|||
|
return MaximumLogFileSize;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
NTFS_LOG_FILE::EnsureCleanShutdown(
|
|||
|
OUT PLSN Lsn
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This method looks at the logfile to verify that the volume
|
|||
|
was shut down cleanly. If we can't read the logfile well
|
|||
|
enough to say for sure, we assume that it was not shut down
|
|||
|
cleanly.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Lsn - Retrieves the last lsn in the restart area
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE - The volume was shut down cleanly.
|
|||
|
FALSE - The volume was not shut down cleanly.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTFS_ATTRIBUTE attrib;
|
|||
|
BOOLEAN error;
|
|||
|
ULONG nbyte;
|
|||
|
PLFS_RESTART_PAGE_HEADER header;
|
|||
|
PLFS_RESTART_AREA restarea;
|
|||
|
PBYTE buf;
|
|||
|
BOOLEAN r = TRUE;
|
|||
|
|
|||
|
// Read the logfile contents to make sure the volume was shut
|
|||
|
// down cleanly. If it wasn't generate an error message for
|
|||
|
// the user and bail. Also generate an error if the logfile's
|
|||
|
// data isn't big enough to contain an indication of whether it
|
|||
|
// was cleanly shut down or not.
|
|||
|
//
|
|||
|
|
|||
|
if (!QueryAttribute(&attrib, &error, $DATA)) {
|
|||
|
|
|||
|
DebugPrintTrace(("UNTFS: Could not query logfile data\n"));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (attrib.QueryValueLength() < IFS_SYSTEM::QueryPageSize()) {
|
|||
|
|
|||
|
DebugPrintTrace(("UNTFS: LogFile too small to hold restart area\n"));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (NULL == (buf = NEW BYTE[IFS_SYSTEM::QueryPageSize()])) {
|
|||
|
|
|||
|
DebugPrintTrace(("UNTFS: Out of memory\n"));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (!attrib.Read(buf, 0, IFS_SYSTEM::QueryPageSize(), &nbyte) ||
|
|||
|
nbyte != IFS_SYSTEM::QueryPageSize()) {
|
|||
|
|
|||
|
DebugPrintTrace(("UNTFS: Unable to read logfile\n"));
|
|||
|
delete[] buf;
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
header = PLFS_RESTART_PAGE_HEADER(buf);
|
|||
|
|
|||
|
if (0xffff == header->RestartOffset) {
|
|||
|
|
|||
|
DebugPrintTrace(("UNTFS: Invalid restart offset\n"));
|
|||
|
delete[] buf;
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
restarea = PLFS_RESTART_AREA(buf + header->RestartOffset);
|
|||
|
|
|||
|
|
|||
|
*Lsn = restarea->CurrentLsn;
|
|||
|
// DebugPrintTrace(("UNTFS: Lsn value %I64x at offset %x\n", *Lsn, header->RestartOffset));
|
|||
|
|
|||
|
if (!(restarea->Flags & LFS_CLEAN_SHUTDOWN)) {
|
|||
|
DebugPrintTrace(("UNTFS: LFS_CLEAN_SHUTDOWN flag not on %x\n", restarea->Flags));
|
|||
|
// r = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
delete[] buf;
|
|||
|
|
|||
|
return r;
|
|||
|
}
|