/** * Changes the file mode of one file system object. * * @returns exit code * @param pOpts The chmod options. * @param pszPath The path to the file system object to change the * file mode of. */ static RTEXITCODE rtCmdChModOne(RTCMDCHMODOPTS const *pOpts, const char *pszPath) { int rc; RTFSOBJINFO ObjInfo; bool fChanges = false; if (!pOpts->fAlwaysUseChainApi && !RTVfsChainIsSpec(pszPath) ) { rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK); if (RT_SUCCESS(rc)) { RTFMODE fNewMode = rtCmdMkModCalcNewMode(pOpts, ObjInfo.Attr.fMode); fChanges = fNewMode != ObjInfo.Attr.fMode; if (fChanges) { rc = RTPathSetMode(pszPath, fNewMode); if (RT_FAILURE(rc)) RTMsgError("RTPathSetMode failed on '%s' with fNewMode=%#x: %Rrc", pszPath, fNewMode, rc); } } else RTMsgError("RTPathQueryInfoEx failed on '%s': %Rrc", pszPath, rc); } else { RTVFSOBJ hVfsObj; uint32_t offError; RTERRINFOSTATIC ErrInfo; rc = RTVfsChainOpenObj(pszPath, RTFILE_O_ACCESS_ATTR_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, RTVFSOBJ_F_OPEN_ANY | RTVFSOBJ_F_CREATE_NOTHING | RTPATH_F_FOLLOW_LINK, &hVfsObj, &offError, RTErrInfoInitStatic(&ErrInfo)); if (RT_SUCCESS(rc)) { rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING); if (RT_SUCCESS(rc)) { RTFMODE fNewMode = rtCmdMkModCalcNewMode(pOpts, ObjInfo.Attr.fMode); fChanges = fNewMode != ObjInfo.Attr.fMode; if (fChanges) { rc = RTVfsObjSetMode(hVfsObj, fNewMode, RTCHMOD_SET_ALL_MASK); if (RT_FAILURE(rc)) RTMsgError("RTVfsObjSetMode failed on '%s' with fNewMode=%#x: %Rrc", pszPath, fNewMode, rc); } } else RTVfsChainMsgError("RTVfsObjQueryInfo", pszPath, rc, offError, &ErrInfo.Core); RTVfsObjRelease(hVfsObj); } else RTVfsChainMsgError("RTVfsChainOpenObject", pszPath, rc, offError, &ErrInfo.Core); } if (RT_SUCCESS(rc)) { if (pOpts->enmNoiseLevel >= (fChanges ? kRTCmdChModNoise_Changes : kRTCmdChModNoise_Verbose)) RTPrintf("%s\n", pszPath); return RTEXITCODE_SUCCESS; } return RTEXITCODE_FAILURE; }
/** * @interface_method_impl{RTVFSOBJOPS,pfnClose} */ static DECLCALLBACK(int) rtZipTarFss_Close(void *pvThis) { PRTZIPTARFSSTREAM pThis = (PRTZIPTARFSSTREAM)pvThis; RTVfsObjRelease(pThis->hVfsCurObj); pThis->hVfsCurObj = NIL_RTVFSOBJ; pThis->pCurIosData = NULL; RTVfsIoStrmRelease(pThis->hVfsIos); pThis->hVfsIos = NIL_RTVFSIOSTREAM; return VINF_SUCCESS; }
/** * Validates a standard file. * * Generally all files are * * @returns VBox status code, failure message in @a pszError. * @param pszAdjName The adjusted member name. * @param enmType The VFS object type. * @param phVfsObj The pointer to the VFS object handle variable. * This is both input and output. * @param phVfsFile Where to store the handle to the memorized * file. This is NULL for license files. * @param pszError Where to write an error message on failure. * @param cbError The size of the @a pszError buffer. */ static int VBoxExtPackValidateStandardFile(const char *pszAdjName, RTVFSOBJTYPE enmType, PRTVFSOBJ phVfsObj, PRTVFSFILE phVfsFile, char *pszError, size_t cbError) { int rc; /* * Make sure it's a file and that it isn't too large. */ if (phVfsFile && *phVfsFile != NIL_RTVFSFILE) rc = vboxExtPackReturnError(VERR_DUPLICATE, pszError, cbError, "There can only be one '%s'", pszAdjName); else if (enmType != RTVFSOBJTYPE_IO_STREAM && enmType != RTVFSOBJTYPE_FILE) rc = vboxExtPackReturnError(VERR_NOT_A_FILE, pszError, cbError, "Standard member '%s' is not a file", pszAdjName); else { RTFSOBJINFO ObjInfo; rc = RTVfsObjQueryInfo(*phVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING); if (RT_SUCCESS(rc)) { if (!RTFS_IS_FILE(ObjInfo.Attr.fMode)) rc = vboxExtPackReturnError(VERR_NOT_A_FILE, pszError, cbError, "Standard member '%s' is not a file", pszAdjName); else if (ObjInfo.cbObject >= _1M) rc = vboxExtPackReturnError(VERR_OUT_OF_RANGE, pszError, cbError, "Standard member '%s' is too large: %'RU64 bytes (max 1 MB)", pszAdjName, (uint64_t)ObjInfo.cbObject); else { /* * Make an in memory copy of the stream and check that the file * is UTF-8 clean. */ RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(*phVfsObj); RTVFSFILE hVfsFile; rc = RTVfsMemorizeIoStreamAsFile(hVfsIos, RTFILE_O_READ, &hVfsFile); if (RT_SUCCESS(rc)) { rc = RTVfsIoStrmValidateUtf8Encoding(hVfsIos, RTVFS_VALIDATE_UTF8_BY_RTC_3629 | RTVFS_VALIDATE_UTF8_NO_NULL, NULL); if (RT_SUCCESS(rc)) { /* * Replace *phVfsObj with the memorized file. */ rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL); if (RT_SUCCESS(rc)) { RTVfsObjRelease(*phVfsObj); *phVfsObj = RTVfsObjFromFile(hVfsFile); } else vboxExtPackSetError(pszError, cbError, "RTVfsFileSeek failed on '%s': %Rrc", pszAdjName, rc); } if (phVfsFile && RT_SUCCESS(rc)) *phVfsFile = hVfsFile; else RTVfsFileRelease(hVfsFile); } else vboxExtPackSetError(pszError, cbError, "RTVfsMemorizeIoStreamAsFile failed on '%s': %Rrc", pszAdjName, rc); RTVfsIoStrmRelease(hVfsIos); } } else vboxExtPackSetError(pszError, cbError, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszAdjName, rc); } return rc; }
/** * Validates the extension pack tarball prior to unpacking. * * Operations performed: * - Mandatory files. * - Manifest check. * - Manifest seal check. * - XML check, match name. * * @returns VBox status code, failures with message. * @param hTarballFile The handle to open the @a pszTarball file. * @param pszExtPackName The name of the extension pack name. NULL if * the name is not fixed. * @param pszTarball The name of the tarball in case we have to * complain about something. * @param pszTarballDigest The SHA-256 digest of the tarball. Empty string * if no digest available. * @param pszError Where to store an error message on failure. * @param cbError The size of the buffer @a pszError points to. * @param phValidManifest Where to optionally return the handle to fully * validated the manifest for the extension pack. * This includes all files. * @param phXmlFile Where to optionally return the memorized XML * file. * @param pStrDigest Where to return the digest of the file. * Optional. */ int VBoxExtPackValidateTarball(RTFILE hTarballFile, const char *pszExtPackName, const char *pszTarball, const char *pszTarballDigest, char *pszError, size_t cbError, PRTMANIFEST phValidManifest, PRTVFSFILE phXmlFile, RTCString *pStrDigest) { /* * Clear return values. */ if (phValidManifest) *phValidManifest = NIL_RTMANIFEST; if (phXmlFile) *phXmlFile = NIL_RTVFSFILE; Assert(cbError > 1); *pszError = '\0'; NOREF(pszTarball); /* * Open the tar.gz filesystem stream and set up an manifest in-memory file. */ RTMANIFEST hFileManifest; RTVFSFSSTREAM hTarFss; int rc = VBoxExtPackOpenTarFss(hTarballFile, pszError, cbError, &hTarFss, &hFileManifest); if (RT_FAILURE(rc)) return rc; RTMANIFEST hOurManifest; rc = RTManifestCreate(0 /*fFlags*/, &hOurManifest); if (RT_SUCCESS(rc)) { /* * Process the tarball (would be nice to move this to a function). */ RTVFSFILE hXmlFile = NIL_RTVFSFILE; RTVFSFILE hManifestFile = NIL_RTVFSFILE; RTVFSFILE hSignatureFile = NIL_RTVFSFILE; for (;;) { /* * Get the next stream object. */ char *pszName; RTVFSOBJ hVfsObj; RTVFSOBJTYPE enmType; rc = RTVfsFsStrmNext(hTarFss, &pszName, &enmType, &hVfsObj); if (RT_FAILURE(rc)) { if (rc != VERR_EOF) vboxExtPackSetError(pszError, cbError, "RTVfsFsStrmNext failed: %Rrc", rc); else rc = VINF_SUCCESS; break; } const char *pszAdjName = pszName[0] == '.' && pszName[1] == '/' ? &pszName[2] : pszName; /* * Check the type & name validity, performing special tests on * standard extension pack member files. * * N.B. We will always reach the end of the loop before breaking on * failure - cleanup reasons. */ rc = VBoxExtPackValidateMember(pszName, enmType, hVfsObj, pszError, cbError); if (RT_SUCCESS(rc)) { PRTVFSFILE phVfsFile = NULL; if (!strcmp(pszAdjName, VBOX_EXTPACK_DESCRIPTION_NAME)) phVfsFile = &hXmlFile; else if (!strcmp(pszAdjName, VBOX_EXTPACK_MANIFEST_NAME)) phVfsFile = &hManifestFile; else if (!strcmp(pszAdjName, VBOX_EXTPACK_SIGNATURE_NAME)) phVfsFile = &hSignatureFile; else if (!strncmp(pszAdjName, VBOX_EXTPACK_LICENSE_NAME_PREFIX, sizeof(VBOX_EXTPACK_LICENSE_NAME_PREFIX) - 1)) rc = VBoxExtPackValidateStandardFile(pszAdjName, enmType, &hVfsObj, NULL, pszError, cbError); if (phVfsFile) rc = VBoxExtPackValidateStandardFile(pszAdjName, enmType, &hVfsObj, phVfsFile, pszError, cbError); } /* * Add any I/O stream to the manifest */ if ( RT_SUCCESS(rc) && ( enmType == RTVFSOBJTYPE_FILE || enmType == RTVFSOBJTYPE_IO_STREAM)) { RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj); rc = RTManifestEntryAddIoStream(hOurManifest, hVfsIos, pszAdjName, RTMANIFEST_ATTR_SIZE | RTMANIFEST_ATTR_SHA256); if (RT_FAILURE(rc)) vboxExtPackSetError(pszError, cbError, "RTManifestEntryAddIoStream failed on '%s': %Rrc", pszAdjName, rc); RTVfsIoStrmRelease(hVfsIos); } /* * Clean up and break out on failure. */ RTVfsObjRelease(hVfsObj); RTStrFree(pszName); if (RT_FAILURE(rc)) break; } /* * Check the integrity of the tarball file. */ if (RT_SUCCESS(rc)) { RTVfsFsStrmRelease(hTarFss); hTarFss = NIL_RTVFSFSSTREAM; rc = vboxExtPackVerifyFileDigest(hFileManifest, pszTarballDigest, pStrDigest, pszError, cbError); } /* * If we've successfully processed the tarball, verify that the * mandatory files are present. */ if (RT_SUCCESS(rc)) { if (hXmlFile == NIL_RTVFSFILE) rc = vboxExtPackReturnError(VERR_MISSING, pszError, cbError, "Mandator file '%s' is missing", VBOX_EXTPACK_DESCRIPTION_NAME); if (hManifestFile == NIL_RTVFSFILE) rc = vboxExtPackReturnError(VERR_MISSING, pszError, cbError, "Mandator file '%s' is missing", VBOX_EXTPACK_MANIFEST_NAME); if (hSignatureFile == NIL_RTVFSFILE) rc = vboxExtPackReturnError(VERR_MISSING, pszError, cbError, "Mandator file '%s' is missing", VBOX_EXTPACK_SIGNATURE_NAME); } /* * Check the manifest and it's signature. */ if (RT_SUCCESS(rc)) rc = vboxExtPackVerifyManifestAndSignature(hOurManifest, hManifestFile, hSignatureFile, pszError, cbError); /* * Check the XML. */ if (RT_SUCCESS(rc)) rc = vboxExtPackVerifyXml(hXmlFile, pszExtPackName, pszError, cbError); /* * Returns objects. */ if (RT_SUCCESS(rc)) { if (phValidManifest) { RTManifestRetain(hOurManifest); *phValidManifest = hOurManifest; } if (phXmlFile) { RTVfsFileRetain(hXmlFile); *phXmlFile = hXmlFile; } } /* * Release our object references. */ RTManifestRelease(hOurManifest); RTVfsFileRelease(hXmlFile); RTVfsFileRelease(hManifestFile); RTVfsFileRelease(hSignatureFile); } else vboxExtPackSetError(pszError, cbError, "RTManifestCreate failed: %Rrc", rc); RTVfsFsStrmRelease(hTarFss); RTManifestRelease(hFileManifest); return rc; }
/** * Worker for the --list and --extract commands. * * @returns The appropriate exit code. * @param pOpts The Unzip options. * @param pfnCallback The command specific callback. */ static RTEXITCODE rtZipUnzipDoWithMembers(PRTZIPUNZIPCMDOPS pOpts, PFNDOWITHMEMBER pfnCallback, uint32_t *pcFiles, PRTFOFF pcBytes) { /* * Allocate a bitmap to go with the file list. This will be used to * indicate which files we've processed and which not. */ uint32_t *pbmFound = NULL; if (pOpts->cFiles) { pbmFound = (uint32_t *)RTMemAllocZ(((pOpts->cFiles + 31) / 32) * sizeof(uint32_t)); if (!pbmFound) return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to allocate the found-file-bitmap"); } uint32_t cFiles = 0; RTFOFF cBytesSum = 0; /* * Open the input archive. */ RTVFSFSSTREAM hVfsFssIn; RTEXITCODE rcExit = rtZipUnzipCmdOpenInputArchive(pOpts, &hVfsFssIn); if (rcExit == RTEXITCODE_SUCCESS) { /* * Process the stream. */ for (;;) { /* * Retrieve the next object. */ char *pszName; RTVFSOBJ hVfsObj; int rc = RTVfsFsStrmNext(hVfsFssIn, &pszName, NULL, &hVfsObj); if (RT_FAILURE(rc)) { if (rc != VERR_EOF) rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsFsStrmNext returned %Rrc", rc); break; } /* * Should we process this object? */ uint32_t iFile = UINT32_MAX; if ( !pOpts->cFiles || rtZipUnzipCmdIsNameInArray(pszName, pOpts->papszFiles, &iFile)) { if (pbmFound) ASMBitSet(pbmFound, iFile); RTFOFF cBytes = 0; rcExit = pfnCallback(pOpts, hVfsObj, pszName, rcExit, &cBytes); cBytesSum += cBytes; cFiles++; } /* * Release the current object and string. */ RTVfsObjRelease(hVfsObj); RTStrFree(pszName); } /* * Complain about any files we didn't find. */ for (uint32_t iFile = 0; iFile <pOpts->cFiles; iFile++) if (!ASMBitTest(pbmFound, iFile)) { RTMsgError("%s: Was not found in the archive", pOpts->papszFiles[iFile]); rcExit = RTEXITCODE_FAILURE; } RTVfsFsStrmRelease(hVfsFssIn); } RTMemFree(pbmFound); *pcFiles = cFiles; *pcBytes = cBytesSum; return RTEXITCODE_SUCCESS; }
/** * @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; }
RTR3DECL(int) RTTarFileOpen(RTTAR hTar, PRTTARFILE phFile, const char *pszFilename, uint32_t fOpen) { /* Write only interface now. */ AssertReturn(fOpen & RTFILE_O_WRITE, VERR_INVALID_PARAMETER); PRTTARINTERNAL pInt = hTar; RTTAR_VALID_RETURN(pInt); if (!pInt->hTarFile) return VERR_INVALID_HANDLE; if (fOpen & RTFILE_O_WRITE) { if (!(pInt->fOpenMode & RTFILE_O_WRITE)) return VERR_WRITE_PROTECT; if (pInt->fFileOpenForWrite) return VERR_TOO_MANY_OPEN_FILES; } int rc = VINF_SUCCESS; if (!(fOpen & RTFILE_O_WRITE)) { /* * Rewind the stream if necessary. */ if (!pInt->fFssAtStart) { if (pInt->hVfsFss != NIL_RTVFSFSSTREAM) { uint32_t cRefs = RTVfsFsStrmRelease(pInt->hVfsFss); Assert(cRefs != UINT32_MAX); pInt->hVfsFss = NIL_RTVFSFSSTREAM; } if (pInt->hVfsFile == NIL_RTVFSFILE) { rc = RTVfsFileFromRTFile(pInt->hTarFile, RTFILE_O_READ, true /*fLeaveOpen*/, &pInt->hVfsFile); if (RT_FAILURE(rc)) return rc; } RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(pInt->hVfsFile); rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0 /*fFlags*/, &pInt->hVfsFss); RTVfsIoStrmRelease(hVfsIos); if (RT_FAILURE(rc)) return rc; } /* * Search the file system stream. */ pInt->fFssAtStart = false; for (;;) { char *pszName; RTVFSOBJTYPE enmType; RTVFSOBJ hVfsObj; rc = RTVfsFsStrmNext(pInt->hVfsFss, &pszName, &enmType, &hVfsObj); if (rc == VERR_EOF) return VERR_FILE_NOT_FOUND; if (RT_FAILURE(rc)) return rc; if (!RTStrCmp(pszName, pszFilename)) { if (enmType == RTVFSOBJTYPE_FILE || enmType == RTVFSOBJTYPE_IO_STREAM) rc = rtTarFileCreateHandleForReadOnly(pszName, RTVfsObjToIoStream(hVfsObj), fOpen, phFile); else { rc = VERR_UNEXPECTED_FS_OBJ_TYPE; RTStrFree(pszName); } RTVfsObjRelease(hVfsObj); break; } RTStrFree(pszName); RTVfsObjRelease(hVfsObj); } /* Search loop. */ } else { PRTTARFILEINTERNAL pFileInt = rtTarFileCreateForWrite(pInt, pszFilename, fOpen); if (!pFileInt) return VERR_NO_MEMORY; pInt->fFileOpenForWrite = true; /* If we are in write mode, we also in append mode. Add an dummy * header at the end of the current file. It will be filled by the * close operation. */ rc = RTFileSeek(pFileInt->pTar->hTarFile, 0, RTFILE_SEEK_END, &pFileInt->offStart); if (RT_SUCCESS(rc)) { RTTARRECORD record; RT_ZERO(record); rc = RTFileWrite(pFileInt->pTar->hTarFile, &record, sizeof(RTTARRECORD), NULL); } if (RT_SUCCESS(rc)) *phFile = (RTTARFILE)pFileInt; else { /* Cleanup on failure */ if (pFileInt->pszFilename) RTStrFree(pFileInt->pszFilename); RTMemFree(pFileInt); } } return rc; }