summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristophe Grenier <grenier@cgsecurity.org>2010-04-27 09:03:05 +0200
committerChristophe Grenier <grenier@cgsecurity.org>2010-04-27 09:03:05 +0200
commitd88cbb2bb02c3f89e5b0d55510c649e82632111e (patch)
tree7c0d5b797d621cb91986ea6bb8c6275204556fde
parentc1afbdf78024f1590b7805244ddc40ba16297379 (diff)
PhotoRec: improve jpg file recovery in brute-force mode
-rw-r--r--src/Makefile.am12
-rw-r--r--src/file_jpg.c961
-rw-r--r--src/suspend.c326
-rw-r--r--src/suspend.h2
-rw-r--r--src/suspend_no.c22
5 files changed, 1194 insertions, 129 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 1ce0908..3cc257f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -11,7 +11,7 @@ if USEQT
endif
sbin_PROGRAMS = testdisk photorec fidentify $(QPHOTOREC)
-EXTRA_PROGRAMS =
+EXTRA_PROGRAMS = photorecf
base_C = autoset.c common.c crc.c ewf.c fnctdsk.c hdaccess.c hdcache.c hdwin32.c hidden.c hpa_dco.c intrf.c iso.c log.c log_part.c misc.c msdos.c parti386.c partgpt.c partmac.c partsun.c partnone.c partxbox.c io_redir.c ntfs_io.c ntfs_utl.c partauto.c sudo.c unicode.c win32.c
base_H = alignio.h autoset.h common.h crc.h ewf.h fnctdsk.h hdaccess.h hdwin32.h hidden.h guid_cmp.h guid_cpy.h hdcache.h hpa_dco.h intrf.h iso.h iso9660.h lang.h log.h log_part.h misc.h types.h io_redir.h msdos.h ntfs_utl.h parti386.h partgpt.h partmac.h partsun.h partxbox.h partauto.h sudo.h unicode.h win32.h
@@ -224,7 +224,7 @@ file_C = filegen.c \
file_xv.c \
file_zip.c
-file_H = ext2.h filegen.h file_jpg.h file_sp3.h file_tar.h file_tiff.h file_txt.h list.h ole.h pe.h
+file_H = ext2.h filegen.h file_jpg.h file_sp3.h file_tar.h file_tiff.h file_txt.h list.h ole.h pe.h suspend.h
photorec_C = photorec.c phcfg.c dir.c ext2grp.c ext2_dir.c ext2p.c fat_dir.c fatp.c file_found.c list.c ntfs_dir.c ntfsp.c sessionp.c setdate.c
@@ -233,11 +233,13 @@ photorec_H = photorec.h phcfg.h dir.h ext2grp.h ext2p.h ext2_dir.h ext2_inc.h f
photorec_ncurses_C = addpart.c askloc.c chgtype.c chgtypen.c fat_cluster.c fat_unformat.c geometry.c hiddenn.c intrfn.c nodisk.c parti386n.c partgptn.c partmacn.c partsunn.c partxboxn.c pbanner.c pblocksize.c pdisksel.c pfree_whole.c phbf.c phbs.c phnc.c phrecn.c ppartsel.c
photorec_ncurses_H = addpart.h askloc.h chgtype.h chgtypen.h fat_cluster.h fat_unformat.h geometry.h hiddenn.h intrfn.h nodisk.h parti386n.h partgptn.h partmacn.h partsunn.h partxboxn.h pblocksize.h pdisksel.h pfree_whole.h pnext.h phbf.h phbs.h phnc.h phrecn.h ppartsel.h
-photorec_SOURCES = phmain.c $(photorec_C) $(photorec_H) $(photorec_ncurses_C) $(photorec_ncurses_H) $(file_C) $(file_H) $(base_C) $(base_H) partgptro.c $(fs_C) $(fs_H) $(ICON_PHOTOREC)
+photorec_SOURCES = phmain.c $(photorec_C) $(photorec_H) $(photorec_ncurses_C) $(photorec_ncurses_H) $(file_C) $(file_H) $(base_C) $(base_H) partgptro.c $(fs_C) $(fs_H) $(ICON_PHOTOREC) suspend_no.c
-qphotorec_SOURCES = qmainrec.cpp qphotorec.cpp qphotorec.h chgtype.c chgtype.h $(photorec_C) $(photorec_H) $(file_C) $(file_H) $(base_C) $(base_H) partgptro.c $(fs_C) $(fs_H) $(ICON_PHOTOREC)
+photorecf_SOURCES = phmain.c $(photorec_C) $(photorec_H) $(photorec_ncurses_C) $(photorec_ncurses_H) $(file_C) $(file_H) $(base_C) $(base_H) partgptro.c $(fs_C) $(fs_H) $(ICON_PHOTOREC) suspend.c
-fidentify_SOURCES = fidentify.c common.c common.h phcfg.c phcfg.h setdate.c setdate.h $(file_C) $(file_H) log.c log.h crc.c crc.h fat_common.c
+qphotorec_SOURCES = qmainrec.cpp qphotorec.cpp qphotorec.h chgtype.c chgtype.h $(photorec_C) $(photorec_H) $(file_C) $(file_H) $(base_C) $(base_H) partgptro.c $(fs_C) $(fs_H) $(ICON_PHOTOREC) suspend_no.c
+
+fidentify_SOURCES = fidentify.c common.c common.h phcfg.c phcfg.h setdate.c setdate.h $(file_C) $(file_H) log.c log.h crc.c crc.h fat_common.c suspend_no.c
DISTCLEANFILES = *~ core
diff --git a/src/file_jpg.c b/src/file_jpg.c
index cdb3131..771d6cc 100644
--- a/src/file_jpg.c
+++ b/src/file_jpg.c
@@ -40,6 +40,7 @@
#endif
#ifdef HAVE_JPEGLIB_H
#include <jpeglib.h>
+#include "suspend.h"
#endif
#include "filegen.h"
#include "common.h"
@@ -52,9 +53,7 @@ extern const file_hint_t file_hint_riff;
static void register_header_check_jpg(file_stat_t *file_stat);
static int header_check_jpg(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);
static void file_check_jpg(file_recovery_t *file_recovery);
-static void jpg_check_structure(file_recovery_t *file_recovery, const unsigned int extract_thumb);
-static int data_check_jpg(const unsigned char *buffer, const unsigned int buffer_size, file_recovery_t *file_recovery);
-static int data_check_jpg2(const unsigned char *buffer, const unsigned int buffer_size, file_recovery_t *file_recovery);
+int data_check_jpg(const unsigned char *buffer, const unsigned int buffer_size, file_recovery_t *file_recovery);
const file_hint_t file_hint_jpg= {
.extension="jpg",
@@ -102,10 +101,23 @@ static int header_check_jpg(const unsigned char *buffer, const unsigned int buff
{
if(buffer[i]!=0xff)
return 0;
+ /* 0x00 */
+ /* 0x01 */
+ /* 0xc4 Define Huffman table */
+ /* 0xc0 SOF0 Start of Frame */
+ /* 0xcc DAC arithmetic-coding conditioning*/
+ /* 0xcf SOF15 */
+ /* 0xd0-d7 JPEG_RST0 .. JPEG_RST7 markers */
+ /* 0xd8 SOI Start of Image */
+ /* 0xd9 EOI End of Image */
+ /* 0xda SOS: Start Of Scan */
+ /* 0xdb DQT: Define Quantization Table */
+ /* 0xdc DNL: Define Number of Lines */
+ /* 0xdd DRI: define restart interval */
+ /* 0xde DHP: define hierarchical progression */
/* 0xe0 APP0 */
/* 0xef APP15 */
/* 0xfe COM */
- /* 0xdb DQT */
if(buffer[i+1]==0xe1)
{ /* APP1 Exif information */
if(i+0x0A < buffer_size && 2+(buffer[i+2]<<8)+buffer[i+3] > 0x0A)
@@ -153,6 +165,19 @@ struct my_error_mgr {
jmp_buf setjmp_buffer; /* for return to caller */
};
+typedef struct {
+ struct jpeg_source_mgr pub; /* public fields */
+
+ FILE * infile; /* source stream */
+ JOCTET * buffer; /* start of buffer */
+ boolean start_of_file; /* have we gotten any data yet? */
+ unsigned long int offset;
+ unsigned long int file_size;
+ unsigned long int file_size_max;
+ unsigned long int offset_ok;
+ unsigned int blocksize;
+} my_source_mgr;
+
static void my_output_message (j_common_ptr cinfo);
static void my_error_exit (j_common_ptr cinfo);
static void my_emit_message (j_common_ptr cinfo, int msg_level);
@@ -199,17 +224,8 @@ static void my_emit_message (j_common_ptr cinfo, int msg_level)
}
}
-typedef struct {
- struct jpeg_source_mgr pub; /* public fields */
-
- FILE * infile; /* source stream */
- JOCTET * buffer; /* start of buffer */
- boolean start_of_file; /* have we gotten any data yet? */
- unsigned long int file_size;
- unsigned long int offset_ok;
-} my_source_mgr;
-
-#define JPG_INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */
+//#define JPG_INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */
+//#define JPG_INPUT_BUF_SIZE 512 /* TODO: use the blocksize */
/*
* Initialize source --- called by jpeg_read_header
@@ -225,8 +241,10 @@ static void jpg_init_source (j_decompress_ptr cinfo)
* This is correct behavior for reading a series of images from one source.
*/
src->start_of_file = TRUE;
+ src->offset= 0;
src->file_size = 0;
- src->offset_ok = 0;
+ src->file_size_max = 0;
+// src->offset_ok = 0;
}
@@ -267,7 +285,17 @@ static boolean jpg_fill_input_buffer (j_decompress_ptr cinfo)
{
my_source_mgr * src = (my_source_mgr *) cinfo->src;
size_t nbytes;
- nbytes = fread(src->buffer, 1, JPG_INPUT_BUF_SIZE, src->infile);
+#if 0
+ log_info("jpg_fill_input_buffer file_size=%llu -> %llu (offset=%llu, blocksize=%u)\n",
+ (long long unsigned)src->file_size,
+ (long long unsigned)src->file_size+src->blocksize - (src->offset + src->file_size)%src->blocksize,
+ (long long unsigned)src->offset,
+ src->blocksize);
+#endif
+ nbytes = fread(src->buffer, 1,
+ src->blocksize - (src->offset + src->file_size)%src->blocksize,
+// 512 - (src->offset + src->file_size)%512,
+ src->infile);
if (nbytes <= 0) {
if (src->start_of_file) /* Treat empty input file as fatal error */
{
@@ -282,6 +310,14 @@ static boolean jpg_fill_input_buffer (j_decompress_ptr cinfo)
nbytes = 2;
}
src->pub.next_input_byte = src->buffer;
+ if(src->file_size_max!=0 && src->file_size + nbytes > src->file_size_max)
+ {
+ const uint64_t off_end=(src->file_size_max > src->file_size ? src->file_size_max - src->file_size: 0);
+// memset(&src->buffer[off_end], 0, nbytes);
+ src->buffer[off_end] = (JOCTET) 0xFF;
+ src->buffer[off_end+1] = (JOCTET) JPEG_EOI;
+ nbytes=off_end+2;
+ }
src->pub.bytes_in_buffer = nbytes;
src->start_of_file = FALSE;
src->file_size += nbytes;
@@ -353,7 +389,7 @@ static void jpg_term_source (j_decompress_ptr cinfo)
* for closing it after finishing decompression.
*/
-static void jpeg_testdisk_src (j_decompress_ptr cinfo, FILE * infile)
+static void jpeg_testdisk_src (j_decompress_ptr cinfo, FILE * infile, uint64_t offset, const unsigned int blocksize)
{
my_source_mgr * src;
@@ -371,7 +407,7 @@ static void jpeg_testdisk_src (j_decompress_ptr cinfo, FILE * infile)
src = (my_source_mgr *) cinfo->src;
src->buffer = (JOCTET *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
- JPG_INPUT_BUF_SIZE * sizeof(JOCTET));
+ blocksize * sizeof(JOCTET));
}
src = (my_source_mgr *) cinfo->src;
@@ -380,108 +416,474 @@ static void jpeg_testdisk_src (j_decompress_ptr cinfo, FILE * infile)
src->pub.skip_input_data = jpg_skip_input_data;
src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
src->pub.term_source = jpg_term_source;
- src->infile = infile;
src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
src->pub.next_input_byte = NULL; /* until buffer loaded */
+ src->infile = infile;
+ src->offset = offset;
+ src->blocksize=blocksize;
}
-#endif
-static void file_check_jpg(file_recovery_t *file_recovery)
+struct jpeg_session_struct
+{
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_decompress_struct cinfo_backup;
+ unsigned char *frame;
+ unsigned int row_stride;
+ unsigned int output_components;
+ unsigned int output_width;
+ unsigned int output_height;
+ uint64_t offset;
+ FILE *handle;
+ unsigned int flags;
+ unsigned int blocksize;
+};
+
+static void jpeg_init_session(struct jpeg_session_struct *jpeg_session)
+{
+ jpeg_session->frame=NULL;
+ jpeg_session->row_stride=0;
+ jpeg_session->output_components=0;
+ jpeg_session->output_width=0;
+ jpeg_session->output_height=0;
+ jpeg_session->offset=0;
+ jpeg_session->handle=NULL;
+ jpeg_session->flags=0;
+}
+
+static void jpeg_session_delete(struct jpeg_session_struct *jpeg_session)
+{
+ jpeg_destroy_decompress(&jpeg_session->cinfo);
+ free(jpeg_session->frame);
+ jpeg_session->frame=NULL;
+ jpeg_session->row_stride=0;
+}
+
+static inline int jpeg_session_resume(struct jpeg_session_struct *jpeg_session)
+{
+ my_source_mgr * src;
+ memcpy(&jpeg_session->cinfo, &jpeg_session->cinfo_backup, sizeof(jpeg_session->cinfo));
+ if(resume_memory((j_common_ptr)&jpeg_session->cinfo))
+ return -1;
+ src = (my_source_mgr *) jpeg_session->cinfo.src;
+ fseek(jpeg_session->handle, jpeg_session->offset + src->file_size, SEEK_SET);
+ return 0;
+}
+
+static inline void jpeg_session_suspend(struct jpeg_session_struct *jpeg_session)
+{
+ suspend_memory((j_common_ptr)&jpeg_session->cinfo);
+ memcpy(&jpeg_session->cinfo_backup, &jpeg_session->cinfo, sizeof(jpeg_session->cinfo));
+}
+
+static void jpeg_session_start(struct jpeg_session_struct *jpeg_session)
+{
+ fseek(jpeg_session->handle, jpeg_session->offset, SEEK_SET);
+ jpeg_create_decompress(&jpeg_session->cinfo);
+ jpeg_testdisk_src(&jpeg_session->cinfo, jpeg_session->handle, jpeg_session->offset, jpeg_session->blocksize);
+ (void) jpeg_read_header(&jpeg_session->cinfo, TRUE);
+ jpeg_session->cinfo.two_pass_quantize = FALSE;
+ jpeg_session->cinfo.dither_mode = JDITHER_NONE;
+ jpeg_session->cinfo.dct_method = JDCT_FASTEST;
+ jpeg_session->cinfo.do_block_smoothing = FALSE;
+ jpeg_session->cinfo.do_fancy_upsampling = FALSE;
+ (void) jpeg_start_decompress(&jpeg_session->cinfo);
+ jpeg_session->output_width=jpeg_session->cinfo.output_width;
+ jpeg_session->output_height=jpeg_session->cinfo.output_height;
+ jpeg_session->output_components=jpeg_session->cinfo.output_components;
+ jpeg_session->row_stride = jpeg_session->cinfo.output_width * jpeg_session->cinfo.output_components;
+ jpeg_session->frame=NULL;
+}
+
+static uint64_t jpg_xy_to_offset(FILE *infile, const unsigned int x, const unsigned y,
+ const uint64_t offset_rel1, const uint64_t offset_rel2, const uint64_t offset, const unsigned int blocksize)
{
- FILE* infile=file_recovery->handle;
- uint64_t jpeg_size;
-#if defined(HAVE_LIBJPEG) && defined(HAVE_JPEGLIB_H)
static struct my_error_mgr jerr;
- static struct jpeg_decompress_struct cinfo;
-#endif
- file_recovery->file_size=0;
- if(file_recovery->calculated_file_size==0)
- file_recovery->offset_error=0;
+ static uint64_t file_size_max;
+ static struct jpeg_session_struct jpeg_session;
+ unsigned int checkpoint_status=0;
+ int avoid_leak=0;
+ jpeg_init_session(&jpeg_session);
+ jpeg_session.handle=infile;
+ jpeg_session.offset=offset;
+ jpeg_session.blocksize=blocksize;
+ file_size_max=(offset_rel1+blocksize-1)/blocksize*blocksize;
#ifdef DEBUG_JPEG
- log_info("%s %llu error at %llu\n", file_recovery->filename,
- (long long unsigned)file_recovery->calculated_file_size,
- (long long unsigned)file_recovery->offset_error);
+ log_info("jpg_xy_to_offset(infile, x=%u, y=%u, offset_rel1=%lu, offset_rel2=%lu)\n",
+ x, y, (long unsigned)offset_rel1, (long unsigned)offset_rel2);
#endif
- if(file_recovery->offset_error!=0)
- return ;
- jpg_check_structure(file_recovery, 0);
- if(file_recovery->offset_error!=0)
- return ;
-#if defined(HAVE_LIBJPEG) && defined(HAVE_JPEGLIB_H)
+ jpeg_session.cinfo.err = jpeg_std_error(&jerr.pub);
+ jerr.pub.output_message = my_output_message;
+ jerr.pub.error_exit = my_error_exit;
+ /* Establish the setjmp return context for my_error_exit to use. */
+ if (setjmp(jerr.setjmp_buffer))
{
- JSAMPARRAY buffer; /* Output row buffer */
- unsigned int row_stride; /* physical row width in output buffer */
- fseek(infile,0,SEEK_SET);
- cinfo.err = jpeg_std_error(&jerr.pub);
- jerr.pub.output_message = my_output_message;
- jerr.pub.error_exit = my_error_exit;
- jerr.pub.emit_message= my_emit_message;
-#ifdef DEBUG_JPEG
- jerr.pub.trace_level= 3;
-#endif
- /* Establish the setjmp return context for my_error_exit to use. */
- if (setjmp(jerr.setjmp_buffer))
+ if(jpeg_session.frame!=NULL && jpeg_session.cinfo.output_scanline >= y)
+ {
+ int data=0;
+ unsigned int i;
+ for(i=0; i< (jpeg_session.output_width-x) * jpeg_session.output_components; i++)
+ {
+ if(jpeg_session.frame[x*jpeg_session.output_components+i]!=0x80)
+ data=1;
+ }
+ if(data==1)
+ {
+ jpeg_session_delete(&jpeg_session);
+ return offset + file_size_max - blocksize;
+ }
+ }
+ file_size_max+=blocksize;
+ }
+ while(file_size_max<offset_rel2)
+ {
+ if(checkpoint_status==0 || jpeg_session_resume(&jpeg_session)<0)
+ {
+ if(avoid_leak)
+ jpeg_session_delete(&jpeg_session);
+ jpeg_session_start(&jpeg_session);
+ jpeg_session.frame = (unsigned char *)MALLOC(jpeg_session.row_stride);
+ avoid_leak=1;
+ }
{
- /* If we get here, the JPEG code has signaled an error.
- * We need to clean up the JPEG object and return.
- */
my_source_mgr * src;
- src = (my_source_mgr *) cinfo.src;
- jpeg_size=src->file_size - src->pub.bytes_in_buffer;
- log_error("JPG error, ok at %llu - bad at %llu\n",
- (long long unsigned)src->offset_ok, (long long unsigned)jpeg_size);
- jpeg_destroy_decompress(&cinfo);
- if(jpeg_size>0)
- file_recovery->offset_error=jpeg_size;
+ src = (my_source_mgr *) jpeg_session.cinfo.src;
+ src->file_size_max=file_size_max;
+ }
+ {
+ my_source_mgr * src;
+ src = (my_source_mgr *) jpeg_session.cinfo.src;
+ while (jpeg_session.cinfo.output_scanline < jpeg_session.cinfo.output_height &&
+ jpeg_session.cinfo.output_scanline < y)
+ {
+ JSAMPROW row_pointer[1];
+ row_pointer[0] = (unsigned char *)jpeg_session.frame;
+ (void)jpeg_read_scanlines(&jpeg_session.cinfo, row_pointer, 1);
+ }
+ if(src->file_size < src->file_size_max)
+ {
+ jpeg_session_suspend(&jpeg_session);
+ checkpoint_status=1;
+ }
+ if(jpeg_session.cinfo.output_scanline < jpeg_session.cinfo.output_height)
+ {
+ JSAMPROW row_pointer[1];
+ int data=0;
+ unsigned int i;
+ row_pointer[0] = (unsigned char *)jpeg_session.frame;
+ /* 0x100/2=0x80, medium value */
+ memset(jpeg_session.frame, 0x80, jpeg_session.row_stride);
+ (void)jpeg_read_scanlines(&jpeg_session.cinfo, row_pointer, 1);
+ for(i=0; i< (jpeg_session.output_width-x) * jpeg_session.output_components; i++)
+ {
+ if(jpeg_session.frame[x*jpeg_session.output_components+i]!=0x80)
+ data=1;
+ }
+ if(data==1)
+ {
+ (void) jpeg_finish_decompress(&jpeg_session.cinfo);
+ jpeg_session_delete(&jpeg_session);
+ return offset + file_size_max;
+ }
+ }
+ }
+ file_size_max+=blocksize;
+ }
+/* Do not call jpeg_finish_decompress(&cinfo); to avoid an endless loop */
+ jpeg_session_delete(&jpeg_session);
+ return 0;
+}
+
+static unsigned int is_line_cut(const unsigned int output_scanline, const unsigned int output_width, const unsigned int output_components, const unsigned char *frame, const unsigned int y)
+{
+ const unsigned int row_stride = output_width * output_components;
+ unsigned int result_max=0;
+ unsigned int result_i=0;
+ unsigned int i;
+ for(i=(8 - 1) * output_components; i<row_stride; i+=8 * output_components)
+ {
+ unsigned int result=0;
+ unsigned int j;
+ unsigned int pos;
+ for(j=0,
+ pos= y * row_stride + i;
+ j<8 && y+j < output_scanline;
+ j++, pos+=row_stride)
+ {
+ /* FIXME: out of bound read access */
+ result += abs(2 * frame[pos] - frame[pos - output_components]
+ - (pos + output_components < row_stride * output_scanline ? frame[pos + output_components]: 0));
+ }
#ifdef DEBUG_JPEG
- jpg_check_structure(file_recovery, 1);
+ log_info("y=%u x=%u i=%u result=%u\n", y, i/output_components, i, result);
#endif
- return;
+ if(result_max <= result)
+ {
+ result_max=result;
+ result_i=i;
}
- jpeg_create_decompress(&cinfo);
- cinfo.two_pass_quantize = FALSE;
- cinfo.dither_mode = JDITHER_NONE;
- cinfo.desired_number_of_colors = 0;
- cinfo.dct_method = JDCT_FASTEST;
- cinfo.do_fancy_upsampling = FALSE;
- cinfo.raw_data_out = TRUE;
-
- jpeg_testdisk_src(&cinfo, infile);
- (void) jpeg_read_header(&cinfo, TRUE);
- (void) jpeg_start_decompress(&cinfo);
- row_stride = cinfo.output_width * cinfo.output_components;
- buffer = (*cinfo.mem->alloc_sarray)
- ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
- while (cinfo.output_scanline < cinfo.output_height)
+ }
+#ifdef DEBUG_JPEG
+ log_info("y=%u result_i=%u\n", y, result_i);
+#endif
+ return (result_i != row_stride-output_components);
+}
+
+static unsigned int jpg_find_border(const unsigned int output_scanline, const unsigned int output_width, const unsigned int output_components, const unsigned char *frame)
+{
+ unsigned int y;
+#ifdef DEBUG_JPEG
+ log_info("jpg_find_border output_scanline=%u output_width=%u output_components=%u\n",
+ output_scanline, output_width, output_components);
+#endif
+ /* TODO handle output_width%8!=0 */
+ if(output_width%8!=0)
+ return output_scanline;
+ for(y=output_scanline-8; y>=8; y-=8)
+ {
+ if(!is_line_cut(output_scanline, output_width, output_components, frame, y))
+ return y+8;
+ }
+ return output_scanline;
+}
+
+/* FIXME: it doesn handle correctly when there is a few extra sectors */
+static uint64_t jpg_find_error(FILE *handle, const unsigned int output_scanline, const unsigned int output_width, const unsigned int output_components, const unsigned char *frame, const unsigned int *offsets, const uint64_t offset, const unsigned int blocksize, const uint64_t checkpoint_offset)
+{
+ const unsigned int row_stride = output_width * output_components;
+ unsigned int result=0;
+ unsigned int result_max=0;
+ unsigned int result_x;
+ unsigned int result_y;
+ unsigned int y;
+ unsigned int i;
+ unsigned int pos_new;
+ unsigned int output_scanline_max;
+ output_scanline_max=jpg_find_border(output_scanline, output_width, output_components, frame);
+ for(i = 0, pos_new= 8 * row_stride;
+ i < row_stride;
+ i++, pos_new++)
+ {
+ result += abs(2 * frame[pos_new] - frame[pos_new - row_stride] - frame[pos_new + row_stride]);
+ }
+ result_x=0;
+ result_y=8;
+ result_max=result;
+
+ for(y=8; y+8 < output_scanline; y+=8)
+ {
+ unsigned int pos;
+ for(i = 0,
+ pos = y * row_stride,
+ pos_new = (y + 8) * row_stride;
+ i < row_stride;
+ i++, pos++, pos_new++)
{
- my_source_mgr * src;
- src = (my_source_mgr *) cinfo.src;
- src->offset_ok=src->file_size - src->pub.bytes_in_buffer;
- (void)jpeg_read_scanlines(&cinfo, buffer, 1);
+ if(i % (8 * output_components)==0)
+ {
+ int stop=0;
+// log_info("x %4u, y %4u: %6u\n", i/output_components, y, result);
+ if(result_max < result)
+ {
+ // FIXME
+#if 1
+ if(2 * result_max < result) // && offset + offsets[result_x / 8] >= checkpoint_offset)
+ stop=1;
+#endif
+ result_max=result;
+ result_x=i/output_components;
+ result_y=y;
+ }
+ /* 12 is a magic value */
+#if 1
+ else if(2 * result < result_max && result_max > 12 * row_stride) // && offset + offsets[result_x / 8] >= checkpoint_offset)
+ stop=1;
+#endif
+#if 1
+ else if(y > output_scanline_max)
+ {
+ stop=1;
+ }
+#endif
+ if(stop==1
+ && is_line_cut(output_scanline, output_width, output_components, frame, y))
+ {
+ uint64_t offset_rel1,offset_rel2;
+#ifdef DEBUG_JPEG
+ log_info("x %4u, y %4u: %6u, result=%u, output_scanline_max=%u\n",
+ result_x, result_y, result_max, result, output_scanline_max);
+#endif
+ offset_rel1=offsets[result_y / 8];
+ offset_rel2=offsets[result_y / 8 + 1];
+ if(offset_rel1 < offset_rel2)
+ {
+ const uint64_t offset_error=jpg_xy_to_offset(handle, result_x, result_y,
+ offset_rel1, offset_rel2, offset, 512);
+// offset_rel1, offset_rel2, offset, blocksize);
+ if(offset_error>0)
+ return offset_error;
+ }
+ return offset + offset_rel2;
+ }
+ }
+ result -= abs(2 * frame[pos] - frame[pos - row_stride] - frame[pos + row_stride]);
+ result += abs(2 * frame[pos_new] - frame[pos_new - row_stride] - frame[pos_new + row_stride]);
}
- (void) jpeg_finish_decompress(&cinfo);
+ }
+ return 0;
+}
+
+static uint64_t jpg_check_thumb(FILE *infile, const uint64_t offset, const unsigned int blocksize, const uint64_t checkpoint_offset, const unsigned int flags)
+{
+ static struct my_error_mgr jerr;
+ static unsigned int offsets[10240];
+ static struct jpeg_session_struct jpeg_session;
+ jpeg_init_session(&jpeg_session);
+ jpeg_session.flags=flags;
+ jpeg_session.handle=infile;
+ jpeg_session.offset=offset;
+ jpeg_session.blocksize=blocksize;
+ jpeg_session.cinfo.err = jpeg_std_error(&jerr.pub);
+ jerr.pub.output_message = my_output_message;
+ jerr.pub.error_exit = my_error_exit;
+ jerr.pub.emit_message= my_emit_message;
+#ifdef DEBUG_JPEG
+ jerr.pub.trace_level= 3;
+#endif
+ /* Establish the setjmp return context for my_error_exit to use. */
+ if (setjmp(jerr.setjmp_buffer))
+ {
+ /* If we get here, the JPEG code has signaled an error.
+ * We need to clean up the JPEG object and return.
+ */
+ uint64_t offset_error;
+ my_source_mgr * src;
+ src = (my_source_mgr *) jpeg_session.cinfo.src;
+ offset_error=jpeg_session.offset + src->file_size - src->pub.bytes_in_buffer;
+ if(jpeg_session.frame!=NULL && jpeg_session.flags!=0)
{
- my_source_mgr * src;
- src = (my_source_mgr *) cinfo.src;
- jpeg_size=src->file_size - src->pub.bytes_in_buffer;
+// log_info("jpg_check_thumb jpeg corrupted near %llu\n", offset_error);
+ offset_error=jpg_find_error(jpeg_session.handle, jpeg_session.cinfo.output_scanline, jpeg_session.output_width, jpeg_session.output_components, jpeg_session.frame, &offsets[0], jpeg_session.offset, blocksize, checkpoint_offset);
+ log_info("jpg_check_thumb find_error estimation %llu\n", (long long unsigned)offset_error);
}
- jpeg_destroy_decompress(&cinfo);
+ jpeg_session_delete(&jpeg_session);
+ return offset_error;
+ }
+ memset(offsets, 0, sizeof(offsets));
+ jpeg_session_start(&jpeg_session);
+ jpeg_session.frame = (unsigned char*)MALLOC(jpeg_session.output_height * jpeg_session.row_stride);
+ /* 0x100/2=0x80, medium value */
+ memset(jpeg_session.frame, 0x80, jpeg_session.row_stride * jpeg_session.cinfo.output_height);
+ while (jpeg_session.cinfo.output_scanline < jpeg_session.cinfo.output_height)
+ {
+ JSAMPROW row_pointer[1];
+ my_source_mgr * src;
+ src = (my_source_mgr *) jpeg_session.cinfo.src;
+ src->offset_ok=src->file_size - src->pub.bytes_in_buffer;
+ if(jpeg_session.cinfo.output_scanline/8 < 10240 && offsets[jpeg_session.cinfo.output_scanline/8]==0)
+ offsets[jpeg_session.cinfo.output_scanline/8]=src->file_size - src->pub.bytes_in_buffer;
+ // Calculate where this line needs to go.
+ row_pointer[0] = (unsigned char *)jpeg_session.frame + jpeg_session.cinfo.output_scanline * jpeg_session.row_stride;
+ (void)jpeg_read_scanlines(&jpeg_session.cinfo, row_pointer, 1);
}
+ (void) jpeg_finish_decompress(&jpeg_session.cinfo);
+ jpeg_session_delete(&jpeg_session);
+ return 0;
+}
+
+static void jpg_check_picture(file_recovery_t *file_recovery)
+{
+ static struct my_error_mgr jerr;
+ static unsigned int offsets[10240];
+ uint64_t jpeg_size=0;
+ static struct jpeg_session_struct jpeg_session;
+ static int jpeg_session_initialised=0;
+ if(file_recovery->checkpoint_status==0)
+ {
+ if(jpeg_session_initialised==1)
+ jpeg_session_delete(&jpeg_session);
+ jpeg_init_session(&jpeg_session);
+ jpeg_session.flags=file_recovery->flags;
+ jpeg_session_initialised=1;
+ jpeg_session.blocksize=file_recovery->blocksize;
+ }
+ jpeg_session.handle=file_recovery->handle;
+ jpeg_session.cinfo.err = jpeg_std_error(&jerr.pub);
+ jerr.pub.output_message = my_output_message;
+ jerr.pub.error_exit = my_error_exit;
+ jerr.pub.emit_message= my_emit_message;
+#ifdef DEBUG_JPEG
+ jerr.pub.trace_level= 3;
#endif
-// log_error("JPG size: %llu\n", (long long unsigned)jpeg_size);
- if(jpeg_size<=0)
- return;
-#if defined(HAVE_LIBJPEG) && defined(HAVE_JPEGLIB_H)
- if(jerr.pub.num_warnings>0)
+ /* Establish the setjmp return context for my_error_exit to use. */
+ if (setjmp(jerr.setjmp_buffer))
{
- file_recovery->offset_error=jpeg_size;
+ /* If we get here, the JPEG code has signaled an error.
+ * We need to clean up the JPEG object and return.
+ */
+ my_source_mgr * src;
+ src = (my_source_mgr *) jpeg_session.cinfo.src;
+ jpeg_size=src->file_size - src->pub.bytes_in_buffer;
+ if(jpeg_size>0)
+ file_recovery->offset_error=jpeg_size;
+ if(file_recovery->offset_ok < src->offset_ok)
+ file_recovery->offset_ok=src->offset_ok;
+#ifdef DEBUG_JPEG
+ log_error("JPG error, ok at %llu - bad at %llu\n",
+ (long long unsigned)file_recovery->offset_ok,
+ (long long unsigned)file_recovery->offset_error);
+#endif
+#if 1
+ if(jpeg_session.frame!=NULL && jpeg_session.flags!=0)
+ {
+ uint64_t offset_error;
+ offset_error=jpg_find_error(jpeg_session.handle, jpeg_session.cinfo.output_scanline, jpeg_session.output_width, jpeg_session.output_components, jpeg_session.frame, &offsets[0], jpeg_session.offset, jpeg_session.blocksize, file_recovery->checkpoint_offset);
+ if(offset_error !=0 && file_recovery->offset_error > offset_error)
+ file_recovery->offset_error=offset_error;
#ifdef DEBUG_JPEG
- log_error("JPG warning: %llu\n", (long long unsigned)jpeg_size);
- jpg_check_structure(file_recovery, 1);
+ log_error("JPG error, ok at %llu - bad at %llu (jpg_find_error)\n",
+ (long long unsigned)file_recovery->offset_ok,
+ (long long unsigned)file_recovery->offset_error);
+#endif
+ }
#endif
+ jpeg_session_delete(&jpeg_session);
return;
}
-#endif
+ memset(offsets, 0, sizeof(offsets));
+ jpeg_session_start(&jpeg_session);
+ {
+ my_source_mgr * src;
+ src = (my_source_mgr *) jpeg_session.cinfo.src;
+ src->file_size_max=file_recovery->file_size;
+ }
+ jpeg_session.frame = (unsigned char *)MALLOC(jpeg_session.output_height * jpeg_session.row_stride);
+ /* 0x100/2=0x80, medium value */
+ memset(jpeg_session.frame, 0x80, jpeg_session.row_stride * jpeg_session.cinfo.output_height);
+ while (jpeg_session.cinfo.output_scanline < jpeg_session.cinfo.output_height)
+ {
+ JSAMPROW row_pointer[1];
+ my_source_mgr * src;
+ src = (my_source_mgr *) jpeg_session.cinfo.src;
+ src->offset_ok=src->file_size - src->pub.bytes_in_buffer;
+ if(jpeg_session.cinfo.output_scanline/8 < 10240 && offsets[jpeg_session.cinfo.output_scanline/8]==0)
+ {
+ offsets[jpeg_session.cinfo.output_scanline/8]=src->file_size - src->pub.bytes_in_buffer;
+ }
+ // Calculate where this line needs to go.
+ row_pointer[0] = (unsigned char *)jpeg_session.frame + jpeg_session.cinfo.output_scanline * jpeg_session.row_stride;
+ (void)jpeg_read_scanlines(&jpeg_session.cinfo, row_pointer, 1);
+ }
+ {
+ my_source_mgr * src;
+ src = (my_source_mgr *) jpeg_session.cinfo.src;
+ jpeg_size=src->file_size - src->pub.bytes_in_buffer;
+ }
+ (void) jpeg_finish_decompress(&jpeg_session.cinfo);
+ jpeg_session_delete(&jpeg_session);
+ jpeg_session_initialised=0;
+ file_recovery->checkpoint_status=0;
+ if(jpeg_size<=0)
+ return;
if(file_recovery->calculated_file_size>0)
file_recovery->file_size=file_recovery->calculated_file_size;
else
@@ -490,30 +892,132 @@ static void file_check_jpg(file_recovery_t *file_recovery)
file_search_footer(file_recovery, jpg_footer, sizeof(jpg_footer), 0);
}
}
+#endif
+
+static int jpg_check_dht(const unsigned char *buffer, const unsigned int buffer_size, const unsigned i, const unsigned int size)
+{
+ /* DHT must not be shorter than 18 bytes, 1+16+1 */
+ /* DHT should not be longer than 1088 bytes, 4*(1+16+255) */
+ if(size<18)
+ return 2;
+ if(i + 4 < buffer_size)
+ {
+ unsigned int j=i+4;
+ const unsigned int n=buffer[j] & 0x0f;
+ /* Must be between 0 and 3 Huffman table */
+ if(n > 3)
+ return 2;
+ {
+ unsigned int l;
+ unsigned int sum=0;
+ if(j < buffer_size && (buffer[j]&0x0f)>3)
+ return 2;
+ j++;
+ for(l=0; l < 16; l++)
+ if(j < buffer_size)
+ sum+=buffer[j+l];
+ if(sum>255)
+ return 2;
+ j+=16;
+ j+=sum;
+ }
+ if(j > i+size)
+ return 2;
+ }
+ return 0;
+}
+
+static void jpg_search_marker(file_recovery_t *file_recovery)
+{
+ FILE* infile=file_recovery->handle;
+ unsigned char buffer[40*8192];
+ int nbytes;
+ uint64_t offset;
+ unsigned int i;
+ if(file_recovery->blocksize==0)
+ return ;
+ offset=file_recovery->offset_error / file_recovery->blocksize * file_recovery->blocksize;
+ i=file_recovery->offset_error % file_recovery->blocksize;
+ fseek(infile, offset, SEEK_SET);
+ do
+ {
+ while((nbytes=fread(&buffer, 1, sizeof(buffer), infile))>0)
+ {
+ for(;i+1<nbytes; i+=file_recovery->blocksize)
+ {
+ if(buffer[i]==0xff &&
+ (buffer[i+1]==0xd8 || /* SOI */
+ buffer[i+1]==0xdb || /* DQT */
+ (buffer[i+1]>=0xc0 && buffer[i+1]<=0xcf) || /* SOF0 - SOF15, 0xc4=DHT */
+ buffer[i+1]==0xda || /* SOS: Start Of Scan */
+ buffer[i+1]==0xdd || /* DRI */
+ (buffer[i+1]>=0xe0 && buffer[i+1]<=0xef) || /* APP0 - APP15 */
+ buffer[i+1]==0xfe) /* COM */
+ )
+ {
+ file_recovery->extra=offset + i - file_recovery->offset_error;
+ if(file_recovery->extra % file_recovery->blocksize != 0)
+ {
+ log_info("jpg_search_marker %s extra=%llu\n",
+ file_recovery->filename,
+ (long long unsigned)file_recovery->extra);
+ }
+ return ;
+ }
+ }
+ }
+ offset +=nbytes;
+ i=i % file_recovery->blocksize;
+ } while(nbytes == sizeof(buffer));
+ return ;
+}
-static void jpg_check_structure(file_recovery_t *file_recovery, const unsigned int extract_thumb)
+static uint64_t jpg_check_structure(file_recovery_t *file_recovery, const unsigned int extract_thumb)
{
FILE* infile=file_recovery->handle;
unsigned char buffer[40*8192];
+ uint64_t thumb_offset=0;
int nbytes;
+ file_recovery->extra=0;
fseek(infile, 0, SEEK_SET);
if((nbytes=fread(&buffer, 1, sizeof(buffer), infile))>0)
{
- unsigned int offset=2;
- while(offset < nbytes)
+ unsigned int offset;
+ file_recovery->offset_error=0;
+ for(offset=file_recovery->blocksize; offset < nbytes && file_recovery->offset_error==0; offset+=file_recovery->blocksize)
+ {
+ if(buffer[offset]==0xff && buffer[offset+1]==0xd8 && buffer[offset+2]==0xff &&
+ (buffer[offset+3]==0xe0 || buffer[offset+3]==0xe1 || buffer[offset+3]==0xec))
+ {
+ file_recovery->offset_error=offset;
+ }
+ }
+ offset=2;
+ while(offset + 4 < nbytes && (file_recovery->offset_error==0 || offset < file_recovery->offset_error))
{
const unsigned int i=offset;
const unsigned int size=(buffer[i+2]<<8)+buffer[i+3];
if(buffer[i]!=0xff)
{
+#ifdef DEBUG_JPEG
+ log_info("%s no marker at 0x%x\n", file_recovery->filename, i);
+#endif
file_recovery->offset_error=i;
- return;
+ jpg_search_marker(file_recovery);
+ return thumb_offset;
}
+#ifdef DEBUG_JPEG
+ log_info("%s marker 0x%02x at 0x%x\n", file_recovery->filename, buffer[i+1], i);
+#endif
offset+=2+size;
if(buffer[i+1]==0xda) /* SOS: Start Of Scan */
- return;
+ {
+ file_recovery->offset_ok=i+1;
+ return thumb_offset;
+ }
else if(buffer[i+1]==0xe1)
{ /* APP1 Exif information */
+#if 1
if(i+0x0A < nbytes && 2+size > 0x0A)
{
const TIFFHeader *tiff=(const TIFFHeader*)&buffer[i+0x0A];
@@ -524,39 +1028,89 @@ static void jpg_check_structure(file_recovery_t *file_recovery, const unsigned i
tiff_size=nbytes - (i+0x0A);
thumb_data=find_tag_from_tiff_header(tiff, tiff_size, TIFFTAG_JPEGIFOFFSET);
if(thumb_data!=NULL)
+ {
+ thumb_offset=thumb_data-(const char*)buffer;
ifbytecount=find_tag_from_tiff_header(tiff, tiff_size, TIFFTAG_JPEGIFBYTECOUNT);
+ }
+ if(file_recovery->offset_ok<i)
+ file_recovery->offset_ok=i;
if(thumb_data!=NULL && ifbytecount!=NULL)
{
- const unsigned int thumb_offset=thumb_data-(const char*)buffer;
const unsigned int thumb_size=ifbytecount-(const char*)tiff;
- if(thumb_offset < sizeof(buffer) && thumb_offset+thumb_size < sizeof(buffer))
+ if(thumb_offset < nbytes - 1)
{
unsigned int j=thumb_offset+2;
unsigned int thumb_sos_found=0;
unsigned int j_old;
j_old=j;
- while(j+4<sizeof(buffer) && thumb_sos_found==0)
+ if(buffer[thumb_offset]!=0xff)
+ {
+ file_recovery->offset_error=thumb_offset;
+ jpg_search_marker(file_recovery);
+ return 0;
+ }
+ if(buffer[thumb_offset+1]!=0xd8)
+ {
+ file_recovery->offset_error=thumb_offset+1;
+ return 0;
+ }
+ while(j+4<nbytes && thumb_sos_found==0)
{
if(buffer[j]!=0xff)
{
file_recovery->offset_error=j;
#ifdef DEBUG_JPEG
+ log_info("%s thumb no marker at 0x%x\n", file_recovery->filename, j);
log_error("%s Error between %u and %u\n", file_recovery->filename, j_old, j);
#endif
- return;
+ jpg_search_marker(file_recovery);
+ return 0;
}
+#ifdef DEBUG_JPEG
+ log_info("%s thumb marker 0x%02x at 0x%x\n", file_recovery->filename, buffer[j+1], j);
+#endif
if(buffer[j+1]==0xda) /* Thumb SOS: Start Of Scan */
thumb_sos_found=1;
+ else if(buffer[j+1]==0xc4) /* DHT */
+ {
+#if 1
+ if(jpg_check_dht(buffer, nbytes, j, 2+(buffer[j+2]<<8)+buffer[j+3])!=0)
+ {
+ file_recovery->offset_error=j+2;
+ return 0;
+ }
+#endif
+ }
+ else if(buffer[j+1]==0xdb || /* DQT */
+ buffer[j+1]==0xc0 || /* SOF0 */
+ buffer[j+1]==0xdd) /* DRI */
+ {
+ }
+ else if((buffer[j+1]>=0xc0 && buffer[j+1]<=0xcf) || /* SOF0 - SOF15 */
+ (buffer[j+1]>=0xe0 && buffer[j+1]<=0xef) || /* APP0 - APP15 */
+ buffer[j+1]==0xfe) /* COM */
+ {
+ /* Unusual marker, bug ? */
+ }
+ else
+ {
+ log_info("%s thumb unknown marker 0x%02x at 0x%x\n", file_recovery->filename, buffer[j+1], j);
+ file_recovery->offset_error=j;
+ return 0;
+ }
+ if(file_recovery->offset_ok<j)
+ file_recovery->offset_ok=j;
j_old=j;
j+=2+(buffer[j+2]<<8)+buffer[j+3];
}
- if(thumb_sos_found>0 && extract_thumb>0)
+ if(thumb_sos_found>0 && extract_thumb>0
+ && offset < nbytes && buffer[offset]==0xff)
{
char *thumbname;
char *sep;
thumbname=strdup(file_recovery->filename);
sep=strrchr(thumbname,'/');
- if(sep!=NULL && *(sep+1)=='f' && thumb_offset+thumb_size < sizeof(buffer))
+ if(sep!=NULL && *(sep+1)=='f' && thumb_offset+thumb_size < nbytes)
{
FILE *out;
*(sep+1)='t';
@@ -578,39 +1132,133 @@ static void jpg_check_structure(file_recovery_t *file_recovery, const unsigned i
}
}
}
- return ;
+#endif
+ }
+ else if(buffer[i+1]==0xc4) /* DHT */
+ {
+#if 1
+ if(jpg_check_dht(buffer, nbytes, i, 2+size)!=0)
+ {
+ file_recovery->offset_error=i+2;
+ return thumb_offset;
+ }
+#endif
+ if(file_recovery->offset_ok<i+1)
+ file_recovery->offset_ok=i+1;
+ }
+ else if(buffer[i+1]==0xdb || /* DQT */
+ (buffer[i+1]>=0xc0 && buffer[i+1]<=0xcf) || /* SOF0 - SOF15 */
+ buffer[i+1]==0xdd || /* DRI */
+ (buffer[i+1]>=0xe0 && buffer[i+1]<=0xef) || /* APP0 - APP15 */
+ buffer[i+1]==0xfe) /* COM */
+ {
+ if(file_recovery->offset_ok<i+1)
+ file_recovery->offset_ok=i+1;
+ }
+ else
+ {
+ log_info("%s unknown marker 0x%02x at 0x%x\n", file_recovery->filename, buffer[i+1], i+1);
+ file_recovery->offset_error=i+1;
+ return thumb_offset;
}
}
+ if(offset > nbytes && nbytes < sizeof(buffer))
+ {
+ file_recovery->offset_error=nbytes;
+ return thumb_offset;
+ }
}
+ return thumb_offset;
}
-static int data_check_jpg(const unsigned char *buffer, const unsigned int buffer_size, file_recovery_t *file_recovery)
+static void file_check_jpg(file_recovery_t *file_recovery)
{
- while(file_recovery->calculated_file_size + buffer_size/2 >= file_recovery->file_size &&
- file_recovery->calculated_file_size + 4 < file_recovery->file_size + buffer_size/2)
+ uint64_t thumb_offset;
+ static uint64_t thumb_error=0;
+ /* FIXME REMOVE ME */
+ file_recovery->flags=1;
+ file_recovery->file_size=0;
+ if(file_recovery->calculated_file_size==0)
+ file_recovery->offset_error=0;
+#ifdef DEBUG_JPEG
+ log_info("file_check_jpg %s calculated_file_size=%llu, error at %llu\n", file_recovery->filename,
+ (long long unsigned)file_recovery->calculated_file_size,
+ (long long unsigned)file_recovery->offset_error);
+#endif
+ if(file_recovery->offset_error!=0)
+ return ;
+ thumb_offset=jpg_check_structure(file_recovery, 1);
+#ifdef DEBUG_JPEG
+ log_info("jpg_check_structure error at %llu\n", (long long unsigned)file_recovery->offset_error);
+#endif
+#if defined(HAVE_LIBJPEG) && defined(HAVE_JPEGLIB_H)
+ if(thumb_offset!=0 &&
+ (file_recovery->checkpoint_status==0 || thumb_error!=0) &&
+ (file_recovery->offset_error==0 || thumb_offset < file_recovery->offset_error))
{
- const unsigned int i=file_recovery->calculated_file_size - file_recovery->file_size + buffer_size/2;
- if(buffer[i]==0xFF)
+#ifdef DEBUG_JPEG
+ log_info("jpg_check_thumb\n");
+#endif
+ thumb_error=jpg_check_thumb(file_recovery->handle, thumb_offset, file_recovery->blocksize, file_recovery->checkpoint_offset, file_recovery->flags);
+ if(thumb_error!=0)
{
- const unsigned int size=(buffer[i+2]<<8)+buffer[i+3];
- file_recovery->calculated_file_size+=2+size;
- if(buffer[i+1]==0xda) /* SOS: Start Of Scan */
+#ifdef DEBUG_JPEG
+ log_info("%s thumb corrupted at %llu, previous error at %llu\n",
+ file_recovery->filename, (long long unsigned)thumb_error,
+ (long long unsigned)file_recovery->offset_error);
+#endif
+ if(file_recovery->offset_error==0 || file_recovery->offset_error > thumb_error)
{
- file_recovery->data_check=&data_check_jpg2;
- return data_check_jpg2(buffer, buffer_size, file_recovery);
+#ifdef DEBUG_JPEG
+ log_info("Thumb usefull\n");
+#endif
+ file_recovery->offset_error = thumb_error;
}
}
- else
- {
- return 2;
- }
}
- return 1;
+#endif
+ if(file_recovery->offset_error!=0)
+ return ;
+#if defined(HAVE_LIBJPEG) && defined(HAVE_JPEGLIB_H)
+ jpg_check_picture(file_recovery);
+#else
+ file_recovery->file_size=file_recovery->calculated_file_size;
+#endif
+#if 0
+ /* FIXME REMOVE ME */
+ if(file_recovery->offset_error!=0)
+ {
+ file_recovery->file_size=file_recovery->offset_error;
+ file_recovery->offset_error=0;
+ fseek(file_recovery->handle, file_recovery->file_size, SEEK_SET);
+ fwrite(jpg_footer, sizeof(jpg_footer), 1, file_recovery->handle);
+ file_recovery->file_size+=2;
+ return ;
+ }
+#endif
}
static int data_check_jpg2(const unsigned char *buffer, const unsigned int buffer_size, file_recovery_t *file_recovery)
{
- while(file_recovery->calculated_file_size + buffer_size/2 >= file_recovery->file_size &&
+#if 0
+ unsigned int old_marker=0;
+#endif
+ if(file_recovery->calculated_file_size<2)
+ {
+ /* Reset to the correct file checker */
+ file_recovery->data_check=&data_check_jpg;
+ return data_check_jpg(buffer, buffer_size, file_recovery);
+ }
+ if(buffer[buffer_size/2]==0xff && buffer[buffer_size/2]==0xd8 &&
+ file_recovery->calculated_file_size != file_recovery->file_size)
+ {
+#ifdef DEBUG_JPEG
+ log_info("%s data_check_jpg2 0xffd8 at 0x%llx\n", file_recovery->filename,
+ (long long unsigned)file_recovery->calculated_file_size);
+#endif
+ return 2;
+ }
+ while(file_recovery->calculated_file_size + buffer_size/2 > file_recovery->file_size &&
file_recovery->calculated_file_size < file_recovery->file_size + buffer_size/2)
{
const unsigned int i=file_recovery->calculated_file_size - file_recovery->file_size + buffer_size/2;
@@ -625,9 +1273,26 @@ static int data_check_jpg2(const unsigned char *buffer, const unsigned int buffe
else if(buffer[i] >= 0xd0 && buffer[i] <= 0xd7)
{
/* JPEG_RST0 .. JPEG_RST7 markers */
+#if 0
+ if((buffer[i]==0xd0 && old_marker!=0 && old_marker!=0xd7) ||
+ (buffer[i]!=0xd0 && old_marker+1 != buffer[i]))
+ {
+#ifdef DEBUG_JPEG
+ log_info("Rejected due to JPEG_RST marker\n");
+#endif
+ file_recovery->calculated_file_size++;
+ return 2;
+ }
+ /* TODO: store old_marker in file_recovery */
+ old_marker=buffer[i];
+#endif
}
else if(buffer[i]!=0x00)
{
+#ifdef DEBUG_JPEG
+ log_info("%s data_check_jpg2 marker 0x%02x at 0x%llx\n", file_recovery->filename, buffer[i],
+ (long long unsigned)file_recovery->calculated_file_size);
+#endif
file_recovery->offset_error=file_recovery->calculated_file_size;
return 2;
}
@@ -636,3 +1301,51 @@ static int data_check_jpg2(const unsigned char *buffer, const unsigned int buffe
}
return 1;
}
+
+int data_check_jpg(const unsigned char *buffer, const unsigned int buffer_size, file_recovery_t *file_recovery)
+{
+ if(buffer[buffer_size/2]==0xff && buffer[buffer_size/2]==0xd8 &&
+ file_recovery->calculated_file_size != file_recovery->file_size)
+ {
+ log_info("data_check_jpg ffd8\n");
+ return 2;
+ }
+ /* Skip the SOI */
+ if(file_recovery->calculated_file_size==0)
+ file_recovery->calculated_file_size+=2;
+ /* Search SOS */
+ while(file_recovery->calculated_file_size + buffer_size/2 >= file_recovery->file_size &&
+ file_recovery->calculated_file_size + 4 < file_recovery->file_size + buffer_size/2)
+ {
+ const unsigned int i=file_recovery->calculated_file_size - file_recovery->file_size + buffer_size/2;
+ if(buffer[i]==0xFF)
+ {
+ const unsigned int size=(buffer[i+2]<<8)+buffer[i+3];
+#if 0
+ log_info("data_check_jpg %02x%02x at %llu, next expected at %llu\n", buffer[i], buffer[i+1],
+ (long long unsigned)file_recovery->calculated_file_size,
+ (long long unsigned)file_recovery->calculated_file_size+2+size);
+#endif
+ file_recovery->calculated_file_size+=2+size;
+ if(buffer[i+1]==0xc4) /* DHT */
+ {
+ if(jpg_check_dht(buffer, buffer_size, i, 2+size)!=0)
+ return 2;
+ }
+ if(buffer[i+1]==0xda) /* SOS: Start Of Scan */
+ {
+ file_recovery->data_check=&data_check_jpg2;
+ return data_check_jpg2(buffer, buffer_size, file_recovery);
+ }
+ }
+ else
+ {
+#if 0
+ log_info("data_check_jpg %02x at %llu\n", buffer[i],
+ (long long unsigned)file_recovery->calculated_file_size);
+#endif
+ return 2;
+ }
+ }
+ return 1;
+}
diff --git a/src/suspend.c b/src/suspend.c
new file mode 100644
index 0000000..5a6bda1
--- /dev/null
+++ b/src/suspend.c
@@ -0,0 +1,326 @@
+/*
+
+ File: suspend.c
+ A suspending/resuming memory manager for libjpeg
+
+ Copyright (C) 2009 Christophe GRENIER <grenier@cgsecurity.org>
+ Copyright (C) 2008 Michael Cohen <scudette@users.sourceforge.net>, part of pyflag
+
+ 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
+
+// This should be large enough to handle most memory requests. On
+// linux it can be rediculously large because the system will only
+// actually allocate the memory when we write on it.
+#define POOL_SIZE 4 * 1024 * 1024
+
+#define AM_MEMORY_MANAGER /* we define jvirt_Xarray_control structs */
+#ifdef HAVE_JPEGLIB_H
+#include <jpeglib.h>
+#endif
+#include "types.h"
+#include "common.h"
+#include "log.h"
+
+#if defined(HAVE_LIBJPEG) && defined(HAVE_JPEGLIB_H)
+static void *alloc_small(j_common_ptr cinfo, int pool_id, size_t sizeofobject);
+
+struct my_memory_mgr {
+ struct jpeg_memory_mgr pub; /* public fields */
+
+ // This is for libjpegs benefit:
+ jvirt_sarray_ptr virt_sarray_list;
+ jvirt_barray_ptr virt_barray_list;
+ JDIMENSION last_rowsperchunk; /* from most recent alloc_sarray/barray */
+
+ // All memory is allocated to this pool.
+ char *pool;
+ char *shadow_pool;
+
+ // A highwater mark for pool allocations
+ long pool_size;
+
+ // This is the very end of the allocated pool.
+ long total_space_allocated;
+ long total_space_shadowed;
+
+};
+
+typedef struct my_memory_mgr *my_mem_ptr;
+#include "jerror.h"
+#include "suspend.h"
+
+#ifndef ALIGN_TYPE /* so can override from jconfig.h */
+#define ALIGN_TYPE double
+#endif
+
+#ifndef MAX_ALLOC_CHUNK /* may be overridden in jconfig.h */
+#define MAX_ALLOC_CHUNK 1000000000L
+#endif
+
+#define SIZEOF sizeof
+
+/*
+ * The control blocks for virtual arrays.
+ * Note that these blocks are allocated in the "small" pool area.
+ * System-dependent info for the associated backing store (if any) is hidden
+ * inside the backing_store_info struct.
+ */
+
+struct jvirt_sarray_control {
+ JSAMPARRAY mem_buffer; /* => the in-memory buffer */
+ JDIMENSION rows_in_array; /* total virtual array height */
+ JDIMENSION samplesperrow; /* width of array (and of memory buffer) */
+ JDIMENSION maxaccess; /* max rows accessed by access_virt_sarray */
+ JDIMENSION rows_in_mem; /* height of memory buffer */
+ JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */
+ JDIMENSION cur_start_row; /* first logical row # in the buffer */
+ JDIMENSION first_undef_row; /* row # of first uninitialized row */
+ boolean pre_zero; /* pre-zero mode requested? */
+ boolean dirty; /* do current buffer contents need written? */
+ boolean b_s_open; /* is backing-store data valid? */
+ jvirt_sarray_ptr next; /* link to next virtual sarray control block */
+};
+
+struct jvirt_barray_control {
+ JBLOCKARRAY mem_buffer; /* => the in-memory buffer */
+ JDIMENSION rows_in_array; /* total virtual array height */
+ JDIMENSION blocksperrow; /* width of array (and of memory buffer) */
+ JDIMENSION maxaccess; /* max rows accessed by access_virt_barray */
+ JDIMENSION rows_in_mem; /* height of memory buffer */
+ JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */
+ JDIMENSION cur_start_row; /* first logical row # in the buffer */
+ JDIMENSION first_undef_row; /* row # of first uninitialized row */
+ boolean pre_zero; /* pre-zero mode requested? */
+ boolean dirty; /* do current buffer contents need written? */
+ boolean b_s_open; /* is backing-store data valid? */
+ jvirt_barray_ptr next; /* link to next virtual barray control block */
+};
+
+static void *alloc_small (j_common_ptr cinfo, int pool_id, size_t sizeofobject) {
+ struct my_memory_mgr *self = (struct my_memory_mgr *)(cinfo->mem);
+ char *obj_ptr = self->pool + self->total_space_allocated;
+
+ // log_info("allocating %u bytes\n", sizeofobject);
+
+ self->total_space_allocated += sizeofobject;
+ if(self->total_space_allocated > self->pool_size)
+ {
+ log_critical("suspend.c: no memory left\n");
+ return NULL;
+ }
+
+ return obj_ptr;
+}
+
+METHODDEF(JSAMPARRAY) alloc_sarray (j_common_ptr cinfo, int pool_id,
+ JDIMENSION samplesperrow, JDIMENSION numrows) {
+ JSAMPARRAY result;
+ JDIMENSION i;
+ result = (JSAMPARRAY) alloc_small(cinfo, pool_id, (size_t) (numrows * SIZEOF(JSAMPROW)));
+
+ for(i=0; i<numrows; i++) {
+ result[i] = (JSAMPROW) alloc_small(cinfo, pool_id,
+ (size_t) ((size_t) samplesperrow * SIZEOF(JSAMPLE)));
+ }
+ return result;
+}
+
+
+METHODDEF(jvirt_sarray_ptr) request_virt_sarray (j_common_ptr cinfo, int pool_id, boolean pre_zero,
+ JDIMENSION samplesperrow, JDIMENSION numrows,
+ JDIMENSION maxaccess) {
+ my_mem_ptr mem = (my_mem_ptr) cinfo->mem;
+ jvirt_sarray_ptr result;
+
+ result = (jvirt_sarray_ptr) alloc_small(cinfo, pool_id,
+ SIZEOF(struct jvirt_sarray_control));
+ result->mem_buffer = NULL;
+ result->rows_in_array = numrows;
+ result->samplesperrow = samplesperrow;
+ result->maxaccess = maxaccess;
+ result->pre_zero = pre_zero;
+ result->b_s_open = FALSE;
+ result->next = mem->virt_sarray_list;
+ mem->virt_sarray_list = result;
+ return result;
+};
+
+METHODDEF(void) realize_virt_arrays (j_common_ptr cinfo) {
+ my_mem_ptr mem = (my_mem_ptr) cinfo->mem;
+ long space_per_minheight, maximum_space, avail_mem;
+ long minheights, max_minheights;
+ jvirt_sarray_ptr sptr;
+ jvirt_barray_ptr bptr;
+
+ space_per_minheight = 0;
+ maximum_space = 0;
+ for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) {
+ if (sptr->mem_buffer == NULL) { /* if not realized yet */
+ space_per_minheight += (long) sptr->maxaccess *
+ (long) sptr->samplesperrow * SIZEOF(JSAMPLE);
+ maximum_space += (long) sptr->rows_in_array *
+ (long) sptr->samplesperrow * SIZEOF(JSAMPLE);
+ }
+ }
+ for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) {
+ if (bptr->mem_buffer == NULL) { /* if not realized yet */
+ space_per_minheight += (long) bptr->maxaccess *
+ (long) bptr->blocksperrow * SIZEOF(JBLOCK);
+ maximum_space += (long) bptr->rows_in_array *
+ (long) bptr->blocksperrow * SIZEOF(JBLOCK);
+ }
+ }
+
+ if (space_per_minheight <= 0)
+ return; /* no unrealized arrays, no work */
+
+ /* Determine amount of memory to actually use */
+ avail_mem = maximum_space;
+ max_minheights = 1000000000L;
+
+ /* Allocate the in-memory buffers and initialize backing store as needed. */
+ for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) {
+ if (sptr->mem_buffer == NULL) { /* if not realized yet */
+ minheights = ((long) sptr->rows_in_array - 1L) / sptr->maxaccess + 1L;
+ sptr->rows_in_mem = sptr->rows_in_array;
+ sptr->mem_buffer = alloc_sarray(cinfo, JPOOL_IMAGE,
+ sptr->samplesperrow, sptr->rows_in_mem);
+ sptr->rowsperchunk = mem->last_rowsperchunk;
+ sptr->cur_start_row = 0;
+ sptr->first_undef_row = 0;
+ sptr->dirty = FALSE;
+ }
+ }
+
+ for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) {
+ if (bptr->mem_buffer == NULL) { /* if not realized yet */
+ minheights = ((long) bptr->rows_in_array - 1L) / bptr->maxaccess + 1L;
+ bptr->rows_in_mem = bptr->rows_in_array;
+ bptr->mem_buffer = (JBLOCKARRAY)alloc_sarray(cinfo, JPOOL_IMAGE,
+ bptr->blocksperrow, bptr->rows_in_mem);
+ bptr->rowsperchunk = mem->last_rowsperchunk;
+ bptr->cur_start_row = 0;
+ bptr->first_undef_row = 0;
+ bptr->dirty = FALSE;
+ }
+ }
+}
+
+METHODDEF(JSAMPARRAY) access_virt_sarray (j_common_ptr cinfo, jvirt_sarray_ptr ptr,
+ JDIMENSION start_row, JDIMENSION num_rows,
+ boolean writable) {
+ if (writable)
+ ptr->dirty = TRUE;
+ return ptr->mem_buffer + (start_row - ptr->cur_start_row);
+}
+
+METHODDEF(void) free_pool (j_common_ptr cinfo, int pool_id) {
+ struct my_memory_mgr *self = (struct my_memory_mgr *)(cinfo->mem);
+ // log_info("Freeing pool\n");
+ // self->total_space_allocated = 0;
+}
+
+METHODDEF(void) self_destruct (j_common_ptr cinfo) {
+ struct my_memory_mgr *self = (struct my_memory_mgr *)(cinfo->mem);
+ //log_info("Destroying pool\n");
+ //free(self->pool);
+ //free(self->shadow_pool);
+}
+
+void suspend_memory(j_common_ptr cinfo) {
+ struct my_memory_mgr *self = (struct my_memory_mgr *)(cinfo->mem);
+
+// log_info("Suspending at sector %u\n", sector);
+ memcpy(self->shadow_pool, self->pool, self->total_space_allocated);
+ self->total_space_shadowed = self->total_space_allocated;
+};
+
+int resume_memory(j_common_ptr cinfo)
+{
+ struct my_memory_mgr *self = (struct my_memory_mgr *)(cinfo->mem);
+// log_info("Resuming from sector %u (copying %lu bytes)\n", self->sector, self->total_space_shadowed);
+ memcpy(self->pool, self->shadow_pool, self->total_space_shadowed);
+ self->total_space_allocated = self->total_space_shadowed;
+ return 0;
+};
+
+GLOBAL(void) jinit_memory_mgr (j_common_ptr cinfo);
+GLOBAL(void) jinit_memory_mgr (j_common_ptr cinfo)
+{
+ static my_mem_ptr mem=NULL;
+ long max_to_use;
+ size_t test_mac;
+
+ cinfo->mem = NULL;
+ if ((SIZEOF(ALIGN_TYPE) & (SIZEOF(ALIGN_TYPE)-1)) != 0)
+ ERREXIT(cinfo, JERR_BAD_ALIGN_TYPE);
+ test_mac = (size_t) MAX_ALLOC_CHUNK;
+ if ((long) test_mac != MAX_ALLOC_CHUNK ||
+ (MAX_ALLOC_CHUNK % SIZEOF(ALIGN_TYPE)) != 0)
+ ERREXIT(cinfo, JERR_BAD_ALLOC_CHUNK);
+
+ max_to_use = 0;
+ if(mem == NULL)
+ mem = (struct my_memory_mgr *)MALLOC(sizeof(struct my_memory_mgr));
+
+ // Prepare our memory pools:
+ if(mem->pool == NULL)
+ mem->pool = (char *)MALLOC(POOL_SIZE);
+ if(mem->shadow_pool == NULL)
+ mem->shadow_pool = (char *)MALLOC(POOL_SIZE);
+ mem->pool_size = POOL_SIZE;
+
+ /* OK, fill in the method pointers */
+ mem->pub.alloc_small = alloc_small;
+ mem->pub.alloc_large = alloc_small;
+ mem->pub.alloc_sarray = alloc_sarray;
+ mem->pub.alloc_barray = alloc_sarray;
+
+ mem->pub.request_virt_sarray = request_virt_sarray;
+ mem->pub.request_virt_barray = request_virt_sarray;
+ mem->pub.realize_virt_arrays = realize_virt_arrays;
+ mem->pub.access_virt_sarray = access_virt_sarray;
+ mem->pub.access_virt_barray = access_virt_sarray;
+ mem->pub.free_pool = free_pool;
+ mem->pub.self_destruct = self_destruct;
+
+ /* Make MAX_ALLOC_CHUNK accessible to other modules */
+ mem->pub.max_alloc_chunk = MAX_ALLOC_CHUNK;
+
+ /* Initialize working state */
+ mem->pub.max_memory_to_use = max_to_use;
+ mem->virt_sarray_list = NULL;
+ mem->virt_barray_list = NULL;
+ mem->total_space_allocated = SIZEOF(struct my_memory_mgr);
+
+ /* Declare ourselves open for business */
+ cinfo->mem = & mem->pub;
+}
+#endif
diff --git a/src/suspend.h b/src/suspend.h
new file mode 100644
index 0000000..7e6779b
--- /dev/null
+++ b/src/suspend.h
@@ -0,0 +1,2 @@
+void suspend_memory(j_common_ptr cinfo);
+int resume_memory(j_common_ptr cinfo);
diff --git a/src/suspend_no.c b/src/suspend_no.c
new file mode 100644
index 0000000..a5bdc30
--- /dev/null
+++ b/src/suspend_no.c
@@ -0,0 +1,22 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+#if defined(HAVE_LIBJPEG) && defined(HAVE_JPEGLIB_H)
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <jpeglib.h>
+#include "suspend.h"
+
+void suspend_memory(j_common_ptr cinfo) {
+};
+
+int resume_memory(j_common_ptr cinfo)
+{
+ /* Can't resume */
+ return -1;
+};
+#endif