/**
 * Add an entry for an I/O stream using a passthru stream.
 *
 * The passthru I/O stream will hash all the data read from or written to the
 * stream and automatically add an entry to the manifest with the desired
 * attributes when it is released.  Alternatively one can call
 * RTManifestPtIosAddEntryNow() to have more control over exactly when this
 * action is performed and which status it yields.
 *
 * @returns IPRT status code.
 * @param   hManifest           The manifest to add the entry to.
 * @param   hVfsIos             The I/O stream to pass thru to/from.
 * @param   pszEntry            The entry name.
 * @param   fAttrs              The attributes to create for this stream.
 * @param   fReadOrWrite        Whether it's a read or write I/O stream.
 * @param   phVfsIosPassthru    Where to return the new handle.
 */
RTDECL(int) RTManifestEntryAddPassthruIoStream(RTMANIFEST hManifest, RTVFSIOSTREAM hVfsIos, const char *pszEntry,
                                               uint32_t fAttrs, bool fReadOrWrite, PRTVFSIOSTREAM phVfsIosPassthru)
{
    /*
     * Validate input.
     */
    AssertReturn(fAttrs < RTMANIFEST_ATTR_END, VERR_INVALID_PARAMETER);
    AssertPtr(pszEntry);
    AssertPtr(phVfsIosPassthru);
    uint32_t cRefs = RTManifestRetain(hManifest);
    AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
    cRefs = RTVfsIoStrmRetain(hVfsIos);
    AssertReturnStmt(cRefs != UINT32_MAX, RTManifestRelease(hManifest), VERR_INVALID_HANDLE);

    /*
     * Create an instace of the passthru I/O stream.
     */
    PRTMANIFESTPTIOS pThis;
    RTVFSIOSTREAM    hVfsPtIos;
    int rc = RTVfsNewIoStream(&g_rtManifestPassthruIosOps, sizeof(*pThis), fReadOrWrite ? RTFILE_O_READ : RTFILE_O_WRITE,
                              NIL_RTVFS, NIL_RTVFSLOCK, &hVfsPtIos, (void **)&pThis);
    if (RT_SUCCESS(rc))
    {
        pThis->hVfsIos          = hVfsIos;
        pThis->pHashes          = rtManifestHashesCreate(fAttrs);
        pThis->hManifest        = hManifest;
        pThis->fReadOrWrite     = fReadOrWrite;
        pThis->fAddedEntry      = false;
        pThis->pszEntry         = RTStrDup(pszEntry);
        if (pThis->pszEntry && pThis->pHashes)
        {
            *phVfsIosPassthru = hVfsPtIos;
            return VINF_SUCCESS;
        }

        RTVfsIoStrmRelease(hVfsPtIos);
    }
    else
    {
        RTVfsIoStrmRelease(hVfsIos);
        RTManifestRelease(hManifest);
    }
    return rc;
}
RTDECL(int) RTZipGzipDecompressIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSIOSTREAM phVfsIosOut)
{
    AssertPtrReturn(hVfsIosIn, VERR_INVALID_HANDLE);
    AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
    AssertPtrReturn(phVfsIosOut, VERR_INVALID_POINTER);

    uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosIn);
    AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);

    /*
     * Create the decompression I/O stream.
     */
    RTVFSIOSTREAM    hVfsIos;
    PRTZIPGZIPSTREAM pThis;
    int rc = RTVfsNewIoStream(&g_rtZipGzipOps, sizeof(RTZIPGZIPSTREAM), RTFILE_O_READ, NIL_RTVFS, NIL_RTVFSLOCK,
                              &hVfsIos, (void **)&pThis);
    if (RT_SUCCESS(rc))
    {
        pThis->hVfsIos      = hVfsIosIn;
        pThis->offStream    = 0;
        pThis->fDecompress  = true;
        pThis->SgSeg.pvSeg  = &pThis->abBuffer[0];
        pThis->SgSeg.cbSeg  = sizeof(pThis->abBuffer);
        RTSgBufInit(&pThis->SgBuf, &pThis->SgSeg, 1);

        memset(&pThis->Zlib, 0, sizeof(pThis->Zlib));
        pThis->Zlib.opaque  = pThis;
        rc = inflateInit2(&pThis->Zlib, MAX_WBITS + 16 /* autodetect gzip header */);
        if (rc >= 0)
        {
            /*
             * Read the gzip header from the input stream to check that it's
             * a gzip stream.
             *
             * Note!. Since we've told zlib to check for the gzip header, we
             *        prebuffer what we read in the input buffer so it can
             *        be handed on to zlib later on.
             */
            rc = RTVfsIoStrmRead(pThis->hVfsIos, pThis->abBuffer, sizeof(RTZIPGZIPHDR), true /*fBlocking*/, NULL /*pcbRead*/);
            if (RT_SUCCESS(rc))
            {
                /* Validate the header and make a copy of it. */
                PCRTZIPGZIPHDR pHdr = (PCRTZIPGZIPHDR)pThis->abBuffer;
                if (   pHdr->bId1 != RTZIPGZIPHDR_ID1
                    || pHdr->bId2 != RTZIPGZIPHDR_ID2
                    || pHdr->fFlags & ~RTZIPGZIPHDR_FLG_VALID_MASK)
                    rc = VERR_ZIP_BAD_HEADER;
                else if (pHdr->bCompressionMethod != RTZIPGZIPHDR_CM_DEFLATE)
                    rc = VERR_ZIP_UNSUPPORTED_METHOD;
                else
                {
                    pThis->Hdr = *pHdr;
                    pThis->Zlib.avail_in = sizeof(RTZIPGZIPHDR);
                    pThis->Zlib.next_in  = &pThis->abBuffer[0];

                    /* Parse on if there are names or comments. */
                    if (pHdr->fFlags & (RTZIPGZIPHDR_FLG_NAME | RTZIPGZIPHDR_FLG_COMMENT))
                    {
                        /** @todo Can implement this when someone needs the
                         *        name or comment for something useful. */
                    }
                    if (RT_SUCCESS(rc))
                    {
                        *phVfsIosOut = hVfsIos;
                        return VINF_SUCCESS;
                    }
                }
            }
        }
        else
            rc = rtZipGzipConvertErrFromZlib(pThis, rc); /** @todo cleaning up in this situation is going to go wrong. */
        RTVfsIoStrmRelease(hVfsIos);
    }
    else
        RTVfsIoStrmRelease(hVfsIosIn);
    return rc;
}
Beispiel #3
0
/**
 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnNext}
 */
