/* $Id$ */
/** @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;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/******************************************************************************
* Header Files *
******************************************************************************/
#include <iprt/vfslowlevel.h>
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** @name Hash state
* @{ */
#define RTZIPXAR_HASH_PENDING 0
/** @} */
/*******************************************************************************
* 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
{
/** Pointer to a XAR hash digest union. */
/** Pointer to a const XAR hash digest union. */
/**
* Hash context union.
*/
typedef union RTZIPXARHASHCTX
{
/** Pointer to a hash context union. */
/**
* XAR reader instance data.
*/
typedef struct RTZIPXARREADER
{
/** The TOC XML element. */
/** The TOC XML document. */
/** The current file. */
/** The depth of the current file, with 0 being the root level. */
/** Pointer to the XAR reader instance data. */
/**
* Xar directory, character device, block device, fifo socket or symbolic link.
*/
typedef struct RTZIPXARBASEOBJ
{
/** The file TOC element. */
/** RTFS_TYPE_XXX value for the object. */
/** Pointer to a XAR filesystem stream base object. */
/**
* XAR data encoding.
*/
typedef enum 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. */
/** The size of the archived data. */
/** The size of the extracted data. */
/** The encoding of the archived ata. */
/** The hash function used for the archived data. */
/** The hash function used for the extracted data. */
/** The digest of the archived data. */
/** The digest of the extracted data. */
/** Pointer to XAR data stream attributes. */
/**
* Xar file represented as a VFS I/O stream.
*/
typedef struct RTZIPXARIOSTREAM
{
/** The basic XAR object data. */
/** The attributes of the primary data stream. */
/** The current file position in the archived file. */
/** The input I/O stream. */
/** 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. */
/** 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. */
/** The digest of the archived data. */
/** The digest of the extracted data. */
/** Pointer to a the private data of a XAR file I/O stream. */
/**
* Xar file represented as a VFS file.
*/
typedef struct RTZIPXARFILE
{
/** The XAR I/O stream data. */
/** The input file. */
} RTZIPXARFILE;
/** Pointer to the private data of a XAR file. */
/**
* 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. */
/** The raw XAR I/O stream. */
/** Pointer to the raw XAR I/O stream instance data. */
/** The current file position in the archived file. */
/** The hash function to use on the extracted data. */
/** Hash state on the extracted data. */
/** The digest of the extracted data. */
/** The expected digest of the extracted data. */
/** Pointer to the private data of a XAR decompressed I/O stream. */
/**
* Xar filesystem stream private data.
*/
typedef struct RTZIPXARFSSTREAM
{
/** The input I/O stream. */
/** The input file, if the stream is actually a file. */
/** The start offset in the input I/O stream. */
/** The zero offset in the file which all others are relative to. */
/** The hash function we're using (XAR_HASH_XXX). */
/** The size of the digest produced by the hash function we're using. */
/** 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. */
/** Pointer to a the private data of a XAR filesystem stream. */
/**
* 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:
break;
case XAR_HASH_MD5:
break;
default:
break;
}
}
/**
* Initializes a hash context.
*
* @param pCtx Pointer to the context union.
* @param uHashFunction The hash function to use.
*/
{
switch (uHashFunction)
{
case XAR_HASH_SHA1:
break;
case XAR_HASH_MD5:
break;
default:
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:
break;
case XAR_HASH_MD5:
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:
break;
case XAR_HASH_MD5:
break;
default:
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:
case XAR_HASH_MD5:
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.
*/
{
/*
* The offset.
*/
if (!pElem)
return VERR_XAR_MISSING_OFFSET_ELEMENT;
if (!pszValue)
return VERR_XAR_BAD_OFFSET_ELEMENT;
if ( RT_FAILURE(rc)
|| rc == VWRN_NUMBER_TOO_BIG
|| *poff < 0)
return VERR_XAR_BAD_OFFSET_ELEMENT;
/*
* The 'size' stored in the archive.
*/
if (!pElem)
return VERR_XAR_MISSING_SIZE_ELEMENT;
if (!pszValue)
return VERR_XAR_BAD_SIZE_ELEMENT;
if ( RT_FAILURE(rc)
|| rc == VWRN_NUMBER_TOO_BIG
|| *pcbSize < 0)
return VERR_XAR_BAD_SIZE_ELEMENT;
/*
* The 'length' of the uncompressed data. Not present for checksums, so
* the caller might not want it.
*/
if (pcbLength)
{
if (!pElem)
return VERR_XAR_MISSING_LENGTH_ELEMENT;
if (!pszValue)
return VERR_XAR_BAD_LENGTH_ELEMENT;
if ( RT_FAILURE(rc)
|| rc == VWRN_NUMBER_TOO_BIG
|| *pcbLength < 0)
return VERR_XAR_BAD_LENGTH_ELEMENT;
}
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.
*/
{
if ( cchStyle == 4
else if ( cchStyle == 3
else if ( cchStyle == 4
else
{
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.
*/
{
/* Default is no checksum. */
if (!pChecksumElem)
return VINF_SUCCESS;
/* The style. */
if (!pszStyle)
return VERR_XAR_BAD_CHECKSUM_ELEMENT;
if (RT_FAILURE(rc))
return rc;
if (*puHashFunction == XAR_HASH_NONE)
return VINF_SUCCESS;
/* The digest. */
if (!pszDigest)
return VERR_XAR_BAD_CHECKSUM_ELEMENT;
switch (*puHashFunction)
{
case XAR_HASH_SHA1:
break;
case XAR_HASH_MD5:
break;
default:
}
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.
*/
if (!pDataElem)
return VERR_XAR_MISSING_DATA_ELEMENT;
/*
* Checksums.
*/
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
/*
* The encoding.
*/
if (!pszEncoding)
return VERR_XAR_NO_ENCODING;
else
/*
* The data offset and the compressed and uncompressed sizes.
*/
if (RT_FAILURE(rc))
return rc;
/* No zero padding or other alignment crap, please. */
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)
{
if (pszValue)
{
return true;
return false;
}
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.
*/
if (pChild)
{
*pcCurDepth += 1;
return pChild;
}
/*
* Siblings and ancestor siblings.
*/
for (;;)
{
return pSibling;
if (*pcCurDepth == 0)
break;
*pcCurDepth -= 1;
}
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}
*/
{
/* Currently there is nothing we really have to do here. */
return VINF_SUCCESS;
}
/**
* @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
*/
static DECLCALLBACK(int) rtZipXarFssBaseObj_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
{
/*
* Get the common data.
*/
/* Sizes. */
{
}
else
{
pObjInfo->cbAllocated = 0;
}
/* The file mode. */
return VERR_XAR_BAD_FILE_MODE;
return VERR_XAR_BAD_FILE_MODE;
/* File times. */
return VERR_XAR_BAD_FILE_TIMESTAMP;
return VERR_XAR_BAD_FILE_TIMESTAMP;
return VERR_XAR_BAD_FILE_TIMESTAMP;
pObjInfo->BirthTime = RTTimeSpecGetNano(&pObjInfo->AccessTime) <= RTTimeSpecGetNano(&pObjInfo->ChangeTime)
/*
* Copy the desired data.
*/
switch (enmAddAttr)
{
case RTFSOBJATTRADD_NOTHING:
case RTFSOBJATTRADD_UNIX:
return VERR_XAR_BAD_FILE_UID;
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;
break;
{
return VERR_XAR_BAD_FILE_UID;
if (pszUser)
else
break;
}
{
return VERR_XAR_BAD_FILE_GID;
if (pszGroup)
else
break;
}
case RTFSOBJATTRADD_EASIZE:
break;
default:
return VERR_NOT_SUPPORTED;
}
return VINF_SUCCESS;
}
/**
* Xar filesystem base object operations.
*/
{
"XarFsStream::Obj",
};
/**
* @interface_method_impl{RTVFSOBJOPS,pfnClose}
*/
{
}
/**
* @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
*/
static DECLCALLBACK(int) rtZipXarFssIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
{
}
/**
* @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
*/
static DECLCALLBACK(int) rtZipXarFssIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
{
/*
* Fend of reads beyond the end of the stream here. If
*/
if (off == -1)
return VERR_EOF;
if (pThis->fEndOfStream)
{
return VERR_SEEK_ON_DEVICE;
pThis->fEndOfStream = false;
}
{
if (!pcbRead)
return VERR_EOF;
}
/*
* Do the reading.
*/
if (!pcbRead)
pcbRead = &cbReadStack;
/* Feed the hashes. */
{
{
rtZipXarHashUpdate(&pThis->CtxArchived, pThis->DataAttr.uHashFunArchived, pSgBuf->paSegs[0].pvSeg, cbActuallyRead);
rtZipXarHashUpdate(&pThis->CtxExtracted, pThis->DataAttr.uHashFunExtracted, pSgBuf->paSegs[0].pvSeg, cbActuallyRead);
}
{
}
}
/* Update the file position. */
/*
* Check for end of stream, also check the hash.
*/
{
pThis->fEndOfStream = true;
/* Check hash. */
{
if (rtZipXarHashIsEqual(pThis->DataAttr.uHashFunArchived, &Digest, &pThis->DataAttr.DigestArchived))
{
if (rtZipXarHashIsEqual(pThis->DataAttr.uHashFunExtracted, &Digest, &pThis->DataAttr.DigestExtracted))
else
{
}
}
else
{
}
}
}
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. */
return VERR_ACCESS_DENIED;
}
/**
* @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
*/
{
/* It's a read only stream, nothing dirty to flush. */
return VINF_SUCCESS;
}
/**
* @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
*/
static DECLCALLBACK(int) rtZipXarFssIos_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
{
/* When we've reached the end, read will be set to indicate it. */
if ( (fEvents & RTPOLL_EVT_READ)
&& pThis->fEndOfStream)
{
if (RT_SUCCESS(rc))
else
return VINF_SUCCESS;
}
}
/**
* @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
*/
{
return VINF_SUCCESS;
}
/**
* Xar I/O stream operations.
*/
{
{ /* Obj */
"XarFsStream::IoStream",
},
0,
NULL /*Skip*/,
NULL /*ZeroFill*/,
};
/**
* @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
*/
{
return VERR_NOT_SUPPORTED;
}
/**
* @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
*/
static DECLCALLBACK(int) rtZipXarFssFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
{
return VERR_NOT_SUPPORTED;
}
/**
* @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
*/
{
return VERR_NOT_SUPPORTED;
}
/**
* @interface_method_impl{RTVFSFILEOPS,pfnSeek}
*/
static DECLCALLBACK(int) rtZipXarFssFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
{
/* Recalculate the request to RTFILE_SEEK_BEGIN. */
switch (uMethod)
{
case RTFILE_SEEK_BEGIN:
break;
case RTFILE_SEEK_CURRENT:
break;
case RTFILE_SEEK_END:
break;
default:
}
/* Do limit checks. */
if (offSeek < 0)
offSeek = 0;
/* Apply and return. */
if (poffActual)
*poffActual = offSeek;
return VINF_SUCCESS;
}
/**
* @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
*/
{
return VINF_SUCCESS;
}
/**
* Xar file operations.
*/
{
{ /* I/O stream */
{ /* Obj */
"XarFsStream::File",
},
NULL /*Skip*/,
NULL /*ZeroFill*/,
},
0,
{ /* ObjSet */
},
};
/**
* @interface_method_impl{RTVFSOBJOPS,pfnClose}
*/
{
return rc;
}
/**
* @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
*/
static DECLCALLBACK(int) rtZipXarFssDecompIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
{
return rc;
}
/**
* @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
*/
static DECLCALLBACK(int) rtZipXarFssDecompIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
{
/*
* Enforce the cbDataExtracted limit.
*/
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,
if (RT_FAILURE(rc))
return rc;
/*
* Hash the data. When reaching the end match against the expected digest.
*/
rtZipXarHashUpdate(&pThis->CtxExtracted, pThis->uHashFunExtracted, pSgBuf->paSegs[0].pvSeg, cbActuallyRead);
{
{
{
else
{
}
}
}
else
/* Ensure that the raw stream is also at the end so that both
message digests are checked. */
if (RT_SUCCESS(rc))
{
}
}
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. */
return VERR_ACCESS_DENIED;
}
/**
* @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
*/
{
/* It's a read only stream, nothing dirty to flush. */
return VINF_SUCCESS;
}
/**
* @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
*/
static DECLCALLBACK(int) rtZipXarFssDecompIos_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
{
}
/**
* @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
*/
{
return VINF_SUCCESS;
}
/**
* Xar I/O stream operations.
*/
{
{ /* Obj */
"XarFsStream::DecompIoStream",
},
0,
NULL /*Skip*/,
NULL /*ZeroFill*/,
};
/**
* @interface_method_impl{RTVFSOBJOPS,pfnClose}
*/
{
return rtZipXarFssBaseObj_Close(pThis);
}
/**
* @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
*/
static DECLCALLBACK(int) rtZipXarFssSym_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
{
}
/**
* @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
*/
{
return VERR_ACCESS_DENIED;
}
/**
* @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
*/
static DECLCALLBACK(int) rtZipXarFssSym_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
{
return VERR_ACCESS_DENIED;
}
/**
* @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
*/
{
return VERR_ACCESS_DENIED;
}
/**
* @interface_method_impl{RTVFSSYMLINKOPS,pfnRead}
*/
{
#if 0
#else
return VERR_NOT_IMPLEMENTED;
#endif
}
/**
* Xar symbolic (and hardlink) operations.
*/
{
{ /* Obj */
"XarFsStream::Symlink",
},
0,
{ /* ObjSet */
},
};
/**
* @interface_method_impl{RTVFSOBJOPS,pfnClose}
*/
{
return VINF_SUCCESS;
}
/**
* @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
*/
static DECLCALLBACK(int) rtZipXarFss_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
{
/* Take the lazy approach here, with the sideffect of providing some info
that is actually kind of useful. */
}
/**
* @interface_method_impl{RTVFSFSSTREAMOPS,pfnNext}
*/
static DECLCALLBACK(int) rtZipXarFss_Next(void *pvThis, char **ppszName, RTVFSOBJTYPE *penmType, PRTVFSOBJ phVfsObj)
{
/*
* Check if we've already reached the end in some way.
*/
if (pThis->fEndOfStream)
return VERR_EOF;
/*
* Get the next file element.
*/
if (pCurFile)
pThis->XarReader.pCurFile = pCurFile = rtZipXarGetNextFileElement(pCurFile, &pThis->XarReader.cCurDepth);
else if (!pThis->fEndOfStream)
{
}
if (!pCurFile)
{
pThis->fEndOfStream = true;
return VERR_EOF;
}
/*
* Retrive the fundamental attributes (elements actually).
*/
/*
* Validate the filename. Being a little too paranoid here, perhaps, wrt
* path separators and escapes...
*/
if ( !*pszName
/*
* Gather any additional attributes that are essential to the file type,
* then create the VFS object we're going to return.
*/
int rc;
{
if (RT_FAILURE(rc))
{
/*
* The input is seekable and the XAR file isn't compressed, so we
* can provide a seekable file to the user.
*/
sizeof(*pFileData),
&hVfsFile,
(void **)&pFileData);
if (RT_FAILURE(rc))
/* Try avoid double content hashing. */
}
else
{
sizeof(*pIosData),
(void **)&pIosData);
if (RT_FAILURE(rc))
pIosData->fEndOfStream = false;
pIosData->cbDigested = 0;
{
/*
* We need to set up a decompression chain.
*/
sizeof(*pIosDecompData),
(void **)&pIosDecompData);
if (RT_FAILURE(rc))
{
}
pIosDecompData->offCurPos = 0;
/* Tell the raw end to only hash the archived data. */
/*
* 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. */
break;
default:
break;
}
if (RT_FAILURE(rc))
{
}
/* What to return. */
}
else
{
/* Try avoid double content hashing. */
/* What to return. */
}
}
}
{
sizeof(*pBaseObjData),
&hVfsObj,
(void **)&pBaseObjData);
if (RT_FAILURE(rc))
}
{
sizeof(*pBaseObjData),
&hVfsSym,
(void **)&pBaseObjData);
if (RT_FAILURE(rc))
}
else
/*
* Set the return data and we're done.
*/
if (ppszName)
{
/* Figure the length. */
while (cLeft-- > 0)
{
}
/* Allocate a buffer. */
if (!psz)
{
return VERR_NO_STR_MEMORY;
}
/* Construct it, from the end. */
psz += cbFullName;
while (cLeft-- > 0)
{
*--psz = '/';
psz -= cchAncestorName;
}
}
if (phVfsObj)
{
}
if (penmType)
return VINF_SUCCESS;
}
/**
* Xar filesystem stream operations.
*/
{
{ /* Obj */
"XarFsStream",
},
0,
};
/**
* 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.
*/
if (pChecksumElem)
{
if (!pAttr)
return VERR_XAR_BAD_CHECKSUM_ELEMENT;
if (!pszStyle)
return VERR_XAR_BAD_CHECKSUM_ELEMENT;
if (RT_FAILURE(rc))
return rc;
return VERR_XAR_HASH_FUNCTION_MISMATCH;
/*
* Verify the checksum if we got one.
*/
{
if (RT_FAILURE(rc))
return rc;
return VERR_XAR_BAD_DIGEST_LENGTH;
rc = RTVfsIoStrmReadAt(pThis->hVfsIos, pThis->offZero + offChecksum, &StoredDigest, pThis->cbHashDigest,
if (RT_FAILURE(rc))
return rc;
return VERR_XAR_TOC_DIGEST_MISMATCH;
}
}
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.
*/
{
/*
* Decompress it, calculating the hash while doing so.
*/
if (!pszOutput)
return VERR_NO_TMP_MEMORY;
if (pvInput)
{
if (RT_SUCCESS(rc))
{
}
}
if (RT_SUCCESS(rc))
{
/*
* Parse the TOC (XML document) and do some basic validations.
*/
{
if (RT_SUCCESS(rc))
{
try
{
}
{
}
catch (...)
{
rc = VERR_NO_MEMORY;
}
if (RT_SUCCESS(rc))
{
if (pTocElem)
{
#ifndef USE_STD_LIST_FOR_CHILDREN
if ( !pTocElem->getNextSibiling()
&& !pTocElem->getPrevSibiling())
#endif
{
/*
* Further parsing and validation is done after the
* caller has created an file system stream instance.
*/
return VINF_SUCCESS;
}
}
else
}
}
else
}
else
}
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.
*/
{
/*
* Read it and check the signature.
*/
if (RT_FAILURE(rc))
return rc;
return VERR_XAR_WRONG_MAGIC;
/*
* Correct the byte order.
*/
/*
* Validate the header.
*/
return VERR_XAR_UNSUPPORTED_VERSION;
return VERR_XAR_BAD_HDR_SIZE;
return VERR_XAR_TOC_TOO_SMALL;
return VERR_XAR_TOC_TOO_BIG;
return VERR_XAR_TOC_TOO_BIG_COMPRESSED;
/*
* Skip over bytes we don't understand (could be padding).
*/
{
if (RT_FAILURE(rc))
return rc;
}
return VINF_SUCCESS;
}
RTDECL(int) RTZipXarFsStreamFromIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
{
/*
* Input validation.
*/
/*
* Read and validate the header, then uncompress the TOC.
*/
if (RT_SUCCESS(rc))
{
catch (...) { }
if (pDoc)
{
if (RT_SUCCESS(rc))
{
if (offZero > 0)
{
/*
* Create a file system stream before we continue the parsing.
*/
rc = RTVfsNewFsStream(&rtZipXarFssOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFss, (void **)&pThis);
if (RT_SUCCESS(rc))
{
switch (pThis->uHashFunction)
{
default: pThis->cbHashDigest = 0; break;
}
pThis->fEndOfStream = false;
/*
* Next validation step.
*/
if (RT_SUCCESS(rc))
{
return VINF_SUCCESS;
}
return rc;
}
}
else
}
delete pDoc;
}
else
rc = VERR_NO_MEMORY;
}
return rc;
}