/* File: file_arj.c Copyright (C) 2008 Christophe GRENIER 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_arj) #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_STRING_H #include #endif #include #include "types.h" #include "filegen.h" #include "common.h" /*@ requires valid_register_header_check(file_stat); */ static void register_header_check_arj(file_stat_t *file_stat); const file_hint_t file_hint_arj= { .extension="arj", .description="ARJ archive", .max_filesize=PHOTOREC_MAX_SIZE_32, .recover=1, .enable_by_default=1, .register_header_check=®ister_header_check_arj }; /* * 60 ea 24 00 22 0b 01 02 10 00 02 XX XX XX 50 48 * ID HS FH V V OS FL SV FT R DATE/TIME * * Extract from "ARJ TECHNICAL INFORMATION April 1993" http://datacompression.info/ArchiveFormats/arj.txt Structure of main header (low order byte first): Bytes Description ------------------------------------------------------------------- 2 header id (main and local file) = 0x60 0xEA 2 basic header size (from 'first_hdr_size' thru 'comment' below) = first_hdr_size + strlen(filename) + 1 + strlen(comment) + 1 = 0 if end of archive maximum header size is 2600 1 first_hdr_size (size up to and including 'extra data') 1 archiver version number 1 minimum archiver version to extract 1 host OS (0 = MSDOS, 1 = PRIMOS, 2 = UNIX, 3 = AMIGA, 4 = MAC-OS) (5 = OS/2, 6 = APPLE GS, 7 = ATARI ST, 8 = NEXT) (9 = VAX VMS) 1 arj flags (0x01 = NOT USED) (0x02 = OLD_SECURED_FLAG) (0x04 = VOLUME_FLAG) indicates presence of succeeding volume (0x08 = NOT USED) (0x10 = PATHSYM_FLAG) indicates archive name translated ("\" changed to "/") (0x20 = BACKUP_FLAG) indicates backup type archive (0x40 = SECURED_FLAG) 1 security version (2 = current) 1 file type (must equal 2) 1 reserved 4 date time when original archive was created 4 date time when archive was last modified 4 archive size (currently used only for secured archives) 4 security envelope file position 2 filespec position in filename 2 length in bytes of security envelope data 2 (currently not used) ? (currently none) ? filename of archive when created (null-terminated string) ? archive comment (null-terminated string) 4 basic header CRC 2 1st extended header size (0 if none) ? 1st extended header (currently not used) 4 1st extended header's CRC (not present when 0 extended header size) */ struct arj_main_header { uint16_t header_id; uint16_t basic_header_size; uint8_t first_header_size; uint8_t archiver_ver; uint8_t archiver_ver_min; uint8_t host_os; uint8_t flags; uint8_t security_ver; uint8_t file_type; uint8_t reserved; uint32_t ctime; uint32_t mtime; uint32_t size; uint32_t security_env_pos; uint16_t filespec_pos; uint16_t security_env_size; uint16_t unused; char filename; } __attribute__ ((gcc_struct, __packed__)); /*@ @ requires file_recovery->file_check == &file_check_arj; @ requires valid_file_check_param(file_recovery); @ ensures valid_file_check_result(file_recovery); @ assigns *file_recovery->handle, errno, file_recovery->file_size; @ assigns Frama_C_entropy_source; @*/ static void file_check_arj(file_recovery_t *file_recovery) { static const unsigned char arj_footer[4]={0x60, 0xEA, 0x00, 0x00 }; file_search_footer(file_recovery, arj_footer, sizeof(arj_footer), 0); } /*@ @ requires buffer_size >= sizeof(struct arj_main_header); @ requires separation: \separated(&file_hint_arj, 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); @ assigns *file_recovery_new; @*/ static int header_check_arj(const unsigned char *buffer, const unsigned int buffer_size, const unsigned int safe_header_only, const file_recovery_t *file_recovery, file_recovery_t *file_recovery_new) { const struct arj_main_header *arj=(const struct arj_main_header*)buffer; if(le16(arj->basic_header_size) > 0 && le16(arj->basic_header_size) <= 2600 && arj->archiver_ver_min <= arj->archiver_ver && arj->archiver_ver <=12 && (arj->flags&0x01)==0 && arj->file_type==2) { if((arj->flags&0x040)!=0) { if(le32(arj->size) < sizeof(struct arj_main_header)) return 0; reset_file_recovery(file_recovery_new); file_recovery_new->calculated_file_size=le32(arj->size); file_recovery_new->data_check=&data_check_size; file_recovery_new->file_check=&file_check_size; } else { // if(le32(arj->size)!=0) // return 0; reset_file_recovery(file_recovery_new); file_recovery_new->file_check=&file_check_arj; } file_recovery_new->extension=file_hint_arj.extension; file_recovery_new->time=le32(arj->ctime); if(file_recovery_new->time < le32(arj->mtime)) file_recovery_new->time=le32(arj->mtime); return 1; } return 0; } static void register_header_check_arj(file_stat_t *file_stat) { static const unsigned char arj_header[2]={0x60, 0xEA}; register_header_check(0, arj_header,sizeof(arj_header), &header_check_arj, file_stat); } #endif