ClamAV is an open source (GPLv2) anti-virus toolkit.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
clamav/libclammspack/examples/multifh.c

435 lines
12 KiB

/* An implementation of the mspack_system interface which can access many
* things:
* - regular disk files
* - already opened stdio FILE* file pointers
* - open file descriptors
* - blocks of memory
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <mspack.h>
/* definitions */
#define MTYPE_DISKFILE (0x01)
#define MTYPE_STDIOFH (0x02)
#define MTYPE_FILEDESC (0x03)
#define MTYPE_MEMORY (0x04)
struct m_filename {
unsigned char type; /* one of MTYPE_DISKFILE, STDIOFH, FILEDESC or MEMORY */
const char *filename; /* the user-friendly printable filename (may be NULL) */
union {
const char *diskfile; /* char *filename for MTYPE_DISKFILE */
FILE *stdiofh; /* FILE *existing_fh for MTYPE_STDIOFH */
int filedesc; /* int file_descriptor for MTYPE_FILEDESC */
struct {
unsigned char *data;
size_t length;
} memory;
} x;
};
struct m_file {
struct m_filename *file; /* pointer back to the m_filename data */
union {
FILE *fh; /* only used in DISKFILE, STDIOFH and FILEDESC types */
size_t position; /* only used in MEMORY types */
} x;
};
/* ------------------------------------------------------------------------ */
/* mspack_system implementation */
static void *m_alloc(struct mspack_system *self, size_t bytes) {
return malloc(bytes);
}
static void m_free(void *buffer) {
free(buffer);
}
static void m_copy(void *src, void *dest, size_t bytes) {
memcpy(dest, src, bytes);
}
/* A message printer that prints to stderr */
static void m_msg(struct m_file *file, const char *format, ...) {
va_list ap;
if (file && file->file && file->file->filename) {
fprintf(stderr, "%s: ", file->file->filename);
}
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
fputc((int) '\n', stderr);
fflush(stderr);
}
static struct m_file *m_open_mem(struct mspack_system *self,
struct m_filename *fn, int mode)
{
struct m_file *fh;
/* validate arguments of the filename */
if (!fn->x.memory.data) return NULL;
if (!fn->x.memory.length) return NULL;
if ((fh = (struct m_file *) m_alloc(self, sizeof(struct m_file)))) {
fh->x.position = (mode == MSPACK_SYS_OPEN_APPEND) ?
fn->x.memory.length : 0;
fh->file = fn;
}
return fh;
}
static struct m_file *m_open_file(struct mspack_system *self,
struct m_filename *fn, int mode)
{
struct m_file *fh;
const char *fmode;
int fd;
switch (mode) {
case MSPACK_SYS_OPEN_READ: fmode = "rb"; break;
case MSPACK_SYS_OPEN_WRITE: fmode = "wb"; break;
case MSPACK_SYS_OPEN_UPDATE: fmode = "r+b"; break;
case MSPACK_SYS_OPEN_APPEND: fmode = "ab"; break;
default: return NULL;
}
/* validate the arguments in the provided filename */
switch (fn->type) {
case MTYPE_DISKFILE: if (!fn->x.diskfile) return NULL; break;
case MTYPE_STDIOFH: if (!fn->x.stdiofh) return NULL; break;
case MTYPE_FILEDESC: if (fn->x.filedesc < 0) return NULL; break;
}
/* allocate memory for the file handle */
if (!(fh = (struct m_file *) m_alloc(self, sizeof(struct m_file)))) return NULL;
/* open or duplicate the filehandle */
switch (fn->type) {
case MTYPE_DISKFILE:
fh->x.fh = fopen(fn->x.diskfile, fmode);
break;
case MTYPE_STDIOFH:
fd = fileno(fn->x.stdiofh);
fh->x.fh = (fd >= 0) ? fdopen(fd, fmode) : NULL;
break;
case MTYPE_FILEDESC:
fh->x.fh = fdopen(fn->x.filedesc, fmode);
break;
}
/* validate the new stdio filehandle */
if (fh->x.fh) {
fh->file = fn;
}
else {
free(fh);
fh = NULL;
}
return fh;
}
static struct m_file *m_open(struct mspack_system *self,
struct m_filename *fn, int mode)
{
if (!self || !fn) return NULL;
switch (fn->type) {
case MTYPE_DISKFILE:
case MTYPE_STDIOFH:
case MTYPE_FILEDESC:
return m_open_file(self, fn, mode);
case MTYPE_MEMORY:
return m_open_mem(self, fn, mode);
}
return NULL;
}
static void m_close(struct m_file *fh) {
if (!fh || !fh->file) return;
if (fh->file->type != MTYPE_MEMORY) fclose(fh->x.fh);
m_free(fh);
}
static int m_read(struct m_file *fh, void *buffer, int bytes) {
if (!fh || !fh->file || !buffer || bytes < 0) return -1;
if (fh->file->type == MTYPE_MEMORY) {
int count = fh->file->x.memory.length - fh->x.position;
if (count > bytes) count = bytes;
if (count > 0) {
m_copy(&fh->file->x.memory.data[fh->x.position], buffer, (size_t) count);
}
fh->x.position += count;
return count;
}
else {
size_t count = fread(buffer, 1, (size_t) bytes, fh->x.fh);
if (!ferror(fh->x.fh)) return (int) count;
}
return -1;
}
static int m_write(struct m_file *fh, void *buffer, int bytes) {
if (!fh || !fh->file || !buffer || bytes < 0) return -1;
if (fh->file->type == MTYPE_MEMORY) {
int count = fh->file->x.memory.length - fh->x.position;
if (count > bytes) count = bytes;
if (count > 0) {
m_copy(buffer, &fh->file->x.memory.data[fh->x.position], (size_t) count);
}
fh->x.position += count;
return count;
}
else {
size_t count = fwrite(buffer, 1, (size_t) bytes, fh->x.fh);
if (!ferror(fh->x.fh)) return (int) count;
}
return -1;
}
static int m_seek(struct m_file *fh, off_t offset, int mode) {
if (!fh || !fh->file) return 1;
if (fh->file->type == MTYPE_MEMORY) {
switch (mode) {
case MSPACK_SYS_SEEK_START:
break;
case MSPACK_SYS_SEEK_CUR:
offset += (off_t) fh->x.position;
break;
case MSPACK_SYS_SEEK_END:
offset += (off_t) fh->file->x.memory.length;
break;
default:
return 1;
}
if (offset < 0) return 1;
if (offset > (off_t) fh->file->x.memory.length) return 1;
fh->x.position = (size_t) offset;
return 0;
}
/* file IO based method */
switch (mode) {
case MSPACK_SYS_SEEK_START: mode = SEEK_SET; break;
case MSPACK_SYS_SEEK_CUR: mode = SEEK_CUR; break;
case MSPACK_SYS_SEEK_END: mode = SEEK_END; break;
default: return 1;
}
#if HAVE_FSEEKO
return fseeko(fh->x.fh, offset, mode);
#else
return fseek(fh->x.fh, offset, mode);
#endif
}
static off_t m_tell(struct m_file *fh) {
if (!fh || !fh->file) return -1;
if (fh->file->type == MTYPE_MEMORY) {
return (off_t) fh->x.position;
}
#if HAVE_FSEEKO
return (off_t) ftello(fh->x.fh);
#else
return (off_t) ftell(fh->x.fh);
#endif
}
static struct mspack_system multi_system = {
(struct mspack_file * (*)(struct mspack_system *, const char *, int)) &m_open,
(void (*)(struct mspack_file *)) &m_close,
(int (*)(struct mspack_file *, void *, int)) &m_read,
(int (*)(struct mspack_file *, void *, int)) &m_write,
(int (*)(struct mspack_file *, off_t, int)) &m_seek,
(off_t (*)(struct mspack_file *)) &m_tell,
(void (*)(struct mspack_file *, const char *, ...)) &m_msg,
&m_alloc,
&m_free,
&m_copy,
NULL
};
/* ------------------------------------------------------------------------ */
/* constructors and destructor */
const char *create_filename(const char *filename) {
struct m_filename *fn;
if (!filename) return NULL; /* filename must not be null */
if ((fn = (struct m_filename *) malloc(sizeof(struct m_filename)))) {
fn->type = MTYPE_DISKFILE;
fn->filename = filename; /* pretty-printable filename */
fn->x.diskfile = filename;
}
return (const char *) fn;
}
const char *create_filename_from_handle(FILE *fh) {
struct m_filename *fn;
if (!fh) return NULL; /* file handle must not be null */
if ((fn = (struct m_filename *) malloc(sizeof(struct m_filename)))) {
fn->type = MTYPE_STDIOFH;
fn->filename = NULL; /* pretty-printable filename */
fn->x.stdiofh = fh;
}
return (const char *) fn;
}
const char *create_filename_from_descriptor(int fd) {
struct m_filename *fn;
if (fd < 0) return NULL; /* file descriptor must be valid */
if ((fn = (struct m_filename *) malloc(sizeof(struct m_filename)))) {
fn->type = MTYPE_FILEDESC;
fn->filename = NULL; /* pretty-printable filename */
fn->x.filedesc = fd;
}
return (const char *) fn;
}
const char *create_filename_from_memory(void *data, size_t length) {
struct m_filename *fn;
if (!data) return NULL; /* data pointer must not be NULL */
if (length == 0) return NULL; /* length must not be zero */
if ((fn = (struct m_filename *) malloc(sizeof(struct m_filename)))) {
fn->type = MTYPE_MEMORY;
fn->filename = NULL; /* pretty-printable filename */
fn->x.memory.data = (unsigned char *) data;
fn->x.memory.length = length;
}
return (const char *) fn;
}
void set_filename_printable_name(const char *filename, const char *name) {
struct m_filename *fn = (struct m_filename *) filename;
if (!fn) return;
/* very basic validation of structure */
if ((fn->type < MTYPE_DISKFILE) || (fn->type > MTYPE_MEMORY)) return;
fn->filename = name;
}
void free_filename(const char *filename) {
free((void *) filename);
}
/* ------------------------------------------------------------------------ */
/* example of usage with mscab_decompressor */
/* a simple cabinet */
static unsigned char memory_cab[] = {
0x4D,0x53,0x43,0x46,0x00,0x00,0x00,0x00,0xFD,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x2C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x01,0x01,0x00,0x02,0x00,
0x00,0x00,0x22,0x06,0x00,0x00,0x5E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x4D,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6C,0x22,0xBA,0x59,0x20,0x00,
0x68,0x65,0x6C,0x6C,0x6F,0x2E,0x63,0x00,0x4A,0x00,0x00,0x00,0x4D,0x00,0x00,
0x00,0x00,0x00,0x6C,0x22,0xE7,0x59,0x20,0x00,0x77,0x65,0x6C,0x63,0x6F,0x6D,
0x65,0x2E,0x63,0x00,0xBD,0x5A,0xA6,0x30,0x97,0x00,0x97,0x00,0x23,0x69,0x6E,
0x63,0x6C,0x75,0x64,0x65,0x20,0x3C,0x73,0x74,0x64,0x69,0x6F,0x2E,0x68,0x3E,
0x0D,0x0A,0x0D,0x0A,0x76,0x6F,0x69,0x64,0x20,0x6D,0x61,0x69,0x6E,0x28,0x76,
0x6F,0x69,0x64,0x29,0x0D,0x0A,0x7B,0x0D,0x0A,0x20,0x20,0x20,0x20,0x70,0x72,
0x69,0x6E,0x74,0x66,0x28,0x22,0x48,0x65,0x6C,0x6C,0x6F,0x2C,0x20,0x77,0x6F,
0x72,0x6C,0x64,0x21,0x5C,0x6E,0x22,0x29,0x3B,0x0D,0x0A,0x7D,0x0D,0x0A,0x23,
0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,0x20,0x3C,0x73,0x74,0x64,0x69,0x6F,0x2E,
0x68,0x3E,0x0D,0x0A,0x0D,0x0A,0x76,0x6F,0x69,0x64,0x20,0x6D,0x61,0x69,0x6E,
0x28,0x76,0x6F,0x69,0x64,0x29,0x0D,0x0A,0x7B,0x0D,0x0A,0x20,0x20,0x20,0x20,
0x70,0x72,0x69,0x6E,0x74,0x66,0x28,0x22,0x57,0x65,0x6C,0x63,0x6F,0x6D,0x65,
0x21,0x5C,0x6E,0x22,0x29,0x3B,0x0D,0x0A,0x7D,0x0D,0x0A,0x0D,0x0A
};
int main() {
const char *mem_cab, *std_out, *std_err, *example;
struct mscab_decompressor *cabd;
struct mscabd_cabinet *cab;
struct mscabd_file *file;
int err;
mem_cab = create_filename_from_memory(&memory_cab[0], sizeof(memory_cab));
if (!mem_cab) exit(1);
std_out = create_filename_from_descriptor(1);
if (!std_out) exit(1);
std_err = create_filename_from_handle(stderr);
if (!std_err) exit(1);
example = create_filename("example.txt");
if (!example) exit(1);
set_filename_printable_name(mem_cab, "internal");
set_filename_printable_name(std_out, "stdout");
set_filename_printable_name(std_err, "stderr");
/* if self-test reveals an error */
MSPACK_SYS_SELFTEST(err);
if (err) exit(1);
/* create a cab decompressor using our custom mspack_system interface */
if ((cabd = mspack_create_cab_decompressor(&multi_system))) {
/* open a cab file direct from memory */
if ((cab = cabd->open(cabd, mem_cab))) {
/* first file in the cabinet: print it to stdout */
file = cab->files;
if (cabd->extract(cabd, file, std_out)) {
exit(1);
}
/* second file in the cabinet: print it to stderr */
file = file->next;
if (cabd->extract(cabd, file, std_err)) {
exit(1);
}
/* also write it to "example.txt" */
if (cabd->extract(cabd, file, example)) {
exit(1);
}
cabd->close(cabd, cab);
}
else {
fprintf(stderr, "can't open cabinet (%d)\n", cabd->last_error(cabd));
}
mspack_destroy_cab_decompressor(cabd);
}
else {
fprintf(stderr, "can't make decompressor\n");
}
free_filename(example);
free_filename(std_err);
free_filename(std_out);
free_filename(mem_cab);
return 0;
}