tarvfs.cpp revision e4bb2f8312bbd05cdb3c4c01406274f946e7b76c
05afe08870681beb0792f384475077c988916762vboxsync * IPRT - TAR Virtual Filesystem.
e64031e20c39650a7bc902a3e1aba613b9415deevboxsync * Copyright (C) 2010-2011 Oracle Corporation
05afe08870681beb0792f384475077c988916762vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
05afe08870681beb0792f384475077c988916762vboxsync * available from http://www.virtualbox.org. This file is free software;
05afe08870681beb0792f384475077c988916762vboxsync * you can redistribute it and/or modify it under the terms of the GNU
05afe08870681beb0792f384475077c988916762vboxsync * General Public License (GPL) as published by the Free Software
05afe08870681beb0792f384475077c988916762vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
05afe08870681beb0792f384475077c988916762vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
05afe08870681beb0792f384475077c988916762vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
05afe08870681beb0792f384475077c988916762vboxsync * The contents of this file may alternatively be used under the terms
05afe08870681beb0792f384475077c988916762vboxsync * of the Common Development and Distribution License Version 1.0
05afe08870681beb0792f384475077c988916762vboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
05afe08870681beb0792f384475077c988916762vboxsync * VirtualBox OSE distribution, in which case the provisions of the
05afe08870681beb0792f384475077c988916762vboxsync * CDDL are applicable instead of those of the GPL.
05afe08870681beb0792f384475077c988916762vboxsync * You may elect to license modified versions of this file under the
05afe08870681beb0792f384475077c988916762vboxsync * terms and conditions of either the GPL or the CDDL or both.
05afe08870681beb0792f384475077c988916762vboxsync/******************************************************************************
05afe08870681beb0792f384475077c988916762vboxsync * Header Files *
05afe08870681beb0792f384475077c988916762vboxsync ******************************************************************************/
05afe08870681beb0792f384475077c988916762vboxsync/*******************************************************************************
05afe08870681beb0792f384475077c988916762vboxsync* Structures and Typedefs *
05afe08870681beb0792f384475077c988916762vboxsync*******************************************************************************/
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * TAR reader state machine states.
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** Invalid state. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** Expecting the next file/dir/whatever entry. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** Expecting more zero headers or the end of the stream. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** Expecting a GNU long name. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** Expecting a GNU long link. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** Expecting a normal header or another GNU specific one. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** End of valid states (not included). */
05afe08870681beb0792f384475077c988916762vboxsync * Tar reader instance data.
05afe08870681beb0792f384475077c988916762vboxsync /** Zero header counter. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** The state machine state. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** The type of the previous TAR header. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** The type of the current TAR header. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** The current header. */
05afe08870681beb0792f384475077c988916762vboxsync /** The expected long name/link length (GNU). */
05afe08870681beb0792f384475077c988916762vboxsync /** The current long name/link length (GNU). */
05afe08870681beb0792f384475077c988916762vboxsync /** The name of the current object.
05afe08870681beb0792f384475077c988916762vboxsync * This is for handling GNU and PAX long names. */
05afe08870681beb0792f384475077c988916762vboxsync /** The current link target if symlink or hardlink. */
05afe08870681beb0792f384475077c988916762vboxsync/** Pointer to the TAR reader instance data. */
05afe08870681beb0792f384475077c988916762vboxsync * Tar directory, character device, block device, fifo socket or symbolic link.
05afe08870681beb0792f384475077c988916762vboxsync /** The stream offset of the (first) header. */
05afe08870681beb0792f384475077c988916762vboxsync /** Pointer to the reader instance data (resides in the filesystem
05afe08870681beb0792f384475077c988916762vboxsync * @todo Fix this so it won't go stale... Back ref from this obj to fss? */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** The object info with unix attributes. */
05afe08870681beb0792f384475077c988916762vboxsync/** Pointer to a TAR filesystem stream base object. */
05afe08870681beb0792f384475077c988916762vboxsync * Tar file represented as a VFS I/O stream.
05afe08870681beb0792f384475077c988916762vboxsync /** The basic TAR object data. */
05afe08870681beb0792f384475077c988916762vboxsync /** The number of bytes in the file. */
05afe08870681beb0792f384475077c988916762vboxsync /** The current file position. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** The number of padding bytes following the file. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** Set if we've reached the end of the file. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** The input I/O stream. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync/** Pointer to a the private data of a TAR file I/O stream. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * Tar filesystem stream private data.
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** The input I/O stream. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** The current object (referenced). */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** Pointer to the private data if hVfsCurObj is representing a file. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** The start offset. */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync /** The offset of the next header. */
05afe08870681beb0792f384475077c988916762vboxsync /** Set if we've reached the end of the stream. */
05afe08870681beb0792f384475077c988916762vboxsync /** Set if we've encountered a fatal error. */
05afe08870681beb0792f384475077c988916762vboxsync /** The TAR reader instance data. */
05afe08870681beb0792f384475077c988916762vboxsync/** Pointer to a the private data of a TAR filesystem stream. */
05afe08870681beb0792f384475077c988916762vboxsync * Converts a numeric header field to the C native type.
05afe08870681beb0792f384475077c988916762vboxsync * @returns IPRT status code.
05afe08870681beb0792f384475077c988916762vboxsync * @param pszField The TAR header field.
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * @param cchField The length of the field.
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * @param fOctalOnly Must be octal.
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * @param pi64 Where to store the value.
05afe08870681beb0792f384475077c988916762vboxsyncstatic int rtZipTarHdrFieldToNum(const char *pszField, size_t cchField, bool fOctalOnly, int64_t *pi64)
05afe08870681beb0792f384475077c988916762vboxsync unsigned char const *puchField = (unsigned char const *)pszField;
05afe08870681beb0792f384475077c988916762vboxsync * Skip leading spaces. Include zeros to save a few slower loops below.
05afe08870681beb0792f384475077c988916762vboxsync unsigned char ch;
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync while (cchField > 0 && ((ch = *puchField) == ' '|| ch == '0'))
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * Convert octal digits.
05afe08870681beb0792f384475077c988916762vboxsync * Was it terminated correctly?
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * The first byte has the bit 7 set to indicate base-256, while bit 6
05afe08870681beb0792f384475077c988916762vboxsync * is the signed bit. Bits 5:0 are the most significant value bits.
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * The remaining bytes are used in full.
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync while (cchField-- > 0)
05afe08870681beb0792f384475077c988916762vboxsync * Calculates the TAR header checksums and detects if it's all zeros.
05afe08870681beb0792f384475077c988916762vboxsync * @returns true if all zeros, false if not.
05afe08870681beb0792f384475077c988916762vboxsync * @param pHdr The header to checksum.
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * @param pi32Unsigned Where to store the checksum calculated using
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * unsigned chars. This is the one POSIX
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * specifies.
05afe08870681beb0792f384475077c988916762vboxsync * @param pi32Signed Where to store the checksum calculated using
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * signed chars.
05afe08870681beb0792f384475077c988916762vboxsync * @remarks The reason why we calculate the checksum as both signed and unsigned
05afe08870681beb0792f384475077c988916762vboxsync * has to do with various the char C type being signed on some hosts
05afe08870681beb0792f384475077c988916762vboxsync * and unsigned on others.
95e56b6db151d1f9011694dcc552a6012f305a2avboxsyncstatic bool rtZipTarCalcChkSum(PCRTZIPTARHDR pHdr, int32_t *pi32Unsigned, int32_t *pi32Signed)
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * Sum up the entire header.
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * Check if it's all zeros and replace the chksum field with spaces.
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync i32Unsigned += (unsigned char)' ' * sizeof(pHdr->Common.chksum);
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync i32Signed += (signed char)' ' * sizeof(pHdr->Common.chksum);
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * Validates the TAR header.
05afe08870681beb0792f384475077c988916762vboxsync * @returns VINF_SUCCESS if valid, VERR_TAR_ZERO_HEADER if all zeros, and
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * the appropriate VERR_TAR_XXX otherwise.
05afe08870681beb0792f384475077c988916762vboxsync * @param pTar The TAR header.
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * @param penmType Where to return the type of header on success.
95e56b6db151d1f9011694dcc552a6012f305a2avboxsyncstatic int rtZipTarHdrValidate(PCRTZIPTARHDR pTar, PRTZIPTARTYPE penmType)
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * Calc the checksum first since this enables us to detect zero headers.
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync if (rtZipTarCalcChkSum(pTar, &i32ChkSum, &i32ChkSumSignedAlt))
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * Read the checksum field and match the checksums.
05afe08870681beb0792f384475077c988916762vboxsync int rc = rtZipTarHdrFieldToNum(pTar->Common.chksum, sizeof(pTar->Common.chksum), true /*fOctalOnly*/, &i64HdrChkSum);
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync && i32ChkSumSignedAlt != i64HdrChkSum) /** @todo test this */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync * Detect the TAR type.
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync/** @todo detect star headers */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync else if ( pTar->Common.magic[5] == '\0' /* VMWare ambiguity - they probably mean posix but */
95e56b6db151d1f9011694dcc552a6012f305a2avboxsync && pTar->Common.version[0] == ' ' /* got the version wrong. */
05afe08870681beb0792f384475077c988916762vboxsync * Perform some basic checks.
return VERR_TAR_UNKNOWN_TYPE_FLAG;
case RTZIPTARTYPE_ANCIENT:
case RTZIPTAR_TF_OLDNORMAL:
case RTZIPTAR_TF_NORMAL:
case RTZIPTAR_TF_CONTIG:
case RTZIPTAR_TF_DIR:
case RTZIPTAR_TF_LINK:
case RTZIPTAR_TF_SYMLINK:
case RTZIPTAR_TF_FIFO:
return VERR_TAR_UNKNOWN_TYPE_FLAG;
return VINF_SUCCESS;
int rc;
return VINF_SUCCESS;
return rc;
if (fFirst)
case RTZIPTAR_TF_OLDNORMAL:
case RTZIPTAR_TF_NORMAL:
case RTZIPTAR_TF_CONTIG:
case RTZIPTAR_TF_LINK:
case RTZIPTAR_TF_SYMLINK:
case RTZIPTAR_TF_CHR:
case RTZIPTAR_TF_BLK:
case RTZIPTAR_TF_FIFO:
case RTZIPTAR_TF_DIR:
return VERR_TAR_EMPTY_NAME;
rc = RTStrCopyEx(pThis->szName, sizeof(pThis->szName), pHdr->Posix.prefix, sizeof(pHdr->Posix.prefix));
rc = RTStrCatEx(pThis->szName, sizeof(pThis->szName), pHdr->Common.name, sizeof(pHdr->Common.name));
rc = RTStrCopyEx(pThis->szName, sizeof(pThis->szName), pHdr->Common.name, sizeof(pHdr->Common.name));
rc = RTStrCopyEx(pThis->szName, sizeof(pThis->szName), pHdr->Common.name, sizeof(pHdr->Common.name));
case RTZIPTAR_TF_X_HDR:
case RTZIPTAR_TF_X_GLOBAL:
return VERR_TAR_UNSUPPORTED_PAX_TYPE;
case RTZIPTAR_TF_SOLARIS_XHDR:
case RTZIPTAR_TF_GNU_LONGNAME:
case RTZIPTAR_TF_GNU_LONGLINK:
return VERR_TAR_MALFORMED_GNU_LONGXXXX;
return VERR_TAR_MALFORMED_GNU_LONGXXXX;
return VERR_TAR_NAME_TOO_LONG;
case RTZIPTAR_TF_GNU_DUMPDIR:
case RTZIPTAR_TF_GNU_MULTIVOL:
case RTZIPTAR_TF_GNU_SPARSE:
case RTZIPTAR_TF_GNU_VOLDHR:
return VERR_TAR_UNKNOWN_TYPE_FLAG;
return VINF_SUCCESS;
case RTZIPTARREADERSTATE_ZERO:
return VERR_TAR_ZERO_HEADER;
return VINF_SUCCESS;
return VERR_TAR_ZERO_HEADER;
return VERR_TAR_MALFORMED_GNU_LONGXXXX;
return VERR_TAR_MALFORMED_GNU_LONGXXXX;
char *pszDst = pThis->enmState == RTZIPTARREADERSTATE_GNU_LONGNAME ? pThis->szName : pThis->szTarget;
return VINF_SUCCESS;
return VERR_INTERNAL_ERROR_5;
int rc;
return rc; \
return VERR_TAR_NUM_VALUE_TOO_LARGE; \
return VERR_TAR_NUM_VALUE_TOO_LARGE;
case RTZIPTARTYPE_POSIX:
case RTZIPTARTYPE_GNU:
return VERR_TAR_DEV_VALUE_TOO_LARGE;
return VERR_TAR_UNKNOWN_TYPE_FLAG;
return VERR_TAR_BAD_MODE_FIELD;
case RTZIPTAR_TF_OLDNORMAL:
case RTZIPTAR_TF_NORMAL:
case RTZIPTAR_TF_CONTIG:
case RTZIPTAR_TF_LINK:
return VERR_TAR_SIZE_NOT_ZERO;
case RTZIPTAR_TF_SYMLINK:
case RTZIPTAR_TF_CHR:
case RTZIPTAR_TF_BLK:
case RTZIPTAR_TF_DIR:
case RTZIPTAR_TF_FIFO:
case RTZIPTAR_TF_GNU_LONGLINK:
case RTZIPTAR_TF_GNU_LONGNAME:
switch (fModeType)
case RTFS_TYPE_FILE:
case RTFS_TYPE_DIRECTORY:
case RTFS_TYPE_SYMLINK:
case RTFS_TYPE_DEV_BLOCK:
case RTFS_TYPE_DEV_CHAR:
case RTFS_TYPE_FIFO:
return VERR_TAR_MODE_WITH_TYPE;
case RTZIPTAR_TF_CHR:
case RTZIPTAR_TF_BLK:
case RTZIPTAR_TF_DIR:
case RTZIPTAR_TF_FIFO:
return VINF_SUCCESS;
return VINF_SUCCESS;
static DECLCALLBACK(int) rtZipTarFssBaseObj_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
switch (enmAddAttr)
case RTFSOBJATTRADD_NOTHING:
case RTFSOBJATTRADD_UNIX:
case RTFSOBJATTRADD_EASIZE:
return VERR_NOT_SUPPORTED;
return VINF_SUCCESS;
static DECLCALLBACK(int) rtZipTarFssIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
static int rtZipTarFssIos_ReadOneSeg(PRTZIPTARIOSTREAM pThis, void *pvBuf, size_t cbToRead, bool fBlocking, size_t *pcbRead)
if (!pcbRead)
return VERR_EOF;
if (!pcbRead)
return rc;
static DECLCALLBACK(int) rtZipTarFssIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
int rc;
rc = rtZipTarFssIos_ReadOneSeg(pThis, pSgBuf->paSegs[0].pvSeg, pSgBuf->paSegs[0].cbSeg, fBlocking, pcbRead);
cbReadSeg = 0;
rc = rtZipTarFssIos_ReadOneSeg(pThis, pSgBuf->paSegs[iSeg].pvSeg, pSgBuf->paSegs[iSeg].cbSeg, fBlocking, pcbReadSeg);
if (pcbRead)
if (pcbRead)
return rc;
static DECLCALLBACK(int) rtZipTarFssIos_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
return VERR_ACCESS_DENIED;
return VINF_SUCCESS;
static DECLCALLBACK(int) rtZipTarFssIos_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
return VINF_SUCCESS;
return VINF_SUCCESS;
static DECLCALLBACK(int) rtZipTarFssSym_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
return VERR_ACCESS_DENIED;
static DECLCALLBACK(int) rtZipTarFssSym_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
return VERR_ACCESS_DENIED;
return VERR_ACCESS_DENIED;
return VINF_SUCCESS;
static DECLCALLBACK(int) rtZipTarFss_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
static DECLCALLBACK(int) rtZipTarFss_Next(void *pvThis, char **ppszName, RTVFSOBJTYPE *penmType, PRTVFSOBJ phVfsObj)
return VERR_EOF;
while ( offHdr >= 0
if (offHdr < 0)
int rc;
switch (fType)
case RTFS_TYPE_FILE:
sizeof(*pIosData),
&hVfsIos,
(void **)&pIosData);
case RTFS_TYPE_SYMLINK:
sizeof(*pBaseObjData),
&hVfsSym,
(void **)&pBaseObjData);
case RTFS_TYPE_DEV_BLOCK:
case RTFS_TYPE_DEV_CHAR:
case RTFS_TYPE_DIRECTORY:
case RTFS_TYPE_FIFO:
sizeof(*pBaseObjData),
&hVfsObj,
(void **)&pBaseObjData);
AssertFailed();
if (ppszName)
return rc;
if (phVfsObj)
if (penmType)
return VINF_SUCCESS;
RTDECL(int) RTZipTarFsStreamFromIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
int rc = RTVfsNewFsStream(&rtZipTarFssOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFss, (void **)&pThis);
return VINF_SUCCESS;
return rc;