VirtualBox

Changeset 34787 in vbox


Ignore:
Timestamp:
Dec 7, 2010 2:51:18 PM (14 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
68597
Message:

Main,VBoxManage,FE/Qt: Implemented IExtPackFile and dropped IExtPackManager::Install.

Location:
trunk/src/VBox
Files:
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VBoxManage/VBoxManageMisc.cpp

    r34610 r34787  
    2121*******************************************************************************/
    2222#ifndef VBOX_ONLY_DOCS
    23 #include <VBox/com/com.h>
    24 #include <VBox/com/string.h>
    25 #include <VBox/com/Guid.h>
    26 #include <VBox/com/array.h>
    27 #include <VBox/com/ErrorInfo.h>
    28 #include <VBox/com/errorprint.h>
    29 #include <VBox/com/EventQueue.h>
    30 
    31 #include <VBox/com/VirtualBox.h>
     23# include <VBox/com/com.h>
     24# include <VBox/com/string.h>
     25# include <VBox/com/Guid.h>
     26# include <VBox/com/array.h>
     27# include <VBox/com/ErrorInfo.h>
     28# include <VBox/com/errorprint.h>
     29# include <VBox/com/EventQueue.h>
     30
     31# include <VBox/com/VirtualBox.h>
    3232#endif /* !VBOX_ONLY_DOCS */
    3333
     
    912912        Bstr bstrTarball(szPath);
    913913        Bstr bstrName;
    914         CHECK_ERROR2_RET(ptrExtPackMgr, Install(bstrTarball.raw(), bstrName.asOutParam()), RTEXITCODE_FAILURE);
     914        ComPtr<IExtPackFile> ptrExtPackFile;
     915        CHECK_ERROR2_RET(ptrExtPackMgr, OpenExtPackFile(bstrTarball.raw(), ptrExtPackFile.asOutParam()), RTEXITCODE_FAILURE);
     916        CHECK_ERROR2_RET(ptrExtPackFile, COMGETTER(Name)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
     917        CHECK_ERROR2_RET(ptrExtPackFile, Install(), RTEXITCODE_FAILURE);
    915918        RTPrintf("Successfully installed \"%lS\".\n", bstrName.raw());
    916919    }
  • trunk/src/VBox/Frontends/VirtualBox/src/globals/VBoxProblemReporter.cpp

    r34781 r34787  
    4545
    4646#if defined (Q_WS_WIN32)
    47 #include <Htmlhelp.h>
     47# include <Htmlhelp.h>
    4848#endif
    4949
     
    21102110}
    21112111
    2112 void VBoxProblemReporter::cannotInstallExtPack(const QString &strFilename, const CExtPackManager &extPackManager, QWidget *pParent /* = 0 */)
     2112void VBoxProblemReporter::cannotOpenExtPack(const QString &strFilename, const CExtPackManager &extPackManager, QWidget *pParent /* = 0 */)
     2113{
     2114    message (pParent ? pParent : mainWindowShown(),
     2115             Error,
     2116             tr("Failed to open the Extension Pack <b>%1</b>.").arg(strFilename),
     2117             formatErrorInfo(extPackManager));
     2118}
     2119
     2120void VBoxProblemReporter::badExtPackFile(const QString &strFilename, const CExtPackFile &extPackFile, QWidget *pParent /* = 0 */)
     2121{
     2122    message (pParent ? pParent : mainWindowShown(),
     2123             Error,
     2124             tr("Failed to open the Extension Pack <b>%1</b>.").arg(strFilename),
     2125             extPackFile.GetWhyUnusable());
     2126}
     2127
     2128void VBoxProblemReporter::cannotInstallExtPack(const QString &strFilename, const CExtPackFile &extPackFile, QWidget *pParent /* = 0 */)
    21132129{
    21142130    message (pParent ? pParent : mainWindowShown(),
    21152131             Error,
    21162132             tr("Failed to install the Extension Pack <b>%1</b>.").arg(strFilename),
    2117              formatErrorInfo(extPackManager));
     2133             formatErrorInfo(extPackFile));
    21182134}
    21192135
  • trunk/src/VBox/Frontends/VirtualBox/src/globals/VBoxProblemReporter.h

    r34740 r34787  
    338338    void cannotUpdateGuestAdditions (const CProgress &aProgress, QWidget *aParent /* = NULL */) const;
    339339
    340     void cannotInstallExtPack(const QString &strFilename, const CExtPackManager &extPackManager, QWidget *pParent = 0);
     340    void cannotOpenExtPack(const QString &strFilename, const CExtPackManager &extPackManager, QWidget *pParent = 0);
     341    void badExtPackFile(const QString &strFilename, const CExtPackFile &extPackFile, QWidget *pParent = 0);
     342    void cannotInstallExtPack(const QString &strFilename, const CExtPackFile &extPackFile, QWidget *pParent = 0);
    341343    void cannotUninstallExtPack(const QString &strPackName, const CExtPackManager &extPackManager, QWidget *pParent = 0);
    342344    bool confirmRemovingPackage(const QString &strPackName, QWidget *pParent = 0);
  • trunk/src/VBox/Frontends/VirtualBox/src/selector/VBoxSelectorWnd.cpp

    r34728 r34787  
    3939#include "QIFileDialog.h"
    4040#include "UIDesktopServices.h"
     41#include "UIGlobalSettingsExtension.h" /* extension pack installation */
    4142
    4243#ifdef VBOX_GUI_WITH_SYSTRAY
     
    10111012            else if (VBoxGlobal::hasAllowedExtension(strFile, VBoxDefs::VBoxExtPackFileExts))
    10121013            {
    1013                 CExtPackManager extPackManager = vboxGlobal().virtualBox().GetExtensionPackManager();
    1014                 extPackManager.Install(strFile);
    1015                 if (!extPackManager.isOk())
    1016                     vboxProblem().cannotInstallExtPack(strFile, extPackManager, this);
     1014                UIGlobalSettingsExtension::doInstallation(strFile, this, NULL);
    10171015            }
    10181016        }
  • trunk/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsExtension.cpp

    r34596 r34787  
    104104}
    105105
     106/**
     107 * Attempt the actual installation.
     108 *
     109 * This code is shared by UIGlobalSettingsExtension::sltInstallPackage and
     110 * VBoxSelectorWnd::sltOpenUrls.
     111 * @todo    Is there perhaps a better home for this method?
     112 *
     113 * @returns true if successfully installed, false if not.
     114 * @param   strFilePath     The path to the tarball.
     115 * @param   pParent         The parent widget.
     116 * @param   pstrExtPackName Where to return the extension pack name. Optional.
     117 */
     118/*static*/ bool UIGlobalSettingsExtension::doInstallation(QString const &strFilePath, QWidget *pParent, QString *pstrExtPackName)
     119{
     120    bool fInstalled = false;
     121
     122    /*
     123     * Open the extpack tarball via IExtPackManager.
     124     */
     125    CExtPackManager manager = vboxGlobal().virtualBox().GetExtensionPackManager();
     126    CExtPackFile extPackFile = manager.OpenExtPackFile(strFilePath);
     127    if (manager.isOk())
     128    {
     129        if (pstrExtPackName)
     130            *pstrExtPackName = extPackFile.GetName();
     131        if (extPackFile.GetUsable())
     132        {
     133            bool fAck = true;
     134            /** @todo display big fat warning message. */
     135
     136            if (fAck)
     137            {
     138                /** @todo display licenses.  */
     139            }
     140
     141            /*
     142             * Install it if everything was ACKed by the user.
     143             */
     144            if (fAck)
     145            {
     146                extPackFile.Install();
     147                if (extPackFile.isOk())
     148                    fInstalled = true;
     149                else
     150                    vboxProblem().cannotInstallExtPack(strFilePath, extPackFile, pParent);
     151            }
     152        }
     153        else
     154            vboxProblem().badExtPackFile(strFilePath, extPackFile, pParent);
     155    }
     156    else
     157        vboxProblem().cannotOpenExtPack(strFilePath, manager, pParent);
     158
     159    return fInstalled;
     160}
     161
    106162/* Load data to cashe from corresponding external object(s),
    107163 * this task COULD be performed in other than GUI thread: */
     
    208264        extensions << QString("*.%1").arg(VBoxDefs::VBoxExtPackFileExts[i]);
    209265    QString strFilter = tr("Extension package files (%1)").arg(extensions.join(" "));
     266
    210267    /* Create open file dialog: */
    211268    QStringList fileNames = QIFileDialog::getOpenFileNames(strBaseFolder, strFilter, this, strTitle, 0, true, true);
     
    213270        strFilePath = fileNames.at(0);
    214271
    215     /* Install chosen package: */
     272    /*
     273     * Install chosen package.
     274     */
    216275    if (!strFilePath.isEmpty())
    217276    {
    218         /* Get package manager: */
    219         CExtPackManager manager = vboxGlobal().virtualBox().GetExtensionPackManager();
    220         /* Install package & get the name of newly installed package: */
    221         QString strAddedPackageName = manager.Install(strFilePath);
    222         if (manager.isOk())
     277        QString strExtPackName;
     278        if (doInstallation(strFilePath, this, &strExtPackName))
    223279        {
    224             /* Get the newly added package: */
    225             const CExtPack &package = manager.Find(strAddedPackageName);
    226             /* Add newly added package into cache: */
     280            /*
     281             * Insert the fresly installed extension pack, mark it as
     282             * current in the tree and sort by name (col 1).
     283             */
     284            CExtPackManager manager = vboxGlobal().virtualBox().GetExtensionPackManager();
     285            const CExtPack &package = manager.Find(strExtPackName);
    227286            m_cache.m_items << fetchData(package);
    228             /* Add newly added package into tree: */
    229287            UIExtensionPackageItem *pItem = new UIExtensionPackageItem(m_pPackagesTree, m_cache.m_items.last());
    230             /* Set the newly created item as current: */
    231288            m_pPackagesTree->setCurrentItem(pItem);
    232             /* Sort the tree by <name> column: */
    233289            m_pPackagesTree->sortByColumn(1, Qt::AscendingOrder);
    234290        }
    235         else vboxProblem().cannotInstallExtPack(strFilePath, manager, this);
    236291    }
    237292}
  • trunk/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsExtension.h

    r34543 r34787  
    5151    UIGlobalSettingsExtension();
    5252
     53    static bool doInstallation(QString const &strFilePath, QWidget *pParent, QString *pstrExtPackName);
     54
    5355protected:
    5456
  • trunk/src/VBox/Main/ExtPackManagerImpl.cpp

    r34730 r34787  
    8888    /** The file handle of the extension pack file. */
    8989    RTFILE              hExtPackFile;
     90    /** Pointer to the extension pack manager. */
     91    ComObjPtr<ExtPackManager> ptrExtPackMgr;
     92
     93    RTMEMEF_NEW_AND_DELETE_OPERATORS();
    9094};
    9195
     
    119123    /** Set if we've made the pfnVirtualBoxReady or pfnConsoleReady call. */
    120124    bool                fMadeReadyCall;
     125
     126    RTMEMEF_NEW_AND_DELETE_OPERATORS();
    121127};
    122128
     
    143149    /** The current context. */
    144150    VBOXEXTPACKCTX      enmContext;
     151
     152    RTMEMEF_NEW_AND_DELETE_OPERATORS();
    145153};
    146154
     
    166174 * @returns COM status code.
    167175 * @param   a_pszFile       The path to the extension pack file.
    168  */
    169 HRESULT ExtPackFile::initWithFile(const char *a_pszFile)
     176 * @param   a_pExtPackMgr   Pointer to the extension pack manager.
     177 */
     178HRESULT ExtPackFile::initWithFile(const char *a_pszFile, ExtPackManager *a_pExtPackMgr)
    170179{
    171180    AutoInitSpan autoInitSpan(this);
     
    181190    m->strWhyUnusable               = tr("ExtPack::init failed");
    182191    m->strExtPackFile               = a_pszFile;
    183 
    184     /*
    185      * Probe the extension pack (this code is shared with refresh()).
    186      */
     192    m->hExtPackFile                 = NIL_RTFILE;
     193    m->ptrExtPackMgr                = a_pExtPackMgr;
     194
     195    iprt::MiniString *pstrTarName = VBoxExtPackExtractNameFromTarballPath(a_pszFile);
     196    if (pstrTarName)
     197    {
     198        m->Desc.strName = *pstrTarName;
     199        delete pstrTarName;
     200        pstrTarName = NULL;
     201    }
    187202
    188203    autoInitSpan.setSucceeded();
     204
     205    /*
     206     * Try open the extension pack and check that it is a regular file.
     207     */
     208    int vrc = RTFileOpen(&m->hExtPackFile, a_pszFile,
     209                         RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN);
     210    if (RT_FAILURE(vrc))
     211    {
     212        if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
     213            return initFailed(tr("'%s' file not found"), a_pszFile);
     214        return initFailed(tr("RTFileOpen('%s',,) failed with %Rrc"), a_pszFile, vrc);
     215    }
     216
     217    RTFSOBJINFO ObjInfo;
     218    vrc = RTFileQueryInfo(m->hExtPackFile, &ObjInfo, RTFSOBJATTRADD_UNIX);
     219    if (RT_FAILURE(vrc))
     220        return initFailed(tr("RTFileQueryInfo failed with %Rrc on '%s'"), vrc, a_pszFile);
     221    if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
     222        return initFailed(tr("Not a regular file: %s"), a_pszFile);
     223
     224    /*
     225     * Validate the tarball and extract the XML file.
     226     */
     227    char        szError[8192];
     228    RTVFSFILE   hXmlFile;
     229    vrc = VBoxExtPackValidateTarball(m->hExtPackFile, NULL /*pszExtPackName*/, a_pszFile,
     230                                     szError, sizeof(szError), NULL /*phValidManifest*/, &hXmlFile);
     231    if (RT_FAILURE(vrc))
     232        return initFailed(tr("%s"), szError);
     233
     234    /*
     235     * Parse the XML.
     236     */
     237    iprt::MiniString strSavedName(m->Desc.strName);
     238    iprt::MiniString *pStrLoadErr = VBoxExtPackLoadDescFromVfsFile(hXmlFile, &m->Desc, &m->ObjInfoDesc);
     239    RTVfsFileRelease(hXmlFile);
     240    if (pStrLoadErr != NULL)
     241    {
     242        m->strWhyUnusable.printf(tr("Failed to the xml file: %s"), pStrLoadErr->c_str());
     243        m->Desc.strName = strSavedName;
     244        delete pStrLoadErr;
     245        return S_OK;
     246    }
     247
     248    /*
     249     * Match the tarball name with the name from the XML.
     250     */
     251    /** @todo drop this restriction after the old install interface is
     252     *        dropped. */
     253    if (!strSavedName.equalsIgnoreCase(m->Desc.strName))
     254        return initFailed(tr("Extension pack name mismatch between the downloaded file and the XML inside it (xml='%s' file='%s')"),
     255                          m->Desc.strName.c_str(), strSavedName.c_str());
     256
     257    m->fUsable = true;
     258    m->strWhyUnusable.setNull();
     259    return S_OK;
     260}
     261
     262/**
     263 * Protected helper that formats the strExtPackFile value.
     264 *
     265 * @returns S_OK
     266 * @param   a_pszWhyFmt         Why it failed, format string.
     267 * @param   ...                 The format arguments.
     268 */
     269HRESULT ExtPackFile::initFailed(const char *a_pszWhyFmt, ...)
     270{
     271    va_list va;
     272    va_start(va, a_pszWhyFmt);
     273    m->strExtPackFile.printfV(a_pszWhyFmt, va);
     274    va_end(va);
    189275    return S_OK;
    190276}
     
    208294    {
    209295        VBoxExtPackFreeDesc(&m->Desc);
     296        RTFileClose(m->hExtPackFile);
     297        m->hExtPackFile = NIL_RTFILE;
    210298
    211299        delete m;
     
    326414STDMETHODIMP ExtPackFile::Install(void)
    327415{
    328     return E_NOTIMPL;
     416    AutoCaller autoCaller(this);
     417    HRESULT hrc = autoCaller.rc();
     418    if (SUCCEEDED(hrc))
     419    {
     420        if (m->fUsable)
     421            hrc = m->ptrExtPackMgr->doInstall(this);
     422        else
     423            hrc = setError(E_FAIL, "%s", m->strWhyUnusable.c_str());
     424    }
     425    return hrc;
    329426}
    330427
     
    14521549    /* else: ignore, the directory probably does not exist or something. */
    14531550
     1551#if 0
    14541552    /*
    14551553     * Look for things in the drop zone.
     
    14571555    if (SUCCEEDED(hrc) && a_fCheckDropZone)
    14581556        processDropZone();
     1557#else
     1558    NOREF(a_fCheckDropZone);
     1559#endif
    14591560
    14601561    if (SUCCEEDED(hrc))
     
    15341635    AssertReturn(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON, E_UNEXPECTED);
    15351636
    1536     return E_NOTIMPL;
    1537 }
    1538 
    1539 STDMETHODIMP ExtPackManager::Install(IN_BSTR a_bstrTarball, BSTR *a_pbstrName)
    1540 {
    1541     CheckComArgNotNull(a_bstrTarball);
    1542     CheckComArgOutPointerValid(a_pbstrName);
    1543     Utf8Str strTarball(a_bstrTarball);
    1544     AssertReturn(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON, E_UNEXPECTED);
    1545 
    1546     AutoCaller autoCaller(this);
    1547     HRESULT hrc = autoCaller.rc();
    1548     if (SUCCEEDED(hrc))
    1549     {
    1550         AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
    1551 
    1552         /*
    1553          * Check that the file exists and that we can access it.
    1554          */
    1555         if (RTFileExists(strTarball.c_str()))
    1556         {
    1557             RTFILE hFile;
    1558             int vrc = RTFileOpen(&hFile, strTarball.c_str(),
    1559                                  RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN | RTFILE_O_INHERIT);
    1560             if (RT_SUCCESS(vrc))
    1561             {
    1562                 RTFSOBJINFO ObjInfo;
    1563                 vrc = RTFileQueryInfo(hFile, &ObjInfo, RTFSOBJATTRADD_NOTHING);
    1564                 if (   RT_SUCCESS(vrc)
    1565                     && RTFS_IS_FILE(ObjInfo.Attr.fMode))
    1566                 {
    1567                     /*
    1568                      * Derive the name of the extension pack from the file
    1569                      * name, saving us the trouble of having to unpack it and
    1570                      * parse its XML to figure it out.   While this restricts
    1571                      * the filename a bit, it also forces it to be clearer, so
    1572                      * it shouldn't really be a problem.
    1573                      */
    1574                     iprt::MiniString *pStrName = VBoxExtPackExtractNameFromTarballPath(strTarball.c_str());
    1575                     if (pStrName)
    1576                     {
    1577                         /*
    1578                          * Refresh the data we have on the extension pack as it
    1579                          * may be made stale by direct meddling or some other user.
    1580                          */
    1581                         ExtPack *pExtPack;
    1582                         hrc = refreshExtPack(pStrName->c_str(), false /*a_fUnsuableIsError*/, &pExtPack);
    1583                         if (SUCCEEDED(hrc) && !pExtPack)
    1584                         {
    1585                             /*
    1586                              * Run the set-uid-to-root binary that performs the actual
    1587                              * installation.  Then create an object for the packet (we
    1588                              * do this even on failure, to be on the safe side).
    1589                              */
    1590                             char szTarballFd[64];
    1591                             RTStrPrintf(szTarballFd, sizeof(szTarballFd), "0x%RX64",
    1592                                         (uint64_t)RTFileToNative(hFile));
    1593 
    1594                             hrc = runSetUidToRootHelper("install",
    1595                                                         "--base-dir",   m->strBaseDir.c_str(),
    1596                                                         "--cert-dir",   m->strCertificatDirPath.c_str(),
    1597                                                         "--name",       pStrName->c_str(),
    1598                                                         "--tarball",    strTarball.c_str(),
    1599 #ifndef RT_OS_WINDOWS /* Not possible since the app might be launched by some unrelated service. */
    1600                                                         "--tarball-fd", &szTarballFd[0],
    1601 #endif
    1602                                                         NULL);
    1603                             RTFileClose(hFile); hFile = NIL_RTFILE;
    1604                             if (SUCCEEDED(hrc))
    1605                             {
    1606                                 hrc = refreshExtPack(pStrName->c_str(), true /*a_fUnsuableIsError*/, &pExtPack);
    1607                                 if (SUCCEEDED(hrc))
    1608                                 {
    1609                                     LogRel(("ExtPackManager: Successfully installed extension pack '%s'.\n", pStrName->c_str()));
    1610                                     pExtPack->callInstalledHook(m->pVirtualBox, &autoLock);
    1611 
    1612                                     /*
    1613                                      * Finally, return the name.
    1614                                      */
    1615                                     hrc = pExtPack->COMGETTER(Name)(a_pbstrName);
    1616                                 }
    1617                             }
    1618                             else
    1619                             {
    1620                                 ErrorInfoKeeper Eik;
    1621                                 refreshExtPack(pStrName->c_str(), false /*a_fUnsuableIsError*/, NULL);
    1622                             }
    1623                         }
    1624                         else if (SUCCEEDED(hrc))
    1625                             hrc = setError(E_FAIL,
    1626                                            tr("Extension pack '%s' is already installed."
    1627                                               " In case of a reinstallation, please uninstall it first"),
    1628                                            pStrName->c_str());
    1629                         delete pStrName;
    1630                     }
    1631                     else
    1632                         hrc = setError(E_FAIL, tr("Malformed '%s' file name"), strTarball.c_str());
    1633                 }
    1634                 else if (RT_SUCCESS(vrc))
    1635                     hrc = setError(E_FAIL, tr("'%s' is not a regular file"), strTarball.c_str());
    1636                 else
    1637                     hrc = setError(E_FAIL, tr("Failed to query info on '%s' (%Rrc)"), strTarball.c_str(), vrc);
    1638                 RTFileClose(hFile);
    1639             }
    1640             else
    1641                 hrc = setError(E_FAIL, tr("Failed to open '%s' (%Rrc)"), strTarball.c_str(), vrc);
    1642         }
    1643         else if (RTPathExists(strTarball.c_str()))
    1644             hrc = setError(E_FAIL, tr("'%s' is not a regular file"), strTarball.c_str());
    1645         else
    1646             hrc = setError(E_FAIL, tr("File '%s' was inaccessible or not found"), strTarball.c_str());
    1647 
    1648         /*
    1649          * Do VirtualBoxReady callbacks now for any freshly installed
    1650          * extension pack (old ones will not be called).
    1651          */
    1652         if (m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON)
    1653         {
    1654             autoLock.release();
    1655             callAllVirtualBoxReadyHooks();
    1656         }
    1657     }
     1637    ComObjPtr<ExtPackFile> NewExtPackFile;
     1638    HRESULT hrc = NewExtPackFile.createObject();
     1639    if (SUCCEEDED(hrc))
     1640        hrc = NewExtPackFile->initWithFile(strTarball.c_str(), this);
     1641    if (SUCCEEDED(hrc))
     1642        NewExtPackFile.queryInterfaceTo(a_ppExtPackFile);
    16581643
    16591644    return hrc;
     
    21572142}
    21582143
    2159 
     2144/**
     2145 * Worker for IExtPackFile::Install.
     2146 *
     2147 * @returns COM status code.
     2148 * @param   a_pExtPackFile  The extension pack file, caller checks that it's
     2149 *                          usable.
     2150 */
     2151HRESULT ExtPackManager::doInstall(ExtPackFile *a_pExtPackFile)
     2152{
     2153    AssertReturn(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON, E_UNEXPECTED);
     2154    iprt::MiniString const * const pStrName     = &a_pExtPackFile->m->Desc.strName;
     2155    iprt::MiniString const * const pStrTarball  = &a_pExtPackFile->m->strExtPackFile;
     2156
     2157    AutoCaller autoCaller(this);
     2158    HRESULT hrc = autoCaller.rc();
     2159    if (SUCCEEDED(hrc))
     2160    {
     2161        AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
     2162
     2163        /*
     2164         * Refresh the data we have on the extension pack as it
     2165         * may be made stale by direct meddling or some other user.
     2166         */
     2167        ExtPack *pExtPack;
     2168        hrc = refreshExtPack(pStrName->c_str(), false /*a_fUnsuableIsError*/, &pExtPack);
     2169        if (SUCCEEDED(hrc) && !pExtPack)
     2170        {
     2171            /*
     2172             * Run the privileged helper binary that performs the actual
     2173             * installation.  Then create an object for the packet (we do this
     2174             * even on failure, to be on the safe side).
     2175             */
     2176/** @todo add a hash (SHA-256) of the tarball or maybe just the manifest. */
     2177            hrc = runSetUidToRootHelper("install",
     2178                                        "--base-dir",   m->strBaseDir.c_str(),
     2179                                        "--cert-dir",   m->strCertificatDirPath.c_str(),
     2180                                        "--name",       pStrName->c_str(),
     2181                                        "--tarball",    pStrTarball->c_str(),
     2182                                        NULL);
     2183            if (SUCCEEDED(hrc))
     2184            {
     2185                hrc = refreshExtPack(pStrName->c_str(), true /*a_fUnsuableIsError*/, &pExtPack);
     2186                if (SUCCEEDED(hrc))
     2187                {
     2188                    LogRel(("ExtPackManager: Successfully installed extension pack '%s'.\n", pStrName->c_str()));
     2189                    pExtPack->callInstalledHook(m->pVirtualBox, &autoLock);
     2190                }
     2191            }
     2192            else
     2193            {
     2194                ErrorInfoKeeper Eik;
     2195                refreshExtPack(pStrName->c_str(), false /*a_fUnsuableIsError*/, NULL);
     2196            }
     2197        }
     2198        else if (SUCCEEDED(hrc))
     2199            hrc = setError(E_FAIL,
     2200                           tr("Extension pack '%s' is already installed."
     2201                              " In case of a reinstallation, please uninstall it first"),
     2202                           pStrName->c_str());
     2203
     2204        /*
     2205         * Do VirtualBoxReady callbacks now for any freshly installed
     2206         * extension pack (old ones will not be called).
     2207         */
     2208        if (m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON)
     2209        {
     2210            autoLock.release();
     2211            callAllVirtualBoxReadyHooks();
     2212        }
     2213    }
     2214
     2215    return hrc;
     2216}
     2217
     2218#if 0
    21602219/**
    21612220 * Processes anything new in the drop zone.
     
    22412300    RTDirClose(pDir);
    22422301}
     2302#endif
    22432303
    22442304
  • trunk/src/VBox/Main/ExtPackUtil.cpp

    r34579 r34787  
    2525#include <iprt/dir.h>
    2626#include <iprt/file.h>
     27#include <iprt/manifest.h>
    2728#include <iprt/param.h>
    2829#include <iprt/path.h>
    2930#include <iprt/string.h>
     31#include <iprt/vfs.h>
     32#include <iprt/tar.h>
     33#include <iprt/zip.h>
    3034#include <iprt/cpp/xml.h>
    3135
     
    5761
    5862/**
    59  * Reads the extension pack descriptor.
    60  *
    61  * @returns NULL on success, pointer to an error message on failure (caller
    62  *          deletes it).
    63  * @param   a_pszDir        The directory containing the description file.
    64  * @param   a_pExtPackDesc  Where to store the extension pack descriptor.
    65  * @param   a_pObjInfo      Where to store the object info for the file (unix
    66  *                          attribs). Optional.
    67  */
    68 iprt::MiniString *VBoxExtPackLoadDesc(const char *a_pszDir, PVBOXEXTPACKDESC a_pExtPackDesc, PRTFSOBJINFO a_pObjInfo)
    69 {
    70     /*
    71      * Clear the descriptor.
    72      */
     63 * Clears the extension pack descriptor.
     64 *
     65 * @param   a_pExtPackDesc  The descriptor to clear.
     66 */
     67static void vboxExtPackClearDesc(PVBOXEXTPACKDESC a_pExtPackDesc)
     68{
    7369    a_pExtPackDesc->strName.setNull();
    7470    a_pExtPackDesc->strDescription.setNull();
     
    7975    a_pExtPackDesc->cPlugIns = 0;
    8076    a_pExtPackDesc->paPlugIns = NULL;
    81 
    82     /*
    83      * Validate, open and parse the XML file.
    84      */
    85     char szFilePath[RTPATH_MAX];
    86     int vrc = RTPathJoin(szFilePath, sizeof(szFilePath), a_pszDir, VBOX_EXTPACK_DESCRIPTION_NAME);
    87     if (RT_FAILURE(vrc))
    88         return new iprt::MiniString("RTPathJoin failed with %Rrc", vrc);
    89 
    90     RTFSOBJINFO ObjInfo;
    91     vrc = RTPathQueryInfoEx(szFilePath, &ObjInfo,  RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
    92     if (RT_FAILURE(vrc))
    93         return &(new iprt::MiniString())->printf("RTPathQueryInfoEx failed with %Rrc", vrc);
    94     if (a_pObjInfo)
    95         *a_pObjInfo = ObjInfo;
    96     if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
    97     {
    98         if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
    99             return new iprt::MiniString("The XML file is symlinked, that is not allowed");
    100         return &(new iprt::MiniString)->printf("The XML file is not a file (fMode=%#x)", ObjInfo.Attr.fMode);
    101     }
    102 
    103     xml::Document       Doc;
    104     xml::XmlFileParser  Parser;
    105     try
    106     {
    107         Parser.read(szFilePath, Doc);
    108     }
    109     catch (xml::XmlError Err)
    110     {
    111         return new iprt::MiniString(Err.what());
    112     }
    113 
     77}
     78
     79/**
     80 * Load the extension pack descriptor from an XML document.
     81 *
     82 * @returns NULL on success, pointer to an error message on failure (caller
     83 *          deletes it).
     84 * @param   a_pDoc              Pointer to the the XML document.
     85 * @param   a_pExtPackDesc      Where to store the extension pack descriptor.
     86 */
     87static iprt::MiniString *vboxExtPackLoadDescFromDoc(xml::Document *a_pDoc, PVBOXEXTPACKDESC a_pExtPackDesc)
     88{
    11489    /*
    11590     * Get the main element and check its version.
    11691     */
    117     const xml::ElementNode *pVBoxExtPackElm = Doc.getRootElement();
     92    const xml::ElementNode *pVBoxExtPackElm = a_pDoc->getRootElement();
    11893    if (   !pVBoxExtPackElm
    11994        || strcmp(pVBoxExtPackElm->getName(), "VirtualBoxExtensionPack") != 0)
     
    209184}
    210185
     186/**
     187 * Reads the extension pack descriptor.
     188 *
     189 * @returns NULL on success, pointer to an error message on failure (caller
     190 *          deletes it).
     191 * @param   a_pszDir        The directory containing the description file.
     192 * @param   a_pExtPackDesc  Where to store the extension pack descriptor.
     193 * @param   a_pObjInfo      Where to store the object info for the file (unix
     194 *                          attribs). Optional.
     195 */
     196iprt::MiniString *VBoxExtPackLoadDesc(const char *a_pszDir, PVBOXEXTPACKDESC a_pExtPackDesc, PRTFSOBJINFO a_pObjInfo)
     197{
     198    vboxExtPackClearDesc(a_pExtPackDesc);
     199
     200    /*
     201     * Validate, open and parse the XML file.
     202     */
     203    char szFilePath[RTPATH_MAX];
     204    int vrc = RTPathJoin(szFilePath, sizeof(szFilePath), a_pszDir, VBOX_EXTPACK_DESCRIPTION_NAME);
     205    if (RT_FAILURE(vrc))
     206        return new iprt::MiniString("RTPathJoin failed with %Rrc", vrc);
     207
     208    RTFSOBJINFO ObjInfo;
     209    vrc = RTPathQueryInfoEx(szFilePath, &ObjInfo,  RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
     210    if (RT_FAILURE(vrc))
     211        return &(new iprt::MiniString())->printf("RTPathQueryInfoEx failed with %Rrc", vrc);
     212    if (a_pObjInfo)
     213        *a_pObjInfo = ObjInfo;
     214    if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
     215    {
     216        if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
     217            return new iprt::MiniString("The XML file is symlinked, that is not allowed");
     218        return &(new iprt::MiniString)->printf("The XML file is not a file (fMode=%#x)", ObjInfo.Attr.fMode);
     219    }
     220
     221    xml::Document       Doc;
     222    {
     223        xml::XmlFileParser  Parser;
     224        try
     225        {
     226            Parser.read(szFilePath, Doc);
     227        }
     228        catch (xml::XmlError Err)
     229        {
     230            return new iprt::MiniString(Err.what());
     231        }
     232    }
     233
     234    /*
     235     * Hand the xml doc over to the common code.
     236     */
     237    return vboxExtPackLoadDescFromDoc(&Doc, a_pExtPackDesc);
     238}
     239
     240/**
     241 * Reads the extension pack descriptor.
     242 *
     243 * @returns NULL on success, pointer to an error message on failure (caller
     244 *          deletes it).
     245 * @param   a_pszDir        The directory containing the description file.
     246 * @param   a_pExtPackDesc  Where to store the extension pack descriptor.
     247 * @param   a_pObjInfo      Where to store the object info for the file (unix
     248 *                          attribs). Optional.
     249 */
     250iprt::MiniString *VBoxExtPackLoadDescFromVfsFile(RTVFSFILE hVfsFile, PVBOXEXTPACKDESC a_pExtPackDesc, PRTFSOBJINFO a_pObjInfo)
     251{
     252    vboxExtPackClearDesc(a_pExtPackDesc);
     253
     254    /*
     255     * Query the object info.
     256     */
     257    RTFSOBJINFO ObjInfo;
     258    int rc = RTVfsFileQueryInfo(hVfsFile, &ObjInfo, RTFSOBJATTRADD_UNIX);
     259    if (RT_FAILURE(rc))
     260        return &(new iprt::MiniString)->printf("RTVfsFileQueryInfo failed: %Rrc", rc);
     261    if (a_pObjInfo)
     262        *a_pObjInfo = ObjInfo;
     263
     264    /*
     265     * The simple approach, read the whole thing into memory and pass this to
     266     * the XML parser.
     267     */
     268
     269    /* Check the file size. */
     270    if (ObjInfo.cbObject > _1M || ObjInfo.cbObject < 0)
     271        return &(new iprt::MiniString)->printf("The XML file is too large (%'RU64 bytes)", ObjInfo.cbObject);
     272    size_t const cbFile = (size_t)ObjInfo.cbObject;
     273
     274    /* Rewind to the start of the file. */
     275    rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
     276    if (RT_FAILURE(rc))
     277        return &(new iprt::MiniString)->printf("RTVfsFileSeek(,0,BEGIN) failed: %Rrc", rc);
     278
     279    /* Allocate memory and read the file content into it. */
     280    void *pvFile = RTMemTmpAlloc(cbFile);
     281    if (!pvFile)
     282        return &(new iprt::MiniString)->printf("RTMemTmpAlloc(%zu) failed", cbFile);
     283
     284    iprt::MiniString *pstrErr = NULL;
     285    rc = RTVfsFileRead(hVfsFile, pvFile, cbFile, NULL);
     286    if (RT_FAILURE(rc))
     287        pstrErr = &(new iprt::MiniString)->printf("RTVfsFileRead failed: %Rrc", rc);
     288
     289    /*
     290     * Parse the file.
     291     */
     292    xml::Document Doc;
     293    if (RT_SUCCESS(rc))
     294    {
     295        xml::XmlMemParser   Parser;
     296        iprt::MiniString    strFileName = VBOX_EXTPACK_DESCRIPTION_NAME;
     297        try
     298        {
     299            Parser.read(pvFile, cbFile, strFileName, Doc);
     300        }
     301        catch (xml::XmlError Err)
     302        {
     303            pstrErr = new iprt::MiniString(Err.what());
     304            rc = VERR_PARSE_ERROR;
     305        }
     306    }
     307    RTMemTmpFree(pvFile);
     308
     309    /*
     310     * Hand the xml doc over to the common code.
     311     */
     312    if (RT_SUCCESS(rc))
     313        pstrErr = vboxExtPackLoadDescFromDoc(&Doc, a_pExtPackDesc);
     314
     315    return pstrErr;
     316}
    211317
    212318/**
     
    232338}
    233339
    234 
    235340/**
    236341 * Extract the extension pack name from the tarball path.
     
    488593}
    489594
     595/**
     596 * RTStrPrintfv wrapper.
     597 *
     598 * @returns @a rc
     599 * @param   rc                  The status code to return.
     600 * @param   pszError            The error buffer.
     601 * @param   cbError             The size of the buffer.
     602 * @param   pszFormat           The error message format string.
     603 * @param   ...                 Format arguments.
     604 */
     605static int vboxExtPackReturnError(int rc, char *pszError, size_t cbError, const char *pszFormat, ...)
     606{
     607    va_list va;
     608    va_start(va, pszFormat);
     609    RTStrPrintfV(pszError, cbError, pszFormat, va);
     610    va_end(va);
     611    return rc;
     612}
     613
     614/**
     615 * RTStrPrintfv wrapper.
     616 *
     617 * @param   pszError            The error buffer.
     618 * @param   cbError             The size of the buffer.
     619 * @param   pszFormat           The error message format string.
     620 * @param   ...                 Format arguments.
     621 */
     622static void vboxExtPackSetError(char *pszError, size_t cbError, const char *pszFormat, ...)
     623{
     624    va_list va;
     625    va_start(va, pszFormat);
     626    RTStrPrintfV(pszError, cbError, pszFormat, va);
     627    va_end(va);
     628}
     629
     630/**
     631 * Verifies the manifest and its signature.
     632 *
     633 * @returns VBox status code, failures with message.
     634 * @param   hManifestFile       The xml from the extension pack.
     635 * @param   pszExtPackName      The expected extension pack name.  This can be
     636 *                              NULL, in which we don't have any expectations.
     637 * @param   pszError            Where to store an error message on failure.
     638 * @param   cbError             The size of the buffer @a pszError points to.
     639 */
     640static int vboxExtPackVerifyXml(RTVFSFILE hXmlFile, const char *pszExtPackName, char *pszError, size_t cbError)
     641{
     642    /*
     643     * Load the XML.
     644     */
     645    VBOXEXTPACKDESC     ExtPackDesc;
     646    iprt::MiniString   *pstrErr = VBoxExtPackLoadDescFromVfsFile(hXmlFile, &ExtPackDesc, NULL);
     647    if (pstrErr)
     648    {
     649        RTStrCopy(pszError, cbError, pstrErr->c_str());
     650        delete pstrErr;
     651        return VERR_PARSE_ERROR;
     652    }
     653
     654    /*
     655     * Check the name.
     656     */
     657    /** @todo drop this restriction after the old install interface is
     658     *        dropped. */
     659    int rc = VINF_SUCCESS;
     660    if (   pszExtPackName
     661        && !ExtPackDesc.strName.equalsIgnoreCase(pszExtPackName))
     662        rc = vboxExtPackReturnError(VERR_NOT_EQUAL, pszError, cbError,
     663                                    "The name of the downloaded file and the name stored inside the extension pack does not match"
     664                                    " (xml='%s' file='%s')", ExtPackDesc.strName.c_str(), pszExtPackName);
     665    return rc;
     666}
     667
     668/**
     669 * Verifies the manifest and its signature.
     670 *
     671 * @returns VBox status code, failures with message.
     672 * @param   hOurManifest    The manifest we compiled.
     673 * @param   hManifestFile   The manifest file in the extension pack.
     674 * @param   hSignatureFile  The manifest signature file.
     675 * @param   pszError            Where to store an error message on failure.
     676 * @param   cbError             The size of the buffer @a pszError points to.
     677 */
     678static int vboxExtPackVerifyManifestAndSignature(RTMANIFEST hOurManifest, RTVFSFILE hManifestFile, RTVFSFILE hSignatureFile,
     679                                                 char *pszError, size_t cbError)
     680{
     681    /*
     682     * Read the manifest from the extension pack.
     683     */
     684    int rc = RTVfsFileSeek(hManifestFile, 0, RTFILE_SEEK_BEGIN, NULL);
     685    if (RT_FAILURE(rc))
     686        return vboxExtPackReturnError(rc, pszError, cbError, "RTVfsFileSeek failed: %Rrc", rc);
     687
     688    RTMANIFEST hTheirManifest;
     689    rc = RTManifestCreate(0 /*fFlags*/, &hTheirManifest);
     690    if (RT_FAILURE(rc))
     691        return vboxExtPackReturnError(rc, pszError, cbError, "RTManifestCreate failed: %Rrc", rc);
     692
     693    RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hManifestFile);
     694    rc = RTManifestReadStandard(hTheirManifest, hVfsIos);
     695    RTVfsIoStrmRelease(hVfsIos);
     696    if (RT_SUCCESS(rc))
     697    {
     698        /*
     699         * Compare the manifests.
     700         */
     701        static const char *s_apszIgnoreEntries[] =
     702        {
     703            VBOX_EXTPACK_MANIFEST_NAME,
     704            VBOX_EXTPACK_SIGNATURE_NAME,
     705            "./" VBOX_EXTPACK_MANIFEST_NAME,
     706            "./" VBOX_EXTPACK_SIGNATURE_NAME,
     707            NULL
     708        };
     709        char szError[RTPATH_MAX];
     710        rc = RTManifestEqualsEx(hOurManifest, hTheirManifest, &s_apszIgnoreEntries[0], NULL,
     711                                RTMANIFEST_EQUALS_IGN_MISSING_ATTRS /*fFlags*/,
     712                                szError, sizeof(szError));
     713        if (RT_SUCCESS(rc))
     714        {
     715            /*
     716             * Validate the manifest file signature.
     717             */
     718            /** @todo implement signature stuff */
     719            NOREF(hSignatureFile);
     720
     721        }
     722        else if (rc == VERR_NOT_EQUAL && szError[0])
     723            vboxExtPackSetError(pszError, cbError, "Manifest mismatch: %s", szError);
     724        else
     725            vboxExtPackSetError(pszError, cbError, "RTManifestEqualsEx failed: %Rrc", rc);
     726#if 0
     727        RTVFSIOSTREAM hVfsIosStdOut = NIL_RTVFSIOSTREAM;
     728        RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, RTFILE_O_WRITE, true, &hVfsIosStdOut);
     729        RTVfsIoStrmWrite(hVfsIosStdOut, "Our:\n", sizeof("Our:\n") - 1, true, NULL);
     730        RTManifestWriteStandard(hOurManifest, hVfsIosStdOut);
     731        RTVfsIoStrmWrite(hVfsIosStdOut, "Their:\n", sizeof("Their:\n") - 1, true, NULL);
     732        RTManifestWriteStandard(hTheirManifest, hVfsIosStdOut);
     733#endif
     734    }
     735    else
     736        vboxExtPackSetError(pszError, cbError, "Error parsing '%s': %Rrc", VBOX_EXTPACK_MANIFEST_NAME, rc);
     737
     738    RTManifestRelease(hTheirManifest);
     739    return rc;
     740}
     741
     742
     743/**
     744 * Validates a name in an extension pack.
     745 *
     746 * We restrict the charset to try make sure the extension pack can be unpacked
     747 * on all file systems.
     748 *
     749 * @returns VBox status code, failures with message.
     750 * @param   pszName             The name to validate.
     751 * @param   pszError            Where to store an error message on failure.
     752 * @param   cbError             The size of the buffer @a pszError points to.
     753 */
     754static int vboxExtPackValidateMemberName(const char *pszName, char *pszError, size_t cbError)
     755{
     756    if (RTPathStartsWithRoot(pszName))
     757        return vboxExtPackReturnError(VERR_PATH_IS_NOT_RELATIVE, pszError, cbError, "'%s': starts with root spec", pszName);
     758
     759    const char *pszErr = NULL;
     760    const char *psz = pszName;
     761    int ch;
     762    while ((ch = *psz) != '\0')
     763    {
     764        /* Character set restrictions. */
     765        if (ch < 0 || ch >= 128)
     766        {
     767            pszErr = "Only 7-bit ASCII allowed";
     768            break;
     769        }
     770        if (ch <= 31 || ch == 127)
     771        {
     772            pszErr = "No control characters are not allowed";
     773            break;
     774        }
     775        if (ch == '\\')
     776        {
     777            pszErr = "Only backward slashes are not allowed";
     778            break;
     779        }
     780        if (strchr("'\":;*?|[]<>(){}", ch))
     781        {
     782            pszErr = "The characters ', \", :, ;, *, ?, |, [, ], <, >, (, ), { and } are not allowed";
     783            break;
     784        }
     785
     786        /* Take the simple way out and ban all ".." sequences. */
     787        if (   ch     == '.'
     788            && psz[1] == '.')
     789        {
     790            pszErr = "Double dot sequence are not allowed";
     791            break;
     792        }
     793
     794        /* Keep the tree shallow or the hardening checks will fail. */
     795        if (psz - pszName > VBOX_EXTPACK_MAX_MEMBER_NAME_LENGTH)
     796        {
     797            pszErr = "Too long";
     798            break;
     799        }
     800
     801        /* advance */
     802        psz++;
     803    }
     804
     805    if (pszErr)
     806        return vboxExtPackReturnError(VERR_INVALID_NAME, pszError, cbError,
     807                                      "Bad member name '%s' (pos %zu): %s", pszName, (size_t)(psz - pszName), pszErr);
     808    return RTEXITCODE_SUCCESS;
     809}
     810
     811
     812/**
     813 * Validates a file in an extension pack.
     814 *
     815 * @returns VBox status code, failures with message.
     816 * @param   pszName             The name of the file.
     817 * @param   hVfsObj             The VFS object.
     818 * @param   pszError            Where to store an error message on failure.
     819 * @param   cbError             The size of the buffer @a pszError points to.
     820 */
     821static int vboxExtPackValidateMemberFile(const char *pszName, RTVFSOBJ hVfsObj, char *pszError, size_t cbError)
     822{
     823    int rc = vboxExtPackValidateMemberName(pszName, pszError, cbError);
     824    if (RT_SUCCESS(rc))
     825    {
     826        RTFSOBJINFO ObjInfo;
     827        rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
     828        if (RT_SUCCESS(rc))
     829        {
     830            if (ObjInfo.cbObject >= 9*_1G64)
     831                rc = vboxExtPackReturnError(VERR_OUT_OF_RANGE, pszError, cbError,
     832                                            "'%s': too large (%'RU64 bytes)",
     833                                            pszName, (uint64_t)ObjInfo.cbObject);
     834            if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
     835                rc = vboxExtPackReturnError(VERR_NOT_A_FILE, pszError, cbError,
     836                                            "The alleged file '%s' has a mode mask stating otherwise (%RTfmode)",
     837                                            pszName, ObjInfo.Attr.fMode);
     838        }
     839        else
     840            vboxExtPackSetError(pszError, cbError, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszName, rc);
     841    }
     842    return rc;
     843}
     844
     845
     846/**
     847 * Validates a directory in an extension pack.
     848 *
     849 * @returns VBox status code, failures with message.
     850 * @param   pszName             The name of the directory.
     851 * @param   hVfsObj             The VFS object.
     852 * @param   pszError            Where to store an error message on failure.
     853 * @param   cbError             The size of the buffer @a pszError points to.
     854 */
     855static int vboxExtPackValidateMemberDir(const char *pszName, RTVFSOBJ hVfsObj, char *pszError, size_t cbError)
     856{
     857    int rc = vboxExtPackValidateMemberName(pszName, pszError, cbError);
     858    if (RT_SUCCESS(rc))
     859    {
     860        RTFSOBJINFO ObjInfo;
     861        rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
     862        if (RT_SUCCESS(rc))
     863        {
     864            if (!RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
     865                rc = vboxExtPackReturnError(VERR_NOT_A_DIRECTORY, pszError, cbError,
     866                                            "The alleged directory '%s' has a mode mask saying differently (%RTfmode)",
     867                                            pszName, ObjInfo.Attr.fMode);
     868        }
     869        else
     870            vboxExtPackSetError(pszError, cbError, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszName, rc);
     871    }
     872    return rc;
     873}
     874
     875/**
     876 * Validates a member of an extension pack.
     877 *
     878 * @returns VBox status code, failures with message.
     879 * @param   pszName             The name of the directory.
     880 * @param   enmType             The object type.
     881 * @param   hVfsObj             The VFS object.
     882 * @param   pszError            Where to store an error message on failure.
     883 * @param   cbError             The size of the buffer @a pszError points to.
     884 */
     885int VBoxExtPackValidateMember(const char *pszName, RTVFSOBJTYPE enmType, RTVFSOBJ hVfsObj, char *pszError, size_t cbError)
     886{
     887    Assert(cbError > 0);
     888    *pszError = '\0';
     889
     890    int rc;
     891    if (   enmType == RTVFSOBJTYPE_FILE
     892        || enmType == RTVFSOBJTYPE_IO_STREAM)
     893        rc = vboxExtPackValidateMemberFile(pszName, hVfsObj, pszError, cbError);
     894    else if (   enmType == RTVFSOBJTYPE_DIR
     895             || enmType == RTVFSOBJTYPE_BASE)
     896        rc = vboxExtPackValidateMemberDir(pszName, hVfsObj, pszError, cbError);
     897    else
     898        rc = vboxExtPackReturnError(VERR_UNEXPECTED_FS_OBJ_TYPE, pszError, cbError,
     899                                    "'%s' is not a file or directory (enmType=%d)", pszName, enmType);
     900    return rc;
     901}
     902
     903
     904/**
     905 * Rewinds the tarball file handle and creates a gunzip | tar chain that
     906 * results in a filesystem stream.
     907 *
     908 * @returns VBox status code, failures with message.
     909 * @param   hTarballFile        The handle to the tarball file.
     910 * @param   pszError            Where to store an error message on failure.
     911 * @param   cbError             The size of the buffer @a pszError points to.
     912 * @param   phTarFss            Where to return the filesystem stream handle.
     913 */
     914int VBoxExtPackOpenTarFss(RTFILE hTarballFile, char *pszError, size_t cbError, PRTVFSFSSTREAM phTarFss)
     915{
     916    Assert(cbError > 0);
     917    *pszError = '\0';
     918    *phTarFss = NIL_RTVFSFSSTREAM;
     919
     920    /*
     921     * Rewind the file and set up a VFS chain for it.
     922     */
     923    int rc = RTFileSeek(hTarballFile, 0, RTFILE_SEEK_BEGIN, NULL);
     924    if (RT_FAILURE(rc))
     925        return vboxExtPackReturnError(rc, pszError, cbError, "Failed seeking to the start of the tarball: %Rrc", rc);
     926
     927    RTVFSIOSTREAM hTarballIos;
     928    rc = RTVfsIoStrmFromRTFile(hTarballFile, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN, true /*fLeaveOpen*/,
     929                               &hTarballIos);
     930    if (RT_FAILURE(rc))
     931        return vboxExtPackReturnError(rc, pszError, cbError, "RTVfsIoStrmFromRTFile failed: %Rrc", rc);
     932
     933    RTVFSIOSTREAM hGunzipIos;
     934    rc = RTZipGzipDecompressIoStream(hTarballIos, 0 /*fFlags*/, &hGunzipIos);
     935    if (RT_SUCCESS(rc))
     936    {
     937        RTVFSFSSTREAM hTarFss;
     938        rc = RTZipTarFsStreamFromIoStream(hGunzipIos, 0 /*fFlags*/, &hTarFss);
     939        if (RT_SUCCESS(rc))
     940        {
     941            RTVfsIoStrmRelease(hGunzipIos);
     942            RTVfsIoStrmRelease(hTarballIos);
     943            *phTarFss = hTarFss;
     944            return VINF_SUCCESS;
     945        }
     946        vboxExtPackSetError(pszError, cbError, "RTZipTarFsStreamFromIoStream failed: %Rrc", rc);
     947        RTVfsIoStrmRelease(hGunzipIos);
     948    }
     949    else
     950        vboxExtPackSetError(pszError, cbError, "RTZipGzipDecompressIoStream failed: %Rrc", rc);
     951    RTVfsIoStrmRelease(hTarballIos);
     952    return rc;
     953}
     954
     955
     956/**
     957 * Validates the extension pack tarball prior to unpacking.
     958 *
     959 * Operations performed:
     960 *      - Mandatory files.
     961 *      - Manifest check.
     962 *      - Manifest seal check.
     963 *      - XML check, match name.
     964 *
     965 * @returns VBox status code, failures with message.
     966 * @param   hTarballFile        The handle to open the @a pszTarball file.
     967 * @param   pszExtPackName      The name of the extension pack name.  NULL if
     968 *                              the name is not fixed.
     969 * @param   pszTarball          The name of the tarball in case we have to
     970 *                              complain about something.
     971 * @param   pszError            Where to store an error message on failure.
     972 * @param   cbError             The size of the buffer @a pszError points to.
     973 * @param   phValidManifest     Where to optionally return the handle to fully
     974 *                              validated the manifest for the extension pack.
     975 *                              This includes all files.
     976 * @param   phXmlFile           Where to optionally return the memorized XML
     977 *                              file.
     978 *
     979 * @todo    This function is a bit too long and should be split up if possible.
     980 */
     981int VBoxExtPackValidateTarball(RTFILE hTarballFile, const char *pszExtPackName, const char *pszTarball,
     982                               char *pszError, size_t cbError, PRTMANIFEST phValidManifest, PRTVFSFILE phXmlFile)
     983{
     984    /*
     985     * Clear return values.
     986     */
     987    if (phValidManifest)
     988        *phValidManifest = NIL_RTMANIFEST;
     989    if (phXmlFile)
     990        *phXmlFile = NIL_RTVFSFILE;
     991    Assert(cbError > 1);
     992    *pszError = '\0';
     993    NOREF(pszTarball);
     994
     995    /*
     996     * Open the tar.gz filesystem stream and set up an manifest in-memory file.
     997     */
     998    RTVFSFSSTREAM hTarFss;
     999    int rc = VBoxExtPackOpenTarFss(hTarballFile, pszError, cbError, &hTarFss);
     1000    if (RT_FAILURE(rc))
     1001        return rc;
     1002
     1003    RTMANIFEST hOurManifest;
     1004    rc = RTManifestCreate(0 /*fFlags*/, &hOurManifest);
     1005    if (RT_SUCCESS(rc))
     1006    {
     1007        /*
     1008         * Process the tarball (would be nice to move this to a function).
     1009         */
     1010        RTVFSFILE hXmlFile      = NIL_RTVFSFILE;
     1011        RTVFSFILE hManifestFile = NIL_RTVFSFILE;
     1012        RTVFSFILE hSignatureFile= NIL_RTVFSFILE;
     1013        for (;;)
     1014        {
     1015            /*
     1016             * Get the next stream object.
     1017             */
     1018            char           *pszName;
     1019            RTVFSOBJ        hVfsObj;
     1020            RTVFSOBJTYPE    enmType;
     1021            rc = RTVfsFsStrmNext(hTarFss, &pszName, &enmType, &hVfsObj);
     1022            if (RT_FAILURE(rc))
     1023            {
     1024                if (rc != VERR_EOF)
     1025                    vboxExtPackSetError(pszError, cbError, "RTVfsFsStrmNext failed: %Rrc", rc);
     1026                else
     1027                    rc = VINF_SUCCESS;
     1028                break;
     1029            }
     1030            const char     *pszAdjName = pszName[0] == '.' && pszName[1] == '/' ? &pszName[2] : pszName;
     1031
     1032            /*
     1033             * Check the type & name validity.
     1034             *
     1035             * N.B. We will always reach the end of the loop before breaking on
     1036             *      failure - cleanup reasons.
     1037             */
     1038            rc = VBoxExtPackValidateMember(pszName, enmType, hVfsObj, pszError, cbError);
     1039            if (RT_SUCCESS(rc))
     1040            {
     1041                /*
     1042                 * Check if this is one of the standard files.
     1043                 */
     1044                PRTVFSFILE phVfsFile;
     1045                if (!strcmp(pszAdjName, VBOX_EXTPACK_DESCRIPTION_NAME))
     1046                    phVfsFile = &hXmlFile;
     1047                else if (!strcmp(pszAdjName, VBOX_EXTPACK_MANIFEST_NAME))
     1048                    phVfsFile = &hManifestFile;
     1049                else if (!strcmp(pszAdjName, VBOX_EXTPACK_SIGNATURE_NAME))
     1050                    phVfsFile = &hSignatureFile;
     1051                else
     1052                    phVfsFile = NULL;
     1053                if (phVfsFile)
     1054                {
     1055                    /*
     1056                     * Make sure it's a file and that it isn't too large.
     1057                     */
     1058                    if (*phVfsFile != NIL_RTVFSFILE)
     1059                        rc = vboxExtPackReturnError(VERR_DUPLICATE, pszError, cbError,
     1060                                                    "There can only be one '%s'", pszAdjName);
     1061                    else if (enmType != RTVFSOBJTYPE_IO_STREAM && enmType != RTVFSOBJTYPE_FILE)
     1062                        rc = vboxExtPackReturnError(VERR_NOT_A_FILE, pszError, cbError,
     1063                                                    "Standard member '%s' is not a file", pszAdjName);
     1064                    else
     1065                    {
     1066                        RTFSOBJINFO ObjInfo;
     1067                        rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
     1068                        if (RT_SUCCESS(rc))
     1069                        {
     1070                            if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
     1071                                rc = vboxExtPackReturnError(VERR_NOT_A_FILE, pszError, cbError,
     1072                                                            "Standard member '%s' is not a file", pszAdjName);
     1073                            else if (ObjInfo.cbObject >= _1M)
     1074                                rc = vboxExtPackReturnError(VERR_OUT_OF_RANGE, pszError, cbError,
     1075                                                            "Standard member '%s' is too large: %'RU64 bytes (max 1 MB)",
     1076                                                            pszAdjName, (uint64_t)ObjInfo.cbObject);
     1077                            else
     1078                            {
     1079                                /*
     1080                                 * Make an in memory copy of the stream.
     1081                                 */
     1082                                RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
     1083                                rc = RTVfsMemorizeIoStreamAsFile(hVfsIos, RTFILE_O_READ, phVfsFile);
     1084                                if (RT_SUCCESS(rc))
     1085                                {
     1086                                    /*
     1087                                     * To simplify the code below, replace
     1088                                     * hVfsObj with the memorized file.
     1089                                     */
     1090                                    RTVfsObjRelease(hVfsObj);
     1091                                    hVfsObj = RTVfsObjFromFile(*phVfsFile);
     1092                                }
     1093                                else
     1094                                    vboxExtPackSetError(pszError, cbError, "RTVfsMemorizeIoStreamAsFile failed on '%s': %Rrc", pszName, rc);
     1095                                RTVfsIoStrmRelease(hVfsIos);
     1096                            }
     1097                        }
     1098                        else
     1099                            vboxExtPackSetError(pszError, cbError, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszName, rc);
     1100                    }
     1101                }
     1102            }
     1103
     1104            /*
     1105             * Add any I/O stream to the manifest
     1106             */
     1107            if (   RT_SUCCESS(rc)
     1108                && (   enmType == RTVFSOBJTYPE_FILE
     1109                    || enmType == RTVFSOBJTYPE_IO_STREAM))
     1110            {
     1111                RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
     1112                rc = RTManifestEntryAddIoStream(hOurManifest, hVfsIos, pszAdjName, RTMANIFEST_ATTR_SIZE | RTMANIFEST_ATTR_SHA256);
     1113                if (RT_FAILURE(rc))
     1114                    vboxExtPackSetError(pszError, cbError, "RTManifestEntryAddIoStream failed on '%s': %Rrc", pszAdjName, rc);
     1115                RTVfsIoStrmRelease(hVfsIos);
     1116            }
     1117
     1118            /*
     1119             * Clean up and break out on failure.
     1120             */
     1121            RTVfsObjRelease(hVfsObj);
     1122            RTStrFree(pszName);
     1123            if (RT_FAILURE(rc))
     1124                break;
     1125        }
     1126
     1127        /*
     1128         * If we've successfully processed the tarball, verify that the
     1129         * mandatory files are present.
     1130         */
     1131        if (RT_SUCCESS(rc))
     1132        {
     1133            if (hXmlFile == NIL_RTVFSFILE)
     1134                rc = vboxExtPackReturnError(VERR_MISSING, pszError, cbError, "Mandator file '%s' is missing", VBOX_EXTPACK_DESCRIPTION_NAME);
     1135            if (hManifestFile == NIL_RTVFSFILE)
     1136                rc = vboxExtPackReturnError(VERR_MISSING, pszError, cbError, "Mandator file '%s' is missing", VBOX_EXTPACK_MANIFEST_NAME);
     1137            if (hSignatureFile == NIL_RTVFSFILE)
     1138                rc = vboxExtPackReturnError(VERR_MISSING, pszError, cbError, "Mandator file '%s' is missing", VBOX_EXTPACK_SIGNATURE_NAME);
     1139        }
     1140
     1141        /*
     1142         * Check the manifest and it's signature.
     1143         */
     1144        if (RT_SUCCESS(rc))
     1145            rc = vboxExtPackVerifyManifestAndSignature(hOurManifest, hManifestFile, hSignatureFile, pszError, cbError);
     1146
     1147        /*
     1148         * Check the XML.
     1149         */
     1150        if (RT_SUCCESS(rc))
     1151            rc = vboxExtPackVerifyXml(hXmlFile, pszExtPackName, pszError, cbError);
     1152
     1153        /*
     1154         * Returns objects.
     1155         */
     1156        if (RT_SUCCESS(rc))
     1157        {
     1158            if (phValidManifest)
     1159            {
     1160                RTManifestRetain(hOurManifest);
     1161                *phValidManifest = hOurManifest;
     1162            }
     1163            if (phXmlFile)
     1164            {
     1165                RTVfsFileRetain(hXmlFile);
     1166                *phXmlFile = hXmlFile;
     1167            }
     1168        }
     1169
     1170        /*
     1171         * Release our object references.
     1172         */
     1173        RTManifestRelease(hOurManifest);
     1174        RTVfsFileRelease(hXmlFile);
     1175        RTVfsFileRelease(hManifestFile);
     1176        RTVfsFileRelease(hSignatureFile);
     1177    }
     1178    else
     1179        vboxExtPackSetError(pszError, cbError, "RTManifestCreate failed: %Rrc", rc);
     1180    RTVfsFsStrmRelease(hTarFss);
     1181
     1182    return rc;
     1183}
     1184
  • trunk/src/VBox/Main/VBoxExtPackHelperApp.cpp

    r34762 r34787  
    7878#endif
    7979
    80 /** The maximum entry name length.
    81  * Play short and safe. */
    82 #define VBOX_EXTPACK_MAX_ENTRY_NAME_LENGTH      128
    8380
    8481
     
    202199
    203200/**
    204  * Rewinds the tarball file handle and creates a gunzip | tar chain that
    205  * results in a filesystem stream.
     201 * Wrapper around VBoxExtPackOpenTarFss.
    206202 *
    207203 * @returns success or failure, message displayed on failure.
     
    211207static RTEXITCODE OpenTarFss(RTFILE hTarballFile, PRTVFSFSSTREAM phTarFss)
    212208{
    213     /*
    214      * Rewind the file and set up a VFS chain for it.
    215      */
    216     int rc = RTFileSeek(hTarballFile, 0, RTFILE_SEEK_BEGIN, NULL);
    217     if (RT_FAILURE(rc))
    218         return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed seeking to the start of the tarball: %Rrc\n", rc);
    219 
    220     RTVFSIOSTREAM hTarballIos;
    221     rc = RTVfsIoStrmFromRTFile(hTarballFile, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN, true /*fLeaveOpen*/,
    222                                &hTarballIos);
    223     if (RT_FAILURE(rc))
    224         return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsIoStrmFromRTFile failed: %Rrc\n", rc);
    225 
    226     RTVFSIOSTREAM hGunzipIos;
    227     rc = RTZipGzipDecompressIoStream(hTarballIos, 0 /*fFlags*/, &hGunzipIos);
    228     if (RT_SUCCESS(rc))
    229     {
    230         RTVFSFSSTREAM hTarFss;
    231         rc = RTZipTarFsStreamFromIoStream(hGunzipIos, 0 /*fFlags*/, &hTarFss);
    232         if (RT_SUCCESS(rc))
    233         {
    234             RTVfsIoStrmRelease(hGunzipIos);
    235             RTVfsIoStrmRelease(hTarballIos);
    236             *phTarFss = hTarFss;
    237             return RTEXITCODE_SUCCESS;
    238         }
    239         RTMsgError("RTZipTarFsStreamFromIoStream failed: %Rrc\n", rc);
    240         RTVfsIoStrmRelease(hGunzipIos);
    241     }
    242     else
    243         RTMsgError("RTZipGzipDecompressIoStream failed: %Rrc\n", rc);
    244     RTVfsIoStrmRelease(hTarballIos);
    245     return RTEXITCODE_FAILURE;
     209    char szError[8192];
     210    int rc = VBoxExtPackOpenTarFss(hTarballFile, szError, sizeof(szError), phTarFss);
     211    if (RT_FAILURE(rc))
     212    {
     213        Assert(szError[0]);
     214        return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s", szError);
     215    }
     216    Assert(!szError[0]);
     217    return RTEXITCODE_SUCCESS;
    246218}
    247219
     
    273245
    274246/**
    275  * Verifies the manifest and its signature.
    276  *
    277  * @returns Program exit code, failure with message.
    278  * @param   hManifestFile   The xml from the extension pack.
    279  * @param   pszExtPackName  The expected extension pack name.
    280  */
    281 static RTEXITCODE VerifyXml(RTVFSFILE hXmlFile, const char *pszExtPackName)
    282 {
    283     /** @todo implement XML verification. */
    284     return RTEXITCODE_SUCCESS;
    285 }
    286 
    287 
    288 /**
    289  * Verifies the manifest and its signature.
    290  *
    291  * @returns Program exit code, failure with message.
    292  * @param   hOurManifest    The manifest we compiled.
    293  * @param   hManifestFile   The manifest file in the extension pack.
    294  * @param   hSignatureFile  The manifest signature file.
    295  */
    296 static RTEXITCODE VerifyManifestAndSignature(RTMANIFEST hOurManifest, RTVFSFILE hManifestFile, RTVFSFILE hSignatureFile)
    297 {
    298     /*
    299      * Read the manifest from the extension pack.
    300      */
    301     int rc = RTVfsFileSeek(hManifestFile, 0, RTFILE_SEEK_BEGIN, NULL);
    302     if (RT_FAILURE(rc))
    303         return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsFileSeek failed: %Rrc", rc);
    304 
    305     RTMANIFEST hTheirManifest;
    306     rc = RTManifestCreate(0 /*fFlags*/, &hTheirManifest);
    307     if (RT_FAILURE(rc))
    308         return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTManifestCreate failed: %Rrc", rc);
    309 
    310     RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hManifestFile);
    311     rc = RTManifestReadStandard(hTheirManifest, hVfsIos);
    312     RTVfsIoStrmRelease(hVfsIos);
    313     if (RT_SUCCESS(rc))
    314     {
    315         /*
    316          * Compare the manifests.
    317          */
    318         static const char *s_apszIgnoreEntries[] =
    319         {
    320             VBOX_EXTPACK_MANIFEST_NAME,
    321             VBOX_EXTPACK_SIGNATURE_NAME,
    322             "./" VBOX_EXTPACK_MANIFEST_NAME,
    323             "./" VBOX_EXTPACK_SIGNATURE_NAME,
    324             NULL
    325         };
    326         char szError[RTPATH_MAX];
    327         rc = RTManifestEqualsEx(hOurManifest, hTheirManifest, &s_apszIgnoreEntries[0], NULL,
    328                                 RTMANIFEST_EQUALS_IGN_MISSING_ATTRS /*fFlags*/,
    329                                 szError, sizeof(szError));
    330         if (RT_SUCCESS(rc))
    331         {
    332             /*
    333              * Validate the manifest file signature.
    334              */
    335             /** @todo implement signature stuff */
    336 
    337         }
    338         else if (rc == VERR_NOT_EQUAL && szError[0])
    339             RTMsgError("Manifest mismatch: %s", szError);
    340         else
    341             RTMsgError("RTManifestEqualsEx failed: %Rrc", rc);
    342 #if 0
    343         RTVFSIOSTREAM hVfsIosStdOut = NIL_RTVFSIOSTREAM;
    344         RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, RTFILE_O_WRITE, true, &hVfsIosStdOut);
    345         RTVfsIoStrmWrite(hVfsIosStdOut, "Our:\n", sizeof("Our:\n") - 1, true, NULL);
    346         RTManifestWriteStandard(hOurManifest, hVfsIosStdOut);
    347         RTVfsIoStrmWrite(hVfsIosStdOut, "Their:\n", sizeof("Their:\n") - 1, true, NULL);
    348         RTManifestWriteStandard(hTheirManifest, hVfsIosStdOut);
    349 #endif
    350     }
    351     else
    352         RTMsgError("Error parsing '%s': %Rrc", VBOX_EXTPACK_MANIFEST_NAME, rc);
    353 
    354     RTManifestRelease(hTheirManifest);
    355     return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
    356 }
    357 
    358 
    359 /**
    360  * Validates a name in an extension pack.
    361  *
    362  * We restrict the charset to try make sure the extension pack can be unpacked
    363  * on all file systems.
    364  *
    365  * @returns Program exit code, failure with message.
    366  * @param   pszName             The name to validate.
    367  */
    368 static RTEXITCODE ValidateNameInExtPack(const char *pszName)
    369 {
    370     if (RTPathStartsWithRoot(pszName))
    371         return RTMsgErrorExit(RTEXITCODE_FAILURE, "'%s': starts with root spec", pszName);
    372 
    373     const char *pszErr = NULL;
    374     const char *psz = pszName;
    375     int ch;
    376     while ((ch = *psz) != '\0')
    377     {
    378         /* Character set restrictions. */
    379         if (ch < 0 || ch >= 128)
    380         {
    381             pszErr = "Only 7-bit ASCII allowed";
    382             break;
    383         }
    384         if (ch <= 31 || ch == 127)
    385         {
    386             pszErr = "No control characters are not allowed";
    387             break;
    388         }
    389         if (ch == '\\')
    390         {
    391             pszErr = "Only backward slashes are not allowed";
    392             break;
    393         }
    394         if (strchr("'\":;*?|[]<>(){}", ch))
    395         {
    396             pszErr = "The characters ', \", :, ;, *, ?, |, [, ], <, >, (, ), { and } are not allowed";
    397             break;
    398         }
    399 
    400         /* Take the simple way out and ban all ".." sequences. */
    401         if (   ch     == '.'
    402             && psz[1] == '.')
    403         {
    404             pszErr = "Double dot sequence are not allowed";
    405             break;
    406         }
    407 
    408         /* Keep the tree shallow or the hardening checks will fail. */
    409         if (psz - pszName > VBOX_EXTPACK_MAX_ENTRY_NAME_LENGTH)
    410         {
    411             pszErr = "Too long";
    412             break;
    413         }
    414 
    415         /* advance */
    416         psz++;
    417     }
    418 
    419     if (pszErr)
    420         return RTMsgErrorExit(RTEXITCODE_FAILURE, "Bad member name '%s' (pos %zu): %s", pszName, (size_t)(psz - pszName), pszErr);
    421     return RTEXITCODE_SUCCESS;
    422 }
    423 
    424 
    425 /**
    426  * Validates a file in an extension pack.
    427  *
    428  * @returns Program exit code, failure with message.
    429  * @param   pszName             The name of the file.
    430  * @param   hVfsObj             The VFS object.
    431  */
    432 static RTEXITCODE ValidateFileInExtPack(const char *pszName, RTVFSOBJ hVfsObj)
    433 {
    434     RTEXITCODE rcExit = ValidateNameInExtPack(pszName);
    435     if (rcExit == RTEXITCODE_SUCCESS)
    436     {
    437         RTFSOBJINFO ObjInfo;
    438         int rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
    439         if (RT_SUCCESS(rc))
    440         {
    441             if (ObjInfo.cbObject >= 9*_1G64)
    442                 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "'%s': too large (%'RU64 bytes)",
    443                                         pszName, (uint64_t)ObjInfo.cbObject);
    444             if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
    445                 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
    446                                         "The alleged file '%s' has a mode mask saying differently (%RTfmode)",
    447                                         pszName, ObjInfo.Attr.fMode);
    448         }
    449         else
    450             rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszName, rc);
    451     }
    452     return rcExit;
    453 }
    454 
    455 
    456 /**
    457  * Validates a directory in an extension pack.
    458  *
    459  * @returns Program exit code, failure with message.
    460  * @param   pszName             The name of the directory.
    461  * @param   hVfsObj             The VFS object.
    462  */
    463 static RTEXITCODE ValidateDirInExtPack(const char *pszName, RTVFSOBJ hVfsObj)
    464 {
    465     RTEXITCODE rcExit = ValidateNameInExtPack(pszName);
    466     if (rcExit == RTEXITCODE_SUCCESS)
    467     {
    468         RTFSOBJINFO ObjInfo;
    469         int rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
    470         if (RT_SUCCESS(rc))
    471         {
    472             if (!RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
    473                 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
    474                                         "The alleged directory '%s' has a mode mask saying differently (%RTfmode)",
    475                                         pszName, ObjInfo.Attr.fMode);
    476         }
    477         else
    478             rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszName, rc);
    479     }
    480     return rcExit;
    481 }
    482 
    483 
    484 /**
    485  * Validates a member of an extension pack.
     247 * Wrapper around VBoxExtPackValidateMember.
    486248 *
    487249 * @returns Program exit code, failure with message.
     
    492254static RTEXITCODE ValidateMemberOfExtPack(const char *pszName, RTVFSOBJTYPE enmType, RTVFSOBJ hVfsObj)
    493255{
    494     RTEXITCODE rcExit;
    495     if (   enmType == RTVFSOBJTYPE_FILE
    496         || enmType == RTVFSOBJTYPE_IO_STREAM)
    497         rcExit = ValidateFileInExtPack(pszName, hVfsObj);
    498     else if (   enmType == RTVFSOBJTYPE_DIR
    499              || enmType == RTVFSOBJTYPE_BASE)
    500         rcExit = ValidateDirInExtPack(pszName, hVfsObj);
    501     else
    502         rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "'%s' is not a file or directory (enmType=%d)", pszName, enmType);
    503     return rcExit;
     256    char szError[8192];
     257    int rc = VBoxExtPackValidateMember(pszName, enmType, hVfsObj, szError, sizeof(szError));
     258    if (RT_FAILURE(rc))
     259    {
     260        Assert(szError[0]);
     261        return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s", szError);
     262    }
     263    Assert(!szError[0]);
     264    return RTEXITCODE_SUCCESS;
    504265}
    505266
     
    657418     */
    658419    char szDstPath[RTPATH_MAX];
    659     int rc = RTPathAbs(pszDirDst, szDstPath, sizeof(szDstPath) - VBOX_EXTPACK_MAX_ENTRY_NAME_LENGTH - 2);
     420    int rc = RTPathAbs(pszDirDst, szDstPath, sizeof(szDstPath) - VBOX_EXTPACK_MAX_MEMBER_NAME_LENGTH - 2);
    660421    if (RT_FAILURE(rc))
    661422        return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs('%s',,) failed: %Rrc", pszDirDst, rc);
     
    763524
    764525/**
    765  * Validates the extension pack tarball prior to unpacking.
    766  *
    767  * Operations performed:
    768  *      - Mandatory files.
    769  *      - Manifest check.
    770  *      - Manifest seal check.
    771  *      - XML check, match name.
     526 * Wrapper around VBoxExtPackValidateTarball.
    772527 *
    773528 * @returns The program exit code.
     
    779534 *                              the manifest for the extension pack.  This
    780535 *                              includes all files.
    781  *
    782  * @todo    This function is a bit too long and should be split up if possible.
    783536 */
    784537static RTEXITCODE ValidateExtPackTarball(RTFILE hTarballFile, const char *pszExtPackName, const char *pszTarball,
     
    788541    RTMsgInfo("Validating extension pack '%s' ('%s')...", pszTarball, pszExtPackName);
    789542
    790     /*
    791      * Open the tar.gz filesystem stream and set up an manifest in-memory file.
    792      */
    793     RTVFSFSSTREAM hTarFss;
    794     RTEXITCODE rcExit = OpenTarFss(hTarballFile, &hTarFss);
    795     if (rcExit != RTEXITCODE_SUCCESS)
    796         return rcExit;
    797 
    798     RTMANIFEST hOurManifest;
    799     int rc = RTManifestCreate(0 /*fFlags*/, &hOurManifest);
    800     if (RT_SUCCESS(rc))
    801     {
    802         /*
    803          * Process the tarball (would be nice to move this to a function).
    804          */
    805         RTVFSFILE hXmlFile      = NIL_RTVFSFILE;
    806         RTVFSFILE hManifestFile = NIL_RTVFSFILE;
    807         RTVFSFILE hSignatureFile= NIL_RTVFSFILE;
    808         for (;;)
    809         {
    810             /*
    811              * Get the next stream object.
    812              */
    813             char           *pszName;
    814             RTVFSOBJ        hVfsObj;
    815             RTVFSOBJTYPE    enmType;
    816             rc = RTVfsFsStrmNext(hTarFss, &pszName, &enmType, &hVfsObj);
    817             if (RT_FAILURE(rc))
    818             {
    819                 if (rc != VERR_EOF)
    820                     rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsFsStrmNext failed: %Rrc", rc);
    821                 break;
    822             }
    823             const char     *pszAdjName = pszName[0] == '.' && pszName[1] == '/' ? &pszName[2] : pszName;
    824 
    825             /*
    826              * Check the type & name validity.
    827              */
    828             rcExit = ValidateMemberOfExtPack(pszName, enmType, hVfsObj);
    829             if (rcExit == RTEXITCODE_SUCCESS)
    830             {
    831                 /*
    832                  * Check if this is one of the standard files.
    833                  */
    834                 PRTVFSFILE  phVfsFile;
    835                 if (!strcmp(pszAdjName, VBOX_EXTPACK_DESCRIPTION_NAME))
    836                     phVfsFile = &hXmlFile;
    837                 else if (!strcmp(pszAdjName, VBOX_EXTPACK_MANIFEST_NAME))
    838                     phVfsFile = &hManifestFile;
    839                 else if (!strcmp(pszAdjName, VBOX_EXTPACK_SIGNATURE_NAME))
    840                     phVfsFile = &hSignatureFile;
    841                 else
    842                     phVfsFile = NULL;
    843                 if (phVfsFile)
    844                 {
    845                     /*
    846                      * Make sure it's a file and that it isn't too large.
    847                      */
    848                     if (*phVfsFile != NIL_RTVFSFILE)
    849                         rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "There can only be one '%s'", pszAdjName);
    850                     else if (enmType != RTVFSOBJTYPE_IO_STREAM && enmType != RTVFSOBJTYPE_FILE)
    851                         rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Standard member '%s' is not a file", pszAdjName);
    852                     else
    853                     {
    854                         RTFSOBJINFO ObjInfo;
    855                         rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
    856                         if (RT_SUCCESS(rc))
    857                         {
    858                             if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
    859                                 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Standard member '%s' is not a file", pszAdjName);
    860                             else if (ObjInfo.cbObject >= _1M)
    861                                 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
    862                                                         "Standard member '%s' is too large: %'RU64 bytes (max 1 MB)",
    863                                                         pszAdjName, (uint64_t)ObjInfo.cbObject);
    864                             else
    865                             {
    866                                 /*
    867                                  * Make an in memory copy of the stream.
    868                                  */
    869                                 RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
    870                                 rc = RTVfsMemorizeIoStreamAsFile(hVfsIos, RTFILE_O_READ, phVfsFile);
    871                                 if (RT_SUCCESS(rc))
    872                                 {
    873                                     /*
    874                                      * To simplify the code below, replace
    875                                      * hVfsObj with the memorized file.
    876                                      */
    877                                     RTVfsObjRelease(hVfsObj);
    878                                     hVfsObj = RTVfsObjFromFile(*phVfsFile);
    879                                 }
    880                                 else
    881                                     rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
    882                                                             "RTVfsMemorizeIoStreamAsFile failed on '%s': %Rrc", pszName, rc);
    883                                 RTVfsIoStrmRelease(hVfsIos);
    884                             }
    885                         }
    886                         else
    887                             rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszName, rc);
    888                     }
    889                 }
    890             }
    891 
    892             /*
    893              * Add any I/O stream to the manifest
    894              */
    895             if (   rcExit == RTEXITCODE_SUCCESS
    896                 && (   enmType == RTVFSOBJTYPE_FILE
    897                     || enmType == RTVFSOBJTYPE_IO_STREAM))
    898             {
    899                 RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
    900                 rc = RTManifestEntryAddIoStream(hOurManifest, hVfsIos, pszAdjName, RTMANIFEST_ATTR_SIZE | RTMANIFEST_ATTR_SHA256);
    901                 if (RT_FAILURE(rc))
    902                     rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTManifestEntryAddIoStream failed on '%s': %Rrc", pszAdjName, rc);
    903                 RTVfsIoStrmRelease(hVfsIos);
    904             }
    905 
    906             /*
    907              * Clean up and break out on failure.
    908              */
    909             RTVfsObjRelease(hVfsObj);
    910             RTStrFree(pszName);
    911             if (rcExit != RTEXITCODE_SUCCESS)
    912                 break;
    913         }
    914 
    915         /*
    916          * If we've successfully processed the tarball, verify that the
    917          * mandatory files are present.
    918          */
    919         if (rcExit == RTEXITCODE_SUCCESS)
    920         {
    921             if (hXmlFile == NIL_RTVFSFILE)
    922                 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Mandator file '%s' is missing", VBOX_EXTPACK_DESCRIPTION_NAME);
    923             if (hManifestFile == NIL_RTVFSFILE)
    924                 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Mandator file '%s' is missing", VBOX_EXTPACK_MANIFEST_NAME);
    925             if (hSignatureFile == NIL_RTVFSFILE)
    926                 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Mandator file '%s' is missing", VBOX_EXTPACK_SIGNATURE_NAME);
    927         }
    928 
    929         /*
    930          * Check the manifest and it's signature.
    931          */
    932         if (rcExit == RTEXITCODE_SUCCESS)
    933             rcExit = VerifyManifestAndSignature(hOurManifest, hManifestFile, hSignatureFile);
    934 
    935         /*
    936          * Check the XML.
    937          */
    938         if (rcExit == RTEXITCODE_SUCCESS)
    939             rcExit = VerifyXml(hXmlFile, pszExtPackName);
    940 
    941         /*
    942          * Release objects and stuff.
    943          */
    944         if (rcExit == RTEXITCODE_SUCCESS)
    945             *phValidManifest = hOurManifest;
    946         else
    947             RTManifestRelease(hOurManifest);
    948 
    949         RTVfsFileRelease(hXmlFile);
    950         RTVfsFileRelease(hManifestFile);
    951         RTVfsFileRelease(hSignatureFile);
    952     }
    953     RTVfsFsStrmRelease(hTarFss);
    954 
    955     return rcExit;
     543    char szError[8192];
     544    int rc = VBoxExtPackValidateTarball(hTarballFile, pszExtPackName, pszTarball,
     545                                        szError, sizeof(szError), phValidManifest, NULL /*phXmlFile*/);
     546    if (RT_FAILURE(rc))
     547    {
     548        Assert(szError[0]);
     549        return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s", szError);
     550    }
     551    Assert(!szError[0]);
     552    return RTEXITCODE_SUCCESS;
    956553}
    957554
  • trunk/src/VBox/Main/idl/VirtualBox.xidl

    r34714 r34787  
    1443214432  <interface
    1443314433    name="IExtPackManager" extends="$unknown"
    14434     uuid="8104df65-74d6-4e33-b374-50aa062b4afd"
     14434    uuid="70d808a9-176f-4d45-adad-7c509b8309b3"
    1443514435    wsmap="suppress"
    1443614436    >
     
    1447414474      <param name="file" type="IExtPackFile" dir="return">
    1447514475        <desc>The interface of the extension pack file object.</desc>
    14476       </param>
    14477     </method>
    14478 
    14479     <method name="install">
    14480       <desc>
    14481         Please use openExtPackFile + IExtPackFile::install instead of this
    14482         interface.   This will be removed later in the beta cycle.
    14483       </desc>
    14484       <param name="path" type="wstring" dir="in">
    14485         <desc>The path of the extension pack tarball.</desc>
    14486       </param>
    14487       <param name="name" type="wstring" dir="return">
    14488         <desc>The name of the installed extension pack.</desc>
    1448914476      </param>
    1449014477    </method>
  • trunk/src/VBox/Main/include/ExtPackManagerImpl.h

    r34714 r34787  
    4646    HRESULT     FinalConstruct();
    4747    void        FinalRelease();
    48     HRESULT     initWithFile(const char *a_pszFile);
     48    HRESULT     initWithFile(const char *a_pszFile, class ExtPackManager *a_pExtPackMgr);
    4949    void        uninit();
     50    RTMEMEF_NEW_AND_DELETE_OPERATORS();
    5051    /** @}  */
    5152
     
    6768    STDMETHOD(Install)(void);
    6869    /** @}  */
     70
     71private:
     72    /** @name Misc init helpers
     73     * @{ */
     74    HRESULT     initFailed(const char *a_pszWhyFmt, ...);
     75    /** @} */
    6976
    7077private:
     
    102109    HRESULT     initWithDir(VBOXEXTPACKCTX a_enmContext, const char *a_pszName, const char *a_pszDir);
    103110    void        uninit();
     111    RTMEMEF_NEW_AND_DELETE_OPERATORS();
    104112    /** @}  */
    105113
     
    188196                     VBOXEXTPACKCTX a_enmContext);
    189197    void        uninit();
     198    RTMEMEF_NEW_AND_DELETE_OPERATORS();
    190199    /** @}  */
    191200
     
    195204    STDMETHOD(Find)(IN_BSTR a_bstrName, IExtPack **a_pExtPack);
    196205    STDMETHOD(OpenExtPackFile)(IN_BSTR a_bstrTarball, IExtPackFile **a_ppExtPackFile);
    197     STDMETHOD(Install)(IN_BSTR a_bstrTarball, BSTR *a_pbstrName);
    198206    STDMETHOD(Uninstall)(IN_BSTR a_bstrName, BOOL a_fForcedRemoval);
    199207    STDMETHOD(Cleanup)(void);
     
    203211    /** @name Internal interfaces used by other Main classes.
    204212     * @{ */
    205     void        processDropZone(void);
     213    HRESULT     doInstall(ExtPackFile *a_pExtPackFile);
     214    /*void        processDropZone(void);*/
    206215    void        callAllVirtualBoxReadyHooks(void);
    207216    void        callAllConsoleReadyHooks(IConsole *a_pConsole);
  • trunk/src/VBox/Main/include/ExtPackUtil.h

    r34579 r34787  
    2121#include <iprt/cpp/ministring.h>
    2222#include <iprt/fs.h>
     23#include <iprt/vfs.h>
    2324
    2425
     
    4748 * certificates are installed.  Relative to RTPathAppPrivateNoArch. */
    4849#define VBOX_EXTPACK_CERT_DIR           "ExtPackCertificates"
     50
     51/** The maximum entry name length.
     52 * Play short and safe. */
     53#define VBOX_EXTPACK_MAX_MEMBER_NAME_LENGTH 128
    4954
    5055
     
    98103
    99104iprt::MiniString   *VBoxExtPackLoadDesc(const char *a_pszDir, PVBOXEXTPACKDESC a_pExtPackDesc, PRTFSOBJINFO a_pObjInfo);
     105iprt::MiniString   *VBoxExtPackLoadDescFromVfsFile(RTVFSFILE hVfsFile, PVBOXEXTPACKDESC a_pExtPackDesc, PRTFSOBJINFO a_pObjInfo);
    100106iprt::MiniString   *VBoxExtPackExtractNameFromTarballPath(const char *pszTarball);
    101107void                VBoxExtPackFreeDesc(PVBOXEXTPACKDESC a_pExtPackDesc);
     
    108114bool                VBoxExtPackIsValidModuleString(const char *pszModule);
    109115
     116int                 VBoxExtPackValidateMember(const char *pszName, RTVFSOBJTYPE enmType, RTVFSOBJ hVfsObj, char *pszError, size_t cbError);
     117int                 VBoxExtPackOpenTarFss(RTFILE hTarballFile, char *pszError, size_t cbError, PRTVFSFSSTREAM phTarFss);
     118int                 VBoxExtPackValidateTarball(RTFILE hTarballFile, const char *pszExtPackName, const char *pszTarball,
     119                                               char *pszError, size_t cbError, PRTMANIFEST phValidManifest, PRTVFSFILE phXmlFile);
     120
    110121
    111122#endif
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette