![]() |
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 void MovePartOfFileBlock(PFILENAME pfn,ULONGLONG startVcn, 00030 ULONGLONG targetLcn,ULONGLONG n_clusters); 00031 void DefragmentFreeSpaceRTL(void); 00032 void DefragmentFreeSpaceLTR(void); 00033 int MoveAllFilesRTL(void); 00034 int OptimizationRoutine(char *volume_name); 00035 00036 ULONGLONG StartingPoint = 0; 00037 int pass_number = 0; 00038 00050 int Optimize(char *volume_name) 00051 { 00052 PFREEBLOCKMAP freeblock; 00053 ULONGLONG threshold; 00054 ULONGLONG FragmentedClustersBeforeStartingPoint = 0; 00055 ULONGLONG ClustersBeforeStartingPoint; 00056 PFILENAME pfn; 00057 PBLOCKMAP block; 00058 00059 Stat.clusters_to_process = Stat.processed_clusters = 0; 00060 Stat.current_operation = 'C'; 00061 00062 /* define threshold */ 00063 threshold = clusters_total / 200; /* 0.5% */ 00064 if(threshold < 2) threshold = 2; 00065 /* define starting point */ 00066 StartingPoint = 0; /* start moving from the beginning of the volume */ 00067 pass_number = 0; 00068 for(freeblock = free_space_map; freeblock != NULL; freeblock = freeblock->next_ptr){ 00069 /* is block larger than 0.5% of the volume space? */ 00070 if(freeblock->lcn > StartingPoint && freeblock->length >= threshold){ 00071 StartingPoint = freeblock->lcn; 00072 break; 00073 } 00074 if(freeblock->next_ptr == free_space_map) return 0; 00075 } 00076 00077 /* skip all locked files */ 00078 //CheckAllFiles(); 00079 00080 /* validate StartingPoint */ 00081 for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){ 00082 if(pfn->is_reparse_point == FALSE && pfn->is_fragm){ 00083 ClustersBeforeStartingPoint = 0; 00084 for(block = pfn->blockmap; block != NULL; block = block->next_ptr){ 00085 if((block->lcn + block->length) <= StartingPoint) 00086 ClustersBeforeStartingPoint += block->length; 00087 if(block->next_ptr == pfn->blockmap) break; 00088 } 00089 if(ClustersBeforeStartingPoint){ 00090 if(!IsFileLocked(pfn)) /* TODO: speedup here? */ 00091 FragmentedClustersBeforeStartingPoint += ClustersBeforeStartingPoint; 00092 } 00093 } 00094 if(FragmentedClustersBeforeStartingPoint >= threshold) break; 00095 if(pfn->next_ptr == filelist) break; 00096 } 00097 if(FragmentedClustersBeforeStartingPoint >= threshold) StartingPoint = 0; 00098 00099 do { 00100 DebugPrint("Optimization pass #%u, StartingPoint = %I64u\n",pass_number,StartingPoint); 00101 Stat.pass_number = pass_number; 00102 /* call optimization routine */ 00103 if(OptimizationRoutine(volume_name) < 0) return (-1); 00104 if(CheckForStopEvent()) return 0; 00105 /* if(!movings) return 0; */ 00106 pass_number ++; 00107 /* redefine starting point */ 00108 for(freeblock = free_space_map; freeblock != NULL; freeblock = freeblock->next_ptr){ 00109 /* is block larger than 0.5% of the volume space? */ 00110 if(freeblock->lcn > StartingPoint && freeblock->length >= threshold){ 00111 StartingPoint = freeblock->lcn; 00112 break; 00113 } 00114 if(freeblock->next_ptr == free_space_map) return 0; 00115 } 00116 } while (1); 00117 return 0; 00118 } 00119 00126 int OptimizationRoutine(char *volume_name) 00127 { 00128 PFREEBLOCKMAP freeblock; 00129 int defragmenter_result = 0; 00130 int optimizer_result = 0; 00131 00132 DebugPrint("----- Optimization of %s: -----\n",volume_name); 00133 00134 /* Initialize progress counters. */ 00135 Stat.clusters_to_process = Stat.processed_clusters = 0; 00136 Stat.current_operation = 'C'; 00137 if(free_space_map){ 00138 for(freeblock = free_space_map->prev_ptr; 1; freeblock = freeblock->prev_ptr){ 00139 if(freeblock->lcn >= StartingPoint) Stat.clusters_to_process += freeblock->length; 00140 if(freeblock->prev_ptr == free_space_map) break; 00141 } 00142 } 00143 00144 /* On FAT volumes it increase distance between dir & files inside it. */ 00145 if(partition_type != NTFS_PARTITION) return (-1); 00146 00147 /* 00148 * First of all - free the leading part of the volume. 00149 */ 00150 DebugPrint("----- First step of optimization of %s: -----\n",volume_name); 00151 DefragmentFreeSpaceLTR(); 00152 if(CheckForStopEvent()) goto optimization_done; 00153 00154 /* 00155 * Second step - defragment files. 00156 * Large files will be moved to the beginning of the volume. 00157 */ 00158 DebugPrint("----- Second step of optimization of %s: -----\n",volume_name); 00159 if(Analyze(volume_name) < 0) return (-1); 00161 defragmenter_result = Defragment(volume_name); 00163 00164 /* 00165 * Final step - move all files to the leading part of the volume. 00166 */ 00167 DebugPrint("----- Third step of optimization of %s: -----\n",volume_name); 00169 optimizer_result = MoveAllFilesRTL(); 00170 00171 optimization_done: 00172 /* Analyse volume again to update fragmented files list. */ 00173 (void)NtClearEvent(hStopEvent); 00174 if(Analyze(volume_name) < 0) return (-1); 00175 00176 if(defragmenter_result >= 0 || optimizer_result >= 0) return 0; 00177 return (-1); 00178 } 00179 00187 void DefragmentFreeSpaceRTL(void) 00188 { 00189 PFILENAME pfn, lastpfn; 00190 PBLOCKMAP block, lastblock; 00191 PFREEBLOCKMAP freeblock; 00192 ULONGLONG maxlcn; 00193 ULONGLONG vcn, length; 00194 ULONGLONG movings; 00195 00196 /* skip all locked files */ 00197 //CheckAllFiles(); 00198 00199 while(1){ 00200 /* 1. Find the latest file block on the volume. */ 00201 lastblock = NULL; lastpfn = NULL; maxlcn = 0; 00202 for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){ 00203 if(pfn->is_reparse_point == FALSE){ 00204 for(block = pfn->blockmap; block != NULL; block = block->next_ptr){ 00205 if(block->lcn > maxlcn){ 00206 lastblock = block; 00207 lastpfn = pfn; 00208 maxlcn = block->lcn; 00209 } 00210 if(block->next_ptr == pfn->blockmap) break; 00211 } 00212 } 00213 if(pfn->next_ptr == filelist) break; 00214 } 00215 if(!lastblock) break; 00216 if(IsFileLocked(lastpfn)) continue; 00217 DebugPrint("Last block = %ws: Lcn:%I64u Length:%I64u\n",lastpfn->name.Buffer, 00218 lastblock->lcn,lastblock->length); 00219 if(CheckForStopEvent()) break; 00220 /* 2. Fill free space areas in the beginning of the volume with lastblock contents. */ 00221 movings = 0; 00222 for(freeblock = free_space_map; freeblock != NULL; freeblock = freeblock->next_ptr){ 00223 if(freeblock->lcn < lastblock->lcn && freeblock->length){ 00224 /* fill block with lastblock contents */ 00225 length = min(freeblock->length,lastblock->length); 00226 vcn = lastblock->vcn + (lastblock->length - length); 00227 MovePartOfFileBlock(lastpfn,vcn,freeblock->lcn,length); 00228 movings ++; 00229 /*Stat.processed_clusters += length;*/ 00230 RemarkBlock(freeblock->lcn,length,UNKNOWN_SPACE,FREE_SPACE); 00231 RemarkBlock(lastblock->lcn + (lastblock->length - length),length, 00232 FREE_OR_MFT_ZONE_SPACE,GetFileSpaceState(lastpfn)); 00233 freeblock->length -= length; 00234 freeblock->lcn += length; 00235 lastblock->length -= length; 00236 if(!lastblock->length){ 00237 winx_list_remove_item((list_entry **)&lastpfn->blockmap,(list_entry *)lastblock); 00238 break; 00239 } 00240 } 00241 if(CheckForStopEvent()) break; 00242 if(freeblock->next_ptr == free_space_map) break; 00243 } 00244 if(!movings) break; 00245 } 00246 } 00247 00255 void DefragmentFreeSpaceLTR(void) 00256 { 00257 PFILENAME pfn, firstpfn; 00258 PBLOCKMAP block, firstblock; 00259 PFREEBLOCKMAP freeblock; 00260 ULONGLONG minlcn; 00261 ULONGLONG vcn, length; 00262 ULONGLONG movings; 00263 00264 /* skip all locked files */ 00265 //CheckAllFiles(); 00266 00267 while(1){ 00268 /* 1. Find the first file block on the volume: AFTER StartingPoint. */ 00269 firstblock = NULL; firstpfn = NULL; minlcn = (clusters_total - 1); 00270 for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){ 00271 if(pfn->is_reparse_point == FALSE){ 00272 for(block = pfn->blockmap; block != NULL; block = block->next_ptr){ 00273 if(block->lcn < minlcn && block->lcn >= StartingPoint){ 00274 firstblock = block; 00275 firstpfn = pfn; 00276 minlcn = block->lcn; 00277 } 00278 if(block->next_ptr == pfn->blockmap) break; 00279 } 00280 } 00281 if(pfn->next_ptr == filelist) break; 00282 } 00283 if(!firstblock) break; 00284 if(IsFileLocked(firstpfn)) continue; 00285 DebugPrint("First block = %ws: Lcn:%I64u Length:%I64u\n",firstpfn->name.Buffer, 00286 firstblock->lcn,firstblock->length); 00287 if(CheckForStopEvent()) break; 00288 /* 2. Fill free space areas in the terminal part of the volume with firstblock contents. */ 00289 movings = 0; 00290 if(!free_space_map) break; 00291 for(freeblock = free_space_map->prev_ptr; 1; freeblock = freeblock->prev_ptr){ 00292 if(freeblock->lcn > firstblock->lcn && freeblock->length){ 00293 /* fill block with firstblock contents */ 00294 length = min(freeblock->length,firstblock->length); 00295 vcn = firstblock->vcn; 00296 MovePartOfFileBlock(firstpfn,vcn, 00297 freeblock->lcn + (freeblock->length - length),length); 00298 movings ++; 00299 /*Stat.processed_clusters += length;*/ 00300 RemarkBlock(freeblock->lcn + (freeblock->length - length),length, 00301 UNKNOWN_SPACE,FREE_SPACE); 00302 RemarkBlock(firstblock->lcn,length,FREE_OR_MFT_ZONE_SPACE,GetFileSpaceState(firstpfn)); 00303 freeblock->length -= length; 00304 firstblock->vcn += length; 00305 firstblock->lcn += length; 00306 firstblock->length -= length; 00307 if(!firstblock->length){ 00308 winx_list_remove_item((list_entry **)&firstpfn->blockmap,(list_entry *)firstblock); 00309 break; 00310 } 00311 } 00312 if(CheckForStopEvent()) break; 00313 if(freeblock->prev_ptr == free_space_map->prev_ptr) break; 00314 } 00315 if(!movings) break; 00316 } 00317 } 00318 00329 BOOLEAN MoveTheUnfragmentedFile(PFILENAME pfn,ULONGLONG target) 00330 { 00331 NTSTATUS Status; 00332 HANDLE hFile; 00333 PBLOCKMAP block; 00334 00335 /* Open the file */ 00336 Status = OpenTheFile(pfn,&hFile); 00337 if(Status){ 00338 DebugPrint("Can't open %ws file: %x\n",pfn->name.Buffer,(UINT)Status); 00339 /* we need to destroy the block map to avoid infinite loops */ 00340 DeleteBlockmap(pfn); /* file is locked by other application, so its state is unknown */ 00341 return FALSE; 00342 } 00343 00344 DebugPrint("%ws\n",pfn->name.Buffer); 00345 DebugPrint("t: %I64u n: %I64u\n",target,pfn->clusters_total); 00346 00347 Status = MoveBlocksOfFile(pfn,hFile,target); 00348 NtClose(hFile); 00349 00350 /* first of all: remove target space from free space pool */ 00351 RemarkBlock(target,pfn->clusters_total,GetFileSpaceState(pfn),FREE_SPACE); 00352 TruncateFreeSpaceBlock(target,pfn->clusters_total); 00353 00354 /* free previously allocated space (after TruncateFreeSpaceBlock() call!) */ 00355 for(block = pfn->blockmap; block != NULL; block = block->next_ptr){ 00356 ProcessFreedBlock(block->lcn,block->length,FRAGM_SPACE/*old_state*/); 00357 if(block->next_ptr == pfn->blockmap) break; 00358 } 00359 00360 DeleteBlockmap(pfn); /* because we don't need this info after file moving */ 00361 return TRUE; 00362 } 00363 00369 int MoveAllFilesRTL(void) 00370 { 00371 PFREEBLOCKMAP block; 00372 PFILENAME pfn, plargest; 00373 ULONGLONG length; 00374 ULONGLONG locked_clusters; 00375 ULONGLONG movings = 0; 00376 00377 Stat.clusters_to_process = Stat.processed_clusters = 0; 00378 Stat.current_operation = 'C'; 00379 00380 /* skip all locked files */ 00381 //CheckAllFiles(); 00382 00383 /* Reinitialize progress counters. */ 00384 for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){ 00385 if(pfn->blockmap && !pfn->is_reparse_point) 00386 if(pfn->blockmap->lcn >= StartingPoint) 00387 Stat.clusters_to_process += pfn->clusters_total; 00388 if(pfn->next_ptr == filelist) break; 00389 } 00390 00391 for(block = free_space_map; block != NULL; block = block->next_ptr){ 00392 L0: 00393 if(block->length <= 1) goto L1; /* skip 1 cluster blocks and zero length blocks */ 00394 //if(block->lcn < StartingPoint) goto L1; /* skip blocks before StartingPoint */ 00395 /* find largest unfragmented file that can be stored here */ 00396 plargest = NULL; length = 0; 00397 for(pfn = filelist; pfn != NULL; pfn = pfn->next_ptr){ 00398 if(pfn->is_reparse_point == FALSE){ 00399 if(pfn->blockmap && pfn->clusters_total <= block->length){ 00400 if(pfn->clusters_total > length && pfn->blockmap->lcn >= StartingPoint){ 00401 plargest = pfn; 00402 length = pfn->clusters_total; 00403 } 00404 } 00405 } 00406 if(pfn->next_ptr == filelist) break; 00407 } 00408 if(!plargest) goto L1; /* current block is too small */ 00409 locked_clusters = 0; 00410 if(plargest->blockmap) locked_clusters = plargest->clusters_total; 00411 if(IsFileLocked(plargest)){ 00412 Stat.processed_clusters += locked_clusters; 00413 goto L0; 00414 } 00415 /* skip fragmented files */ 00416 //if(plargest->is_fragm) goto L1; /* never do it!!! */ 00417 /* if uncomment the previous line the file moving will never start, 00418 because free blocks will be always skipped by this instruction */ 00419 /* move the file */ 00420 if(MoveTheUnfragmentedFile(plargest,block->lcn)){ 00421 DebugPrint("Moving success for %ws\n",plargest->name.Buffer); 00422 movings++; 00423 } else { 00424 DebugPrint("Moving error for %ws\n",plargest->name.Buffer); 00425 } 00426 /*Stat.processed_clusters += plargest->clusters_total;*/ 00427 if(CheckForStopEvent()) break; 00428 /* after file moving continue from the first free space block */ 00429 block = free_space_map; if(!block) break; else goto L0; 00430 L1: 00431 if(block->next_ptr == free_space_map) break; 00432 } 00433 return (movings == 0) ? (-1) : (0); 00434 } 00435