summaryrefslogtreecommitdiff
path: root/src/libs/uio/zip/zip.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/uio/zip/zip.c')
-rw-r--r--src/libs/uio/zip/zip.c1680
1 files changed, 1680 insertions, 0 deletions
diff --git a/src/libs/uio/zip/zip.c b/src/libs/uio/zip/zip.c
new file mode 100644
index 0000000..6da462b
--- /dev/null
+++ b/src/libs/uio/zip/zip.c
@@ -0,0 +1,1680 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * This file makes use of zlib (http://www.gzip.org/zlib/)
+ *
+ * References:
+ * The .zip format description from PKWare:
+ * http://www.pkware.com/products/enterprise/white_papers/appnote.html
+ * The .zip format description from InfoZip:
+ * ftp://ftp.info-zip.org/pub/infozip/doc/appnote-011203-iz.zip
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "zip.h"
+#include "../physical.h"
+#include "../uioport.h"
+#include "../paths.h"
+#include "../uioutils.h"
+#ifdef uio_MEM_DEBUG
+# include "../memdebug.h"
+#endif
+
+
+#define DIR_STRUCTURE_READ_BUFSIZE 0x10000
+
+static int zip_badFile(zip_GPFileData *gPFileData, char *fileName);
+static int zip_fillDirStructure(uio_GPDir *top, uio_Handle *handle);
+#if zip_USE_HEADERS == zip_USE_LOCAL_HEADERS
+static int zip_fillDirStructureLocal(uio_GPDir *top, uio_Handle *handle);
+static int zip_fillDirStructureLocalProcessEntry(uio_GPDir *topGPDir,
+ uio_FileBlock *fileBlock, off_t *pos);
+#endif
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+static off_t zip_findEndOfCentralDirectoryRecord(uio_Handle *handle,
+ uio_FileBlock *fileBlock);
+static int zip_fillDirStructureCentral(uio_GPDir *top, uio_Handle *handle);
+static int zip_fillDirStructureCentralProcessEntry(uio_GPDir *topGPDir,
+ uio_FileBlock *fileBlock, off_t *pos);
+static int zip_updatePFileDataFromLocalFileHeader(zip_GPFileData *gPFileData,
+ uio_FileBlock *fileBlock, int pos);
+int zip_updateFileDataFromLocalHeader(uio_Handle *handle,
+ zip_GPFileData *gPFileData);
+#endif
+static int zip_fillDirStructureProcessExtraFields(
+ uio_FileBlock *fileBlock, off_t extraFieldLength,
+ zip_GPFileData *gPFileData, const char *path, off_t pos,
+ uio_bool central);
+static inline int zip_foundFile(uio_GPDir *gPDir, const char *path,
+ zip_GPFileData *gPFileData);
+static inline int zip_foundDir(uio_GPDir *gPDir, const char *dirName,
+ zip_GPDirData *gPDirData);
+static int zip_initZipStream(z_stream *zipStream);
+static int zip_unInitZipStream(z_stream *zipStream);
+static int zip_reInitZipStream(z_stream *zipStream);
+
+static voidpf zip_alloc(voidpf opaque, uInt items, uInt size);
+static void zip_free(voidpf opaque, voidpf address);
+
+static inline zip_GPFileData * zip_GPFileData_new(void);
+static inline void zip_GPFileData_delete(zip_GPFileData *gPFileData);
+static inline zip_GPFileData *zip_GPFileData_alloc(void);
+static inline void zip_GPFileData_free(zip_GPFileData *gPFileData);
+static inline void zip_GPDirData_delete(zip_GPDirData *gPDirData);
+static inline void zip_GPDirData_free(zip_GPDirData *gPDirData);
+
+static ssize_t zip_readStored(uio_Handle *handle, void *buf, size_t count);
+static ssize_t zip_readDeflated(uio_Handle *handle, void *buf, size_t count);
+static off_t zip_seekStored(uio_Handle *handle, off_t offset);
+static off_t zip_seekDeflated(uio_Handle *handle, off_t offset);
+
+uio_FileSystemHandler zip_fileSystemHandler = {
+ /* .init = */ NULL,
+ /* .unInit = */ NULL,
+ /* .cleanup = */ NULL,
+
+ /* .mount = */ zip_mount,
+ /* .umount = */ uio_GPRoot_umount,
+
+ /* .access = */ zip_access,
+ /* .close = */ zip_close,
+ /* .fstat = */ zip_fstat,
+ /* .stat = */ zip_stat,
+ /* .mkdir = */ NULL,
+ /* .open = */ zip_open,
+ /* .read = */ zip_read,
+ /* .rename = */ NULL,
+ /* .rmdir = */ NULL,
+ /* .seek = */ zip_seek,
+ /* .write = */ NULL,
+ /* .unlink = */ NULL,
+
+ /* .openEntries = */ uio_GPDir_openEntries,
+ /* .readEntries = */ uio_GPDir_readEntries,
+ /* .closeEntries = */ uio_GPDir_closeEntries,
+
+ /* .getPDirEntryHandle = */ uio_GPDir_getPDirEntryHandle,
+ /* .deletePRootExtra = */ uio_GPRoot_delete,
+ /* .deletePDirHandleExtra = */ uio_GPDirHandle_delete,
+ /* .deletePFileHandleExtra = */ uio_GPFileHandle_delete,
+};
+
+uio_GPRoot_Operations zip_GPRootOperations = {
+ /* .fillGPDir = */ NULL,
+ /* .deleteGPRootExtra = */ NULL,
+ /* .deleteGPDirExtra = */ zip_GPDirData_delete,
+ /* .deleteGPFileExtra = */ zip_GPFileData_delete,
+};
+
+
+#define NUM_COMPRESSION_METHODS 11
+/*
+ * [0] = stored uncompressed
+ * [1] = Shrunk
+ * [2] = Reduced with compression factor 1
+ * [3] = Reduced with compression factor 2
+ * [4] = Reduced with compression factor 3
+ * [5] = Reduced with compression factor 4
+ * [6] = Imploded
+ * [7] = Reserved for Tokenizing
+ * [8] = Deflated
+ * [9] = Deflate64
+ * [10] = "PKWARE Data Compression Library Imploding"
+ */
+static const uio_bool
+ zip_compressionMethodSupported[NUM_COMPRESSION_METHODS] = {
+ true, false, false, false, false, false, false, false,
+ true, false, false };
+
+typedef ssize_t (*zip_readFunctionType)(uio_Handle *handle,
+ void *buf, size_t count);
+zip_readFunctionType zip_readMethods[NUM_COMPRESSION_METHODS] = {
+ zip_readStored, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ zip_readDeflated, NULL, NULL
+};
+typedef off_t (*zip_seekFunctionType)(uio_Handle *handle, off_t offset);
+zip_seekFunctionType zip_seekMethods[NUM_COMPRESSION_METHODS] = {
+ zip_seekStored, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ zip_seekDeflated, NULL, NULL
+};
+
+
+typedef enum {
+ zip_OSType_FAT,
+ zip_OSType_Amiga,
+ zip_OSType_OpenVMS,
+ zip_OSType_UNIX,
+ zip_OSType_VMCMS,
+ zip_OSType_AtariST,
+ zip_OSType_HPFS,
+ zip_OSType_HFS,
+ zip_OSType_ZSystem,
+ zip_OSType_CPM,
+ zip_OSType_TOPS20,
+ zip_OSType_NTFS,
+ zip_OSType_QDOS,
+ zip_OSType_Acorn,
+ zip_OSType_VFAT,
+ zip_OSType_MVS,
+ zip_OSType_BeOS,
+ zip_OSType_Tandem,
+
+ zip_numOSTypes
+} zip_OSType;
+
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+static mode_t zip_makeFileMode(zip_OSType creatorOS, uio_uint32 modeBytes);
+#endif
+
+#define zip_INPUT_BUFFER_SIZE 0x10000
+ // TODO: make this configurable a la sysctl?
+#define zip_SEEK_BUFFER_SIZE zip_INPUT_BUFFER_SIZE
+
+
+void
+zip_close(uio_Handle *handle) {
+ zip_Handle *zip_handle;
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "zip_close - handle=%p\n", (void *) handle);
+#endif
+ zip_handle = handle->native;
+ uio_GPFile_unref(zip_handle->file);
+ zip_unInitZipStream(&zip_handle->zipStream);
+ uio_closeFileBlock(zip_handle->fileBlock);
+ uio_free(zip_handle);
+}
+
+static void
+zip_fillStat(struct stat *statBuf, const zip_GPFileData *gPFileData) {
+ memset(statBuf, '\0', sizeof (struct stat));
+ statBuf->st_size = gPFileData->uncompressedSize;
+ statBuf->st_uid = gPFileData->uid;
+ statBuf->st_gid = gPFileData->gid;
+ statBuf->st_mode = gPFileData->mode;
+ statBuf->st_atime = gPFileData->atime;
+ statBuf->st_mtime = gPFileData->mtime;
+ statBuf->st_ctime = gPFileData->ctime;
+}
+
+int
+zip_access(uio_PDirHandle *pDirHandle, const char *name, int mode) {
+ errno = ENOSYS; // Not implemented.
+ (void) pDirHandle;
+ (void) name;
+ (void) mode;
+ return -1;
+
+#if 0
+ uio_GPDirEntry *entry;
+
+ if (name[0] == '.' && name[1] == '\0') {
+ entry = (uio_GPDirEntry *) pDirHandle->extra;
+ } else {
+ entry = uio_GPDir_getGPDirEntry(pDirHandle->extra, name);
+ if (entry == NULL) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ if (mode & R_OK)
+ {
+ // Read permission is always granted. Nothing to check here.
+ }
+
+ if (mode & W_OK) {
+ errno = EACCES;
+ return -1;
+ }
+
+ if (mode & X_OK) {
+ if (uio_GPDirEntry_isDir(entry)) {
+ // Search permission on directories is always granted.
+ } else {
+ // WORK
+#error
+ }
+ }
+
+ if (mode & F_OK) {
+ // WORK
+#error
+ }
+
+ return 0;
+#endif
+}
+
+int
+zip_fstat(uio_Handle *handle, struct stat *statBuf) {
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+ if (handle->native->file->extra->fileOffset == -1) {
+ // The local header wasn't read in yet.
+ if (zip_updateFileDataFromLocalHeader(handle->root->handle,
+ handle->native->file->extra) == -1) {
+ // errno is set
+ return -1;
+ }
+ }
+#endif /* zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS */
+ zip_fillStat(statBuf, handle->native->file->extra);
+ return 0;
+}
+
+int
+zip_stat(uio_PDirHandle *pDirHandle, const char *name, struct stat *statBuf) {
+ uio_GPDirEntry *entry;
+
+ if (name[0] == '.' && name[1] == '\0') {
+ entry = (uio_GPDirEntry *) pDirHandle->extra;
+ } else {
+ entry = uio_GPDir_getGPDirEntry(pDirHandle->extra, name);
+ if (entry == NULL) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ if (uio_GPDirEntry_isDir(entry) && entry->extra == NULL) {
+ // No information about this directory was stored.
+ // We'll have to make something up.
+ memset(statBuf, '\0', sizeof (struct stat));
+ statBuf->st_mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR |
+ S_IRGRP | S_IWGRP | S_IXOTH |
+ S_IROTH | S_IWOTH | S_IXOTH;
+ statBuf->st_uid = 0;
+ statBuf->st_gid = 0;
+ return 0;
+ }
+
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+#ifndef zip_INCOMPLETE_STAT
+ if (((zip_GPFileData *) entry->extra)->fileOffset == -1) {
+ // The local header wasn't read in yet.
+ if (zip_updateFileDataFromLocalHeader(pDirHandle->pRoot->handle,
+ (zip_GPFileData *) entry->extra) == -1) {
+ // errno is set
+ return -1;
+ }
+ }
+#endif /* !defined(zip_INCOMPLETE_STAT) */
+#endif /* zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS */
+
+ zip_fillStat(statBuf, (zip_GPFileData *) entry->extra);
+ return 0;
+}
+
+/*
+ * Function name: zip_open
+ * Description: open a file in zip file
+ * Arguments: pDirHandle - handle to the dir where to open the file
+ * name - the name of the file to open
+ * flags - flags, as to stdio open()
+ * mode - mode, as to stdio open()
+ * Returns: handle, as from stdio open()
+ * If failed, errno is set and handle is -1.
+ */
+uio_Handle *
+zip_open(uio_PDirHandle *pDirHandle, const char *name, int flags,
+ mode_t mode) {
+ zip_Handle *handle;
+ uio_GPFile *gPFile;
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "zip_open - pDirHandle=%p name=%s flags=%d mode=0%o\n",
+ (void *) pDirHandle, name, flags, mode);
+#endif
+
+ if ((flags & O_ACCMODE) != O_RDONLY) {
+ errno = EACCES;
+ return NULL;
+ }
+
+ gPFile = (uio_GPFile *) uio_GPDir_getGPDirEntry(pDirHandle->extra, name);
+ if (gPFile == NULL) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+ if (gPFile->extra->fileOffset == -1) {
+ // The local header wasn't read in yet.
+ if (zip_updateFileDataFromLocalHeader(pDirHandle->pRoot->handle,
+ gPFile->extra) == -1) {
+ // errno is set
+ return NULL;
+ }
+ }
+#endif
+
+ handle = uio_malloc(sizeof (zip_Handle));
+ uio_GPFile_ref(gPFile);
+ handle->file = gPFile;
+ handle->fileBlock = uio_openFileBlock2(pDirHandle->pRoot->handle,
+ gPFile->extra->fileOffset, gPFile->extra->compressedSize);
+ if (handle->fileBlock == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ if (zip_initZipStream(&handle->zipStream) == -1) {
+ uio_GPFile_unref(gPFile);
+ uio_closeFileBlock(handle->fileBlock);
+ return NULL;
+ }
+ handle->compressedOffset = 0;
+ handle->uncompressedOffset = 0;
+
+ (void) mode;
+ return uio_Handle_new(pDirHandle->pRoot, handle, flags);
+}
+
+ssize_t
+zip_read(uio_Handle *handle, void *buf, size_t count) {
+ ssize_t result;
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "zip_read - handle=%p buf=%p count=%d: ", (void *) handle,
+ (void *) buf, count);
+#endif
+ result = zip_readMethods[handle->native->file->extra->compressionMethod]
+ (handle, buf, count);
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "%d\n", result);
+#endif
+ return result;
+}
+
+static ssize_t
+zip_readStored(uio_Handle *handle, void *buf, size_t count) {
+ int numBytes;
+ zip_Handle *zipHandle;
+
+ zipHandle = handle->native;
+ numBytes = uio_copyFileBlock(zipHandle->fileBlock,
+ zipHandle->uncompressedOffset, buf, count);
+ if (numBytes == -1) {
+ // errno is set
+ return -1;
+ }
+ zipHandle->uncompressedOffset += numBytes;
+ zipHandle->compressedOffset += numBytes;
+ return numBytes;
+}
+
+static ssize_t
+zip_readDeflated(uio_Handle *handle, void *buf, size_t count) {
+ zip_Handle *zipHandle;
+ int inflateResult;
+
+ zipHandle = handle->native;
+
+ if (count > ((zip_GPFileData *) (zipHandle->file->extra))->
+ uncompressedSize - zipHandle->zipStream.total_out)
+ count = ((zip_GPFileData *) (zipHandle->file->extra))->
+ uncompressedSize - zipHandle->zipStream.total_out;
+
+ zipHandle->zipStream.next_out = (Bytef *) buf;
+ zipHandle->zipStream.avail_out = count;
+ while (zipHandle->zipStream.avail_out > 0) {
+ if (zipHandle->zipStream.avail_in == 0) {
+ ssize_t numBytes;
+ numBytes = uio_accessFileBlock(zipHandle->fileBlock,
+ zipHandle->compressedOffset, zip_INPUT_BUFFER_SIZE,
+ (char **) &zipHandle->zipStream.next_in);
+ if (numBytes == -1) {
+ // errno is set
+ return -1;
+ }
+#if 0
+ if (numBytes == 0) {
+ if (zipHandle->uncompressedOffset !=
+ zipHandle->file->extra->uncompressedSize) {
+ // premature eof
+ errno = EIO;
+ return -1;
+ }
+ break;
+ }
+#endif
+ zipHandle->zipStream.avail_in = numBytes;
+ zipHandle->compressedOffset += numBytes;
+ }
+ inflateResult = inflate(&zipHandle->zipStream, Z_SYNC_FLUSH);
+ zipHandle->uncompressedOffset = zipHandle->zipStream.total_out;
+ if (inflateResult == Z_STREAM_END) {
+ // Everything is decompressed
+ break;
+ }
+ if (inflateResult != Z_OK) {
+ switch (inflateResult) {
+ case Z_VERSION_ERROR:
+ fprintf(stderr, "Error: Incompatible version problem for "
+ " decompression.\n");
+ break;
+ case Z_NEED_DICT:
+ fprintf(stderr, "Error: Decompressing requires "
+ "preset dictionary.\n");
+ break;
+ case Z_DATA_ERROR:
+ fprintf(stderr, "Error: Compressed file is corrupted.\n");
+ break;
+ case Z_STREAM_ERROR:
+ // This means zipHandle->zipStream is bad, which is
+ // most likely an error in the code using zlib.
+ fprintf(stderr, "Fatal: internal error using zlib.\n");
+ abort();
+ break;
+ case Z_MEM_ERROR:
+ fprintf(stderr, "Error: Not enough memory available "
+ "while decompressing.\n");
+ break;
+ case Z_BUF_ERROR:
+ // No progress possible. Probably caused by premature
+ // end of input file.
+ fprintf(stderr, "Error: When decompressing: premature "
+ "end of input file.\n");
+ errno = EIO;
+ return -1;
+#if 0
+ // If this happens, either the input buffer is empty
+ // or the output buffer is full. This should not happen.
+ fprintf(stderr, "Fatal: internal error using zlib: "
+ " no progress is possible in decompression.\n");
+ abort();
+ break;
+#endif
+ default:
+ fprintf(stderr, "Fatal: unknown error from inflate().\n");
+ abort();
+ }
+ if (zipHandle->zipStream.msg != NULL)
+ fprintf(stderr, "ZLib reports: %s\n",
+ zipHandle->zipStream.msg);
+ errno = EIO;
+ // Using EIO to report an error in the backend.
+ return -1;
+ }
+ }
+ return count - zipHandle->zipStream.avail_out;
+}
+
+off_t
+zip_seek(uio_Handle *handle, off_t offset, int whence) {
+ zip_Handle *zipHandle;
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "zip_seek - handle=%p offset=%d whence=%s\n",
+ (void *) handle, (int) offset,
+ whence == SEEK_SET ? "SEEK_SET" :
+ whence == SEEK_CUR ? "SEEK_CUR" :
+ whence == SEEK_END ? "SEEK_END" : "INVALID");
+#endif
+ zipHandle = handle->native;
+
+ assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END);
+ switch(whence) {
+ case SEEK_SET:
+ break;
+ case SEEK_CUR:
+ offset += zipHandle->uncompressedOffset;
+ break;
+ case SEEK_END:
+ offset += zipHandle->file->extra->uncompressedSize;
+ break;
+ }
+ if (offset < 0) {
+ offset = 0;
+ } else if (offset > zipHandle->file->extra->uncompressedSize) {
+ offset = zipHandle->file->extra->uncompressedSize;
+ }
+ return zip_seekMethods[handle->native->file->extra->compressionMethod]
+ (handle, offset);
+}
+
+static off_t
+zip_seekStored(uio_Handle *handle, off_t offset) {
+ zip_Handle *zipHandle;
+
+ zipHandle = handle->native;
+ if (offset > zipHandle->file->extra->uncompressedSize)
+ offset = zipHandle->file->extra->uncompressedSize;
+
+ zipHandle->compressedOffset = offset;
+ zipHandle->uncompressedOffset = offset;
+ return offset;
+}
+
+static off_t
+zip_seekDeflated(uio_Handle *handle, off_t offset) {
+ zip_Handle *zipHandle;
+
+ zipHandle = handle->native;
+
+ if (offset < zipHandle->uncompressedOffset) {
+ // The new offset is earlier than the current offset. We need to
+ // seek from the beginning.
+ if (zip_reInitZipStream(&zipHandle->zipStream) == -1) {
+ // Need to abort. Handle would get in an inconsistent state.
+ // Should not fail anyhow.
+ fprintf(stderr, "Fatal: Could not reinitialise zip stream: "
+ "%s.\n", strerror(errno));
+ abort();
+ }
+ zipHandle->compressedOffset = 0;
+ zipHandle->uncompressedOffset = 0;
+ }
+
+ if (offset == zipHandle->uncompressedOffset)
+ return offset;
+
+ // Seek from the current position.
+ {
+ char *buffer;
+ ssize_t numRead;
+ size_t toRead;
+
+ buffer = uio_malloc(zip_SEEK_BUFFER_SIZE);
+ toRead = offset - zipHandle->uncompressedOffset;
+ while (toRead > 0) {
+ numRead = zip_read(handle, buffer,
+ toRead < zip_SEEK_BUFFER_SIZE ?
+ toRead : zip_SEEK_BUFFER_SIZE);
+ if (numRead == -1) {
+ fprintf(stderr, "Warning: Could not read zipped file: %s\n",
+ strerror(errno));
+ break;
+ // The current location is returned.
+ }
+ toRead -= numRead;
+ }
+ uio_free(buffer);
+ }
+ return zipHandle->uncompressedOffset;
+}
+
+uio_PRoot *
+zip_mount(uio_Handle *handle, int flags) {
+ uio_PRoot *result;
+ uio_PDirHandle *rootDirHandle;
+
+ if ((flags & uio_MOUNT_RDONLY) != uio_MOUNT_RDONLY) {
+ errno = EACCES;
+ return NULL;
+ }
+
+ uio_Handle_ref(handle);
+ result = uio_GPRoot_makePRoot(
+ uio_getFileSystemHandler(uio_FSTYPE_ZIP), flags,
+ &zip_GPRootOperations, NULL, uio_GPRoot_PERSISTENT,
+ handle, NULL, uio_GPDir_COMPLETE);
+
+ rootDirHandle = uio_PRoot_getRootDirHandle(result);
+ if (zip_fillDirStructure(rootDirHandle->extra, handle) == -1) {
+ int savedErrno = errno;
+#ifdef DEBUG
+ fprintf(stderr, "Error: failed to read the zip directory "
+ "structure - %s.\n", strerror(errno));
+#endif
+ uio_GPRoot_umount(result);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ return result;
+}
+
+static int
+zip_fillDirStructure(uio_GPDir *top, uio_Handle *handle) {
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+ return zip_fillDirStructureCentral(top, handle);
+#endif
+#if zip_USE_HEADERS == zip_USE_LOCAL_HEADERS
+ return zip_fillDirStructureLocal(top, handle);
+#endif
+}
+
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+static int
+zip_fillDirStructureCentral(uio_GPDir *top, uio_Handle *handle) {
+ uio_FileBlock *fileBlock;
+ off_t pos;
+ char *buf;
+ ssize_t numBytes;
+ uio_uint16 numEntries;
+ // TODO: use numEntries to initialise the hash table
+ // to a smart size
+ off_t eocdr;
+ off_t startCentralDir;
+
+ fileBlock = uio_openFileBlock(handle);
+ if (fileBlock == NULL) {
+ // errno is set
+ goto err;
+ }
+
+ // first find the 'End of Central Directory Record'
+ eocdr = zip_findEndOfCentralDirectoryRecord(handle, fileBlock);
+ if (eocdr == -1) {
+ // errno is set
+ goto err;
+ }
+
+ numBytes = uio_accessFileBlock(fileBlock, eocdr, 22, &buf);
+ if (numBytes == -1) {
+ // errno is set
+ goto err;
+ }
+ if (numBytes != 22) {
+ errno = EIO;
+ goto err;
+ }
+
+ numEntries = makeUInt16(buf[10], buf[11]);
+ if (numEntries == 0xffff) {
+ fprintf(stderr, "Error: Zip64 .zip files are not supported.\n");
+ errno = ENOSYS;
+ goto err;
+ }
+
+ startCentralDir = makeUInt32(buf[16], buf[17], buf[18], buf[19]);
+
+ // Enable read-ahead buffering, for speed.
+ uio_setFileBlockUsageHint(fileBlock, uio_FB_USAGE_FORWARD,
+ DIR_STRUCTURE_READ_BUFSIZE);
+
+ pos = startCentralDir;
+ while (numEntries--) {
+ if (zip_fillDirStructureCentralProcessEntry(top, fileBlock, &pos)
+ == -1) {
+ // errno is set
+ goto err;
+ }
+ }
+
+ uio_closeFileBlock(fileBlock);
+ return 0;
+
+err:
+ {
+ int savedErrno = errno;
+
+ if (fileBlock != NULL)
+ uio_closeFileBlock(fileBlock);
+ errno = savedErrno;
+ return -1;
+ }
+}
+
+static int
+zip_fillDirStructureCentralProcessEntry(uio_GPDir *topGPDir,
+ uio_FileBlock *fileBlock, off_t *pos) {
+ char *buf;
+ zip_GPFileData *gPFileData;
+ ssize_t numBytes;
+
+ uio_uint32 signature;
+ uio_uint16 lastModTime;
+ uio_uint16 lastModDate;
+ uio_uint32 crc;
+ uio_uint16 fileNameLength;
+ uio_uint16 extraFieldLength;
+ uio_uint16 fileCommentLength;
+ char *fileName;
+ zip_OSType creatorOS;
+
+ off_t nextEntryOffset;
+
+ numBytes = uio_accessFileBlock(fileBlock, *pos, 46, &buf);
+ if (numBytes != 46)
+ return zip_badFile(NULL, NULL);
+
+ signature = makeUInt32(buf[0], buf[1], buf[2], buf[3]);
+ if (signature != 0x02014b50) {
+ fprintf(stderr, "Error: Premature end of central directory.\n");
+ errno = EIO;
+ return -1;
+ }
+
+ gPFileData = zip_GPFileData_new();
+ creatorOS = (zip_OSType) buf[5];
+ gPFileData->compressionFlags = makeUInt16(buf[8], buf[9]);
+ gPFileData->compressionMethod = makeUInt16(buf[10], buf[11]);
+ lastModTime = makeUInt16(buf[12], buf[13]);
+ lastModDate = makeUInt16(buf[14], buf[15]);
+ gPFileData->atime = (time_t) 0;
+ gPFileData->mtime = dosToUnixTime(lastModDate, lastModTime);
+ gPFileData->ctime = (time_t) 0;
+ crc = makeUInt32(buf[16], buf[17], buf[18], buf[19]);
+ gPFileData->compressedSize =
+ makeUInt32(buf[20], buf[21], buf[22], buf[23]);
+ gPFileData->uncompressedSize =
+ makeUInt32(buf[24], buf[25], buf[26], buf[27]);
+ fileNameLength = makeUInt16(buf[28], buf[29]);
+ extraFieldLength = makeUInt16(buf[30], buf[31]);
+ fileCommentLength = makeUInt16(buf[32], buf[33]);
+ gPFileData->uid = 0;
+ gPFileData->gid = 0;
+ gPFileData->mode = zip_makeFileMode(creatorOS,
+ makeUInt32(buf[38], buf[39], buf[40], buf[41]));
+
+ gPFileData->headerOffset =
+ (off_t) makeSInt32(buf[42], buf[43], buf[44], buf[45]);
+ gPFileData->fileOffset = (off_t) -1;
+
+ *pos += 46;
+ nextEntryOffset = *pos + fileNameLength + extraFieldLength +
+ fileCommentLength;
+
+ numBytes = uio_accessFileBlock(fileBlock, *pos, fileNameLength, &buf);
+ if (numBytes != fileNameLength)
+ return zip_badFile(gPFileData, NULL);
+ fileName = uio_malloc(fileNameLength + 1);
+ memcpy(fileName, buf, fileNameLength);
+ fileName[fileNameLength] = '\0';
+ *pos += fileNameLength;
+
+ if (gPFileData->compressionMethod >= NUM_COMPRESSION_METHODS ||
+ !zip_compressionMethodSupported[gPFileData->compressionMethod]) {
+ fprintf(stderr, "Warning: File '%s' is compressed with "
+ "unsupported method %d - skipped.\n", fileName,
+ gPFileData->compressionMethod);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ if (gPFileData->compressedSize == (off_t) 0xffffffff ||
+ gPFileData->uncompressedSize == (off_t) 0xffffffff ||
+ gPFileData->headerOffset < 0) {
+ fprintf(stderr, "Warning: Skipping Zip64 file '%s'\n", fileName);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ if (isBitSet(gPFileData->compressionFlags, 0)) {
+ fprintf(stderr, "Warning: Skipping encrypted file '%s'\n", fileName);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ switch (zip_fillDirStructureProcessExtraFields(fileBlock,
+ extraFieldLength, gPFileData, fileName, *pos, true)) {
+ case 0: // file is ok
+ break;
+ case 1: // file is not acceptable - skip file
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ case -1:
+ return zip_badFile(gPFileData, fileName);
+ }
+
+ *pos += extraFieldLength;
+
+ // If ctime or atime is 0, they will be filled in when the local
+ // file header is read.
+
+ if (S_ISREG(gPFileData->mode)) {
+ if (zip_foundFile(topGPDir, fileName, gPFileData) == -1) {
+ if (errno == EISDIR) {
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+ return zip_badFile(gPFileData, fileName);
+ }
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "Debug: Found file '%s'.\n", fileName);
+#endif
+ } else if (S_ISDIR(gPFileData->mode)) {
+ if (fileName[fileNameLength - 1] == '/')
+ fileName[fileNameLength - 1] = '\0';
+ if (zip_foundDir(topGPDir, fileName, gPFileData) == -1) {
+ if (errno == EISDIR) {
+ fprintf(stderr, "Warning: file '%s' already exists as a dir - "
+ "skipped.\n", fileName);
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+ return zip_badFile(gPFileData, fileName);
+ }
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "Debug: Found dir '%s'.\n", fileName);
+#endif
+ } else {
+ fprintf(stderr, "Warning: '%s' is not a regular file, nor a "
+ "directory - skipped.\n", fileName);
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+
+ uio_free(fileName);
+ return 0;
+}
+
+static off_t
+zip_findEndOfCentralDirectoryRecord(uio_Handle *handle,
+ uio_FileBlock *fileBlock) {
+ off_t fileSize;
+ off_t endPos, startPos;
+ char *buf, *bufPtr;
+ ssize_t bufLen;
+
+ struct stat statBuf;
+ if (uio_fstat(handle, &statBuf) == -1) {
+ // errno is set
+ return -1;
+ }
+ fileSize = statBuf.st_size;
+ startPos = fileSize - 0xffff - 22; // max comment and record size
+ if (startPos < 0)
+ startPos = 0;
+ endPos = fileSize - 22; // last position to be checked
+ bufLen = uio_accessFileBlock(fileBlock, startPos, endPos - startPos + 4,
+ &buf);
+ if (bufLen == -1) {
+ int savedErrno = errno;
+ fprintf(stderr, "Error: Read error while searching for "
+ "'end-of-central-directory record'.\n");
+ errno = savedErrno;
+ return -1;
+ }
+ if (bufLen != endPos - startPos + 4) {
+ fprintf(stderr, "Error: Read error while searching for "
+ "'end-of-central-directory record'.\n");
+ errno = EIO;
+ return -1;
+ }
+ bufPtr = buf + (endPos - startPos);
+ while (1) {
+ if (bufPtr < buf) {
+ fprintf(stderr, "Error: Zip file corrupt; could not find "
+ "'end-of-central-directory record'.\n");
+ errno = EIO;
+ return -1;
+ }
+ if (bufPtr[0] == 0x50 && bufPtr[1] == 0x4b && bufPtr[2] == 0x05 &&
+ bufPtr[3] == 0x06)
+ break;
+ bufPtr--;
+ }
+ return startPos + (bufPtr - buf);
+}
+
+static mode_t
+zip_makeFileMode(zip_OSType creatorOS, uio_uint32 modeBytes) {
+ switch (creatorOS) {
+ case zip_OSType_FAT:
+ case zip_OSType_NTFS:
+ case zip_OSType_VFAT: {
+ // Only the least signigicant byte is relevant.
+ mode_t mode;
+
+ if (modeBytes == 0) {
+ // File came from standard input
+ return S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
+ S_IROTH | S_IWOTH;
+ }
+ if (modeBytes & 0x10) {
+ // Directory
+ mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP |
+ S_IROTH | S_IXOTH;
+ } else {
+ // Regular file
+ mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
+ }
+ if (modeBytes & 0x01) {
+ // readonly
+ return mode;
+ } else {
+ // Write allowed
+ return mode | S_IWUSR | S_IWGRP | S_IWOTH;
+ }
+ }
+ case zip_OSType_UNIX:
+ return (mode_t) (modeBytes >> 16);
+ default:
+ fprintf(stderr, "Warning: file created by unknown OS.\n");
+ return S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ }
+}
+
+// If the data is read from the central directory, certain data
+// will need to be updated from the local directory when it is needed.
+// This function does that.
+// returns 0 for success, -1 for error (errno is set)
+// NB: Only the fields that may offer new information are checked.
+// the fields that were already read from the central directory
+// aren't verified.
+static int
+zip_updatePFileDataFromLocalFileHeader(zip_GPFileData *gPFileData,
+ uio_FileBlock *fileBlock, int pos) {
+ ssize_t numBytes;
+ char *buf;
+ uio_uint32 signature;
+ uio_uint16 fileNameLength;
+ uio_uint16 extraFieldLength;
+
+ numBytes = uio_accessFileBlock(fileBlock, pos, 30, &buf);
+ if (numBytes != 30) {
+ errno = EIO;
+ return -1;
+ }
+ signature = makeUInt32(buf[0], buf[1], buf[2], buf[3]);
+ if (signature != 0x04034b50) {
+ errno = EIO;
+ return -1;
+ }
+ fileNameLength = makeUInt16(buf[26], buf[27]);
+ extraFieldLength = makeUInt16(buf[28], buf[29]);
+ pos += 30 + fileNameLength;
+
+ switch (zip_fillDirStructureProcessExtraFields(fileBlock,
+ extraFieldLength, gPFileData, "<unknown>", pos, false)) {
+ case 0: // file is ok
+ break;
+ case 1:
+ // File is not acceptable (but according to the central header
+ // it was)
+ fprintf(stderr, "Warning: according to the central directory "
+ "of a zip file, some file inside is acceptable, "
+ "but according to the local header it isn't.\n");
+ errno = EIO;
+ return -1;
+ case -1:
+ errno = EIO;
+ return -1;
+ }
+ pos += extraFieldLength;
+ gPFileData->fileOffset = pos;
+ return 0;
+}
+#endif /* zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS */
+
+#if zip_USE_HEADERS == zip_USE_LOCAL_HEADERS
+static int
+zip_fillDirStructureLocal(uio_GPDir *top, uio_Handle *handle) {
+ uio_FileBlock *fileBlock;
+ off_t pos;
+ char *buf;
+ ssize_t numBytes;
+
+ pos = uio_lseek(handle, 0, SEEK_SET);
+ if (pos == -1) {
+ int savedErrno = errno;
+ errno = savedErrno;
+ return -1;
+ }
+ if (pos != 0) {
+ errno = EIO;
+ // Using EIO to report an error in the backend.
+ return -1;
+ }
+
+ // We read all the files from the beginning of the zip file to the end.
+ // (the directory record at the end of the file is ignored)
+ fileBlock = uio_openFileBlock(handle);
+ if (fileBlock == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ pos = 0;
+ while (1) {
+ uio_uint32 signature;
+
+ numBytes = uio_accessFileBlock(fileBlock, pos, 4, &buf);
+ if (numBytes == -1)
+ goto err;
+ if (numBytes != 4)
+ break;
+ signature = makeUInt32(buf[0], buf[1], buf[2], buf[3]);
+ if (signature != 0x04034b50) {
+ // End of file data reached.
+ break;
+ }
+ pos += 4;
+ if (zip_fillDirStructureLocalProcessEntry(top, fileBlock, &pos) == -1)
+ goto err;
+ }
+
+ uio_closeFileBlock(fileBlock);
+ return 0;
+
+err:
+ {
+ int savedErrno = errno;
+
+ uio_closeFileBlock(fileBlock);
+ errno = savedErrno;
+ return -1;
+ }
+}
+
+static int
+zip_fillDirStructureLocalProcessEntry(uio_GPDir *topGPDir,
+ uio_FileBlock *fileBlock, off_t *pos) {
+ char *buf;
+ zip_GPFileData *gPFileData;
+ ssize_t numBytes;
+
+ uio_uint16 lastModTime;
+ uio_uint16 lastModDate;
+ uio_uint32 crc;
+ uio_uint16 fileNameLength;
+ uio_uint16 extraFieldLength;
+ char *fileName;
+
+ off_t nextEntryOffset;
+
+ numBytes = uio_accessFileBlock(fileBlock, *pos, 26, &buf);
+ if (numBytes != 26)
+ return zip_badFile(NULL, NULL);
+
+ gPFileData = zip_GPFileData_new();
+ gPFileData->compressionFlags = makeUInt16(buf[2], buf[3]);
+ gPFileData->compressionMethod = makeUInt16(buf[4], buf[5]);
+ lastModTime = makeUInt16(buf[6], buf[7]);
+ lastModDate = makeUInt16(buf[8], buf[9]);
+ gPFileData->atime = (time_t) 0;
+ gPFileData->mtime = dosToUnixTime(lastModDate, lastModTime);
+ gPFileData->ctime = (time_t) 0;
+ gPFileData->uid = 0;
+ gPFileData->gid = 0;
+ gPFileData->mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ if (!isBitSet(gPFileData->compressionFlags, 3)) {
+ // If bit 3 is not set, this info will be in the data descriptor
+ // behind the file data.
+ crc = makeUInt32(buf[10], buf[11], buf[12], buf[13]);
+ gPFileData->compressedSize =
+ makeUInt32(buf[14], buf[15], buf[16], buf[17]);
+ gPFileData->uncompressedSize =
+ makeUInt32(buf[18], buf[19], buf[20], buf[21]);
+ }
+ fileNameLength = makeUInt16(buf[22], buf[23]);
+ extraFieldLength = makeUInt16(buf[24], buf[25]);
+ *pos += 26;
+ nextEntryOffset = *pos + fileNameLength + extraFieldLength +
+ gPFileData->compressedSize;
+ if (isBitSet(gPFileData->compressionFlags, 3)) {
+ // There's a data descriptor present behind the file data.
+ nextEntryOffset += 16;
+ }
+
+ if (gPFileData->compressionMethod >= NUM_COMPRESSION_METHODS ||
+ !zip_compressionMethodSupported[gPFileData->compressionMethod]) {
+ fprintf(stderr, "Warning: File '%s' is compressed with "
+ "unsupported method %d - skipped.\n", fileName,
+ gPFileData->compressionMethod);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ if (gPFileData->compressedSize == (off_t) 0xffffffff ||
+ gPFileData->uncompressedSize == (off_t) 0xffffffff) {
+ fprintf(stderr, "Warning: Skipping Zip64 file '%s'\n", fileName);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ if (isBitSet(gPFileData->compressionFlags, 0)) {
+ fprintf(stderr, "Warning: Skipping encrypted file '%s'\n", fileName);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ numBytes = uio_accessFileBlock(fileBlock, *pos, fileNameLength, &buf);
+ if (numBytes != fileNameLength)
+ return zip_badFile(gPFileData, NULL);
+ *pos += fileNameLength;
+ if (buf[fileNameLength - 1] == '/') {
+ gPFileData->mode |= S_IFDIR;
+ fileNameLength--;
+ } else
+ gPFileData->mode |= S_IFREG;
+ fileName = uio_malloc(fileNameLength + 1);
+ memcpy(fileName, buf, fileNameLength);
+ fileName[fileNameLength] = '\0';
+
+ switch (zip_fillDirStructureProcessExtraFields(fileBlock,
+ extraFieldLength, gPFileData, fileName, *pos, false)) {
+ case 0: // file is ok
+ break;
+ case 1: // file is not acceptable - skip file
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ case -1:
+ return zip_badFile(gPFileData, fileName);
+ }
+ *pos += extraFieldLength;
+
+ gPFileData->fileOffset = *pos;
+
+ *pos += gPFileData->compressedSize;
+ if (isBitSet(gPFileData->compressionFlags, 3)) {
+ // Now comes a data descriptor.
+ // The PKWare version (which was never used) misses the signature.
+ // The InfoZip version is used below.
+ uio_uint32 signature;
+
+ numBytes = uio_accessFileBlock(fileBlock, *pos, 16, &buf);
+ if (numBytes != 16)
+ return zip_badFile(gPFileData, fileName);
+
+ signature = makeUInt32(buf[0], buf[1], buf[2], buf[3]);
+ if (signature != 0x08074b50)
+ return zip_badFile(gPFileData, fileName);
+ crc = makeUInt32(buf[4], buf[5], buf[6], buf[7]);
+ gPFileData->compressedSize =
+ makeUInt32(buf[8], buf[9], buf[10], buf[11]);
+ gPFileData->uncompressedSize =
+ makeUInt32(buf[12], buf[13], buf[14], buf[15]);
+ }
+
+ if (gPFileData->ctime == (time_t) 0)
+ gPFileData->ctime = gPFileData->mtime;
+
+ if (gPFileData->atime == (time_t) 0)
+ gPFileData->atime = gPFileData->mtime;
+
+ if (S_ISREG(gPFileData->mode)) {
+ if (zip_foundFile(topGPDir, fileName, gPFileData) == -1) {
+ if (errno == EISDIR) {
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+ return zip_badFile(gPFileData, fileName);
+ }
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "Debug: Found file '%s'.\n", fileName);
+#endif
+ } else if (S_ISDIR(gPFileData->mode)) {
+ if (fileName[fileNameLength - 1] == '/')
+ fileName[fileNameLength - 1] = '\0';
+ if (zip_foundDir(topGPDir, fileName, gPFileData) == -1) {
+ if (errno == EISDIR) {
+ fprintf(stderr, "Warning: file '%s' already exists as a dir - "
+ "skipped.\n", fileName);
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+ return zip_badFile(gPFileData, fileName);
+ }
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "Debug: Found dir '%s'.\n", fileName);
+#endif
+ } else {
+ fprintf(stderr, "Warning: '%s' is not a regular file, nor a "
+ "directory - skipped.\n", fileName);
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+
+ uio_free(fileName);
+ return 0;
+}
+#endif /* zip_USE_HEADERS == zip_USE_LOCAL_HEADERS */
+
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+int
+zip_updateFileDataFromLocalHeader(uio_Handle *handle,
+ zip_GPFileData *gPFileData) {
+ uio_FileBlock *fileBlock;
+
+ fileBlock = uio_openFileBlock(handle);
+ if (fileBlock == NULL) {
+ // errno is set
+ return -1;
+ }
+ if (zip_updatePFileDataFromLocalFileHeader(gPFileData,
+ fileBlock, gPFileData->headerOffset) == -1) {
+ int savedErrno = errno;
+ uio_closeFileBlock(fileBlock);
+ errno = savedErrno;
+ return -1;
+ }
+ if (gPFileData->ctime == (time_t) 0)
+ gPFileData->ctime = gPFileData->mtime;
+ if (gPFileData->atime == (time_t) 0)
+ gPFileData->atime = gPFileData->mtime;
+ uio_closeFileBlock(fileBlock);
+ return 0;
+}
+#endif
+
+// If the zip file is bad, -1 is returned (no errno set!)
+// If the file in the zip file should be skipped, 1 is returned.
+// If the file in the zip file is ok, 0 is returned.
+static int
+zip_fillDirStructureProcessExtraFields(uio_FileBlock *fileBlock,
+ off_t extraFieldLength, zip_GPFileData *gPFileData,
+ const char *fileName, off_t pos, uio_bool central) {
+ off_t posEnd;
+ uio_uint16 headerID;
+ ssize_t dataSize;
+ ssize_t numBytes;
+ char *buf;
+
+ posEnd = pos + extraFieldLength;
+ while (pos < posEnd) {
+ numBytes = uio_accessFileBlock(fileBlock, pos, 4, &buf);
+ if (numBytes != 4)
+ return -1;
+ headerID = makeUInt16(buf[0], buf[1]);
+ dataSize = (ssize_t) makeUInt16(buf[2], buf[3]);
+ pos += 4;
+ numBytes = uio_accessFileBlock(fileBlock, pos, dataSize, &buf);
+ if (numBytes != dataSize)
+ return -1;
+ switch(headerID) {
+ case 0x000d: // 'Unix0'
+ // fallthrough
+ case 0x5855: // 'Unix1'
+ gPFileData->atime = (time_t) makeUInt32(
+ buf[0], buf[1], buf[2], buf[3]);
+ gPFileData->mtime = (time_t) makeUInt32(
+ buf[4], buf[5], buf[6], buf[7]);
+ if (central)
+ break;
+ if (dataSize > 8) {
+ gPFileData->uid = (uid_t) makeUInt16(buf[8], buf[9]);
+ gPFileData->gid = (uid_t) makeUInt16(buf[10], buf[11]);
+ }
+ // Unix0 has an extra (ignored) field at the end.
+ break;
+ case 0x5455: { // 'time'
+ uio_uint8 flags;
+ const char *bufPtr;
+ flags = buf[0];
+ bufPtr = buf + 1;
+ if (isBitSet(flags, 0)) {
+ // modification time is present
+ gPFileData->mtime = (time_t) makeUInt32(
+ bufPtr[0], bufPtr[1], bufPtr[2], bufPtr[3]);
+ bufPtr += 4;
+ }
+ // The flags field, even in the central header, specifies what
+ // is present in the local header.
+ // The central header only contains a field for the mtime
+ // when it is present in the local header too, and
+ // never contains fields for other times.
+ if (central)
+ break;
+ if (isBitSet(flags, 1)) {
+ // modification time is present
+ gPFileData->atime = (time_t) makeUInt32(
+ bufPtr[0], bufPtr[1], bufPtr[2], bufPtr[3]);
+ bufPtr += 4;
+ }
+ // Creation time and possible other times are skipped.
+ break;
+ }
+ case 0x7855: // 'Unix2'
+ if (central)
+ break;
+ gPFileData->uid = (uid_t) makeUInt16(buf[0], buf[1]);
+ gPFileData->gid = (uid_t) makeUInt16(buf[2], buf[3]);
+ break;
+ case 0x756e: { // 'Unix3'
+ mode_t mode;
+ mode = (mode_t) makeUInt16(buf[4], buf[5]);
+ if (!S_ISREG(mode) && !S_ISDIR(mode)) {
+ fprintf(stderr, "Warning: Skipping '%s'; not a regular "
+ "file, nor a directory.\n", fileName);
+ return 1;
+ }
+ gPFileData->uid = (uid_t) makeUInt16(buf[10], buf[11]);
+ gPFileData->gid = (uid_t) makeUInt16(buf[12], buf[13]);
+ break;
+ }
+ default:
+#ifdef DEBUG
+ fprintf(stderr, "Debug: Extra field 0x%04x unsupported, "
+ "used for file '%s' - ignored.\n", headerID,
+ fileName);
+#endif
+ break;
+ } // switch
+ pos += dataSize;
+ } // while
+ if (pos != posEnd)
+ return -1;
+ return 0;
+}
+
+static int
+zip_badFile(zip_GPFileData *gPFileData, char *fileName) {
+ fprintf(stderr, "Error: Bad file format for .zip file.\n");
+ if (gPFileData != NULL)
+ zip_GPFileData_delete(gPFileData);
+ if (fileName != NULL)
+ uio_free(fileName);
+ errno = EINVAL; // Is this the best choice?
+ return -1;
+}
+
+static inline int
+zip_foundFile(uio_GPDir *gPDir, const char *path, zip_GPFileData *gPFileData) {
+ uio_GPFile *file;
+ size_t pathLen;
+ const char *rest;
+ const char *pathEnd;
+ const char *start, *end;
+ char *buf;
+
+ if (path[0] == '/')
+ path++;
+ pathLen = strlen(path);
+ if (path[pathLen - 1] == '/') {
+ fprintf(stderr, "Warning: '%s' is not a valid file name - skipped.\n",
+ path);
+ errno = EISDIR;
+ return -1;
+ }
+ pathEnd = path + pathLen;
+
+ switch (uio_walkGPPath(gPDir, path, pathLen, &gPDir, &rest)) {
+ case 0:
+ // The entire path was matched. The last part was not supposed
+ // to be a dir.
+ fprintf(stderr, "Warning: '%s' already exists as a dir - "
+ "skipped.\n", path);
+ errno = EISDIR;
+ return -1;
+ case ENOTDIR:
+ fprintf(stderr, "Warning: A component to '%s' is not a "
+ "directory - file skipped.\n", path);
+ errno = ENOTDIR;
+ return -1;
+ case ENOENT:
+ break;
+ }
+
+ buf = uio_malloc(pathLen + 1);
+ getFirstPathComponent(rest, pathEnd, &start, &end);
+ while (1) {
+ uio_GPDir *newGPDir;
+
+ if (end == start || (end - start == 1 && start[0] == '.') ||
+ (end - start == 2 && start[0] == '.' && start[1] == '.')) {
+ fprintf(stderr, "Warning: file '%s' has an invalid path - "
+ "skipped.\n", path);
+ uio_free(buf);
+ errno = EINVAL;
+ return -1;
+ }
+ if (end == pathEnd) {
+ // This is the last component; it should be the name of the dir.
+ rest = start;
+ break;
+ }
+ memcpy(buf, start, end - start);
+ buf[end - start] = '\0';
+ newGPDir = uio_GPDir_prepareSubDir(gPDir, buf);
+ newGPDir->flags |= uio_GPDir_COMPLETE;
+ // It will be complete when we're done adding
+ // all files, and it won't be used before that.
+ uio_GPDir_commitSubDir(gPDir, buf, newGPDir);
+
+ gPDir = newGPDir;
+ getNextPathComponent(pathEnd, &start, &end);
+ }
+
+ uio_free(buf);
+
+ file = uio_GPFile_new(gPDir->pRoot, (uio_GPFileExtra) gPFileData,
+ uio_gPFileFlagsFromPRootFlags(gPDir->pRoot->flags));
+ uio_GPDir_addFile(gPDir, rest, file);
+ return 0;
+}
+
+static inline int
+zip_foundDir(uio_GPDir *gPDir, const char *path, zip_GPDirData *gPDirData) {
+ size_t pathLen;
+ const char *pathEnd;
+ const char *rest;
+ const char *start, *end;
+ char *buf;
+
+ if (path[0] == '/')
+ path++;
+ pathLen = strlen(path);
+ pathEnd = path + pathLen;
+
+ switch (uio_walkGPPath(gPDir, path, pathLen, &gPDir, &rest)) {
+ case 0:
+ // The dir already exists. Only need to add gPDirData
+ if (gPDir->extra != NULL) {
+ fprintf(stderr, "Warning: '%s' is present more than once "
+ "in the zip file. The last entry will be used.\n",
+ path);
+ zip_GPDirData_delete(gPDir->extra);
+ }
+ gPDir->extra = gPDirData;
+ return 0;
+ case ENOTDIR:
+ fprintf(stderr, "Warning: A component of '%s' is not a "
+ "directory - file skipped.\n", path);
+ errno = ENOTDIR;
+ return -1;
+ case ENOENT:
+ break;
+ }
+
+ buf = uio_malloc(pathLen + 1);
+ getFirstPathComponent(rest, pathEnd, &start, &end);
+ while (start < pathEnd) {
+ uio_GPDir *newGPDir;
+
+ if (end == start || (end - start == 1 && start[0] == '.') ||
+ (end - start == 2 && start[0] == '.' && start[1] == '.')) {
+ fprintf(stderr, "Warning: directory '%s' has an invalid path - "
+ "skipped.\n", path);
+ uio_free(buf);
+ errno = EINVAL;
+ return -1;
+ }
+ memcpy(buf, start, end - start);
+ buf[end - start] = '\0';
+ newGPDir = uio_GPDir_prepareSubDir(gPDir, buf);
+ newGPDir->flags |= uio_GPDir_COMPLETE;
+ // It will be complete when we're done adding
+ // all files, and it won't be used before that.
+ uio_GPDir_commitSubDir(gPDir, buf, newGPDir);
+
+ gPDir = newGPDir;
+ getNextPathComponent(pathEnd, &start, &end);
+ }
+ gPDir->extra = gPDirData;
+
+ uio_free(buf);
+ return 0;
+}
+
+static int
+zip_initZipStream(z_stream *zipStream) {
+ int retVal;
+
+ zipStream->next_in = Z_NULL;
+ zipStream->avail_in = 0;
+ zipStream->zalloc = zip_alloc;
+ zipStream->zfree = zip_free;
+ zipStream->opaque = NULL;
+ retVal = inflateInit2(zipStream, -MAX_WBITS);
+ // Negative window size means that no zlib header is present.
+ // This feature is undocumented in zlib, but it's used
+ // in the minizip program from the zlib contrib dir.
+ // The absolute value is used as real Window size.
+ if (retVal != Z_OK) {
+ switch (retVal) {
+ case Z_MEM_ERROR:
+ fprintf(stderr, "Error: Not enough memory available for "
+ " decompression.\n");
+ break;
+ case Z_VERSION_ERROR:
+ fprintf(stderr, "Error: Incompatible version problem for "
+ " decompression.\n");
+ break;
+ default:
+ fprintf(stderr, "Fatal: unknown error from inflateInit().\n");
+ abort();
+ }
+ if (zipStream->msg != NULL)
+ fprintf(stderr, "ZLib reports: %s\n", zipStream->msg);
+ errno = EIO;
+ // Using EIO to report an error in the backend.
+ return -1;
+ }
+ return 0;
+}
+
+static int
+zip_unInitZipStream(z_stream *zipStream) {
+ int retVal;
+
+ retVal = inflateEnd(zipStream);
+ if (retVal != Z_OK) {
+ switch (retVal) {
+ case Z_STREAM_ERROR:
+ // This means zipStream is bad, which is most likely an
+ // error in the code using zlib.
+ fprintf(stderr, "Fatal: internal error using zlib.\n");
+ abort();
+ break;
+ default:
+ fprintf(stderr, "Fatal: unknown error from inflateEnd().\n");
+ abort();
+ }
+ if (zipStream->msg != NULL)
+ fprintf(stderr, "ZLib reports: %s\n", zipStream->msg);
+ errno = EIO;
+ // Using EIO to report an error in the backend.
+ return -1;
+ }
+ return 0;
+}
+
+static int
+zip_reInitZipStream(z_stream *zipStream) {
+ int retVal;
+
+ zipStream->next_in = Z_NULL;
+ zipStream->avail_in = 0;
+ retVal = inflateReset(zipStream);
+ if (retVal != Z_OK) {
+ switch (retVal) {
+ case Z_STREAM_ERROR:
+ // This means zipStream is bad, which is most likely an
+ // error in the code using zlib.
+ fprintf(stderr, "Fatal: internal error using zlib.\n");
+ abort();
+ break;
+ default:
+ fprintf(stderr, "Fatal: unknown error from inflateInit().\n");
+ abort();
+ }
+ if (zipStream->msg != NULL)
+ fprintf(stderr, "ZLib reports: %s\n", zipStream->msg);
+ errno = EIO;
+ // Using EIO to report an error in the backend.
+ return -1;
+ }
+ return 0;
+}
+
+
+// Used internally by zlib for allocating memory.
+static voidpf
+zip_alloc(voidpf opaque, uInt items, uInt size) {
+ (void) opaque;
+ return (voidpf) uio_calloc((size_t) items, (size_t) size);
+}
+
+// Used internally by zlib for freeing memory.
+static void
+zip_free(voidpf opaque, voidpf address) {
+ (void) opaque;
+ uio_free((void *) address);
+}
+
+static inline zip_GPFileData *
+zip_GPFileData_new(void) {
+ return zip_GPFileData_alloc();
+}
+
+static inline void
+zip_GPFileData_delete(zip_GPFileData *gPFileData) {
+ zip_GPFileData_free(gPFileData);
+}
+
+static inline zip_GPFileData *
+zip_GPFileData_alloc(void) {
+ zip_GPFileData *result = uio_malloc(sizeof (zip_GPFileData));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(zip_GPFileData, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+zip_GPFileData_free(zip_GPFileData *gPFileData) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(zip_GPFileData, (void *) gPFileData);
+#endif
+ uio_free(gPFileData);
+}
+
+static inline void
+zip_GPDirData_delete(zip_GPDirData *gPDirData) {
+ zip_GPDirData_free(gPDirData);
+}
+
+static inline void
+zip_GPDirData_free(zip_GPDirData *gPDirData) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(zip_GPFileData, (void *) gPDirData);
+#endif
+ uio_free(gPDirData);
+}
+
+
+