| /* |
| * 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; |
| ZIP_NEXT_U16 (endEntry->diskNumber, current); |
| ZIP_NEXT_U16 (endEntry->dirStartDisk, current); |
| ZIP_NEXT_U16 (endEntry->thisDiskEntries, current); |
| ZIP_NEXT_U16 (endEntry->totalEntries, current); |
| ZIP_NEXT_U32 (endEntry->dirSize, current); |
| ZIP_NEXT_U32 (endEntry->dirOffset, current); |
| 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. */ |
| ZIP_NEXT_U32 (zipEntry->crc32, current); |
| ZIP_NEXT_U32 (zipEntry->compressedSize, current); |
| 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; |
| 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 */ |
| ZIP_NEXT_U16 (entry.versionCreated, current); |
| ZIP_NEXT_U16 (entry.versionNeeded, current); |
| ZIP_NEXT_U16 (entry.flags, current); |
| ZIP_NEXT_U16 (entry.compressionMethod, current); |
| ZIP_NEXT_U16 (entry.lastModTime, current); |
| ZIP_NEXT_U16 (entry.lastModDate, current); |
| ZIP_NEXT_U32 (entry.crc32, current); |
| ZIP_NEXT_U32 (entry.compressedSize, current); |
| ZIP_NEXT_U32 (entry.uncompressedSize, current); |
| ZIP_NEXT_U16 (entry.filenameLength, current); |
| ZIP_NEXT_U16 (entry.extraFieldLength, current); |
| ZIP_NEXT_U16 (entry.fileCommentLength, current); |
| current += sizeof (U_16); /* skip disk number field */ |
| ZIP_NEXT_U16 (entry.internalAttributes, current); |
| current += sizeof (U_32); /* skip external attributes field */ |
| 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 */ |
| 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 */ |
| } |
| ZIP_NEXT_U16 (zipEntry->versionNeeded, current); |
| ZIP_NEXT_U16 (zipEntry->flags, current); |
| ZIP_NEXT_U16 (zipEntry->compressionMethod, current); |
| ZIP_NEXT_U16 (zipEntry->lastModTime, current); |
| ZIP_NEXT_U16 (zipEntry->lastModDate, current); |
| ZIP_NEXT_U32 (zipEntry->crc32, current); |
| ZIP_NEXT_U32 (zipEntry->compressedSize, current); |
| ZIP_NEXT_U32 (zipEntry->uncompressedSize, current); |
| ZIP_NEXT_U16 (zipEntry->filenameLength, current); |
| ZIP_NEXT_U16 (zipEntry->extraFieldLength, current); |
| zipEntry->fileCommentLength = 0; |
| |
| if (sig == ZIP_CentralHeader) |
| { |
| ZIP_NEXT_U16 (zipEntry->fileCommentLength, current); |
| current += 8; /* skip disk number start + internal attrs + external attrs */ |
| 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) |
| { |
| 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 |