![]() |
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 MoveTheFile(PFILENAME pfn,ULONGLONG target); 00030 00031 /* 00032 * Why analysis repeats before each defragmentation attempt (v2.1.2)? 00033 * 00034 * 1. On NTFS volumes we must do it, because otherwise Windows will not free 00035 * temporarily allocated space. 00036 * 2. Modern FAT volumes aren't large enough to extremely slow down 00037 * defragmentation by an additional analysis. 00038 * 3. We cannot use NtNotifyChangeDirectoryFile() function in kernel mode. 00039 * It always returns 0xc0000005 error code (tested in XP 32-bit). It seems 00040 * that its arguments must be in the user memory space. 00041 * 4. Filesystem data is cached by Windows, therefore the second analysis will be 00042 * always much faster (at least on modern versions of Windows - tested on XP) 00043 * than the first attempt. 00044 * 5. On a very large FAT volumes the second analysis may take few minutes. Well, the 00045 * first one may take few hours... :) 00046 */ 00047 00054 int Defragment(char *volume_name) 00055 { 00056 PFRAGMENTED pf, plargest; 00057 PFREEBLOCKMAP block; 00058 ULONGLONG length; 00059 ULONGLONG locked_clusters; 00060 ULONGLONG defragmented = 0; 00061 00062 DebugPrint("----- Defragmentation of %s: -----\n",volume_name); 00063 00064 /* Initialize progress counters. */ 00065 Stat.clusters_to_process = Stat.processed_clusters = 0; 00066 Stat.current_operation = 'D'; 00067 00068 /* skip all locked files */ 00069 //CheckAllFragmentedFiles(); 00070 00071 for(pf = fragmfileslist; pf != NULL; pf = pf->next_ptr){ 00072 if(!pf->pfn->blockmap) goto next_item; /* skip fragmented files with unknown state */ 00073 if(!optimize_flag){ 00074 if(pf->pfn->is_overlimit) goto next_item; /* skip fragmented but filtered out files */ 00075 //if(pf->pfn->is_filtered) goto next_item; /* since v3.2.0 fragmfileslist never contains such entries */ 00076 /* skip fragmented directories on FAT/UDF partitions */ 00077 if(pf->pfn->is_dir && partition_type != NTFS_PARTITION) goto next_item; 00078 } 00079 Stat.clusters_to_process += pf->pfn->clusters_total; 00080 next_item: 00081 if(pf->next_ptr == fragmfileslist) break; 00082 } 00083 00084 /* Fill free space areas. */ 00085 for(block = free_space_map; block != NULL; block = block->next_ptr){ 00086 L0: 00087 if(block->length <= 1) goto L1; /* skip 1 cluster blocks and zero length blocks */ 00088 /* find largest fragmented file that can be stored here */ 00089 plargest = NULL; length = 0; 00090 for(pf = fragmfileslist; pf != NULL; pf = pf->next_ptr){ 00091 if(!pf->pfn->blockmap) goto L2; /* skip fragmented files with unknown state */ 00092 if(!optimize_flag){ 00093 if(pf->pfn->is_overlimit) goto L2; /* skip fragmented but filtered out files */ 00094 if(pf->pfn->is_filtered) goto L2; /* in v3.2.0 fragmfileslist never contained such entries */ 00095 /* skip fragmented directories on FAT/UDF partitions */ 00096 if(pf->pfn->is_dir && partition_type != NTFS_PARTITION) goto L2; 00097 } 00098 if(pf->pfn->clusters_total <= block->length){ 00099 if(pf->pfn->clusters_total > length){ 00100 /* skip locked files here to prevent skipping the current free space block */ 00101 plargest = pf; 00102 length = pf->pfn->clusters_total; 00103 } 00104 } 00105 L2: 00106 if(pf->next_ptr == fragmfileslist) break; 00107 } 00108 if(!plargest) goto L1; /* current block is too small */ 00109 locked_clusters = 0; 00110 if(plargest->pfn->blockmap) locked_clusters = plargest->pfn->clusters_total; 00111 if(IsFileLocked(plargest->pfn)){ 00112 Stat.processed_clusters += locked_clusters; 00113 goto L0; 00114 } 00115 /* move file */ 00116 if(MoveTheFile(plargest->pfn,block->lcn)){ 00117 DebugPrint("Defrag success for %ws\n",plargest->pfn->name.Buffer); 00118 defragmented++; 00119 } else { 00120 DebugPrint("Defrag error for %ws\n",plargest->pfn->name.Buffer); 00121 } 00122 /*Stat.processed_clusters += plargest->pfn->clusters_total;*/ 00123 UpdateFragmentedFilesList(); 00124 if(CheckForStopEvent()) break; 00125 /* after file moving continue from the first free space block */ 00126 block = free_space_map; if(!block) break; else goto L0; 00127 L1: 00128 if(block->next_ptr == free_space_map) break; 00129 } 00130 return (defragmented == 0) ? (-1) : (0); 00131 } 00132 00133 /* ------------------------------------------------------------------------- */ 00134 /* The following function moves clusters. */ 00135 /* ------------------------------------------------------------------------- */ 00136 00150 NTSTATUS MovePartOfFile(HANDLE hFile,ULONGLONG startVcn, ULONGLONG targetLcn, ULONGLONG n_clusters) 00151 { 00152 NTSTATUS status; 00153 IO_STATUS_BLOCK ioStatus; 00154 MOVEFILE_DESCRIPTOR moveFile; 00155 00156 DebugPrint("sVcn: %I64u,tLcn: %I64u,n: %u\n", 00157 startVcn,targetLcn,n_clusters); 00158 00159 if(CheckForStopEvent()) return STATUS_UNSUCCESSFUL; 00160 00161 /* Setup movefile descriptor and make the call */ 00162 moveFile.FileHandle = hFile; 00163 moveFile.StartVcn.QuadPart = startVcn; 00164 moveFile.TargetLcn.QuadPart = targetLcn; 00165 #ifdef _WIN64 00166 moveFile.NumVcns = n_clusters; 00167 #else 00168 moveFile.NumVcns = (ULONG)n_clusters; 00169 #endif 00170 /* On NT 4.0 it can move no more than 256 kbytes once. */ 00171 status = NtFsControlFile(winx_fileno(fVolume),NULL,NULL,0,&ioStatus, 00172 FSCTL_MOVE_FILE, 00173 &moveFile,sizeof(MOVEFILE_DESCRIPTOR), 00174 NULL,0); 00175 00176 /* If the operation is pending, wait for it to finish */ 00177 if(NT_SUCCESS(status)/* == STATUS_PENDING*/){ 00178 /* FIXME: winx_fileno(fVolume) ??? */ 00179 NtWaitForSingleObject(winx_fileno(fVolume)/*hFile*/,FALSE,NULL); 00180 status = ioStatus.Status; 00181 } 00182 if(!NT_SUCCESS(status)) return status; 00183 return STATUS_SUCCESS; /* it means: the result is unknown */ 00184 } 00185 00186 /* ------------------------------------------------------------------------- */ 00187 /* The following 2 functions are drivers for MovePartOfFile(). */ 00188 /* They must update Stat.processed_clusters correctly. */ 00189 /* ------------------------------------------------------------------------- */ 00190 00200 NTSTATUS MoveBlocksOfFile(PFILENAME pfn,HANDLE hFile,ULONGLONG targetLcn) 00201 { 00202 PBLOCKMAP block; 00203 ULONGLONG curr_target; 00204 ULONGLONG j,n,r; 00205 NTSTATUS Status; 00206 ULONGLONG clusters_to_process; 00207 00208 clusters_to_process = pfn->clusters_total; 00209 curr_target = targetLcn; 00210 for(block = pfn->blockmap; block != NULL; block = block->next_ptr){ 00211 /* try to move current block */ 00212 n = block->length / clusters_per_256k; 00213 for(j = 0; j < n; j++){ 00214 Status = MovePartOfFile(hFile,block->vcn + j * clusters_per_256k, \ 00215 curr_target,clusters_per_256k); 00216 if(Status != STATUS_SUCCESS){ /* failure */ 00217 Stat.processed_clusters += clusters_to_process; 00218 return Status; 00219 } 00220 Stat.processed_clusters += clusters_per_256k; 00221 clusters_to_process -= clusters_per_256k; 00222 curr_target += clusters_per_256k; 00223 } 00224 /* try to move rest of block */ 00225 r = block->length % clusters_per_256k; 00226 if(r){ 00227 Status = MovePartOfFile(hFile,block->vcn + j * clusters_per_256k, \ 00228 curr_target,r); 00229 if(Status != STATUS_SUCCESS){ /* failure */ 00230 Stat.processed_clusters += clusters_to_process; 00231 return Status; 00232 } 00233 Stat.processed_clusters += r; 00234 clusters_to_process -= r; 00235 curr_target += r; 00236 } 00237 if(block->next_ptr == pfn->blockmap) break; 00238 } 00239 return STATUS_SUCCESS; 00240 } 00241 00254 void MovePartOfFileBlock(PFILENAME pfn,ULONGLONG startVcn, 00255 ULONGLONG targetLcn,ULONGLONG n_clusters) 00256 { 00257 HANDLE hFile; 00258 ULONGLONG target; 00259 ULONGLONG j,n,r; 00260 NTSTATUS Status; 00261 ULONGLONG clusters_to_process; 00262 00263 clusters_to_process = n_clusters; 00264 Status = OpenTheFile(pfn,&hFile); 00265 if(Status != STATUS_SUCCESS){ 00266 Stat.processed_clusters += clusters_to_process; 00267 return; 00268 } 00269 00270 target = targetLcn; 00271 n = n_clusters / clusters_per_256k; 00272 for(j = 0; j < n; j++){ 00273 Status = MovePartOfFile(hFile,startVcn + j * clusters_per_256k, \ 00274 target,clusters_per_256k); 00275 if(Status != STATUS_SUCCESS){ /* failure */ 00276 Stat.processed_clusters += clusters_to_process; 00277 NtClose(hFile); 00278 return; 00279 } 00280 Stat.processed_clusters += clusters_per_256k; 00281 clusters_to_process -= clusters_per_256k; 00282 target += clusters_per_256k; 00283 } 00284 r = n_clusters % clusters_per_256k; 00285 if(r){ 00286 Status = MovePartOfFile(hFile,startVcn + j * clusters_per_256k, \ 00287 target,r); 00288 if(Status != STATUS_SUCCESS){ /* failure */ 00289 Stat.processed_clusters += clusters_to_process; 00290 NtClose(hFile); 00291 return; 00292 } 00293 Stat.processed_clusters += r; 00294 clusters_to_process -= r; 00295 } 00296 00297 NtClose(hFile); 00298 } 00299 00304 void DbgPrintBlocksOfFile(PBLOCKMAP blockmap) 00305 { 00306 PBLOCKMAP block; 00307 00308 for(block = blockmap; block != NULL; block = block->next_ptr){ 00309 DebugPrint("VCN: %I64u, LCN: %I64u, LENGTH: %u\n", 00310 block->vcn,block->lcn,block->length); 00311 if(block->next_ptr == blockmap) break; 00312 } 00313 } 00314 00324 BOOLEAN MoveTheFile(PFILENAME pfn,ULONGLONG target) 00325 { 00326 NTSTATUS Status; 00327 HANDLE hFile; 00328 PBLOCKMAP block; 00329 FILENAME fn; 00330 00331 /* Open the file */ 00332 Status = OpenTheFile(pfn,&hFile); 00333 if(Status){ 00334 DebugPrint("Can't open %ws file: %x\n",pfn->name.Buffer,(UINT)Status); 00335 /* we need to destroy the block map to avoid infinite loops */ 00336 DeleteBlockmap(pfn); /* file is locked by other application, so its state is unknown */ 00337 return FALSE; 00338 } 00339 00340 DebugPrint("%ws\n",pfn->name.Buffer); 00341 DebugPrint("t: %I64u n: %I64u\n",target,pfn->clusters_total); 00342 00343 Status = MoveBlocksOfFile(pfn,hFile,target); 00344 NtClose(hFile); 00345 00346 if(Status == STATUS_SUCCESS){ /* we have undefined result */ 00347 /* Check new file blocks to get moving status. */ 00348 fn.name = pfn->name; 00349 if(DumpFile(&fn)){ /* otherwise let's assume the successful moving result? */ 00350 if(fn.is_fragm){ 00351 DebugPrint("MoveBlocksOfFile failed: " 00352 "still fragmented.\n"); 00353 DbgPrintBlocksOfFile(fn.blockmap); 00354 Status = STATUS_UNSUCCESSFUL; 00355 } 00356 if(fn.blockmap){ 00357 if(fn.blockmap->lcn != target){ 00358 DebugPrint("MoveBlocksOfFile failed: " 00359 "first block not found on target space.\n"); 00360 DbgPrintBlocksOfFile(fn.blockmap); 00361 Status = STATUS_UNSUCCESSFUL; 00362 } 00363 } 00364 DeleteBlockmap(&fn); 00365 } 00366 } 00367 00368 /* first of all: remove target space from the free space list */ 00369 if(Status == STATUS_SUCCESS){ 00370 Stat.fragmfilecounter --; 00371 Stat.fragmcounter -= (pfn->n_fragments - 1); 00372 pfn->is_fragm = FALSE; /* before GetSpaceState() call */ 00373 } 00374 RemarkBlock(target,pfn->clusters_total,GetFileSpaceState(pfn),FREE_SPACE); 00375 TruncateFreeSpaceBlock(target,pfn->clusters_total); 00376 00377 if(Status == STATUS_SUCCESS){ 00378 /* free previously allocated space (after TruncateFreeSpaceBlock() call!) */ 00379 for(block = pfn->blockmap; block != NULL; block = block->next_ptr){ 00380 ProcessFreedBlock(block->lcn,block->length,FRAGM_SPACE/*old_state*/); 00381 if(block->next_ptr == pfn->blockmap) break; 00382 } 00383 } else { 00384 DebugPrint("MoveFile error: %x\n",(UINT)Status); 00385 } 00386 DeleteBlockmap(pfn); /* because we don't need this info after file moving */ 00387 return (!pfn->is_fragm); 00388 } 00389