/* File: phbf.c Copyright (C) 1998-2009 Christophe GRENIER This software is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_TIME_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #include "types.h" #include "common.h" #include "intrf.h" #ifdef HAVE_NCURSES #include "intrfn.h" #endif #include #ifdef HAVE_WINDEF_H #include #endif #ifdef HAVE_WINBASE_H #include #include #endif #include "dir.h" #include "fat_dir.h" #include "list.h" #include "lang.h" #include "filegen.h" #include "photorec.h" #include "sessionp.h" #include "phrecn.h" #include "log.h" #include "log_part.h" #include "file_tar.h" #include "phcfg.h" #include "pblocksize.h" #include "pnext.h" #include "phbf.h" #include "phnc.h" //#define DEBUG_BF //#define DEBUG_BF2 #define READ_SIZE 1024*512 extern file_check_list_t file_check_list; extern uint64_t free_list_allocation_end; static int photorec_bf_aux(disk_t *disk_car, partition_t *partition, const char *recup_dir, const int interface, file_stat_t *file_stats, unsigned int *file_nbr, file_recovery_t *file_recovery, const unsigned int blocksize, alloc_data_t *list_search_space, alloc_data_t *current_search_space, const time_t real_start_time, unsigned int *dir_num, const photorec_status_t status, const int phase); static int photorec_bf_frag(disk_t *disk_car, partition_t *partition, const char *recup_dir, const int interface, file_stat_t *file_stats, unsigned int *file_nbr, file_recovery_t *file_recovery, const unsigned int blocksize, alloc_data_t *list_search_space, alloc_data_t *start_search_space, const time_t real_start_time, unsigned int *dir_num, const photorec_status_t status, const int phase, alloc_data_t **current_search_space, uint64_t *offset, unsigned char *buffer, unsigned char *block_buffer, const unsigned int frag); #ifdef DEBUG_BF static void list_space_used(const file_recovery_t *file_recovery, const unsigned int sector_size) { struct td_list_head *tmp; uint64_t file_size=0; if(file_recovery->filename==NULL) return; log_info("%s\t",file_recovery->filename); td_list_for_each(tmp, &file_recovery->location.list) { const alloc_list_t *element=td_list_entry(tmp, alloc_list_t, list); if(element->data>0) { log_info(" %lu-%lu", (unsigned long)(element->start/sector_size), (unsigned long)(element->end/sector_size)); file_size+=(element->end-element->start+1); } else { log_info(" (%lu-%lu)", (unsigned long)(element->start/sector_size), (unsigned long)(element->end/sector_size)); } } log_info("\n"); } #endif static inline void file_recovery_cpy(file_recovery_t *dst, file_recovery_t *src) { memcpy(dst, src, sizeof(*dst)); dst->location.list.prev=&dst->location.list; dst->location.list.next=&dst->location.list; } static inline void list_append_block(alloc_list_t *list, const uint64_t offset, const uint64_t blocksize, const unsigned int data) { if(!td_list_empty(&list->list)) { alloc_list_t *prev=td_list_entry(list->list.prev, alloc_list_t, list); if(prev->end+1==offset && prev->data==data) { prev->end=offset+blocksize-1; return ; } } { alloc_list_t *new_list=(alloc_list_t *)MALLOC(sizeof(*new_list)); new_list->start=offset; new_list->end=offset+blocksize-1; new_list->data=data; td_list_add_tail(&new_list->list, &list->list); } } static struct td_list_head *next_file(struct td_list_head *search_walker, alloc_data_t *list_search_space) { struct td_list_head *tmp_walker; for(tmp_walker=search_walker->next; tmp_walker!=&list_search_space->list; tmp_walker=tmp_walker->next) { alloc_data_t *tmp; tmp=td_list_entry(tmp_walker, alloc_data_t, list); if(tmp->file_stat!=NULL && tmp->file_stat->file_hint!=NULL) return tmp_walker; } return search_walker; } int photorec_bf(disk_t *disk_car, partition_t *partition, const int verbose, const char *recup_dir, const int interface, file_stat_t *file_stats, unsigned int *file_nbr, const unsigned int blocksize, alloc_data_t *list_search_space, const time_t real_start_time, unsigned int *dir_num, const photorec_status_t status, const unsigned int pass) { struct td_list_head *search_walker = NULL; struct td_list_head *p= NULL; unsigned char *buffer_start; const unsigned int read_size=(blocksize>65536?blocksize:65536); unsigned int buffer_size; int ind_stop=0; int pass2=pass; int phase; buffer_size=blocksize+READ_SIZE; buffer_start=(unsigned char *)MALLOC(buffer_size); for(phase=0; phase<2; phase++) { const unsigned int file_nbr_phase_old=*file_nbr; for(search_walker=list_search_space->list.prev, p=search_walker->prev; search_walker!=&list_search_space->list && ind_stop==0; p=search_walker->prev) { alloc_data_t *current_search_space; unsigned char *buffer; unsigned char *buffer_olddata; uint64_t offset; int need_to_check_file; int go_backward=1; file_recovery_t file_recovery; reset_file_recovery(&file_recovery); file_recovery.blocksize=blocksize; current_search_space=td_list_entry(search_walker, alloc_data_t, list); offset=current_search_space->start; buffer_olddata=buffer_start; buffer=buffer_olddata+blocksize; memset(buffer_olddata, 0, blocksize); disk_car->pread(disk_car, buffer, READ_SIZE, offset); info_list_search_space(list_search_space, current_search_space, disk_car->sector_size, 0, verbose); #ifdef DEBUG_BF #endif log_flush(); do { uint64_t old_offset=offset; need_to_check_file=0; if(offset==current_search_space->start) { struct td_list_head *tmpl; file_recovery_t file_recovery_new; file_recovery_new.blocksize=blocksize; file_recovery_new.file_stat=NULL; td_list_for_each(tmpl, &file_check_list.list) { struct td_list_head *tmp; const file_check_list_t *pos=td_list_entry(tmpl, file_check_list_t, list); td_list_for_each(tmp, &pos->file_checks[buffer[pos->offset]].list) { const file_check_t *file_check=td_list_entry(tmp, file_check_t, list); if((file_check->length==0 || memcmp(buffer + file_check->offset, file_check->value, file_check->length)==0) && file_check->header_check(buffer, read_size, 0, &file_recovery, &file_recovery_new)!=0) { file_recovery_new.file_stat=file_check->file_stat; break; } } if(file_recovery_new.file_stat!=NULL) break; } if(file_recovery_new.file_stat!=NULL) { file_recovery_new.location.start=offset; if(verbose>0) { log_info("%s header found at sector %lu\n", ((file_recovery_new.extension!=NULL && file_recovery_new.extension[0]!='\0')? file_recovery_new.extension:file_recovery_new.file_stat->file_hint->description), (unsigned long)((offset-partition->part_offset)/disk_car->sector_size)); } if(file_recovery.file_stat==NULL) { /* Header found => file found */ file_recovery_cpy(&file_recovery, &file_recovery_new); } else if(file_recovery_new.file_stat->file_hint!=NULL) { if(verbose>0) log_verbose("New file found => stop the recovery of current file\n"); need_to_check_file=1; } } else if(file_recovery.file_stat==NULL) need_to_check_file=1; /* No header found => no file => stop */ } if(file_recovery.file_stat!=NULL && file_recovery.handle==NULL) { /* Create new file */ set_filename(&file_recovery, recup_dir, *dir_num, disk_car, partition, 0); if(file_recovery.file_stat->file_hint->recover==1) { if(!(file_recovery.handle=fopen(file_recovery.filename,"w+b"))) { log_critical("Cannot create file %s: %s\n", file_recovery.filename, strerror(errno)); ind_stop=2; } } } if(need_to_check_file==0 && file_recovery.handle!=NULL) { if(file_recovery.handle!=NULL) { if(fwrite(buffer,blocksize,1,file_recovery.handle)<1) { log_critical("Cannot write to file %s:%s\n", file_recovery.filename, strerror(errno)); ind_stop=3; } } if(file_recovery.file_stat!=NULL) { int res=1; // log_info("add sector %llu\n", (long long unsigned)(offset/512)); list_append_block(&file_recovery.location, offset, blocksize,1); if(file_recovery.data_check!=NULL) res=file_recovery.data_check(buffer_olddata, 2*blocksize, &file_recovery); file_recovery.file_size+=blocksize; file_recovery.file_size_on_disk+=blocksize; if(res==2) { /* EOF found */ need_to_check_file=1; } } if(file_recovery.file_stat!=NULL && file_recovery.file_stat->file_hint->max_filesize>0 && file_recovery.file_size>=file_recovery.file_stat->file_hint->max_filesize) { log_verbose("File should not be bigger than %llu, stop adding data\n", (long long unsigned)file_recovery.file_stat->file_hint->max_filesize); need_to_check_file=1; } } get_next_sector(list_search_space, ¤t_search_space, &offset, blocksize); if(current_search_space==list_search_space) need_to_check_file=1; if(need_to_check_file==0) { buffer_olddata+=blocksize; buffer+=blocksize; if(old_offset+blocksize!=offset || buffer+read_size>buffer_start+buffer_size) { memcpy(buffer_start, buffer_olddata, blocksize); buffer_olddata=buffer_start; buffer=buffer_olddata+blocksize; if(verbose>1) { log_verbose("Reading sector %10llu/%llu\n", (unsigned long long)((offset-partition->part_offset)/disk_car->sector_size), (unsigned long long)((partition->part_size-1)/disk_car->sector_size)); } disk_car->pread(disk_car, buffer, READ_SIZE, offset); } } } while(need_to_check_file==0); if(need_to_check_file==1) { uint64_t offset_next_file=0; struct td_list_head *tmp_walker; const unsigned int file_nbr_old=*file_nbr; /* find the offset of the next file */ for(tmp_walker=search_walker; tmp_walker!=&list_search_space->list && offset_next_file==0; tmp_walker=tmp_walker->next) { alloc_data_t *tmp; tmp=td_list_entry(tmp_walker, alloc_data_t, list); if(tmp->file_stat!=NULL && tmp->file_stat->file_hint!=NULL) offset_next_file=tmp->start; } file_recovery.flags=1; if(file_finish(&file_recovery, recup_dir, 2, file_nbr, blocksize, list_search_space, ¤t_search_space, &offset, dir_num, status, disk_car)<0) { /* BF */ current_search_space=td_list_entry(search_walker, alloc_data_t, list); ind_stop=photorec_bf_aux(disk_car, partition, recup_dir, interface, file_stats, file_nbr, &file_recovery, blocksize, list_search_space, current_search_space, real_start_time, dir_num, status, phase); pass2++; if(file_nbr_old < *file_nbr && free_list_allocation_end > offset_next_file) go_backward=0; #ifdef DEBUG_BF log_info("file_nbr_old %u, file_nbr=%u\n", file_nbr_old, *file_nbr); log_info("free_list_allocation_end %llu, offset_next_file %llu\n", (long long unsigned)free_list_allocation_end, (long long unsigned)offset_next_file); #endif } } search_walker=p; if(go_backward==0) { #ifdef DEBUG_BF log_info("go_backward==0\n"); #endif search_walker=next_file(search_walker, list_search_space); } } log_info("phase=%d +%u\n", phase, *file_nbr - file_nbr_phase_old); } free(buffer_start); #ifdef HAVE_NCURSES photorec_info(stdscr, file_stats); #endif return ind_stop; } enum { BF_FILE_FOUND=0, BF_USER_STOP=1, BF_ERR_STOP=2, BF_FRAG_FOUND=3, BF_EOF=4, BF_NO_FILE=5, BF_TOO_FAR=6}; static int photorec_bf_pad(disk_t *disk_car, const char *recup_dir, unsigned int *file_nbr, file_recovery_t *file_recovery, const unsigned int blocksize, alloc_data_t *list_search_space, unsigned int *dir_num, const photorec_status_t status, const int phase, const uint64_t file_offset, alloc_data_t **current_search_space, uint64_t *offset, unsigned char *buffer, unsigned char *block_buffer) { { /* Add remaining data blocs */ unsigned int nbr; uint64_t offset_error_tmp; file_recovery->offset_error=file_offset; do { uint64_t file_size_backup; nbr=0; offset_error_tmp=file_recovery->offset_error; fseek(file_recovery->handle, file_recovery->file_size, SEEK_SET); #if 1 if(file_recovery->data_check!=NULL) { int stop=0; memset(buffer, 0, blocksize); while(*current_search_space != list_search_space && stop==0 && file_recovery->file_size < file_recovery->offset_error+1000*blocksize) { if( ((*current_search_space)->start!=*offset && phase!=1) || (*current_search_space)->file_stat==NULL || (*current_search_space)->file_stat->file_hint==NULL) { disk_car->pread(disk_car, block_buffer, blocksize, *offset); if(file_recovery->data_check(buffer, 2*blocksize, file_recovery)!=1) { stop=1; } if(fwrite(block_buffer, blocksize, 1, file_recovery->handle)<1) { log_critical("Cannot write to file %s:%s\n", file_recovery->filename, strerror(errno)); fclose(file_recovery->handle); file_recovery->handle=NULL; return BF_ERR_STOP; } list_append_block(&file_recovery->location, *offset, blocksize, 1); file_recovery->file_size+=blocksize; file_recovery->file_size_on_disk+=blocksize; nbr++; memcpy(buffer, block_buffer, blocksize); } get_next_sector(list_search_space, current_search_space, offset, blocksize); } } else #endif { while(*current_search_space != list_search_space && file_recovery->file_size < file_recovery->offset_error+100*blocksize) { if((*current_search_space)->start!=*offset || (*current_search_space)->file_stat==NULL || (*current_search_space)->file_stat->file_hint==NULL) { disk_car->pread(disk_car, block_buffer, blocksize, *offset); if(fwrite(block_buffer, blocksize, 1, file_recovery->handle)<1) { log_critical("Cannot write to file %s:%s\n", file_recovery->filename, strerror(errno)); fclose(file_recovery->handle); file_recovery->handle=NULL; return BF_ERR_STOP; } list_append_block(&file_recovery->location, *offset, blocksize, 1); file_recovery->file_size+=blocksize; nbr++; } get_next_sector(list_search_space, current_search_space, offset, blocksize); } } #ifdef DEBUG_BF log_trace("BF "); list_space_used(file_recovery, 512); #endif file_size_backup=file_recovery->file_size; file_recovery->flags=1; file_recovery->offset_error=0; file_recovery->offset_ok=0; file_recovery->calculated_file_size=0; file_recovery->file_check(file_recovery); file_recovery->file_size=file_size_backup; #ifdef DEBUG_BF log_trace("offset_error=%llu offset_error_tmp=%llu nbr=%u blocksize=%u\n", (long long unsigned) file_recovery->offset_error, (long long unsigned) offset_error_tmp, nbr, blocksize); #endif } while(file_recovery->offset_error > offset_error_tmp + nbr /2 * blocksize); } #ifdef DEBUG_BF log_info("photorec_bf_aux %s split file at %llu, error %llu\n", file_recovery->filename, (long long unsigned)file_offset, (long long unsigned)file_recovery->offset_error); #endif if(file_recovery->offset_error==0) { /* Recover the file */ #ifdef DEBUG_BF log_info("photorec_bf_aux, call file_finish\n"); #endif file_finish(file_recovery, recup_dir, 2, file_nbr, blocksize, list_search_space, current_search_space, offset, dir_num, status, disk_car); return BF_FILE_FOUND; } /* FIXME +4096 => +blocksize*/ /* 21/11/2009: 2 blocksize */ else if(file_recovery->offset_error/blocksize*blocksize >= (file_offset / blocksize * blocksize + 2 * blocksize)) { /* Try to recover file composed of multiple fragments */ #ifdef DEBUG_BF log_info("%s multiple fragment %llu -> %llu, blocksize %u\n", file_recovery->filename, (unsigned long long)file_offset, (unsigned long long)file_recovery->offset_error, blocksize); list_space_used(file_recovery, 512); #endif return BF_FRAG_FOUND; } return BF_NO_FILE; } static int photorec_bf_frag_fast(disk_t *disk_car, partition_t *partition, const char *recup_dir, const int interface, file_stat_t *file_stats, unsigned int *file_nbr, file_recovery_t *file_recovery, const unsigned int blocksize, alloc_data_t *list_search_space, alloc_data_t *start_search_space, const time_t real_start_time, unsigned int *dir_num, const photorec_status_t status, const int phase, alloc_data_t **current_search_space, uint64_t *offset, unsigned char *buffer, unsigned char *block_buffer, const unsigned int frag) { const uint64_t original_offset_error=file_recovery->offset_error; const uint64_t original_offset_ok=file_recovery->offset_ok; const int blocs_to_skip=file_recovery->extra / blocksize; unsigned int i; log_info("photorec_bf_frag_fast %s, original_offset_ok=%llu, original_offset_error=%llu, blocs_to_skip=%d, extra=%llu\n", file_recovery->filename, (long long unsigned)original_offset_ok, (long long unsigned)original_offset_error, blocs_to_skip, (long long unsigned)file_recovery->extra); #ifdef DEBUG_BF log_info("Frag %u\n", frag); #endif /* * offset_ok=0 * offset_error=2 * blocksize=3 * 0 1 2 3 4 5 6 7 * 0 1 5 6 7 * 0 2 5 6 7 * 0 3 5 6 7 * 0 4 5 6 7 * */ file_recovery->extra=0; for(i=0; istart; file_recovery->checkpoint_status=0; file_recovery->checkpoint_offset=original_offset_ok/blocksize*blocksize; file_recovery->calculated_file_size=0; file_recovery->file_size=0; file_recovery->file_size_on_disk=0; for(file_recovery->file_size = 0; file_recovery->file_size <= original_offset_ok/blocksize*blocksize; ) { /* FIXME: Handle ext2/ext3 */ if(file_recovery->data_check!=NULL) { disk_car->pread(disk_car, block_buffer, blocksize, *offset); file_recovery->data_check(buffer, 2*blocksize, file_recovery); memcpy(buffer, block_buffer, blocksize); } file_recovery->file_size+=blocksize; file_recovery->file_size_on_disk+=blocksize; get_next_sector(list_search_space, current_search_space, offset, blocksize); } list_truncate(&file_recovery->location, file_recovery->file_size); for(j=0; jpread(disk_car, block_buffer, blocksize, *offset); if(file_recovery->data_check(buffer, 2*blocksize, file_recovery)!=1) { /* TODO handle this problem */ } if(fwrite(block_buffer, blocksize, 1, file_recovery->handle)<1) { log_critical("Cannot write to file %s:%s\n", file_recovery->filename, strerror(errno)); fclose(file_recovery->handle); file_recovery->handle=NULL; return BF_ERR_STOP; } list_append_block(&file_recovery->location, *offset, blocksize, 1); file_recovery->file_size+=blocksize; file_recovery->file_size_on_disk+=blocksize; memcpy(buffer, block_buffer, blocksize); get_next_sector(list_search_space, current_search_space, offset, blocksize); } for(; joffset_error, current_search_space, offset, buffer, block_buffer); if(res==BF_FRAG_FOUND) { if(frag>5) return BF_NO_FILE; res=photorec_bf_frag(disk_car, partition, recup_dir, interface, file_stats, file_nbr, file_recovery, blocksize, list_search_space, start_search_space, real_start_time, dir_num, status, phase, current_search_space, offset, buffer, block_buffer, frag+1); } if(res==BF_FILE_FOUND || res==BF_USER_STOP || res==BF_ERR_STOP) return res; if(res==BF_EOF || res==BF_TOO_FAR) return BF_NO_FILE; } return BF_NO_FILE; } static int photorec_bf_frag(disk_t *disk_car, partition_t *partition, const char *recup_dir, const int interface, file_stat_t *file_stats, unsigned int *file_nbr, file_recovery_t *file_recovery, const unsigned int blocksize, alloc_data_t *list_search_space, alloc_data_t *start_search_space, const time_t real_start_time, unsigned int *dir_num, const photorec_status_t status, const int phase, alloc_data_t **current_search_space, uint64_t *offset, unsigned char *buffer, unsigned char *block_buffer, const unsigned int frag) { uint64_t file_offset; const uint64_t original_offset_error=file_recovery->offset_error; int testbf=0; #if 1 if(file_recovery->extra > 0 && file_recovery->offset_error / blocksize > file_recovery->offset_ok / blocksize && file_recovery->offset_ok > 0) { int res=photorec_bf_frag_fast(disk_car, partition, recup_dir, interface, file_stats, file_nbr, file_recovery, blocksize, list_search_space, start_search_space, real_start_time, dir_num, status, phase, current_search_space, offset, buffer, block_buffer, frag); // if(res==BF_TOO_FAR) // res=photorec_bf_frag(disk_car, partition, recup_dir, interface, file_stats, file_nbr, file_recovery, blocksize, list_search_space, start_search_space, real_start_time, dir_num, status, phase, current_search_space, offset, buffer, block_buffer, frag); if(res!=BF_NO_FILE) return res; } #endif log_info("photorec_bf_frag %s, original_offset_ok=%llu, original_offset_error=%llu, blocs_to_skip=%llu\n", file_recovery->filename, (long long unsigned)file_recovery->offset_ok, (long long unsigned)file_recovery->offset_error, (long long unsigned)file_recovery->extra); #ifdef DEBUG_BF log_info("Frag %u\n", frag); #endif for(file_offset=original_offset_error/blocksize*blocksize; file_offset >= blocksize && (original_offset_error <= file_offset+6*512 || original_offset_error < file_offset+2*blocksize); file_offset -= blocksize) { alloc_data_t *extractblock_search_space; uint64_t extrablock_offset; int blocs_to_skip; file_recovery_t file_recovery_backup; file_recovery->checkpoint_status=0; file_recovery->checkpoint_offset = file_offset; file_recovery->calculated_file_size=0; if(file_recovery->data_check!=NULL) { *current_search_space=start_search_space; *offset=start_search_space->start; for(file_recovery->file_size = 0; file_recovery->file_size < file_offset; file_recovery->file_size += blocksize) { disk_car->pread(disk_car, block_buffer, blocksize, *offset); /* FIXME: Handle ext2/ext3 */ file_recovery->data_check(buffer, 2*blocksize, file_recovery); memcpy(buffer, block_buffer, blocksize); get_next_sector(list_search_space, current_search_space, offset, blocksize); } } list_truncate(&file_recovery->location, file_offset); file_recovery->file_size=file_offset; file_recovery->file_size_on_disk=file_recovery->file_size; /* Set extractblock_search_space & extrablock_offset to the begining of the potential extra block */ /* FIXME */ #ifdef DEBUG_BF log_debug("Set extractblock_search_space & extrablock_offset to the begining of the potential extra block\n"); log_info("photorec_bf_aux %s split file at %llu\n", file_recovery->filename, (long long unsigned)file_offset); // list_space_used(file_recovery, 512); #endif /* Get the last block added to the file */ extrablock_offset=0; if(!td_list_empty(&file_recovery->location.list)) { const alloc_list_t *element=td_list_entry(file_recovery->location.list.prev, alloc_list_t, list); extrablock_offset=element->end/blocksize*blocksize; } /* Get the corresponding search_place */ extractblock_search_space=td_list_entry(list_search_space->list.next, alloc_data_t, list); while(extractblock_search_space != list_search_space && !(extractblock_search_space->start <= extrablock_offset && extrablock_offset <= extractblock_search_space->end)) extractblock_search_space=td_list_entry(extractblock_search_space->list.next, alloc_data_t, list); /* Update extractblock_search_space & extrablock_offset */ get_next_sector(list_search_space, &extractblock_search_space, &extrablock_offset, blocksize); /* */ file_recovery->offset_error=0; #ifdef DEBUG_BF log_info("extrablock_offset=%llu sectors\n", (long long unsigned)(extrablock_offset/512)); #endif memcpy(&file_recovery_backup, file_recovery, sizeof(file_recovery_backup)); { /* FIXME 16 100 250 */ for(blocs_to_skip=-2; blocs_to_skip<5000 && (file_recovery->offset_error==0 || (phase==0 && (file_recovery->offset_error >= file_offset || blocs_to_skip<16)) || (phase==1 && (file_recovery->offset_error + blocksize >= file_offset || blocs_to_skip<100)) || (phase==2 && blocs_to_skip<10) ); blocs_to_skip++,testbf++) { memcpy(file_recovery, &file_recovery_backup, sizeof(file_recovery_backup)); *current_search_space=extractblock_search_space; *offset=extrablock_offset; #ifdef DEBUG_BF log_info("photorec_bf_aux %s split file at %llu, skip=%u\n", file_recovery->filename, (long long unsigned)file_offset, blocs_to_skip); #endif file_recovery->offset_error=0; list_truncate(&file_recovery->location,file_offset); if( interface != 0) { static time_t previous_time=0; time_t current_time; current_time=time(NULL); if(current_time>previous_time) { int ind_stop=0; previous_time=current_time; #ifdef HAVE_NCURSES ind_stop=photorec_progressbar(stdscr, testbf, status, file_recovery->location.start, disk_car, partition, *file_nbr, (current_time > real_start_time ? current_time - real_start_time: 0), file_stats); #endif if(ind_stop!=0) { file_recovery->flags=0; file_finish(file_recovery, recup_dir, 2, file_nbr, blocksize, list_search_space, current_search_space, offset, dir_num, status, disk_car); log_info("photorec_bf_aux, user choose to stop\n"); return BF_USER_STOP; } } } /* Skip extra blocs */ #ifdef DEBUG_BF log_debug("Skip %u extra blocs\n", blocs_to_skip); #endif // log_info("%s Skip %u extra blocs\n", file_recovery->filename, blocs_to_skip); if(blocs_to_skip < 0) { int i; for(i=0; i< 2+blocs_to_skip; i++) { get_next_header(list_search_space, current_search_space, offset); } } else { int i; for(i=0; i5) return BF_NO_FILE; switch(photorec_bf_frag(disk_car, partition, recup_dir, interface, file_stats, file_nbr, file_recovery, blocksize, list_search_space, start_search_space, real_start_time, dir_num, status, phase, current_search_space, offset, buffer, block_buffer, frag+1)) { case BF_FILE_FOUND: return BF_FILE_FOUND; case BF_USER_STOP: return BF_USER_STOP; case BF_ERR_STOP: return BF_ERR_STOP; case BF_TOO_FAR: return BF_NO_FILE; case BF_NO_FILE: #if 0 /* TODO: Continue to iterate blocs_to_skip */ if(file_recovery->offset_error/blocksize*blocksize >= (file_offset / blocksize * blocksize + 30 * blocksize)) return BF_NO_FILE; break; #else return BF_NO_FILE; #endif } break; case BF_EOF: return BF_NO_FILE; } } if(file_recovery->offset_error > 0 && file_recovery->offset_error < file_offset) return BF_TOO_FAR; } #ifdef DEBUG_BF log_info("blocs_to_skip=%u offset_error=0x%llx file_offset=0x%llx\n", blocs_to_skip, (long long unsigned)file_recovery->offset_error, (long long unsigned)file_offset); #endif } return BF_NO_FILE; } static int photorec_bf_aux(disk_t *disk_car, partition_t *partition, const char *recup_dir, const int interface, file_stat_t *file_stats, unsigned int *file_nbr, file_recovery_t *file_recovery, const unsigned int blocksize, alloc_data_t *list_search_space, alloc_data_t *start_search_space, const time_t real_start_time, unsigned int *dir_num, const photorec_status_t status, const int phase) { uint64_t offset; unsigned char *buffer; unsigned char *block_buffer; int ind_stop; alloc_data_t *current_search_space; //Init. of the brute force file_recovery->handle=fopen(file_recovery->filename, "w+b"); if(file_recovery->handle==NULL) { log_critical("Brute Force : Cannot create file %s: %s\n", file_recovery->filename, strerror(errno)); return BF_ERR_STOP; } buffer=(unsigned char *) MALLOC(2*blocksize); block_buffer=&buffer[blocksize]; current_search_space=start_search_space; /* We have offset==start_search_space->start==file_recovery->location.start */ offset=start_search_space->start; #ifdef DEBUG_BF2 log_trace("photorec_bf_aux location.start=%llu, ok at %llu, bad at %llu\n", (long long unsigned)(file_recovery->location.start/disk_car->sector_size), (long long unsigned)file_recovery->offset_ok, (long long unsigned)file_recovery->offset_error); #endif file_recovery->blocksize=blocksize; // Writing the file until the error location for(file_recovery->file_size = 0; file_recovery->file_size + blocksize -1 < file_recovery->offset_error; file_recovery->file_size += blocksize) { disk_car->pread(disk_car, block_buffer, blocksize, offset); /* FIXME: Handle ext2/ext3 */ if(fwrite(block_buffer, blocksize, 1, file_recovery->handle)<1) { log_critical("Cannot write to file %s:%s\n", file_recovery->filename, strerror(errno)); fclose(file_recovery->handle); file_recovery->handle=NULL; free(buffer); return BF_ERR_STOP; } list_append_block(&file_recovery->location, offset, blocksize, 1); get_next_sector(list_search_space, ¤t_search_space, &offset, blocksize); } file_recovery->file_size_on_disk=file_recovery->file_size; file_recovery->offset_error=file_recovery->file_size; #ifdef DEBUG_BF log_trace("BF Amorce "); list_space_used(file_recovery, 512); log_trace("\n"); #endif ind_stop=photorec_bf_frag(disk_car, partition, recup_dir, interface, file_stats, file_nbr, file_recovery, blocksize, list_search_space, start_search_space, real_start_time, dir_num, status, phase, ¤t_search_space, &offset, buffer, block_buffer, 0); /* Cleanup */ file_finish(file_recovery,recup_dir,2, file_nbr,blocksize,list_search_space,¤t_search_space, &offset, dir_num,status,disk_car); free(buffer); if(ind_stop==BF_TOO_FAR) { // return photorec_bf_aux(disk_car, partition, recup_dir, interface, file_stats, file_nbr, file_recovery, blocksize, list_search_space, start_search_space, real_start_time, dir_num, status, phase); return BF_FILE_FOUND; } if(ind_stop==BF_NO_FILE) return BF_FILE_FOUND; return ind_stop; }