Index: /trunk/include/iprt/manifest.h
===================================================================
--- /trunk/include/iprt/manifest.h	(revision 34380)
+++ /trunk/include/iprt/manifest.h	(revision 34381)
@@ -36,4 +36,213 @@
  * @{
  */
+
+/** @name Manifest attribute types.
+ * The types can be ORed together to form a set.
+ * @{ */
+/** For use with other attributes. Representation unknown. */
+#define RTMANIFEST_ATTR_UNKNOWN     0
+/** The size of the content.    Represented as a decimal number. */
+#define RTMANIFEST_ATTR_SIZE        RT_BIT_32(0)
+/** The MD5 of the content.     Represented as a hex string. */
+#define RTMANIFEST_ATTR_MD5         RT_BIT_32(1)
+/** The SHA-1 of the content.   Represented as a hex string. */
+#define RTMANIFEST_ATTR_SHA1        RT_BIT_32(2)
+/** The SHA-256 of the content. Represented as a hex string. */
+#define RTMANIFEST_ATTR_SHA256      RT_BIT_32(3)
+/** The SHA-512 of the content. Represented as a hex string. */
+#define RTMANIFEST_ATTR_SHA512      RT_BIT_32(4)
+/** The end of the valid values. */
+#define RTMANIFEST_ATTR_END         RT_BIT_32(5)
+/** @} */
+
+
+/**
+ * Creates an empty manifest.
+ *
+ * @returns IPRT status code.
+ * @param   fFlags              Flags, MBZ.
+ * @param   phManifest          Where to return the handle to the manifest.
+ */
+RTDECL(int) RTManifestCreate(uint32_t fFlags, PRTMANIFEST phManifest);
+
+/**
+ * Retains a reference to the manifest handle.
+ *
+ * @returns The new reference count, UINT32_MAX if the handle is invalid.
+ * @param   hManifest           The handle to retain.
+ */
+RTDECL(uint32_t) RTManifestRetain(RTMANIFEST hManifest);
+
+/**
+ * Releases a reference to the manifest handle.
+ *
+ * @returns The new reference count, 0 if free. UINT32_MAX is returned if the
+ *          handle is invalid.
+ * @param   hManifest           The handle to release.
+ *                              NIL is quietly ignored (returns 0).
+ */
+RTDECL(uint32_t) RTManifestRelease(RTMANIFEST hManifest);
+
+/**
+ * Creates a duplicate of the specified manifest.
+ *
+ * @returns IPRT status code
+ * @param   hManifestSrc        The manifest to clone.
+ * @param   phManifestDst       Where to store the handle to the duplicate.
+ */
+RTDECL(int) RTManifestDup(RTMANIFEST hManifestSrc, PRTMANIFEST phManifestDst);
+
+/**
+ * Compares two manifests for equality.
+ *
+ * @returns true if equals, false if not.
+ * @param   hManifest1          The first manifest.
+ * @param   hManifest2          The second manifest.
+ * @param   papszIgnoreEntries  Entries to ignore.  Ends with a NULL entry.
+ * @param   papszIgnoreAttrs    Attributes to ignore.  Ends with a NULL entry.
+ */
+RTDECL(int) RTManifestEqualsEx(RTMANIFEST hManifest1, RTMANIFEST hManifest2, const char * const *papszIgnoreEntries,
+                               const char * const *papszIgnoreAttr);
+
+/**
+ * Compares two manifests for equality.
+ *
+ * @returns true if equals, false if not.
+ * @param   hManifest1          The first manifest.
+ * @param   hManifest2          The second manifest.
+ */
+RTDECL(int) RTManifestEquals(RTMANIFEST hManifest1, RTMANIFEST hManifest2);
+
+/**
+ * Sets a manifest attribute.
+ *
+ * @returns IPRT status code.
+ * @param   hManifest           The manifest handle.
+ * @param   pszAttr             The attribute name.  If this already exists,
+ *                              its value will be replaced.
+ * @param   pszValue            The value string.
+ * @param   fType               The attribute type, pass
+ *                              RTMANIFEST_ATTR_UNKNOWN if not known.
+ */
+RTDECL(int) RTManifestSetAttr(RTMANIFEST hManifest, const char *pszAttr, const char *pszValue, uint32_t fType);
+
+/**
+ * Unsets (removes) a manifest attribute if it exists.
+ *
+ * @returns IPRT status code.
+ * @retval  VWRN_NOT_FOUND if not found.
+ *
+ * @param   hManifest           The manifest handle.
+ * @param   pszAttr             The attribute name.
+ */
+RTDECL(int) RTManifestUnsetAttr(RTMANIFEST hManifest, const char *pszAttr);
+
+/**
+ * Sets an attribute of a manifest entry.
+ *
+ * @returns IPRT status code.
+ * @param   hManifest           The manifest handle.
+ * @param   pszEntry            The entry name.  This will automatically be
+ *                              added if there was no previous call to
+ *                              RTManifestEntryAdd for this name.  See
+ *                              RTManifestEntryAdd for the entry name rules.
+ * @param   pszAttr             The attribute name.  If this already exists,
+ *                              its value will be replaced.
+ * @param   pszValue            The value string.
+ * @param   fType               The attribute type, pass
+ *                              RTMANIFEST_ATTR_UNKNOWN if not known.
+ */
+RTDECL(int) RTManifestEntrySetAttr(RTMANIFEST hManifest, const char *pszEntry, const char *pszAttr,
+                                   const char *pszValue, uint32_t fType);
+
+/**
+ * Unsets (removes) an attribute of a manifest entry if they both exist.
+ *
+ * @returns IPRT status code.
+ * @retval  VWRN_NOT_FOUND if not found.
+ *
+ * @param   hManifest           The manifest handle.
+ * @param   pszEntry            The entry name.
+ * @param   pszAttr             The attribute name.
+ */
+RTDECL(int) RTManifestEntryUnsetAttr(RTMANIFEST hManifest, const char *pszEntry, const char *pszAttr);
+
+/**
+ * Adds a new entry to a manifest.
+ *
+ * The entry name rules:
+ *     - The entry name can contain any character defined by unicode, except
+ *       control characters, ':', '(' and ')'.  The exceptions are mainly there
+ *       because of uncertainty around how various formats handles these.
+ *     - It is considered case sensitive.
+ *     - Forward (unix) and backward (dos) slashes are considered path
+ *       separators and converted to forward slashes.
+ *
+ * @returns IPRT status code.
+ * @retval  VWRN_ALREADY_EXISTS if the entry already exists.
+ *
+ * @param   hManifest           The manifest handle.
+ * @param   pszEntry            The entry name (UTF-8).
+ *
+ * @remarks Some manifest formats will not be able to store an entry without
+ *          any attributes.  So, this is just here in case it comes in handy
+ *          when dealing with formats which can.
+ */
+RTDECL(int) RTManifestEntryAdd(RTMANIFEST hManifest, const char *pszEntry);
+
+/**
+ * Removes an entry.
+ *
+ * @returns IPRT status code.
+ * @param   hManifest           The manifest handle.
+ * @param   pszEntry            The entry name.
+ */
+RTDECL(int) RTManifestEntryRemove(RTMANIFEST hManifest, const char *pszEntry);
+
+
+/**
+ * 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);
+
+/**
+ * Reads in a "standard" manifest.
+ *
+ * This reads the format used by OVF, the distinfo in FreeBSD ports, and
+ * others.
+ *
+ * @returns IPRT status code.
+ * @param   hManifest           The handle to the manifest where to add the
+ *                              manifest that's read in.
+ * @param   hVfsIos             The I/O stream to read the manifest from.
+ */
+RTDECL(int) RTManifestReadStandard(RTMANIFEST hManifest, RTVFSIOSTREAM hVfsIos);
+
+/**
+ * Writes a "standard" manifest.
+ *
+ * This writes the format used by OVF, the distinfo in FreeBSD ports, and
+ * others.
+ *
+ * @returns IPRT status code.
+ * @param   hManifest           The handle to the manifest where to add the
+ *                              manifest that's read in.
+ * @param   hVfsIos             The I/O stream to read the manifest from.
+ */
+RTDECL(int) RTManifestWriteStandard(RTMANIFEST hManifest, RTVFSIOSTREAM hVfsIos);
+
+
+
+
 
 /**
Index: /trunk/include/iprt/types.h
===================================================================
--- /trunk/include/iprt/types.h	(revision 34380)
+++ /trunk/include/iprt/types.h	(revision 34381)
@@ -1487,4 +1487,11 @@
 #define NIL_RTDBGMOD                                ((RTDBGMOD)0)
 
+/** Manifest handle. */
+typedef struct RTMANIFESTINT                       *RTMANIFEST;
+/** Pointer to a manifest handle. */
+typedef RTMANIFEST                                 *PRTMANIFEST;
+/** NIL manifest handle. */
+#define NIL_RTMANIFEST                              ((RTMANIFEST)~(uintptr_t)0)
+
 /** Memory pool handle. */
 typedef R3R0PTRTYPE(struct RTMEMPOOLINT *)          RTMEMPOOL;
Index: /trunk/src/VBox/Main/VBoxExtPackHelperApp.cpp
===================================================================
--- /trunk/src/VBox/Main/VBoxExtPackHelperApp.cpp	(revision 34380)
+++ /trunk/src/VBox/Main/VBoxExtPackHelperApp.cpp	(revision 34381)
@@ -30,4 +30,5 @@
 #include <iprt/getopt.h>
 #include <iprt/initterm.h>
+#include <iprt/manifest.h>
 #include <iprt/message.h>
 #include <iprt/param.h>
@@ -37,4 +38,6 @@
 #include <iprt/string.h>
 #include <iprt/stream.h>
+#include <iprt/vfs.h>
+#include <iprt/zip.h>
 
 #include <VBox/log.h>
@@ -51,4 +54,5 @@
 }
 #endif
