VirtualBox

Ignore:
Timestamp:
Jul 25, 2022 9:01:44 AM (2 years ago)
Author:
vboxsync
Message:

IPRT/env-win.cpp: IPRT_NO_CRT adjustments, rewrote a few bits. bugref:10261

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/r3/win/env-win.cpp

    r93115 r95792  
    3131#include <iprt/env.h>
    3232
     33#ifdef IPRT_NO_CRT
     34# include <iprt/asm.h>
     35#endif
    3336#include <iprt/alloca.h>
    3437#include <iprt/assert.h>
     
    3841#include <iprt/utf16.h>
    3942
    40 #include <stdlib.h>
    41 #include <errno.h>
     43#ifndef IPRT_NO_CRT
     44# include <stdlib.h>
     45# include <errno.h>
     46#endif
     47#include <iprt/win/windows.h>
     48
     49
     50/*********************************************************************************************************************************
     51*   Global Variables                                                                                                             *
     52*********************************************************************************************************************************/
     53#ifdef IPRT_NO_CRT
     54static uint32_t volatile g_idxGetEnvBufs = 0;
     55static char             *g_apszGetEnvBufs[64]; /* leak */
     56#endif
     57
     58
     59/*********************************************************************************************************************************
     60*   Internal Functions                                                                                                           *
     61*********************************************************************************************************************************/
     62int rtEnvSetUtf8Worker(const char *pchVar, size_t cchVar, const char *pszValue);
     63
    4264
    4365
    4466RTDECL(bool) RTEnvExistsBad(const char *pszVar)
    4567{
     68#ifndef IPRT_NO_CRT
    4669    return RTEnvGetBad(pszVar) != NULL;
     70#else
     71    return RTEnvExistsUtf8(pszVar);
     72#endif
    4773}
    4874
     
    5076RTDECL(bool) RTEnvExist(const char *pszVar)
    5177{
     78#ifndef IPRT_NO_CRT
    5279    return RTEnvExistsBad(pszVar);
     80#else
     81    return RTEnvExistsUtf8(pszVar);
     82#endif
    5383}
    5484
     
    6191    int rc = RTStrToUtf16(pszVar, &pwszVar);
    6292    AssertRCReturn(rc, false);
     93
     94#ifndef IPRT_NO_CRT
    6395    bool fRet = _wgetenv(pwszVar) != NULL;
     96#else
     97    DWORD dwRet = GetEnvironmentVariableW(pwszVar, NULL, 0);
     98    bool fRet = dwRet != 0;
     99#endif
     100
    64101    RTUtf16Free(pwszVar);
    65102    return fRet;
     
    70107{
    71108    AssertReturn(strchr(pszVar, '=') == NULL, NULL);
     109#ifndef IPRT_NO_CRT
    72110    return getenv(pszVar);
     111#else
     112    /*
     113     * Query the value into heap buffer which we give a lifetime of 64
     114     * RTEnvGetBad calls.
     115     */
     116    char *pszValue = RTEnvDup(pszVar);
     117    if (pszValue)
     118    {
     119        RTMEM_MAY_LEAK(pszValue); /* Quite possible we'll leak this, but the leak is limited to 64 values. */
     120
     121        uint32_t idx = ASMAtomicIncU32(&g_idxGetEnvBufs) % RT_ELEMENTS(g_apszGetEnvBufs);
     122        char *pszOld = (char *)ASMAtomicXchgPtr((void * volatile *)&g_apszGetEnvBufs[idx], pszValue);
     123        RTStrFree(pszOld);
     124    }
     125    return pszValue;
     126#endif
    73127}
    74128
     
    91145        *pcchActual = 0;
    92146
     147    /*
     148     * Convert the name to UTF-16.
     149     */
    93150    PRTUTF16 pwszVar;
    94151    int rc = RTStrToUtf16(pszVar, &pwszVar);
    95152    AssertRCReturn(rc, rc);
    96153
    97     /** @todo Consider _wgetenv_s or GetEnvironmentVariableW here to avoid the
    98      *        potential race with a concurrent _wputenv/_putenv. */
    99     PCRTUTF16 pwszValue = _wgetenv(pwszVar);
     154    /*
     155     * Query the variable.  First try with a medium sized stack buffer (too
     156     * small for your typical PATH, but large enough for most other things).
     157     */
     158    RTUTF16  wszValue[512];
     159    uint32_t cwcValueBuf   = RT_ELEMENTS(wszValue);
     160    PRTUTF16 pwszValue     = wszValue;
     161    PRTUTF16 pwszValueFree = NULL;
     162
     163    for (unsigned iTry = 0;; iTry++)
     164    {
     165        /* This API is weird, it didn't always set ERROR_BUFFER_OVERFLOW.
     166           Note! Assume that the CRT transparently updates the process
     167                 environment and that we don't need to use _wgetenv_s here. */
     168        SetLastError(NO_ERROR);
     169        DWORD const cwcValueRet = GetEnvironmentVariableW(pwszVar, pwszValue, cwcValueBuf);
     170        DWORD const dwErr       = GetLastError();
     171
     172        if (cwcValueRet < cwcValueBuf)
     173        {
     174            if (cwcValueRet > 0 || dwErr == NO_ERROR) /* In case of empty values we have to see if last error was set or not. */
     175            {
     176                if (cbValue)
     177                    rc = RTUtf16ToUtf8Ex(pwszValue, cwcValueRet, &pszValue, cbValue, pcchActual);
     178                else
     179                    rc = RTUtf16CalcUtf8LenEx(pwszValue, cwcValueRet, pcchActual);
     180            }
     181            else
     182            {
     183                Assert(cwcValueRet == 0);
     184                Assert(dwErr != NO_ERROR);
     185                rc = RTErrConvertFromWin32(dwErr);
     186            }
     187            break;
     188        }
     189
     190        /*
     191         * Insufficient buffer, so increase it.  The first re-try will use the
     192         * returned size, further re-tries will max out with a multiple of the
     193         * stack buffer till we reaches 32KB chars (128 loops).
     194         */
     195        Assert(dwErr == NO_ERROR || dwErr == ERROR_BUFFER_OVERFLOW);
     196        RTMemTmpFree(pwszValueFree);
     197        AssertBreakStmt(cwcValueBuf < _32K, rc = VERR_INTERNAL_ERROR_3 /* not a good one */);
     198
     199        cwcValueBuf = RT_MAX(cwcValueRet + iTry, RT_ELEMENTS(wszValue) * iTry);
     200        pwszValueFree = pwszValue = (PRTUTF16)RTMemTmpAlloc(cwcValueBuf * sizeof(RTUTF16));
     201        AssertBreakStmt(pwszValue, rc = VERR_NO_TMP_MEMORY);
     202    }
     203
     204    RTMemTmpFree(pwszValueFree);
    100205    RTUtf16Free(pwszVar);
    101     if (pwszValue)
    102     {
    103         if (cbValue)
    104             rc = RTUtf16ToUtf8Ex(pwszValue, RTSTR_MAX, &pszValue, cbValue, pcchActual);
    105         else
    106             rc = RTUtf16CalcUtf8LenEx(pwszValue, RTSTR_MAX, pcchActual);
    107     }
    108     else
    109         rc = VERR_ENV_VAR_NOT_FOUND;
    110     return rc;
     206    return rc;
     207}
     208
     209
     210RTDECL(char *) RTEnvDup(const char *pszVar)
     211{
     212    AssertPtrReturn(pszVar, NULL);
     213
     214    /*
     215     * Convert the name to UTF-16.
     216     */
     217    PRTUTF16 pwszVar;
     218    int rc = RTStrToUtf16(pszVar, &pwszVar);
     219    AssertRCReturn(rc, NULL);
     220
     221    /*
     222     * Query the variable.  First try with a medium sized stack buffer (too
     223     * small for your typical PATH, but large enough for most other things).
     224     */
     225    char    *pszRet = NULL;
     226    RTUTF16  wszValue[512];
     227    uint32_t cwcValueBuf   = RT_ELEMENTS(wszValue);
     228    PRTUTF16 pwszValue     = wszValue;
     229    PRTUTF16 pwszValueFree = NULL;
     230
     231    for (unsigned iTry = 0;; iTry++)
     232    {
     233        /* This API is weird, it didn't always set ERROR_BUFFER_OVERFLOW.
     234           Note! Assume that the CRT transparently updates the process
     235                 environment and that we don't need to use _wgetenv_s here. */
     236        SetLastError(NO_ERROR);
     237        DWORD const cwcValueRet = GetEnvironmentVariableW(pwszVar, pwszValue, cwcValueBuf);
     238        DWORD const dwErr       = GetLastError();
     239
     240        if (cwcValueRet < cwcValueBuf)
     241        {
     242            if (cwcValueRet > 0 || dwErr == NO_ERROR) /* In case of empty values we have to see if last error was set or not. */
     243            {
     244                rc = RTUtf16ToUtf8Ex(pwszValue, cwcValueRet, &pszRet, 0, NULL);
     245                if (RT_FAILURE(rc))
     246                    pszRet = NULL;
     247            }
     248            else
     249            {
     250                Assert(cwcValueRet == 0);
     251                Assert(dwErr != NO_ERROR);
     252            }
     253            break;
     254        }
     255
     256        /*
     257         * Insufficient buffer, so increase it.  The first re-try will use the
     258         * returned size, further re-tries will max out with a multiple of the
     259         * stack buffer till we reaches 32KB chars (128 loops).
     260         */
     261        Assert(dwErr == NO_ERROR || dwErr == ERROR_BUFFER_OVERFLOW);
     262        RTMemTmpFree(pwszValueFree);
     263        AssertBreakStmt(cwcValueBuf < _32K, rc = VERR_INTERNAL_ERROR_3 /* not a good one */);
     264
     265        cwcValueBuf = RT_MAX(cwcValueRet + iTry, RT_ELEMENTS(wszValue) * iTry);
     266        pwszValueFree = pwszValue = (PRTUTF16)RTMemTmpAlloc(cwcValueBuf * sizeof(RTUTF16));
     267        AssertBreakStmt(pwszValue, rc = VERR_NO_TMP_MEMORY);
     268    }
     269
     270    RTMemTmpFree(pwszValueFree);
     271    RTUtf16Free(pwszVar);
     272    return pszRet;
    111273}
    112274
     
    114276RTDECL(int) RTEnvPutBad(const char *pszVarEqualValue)
    115277{
     278#ifndef IPRT_NO_CRT
    116279    /** @todo putenv is a source memory leaks. deal with this on a per system basis. */
    117280    if (!putenv((char *)pszVarEqualValue))
    118281        return 0;
    119282    return RTErrConvertFromErrno(errno);
     283#else
     284    return RTEnvPutUtf8(pszVarEqualValue);
     285#endif
    120286}
    121287
     
    123289RTDECL(int) RTEnvPut(const char *pszVarEqualValue)
    124290{
     291#ifndef IPRT_NO_CRT
    125292    return RTEnvPutBad(pszVarEqualValue);
     293#else
     294    return RTEnvPutUtf8(pszVarEqualValue);
     295#endif
    126296}
    127297
     
    133303    if (RT_SUCCESS(rc))
    134304    {
     305#ifndef IPRT_NO_CRT
    135306        if (!_wputenv(pwszVarEqualValue))
    136307            rc = VINF_SUCCESS;
    137308        else
    138309            rc = RTErrConvertFromErrno(errno);
     310#else
     311        PRTUTF16 pwszValue = RTUtf16Chr(pwszVarEqualValue, '=');
     312        if (pwszValue)
     313        {
     314            *pwszValue++ = '\0';
     315
     316            SetLastError(*pwszValue ? ERROR_OUTOFMEMORY : ERROR_ENVVAR_NOT_FOUND); /* The API did not always set the last error. */
     317            if (SetEnvironmentVariableW(pwszVarEqualValue, *pwszValue ? pwszValue : NULL))
     318                rc = VINF_SUCCESS;
     319            else
     320            {
     321                DWORD dwErr = GetLastError();
     322                if (dwErr == ERROR_ENVVAR_NOT_FOUND)
     323                {
     324                    Assert(!*pwszValue);
     325                    rc = VINF_SUCCESS;
     326                }
     327                else
     328                {
     329                    Assert(*pwszValue);
     330                    rc = RTErrConvertFromWin32(GetLastError());
     331                }
     332            }
     333        }
     334        else
     335            rc = VERR_INVALID_PARAMETER;
     336#endif
    139337        RTUtf16Free(pwszVarEqualValue);
    140338    }
     
    146344RTDECL(int) RTEnvSetBad(const char *pszVar, const char *pszValue)
    147345{
     346#ifndef IPRT_NO_CRT
    148347    AssertMsgReturn(strchr(pszVar, '=') == NULL, ("'%s'\n", pszVar), VERR_ENV_INVALID_VAR_NAME);
    149 
    150     /* make a local copy and feed it to putenv. */
    151     const size_t cchVar = strlen(pszVar);
    152     const size_t cchValue = strlen(pszValue);
    153     char *pszTmp = (char *)alloca(cchVar + cchValue + 2 + !*pszValue);
    154     memcpy(pszTmp, pszVar, cchVar);
    155     pszTmp[cchVar] = '=';
    156     if (*pszValue)
    157         memcpy(pszTmp + cchVar + 1, pszValue, cchValue + 1);
     348    int rc;
     349    if (!RTEnvExist(pszVar))
     350        rc = VINF_ENV_VAR_NOT_FOUND;
    158351    else
    159352    {
    160         pszTmp[cchVar + 1] = ' '; /* wrong, but putenv will remove it otherwise. */
    161         pszTmp[cchVar + 2] = '\0';
    162     }
    163 
    164     if (!putenv(pszTmp))
    165         return 0;
    166     return RTErrConvertFromErrno(errno);
     353        errno_t rcErrno = _putenv_s(pszVar, *pszValue ? pszValue : " " /* wrong, but will be treated as unset otherwise */);
     354        if (rcErrno == 0)
     355            rc = VINF_SUCCESS;
     356        else
     357            rc = RTErrConvertFromErrno(rcErrno);
     358    }
     359    return rc;
     360#else
     361    return RTEnvSetUtf8(pszVar, pszValue);
     362#endif
    167363}
    168364
     
    170366RTDECL(int) RTEnvSet(const char *pszVar, const char *pszValue)
    171367{
     368#ifndef IPRT_NO_CRT
    172369    return RTEnvSetBad(pszVar, pszValue);
     370#else
     371    return RTEnvSetUtf8(pszVar, pszValue);
     372#endif
    173373}
    174374
     
    179379int rtEnvSetUtf8Worker(const char *pchVar, size_t cchVar, const char *pszValue)
    180380{
    181     size_t cwcVar;
    182     int rc = RTStrCalcUtf16LenEx(pchVar, cchVar, &cwcVar);
     381    PRTUTF16 pwszVar;
     382    int rc = RTStrToUtf16Ex(pchVar, cchVar, &pwszVar, 0, NULL);
    183383    if (RT_SUCCESS(rc))
    184384    {
    185         size_t cwcValue;
    186         rc = RTStrCalcUtf16LenEx(pszValue, RTSTR_MAX, &cwcValue);
     385        PRTUTF16 pwszValue;
     386        rc = RTStrToUtf16(pszValue, &pwszValue);
    187387        if (RT_SUCCESS(rc))
    188388        {
    189             PRTUTF16 pwszTmp = (PRTUTF16)RTMemTmpAlloc((cwcVar + 1 + cwcValue + 1) * sizeof(RTUTF16));
    190             if (pwszTmp)
    191             {
    192                 rc = RTStrToUtf16Ex(pchVar, cchVar, &pwszTmp, cwcVar + 1, NULL);
    193                 if (RT_SUCCESS(rc))
    194                 {
    195                     PRTUTF16 pwszTmpValue = &pwszTmp[cwcVar];
    196                     *pwszTmpValue++ = '=';
    197                     rc = RTStrToUtf16Ex(pszValue, RTSTR_MAX, &pwszTmpValue, cwcValue + 1, NULL);
    198                     if (RT_SUCCESS(rc))
    199                     {
    200                         if (!_wputenv(pwszTmp))
    201                             rc = VINF_SUCCESS;
    202                         else
    203                             rc = RTErrConvertFromErrno(errno);
    204                     }
    205                 }
    206                 RTMemTmpFree(pwszTmp);
    207             }
    208             else
    209                 rc = VERR_NO_TMP_MEMORY;
    210         }
     389#ifndef IPRT_NO_CRT
     390            errno_t rcErrno = _wputenv_s(pwszVar,
     391                                         *pwszValue ? pwszValue : L" " /* wrong, but will be treated as unset otherwise */);
     392            if (rcErrno == 0)
     393                rc = VINF_SUCCESS;
     394            else
     395                rc = RTErrConvertFromErrno(rcErrno);
     396#else
     397            SetLastError(ERROR_OUTOFMEMORY); /* The API did not always set the last error. */
     398            if (SetEnvironmentVariableW(pwszVar, pwszValue))
     399                rc = VINF_SUCCESS;
     400            else
     401                rc = RTErrConvertFromWin32(GetLastError());
     402#endif
     403            RTUtf16Free(pwszValue);
     404        }
     405        RTUtf16Free(pwszVar);
    211406    }
    212407    return rc;
     
    224419RTDECL(int) RTEnvUnsetBad(const char *pszVar)
    225420{
     421#ifndef IPRT_NO_CRT
    226422    AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
    227 
    228     /*
    229      * Check that it exists first.
    230      */
     423    int rc;
    231424    if (!RTEnvExist(pszVar))
    232         return VINF_ENV_VAR_NOT_FOUND;
    233 
    234     /*
    235      * Ok, try remove it.
    236      */
    237 #ifdef RT_OS_WINDOWS
    238     /* Use putenv(var=) since Windows does not have unsetenv(). */
    239     size_t cchVar = strlen(pszVar);
    240     char *pszBuf = (char *)alloca(cchVar + 2);
    241     memcpy(pszBuf, pszVar, cchVar);
    242     pszBuf[cchVar]     = '=';
    243     pszBuf[cchVar + 1] = '\0';
    244 
    245     if (!putenv(pszBuf))
    246         return VINF_SUCCESS;
    247 
    248 #else
    249     /* This is the preferred function as putenv() like used above does neither work on Solaris nor on Darwin. */
    250     if (!unsetenv((char*)pszVar))
    251         return VINF_SUCCESS;
    252 #endif
    253 
    254     return RTErrConvertFromErrno(errno);
     425        rc = VINF_ENV_VAR_NOT_FOUND;
     426    else
     427    {
     428        errno_t rcErrno = _putenv_s(pszVar, NULL);
     429        if (rcErrno == 0)
     430            rc = VINF_SUCCESS;
     431        else
     432            rc = RTErrConvertFromErrno(rcErrno);
     433    }
     434    return rc;
     435#else
     436    return RTEnvUnsetUtf8(pszVar);
     437#endif
    255438}
    256439
     
    258441RTDECL(int) RTEnvUnset(const char *pszVar)
    259442{
     443#ifndef IPRT_NO_CRT
    260444    return RTEnvUnsetBad(pszVar);
     445#else
     446    return RTEnvUnsetUtf8(pszVar);
     447#endif
    261448}
    262449
     
    266453    AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
    267454
    268     size_t cwcVar;
    269     int rc = RTStrCalcUtf16LenEx(pszVar, RTSTR_MAX, &cwcVar);
     455    PRTUTF16 pwszVar;
     456    int rc = RTStrToUtf16(pszVar, &pwszVar);
    270457    if (RT_SUCCESS(rc))
    271458    {
    272         PRTUTF16 pwszTmp = (PRTUTF16)RTMemTmpAlloc((cwcVar + 1 + 1) * sizeof(RTUTF16));
    273         if (pwszTmp)
    274         {
    275             rc = RTStrToUtf16Ex(pszVar, RTSTR_MAX, &pwszTmp, cwcVar + 1, NULL);
    276             if (RT_SUCCESS(rc))
    277             {
    278                 pwszTmp[cwcVar] = '=';
    279                 pwszTmp[cwcVar + 1] = '\0';
    280                 if (!_wputenv(pwszTmp))
    281                     rc = VINF_SUCCESS;
    282                 else
    283                     rc = RTErrConvertFromErrno(errno);
    284             }
    285             RTMemTmpFree(pwszTmp);
    286         }
    287     }
    288     return rc;
    289 }
    290 
     459#ifndef IPRT_NO_CRT
     460        if (_wgetenv(pwszVar))
     461        {
     462            errno_t rcErrno = _wputenv_s(pwszVar, NULL);
     463            if (rcErrno == 0)
     464                rc = VINF_SUCCESS;
     465            else
     466                rc = RTErrConvertFromErrno(rcErrno);
     467        }
     468        else
     469            rc = VINF_ENV_VAR_NOT_FOUND;
     470#else
     471        SetLastError(ERROR_ENVVAR_NOT_FOUND); /* The API did not always set the last error. */
     472        if (SetEnvironmentVariableW(pwszVar, NULL))
     473            rc = VINF_SUCCESS;
     474        else
     475        {
     476            DWORD dwErr = GetLastError();
     477            rc = dwErr == ERROR_ENVVAR_NOT_FOUND ? VINF_ENV_VAR_NOT_FOUND : RTErrConvertFromWin32(dwErr);
     478        }
     479#endif
     480        RTUtf16Free(pwszVar);
     481    }
     482    return rc;
     483}
     484
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