windows-nt/Source/XPSP1/NT/base/fs/utils/dfrg/diskview.cpp

944 lines
26 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/**********************************************************************************
FILENAME: DiskView.cpp
CLASS: DISKVIEW
CLASS DESCRIPTION:
DiskView takes data from the engines about what each cluster of the disk is used for.
It creates an array in memory (ClusterArray) to hold this data and then creates a
similar array to hold the data that DiskDisplay will use (LineArray) to display a
graphical image on the screen. This means that DiskView must look through many
clusters in it's array an determine that the bulk of them are fragmented or not,
(or some special type of file), and set a byte to a specific color in the LineArray
(memory array for the display data -- 1 line = 1 block of clusters).
COPYRIGHT<EFBFBD> 2001 Microsoft Corporation and Executive Software International, Inc.
*/
#include "stdafx.h"
#ifndef SNAPIN
#include <windows.h>
#endif
extern "C" {
#include "SysStruc.h"
}
#include "ErrMacro.h"
#include "DfrgCmn.h"
#include "DfrgEngn.h"
#include "DfrgRes.h"
#include "DiskView.h"
#include "Graphix.h"
#include "IntFuncs.h"
#include "GetReg.h"
#ifdef _DEBUG
#define CLASSINVARIANT() EH_ASSERT(Invariant())
#else
#define CLASSINVARIANT()
#endif
// default: set line to frag color if 3% of clusters are fragged
#define FRAGTHRESHDEFAULT 3
// TEMP TEMP
#define ANALYZE_MEM_FAIL 0
#define DEFRAG_MEM_FAIL 0
/**********************************************************************************
Construction/Destruction
**********************************************************************************/
DiskView::DiskView()
{
::InitializeCriticalSection(&m_CriticalSection);
// data we need from UI
NumLines = 0;
ClusterFactor = 0;
pLineArray = NULL;
// control variables
m_bIsDataSent = FALSE;
m_IsActive = FALSE;
m_FragColorThreshold = FRAGTHRESHDEFAULT;
m_bMemAllocFailed = FALSE;
m_nFreeDelta = 0;
m_nFragDelta = 0;
m_nNonMovableDelta = 0;
m_nUsedDelta = 0;
// init basic variables.
MftZoneStart = 0;
MftZoneEnd = 0;
Clusters = 0;
pClusterArray = NULL;
InitFragColorThreshold();
CLASSINVARIANT();
}
DiskView::DiskView(const int InClusters,
const int InMftZoneStart = 0,
const int InMftZoneEnd = 0)
{
require(InClusters > 0);
::InitializeCriticalSection(&m_CriticalSection);
// data we need from UI
NumLines = 0;
ClusterFactor = 0;
pLineArray = NULL;
// control variables
m_bIsDataSent = FALSE;
m_IsActive = FALSE;
m_FragColorThreshold = FRAGTHRESHDEFAULT;
m_bMemAllocFailed = FALSE;
// init basic variables.
MftZoneStart = InMftZoneStart;
MftZoneEnd = InMftZoneEnd;
Clusters = InClusters;
// cluster map
ClusterArraySize = Clusters / 2 + Clusters % 2; // one cluster per nibble
#if ANALYZE_MEM_FAIL // TEMP TEMP
pClusterArray = new char [ClusterArraySize * 10000];
#else
pClusterArray = new char [ClusterArraySize];
#endif
EH(pClusterArray);
// fill the array initially with free space.
if (pClusterArray) {
FillMemory(pClusterArray, ClusterArraySize, (FreeSpaceColor << 4) | FreeSpaceColor);
}
else {
// new failed, keep class consistent
m_bMemAllocFailed = TRUE;
Clusters = 0;
ClusterArraySize = 0;
}
InitFragColorThreshold();
CLASSINVARIANT();
}
/*
METHOD: DiskView::InitFragColorThreshold
DESCRIPTION:
Get the threshold percentage of fragmented clusters that it takes to cause
a line to display in the fragmented color. If it is not in the registry at
all then we don't set it. Otherwise it will be defaulted by the constructor.
*/
void DiskView::InitFragColorThreshold()
{
// get frag color threshold percent from registry
TCHAR cRegValue[100];
HKEY hValue = NULL;
DWORD dwRegValueSize = sizeof(cRegValue);
// get the free space error threshold from the registry
long ret = GetRegValue(
&hValue,
TEXT("SOFTWARE\\Microsoft\\Dfrg"),
TEXT("FragColorThreshold"),
cRegValue,
&dwRegValueSize);
RegCloseKey(hValue);
// convert it and apply range limits
if (ret == ERROR_SUCCESS) {
m_FragColorThreshold = (UINT) _ttoi(cRegValue);
// > 50 does not make sense!
if (m_FragColorThreshold > 50)
m_FragColorThreshold = 50;
// < 0 does not either!
if (m_FragColorThreshold < 1)
m_FragColorThreshold = 0;
}
}
/*
METHOD: DiskView::DiskView (Copy Contructor)
DESCRIPTION:
Copies a DiskView class.
RETURN:
New Class reference.
*/
DiskView::DiskView(const DiskView& InDiskView)
{
EH_ASSERT(InDiskView.Invariant());
require(InDiskView.HasMapMemory());
::InitializeCriticalSection(&m_CriticalSection);
// copy over some basic variables from the input DiskView.
Clusters = InDiskView.Clusters;
ClusterArraySize = Clusters / 2 + Clusters % 2; // one cluster per nibble
NumLines = InDiskView.NumLines;
if (NumLines > 0)
ClusterFactor = (Clusters / NumLines) + ((Clusters % NumLines) ? 1 : 0);
else
ClusterFactor = 0;
EH_ASSERT(ClusterFactor == InDiskView.ClusterFactor);
MftZoneEnd = InDiskView.MftZoneEnd;
MftZoneStart = InDiskView.MftZoneStart;
m_bIsDataSent = FALSE;
m_bMemAllocFailed = FALSE;
m_IsActive = InDiskView.m_IsActive;
m_FragColorThreshold = InDiskView.m_FragColorThreshold;
// cluster map
#if DEFRAG_MEM_FAIL // TEMP TEMP
pClusterArray = new char [ClusterArraySize * 10000];
#else
pClusterArray = new char [ClusterArraySize];
#endif
EH(pClusterArray);
// copy over the cluster array.
if (pClusterArray) {
CopyMemory(pClusterArray, InDiskView.pClusterArray, ClusterArraySize);
}
else {
// new failed, keep class consistent
m_bMemAllocFailed = TRUE;
Clusters = 0;
ClusterArraySize = 0;
}
// line array
if (NumLines > 0) {
pLineArray = new char [NumLines];
EH(pLineArray);
if (pLineArray) {
FillMemory(pLineArray, NumLines, DirtyColor);
}
else {
// new failed, keep class consistent
m_bMemAllocFailed = TRUE;
NumLines = 0;
}
}
else {
pLineArray = NULL;
}
CLASSINVARIANT();
}
/*
METHOD: DiskView::operator= (Assignment Operator)
DESCRIPTION:
Makes the Lvalue a duplicate of the Rvalue.
*/
DiskView& DiskView::operator=(const DiskView& InDiskView)
{
// check for assignment to self
if (this == &InDiskView) {
return *this;
}
EH_ASSERT(InDiskView.Invariant());
require(InDiskView.HasMapMemory());
__try
{
::EnterCriticalSection(&m_CriticalSection);
// copy over some basic variables from the input DiskView.
MftZoneEnd = InDiskView.MftZoneEnd;
MftZoneStart = InDiskView.MftZoneStart;
m_bIsDataSent = FALSE;
m_bMemAllocFailed = FALSE;
m_IsActive = InDiskView.m_IsActive;
m_FragColorThreshold = InDiskView.m_FragColorThreshold;
// if the number of clusters in the cluster array are not identical, realloc the array.
if (Clusters != InDiskView.Clusters) {
// get the new size for the cluster array.
Clusters = InDiskView.Clusters;
ClusterArraySize = Clusters / 2 + Clusters % 2; // one cluster per nibble
// redimension the cluster array.
if (pClusterArray)
delete [] pClusterArray;
#if DEFRAG_MEM_FAIL // TEMP TEMP
pClusterArray = new char [ClusterArraySize * 10000];
#else
pClusterArray = new char [ClusterArraySize];
#endif
EH(pClusterArray);
}
// copy over the cluster array.
if (pClusterArray) {
CopyMemory(pClusterArray, InDiskView.pClusterArray, ClusterArraySize);
}
else {
// new failed, keep class consistent
m_bMemAllocFailed = TRUE;
Clusters = 0;
ClusterArraySize = 0;
}
// line array
NumLines = InDiskView.NumLines;
if (NumLines > 0)
ClusterFactor = (Clusters / NumLines) + ((Clusters % NumLines) ? 1 : 0);
else
ClusterFactor = 0;
if (NumLines > 0) {
pLineArray = new char [NumLines];
EH(pLineArray);
if (pLineArray) {
FillMemory(pLineArray, NumLines, DirtyColor);
}
else {
// new failed, keep class consistent
m_bMemAllocFailed = TRUE;
NumLines = 0;
}
}
else {
pLineArray = NULL;
}
CLASSINVARIANT();
}
__finally
{
::LeaveCriticalSection(&m_CriticalSection);
}
return *this;
}
/*
METHOD: DiskView::~DiskView (Destructor)
*/
DiskView::~DiskView()
{
CLASSINVARIANT();
::DeleteCriticalSection(&m_CriticalSection);
if (pClusterArray)
delete [] pClusterArray;
if (pLineArray)
delete [] pLineArray;
}
/**********************************************************************************
Implementation
**********************************************************************************/
void DiskView::SetClusterCount(const int InClusters)
{
require(InClusters > 0);
__try
{
::EnterCriticalSection(&m_CriticalSection);
m_IsActive = TRUE;
Clusters = InClusters;
// cluster map
ClusterArraySize = Clusters / 2 + Clusters % 2; // one cluster per nibble
#if ANALYZE_MEM_FAIL // TEMP TEMP
pClusterArray = new char [ClusterArraySize * 10000];
#else
pClusterArray = new char [ClusterArraySize];
#endif
EH(pClusterArray);
// fill the array initially with free space.
if (pClusterArray) {
FillMemory(pClusterArray, ClusterArraySize, (FreeSpaceColor << 4) | FreeSpaceColor);
}
else {
// new failed, keep class consistent
m_bMemAllocFailed = TRUE;
Clusters = 0;
ClusterArraySize = 0;
}
CLASSINVARIANT();
}
__finally
{
::LeaveCriticalSection(&m_CriticalSection);
}
}
void DiskView::SetMftZone(const int InZoneStart, const int InZoneEnd)
{
require(InZoneEnd >= InZoneStart);
__try
{
::EnterCriticalSection(&m_CriticalSection);
MftZoneStart = InZoneStart;
MftZoneEnd = InZoneEnd;
CLASSINVARIANT();
}
__finally
{
::LeaveCriticalSection(&m_CriticalSection);
}
}
/*
METHOD: DiskView::SetNumLines
DESCRIPTION:
Called to update the number of lines (pixels) that are displayed on the UI
*/
void DiskView::SetNumLines(const int inNumLines)
{
if (NumLines != inNumLines && inNumLines > 0 && pClusterArray != NULL) {
__try
{
::EnterCriticalSection(&m_CriticalSection);
CLASSINVARIANT();
NumLines = inNumLines;
ClusterFactor = (Clusters / NumLines) + ((Clusters % NumLines) ? 1 : 0);
EH(ClusterFactor > 0);
if (pLineArray)
delete [] pLineArray;
pLineArray = new char [NumLines];
EH(pLineArray);
if (pLineArray) {
FillMemory(pLineArray, NumLines, DirtyColor);
}
else {
// new failed, keep class consistent
m_bMemAllocFailed = TRUE;
NumLines = 0;
}
CLASSINVARIANT();
}
__finally
{
::LeaveCriticalSection(&m_CriticalSection);
}
}
}
/*
METHOD: DiskView::GetLineArray
DESCRIPTION:
Makes LineArray available outside of the class so it can be passed to DiskDisplay.
*/
BOOL DiskView::GetLineArray(char ** ppOutLineArray, DWORD *pNumLines)
{
BOOL bReturn = TRUE;
// initialize outputs
*ppOutLineArray = NULL;
*pNumLines = 0;
if (!m_IsActive || pClusterArray == NULL){
bReturn = FALSE;
} else
{
// see if we have a line array
if (pLineArray != NULL && NumLines > 0) {
__try
{
::EnterCriticalSection(&m_CriticalSection);
CLASSINVARIANT();
// update the line array from the cluster array
TransferToLineArray();
// allocate space for a copy of the line array
*ppOutLineArray = new char[NumLines];
// if we got memory, set the outputs
if (*ppOutLineArray) {
CopyMemory(*ppOutLineArray, pLineArray, NumLines);
*pNumLines = (DWORD) NumLines;
}
m_bIsDataSent = TRUE;
}
__finally
{
::LeaveCriticalSection(&m_CriticalSection);
bReturn = TRUE;
}
}
else {
bReturn = FALSE;
}
}
return bReturn;
}
/*
METHOD: DiskView::UpdateClusterArray
DESCRIPTION:
Updates portions of the cluster array from extent data about the disk. (An extent
is a series of adjacent clusters that are part of the same file -- or sometimes in
our program, part of no file.) The extents are passed in as a series of EXTENT_LIST
structures with a LONGLONG header. The first 7 bytes of the LONGLONG contain the
number of EXTENT_LIST structures following, the last byte of the LONGLONG contains
the color code of the extents (meaning a series of extents all represent the same
type of data: contiguous files, fragmented files, etc.) After the last EXTENT_LIST
structure, there may be another LONGLONG identifying that there is another series of
EXTENT_LIST's. Eventually there will be a zeroed out LONGLONG indicating there is
no more data. UpdateClusterArray goes through each of these extents and updates the
appropriate clusters in ClusterArray. It then re-evaluates those specific lines in
the LineArray that would be affected by the changes in ClusterArray.
INPUT + OUTPUT:
pBuffer - The buffer holding the EXTENT_LIST's and their LONGLONG headers.
*/
void DiskView::UpdateClusterArray(PBYTE pBuffer)
{
LONGLONG StartingLcn;
BYTE Color;
int ClusterCount;
EXTENT_LIST* pUpdateArray;
LONGLONG Extents = 0;
LONGLONG Extent = 0;
int TotalRunLength = 0;
BYTE * pTempBuffer = pBuffer;
if (!m_IsActive || pClusterArray == NULL){
return;
}
__try
{
::EnterCriticalSection(&m_CriticalSection);
CLASSINVARIANT();
//Indefinite length loop that goes through all the series of EXTENT_LIST's
//until there are no more.
while(TRUE){
//Get the number of extents.
Extents = *(LONGLONG*)pBuffer & 0x0FFFFFFFFFFF;
if(Extents == 0){
break;
}
//Point to the EXTENT_LIST's.
pBuffer += sizeof(LONGLONG);
//Get the color code.
Color = pBuffer[-1];
//Get a movable pointer to the EXTENT_LIST's
pUpdateArray = (EXTENT_LIST*)pBuffer;
//Set the high nibble to have the same color code as the low nibble of color.
//Color = (Low nibble) | (High nibble which is low nibble shifted)
Color = (Color & 0x0F) | (Color << 4);
//Go through each extent and
//update the ClusterArray and LineArray for that extent.
for(Extent = 0; Extent < Extents; pUpdateArray ++, Extent ++) {
//Get the data for this extent and validate it is correct
//(doesn't run of the end of the disk)
StartingLcn = pUpdateArray->StartingLcn;
ClusterCount = (UINT)pUpdateArray->ClusterCount;
if ((StartingLcn < 0) || (ClusterCount <= 0) ||
(StartingLcn + ClusterCount > Clusters)) {
__leave;
}
if (pLineArray != NULL && NumLines > 0) {
// mark the affected lines in the line array dirty
UINT nStartLine = (UINT) (StartingLcn / ClusterFactor);
UINT nEndLine = (UINT) ((StartingLcn + ClusterCount - 1) / ClusterFactor);
for (UINT ii = nStartLine; ii <= nEndLine; ii++) {
//sks bug#206244
if(ii >= (UINT) NumLines)
{
__leave;
}
pLineArray[ii] = DirtyColor;
}
}
//Set the ClusterArray nibbles to the appropriate color for this extent.
//If the extent starts on an odd number,
//then the first cluster is a high nibble of one byte.
if(StartingLcn % 2){
//Clear out that high nibble.
pClusterArray[StartingLcn / 2] &= 0x0F;
//Put our color in it.
pClusterArray[StartingLcn / 2] |= (Color & 0xF0);
//Change the counters to reflect that we wrote this cluster.
ClusterCount --;
StartingLcn ++;
}
//Fill in the middle range that consists of whole bytes
//(high and low nibbles) needing to be set.
if(ClusterCount != 0){
FillMemory(pClusterArray + (StartingLcn / 2), ClusterCount / 2, Color);
}
//If the extent ends on an odd number,
//then the last cluster is a low nibble of one byte.
if ((ClusterCount % 2) && (ClusterCount != 0)) {
//Update counter to point to it after previous writes.
StartingLcn += ClusterCount - 1;
//Clear out that low nibble.
pClusterArray[StartingLcn / 2] &= 0xF0;
//Put our color in it.
pClusterArray[StartingLcn / 2] |= (Color & 0x0F);
}
}
//Point to the next series of extents (since the for loop we just exited exits
//when all the extents in that series have been done).
pBuffer = (PBYTE)pUpdateArray;
}
}
__finally
{
::LeaveCriticalSection(&m_CriticalSection);
}
}
/*
METHOD: DiskView::TransferToLineArray
DESCRIPTION:
Create the compressed line array from the current data in the cluster array.
RETURN:
TRUE=ok, FALSE=error.
*/
BOOL DiskView::TransferToLineArray()
{
// make sure we have the arrays
if (!m_IsActive || pClusterArray == NULL || pLineArray == NULL) {
return FALSE;
}
EF_ASSERT(ClusterFactor > 0);
EF_ASSERT(NumLines > 0);
__try
{
::EnterCriticalSection(&m_CriticalSection);
CLASSINVARIANT();
// loop variables
int nClust; // nClust = cluster number
int nClustByte; // nClustByte = byte in cluster array
// (nClust / 2, bytes to nibbles)
BOOL bHighNibble; // bHighNibble = TRUE for high nibble and FALSE for low nibble
// (stored one cluster per nibble)
int nLine; // nLine = line number
char cClustColor; // current cluster's color
UINT nUsed; // number of used clusters in this line
UINT nFree; // number of free clusters in this line
UINT nFrag; // fragmented cluster(s) in this line
UINT nNonMovable; // number of non movable clusters in this line
BOOL bNonMovable; // system file cluster(s) in this line
BOOL bMftFragmented; // MFT in this line sks added MFT defrag
BOOL bMftUnFragmented; // MFT in this line sks added MFT defrag
// loop through line array
for (nLine = 0; nLine < NumLines; nLine++) {
// if this item is not dirty, move on
if (pLineArray[nLine] != DirtyColor) {
continue;
}
// initialize line variables
nUsed = 0;
nFree = 0;
nFrag = 0;
nNonMovable = 0; //bug#184739 sks 9/18/2000
bNonMovable = FALSE;
bMftFragmented = FALSE;
bMftUnFragmented = FALSE;
// reset to the starting point for this line
nClust = nLine * ClusterFactor;
// loop through the clusters in this line
for (int ii = 0; ii < ClusterFactor && nClust < Clusters; ii++, nClust++) {
// set loop variables
nClustByte = nClust / 2; // cluster array byte index
bHighNibble = (nClust % 2) ? TRUE : FALSE; // which nibble in byte?
// get the current cluster's color
// (the >> 4 extracts the high nibble, the & 0x0F extracts the low nibble)
if(bHighNibble) {
cClustColor = pClusterArray[nClustByte] >> 4;
}
else {
cClustColor = pClusterArray[nClustByte] & 0x0F;
}
// evaluate the color
switch (cClustColor) {
case SystemFileColor:
case PageFileColor:
nNonMovable++; //bug#184739 sks 9/18/2000
break;
case FragmentColor:
nFrag++;
break;
case UsedSpaceColor:
case DirectoryColor:
nUsed++;
break;
case FreeSpaceColor:
nFree++;
break;
default:
__leave;
break;
}
//bug#184739 sks 9/18/2000
// system file overrides all, break out of inner loop
} // end inner loop: clusters in current line
//
// #243245 changing the color scheme as follows
//
// Add the current file counts for each type to our running
// total. Think of this running total as "the file-counts of each
// type that were ignored in the previous lines displayed."
//
// For each line, then test which running total is more:
// contiguous, unmovable, fragmented files or free space.
//
// Whoever is greater wins, (if all are equal, fragmented wins)
// Subtract the ClusterFactor from the running count for the
// winner (since this line is his)
//
m_nFreeDelta += nFree;
m_nFragDelta += nFrag;
m_nNonMovableDelta += nNonMovable;
m_nUsedDelta += nUsed;
if((m_nFragDelta > m_nNonMovableDelta) &&
(m_nFragDelta > m_nFreeDelta) &&
(m_nFragDelta > m_nUsedDelta)
) {
//
// Fragmented is greatest
//
m_nFragDelta -= ClusterFactor;
pLineArray[nLine] = FragmentColor;
}
else if ((m_nUsedDelta > m_nFreeDelta) &&
(m_nUsedDelta > m_nNonMovableDelta) &&
(m_nUsedDelta > m_nFragDelta)
) {
//
// Used is greatest
//
m_nUsedDelta -= ClusterFactor;
pLineArray[nLine] = UsedSpaceColor;
}
else if ((m_nFreeDelta > m_nUsedDelta) &&
(m_nFreeDelta > m_nNonMovableDelta) &&
(m_nFreeDelta > m_nFragDelta)
) {
//
// Free is greatest
//
m_nFreeDelta -= ClusterFactor;
pLineArray[nLine] = FreeSpaceColor;
}
else if ((m_nNonMovableDelta > m_nUsedDelta) &&
(m_nNonMovableDelta > m_nFreeDelta) &&
(m_nNonMovableDelta > m_nFragDelta)
) {
//
// Nonmovable is greatest
//
m_nNonMovableDelta -= ClusterFactor;
pLineArray[nLine] = SystemFileColor;
}
else {
//
// They're all equal--use Fragmented colour
//
m_nFragDelta -= ClusterFactor;
pLineArray[nLine] = FragmentColor;
}
} // end outer loop: lines
}
__finally
{
::LeaveCriticalSection(&m_CriticalSection);
}
return TRUE;
}
/*
METHOD: DiskView::Invariant
DESCRIPTION:
Check the internal state of a DiskView instance.
*/
BOOL DiskView::Invariant() const
{
// clusters
EF((pClusterArray == NULL && Clusters == 0) ||
(pClusterArray != NULL && Clusters > 0));
EF(ClusterArraySize == Clusters / 2 + Clusters % 2); // one cluster per nibble
// lines
EF((pLineArray == NULL && NumLines == 0) ||
(pLineArray != NULL && NumLines > 0));
EF((NumLines == 0 && ClusterFactor == 0) ||
(NumLines > 0 && ClusterFactor > 0));
// MFT zone
EF(MftZoneEnd >= MftZoneStart);
return TRUE;
}
/*
METHOD: DiskView::HasMapMemory
DESCRIPTION:
Check if the DiskView has memory for the cluster array
(and line array if applicable).
*/
BOOL DiskView::HasMapMemory() const
{
return !m_bMemAllocFailed;
}
/*
METHOD: DiskView::EnterMyCriticalSection()
DESCRIPTION:
Enter the critical section, so that when we terminate thread was have the critical section
bug #26213 sks .
*/
void DiskView::EnterMyCriticalSection()
{
::EnterCriticalSection(&m_CriticalSection);
}
/*
METHOD: DiskView::LeaveMyCriticalSection()
DESCRIPTION:
Leave the critical section, so that when we terminate thread was have the critical section
bug #26213 sks .
*/
void DiskView::LeaveMyCriticalSection()
{
::LeaveCriticalSection(&m_CriticalSection);
}