641 lines
25 KiB
C
641 lines
25 KiB
C
/*
|
|
|
|
File: file_mov.c
|
|
|
|
Copyright (C) 1998-2008 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.
|
|
|
|
*/
|
|
|
|
#if !defined(SINGLE_FORMAT) || defined(SINGLE_FORMAT_mov) || defined(SINGLE_FORMAT_mov_mdat)
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include "types.h"
|
|
#include "filegen.h"
|
|
#include "common.h"
|
|
#include "log.h"
|
|
#if defined(__FRAMAC__)
|
|
#include "__fc_builtin.h"
|
|
#endif
|
|
|
|
/*@ requires valid_register_header_check(file_stat); */
|
|
static void register_header_check_mov(file_stat_t *file_stat);
|
|
/*@ requires valid_register_header_check(file_stat); */
|
|
static void register_header_check_mov_mdat(file_stat_t *file_stat);
|
|
|
|
const file_hint_t file_hint_mov= {
|
|
.extension="mov",
|
|
.description="mov/mp4/3gp/3g2/jp2",
|
|
.max_filesize=PHOTOREC_MAX_FILE_SIZE,
|
|
.recover=1,
|
|
.enable_by_default=1,
|
|
.register_header_check=®ister_header_check_mov
|
|
};
|
|
|
|
const file_hint_t file_hint_mov_mdat= {
|
|
.extension="mov/mdat",
|
|
.description="Recover mdat atom as a separate file",
|
|
.max_filesize=PHOTOREC_MAX_FILE_SIZE,
|
|
.recover=1,
|
|
.enable_by_default=0,
|
|
.register_header_check=®ister_header_check_mov_mdat
|
|
};
|
|
|
|
static const char *extension_mp4="mp4";
|
|
static const char *extension_m4a="m4a";
|
|
static const char *extension_3gp="3gp";
|
|
static const char *extension_3g2="3g2";
|
|
static const char *extension_heic="heic";
|
|
static const char *extension_jp2="jp2";
|
|
static const char *extension_cr3="cr3";
|
|
|
|
struct atom_struct
|
|
{
|
|
uint32_t size;
|
|
uint32_t type;
|
|
} __attribute__ ((gcc_struct, __packed__));
|
|
|
|
struct atom64_struct
|
|
{
|
|
uint32_t size1;
|
|
uint32_t type;
|
|
uint64_t size;
|
|
} __attribute__ ((gcc_struct, __packed__));
|
|
|
|
/*@
|
|
@ requires file_recovery->file_rename == &file_rename_mov;
|
|
@ requires valid_file_rename_param(file_recovery);
|
|
@ ensures valid_file_rename_result(file_recovery);
|
|
@*/
|
|
static void file_rename_mov(file_recovery_t *file_recovery)
|
|
{
|
|
FILE *file;
|
|
unsigned char buffer[512];
|
|
if((file=fopen(file_recovery->filename, "rb"))==NULL)
|
|
return;
|
|
if(fread(&buffer,sizeof(buffer),1,file)!=1)
|
|
{
|
|
fclose(file);
|
|
return ;
|
|
}
|
|
fclose(file);
|
|
#if defined(__FRAMAC__)
|
|
Frama_C_make_unknown((char *)buffer, sizeof(buffer));
|
|
#endif
|
|
buffer[8]='\0';
|
|
file_rename(file_recovery, buffer, sizeof(buffer), 4, NULL, 1);
|
|
}
|
|
|
|
/*@
|
|
@ requires \valid_read(atom + (0 .. 3));
|
|
@ assigns \nothing;
|
|
@*/
|
|
static inline int is_known_atom(const unsigned char *atom)
|
|
{
|
|
if( (atom[0]=='c' && atom[1]=='m' && atom[2]=='o' && atom[3]=='v') ||
|
|
(atom[0]=='c' && atom[1]=='m' && atom[2]=='v' && atom[3]=='d') ||
|
|
(atom[0]=='d' && atom[1]=='c' && atom[2]=='o' && atom[3]=='m') ||
|
|
(atom[0]=='f' && atom[1]=='r' && atom[2]=='e' && atom[3]=='a') ||
|
|
(atom[0]=='f' && atom[1]=='r' && atom[2]=='e' && atom[3]=='e') ||
|
|
(atom[0]=='f' && atom[1]=='t' && atom[2]=='y' && atom[3]=='p') ||
|
|
(atom[0]=='j' && atom[1]=='p' && atom[2]=='2' && atom[3]=='h') ||
|
|
(atom[0]=='m' && atom[1]=='d' && atom[2]=='i' && atom[3]=='a') ||
|
|
(atom[0]=='m' && atom[1]=='e' && atom[2]=='t' && atom[3]=='a') ||
|
|
(atom[0]=='m' && atom[1]=='o' && atom[2]=='o' && atom[3]=='v') ||
|
|
(atom[0]=='P' && atom[1]=='I' && atom[2]=='C' && atom[3]=='T') ||
|
|
(atom[0]=='p' && atom[1]=='n' && atom[2]=='o' && atom[3]=='t') ||
|
|
(atom[0]=='s' && atom[1]=='k' && atom[2]=='i' && atom[3]=='p') ||
|
|
(atom[0]=='s' && atom[1]=='t' && atom[2]=='b' && atom[3]=='l') ||
|
|
(atom[0]=='t' && atom[1]=='h' && atom[2]=='u' && atom[3]=='m') ||
|
|
(atom[0]=='t' && atom[1]=='r' && atom[2]=='a' && atom[3]=='k') ||
|
|
(atom[0]=='u' && atom[1]=='u' && atom[2]=='i' && atom[3]=='d') ||
|
|
(atom[0]=='w' && atom[1]=='i' && atom[2]=='d' && atom[3]=='e') )
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/*@
|
|
@ requires buffer_size >= 16;
|
|
@ requires file_recovery->data_check==&data_check_mov;
|
|
@ requires valid_data_check_param(buffer, buffer_size, file_recovery);
|
|
@ ensures valid_data_check_result(\result, file_recovery);
|
|
@ assigns file_recovery->calculated_file_size;
|
|
@*/
|
|
static data_check_t data_check_mov(const unsigned char *buffer, const unsigned int buffer_size, file_recovery_t *file_recovery)
|
|
{
|
|
/*@ assert file_recovery->calculated_file_size <= PHOTOREC_MAX_FILE_SIZE; */
|
|
/*@ assert file_recovery->file_size <= PHOTOREC_MAX_FILE_SIZE; */
|
|
/*@
|
|
@ loop assigns file_recovery->calculated_file_size;
|
|
@*/
|
|
while(file_recovery->calculated_file_size + buffer_size/2 >= file_recovery->file_size &&
|
|
file_recovery->calculated_file_size + 8 <= file_recovery->file_size + buffer_size/2)
|
|
{
|
|
const unsigned int i=file_recovery->calculated_file_size + buffer_size/2 - file_recovery->file_size;
|
|
/*@ assert 0 <= i <= buffer_size - 8 ; */
|
|
const struct atom_struct *atom=(const struct atom_struct*)&buffer[i];
|
|
/*@ assert \valid_read(atom); */
|
|
uint64_t atom_size=be32(atom->size);
|
|
if(atom_size==1)
|
|
{
|
|
const struct atom64_struct *atom64;
|
|
if(i + 16 > buffer_size)
|
|
{
|
|
return DC_CONTINUE;
|
|
}
|
|
/*@ assert i + 16 <= buffer_size; */
|
|
atom64=(const struct atom64_struct*)&buffer[i];
|
|
/*@ assert \valid_read(atom64); */
|
|
atom_size=be64(atom64->size);
|
|
if(atom_size<16)
|
|
return DC_STOP;
|
|
/*@ assert atom_size >= 16; */
|
|
}
|
|
else if(atom_size<8)
|
|
return DC_STOP;
|
|
/*@ assert atom_size >= 8; */
|
|
if(atom_size >= 0x800000000000)
|
|
return DC_STOP;
|
|
/*@ assert 8 <= atom_size < 0x800000000000; */
|
|
#ifdef DEBUG_MOV
|
|
log_trace("file_mov.c: %s atom %c%c%c%c (0x%02x%02x%02x%02x) size %llu, calculated_file_size %llu\n",
|
|
file_recovery->filename,
|
|
buffer[i+4],buffer[i+5],buffer[i+6],buffer[i+7],
|
|
buffer[i+4],buffer[i+5],buffer[i+6],buffer[i+7],
|
|
(long long unsigned)atom_size,
|
|
(long long unsigned)file_recovery->calculated_file_size);
|
|
#endif
|
|
if(buffer[i+4]=='m' && buffer[i+5]=='d' && buffer[i+6]=='a' && buffer[i+7]=='t')
|
|
{
|
|
file_recovery->calculated_file_size+=atom_size;
|
|
#if 0
|
|
if(i+8 == buffer_size)
|
|
{
|
|
return -((atom_size + buffer_size/2 - 1)/ (buffer_size/2));
|
|
}
|
|
#endif
|
|
}
|
|
else if(is_known_atom(&buffer[i+4]))
|
|
{
|
|
file_recovery->calculated_file_size+=atom_size;
|
|
}
|
|
else
|
|
{
|
|
#ifndef DISABLED_FOR_FRAMAC
|
|
if(!(buffer[i+4]==0 && buffer[i+5]==0 && buffer[i+6]==0 && buffer[i+7]==0))
|
|
log_warning("file_mov.c: unknown atom 0x%02x%02x%02x%02x at %llu\n",
|
|
buffer[i+4],buffer[i+5],buffer[i+6],buffer[i+7],
|
|
(long long unsigned)file_recovery->calculated_file_size);
|
|
#endif
|
|
return DC_STOP;
|
|
}
|
|
}
|
|
#ifdef DEBUG_MOV
|
|
log_trace("file_mov.c: new calculated_file_size %llu\n",
|
|
(long long unsigned)file_recovery->calculated_file_size);
|
|
#endif
|
|
return DC_CONTINUE;
|
|
}
|
|
|
|
/*@
|
|
@ requires buffer_size >= 16;
|
|
@ requires separation: \separated(file_recovery, file_recovery_new);
|
|
@ requires valid_header_check_param(buffer, buffer_size, safe_header_only, file_recovery, file_recovery_new);
|
|
@ ensures valid_header_check_result(\result, file_recovery_new);
|
|
@ ensures (\result == 1) ==> (file_recovery_new->file_stat == \null);
|
|
@ ensures (\result == 1) ==> (file_recovery_new->handle == \null);
|
|
@ ensures (\result == 1) ==> (file_recovery_new->extension == file_hint_mov.extension ||
|
|
file_recovery_new->extension == extension_3g2 ||
|
|
file_recovery_new->extension == extension_3gp ||
|
|
file_recovery_new->extension == extension_cr3 ||
|
|
file_recovery_new->extension == extension_heic ||
|
|
file_recovery_new->extension == extension_jp2 ||
|
|
file_recovery_new->extension == extension_m4a ||
|
|
file_recovery_new->extension == extension_mp4);
|
|
@ ensures (\result == 1) ==> (file_recovery_new->time == 0);
|
|
@ ensures (\result == 1) ==> (valid_read_string(file_recovery_new->extension));
|
|
@ ensures (\result == 1) ==> (file_recovery_new->file_rename == &file_rename_mov || file_recovery_new->file_rename == \null);
|
|
@ ensures (\result == 1 && file_recovery_new->extension == file_hint_mov.extension) ==> (file_recovery_new->file_rename == file_rename_mov);
|
|
@ ensures (\result == 1 && file_recovery_new->extension != file_hint_mov.extension) ==> (file_recovery_new->file_rename == \null);
|
|
@ ensures (\result == 1 && (file_recovery_new->extension == extension_jp2 || file_recovery_new->blocksize < 16)) ==> (file_recovery_new->data_check == \null && file_recovery_new->file_check == \null && file_recovery_new->file_rename == \null && file_recovery_new->min_filesize > 0);
|
|
@ ensures (\result == 1 && file_recovery_new->extension != extension_jp2 && file_recovery_new->blocksize >= 16) ==> (file_recovery_new->calculated_file_size > 0 && file_recovery_new->file_check == &file_check_size && file_recovery_new->data_check == &data_check_mov);
|
|
@ ensures (\result == 1) ==> \separated(file_recovery_new, file_recovery_new->extension);
|
|
@*/
|
|
static int header_check_mov_aux(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)
|
|
{
|
|
uint64_t i=0;
|
|
while(i <= buffer_size-16)
|
|
{
|
|
/*@ assert i <= buffer_size - 16; */
|
|
const struct atom_struct *atom=(const struct atom_struct*)&buffer[i];
|
|
uint64_t calculated_file_size;
|
|
uint64_t atom_size=be32(atom->size);
|
|
if(atom_size==1)
|
|
{
|
|
const struct atom64_struct *atom64=(const struct atom64_struct*)&buffer[i];
|
|
atom_size=be64(atom64->size);
|
|
if(atom_size<16)
|
|
return 0;
|
|
/*@ assert atom_size >= 16; */
|
|
}
|
|
else if(atom_size<8)
|
|
return 0;
|
|
/*@ assert 8 <= atom_size; */
|
|
if(atom_size >= 0x800000000000)
|
|
return 0;
|
|
/*@ assert 8 <= atom_size < 0x800000000000; */
|
|
calculated_file_size=atom_size+i;
|
|
/* check for commun atom type */
|
|
if(buffer[i+4]=='p' && buffer[i+5]=='n' && buffer[i+6]=='o' && buffer[i+7]=='t')
|
|
{
|
|
if(atom_size != 20)
|
|
return 0;
|
|
/*@ assert atom_size == 20; */
|
|
reset_file_recovery(file_recovery_new);
|
|
file_recovery_new->extension=file_hint_mov.extension;
|
|
file_recovery_new->file_rename=&file_rename_mov;
|
|
if(file_recovery_new->blocksize < 16)
|
|
{
|
|
file_recovery_new->min_filesize=calculated_file_size;
|
|
return 1;
|
|
}
|
|
file_recovery_new->data_check=&data_check_mov;
|
|
file_recovery_new->file_check=&file_check_size;
|
|
file_recovery_new->calculated_file_size=calculated_file_size;
|
|
return 1;
|
|
}
|
|
if(buffer[i+4]=='w' && buffer[i+5]=='i' && buffer[i+6]=='d' && buffer[i+7]=='e')
|
|
{
|
|
if(atom_size != 8)
|
|
return 0;
|
|
/*@ assert atom_size == 8; */
|
|
reset_file_recovery(file_recovery_new);
|
|
file_recovery_new->extension=file_hint_mov.extension;
|
|
file_recovery_new->file_rename=&file_rename_mov;
|
|
if(file_recovery_new->blocksize < 16)
|
|
{
|
|
file_recovery_new->min_filesize=calculated_file_size;
|
|
return 1;
|
|
}
|
|
file_recovery_new->data_check=&data_check_mov;
|
|
file_recovery_new->file_check=&file_check_size;
|
|
file_recovery_new->calculated_file_size=calculated_file_size;
|
|
return 1;
|
|
}
|
|
if(buffer[i+4]=='m' && buffer[i+5]=='o' && buffer[i+6]=='o' && buffer[i+7]=='v')
|
|
{
|
|
if(atom_size > 256*256*256)
|
|
return 0;
|
|
/*@ assert atom_size <= 256*256*256; */
|
|
reset_file_recovery(file_recovery_new);
|
|
file_recovery_new->extension=file_hint_mov.extension;
|
|
file_recovery_new->file_rename=&file_rename_mov;
|
|
if(file_recovery_new->blocksize < 16)
|
|
{
|
|
file_recovery_new->min_filesize=calculated_file_size;
|
|
return 1;
|
|
}
|
|
/*
|
|
if(i==0 && buffer[12]=='m' && buffer[13]=='v' && buffer[14]=='h' && buffer[15]=='d')
|
|
{
|
|
file_recovery_new->calculated_file_size=atom_size;
|
|
file_recovery_new->data_check=&data_check_size;
|
|
}
|
|
else
|
|
*/
|
|
file_recovery_new->data_check=&data_check_mov;
|
|
file_recovery_new->file_check=&file_check_size;
|
|
file_recovery_new->calculated_file_size=calculated_file_size;
|
|
return 1;
|
|
}
|
|
if(buffer[i+4]=='f' && buffer[i+5]=='t' && buffer[i+6]=='y' && buffer[i+7]=='p')
|
|
{
|
|
if(atom_size < 20 || (atom_size&3)!=0 || atom_size>256)
|
|
return 0;
|
|
/*@ assert 20 <= atom_size <= 256; */
|
|
if(memcmp(&buffer[i+8], "isom", 4)==0 ||
|
|
memcmp(&buffer[i+8], "mp41", 4)==0 ||
|
|
memcmp(&buffer[i+8], "mp42", 4)==0 ||
|
|
memcmp(&buffer[i+8], "mmp4", 4)==0 ||
|
|
memcmp(&buffer[i+8], "M4B", 3)==0 ||
|
|
memcmp(&buffer[i+8], "M4P", 3)==0 ||
|
|
memcmp(&buffer[i+8], "XAVC", 4)==0)
|
|
{
|
|
reset_file_recovery(file_recovery_new);
|
|
file_recovery_new->extension=extension_mp4;
|
|
if(file_recovery->blocksize < 16)
|
|
{
|
|
file_recovery_new->min_filesize=calculated_file_size;
|
|
return 1;
|
|
}
|
|
file_recovery_new->data_check=&data_check_mov;
|
|
file_recovery_new->file_check=&file_check_size;
|
|
file_recovery_new->calculated_file_size=calculated_file_size;
|
|
return 1;
|
|
}
|
|
else if(memcmp(&buffer[i+8], "M4A ", 4)==0)
|
|
{
|
|
reset_file_recovery(file_recovery_new);
|
|
file_recovery_new->extension=extension_m4a;
|
|
if(file_recovery->blocksize < 16)
|
|
{
|
|
file_recovery_new->min_filesize=calculated_file_size;
|
|
return 1;
|
|
}
|
|
file_recovery_new->data_check=&data_check_mov;
|
|
file_recovery_new->file_check=&file_check_size;
|
|
file_recovery_new->calculated_file_size=calculated_file_size;
|
|
return 1;
|
|
}
|
|
else if(memcmp(&buffer[i+8], "3gp", 3)==0)
|
|
{
|
|
/* Video for 3G mobile phone (GSM) */
|
|
reset_file_recovery(file_recovery_new);
|
|
file_recovery_new->extension=extension_3gp;
|
|
if(file_recovery->blocksize < 16)
|
|
{
|
|
file_recovery_new->min_filesize=calculated_file_size;
|
|
return 1;
|
|
}
|
|
file_recovery_new->data_check=&data_check_mov;
|
|
file_recovery_new->file_check=&file_check_size;
|
|
file_recovery_new->calculated_file_size=calculated_file_size;
|
|
return 1;
|
|
}
|
|
else if(memcmp(&buffer[i+8], "3g2", 3)==0)
|
|
{
|
|
/* Video for 3G mobile phone (CDMA) */
|
|
reset_file_recovery(file_recovery_new);
|
|
file_recovery_new->extension=extension_3g2;
|
|
if(file_recovery->blocksize < 16)
|
|
{
|
|
file_recovery_new->min_filesize=calculated_file_size;
|
|
return 1;
|
|
}
|
|
file_recovery_new->data_check=&data_check_mov;
|
|
file_recovery_new->file_check=&file_check_size;
|
|
file_recovery_new->calculated_file_size=calculated_file_size;
|
|
return 1;
|
|
}
|
|
else if(memcmp(&buffer[i+8], "heic", 4)==0)
|
|
{
|
|
reset_file_recovery(file_recovery_new);
|
|
file_recovery_new->extension=extension_heic;
|
|
if(file_recovery->blocksize < 16)
|
|
{
|
|
file_recovery_new->min_filesize=calculated_file_size;
|
|
return 1;
|
|
}
|
|
file_recovery_new->data_check=&data_check_mov;
|
|
file_recovery_new->file_check=&file_check_size;
|
|
file_recovery_new->calculated_file_size=calculated_file_size;
|
|
return 1;
|
|
}
|
|
else if(memcmp(&buffer[i+8], "jp2 ", 4)==0)
|
|
{
|
|
reset_file_recovery(file_recovery_new);
|
|
file_recovery_new->extension=extension_jp2;
|
|
file_recovery_new->min_filesize=calculated_file_size;
|
|
/* jP + ftyp "jp2 " + jp2h + jp2c (atom_size=0) => no data check */
|
|
return 1;
|
|
}
|
|
else if(memcmp(&buffer[i+8], "qt ", 4)==0)
|
|
{
|
|
reset_file_recovery(file_recovery_new);
|
|
file_recovery_new->extension=file_hint_mov.extension;
|
|
file_recovery_new->file_rename=&file_rename_mov;
|
|
if(file_recovery->blocksize < 16)
|
|
{
|
|
file_recovery_new->min_filesize=calculated_file_size;
|
|
return 1;
|
|
}
|
|
file_recovery_new->data_check=&data_check_mov;
|
|
file_recovery_new->file_check=&file_check_size;
|
|
file_recovery_new->calculated_file_size=calculated_file_size;
|
|
return 1;
|
|
}
|
|
else if(memcmp(&buffer[i+8], "crx ", 4)==0)
|
|
{
|
|
reset_file_recovery(file_recovery_new);
|
|
file_recovery_new->extension=extension_cr3;
|
|
if(file_recovery->blocksize < 16)
|
|
{
|
|
file_recovery_new->min_filesize=calculated_file_size;
|
|
return 1;
|
|
}
|
|
file_recovery_new->data_check=&data_check_mov;
|
|
file_recovery_new->file_check=&file_check_size;
|
|
file_recovery_new->calculated_file_size=calculated_file_size;
|
|
return 1;
|
|
}
|
|
}
|
|
if(buffer[i+4]=='m' && buffer[i+5]=='d' && buffer[i+6]=='a' && buffer[i+7]=='t')
|
|
{
|
|
if(memcmp(&buffer[i], "der.mdat\" anim=\"", 16)==0)
|
|
return 0;
|
|
if(file_recovery->file_stat!=NULL &&
|
|
file_recovery->file_check!=NULL &&
|
|
buffer[8]=='a' && isprint(buffer[0]) && isprint(buffer[1]) && isprint(buffer[2]) && isprint(buffer[3]))
|
|
{
|
|
header_ignored(file_recovery_new);
|
|
return 0;
|
|
}
|
|
reset_file_recovery(file_recovery_new);
|
|
file_recovery_new->extension=file_hint_mov.extension;
|
|
file_recovery_new->file_rename=&file_rename_mov;
|
|
if(file_recovery_new->blocksize < 16)
|
|
{
|
|
file_recovery_new->min_filesize=calculated_file_size;
|
|
return 1;
|
|
}
|
|
file_recovery_new->data_check=&data_check_mov;
|
|
file_recovery_new->file_check=&file_check_size;
|
|
file_recovery_new->calculated_file_size=calculated_file_size;
|
|
return 1;
|
|
}
|
|
if(atom_size > buffer_size)
|
|
return 0;
|
|
i+=atom_size;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*@
|
|
@ requires buffer_size >= 16;
|
|
@ requires separation: \separated(file_recovery, file_recovery_new);
|
|
@ requires valid_header_check_param(buffer, buffer_size, safe_header_only, file_recovery, file_recovery_new);
|
|
@ ensures valid_header_check_result(\result, file_recovery_new);
|
|
@ ensures (\result == 1) ==> (file_recovery_new->extension == file_hint_mov.extension ||
|
|
file_recovery_new->extension == extension_3g2 ||
|
|
file_recovery_new->extension == extension_3gp ||
|
|
file_recovery_new->extension == extension_cr3 ||
|
|
file_recovery_new->extension == extension_heic ||
|
|
file_recovery_new->extension == extension_jp2 ||
|
|
file_recovery_new->extension == extension_m4a ||
|
|
file_recovery_new->extension == extension_mp4);
|
|
@ ensures (\result == 1) ==> (valid_read_string(file_recovery_new->extension));
|
|
@ ensures (\result == 1) ==> (file_recovery_new->file_rename == &file_rename_mov || file_recovery_new->file_rename == \null);
|
|
@ ensures (\result == 1 && file_recovery_new->extension == file_hint_mov.extension) ==> (file_recovery_new->file_rename == file_rename_mov);
|
|
@ ensures (\result == 1 && file_recovery_new->extension != file_hint_mov.extension) ==> (file_recovery_new->file_rename == \null);
|
|
@ ensures (\result == 1 && (file_recovery_new->extension == extension_jp2 || file_recovery_new->blocksize < 16)) ==> (file_recovery_new->data_check == \null && file_recovery_new->file_check == \null && file_recovery_new->file_rename == \null && file_recovery_new->min_filesize > 0);
|
|
@ ensures (\result == 1 && file_recovery_new->extension != extension_jp2 && file_recovery_new->blocksize >= 16) ==> (file_recovery_new->calculated_file_size > 0 && file_recovery_new->file_check == &file_check_size && file_recovery_new->data_check == &data_check_mov);
|
|
@*/
|
|
static int header_check_mov(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)
|
|
{
|
|
if(file_recovery->file_stat!=NULL &&
|
|
file_recovery->file_check!=NULL &&
|
|
file_recovery->file_stat->file_hint==&file_hint_mov &&
|
|
file_recovery->calculated_file_size == file_recovery->file_size)
|
|
{ /* PhotoRec is already trying to recover this mov file */
|
|
header_ignored(file_recovery_new);
|
|
return 0;
|
|
}
|
|
return header_check_mov_aux(buffer, buffer_size, safe_header_only, file_recovery, file_recovery_new);
|
|
}
|
|
|
|
/*@
|
|
@ requires valid_register_header_check(file_stat);
|
|
@*/
|
|
static void register_header_check_mov_mdat(file_stat_t *file_stat)
|
|
{
|
|
register_header_check(4, (const unsigned char*)"mdat",4, &header_check_mov_aux, file_stat);
|
|
}
|
|
|
|
/*@
|
|
@ requires valid_register_header_check(file_stat);
|
|
@*/
|
|
static void register_header_check_mov(file_stat_t *file_stat)
|
|
{
|
|
register_header_check(4, (const unsigned char*)"cmov",4, &header_check_mov, file_stat);
|
|
#ifndef DISABLED_FOR_FRAMAC
|
|
register_header_check(4, (const unsigned char*)"cmvd",4, &header_check_mov, file_stat);
|
|
register_header_check(4, (const unsigned char*)"dcom",4, &header_check_mov, file_stat);
|
|
register_header_check(4, (const unsigned char*)"free",4, &header_check_mov, file_stat);
|
|
register_header_check(4, (const unsigned char*)"ftyp",4, &header_check_mov_aux, file_stat);
|
|
register_header_check(4, (const unsigned char*)"jp2h",4, &header_check_mov, file_stat);
|
|
register_header_check(4, (const unsigned char*)"mdat",4, &header_check_mov, file_stat);
|
|
register_header_check(4, (const unsigned char*)"mdia",4, &header_check_mov, file_stat);
|
|
register_header_check(4, (const unsigned char*)"moov",4, &header_check_mov, file_stat);
|
|
register_header_check(4, (const unsigned char*)"PICT",4, &header_check_mov, file_stat);
|
|
register_header_check(4, (const unsigned char*)"pnot",4, &header_check_mov, file_stat);
|
|
register_header_check(4, (const unsigned char*)"skip",4, &header_check_mov, file_stat);
|
|
register_header_check(4, (const unsigned char*)"stbl",4, &header_check_mov, file_stat);
|
|
register_header_check(4, (const unsigned char*)"trak",4, &header_check_mov, file_stat);
|
|
register_header_check(4, (const unsigned char*)"wide",4, &header_check_mov, file_stat);
|
|
register_header_check(4, (const unsigned char*)"jP ",4, &header_check_mov, file_stat);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#if defined(MAIN_mov)
|
|
#define BLOCKSIZE 65536u
|
|
int main()
|
|
{
|
|
const char fn[] = "recup_dir.1/f0000000.mov";
|
|
unsigned char buffer[BLOCKSIZE];
|
|
file_recovery_t file_recovery_new;
|
|
file_recovery_t file_recovery;
|
|
file_stat_t file_stats;
|
|
|
|
/*@ assert \valid(buffer + (0 .. (BLOCKSIZE - 1))); */
|
|
#if defined(__FRAMAC__)
|
|
Frama_C_make_unknown((char *)buffer, BLOCKSIZE);
|
|
#endif
|
|
|
|
reset_file_recovery(&file_recovery);
|
|
file_recovery.blocksize=BLOCKSIZE;
|
|
file_recovery_new.blocksize=BLOCKSIZE;
|
|
file_recovery_new.data_check=NULL;
|
|
file_recovery_new.file_stat=NULL;
|
|
file_recovery_new.file_check=NULL;
|
|
file_recovery_new.file_rename=NULL;
|
|
file_recovery_new.calculated_file_size=0;
|
|
file_recovery_new.file_size=0;
|
|
file_recovery_new.offset_ok=0;
|
|
file_recovery_new.checkpoint_status=0;
|
|
file_recovery_new.location.start=0;
|
|
file_recovery_new.offset_error=0;
|
|
|
|
file_stats.file_hint=&file_hint_mov;
|
|
file_stats.not_recovered=0;
|
|
file_stats.recovered=0;
|
|
register_header_check_mov(&file_stats);
|
|
/*@ assert file_recovery_new.blocksize >= 16; */
|
|
if(header_check_mov(buffer, BLOCKSIZE, 0u, &file_recovery, &file_recovery_new)!=1)
|
|
return 0;
|
|
/*@ assert file_recovery_new.blocksize >= 16; */
|
|
/*@ assert valid_read_string(file_recovery_new.extension); */
|
|
/*@ assert file_recovery_new.file_size == 0; */
|
|
/*@ assert file_recovery_new.offset_ok == 0; */
|
|
/*@ assert valid_read_string((char *)&fn); */
|
|
memcpy(file_recovery_new.filename, fn, sizeof(fn));
|
|
/*@ assert valid_read_string((char *)&file_recovery_new.filename); */
|
|
/*@ assert file_recovery_new.offset_ok == 0; */
|
|
file_recovery_new.file_stat=&file_stats;
|
|
if(file_recovery_new.data_check != NULL)
|
|
{
|
|
/*@ assert file_recovery_new.data_check == &data_check_mov; */
|
|
/*@ assert file_recovery_new.file_check == file_check_size; */
|
|
unsigned char big_buffer[2*BLOCKSIZE];
|
|
data_check_t res_data_check=DC_CONTINUE;
|
|
memset(big_buffer, 0, BLOCKSIZE);
|
|
memcpy(big_buffer + BLOCKSIZE, buffer, BLOCKSIZE);
|
|
/*@ assert file_recovery_new.file_size == 0; */;
|
|
res_data_check=data_check_mov(big_buffer, 2*BLOCKSIZE, &file_recovery_new);
|
|
file_recovery_new.file_size+=BLOCKSIZE;
|
|
if(res_data_check == DC_CONTINUE)
|
|
{
|
|
/*@ assert file_recovery_new.calculated_file_size > file_recovery_new.file_size - 16; */
|
|
memcpy(big_buffer, big_buffer + BLOCKSIZE, BLOCKSIZE);
|
|
#if defined(__FRAMAC__)
|
|
Frama_C_make_unknown((char *)big_buffer + BLOCKSIZE, BLOCKSIZE);
|
|
#endif
|
|
data_check_mov(big_buffer, 2*BLOCKSIZE, &file_recovery_new);
|
|
}
|
|
}
|
|
/*@ assert file_recovery_new.offset_ok == 0; */
|
|
{
|
|
file_recovery_t file_recovery_new2;
|
|
/* Test when another file of the same is detected in the next block */
|
|
file_recovery_new2.blocksize=BLOCKSIZE;
|
|
file_recovery_new2.file_stat=NULL;
|
|
file_recovery_new2.file_check=NULL;
|
|
file_recovery_new2.location.start=BLOCKSIZE;
|
|
file_recovery_new.handle=NULL; /* In theory should be not null */
|
|
header_check_mov(buffer, BLOCKSIZE, 0, &file_recovery_new, &file_recovery_new2);
|
|
}
|
|
/*@ assert file_recovery_new.offset_ok == 0; */
|
|
if(file_recovery_new.file_check != NULL)
|
|
{
|
|
/*@ assert file_recovery_new.file_check == file_check_size; */
|
|
file_recovery_new.handle=fopen(fn, "rb");
|
|
if(file_recovery_new.handle!=NULL)
|
|
{
|
|
file_check_size(&file_recovery_new);
|
|
fclose(file_recovery_new.handle);
|
|
}
|
|
}
|
|
/*@ assert valid_read_string((char *)file_recovery_new.filename); */
|
|
file_rename_mov(&file_recovery_new);
|
|
return 0;
|
|
}
|
|
#endif
|