/* File: phrecn.c Copyright (C) 1998-2007 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 #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_UNISTD_H #include /* unlink, ftruncate */ #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_TIME_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #include /* tolower */ #include "types.h" #include "common.h" #include "intrf.h" #include "intrfn.h" #include "fnctdsk.h" #include "dir.h" #include "fat_dir.h" #include "list.h" #include "chgtype.h" #include "lang.h" #include "filegen.h" #include "photorec.h" #include "sessionp.h" #include "phrecn.h" #include "partauto.h" #include "log.h" /* #define DEBUG */ /* #define DEBUG_GET_NEXT_SECTOR */ /* #define DEBUG_BF */ #define READ_SIZE 256*512 #define INTER_MENU_DISK 10 extern const file_hint_t file_hint_tar; extern const file_hint_t file_hint_dir; extern const file_hint_t file_hint_txt; extern file_check_t *file_check_list; static void recovery_finished(const unsigned int file_nbr, const char *recup_dir, const int ind_stop, char **current_cmd); static int photorec_progressbar(WINDOW *window, const unsigned int pass, const photorec_status_t status, const uint64_t offset, disk_t *disk_car, partition_t *partition, const unsigned int file_nbr, const time_t elapsed_time, const file_stat_t *file_stats); static int ask_mode_ext2(const disk_t *disk_car, const partition_t *partition, unsigned int *mode_ext2, unsigned int *carve_free_space_only); static int photorec_bf(disk_t *disk_car, partition_t *partition, const int debug, const int paranoid, const char *recup_dir, const int interface, file_stat_t *file_stats, unsigned int *file_nbr, 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, const unsigned int expert, const unsigned int lowmem); static int photorec_aux(disk_t *disk_car, partition_t *partition, const int debug, const int paranoid, const char *recup_dir, const int interface, file_stat_t *file_stats, unsigned int *file_nbr, 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, const unsigned int expert, const unsigned int lowmem); static int photorec(disk_t *disk_car, partition_t *partition, const int debug, const int paranoid, const char *recup_dir, const int keep_corrupted_file, const int interface, file_enable_t *file_enable, const unsigned int mode_ext2, char **current_cmd, alloc_data_t *list_search_space, unsigned int blocksize, const unsigned int expert, const unsigned int lowmem, const unsigned int carve_free_space_only); static void interface_options_photorec(int *paranoid, int *allow_partial_last_cylinder, int *keep_corrupted_file, unsigned int *mode_ext2, unsigned int *expert, unsigned int *lowmem, char**current_cmd); static int photorec_bf_aux(disk_t *disk_car, partition_t *partition, const int paranoid, const char *recup_dir, const int interface, file_stat_t *file_stats, unsigned int *file_nbr, file_recovery_t *file_recovery, 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 unsigned int pass); static inline int ind_block(const unsigned char *buffer, const unsigned int blocksize); static void interface_file_select(file_enable_t *files_enable, char**current_cmd); static #ifndef DEBUG_GET_NEXT_SECTOR inline #endif void get_next_sector(alloc_data_t *list_search_space, alloc_data_t **current_search_space, uint64_t *offset, const unsigned int blocksize); static #ifndef DEBUG_GET_NEXT_SECTOR inline #endif void get_next_header(alloc_data_t *list_search_space, alloc_data_t **current_search_space, uint64_t *offset); static void photorec_info(WINDOW *window, const file_stat_t *file_stats) { unsigned int i; unsigned int nbr; unsigned int others=0; file_stat_t *new_file_stats; for(i=0;file_stats[i].file_hint!=NULL;i++); nbr=i; if(nbr==0) return ; new_file_stats=(file_stat_t*)MALLOC(nbr*sizeof(file_stat_t)); memcpy(new_file_stats, file_stats, nbr*sizeof(file_stat_t)); qsort(new_file_stats, nbr, sizeof(file_stat_t), sorfile_stat_ts); for(i=0;i0;i++) { if(i<10) { wmove(window,11+i,0); wclrtoeol(window); wdoprintf(window, "%s: %u recovered\n", (new_file_stats[i].file_hint->extension!=NULL? new_file_stats[i].file_hint->extension:""), new_file_stats[i].recovered); } else others+=new_file_stats[i].recovered; } if(others>0) { wmove(window,11+10,0); wclrtoeol(window); wdoprintf(window, "others: %u recovered\n", others); } free(new_file_stats); } static int photorec_progressbar(WINDOW *window, const unsigned int pass, const photorec_status_t status, const uint64_t offset, disk_t *disk_car, partition_t *partition, const unsigned int file_nbr, const time_t elapsed_time, const file_stat_t *file_stats) { wmove(window,9,0); wclrtoeol(window); if(status==STATUS_EXT2_ON_BF || status==STATUS_EXT2_OFF_BF) { wdoprintf(window,"Bruteforce %10lu sectors remaining (test %u), %u files found\n", (unsigned long)((offset-partition->part_offset)/disk_car->sector_size), pass, file_nbr); } else { wdoprintf(window, "Pass %u - ", pass); if(status==STATUS_FIND_OFFSET) wdoprintf(window,"Reading sector %10lu/%lu, %u/10 headers found\n", (unsigned long)((offset-partition->part_offset)/disk_car->sector_size), (unsigned long)(partition->part_size/disk_car->sector_size), file_nbr); else wdoprintf(window,"Reading sector %10lu/%lu, %u files found\n", (unsigned long)((offset-partition->part_offset)/disk_car->sector_size), (unsigned long)(partition->part_size/disk_car->sector_size), file_nbr); } wmove(window,10,0); wclrtoeol(window); wdoprintf(window,"Elapsed time %uh%02um%02us", (unsigned)(elapsed_time/60/60), (unsigned)(elapsed_time/60%60), (unsigned)(elapsed_time%60)); if(offset-partition->part_offset!=0 && (status!=STATUS_EXT2_ON_BF && status!=STATUS_EXT2_OFF_BF)) { wdoprintf(window," - Estimated time for achievement %uh%02um%02u\n", (unsigned)((partition->part_offset+partition->part_size-1-offset)*elapsed_time/(offset-partition->part_offset)/3600), (unsigned)(((partition->part_offset+partition->part_size-1-offset)*elapsed_time/(offset-partition->part_offset)/60)%60), (unsigned)((partition->part_offset+partition->part_size-1-offset)*elapsed_time/(offset-partition->part_offset))%60); } photorec_info(window, file_stats); wrefresh(window); return check_enter_or_s(window); } void aff_copy(WINDOW *window) { wclear(window); keypad(window, TRUE); /* Need it to get arrow key */ wmove(window,0,0); wdoprintf(window, "PhotoRec %s, Data Recovery Utility, %s\n",VERSION,TESTDISKDATE); wmove(window,1,0); wdoprintf(window, "Christophe GRENIER "); wmove(window,2,0); wdoprintf(window, "http://www.cgsecurity.org"); } static int ask_mode_ext2(const disk_t *disk_car, const partition_t *partition, unsigned int *mode_ext2, unsigned int *carve_free_space_only) { static struct MenuItem menuMode[]= { {'E',"EXT2/EXT3","EXT2/EXT3 filesystem"}, {'O',"Other","FAT/NTFS/HFS+/ReiserFS/..."}, {0,NULL,NULL} }; static struct MenuItem menuFAT16[]= { {'F',"Free", "Scan for files from FAT16 unallocated space only"}, {'W',"Whole","Extract files from whole partition"}, {0,NULL,NULL} }; static struct MenuItem menuFAT32[]= { {'F',"Free", "Scan for file from FAT32 unallocated space only"}, {'W',"Whole","Extract files from whole partition"}, {0,NULL,NULL} }; static struct MenuItem menuNTFS[]= { {'F',"Free", "Scan for file from NTFS unallocated space only"}, {'W',"Whole","Extract files from whole partition"}, {0,NULL,NULL} }; const char *options="EO"; WINDOW *window; unsigned int menu; int command; if(partition->upart_type==UP_EXT2 || partition->upart_type==UP_EXT3) menu=0; else menu=1; window=newwin(0,0,0,0); /* full screen */ aff_copy(window); wmove(window,4,0); aff_part(window, AFF_PART_ORDER,disk_car,partition); wmove(window,6,0); waddstr(window,"To recover lost files, PhotoRec need to know the filesystem type where the"); wmove(window,7,0); waddstr(window,"file were stored:"); command = wmenuSelect_ext(window,8, 0, menuMode, 11, options, MENU_VERT | MENU_VERT_WARN | MENU_BUTTON, &menu,NULL); *mode_ext2=(command=='E' || command=='e'); if(*mode_ext2>0) { log_info("Mode EXT/EXT3 activated.\n"); } *carve_free_space_only=0; if((*mode_ext2)!=0) return 0; { menu=0; options="FW"; wmove(window,6,0); wclrtoeol(window); wmove(window,7,0); wclrtoeol(window); waddstr(window,"Please choose if all space need to be analysed:"); if(partition->upart_type==UP_FAT16) command = wmenuSelect_ext(window,8, 0, menuFAT16, 11, options, MENU_VERT | MENU_VERT_WARN | MENU_BUTTON, &menu,NULL); else if(partition->upart_type==UP_FAT32) command = wmenuSelect_ext(window,8, 0, menuFAT32, 11, options, MENU_VERT | MENU_VERT_WARN | MENU_BUTTON, &menu,NULL); #ifdef HAVE_LIBNTFS else if(partition->upart_type==UP_NTFS) command = wmenuSelect_ext(window,8, 0, menuNTFS, 11, options, MENU_VERT | MENU_VERT_WARN | MENU_BUTTON, &menu,NULL); #endif else command='W'; if(command=='F' || command=='f') *carve_free_space_only=1; if(*carve_free_space_only>0) { log_info("Carve free space only.\n"); } } delwin(window); return 0; } static unsigned int menu_choose_blocksize(unsigned int blocksize, const unsigned int sector_size, uint64_t *offset) { int command; unsigned int menu=0; const char *optionsBlocksize="51248736"; static struct MenuItem menuBlocksize[]= { {'5',"512",""}, {'1',"1024",""}, {'2',"2048",""}, {'4',"4096",""}, {'8',"8192",""}, {'7',"16384",""}, {'3',"32768",""}, {'6',"65536",""}, {0,NULL,NULL} }; switch(sector_size) { case 1024: optionsBlocksize+=1; break; case 2048: optionsBlocksize+=2; break; case 4096: optionsBlocksize+=3; break; case 8192: optionsBlocksize+=4; break; case 16384: optionsBlocksize+=5;break; case 32768: optionsBlocksize+=6; break; case 65536: optionsBlocksize+=7; break; } switch(blocksize) { case 512: menu=0; break; case 1024: menu=1; break; case 2048: menu=2; break; case 4096: menu=3; break; case 8192: menu=4; break; case 16384: menu=5; break; case 32768: menu=6; break; case 65536: menu=7; break; } aff_copy(stdscr); wmove(stdscr,INTER_PARTITION_Y-1,0); wdoprintf(stdscr,"Please select the block size, press Enter when done."); command = wmenuSelect_ext(stdscr,INTER_PARTITION_Y, INTER_PARTITION_X, menuBlocksize, 7, optionsBlocksize, MENU_VERT| MENU_BUTTON|MENU_VERT_WARN, &menu,NULL); switch(command) { case '5': blocksize=512; break; case '1': blocksize=1024; break; case '2': blocksize=2048; break; case '4': blocksize=4096; break; case '8': blocksize=8192; break; case '7': blocksize=16384; break; case '3': blocksize=32768; break; case '6': blocksize=65536; break; } if(*offset%sector_size!=0 || *offset>=blocksize) *offset=0; if(sector_size < blocksize) { unsigned int quit=0; aff_copy(stdscr); wmove(stdscr,INTER_PARTITION_Y-2,0); wdoprintf(stdscr,"Please select the offset (0 - %u). Press Up/Down to increase/decrease it,",blocksize-sector_size); wmove(stdscr,INTER_PARTITION_Y-1,0); wdoprintf(stdscr,"Enter when done."); do { wmove(stdscr,INTER_PARTITION_Y,0); wclrtoeol(stdscr); wdoprintf(stdscr,"Offset %u",(unsigned int)(*offset)); switch(wgetch(stdscr)) { case KEY_ENTER: #ifdef PADENTER case PADENTER: #endif case '\n': case '\r': quit=1; break; case KEY_PPAGE: case KEY_UP: case KEY_RIGHT: case '+': if(*offset + sector_size < blocksize) *offset+=sector_size; break; case KEY_NPAGE: case KEY_DOWN: case KEY_LEFT: case '-': if(*offset >= sector_size) *offset-=sector_size; break; } } while(quit==0); } log_info("blocksize=%u,offset=%u\n",blocksize,(unsigned int)*offset); return blocksize; } static int photorec_bf(disk_t *disk_car, partition_t *partition, const int debug, const int paranoid, const char *recup_dir, const int interface, file_stat_t *file_stats, unsigned int *file_nbr, 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, const unsigned int expert, const unsigned int lowmem) { struct list_head *search_walker = NULL; struct list_head *n= NULL; unsigned char *buffer_start; unsigned int read_size; unsigned int buffer_size; int ind_stop=0; int pass2=pass; read_size=((*blocksize)>8192?(*blocksize):8192); buffer_size=(*blocksize)+READ_SIZE; buffer_start=MALLOC(buffer_size); for(search_walker=list_search_space->list.prev, n=search_walker->prev; search_walker!=&list_search_space->list && ind_stop==0; search_walker=n,n=search_walker->prev) { alloc_data_t *current_search_space; unsigned char *buffer; unsigned char *buffer_olddata; uint64_t offset; int need_to_check_file; file_recovery_t file_recovery; current_search_space=list_entry(search_walker, alloc_data_t, list); offset=current_search_space->start; buffer_olddata=buffer_start; buffer=buffer_olddata+(*blocksize); reset_file_recovery(&file_recovery); memset(buffer_olddata,0,(*blocksize)); disk_car->read(disk_car,READ_SIZE, buffer, offset); #ifdef DEBUG_BF { struct list_head *tmp= NULL; log_debug("Explore "); list_for_each(tmp, &list_search_space->list) { alloc_data_t *cur_free_space; cur_free_space=list_entry(tmp, alloc_data_t, list); log_debug(" %lu-%lu",(long unsigned)(cur_free_space->start/disk_car->sector_size), (long unsigned)(cur_free_space->end/disk_car->sector_size)); if(cur_free_space==current_search_space) log_debug("*"); } log_debug("\n"); } #endif log_flush(); do { uint64_t old_offset=offset; need_to_check_file=0; if(offset==current_search_space->start) { file_recovery_t file_recovery_new; file_check_t *file_check; for(file_check=file_check_list; file_check!=NULL && !((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_check=file_check->next); file_recovery_new.file_stat=(file_check==NULL?NULL:file_check->file_stat); file_recovery_new.location.start=offset; if(file_recovery_new.file_stat!=NULL) { if(file_recovery.file_stat==NULL) { /* Header found => file found */ memcpy(&file_recovery, &file_recovery_new, sizeof(file_recovery)); if(debug>0) { log_info("%s header found at sector %lu\n", ((file_recovery.extension!=NULL && file_recovery.extension[0]!='\0')? file_recovery.extension:file_recovery.file_stat->file_hint->description), (unsigned long)((offset-partition->part_offset)/disk_car->sector_size)); } } else if(file_recovery_new.file_stat->file_hint!=NULL) need_to_check_file=1; /* New file found => stop the recovery of current file */ } 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 */ if(file_recovery.extension==NULL || file_recovery.extension[0]=='\0') { snprintf(file_recovery.filename,sizeof(file_recovery.filename)-1,"%s.%u/%c%u",recup_dir, *dir_num,(status==STATUS_EXT2_ON_SAVE_EVERYTHING||status==STATUS_EXT2_OFF_SAVE_EVERYTHING?'b':'f'), (unsigned int)((file_recovery.location.start-partition->part_offset)/disk_car->sector_size)); } else { snprintf(file_recovery.filename,sizeof(file_recovery.filename)-1,"%s.%u/%c%u.%s",recup_dir, *dir_num,(status==STATUS_EXT2_ON_SAVE_EVERYTHING||status==STATUS_EXT2_OFF_SAVE_EVERYTHING?'b':'f'), (unsigned int)((file_recovery.location.start-partition->part_offset)/disk_car->sector_size), file_recovery.extension); } 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\n", file_recovery.filename); ind_stop=2; } } } if(file_recovery.handle!=NULL) { if((status==STATUS_EXT2_ON || status==STATUS_EXT2_ON_SAVE_EVERYTHING) && file_recovery.file_stat!=NULL && file_recovery.file_size_on_disk>=12*(*blocksize) && ind_block(buffer,*blocksize)!=0) { list_append_block(&file_recovery.location,offset,*blocksize,0); file_recovery.file_size_on_disk+=*blocksize; if(debug>1) { log_debug("Skipping sector %10lu/%lu\n",(unsigned long)((offset-partition->part_offset)/disk_car->sector_size),(unsigned long)(partition->part_size/disk_car->sector_size)); } memcpy(buffer,buffer_olddata,(*blocksize)); } else { if(file_recovery.handle!=NULL) { if(fwrite(buffer,*blocksize,1,file_recovery.handle)<1) { log_critical("Cannot write to file %s\n", file_recovery.filename); ind_stop=3; } } if(file_recovery.file_stat!=NULL) { int res=1; 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_debug("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(debug>1) { log_debug("Reading sector %10lu/%lu\n", (unsigned long)((offset-partition->part_offset)/disk_car->sector_size), (unsigned long)((partition->part_size-1)/disk_car->sector_size)); } disk_car->read(disk_car,READ_SIZE, buffer, offset); } } } while(need_to_check_file==0); if(need_to_check_file==1) { if(file_finish(&file_recovery,recup_dir,paranoid,file_nbr, *blocksize, list_search_space, ¤t_search_space, &offset, dir_num,status,disk_car->sector_size,disk_car)<0) { /* BF */ current_search_space=list_entry(search_walker, alloc_data_t, list); ind_stop=photorec_bf_aux(disk_car, partition, paranoid, recup_dir, interface, file_stats, file_nbr, &file_recovery, *blocksize, list_search_space, current_search_space, real_start_time, dir_num, status, pass2); pass2++; } } } photorec_info(stdscr, file_stats); return ind_stop; } static int photorec_bf_aux(disk_t *disk_car, partition_t *partition, const int paranoid, const char *recup_dir, const int interface, file_stat_t *file_stats, unsigned int *file_nbr, file_recovery_t *file_recovery, 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 unsigned int pass) { uint64_t offset; uint64_t original_offset_error, file_offset; long int save_seek; unsigned char *block_buffer; int blocs_to_skip,i; int ind_stop; int testbf=0; time_t previous_time=0; alloc_data_t *current_search_space; //Init. of the brute force #ifdef DEBUG_BF log_trace("photorec_bf_aux location.start=%lu\n", (long unsigned)(file_recovery->location.start/disk_car->sector_size)); #endif original_offset_error=file_recovery->offset_error; file_recovery->handle=fopen(file_recovery->filename, "w+b"); if(file_recovery->handle==NULL) { log_critical("Brute Force : Cannot create file %s\n", file_recovery->filename); return 2; } block_buffer=(unsigned char *) malloc(sizeof(unsigned char)*blocksize); current_search_space=start_search_space; /* We have offset==start_search_space->start==file_recovery->location.start */ offset=start_search_space->start;; // Writing the file until the error location #ifdef DEBUG_BF log_debug("Writing the file until the error location %llu\n", (long long unsigned)original_offset_error); #endif //FIXME: Handle ext2/ext3, handle fwrite return value file_recovery->file_size=0; for(i=0; i<(original_offset_error+blocksize-1)/blocksize; i++) { disk_car->read(disk_car,blocksize, block_buffer, offset); fwrite(block_buffer, blocksize, 1, file_recovery->handle); list_append_block(&file_recovery->location, offset, blocksize, 1); file_recovery->file_size+=blocksize; get_next_sector(list_search_space, ¤t_search_space, &offset, blocksize); } #ifdef DEBUG_BF log_trace("BF Amorce "); list_space_used(file_recovery, blocksize); log_trace("\n"); #endif //Main Loop do { ind_stop=0; for(file_offset=(original_offset_error+blocksize-1)/blocksize*blocksize; file_offset >= blocksize && (original_offset_error+blocksize-1)/blocksize*blocksizelocation;element!=NULL;element=element->next) extrablock_offset=element->end/blocksize*blocksize; } /* Get the corresponding search_place */ extractblock_search_space=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=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); /* */ for(blocs_to_skip=1; blocs_to_skip<=250 && ind_stop==0; blocs_to_skip++) { offset=extrablock_offset; current_search_space=extractblock_search_space; testbf++; if(interface!=0) { time_t current_time; current_time=time(NULL); if(current_time>previous_time) { previous_time=current_time; ind_stop=photorec_progressbar(stdscr, testbf, status, file_recovery->location.start, disk_car, partition, *file_nbr, current_time-real_start_time, file_stats); if(ind_stop!=0) { file_finish(file_recovery,recup_dir,paranoid,file_nbr,blocksize,list_search_space,¤t_search_space, &offset, dir_num,status,disk_car->sector_size,disk_car); free(block_buffer); return ind_stop; } } } fseek(file_recovery->handle,file_offset,SEEK_SET); list_truncate(&file_recovery->location,file_offset); file_recovery->file_size=file_offset; /* Skip extra blocs */ #ifdef DEBUG_BF log_debug("Skip %u extra blocs\n", blocs_to_skip); #endif for(i=0; ioffset_error=original_offset_error; do { offset_error_tmp=file_recovery->offset_error; file_recovery->file_size=file_offset; for (;file_recovery->file_size < file_recovery->offset_error+100*blocksize && current_search_space != list_search_space; file_recovery->file_size+=blocksize) { /* FIXME: handle fwrite return value */ disk_car->read(disk_car, blocksize, block_buffer, offset); fwrite(block_buffer, blocksize, 1, file_recovery->handle); list_append_block(&file_recovery->location, offset, blocksize, 1); get_next_sector(list_search_space, ¤t_search_space, &offset, blocksize); } save_seek=ftell(file_recovery->handle); #ifdef DEBUG_BF log_trace("BF "); list_space_used(file_recovery, blocksize); #endif file_recovery->file_check(file_recovery); #ifdef DEBUG_BF log_trace("offset_error %llu %llu\n", (long long unsigned) file_recovery->offset_error, (long long unsigned) offset_error_tmp); #endif log_flush(); fseek(file_recovery->handle, save_seek, SEEK_SET); } while(file_recovery->offset_error/blocksize*blocksize > offset_error_tmp/blocksize*blocksize); } if(file_recovery->offset_error==0) { /* Recover the file */ file_finish(file_recovery,recup_dir,paranoid,file_nbr,blocksize,list_search_space,¤t_search_space, &offset, dir_num,status,disk_car->sector_size,disk_car); free(block_buffer); return ind_stop; } else if(file_recovery->offset_error/blocksize*blocksize >= original_offset_error+4096) { /* Try to recover file composed of multiple fragments */ log_info("%s multiple fragment %llu -> %llu\n", file_recovery->filename, (unsigned long long)original_offset_error, (unsigned long long)file_recovery->offset_error); log_flush(); original_offset_error=file_recovery->offset_error; ind_stop=2; } } } } while(ind_stop==2); /* Cleanup */ file_finish(file_recovery,recup_dir,paranoid,file_nbr,blocksize,list_search_space,¤t_search_space, &offset, dir_num,status,disk_car->sector_size,disk_car); free(block_buffer); return ind_stop; } static int photorec_aux(disk_t *disk_car, partition_t *partition, const int debug, const int paranoid, const char *recup_dir, const int interface, file_stat_t *file_stats, unsigned int *file_nbr, 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, const unsigned int expert, const unsigned int lowmem) { uint64_t offset=0; unsigned char *buffer_start; unsigned char *buffer_olddata; unsigned char *buffer; time_t start_time; time_t previous_time; int ind_stop=0; unsigned int buffer_size; unsigned int read_size; alloc_data_t *current_search_space; file_recovery_t file_recovery; static alloc_data_t list_file={ .list = LIST_HEAD_INIT(list_file.list) }; static list_cluster_t list_cluster= { .list = LIST_HEAD_INIT(list_cluster.list) }; #define READ_SIZE 256*512 read_size=((*blocksize)>8192?(*blocksize):8192); buffer_size=(*blocksize)+READ_SIZE; buffer_start=MALLOC(buffer_size); buffer_olddata=buffer_start; buffer=buffer_olddata+(*blocksize); reset_file_recovery(&file_recovery); start_time=time(NULL); previous_time=start_time; memset(buffer_olddata,0,(*blocksize)); if(debug>1) { struct list_head *search_walker = NULL; log_debug("Explore "); list_for_each(search_walker, &list_search_space->list) { alloc_data_t *cur_free_space; cur_free_space=list_entry(search_walker, alloc_data_t, list); log_debug(" %lu-%lu",(long unsigned)(cur_free_space->start/disk_car->sector_size), (long unsigned)(cur_free_space->end/disk_car->sector_size)); } log_debug("\n"); } current_search_space=list_entry(list_search_space->list.next, alloc_data_t, list); if(current_search_space!=list_search_space) offset=current_search_space->start; disk_car->read(disk_car,READ_SIZE, buffer, offset); while(current_search_space!=list_search_space) { int move_next=1; uint64_t old_offset=offset; #ifdef DEBUG log_debug("sector %llu\n", (unsigned long long)((offset-partition->part_offset)/disk_car->sector_size)); #endif if(!(current_search_space->start<=offset && offset<=current_search_space->end)) { log_critical("BUG: offset=%llu not in [%llu-%llu]\n", (unsigned long long)(offset/disk_car->sector_size), (unsigned long long)(current_search_space->start/disk_car->sector_size), (unsigned long long)(current_search_space->end/disk_car->sector_size)); log_flush(); exit(1); } { file_recovery_t file_recovery_new; if(file_recovery.file_stat!=NULL && file_recovery.file_stat->file_hint->min_header_distance > 0 && file_recovery.file_size<=file_recovery.file_stat->file_hint->min_header_distance) { } else if(file_recovery.file_stat!=NULL && file_recovery.file_stat->file_hint==&file_hint_tar && file_hint_tar.header_check(buffer-0x200,0x200,0,&file_recovery,&file_recovery_new)) { /* Currently saving a tar, do not check the data for know header */ if(debug>1) { log_debug("Currently saving a tar file, sector %lu.\n", (unsigned long)((offset-partition->part_offset)/disk_car->sector_size)); } } else { { file_check_t *file_check; for(file_check=file_check_list;file_check!=NULL && !((file_check->length==0 || memcmp(buffer + file_check->offset, file_check->value, file_check->length)==0) && file_check->header_check(buffer, read_size, (status==STATUS_FIND_OFFSET), &file_recovery, &file_recovery_new)!=0); file_check=file_check->next); file_recovery_new.file_stat=(file_check==NULL?NULL:file_check->file_stat); file_recovery_new.location.start=offset; } if(file_recovery_new.file_stat!=NULL && file_recovery_new.file_stat->file_hint!=NULL) { /* A known header has been found, recovery of the previous file is finished */ if(file_finish(&file_recovery,recup_dir,paranoid,file_nbr,*blocksize,list_search_space,¤t_search_space, &offset, dir_num,status,disk_car->sector_size,disk_car)>0) move_next=0; reset_file_recovery(&file_recovery); if(lowmem>0) forget(list_search_space,current_search_space); if(move_next!=0) { memcpy(&file_recovery, &file_recovery_new, sizeof(file_recovery)); if(debug>1) { log_info("%s header found at sector %lu\n", ((file_recovery.extension!=NULL && file_recovery.extension[0]!='\0')? file_recovery.extension:file_recovery.file_stat->file_hint->description), (unsigned long)((file_recovery.location.start-partition->part_offset)/disk_car->sector_size)); log_info("file_recovery.location.start=%lu\n", (unsigned long)(file_recovery.location.start/disk_car->sector_size)); } if(status==STATUS_FIND_OFFSET) { /* Backup file offset */ alloc_data_t *new_file_alloc; new_file_alloc=(alloc_data_t*)MALLOC(sizeof(*new_file_alloc)); new_file_alloc->start=file_recovery.location.start; new_file_alloc->end=0; list_add_tail(&new_file_alloc->list,&list_file.list); (*file_nbr)++; } if(file_recovery.file_stat->file_hint==&file_hint_dir) { file_data_t *dir_list; dir_list=dir_fat_aux(buffer,read_size,0); if(dir_list!=NULL) { if(debug>0) { dir_aff_log(disk_car, partition, NULL, dir_list); } delete_list_file(dir_list); } } } } } if(file_recovery.file_stat!=NULL && file_recovery.handle==NULL) { if(file_recovery.extension==NULL || file_recovery.extension[0]=='\0') { snprintf(file_recovery.filename,sizeof(file_recovery.filename)-1,"%s.%u/%c%u",recup_dir, *dir_num,(status==STATUS_EXT2_ON_SAVE_EVERYTHING||status==STATUS_EXT2_OFF_SAVE_EVERYTHING?'b':'f'), (unsigned int)((file_recovery.location.start-partition->part_offset)/disk_car->sector_size)); } else { snprintf(file_recovery.filename,sizeof(file_recovery.filename)-1,"%s.%u/%c%u.%s",recup_dir, *dir_num,(status==STATUS_EXT2_ON_SAVE_EVERYTHING||status==STATUS_EXT2_OFF_SAVE_EVERYTHING?'b':'f'), (unsigned int)((file_recovery.location.start-partition->part_offset)/disk_car->sector_size), file_recovery.extension); } if(file_recovery.file_stat->file_hint->recover==1 && status!=STATUS_FIND_OFFSET) { if(!(file_recovery.handle=fopen(file_recovery.filename,"w+b"))) { log_critical("Cannot create file %s\n", file_recovery.filename); ind_stop=2; } } } } /* try to skip ext2/ext3 indirect block */ #ifdef OLD /* EXT2_NDIR_BLOCKS=12 */ if((status==STATUS_EXT2_ON || status==STATUS_EXT2_ON_SAVE_EVERYTHING) && file_recovery.file_stat!=NULL && ((file_recovery.file_size_on_disk==12*(*blocksize)) || (file_recovery.file_size_on_disk==(12+1+(*blocksize)/4)*(*blocksize)) || (file_recovery.file_size_on_disk%(*blocksize)==0 && file_recovery.file_size_on_disk>(12+1+(*blocksize)/4)*(*blocksize) && ((file_recovery.file_size_on_disk/(*blocksize))-(12+1))%((*blocksize)/4+1)==0)) && (file_recovery.location.start>=current_search_space->start) ) #else if((status==STATUS_EXT2_ON || status==STATUS_EXT2_ON_SAVE_EVERYTHING) && file_recovery.file_stat!=NULL && file_recovery.file_size_on_disk>=12*(*blocksize) && ind_block(buffer,*blocksize)!=0) #endif { list_append_block(&file_recovery.location,offset,*blocksize,0); file_recovery.file_size_on_disk+=*blocksize; if(debug>1) { log_debug("Skipping sector %10lu/%lu\n", (unsigned long)((offset-partition->part_offset)/disk_car->sector_size), (unsigned long)(partition->part_size/disk_car->sector_size)); } memcpy(buffer,buffer_olddata,(*blocksize)); } else { if(file_recovery.handle!=NULL) { if(fwrite(buffer,*blocksize,1,file_recovery.handle)<1) { log_critical("Cannot write file %s\n", file_recovery.filename); ind_stop=3; } } if(file_recovery.file_stat!=NULL) { int res=1; 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 */ if(file_finish(&file_recovery,recup_dir,paranoid,file_nbr, *blocksize, list_search_space, ¤t_search_space, &offset, dir_num,status,disk_car->sector_size,disk_car)>0) move_next=0; reset_file_recovery(&file_recovery); if(lowmem>0) forget(list_search_space,current_search_space); } } } 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_debug("File should not be bigger than %llu, stop adding data\n", (long long unsigned)file_recovery.file_stat->file_hint->max_filesize); if(file_finish(&file_recovery,recup_dir,paranoid,file_nbr,*blocksize, list_search_space, ¤t_search_space, &offset, dir_num,status,disk_car->sector_size,disk_car)>0) move_next=0; reset_file_recovery(&file_recovery); if(lowmem>0) forget(list_search_space,current_search_space); } if(ind_stop>0) { log_info("PhotoRec has been stopped\n"); current_search_space=list_search_space; } else if(*file_nbr>=10 && status==STATUS_FIND_OFFSET) { current_search_space=list_search_space; } else if(move_next!=0) { get_next_sector(list_search_space, ¤t_search_space,&offset,*blocksize); } else // if(move_next==0) { /* try to recover the previous file, otherwise stay at the current location */ get_prev_file_header(list_search_space, ¤t_search_space, &offset); } if(current_search_space==list_search_space) { #ifdef DEBUG_GET_NEXT_SECTOR log_trace("current_search_space==list_search_space=%p (prev=%p,next=%p)\n", current_search_space, current_search_space->list.prev, current_search_space->list.next); #endif if(file_finish(&file_recovery,recup_dir,paranoid,file_nbr,*blocksize,list_search_space, ¤t_search_space, &offset, dir_num,status,disk_car->sector_size,disk_car)>0) { move_next=0; get_prev_file_header(list_search_space, ¤t_search_space, &offset); } reset_file_recovery(&file_recovery); if(lowmem>0) forget(list_search_space,current_search_space); } buffer_olddata+=*blocksize; buffer+=*blocksize; if(move_next==0 || old_offset+*blocksize!=offset || buffer+read_size>buffer_start+buffer_size) { if(move_next==0) memset(buffer_start,0,(*blocksize)); else memcpy(buffer_start,buffer_olddata,*blocksize); buffer_olddata=buffer_start; buffer=buffer_olddata+*blocksize; if(debug>1) { log_debug("Reading sector %10lu/%lu\n",(unsigned long)((offset-partition->part_offset)/disk_car->sector_size),(unsigned long)((partition->part_size-1)/disk_car->sector_size)); } disk_car->read(disk_car,READ_SIZE, buffer, offset); if(interface!=0) { time_t current_time; current_time=time(NULL); if(current_time>previous_time) { previous_time=current_time; ind_stop=photorec_progressbar(stdscr, pass, status, offset, disk_car, partition, *file_nbr, current_time-real_start_time, file_stats); } } } } /* end while(current_search_space!=list_search_space) */ if(status==STATUS_FIND_OFFSET) { uint64_t start_offset; *blocksize=find_blocksize(&list_file,disk_car->sector_size, &start_offset); if(expert>0) *blocksize=menu_choose_blocksize(*blocksize, disk_car->sector_size, &start_offset); update_blocksize(*blocksize,list_search_space, start_offset); free_list_search_space(&list_file); } free(buffer_start); photorec_info(stdscr, file_stats); return ind_stop; } static void recovery_finished(const unsigned int file_nbr, const char *recup_dir, const int ind_stop, char **current_cmd) { wmove(stdscr,9,0); wclrtoeol(stdscr); wdoprintf(stdscr,"%u files saved in %s directory.\n",file_nbr,recup_dir); wmove(stdscr,10,0); wclrtoeol(stdscr); switch(ind_stop) { case 0: wdoprintf(stdscr,"Recovery completed."); break; case 1: wdoprintf(stdscr,"Recovery aborted by the user."); break; case 2: wdoprintf(stdscr,"Cannot create file in current directory."); break; case 3: wdoprintf(stdscr,"Cannot write file, no space left."); break; } if(*current_cmd==NULL) { int quit=0; wmove(stdscr,22,0); wclrtoeol(stdscr); wattrset(stdscr, A_REVERSE); wdoprintf(stdscr,"[ Quit ]"); wattroff(stdscr, A_REVERSE); wrefresh(stdscr); do { switch(wgetch(stdscr)) { case KEY_ENTER: #ifdef PADENTER case PADENTER: #endif case '\n': case '\r': case 'q': case 'Q': quit=1; break; } } while(quit==0); } } static int photorec(disk_t *disk_car, partition_t *partition, const int debug, const int paranoid, const char *recup_dir, const int keep_corrupted_file, const int interface, file_enable_t *files_enable, unsigned int mode_ext2, char **current_cmd, alloc_data_t *list_search_space, unsigned int blocksize, const unsigned int expert, const unsigned int lowmem, const unsigned int carve_free_space_only) { file_stat_t *file_stats=NULL; time_t real_start_time; unsigned int file_nbr=0; unsigned int dir_num=1; int ind_stop=0; unsigned int pass; unsigned int blocksize_is_known=0; photorec_status_t status; aff_buffer(BUFFER_RESET,"Q"); log_info("\nAnalyse\n"); log_partition(disk_car,partition); if(blocksize==0 || list_empty(&list_search_space->list)) { blocksize=disk_car->sector_size; blocksize_is_known=0; } else blocksize_is_known=1; if(list_empty(&list_search_space->list)) { alloc_data_t *tmp=init_search_space(partition,disk_car); list_add_tail(&tmp->list, &list_search_space->list); if(mode_ext2==0 && carve_free_space_only>0) { blocksize=remove_used_space(disk_car, partition, list_search_space); if(blocksize==0) blocksize=disk_car->sector_size; else blocksize_is_known=1; } } else { /* Correct the values */ struct list_head *search_walker = NULL; list_for_each(search_walker, &list_search_space->list) { alloc_data_t *current_search_space; current_search_space=list_entry(search_walker, alloc_data_t, list); current_search_space->start=current_search_space->start*disk_car->sector_size; current_search_space->end=current_search_space->end*disk_car->sector_size+disk_car->sector_size-1; } } { file_enable_t *file_enable; unsigned int enable_count=1; /* Lists are terminated by NULL */ for(file_enable=files_enable;file_enable->file_hint!=NULL;file_enable++) { if(file_enable->enable>0) { enable_count++; } } file_stats=MALLOC(enable_count * sizeof(file_stat_t)); enable_count=0; for(file_enable=files_enable;file_enable->file_hint!=NULL;file_enable++) { if(file_enable->enable>0) { file_stats[enable_count].file_hint=file_enable->file_hint; file_stats[enable_count].not_recovered=0; file_stats[enable_count].recovered=0; if(file_enable->file_hint->register_header_check!=NULL) file_enable->file_hint->register_header_check(&file_stats[enable_count]); enable_count++; } } file_stats[enable_count].file_hint=NULL; } real_start_time=time(NULL); dir_num=photorec_mkdir(recup_dir,dir_num); status=STATUS_FIND_OFFSET; for(pass=0;status!=STATUS_QUIT;pass++) { unsigned int old_file_nbr=file_nbr; log_info("Pass %u (blocksize=%u) ",pass,blocksize); switch(status) { case STATUS_FIND_OFFSET: log_info("STATUS_FIND_OFFSET\n"); break; case STATUS_EXT2_ON: log_info("STATUS_EXT2_ON\n"); break; case STATUS_EXT2_ON_BF: log_info("STATUS_EXT2_ON_BF\n"); break; case STATUS_EXT2_OFF: log_info("STATUS_EXT2_OFF\n"); break; case STATUS_EXT2_OFF_BF: log_info("STATUS_EXT2_OFF_BF\n"); break; case STATUS_EXT2_ON_SAVE_EVERYTHING: log_info("STATUS_EXT2_ON_SAVE_EVERYTHING\n"); break; case STATUS_EXT2_OFF_SAVE_EVERYTHING: log_info("STATUS_EXT2_OFF_SAVE_EVERYTHING\n"); break; case STATUS_QUIT : log_info("STATUS_QUIT\n"); break; } if(interface) { aff_copy(stdscr); wmove(stdscr,4,0); wdoprintf(stdscr,"%s",disk_car->description_short(disk_car)); mvwaddstr(stdscr,5,0,msg_PART_HEADER_LONG); wmove(stdscr,6,0); aff_part(stdscr,AFF_PART_ORDER,disk_car,partition); wmove(stdscr,22,0); wattrset(stdscr, A_REVERSE); waddstr(stdscr," Stop "); wattroff(stdscr, A_REVERSE); wrefresh(stdscr); } if(status==STATUS_FIND_OFFSET && blocksize_is_known>0) ind_stop=0; else if(status==STATUS_EXT2_ON_BF || status==STATUS_EXT2_OFF_BF) { ind_stop=photorec_bf(disk_car, partition, debug, paranoid, recup_dir, interface, file_stats, &file_nbr, &blocksize, list_search_space, real_start_time, &dir_num, status, pass,expert, lowmem); session_save(list_search_space, disk_car, partition, files_enable, blocksize, debug); } else { ind_stop=photorec_aux(disk_car, partition, debug, paranoid, recup_dir, interface, file_stats, &file_nbr, &blocksize, list_search_space, real_start_time, &dir_num, status, pass,expert, lowmem); session_save(list_search_space, disk_car, partition, files_enable, blocksize, debug); } if(ind_stop>0) status=STATUS_QUIT; else if(paranoid>0) { switch(status) { case STATUS_FIND_OFFSET: status=(mode_ext2>0?STATUS_EXT2_ON:STATUS_EXT2_OFF); file_nbr=0; break; case STATUS_EXT2_ON: status=(paranoid>1?STATUS_EXT2_ON_BF:STATUS_EXT2_OFF); break; case STATUS_EXT2_ON_BF: status=STATUS_EXT2_OFF; break; case STATUS_EXT2_OFF: if(paranoid>1) { status=STATUS_EXT2_OFF_BF; } else { if(keep_corrupted_file>0) status=(mode_ext2>0?STATUS_EXT2_ON_SAVE_EVERYTHING:STATUS_EXT2_OFF_SAVE_EVERYTHING); else { status=STATUS_QUIT; unlink("photorec.ses"); } } break; case STATUS_EXT2_OFF_BF: if(keep_corrupted_file>0) status=(mode_ext2>0?STATUS_EXT2_ON_SAVE_EVERYTHING:STATUS_EXT2_OFF_SAVE_EVERYTHING); else { status=STATUS_QUIT; unlink("photorec.ses"); } break; case STATUS_EXT2_ON_SAVE_EVERYTHING: status=STATUS_EXT2_OFF_SAVE_EVERYTHING; break; default: status=STATUS_QUIT; unlink("photorec.ses"); break; } } else { switch(status) { case STATUS_FIND_OFFSET: status=(mode_ext2>0?STATUS_EXT2_ON_SAVE_EVERYTHING:STATUS_EXT2_OFF_SAVE_EVERYTHING); file_nbr=0; break; default: status=STATUS_QUIT; unlink("photorec.ses"); break; } } { time_t current_time; current_time=time(NULL); log_info("Elapsed time %uh%02um%02us\n", (unsigned)((current_time-real_start_time)/60/60), (unsigned)((current_time-real_start_time)/60%60), (unsigned)((current_time-real_start_time)%60)); } update_stats(file_stats,list_search_space); log_info("Pass %u +%u file%s\n",pass,file_nbr-old_file_nbr,(file_nbr-old_file_nbr<=1?"":"s")); write_stats_log(file_stats); if(interface==0) { printf("Pass %u +%u file%s\n",pass,file_nbr-old_file_nbr,(file_nbr-old_file_nbr<=1?"":"s")); write_stats_stdout(file_stats); fflush(stdout); } } { struct list_head *search_walker = NULL; struct list_head *search_walker_next = NULL; unsigned long int nbr_headers=0; uint64_t sectors_with_unknown_data=0; /* Free memory */ list_for_each_safe(search_walker,search_walker_next,&list_search_space->list) { alloc_data_t *current_search_space; current_search_space=list_entry(search_walker, alloc_data_t, list); if(current_search_space->file_stat!=NULL) { nbr_headers++; current_search_space->file_stat->not_recovered++; } sectors_with_unknown_data+=(current_search_space->end-current_search_space->start+disk_car->sector_size-1)/disk_car->sector_size; if(debug>0) { log_info("%lu-%lu: %s\n",(long unsigned)(current_search_space->start/disk_car->sector_size), (long unsigned)(current_search_space->end/disk_car->sector_size), (current_search_space->file_stat!=NULL && current_search_space->file_stat->file_hint!=NULL? (current_search_space->file_stat->file_hint->extension? current_search_space->file_stat->file_hint->extension:""): "(null)")); } list_del(search_walker); free(current_search_space); } log_info("%llu sectors contains unknown data, %lu invalid files found %s.\n", (long long unsigned)sectors_with_unknown_data, (long unsigned)nbr_headers, (keep_corrupted_file>0?"but saved":"and rejected")); } if(interface) recovery_finished(file_nbr, recup_dir, ind_stop, current_cmd); free(file_stats); free_header_check(); return 0; } static int menu_photorec(disk_t *disk_car, int debug, const char *recup_dir, file_enable_t *file_enable, char **current_cmd, alloc_data_t*list_search_space) { int command; int done=0; int offset=0; int allow_partial_last_cylinder=0; int paranoid=1; unsigned int menu=0; int keep_corrupted_file=0; int current_element_num=0; unsigned int mode_ext2=0; unsigned int blocksize=0; unsigned int expert=0; unsigned int lowmem=0; unsigned int carve_free_space_only=0; list_part_t *list_part; list_part_t *element; list_part_t *current_element; static struct MenuItem menuMain[]= { {'S',"Search","Start file recovery"}, {'O',"Options","Modify options"}, {'F',"File Opt","Modify file options"}, {'G',"Geometry", "Change disk geometry" }, {'Q',"Quit","Return to disk selection"}, {0,NULL,NULL} }; list_part=disk_car->arch->read_part(disk_car,debug,0); list_part=insert_new_partition(list_part,new_whole_disk(disk_car)); current_element=list_part; for(element=list_part;element!=NULL;element=element->next) { log_partition(disk_car,element->part); } /* ncurses interface */ while(done==0) { unsigned int i; aff_copy(stdscr); wmove(stdscr,4,0); wdoprintf(stdscr,"%s",disk_car->description_short(disk_car)); mvwaddstr(stdscr,6,0,msg_PART_HEADER_LONG); for(i=0,element=list_part;(element!=NULL) && (inext,i++); for(i=offset;(element!=NULL) && ((i-offset)next) { wmove(stdscr,5+2+i-offset,0); wclrtoeol(stdscr); /* before addstr for BSD compatibility */ if(element==current_element) { wattrset(stdscr, A_REVERSE); aff_part(stdscr,AFF_PART_ORDER,disk_car,element->part); wattroff(stdscr, A_REVERSE); } else { aff_part(stdscr,AFF_PART_ORDER,disk_car,element->part); } } if(*current_cmd!=NULL) { int keep_asking; command='q'; do { keep_asking=0; while(*current_cmd[0]==',') (*current_cmd)++; if(*current_cmd[0]=='\0') { } else if(strncmp(*current_cmd,"search",6)==0) { (*current_cmd)+=6; command='s'; } else if(strncmp(*current_cmd,"options",7)==0) { (*current_cmd)+=7; command='O'; } else if(strncmp(*current_cmd,"fileopt",7)==0) { (*current_cmd)+=7; command='F'; } else if(strncmp(*current_cmd,"blocksize",9)==0) { (*current_cmd)+=9; while(*current_cmd[0]!=',' && *current_cmd[0]!='\0') (*current_cmd)++; blocksize=atoi(*current_cmd); while(*current_cmd[0]!=',' && *current_cmd[0]!='\0') (*current_cmd)++; } else if(isdigit(*current_cmd[0])) { unsigned int order; order= atoi(*current_cmd); while(*current_cmd[0]!=',' && *current_cmd[0]!='\0') (*current_cmd)++; for(element=list_part;element!=NULL && element->part->order!=order;element=element->next); if(element!=NULL) { current_element=element; keep_asking=1; } } else { log_critical("error >%s<\n",*current_cmd); command='Q'; while(*current_cmd[0]!='\0') (*current_cmd)++; } } while(keep_asking>0); } else command = wmenuSelect(stdscr,INTER_SELECT_Y, INTER_SELECT_X, menuMain, 8, (expert==0?"SOFQ":"SOFGQ"), MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, menu); switch(command) { case KEY_UP: if(current_element!=NULL) { if(current_element->prev!=NULL) { current_element=current_element->prev; current_element_num--; } if(current_element_numnext!=NULL) { current_element=current_element->next; current_element_num++; } if(current_element_num>=offset+INTER_SELECT) offset++; } break; case 's': case 'S': if(*current_cmd==NULL) { ask_mode_ext2(disk_car, current_element->part, &mode_ext2, &carve_free_space_only); } { char *res; menu=0; if(recup_dir!=NULL) res=recup_dir; else { res=ask_location("Do you want to save recovered files in %s%s ? [Y/N]\nDo not choose to write the files to the same partition they were stored on.",""); if(res!=NULL) { char *new_recup_dir=MALLOC(strlen(res)+1+strlen(DEFAULT_RECUP_DIR)+1); strcpy(new_recup_dir,res); strcat(new_recup_dir,"/"); strcat(new_recup_dir,DEFAULT_RECUP_DIR); free(res); res=new_recup_dir; } } if(res!=NULL) photorec(disk_car, current_element->part, debug, paranoid, res, keep_corrupted_file,1,file_enable,mode_ext2,current_cmd,list_search_space,blocksize,expert, lowmem, carve_free_space_only); if(res!=recup_dir) free(res); } break; case 'o': case 'O': { int old_allow_partial_last_cylinder=allow_partial_last_cylinder; interface_options_photorec(¶noid, &allow_partial_last_cylinder, &keep_corrupted_file, &mode_ext2, &expert, &lowmem, current_cmd); if(old_allow_partial_last_cylinder!=allow_partial_last_cylinder) hd_update_geometry(disk_car,allow_partial_last_cylinder,debug); menu=1; } break; case 'f': case 'F': interface_file_select(file_enable,current_cmd); menu=2; break; case 'g': case 'G': if(expert!=0) change_geometry(disk_car,current_cmd); break; case 'q': case 'Q': done = 1; break; } } log_info("\n"); delete_list_part(list_part); return 0; } int do_curses_photorec(int debug, const char *recup_dir, const list_disk_t *list_disk, file_enable_t *file_enable, char *cmd_device, char *cmd_run) { int command; int real_key; int allow_partial_last_cylinder=0; int done=0; unsigned int menu=0; int offset=0; int pos_num=0; const list_disk_t *element_disk; const list_disk_t *current_disk=NULL; static struct MenuItem menuMain[]= { { 'P', "Previous",""}, { 'N', "Next","" }, { 'O',"Proceed",""}, { 'Q',"Quit","Quit program"}, { 0,NULL,NULL} }; char *current_cmd=cmd_run; alloc_data_t list_search_space={ .list = LIST_HEAD_INIT(list_search_space.list) }; if(cmd_device==NULL) { session_load(&cmd_device, ¤t_cmd,&list_search_space); if(cmd_device!=NULL && current_cmd!=NULL && !list_empty(&list_search_space.list) && ask_confirmation("Continue previous session ? (Y/N)")!=0) { /* yes */ } else { if(cmd_device!=NULL) free(cmd_device); cmd_device=NULL; if(current_cmd!=NULL) free(current_cmd); current_cmd=NULL; free_list_search_space(&list_search_space); } } if(cmd_device!=NULL) { for(element_disk=list_disk;element_disk!=NULL;element_disk=element_disk->next) { if(strcmp(element_disk->disk->device,cmd_device)==0) current_disk=element_disk; } } else current_disk=list_disk; if(current_disk==NULL) { intrf_no_disk("PhotoRec"); return 0; } /* ncurses interface */ while(done==0) { const char *options; int i; aff_copy(stdscr); wmove(stdscr,4,0); wdoprintf(stdscr," PhotoRec is free software, and"); wmove(stdscr,5,0); wdoprintf(stdscr,"comes with ABSOLUTELY NO WARRANTY."); wmove(stdscr,7,0); wdoprintf(stdscr,"Select a media (use Arrow keys, then press Enter):"); for(i=0,element_disk=list_disk;(element_disk!=NULL) && (inext,i++); for(;element_disk!=NULL && (i-offset)<10;i++,element_disk=element_disk->next) { wmove(stdscr,8+i-offset,0); if(element_disk!=current_disk) wdoprintf(stdscr,"%s\n",element_disk->disk->description_short(element_disk->disk)); else { wattrset(stdscr, A_REVERSE); wdoprintf(stdscr,"%s\n",element_disk->disk->description_short(element_disk->disk)); wattroff(stdscr, A_REVERSE); } } if(i<=10 && element_disk==NULL) options="OQ"; else options="PNOQ"; { int line=20; #if defined(__CYGWIN__) || defined(__MINGW32__) #else #ifndef DJGPP #ifdef HAVE_GETEUID if(geteuid()!=0) { wmove(stdscr,line++,0); wdoprintf(stdscr,"Note: Some disks won't appear unless you're root user."); } #endif #endif #endif wmove(stdscr,line++,0); if(line==22) wdoprintf(stdscr,"Disk capacity must be correctly detected for a successful recovery."); else wdoprintf(stdscr,"Note: Disk capacity must be correctly detected for a successful recovery."); wmove(stdscr,line++,0); wdoprintf(stdscr,"If a disk listed above has incorrect size, check HD jumper settings, BIOS"); wmove(stdscr,line++,0); wdoprintf(stdscr,"detection, and install the latest OS patches and disk drivers."); } if(current_cmd!=NULL) { while(current_cmd[0]==',') current_cmd++; if(current_cmd[0]=='\0') command='Q'; else if(strncmp(current_cmd,"inter",4)==0) { cmd_device=NULL; current_cmd=NULL; } else command='O'; } if(current_cmd==NULL) command = wmenuSelect_ext(stdscr,INTER_MAIN_Y, INTER_MAIN_X, menuMain, 8, options, MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, &menu,&real_key); switch(command) { case KEY_UP: case 'P': if(current_disk->prev!=NULL) { current_disk=current_disk->prev; pos_num--; } if(pos_numnext!=NULL) { current_disk=current_disk->next; pos_num++; } if(pos_num>=offset+INTER_MENU_DISK) offset++; break; case KEY_PPAGE: for(i=0;iprev!=NULL;i++) { current_disk=current_disk->prev; pos_num--; if(pos_numnext!=NULL;i++) { current_disk=current_disk->next; pos_num++; if(pos_num>=offset+INTER_MENU_DISK) offset++; } break; case 'o': case 'O': { disk_t *disk=current_disk->disk; autodetect_arch(disk); if(interface_partition_type(disk, allow_partial_last_cylinder, debug, ¤t_cmd)==0) menu_photorec(disk, debug, recup_dir, file_enable, ¤t_cmd, &list_search_space); } break; case 'q': case 'Q': done=1; break; } } end_ncurses(); log_info("\n"); return 0; } static void interface_options_photorec(int *paranoid, int *allow_partial_last_cylinder, int *keep_corrupted_file, unsigned int *mode_ext2, unsigned int *expert, unsigned int *lowmem, char**current_cmd) { if(*current_cmd!=NULL) { int keep_asking=1; do { while(*current_cmd[0]==',') (*current_cmd)++; if(strncmp(*current_cmd,"mode_ext2",9)==0) { (*current_cmd)+=9; *mode_ext2=1; } else if(strncmp(*current_cmd,"expert",6)==0) { (*current_cmd)+=6; *expert=1; } else if(strncmp(*current_cmd,"lowmem",6)==0) { (*current_cmd)+=6; *lowmem=1; } else if(strncmp(*current_cmd,"keep_corrupted_file",19)==0) { (*current_cmd)+=19; *keep_corrupted_file=1; } else keep_asking=0; } while(keep_asking>0); } else { int done = FALSE; unsigned int menu = 6; while (done==FALSE) { int car; int real_key; struct MenuItem menuOptions[]= { { 'P', NULL, "Check JPG files" }, { 'A',NULL,"" }, { 'K',NULL,"Keep corrupted files"}, { 'S',NULL,"Try to skip indirect block"}, { 'E',NULL,"Provide additional controls"}, { 'L',NULL,"Low memory"}, { 'Q',"Quit","Return to main menu"}, { 0, NULL, NULL } }; switch(*paranoid) { case 0: menuOptions[0].name="Paranoid : No"; break; case 1: menuOptions[0].name="Paranoid : Yes (Brute force disabled)"; break; default: menuOptions[0].name="Paranoid : Yes (Brute force enabled)"; break; } menuOptions[1].name=*allow_partial_last_cylinder?"Allow partial last cylinder : Yes":"Allow partial last cylinder : No"; menuOptions[2].name=*keep_corrupted_file?"Keep corrupted files : Yes":"Keep corrupted files : No"; menuOptions[3].name=*mode_ext2?"Mode ext2/ext3 : Yes":"Mode ext2/ext3 : No"; menuOptions[4].name=*expert?"Mode expert : Yes":"Mode expert : No"; menuOptions[5].name=*lowmem?"Low memory: Yes":"Low memory: No"; /* Jpg Mov Mpg Minolta MRW Canon CRW Signa/Foveon X3F Fuji RAF Rollei RDC MP3 */ aff_copy(stdscr); car=wmenuSelect_ext(stdscr,INTER_OPTION_Y, INTER_OPTION_X, menuOptions, 0, "PAKELQ", MENU_VERT|MENU_VERT_ARROW2VALID, &menu,&real_key); switch(car) { case 'p': case 'P': if(*paranoid<2) (*paranoid)++; else *paranoid=0; break; case 'a': case 'A': *allow_partial_last_cylinder=!*allow_partial_last_cylinder; break; case 'k': case 'K': *keep_corrupted_file=!*keep_corrupted_file; break; case 's': case 'S': *mode_ext2=!*mode_ext2; break; case 'e': case 'E': *expert=!*expert; break; case 'l': case 'L': *lowmem=!*lowmem; break; case key_ESC: case 'q': case 'Q': done = TRUE; break; } } } /* write new options to log file */ log_info("New options :\n Paranoid : %s\n", *paranoid?"Yes":"No"); log_info(" Brute force : %s\n", ((*paranoid)>1?"Yes":"No")); log_info(" Allow partial last cylinder : %s\n Keep corrupted files : %s\n Mode ext2/ext3 : %s\n Mode expert : %s\n Low memory : %s\n", *allow_partial_last_cylinder?"Yes":"No", *keep_corrupted_file?"Yes":"No", *mode_ext2?"Yes":"No", *expert?"Yes":"No", *lowmem?"Yes":"No"); } static void interface_file_select(file_enable_t *files_enable, char**current_cmd) { log_info("\nInterface File Select\n"); if(*current_cmd!=NULL) { int keep_asking; do { unsigned int cmd_length=0; int i; keep_asking=0; while(*current_cmd[0]==',') (*current_cmd)++; while((*current_cmd)[cmd_length]!='\0' && (*current_cmd)[cmd_length]!=',') cmd_length++; for(i=0;files_enable[i].file_hint!=NULL;i++) { if(files_enable[i].file_hint->extension!=NULL && strncmp(files_enable[i].file_hint->extension,*current_cmd,cmd_length)==0) { keep_asking=1; while(*current_cmd[0]!='\0' && *current_cmd[0]!=',') (*current_cmd)++; while(*current_cmd[0]==',') (*current_cmd)++; if(strncmp(*current_cmd,"enable",6)==0) { (*current_cmd)+=6; files_enable[i].enable=1; } else if(strncmp(*current_cmd,"disable",7)==0) { (*current_cmd)+=7; files_enable[i].enable=0; } else { log_critical("Syntax error %s\n",*current_cmd); return; } } } } while(keep_asking>0); return ; } { int quit; int offset=0; int current_element_num=0; int rewrite=1; unsigned int menu=0; do { static struct MenuItem menuAdv[]= { {'q',"Quit","Return to main menu"}, {0,NULL,NULL} }; int i; int command; if(rewrite!=0) { aff_copy(stdscr); wmove(stdscr,5,0); wdoprintf(stdscr,"PhotoRec will try to locate the following files"); rewrite=0; } wmove(stdscr,5+1,4); wclrtoeol(stdscr); if(offset>0) wdoprintf(stdscr,"Previous"); for(i=offset;files_enable[i].file_hint!=NULL && ((i-offset)extension!=NULL? files_enable[i].file_hint->extension:""), files_enable[i].file_hint->description); wattroff(stdscr, A_REVERSE); } else { wdoprintf(stdscr,"[%c] %-4s %s", (files_enable[i].enable==0?' ':'X'), (files_enable[i].file_hint->extension!=NULL? files_enable[i].file_hint->extension:""), files_enable[i].file_hint->description); } } wmove(stdscr,5+2+INTER_SELECT,4); wclrtoeol(stdscr); /* before addstr for BSD compatibility */ if(files_enable[i].file_hint!=NULL) wdoprintf(stdscr,"Next"); quit=0; command = wmenuSelect(stdscr,INTER_SELECT_Y, INTER_SELECT_X, menuAdv, 8, "q", MENU_BUTTON | MENU_ACCEPT_OTHERS, menu); switch(command) { case KEY_UP: if(current_element_num>0) { current_element_num--; if(current_element_num0);i++) { current_element_num--; if(current_element_num=offset+INTER_SELECT) offset++; } break; case KEY_NPAGE: for(i=0;(i=offset+INTER_SELECT) offset++; } break; case KEY_RIGHT: case '+': case ' ': case KEY_LEFT: case '-': case 'x': case 'X': files_enable[current_element_num].enable=1-files_enable[current_element_num].enable; break; case 's': case 'S': { file_enable_t *file_enable; int enable_status=1-files_enable[0].enable; for(file_enable=&files_enable[0];file_enable->file_hint!=NULL;file_enable++) file_enable->enable=enable_status; } break; case 'q': case 'Q': quit=1; break; } } while(quit==0); } } #ifdef DEBUG_GET_NEXT_SECTOR void bug(void) { log_critical("bug\n"); } #endif static #ifndef DEBUG_GET_NEXT_SECTOR inline #endif void get_next_sector(alloc_data_t *list_search_space, alloc_data_t **current_search_space, uint64_t *offset, const unsigned int blocksize) { #ifdef DEBUG_GET_NEXT_SECTOR log_debug(" get_next_sector %llu (%llu-%llu)\n", (unsigned long long)((*offset)/512), (unsigned long long)((*current_search_space)->start/512), (unsigned long long)((*current_search_space)->end)/512); #endif if((*current_search_space) == list_search_space) { return ; } if(! ((*current_search_space)->start <= *offset && (*offset)<=(*current_search_space)->end)) { log_critical("BUG: get_next_sector stop everything %llu (%llu-%llu)\n", (unsigned long long)((*offset)/512), (unsigned long long)((*current_search_space)->start/512), (unsigned long long)((*current_search_space)->end/512)); log_flush(); #ifdef DEBUG_GET_NEXT_SECTOR bug(); #endif exit(1); } if((*offset)+blocksize <= (*current_search_space)->end) *offset+=blocksize; else get_next_header(list_search_space, current_search_space, offset); } static #ifndef DEBUG_GET_NEXT_SECTOR inline #endif void get_next_header(alloc_data_t *list_search_space, alloc_data_t **current_search_space, uint64_t *offset) { #ifdef DEBUG_GET_NEXT_SECTOR log_trace(" get_next_header %llu (%llu-%llu)\n", (unsigned long long)((*offset)/512), (unsigned long long)((*current_search_space)->start/512), (unsigned long long)((*current_search_space)->end)/512); #endif if((*current_search_space) != list_search_space) *current_search_space=list_entry((*current_search_space)->list.next, alloc_data_t, list); *offset=(*current_search_space)->start; } /* Check if the block looks like an indirect/double-indirect block */ static inline int ind_block(const unsigned char *buffer, const unsigned int blocksize) { const uint32_t *p32=(const uint32_t *)buffer; unsigned int i; unsigned int diff=1; /* IND: Indirect block */ if(le32(p32[0])==0) return 0; if(le32(p32[1])==le32(p32[0])+blocksize/4+1) diff=blocksize/4+1; /* DIND: Double Indirect block */ for(i=0;i