/*

    File: filegen.h

    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.

 */
#ifndef _TESTDISK_FILEGEN_H
#define _TESTDISK_FILEGEN_H
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__FRAMAC__)
#include "__fc_builtin.h"
#endif

#include "list.h"

#if defined(DJGPP)
#define PHOTOREC_MAX_FILE_SIZE (((uint64_t)1<<31)-1)
#else
#define PHOTOREC_MAX_FILE_SIZE (((uint64_t)1<<41)-1)
#endif
#define PHOTOREC_MAX_SIZE_16 (((uint64_t)1<<15)-1)
#define PHOTOREC_MAX_SIZE_32 (((uint64_t)1<<31)-1)
#define PHOTOREC_MAX_SIG_OFFSET	65535
#define PHOTOREC_MAX_SIG_SIZE 4095
/* TODO: Support blocksize up to 32 MB */
#define PHOTOREC_MAX_BLOCKSIZE 32*1024*1024

typedef enum { DC_SCAN=0, DC_CONTINUE=1, DC_STOP=2, DC_ERROR=3} data_check_t;
typedef struct file_hint_struct file_hint_t;
typedef struct file_stat_struct file_stat_t;
typedef struct file_recovery_struct file_recovery_t;
typedef struct file_enable_struct file_enable_t;

struct file_hint_struct
{
  const char *extension;
  const char *description;
  const uint64_t max_filesize;
  const int recover;
  const unsigned int enable_by_default;
  void (*register_header_check)(file_stat_t *file_stat);
};

typedef struct
{
  struct td_list_head list;
  uint64_t start;
  uint64_t end;
  file_stat_t *file_stat;
  unsigned int data;
} alloc_data_t;

struct file_enable_struct
{
  const file_hint_t *file_hint;
  unsigned int enable;
};

struct file_stat_struct
{
  unsigned int not_recovered;
  unsigned int recovered;
  const file_hint_t *file_hint;
};

struct file_recovery_struct
{
  char filename[2048];
  alloc_list_t location;
  file_stat_t *file_stat;
  FILE *handle;
  time_t time;
  uint64_t file_size;
  const char *extension;
  uint64_t min_filesize;
  uint64_t offset_ok;
  uint64_t offset_error;
  uint64_t extra;	/* extra bytes between offset_ok and offset_error */
  uint64_t calculated_file_size;
  data_check_t (*data_check)(const unsigned char*buffer, const unsigned int buffer_size, file_recovery_t *file_recovery);
  /* data_check modifies file_recovery->calculated_file_size, it can also update data_check, file_check, offset_error, offset_ok, time, data_check_tmp */
  void (*file_check)(file_recovery_t *file_recovery);
  void (*file_rename)(file_recovery_t *file_recovery);
  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 flags;
  unsigned int data_check_tmp;
};

typedef struct
{
  struct td_list_head list;
  const void *value;
  unsigned int length;
  unsigned int offset;
  int (*header_check)(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);
  file_stat_t *file_stat;
} file_check_t;

/*@
   predicate valid_file_hint(file_hint_t *file_hint) = (\valid_read(file_hint) && valid_read_string(file_hint->description));

   predicate valid_file_stat(file_stat_t *file_stat) = (\valid_read(file_stat) && valid_file_hint(file_stat->file_hint));

   predicate valid_file_check_node(file_check_t *node) = (\valid_read(node) &&
     \initialized(&node->offset) &&
     \initialized(&node->length) &&
     node->offset <= PHOTOREC_MAX_SIG_OFFSET &&
     0 < node->length <= PHOTOREC_MAX_SIG_SIZE &&
     node->offset + node->length <= PHOTOREC_MAX_SIG_OFFSET &&
     \valid_read((const char *)node->value+(0..node->length-1)) &&
     \valid_function(node->header_check) &&
     \valid(node->file_stat) &&
     valid_file_stat(node->file_stat)
   );

   predicate valid_file_enable_node(file_enable_t *node) = (\valid_read(node) && (node->file_hint == \null || valid_file_hint(node->file_hint)));

   @*/

typedef struct
{
  file_check_t file_checks[256];
  struct td_list_head list;
  unsigned int offset;
} file_check_list_t;

#define NL_BARENL       (1 << 0)
#define NL_CRLF         (1 << 1)
#define NL_BARECR       (1 << 2)