static DECLCALLBACK(int) rtZipTarFss_Next(void *pvThis, char **ppszName, RTVFSOBJTYPE *penmType, PRTVFSOBJ phVfsObj)
{
    PRTZIPTARFSSTREAM pThis = (PRTZIPTARFSSTREAM)pvThis;

    /*
     * Dispense with the current object.
     */
    if (pThis->hVfsCurObj != NIL_RTVFSOBJ)
    {
        if (pThis->pCurIosData)
        {
            pThis->pCurIosData->fEndOfStream = true;
            pThis->pCurIosData->offFile      = pThis->pCurIosData->cbFile;
            pThis->pCurIosData = NULL;
        }

        RTVfsObjRelease(pThis->hVfsCurObj);
        pThis->hVfsCurObj = NIL_RTVFSOBJ;
    }

    /*
     * Check if we've already reached the end in some way.
     */
    if (pThis->fEndOfStream)
        return VERR_EOF;
    if (pThis->rcFatal != VINF_SUCCESS)
        return pThis->rcFatal;

    /*
     * Make sure the input stream is in the right place.
     */
    RTFOFF offHdr = RTVfsIoStrmTell(pThis->hVfsIos);
    while (   offHdr >= 0
           && offHdr < pThis->offNextHdr)
    {
        int rc = RTVfsIoStrmSkip(pThis->hVfsIos, pThis->offNextHdr - offHdr);
        if (RT_FAILURE(rc))
        {
            /** @todo Ignore if we're at the end of the stream? */
            return pThis->rcFatal = rc;
        }

        offHdr = RTVfsIoStrmTell(pThis->hVfsIos);
    }

    if (offHdr < 0)
        return pThis->rcFatal = (int)offHdr;
    if (offHdr > pThis->offNextHdr)
        return pThis->rcFatal = VERR_INTERNAL_ERROR_3;

    /*
     * Consume TAR headers.
     */
    size_t cbHdrs = 0;
    int rc;
    do
    {
        /*
         * Read the next header.
         */
        RTZIPTARHDR Hdr;
        size_t cbRead;
        rc = RTVfsIoStrmRead(pThis->hVfsIos, &Hdr, sizeof(Hdr), true /*fBlocking*/, &cbRead);
        if (RT_FAILURE(rc))
            return pThis->rcFatal = rc;
        if (rc == VINF_EOF && cbRead == 0)
        {
            pThis->fEndOfStream = true;
            return rtZipTarReaderIsAtEnd(&pThis->TarReader) ? VERR_EOF : VERR_TAR_UNEXPECTED_EOS;
        }
        if (cbRead != sizeof(Hdr))
            return pThis->rcFatal = VERR_TAR_UNEXPECTED_EOS;

        cbHdrs += sizeof(Hdr);

        /*
         * Parse the it.
         */
        rc = rtZipTarReaderParseHeader(&pThis->TarReader, &Hdr);
        if (RT_FAILURE(rc))
            return pThis->rcFatal = rc;
    } while (rtZipTarReaderExpectingMoreHeaders(&pThis->TarReader));

    pThis->offNextHdr = offHdr + cbHdrs;

    /*
     * Fill an object info structure from the current TAR state.
     */
    RTFSOBJINFO Info;
    rc = rtZipTarReaderGetFsObjInfo(&pThis->TarReader, &Info);
    if (RT_FAILURE(rc))
        return pThis->rcFatal = rc;

    /*
     * Create an object of the appropriate type.
     */
    RTVFSOBJTYPE    enmType;
    RTVFSOBJ        hVfsObj;
    RTFMODE         fType = Info.Attr.fMode & RTFS_TYPE_MASK;
    if (rtZipTarReaderIsHardlink(&pThis->TarReader))
        fType = RTFS_TYPE_SYMLINK;
    switch (fType)
    {
        /*
         * Files are represented by a VFS I/O stream.
         */
        case RTFS_TYPE_FILE:
        {
            RTVFSIOSTREAM       hVfsIos;
            PRTZIPTARIOSTREAM   pIosData;
            rc = RTVfsNewIoStream(&g_rtZipTarFssIosOps,
                                  sizeof(*pIosData),
                                  RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
                                  NIL_RTVFS,
                                  NIL_RTVFSLOCK,
                                  &hVfsIos,
                                  (void **)&pIosData);
            if (RT_FAILURE(rc))
                return pThis->rcFatal = rc;

            pIosData->BaseObj.offHdr    = offHdr;
            pIosData->BaseObj.pTarReader= &pThis->TarReader;
            pIosData->BaseObj.ObjInfo   = Info;
            pIosData->cbFile            = Info.cbObject;
            pIosData->offFile           = 0;
            pIosData->cbPadding         = (uint32_t)(Info.cbAllocated - Info.cbObject);
            pIosData->fEndOfStream      = false;
            pIosData->hVfsIos           = pThis->hVfsIos;
            RTVfsIoStrmRetain(pThis->hVfsIos);

            pThis->pCurIosData = pIosData;
            pThis->offNextHdr += pIosData->cbFile + pIosData->cbPadding;

            enmType = RTVFSOBJTYPE_IO_STREAM;
            hVfsObj = RTVfsObjFromIoStream(hVfsIos);
            RTVfsIoStrmRelease(hVfsIos);
            break;
        }

        /*
         * We represent hard links using a symbolic link object.  This fits
         * best with the way TAR stores it and there is currently no better
         * fitting VFS type alternative.
         */
        case RTFS_TYPE_SYMLINK:
        {
            RTVFSSYMLINK        hVfsSym;
            PRTZIPTARBASEOBJ    pBaseObjData;
            rc = RTVfsNewSymlink(&g_rtZipTarFssSymOps,
                                 sizeof(*pBaseObjData),
                                 NIL_RTVFS,
                                 NIL_RTVFSLOCK,
                                 &hVfsSym,
                                 (void **)&pBaseObjData);
            if (RT_FAILURE(rc))
                return pThis->rcFatal = rc;

            pBaseObjData->offHdr    = offHdr;
            pBaseObjData->pTarReader= &pThis->TarReader;
            pBaseObjData->ObjInfo   = Info;

            enmType = RTVFSOBJTYPE_SYMLINK;
            hVfsObj = RTVfsObjFromSymlink(hVfsSym);
            RTVfsSymlinkRelease(hVfsSym);
            break;
        }

        /*
         * All other objects are repesented using a VFS base object since they
         * carry no data streams (unless some TAR extension implements extended
         * attributes / alternative streams).
         */
        case RTFS_TYPE_DEV_BLOCK:
        case RTFS_TYPE_DEV_CHAR:
        case RTFS_TYPE_DIRECTORY:
        case RTFS_TYPE_FIFO:
        {
            PRTZIPTARBASEOBJ pBaseObjData;
            rc = RTVfsNewBaseObj(&g_rtZipTarFssBaseObjOps,
                                 sizeof(*pBaseObjData),
                                 NIL_RTVFS,
                                 NIL_RTVFSLOCK,
                                 &hVfsObj,
                                 (void **)&pBaseObjData);
            if (RT_FAILURE(rc))
                return pThis->rcFatal = rc;

            pBaseObjData->offHdr    = offHdr;
            pBaseObjData->pTarReader= &pThis->TarReader;
            pBaseObjData->ObjInfo   = Info;

            enmType = RTVFSOBJTYPE_BASE;
            break;
        }

        default:
            AssertFailed();
            return pThis->rcFatal = VERR_INTERNAL_ERROR_5;
    }
    pThis->hVfsCurObj = hVfsObj;

    /*
     * Set the return data and we're done.
     */
    if (ppszName)
    {
        rc = RTStrDupEx(ppszName, pThis->TarReader.szName);
        if (RT_FAILURE(rc))
            return rc;
    }

    if (phVfsObj)
    {
        RTVfsObjRetain(hVfsObj);
        *phVfsObj = hVfsObj;
    }

    if (penmType)
        *penmType = enmType;

    return VINF_SUCCESS;
}