1 #include "DriveVolume.h"
2 
3 
4 DriveVolume::DriveVolume()
5 {
6     Handle = INVALID_HANDLE_VALUE;
7     BitmapDetail = NULL;
8     ZeroMemory(&Geometry, sizeof(Geometry));
9 }
10 
11 
12 DriveVolume::~DriveVolume ()
13 {
14     Close ();
15     Directories.clear ();
16     Files.clear ();
17     return;
18 }
19 
20 
21 void DriveVolume::Close (void)
22 {
23     if (Handle != INVALID_HANDLE_VALUE)
24     {
25         CloseHandle (Handle);
26         Handle = INVALID_HANDLE_VALUE;
27     }
28 
29     if (BitmapDetail != NULL)
30     {
31         HeapFree(GetProcessHeap(), 0, BitmapDetail);
32         BitmapDetail = NULL;
33     }
34 
35     return;
36 }
37 
38 
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)
43 {
44     wchar_t FileName[100];
45     bool ReturnVal;
46 
47     swprintf (FileName, L"\\\\.\\%s", Name.c_str());
48     RootPath = Name.c_str();
49     RootPath += L"\\";
50 
51     Handle = CreateFileW
52     (
53         FileName,
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)
59         NULL                                      // template
60     );
61 
62     if (Handle == INVALID_HANDLE_VALUE)
63         ReturnVal = false;
64     else
65     {
66         wchar_t  VolName[64];
67         DWORD VolSN;
68         DWORD VolMaxFileLen;
69         DWORD FSFlags;
70         wchar_t  FSName[64];
71         BOOL  Result;
72 
73         ReturnVal = true;
74         Result = GetVolumeInformationW
75         (
76             RootPath.c_str(),
77             VolName,
78             sizeof (VolName),
79             &VolSN,
80             &VolMaxFileLen,
81             &FSFlags,
82             FSName,
83             sizeof (FSName)
84         );
85 
86         if (Result)
87         {
88             wchar_t SerialText[10];
89 
90             VolInfo.FileSystem = FSName;
91             VolInfo.MaxNameLen = VolMaxFileLen;
92             VolInfo.Name       = VolName;
93 
94             swprintf (SerialText, L"%x-%x", (VolSN & 0xffff0000) >> 16,
95                 VolSN & 0x0000ffff);
96 
97             _wcsupr (SerialText);
98             VolInfo.Serial     = SerialText;
99         }
100         else
101         {
102             VolInfo.FileSystem = L"(Unknown)";
103             VolInfo.MaxNameLen = 255;
104             VolInfo.Name       = L"(Unknown)";
105             VolInfo.Serial     = L"(Unknown)";
106         }
107     }
108 
109     return (ReturnVal);
110 }
111 
112 
113 bool DriveVolume::ObtainInfo (void)
114 {
115     BOOL Result;
116     DWORD BytesGot;
117     uint64 nan;
118 
119     BytesGot = 0;
120     ZeroMemory (&Geometry, sizeof (Geometry));
121     Result = DeviceIoControl
122     (
123         Handle,
124         IOCTL_DISK_GET_DRIVE_GEOMETRY,
125         NULL,
126         0,
127         &Geometry,
128         sizeof (Geometry),
129         &BytesGot,
130         NULL
131     );
132 
133     // Call failed? Aww :(
134     if (!Result)
135         return (false);
136 
137     // Get cluster size
138     DWORD SectorsPerCluster;
139     DWORD BytesPerSector;
140     DWORD FreeClusters;
141     DWORD TotalClusters;
142 
143     Result = GetDiskFreeSpaceW
144     (
145         RootPath.c_str(),
146         &SectorsPerCluster,
147         &BytesPerSector,
148         &FreeClusters,
149         &TotalClusters
150     );
151 
152     // Failed? Weird.
153     if (!Result)
154         return (false);
155 
156     VolInfo.ClusterSize = SectorsPerCluster * BytesPerSector;
157 
158     Result = GetDiskFreeSpaceExW
159     (
160         RootPath.c_str(),
161         (PULARGE_INTEGER)&nan,
162         (PULARGE_INTEGER)&VolInfo.TotalBytes,
163         (PULARGE_INTEGER)&VolInfo.FreeBytes
164     );
165 
166     return (true);
167 }
168 
169 
170 // Get bitmap, several clusters at a time ...
171 #define CLUSTERS 4096
172 bool DriveVolume::GetBitmap (void)
173 {
174     STARTING_LCN_INPUT_BUFFER StartingLCN;
175     VOLUME_BITMAP_BUFFER *Bitmap = NULL;
176     uint32 BitmapSize;
177     DWORD BytesReturned;
178     BOOL Result;
179 
180     StartingLCN.StartingLcn.QuadPart = 0;
181 
182     // Allocate buffer
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 *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, BitmapSize);
189 
190     Result = DeviceIoControl
191     (
192         Handle,
193         FSCTL_GET_VOLUME_BITMAP,
194         &StartingLCN,
195         sizeof (StartingLCN),
196         Bitmap,
197         BitmapSize,
198         &BytesReturned,
199         NULL
200     );
201 
202     // Bad result?
203     if (Result == FALSE && GetLastError() != ERROR_MORE_DATA)
204     {
205         //wprintf ("\nDeviceIoControl returned false, GetLastError() was not ERROR_MORE_DATA\n");
206         HeapFree(GetProcessHeap(), 0, Bitmap);
207         return (false);
208     }
209 
210     // Otherwise, we're good
211     BitmapSize = sizeof (VOLUME_BITMAP_BUFFER) + (Bitmap->BitmapSize.QuadPart / 8) + 1;
212 
213     void *reallBitmap = HeapReAlloc(GetProcessHeap(), 0, Bitmap, BitmapSize);
214 
215     if (reallBitmap == NULL)
216     {
217         // Fail "miserably"
218         wprintf(L"\nNot enough memory to read volume bitmap\n");
219         HeapFree(GetProcessHeap(), 0, Bitmap);
220         return (false);
221     }
222 
223     Bitmap = (VOLUME_BITMAP_BUFFER *)reallBitmap;
224     Result = DeviceIoControl
225     (
226         Handle,
227         FSCTL_GET_VOLUME_BITMAP,
228         &StartingLCN,
229         sizeof (StartingLCN),
230         Bitmap,
231         BitmapSize,
232         &BytesReturned,
233         NULL
234     );
235 
236     //DWORD LastError = GetLastError ();
237 
238     if (Result == FALSE)
239     {
240         wprintf (L"\nCouldn't properly read volume bitmap\n");
241         HeapFree(GetProcessHeap(), 0, Bitmap);
242         return (false);
243     }
244 
245     // Convert to a L'quick use' bitmap
246     //const int BitShift[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
247 
248     VolInfo.ClusterCount = Bitmap->BitmapSize.QuadPart;
249 
250     if (BitmapDetail != NULL)
251         HeapFree(GetProcessHeap(), 0, BitmapDetail);
252 
253     BitmapDetail = (uint32 *) HeapAlloc(GetProcessHeap(), 0, sizeof(uint32) * (1 + (VolInfo.ClusterCount / 32)));
254     memcpy (BitmapDetail, Bitmap->Buffer, sizeof(uint32) * (1 + (VolInfo.ClusterCount / 32)));
255 
256     /*
257     BitmapDetail = (Cluster *) malloc (VolInfo.ClusterCount * sizeof (Cluster));
258     for (uint64 i = 0; i < VolInfo.ClusterCount; i++)
259     {
260         if (Bitmap->Buffer[i / 8] & BitShift[i % 8])
261             BitmapDetail[i].Allocated = true;
262         else
263             BitmapDetail[i].Allocated = false;
264     }
265     */
266 
267     HeapFree(GetProcessHeap(), 0, Bitmap);
268     return (true);
269 }
270 
271 
272 bool DriveVolume::IsClusterUsed (uint64 Cluster)
273 {
274     return ((BitmapDetail[Cluster / 32] & (1 << (Cluster % 32))) ? true : false);
275     //return (BitmapDetail[Cluster].Allocated);
276 }
277 
278 
279 void DriveVolume::SetClusterUsed (uint64 Cluster, bool Used)
280 {
281     if (Used)
282         BitmapDetail[Cluster / 32] |= (1 << (Cluster % 32));
283     else
284         BitmapDetail[Cluster / 32] &= ~(1 << (Cluster % 32));
285 
286     return;
287 }
288 
289 
290 typedef struct
291 {
292     DriveVolume *Volume;
293     double       *Percent;
294     bool        *QuitMonitor;
295     uint64       ClusterCount;
296     uint64       ClusterProgress;
297 } BuildDBInfo;
298 
299 
300 bool DriveVolume::BuildFileList (bool &QuitMonitor, double &Percent)
301 {
302     BuildDBInfo Info;
303 
304     Files.clear ();
305     Directories.clear ();
306     Directories.push_back (RootPath);
307 
308     Info.Volume = this;
309     Info.QuitMonitor = &QuitMonitor;
310     Info.ClusterCount = (GetVolumeInfo().TotalBytes - GetVolumeInfo().FreeBytes) / (uint64)GetVolumeInfo().ClusterSize;
311     Info.ClusterProgress = 0;
312     Info.Percent = &Percent;
313 
314     ScanDirectory (RootPath, BuildDBCallback, &Info);
315 
316     if (QuitMonitor == true)
317     {
318         Directories.resize (0);
319         Files.resize (0);
320     }
321 
322     return (true);
323 }
324 
325 
326 // UserData = pointer to BuildDBInfo instance
327 bool BuildDBCallback (FileInfo &Info, HANDLE &FileHandle, void *UserData)
328 {
329     BuildDBInfo *DBInfo = (BuildDBInfo *) UserData;
330     DriveVolume *Vol = DBInfo->Volume;
331 
332     Vol->Files.push_back (Info);
333 
334     if (*(DBInfo->QuitMonitor) == true)
335         return (false);
336 
337     DBInfo->ClusterProgress += (uint64)Info.Clusters;
338     *(DBInfo->Percent) =
339         ((double)DBInfo->ClusterProgress / (double)DBInfo->ClusterCount) * 100.0f;
340 
341     return (true);
342 }
343 
344 
345 wstring &DriveVolume::GetDBDir (uint32 Indice)
346 {
347     return (Directories[Indice]);
348 }
349 
350 
351 uint32 DriveVolume::GetDBDirCount (void)
352 {
353     return (Directories.size());
354 }
355 
356 
357 FileInfo &DriveVolume::GetDBFile (uint32 Indice)
358 {
359     return (Files[Indice]);
360 }
361 
362 
363 uint32 DriveVolume::GetDBFileCount (void)
364 {
365     return (Files.size());
366 }
367 
368 
369 uint32 DriveVolume::RemoveDBFile (uint32 Indice)
370 {
371     vector<FileInfo>::iterator it;
372 
373     it = Files.begin() + Indice;
374     Files.erase (it);
375     return (GetDBFileCount());
376 }
377 
378 
379 bool DriveVolume::ScanDirectory (wstring DirPrefix, ScanCallback Callback, void *UserData)
380 {
381     WIN32_FIND_DATAW FindData;
382     HANDLE          FindHandle;
383     wstring          SearchString;
384     uint32          DirIndice;
385 
386     DirIndice = Directories.size() - 1;
387 
388     SearchString = DirPrefix;
389     SearchString += L"*.*";
390     ZeroMemory (&FindData, sizeof (FindData));
391     FindHandle = FindFirstFileW (SearchString.c_str(), &FindData);
392 
393     if (FindHandle == INVALID_HANDLE_VALUE)
394         return (false);
395 
396     do
397     {
398         FileInfo Info;
399         HANDLE   Handle;
400         bool     CallbackResult;
401 
402         Handle = INVALID_HANDLE_VALUE;
403 
404         // First copy over the easy stuff.
405         Info.Name = FindData.cFileName;
406 
407         // DonLL't ever include '.L' and '..'
408         if (Info.Name == L"."  ||  Info.Name == L"..")
409             continue;
410 
411         //Info.FullName = DirPrefix + Info.Name;
412         Info.Size      = (uint64)FindData.nFileSizeLow + ((uint64)FindData.nFileSizeHigh << (uint64)32);
413         Info.DirIndice = DirIndice;
414 
415         Info.Attributes.Archive    = (FindData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)       ? 1 : 0;
416         Info.Attributes.Compressed = (FindData.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)    ? 1 : 0;
417         Info.Attributes.Directory  = (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)     ? 1 : 0;
418         Info.Attributes.Encrypted  = (FindData.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)     ? 1 : 0;
419         Info.Attributes.Hidden     = (FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)        ? 1 : 0;
420         Info.Attributes.Normal     = (FindData.dwFileAttributes & FILE_ATTRIBUTE_NORMAL)        ? 1 : 0;
421         Info.Attributes.Offline    = (FindData.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE)       ? 1 : 0;
422         Info.Attributes.ReadOnly   = (FindData.dwFileAttributes & FILE_ATTRIBUTE_READONLY)      ? 1 : 0;
423         Info.Attributes.Reparse    = (FindData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? 1 : 0;
424         Info.Attributes.Sparse     = (FindData.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE)   ? 1 : 0;
425         Info.Attributes.System     = (FindData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)        ? 1 : 0;
426         Info.Attributes.Temporary  = (FindData.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY)     ? 1 : 0;
427         Info.Attributes.AccessDenied = 0;
428         Info.Attributes.Unmovable    = 0;
429         Info.Attributes.Process      = 1;
430 
431         Info.Clusters = 0;
432         if (GetClusterInfo (Info, Handle))
433         {
434             uint64 TotalClusters = 0;
435 
436             for (size_t i = 0; i < Info.Fragments.size(); i++)
437             {
438                 TotalClusters += Info.Fragments[i].Length;
439             }
440 
441             Info.Clusters = TotalClusters;
442         }
443         else
444         {
445             Info.Attributes.Unmovable = 1;
446             Info.Attributes.Process = 0;
447         }
448 
449         if (Info.Attributes.Process == 1)
450             Info.Attributes.Process = ShouldProcess (Info.Attributes) ? 1 : 0;
451 
452         // Run the user-defined callback function
453         CallbackResult = Callback (Info, Handle, UserData);
454 
455         if (Handle != INVALID_HANDLE_VALUE)
456             CloseHandle (Handle);
457 
458         if (!CallbackResult)
459             break;
460 
461         // If directory, perform recursion
462         if (Info.Attributes.Directory == 1)
463         {
464             wstring Dir;
465 
466             Dir = GetDBDir (Info.DirIndice);
467             Dir += Info.Name;
468             Dir += L"\\";
469 
470             Directories.push_back (Dir);
471             ScanDirectory (Dir, Callback, UserData);
472         }
473 
474     } while (FindNextFileW (FindHandle, &FindData) == TRUE);
475 
476     FindClose (FindHandle);
477     return (false);
478 }
479 
480 
481 bool DriveVolume::ShouldProcess (FileAttr Attr)
482 {
483     if (Attr.Offline == 1  ||  Attr.Reparse == 1  ||  Attr.Temporary == 1)
484     {
485         return (false);
486     }
487 
488     return (true);
489 }
490 
491 
492 // Gets info on a file and returns a valid handle for read/write access
493 // Name, FullName, Clusters, Attributes, and Size should already be filled out.
494 // This function fills in the Fragments vector
495 bool DriveVolume::GetClusterInfo (FileInfo &Info, HANDLE &HandleResult)
496 {
497     BOOL     Result;
498     HANDLE   Handle;
499     wstring   FullName;
500     BY_HANDLE_FILE_INFORMATION FileInfo;
501 
502     Info.Fragments.resize (0);
503 
504     /*
505     if (Info.Attributes.Directory == 1)
506         return (false);
507     */
508 
509     FullName = GetDBDir (Info.DirIndice) + Info.Name;
510 
511     Handle = CreateFileW
512     (
513         FullName.c_str(),
514         0, //GENERIC_READ,
515         FILE_SHARE_READ,
516         NULL,
517         OPEN_EXISTING,
518         (Info.Attributes.Directory == 1) ? FILE_FLAG_BACKUP_SEMANTICS : 0,
519         NULL
520     );
521 
522     if (Handle == INVALID_HANDLE_VALUE)
523     {
524 	    LPVOID lpMsgBuf;
525 
526 	    FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
527 					    NULL, GetLastError(),
528 					    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
529 					    (LPTSTR) &lpMsgBuf, 0, NULL );
530 
531 
532         Info.Attributes.AccessDenied = 1;
533 	    LocalFree( lpMsgBuf );
534         return (false);
535     }
536 
537     ZeroMemory (&FileInfo, sizeof (FileInfo));
538     Result = GetFileInformationByHandle (Handle, &FileInfo);
539 
540     if (Result == FALSE)
541     {
542         Info.Attributes.AccessDenied = 1;
543         wprintf (L"GetFileInformationByHandle ('%s%s') failed\n", GetDBDir (Info.DirIndice).c_str(),
544             Info.Name.c_str());
545 
546         CloseHandle (Handle);
547         return (false);
548     }
549 
550     // Get cluster allocation information
551     STARTING_VCN_INPUT_BUFFER  StartingVCN;
552     RETRIEVAL_POINTERS_BUFFER *Retrieval;
553     uint64                     RetSize;
554     uint64                     Extents;
555     DWORD                      BytesReturned;
556 
557     // Grab info one extent at a time, until it's done grabbing all the extent data
558     // Yeah, well it doesn't give us a way to ask L"how many extents?" that I know of ...
559     // btw, the Extents variable tends to only reflect memory usage, so when we have
560     // all the extents we look at the structure Win32 gives us for the REAL count!
561     Extents = 10;
562     Retrieval = NULL;
563     RetSize = 0;
564     StartingVCN.StartingVcn.QuadPart = 0;
565 
566     do
567     {
568         Extents *= 2;
569         RetSize = sizeof (RETRIEVAL_POINTERS_BUFFER) + ((Extents - 1) * sizeof (LARGE_INTEGER) * 2);
570 
571         if (Retrieval != NULL)
572             Retrieval = (RETRIEVAL_POINTERS_BUFFER *) realloc (Retrieval, RetSize);
573         else
574             Retrieval = (RETRIEVAL_POINTERS_BUFFER *) malloc (RetSize);
575 
576         Result = DeviceIoControl
577         (
578             Handle,
579             FSCTL_GET_RETRIEVAL_POINTERS,
580             &StartingVCN,
581             sizeof (StartingVCN),
582             Retrieval,
583             RetSize,
584             &BytesReturned,
585             NULL
586         );
587 
588         if (Result == FALSE)
589         {
590             if (GetLastError() != ERROR_MORE_DATA)
591             {
592                 Info.Clusters = 0;
593                 Info.Attributes.AccessDenied = 1;
594                 Info.Attributes.Process = 0;
595                 Info.Fragments.clear ();
596                 CloseHandle (Handle);
597                 free (Retrieval);
598 
599                 return (false);
600             }
601 
602             Extents++;
603         }
604     } while (Result == FALSE);
605 
606     // Readjust extents, as it only reflects how much memory was allocated and may not
607     // be accurate
608     Extents = Retrieval->ExtentCount;
609 
610     // Ok, we have the info. Now translate it. hrmrmr
611 
612     Info.Fragments.clear ();
613     for (uint64 i = 0; i < Extents; i++)
614     {
615         Extent Add;
616 
617         Add.StartLCN = Retrieval->Extents[i].Lcn.QuadPart;
618         if (i != 0)
619             Add.Length = Retrieval->Extents[i].NextVcn.QuadPart - Retrieval->Extents[i - 1].NextVcn.QuadPart;
620         else
621             Add.Length = Retrieval->Extents[i].NextVcn.QuadPart - Retrieval->StartingVcn.QuadPart;
622 
623         Info.Fragments.push_back (Add);
624     }
625 
626     free (Retrieval);
627     HandleResult = Handle;
628     return (true);
629 }
630 
631 
632 bool DriveVolume::FindFreeRange (uint64 StartLCN, uint64 ReqLength, uint64 &LCNResult)
633 {
634     uint64 Max;
635     uint64 i;
636     uint64 j;
637 
638     // Make sure we don't spill over our array
639     Max = VolInfo.ClusterCount - ReqLength;
640 
641     for (i = StartLCN; i < Max; i++)
642     {
643         bool Found = true;
644 
645         // First check the first cluster
646         if (IsClusterUsed (i))
647             Found = false;
648         else
649         // THen check the last cluster
650         if (IsClusterUsed (i + ReqLength - 1))
651             Found = false;
652         else
653         // Check the whole darn range.
654         for (j = (i + 1); j < (i + ReqLength - 2); j++)
655         {
656             if (IsClusterUsed (j) == true)
657             {
658                 Found = false;
659                 break;
660             }
661         }
662 
663         if (!Found)
664             continue;
665         else
666         {
667             LCNResult = i;
668             return (true);
669         }
670     }
671 
672     return (false);
673 }
674 
675 
676 // btw we have to move each fragment of the file, as per the Win32 API
677 bool DriveVolume::MoveFileDumb (uint32 FileIndice, uint64 NewLCN)
678 {
679     bool ReturnVal = false;
680     FileInfo Info;
681     HANDLE FileHandle;
682     wstring FullName;
683     MOVE_FILE_DATA MoveData;
684     uint64 CurrentLCN;
685     uint64 CurrentVCN;
686 
687     // Set up variables
688     Info = GetDBFile (FileIndice);
689     FullName = GetDBDir (Info.DirIndice);
690     FullName += Info.Name;
691     CurrentLCN = NewLCN;
692     CurrentVCN = 0;
693 
694     /*
695     if (Info.Attributes.Directory == 1)
696     {
697         //
698     }
699     */
700 
701     // Open file
702     FileHandle = CreateFileW
703     (
704         FullName.c_str (),
705         GENERIC_READ,
706         FILE_SHARE_READ,
707         NULL,
708         OPEN_EXISTING,
709         (Info.Attributes.Directory == 1) ? FILE_FLAG_BACKUP_SEMANTICS : 0,
710         NULL
711     );
712 
713     if (FileHandle == INVALID_HANDLE_VALUE)
714     {
715         //
716 	    LPVOID lpMsgBuf;
717 
718 	    FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
719 					    NULL, GetLastError(),
720 					    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
721 					    (LPTSTR) &lpMsgBuf, 0, NULL );
722 
723 
724 	    LocalFree (lpMsgBuf);
725         //
726 
727         ReturnVal = false;
728     }
729     else
730     {
731         ReturnVal = true; // innocent until proven guilty ...
732 
733         for (uint32 i = 0; i < Info.Fragments.size(); i++)
734         {
735             BOOL Result;
736             DWORD BytesReturned;
737 
738             //wprintf (L"%3u", i);
739 
740             MoveData.ClusterCount = Info.Fragments[i].Length;
741             MoveData.StartingLcn.QuadPart = CurrentLCN;
742             MoveData.StartingVcn.QuadPart = CurrentVCN;
743 
744             MoveData.FileHandle = FileHandle;
745 
746             /*
747             wprintf (L"\n");
748             wprintf (L"StartLCN: %I64u\n", MoveData.StartingLcn.QuadPart);
749             wprintf (L"StartVCN: %I64u\n", MoveData.StartingVcn.QuadPart);
750             wprintf (L"Clusters: %u (%I64u-%I64u --> %I64u-%I64u)\n", MoveData.ClusterCount,
751                 Info.Fragments[i].StartLCN,
752                 Info.Fragments[i].StartLCN + MoveData.ClusterCount,
753                 MoveData.StartingLcn.QuadPart,
754                 MoveData.StartingLcn.QuadPart + MoveData.ClusterCount - 1);
755             wprintf (L"\n");
756             */
757 
758             Result = DeviceIoControl
759             (
760                 Handle,
761                 FSCTL_MOVE_FILE,
762                 &MoveData,
763                 sizeof (MoveData),
764                 NULL,
765                 0,
766                 &BytesReturned,
767                 NULL
768             );
769 
770             //wprintf (L"\b\b\b");
771 
772             if (Result == FALSE)
773             {
774                 //
775 	            LPVOID lpMsgBuf;
776 
777 	            FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
778 					            NULL, GetLastError(),
779 					            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
780 					            (LPTSTR) &lpMsgBuf, 0, NULL );
781 
782 
783 	            LocalFree( lpMsgBuf );
784                 //
785 
786                 ReturnVal = false;
787                 goto FinishUp;  // yeah, bite me
788             }
789 
790             // Ok good. Now update our drive bitmap and file infos.
791             uint64 j;
792             for (j = 0;
793                  j < Info.Fragments[i].Length;
794                  j++)
795             {
796                 SetClusterUsed (Info.Fragments[i].StartLCN + j, false);
797                 SetClusterUsed (CurrentLCN + j, true);
798                 //BitmapDetail[Info.Fragments[i].StartLCN + j].Allocated = false;
799                 //BitmapDetail[CurrentLCN + j].Allocated = true;
800             }
801 
802             CurrentLCN += Info.Fragments[i].Length;
803             CurrentVCN += Info.Fragments[i].Length;
804         }
805 
806         // Update file info either way
807     FinishUp:
808         CloseHandle (FileHandle);
809         FileHandle = INVALID_HANDLE_VALUE;
810         GetClusterInfo (Files[FileIndice], FileHandle);
811         CloseHandle (FileHandle);
812     }
813 
814     return (ReturnVal);
815 }
816 
817 
818