Index: /trunk/include/iprt/err.h
===================================================================
--- /trunk/include/iprt/err.h	(revision 51695)
+++ /trunk/include/iprt/err.h	(revision 51696)
@@ -1712,4 +1712,20 @@
 /** @} */
 
+/** @name Pkzip status codes
+ * @{ */
+/** No end of central directory record found. */
+#define VERR_PKZIP_NO_EOCB                      (-960)
+/** Too long name string. */
+#define VERR_PKZIP_NAME_TOO_LONG                (-961)
+/** Local file header corrupt. */
+#define VERR_PKZIP_BAD_LF_HEADER                (-962)
+/** Central directory file header corrupt. */
+#define VERR_PKZIP_BAD_CDF_HEADER               (-963)
+/** Encountered an unknown type flag. */
+#define VERR_PKZIP_UNKNOWN_TYPE_FLAG            (-964)
+/** Found a ZIP64 Extra Information Field in a ZIP32 file. */
+#define VERR_PKZIP_ZIP64EX_IN_ZIP32             (-965)
+
+
 /** @name RTZip status codes
  * @{ */
Index: /trunk/include/iprt/mangling.h
===================================================================
--- /trunk/include/iprt/mangling.h	(revision 51695)
+++ /trunk/include/iprt/mangling.h	(revision 51696)
@@ -1903,6 +1903,8 @@
 # define RTZipGzipDecompressIoStream                    RT_MANGLER(RTZipGzipDecompressIoStream)
 # define RTZipTarCmd                                    RT_MANGLER(RTZipTarCmd)
+# define RTZipUnzipCmd                                  RT_MANGLER(RTZipUnzipCmd)
 # define RTZipTarFsStreamFromIoStream                   RT_MANGLER(RTZipTarFsStreamFromIoStream)
 # define RTZipXarFsStreamFromIoStream                   RT_MANGLER(RTZipXarFsStreamFromIoStream)
+# define RTZipPkzipFsStreamFromIoStream                 RT_MANGLER(RTZipPkzipFsStreamFromIoStream)
 /*
  * Stable variables (alphabetical order):
Index: /trunk/include/iprt/zip.h
===================================================================
--- /trunk/include/iprt/zip.h	(revision 51695)
+++ /trunk/include/iprt/zip.h	(revision 51696)
@@ -86,4 +86,6 @@
     /** Lempel-Ziv-Oberhumer compression. */
     RTZIPTYPE_LZO,
+    /* Zlib compression the data without zlib header. */
+    RTZIPTYPE_ZLIB_NO_HEADER,
     /** End of valid the valid compression types.  */
     RTZIPTYPE_END
