1 #include "DriveVolume.h"
2 
3 
4 DriveVolume::DriveVolume ()
5 {
6     Handle = INVALID_HANDLE_VALUE;
7     BitmapDetail = NULL;
8     return;
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         free (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 *) malloc (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         free (Bitmap);
207         return (false);
208     }
209 
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
214     (
215         Handle,
216         FSCTL_GET_VOLUME_BITMAP,
217         &StartingLCN,
218         sizeof (StartingLCN),
219         Bitmap,
220         BitmapSize,
221         &BytesReturned,
222         NULL
223     );
224 
225     //DWORD LastError = GetLastError ();
226 
227     if (Result == FALSE)
228     {
229         wprintf (L"\nCouldn't properly read volume bitmap\n");
230         free (Bitmap);
231         return (false);
232     }
233 
234     // Convert to a L'quick use' bitmap
235     //const int BitShift[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
236 
237     VolInfo.ClusterCount = Bitmap->BitmapSize.QuadPart;
238 
239     if (BitmapDetail != NULL)
240         free (BitmapDetail);
241 
242     BitmapDetail = (uint32 *) malloc (sizeof(uint32) * (1 + (VolInfo.ClusterCount / 32)));
243     memcpy (BitmapDetail, Bitmap->Buffer, sizeof(uint32) * (1 + (VolInfo.ClusterCount / 32)));
244 
245     /*
246     BitmapDetail = (Cluster *) malloc (VolInfo.ClusterCount * sizeof (Cluster));
247     for (uint64 i = 0; i < VolInfo.ClusterCount; i++)
248     {
249         if (Bitmap->Buffer[i / 8] & BitShift[i % 8])
250             BitmapDetail[i].Allocated = true;
251         else
252             BitmapDetail[i].Allocated = false;
253     }
254     */
255 
256     free (Bitmap);
257     return (true);
258 }
259 
260 
261 bool DriveVolume::IsClusterUsed (uint64 Cluster)
262 {
263     return ((BitmapDetail[Cluster / 32] & (1 << (Cluster % 32))) ? true : false);
264     //return (BitmapDetail[Cluster].Allocated);
265 }
266 
267 
268 void DriveVolume::SetClusterUsed (uint64 Cluster, bool Used)
269 {
270     if (Used)
271         BitmapDetail[Cluster / 32] |= (1 << (Cluster % 32));
272     else
273         BitmapDetail[Cluster / 32] &= ~(1 << (Cluster % 32));
274 
275     return;
276 }
277 
278 
279 typedef struct
280 {
281     DriveVolume *Volume;
282     double       *Percent;
283     bool        *QuitMonitor;
284     uint64       ClusterCount;
285     uint64       ClusterProgress;
286 } BuildDBInfo;
287 
288 
289 bool DriveVolume::BuildFileList (bool &QuitMonitor, double &Percent)
290 {
291     BuildDBInfo Info;
292 
293     Files.clear ();
294     Directories.clear ();
295     Directories.push_back (RootPath);
296 
297     Info.Volume = this;
298     Info.QuitMonitor = &QuitMonitor;
299     Info.ClusterCount = (GetVolumeInfo().TotalBytes - GetVolumeInfo().FreeBytes) / (uint64)GetVolumeInfo().ClusterSize;
300     Info.ClusterProgress = 0;
301     Info.Percent = &Percent;
302 
303     ScanDirectory (RootPath, BuildDBCallback, &Info);
304 
305     if (QuitMonitor == true)
306     {
307         Directories.resize (0);
308         Files.resize (0);
309     }
310 
311     return (true);
312 }
313 
314 
315 // UserData = pointer to BuildDBInfo instance
316 bool BuildDBCallback (FileInfo &Info, HANDLE &FileHandle, void *UserData)
317 {
318     BuildDBInfo *DBInfo = (BuildDBInfo *) UserData;
319     DriveVolume *Vol = DBInfo->Volume;
320 
321     Vol->Files.push_back (Info);
322 
323     if (*(DBInfo->QuitMonitor) == true)
324         return (false);
325 
326     DBInfo->ClusterProgress += (uint64)Info.Clusters;
327     *(DBInfo->Percent) =
328         ((double)DBInfo->ClusterProgress / (double)DBInfo->ClusterCount) * 100.0f;
329 
330     return (true);
331 }
332 
333 
334 wstring &DriveVolume::GetDBDir (uint32 Indice)
335 {
336     return (Directories[Indice]);
337 }
338 
339 
340 uint32 DriveVolume::GetDBDirCount (void)
341 {
342     return (Directories.size());
343 }
344 
345 
346 FileInfo &DriveVolume::GetDBFile (uint32 Indice)
347 {
348     return (Files[Indice]);
349 }
350 
351 
352 uint32 DriveVolume::GetDBFileCount (void)
353 {
354     return (Files.size());
355 }
356 
357 
358 uint32 DriveVolume::RemoveDBFile (uint32 Indice)
359 {
360     vector<FileInfo>::iterator it;
361 
362     it = Files.begin() + Indice;
363     Files.erase (it);
364     return (GetDBFileCount());
365 }
366 
367 
368 bool DriveVolume::ScanDirectory (wstring DirPrefix, ScanCallback Callback, void *UserData)
369 {
370     WIN32_FIND_DATAW FindData;
371     HANDLE          FindHandle;
372     wstring          SearchString;
373     uint32          DirIndice;
374 
375     DirIndice = Directories.size() - 1;
376 
377     SearchString = DirPrefix;
378     SearchString += L"*.*";
379     ZeroMemory (&FindData, sizeof (FindData));
380     FindHandle = FindFirstFileW (SearchString.c_str(), &FindData);
381 
382     if (FindHandle == INVALID_HANDLE_VALUE)
383         return (false);
384 
385     do
386     {
387         FileInfo Info;
388         HANDLE   Handle;
389         bool     CallbackResult;
390 
391         Handle = INVALID_HANDLE_VALUE;
392 
393         // First copy over the easy stuff.
394         Info.Name = FindData.cFileName;
395 
396         // DonLL't ever include '.L' and '..'
397         if (Info.Name == L"."  ||  Info.Name == L"..")
398             continue;
399 
400         //Info.FullName = DirPrefix + Info.Name;
401         Info.Size      = (uint64)FindData.nFileSizeLow + ((uint64)FindData.nFileSizeHigh << (uint64)32);
402         Info.DirIndice = DirIndice;
403 
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;
419 
420         Info.Clusters = 0;
421         if (GetClusterInfo (Info, Handle))
422         {
423             uint64 TotalClusters = 0;
424 
425             for (size_t i = 0; i < Info.Fragments.size(); i++)
426             {
427                 TotalClusters += Info.Fragments[i].Length;
428             }
429 
430             Info.Clusters = TotalClusters;
431         }
432         else
433         {
434             Info.Attributes.Unmovable = 1;
435             Info.Attributes.Process = 0;
436         }
437 
438         if (Info.Attributes.Process == 1)
439             Info.Attributes.Process = ShouldProcess (Info.Attributes) ? 1 : 0;
440 
441         // Run the user-defined callback function
442         CallbackResult = Callback (Info, Handle, UserData);
443 
444         if (Handle != INVALID_HANDLE_VALUE)
445             CloseHandle (Handle);
446 
447         if (!CallbackResult)
448             break;
449 
450         // If directory, perform recursion
451         if (Info.Attributes.Directory == 1)
452         {
453             wstring Dir;
454 
455             Dir = GetDBDir (Info.DirIndice);
456             Dir += Info.Name;
457             Dir += L"\\";
458 
459             Directories.push_back (Dir);
460             ScanDirectory (Dir, Callback, UserData);
461         }
462 
463     } while (FindNextFileW (FindHandle, &FindData) == TRUE);
464 
465     FindClose (FindHandle);
466     return (false);
467 }
468 
469 
470 bool DriveVolume::ShouldProcess (FileAttr Attr)
471 {
472     if (Attr.Offline == 1  ||  Attr.Reparse == 1  ||  Attr.Temporary == 1)
473     {
474         return (false);
475     }
476 
477     return (true);
478 }
479 
480 
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)
485 {
486     BOOL     Result;
487     HANDLE   Handle;
488     wstring   FullName;
489     BY_HANDLE_FILE_INFORMATION FileInfo;
490 
491     Info.Fragments.resize (0);
492 
493     /*
494     if (Info.Attributes.Directory == 1)
495         return (false);
496     */
497 
498     FullName = GetDBDir (Info.DirIndice) + Info.Name;
499 
500     Handle = CreateFileW
501     (
502         FullName.c_str(),
503         0, //GENERIC_READ,
504         FILE_SHARE_READ,
505         NULL,
506         OPEN_EXISTING,
507         (Info.Attributes.Directory == 1) ? FILE_FLAG_BACKUP_SEMANTICS : 0,
508         NULL
509     );
510 
511     if (Handle == INVALID_HANDLE_VALUE)
512     {
513 	    LPVOID lpMsgBuf;
514 
515 	    FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
516 					    NULL, GetLastError(),
517 					    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
518 					    (LPTSTR) &lpMsgBuf, 0, NULL );
519 
520 
521         Info.Attributes.AccessDenied = 1;
522 	    LocalFree( lpMsgBuf );
523         return (false);
524     }
525 
526     ZeroMemory (&FileInfo, sizeof (FileInfo));
527     Result = GetFileInformationByHandle (Handle, &FileInfo);
528 
529     if (Result == FALSE)
530     {
531         Info.Attributes.AccessDenied = 1;
532         wprintf (L"GetFileInformationByHandle ('%s%s') failed\n", GetDBDir (Info.DirIndice).c_str(),
533             Info.Name.c_str());
534 
535         CloseHandle (Handle);
536         return (false);
537     }
538 
539     // Get cluster allocation information
540     STARTING_VCN_INPUT_BUFFER  StartingVCN;
541     RETRIEVAL_POINTERS_BUFFER *Retrieval;
542     uint64                     RetSize;
543     uint64                     Extents;
544     DWORD                      BytesReturned;
545 
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!
550     Extents = 10;
551     Retrieval = NULL;
552     RetSize = 0;
553     StartingVCN.StartingVcn.QuadPart = 0;
554 
555     do
556     {
557         Extents *= 2;
558         RetSize = sizeof (RETRIEVAL_POINTERS_BUFFER) + ((Extents - 1) * sizeof (LARGE_INTEGER) * 2);
559 
560         if (Retrieval != NULL)
561             Retrieval = (RETRIEVAL_POINTERS_BUFFER *) realloc (Retrieval, RetSize);
562         else
563             Retrieval = (RETRIEVAL_POINTERS_BUFFER *) malloc (RetSize);
564 
565         Result = DeviceIoControl
566         (
567             Handle,
568             FSCTL_GET_RETRIEVAL_POINTERS,
569             &StartingVCN,
570             sizeof (StartingVCN),
571             Retrieval,
572             RetSize,
573             &BytesReturned,
574             NULL
575         );
576 
577         if (Result == FALSE)
578         {
579             if (GetLastError() != ERROR_MORE_DATA)
580             {
581                 Info.Clusters = 0;
582                 Info.Attributes.AccessDenied = 1;
583                 Info.Attributes.Process = 0;
584                 Info.Fragments.clear ();
585                 CloseHandle (Handle);
586                 free (Retrieval);
587 
588                 return (false);
589             }
590 
591             Extents++;
592         }
593     } while (Result == FALSE);
594 
595     // Readjust extents, as it only reflects how much memory was allocated and may not
596     // be accurate
597     Extents = Retrieval->ExtentCount;
598 
599     // Ok, we have the info. Now translate it. hrmrmr
600 
601     Info.Fragments.clear ();
602     for (uint64 i = 0; i < Extents; i++)
603     {
604         Extent Add;
605 
606         Add.StartLCN = Retrieval->Extents[i].Lcn.QuadPart;
607         if (i != 0)
608             Add.Length = Retrieval->Extents[i].NextVcn.QuadPart - Retrieval->Extents[i - 1].NextVcn.QuadPart;
609         else
610             Add.Length = Retrieval->Extents[i].NextVcn.QuadPart - Retrieval->StartingVcn.QuadPart;
611 
612         Info.Fragments.push_back (Add);
613     }
614 
615     free (Retrieval);
616     HandleResult = Handle;
617     return (true);
618 }
619 
620 
621 bool DriveVolume::FindFreeRange (uint64 StartLCN, uint64 ReqLength, uint64 &LCNResult)
622 {
623     uint64 Max;
624     uint64 i;
625     uint64 j;
626 
627     // Make sure we don't spill over our array
628     Max = VolInfo.ClusterCount - ReqLength;
629 
630     for (i = StartLCN; i < Max; i++)
631     {
632         bool Found = true;
633 
634         // First check the first cluster
635         if (IsClusterUsed (i))
636             Found = false;
637         else
638         // THen check the last cluster
639         if (IsClusterUsed (i + ReqLength - 1))
640             Found = false;
641         else
642         // Check the whole darn range.
643         for (j = (i + 1); j < (i + ReqLength - 2); j++)
644         {
645             if (IsClusterUsed (j) == true)
646             {
647                 Found = false;
648                 break;
649             }
650         }
651 
652         if (!Found)
653             continue;
654         else
655         {
656             LCNResult = i;
657             return (true);
658         }
659     }
660 
661     return (false);
662 }
663 
664 
665 // btw we have to move each fragment of the file, as per the Win32 API
666 bool DriveVolume::MoveFileDumb (uint32 FileIndice, uint64 NewLCN)
667 {
668     bool ReturnVal = false;
669     FileInfo Info;
670     HANDLE FileHandle;
671     wstring FullName;
672     MOVE_FILE_DATA MoveData;
673     uint64 CurrentLCN;
674     uint64 CurrentVCN;
675 
676     // Set up variables
677     Info = GetDBFile (FileIndice);
678     FullName = GetDBDir (Info.DirIndice);
679     FullName += Info.Name;
680     CurrentLCN = NewLCN;
681     CurrentVCN = 0;
682 
683     /*
684     if (Info.Attributes.Directory == 1)
685     {
686         //
687     }
688     */
689 
690     // Open file
691     FileHandle = CreateFileW
692     (
693         FullName.c_str (),
694         GENERIC_READ,
695         FILE_SHARE_READ,
696         NULL,
697         OPEN_EXISTING,
698         (Info.Attributes.Directory == 1) ? FILE_FLAG_BACKUP_SEMANTICS : 0,
699         NULL
700     );
701 
702     if (FileHandle == INVALID_HANDLE_VALUE)
703     {
704         //
705 	    LPVOID lpMsgBuf;
706 
707 	    FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
708 					    NULL, GetLastError(),
709 					    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
710 					    (LPTSTR) &lpMsgBuf, 0, NULL );
711 
712 
713 	    LocalFree (lpMsgBuf);
714         //
715 
716         ReturnVal = false;
717     }
718     else
719     {
720         ReturnVal = true; // innocent until proven guilty ...
721 
722         for (uint32 i = 0; i < Info.Fragments.size(); i++)
723         {
724             BOOL Result;
725             DWORD BytesReturned;
726 
727             //wprintf (L"%3u", i);
728 
729             MoveData.ClusterCount = Info.Fragments[i].Length;
730             MoveData.StartingLcn.QuadPart = CurrentLCN;
731             MoveData.StartingVcn.QuadPart = CurrentVCN;
732 
733             MoveData.FileHandle = FileHandle;
734 
735             /*
736             wprintf (L"\n");
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);
744             wprintf (L"\n");
745             */
746 
747             Result = DeviceIoControl
748             (
749                 Handle,
750                 FSCTL_MOVE_FILE,
751                 &MoveData,
752                 sizeof (MoveData),
753                 NULL,
754                 0,
755                 &BytesReturned,
756                 NULL
757             );
758 
759             //wprintf (L"\b\b\b");
760 
761             if (Result == FALSE)
762             {
763                 //
764 	            LPVOID lpMsgBuf;
765 
766 	            FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
767 					            NULL, GetLastError(),
768 					            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
769 					            (LPTSTR) &lpMsgBuf, 0, NULL );
770 
771 
772 	            LocalFree( lpMsgBuf );
773                 //
774 
775                 ReturnVal = false;
776                 goto FinishUp;  // yeah, bite me
777             }
778 
779             // Ok good. Now update our drive bitmap and file infos.
780             uint64 j;
781             for (j = 0;
782                  j < Info.Fragments[i].Length;
783                  j++)
784             {
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;
789             }
790 
791             CurrentLCN += Info.Fragments[i].Length;
792             CurrentVCN += Info.Fragments[i].Length;
793         }
794 
795         // Update file info either way
796     FinishUp:
797         CloseHandle (FileHandle);
798         FileHandle = INVALID_HANDLE_VALUE;
799         GetClusterInfo (Files[FileIndice], FileHandle);
800         CloseHandle (FileHandle);
801     }
802 
803     return (ReturnVal);
804 }
805 
806 
807