VBoxExtPackHelperApp.cpp revision 939e2ecb812c6402abcc63e7d615c5444acfd02e
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * VirtualBox Main - Extension Pack Helper Application, usually set-uid-to-root.
f8e804dad2cc0262b6384e97c12be107cf7e19e0vboxsync * Copyright (C) 2010-2011 Oracle Corporation
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * available from http://www.virtualbox.org. This file is free software;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * you can redistribute it and/or modify it under the terms of the GNU
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * General Public License (GPL) as published by the Free Software
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/*******************************************************************************
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync* Header Files *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync*******************************************************************************/
ae5379e3e7573369566d4628ef6c597da693cc55vboxsync/*******************************************************************************
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync* Defined Constants And Macros *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync*******************************************************************************/
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** Enable elevation on Windows and Darwin. */
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync/** @name Command and option names
54d3b0107d9bf326fe6e0de92e012c791dbb1587vboxsync/*******************************************************************************
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync* Global Variables *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync*******************************************************************************/
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/* Override RTAssertShouldPanic to prevent gdb process creation. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return true;
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * Handle the special standard options when these are specified after the
77cce7691847be5aef145f31ba3f9d66fc2cf594vboxsync * @param ch The option character.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTMsgInfo(VBOX_PRODUCT " Extension Pack Helper App\n"
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync "All rights reserved.\n"
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync "This NOT intended for general use, please use VBoxManage instead\n"
9e293277b378073ce86910209a246b744b4caa2cvboxsync "or call the IExtPackManager API directly.\n"
9e293277b378073ce86910209a246b744b4caa2cvboxsync "Usage: %s <command> [options]\n"
9e293277b378073ce86910209a246b744b4caa2cvboxsync "Commands:\n"
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync " install --base-dir <dir> --cert-dir <dir> --name <name> \\\n"
9e293277b378073ce86910209a246b744b4caa2cvboxsync " --tarball <tarball> --tarball-fd <fd>\n"
aabfd650b7e36e2c25a6d48b2a2bfd95fe93b65avboxsync " uninstall --base-dir <dir> --name <name>\n"
aabfd650b7e36e2c25a6d48b2a2bfd95fe93b65avboxsync " cleanup --base-dir <dir>\n"
bdd15592ca3578b623ff588055a561f58b7e5586vboxsync RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Checks if the cerficiate directory is valid.
f350b7cf96f1e2f3b0cfd34cfe8726c754f43584vboxsync * @returns true if it is valid, false if it isn't.
f350b7cf96f1e2f3b0cfd34cfe8726c754f43584vboxsync * @param pszCertDir The certificate directory to validate.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic bool IsValidCertificateDir(const char *pszCertDir)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Just be darn strict for now.
551d9b8ee3568ad3e11b65ce6ef2867c36375f37vboxsync int rc = RTPathAppPrivateNoArch(szCorrect, sizeof(szCorrect));
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync return false;
b8908d384db2324f04a2f68a13e67ea32ebf609avboxsync rc = RTPathAppend(szCorrect, sizeof(szCorrect), VBOX_EXTPACK_CERT_DIR);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return false;
8e0c2ca3abd721979958f95b9af73b60665478c8vboxsync * Checks if the base directory is valid.
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * @returns true if it is valid, false if it isn't.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszBaesDir The base directory to validate.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Just be darn strict for now.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc = RTPathAppPrivateArchTop(szCorrect, sizeof(szCorrect));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return false;
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync rc = RTPathAppend(szCorrect, sizeof(szCorrect), VBOX_EXTPACK_INSTALL_DIR);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return false;
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * Cleans up a temporary extension pack directory.
39592d8ff3243f6116c4e99be391bcf30a4ad187vboxsync * This is used by 'uninstall', 'cleanup' and in the failure path of 'install'.
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * @returns The program exit code.
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * @param pszDir The directory to clean up. The caller is
39592d8ff3243f6116c4e99be391bcf30a4ad187vboxsync * responsible for making sure this is valid.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param fTemporary Whether this is a temporary install directory or
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic RTEXITCODE RemoveExtPackDir(const char *pszDir, bool fTemporary)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** @todo May have to undo 555 modes here later. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc = RTDirRemoveRecursive(pszDir, RTDIRRMREC_F_CONTENT_AND_DIR);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync "Failed to delete the %sextension pack directory: %Rrc ('%s')",
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Common uninstall worker used by both uninstall and install --replace.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns success or failure, message displayed on failure.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszExtPackDir The extension pack directory name.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic RTEXITCODE CommonUninstallWorker(const char *pszExtPackDir)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /* Rename the extension pack directory before deleting it to prevent new
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VM processes from picking it up. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc = RTStrCopy(szExtPackUnInstDir, sizeof(szExtPackUnInstDir), pszExtPackDir);
c77e7bff89c7639353778366984d51ff165ea0e3vboxsync rc = RTStrCat(szExtPackUnInstDir, sizeof(szExtPackUnInstDir), "-_-uninst");
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to construct temporary extension pack path: %Rrc", rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTDirRename(pszExtPackDir, szExtPackUnInstDir, RTPATHRENAME_FLAGS_NO_REPLACE);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to rename the extension pack directory: %Rrc", rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /* Recursively delete the directory content. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RemoveExtPackDir(szExtPackUnInstDir, false /*fTemporary*/);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Wrapper around VBoxExtPackOpenTarFss.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns success or failure, message displayed on failure.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param hTarballFile The handle to the tarball file.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param phTarFss Where to return the filesystem stream handle.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic RTEXITCODE OpenTarFss(RTFILE hTarballFile, PRTVFSFSSTREAM phTarFss)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc = VBoxExtPackOpenTarFss(hTarballFile, szError, sizeof(szError), phTarFss);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s", szError);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Sets the permissions of the temporary extension pack directory just before
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * renaming it.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * By default the temporary directory is only accessible by root, this function
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * will make it world readable and browseable.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns The program exit code.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszDir The temporary extension pack directory.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic RTEXITCODE SetExtPackPermissions(const char *pszDir)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to set directory permissions: %Rrc ('%s')", rc, pszDir);
f5eadb22976c1f9813300e4042b8255cfaef7e19vboxsync /** @todo */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Wrapper around VBoxExtPackValidateMember.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns Program exit code, failure with message.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszName The name of the directory.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param enmType The object type.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param hVfsObj The VFS object.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic RTEXITCODE ValidateMemberOfExtPack(const char *pszName, RTVFSOBJTYPE enmType, RTVFSOBJ hVfsObj)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc = VBoxExtPackValidateMember(pszName, enmType, hVfsObj, szError, sizeof(szError));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s", szError);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Validates the extension pack tarball prior to unpacking.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Operations performed:
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * - Hardening checks.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns The program exit code.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszDir The directory where the extension pack has been
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * unpacked.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszExtPackName The expected extension pack name.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszTarball The name of the tarball in case we have to
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * complain about something.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic RTEXITCODE ValidateUnpackedExtPack(const char *pszDir, const char *pszTarball, const char *pszExtPackName)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTMsgInfo("Validating unpacked extension pack...");
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc = SUPR3HardenedVerifyDir(pszDir, true /*fRecursive*/, true /*fCheckFiles*/, &ErrInfo.Core);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Hardening check failed with %Rrc: %s", rc, ErrInfo.Core.pszMsg);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Unpacks a directory from an extension pack tarball.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns Program exit code, failure with message.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszDstDirName The name of the unpacked directory.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param hVfsObj The source object for the directory.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic RTEXITCODE UnpackExtPackDir(const char *pszDstDirName, RTVFSOBJ hVfsObj)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Get the mode mask before creating the directory.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszDstDirName, rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync ObjInfo.Attr.fMode &= ~(RTFS_UNIX_IWOTH | RTFS_UNIX_IWGRP);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTDirCreate(pszDstDirName, ObjInfo.Attr.fMode, 0);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create directory '%s': %Rrc", pszDstDirName, rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Because of umask, we have to apply the mode again.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTPathSetMode(pszDstDirName, ObjInfo.Attr.fMode);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to set directory permissions on '%s': %Rrc", pszDstDirName, rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** @todo Ownership tricks on windows? */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Unpacks a file from an extension pack tarball.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns Program exit code, failure with message.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszName The name in the tarball.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszDstFilename The name of the unpacked file.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param hVfsIosSrc The source stream for the file.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param hUnpackManifest The manifest to add the file digest to.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic RTEXITCODE UnpackExtPackFile(const char *pszName, const char *pszDstFilename,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTVFSIOSTREAM hVfsIosSrc, RTMANIFEST hUnpackManifest)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Query the object info, we'll need it for buffer sizing as well as
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * setting the file mode.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc = RTVfsIoStrmQueryInfo(hVfsIosSrc, &ObjInfo, RTFSOBJATTRADD_NOTHING);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsIoStrmQueryInfo failed with %Rrc on '%s'", rc, pszDstFilename);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Create the file.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint32_t fFlags = RTFILE_O_WRITE | RTFILE_O_DENY_ALL | RTFILE_O_CREATE | (0600 << RTFILE_O_CREATE_MODE_SHIFT);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create '%s': %Rrc", pszDstFilename, rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Create a I/O stream for the destination file, stack a manifest entry
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * creator on top of it.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTVfsIoStrmFromRTFile(hFile, fFlags, true /*fLeaveOpen*/, &hVfsIosDst2);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTManifestEntryAddPassthruIoStream(hUnpackManifest, hVfsIosDst2, pszName,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Pump the data thru.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, (uint32_t)RT_MIN(ObjInfo.cbObject, _1G));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Set the mode mask.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync ObjInfo.Attr.fMode &= ~(RTFS_UNIX_IWOTH | RTFS_UNIX_IWGRP);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** @todo Windows needs to do more here, I think. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTMsgError("Failed to set the mode of '%s' to %RTfmode: %Rrc", pszDstFilename, ObjInfo.Attr.fMode, rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTMsgError("RTManifestPtIosAddEntryNow failed for '%s': %Rrc", pszDstFilename, rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTMsgError("RTVfsUtilPumpIoStreams failed for '%s': %Rrc", pszDstFilename, rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTMsgError("RTManifestEntryAddPassthruIoStream failed: %Rrc", rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTMsgError("RTVfsIoStrmFromRTFile failed: %Rrc", rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Unpacks the extension pack into the specified directory.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * This will apply ownership and permission changes to all the content, the
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * exception is @a pszDirDst which will be handled by SetExtPackPermissions.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns The program exit code.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param hTarballFile The tarball to unpack.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszDirDst Where to unpack it.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param hValidManifest The manifest we've validated.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszTarball The name of the tarball in case we have to
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * complain about something.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic RTEXITCODE UnpackExtPack(RTFILE hTarballFile, const char *pszDirDst, RTMANIFEST hValidManifest,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTMsgInfo("Unpacking extension pack into '%s'...", pszDirDst);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Set up the destination path.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc = RTPathAbs(pszDirDst, szDstPath, sizeof(szDstPath) - VBOX_EXTPACK_MAX_MEMBER_NAME_LENGTH - 2);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs('%s',,) failed: %Rrc", pszDirDst, rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync size_t offDstPath = RTPathStripTrailingSlash(szDstPath);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Open the tar.gz filesystem stream and set up an manifest in-memory file.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTEXITCODE rcExit = OpenTarFss(hTarballFile, &hTarFss);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTManifestCreate(0 /*fFlags*/, &hUnpackManifest);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Process the tarball (would be nice to move this to a function).
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Get the next stream object.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTVfsFsStrmNext(hTarFss, &pszName, &enmType, &hVfsObj);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsFsStrmNext failed: %Rrc", rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync const char *pszAdjName = pszName[0] == '.' && pszName[1] == '/' ? &pszName[2] : pszName;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Check the type & name validity then unpack it.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rcExit = ValidateMemberOfExtPack(pszName, enmType, hVfsObj);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTStrCopy(&szDstPath[offDstPath], sizeof(szDstPath) - offDstPath, pszAdjName);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rcExit = UnpackExtPackFile(pszAdjName, szDstPath, hVfsIos, hUnpackManifest);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Name is too long: '%s' (%Rrc)", pszAdjName, rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Clean up and break out on failure.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Check that what we just extracted matches the already verified
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * manifest.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTManifestEqualsEx(hUnpackManifest, hValidManifest, NULL /*papszIgnoreEntries*/, NULL /*papszIgnoreAttr*/,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Manifest mismatch: %s", szError);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTManifestEqualsEx failed: %Rrc", rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, RTFILE_O_WRITE, true, &hVfsIosStdOut);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTVfsIoStrmWrite(hVfsIosStdOut, "Unpack:\n", sizeof("Unpack:\n") - 1, true, NULL);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTManifestWriteStandard(hUnpackManifest, hVfsIosStdOut);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTVfsIoStrmWrite(hVfsIosStdOut, "Valid:\n", sizeof("Valid:\n") - 1, true, NULL);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTManifestWriteStandard(hValidManifest, hVfsIosStdOut);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Wrapper around VBoxExtPackValidateTarball.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns The program exit code.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param hTarballFile The handle to open the @a pszTarball file.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszExtPackName The name of the extension pack name.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszTarball The name of the tarball in case we have to
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * complain about something.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param phValidManifest Where to return the handle to fully validated
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * the manifest for the extension pack. This
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * includes all files.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic RTEXITCODE ValidateExtPackTarball(RTFILE hTarballFile, const char *pszExtPackName, const char *pszTarball,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTMsgInfo("Validating extension pack '%s' ('%s')...", pszTarball, pszExtPackName);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc = VBoxExtPackValidateTarball(hTarballFile, pszExtPackName, pszTarball,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync szError, sizeof(szError), phValidManifest, NULL /*phXmlFile*/);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s", szError);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * The 2nd part of the installation process.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns The program exit code.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszBaseDir The base directory.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszCertDir The certificat directory.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszTarball The tarball name.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param hTarballFile The handle to open the @a pszTarball file.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param hTarballFileOpt The tarball file handle (optional).
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync * @param pszName The extension pack name.
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync * @param pszMangledName The mangled extension pack name.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param fReplace Whether to replace any existing ext pack.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic RTEXITCODE DoInstall2(const char *pszBaseDir, const char *pszCertDir, const char *pszTarball,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync const char *pszName, const char *pszMangledName, bool fReplace)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Do some basic validation of the tarball file.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc = RTFileQueryInfo(hTarballFile, &ObjInfo, RTFSOBJATTRADD_UNIX);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTFileQueryInfo failed with %Rrc on '%s'", rc, pszTarball);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Not a regular file: %s", pszTarball);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTFileQueryInfo(hTarballFileOpt, &ObjInfo2, RTFSOBJATTRADD_UNIX);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTFileQueryInfo failed with %Rrc on --tarball-fd", rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if ( ObjInfo.Attr.u.Unix.INodeIdDevice != ObjInfo2.Attr.u.Unix.INodeIdDevice
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync || ObjInfo.Attr.u.Unix.INodeId != ObjInfo2.Attr.u.Unix.INodeId)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "--tarball and --tarball-fd does not match");
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Construct the paths to the two directories we'll be using.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTPathJoin(szFinalPath, sizeof(szFinalPath), pszBaseDir, pszMangledName);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync "Failed to construct the path to the final extension pack directory: %Rrc", rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTPathJoin(szTmpPath, sizeof(szTmpPath) - 64, pszBaseDir, pszMangledName);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTStrPrintf(&szTmpPath[cchTmpPath], sizeof(szTmpPath) - cchTmpPath, "-_-inst-%u", (uint32_t)RTProcSelf());
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync "Failed to construct the path to the temporary extension pack directory: %Rrc", rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Check that they don't exist at this point in time, unless fReplace=true.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTPathQueryInfoEx(szFinalPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (RT_SUCCESS(rc) && RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync "The extension pack is already installed. You must uninstall the old one first.");
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync "Found non-directory file system object where the extension pack would be installed ('%s')",
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync else if (rc != VERR_FILE_NOT_FOUND && rc != VERR_PATH_NOT_FOUND)
c77e7bff89c7639353778366984d51ff165ea0e3vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unexpected RTPathQueryInfoEx status code %Rrc for '%s'", rc, szFinalPath);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTPathQueryInfoEx(szTmpPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (rc != VERR_FILE_NOT_FOUND && rc != VERR_PATH_NOT_FOUND)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unexpected RTPathQueryInfoEx status code %Rrc for '%s'", rc, szFinalPath);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Create the temporary directory and prepare the extension pack within it.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * If all checks out correctly, rename it to the final directory.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Because of umask, we have to apply the mode again.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to set directory permissions on '%s': %Rrc", pszBaseDir, rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** @todo Ownership tricks on windows? */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create temporary directory: %Rrc ('%s')", rc, szTmpPath);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTEXITCODE rcExit = ValidateExtPackTarball(hTarballFile, pszName, pszTarball, &hValidManifest);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rcExit = UnpackExtPack(hTarballFile, szTmpPath, hValidManifest, pszTarball);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rcExit = ValidateUnpackedExtPack(szTmpPath, pszTarball, pszName);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTDirRename(szTmpPath, szFinalPath, RTPATHRENAME_FLAGS_NO_REPLACE);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /* Automatic uninstall if --replace was given. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTDirRename(szTmpPath, szFinalPath, RTPATHRENAME_FLAGS_NO_REPLACE);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTMsgInfo("Successfully installed '%s' (%s)", pszName, pszTarball);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync "Failed to rename the temporary directory to the final one: %Rrc ('%s' -> '%s')",
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Clean up the temporary directory on failure.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Implements the 'install' command.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns The program exit code.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param argc The number of program arguments.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param argv The program arguments.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Parse the parameters.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Note! The --base-dir and --cert-dir are only for checking that the
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * caller and this help applications have the same idea of where
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * things are. Likewise, the --name is for verifying assumptions
3ee1fadbf124a56daa99470bee91759440ef5da9vboxsync * the caller made about the name. The optional --tarball-fd option
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * is just for easing the paranoia on the user side.
3ee1fadbf124a56daa99470bee91759440ef5da9vboxsync int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /*fFlags*/);
3ee1fadbf124a56daa99470bee91759440ef5da9vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
3ee1fadbf124a56daa99470bee91759440ef5da9vboxsync bool fReplace = false;
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --base-dir options");
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid base directory: '%s'", pszBaseDir);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --cert-dir options");
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid certificate directory: '%s'", pszCertDir);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --name options");
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid extension pack name: '%s'", pszName);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --tarball options");
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --tarball-fd options");
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The --tarball-fd value is out of range: %#RX64", ValueUnion.u64);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync return RTMsgErrorExit(RTEXITCODE_SYNTAX, "RTFileFromNative failed on --target-fd value: %Rrc", rc);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --name option");
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --base-dir option");
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --cert-dir option");
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --tarball option");
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Ok, down to business.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync RTCString *pstrMangledName = VBoxExtPackMangleName(pszName);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to mangle name ('%s)", pszName);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync rc = RTFileOpen(&hTarballFile, pszTarball, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync rcExit = DoInstall2(pszBaseDir, pszCertDir, pszTarball, hTarballFile, hTarballFileOpt,
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open the extension pack tarball: %Rrc ('%s')", rc, pszTarball);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Implements the 'uninstall' command.
3ee1fadbf124a56daa99470bee91759440ef5da9vboxsync * @returns The program exit code.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @param argc The number of program arguments.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param argv The program arguments.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsyncstatic RTEXITCODE DoUninstall(int argc, char **argv)
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * Parse the parameters.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Note! The --base-dir is only for checking that the caller and this help
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * applications have the same idea of where things are.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /*fFlags*/);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
3ee1fadbf124a56daa99470bee91759440ef5da9vboxsync return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --base-dir options");
3ee1fadbf124a56daa99470bee91759440ef5da9vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid base directory: '%s'", pszBaseDir);
d6c4b5eecea7735227dc41255d4e742543ddc86fvboxsync return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --name options");
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid extension pack name: '%s'", pszName);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --name option");
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --base-dir option");
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Mangle the name so we can construct the directory names.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync RTCString *pstrMangledName = VBoxExtPackMangleName(pszName);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to mangle name ('%s)", pszName);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Ok, down to business.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync /* Check that it exists. */
79fe674227c3f2b82ab22a6b7d340283b610fb83vboxsync rc = RTPathJoin(szExtPackDir, sizeof(szExtPackDir), pszBaseDir, strMangledName.c_str());
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to construct extension pack path: %Rrc", rc);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync RTMsgInfo("Extension pack not installed. Nothing to do.");
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync RTEXITCODE rcExit = CommonUninstallWorker(szExtPackDir);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync RTMsgInfo("Successfully removed extension pack '%s'\n", pszName);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Implements the 'cleanup' command.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @returns The program exit code.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param argc The number of program arguments.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param argv The program arguments.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Parse the parameters.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Note! The --base-dir is only for checking that the caller and this help
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * applications have the same idea of where things are.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /*fFlags*/);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --base-dir options");
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid base directory: '%s'", pszBaseDir);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --base-dir option");
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * Ok, down to business.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed open the base directory: %Rrc ('%s')", rc, pszBaseDir);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync rc = RTDirReadEx(pDir, &Entry, NULL /*pcbDirEntry*/, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDirReadEx returns %Rrc", rc);
3ee1fadbf124a56daa99470bee91759440ef5da9vboxsync * Only directories which conform with our temporary install/uninstall
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * naming scheme are candidates for cleaning.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync bool fCandidate = false;
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync || !strncmp(pszMarker, "-_-inst", sizeof("-_-inst") - 1)))
3ee1fadbf124a56daa99470bee91759440ef5da9vboxsync fCandidate = VBoxExtPackIsValidMangledName(Entry.szName, pszMarker - &Entry.szName[0]);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Recursive delete, safe.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync rc = RTPathJoin(szPath, sizeof(szPath), pszBaseDir, Entry.szName);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync RTEXITCODE rcExit2 = RemoveExtPackDir(szPath, true /*fTemporary*/);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync RTMsgInfo("Successfully removed '%s'.", Entry.szName);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathJoin failed with %Rrc for '%s'", rc, Entry.szName);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_DARWIN)
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Looks in standard locations for a suitable exec tool.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @returns true if found, false if not.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param pszPath Where to store the path to the tool on
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * successs.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param cbPath The size of the buffer @a pszPath points to.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param pszName The name of the tool we're looking for.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsyncstatic bool FindExecTool(char *pszPath, size_t cbPath, const char *pszName)
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync static const char * const s_apszPaths[] =
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync for (unsigned i = 0; i < RT_ELEMENTS(s_apszPaths); i++)
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync int rc = RTPathJoin(pszPath, cbPath, s_apszPaths[i], pszName);
980eeb78f6811c935c7ebbe33de92f757ec3bc78vboxsync rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_FOLLOW_LINK);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return true;
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync return false;
f5eadb22976c1f9813300e4042b8255cfaef7e19vboxsync * Copies the content of a file to a stream.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @param hSrc The source file.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @param pDst The destination stream.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @param fComplain Whether to complain about errors (i.e. is this
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * stderr, if not keep the trap shut because it
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * may be missing when running under VBoxSVC.)
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsyncstatic void CopyFileToStdXxx(RTFILE hSrc, PRTSTREAM pDst, bool fComplain)
f65d92209d8ab9f6093c0e2a90e7d5b73a2254fevboxsync rc = RTFileRead(hSrc, abBuf, sizeof(abBuf), &cbRead);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Relaunches ourselves as a elevated process using platform specific facilities.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @returns Program exit code.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param pszExecPath The executable path.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param papszArgs The arguments.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @param cSuArgs The number of argument entries reserved for the
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * 'su' like programs at the start of papszArgs.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param cMyArgs The number of arguments following @a cSuArgs.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param iCmd The command that is being executed. (For
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * selecting messages.)
980eeb78f6811c935c7ebbe33de92f757ec3bc78vboxsync * @param pszDisplayInfoHack Display information hack. Platform specific++.
f7f5cd7b1e530eb5636da51c974b48ae0c1775b3vboxsyncstatic RTEXITCODE RelaunchElevatedNative(const char *pszExecPath, const char **papszArgs, int cSuArgs, int cMyArgs,
f7f5cd7b1e530eb5636da51c974b48ae0c1775b3vboxsync CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync int rc = RTStrToUtf16(pszExecPath, (PRTUTF16 *)&Info.lpFile);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync rc = RTGetOptArgvToString(&pszCmdLine, &papszArgs[cSuArgs + 1], RTGETOPTARGV_CNV_QUOTE_MS_CRT);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync rc = RTStrToUtf16(pszCmdLine, (PRTUTF16 *)&Info.lpParameters);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync /* Apply display hacks. */
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync const char *pszArg = strstr(pszDisplayInfoHack, "hwnd=");
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync rc = RTStrToUInt64Ex(pszArg + sizeof("hwnd=") - 1, NULL, 0, &u64Hwnd);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync Info.hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync Info.hMonitor = MonitorFromPoint(Pt, MONITOR_DEFAULTTOPRIMARY);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * Wait for the process, make sure the deal with messages.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync DWORD dwRc = MsgWaitForMultipleObjects(1, &Info.hProcess, FALSE, 5000/*ms*/, QS_ALLEVENTS);
3ee1fadbf124a56daa99470bee91759440ef5da9vboxsync RTMsgError("MsgWaitForMultipleObjects returned: %#x (%d), err=%u", dwRc, dwRc, GetLastError());
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync if (GetExitCodeProcess(Info.hProcess, &dwExitCode))
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync RTMsgError("ShellExecuteExW return INVALID_HANDLE_VALUE as Info.hProcess");
3ee1fadbf124a56daa99470bee91759440ef5da9vboxsync RTMsgError("ShellExecuteExW failed: %u (%#x)", GetLastError(), GetLastError());
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync int rc = RTPathAppPrivateArch(szIconName, sizeof(szIconName));
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync rc = RTPathAppend(szIconName, sizeof(szIconName), "../Resources/virtualbox.png");
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to construct icon path: %Rrc", rc);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync OSStatus orc = AuthorizationCreate(NULL, 0, kAuthorizationFlagDefaults, &AuthRef);
3ee1fadbf124a56daa99470bee91759440ef5da9vboxsync * Preautorize the privileged execution of ourselves.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync AuthorizationItem AuthItem = { kAuthorizationRightExecute, 0, NULL, 0 };
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync static char s_szPrompt[] = "VirtualBox needs further rights to make changes to your installation.\n\n";
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync { kAuthorizationEnvironmentPrompt, strlen(s_szPrompt), s_szPrompt, 0 },
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync { kAuthorizationEnvironmentIcon, strlen(szIconName), szIconName, 0 }
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync AuthorizationEnvironment AuthEnv = { RT_ELEMENTS(aAuthEnvItems), aAuthEnvItems };
3ee1fadbf124a56daa99470bee91759440ef5da9vboxsync orc = AuthorizationCopyRights(AuthRef, &AuthRights, &AuthEnv,
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync kAuthorizationFlagPreAuthorize | kAuthorizationFlagInteractionAllowed
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Execute with extra permissions
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync orc = AuthorizationExecuteWithPrivileges(AuthRef, pszExecPath, kAuthorizationFlagDefaults,
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Read the output of the tool, the read will fail when it quits.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync size_t cbRead = fread(achBuf, 1, sizeof(achBuf), pSocketStrm);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync RTMsgError("AuthorizationExecuteWithPrivileges failed: %d", orc);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync RTMsgError("AuthorizationCopyRights failed: %d", orc);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync AuthorizationFree(AuthRef, kAuthorizationFlagDefaults);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * Several of the alternatives below will require a command line.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync int rc = RTGetOptArgvToString(&pszCmdLine, &papszArgs[cSuArgs], RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptArgvToString failed: %Rrc");
3ee1fadbf124a56daa99470bee91759440ef5da9vboxsync * Look for various standard stuff for executing a program as root.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * N.B. When adding new arguments, please make 100% sure RelaunchElevated
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * allocates enough array entries.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * TODO: Feel free to contribute code for using PolicyKit directly.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * kdesudo is available on KDE3/KDE4
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync if (fHaveDisplayVar && FindExecTool(szExecTool, sizeof(szExecTool), "kdesudo"))
3ee1fadbf124a56daa99470bee91759440ef5da9vboxsync ? "VirtualBox extension pack installer"
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync ? "VirtualBox extension pack uninstaller"
33619506f3e6045180309528f76b39cc36760431vboxsync : "VirtualBox extension pack maintainer";
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * gksu is our favorite as it is very well integrated.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync else if (fHaveDisplayVar && FindExecTool(szExecTool, sizeof(szExecTool), "gksu"))
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync#if 0 /* older gksu does not grok --description nor '--' and multiple args. */
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync ? "VirtualBox extension pack installer"
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync ? "VirtualBox extension pack uninstaller"
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync : "VirtualBox extension pack maintainer";
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync#elif defined(RT_OS_SOLARIS) /* Force it not to use pfexec as it won't wait then. */
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * pkexec may work for ssh console sessions as well if the right agents
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * are installed. However it is very generic and does not allow for any
3ee1fadbf124a56daa99470bee91759440ef5da9vboxsync * custom messages. Thus it comes after gksu.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync else if (FindExecTool(szExecTool, sizeof(szExecTool), "pkexec"))
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * The ultimate fallback is running 'su -' within an xterm. We use the
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * title of the xterm to tell what is going on.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync && FindExecTool(szExecTool, sizeof(szExecTool), "su")
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync && FindExecTool(szXterm, sizeof(szXterm), "xterm"))
3ee1fadbf124a56daa99470bee91759440ef5da9vboxsync ? "VirtualBox extension pack installer - su"
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync ? "VirtualBox extension pack uninstaller - su"
33619506f3e6045180309528f76b39cc36760431vboxsync : "VirtualBox extension pack maintainer - su";
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync RTMsgError("Unable to locate 'pkexec', 'gksu' or 'su+xterm'. Try perform the operation using VBoxManage running as root");
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTMsgError("Unable to locate 'pkexec'. Try perform the operation using VBoxManage running as root");
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Argument list constructed, execute it and wait for the exec
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * program to complete.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTProcCreateEx(papszArgs[iSuArg], &papszArgs[iSuArg], RTENV_DEFAULT, 0 /*fFlags*/,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync NULL /*phStdIn*/, NULL /*phStdOut*/, NULL /*phStdErr*/, NULL /*pszAsUser*/, NULL /*pszPassword*/,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTProcWait(hProcess, RTPROCWAIT_FLAGS_BLOCK, &Status);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTMsgError("Error while waiting for '%s': %Rrc", papszArgs[iSuArg], rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTMsgError("Failed to execute '%s': %Rrc", papszArgs[iSuArg], rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Relaunches ourselves as a elevated process using platform specific facilities.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns Program exit code.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param argc The number of arguments.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param argv The arguments.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param iCmd The command that is being executed.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszDisplayInfoHack Display information hack. Platform specific++.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic RTEXITCODE RelaunchElevated(int argc, char **argv, int iCmd, const char *pszDisplayInfoHack)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * We need the executable name later, so get it now when it's easy to quit.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (!RTProcGetExecutablePath(szExecPath,sizeof(szExecPath)))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcGetExecutablePath failed");
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Create a couple of temporary files for stderr and stdout.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathTemp failed: %Rrc", rc);
char const **papszArgs = (char const **)RTMemTmpAllocZ((cSuArgs + cArgs + 1) * sizeof(const char *));
if (papszArgs)
return rcExit;
*pfElevated = false;
# if defined(RT_OS_WINDOWS)
return RTMsgErrorExit(RTEXITCODE_FAILURE, "OpenProcessToken failed: %u (%#x)", GetLastError(), GetLastError());
if (AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminGrpSid))
# ifdef DEBUG
# ifdef DEBUG
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "GetTokenInformation(TokenGroups,cb) failed: %u (%#x)", GetLastError(), GetLastError());
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "GetTokenInformation(TokenGroups,0) failed: %u (%#x)", GetLastError(), GetLastError());
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "AllocateAndInitializeSid failed: %u (%#x)", GetLastError(), GetLastError());
if (fIsAdmin)
DWORD dwIntegrityLevel = *GetSidSubAuthority(pSidAndAttr->Sid, *GetSidSubAuthorityCount(pSidAndAttr->Sid) - 1U);
*pfElevated = true;
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "GetTokenInformation failed: %u (%#x)", GetLastError(), GetLastError());
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "GetTokenInformation failed: %u (%#x)", GetLastError(), GetLastError());
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Membership in the Administrators group is required to perform this action");
return rcExit;
return RTEXITCODE_SUCCESS;
#ifdef WITH_ELEVATION
bool fElevated;
return rcExit;
#ifdef WITH_ELEVATION
switch (ch)
case CMD_INSTALL:
case CMD_UNINSTALL:
case CMD_CLEANUP:
#ifdef WITH_ELEVATION
if (!fElevated)
switch (ch)
case CMD_INSTALL:
case CMD_UNINSTALL:
case CMD_CLEANUP:
switch (rcExit)
case RTEXITCODE_SUCCESS:
return rcExit;
#ifdef WITH_ELEVATION
case OPT_ELEVATED:
fElevated = true;
case OPT_STDERR:
case OPT_STDOUT:
if (!pFile)
case OPT_DISP_INFO_HACK:
if (pszDisplayInfoHack)
#ifdef RT_OS_WINDOWS
extern "C" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
if (!pwszCmdLine)
char *pszCmdLine;
int cArgs;
char **papszArgs;
return rc;