VirtualBox

Ignore:
Timestamp:
Mar 17, 2022 11:12:27 AM (3 years ago)
Author:
vboxsync
Message:

Win/Installer: Fixes for VBoxStub. bugref:10201

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Installer/win/Stub/VBoxStub.cpp

    r93391 r94284  
    8282    /** List entry. */
    8383    RTLISTNODE  ListEntry;
     84    /** Stub package index (zero-based) this record belongs to. */
     85    unsigned    idxPkg;
    8486    /** True if file, false if directory. */
    8587    bool        fFile;
     88    union
     89    {
     90        /** File handle (if \a fFile is \c true). */
     91        RTFILE  hFile;
     92        /** Directory handle (if \a fFile is \c false). */
     93        RTDIR   hDir;
     94    };
    8695    /** The path to the file or directory to clean up. */
    8796    char        szPath[1];
     
    8998/** Pointer to a cleanup record. */
    9099typedef STUBCLEANUPREC *PSTUBCLEANUPREC;
     100
     101/*********************************************************************************************************************************
     102*   Prototypes                                                                                                                   *
     103*********************************************************************************************************************************/
     104static bool AddCleanupRec(const char *pszPath, bool fFile, const void *phObj);
    91105
    92106
     
    292306                       const char *pszTempFile)
    293307{
    294 #if 0 /* Another example of how unnecessarily complicated things get with
    295          do-break-while-false and you end up with buggy code using uninitialized
    296          variables. */
    297     int rc;
    298     RTFILE fh;
    299     BOOL bCreatedFile = FALSE;
    300 
    301     do
    302     {
    303         AssertMsgBreak(pszResourceName, ("Resource pointer invalid!\n")); /* rc is not initialized here, we'll return garbage. */
    304         AssertMsgBreak(pszTempFile, ("Temp file pointer invalid!"));      /* Ditto. */
    305 
    306         /* Read the data of the built-in resource. */
    307         PVOID pvData = NULL;
    308         DWORD dwDataSize = 0;
    309         rc = FindData(pszResourceName, &pvData, &dwDataSize);
    310         AssertMsgRCBreak(rc, ("Could not read resource data!\n"));
    311 
    312         /* Create new (and replace an old) file. */
    313         rc = RTFileOpen(&fh, pszTempFile,
    314                           RTFILE_O_CREATE_REPLACE
    315                         | RTFILE_O_WRITE
    316                         | RTFILE_O_DENY_NOT_DELETE
    317                         | RTFILE_O_DENY_WRITE);
    318         AssertMsgRCBreak(rc, ("Could not open file for writing!\n"));
    319         bCreatedFile = TRUE;
    320 
    321         /* Write contents to new file. */
    322         size_t cbWritten = 0;
    323         rc = RTFileWrite(fh, pvData, dwDataSize, &cbWritten);
    324         AssertMsgRCBreak(rc, ("Could not open file for writing!\n"));
    325         AssertMsgBreak(dwDataSize == cbWritten, ("File was not extracted completely! Disk full?\n"));
    326 
    327     } while (0);
    328 
    329     if (RTFileIsValid(fh)) /* fh is unused uninitalized (MSC agrees) */
    330         RTFileClose(fh);
    331 
    332     if (RT_FAILURE(rc))
    333     {
    334         if (bCreatedFile)
    335             RTFileDelete(pszTempFile);
    336     }
    337 
    338 #else /* This is exactly the same as above, except no bug and better assertion
    339          message.  Note only the return-success statment is indented, indicating
    340          that the whole do-break-while-false approach was totally unnecessary.   */
    341 
    342308    AssertPtrReturn(pszResourceName, VERR_INVALID_POINTER);
    343309    AssertPtrReturn(pszTempFile, VERR_INVALID_POINTER);
     
    354320                      RTFILE_O_CREATE_REPLACE
    355321                    | RTFILE_O_WRITE
    356                     | RTFILE_O_DENY_NOT_DELETE
    357322                    | RTFILE_O_DENY_WRITE);
     323
    358324    AssertMsgRCReturn(rc, ("Could not open '%s' for writing: %Rrc\n", pszTempFile, rc), rc);
     325
     326    /* Add a cleanup record, so that we can properly clean up (partially run) stuff. */
     327    bool fRc = AddCleanupRec(pszTempFile, true /*fFile*/, &hFile);
     328    AssertMsgReturn(fRc, ("Could not add cleanup record '%s': %Rrc\n", pszTempFile, rc), rc);
    359329
    360330    /* Write contents to new file. */
     
    363333    AssertMsgStmt(cbWritten == dwDataSize || RT_FAILURE_NP(rc), ("%#zx vs %#x\n", cbWritten, dwDataSize), rc = VERR_WRITE_ERROR);
    364334
    365     int rc2 = RTFileClose(hFile);
    366     AssertRC(rc2);
     335    /* See @bugref{10201} comment 8. */
     336
     337    rc = RTFileClose(hFile);
     338    AssertMsgRCReturn(rc, ("Closing file failed: %Rrc\n", rc), rc);
     339
     340    rc = RTFileOpen(&hFile, pszTempFile,
     341                      RTFILE_O_OPEN
     342                    | RTFILE_O_READ
     343                    | RTFILE_O_DENY_WRITE);
     344    AssertMsgRCReturn(rc, ("Could not open '%s' for reading: %Rrc\n", pszTempFile, rc), rc);
     345
     346    /*
     347     * Note: We keep the file open so that nobody else can write to it until we're done.
     348     * See @bugref{10201}
     349     */
    367350
    368351    if (RT_SUCCESS(rc))
    369352        return VINF_SUCCESS;
    370353
    371     RTFileDelete(pszTempFile);
    372 
    373 #endif
    374354    return rc;
    375355}
     
    434414 * @param   pszPath             The path to the file or directory to clean up.
    435415 * @param   fFile               @c true if file, @c false if directory.
    436  */
    437 static bool AddCleanupRec(const char *pszPath, bool fFile)
    438 {
     416 * @param   phObj               File / directory handle, depending on \a fFile.
     417 */
     418static bool AddCleanupRec(const char *pszPath, bool fFile, const void *phObj)
     419{
     420    AssertPtrReturn(phObj, false);
    439421    size_t cchPath = strlen(pszPath); Assert(cchPath > 0);
    440     PSTUBCLEANUPREC pRec = (PSTUBCLEANUPREC)RTMemAlloc(RT_UOFFSETOF_DYN(STUBCLEANUPREC, szPath[cchPath + 1]));
     422    PSTUBCLEANUPREC pRec = (PSTUBCLEANUPREC)RTMemAllocZ(RT_UOFFSETOF_DYN(STUBCLEANUPREC, szPath[cchPath + 1]));
    441423    if (!pRec)
    442424    {
     
    445427    }
    446428    pRec->fFile = fFile;
     429    if (pRec->fFile)
     430        memcpy(&pRec->hFile, phObj, sizeof(RTFILE));
     431    else
     432        memcpy(&pRec->hDir, phObj, sizeof(RTDIR));
    447433    memcpy(pRec->szPath, pszPath, cchPath + 1);
    448434
     
    463449    for (int i = 0; i < 5; i++)
    464450    {
    465         int rc;
     451        int rc = VINF_SUCCESS;
    466452        bool fFinalTry = i == 4;
    467453
     
    470456        {
    471457            if (pCur->fFile)
    472                 rc = RTFileDelete(pCur->szPath);
    473             else
    474             {
    475                 rc = RTDirRemoveRecursive(pCur->szPath, RTDIRRMREC_F_CONTENT_AND_DIR);
    476                 if (rc == VERR_DIR_NOT_EMPTY && fFinalTry)
    477                     rc = VINF_SUCCESS;
     458            {
     459                if (RTFileIsValid(pCur->hFile))
     460                {
     461                    rc = RTFileClose(pCur->hFile);
     462                    if (RT_SUCCESS(rc))
     463                        pCur->hFile = NIL_RTFILE;
     464                }
     465
     466                if (RT_SUCCESS(rc))
     467                    rc = RTFileDelete(pCur->szPath);
     468            }
     469            else /* Directory */
     470            {
     471                if (RTDirIsValid(pCur->hDir))
     472                {
     473                    rc = RTDirClose(pCur->hDir);
     474                    if (RT_SUCCESS(rc))
     475                        pCur->hDir = NIL_RTDIR;
     476                }
     477
     478                if (RT_SUCCESS(rc))
     479                {
     480                    /* Note: Not removing the directory recursively, as we should have separate cleanup records for that. */
     481                    rc = RTDirRemove(pCur->szPath);
     482                    if (rc == VERR_DIR_NOT_EMPTY && fFinalTry)
     483                        rc = VINF_SUCCESS;
     484                }
    478485            }
    479486            if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
     
    682689     */
    683690    char szPkgFile[RTPATH_MAX];
    684     int rc = RTPathJoin(szPkgFile, sizeof(szPkgFile), pszPkgDir, pPackage->szFileName);
     691
     692    /*
     693     * Use the cleanup record to built-up the final name to process, as the cleanup record's file name might be different
     694     * than the original package's name.
     695     *
     696     * We can't write to the package's attributes, as those point to a write-protected area within the stub loader.
     697     * So I didn't feel to re-writing nearly everything here and went for this approach instead.
     698     *
     699     ** @todo The structure / stub API needs a major overhaul.*
     700     */
     701    PSTUBCLEANUPREC pRec = NULL;
     702    PSTUBCLEANUPREC pCur;
     703    RTListForEach(&g_TmpFiles, pCur, STUBCLEANUPREC, ListEntry)
     704    {
     705        if (pCur->idxPkg == iPackage)
     706        {
     707            pRec = pCur;
     708            break;
     709        }
     710    }
     711    AssertMsgReturn(pRec != NULL, ("Package #%x not found in cleanup records\n", iPackage), RTEXITCODE_FAILURE);
     712
     713    const char *pszFileName = RTPathFilename(pRec->szPath);
     714    AssertMsgReturn(pszFileName != NULL, ("Cleanup record does not have a valid file name\n"), RTEXITCODE_FAILURE);
     715    int rc = RTPathJoin(szPkgFile, sizeof(szPkgFile), pszPkgDir, pszFileName);
    685716    if (RT_FAILURE(rc))
    686717        return ShowError("Internal error: RTPathJoin failed: %Rrc", rc);
     718
    687719    RTPathChangeToDosSlashes(szPkgFile, true /* Force conversion. */); /* paranoia */
    688720
     
    786818        if (!pszDstSubDir)
    787819            return ShowError("Out of memory!");
    788         bool fRc = AddCleanupRec(pszDstSubDir, false /*fFile*/);
     820
     821        /*
     822         * Note: We keep the directory open so that nobody else can delete / replace it while we're working on it.
     823         * See @bugref{10201}
     824         */
     825        RTDIR hDstSubDir;
     826        rc = RTDirOpen(&hDstSubDir, pszDstSubDir);
     827        if (RT_FAILURE(rc))
     828            return ShowError("Unable to open the destination .custom directory: %Rrc", rc);
     829
     830        /* Add a cleanup record. */
     831        bool fRc = AddCleanupRec(pszDstSubDir, false /*fFile*/, &hDstSubDir);
     832        AssertMsgReturn(fRc, ("Could not add cleanup record '%s': %Rrc\n", pszDstSubDir, rc), RTEXITCODE_FAILURE);
     833
    789834        RTStrFree(pszDstSubDir);
    790835        if (!fRc)
     
    809854        if (RT_FAILURE(rc))
    810855            return ShowError("Failed to create extraction path '%s': %Rrc", pszDstDir, rc);
     856
    811857        *pfCreatedExtractDir = true;
    812858    }
    813859
     860    RTDIR hDir;
     861    rc = RTDirOpen(&hDir, pszDstDir);
     862    if (RT_FAILURE(rc))
     863        return ShowError("Failed to open extraction path '%s': %Rrc", pszDstDir, rc);
     864
     865    /* Add a cleanup record. */
     866    bool fRc = AddCleanupRec(pszDstDir, false /*fFile*/, &hDir);
     867    AssertMsgReturn(fRc, ("Could not add cleanup record '%s': %Rrc\n", pszDstDir, rc), RTEXITCODE_FAILURE);
     868
    814869    /*
    815870     * Extract files.
     
    817872    for (unsigned k = 0; k < cPackages; k++)
    818873    {
    819         PVBOXSTUBPKG pPackage = FindPackageHeader(k);
     874        PVBOXSTUBPKG const pPackage = FindPackageHeader(k);
    820875        if (!pPackage)
    821876            return RTEXITCODE_FAILURE; /* Done complaining already. */
     
    824879        {
    825880            char szDstFile[RTPATH_MAX];
    826             rc = RTPathJoin(szDstFile, sizeof(szDstFile), pszDstDir, pPackage->szFileName);
     881            if (fExtractOnly) /* If we only extract, use the original file name. */
     882            {
     883                rc = RTPathJoin(szDstFile, sizeof(szDstFile), pszDstDir, pPackage->szFileName);
     884            }
     885            else /* When temporarily extracting and running, use a random file name. See @bugref{10201} */
     886            {
     887                rc = RTPathJoin(szDstFile, sizeof(szDstFile), pszDstDir, "XXXXXXXXXXXX");
     888                if (RT_SUCCESS(rc))
     889                {
     890                    const char *pszDotExt = RTPathSuffix(pPackage->szFileName);
     891                    if (pszDotExt)
     892                        rc = RTStrCat(szDstFile, sizeof(szDstFile), pszDotExt);
     893                    if (RT_SUCCESS(rc))
     894                        rc = RTFileCreateTemp(szDstFile, 0700);
     895                }
     896            }
    827897            if (RT_FAILURE(rc))
    828                 return ShowError("Internal error: RTPathJoin failed: %Rrc", rc);
     898                return ShowError("Internal error: Build extraction file name failed: %Rrc", rc);
    829899
    830900            rc = Extract(pPackage, szDstFile);
    831901            if (RT_FAILURE(rc))
    832902                return ShowError("Error extracting package #%u: %Rrc", k, rc);
    833 
    834             if (!fExtractOnly && !AddCleanupRec(szDstFile, true /*fFile*/))
    835             {
    836                 RTFileDelete(szDstFile);
    837                 return RTEXITCODE_FAILURE;
    838             }
    839903        }
    840904    }
     905
     906    /* Note: Closing the directory will be done when processing the cleanup record. */
    841907
    842908    return RTEXITCODE_SUCCESS;
     
    10691135    }
    10701136
    1071     /* Set the default extraction path if not given the the user. */
     1137    /*
     1138     * Set a default extraction path if not explicitly given by the user.
     1139     *
     1140     * By default we're constructing a non-deterministic path.
     1141     * See @bugref{10201}
     1142     */
    10721143    if (szExtractPath[0] == '\0')
    10731144    {
    10741145        vrc = RTPathTemp(szExtractPath, sizeof(szExtractPath));
    10751146        if (RT_SUCCESS(vrc))
    1076             vrc = RTPathAppend(szExtractPath, sizeof(szExtractPath), "VirtualBox");
     1147        {
     1148            if (!fExtractOnly) /* Only use a random sub-dir if we extract + run (and not just extract). */
     1149            {
     1150                vrc = RTPathAppend(szExtractPath, sizeof(szExtractPath), "XXXXXXXXXXXXXXXX");
     1151                if (RT_SUCCESS(vrc))
     1152                    vrc = RTDirCreateTemp(szExtractPath, 0700); /** @todo Use RTDirCreateTempSecure() once it's implemented. */
     1153            }
     1154        }
     1155
    10771156        if (RT_FAILURE(vrc))
    1078             return ShowError("Failed to construct extraction path: %Rrc", vrc);
     1157            return ShowError("Failed to create extraction path: %Rrc", vrc);
    10791158    }
    10801159    RTPathChangeToDosSlashes(szExtractPath, true /* Force conversion. */); /* MSI requirement. */
     
    11181197    if (g_iVerbosity)
    11191198    {
     1199        RTPrintf("Extraction path          : %s\n",      szExtractPath);
    11201200        RTPrintf("Silent installation      : %RTbool\n", g_fSilent);
    11211201        RTPrintf("Logging enabled          : %RTbool\n", fEnableLogging);
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