+
 
 
@@ -77,4 +81,5 @@
                       "        --tarball <tarball> --tarball-fd <fd>\n"
                       "    uninstall --base-dir <dir> --name <name>\n"
+                      "    cleanup --base-dir <dir>\n"
                       , RTProcShortName());
             return RTEXITCODE_SUCCESS;
@@ -135,4 +140,5 @@
     return RTPathCompare(szCorrect, pszBaseDir) == 0;
 }
+
 
 /**
@@ -156,4 +162,50 @@
                               fTemporary ? "temporary " : "", rc, pszDir);
     return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Rewinds the tarball file handle and creates a gunzip | tar chain that
+ * results in a filesystem stream.
+ *
+ * @returns success or failure, message displayed on failure.
+ * @param   hTarballFile    The handle to the tarball file.
+ * @param   phTarFss        Where to return the filesystem stream handle.
+ */
+static RTEXITCODE OpenTarFss(RTFILE hTarballFile, PRTVFSFSSTREAM phTarFss)
+{
+    /*
+     * Rewind the file and set up a VFS chain for it.
+     */
+    int rc = RTFileSeek(hTarballFile, 0, RTFILE_SEEK_BEGIN, NULL);
+    if (RT_FAILURE(rc))
+        return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed seeking to the start of the tarball: %Rrc\n", rc);
+
+    RTVFSIOSTREAM hTarballIos;
+    rc = RTVfsIoStrmFromRTFile(hTarballFile, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN, true /*fLeaveOpen*/,
+                               &hTarballIos);
+    if (RT_FAILURE(rc))
+        return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsIoStrmFromRTFile failed: %Rrc\n", rc);
+
+    RTVFSIOSTREAM hGunzipIos;
+    rc = RTZipGzipDecompressIoStream(hTarballIos, 0 /*fFlags*/, &hGunzipIos);
+    if (RT_SUCCESS(rc))
+    {
+        RTVFSFSSTREAM hTarFss;
+        rc = RTZipTarFsStreamFromIoStream(hGunzipIos, 0 /*fFlags*/, &hTarFss);
+        if (RT_SUCCESS(rc))
+        {
+            RTVfsIoStrmRelease(hGunzipIos);
+            RTVfsIoStrmRelease(hTarballIos);
+            *phTarFss = hTarFss;
+            return RTEXITCODE_SUCCESS;
+        }
+        RTMsgError("RTZipTarFsStreamFromIoStream failed: %Rrc\n", rc);
+        RTVfsIoStrmRelease(hGunzipIos);
+    }
+    else
+        RTMsgError("RTZipGzipDecompressIoStream failed: %Rrc\n", rc);
+    RTVfsIoStrmRelease(hTarballIos);
+    return RTEXITCODE_FAILURE;
 }
 
@@ -183,10 +235,8 @@
 
 /**
- * Validates the extension pack.
+ * Validates the extension pack tarball prior to unpacking.
  *
  * Operations performed:
- *      - Manifest seal check.
- *      - Manifest check.
- *      - Recursive hardening check.
+ *      - Hardening checks.
  *      - XML validity check.
  *      - Name check (against XML).
@@ -199,5 +249,5 @@
  *                              complain about something.
  */