@@ -217,4 +219,19 @@
 
 /**
+ * Opens a zip decompression I/O stream.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   hVfsIosIn           The compressed input stream (must be readable).
+ *                              The reference is not consumed, instead another
+ *                              one is retained.
+ * @param   fFlags              Flags, MBZ.
+ * @param   phVfsIosUnzip       Where to return the handle to the gunzipped I/O
+ *                              stream (read).
+ */
+RTDECL(int) RTZipDecompressIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSIOSTREAM phVfsIosUnzip);
+
+
+/**
  * Opens a gzip decompression I/O stream.
  *
@@ -279,7 +296,7 @@
 
 /**
- * Opens a XAR filesystem stream.
- *
- * This is used to extract, list or check a XAR archive.
+ * Opens a ZIP filesystem stream.
+ *
+ * This is used to extract, list or check a ZIP archive.
  *
  * @returns IPRT status code.
@@ -288,4 +305,30 @@
  *                              not consumed, instead another one is retained.
  * @param   fFlags              Flags, MBZ.
+ * @param   phVfsFss            Where to return the handle to the TAR
+ *                              filesystem stream.
+ */
+RTDECL(int) RTZipPkzipFsStreamFromIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss);
+
+/**
+ * A mini UNZIP program.
+ *
+ * @returns Program exit code.
+ * @
+ * @param   cArgs               The number of arguments.
+ * @param   papszArgs           The argument vector.  (Note that this may be
+ *                              reordered, so the memory must be writable.)
+ */
+RTDECL(RTEXITCODE) RTZipUnzipCmd(unsigned cArgs, char **papszArgs);
+
+/**
+ * Opens a XAR filesystem stream.
+ *
+ * This is used to extract, list or check a XAR archive.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   hVfsIosIn           The compressed input stream.  The reference is
+ *                              not consumed, instead another one is retained.
+ * @param   fFlags              Flags, MBZ.
  * @param   phVfsFss            Where to return the handle to the XAR filesystem
  *                              stream.
Index: /trunk/src/VBox/Runtime/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Runtime/Makefile.kmk	(revision 51695)
+++ /trunk/src/VBox/Runtime/Makefile.kmk	(revision 51696)
@@ -464,6 +464,8 @@
 	common/zip/tar.cpp \
 	common/zip/tarcmd.cpp \
+	common/zip/unzipcmd.cpp \
 	common/zip/tarvfs.cpp \
 	common/zip/gzipvfs.cpp \
+	common/zip/pkzipvfs.cpp \
 	common/zip/zip.cpp \
 	generic/createtemp-generic.cpp \
Index: /trunk/src/VBox/Runtime/common/zip/pkzipvfs.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/zip/pkzipvfs.cpp	(revision 51696)
+++ /trunk/src/VBox/Runtime/common/zip/pkzipvfs.cpp	(revision 51696)
@@ -0,0 +1,1260 @@
+/* $Id$ */
+/** @file
+ * IPRT - PKZIP Virtual Filesystem.
+ */
+
+/*
+ * Copyright (C) 2014 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/******************************************************************************
+ *   Header Files                                                             *
+ ******************************************************************************/
+#include <iprt/zip.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/poll.h>
+#include <iprt/string.h>
+#include <iprt/vfs.h>
+#include <iprt/vfslowlevel.h>
+#include <iprt/stream.h>
+
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+/* See http://www.pkware.com/documents/casestudies/APPNOTE.TXT */
+
+/**
+ * PKZip Local File Header.
+ */
+#pragma pack(1)
+typedef struct RTZIPPKZIPLOCALFILEHDR
+{
+    /** Magic value, see RTZIPPKZIPLOCALFILEHDR_MAGIC. */
+    uint32_t                u32Magic;
+    /** Minimum version needed to extract. */
+    uint16_t                u16Version;
+    /** General purpose bit flag. */
+    uint16_t                fFlags;
+    /** Compression method. See RTZIPPKZIP_COMP_METHOD_XXX. */
+    uint16_t                u16ComprMethod;
+    /** Last modified time, MS-DOS format: HHHHHMMM MMMSSSSS, multiply seconds by 2 */
+    uint16_t                u16LastModifiedTime;
+    /** Last modified date, MS-DOS format: YYYYYYYM MMMDDDDD, year starts at 1980 */
+    uint16_t                u16LastModifiedDate;
+    /** Checksum. */
+    uint32_t                u32Crc;
+    /** Compressed size. */
+    uint32_t                cbCompressed;
+    /** Uncompressed size. */
+    uint32_t                cbUncompressed;
+    /** Length of the file name. */
+    uint16_t                cbFilename;
+    /** Length of the extra field. */
+    uint16_t                cbExtra;
+    /** Start of the file name. */
+    uint8_t                 u8Filename;
+} RTZIPPKZIPLOCALFILEHDR;
+#pragma pack()
+AssertCompileSize(RTZIPPKZIPLOCALFILEHDR, 30+1);
+/** Pointer to PKZip Local File Header. */
+typedef RTZIPPKZIPLOCALFILEHDR *PRTZIPPKZIPLOCALFILEHDR;
+
+#define RTZIPPKZIPLOCALFILEHDR_MAGIC        RT_MAKE_U32_FROM_U8('P','K','\003','\004')
+
+/**
+ * PKZip compression method.
+ */
+typedef enum RTZIPPKZIP_COMP_METHOD
+{
+    /** No compression */
+    RTZIPPKZIP_COMP_METHOD_STORED = 0,
+    /** Shrunk */
+    RTZIPPKZIP_COMP_METHOD_SHRUNK = 1,
+    /** Reduced with compression factor 1 */
+    RTZIPPKZIP_COMP_METHOD_REDUCED1 = 2,
+    /** Reduced with compression factor 2 */
+    RTZIPPKZIP_COMP_METHOD_REDUCED2 = 3,
+    /** Reduced with compression factor 3 */
+    RTZIPPKZIP_COMP_METHOD_REDUCED3 = 4,
+    /** Reduced with compression factor 4 */
+    RTZIPPKZIP_COMP_METHOD_REDUCED4 = 5,
+    /** Imploded */
+    RTZIPPKZIP_COMP_METHOD_IMPLODED = 6,
+    /** Deflated */
+    RTZIPPKZIP_COMP_METHOD_DEFLATED = 8,
+    /** Deflated64 */
+    RTZIPPKZIP_COMP_METHOD_DEFLATED64 = 9,
+    /* Compressed using bzip2 */
+    RTZIPPKZIP_COMP_METHOD_BZIP2 = 12,
+    /** Compressed using LZMA */
+    RTZIPPKZIP_COMP_METHOD_LZMA = 14
+} RTZIPPKZIP_COMP_METHOD;
+
+/**
+ * PKZip Central Directory Header.
+ */
+#pragma pack(1)
+typedef struct RTZIPPKZIPCENTRDIRHDR
+{
+    /** The magic value. See RTZIPPKZIPCENTRDIRHDR_MAGIC. */
+    uint32_t                u32Magic;
+    /** The version used for creating the item. */
+    uint16_t                u16VerMade;
+    /** The minimum version required for extracting the item. */
+    uint16_t                u16VerRequired;
+    /** General purpose flags. */
+    uint16_t                fFlags;
+    /** Compresstion method. See RTZIPPKZIP_COMP_METHOD_XXX */
+    uint16_t                u16ComprMethod;
+    /** Last modified time, MS-DOS format: HHHHHMMM MMMSSSSS, multiply seconds by 2 */
+    uint16_t                u16LastModifiedTime;
+    /** Last modified date, MS-DOS format: YYYYYYYM MMMDDDDD, year starts at 1980 */
+    uint16_t                u16LastModifiedDate;
+    /** Checksum. */
+    uint32_t                u32Crc;
+    /** Compressed size. */
+    uint32_t                cbCompressed;
+    /** Uncompressed size. */
+    uint32_t                cbUncompressed;
+    /** Length of the object file name. */
+    uint16_t                cbFilename;
+    /** Length of the extra field. */
+    uint16_t                cbExtra;
+    /** Length of the object comment. */
+    uint16_t                cbComment;
+    /** The number of the disk on which this file begins. */
+    uint16_t                iDiskStart;
+    /** Internal attributes. */
+    uint16_t                u16IntAttrib;
+    /** External attributes. */
+    uint32_t                u32ExtAttrib;
+    /** Offset from the start of the first disk on which this file appears to
+     * where the local file header should be found. */
+    uint32_t                offLocalFileHeader;
+    /** Start of the file name. */
+    uint8_t                 u8Filename;
+} RTZIPPKZIPCENTRDIRHDR;
+#pragma pack()
+AssertCompileSize(RTZIPPKZIPCENTRDIRHDR, 46+1);
+/** Pointer to the PKZip Central Directory Header. */
+typedef RTZIPPKZIPCENTRDIRHDR *PRTZIPPKZIPCENTRDIRHDR;
+
+#define RTZIPPKZIPCENTRDIRHDR_MAGIC         RT_MAKE_U32_FROM_U8('P','K','\001','\002')
+
+/**
+ * PKZip End of Central Directory Record.
+ */
+#pragma pack(1)
+typedef struct RTZIPPKZIPENDOFCENTRDIRREC
+{
+    /** The magic value. See RTZIPPKZIPENDOFCENTRDIRREC_MAGIC. */
+    uint32_t                u32Magic;
+    /** Number of this disk. */
+    uint16_t                iThisDisk;
+    /** Number of the disk with the start of the Central Directory. */
+    uint16_t                iDiskStartCentrDirectory;
+    /** Number of Central Directory entries on this disk. */
+    uint16_t                cCentrDirRecordsThisDisk;
+    /** Number of Central Directory records. */
+    uint16_t                cCentrDirRecords;
+    /** Size of the Central Directory in bytes. */
+    uint32_t                cbCentrDir;
+    /** Offset of the Central Directory. */
+    uint32_t                offCentrDir;
+    /** Size of the comment in bytes. */
+    uint16_t                cbComment;
+    /** Start of the comment. */
+    uint8_t                 u8Comment;
+} RTZIPPKZIPENDOFCENTRDIRREC;
+#pragma pack()
+AssertCompileSize(RTZIPPKZIPENDOFCENTRDIRREC, 22+1);
+/** Pointer to the PKZip End of Central Directory Record. */
+typedef RTZIPPKZIPENDOFCENTRDIRREC const *PCRTZIPPKZIPENDOFCENTRDIRREC;
+
+#define RTZIPPKZIPENDOFCENTRDIRREC_MAGIC    RT_MAKE_U32_FROM_U8('P','K','\005','\006')
+
+/**
+ * PKZip ZIP64 End of Central Directory Record.
+ */
+#pragma pack(1)
+typedef struct RTZIPPKZIP64ENDOFCENTRDIRREC
+{
+    /** The magic value. See RTZIPPKZIP64ENDOFCENTRDIRREC_MAGIC. */
+    uint32_t                u32Magic;
+    /** Size of Zip64 end of Central Directory Record. */
+    uint64_t                cbSizeEocdr;
+    /** The version used for creating the item. */
+    uint16_t                u16VerMade;
+    /** The minimum version required for extracting the item. */
+    uint16_t                u16VerRequired;
+    /** Number of this disk. */
+    uint32_t                iThisDisk;
+    /** Number of the disk with the start of the Central Directory. */
+    uint32_t                iDiskStartCentrDirectory;
+    /** Number of Central Directory entries on this disk. */
+    uint64_t                cCentrDirRecordsThisDisk;
+    /** Number of Central Directory records. */
+    uint64_t                cCentrDirRecords;
+    /** Size of the Central Directory in bytes. */
+    uint64_t                cbCentrDir;
+    /** Offset of the Central Directory. */
+    uint64_t                offCentrDir;
+} RTZIPPKZIP64ENDOFCENTRDIRREC;
+#pragma pack()
+AssertCompileSize(RTZIPPKZIP64ENDOFCENTRDIRREC, 56);
+/** Pointer to the 64-bit PKZip End of Central Directory Record. */
+typedef RTZIPPKZIP64ENDOFCENTRDIRREC *PRTZIPPKZIP64ENDOFCENTRDIRREC;
+
+#define RTZIPPKZIP64ENDOFCENTRDIRREC_MAGIC  RT_MAKE_U32_FROM_U8('P','K','\006','\006')
+
+/**
+ * PKZip ZIP64 End of Central Directory Locator.
+ */
+#pragma pack(1)
+typedef struct RTZIPPKZIP64ENDOFCENTRDIRLOC
+{
+    /** The magic value. See RTZIPPKZIP64ENDOFCENTRDIRLOC_MAGIC. */
+    uint32_t                u32Magic;
+    /** Number of the disk with the start of the ZIP64 End of Central Directory. */
+    uint32_t                iDiskStartCentrDir;
+    /** Relative offset of the ZIP64 End of Central Directory Record. */
+    uint64_t                offEndOfCentrDirRec;
+    /** Total number of disks. */
+    uint32_t                cDisks;
+} RTZIPPKZIP64ENDOFCENTRDIRLOC;
+#pragma pack()
+AssertCompileSize(RTZIPPKZIP64ENDOFCENTRDIRLOC, 20);
+
+#define RTZIPPKZIP64ENDOFCENTRDIRLOC_MAGIC  RT_MAKE_U32_FROM_U8('P','K','\006','\007')
+
+/**
+ * PKZip ZIP64 Extended Information Extra Field.
+ */
+#pragma pack(1)
+typedef struct RTZIPPKZIP64EXTRAFIELD
+{
+    /** Uncompressed size. */
+    uint64_t                cbUncompressed;
+    /** Compressed size. */
+    uint64_t                cbCompressed;
+    /** Offset from the start of the first disk on which this file appears to
+     * where the local file header should be found. */
+    uint64_t                offLocalFileHeader;
+    /** The number of the disk on which this file begins. */
+    uint32_t                iDiskStart;
+} RTZIPPKZIP64EXTRAFIELD;
+#pragma pack()
+/** Pointer to the ZIP64 Extended Information Extra Field. */
+typedef RTZIPPKZIP64EXTRAFIELD *PRTZIPPKZIP64EXTRAFIELD;
+AssertCompileSize(RTZIPPKZIP64EXTRAFIELD, 28);
+
+/**
+ * PKZip reader instance data.
+ */
+typedef struct RTZIPPKZIPREADER
+{
+    /** Set if we have the End of Central Directory record. */
+    bool                    fHaveEocd;
+    /** The Central Directory header. */
+    RTZIPPKZIPCENTRDIRHDR   cdh;
+    /** ZIP64 extended information. */
+    RTZIPPKZIP64EXTRAFIELD  cd64ex;
+    /** Set if ZIP64 End of Central Directory Locator is present (archive setting). */
+    bool                    fZip64Eocd;
+    /** Set if cd64ex is valid for the current file header (object setting). */
+    bool                    fZip64Ex;
+    /* The name of the current object. */
+    char                    szName[RTPATH_MAX];
+} RTZIPPKZIPREADER;
+/** Pointer to the PKZip reader instance data. */
+typedef RTZIPPKZIPREADER *PRTZIPPKZIPREADER;
+
+/**
+ * Pkzip object (directory).
+ */
+typedef struct RTZIPPKZIPBASEOBJ
+{
+    /** Pointer to the reader instance data (resides in the filesystem
+     * stream). */
+    PRTZIPPKZIPREADER       pPkzipReader;
+    /** The object info with unix attributes. */
+    RTFSOBJINFO             ObjInfo;
+} RTZIPPKZIPBASEOBJ;
+/** Pointer to a PKZIP filesystem stream base object. */
+typedef RTZIPPKZIPBASEOBJ *PRTZIPPKZIPBASEOBJ;
+
+/**
+ * Pkzip object (file) represented as a VFS I/O stream.
+ */
+typedef struct RTZIPPKZIPIOSTREAM
+{
+    /** The basic PKZIP object data. */
+    RTZIPPKZIPBASEOBJ       BaseObj;
+    /** The number of (uncompressed) bytes in the file. */
+    RTFOFF                  cbFile;
+    /** The current file position at uncompressed file data. */
+    RTFOFF                  offFile;
+    /** The start position of the compressed data in the hVfsIos. */
+    RTFOFF                  offCompStart;
+    /** The current position for decompressing bytes in the hVfsIos. */
+    RTFOFF                  offComp;
+    /** The number of compressed bytes starting at offCompStart. */
+    RTFOFF                  cbComp;
+    /** Set if we have to pass the type function the next time the input
+     * function is called. */
+    bool                    fPassZipType;
+    /** Set if we've reached the end of the file. */
+    bool                    fEndOfStream;
+    /** Pkzip compression method for this object. */
+    RTZIPPKZIP_COMP_METHOD  enmCompMethod;
+    /** Zip compression method. */
+    RTZIPTYPE               enmZipType;
+    /** The decompressor instance. */
+    PRTZIPDECOMP            pZip;
+    /** The input I/O stream. */
+    RTVFSIOSTREAM           hVfsIos;
+} RTZIPPKZIPIOSTREAM;
+/** Pointer to a the private data of a PKZIP file I/O stream. */
+typedef RTZIPPKZIPIOSTREAM *PRTZIPPKZIPIOSTREAM;
+
+
+/**
+ * PKZip filesystem stream private data. The stream must be seekable!
+ */
+typedef struct RTZIPPKZIPFSSTREAM
+{
+    /** The input I/O stream. */
+    RTVFSIOSTREAM           hVfsIos;
+
+    /** The current object (referenced). */
+    RTVFSOBJ                hVfsCurObj;
+    /** Pointer to the private data if hVfsCurObj is representing a file. */
+    PRTZIPPKZIPIOSTREAM     pCurIosData;
+
+    /** The offset of the first Central Directory header. */
+    RTFOFF                  offFirstCdh;
+    /** The offset of the next Central Directory header. */
+    RTFOFF                  offNextCdh;
+
+    /** Size of the central directory. */
+    uint64_t                cbCentrDir;
+    /** Current central directory entry. */
+    uint64_t                iCentrDirEntry;
+    /** Number of central directory entries. */
+    uint64_t                cCentrDirEntries;
+
+    /** Set if we have the End of Central Directory Record. */
+    bool                    fHaveEocd;
+    /** Set if we've reached the end of the stream. */
+    bool                    fEndOfStream;
+    /** Set if we've encountered a fatal error. */
+    int                     rcFatal;
+
+    /** The PKZIP reader instance data. */
+    RTZIPPKZIPREADER        PkzipReader;
+} RTZIPPKZIPFSSTREAM;
+/** Pointer to a the private data of a PKZIP filesystem stream. */
+typedef RTZIPPKZIPFSSTREAM *PRTZIPPKZIPFSSTREAM;
+
+
+
+/**
+ * Decode date/time from DOS format as used in PKZip.
+ */
+static int rtZipPkzipReaderDecodeDosTime(PRTTIMESPEC pTimeSpec, uint16_t u16Time, uint16_t u16Date)
+{
+    RTTIME time;
+    RT_ZERO(time);
+    time.i32Year    = ((u16Date & 0xfe00) >>  9) + 1980;
+    time.u8Month    =  (u16Date & 0x01e0) >>  5;
+    time.u8MonthDay =   u16Date & 0x001f;
+    time.u8Hour     =  (u16Time & 0xf800) >> 11;
+    time.u8Minute   =  (u16Time & 0x07e0) >>  5;
+    time.u8Second   =   u16Time & 0x001f;
+    RTTimeNormalize(&time);
+    RTTimeImplode(pTimeSpec, &time);
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Parse the Local File Header.
+ * Just skip the data as we trust the Central Directory.
+ */
+static int rtZipPkzipParseLocalFileHeader(PRTZIPPKZIPREADER pThis, PRTZIPPKZIPLOCALFILEHDR pLfh, size_t *pcbExtra)
+{
+    if (pLfh->cbFilename >= sizeof(pThis->szName))
+        return VERR_PKZIP_NAME_TOO_LONG;
+
+    *pcbExtra = pLfh->cbFilename + pLfh->cbExtra;
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Parse the Central Directory Header.
+ */
+static int rtZipPkzipParseCentrDirHeader(PRTZIPPKZIPREADER pThis, PRTZIPPKZIPCENTRDIRHDR pCdh, size_t *pcbExtra)
+{
+    if (pCdh->u32Magic != RTZIPPKZIPCENTRDIRHDR_MAGIC)
+        return VERR_PKZIP_BAD_CDF_HEADER;
+
+    if (pCdh->cbFilename >= sizeof(pThis->szName))
+        return VERR_PKZIP_NAME_TOO_LONG;
+
+    *pcbExtra = pCdh->cbFilename + pCdh->cbExtra + pCdh->cbComment;
+
+    pThis->cdh = *pCdh;
+    pThis->fZip64Ex = false;
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Return the offset of the Local File Header.
+ */
+static RTFOFF rtZipPkzipReaderOffLocalHeader(PRTZIPPKZIPREADER pThis)
+{
+    if (pThis->fZip64Ex && pThis->cdh.offLocalFileHeader == -1U)
+        return pThis->cd64ex.offLocalFileHeader;
+
+    return pThis->cdh.offLocalFileHeader;
+}
+
+
+/**
+ * Return the uncompressed object size.
+ */
+static RTFOFF rtZipPkzipReaderUncompressed(PRTZIPPKZIPREADER pThis)
+{
+    if (pThis->fZip64Ex && pThis->cdh.cbUncompressed == -1U)
+        return pThis->cd64ex.cbUncompressed;
+    
+    return pThis->cdh.cbUncompressed;
+}
+
+
+/**
+ * Return the compressed object size.
+ */
+static RTFOFF rtZipPkzipReaderCompressed(PRTZIPPKZIPREADER pThis)
+{
+    if (pThis->fZip64Ex && pThis->cdh.cbCompressed == -1U)
+        return pThis->cd64ex.cbCompressed;
+    
+    return pThis->cdh.cbCompressed;
+}
+
+
+/**
+ * Parse the extra part of the Central Directory Header.
+ */
+static int rtZipPkzipParseCentrDirHeaderExtra(PRTZIPPKZIPREADER pThis, uint8_t *pu8Buf, size_t cb,
+                                              RTZIPPKZIP_COMP_METHOD *penmCompMethod, PRTFOFF pcbCompressed)
+{
+    int rc = RTStrCopyEx(pThis->szName, sizeof(pThis->szName), (const char*)pu8Buf, pThis->cdh.cbFilename);
+    if (RT_SUCCESS(rc))
+    {
+        uint8_t *offStart = pu8Buf;
+        pu8Buf += pThis->cdh.cbFilename;
+        cb      = pThis->cdh.cbExtra;
+        while (cb >= 4)
+        {
+            uint16_t idExtra = *(uint16_t*)pu8Buf;
+            pu8Buf += 2;
+            uint16_t cbExtra = *(uint16_t*)pu8Buf;
+            pu8Buf += 2;
+            cb     -= 4;
+
+            if (cb >= cbExtra)
+            {
+                switch (idExtra)
+                {
+                    case 0x0001:
+                        /*
+                         * ZIP64 Extended Information Extra Field.
+                         */
+                        if (!pThis->fZip64Eocd)
+                            return VERR_PKZIP_ZIP64EX_IN_ZIP32;
+                        /* Not all fields are really used. */
+                        RT_ZERO(pThis->cd64ex);
+                        memcpy(&pThis->cd64ex, pu8Buf, cbExtra);
+                        pThis->fZip64Ex = true;
+                        break;
+
+                    default:
+                        /* unknown, just skip */
+                        break;
+                }
+                pu8Buf += cbExtra;
+                cb     -= cbExtra;
+            }
+            else
+            {
+                rc = VERR_PKZIP_BAD_CDF_HEADER;
+                break;
+            }
+        }
+
+        *penmCompMethod = (RTZIPPKZIP_COMP_METHOD)pThis->cdh.u16ComprMethod;
+        *pcbCompressed  = rtZipPkzipReaderCompressed(pThis);
+    }
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Translate a PKZip header to an IPRT object info structure.
+ */
+static int rtZipPkzipReaderGetFsObjInfo(PRTZIPPKZIPREADER pThis, PRTFSOBJINFO pObjInfo)
+{
+    /*
+     * Zap the whole structure, this takes care of unused space in the union.
+     */
+    RT_ZERO(*pObjInfo);
+    pObjInfo->cbObject = rtZipPkzipReaderUncompressed(pThis);
+    pObjInfo->cbAllocated = rtZipPkzipReaderUncompressed(pThis); /* XXX */
+    RTTIMESPEC ts;
+    rtZipPkzipReaderDecodeDosTime(&ts, pThis->cdh.u16LastModifiedTime, pThis->cdh.u16LastModifiedDate);
+    pObjInfo->ChangeTime = ts;
+    pObjInfo->ModificationTime = ts;
+    pObjInfo->AccessTime = ts;
+    pObjInfo->BirthTime = ts;
+    const char *pszEnd = strchr(pThis->szName, '\0');
+    if (pszEnd == &pThis->szName[0] || pszEnd[-1] != '/')
+        pObjInfo->Attr.fMode = RTFS_TYPE_FILE | RTFS_UNIX_IRUSR | RTFS_UNIX_IRWXU;
+    else
+        pObjInfo->Attr.fMode = RTFS_TYPE_DIRECTORY | RTFS_UNIX_IRUSR | RTFS_UNIX_IRUSR | RTFS_UNIX_IXUSR;
+    pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+    pObjInfo->Attr.u.Unix.cHardlinks = 1;
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Search the magic value of the End Of Central Directory Record.
+ *
+ * @returns true if found, false otherwise.
+ * @param   pu8Buf      buffer.
+ * @param   cb          size of buffer.
+ * @param   piPos       where to store the position of the magic value.
+ */
+static bool rtZipPkzipReaderScanEocd(const uint8_t *pu8Buf, size_t cb, int *piPos)
+{
+    if (cb < 4)
+        return false;
+    int i;
+    for (i = cb - 4; i >= 0; --i)
+        if (*(uint32_t*)(pu8Buf + i) == RTZIPPKZIPENDOFCENTRDIRREC_MAGIC)
+        {
+            *piPos = i;
+            return true;
+        }
+    return false;
+}
+
+
+/**
+ * Read the Local File Header. We ignore the content -- we trust the Central
+ * Directory.
+ */
+static int rtZipPkzipFssIosReadLfh(PRTZIPPKZIPFSSTREAM pThis, RTFOFF *poffStartData)
+{
+    RTZIPPKZIPLOCALFILEHDR lfh;
+    RTFOFF offLocalFileHeader = rtZipPkzipReaderOffLocalHeader(&pThis->PkzipReader);
+    int rc = RTVfsIoStrmReadAt(pThis->hVfsIos, offLocalFileHeader,
+                               &lfh, sizeof(lfh) - 1, true /*fBlocking*/, NULL);
+    if (RT_SUCCESS(rc))
+    {
+        if (lfh.u32Magic == RTZIPPKZIPLOCALFILEHDR_MAGIC)
+        {
+            size_t cbExtra;;
+            rc = rtZipPkzipParseLocalFileHeader(&pThis->PkzipReader, &lfh, &cbExtra);
+            if (RT_SUCCESS(rc))
+            {
+                /* Just skip the file name and and extra field. We use the data
+                 * from the Central Directory Header. */
+                rc = RTVfsIoStrmSkip(pThis->hVfsIos, cbExtra);
+                if (RT_SUCCESS(rc))
+                    *poffStartData = offLocalFileHeader + sizeof(lfh) - 1 + cbExtra;
+            }
+        }
+        else
+            rc = VERR_PKZIP_BAD_LF_HEADER;
+    }
+
+    return rc;
+}
+
+
+/**
+ * Scan the current Central Directory Header.
+ */
+static int rtZipPkzipFssIosReadCdh(PRTZIPPKZIPFSSTREAM pThis, RTFOFF *poffStartData,
+                                    RTZIPPKZIP_COMP_METHOD *penmCompMethod, PRTFOFF pcbCompressed)
+{
+    int rc;
+
+    RTFOFF offCd = pThis->offNextCdh - pThis->offFirstCdh;
+    if (   pThis->iCentrDirEntry < pThis->cCentrDirEntries
+        || offCd + sizeof(RTZIPPKZIPCENTRDIRHDR) - 1 <= pThis->cbCentrDir)
+    {
+        RTZIPPKZIPCENTRDIRHDR cdh;
+        rc = RTVfsIoStrmReadAt(pThis->hVfsIos, pThis->offNextCdh,
+                               &cdh, sizeof(cdh) - 1, true /*fBlocking*/, NULL);
+        if (RT_SUCCESS(rc))
+        {
+            pThis->offNextCdh += sizeof(cdh) - 1;
+            pThis->iCentrDirEntry++;
+            size_t cbExtra;
+            rc = rtZipPkzipParseCentrDirHeader(&pThis->PkzipReader, &cdh, &cbExtra);
+            if (RT_SUCCESS(rc))
+            {
+                if (offCd + sizeof(RTZIPPKZIPCENTRDIRHDR) - 1 + cbExtra <= pThis->cbCentrDir)
+                {
+                    /* extra data up to 64k */
+                    uint8_t *pu8Buf = (uint8_t*)RTMemTmpAlloc(cbExtra);
+                    if (pu8Buf)
+                    {
+                        rc = RTVfsIoStrmRead(pThis->hVfsIos, pu8Buf, cbExtra, true /*fBlocking*/, NULL);
+                        if (RT_SUCCESS(rc))
+                        {
+                            rc = rtZipPkzipParseCentrDirHeaderExtra(&pThis->PkzipReader, pu8Buf, cbExtra,
+                                                                    penmCompMethod, pcbCompressed);
+                            if (RT_SUCCESS(rc))
+                                rc = rtZipPkzipFssIosReadLfh(pThis, poffStartData);
+                        }
+                        pThis->offNextCdh += cbExtra;
+                        RTMemTmpFree(pu8Buf);
+                    }
+                    else
+                        rc = VERR_NO_MEMORY;
+                }
+                else
+                    rc = VERR_EOF;
+            }
+        }
+    }
+    else
+        rc = VERR_EOF;
+
+    return rc;
+}
+
+
+/**
+ * Scan for the End of Central Directory Record. Of course this works not if
+ * the stream is non-seekable (i.e. a pipe).
+ */
+static int rtZipPkzipFssIosReadEocb(PRTZIPPKZIPFSSTREAM pThis)
+{
+    uint64_t cbFile;
+    int rc = RTVfsFileSeek(RTVfsIoStrmToFile(pThis->hVfsIos), 0, RTFILE_SEEK_END, &cbFile);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    if (cbFile < sizeof(RTZIPPKZIPENDOFCENTRDIRREC)-1)
+        return VERR_PKZIP_NO_EOCB;
+
+    /* search for start of the 'end of Central Directory Record' */
+    size_t cbBuf = RT_MIN(_1K, cbFile);
+    uint8_t *pu8Buf = (uint8_t*)RTMemTmpAlloc(cbBuf);
+    if (!pu8Buf)
+        return VERR_NO_MEMORY;
+
+    /* maximum size of EOCD comment 2^16-1 */
+    const size_t cbHdrMax = 0xffff + sizeof(RTZIPPKZIPENDOFCENTRDIRREC) - 1;
+    RTFOFF offMin = cbFile >= cbHdrMax ? cbFile - cbHdrMax : 0;
+
+    RTFOFF off = cbFile - cbBuf;
+    while (off >= offMin)
+    {
+        rc = RTVfsIoStrmReadAt(pThis->hVfsIos, off, pu8Buf, cbBuf, true /*fBlocking*/, NULL);
+        if (RT_FAILURE(rc))
+            break;
+        int offMagic;
+        if (rtZipPkzipReaderScanEocd(pu8Buf, cbBuf, &offMagic))
+        {
+            off += offMagic;
+            RTZIPPKZIPENDOFCENTRDIRREC eocd;
+            rc = RTVfsIoStrmReadAt(pThis->hVfsIos, off, &eocd, sizeof(eocd) - 1,
+                                   true /*fBlocking*/, NULL);
+            if (RT_SUCCESS(rc))
+            {
+                /* well, this shouldn't fail if the content didn't change */
+                if (eocd.u32Magic == RTZIPPKZIPENDOFCENTRDIRREC_MAGIC)
+                {
+                    /* sanity check */
+                    if (off + RT_UOFFSETOF(RTZIPPKZIPENDOFCENTRDIRREC, u8Comment) + eocd.cbComment == cbFile)
+                    {
+                        pThis->offFirstCdh = eocd.offCentrDir;
+                        pThis->offNextCdh = eocd.offCentrDir;
+                        pThis->iCentrDirEntry = 0;
+                        pThis->cCentrDirEntries = eocd.cCentrDirRecords;
+                        pThis->cbCentrDir = eocd.cbCentrDir;
+                        pThis->PkzipReader.fHaveEocd = true;
+                    }
+                    else
+                        rc = VERR_PKZIP_NO_EOCB;
+                }
+                else
+                    rc = VERR_PKZIP_NO_EOCB;
+            }
+            if (rc != VERR_PKZIP_NO_EOCB)
+                break;
+        }
+        else
+            rc = VERR_PKZIP_NO_EOCB;
+        /* overlap the following read */
+        off -= cbBuf - 4;
+    }
+
+    RTMemTmpFree(pu8Buf);
+
+    /*
+     * Now check for the presence of the Zip64 End of Central Directory Locator.
+     */
+    if (   RT_SUCCESS(rc)
+        && off > (unsigned)sizeof(RTZIPPKZIP64ENDOFCENTRDIRLOC))
+    {
+        off -= sizeof(RTZIPPKZIP64ENDOFCENTRDIRLOC);
+
+        RTZIPPKZIP64ENDOFCENTRDIRLOC eocd64loc;
+        rc = RTVfsIoStrmReadAt(pThis->hVfsIos, off, &eocd64loc, sizeof(eocd64loc), true /*fBlocking*/, NULL);
+        if (RT_SUCCESS(rc))
+        {
+            if (eocd64loc.u32Magic == RTZIPPKZIP64ENDOFCENTRDIRLOC_MAGIC)
+                pThis->PkzipReader.fZip64Eocd = true;
+        }
+    }
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtZipPkzipFssBaseObj_Close(void *pvThis)
+{
+    PRTZIPPKZIPBASEOBJ pThis = (PRTZIPPKZIPBASEOBJ)pvThis;
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtZipPkzipFssBaseObj_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+    PRTZIPPKZIPBASEOBJ pThis = (PRTZIPPKZIPBASEOBJ)pvThis;
+
+    /*
+     * Copy the desired data.
+     */
+    switch (enmAddAttr)
+    {
+        case RTFSOBJATTRADD_NOTHING:
+        case RTFSOBJATTRADD_UNIX:
+            *pObjInfo = pThis->ObjInfo;
+            break;
+
+        case RTFSOBJATTRADD_UNIX_OWNER:
+            *pObjInfo = pThis->ObjInfo;
+            break;
+
+        case RTFSOBJATTRADD_UNIX_GROUP:
+            *pObjInfo = pThis->ObjInfo;
+            break;
+
+        case RTFSOBJATTRADD_EASIZE:
+            *pObjInfo = pThis->ObjInfo;
+            break;
+
+        default:
+            return VERR_NOT_SUPPORTED;
+    }
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * PKZip filesystem base object operations (directory objects).
+ */
+static const RTVFSOBJOPS g_rtZipPkzipFssBaseObjOps =
+{
+    RTVFSOBJOPS_VERSION,
+    RTVFSOBJTYPE_BASE,
+    "PkzipFsStream::Obj",
+    rtZipPkzipFssBaseObj_Close,
+    rtZipPkzipFssBaseObj_QueryInfo,
+    RTVFSOBJOPS_VERSION
+};
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtZipPkzipFssIos_Close(void *pvThis)
+{
+    PRTZIPPKZIPIOSTREAM pThis = (PRTZIPPKZIPIOSTREAM)pvThis;
+
+    RTVfsIoStrmRelease(pThis->hVfsIos);
+    pThis->hVfsIos = NIL_RTVFSIOSTREAM;
+
+    if (pThis->pZip)
+    {
+        RTZipDecompDestroy(pThis->pZip);
+        pThis->pZip = NULL;
+    }
+
+    return rtZipPkzipFssBaseObj_Close(&pThis->BaseObj);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtZipPkzipFssIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+    PRTZIPPKZIPIOSTREAM pThis = (PRTZIPPKZIPIOSTREAM)pvThis;
+    return rtZipPkzipFssBaseObj_QueryInfo(&pThis->BaseObj, pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * Callback function for rtZipPkzipFssIos_Read. For feeding compressed data
+ * into the decompressor function.
+ */
+static DECLCALLBACK(int) rtZipPkzipFssIosReadHelper(void *pvThis, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+    PRTZIPPKZIPIOSTREAM pThis = (PRTZIPPKZIPIOSTREAM)pvThis;
+    int rc = VINF_SUCCESS;
+    if (!cbToRead)
+        return rc;
+    if (   pThis->fPassZipType
+        && cbToRead > 0)
+    {
+        uint8_t *pu8Buf = (uint8_t*)pvBuf;
+        pu8Buf[0] = pThis->enmZipType;
+        pvBuf = &pu8Buf[1];
+        cbToRead--;
+        pThis->fPassZipType = false;
+    }
+    if (cbToRead > 0)
+    {
+        size_t cbRead = 0;
+        const size_t cbAvail = pThis->cbComp;
+        rc = RTVfsIoStrmReadAt(pThis->hVfsIos, pThis->offComp, pvBuf,
+                               RT_MIN(cbToRead, cbAvail), true /*fBlocking*/, &cbRead);
+        if (   RT_SUCCESS(rc)
+            && cbToRead > cbAvail)
+            rc = pcbRead ? VINF_EOF : VERR_EOF;
+        if (   rc == VINF_EOF
+            && !pcbRead)
+            rc = VERR_EOF;
+        pThis->offComp += cbRead;
+        if (pcbRead)
+            *pcbRead = cbRead;
+    }
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
+ */
+static DECLCALLBACK(int) rtZipPkzipFssIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
+{
+    PRTZIPPKZIPIOSTREAM pThis = (PRTZIPPKZIPIOSTREAM)pvThis;
+    Assert(pSgBuf->cSegs == 1);
+
+    if (off < 0)
+        off = pThis->offFile;
+    if (off >= pThis->cbFile)
+        return pcbRead ? VINF_EOF : VERR_EOF;
+
+    Assert(pThis->cbFile >= pThis->offFile);
+    uint64_t cbLeft   = (uint64_t)(pThis->cbFile - pThis->offFile);
+    size_t   cbToRead = pSgBuf->paSegs[0].cbSeg;
+    if (cbToRead > cbLeft)
+    {
+        if (!pcbRead)
+            return VERR_EOF;
+        cbToRead = (size_t)cbLeft;
+    }
+
+    /*
+     * Restart decompression at start of stream or on backward seeking.
+     */
+    if (   !pThis->pZip
+        || !off
+        || off < pThis->offFile)
+    {
+        switch (pThis->enmCompMethod)
+        {
+            case RTZIPPKZIP_COMP_METHOD_STORED:
+                pThis->enmZipType = RTZIPTYPE_STORE;
+                break;
+
+            case RTZIPPKZIP_COMP_METHOD_DEFLATED:
+                pThis->enmZipType = RTZIPTYPE_ZLIB_NO_HEADER;
+                break;
+
+            default:
+                pThis->enmZipType = RTZIPTYPE_INVALID;
+                break;
+        }
+
+        if (pThis->pZip)
+        {
+            RTZipDecompDestroy(pThis->pZip);
+            pThis->pZip = NULL;
+        }
+        int rc = RTZipDecompCreate(&pThis->pZip, (void*)pThis, rtZipPkzipFssIosReadHelper);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+    /*
+     * Skip bytes if necessary.
+     */
+    if (off > pThis->offFile)
+    {
+        uint8_t u8Buf[_1K];
+        while (off > pThis->offFile)
+        {
+            size_t cbSkip = off - pThis->offFile;
+            if (cbSkip > sizeof(u8Buf))
+                cbSkip = sizeof(u8Buf);
+            int rc = RTZipDecompress(pThis->pZip, u8Buf, cbSkip, NULL);
+            if (RT_FAILURE(rc))
+                return rc;
+            pThis->offFile += cbSkip;
+        }
+    }
+
+    /*
+     * Do the actual reading.
+     */
+    size_t cbReadStack = 0;
+    if (!pcbRead)
+        pcbRead = &cbReadStack;
+    int rc = RTZipDecompress(pThis->pZip, pSgBuf->paSegs[0].pvSeg, cbToRead, pcbRead);
+    pThis->offFile = off + *pcbRead;
+    if (pThis->offFile >= pThis->cbFile)
+    {
+        Assert(pThis->offFile == pThis->cbFile);
+        pThis->fEndOfStream = true;
+    }
+
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
+ */
+static DECLCALLBACK(int) rtZipPkzipFssIos_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies,
+                                                  bool fIntr, uint32_t *pfRetEvents)
+{
+    PRTZIPPKZIPIOSTREAM pThis = (PRTZIPPKZIPIOSTREAM)pvThis;
+
+    /* When we've reached the end, read will be set to indicate it. */
+    if (   (fEvents & RTPOLL_EVT_READ)
+        && pThis->fEndOfStream)
+    {
+        int rc = RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, 0, fIntr, pfRetEvents);
+        if (RT_SUCCESS(rc))
+            *pfRetEvents |= RTPOLL_EVT_READ;
+        else
+            *pfRetEvents = RTPOLL_EVT_READ;
+        return VINF_SUCCESS;
+    }
+
+    return RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, cMillies, fIntr, pfRetEvents);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
+ */
+static DECLCALLBACK(int) rtZipPkzipFssIos_Tell(void *pvThis, PRTFOFF poffActual)
+{
+    PRTZIPPKZIPIOSTREAM pThis = (PRTZIPPKZIPIOSTREAM)pvThis;
+    *poffActual = pThis->offFile;
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Pkzip I/O object stream operations.
+ */
+static const RTVFSIOSTREAMOPS g_rtZipPkzipFssIosOps =
+{
+    { /* Obj */
+        RTVFSOBJOPS_VERSION,
+        RTVFSOBJTYPE_IO_STREAM,
+        "PkzipFsStream::IoStream",
+        rtZipPkzipFssIos_Close,
+        rtZipPkzipFssIos_QueryInfo,
+        RTVFSOBJOPS_VERSION
+    },
+    RTVFSIOSTREAMOPS_VERSION,
+    RTVFSIOSTREAMOPS_FEAT_NO_SG,
+    rtZipPkzipFssIos_Read,
+    NULL /*Write*/,
+    NULL /*Flush*/,
+    rtZipPkzipFssIos_PollOne,
+    rtZipPkzipFssIos_Tell,
+    NULL /*Skip*/,
+    NULL /*ZeroFill*/,
+    RTVFSIOSTREAMOPS_VERSION
+};
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtZipPkzipFss_Close(void *pvThis)
+{
+    PRTZIPPKZIPFSSTREAM pThis = (PRTZIPPKZIPFSSTREAM)pvThis;
+
+    RTVfsObjRelease(pThis->hVfsCurObj);
+    pThis->hVfsCurObj  = NIL_RTVFSOBJ;
+    pThis->pCurIosData = NULL;
+
+    RTVfsIoStrmRelease(pThis->hVfsIos);
+    pThis->hVfsIos = NIL_RTVFSIOSTREAM;
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtZipPkzipFss_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+    PRTZIPPKZIPFSSTREAM pThis = (PRTZIPPKZIPFSSTREAM)pvThis;
+    /* Take the lazy approach here, with the sideffect of providing some info
+       that is actually kind of useful. */
+    return RTVfsIoStrmQueryInfo(pThis->hVfsIos, pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFSSTREAMOPS,pfnNext}
+ */
+static DECLCALLBACK(int) rtZipPkzipFss_Next(void *pvThis, char **ppszName, RTVFSOBJTYPE *penmType, PRTVFSOBJ phVfsObj)
+{
+    PRTZIPPKZIPFSSTREAM pThis = (PRTZIPPKZIPFSSTREAM)pvThis;
+
+    /*
+     * Dispense with the current object.
+     */
+    if (pThis->hVfsCurObj != NIL_RTVFSOBJ)
+    {
+        if (pThis->pCurIosData)
+        {
+            pThis->pCurIosData->fEndOfStream = true;
+            pThis->pCurIosData->offFile      = pThis->pCurIosData->cbFile;
+            pThis->pCurIosData = NULL;
+        }
+
+        RTVfsObjRelease(pThis->hVfsCurObj);
+        pThis->hVfsCurObj = NIL_RTVFSOBJ;
+    }
+
+    /*
+     * Check if we've already reached the end in some way.
+     */
+    if (pThis->fEndOfStream)
+        return VERR_EOF;
+    if (pThis->rcFatal != VINF_SUCCESS)
+        return pThis->rcFatal;
+
+    int rc = VINF_SUCCESS;
+
+    /*
+     * Read the end of Central Directory Record once.
+     */
+    if (!pThis->PkzipReader.fHaveEocd)
+        rc = rtZipPkzipFssIosReadEocb(pThis);
+    RTFOFF offData = 0;
+
+    /*
+     * Parse the current Central Directory Header.
+     */
+    RTZIPPKZIP_COMP_METHOD enmCompMethod = RTZIPPKZIP_COMP_METHOD_STORED;
+    RTFOFF cbCompressed = 0;
+    if (RT_SUCCESS(rc))
+        rc = rtZipPkzipFssIosReadCdh(pThis, &offData, &enmCompMethod, &cbCompressed);
+    if (RT_FAILURE(rc))
+        return pThis->rcFatal = rc;
+
+    /*
+     * Fill an object info structure from the current Pkzip state.
+     */
+    RTFSOBJINFO Info;
+    rc = rtZipPkzipReaderGetFsObjInfo(&pThis->PkzipReader, &Info);
+    if (RT_FAILURE(rc))
+        return pThis->rcFatal = rc;
+
+    /*
+     * Create an object of the appropriate type.
+     */
+    RTVFSOBJTYPE enmType;
+    RTVFSOBJ hVfsObj;
+    RTFMODE fType = Info.Attr.fMode & RTFS_TYPE_MASK;
+    switch (fType)
+    {
+        case RTFS_TYPE_FILE:
+            RTVFSIOSTREAM hVfsIos;
+            PRTZIPPKZIPIOSTREAM pIosData;
+            rc = RTVfsNewIoStream(&g_rtZipPkzipFssIosOps,
+                                  sizeof(*pIosData),
+                                  RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
+                                  NIL_RTVFS,
+                                  NIL_RTVFSLOCK,
+                                  &hVfsIos,
+                                  (void **)&pIosData);
+            if (RT_FAILURE(rc))
+                return pThis->rcFatal = rc;
+
+            pIosData->BaseObj.pPkzipReader = &pThis->PkzipReader;
+            pIosData->BaseObj.ObjInfo      = Info;
+            pIosData->cbFile               = Info.cbObject;
+            pIosData->offFile              = 0;
+            pIosData->offComp              = offData;
+            pIosData->offCompStart         = offData;
+            pIosData->cbComp               = cbCompressed;
+            pIosData->enmCompMethod        = enmCompMethod;
+            pIosData->fPassZipType         = true;
+            pIosData->hVfsIos              = pThis->hVfsIos;
+            RTVfsIoStrmRetain(pThis->hVfsIos);
+            pThis->pCurIosData = pIosData;
+            enmType = RTVFSOBJTYPE_IO_STREAM;
+            hVfsObj = RTVfsObjFromIoStream(hVfsIos);
+            RTVfsIoStrmRelease(hVfsIos);
+            break;
+
+        case RTFS_TYPE_DIRECTORY:
+            PRTZIPPKZIPBASEOBJ pBaseObjData;
+            rc = RTVfsNewBaseObj(&g_rtZipPkzipFssBaseObjOps,
+                                 sizeof(*pBaseObjData),
+                                 NIL_RTVFS,
+                                 NIL_RTVFSLOCK,
+                                 &hVfsObj,
+                                 (void **)&pBaseObjData);
+            if (RT_FAILURE(rc))
+                return pThis->rcFatal = rc;
+
+            pBaseObjData->pPkzipReader = &pThis->PkzipReader;
+            pBaseObjData->ObjInfo      = Info;
+            enmType = RTVFSOBJTYPE_BASE;
+            break;
+
+        default:
+            return pThis->rcFatal = VERR_PKZIP_UNKNOWN_TYPE_FLAG;
+    }
+
+    if (ppszName)
+    {
+        rc = RTStrDupEx(ppszName, pThis->PkzipReader.szName);
+        if (RT_FAILURE(rc))
+            return pThis->rcFatal = rc;
+    }
+
+    if (phVfsObj)
+    {
+        RTVfsObjRetain(hVfsObj);
+        *phVfsObj = hVfsObj;
+    }
+
+    if (penmType)
+        *penmType = enmType;
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * PKZip filesystem stream operations.
+ */
+static const RTVFSFSSTREAMOPS rtZipPkzipFssOps =
+{
+    { /* Obj */
+        RTVFSOBJOPS_VERSION,
+        RTVFSOBJTYPE_FS_STREAM,
+        "PkzipFsStream",
+        rtZipPkzipFss_Close,
+        rtZipPkzipFss_QueryInfo,
+        RTVFSOBJOPS_VERSION
+    },
+    RTVFSFSSTREAMOPS_VERSION,
+    0,
+    rtZipPkzipFss_Next,
+    RTVFSFSSTREAMOPS_VERSION
+};
+
+
+RTDECL(int) RTZipPkzipFsStreamFromIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
+{
+    /*
+     * Input validation.
+     */
+    AssertPtrReturn(phVfsFss, VERR_INVALID_HANDLE);
+    *phVfsFss = NIL_RTVFSFSSTREAM;
+    AssertPtrReturn(hVfsIosIn, VERR_INVALID_HANDLE);
+    AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
+
+    uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosIn);
+    AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
+
+    /*
+     * Retain the input stream and create a new filesystem stream handle.
+     */
+    PRTZIPPKZIPFSSTREAM pThis;
+    RTVFSFSSTREAM     hVfsFss;
+    int rc = RTVfsNewFsStream(&rtZipPkzipFssOps, sizeof(*pThis),
+                              NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFss, (void **)&pThis);
+    if (RT_SUCCESS(rc))
+    {
+        pThis->hVfsIos              = hVfsIosIn;
+        pThis->hVfsCurObj           = NIL_RTVFSOBJ;
+        pThis->pCurIosData          = NULL;
+        pThis->fEndOfStream         = false;
+        pThis->rcFatal              = VINF_SUCCESS;
+        pThis->fHaveEocd            = false;
+
+        *phVfsFss = hVfsFss;
+        return VINF_SUCCESS;
+    }
+
+    RTVfsIoStrmRelease(hVfsIosIn);
+    return rc;
+}
Index: /trunk/src/VBox/Runtime/common/zip/unzipcmd.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/zip/unzipcmd.cpp	(revision 51696)
+++ /trunk/src/VBox/Runtime/common/zip/unzipcmd.cpp	(revision 51696)
@@ -0,0 +1,481 @@
+/* $Id$ */
+/** @file
+ * IPRT - A mini UNZIP Command.
+ */
+
+/*
+ * Copyright (C) 2014 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <iprt/zip.h>
+#include <iprt/asm.h>
+#include <iprt/getopt.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/vfs.h>
+#include <iprt/stream.h>
+
+
+/*******************************************************************************
+*   Defined Constants And Macros                                               *
+*******************************************************************************/
+
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+
+/**
+ * IPRT UNZIP option structure.
+ */
+typedef struct RTZIPUNZIPCMDOPS
+{
+    /** The operation. */
+    int            iOperation;
+    /** The long operation option name. */
+    const char     *pszOperation;
+    /** The directory to change into when upacking. */
+    const char     *pszDirectory;
+    /** The unzip file name. */
+    const char     *pszFile;
+    /** The number of files/directories to be extracted from archive specified. */
+    uint32_t       cFiles;
+    /** Wether we're verbose or quiet. */
+    bool           fVerbose;
+    /** Skip the restauration of the modification time for directories. */
+    bool           fNoModTimeDirectories;
+    /** Skip the restauration of the modification time for files. */
+    bool           fNoModTimeFiles;
+    /** Array of files/directories, terminated by a NULL entry. */
+    const char * const *papszFiles;
+} RTZIPUNZIPCMDOPS;
+/** Pointer to the IPRT tar options. */
+typedef RTZIPUNZIPCMDOPS *PRTZIPUNZIPCMDOPS;
+
+/**
+ * Callback used by rtZipTarDoWithMembers
+ *
+ * @returns rcExit or RTEXITCODE_FAILURE.
+ * @param   pOpts               The tar options.
+ * @param   hVfsObj             The tar object to display
+ * @param   pszName             The name.
+ * @param   rcExit              The current exit code.
+ */
+typedef RTEXITCODE (*PFNDOWITHMEMBER)(PRTZIPUNZIPCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit, PRTFOFF pcBytes);
+
+
+/**
+ *
+ */
+static RTEXITCODE rtZipUnzipCmdListCallback(PRTZIPUNZIPCMDOPS pOpts, RTVFSOBJ hVfsObj,
+                                            const char *pszName, RTEXITCODE rcExit, PRTFOFF pcBytes)
+{
+    /*
+     * Query all the information.
+     */
+    RTFSOBJINFO UnixInfo;
+    int rc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
+    if (RT_FAILURE(rc))
+        return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo returned %Rrc on '%s'", rc, pszName);
+
+    RTTIME time;
+    if (!RTTimeExplode(&time, &UnixInfo.ModificationTime))
+        return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot explode time on '%s'", pszName);
+
+    RTPrintf("%9RU64  %04d-%02d-%02d %02d:%02d   %s\n",
+             UnixInfo.cbObject,
+             time.i32Year, time.u8Month, time.u8MonthDay,
+             time.u8Hour, time.u8Minute,
+             pszName);
+
+    *pcBytes = UnixInfo.cbObject;
+    return rcExit;
+}
+
+
+/**
+ * Extracts a file.
+ */
+static RTEXITCODE rtZipUnzipCmdExtractFile(PRTZIPUNZIPCMDOPS pOpts, RTVFSOBJ hVfsObj, RTEXITCODE rcExit,
+                                           const char *pszDst, PCRTFSOBJINFO pUnixInfo)
+{
+    /*
+     * Open the destination file and create a stream object for it.
+     */
+    uint32_t fOpen = RTFILE_O_READWRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_ACCESS_ATTR_DEFAULT
+                   | ((RTFS_UNIX_IWUSR | RTFS_UNIX_IRUSR) << RTFILE_O_CREATE_MODE_SHIFT);
+    RTFILE hFile;
+    int rc = RTFileOpen(&hFile, pszDst, fOpen);
+    if (RT_FAILURE(rc))
+        return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating file: %Rrc", pszDst, rc);
+
+    RTVFSIOSTREAM hVfsIosDst;
+    rc = RTVfsIoStrmFromRTFile(hFile, fOpen, true /*fLeaveOpen*/, &hVfsIosDst);
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Pump the data thru.
+         */
+        RTVFSIOSTREAM hVfsIosSrc = RTVfsObjToIoStream(hVfsObj);
+        rc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, (uint32_t)RT_MIN(pUnixInfo->cbObject, _1M));
+        if (RT_SUCCESS(rc))
+        {
+            /*
+             * Correct the file mode and other attributes.
+             */
+            if (!pOpts->fNoModTimeFiles)
+            {
+                rc = RTFileSetTimes(hFile, NULL, &pUnixInfo->ModificationTime, NULL, NULL);
+                if (RT_FAILURE(rc))
+                    rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error setting times: %Rrc", pszDst, rc);
+            }
+        }
+        else
+            rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error writing out file: %Rrc", pszDst, rc);
+        RTVfsIoStrmRelease(hVfsIosSrc);
+        RTVfsIoStrmRelease(hVfsIosDst);
+    }
+    else
+        rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating I/O stream for file: %Rrc", pszDst, rc);
+
+    return rcExit;
+}
+
+
+/**
+ *
+ */
+static RTEXITCODE rtZipUnzipCmdExtractCallback(PRTZIPUNZIPCMDOPS pOpts, RTVFSOBJ hVfsObj,
+                                               const char *pszName, RTEXITCODE rcExit, PRTFOFF pcBytes)
+{
+    if (pOpts->fVerbose)
+        RTPrintf("%s\n", pszName);
+
+    /*
+     * Query all the information.
+     */
+    RTFSOBJINFO UnixInfo;
+    int rc = RTVfsObjQueryInfo(hVfsObj, &UnixInfo, RTFSOBJATTRADD_UNIX);
+    if (RT_FAILURE(rc))
+        return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo returned %Rrc on '%s'", rc, pszName);
+    
+    *pcBytes = UnixInfo.cbObject;
+
+    char szDst[RTPATH_MAX];
+    rc = RTPathJoin(szDst, sizeof(szDst), pOpts->pszDirectory ? pOpts->pszDirectory : ".", pszName);
+    if (RT_FAILURE(rc))
+        return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Failed to construct destination path for: %Rrc", pszName, rc);
+
+    /*
+     * Extract according to the type.
+     */
+    switch (UnixInfo.Attr.fMode & RTFS_TYPE_MASK)
+    {
+        case RTFS_TYPE_FILE:
+            return rtZipUnzipCmdExtractFile(pOpts, hVfsObj, rcExit, szDst, &UnixInfo);
+
+        case RTFS_TYPE_DIRECTORY:
+            rc = RTDirCreateFullPath(szDst, UnixInfo.Attr.fMode & RTFS_UNIX_ALL_ACCESS_PERMS);
+            if (RT_FAILURE(rc))
+                return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating directory: %Rrc", szDst, rc);
+            break;
+
+        default:
+            return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Unknown file type.", pszName);
+    }
+
+    if (!pOpts->fNoModTimeDirectories)
+    {
+        rc = RTPathSetTimesEx(szDst, NULL, &UnixInfo.ModificationTime, NULL, NULL, RTPATH_F_ON_LINK);
+        if (RT_FAILURE(rc) && rc != VERR_NOT_SUPPORTED && rc != VERR_NS_SYMLINK_SET_TIME)
+            rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error changing modification time: %Rrc.", pszName, rc);
+    }
+
+    return rcExit;
+}
+
+
+/**
+ * Checks if @a pszName is a member of @a papszNames, optionally returning the
+ * index.
+ *
+ * @returns true if the name is in the list, otherwise false.
+ * @param   pszName             The name to find.
+ * @param   papszNames          The array of names.
+ * @param   piName              Where to optionally return the array index.
+ */
+static bool rtZipUnzipCmdIsNameInArray(const char *pszName, const char * const *papszNames, uint32_t *piName)
+{
+    for (uint32_t iName = 0; papszNames[iName]; ++iName)
+        if (!strcmp(papszNames[iName], pszName))
+        {
+            if (piName)
+                *piName = iName;
+            return true;
+        }
+    return false;
+}
+
+
+/**
+ * Opens the input archive specified by the options.
+ *
+ * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
+ * @param   pOpts           The options.
+ * @param   phVfsFss        Where to return the UNZIP filesystem stream handle.
+ */
+static RTEXITCODE rtZipUnzipCmdOpenInputArchive(PRTZIPUNZIPCMDOPS pOpts, PRTVFSFSSTREAM phVfsFss)
+{
+    /*
+     * Open the input file.
+     */
+    RTVFSIOSTREAM hVfsIos;
+    const char    *pszError;
+    int rc = RTVfsChainOpenIoStream(pOpts->pszFile,
+                                    RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
+                                    &hVfsIos,
+                                    &pszError);
+    if (RT_FAILURE(rc))
+    {
+        if (pszError && *pszError)
+            return RTMsgErrorExit(RTEXITCODE_FAILURE,
+                                  "RTVfsChainOpenIoStream failed with rc=%Rrc:\n"
+                                  "    '%s'\n",
+                                  "     %*s^\n",
+                                  rc, pOpts->pszFile, pszError - pOpts->pszFile, "");
+        return RTMsgErrorExit(RTEXITCODE_FAILURE,
+                              "Failed with %Rrc opening the input archive '%s'", rc, pOpts->pszFile);
+    }
+
+    rc = RTZipPkzipFsStreamFromIoStream(hVfsIos, 0 /*fFlags*/, phVfsFss);
+    RTVfsIoStrmRelease(hVfsIos);
+    if (RT_FAILURE(rc))
+        return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open tar filesystem stream: %Rrc", rc);
+
+    return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Worker for the --list and --extract commands.
+ *
+ * @returns The appropriate exit code.
+ * @param   pOpts               The tar options.
+ * @param   pfnCallback         The command specific callback.
+ */
+static RTEXITCODE rtZipUnzipDoWithMembers(PRTZIPUNZIPCMDOPS pOpts, PFNDOWITHMEMBER pfnCallback,
+                                          uint32_t *pcFiles, PRTFOFF pcBytes)
+{
+    /*
+     * Allocate a bitmap to go with the file list.  This will be used to
+     * indicate which files we've processed and which not.
+     */
+    uint32_t *pbmFound = NULL;
+    if (pOpts->cFiles)
+    {
+        pbmFound = (uint32_t *)RTMemAllocZ(((pOpts->cFiles + 31) / 32) * sizeof(uint32_t));
+        if (!pbmFound)
+            return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to allocate the found-file-bitmap");
+    }
+
+    uint32_t cFiles = 0;
+    RTFOFF cBytesSum = 0;
+
+    /*
+     * Open the input archive.
+     */
+    RTVFSFSSTREAM hVfsFssIn;
+    RTEXITCODE rcExit = rtZipUnzipCmdOpenInputArchive(pOpts, &hVfsFssIn);
+    if (rcExit == RTEXITCODE_SUCCESS)
+    {
+        /*
+         * Process the stream.
+         */
+        for (;;)
+        {
+            /*
+             * Retrieve the next object.
+             */
+            char       *pszName;
+            RTVFSOBJ    hVfsObj;
+            int rc = RTVfsFsStrmNext(hVfsFssIn, &pszName, NULL, &hVfsObj);
+            if (RT_FAILURE(rc))
+            {
+                if (rc != VERR_EOF)
+                    rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsFsStrmNext returned %Rrc", rc);
+                break;
+            }
+
+            /*
+             * Should we process this object?
+             */
+            uint32_t iFile = UINT32_MAX;
+            if (   !pOpts->cFiles
+                || rtZipUnzipCmdIsNameInArray(pszName, pOpts->papszFiles, &iFile))
+            {
+                if (pbmFound)
+                    ASMBitSet(pbmFound, iFile);
+
+                RTFOFF cBytes = 0;
+                rcExit = pfnCallback(pOpts, hVfsObj, pszName, rcExit, &cBytes);
+
+                cBytesSum += cBytes;
+                cFiles++;
+            }
+
+            /*
+             * Release the current object and string.
+             */
+            RTVfsObjRelease(hVfsObj);
+            RTStrFree(pszName);
+        }
+
+        /*
+         * Complain about any files we didn't find.
+         */
+        for (uint32_t iFile = 0; iFile <pOpts->cFiles; iFile++)
+            if (!ASMBitTest(pbmFound, iFile))
+            {
+                RTMsgError("%s: Was not found in the archive", pOpts->papszFiles[iFile]);
+                rcExit = RTEXITCODE_FAILURE;
+            }
+
+        RTVfsFsStrmRelease(hVfsFssIn);
+    }
+
+    RTMemFree(pbmFound);
+
+    *pcFiles = cFiles;
+    *pcBytes = cBytesSum;
+
+    return RTEXITCODE_SUCCESS;
+}
+
+
+RTDECL(RTEXITCODE) RTZipUnzipCmd(unsigned cArgs, char **papszArgs)
+{
+    /*
+     * Parse the command line.
+     */
+    static const RTGETOPTDEF s_aOptions[] =
+    {
+        /* options */
+        { NULL,            'c', RTGETOPT_REQ_NOTHING }, /* extract files to stdout/stderr */
+        { NULL,            'd', RTGETOPT_REQ_STRING  }, /* extract files to this directory */
+        { NULL,            'l', RTGETOPT_REQ_NOTHING }, /* list archive files (short format) */
+        { NULL,            'p', RTGETOPT_REQ_NOTHING }, /* extract files to stdout */
+        { NULL,            't', RTGETOPT_REQ_NOTHING }, /* test archive files */
+        { NULL,            'v', RTGETOPT_REQ_NOTHING }, /* verbose */
+
+        /* modifiers */
+        { NULL,            'a', RTGETOPT_REQ_NOTHING }, /* convert text files */
+        { NULL,            'b', RTGETOPT_REQ_NOTHING }, /* no conversion, treat as binary */
+        { NULL,            'D', RTGETOPT_REQ_NOTHING }, /* don't restore timestamps for directories
+                                                           (and files) */
+    };
+
+    RTGETOPTSTATE GetState;
+    int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
+                          RTGETOPTINIT_FLAGS_OPTS_FIRST);
+    if (RT_FAILURE(rc))
+        return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOpt failed: %Rrc", rc);
+
+    RTZIPUNZIPCMDOPS Opts;
+    RT_ZERO(Opts);
+    
+    RTGETOPTUNION  ValueUnion;
+    while (   (rc = RTGetOpt(&GetState, &ValueUnion)) != 0
+           && rc != VINF_GETOPT_NOT_OPTION)
+    {
+        switch (rc)
+        {
+            case 'd':
+                if (Opts.pszDirectory)
+                    return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify -d once");
+                Opts.pszDirectory = ValueUnion.psz;
+                break;
+
+            case 'D':
+                if (!Opts.fNoModTimeDirectories)
+                    Opts.fNoModTimeDirectories = true; /* -D */
+                else
+                    Opts.fNoModTimeFiles = true; /* -DD */
+                break;
+
+            case 'l':
+            case 't': /* treat 'test' like 'list' */
+                if (Opts.iOperation)
+                    return RTMsgErrorExit(RTEXITCODE_SYNTAX,
+                                          "Conflicting unzip operation (%s already set, now %s)",
+                                          Opts.pszOperation, ValueUnion.pDef->pszLong);
+                Opts.iOperation   = rc;
+                Opts.pszOperation = ValueUnion.pDef->pszLong;
+                break;
+
+            case 'v':
+                Opts.fVerbose = true;
+                break;
+
+            default:
+                Opts.pszFile = ValueUnion.psz;
+                return RTGetOptPrintError(rc, &ValueUnion);
+        }
+    }
+
+    if (rc == VINF_GETOPT_NOT_OPTION)
+    {
+        Assert((unsigned)GetState.iNext - 1 <= cArgs);
+        Opts.pszFile = papszArgs[GetState.iNext - 1];
+        if ((unsigned)GetState.iNext <= cArgs)
+        {
+            Opts.papszFiles = (const char * const *)&papszArgs[GetState.iNext];
+            Opts.cFiles     = cArgs - GetState.iNext;
+        }
+    }
+
+    RTFOFF cBytes = 0;
+    uint32_t cFiles = 0;
+    switch (Opts.iOperation)
+    {
+        case 'l':
+        {
+            RTPrintf("  Length      Date    Time    Name\n"
+                     "---------  ---------- -----   ----\n");
+            RTEXITCODE rcExit = rtZipUnzipDoWithMembers(&Opts, rtZipUnzipCmdListCallback, &cFiles, &cBytes);
+            RTPrintf("---------                     -------\n"
+                     "%9RU64                     %u file%s\n",
+                     cBytes, cFiles, cFiles != 1 ? "s" : "");
+
+            return rcExit;
+        }
+
+        default:
+            return rtZipUnzipDoWithMembers(&Opts, rtZipUnzipCmdExtractCallback, &cFiles, &cBytes);
+    }
+
+    return RTEXITCODE_SUCCESS;
+}
Index: /trunk/src/VBox/Runtime/common/zip/zip.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/zip/zip.cpp	(revision 51695)
+++ /trunk/src/VBox/Runtime/common/zip/zip.cpp	(revision 51696)
@@ -420,4 +420,16 @@
 
 #ifdef RTZIP_USE_ZLIB
+
+/*
+ * Missing definitions from zutil.h. We need these constants for calling
+ * inflateInit2() / deflateInit2().
+ */
+# ifndef Z_DEF_WBITS
+#  define Z_DEF_WBITS        MAX_WBITS
+# endif
+# ifndef Z_DEF_MEM_LEVEL
+#  define Z_DEF_MEM_LEVEL    8
+# endif
+
 /**
  * Convert from zlib errno to iprt status code.
@@ -543,6 +555,7 @@
  * @param   pZip        The compressor instance.
  * @param   enmLevel    The desired compression level.
- */
-static DECLCALLBACK(int) rtZipZlibCompInit(PRTZIPCOMP pZip, RTZIPLEVEL enmLevel)
+ * @param   fZlibHeader If true, write the Zlib header.
+ */
+static DECLCALLBACK(int) rtZipZlibCompInit(PRTZIPCOMP pZip, RTZIPLEVEL enmLevel, bool fZlibHeader)
 {
     pZip->pfnCompress = rtZipZlibCompress;
@@ -564,5 +577,6 @@
     pZip->u.Zlib.opaque    = pZip;
 
-    int rc = deflateInit(&pZip->u.Zlib, iLevel);
+    int rc = deflateInit2(&pZip->u.Zlib, iLevel, Z_DEFLATED, fZlibHeader ? Z_DEF_WBITS : -Z_DEF_WBITS,
+                          Z_DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
     return rc >= 0 ? rc = VINF_SUCCESS : zipErrConvertFromZlib(rc, true /*fCompressing*/);
 }
@@ -636,6 +650,7 @@
  * @returns iprt status code.
  * @param   pZip        The decompressor instance.
- */
-static DECLCALLBACK(int) rtZipZlibDecompInit(PRTZIPDECOMP pZip)
+ * @param   fZlibHeader If true, expect the Zlib header.
+ */
+static DECLCALLBACK(int) rtZipZlibDecompInit(PRTZIPDECOMP pZip, bool fZlibHeader)
 {
     pZip->pfnDecompress = rtZipZlibDecompress;
@@ -645,5 +660,5 @@
     pZip->u.Zlib.opaque    = pZip;
 
-    int rc = inflateInit(&pZip->u.Zlib);
+    int rc = inflateInit2(&pZip->u.Zlib, fZlibHeader ? Z_DEF_WBITS : -Z_DEF_WBITS);
     return rc >= 0 ? VINF_SUCCESS : zipErrConvertFromZlib(rc, false /*fCompressing*/);
 }
@@ -1406,6 +1421,7 @@
 
         case RTZIPTYPE_ZLIB:
+        case RTZIPTYPE_ZLIB_NO_HEADER:
 #ifdef RTZIP_USE_ZLIB
-            rc = rtZipZlibCompInit(pZip, enmLevel);
+            rc = rtZipZlibCompInit(pZip, enmLevel, enmType == RTZIPTYPE_ZLIB /*fZlibHeader*/);
 #endif
             break;
@@ -1584,6 +1600,7 @@
 
         case RTZIPTYPE_ZLIB:
+        case RTZIPTYPE_ZLIB_NO_HEADER:
 #ifdef RTZIP_USE_ZLIB
-            rc = rtZipZlibDecompInit(pZip);
+            rc = rtZipZlibDecompInit(pZip, pZip->enmType == RTZIPTYPE_ZLIB /*fHeader*/);
 #else
             AssertMsgFailed(("Zlib is not include in this build!\n"));
Index: /trunk/src/VBox/Runtime/tools/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Runtime/tools/Makefile.kmk	(revision 51695)
+++ /trunk/src/VBox/Runtime/tools/Makefile.kmk	(revision 51696)
@@ -66,4 +66,9 @@
  RTTar_SOURCES = RTTar.cpp
 
+ # RTUnzip - our unzip clone (for testing the unzip streaming code)
+ PROGRAMS += RTUnzip
+ RTUnzip_TEMPLATE = VBoxR3Tool
+ RTUnzip_SOURCES = RTUnzip.cpp
+
  # RTNtDbgHelp - our tar clone (for testing the tar/gzip/gunzip streaming code)
  PROGRAMS.win += RTNtDbgHelp
Index: /trunk/src/VBox/Runtime/tools/RTUnzip.cpp
===================================================================
--- /trunk/src/VBox/Runtime/tools/RTUnzip.cpp	(revision 51696)
+++ /trunk/src/VBox/Runtime/tools/RTUnzip.cpp	(revision 51696)
@@ -0,0 +1,44 @@
+/* $Id$ */
+/** @file
+ * IPRT - TAR Utility.
+ */
+
+/*
+ * Copyright (C) 2010-2011 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <iprt/zip.h>
+#include <iprt/err.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+
+
+int main(int argc, char **argv)
+{
+    int rc = RTR3InitExe(argc, &argv, 0);
+    if (RT_FAILURE(rc))
+        return RTMsgInitFailure(rc);
+    return RTZipUnzipCmd(argc, argv);
+}
+
