1 #include "DriveVolume.h"
4 DriveVolume::DriveVolume ()
6 Handle
= INVALID_HANDLE_VALUE
;
12 DriveVolume::~DriveVolume ()
21 void DriveVolume::Close (void)
23 if (Handle
!= INVALID_HANDLE_VALUE
)
26 Handle
= INVALID_HANDLE_VALUE
;
29 if (BitmapDetail
!= NULL
)
39 // "Name" should be the drive letter followed by a colon. ie, "c:"
40 // It's a string to allow for further expansion (ie, defragging over the network?)
41 // or some other baloney reason
42 bool DriveVolume::Open (wstring Name
)
44 wchar_t FileName
[100];
47 swprintf (FileName
, L
"\\\\.\\%s", Name
.c_str());
48 RootPath
= Name
.c_str();
54 MAXIMUM_ALLOWED
, // access
55 FILE_SHARE_READ
| FILE_SHARE_WRITE
, // share type
56 NULL
, // security descriptor
57 OPEN_EXISTING
, // open type
58 0, // attributes (none)
62 if (Handle
== INVALID_HANDLE_VALUE
)
74 Result
= GetVolumeInformationW
88 wchar_t SerialText
[10];
90 VolInfo
.FileSystem
= FSName
;
91 VolInfo
.MaxNameLen
= VolMaxFileLen
;
92 VolInfo
.Name
= VolName
;
94 swprintf (SerialText
, L
"%x-%x", (VolSN
& 0xffff0000) >> 16,
98 VolInfo
.Serial
= SerialText
;
102 VolInfo
.FileSystem
= L
"(Unknown)";
103 VolInfo
.MaxNameLen
= 255;
104 VolInfo
.Name
= L
"(Unknown)";
105 VolInfo
.Serial
= L
"(Unknown)";
113 bool DriveVolume::ObtainInfo (void)
120 ZeroMemory (&Geometry
, sizeof (Geometry
));
121 Result
= DeviceIoControl
124 IOCTL_DISK_GET_DRIVE_GEOMETRY
,
133 // Call failed? Aww :(
138 DWORD SectorsPerCluster
;
139 DWORD BytesPerSector
;
143 Result
= GetDiskFreeSpaceW
156 VolInfo
.ClusterSize
= SectorsPerCluster
* BytesPerSector
;
158 Result
= GetDiskFreeSpaceExW
161 (PULARGE_INTEGER
)&nan
,
162 (PULARGE_INTEGER
)&VolInfo
.TotalBytes
,
163 (PULARGE_INTEGER
)&VolInfo
.FreeBytes
170 // Get bitmap, several clusters at a time ...
171 #define CLUSTERS 4096
172 bool DriveVolume::GetBitmap (void)
174 STARTING_LCN_INPUT_BUFFER StartingLCN
;
175 VOLUME_BITMAP_BUFFER
*Bitmap
= NULL
;
180 StartingLCN
.StartingLcn
.QuadPart
= 0;
183 // Call FSCTL_GET_VOLUME_BITMAP once with a very small buffer
184 // This will leave the total number of clusters in Bitmap->BitmapSize and we can
185 // then correctly allocate based off that
186 // I suppose this won't work if your drive has only 40 clusters on it or so :)
187 BitmapSize
= sizeof (VOLUME_BITMAP_BUFFER
) + 4;
188 Bitmap
= (VOLUME_BITMAP_BUFFER
*) malloc (BitmapSize
);
190 Result
= DeviceIoControl
193 FSCTL_GET_VOLUME_BITMAP
,
195 sizeof (StartingLCN
),
203 if (Result
== FALSE
&& GetLastError () != ERROR_MORE_DATA
)
205 //wprintf ("\nDeviceIoControl returned false, GetLastError() was not ERROR_MORE_DATA\n");
210 // Otherwise, we're good
211 BitmapSize
= sizeof (VOLUME_BITMAP_BUFFER
) + (Bitmap
->BitmapSize
.QuadPart
/ 8) + 1;
212 Bitmap
= (VOLUME_BITMAP_BUFFER
*) realloc (Bitmap
, BitmapSize
);
213 Result
= DeviceIoControl
216 FSCTL_GET_VOLUME_BITMAP
,
218 sizeof (StartingLCN
),
225 //DWORD LastError = GetLastError ();
229 wprintf (L
"\nCouldn't properly read volume bitmap\n");
234 // Convert to a L'quick use' bitmap
235 //const int BitShift[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
237 VolInfo
.ClusterCount
= Bitmap
->BitmapSize
.QuadPart
;
239 if (BitmapDetail
!= NULL
)
242 BitmapDetail
= (uint32
*) malloc (sizeof(uint32
) * (1 + (VolInfo
.ClusterCount
/ 32)));
243 memcpy (BitmapDetail
, Bitmap
->Buffer
, sizeof(uint32
) * (1 + (VolInfo
.ClusterCount
/ 32)));
246 BitmapDetail = (Cluster *) malloc (VolInfo.ClusterCount * sizeof (Cluster));
247 for (uint64 i = 0; i < VolInfo.ClusterCount; i++)
249 if (Bitmap->Buffer[i / 8] & BitShift[i % 8])
250 BitmapDetail[i].Allocated = true;
252 BitmapDetail[i].Allocated = false;
261 bool DriveVolume::IsClusterUsed (uint64 Cluster
)
263 return ((BitmapDetail
[Cluster
/ 32] & (1 << (Cluster
% 32))) ? true : false);
264 //return (BitmapDetail[Cluster].Allocated);
268 void DriveVolume::SetClusterUsed (uint64 Cluster
, bool Used
)
271 BitmapDetail
[Cluster
/ 32] |= (1 << (Cluster
% 32));
273 BitmapDetail
[Cluster
/ 32] &= ~(1 << (Cluster
% 32));
285 uint64 ClusterProgress
;
289 bool DriveVolume::BuildFileList (bool &QuitMonitor
, double &Percent
)
294 Directories
.clear ();
295 Directories
.push_back (RootPath
);
298 Info
.QuitMonitor
= &QuitMonitor
;
299 Info
.ClusterCount
= (GetVolumeInfo().TotalBytes
- GetVolumeInfo().FreeBytes
) / (uint64
)GetVolumeInfo().ClusterSize
;
300 Info
.ClusterProgress
= 0;
301 Info
.Percent
= &Percent
;
303 ScanDirectory (RootPath
, BuildDBCallback
, &Info
);
305 if (QuitMonitor
== true)
307 Directories
.resize (0);
315 // UserData = pointer to BuildDBInfo instance
316 bool BuildDBCallback (FileInfo
&Info
, HANDLE
&FileHandle
, void *UserData
)
318 BuildDBInfo
*DBInfo
= (BuildDBInfo
*) UserData
;
319 DriveVolume
*Vol
= DBInfo
->Volume
;
321 Vol
->Files
.push_back (Info
);
323 if (*(DBInfo
->QuitMonitor
) == true)
326 DBInfo
->ClusterProgress
+= (uint64
)Info
.Clusters
;
328 ((double)DBInfo
->ClusterProgress
/ (double)DBInfo
->ClusterCount
) * 100.0f
;
334 wstring
&DriveVolume::GetDBDir (uint32 Indice
)
336 return (Directories
[Indice
]);
340 uint32
DriveVolume::GetDBDirCount (void)
342 return (Directories
.size());
346 FileInfo
&DriveVolume::GetDBFile (uint32 Indice
)
348 return (Files
[Indice
]);
352 uint32
DriveVolume::GetDBFileCount (void)
354 return (Files
.size());
358 uint32
DriveVolume::RemoveDBFile (uint32 Indice
)
360 vector
<FileInfo
>::iterator it
;
362 it
= Files
.begin() + Indice
;
364 return (GetDBFileCount());
368 bool DriveVolume::ScanDirectory (wstring DirPrefix
, ScanCallback Callback
, void *UserData
)
370 WIN32_FIND_DATAW FindData
;
372 wstring SearchString
;
375 DirIndice
= Directories
.size() - 1;
377 SearchString
= DirPrefix
;
378 SearchString
+= L
"*.*";
379 ZeroMemory (&FindData
, sizeof (FindData
));
380 FindHandle
= FindFirstFileW (SearchString
.c_str(), &FindData
);
382 if (FindHandle
== INVALID_HANDLE_VALUE
)
391 Handle
= INVALID_HANDLE_VALUE
;
393 // First copy over the easy stuff.
394 Info
.Name
= FindData
.cFileName
;
396 // DonLL't ever include '.L' and '..'
397 if (Info
.Name
== L
"." || Info
.Name
== L
"..")
400 //Info.FullName = DirPrefix + Info.Name;
401 Info
.Size
= (uint64
)FindData
.nFileSizeLow
+ ((uint64
)FindData
.nFileSizeHigh
<< (uint64
)32);
402 Info
.DirIndice
= DirIndice
;
404 Info
.Attributes
.Archive
= (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_ARCHIVE
) ? 1 : 0;
405 Info
.Attributes
.Compressed
= (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_COMPRESSED
) ? 1 : 0;
406 Info
.Attributes
.Directory
= (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) ? 1 : 0;
407 Info
.Attributes
.Encrypted
= (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_ENCRYPTED
) ? 1 : 0;
408 Info
.Attributes
.Hidden
= (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
) ? 1 : 0;
409 Info
.Attributes
.Normal
= (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_NORMAL
) ? 1 : 0;
410 Info
.Attributes
.Offline
= (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_OFFLINE
) ? 1 : 0;
411 Info
.Attributes
.ReadOnly
= (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
) ? 1 : 0;
412 Info
.Attributes
.Reparse
= (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) ? 1 : 0;
413 Info
.Attributes
.Sparse
= (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_SPARSE_FILE
) ? 1 : 0;
414 Info
.Attributes
.System
= (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_SYSTEM
) ? 1 : 0;
415 Info
.Attributes
.Temporary
= (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_TEMPORARY
) ? 1 : 0;
416 Info
.Attributes
.AccessDenied
= 0;
417 Info
.Attributes
.Unmovable
= 0;
418 Info
.Attributes
.Process
= 1;
421 if (GetClusterInfo (Info
, Handle
))
423 uint64 TotalClusters
= 0;
425 for (size_t i
= 0; i
< Info
.Fragments
.size(); i
++)
427 TotalClusters
+= Info
.Fragments
[i
].Length
;
430 Info
.Clusters
= TotalClusters
;
434 Info
.Attributes
.Unmovable
= 1;
435 Info
.Attributes
.Process
= 0;
438 if (Info
.Attributes
.Process
== 1)
439 Info
.Attributes
.Process
= ShouldProcess (Info
.Attributes
) ? 1 : 0;
441 // Run the user-defined callback function
442 CallbackResult
= Callback (Info
, Handle
, UserData
);
444 if (Handle
!= INVALID_HANDLE_VALUE
)
445 CloseHandle (Handle
);
450 // If directory, perform recursion
451 if (Info
.Attributes
.Directory
== 1)
455 Dir
= GetDBDir (Info
.DirIndice
);
459 Directories
.push_back (Dir
);
460 ScanDirectory (Dir
, Callback
, UserData
);
463 } while (FindNextFileW (FindHandle
, &FindData
) == TRUE
);
465 FindClose (FindHandle
);
470 bool DriveVolume::ShouldProcess (FileAttr Attr
)
472 if (Attr
.Offline
== 1 || Attr
.Reparse
== 1 || Attr
.Temporary
== 1)
481 // Gets info on a file and returns a valid handle for read/write access
482 // Name, FullName, Clusters, Attributes, and Size should already be filled out.
483 // This function fills in the Fragments vector
484 bool DriveVolume::GetClusterInfo (FileInfo
&Info
, HANDLE
&HandleResult
)
489 BY_HANDLE_FILE_INFORMATION FileInfo
;
491 Info
.Fragments
.resize (0);
494 if (Info.Attributes.Directory == 1)
498 FullName
= GetDBDir (Info
.DirIndice
) + Info
.Name
;
507 (Info
.Attributes
.Directory
== 1) ? FILE_FLAG_BACKUP_SEMANTICS
: 0,
511 if (Handle
== INVALID_HANDLE_VALUE
)
515 FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
,
516 NULL
, GetLastError(),
517 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
518 (LPTSTR
) &lpMsgBuf
, 0, NULL
);
521 Info
.Attributes
.AccessDenied
= 1;
522 LocalFree( lpMsgBuf
);
526 ZeroMemory (&FileInfo
, sizeof (FileInfo
));
527 Result
= GetFileInformationByHandle (Handle
, &FileInfo
);
531 Info
.Attributes
.AccessDenied
= 1;
532 wprintf (L
"GetFileInformationByHandle ('%s%s') failed\n", GetDBDir (Info
.DirIndice
).c_str(),
535 CloseHandle (Handle
);
539 // Get cluster allocation information
540 STARTING_VCN_INPUT_BUFFER StartingVCN
;
541 RETRIEVAL_POINTERS_BUFFER
*Retrieval
;
546 // Grab info one extent at a time, until it's done grabbing all the extent data
547 // Yeah, well it doesn't give us a way to ask L"how many extents?" that I know of ...
548 // btw, the Extents variable tends to only reflect memory usage, so when we have
549 // all the extents we look at the structure Win32 gives us for the REAL count!
553 StartingVCN
.StartingVcn
.QuadPart
= 0;
558 RetSize
= sizeof (RETRIEVAL_POINTERS_BUFFER
) + ((Extents
- 1) * sizeof (LARGE_INTEGER
) * 2);
560 if (Retrieval
!= NULL
)
561 Retrieval
= (RETRIEVAL_POINTERS_BUFFER
*) realloc (Retrieval
, RetSize
);
563 Retrieval
= (RETRIEVAL_POINTERS_BUFFER
*) malloc (RetSize
);
565 Result
= DeviceIoControl
568 FSCTL_GET_RETRIEVAL_POINTERS
,
570 sizeof (StartingVCN
),
579 if (GetLastError() != ERROR_MORE_DATA
)
582 Info
.Attributes
.AccessDenied
= 1;
583 Info
.Attributes
.Process
= 0;
584 Info
.Fragments
.clear ();
585 CloseHandle (Handle
);
593 } while (Result
== FALSE
);
595 // Readjust extents, as it only reflects how much memory was allocated and may not
597 Extents
= Retrieval
->ExtentCount
;
599 // Ok, we have the info. Now translate it. hrmrmr
601 Info
.Fragments
.clear ();
602 for (uint64 i
= 0; i
< Extents
; i
++)
606 Add
.StartLCN
= Retrieval
->Extents
[i
].Lcn
.QuadPart
;
608 Add
.Length
= Retrieval
->Extents
[i
].NextVcn
.QuadPart
- Retrieval
->Extents
[i
- 1].NextVcn
.QuadPart
;
610 Add
.Length
= Retrieval
->Extents
[i
].NextVcn
.QuadPart
- Retrieval
->StartingVcn
.QuadPart
;
612 Info
.Fragments
.push_back (Add
);
616 HandleResult
= Handle
;
621 bool DriveVolume::FindFreeRange (uint64 StartLCN
, uint64 ReqLength
, uint64
&LCNResult
)
627 // Make sure we don't spill over our array
628 Max
= VolInfo
.ClusterCount
- ReqLength
;
630 for (i
= StartLCN
; i
< Max
; i
++)
634 // First check the first cluster
635 if (IsClusterUsed (i
))
638 // THen check the last cluster
639 if (IsClusterUsed (i
+ ReqLength
- 1))
642 // Check the whole darn range.
643 for (j
= (i
+ 1); j
< (i
+ ReqLength
- 2); j
++)
645 if (IsClusterUsed (j
) == true)
665 // btw we have to move each fragment of the file, as per the Win32 API
666 bool DriveVolume::MoveFileDumb (uint32 FileIndice
, uint64 NewLCN
)
668 bool ReturnVal
= false;
672 MOVE_FILE_DATA MoveData
;
677 Info
= GetDBFile (FileIndice
);
678 FullName
= GetDBDir (Info
.DirIndice
);
679 FullName
+= Info
.Name
;
684 if (Info.Attributes.Directory == 1)
691 FileHandle
= CreateFileW
698 (Info
.Attributes
.Directory
== 1) ? FILE_FLAG_BACKUP_SEMANTICS
: 0,
702 if (FileHandle
== INVALID_HANDLE_VALUE
)
707 FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
,
708 NULL
, GetLastError(),
709 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
710 (LPTSTR
) &lpMsgBuf
, 0, NULL
);
713 LocalFree (lpMsgBuf
);
720 ReturnVal
= true; // innocent until proven guilty ...
722 for (uint32 i
= 0; i
< Info
.Fragments
.size(); i
++)
727 //wprintf (L"%3u", i);
729 MoveData
.ClusterCount
= Info
.Fragments
[i
].Length
;
730 MoveData
.StartingLcn
.QuadPart
= CurrentLCN
;
731 MoveData
.StartingVcn
.QuadPart
= CurrentVCN
;
733 MoveData
.FileHandle
= FileHandle
;
737 wprintf (L"StartLCN: %I64u\n", MoveData.StartingLcn.QuadPart);
738 wprintf (L"StartVCN: %I64u\n", MoveData.StartingVcn.QuadPart);
739 wprintf (L"Clusters: %u (%I64u-%I64u --> %I64u-%I64u)\n", MoveData.ClusterCount,
740 Info.Fragments[i].StartLCN,
741 Info.Fragments[i].StartLCN + MoveData.ClusterCount,
742 MoveData.StartingLcn.QuadPart,
743 MoveData.StartingLcn.QuadPart + MoveData.ClusterCount - 1);
747 Result
= DeviceIoControl
759 //wprintf (L"\b\b\b");
766 FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
,
767 NULL
, GetLastError(),
768 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
769 (LPTSTR
) &lpMsgBuf
, 0, NULL
);
772 LocalFree( lpMsgBuf
);
776 goto FinishUp
; // yeah, bite me
779 // Ok good. Now update our drive bitmap and file infos.
782 j
< Info
.Fragments
[i
].Length
;
785 SetClusterUsed (Info
.Fragments
[i
].StartLCN
+ j
, false);
786 SetClusterUsed (CurrentLCN
+ j
, true);
787 //BitmapDetail[Info.Fragments[i].StartLCN + j].Allocated = false;
788 //BitmapDetail[CurrentLCN + j].Allocated = true;
791 CurrentLCN
+= Info
.Fragments
[i
].Length
;
792 CurrentVCN
+= Info
.Fragments
[i
].Length
;
795 // Update file info either way
797 CloseHandle (FileHandle
);
798 FileHandle
= INVALID_HANDLE_VALUE
;
799 GetClusterInfo (Files
[FileIndice
], FileHandle
);
800 CloseHandle (FileHandle
);