pkzipvfs.cpp revision 96e8e3254d9f53263d49e275e71bbd028481ebc3
/* $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;
* 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/vfslowlevel.h>
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* PKZip Local File Header.
*/
#pragma pack(1)
typedef struct RTZIPPKZIPLOCALFILEHDR
{
/** Magic value, see RTZIPPKZIPLOCALFILEHDR_MAGIC. */
/** Minimum version needed to extract. */
/** General purpose bit flag. */
/** Compression method. See RTZIPPKZIP_COMP_METHOD_XXX. */
/** Last modified time, MS-DOS format: HHHHHMMM MMMSSSSS, multiply seconds by 2 */
/** Last modified date, MS-DOS format: YYYYYYYM MMMDDDDD, year starts at 1980 */
/** Checksum. */
/** Compressed size. */
/** Uncompressed size. */
/** Length of the file name. */
/** Length of the extra field. */
/** Start of the file name. */
#pragma pack()
/** Pointer to PKZip Local File Header. */
/**
* PKZip compression method.
*/
typedef enum RTZIPPKZIP_COMP_METHOD
{
/** No compression */
/** Shrunk */
/** Reduced with compression factor 1 */
/** Reduced with compression factor 2 */
/** Reduced with compression factor 3 */
/** Reduced with compression factor 4 */
/** Imploded */
/** Deflated */
/** Deflated64 */
/* Compressed using bzip2 */
/** Compressed using LZMA */
/**
* PKZip Central Directory Header.
*/
#pragma pack(1)
typedef struct RTZIPPKZIPCENTRDIRHDR
{
/** The magic value. See RTZIPPKZIPCENTRDIRHDR_MAGIC. */
/** The version used for creating the item. */
/** The minimum version required for extracting the item. */
/** General purpose flags. */
/** Compresstion method. See RTZIPPKZIP_COMP_METHOD_XXX */
/** Last modified time, MS-DOS format: HHHHHMMM MMMSSSSS, multiply seconds by 2 */
/** Last modified date, MS-DOS format: YYYYYYYM MMMDDDDD, year starts at 1980 */
/** Checksum. */
/** Compressed size. */
/** Uncompressed size. */
/** Length of the object file name. */
/** Length of the extra field. */
/** Length of the object comment. */
/** The number of the disk on which this file begins. */
/** Internal attributes. */
/** External attributes. */
/** Offset from the start of the first disk on which this file appears to
* where the local file header should be found. */
/** Start of the file name. */
#pragma pack()
/** Pointer to the PKZip Central Directory Header. */
typedef RTZIPPKZIPCENTRDIRHDR *PRTZIPPKZIPCENTRDIRHDR;
/**
* PKZip End of Central Directory Record.
*/
#pragma pack(1)
typedef struct RTZIPPKZIPENDOFCENTRDIRREC
{
/** The magic value. See RTZIPPKZIPENDOFCENTRDIRREC_MAGIC. */
/** Number of this disk. */
/** Number of the disk with the start of the Central Directory. */
/** Number of Central Directory entries on this disk. */
/** Number of Central Directory records. */
/** Size of the Central Directory in bytes. */
/** Offset of the Central Directory. */
/** Size of the comment in bytes. */
/** Start of the comment. */
#pragma pack()
/** Pointer to the PKZip End of Central Directory Record. */
typedef RTZIPPKZIPENDOFCENTRDIRREC const *PCRTZIPPKZIPENDOFCENTRDIRREC;
/**
* PKZip ZIP64 End of Central Directory Record.
*/
#pragma pack(1)
typedef struct RTZIPPKZIP64ENDOFCENTRDIRREC
{
/** The magic value. See RTZIPPKZIP64ENDOFCENTRDIRREC_MAGIC. */
/** Size of Zip64 end of Central Directory Record. */
/** The version used for creating the item. */
/** The minimum version required for extracting the item. */
/** Number of this disk. */
/** Number of the disk with the start of the Central Directory. */
/** Number of Central Directory entries on this disk. */
/** Number of Central Directory records. */
/** Size of the Central Directory in bytes. */
/** Offset of the Central Directory. */
#pragma pack()
/** Pointer to the 64-bit PKZip End of Central Directory Record. */
/**
* PKZip ZIP64 End of Central Directory Locator.
*/
#pragma pack(1)
typedef struct RTZIPPKZIP64ENDOFCENTRDIRLOC
{
/** The magic value. See RTZIPPKZIP64ENDOFCENTRDIRLOC_MAGIC. */
/** Number of the disk with the start of the ZIP64 End of Central Directory. */
/** Relative offset of the ZIP64 End of Central Directory Record. */
/** Total number of disks. */
#pragma pack()
/**
* PKZip ZIP64 Extended Information Extra Field.
*/
#pragma pack(1)
typedef struct RTZIPPKZIP64EXTRAFIELD
{
/** Uncompressed size. */
/** Compressed size. */
/** Offset from the start of the first disk on which this file appears to
* where the local file header should be found. */
/** The number of the disk on which this file begins. */
#pragma pack()
/** Pointer to the ZIP64 Extended Information Extra Field. */
/**
* PKZip reader instance data.
*/
typedef struct RTZIPPKZIPREADER
{
/** Set if we have the End of Central Directory record. */
bool fHaveEocd;
/** The Central Directory header. */
/** ZIP64 extended information. */
/** 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];
/** 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). */
/** The object info with unix attributes. */
/** 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. */
/** The number of (uncompressed) bytes in the file. */
/** The current file position at uncompressed file data. */
/** The start position of the compressed data in the hVfsIos. */
/** The current position for decompressing bytes in the hVfsIos. */
/** The number of compressed bytes starting at offCompStart. */
/** 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. */
/** Zip compression method. */
/** The decompressor instance. */
/** The input I/O stream. */
/** 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. */
/** The current object (referenced). */
/** Pointer to the private data if hVfsCurObj is representing a file. */
/** The offset of the first Central Directory header. */
/** The offset of the next Central Directory header. */
/** Size of the central directory. */
/** Current central directory entry. */
/** Number of central directory entries. */
/** 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. */
/** Pointer to a the private data of a PKZIP filesystem stream. */
typedef RTZIPPKZIPFSSTREAM *PRTZIPPKZIPFSSTREAM;
/**
*/
{
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)
{
return VERR_PKZIP_NAME_TOO_LONG;
return VINF_SUCCESS;
}
/**
* Parse the Central Directory Header.
*/
static int rtZipPkzipParseCentrDirHeader(PRTZIPPKZIPREADER pThis, PRTZIPPKZIPCENTRDIRHDR pCdh, size_t *pcbExtra)
{
return VERR_PKZIP_BAD_CDF_HEADER;
return VERR_PKZIP_NAME_TOO_LONG;
return VINF_SUCCESS;
}
/**
* Return the offset of the Local File Header.
*/
{
}
/**
* Return the uncompressed object size.
*/
{
}
/**
* Return the compressed object size.
*/
{
}
/**
* Parse the extra part of the Central Directory Header.
*/
{
int rc = RTStrCopyEx(pThis->szName, sizeof(pThis->szName), (const char*)pu8Buf, pThis->cdh.cbFilename);
if (RT_SUCCESS(rc))
{
while (cb >= 4)
{
pu8Buf += 2;
pu8Buf += 2;
cb -= 4;
{
switch (idExtra)
{
case 0x0001:
/*
* ZIP64 Extended Information Extra Field.
*/
if (!pThis->fZip64Eocd)
return VERR_PKZIP_ZIP64EX_IN_ZIP32;
/* Not all fields are really used. */
break;
default:
/* unknown, just skip */
break;
}
}
else
{
break;
}
}
}
return VINF_SUCCESS;
}
/**
* Translate a PKZip header to an IPRT object info structure.
*/
{
/*
* Zap the whole structure, this takes care of unused space in the union.
*/
| RTFS_UNIX_IRGRP \
else
| RTFS_UNIX_IRWXU \
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.
*/
{
if (cb < 4)
return false;
ssize_t i;
{
*piPos = i;
return true;
}
return false;
}
/**
* Read the Local File Header. We ignore the content -- we trust the Central
* Directory.
*/
{
if (RT_SUCCESS(rc))
{
{
if (RT_SUCCESS(rc))
{
/* Just skip the file name and and extra field. We use the data
* from the Central Directory Header. */
if (RT_SUCCESS(rc))
}
}
else
}
return rc;
}
/**
* Scan the current Central Directory Header.
*/
{
int rc;
{
if (RT_SUCCESS(rc))
{
pThis->iCentrDirEntry++;
if (RT_SUCCESS(rc))
{
{
/* extra data up to 64k */
if (pu8Buf)
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
}
}
else
rc = VERR_NO_MEMORY;
}
else
}
}
}
else
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).
*/
{
if (RT_FAILURE(rc))
return rc;
return VERR_PKZIP_NO_EOCB;
/* search for start of the 'end of Central Directory Record' */
if (!pu8Buf)
return VERR_NO_MEMORY;
/* maximum size of EOCD comment 2^16-1 */
{
if (RT_FAILURE(rc))
break;
int offMagic;
{
true /*fBlocking*/, NULL);
if (RT_SUCCESS(rc))
{
/* well, this shouldn't fail if the content didn't change */
{
/* sanity check */
{
pThis->iCentrDirEntry = 0;
}
else
}
else
}
if (rc != VERR_PKZIP_NO_EOCB)
break;
}
else
/* overlap the following read */
}
/*
* Now check for the presence of the Zip64 End of Central Directory Locator.
*/
if ( RT_SUCCESS(rc)
&& off > (unsigned)sizeof(RTZIPPKZIP64ENDOFCENTRDIRLOC))
{
off -= sizeof(RTZIPPKZIP64ENDOFCENTRDIRLOC);
rc = RTVfsIoStrmReadAt(pThis->hVfsIos, off, &eocd64loc, sizeof(eocd64loc), true /*fBlocking*/, NULL);
if (RT_SUCCESS(rc))
{
}
}
return rc;
}
/**
* @interface_method_impl{RTVFSOBJOPS,pfnClose}
*/
{
return VINF_SUCCESS;
}
/**
* @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
*/
static DECLCALLBACK(int) rtZipPkzipFssBaseObj_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
{
/*
* Copy the desired data.
*/
switch (enmAddAttr)
{
case RTFSOBJATTRADD_NOTHING:
case RTFSOBJATTRADD_UNIX:
break;
break;
break;
case RTFSOBJATTRADD_EASIZE:
break;
default:
return VERR_NOT_SUPPORTED;
}
return VINF_SUCCESS;
}
/**
* PKZip filesystem base object operations (directory objects).
*/
static const RTVFSOBJOPS g_rtZipPkzipFssBaseObjOps =
{
"PkzipFsStream::Obj",
};
/**
* @interface_method_impl{RTVFSOBJOPS,pfnClose}
*/
{
{
}
}
/**
* @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
*/
static DECLCALLBACK(int) rtZipPkzipFssIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD 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)
{
int rc = VINF_SUCCESS;
if (!cbToRead)
return rc;
if ( pThis->fPassZipType
&& cbToRead > 0)
{
cbToRead--;
pThis->fPassZipType = false;
}
if (cbToRead > 0)
{
if ( RT_SUCCESS(rc)
&& !pcbRead)
if (pcbRead)
}
return rc;
}
/**
* @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
*/
static DECLCALLBACK(int) rtZipPkzipFssIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
{
if (off < 0)
{
if (!pcbRead)
return VERR_EOF;
}
/*
* Restart decompression at start of stream or on backward seeking.
*/
|| !off
{
switch (pThis->enmCompMethod)
{
break;
break;
default:
break;
}
{
}
if (RT_FAILURE(rc))
return rc;
}
/*
* Skip bytes if necessary.
*/
{
{
if (RT_FAILURE(rc))
return rc;
}
}
/*
* Do the actual reading.
*/
size_t cbReadStack = 0;
if (!pcbRead)
pcbRead = &cbReadStack;
{
pThis->fEndOfStream = true;
}
return rc;
}
static DECLCALLBACK(int) rtZipPkzipFssIos_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcWritten)
{
return VERR_NOT_IMPLEMENTED;
}
{
return VERR_NOT_IMPLEMENTED;
}
/**
* @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
*/
static DECLCALLBACK(int) rtZipPkzipFssIos_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies,
{
/* When we've reached the end, read will be set to indicate it. */
if ( (fEvents & RTPOLL_EVT_READ)
&& pThis->fEndOfStream)
{
if (RT_SUCCESS(rc))
else
return VINF_SUCCESS;
}
}
/**
* @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
*/
{
return VINF_SUCCESS;
}
/**
* Pkzip I/O object stream operations.
*/
static const RTVFSIOSTREAMOPS g_rtZipPkzipFssIosOps =
{
{ /* Obj */
"PkzipFsStream::IoStream",
},
NULL /*Skip*/,
NULL /*ZeroFill*/,
};
/**
* @interface_method_impl{RTVFSOBJOPS,pfnClose}
*/
{
return VINF_SUCCESS;
}
/**
* @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
*/
static DECLCALLBACK(int) rtZipPkzipFss_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
{
/* Take the lazy approach here, with the sideffect of providing some info
that is actually kind of useful. */
}
/**
* @interface_method_impl{RTVFSFSSTREAMOPS,pfnNext}
*/
static DECLCALLBACK(int) rtZipPkzipFss_Next(void *pvThis, char **ppszName, RTVFSOBJTYPE *penmType, PRTVFSOBJ phVfsObj)
{
/*
* Dispense with the current object.
*/
{
if (pThis->pCurIosData)
{
}
}
/*
* Check if we've already reached the end in some way.
*/
if (pThis->fEndOfStream)
return VERR_EOF;
int rc = VINF_SUCCESS;
/*
* Read the end of Central Directory Record once.
*/
/*
* Parse the current Central Directory Header.
*/
uint64_t cbCompressed = 0;
if (RT_SUCCESS(rc))
if (RT_FAILURE(rc))
/*
* Fill an object info structure from the current Pkzip state.
*/
if (RT_FAILURE(rc))
/*
* Create an object of the appropriate type.
*/
switch (fType)
{
case RTFS_TYPE_FILE:
sizeof(*pIosData),
&hVfsIos,
(void **)&pIosData);
if (RT_FAILURE(rc))
pIosData->fPassZipType = true;
break;
case RTFS_TYPE_DIRECTORY:
sizeof(*pBaseObjData),
&hVfsObj,
(void **)&pBaseObjData);
if (RT_FAILURE(rc))
break;
default:
}
if (ppszName)
{
if (RT_FAILURE(rc))
}
if (phVfsObj)
{
}
if (penmType)
return VINF_SUCCESS;
}
/**
* PKZip filesystem stream operations.
*/
static const RTVFSFSSTREAMOPS rtZipPkzipFssOps =
{
{ /* Obj */
"PkzipFsStream",
},
0,
};
RTDECL(int) RTZipPkzipFsStreamFromIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
{
/*
* Input validation.
*/
/*
* Retain the input stream and create a new filesystem stream handle.
*/
if (RT_SUCCESS(rc))
{
pThis->fEndOfStream = false;
return VINF_SUCCESS;
}
return rc;
}