![]() |
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 00029 BOOLEAN AddFile(short *path,PFILE_BOTH_DIR_INFORMATION pFileInfo); 00030 BOOLEAN UnwantedStuffOnFatOrUdfDetected(PFILE_BOTH_DIR_INFORMATION pFileInfo,PFILENAME pfn); 00031 BOOLEAN ConsoleUnwantedStuffDetected(WCHAR *Path,ULONG *InsideFlag); 00032 00042 int FindFiles(WCHAR *ParentDirectoryPath) 00043 { 00044 OBJECT_ATTRIBUTES ObjectAttributes; 00045 PFILE_BOTH_DIR_INFORMATION pFileInfoFirst = NULL, pFileInfo; 00046 IO_STATUS_BLOCK IoStatusBlock; 00047 UNICODE_STRING us; 00048 NTSTATUS Status; 00049 HANDLE DirectoryHandle; 00050 WCHAR *Path = NULL; 00051 #define PATH_BUFFER_LENGTH (MAX_PATH + 5) 00052 static WCHAR FileName[MAX_PATH]; /* static is used to allocate this variable in global space */ 00053 USHORT FileNameLength; 00054 ULONG inside_flag = TRUE; 00055 00056 /* allocate memory */ 00057 pFileInfoFirst = (PFILE_BOTH_DIR_INFORMATION)winx_heap_alloc( 00058 FIND_DATA_SIZE + sizeof(PFILE_BOTH_DIR_INFORMATION)); 00059 if(!pFileInfoFirst){ 00060 DebugPrint("Cannot allocate memory for FILE_BOTH_DIR_INFORMATION structure!\n"); 00061 out_of_memory_condition_counter ++; 00062 return UDEFRAG_NO_MEM; 00063 } 00064 Path = (WCHAR *)winx_heap_alloc(PATH_BUFFER_LENGTH * sizeof(WCHAR)); 00065 if(Path == NULL){ 00066 DebugPrint("Cannot allocate memory for FILE_BOTH_DIR_INFORMATION structure!\n"); 00067 out_of_memory_condition_counter ++; 00068 winx_heap_free(pFileInfoFirst); 00069 return UDEFRAG_NO_MEM; 00070 } 00071 00072 /* open directory */ 00073 RtlInitUnicodeString(&us,ParentDirectoryPath); 00074 InitializeObjectAttributes(&ObjectAttributes,&us,0,NULL,NULL); 00075 Status = NtCreateFile(&DirectoryHandle,FILE_LIST_DIRECTORY | FILE_RESERVE_OPFILTER, 00076 &ObjectAttributes,&IoStatusBlock,NULL,0, 00077 FILE_SHARE_READ|FILE_SHARE_WRITE,FILE_OPEN, 00078 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT, 00079 NULL,0); 00080 if(Status != STATUS_SUCCESS){ 00081 DebugPrint("Cannot open directory: %ws: %x\n",ParentDirectoryPath,(UINT)Status); 00082 DirectoryHandle = NULL; winx_heap_free(pFileInfoFirst); winx_heap_free(Path); 00083 return (-1); 00084 } 00085 00086 /* Query information about files */ 00087 pFileInfo = pFileInfoFirst; 00088 pFileInfo->FileIndex = 0; 00089 pFileInfo->NextEntryOffset = 0; 00090 while(!CheckForStopEvent()){ 00091 if (pFileInfo->NextEntryOffset != 0){ 00092 pFileInfo = (PVOID)((char *)(ULONG_PTR)pFileInfo + pFileInfo->NextEntryOffset); 00093 } else { 00094 pFileInfo = (PVOID)((char *)(ULONG_PTR)pFileInfoFirst + sizeof(PFILE_BOTH_DIR_INFORMATION)); 00095 pFileInfo->FileIndex = 0; 00096 RtlZeroMemory(pFileInfo,FIND_DATA_SIZE); 00097 Status = NtQueryDirectoryFile(DirectoryHandle,NULL,NULL,NULL, 00098 &IoStatusBlock,(PVOID)pFileInfo, 00099 FIND_DATA_SIZE, 00100 FileBothDirectoryInformation, 00101 FALSE, /* ReturnSingleEntry */ 00102 NULL, 00103 FALSE); /* RestartScan */ 00104 if(Status != STATUS_SUCCESS) break; /* no more items */ 00105 } 00106 00107 FileNameLength = (USHORT)(pFileInfo->FileNameLength / sizeof(WCHAR)); 00108 if(FileNameLength > (MAX_PATH - 1)) continue; /* really it must be no longer than 255 characters */ 00109 00110 /* here we can use the FileName variable */ 00111 (void)wcsncpy(FileName,pFileInfo->FileName,FileNameLength); 00112 FileName[FileNameLength] = 0; 00113 00114 /* skip . and .. */ 00115 if(wcscmp(FileName,L".") == 0 || wcscmp(FileName,L"..") == 0) continue; 00116 00117 /* Path = ParentDirectoryPath + FileName */ 00118 if(ParentDirectoryPath[wcslen(ParentDirectoryPath) - 1] == '\\'){ 00119 /* rootdir contains closing backslash, other directories aren't enclosed */ 00120 (void)_snwprintf(Path,PATH_BUFFER_LENGTH,L"%s%s",ParentDirectoryPath,FileName); 00121 } else { 00122 (void)_snwprintf(Path,PATH_BUFFER_LENGTH,L"%s\\%s",ParentDirectoryPath,FileName); 00123 } 00124 Path[PATH_BUFFER_LENGTH - 1] = 0; 00125 /* and here we cannot use the FileName variable! */ 00126 00127 /* VERY IMPORTANT: skip reparse points */ 00128 /* FIXME: what is reparse point? How to detect these that represents another volumes? */ 00129 if(IS_REPARSE_POINT(pFileInfo)){ 00130 DebugPrint("Reparse point found %ws\n",Path); 00131 continue; 00132 } 00133 00134 /* 00135 * Skip temporary files: 00136 * Not applicable for the volume optimization. 00137 * 00138 * Skip hard links: 00139 * It seems that we don't have any simple way to get information 00140 * on hard links. So we couldn't skip them, although there is safe enough. 00141 */ 00142 00143 /* UltraDefrag has a full support for sparse files! :D */ 00144 if(IS_SPARSE_FILE(pFileInfo)){ 00145 DebugPrint("Sparse file found %ws\n",Path); 00146 /* Let's defragment them! :) */ 00147 } 00148 00149 if(IS_ENCRYPTED_FILE(pFileInfo)){ 00150 DebugPrint2("Encrypted file found %ws\n",Path); 00151 } 00152 00153 if(ConsoleUnwantedStuffDetected(Path,&inside_flag)){ 00154 //DbgPrint("excluded = %ws\n",dx->tmp_buf); 00155 continue; 00156 } 00157 00158 /*if(IS_DIR(pFileInfo) && IS_COMPRESSED(pFileInfo)) 00159 DebugPrint("-Ultradfg- Compressed directory found %ws\n",Path); 00160 */ 00161 00162 if(IS_DIR(pFileInfo)) (void)FindFiles(Path); /* directory found */ 00163 else if(!pFileInfo->EndOfFile.QuadPart) continue; /* empty file found */ 00164 00165 /* skip parent directories in context menu handler */ 00166 if(context_menu_handler && !inside_flag) continue; 00167 00168 if(!AddFile(Path,pFileInfo)){ 00169 DebugPrint("AddFile failed for %ws\n",Path); 00170 NtClose(DirectoryHandle); 00171 winx_heap_free(pFileInfoFirst); 00172 winx_heap_free(Path); 00173 return UDEFRAG_NO_MEM; 00174 } 00175 } 00176 00177 NtClose(DirectoryHandle); 00178 winx_heap_free(pFileInfoFirst); 00179 winx_heap_free(Path); 00180 return 0; 00181 } 00182 00191 BOOLEAN AddFile(short *path,PFILE_BOTH_DIR_INFORMATION pFileInfo) 00192 { 00193 PFILENAME pfn; 00194 ULONGLONG filesize; 00195 00196 pfn = (PFILENAME)winx_list_insert_item((list_entry **)(void *)&filelist,NULL,sizeof(FILENAME)); 00197 if(pfn == NULL) return FALSE; 00198 00199 /* Initialize pfn->name field. */ 00200 if(!RtlCreateUnicodeString(&pfn->name,path)){ 00201 DebugPrint("Not enough memory for pfn->name initialization!\n"); 00202 winx_list_remove_item((list_entry **)(void *)&filelist,(list_entry *)pfn); 00203 out_of_memory_condition_counter ++; 00204 return FALSE; 00205 } 00206 00207 /* Set flags. */ 00208 pfn->is_dir = IS_DIR(pFileInfo); 00209 pfn->is_compressed = IS_COMPRESSED(pFileInfo); 00210 pfn->is_reparse_point = IS_REPARSE_POINT(pFileInfo); 00211 00212 /* 00213 * This code fails for 3.99 Gb files on FAT32 volumes with 32k cluster size (tested on 32-bit XP), 00214 * because pFileInfo->AllocationSize member holds zero value in this case. 00215 */ 00216 /*if(dx->sizelimit && \ 00217 (ULONGLONG)(pFileInfo->AllocationSize.QuadPart) > dx->sizelimit) 00218 pfn->is_overlimit = TRUE; 00219 else 00220 pfn->is_overlimit = FALSE; 00221 */ 00222 00223 if(UnwantedStuffOnFatOrUdfDetected(pFileInfo,pfn)) pfn->is_filtered = TRUE; 00224 else pfn->is_filtered = FALSE; 00225 00226 /* Dump the file. */ 00227 if(!DumpFile(pfn)){ 00228 /* skip files with unknown state */ 00229 RtlFreeUnicodeString(&pfn->name); 00230 winx_list_remove_item((list_entry **)(void *)&filelist,(list_entry *)pfn); 00231 return TRUE; 00232 } 00233 00234 filesize = pfn->clusters_total * bytes_per_cluster; 00235 if(sizelimit && filesize > sizelimit) pfn->is_overlimit = TRUE; 00236 else pfn->is_overlimit = FALSE; 00237 00238 /* mark some files as filtered out */ 00239 CHECK_FOR_FRAGLIMIT(pfn); 00240 00241 /* FIXME: it shows approx. 1.6 Gb instead of 3.99 Gb */ 00242 if(wcsstr(path,L"largefile")) 00243 DebugPrint("SIZE = %I64u\n", filesize); 00244 00245 /* Update statistics and cluster map. */ 00246 Stat.filecounter ++; 00247 if(pfn->is_dir) Stat.dircounter ++; 00248 if(pfn->is_compressed) Stat.compressedcounter ++; 00249 /* skip here filtered out and big files and reparse points */ 00250 if(pfn->is_fragm && !pfn->is_reparse_point && 00251 ((!pfn->is_filtered && !pfn->is_overlimit) || optimize_flag) 00252 ){ 00253 Stat.fragmfilecounter ++; 00254 Stat.fragmcounter += pfn->n_fragments; 00255 } else { 00256 Stat.fragmcounter ++; 00257 } 00258 Stat.processed_clusters += pfn->clusters_total; 00259 MarkFileSpace(pfn,SYSTEM_SPACE); 00260 00261 if(optimize_flag || pfn->is_fragm) return TRUE; 00262 00263 /* 7. Destroy useless data. */ 00264 DeleteBlockmap(pfn); 00265 RtlFreeUnicodeString(&pfn->name); 00266 winx_list_remove_item((list_entry **)(void *)&filelist,(list_entry *)pfn); 00267 return TRUE; 00268 } 00269 00275 BOOLEAN AddFileToFragmented(PFILENAME pfn) 00276 { 00277 PFRAGMENTED pf, prev_pf = NULL; 00278 00279 for(pf = fragmfileslist; pf != NULL; pf = pf->next_ptr){ 00280 if(pf->pfn->n_fragments <= pfn->n_fragments){ 00281 if(pf != fragmfileslist) prev_pf = pf->prev_ptr; 00282 break; 00283 } 00284 if(pf->next_ptr == fragmfileslist){ 00285 prev_pf = pf; 00286 break; 00287 } 00288 } 00289 00290 pf = (PFRAGMENTED)winx_list_insert_item((list_entry **)(void *)&fragmfileslist,(list_entry *)prev_pf,sizeof(FRAGMENTED)); 00291 if(!pf){ 00292 DebugPrint("Cannot allocate memory for InsertFragmentedFile()!\n"); 00293 out_of_memory_condition_counter ++; 00294 return FALSE; 00295 } 00296 pf->pfn = pfn; 00297 return TRUE; 00298 } 00299 00303 void UpdateFragmentedFilesList(void) 00304 { 00305 PFRAGMENTED pf, next_pf, head; 00306 00307 head = fragmfileslist; 00308 for(pf = fragmfileslist; pf != NULL;){ 00309 next_pf = pf->next_ptr; 00310 if(!pf->pfn->is_fragm){ 00311 winx_list_remove_item((list_entry **)(void *)&fragmfileslist,(list_entry *)pf); 00312 if(fragmfileslist == NULL) break; 00313 if(fragmfileslist != head){ 00314 head = fragmfileslist; 00315 pf = next_pf; 00316 continue; 00317 } 00318 } 00319 pf = next_pf; 00320 if(pf == head) break; 00321 } 00322 } 00323 00333 BOOLEAN UnwantedStuffOnFatOrUdfDetected(PFILE_BOTH_DIR_INFORMATION pFileInfo,PFILENAME pfn) 00334 { 00335 UNICODE_STRING us; 00336 00337 /* skip temporary files ;-) */ 00338 if(IS_TEMPORARY_FILE(pFileInfo)){ 00339 DebugPrint2("Temporary file found %ws\n",pfn->name.Buffer); 00340 return TRUE; 00341 } 00342 00343 /* skip all unwanted files by user defined patterns */ 00344 if(!RtlCreateUnicodeString(&us,pfn->name.Buffer)){ 00345 DebugPrint("Cannot allocate memory for UnwantedStuffDetected()!\n"); 00346 out_of_memory_condition_counter ++; 00347 return FALSE; 00348 } 00349 (void)_wcslwr(us.Buffer); 00350 00351 if(in_filter.buffer){ 00352 if(!IsStringInFilter(us.Buffer,&in_filter)){ 00353 RtlFreeUnicodeString(&us); return TRUE; /* not included */ 00354 } 00355 } 00356 00357 if(ex_filter.buffer){ 00358 if(IsStringInFilter(us.Buffer,&ex_filter)){ 00359 RtlFreeUnicodeString(&us); return TRUE; /* excluded */ 00360 } 00361 } 00362 RtlFreeUnicodeString(&us); 00363 00364 return FALSE; 00365 } 00366 00384 BOOLEAN ConsoleUnwantedStuffDetected(WCHAR *Path,ULONG *InsideFlag) 00385 { 00386 WCHAR *lpath = NULL; 00387 ULONG path_length; 00388 00389 /* let's assume that we are inside directory selected in context menu handler */ 00390 if(InsideFlag) *InsideFlag = TRUE; 00391 00392 if(new_cluster_map || optimize_flag) return FALSE; 00393 00394 /* allocate memory for Path converted to lowercase */ 00395 path_length = wcslen(Path); 00396 if(path_length < 4) return FALSE; /* because must contain \??\ sequence */ 00397 lpath = (WCHAR *)winx_heap_alloc((path_length + 1) * sizeof(WCHAR)); 00398 if(lpath == NULL){ 00399 DebugPrint("Cannot allocate memory for ConsoleUnwantedStuffDetected()!\n"); 00400 out_of_memory_condition_counter ++; 00401 return FALSE; 00402 } 00403 (void)wcscpy(lpath,Path); 00404 (void)_wcslwr(lpath); 00405 00406 /* USE THE FOLLOWING CODE ONLY FOR THE CONTEXT MENU HANDLER: */ 00407 if(context_menu_handler){ 00408 /* is the current file placed in directory selected in context menu? */ 00409 /* in other words: are we inside the selected folder? */ 00410 if(!wcsstr(lpath,in_filter.buffer + in_filter.offsets->offset)){ 00411 if(InsideFlag) *InsideFlag = FALSE; 00412 /* is current path a part of the path selected in context menu? */ 00413 /* in other words: are we going in right direction? */ 00414 if(!wcsstr(in_filter.buffer + in_filter.offsets->offset,lpath + 4)){ 00415 DebugPrint1("Not included: %ws\n",Path); 00416 winx_heap_free(lpath); 00417 return TRUE; 00418 } 00419 } 00420 } 00421 00422 /* 00423 * To speed up the analysis in console/native apps 00424 * set UD_EX_FILTER option, to speed up both analysis 00425 * and defragmentation set UD_IN_FILTER and UD_EX_FILTER options. 00426 */ 00427 if(ex_filter.buffer){ 00428 if(IsStringInFilter(lpath,&ex_filter)){ 00429 DebugPrint1("Excluded: %ws\n",Path); 00430 winx_heap_free(lpath); 00431 return TRUE; 00432 } 00433 } 00434 winx_heap_free(lpath); 00435 return FALSE; 00436 } 00437