windows-nt/Source/XPSP1/NT/base/fs/utils/untfs/src/logfile.cxx
2020-09-26 16:20:57 +08:00

1042 lines
27 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

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

/*++
Copyright (c) 1991-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;
}