/*@
   predicate valid_file_recovery(file_recovery_t *file_recovery) = (\valid_read(file_recovery) &&
	strlen((const char*)file_recovery->filename) < 1<<30 &&
        valid_read_string((const char *)file_recovery->filename) &&
	(file_recovery->file_stat == \null || valid_file_stat(file_recovery->file_stat)) &&
	(file_recovery->handle == \null || \valid(file_recovery->handle)) &&
        (file_recovery->extension == \null || valid_read_string(file_recovery->extension)) &&
	(file_recovery->data_check == \null || \valid_function(file_recovery->data_check)) &&
	(file_recovery->file_check == \null || \valid_function(file_recovery->file_check)) &&
	(file_recovery->file_rename == \null || \valid_function(file_recovery->file_rename)) &&
	\separated(file_recovery, file_recovery->extension) &&
	\separated(file_recovery, file_recovery->handle) &&
	\initialized(&file_recovery->calculated_file_size) &&
	\initialized(&file_recovery->file_check) &&
	\initialized(&file_recovery->file_size) &&
	\initialized(&file_recovery->min_filesize) &&
	\initialized(&file_recovery->time)
	);

    predicate valid_list_search_space(alloc_data_t *list) = (\valid_read(list) && \valid(list->list.prev) && \valid_read(list->list.next));

    predicate valid_header_check_param(unsigned char *buffer, unsigned int buffer_size, unsigned int safe_header_only, file_recovery_t *file_recovery, file_recovery_t *file_recovery_new) =(
      buffer_size > 0 &&
      \valid_read(buffer+(0..buffer_size-1)) &&
      \initialized(buffer+(0..buffer_size-1)) &&
      valid_file_recovery(file_recovery) &&
      \valid(file_recovery_new) &&
      file_recovery_new->blocksize > 0 &&
      file_recovery_new->blocksize <= buffer_size &&
      \separated(buffer+(..), file_recovery, file_recovery_new)
   );

    predicate valid_header_check_result(int res, file_recovery_t *file_recovery_new) =(
    (res == 0 || res == 1) &&
    (res != 0 ==> valid_file_recovery(file_recovery_new)) &&
    (res != 0 ==> (file_recovery_new->file_stat == \null)) &&
    (res != 0 ==> (file_recovery_new->handle == \null))
    );

    predicate valid_file_check_param(file_recovery_t *file_recovery) = (
      \valid(file_recovery) &&
      \valid(file_recovery->handle) &&
      valid_file_recovery(file_recovery) &&
      \separated(file_recovery, file_recovery->handle, file_recovery->extension, &errno, &Frama_C_entropy_source)
    );

    predicate valid_file_check_result(file_recovery_t *file_recovery) = (
      valid_file_recovery(file_recovery) &&
      \valid(file_recovery->handle)
    );

    predicate valid_data_check_param(unsigned char *buffer, unsigned int buffer_size, file_recovery_t *file_recovery) = (
      buffer_size > 0 &&
      (buffer_size&1)==0 &&
      buffer_size <= 2 * PHOTOREC_MAX_BLOCKSIZE &&
      \valid_read(buffer+(0..buffer_size-1)) &&
      \initialized(buffer+(0..buffer_size-1)) &&
      \valid(file_recovery) &&
      valid_file_recovery(file_recovery) &&
      file_recovery->calculated_file_size <= PHOTOREC_MAX_FILE_SIZE &&
      file_recovery->file_size <= PHOTOREC_MAX_FILE_SIZE &&
      \separated(buffer + (..), file_recovery)
    );

    predicate valid_data_check_result(data_check_t res, file_recovery_t *file_recovery) = (
      (res == DC_CONTINUE || res == DC_STOP || res == DC_ERROR) &&
      valid_file_recovery(file_recovery)
    );

    predicate valid_file_rename_param(file_recovery_t *file_recovery) = (
      \valid(file_recovery) &&
      valid_file_recovery(file_recovery) &&
      \separated(file_recovery, &errno, &Frama_C_entropy_source)
    );
    predicate valid_file_rename_result(file_recovery_t *file_recovery) = (
      \valid(file_recovery) &&
      valid_file_recovery(file_recovery)
    );

    predicate valid_register_header_check(file_stat_t *file_stat) = (
	\valid(file_stat) &&
	valid_file_stat(file_stat)
    );
  @*/
void free_header_check(void);

/*@
  @ requires \valid(file_recovery);
  @ requires \valid(file_recovery->handle);
  @ requires valid_file_recovery(file_recovery);
  @ requires valid_file_check_param(file_recovery);
  @ requires \separated(file_recovery, file_recovery->handle, &errno, &Frama_C_entropy_source);
  @ ensures file_recovery->handle == \old(file_recovery->handle);
  @ assigns *file_recovery->handle, errno, Frama_C_entropy_source, file_recovery->file_size;
  @*/
//  TODO ensures  valid_file_check_result(file_recovery);
void file_allow_nl(file_recovery_t *file_recovery, const unsigned int nl_mode);

/*@
  @ requires \valid(handle);
  @ requires offset < 0x8000000000000000;
  @ requires 0 < footer_length <4096;
  @ requires \valid_read((char *)footer+(0..footer_length-1));
  @ requires \separated(handle, (char *)footer + (..), &errno, &Frama_C_entropy_source);
  @ ensures \result < 0x8000000000000000;
  @ assigns *handle, errno, Frama_C_entropy_source;
  @*/
