24fd05e1 |
/* |
9518ee06 |
* Copyright (C) 2007-2008 Sourcefire, Inc.
* |
2023340a |
* Authors: Tomasz Kojm |
24fd05e1 |
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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 to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <fcntl.h>
|
60d8d2c3 |
#include "clamav.h" |
24fd05e1 |
#include "cltypes.h"
#include "others.h"
#include "mspack.h"
#include "cab.h"
|
8bc88497 |
#define EC32(x) cli_readint32(&x) /* Convert little endian to host */
#define EC16(x) cli_readint16(&x) |
24fd05e1 |
/* hard limits */ |
86e209d6 |
#define CAB_FOLDER_LIMIT 5000 |
24fd05e1 |
#define CAB_FILE_LIMIT 5000
/* Cabinet format data structures */
struct cab_hdr {
uint32_t signature; /* file signature */
uint32_t res1; /* reserved */
uint32_t cbCabinet; /* size of cabinet file */
uint32_t res2; /* reserved */
uint32_t coffFiles; /* offset of the first file entry */
uint32_t res3; /* reserved */
uint8_t versionMinor; /* file format version, minor */
uint8_t versionMajor; /* file format version, major */
uint16_t cFolders; /* number of folder entries */
uint16_t cFiles; /* number of file entries */
uint16_t flags; /* option flags */
uint16_t setID; /* multiple cabs related */
uint16_t iCabinet; /* multiple cabs related */
};
struct cab_hdr_opt {
uint16_t cbCFHeader; /* size of reserved header area */
uint8_t cbCFFolder; /* size of reserved folder area */
uint8_t cbCFData; /* size of reserved block area */
};
struct cab_folder_hdr
{
uint32_t coffCabStart; /* offset of the first data block */
uint16_t cCFData; /* number of data blocks */
uint16_t typeCompress; /* compression type */
};
struct cab_file_hdr
{
uint32_t cbFile; /* uncompressed size */
uint32_t uoffFolderStart; /* uncompressed offset of file in folder */
uint16_t iFolder; /* folder index */
uint16_t date; /* date stamp */
uint16_t time; /* time stamp */
uint16_t attribs; /* attribute flags */
};
struct cab_block_hdr
{
uint32_t csum; /* data block checksum */
uint16_t cbData; /* number of compressed bytes */
uint16_t cbUncomp; /* number of uncompressed bytes */
};
|
f162c401 |
static char *cab_readstr(fmap_t *map, off_t *offset, int *ret) |
24fd05e1 |
{ |
f162c401 |
int i; |
f304dc68 |
const char *str;
char *retstr; |
24fd05e1 |
|
f162c401 |
if(!(str = fmap_need_offstr(map, *offset, 256))) {
*ret = CL_EFORMAT; |
24fd05e1 |
return NULL;
}
|
f162c401 |
i = strlen(str) + 1;
if(i>=255) {
fmap_unneed_ptr(map, str, i); |
24fd05e1 |
*ret = CL_EFORMAT;
return NULL;
}
|
f162c401 |
*offset += i;
if((retstr = cli_malloc(i)))
memcpy(retstr, str, i);
fmap_unneed_ptr(map, str, i); |
24fd05e1 |
|
f162c401 |
if(!retstr) { |
24fd05e1 |
*ret = CL_EMEM;
return NULL;
}
*ret = CL_SUCCESS; |
f162c401 |
return retstr; |
24fd05e1 |
}
|
72ce4b70 |
static int cab_chkname(char *name, int san) |
24fd05e1 |
{
size_t i, len = strlen(name);
for(i = 0; i < len; i++) { |
72ce4b70 |
if(!san && (strchr("%/*?|\\\"+=<>;:\t ", name[i]) || !isascii(name[i]))) { |
24fd05e1 |
cli_dbgmsg("cab_chkname: File name contains disallowed characters\n");
return 1; |
72ce4b70 |
} else if(san && !isalnum(name[i])) {
name[i] = '*'; |
24fd05e1 |
}
}
return 0;
}
void cab_free(struct cab_archive *cab)
{
struct cab_folder *folder;
struct cab_file *file;
|
9518ee06 |
if(cab->state) { |
4ad62d4e |
if(cab->state->stream) {
switch(cab->state->cmethod & 0x000f) {
case 0x0001:
mszip_free(cab->state->stream);
break;
case 0x0002:
qtm_free(cab->state->stream);
break;
case 0x0003:
lzx_free(cab->state->stream);
} |
9518ee06 |
}
free(cab->state);
}
|
24fd05e1 |
while(cab->folders) {
folder = cab->folders;
cab->folders = cab->folders->next;
free(folder);
}
while(cab->files) {
file = cab->files;
cab->files = cab->files->next;
free(file->name);
free(file);
}
}
|
70f20eb8 |
int cab_open(fmap_t *map, off_t offset, struct cab_archive *cab) |
24fd05e1 |
{ |
72ce4b70 |
unsigned int i, folders = 0; |
24fd05e1 |
struct cab_file *file, *lfile = NULL;
struct cab_folder *folder, *lfolder = NULL; |
f304dc68 |
const struct cab_hdr *hdr;
const struct cab_hdr_opt *hdr_opt; |
24fd05e1 |
uint16_t fidx; |
f162c401 |
uint32_t coffFiles; |
24fd05e1 |
char *pt;
int ret; |
f162c401 |
off_t resfold = 0, rsize, cur_offset = offset; |
24fd05e1 |
|
f162c401 |
if(!(hdr=fmap_need_off_once(map, cur_offset, sizeof(*hdr)))) { |
c35ebc99 |
cli_dbgmsg("cab_open: Can't read cabinet header\n");
return CL_EFORMAT; /* most likely a corrupted file */
} |
f162c401 |
cur_offset += sizeof(*hdr); |
24fd05e1 |
|
c35ebc99 |
if(EC32(hdr->signature) != 0x4643534d) { |
24fd05e1 |
cli_dbgmsg("cab_open: Incorrect CAB signature\n");
return CL_EFORMAT;
} else {
cli_dbgmsg("CAB: -------------- Cabinet file ----------------\n");
}
|
f162c401 |
rsize = map->len; |
24fd05e1 |
memset(cab, 0, sizeof(struct cab_archive));
|
c35ebc99 |
cab->length = EC32(hdr->cbCabinet); |
24fd05e1 |
cli_dbgmsg("CAB: Cabinet length: %u\n", cab->length); |
f0f7f92f |
if((off_t) cab->length > rsize) { |
72ce4b70 |
cli_dbgmsg("CAB: Truncating file size from %lu to %lu\n", (unsigned long int) cab->length, (unsigned long int) rsize); |
f0f7f92f |
cab->length = (uint32_t) rsize;
} |
24fd05e1 |
|
c35ebc99 |
cab->nfolders = EC16(hdr->cFolders); |
24fd05e1 |
if(!cab->nfolders) {
cli_dbgmsg("cab_open: No folders in cabinet (fake cab?)\n");
return CL_EFORMAT;
} else {
cli_dbgmsg("CAB: Folders: %u\n", cab->nfolders);
if(cab->nfolders > CAB_FOLDER_LIMIT) {
cab->nfolders = CAB_FOLDER_LIMIT;
cli_dbgmsg("CAB: *** Number of folders limited to %u ***\n", cab->nfolders);
}
}
|
c35ebc99 |
cab->nfiles = EC16(hdr->cFiles); |
24fd05e1 |
if(!cab->nfiles) {
cli_dbgmsg("cab_open: No files in cabinet (fake cab?)\n");
return CL_EFORMAT;
} else {
cli_dbgmsg("CAB: Files: %u\n", cab->nfiles);
if(cab->nfiles > CAB_FILE_LIMIT) {
cab->nfiles = CAB_FILE_LIMIT;
cli_dbgmsg("CAB: *** Number of files limited to %u ***\n", cab->nfiles);
}
}
|
c35ebc99 |
cli_dbgmsg("CAB: File format version: %u.%u\n", hdr->versionMajor, hdr->versionMinor); |
24fd05e1 |
|
c35ebc99 |
cab->flags = EC16(hdr->flags); |
f162c401 |
coffFiles = EC16(hdr->coffFiles);
|
24fd05e1 |
if(cab->flags & 0x0004) { |
f162c401 |
if(!(hdr_opt = fmap_need_off_once(map, cur_offset, sizeof(*hdr_opt)))) { |
24fd05e1 |
cli_dbgmsg("cab_open: Can't read file header (fake cab?)\n"); |
0ba3503b |
return CL_EFORMAT; /* most likely a corrupted file */ |
24fd05e1 |
}
|
f162c401 |
cab->reshdr = EC16(hdr_opt->cbCFHeader);
resfold = hdr_opt->cbCFFolder;
cab->resdata = hdr_opt->cbCFData; |
24fd05e1 |
|
f060dbf9 |
cur_offset += sizeof(*hdr_opt) + cab->reshdr; |
24fd05e1 |
if(cab->reshdr) { |
f162c401 |
if(cab->reshdr >= rsize) { |
24fd05e1 |
cli_dbgmsg("cab_open: Can't lseek to %u (fake cab?)\n", cab->reshdr); |
0ba3503b |
return CL_EFORMAT; /* most likely a corrupted file */ |
24fd05e1 |
} |
f060dbf9 |
} |
24fd05e1 |
}
|
09b0916b |
if(cab->flags & 0x0001) { /* preceding cabinet */ |
24fd05e1 |
/* name */ |
f162c401 |
pt = cab_readstr(map, &cur_offset, &ret); |
24fd05e1 |
if(ret)
return ret; |
72ce4b70 |
if(cab_chkname(pt, 0)) |
09b0916b |
cli_dbgmsg("CAB: Invalid name of preceding cabinet\n"); |
24fd05e1 |
else |
09b0916b |
cli_dbgmsg("CAB: Preceding cabinet name: %s\n", pt); |
24fd05e1 |
free(pt);
/* info */ |
f162c401 |
pt = cab_readstr(map, &cur_offset, &ret); |
24fd05e1 |
if(ret)
return ret; |
72ce4b70 |
if(cab_chkname(pt, 0)) |
09b0916b |
cli_dbgmsg("CAB: Invalid info for preceding cabinet\n"); |
24fd05e1 |
else |
09b0916b |
cli_dbgmsg("CAB: Preceding cabinet info: %s\n", pt); |
24fd05e1 |
free(pt);
}
if(cab->flags & 0x0002) { /* next cabinet */
/* name */ |
f162c401 |
pt = cab_readstr(map, &cur_offset, &ret); |
24fd05e1 |
if(ret)
return ret; |
72ce4b70 |
if(cab_chkname(pt, 0))
cli_dbgmsg("CAB: Invalid name of next cabinet\n"); |
24fd05e1 |
else
cli_dbgmsg("CAB: Next cabinet name: %s\n", pt);
free(pt);
/* info */ |
f162c401 |
pt = cab_readstr(map, &cur_offset, &ret); |
24fd05e1 |
if(ret)
return ret; |
72ce4b70 |
if(cab_chkname(pt, 0))
cli_dbgmsg("CAB: Invalid info for next cabinet\n"); |
24fd05e1 |
else
cli_dbgmsg("CAB: Next cabinet info: %s\n", pt);
free(pt);
}
/* folders */
for(i = 0; i < cab->nfolders; i++) { |
f304dc68 |
const struct cab_folder_hdr *folder_hdr; |
f162c401 |
if(!(folder_hdr = fmap_need_off_once(map, cur_offset, sizeof(*folder_hdr)))) { |
72ce4b70 |
cli_dbgmsg("cab_open: Can't read header for folder %u\n", i);
break; |
24fd05e1 |
}
|
f162c401 |
cur_offset += sizeof(*folder_hdr) + resfold; |
24fd05e1 |
|
f162c401 |
if(EC32(folder_hdr->coffCabStart) + offset > rsize) { |
72ce4b70 |
cli_dbgmsg("CAB: Folder out of file\n");
continue;
}
|
f162c401 |
if((EC16(folder_hdr->typeCompress) & 0x000f) > 3) { |
72ce4b70 |
cli_dbgmsg("CAB: Unknown compression method\n");
continue;
}
|
24fd05e1 |
folder = (struct cab_folder *) cli_calloc(1, sizeof(struct cab_folder));
if(!folder) {
cli_errmsg("cab_open: Can't allocate memory for folder\n");
cab_free(cab);
return CL_EMEM;
}
folder->cab = (struct cab_archive *) cab; |
f162c401 |
folder->offset = (off_t) EC32(folder_hdr->coffCabStart) + offset;
folder->nblocks = EC16(folder_hdr->cCFData);
folder->cmethod = EC16(folder_hdr->typeCompress); |
24fd05e1 |
cli_dbgmsg("CAB: Folder record %u\n", i);
cli_dbgmsg("CAB: Folder offset: %u\n", (unsigned int) folder->offset);
cli_dbgmsg("CAB: Folder compression method: %d\n", folder->cmethod);
if(!lfolder)
cab->folders = folder;
else
lfolder->next = folder;
lfolder = folder; |
72ce4b70 |
folders++; |
24fd05e1 |
} |
72ce4b70 |
cli_dbgmsg("CAB: Recorded folders: %u\n", folders); |
24fd05e1 |
/* files */ |
f162c401 |
if(cab->nfolders != folders) {
if(coffFiles >= rsize) {
cli_dbgmsg("cab_open: Can't lseek to hdr.coffFiles\n");
cab_free(cab);
return CL_EFORMAT;
}
cur_offset = coffFiles; |
72ce4b70 |
} |
24fd05e1 |
for(i = 0; i < cab->nfiles; i++) { |
f304dc68 |
const struct cab_file_hdr *file_hdr; |
f162c401 |
if(!(file_hdr = fmap_need_off_once(map, cur_offset, sizeof(*file_hdr)))) { |
72ce4b70 |
cli_dbgmsg("cab_open: Can't read file %u header\n", i);
break; |
24fd05e1 |
} |
f162c401 |
cur_offset += sizeof(*file_hdr); |
24fd05e1 |
file = (struct cab_file *) cli_calloc(1, sizeof(struct cab_file));
if(!file) {
cli_errmsg("cab_open: Can't allocate memory for file\n");
cab_free(cab);
return CL_EMEM;
}
file->cab = cab; |
7e1fb3ee |
cab->map = map; |
f162c401 |
file->offset = EC32(file_hdr->uoffFolderStart);
file->length = EC32(file_hdr->cbFile); |
2704967c |
file->attribs = EC16(file_hdr->attribs);
fidx = EC16(file_hdr->iFolder); |
72ce4b70 |
file->error = CL_SUCCESS; |
24fd05e1 |
|
f162c401 |
file->name = cab_readstr(map, &cur_offset, &ret); |
24fd05e1 |
if(ret) {
free(file); |
72ce4b70 |
continue; |
24fd05e1 |
} |
72ce4b70 |
cab_chkname(file->name, 1); |
24fd05e1 |
cli_dbgmsg("CAB: File record %u\n", i);
cli_dbgmsg("CAB: File name: %s\n", file->name); |
794f3f23 |
cli_dbgmsg("CAB: File offset: %u\n", (unsigned int) file->offset); |
24fd05e1 |
cli_dbgmsg("CAB: File folder index: %u\n", fidx);
cli_dbgmsg("CAB: File attribs: 0x%x\n", file->attribs);
if(file->attribs & 0x01)
cli_dbgmsg("CAB: * file is read-only\n");
if(file->attribs & 0x02)
cli_dbgmsg("CAB: * file is hidden\n");
if(file->attribs & 0x04)
cli_dbgmsg("CAB: * file is a system file\n");
if(file->attribs & 0x20)
cli_dbgmsg("CAB: * file modified since last backup\n");
if(file->attribs & 0x40)
cli_dbgmsg("CAB: * file to be run after extraction\n");
if(file->attribs & 0x80)
cli_dbgmsg("CAB: * file name contains UTF\n");
/* folder index */
if(fidx < 0xfffd) {
if(fidx > cab->nfolders) { |
72ce4b70 |
cli_dbgmsg("cab_open: File %s is not associated with any folder\n", file->name); |
24fd05e1 |
free(file->name);
free(file);
continue;
}
file->folder = cab->folders;
while(file->folder && fidx--)
file->folder = file->folder->next;
if(!file->folder) { |
72ce4b70 |
cli_dbgmsg("cab_open: Folder not found for file %s\n", file->name); |
24fd05e1 |
free(file->name);
free(file); |
72ce4b70 |
continue; |
24fd05e1 |
}
} else {
cli_dbgmsg("CAB: File is split *skipping*\n");
free(file->name);
free(file);
continue;
}
if(!lfile)
cab->files = file;
else
lfile->next = file;
lfile = file;
}
return CL_SUCCESS;
}
|
7e1fb3ee |
static int cab_read_block(struct cab_file *file) |
24fd05e1 |
{ |
f304dc68 |
const struct cab_block_hdr *block_hdr; |
7e1fb3ee |
struct cab_state *state = file->cab->state; |
24fd05e1 |
|
7e1fb3ee |
if(!(block_hdr = fmap_need_off_once(file->cab->map, file->cab->cur_offset, sizeof(*block_hdr)))) { |
2b304af9 |
cli_dbgmsg("cab_read_block: Can't read block header\n"); |
0ba3503b |
return CL_EFORMAT; /* most likely a corrupted file */ |
24fd05e1 |
}
|
7e1fb3ee |
file->cab->cur_offset += sizeof(*block_hdr) + file->cab->resdata;
state->blklen = EC16(block_hdr->cbData);
state->outlen = EC16(block_hdr->cbUncomp); |
24fd05e1 |
|
7e1fb3ee |
if(fmap_readn(file->cab->map, state->block, file->cab->cur_offset, state->blklen) != state->blklen) { |
24fd05e1 |
cli_dbgmsg("cab_read_block: Can't read block data\n"); |
0ba3503b |
return CL_EFORMAT; /* most likely a corrupted file */ |
24fd05e1 |
}
|
7e1fb3ee |
file->cab->cur_offset += state->blklen; |
24fd05e1 |
state->pt = state->end = state->block;
state->end += state->blklen;
return CL_SUCCESS;
}
static int cab_read(struct cab_file *file, unsigned char *buffer, int bytes)
{
uint16_t todo, left;
|
72ce4b70 |
if((file->cab->state->blknum > file->folder->nblocks) && !file->lread) {
file->error = CL_BREAK;
return -1;
}
|
24fd05e1 |
todo = bytes;
while(todo > 0) { |
9518ee06 |
left = file->cab->state->end - file->cab->state->pt; |
24fd05e1 |
if(left) {
if(left > todo)
left = todo;
|
9518ee06 |
memcpy(buffer, file->cab->state->pt, left);
file->cab->state->pt += left; |
24fd05e1 |
buffer += left;
todo -= left;
} else { |
72ce4b70 |
if(file->cab->state->blknum++ >= file->folder->nblocks) |
24fd05e1 |
break;
|
7e1fb3ee |
file->error = cab_read_block(file); |
24fd05e1 |
if(file->error)
return -1;
if((file->folder->cmethod & 0x000f) == 0x0002) /* Quantum hack */ |
9518ee06 |
*file->cab->state->end++ = 0xff; |
24fd05e1 |
|
9518ee06 |
if(file->cab->state->blknum >= file->folder->nblocks) { |
24fd05e1 |
if((file->folder->cmethod & 0x000f) == 0x0003) { /* LZX hack */ |
6cb9a52c |
lzx_set_output_length(file->cab->state->stream, (off_t) ((file->cab->state->blknum - 1) * 32768 + file->cab->state->outlen)); |
24fd05e1 |
}
} else { |
6cb9a52c |
if(file->cab->state->outlen != 32768) { |
24fd05e1 |
cli_dbgmsg("cab_read: WARNING: partial data block\n");
}
}
}
}
|
72ce4b70 |
return file->lread = bytes - todo; |
24fd05e1 |
}
|
7e1fb3ee |
static int cab_unstore(struct cab_file *file) |
5a5e6312 |
{ |
7e1fb3ee |
int todo, bread, bytes = file->length; |
5a5e6312 |
unsigned char buff[4096];
|
7e727361 |
if(bytes < 0) { |
d6f52e09 |
cli_dbgmsg("cab_unstore: bytes < 0\n"); |
7e727361 |
return CL_EFORMAT;
}
|
8df99a92 |
todo = MIN((unsigned int) bytes, file->max_size); |
7e727361 |
|
5a5e6312 |
while(1) {
|
50084a59 |
if((unsigned int) todo <= sizeof(buff))
bread = todo;
else
bread = sizeof(buff); |
5a5e6312 |
|
50084a59 |
if((bread = cab_read(file, buff, bread)) == -1) { |
7e1fb3ee |
cli_dbgmsg("cab_unstore: cab_read failed\n"); |
50084a59 |
return file->error;
} else if(cli_writen(file->ofd, buff, bread) != bread) {
cli_warnmsg("cab_unstore: Can't write %d bytes to descriptor %d\n", bread, file->ofd); |
871177cd |
return CL_EWRITE; |
5a5e6312 |
} |
50084a59 |
todo -= bread;
if(!bread || todo <= 0)
break; |
5a5e6312 |
}
return CL_SUCCESS;
}
|
9518ee06 |
#define CAB_CHGFOLDER \ |
4c9703c2 |
if(!file->cab->actfol || (file->folder != file->cab->actfol) \
|| (file->cab->state && file->cab->state->cmethod != file->folder->cmethod)) { \ |
9518ee06 |
if(file->cab->state) { \ |
4ad62d4e |
if(file->cab->state->stream) { \
switch(file->cab->state->cmethod & 0x000f) { \
case 0x0001: \
mszip_free(file->cab->state->stream); \
break; \
case 0x0002: \
qtm_free(file->cab->state->stream); \
break; \
case 0x0003: \
lzx_free(file->cab->state->stream); \
} \ |
9518ee06 |
} \
free(file->cab->state); \ |
4ad62d4e |
file->cab->state = NULL; \ |
9518ee06 |
} \ |
7e1fb3ee |
file->cab->cur_offset = file->folder->offset; \ |
4c9703c2 |
file->cab->state = (struct cab_state *) cli_calloc(1, sizeof(struct cab_state)); \ |
9518ee06 |
if(!file->cab->state) { \ |
4c9703c2 |
cli_errmsg("cab_extract: Can't allocate memory for internal state\n"); \ |
24555841 |
close(file->ofd); \ |
9518ee06 |
return CL_EMEM; \
} \
file->cab->state->cmethod = file->folder->cmethod; \
switch(file->folder->cmethod & 0x000f) { \
case 0x0001: \ |
4c9703c2 |
file->cab->state->stream = (struct mszip_stream *) mszip_init(file->ofd, 4096, 1, file, &cab_read); \ |
9518ee06 |
break; \
case 0x0002: \ |
f863fc90 |
file->cab->state->stream = (struct qtm_stream *) qtm_init(file->ofd, (int) (file->folder->cmethod >> 8) & 0x1f, 4096, file, &cab_read); \ |
9518ee06 |
break; \
case 0x0003: \ |
f863fc90 |
file->cab->state->stream = (struct lzx_stream *) lzx_init(file->ofd, (int) (file->folder->cmethod >> 8) & 0x1f, 0, 4096, 0, file, &cab_read); \ |
9518ee06 |
} \ |
79af2a64 |
if((file->folder->cmethod & 0x000f) && !file->cab->state->stream) { \ |
9518ee06 |
close(file->ofd); \ |
871177cd |
return CL_EUNPACK; \ |
9518ee06 |
} \ |
4ad62d4e |
file->cab->actfol = file->folder; \ |
24555841 |
} else { \
if(file->cab->state && file->cab->state->stream) { \
switch(file->cab->state->cmethod & 0x000f) { \
case 0x0001: \ |
4c9703c2 |
((struct mszip_stream *) file->cab->state->stream)->ofd = file->ofd; \ |
24555841 |
break; \
case 0x0002: \ |
4c9703c2 |
((struct qtm_stream *) file->cab->state->stream)->ofd = file->ofd; \ |
24555841 |
break; \
case 0x0003: \ |
4c9703c2 |
((struct lzx_stream *) file->cab->state->stream)->ofd = file->ofd; \ |
24555841 |
break; \
} \
} \ |
9518ee06 |
}
|
5a5e6312 |
int cab_extract(struct cab_file *file, const char *name) |
24fd05e1 |
{ |
5a5e6312 |
int ret; |
24fd05e1 |
if(!file || !name) {
cli_errmsg("cab_extract: !file || !name\n");
return CL_ENULLARG;
}
|
9518ee06 |
if(!file->folder) { |
24fd05e1 |
cli_errmsg("cab_extract: file->folder == NULL\n");
return CL_ENULLARG;
}
|
5a5e6312 |
file->ofd = open(name, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU);
if(file->ofd == -1) { |
24fd05e1 |
cli_errmsg("cab_extract: Can't open file %s in write mode\n", name); |
871177cd |
return CL_ECREAT; |
24fd05e1 |
}
switch(file->folder->cmethod & 0x000f) { |
5a5e6312 |
case 0x0000: /* STORE */ |
79af2a64 |
cli_dbgmsg("CAB: Compression method: STORED\n");
CAB_CHGFOLDER; |
f0f7f92f |
if(file->length > file->cab->length) {
cli_dbgmsg("cab_extract: Stored file larger than archive itself, trimming down\n");
file->length = file->cab->length;
} |
7e1fb3ee |
ret = cab_unstore(file); |
5a5e6312 |
break;
|
24fd05e1 |
case 0x0001: /* MSZIP */
cli_dbgmsg("CAB: Compression method: MSZIP\n"); |
9518ee06 |
CAB_CHGFOLDER;
ret = mszip_decompress(file->cab->state->stream, file->length); |
24fd05e1 |
break;
case 0x0002: /* QUANTUM */
cli_dbgmsg("CAB: Compression method: QUANTUM\n"); |
9518ee06 |
CAB_CHGFOLDER;
ret = qtm_decompress(file->cab->state->stream, file->length); |
24fd05e1 |
break;
case 0x0003: /* LZX */
cli_dbgmsg("CAB: Compression method: LZX\n"); |
9518ee06 |
CAB_CHGFOLDER;
ret = lzx_decompress(file->cab->state->stream, file->length); |
24fd05e1 |
break;
default: |
72ce4b70 |
cli_dbgmsg("CAB: Not supported compression method: 0x%x\n", file->folder->cmethod & 0x000f); |
24fd05e1 |
ret = CL_EFORMAT;
}
|
5a5e6312 |
close(file->ofd); |
24fd05e1 |
|
72ce4b70 |
if(ret == CL_BREAK)
ret = CL_SUCCESS;
|
24fd05e1 |
return ret;
} |