int main(int argc, char **argv) { /* * Initialize IPRT and create the test. */ RTTEST hTest; int rc = RTTestInitAndCreate("tstRTFilesystem", &hTest); if (rc) return rc; RTTestBanner(hTest); /* * If no args, display usage. */ if (argc < 2) { RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Syntax: %s <image>\n", argv[0]); return RTTestSkipAndDestroy(hTest, "Missing required arguments\n"); } /* Open image. */ RTFILE hFile; RTVFSFILE hVfsFile; rc = RTFileOpen(&hFile, argv[1], RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ); if (RT_FAILURE(rc)) { RTTestIFailed("RTFileOpen -> %Rrc", rc); return RTTestSummaryAndDestroy(hTest); } rc = RTVfsFileFromRTFile(hFile, 0, false, &hVfsFile); if (RT_FAILURE(rc)) { RTTestIFailed("RTVfsFileFromRTFile -> %Rrc", rc); return RTTestSummaryAndDestroy(hTest); } rc = tstRTFilesystem(hTest, hVfsFile); RTTESTI_CHECK(rc == VINF_SUCCESS); RTVfsFileRelease(hVfsFile); /* * Summary */ return RTTestSummaryAndDestroy(hTest); }
RTR3DECL(int) RTTarClose(RTTAR hTar) { if (hTar == NIL_RTTAR) return VINF_SUCCESS; PRTTARINTERNAL pInt = hTar; RTTAR_VALID_RETURN(pInt); int rc = VINF_SUCCESS; /* gtar gives a warning, but the documentation says EOF is indicated by a * zero block. Disabled for now. */ #if 0 { /* Append the EOF record which is filled all by zeros */ RTTARRECORD record; RT_ZERO(record); rc = RTFileWrite(pInt->hTarFile, &record, sizeof(record), NULL); } #endif if (pInt->hVfsFss != NIL_RTVFSFSSTREAM) { uint32_t cRefs = RTVfsFsStrmRelease(pInt->hVfsFss); Assert(cRefs != UINT32_MAX); pInt->hVfsFss = NIL_RTVFSFSSTREAM; } if (pInt->hVfsFile != NIL_RTVFSFILE) { uint32_t cRefs = RTVfsFileRelease(pInt->hVfsFile); Assert(cRefs != UINT32_MAX); pInt->hVfsFile = NIL_RTVFSFILE; } if (pInt->hTarFile != NIL_RTFILE) { rc = RTFileClose(pInt->hTarFile); pInt->hTarFile = NIL_RTFILE; } pInt->u32Magic = RTTAR_MAGIC_DEAD; RTMemFree(pInt); return rc; }
/** * 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; }