uint64_t file_rsearch(FILE *handle, uint64_t offset, const void*footer, const unsigned int footer_length);

/*@
  @ requires 0 < footer_length < 4096;
  @ requires \valid_read((char *)footer+(0..footer_length-1));
  @ requires \separated(file_recovery, file_recovery->handle, file_recovery->extension, &errno, &Frama_C_entropy_source);
  @ requires valid_file_check_param(file_recovery);
  @ requires extra_length <= PHOTOREC_MAX_FILE_SIZE;
  @ requires file_recovery->file_size < 0x8000000000000000;
  @ ensures  valid_file_check_result(file_recovery);
  @ assigns *file_recovery->handle, errno, file_recovery->file_size;
  @ assigns Frama_C_entropy_source;
  @*/
void file_search_footer(file_recovery_t *file_recovery, const void*footer, const unsigned int footer_length, const unsigned int extra_length);

/*@
  @ requires file_recovery->data_check == &data_check_size;
  @ requires valid_data_check_param(buffer, buffer_size, file_recovery);
  @ terminates \true;
  @ ensures  valid_data_check_result(\result, file_recovery);
  @ ensures  \result == DC_STOP || \result == DC_CONTINUE;
  @ ensures  file_recovery->data_check == &data_check_size;
  @ assigns  \nothing;
  @*/
data_check_t data_check_size(const unsigned char *buffer, const unsigned int buffer_size, file_recovery_t *file_recovery);

/*@
  @ requires file_recovery->file_check == &file_check_size;
  @ requires valid_file_check_param(file_recovery);
  @ terminates \true;
  @ ensures  valid_file_check_result(file_recovery);
  @ assigns  file_recovery->file_size;
  @*/
void file_check_size(file_recovery_t *file_recovery);

/*@
  @ requires file_recovery->file_check == &file_check_size_min;
  @ requires valid_file_check_param(file_recovery);
  @ terminates \true;
  @ ensures  valid_file_check_result(file_recovery);
  @ assigns  file_recovery->file_size;
  @*/
void file_check_size_min(file_recovery_t *file_recovery);

/*@
  @ requires file_recovery->file_check == &file_check_size_max;
  @ requires valid_file_check_param(file_recovery);
  @ terminates \true;
  @ ensures  valid_file_check_result(file_recovery);
  @ assigns  file_recovery->file_size;
  @*/
void file_check_size_max(file_recovery_t *file_recovery);

/*@
  requires \valid(file_recovery);
  terminates \true;
  ensures file_recovery->filename[0]=='\0';
  ensures file_recovery->time==0;
  ensures file_recovery->file_stat==\null;
  ensures file_recovery->handle==\null;
  ensures file_recovery->file_size==0;
  ensures file_recovery->location.list.prev==&file_recovery->location.list;
  ensures file_recovery->location.list.next==&file_recovery->location.list;
  ensures file_recovery->location.end==0;
  ensures file_recovery->location.data==0;
  ensures file_recovery->extension==\null;
  ensures file_recovery->min_filesize==0;
  ensures file_recovery->calculated_file_size==0;
  ensures file_recovery->data_check==\null;
  ensures file_recovery->file_check==\null;
  ensures file_recovery->file_rename==\null;
  ensures file_recovery->offset_error==0;
  ensures file_recovery->offset_ok==0;
  ensures file_recovery->checkpoint_status==0;
  ensures file_recovery->checkpoint_offset==0;
  ensures file_recovery->flags==0;
  ensures file_recovery->extra==0;
  ensures file_recovery->data_check_tmp==0;
  assigns file_recovery->filename[0];
  assigns file_recovery->time;
  assigns file_recovery->file_stat;
  assigns file_recovery->handle;
  assigns file_recovery->file_size;
  assigns file_recovery->location.list.prev;
  assigns file_recovery->location.list.next;
  assigns file_recovery->location.end;
  assigns file_recovery->location.data;
  assigns file_recovery->extension;
  assigns file_recovery->min_filesize;
  assigns file_recovery->calculated_file_size;
  assigns file_recovery->data_check;
  assigns file_recovery->file_check;
  assigns file_recovery->file_rename;
  assigns file_recovery->offset_error;
  assigns file_recovery->offset_ok;
  assigns file_recovery->checkpoint_status;
  assigns file_recovery->checkpoint_offset;
  assigns file_recovery->flags;
  assigns file_recovery->extra;
  assigns file_recovery->data_check_tmp;
*/
//  ensures valid_file_recovery(file_recovery);
void reset_file_recovery(file_recovery_t *file_recovery);

