ca5c8fc157
It still need work.
837 lines
32 KiB
C
837 lines
32 KiB
C
/*
|
|
|
|
File: phbf.c
|
|
|
|
Copyright (C) 1998-2009 Christophe GRENIER <grenier@cgsecurity.org>
|
|
|
|
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 <config.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
#ifdef HAVE_TIME_H
|
|
#include <time.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_TIME_H
|
|
#include <sys/time.h>
|
|
#endif
|
|
#include "types.h"
|
|
#include "common.h"
|
|
#include "intrf.h"
|
|
#ifdef HAVE_NCURSES
|
|
#include "intrfn.h"
|
|
#endif
|
|
#include <errno.h>
|
|
#ifdef HAVE_WINDEF_H
|
|
#include <windef.h>
|
|
#endif
|
|
#ifdef HAVE_WINBASE_H
|
|
#include <stdarg.h>
|
|
#include <winbase.h>
|
|
#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[pos->has_value==0?0: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 %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->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=%u, 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; i<blocs_to_skip; i++)
|
|
{
|
|
unsigned int j,k;
|
|
int res;
|
|
*current_search_space=start_search_space;
|
|
*offset=start_search_space->start;
|
|
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; j<i; j++)
|
|
{
|
|
get_next_sector(list_search_space, current_search_space, offset, blocksize);
|
|
}
|
|
for(k=original_offset_ok/blocksize+1; k<original_offset_error/blocksize; k++)
|
|
{
|
|
disk_car->pread(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(; j<blocs_to_skip; j++)
|
|
{
|
|
get_next_sector(list_search_space, current_search_space, offset, blocksize);
|
|
}
|
|
res=photorec_bf_pad(disk_car, recup_dir, file_nbr, file_recovery, blocksize, list_search_space, dir_num, status, phase, file_recovery->offset_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, 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; i<blocs_to_skip; i++)
|
|
{
|
|
get_next_sector(list_search_space, current_search_space, offset, blocksize);
|
|
if(*current_search_space==list_search_space)
|
|
return BF_NO_FILE;
|
|
}
|
|
}
|
|
|
|
switch(photorec_bf_pad(disk_car, recup_dir, file_nbr, file_recovery, blocksize, list_search_space, dir_num, status, phase, file_offset, current_search_space, offset, buffer, block_buffer))
|
|
{
|
|
case BF_FILE_FOUND:
|
|
return BF_FILE_FOUND;
|
|
case BF_ERR_STOP:
|
|
return BF_ERR_STOP;
|
|
case BF_FRAG_FOUND:
|
|
if(frag>5)
|
|
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;
|
|
}
|