![]() |
UltraDefrag Engine Architecture - Reference Manual - Guides |
|
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 00052 void CheckForNtfsPartition(void) 00053 { 00054 PARTITION_INFORMATION *part_info; 00055 NTFS_DATA *ntfs_data; 00056 NTSTATUS status; 00057 IO_STATUS_BLOCK iosb; 00058 00059 /* allocate memory */ 00060 part_info = winx_heap_alloc(sizeof(PARTITION_INFORMATION)); 00061 if(part_info == NULL){ 00062 DebugPrint("Cannot allocate memory for CheckForNtfsPartition()!\n"); 00063 out_of_memory_condition_counter ++; 00064 partition_type = FAT32_PARTITION; 00065 return; 00066 } 00067 ntfs_data = winx_heap_alloc(sizeof(NTFS_DATA)); 00068 if(ntfs_data == NULL){ 00069 winx_heap_free(part_info); 00070 DebugPrint("Cannot allocate memory for CheckForNtfsPartition()!\n"); 00071 out_of_memory_condition_counter ++; 00072 partition_type = FAT32_PARTITION; 00073 return; 00074 } 00075 00076 /* 00077 * Try to get fs type through IOCTL_DISK_GET_PARTITION_INFO request. 00078 * Works only on MBR-formatted disks. To retrieve information about 00079 * GPT-formatted disks use IOCTL_DISK_GET_PARTITION_INFO_EX. 00080 */ 00081 RtlZeroMemory(part_info,sizeof(PARTITION_INFORMATION)); 00082 status = NtDeviceIoControlFile(winx_fileno(fVolume),NULL,NULL,NULL,&iosb, \ 00083 IOCTL_DISK_GET_PARTITION_INFO,NULL,0, \ 00084 part_info, sizeof(PARTITION_INFORMATION)); 00085 if(NT_SUCCESS(status)/* == STATUS_PENDING*/){ 00086 (void)NtWaitForSingleObject(winx_fileno(fVolume),FALSE,NULL); 00087 status = iosb.Status; 00088 } 00089 if(NT_SUCCESS(status)){ 00090 DebugPrint("Partition type: %u\n",part_info->PartitionType); 00091 if(part_info->PartitionType == 0x7){ 00092 DebugPrint("NTFS found\n"); 00093 partition_type = NTFS_PARTITION; 00094 winx_heap_free(part_info); 00095 winx_heap_free(ntfs_data); 00096 return; 00097 } 00098 } else { 00099 DebugPrint("Can't get fs info: %x!\n",(UINT)status); 00100 } 00101 00102 /* 00103 * To ensure that we have a NTFS formatted partition 00104 * on GPT disks and when partition type is 0x27 00105 * FSCTL_GET_NTFS_VOLUME_DATA request can be used. 00106 */ 00107 RtlZeroMemory(ntfs_data,sizeof(NTFS_DATA)); 00108 status = NtFsControlFile(winx_fileno(fVolume),NULL,NULL,NULL,&iosb, \ 00109 FSCTL_GET_NTFS_VOLUME_DATA,NULL,0, \ 00110 ntfs_data, sizeof(NTFS_DATA)); 00111 if(NT_SUCCESS(status)/* == STATUS_PENDING*/){ 00112 (void)NtWaitForSingleObject(winx_fileno(fVolume),FALSE,NULL); 00113 status = iosb.Status; 00114 } 00115 if(NT_SUCCESS(status)){ 00116 DebugPrint("NTFS found\n"); 00117 partition_type = NTFS_PARTITION; 00118 winx_heap_free(part_info); 00119 winx_heap_free(ntfs_data); 00120 return; 00121 } else { 00122 DebugPrint("Can't get ntfs info: %x!\n",status); 00123 } 00124 00125 /* 00126 * Let's assume that we have a FAT32 formatted partition. 00127 * Currently we don't need more detailed information. 00128 */ 00129 partition_type = FAT32_PARTITION; 00130 DebugPrint("NTFS not found\n"); 00131 winx_heap_free(part_info); 00132 winx_heap_free(ntfs_data); 00133 } 00134 00139 NTSTATUS GetMftLayout(void) 00140 { 00141 IO_STATUS_BLOCK iosb; 00142 NTFS_DATA *ntfs_data; 00143 NTSTATUS status; 00144 ULONGLONG mft_len; 00145 00146 ntfs_data = winx_heap_alloc(sizeof(NTFS_DATA)); 00147 if(ntfs_data == NULL){ 00148 DebugPrint("Cannot allocate memory for GetMftLayout()!\n"); 00149 out_of_memory_condition_counter ++; 00150 return STATUS_NO_MEMORY; 00151 } 00152 00153 RtlZeroMemory(ntfs_data,sizeof(NTFS_DATA)); 00154 status = NtFsControlFile(winx_fileno(fVolume),NULL,NULL,NULL,&iosb, \ 00155 FSCTL_GET_NTFS_VOLUME_DATA,NULL,0, \ 00156 ntfs_data, sizeof(NTFS_DATA)); 00157 if(NT_SUCCESS(status)/* == STATUS_PENDING*/){ 00158 (void)NtWaitForSingleObject(winx_fileno(fVolume),FALSE,NULL); 00159 status = iosb.Status; 00160 } 00161 if(!NT_SUCCESS(status)){ 00162 DebugPrint("Cannot get ntfs info: %x!\n",status); 00163 winx_heap_free(ntfs_data); 00164 return status; 00165 } 00166 00167 mft_len = ProcessMftSpace(ntfs_data); 00168 00169 Stat.mft_size = mft_len * bytes_per_cluster; 00170 DebugPrint("MFT size = %I64u bytes\n",Stat.mft_size); 00171 if(mft_len == 0){ 00172 DebugPrint("MFT size is equal to zero!\n"); 00173 winx_heap_free(ntfs_data); 00174 return STATUS_UNSUCCESSFUL; 00175 } 00176 00177 ntfs_record_size = ntfs_data->BytesPerFileRecordSegment; 00178 DebugPrint("NTFS record size = %u bytes\n",ntfs_record_size); 00179 if(ntfs_record_size == 0){ 00180 DebugPrint("NTFS record size is equal to zero!\n"); 00181 winx_heap_free(ntfs_data); 00182 return STATUS_UNSUCCESSFUL; 00183 } 00184 00185 max_mft_entries = Stat.mft_size / ntfs_record_size; 00186 DebugPrint("MFT contains no more than %I64u records\n", 00187 max_mft_entries); 00188 00189 //DbgPrintFreeSpaceList(); 00190 winx_heap_free(ntfs_data); 00191 return STATUS_SUCCESS; 00192 } 00193 00199 BOOLEAN ScanMFT(void) 00200 { 00201 PMY_FILE_INFORMATION pmfi; 00202 PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob = NULL; 00203 ULONG nfrob_size; 00204 ULONGLONG mft_id, ret_mft_id; 00205 NTSTATUS status; 00206 00207 ULONGLONG tm, time, tm2, time2; 00208 00209 DebugPrint("MFT scan started!\n"); 00210 number_of_processed_attr_list_entries = 0; 00211 00212 /* Get information about MFT */ 00213 status = GetMftLayout(); 00214 if(!NT_SUCCESS(status)){ 00215 DebugPrint("ProcessMFT() failed!\n"); 00216 DebugPrint("MFT scan finished!\n"); 00217 return FALSE; 00218 } 00219 00220 /* allocate memory for NTFS record */ 00221 nfrob_size = sizeof(NTFS_FILE_RECORD_OUTPUT_BUFFER) + ntfs_record_size - 1; 00222 tm = _rdtsc(); 00223 pnfrob = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)winx_heap_alloc(nfrob_size); 00224 if(!pnfrob){ 00225 DebugPrint("Not enough memory for NTFS_FILE_RECORD_OUTPUT_BUFFER!\n"); 00226 out_of_memory_condition_counter ++; 00227 DebugPrint("MFT scan finished!\n"); 00228 return FALSE; 00229 } 00230 00231 /* allocate memory for MY_FILE_INFORMATION structure */ 00232 pmfi = (PMY_FILE_INFORMATION)winx_heap_alloc(sizeof(MY_FILE_INFORMATION)); 00233 if(!pmfi){ 00234 DebugPrint("Not enough memory for MY_FILE_INFORMATION structure!\n"); 00235 out_of_memory_condition_counter ++; 00236 winx_heap_free(pnfrob); 00237 DebugPrint("MFT scan finished!\n"); 00238 return FALSE; 00239 } 00240 00241 /* read all MFT records sequentially */ 00242 //MftBlockmap = NULL; 00243 MaxMftEntriesNumberUpdated = FALSE; 00244 UpdateMaxMftEntriesNumber(pnfrob,nfrob_size); 00245 mft_id = max_mft_entries - 1; 00246 DebugPrint("\n"); 00247 00248 if(MaxMftEntriesNumberUpdated == FALSE){ 00249 DebugPrint("UpdateMaxMftEntriesNumber() failed!\n"); 00250 DebugPrint("MFT scan finished!\n"); 00251 winx_heap_free(pnfrob); 00252 winx_heap_free(pmfi); 00253 //DestroyMftBlockmap(); 00254 return FALSE; 00255 } 00256 00257 DebugPrint("+-------------------------------------------------------+\n"); 00258 DebugPrint("| MFT records scanning loop begins... |\n"); 00259 DebugPrint("+-------------------------------------------------------+\n"); 00260 tm2 = _rdtsc(); 00261 /* Is MFT size an integral of NTFS record size? */ 00262 /*if(MftClusters == 0 || \ 00263 (MftClusters * (ULONGLONG)dx->bytes_per_cluster % (ULONGLONG)dx->ntfs_record_size) || \ 00264 (dx->ntfs_record_size % dx->bytes_per_sector)){*/ 00265 if(TRUE){ 00266 MftScanDirection = MFT_SCAN_RTL; 00267 while(1){ 00268 if(CheckForStopEvent()) break; 00269 status = GetMftRecord(pnfrob,nfrob_size,mft_id); 00270 if(!NT_SUCCESS(status)){ 00271 if(mft_id == 0){ 00272 DebugPrint("FSCTL_GET_NTFS_FILE_RECORD failed: %x!\n",status); 00273 winx_heap_free(pnfrob); 00274 winx_heap_free(pmfi); 00275 DebugPrint("MFT records scanning loop completed!\n"); 00276 DebugPrint("MFT scan finished!\n"); 00277 //DestroyMftBlockmap(); 00278 return FALSE; 00279 } 00280 /* it returns 0xc000000d (invalid parameter) for non existing records */ 00281 mft_id --; /* try to retrieve a previous record */ 00282 continue; 00283 } 00284 00285 /* analyse retrieved mft record */ 00286 ret_mft_id = GetMftIdFromFRN(pnfrob->FileReferenceNumber); 00287 #ifdef DETAILED_LOGGING 00288 DebugPrint("NTFS record found, id = %I64u\n",ret_mft_id); 00289 #endif 00290 AnalyseMftRecord(pnfrob,nfrob_size,pmfi); 00291 00292 /* go to the next record */ 00293 if(ret_mft_id == 0 || mft_id == 0) break; 00294 if(ret_mft_id > mft_id){ 00295 /* avoid infinite loops */ 00296 DebugPrint("Returned MFT record ID is above expected!\n"); 00297 mft_id --; 00298 } else { 00299 mft_id = ret_mft_id - 1; 00300 } 00301 } 00302 }/* else { 00303 DebugPrint("-Ultradfg- MFT size is an integral of NTFS record size :-D\n"); 00304 ScanMftDirectly(dx,pnfrob,nfrob_size,pmfi); 00305 }*/ 00306 00307 /* free allocated memory */ 00308 winx_heap_free(pnfrob); 00309 winx_heap_free(pmfi); 00310 //DestroyMftBlockmap(); 00311 time2 = _rdtsc() - tm2; 00312 DebugPrint("MFT records scanning loop completed in %I64u ms.\n",time2); 00313 00314 /* Build paths. */ 00315 BuildPaths(); 00316 00317 DebugPrint("%u attribute lists entries totally processed.\n", 00318 number_of_processed_attr_list_entries); 00319 time = _rdtsc() - tm; 00320 DebugPrint("MFT scan completed in %I64u ms.\n",time); 00321 return TRUE; 00322 } 00323 00330 void UpdateMaxMftEntriesNumber(PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob,ULONG nfrob_size) 00331 { 00332 NTSTATUS status; 00333 PFILE_RECORD_HEADER pfrh; 00334 00335 /* Get record for FILE_MFT using WinAPI, because MftBitmap is not ready yet. */ 00336 status = GetMftRecord(pnfrob,nfrob_size,FILE_MFT); 00337 if(!NT_SUCCESS(status)){ 00338 DebugPrint("UpdateMaxMftEntriesNumber(): FSCTL_GET_NTFS_FILE_RECORD failed: %x!\n",status); 00339 return; 00340 } 00341 if(GetMftIdFromFRN(pnfrob->FileReferenceNumber) != FILE_MFT){ 00342 DebugPrint("UpdateMaxMftEntriesNumber() failed - unable to get FILE_MFT record.\n"); 00343 return; 00344 } 00345 00346 /* Find DATA attribute. */ 00347 pfrh = (PFILE_RECORD_HEADER)pnfrob->FileRecordBuffer; 00348 if(!IsFileRecord(pfrh)){ 00349 DebugPrint("UpdateMaxMftEntriesNumber() failed - FILE_MFT record has invalid type %u.\n", 00350 pfrh->Ntfs.Type); 00351 return; 00352 } 00353 if(!(pfrh->Flags & 0x1)){ 00354 DebugPrint("UpdateMaxMftEntriesNumber() failed - FILE_MFT record marked as free.\n"); 00355 return; /* skip free records */ 00356 } 00357 00358 EnumerateAttributes(pfrh,UpdateMaxMftEntriesNumberCallback,NULL); 00359 } 00360 00363 void __stdcall UpdateMaxMftEntriesNumberCallback(PATTRIBUTE pattr,PMY_FILE_INFORMATION pmfi) 00364 { 00365 PNONRESIDENT_ATTRIBUTE pnr_attr; 00366 00367 /* here pmfi == NULL always */ 00368 (void)pmfi; 00369 00370 if(pattr->Nonresident && pattr->AttributeType == AttributeData){ 00371 pnr_attr = (PNONRESIDENT_ATTRIBUTE)pattr; 00372 if(ntfs_record_size){ 00373 max_mft_entries = pnr_attr->DataSize / ntfs_record_size; 00374 Stat.mft_size = pnr_attr->DataSize; 00375 //MftClusters = 0; 00376 //MftBlockmap = NULL; 00377 //ProcessMFTRunList(dx,pnr_attr); 00378 /* FIXME: check correctness of MftBlockmap */ 00379 MaxMftEntriesNumberUpdated = TRUE; 00380 } 00381 DebugPrint("MFT contains no more than %I64u records (more accurately)\n", 00382 max_mft_entries); 00383 } 00384 } 00385 00394 NTSTATUS GetMftRecord(PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob, 00395 ULONG nfrob_size,ULONGLONG mft_id) 00396 { 00397 NTFS_FILE_RECORD_INPUT_BUFFER nfrib; 00398 IO_STATUS_BLOCK iosb; 00399 NTSTATUS status; 00400 00401 nfrib.FileReferenceNumber = mft_id; 00402 00403 RtlZeroMemory(pnfrob,nfrob_size); /* required by x64 system, otherwise it trashes stack */ 00404 status = NtFsControlFile(winx_fileno(fVolume),NULL,NULL,NULL,&iosb, \ 00405 FSCTL_GET_NTFS_FILE_RECORD, \ 00406 &nfrib,sizeof(nfrib), \ 00407 pnfrob, nfrob_size); 00408 if(NT_SUCCESS(status)/* == STATUS_PENDING*/){ 00409 (void)NtWaitForSingleObject(winx_fileno(fVolume),FALSE,NULL); 00410 status = iosb.Status; 00411 } 00412 return status; 00413 } 00414 00423 int UpdateAttributeName(PFILENAME pfn,PMY_FILE_INFORMATION pmfi) 00424 { 00425 short *buffer; 00426 UNICODE_STRING us; 00427 00428 /* if file name is empty, we should do nothing */ 00429 if(pmfi->Name[0] == 0){ 00430 DebugPrint("MftRecord has empty filename, MftId = %I64u, Parent MftId = %I64u\n", 00431 pmfi->BaseMftId,pmfi->ParentDirectoryMftId); 00432 return (-1); 00433 } 00434 00435 /* if file name is not empty, append them */ 00436 buffer = winx_heap_alloc(MAX_NTFS_PATH * sizeof(short)); 00437 if(buffer == NULL){ 00438 DebugPrint("Cannot allocate memory for buffer in UpdateAttributeName()!\n"); 00439 out_of_memory_condition_counter ++; 00440 return (-1); 00441 } 00442 if(pfn->name.Buffer[0]) 00443 (void)_snwprintf(buffer,MAX_NTFS_PATH,L"%s:%s",pmfi->Name,pfn->name.Buffer); 00444 else 00445 (void)wcsncpy(buffer,pmfi->Name,MAX_NTFS_PATH); 00446 buffer[MAX_NTFS_PATH - 1] = 0; 00447 00448 if(!RtlCreateUnicodeString(&us,buffer)){ 00449 DebugPrint("UpdateAttributeName: Cannot allocate memory for new name!\n"); 00450 out_of_memory_condition_counter ++; 00451 winx_heap_free(buffer); 00452 return (-1); 00453 } 00454 00455 RtlFreeUnicodeString(&(pfn->name)); 00456 pfn->name.Buffer = us.Buffer; 00457 pfn->name.Length = us.Length; 00458 pfn->name.MaximumLength = us.MaximumLength; 00459 00460 /*DebugPrint("UpdateAttributeName: %ws, MftId = %I64u, Parent MftId = %I64u\n", 00461 pfn->name.Buffer,pmfi->BaseMftId,pmfi->ParentDirectoryMftId);*/ 00462 winx_heap_free(buffer); 00463 return 0; 00464 } 00465 00474 void AnalyseMftRecord(PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob, 00475 ULONG nfrob_size,PMY_FILE_INFORMATION pmfi) 00476 { 00477 PFILE_RECORD_HEADER pfrh; 00478 #ifdef DETAILED_LOGGING 00479 USHORT Flags; 00480 #endif 00481 PFILENAME pfn, next_pfn, head; 00482 BOOLEAN DirectoryAdded; 00483 00484 /* analyse record's header */ 00485 pfrh = (PFILE_RECORD_HEADER)pnfrob->FileRecordBuffer; 00486 00487 if(!IsFileRecord(pfrh)) return; 00488 if(!(pfrh->Flags & 0x1)) return; /* skip free records */ 00489 00490 /* analyse file record */ 00491 #ifdef DETAILED_LOGGING 00492 Flags = pfrh->Flags; 00493 if(Flags & 0x2) DebugPrint("Directory\n"); /* May be wrong? */ 00494 #endif 00495 00496 if(pfrh->BaseFileRecord){ /* skip child records, we will analyse them later */ 00497 //DebugPrint("-Ultradfg- BaseFileRecord (id) = %I64u\n", 00498 // GetMftIdFromFRN(pfrh->BaseFileRecord)); 00499 //DebugPrint("\n"); 00500 return; 00501 } 00502 00503 /* analyse attributes of MFT entry */ 00504 pmfi->BaseMftId = GetMftIdFromFRN(pnfrob->FileReferenceNumber); 00505 pmfi->ParentDirectoryMftId = FILE_root; 00506 pmfi->Flags = 0x0; 00507 00508 /* FIXME: L:\.:$SECURITY_DESCRIPTOR ?! */ 00509 pmfi->IsDirectory = (pfrh->Flags & 0x2) ? TRUE : FALSE; 00510 00511 pmfi->IsReparsePoint = FALSE; 00512 pmfi->NameType = 0x0; /* Assume FILENAME_POSIX */ 00513 memset(pmfi->Name,0,MAX_NTFS_PATH); 00514 00515 /* skip AttributeList attributes */ 00516 EnumerateAttributes(pfrh,AnalyseAttributeCallback,pmfi); 00517 00518 /* analyse AttributeList attributes */ 00519 EnumerateAttributes(pfrh,AnalyseAttributeListCallback,pmfi); 00520 00521 /* append the filename to attributes */ 00522 head = filelist; 00523 for(pfn = filelist; pfn != NULL;){ 00524 if(MftScanDirection == MFT_SCAN_RTL){ 00525 if(pfn->BaseMftId > pmfi->BaseMftId) break; /* we have no chance to find record in list */ 00526 } else { 00527 if(pfn->BaseMftId < pmfi->BaseMftId) break; 00528 } 00529 next_pfn = pfn->next_ptr; 00530 if(pfn->ParentDirectoryMftId == pmfi->ParentDirectoryMftId && \ 00531 pfn->BaseMftId == pmfi->BaseMftId){ 00532 if(UpdateAttributeName(pfn,pmfi) < 0){ 00533 winx_list_remove_item((list_entry **)(void *)&filelist,(list_entry *)pfn); 00534 if(filelist == NULL) break; 00535 if(filelist != head){ 00536 head = filelist; 00537 pfn = next_pfn; 00538 continue; 00539 } 00540 } 00541 } 00542 pfn = next_pfn; 00543 if(pfn == head) break; 00544 } 00545 00546 /* just for debugging */ 00547 /*if(wcsistr(pmfi->Name,L"Scratch")) 00548 DebugPrint("@@@@@ %ws DIRECTORY FOUND, MFT_ID = %I64u, PARENT ID = %I64u\n", 00549 pmfi->Name,pmfi->BaseMftId,pmfi->ParentDirectoryMftId);*/ 00550 00551 /* add resident directories to filelist - required by BuildPaths() */ 00552 if(pmfi->IsDirectory){ 00553 /* is directory already added? */ 00554 DirectoryAdded = FALSE; 00555 for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){ 00556 /* 00557 * we scan mft from the end to the beginning (RTL) 00558 * and we add new records to the left side of the file list... 00559 */ 00560 if(MftScanDirection == MFT_SCAN_RTL){ 00561 if(pfn->BaseMftId > pmfi->BaseMftId) break; /* we have no chance to find record in list */ 00562 } else { 00563 if(pfn->BaseMftId < pmfi->BaseMftId) break; 00564 } 00565 if(!wcscmp(pfn->name.Buffer,pmfi->Name) && \ 00566 (pfn->ParentDirectoryMftId == pmfi->ParentDirectoryMftId) && \ 00567 (pfn->BaseMftId == pmfi->BaseMftId)) 00568 DirectoryAdded = TRUE; 00569 if(pfn->next_ptr == filelist) break; 00570 } 00571 if(!DirectoryAdded) AddResidentDirectoryToFileList(pmfi); 00572 } 00573 00574 /* update cluster map and statistics */ 00575 UpdateClusterMapAndStatistics(pmfi); 00576 00577 #ifdef DETAILED_LOGGING 00578 DebugPrint("\n"); 00579 #endif 00580 } 00581 00592 void EnumerateAttributes(PFILE_RECORD_HEADER pfrh, 00593 ATTRHANDLER_PROC ahp,PMY_FILE_INFORMATION pmfi) 00594 { 00595 PATTRIBUTE pattr; 00596 ULONG attr_length; 00597 USHORT attr_offset; 00598 00599 attr_offset = pfrh->AttributeOffset; 00600 pattr = (PATTRIBUTE)((char *)pfrh + attr_offset); 00601 00602 while(pattr){ 00603 if(CheckForStopEvent()) break; 00604 00605 /* is an attribute header inside a record bounds? */ 00606 if(attr_offset + sizeof(ATTRIBUTE) > pfrh->BytesInUse || \ 00607 attr_offset + sizeof(ATTRIBUTE) > ntfs_record_size) break; 00608 00609 /* is it a valid attribute */ 00610 if(pattr->AttributeType == 0xffffffff) break; 00611 if(pattr->AttributeType == 0x0) break; 00612 if(pattr->Length == 0) break; 00613 00614 /* is an attribute inside a record bounds? */ 00615 if(attr_offset + pattr->Length > pfrh->BytesInUse || \ 00616 attr_offset + pattr->Length > ntfs_record_size) break; 00617 00618 /* is an attribute length valid? */ 00619 if(pattr->Nonresident){ 00620 if(pattr->Length < (sizeof(NONRESIDENT_ATTRIBUTE) - sizeof(ULONGLONG))){ 00621 DebugPrint("Nonresident attribute length is invalid!\n"); 00622 break; 00623 } 00624 } else { 00625 if(pattr->Length < sizeof(RESIDENT_ATTRIBUTE)){ 00626 DebugPrint("Resident attribute length is invalid!\n"); 00627 break; 00628 } 00629 } 00630 00631 /* call specified callback procedure */ 00632 ahp(pattr,pmfi); 00633 00634 /* go to the next attribute */ 00635 attr_length = pattr->Length; 00636 attr_offset += (USHORT)(attr_length); 00637 pattr = (PATTRIBUTE)((char *)pattr + attr_length); 00638 } 00639 } 00640 00643 void __stdcall AnalyseAttributeCallback(PATTRIBUTE pattr,PMY_FILE_INFORMATION pmfi) 00644 { 00645 if(pattr->AttributeType != AttributeAttributeList) 00646 AnalyseAttribute(pattr,pmfi); 00647 } 00648 00651 void __stdcall AnalyseAttributeListCallback(PATTRIBUTE pattr,PMY_FILE_INFORMATION pmfi) 00652 { 00653 if(pattr->AttributeType == AttributeAttributeList) 00654 AnalyseAttribute(pattr,pmfi); 00655 } 00656 00660 void AnalyseAttribute(PATTRIBUTE pattr,PMY_FILE_INFORMATION pmfi) 00661 { 00662 if(pattr->Nonresident) AnalyseNonResidentAttribute((PNONRESIDENT_ATTRIBUTE)pattr,pmfi); 00663 else AnalyseResidentAttribute((PRESIDENT_ATTRIBUTE)pattr,pmfi); 00664 } 00665 00677 void AnalyseResidentAttribute(PRESIDENT_ATTRIBUTE pr_attr,PMY_FILE_INFORMATION pmfi) 00678 { 00679 if(pr_attr->ValueOffset == 0 || pr_attr->ValueLength == 0){ 00680 /*DebugPrint("AnalyseResidentAttribute: Invalid attribute, " 00681 "MFT ID = %I64u, Attribute Type = 0x%x, ValueOffset = %u, ValueLength = %u\n", 00682 pmfi->BaseMftId,(UINT)pr_attr->Attribute.AttributeType, 00683 (UINT)pr_attr->ValueOffset,(UINT)pr_attr->ValueLength); 00684 */ 00685 /* 00686 * This is an ordinary case when some attribute data was truncated. 00687 * For example, when some large file becomes empty its $DATA attribute 00688 * becomes resident and its ValueLength becomes to be equal to zero. 00689 */ 00690 return; 00691 } 00692 00693 switch(pr_attr->Attribute.AttributeType){ 00694 case AttributeStandardInformation: /* always resident */ 00695 GetFileFlags(pr_attr,pmfi); break; 00696 00697 case AttributeFileName: /* always resident */ 00698 GetFileName(pr_attr,pmfi); break; 00699 00700 case AttributeVolumeInformation: /* always resident */ 00701 GetVolumeInformationData(pr_attr); break; 00702 00703 case AttributeAttributeList: 00704 //DebugPrint("Resident AttributeList found!\n"); 00705 AnalyseResidentAttributeList(pr_attr,pmfi); 00706 break; 00707 00708 /*case AttributeIndexRoot: // always resident */ 00709 /*case AttributeIndexAllocation:*/ 00710 00711 case AttributeReparsePoint: 00712 CheckReparsePointResident(pr_attr,pmfi); 00713 break; 00714 00715 default: 00716 break; 00717 } 00718 } 00719 00727 void GetFileFlags(PRESIDENT_ATTRIBUTE pr_attr,PMY_FILE_INFORMATION pmfi) 00728 { 00729 PSTANDARD_INFORMATION psi; 00730 ULONG Flags; 00731 00732 psi = (PSTANDARD_INFORMATION)((char *)pr_attr + pr_attr->ValueOffset); 00733 if(pr_attr->ValueLength < 48){ /* 48 = size of the shortest STANDARD_INFORMATION structure */ 00734 DebugPrint("STANDARD_INFORMATION attribute is too short!\n"); 00735 } else { 00736 Flags = psi->FileAttributes; 00737 pmfi->Flags = Flags; 00738 } 00739 } 00740 00749 void GetFileName(PRESIDENT_ATTRIBUTE pr_attr,PMY_FILE_INFORMATION pmfi) 00750 { 00751 PFILENAME_ATTRIBUTE pfn_attr; 00752 short *name; 00753 UCHAR name_type; 00754 ULONGLONG parent_mft_id; 00755 00756 pfn_attr = (PFILENAME_ATTRIBUTE)((char *)pr_attr + pr_attr->ValueOffset); 00757 if(pr_attr->ValueLength < sizeof(FILENAME_ATTRIBUTE)){ 00758 DebugPrint("FILENAME_ATTRIBUTE is too short!\n"); 00759 return; 00760 } 00761 00762 parent_mft_id = GetMftIdFromFRN(pfn_attr->DirectoryFileReferenceNumber); 00763 00764 if(pfn_attr->NameLength){ 00765 name = (short *)winx_heap_alloc((pfn_attr->NameLength + 1) * sizeof(short)); 00766 if(!name){ 00767 DebugPrint("Cannot allocate memory for GetFileName()!\n"); 00768 out_of_memory_condition_counter ++; 00769 return; 00770 } 00771 (void)wcsncpy(name,pfn_attr->Name,pfn_attr->NameLength); 00772 name[pfn_attr->NameLength] = 0; 00773 //DbgPrint("-Ultradfg- Filename = %ws, parent id = %I64u\n",name,parent_mft_id); 00774 if(name[0] == 0) DebugPrint("Empty filename found ;)\n"); 00775 if(parent_mft_id == pmfi->BaseMftId && pmfi->BaseMftId != FILE_root) 00776 DebugPrint("Recursion found - file identifies themselves as a parent ;)\n"); 00777 if(name[0] && (parent_mft_id != pmfi->BaseMftId || pmfi->BaseMftId == FILE_root)){ 00778 /* update pmfi members */ 00779 pmfi->ParentDirectoryMftId = parent_mft_id; 00780 /* save filename */ 00781 name_type = pfn_attr->NameType; 00782 UpdateFileName(pmfi,name,name_type); 00783 } 00784 winx_heap_free(name); 00785 } else DebugPrint("GetFileName: Empty name found, MFT ID = %I64u\n",pmfi->BaseMftId); 00786 } 00787 00797 void UpdateFileName(PMY_FILE_INFORMATION pmfi,WCHAR *name,UCHAR name_type) 00798 { 00799 /* compare name type with type of saved name */ 00800 if(pmfi->Name[0] == 0 || pmfi->NameType == FILENAME_DOS || \ 00801 ((pmfi->NameType & FILENAME_WIN32) && (name_type == FILENAME_POSIX))){ 00802 (void)wcsncpy(pmfi->Name,name,MAX_NTFS_PATH); 00803 pmfi->Name[MAX_NTFS_PATH-1] = 0; 00804 pmfi->NameType = name_type; 00805 } 00806 } 00807 00813 void GetVolumeInformationData(PRESIDENT_ATTRIBUTE pr_attr) 00814 { 00815 PVOLUME_INFORMATION pvi; 00816 ULONG mj_ver, mn_ver; 00817 BOOLEAN dirty_flag = FALSE; 00818 00819 pvi = (PVOLUME_INFORMATION)((char *)pr_attr + pr_attr->ValueOffset); 00820 if(pr_attr->ValueLength < sizeof(VOLUME_INFORMATION)){ 00821 DebugPrint("VOLUME_INFORMATION is too short!\n"); 00822 return; 00823 } 00824 00825 mj_ver = (ULONG)pvi->MajorVersion; 00826 mn_ver = (ULONG)pvi->MinorVersion; 00827 if(pvi->Flags & 0x1) dirty_flag = TRUE; 00828 DebugPrint("NTFS Version %u.%u\n",mj_ver,mn_ver); 00829 if(dirty_flag) DebugPrint("Volume is dirty!\n"); 00830 } 00831 00839 void CheckReparsePointResident(PRESIDENT_ATTRIBUTE pr_attr,PMY_FILE_INFORMATION pmfi) 00840 { 00841 PREPARSE_POINT prp; 00842 ULONG tag; 00843 00844 prp = (PREPARSE_POINT)((char *)pr_attr + pr_attr->ValueOffset); 00845 if(pr_attr->ValueLength >= sizeof(ULONG)){ 00846 tag = prp->ReparseTag; 00847 DebugPrint("Reparse tag = 0x%x\n",tag); 00848 } else { 00849 DebugPrint("REPARSE_POINT is too short!\n"); 00850 } 00851 00852 pmfi->IsReparsePoint = TRUE; 00853 } 00854 00864 void AnalyseResidentAttributeList(PRESIDENT_ATTRIBUTE pr_attr,PMY_FILE_INFORMATION pmfi) 00865 { 00866 PATTRIBUTE_LIST attr_list_entry; 00867 USHORT length; 00868 00869 attr_list_entry = (PATTRIBUTE_LIST)((char *)pr_attr + pr_attr->ValueOffset); 00870 00871 while(TRUE){ 00872 if( ((char *)attr_list_entry + sizeof(ATTRIBUTE_LIST) - sizeof(attr_list_entry->AlignmentOrReserved)) > 00873 ((char *)pr_attr + pr_attr->ValueOffset + pr_attr->ValueLength) ) break; 00874 if(CheckForStopEvent()) break; 00875 /* is it a valid attribute */ 00876 if(attr_list_entry->AttributeType == 0xffffffff) break; 00877 if(attr_list_entry->AttributeType == 0x0) break; 00878 if(attr_list_entry->Length == 0) break; 00879 //DebugPrint("@@@@@@@@@ FUCKED Length = %u\n", attr_list_entry->Length); 00880 AnalyseAttributeFromAttributeList(attr_list_entry,pmfi); 00881 /* go to the next attribute list entry */ 00882 length = attr_list_entry->Length; 00883 attr_list_entry = (PATTRIBUTE_LIST)((char *)attr_list_entry + length); 00884 } 00885 } 00886 00893 void AnalyseAttributeFromAttributeList(PATTRIBUTE_LIST attr_list_entry,PMY_FILE_INFORMATION pmfi) 00894 { 00895 ULONGLONG child_record_mft_id; 00896 ATTRIBUTE_TYPE attr_type; 00897 USHORT attr_number; 00898 UCHAR name_length = 0; 00899 short *attr_name = NULL; 00900 00901 /* 00902 * 21 Feb 2010 00903 * The right implementation must analyze a single 00904 * specific attribute from the MFT record pointed 00905 * by the attribute list entry. 00906 */ 00907 00908 /* 1. save the name of the attribute */ 00909 name_length = attr_list_entry->NameLength; 00910 if(attr_list_entry->NameOffset && name_length){ 00911 attr_name = winx_heap_alloc((name_length + 1) * sizeof(short)); 00912 if(attr_name == NULL){ 00913 DebugPrint("Cannot allocate %u bytes of memory for AnalyseAttributeFromAttributeList()!\n", 00914 (name_length + 1) * sizeof(short)); 00915 out_of_memory_condition_counter ++; 00916 return; 00917 } 00918 memcpy(attr_name, 00919 (char *)attr_list_entry + attr_list_entry->NameOffset, 00920 name_length * sizeof(short)); 00921 attr_name[name_length] = 0; 00922 if(attr_name[0] == 0){ 00923 winx_heap_free(attr_name); 00924 attr_name = NULL; 00925 } 00926 } 00927 /* attr_name is NULL here for empty names */ 00928 00929 /* 2. save the attribute type */ 00930 attr_type = attr_list_entry->AttributeType; 00931 00932 /* 3. save the identifier of the child record containing the attribute */ 00933 child_record_mft_id = GetMftIdFromFRN(attr_list_entry->FileReferenceNumber); 00934 00935 /* 4. save the AttributeNumber */ 00936 attr_number = attr_list_entry->AttributeNumber; 00937 00938 /* 5. analyze a single attribute */ 00939 AnalyseAttributeFromMftRecord(child_record_mft_id,attr_type,attr_name,attr_number,pmfi); 00940 00941 /* 6. free resources */ 00942 if(attr_name) winx_heap_free(attr_name); 00943 } 00944 00956 void AnalyseAttributeFromMftRecord(ULONGLONG mft_id,ATTRIBUTE_TYPE attr_type, 00957 short *attr_name,USHORT attr_number,PMY_FILE_INFORMATION pmfi) 00958 { 00959 PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob = NULL; 00960 ULONG nfrob_size; 00961 NTSTATUS status; 00962 PFILE_RECORD_HEADER pfrh; 00963 00964 /* 00965 * Skip attributes stored in the base mft record, 00966 * because they will be scanned anyway. 00967 */ 00968 if(mft_id == pmfi->BaseMftId){ 00969 //DebugPrint("Attribute list entry points to 0x%x attribute of the base record.\n",(UINT)attr_type); 00970 return; 00971 } 00972 00973 /* allocate memory for a single mft record */ 00974 nfrob_size = sizeof(NTFS_FILE_RECORD_OUTPUT_BUFFER) + ntfs_record_size - 1; 00975 pnfrob = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)winx_heap_alloc(nfrob_size); 00976 if(!pnfrob){ 00977 DebugPrint("AnalyseAttributeFromMftRecord():\n"); 00978 DebugPrint("Not enough memory for NTFS_FILE_RECORD_OUTPUT_BUFFER!\n"); 00979 out_of_memory_condition_counter ++; 00980 return; 00981 } 00982 00983 /* get specified mft record */ 00984 status = GetMftRecord(pnfrob,nfrob_size,mft_id); 00985 if(!NT_SUCCESS(status)){ 00986 DebugPrint("AnalyseAttributeFromMftRecord(): FSCTL_GET_NTFS_FILE_RECORD failed: %x!\n",status); 00987 winx_heap_free(pnfrob); 00988 return; 00989 } 00990 if(GetMftIdFromFRN(pnfrob->FileReferenceNumber) != mft_id){ 00991 DebugPrint("AnalyseAttributeFromAttributeList() failed - unable to get %I64u record.\n",mft_id); 00992 winx_heap_free(pnfrob); 00993 return; 00994 } 00995 00996 /* validate the record header */ 00997 pfrh = (PFILE_RECORD_HEADER)pnfrob->FileRecordBuffer; 00998 if(!IsFileRecord(pfrh)){ 00999 DebugPrint("AnalyseAttributeFromMftRecord() failed - %I64u record has invalid type %u.\n", 01000 mft_id,pfrh->Ntfs.Type); 01001 winx_heap_free(pnfrob); 01002 return; 01003 } 01004 if(!(pfrh->Flags & 0x1)){ 01005 DebugPrint("AnalyseAttributeFromMftRecord() failed\n"); 01006 DebugPrint("%I64u record marked as free.\n",mft_id); 01007 winx_heap_free(pnfrob); 01008 return; /* skip free records */ 01009 } 01010 01011 if(pfrh->BaseFileRecord == 0){ 01012 DebugPrint("AnalyseAttributeFromMftRecord() failed - %I64u is not a child record.\n",mft_id); 01013 winx_heap_free(pnfrob); 01014 return; 01015 } 01016 01017 /* search for a specified attribute */ 01018 AnalyseSingleAttribute(mft_id,pfrh,attr_type,attr_name,attr_number,pmfi); 01019 01020 /* free allocated memory */ 01021 winx_heap_free(pnfrob); 01022 } 01023 01026 void AnalyseSingleAttribute(ULONGLONG mft_id,PFILE_RECORD_HEADER pfrh, 01027 ATTRIBUTE_TYPE attr_type,short *attr_name,USHORT attr_number,PMY_FILE_INFORMATION pmfi) 01028 { 01029 int name_length; 01030 PATTRIBUTE pattr; 01031 ULONG attr_length; 01032 USHORT attr_offset; 01033 short *name = NULL; 01034 BOOLEAN attribute_found = FALSE; 01035 char *resident_status = ""; 01036 01037 attr_offset = pfrh->AttributeOffset; 01038 pattr = (PATTRIBUTE)((char *)pfrh + attr_offset); 01039 01040 while(pattr){ 01041 if(CheckForStopEvent()) break; 01042 01043 /* is an attribute header inside a record bounds? */ 01044 if(attr_offset + sizeof(ATTRIBUTE) > pfrh->BytesInUse || \ 01045 attr_offset + sizeof(ATTRIBUTE) > ntfs_record_size) break; 01046 01047 /* is it a valid attribute */ 01048 if(pattr->AttributeType == 0xffffffff) break; 01049 if(pattr->AttributeType == 0x0) break; 01050 if(pattr->Length == 0) break; 01051 01052 /* is an attribute inside a record bounds? */ 01053 if(attr_offset + pattr->Length > pfrh->BytesInUse || \ 01054 attr_offset + pattr->Length > ntfs_record_size) break; 01055 01056 /* is an attribute length valid? */ 01057 if(pattr->Nonresident){ 01058 if(pattr->Length < (sizeof(NONRESIDENT_ATTRIBUTE) - sizeof(ULONGLONG))){ 01059 DebugPrint("Nonresident attribute length is invalid!\n"); 01060 break; 01061 } 01062 } else { 01063 if(pattr->Length < sizeof(RESIDENT_ATTRIBUTE)){ 01064 DebugPrint("Resident attribute length is invalid!\n"); 01065 break; 01066 } 01067 } 01068 01069 /* do we have found the specified attribute? */ 01070 if(pattr->AttributeType == attr_type){ 01071 if(pattr->NameOffset && pattr->NameLength){ 01072 name = (short *)((char *)pattr + pattr->NameOffset); 01073 if(name[0] == 0) name = NULL; 01074 } 01075 if(attr_name == NULL){ 01076 if(name == NULL && pattr->AttributeNumber == attr_number) 01077 attribute_found = TRUE; 01078 } else { 01079 if(name != NULL){ 01080 name_length = wcslen(attr_name); 01081 if(name_length == pattr->NameLength){ 01082 if(memcmp((void *)attr_name,(void *)name,name_length * sizeof(short)) == 0){ 01083 if(pattr->AttributeNumber == attr_number) 01084 attribute_found = TRUE; 01085 } 01086 } 01087 } 01088 } 01089 } 01090 01091 if(attribute_found){ 01092 if(pattr->Nonresident) resident_status = "Nonresident"; 01093 else resident_status = "Resident"; 01094 //DebugPrint("An attribute pointed by the attribute list entry found...\n"); 01095 DebugPrint("AttrListEntry: Base MftId = %I64u, MftId = %I64u, Attribute Type = 0x%x, Attribute Number = %u, %s\n", 01096 pmfi->BaseMftId,mft_id,(UINT)attr_type,(UINT)attr_number,resident_status); 01097 if(pattr->Nonresident) AnalyseNonResidentAttribute((PNONRESIDENT_ATTRIBUTE)pattr,pmfi); 01098 else AnalyseResidentAttribute((PRESIDENT_ATTRIBUTE)pattr,pmfi); 01099 number_of_processed_attr_list_entries ++; 01100 return; 01101 } 01102 01103 /* go to the next attribute */ 01104 attr_length = pattr->Length; 01105 attr_offset += (USHORT)(attr_length); 01106 pattr = (PATTRIBUTE)((char *)pattr + attr_length); 01107 } 01108 } 01109 01113 ATTRIBUTE_NAME default_attribute_names[] = { 01114 {AttributeAttributeList, L"$ATTRIBUTE_LIST" }, 01115 {AttributeEA, L"$EA" }, 01116 {AttributeEAInformation, L"$EA_INFORMATION" }, 01117 {AttributeSecurityDescriptor, L"$SECURITY_DESCRIPTOR" }, 01118 {AttributeData, L"$DATA" }, 01119 {AttributeIndexRoot, L"$INDEX_ROOT" }, 01120 {AttributeIndexAllocation, L"$INDEX_ALLOCATION" }, 01121 {AttributeBitmap, L"$BITMAP" }, 01122 {AttributeReparsePoint, L"$REPARSE_POINT" }, 01123 {AttributeLoggedUtulityStream, L"$LOGGED_UTILITY_STREAM"}, /* used by EFS */ 01124 {0, NULL } 01125 }; 01126 01133 short *GetDefaultAttributeName(ATTRIBUTE_TYPE attr_type) 01134 { 01135 int i; 01136 01137 for(i = 0;; i++){ 01138 if(default_attribute_names[i].AttributeName == NULL) break; 01139 if(default_attribute_names[i].AttributeType == attr_type) break; 01140 } 01141 return default_attribute_names[i].AttributeName; 01142 } 01143 01155 void AnalyseNonResidentAttribute(PNONRESIDENT_ATTRIBUTE pnr_attr,PMY_FILE_INFORMATION pmfi) 01156 { 01157 ATTRIBUTE_TYPE attr_type; 01158 WCHAR *default_attr_name = NULL; 01159 short *attr_name; 01160 BOOLEAN NonResidentAttrListFound = FALSE; 01161 01162 /* get default name of the attribute */ 01163 attr_type = pnr_attr->Attribute.AttributeType; 01164 default_attr_name = GetDefaultAttributeName(attr_type); 01165 01166 /* skip attributes of unknown type */ 01167 if(default_attr_name == NULL){ 01168 DebugPrint("Nonresident attribute of unknown type 0x%x found!\n",(UINT)attr_type); 01169 return; 01170 } 01171 01172 /* allocate memory */ 01173 attr_name = (short *)winx_heap_alloc((MAX_NTFS_PATH + 1) * sizeof(short)); 01174 if(!attr_name){ 01175 DebugPrint("Cannot allocate memory for attr_name in AnalyseNonResidentAttribute()!\n"); 01176 out_of_memory_condition_counter ++; 01177 return; 01178 } 01179 01180 /* additional checks */ 01181 if(attr_type == AttributeAttributeList){ 01182 DebugPrint("Nonresident AttributeList found!\n"); 01183 NonResidentAttrListFound = TRUE; 01184 } 01185 01186 if(attr_type == AttributeReparsePoint) 01187 pmfi->IsReparsePoint = TRUE; 01188 01189 /* ------------------------------------------------------------------------- */ 01190 /* get the attribute name */ 01191 /* ------------------------------------------------------------------------- */ 01192 01193 attr_name[0] = 0; 01194 if(pnr_attr->Attribute.NameLength){ 01195 /* NameLength is always less than MAX_PATH! */ 01196 (void)wcsncpy(attr_name,(short *)((char *)pnr_attr + pnr_attr->Attribute.NameOffset), 01197 pnr_attr->Attribute.NameLength); 01198 attr_name[pnr_attr->Attribute.NameLength] = 0; 01199 } 01200 01201 if(attr_name[0] == 0){ 01202 (void)wcsncpy(attr_name,default_attr_name,MAX_NTFS_PATH); 01203 attr_name[MAX_NTFS_PATH - 1] = 0; 01204 } 01205 01206 /* never append $DATA attribute name */ 01207 if(wcscmp(attr_name,L"$DATA") == 0) attr_name[0] = 0; 01208 01209 /* do not append $I30 attribute name - required by GetFileNameAndParentMftId() */ 01210 if(wcscmp(attr_name,L"$I30") == 0) attr_name[0] = 0; 01211 if(wcscmp(attr_name,L"$INDEX_ALLOCATION") == 0) attr_name[0] = 0; 01212 01213 /* ------------------------------------------------------------------------- */ 01214 /* now we have the name of the attribute */ 01215 /* ------------------------------------------------------------------------- */ 01216 01217 /* just for debugging */ 01218 /*if(wcsistr(pmfi->Name,L"Scratch")){ 01219 DebugPrint("@@@@ %ws DIRECTORY FOUND, MFT_ID = %I64u, PARENT ID = %I64u\n", 01220 pmfi->Name,pmfi->BaseMftId,pmfi->ParentDirectoryMftId); 01221 DebugPrint("@@@@ FULL ATTRIBUTE NAME = %ws:%ws, DEFAULT ATTR NAME = %ws\n", 01222 pmfi->Name,attr_name,default_attr_name); 01223 }*/ 01224 01225 if(NonResidentAttrListFound) DebugPrint("%ws:%ws\n",pmfi->Name,attr_name); 01226 01227 /* skip $BadClus file which may have wrong number of clusters */ 01228 if(/*wcsistr(attr_name,L"$BadClus") || */wcsistr(pmfi->Name,L"$BadClus")){ 01229 /* on my system this file always exists, even after chkdsk execution */ 01230 /*DebugPrint("WARNING: %ws:%ws file found! Run CheckDisk program!\n",pmfi->Name,attr_name);*/ 01231 } else { 01232 ProcessRunList(attr_name,pnr_attr,pmfi,NonResidentAttrListFound); 01233 } 01234 01235 /* free allocated memory */ 01236 winx_heap_free(attr_name); 01237 } 01238 01241 static ULONG RunLength(PUCHAR run) 01242 { 01243 return (*run & 0xf) + ((*run >> 4) & 0xf) + 1; 01244 } 01245 01248 static LONGLONG RunLCN(PUCHAR run) 01249 { 01250 LONG i; 01251 UCHAR n1 = *run & 0xf; 01252 UCHAR n2 = (*run >> 4) & 0xf; 01253 LONGLONG lcn = (n2 == 0) ? 0 : (LONGLONG)(((signed char *)run)[n1 + n2]); 01254 01255 for(i = n1 + n2 - 1; i > n1; i--) 01256 lcn = (lcn << 8) + run[i]; 01257 return lcn; 01258 } 01259 01262 static ULONGLONG RunCount(PUCHAR run) 01263 { 01264 ULONG i; 01265 UCHAR n = *run & 0xf; 01266 ULONGLONG count = 0; 01267 01268 for(i = n; i > 0; i--) 01269 count = (count << 8) + run[i]; 01270 return count; 01271 } 01272 01283 void ProcessRunList(WCHAR *full_path, 01284 PNONRESIDENT_ATTRIBUTE pnr_attr,PMY_FILE_INFORMATION pmfi, 01285 BOOLEAN is_attr_list) 01286 { 01287 PUCHAR run; 01288 ULONGLONG lcn, vcn, length; 01289 PFILENAME pfn; 01290 BOOLEAN is_compressed = (pnr_attr->Attribute.Flags & 0x1) ? TRUE : FALSE; 01291 01292 /* if(pnr_attr->Attribute.Flags & 0x1) 01293 DbgPrint("[CMP] %ws VCN %I64u - %I64u\n",full_path,pnr_attr->LowVcn,pnr_attr->HighVcn); 01294 else 01295 DbgPrint("[ORD] %ws VCN %I64u - %I64u\n",full_path,pnr_attr->LowVcn,pnr_attr->HighVcn); 01296 */ 01297 /* find corresponding FILENAME structure in dx->filelist or add a new one */ 01298 pfn = FindFileListEntryForTheAttribute(full_path,pmfi); 01299 if(pfn == NULL) return; 01300 01301 if(is_compressed) pfn->is_compressed = TRUE; 01302 01303 /* loop through runs */ 01304 lcn = 0; vcn = pnr_attr->LowVcn; 01305 run = (PUCHAR)((char *)pnr_attr + pnr_attr->RunArrayOffset); 01306 while(*run){ 01307 lcn += RunLCN(run); 01308 length = RunCount(run); 01309 01310 /* skip virtual runs */ 01311 if(RunLCN(run)){ 01312 /* check for data consistency */ 01313 if(!CheckBlock(lcn,length)){ 01314 DebugPrint("Error in MFT found, run Check Disk program!\n"); 01315 break; 01316 } 01317 ProcessRun(full_path,pmfi,pfn,vcn,length,lcn); 01318 } 01319 01320 /* go to the next run */ 01321 run += RunLength(run); 01322 vcn += length; 01323 } 01324 01325 /* analyze nonresident attribute lists */ 01326 if(is_attr_list) AnalyseNonResidentAttributeList(pfn,pmfi,pnr_attr->InitializedSize); 01327 } 01328 01337 void AnalyseNonResidentAttributeList(PFILENAME pfn,PMY_FILE_INFORMATION pmfi,ULONGLONG size) 01338 { 01339 ULONGLONG clusters_to_read; 01340 char *cluster; 01341 char *current_cluster; 01342 PBLOCKMAP block; 01343 ULONGLONG lsn; 01344 NTSTATUS status; 01345 PATTRIBUTE_LIST attr_list_entry; 01346 int i; 01347 USHORT length; 01348 01349 DebugPrint("Allocated size = %I64u bytes.\n",size); 01350 if(size == 0){ 01351 DebugPrint("Empty nonresident attribute list found.\n"); 01352 return; 01353 } 01354 01355 /* allocate memory for an integral number of cluster to hold a whole AttributeList */ 01356 clusters_to_read = size / bytes_per_cluster; 01357 /* the following check is a little bit complicated, because _aulldvrm() call is missing on w2k */ 01358 if(size - clusters_to_read * bytes_per_cluster/*size % bytes_per_cluster*/) clusters_to_read ++; 01359 cluster = (char *)winx_heap_alloc((SIZE_T)(bytes_per_cluster * clusters_to_read)); 01360 if(!cluster){ 01361 DebugPrint("Cannot allocate %I64u bytes of memory for AnalyseNonResidentAttributeList()!\n", 01362 bytes_per_cluster * clusters_to_read); 01363 out_of_memory_condition_counter ++; 01364 return; 01365 } 01366 01367 /* loop through all blocks of file */ 01368 current_cluster = cluster; 01369 for(block = pfn->blockmap; block != NULL; block = block->next_ptr){ 01370 /* loop through clusters of the current block */ 01371 for(i = 0; i < block->length; i++){ 01372 /* read current cluster */ 01373 lsn = (block->lcn + i) * sectors_per_cluster; 01374 status = ReadSectors(lsn,current_cluster,(ULONG)bytes_per_cluster); 01375 if(!NT_SUCCESS(status)){ 01376 DebugPrint("Cannot read the %I64u sector: %x!\n", 01377 lsn,(UINT)status); 01378 goto scan_done; 01379 } 01380 clusters_to_read --; 01381 if(clusters_to_read == 0){ 01382 /* is it the last cluster of the file? */ 01383 if(i < (block->length - 1) || block->next_ptr != pfn->blockmap) 01384 DebugPrint("The attribute list has more clusters than expected.\n"); 01385 goto analyze_list; 01386 } 01387 current_cluster += bytes_per_cluster; 01388 } 01389 if(block->next_ptr == pfn->blockmap) break; 01390 } 01391 01392 analyze_list: 01393 if(clusters_to_read){ 01394 DebugPrint("The attribute list has less number of clusters than expected.\n"); 01395 DebugPrint("Therefore it will be skipped, because anyway we don\'t know its exact size.\n"); 01396 goto scan_done; 01397 } 01398 01399 DebugPrint("Attribute list analysis started...\n"); 01400 attr_list_entry = (PATTRIBUTE_LIST)cluster; 01401 01402 while(TRUE){ 01403 if( ((char *)attr_list_entry + sizeof(ATTRIBUTE_LIST) - sizeof(attr_list_entry->AlignmentOrReserved)) > 01404 ((char *)cluster + size) ) break; 01405 if(CheckForStopEvent()) break; 01406 /* is it a valid attribute */ 01407 if(attr_list_entry->AttributeType == 0xffffffff) break; 01408 if(attr_list_entry->AttributeType == 0x0) break; 01409 if(attr_list_entry->Length == 0) break; 01410 //DebugPrint("@@@@@@@@@ FUCKED Length = %u\n", attr_list_entry->Length); 01411 AnalyseAttributeFromAttributeList(attr_list_entry,pmfi); 01412 /* go to the next attribute list entry */ 01413 length = attr_list_entry->Length; 01414 attr_list_entry = (PATTRIBUTE_LIST)((char *)attr_list_entry + length); 01415 } 01416 DebugPrint("Attribute list analysis completed.\n"); 01417 01418 scan_done: 01419 /* free allocated resources */ 01420 winx_heap_free(cluster); 01421 } 01422 01423 /*------------------------ Defragmentation related code ------------------------------*/ 01424 01433 ULONGLONG ProcessMftSpace(PNTFS_DATA nd) 01434 { 01435 ULONGLONG start,len,mft_len = 0,mirror_size; 01436 01437 /* 01438 * MFT space must be excluded from the free space list. 01439 * Because Windows 2000 disallows to move files there. 01440 * And because on other systems this dirty technique 01441 * causes MFT fragmentation. 01442 */ 01443 01444 /* 01445 * Don't increment dx->processed_clusters here, 01446 * because some parts of MFT are really free. 01447 */ 01448 DebugPrint("MFT part : start : length\n"); 01449 01450 /* $MFT */ 01451 start = nd->MftStartLcn.QuadPart; 01452 if(nd->BytesPerCluster) 01453 len = nd->MftValidDataLength.QuadPart / nd->BytesPerCluster; 01454 else 01455 len = 0; 01456 DebugPrint("$MFT :%I64u :%I64u\n",start,len); 01457 if(CheckBlock(start,len)){ 01458 /* remark space as reserved */ 01459 RemarkBlock(start,len,MFT_ZONE_SPACE,SYSTEM_OR_FREE_SPACE); 01460 RemoveFreeSpaceBlock(start,len); 01461 mft_start = start; mft_end = start + len - 1; 01462 mft_len += len; 01463 } 01464 01465 /* MFT Zone */ 01466 start = nd->MftZoneStart.QuadPart; 01467 len = nd->MftZoneEnd.QuadPart - nd->MftZoneStart.QuadPart + 1; 01468 DebugPrint("MFT Zone :%I64u :%I64u\n",start,len); 01469 if(CheckBlock(start,len)){ 01470 /* remark space as reserved */ 01471 RemarkBlock(start,len,MFT_ZONE_SPACE,SYSTEM_OR_FREE_SPACE); 01472 RemoveFreeSpaceBlock(start,len); 01473 mftzone_start = start; mftzone_end = start + len - 1; 01474 } 01475 01476 /* $MFTMirror */ 01477 start = nd->Mft2StartLcn.QuadPart; 01478 len = 1; 01479 mirror_size = nd->BytesPerFileRecordSegment * 4; 01480 if(nd->BytesPerCluster && mirror_size > nd->BytesPerCluster){ 01481 len = mirror_size / nd->BytesPerCluster; 01482 if(mirror_size - len * nd->BytesPerCluster) 01483 len ++; 01484 } 01485 DebugPrint("$MFTMirror :%I64u :%I64u\n",start,len); 01486 if(CheckBlock(start,len)){ 01487 /* remark space as reserved */ 01488 RemarkBlock(start,len,MFT_ZONE_SPACE,SYSTEM_OR_FREE_SPACE); 01489 RemoveFreeSpaceBlock(start,len); 01490 mftmirr_start = start; mftmirr_end = start + len - 1; 01491 } 01492 01493 return mft_len; 01494 } 01495 01496 /* 01497 * This code may slow down the program: 01498 * for(pfn = dx->filelist; pfn != NULL; pfn = pfn->next_ptr) for each file 01499 */ 01500 01513 void ProcessRun(WCHAR *full_path,PMY_FILE_INFORMATION pmfi, 01514 PFILENAME pfn,ULONGLONG vcn,ULONGLONG length,ULONGLONG lcn) 01515 { 01516 PBLOCKMAP block, prev_block = NULL; 01517 01518 /* if(!wcscmp(full_path,L"\\??\\L:\\go.zip")){ 01519 DbgPrint("VCN %I64u : LEN %I64u : LCN %I64u\n",vcn,length,lcn); 01520 } 01521 */ 01522 (void)pmfi; 01523 01524 /* add information to blockmap member of specified pfn structure */ 01525 if(pfn->blockmap) prev_block = pfn->blockmap->prev_ptr; 01526 block = (PBLOCKMAP)winx_list_insert_item((list_entry **)&pfn->blockmap,(list_entry *)prev_block,sizeof(BLOCKMAP)); 01527 if(!block){ 01528 DebugPrint("Cannot allocate %u bytes of memory for ProcessRun()!\n",sizeof(BLOCKMAP)); 01529 out_of_memory_condition_counter ++; 01530 return; 01531 } 01532 01533 block->vcn = vcn; 01534 block->length = length; 01535 block->lcn = lcn; 01536 01537 pfn->n_fragments ++; 01538 pfn->clusters_total += block->length; 01539 /* 01540 * Sometimes normal file has more than one fragment, 01541 * but is not fragmented yet! 8-) 01542 */ 01543 if(block != pfn->blockmap && \ 01544 block->lcn != (block->prev_ptr->lcn + block->prev_ptr->length)) 01545 pfn->is_fragm = TRUE; 01546 } 01547 01557 PFILENAME FindFileListEntryForTheAttribute(WCHAR *full_path,PMY_FILE_INFORMATION pmfi) 01558 { 01559 PFILENAME pfn; 01560 01561 /* few entries (attributes) may have the same mft id */ 01562 for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){ 01563 /* 01564 * we scan mft from the end to the beginning (RTL) 01565 * and we add new records to the left side of the file list... 01566 */ 01567 if(MftScanDirection == MFT_SCAN_RTL){ 01568 if(pfn->BaseMftId > pmfi->BaseMftId) break; /* we have no chance to find record in list */ 01569 } else { 01570 if(pfn->BaseMftId < pmfi->BaseMftId) break; 01571 } 01572 if(!wcscmp(pfn->name.Buffer,full_path) && \ 01573 (pfn->ParentDirectoryMftId == pmfi->ParentDirectoryMftId) && \ 01574 (pfn->BaseMftId == pmfi->BaseMftId)) 01575 return pfn; /* very slow? */ 01576 //if(pfn->BaseMftId == pmfi->BaseMftId) return pfn; /* safe? */ 01577 if(pfn->next_ptr == filelist) break; 01578 } 01579 01580 pfn = (PFILENAME)winx_list_insert_item((list_entry **)(void *)&filelist,NULL,sizeof(FILENAME)); 01581 if(pfn == NULL){ 01582 DebugPrint("Cannot allocate %u bytes of memory for FindFileListEntryForTheAttribute()!\n",sizeof(FILENAME)); 01583 out_of_memory_condition_counter ++; 01584 return NULL; 01585 } 01586 01587 /* fill a name member of the created structure */ 01588 if(!RtlCreateUnicodeString(&pfn->name,full_path)){ 01589 DebugPrint("Not enough memory for pfn->name initialization!\n"); 01590 out_of_memory_condition_counter ++; 01591 winx_list_remove_item((list_entry **)(void *)&filelist,(list_entry *)pfn); 01592 return NULL; 01593 } 01594 pfn->blockmap = NULL; /* !!! */ 01595 pfn->BaseMftId = pmfi->BaseMftId; 01596 pfn->ParentDirectoryMftId = pmfi->ParentDirectoryMftId; 01597 pfn->PathBuilt = FALSE; 01598 pfn->n_fragments = 0; 01599 pfn->clusters_total = 0; 01600 pfn->is_fragm = FALSE; 01601 pfn->is_compressed = FALSE; 01602 pfn->is_dirty = TRUE; 01603 pfn->is_filtered = FALSE; /* initial state */ 01604 return pfn; 01605 } 01606 01613 void UpdateClusterMapAndStatistics(PMY_FILE_INFORMATION pmfi) 01614 { 01615 PFILENAME pfn; 01616 ULONGLONG filesize; 01617 01618 /* All stuff commented with C++ style comments was moved to BuildPaths() function. */ 01619 for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){ 01620 /* only the first few entries may have dirty flag set */ 01621 if(pfn->is_dirty == FALSE) break; 01622 01623 pfn->is_dirty = FALSE; 01624 /* 1. fill all members of pfn */ 01625 /* 1.1 set flags in pfn ??? */ 01626 /* Note, FILE_ATTR_DIRECTORY is not considered valid in NT. It is 01627 reserved for the DOS SUBDIRECTORY flag. */ 01628 /* always sets is_dir flag to FALSE */ 01629 /*if(pmfi->Flags & FILE_ATTRIBUTE_DIRECTORY) pfn->is_dir = TRUE; 01630 else pfn->is_dir = FALSE; 01631 */ 01632 pfn->is_dir = pmfi->IsDirectory; 01633 /* ordinary attributes in compressed file? */ 01634 /*if(pmfi->Flags & FILE_ATTRIBUTE_COMPRESSED) pfn->is_compressed = TRUE; 01635 else pfn->is_compressed = FALSE; 01636 */ 01637 if((pmfi->Flags & FILE_ATTRIBUTE_REPARSE_POINT) || pmfi->IsReparsePoint){ 01638 DebugPrint("Reparse point found %ws\n",pfn->name.Buffer); 01639 pfn->is_reparse_point = TRUE; 01640 } else pfn->is_reparse_point = FALSE; 01641 /* 1.2 calculate size of attribute data */ 01642 filesize = pfn->clusters_total * bytes_per_cluster; 01643 if(sizelimit && filesize > sizelimit) pfn->is_overlimit = TRUE; 01644 else pfn->is_overlimit = FALSE; 01645 /* mark some files as filtered out */ 01646 CHECK_FOR_FRAGLIMIT(pfn); 01647 /* 1.3 detect temporary files and other unwanted stuff */ 01648 if(TemporaryStuffDetected(pmfi)) pfn->is_filtered = TRUE; 01649 /* 1.4 detect sparse files */ 01650 if(pmfi->Flags & FILE_ATTRIBUTE_SPARSE_FILE) 01651 DebugPrint("Sparse file found %ws\n",pfn->name.Buffer); 01652 /* 1.5 detect encrypted files */ 01653 if(pmfi->Flags & FILE_ATTRIBUTE_ENCRYPTED){ 01654 DebugPrint2("Encrypted file found %ws\n",pfn->name.Buffer); 01655 } 01656 /* 2. redraw cluster map */ 01657 // MarkSpace(dx,pfn,SYSTEM_SPACE); 01658 /* 3. update statistics */ 01659 Stat.filecounter ++; 01660 if(pfn->is_dir) Stat.dircounter ++; 01661 if(pfn->is_compressed) Stat.compressedcounter ++; 01662 /* skip here filtered out and big files and reparse points */ 01663 // if(pfn->is_fragm && !pfn->is_filtered && !pfn->is_overlimit && !pfn->is_reparse_point){ 01664 // dx->fragmfilecounter ++; 01665 // dx->fragmcounter += pfn->n_fragments; 01666 // } else { 01667 // dx->fragmcounter ++; 01668 // } 01669 /* MFT? */ 01670 //if(pfn->clusters_total > 10000) DbgPrint("%I64u %ws\n",pfn->clusters_total,pfn->name.Buffer); 01671 //DbgPrint("%ws %I64u\n",pfn->name.Buffer,pfn->clusters_total); 01672 Stat.processed_clusters += pfn->clusters_total; 01673 01674 if(pfn->next_ptr == filelist) break; 01675 } 01676 } 01677 01685 BOOLEAN TemporaryStuffDetected(PMY_FILE_INFORMATION pmfi) 01686 { 01687 /* skip temporary files ;-) */ 01688 if(pmfi->Flags & FILE_ATTRIBUTE_TEMPORARY){ 01689 DebugPrint2("-Ultradfg- Temporary file found %ws\n",pmfi->Name); 01690 return TRUE; 01691 } 01692 return FALSE; 01693 } 01694 01695 /* that's unbelievable, but this function runs fast */ 01703 BOOLEAN UnwantedStuffDetected(PFILENAME pfn) 01704 { 01705 UNICODE_STRING us; 01706 01707 /* skip all unwanted files by user defined patterns */ 01708 if(!RtlCreateUnicodeString(&us,pfn->name.Buffer)){ 01709 DebugPrint("Cannot allocate memory for UnwantedStuffDetected()!\n"); 01710 out_of_memory_condition_counter ++; 01711 return FALSE; 01712 } 01713 (void)_wcslwr(us.Buffer); 01714 01715 if(in_filter.buffer){ 01716 if(!IsStringInFilter(us.Buffer,&in_filter)){ 01717 RtlFreeUnicodeString(&us); return TRUE; /* not included */ 01718 } 01719 } 01720 01721 if(ex_filter.buffer){ 01722 if(IsStringInFilter(us.Buffer,&ex_filter)){ 01723 RtlFreeUnicodeString(&us); return TRUE; /* excluded */ 01724 } 01725 } 01726 RtlFreeUnicodeString(&us); 01727 01728 return FALSE; 01729 } 01730 01731 PMY_FILE_ENTRY mf = NULL; /* pointer to array of MY_FILE_ENTRY structures */ 01732 ULONG n_entries; 01733 BOOLEAN mf_allocated = FALSE; 01734 01739 void BuildPaths(void) 01740 { 01741 ULONGLONG tm, time; 01742 PFILENAME pfn; 01743 ULONG i; 01744 01745 DebugPrint("BuildPaths() started...\n"); 01746 tm = _rdtsc(); 01747 01748 /* prepare data for fast binary search */ 01749 mf_allocated = FALSE; 01750 /*n_entries = Stat.filecounter;*/ 01751 /* more accurately */ 01752 n_entries = 0; 01753 for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){ 01754 n_entries++; 01755 if(pfn->next_ptr == filelist) break; 01756 } 01757 01758 if(n_entries){ 01759 mf = (PMY_FILE_ENTRY)winx_heap_alloc(n_entries * sizeof(MY_FILE_ENTRY)); 01760 if(mf != NULL) mf_allocated = TRUE; 01761 else DebugPrint("Cannot allocate %u bytes of memory for mf array!\n", 01762 n_entries * sizeof(MY_FILE_ENTRY)); 01763 } 01764 if(mf_allocated){ 01765 /* fill array */ 01766 i = 0; 01767 for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){ 01768 mf[i].mft_id = pfn->BaseMftId; 01769 mf[i].pfn = pfn; 01770 if(i == (n_entries - 1)){ 01771 if(pfn->next_ptr != filelist) 01772 DebugPrint("BuildPaths(): ???\n"); 01773 break; 01774 } 01775 i++; 01776 if(pfn->next_ptr == filelist) break; 01777 } 01778 DebugPrint("Fast binary search will be used.\n"); 01779 } else { 01780 DebugPrint("Slow linear search will be used.\n"); 01781 } 01782 01783 for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){ 01784 BuildPath2(pfn); 01785 if(UnwantedStuffDetected(pfn)) pfn->is_filtered = TRUE; 01786 MarkFileSpace(pfn,SYSTEM_OR_MFT_ZONE_SPACE); 01787 /* skip here filtered out and big files and reparse points */ 01788 if(pfn->is_fragm && !pfn->is_reparse_point && 01789 ((!pfn->is_filtered && !pfn->is_overlimit) || optimize_flag) 01790 ){ 01791 Stat.fragmfilecounter ++; 01792 Stat.fragmcounter += pfn->n_fragments; 01793 } else { 01794 Stat.fragmcounter ++; 01795 } 01796 if(pfn->next_ptr == filelist) break; 01797 } 01798 01799 /* free allocated resources */ 01800 if(mf_allocated) winx_heap_free(mf); 01801 time = _rdtsc() - tm; 01802 DebugPrint("BuildPaths() completed in %I64u ms.\n",time); 01803 } 01804 01810 void BuildPath2(PFILENAME pfn) 01811 { 01812 WCHAR *buffer1; 01813 WCHAR *buffer2; 01814 ULONG offset; 01815 ULONGLONG mft_id,parent_mft_id; 01816 ULONG name_length; 01817 WCHAR header[] = L"\\??\\A:"; 01818 BOOLEAN FullPathRetrieved = FALSE; 01819 UNICODE_STRING us; 01820 01821 /* skip invalid files which have no name */ 01822 if(pfn->name.Buffer[0] == 0){ 01823 DebugPrint("BuildPath2: Invalid entry found: file has no name!\n"); 01824 DebugPrint("BuildPath2: MFT ID = %I64u\n",pfn->BaseMftId); 01825 return; 01826 } 01827 01828 /* allocate memory */ 01829 buffer1 = (WCHAR *)winx_heap_alloc((MAX_NTFS_PATH) * sizeof(short)); 01830 if(!buffer1){ 01831 DebugPrint("BuildPath2(): cannot allocate memory for buffer1\n"); 01832 out_of_memory_condition_counter ++; 01833 return; 01834 } 01835 buffer2 = (WCHAR *)winx_heap_alloc((MAX_NTFS_PATH) * sizeof(short)); 01836 if(!buffer2){ 01837 DebugPrint("BuildPath2(): cannot allocate memory for buffer2\n"); 01838 out_of_memory_condition_counter ++; 01839 winx_heap_free(buffer1); 01840 return; 01841 } 01842 01843 /* terminate buffer1 with zero */ 01844 offset = MAX_NTFS_PATH - 1; 01845 buffer1[offset] = 0; /* terminating zero */ 01846 offset --; 01847 01848 /* copy filename to the right side of the buffer1 */ 01849 name_length = wcslen(pfn->name.Buffer); 01850 if(offset < (name_length - 1)){ 01851 DebugPrint("BuildPath2(): %ws filename is too long (%u characters)\n", 01852 pfn->name.Buffer,name_length); 01853 winx_heap_free(buffer1); 01854 winx_heap_free(buffer2); 01855 return; 01856 } 01857 01858 offset -= (name_length - 1); 01859 (void)wcsncpy(buffer1 + offset,pfn->name.Buffer,name_length); 01860 01861 if(offset == 0) goto path_is_too_long; 01862 offset --; 01863 01864 /* add backslash */ 01865 buffer1[offset] = '\\'; 01866 01867 if(offset == 0) goto path_is_too_long; 01868 offset --; 01869 01870 if(offset == 0) goto path_is_too_long; 01871 01872 parent_mft_id = pfn->ParentDirectoryMftId; 01873 while(parent_mft_id != FILE_root){ 01874 if(CheckForStopEvent()) goto build_path_done; 01875 mft_id = parent_mft_id; 01876 FullPathRetrieved = GetFileNameAndParentMftId(mft_id,&parent_mft_id,buffer2,MAX_NTFS_PATH); 01877 if(buffer2[0] == 0){ 01878 DebugPrint("BuildPath2(): cannot retrieve parent directory name!\n"); 01879 goto build_path_done; 01880 } 01881 //DbgPrint("%ws\n",buffer2); 01882 /* append buffer2 contents to the right side of buffer1 */ 01883 name_length = wcslen(buffer2); 01884 if(offset < (name_length - 1)) goto path_is_too_long; 01885 offset -= (name_length - 1); 01886 (void)wcsncpy(buffer1 + offset,buffer2,name_length); 01887 01888 if(FullPathRetrieved) goto update_filename; 01889 01890 if(offset == 0) goto path_is_too_long; 01891 offset --; 01892 01893 /* add backslash */ 01894 buffer1[offset] = '\\'; 01895 01896 if(offset == 0) goto path_is_too_long; 01897 offset --; 01898 01899 if(offset == 0) goto path_is_too_long; 01900 } 01901 01902 /* 01903 * the root directory must not contain trailing dot, 01904 * otherwise it cannot be opened for moving 01905 */ 01906 if(offset == ((MAX_NTFS_PATH - 1) - 3) && buffer1[offset + 1] == '\\' 01907 && buffer1[offset + 2] == '.'){ 01908 DebugPrint("Root directory detected, its trailing dot will be removed.\n"); 01909 buffer1[offset + 2] = 0; 01910 } 01911 01912 /* append volume letter */ 01913 header[4] = volume_letter; 01914 name_length = wcslen(header); 01915 if(offset < (name_length - 1)) goto path_is_too_long; 01916 offset -= (name_length - 1); 01917 (void)wcsncpy(buffer1 + offset,header,name_length); 01918 01919 update_filename: 01920 /* replace pfn->name contents with full path */ 01921 //wcsncpy(pmfi->Name,buffer1 + offset,MAX_NTFS_PATH); 01922 //pmfi->Name[MAX_NTFS_PATH - 1] = 0; 01923 if(!RtlCreateUnicodeString(&us,buffer1 + offset)){ 01924 DebugPrint("Cannot allocate memory for BuildPath2()!\n"); 01925 out_of_memory_condition_counter ++; 01926 } else { 01927 RtlFreeUnicodeString(&(pfn->name)); 01928 pfn->name.Buffer = us.Buffer; 01929 pfn->name.Length = us.Length; 01930 pfn->name.MaximumLength = us.MaximumLength; 01931 } 01932 pfn->PathBuilt = TRUE; 01933 01934 #ifdef DETAILED_LOGGING 01935 DebugPrint("FULL PATH = %ws\n",pfn->name.Buffer); 01936 #endif 01937 01938 build_path_done: 01939 winx_heap_free(buffer1); 01940 winx_heap_free(buffer2); 01941 return; 01942 01943 path_is_too_long: 01944 DebugPrint("BuildPath2(): path is too long: %ws\n",buffer1); 01945 winx_heap_free(buffer1); 01946 winx_heap_free(buffer2); 01947 } 01948 01963 BOOLEAN GetFileNameAndParentMftId(ULONGLONG mft_id,ULONGLONG *parent_mft_id,WCHAR *buffer,ULONG length) 01964 { 01965 PFILENAME pfn; 01966 BOOLEAN FullPathRetrieved = FALSE; 01967 01968 /* initialize data */ 01969 buffer[0] = 0; 01970 *parent_mft_id = FILE_root; 01971 01972 /* find an appropriate pfn structure */ 01973 pfn = FindDirectoryByMftId(mft_id); 01974 01975 if(pfn == NULL){ 01976 DebugPrint("%I64u directory not found!\n",mft_id); 01977 return FullPathRetrieved; 01978 } 01979 01980 /* update data */ 01981 *parent_mft_id = pfn->ParentDirectoryMftId; 01982 (void)wcsncpy(buffer,pfn->name.Buffer,length); 01983 buffer[length-1] = 0; 01984 FullPathRetrieved = pfn->PathBuilt; 01985 01986 if(buffer[0] == 0){ 01987 DebugPrint("GetFileNameAndParentMftId: Invalid entry found: file has no name!\n"); 01988 DebugPrint("GetFileNameAndParentMftId: MFT ID = %I64u\n",mft_id); 01989 } 01990 01991 return FullPathRetrieved; 01992 } 01993 01999 void AddResidentDirectoryToFileList(PMY_FILE_INFORMATION pmfi) 02000 { 02001 PFILENAME pfn; 02002 02003 if(pmfi->Name[0] == 0){ 02004 DebugPrint("AddResidentDirectoryToFileList: Invalid entry found: file has no name!\n"); 02005 DebugPrint("AddResidentDirectoryToFileList: MFT ID = %I64u\n",pmfi->BaseMftId); 02006 return; 02007 } 02008 02009 pfn = (PFILENAME)winx_list_insert_item((list_entry **)(void *)&filelist,NULL,sizeof(FILENAME)); 02010 if(pfn == NULL){ 02011 DebugPrint("Cannot allocate %u bytes of memory for AddResidentDirectoryToFileList()!\n",sizeof(FILENAME)); 02012 out_of_memory_condition_counter ++; 02013 return; 02014 } 02015 02016 /* fill a name member of the created structure */ 02017 if(!RtlCreateUnicodeString(&pfn->name,pmfi->Name)){ 02018 DebugPrint("AddResidentDirectoryToFileList():\n"); 02019 DebugPrint("no enough memory for pfn->name initialization!\n"); 02020 out_of_memory_condition_counter ++; 02021 winx_list_remove_item((list_entry **)(void *)&filelist,(list_entry *)pfn); 02022 return; 02023 } 02024 pfn->blockmap = NULL; /* !!! */ 02025 pfn->BaseMftId = pmfi->BaseMftId; 02026 pfn->ParentDirectoryMftId = pmfi->ParentDirectoryMftId; 02027 pfn->PathBuilt = FALSE; // what's fucked mistake - it was TRUE here; 02028 pfn->n_fragments = 0; 02029 pfn->clusters_total = 0; 02030 pfn->is_fragm = FALSE; 02031 pfn->is_compressed = FALSE; 02032 pfn->is_dir = TRUE; 02033 pfn->is_reparse_point = pmfi->IsReparsePoint; 02034 pfn->is_overlimit = FALSE; 02035 pfn->is_filtered = TRUE; 02036 pfn->is_dirty = TRUE; 02037 } 02038 02045 PFILENAME FindDirectoryByMftId(ULONGLONG mft_id) 02046 { 02047 PFILENAME pfn; 02048 ULONG lim, i, k; 02049 signed long m; 02050 BOOLEAN ascending_order; 02051 02052 if(mf_allocated == FALSE){ /* use slow search */ 02053 for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){ 02054 if(pfn->BaseMftId == mft_id){ 02055 if(wcsstr(pfn->name.Buffer,L":$") == NULL) 02056 return pfn; 02057 } 02058 if(pfn->next_ptr == filelist) break; 02059 } 02060 return NULL; 02061 } else { /* use fast binary search */ 02062 /* Based on bsearch() algorithm copyrighted by DJ Delorie (1994). */ 02063 ascending_order = (MftScanDirection == MFT_SCAN_RTL) ? TRUE : FALSE; 02064 i = 0; 02065 for(lim = n_entries; lim != 0; lim >>= 1){ 02066 k = i + (lim >> 1); 02067 if(mf[k].mft_id == mft_id){ 02068 /* search for proper entry in neighbourhood of found entry */ 02069 for(m = k; m >= 0; m --){ 02070 if(mf[m].mft_id != mft_id) break; 02071 } 02072 for(m = m + 1; (unsigned long)(m) < n_entries; m ++){ 02073 if(mf[m].mft_id != mft_id) break; 02074 if(wcsstr(mf[m].pfn->name.Buffer,L":$") == NULL) 02075 return mf[m].pfn; 02076 } 02077 DebugPrint("FUCK 1\n"); 02078 return NULL; 02079 } 02080 if(ascending_order){ 02081 if(mft_id > mf[k].mft_id){ 02082 i = k + 1; lim --; /* move right */ 02083 } /* else move left */ 02084 } else { 02085 if(mft_id < mf[k].mft_id){ 02086 i = k + 1; lim --; /* move right */ 02087 } /* else move left */ 02088 } 02089 } 02090 DebugPrint("FUCK 2\n"); 02091 return NULL; 02092 } 02093 } 02094 02107 NTSTATUS ReadSectors(ULONGLONG lsn,PVOID buffer,ULONG length) 02108 { 02109 IO_STATUS_BLOCK ioStatus; 02110 LARGE_INTEGER offset; 02111 NTSTATUS Status; 02112 ULONGLONG tm, time; 02113 02114 offset.QuadPart = lsn * bytes_per_sector; 02115 Status = NtReadFile(winx_fileno(fVolume),NULL,NULL,NULL,&ioStatus,buffer,length,&offset,NULL); 02116 if(NT_SUCCESS(Status)/* == STATUS_PENDING*/){ 02117 DebugPrint("ReadSectors waiting started...\n"); 02118 tm = _rdtsc(); 02119 Status = NtWaitForSingleObject(winx_fileno(fVolume),FALSE,NULL); 02120 time = _rdtsc() - tm; 02121 DebugPrint("ReadSectors waiting completed in %I64u ms.\n", time); 02122 if(NT_SUCCESS(Status)) Status = ioStatus.Status; 02123 } 02124 /* FIXME: number of bytes actually read check? */ 02125 return Status; 02126 } 02127