33 #include "udefrag-internals.h"
35 static ULONGLONG defrag_cc_routine(udefrag_job_parameters *jp);
49 #ifdef TEST_SPECIAL_FILES_DEFRAG
50 void test_move(winx_file_info *f,udefrag_job_parameters *jp)
52 winx_volume_region *target_rgn;
53 ULONGLONG source_lcn = f->disp.blockmap->lcn;
56 target_rgn = find_last_free_region(jp,1);
57 if(target_rgn == NULL){
58 etrace(
"no free region found on disk");
61 if(move_file(f,f->disp.blockmap->vcn,1,target_rgn->lcn,jp) < 0){
62 etrace(
"move failed for %ws",f->path);
65 dtrace(
"move succeeded for %ws",f->path);
69 if(move_file(f,f->disp.blockmap->vcn,1,source_lcn,jp) < 0){
70 etrace(
"move failed for %ws",f->path);
73 dtrace(
"move succeeded for %ws",f->path);
76 etrace(
"file became unmovable %ws",f->path);
79 release_temp_space_regions(jp);
86 void test_special_files_defrag(udefrag_job_parameters *jp)
91 dtrace(
"test of special files defragmentation started");
93 for(f = jp->filelist; f; f = f->next){
96 if(is_reparse_point(f)){
97 dtrace(
"reparse point detected: %ws",f->path);
99 }
else if(is_encrypted(f)){
100 dtrace(
"encrypted file detected: %ws",f->path);
102 }
else if(winx_wcsistr(f->path,L
"$BITMAP")){
103 dtrace(
"bitmap detected: %ws",f->path);
105 }
else if(winx_wcsistr(f->path,L
"$ATTRIBUTE_LIST")){
106 dtrace(
"attribute list detected: %ws",f->path);
112 if(f->next == jp->filelist)
break;
115 dtrace(
"test of special files defragmentation completed");
127 static int can_defragment(winx_file_info *f,udefrag_job_parameters *jp)
133 if(!is_fragmented(f))
141 if(jp->is_fat && is_directory(f))
145 if(jp->job_type == MFT_OPTIMIZATION_JOB \
146 && !is_fragmented_by_file_opt(f))
156 static winx_blockmap *add_fragment(winx_blockmap **fragments,
157 winx_blockmap **prev_fragment, ULONGLONG vcn, ULONGLONG lcn,
160 winx_blockmap *fragment;
162 fragment = (winx_blockmap *)winx_list_insert((list_entry **)(
void *)fragments,
163 (list_entry *)*prev_fragment,
sizeof(winx_blockmap));
166 fragment->length = length;
167 *prev_fragment = fragment;
175 winx_blockmap *build_fragments_list(winx_file_info *f,ULONGLONG *n_fragments)
177 winx_blockmap *block, *p = NULL, *fragments = NULL;
178 ULONGLONG vcn = 0, lcn = 0, length = 0;
180 if(n_fragments) *n_fragments = 0;
182 for(block = f->disp.blockmap; block; block = block->next){
183 if(block == f->disp.blockmap){
186 length = block->length;
188 if(block->lcn == block->prev->lcn + block->prev->length){
189 length += block->length;
192 if(!add_fragment(&fragments,&p,vcn,lcn,length))
194 if(n_fragments) (*n_fragments) ++;
198 length = block->length;
201 if(block->next == f->disp.blockmap)
break;
205 if(add_fragment(&fragments,&p,vcn,lcn,length)){
206 if(n_fragments) (*n_fragments) ++;
210 if(fragments == NULL && n_fragments) *n_fragments = 0;
218 void release_fragments_list(winx_blockmap **fragments)
220 winx_list_destroy((list_entry **)(
void *)fragments);
227 void clear_currently_excluded_flag(udefrag_job_parameters *jp)
231 for(f = jp->filelist; f; f = f->next){
232 f->user_defined_flags &= ~UD_FILE_CURRENTLY_EXCLUDED;
233 if(f->next == jp->filelist)
break;
242 static ULONGLONG defrag_cc_routine(udefrag_job_parameters *jp)
244 struct prb_traverser t;
245 winx_file_info *file;
249 prb_t_init(&t,jp->fragmented_files);
250 file = prb_t_first(&t,jp->fragmented_files);
252 if(jp->termination_router((
void *)jp))
break;
254 if(can_defragment(file,jp)) n += file->disp.clusters;
255 file = prb_t_next(&t);
265 static void defrag_routine(udefrag_job_parameters *jp)
267 winx_volume_region *rgn, *largest_rgn;
268 struct prb_traverser t;
269 winx_file_info *file, *next_file;
271 ULONGLONG defragmented_files;
272 ULONGLONG defragmented_entirely = 0, defragmented_partially = 0;
273 ULONGLONG x, moved_entirely = 0, moved_partially = 0;
274 ULONGLONG min_vcn, max_vcn;
275 winx_blockmap *fragments, *fr, *fr2, *next_fr, *head_fr;
276 ULONGLONG vcn, length, n, new_min_vcn;
277 ULONGLONG cut_length;
278 int defrag_succeeded;
281 winx_dbg_print_header(0,0,I
"defragmentation pass #%u",++jp->pi.pass_number);
282 jp->pi.current_operation = VOLUME_DEFRAGMENTATION;
283 jp->pi.moved_clusters = 0;
286 release_temp_space_regions(jp);
289 clear_currently_excluded_flag(jp);
291 jp->pi.clusters_to_process = \
292 jp->pi.processed_clusters + defrag_cc_routine(jp);
303 defragmented_files = 0;
304 prb_t_init(&t,jp->fragmented_files);
305 file = prb_t_first(&t,jp->fragmented_files);
307 if(jp->termination_router((
void *)jp))
break;
308 next_file = prb_t_next(&t);
309 if(can_defragment(file,jp)){
311 if(file->disp.clusters * jp->v_info.bytes_per_cluster \
312 < 2 * jp->udo.fragment_size_threshold) move_entirely = 1;
313 else if(jp->win_version < WINDOWS_XP && jp->fs_type == FS_NTFS)
317 rgn = find_first_free_region(jp,0,file->disp.clusters,NULL);
319 x = jp->pi.moved_clusters;
320 if(move_file(file,file->disp.blockmap->vcn,
321 file->disp.clusters,rgn->lcn,jp) >= 0){
322 if(jp->udo.dbgprint_level >= DBG_DETAILED)
323 itrace(
"Defrag success for %ws",file->path);
324 defragmented_files ++;
325 defragmented_entirely ++;
326 moved_entirely += (jp->pi.moved_clusters - x);
328 etrace(
"Defrag failure for %ws",file->path);
333 min_vcn = file->disp.blockmap->vcn;
334 max_vcn = file->disp.blockmap->prev->vcn + \
335 file->disp.blockmap->prev->length;
336 defrag_succeeded = 0;
337 x = jp->pi.moved_clusters;
338 while(min_vcn < max_vcn && can_defragment(file,jp)){
340 fragments = build_fragments_list(file,NULL);
341 if(fragments == NULL)
break;
344 for(fr = fragments; fr; fr = next_fr){
347 if(fr->vcn < min_vcn || (fr->vcn + fr->length > max_vcn))
348 winx_list_remove((list_entry **)(
void *)&fragments,(list_entry *)(
void *)fr);
349 if(fragments == NULL)
goto completed;
350 if(next_fr == head_fr)
break;
354 largest_rgn = find_largest_free_region(jp);
355 if(largest_rgn == NULL)
break;
358 vcn = length = n = new_min_vcn = 0;
359 for(fr = fragments; fr; fr = fr->next){
361 if(fr->length * jp->v_info.bytes_per_cluster < jp->udo.fragment_size_threshold){
362 if(fr->length >= largest_rgn->length)
break;
364 length = fr->length, n++;
365 new_min_vcn = fr->vcn + fr->length;
367 for(fr2 = fr->next; fr2 != fragments; fr2 = fr2->next){
368 if(fr2->length * jp->v_info.bytes_per_cluster >= jp->udo.fragment_size_threshold)
370 if(length + fr2->length > largest_rgn->length)
goto move_clusters;
371 length += fr2->length, n++;
372 new_min_vcn = fr2->vcn + fr2->length;
374 if(largest_rgn->length * jp->v_info.bytes_per_cluster < jp->udo.fragment_size_threshold)
break;
375 if(length * jp->v_info.bytes_per_cluster < jp->udo.fragment_size_threshold){
376 cut_length = jp->udo.fragment_size_threshold / jp->v_info.bytes_per_cluster;
377 if(cut_length * jp->v_info.bytes_per_cluster != jp->udo.fragment_size_threshold)
379 cut_length -= length;
380 if(fr2 != fragments){
382 if((fr2->length - cut_length) * jp->v_info.bytes_per_cluster \
383 < jp->udo.fragment_size_threshold){
384 length += fr2->length, n++;
385 new_min_vcn = fr2->vcn + fr2->length;
387 length += cut_length, n++;
388 new_min_vcn = fr2->vcn + cut_length;
390 }
else if(fr != fragments){
392 if((fr->prev->length - cut_length) * jp->v_info.bytes_per_cluster \
393 < jp->udo.fragment_size_threshold){
395 length += fr->prev->length, n++;
397 vcn = fr->prev->vcn + (fr->prev->length - cut_length);
398 length += cut_length, n++;
404 if(fr->next == fragments)
break;
409 if(length == 0 || n < 2){
412 rgn = find_first_free_region(jp,0,length,NULL);
414 if(move_file(file,vcn,length,rgn->lcn,jp) >= 0){
415 if(jp->udo.dbgprint_level >= DBG_DETAILED)
416 itrace(
"Defrag success for %ws",file->path);
417 defrag_succeeded = 1;
419 etrace(
"Defrag failure for %ws",file->path);
422 min_vcn = new_min_vcn;
426 release_fragments_list(&fragments);
428 if(defrag_succeeded){
429 defragmented_files ++;
430 defragmented_partially ++;
431 moved_partially += (jp->pi.moved_clusters - x);
436 file->user_defined_flags |= UD_FILE_CURRENTLY_EXCLUDED;
446 itrace(
"%I64u files defragmented",defragmented_files);
447 itrace(
" %I64u clusters moved",jp->pi.moved_clusters);
448 winx_bytes_to_hr(jp->pi.moved_clusters * jp->v_info.bytes_per_cluster,1,buffer,
sizeof(buffer));
449 itrace(
" %s moved",buffer);
451 itrace(
"%I64u files defragmented entirely",defragmented_entirely);
452 itrace(
" %I64u clusters moved",moved_entirely);
453 winx_bytes_to_hr(moved_entirely * jp->v_info.bytes_per_cluster,1,buffer,
sizeof(buffer));
454 itrace(
" %s moved",buffer);
455 itrace(
"%I64u files defragmented partially",defragmented_partially);
456 itrace(
" %I64u clusters moved",moved_partially);
457 winx_bytes_to_hr(moved_partially * jp->v_info.bytes_per_cluster,1,buffer,
sizeof(buffer));
458 itrace(
" %s moved",buffer);
461 clear_currently_excluded_flag(jp);
468 static void defrag_sequence(udefrag_job_parameters *jp)
471 if(jp->pi.fragmented == 0)
return;
472 if(jp->termination_router((
void *)jp))
return;
474 }
while(jp->pi.moved_clusters);
477 if(jp->udo.fragment_size_threshold == DEFAULT_FRAGMENT_SIZE_THRESHOLD){
478 jp->udo.fragment_size_threshold = PART_DEFRAG_MAGIC_CONSTANT;
479 jp->udo.algorithm_defined_fst = 1;
480 itrace(
"partial defragmentation: fragment size threshold = %I64u",
481 jp->udo.fragment_size_threshold);
483 if(jp->pi.fragmented == 0)
break;
484 if(jp->termination_router((
void *)jp))
break;
486 }
while(jp->pi.moved_clusters);
487 jp->udo.fragment_size_threshold = DEFAULT_FRAGMENT_SIZE_THRESHOLD;
488 jp->udo.algorithm_defined_fst = 0;
505 int defragment(udefrag_job_parameters *jp)
508 struct prb_traverser t;
509 winx_file_info *file;
510 int second_attempt = 0;
513 if(jp->job_type == DEFRAGMENTATION_JOB){
515 result = analyze(jp);
516 if(result < 0)
return result;
517 if(jp->termination_router((
void *)jp))
return 0;
518 #ifdef TEST_SPECIAL_FILES_DEFRAG
519 test_special_files_defrag(jp);
523 if(!check_fragmentation_level(jp))
526 jp->pi.processed_clusters = 0;
527 jp->pi.clusters_to_process = 0;
530 time = start_timing(
"defragmentation",jp);
540 prb_t_init(&t,jp->fragmented_files);
541 file = prb_t_first(&t,jp->fragmented_files);
543 if(jp->termination_router((
void *)jp))
break;
544 if(is_moving_failed(file)){
546 file->user_defined_flags &= ~UD_FILE_MOVING_FAILED;
548 file = prb_t_next(&t);
550 if(second_attempt) defrag_sequence(jp);
552 stop_timing(
"defragmentation",time,jp);