summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/tools/RTManifest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/tools/RTManifest.cpp')
-rw-r--r--src/VBox/Runtime/tools/RTManifest.cpp394
1 files changed, 394 insertions, 0 deletions
diff --git a/src/VBox/Runtime/tools/RTManifest.cpp b/src/VBox/Runtime/tools/RTManifest.cpp
new file mode 100644
index 00000000..b59778fc
--- /dev/null
+++ b/src/VBox/Runtime/tools/RTManifest.cpp
@@ -0,0 +1,394 @@
+/* $Id: RTManifest.cpp $ */
+/** @file
+ * IPRT - Manifest Utility.
+ */
+
+/*
+ * Copyright (C) 2010-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/manifest.h>
+
+#include <iprt/buildconfig.h>
+#include <iprt/errcore.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/vfs.h>
+
+
+/**
+ * Verify a manifest.
+ *
+ * @returns Program exit code, failures with error message.
+ * @param pszManifest The manifest file. NULL if standard input.
+ * @param fStdFormat Whether to expect standard format (true) or
+ * java format (false).
+ * @param pszChDir The directory to change into before processing
+ * the files in the manifest.
+ */
+static RTEXITCODE rtManifestDoVerify(const char *pszManifest, bool fStdFormat, const char *pszChDir)
+{
+ RT_NOREF_PV(pszChDir); /** @todo implement pszChDir! */
+
+ /*
+ * Open the manifest.
+ */
+ int rc;
+ RTVFSIOSTREAM hVfsIos;
+ if (!pszManifest)
+ {
+ rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_INPUT, RTFILE_O_READ, false /*fLeaveOpen*/, &hVfsIos);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to prepare standard input for reading: %Rrc", rc);
+ }
+ else
+ {
+ uint32_t offError = 0;
+ RTERRINFOSTATIC ErrInfo;
+ rc = RTVfsChainOpenIoStream(pszManifest, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
+ &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
+ if (RT_FAILURE(rc))
+ return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenIoStream", pszManifest, rc, offError, &ErrInfo.Core);
+ }
+
+ /*
+ * Read it.
+ */
+ RTMANIFEST hManifest;
+ rc = RTManifestCreate(0 /*fFlags*/, &hManifest);
+ if (RT_SUCCESS(rc))
+ {
+ if (fStdFormat)
+ {
+ char szErr[4096 + 1024];
+ rc = RTManifestReadStandardEx(hManifest, hVfsIos, szErr, sizeof(szErr));
+ if (RT_SUCCESS(rc))
+ {
+ RTVfsIoStrmRelease(hVfsIos);
+ hVfsIos = NIL_RTVFSIOSTREAM;
+
+ /*
+ * Do the verification.
+ */
+ /** @todo We're missing some enumeration APIs here! */
+ RTMsgError("The manifest read fine, but the actual verification code is yet to be written. Sorry.");
+ rc = VERR_NOT_IMPLEMENTED;
+#if 1 /* For now, just write the manifest to stdout so we can test the read routine. */
+ RTVFSIOSTREAM hVfsIosOut;
+ rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, RTFILE_O_WRITE, false /*fLeaveOpen*/, &hVfsIosOut);
+ if (RT_SUCCESS(rc))
+ {
+ RTManifestWriteStandard(hManifest, hVfsIosOut);
+ RTVfsIoStrmRelease(hVfsIosOut);
+ }
+#endif
+ }
+ else if (szErr[0])
+ RTMsgError("Error reading manifest: %s", szErr);
+ else
+ RTMsgError("Error reading manifest: %Rrc", rc);
+ }
+ else
+ {
+ RTMsgError("Support for Java manifest files is not implemented yet");
+ rc = VERR_NOT_IMPLEMENTED;
+ }
+ RTManifestRelease(hManifest);
+ }
+
+ RTVfsIoStrmRelease(hVfsIos);
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Adds a file to the manifest.
+ *
+ * @returns IPRT status code, failures with error message.
+ * @param hManifest The manifest to add it to.
+ * @param pszFilename The name of the file to add.
+ * @param fAttr The manifest attributes to add.
+ */
+static int rtManifestAddFileToManifest(RTMANIFEST hManifest, const char *pszFilename, uint32_t fAttr)
+{
+ RTVFSIOSTREAM hVfsIos;
+ uint32_t offError = 0;
+ RTERRINFOSTATIC ErrInfo;
+ int rc = RTVfsChainOpenIoStream(pszFilename, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
+ &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
+ if (RT_FAILURE(rc))
+ {
+ RTVfsChainMsgError("RTVfsChainOpenIoStream", pszFilename, rc, offError, &ErrInfo.Core);
+ return rc;
+ }
+
+ rc = RTManifestEntryAddIoStream(hManifest, hVfsIos, pszFilename, fAttr);
+ if (RT_FAILURE(rc))
+ RTMsgError("RTManifestEntryAddIoStream failed for '%s': %Rrc", pszFilename, rc);
+
+ RTVfsIoStrmRelease(hVfsIos);
+ return rc;
+}
+
+
+/**
+ * Create a manifest from the specified input files.
+ *
+ * @returns Program exit code, failures with error message.
+ * @param pszManifest The name of the output manifest file. NULL if
+ * it should be written to standard output.
+ * @param fStdFormat Whether to expect standard format (true) or
+ * java format (false).
+ * @param pszChDir The directory to change into before processing
+ * the file arguments.
+ * @param fAttr The file attributes to put in the manifest.
+ * @param pGetState The RTGetOpt state.
+ * @param pUnion What the last RTGetOpt() call returned.
+ * @param chOpt What the last RTGetOpt() call returned.
+ */
+static RTEXITCODE rtManifestDoCreate(const char *pszManifest, bool fStdFormat, const char *pszChDir, uint32_t fAttr,
+ PRTGETOPTSTATE pGetState, PRTGETOPTUNION pUnion, int chOpt)
+{
+ /*
+ * Open the manifest file.
+ */
+ int rc;
+ RTVFSIOSTREAM hVfsIos;
+ if (!pszManifest)
+ {
+ rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, RTFILE_O_WRITE, false /*fLeaveOpen*/, &hVfsIos);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to prepare standard output for writing: %Rrc", rc);
+ }
+ else
+ {
+ RTERRINFOSTATIC ErrInfo;
+ uint32_t offError;
+ rc = RTVfsChainOpenIoStream(pszManifest, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
+ &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
+ if (RT_FAILURE(rc))
+ return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenIoStream", pszManifest, rc, offError, &ErrInfo.Core);
+ }
+
+ /*
+ * Create the internal manifest.
+ */
+ RTMANIFEST hManifest;
+ rc = RTManifestCreate(0 /*fFlags*/, &hManifest);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Change directory and start processing the specified files.
+ */
+ if (pszChDir)
+ {
+ rc = RTPathSetCurrent(pszChDir);
+ if (RT_FAILURE(rc))
+ RTMsgError("Failed to change directory to '%s': %Rrc", pszChDir, rc);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ while (chOpt == VINF_GETOPT_NOT_OPTION)
+ {
+ rc = rtManifestAddFileToManifest(hManifest, pUnion->psz, fAttr);
+ if (RT_FAILURE(rc))
+ break;
+
+ /* next */
+ chOpt = RTGetOpt(pGetState, pUnion);
+ }
+ if (RT_SUCCESS(rc) && chOpt != 0)
+ {
+ RTGetOptPrintError(chOpt, pUnion);
+ rc = chOpt < 0 ? chOpt : -chOpt;
+ }
+ }
+
+ /*
+ * Write the manifest.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ if (fStdFormat)
+ {
+ rc = RTManifestWriteStandard(hManifest, hVfsIos);
+ if (RT_FAILURE(rc))
+ RTMsgError("RTManifestWriteStandard failed: %Rrc", rc);
+ }
+ else
+ {
+ RTMsgError("Support for Java manifest files is not implemented yet");
+ rc = VERR_NOT_IMPLEMENTED;
+ }
+ }
+
+ RTManifestRelease(hManifest);
+ }
+
+ RTVfsIoStrmRelease(hVfsIos);
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+int main(int argc, char **argv)
+{
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ /*
+ * Parse arguments.
+ */
+ static RTGETOPTDEF const s_aOptions[] =
+ {
+ { "--manifest", 'm', RTGETOPT_REQ_STRING },
+ { "--java", 'j', RTGETOPT_REQ_NOTHING },
+ { "--chdir", 'C', RTGETOPT_REQ_STRING },
+ { "--attribute", 'a', RTGETOPT_REQ_STRING },
+ { "--verify", 'v', RTGETOPT_REQ_NOTHING },
+ };
+
+ bool fVerify = false;
+ bool fStdFormat = true;
+ const char *pszManifest = NULL;
+ const char *pszChDir = NULL;
+ uint32_t fAttr = RTMANIFEST_ATTR_UNKNOWN;
+
+ RTGETOPTSTATE GetState;
+ rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc", rc);
+
+ RTGETOPTUNION ValueUnion;
+ while ( (rc = RTGetOpt(&GetState, &ValueUnion)) != 0
+ && rc != VINF_GETOPT_NOT_OPTION)
+ {
+ switch (rc)
+ {
+ case 'a':
+ {
+ static struct
+ {
+ const char *pszAttr;
+ uint32_t fAttr;
+ } s_aAttributes[] =
+ {
+ { "size", RTMANIFEST_ATTR_SIZE },
+ { "md5", RTMANIFEST_ATTR_MD5 },
+ { "sha1", RTMANIFEST_ATTR_SHA1 },
+ { "sha256", RTMANIFEST_ATTR_SHA256 },
+ { "sha512", RTMANIFEST_ATTR_SHA512 }
+ };
+ uint32_t fThisAttr = RTMANIFEST_ATTR_UNKNOWN;
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aAttributes); i++)
+ if (!RTStrICmp(s_aAttributes[i].pszAttr, ValueUnion.psz))
+ {
+ fThisAttr = s_aAttributes[i].fAttr;
+ break;
+ }
+ if (fThisAttr == RTMANIFEST_ATTR_UNKNOWN)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown attribute type '%s'", ValueUnion.psz);
+
+ if (fAttr == RTMANIFEST_ATTR_UNKNOWN)
+ fAttr = fThisAttr;
+ else
+ fAttr |= fThisAttr;
+ break;
+ }
+
+ case 'j':
+ fStdFormat = false;
+ break;
+
+ case 'm':
+ if (pszManifest)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Only one manifest can be specified");
+ pszManifest = ValueUnion.psz;
+ break;
+
+ case 'v':
+ fVerify = true;
+ break;
+
+ case 'C':
+ if (pszChDir)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Only one directory change can be specified");
+ pszChDir = ValueUnion.psz;
+ break;
+
+ case 'h':
+ RTPrintf("Usage: %s [--manifest <file>] [--chdir <dir>] [--attribute <attrib-name> [..]] <files>\n"
+ " or %s --verify [--manifest <file>] [--chdir <dir>]\n"
+ "\n"
+ "attrib-name: size, md5, sha1, sha256 or sha512\n"
+ , RTProcShortName(), RTProcShortName());
+ return RTEXITCODE_SUCCESS;
+
+#ifndef IN_BLD_PROG /* RTBldCfgVersion or RTBldCfgRevision in build time IPRT lib. */
+ case 'V':
+ RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
+ return RTEXITCODE_SUCCESS;
+#endif
+
+ default:
+ return RTGetOptPrintError(rc, &ValueUnion);
+ }
+ }
+
+ /*
+ * Take action.
+ */
+ RTEXITCODE rcExit;
+ if (!fVerify)
+ {
+ if (rc != VINF_GETOPT_NOT_OPTION)
+ RTMsgWarning("No files specified, the manifest will be empty.");
+ if (fAttr == RTMANIFEST_ATTR_UNKNOWN)
+ fAttr = RTMANIFEST_ATTR_SIZE | RTMANIFEST_ATTR_MD5
+ | RTMANIFEST_ATTR_SHA1 | RTMANIFEST_ATTR_SHA256 | RTMANIFEST_ATTR_SHA512;
+ rcExit = rtManifestDoCreate(pszManifest, fStdFormat, pszChDir, fAttr, &GetState, &ValueUnion, rc);
+ }
+ else
+ {
+ if (rc == VINF_GETOPT_NOT_OPTION)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX,
+ "No files should be specified when verifying a manifest (--verfiy), "
+ "only a manifest via the --manifest option");
+ if (fAttr != RTMANIFEST_ATTR_UNKNOWN)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX,
+ "The --attribute (-a) option does not combine with --verify (-v)");
+
+
+ rcExit = rtManifestDoVerify(pszManifest, fStdFormat, pszChDir);
+ }
+
+ return rcExit;
+}
+