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

2515 lines
76 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*****************************************************************************************************************
FILENAME: FreeSpac.cpp
COPYRIGHT<EFBFBD> 2001 Microsoft Corporation and Executive Software International, Inc.
DESCRIPTION:
Contains routines for finding free clusters using the volume's bitmap.
*/
#include "stdafx.h"
#ifdef OFFLINEDK
extern "C"{
#include <stdio.h>
}
#endif
#ifdef BOOTIME
#include "Offline.h"
#else
#include "Windows.h"
#endif
#include <winioctl.h>
#include <math.h>
extern "C" {
#include "SysStruc.h"
}
#include "ErrMacro.h"
#include "DfrgCmn.h"
#include "DfrgEngn.h"
#include "DfrgRes.h"
#include "GetDfrgRes.h"
#include "GetReg.h"
#include "Devio.h"
#include "FreeSpace.h"
#include "Alloc.h"
#include "Message.h"
#define THIS_MODULE 'F'
#include "logfile.h"
// macros to speed up LONGLONG math (multiplication and division)
// based on profiling data by Mark Patton
// set OPTLONGLONGMATH as follows:
// 1=optimize with shift and &
// 0=use / and %
#define OPTLONGLONGMATH 1
#if OPTLONGLONGMATH
#define DIVIDELONGLONGBY32(num) Int64ShraMod32((num), 5)
#define MODULUSLONGLONGBY32(num) ((num) & 0x1F)
#else
#define DIVIDELONGLONGBY32(num) ((num) / 32)
#define MODULUSLONGLONGBY32(num) ((num) % 32)
#endif
//
// This structure is the header for a generic table entry.
// Align this structure on a 8 byte boundary so the user
// data is correctly aligned.
//
typedef struct _TABLE_ENTRY_HEADER {
RTL_BALANCED_LINKS BalancedLinks;
LONGLONG UserData;
} TABLE_ENTRY_HEADER, *PTABLE_ENTRY_HEADER;
PVOID
RealPredecessor (
IN PRTL_BALANCED_LINKS Links
)
/*++
Routine Description:
The RealPredecessor function takes as input a pointer to a balanced link
in a tree and returns a pointer to the predecessor of the input node
within the entire tree. If there is not a predecessor, the return value
is NULL.
Arguments:
Links - Supplies a pointer to a balanced link in a tree.
Return Value:
PRTL_BALANCED_LINKS - returns a pointer to the predecessor in the entire tree
--*/
{
PRTL_BALANCED_LINKS Ptr;
/*
first check to see if there is a left subtree to the input link
if there is then the real predecessor is the right most node in
the left subtree. That is find and return P in the following diagram
Links
/
.
.
.
P
/
*/
if ((Ptr = Links->LeftChild) != NULL) {
while (Ptr->RightChild != NULL) {
Ptr = Ptr->RightChild;
}
return ((PVOID)&((PTABLE_ENTRY_HEADER)Ptr)->UserData);
}
/*
we do not have a left child so check to see if have a parent and if
so find the first ancestor that we are a right decendent of. That
is find and return P in the following diagram
P
\
.
.
.
Links
Note that this code depends on how the BalancedRoot is initialized, which is
Parent points to self, and the RightChild points to an actual node which is
the root of the tree.
*/
Ptr = Links;
while (RtlIsLeftChild(Ptr)) {
Ptr = Ptr->Parent;
}
if (RtlIsRightChild(Ptr) && (Ptr->Parent->Parent != Ptr->Parent)) {
return ((PVOID)&((PTABLE_ENTRY_HEADER)(Ptr->Parent))->UserData);
}
//
// otherwise we are do not have a real predecessor so we simply return
// NULL
//
return NULL;
}
PVOID
PreviousEntry(
IN PVOID pCurrentEntry
)
{
if (!pCurrentEntry) {
return NULL;
}
PTABLE_ENTRY_HEADER q = (PTABLE_ENTRY_HEADER) CONTAINING_RECORD(
pCurrentEntry,
TABLE_ENTRY_HEADER,
UserData);
return RealPredecessor(&(q->BalancedLinks));
}
PVOID
LastEntry(
IN PRTL_AVL_TABLE Table
)
{
if (!Table) {
return NULL;
}
PRTL_BALANCED_LINKS NodeToExamine = Table->BalancedRoot.RightChild;
PRTL_BALANCED_LINKS Child;
while (Child = NodeToExamine->RightChild) {
NodeToExamine = Child;
}
if (NodeToExamine) {
return ((PVOID)&((PTABLE_ENTRY_HEADER)NodeToExamine)->UserData);
}
else {
return NULL;
}
}
#if 0
void
DumpFreeSpace(
IN PRTL_GENERIC_TABLE pTable
)
{
PFREE_SPACE_ENTRY pFreeSpaceEntry;
BOOLEAN bNext = FALSE;
WCHAR szTemp[256];
PVOID pRestartKey = NULL;
ULONG ulDeleteCount = 0;
int iCount = 0;
FREE_SPACE_ENTRY entry;
ZeroMemory(&entry, sizeof(FREE_SPACE_ENTRY));
do {
pFreeSpaceEntry = (PFREE_SPACE_ENTRY) RtlEnumerateGenericTableLikeADirectory(
pTable,
NULL,
NULL,
bNext,
&pRestartKey,
&ulDeleteCount,
&entry);
bNext = TRUE;
if (!pFreeSpaceEntry) {
// No free space left
OutputDebugString(L"No free space left\n\n");
break;
}
wsprintf(szTemp, L">> Free space \t Start:%I64u, \t ClusterCount:%I64u\n",
pFreeSpaceEntry->StartingLcn,
pFreeSpaceEntry->ClusterCount);
OutputDebugString(szTemp);
} while ((pFreeSpaceEntry) && (++iCount < 20));
}
#endif
BOOL
BuildFreeSpaceList(
IN OUT PRTL_GENERIC_TABLE pFreeSpaceTable,
IN CONST LONGLONG MinClusterCount,
IN CONST BOOL bSortBySize,
OUT LONGLONG *pBiggestFreeSpaceClusterCount,
OUT LONGLONG *pBiggestFreeSpaceStartingLcn,
IN CONST BOOL bIgnoreMftZone
)
{
LONGLONG Lcn = 0,
StartingLcn = 0,
EndingLcn = 0,
ClusterCount = 0;
FREE_SPACE_ENTRY FreeSpaceEntry;
PVOID p = NULL;
BOOL bResult = TRUE;
BOOLEAN bNewElement = FALSE;
PULONG pBitmap = NULL;
GetVolumeBitmap();
//0.0E00 Get a pointer to the free space bitmap
PVOLUME_BITMAP_BUFFER pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
if (!pVolumeBitmap){
Trace(warn, "BuildFreeSpaceList. Unable to get VolumeBitmap");
return FALSE; // no need to Unlock--the lock failed
}
pBitmap = (PULONG)&pVolumeBitmap->Buffer;
//0.0E00 Scan thru the entire bitmap looking for free space extents
for(Lcn = 0; Lcn < VolData.TotalClusters; ) {
//0.0E00 Find the next free space extent
FindFreeExtent(pBitmap, VolData.TotalClusters, &Lcn, &StartingLcn, &ClusterCount);
if (0 == ClusterCount) {
// No more free spaces, we're done
break;
}
if (ClusterCount < MinClusterCount) {
continue;
}
EndingLcn = StartingLcn + ClusterCount;
//0.0E00 If NTFS clip the free space extent to exclude the MFT zone
if(VolData.FileSystem == FS_NTFS) {
if (!bIgnoreMftZone) {
if((StartingLcn < VolData.MftZoneEnd) &&
(EndingLcn > VolData.MftZoneStart)) {
if(StartingLcn < VolData.MftZoneStart) {
EndingLcn = VolData.MftZoneStart;
}
else if(EndingLcn <= VolData.MftZoneEnd) {
continue; // this zone is fully within the MFT zone
}
else {
//0.0E00 Handle the case of EndingLcn > pVolData->MftZoneEnd.
StartingLcn = VolData.MftZoneEnd;
}
}
}
if((StartingLcn < VolData.BootOptimizeEndClusterExclude) &&
(EndingLcn > VolData.BootOptimizeBeginClusterExclude)) {
if(StartingLcn < VolData.BootOptimizeBeginClusterExclude) {
EndingLcn = VolData.BootOptimizeBeginClusterExclude;
}
else if(EndingLcn <= VolData.BootOptimizeEndClusterExclude) {
continue; // this zone is fully within the boot-opt zone
}
else {
//0.0E00 Handle the case of EndingLcn > pVolData->bootoptZoneEnd.
StartingLcn = VolData.BootOptimizeEndClusterExclude;
}
}
}
FreeSpaceEntry.StartingLcn = StartingLcn;
FreeSpaceEntry.ClusterCount = EndingLcn - StartingLcn;
FreeSpaceEntry.SortBySize = bSortBySize;
if (pBiggestFreeSpaceClusterCount) {
if (FreeSpaceEntry.ClusterCount > *pBiggestFreeSpaceClusterCount) {
*pBiggestFreeSpaceClusterCount = FreeSpaceEntry.ClusterCount;
if (pBiggestFreeSpaceStartingLcn) {
*pBiggestFreeSpaceStartingLcn = StartingLcn;
}
}
}
p = RtlInsertElementGenericTable(
pFreeSpaceTable,
(PVOID) &FreeSpaceEntry,
sizeof(FREE_SPACE_ENTRY),
&bNewElement);
if (!p) {
//
// An allocation failed
//
bResult = FALSE;
break;
};
}
if (VolData.hVolumeBitmap) {
GlobalUnlock(VolData.hVolumeBitmap);
}
//DumpFreeSpace(pFreeSpaceTable);
return bResult;
}
BOOL
BuildFreeSpaceListWithExclude(
IN OUT PRTL_GENERIC_TABLE pFreeSpaceTable,
IN CONST LONGLONG MinClusterCount,
IN CONST LONGLONG ExcludeZoneStart,
IN CONST LONGLONG ExcludeZoneEnd,
IN CONST BOOL bSortBySize,
IN CONST BOOL bExcludeMftZone
)
{
LONGLONG Lcn = 0,
StartingLcn = 0,
EndingLcn = 0,
ClusterCount = 0;
FREE_SPACE_ENTRY FreeSpaceEntry;
PVOID p = NULL;
BOOL bResult = TRUE;
BOOLEAN bNewElement = FALSE;
PULONG pBitmap = NULL;
GetVolumeBitmap();
//0.0E00 Get a pointer to the free space bitmap
PVOLUME_BITMAP_BUFFER pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
if (!pVolumeBitmap){
Trace(warn, "BuildFreeSpaceList. Unable to get VolumeBitmap");
return FALSE; // no need to Unlock--the lock failed
}
pBitmap = (PULONG)&pVolumeBitmap->Buffer;
//0.0E00 Scan thru the entire bitmap looking for free space extents
for(Lcn = 0; Lcn < VolData.TotalClusters; ) {
//0.0E00 Find the next free space extent
FindFreeExtent(pBitmap, VolData.TotalClusters, &Lcn, &StartingLcn, &ClusterCount);
if (0 == ClusterCount) {
// No more free spaces, we're done
break;
}
if (ClusterCount < MinClusterCount) {
continue;
}
EndingLcn = StartingLcn + ClusterCount;
//0.0E00 If NTFS clip the free space extent to exclude the MFT zone
if ((bExcludeMftZone) && (VolData.FileSystem == FS_NTFS)) {
if((StartingLcn < VolData.MftZoneEnd) &&
(EndingLcn > VolData.MftZoneStart)) {
if(StartingLcn < VolData.MftZoneStart) {
EndingLcn = VolData.MftZoneStart;
}
else if(EndingLcn <= VolData.MftZoneEnd) {
continue; // this zone is fully within the MFT zone
}
else {
//0.0E00 Handle the case of EndingLcn > pVolData->MftZoneEnd.
StartingLcn = VolData.MftZoneEnd;
}
}
if((StartingLcn < VolData.BootOptimizeEndClusterExclude) &&
(EndingLcn > VolData.BootOptimizeBeginClusterExclude)) {
if(StartingLcn < VolData.BootOptimizeBeginClusterExclude) {
EndingLcn = VolData.BootOptimizeBeginClusterExclude;
}
else if(EndingLcn <= VolData.BootOptimizeEndClusterExclude) {
continue; // this zone is fully within the boot-opt zone
}
else {
//0.0E00 Handle the case of EndingLcn > pVolData->bootoptZoneEnd.
StartingLcn = VolData.BootOptimizeEndClusterExclude;
}
}
}
if((StartingLcn < ExcludeZoneEnd) &&
(EndingLcn > ExcludeZoneStart)) {
if(StartingLcn < ExcludeZoneStart) {
EndingLcn = ExcludeZoneStart;
}
else if(EndingLcn <= ExcludeZoneEnd) {
continue; // this zone is fully within the MFT zone
}
else {
//0.0E00 Handle the case of EndingLcn > pVolData->MftZoneEnd.
StartingLcn = ExcludeZoneEnd;
}
}
FreeSpaceEntry.StartingLcn = StartingLcn;
FreeSpaceEntry.ClusterCount = EndingLcn - StartingLcn;
FreeSpaceEntry.SortBySize = bSortBySize;
p = RtlInsertElementGenericTable(
pFreeSpaceTable,
(PVOID) &FreeSpaceEntry,
sizeof(FREE_SPACE_ENTRY),
&bNewElement);
if (!p) {
//
// An allocation failed
//
bResult = FALSE;
break;
};
}
if (VolData.hVolumeBitmap) {
GlobalUnlock(VolData.hVolumeBitmap);
}
return bResult;
}
BOOL
BuildFreeSpaceListWithMultipleTrees(
OUT LONGLONG *pBiggestFreeSpaceClusterCount,
IN CONST LONGLONG IncludeZoneStartingLcn,
IN CONST LONGLONG IncludeZoneEndingLcn
)
{
LONGLONG Lcn = 0,
StartingLcn = 0,
EndingLcn = 0,
ClusterCount = 0;
FREE_SPACE_ENTRY FreeSpaceEntry;
PRTL_GENERIC_TABLE pFreeSpaceTable = NULL;
PVOID p = NULL;
BOOL bResult = TRUE;
BOOLEAN bNewElement = FALSE;
PULONG pBitmap = NULL;
DWORD dwTableIndex = 0;
GetVolumeBitmap();
//0.0E00 Get a pointer to the free space bitmap
PVOLUME_BITMAP_BUFFER pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
if (!pVolumeBitmap){
Trace(warn, "BuildFreeSpaceList. Unable to get VolumeBitmap");
return FALSE; // no need to Unlock--the lock failed
}
pBitmap = (PULONG)&pVolumeBitmap->Buffer;
//0.0E00 Scan thru the entire bitmap looking for free space extents
for(Lcn = 0; Lcn < VolData.TotalClusters; ) {
//0.0E00 Find the next free space extent
FindFreeExtent(pBitmap, VolData.TotalClusters, &Lcn, &StartingLcn, &ClusterCount);
if (0 == ClusterCount) {
// No more free spaces, we're done
break;
}
EndingLcn = StartingLcn + ClusterCount;
//0.0E00 If NTFS clip the free space extent to exclude the MFT zone
if ((VolData.FileSystem == FS_NTFS) && (0 == IncludeZoneEndingLcn)) {
if((StartingLcn < VolData.MftZoneEnd) &&
(EndingLcn > VolData.MftZoneStart)) {
if(StartingLcn < VolData.MftZoneStart) {
EndingLcn = VolData.MftZoneStart;
}
else if(EndingLcn <= VolData.MftZoneEnd) {
continue; // this zone is fully within the MFT zone
}
else {
//0.0E00 Handle the case of EndingLcn > pVolData->MftZoneEnd.
StartingLcn = VolData.MftZoneEnd;
}
}
if((StartingLcn < VolData.BootOptimizeEndClusterExclude) &&
(EndingLcn > VolData.BootOptimizeBeginClusterExclude)) {
if(StartingLcn < VolData.BootOptimizeBeginClusterExclude) {
EndingLcn = VolData.BootOptimizeBeginClusterExclude;
}
else if(EndingLcn <= VolData.BootOptimizeEndClusterExclude) {
continue; // this zone is fully within the boot-opt zone
}
else {
//0.0E00 Handle the case of EndingLcn > pVolData->bootoptZoneEnd.
StartingLcn = VolData.BootOptimizeEndClusterExclude;
}
}
}
//
// Trim it down to the interesting zone. If this zone starts beyond
// the zone we're interested in, or ends before it, we ignore it.
//
if (IncludeZoneEndingLcn) {
if ((StartingLcn < IncludeZoneEndingLcn) &&
(EndingLcn > IncludeZoneStartingLcn)) {
if (StartingLcn < IncludeZoneStartingLcn) {
StartingLcn = IncludeZoneStartingLcn;
}
if (EndingLcn > IncludeZoneEndingLcn) {
EndingLcn = IncludeZoneEndingLcn;
}
}
else {
continue;
}
}
FreeSpaceEntry.StartingLcn = StartingLcn;
FreeSpaceEntry.ClusterCount = EndingLcn - StartingLcn;
FreeSpaceEntry.SortBySize = FALSE;
if (pBiggestFreeSpaceClusterCount) {
if (FreeSpaceEntry.ClusterCount > *pBiggestFreeSpaceClusterCount) {
*pBiggestFreeSpaceClusterCount = FreeSpaceEntry.ClusterCount;
}
}
//
// Add this entry to the appropriate table. We have ten free space
// tables, and they contain entries of the following sizes:
//
// dwTableIndex Free space size
// ------------ ---------------
// 0 1
// 1 2
// 2 3-4
// 3 5-8
// 4 9-16
// 5 17-32
// 6 33-64
// 7 65-256
// 8 257-4096
// 9 4097+
//
//
// Entries in each table are sorted by StartingLcn.
//
if (FreeSpaceEntry.ClusterCount <= 1) {
dwTableIndex = 0;
}
else if (FreeSpaceEntry.ClusterCount == 2) {
dwTableIndex = 1;
}
else if (FreeSpaceEntry.ClusterCount <= 4) {
dwTableIndex = 2;
}
else if (FreeSpaceEntry.ClusterCount <= 8) {
dwTableIndex = 3;
}
else if (FreeSpaceEntry.ClusterCount <= 16) {
dwTableIndex = 4;
}
else if (FreeSpaceEntry.ClusterCount <= 32) {
dwTableIndex = 5;
}
else if (FreeSpaceEntry.ClusterCount <= 64) {
dwTableIndex = 6;
}
else if (FreeSpaceEntry.ClusterCount <= 256) {
dwTableIndex = 7;
}
else if (FreeSpaceEntry.ClusterCount <= 4096) {
dwTableIndex = 8;
}
else {
dwTableIndex = 9;
}
pFreeSpaceTable = &VolData.MultipleFreeSpaceTrees[dwTableIndex];
p = RtlInsertElementGenericTable(
pFreeSpaceTable,
(PVOID) &FreeSpaceEntry,
sizeof(FREE_SPACE_ENTRY),
&bNewElement);
if (!p) {
//
// An allocation failed
//
bResult = FALSE;
break;
};
}
if (VolData.hVolumeBitmap) {
GlobalUnlock(VolData.hVolumeBitmap);
}
return bResult;
}
BOOL
FindFreeSpaceWithMultipleTrees(
IN CONST LONGLONG ClusterCount,
IN CONST LONGLONG MaxStartingLcn
)
{
PRTL_GENERIC_TABLE pFreeSpaceTable = NULL;
DWORD dwTableIndex = 0;
BOOL done = FALSE;
PFREE_SPACE_ENTRY pFreeSpaceEntry;
BOOLEAN bRestart = TRUE;
LONGLONG CurrentBest = VolData.TotalClusters;
//
// Find out which table we want to start our search from. We have ten free
// space tables, and they contain entries of the following sizes:
//
// dwTableIndex Free space size
// ------------ ---------------
// 0 1
// 1 2
// 2 3-4
// 3 5-8
// 4 9-16
// 5 17-32
// 6 33-64
// 7 65-256
// 8 257-4096
// 9 4097+
//
//
// Entries in each table are sorted by StartingLcn.
//
if (ClusterCount <= 1) {
dwTableIndex = 0;
}
else if (ClusterCount == 2) {
dwTableIndex = 1;
}
else if (ClusterCount <= 4) {
dwTableIndex = 2;
}
else if (ClusterCount <= 8) {
dwTableIndex = 3;
}
else if (ClusterCount <= 16) {
dwTableIndex = 4;
}
else if (ClusterCount <= 32) {
dwTableIndex = 5;
}
else if (ClusterCount <= 64) {
dwTableIndex = 6;
}
else if (ClusterCount <= 256) {
dwTableIndex = 7;
}
else if (ClusterCount <= 4096) {
dwTableIndex = 8;
}
else {
dwTableIndex = 9;
}
// Assume no free space left
VolData.pFreeSpaceEntry = NULL;
VolData.FoundLcn = VolData.TotalClusters;
VolData.FoundLen = 0;
//
// Search through the entries in this table, till we come across the first
// entry that is big enough.
//
pFreeSpaceTable = &VolData.MultipleFreeSpaceTrees[dwTableIndex];
do {
pFreeSpaceEntry = (PFREE_SPACE_ENTRY) RtlEnumerateGenericTableAvl(pFreeSpaceTable, bRestart);
bRestart = FALSE;
if ((!pFreeSpaceEntry) || (pFreeSpaceEntry->StartingLcn > MaxStartingLcn)) {
break;
}
if (pFreeSpaceEntry->ClusterCount >= ClusterCount) {
VolData.pFreeSpaceEntry = pFreeSpaceEntry;
VolData.FoundLcn = pFreeSpaceEntry->StartingLcn;
VolData.FoundLen = pFreeSpaceEntry->ClusterCount;
CurrentBest = pFreeSpaceEntry->StartingLcn;
break;
}
} while (TRUE);
//
// For the remaining tables, search through to see if any of them
// have entries that start before our current best.
//
while (++dwTableIndex < 10) {
pFreeSpaceTable = &VolData.MultipleFreeSpaceTrees[dwTableIndex];
pFreeSpaceEntry = (PFREE_SPACE_ENTRY) RtlEnumerateGenericTableAvl(pFreeSpaceTable, TRUE);
if ((pFreeSpaceEntry) &&
(pFreeSpaceEntry->ClusterCount >= ClusterCount) &&
(pFreeSpaceEntry->StartingLcn < MaxStartingLcn) &&
(pFreeSpaceEntry->StartingLcn < CurrentBest)
) {
VolData.pFreeSpaceEntry = pFreeSpaceEntry;
VolData.FoundLcn = pFreeSpaceEntry->StartingLcn;
VolData.FoundLen = pFreeSpaceEntry->ClusterCount;
CurrentBest = pFreeSpaceEntry->StartingLcn;
}
}
if (VolData.pFreeSpaceEntry) {
return TRUE;
}
else {
return FALSE;
}
}
BOOL
UpdateInMultipleTrees(
IN PFREE_SPACE_ENTRY pOldEntry,
IN PFREE_SPACE_ENTRY pNewEntry
)
{
PRTL_GENERIC_TABLE pFreeSpaceTable = NULL;
PVOID p = NULL;
BOOL bResult = TRUE;
BOOLEAN bNewElement = FALSE;
LONGLONG ClusterCount = pOldEntry->ClusterCount;
DWORD dwTableIndex = 0;
//
// Find out which table we want to delete from. We have ten free
// space tables, and they contain entries of the following sizes:
//
// dwTableIndex Free space size
// ------------ ---------------
// 0 1
// 1 2
// 2 3-4
// 3 5-8
// 4 9-16
// 5 17-32
// 6 33-64
// 7 65-256
// 8 257-4096
// 9 4097+
//
//
// Entries in each table are sorted by StartingLcn.
//
if (ClusterCount <= 1) {
dwTableIndex = 0;
}
else if (ClusterCount == 2) {
dwTableIndex = 1;
}
else if (ClusterCount <= 4) {
dwTableIndex = 2;
}
else if (ClusterCount <= 8) {
dwTableIndex = 3;
}
else if (ClusterCount <= 16) {
dwTableIndex = 4;
}
else if (ClusterCount <= 32) {
dwTableIndex = 5;
}
else if (ClusterCount <= 64) {
dwTableIndex = 6;
}
else if (ClusterCount <= 256) {
dwTableIndex = 7;
}
else if (ClusterCount <= 4096) {
dwTableIndex = 8;
}
else {
dwTableIndex = 9;
}
pFreeSpaceTable = &VolData.MultipleFreeSpaceTrees[dwTableIndex];
bNewElement = RtlDeleteElementGenericTable(pFreeSpaceTable, (PVOID)pOldEntry);
if (!bNewElement) {
Trace(warn, "Could not find Element in Free Space Table!");
assert(FALSE);
bResult = FALSE;
}
//
// Find out which table we want to add to.
//
if (pNewEntry) {
ClusterCount = pNewEntry->ClusterCount;
if (ClusterCount > 0) {
if (ClusterCount <= 1) {
dwTableIndex = 0;
}
else if (ClusterCount == 2) {
dwTableIndex = 1;
}
else if (ClusterCount <= 4) {
dwTableIndex = 2;
}
else if (ClusterCount <= 8) {
dwTableIndex = 3;
}
else if (ClusterCount <= 16) {
dwTableIndex = 4;
}
else if (ClusterCount <= 32) {
dwTableIndex = 5;
}
else if (ClusterCount <= 64) {
dwTableIndex = 6;
}
else if (ClusterCount <= 256) {
dwTableIndex = 7;
}
else if (ClusterCount <= 4096) {
dwTableIndex = 8;
}
else {
dwTableIndex = 9;
}
pFreeSpaceTable = &VolData.MultipleFreeSpaceTrees[dwTableIndex];
p = RtlInsertElementGenericTable(
pFreeSpaceTable,
(PVOID) pNewEntry,
sizeof(FREE_SPACE_ENTRY),
&bNewElement);
if (!p) {
//
// An allocation failed
//
bResult = FALSE;
};
}
}
return bResult;
}
BOOL
FindFreeSpace(
IN PRTL_GENERIC_TABLE pTable,
IN BOOL DeleteUnusedEntries,
IN LONGLONG MaxStartingLcn
)
{
BOOL done = FALSE;
PFREE_SPACE_ENTRY pFreeSpaceEntry;
BOOLEAN bNext = FALSE;
PVOID pRestartKey = NULL;
ULONG ulDeleteCount = 0;
FREE_SPACE_ENTRY entry;
ZeroMemory(&entry, sizeof(FREE_SPACE_ENTRY));
do {
pFreeSpaceEntry = (PFREE_SPACE_ENTRY) RtlEnumerateGenericTableLikeADirectory(
pTable,
NULL,
NULL,
bNext,
&pRestartKey,
&ulDeleteCount,
&entry);
bNext = TRUE;
if (!pFreeSpaceEntry) {
// No free space left
VolData.pFreeSpaceEntry = NULL;
VolData.FoundLcn = 0;
VolData.FoundLen = 0;
break;
}
if (pFreeSpaceEntry->StartingLcn > MaxStartingLcn) {
break;
}
if (pFreeSpaceEntry->ClusterCount < VolData.NumberOfClusters) {
// This space is of no use to us--it's too small.
if ((0 == pFreeSpaceEntry->ClusterCount) || (DeleteUnusedEntries)) {
RtlDeleteElementGenericTable(pTable, pFreeSpaceEntry);
}
}
else {
VolData.pFreeSpaceEntry = pFreeSpaceEntry;
VolData.FoundLcn = pFreeSpaceEntry->StartingLcn;
VolData.FoundLen = pFreeSpaceEntry->ClusterCount;
done = TRUE;
}
} while (!done);
return done;
}
BOOL
FindSortedFreeSpace(
IN PRTL_GENERIC_TABLE pTable
)
{
BOOL bFound = FALSE;
PFREE_SPACE_ENTRY pFreeSpaceEntry;
PVOID pRestartKey = NULL;
ULONG ulDeleteCount = 0;
FREE_SPACE_ENTRY entry;
ZeroMemory(&entry, sizeof(FREE_SPACE_ENTRY));
entry.ClusterCount = VolData.NumberOfClusters;
entry.SortBySize = TRUE;
pFreeSpaceEntry = (PFREE_SPACE_ENTRY) RtlEnumerateGenericTableLikeADirectory(
pTable,
NULL,
NULL,
FALSE,
&pRestartKey,
&ulDeleteCount,
&entry);
if (!pFreeSpaceEntry) {
// No free space left
VolData.pFreeSpaceEntry = NULL;
VolData.FoundLcn = 0;
VolData.FoundLen = 0;
bFound = FALSE;
}
else {
VolData.pFreeSpaceEntry = pFreeSpaceEntry;
VolData.FoundLcn = pFreeSpaceEntry->StartingLcn;
VolData.FoundLen = pFreeSpaceEntry->ClusterCount;
bFound = TRUE;
}
return bFound;
}
/*****************************************************************************************************************
COPYRIGHT<EFBFBD> 2001 Microsoft Corporation and Executive Software International, Inc.
ROUTINE DESCRIPTION:
Common routine for most requests to locate free space.
This routine searches the area of the volume delimited by
the variables VolData.DestStartLcn and VolData.DestEndLcn.
Input parm Type specifies which type of free space to find:
FIRST_FIT Find earliest free space extent large enough to hold the entire file
BEST_FIT Find smallest free space extent large enough to hold the entire file
LAST_FIT Find latest free space extent large enough to hold the entire file
EARLIER Locate the start of the free space extent adjacent to the start of this file
FIND_FIRST Find earliest free space extent
FIND_LAST Find latest free space extent
FIND_LARGEST Find largest free space extent
AT_END Find the Lcn to position the data at the end of the found free space extent
COUNT_FREE_SPACES Count the nuumber of free spaces
INPUT + OUTPUT:
IN Type - Which type of free space to find
GLOBALS:
IN OUT Various VolData fields.
RETURN:
TRUE - Success
FALSE - Failure
*/
BOOL
FindFreeSpace(
IN int Type
)
{
BOOL bRetStatus = TRUE;
LONGLONG StartingLcn;
LONGLONG EndingLcn;
LONGLONG ClusterCount;
TCHAR cString[500];
LONGLONG Lcn;
PULONG pBitmap = NULL;
LONGLONG BestFitStart = 0;
LONGLONG BestFitLength = 0x7fffffffffffffff;
LONGLONG RunLength = VolData.NumberOfClusters;
LONGLONG LargestFound = 0;
LONGLONG RangeStart = VolData.DestStartLcn;
LONGLONG RangeEnd = VolData.DestEndLcn;
switch(Type) {
case FIRST_FIT:
Message(TEXT("FindFreeSpace - FIRST_FIT"), -1, NULL);
break;
case BEST_FIT:
Message(TEXT("FindFreeSpace - BEST_FIT"), -1, NULL);
break;
case LAST_FIT:
Message(TEXT("FindFreeSpace - LAST_FIT"), -1, NULL);
break;
case EARLIER:
Message(TEXT("FindFreeSpace - EARLIER"), -1, NULL);
break;
case FIND_FIRST:
Message(TEXT("FindFreeSpace - FIND_FIRST"), -1, NULL);
break;
case FIND_LAST:
Message(TEXT("FindFreeSpace - FIND_LAST"), -1, NULL);
break;
case FIND_LARGEST:
Message(TEXT("FindFreeSpace - FIND_LARGEST"), -1, NULL);
break;
case AT_END:
Message(TEXT("FindFreeSpace - AT_END"), -1, NULL);
break;
case COUNT_FREE_SPACES:
Message(TEXT("FindFreeSpace - COUNT_FREE_SPACES"), -1, NULL);
break;
default:
Message(TEXT("FindFreeSpace - ERROR"), -1, NULL);
return FALSE;
}
__try {
//0.0E00 Preset return values for failure
VolData.Status = NEXT_ALGO_STEP;
VolData.FoundLen = 0;
VolData.FreeSpaces = 0;
VolData.LargestFound = 0;
//0.0E00 If find earlier than file is requested set the end of the search range to start of file
if(Type & EARLIER) {
RangeStart = 0;
RangeEnd = VolData.LastStartingLcn;
}
//0.0E00 If find largest is requested preset best found so far to smallest value
if(Type & FIND_LARGEST) {
BestFitLength = 0;
}
#ifndef OFFLINEDK
//0.0E00 If find earliest requested preset size of minimum free space extent to search for.
if(Type & FIND_FIRST) {
RunLength = 1;
}
#endif
//0.0E00 Get a pointer to the free space bitmap
PVOLUME_BITMAP_BUFFER pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
if (pVolumeBitmap == (PVOLUME_BITMAP_BUFFER) NULL){
bRetStatus = FALSE;
EH_ASSERT(FALSE);
__leave;
}
pBitmap = (PULONG)&pVolumeBitmap->Buffer;
//0.0E00 Scan thru the specified range looking for the specified type of free space extent
for(Lcn = RangeStart; Lcn < RangeEnd; ) {
//0.0E00 Find the next free space extent within the range
FindFreeExtent(pBitmap, RangeEnd, &Lcn, &StartingLcn, &ClusterCount);
//0.0E00 Stop if there are no more free space extents within the range
if(ClusterCount == 0) {
break;
}
//0.0E00 Calc the end of this free space extent
EndingLcn = StartingLcn + ClusterCount;
//0.0E00 Stop if earlier than file requested and this free space later than file
if((Type & EARLIER) && (EndingLcn > VolData.LastStartingLcn)) {
break;
}
//0.0E00 If NTFS and range is outside MFT zone then clip the free space extent to exclude the MFT zone
if((VolData.FileSystem == FS_NTFS) && (VolData.DestEndLcn != VolData.MftZoneEnd)) {
if((StartingLcn < VolData.MftZoneEnd) &&
(EndingLcn > VolData.MftZoneStart)) {
if(StartingLcn < VolData.MftZoneStart) {
EndingLcn = VolData.MftZoneStart;
}
else if(EndingLcn <= VolData.MftZoneEnd) {
continue;
//0.0E00 Handle the case of EndingLcn > pVolData->MftZoneEnd.
}
else {
StartingLcn = VolData.MftZoneEnd;
}
}
}
if(ClusterCount >= LargestFound) {
LargestFound = ClusterCount;
}
//0.0E00 If we are only counting free space just bump the count and skip to the next free space extent
if(Type & COUNT_FREE_SPACES) {
VolData.FreeSpaces ++;
continue;
}
//0.0E00 Record this free space extent if any of the following conditions are met:
//0.0E00 Find earliest or find latest requested
//0.0E00 Find first fit or find last fit or find earlier and the free space extent large enough
//0.0E00 Find largest and the free space extent largest so far
//0.0E00 Find best fit and the free space extent smallest so far that is large enough
if( (Type & (FIND_FIRST | FIND_LAST)) ||
((Type & (LAST_FIT | FIRST_FIT | EARLIER)) && (ClusterCount >= RunLength)) ||
((Type & FIND_LARGEST) && (ClusterCount > BestFitLength)) ||
((Type & BEST_FIT) && (ClusterCount >= RunLength) && (ClusterCount < BestFitLength)) ) {
BestFitStart = StartingLcn;
BestFitLength = ClusterCount;
}
//0.0E00 Stop if find first...it has been found
if(Type & FIND_FIRST) {
break;
}
//0.0E00 If find last fit and large enough skip to next free space extent
if((Type & LAST_FIT) && (ClusterCount >= RunLength)) {
continue;
}
//0.0E00 Stop if first fit or earlier and large enough
if( (Type & (FIRST_FIT | EARLIER)) &&
(ClusterCount >= RunLength) ) {
break;
}
}
//0.0E00 If count free spaces exit now...that has been done
if(Type & COUNT_FREE_SPACES) {
bRetStatus = TRUE;
__leave;
}
VolData.LargestFound = LargestFound;
//0.0E00 If none were found that match the requirements then exit
if(BestFitLength == 0x7fffffffffffffff) {
VolData.FoundLen = 0;
wsprintf(cString,TEXT("No free space on disk for 0x%lX clusters."), (ULONG)RunLength);
Message(cString, -1, NULL);
bRetStatus = FALSE;
__leave;
}
//0.0E00 If at end then calc Lcn to position at end of the free space extent
if(Type & AT_END) {
BestFitStart = (BestFitStart + BestFitLength) - RunLength;
BestFitLength = RunLength;
}
//0.0E00 Pass back the free space extent's location and length
VolData.FoundLcn = BestFitStart;
VolData.FoundLen = BestFitLength;
wsprintf(cString, TEXT("Found contiguous free space at LCN 0x%lX"), (ULONG)BestFitStart);
wsprintf(cString+lstrlen(cString), TEXT(" for Cluster Count of 0x%lX"), (ULONG)BestFitLength);
Message(cString, -1, NULL);
bRetStatus = TRUE;
}
__finally {
if (VolData.hVolumeBitmap) {
GlobalUnlock(VolData.hVolumeBitmap);
}
}
return bRetStatus;
}
/*****************************************************************************************************************
COPYRIGHT<EFBFBD> 2001 Microsoft Corporation and Executive Software International, Inc.
ROUTINE DESCRIPTION:
Get the volume bitmap and fill the MFT zone and all the buffer beyond the end of the bitmap with not-free.
The volume bitmap is stored in the Buffer field of the VOLUME_BITMAP_BUFFER structure.
INPUT + OUTPUT:
None.
GLOBALS:
IN OUT Various VolData fields.
RETURN:
TRUE - Success
FALSE - Failure
*/
BOOL
GetVolumeBitmap(
)
{
STARTING_LCN_INPUT_BUFFER StartingLcnInputBuffer;
PVOLUME_BITMAP_BUFFER pVolumeBitmap = NULL;
PULONG pBitmap = NULL;
ULONG BytesReturned = 0;
LONGLONG Cluster;
BOOL bRetStatus = FALSE; // assume an error
__try {
//0.0E00 Lock and clear the bitmap buffer
pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
if (pVolumeBitmap == (PVOLUME_BITMAP_BUFFER) NULL){
bRetStatus = FALSE;
EH_ASSERT(FALSE);
__leave;
}
ZeroMemory(pVolumeBitmap, (DWORD)(sizeof(VOLUME_BITMAP_BUFFER) + (VolData.BitmapSize / 8)));
//0.0E00 Load the bitmap
StartingLcnInputBuffer.StartingLcn.QuadPart = 0;
pVolumeBitmap->BitmapSize.QuadPart = VolData.BitmapSize;
BOOL ret = ESDeviceIoControl(
VolData.hVolume,
FSCTL_GET_VOLUME_BITMAP,
&StartingLcnInputBuffer,
sizeof(STARTING_LCN_INPUT_BUFFER),
pVolumeBitmap,
(DWORD)GlobalSize(VolData.hVolumeBitmap),
&BytesReturned,
NULL);
if (!ret){
bRetStatus = FALSE;
EH_ASSERT(FALSE);
__leave;
}
pBitmap = (PULONG)&pVolumeBitmap->Buffer;
//0.0E00 Fill the MFT zone with not-free
/* if(VolData.FileSystem == FS_NTFS) {
Cluster = VolData.MftZoneStart;
while((MODULUSLONGLONGBY32(Cluster) != 0) && (Cluster < VolData.MftZoneEnd)) {
pBitmap[DIVIDELONGLONGBY32(Cluster)] |= (1 << (ULONG) MODULUSLONGLONGBY32(Cluster));
Cluster ++;
}
if(Cluster < VolData.MftZoneEnd) {
while(VolData.MftZoneEnd - Cluster >= 32) {
pBitmap[DIVIDELONGLONGBY32(Cluster)] = 0xffffffff;
Cluster += 32;
}
while(Cluster < VolData.MftZoneEnd) {
pBitmap[DIVIDELONGLONGBY32(Cluster)] |= (1 << (ULONG) MODULUSLONGLONGBY32(Cluster));
Cluster ++;
}
}
}
*/ //0.0E00 Fill the end of bitmap with not-free
for(Cluster = VolData.TotalClusters; Cluster < VolData.BitmapSize; Cluster ++) {
pBitmap[DIVIDELONGLONGBY32(Cluster)] |= (1 << (ULONG) MODULUSLONGLONGBY32(Cluster));
}
// #DK312_083 JLJ 20May99
// Make sure there's a zero bit in the fill-space (to stop the
// used space/free space optimizations)
Cluster = VolData.TotalClusters + 1;
pBitmap[DIVIDELONGLONGBY32(Cluster)] &= ~(1 << (ULONG) MODULUSLONGLONGBY32(Cluster));
bRetStatus = TRUE;
}
__finally {
if(VolData.hVolumeBitmap) {
GlobalUnlock(VolData.hVolumeBitmap);
}
}
return bRetStatus;
}
/*****************************************************************************************************************
COPYRIGHT<EFBFBD> 2001 Microsoft Corporation and Executive Software International, Inc.
ROUTINE DESCRIPTION:
Fills the MFT zone with not-free. (This is an ham-handed way of ensuring we
don't move files into the MFT zone.) The volume bitmap is stored in the Buffer
field of the VOLUME_BITMAP_BUFFER structure.
INPUT + OUTPUT:
None.
GLOBALS:
IN OUT Various VolData fields.
RETURN:
TRUE - Success
FALSE - Failure
*/
BOOL
MarkMFTUnavailable(void)
{
//0.0E00 Lock the bitmap buffer and get a pointer to it...
PVOLUME_BITMAP_BUFFER pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
EF(pVolumeBitmap != NULL);
//0.0E00 Get a pointer to the actual bitmap portion
PULONG pBitmap = (PULONG)&pVolumeBitmap->Buffer;
//0.0E00 Mark the MFT zone as "not free"
if(VolData.FileSystem == FS_NTFS) {
LONGLONG Cluster = VolData.MftZoneStart;
// this loop marks the bits of the first DWORD in the bitmap
// for the first few clusters of the MFT zone
while (MODULUSLONGLONGBY32(Cluster) != 0 && Cluster < VolData.MftZoneEnd) {
pBitmap[DIVIDELONGLONGBY32(Cluster)] |= (1 << (ULONG) MODULUSLONGLONGBY32(Cluster));
Cluster++;
}
// this section gets the rest of the clusters
if(Cluster < VolData.MftZoneEnd) {
// mark out the groups of 32 cluster sections
while(VolData.MftZoneEnd - Cluster >= 32) {
pBitmap[DIVIDELONGLONGBY32(Cluster)] = 0xffffffff;
Cluster += 32;
}
// mark out the remaining bits of the last section of the MFT zone
while(Cluster < VolData.MftZoneEnd) {
pBitmap[DIVIDELONGLONGBY32(Cluster)] |= (1 << (ULONG) MODULUSLONGLONGBY32(Cluster));
Cluster++;
}
}
}
//0.0E00 Finally, unlock the bitmap...
if(VolData.hVolumeBitmap) {
GlobalUnlock(VolData.hVolumeBitmap);
}
return TRUE;
}
/*****************************************************************************************************************
COPYRIGHT<EFBFBD> 2001 Microsoft Corporation and Executive Software International, Inc.
ROUTINE DESCRIPTION:
Check the clusters adjacent to the start of this file to see if they are free.
INPUT + OUTPUT:
None.
GLOBALS:
IN OUT Various VolData fields.
RETURN:
TRUE - Success
FALSE - Failure
*/
BOOL
IsFreeSpaceAtHeadOfFile(
)
{
BOOL bRetStatus = FALSE;
PVOLUME_BITMAP_BUFFER pVolumeBitmap = NULL;
PULONG pBitmap = NULL;
ULONG Mask = 1;
LONGLONG Word = 0;
LONGLONG Bit = 0;
LONGLONG StartingLcn = VolData.StartingLcn;
LONGLONG EndingLcn = VolData.StartingLcn;
LONGLONG ClusterCount;
TCHAR cString[500];
__try {
//0.0E00 Preset status in case an error occurs
VolData.Status = NEXT_ALGO_STEP;
VolData.FoundLen = 0;
//0.0E00 Lock the bitmap and get a pointer to it
pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
if (pVolumeBitmap == (PVOLUME_BITMAP_BUFFER) NULL){
bRetStatus = FALSE;
EH_ASSERT(FALSE);
__leave;
}
pBitmap = (PULONG)&pVolumeBitmap->Buffer;
//0.0E00 Setup bitmap indexes
Word = DIVIDELONGLONGBY32(StartingLcn - 1);
Mask = 1 << (((ULONG)StartingLcn - 1) & 31);
//0.0E00 Check for file against start of disk or one of the MFT zones
if((StartingLcn == 0) ||
(StartingLcn == VolData.MftZoneEnd) ||
(StartingLcn == VolData.CenterOfDisk) ||
((pBitmap[Word] & Mask) != 0)) {
Message(TEXT("No free space before this file"), -1, NULL);
bRetStatus = TRUE;
__leave;
}
//0.0E00 On FAT volumes any space before the file is enough
if(VolData.FileSystem != FS_NTFS) {
VolData.FoundLen = 1;
Message(TEXT("Found free space before this file"), -1, NULL);
bRetStatus = TRUE;
__leave;
}
//0.0E00 Find the start of the free space preceding this file
while((Word >= 0) && ((pBitmap[Word] & Mask) == 0)) {
Mask >>= 1;
if(Mask == 0) {
Mask = 0x80000000;
for (Word--; (Word >= 0) && (pBitmap[Word] == 0); Word--)
;
}
}
Mask <<= 1;
if(Mask == 0) {
Mask = 1;
Word ++;
}
for(Bit = -1; Mask; Mask >>= 1, Bit ++);
//0.0E00 Convert the indexes into an Lcn
StartingLcn = (Word * 32) + Bit;
//0.0E00 Adjust for free space that spans the center of the disk
if((VolData.StartingLcn >= VolData.CenterOfDisk) && (StartingLcn < VolData.CenterOfDisk)) {
StartingLcn = VolData.CenterOfDisk;
}
//0.0E00 Adjust for free space that spans the MFT zone
if(VolData.FileSystem == FS_NTFS) {
if((StartingLcn < VolData.MftZoneEnd) &&
(EndingLcn > VolData.MftZoneStart)) {
if(StartingLcn < VolData.MftZoneStart) {
EndingLcn = VolData.MftZoneStart;
}
else if(EndingLcn <= VolData.MftZoneEnd) {
StartingLcn = VolData.MftZoneEnd;
EndingLcn = VolData.MftZoneEnd;
}
//0.0E00 Handle the case of EndingLcn > VolData.MftZoneEnd.
else {
StartingLcn = VolData.MftZoneEnd;
}
}
}
ClusterCount = EndingLcn - StartingLcn;
//0.0E00 Return the location and size of the free space found
VolData.FoundLcn = StartingLcn;
VolData.FoundLen = ClusterCount;
wsprintf(cString, TEXT("Free space before file at LCN 0x%X"), VolData.FoundLcn);
wsprintf(cString+lstrlen(cString), TEXT(" for Cluster Count of 0x%lX\n"), VolData.FoundLen);
Message(cString, -1, NULL);
bRetStatus = TRUE;
}
__finally {
if (VolData.hVolumeBitmap) {
GlobalUnlock(VolData.hVolumeBitmap);
}
}
return bRetStatus;
}
/*********************************************************************************************
COPYRIGHT<EFBFBD> 2001 Microsoft Corporation and Executive Software International, Inc.
ROUTINE DESCRIPTION:
Find the next run of un-allocated clusters.
Bits set to '1' in the volume bitmap are allocated or "used".
Bits set to '0' in the volume bitmap are un-allocated or "free".
A bit set to '1' is considered "set".
A bit set to '0' is considered "clear".
INPUT + OUTPUT:
IN pClusterBitmap - Volume cluster bitmap
IN RangeEnd - End of the area of the volume to search in
IN OUT pLcn - Where to start searching and returns where search stopped
OUT pStartingLcn - Returns where the free run starts
OUT pClusterCount - Returns the length of the free run
GLOBALS:
None.
RETURN:
None
*/
VOID
FindFreeExtent(
IN PULONG pClusterBitmap,
IN LONGLONG RangeEnd,
IN OUT PLONGLONG pLcn,
OUT PLONGLONG pStartingLcn,
OUT PLONGLONG pClusterCount
)
{
// default returns to 0
*pStartingLcn = 0;
*pClusterCount = 0;
// check for bad LCN input
if (pLcn == NULL) {
Message(TEXT("FindFreeExtent called with NULL arg"), -1, NULL);
assert(pLcn != NULL);
return;
}
// check for bad RangeEnd
if (RangeEnd > VolData.TotalClusters) {
Message(TEXT("FindFreeExtent called with RangeEnd > TotalClusters"), -1, NULL);
assert(RangeEnd <= VolData.TotalClusters);
return;
}
// if requester asks for extent past end of volume, don't honor it.
if (*pLcn >= RangeEnd) {
Message(TEXT("FindFreeExtent called with LCN > RangeEnd"), -1, NULL);
return;
}
// iBitmapWord - index into the cluster bitmap array (array of ULONGs)
// divide lcn by word size (word index) = Lcn / 32 (same as shift right 5)
ULONG iBitmapWord = (ULONG) DIVIDELONGLONGBY32(*pLcn);
// iBit - index of the bit in the current word
// remainder (bit index in current word) = Lcn % 32 (same as and'ing lowest 5 bits)
ULONG iBit = (ULONG) MODULUSLONGLONGBY32(*pLcn);
// LCN should always be the cluster array index times 32 plus the bit index
assert(*pLcn == (iBitmapWord * 32) + iBit);
// an empty word except for the starting Lcn bit
ULONG Mask = (ULONG) (1 << iBit);
// find the start of the next free space extent
while (*pLcn < RangeEnd && *pStartingLcn == 0) {
// checks
assert(*pLcn == (iBitmapWord * 32) + iBit);
assert(Mask != 0);
assert(iBit >= 0 && iBit < 32);
// if there are no free bits in the entire word we can jump ahead
// we can only check this on a word boundary when iBit = 0
if (iBit == 0 && pClusterBitmap[iBitmapWord] == 0xFFFFFFFF) {
(*pLcn) += 32;
iBitmapWord++;
Mask = 1;
continue;
}
// otherwise, check for a free cluster: bit = 0
else if (!(pClusterBitmap[iBitmapWord] & Mask)) {
*pStartingLcn = *pLcn; // mark start of free space extent
}
// go to the next cluster
(*pLcn)++;
iBit++;
Mask <<= 1;
// check for crossing into the next word of the cluster array
if (iBit >= 32) {
iBit = 0;
iBitmapWord++;
Mask = 1;
}
}
// LCN should always be the cluster array index times 32 plus the bit index
assert(*pLcn == (iBitmapWord * 32) + iBit);
// if we found a free cluster
if (*pStartingLcn > 0) {
// find the next used cluster
// and count the free clusters
while (*pLcn < RangeEnd) {
// checks
assert(*pLcn == (iBitmapWord * 32) + iBit);
assert(Mask != 0);
assert(iBit >= 0 && iBit < 32);
// if the entire word is free space we can jump ahead
// we can only check this on a word boundary when iBit = 0
if (iBit == 0 && pClusterBitmap[iBitmapWord] == 0) {
(*pLcn) += 32;
iBitmapWord++;
Mask = 1;
continue;
}
// otherwise, check for a used cluster: bit = 1
else if (pClusterBitmap[iBitmapWord] & Mask) {
break; // we're done
}
// go to the next cluster
(*pLcn)++;
iBit++;
Mask <<= 1;
// check for crossing into the next word of the cluster array
if (iBit >= 32) {
iBit = 0;
iBitmapWord++;
Mask = 1;
}
}
}
// set cluster count
if (*pStartingLcn > 0) {
*pClusterCount = *pLcn - *pStartingLcn;
}
// sanity check of outputs
assert(*pClusterCount == 0 && *pStartingLcn == 0 ||
*pClusterCount > 0 && *pStartingLcn > 0);
}
/*****************************************************************************************************************
COPYRIGHT<EFBFBD> 2001 Microsoft Corporation and Executive Software International, Inc.
ROUTINE DESCRIPTION:
This routine is part of the so-called "partial defrag" code. So called because
in its current incarnation it really works on the whole file, not part of the file.
This routine is called because there was no contiguous run of free space on the
volume long enough to hold the entire file. This routine uses the current size
of the file to estimate how many freespace extents might be needed to hold the
file (different if FAT or NTFS) and uses that data to fill an allocated array
with free space extents. This array is then used in the Partial Defrag routine
to move the file around.
The purpose of this routine is find free space that will get the file out of the
way so that the rest of the volume can be defragmented. It is not always clear
how one should accomplish this, however, so the routine gathers the data in different
ways: toward the front of the partition, toward the back of the partition, or just
using the largest available free extents.
INPUT + OUTPUT:
None.
GLOBALS:
IN OUT Various VolData fields.
Allocated and initialized in this routine:
pVolData->hFreeExtents = free space extent list,
pVolData->FreeExtents = number of free space extents.
Updated in this routine:
VolData.Pass = NEXT_FILE if return=false
VolData.Pass = NEXT_ALGO_STEP if return=true
RETURN:
TRUE - Success
FALSE - Failure
*/
BOOL
FindLastFreeSpaceChunks(
)
{
//the next item is static so it can be used to ping-pong the
//selection of the sort algorithm. Just didn't seem worth
//it to add it to Voldata right now.
BOOL bRetStatus = FALSE;
LONGLONG FirstLcn = VolData.StartingLcn;
LONGLONG LastLcn = VolData.DestEndLcn;
LONGLONG RunLength = VolData.NumberOfClusters;
EXTENT_LIST* pExtentList = NULL;
LONGLONG FreeExtent = 0;
TCHAR cString[500];
LONGLONG SearchStartLcn = 0;
LONGLONG FreeStartLcn;
LONGLONG FreeCount;
LONG iii,jjj;
Message(TEXT("FindLastFreeSpaceChunks"), -1, NULL);
//0.0E00 Preset clusters found to none in case not enough are found
VolData.Status = NEXT_ALGO_STEP;
VolData.FoundLen = 0;
__try {
//0.0E00 Lock the volume bitmap, and get a pointer to it
VOLUME_BITMAP_BUFFER *pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
if (pVolumeBitmap == (PVOLUME_BITMAP_BUFFER) NULL){
EH_ASSERT(FALSE);
__leave;
}
//get a pointer that points to the bitmap part of the bitmap
//(past the header)
ULONG *pBitmap = (PULONG)&pVolumeBitmap->Buffer;
//estimate the extent list entries needed to hold the file's new extent list
//(Note, this is purely speculative jlj 15Apr99)
if(VolData.FileSystem == FS_NTFS) {
VolData.FreeExtents = 2 * (VolData.NumberOfClusters / 16);
}
else {
VolData.FreeExtents = VolData.NumberOfClusters;
}
// limit the estimate to 2000 extents (at least for now)
//(Note, this is purely speculative jlj 15Apr99)
if (VolData.FreeExtents > 2000) {
VolData.FreeExtents = 2000;
}
//John Joesph change not approved yet
//VolData.FreeExtents = VolData.currentnumberoffragments -1.
//if VolData.FreeExtents == 0 do something
//0.0E00 Allocate a buffer for the free space extent list
if (!AllocateMemory((DWORD)(VolData.FreeExtents * sizeof(EXTENT_LIST)), &VolData.hFreeExtents, (void**)&pExtentList)){
VolData.Status = NEXT_FILE;
EH_ASSERT(FALSE);
__leave;
}
// This section of code scans the free space bitmap and finds the largest
// chunks, but no more than VolData.FreeExtents chunks
// This code basically loops through the free space, gathering
// the statistics for each fragment of free space, starting with
// cluster zero
SearchStartLcn = 0;
while (SearchStartLcn != -1) {
// go get a the next chunk of free space
FindFreeExtent((PULONG) pBitmap, // pointer to the volume bitmap gotten from locking
VolData.TotalClusters, // end of range
&SearchStartLcn, // where to start
&FreeStartLcn, // where the free block starts
&FreeCount); // how big the block is (zero if done)
//a return value of zero means the end of the partition has
//been hit, so if we hit the end of the partition, stop the
//loop.
if (FreeCount == 0) {
SearchStartLcn = -1;
}
else {
//if there's space in the array, just insert it into the array
if (FreeExtent< (VolData.FreeExtents-1)) {
//0.0E00 Add this extent to the free space extent list
pExtentList[FreeExtent].StartingLcn = FreeStartLcn;
pExtentList[FreeExtent].ClusterCount = FreeCount;
FreeExtent++;
continue;
}
// replace the current min with a bigger min
UINT uMinIndex = 0;
LONGLONG llMinClusterCount = 99999;
for (iii=0; iii<FreeExtent; iii++) {
if (pExtentList[iii].ClusterCount < llMinClusterCount) {
uMinIndex = iii;
llMinClusterCount = pExtentList[iii].ClusterCount;
}
}
if (FreeCount > llMinClusterCount){
pExtentList[iii].StartingLcn = FreeStartLcn;
pExtentList[iii].ClusterCount = FreeCount;
}
} //end else (i.e. FoundCount != 0)
} // end while searchstartlcn != -1
// add up the clusters found
LONGLONG FoundClusterCount = 0;
for (iii=0; iii<FreeExtent; iii++) {
FoundClusterCount = FoundClusterCount + pExtentList[iii].ClusterCount;
}
// not enough room to hold the file
if (FoundClusterCount < VolData.NumberOfClusters) {
Message(TEXT("FLFSC:Couldn't find enough free space for this file"), -1, NULL);
VolData.Status = NEXT_FILE;
__leave;
}
if (FreeExtent <= 1) {
Message(TEXT("FLFSC: NO FREE SPACE CHUNKS FOUND ON THIS VOLUME!!!"), -1, NULL);
VolData.Status = NEXT_FILE;
__leave;
}
//sort from big to small
EXTENT_LIST tmpExtentList;
BOOL bSwitched = TRUE;
for (iii=0; iii<(FreeExtent-1) && bSwitched; iii++) {
bSwitched = FALSE;
for (jjj=0; jjj<(FreeExtent-1); jjj++) {
if (pExtentList[jjj].ClusterCount < pExtentList[jjj+1].ClusterCount) {
tmpExtentList = pExtentList[jjj];
pExtentList[jjj] = pExtentList[jjj+1];
pExtentList[jjj+1] = tmpExtentList;
bSwitched = TRUE;
}
}
}
//0.0E00 If we found extents larger than the minimum we may have less extents than expected
VolData.FreeExtents = FreeExtent-1;
//0.0E00 Reallocate the free space entent list
if (!AllocateMemory((DWORD)(VolData.FreeExtents * sizeof(EXTENT_LIST)), &VolData.hFreeExtents, (void**)&pExtentList)){
bRetStatus = FALSE;
EH_ASSERT(FALSE);
__leave;
}
wsprintf(cString,TEXT("FLFSC: Found 0x%lX free extents for 0x%lX Clusters"),
(ULONG)VolData.FreeExtents, (ULONG)FoundClusterCount);
Message(cString, -1, NULL);
//0.0E00 Flag that we were successful
VolData.FoundLcn = pExtentList->StartingLcn;
VolData.FoundLen = FoundClusterCount;
bRetStatus = TRUE;
}
__finally {
if (VolData.hVolumeBitmap) {
GlobalUnlock(VolData.hVolumeBitmap);
}
if (VolData.hFreeExtents) {
GlobalUnlock(VolData.hFreeExtents);
}
}
return bRetStatus;
}
/*****************************************************************************************************************
COPYRIGHT<EFBFBD> 2001 Microsoft Corporation and Executive Software International, Inc.
REVISION HISTORY:
0.0E00 - 26 February 1996 - Chuck Tingley - created
1.0E00 - 24 April 1997 - Zack Gainsforth - Reworked module for Dfrg engine.
ROUTINE DESCRIPTION:
Partial defrag code.
There is no contiguous run of free space on the volume long enough to hold the entire file.
So we gonna move the file anyaway and in such a way that we hope will increase our chances
to defragment the drive. We gonna take the square root of the number of clusters in the file.
Then we look for that many runs (or less) of that many free clusters per run (or more).
This routine finds this set of free cluster runs and builds an extent list to describe the set.
Then Another routine is called to move the file into these free space extents.
Since the purpose here is to get this file out of the way so the rest of the volume can be
defragmented, the search is done from the end of the volume backward.
pVolData->hFreeExtents = free space extent list, pVolData->FreeExtents = number of free space extents.
INPUT + OUTPUT:
None.
GLOBALS:
IN OUT Various VolData fields.
RETURN:
TRUE - Success
FALSE - Failure
*/
/*
BOOL
FindLastFreeSpaceChunks(
)
{
BOOL bRetStatus = TRUE;
VOLUME_BITMAP_BUFFER* pVolumeBitmap = NULL;
ULONG* pBitmap = NULL;
LONGLONG FirstLcn = VolData.StartingLcn;
LONGLONG LastLcn = VolData.DestEndLcn;
LONGLONG Lcn;
LONGLONG StartingLcn;
LONGLONG ClusterCount;
LONGLONG RunLength = VolData.NumberOfClusters;
EXTENT_LIST* pExtentList = NULL;
LONGLONG ExtentLength;
LONGLONG FreeExtent = 0;
LONGLONG Extent;
LONGLONG LastStartingLcn;
LONGLONG LastClusterCount;
LONGLONG FoundClusterCount = 0;
TCHAR cString[500];
Message(TEXT("FindLastFreeSpaceChunks"), -1, NULL);
//0.0E00 Preset clusters found to none in case not enough are found
VolData.Status = NEXT_ALGO_STEP;
VolData.FoundLen = 0;
__try {
#ifndef OFFLINEDK
//0.0E00 Round file's total cluster size up to the nearest 16.
if((VolData.FileSystem == FS_NTFS) && ((RunLength & 0x0F) != 0)) {
RunLength = (RunLength & 0xFFFFFFFFFFFFFFF0) + 0x10;
}
#endif
//0.0E00 Use the square root of the file's cluster count for number of extents and size of each extent
ExtentLength = (LONGLONG)sqrt((double)RunLength);
#ifndef OFFLINEDK
//0.0E00 Round cluster length of extents up to the nearest 16.
if((VolData.FileSystem == FS_NTFS) && ((ExtentLength & 0x0F) != 0)) {
ExtentLength = (ExtentLength & 0xFFFFFFFFFFFFFFF0) + 0x10;
}
#endif
//0.0E00 Number of extents = total clusters / minimum extent length
if (ExtentLength == 0){
bRetStatus = FALSE;
EH_ASSERT(FALSE);
__leave;
}
VolData.FreeExtents = (VolData.NumberOfClusters / ExtentLength) + ((VolData.NumberOfClusters % ExtentLength) ? 1 : 0);
//0.0E00 Allocate a buffer for the free space extent list
if (!AllocateMemory((DWORD)(VolData.FreeExtents * sizeof(EXTENT_LIST)), &VolData.hFreeExtents, (void**)&pExtentList)){
bRetStatus = FALSE;
EH_ASSERT(FALSE);
__leave;
}
//0.0E00 Lock the volume bitmap
pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
if (pVolumeBitmap == (PVOLUME_BITMAP_BUFFER) NULL){
bRetStatus = FALSE;
EH_ASSERT(FALSE);
__leave;
}
pBitmap = (PULONG)&pVolumeBitmap->Buffer;
//0.0E00 Find the set of the last free space extents of minimum size to hold the file
for(FoundClusterCount = 0, FreeExtent = 0;
FoundClusterCount < RunLength;
FoundClusterCount += LastClusterCount, FreeExtent ++) {
//0.0E00 Find the last extent that is long enough and not in the list yet
for(LastClusterCount = 0, Lcn = FirstLcn; Lcn < LastLcn; ) {
//0.0E00 Find the next extent
FindFreeExtent(pBitmap, LastLcn, &Lcn, &StartingLcn, &ClusterCount);
if(ClusterCount == 0) {
break;
}
//0.0E00 If this extent is long enough...
if(ClusterCount >= ExtentLength) {
//0.0E00 ...And not already in the list...
for(Extent = 0;
(Extent < FreeExtent) &&
(pExtentList[Extent].StartingLcn != StartingLcn);
Extent ++)
;
//0.0E00 ...Remember it
if(Extent >= FreeExtent) {
LastStartingLcn = StartingLcn;
LastClusterCount = ClusterCount;
}
}
}
if(LastClusterCount == 0) {
break;
}
#ifndef OFFLINEDK
//0.0E00 Round this extent's length down to the nearest 16.
if(VolData.FileSystem == FS_NTFS) {
LastClusterCount = LastClusterCount & 0xFFFFFFFFFFFFFFF0;
}
#endif
//0.0E00 Add this extent to the free space extent list
pExtentList[FreeExtent].StartingLcn = LastStartingLcn;
pExtentList[FreeExtent].ClusterCount = LastClusterCount;
}
//0.0E00 If we found extents larger than the minimum we may have less extents than expected
if(FreeExtent < VolData.FreeExtents) {
VolData.FreeExtents = FreeExtent;
if(VolData.FreeExtents == 0){
wsprintf(cString,TEXT("0x%lX free extents found for 0x%lX Clusters"), (ULONG)VolData.FreeExtents, (ULONG)RunLength);
Message(cString, -1, NULL);
VolData.Status = NEXT_FILE;
return FALSE;
}
//0.0E00 Reallocate the free space entent list
if (!AllocateMemory((DWORD)(VolData.FreeExtents * sizeof(EXTENT_LIST)), &VolData.hFreeExtents, (void**)&pExtentList)){
bRetStatus = FALSE;
EH_ASSERT(FALSE);
__leave;
}
}
wsprintf(cString,TEXT("Found 0x%lX free extents for 0x%lX Clusters"), (ULONG)VolData.FreeExtents, (ULONG)RunLength);
Message(cString, -1, NULL);
//0.0E00 Flag that we were successful
VolData.FoundLcn = pExtentList->StartingLcn;
VolData.FoundLen = FoundClusterCount;
bRetStatus = TRUE;
}
__finally {
if (VolData.hVolumeBitmap) {
GlobalUnlock(VolData.hVolumeBitmap);
}
if (VolData.hFreeExtents) {
GlobalUnlock(VolData.hFreeExtents);
}
}
return bRetStatus;
}
*/
/*****************************************************************************************************************
COPYRIGHT<EFBFBD> 2001 Microsoft Corporation and Executive Software International, Inc.
ROUTINE DESCRIPTION:
Counts the number of free spaces in the bitmap, and calculates stats about the free spaces.
INPUT + OUTPUT:
GLOBALS:
LONGLONG NumFreeSpaces; //The number of free spaces on the volume.
LONGLONG SmallestFreeSpaceClusters; //The size of the smallest free space in clusters.
LONGLONG LargestFreeSpaceClusters; //The size of the largest free space in clusters.
LONGLONG AveFreeSpaceClusters; //The average size of the free spaces in clusters.
RETURN:
TRUE - Success
FALSE - Failure
*/
BOOL
CountFreeSpaces(
)
{
PULONG pBitmap;
LONGLONG LastLcn = 0;
LONGLONG Lcn = 0;
LONGLONG StartingLcn = 0;
LONGLONG ClusterCount = 0;
VOLUME_BITMAP_BUFFER* pVolumeBitmap = NULL;
//Initialize the VolData fields.
VolData.NumFreeSpaces = 0;
VolData.SmallestFreeSpaceClusters = 0x7FFFFFFFFFFFFFFF;
VolData.LargestFreeSpaceClusters = 0;
VolData.AveFreeSpaceClusters = 0;
//0.0E00 Lock the volume bitmap
pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
EF_ASSERT(pVolumeBitmap);
//Get a pointer to the bitmap.
pBitmap = (PULONG)&pVolumeBitmap->Buffer;
//Get the length of the bitmap.
LastLcn = VolData.TotalClusters;
//This loop will cycle until the end of the volume.
do{
//Find the next extent
FindFreeExtent(pBitmap, LastLcn, &Lcn, &StartingLcn, &ClusterCount);
//Check to make sure a free space was found.
if(ClusterCount == 0) {
break;
}
//Note the running total of free space extents.
VolData.NumFreeSpaces++;
//Keep track of the smallest free space extent.
if(VolData.SmallestFreeSpaceClusters > ClusterCount){
VolData.SmallestFreeSpaceClusters = ClusterCount;
}
//Keep track of the largest free space extent.
if(VolData.LargestFreeSpaceClusters < ClusterCount){
VolData.LargestFreeSpaceClusters = ClusterCount;
}
//Reset StartingLcn to point to the next position to check from.
StartingLcn += ClusterCount;
//Reset ClusterCount.
ClusterCount = 0;
}while(StartingLcn < VolData.TotalClusters);
//Calculate the average free space extent size.
if(VolData.NumFreeSpaces){
VolData.AveFreeSpaceClusters = (VolData.TotalClusters - VolData.UsedClusters) / VolData.NumFreeSpaces;
}
else{
VolData.AveFreeSpaceClusters = 0;
}
GlobalUnlock(VolData.hVolumeBitmap);
return TRUE;
}
/*****************************************************************************************************************
COPYRIGHT<EFBFBD> 2001 Microsoft Corporation and Executive Software International, Inc.
ROUTINE DESCRIPTION:
Examines the freespace map of the volume and does statistical breakdown on it for
debugging purposes.
29Apr99 - Also summarizes "truly usable freespace" so that user can be informed if
volume is getting too full for defragmentation.
OUTPUT:
LONGLONG * llUsableFreeClusters - clusters that are actually usable
RETURN:
TRUE - Success
FALSE - Failure
*/
BOOL
DetermineUsableFreespace(
LONGLONG *llUsableFreeClusters
)
{
const MAX_BUCKET = 16;
struct {
UINT uLowerBound;
UINT uUpperBound;
LONGLONG llFragCount;
LONGLONG llTotalFragSize;
} freeBuckets[MAX_BUCKET] = {0};
LONGLONG llPercentOfFreeFragments;
LONGLONG llPercentOfFreeSpace;
LONGLONG llTotalFreeClusters = 0;
LONGLONG llTotalFreeFragments = 0;
LONGLONG SearchStartLcn = 0;
LONGLONG FreeStartLcn;
LONGLONG FreeCount;
TCHAR cOutline[1024];
TCHAR cTemp[20];
UINT iBucket;
LONGLONG llSumTotalFreeClusters = 0;
LONGLONG llSumUsableFreeClusters = 0;
LONGLONG llSumUnusableFreeClusters = 0;
Trace(log, " ");
Trace(log, "Start: Determine UsableFreeSpace");
//clear return
*llUsableFreeClusters = 0;
//1.0E00 Load the volume bitmap. (MFT-excluded)
EF(MarkMFTUnavailable());
//0.0E00 Lock and grab a pointer to the volume bitmap
PVOLUME_BITMAP_BUFFER pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
EF(pVolumeBitmap);
//set up the first bucket's bounds
freeBuckets[0].uLowerBound = 0;
freeBuckets[0].uUpperBound = 2;
//then fill in the parameters for the remainder of the buckets
for (iBucket=1; iBucket<MAX_BUCKET; iBucket++) {
//ensure the low bound of the bucket is the last bucket's high side plus one
freeBuckets[iBucket].uLowerBound = freeBuckets[iBucket-1].uUpperBound + 1;
//make the high bound of the bucket 2x the last bucket's high bound
freeBuckets[iBucket].uUpperBound = freeBuckets[iBucket-1].uUpperBound * 2;
}
//last bucket dump headers
Trace(log, " last bucket detail:");
Trace(log, " LCN LEN");
Trace(log, " -------- --------");
//this is just a copy of the loop that "prints the freespace clusters"
//above. Basically, it just loops through the free space, gathering
//the statistics for each fragment of free space, starting with cluster zero
SearchStartLcn = 0;
while (SearchStartLcn != -1) {
// go get a the next chunk of free space
FindFreeExtent((PULONG) pVolumeBitmap->Buffer, // pointer to the volume bitmap gotten from locking
VolData.TotalClusters, // end of range
&SearchStartLcn, // where to start
&FreeStartLcn, // where the free block starts
&FreeCount); // how big the block is (zero if done)
llSumTotalFreeClusters += FreeCount;
llSumUsableFreeClusters += FreeCount;
//a return value of zero means the end of the bitmap has
//been hit, so if we hit the end of the bitmap, stop the
//loop.
if (FreeCount == 0) {
SearchStartLcn = -1;
}
else { //we got a value, so do the statistics on it.
*llUsableFreeClusters += FreeCount;
//see if the free space fragment fits in a bucket
//(I guess this could have been done with a binary search
//method, but why bother for 16 items?)
BOOL bFoundTheBucket = FALSE;
for (iBucket=0; iBucket < MAX_BUCKET && !bFoundTheBucket; iBucket++) {
if (FreeCount >= freeBuckets[iBucket].uLowerBound &&
FreeCount <= freeBuckets[iBucket].uUpperBound) {
//if it fits in this bucket:
//(a) increment the fragment count for this bucket
freeBuckets[iBucket].llFragCount++;
//(b) add up the size of the fragments in this bucket
freeBuckets[iBucket].llTotalFragSize += FreeCount;
//(c) add it into the total free space count
llTotalFreeClusters += FreeCount;
//(d) and count it on the total fragment count
llTotalFreeFragments++;
//then set the flag saying the buck for this
//free space was found. (the loop will then end)
bFoundTheBucket = TRUE;
}
}
//if we got here and it didn't fit in a bucket, just put it
//in the last bucket
if (!bFoundTheBucket) {
iBucket = MAX_BUCKET - 1;
freeBuckets[iBucket].llFragCount++;
freeBuckets[iBucket].llTotalFragSize += FreeCount;
llTotalFreeClusters += FreeCount;
llTotalFreeFragments++;
}
//dump the last bucket
if (iBucket == MAX_BUCKET - 1) {
Trace(log, " %8lu %8lu", (ULONG) FreeStartLcn, (ULONG) FreeCount);
}
} //end else (i.e. FoundCount != 0)
} // end while searchstartlcn != -1
//last bucket dump footers
Trace(log, " -------- --------", -1, NULL);
Trace(log, " total: %8lu", (ULONG) freeBuckets[MAX_BUCKET - 1].llTotalFragSize);
//ensure no division by zero
if (llTotalFreeClusters == 0) llTotalFreeClusters = 1;
if (llTotalFreeFragments == 0) llTotalFreeFragments = 1;
//print the statistics header
Trace(log, " ");
Trace(log, " Occurs x Totalled %% of %% of ");
Trace(log, " Freespace of sizes times clusters Fragments Free Space ");
Trace(log, " ------------------ -------- --------- --------- ----------- ");
//dump the data
for (iBucket = 0; iBucket<MAX_BUCKET; iBucket++) {
//compute the percentage this bucket is of the free fragments
llPercentOfFreeFragments = (1000000 * freeBuckets[iBucket].llFragCount) / llTotalFreeFragments;
//compute the percentage this bucket is of the free clusters
llPercentOfFreeSpace = (1000000 * freeBuckets[iBucket].llTotalFragSize) / llTotalFreeClusters;
//special case; if the bucket is the last one, just say "maximum" instead of the
//value as the upper bound, since that's how the code above works
if (iBucket == (MAX_BUCKET - 1)) {
wsprintf (cTemp, TEXT(" maximum"));
}
else {
wsprintf (cTemp, TEXT("%8lu"), (ULONG)freeBuckets[iBucket].uUpperBound);
}
//now we can print the detail line; note the quad percent signs, this
//is what we need in the boot-time code; it may not be needed elsewhere
Trace(log, " %8lu-%8s %8lu %8lu %3ld.%04ld%% %3ld.%04ld%% ",
(ULONG) freeBuckets[iBucket].uLowerBound,
cTemp,
(ULONG) freeBuckets[iBucket].llFragCount,
(ULONG) freeBuckets[iBucket].llTotalFragSize,
(LONG) llPercentOfFreeFragments/10000,
(LONG) llPercentOfFreeFragments%10000,
(LONG) llPercentOfFreeSpace/10000,
(LONG) llPercentOfFreeSpace%10000);
} //end the loop (iBucket=0->MAX_BUCKET-1)
// print some summary data
Trace(log, " ------------------ -------- --------- --------- -----------");
//such as the total clusters and fragments; this is a bit redundant, but if
//it's all printed, some self-consistency checks can be done (and bugs located)
Trace(log, " Totals: %8lu %8lu ",
(ULONG) llTotalFreeFragments,
(ULONG)llTotalFreeClusters);
//compute the percentage this bucket is of the free fragments
LONGLONG llUsableFreePercent = 100;
if (VolData.TotalClusters != 0) {
llUsableFreePercent = (1000000 * *llUsableFreeClusters) / VolData.TotalClusters;
}
//additionally, report the free space truly usable by the defragmenter
Trace(log, " ");
Trace(log, " Usable free space: %8lu clusters (%3ld.%04ld%% of volume space)",
(ULONG) *llUsableFreeClusters,
(LONG) llUsableFreePercent / 10000,
(LONG) llUsableFreePercent % 10000);
LONGLONG llCalcFreeClusters;
LONGLONG llCalcFreePercent = 100;
Trace(log, " Sum usable free space: %I64d clusters", llSumUsableFreeClusters);
Trace(log, " Sum unusable free space: %I64d clusters", llSumUnusableFreeClusters);
Trace(log, " Sum total free space: %I64d clusters", llSumTotalFreeClusters);
Trace(log, " ");
Trace(log, " Total free space: %I64d clusters", VolData.TotalClusters - VolData.UsedClusters);
Trace(log, " - MFT zone: %I64d clusters", VolData.MftZoneEnd - VolData.MftZoneStart);
Trace(log, " - sum unusable free space: %I64d clusters", llSumUnusableFreeClusters);
llCalcFreeClusters = VolData.TotalClusters - VolData.UsedClusters -
(VolData.MftZoneEnd - VolData.MftZoneStart) - llSumUnusableFreeClusters;
if (VolData.TotalClusters != 0) {
llCalcFreePercent = (1000000 * llCalcFreeClusters) / VolData.TotalClusters;
}
Trace(log, " = Usable free space: %I64d clusters (%3ld.%02ld%%)",
llCalcFreeClusters, (LONG) llCalcFreePercent / 10000, (LONG) llCalcFreePercent % 10000);
Trace(log, " ");
assert(llSumTotalFreeClusters == llSumUsableFreeClusters + llSumUnusableFreeClusters);
assert(llSumUsableFreeClusters == *llUsableFreeClusters);
if(VolData.hVolumeBitmap) {
GlobalUnlock(VolData.hVolumeBitmap);
}
Trace(log, "End: Determine UsableFreeSpace");
return TRUE;
}