diff --git a/src/file_jpg.c b/src/file_jpg.c index 0c321015..3ad1da13 100644 --- a/src/file_jpg.c +++ b/src/file_jpg.c @@ -93,6 +93,29 @@ const file_hint_t file_hint_jpg= { .register_header_check=®ister_header_check_jpg }; +static int jpg_marker_is_sof(const unsigned char marker) +{ + switch(marker) + { + case 0xc0: + case 0xc1: + case 0xc2: + case 0xc3: + case 0xc5: + case 0xc6: + case 0xc7: + case 0xc9: + case 0xca: + case 0xcb: + case 0xcd: + case 0xce: + case 0xcf: + return 1; + default: + return 0; + } +} + /*@ @ requires PHOTOREC_MAX_BLOCKSIZE >= buffer_size; @ requires \valid_read(buffer + (0 .. buffer_size-1)); @@ -120,7 +143,7 @@ static void jpg_get_size(const unsigned char *buffer, const unsigned int buffer_ /*@ assert 0 <= ((buffer[i+2]<<8) | buffer[i+3]) <= 0xffff; */ const unsigned int size=((unsigned int)buffer[i+2]<<8)|buffer[i+3]; /*@ assert size <= 0xffff; */ - if(buffer[i+1]==0xc0) /* SOF0 */ + if(jpg_marker_is_sof(buffer[i+1])!=0) { /*@ assert 0<= (buffer[i+5]<<8) <= 0xff00; */ /*@ assert 0 <= ((buffer[i+5]<<8) | buffer[i+6]) <= 0xffff; */ @@ -884,6 +907,8 @@ static int header_check_jpg(const unsigned char *buffer, const unsigned int buff /*@ assert valid_header_check_param(buffer, buffer_size, safe_header_only, file_recovery, file_recovery_new); */ unsigned int i=2; time_t jpg_time=0; + unsigned int width=0; + unsigned int height=0; /*@ @ loop invariant \valid_read(buffer+(0..buffer_size-1)); @ loop invariant \initialized(buffer+(0..buffer_size-1)); @@ -925,6 +950,7 @@ static int header_check_jpg(const unsigned char *buffer, const unsigned int buff if(i+1 < buffer_size && buffer[i+1]!=0xda) return 0; } + jpg_get_size(buffer, buffer_size, &height, &width); if(file_recovery->file_stat!=NULL && file_recovery->file_check!=NULL) { @@ -940,9 +966,6 @@ static int header_check_jpg(const unsigned char *buffer, const unsigned int buff 0x00, 0x48, 0x00, 0x00, 0xff, 0xfe, 0x00 }; - unsigned int width=0; - unsigned int height=0; - jpg_get_size(buffer, buffer_size, &height, &width); #if !defined(MAIN_jpg) && !defined(SINGLE_FORMAT) if(file_recovery->file_stat->file_hint==&file_hint_indd) { @@ -1047,6 +1070,8 @@ static int header_check_jpg(const unsigned char *buffer, const unsigned int buff } } reset_file_recovery(file_recovery_new); + file_recovery_new->image_width=width; + file_recovery_new->image_height=height; file_recovery_new->min_filesize=i; file_recovery_new->calculated_file_size=0; file_recovery_new->time=jpg_time; diff --git a/src/file_png.c b/src/file_png.c index 10a349f5..d8ddb71c 100644 --- a/src/file_png.c +++ b/src/file_png.c @@ -322,14 +322,22 @@ static int header_check_mng(const unsigned char *buffer, const unsigned int buff @*/ static int header_check_png(const unsigned char *buffer, const unsigned int buffer_size, const unsigned int safe_header_only, const file_recovery_t *file_recovery, file_recovery_t *file_recovery_new) { + const struct png_ihdr *ihdr=(const struct png_ihdr *)&buffer[16]; + unsigned int width=0; + unsigned int height=0; if( !((isupper(buffer[8+4]) || islower(buffer[8+4])) && (isupper(buffer[8+5]) || islower(buffer[8+5])) && (isupper(buffer[8+6]) || islower(buffer[8+6])) && (isupper(buffer[8+7]) || islower(buffer[8+7])))) return 0; if(memcmp(&buffer[8+4], "IHDR", 4) == 0 && - png_check_ihdr((const struct png_ihdr *)&buffer[16])==0) + png_check_ihdr(ihdr)==0) return 0; + if(memcmp(&buffer[8+4], "IHDR", 4) == 0) + { + width=be32(ihdr->width); + height=be32(ihdr->height); + } #if !defined(SINGLE_FORMAT) /* SolidWorks files contain a png */ if(file_recovery->file_stat!=NULL && @@ -340,6 +348,8 @@ static int header_check_png(const unsigned char *buffer, const unsigned int buff } #endif reset_file_recovery(file_recovery_new); + file_recovery_new->image_width=width; + file_recovery_new->image_height=height; file_recovery_new->extension=file_hint_png.extension; file_recovery_new->min_filesize=16; if(file_recovery_new->blocksize < 8) diff --git a/src/filegen.c b/src/filegen.c index 86b1ffec..febb8acc 100644 --- a/src/filegen.c +++ b/src/filegen.c @@ -510,6 +510,8 @@ void reset_file_recovery(file_recovery_t *file_recovery) file_recovery->offset_ok=0; file_recovery->checkpoint_status=0; file_recovery->checkpoint_offset=0; + file_recovery->image_width=0; + file_recovery->image_height=0; file_recovery->flags=0; file_recovery->extra=0; file_recovery->data_check_tmp=0; diff --git a/src/filegen.h b/src/filegen.h index c713eafb..f55de8cb 100644 --- a/src/filegen.h +++ b/src/filegen.h @@ -101,6 +101,8 @@ struct file_recovery_struct uint64_t checkpoint_offset; int checkpoint_status; /* 0=suspend at offset_checkpoint if offset_checkpoint>0, 1=resume at offset_checkpoint */ unsigned int blocksize; + unsigned int image_width; + unsigned int image_height; unsigned int flags; unsigned int data_check_tmp; }; @@ -164,6 +166,8 @@ typedef struct \initialized(&file_recovery->calculated_file_size) && \initialized(&file_recovery->file_check) && \initialized(&file_recovery->file_size) && + \initialized(&file_recovery->image_height) && + \initialized(&file_recovery->image_width) && \initialized(&file_recovery->min_filesize) && \initialized(&file_recovery->time) ); diff --git a/src/phmain.c b/src/phmain.c index 626e8f23..0a05b323 100644 --- a/src/phmain.c +++ b/src/phmain.c @@ -186,6 +186,10 @@ int main( int argc, char **argv ) .expert=0, .lowmem=0, .verbose=0, + .image_min_width=0, + .image_min_height=0, + .image_min_pixels=0, + .image_min_filesize=0, .list_file_format=array_file_enable }; struct ph_param params; diff --git a/src/photorec.h b/src/photorec.h index 59f9dfd8..0ea73780 100644 --- a/src/photorec.h +++ b/src/photorec.h @@ -42,6 +42,10 @@ struct ph_options unsigned int expert; unsigned int lowmem; int verbose; + unsigned int image_min_width; + unsigned int image_min_height; + uint64_t image_min_pixels; + uint64_t image_min_filesize; file_enable_t *list_file_format; }; diff --git a/src/photorec_check_header.h b/src/photorec_check_header.h index fa7e4aeb..6abbe3f7 100644 --- a/src/photorec_check_header.h +++ b/src/photorec_check_header.h @@ -154,6 +154,36 @@ static pstatus_t photorec_header_found(const file_recovery_t *file_recovery_new, return PSTATUS_OK; } +inline static int photorec_image_filter(file_recovery_t *file_recovery_new, const struct ph_options *options) +{ + const int filter_dimensions=(options->image_min_width > 0 || options->image_min_height > 0 || options->image_min_pixels > 0); + const int filter_filesize=(options->image_min_filesize > 0); + int is_filtered_image=0; + if(filter_dimensions==0 && filter_filesize==0) + return 1; + if(file_recovery_new->extension==NULL) + return 1; + if(strcmp(file_recovery_new->extension, "jpg")==0 || strcmp(file_recovery_new->extension, "png")==0) + is_filtered_image=1; + if(is_filtered_image==0 && (file_recovery_new->image_width==0 || file_recovery_new->image_height==0)) + return 1; + if(filter_dimensions!=0) + { + const uint64_t pixels=(uint64_t)file_recovery_new->image_width * (uint64_t)file_recovery_new->image_height; + if(file_recovery_new->image_width==0 || file_recovery_new->image_height==0) + return 0; + if(options->image_min_width > 0 && file_recovery_new->image_width < options->image_min_width) + return 0; + if(options->image_min_height > 0 && file_recovery_new->image_height < options->image_min_height) + return 0; + if(options->image_min_pixels > 0 && pixels < options->image_min_pixels) + return 0; + } + if(filter_filesize!=0 && file_recovery_new->min_filesize < options->image_min_filesize) + file_recovery_new->min_filesize=options->image_min_filesize; + return 1; +} + /*@ @ requires \valid(file_recovery); @ requires valid_file_recovery(file_recovery); @@ -208,6 +238,13 @@ inline static pstatus_t photorec_check_header(file_recovery_t *file_recovery, st file_check->header_check(buffer, read_size, 0, file_recovery, &file_recovery_new)!=0) { file_recovery_new.file_stat=file_check->file_stat; + if(photorec_image_filter(&file_recovery_new, options)==0) + { + file_recovery_new.file_stat=NULL; + file_recovery_new.blocksize=blocksize; + file_recovery_new.location.start=offset; + continue; + } /*@ assert valid_file_recovery(&file_recovery_new); */ return photorec_header_found(&file_recovery_new, file_recovery, params, options, list_search_space, buffer, file_recovered, offset); } diff --git a/src/phrecn.c b/src/phrecn.c index d20ea805..bde989c2 100644 --- a/src/phrecn.c +++ b/src/phrecn.c @@ -504,7 +504,8 @@ int photorec(struct ph_param *params, const struct ph_options *options, alloc_da #ifdef HAVE_NCURSES void interface_options_photorec_ncurses(struct ph_options *options) { - unsigned int menu = 5; + unsigned int menu = 6; + char image_options[128]; struct MenuItem menuOptions[]= { { 'P', NULL, "Check JPG files" }, @@ -512,6 +513,7 @@ void interface_options_photorec_ncurses(struct ph_options *options) { 'S',NULL,"Try to skip indirect block"}, { 'E',NULL,"Provide additional controls"}, { 'L',NULL,"Low memory"}, + { 'I',NULL,"Set minimum dimensions or size for recovered images"}, { 'Q',"Quit","Return to main menu"}, { 0, NULL, NULL } }; @@ -535,8 +537,19 @@ void interface_options_photorec_ncurses(struct ph_options *options) menuOptions[2].name=options->mode_ext2?"ext2/ext3 mode: Yes":"ext2/ext3 mode : No"; menuOptions[3].name=options->expert?"Expert mode : Yes":"Expert mode : No"; menuOptions[4].name=options->lowmem?"Low memory: Yes":"Low memory: No"; + if(options->image_min_width==0 && options->image_min_height==0 && + options->image_min_pixels==0 && options->image_min_filesize==0) + menuOptions[5].name="Image minimums : Off"; + else + { + snprintf(image_options, sizeof(image_options), "Image minimums : %ux%u %llu px %llu bytes", + options->image_min_width, options->image_min_height, + (long long unsigned)options->image_min_pixels, + (long long unsigned)options->image_min_filesize); + menuOptions[5].name=image_options; + } aff_copy(stdscr); - car=wmenuSelect_ext(stdscr, 23, INTER_OPTION_Y, INTER_OPTION_X, menuOptions, 0, "PKELQ", MENU_VERT|MENU_VERT_ARROW2VALID, &menu,&real_key); + car=wmenuSelect_ext(stdscr, 23, INTER_OPTION_Y, INTER_OPTION_X, menuOptions, 0, "PKSELIQ", MENU_VERT|MENU_VERT_ARROW2VALID, &menu,&real_key); switch(car) { case 'p': @@ -562,6 +575,15 @@ void interface_options_photorec_ncurses(struct ph_options *options) case 'L': options->lowmem=!options->lowmem; break; + case 'i': + case 'I': + aff_copy(stdscr); + wmove(stdscr, INTER_OPTION_Y, INTER_OPTION_X); + options->image_min_width=(unsigned int)ask_number(options->image_min_width, 0, 4294967295ULL, "Minimum image width in pixels "); + options->image_min_height=(unsigned int)ask_number(options->image_min_height, 0, 4294967295ULL, "Minimum image height in pixels "); + options->image_min_pixels=ask_number(options->image_min_pixels, 0, 0, "Minimum image width*height in pixels "); + options->image_min_filesize=ask_number(options->image_min_filesize, 0, 0, "Minimum image file size in bytes "); + break; case key_ESC: case 'q': case 'Q': diff --git a/src/poptions.c b/src/poptions.c index da28aec1..9db3a03c 100644 --- a/src/poptions.c +++ b/src/poptions.c @@ -34,6 +34,12 @@ #include "log.h" #include "poptions.h" +static unsigned int get_image_dimension_from_command(char **current_cmd) +{ + const uint64_t value=get_int_from_command(current_cmd); + return (value > 4294967295ULL ? 4294967295U : (unsigned int)value); +} + void interface_options_photorec_cli(struct ph_options *options, char **current_cmd) { if(*current_cmd==NULL) @@ -46,6 +52,8 @@ void interface_options_photorec_cli(struct ph_options *options, char **current_c @ loop assigns *current_cmd; @ loop assigns options->paranoid, options->keep_corrupted_file, options->mode_ext2; @ loop assigns options->expert, options->lowmem; + @ loop assigns options->image_min_width, options->image_min_height; + @ loop assigns options->image_min_pixels, options->image_min_filesize; @*/ while(1) { @@ -87,6 +95,26 @@ void interface_options_photorec_cli(struct ph_options *options, char **current_c { options->lowmem=1; } + else if(check_command(current_cmd,"image_min_width",15)==0) + { + skip_comma_in_command(current_cmd); + options->image_min_width=get_image_dimension_from_command(current_cmd); + } + else if(check_command(current_cmd,"image_min_height",16)==0) + { + skip_comma_in_command(current_cmd); + options->image_min_height=get_image_dimension_from_command(current_cmd); + } + else if(check_command(current_cmd,"image_min_pixels",16)==0) + { + skip_comma_in_command(current_cmd); + options->image_min_pixels=get_int_from_command(current_cmd); + } + else if(check_command(current_cmd,"image_min_filesize",18)==0) + { + skip_comma_in_command(current_cmd); + options->image_min_filesize=get_int_from_command(current_cmd); + } else { #ifndef DISABLED_FOR_FRAMAC @@ -108,4 +136,9 @@ void interface_options_photorec_log(const struct ph_options *options) options->mode_ext2?"Yes":"No", options->expert?"Yes":"No", options->lowmem?"Yes":"No"); + log_info(" Image minimum width : %u\n Image minimum height : %u\n Image minimum pixels : %llu\n Image minimum filesize : %llu\n", + options->image_min_width, + options->image_min_height, + (long long unsigned)options->image_min_pixels, + (long long unsigned)options->image_min_filesize); } diff --git a/src/qphotorec.cpp b/src/qphotorec.cpp index 3d763771..c58e022e 100644 --- a/src/qphotorec.cpp +++ b/src/qphotorec.cpp @@ -117,6 +117,10 @@ QPhotorec::QPhotorec(QWidget *my_parent) : QWidget(my_parent) options->expert=0; options->lowmem=0; options->verbose=0; + options->image_min_width=0; + options->image_min_height=0; + options->image_min_pixels=0; + options->image_min_filesize=0; options->list_file_format=array_file_enable; reset_array_file_enable(options->list_file_format); diff --git a/src/sessionp.c b/src/sessionp.c index 12841eca..89d33ecc 100644 --- a/src/sessionp.c +++ b/src/sessionp.c @@ -299,6 +299,14 @@ int session_save(const alloc_data_t *list_free_space, const struct ph_param *par fprintf(f_session, "expert,"); if(options->lowmem>0) fprintf(f_session, "lowmem,"); + if(options->image_min_width>0) + fprintf(f_session, "image_min_width,%u,", options->image_min_width); + if(options->image_min_height>0) + fprintf(f_session, "image_min_height,%u,", options->image_min_height); + if(options->image_min_pixels>0) + fprintf(f_session, "image_min_pixels,%llu,", (long long unsigned)options->image_min_pixels); + if(options->image_min_filesize>0) + fprintf(f_session, "image_min_filesize,%llu,", (long long unsigned)options->image_min_filesize); /* Save options - End */ if(params->carve_free_space_only>0) fprintf(f_session,"freespace,");