VirtualBox

Ignore:
Timestamp:
May 26, 2017 12:51:18 PM (8 years ago)
Author:
vboxsync
Message:

IPRT: Started on VFS file system output streams for implementing TAR creation.

File:
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/zip/tarvfswriter.cpp

    r67070 r67116  
    11/* $Id$ */
    22/** @file
    3  * IPRT - TAR Virtual Filesystem.
     3 * IPRT - TAR Virtual Filesystem, Writer.
    44 */
    55
    66/*
    7  * Copyright (C) 2010-2016 Oracle Corporation
     7 * Copyright (C) 2010-2017 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    3232#include <iprt/zip.h>
    3333
    34 #include <iprt/asm.h>
    3534#include <iprt/assert.h>
    36 #include <iprt/ctype.h>
    3735#include <iprt/err.h>
    38 #include <iprt/poll.h>
    39 #include <iprt/file.h>
     36#include <iprt/mem.h>
     37#include <iprt/path.h>
    4038#include <iprt/string.h>
    4139#include <iprt/vfs.h>
     
    4947*********************************************************************************************************************************/
    5048/**
    51  * TAR reader state machine states.
    52  */
    53 typedef enum RTZIPTARREADERSTATE
    54 {
    55     /** Invalid state. */
    56     RTZIPTARREADERSTATE_INVALID = 0,
    57     /** Expecting the next file/dir/whatever entry. */
    58     RTZIPTARREADERSTATE_FIRST,
    59     /** Expecting more zero headers or the end of the stream. */
    60     RTZIPTARREADERSTATE_ZERO,
    61     /** Expecting a GNU long name. */
    62     RTZIPTARREADERSTATE_GNU_LONGNAME,
    63     /** Expecting a GNU long link. */
    64     RTZIPTARREADERSTATE_GNU_LONGLINK,
    65     /** Expecting a normal header or another GNU specific one. */
    66     RTZIPTARREADERSTATE_GNU_NEXT,
    67     /** End of valid states (not included). */
    68     RTZIPTARREADERSTATE_END
    69 } RTZIPTARREADERSTATE;
    70 
    71 /**
    72  * Tar reader instance data.
    73  */
    74 typedef struct RTZIPTARREADER
    75 {
    76     /** Zero header counter. */
    77     uint32_t                cZeroHdrs;
    78     /** The state machine state. */
    79     RTZIPTARREADERSTATE     enmState;
    80     /** The type of the previous TAR header.
    81      * @remarks Same a enmType for the first header in the TAR stream. */
    82     RTZIPTARTYPE            enmPrevType;
    83     /** The type of the current TAR header. */
    84     RTZIPTARTYPE            enmType;
    85     /** The current header. */
    86     RTZIPTARHDR             Hdr;
    87     /** The expected long name/link length (GNU). */
    88     uint32_t                cbGnuLongExpect;
    89     /** The current long name/link length (GNU). */
    90     uint32_t                offGnuLongCur;
    91     /** The name of the current object.
    92      * This is for handling GNU and PAX long names. */
    93     char                    szName[RTPATH_MAX];
    94     /** The current link target if symlink or hardlink. */
    95     char                    szTarget[RTPATH_MAX];
    96 } RTZIPTARREADER;
    97 /** Pointer to the TAR reader instance data. */
    98 typedef RTZIPTARREADER *PRTZIPTARREADER;
    99 
    100 /**
    101  * Tar directory, character device, block device, fifo socket or symbolic link.
    102  */
    103 typedef struct RTZIPTARBASEOBJ
    104 {
    105     /** The stream offset of the (first) header.  */
    106     RTFOFF                  offHdr;
    107     /** Pointer to the reader instance data (resides in the filesystem
    108      * stream).
    109      * @todo Fix this so it won't go stale... Back ref from this obj to fss? */
    110     PRTZIPTARREADER         pTarReader;
    111     /** The object info with unix attributes. */
    112     RTFSOBJINFO             ObjInfo;
    113 } RTZIPTARBASEOBJ;
    114 /** Pointer to a TAR filesystem stream base object. */
    115 typedef RTZIPTARBASEOBJ *PRTZIPTARBASEOBJ;
    116 
    117 
    118 /**
    119  * Tar file represented as a VFS I/O stream.
    120  */
    121 typedef struct RTZIPTARIOSTREAM
    122 {
    123     /** The basic TAR object data. */
    124     RTZIPTARBASEOBJ         BaseObj;
    125     /** The number of bytes in the file. */
    126     RTFOFF                  cbFile;
    127     /** The current file position. */
    128     RTFOFF                  offFile;
    129     /** The start position in the hVfsIos (for seekable hVfsIos). */
    130     RTFOFF                  offStart;
    131     /** The number of padding bytes following the file. */
    132     uint32_t                cbPadding;
    133     /** Set if we've reached the end of the file. */
    134     bool                    fEndOfStream;
    135     /** The input I/O stream. */
     49 * Tar filesystem stream private data.
     50 */
     51typedef struct RTZIPTARFSSTREAMWRITER
     52{
     53    /** The output I/O stream. */
    13654    RTVFSIOSTREAM           hVfsIos;
    137 } RTZIPTARIOSTREAM;
    138 /** Pointer to a the private data of a TAR file I/O stream. */
    139 typedef RTZIPTARIOSTREAM *PRTZIPTARIOSTREAM;
    140 
    141 
    142 /**
    143  * Tar filesystem stream private data.
    144  */
    145 typedef struct RTZIPTARFSSTREAM
    146 {
    147     /** The input I/O stream. */
    148     RTVFSIOSTREAM           hVfsIos;
    149 
    150     /** The current object (referenced). */
    151     RTVFSOBJ                hVfsCurObj;
    152     /** Pointer to the private data if hVfsCurObj is representing a file. */
    153     PRTZIPTARIOSTREAM       pCurIosData;
    154 
    155     /** The start offset. */
    156     RTFOFF                  offStart;
    157     /** The offset of the next header. */
    158     RTFOFF                  offNextHdr;
    159 
    160     /** Set if we've reached the end of the stream. */
    161     bool                    fEndOfStream;
     55
    16256    /** Set if we've encountered a fatal error. */
    16357    int                     rcFatal;
    164 
    165     /** The TAR reader instance data. */
    166     RTZIPTARREADER          TarReader;
    167 } RTZIPTARFSSTREAM;
     58    /** Flags. */
     59    uint32_t                fFlags;
     60
     61    /** Number of bytes written. */
     62    uint64_t                cbWritten;
     63
     64    /** Number of headers returned by rtZipTarFssWriter_ObjInfoToHdr. */
     65    uint32_t                cHdrs;
     66    /** Header buffers returned by rtZipTarFssWriter_ObjInfoToHdr. */
     67    RTZIPTARHDR             aHdrs[3];
     68} RTZIPTARFSSTREAMWRITER;
    16869/** Pointer to a the private data of a TAR filesystem stream. */
    169 typedef RTZIPTARFSSTREAM *PRTZIPTARFSSTREAM;
    170 
    171 
    172 
    173 /**
    174  * Converts a numeric header field to the C native type.
     70typedef RTZIPTARFSSTREAMWRITER *PRTZIPTARFSSTREAMWRITER;
     71
     72
     73/**
     74 * Calculates the header checksum and stores it in the chksum field.
    17575 *
    17676 * @returns IPRT status code.
    177  *
    178  * @param   pszField            The TAR header field.
    179  * @param   cchField            The length of the field.
    180  * @param   fOctalOnly          Must be octal.
    181  * @param   pi64                Where to store the value.
    182  */
    183 static int rtZipTarHdrFieldToNum(const char *pszField, size_t cchField, bool fOctalOnly, int64_t *pi64)
    184 {
    185     unsigned char const *puchField   = (unsigned char const *)pszField;
    186     size_t const         cchFieldOrg = cchField;
    187     if (   fOctalOnly
    188         || !(*puchField & 0x80))
    189     {
    190         /*
    191          * Skip leading spaces. Include zeros to save a few slower loops below.
    192          */
    193         unsigned char ch;
    194         while (cchField > 0 && ((ch = *puchField) == ' '|| ch == '0'))
    195             cchField--, puchField++;
    196 
    197         /*
    198          * Convert octal digits.
    199          */
    200         int64_t i64 = 0;
    201         while (cchField > 0)
    202         {
    203             unsigned char uDigit = *puchField - '0';
    204             if (uDigit >= 8)
    205                 break;
    206             i64 <<= 3;
    207             i64 |= uDigit;
    208 
    209             puchField++;
    210             cchField--;
    211         }
    212         *pi64 = i64;
    213 
    214         /*
    215          * Was it terminated correctly?
    216          */
    217         while (cchField > 0)
    218         {
    219             ch = *puchField++;
    220             if (ch != 0 && ch != ' ')
    221                 return cchField < cchFieldOrg
    222                      ? VERR_TAR_BAD_NUM_FIELD_TERM
    223                      : VERR_TAR_BAD_NUM_FIELD;
    224             cchField--;
    225         }
     77 * @param   pHdr                The header.
     78 */
     79static int rtZipTarFssWriter_ChecksumHdr(PRTZIPTARHDR pHdr)
     80{
     81    int32_t iUnsignedChksum;
     82    rtZipTarCalcChkSum(pHdr, &iUnsignedChksum, NULL);
     83
     84    int rc = RTStrFormatU32(pHdr->Common.chksum, sizeof(pHdr->Common.chksum), iUnsignedChksum,
     85                            8 /*uBase*/, -1 /*cchWidth*/, sizeof(pHdr->Common.chksum) - 1, RTSTR_F_ZEROPAD);
     86    AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
     87    return VINF_SUCCESS;
     88}
     89
     90
     91/**
     92 * Creates one or more tar headers for the object.
     93 *
     94 * Returns RTZIPTARFSSTREAMWRITER::aHdrs and RTZIPTARFSSTREAMWRITER::cHdrs.
     95 *
     96 * @returns IPRT status code.
     97 * @param   pThis           The TAR writer instance.
     98 * @param   pszPath         The path to the file.
     99 * @param   hVfsIos         The I/O stream of the file.
     100 * @param   fFlags          The RTVFSFSSTREAMOPS::pfnAdd flags.
     101 * @param   pObjInfo        The object information.
     102 * @param   pszOwnerNm      The owner name.
     103 * @param   pszGroupNm      The group name.
     104 * @param   chType          The tar record type, UINT8_MAX for default.
     105 */
     106static int rtZipTarFssWriter_ObjInfoToHdr(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, PCRTFSOBJINFO pObjInfo,
     107                                          const char *pszOwnerNm, const char *pszGroupNm, uint8_t chType)
     108{
     109    pThis->cHdrs = 0;
     110    RT_ZERO(pThis->aHdrs[0]);
     111
     112    /*
     113     * The path name first.  Make sure to flip DOS slashes.
     114     */
     115    size_t cchPath = strlen(pszPath);
     116    if (cchPath < sizeof(pThis->aHdrs[0].Common.name))
     117    {
     118        memcpy(pThis->aHdrs[0].Common.name, pszPath, cchPath + 1);
     119#if RTPATH_STYLE == RTPATH_STR_F_STYLE_UNIX
     120        char *pszDosSlash = strchr(pThis->aHdrs[0].Common.name, '\\');
     121        while (pszDosSlash)
     122        {
     123            *pszDosSlash = '/';
     124            pszDosSlash = strchr(pszDosSlash + 1, '\\');
     125        }
     126#endif
    226127    }
    227128    else
    228129    {
    229         /*
    230          * The first byte has the bit 7 set to indicate base-256, while bit 6
    231          * is the signed bit. Bits 5:0 are the most significant value bits.
    232          */
    233         uint64_t u64;
    234         if (!(0x40 & *puchField))
    235         {
    236             /* Positive or zero value. */
    237             u64 = *puchField & 0x3f;
    238             cchField--;
    239             puchField++;
    240 
    241             while (cchField-- > 0)
     130        /** @todo implement gnu and pax long name extensions. */
     131        return VERR_TAR_NAME_TOO_LONG;
     132    }
     133
     134    /*
     135     * File mode.  ASSUME that the unix part of the IPRT mode mask is
     136     * compatible with the TAR/Unix world.
     137     */
     138    int rc;
     139    rc = RTStrFormatU32(pThis->aHdrs[0].Common.mode, sizeof(pThis->aHdrs[0].Common.mode), pObjInfo->Attr.fMode & RTFS_UNIX_MASK,
     140                        8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.mode) - 1, RTSTR_F_ZEROPAD);
     141    AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
     142
     143
     144    /*
     145     * uid & gid.  Just guard against NIL values as they won't fit.
     146     */
     147    uint32_t uValue = pObjInfo->Attr.u.Unix.uid != NIL_RTUID ? pObjInfo->Attr.u.Unix.uid : 0;
     148    rc = RTStrFormatU32(pThis->aHdrs[0].Common.uid, sizeof(pThis->aHdrs[0].Common.uid), uValue,
     149                        8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.uid) - 1, RTSTR_F_ZEROPAD);
     150    AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
     151
     152    uValue = pObjInfo->Attr.u.Unix.gid != NIL_RTUID ? pObjInfo->Attr.u.Unix.gid : 0;
     153    rc = RTStrFormatU32(pThis->aHdrs[0].Common.gid, sizeof(pThis->aHdrs[0].Common.gid), uValue,
     154                        8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.gid) - 1, RTSTR_F_ZEROPAD);
     155    AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
     156
     157    /*
     158     * Is the size small enough for the standard octal string encoding?
     159     *
     160     * Note! We could actually use the terminator character as well if we liked,
     161     *       but let not do that as it's easier to test this way.
     162     */
     163    uint64_t cbObject = pObjInfo->cbObject;
     164    if (cbObject < _4G * 2U)
     165    {
     166        rc = RTStrFormatU64(pThis->aHdrs[0].Common.size, sizeof(pThis->aHdrs[0].Common.size), cbObject,
     167                            8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.size) - 1, RTSTR_F_ZEROPAD);
     168        AssertRCReturn(rc, rc);
     169    }
     170    /*
     171     * No, use the base 256 extension. Set the highest bit of the left most
     172     * character.  We don't deal with negatives here, cause the size have to
     173     * be greater than zero.
     174     *
     175     * Note! The base-256 extension are never used by gtar or libarchive
     176     *       with the "ustar  \0" format version, only the later
     177     *       "ustar\000" version.  However, this shouldn't cause much
     178     *       trouble as they are not picky about what they read.
     179     */
     180    else
     181    {
     182        size_t         cchField  = sizeof(pThis->aHdrs[0].Common.size) - 1;
     183        unsigned char *puchField = (unsigned char*)pThis->aHdrs[0].Common.size;
     184        puchField[0] = 0x80;
     185        do
     186        {
     187            puchField[cchField--] = cbObject & 0xff;
     188            cbObject >>= 8;
     189        } while (cchField);
     190    }
     191
     192    /*
     193     * Modification time relative to unix epoc.
     194     */
     195    rc = RTStrFormatU64(pThis->aHdrs[0].Common.mtime, sizeof(pThis->aHdrs[0].Common.mtime),
     196                        RTTimeSpecGetSeconds(&pObjInfo->ModificationTime),
     197                        8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.mtime) - 1, RTSTR_F_ZEROPAD);
     198    AssertRCReturn(rc, rc);
     199
     200    /* Skipping checksum for now */
     201
     202    /*
     203     * The type flag.
     204     */
     205    if (chType == UINT8_MAX)
     206        switch (pObjInfo->Attr.fMode & RTFS_TYPE_MASK)
     207        {
     208            case RTFS_TYPE_FIFO:        chType = RTZIPTAR_TF_FIFO; break;
     209            case RTFS_TYPE_DEV_CHAR:    chType = RTZIPTAR_TF_CHR; break;
     210            case RTFS_TYPE_DIRECTORY:   chType = RTZIPTAR_TF_DIR; break;
     211            case RTFS_TYPE_DEV_BLOCK:   chType = RTZIPTAR_TF_BLK; break;
     212            case RTFS_TYPE_FILE:        chType = RTZIPTAR_TF_NORMAL; break;
     213            case RTFS_TYPE_SYMLINK:     chType = RTZIPTAR_TF_SYMLINK; break;
     214            case RTFS_TYPE_SOCKET:      chType = RTZIPTAR_TF_FIFO; break;
     215            case RTFS_TYPE_WHITEOUT:    AssertFailedReturn(VERR_WRONG_TYPE);
     216        }
     217    pThis->aHdrs[0].Common.typeflag = chType;
     218
     219    /* No link name, at least not for now.  Caller might set it. */
     220
     221    /*
     222     * Set TAR record magic and version.
     223     */
     224    memcpy(pThis->aHdrs[0].Common.magic, RT_STR_TUPLE("ustar "));
     225    pThis->aHdrs[0].Common.version[0] = ' ';
     226    pThis->aHdrs[0].Common.version[1] = ' ';
     227
     228    /*
     229     * Owner and group names.  Silently truncate them for now.
     230     */
     231    RTStrCopy(pThis->aHdrs[0].Common.uname, sizeof(pThis->aHdrs[0].Common.uname), pszOwnerNm);
     232    RTStrCopy(pThis->aHdrs[0].Common.gname, sizeof(pThis->aHdrs[0].Common.uname), pszGroupNm);
     233
     234    /*
     235     * Char/block device numbers.
     236     */
     237    if (   RTFS_IS_DEV_BLOCK(pObjInfo->Attr.fMode)
     238        || RTFS_IS_DEV_CHAR(pObjInfo->Attr.fMode) )
     239    {
     240        rc = RTStrFormatU32(pThis->aHdrs[0].Common.devmajor, sizeof(pThis->aHdrs[0].Common.devmajor),
     241                            RTDEV_MAJOR(pObjInfo->Attr.u.Unix.Device),
     242                            8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.devmajor) - 1, RTSTR_F_ZEROPAD);
     243        AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
     244
     245        rc = RTStrFormatU32(pThis->aHdrs[0].Common.devminor, sizeof(pThis->aHdrs[0].Common.devmajor),
     246                            RTDEV_MINOR(pObjInfo->Attr.u.Unix.Device),
     247                            8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.devmajor) - 1, RTSTR_F_ZEROPAD);
     248        AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
     249    }
     250
     251    /*
     252     * Finally the checksum.
     253     */
     254    return rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
     255}
     256
     257
     258/**
     259 * Adds a file to the stream.
     260 *
     261 * @returns IPRT status code.
     262 * @param   pThis           The TAR writer instance.
     263 * @param   pszPath         The path to the file.
     264 * @param   hVfsIos         The I/O stream of the file.
     265 * @param   fFlags          The RTVFSFSSTREAMOPS::pfnAdd flags.
     266 * @param   pObjInfo        The object information.
     267 * @param   pszOwnerNm      The owner name.
     268 * @param   pszGroupNm      The group name.
     269 */
     270static int rtZipTarFssWriter_AddFile(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos, uint32_t fFlags,
     271                                     PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
     272{
     273    RT_NOREF(fFlags);
     274
     275    /*
     276     * Append the header.
     277     */
     278    int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
     279    if (RT_SUCCESS(rc))
     280    {
     281        rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
     282        if (RT_SUCCESS(rc))
     283        {
     284            pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
     285
     286            /*
     287             * Allocate a tmporary buffer
     288             */
     289            uint8_t *pbFree;
     290            size_t   cbBuf = _512K;
     291            uint8_t *pbBuf = pbFree = (uint8_t *)RTMemTmpAlloc(cbBuf);
     292            if (!pbBuf)
    242293            {
    243                 if (RT_LIKELY(u64 <= (uint64_t)INT64_MAX / 256))
    244                     u64 = (u64 << 8) | *puchField++;
    245                 else
    246                     return VERR_TAR_NUM_VALUE_TOO_LARGE;
    247             }
    248         }
    249         else
    250         {
    251             /* Negative value (could be used in timestamp). We do manual sign extending here. */
    252             u64 = (UINT64_MAX << 6) | (*puchField & 0x3f);
    253             cchField--;
    254             puchField++;
    255 
    256             while (cchField-- > 0)
    257             {
    258                 if (RT_LIKELY(u64 >= (uint64_t)(INT64_MIN / 256)))
    259                     u64 = (u64 << 8) | *puchField++;
    260                 else
    261                     return VERR_TAR_NUM_VALUE_TOO_LARGE;
    262             }
    263         }
    264         *pi64 = (int64_t)u64;
    265     }
    266 
    267     return VINF_SUCCESS;
    268 }
    269 
    270 
    271 /**
    272  * Calculates the TAR header checksums and detects if it's all zeros.
    273  *
    274  * @returns true if all zeros, false if not.
    275  * @param   pHdr                The header to checksum.
    276  * @param   pi32Unsigned        Where to store the checksum calculated using
    277  *                              unsigned chars.   This is the one POSIX
    278  *                              specifies.
    279  * @param   pi32Signed          Where to store the checksum calculated using
    280  *                              signed chars.
    281  *
    282  * @remarks The reason why we calculate the checksum as both signed and unsigned
    283  *          has to do with various the char C type being signed on some hosts
    284  *          and unsigned on others.
    285  */
    286 static bool rtZipTarCalcChkSum(PCRTZIPTARHDR pHdr, int32_t *pi32Unsigned, int32_t *pi32Signed)
    287 {
    288     int32_t i32Unsigned = 0;
    289     int32_t i32Signed   = 0;
    290 
    291     /*
    292      * Sum up the entire header.
    293      */
    294     const char *pch    = (const char *)pHdr;
    295     const char *pchEnd = pch + sizeof(*pHdr);
    296     do
    297     {
    298         i32Unsigned += *(unsigned char *)pch;
    299         i32Signed   += *(signed   char *)pch;
    300     } while (++pch != pchEnd);
    301 
    302     /*
    303      * Check if it's all zeros and replace the chksum field with spaces.
    304      */
    305     bool const fZeroHdr = i32Unsigned == 0;
    306 
    307     pch    = pHdr->Common.chksum;
    308     pchEnd = pch + sizeof(pHdr->Common.chksum);
    309     do
    310     {
    311         i32Unsigned -= *(unsigned char *)pch;
    312         i32Signed   -= *(signed   char *)pch;
    313     } while (++pch != pchEnd);
    314 
    315     i32Unsigned += (unsigned char)' ' * sizeof(pHdr->Common.chksum);
    316     i32Signed   += (signed   char)' ' * sizeof(pHdr->Common.chksum);
    317 
    318     *pi32Unsigned = i32Unsigned;
    319     if (pi32Signed)
    320         *pi32Signed = i32Signed;
    321     return fZeroHdr;
    322 }
    323 
    324 
    325 /**
    326  * Validates the TAR header.
    327  *
    328  * @returns VINF_SUCCESS if valid, VERR_TAR_ZERO_HEADER if all zeros, and
    329  *          the appropriate VERR_TAR_XXX otherwise.
    330  * @param   pTar                The TAR header.
    331  * @param   penmType            Where to return the type of header on success.
    332  */
    333 static int rtZipTarHdrValidate(PCRTZIPTARHDR pTar, PRTZIPTARTYPE penmType)
    334 {
    335     /*
    336      * Calc the checksum first since this enables us to detect zero headers.
    337      */
    338     int32_t i32ChkSum;
    339     int32_t i32ChkSumSignedAlt;
    340     if (rtZipTarCalcChkSum(pTar, &i32ChkSum, &i32ChkSumSignedAlt))
    341         return VERR_TAR_ZERO_HEADER;
    342 
    343     /*
    344      * Read the checksum field and match the checksums.
    345      */
    346     int64_t i64HdrChkSum;
    347     int rc = rtZipTarHdrFieldToNum(pTar->Common.chksum, sizeof(pTar->Common.chksum), true /*fOctalOnly*/, &i64HdrChkSum);
    348     if (RT_FAILURE(rc))
    349         return VERR_TAR_BAD_CHKSUM_FIELD;
    350     if (   i32ChkSum          != i64HdrChkSum
    351         && i32ChkSumSignedAlt != i64HdrChkSum) /** @todo test this */
    352         return VERR_TAR_CHKSUM_MISMATCH;
    353 
    354     /*
    355      * Detect the TAR type.
    356      */
    357     RTZIPTARTYPE enmType;
    358     if (   pTar->Common.magic[0] == 'u'
    359         && pTar->Common.magic[1] == 's'
    360         && pTar->Common.magic[2] == 't'
    361         && pTar->Common.magic[3] == 'a'
    362         && pTar->Common.magic[4] == 'r')
    363     {
    364 /** @todo detect star headers */
    365         if (   pTar->Common.magic[5]   == '\0'
    366             && pTar->Common.version[0] == '0'
    367             && pTar->Common.version[1] == '0')
    368             enmType = RTZIPTARTYPE_POSIX;
    369         else if (   pTar->Common.magic[5]   == ' '
    370                  && pTar->Common.version[0] == ' '
    371                  && pTar->Common.version[1] == '\0')
    372             enmType = RTZIPTARTYPE_GNU;
    373         else if (   pTar->Common.magic[5]   == '\0' /* VMWare ambiguity - they probably mean posix but */
    374                  && pTar->Common.version[0] == ' '  /*                    got the version wrong. */
    375                  && pTar->Common.version[1] == '\0')
    376             enmType = RTZIPTARTYPE_POSIX;
    377         else
    378             return VERR_TAR_NOT_USTAR_V00;
    379     }
    380     else
    381         enmType = RTZIPTARTYPE_ANCIENT;
    382     *penmType = enmType;
    383 
    384     /*
    385      * Perform some basic checks.
    386      */
    387     switch (enmType)
    388     {
    389         case RTZIPTARTYPE_POSIX:
    390             if (   !RT_C_IS_ALNUM(pTar->Common.typeflag)
    391                 && pTar->Common.typeflag != '\0')
    392                 return VERR_TAR_UNKNOWN_TYPE_FLAG;
    393             break;
    394 
    395         case RTZIPTARTYPE_GNU:
    396             switch (pTar->Common.typeflag)
    397             {
    398                 case RTZIPTAR_TF_OLDNORMAL:
    399                 case RTZIPTAR_TF_NORMAL:
    400                 case RTZIPTAR_TF_CONTIG:
    401                 case RTZIPTAR_TF_DIR:
    402                 case RTZIPTAR_TF_CHR:
    403                 case RTZIPTAR_TF_BLK:
    404                 case RTZIPTAR_TF_LINK:
    405                 case RTZIPTAR_TF_SYMLINK:
    406                 case RTZIPTAR_TF_FIFO:
    407                     break;
    408 
    409                 case RTZIPTAR_TF_GNU_LONGLINK:
    410                 case RTZIPTAR_TF_GNU_LONGNAME:
    411                     break;
    412 
    413                 case RTZIPTAR_TF_GNU_DUMPDIR:
    414                 case RTZIPTAR_TF_GNU_MULTIVOL:
    415                 case RTZIPTAR_TF_GNU_SPARSE:
    416                 case RTZIPTAR_TF_GNU_VOLDHR:
    417                     /** @todo Implement full GNU TAR support. .*/
    418                     return VERR_TAR_UNSUPPORTED_GNU_HDR_TYPE;
    419 
    420                 default:
    421                     return VERR_TAR_UNKNOWN_TYPE_FLAG;
    422             }
    423             break;
    424 
    425         case RTZIPTARTYPE_ANCIENT:
    426             switch (pTar->Common.typeflag)
    427             {
    428                 case RTZIPTAR_TF_OLDNORMAL:
    429                 case RTZIPTAR_TF_NORMAL:
    430                 case RTZIPTAR_TF_CONTIG:
    431                 case RTZIPTAR_TF_DIR:
    432                 case RTZIPTAR_TF_LINK:
    433                 case RTZIPTAR_TF_SYMLINK:
    434                 case RTZIPTAR_TF_FIFO:
    435                     break;
    436                 default:
    437                     return VERR_TAR_UNKNOWN_TYPE_FLAG;
    438             }
    439             break;
    440         default: /* shut up gcc */
    441             AssertFailedReturn(VERR_INTERNAL_ERROR_3);
    442     }
    443 
    444     return VINF_SUCCESS;
    445 }
    446 
    447 
    448 /**
    449  * Parses and validates the first TAR header of a archive/file/dir/whatever.
    450  *
    451  * @returns IPRT status code.
    452  * @param   pThis               The TAR reader stat.
    453  * @param   pTar                The TAR header that has been read.
    454  * @param   fFirst              Set if this is the first header, otherwise
    455  *                              clear.
    456  */
    457 static int rtZipTarReaderParseNextHeader(PRTZIPTARREADER pThis, PCRTZIPTARHDR pHdr, bool fFirst)
    458 {
    459     int rc;
    460 
    461     /*
    462      * Basic header validation and detection first.
    463      */
    464     RTZIPTARTYPE enmType;
    465     rc = rtZipTarHdrValidate(pHdr, &enmType);
    466     if (RT_FAILURE_NP(rc))
    467     {
    468         if (rc == VERR_TAR_ZERO_HEADER)
    469         {
    470             pThis->cZeroHdrs = 1;
    471             pThis->enmState = RTZIPTARREADERSTATE_ZERO;
    472             return VINF_SUCCESS;
    473         }
    474         return rc;
    475     }
    476     if (fFirst)
    477     {
    478         pThis->enmType = enmType;
    479         if (pThis->enmPrevType == RTZIPTARTYPE_INVALID)
    480             pThis->enmPrevType = enmType;
    481     }
    482 
    483     /*
    484      * Handle the header by type.
    485      */
    486     switch (pHdr->Common.typeflag)
    487     {
    488         case RTZIPTAR_TF_OLDNORMAL:
    489         case RTZIPTAR_TF_NORMAL:
    490         case RTZIPTAR_TF_CONTIG:
    491         case RTZIPTAR_TF_LINK:
    492         case RTZIPTAR_TF_SYMLINK:
    493         case RTZIPTAR_TF_CHR:
    494         case RTZIPTAR_TF_BLK:
    495         case RTZIPTAR_TF_FIFO:
    496         case RTZIPTAR_TF_DIR:
    497             /*
    498              * Extract the name first.
    499              */
    500             if (!pHdr->Common.name[0])
    501                 return VERR_TAR_EMPTY_NAME;
    502             if (pThis->enmType == RTZIPTARTYPE_POSIX)
    503             {
    504                 Assert(pThis->offGnuLongCur == 0); Assert(pThis->szName[0] == '\0');
    505                 pThis->szName[0] = '\0';
    506                 if (pHdr->Posix.prefix[0])
     294                cbBuf = _32K;
     295                pbBuf = pbFree = (uint8_t *)RTMemTmpAlloc(cbBuf);
     296                if (!pbBuf)
    507297                {
    508                     rc = RTStrCopyEx(pThis->szName, sizeof(pThis->szName), pHdr->Posix.prefix, sizeof(pHdr->Posix.prefix));
    509                     AssertRC(rc); /* shall not fail */
    510                     rc = RTStrCat(pThis->szName, sizeof(pThis->szName), "/");
    511                     AssertRC(rc); /* ditto */
    512                 }
    513                 rc = RTStrCatEx(pThis->szName, sizeof(pThis->szName), pHdr->Common.name, sizeof(pHdr->Common.name));
    514                 AssertRCReturn(rc, rc);
    515             }
    516             else if (pThis->enmType == RTZIPTARTYPE_GNU)
    517             {
    518                 if (!pThis->szName[0])
    519                 {
    520                     rc = RTStrCopyEx(pThis->szName, sizeof(pThis->szName), pHdr->Common.name, sizeof(pHdr->Common.name));
    521                     AssertRCReturn(rc, rc);
     298                    cbBuf = sizeof(pThis->aHdrs);
     299                    pbBuf = (uint8_t *)&pThis->aHdrs[0];
    522300                }
    523301            }
    524             else
     302
     303            /*
     304             * Copy the bytes.  Padding the last buffer to a multiple of 512.
     305             */
     306            uint64_t cbLeft = pObjInfo->cbObject;
     307            while (cbLeft > 0 && RT_SUCCESS(rc))
    525308            {
    526                 /* Old TAR */
    527                 Assert(pThis->offGnuLongCur == 0); Assert(pThis->szName[0] == '\0');
    528                 rc = RTStrCopyEx(pThis->szName, sizeof(pThis->szName), pHdr->Common.name, sizeof(pHdr->Common.name));
    529                 AssertRCReturn(rc, rc);
     309                size_t cbRead = cbLeft > cbBuf ? cbBuf : (size_t)cbBuf;
     310                rc = RTVfsIoStrmRead(hVfsIos, pbBuf, cbRead, true /*fBlocking*/, NULL);
     311                if (RT_FAILURE(rc))
     312                    break;
     313
     314                size_t cbToWrite = cbRead;
     315                if (cbRead & (sizeof(RTZIPTARHDR) - 1))
     316                {
     317                    size_t cbToZero = sizeof(RTZIPTARHDR) - (cbRead & (sizeof(RTZIPTARHDR) - 1));
     318                    memset(&pbBuf[cbRead], 0, cbToZero);
     319                    cbToWrite += cbToZero;
     320                }
     321
     322                rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbToWrite, true /*fBlocking*/, NULL);
     323                if (RT_FAILURE(rc))
     324                    break;
     325                pThis->cbWritten += cbToWrite;
     326                cbLeft -= cbRead;
    530327            }
    531328
     329            RTMemTmpFree(pbFree);
     330            if (RT_SUCCESS(rc))
     331                return VINF_SUCCESS;
     332        }
     333        pThis->rcFatal = rc;
     334    }
     335    return rc;
     336}
     337
     338
     339/**
     340 * Adds a symbolic link to the stream.
     341 *
     342 * @returns IPRT status code.
     343 * @param   pThis           The TAR writer instance.
     344 * @param   pszPath         The path to the object.
     345 * @param   hVfsSymlink     The symbolic link object to add.
     346 * @param   pObjInfo        The object information.
     347 * @param   pszOwnerNm      The owner name.
     348 * @param   pszGroupNm      The group name.
     349 */
     350static int rtZipTarFssWriter_AddSymlink(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSSYMLINK hVfsSymlink,
     351                                        PCRTFSOBJINFO pObjInfo,  const char *pszOwnerNm, const char *pszGroupNm)
     352{
     353    /*
     354     * Read the symlink target first and check that it's not too long.
     355     * Flip DOS slashes.
     356     */
     357    char szTarget[RTPATH_MAX];
     358    int rc = RTVfsSymlinkRead(hVfsSymlink, szTarget,  sizeof(szTarget));
     359    if (RT_SUCCESS(rc))
     360    {
     361#if RTPATH_STYLE == RTPATH_STR_F_STYLE_UNIX
     362        char *pszDosSlash = strchr(szTarget, '\\');
     363        while (pszDosSlash)
     364        {
     365            *pszDosSlash = '/';
     366            pszDosSlash = strchr(pszDosSlash + 1, '\\');
     367        }
     368#endif
     369        size_t cchTarget = strlen(szTarget);
     370        if (cchTarget < sizeof(pThis->aHdrs[0].Common.linkname))
     371        {
    532372            /*
    533              * Extract the link target.
     373             * Create a header, add the link target and push it out.
    534374             */
    535             if (   pHdr->Common.typeflag == RTZIPTAR_TF_LINK
    536                 || pHdr->Common.typeflag == RTZIPTAR_TF_SYMLINK)
     375            rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
     376            if (RT_SUCCESS(rc))
    537377            {
    538                 if (   pThis->enmType == RTZIPTARTYPE_POSIX
    539                     || pThis->enmType == RTZIPTARTYPE_ANCIENT
    540                     || (pThis->enmType == RTZIPTARTYPE_GNU && pThis->szTarget[0] == '\0')
    541                    )
     378                memcpy(pThis->aHdrs[0].Common.linkname, szTarget, cchTarget + 1);
     379                rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
     380                if (RT_SUCCESS(rc))
    542381                {
    543                     Assert(pThis->szTarget[0] == '\0');
    544                     rc = RTStrCopyEx(pThis->szTarget, sizeof(pThis->szTarget),
    545                                      pHdr->Common.linkname, sizeof(pHdr->Common.linkname));
    546                     AssertRCReturn(rc, rc);
     382                    rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]),
     383                                          true /*fBlocking*/, NULL);
     384                    if (RT_SUCCESS(rc))
     385                    {
     386                        pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
     387                        return VINF_SUCCESS;
     388                    }
     389                    pThis->rcFatal = rc;
    547390                }
    548391            }
    549             else
    550                 pThis->szTarget[0] = '\0';
    551 
    552             pThis->Hdr = *pHdr;
    553             break;
    554 
    555         case RTZIPTAR_TF_X_HDR:
    556         case RTZIPTAR_TF_X_GLOBAL:
    557             /** @todo implement PAX */
    558             return VERR_TAR_UNSUPPORTED_PAX_TYPE;
    559 
    560         case RTZIPTAR_TF_SOLARIS_XHDR:
    561             /** @todo implement solaris / pax attribute lists. */
    562             return VERR_TAR_UNSUPPORTED_SOLARIS_HDR_TYPE;
    563 
    564 
    565         /*
    566          * A GNU long name or long link is a dummy record followed by one or
    567          * more 512 byte string blocks holding the long name/link.  The name
    568          * lenght is encoded in the size field, null terminator included.  If
    569          * it is a symlink or hard link the long name may be followed by a
    570          * long link sequence.
    571          */
    572         case RTZIPTAR_TF_GNU_LONGNAME:
    573         case RTZIPTAR_TF_GNU_LONGLINK:
    574         {
    575             if (strcmp(pHdr->Gnu.name, "././@LongLink"))
    576                 return VERR_TAR_MALFORMED_GNU_LONGXXXX;
    577 
    578             int64_t cb64;
    579             rc = rtZipTarHdrFieldToNum(pHdr->Gnu.size, sizeof(pHdr->Gnu.size), false /*fOctalOnly*/, &cb64);
    580             if (RT_FAILURE(rc) || cb64 < 0 || cb64 > _1M)
    581                 return VERR_TAR_MALFORMED_GNU_LONGXXXX;
    582             uint32_t cb = (uint32_t)cb64;
    583             if (cb >= sizeof(pThis->szName))
    584                 return VERR_TAR_NAME_TOO_LONG;
    585 
    586             pThis->cbGnuLongExpect  = cb;
    587             pThis->offGnuLongCur    = 0;
    588             pThis->enmState         = pHdr->Common.typeflag == RTZIPTAR_TF_GNU_LONGNAME
    589                                     ? RTZIPTARREADERSTATE_GNU_LONGNAME
    590                                     : RTZIPTARREADERSTATE_GNU_LONGLINK;
    591             break;
    592         }
    593 
    594         case RTZIPTAR_TF_GNU_DUMPDIR:
    595         case RTZIPTAR_TF_GNU_MULTIVOL:
    596         case RTZIPTAR_TF_GNU_SPARSE:
    597         case RTZIPTAR_TF_GNU_VOLDHR:
    598             /** @todo Implement or skip GNU headers */
    599             return VERR_TAR_UNSUPPORTED_GNU_HDR_TYPE;
    600 
    601         default:
    602             return VERR_TAR_UNKNOWN_TYPE_FLAG;
    603     }
    604 
    605     return VINF_SUCCESS;
    606 }
    607 
    608 
    609 /**
    610  * Parses and validates a TAR header.
     392        }
     393        else
     394        {
     395            /** @todo implement gnu and pax long name extensions. */
     396            rc = VERR_TAR_NAME_TOO_LONG;
     397        }
     398    }
     399    return rc;
     400}
     401
     402
     403/**
     404 * Adds a simple object to the stream.
     405 *
     406 * Simple objects only contains metadata, no actual data bits.  Directories,
     407 * devices, fifos, sockets and such.
    611408 *
    612409 * @returns IPRT status code.
    613  * @param   pThis               The TAR reader stat.
    614  * @param   pTar                The TAR header that has been read.
    615  */
    616 static int rtZipTarReaderParseHeader(PRTZIPTARREADER pThis, PCRTZIPTARHDR pHdr)
    617 {
    618     switch (pThis->enmState)
    619     {
    620         /*
    621          * The first record for a file/directory/whatever.
    622          */
    623         case RTZIPTARREADERSTATE_FIRST:
    624             pThis->Hdr.Common.typeflag  = 0x7f;
    625             pThis->enmPrevType          = pThis->enmType;
    626             pThis->enmType              = RTZIPTARTYPE_INVALID;
    627             pThis->offGnuLongCur        = 0;
    628             pThis->cbGnuLongExpect      = 0;
    629             pThis->szName[0]            = '\0';
    630             pThis->szTarget[0]          = '\0';
    631             return rtZipTarReaderParseNextHeader(pThis, pHdr, true /*fFirst*/);
    632 
    633         /*
    634          * There should only be so many zero headers at the end of the file as
    635          * it is a function of the block size used when writing.  Don't go on
    636          * reading them forever in case someone points us to /dev/zero.
    637          */
    638         case RTZIPTARREADERSTATE_ZERO:
    639             if (!ASMMemIsZero(pHdr, sizeof(*pHdr)))
    640                 return VERR_TAR_ZERO_HEADER;
    641             pThis->cZeroHdrs++;
    642             if (pThis->cZeroHdrs <= _64K / 512 + 2)
    643                 return VINF_SUCCESS;
    644             return VERR_TAR_ZERO_HEADER;
    645 
    646         case RTZIPTARREADERSTATE_GNU_LONGNAME:
    647         case RTZIPTARREADERSTATE_GNU_LONGLINK:
    648         {
    649             size_t cbIncoming = RTStrNLen((const char *)pHdr->ab, sizeof(*pHdr));
    650             if (cbIncoming < sizeof(*pHdr))
    651                 cbIncoming += 1;
    652 
    653             if (cbIncoming + pThis->offGnuLongCur > pThis->cbGnuLongExpect)
    654                 return VERR_TAR_MALFORMED_GNU_LONGXXXX;
    655             if (   cbIncoming < sizeof(*pHdr)
    656                 && cbIncoming + pThis->offGnuLongCur != pThis->cbGnuLongExpect)
    657                 return VERR_TAR_MALFORMED_GNU_LONGXXXX;
    658 
    659             char *pszDst = pThis->enmState == RTZIPTARREADERSTATE_GNU_LONGNAME ? pThis->szName : pThis->szTarget;
    660             pszDst += pThis->offGnuLongCur;
    661             memcpy(pszDst, pHdr->ab, cbIncoming);
    662 
    663             pThis->offGnuLongCur += (uint32_t)cbIncoming;
    664             if (pThis->offGnuLongCur == pThis->cbGnuLongExpect)
    665                 pThis->enmState = RTZIPTARREADERSTATE_GNU_NEXT;
     410 * @param   pThis           The TAR writer instance.
     411 * @param   pszPath         The path to the object.
     412 * @param   pObjInfo        The object information.
     413 * @param   pszOwnerNm      The owner name.
     414 * @param   pszGroupNm      The group name.
     415 */
     416static int rtZipTarFssWriter_AddSimpleObject(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, PCRTFSOBJINFO pObjInfo,
     417                                             const char *pszOwnerNm, const char *pszGroupNm)
     418{
     419    int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
     420    if (RT_SUCCESS(rc))
     421    {
     422        rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
     423        if (RT_SUCCESS(rc))
     424        {
     425            pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
    666426            return VINF_SUCCESS;
    667427        }
    668 
    669         case RTZIPTARREADERSTATE_GNU_NEXT:
    670             pThis->enmState = RTZIPTARREADERSTATE_FIRST;
    671             return rtZipTarReaderParseNextHeader(pThis, pHdr, false /*fFirst*/);
    672 
    673         default:
    674             return VERR_INTERNAL_ERROR_5;
    675     }
    676 }
    677 
    678 
    679 /**
    680  * Translate a TAR header to an IPRT object info structure with additional UNIX
    681  * attributes.
    682  *
    683  * This completes the validation done by rtZipTarHdrValidate.
    684  *
    685  * @returns VINF_SUCCESS if valid, appropriate VERR_TAR_XXX if not.
    686  * @param   pThis               The TAR reader instance.
    687  * @param   pObjInfo            The object info structure (output).
    688  */
    689 static int rtZipTarReaderGetFsObjInfo(PRTZIPTARREADER pThis, PRTFSOBJINFO pObjInfo)
    690 {
    691     /*
    692      * Zap the whole structure, this takes care of unused space in the union.
    693      */
    694     RT_ZERO(*pObjInfo);
    695 
    696     /*
    697      * Convert the TAR field in RTFSOBJINFO order.
    698      */
    699     int         rc;
    700     int64_t     i64Tmp;
    701 #define GET_TAR_NUMERIC_FIELD_RET(a_Var, a_Field) \
    702         do { \
    703             rc = rtZipTarHdrFieldToNum(a_Field, sizeof(a_Field), false /*fOctalOnly*/, &i64Tmp); \
    704             if (RT_FAILURE(rc)) \
    705                 return rc; \
    706             (a_Var) = i64Tmp; \
    707             if ((a_Var) != i64Tmp) \
    708                 return VERR_TAR_NUM_VALUE_TOO_LARGE; \
    709         } while (0)
    710 
    711     GET_TAR_NUMERIC_FIELD_RET(pObjInfo->cbObject,        pThis->Hdr.Common.size);
    712     pObjInfo->cbAllocated = RT_ALIGN_64(pObjInfo->cbObject, 512);
    713     int64_t c64SecModTime;
    714     GET_TAR_NUMERIC_FIELD_RET(c64SecModTime,             pThis->Hdr.Common.mtime);
    715     RTTimeSpecSetSeconds(&pObjInfo->ChangeTime,          c64SecModTime);
    716     RTTimeSpecSetSeconds(&pObjInfo->ModificationTime,    c64SecModTime);
    717     RTTimeSpecSetSeconds(&pObjInfo->AccessTime,          c64SecModTime);
    718     RTTimeSpecSetSeconds(&pObjInfo->BirthTime,           c64SecModTime);
    719     if (c64SecModTime != RTTimeSpecGetSeconds(&pObjInfo->ModificationTime))
    720         return VERR_TAR_NUM_VALUE_TOO_LARGE;
    721     GET_TAR_NUMERIC_FIELD_RET(pObjInfo->Attr.fMode,      pThis->Hdr.Common.mode);
    722     pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
    723     GET_TAR_NUMERIC_FIELD_RET(pObjInfo->Attr.u.Unix.uid, pThis->Hdr.Common.uid);
    724     GET_TAR_NUMERIC_FIELD_RET(pObjInfo->Attr.u.Unix.gid, pThis->Hdr.Common.gid);
    725     pObjInfo->Attr.u.Unix.cHardlinks    = 1;
    726     pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
    727     pObjInfo->Attr.u.Unix.INodeId       = 0;
    728     pObjInfo->Attr.u.Unix.fFlags        = 0;
    729     pObjInfo->Attr.u.Unix.GenerationId  = 0;
    730     pObjInfo->Attr.u.Unix.Device        = 0;
    731     switch (pThis->enmType)
    732     {
    733         case RTZIPTARTYPE_POSIX:
    734         case RTZIPTARTYPE_GNU:
    735             if (   pThis->Hdr.Common.typeflag == RTZIPTAR_TF_CHR
    736                 || pThis->Hdr.Common.typeflag == RTZIPTAR_TF_BLK)
    737             {
    738                 uint32_t uMajor, uMinor;
    739                 GET_TAR_NUMERIC_FIELD_RET(uMajor,        pThis->Hdr.Common.devmajor);
    740                 GET_TAR_NUMERIC_FIELD_RET(uMinor,        pThis->Hdr.Common.devminor);
    741                 pObjInfo->Attr.u.Unix.Device    = RTDEV_MAKE(uMajor, uMinor);
    742                 if (   uMajor != RTDEV_MAJOR(pObjInfo->Attr.u.Unix.Device)
    743                     || uMinor != RTDEV_MINOR(pObjInfo->Attr.u.Unix.Device))
    744                     return VERR_TAR_DEV_VALUE_TOO_LARGE;
    745             }
    746             break;
    747 
    748         default:
    749             if (   pThis->Hdr.Common.typeflag == RTZIPTAR_TF_CHR
    750                 || pThis->Hdr.Common.typeflag == RTZIPTAR_TF_BLK)
    751                 return VERR_TAR_UNKNOWN_TYPE_FLAG;
    752     }
    753 
    754 #undef GET_TAR_NUMERIC_FIELD_RET
    755 
    756     /*
    757      * Massage the result a little bit.
    758      * Also validate some more now that we've got the numbers to work with.
    759      */
    760     if (   (pObjInfo->Attr.fMode & ~RTFS_UNIX_MASK)
    761         && pThis->enmType == RTZIPTARTYPE_POSIX)
    762         return VERR_TAR_BAD_MODE_FIELD;
    763     pObjInfo->Attr.fMode &= RTFS_UNIX_MASK;
    764 
    765     RTFMODE fModeType = 0;
    766     switch (pThis->Hdr.Common.typeflag)
    767     {
    768         case RTZIPTAR_TF_OLDNORMAL:
    769         case RTZIPTAR_TF_NORMAL:
    770         case RTZIPTAR_TF_CONTIG:
    771         {
    772             const char *pszEnd = strchr(pThis->szName, '\0');
    773             if (pszEnd == &pThis->szName[0] || pszEnd[-1] != '/')
    774                 fModeType |= RTFS_TYPE_FILE;
    775             else
    776                 fModeType |= RTFS_TYPE_DIRECTORY;
    777             break;
    778         }
    779 
    780         case RTZIPTAR_TF_LINK:
    781             if (pObjInfo->cbObject != 0)
    782 #if 0 /* too strict */
    783                 return VERR_TAR_SIZE_NOT_ZERO;
    784 #else
    785                 pObjInfo->cbObject = pObjInfo->cbAllocated = 0;
    786 #endif
    787             fModeType |= RTFS_TYPE_FILE; /* no better idea for now */
    788             break;
    789 
    790         case RTZIPTAR_TF_SYMLINK:
    791             fModeType |= RTFS_TYPE_SYMLINK;
    792             break;
    793 
    794         case RTZIPTAR_TF_CHR:
    795             fModeType |= RTFS_TYPE_DEV_CHAR;
    796             break;
    797 
    798         case RTZIPTAR_TF_BLK:
    799             fModeType |= RTFS_TYPE_DEV_BLOCK;
    800             break;
    801 
    802         case RTZIPTAR_TF_DIR:
    803             fModeType |= RTFS_TYPE_DIRECTORY;
    804             break;
    805 
    806         case RTZIPTAR_TF_FIFO:
    807             fModeType |= RTFS_TYPE_FIFO;
    808             break;
    809 
    810         case RTZIPTAR_TF_GNU_LONGLINK:
    811         case RTZIPTAR_TF_GNU_LONGNAME:
    812             /* ASSUMES RTFS_TYPE_XXX uses the same values as GNU stored in the mode field. */
    813             fModeType = pObjInfo->Attr.fMode & RTFS_TYPE_MASK;
    814             switch (fModeType)
    815             {
    816                 case RTFS_TYPE_FILE:
    817                 case RTFS_TYPE_DIRECTORY:
    818                 case RTFS_TYPE_SYMLINK:
    819                 case RTFS_TYPE_DEV_BLOCK:
    820                 case RTFS_TYPE_DEV_CHAR:
    821                 case RTFS_TYPE_FIFO:
    822                     break;
    823 
    824                 default:
    825                 case 0:
    826                     return VERR_TAR_UNKNOWN_TYPE_FLAG; /** @todo new status code */
    827             }
    828 
    829         default:
    830             return VERR_TAR_UNKNOWN_TYPE_FLAG; /* Should've been caught in validate. */
    831     }
    832     if (   (pObjInfo->Attr.fMode & RTFS_TYPE_MASK)
    833         && (pObjInfo->Attr.fMode & RTFS_TYPE_MASK) != fModeType)
    834         return VERR_TAR_MODE_WITH_TYPE;
    835     pObjInfo->Attr.fMode &= ~RTFS_TYPE_MASK;
    836     pObjInfo->Attr.fMode |= fModeType;
    837 
    838     switch (pThis->Hdr.Common.typeflag)
    839     {
    840         case RTZIPTAR_TF_CHR:
    841         case RTZIPTAR_TF_BLK:
    842         case RTZIPTAR_TF_DIR:
    843         case RTZIPTAR_TF_FIFO:
    844             pObjInfo->cbObject    = 0;
    845             pObjInfo->cbAllocated = 0;
    846             break;
    847     }
    848 
    849     return VINF_SUCCESS;
    850 }
    851 
    852 
    853 /**
    854  * Checks if the reader is expecting more headers.
    855  *
    856  * @returns true / false.
    857  * @param   pThis               The TAR reader instance.
    858  */
    859 static bool rtZipTarReaderExpectingMoreHeaders(PRTZIPTARREADER pThis)
    860 {
    861     return pThis->enmState != RTZIPTARREADERSTATE_FIRST;
    862 }
    863 
    864 
    865 /**
    866  * Checks if we're at the end of the TAR file.
    867  *
    868  * @returns true / false.
    869  * @param   pThis               The TAR reader instance.
    870  */
    871 static bool rtZipTarReaderIsAtEnd(PRTZIPTARREADER pThis)
    872 {
    873     /* Turns out our own tar writer code doesn't get this crap right.
    874        Kludge our way around it. */
    875     if (!pThis->cZeroHdrs)
    876         return pThis->enmPrevType == RTZIPTARTYPE_GNU ? true /* IPRT tar.cpp */ : false;
    877 
    878     /* Here is a kludge to try deal with archivers not putting at least two
    879        zero headers at the end.  Afraid it may require further relaxing
    880        later on, but let's try be strict about things for now. */
    881     return pThis->cZeroHdrs >= (pThis->enmPrevType == RTZIPTARTYPE_POSIX ? 2U : 1U);
    882 }
    883 
    884 
    885 /**
    886  * Checks if the current TAR object is a hard link or not.
    887  *
    888  * @returns true if it is, false if not.
    889  * @param   pThis               The TAR reader instance.
    890  */
    891 static bool rtZipTarReaderIsHardlink(PRTZIPTARREADER pThis)
    892 {
    893     return pThis->Hdr.Common.typeflag == RTZIPTAR_TF_LINK;
    894 }
    895 
    896 
    897 /**
    898  * Checks if the TAR header includes a POSIX or GNU user name field.
    899  *
    900  * @returns true / false.
    901  * @param   pThis               The TAR reader instance.
    902  */
    903 DECLINLINE(bool) rtZipTarReaderHasUserName(PRTZIPTARREADER pThis)
    904 {
    905     return pThis->Hdr.Common.uname[0] != '\0'
    906         && (   pThis->enmType == RTZIPTARTYPE_POSIX
    907             || pThis->enmType == RTZIPTARTYPE_GNU);
    908 }
    909 
    910 
    911 /**
    912  * Checks if the TAR header includes a POSIX or GNU group name field.
    913  *
    914  * @returns true / false.
    915  * @param   pThis               The TAR reader instance.
    916  */
    917 DECLINLINE(bool) rtZipTarReaderHasGroupName(PRTZIPTARREADER pThis)
    918 {
    919     return pThis->Hdr.Common.gname[0] != '\0'
    920         && (   pThis->enmType == RTZIPTARTYPE_POSIX
    921             || pThis->enmType == RTZIPTARTYPE_GNU);
    922 }
    923 
    924 
    925 /*
    926  *
    927  * 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.
    928  * 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.
    929  * 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.
    930  *
    931  */
     428        pThis->rcFatal = rc;
     429    }
     430    return rc;
     431}
     432
    932433
    933434/**
    934435 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
    935436 */
    936 static DECLCALLBACK(int) rtZipTarFssBaseObj_Close(void *pvThis)
    937 {
    938     PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis;
    939 
    940     /* Currently there is nothing we really have to do here. */
    941     pThis->offHdr = -1;
    942 
    943     return VINF_SUCCESS;
    944 }
    945 
    946 
    947 /**
    948  * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
    949  */
    950 static DECLCALLBACK(int) rtZipTarFssBaseObj_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
    951 {
    952     PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis;
    953 
    954     /*
    955      * Copy the desired data.
    956      */
    957     switch (enmAddAttr)
    958     {
    959         case RTFSOBJATTRADD_NOTHING:
    960         case RTFSOBJATTRADD_UNIX:
    961             *pObjInfo = pThis->ObjInfo;
    962             break;
    963 
    964         case RTFSOBJATTRADD_UNIX_OWNER:
    965             *pObjInfo = pThis->ObjInfo;
    966             pObjInfo->Attr.enmAdditional         = RTFSOBJATTRADD_UNIX_OWNER;
    967             pObjInfo->Attr.u.UnixOwner.uid       = pThis->ObjInfo.Attr.u.Unix.uid;
    968             pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
    969             if (rtZipTarReaderHasUserName(pThis->pTarReader))
    970                 RTStrCopy(pObjInfo->Attr.u.UnixOwner.szName, sizeof(pObjInfo->Attr.u.UnixOwner.szName),
    971                           pThis->pTarReader->Hdr.Common.uname);
    972             break;
    973 
    974         case RTFSOBJATTRADD_UNIX_GROUP:
    975             *pObjInfo = pThis->ObjInfo;
    976             pObjInfo->Attr.enmAdditional         = RTFSOBJATTRADD_UNIX_GROUP;
    977             pObjInfo->Attr.u.UnixGroup.gid       = pThis->ObjInfo.Attr.u.Unix.gid;
    978             pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
    979             if (rtZipTarReaderHasGroupName(pThis->pTarReader))
    980                 RTStrCopy(pObjInfo->Attr.u.UnixGroup.szName, sizeof(pObjInfo->Attr.u.UnixGroup.szName),
    981                           pThis->pTarReader->Hdr.Common.gname);
    982             break;
    983 
    984         case RTFSOBJATTRADD_EASIZE:
    985             *pObjInfo = pThis->ObjInfo;
    986             pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
    987             RT_ZERO(pObjInfo->Attr.u);
    988             break;
    989 
    990         default:
    991             return VERR_NOT_SUPPORTED;
    992     }
    993 
    994     return VINF_SUCCESS;
    995 }
    996 
    997 
    998 /**
    999  * Tar filesystem base object operations.
    1000  */
    1001 static const RTVFSOBJOPS g_rtZipTarFssBaseObjOps =
    1002 {
    1003     RTVFSOBJOPS_VERSION,
    1004     RTVFSOBJTYPE_BASE,
    1005     "TarFsStream::Obj",
    1006     rtZipTarFssBaseObj_Close,
    1007     rtZipTarFssBaseObj_QueryInfo,
    1008     RTVFSOBJOPS_VERSION
    1009 };
    1010 
    1011 
    1012 /**
    1013  * @interface_method_impl{RTVFSOBJOPS,pfnClose}
    1014  */
    1015 static DECLCALLBACK(int) rtZipTarFssIos_Close(void *pvThis)
    1016 {
    1017     PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis;
     437static DECLCALLBACK(int) rtZipTarFssWriter_Close(void *pvThis)
     438{
     439    PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
    1018440
    1019441    RTVfsIoStrmRelease(pThis->hVfsIos);
    1020442    pThis->hVfsIos = NIL_RTVFSIOSTREAM;
    1021443
    1022     return rtZipTarFssBaseObj_Close(&pThis->BaseObj);
     444    /** @todo investigate zero end records.  */
     445
     446    return VINF_SUCCESS;
    1023447}
    1024448
     
    1027451 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
    1028452 */
    1029 static DECLCALLBACK(int) rtZipTarFssIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
    1030 {
    1031     PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis;
    1032     return rtZipTarFssBaseObj_QueryInfo(&pThis->BaseObj, pObjInfo, enmAddAttr);
    1033 }
    1034 
    1035 
    1036 /**
    1037  * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
    1038  */
    1039 static DECLCALLBACK(int) rtZipTarFssIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
    1040 {
    1041     PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis;
    1042     Assert(pSgBuf->cSegs == 1);
    1043 
    1044     /*
    1045      * Make offset into a real offset so it's possible to do random access
    1046      * on TAR files that are seekable.  Fend of reads beyond the end of the
    1047      * stream.
    1048      */
    1049     if (off < 0)
    1050         off = pThis->offFile;
    1051     if (off >= pThis->cbFile)
    1052         return pcbRead ? VINF_EOF : VERR_EOF;
    1053 
    1054 
    1055     Assert(pThis->cbFile >= pThis->offFile);
    1056     uint64_t cbLeft   = (uint64_t)(pThis->cbFile - pThis->offFile);
    1057     size_t   cbToRead = pSgBuf->paSegs[0].cbSeg;
    1058     if (cbToRead > cbLeft)
    1059     {
    1060         if (!pcbRead)
    1061             return VERR_EOF;
    1062         cbToRead = (size_t)cbLeft;
    1063     }
    1064 
    1065     /*
    1066      * Do the reading.
    1067      */
    1068     size_t cbReadStack = 0;
    1069     if (!pcbRead)
    1070         pcbRead = &cbReadStack;
    1071     int rc = RTVfsIoStrmReadAt(pThis->hVfsIos, pThis->offStart + off, pSgBuf->paSegs[0].pvSeg, cbToRead, fBlocking, pcbRead);
    1072     pThis->offFile = off + *pcbRead;
    1073     if (pThis->offFile >= pThis->cbFile)
    1074     {
    1075         Assert(pThis->offFile == pThis->cbFile);
    1076         pThis->fEndOfStream = true;
    1077         RTVfsIoStrmSkip(pThis->hVfsIos, pThis->cbPadding);
    1078     }
    1079 
    1080     return rc;
    1081 }
    1082 
    1083 
    1084 /**
    1085  * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
    1086  */
    1087 static DECLCALLBACK(int) rtZipTarFssIos_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
    1088 {
    1089     /* Cannot write to a read-only I/O stream. */
    1090     NOREF(pvThis); NOREF(off); NOREF(pSgBuf); NOREF(fBlocking); NOREF(pcbWritten);
    1091     return VERR_ACCESS_DENIED;
    1092 }
    1093 
    1094 
    1095 /**
    1096  * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
    1097  */
    1098 static DECLCALLBACK(int) rtZipTarFssIos_Flush(void *pvThis)
    1099 {
    1100     /* It's a read only stream, nothing dirty to flush. */
    1101     NOREF(pvThis);
    1102     return VINF_SUCCESS;
    1103 }
    1104 
    1105 
    1106 /**
    1107  * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
    1108  */
    1109 static DECLCALLBACK(int) rtZipTarFssIos_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
    1110                                                 uint32_t *pfRetEvents)
    1111 {
    1112     PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis;
    1113 
    1114     /* When we've reached the end, read will be set to indicate it. */
    1115     if (   (fEvents & RTPOLL_EVT_READ)
    1116         && pThis->fEndOfStream)
    1117     {
    1118         int rc = RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, 0, fIntr, pfRetEvents);
    1119         if (RT_SUCCESS(rc))
    1120             *pfRetEvents |= RTPOLL_EVT_READ;
    1121         else
    1122             *pfRetEvents = RTPOLL_EVT_READ;
    1123         return VINF_SUCCESS;
    1124     }
    1125 
    1126     return RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, cMillies, fIntr, pfRetEvents);
    1127 }
    1128 
    1129 
    1130 /**
    1131  * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
    1132  */
    1133 static DECLCALLBACK(int) rtZipTarFssIos_Tell(void *pvThis, PRTFOFF poffActual)
    1134 {
    1135     PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis;
    1136     *poffActual = pThis->offFile;
    1137     return VINF_SUCCESS;
    1138 }
    1139 
    1140 
    1141 /**
    1142  * Tar I/O stream operations.
    1143  */
    1144 static const RTVFSIOSTREAMOPS g_rtZipTarFssIosOps =
    1145 {
    1146     { /* Obj */
    1147         RTVFSOBJOPS_VERSION,
    1148         RTVFSOBJTYPE_IO_STREAM,
    1149         "TarFsStream::IoStream",
    1150         rtZipTarFssIos_Close,
    1151         rtZipTarFssIos_QueryInfo,
    1152         RTVFSOBJOPS_VERSION
    1153     },
    1154     RTVFSIOSTREAMOPS_VERSION,
    1155     RTVFSIOSTREAMOPS_FEAT_NO_SG,
    1156     rtZipTarFssIos_Read,
    1157     rtZipTarFssIos_Write,
    1158     rtZipTarFssIos_Flush,
    1159     rtZipTarFssIos_PollOne,
    1160     rtZipTarFssIos_Tell,
    1161     NULL /*Skip*/,
    1162     NULL /*ZeroFill*/,
    1163     RTVFSIOSTREAMOPS_VERSION
    1164 };
    1165 
    1166 
    1167 /**
    1168  * @interface_method_impl{RTVFSOBJOPS,pfnClose}
    1169  */
    1170 static DECLCALLBACK(int) rtZipTarFssSym_Close(void *pvThis)
    1171 {
    1172     PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis;
    1173     return rtZipTarFssBaseObj_Close(pThis);
    1174 }
    1175 
    1176 
    1177 /**
    1178  * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
    1179  */
    1180 static DECLCALLBACK(int) rtZipTarFssSym_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
    1181 {
    1182     PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis;
    1183     return rtZipTarFssBaseObj_QueryInfo(pThis, pObjInfo, enmAddAttr);
    1184 }
    1185 
    1186 /**
    1187  * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
    1188  */
    1189 static DECLCALLBACK(int) rtZipTarFssSym_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
    1190 {
    1191     NOREF(pvThis); NOREF(fMode); NOREF(fMask);
    1192     return VERR_ACCESS_DENIED;
    1193 }
    1194 
    1195 
    1196 /**
    1197  * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
    1198  */
    1199 static DECLCALLBACK(int) rtZipTarFssSym_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
    1200                                                  PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
    1201 {
    1202     NOREF(pvThis); NOREF(pAccessTime); NOREF(pModificationTime); NOREF(pChangeTime); NOREF(pBirthTime);
    1203     return VERR_ACCESS_DENIED;
    1204 }
    1205 
    1206 
    1207 /**
    1208  * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
    1209  */
    1210 static DECLCALLBACK(int) rtZipTarFssSym_SetOwner(void *pvThis, RTUID uid, RTGID gid)
    1211 {
    1212     NOREF(pvThis); NOREF(uid); NOREF(gid);
    1213     return VERR_ACCESS_DENIED;
    1214 }
    1215 
    1216 
    1217 /**
    1218  * @interface_method_impl{RTVFSSYMLINKOPS,pfnRead}
    1219  */
    1220 static DECLCALLBACK(int) rtZipTarFssSym_Read(void *pvThis, char *pszTarget, size_t cbTarget)
    1221 {
    1222     PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis;
    1223     return RTStrCopy(pszTarget, cbTarget, pThis->pTarReader->szTarget);
    1224 }
    1225 
    1226 
    1227 /**
    1228  * Tar symbolic (and hardlink) operations.
    1229  */
    1230 static const RTVFSSYMLINKOPS g_rtZipTarFssSymOps =
    1231 {
    1232     { /* Obj */
    1233         RTVFSOBJOPS_VERSION,
    1234         RTVFSOBJTYPE_SYMLINK,
    1235         "TarFsStream::Symlink",
    1236         rtZipTarFssSym_Close,
    1237         rtZipTarFssSym_QueryInfo,
    1238         RTVFSOBJOPS_VERSION
    1239     },
    1240     RTVFSSYMLINKOPS_VERSION,
    1241     0,
    1242     { /* ObjSet */
    1243         RTVFSOBJSETOPS_VERSION,
    1244         RT_OFFSETOF(RTVFSSYMLINKOPS, Obj) - RT_OFFSETOF(RTVFSSYMLINKOPS, ObjSet),
    1245         rtZipTarFssSym_SetMode,
    1246         rtZipTarFssSym_SetTimes,
    1247         rtZipTarFssSym_SetOwner,
    1248         RTVFSOBJSETOPS_VERSION
    1249     },
    1250     rtZipTarFssSym_Read,
    1251     RTVFSSYMLINKOPS_VERSION
    1252 };
    1253 
    1254 
    1255 /**
    1256  * @interface_method_impl{RTVFSOBJOPS,pfnClose}
    1257  */
    1258 static DECLCALLBACK(int) rtZipTarFss_Close(void *pvThis)
    1259 {
    1260     PRTZIPTARFSSTREAM pThis = (PRTZIPTARFSSTREAM)pvThis;
    1261 
    1262     RTVfsObjRelease(pThis->hVfsCurObj);
    1263     pThis->hVfsCurObj  = NIL_RTVFSOBJ;
    1264     pThis->pCurIosData = NULL;
    1265 
    1266     RTVfsIoStrmRelease(pThis->hVfsIos);
    1267     pThis->hVfsIos = NIL_RTVFSIOSTREAM;
    1268 
    1269     return VINF_SUCCESS;
    1270 }
    1271 
    1272 
    1273 /**
    1274  * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
    1275  */
    1276 static DECLCALLBACK(int) rtZipTarFss_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
    1277 {
    1278     PRTZIPTARFSSTREAM pThis = (PRTZIPTARFSSTREAM)pvThis;
     453static DECLCALLBACK(int) rtZipTarFssWriter_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
     454{
     455    PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
    1279456    /* Take the lazy approach here, with the sideffect of providing some info
    1280457       that is actually kind of useful. */
     
    1284461
    1285462/**
    1286  * @interface_method_impl{RTVFSFSSTREAMOPS,pfnNext}
    1287  */
    1288 static DECLCALLBACK(int) rtZipTarFss_Next(void *pvThis, char **ppszName, RTVFSOBJTYPE *penmType, PRTVFSOBJ phVfsObj)
    1289 {
    1290     PRTZIPTARFSSTREAM pThis = (PRTZIPTARFSSTREAM)pvThis;
    1291 
    1292     /*
    1293      * Dispense with the current object.
    1294      */
    1295     if (pThis->hVfsCurObj != NIL_RTVFSOBJ)
    1296     {
    1297         if (pThis->pCurIosData)
    1298         {
    1299             pThis->pCurIosData->fEndOfStream = true;
    1300             pThis->pCurIosData->offFile      = pThis->pCurIosData->cbFile;
    1301             pThis->pCurIosData = NULL;
    1302         }
    1303 
    1304         RTVfsObjRelease(pThis->hVfsCurObj);
    1305         pThis->hVfsCurObj = NIL_RTVFSOBJ;
    1306     }
    1307 
    1308     /*
    1309      * Check if we've already reached the end in some way.
    1310      */
    1311     if (pThis->fEndOfStream)
    1312         return VERR_EOF;
    1313     if (pThis->rcFatal != VINF_SUCCESS)
    1314         return pThis->rcFatal;
    1315 
    1316     /*
    1317      * Make sure the input stream is in the right place.
    1318      */
    1319     RTFOFF offHdr = RTVfsIoStrmTell(pThis->hVfsIos);
    1320     while (   offHdr >= 0
    1321            && offHdr < pThis->offNextHdr)
    1322     {
    1323         int rc = RTVfsIoStrmSkip(pThis->hVfsIos, pThis->offNextHdr - offHdr);
    1324         if (RT_FAILURE(rc))
    1325         {
    1326             /** @todo Ignore if we're at the end of the stream? */
    1327             return pThis->rcFatal = rc;
    1328         }
    1329 
    1330         offHdr = RTVfsIoStrmTell(pThis->hVfsIos);
    1331     }
    1332 
    1333     if (offHdr < 0)
    1334         return pThis->rcFatal = (int)offHdr;
    1335     if (offHdr > pThis->offNextHdr)
    1336         return pThis->rcFatal = VERR_INTERNAL_ERROR_3;
    1337 
    1338     /*
    1339      * Consume TAR headers.
    1340      */
    1341     size_t cbHdrs = 0;
    1342     int rc;
    1343     do
    1344     {
    1345         /*
    1346          * Read the next header.
    1347          */
    1348         RTZIPTARHDR Hdr;
    1349         size_t cbRead;
    1350         rc = RTVfsIoStrmRead(pThis->hVfsIos, &Hdr, sizeof(Hdr), true /*fBlocking*/, &cbRead);
    1351         if (RT_FAILURE(rc))
    1352             return pThis->rcFatal = rc;
    1353         if (rc == VINF_EOF && cbRead == 0)
    1354         {
    1355             pThis->fEndOfStream = true;
    1356             return rtZipTarReaderIsAtEnd(&pThis->TarReader) ? VERR_EOF : VERR_TAR_UNEXPECTED_EOS;
    1357         }
    1358         if (cbRead != sizeof(Hdr))
    1359             return pThis->rcFatal = VERR_TAR_UNEXPECTED_EOS;
    1360 
    1361         cbHdrs += sizeof(Hdr);
    1362 
    1363         /*
    1364          * Parse the it.
    1365          */
    1366         rc = rtZipTarReaderParseHeader(&pThis->TarReader, &Hdr);
    1367         if (RT_FAILURE(rc))
    1368             return pThis->rcFatal = rc;
    1369     } while (rtZipTarReaderExpectingMoreHeaders(&pThis->TarReader));
    1370 
    1371     pThis->offNextHdr = offHdr + cbHdrs;
    1372 
    1373     /*
    1374      * Fill an object info structure from the current TAR state.
    1375      */
    1376     RTFSOBJINFO Info;
    1377     rc = rtZipTarReaderGetFsObjInfo(&pThis->TarReader, &Info);
    1378     if (RT_FAILURE(rc))
    1379         return pThis->rcFatal = rc;
    1380 
    1381     /*
    1382      * Create an object of the appropriate type.
    1383      */
    1384     RTVFSOBJTYPE    enmType;
    1385     RTVFSOBJ        hVfsObj;
    1386     RTFMODE         fType = Info.Attr.fMode & RTFS_TYPE_MASK;
    1387     if (rtZipTarReaderIsHardlink(&pThis->TarReader))
    1388         fType = RTFS_TYPE_SYMLINK;
    1389     switch (fType)
    1390     {
    1391         /*
    1392          * Files are represented by a VFS I/O stream.
    1393          */
    1394         case RTFS_TYPE_FILE:
    1395         {
    1396             RTVFSIOSTREAM       hVfsIos;
    1397             PRTZIPTARIOSTREAM   pIosData;
    1398             rc = RTVfsNewIoStream(&g_rtZipTarFssIosOps,
    1399                                   sizeof(*pIosData),
    1400                                   RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
    1401                                   NIL_RTVFS,
    1402                                   NIL_RTVFSLOCK,
    1403                                   &hVfsIos,
    1404                                   (void **)&pIosData);
    1405             if (RT_FAILURE(rc))
    1406                 return pThis->rcFatal = rc;
    1407 
    1408             pIosData->BaseObj.offHdr    = offHdr;
    1409             pIosData->BaseObj.pTarReader= &pThis->TarReader;
    1410             pIosData->BaseObj.ObjInfo   = Info;
    1411             pIosData->cbFile            = Info.cbObject;
    1412             pIosData->offFile           = 0;
    1413             pIosData->offStart          = RTVfsIoStrmTell(pThis->hVfsIos);
    1414             pIosData->cbPadding         = (uint32_t)(Info.cbAllocated - Info.cbObject);
    1415             pIosData->fEndOfStream      = false;
    1416             pIosData->hVfsIos           = pThis->hVfsIos;
    1417             RTVfsIoStrmRetain(pThis->hVfsIos);
    1418 
    1419             pThis->pCurIosData = pIosData;
    1420             pThis->offNextHdr += pIosData->cbFile + pIosData->cbPadding;
    1421 
    1422             enmType = RTVFSOBJTYPE_IO_STREAM;
    1423             hVfsObj = RTVfsObjFromIoStream(hVfsIos);
    1424             RTVfsIoStrmRelease(hVfsIos);
    1425             break;
    1426         }
    1427 
    1428         /*
    1429          * We represent hard links using a symbolic link object.  This fits
    1430          * best with the way TAR stores it and there is currently no better
    1431          * fitting VFS type alternative.
    1432          */
    1433         case RTFS_TYPE_SYMLINK:
    1434         {
    1435             RTVFSSYMLINK        hVfsSym;
    1436             PRTZIPTARBASEOBJ    pBaseObjData;
    1437             rc = RTVfsNewSymlink(&g_rtZipTarFssSymOps,
    1438                                  sizeof(*pBaseObjData),
    1439                                  NIL_RTVFS,
    1440                                  NIL_RTVFSLOCK,
    1441                                  &hVfsSym,
    1442                                  (void **)&pBaseObjData);
    1443             if (RT_FAILURE(rc))
    1444                 return pThis->rcFatal = rc;
    1445 
    1446             pBaseObjData->offHdr    = offHdr;
    1447             pBaseObjData->pTarReader= &pThis->TarReader;
    1448             pBaseObjData->ObjInfo   = Info;
    1449 
    1450             enmType = RTVFSOBJTYPE_SYMLINK;
    1451             hVfsObj = RTVfsObjFromSymlink(hVfsSym);
    1452             RTVfsSymlinkRelease(hVfsSym);
    1453             break;
    1454         }
    1455 
    1456         /*
    1457          * All other objects are repesented using a VFS base object since they
    1458          * carry no data streams (unless some TAR extension implements extended
    1459          * attributes / alternative streams).
    1460          */
    1461         case RTFS_TYPE_DEV_BLOCK:
    1462         case RTFS_TYPE_DEV_CHAR:
    1463         case RTFS_TYPE_DIRECTORY:
    1464         case RTFS_TYPE_FIFO:
    1465         {
    1466             PRTZIPTARBASEOBJ pBaseObjData;
    1467             rc = RTVfsNewBaseObj(&g_rtZipTarFssBaseObjOps,
    1468                                  sizeof(*pBaseObjData),
    1469                                  NIL_RTVFS,
    1470                                  NIL_RTVFSLOCK,
    1471                                  &hVfsObj,
    1472                                  (void **)&pBaseObjData);
    1473             if (RT_FAILURE(rc))
    1474                 return pThis->rcFatal = rc;
    1475 
    1476             pBaseObjData->offHdr    = offHdr;
    1477             pBaseObjData->pTarReader= &pThis->TarReader;
    1478             pBaseObjData->ObjInfo   = Info;
    1479 
    1480             enmType = RTVFSOBJTYPE_BASE;
    1481             break;
    1482         }
    1483 
    1484         default:
    1485             AssertFailed();
    1486             return pThis->rcFatal = VERR_INTERNAL_ERROR_5;
    1487     }
    1488     pThis->hVfsCurObj = hVfsObj;
    1489 
    1490     /*
    1491      * Set the return data and we're done.
    1492      */
    1493     if (ppszName)
    1494     {
    1495         rc = RTStrDupEx(ppszName, pThis->TarReader.szName);
    1496         if (RT_FAILURE(rc))
    1497             return rc;
    1498     }
    1499 
    1500     if (phVfsObj)
    1501     {
    1502         RTVfsObjRetain(hVfsObj);
    1503         *phVfsObj = hVfsObj;
    1504     }
    1505 
    1506     if (penmType)
    1507         *penmType = enmType;
    1508 
    1509     return VINF_SUCCESS;
     463 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnAdd}
     464 */
     465static DECLCALLBACK(int) rtZipTarFssWriter_Add(void *pvThis, const char *pszPath, RTVFSOBJ hVfsObj, uint32_t fFlags)
     466{
     467    PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
     468    RT_NOREF(pThis, pszPath, hVfsObj, fFlags);
     469
     470    /*
     471     * Refuse to do anything if we've encountered a fatal error.
     472     * Assert this because the caller should know better than calling us again.
     473     */
     474    AssertRCReturn(pThis->rcFatal, pThis->rcFatal);
     475
     476    /*
     477     * Query information about the object.
     478     */
     479    RTFSOBJINFO ObjInfo;
     480    int rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_UNIX);
     481    AssertRCReturn(rc, rc);
     482
     483    RTFSOBJINFO ObjOwnerName;
     484    rc = RTVfsObjQueryInfo(hVfsObj, &ObjOwnerName, RTFSOBJATTRADD_UNIX_OWNER);
     485    if (RT_FAILURE(rc) || ObjOwnerName.Attr.u.UnixOwner.szName[0] == '\0')
     486        strcpy(ObjOwnerName.Attr.u.UnixOwner.szName, "someone");
     487
     488    RTFSOBJINFO ObjGrpName;
     489    rc = RTVfsObjQueryInfo(hVfsObj, &ObjGrpName, RTFSOBJATTRADD_UNIX_GROUP);
     490    if (RT_FAILURE(rc) || ObjGrpName.Attr.u.UnixGroup.szName[0] == '\0')
     491        strcpy(ObjGrpName.Attr.u.UnixGroup.szName, "somegroup");
     492
     493    /*
     494     * Do type specific handling.
     495     */
     496    if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
     497    {
     498        RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
     499        AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_WRONG_TYPE);
     500        rc = rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, fFlags, &ObjInfo,
     501                                       ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
     502        RTVfsIoStrmRelease(hVfsIos);
     503    }
     504    else if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
     505    {
     506        RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
     507        AssertReturn(hVfsSymlink != NIL_RTVFSSYMLINK, VERR_WRONG_TYPE);
     508        rc = rtZipTarFssWriter_AddSymlink(pThis, pszPath, hVfsSymlink, &ObjInfo,
     509                                          ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
     510        RTVfsSymlinkRelease(hVfsSymlink);
     511    }
     512    else
     513        rc = rtZipTarFssWriter_AddSimpleObject(pThis, pszPath, &ObjInfo,
     514                                               ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
     515
     516    return rc;
    1510517}
    1511518
     
    1520527        RTVFSOBJOPS_VERSION,
    1521528        RTVFSOBJTYPE_FS_STREAM,
    1522         "TarFsStream",
    1523         rtZipTarFss_Close,
    1524         rtZipTarFss_QueryInfo,
     529        "TarFsStreamWriter",
     530        rtZipTarFssWriter_Close,
     531        rtZipTarFssWriter_QueryInfo,
    1525532        RTVFSOBJOPS_VERSION
    1526533    },
    1527534    RTVFSFSSTREAMOPS_VERSION,
    1528535    0,
    1529     rtZipTarFss_Next,
     536    NULL,
     537    rtZipTarFssWriter_Add,
    1530538    RTVFSFSSTREAMOPS_VERSION
    1531539};
    1532540
    1533541
    1534 RTDECL(int) RTZipTarFsStreamFromIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
     542RTDECL(int) RTZipTarFsStreamToIoStream(RTVFSIOSTREAM hVfsIosOut, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
    1535543{
    1536544    /*
     
    1539547    AssertPtrReturn(phVfsFss, VERR_INVALID_HANDLE);
    1540548    *phVfsFss = NIL_RTVFSFSSTREAM;
    1541     AssertPtrReturn(hVfsIosIn, VERR_INVALID_HANDLE);
     549    AssertPtrReturn(hVfsIosOut, VERR_INVALID_HANDLE);
    1542550    AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
    1543551
    1544     RTFOFF const offStart = RTVfsIoStrmTell(hVfsIosIn);
    1545     AssertReturn(offStart >= 0, (int)offStart);
    1546 
    1547     uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosIn);
     552    uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosOut);
    1548553    AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
    1549554
     
    1551556     * Retain the input stream and create a new filesystem stream handle.
    1552557     */
    1553     PRTZIPTARFSSTREAM pThis;
    1554     RTVFSFSSTREAM     hVfsFss;
    1555     int rc = RTVfsNewFsStream(&rtZipTarFssOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFss, (void **)&pThis);
     558    PRTZIPTARFSSTREAMWRITER pThis;
     559    RTVFSFSSTREAM           hVfsFss;
     560    int rc = RTVfsNewFsStream(&rtZipTarFssOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, false /*fReadOnly*/,
     561                              &hVfsFss, (void **)&pThis);
    1556562    if (RT_SUCCESS(rc))
    1557563    {
    1558         pThis->hVfsIos              = hVfsIosIn;
    1559         pThis->hVfsCurObj           = NIL_RTVFSOBJ;
    1560         pThis->pCurIosData          = NULL;
    1561         pThis->offStart             = offStart;
    1562         pThis->offNextHdr           = offStart;
    1563         pThis->fEndOfStream         = false;
    1564         pThis->rcFatal              = VINF_SUCCESS;
    1565         pThis->TarReader.enmPrevType= RTZIPTARTYPE_INVALID;
    1566         pThis->TarReader.enmType    = RTZIPTARTYPE_INVALID;
    1567         pThis->TarReader.enmState   = RTZIPTARREADERSTATE_FIRST;
    1568 
    1569         /* Don't check if it's a TAR stream here, do that in the
    1570            rtZipTarFss_Next. */
     564        pThis->hVfsIos = hVfsIosOut;
     565        pThis->rcFatal = VINF_SUCCESS;
    1571566
    1572567        *phVfsFss = hVfsFss;
     
    1574569    }
    1575570
    1576     RTVfsIoStrmRelease(hVfsIosIn);
     571    RTVfsIoStrmRelease(hVfsIosOut);
    1577572    return rc;
    1578573}
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette