Something went wrong. Try again.
Reactos
Something went wrong. Try again.
21 kB · 817 lines
C++
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818#include "DriveVolume.h"
DriveVolume::DriveVolume(){ Handle = INVALID_HANDLE_VALUE; BitmapDetail = NULL; ZeroMemory(&Geometry, sizeof(Geometry));}
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) { HeapFree(GetProcessHeap(), 0, 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 reasonbool 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 4096bool 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 *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 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"); HeapFree(GetProcessHeap(), 0, Bitmap); return (false); }
// Otherwise, we're good BitmapSize = sizeof (VOLUME_BITMAP_BUFFER) + (Bitmap->BitmapSize.QuadPart / 8) + 1;
void *reallBitmap = HeapReAlloc(GetProcessHeap(), 0, Bitmap, BitmapSize);
if (reallBitmap == NULL) { // Fail "miserably" wprintf(L"\nNot enough memory to read volume bitmap\n"); HeapFree(GetProcessHeap(), 0, Bitmap); return (false); }
Bitmap = (VOLUME_BITMAP_BUFFER *)reallBitmap; 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"); HeapFree(GetProcessHeap(), 0, 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) HeapFree(GetProcessHeap(), 0, BitmapDetail);
BitmapDetail = (uint32 *) HeapAlloc(GetProcessHeap(), 0, 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; } */
HeapFree(GetProcessHeap(), 0, 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 instancebool 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 vectorbool 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 APIbool 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);}