/*@
  @ requires offset <= PHOTOREC_MAX_SIG_OFFSET;
  @ requires 0 < length <= PHOTOREC_MAX_SIG_SIZE;
  @ requires offset + length <= PHOTOREC_MAX_SIG_OFFSET;
  @ requires \valid_read((const char *)value+(0..length-1));
  @ requires \valid_function(header_check);
  @ requires \valid(file_stat);
  @ requires valid_file_stat(file_stat);
  @ ensures  \valid_read((const char *)value+(0..length-1));
  @*/
void register_header_check(const unsigned int offset, const void *value, const unsigned int length,
    int (*header_check)(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),
    file_stat_t *file_stat);

/*@
  @ requires valid_file_enable_node(files_enable);
  @ ensures valid_file_stat(\result);
  @*/
file_stat_t * init_file_stats(file_enable_t *files_enable);

/*@
  @ requires \valid(file_recovery);
  @ requires valid_file_recovery(file_recovery);
  @ requires buffer_size < 1<<30;
  @ requires \valid_read((char *)buffer+(0..buffer_size-1));
  @ requires new_ext==\null || (valid_read_string(new_ext) && strlen(new_ext) < 1<<30);
  @ requires \separated(file_recovery, new_ext);
  @ ensures  valid_file_recovery(file_recovery);
  @*/
int file_rename(file_recovery_t *file_recovery, const void *buffer, const int buffer_size, const int offset, const char *new_ext, const int force_ext);

/*@
  @ requires \valid(file_recovery);
  @ requires new_ext==\null || (valid_read_string(new_ext) && strlen(new_ext) < 1<<30);
  @ requires valid_file_recovery(file_recovery);
  @ requires buffer_size < 1<<30;
  @ requires \valid_read((char *)buffer+(0..buffer_size-1));
  @ requires \separated(file_recovery, new_ext);
  @ ensures  valid_file_recovery(file_recovery);
  @*/
int file_rename_unicode(file_recovery_t *file_recovery, const void *buffer, const int buffer_size, const int offset, const char *new_ext, const int force_ext);

/*@
  @ terminates \true;
  @*/
void header_ignored_cond_reset(uint64_t start, uint64_t end);

/*@
  @ requires file_recovery_new==\null || valid_file_recovery(file_recovery_new);
  @ terminates \true;
  @*/
void header_ignored(const file_recovery_t *file_recovery_new);

/*@
  @ requires separation: \separated(file_recovery, file_recovery->handle, file_recovery_new, &errno);
  @ requires \valid_read(file_recovery);
  @ requires \valid_read(file_recovery_new);
  @ requires valid_file_recovery(file_recovery);
  @ requires valid_file_recovery(file_recovery_new);
  @ requires \valid_function(file_recovery->file_check);
  @ requires \initialized(&file_recovery->file_check);
  @ requires \initialized(&file_recovery->handle);
  @ decreases 0;
  @ ensures \result == 0 || \result == 1;
  @*/
// ensures  valid_file_recovery(file_recovery);
// ensures  valid_file_recovery(file_recovery_new);
int header_ignored_adv(const file_recovery_t *file_recovery, const file_recovery_t *file_recovery_new);

/*@
  requires valid_stream: \valid(stream);
  requires whence_enum: whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END;
  requires \separated(&errno, stream);
  assigns *stream, \result, errno;
*/
// assigns *stream \from *stream, indirect:offset, indirect:whence;
// assigns \result, errno \from indirect:*stream, indirect:offset, indirect:whence;
int my_fseek(FILE *stream, off_t offset, int whence);

/*@
  @ requires \valid_read(date_asc + (0 .. 11));
  @ assigns \nothing;
  @*/
time_t get_time_from_YYMMDDHHMMSS(const char *date_asc);

/*@
  @ requires \valid_read(date_asc + (0 .. 18));
  @ assigns \nothing;
  @*/
time_t get_time_from_YYYY_MM_DD_HH_MM_SS(const unsigned char *date_asc);

/*@
  @ requires \valid_read(date_asc + (0 .. 16));
  @ assigns \nothing;
  @*/
time_t get_time_from_YYYY_MM_DD_HHMMSS(const char *date_asc);

/*@
  @ requires \valid_read(date_asc + (0 .. 14));
  @ assigns \nothing;
  @*/
time_t get_time_from_YYYYMMDD_HHMMSS(const char *date_asc);

/*@
  @ requires \valid_read(list_search_space);
  @ requires \valid(current_search_space);
  @ requires \valid(offset);
  @ requires \separated(list_search_space, current_search_space, offset);
  @*/
void get_prev_location_smart(const alloc_data_t *list_search_space, alloc_data_t **current_search_space, uint64_t *offset, const uint64_t prev_location);

#ifdef __cplusplus
} /* closing brace for extern "C" */
#endif
#endif
