/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* @ingroup ZipSupport
* @brief Zip Support for Java VM
*/
#include <string.h>
#include <sys/stat.h>
#include "hy2sie.h"
#include "zipsup.h"
#include "zlib.h"
// zlib is statically linked for Android:
#define checkZipLibrary(dummy) 0
#ifdef checkZipLibrary
#define inflateInit2Func(a, b, c, d) inflateInit2_ (a, b, c, d)
#define inflateFunc(a, b) inflate (a, b)
#define inflateEndFunc(a) inflateEnd (a)
#else
/* Globals for the zip library */
UDATA zipDLLDescriptor = 0;
int (*inflateInit2Func) (void *, int, const char *, int);
int (*inflateFunc) (void *, int);
int (*inflateEndFunc) (void *);
#endif
#define ZIP_NEXT_U8(value, index) (value = *(index++))
#define ZIP_NEXT_U16(value, index) ((value = (index[1] << 8) | index[0]), index += 2, value)
#define ZIP_NEXT_U32(value, index) ((value = ((U_32)index[3] << 24) | ((U_32)index[2] << 16) | ((U_32)index[1] << 8) | (U_32)index[0]), index += 4, value)
#define WORK_BUFFER_SIZE 64000
#define SCAN_CHUNK_SIZE 1024
struct workBuffer
{
HyPortLibrary *portLib;
UDATA *bufferStart;
UDATA *bufferEnd;
UDATA *currentAlloc;
UDATA cntr;
};
#define CDEV_CURRENT_FUNCTION _prototypes_private
I_32 zip_populateCache
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile));
static I_32 inflateData
PROTOTYPE ((struct workBuffer * workBuf, U_8 * inputBuffer,
U_32 inputBufferSize, U_8 * outputBuffer, U_32 outputBufferSize));
I_32 checkZipLibrary PROTOTYPE ((HyPortLibrary * portLib));
I_32 scanForDataDescriptor
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile,
HyZipEntry * zipEntry));
void zdatafree PROTOTYPE ((void *opaque, void *address));
static I_32 readZipEntry
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile,
HyZipEntry * zipEntry, const char *filename,
IDATA * enumerationPointer, IDATA * entryStart,
BOOLEAN findDirectory));
I_32 scanForCentralEnd
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile,
HyZipCentralEnd * endEntry));
void *zdataalloc PROTOTYPE ((void *opaque, U_32 items, U_32 size));
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION _prototypes_public
I_32 zip_getZipEntryData
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile, HyZipEntry * entry,
U_8 * buffer, U_32 bufferSize));
I_32 zip_getZipEntryFromOffset
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile, HyZipEntry * entry,
IDATA offset));
I_32 zip_establishCache
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile));
void zip_resetZipFile
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile,
IDATA * nextEntryPointer));
I_32 zip_getNextZipEntry
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile,
HyZipEntry * zipEntry, IDATA * nextEntryPointer));
I_32 zip_getZipEntry
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile, HyZipEntry * entry,
const char *filename, BOOLEAN findDirectory));
I_32 zip_getZipEntryExtraField
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile, HyZipEntry * entry,
U_8 * buffer, U_32 bufferSize));
void zip_initZipEntry
PROTOTYPE ((HyPortLibrary * portLib, HyZipEntry * entry));
I_32 zip_openZipFile
PROTOTYPE ((HyPortLibrary * portLib, char *filename, HyZipFile * zipFile,
HyZipCachePool * cachePool));
void zip_freeZipEntry
PROTOTYPE ((HyPortLibrary * portLib, HyZipEntry * entry));
I_32 VMCALL zip_closeZipFile
PROTOTYPE ((HyPortLibrary * portLib, struct HyZipFile * zipFile));
I_32 zip_getZipEntryComment
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile, HyZipEntry * entry,
U_8 * buffer, U_32 bufferSize));
#undef CDEV_CURRENT_FUNCTION
//#include "hythread.h"
//#define ENTER() hythread_monitor_enter(hythread_global_monitor())
//#define EXIT() hythread_monitor_exit(hythread_global_monitor())
#include "hymutex.h"
MUTEX zip_globalMutex;
static int initialized = 0;
#define ENTER() \
if (!initialized) { MUTEX_INIT(zip_globalMutex); initialized = 1; } \
MUTEX_ENTER(zip_globalMutex);
#define EXIT() MUTEX_EXIT(zip_globalMutex);
HyZipCachePool *
zipsup_GetZipCachePool(JNIEnv * env)
{
static HyZipCachePool *pool = 0;
if (pool == 0) {
pool = zipCachePool_new(env);
}
return pool;
}
#ifndef checkZipLibrary
#define CDEV_CURRENT_FUNCTION checkZipLibrary
/*
Ensure that the zip library is loaded.
Return 0 on success, -1 on failure.
*/
I_32
checkZipLibrary (HyPortLibrary * portLib)
{
PORT_ACCESS_FROM_PORT (portLib);
/* if the library has already been loaded return success/failure */
if (zipDLLDescriptor > 1)
return 0;
if (zipDLLDescriptor == 1)
return -1;
/* open up the zip library by name */
if (hysl_open_shared_library (HY_ZIP_DLL_NAME, &zipDLLDescriptor, TRUE))
goto openFailed;
/* look up the functions */
if (hysl_lookup_name
(zipDLLDescriptor, "inflateInit2_", (void *) &inflateInit2Func,
"ILILI"))
goto loadFailed;
if (hysl_lookup_name
(zipDLLDescriptor, "inflate", (void *) &inflateFunc, "IPI"))
goto loadFailed;
if (hysl_lookup_name
(zipDLLDescriptor, "inflateEnd", (void *) &inflateEndFunc, "IP"))
goto loadFailed;
/* good to go */
return 0;
loadFailed:
hysl_close_shared_library (zipDLLDescriptor);
/* mark the descriptor as a failed load. only report the error once */
zipDLLDescriptor = 1;
/* Unable to open %s (Missing export) */
hynls_printf (PORTLIB, HYNLS_WARNING, HYNLS_ZIP_MISSING_EXPORT,
HY_ZIP_DLL_NAME);
return -1;
openFailed:
/* mark the descriptor as a failed load. only report the error once */
zipDLLDescriptor = 1;
/* Unable to open %s (%s) */
hynls_printf (PORTLIB, HYNLS_WARNING, HYNLS_ZIP_UNABLE_TO_OPEN_ZIP_DLL,
HY_ZIP_DLL_NAME, hyerror_last_error_message ());
return -1;
}
#undef CDEV_CURRENT_FUNCTION
#endif
#define CDEV_CURRENT_FUNCTION inflateData
/*
Returns 0 on success or one of the following:
ZIP_ERR_UNSUPPORTED_FILE_TYPE
ZIP_ERR_FILE_CORRUPT
ZIP_ERR_OUT_OF_MEMORY
ZIP_ERR_INTERNAL_ERROR
*/
static I_32
inflateData (struct workBuffer *workBuf, U_8 * inputBuffer,
U_32 inputBufferSize, U_8 * outputBuffer, U_32 outputBufferSize)
{
PORT_ACCESS_FROM_PORT (workBuf->portLib);
z_stream stream;
I_32 err;
stream.next_in = inputBuffer;
stream.avail_in = inputBufferSize;
stream.next_out = outputBuffer;
stream.avail_out = outputBufferSize;
stream.opaque = workBuf;
stream.zalloc = zdataalloc;
stream.zfree = zdatafree;
/* Initialize stream. Pass "-15" as max number of window bits, negated
to indicate that no zlib header is present in the data. */
err = inflateInit2Func (&stream, -15, ZLIB_VERSION, sizeof (z_stream));
if (err != Z_OK)
return -1;
/* Inflate the data. */
err = inflateFunc (&stream, Z_SYNC_FLUSH);
/* Clean up the stream. */
inflateEndFunc (&stream);
/* Check the return code. Did we complete the inflate? */
if ((err == Z_STREAM_END) || (err == Z_OK))
{
if (stream.total_out == outputBufferSize)
{
return 0;
}
}
switch (err)
{
case Z_OK: /* an error if file is incomplete */
case Z_STREAM_END: /* an error if file is incomplete */
case Z_ERRNO: /* a random error */
case Z_STREAM_ERROR: /* stream inconsistent */
case Z_DATA_ERROR: /* corrupted zip */
return ZIP_ERR_FILE_CORRUPT;
case Z_VERSION_ERROR: /* wrong zlib version */
case Z_NEED_DICT: /* needs a preset dictionary that we can't provide */
return ZIP_ERR_UNSUPPORTED_FILE_TYPE;
case Z_MEM_ERROR: /* out of memory */
return ZIP_ERR_OUT_OF_MEMORY;
case Z_BUF_ERROR: /* no progress / out of output buffer */
default: /* jic */
return ZIP_ERR_INTERNAL_ERROR;
}
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION scanForCentralEnd
/*
Scan backward from end of file for a central end header. Read from zipFile and update the HyZipCentralEnd provided.
Returns 0 on success or one of the following:
ZIP_ERR_FILE_READ_ERROR
ZIP_ERR_FILE_CORRUPT
*/
I_32
scanForCentralEnd (HyPortLibrary * portLib, HyZipFile * zipFile,
HyZipCentralEnd * endEntry)
{
U_8 *current;
U_8 buffer[SCAN_CHUNK_SIZE];
I_32 i, size, state;
U_32 dataSize = 0;
I_64 seekResult;
I_32 fileSize;
I_32 bytesAlreadyRead = 0;
PORT_ACCESS_FROM_PORT (portLib);
/* Haven't seen anything yet. */
state = 0;
seekResult = hyfile_seek (zipFile->fd, 0, HySeekEnd);
if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
{
zipFile->pointer = -1;
return ZIP_ERR_FILE_READ_ERROR;
}
fileSize = (I_32) seekResult;
zipFile->pointer = fileSize;
while (TRUE)
{
/* Fill the buffer. */
if (bytesAlreadyRead == fileSize)
{
zipFile->pointer = -1;
return ZIP_ERR_FILE_CORRUPT;
}
size = SCAN_CHUNK_SIZE;
if (size > fileSize - bytesAlreadyRead)
size = fileSize - bytesAlreadyRead;
bytesAlreadyRead += size;
seekResult =
hyfile_seek (zipFile->fd, fileSize - bytesAlreadyRead, HySeekSet);
if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
{
zipFile->pointer = -1;
return ZIP_ERR_FILE_READ_ERROR;
}
zipFile->pointer = (I_32) seekResult;
if (hyfile_read (zipFile->fd, buffer, size) != size)
{
zipFile->pointer = -1;
return ZIP_ERR_FILE_READ_ERROR;
}
zipFile->pointer += size;
/* Scan the buffer (backwards) for CentralEnd signature = PK^E^F. */
for (i = size; i--; dataSize++)
{
switch (state)
{
case 0:
/* Nothing yet. */
if (buffer[i] == 6)
state = 1;
break;
case 1:
/* Seen ^F */
if (buffer[i] == 5)
state = 2;
else
state = 0;
break;
case 2:
/* Seen ^E^F */
if (buffer[i] == 'K')
state = 3;
else
state = 0;
break;
case 3:
/* Seen K^E^F */
if (buffer[i] == 'P' && dataSize >= 21)
{
/* Found it. Read the data from the end-of-central-dir record. */
current = buffer + i + 4;
(void) ZIP_NEXT_U16 (endEntry->diskNumber, current);
(void) ZIP_NEXT_U16 (endEntry->dirStartDisk, current);
(void) ZIP_NEXT_U16 (endEntry->thisDiskEntries, current);
(void) ZIP_NEXT_U16 (endEntry->totalEntries, current);
(void) ZIP_NEXT_U32 (endEntry->dirSize, current);
(void) ZIP_NEXT_U32 (endEntry->dirOffset, current);
(void) ZIP_NEXT_U16 (endEntry->commentLength, current);
/* Quick test to ensure that the header isn't invalid.
Current dataSize is the number of bytes of data scanned, up to the ^H in the stream. */
if (dataSize >= (U_32) (21 + endEntry->commentLength))
return 0;
/* Header looked invalid. Pretend we didn't see it and keep scanning.. */
}
state = 0;
break;
}
}
}
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION scanForDataDescriptor
/*
Scan ahead for a data descriptor. Read from zipFile and update the HyZipLocalHeader provided.
Returns 0 on success or one of the following:
ZIP_ERR_FILE_READ_ERROR
ZIP_ERR_FILE_CORRUPT
*/
I_32
scanForDataDescriptor (HyPortLibrary * portLib, HyZipFile * zipFile,
HyZipEntry * zipEntry)
{
U_8 *current;
U_8 buffer[SCAN_CHUNK_SIZE], descriptor[16];
I_32 i, size, state;
U_32 dataSize, blockPointer;
I_64 seekResult;
PORT_ACCESS_FROM_PORT (portLib);
/* Skip ahead and read the data descriptor. The compressed size should be 0. */
if (zipFile->pointer !=
(IDATA) (zipEntry->dataPointer + zipEntry->compressedSize))
{
seekResult =
hyfile_seek (zipFile->fd,
zipEntry->dataPointer + zipEntry->compressedSize,
HySeekSet);
if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
{
zipFile->pointer = -1;
return ZIP_ERR_FILE_READ_ERROR;
}
zipFile->pointer = (I_32) seekResult;
}
/* Haven't seen anything yet. */
blockPointer = dataSize = zipEntry->compressedSize;
state = 0;
/* Scan until we find PK^G^H (otherwise it's an error). */
while (1)
{
/* Fill the buffer. */
size = hyfile_read (zipFile->fd, buffer, SCAN_CHUNK_SIZE);
if (size == 0)
{
return ZIP_ERR_FILE_CORRUPT;
}
else if (size < 0)
{
zipFile->pointer = -1;
return ZIP_ERR_FILE_READ_ERROR;
}
zipFile->pointer += size;
blockPointer += size;
/* Scan the buffer. */
for (i = 0; i < size; i++, dataSize++)
{
switch (state)
{
case 0:
/* Nothing yet. */
if (buffer[i] == 'P')
{
state = 1;
}
break;
case 1:
/* Seen P */
if (buffer[i] == 'K')
{
state = 2;
}
else
state = 0;
break;
case 2:
/* Seen PK */
if (buffer[i] == 7)
{
state = 3;
}
else
{
state = 0;
}
break;
case 3:
/* Seen PK^G */
if (buffer[i] == 8)
{
/* Found it! Read the descriptor */
if (i + 12 < size)
{
current = &buffer[i + 1];
}
else
{
seekResult =
hyfile_seek (zipFile->fd,
zipEntry->dataPointer + dataSize + 1,
HySeekSet);
if ((seekResult < 0)
|| (seekResult > HYCONST64 (0x7FFFFFFF)))
{
zipFile->pointer = -1;
return ZIP_ERR_FILE_READ_ERROR;
}
zipFile->pointer = (I_32) seekResult;
if (hyfile_read (zipFile->fd, descriptor, 12) != 12)
{
zipFile->pointer = -1;
return ZIP_ERR_FILE_READ_ERROR;
}
zipFile->pointer += 12;
current = descriptor;
}
/* Read the data from the descriptor. */
(void) ZIP_NEXT_U32 (zipEntry->crc32, current);
(void) ZIP_NEXT_U32 (zipEntry->compressedSize, current);
(void) ZIP_NEXT_U32 (zipEntry->uncompressedSize, current);
/* Quick test to ensure that the header isn't invalid.
Current dataSize is the number of bytes of data scanned, up to the ^H in the stream. */
if (dataSize - 3 == zipEntry->compressedSize)
{
return 0;
}
/* Header looked invalid. Reset the pointer and continue scanning. */
seekResult =
hyfile_seek (zipFile->fd,
zipEntry->dataPointer + blockPointer,
HySeekSet);
if ((seekResult < 0)
|| (seekResult > HYCONST64 (0x7FFFFFFF)))
{
zipFile->pointer = -1;
return ZIP_ERR_FILE_READ_ERROR;
}
zipFile->pointer = (I_32) seekResult;
}
else
state = 0;
break;
}
}
}
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION zip_populateCache
/*
Fill in the cache of a given zip file. This should only be called once during zip_openZipFile!
Returns 0 on success or one of the following:
ZIP_ERR_FILE_READ_ERROR
ZIP_ERR_FILE_OPEN_ERROR
ZIP_ERR_UNKNOWN_FILE_TYPE
ZIP_ERR_UNSUPPORTED_FILE_TYPE
ZIP_ERR_OUT_OF_MEMORY
ZIP_ERR_INTERNAL_ERROR
*/
I_32
zip_populateCache (HyPortLibrary * portLib, HyZipFile * zipFile)
{
PORT_ACCESS_FROM_PORT (portLib);
I_32 result = 0;
IDATA bufferSize = 65536;
IDATA unreadSize = 0;
IDATA bufferedSize = 0;
IDATA bytesToRead = 0;
IDATA filenameCopied;
HyZipEntry entry;
HyZipCentralEnd endEntry;
U_8 *buffer = NULL;
U_8 *filename = NULL;
IDATA filenameSize = 256; /* Should be sufficient for most filenames */
U_8 *current;
U_32 sig;
U_32 localHeaderOffset;
IDATA startCentralDir;
I_64 seekResult;
if (!zipFile->cache)
return ZIP_ERR_INTERNAL_ERROR;
/* Find and read the end-of-central-dir record. */
result = scanForCentralEnd (portLib, zipFile, &endEntry);
if (result != 0)
return result;
unreadSize = endEntry.dirSize + 4 /* slop */ ;
zipFile->cache->startCentralDir = startCentralDir =
(IDATA) ((UDATA) endEntry.dirOffset);
if (zipFile->pointer != startCentralDir)
{
seekResult = hyfile_seek (zipFile->fd, startCentralDir, HySeekSet);
if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
{
zipFile->pointer = -1;
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
zipFile->pointer = (I_32) seekResult;
if (zipFile->pointer != startCentralDir)
{
result = ZIP_ERR_FILE_READ_ERROR;
zipFile->pointer = -1;
goto finished;
}
}
/* No point in allocating more than we'll actually need.. */
if (bufferSize > unreadSize)
bufferSize = unreadSize;
filename = hymem_allocate_memory (filenameSize);
if (!filename)
{
result = ZIP_ERR_OUT_OF_MEMORY;
goto finished;
}
/* Allocate some space to hold central directory goo as we eat through it */
buffer = hymem_allocate_memory (bufferSize);
if (!buffer && (bufferSize > 4096))
{
/* Not enough memory, fall back to a smaller buffer! */
bufferSize = 4096;
buffer = hymem_allocate_memory (bufferSize);
}
if (!buffer)
{
result = ZIP_ERR_OUT_OF_MEMORY;
goto finished;
}
while (unreadSize)
{
/* Read as much as needed into buffer. */
bytesToRead = bufferSize - bufferedSize;
if (bytesToRead > unreadSize)
bytesToRead = unreadSize;
result = hyfile_read (zipFile->fd, buffer + bufferedSize, bytesToRead);
if (result < 0)
{
result = ZIP_ERR_FILE_READ_ERROR;
zipFile->pointer = -1;
goto finished;
}
zipFile->pointer += result;
unreadSize -= result;
bufferedSize += result;
current = buffer;
/* consume entries until we run out. */
while (current + 46 < buffer + bufferedSize)
{
IDATA entryPointer;
entryPointer =
zipFile->pointer + (current - (buffer + bufferedSize));
sig = 0;
(void) ZIP_NEXT_U32 (sig, current);
if (sig == ZIP_CentralEnd)
{
/* We're done here. */
result = 0;
goto finished;
}
if (sig != ZIP_CentralHeader)
{
/* Umm...What the Hell? */
result = ZIP_ERR_FILE_CORRUPT;
goto finished;
}
/* Read ZIP_CentralHeader entry */
(void) ZIP_NEXT_U16 (entry.versionCreated, current);
(void) ZIP_NEXT_U16 (entry.versionNeeded, current);
(void) ZIP_NEXT_U16 (entry.flags, current);
(void) ZIP_NEXT_U16 (entry.compressionMethod, current);
(void) ZIP_NEXT_U16 (entry.lastModTime, current);
(void) ZIP_NEXT_U16 (entry.lastModDate, current);
(void) ZIP_NEXT_U32 (entry.crc32, current);
(void) ZIP_NEXT_U32 (entry.compressedSize, current);
(void) ZIP_NEXT_U32 (entry.uncompressedSize, current);
(void) ZIP_NEXT_U16 (entry.filenameLength, current);
(void) ZIP_NEXT_U16 (entry.extraFieldLength, current);
(void) ZIP_NEXT_U16 (entry.fileCommentLength, current);
current += sizeof (U_16); /* skip disk number field */
(void) ZIP_NEXT_U16 (entry.internalAttributes, current);
current += sizeof (U_32); /* skip external attributes field */
(void) ZIP_NEXT_U32 (localHeaderOffset, current);
/* Increase filename buffer size if necessary. */
if (filenameSize < entry.filenameLength + 1)
{
hymem_free_memory (filename);
filenameSize = entry.filenameLength + 1;
filename = hymem_allocate_memory (filenameSize);
if (!filename)
{
result = ZIP_ERR_OUT_OF_MEMORY;
goto finished;
}
}
filenameCopied = 0;
while (filenameCopied < entry.filenameLength)
{
IDATA size;
/* Copy as much of the filename as we can see in the buffer (probably the whole thing). */
size = entry.filenameLength - filenameCopied;
if (size > bufferedSize - (current - buffer))
{
size = bufferedSize - (current - buffer);
}
memcpy (filename + filenameCopied, current, size);
filenameCopied += size;
current += size;
if (filenameCopied >= entry.filenameLength)
break; /* done */
/* Otherwise, we ran out of source string. Load another chunk.. */
bufferedSize = 0;
if (!unreadSize)
{
/* Central header is supposedly done? Bak */
result = ZIP_ERR_FILE_CORRUPT;
goto finished;
}
bytesToRead = bufferSize - bufferedSize;
if (bytesToRead > unreadSize)
bytesToRead = unreadSize;
result =
hyfile_read (zipFile->fd, buffer + bufferedSize, bytesToRead);
if (result < 0)
{
result = ZIP_ERR_FILE_READ_ERROR;
zipFile->pointer = -1;
goto finished;
}
zipFile->pointer += result;
unreadSize -= result;
bufferedSize += result;
current = buffer;
}
filename[entry.filenameLength] = '\0'; /* null-terminate */
if (((entry.compressionMethod == ZIP_CM_Deflated)
&& (entry.flags & 0x8)) || (entry.fileCommentLength != 0))
{
/* Either local header doesn't know the compressedSize, or this entry has a file
comment. In either case, cache the central header instead of the local header
so we can find the information we need later. */
result =
zipCache_addElement (zipFile->cache, (char *) filename,
entryPointer);
}
else
{
result =
zipCache_addElement (zipFile->cache, (char *) filename,
localHeaderOffset);
}
if (!result)
{
result = ZIP_ERR_OUT_OF_MEMORY;
goto finished;
}
/* Skip the data and comment. */
bytesToRead = entry.extraFieldLength + entry.fileCommentLength;
if (bufferedSize - (current - buffer) >= bytesToRead)
{
current += bytesToRead;
}
else
{
/* The rest of the buffer is uninteresting. Skip ahead to where the good stuff is */
bytesToRead -= (bufferedSize - (current - buffer));
current = buffer + bufferedSize;
unreadSize -= bytesToRead;
seekResult = hyfile_seek (zipFile->fd, bytesToRead, HySeekCur);
if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
{
zipFile->pointer = -1;
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
zipFile->pointer = (I_32) seekResult;
}
}
bufferedSize -= (current - buffer);
memmove (buffer, current, bufferedSize);
}
result = 0;
finished:
if (filename)
hymem_free_memory (filename);
if (buffer)
hymem_free_memory (buffer);
return result;
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION readZipEntry
/*
Read the next zip entry for the zipFile into the zipEntry provided. If filename is non-NULL, it is expected to match
the filename read for the entry. If (cachePointer != -1) the filename of the entry will be looked up in the cache (assuming
there is one) to help detect use of an invalid cache. If enumerationPointer is non-NULL, sequential access is assumed and
either a local zip entry or a data descriptor will be accepted, but a central zip entry will cause ZIP_ERR_NO_MORE_ENTRIES
to be returned. If enumerationPointer is NULL, random access is assumed and either a local zip entry or a central zip
entry will be accepted.
Returns 0 on success or one of the following:
ZIP_ERR_FILE_READ_ERROR
ZIP_ERR_FILE_CORRUPT
ZIP_ERR_OUT_OF_MEMORY
ZIP_ERR_NO_MORE_ENTRIES
*/
static I_32
readZipEntry (HyPortLibrary * portLib, HyZipFile * zipFile,
HyZipEntry * zipEntry, const char *filename,
IDATA * enumerationPointer, IDATA * entryStart,
BOOLEAN findDirectory)
{
PORT_ACCESS_FROM_PORT (portLib);
I_32 result;
U_8 buffer[46 + 128];
U_8 *current;
U_32 sig;
IDATA readLength;
I_64 seekResult;
U_8 *readBuffer;
IDATA currentEntryPointer, localEntryPointer;
IDATA headerSize;
IDATA filenameLength = filename ? strlen (filename) : 0;
retry:
if (entryStart)
*entryStart = zipFile->pointer;
readBuffer = NULL;
/* Guess how many bytes we'll need to read. If we guess correctly we will do fewer I/O operations */
headerSize = 30; /* local zip header size */
if (zipFile->cache && (zipFile->pointer >= zipFile->cache->startCentralDir))
{
headerSize = 46; /* central zip header size */
}
readLength = headerSize + (filename ? filenameLength : 128);
if (findDirectory)
{
/* Extra byte for possible trailing '/' */
readLength++;
}
/* Allocate some memory if necessary */
if (readLength <= sizeof (buffer))
{
current = buffer;
}
else
{
current = readBuffer = hymem_allocate_memory (readLength);
if (!readBuffer)
return ZIP_ERR_OUT_OF_MEMORY;
}
currentEntryPointer = localEntryPointer = zipFile->pointer;
result = hyfile_read (zipFile->fd, current, readLength);
if ((result < 22)
|| (filename
&& !(result == readLength
|| (findDirectory && result == (readLength - 1)))))
{
/* We clearly didn't get enough bytes */
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
zipFile->pointer += result;
readLength = result; /* If it's not enough, we'll catch that later */
(void) ZIP_NEXT_U32 (sig, current);
if (enumerationPointer)
{
if ((sig == ZIP_CentralEnd))
{
result = ZIP_ERR_NO_MORE_ENTRIES;
goto finished;
}
}
if ((enumerationPointer || (!zipFile->cache))
&& (sig == ZIP_DataDescriptor))
{
/* We failed to predict a data descriptor here. This should be an error (i.e. only happens in malformed zips?)
but, but we will silently skip over it */
seekResult =
hyfile_seek (zipFile->fd, currentEntryPointer + 16, HySeekSet);
if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
{
zipFile->pointer = -1;
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
zipFile->pointer = (I_32) seekResult;
if (zipFile->pointer == currentEntryPointer + 16)
{
if (readBuffer)
{
hymem_free_memory (readBuffer);
}
goto retry;
}
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
if ((sig != ZIP_CentralHeader) && (sig != ZIP_LocalHeader))
{
/* Unexpected. */
result = ZIP_ERR_FILE_CORRUPT;
goto finished;
}
headerSize = ((sig == ZIP_CentralHeader) ? 46 : 30);
if (readLength < headerSize)
{
/* We didn't get the whole header (and none of the filename).. */
/* NOTE: this could happen in normal use if the assumed filename length above is <16. Since it's 128, we don't
handle the impossible case where we would have to read more header. It could also happen if the caller
supplied a filename of length <16 but that only happens when we have a cache (so we'll know the header size)
*/
result = ZIP_ERR_FILE_READ_ERROR;
}
readLength -= headerSize;
if (sig == ZIP_CentralHeader)
{
current += 2; /* skip versionCreated field */
}
(void) ZIP_NEXT_U16 (zipEntry->versionNeeded, current);
(void) ZIP_NEXT_U16 (zipEntry->flags, current);
(void) ZIP_NEXT_U16 (zipEntry->compressionMethod, current);
(void) ZIP_NEXT_U16 (zipEntry->lastModTime, current);
(void) ZIP_NEXT_U16 (zipEntry->lastModDate, current);
(void) ZIP_NEXT_U32 (zipEntry->crc32, current);
(void) ZIP_NEXT_U32 (zipEntry->compressedSize, current);
(void) ZIP_NEXT_U32 (zipEntry->uncompressedSize, current);
(void) ZIP_NEXT_U16 (zipEntry->filenameLength, current);
(void) ZIP_NEXT_U16 (zipEntry->extraFieldLength, current);
zipEntry->fileCommentLength = 0;
if (sig == ZIP_CentralHeader)
{
(void) ZIP_NEXT_U16 (zipEntry->fileCommentLength, current);
current += 8; /* skip disk number start + internal attrs + external attrs */
(void) ZIP_NEXT_U32 (localEntryPointer, current);
}
if (filename)
{
if (zipFile->cache)
{
if (!
(readLength == zipEntry->filenameLength
|| (findDirectory
&& (readLength - 1) == zipEntry->filenameLength)))
{
/* We knew exactly how much we were supposed to read, and this wasn't it */
result = ZIP_ERR_FILE_CORRUPT;
goto finished;
}
}
}
/* Allocate space for filename */
if (zipEntry->filenameLength >= ZIP_INTERNAL_MAX)
{
zipEntry->filename =
hymem_allocate_memory (zipEntry->filenameLength + 1);
if (!zipEntry->filename)
{
result = ZIP_ERR_OUT_OF_MEMORY;
goto finished;
}
}
else
{
zipEntry->filename = zipEntry->internalFilename;
}
if (readLength > zipEntry->filenameLength)
{
readLength = zipEntry->filenameLength;
}
memcpy (zipEntry->filename, current, readLength);
/* Read the rest of the filename if necessary. Allocate space in HyZipEntry for it! */
if (readLength < zipEntry->filenameLength)
{
result =
hyfile_read (zipFile->fd, zipEntry->filename + readLength,
zipEntry->filenameLength - readLength);
if (result != (zipEntry->filenameLength - readLength))
{
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
zipFile->pointer += result;
}
zipEntry->filename[zipEntry->filenameLength] = '\0';
/* If we know what filename is supposed to be, compare it and make sure it matches */
/* Note: CASE-SENSITIVE COMPARE because filenames in zips are case sensitive (even on platforms with
case-insensitive file systems) */
if (filename)
{
if (!
((findDirectory && zipEntry->filenameLength == (filenameLength + 1)
&& zipEntry->filename[filenameLength] == '/'
&& !strncmp ((char *) zipEntry->filename, (const char *) filename,
filenameLength))
|| !strcmp ((const char *) zipEntry->filename,
(const char *) filename)))
{
/* We seem to have read something totally invalid.. */
result = ZIP_ERR_FILE_CORRUPT;
goto finished;
}
}
zipEntry->filenamePointer = currentEntryPointer + headerSize;
zipEntry->extraFieldPointer =
localEntryPointer + 30 + zipEntry->filenameLength;
zipEntry->dataPointer =
zipEntry->extraFieldPointer + zipEntry->extraFieldLength;
zipEntry->extraField = NULL;
zipEntry->fileCommentPointer = 0;
zipEntry->fileComment = NULL;
zipEntry->data = NULL;
if (sig == ZIP_CentralHeader)
{
U_8 buf[2];
U_8 *buf2 = buf;
U_16 lost;
/* Also, we know where the comment is */
zipEntry->fileCommentPointer = currentEntryPointer + headerSize +
zipEntry->filenameLength + zipEntry->extraFieldLength;
if (hyfile_seek (zipFile->fd, localEntryPointer + 28, HySeekSet) ==
localEntryPointer + 28)
{
if (hyfile_read (zipFile->fd, buf, 2) == 2)
{
(void) ZIP_NEXT_U16 (lost, buf2);
zipEntry->dataPointer = zipEntry->extraFieldPointer + lost;
zipFile->pointer = localEntryPointer + 30;
}
}
}
if ((sig == ZIP_LocalHeader)
&& (zipEntry->compressionMethod == ZIP_CM_Deflated)
&& (zipEntry->flags & 0x8))
{
/* What we just read doesn't tell us how big the compressed data is. We have to do a heuristic search for a
valid data descriptor at the end of the compressed text */
result = scanForDataDescriptor (portLib, zipFile, zipEntry);
if (result < 0)
goto finished;
}
/* Entry read successfully */
if (enumerationPointer)
{
/* Work out where the next entry is supposed to be */
*enumerationPointer =
zipEntry->fileCommentPointer + zipEntry->fileCommentLength;
}
if (readBuffer)
hymem_free_memory (readBuffer);
return 0;
finished:
if (readBuffer)
{
hymem_free_memory (readBuffer);
}
if ((zipEntry->filename)
&& (zipEntry->filename != zipEntry->internalFilename))
{
hymem_free_memory (zipEntry->filename);
}
zipEntry->filename = NULL;
if (result == ZIP_ERR_FILE_READ_ERROR)
{
zipFile->pointer = -1;
}
return result;
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION zip_closeZipFile
/**
* Attempt to close the zipfile.
*
* @param[in] portLib the port library
* @param[in] zipFile The zip file to be closed
*
* @return 0 on success
* @return ZIP_ERR_FILE_CLOSE_ERROR if there is an error closing the file
* @return ZIP_ERR_INTERNAL_ERROR if there is an internal error
*
*/
I_32 VMCALL
zip_closeZipFile (HyPortLibrary * portLib, struct HyZipFile * zipFile)
{
PORT_ACCESS_FROM_PORT (portLib);
#if defined(HY_NO_THR)
THREAD_ACCESS_FROM_PORT(portLib);
#endif /* HY_NO_THR */
IDATA fd;
ENTER ();
fd = zipFile->fd;
zipFile->fd = -1;
if (zipFile->cache && zipFile->cachePool)
{
zipCachePool_release (zipFile->cachePool, zipFile->cache);
zipFile->cache = NULL;
}
if ((zipFile->filename) && (zipFile->filename != zipFile->internalFilename))
{
hymem_free_memory (zipFile->filename);
}
zipFile->filename = NULL;
if (fd == -1)
{
EXIT ();
return ZIP_ERR_INTERNAL_ERROR;
}
if (hyfile_close (fd))
{
EXIT ();
return ZIP_ERR_FILE_CLOSE_ERROR;
}
EXIT ();
return 0;
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION zip_establishCache
/**
* Called to set up a cache when a zip file is opened with a cachePool but without a cache, or when the
* current cache is found to be invalid in some way.
*
* @param[in] portLib the port library
* @param[in] zipFile the zip file for which we want to establish a cache
*
* The current cache is marked as invalid such that new instances of zip files
* won't try to use it and an attempt is made to establish a new cache.
*
* @return 0 on success
* @return ZIP_ERR_FILE_READ_ERROR if there is an error reading from zipFile
* @return ZIP_ERR_FILE_OPEN_ERROR if is there is an error opening the file
* @return ZIP_ERR_UNKNOWN_FILE_TYPE if the file type is unknown
* @return ZIP_ERR_UNSUPPORTED_FILE_TYPE if the file type is unsupported
* @return ZIP_ERR_OUT_OF_MEMORY if there is not enough memory to complete this call
* @return ZIP_ERR_INTERNAL_ERROR if there was an internal error
*/
I_32
zip_establishCache (HyPortLibrary * portLib, HyZipFile * zipFile)
{
PORT_ACCESS_FROM_PORT (portLib);
I_32 result;
I_64 timeStamp, actualFileSize;
IDATA fileSize, filenameLength;
if (zipFile->cache)
{
if (zipFile->cachePool)
{
/* Whack cache timestamp to keep other people from starting to use it (we will create a new one for them
to start to use instead). Once all the current users of the cache have stopped using it, it will go away */
zipFile->cache->zipTimeStamp = -2;
zipCachePool_release (zipFile->cachePool, zipFile->cache);
}
zipFile->cache = NULL;
}
if (!zipFile->cachePool)
{
return ZIP_ERR_INTERNAL_ERROR;
}
/* Check the cachePool for a suitable cache. */
filenameLength = strlen ((const char *) zipFile->filename);
// timeStamp = hyfile_lastmod ((const char *) zipFile->filename);
// actualFileSize = hyfile_length ((const char *) zipFile->filename);
{
struct stat st;
tzset ();
stat ((mcSignednessBull)zipFile->filename, &st);
timeStamp = (U_64)st.st_mtime * 1000;
actualFileSize = (I_64) st.st_size;
}
if ((actualFileSize < 0) || (actualFileSize > HYCONST64 (0x7FFFFFFF)))
{
return ZIP_ERR_INTERNAL_ERROR;
}
fileSize = (IDATA) actualFileSize;
zipFile->cache =
zipCachePool_findCache (zipFile->cachePool,
(const char *) zipFile->filename, filenameLength,
fileSize, timeStamp);
if (!zipFile->cache)
{
/* Build a new cache. Because caller asked for a cache, fail if we can't provide one */
zipFile->cache =
zipCache_new (portLib, (char *) zipFile->filename, filenameLength);
if (!zipFile->cache)
return ZIP_ERR_OUT_OF_MEMORY;
zipFile->cache->zipFileSize = fileSize;
zipFile->cache->zipTimeStamp = timeStamp;
result = zip_populateCache (portLib, zipFile);
if (result != 0)
{
zipCache_kill (zipFile->cache);
zipFile->cache = NULL;
return result;
}
if (!zipCachePool_addCache (zipFile->cachePool, zipFile->cache))
{
zipCache_kill (zipFile->cache);
zipFile->cache = NULL;
return ZIP_ERR_OUT_OF_MEMORY;
}
}
return 0;
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION zip_initZipEntry
/**
* Initialize a zip entry.
*
* Should be called before the entry is passed to any other zip support functions
*
* @param[in] portLib the port library
* @param[in] entry the zip entry to init
*
* @return none
*/
void
zip_initZipEntry (HyPortLibrary * portLib, HyZipEntry * entry)
{
memset (entry, 0, sizeof (*entry));
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION zip_freeZipEntry
/**
* Free any memory associated with a zip entry.
*
* @param[in] portLib the port library
* @param[in] entry the zip entry we are freeing
*
* @return none
*
* @note This does not free the entry itself.
*/
void
zip_freeZipEntry (HyPortLibrary * portLib, HyZipEntry * entry)
{
PORT_ACCESS_FROM_PORT (portLib);
if ((entry->filename) && (entry->filename != entry->internalFilename))
{
hymem_free_memory (entry->filename);
}
entry->filename = NULL;
if (entry->extraField)
{
hymem_free_memory (entry->extraField);
entry->extraField = NULL;
}
if (entry->data)
{
hymem_free_memory (entry->data);
entry->data = NULL;
}
if (entry->fileComment)
{
hymem_free_memory (entry->fileComment);
entry->fileComment = NULL;
}
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION zip_getNextZipEntry
/**
* Read the next zip entry at nextEntryPointer into zipEntry.
*
* Any memory held onto by zipEntry may be lost, and therefore
* MUST be freed with @ref zip_freeZipEntry first.
*
* @param[in] portLib the port library
* @param[in] zipFile The zip file being read
* @param[out] zipEntry compressed data is placed here
* @param[in] nextEntryPointer
*
* @return 0 on success
* @return ZIP_ERR_FILE_READ_ERROR if there is an error reading zipFile
* @return ZIP_ERR_FILE_CORRUPT if zipFile is corrupt
* @return ZIP_ERR_NO_MORE_ENTRIES if there are no more entries in zipFile
* @return ZIP_ERR_OUT_OF_MEMORY if there is not enough memory to complete this call
*
* @see zip_freeZipEntry
*
*/
I_32
zip_getNextZipEntry (HyPortLibrary * portLib, HyZipFile * zipFile,
HyZipEntry * zipEntry, IDATA * nextEntryPointer)
{
PORT_ACCESS_FROM_PORT (portLib);
#if defined(HY_NO_THR)
THREAD_ACCESS_FROM_PORT(portLib);
#endif /* HY_NO_THR */
IDATA result;
BOOLEAN retryAllowed = TRUE;
IDATA pointer;
IDATA entryStart;
I_64 seekResult;
ENTER ();
retry:
pointer = *nextEntryPointer;
/* Seek to the entry's position in the file. */
if (pointer != zipFile->pointer)
{
seekResult = hyfile_seek (zipFile->fd, pointer, HySeekSet);
if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
{
zipFile->pointer = -1;
EXIT ();
return ZIP_ERR_FILE_READ_ERROR;
}
zipFile->pointer = (I_32) seekResult;
if (pointer != zipFile->pointer)
{
zipFile->pointer = -1;
EXIT ();
return ZIP_ERR_FILE_READ_ERROR;
}
}
/* Read the entry */
entryStart = *nextEntryPointer;
result =
readZipEntry (portLib, zipFile, zipEntry, NULL, &pointer, &entryStart,
FALSE);
if (result != 0)
{
if (!retryAllowed || (result == ZIP_ERR_NO_MORE_ENTRIES))
{
EXIT ();
return result;
}
zip_establishCache (portLib, zipFile);
retryAllowed = FALSE;
goto retry;
}
if (zipFile->cache)
{
/* Validity check: look up filename in the cache... */
result =
(IDATA) zipCache_findElement (zipFile->cache,
(const char *) zipEntry->filename,
FALSE);
if (result != entryStart)
{
if (result >= zipFile->cache->startCentralDir)
{
/* ! Cache contents are not valid. Invalidate it and make a new one */
if (!retryAllowed)
{
EXIT ();
return ZIP_ERR_FILE_CORRUPT; /* should never happen! */
}
result = zip_establishCache (portLib, zipFile);
if (!result)
{
/* (silently start operating without a cache if we couldn't make a new one) */
}
else
{
retryAllowed = FALSE;
goto retry;
}
}
else
{
/* We know that the central header for this entry contains info that the
local header is missing (comment length and/or uncompressed size) */
zipEntry->fileCommentPointer = -1;
}
}
}
*nextEntryPointer = pointer;
EXIT ();
return 0;
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION zip_getZipEntry
/**
* Attempt to find and read the zip entry corresponding to filename.
* If found, read the entry into the parameter entry.
*
* If an uncached entry is found, the filename field will be filled in. This
* memory will have to be freed with @ref zip_freeZipEntry.
*
* @param[in] portLib the port library
* @param[in] zipFile the file being read from
* @param[out] entry the zip entry found in zipFile is read to here
* @param[in] filename the name of the file that corresponds to entry
* @param[in] findDirectory when true, match a directory even if filename does not end in '/'.
* Note findDirectory is only supported (for the JCL) when there is a cache
*
* @return 0 on success or one of the following:
* @return ZIP_ERR_FILE_READ_ERROR if there is an error reading from zipFile
* @return ZIP_ERR_FILE_CORRUPT if zipFile is corrupt
* @return ZIP_ERR_ENTRY_NOT_FOUND if a zip entry with name filename was not found
* @return ZIP_ERR_OUT_OF_MEMORY if there is not enough memory to complete this call
*
* @see zip_freeZipEntry
*/
I_32
zip_getZipEntry (HyPortLibrary * portLib, HyZipFile * zipFile,
HyZipEntry * entry, const char *filename,
BOOLEAN findDirectory)
{
PORT_ACCESS_FROM_PORT (portLib);
#if defined(HY_NO_THR)
THREAD_ACCESS_FROM_PORT(portLib);
#endif /* HY_NO_THR */
IDATA result, position;
BOOLEAN retryAllowed = TRUE;
I_64 seekResult;
ENTER ();
retry:
if (zipFile->cache)
{
/* Look up filename in the cache. */
position =
(IDATA) zipCache_findElement (zipFile->cache, filename,
findDirectory);
if (position == -1)
{
/* Note: we assume the cache is still valid here */
EXIT ();
return ZIP_ERR_ENTRY_NOT_FOUND;
}
/* Seek to the entry's position in the file. */
if (zipFile->pointer != position)
{
seekResult = hyfile_seek (zipFile->fd, position, HySeekSet);
if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
{
zipFile->pointer = -1;
EXIT ();
return ZIP_ERR_FILE_READ_ERROR;
}
zipFile->pointer = (I_32) seekResult;
if (zipFile->pointer != position)
{
zipFile->pointer = -1;
EXIT ();
return ZIP_ERR_FILE_READ_ERROR;
}
}
/* Read the entry */
result =
readZipEntry (portLib, zipFile, entry, filename, NULL, NULL,
findDirectory);
if (result != 0)
{
if (!retryAllowed)
{
EXIT ();
return result;
}
result = zip_establishCache (portLib, zipFile); /* invalidate existing cache */
if (result)
{
EXIT ();
return result;
}
retryAllowed = FALSE;
goto retry;
}
EXIT ();
return 0;
}
else
{
/* Uh oh -- random access without a cache (SLOW!) */
position = 0;
zip_resetZipFile (PORTLIB, zipFile, &position);
while (TRUE)
{
if (zipFile->pointer != position)
{
seekResult = hyfile_seek (zipFile->fd, position, HySeekSet);
if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
{
zipFile->pointer = -1;
EXIT ();
return ZIP_ERR_FILE_READ_ERROR;
}
zipFile->pointer = (I_32) seekResult;
if (zipFile->pointer != position)
{
zipFile->pointer = -1;
EXIT ();
return ZIP_ERR_FILE_READ_ERROR;
}
}
result =
readZipEntry (portLib, zipFile, entry, NULL, &position, NULL,
FALSE);
if (result || !strcmp ((const char *) entry->filename, filename))
{
EXIT ();
return result;
}
/* No match. Reset for next entry */
zip_freeZipEntry (portLib, entry);
zip_initZipEntry (portLib, entry);
}
}
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION zip_getZipEntryData
/**
* Attempt to read and uncompress the data for the zip entry entry.
*
* If buffer is non-NULL it is used, but not explicitly held onto by the entry.
* If buffer is NULL, memory is allocated and held onto by the entry, and thus
* should later be freed with @ref zip_freeZipEntry.
*
* @param[in] portLib the port library
* @param[in] zipFile the zip file being read from.
* @param[in,out] entry the zip entry
* @param[in] buffer may or may not be NULL
* @param[in] bufferSize
* @return 0 on success
* @return ZIP_ERR_FILE_READ_ERROR if there is an error reading from zipEntry
* @return ZIP_ERR_FILE_CORRUPT if zipFile is corrupt
* @return ZIP_ERR_ENTRY_NOT_FOUND if entry is not found
* @return ZIP_ERR_OUT_OF_MEMORY if there is not enough memory to complete this call
* @return ZIP_ERR_BUFFER_TOO_SMALL if buffer is too small to hold the comment for zipFile
*
* @see zip_freeZipEntry
*
*/
I_32
zip_getZipEntryData (HyPortLibrary * portLib, HyZipFile * zipFile,
HyZipEntry * entry, U_8 * buffer, U_32 bufferSize)
{
PORT_ACCESS_FROM_PORT (portLib);
#if defined(HY_NO_THR)
THREAD_ACCESS_FROM_PORT(portLib);
#endif /* HY_NO_THR */
I_32 result;
U_8 *dataBuffer;
struct workBuffer wb;
I_64 seekResult;
ENTER ();
wb.portLib = portLib;
wb.bufferStart = wb.bufferEnd = wb.currentAlloc = 0;
if (buffer)
{
if (bufferSize < entry->uncompressedSize)
{
EXIT ();
return ZIP_ERR_BUFFER_TOO_SMALL;
}
dataBuffer = buffer;
}
else
{
/* Note that this is the first zalloc. This memory must be available to the calling method and is freed explicitly in zip_freeZipEntry. */
/* Note that other allocs freed in zip_freeZipEntry are not alloc'd using zalloc */
dataBuffer = zdataalloc (&wb, 1, entry->uncompressedSize);
if (!dataBuffer)
{
EXIT ();
return ZIP_ERR_OUT_OF_MEMORY;
}
entry->data = dataBuffer;
}
if (entry->compressionMethod == ZIP_CM_Stored)
{
/* No compression - just read the data in. */
if (zipFile->pointer != entry->dataPointer)
{
seekResult =
hyfile_seek (zipFile->fd, entry->dataPointer, HySeekSet);
if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
{
zipFile->pointer = -1;
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
zipFile->pointer = (I_32) seekResult;
if (zipFile->pointer != entry->dataPointer)
{
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
}
result = hyfile_read (zipFile->fd, dataBuffer, entry->compressedSize);
if (result != (I_32) entry->compressedSize)
{
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
zipFile->pointer += result;
EXIT ();
return 0;
}
if (entry->compressionMethod == ZIP_CM_Deflated)
{
U_8 *readBuffer;
/* Ensure that the library is loaded. */
if (checkZipLibrary (portLib))
{
result = ZIP_ERR_UNSUPPORTED_FILE_TYPE;
goto finished;
}
/* Read the file contents. */
readBuffer = zdataalloc (&wb, 1, entry->compressedSize);
if (!readBuffer)
{
result = ZIP_ERR_OUT_OF_MEMORY;
goto finished;
}
if (zipFile->pointer != entry->dataPointer)
{
seekResult =
hyfile_seek (zipFile->fd, entry->dataPointer, HySeekSet);
if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
{
zipFile->pointer = -1;
zdatafree (&wb, readBuffer);
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
zipFile->pointer = (I_32) seekResult;
if (zipFile->pointer != entry->dataPointer)
{
zdatafree (&wb, readBuffer);
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
}
if (hyfile_read (zipFile->fd, readBuffer, entry->compressedSize) !=
(I_32) entry->compressedSize)
{
zdatafree (&wb, readBuffer);
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
zipFile->pointer += (I_32) entry->compressedSize;
/* Deflate the data. */
result =
inflateData (&wb, readBuffer, entry->compressedSize, dataBuffer,
entry->uncompressedSize);
zdatafree (&wb, readBuffer);
if (result)
goto finished;
EXIT ();
return 0;
}
/* Whatever this is, we can't decompress it */
result = ZIP_ERR_UNSUPPORTED_FILE_TYPE;
finished:
if (!buffer)
{
entry->data = NULL;
zdatafree (&wb, dataBuffer);
}
if (result == ZIP_ERR_FILE_READ_ERROR)
{
zipFile->pointer = -1;
}
EXIT ();
return result;
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION zip_getZipEntryExtraField
/**
* Read the extra field of entry from the zip file filename.
*
* buffer is used if non-NULL, but is not held onto by entry.
*
* If buffer is NULL, memory is allocated and held onto by entry, and MUST be freed later with
* @ref zip_freeZipEntry.
*
* @param[in] portLib the port library
* @param[in] zipFile the zip file being read from.
* @param[in,out] entry the zip entry concerned
* @param[in] buffer may or may not be NULL
* @param[in] bufferSize
*
* @return 0 on success or one of the following:
* @return ZIP_ERR_FILE_READ_ERROR if there is an error reading from zipFile
* @return ZIP_ERR_FILE_CORRUPT if zipFile is corrupt
* @return ZIP_ERR_OUT_OF_MEMORY if there is not enough memory to complete this call
* @return ZIP_ERR_BUFFER_TOO_SMALL if the buffer was non-Null but not large enough to hold the contents of entry
*
* @see zip_freeZipEntry
*/
I_32
zip_getZipEntryExtraField (HyPortLibrary * portLib, HyZipFile * zipFile,
HyZipEntry * entry, U_8 * buffer, U_32 bufferSize)
{
PORT_ACCESS_FROM_PORT (portLib);
#if defined(HY_NO_THR)
THREAD_ACCESS_FROM_PORT(portLib);
#endif /* HY_NO_THR */
I_32 result;
U_8 *extraFieldBuffer;
I_64 seekResult;
ENTER ();
if (entry->extraFieldLength == 0)
{
EXIT ();
return 0;
}
if (buffer)
{
if (bufferSize < entry->extraFieldLength)
{
EXIT ();
return ZIP_ERR_BUFFER_TOO_SMALL;
}
extraFieldBuffer = buffer;
}
else
{
extraFieldBuffer = hymem_allocate_memory (entry->extraFieldLength);
if (!extraFieldBuffer)
{
EXIT ();
return ZIP_ERR_OUT_OF_MEMORY;
}
entry->extraField = extraFieldBuffer;
}
if (zipFile->pointer != entry->extraFieldPointer)
{
seekResult =
hyfile_seek (zipFile->fd, entry->extraFieldPointer, HySeekSet);
if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
{
zipFile->pointer = -1;
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
zipFile->pointer = (I_32) seekResult;
if (zipFile->pointer != entry->extraFieldPointer)
{
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
}
result =
hyfile_read (zipFile->fd, extraFieldBuffer, entry->extraFieldLength);
if (result != (I_32) entry->extraFieldLength)
{
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
zipFile->pointer += result;
EXIT ();
return 0;
finished:
if (!buffer)
{
entry->extraField = NULL;
hymem_free_memory (extraFieldBuffer);
}
if (result == ZIP_ERR_FILE_READ_ERROR)
zipFile->pointer = -1;
EXIT ();
return result;
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION zip_getZipEntryFilename
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION zip_getZipEntryComment
/**
* Read the file comment for entry.
*
* If buffer is non-NULL, it is used, but not held onto by entry.
*
* If buffer is NULL, memory is allocated and
* held onto by entry, and thus should later be freed with @ref zip_freeZipEntry.
*
* @param[in] portLib the port library
* @param[in] zipFile the zip file concerned
* @param[in] entry the entry who's comment we want
* @param[in] buffer may or may not be NULL
* @param[in] bufferSize
* @return 0 on success or one of the following
* @return ZIP_ERR_FILE_READ_ERROR if there is an error reading the file comment from zipEntry
* @return ZIP_ERR_FILE_CORRUPT if zipFile is corrupt
* @return ZIP_ERR_ENTRY_NOT_FOUND if entry is not found
* @return ZIP_ERR_OUT_OF_MEMORY if there is not enough memory to complete this call
* @return ZIP_ERR_BUFFER_TOO_SMALL if buffer is too small to hold the comment for zipFile
*/
I_32
zip_getZipEntryComment (HyPortLibrary * portLib, HyZipFile * zipFile,
HyZipEntry * entry, U_8 * buffer, U_32 bufferSize)
{
PORT_ACCESS_FROM_PORT (portLib);
#if defined(HY_NO_THR)
THREAD_ACCESS_FROM_PORT(portLib);
#endif /* HY_NO_THR */
I_32 result;
U_8 *fileCommentBuffer;
I_64 seekResult;
ENTER ();
if (entry->fileCommentLength == 0)
{
if (entry->fileCommentPointer == -1)
{
/* TODO: we don't know where the comment is (or even if there is one)! This only happens if you're running
without a cache, so too bad for now */
}
EXIT ();
return 0;
}
if (buffer)
{
if (bufferSize < entry->fileCommentLength)
{
EXIT ();
return ZIP_ERR_BUFFER_TOO_SMALL;
}
fileCommentBuffer = buffer;
}
else
{
fileCommentBuffer = hymem_allocate_memory (entry->fileCommentLength);
if (!fileCommentBuffer)
{
EXIT ();
return ZIP_ERR_OUT_OF_MEMORY;
}
entry->fileComment = fileCommentBuffer;
}
if (zipFile->pointer != entry->fileCommentPointer)
{
seekResult =
hyfile_seek (zipFile->fd, entry->fileCommentPointer, HySeekSet);
if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
{
zipFile->pointer = -1;
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
zipFile->pointer = (I_32) seekResult;
if (zipFile->pointer != entry->fileCommentPointer)
{
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
}
result =
hyfile_read (zipFile->fd, fileCommentBuffer, entry->fileCommentLength);
if (result != (I_32) entry->fileCommentLength)
{
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
zipFile->pointer += result;
EXIT ();
return 0;
finished:
if (!buffer)
{
entry->fileComment = NULL;
hymem_free_memory (fileCommentBuffer);
}
if (result == ZIP_ERR_FILE_READ_ERROR)
{
zipFile->pointer = -1;
}
EXIT ();
return result;
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION zip_openZipFile
/**
* Attempt to open a zip file.
*
* If the cache pool is non-NULL, the cachePool will be used to find a suitable cache, and if none can be found it will create one and add it to cachePool.
* Zip support is responsible for managing the lifetime of the cache.
*
* @note If cachePool is NULL, zipFile will be opened without a cache.
*
* @param[in] portLib the port library
* @param[in] filename the zip file to open
* @param[out] zipFile the zip file structure to populate
* @param[in] cachePool the cache pool
*
* @return 0 on success
* @return ZIP_ERR_FILE_OPEN_ERROR if is there is an error opening the file
* @return ZIP_ERR_FILE_READ_ERROR if there is an error reading the file
* @return ZIP_ERR_FILE_CORRUPT if the file is corrupt
* @return ZIP_ERR_UNKNOWN_FILE_TYPE if the file type is not known
* @return ZIP_ERR_UNSUPPORTED_FILE_TYPE if the file type is unsupported
* @return ZIP_ERR_OUT_OF_MEMORY if we are out of memory
*/
I_32
zip_openZipFile (HyPortLibrary * portLib, char *filename, HyZipFile * zipFile,
HyZipCachePool * cachePool)
{
PORT_ACCESS_FROM_PORT (portLib);
#if defined(HY_NO_THR)
THREAD_ACCESS_FROM_PORT(portLib);
#endif /* HY_NO_THR */
IDATA fd = -1;
I_32 result = 0;
I_64 seekResult;
U_8 buffer[4];
I_32 len;
ENTER ();
len = strlen (filename);
zipFile->fd = -1;
zipFile->type = ZIP_Unknown;
zipFile->cache = NULL;
zipFile->cachePool = NULL;
zipFile->pointer = -1;
/* Allocate space for filename */
if (len >= ZIP_INTERNAL_MAX)
{
zipFile->filename = hymem_allocate_memory (len + 1);
if (!zipFile->filename)
{
EXIT ();
return ZIP_ERR_OUT_OF_MEMORY;
}
}
else
{
zipFile->filename = zipFile->internalFilename;
}
strcpy ((char *) zipFile->filename, filename);
fd = hyfile_open (filename, HyOpenRead, 0);
if (fd == -1)
{
result = ZIP_ERR_FILE_OPEN_ERROR;
goto finished;
}
if (hyfile_read (fd, buffer, 4) != 4)
{
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
if ((buffer[0] == 'P') && (buffer[1] == 'K'))
{
/* If not the central header or local file header, its corrupt */
if (!
((buffer[2] == 1 && buffer[3] == 2)
|| (buffer[2] == 3 && buffer[3] == 4)))
{
result = ZIP_ERR_FILE_CORRUPT;
goto finished;
}
/* PKZIP file. Back up the pointer to the beginning. */
seekResult = hyfile_seek (fd, 0, HySeekSet);
if (seekResult != 0)
{
result = ZIP_ERR_FILE_READ_ERROR;
goto finished;
}
zipFile->fd = fd;
zipFile->type = ZIP_PKZIP;
zipFile->pointer = 0;
}
if ((buffer[0] == 0x1F) && (buffer[1] == 0x8B))
{
/* GZIP - currently unsupported.
zipFile->fd = fd;
zipFile->type = ZIP_GZIP;
zipFile->pointer = 2;
*/
result = ZIP_ERR_UNSUPPORTED_FILE_TYPE;
goto finished;
}
if (zipFile->type == ZIP_Unknown)
{
result = ZIP_ERR_UNKNOWN_FILE_TYPE;
goto finished;
}
result = 0;
if (cachePool)
{
zipFile->cachePool = cachePool;
result = zip_establishCache (portLib, zipFile);
}
finished:
if (result == 0)
{
zipFile->fd = fd;
EXIT ();
return 0;
}
if (fd != -1)
{
hyfile_close (fd);
}
if ((zipFile->filename) && (zipFile->filename != zipFile->internalFilename))
{
hymem_free_memory (zipFile->filename);
}
zipFile->filename = NULL;
EXIT ();
return result;
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION zip_resetZipFile
/**
* Reset nextEntryPointer to the first entry in the file.
*
* @param[in] portLib the port library
* @param[in] zipFile the zip being read from
* @param[out] nextEntryPointer will be reset to the first entry in the file
*
* @return none
*
*
*/
void
zip_resetZipFile (HyPortLibrary * portLib, HyZipFile * zipFile,
IDATA * nextEntryPointer)
{
*nextEntryPointer = 0;
if (zipFile)
{
if (zipFile->cache)
*nextEntryPointer = zipFile->cache->startCentralDir;
else
{
I_32 result;
HyZipCentralEnd endEntry;
result = scanForCentralEnd (portLib, zipFile, &endEntry);
if (result != 0)
return;
*nextEntryPointer = (IDATA) ((UDATA) endEntry.dirOffset);
}
}
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION zip_getZipEntryFromOffset
/**
* Attempt to read a zip entry at offset from the zip file provided.
* If found, read into entry.
*
* @note If an uncached entry is found, the filename field will be filled in. This
* memory MUST be freed with @ref zip_freeZipEntry.
*
* @param[in] portLib the port library
* @param[in] zipFile the zip file being read
* @param[in] offset the offset into the zipFile of the desired zip entry
* @param[out] entry the compressed data
*
* @return 0 on success
* @return ZIP_ERR_FILE_READ_ERROR if there is an error reading from @ref zipFile
* @return ZIP_ERR_FILE_CORRUPT if @ref zipFile is corrupt
* @return ZIP_ERR_ENTRY_NOT_FOUND if the entry is not found
* @return ZIP_ERR_OUT_OF_MEMORY if there is not enough memory to complete this call
*
* @see zip_freeZipEntry
*/
I_32
zip_getZipEntryFromOffset (HyPortLibrary * portLib, HyZipFile * zipFile,
HyZipEntry * entry, IDATA offset)
{
PORT_ACCESS_FROM_PORT (portLib);
#if defined(HY_NO_THR)
THREAD_ACCESS_FROM_PORT(portLib);
#endif /* HY_NO_THR */
I_32 result;
I_64 seekResult;
ENTER ();
if (zipFile->pointer != offset)
{
seekResult = hyfile_seek (zipFile->fd, offset, HySeekSet);
if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
{
zipFile->pointer = -1;
EXIT ();
return ZIP_ERR_FILE_READ_ERROR;
}
zipFile->pointer = (I_32) seekResult;
if (zipFile->pointer != offset)
{
zipFile->pointer = -1;
EXIT ();
return ZIP_ERR_FILE_READ_ERROR;
}
}
result = readZipEntry (portLib, zipFile, entry, NULL, NULL, NULL, FALSE);
EXIT ();
return result;
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION zdataalloc
/*
cached alloc management for zip_getZipEntryData.
*/
void *
zdataalloc (void *opaque, U_32 items, U_32 size)
{
UDATA *returnVal = 0;
U_32 byteSize = items * size;
U_32 allocSize = WORK_BUFFER_SIZE;
struct workBuffer *wb = (struct workBuffer *) opaque;
PORT_ACCESS_FROM_PORT (wb->portLib);
/* Round to UDATA multiple */
byteSize = (byteSize + (sizeof (UDATA) - 1)) & ~(sizeof (UDATA) - 1);
if (wb->bufferStart == 0)
{
if (byteSize > WORK_BUFFER_SIZE)
{
allocSize = byteSize;
}
wb->bufferStart = hymem_allocate_memory (allocSize);
if (wb->bufferStart)
{
wb->bufferEnd = (UDATA *) ((UDATA) wb->bufferStart + allocSize);
wb->currentAlloc = wb->bufferStart;
wb->cntr = 0;
}
}
if ((wb->bufferStart == 0)
|| (((UDATA) wb->currentAlloc + byteSize) > (UDATA) wb->bufferEnd))
{
returnVal = hymem_allocate_memory (byteSize);
}
else
{
++(wb->cntr);
returnVal = wb->currentAlloc;
wb->currentAlloc = (UDATA *) ((UDATA) wb->currentAlloc + byteSize);
}
return returnVal;
}
#undef CDEV_CURRENT_FUNCTION
#define CDEV_CURRENT_FUNCTION zdatafree
/*
cached alloc management for zip_getZipEntryData.
*/
void
zdatafree (void *opaque, void *address)
{
struct workBuffer *wb = (struct workBuffer *) opaque;
PORT_ACCESS_FROM_PORT (wb->portLib);
if ((address < (void *) wb->bufferStart)
|| (address >= (void *) wb->bufferEnd))
{
hymem_free_memory (address);
}
else if (--(wb->cntr) == 0)
{
hymem_free_memory (wb->bufferStart);
wb->bufferStart = wb->bufferEnd = wb->currentAlloc = 0;
}
}
#undef CDEV_CURRENT_FUNCTION