summaryrefslogtreecommitdiff
path: root/src/VBox/Runtime/common/zip
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/zip')
-rw-r--r--src/VBox/Runtime/common/zip/gzipvfs.cpp348
-rw-r--r--src/VBox/Runtime/common/zip/tar.cpp170
-rw-r--r--src/VBox/Runtime/common/zip/tarcmd.cpp745
-rw-r--r--src/VBox/Runtime/common/zip/tarvfs.cpp2
-rw-r--r--src/VBox/Runtime/common/zip/xarvfs.cpp2111
-rw-r--r--src/VBox/Runtime/common/zip/zip.cpp45
6 files changed, 3208 insertions, 213 deletions
diff --git a/src/VBox/Runtime/common/zip/gzipvfs.cpp b/src/VBox/Runtime/common/zip/gzipvfs.cpp
index d6b952ca..87efeb1b 100644
--- a/src/VBox/Runtime/common/zip/gzipvfs.cpp
+++ b/src/VBox/Runtime/common/zip/gzipvfs.cpp
@@ -56,6 +56,7 @@ PFNRT g_apfnRTZlibDeps[] =
};
#endif /* RT_OS_OS2 || RT_OS_SOLARIS || RT_OS_WINDOWS */
+
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
@@ -167,6 +168,12 @@ typedef struct RTZIPGZIPSTREAM
typedef RTZIPGZIPSTREAM *PRTZIPGZIPSTREAM;
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+static int rtZipGzip_FlushIt(PRTZIPGZIPSTREAM pThis, uint8_t fFlushType);
+
+
/**
* Convert from zlib to IPRT status codes.
*
@@ -223,11 +230,22 @@ static DECLCALLBACK(int) rtZipGzip_Close(void *pvThis)
int rc;
if (pThis->fDecompress)
+ {
rc = inflateEnd(&pThis->Zlib);
+ if (rc != Z_OK)
+ rc = rtZipGzipConvertErrFromZlib(pThis, rc);
+ }
else
- rc = deflateEnd(&pThis->Zlib);
- if (rc != Z_OK)
- rc = rtZipGzipConvertErrFromZlib(pThis, rc);
+ {
+ /* Flush the compression stream before terminating it. */
+ rc = VINF_SUCCESS;
+ if (!pThis->fFatalError)
+ rc = rtZipGzip_FlushIt(pThis, Z_FINISH);
+
+ int rc2 = deflateEnd(&pThis->Zlib);
+ if (RT_SUCCESS(rc) && rc2 != Z_OK)
+ rc = rtZipGzipConvertErrFromZlib(pThis, rc);
+ }
RTVfsIoStrmRelease(pThis->hVfsIos);
pThis->hVfsIos = NIL_RTVFSIOSTREAM;
@@ -349,44 +367,138 @@ static int rtZipGzip_ReadOneSeg(PRTZIPGZIPSTREAM pThis, void *pvBuf, size_t cbTo
return rc;
}
+
/**
* @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
*/
static DECLCALLBACK(int) rtZipGzip_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
{
PRTZIPGZIPSTREAM pThis = (PRTZIPGZIPSTREAM)pvThis;
- int rc;
+ Assert(pSgBuf->cSegs == 1);
AssertReturn(off == -1, VERR_INVALID_PARAMETER);
if (!pThis->fDecompress)
return VERR_ACCESS_DENIED;
- if (pSgBuf->cSegs == 1)
- rc = rtZipGzip_ReadOneSeg(pThis, pSgBuf->paSegs[0].pvSeg, pSgBuf->paSegs[0].cbSeg, fBlocking, pcbRead);
- else
+ return rtZipGzip_ReadOneSeg(pThis, pSgBuf->paSegs[0].pvSeg, pSgBuf->paSegs[0].cbSeg, fBlocking, pcbRead);
+}
+
+
+/**
+ * Internal helper for rtZipGzip_Write, rtZipGzip_Flush and rtZipGzip_Close.
+ *
+ * @returns IPRT status code.
+ * @retval VINF_SUCCESS
+ * @retval VINF_TRY_AGAIN - the only informational status.
+ * @retval VERR_INTERRUPTED - call again.
+ *
+ * @param pThis The gzip I/O stream instance data.
+ * @param fBlocking Whether to block or not.
+ */
+static int rtZipGzip_WriteOutputBuffer(PRTZIPGZIPSTREAM pThis, bool fBlocking)
+{
+ /*
+ * Anything to write? No, then just return immediately.
+ */
+ size_t cbToWrite = sizeof(pThis->abBuffer) - pThis->Zlib.avail_out;
+ if (cbToWrite == 0)
{
- rc = VINF_SUCCESS;
- size_t cbRead = 0;
- size_t cbReadSeg;
- size_t *pcbReadSeg = pcbRead ? &cbReadSeg : NULL;
- for (uint32_t iSeg = 0; iSeg < pSgBuf->cSegs; iSeg++)
+ Assert(pThis->Zlib.next_out == &pThis->abBuffer[0]);
+ return VINF_SUCCESS;
+ }
+ Assert(cbToWrite <= sizeof(pThis->abBuffer));
+
+ /*
+ * Loop write on VERR_INTERRUPTED.
+ *
+ * Note! Asserting a bit extra here to make sure the
+ * RTVfsIoStrmSgWrite works correctly.
+ */
+ int rc;
+ size_t cbWrittenOut;
+ for (;;)
+ {
+ /* Set up the buffer. */
+ pThis->SgSeg.cbSeg = cbToWrite;
+ Assert(pThis->SgSeg.pvSeg == &pThis->abBuffer[0]);
+ RTSgBufReset(&pThis->SgBuf);
+
+ cbWrittenOut = ~(size_t)0;
+ rc = RTVfsIoStrmSgWrite(pThis->hVfsIos, &pThis->SgBuf, fBlocking, &cbWrittenOut);
+ if (rc != VINF_SUCCESS)
{
- cbReadSeg = 0;
- rc = rtZipGzip_ReadOneSeg(pThis, pSgBuf->paSegs[iSeg].pvSeg, pSgBuf->paSegs[iSeg].cbSeg, fBlocking, pcbReadSeg);
- if (RT_FAILURE(rc))
- break;
- if (pcbRead)
+ AssertMsg(RT_FAILURE(rc) || rc == VINF_TRY_AGAIN, ("%Rrc\n", rc));
+ if (rc == VERR_INTERRUPTED)
{
- cbRead += cbReadSeg;
- if (cbReadSeg != pSgBuf->paSegs[iSeg].cbSeg)
- break;
+ Assert(cbWrittenOut == 0);
+ continue;
+ }
+ if (RT_FAILURE(rc) || rc == VINF_TRY_AGAIN || cbWrittenOut == 0)
+ {
+ AssertReturn(cbWrittenOut == 0, VERR_INTERNAL_ERROR_3);
+ AssertReturn(rc != VINF_SUCCESS, VERR_IPE_UNEXPECTED_INFO_STATUS);
+ return rc;
}
}
- if (pcbRead)
- *pcbRead = cbRead;
+ break;
}
+ AssertMsgReturn(cbWrittenOut > 0 && cbWrittenOut <= sizeof(pThis->abBuffer),
+ ("%zu %Rrc\n", cbWrittenOut, rc),
+ VERR_INTERNAL_ERROR_4);
- return rc;
+ /*
+ * Adjust the Zlib output buffer members.
+ */
+ if (cbWrittenOut == pThis->SgBuf.paSegs[0].cbSeg)
+ {
+ pThis->Zlib.avail_out = sizeof(pThis->abBuffer);
+ pThis->Zlib.next_out = &pThis->abBuffer[0];
+ }
+ else
+ {
+ Assert(cbWrittenOut <= pThis->SgBuf.paSegs[0].cbSeg);
+ size_t cbLeft = pThis->SgBuf.paSegs[0].cbSeg - cbWrittenOut;
+ memmove(&pThis->abBuffer[0], &pThis->abBuffer[cbWrittenOut], cbLeft);
+ pThis->Zlib.avail_out += (uInt)cbWrittenOut;
+ pThis->Zlib.next_out = &pThis->abBuffer[cbWrittenOut];
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Processes all available input.
+ *
+ * @returns IPRT status code.
+ *
+ * @param pThis The gzip I/O stream instance data.
+ * @param fBlocking Whether to block or not.
+ */
+static int rtZipGzip_CompressIt(PRTZIPGZIPSTREAM pThis, bool fBlocking)
+{
+ /*
+ * Processes all the intput currently lined up for us.
+ */
+ while (pThis->Zlib.avail_in > 0)
+ {
+ /* Make sure there is some space in the output buffer before calling
+ deflate() so we don't waste time filling up the corners. */
+ static const size_t s_cbFlushThreshold = 4096;
+ AssertCompile(sizeof(pThis->abBuffer) >= s_cbFlushThreshold * 4);
+ if (pThis->Zlib.avail_out < s_cbFlushThreshold)
+ {
+ int rc = rtZipGzip_WriteOutputBuffer(pThis, fBlocking);
+ if (rc != VINF_SUCCESS)
+ return rc;
+ Assert(pThis->Zlib.avail_out >= s_cbFlushThreshold);
+ }
+
+ int rcZlib = deflate(&pThis->Zlib, Z_NO_FLUSH);
+ if (rcZlib != Z_OK)
+ return rtZipGzipConvertErrFromZlib(pThis, rcZlib);
+ }
+ return VINF_SUCCESS;
}
@@ -396,16 +508,87 @@ static DECLCALLBACK(int) rtZipGzip_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgB
static DECLCALLBACK(int) rtZipGzip_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
{
PRTZIPGZIPSTREAM pThis = (PRTZIPGZIPSTREAM)pvThis;
- //int rc;
AssertReturn(off == -1, VERR_INVALID_PARAMETER);
- NOREF(fBlocking);
+ Assert(pSgBuf->cSegs == 1); NOREF(fBlocking);
+
if (pThis->fDecompress)
return VERR_ACCESS_DENIED;
- /** @todo implement compression. */
- NOREF(pSgBuf); NOREF(pcbWritten);
- return VERR_NOT_IMPLEMENTED;
+ /*
+ * Write out the intput buffer. Using a loop here because of potential
+ * integer type overflow since avail_in is uInt and cbSeg is size_t.
+ */
+ int rc = VINF_SUCCESS;
+ size_t cbWritten = 0;
+ uint8_t const *pbSrc = (uint8_t const *)pSgBuf->paSegs[0].pvSeg;
+ size_t cbLeft = pSgBuf->paSegs[0].cbSeg;
+ if (cbLeft > 0)
+ for (;;)
+ {
+ size_t cbThis = cbLeft < ~(uInt)0 ? cbLeft : ~(uInt)0 / 2;
+ pThis->Zlib.next_in = (Bytef * )pbSrc;
+ pThis->Zlib.avail_in = (uInt)cbThis;
+ rc = rtZipGzip_CompressIt(pThis, fBlocking);
+
+ Assert(cbThis >= pThis->Zlib.avail_in);
+ cbThis -= pThis->Zlib.avail_in;
+ cbWritten += cbThis;
+ if (cbLeft == cbThis || rc != VINF_SUCCESS)
+ break;
+ pbSrc += cbThis;
+ cbLeft -= cbThis;
+ }
+
+ if (pcbWritten)
+ *pcbWritten = cbWritten;
+ return rc;
+}
+
+
+/**
+ * Processes all available input.
+ *
+ * @returns IPRT status code.
+ *
+ * @param pThis The gzip I/O stream instance data.
+ * @param fFlushType The flush type to pass to deflate().
+ */
+static int rtZipGzip_FlushIt(PRTZIPGZIPSTREAM pThis, uint8_t fFlushType)
+{
+ /*
+ * Tell Zlib to flush until it stops producing more output.
+ */
+ int rc;
+ bool fMaybeMore = true;
+ for (;;)
+ {
+ /* Write the entire output buffer. */
+ do
+ {
+ rc = rtZipGzip_WriteOutputBuffer(pThis, true /*fBlocking*/);
+ if (RT_FAILURE(rc))
+ return rc;
+ Assert(rc == VINF_SUCCESS);
+ } while (pThis->Zlib.avail_out < sizeof(pThis->abBuffer));
+
+ if (!fMaybeMore)
+ return VINF_SUCCESS;
+
+ /* Do the flushing. */
+ pThis->Zlib.next_in = NULL;
+ pThis->Zlib.avail_in = 0;
+ int rcZlib = deflate(&pThis->Zlib, fFlushType);
+ if (rcZlib == Z_OK)
+ fMaybeMore = pThis->Zlib.avail_out < 64 || fFlushType == Z_FINISH;
+ else if (rcZlib == Z_STREAM_END)
+ fMaybeMore = false;
+ else
+ {
+ rtZipGzip_WriteOutputBuffer(pThis, true /*fBlocking*/);
+ return rtZipGzipConvertErrFromZlib(pThis, rcZlib);
+ }
+ }
}
@@ -415,6 +598,13 @@ static DECLCALLBACK(int) rtZipGzip_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSg
static DECLCALLBACK(int) rtZipGzip_Flush(void *pvThis)
{
PRTZIPGZIPSTREAM pThis = (PRTZIPGZIPSTREAM)pvThis;
+ if (!pThis->fDecompress)
+ {
+ int rc = rtZipGzip_FlushIt(pThis, Z_SYNC_FLUSH);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
return RTVfsIoStrmFlush(pThis->hVfsIos);
}
@@ -480,7 +670,7 @@ static RTVFSIOSTREAMOPS g_rtZipGzipOps =
RTVFSOBJOPS_VERSION
},
RTVFSIOSTREAMOPS_VERSION,
- 0,
+ RTVFSIOSTREAMOPS_FEAT_NO_SG,
rtZipGzip_Read,
rtZipGzip_Write,
rtZipGzip_Flush,
@@ -495,7 +685,7 @@ static RTVFSIOSTREAMOPS g_rtZipGzipOps =
RTDECL(int) RTZipGzipDecompressIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSIOSTREAM phVfsIosOut)
{
AssertPtrReturn(hVfsIosIn, VERR_INVALID_HANDLE);
- AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR), VERR_INVALID_PARAMETER);
AssertPtrReturn(phVfsIosOut, VERR_INVALID_POINTER);
uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosIn);
@@ -519,12 +709,15 @@ RTDECL(int) RTZipGzipDecompressIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags
memset(&pThis->Zlib, 0, sizeof(pThis->Zlib));
pThis->Zlib.opaque = pThis;
- rc = inflateInit2(&pThis->Zlib, MAX_WBITS + 16 /* autodetect gzip header */);
+ rc = inflateInit2(&pThis->Zlib,
+ fFlags & RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR
+ ? MAX_WBITS
+ : MAX_WBITS + 16 /* autodetect gzip header */);
if (rc >= 0)
{
/*
* Read the gzip header from the input stream to check that it's
- * a gzip stream.
+ * a gzip stream as specified by the user.
*
* Note!. Since we've told zlib to check for the gzip header, we
* prebuffer what we read in the input buffer so it can
@@ -535,23 +728,37 @@ RTDECL(int) RTZipGzipDecompressIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags
{
/* Validate the header and make a copy of it. */
PCRTZIPGZIPHDR pHdr = (PCRTZIPGZIPHDR)pThis->abBuffer;
- if ( pHdr->bId1 != RTZIPGZIPHDR_ID1
- || pHdr->bId2 != RTZIPGZIPHDR_ID2
- || pHdr->fFlags & ~RTZIPGZIPHDR_FLG_VALID_MASK)
- rc = VERR_ZIP_BAD_HEADER;
- else if (pHdr->bCompressionMethod != RTZIPGZIPHDR_CM_DEFLATE)
- rc = VERR_ZIP_UNSUPPORTED_METHOD;
+ if ( pHdr->bId1 == RTZIPGZIPHDR_ID1
+ && pHdr->bId2 == RTZIPGZIPHDR_ID2
+ && !(pHdr->fFlags & ~RTZIPGZIPHDR_FLG_VALID_MASK))
+ {
+ if (pHdr->bCompressionMethod == RTZIPGZIPHDR_CM_DEFLATE)
+ rc = VINF_SUCCESS;
+ else
+ rc = VERR_ZIP_UNSUPPORTED_METHOD;
+ }
+ else if ( (fFlags & RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR)
+ && (RT_MAKE_U16(pHdr->bId2, pHdr->bId1) % 31) == 0
+ && (pHdr->bId1 & 0xf) == RTZIPGZIPHDR_CM_DEFLATE )
+ {
+ pHdr = NULL;
+ rc = VINF_SUCCESS;
+ }
else
+ rc = VERR_ZIP_BAD_HEADER;
+ if (RT_SUCCESS(rc))
{
- pThis->Hdr = *pHdr;
pThis->Zlib.avail_in = sizeof(RTZIPGZIPHDR);
pThis->Zlib.next_in = &pThis->abBuffer[0];
-
- /* Parse on if there are names or comments. */
- if (pHdr->fFlags & (RTZIPGZIPHDR_FLG_NAME | RTZIPGZIPHDR_FLG_COMMENT))
+ if (pHdr)
{
- /** @todo Can implement this when someone needs the
- * name or comment for something useful. */
+ pThis->Hdr = *pHdr;
+ /* Parse on if there are names or comments. */
+ if (pHdr->fFlags & (RTZIPGZIPHDR_FLG_NAME | RTZIPGZIPHDR_FLG_COMMENT))
+ {
+ /** @todo Can implement this when someone needs the
+ * name or comment for something useful. */
+ }
}
if (RT_SUCCESS(rc))
{
@@ -570,3 +777,56 @@ RTDECL(int) RTZipGzipDecompressIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags
return rc;
}
+
+RTDECL(int) RTZipGzipCompressIoStream(RTVFSIOSTREAM hVfsIosDst, uint32_t fFlags, uint8_t uLevel, PRTVFSIOSTREAM phVfsIosZip)
+{
+ AssertPtrReturn(hVfsIosDst, VERR_INVALID_HANDLE);
+ AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(phVfsIosZip, VERR_INVALID_POINTER);
+ AssertReturn(uLevel > 0 && uLevel <= 9, VERR_INVALID_PARAMETER);
+
+ uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosDst);
+ AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
+
+ /*
+ * Create the compression I/O stream.
+ */
+ RTVFSIOSTREAM hVfsIos;
+ PRTZIPGZIPSTREAM pThis;
+ int rc = RTVfsNewIoStream(&g_rtZipGzipOps, sizeof(RTZIPGZIPSTREAM), RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
+ &hVfsIos, (void **)&pThis);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->hVfsIos = hVfsIosDst;
+ pThis->offStream = 0;
+ pThis->fDecompress = false;
+ pThis->SgSeg.pvSeg = &pThis->abBuffer[0];
+ pThis->SgSeg.cbSeg = sizeof(pThis->abBuffer);
+ RTSgBufInit(&pThis->SgBuf, &pThis->SgSeg, 1);
+
+ RT_ZERO(pThis->Zlib);
+ pThis->Zlib.opaque = pThis;
+ pThis->Zlib.next_out = &pThis->abBuffer[0];
+ pThis->Zlib.avail_out = sizeof(pThis->abBuffer);
+
+ rc = deflateInit2(&pThis->Zlib,
+ uLevel,
+ Z_DEFLATED,
+ 15 /* Windows Size */ + 16 /* GZIP header */,
+ 9 /* Max memory level for optimal speed */,
+ Z_DEFAULT_STRATEGY);
+
+ if (rc >= 0)
+ {
+ *phVfsIosZip = hVfsIos;
+ return VINF_SUCCESS;
+ }
+
+ rc = rtZipGzipConvertErrFromZlib(pThis, rc); /** @todo cleaning up in this situation is going to go wrong. */
+ RTVfsIoStrmRelease(hVfsIos);
+ }
+ else
+ RTVfsIoStrmRelease(hVfsIosDst);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/zip/tar.cpp b/src/VBox/Runtime/common/zip/tar.cpp
index cb342d23..e2ced7e6 100644
--- a/src/VBox/Runtime/common/zip/tar.cpp
+++ b/src/VBox/Runtime/common/zip/tar.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2009-2010 Oracle Corporation
+ * Copyright (C) 2009-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -40,6 +40,7 @@
#include <iprt/string.h>
#include "internal/magics.h"
+#include "tar.h"
/******************************************************************************
@@ -85,19 +86,10 @@ typedef union RTTARRECORD
} RTTARRECORD;
AssertCompileSize(RTTARRECORD, 512);
AssertCompileMemberOffset(RTTARRECORD, h.size, 100+8*3);
+AssertCompileMemberSize(RTTARRECORD, h.name, RTTAR_NAME_MAX+1);
/** Pointer to a tar file header. */
typedef RTTARRECORD *PRTTARRECORD;
-
-#if 0 /* not currently used */
-typedef struct RTTARFILELIST
-{
- char *pszFilename;
- RTTARFILELIST *pNext;
-} RTTARFILELIST;
-typedef RTTARFILELIST *PRTTARFILELIST;
-#endif
-
/** Pointer to a tar file handle. */
typedef struct RTTARFILEINTERNAL *PRTTARFILEINTERNAL;
@@ -144,10 +136,22 @@ typedef struct RTTARFILEINTERNAL
uint64_t offCurrent;
/** The open mode. */
uint32_t fOpenMode;
+ /** The link flag. */
+ char linkflag;
} RTTARFILEINTERNAL;
/** Pointer to the internal data of a tar file. */
typedef RTTARFILEINTERNAL *PRTTARFILEINTERNAL;
+#if 0 /* not currently used */
+typedef struct RTTARFILELIST
+{
+ char *pszFilename;
+ RTTARFILELIST *pNext;
+} RTTARFILELIST;
+typedef RTTARFILELIST *PRTTARFILELIST;
+#endif
+
+
/******************************************************************************
* Defined Constants And Macros *
@@ -265,29 +269,59 @@ DECLINLINE(uint64_t) rtTarRecToSize(PRTTARRECORD pRecord)
return (uint64_t)cbSize;
}
-DECLINLINE(int) rtTarCalcChkSum(PRTTARRECORD pRecord, uint32_t *pChkSum)
+/**
+ * Calculates the TAR header checksums and detects if it's all zeros.
+ *
+ * @returns true if all zeros, false if not.
+ * @param pHdr The header to checksum.
+ * @param pi32Unsigned Where to store the checksum calculated using
+ * unsigned chars. This is the one POSIX
+ * specifies.
+ * @param pi32Signed Where to store the checksum calculated using
+ * signed chars.
+ *
+ * @remarks The reason why we calculate the checksum as both signed and unsigned
+ * has to do with various the char C type being signed on some hosts
+ * and unsigned on others.
+ *
+ * @remarks Borrowed from tarvfs.cpp.
+ */
+static bool rtZipTarCalcChkSum(PCRTZIPTARHDR pHdr, int32_t *pi32Unsigned, int32_t *pi32Signed)
{
- uint32_t check = 0;
- uint32_t zero = 0;
- for (size_t i = 0; i < sizeof(RTTARRECORD); ++i)
+ int32_t i32Unsigned = 0;
+ int32_t i32Signed = 0;
+
+ /*
+ * Sum up the entire header.
+ */
+ const char *pch = (const char *)pHdr;
+ const char *pchEnd = pch + sizeof(*pHdr);
+ do
{
- /* Calculate the sum of every byte from the header. The checksum field
- * itself is counted as all blanks. */
- if ( i < RT_UOFFSETOF(RTTARRECORD, h.chksum)
- || i >= RT_UOFFSETOF(RTTARRECORD, h.linkflag))
- check += pRecord->d[i];
- else
- check += ' ';
- /* Additional check if all fields are zero, which indicate EOF. */
- zero += pRecord->d[i];
- }
+ i32Unsigned += *(unsigned char *)pch;
+ i32Signed += *(signed char *)pch;
+ } while (++pch != pchEnd);
- /* EOF? */
- if (!zero)
- return VERR_TAR_END_OF_FILE;
+ /*
+ * Check if it's all zeros and replace the chksum field with spaces.
+ */
+ bool const fZeroHdr = i32Unsigned == 0;
- *pChkSum = check;
- return VINF_SUCCESS;
+ pch = pHdr->Common.chksum;
+ pchEnd = pch + sizeof(pHdr->Common.chksum);
+ do
+ {
+ i32Unsigned -= *(unsigned char *)pch;
+ i32Signed -= *(signed char *)pch;
+ } while (++pch != pchEnd);
+
+ i32Unsigned += (unsigned char)' ' * sizeof(pHdr->Common.chksum);
+ i32Signed += (signed char)' ' * sizeof(pHdr->Common.chksum);
+
+ *pi32Unsigned = i32Unsigned;
+ if (pi32Signed)
+ *pi32Signed = i32Signed;
+ return fZeroHdr;
}
DECLINLINE(int) rtTarReadHeaderRecord(RTFILE hFile, PRTTARRECORD pRecord)
@@ -302,16 +336,16 @@ DECLINLINE(int) rtTarReadHeaderRecord(RTFILE hFile, PRTTARRECORD pRecord)
return rc;
/* Check for data integrity & an EOF record */
- uint32_t check = 0;
- rc = rtTarCalcChkSum(pRecord, &check);
- /* EOF? */
- if (RT_FAILURE(rc))
- return rc;
+ int32_t iUnsignedChksum, iSignedChksum;
+ if (rtZipTarCalcChkSum((PCRTZIPTARHDR)pRecord, &iUnsignedChksum, &iSignedChksum))
+ return VERR_TAR_END_OF_FILE;
/* Verify the checksum */
uint32_t sum;
rc = RTStrToUInt32Full(pRecord->h.chksum, 8, &sum);
- if (RT_SUCCESS(rc) && sum == check)
+ if ( RT_SUCCESS(rc)
+ && ( sum == (uint32_t)iSignedChksum
+ || sum == (uint32_t)iUnsignedChksum) )
{
/* Make sure the strings are zero terminated. */
pRecord->h.name[sizeof(pRecord->h.name) - 1] = 0;
@@ -332,24 +366,27 @@ DECLINLINE(int) rtTarCreateHeaderRecord(PRTTARRECORD pRecord, const char *pszSrc
/** @todo check for field overflows. */
/* Fill the header record */
// RT_ZERO(pRecord); - done by the caller.
- RTStrPrintf(pRecord->h.name, sizeof(pRecord->h.name), "%s", pszSrcName);
- RTStrPrintf(pRecord->h.mode, sizeof(pRecord->h.mode), "%0.7o", fmode);
- RTStrPrintf(pRecord->h.uid, sizeof(pRecord->h.uid), "%0.7o", uid);
- RTStrPrintf(pRecord->h.gid, sizeof(pRecord->h.gid), "%0.7o", gid);
+ /** @todo use RTStrCopy */
+ size_t cb = RTStrPrintf(pRecord->h.name, sizeof(pRecord->h.name), "%s", pszSrcName);
+ if (cb < strlen(pszSrcName))
+ return VERR_BUFFER_OVERFLOW;
+ RTStrPrintf(pRecord->h.mode, sizeof(pRecord->h.mode), "%0.7o", fmode);
+ RTStrPrintf(pRecord->h.uid, sizeof(pRecord->h.uid), "%0.7o", uid);
+ RTStrPrintf(pRecord->h.gid, sizeof(pRecord->h.gid), "%0.7o", gid);
rtTarSizeToRec(pRecord, cbSize);
- RTStrPrintf(pRecord->h.mtime, sizeof(pRecord->h.mtime), "%0.11o", mtime);
+ RTStrPrintf(pRecord->h.mtime, sizeof(pRecord->h.mtime), "%0.11llo", mtime);
RTStrPrintf(pRecord->h.magic, sizeof(pRecord->h.magic), "ustar ");
RTStrPrintf(pRecord->h.uname, sizeof(pRecord->h.uname), "someone");
RTStrPrintf(pRecord->h.gname, sizeof(pRecord->h.gname), "someone");
pRecord->h.linkflag = LF_NORMAL;
/* Create the checksum out of the new header */
- uint32_t uChkSum = 0;
- int rc = rtTarCalcChkSum(pRecord, &uChkSum);
- if (RT_FAILURE(rc))
- return rc;
+ int32_t iUnsignedChksum, iSignedChksum;
+ if (rtZipTarCalcChkSum((PCRTZIPTARHDR)pRecord, &iUnsignedChksum, &iSignedChksum))
+ return VERR_TAR_END_OF_FILE;
+
/* Format the checksum */
- RTStrPrintf(pRecord->h.chksum, sizeof(pRecord->h.chksum), "%0.7o", uChkSum);
+ RTStrPrintf(pRecord->h.chksum, sizeof(pRecord->h.chksum), "%0.7o", iUnsignedChksum);
return VINF_SUCCESS;
}
@@ -1178,7 +1215,7 @@ RTR3DECL(int) RTTarFileSetTime(RTTARFILE hFile, PRTTIMESPEC pTime)
/* Convert the time to an string. */
char szModTime[RT_SIZEOFMEMB(RTTARRECORD, h.mtime)];
- RTStrPrintf(szModTime, sizeof(szModTime), "%0.11o", RTTimeSpecGetSeconds(pTime));
+ RTStrPrintf(szModTime, sizeof(szModTime), "%0.11llo", RTTimeSpecGetSeconds(pTime));
/* Write it directly into the header */
return RTFileWriteAt(pFileInt->pTar->hTarFile,
@@ -1617,14 +1654,23 @@ RTR3DECL(int) RTTarSeekNextFile(RTTAR hTar)
* If not we are somehow busted. */
uint64_t offCur = RTFileTell(pInt->hTarFile);
if (!( pInt->pFileCache->offStart <= offCur
- && offCur < pInt->pFileCache->offStart + sizeof(RTTARRECORD) + pInt->pFileCache->cbSize))
+ && offCur <= pInt->pFileCache->offStart + sizeof(RTTARRECORD) + pInt->pFileCache->cbSize))
return VERR_INVALID_STATE;
/* Seek to the next file header. */
uint64_t offNext = RT_ALIGN(pInt->pFileCache->offStart + sizeof(RTTARRECORD) + pInt->pFileCache->cbSize, sizeof(RTTARRECORD));
- rc = RTFileSeek(pInt->hTarFile, offNext - offCur, RTFILE_SEEK_CURRENT, NULL);
- if (RT_FAILURE(rc))
- return rc;
+ if (pInt->pFileCache->cbSize != 0)
+ {
+ rc = RTFileSeek(pInt->hTarFile, offNext - offCur, RTFILE_SEEK_CURRENT, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ else
+ {
+ /* Else delete the last open file cache. Might be recreated below. */
+ rtDeleteTarFileInternal(pInt->pFileCache);
+ pInt->pFileCache = NULL;
+ }
/* Again check the current filename to fill the cache with the new value. */
return RTTarCurrentFile(hTar, NULL);
@@ -1648,20 +1694,24 @@ RTR3DECL(int) RTTarFileOpenCurrentFile(RTTAR hTar, PRTTARFILE phFile, char **pps
/* Is there some cached entry? */
if (pInt->pFileCache)
{
- /* Are we still direct behind that header? */
- if (pInt->pFileCache->offStart + sizeof(RTTARRECORD) == RTFileTell(pInt->hTarFile))
+ if (pInt->pFileCache->offStart + sizeof(RTTARRECORD) < RTFileTell(pInt->hTarFile))
+ {
+ /* Else delete the last open file cache. Might be recreated below. */
+ rtDeleteTarFileInternal(pInt->pFileCache);
+ pInt->pFileCache = NULL;
+ }
+ else/* Are we still directly behind that header? */
{
/* Yes, so the streaming can start. Just return the cached file
* structure to the caller. */
*phFile = rtCopyTarFileInternal(pInt->pFileCache);
if (ppszFilename)
*ppszFilename = RTStrDup(pInt->pFileCache->pszFilename);
+ if (pInt->pFileCache->linkflag == LF_DIR)
+ return VINF_TAR_DIR_PATH;
return VINF_SUCCESS;
}
- /* Else delete the last open file cache. Might be recreated below. */
- rtDeleteTarFileInternal(pInt->pFileCache);
- pInt->pFileCache = NULL;
}
PRTTARFILEINTERNAL pFileInt = NULL;
@@ -1679,7 +1729,8 @@ RTR3DECL(int) RTTarFileOpenCurrentFile(RTTAR hTar, PRTTARFILE phFile, char **pps
/* We support normal files only */
if ( record.h.linkflag == LF_OLDNORMAL
- || record.h.linkflag == LF_NORMAL)
+ || record.h.linkflag == LF_NORMAL
+ || record.h.linkflag == LF_DIR)
{
pFileInt = rtCreateTarFileInternal(pInt, record.h.name, fOpen);
if (!pFileInt)
@@ -1692,11 +1743,16 @@ RTR3DECL(int) RTTarFileOpenCurrentFile(RTTAR hTar, PRTTARFILE phFile, char **pps
pFileInt->cbSize = rtTarRecToSize(&record);
/* The start is -512 from here. */
pFileInt->offStart = RTFileTell(pInt->hTarFile) - sizeof(RTTARRECORD);
+ /* remember the type of a file */
+ pFileInt->linkflag = record.h.linkflag;
/* Copy the new file structure to our cache. */
pInt->pFileCache = rtCopyTarFileInternal(pFileInt);
if (ppszFilename)
*ppszFilename = RTStrDup(pFileInt->pszFilename);
+
+ if (pFileInt->linkflag == LF_DIR)
+ rc = VINF_TAR_DIR_PATH;
}
} while (0);
diff --git a/src/VBox/Runtime/common/zip/tarcmd.cpp b/src/VBox/Runtime/common/zip/tarcmd.cpp
index 6e7f84a9..b018b283 100644
--- a/src/VBox/Runtime/common/zip/tarcmd.cpp
+++ b/src/VBox/Runtime/common/zip/tarcmd.cpp
@@ -1,10 +1,10 @@
/* $Id: tarcmd.cpp $ */
/** @file
- * IPRT - TAR Command.
+ * IPRT - A mini TAR Command.
*/
/*
- * Copyright (C) 2010 Oracle Corporation
+ * Copyright (C) 2010-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -33,35 +33,58 @@
#include <iprt/asm.h>
#include <iprt/buildconfig.h>
#include <iprt/ctype.h>
+#include <iprt/dir.h>
#include <iprt/file.h>
#include <iprt/getopt.h>
#include <iprt/initterm.h>
#include <iprt/mem.h>
#include <iprt/message.h>
#include <iprt/param.h>
+#include <iprt/path.h>
#include <iprt/stream.h>
#include <iprt/string.h>
+#include <iprt/symlink.h>
#include <iprt/vfs.h>
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
-#define RTZIPTARCMD_OPT_DELETE 1000
-#define RTZIPTARCMD_OPT_OWNER 1001
-#define RTZIPTARCMD_OPT_GROUP 1002
-#define RTZIPTARCMD_OPT_UTC 1003
-#define RTZIPTARCMD_OPT_PREFIX 1004
+#define RTZIPTARCMD_OPT_DELETE 1000
+#define RTZIPTARCMD_OPT_OWNER 1001
+#define RTZIPTARCMD_OPT_GROUP 1002
+#define RTZIPTARCMD_OPT_UTC 1003
+#define RTZIPTARCMD_OPT_PREFIX 1004
+#define RTZIPTARCMD_OPT_FILE_MODE_AND_MASK 1005
+#define RTZIPTARCMD_OPT_FILE_MODE_OR_MASK 1006
+#define RTZIPTARCMD_OPT_DIR_MODE_AND_MASK 1007
+#define RTZIPTARCMD_OPT_DIR_MODE_OR_MASK 1008
+#define RTZIPTARCMD_OPT_FORMAT 1009
+
+/** File format. */
+typedef enum RTZIPTARFORMAT
+{
+ RTZIPTARFORMAT_INVALID = 0,
+ /** Autodetect if possible, defaulting to TAR. */
+ RTZIPTARFORMAT_AUTO_DEFAULT,
+ /** TAR. */
+ RTZIPTARFORMAT_TAR,
+ /** XAR. */
+ RTZIPTARFORMAT_XAR
+} RTZIPTARFORMAT;
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
- * IPT TAR option structure.
+ * IPRT TAR option structure.
*/
typedef struct RTZIPTARCMDOPS
{
+ /** The file format. */
+ RTZIPTARFORMAT enmFormat;
+
/** The operation (Acdrtux or RTZIPTARCMD_OPT_DELETE). */
int iOperation;
/** The long operation option name. */
@@ -73,21 +96,36 @@ typedef struct RTZIPTARCMDOPS
const char *pszFile;
/** Whether we're verbose or quiet. */
bool fVerbose;
- /** Whether to preserve permissions when restoring. */
- bool fPreservePermissions;
+ /** Whether to preserve the original file owner when restoring. */
+ bool fPreserveOwner;
+ /** Whether to preserve the original file group when restoring. */
+ bool fPreserveGroup;
+ /** Whether to skip restoring the modification time (only time stored by the
+ * traditional TAR format). */
+ bool fNoModTime;
/** The compressor/decompressor method to employ (0, z or j). */
char chZipper;
- /** The owner to set. */
+ /** The owner to set. NULL if not applicable.
+ * Always resolved into uidOwner for extraction. */
const char *pszOwner;
- /** The owner ID to set when unpacking if pszOwner is not NULL. */
+ /** The owner ID to set. NIL_RTUID if not applicable. */
RTUID uidOwner;
- /** The group to set. */
+ /** The group to set. NULL if not applicable.
+ * Always resolved into gidGroup for extraction. */
const char *pszGroup;
- /** The group ID to set when unpacking if pszGroup is not NULL. */
+ /** The group ID to set. NIL_RTGUID if not applicable. */
RTGID gidGroup;
/** Display the modification times in UTC instead of local time. */
bool fDisplayUtc;
+ /** File mode AND mask. */
+ RTFMODE fFileModeAndMask;
+ /** File mode OR mask. */
+ RTFMODE fFileModeOrMask;
+ /** Directory mode AND mask. */
+ RTFMODE fDirModeAndMask;
+ /** Directory mode OR mask. */
+ RTFMODE fDirModeOrMask;
/** What to prefix all names with when creating, adding, whatever. */
const char *pszPrefix;
@@ -101,6 +139,17 @@ typedef struct RTZIPTARCMDOPS
/** Pointer to the IPRT tar options. */
typedef RTZIPTARCMDOPS *PRTZIPTARCMDOPS;
+/**
+ * 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)(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit);
+
/**
* Checks if @a pszName is a member of @a papszNames, optionally returning the
@@ -213,9 +262,18 @@ static RTEXITCODE rtZipTarCmdOpenInputArchive(PRTZIPTARCMDOPS pOpts, PRTVFSFSSTR
}
/*
- * Open the tar filesystem stream.
+ * Open the filesystem stream.
*/
- rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
+ if (pOpts->enmFormat == RTZIPTARFORMAT_TAR)
+ rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
+ else if (pOpts->enmFormat == RTZIPTARFORMAT_XAR)
+#ifdef IPRT_WITH_XAR /* Requires C++ and XML, so only in some configruation of IPRT. */
+ rc = RTZipXarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
+#else
+ rc = VERR_NOT_SUPPORTED;
+#endif
+ else /** @todo make RTZipTarFsStreamFromIoStream fail if not tar file! */
+ rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
RTVfsIoStrmRelease(hVfsIos);
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open tar filesystem stream: %Rrc", rc);
@@ -225,17 +283,424 @@ static RTEXITCODE rtZipTarCmdOpenInputArchive(PRTZIPTARCMDOPS pOpts, PRTVFSFSSTR
/**
- * Display a tar entry in the verbose form.
+ * 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 rtZipTarDoWithMembers(PRTZIPTARCMDOPS pOpts, PFNDOWITHMEMBER pfnCallback)
+{
+ /*
+ * 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");
+ }
+
+
+ /*
+ * Open the input archive.
+ */
+ RTVFSFSSTREAM hVfsFssIn;
+ RTEXITCODE rcExit = rtZipTarCmdOpenInputArchive(pOpts, &hVfsFssIn);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ /*
+ * Process the stream.
+ */
+ for (;;)
+ {
+ /*
+ * Retrive 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 entry?
+ */
+ uint32_t iFile = UINT32_MAX;
+ if ( !pOpts->cFiles
+ || rtZipTarCmdIsNameInArray(pszName, pOpts->papszFiles, &iFile) )
+ {
+ if (pbmFound)
+ ASMBitSet(pbmFound, iFile);
+
+ rcExit = pfnCallback(pOpts, hVfsObj, pszName, rcExit);
+ }
+
+ /*
+ * 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;
+ }
+ }
+ RTMemFree(pbmFound);
+ return rcExit;
+}
+
+
+/**
+ * Checks if the name contains any escape sequences.
+ *
+ * An escape sequence would generally be one or more '..' references. On DOS
+ * like system, something that would make up a drive letter reference is also
+ * considered an escape sequence.
+ *
+ * @returns true / false.
+ * @param pszName The name to consider.
+ */
+static bool rtZipTarHasEscapeSequence(const char *pszName)
+{
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ if (pszName[0] == ':')
+ return true;
+#endif
+ while (*pszName)
+ {
+ while (RTPATH_IS_SEP(*pszName))
+ pszName++;
+ if ( pszName[0] == '.'
+ && pszName[1] == '.'
+ && (pszName[2] == '\0' || RTPATH_IS_SLASH(pszName[2])) )
+ return true;
+ while (*pszName && !RTPATH_IS_SEP(*pszName))
+ pszName++;
+ }
+
+ return false;
+}
+
+
+/**
+ * Queries the user ID to use when extracting a member.
*
* @returns rcExit or RTEXITCODE_FAILURE.
+ * @param pOpts The tar options.
+ * @param pUser The user info.
+ * @param pszName The file name to use when complaining.
* @param rcExit The current exit code.
- * @param hVfsObj The tar object to display
- * @param pszName The name.
+ * @param pUid Where to return the user ID.
+ */
+static RTEXITCODE rtZipTarQueryExtractOwner(PRTZIPTARCMDOPS pOpts, PCRTFSOBJINFO pOwner, const char *pszName, RTEXITCODE rcExit,
+ PRTUID pUid)
+{
+ if (pOpts->uidOwner != NIL_RTUID)
+ *pUid = pOpts->uidOwner;
+ else if (pOpts->fPreserveGroup)
+ {
+ if (!pOwner->Attr.u.UnixGroup.szName[0])
+ *pUid = pOwner->Attr.u.UnixOwner.uid;
+ else
+ {
+ *pUid = NIL_RTUID;
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: User resolving is not implemented.", pszName);
+ }
+ }
+ else
+ *pUid = NIL_RTUID;
+ return rcExit;
+}
+
+
+/**
+ * Queries the group ID to use when extracting a member.
+ *
+ * @returns rcExit or RTEXITCODE_FAILURE.
* @param pOpts The tar options.
+ * @param pGroup The group info.
+ * @param pszName The file name to use when complaining.
+ * @param rcExit The current exit code.
+ * @param pGid Where to return the group ID.
*/
-static RTEXITCODE rtZipTarCmdDisplayEntryVerbose(RTEXITCODE rcExit, RTVFSOBJ hVfsObj, const char *pszName,
- PRTZIPTARCMDOPS pOpts)
+static RTEXITCODE rtZipTarQueryExtractGroup(PRTZIPTARCMDOPS pOpts, PCRTFSOBJINFO pGroup, const char *pszName, RTEXITCODE rcExit,
+ PRTGID pGid)
{
+ if (pOpts->gidGroup != NIL_RTGID)
+ *pGid = pOpts->gidGroup;
+ else if (pOpts->fPreserveGroup)
+ {
+ if (!pGroup->Attr.u.UnixGroup.szName[0])
+ *pGid = pGroup->Attr.u.UnixGroup.gid;
+ else
+ {
+ *pGid = NIL_RTGID;
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Group resolving is not implemented.", pszName);
+ }
+ }
+ else
+ *pGid = NIL_RTGID;
+ return rcExit;
+}
+
+
+
+/**
+ * Extracts a file.
+ *
+ * Since we can restore permissions and attributes more efficiently by working
+ * directly on the file handle, we have special code path for files.
+ *
+ * @returns rcExit or RTEXITCODE_FAILURE.
+ * @param pOpts The tar options.
+ * @param hVfsObj The tar object to display
+ * @param rcExit The current exit code.
+ * @param pUnixInfo The unix fs object info.
+ * @param pOwner The owner info.
+ * @param pGroup The group info.
+ */
+static RTEXITCODE rtZipTarCmdExtractFile(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, RTEXITCODE rcExit,
+ const char *pszDst, PCRTFSOBJINFO pUnixInfo, PCRTFSOBJINFO pOwner, PCRTFSOBJINFO pGroup)
+{
+ /*
+ * 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->fNoModTime)
+ {
+ rc = RTFileSetTimes(hFile, NULL, &pUnixInfo->ModificationTime, NULL, NULL);
+ if (RT_FAILURE(rc))
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error setting times: %Rrc", pszDst, rc);
+ }
+
+#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
+ if ( pOpts->uidOwner != NIL_RTUID
+ || pOpts->gidGroup != NIL_RTGID
+ || pOpts->fPreserveOwner
+ || pOpts->fPreserveGroup)
+ {
+ RTUID uidFile;
+ rcExit = rtZipTarQueryExtractOwner(pOpts, pOwner, pszDst, rcExit, &uidFile);
+
+ RTGID gidFile;
+ rcExit = rtZipTarQueryExtractGroup(pOpts, pGroup, pszDst, rcExit, &gidFile);
+ if (uidFile != NIL_RTUID || gidFile != NIL_RTGID)
+ {
+ rc = RTFileSetOwner(hFile, uidFile, gidFile);
+ if (RT_FAILURE(rc))
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error owner/group: %Rrc", pszDst, rc);
+ }
+ }
+#endif
+
+ RTFMODE fMode = (pUnixInfo->Attr.fMode & pOpts->fFileModeAndMask) | pOpts->fFileModeOrMask;
+ rc = RTFileSetMode(hFile, fMode | RTFS_TYPE_FILE);
+ if (RT_FAILURE(rc))
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error changing mode: %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);
+ RTFileClose(hFile);
+ return rcExit;
+}
+
+
+/**
+ * @callback_method_impl{PFNDOWITHMEMBER, Implements --extract.}
+ */
+static RTEXITCODE rtZipTarCmdExtractCallback(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit)
+{
+ 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);
+
+ RTFSOBJINFO Owner;
+ rc = RTVfsObjQueryInfo(hVfsObj, &Owner, RTFSOBJATTRADD_UNIX_OWNER);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE,
+ "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
+ rc, pszName);
+
+ RTFSOBJINFO Group;
+ rc = RTVfsObjQueryInfo(hVfsObj, &Group, RTFSOBJATTRADD_UNIX_GROUP);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE,
+ "RTVfsObjQueryInfo(,,UNIX_OWNER) returned %Rrc on '%s'",
+ rc, pszName);
+
+ const char *pszLinkType = NULL;
+ char szTarget[RTPATH_MAX];
+ szTarget[0] = '\0';
+ RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
+ if (hVfsSymlink != NIL_RTVFSSYMLINK)
+ {
+ rc = RTVfsSymlinkRead(hVfsSymlink, szTarget, sizeof(szTarget));
+ RTVfsSymlinkRelease(hVfsSymlink);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: RTVfsSymlinkRead failed: %Rrc", pszName, rc);
+ if (!RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Hardlinks are not supported.", pszName);
+ if (!szTarget[0])
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Link target is empty.", pszName);
+ }
+ else if (RTFS_IS_SYMLINK(UnixInfo.Attr.fMode))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get symlink object for '%s'", pszName);
+
+ if (rtZipTarHasEscapeSequence(pszName))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Name '%s' contains an escape sequence.", pszName);
+
+ /*
+ * Construct the path to the extracted member.
+ */
+ 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 rtZipTarCmdExtractFile(pOpts, hVfsObj, rcExit, szDst, &UnixInfo, &Owner, &Group);
+
+ 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;
+
+ case RTFS_TYPE_SYMLINK:
+ rc = RTSymlinkCreate(szDst, szTarget, RTSYMLINKTYPE_UNKNOWN, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error creating symbolic link: %Rrc", szDst, rc);
+ break;
+
+ case RTFS_TYPE_FIFO:
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: FIFOs are not supported.", pszName);
+ case RTFS_TYPE_DEV_CHAR:
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: FIFOs are not supported.", pszName);
+ case RTFS_TYPE_DEV_BLOCK:
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Block devices are not supported.", pszName);
+ case RTFS_TYPE_SOCKET:
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Sockets are not supported.", pszName);
+ case RTFS_TYPE_WHITEOUT:
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Whiteouts are not support.", pszName);
+ default:
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Unknown file type.", pszName);
+ }
+
+ /*
+ * Set other attributes as requested .
+ * .
+ * Note! File extraction does get here.
+ */
+ if (!pOpts->fNoModTime)
+ {
+ 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);
+ }
+
+#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
+ if ( pOpts->uidOwner != NIL_RTUID
+ || pOpts->gidGroup != NIL_RTGID
+ || pOpts->fPreserveOwner
+ || pOpts->fPreserveGroup)
+ {
+ RTUID uidFile;
+ rcExit = rtZipTarQueryExtractOwner(pOpts, &Owner, szDst, rcExit, &uidFile);
+
+ RTGID gidFile;
+ rcExit = rtZipTarQueryExtractGroup(pOpts, &Group, szDst, rcExit, &gidFile);
+ if (uidFile != NIL_RTUID || gidFile != NIL_RTGID)
+ {
+ rc = RTPathSetOwnerEx(szDst, uidFile, gidFile, RTPATH_F_ON_LINK);
+ if (RT_FAILURE(rc))
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error owner/group: %Rrc", szDst, rc);
+ }
+ }
+#endif
+
+#if !defined(RT_OS_WINDOWS) /** @todo implement RTPathSetMode on windows... */
+ if (!RTFS_IS_SYMLINK(UnixInfo.Attr.fMode)) /* RTPathSetMode follows symbolic links atm. */
+ {
+ RTFMODE fMode;
+ if (RTFS_IS_DIRECTORY(UnixInfo.Attr.fMode))
+ fMode = (UnixInfo.Attr.fMode & (pOpts->fDirModeAndMask | RTFS_TYPE_MASK)) | pOpts->fDirModeOrMask;
+ else
+ fMode = (UnixInfo.Attr.fMode & (pOpts->fFileModeAndMask | RTFS_TYPE_MASK)) | pOpts->fFileModeOrMask;
+ rc = RTPathSetMode(szDst, fMode);
+ if (RT_FAILURE(rc))
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Error changing mode: %Rrc", szDst, rc);
+ }
+#endif
+
+ return rcExit;
+}
+
+
+/**
+ * @callback_method_impl{PFNDOWITHMEMBER, Implements --list.}
+ */
+static RTEXITCODE rtZipTarCmdListCallback(PRTZIPTARCMDOPS pOpts, RTVFSOBJ hVfsObj, const char *pszName, RTEXITCODE rcExit)
+{
+ /*
+ * This is very simple in non-verbose mode.
+ */
+ if (!pOpts->fVerbose)
+ {
+ RTPrintf("%s\n", pszName);
+ return rcExit;
+ }
+
/*
* Query all the information.
*/
@@ -392,91 +857,86 @@ static RTEXITCODE rtZipTarCmdDisplayEntryVerbose(RTEXITCODE rcExit, RTVFSOBJ hVf
return rcExit;
}
+
/**
- * Implements the -t/--list operation.
+ * Display usage.
*
- * @returns The appropriate exit code.
- * @param pOpts The tar options.
+ * @param pszProgName The program name.
*/
-static RTEXITCODE rtZipTarCmdList(PRTZIPTARCMDOPS pOpts)
+static void rtZipTarUsage(const char *pszProgName)
{
/*
- * Allocate a bitmap to go with the file list. This will be used to
- * indicate which files we've processed and which not.
+ * 0 1 2 3 4 5 6 7 8
+ * 012345678901234567890123456789012345678901234567890123456789012345678901234567890
*/
- 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");
- }
-
-
- /*
- * Open the input archive.
- */
- RTVFSFSSTREAM hVfsFssIn;
- RTEXITCODE rcExit = rtZipTarCmdOpenInputArchive(pOpts, &hVfsFssIn);
- if (rcExit == RTEXITCODE_SUCCESS)
- {
- /*
- * Process the stream.
- */
- for (;;)
- {
- /*
- * Retrive 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 display this entry?
- */
- uint32_t iFile = UINT32_MAX;
- if ( !pOpts->cFiles
- || rtZipTarCmdIsNameInArray(pszName, pOpts->papszFiles, &iFile) )
- {
- if (pbmFound)
- ASMBitSet(pbmFound, iFile);
-
- if (!pOpts->fVerbose)
- RTPrintf("%s\n", pszName);
- else
- rcExit = rtZipTarCmdDisplayEntryVerbose(rcExit, hVfsObj, pszName, pOpts);
- }
-
- /*
- * 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;
- }
- }
- RTMemFree(pbmFound);
- return rcExit;
+ RTPrintf("Usage: %s [options]\n"
+ "\n",
+ pszProgName);
+ RTPrintf("Operations:\n"
+ " -A, --concatenate, --catenate\n"
+ " Append the content of one tar archive to another. (not impl)\n"
+ " -c, --create\n"
+ " Create a new tar archive. (not impl)\n"
+ " -d, --diff, --compare\n"
+ " Compare atar archive with the file system. (not impl)\n"
+ " -r, --append\n"
+ " Append more files to the tar archive. (not impl)\n"
+ " -t, --list\n"
+ " List the contents of the tar archive.\n"
+ " -u, --update\n"
+ " Update the archive, adding files that are newer than the\n"
+ " ones in the archive. (not impl)\n"
+ " -x, --extract, --get\n"
+ " Extract the files from the tar archive.\n"
+ " --delete\n"
+ " Delete files from the tar archive.\n"
+ "\n"
+ );
+ RTPrintf("Basic Options:\n"
+ " -C <dir>, --directory <dir> (-A, -C, -d, -r, -u, -x)\n"
+ " Sets the base directory for input and output file members.\n"
+ " This does not apply to --file, even if it preceeds it.\n"
+ " -f <archive>, --file <archive> (all)\n"
+ " The tar file to create or process. '-' indicates stdout/stdin,\n"
+ " which is is the default.\n"
+ " -v, --verbose (all)\n"
+ " Verbose operation.\n"
+ " -p, --preserve-permissions (-x)\n"
+ " Preserve all permissions when extracting. Must be used\n"
+ " before the mode mask options as it will change some of these.\n"
+ " -j, --bzip2 (all)\n"
+ " Compress/decompress the archive with bzip2.\n"
+ " -z, --gzip, --gunzip, --ungzip (all)\n"
+ " Compress/decompress the archive with gzip.\n"
+ "\n");
+ RTPrintf("Misc Options:\n"
+ " --owner <uid/username> (-A, -C, -d, -r, -u, -x)\n"
+ " Set the owner of extracted and archived files to the user specified.\n"
+ " --group <uid/username> (-A, -C, -d, -r, -u, -x)\n"
+ " Set the group of extracted and archived files to the group specified.\n"
+ " --utc (-t)\n"
+ " Display timestamps as UTC instead of local time.\n"
+ "\n");
+ RTPrintf("IPRT Options:\n"
+ " --prefix <dir-prefix> (-A, -C, -d, -r, -u)\n"
+ " Directory prefix to give the members added to the archive.\n"
+ " --file-mode-and-mask <octal-mode> (-A, -C, -d, -r, -u, -x)\n"
+ " Restrict the access mode of regular and special files.\n"
+ " --file-mode-and-mask <octal-mode> (-A, -C, -d, -r, -u, -x)\n"
+ " Include the given access mode for regular and special files.\n"
+ " --dir-mode-and-mask <octal-mode> (-A, -C, -d, -r, -u, -x)\n"
+ " Restrict the access mode of directories.\n"
+ " --dir-mode-and-mask <octal-mode> (-A, -C, -d, -r, -u, -x)\n"
+ " Include the given access mode for directories.\n"
+ "\n");
+ RTPrintf("Standard Options:\n"
+ " -h, -?, --help\n"
+ " Display this help text.\n"
+ " -V, --version\n"
+ " Display version number.\n");
}
-
RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
{
/*
@@ -514,10 +974,15 @@ RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
/* other options. */
{ "--owner", RTZIPTARCMD_OPT_OWNER, RTGETOPT_REQ_STRING },
{ "--group", RTZIPTARCMD_OPT_GROUP, RTGETOPT_REQ_STRING },
- { "--utc", RTZIPTARCMD_OPT_UTC, RTGETOPT_REQ_NOTHING },
+ { "--utc", RTZIPTARCMD_OPT_UTC, RTGETOPT_REQ_NOTHING },
/* IPRT extensions */
- { "--prefix", RTZIPTARCMD_OPT_PREFIX, RTGETOPT_REQ_STRING },
+ { "--prefix", RTZIPTARCMD_OPT_PREFIX, RTGETOPT_REQ_STRING },
+ { "--file-mode-and-mask", RTZIPTARCMD_OPT_FILE_MODE_AND_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
+ { "--file-mode-or-mask", RTZIPTARCMD_OPT_FILE_MODE_OR_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
+ { "--dir-mode-and-mask", RTZIPTARCMD_OPT_DIR_MODE_AND_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
+ { "--dir-mode-or-mask", RTZIPTARCMD_OPT_DIR_MODE_OR_MASK, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
+ { "--format", RTZIPTARCMD_OPT_FORMAT, RTGETOPT_REQ_STRING },
};
RTGETOPTSTATE GetState;
@@ -527,7 +992,21 @@ RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOpt failed: %Rrc", rc);
RTZIPTARCMDOPS Opts;
- RT_ZERO(Opts); /* nice defaults :-) */
+ RT_ZERO(Opts);
+ Opts.enmFormat = RTZIPTARFORMAT_AUTO_DEFAULT;
+ Opts.uidOwner = NIL_RTUID;
+ Opts.gidGroup = NIL_RTUID;
+ Opts.fFileModeAndMask = RTFS_UNIX_ALL_ACCESS_PERMS;
+ Opts.fDirModeAndMask = RTFS_UNIX_ALL_ACCESS_PERMS;
+#if 0
+ if (RTPermIsSuperUser())
+ {
+ Opts.fFileModeAndMask = RTFS_UNIX_ALL_PERMS;
+ Opts.fDirModeAndMask = RTFS_UNIX_ALL_PERMS;
+ Opts.fPreserveOwner = true;
+ Opts.fPreserveGroup = true;
+ }
+#endif
RTGETOPTUNION ValueUnion;
while ( (rc = RTGetOpt(&GetState, &ValueUnion)) != 0
@@ -569,7 +1048,10 @@ RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
break;
case 'p':
- Opts.fPreservePermissions = true;
+ Opts.fFileModeAndMask = RTFS_UNIX_ALL_PERMS;
+ Opts.fDirModeAndMask = RTFS_UNIX_ALL_PERMS;
+ Opts.fPreserveOwner = true;
+ Opts.fPreserveGroup = true;
break;
case 'j':
@@ -583,12 +1065,32 @@ RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
if (Opts.pszOwner)
return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --owner once");
Opts.pszOwner = ValueUnion.psz;
+
+ rc = RTStrToUInt32Full(Opts.pszOwner, 0, &ValueUnion.u32);
+ if (RT_SUCCESS(rc) && rc != VINF_SUCCESS)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX,
+ "Error convering --owner '%s' into a number: %Rrc", Opts.pszOwner, rc);
+ if (RT_SUCCESS(rc))
+ {
+ Opts.uidOwner = ValueUnion.u32;
+ Opts.pszOwner = NULL;
+ }
break;
case RTZIPTARCMD_OPT_GROUP:
if (Opts.pszGroup)
return RTMsgErrorExit(RTEXITCODE_SYNTAX, "You may only specify --group once");
Opts.pszGroup = ValueUnion.psz;
+
+ rc = RTStrToUInt32Full(Opts.pszGroup, 0, &ValueUnion.u32);
+ if (RT_SUCCESS(rc) && rc != VINF_SUCCESS)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX,
+ "Error convering --group '%s' into a number: %Rrc", Opts.pszGroup, rc);
+ if (RT_SUCCESS(rc))
+ {
+ Opts.gidGroup = ValueUnion.u32;
+ Opts.pszGroup = NULL;
+ }
break;
case RTZIPTARCMD_OPT_UTC:
@@ -602,13 +1104,36 @@ RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
Opts.pszPrefix = ValueUnion.psz;
break;
+ case RTZIPTARCMD_OPT_FILE_MODE_AND_MASK:
+ Opts.fFileModeAndMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
+ break;
+
+ case RTZIPTARCMD_OPT_FILE_MODE_OR_MASK:
+ Opts.fFileModeOrMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
+ break;
+
+ case RTZIPTARCMD_OPT_DIR_MODE_AND_MASK:
+ Opts.fDirModeAndMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
+ break;
+
+ case RTZIPTARCMD_OPT_DIR_MODE_OR_MASK:
+ Opts.fDirModeOrMask = ValueUnion.u32 & RTFS_UNIX_ALL_PERMS;
+ break;
+
+ case RTZIPTARCMD_OPT_FORMAT:
+ if (!strcmp(ValueUnion.psz, "auto") || !strcmp(ValueUnion.psz, "default"))
+ Opts.enmFormat = RTZIPTARFORMAT_AUTO_DEFAULT;
+ else if (!strcmp(ValueUnion.psz, "tar"))
+ Opts.enmFormat = RTZIPTARFORMAT_TAR;
+ else if (!strcmp(ValueUnion.psz, "xar"))
+ Opts.enmFormat = RTZIPTARFORMAT_XAR;
+ else
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown archive format: '%s'", ValueUnion.psz);
+ break;
+
+ /* Standard bits. */
case 'h':
- RTPrintf("Usage: to be written\nOption dump:\n");
- for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
- if (RT_C_IS_PRINT(s_aOptions[i].iShort))
- RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
- else
- RTPrintf(" %s\n", s_aOptions[i].pszLong);
+ rtZipTarUsage(RTPathFilename(papszArgs[0]));
return RTEXITCODE_SUCCESS;
case 'V':
@@ -651,14 +1176,16 @@ RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
switch (Opts.iOperation)
{
case 't':
- return rtZipTarCmdList(&Opts);
+ return rtZipTarDoWithMembers(&Opts, rtZipTarCmdListCallback);
+
+ case 'x':
+ return rtZipTarDoWithMembers(&Opts, rtZipTarCmdExtractCallback);
case 'A':
case 'c':
case 'd':
case 'r':
case 'u':
- case 'x':
case RTZIPTARCMD_OPT_DELETE:
return RTMsgErrorExit(RTEXITCODE_FAILURE, "The operation %s is not implemented yet", Opts.pszOperation);
diff --git a/src/VBox/Runtime/common/zip/tarvfs.cpp b/src/VBox/Runtime/common/zip/tarvfs.cpp
index 740c1397..67200fe8 100644
--- a/src/VBox/Runtime/common/zip/tarvfs.cpp
+++ b/src/VBox/Runtime/common/zip/tarvfs.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2010 Oracle Corporation
+ * 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;
diff --git a/src/VBox/Runtime/common/zip/xarvfs.cpp b/src/VBox/Runtime/common/zip/xarvfs.cpp
new file mode 100644
index 00000000..8b6d0abc
--- /dev/null
+++ b/src/VBox/Runtime/common/zip/xarvfs.cpp
@@ -0,0 +1,2111 @@
+/* $Id: xarvfs.cpp $ */
+/** @file
+ * IPRT - XAR Virtual Filesystem.
+ */
+
+/*
+ * Copyright (C) 2010-2013 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 "internal/iprt.h"
+#include <iprt/zip.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/md5.h>
+#include <iprt/poll.h>
+#include <iprt/file.h>
+#include <iprt/sha.h>
+#include <iprt/string.h>
+#include <iprt/vfs.h>
+#include <iprt/vfslowlevel.h>
+#include <iprt/formats/xar.h>
+#include <iprt/cpp/xml.h>
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** @name Hash state
+ * @{ */
+#define RTZIPXAR_HASH_PENDING 0
+#define RTZIPXAR_HASH_OK 1
+#define RTZIPXAR_HASH_FAILED_ARCHIVED 2
+#define RTZIPXAR_HASH_FAILED_EXTRACTED 3
+/** @} */
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * Hash digest value union for the supported XAR hash functions.
+ * @todo This could be generalized in iprt/checksum.h or somewhere.
+ */
+typedef union RTZIPXARHASHDIGEST
+{
+ uint8_t abMd5[RTMD5_HASH_SIZE];
+ uint8_t abSha1[RTSHA1_HASH_SIZE];
+} RTZIPXARHASHDIGEST;
+/** Pointer to a XAR hash digest union. */
+typedef RTZIPXARHASHDIGEST *PRTZIPXARHASHDIGEST;
+/** Pointer to a const XAR hash digest union. */
+typedef RTZIPXARHASHDIGEST *PCRTZIPXARHASHDIGEST;
+
+/**
+ * Hash context union.
+ */
+typedef union RTZIPXARHASHCTX
+{
+ RTMD5CONTEXT Md5;
+ RTSHA1CONTEXT Sha1;
+} RTZIPXARHASHCTX;
+/** Pointer to a hash context union. */
+typedef RTZIPXARHASHCTX *PRTZIPXARHASHCTX;
+
+/**
+ * XAR reader instance data.
+ */
+typedef struct RTZIPXARREADER
+{
+ /** The TOC XML element. */
+ xml::ElementNode const *pToc;
+ /** The TOC XML document. */
+ xml::Document *pDoc;
+
+ /** The current file. */
+ xml::ElementNode const *pCurFile;
+ /** The depth of the current file, with 0 being the root level. */
+ uint32_t cCurDepth;
+} RTZIPXARREADER;
+/** Pointer to the XAR reader instance data. */
+typedef RTZIPXARREADER *PRTZIPXARREADER;
+
+/**
+ * Xar directory, character device, block device, fifo socket or symbolic link.
+ */
+typedef struct RTZIPXARBASEOBJ
+{
+ /** The file TOC element. */
+ xml::ElementNode const *pFileElem;
+ /** RTFS_TYPE_XXX value for the object. */
+ RTFMODE fModeType;
+} RTZIPXARBASEOBJ;
+/** Pointer to a XAR filesystem stream base object. */
+typedef RTZIPXARBASEOBJ *PRTZIPXARBASEOBJ;
+
+
+/**
+ * XAR data encoding.
+ */
+typedef enum RTZIPXARENCODING
+{
+ RTZIPXARENCODING_INVALID = 0,
+ RTZIPXARENCODING_STORE,
+ RTZIPXARENCODING_GZIP,
+ RTZIPXARENCODING_UNSUPPORTED,
+ RTZIPXARENCODING_END
+} RTZIPXARENCODING;
+
+
+/**
+ * Data stream attributes.
+ */
+typedef struct RTZIPXARDATASTREAM
+{
+ /** Offset of the data in the stream.
+ * @remarks The I/O stream and file constructor will adjust this so that it
+ * relative to the start of the input stream, instead of the first byte
+ * after the TOC. */
+ RTFOFF offData;
+ /** The size of the archived data. */
+ RTFOFF cbDataArchived;
+ /** The size of the extracted data. */
+ RTFOFF cbDataExtracted;
+ /** The encoding of the archived ata. */
+ RTZIPXARENCODING enmEncoding;
+ /** The hash function used for the archived data. */
+ uint8_t uHashFunArchived;
+ /** The hash function used for the extracted data. */
+ uint8_t uHashFunExtracted;
+ /** The digest of the archived data. */
+ RTZIPXARHASHDIGEST DigestArchived;
+ /** The digest of the extracted data. */
+ RTZIPXARHASHDIGEST DigestExtracted;
+} RTZIPXARDATASTREAM;
+/** Pointer to XAR data stream attributes. */
+typedef RTZIPXARDATASTREAM *PRTZIPXARDATASTREAM;
+
+
+/**
+ * Xar file represented as a VFS I/O stream.
+ */
+typedef struct RTZIPXARIOSTREAM
+{
+ /** The basic XAR object data. */
+ RTZIPXARBASEOBJ BaseObj;
+ /** The attributes of the primary data stream. */
+ RTZIPXARDATASTREAM DataAttr;
+ /** The current file position in the archived file. */
+ RTFOFF offCurPos;
+ /** The input I/O stream. */
+ RTVFSIOSTREAM hVfsIos;
+ /** Set if we've reached the end of the file or if the next object in the
+ * file system stream has been requested. */
+ bool fEndOfStream;
+ /** Whether the stream is seekable. */
+ bool fSeekable;
+ /** Hash state. */
+ uint8_t uHashState;
+ /** The size of the file that we've currently hashed.
+ * We use this to check whether the user skips part of the file while reading
+ * and when to compare the digests. */
+ RTFOFF cbDigested;
+ /** The digest of the archived data. */
+ RTZIPXARHASHCTX CtxArchived;
+ /** The digest of the extracted data. */
+ RTZIPXARHASHCTX CtxExtracted;
+} RTZIPXARIOSTREAM;
+/** Pointer to a the private data of a XAR file I/O stream. */
+typedef RTZIPXARIOSTREAM *PRTZIPXARIOSTREAM;
+
+
+/**
+ * Xar file represented as a VFS file.
+ */
+typedef struct RTZIPXARFILE
+{
+ /** The XAR I/O stream data. */
+ RTZIPXARIOSTREAM Ios;
+ /** The input file. */
+ RTVFSFILE hVfsFile;
+} RTZIPXARFILE;
+/** Pointer to the private data of a XAR file. */
+typedef RTZIPXARFILE *PRTZIPXARFILE;
+
+
+/**
+ * Decompressed I/O stream instance.
+ *
+ * This is just a front that checks digests and other sanity stuff.
+ */
+typedef struct RTZIPXARDECOMPIOS
+{
+ /** The decompressor I/O stream. */
+ RTVFSIOSTREAM hVfsIosDecompressor;
+ /** The raw XAR I/O stream. */
+ RTVFSIOSTREAM hVfsIosRaw;
+ /** Pointer to the raw XAR I/O stream instance data. */
+ PRTZIPXARIOSTREAM pIosRaw;
+ /** The current file position in the archived file. */
+ RTFOFF offCurPos;
+ /** The hash function to use on the extracted data. */
+ uint8_t uHashFunExtracted;
+ /** Hash state on the extracted data. */
+ uint8_t uHashState;
+ /** The digest of the extracted data. */
+ RTZIPXARHASHCTX CtxExtracted;
+ /** The expected digest of the extracted data. */
+ RTZIPXARHASHDIGEST DigestExtracted;
+} RTZIPXARDECOMPIOS;
+/** Pointer to the private data of a XAR decompressed I/O stream. */
+typedef RTZIPXARDECOMPIOS *PRTZIPXARDECOMPIOS;
+
+
+/**
+ * Xar filesystem stream private data.
+ */
+typedef struct RTZIPXARFSSTREAM
+{
+ /** The input I/O stream. */
+ RTVFSIOSTREAM hVfsIos;
+ /** The input file, if the stream is actually a file. */
+ RTVFSFILE hVfsFile;
+
+ /** The start offset in the input I/O stream. */
+ RTFOFF offStart;
+ /** The zero offset in the file which all others are relative to. */
+ RTFOFF offZero;
+
+ /** The hash function we're using (XAR_HASH_XXX). */
+ uint8_t uHashFunction;
+ /** The size of the digest produced by the hash function we're using. */
+ uint8_t cbHashDigest;
+
+ /** Set if we've reached the end of the stream. */
+ bool fEndOfStream;
+ /** Set if we've encountered a fatal error. */
+ int rcFatal;
+
+
+ /** The XAR reader instance data. */
+ RTZIPXARREADER XarReader;
+} RTZIPXARFSSTREAM;
+/** Pointer to a the private data of a XAR filesystem stream. */
+typedef RTZIPXARFSSTREAM *PRTZIPXARFSSTREAM;
+
+
+/**
+ * Hashes a block of data.
+ *
+ * @param uHashFunction The hash function to use.
+ * @param pvSrc The data to hash.
+ * @param cbSrc The size of the data to hash.
+ * @param pHashDigest Where to return the message digest.
+ */
+static void rtZipXarCalcHash(uint32_t uHashFunction, void const *pvSrc, size_t cbSrc, PRTZIPXARHASHDIGEST pHashDigest)
+{
+ switch (uHashFunction)
+ {
+ case XAR_HASH_SHA1:
+ RTSha1(pvSrc, cbSrc, pHashDigest->abSha1);
+ break;
+ case XAR_HASH_MD5:
+ RTMd5(pvSrc, cbSrc, pHashDigest->abMd5);
+ break;
+ default:
+ RT_ZERO(*pHashDigest);
+ break;
+ }
+}
+
+
+/**
+ * Initializes a hash context.
+ *
+ * @param pCtx Pointer to the context union.
+ * @param uHashFunction The hash function to use.
+ */
+static void rtZipXarHashInit(PRTZIPXARHASHCTX pCtx, uint32_t uHashFunction)
+{
+ switch (uHashFunction)
+ {
+ case XAR_HASH_SHA1:
+ RTSha1Init(&pCtx->Sha1);
+ break;
+ case XAR_HASH_MD5:
+ RTMd5Init(&pCtx->Md5);;
+ break;
+ default:
+ RT_ZERO(*pCtx);
+ break;
+ }
+}
+
+
+/**
+ * Adds a block to the hash calculation.
+ *
+ * @param pCtx Pointer to the context union.
+ * @param uHashFunction The hash function to use.
+ * @param pvSrc The data to add to the hash.
+ * @param cbSrc The size of the data.
+ */
+static void rtZipXarHashUpdate(PRTZIPXARHASHCTX pCtx, uint32_t uHashFunction, void const *pvSrc, size_t cbSrc)
+{
+ switch (uHashFunction)
+ {
+ case XAR_HASH_SHA1:
+ RTSha1Update(&pCtx->Sha1, pvSrc, cbSrc);
+ break;
+ case XAR_HASH_MD5:
+ RTMd5Update(&pCtx->Md5, pvSrc, cbSrc);
+ break;
+ }
+}
+
+
+/**
+ * Finalizes the hash, producing the message digest.
+ *
+ * @param pCtx Pointer to the context union.
+ * @param uHashFunction The hash function to use.
+ * @param pHashDigest Where to return the message digest.
+ */
+static void rtZipXarHashFinal(PRTZIPXARHASHCTX pCtx, uint32_t uHashFunction, PRTZIPXARHASHDIGEST pHashDigest)
+{
+ switch (uHashFunction)
+ {
+ case XAR_HASH_SHA1:
+ RTSha1Final(&pCtx->Sha1, pHashDigest->abSha1);
+ break;
+ case XAR_HASH_MD5:
+ RTMd5Final(pHashDigest->abMd5, &pCtx->Md5);
+ break;
+ default:
+ RT_ZERO(*pHashDigest);
+ break;
+ }
+}
+
+
+/**
+ * Compares two hash digests.
+ *
+ * @returns true if equal, false if not.
+ * @param uHashFunction The hash function to use.
+ * @param pHashDigest1 The first hash digest.
+ * @param pHashDigest2 The second hash digest.
+ */
+static bool rtZipXarHashIsEqual(uint32_t uHashFunction, PRTZIPXARHASHDIGEST pHashDigest1, PRTZIPXARHASHDIGEST pHashDigest2)
+{
+ switch (uHashFunction)
+ {
+ case XAR_HASH_SHA1:
+ return memcmp(pHashDigest1->abSha1, pHashDigest2->abSha1, sizeof(pHashDigest1->abSha1)) == 0;
+ case XAR_HASH_MD5:
+ return memcmp(pHashDigest1->abMd5, pHashDigest2->abMd5, sizeof(pHashDigest1->abMd5)) == 0;
+ default:
+ return true;
+ }
+}
+
+
+/**
+ * Gets the 'offset', 'size' and optionally 'length' sub elements.
+ *
+ * @returns IPRT status code.
+ * @param pElement The parent element.
+ * @param poff Where to return the offset value.
+ * @param pcbSize Where to return the size value.
+ * @param pcbLength Where to return the length value, optional.
+ */
+static int rtZipXarGetOffsetSizeLengthFromElem(xml::ElementNode const *pElement,
+ PRTFOFF poff, PRTFOFF pcbSize, PRTFOFF pcbLength)
+{
+ /*
+ * The offset.
+ */
+ xml::ElementNode const *pElem = pElement->findChildElement("offset");
+ if (!pElem)
+ return VERR_XAR_MISSING_OFFSET_ELEMENT;
+ const char *pszValue = pElem->getValue();
+ if (!pszValue)
+ return VERR_XAR_BAD_OFFSET_ELEMENT;
+
+ int rc = RTStrToInt64Full(pszValue, 0, poff);
+ if ( RT_FAILURE(rc)
+ || rc == VWRN_NUMBER_TOO_BIG
+ || *poff > RTFOFF_MAX / 2 /* make sure to not overflow calculating offsets. */
+ || *poff < 0)
+ return VERR_XAR_BAD_OFFSET_ELEMENT;
+
+ /*
+ * The 'size' stored in the archive.
+ */
+ pElem = pElement->findChildElement("size");
+ if (!pElem)
+ return VERR_XAR_MISSING_SIZE_ELEMENT;
+
+ pszValue = pElem->getValue();
+ if (!pszValue)
+ return VERR_XAR_BAD_SIZE_ELEMENT;
+
+ rc = RTStrToInt64Full(pszValue, 0, pcbSize);
+ if ( RT_FAILURE(rc)
+ || rc == VWRN_NUMBER_TOO_BIG
+ || *pcbSize >= RTFOFF_MAX - _1M
+ || *pcbSize < 0)
+ return VERR_XAR_BAD_SIZE_ELEMENT;
+ AssertCompile(RTFOFF_MAX == UINT64_MAX / 2);
+
+ /*
+ * The 'length' of the uncompressed data. Not present for checksums, so
+ * the caller might not want it.
+ */
+ if (pcbLength)
+ {
+ pElem = pElement->findChildElement("length");
+ if (!pElem)
+ return VERR_XAR_MISSING_LENGTH_ELEMENT;
+
+ pszValue = pElem->getValue();
+ if (!pszValue)
+ return VERR_XAR_BAD_LENGTH_ELEMENT;
+
+ rc = RTStrToInt64Full(pszValue, 0, pcbLength);
+ if ( RT_FAILURE(rc)
+ || rc == VWRN_NUMBER_TOO_BIG
+ || *pcbLength >= RTFOFF_MAX - _1M
+ || *pcbLength < 0)
+ return VERR_XAR_BAD_LENGTH_ELEMENT;
+ AssertCompile(RTFOFF_MAX == UINT64_MAX / 2);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Convers a checksum style value into a XAR hash function number.
+ *
+ * @returns IPRT status code.
+ * @param pszStyle The XAR checksum style.
+ * @param puHashFunction Where to return the hash function number on success.
+ */
+static int rtZipXarParseChecksumStyle(const char *pszStyle, uint8_t *puHashFunction)
+{
+ size_t cchStyle = strlen(pszStyle);
+ if ( cchStyle == 4
+ && (pszStyle[0] == 's' || pszStyle[0] == 'S')
+ && (pszStyle[1] == 'h' || pszStyle[1] == 'H')
+ && (pszStyle[2] == 'a' || pszStyle[2] == 'A')
+ && pszStyle[3] == '1' )
+ *puHashFunction = XAR_HASH_SHA1;
+ else if ( cchStyle == 3
+ && (pszStyle[0] == 'm' || pszStyle[0] == 'M')
+ && (pszStyle[1] == 'd' || pszStyle[1] == 'D')
+ && pszStyle[2] == '5' )
+ *puHashFunction = XAR_HASH_MD5;
+ else if ( cchStyle == 4
+ && (pszStyle[0] == 'n' || pszStyle[0] == 'N')
+ && (pszStyle[1] == 'o' || pszStyle[1] == 'O')
+ && (pszStyle[2] == 'n' || pszStyle[2] == 'N')
+ && (pszStyle[3] == 'e' || pszStyle[3] == 'E') )
+ *puHashFunction = XAR_HASH_NONE;
+ else
+ {
+ *puHashFunction = UINT8_MAX;
+ return VERR_XAR_BAD_CHECKSUM_ELEMENT;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Parses a checksum element typically found under 'data'.
+ *
+ * @returns IPRT status code.
+ * @param pParentElem The parent element ('data').
+ * @param pszName The name of the element, like 'checksum-archived' or
+ * 'checksum-extracted'.
+ * @param puHashFunction Where to return the XAR hash function number.
+ * @param pDigest Where to return the expected message digest.
+ */
+static int rtZipXarParseChecksumElem(xml::ElementNode const *pParentElem, const char *pszName,
+ uint8_t *puHashFunction, PRTZIPXARHASHDIGEST pDigest)
+{
+ /* Default is no checksum. */
+ *puHashFunction = XAR_HASH_NONE;
+ RT_ZERO(*pDigest);
+
+ xml::ElementNode const *pChecksumElem = pParentElem->findChildElement(pszName);
+ if (!pChecksumElem)
+ return VINF_SUCCESS;
+
+ /* The style. */
+ const char *pszStyle = pChecksumElem->findAttributeValue("style");
+ if (!pszStyle)
+ return VERR_XAR_BAD_CHECKSUM_ELEMENT;
+ int rc = rtZipXarParseChecksumStyle(pszStyle, puHashFunction);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (*puHashFunction == XAR_HASH_NONE)
+ return VINF_SUCCESS;
+
+ /* The digest. */
+ const char *pszDigest = pChecksumElem->getValue();
+ if (!pszDigest)
+ return VERR_XAR_BAD_CHECKSUM_ELEMENT;
+
+ switch (*puHashFunction)
+ {
+ case XAR_HASH_SHA1:
+ rc = RTSha1FromString(pszDigest, pDigest->abSha1);
+ break;
+ case XAR_HASH_MD5:
+ rc = RTMd5FromString(pszDigest, pDigest->abMd5);
+ break;
+ default:
+ rc = VERR_INTERNAL_ERROR_2;
+ }
+ return rc;
+}
+
+
+/**
+ * Gets all the attributes of the primary data stream.
+ *
+ * @returns IPRT status code.
+ * @param pFileElem The file element, we'll be parsing the 'data'
+ * sub element of this.
+ * @param pDataAttr Where to return the attributes.
+ */
+static int rtZipXarGetDataStreamAttributes(xml::ElementNode const *pFileElem, PRTZIPXARDATASTREAM pDataAttr)
+{
+ /*
+ * Get the data element.
+ */
+ xml::ElementNode const *pDataElem = pFileElem->findChildElement("data");
+ if (!pDataElem)
+ return VERR_XAR_MISSING_DATA_ELEMENT;
+
+ /*
+ * Checksums.
+ */
+ int rc = rtZipXarParseChecksumElem(pDataElem, "extracted-checksum",
+ &pDataAttr->uHashFunExtracted, &pDataAttr->DigestExtracted);
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = rtZipXarParseChecksumElem(pDataElem, "archived-checksum",
+ &pDataAttr->uHashFunArchived, &pDataAttr->DigestArchived);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * The encoding.
+ */
+ const char *pszEncoding = pDataElem->findChildElementAttributeValueP("encoding", "style");
+ if (!pszEncoding)
+ return VERR_XAR_NO_ENCODING;
+ if (!strcmp(pszEncoding, "application/octet-stream"))
+ pDataAttr->enmEncoding = RTZIPXARENCODING_STORE;
+ else if (!strcmp(pszEncoding, "application/x-gzip"))
+ pDataAttr->enmEncoding = RTZIPXARENCODING_GZIP;
+ else
+ pDataAttr->enmEncoding = RTZIPXARENCODING_UNSUPPORTED;
+
+ /*
+ * The data offset and the compressed and uncompressed sizes.
+ */
+ rc = rtZipXarGetOffsetSizeLengthFromElem(pDataElem, &pDataAttr->offData,
+ &pDataAttr->cbDataExtracted, &pDataAttr->cbDataArchived);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* No zero padding or other alignment crap, please. */
+ if ( pDataAttr->enmEncoding == RTZIPXARENCODING_STORE
+ && pDataAttr->cbDataExtracted != pDataAttr->cbDataArchived)
+ return VERR_XAR_ARCHIVED_AND_EXTRACTED_SIZES_MISMATCH;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Parses a timestamp.
+ *
+ * We consider all timestamps optional, and will only fail (return @c false) on
+ * parse errors. If the specified element isn't found, we'll return epoc time.
+ *
+ * @returns boolean success indicator.
+ * @param pParent The parent element (typically 'file').
+ * @param pszChild The name of the child element.
+ * @param pTimeSpec Where to return the timespec on success.
+ */
+static bool rtZipXarParseTimestamp(const xml::ElementNode *pParent, const char *pszChild, PRTTIMESPEC pTimeSpec)
+{
+ const char *pszValue = pParent->findChildElementValueP(pszChild);
+ if (pszValue)
+ {
+ if (RTTimeSpecFromString(pTimeSpec, pszValue))
+ return true;
+ return false;
+ }
+ RTTimeSpecSetNano(pTimeSpec, 0);
+ return true;
+}
+
+
+/**
+ * Gets the next file element in the TOC.
+ *
+ * @returns Pointer to the next file, NULL if we've reached the end.
+ * @param pCurFile The current element.
+ * @param pcCurDepth Depth gauge we update when decending and
+ * acending thru the tree.
+ */
+static xml::ElementNode const *rtZipXarGetNextFileElement(xml::ElementNode const *pCurFile, uint32_t *pcCurDepth)
+{
+ /*
+ * Consider children first.
+ */
+ xml::ElementNode const *pChild = pCurFile->findChildElement("file");
+ if (pChild)
+ {
+ *pcCurDepth += 1;
+ return pChild;
+ }
+
+ /*
+ * Siblings and ancestor siblings.
+ */
+ for (;;)
+ {
+ xml::ElementNode const *pSibling = pCurFile->findNextSibilingElement("file");
+ if (pSibling != NULL)
+ return pSibling;
+
+ if (*pcCurDepth == 0)
+ break;
+ *pcCurDepth -= 1;
+ pCurFile = static_cast<const xml::ElementNode *>(pCurFile->getParent());
+ AssertBreak(pCurFile);
+ Assert(pCurFile->nameEquals("file"));
+ }
+
+ return NULL;
+}
+
+
+
+/*
+ *
+ * T h e V F S F i l e s y s t e m S t r e a m B i t s.
+ * T h e V F S F i l e s y s t e m S t r e a m B i t s.
+ * T h e V F S F i l e s y s t e m S t r e a m B i t s.
+ *
+ */
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtZipXarFssBaseObj_Close(void *pvThis)
+{
+ PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
+
+ /* Currently there is nothing we really have to do here. */
+ NOREF(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtZipXarFssBaseObj_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
+
+ /*
+ * Get the common data.
+ */
+
+ /* Sizes. */
+ if (pThis->fModeType == RTFS_TYPE_FILE)
+ {
+ PRTZIPXARIOSTREAM pThisIos = RT_FROM_MEMBER(pThis, RTZIPXARIOSTREAM, BaseObj);
+ pObjInfo->cbObject = pThisIos->DataAttr.cbDataArchived; /* Modified by decomp ios. */
+ pObjInfo->cbAllocated = pThisIos->DataAttr.cbDataArchived;
+ }
+ else
+ {
+ pObjInfo->cbObject = 0;
+ pObjInfo->cbAllocated = 0;
+ }
+
+ /* The file mode. */
+ if (RT_UNLIKELY(!pThis->pFileElem->getChildElementValueDefP("mode", 0755, &pObjInfo->Attr.fMode)))
+ return VERR_XAR_BAD_FILE_MODE;
+ if (pObjInfo->Attr.fMode & RTFS_TYPE_MASK)
+ return VERR_XAR_BAD_FILE_MODE;
+ pObjInfo->Attr.fMode &= RTFS_UNIX_MASK & ~RTFS_TYPE_MASK;
+ pObjInfo->Attr.fMode |= pThis->fModeType;
+
+ /* File times. */
+ if (RT_UNLIKELY(!rtZipXarParseTimestamp(pThis->pFileElem, "atime", &pObjInfo->AccessTime)))
+ return VERR_XAR_BAD_FILE_TIMESTAMP;
+ if (RT_UNLIKELY(!rtZipXarParseTimestamp(pThis->pFileElem, "ctime", &pObjInfo->ChangeTime)))
+ return VERR_XAR_BAD_FILE_TIMESTAMP;
+ if (RT_UNLIKELY(!rtZipXarParseTimestamp(pThis->pFileElem, "mtime", &pObjInfo->ModificationTime)))
+ return VERR_XAR_BAD_FILE_TIMESTAMP;
+ pObjInfo->BirthTime = RTTimeSpecGetNano(&pObjInfo->AccessTime) <= RTTimeSpecGetNano(&pObjInfo->ChangeTime)
+ ? pObjInfo->AccessTime : pObjInfo->ChangeTime;
+ if (RTTimeSpecGetNano(&pObjInfo->BirthTime) > RTTimeSpecGetNano(&pObjInfo->ModificationTime))
+ pObjInfo->BirthTime = pObjInfo->ModificationTime;
+
+ /*
+ * Copy the desired data.
+ */
+ switch (enmAddAttr)
+ {
+ case RTFSOBJATTRADD_NOTHING:
+ case RTFSOBJATTRADD_UNIX:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+ if (RT_UNLIKELY(!pThis->pFileElem->getChildElementValueDefP("uid", 0, &pObjInfo->Attr.u.Unix.uid)))
+ return VERR_XAR_BAD_FILE_UID;
+ if (RT_UNLIKELY(!pThis->pFileElem->getChildElementValueDefP("gid", 0, &pObjInfo->Attr.u.Unix.gid)))
+ return VERR_XAR_BAD_FILE_GID;
+ if (RT_UNLIKELY(!pThis->pFileElem->getChildElementValueDefP("deviceno", 0, &pObjInfo->Attr.u.Unix.INodeIdDevice)))
+ return VERR_XAR_BAD_FILE_DEVICE_NO;
+ if (RT_UNLIKELY(!pThis->pFileElem->getChildElementValueDefP("inode", 0, &pObjInfo->Attr.u.Unix.INodeId)))
+ return VERR_XAR_BAD_FILE_INODE;
+ pObjInfo->Attr.u.Unix.cHardlinks = 1;
+ pObjInfo->Attr.u.Unix.fFlags = 0;
+ pObjInfo->Attr.u.Unix.GenerationId = 0;
+ pObjInfo->Attr.u.Unix.Device = 0;
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ {
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
+ if (RT_UNLIKELY(!pThis->pFileElem->getChildElementValueDefP("uid", 0, &pObjInfo->Attr.u.Unix.uid)))
+ return VERR_XAR_BAD_FILE_UID;
+ const char *pszUser = pThis->pFileElem->findChildElementValueP("user");
+ if (pszUser)
+ RTStrCopy(pObjInfo->Attr.u.UnixOwner.szName, sizeof(pObjInfo->Attr.u.UnixOwner.szName), pszUser);
+ else
+ pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
+ break;
+ }
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ {
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
+ if (RT_UNLIKELY(!pThis->pFileElem->getChildElementValueDefP("gid", 0, &pObjInfo->Attr.u.Unix.gid)))
+ return VERR_XAR_BAD_FILE_GID;
+ const char *pszGroup = pThis->pFileElem->findChildElementValueP("group");
+ if (pszGroup)
+ RTStrCopy(pObjInfo->Attr.u.UnixGroup.szName, sizeof(pObjInfo->Attr.u.UnixGroup.szName), pszGroup);
+ else
+ pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
+ break;
+ }
+
+ case RTFSOBJATTRADD_EASIZE:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
+ RT_ZERO(pObjInfo->Attr.u);
+ break;
+
+ default:
+ return VERR_NOT_SUPPORTED;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Xar filesystem base object operations.
+ */
+static const RTVFSOBJOPS g_rtZipXarFssBaseObjOps =
+{
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_BASE,
+ "XarFsStream::Obj",
+ rtZipXarFssBaseObj_Close,
+ rtZipXarFssBaseObj_QueryInfo,
+ RTVFSOBJOPS_VERSION
+};
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtZipXarFssIos_Close(void *pvThis)
+{
+ PRTZIPXARIOSTREAM pThis = (PRTZIPXARIOSTREAM)pvThis;
+
+ RTVfsIoStrmRelease(pThis->hVfsIos);
+ pThis->hVfsIos = NIL_RTVFSIOSTREAM;
+
+ return rtZipXarFssBaseObj_Close(&pThis->BaseObj);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtZipXarFssIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTZIPXARIOSTREAM pThis = (PRTZIPXARIOSTREAM)pvThis;
+ return rtZipXarFssBaseObj_QueryInfo(&pThis->BaseObj, pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
+ */
+static DECLCALLBACK(int) rtZipXarFssIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
+{
+ PRTZIPXARIOSTREAM pThis = (PRTZIPXARIOSTREAM)pvThis;
+ AssertReturn(off >= -1, VERR_INVALID_PARAMETER);
+ AssertReturn(pSgBuf->cSegs == 1, VERR_INVALID_PARAMETER);
+
+ /*
+ * Fend of reads beyond the end of the stream here. If
+ */
+ if (off == -1)
+ off = pThis->offCurPos;
+ if (off < 0 || off > pThis->DataAttr.cbDataArchived)
+ return VERR_EOF;
+ if (pThis->fEndOfStream)
+ {
+ if (off >= pThis->DataAttr.cbDataArchived)
+ return pcbRead ? VINF_EOF : VERR_EOF;
+ if (!pThis->fSeekable)
+ return VERR_SEEK_ON_DEVICE;
+ pThis->fEndOfStream = false;
+ }
+
+ size_t cbToRead = pSgBuf->paSegs[0].cbSeg;
+ uint64_t cbLeft = pThis->DataAttr.cbDataArchived - off;
+ if (cbToRead > cbLeft)
+ {
+ if (!pcbRead)
+ return VERR_EOF;
+ cbToRead = (size_t)cbLeft;
+ }
+
+ /*
+ * Do the reading.
+ */
+ size_t cbReadStack = 0;
+ if (!pcbRead)
+ pcbRead = &cbReadStack;
+ int rc = RTVfsIoStrmReadAt(pThis->hVfsIos, off + pThis->DataAttr.offData, pSgBuf->paSegs[0].pvSeg,
+ cbToRead, fBlocking, pcbRead);
+
+ /* Feed the hashes. */
+ size_t cbActuallyRead = *pcbRead;
+ if (pThis->uHashState == RTZIPXAR_HASH_PENDING)
+ {
+ if (pThis->offCurPos == pThis->cbDigested)
+ {
+ rtZipXarHashUpdate(&pThis->CtxArchived, pThis->DataAttr.uHashFunArchived, pSgBuf->paSegs[0].pvSeg, cbActuallyRead);
+ rtZipXarHashUpdate(&pThis->CtxExtracted, pThis->DataAttr.uHashFunExtracted, pSgBuf->paSegs[0].pvSeg, cbActuallyRead);
+ pThis->cbDigested += cbActuallyRead;
+ }
+ else if ( pThis->cbDigested > pThis->offCurPos
+ && pThis->cbDigested < (RTFOFF)(pThis->offCurPos + cbActuallyRead))
+ {
+ size_t offHash = pThis->cbDigested - pThis->offCurPos;
+ void const *pvHash = (uint8_t const *)pSgBuf->paSegs[0].pvSeg + offHash;
+ size_t cbHash = cbActuallyRead - offHash;
+ rtZipXarHashUpdate(&pThis->CtxArchived, pThis->DataAttr.uHashFunArchived, pvHash, cbHash);
+ rtZipXarHashUpdate(&pThis->CtxExtracted, pThis->DataAttr.uHashFunExtracted, pvHash, cbHash);
+ pThis->cbDigested += cbHash;
+ }
+ }
+
+ /* Update the file position. */
+ pThis->offCurPos += cbActuallyRead;
+
+ /*
+ * Check for end of stream, also check the hash.
+ */
+ if (pThis->offCurPos >= pThis->DataAttr.cbDataArchived)
+ {
+ Assert(pThis->offCurPos == pThis->DataAttr.cbDataArchived);
+ pThis->fEndOfStream = true;
+
+ /* Check hash. */
+ if ( pThis->uHashState == RTZIPXAR_HASH_PENDING
+ && pThis->cbDigested == pThis->DataAttr.cbDataArchived)
+ {
+ RTZIPXARHASHDIGEST Digest;
+ rtZipXarHashFinal(&pThis->CtxArchived, pThis->DataAttr.uHashFunArchived, &Digest);
+ if (rtZipXarHashIsEqual(pThis->DataAttr.uHashFunArchived, &Digest, &pThis->DataAttr.DigestArchived))
+ {
+ rtZipXarHashFinal(&pThis->CtxExtracted, pThis->DataAttr.uHashFunExtracted, &Digest);
+ if (rtZipXarHashIsEqual(pThis->DataAttr.uHashFunExtracted, &Digest, &pThis->DataAttr.DigestExtracted))
+ pThis->uHashState = RTZIPXAR_HASH_OK;
+ else
+ {
+ pThis->uHashState = RTZIPXAR_HASH_FAILED_EXTRACTED;
+ rc = VERR_XAR_EXTRACTED_HASH_MISMATCH;
+ }
+ }
+ else
+ {
+ pThis->uHashState = RTZIPXAR_HASH_FAILED_ARCHIVED;
+ rc = VERR_XAR_ARCHIVED_HASH_MISMATCH;
+ }
+ }
+ else if (pThis->uHashState == RTZIPXAR_HASH_FAILED_ARCHIVED)
+ rc = VERR_XAR_ARCHIVED_HASH_MISMATCH;
+ else if (pThis->uHashState == RTZIPXAR_HASH_FAILED_EXTRACTED)
+ rc = VERR_XAR_EXTRACTED_HASH_MISMATCH;
+ }
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
+ */
+static DECLCALLBACK(int) rtZipXarFssIos_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
+{
+ /* Cannot write to a read-only I/O stream. */
+ NOREF(pvThis); NOREF(off); NOREF(pSgBuf); NOREF(fBlocking); NOREF(pcbWritten);
+ return VERR_ACCESS_DENIED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
+ */
+static DECLCALLBACK(int) rtZipXarFssIos_Flush(void *pvThis)
+{
+ /* It's a read only stream, nothing dirty to flush. */
+ NOREF(pvThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
+ */
+static DECLCALLBACK(int) rtZipXarFssIos_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
+ uint32_t *pfRetEvents)
+{
+ PRTZIPXARIOSTREAM pThis = (PRTZIPXARIOSTREAM)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) rtZipXarFssIos_Tell(void *pvThis, PRTFOFF poffActual)
+{
+ PRTZIPXARIOSTREAM pThis = (PRTZIPXARIOSTREAM)pvThis;
+ *poffActual = pThis->offCurPos;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Xar I/O stream operations.
+ */
+static const RTVFSIOSTREAMOPS g_rtZipXarFssIosOps =
+{
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_IO_STREAM,
+ "XarFsStream::IoStream",
+ rtZipXarFssIos_Close,
+ rtZipXarFssIos_QueryInfo,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSIOSTREAMOPS_VERSION,
+ 0,
+ rtZipXarFssIos_Read,
+ rtZipXarFssIos_Write,
+ rtZipXarFssIos_Flush,
+ rtZipXarFssIos_PollOne,
+ rtZipXarFssIos_Tell,
+ NULL /*Skip*/,
+ NULL /*ZeroFill*/,
+ RTVFSIOSTREAMOPS_VERSION
+};
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
+ */
+static DECLCALLBACK(int) rtZipXarFssFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
+{
+ NOREF(pvThis);
+ NOREF(fMode);
+ NOREF(fMask);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
+ */
+static DECLCALLBACK(int) rtZipXarFssFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ NOREF(pvThis);
+ NOREF(pAccessTime);
+ NOREF(pModificationTime);
+ NOREF(pChangeTime);
+ NOREF(pBirthTime);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
+ */
+static DECLCALLBACK(int) rtZipXarFssFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
+{
+ NOREF(pvThis);
+ NOREF(uid);
+ NOREF(gid);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
+ */
+static DECLCALLBACK(int) rtZipXarFssFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
+{
+ PRTZIPXARFILE pThis = (PRTZIPXARFILE)pvThis;
+
+ /* Recalculate the request to RTFILE_SEEK_BEGIN. */
+ switch (uMethod)
+ {
+ case RTFILE_SEEK_BEGIN:
+ break;
+ case RTFILE_SEEK_CURRENT:
+ offSeek += pThis->Ios.offCurPos;
+ break;
+ case RTFILE_SEEK_END:
+ offSeek = pThis->Ios.DataAttr.cbDataArchived + offSeek;
+ break;
+ default:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+
+ /* Do limit checks. */
+ if (offSeek < 0)
+ offSeek = 0;
+ else if (offSeek > pThis->Ios.DataAttr.cbDataArchived)
+ offSeek = pThis->Ios.DataAttr.cbDataArchived;
+
+ /* Apply and return. */
+ pThis->Ios.fEndOfStream = (offSeek >= pThis->Ios.DataAttr.cbDataArchived);
+ pThis->Ios.offCurPos = offSeek;
+ if (poffActual)
+ *poffActual = offSeek;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
+ */
+static DECLCALLBACK(int) rtZipXarFssFile_QuerySize(void *pvThis, uint64_t *pcbFile)
+{
+ PRTZIPXARFILE pThis = (PRTZIPXARFILE)pvThis;
+ *pcbFile = pThis->Ios.DataAttr.cbDataArchived;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Xar file operations.
+ */
+static const RTVFSFILEOPS g_rtZipXarFssFileOps =
+{
+ { /* I/O stream */
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_FILE,
+ "XarFsStream::File",
+ rtZipXarFssIos_Close,
+ rtZipXarFssIos_QueryInfo,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSIOSTREAMOPS_VERSION,
+ RTVFSIOSTREAMOPS_FEAT_NO_SG,
+ rtZipXarFssIos_Read,
+ rtZipXarFssIos_Write,
+ rtZipXarFssIos_Flush,
+ rtZipXarFssIos_PollOne,
+ rtZipXarFssIos_Tell,
+ NULL /*Skip*/,
+ NULL /*ZeroFill*/,
+ RTVFSIOSTREAMOPS_VERSION
+ },
+ RTVFSFILEOPS_VERSION,
+ 0,
+ { /* ObjSet */
+ RTVFSOBJSETOPS_VERSION,
+ RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
+ rtZipXarFssFile_SetMode,
+ rtZipXarFssFile_SetTimes,
+ rtZipXarFssFile_SetOwner,
+ RTVFSOBJSETOPS_VERSION
+ },
+ rtZipXarFssFile_Seek,
+ rtZipXarFssFile_QuerySize,
+ RTVFSFILEOPS_VERSION,
+};
+
+
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtZipXarFssDecompIos_Close(void *pvThis)
+{
+ PRTZIPXARDECOMPIOS pThis = (PRTZIPXARDECOMPIOS)pvThis;
+
+ RTVfsIoStrmRelease(pThis->hVfsIosDecompressor);
+ pThis->hVfsIosDecompressor = NIL_RTVFSIOSTREAM;
+
+ int rc = RTVfsIoStrmRelease(pThis->hVfsIosRaw);
+ pThis->hVfsIosRaw = NIL_RTVFSIOSTREAM;
+ pThis->pIosRaw = NULL;
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtZipXarFssDecompIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTZIPXARDECOMPIOS pThis = (PRTZIPXARDECOMPIOS)pvThis;
+
+ int rc = rtZipXarFssBaseObj_QueryInfo(&pThis->pIosRaw->BaseObj, pObjInfo, enmAddAttr);
+ pObjInfo->cbObject = pThis->pIosRaw->DataAttr.cbDataExtracted;
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
+ */
+static DECLCALLBACK(int) rtZipXarFssDecompIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
+{
+ PRTZIPXARDECOMPIOS pThis = (PRTZIPXARDECOMPIOS)pvThis;
+ AssertReturn(pSgBuf->cSegs == 1, VERR_INVALID_PARAMETER);
+
+ /*
+ * Enforce the cbDataExtracted limit.
+ */
+ if (pThis->offCurPos > pThis->pIosRaw->DataAttr.cbDataExtracted)
+ return VERR_XAR_EXTRACTED_SIZE_EXCEEDED;
+
+ /*
+ * Read the data.
+ *
+ * ASSUMES the decompressor stream isn't seekable, so we don't have to
+ * validate off wrt data digest updating.
+ */
+ int rc = RTVfsIoStrmReadAt(pThis->hVfsIosDecompressor, off, pSgBuf->paSegs[0].pvSeg, pSgBuf->paSegs[0].cbSeg,
+ fBlocking, pcbRead);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Hash the data. When reaching the end match against the expected digest.
+ */
+ size_t cbActuallyRead = pcbRead ? *pcbRead : pSgBuf->paSegs[0].cbSeg;
+ pThis->offCurPos += cbActuallyRead;
+ rtZipXarHashUpdate(&pThis->CtxExtracted, pThis->uHashFunExtracted, pSgBuf->paSegs[0].pvSeg, cbActuallyRead);
+ if (rc == VINF_EOF)
+ {
+ if (pThis->offCurPos == pThis->pIosRaw->DataAttr.cbDataExtracted)
+ {
+ if (pThis->uHashState == RTZIPXAR_HASH_PENDING)
+ {
+ RTZIPXARHASHDIGEST Digest;
+ rtZipXarHashFinal(&pThis->CtxExtracted, pThis->uHashFunExtracted, &Digest);
+ if (rtZipXarHashIsEqual(pThis->uHashFunExtracted, &Digest, &pThis->DigestExtracted))
+ pThis->uHashState = RTZIPXAR_HASH_OK;
+ else
+ {
+ pThis->uHashState = RTZIPXAR_HASH_FAILED_EXTRACTED;
+ rc = VERR_XAR_EXTRACTED_HASH_MISMATCH;
+ }
+ }
+ else if (pThis->uHashState != RTZIPXAR_HASH_OK)
+ rc = VERR_XAR_EXTRACTED_HASH_MISMATCH;
+ }
+ else
+ rc = VERR_XAR_EXTRACTED_SIZE_EXCEEDED;
+
+ /* Ensure that the raw stream is also at the end so that both
+ message digests are checked. */
+ if (RT_SUCCESS(rc))
+ {
+ if ( pThis->pIosRaw->offCurPos < pThis->pIosRaw->DataAttr.cbDataArchived
+ || pThis->pIosRaw->uHashState == RTZIPXAR_HASH_PENDING)
+ rc = VERR_XAR_UNUSED_ARCHIVED_DATA;
+ else if (pThis->pIosRaw->uHashState != RTZIPXAR_HASH_OK)
+ rc = VERR_XAR_ARCHIVED_HASH_MISMATCH;
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
+ */
+static DECLCALLBACK(int) rtZipXarFssDecompIos_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
+{
+ /* Cannot write to a read-only I/O stream. */
+ NOREF(pvThis); NOREF(off); NOREF(pSgBuf); NOREF(fBlocking); NOREF(pcbWritten);
+ return VERR_ACCESS_DENIED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
+ */
+static DECLCALLBACK(int) rtZipXarFssDecompIos_Flush(void *pvThis)
+{
+ /* It's a read only stream, nothing dirty to flush. */
+ NOREF(pvThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
+ */
+static DECLCALLBACK(int) rtZipXarFssDecompIos_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
+ uint32_t *pfRetEvents)
+{
+ PRTZIPXARDECOMPIOS pThis = (PRTZIPXARDECOMPIOS)pvThis;
+ return RTVfsIoStrmPoll(pThis->hVfsIosDecompressor, fEvents, cMillies, fIntr, pfRetEvents);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
+ */
+static DECLCALLBACK(int) rtZipXarFssDecompIos_Tell(void *pvThis, PRTFOFF poffActual)
+{
+ PRTZIPXARDECOMPIOS pThis = (PRTZIPXARDECOMPIOS)pvThis;
+ *poffActual = pThis->offCurPos;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Xar I/O stream operations.
+ */
+static const RTVFSIOSTREAMOPS g_rtZipXarFssDecompIosOps =
+{
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_IO_STREAM,
+ "XarFsStream::DecompIoStream",
+ rtZipXarFssDecompIos_Close,
+ rtZipXarFssDecompIos_QueryInfo,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSIOSTREAMOPS_VERSION,
+ 0,
+ rtZipXarFssDecompIos_Read,
+ rtZipXarFssDecompIos_Write,
+ rtZipXarFssDecompIos_Flush,
+ rtZipXarFssDecompIos_PollOne,
+ rtZipXarFssDecompIos_Tell,
+ NULL /*Skip*/,
+ NULL /*ZeroFill*/,
+ RTVFSIOSTREAMOPS_VERSION
+};
+
+
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtZipXarFssSym_Close(void *pvThis)
+{
+ PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
+ return rtZipXarFssBaseObj_Close(pThis);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtZipXarFssSym_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
+ return rtZipXarFssBaseObj_QueryInfo(pThis, pObjInfo, enmAddAttr);
+}
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
+ */
+static DECLCALLBACK(int) rtZipXarFssSym_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
+{
+ NOREF(pvThis); NOREF(fMode); NOREF(fMask);
+ return VERR_ACCESS_DENIED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
+ */
+static DECLCALLBACK(int) rtZipXarFssSym_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ NOREF(pvThis); NOREF(pAccessTime); NOREF(pModificationTime); NOREF(pChangeTime); NOREF(pBirthTime);
+ return VERR_ACCESS_DENIED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
+ */
+static DECLCALLBACK(int) rtZipXarFssSym_SetOwner(void *pvThis, RTUID uid, RTGID gid)
+{
+ NOREF(pvThis); NOREF(uid); NOREF(gid);
+ return VERR_ACCESS_DENIED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSSYMLINKOPS,pfnRead}
+ */
+static DECLCALLBACK(int) rtZipXarFssSym_Read(void *pvThis, char *pszTarget, size_t cbXarget)
+{
+ PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
+#if 0
+ return RTStrCopy(pszTarget, cbXarget, pThis->pXarReader->szTarget);
+#else
+ return VERR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * Xar symbolic (and hardlink) operations.
+ */
+static const RTVFSSYMLINKOPS g_rtZipXarFssSymOps =
+{
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_SYMLINK,
+ "XarFsStream::Symlink",
+ rtZipXarFssSym_Close,
+ rtZipXarFssSym_QueryInfo,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSSYMLINKOPS_VERSION,
+ 0,
+ { /* ObjSet */
+ RTVFSOBJSETOPS_VERSION,
+ RT_OFFSETOF(RTVFSSYMLINKOPS, Obj) - RT_OFFSETOF(RTVFSSYMLINKOPS, ObjSet),
+ rtZipXarFssSym_SetMode,
+ rtZipXarFssSym_SetTimes,
+ rtZipXarFssSym_SetOwner,
+ RTVFSOBJSETOPS_VERSION
+ },
+ rtZipXarFssSym_Read,
+ RTVFSSYMLINKOPS_VERSION
+};
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtZipXarFss_Close(void *pvThis)
+{
+ PRTZIPXARFSSTREAM pThis = (PRTZIPXARFSSTREAM)pvThis;
+
+ RTVfsIoStrmRelease(pThis->hVfsIos);
+ pThis->hVfsIos = NIL_RTVFSIOSTREAM;
+
+ RTVfsFileRelease(pThis->hVfsFile);
+ pThis->hVfsFile = NIL_RTVFSFILE;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtZipXarFss_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTZIPXARFSSTREAM pThis = (PRTZIPXARFSSTREAM)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) rtZipXarFss_Next(void *pvThis, char **ppszName, RTVFSOBJTYPE *penmType, PRTVFSOBJ phVfsObj)
+{
+ PRTZIPXARFSSTREAM pThis = (PRTZIPXARFSSTREAM)pvThis;
+
+ /*
+ * 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;
+
+ /*
+ * Get the next file element.
+ */
+ xml::ElementNode const *pCurFile = pThis->XarReader.pCurFile;
+ if (pCurFile)
+ pThis->XarReader.pCurFile = pCurFile = rtZipXarGetNextFileElement(pCurFile, &pThis->XarReader.cCurDepth);
+ else if (!pThis->fEndOfStream)
+ {
+ pThis->XarReader.cCurDepth = 0;
+ pThis->XarReader.pCurFile = pCurFile = pThis->XarReader.pToc->findChildElement("file");
+ }
+ if (!pCurFile)
+ {
+ pThis->fEndOfStream = true;
+ return VERR_EOF;
+ }
+
+ /*
+ * Retrive the fundamental attributes (elements actually).
+ */
+ const char *pszName = pCurFile->findChildElementValueP("name");
+ const char *pszType = pCurFile->findChildElementValueP("type");
+ if (RT_UNLIKELY(!pszName || !pszType))
+ return pThis->rcFatal = VERR_XAR_BAD_FILE_ELEMENT;
+
+ /*
+ * Validate the filename. Being a little too paranoid here, perhaps, wrt
+ * path separators and escapes...
+ */
+ if ( !*pszName
+ || strchr(pszName, '/')
+ || strchr(pszName, '\\')
+ || strchr(pszName, ':')
+ || !strcmp(pszName, "..") )
+ return pThis->rcFatal = VERR_XAR_INVALID_FILE_NAME;
+
+ /*
+ * Gather any additional attributes that are essential to the file type,
+ * then create the VFS object we're going to return.
+ */
+ int rc;
+ RTVFSOBJ hVfsObj;
+ RTVFSOBJTYPE enmType;
+ if (!strcmp(pszType, "file"))
+ {
+ RTZIPXARDATASTREAM DataAttr;
+ rc = rtZipXarGetDataStreamAttributes(pCurFile, &DataAttr);
+ if (RT_FAILURE(rc))
+ return pThis->rcFatal = rc;
+ DataAttr.offData += pThis->offZero + pThis->offStart;
+
+ if ( pThis->hVfsFile != NIL_RTVFSFILE
+ && DataAttr.enmEncoding == RTZIPXARENCODING_STORE)
+ {
+ /*
+ * The input is seekable and the XAR file isn't compressed, so we
+ * can provide a seekable file to the user.
+ */
+ RTVFSFILE hVfsFile;
+ PRTZIPXARFILE pFileData;
+ rc = RTVfsNewFile(&g_rtZipXarFssFileOps,
+ sizeof(*pFileData),
+ RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
+ NIL_RTVFS,
+ NIL_RTVFSLOCK,
+ &hVfsFile,
+ (void **)&pFileData);
+ if (RT_FAILURE(rc))
+ return pThis->rcFatal = rc;
+
+ pFileData->Ios.BaseObj.pFileElem = pCurFile;
+ pFileData->Ios.BaseObj.fModeType = RTFS_TYPE_FILE;
+ pFileData->Ios.DataAttr = DataAttr;
+ pFileData->Ios.offCurPos = 0;
+ pFileData->Ios.fEndOfStream = false;
+ pFileData->Ios.fSeekable = true;
+ pFileData->Ios.uHashState = RTZIPXAR_HASH_PENDING;
+ pFileData->Ios.cbDigested = 0;
+ rtZipXarHashInit(&pFileData->Ios.CtxArchived, pFileData->Ios.DataAttr.uHashFunArchived);
+ rtZipXarHashInit(&pFileData->Ios.CtxExtracted, pFileData->Ios.DataAttr.uHashFunExtracted);
+
+ pFileData->Ios.hVfsIos = pThis->hVfsIos;
+ RTVfsIoStrmRetain(pFileData->Ios.hVfsIos);
+ pFileData->hVfsFile = pThis->hVfsFile;
+ RTVfsFileRetain(pFileData->hVfsFile);
+
+ /* Try avoid double content hashing. */
+ if (pFileData->Ios.DataAttr.uHashFunArchived == pFileData->Ios.DataAttr.uHashFunExtracted)
+ pFileData->Ios.DataAttr.uHashFunExtracted = XAR_HASH_NONE;
+
+ enmType = RTVFSOBJTYPE_FILE;
+ hVfsObj = RTVfsObjFromFile(hVfsFile);
+ RTVfsFileRelease(hVfsFile);
+ }
+ else
+ {
+ RTVFSIOSTREAM hVfsIosRaw;
+ PRTZIPXARIOSTREAM pIosData;
+ rc = RTVfsNewIoStream(&g_rtZipXarFssIosOps,
+ sizeof(*pIosData),
+ RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
+ NIL_RTVFS,
+ NIL_RTVFSLOCK,
+ &hVfsIosRaw,
+ (void **)&pIosData);
+ if (RT_FAILURE(rc))
+ return pThis->rcFatal = rc;
+
+ pIosData->BaseObj.pFileElem = pCurFile;
+ pIosData->BaseObj.fModeType = RTFS_TYPE_FILE;
+ pIosData->DataAttr = DataAttr;
+ pIosData->offCurPos = 0;
+ pIosData->fEndOfStream = false;
+ pIosData->fSeekable = pThis->hVfsFile != NIL_RTVFSFILE;
+ pIosData->uHashState = RTZIPXAR_HASH_PENDING;
+ pIosData->cbDigested = 0;
+ rtZipXarHashInit(&pIosData->CtxArchived, pIosData->DataAttr.uHashFunArchived);
+ rtZipXarHashInit(&pIosData->CtxExtracted, pIosData->DataAttr.uHashFunExtracted);
+
+ pIosData->hVfsIos = pThis->hVfsIos;
+ RTVfsIoStrmRetain(pThis->hVfsIos);
+
+ if ( pIosData->DataAttr.enmEncoding != RTZIPXARENCODING_STORE
+ && pIosData->DataAttr.enmEncoding != RTZIPXARENCODING_UNSUPPORTED)
+ {
+ /*
+ * We need to set up a decompression chain.
+ */
+ RTVFSIOSTREAM hVfsIosDecomp;
+ PRTZIPXARDECOMPIOS pIosDecompData;
+ rc = RTVfsNewIoStream(&g_rtZipXarFssDecompIosOps,
+ sizeof(*pIosDecompData),
+ RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
+ NIL_RTVFS,
+ NIL_RTVFSLOCK,
+ &hVfsIosDecomp,
+ (void **)&pIosDecompData);
+ if (RT_FAILURE(rc))
+ {
+ RTVfsIoStrmRelease(hVfsIosRaw);
+ return pThis->rcFatal = rc;
+ }
+
+ pIosDecompData->hVfsIosDecompressor = NIL_RTVFSIOSTREAM;
+ pIosDecompData->hVfsIosRaw = hVfsIosRaw;
+ pIosDecompData->pIosRaw = pIosData;
+ pIosDecompData->offCurPos = 0;
+ pIosDecompData->uHashFunExtracted = DataAttr.uHashFunExtracted;
+ pIosDecompData->uHashState = RTZIPXAR_HASH_PENDING;
+ rtZipXarHashInit(&pIosDecompData->CtxExtracted, pIosDecompData->uHashFunExtracted);
+ pIosDecompData->DigestExtracted = DataAttr.DigestExtracted;
+
+ /* Tell the raw end to only hash the archived data. */
+ pIosData->DataAttr.uHashFunExtracted = XAR_HASH_NONE;
+
+ /*
+ * Hook up the decompressor.
+ */
+ switch (DataAttr.enmEncoding)
+ {
+ case RTZIPXARENCODING_GZIP:
+ /* Must allow zlib header, all examples I've got seems
+ to be using it rather than the gzip one. Makes
+ sense as there is no need to repeat the file name
+ and the attributes. */
+ rc = RTZipGzipDecompressIoStream(hVfsIosRaw, RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR,
+ &pIosDecompData->hVfsIosDecompressor);
+ break;
+ default:
+ rc = VERR_INTERNAL_ERROR_5;
+ break;
+ }
+ if (RT_FAILURE(rc))
+ {
+ RTVfsIoStrmRelease(hVfsIosDecomp);
+ return pThis->rcFatal = rc;
+ }
+
+ /* What to return. */
+ hVfsObj = RTVfsObjFromIoStream(hVfsIosDecomp);
+ RTVfsIoStrmRelease(hVfsIosDecomp);
+ }
+ else
+ {
+ /* Try avoid double content hashing. */
+ if (pIosData->DataAttr.uHashFunArchived == pIosData->DataAttr.uHashFunExtracted)
+ pIosData->DataAttr.uHashFunExtracted = XAR_HASH_NONE;
+
+ /* What to return. */
+ hVfsObj = RTVfsObjFromIoStream(hVfsIosRaw);
+ RTVfsIoStrmRelease(hVfsIosRaw);
+ }
+ enmType = RTVFSOBJTYPE_IO_STREAM;
+ }
+ }
+ else if (!strcmp(pszType, "directory"))
+ {
+ PRTZIPXARBASEOBJ pBaseObjData;
+ rc = RTVfsNewBaseObj(&g_rtZipXarFssBaseObjOps,
+ sizeof(*pBaseObjData),
+ NIL_RTVFS,
+ NIL_RTVFSLOCK,
+ &hVfsObj,
+ (void **)&pBaseObjData);
+ if (RT_FAILURE(rc))
+ return pThis->rcFatal = rc;
+
+ pBaseObjData->pFileElem = pCurFile;
+ pBaseObjData->fModeType = RTFS_TYPE_DIRECTORY;
+
+ enmType = RTVFSOBJTYPE_BASE;
+ }
+ else if (!strcmp(pszType, "symlink"))
+ {
+ RTVFSSYMLINK hVfsSym;
+ PRTZIPXARBASEOBJ pBaseObjData;
+ rc = RTVfsNewSymlink(&g_rtZipXarFssSymOps,
+ sizeof(*pBaseObjData),
+ NIL_RTVFS,
+ NIL_RTVFSLOCK,
+ &hVfsSym,
+ (void **)&pBaseObjData);
+ if (RT_FAILURE(rc))
+ return pThis->rcFatal = rc;
+
+ pBaseObjData->pFileElem = pCurFile;
+ pBaseObjData->fModeType = RTFS_TYPE_SYMLINK;
+
+ enmType = RTVFSOBJTYPE_SYMLINK;
+ hVfsObj = RTVfsObjFromSymlink(hVfsSym);
+ RTVfsSymlinkRelease(hVfsSym);
+ }
+ else
+ return pThis->rcFatal = VERR_XAR_UNKNOWN_FILE_TYPE;
+
+ /*
+ * Set the return data and we're done.
+ */
+ if (ppszName)
+ {
+ /* Figure the length. */
+ size_t const cbCurName = strlen(pszName) + 1;
+ size_t cbFullName = cbCurName;
+ const xml::ElementNode *pAncestor = pCurFile;
+ uint32_t cLeft = pThis->XarReader.cCurDepth;
+ while (cLeft-- > 0)
+ {
+ pAncestor = (const xml::ElementNode *)pAncestor->getParent(); Assert(pAncestor);
+ const char *pszAncestorName = pAncestor->findChildElementValueP("name"); Assert(pszAncestorName);
+ cbFullName += strlen(pszAncestorName) + 1;
+ }
+
+ /* Allocate a buffer. */
+ char *psz = *ppszName = RTStrAlloc(cbFullName);
+ if (!psz)
+ {
+ RTVfsObjRelease(hVfsObj);
+ return VERR_NO_STR_MEMORY;
+ }
+
+ /* Construct it, from the end. */
+ psz += cbFullName;
+ psz -= cbCurName;
+ memcpy(psz, pszName, cbCurName);
+
+ pAncestor = pCurFile;
+ cLeft = pThis->XarReader.cCurDepth;
+ while (cLeft-- > 0)
+ {
+ pAncestor = (const xml::ElementNode *)pAncestor->getParent(); Assert(pAncestor);
+ const char *pszAncestorName = pAncestor->findChildElementValueP("name"); Assert(pszAncestorName);
+ *--psz = '/';
+ size_t cchAncestorName = strlen(pszAncestorName);
+ psz -= cchAncestorName;
+ memcpy(psz, pszAncestorName, cchAncestorName);
+ }
+ Assert(*ppszName == psz);
+ }
+
+ if (phVfsObj)
+ {
+ RTVfsObjRetain(hVfsObj);
+ *phVfsObj = hVfsObj;
+ }
+
+ if (penmType)
+ *penmType = enmType;
+
+ return VINF_SUCCESS;
+}
+
+
+
+/**
+ * Xar filesystem stream operations.
+ */
+static const RTVFSFSSTREAMOPS rtZipXarFssOps =
+{
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_FS_STREAM,
+ "XarFsStream",
+ rtZipXarFss_Close,
+ rtZipXarFss_QueryInfo,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSFSSTREAMOPS_VERSION,
+ 0,
+ rtZipXarFss_Next,
+ RTVFSFSSTREAMOPS_VERSION
+};
+
+
+
+/**
+ * TOC validation part 2.
+ *
+ * Will advance the input stream past the TOC hash and signature data.
+ *
+ * @returns IPRT status code.
+ * @param pThis The FS stream instance being created.
+ * @param pXarHdr The XAR header.
+ * @param pTocDigest The TOC input data digest.
+ */
+static int rtZipXarValidateTocPart2(PRTZIPXARFSSTREAM pThis, PCXARHEADER pXarHdr, PCRTZIPXARHASHDIGEST pTocDigest)
+{
+ int rc;
+
+ /*
+ * Check that the hash function in the TOC matches the one in the XAR header.
+ */
+ const xml::ElementNode *pChecksumElem = pThis->XarReader.pToc->findChildElement("checksum");
+ if (pChecksumElem)
+ {
+ const xml::AttributeNode *pAttr = pChecksumElem->findAttribute("style");
+ if (!pAttr)
+ return VERR_XAR_BAD_CHECKSUM_ELEMENT;
+
+ const char *pszStyle = pAttr->getValue();
+ if (!pszStyle)
+ return VERR_XAR_BAD_CHECKSUM_ELEMENT;
+
+ uint8_t uHashFunction;
+ rc = rtZipXarParseChecksumStyle(pszStyle, &uHashFunction);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (uHashFunction != pThis->uHashFunction)
+ return VERR_XAR_HASH_FUNCTION_MISMATCH;
+
+ /*
+ * Verify the checksum if we got one.
+ */
+ if (pThis->uHashFunction != XAR_HASH_NONE)
+ {
+ RTFOFF offChecksum;
+ RTFOFF cbChecksum;
+ rc = rtZipXarGetOffsetSizeLengthFromElem(pChecksumElem, &offChecksum, &cbChecksum, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (cbChecksum != (RTFOFF)pThis->cbHashDigest)
+ return VERR_XAR_BAD_DIGEST_LENGTH;
+ if (offChecksum != 0 && pThis->hVfsFile == NIL_RTVFSFILE)
+ return VERR_XAR_NOT_STREAMBLE_ELEMENT_ORDER;
+
+ RTZIPXARHASHDIGEST StoredDigest;
+ rc = RTVfsIoStrmReadAt(pThis->hVfsIos, pThis->offZero + offChecksum, &StoredDigest, pThis->cbHashDigest,
+ true /*fBlocking*/, NULL /*pcbRead*/);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (memcmp(&StoredDigest, pTocDigest, pThis->cbHashDigest))
+ return VERR_XAR_TOC_DIGEST_MISMATCH;
+ }
+ }
+ else if (pThis->uHashFunction != XAR_HASH_NONE)
+ return VERR_XAR_BAD_CHECKSUM_ELEMENT;
+
+ /*
+ * Check the signature, if we got one.
+ */
+ /** @todo signing. */
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Reads and validates the table of content.
+ *
+ * @returns IPRT status code.
+ * @param hVfsIosIn The input stream.
+ * @param pXarHdr The XAR header.
+ * @param pDoc The TOC XML document.
+ * @param ppTocElem Where to return the pointer to the TOC element on
+ * success.
+ * @param pTocDigest Where to return the TOC digest on success.
+ */
+static int rtZipXarReadAndValidateToc(RTVFSIOSTREAM hVfsIosIn, PCXARHEADER pXarHdr,
+ xml::Document *pDoc, xml::ElementNode const **ppTocElem, PRTZIPXARHASHDIGEST pTocDigest)
+{
+ /*
+ * Decompress it, calculating the hash while doing so.
+ */
+ char *pszOutput = (char *)RTMemTmpAlloc(pXarHdr->cbTocUncompressed + 1);
+ if (!pszOutput)
+ return VERR_NO_TMP_MEMORY;
+ int rc = VERR_NO_TMP_MEMORY;
+ void *pvInput = RTMemTmpAlloc(pXarHdr->cbTocCompressed);
+ if (pvInput)
+ {
+ rc = RTVfsIoStrmRead(hVfsIosIn, pvInput, pXarHdr->cbTocCompressed, true /*fBlocking*/, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rtZipXarCalcHash(pXarHdr->uHashFunction, pvInput, pXarHdr->cbTocCompressed, pTocDigest);
+
+ size_t cbActual;
+ rc = RTZipBlockDecompress(RTZIPTYPE_ZLIB, 0 /*fFlags*/,
+ pvInput, pXarHdr->cbTocCompressed, NULL,
+ pszOutput, pXarHdr->cbTocUncompressed, &cbActual);
+ if (RT_SUCCESS(rc) && cbActual != pXarHdr->cbTocUncompressed)
+ rc = VERR_XAR_TOC_UNCOMP_SIZE_MISMATCH;
+ }
+ RTMemTmpFree(pvInput);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ pszOutput[pXarHdr->cbTocUncompressed] = '\0';
+
+ /*
+ * Parse the TOC (XML document) and do some basic validations.
+ */
+ size_t cchToc = strlen(pszOutput);
+ if ( cchToc == pXarHdr->cbTocUncompressed
+ || cchToc + 1 == pXarHdr->cbTocUncompressed)
+ {
+ rc = RTStrValidateEncoding(pszOutput);
+ if (RT_SUCCESS(rc))
+ {
+ xml::XmlMemParser Parser;
+ try
+ {
+ Parser.read(pszOutput, cchToc, RTCString("xar-toc.xml"), *pDoc);
+ }
+ catch (xml::XmlError Err)
+ {
+ rc = VERR_XAR_TOC_XML_PARSE_ERROR;
+ }
+ catch (...)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ xml::ElementNode const *pRootElem = pDoc->getRootElement();
+ xml::ElementNode const *pTocElem = NULL;
+ if (pRootElem && pRootElem->nameEquals("xar"))
+ pTocElem = pRootElem ? pRootElem->findChildElement("toc") : NULL;
+ if (pTocElem)
+ {
+#ifndef USE_STD_LIST_FOR_CHILDREN
+ Assert(pRootElem->getParent() == NULL);
+ Assert(pTocElem->getParent() == pRootElem);
+ if ( !pTocElem->getNextSibiling()
+ && !pTocElem->getPrevSibiling())
+#endif
+ {
+ /*
+ * Further parsing and validation is done after the
+ * caller has created an file system stream instance.
+ */
+ *ppTocElem = pTocElem;
+
+ RTMemTmpFree(pszOutput);
+ return VINF_SUCCESS;
+ }
+
+ rc = VERR_XML_TOC_ELEMENT_HAS_SIBLINGS;
+ }
+ else
+ rc = VERR_XML_TOC_ELEMENT_MISSING;
+ }
+ }
+ else
+ rc = VERR_XAR_TOC_UTF8_ENCODING;
+ }
+ else
+ rc = VERR_XAR_TOC_STRLEN_MISMATCH;
+ }
+
+ RTMemTmpFree(pszOutput);
+ return rc;
+}
+
+
+/**
+ * Reads and validates the XAR header.
+ *
+ * @returns IPRT status code.
+ * @param hVfsIosIn The input stream.
+ * @param pXarHdr Where to return the XAR header in host byte order.
+ */
+static int rtZipXarReadAndValidateHeader(RTVFSIOSTREAM hVfsIosIn, PXARHEADER pXarHdr)
+{
+ /*
+ * Read it and check the signature.
+ */
+ int rc = RTVfsIoStrmRead(hVfsIosIn, pXarHdr, sizeof(*pXarHdr), true /*fBlocking*/, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (pXarHdr->u32Magic != XAR_HEADER_MAGIC)
+ return VERR_XAR_WRONG_MAGIC;
+
+ /*
+ * Correct the byte order.
+ */
+ pXarHdr->cbHeader = RT_BE2H_U16(pXarHdr->cbHeader);
+ pXarHdr->uVersion = RT_BE2H_U16(pXarHdr->uVersion);
+ pXarHdr->cbTocCompressed = RT_BE2H_U64(pXarHdr->cbTocCompressed);
+ pXarHdr->cbTocUncompressed = RT_BE2H_U64(pXarHdr->cbTocUncompressed);
+ pXarHdr->uHashFunction = RT_BE2H_U32(pXarHdr->uHashFunction);
+
+ /*
+ * Validate the header.
+ */
+ if (pXarHdr->uVersion > XAR_HEADER_VERSION)
+ return VERR_XAR_UNSUPPORTED_VERSION;
+ if (pXarHdr->cbHeader < sizeof(XARHEADER))
+ return VERR_XAR_BAD_HDR_SIZE;
+ if (pXarHdr->uHashFunction > XAR_HASH_MAX)
+ return VERR_XAR_UNSUPPORTED_HASH_FUNCTION;
+ if (pXarHdr->cbTocUncompressed < 16)
+ return VERR_XAR_TOC_TOO_SMALL;
+ if (pXarHdr->cbTocUncompressed > _4M)
+ return VERR_XAR_TOC_TOO_BIG;
+ if (pXarHdr->cbTocCompressed > _4M)
+ return VERR_XAR_TOC_TOO_BIG_COMPRESSED;
+
+ /*
+ * Skip over bytes we don't understand (could be padding).
+ */
+ if (pXarHdr->cbHeader > sizeof(XARHEADER))
+ {
+ rc = RTVfsIoStrmSkip(hVfsIosIn, pXarHdr->cbHeader - sizeof(XARHEADER));
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTZipXarFsStreamFromIoStream(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);
+
+ RTFOFF const offStart = RTVfsIoStrmTell(hVfsIosIn);
+ AssertReturn(offStart >= 0, (int)offStart);
+
+ uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosIn);
+ AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
+
+ /*
+ * Read and validate the header, then uncompress the TOC.
+ */
+ XARHEADER XarHdr;
+ int rc = rtZipXarReadAndValidateHeader(hVfsIosIn, &XarHdr);
+ if (RT_SUCCESS(rc))
+ {
+ xml::Document *pDoc = NULL;
+ try { pDoc = new xml::Document(); }
+ catch (...) { }
+ if (pDoc)
+ {
+ RTZIPXARHASHDIGEST TocDigest;
+ xml::ElementNode const *pTocElem = NULL;
+ rc = rtZipXarReadAndValidateToc(hVfsIosIn, &XarHdr, pDoc, &pTocElem, &TocDigest);
+ if (RT_SUCCESS(rc))
+ {
+ size_t offZero = RTVfsIoStrmTell(hVfsIosIn);
+ if (offZero > 0)
+ {
+ /*
+ * Create a file system stream before we continue the parsing.
+ */
+ PRTZIPXARFSSTREAM pThis;
+ RTVFSFSSTREAM hVfsFss;
+ rc = RTVfsNewFsStream(&rtZipXarFssOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFss, (void **)&pThis);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->hVfsIos = hVfsIosIn;
+ pThis->hVfsFile = RTVfsIoStrmToFile(hVfsIosIn);
+ pThis->offStart = offStart;
+ pThis->offZero = offZero;
+ pThis->uHashFunction = (uint8_t)XarHdr.uHashFunction;
+ switch (pThis->uHashFunction)
+ {
+ case XAR_HASH_MD5: pThis->cbHashDigest = sizeof(TocDigest.abMd5); break;
+ case XAR_HASH_SHA1: pThis->cbHashDigest = sizeof(TocDigest.abSha1); break;
+ default: pThis->cbHashDigest = 0; break;
+ }
+ pThis->fEndOfStream = false;
+ pThis->rcFatal = VINF_SUCCESS;
+ pThis->XarReader.pDoc = pDoc;
+ pThis->XarReader.pToc = pTocElem;
+ pThis->XarReader.pCurFile = 0;
+ pThis->XarReader.cCurDepth = 0;
+
+ /*
+ * Next validation step.
+ */
+ rc = rtZipXarValidateTocPart2(pThis, &XarHdr, &TocDigest);
+ if (RT_SUCCESS(rc))
+ {
+ *phVfsFss = hVfsFss;
+ return VINF_SUCCESS;
+ }
+
+ RTVfsFsStrmRelease(hVfsFss);
+ return rc;
+ }
+ }
+ else
+ rc = (int)offZero;
+ }
+ delete pDoc;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ RTVfsIoStrmRelease(hVfsIosIn);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/zip/zip.cpp b/src/VBox/Runtime/common/zip/zip.cpp
index 8abc2602..fa8d140f 100644
--- a/src/VBox/Runtime/common/zip/zip.cpp
+++ b/src/VBox/Runtime/common/zip/zip.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2011 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -574,7 +574,8 @@ static DECLCALLBACK(int) rtZipZlibCompInit(PRTZIPCOMP pZip, RTZIPLEVEL enmLevel)
static DECLCALLBACK(int) rtZipZlibDecompress(PRTZIPDECOMP pZip, void *pvBuf, size_t cbBuf, size_t *pcbWritten)
{
pZip->u.Zlib.next_out = (Bytef *)pvBuf;
- pZip->u.Zlib.avail_out = (uInt)cbBuf; Assert(pZip->u.Zlib.avail_out == cbBuf);
+ pZip->u.Zlib.avail_out = (uInt)cbBuf;
+ Assert(pZip->u.Zlib.avail_out == cbBuf);
/*
* Be greedy reading input, even if no output buffer is left. It's possible
@@ -1915,6 +1916,46 @@ RTDECL(int) RTZipBlockDecompress(RTZIPTYPE enmType, uint32_t fFlags,
}
case RTZIPTYPE_ZLIB:
+ {
+#ifdef RTZIP_USE_ZLIB
+ AssertReturn(cbSrc == (uInt)cbSrc, VERR_TOO_MUCH_DATA);
+ AssertReturn(cbDst == (uInt)cbDst, VERR_OUT_OF_RANGE);
+
+ z_stream ZStrm;
+ RT_ZERO(ZStrm);
+ ZStrm.next_in = (Bytef *)pvSrc;
+ ZStrm.avail_in = (uInt)cbSrc;
+ ZStrm.next_out = (Bytef *)pvDst;
+ ZStrm.avail_out = (uInt)cbDst;
+
+ int rc = inflateInit(&ZStrm);
+ if (RT_UNLIKELY(rc != Z_OK))
+ return zipErrConvertFromZlib(rc, false /*fCompressing*/);
+ rc = inflate(&ZStrm, Z_FINISH);
+ if (rc != Z_STREAM_END)
+ {
+ inflateEnd(&ZStrm);
+ if ((rc == Z_BUF_ERROR && ZStrm.avail_in == 0) || rc == Z_NEED_DICT)
+ return VERR_ZIP_CORRUPTED;
+ if (rc == Z_BUF_ERROR)
+ return VERR_BUFFER_OVERFLOW;
+ AssertReturn(rc < Z_OK, VERR_GENERAL_FAILURE);
+ return zipErrConvertFromZlib(rc, false /*fCompressing*/);
+ }
+ rc = inflateEnd(&ZStrm);
+ if (rc != Z_OK)
+ return zipErrConvertFromZlib(rc, false /*fCompressing*/);
+
+ if (pcbSrcActual)
+ *pcbSrcActual = ZStrm.avail_in - cbSrc;
+ if (pcbDstActual)
+ *pcbDstActual = ZStrm.total_out;
+ break;
+#else
+ return VERR_NOT_SUPPORTED;
+#endif
+ }
+
case RTZIPTYPE_BZLIB:
return VERR_NOT_SUPPORTED;