/** * Reads one segment. * * @returns IPRT status code. * @param pThis The instance data. * @param pvBuf Where to put the read bytes. * @param cbToRead The number of bytes to read. * @param fBlocking Whether to block or not. * @param pcbRead Where to store the number of bytes actually read. */ static int rtZipTarFssIos_ReadOneSeg(PRTZIPTARIOSTREAM pThis, void *pvBuf, size_t cbToRead, bool fBlocking, size_t *pcbRead) { /* * Fend of reads beyond the end of the stream here. */ if (pThis->fEndOfStream) return pcbRead ? VINF_EOF : VERR_EOF; Assert(pThis->cbFile >= pThis->offFile); uint64_t cbLeft = (uint64_t)(pThis->cbFile - pThis->offFile); if (cbToRead > cbLeft) { if (!pcbRead) return VERR_EOF; cbToRead = (size_t)cbLeft; } /* * Do the reading. */ size_t cbReadStack = 0; if (!pcbRead) pcbRead = &cbReadStack; int rc = RTVfsIoStrmRead(pThis->hVfsIos, pvBuf, cbToRead, fBlocking, pcbRead); pThis->offFile += *pcbRead; if (pThis->offFile >= pThis->cbFile) { Assert(pThis->offFile == pThis->cbFile); pThis->fEndOfStream = true; RTVfsIoStrmSkip(pThis->hVfsIos, pThis->cbPadding); } return rc; }
/** * Reads a line from a VFS I/O stream. * * @todo Replace this with a buffered I/O stream layer. * * @returns IPRT status code. VERR_EOF when trying to read beyond the stream * end. * @param hVfsIos The I/O stream to read from. * @param pszLine Where to store what we've read. * @param cbLine The number of bytes to read. */ static int rtManifestReadLine(RTVFSIOSTREAM hVfsIos, char *pszLine, size_t cbLine) { /* This is horribly slow right now, but it's not a biggy as the input is usually cached in memory somewhere... */ *pszLine = '\0'; while (cbLine > 1) { char ch; int rc = RTVfsIoStrmRead(hVfsIos, &ch, 1, true /*fBLocking*/, NULL); if (RT_FAILURE(rc)) return rc; /* \r\n */ if (ch == '\r') { if (cbLine <= 2) { pszLine[0] = ch; pszLine[1] = '\0'; return VINF_BUFFER_OVERFLOW; } rc = RTVfsIoStrmRead(hVfsIos, &ch, 1, true /*fBLocking*/, NULL); if (RT_SUCCESS(rc) && ch == '\n') return VINF_SUCCESS; pszLine[0] = '\r'; pszLine[1] = ch; pszLine[2] = '\0'; if (RT_FAILURE(rc)) return rc == VERR_EOF ? VINF_EOF : rc; } /* \n */ if (ch == '\n') return VINF_SUCCESS; /* add character. */ pszLine[0] = ch; pszLine[1] = '\0'; /* advance */ pszLine++; cbLine--; } return VINF_BUFFER_OVERFLOW; }
/** * Adds an entry for a file with the specified set of attributes. * * @returns IPRT status code. * * @param hManifest The manifest handle. * @param hVfsIos The I/O stream handle of the entry. This will * be processed to its end on successful return. * (Must be positioned at the start to get * the expected results.) * @param pszEntry The entry name. * @param fAttrs The attributes to create for this stream. */ RTDECL(int) RTManifestEntryAddIoStream(RTMANIFEST hManifest, RTVFSIOSTREAM hVfsIos, const char *pszEntry, uint32_t fAttrs) { /* * Note! This is a convenicence function, so just use the available public * methods to get the job done. */ AssertReturn(fAttrs < RTMANIFEST_ATTR_END, VERR_INVALID_PARAMETER); AssertPtr(pszEntry); /* * Allocate and initialize the hash contexts, hash digests and I/O buffer. */ PRTMANIFESTHASHES pHashes = rtManifestHashesCreate(fAttrs); if (!pHashes) return VERR_NO_TMP_MEMORY; int rc; size_t cbBuf = _1M; void *pvBuf = RTMemTmpAlloc(cbBuf); if (RT_UNLIKELY(!pvBuf)) { cbBuf = _4K; pvBuf = RTMemTmpAlloc(cbBuf); } if (RT_LIKELY(pvBuf)) { /* * Process the stream data. */ for (;;) { size_t cbRead; rc = RTVfsIoStrmRead(hVfsIos, pvBuf, cbBuf, true /*fBlocking*/, &cbRead); if ( (rc == VINF_EOF && cbRead == 0) || RT_FAILURE(rc)) break; rtManifestHashesUpdate(pHashes, pvBuf, cbRead); } RTMemTmpFree(pvBuf); if (RT_SUCCESS(rc)) { /* * Add the entry with the finalized hashes. */ rtManifestHashesFinal(pHashes); rc = RTManifestEntryAdd(hManifest, pszEntry); if (RT_SUCCESS(rc)) rc = rtManifestHashesSetAttrs(pHashes, hManifest, pszEntry); } } else { rtManifestHashesDestroy(pHashes); rc = VERR_NO_TMP_MEMORY; } return rc; }
/** * Pushes data from the input to the output I/O streams. * * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE. * @param hVfsSrc The source I/O stream. * @param hVfsDst The destination I/O stream. */ static RTEXITCODE gzipPush(RTVFSIOSTREAM hVfsSrc, RTVFSIOSTREAM hVfsDst) { for (;;) { uint8_t abBuf[_64K]; size_t cbRead; int rc = RTVfsIoStrmRead(hVfsSrc, abBuf, sizeof(abBuf), true /*fBlocking*/, &cbRead); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsIoStrmRead failed: %Rrc", rc); if (rc == VINF_EOF && cbRead == 0) return RTEXITCODE_SUCCESS; rc = RTVfsIoStrmWrite(hVfsDst, abBuf, cbRead, true /*fBlocking*/, NULL /*cbWritten*/); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsIoStrmWrite failed: %Rrc", rc); } }
/** * For testing the archive (todo). * * @returns Exit code. * @param phVfsSrc The input stream. Set to NIL if closed. * @param pOpts The options. */ static RTEXITCODE gzipTestFile(PRTVFSIOSTREAM phVfsSrc, PCRTGZIPCMDOPTS pOpts) { /* * Read the whole stream. */ RTEXITCODE rcExit = gzipSetupDecompressor(phVfsSrc); if (rcExit == RTEXITCODE_SUCCESS) { for (;;) { uint8_t abBuf[_64K]; size_t cbRead; int rc = RTVfsIoStrmRead(*phVfsSrc, abBuf, sizeof(abBuf), true /*fBlocking*/, &cbRead); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsIoStrmRead failed: %Rrc", rc); if (rc == VINF_EOF && cbRead == 0) return RTEXITCODE_SUCCESS; } } return rcExit; }
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; }
/** * @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; }
RTDECL(int) RTVfsIoStrmValidateUtf8Encoding(RTVFSIOSTREAM hVfsIos, uint32_t fFlags, PRTFOFF poffError) { /* * Validate input. */ if (poffError) { AssertPtrReturn(poffError, VINF_SUCCESS); *poffError = 0; } AssertReturn(!(fFlags & ~RTVFS_VALIDATE_UTF8_VALID_MASK), VERR_INVALID_PARAMETER); /* * The loop. */ char achBuf[1024 + 1]; size_t cbUsed = 0; int rc; for (;;) { /* * Fill the buffer */ size_t cbRead = 0; rc = RTVfsIoStrmRead(hVfsIos, &achBuf[cbUsed], sizeof(achBuf) - cbUsed - 1, true /*fBlocking*/, &cbRead); if (RT_FAILURE(rc)) break; cbUsed += cbRead; if (!cbUsed) { Assert(rc == VINF_EOF); break; } achBuf[sizeof(achBuf) - 1] = '\0'; /* * Process the data in the buffer, maybe leaving the final chars till * the next round. */ const char *pszCur = achBuf; size_t offEnd = rc == VINF_EOF ? cbUsed : cbUsed >= 7 ? cbUsed - 7 : 0; size_t off; while ((off = (pszCur - &achBuf[0])) < offEnd) { RTUNICP uc; rc = RTStrGetCpEx(&pszCur, &uc); if (RT_FAILURE(rc)) break; if (!uc) { if (fFlags & RTVFS_VALIDATE_UTF8_NO_NULL) { rc = VERR_INVALID_UTF8_ENCODING; break; } } else if (uc > 0x10ffff) { if (fFlags & RTVFS_VALIDATE_UTF8_BY_RTC_3629) { rc = VERR_INVALID_UTF8_ENCODING; break; } } } if (off < cbUsed) { cbUsed -= off; memmove(achBuf, pszCur, cbUsed); } } /* * Set the offset on failure. */ if (poffError && RT_FAILURE(rc)) { } return rc == VINF_EOF ? VINF_SUCCESS : rc; }
RTDECL(int) RTVfsIoStrmReadAll(RTVFSIOSTREAM hVfsIos, void **ppvBuf, size_t *pcbBuf) { /* * Try query the object information and in case the stream has a known * size we could use for guidance. */ RTFSOBJINFO ObjInfo; int rc = RTVfsIoStrmQueryInfo(hVfsIos, &ObjInfo, RTFSOBJATTRADD_NOTHING); size_t cbAllocated = RT_SUCCESS(rc) && ObjInfo.cbObject > 0 && ObjInfo.cbObject < _1G ? (size_t)ObjInfo.cbObject + 1 : _16K; cbAllocated += READ_ALL_HEADER_SIZE; void *pvBuf = RTMemAlloc(cbAllocated); if (pvBuf) { memset(pvBuf, 0xfe, READ_ALL_HEADER_SIZE); size_t off = 0; for (;;) { /* * Handle buffer growing and detecting the end of it all. */ size_t cbToRead = cbAllocated - off - READ_ALL_HEADER_SIZE - 1; if (!cbToRead) { /* The end? */ uint8_t bIgn; size_t cbIgn; rc = RTVfsIoStrmRead(hVfsIos, &bIgn, 0, true /*fBlocking*/, &cbIgn); if (rc == VINF_EOF) break; /* Grow the buffer. */ cbAllocated -= READ_ALL_HEADER_SIZE - 1; cbAllocated = RT_MAX(RT_MIN(cbAllocated, _32M), _1K); cbAllocated = RT_ALIGN_Z(cbAllocated, _4K); cbAllocated += READ_ALL_HEADER_SIZE + 1; void *pvNew = RTMemRealloc(pvBuf, cbAllocated); AssertBreakStmt(pvNew, rc = VERR_NO_MEMORY); pvBuf = pvNew; cbToRead = cbAllocated - off - READ_ALL_HEADER_SIZE - 1; } Assert(cbToRead < cbAllocated); /* * Read. */ size_t cbActual; rc = RTVfsIoStrmRead(hVfsIos, (uint8_t *)pvBuf + READ_ALL_HEADER_SIZE + off, cbToRead, true /*fBlocking*/, &cbActual); if (RT_FAILURE(rc)) break; Assert(cbActual > 0); Assert(cbActual <= cbToRead); off += cbActual; if (rc == VINF_EOF) break; } Assert(rc != VERR_EOF); if (RT_SUCCESS(rc)) { ((size_t *)pvBuf)[0] = READ_ALL_HEADER_MAGIC; ((size_t *)pvBuf)[1] = off; ((uint8_t *)pvBuf)[READ_ALL_HEADER_SIZE + off] = 0; *ppvBuf = (uint8_t *)pvBuf + READ_ALL_HEADER_SIZE; *pcbBuf = off; return VINF_SUCCESS; } RTMemFree(pvBuf); } else rc = VERR_NO_MEMORY; *ppvBuf = NULL; *pcbBuf = 0; return rc; }