reactos/modules/rosapps/applications/fraginator/DriveVolume.cpp

806 lines
20 KiB
C++

#include "DriveVolume.h"
DriveVolume::DriveVolume ()
{
Handle = INVALID_HANDLE_VALUE;
BitmapDetail = NULL;
return;
}
DriveVolume::~DriveVolume ()
{
Close ();
Directories.clear ();
Files.clear ();
return;
}
void DriveVolume::Close (void)
{
if (Handle != INVALID_HANDLE_VALUE)
{
CloseHandle (Handle);
Handle = INVALID_HANDLE_VALUE;
}
if (BitmapDetail != NULL)
{
free (BitmapDetail);
BitmapDetail = NULL;
}
return;
}
// "Name" should be the drive letter followed by a colon. ie, "c:"
// It's a string to allow for further expansion (ie, defragging over the network?)
// or some other baloney reason
bool DriveVolume::Open (wstring Name)
{
wchar_t FileName[100];
bool ReturnVal;
swprintf (FileName, L"\\\\.\\%s", Name.c_str());
RootPath = Name.c_str();
RootPath += L"\\";
Handle = CreateFileW
(
FileName,
MAXIMUM_ALLOWED, // access
FILE_SHARE_READ | FILE_SHARE_WRITE, // share type
NULL, // security descriptor
OPEN_EXISTING, // open type
0, // attributes (none)
NULL // template
);
if (Handle == INVALID_HANDLE_VALUE)
ReturnVal = false;
else
{
wchar_t VolName[64];
DWORD VolSN;
DWORD VolMaxFileLen;
DWORD FSFlags;
wchar_t FSName[64];
BOOL Result;
ReturnVal = true;
Result = GetVolumeInformationW
(
RootPath.c_str(),
VolName,
sizeof (VolName),
&VolSN,
&VolMaxFileLen,
&FSFlags,
FSName,
sizeof (FSName)
);
if (Result)
{
wchar_t SerialText[10];
VolInfo.FileSystem = FSName;
VolInfo.MaxNameLen = VolMaxFileLen;
VolInfo.Name = VolName;
swprintf (SerialText, L"%x-%x", (VolSN & 0xffff0000) >> 16,
VolSN & 0x0000ffff);
_wcsupr (SerialText);
VolInfo.Serial = SerialText;
}
else
{
VolInfo.FileSystem = L"(Unknown)";
VolInfo.MaxNameLen = 255;
VolInfo.Name = L"(Unknown)";
VolInfo.Serial = L"(Unknown)";
}
}
return (ReturnVal);
}
bool DriveVolume::ObtainInfo (void)
{
BOOL Result;
DWORD BytesGot;
uint64 nan;
BytesGot = 0;
ZeroMemory (&Geometry, sizeof (Geometry));
Result = DeviceIoControl
(
Handle,
IOCTL_DISK_GET_DRIVE_GEOMETRY,
NULL,
0,
&Geometry,
sizeof (Geometry),
&BytesGot,
NULL
);
// Call failed? Aww :(
if (!Result)
return (false);
// Get cluster size
DWORD SectorsPerCluster;
DWORD BytesPerSector;
DWORD FreeClusters;
DWORD TotalClusters;
Result = GetDiskFreeSpaceW
(
RootPath.c_str(),
&SectorsPerCluster,
&BytesPerSector,
&FreeClusters,
&TotalClusters
);
// Failed? Weird.
if (!Result)
return (false);
VolInfo.ClusterSize = SectorsPerCluster * BytesPerSector;
Result = GetDiskFreeSpaceExW
(
RootPath.c_str(),
(PULARGE_INTEGER)&nan,
(PULARGE_INTEGER)&VolInfo.TotalBytes,
(PULARGE_INTEGER)&VolInfo.FreeBytes
);
return (true);
}
// Get bitmap, several clusters at a time ...
#define CLUSTERS 4096
bool DriveVolume::GetBitmap (void)
{
STARTING_LCN_INPUT_BUFFER StartingLCN;
VOLUME_BITMAP_BUFFER *Bitmap = NULL;
uint32 BitmapSize;
DWORD BytesReturned;
BOOL Result;
StartingLCN.StartingLcn.QuadPart = 0;
// Allocate buffer
// Call FSCTL_GET_VOLUME_BITMAP once with a very small buffer
// This will leave the total number of clusters in Bitmap->BitmapSize and we can
// then correctly allocate based off that
// I suppose this won't work if your drive has only 40 clusters on it or so :)
BitmapSize = sizeof (VOLUME_BITMAP_BUFFER) + 4;
Bitmap = (VOLUME_BITMAP_BUFFER *) malloc (BitmapSize);
Result = DeviceIoControl
(
Handle,
FSCTL_GET_VOLUME_BITMAP,
&StartingLCN,
sizeof (StartingLCN),
Bitmap,
BitmapSize,
&BytesReturned,
NULL
);
// Bad result?
if (Result == FALSE && GetLastError () != ERROR_MORE_DATA)
{
//wprintf ("\nDeviceIoControl returned false, GetLastError() was not ERROR_MORE_DATA\n");
free (Bitmap);
return (false);
}
// Otherwise, we're good
BitmapSize = sizeof (VOLUME_BITMAP_BUFFER) + (Bitmap->BitmapSize.QuadPart / 8) + 1;
Bitmap = (VOLUME_BITMAP_BUFFER *) realloc (Bitmap, BitmapSize);
Result = DeviceIoControl
(
Handle,
FSCTL_GET_VOLUME_BITMAP,
&StartingLCN,
sizeof (StartingLCN),
Bitmap,
BitmapSize,
&BytesReturned,
NULL
);
//DWORD LastError = GetLastError ();
if (Result == FALSE)
{
wprintf (L"\nCouldn't properly read volume bitmap\n");
free (Bitmap);
return (false);
}
// Convert to a L'quick use' bitmap
//const int BitShift[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
VolInfo.ClusterCount = Bitmap->BitmapSize.QuadPart;
if (BitmapDetail != NULL)
free (BitmapDetail);
BitmapDetail = (uint32 *) malloc (sizeof(uint32) * (1 + (VolInfo.ClusterCount / 32)));
memcpy (BitmapDetail, Bitmap->Buffer, sizeof(uint32) * (1 + (VolInfo.ClusterCount / 32)));
/*
BitmapDetail = (Cluster *) malloc (VolInfo.ClusterCount * sizeof (Cluster));
for (uint64 i = 0; i < VolInfo.ClusterCount; i++)
{
if (Bitmap->Buffer[i / 8] & BitShift[i % 8])
BitmapDetail[i].Allocated = true;
else
BitmapDetail[i].Allocated = false;
}
*/
free (Bitmap);
return (true);
}
bool DriveVolume::IsClusterUsed (uint64 Cluster)
{
return ((BitmapDetail[Cluster / 32] & (1 << (Cluster % 32))) ? true : false);
//return (BitmapDetail[Cluster].Allocated);
}
void DriveVolume::SetClusterUsed (uint64 Cluster, bool Used)
{
if (Used)
BitmapDetail[Cluster / 32] |= (1 << (Cluster % 32));
else
BitmapDetail[Cluster / 32] &= ~(1 << (Cluster % 32));
return;
}
typedef struct
{
DriveVolume *Volume;
double *Percent;
bool *QuitMonitor;
uint64 ClusterCount;
uint64 ClusterProgress;
} BuildDBInfo;
bool DriveVolume::BuildFileList (bool &QuitMonitor, double &Percent)
{
BuildDBInfo Info;
Files.clear ();
Directories.clear ();
Directories.push_back (RootPath);
Info.Volume = this;
Info.QuitMonitor = &QuitMonitor;
Info.ClusterCount = (GetVolumeInfo().TotalBytes - GetVolumeInfo().FreeBytes) / (uint64)GetVolumeInfo().ClusterSize;
Info.ClusterProgress = 0;
Info.Percent = &Percent;
ScanDirectory (RootPath, BuildDBCallback, &Info);
if (QuitMonitor == true)
{
Directories.resize (0);
Files.resize (0);
}
return (true);
}
// UserData = pointer to BuildDBInfo instance
bool BuildDBCallback (FileInfo &Info, HANDLE &FileHandle, void *UserData)
{
BuildDBInfo *DBInfo = (BuildDBInfo *) UserData;
DriveVolume *Vol = DBInfo->Volume;
Vol->Files.push_back (Info);
if (*(DBInfo->QuitMonitor) == true)
return (false);
DBInfo->ClusterProgress += (uint64)Info.Clusters;
*(DBInfo->Percent) =
((double)DBInfo->ClusterProgress / (double)DBInfo->ClusterCount) * 100.0f;
return (true);
}
wstring &DriveVolume::GetDBDir (uint32 Indice)
{
return (Directories[Indice]);
}
uint32 DriveVolume::GetDBDirCount (void)
{
return (Directories.size());
}
FileInfo &DriveVolume::GetDBFile (uint32 Indice)
{
return (Files[Indice]);
}
uint32 DriveVolume::GetDBFileCount (void)
{
return (Files.size());
}
uint32 DriveVolume::RemoveDBFile (uint32 Indice)
{
vector<FileInfo>::iterator it;
it = Files.begin() + Indice;
Files.erase (it);
return (GetDBFileCount());
}
bool DriveVolume::ScanDirectory (wstring DirPrefix, ScanCallback Callback, void *UserData)
{
WIN32_FIND_DATAW FindData;
HANDLE FindHandle;
wstring SearchString;
uint32 DirIndice;
DirIndice = Directories.size() - 1;
SearchString = DirPrefix;
SearchString += L"*.*";
ZeroMemory (&FindData, sizeof (FindData));
FindHandle = FindFirstFileW (SearchString.c_str(), &FindData);
if (FindHandle == INVALID_HANDLE_VALUE)
return (false);
do
{
FileInfo Info;
HANDLE Handle;
bool CallbackResult;
Handle = INVALID_HANDLE_VALUE;
// First copy over the easy stuff.
Info.Name = FindData.cFileName;
// DonLL't ever include '.L' and '..'
if (Info.Name == L"." || Info.Name == L"..")
continue;
//Info.FullName = DirPrefix + Info.Name;
Info.Size = (uint64)FindData.nFileSizeLow + ((uint64)FindData.nFileSizeHigh << (uint64)32);
Info.DirIndice = DirIndice;
Info.Attributes.Archive = (FindData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) ? 1 : 0;
Info.Attributes.Compressed = (FindData.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) ? 1 : 0;
Info.Attributes.Directory = (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;
Info.Attributes.Encrypted = (FindData.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) ? 1 : 0;
Info.Attributes.Hidden = (FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ? 1 : 0;
Info.Attributes.Normal = (FindData.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ? 1 : 0;
Info.Attributes.Offline = (FindData.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) ? 1 : 0;
Info.Attributes.ReadOnly = (FindData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? 1 : 0;
Info.Attributes.Reparse = (FindData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? 1 : 0;
Info.Attributes.Sparse = (FindData.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) ? 1 : 0;
Info.Attributes.System = (FindData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) ? 1 : 0;
Info.Attributes.Temporary = (FindData.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) ? 1 : 0;
Info.Attributes.AccessDenied = 0;
Info.Attributes.Unmovable = 0;
Info.Attributes.Process = 1;
Info.Clusters = 0;
if (GetClusterInfo (Info, Handle))
{
uint64 TotalClusters = 0;
for (size_t i = 0; i < Info.Fragments.size(); i++)
{
TotalClusters += Info.Fragments[i].Length;
}
Info.Clusters = TotalClusters;
}
else
{
Info.Attributes.Unmovable = 1;
Info.Attributes.Process = 0;
}
if (Info.Attributes.Process == 1)
Info.Attributes.Process = ShouldProcess (Info.Attributes) ? 1 : 0;
// Run the user-defined callback function
CallbackResult = Callback (Info, Handle, UserData);
if (Handle != INVALID_HANDLE_VALUE)
CloseHandle (Handle);
if (!CallbackResult)
break;
// If directory, perform recursion
if (Info.Attributes.Directory == 1)
{
wstring Dir;
Dir = GetDBDir (Info.DirIndice);
Dir += Info.Name;
Dir += L"\\";
Directories.push_back (Dir);
ScanDirectory (Dir, Callback, UserData);
}
} while (FindNextFileW (FindHandle, &FindData) == TRUE);
FindClose (FindHandle);
return (false);
}
bool DriveVolume::ShouldProcess (FileAttr Attr)
{
if (Attr.Offline == 1 || Attr.Reparse == 1 || Attr.Temporary == 1)
{
return (false);
}
return (true);
}
// Gets info on a file and returns a valid handle for read/write access
// Name, FullName, Clusters, Attributes, and Size should already be filled out.
// This function fills in the Fragments vector
bool DriveVolume::GetClusterInfo (FileInfo &Info, HANDLE &HandleResult)
{
BOOL Result;
HANDLE Handle;
wstring FullName;
BY_HANDLE_FILE_INFORMATION FileInfo;
Info.Fragments.resize (0);
/*
if (Info.Attributes.Directory == 1)
return (false);
*/
FullName = GetDBDir (Info.DirIndice) + Info.Name;
Handle = CreateFileW
(
FullName.c_str(),
0, //GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
(Info.Attributes.Directory == 1) ? FILE_FLAG_BACKUP_SEMANTICS : 0,
NULL
);
if (Handle == INVALID_HANDLE_VALUE)
{
LPVOID lpMsgBuf;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf, 0, NULL );
Info.Attributes.AccessDenied = 1;
LocalFree( lpMsgBuf );
return (false);
}
ZeroMemory (&FileInfo, sizeof (FileInfo));
Result = GetFileInformationByHandle (Handle, &FileInfo);
if (Result == FALSE)
{
Info.Attributes.AccessDenied = 1;
wprintf (L"GetFileInformationByHandle ('%s%s') failed\n", GetDBDir (Info.DirIndice).c_str(),
Info.Name.c_str());
CloseHandle (Handle);
return (false);
}
// Get cluster allocation information
STARTING_VCN_INPUT_BUFFER StartingVCN;
RETRIEVAL_POINTERS_BUFFER *Retrieval;
uint64 RetSize;
uint64 Extents;
DWORD BytesReturned;
// Grab info one extent at a time, until it's done grabbing all the extent data
// Yeah, well it doesn't give us a way to ask L"how many extents?" that I know of ...
// btw, the Extents variable tends to only reflect memory usage, so when we have
// all the extents we look at the structure Win32 gives us for the REAL count!
Extents = 10;
Retrieval = NULL;
RetSize = 0;
StartingVCN.StartingVcn.QuadPart = 0;
do
{
Extents *= 2;
RetSize = sizeof (RETRIEVAL_POINTERS_BUFFER) + ((Extents - 1) * sizeof (LARGE_INTEGER) * 2);
if (Retrieval != NULL)
Retrieval = (RETRIEVAL_POINTERS_BUFFER *) realloc (Retrieval, RetSize);
else
Retrieval = (RETRIEVAL_POINTERS_BUFFER *) malloc (RetSize);
Result = DeviceIoControl
(
Handle,
FSCTL_GET_RETRIEVAL_POINTERS,
&StartingVCN,
sizeof (StartingVCN),
Retrieval,
RetSize,
&BytesReturned,
NULL
);
if (Result == FALSE)
{
if (GetLastError() != ERROR_MORE_DATA)
{
Info.Clusters = 0;
Info.Attributes.AccessDenied = 1;
Info.Attributes.Process = 0;
Info.Fragments.clear ();
CloseHandle (Handle);
free (Retrieval);
return (false);
}
Extents++;
}
} while (Result == FALSE);
// Readjust extents, as it only reflects how much memory was allocated and may not
// be accurate
Extents = Retrieval->ExtentCount;
// Ok, we have the info. Now translate it. hrmrmr
Info.Fragments.clear ();
for (uint64 i = 0; i < Extents; i++)
{
Extent Add;
Add.StartLCN = Retrieval->Extents[i].Lcn.QuadPart;
if (i != 0)
Add.Length = Retrieval->Extents[i].NextVcn.QuadPart - Retrieval->Extents[i - 1].NextVcn.QuadPart;
else
Add.Length = Retrieval->Extents[i].NextVcn.QuadPart - Retrieval->StartingVcn.QuadPart;
Info.Fragments.push_back (Add);
}
free (Retrieval);
HandleResult = Handle;
return (true);
}
bool DriveVolume::FindFreeRange (uint64 StartLCN, uint64 ReqLength, uint64 &LCNResult)
{
uint64 Max;
uint64 i;
uint64 j;
// Make sure we don't spill over our array
Max = VolInfo.ClusterCount - ReqLength;
for (i = StartLCN; i < Max; i++)
{
bool Found = true;
// First check the first cluster
if (IsClusterUsed (i))
Found = false;
else
// THen check the last cluster
if (IsClusterUsed (i + ReqLength - 1))
Found = false;
else
// Check the whole darn range.
for (j = (i + 1); j < (i + ReqLength - 2); j++)
{
if (IsClusterUsed (j) == true)
{
Found = false;
break;
}
}
if (!Found)
continue;
else
{
LCNResult = i;
return (true);
}
}
return (false);
}
// btw we have to move each fragment of the file, as per the Win32 API
bool DriveVolume::MoveFileDumb (uint32 FileIndice, uint64 NewLCN)
{
bool ReturnVal = false;
FileInfo Info;
HANDLE FileHandle;
wstring FullName;
MOVE_FILE_DATA MoveData;
uint64 CurrentLCN;
uint64 CurrentVCN;
// Set up variables
Info = GetDBFile (FileIndice);
FullName = GetDBDir (Info.DirIndice);
FullName += Info.Name;
CurrentLCN = NewLCN;
CurrentVCN = 0;
/*
if (Info.Attributes.Directory == 1)
{
//
}
*/
// Open file
FileHandle = CreateFileW
(
FullName.c_str (),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
(Info.Attributes.Directory == 1) ? FILE_FLAG_BACKUP_SEMANTICS : 0,
NULL
);
if (FileHandle == INVALID_HANDLE_VALUE)
{
//
LPVOID lpMsgBuf;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf, 0, NULL );
LocalFree (lpMsgBuf);
//
ReturnVal = false;
}
else
{
ReturnVal = true; // innocent until proven guilty ...
for (uint32 i = 0; i < Info.Fragments.size(); i++)
{
BOOL Result;
DWORD BytesReturned;
//wprintf (L"%3u", i);
MoveData.ClusterCount = Info.Fragments[i].Length;
MoveData.StartingLcn.QuadPart = CurrentLCN;
MoveData.StartingVcn.QuadPart = CurrentVCN;
MoveData.FileHandle = FileHandle;
/*
wprintf (L"\n");
wprintf (L"StartLCN: %I64u\n", MoveData.StartingLcn.QuadPart);
wprintf (L"StartVCN: %I64u\n", MoveData.StartingVcn.QuadPart);
wprintf (L"Clusters: %u (%I64u-%I64u --> %I64u-%I64u)\n", MoveData.ClusterCount,
Info.Fragments[i].StartLCN,
Info.Fragments[i].StartLCN + MoveData.ClusterCount,
MoveData.StartingLcn.QuadPart,
MoveData.StartingLcn.QuadPart + MoveData.ClusterCount - 1);
wprintf (L"\n");
*/
Result = DeviceIoControl
(
Handle,
FSCTL_MOVE_FILE,
&MoveData,
sizeof (MoveData),
NULL,
0,
&BytesReturned,
NULL
);
//wprintf (L"\b\b\b");
if (Result == FALSE)
{
//
LPVOID lpMsgBuf;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf, 0, NULL );
LocalFree( lpMsgBuf );
//
ReturnVal = false;
goto FinishUp; // yeah, bite me
}
// Ok good. Now update our drive bitmap and file infos.
uint64 j;
for (j = 0;
j < Info.Fragments[i].Length;
j++)
{
SetClusterUsed (Info.Fragments[i].StartLCN + j, false);
SetClusterUsed (CurrentLCN + j, true);
//BitmapDetail[Info.Fragments[i].StartLCN + j].Allocated = false;
//BitmapDetail[CurrentLCN + j].Allocated = true;
}
CurrentLCN += Info.Fragments[i].Length;
CurrentVCN += Info.Fragments[i].Length;
}
// Update file info either way
FinishUp:
CloseHandle (FileHandle);
FileHandle = INVALID_HANDLE_VALUE;
GetClusterInfo (Files[FileIndice], FileHandle);
CloseHandle (FileHandle);
}
return (ReturnVal);
}