#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <zzip.h>
#include <zzip-file.h>
#include <zzipformat.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#ifdef ZZIP_HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#define __sizeof(X) ((zzip_ssize_t)(sizeof(X)))
#define ZZIP_CORRECT_ROOTSEEK 1
uint32_t __zzip_get32(unsigned char * s)
{
return ((uint32_t)s[3] << 24) | ((uint32_t)s[2] << 16)
| ((uint32_t)s[1] << 8) | (uint32_t)s[0];
}
uint16_t __zzip_get16(unsigned char * s)
{
return ((uint16_t)s[1] << 8) | (uint16_t)s[0];
}
int __zzip_find_disk_trailer( int fd, zzip_off_t filesize,
struct zzip_disk_trailer * trailer,
zzip_plugin_io_t io);
int __zzip_parse_root_directory( int fd,
struct zzip_disk_trailer * trailer,
struct zzip_dir_hdr ** hdr_return,
zzip_plugin_io_t io);
_zzip_inline char* __zzip_aligned4(char* p);
#ifdef ZZIP_HARDEN
_zzip_inline static void __fixup_rootseek(
zzip_off_t offset_of_trailer,
struct zzip_disk_trailer* trailer)
{
if ( (zzip_off_t) ZZIP_GET32(trailer->z_rootseek) >
offset_of_trailer - (zzip_off_t) ZZIP_GET32(trailer->z_rootsize) &&
offset_of_trailer > (zzip_off_t) ZZIP_GET32(trailer->z_rootsize))
{
register zzip_off_t offset;
offset = offset_of_trailer - ZZIP_GET32(trailer->z_rootsize);
trailer->z_rootseek[0] = offset & 0xff;
trailer->z_rootseek[1] = offset >> 8 & 0xff;
trailer->z_rootseek[2] = offset >> 16 & 0xff;
trailer->z_rootseek[3] = offset >> 24 & 0xff;
}
}
#define __correct_rootseek(A,B,C)
#elif defined ZZIP_CORRECT_ROOTSEEK
#define __fixup_rootseek(_offset_of_trailer, _trailer) \
*(zzip_off_t*)_trailer = _offset_of_trailer;
#define __correct_rootseek( _u_rootseek, _u_rootsize, _trailer) \
if (_u_rootseek > *(zzip_off_t*)_trailer - _u_rootsize) \
_u_rootseek = *(zzip_off_t*)_trailer - _u_rootsize;
#else
#define __fixup_rootseek(A,B)
#define __correct_rootseek(A,B,C)
#endif
#ifdef DEBUG
_zzip_inline static void __debug_dir_hdr (struct zzip_dir_hdr* hdr)
{
if (sizeof(struct zzip_dir_hdr) > sizeof(struct zzip_root_dirent))
{ WARN1("internal sizeof-mismatch may break wreakage"); }
}
#else
#define __debug_dir_hdr(X)
#endif
#if defined BUFSIZ
#if BUFSIZ == 1024 || BUFSIZ == 512 || BUFSIZ == 256
#define ZZIP_BUFSIZ BUFSIZ
#endif
#endif
#ifndef ZZIP_BUFSIZ
#define ZZIP_BUFSIZ 512
#endif
int
__zzip_find_disk_trailer(int fd, zzip_off_t filesize,
struct zzip_disk_trailer * trailer,
zzip_plugin_io_t io)
{
#define return(val) { e=val; goto cleanup; }
register int e;
#ifndef _LOWSTK
auto char buffer[2*ZZIP_BUFSIZ];
char* buf = buffer;
#else
char* buf = malloc(2*ZZIP_BUFSIZ);
#endif
zzip_off_t offset = 0;
zzip_off_t maplen = 0;
char* fd_map = 0;
if (!trailer)
{ return(EINVAL); }
if (filesize < __sizeof(struct zzip_disk_trailer))
{ return(ZZIP_DIR_TOO_SHORT); }
if (!buf)
{ return(ZZIP_OUTOFMEM); }
offset = filesize;
while(1)
{
register unsigned char* mapped;
if (offset <= 0) { return(ZZIP_DIR_EDH_MISSING); }
if (filesize-offset > 64*1024)
{ return(ZZIP_DIR_EDH_MISSING); }
{
fd_map = 0;
{
zzip_off_t pagesize = ZZIP_BUFSIZ;
if (offset == filesize && filesize > pagesize)
offset -= pagesize;
if (offset < pagesize) {
maplen = offset + pagesize; offset = 0;
} else {
offset -= pagesize; maplen = 2*pagesize;
if (offset & (pagesize-1)) {
pagesize -= offset & (pagesize-1);
offset += pagesize;
maplen -= pagesize;
}
}
if (offset + maplen > filesize) maplen = filesize - offset;
}
if (io->seeks(fd, offset, SEEK_SET) < 0)
{ return(ZZIP_DIR_SEEK); }
if (io->read(fd, buf, (zzip_size_t)maplen) < (zzip_ssize_t)maplen)
{ return(ZZIP_DIR_READ); }
mapped = buf;
}
{
register unsigned char* end = mapped + maplen;
register unsigned char* tail;
for (tail = end-1; (tail >= mapped); tail--)
{
if ((*tail == 'P') &&
end-tail >= __sizeof(*trailer)-2 &&
ZZIP_DISK_TRAILER_CHECKMAGIC(tail))
{
if (end-tail >= __sizeof(*trailer))
{
memcpy (trailer, tail, sizeof(*trailer));
}else{
memcpy (trailer, tail, sizeof(*trailer)-2);
trailer->z_comment[0] = 0;
trailer->z_comment[1] = 0;
}
__fixup_rootseek (offset + tail-mapped, trailer);
{ return(0); }
}
}
}
}
cleanup:
# ifdef _LOWSTK
free(buf);
# endif
# undef return
return e;
}
_zzip_inline char* __zzip_aligned4(char* p)
{
#define aligned4 __zzip_aligned4
p += ((long)p)&1;
p += ((long)p)&2;
return p;
}
int
__zzip_parse_root_directory(int fd,
struct zzip_disk_trailer * trailer,
struct zzip_dir_hdr ** hdr_return,
zzip_plugin_io_t io)
{
auto struct zzip_root_dirent dirent;
struct zzip_dir_hdr * hdr;
struct zzip_dir_hdr * hdr0;
uint16_t * p_reclen = 0;
short entries;
long offset;
char* fd_map = 0;
int32_t fd_gap = 0;
uint16_t u_entries = ZZIP_GET16(trailer->z_entries);
uint32_t u_rootsize = ZZIP_GET32(trailer->z_rootsize);
uint32_t u_rootseek = ZZIP_GET32(trailer->z_rootseek);
__correct_rootseek (u_rootseek, u_rootsize, trailer);
hdr0 = (struct zzip_dir_hdr*) malloc(u_rootsize);
if (!hdr0)
return ZZIP_DIRSIZE;
hdr = hdr0; __debug_dir_hdr (hdr);
for (entries=u_entries, offset=0; entries > 0; entries--)
{
register struct zzip_root_dirent * d;
uint16_t u_extras, u_comment, u_namlen;
uint16_t u_flags;
if (fd_map)
{ d = (void*)(fd_map+fd_gap+offset); }
else
{
if (io->seeks(fd, u_rootseek+offset, SEEK_SET) < 0) {
free(hdr0);
return ZZIP_DIR_SEEK;
}
if (io->read(fd, &dirent, sizeof(dirent)) < __sizeof(dirent)) {
free(hdr0);
return ZZIP_DIR_READ;
}
d = &dirent;
}
if (offset+sizeof(*d) > u_rootsize)
{ break;}
# if 0 && defined DEBUG
zzip_debug_xbuf ((unsigned char*) d, sizeof(*d) + 8);
# endif
u_extras = ZZIP_GET16(d->z_extras);
u_comment = ZZIP_GET16(d->z_comment);
u_namlen = ZZIP_GET16(d->z_namlen);
u_flags = ZZIP_GET16(d->z_flags);
hdr->d_crc32 = ZZIP_GET32(d->z_crc32);
hdr->d_csize = ZZIP_GET32(d->z_csize);
hdr->d_usize = ZZIP_GET32(d->z_usize);
hdr->d_off = ZZIP_GET32(d->z_off);
hdr->d_compr = (uint8_t)ZZIP_GET16(d->z_compr);
hdr->d_flags = u_flags;
if (offset+sizeof(*d) + u_namlen > u_rootsize)
{ break;}
if (fd_map)
{ memcpy(hdr->d_name, fd_map+fd_gap+offset+sizeof(*d), u_namlen); }
else { io->read(fd, hdr->d_name, u_namlen); }
hdr->d_name[u_namlen] = '\0';
hdr->d_namlen = u_namlen;
offset += sizeof(*d) + u_namlen + u_extras + u_comment;
if (offset > (long)u_rootsize)
{ entries--; break;}
p_reclen = &hdr->d_reclen;
{ register char* p = (char*) hdr;
register char* q = aligned4 (p + sizeof(*hdr) + u_namlen + 1);
*p_reclen = (uint16_t)(q - p);
hdr = (struct zzip_dir_hdr*) q;
}
}
if (p_reclen)
{
*p_reclen = 0;
if (hdr_return)
*hdr_return = hdr0;
}
return (entries ? ZZIP_CORRUPTED : 0);
}
#ifndef O_BINARY
#define O_BINARY 0
#endif
static zzip_strings_t* zzip_get_default_ext(void)
{
static zzip_strings_t ext [] =
{
".zip", ".ZIP",
# ifdef ZZIP_USE_ZIPLIKES
".pk3", ".PK3",
".jar", ".JAR",
# endif
0
};
return ext;
}
ZZIP_DIR*
zzip_dir_alloc_ext_io (zzip_strings_t* ext, const zzip_plugin_io_t io)
{
ZZIP_DIR* dir;
if ((dir = (ZZIP_DIR *)calloc(1, sizeof(*dir))) == NULL)
return 0;
dir->fileext = ext ? ext : zzip_get_default_ext();
dir->io = io ? io : zzip_get_default_io ();
return dir;
}
ZZIP_DIR*
zzip_dir_alloc (zzip_strings_t* fileext)
{
return zzip_dir_alloc_ext_io (fileext, 0);
}
int
zzip_dir_free(ZZIP_DIR * dir)
{
if (dir->refcount)
return (dir->refcount);
if (dir->fd >= 0) dir->io->close(dir->fd);
if (dir->hdr0) free(dir->hdr0);
if (dir->cache.fp) free(dir->cache.fp);
if (dir->cache.buf32k) free(dir->cache.buf32k);
if (dir->realname) free(dir->realname);
free(dir);
return 0;
}
int
zzip_dir_close(ZZIP_DIR * dir)
{
dir->refcount &=~ 0x10000000;
return zzip_dir_free(dir);
}
ZZIP_DIR *
zzip_dir_fdopen(int fd, zzip_error_t * errcode_p)
{
return zzip_dir_fdopen_ext_io(fd, errcode_p, 0, 0);
}
static zzip_error_t __zzip_dir_parse (ZZIP_DIR* dir);
ZZIP_DIR *
zzip_dir_fdopen_ext_io(int fd, zzip_error_t * errcode_p,
zzip_strings_t* ext, const zzip_plugin_io_t io)
{
zzip_error_t rv;
ZZIP_DIR * dir;
if ((dir = zzip_dir_alloc_ext_io (ext, io)) == NULL)
{ rv = ZZIP_OUTOFMEM; goto error; }
dir->fd = fd;
if ((rv = __zzip_dir_parse (dir)))
goto error;
dir->hdr = dir->hdr0;
dir->refcount |= 0x10000000;
if (errcode_p) *errcode_p = rv;
return dir;
error:
if (dir) zzip_dir_free(dir);
if (errcode_p) *errcode_p = rv;
return NULL;
}
static zzip_error_t
__zzip_dir_parse (ZZIP_DIR* dir)
{
zzip_error_t rv;
zzip_off_t filesize;
struct zzip_disk_trailer trailer;
if ((filesize = dir->io->filesize(dir->fd)) < 0)
{ rv = ZZIP_DIR_STAT; goto error; }
if ((rv = __zzip_find_disk_trailer(dir->fd, filesize, &trailer,
dir->io)) != 0)
{ goto error; }
if ( (rv = __zzip_parse_root_directory(dir->fd, &trailer, &dir->hdr0,
dir->io)) != 0)
{ goto error; }
error:
return rv;
}
int
__zzip_try_open(zzip_char_t* filename, int filemode,
zzip_strings_t* ext, zzip_plugin_io_t io)
{
auto char file[PATH_MAX];
int fd;
zzip_size_t len = strlen (filename);
if (len+4 >= PATH_MAX) return -1;
memcpy(file, filename, len+1);
if (!io) io = zzip_get_default_io();
if (!ext) ext = zzip_get_default_ext();
for ( ; *ext ; ++ext)
{
strcpy (file+len, *ext);
fd = io->open(file, filemode);
if (fd != -1) return fd;
}
return -1;
}
ZZIP_DIR*
zzip_dir_open(zzip_char_t* filename, zzip_error_t* e)
{
return zzip_dir_open_ext_io (filename, e, 0, 0);
}
ZZIP_DIR*
zzip_dir_open_ext_io(zzip_char_t* filename, zzip_error_t* e,
zzip_strings_t* ext, zzip_plugin_io_t io)
{
int fd;
if (!io) io = zzip_get_default_io();
if (!ext) ext = zzip_get_default_ext();
fd = io->open(filename, O_RDONLY|O_BINARY);
if (fd != -1)
{ return zzip_dir_fdopen_ext_io(fd, e, ext, io); }
else
{
fd = __zzip_try_open(filename, O_RDONLY|O_BINARY, ext, io);
if (fd != -1)
{ return zzip_dir_fdopen_ext_io(fd, e, ext, io); }
else
{
if (e) { *e = ZZIP_DIR_OPEN; }
return 0;
}
}
}
int
zzip_dir_read(ZZIP_DIR * dir, ZZIP_DIRENT * d )
{
if (! dir || ! dir->hdr || ! d) return 0;
d->d_compr = dir->hdr->d_compr;
d->d_csize = dir->hdr->d_csize;
d->st_size = dir->hdr->d_usize;
d->d_name = dir->hdr->d_name;
d->d_flags = dir->hdr->d_flags;
if (! dir->hdr->d_reclen)
{ dir->hdr = 0; }
else
{ dir->hdr = (struct zzip_dir_hdr *)((char *)dir->hdr + dir->hdr->d_reclen); }
return 1;
}