-static RTEXITCODE ValidateExtPack(const char *pszDir, const char *pszTarball, const char *pszName)
+static RTEXITCODE ValidateUnpackedExtPack(const char *pszDir, const char *pszTarball, const char *pszName)
 {
     /** @todo  */
@@ -217,4 +267,5 @@
  * @param   pszTarball          The name of the tarball in case we have to
  *                              complain about something.
+ * @todo    Needs to take the previous verified manifest as input.
  */
 static RTEXITCODE UnpackExtPack(RTFILE hTarballFile, const char *pszDirDst, const char *pszTarball)
@@ -222,4 +273,40 @@
     /** @todo  */
     return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Validates the extension pack tarball prior to unpacking.
+ *
+ * Operations performed:
+ *      - Manifest check.
+ *      - Manifest seal check.
+ *      - Mandatory files.
+ *
+ * @returns The program exit code.
+ * @param   hTarballFile        The handle to open the @a pszTarball file.
+ * @param   pszTarball          The name of the tarball in case we have to
+ *                              complain about something.
+ *
+ * @todo    Should validate the XML and name.
+ * @todo    Needs to return a manifest.
+ */
+static RTEXITCODE ValidateExtPackTarball(RTFILE hTarballFile, const char *pszTarball)
+{
+    /*
+     * Open the tar.gz filesystem stream and set up an manifest in-memory file.
+     */
+    RTVFSFSSTREAM hTarFss;
+    RTEXITCODE rcExit = OpenTarFss(hTarballFile, &hTarFss);
+    if (rcExit != RTEXITCODE_SUCCESS)
+        return rcExit;
+
+    RTMANIFEST hManifest;
+    int rc = RTManifestCreate(0 /*fFlags*/, &hManifest);
+
+    /** @todo continue coding here! */
+    AssertRC(rc);
+
+    return RTEXITCODE_FAILURE;
 }
 
@@ -305,7 +392,9 @@
         return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create temporary directory: %Rrc ('%s')", rc, szTmpPath);
 
-    RTEXITCODE rcExit = UnpackExtPack(hTarballFile, szTmpPath, pszTarball);
+    RTEXITCODE rcExit = ValidateExtPackTarball(hTarballFile, pszTarball);
     if (rcExit == RTEXITCODE_SUCCESS)
-        rcExit = ValidateExtPack(szTmpPath, pszTarball, pszName);
+        rcExit = UnpackExtPack(hTarballFile, szTmpPath, pszTarball);
+    if (rcExit == RTEXITCODE_SUCCESS)
+        rcExit = ValidateUnpackedExtPack(szTmpPath, pszTarball, pszName);
     if (rcExit == RTEXITCODE_SUCCESS)
         rcExit = SetExtPackPermissions(szTmpPath);
Index: /trunk/src/VBox/Runtime/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Runtime/Makefile.kmk	(revision 34380)
+++ /trunk/src/VBox/Runtime/Makefile.kmk	(revision 34381)
@@ -1112,4 +1112,7 @@
 	common/checksum/crc32-zlib.cpp \
 	common/checksum/manifest.cpp \
+	common/checksum/manifest2.cpp \
+	common/checksum/manifest3.cpp \
+	common/checksum/manifest-file.cpp \
 	common/checksum/sha1.cpp \
 	common/checksum/sha1str.cpp \
Index: /trunk/src/VBox/Runtime/common/checksum/manifest-file.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/checksum/manifest-file.cpp	(revision 34381)
+++ /trunk/src/VBox/Runtime/common/checksum/manifest-file.cpp	(revision 34381)
@@ -0,0 +1,85 @@
+/* $Id$ */
+/** @file
+ * IPRT - Manifest, the bits with file dependencies
+ */
+
+/*
+ * Copyright (C) 2010 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 "internal/iprt.h"
+#include <iprt/manifest.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/vfs.h>
+
+#include "internal/magics.h"
+
+
+
+RTDECL(int) RTManifestReadStandardFromFile(RTMANIFEST hManifest, const char *pszFilename)
+{
+    RTFILE      hFile;
+    uint32_t    fFlags = RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN;
+    int rc = RTFileOpen(&hFile, pszFilename, fFlags);
+    if (RT_SUCCESS(rc))
+    {
+        RTVFSIOSTREAM hVfsIos;
+        rc = RTVfsIoStrmFromRTFile(hFile, fFlags, true /*fLeaveOpen*/, &hVfsIos);
+        if (RT_SUCCESS(rc))
+        {
+            rc = RTManifestReadStandard(hManifest, hVfsIos);
+            RTVfsIoStrmRelease(hVfsIos);
+        }
+        RTFileClose(hFile);
+    }
+    return rc;
+}
+
+
+RTDECL(int) RTManifestWriteStandardToFile(RTMANIFEST hManifest, const char *pszFilename)
+{
+    RTFILE      hFile;
+    uint32_t    fFlags = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE;
+    int rc = RTFileOpen(&hFile, pszFilename, fFlags);
+    if (RT_SUCCESS(rc))
+    {
+        RTVFSIOSTREAM hVfsIos;
+        rc = RTVfsIoStrmFromRTFile(hFile, fFlags, true /*fLeaveOpen*/, &hVfsIos);
+        if (RT_SUCCESS(rc))
+        {
+            rc = RTManifestWriteStandard(hManifest, hVfsIos);
+            RTVfsIoStrmRelease(hVfsIos);
+        }
+        RTFileClose(hFile);
+    }
+    return rc;
+}
+
+
Index: /trunk/src/VBox/Runtime/common/checksum/manifest2.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/checksum/manifest2.cpp	(revision 34381)
+++ /trunk/src/VBox/Runtime/common/checksum/manifest2.cpp	(revision 34381)
@@ -0,0 +1,720 @@
+/* $Id$ */
+/** @file
+ * IPRT - Manifest, the core.
+ */
+
+/*
+ * Copyright (C) 2010 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 "internal/iprt.h"
+#include <iprt/manifest.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/vfs.h>
+
+#include "internal/magics.h"
+
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+/**
+ * Manifest attribute.
+ *
+ * Used both for entries and manifest attributes.
+ */
+typedef struct RTMANIFESTATTR
+{
+    /** The string space core (szName). */
+    RTSTRSPACECORE      StrCore;
+    /** The property value. */
+    char               *pszValue;
+    /** The attribute type if applicable, RTMANIFEST_ATTR_UNKNOWN if not. */
+    uint32_t            fType;
+    /** The normalized property name that StrCore::pszString points at. */
+    char                szName[1];
+} RTMANIFESTATTR;
+/** Pointer to a manifest attribute. */
+typedef RTMANIFESTATTR *PRTMANIFESTATTR;
+
+
+/**
+ * Manifest entry.
+ */
+typedef struct RTMANIFESTENTRY
+{
+    /** The string space core (szName). */
+    RTSTRSPACECORE      StrCore;
+    /** The entry attributes (hashes, checksums, size, etc) -
+     *  RTMANIFESTATTR. */
+    RTSTRSPACE          Attributes;
+    /** The normalized entry name that StrCore::pszString points at. */
+    char                szName[1];
+} RTMANIFESTENTRY;
+/** Pointer to a manifest entry. */
+typedef RTMANIFESTENTRY *PRTMANIFESTENTRY;
+
+
+/**
+ * Manifest handle data.
+ */
+typedef struct RTMANIFESTINT
+{
+    /** Magic value (RTMANIFEST_MAGIC). */
+    uint32_t            u32Magic;
+    /** The number of references to this manifest. */
+    uint32_t volatile   cRefs;
+    /** Manifest attributes - RTMANIFESTATTR. */
+    RTSTRSPACE          Attributes;
+    /** String space of the entries covered by this manifest -
+     *  RTMANIFESTENTRY. */
+    RTSTRSPACE          Entries;
+} RTMANIFESTINT;
+
+/** The value of RTMANIFESTINT::u32Magic. */
+#define RTMANIFEST_MAGIC    UINT32_C(0x99998866)
+
+
+/**
+ * Creates an empty manifest.
+ *
+ * @returns IPRT status code.
+ * @param   fFlags              Flags, MBZ.
+ * @param   phManifest          Where to return the handle to the manifest.
+ */
+RTDECL(int) RTManifestCreate(uint32_t fFlags, PRTMANIFEST phManifest)
+{
+    AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
+    AssertPtr(phManifest);
+
+    RTMANIFESTINT *pThis = (RTMANIFESTINT *)RTMemAlloc(sizeof(*pThis));
+    if (!pThis)
+        return VERR_NO_MEMORY;
+
+    pThis->u32Magic     = RTMANIFEST_MAGIC;
+    pThis->cRefs        = 1;
+    pThis->Attributes   = NULL;
+    pThis->Entries      = NULL;
+
+    *phManifest = pThis;
+    return VINF_SUCCESS;
+}
+
+/**
+ * Retains a reference to the manifest handle.
+ *
+ * @returns The new reference count, UINT32_MAX if the handle is invalid.
+ * @param   hManifest           The handle to retain.
+ */
+RTDECL(uint32_t) RTManifestRetain(RTMANIFEST hManifest)
+{
+    RTMANIFESTINT *pThis = hManifest;
+    AssertPtrReturn(pThis, UINT32_MAX);
+    AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, UINT32_MAX);
+
+    uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+    Assert(cRefs > 1 && cRefs < _1M);
+
+    return cRefs;
+}
+
+
+/**
+ * @callback_method_impl{FNRTSTRSPACECALLBACK, Destroys RTMANIFESTATTR.}
+ */
+static DECLCALLBACK(int) rtManifestDestroyAttribute(PRTSTRSPACECORE pStr, void *pvUser)
+{
+    PRTMANIFESTATTR pAttr = RT_FROM_MEMBER(pStr, RTMANIFESTATTR, StrCore);
+    RTStrFree(pAttr->pszValue);
+    pAttr->pszValue = NULL;
+    RTMemFree(pAttr);
+    return 0;
+}
+
+
+/**
+ * @callback_method_impl{FNRTSTRSPACECALLBACK, Destroys RTMANIFESTENTRY.}
+ */
+static DECLCALLBACK(int) rtManifestDestroyEntry(PRTSTRSPACECORE pStr, void *pvUser)
+{
+    PRTMANIFESTENTRY pEntry = RT_FROM_MEMBER(pStr, RTMANIFESTENTRY, StrCore);
+    RTStrSpaceDestroy(&pEntry->Attributes, rtManifestDestroyAttribute, pvUser);
+    RTMemFree(pEntry);
+    return 0;
+}
+
+
+/**
+ * Releases a reference to the manifest handle.
+ *
+ * @returns The new reference count, 0 if free. UINT32_MAX is returned if the
+ *          handle is invalid.
+ * @param   hManifest           The handle to release.
+ *                              NIL is quietly ignored (returns 0).
+ */
+RTDECL(uint32_t) RTManifestRelease(RTMANIFEST hManifest)
+{
+    RTMANIFESTINT *pThis = hManifest;
+    if (pThis == NIL_RTMANIFEST)
+        return 0;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
+
+    uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+    Assert(cRefs < _1M);
+    if (!cRefs)
+    {
+        ASMAtomicWriteU32(&pThis->u32Magic, ~RTMANIFEST_MAGIC);
+        RTStrSpaceDestroy(&pThis->Attributes, rtManifestDestroyAttribute, pThis);
+        RTStrSpaceDestroy(&pThis->Entries,    rtManifestDestroyEntry,     pThis);
+        RTMemFree(pThis);
+    }
+
+    return cRefs;
+}
+
+
+/**
+ * Creates a duplicate of the specified manifest.
+ *
+ * @returns IPRT status code
+ * @param   hManifestSrc        The manifest to clone.
+ * @param   phManifestDst       Where to store the handle to the duplicate.
+ */
+RTDECL(int) RTManifestDup(RTMANIFEST hManifestSrc, PRTMANIFEST phManifestDst)
+{
+    RTMANIFESTINT *pThis = hManifestSrc;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
+    AssertPtr(phManifestDst);
+
+
+    /** @todo implement cloning. */
+
+    return VERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * Compares two manifests for equality.
+ *
+ * @returns true if equals, false if not.
+ * @param   hManifest1          The first manifest.
+ * @param   hManifest2          The second manifest.
+ * @param   papszIgnoreEntries  Entries to ignore.  Ends with a NULL entry.
+ * @param   papszIgnoreAttrs    Attributes to ignore.  Ends with a NULL entry.
+ */
+RTDECL(int) RTManifestEqualsEx(RTMANIFEST hManifest1, RTMANIFEST hManifest2, const char * const *papszIgnoreEntries,
+                               const char * const *papszIgnoreAttr)
+{
+    RTMANIFESTINT *pThis1 = hManifest1;
+    RTMANIFESTINT *pThis2 = hManifest1;
+    if (pThis1 != NIL_RTMANIFEST)
+    {
+        AssertPtrReturn(pThis1, false);
+        AssertReturn(pThis1->u32Magic == RTMANIFEST_MAGIC, false);
+    }
+    if (pThis2 != NIL_RTMANIFEST)
+    {
+        AssertPtrReturn(pThis2, false);
+        AssertReturn(pThis2->u32Magic == RTMANIFEST_MAGIC, false);
+    }
+
+    /*
+     * The simple cases.
+     */
+    if (pThis1 == pThis2)
+        return true;
+    if (pThis1 == NIL_RTMANIFEST || pThis2 == NIL_RTMANIFEST)
+        return false;
+
+    /*
+     *
+     */
+
+
+    /** @todo implement comparing. */
+
+    return VERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * Compares two manifests for equality.
+ *
+ * @returns true if equals, false if not.
+ * @param   hManifest1          The first manifest.
+ * @param   hManifest2          The second manifest.
+ */
+RTDECL(int) RTManifestEquals(RTMANIFEST hManifest1, RTMANIFEST hManifest2)
+{
+    return RTManifestEqualsEx(hManifest1, hManifest2, NULL /*papszIgnoreEntries*/, NULL /*papszIgnoreAttrs*/);
+}
+
+
+/**
+ * Worker common to RTManifestSetAttr and RTManifestEntrySetAttr.
+ *
+ * @returns IPRT status code.
+ * @param   pAttributes         The attribute container.
+ * @param   pszAttr             The name of the attribute to add.
+ * @param   pszValue            The value string.
+ * @param   fType               The attribute type type.
+ */
+static int rtManifestSetAttrWorker(PRTSTRSPACE pAttributes, const char *pszAttr, const char *pszValue, uint32_t fType)
+{
+    char *pszValueCopy;
+    int rc = RTStrDupEx(&pszValueCopy, pszValue);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /*
+     * Does the attribute exist already?
+     */
+    AssertCompileMemberOffset(RTMANIFESTATTR, StrCore, 0);
+    PRTMANIFESTATTR pAttr = (PRTMANIFESTATTR)RTStrSpaceGet(pAttributes, pszAttr);
+    if (pAttr)
+    {
+        RTStrFree(pAttr->pszValue);
+        pAttr->pszValue = pszValueCopy;
+        pAttr->fType    = fType;
+    }
+    else
+    {
+        size_t          cbName = strlen(pszAttr) + 1;
+        pAttr = (PRTMANIFESTATTR)RTMemAllocVar(RT_OFFSETOF(RTMANIFESTATTR, szName[cbName]));
+        if (!pAttr)
+        {
+            RTStrFree(pszValueCopy);
+            return VERR_NO_MEMORY;
+        }
+        memcpy(pAttr->szName, pszAttr, cbName);
+        pAttr->StrCore.pszString = pAttr->szName;
+        pAttr->StrCore.cchString = cbName - 1;
+        pAttr->pszValue = pszValueCopy;
+        pAttr->fType    = fType;
+        if (RT_UNLIKELY(!RTStrSpaceInsert(pAttributes, &pAttr->StrCore)))
+        {
+            AssertFailed();
+            RTStrFree(pszValueCopy);
+            RTMemFree(pAttr);
+            return VERR_INTERNAL_ERROR_4;
+        }
+    }
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Sets a manifest attribute.
+ *
+ * @returns IPRT status code.
+ * @param   hManifest           The manifest handle.
+ * @param   pszAttr             The attribute name.  If this already exists,
+ *                              its value will be replaced.
+ * @param   pszValue            The value string.
+ * @param   fType               The attribute type, pass
+ *                              RTMANIFEST_ATTR_UNKNOWN if not known.
+ */
+RTDECL(int) RTManifestSetAttr(RTMANIFEST hManifest, const char *pszAttr, const char *pszValue, uint32_t fType)
+{
+    RTMANIFESTINT *pThis = hManifest;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
+    AssertPtr(pszAttr);
+    AssertPtr(pszValue);
+    AssertReturn(RT_IS_POWER_OF_TWO(fType) && fType < RTMANIFEST_ATTR_END, VERR_INVALID_PARAMETER);
+
+    return rtManifestSetAttrWorker(&pThis->Attributes, pszAttr, pszValue, fType);
+}
+
+
+/**
+ * Worker common to RTManifestUnsetAttr and RTManifestEntryUnsetAttr.
+ *
+ * @returns IPRT status code.
+ * @param   pAttributes         The attribute container.
+ * @param   pszAttr             The name of the attribute to remove.
+ */
+static int rtManifestUnsetAttrWorker(PRTSTRSPACE pAttributes, const char *pszAttr)
+{
+    PRTSTRSPACECORE pStrCore = RTStrSpaceRemove(pAttributes, pszAttr);
+    if (!pStrCore)
+        return VWRN_NOT_FOUND;
+    rtManifestDestroyAttribute(pStrCore, NULL);
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Unsets (removes) a manifest attribute if it exists.
+ *
+ * @returns IPRT status code.
+ * @retval  VWRN_NOT_FOUND if not found.
+ *
+ * @param   hManifest           The manifest handle.
+ * @param   pszAttr             The attribute name.
+ */
+RTDECL(int) RTManifestUnsetAttr(RTMANIFEST hManifest, const char *pszAttr)
+{
+    RTMANIFESTINT *pThis = hManifest;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
+    AssertPtr(pszAttr);
+
+    return rtManifestUnsetAttrWorker(&pThis->Attributes, pszAttr);
+}
+
+
+/**
+ * Validates the name entry.
+ *
+ * @returns IPRT status code.
+ * @param   pszEntry            The entry name to validate.
+ * @param   pfNeedNormalization Where to return whether it needs normalization
+ *                              or not.  Optional.
+ * @param   pcchEntry           Where to return the length.  Optional.
+ */
+static int rtManifestValidateNameEntry(const char *pszEntry, bool *pfNeedNormalization, size_t *pcchEntry)
+{
+    int         rc;
+    bool        fNeedNormalization = false;
+    const char *pszCur             = pszEntry;
+
+    for (;;)
+    {
+        RTUNICP uc;
+        rc = RTStrGetCpEx(&pszCur, &uc);
+        if (RT_FAILURE(rc))
+            break;
+        if (!uc)
+            break;
+        if (uc == '\\')
+            fNeedNormalization = true;
+        else if (uc < 32 || uc == ':' || uc == '(' || uc == ')')
+        {
+            rc = VERR_INVALID_NAME;
+            break;
+        }
+    }
+
+    if (pfNeedNormalization)
+        *pfNeedNormalization = fNeedNormalization;
+    if (pcchEntry)
+        *pcchEntry = pszCur - pszEntry - 1;
+    return rc;
+}
+
+
+/**
+ * Normalizes a entry name.
+ *
+ * @param   pszEntry            The entry name to normalize.
+ */
+static void rtManifestNormalizeEntry(char *pszEntry)
+{
+    char  ch;
+    while ((ch = *pszEntry))
+    {
+        if (ch == '\\')
+            *pszEntry = '/';
+        pszEntry++;
+    }
+}
+
+
+/**
+ * Gets an entry.
+ *
+ * @returns IPRT status code.
+ * @param   pThis               The manifest to work with.
+ * @param   pszEntry            The entry name.
+ * @param   fNeedNormalization  Whether rtManifestValidateNameEntry said it
+ *                              needed normalization.
+ * @param   cchEntry            The length of the name.
+ * @param   ppEntry             Where to return the entry pointer on success.
+ */
+static int rtManifestGetEntry(RTMANIFESTINT *pThis, const char *pszEntry, bool fNeedNormalization, size_t cchEntry,
+                              PRTMANIFESTENTRY *ppEntry)
+{
+    PRTMANIFESTENTRY pEntry;
+
+    AssertCompileMemberOffset(RTMANIFESTATTR, StrCore, 0);
+    if (!fNeedNormalization)
+        pEntry = (PRTMANIFESTENTRY)RTStrSpaceGet(&pThis->Entries, pszEntry);
+    else
+    {
+        char *pszCopy = (char *)RTMemTmpAlloc(cchEntry + 1);
+        if (RT_UNLIKELY(!pszCopy))
+            return VERR_NO_TMP_MEMORY;
+        memcpy(pszCopy, pszEntry, cchEntry + 1);
+        rtManifestNormalizeEntry(pszCopy);
+
+        pEntry = (PRTMANIFESTENTRY)RTStrSpaceGet(&pThis->Entries, pszCopy);
+        RTMemTmpFree(pszCopy);
+    }
+
+    *ppEntry = pEntry;
+    return pEntry ? VINF_SUCCESS : VERR_NOT_FOUND;
+}
+
+
+/**
+ * Sets an attribute of a manifest entry.
+ *
+ * @returns IPRT status code.
+ * @param   hManifest           The manifest handle.
+ * @param   pszEntry            The entry name.  This will automatically be
+ *                              added if there was no previous call to
+ *                              RTManifestEntryAdd for this name.  See
+ *                              RTManifestEntryAdd for the entry name rules.
+ * @param   pszAttr             The attribute name.  If this already exists,
+ *                              its value will be replaced.
+ * @param   pszValue            The value string.
+ * @param   fType               The attribute type, pass
+ *                              RTMANIFEST_ATTR_UNKNOWN if not known.
+ */
+RTDECL(int) RTManifestEntrySetAttr(RTMANIFEST hManifest, const char *pszEntry, const char *pszAttr,
+                                   const char *pszValue, uint32_t fType)
+{
+    RTMANIFESTINT *pThis = hManifest;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
+    AssertPtr(pszEntry);
+    AssertPtr(pszAttr);
+    AssertPtr(pszValue);
+    AssertReturn(RT_IS_POWER_OF_TWO(fType) && fType < RTMANIFEST_ATTR_END, VERR_INVALID_PARAMETER);
+
+    bool    fNeedNormalization;
+    size_t  cchEntry;
+    int rc = rtManifestValidateNameEntry(pszEntry, &fNeedNormalization, &cchEntry);
+    AssertRCReturn(rc, rc);
+
+    /*
+     * Resolve the entry, adding one if necessary.
+     */
+    PRTMANIFESTENTRY pEntry;
+    rc = rtManifestGetEntry(pThis, pszEntry, fNeedNormalization, cchEntry, &pEntry);
+    if (rc == VERR_NOT_FOUND)
+    {
+        pEntry = (PRTMANIFESTENTRY)RTMemAlloc(RT_OFFSETOF(RTMANIFESTENTRY, szName[cchEntry + 1]));
+        if (!pEntry)
+            return VERR_NO_MEMORY;
+
+        pEntry->StrCore.cchString = cchEntry;
+        pEntry->StrCore.pszString = pEntry->szName;
+        pEntry->Attributes = NULL;
+        memcpy(pEntry->szName, pszEntry, cchEntry + 1);
+        if (fNeedNormalization)
+            rtManifestNormalizeEntry(pEntry->szName);
+
+        if (!RTStrSpaceInsert(&pThis->Entries, &pEntry->StrCore))
+        {
+            RTMemFree(pEntry);
+            return VERR_INTERNAL_ERROR_4;
+        }
+    }
+    else if (RT_FAILURE(rc))
+        return rc;
+
+    return rtManifestSetAttrWorker(&pEntry->Attributes, pszAttr, pszValue, fType);
+}
+
+
+/**
+ * Unsets (removes) an attribute of a manifest entry if they both exist.
+ *
+ * @returns IPRT status code.
+ * @retval  VWRN_NOT_FOUND if not found.
+ *
+ * @param   hManifest           The manifest handle.
+ * @param   pszEntry            The entry name.
+ * @param   pszAttr             The attribute name.
+ */
+RTDECL(int) RTManifestEntryUnsetAttr(RTMANIFEST hManifest, const char *pszEntry, const char *pszAttr)
+{
+    RTMANIFESTINT *pThis = hManifest;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
+    AssertPtr(pszEntry);
+    AssertPtr(pszAttr);
+
+    bool    fNeedNormalization;
+    size_t  cchEntry;
+    int rc = rtManifestValidateNameEntry(pszEntry, &fNeedNormalization, &cchEntry);
+    AssertRCReturn(rc, rc);
+
+    /*
+     * Resolve the entry and hand it over to the worker.
+     */
+    PRTMANIFESTENTRY pEntry;
+    rc = rtManifestGetEntry(pThis, pszEntry, fNeedNormalization, cchEntry, &pEntry);
+    if (RT_SUCCESS(rc))
+        rc = rtManifestUnsetAttrWorker(&pEntry->Attributes, pszAttr);
+    return rc;
+}
+
+
+/**
+ * Adds a new entry to a manifest.
+ *
+ * The entry name rules:
+ *     - The entry name can contain any character defined by unicode, except
+ *       control characters, ':', '(' and ')'.  The exceptions are mainly there
+ *       because of uncertainty around how various formats handles these.
+ *     - It is considered case sensitive.
+ *     - Forward (unix) and backward (dos) slashes are considered path
+ *       separators and converted to forward slashes.
+ *
+ * @returns IPRT status code.
+ * @retval  VWRN_ALREADY_EXISTS if the entry already exists.
+ *
+ * @param   hManifest           The manifest handle.
+ * @param   pszEntry            The entry name (UTF-8).
+ *
+ * @remarks Some manifest formats will not be able to store an entry without
+ *          any attributes.  So, this is just here in case it comes in handy
+ *          when dealing with formats which can.
+ */
+RTDECL(int) RTManifestEntryAdd(RTMANIFEST hManifest, const char *pszEntry)
+{
+    RTMANIFESTINT *pThis = hManifest;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
+    AssertPtr(pszEntry);
+
+    bool    fNeedNormalization;
+    size_t  cchEntry;
+    int rc = rtManifestValidateNameEntry(pszEntry, &fNeedNormalization, &cchEntry);
+    AssertRCReturn(rc, rc);
+
+    /*
+     * Only add one if it does not already exist.
+     */
+    PRTMANIFESTENTRY pEntry;
+    rc = rtManifestGetEntry(pThis, pszEntry, fNeedNormalization, cchEntry, &pEntry);
+    if (rc == VERR_NOT_FOUND)
+    {
+        pEntry = (PRTMANIFESTENTRY)RTMemAlloc(RT_OFFSETOF(RTMANIFESTENTRY, szName[cchEntry + 1]));
+        if (pEntry)
+        {
+            pEntry->StrCore.cchString = cchEntry;
+            pEntry->StrCore.pszString = pEntry->szName;
+            pEntry->Attributes = NULL;
+            memcpy(pEntry->szName, pszEntry, cchEntry + 1);
+            if (fNeedNormalization)
+                rtManifestNormalizeEntry(pEntry->szName);
+
+            if (RTStrSpaceInsert(&pThis->Entries, &pEntry->StrCore))
+                rc = VINF_SUCCESS;
+            else
+            {
+                RTMemFree(pEntry);
+                rc = VERR_INTERNAL_ERROR_4;
+            }
+        }
+        else
+            rc = VERR_NO_MEMORY;
+    }
+    else if (RT_SUCCESS(rc))
+        rc = VWRN_ALREADY_EXISTS;
+
+    return rc;
+}
+
+
+/**
+ * Removes an entry.
+ *
+ * @returns IPRT status code.
+ * @param   hManifest           The manifest handle.
+ * @param   pszEntry            The entry name.
+ */
+RTDECL(int) RTManifestEntryRemove(RTMANIFEST hManifest, const char *pszEntry)
+{
+    RTMANIFESTINT *pThis = hManifest;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertReturn(pThis->u32Magic == RTMANIFEST_MAGIC, VERR_INVALID_HANDLE);
+    AssertPtr(pszEntry);
+
+    bool    fNeedNormalization;
+    size_t  cchEntry;
+    int rc = rtManifestValidateNameEntry(pszEntry, &fNeedNormalization, &cchEntry);
+    AssertRCReturn(rc, rc);
+
+    /*
+     * Only add one if it does not already exist.
+     */
+    PRTMANIFESTENTRY pEntry;
+    rc = rtManifestGetEntry(pThis, pszEntry, fNeedNormalization, cchEntry, &pEntry);
+    if (RT_SUCCESS(rc))
+    {
+        PRTSTRSPACECORE pStrCore = RTStrSpaceRemove(&pThis->Entries, pEntry->StrCore.pszString);
+        AssertReturn(pStrCore, VERR_INTERNAL_ERROR_3);
+        rtManifestDestroyEntry(pStrCore, pThis);
+    }
+
+    return rc;
+}
+
+
+/**
+ * Reads in a "standard" manifest.
+ *
+ * This reads the format used by OVF, the distinfo in FreeBSD ports, and
+ * others.
+ *
+ * @returns IPRT status code.
+ * @param   hManifest           The handle to the manifest where to add the
+ *                              manifest that's read in.
+ * @param   hVfsIos             The I/O stream to read the manifest from.
+ */
+RTDECL(int) RTManifestReadStandard(RTMANIFEST hManifest, RTVFSIOSTREAM hVfsIos)
+{
+    return VERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * Writes a "standard" manifest.
+ *
+ * This writes the format used by OVF, the distinfo in FreeBSD ports, and
+ * others.
+ *
+ * @returns IPRT status code.
+ * @param   hManifest           The handle to the manifest where to add the
+ *                              manifest that's read in.
+ * @param   hVfsIos             The I/O stream to read the manifest from.
+ */
+RTDECL(int) RTManifestWriteStandard(RTMANIFEST hManifest, RTVFSIOSTREAM hVfsIos)
+{
+    return VERR_NOT_IMPLEMENTED;
+}
+
Index: /trunk/src/VBox/Runtime/common/checksum/manifest3.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/checksum/manifest3.cpp	(revision 34381)
+++ /trunk/src/VBox/Runtime/common/checksum/manifest3.cpp	(revision 34381)
@@ -0,0 +1,290 @@
+/* $Id$ */
+/** @file
+ * IPRT - Manifest, the bits with the most dependencies.
+ */
+
+/*
+ * Copyright (C) 2010 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 "internal/iprt.h"
+#include <iprt/manifest.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/md5.h>
+#include <iprt/mem.h>
+#include <iprt/sha.h>
+#include <iprt/string.h>
+#include <iprt/vfs.h>
+
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+/**
+ * Hashes data.
+ *
+ * Used when hashing a file, stream or similar.
+ */
+typedef struct RTMANIFESTHASHES
+{
+    /** The desired attribute types.
+     * Only the hashes indicated by this will be calculated. */
+    uint32_t        fAttrs;
+    /** The size. */
+    RTFOFF          cbStream;
+
+    /** The MD5 context.  */
+    RTMD5CONTEXT    Md5Ctx;
+    /** The SHA-1 context.  */
+    RTSHA1CONTEXT   Sha1Ctx;
+    /** The SHA-256 context.  */
+    RTSHA256CONTEXT Sha256Ctx;
+    /** The SHA-512 context.  */
+    RTSHA512CONTEXT Sha512Ctx;
+
+    /** The MD5 digest. */
+    uint8_t         abMd5Digest[RTMD5_HASH_SIZE];
+    /** The SHA-1 digest. */
+    uint8_t         abSha1Digest[RTSHA1_HASH_SIZE];
+    /** The SHA-256 digest. */
+    uint8_t         abSha256Digest[RTSHA256_HASH_SIZE];
+    /** The SHA-512 digest. */
+    uint8_t         abSha512Digest[RTSHA512_HASH_SIZE];
+} RTMANIFESTHASHES;
+/** Pointer to a the hashes for a stream. */
+typedef RTMANIFESTHASHES *PRTMANIFESTHASHES;
+
+
+/**
+ * Creates a hashes structure.
+ *
+ * @returns Pointer to a hashes structure.
+ * @param   fAttrs              The desired hashes, RTMANIFEST_ATTR_XXX.
+ */
+static PRTMANIFESTHASHES rtManifestHashesCreate(uint32_t fAttrs)
+{
+    PRTMANIFESTHASHES pHashes = (PRTMANIFESTHASHES)RTMemTmpAllocZ(sizeof(*pHashes));
+    if (pHashes)
+    {
+        pHashes->fAttrs     = fAttrs;
+        /*pHashes->cbStream   = 0;*/
+        if (fAttrs & RTMANIFEST_ATTR_MD5)
+            RTMd5Init(&pHashes->Md5Ctx);
+        if (fAttrs & RTMANIFEST_ATTR_SHA1)
+            RTSha1Init(&pHashes->Sha1Ctx);
+        if (fAttrs & RTMANIFEST_ATTR_SHA256)
+            RTSha256Init(&pHashes->Sha256Ctx);
+        if (fAttrs & RTMANIFEST_ATTR_SHA512)
+            RTSha512Init(&pHashes->Sha512Ctx);
+    }
+    return pHashes;
+}
+
+
+/**
+ * Updates the hashes with a block of data.
+ *
+ * @param   pHashes             The hashes structure.
+ * @param   pvBuf               The data block.
+ * @param   cbBuf               The size of the data block.
+ */
+static void rtManifestHashesUpdate(PRTMANIFESTHASHES pHashes, void const *pvBuf, size_t cbBuf)
+{
+    pHashes->cbStream += cbBuf;
+    if (pHashes->fAttrs & RTMANIFEST_ATTR_MD5)
+        RTMd5Update(&pHashes->Md5Ctx, pvBuf, cbBuf);
+    if (pHashes->fAttrs & RTMANIFEST_ATTR_SHA1)
+        RTSha1Update(&pHashes->Sha1Ctx, pvBuf, cbBuf);
+    if (pHashes->fAttrs & RTMANIFEST_ATTR_SHA256)
+        RTSha256Update(&pHashes->Sha256Ctx, pvBuf, cbBuf);
+    if (pHashes->fAttrs & RTMANIFEST_ATTR_SHA512)
+        RTSha512Update(&pHashes->Sha512Ctx, pvBuf, cbBuf);
+}
+
+
+/**
+ * Finalizes all the hashes.
+ *
+ * @param   pHashes             The hashes structure.
+ */
+static void rtManifestHashesFinal(PRTMANIFESTHASHES pHashes)
+{
+    if (pHashes->fAttrs & RTMANIFEST_ATTR_MD5)
+        RTMd5Final(pHashes->abMd5Digest, &pHashes->Md5Ctx);
+    if (pHashes->fAttrs & RTMANIFEST_ATTR_SHA1)
+        RTSha1Final(&pHashes->Sha1Ctx, pHashes->abSha1Digest);
+    if (pHashes->fAttrs & RTMANIFEST_ATTR_SHA256)
+        RTSha256Final(&pHashes->Sha256Ctx, pHashes->abSha256Digest);
+    if (pHashes->fAttrs & RTMANIFEST_ATTR_SHA512)
+        RTSha512Final(&pHashes->Sha512Ctx, pHashes->abSha512Digest);
+}
+
+
+/**
+ * Adds the hashes to a manifest entry.
+ *
+ * @returns IPRT status code.
+ * @param   pHashes             The hashes structure.
+ * @param   hManifest           The manifest to add them to.
+ * @param   pszEntry            The entry name.
+ */
+static int rtManifestHashesSetAttrs(PRTMANIFESTHASHES pHashes, RTMANIFEST hManifest, const char *pszEntry)
+{
+    char szValue[RTSHA512_DIGEST_LEN + 8];
+    int rc = VINF_SUCCESS;
+    int rc2;
+
+    if (pHashes->fAttrs & RTMANIFEST_ATTR_SIZE)
+    {
+        RTStrPrintf(szValue, sizeof(szValue), "%RU64", (uint64_t)pHashes->cbStream);
+        rc2 = RTManifestEntrySetAttr(hManifest, pszEntry, "SIZE", szValue, RTMANIFEST_ATTR_SIZE);
+        if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+            rc = rc2;
+    }
+
+    if (pHashes->fAttrs & RTMANIFEST_ATTR_MD5)
+    {
+        rc2 = RTMd5ToString(pHashes->abMd5Digest, szValue, sizeof(szValue));
+        if (RT_SUCCESS(rc2))
+            rc2 = RTManifestEntrySetAttr(hManifest, pszEntry, "MD5", szValue, RTMANIFEST_ATTR_MD5);
+        if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+            rc = rc2;
+    }
+
+    if (pHashes->fAttrs & RTMANIFEST_ATTR_SHA1)
+    {
+        rc2 = RTSha1ToString(pHashes->abSha1Digest, szValue, sizeof(szValue));
+        if (RT_SUCCESS(rc2))
+            rc2 = RTManifestEntrySetAttr(hManifest, pszEntry, "SHA1", szValue, RTMANIFEST_ATTR_SHA1);
+        if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+            rc = rc2;
+    }
+
+    if (pHashes->fAttrs & RTMANIFEST_ATTR_SHA256)
+    {
+        rc2 = RTSha256ToString(pHashes->abSha256Digest, szValue, sizeof(szValue));
+        if (RT_SUCCESS(rc2))
+            rc2 = RTManifestEntrySetAttr(hManifest, pszEntry, "SHA256", szValue, RTMANIFEST_ATTR_SHA256);
+        if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+            rc = rc2;
+    }
+
+    if (pHashes->fAttrs & RTMANIFEST_ATTR_SHA512)
+    {
+        rc2 = RTSha512ToString(pHashes->abSha512Digest, szValue, sizeof(szValue));
+        if (RT_SUCCESS(rc2))
+            rc2 = RTManifestEntrySetAttr(hManifest, pszEntry, "SHA512", szValue, RTMANIFEST_ATTR_SHA512);
+        if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+            rc = rc2;
+    }
+    return rc;
+}
+
+
+/**
+ * Destroys the hashes.
+ *
+ * @param   pHashes             The hashes structure.  NULL is ignored.
+ */
+static void rtManifestHashesDestroy(PRTMANIFESTHASHES pHashes)
+{
+    RTMemTmpFree(pHashes);
+}
+
+
+/**
+ * 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, cbBuf);
+        }
+        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;
+}
+
+
+
