分享

UltraDefrag Engine

 DavinTang 2010-12-14
00001 /*
00002  *  UltraDefrag - powerful defragmentation tool for Windows NT.
00003  *  Copyright (c) 2007-2010 by Dmitri Arkhangelski (dmitriar@gmail.com).
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00018  */
00019
00027 #include "globals.h"
00028 #include "ntfs.h"
00029
00030 /*
00031 * Add data consistency checks everywhere,
00032 * because NTFS volumes often have invalid
00033 * data in MFT entries.
00034 */
00035
00036 /*
00037 * FIXME: 
00038 * 1. Volume ditry flag?
00039 */
00040
00041 /*---------------------------------- NTFS related code -------------------------------------*/
00042
00043 UINT MftScanDirection = MFT_SCAN_RTL;
00044 BOOLEAN MaxMftEntriesNumberUpdated = FALSE;
00045 int number_of_processed_attr_list_entries = 0;
00046
00051 NTSTATUS GetMftLayout(void)
00052 {
00053         IO_STATUS_BLOCK iosb;
00054         NTFS_DATA *ntfs_data;
00055         NTSTATUS status;
00056         ULONGLONG mft_len;
00057
00058         ntfs_data = winx_heap_alloc(sizeof(NTFS_DATA));
00059         if(ntfs_data == NULL){
00060                 DebugPrint("Cannot allocate memory for GetMftLayout()!\n");
00061                 out_of_memory_condition_counter ++;
00062                 return STATUS_NO_MEMORY;
00063         }
00064
00065         RtlZeroMemory(ntfs_data,sizeof(NTFS_DATA));
00066         status = NtFsControlFile(winx_fileno(fVolume),NULL,NULL,NULL,&iosb, 00067                                 FSCTL_GET_NTFS_VOLUME_DATA,NULL,0, 00068                                 ntfs_data, sizeof(NTFS_DATA));
00069         if(NT_SUCCESS(status)/* == STATUS_PENDING*/){
00070                 (void)NtWaitForSingleObject(winx_fileno(fVolume),FALSE,NULL);
00071                 status = iosb.Status;
00072         }
00073         if(!NT_SUCCESS(status)){
00074                 DebugPrint("Cannot get ntfs info: %x!\n",status);
00075                 winx_heap_free(ntfs_data);
00076                 return status;
00077         }
00078
00079         mft_len = ProcessMftSpace(ntfs_data);
00080
00081         Stat.mft_size = mft_len * bytes_per_cluster;
00082         DebugPrint("MFT size = %I64u bytes\n",Stat.mft_size);
00083         if(mft_len == 0){
00084                 DebugPrint("MFT size is equal to zero!\n");
00085                 winx_heap_free(ntfs_data);
00086                 return STATUS_UNSUCCESSFUL;
00087         }
00088
00089         ntfs_record_size = ntfs_data->BytesPerFileRecordSegment;
00090         DebugPrint("NTFS record size = %u bytes\n",ntfs_record_size);
00091         if(ntfs_record_size == 0){
00092                 DebugPrint("NTFS record size is equal to zero!\n");
00093                 winx_heap_free(ntfs_data);
00094                 return STATUS_UNSUCCESSFUL;
00095         }
00096
00097         max_mft_entries = Stat.mft_size / ntfs_record_size;
00098         DebugPrint("MFT contains no more than %I64u records\n",
00099                 max_mft_entries);
00100
00101         //DbgPrintFreeSpaceList();
00102         winx_heap_free(ntfs_data);
00103         return STATUS_SUCCESS;
00104 }
00105
00111 BOOLEAN ScanMFT(void)
00112 {
00113         PMY_FILE_INFORMATION pmfi;
00114         PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob = NULL;
00115         ULONG nfrob_size;
00116         ULONGLONG mft_id, ret_mft_id;
00117         NTSTATUS status;
00118
00119         ULONGLONG tm, time, tm2, time2;
00120
00121         DebugPrint("MFT scan started!\n");
00122         number_of_processed_attr_list_entries = 0;
00123
00124         /* Get information about MFT */
00125         status = GetMftLayout();
00126         if(!NT_SUCCESS(status)){
00127                 DebugPrint("ProcessMFT() failed!\n");
00128                 DebugPrint("MFT scan finished!\n");
00129                 return FALSE;
00130         }
00131
00132         /* allocate memory for NTFS record */
00133         nfrob_size = sizeof(NTFS_FILE_RECORD_OUTPUT_BUFFER) + ntfs_record_size - 1;
00134         tm = winx_xtime();
00135         pnfrob = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)winx_heap_alloc(nfrob_size);
00136         if(!pnfrob){
00137                 DebugPrint("Not enough memory for NTFS_FILE_RECORD_OUTPUT_BUFFER!\n");
00138                 out_of_memory_condition_counter ++;
00139                 DebugPrint("MFT scan finished!\n");
00140                 return FALSE;
00141         }
00142
00143         /* allocate memory for MY_FILE_INFORMATION structure */
00144         pmfi = (PMY_FILE_INFORMATION)winx_heap_alloc(sizeof(MY_FILE_INFORMATION));
00145         if(!pmfi){
00146                 DebugPrint("Not enough memory for MY_FILE_INFORMATION structure!\n");
00147                 out_of_memory_condition_counter ++;
00148                 winx_heap_free(pnfrob);
00149                 DebugPrint("MFT scan finished!\n");
00150                 return FALSE;
00151         }
00152
00153         /* read all MFT records sequentially */
00154         //MftBlockmap   = NULL;
00155         MaxMftEntriesNumberUpdated = FALSE;
00156         UpdateMaxMftEntriesNumber(pnfrob,nfrob_size);
00157         mft_id = max_mft_entries - 1;
00158         DebugPrint("\n");
00159
00160         if(MaxMftEntriesNumberUpdated == FALSE){
00161                 DebugPrint("UpdateMaxMftEntriesNumber() failed!\n");
00162                 DebugPrint("MFT scan finished!\n");
00163                 winx_heap_free(pnfrob);
00164                 winx_heap_free(pmfi);
00165                 //DestroyMftBlockmap();
00166                 return FALSE;
00167         }
00168
00169         DebugPrint("+-------------------------------------------------------+\n");
00170         DebugPrint("|          MFT records scanning loop begins...          |\n");
00171         DebugPrint("+-------------------------------------------------------+\n");
00172         tm2 = winx_xtime();
00173         /* Is MFT size an integral of NTFS record size? */
00174         /*if(MftClusters == 0 || \
00175           (MftClusters * (ULONGLONG)dx->bytes_per_cluster % (ULONGLONG)dx->ntfs_record_size) || \
00176           (dx->ntfs_record_size % dx->bytes_per_sector)){*/
00177         if(TRUE){
00178                 MftScanDirection = MFT_SCAN_RTL;
00179                 while(1){
00180                         if(CheckForStopEvent()) break;
00181                         status = GetMftRecord(pnfrob,nfrob_size,mft_id);
00182                         if(!NT_SUCCESS(status)){
00183                                 if(mft_id == 0){
00184                                         DebugPrint("FSCTL_GET_NTFS_FILE_RECORD failed: %x!\n",status);
00185                                         winx_heap_free(pnfrob);
00186                                         winx_heap_free(pmfi);
00187                                         DebugPrint("MFT records scanning loop completed!\n");
00188                                         DebugPrint("MFT scan finished!\n");
00189                                         //DestroyMftBlockmap();
00190                                         return FALSE;
00191                                 }
00192                                 /* it returns 0xc000000d (invalid parameter) for non existing records */
00193                                 mft_id --; /* try to retrieve a previous record */
00194                                 continue;
00195                         }
00196
00197                         /* analyse retrieved mft record */
00198                         ret_mft_id = GetMftIdFromFRN(pnfrob->FileReferenceNumber);
00199                         #ifdef DETAILED_LOGGING
00200                         DebugPrint("NTFS record found, id = %I64u\n",ret_mft_id);
00201                         #endif
00202                         AnalyseMftRecord(pnfrob,nfrob_size,pmfi);
00203
00204                         /* go to the next record */
00205                         if(ret_mft_id == 0 || mft_id == 0) break;
00206                         if(ret_mft_id > mft_id){
00207                                 /* avoid infinite loops */
00208                                 DebugPrint("Returned MFT record ID is above expected!\n");
00209                                 mft_id --;
00210                         } else {
00211                                 mft_id = ret_mft_id - 1;
00212                         }
00213                 }
00214         }/* else {
00215                 DebugPrint("-Ultradfg- MFT size is an integral of NTFS record size :-D\n");
00216                 ScanMftDirectly(dx,pnfrob,nfrob_size,pmfi);
00217         }*/
00218
00219         /* free allocated memory */
00220         winx_heap_free(pnfrob);
00221         winx_heap_free(pmfi);
00222         //DestroyMftBlockmap();
00223         time2 = winx_xtime() - tm2;
00224         DebugPrint("MFT records scanning loop completed in %I64u ms.\n",time2);
00225
00226         /* Build paths. */
00227         BuildPaths();
00228
00229         DebugPrint("%u attribute lists entries totally processed.\n",
00230                 number_of_processed_attr_list_entries);
00231         time = winx_xtime() - tm;
00232         DebugPrint("MFT scan completed in %I64u ms.\n",time);
00233         return TRUE;
00234 }
00235
00242 void UpdateMaxMftEntriesNumber(PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob,ULONG nfrob_size)
00243 {
00244         NTSTATUS status;
00245         PFILE_RECORD_HEADER pfrh;
00246
00247         /* Get record for FILE_MFT using WinAPI, because MftBitmap is not ready yet. */
00248         status = GetMftRecord(pnfrob,nfrob_size,FILE_MFT);
00249         if(!NT_SUCCESS(status)){
00250                 DebugPrint("UpdateMaxMftEntriesNumber(): FSCTL_GET_NTFS_FILE_RECORD failed: %x!\n",status);
00251                 return;
00252         }
00253         if(GetMftIdFromFRN(pnfrob->FileReferenceNumber) != FILE_MFT){
00254                 DebugPrint("UpdateMaxMftEntriesNumber() failed - unable to get FILE_MFT record.\n");
00255                 return;
00256         }
00257
00258         /* Find DATA attribute. */
00259         pfrh = (PFILE_RECORD_HEADER)pnfrob->FileRecordBuffer;
00260         if(!IsFileRecord(pfrh)){
00261                 DebugPrint("UpdateMaxMftEntriesNumber() failed - FILE_MFT record has invalid type %u.\n",
00262                         pfrh->Ntfs.Type);
00263                 return;
00264         }
00265         if(!(pfrh->Flags & 0x1)){
00266                 DebugPrint("UpdateMaxMftEntriesNumber() failed - FILE_MFT record marked as free.\n");
00267                 return; /* skip free records */
00268         }
00269
00270         EnumerateAttributes(pfrh,UpdateMaxMftEntriesNumberCallback,NULL);
00271 }
00272
00275 void __stdcall UpdateMaxMftEntriesNumberCallback(PATTRIBUTE pattr,PMY_FILE_INFORMATION pmfi)
00276 {
00277         PNONRESIDENT_ATTRIBUTE pnr_attr;
00278
00279         /* here pmfi == NULL always */
00280         (void)pmfi;
00281
00282         if(pattr->Nonresident && pattr->AttributeType == AttributeData){
00283                 pnr_attr = (PNONRESIDENT_ATTRIBUTE)pattr;
00284                 if(ntfs_record_size){
00285                         max_mft_entries = pnr_attr->DataSize / ntfs_record_size;
00286                         Stat.mft_size = pnr_attr->DataSize;
00287                         //MftClusters = 0;
00288                         //MftBlockmap = NULL;
00289                         //ProcessMFTRunList(dx,pnr_attr);
00290                         /* FIXME: check correctness of MftBlockmap */
00291                         MaxMftEntriesNumberUpdated = TRUE;
00292                 }
00293                 DebugPrint("MFT contains no more than %I64u records (more accurately)\n",
00294                         max_mft_entries);
00295         }
00296 }
00297
00306 NTSTATUS GetMftRecord(PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob,
00307                                           ULONG nfrob_size,ULONGLONG mft_id)
00308 {
00309         NTFS_FILE_RECORD_INPUT_BUFFER nfrib;
00310         IO_STATUS_BLOCK iosb;
00311         NTSTATUS status;
00312
00313         nfrib.FileReferenceNumber = mft_id;
00314
00315         RtlZeroMemory(pnfrob,nfrob_size); /* required by x64 system, otherwise it trashes stack */
00316         status = NtFsControlFile(winx_fileno(fVolume),NULL,NULL,NULL,&iosb, 00317                         FSCTL_GET_NTFS_FILE_RECORD, 00318                         &nfrib,sizeof(nfrib), 00319                         pnfrob, nfrob_size);
00320         if(NT_SUCCESS(status)/* == STATUS_PENDING*/){
00321                 (void)NtWaitForSingleObject(winx_fileno(fVolume),FALSE,NULL);
00322                 status = iosb.Status;
00323         }
00324         return status;
00325 }
00326
00335 int UpdateAttributeName(PFILENAME pfn,PMY_FILE_INFORMATION pmfi)
00336 {
00337         short *buffer;
00338         UNICODE_STRING us;
00339
00340         /* if file name is empty, we should do nothing */
00341         if(pmfi->Name[0] == 0){
00342                 DebugPrint("MftRecord has empty filename, MftId = %I64u, Parent MftId = %I64u\n",
00343                         pmfi->BaseMftId,pmfi->ParentDirectoryMftId);
00344                 return (-1);
00345         }
00346
00347         /* if file name is not empty, append them */
00348         buffer = winx_heap_alloc(MAX_NTFS_PATH * sizeof(short));
00349         if(buffer == NULL){
00350                 DebugPrint("Cannot allocate memory for buffer in UpdateAttributeName()!\n");
00351                 out_of_memory_condition_counter ++;
00352                 return (-1);
00353         }
00354         if(pfn->name.Buffer[0])
00355                 (void)_snwprintf(buffer,MAX_NTFS_PATH,L"%s:%s",pmfi->Name,pfn->name.Buffer);
00356         else
00357                 (void)wcsncpy(buffer,pmfi->Name,MAX_NTFS_PATH);
00358         buffer[MAX_NTFS_PATH - 1] = 0;
00359
00360         if(!RtlCreateUnicodeString(&us,buffer)){
00361                 DebugPrint("UpdateAttributeName: Cannot allocate memory for new name!\n");
00362                 out_of_memory_condition_counter ++;
00363                 winx_heap_free(buffer);
00364                 return (-1);
00365         }
00366
00367         RtlFreeUnicodeString(&(pfn->name));
00368         pfn->name.Buffer = us.Buffer;
00369         pfn->name.Length = us.Length;
00370         pfn->name.MaximumLength = us.MaximumLength;
00371
00372         /*DebugPrint("UpdateAttributeName: %ws, MftId = %I64u, Parent MftId = %I64u\n",
00373                 pfn->name.Buffer,pmfi->BaseMftId,pmfi->ParentDirectoryMftId);*/
00374         winx_heap_free(buffer);
00375         return 0;
00376 }
00377
00386 void AnalyseMftRecord(PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob,
00387                                           ULONG nfrob_size,PMY_FILE_INFORMATION pmfi)
00388 {
00389         PFILE_RECORD_HEADER pfrh;
00390         #ifdef DETAILED_LOGGING
00391         USHORT Flags;
00392         #endif
00393         PFILENAME pfn, next_pfn, head;
00394         BOOLEAN DirectoryAdded;
00395
00396         /* analyse record's header */
00397         pfrh = (PFILE_RECORD_HEADER)pnfrob->FileRecordBuffer;
00398
00399         if(!IsFileRecord(pfrh)) return;
00400         if(!(pfrh->Flags & 0x1)) return; /* skip free records */
00401
00402         /* analyse file record */
00403         #ifdef DETAILED_LOGGING
00404         Flags = pfrh->Flags;
00405         if(Flags & 0x2) DebugPrint("Directory\n"); /* May be wrong? */
00406         #endif
00407 
00408         if(pfrh->BaseFileRecord){ /* skip child records, we will analyse them later */
00409                 //DebugPrint("-Ultradfg- BaseFileRecord (id) = %I64u\n",
00410                 //      GetMftIdFromFRN(pfrh->BaseFileRecord));
00411                 //DebugPrint("\n");
00412                 return;
00413         }
00414
00415         /* analyse attributes of MFT entry */
00416         pmfi->BaseMftId = GetMftIdFromFRN(pnfrob->FileReferenceNumber);
00417         pmfi->ParentDirectoryMftId = FILE_root;
00418         pmfi->Flags = 0x0;
00419
00420         /* FIXME: L:\.:$SECURITY_DESCRIPTOR ?! */
00421         pmfi->IsDirectory = (pfrh->Flags & 0x2) ? TRUE : FALSE;
00422
00423         pmfi->IsReparsePoint = FALSE;
00424         pmfi->NameType = 0x0; /* Assume FILENAME_POSIX */
00425         memset(pmfi->Name,0,MAX_NTFS_PATH);
00426
00427         /* skip AttributeList attributes */
00428         EnumerateAttributes(pfrh,AnalyseAttributeCallback,pmfi);
00429
00430         /* analyse AttributeList attributes */
00431         EnumerateAttributes(pfrh,AnalyseAttributeListCallback,pmfi);
00432
00433         /* append the filename to attributes */
00434         head = filelist;
00435         for(pfn = filelist; pfn != NULL;){
00436                 if(MftScanDirection == MFT_SCAN_RTL){
00437                         if(pfn->BaseMftId > pmfi->BaseMftId) break; /* we have no chance to find record in list */
00438                 } else {
00439                         if(pfn->BaseMftId < pmfi->BaseMftId) break;
00440                 }
00441                 next_pfn = pfn->next_ptr;
00442                 if(pfn->ParentDirectoryMftId == pmfi->ParentDirectoryMftId && 00443                   pfn->BaseMftId == pmfi->BaseMftId){
00444                         if(UpdateAttributeName(pfn,pmfi) < 0){
00445                                 winx_list_remove_item((list_entry **)(void *)&filelist,(list_entry *)pfn);
00446                                 if(filelist == NULL) break;
00447                                 if(filelist != head){
00448                                         head = filelist;
00449                                         pfn = next_pfn;
00450                                         continue;
00451                                 }
00452                         }
00453                 }
00454                 pfn = next_pfn;
00455                 if(pfn == head) break;
00456         }
00457
00458         /* just for debugging */
00459         /*if(winx_wcsistr(pmfi->Name,L"Scratch"))
00460                 DebugPrint("@@@@@ %ws DIRECTORY FOUND, MFT_ID = %I64u, PARENT ID = %I64u\n",
00461                         pmfi->Name,pmfi->BaseMftId,pmfi->ParentDirectoryMftId);*/
00462
00463         /* add resident directories to filelist - required by BuildPaths() */
00464         if(pmfi->IsDirectory){
00465                 /* is directory already added? */
00466                 DirectoryAdded = FALSE;
00467                 for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){
00468                         /*
00469                         * we scan mft from the end to the beginning (RTL)
00470                         * and we add new records to the left side of the file list...
00471                         */
00472                         if(MftScanDirection == MFT_SCAN_RTL){
00473                                 if(pfn->BaseMftId > pmfi->BaseMftId) break; /* we have no chance to find record in list */
00474                         } else {
00475                                 if(pfn->BaseMftId < pmfi->BaseMftId) break;
00476                         }
00477                         if(!wcscmp(pfn->name.Buffer,pmfi->Name) && 00478                           (pfn->ParentDirectoryMftId == pmfi->ParentDirectoryMftId) && 00479                           (pfn->BaseMftId == pmfi->BaseMftId))
00480                                 DirectoryAdded = TRUE;
00481                         if(pfn->next_ptr == filelist) break;
00482                 }
00483                 if(!DirectoryAdded) AddResidentDirectoryToFileList(pmfi);
00484         }
00485
00486         /* update cluster map and statistics */
00487         UpdateClusterMapAndStatistics(pmfi);
00488
00489         #ifdef DETAILED_LOGGING
00490         DebugPrint("\n");
00491         #endif
00492 }
00493
00504 void EnumerateAttributes(PFILE_RECORD_HEADER pfrh,
00505                                                  ATTRHANDLER_PROC ahp,PMY_FILE_INFORMATION pmfi)
00506 {
00507         PATTRIBUTE pattr;
00508         ULONG attr_length;
00509         USHORT attr_offset;
00510
00511         attr_offset = pfrh->AttributeOffset;
00512         pattr = (PATTRIBUTE)((char *)pfrh + attr_offset);
00513
00514         while(pattr){
00515                 if(CheckForStopEvent()) break;
00516
00517                 /* is an attribute header inside a record bounds? */
00518                 if(attr_offset + sizeof(ATTRIBUTE) > pfrh->BytesInUse || 00519                         attr_offset + sizeof(ATTRIBUTE) > ntfs_record_size)     break;
00520
00521                 /* is it a valid attribute */
00522                 if(pattr->AttributeType == 0xffffffff) break;
00523                 if(pattr->AttributeType == 0x0) break;
00524                 if(pattr->Length == 0) break;
00525
00526                 /* is an attribute inside a record bounds? */
00527                 if(attr_offset + pattr->Length > pfrh->BytesInUse || 00528                         attr_offset + pattr->Length > ntfs_record_size) break;
00529
00530                 /* is an attribute length valid? */
00531                 if(pattr->Nonresident){
00532                         if(pattr->Length < (sizeof(NONRESIDENT_ATTRIBUTE) - sizeof(ULONGLONG))){
00533                                 DebugPrint("Nonresident attribute length is invalid!\n");
00534                                 break;
00535                         }
00536                 } else {
00537                         if(pattr->Length < sizeof(RESIDENT_ATTRIBUTE)){
00538                                 DebugPrint("Resident attribute length is invalid!\n");
00539                                 break;
00540                         }
00541                 }
00542
00543                 /* call specified callback procedure */
00544                 ahp(pattr,pmfi);
00545
00546                 /* go to the next attribute */
00547                 attr_length = pattr->Length;
00548                 attr_offset += (USHORT)(attr_length);
00549                 pattr = (PATTRIBUTE)((char *)pattr + attr_length);
00550         }
00551 }
00552
00555 void __stdcall AnalyseAttributeCallback(PATTRIBUTE pattr,PMY_FILE_INFORMATION pmfi)
00556 {
00557         if(pattr->AttributeType != AttributeAttributeList)
00558                 AnalyseAttribute(pattr,pmfi);
00559 }
00560
00563 void __stdcall AnalyseAttributeListCallback(PATTRIBUTE pattr,PMY_FILE_INFORMATION pmfi)
00564 {
00565         if(pattr->AttributeType == AttributeAttributeList)
00566                 AnalyseAttribute(pattr,pmfi);
00567 }
00568
00572 void AnalyseAttribute(PATTRIBUTE pattr,PMY_FILE_INFORMATION pmfi)
00573 {
00574         if(pattr->Nonresident) AnalyseNonResidentAttribute((PNONRESIDENT_ATTRIBUTE)pattr,pmfi);
00575         else AnalyseResidentAttribute((PRESIDENT_ATTRIBUTE)pattr,pmfi);
00576 }
00577
00589 void AnalyseResidentAttribute(PRESIDENT_ATTRIBUTE pr_attr,PMY_FILE_INFORMATION pmfi)
00590 {
00591         if(pr_attr->ValueOffset == 0 || pr_attr->ValueLength == 0){
00592                 /*DebugPrint("AnalyseResidentAttribute: Invalid attribute, "
00593                         "MFT ID = %I64u, Attribute Type = 0x%x, ValueOffset = %u, ValueLength = %u\n",
00594                         pmfi->BaseMftId,(UINT)pr_attr->Attribute.AttributeType,
00595                         (UINT)pr_attr->ValueOffset,(UINT)pr_attr->ValueLength);
00596                 */
00597                 /*
00598                 * This is an ordinary case when some attribute data was truncated.
00599                 * For example, when some large file becomes empty its $DATA attribute
00600                 * becomes resident and its ValueLength becomes to be equal to zero.
00601                 */
00602                 return;
00603         }
00604
00605         switch(pr_attr->Attribute.AttributeType){
00606         case AttributeStandardInformation: /* always resident */
00607                 GetFileFlags(pr_attr,pmfi); break;
00608
00609         case AttributeFileName: /* always resident */
00610                 GetFileName(pr_attr,pmfi); break;
00611
00612         case AttributeVolumeInformation: /* always resident */
00613                 GetVolumeInformationData(pr_attr); break;
00614
00615         case AttributeAttributeList:
00616                 //DebugPrint("Resident AttributeList found!\n");
00617                 AnalyseResidentAttributeList(pr_attr,pmfi);
00618                 break;
00619
00620         /*case AttributeIndexRoot:  // always resident */
00621         /*case AttributeIndexAllocation:*/
00622
00623         case AttributeReparsePoint:
00624                 CheckReparsePointResident(pr_attr,pmfi);
00625                 break;
00626
00627         default:
00628                 break;
00629         }
00630 }
00631
00639 void GetFileFlags(PRESIDENT_ATTRIBUTE pr_attr,PMY_FILE_INFORMATION pmfi)
00640 {
00641         PSTANDARD_INFORMATION psi;
00642         ULONG Flags;
00643
00644         psi = (PSTANDARD_INFORMATION)((char *)pr_attr + pr_attr->ValueOffset);
00645         if(pr_attr->ValueLength < 48){ /* 48 = size of the shortest STANDARD_INFORMATION structure */
00646                 DebugPrint("STANDARD_INFORMATION attribute is too short!\n");
00647         } else {
00648                 Flags = psi->FileAttributes;
00649                 pmfi->Flags = Flags;
00650         }
00651 }
00652
00661 void GetFileName(PRESIDENT_ATTRIBUTE pr_attr,PMY_FILE_INFORMATION pmfi)
00662 {
00663         PFILENAME_ATTRIBUTE pfn_attr;
00664         short *name;
00665         UCHAR name_type;
00666         ULONGLONG parent_mft_id;
00667
00668         pfn_attr = (PFILENAME_ATTRIBUTE)((char *)pr_attr + pr_attr->ValueOffset);
00669         if(pr_attr->ValueLength < sizeof(FILENAME_ATTRIBUTE)){
00670                 DebugPrint("FILENAME_ATTRIBUTE is too short!\n");
00671                 return;
00672         }
00673
00674         parent_mft_id = GetMftIdFromFRN(pfn_attr->DirectoryFileReferenceNumber);
00675
00676         if(pfn_attr->NameLength){
00677                 name = (short *)winx_heap_alloc((pfn_attr->NameLength + 1) * sizeof(short));
00678                 if(!name){
00679                         DebugPrint("Cannot allocate memory for GetFileName()!\n");
00680                         out_of_memory_condition_counter ++;
00681                         return;
00682                 }
00683                 (void)wcsncpy(name,pfn_attr->Name,pfn_attr->NameLength);
00684                 name[pfn_attr->NameLength] = 0;
00685                 //DbgPrint("-Ultradfg- Filename = %ws, parent id = %I64u\n",name,parent_mft_id);
00686                 if(name[0] == 0) DebugPrint("Empty filename found ;)\n");
00687                 if(parent_mft_id == pmfi->BaseMftId && pmfi->BaseMftId != FILE_root)
00688                         DebugPrint("Recursion found - file identifies themselves as a parent ;)\n");
00689                 if(name[0] && (parent_mft_id != pmfi->BaseMftId || pmfi->BaseMftId == FILE_root)){
00690                         /* update pmfi members */
00691                         pmfi->ParentDirectoryMftId = parent_mft_id;
00692                         /* save filename */
00693                         name_type = pfn_attr->NameType;
00694                         UpdateFileName(pmfi,name,name_type);
00695                 }
00696                 winx_heap_free(name);
00697         } else DebugPrint("GetFileName: Empty name found, MFT ID = %I64u\n",pmfi->BaseMftId);
00698 }
00699
00709 void UpdateFileName(PMY_FILE_INFORMATION pmfi,WCHAR *name,UCHAR name_type)
00710 {
00711         /* compare name type with type of saved name */
00712         if(pmfi->Name[0] == 0 || pmfi->NameType == FILENAME_DOS || 00713           ((pmfi->NameType & FILENAME_WIN32) && (name_type == FILENAME_POSIX))){
00714                 (void)wcsncpy(pmfi->Name,name,MAX_NTFS_PATH);
00715                 pmfi->Name[MAX_NTFS_PATH-1] = 0;
00716                 pmfi->NameType = name_type;
00717         }
00718 }
00719
00725 void GetVolumeInformationData(PRESIDENT_ATTRIBUTE pr_attr)
00726 {
00727         PVOLUME_INFORMATION pvi;
00728         ULONG mj_ver, mn_ver;
00729         BOOLEAN dirty_flag = FALSE;
00730
00731         pvi = (PVOLUME_INFORMATION)((char *)pr_attr + pr_attr->ValueOffset);
00732         if(pr_attr->ValueLength < sizeof(VOLUME_INFORMATION)){
00733                 DebugPrint("VOLUME_INFORMATION is too short!\n");
00734                 return;
00735         }
00736
00737         mj_ver = (ULONG)pvi->MajorVersion;
00738         mn_ver = (ULONG)pvi->MinorVersion;
00739         if(pvi->Flags & 0x1) dirty_flag = TRUE;
00740         DebugPrint("NTFS Version %u.%u\n",mj_ver,mn_ver);
00741         if(dirty_flag) DebugPrint("Volume is dirty!\n");
00742 }
00743
00751 void CheckReparsePointResident(PRESIDENT_ATTRIBUTE pr_attr,PMY_FILE_INFORMATION pmfi)
00752 {
00753         PREPARSE_POINT prp;
00754         ULONG tag;
00755
00756         prp = (PREPARSE_POINT)((char *)pr_attr + pr_attr->ValueOffset);
00757         if(pr_attr->ValueLength >= sizeof(ULONG)){
00758                 tag = prp->ReparseTag;
00759                 DebugPrint("Reparse tag = 0x%x\n",tag);
00760         } else {
00761                 DebugPrint("REPARSE_POINT is too short!\n");
00762         }
00763
00764         pmfi->IsReparsePoint = TRUE;
00765 }
00766
00776 void AnalyseResidentAttributeList(PRESIDENT_ATTRIBUTE pr_attr,PMY_FILE_INFORMATION pmfi)
00777 {
00778         PATTRIBUTE_LIST attr_list_entry;
00779         USHORT length;
00780
00781         attr_list_entry = (PATTRIBUTE_LIST)((char *)pr_attr + pr_attr->ValueOffset);
00782
00783         while(TRUE){
00784                 if( ((char *)attr_list_entry + sizeof(ATTRIBUTE_LIST) - sizeof(attr_list_entry->AlignmentOrReserved)) >
00785                         ((char *)pr_attr + pr_attr->ValueOffset + pr_attr->ValueLength) ) break;
00786                 if(CheckForStopEvent()) break;
00787                 /* is it a valid attribute */
00788                 if(attr_list_entry->AttributeType == 0xffffffff) break;
00789                 if(attr_list_entry->AttributeType == 0x0) break;
00790                 if(attr_list_entry->Length == 0) break;
00791                 //DebugPrint("@@@@@@@@@ attr_list_entry Length = %u\n", attr_list_entry->Length);
00792                 AnalyseAttributeFromAttributeList(attr_list_entry,pmfi);
00793                 /* go to the next attribute list entry */
00794                 length = attr_list_entry->Length;
00795                 attr_list_entry = (PATTRIBUTE_LIST)((char *)attr_list_entry + length);
00796         }
00797 }
00798
00805 void AnalyseAttributeFromAttributeList(PATTRIBUTE_LIST attr_list_entry,PMY_FILE_INFORMATION pmfi)
00806 {
00807         ULONGLONG child_record_mft_id;
00808         ATTRIBUTE_TYPE attr_type;
00809         USHORT attr_number;
00810         UCHAR name_length = 0;
00811         short *attr_name = NULL;
00812
00813         /*
00814         * 21 Feb 2010
00815         * The right implementation must analyze a single
00816         * specific attribute from the MFT record pointed
00817         * by the attribute list entry.
00818         */
00819
00820         /* 1. save the name of the attribute */
00821         name_length = attr_list_entry->NameLength;
00822         if(attr_list_entry->NameOffset && name_length){
00823                 attr_name = winx_heap_alloc((name_length + 1) * sizeof(short));
00824                 if(attr_name == NULL){
00825                         DebugPrint("Cannot allocate %u bytes of memory for AnalyseAttributeFromAttributeList()!\n",
00826                                 (name_length + 1) * sizeof(short));
00827                         out_of_memory_condition_counter ++;
00828                         return;
00829                 }
00830                 memcpy(attr_name,
00831                         (char *)attr_list_entry + attr_list_entry->NameOffset,
00832                         name_length * sizeof(short));
00833                 attr_name[name_length] = 0;
00834                 if(attr_name[0] == 0){
00835                         winx_heap_free(attr_name);
00836                         attr_name = NULL;
00837                 }
00838         }
00839         /* attr_name is NULL here for empty names */
00840
00841         /* 2. save the attribute type */
00842         attr_type = attr_list_entry->AttributeType;
00843
00844         /* 3. save the identifier of the child record containing the attribute */
00845         child_record_mft_id = GetMftIdFromFRN(attr_list_entry->FileReferenceNumber);
00846
00847         /* 4. save the AttributeNumber */
00848         attr_number = attr_list_entry->AttributeNumber;
00849
00850         /* 5. analyze a single attribute */
00851         AnalyseAttributeFromMftRecord(child_record_mft_id,attr_type,attr_name,attr_number,pmfi);
00852
00853         /* 6. free resources */
00854         if(attr_name) winx_heap_free(attr_name);
00855 }
00856
00868 void AnalyseAttributeFromMftRecord(ULONGLONG mft_id,ATTRIBUTE_TYPE attr_type,
00869                 short *attr_name,USHORT attr_number,PMY_FILE_INFORMATION pmfi)
00870 {
00871         PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob = NULL;
00872         ULONG nfrob_size;
00873         NTSTATUS status;
00874         PFILE_RECORD_HEADER pfrh;
00875
00876         /*
00877         * Skip attributes stored in the base mft record,
00878         * because they will be scanned anyway.
00879         */
00880         if(mft_id == pmfi->BaseMftId){
00881                 //DebugPrint("Attribute list entry points to 0x%x attribute of the base record.\n",(UINT)attr_type);
00882                 return;
00883         }
00884
00885         /* allocate memory for a single mft record */
00886         nfrob_size = sizeof(NTFS_FILE_RECORD_OUTPUT_BUFFER) + ntfs_record_size - 1;
00887         pnfrob = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)winx_heap_alloc(nfrob_size);
00888         if(!pnfrob){
00889                 DebugPrint("AnalyseAttributeFromMftRecord():\n");
00890                 DebugPrint("Not enough memory for NTFS_FILE_RECORD_OUTPUT_BUFFER!\n");
00891                 out_of_memory_condition_counter ++;
00892                 return;
00893         }
00894
00895         /* get specified mft record */
00896         status = GetMftRecord(pnfrob,nfrob_size,mft_id);
00897         if(!NT_SUCCESS(status)){
00898                 DebugPrint("AnalyseAttributeFromMftRecord(): FSCTL_GET_NTFS_FILE_RECORD failed: %x!\n",status);
00899                 winx_heap_free(pnfrob);
00900                 return;
00901         }
00902         if(GetMftIdFromFRN(pnfrob->FileReferenceNumber) != mft_id){
00903                 DebugPrint("AnalyseAttributeFromAttributeList() failed - unable to get %I64u record.\n",mft_id);
00904                 winx_heap_free(pnfrob);
00905                 return;
00906         }
00907
00908         /* validate the record header */
00909         pfrh = (PFILE_RECORD_HEADER)pnfrob->FileRecordBuffer;
00910         if(!IsFileRecord(pfrh)){
00911                 DebugPrint("AnalyseAttributeFromMftRecord() failed - %I64u record has invalid type %u.\n",
00912                         mft_id,pfrh->Ntfs.Type);
00913                 winx_heap_free(pnfrob);
00914                 return;
00915         }
00916         if(!(pfrh->Flags & 0x1)){
00917                 DebugPrint("AnalyseAttributeFromMftRecord() failed\n");
00918                 DebugPrint("%I64u record marked as free.\n",mft_id);
00919                 winx_heap_free(pnfrob);
00920                 return; /* skip free records */
00921         }
00922
00923         if(pfrh->BaseFileRecord == 0){
00924                 DebugPrint("AnalyseAttributeFromMftRecord() failed - %I64u is not a child record.\n",mft_id);
00925                 winx_heap_free(pnfrob);
00926                 return;
00927         }
00928
00929         /* search for a specified attribute */
00930         AnalyseSingleAttribute(mft_id,pfrh,attr_type,attr_name,attr_number,pmfi);
00931
00932         /* free allocated memory */
00933         winx_heap_free(pnfrob);
00934 }
00935
00938 void AnalyseSingleAttribute(ULONGLONG mft_id,PFILE_RECORD_HEADER pfrh,
00939                 ATTRIBUTE_TYPE attr_type,short *attr_name,USHORT attr_number,PMY_FILE_INFORMATION pmfi)
00940 {
00941         int name_length;
00942         PATTRIBUTE pattr;
00943         ULONG attr_length;
00944         USHORT attr_offset;
00945         short *name = NULL;
00946         BOOLEAN attribute_found = FALSE;
00947         char *resident_status = "";
00948
00949         attr_offset = pfrh->AttributeOffset;
00950         pattr = (PATTRIBUTE)((char *)pfrh + attr_offset);
00951
00952         while(pattr){
00953                 if(CheckForStopEvent()) break;
00954
00955                 /* is an attribute header inside a record bounds? */
00956                 if(attr_offset + sizeof(ATTRIBUTE) > pfrh->BytesInUse || 00957                         attr_offset + sizeof(ATTRIBUTE) > ntfs_record_size)     break;
00958
00959                 /* is it a valid attribute */
00960                 if(pattr->AttributeType == 0xffffffff) break;
00961                 if(pattr->AttributeType == 0x0) break;
00962                 if(pattr->Length == 0) break;
00963
00964                 /* is an attribute inside a record bounds? */
00965                 if(attr_offset + pattr->Length > pfrh->BytesInUse || 00966                         attr_offset + pattr->Length > ntfs_record_size) break;
00967
00968                 /* is an attribute length valid? */
00969                 if(pattr->Nonresident){
00970                         if(pattr->Length < (sizeof(NONRESIDENT_ATTRIBUTE) - sizeof(ULONGLONG))){
00971                                 DebugPrint("Nonresident attribute length is invalid!\n");
00972                                 break;
00973                         }
00974                 } else {
00975                         if(pattr->Length < sizeof(RESIDENT_ATTRIBUTE)){
00976                                 DebugPrint("Resident attribute length is invalid!\n");
00977                                 break;
00978                         }
00979                 }
00980
00981                 /* do we have found the specified attribute? */
00982                 if(pattr->AttributeType == attr_type){
00983                         if(pattr->NameOffset && pattr->NameLength){
00984                                 name = (short *)((char *)pattr + pattr->NameOffset);
00985                                 if(name[0] == 0) name = NULL;
00986                         }
00987                         if(attr_name == NULL){
00988                                 if(name == NULL && pattr->AttributeNumber == attr_number)
00989                                         attribute_found = TRUE;
00990                         } else {
00991                                 if(name != NULL){
00992                                         name_length = wcslen(attr_name);
00993                                         if(name_length == pattr->NameLength){
00994                                                 if(memcmp((void *)attr_name,(void *)name,name_length * sizeof(short)) == 0){
00995                                                         if(pattr->AttributeNumber == attr_number)
00996                                                                 attribute_found = TRUE;
00997                                                 }
00998                                         }
00999                                 }
01000                         }
01001                 }
01002
01003                 if(attribute_found){
01004                         if(pattr->Nonresident) resident_status = "Nonresident";
01005                         else resident_status = "Resident";
01006                         //DebugPrint("An attribute pointed by the attribute list entry found...\n");
01007                         DebugPrint("AttrListEntry: Base MftId = %I64u, MftId = %I64u, Attribute Type = 0x%x, Attribute Number = %u, %s\n",
01008                                 pmfi->BaseMftId,mft_id,(UINT)attr_type,(UINT)attr_number,resident_status);
01009                         if(pattr->Nonresident) AnalyseNonResidentAttribute((PNONRESIDENT_ATTRIBUTE)pattr,pmfi);
01010                         else AnalyseResidentAttribute((PRESIDENT_ATTRIBUTE)pattr,pmfi);
01011                         number_of_processed_attr_list_entries ++;
01012                         return;
01013                 }
01014
01015                 /* go to the next attribute */
01016                 attr_length = pattr->Length;
01017                 attr_offset += (USHORT)(attr_length);
01018                 pattr = (PATTRIBUTE)((char *)pattr + attr_length);
01019         }
01020 }
01021
01025 ATTRIBUTE_NAME __default_attribute_names[] = {
01026         {AttributeAttributeList,       L"$ATTRIBUTE_LIST"       },
01027         {AttributeEA,                  L"$EA"                   },
01028         {AttributeEAInformation,       L"$EA_INFORMATION"       },
01029         {AttributeSecurityDescriptor,  L"$SECURITY_DESCRIPTOR"  },
01030         {AttributeData,                L"$DATA"                 },
01031         {AttributeIndexRoot,           L"$INDEX_ROOT"           },
01032         {AttributeIndexAllocation,     L"$INDEX_ALLOCATION"     },
01033         {AttributeBitmap,              L"$BITMAP"               },
01034         {AttributeReparsePoint,        L"$REPARSE_POINT"        },
01035         {AttributeLoggedUtulityStream, L"$LOGGED_UTILITY_STREAM"}, /* used by EFS */
01036         {0,                            NULL                     }
01037 };
01038
01045 short *GetDefaultAttributeName(ATTRIBUTE_TYPE attr_type)
01046 {
01047         int i;
01048
01049         for(i = 0;; i++){
01050                 if(__default_attribute_names[i].AttributeName == NULL) break;
01051                 if(__default_attribute_names[i].AttributeType == attr_type) break;
01052         }
01053         return __default_attribute_names[i].AttributeName;
01054 }
01055
01067 void AnalyseNonResidentAttribute(PNONRESIDENT_ATTRIBUTE pnr_attr,PMY_FILE_INFORMATION pmfi)
01068 {
01069         ATTRIBUTE_TYPE attr_type;
01070         WCHAR *default_attr_name = NULL;
01071         short *attr_name;
01072         BOOLEAN NonResidentAttrListFound = FALSE;
01073
01074         /* get default name of the attribute */
01075         attr_type = pnr_attr->Attribute.AttributeType;
01076         default_attr_name = GetDefaultAttributeName(attr_type);
01077
01078         /* skip attributes of unknown type */
01079         if(default_attr_name == NULL){
01080                 DebugPrint("Nonresident attribute of unknown type 0x%x found!\n",(UINT)attr_type);
01081                 return;
01082         }
01083
01084         /* allocate memory */
01085         attr_name = (short *)winx_heap_alloc((MAX_NTFS_PATH + 1) * sizeof(short));
01086         if(!attr_name){
01087                 DebugPrint("Cannot allocate memory for attr_name in AnalyseNonResidentAttribute()!\n");
01088                 out_of_memory_condition_counter ++;
01089                 return;
01090         }
01091
01092         /* additional checks */
01093         if(attr_type == AttributeAttributeList){
01094                 DebugPrint("Nonresident AttributeList found!\n");
01095                 NonResidentAttrListFound = TRUE;
01096         }
01097
01098         if(attr_type == AttributeReparsePoint)
01099                 pmfi->IsReparsePoint = TRUE;
01100
01101         /* ------------------------------------------------------------------------- */
01102         /*                          get the attribute name                           */
01103         /* ------------------------------------------------------------------------- */
01104
01105         attr_name[0] = 0;
01106         if(pnr_attr->Attribute.NameLength){
01107                 /* NameLength is always less than MAX_PATH! */
01108                 (void)wcsncpy(attr_name,(short *)((char *)pnr_attr + pnr_attr->Attribute.NameOffset),
01109                         pnr_attr->Attribute.NameLength);
01110                 attr_name[pnr_attr->Attribute.NameLength] = 0;
01111         }
01112
01113         if(attr_name[0] == 0){
01114                 (void)wcsncpy(attr_name,default_attr_name,MAX_NTFS_PATH);
01115                 attr_name[MAX_NTFS_PATH - 1] = 0;
01116         }
01117
01118         /* never append $DATA attribute name */
01119         if(wcscmp(attr_name,L"$DATA") == 0) attr_name[0] = 0;
01120
01121         /* do not append $I30 attribute name - required by GetFileNameAndParentMftId() */
01122         if(wcscmp(attr_name,L"$I30") == 0) attr_name[0] = 0;
01123         if(wcscmp(attr_name,L"$INDEX_ALLOCATION") == 0) attr_name[0] = 0;
01124
01125         /* ------------------------------------------------------------------------- */
01126         /*                   now we have the name of the attribute                   */
01127         /* ------------------------------------------------------------------------- */
01128
01129         /* just for debugging */
01130         /*if(winx_wcsistr(pmfi->Name,L"Scratch")){
01131                 DebugPrint("@@@@ %ws DIRECTORY FOUND, MFT_ID = %I64u, PARENT ID = %I64u\n",
01132                         pmfi->Name,pmfi->BaseMftId,pmfi->ParentDirectoryMftId);
01133                 DebugPrint("@@@@ FULL ATTRIBUTE NAME = %ws:%ws, DEFAULT ATTR NAME = %ws\n",
01134                         pmfi->Name,attr_name,default_attr_name);
01135         }*/
01136
01137         if(NonResidentAttrListFound) DebugPrint("%ws:%ws\n",pmfi->Name,attr_name);
01138
01139         /* skip $BadClus file which may have wrong number of clusters */
01140         if(/*winx_wcsistr(attr_name,L"$BadClus") || */winx_wcsistr(pmfi->Name,L"$BadClus")){
01141                 /* on my system this file always exists, even after chkdsk execution */
01142                 /*DebugPrint("WARNING: %ws:%ws file found! Run CheckDisk program!\n",pmfi->Name,attr_name);*/
01143         } else {
01144                 ProcessRunList(attr_name,pnr_attr,pmfi,NonResidentAttrListFound);
01145         }
01146
01147         /* free allocated memory */
01148         winx_heap_free(attr_name);
01149 }
01150
01153 static ULONG RunLength(PUCHAR run)
01154 {
01155         return (*run & 0xf) + ((*run >> 4) & 0xf) + 1;
01156 }
01157
01160 static LONGLONG RunLCN(PUCHAR run)
01161 {
01162         LONG i;
01163         UCHAR n1 = *run & 0xf;
01164         UCHAR n2 = (*run >> 4) & 0xf;
01165         LONGLONG lcn = (n2 == 0) ? 0 : (LONGLONG)(((signed char *)run)[n1 + n2]);
01166
01167         for(i = n1 + n2 - 1; i > n1; i--)
01168                 lcn = (lcn << 8) + run[i];
01169         return lcn;
01170 }
01171
01174 static ULONGLONG RunCount(PUCHAR run)
01175 {
01176         ULONG i;
01177         UCHAR n = *run & 0xf;
01178         ULONGLONG count = 0;
01179
01180         for(i = n; i > 0; i--)
01181                 count = (count << 8) + run[i];
01182         return count;
01183 }
01184
01195 void ProcessRunList(WCHAR *full_path,
01196                                         PNONRESIDENT_ATTRIBUTE pnr_attr,PMY_FILE_INFORMATION pmfi,
01197                                         BOOLEAN is_attr_list)
01198 {
01199         PUCHAR run;
01200         ULONGLONG lcn, vcn, length;
01201         PFILENAME pfn;
01202         BOOLEAN is_compressed = (pnr_attr->Attribute.Flags & 0x1) ? TRUE : FALSE;
01203
01204 /*      if(pnr_attr->Attribute.Flags & 0x1)
01205                 DbgPrint("[CMP] %ws VCN %I64u - %I64u\n",full_path,pnr_attr->LowVcn,pnr_attr->HighVcn);
01206         else
01207                 DbgPrint("[ORD] %ws VCN %I64u - %I64u\n",full_path,pnr_attr->LowVcn,pnr_attr->HighVcn);
01208 */
01209         /* find corresponding FILENAME structure in dx->filelist or add a new one */
01210         pfn = FindFileListEntryForTheAttribute(full_path,pmfi);
01211         if(pfn == NULL) return;
01212
01213         if(is_compressed) pfn->is_compressed = TRUE;
01214
01215         /* loop through runs */
01216         lcn = 0; vcn = pnr_attr->LowVcn;
01217         run = (PUCHAR)((char *)pnr_attr + pnr_attr->RunArrayOffset);
01218         while(*run){
01219                 lcn += RunLCN(run);
01220                 length = RunCount(run);
01221
01222                 /* skip virtual runs */
01223                 if(RunLCN(run)){
01224                         /* check for data consistency */
01225                         if(!CheckBlock(lcn,length)){
01226                                 DebugPrint("Error in MFT found, run Check Disk program!\n");
01227                                 break;
01228                         }
01229                         ProcessRun(full_path,pmfi,pfn,vcn,length,lcn);
01230                 }
01231
01232                 /* go to the next run */
01233                 run += RunLength(run);
01234                 vcn += length;
01235         }
01236
01237         /* analyze nonresident attribute lists */
01238         if(is_attr_list) AnalyseNonResidentAttributeList(pfn,pmfi,pnr_attr->InitializedSize);
01239 }
01240
01249 void AnalyseNonResidentAttributeList(PFILENAME pfn,PMY_FILE_INFORMATION pmfi,ULONGLONG size)
01250 {
01251         ULONGLONG clusters_to_read;
01252         char *cluster;
01253         char *current_cluster;
01254         PBLOCKMAP block;
01255         ULONGLONG lsn;
01256         NTSTATUS status;
01257         PATTRIBUTE_LIST attr_list_entry;
01258         int i;
01259         USHORT length;
01260
01261         DebugPrint("Allocated size = %I64u bytes.\n",size);
01262         if(size == 0){
01263                 DebugPrint("Empty nonresident attribute list found.\n");
01264                 return;
01265         }
01266
01267         /* allocate memory for an integral number of cluster to hold a whole AttributeList */
01268         clusters_to_read = size / bytes_per_cluster;
01269         /* the following check is a little bit complicated, because _aulldvrm() call is missing on w2k */
01270         if(size - clusters_to_read * bytes_per_cluster/*size % bytes_per_cluster*/) clusters_to_read ++;
01271         cluster = (char *)winx_heap_alloc((SIZE_T)(bytes_per_cluster * clusters_to_read));
01272         if(!cluster){
01273                 DebugPrint("Cannot allocate %I64u bytes of memory for AnalyseNonResidentAttributeList()!\n",
01274                         bytes_per_cluster * clusters_to_read);
01275                 out_of_memory_condition_counter ++;
01276                 return;
01277         }
01278
01279         /* loop through all blocks of file */
01280         current_cluster = cluster;
01281         for(block = pfn->blockmap; block != NULL; block = block->next_ptr){
01282                 /* loop through clusters of the current block */
01283                 for(i = 0; i < block->length; i++){
01284                         /* read current cluster */
01285                         lsn = (block->lcn + i) * sectors_per_cluster;
01286                         status = ReadSectors(lsn,current_cluster,(ULONG)bytes_per_cluster);
01287                         if(!NT_SUCCESS(status)){
01288                                 DebugPrint("Cannot read the %I64u sector: %x!\n",
01289                                         lsn,(UINT)status);
01290                                 goto scan_done;
01291                         }
01292                         clusters_to_read --;
01293                         if(clusters_to_read == 0){
01294                                 /* is it the last cluster of the file? */
01295                                 if(i < (block->length - 1) || block->next_ptr != pfn->blockmap)
01296                                         DebugPrint("The attribute list has more clusters than expected.\n");
01297                                 goto analyze_list;
01298                         }
01299                         current_cluster += bytes_per_cluster;
01300                 }
01301                 if(block->next_ptr == pfn->blockmap) break;
01302         }
01303
01304 analyze_list:
01305         if(clusters_to_read){
01306                 DebugPrint("The attribute list has less number of clusters than expected.\n");
01307                 DebugPrint("Therefore it will be skipped, because anyway we don\'t know its exact size.\n");
01308                 goto scan_done;
01309         }
01310
01311         DebugPrint("Attribute list analysis started...\n");
01312         attr_list_entry = (PATTRIBUTE_LIST)cluster;
01313
01314         while(TRUE){
01315                 if( ((char *)attr_list_entry + sizeof(ATTRIBUTE_LIST) - sizeof(attr_list_entry->AlignmentOrReserved)) >
01316                         ((char *)cluster + size) ) break;
01317                 if(CheckForStopEvent()) break;
01318                 /* is it a valid attribute */
01319                 if(attr_list_entry->AttributeType == 0xffffffff) break;
01320                 if(attr_list_entry->AttributeType == 0x0) break;
01321                 if(attr_list_entry->Length == 0) break;
01322                 //DebugPrint("@@@@@@@@@ attr_list_entry Length = %u\n", attr_list_entry->Length);
01323                 AnalyseAttributeFromAttributeList(attr_list_entry,pmfi);
01324                 /* go to the next attribute list entry */
01325                 length = attr_list_entry->Length;
01326                 attr_list_entry = (PATTRIBUTE_LIST)((char *)attr_list_entry + length);
01327         }
01328         DebugPrint("Attribute list analysis completed.\n");
01329
01330 scan_done:
01331         /* free allocated resources */
01332         winx_heap_free(cluster);
01333 }
01334
01335 /*------------------------ Defragmentation related code ------------------------------*/
01336
01345 ULONGLONG ProcessMftSpace(PNTFS_DATA nd)
01346 {
01347         ULONGLONG start,len,mft_len = 0,mirror_size;
01348
01349         /*
01350         * MFT space must be excluded from the free space list.
01351         * Because Windows 2000 disallows to move files there.
01352         * And because on other systems this dirty technique 
01353         * causes MFT fragmentation.
01354         */
01355
01356         /* 
01357         * Don't increment dx->processed_clusters here, 
01358         * because some parts of MFT are really free.
01359         */
01360         DebugPrint("MFT part   : start : length\n");
01361
01362         /* $MFT */
01363         start = nd->MftStartLcn.QuadPart;
01364         if(nd->BytesPerCluster)
01365                 len = nd->MftValidDataLength.QuadPart / nd->BytesPerCluster;
01366         else
01367                 len = 0;
01368         DebugPrint("$MFT       :%I64u :%I64u\n",start,len);
01369         if(CheckBlock(start,len)){
01370                 /* remark space as reserved */
01371                 RemarkBlock(start,len,MFT_ZONE_SPACE,SYSTEM_OR_FREE_SPACE);
01372                 RemoveFreeSpaceBlock(start,len);
01373                 mft_start = start; mft_end = start + len - 1;
01374                 mft_len += len;
01375         }
01376
01377         /* MFT Zone */
01378         start = nd->MftZoneStart.QuadPart;
01379         len = nd->MftZoneEnd.QuadPart - nd->MftZoneStart.QuadPart + 1;
01380         DebugPrint("MFT Zone   :%I64u :%I64u\n",start,len);
01381         if(CheckBlock(start,len)){
01382                 /* remark space as reserved */
01383                 RemarkBlock(start,len,MFT_ZONE_SPACE,SYSTEM_OR_FREE_SPACE);
01384                 RemoveFreeSpaceBlock(start,len);
01385                 mftzone_start = start; mftzone_end = start + len - 1;
01386         }
01387
01388         /* $MFTMirror */
01389         start = nd->Mft2StartLcn.QuadPart;
01390         len = 1;
01391         mirror_size = nd->BytesPerFileRecordSegment * 4;
01392         if(nd->BytesPerCluster && mirror_size > nd->BytesPerCluster){
01393                 len = mirror_size / nd->BytesPerCluster;
01394                 if(mirror_size - len * nd->BytesPerCluster)
01395                         len ++;
01396         }
01397         DebugPrint("$MFTMirror :%I64u :%I64u\n",start,len);
01398         if(CheckBlock(start,len)){
01399                 /* remark space as reserved */
01400                 RemarkBlock(start,len,MFT_ZONE_SPACE,SYSTEM_OR_FREE_SPACE);
01401                 RemoveFreeSpaceBlock(start,len);
01402                 mftmirr_start = start; mftmirr_end = start + len - 1;
01403         }
01404
01405         return mft_len;
01406 }
01407
01408 /*
01409 * This code may slow down the program: 
01410 * for(pfn = dx->filelist; pfn != NULL; pfn = pfn->next_ptr) for each file
01411 */
01412
01425 void ProcessRun(WCHAR *full_path,PMY_FILE_INFORMATION pmfi,
01426                                 PFILENAME pfn,ULONGLONG vcn,ULONGLONG length,ULONGLONG lcn)
01427 {
01428         PBLOCKMAP block, prev_block = NULL;
01429
01430 /*      if(!wcscmp(full_path,L"\\??\\L:\\go.zip")){
01431                 DbgPrint("VCN %I64u : LEN %I64u : LCN %I64u\n",vcn,length,lcn);
01432         }
01433 */
01434         (void)pmfi;
01435
01436         /* add information to blockmap member of specified pfn structure */
01437         if(pfn->blockmap) prev_block = pfn->blockmap->prev_ptr;
01438         block = (PBLOCKMAP)winx_list_insert_item((list_entry **)&pfn->blockmap,(list_entry *)prev_block,sizeof(BLOCKMAP));
01439         if(!block){
01440                 DebugPrint("Cannot allocate %u bytes of memory for ProcessRun()!\n",sizeof(BLOCKMAP));
01441                 out_of_memory_condition_counter ++;
01442                 return;
01443         }
01444
01445         block->vcn = vcn;
01446         block->length = length;
01447         block->lcn = lcn;
01448
01449         pfn->n_fragments ++;
01450         pfn->clusters_total += block->length;
01451         /*
01452         * Sometimes normal file has more than one fragment, 
01453         * but is not fragmented yet! 8-) 
01454         */
01455         if(block != pfn->blockmap && 01456           block->lcn != (block->prev_ptr->lcn + block->prev_ptr->length))
01457                 pfn->is_fragm = TRUE;
01458 }
01459
01469 PFILENAME FindFileListEntryForTheAttribute(WCHAR *full_path,PMY_FILE_INFORMATION pmfi)
01470 {
01471         PFILENAME pfn;
01472
01473         /* few entries (attributes) may have the same mft id */
01474         for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){
01475                 /*
01476                 * we scan mft from the end to the beginning (RTL)
01477                 * and we add new records to the left side of the file list...
01478                 */
01479                 if(MftScanDirection == MFT_SCAN_RTL){
01480                         if(pfn->BaseMftId > pmfi->BaseMftId) break; /* we have no chance to find record in list */
01481                 } else {
01482                         if(pfn->BaseMftId < pmfi->BaseMftId) break;
01483                 }
01484                 if(!wcscmp(pfn->name.Buffer,full_path) && 01485                   (pfn->ParentDirectoryMftId == pmfi->ParentDirectoryMftId) && 01486                   (pfn->BaseMftId == pmfi->BaseMftId))
01487                         return pfn; /* very slow? */
01488                 //if(pfn->BaseMftId == pmfi->BaseMftId) return pfn; /* safe? */
01489                 if(pfn->next_ptr == filelist) break;
01490         }
01491
01492         pfn = (PFILENAME)winx_list_insert_item((list_entry **)(void *)&filelist,NULL,sizeof(FILENAME));
01493         if(pfn == NULL){
01494                 DebugPrint("Cannot allocate %u bytes of memory for FindFileListEntryForTheAttribute()!\n",sizeof(FILENAME));
01495                 out_of_memory_condition_counter ++;
01496                 return NULL;
01497         }
01498
01499         /* fill a name member of the created structure */
01500         if(!RtlCreateUnicodeString(&pfn->name,full_path)){
01501                 DebugPrint("Not enough memory for pfn->name initialization!\n");
01502                 out_of_memory_condition_counter ++;
01503                 winx_list_remove_item((list_entry **)(void *)&filelist,(list_entry *)pfn);
01504                 return NULL;
01505         }
01506         pfn->blockmap = NULL; /* !!! */
01507         pfn->BaseMftId = pmfi->BaseMftId;
01508         pfn->ParentDirectoryMftId = pmfi->ParentDirectoryMftId;
01509         pfn->PathBuilt = FALSE;
01510         pfn->n_fragments = 0;
01511         pfn->clusters_total = 0;
01512         pfn->is_fragm = FALSE;
01513         pfn->is_compressed = FALSE;
01514         pfn->is_dirty = TRUE;
01515         pfn->is_filtered = FALSE; /* initial state */
01516         return pfn;
01517 }
01518
01525 void UpdateClusterMapAndStatistics(PMY_FILE_INFORMATION pmfi)
01526 {
01527         PFILENAME pfn;
01528         ULONGLONG filesize;
01529
01530         /* All stuff commented with C++ style comments was moved to BuildPaths() function. */
01531         for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){
01532                 /* only the first few entries may have dirty flag set */
01533                 if(pfn->is_dirty == FALSE) break;
01534
01535                 pfn->is_dirty = FALSE;
01536                 /* 1. fill all members of pfn */
01537                 /* 1.1 set flags in pfn ??? */
01538                 /* Note, FILE_ATTR_DIRECTORY is not considered valid in NT.  It is
01539                    reserved for the DOS SUBDIRECTORY flag. */
01540                 /* always sets is_dir flag to FALSE */
01541                 /*if(pmfi->Flags & FILE_ATTRIBUTE_DIRECTORY) pfn->is_dir = TRUE;
01542                 else pfn->is_dir = FALSE;
01543                 */
01544                 pfn->is_dir = pmfi->IsDirectory;
01545                 /* ordinary attributes in compressed file? */
01546                 /*if(pmfi->Flags & FILE_ATTRIBUTE_COMPRESSED) pfn->is_compressed = TRUE;
01547                 else pfn->is_compressed = FALSE;
01548                 */
01549                 if((pmfi->Flags & FILE_ATTRIBUTE_REPARSE_POINT) || pmfi->IsReparsePoint){
01550                         DebugPrint("Reparse point found %ws\n",pfn->name.Buffer);
01551                         pfn->is_reparse_point = TRUE;
01552                 } else pfn->is_reparse_point = FALSE;
01553                 /* 1.2 calculate size of attribute data */
01554                 filesize = pfn->clusters_total * bytes_per_cluster;
01555                 if(sizelimit && filesize > sizelimit) pfn->is_overlimit = TRUE;
01556                 else pfn->is_overlimit = FALSE;
01557                 /* mark some files as filtered out */
01558                 CHECK_FOR_FRAGLIMIT(pfn);
01559                 /* 1.3 detect temporary files and other unwanted stuff */
01560                 if(TemporaryStuffDetected(pmfi)) pfn->is_filtered = TRUE;
01561                 /* 1.4 detect sparse files */
01562                 if(pmfi->Flags & FILE_ATTRIBUTE_SPARSE_FILE)
01563                         DebugPrint("Sparse file found %ws\n",pfn->name.Buffer);
01564                 /* 1.5 detect encrypted files */
01565                 if(pmfi->Flags & FILE_ATTRIBUTE_ENCRYPTED){
01566                         DebugPrint2("Encrypted file found %ws\n",pfn->name.Buffer);
01567                 }
01568                 /* 2. redraw cluster map */
01569 //              MarkSpace(dx,pfn,SYSTEM_SPACE);
01570                 /* 3. update statistics */
01571                 Stat.filecounter ++;
01572                 if(pfn->is_dir) Stat.dircounter ++;
01573                 if(pfn->is_compressed) Stat.compressedcounter ++;
01574                 /* skip here filtered out and big files and reparse points */
01575 //              if(pfn->is_fragm && !pfn->is_filtered && !pfn->is_overlimit && !pfn->is_reparse_point){
01576 //                      dx->fragmfilecounter ++;
01577 //                      dx->fragmcounter += pfn->n_fragments;
01578 //              } else {
01579 //                      dx->fragmcounter ++;
01580 //              }
01581                 /* MFT? */
01582                 //if(pfn->clusters_total > 10000) DbgPrint("%I64u %ws\n",pfn->clusters_total,pfn->name.Buffer);
01583                 //DbgPrint("%ws %I64u\n",pfn->name.Buffer,pfn->clusters_total);
01584                 Stat.processed_clusters += pfn->clusters_total;
01585
01586                 if(pfn->next_ptr == filelist) break;
01587         }
01588 }
01589
01597 BOOLEAN TemporaryStuffDetected(PMY_FILE_INFORMATION pmfi)
01598 {
01599         /* skip temporary files ;-) */
01600         if(pmfi->Flags & FILE_ATTRIBUTE_TEMPORARY){
01601                 DebugPrint2("-Ultradfg- Temporary file found %ws\n",pmfi->Name);
01602                 return TRUE;
01603         }
01604         return FALSE;
01605 }
01606
01607 /* that's unbelievable, but this function runs fast */
01615 BOOLEAN UnwantedStuffDetected(PFILENAME pfn)
01616 {
01617         UNICODE_STRING us;
01618
01619         /* skip all unwanted files by user defined patterns */
01620         if(!RtlCreateUnicodeString(&us,pfn->name.Buffer)){
01621                 DebugPrint("Cannot allocate memory for UnwantedStuffDetected()!\n");
01622                 out_of_memory_condition_counter ++;
01623                 return FALSE;
01624         }
01625         (void)_wcslwr(us.Buffer);
01626
01627         if(in_filter.buffer){
01628                 if(!IsStringInFilter(us.Buffer,&in_filter)){
01629                         RtlFreeUnicodeString(&us); return TRUE; /* not included */
01630                 }
01631         }
01632
01633         if(ex_filter.buffer){
01634                 if(IsStringInFilter(us.Buffer,&ex_filter)){
01635                         RtlFreeUnicodeString(&us); return TRUE; /* excluded */
01636                 }
01637         }
01638         RtlFreeUnicodeString(&us);
01639
01640         return FALSE;
01641 }
01642
01643 PMY_FILE_ENTRY mf = NULL; /* pointer to array of MY_FILE_ENTRY structures */
01644 ULONG n_entries;
01645 BOOLEAN mf_allocated = FALSE;
01646
01651 void BuildPaths(void)
01652 {
01653         ULONGLONG tm, time;
01654         PFILENAME pfn;
01655         ULONG i;
01656
01657         DebugPrint("BuildPaths() started...\n");
01658         tm = winx_xtime();
01659
01660         /* prepare data for fast binary search */
01661         mf_allocated = FALSE;
01662         /*n_entries = Stat.filecounter;*/
01663         /* more accurately */
01664         n_entries = 0;
01665         for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){
01666                 n_entries++;
01667                 if(pfn->next_ptr == filelist) break;
01668         }
01669
01670         if(n_entries){
01671                 mf = (PMY_FILE_ENTRY)winx_heap_alloc(n_entries * sizeof(MY_FILE_ENTRY));
01672                 if(mf != NULL) mf_allocated = TRUE;
01673                 else DebugPrint("Cannot allocate %u bytes of memory for mf array!\n",
01674                                 n_entries * sizeof(MY_FILE_ENTRY));
01675         }
01676         if(mf_allocated){
01677                 /* fill array */
01678                 i = 0;
01679                 for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){
01680                         mf[i].mft_id = pfn->BaseMftId;
01681                         mf[i].pfn = pfn;
01682                         if(i == (n_entries - 1)){
01683                                 if(pfn->next_ptr != filelist)
01684                                         DebugPrint("BuildPaths(): ???\n");
01685                                 break;
01686                         }
01687                         i++;
01688                         if(pfn->next_ptr == filelist) break;
01689                 }
01690                 DebugPrint("Fast binary search will be used.\n");
01691         } else {
01692                 DebugPrint("Slow linear search will be used.\n");
01693         }
01694
01695         for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){
01696                 BuildPath2(pfn);
01697                 if(UnwantedStuffDetected(pfn)) pfn->is_filtered = TRUE;
01698                 MarkFileSpace(pfn,SYSTEM_OR_MFT_ZONE_SPACE);
01699                 /* skip here filtered out and big files and reparse points */
01700                 if(pfn->is_fragm && !pfn->is_reparse_point &&
01701                         ((!pfn->is_filtered && !pfn->is_overlimit) || optimize_flag)
01702                         ){
01703                         Stat.fragmfilecounter ++;
01704                         Stat.fragmcounter += pfn->n_fragments;
01705                 } else {
01706                         Stat.fragmcounter ++;
01707                 }
01708                 if(pfn->next_ptr == filelist) break;
01709         }
01710
01711         /* free allocated resources */
01712         if(mf_allocated) winx_heap_free(mf);
01713         time = winx_xtime() - tm;
01714         DebugPrint("BuildPaths() completed in %I64u ms.\n",time);
01715 }
01716
01722 void BuildPath2(PFILENAME pfn)
01723 {
01724         WCHAR *buffer1;
01725         WCHAR *buffer2;
01726         ULONG offset;
01727         ULONGLONG mft_id,parent_mft_id;
01728         ULONG name_length;
01729         WCHAR header[] = L"\\??\\A:";
01730         BOOLEAN FullPathRetrieved = FALSE;
01731         UNICODE_STRING us;
01732
01733         /* skip invalid files which have no name */
01734         if(pfn->name.Buffer[0] == 0){
01735                 DebugPrint("BuildPath2: Invalid entry found: file has no name!\n");
01736                 DebugPrint("BuildPath2: MFT ID = %I64u\n",pfn->BaseMftId);
01737                 return;
01738         }
01739
01740         /* allocate memory */
01741         buffer1 = (WCHAR *)winx_heap_alloc((MAX_NTFS_PATH) * sizeof(short));
01742         if(!buffer1){
01743                 DebugPrint("BuildPath2(): cannot allocate memory for buffer1\n");
01744                 out_of_memory_condition_counter ++;
01745                 return;
01746         }
01747         buffer2 = (WCHAR *)winx_heap_alloc((MAX_NTFS_PATH) * sizeof(short));
01748         if(!buffer2){
01749                 DebugPrint("BuildPath2(): cannot allocate memory for buffer2\n");
01750                 out_of_memory_condition_counter ++;
01751                 winx_heap_free(buffer1);
01752                 return;
01753         }
01754
01755         /* terminate buffer1 with zero */
01756         offset = MAX_NTFS_PATH - 1;
01757         buffer1[offset] = 0; /* terminating zero */
01758         offset --;
01759
01760         /* copy filename to the right side of the buffer1 */
01761         name_length = wcslen(pfn->name.Buffer);
01762         if(offset < (name_length - 1)){
01763                 DebugPrint("BuildPath2(): %ws filename is too long (%u characters)\n",
01764                         pfn->name.Buffer,name_length);
01765                 winx_heap_free(buffer1);
01766                 winx_heap_free(buffer2);
01767                 return;
01768         }
01769
01770         offset -= (name_length - 1);
01771         (void)wcsncpy(buffer1 + offset,pfn->name.Buffer,name_length);
01772
01773         if(offset == 0) goto path_is_too_long;
01774         offset --;
01775
01776         /* add backslash */
01777         buffer1[offset] = '\\';
01778
01779         if(offset == 0) goto path_is_too_long;
01780         offset --;
01781
01782         if(offset == 0) goto path_is_too_long;
01783
01784         parent_mft_id = pfn->ParentDirectoryMftId;
01785         while(parent_mft_id != FILE_root){
01786                 if(CheckForStopEvent()) goto build_path_done;
01787                 mft_id = parent_mft_id;
01788                 FullPathRetrieved = GetFileNameAndParentMftId(mft_id,&parent_mft_id,buffer2,MAX_NTFS_PATH);
01789                 if(buffer2[0] == 0){
01790                         DebugPrint("BuildPath2(): cannot retrieve parent directory name!\n");
01791                         goto build_path_done;
01792                 }
01793                 //DbgPrint("%ws\n",buffer2);
01794                 /* append buffer2 contents to the right side of buffer1 */
01795                 name_length = wcslen(buffer2);
01796                 if(offset < (name_length - 1)) goto path_is_too_long;
01797                 offset -= (name_length - 1);
01798                 (void)wcsncpy(buffer1 + offset,buffer2,name_length);
01799
01800                 if(FullPathRetrieved) goto update_filename;
01801
01802                 if(offset == 0) goto path_is_too_long;
01803                 offset --;
01804
01805                 /* add backslash */
01806                 buffer1[offset] = '\\';
01807
01808                 if(offset == 0) goto path_is_too_long;
01809                 offset --;
01810
01811                 if(offset == 0) goto path_is_too_long;
01812         }
01813
01814         /*
01815         * the root directory must not contain trailing dot,
01816         * otherwise it cannot be opened for moving
01817         */
01818         if(offset == ((MAX_NTFS_PATH - 1) - 3) && buffer1[offset + 1] == '\\'
01819           && buffer1[offset + 2] == '.'){
01820                 DebugPrint("Root directory detected, its trailing dot will be removed.\n");
01821                 buffer1[offset + 2] = 0;
01822         }
01823
01824         /* append volume letter */
01825         header[4] = volume_letter;
01826         name_length = wcslen(header);
01827         if(offset < (name_length - 1)) goto path_is_too_long;
01828         offset -= (name_length - 1);
01829         (void)wcsncpy(buffer1 + offset,header,name_length);
01830
01831 update_filename:
01832         /* replace pfn->name contents with full path */
01833         //wcsncpy(pmfi->Name,buffer1 + offset,MAX_NTFS_PATH);
01834         //pmfi->Name[MAX_NTFS_PATH - 1] = 0;
01835         if(!RtlCreateUnicodeString(&us,buffer1 + offset)){
01836                 DebugPrint("Cannot allocate memory for BuildPath2()!\n");
01837                 out_of_memory_condition_counter ++;
01838         } else {
01839                 RtlFreeUnicodeString(&(pfn->name));
01840                 pfn->name.Buffer = us.Buffer;
01841                 pfn->name.Length = us.Length;
01842                 pfn->name.MaximumLength = us.MaximumLength;
01843         }
01844         pfn->PathBuilt = TRUE;
01845
01846         #ifdef DETAILED_LOGGING
01847         DebugPrint("FULL PATH = %ws\n",pfn->name.Buffer);
01848         #endif
01849 
01850 build_path_done:
01851         winx_heap_free(buffer1);
01852         winx_heap_free(buffer2);
01853         return;
01854
01855 path_is_too_long:
01856         DebugPrint("BuildPath2(): path is too long: %ws\n",buffer1);
01857         winx_heap_free(buffer1);
01858         winx_heap_free(buffer2);
01859 }
01860
01875 BOOLEAN GetFileNameAndParentMftId(ULONGLONG mft_id,ULONGLONG *parent_mft_id,WCHAR *buffer,ULONG length)
01876 {
01877         PFILENAME pfn;
01878         BOOLEAN FullPathRetrieved = FALSE;
01879
01880         /* initialize data */
01881         buffer[0] = 0;
01882         *parent_mft_id = FILE_root;
01883
01884         /* find an appropriate pfn structure */
01885         pfn = FindDirectoryByMftId(mft_id);
01886
01887         if(pfn == NULL){
01888                 DebugPrint("%I64u directory not found!\n",mft_id);
01889                 return FullPathRetrieved;
01890         }
01891
01892         /* update data */
01893         *parent_mft_id = pfn->ParentDirectoryMftId;
01894         (void)wcsncpy(buffer,pfn->name.Buffer,length);
01895         buffer[length-1] = 0;
01896         FullPathRetrieved = pfn->PathBuilt;
01897
01898         if(buffer[0] == 0){
01899                 DebugPrint("GetFileNameAndParentMftId: Invalid entry found: file has no name!\n");
01900                 DebugPrint("GetFileNameAndParentMftId: MFT ID = %I64u\n",mft_id);
01901         }
01902
01903         return FullPathRetrieved;
01904 }
01905
01911 void AddResidentDirectoryToFileList(PMY_FILE_INFORMATION pmfi)
01912 {
01913         PFILENAME pfn;
01914
01915         if(pmfi->Name[0] == 0){
01916                 DebugPrint("AddResidentDirectoryToFileList: Invalid entry found: file has no name!\n");
01917                 DebugPrint("AddResidentDirectoryToFileList: MFT ID = %I64u\n",pmfi->BaseMftId);
01918                 return;
01919         }
01920
01921         pfn = (PFILENAME)winx_list_insert_item((list_entry **)(void *)&filelist,NULL,sizeof(FILENAME));
01922         if(pfn == NULL){
01923                 DebugPrint("Cannot allocate %u bytes of memory for AddResidentDirectoryToFileList()!\n",sizeof(FILENAME));
01924                 out_of_memory_condition_counter ++;
01925                 return;
01926         }
01927
01928         /* fill a name member of the created structure */
01929         if(!RtlCreateUnicodeString(&pfn->name,pmfi->Name)){
01930                 DebugPrint("AddResidentDirectoryToFileList():\n");
01931                 DebugPrint("no enough memory for pfn->name initialization!\n");
01932                 out_of_memory_condition_counter ++;
01933                 winx_list_remove_item((list_entry **)(void *)&filelist,(list_entry *)pfn);
01934                 return;
01935         }
01936         pfn->blockmap = NULL; /* !!! */
01937         pfn->BaseMftId = pmfi->BaseMftId;
01938         pfn->ParentDirectoryMftId = pmfi->ParentDirectoryMftId;
01939         pfn->PathBuilt = FALSE; // what's mistake - it was TRUE here;
01940         pfn->n_fragments = 0;
01941         pfn->clusters_total = 0;
01942         pfn->is_fragm = FALSE;
01943         pfn->is_compressed = FALSE;
01944         pfn->is_dir = TRUE;
01945         pfn->is_reparse_point = pmfi->IsReparsePoint;
01946         pfn->is_overlimit = FALSE;
01947         pfn->is_filtered = TRUE;
01948         pfn->is_dirty = TRUE;
01949 }
01950
01957 PFILENAME FindDirectoryByMftId(ULONGLONG mft_id)
01958 {
01959         PFILENAME pfn;
01960         ULONG lim, i, k;
01961         signed long m;
01962         BOOLEAN ascending_order;
01963
01964         if(mf_allocated == FALSE){ /* use slow search */
01965                 for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){
01966                         if(pfn->BaseMftId == mft_id){
01967                                 if(wcsstr(pfn->name.Buffer,L":$") == NULL)
01968                                         return pfn;
01969                         }
01970                         if(pfn->next_ptr == filelist) break;
01971                 }
01972                 return NULL;
01973         } else { /* use fast binary search */
01974                 /* Based on bsearch() algorithm copyrighted by DJ Delorie (1994). */
01975                 ascending_order = (MftScanDirection == MFT_SCAN_RTL) ? TRUE : FALSE;
01976                 i = 0;
01977                 for(lim = n_entries; lim != 0; lim >>= 1){
01978                         k = i + (lim >> 1);
01979                         if(mf[k].mft_id == mft_id){
01980                                 /* search for proper entry in neighbourhood of found entry */
01981                                 for(m = k; m >= 0; m --){
01982                                         if(mf[m].mft_id != mft_id) break;
01983                                 }
01984                                 for(m = m + 1; (unsigned long)(m) < n_entries; m ++){
01985                                         if(mf[m].mft_id != mft_id) break;
01986                                         if(wcsstr(mf[m].pfn->name.Buffer,L":$") == NULL)
01987                                                 return mf[m].pfn;
01988                                 }
01989                                 DebugPrint("FindDirectoryByMftId - Exit 1\n");
01990                                 return NULL;
01991                         }
01992                         if(ascending_order){
01993                                 if(mft_id > mf[k].mft_id){
01994                                         i = k + 1; lim --; /* move right */
01995                                 } /* else move left */
01996                         } else {
01997                                 if(mft_id < mf[k].mft_id){
01998                                         i = k + 1; lim --; /* move right */
01999                                 } /* else move left */
02000                         }
02001                 }
02002                 DebugPrint("FindDirectoryByMftId - Exit 2\n");
02003                 return NULL;
02004         }
02005 }
02006
02019 NTSTATUS ReadSectors(ULONGLONG lsn,PVOID buffer,ULONG length)
02020 {
02021         IO_STATUS_BLOCK ioStatus;
02022         LARGE_INTEGER offset;
02023         NTSTATUS Status;
02024         ULONGLONG tm, time;
02025
02026         offset.QuadPart = lsn * bytes_per_sector;
02027         Status = NtReadFile(winx_fileno(fVolume),NULL,NULL,NULL,&ioStatus,buffer,length,&offset,NULL);
02028         if(NT_SUCCESS(Status)/* == STATUS_PENDING*/){
02029                 DebugPrint("ReadSectors waiting started...\n");
02030                 tm = winx_xtime();
02031                 Status = NtWaitForSingleObject(winx_fileno(fVolume),FALSE,NULL);
02032                 time = winx_xtime() - tm;
02033                 DebugPrint("ReadSectors waiting completed in %I64u ms.\n", time);
02034                 if(NT_SUCCESS(Status)) Status = ioStatus.Status;
02035         }
02036         /* FIXME: number of bytes actually read check? */
02037         return Status;
02038 }
02039

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多