1c05a14387
enabled.
419 lines
12 KiB
C
419 lines
12 KiB
C
/*
|
|
|
|
File: file_gz.c
|
|
|
|
Copyright (C) 2006-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.
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#if defined(DISABLED_FOR_FRAMAC)
|
|
#undef HAVE_LIBZ
|
|
#undef HAVE_ZLIB_H
|
|
#endif
|
|
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include "types.h"
|
|
#include "file_gz.h"
|
|
|
|
#ifdef HAVE_ZLIB_H
|
|
#include <zlib.h>
|
|
#endif
|
|
#if !defined(SINGLE_FORMAT) || defined(SINGLE_FORMAT_gz)
|
|
#include "filegen.h"
|
|
#include "common.h"
|
|
|
|
/*@ requires valid_register_header_check(file_stat); */
|
|
static void register_header_check_gz(file_stat_t *file_stat);
|
|
#ifndef SINGLE_FORMAT
|
|
extern const file_hint_t file_hint_doc;
|
|
#endif
|
|
|
|
const file_hint_t file_hint_gz= {
|
|
.extension="gz",
|
|
.description="gzip compressed data",
|
|
.max_filesize=PHOTOREC_MAX_FILE_SIZE,
|
|
.recover=1,
|
|
.enable_by_default=1,
|
|
.register_header_check=®ister_header_check_gz
|
|
};
|
|
|
|
struct gzip_header
|
|
{
|
|
uint16_t id;
|
|
uint8_t compression_method;
|
|
uint8_t flags;
|
|
uint32_t mtime;
|
|
uint8_t extra_flags;
|
|
uint8_t os;
|
|
} __attribute__ ((gcc_struct, __packed__));
|
|
|
|
/* flags:
|
|
bit 0 FTEXT
|
|
bit 1 FHCRC
|
|
bit 2 FEXTRA
|
|
bit 3 FNAME
|
|
bit 4 FCOMMENT
|
|
bit 5 reserved
|
|
bit 6 reserved
|
|
bit 7 reserved
|
|
*/
|
|
#define GZ_FTEXT 1
|
|
#define GZ_FHCRC 2
|
|
#define GZ_FEXTRA 4
|
|
#define GZ_FNAME 8
|
|
#define GZ_FCOMMENT 0x10
|
|
|
|
/*@
|
|
@ requires file_recovery->file_rename==&file_rename_gz;
|
|
@ requires valid_file_rename_param(file_recovery);
|
|
@ ensures valid_file_rename_result(file_recovery);
|
|
@*/
|
|
static void file_rename_gz(file_recovery_t *file_recovery)
|
|
{
|
|
unsigned char buffer[512];
|
|
FILE *file;
|
|
int buffer_size;
|
|
if((file=fopen(file_recovery->filename, "rb"))==NULL)
|
|
return;
|
|
buffer_size=fread(buffer, 1, sizeof(buffer), file);
|
|
fclose(file);
|
|
if(buffer_size<10)
|
|
return;
|
|
/*@ assert \initialized(buffer+(0..10)); */
|
|
if(!(buffer[0]==0x1F && buffer[1]==0x8B && buffer[2]==0x08 && (buffer[3]&0xe0)==0))
|
|
return ;
|
|
{
|
|
const unsigned int flags=buffer[3];
|
|
int off=10;
|
|
if((flags&GZ_FEXTRA)!=0)
|
|
{
|
|
if(buffer_size<12)
|
|
return;
|
|
/*@ assert \initialized(buffer + (0 .. 12)); */
|
|
off+=2;
|
|
off+=buffer[10]|(buffer[11]<<8);
|
|
}
|
|
if((flags&GZ_FNAME)!=0)
|
|
{
|
|
file_rename(file_recovery, buffer, buffer_size, off, NULL, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(HAVE_ZLIB_H) && defined(HAVE_LIBZ)
|
|
/*@ assigns \nothing; */
|
|
static void file_check_bgzf(file_recovery_t *file_recovery)
|
|
{
|
|
}
|
|
|
|
/*@
|
|
@ requires buffer_size >= sizeof(struct gzip_header);
|
|
@ requires \valid_read(buffer+(0..buffer_size-1));
|
|
@ requires \valid_read(buffer_uncompr + (0 .. 4-1));
|
|
@ requires \valid(file_recovery_new);
|
|
@ requires separation: \separated(buffer+(..), file_recovery_new);
|
|
@ requires valid_file_recovery(file_recovery_new);
|
|
@ ensures \result == 1;
|
|
@ ensures valid_header_check_result(\result, file_recovery_new);
|
|
@ assigns *file_recovery_new;
|
|
@*/
|
|
static int header_check_bgzf(const unsigned char *buffer, const unsigned char *buffer_uncompr, const unsigned int buffer_size, file_recovery_t *file_recovery_new)
|
|
{
|
|
const struct gzip_header *gz=(const struct gzip_header *)buffer;
|
|
reset_file_recovery(file_recovery_new);
|
|
file_recovery_new->min_filesize=22;
|
|
file_recovery_new->time=le32(gz->mtime);
|
|
file_recovery_new->file_rename=&file_rename_gz;
|
|
file_recovery_new->file_check=&file_check_bgzf;
|
|
if(memcmp(buffer_uncompr, "BAI\1", 4)==0)
|
|
{
|
|
/* https://github.com/samtools/hts-specs SAM/BAM and related high-throughput sequencing file formats */
|
|
file_recovery_new->extension="bai";
|
|
/*@ assert valid_file_recovery(file_recovery_new); */
|
|
return 1;
|
|
}
|
|
if(memcmp(buffer_uncompr, "BAM\1", 4)==0)
|
|
{
|
|
/* https://github.com/samtools/hts-specs SAM/BAM and related high-throughput sequencing file formats */
|
|
file_recovery_new->extension="bam";
|
|
/*@ assert valid_file_recovery(file_recovery_new); */
|
|
return 1;
|
|
}
|
|
if(memcmp(buffer_uncompr, "CSI\1", 4)==0)
|
|
{
|
|
/* https://github.com/samtools/hts-specs SAM/BAM and related high-throughput sequencing file formats */
|
|
file_recovery_new->extension="csi";
|
|
/*@ assert valid_file_recovery(file_recovery_new); */
|
|
return 1;
|
|
}
|
|
file_recovery_new->extension="bgz";
|
|
/*@ assert valid_file_recovery(file_recovery_new); */
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
/*@
|
|
@ requires buffer_size >= 16;
|
|
@ requires separation: \separated(&file_hint_gz, buffer+(..), 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);
|
|
@*/
|
|
static int header_check_gz(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)
|
|
{
|
|
unsigned int off=10;
|
|
const unsigned int flags=buffer[3];
|
|
const struct gzip_header *gz=(const struct gzip_header *)buffer;
|
|
int bgzf=0;
|
|
/* gzip file format:
|
|
* a 10-byte header, containing a magic number, a version number and a timestamp
|
|
* optional extra headers, such as the original file name,
|
|
* a body, containing a deflate-compressed payload
|
|
* a CRC-32 checksum
|
|
* the length (32 bits) of the original uncompressed data
|
|
*/
|
|
/* gzip, deflate */
|
|
if(!(buffer[0]==0x1F && buffer[1]==0x8B && buffer[2]==0x08 && (buffer[3]&0xe0)==0))
|
|
return 0;
|
|
|
|
/*
|
|
* 4,5,6,7: mtime
|
|
* 8: xfl/extra flags
|
|
* 9: OS 3 - Unix, 7 - Macintosh, 11 - NTFS filesystem (NT)
|
|
*/
|
|
if((flags&GZ_FEXTRA)!=0)
|
|
{
|
|
off+=2;
|
|
off+=buffer[10]|(buffer[11]<<8);
|
|
if(buffer[12]=='B' && buffer[13]=='C' && buffer[14]==2 && buffer[15]==0)
|
|
bgzf=1;
|
|
}
|
|
if((flags&GZ_FNAME)!=0)
|
|
{
|
|
for(; off<buffer_size && buffer[off]!='\0'; off++)
|
|
{
|
|
}
|
|
off++;
|
|
}
|
|
if((flags&GZ_FCOMMENT)!=0)
|
|
{
|
|
for(; off<buffer_size && buffer[off]!='\0'; off++)
|
|
{
|
|
}
|
|
off++;
|
|
}
|
|
if((flags&GZ_FHCRC)!=0)
|
|
{
|
|
off+=2;
|
|
}
|
|
if(off >= 512 || off >= buffer_size)
|
|
return 0;
|
|
/*@ assert off < 512; */
|
|
/*@ assert off < buffer_size ; */
|
|
#if defined(HAVE_ZLIB_H) && defined(HAVE_LIBZ)
|
|
{
|
|
static const unsigned char schematic_header[12]={ 0x0a, 0x00, 0x09,
|
|
'S', 'c', 'h', 'e', 'm', 'a', 't', 'i', 'c'};
|
|
static const unsigned char tar_header_posix[8] = { 'u','s','t','a','r',' ',' ',0x00};
|
|
const unsigned char *buffer_compr=buffer+off;
|
|
unsigned char buffer_uncompr[4096];
|
|
const unsigned int uncomprLen=sizeof(buffer_uncompr)-1;
|
|
const unsigned int bs=td_max(512U,file_recovery_new->blocksize);
|
|
/*@ assert bs >=512; */
|
|
const unsigned int comprLen=td_min(buffer_size,bs)-off;
|
|
/*@ assert comprLen > 0; */
|
|
int err;
|
|
z_stream d_stream; /* decompression stream */
|
|
d_stream.zalloc = (alloc_func)0;
|
|
d_stream.zfree = (free_func)0;
|
|
d_stream.opaque = (voidpf)0;
|
|
|
|
d_stream.next_in = (Bytef*)buffer_compr;
|
|
d_stream.avail_in = 0;
|
|
d_stream.next_out = buffer_uncompr;
|
|
|
|
err = inflateInit2(&d_stream, -MAX_WBITS);
|
|
if(err!=Z_OK)
|
|
return 0;
|
|
while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) {
|
|
d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */
|
|
err = inflate(&d_stream, Z_NO_FLUSH);
|
|
if (err == Z_STREAM_END) break;
|
|
if(err!=Z_OK)
|
|
{
|
|
/* Decompression has failed, free ressources */
|
|
inflateEnd(&d_stream);
|
|
return 0;
|
|
}
|
|
}
|
|
err = inflateEnd(&d_stream);
|
|
if(err!=Z_OK)
|
|
return 0;
|
|
/* Probably too small to be a file */
|
|
if(d_stream.total_out < 16)
|
|
return 0;
|
|
#ifndef SINGLE_FORMAT
|
|
if(file_recovery->file_stat!=NULL &&
|
|
file_recovery->file_stat->file_hint==&file_hint_doc)
|
|
{
|
|
if(header_ignored_adv(file_recovery, file_recovery_new)==0)
|
|
return 0;
|
|
}
|
|
#endif
|
|
if(file_recovery->file_check==&file_check_bgzf)
|
|
{
|
|
/*@ assert \valid_function(file_recovery->file_check); */
|
|
header_ignored(file_recovery_new);
|
|
return 0;
|
|
}
|
|
buffer_uncompr[d_stream.total_out]='\0';
|
|
if(bgzf!=0)
|
|
{
|
|
return header_check_bgzf(buffer, buffer_uncompr, d_stream.total_out, file_recovery_new);
|
|
}
|
|
reset_file_recovery(file_recovery_new);
|
|
file_recovery_new->min_filesize=22;
|
|
file_recovery_new->time=le32(gz->mtime);
|
|
file_recovery_new->file_rename=&file_rename_gz;
|
|
if(d_stream.avail_in==0 && d_stream.total_in < comprLen && d_stream.total_out < uncomprLen)
|
|
{
|
|
/* an 8-byte footer, containing a CRC-32 checksum and
|
|
* the length of the original uncompressed data, modulo 2^32
|
|
*/
|
|
file_recovery_new->calculated_file_size=off+d_stream.total_in+8;
|
|
file_recovery_new->data_check=&data_check_size;
|
|
file_recovery_new->file_check=&file_check_size;
|
|
}
|
|
if(memcmp(buffer_uncompr, "PVP ", 4)==0)
|
|
{
|
|
/* php Video Pro */
|
|
file_recovery_new->extension="pvp";
|
|
return 1;
|
|
}
|
|
if(memcmp(buffer_uncompr, "<?xml version=\"1.0\" standalone=\"no\"?>\n<xournal", 46)==0)
|
|
{
|
|
/* Xournal, http://xournal.sourceforge.net/ */
|
|
file_recovery_new->extension="xoj";
|
|
return 1;
|
|
}
|
|
if( memcmp(buffer_uncompr, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Ableton", 0x30)==0 ||
|
|
memcmp(buffer_uncompr, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Ableton", 0x2f)==0)
|
|
{
|
|
/* Ableton Liveset */
|
|
file_recovery_new->extension="als";
|
|
return 1;
|
|
}
|
|
if(memcmp(buffer_uncompr, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<PremiereData", 0x34)==0)
|
|
{
|
|
/* Adobe Premiere */
|
|
file_recovery_new->extension="prproj";
|
|
return 1;
|
|
}
|
|
if(memcmp(buffer_uncompr, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<gnc-v2", 47)==0)
|
|
{
|
|
/* GnuCash, http://gnucash.org/ */
|
|
file_recovery_new->extension="gnucash";
|
|
return 1;
|
|
}
|
|
if(strstr((const char*)&buffer_uncompr, "<!DOCTYPE KMYMONEY-FILE>")!=NULL)
|
|
{
|
|
file_recovery_new->extension="kmy";
|
|
return 1;
|
|
}
|
|
#ifndef DJGPP
|
|
if(memcmp(buffer_uncompr, "RDX2", 4)==0)
|
|
{
|
|
/* R - language and environment for statistical computing and graphics */
|
|
file_recovery_new->extension="RData";
|
|
return 1;
|
|
}
|
|
if(memcmp(buffer_uncompr, "<?xml version=", 14) == 0)
|
|
{
|
|
file_recovery_new->extension="xml.gz";
|
|
return 1;
|
|
}
|
|
if(memcmp(buffer_uncompr, schematic_header, sizeof(schematic_header))==0)
|
|
{
|
|
/* Minecraft Schematic File */
|
|
file_recovery_new->extension="schematic";
|
|
return 1;
|
|
}
|
|
{
|
|
unsigned int i;
|
|
for(i=0; i<d_stream.total_out && i< 256; i++)
|
|
{
|
|
if(buffer_uncompr[i]=='<')
|
|
{
|
|
if(strncasecmp((const char*)&buffer_uncompr[i], "<html", 5)==0)
|
|
{
|
|
file_recovery_new->extension="html.gz";
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if(d_stream.total_out>0x110 &&
|
|
memcmp(&buffer_uncompr[0x101],tar_header_posix,sizeof(tar_header_posix))==0)
|
|
{
|
|
#ifdef DJGPP
|
|
file_recovery_new->extension="tgz";
|
|
#else
|
|
file_recovery_new->extension="tar.gz";
|
|
#endif
|
|
return 1;
|
|
}
|
|
}
|
|
#else
|
|
#ifndef SINGLE_FORMAT
|
|
if(file_recovery->file_stat!=NULL &&
|
|
file_recovery->file_stat->file_hint==&file_hint_doc)
|
|
{
|
|
if(header_ignored_adv(file_recovery, file_recovery_new)==0)
|
|
return 0;
|
|
}
|
|
#endif
|
|
reset_file_recovery(file_recovery_new);
|
|
file_recovery_new->min_filesize=22;
|
|
file_recovery_new->time=le32(gz->mtime);
|
|
file_recovery_new->file_rename=&file_rename_gz;
|
|
#endif
|
|
file_recovery_new->extension=file_hint_gz.extension;
|
|
return 1;
|
|
}
|
|
|
|
static void register_header_check_gz(file_stat_t *file_stat)
|
|
{
|
|
static const unsigned char gz_header_magic[3]= {0x1F, 0x8B, 0x08};
|
|
register_header_check(0, gz_header_magic,sizeof(gz_header_magic), &header_check_gz, file_stat);
|
|
}
|
|
#endif
|
|
|
|
const char*td_zlib_version(void)
|
|
{
|
|
#if defined(HAVE_ZLIB_H) && defined(HAVE_LIBZ)
|
|
return ZLIB_VERSION;
|
|
#else
|
|
return "none";
|
|
#endif
|
|
}
|