PhotoRec: when a file is recovered, check again each block after the end of the recovered file
This commit is contained in:
parent
f013c26602
commit
26ea4b9117
3 changed files with 145 additions and 100 deletions
|
@ -275,6 +275,26 @@ unsigned int photorec_mkdir(const char *recup_dir, const unsigned int initial_di
|
|||
return dir_num;
|
||||
}
|
||||
|
||||
void get_prev_location(alloc_data_t *list_search_space, alloc_data_t **current_search_space, uint64_t *offset, const uint64_t prev_location)
|
||||
{
|
||||
int nbr;
|
||||
alloc_data_t *file_space=*current_search_space;
|
||||
uint64_t size=0;
|
||||
/* Search backward the first fragment of a file not successfully recovered
|
||||
* Limit the search to 10 fragments or 1GB */
|
||||
for(nbr=0; nbr<10 && size < (uint64_t)1024*1024*1024; nbr++)
|
||||
{
|
||||
file_space=td_list_entry(file_space->list.prev, alloc_data_t, list);
|
||||
if(file_space==list_search_space)
|
||||
return;
|
||||
size+=file_space->end - file_space->start + 1;
|
||||
if(file_space->start < prev_location)
|
||||
return ;
|
||||
*current_search_space=file_space;
|
||||
*offset=file_space->start;
|
||||
}
|
||||
}
|
||||
|
||||
int get_prev_file_header(alloc_data_t *list_search_space, alloc_data_t **current_search_space, uint64_t *offset)
|
||||
{
|
||||
int nbr;
|
||||
|
|
|
@ -63,6 +63,7 @@ struct ph_param
|
|||
uint64_t offset;
|
||||
};
|
||||
|
||||
void get_prev_location(alloc_data_t *list_search_space, alloc_data_t **current_search_space, uint64_t *offset, const uint64_t prev_location);
|
||||
int get_prev_file_header(alloc_data_t *list_search_space, alloc_data_t **current_search_space, uint64_t *offset);
|
||||
int file_finish_bf(file_recovery_t *file_recovery, struct ph_param *params,
|
||||
alloc_data_t *list_search_space);
|
||||
|
|
224
src/psearchn.c
224
src/psearchn.c
|
@ -107,6 +107,122 @@ static FILE *fopen_with_retry(const char *path, const char *mode)
|
|||
}
|
||||
#endif
|
||||
|
||||
static void photorec_dir_fat(const unsigned char *buffer, const unsigned int read_size, const unsigned long long sector)
|
||||
{
|
||||
file_info_t dir_list = {
|
||||
.list = TD_LIST_HEAD_INIT(dir_list.list),
|
||||
.name = NULL
|
||||
};
|
||||
dir_fat_aux(buffer, read_size, 0, &dir_list);
|
||||
if(!td_list_empty(&dir_list.list))
|
||||
{
|
||||
log_info("Sector %llu\n", sector);
|
||||
dir_aff_log(NULL, &dir_list);
|
||||
delete_list_file(&dir_list);
|
||||
}
|
||||
}
|
||||
|
||||
static pstatus_t photorec_new_file(file_recovery_t *file_recovery, struct ph_param *params, const uint64_t offset)
|
||||
{
|
||||
set_filename(file_recovery, params);
|
||||
if(file_recovery->file_stat->file_hint->recover==1)
|
||||
{
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__)
|
||||
file_recovery->handle=fopen_with_retry(file_recovery->filename,"w+b");
|
||||
#else
|
||||
file_recovery->handle=fopen(file_recovery->filename,"w+b");
|
||||
#endif
|
||||
if(!file_recovery->handle)
|
||||
{
|
||||
log_critical("Cannot create file %s: %s\n", file_recovery->filename, strerror(errno));
|
||||
params->offset=offset;
|
||||
return PSTATUS_EACCES;
|
||||
}
|
||||
}
|
||||
return PSTATUS_OK;
|
||||
}
|
||||
|
||||
static pstatus_t photorec_header_found(file_recovery_t *file_recovery_new, file_recovery_t *file_recovery, struct ph_param *params, const struct ph_options *options, alloc_data_t *list_search_space, const unsigned char *buffer, int *file_recovered, alloc_data_t **current_search_space, uint64_t *offset)
|
||||
{
|
||||
if(file_recovery_new->file_stat!=NULL && file_recovery_new->file_stat->file_hint!=NULL)
|
||||
{
|
||||
file_recovery_new->location.start=*offset;
|
||||
if(file_recovery->file_stat!=NULL)
|
||||
{
|
||||
if(options->verbose > 1)
|
||||
log_trace("A known header has been found, recovery of the previous file is finished\n");
|
||||
*file_recovered=file_finish2(file_recovery, params, options->paranoid, list_search_space);
|
||||
if(*file_recovered==1)
|
||||
get_prev_location(list_search_space, current_search_space, offset, file_recovery->location.start);
|
||||
if(options->lowmem > 0)
|
||||
forget(list_search_space, *current_search_space);
|
||||
}
|
||||
if(*file_recovered==0)
|
||||
{
|
||||
file_recovery_cpy(file_recovery, file_recovery_new);
|
||||
if(options->verbose > 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-params->partition->part_offset)/params->disk->sector_size));
|
||||
log_info("file_recovery->location.start=%lu\n",
|
||||
(unsigned long)(file_recovery->location.start/params->disk->sector_size));
|
||||
}
|
||||
|
||||
if(file_recovery->file_stat->file_hint==&file_hint_dir && options->verbose > 0)
|
||||
{ /* FAT directory found, list the file */
|
||||
const unsigned int blocksize=params->blocksize;
|
||||
const unsigned int read_size=(blocksize>65536?blocksize:65536);
|
||||
photorec_dir_fat(buffer, read_size, file_recovery->location.start/params->disk->sector_size);
|
||||
}
|
||||
return photorec_new_file(file_recovery, params, *offset);
|
||||
}
|
||||
}
|
||||
return PSTATUS_OK;
|
||||
}
|
||||
|
||||
static pstatus_t photorec_check_header(file_recovery_t *file_recovery, struct ph_param *params, const struct ph_options *options, alloc_data_t *list_search_space, const unsigned char *buffer, int *file_recovered, alloc_data_t **current_search_space, uint64_t *offset)
|
||||
{
|
||||
struct td_list_head *tmpl;
|
||||
const unsigned int blocksize=params->blocksize;
|
||||
const unsigned int read_size=(blocksize>65536?blocksize:65536);
|
||||
file_recovery_t file_recovery_new;
|
||||
file_recovery_new.blocksize=blocksize;
|
||||
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)
|
||||
return PSTATUS_OK;
|
||||
if(file_recovery->file_stat!=NULL && file_recovery->file_stat->file_hint==&file_hint_tar &&
|
||||
header_check_tar(buffer-0x200,0x200, 0, file_recovery, &file_recovery_new))
|
||||
{ /* Currently saving a tar, do not check the data for know header */
|
||||
if(options->verbose > 1)
|
||||
{
|
||||
log_verbose("Currently saving a tar file, sector %lu.\n",
|
||||
(unsigned long)((*offset-params->partition->part_offset)/params->disk->sector_size));
|
||||
}
|
||||
return PSTATUS_OK;
|
||||
}
|
||||
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);
|
||||
file_recovery_new.location.start=*offset;
|
||||
td_list_for_each(tmp, &pos->file_checks[buffer[pos->offset]].list)
|
||||
{
|
||||
const file_check_t *file_check=td_list_entry(tmp, file_check_t, list);
|
||||
if((file_check->length==0 || memcmp(buffer + file_check->offset, file_check->value, file_check->length)==0) &&
|
||||
file_check->header_check(buffer, read_size, 0, file_recovery, &file_recovery_new)!=0)
|
||||
{
|
||||
file_recovery_new.file_stat=file_check->file_stat;
|
||||
return photorec_header_found(&file_recovery_new, file_recovery, params, options, list_search_space, buffer, file_recovered, current_search_space, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
return PSTATUS_OK;
|
||||
}
|
||||
|
||||
pstatus_t photorec_aux(struct ph_param *params, const struct ph_options *options, alloc_data_t *list_search_space)
|
||||
{
|
||||
uint64_t offset;
|
||||
|
@ -164,106 +280,7 @@ pstatus_t photorec_aux(struct ph_param *params, const struct ph_options *options
|
|||
exit(1);
|
||||
}
|
||||
#endif
|
||||
{
|
||||
file_recovery_t file_recovery_new;
|
||||
file_recovery_new.blocksize=blocksize;
|
||||
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 &&
|
||||
header_check_tar(buffer-0x200,0x200,0,&file_recovery,&file_recovery_new))
|
||||
{ /* Currently saving a tar, do not check the data for know header */
|
||||
if(options->verbose > 1)
|
||||
{
|
||||
log_verbose("Currently saving a tar file, sector %lu.\n",
|
||||
(unsigned long)((offset-params->partition->part_offset)/params->disk->sector_size));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
struct td_list_head *tmpl;
|
||||
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);
|
||||
file_recovery_new.location.start=offset;
|
||||
td_list_for_each(tmp, &pos->file_checks[buffer[pos->offset]].list)
|
||||
{
|
||||
const file_check_t *file_check=td_list_entry(tmp, file_check_t, list);
|
||||
if((file_check->length==0 || memcmp(buffer + file_check->offset, file_check->value, file_check->length)==0) &&
|
||||
file_check->header_check(buffer, read_size, 0, &file_recovery, &file_recovery_new)!=0)
|
||||
{
|
||||
file_recovery_new.file_stat=file_check->file_stat;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(file_recovery_new.file_stat!=NULL)
|
||||
break;
|
||||
}
|
||||
if(file_recovery_new.file_stat!=NULL && file_recovery_new.file_stat->file_hint!=NULL)
|
||||
{
|
||||
file_recovery_new.location.start=offset;
|
||||
if(file_recovery.file_stat!=NULL)
|
||||
{
|
||||
if(options->verbose > 1)
|
||||
log_trace("A known header has been found, recovery of the previous file is finished\n");
|
||||
file_recovered=file_finish2(&file_recovery, params, options->paranoid, list_search_space);
|
||||
if(options->lowmem > 0)
|
||||
forget(list_search_space,current_search_space);
|
||||
}
|
||||
if(file_recovered==0)
|
||||
{
|
||||
file_recovery_cpy(&file_recovery, &file_recovery_new);
|
||||
if(options->verbose > 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-params->partition->part_offset)/params->disk->sector_size));
|
||||
log_info("file_recovery.location.start=%lu\n",
|
||||
(unsigned long)(file_recovery.location.start/params->disk->sector_size));
|
||||
}
|
||||
|
||||
if(file_recovery.file_stat->file_hint==&file_hint_dir && options->verbose > 0)
|
||||
{ /* FAT directory found, list the file */
|
||||
file_info_t dir_list = {
|
||||
.list = TD_LIST_HEAD_INIT(dir_list.list),
|
||||
.name = NULL
|
||||
};
|
||||
dir_fat_aux(buffer, read_size, 0, &dir_list);
|
||||
if(!td_list_empty(&dir_list.list))
|
||||
{
|
||||
log_info("Sector %lu\n",
|
||||
(unsigned long)(file_recovery.location.start/params->disk->sector_size));
|
||||
dir_aff_log(NULL, &dir_list);
|
||||
delete_list_file(&dir_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(file_recovery.file_stat!=NULL && file_recovery.handle==NULL)
|
||||
{
|
||||
set_filename(&file_recovery, params);
|
||||
if(file_recovery.file_stat->file_hint->recover==1)
|
||||
{
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__)
|
||||
file_recovery.handle=fopen_with_retry(file_recovery.filename,"w+b");
|
||||
#else
|
||||
file_recovery.handle=fopen(file_recovery.filename,"w+b");
|
||||
#endif
|
||||
if(!file_recovery.handle)
|
||||
{
|
||||
log_critical("Cannot create file %s: %s\n", file_recovery.filename, strerror(errno));
|
||||
ind_stop=PSTATUS_EACCES;
|
||||
params->offset=offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ind_stop=photorec_check_header(&file_recovery, params, options, list_search_space, buffer, &file_recovered, ¤t_search_space, &offset);
|
||||
if(file_recovery.file_stat!=NULL)
|
||||
{
|
||||
/* try to skip ext2/ext3 indirect block */
|
||||
|
@ -330,6 +347,8 @@ pstatus_t photorec_aux(struct ph_param *params, const struct ph_options *options
|
|||
}
|
||||
if(res==DC_STOP || res==DC_ERROR)
|
||||
{
|
||||
if(res==DC_ERROR)
|
||||
file_recovery.file_size=0;
|
||||
file_recovered=file_finish2(&file_recovery, params, options->paranoid, list_search_space);
|
||||
if(options->lowmem > 0)
|
||||
forget(list_search_space,current_search_space);
|
||||
|
@ -357,7 +376,10 @@ pstatus_t photorec_aux(struct ph_param *params, const struct ph_options *options
|
|||
get_prev_file_header(list_search_space, ¤t_search_space, &offset)==0)
|
||||
back++;
|
||||
else
|
||||
{
|
||||
back=0;
|
||||
get_prev_location(list_search_space, ¤t_search_space, &offset, file_recovery.location.start);
|
||||
}
|
||||
}
|
||||
if(current_search_space==list_search_space)
|
||||
{
|
||||
|
@ -367,6 +389,8 @@ pstatus_t photorec_aux(struct ph_param *params, const struct ph_options *options
|
|||
log_trace("End of media\n");
|
||||
#endif
|
||||
file_recovered=file_finish2(&file_recovery, params, options->paranoid, list_search_space);
|
||||
if(file_recovered==1)
|
||||
get_prev_location(list_search_space, ¤t_search_space, &offset, file_recovery.location.start);
|
||||
if(options->lowmem > 0)
|
||||
forget(list_search_space,current_search_space);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue