VirtualBox

Changeset 66594 in vbox for trunk/src/VBox/Runtime/common


Ignore:
Timestamp:
Apr 17, 2017 3:29:05 PM (8 years ago)
Author:
vboxsync
Message:

iprt: Reworked and implemented VFS chains, adding stdfile, gzip and gunzip element provider/factories.

Location:
trunk/src/VBox/Runtime/common
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/misc/message.cpp

    r62477 r66594  
    138138    return enmExitCode;
    139139}
     140RT_EXPORT_SYMBOL(RTMsgErrorExit);
     141
     142
     143RTDECL(RTEXITCODE) RTMsgErrorExitV(RTEXITCODE enmExitCode, const char *pszFormat, va_list va)
     144{
     145    RTMsgErrorV(pszFormat, va);
     146    return enmExitCode;
     147}
    140148RT_EXPORT_SYMBOL(RTMsgErrorExitV);
    141149
    142150
    143 RTDECL(RTEXITCODE) RTMsgErrorExitV(RTEXITCODE enmExitCode, const char *pszFormat, va_list va)
    144 {
    145     RTMsgErrorV(pszFormat, va);
    146     return enmExitCode;
    147 }
    148 RT_EXPORT_SYMBOL(RTMsgErrorExitV);
     151RTDECL(RTEXITCODE) RTMsgErrorExitFailure(const char *pszFormat, ...)
     152{
     153    va_list va;
     154    va_start(va, pszFormat);
     155    RTMsgErrorV(pszFormat, va);
     156    va_end(va);
     157    return RTEXITCODE_FAILURE;
     158}
     159RT_EXPORT_SYMBOL(RTMsgErrorExitFailure);
     160
     161
     162RTDECL(RTEXITCODE) RTMsgErrorExitFailureV(const char *pszFormat, va_list va)
     163{
     164    RTMsgErrorV(pszFormat, va);
     165    return RTEXITCODE_FAILURE;
     166}
     167RT_EXPORT_SYMBOL(RTMsgErrorExitFailureV);
    149168
    150169
  • trunk/src/VBox/Runtime/common/vfs/vfschain.cpp

    r62477 r66594  
    5353*********************************************************************************************************************************/
    5454/** Init the critical section once. */
    55 static RTONCE       g_rtVfsChainElementInitOnce;
     55static RTONCE       g_rtVfsChainElementInitOnce = RTONCE_INITIALIZER;
    5656/** Critical section protecting g_rtVfsChainElementProviderList. */
    57 static RTCRITSECT  g_rtVfsChainElementCritSect;
     57static RTCRITSECTRW g_rtVfsChainElementCritSect;
    5858/** List of VFS chain element providers (RTVFSCHAINELEMENTREG). */
    5959static RTLISTANCHOR g_rtVfsChainElementProviderList;
    6060
    6161
     62
     63
    6264/**
    6365 * Initializes the globals via RTOnce.
     
    6971{
    7072    NOREF(pvUser);
    71     return RTCritSectInit(&g_rtVfsChainElementCritSect);
     73    if (!g_rtVfsChainElementProviderList.pNext)
     74        RTListInit(&g_rtVfsChainElementProviderList);
     75    int rc = RTCritSectRwInit(&g_rtVfsChainElementCritSect);
     76    if (RT_SUCCESS(rc))
     77    {
     78    }
     79    return rc;
    7280}
    7381
     
    8492    AssertMsgReturn(pRegRec->uEndMarker == RTVFSCHAINELEMENTREG_VERSION, ("%#zx", pRegRec->uEndMarker), VERR_INVALID_POINTER);
    8593    AssertReturn(pRegRec->fReserved == 0, VERR_INVALID_POINTER);
    86     AssertPtrReturn(pRegRec->pszName, VERR_INVALID_POINTER);
    87     AssertPtrNullReturn(pRegRec->pfnOpenVfs,      VERR_INVALID_POINTER);
    88     AssertPtrNullReturn(pRegRec->pfnOpenDir,      VERR_INVALID_POINTER);
    89     AssertPtrNullReturn(pRegRec->pfnOpenFile,     VERR_INVALID_POINTER);
    90     AssertPtrNullReturn(pRegRec->pfnOpenIoStream, VERR_INVALID_POINTER);
    91     AssertPtrNullReturn(pRegRec->pfnOpenFsStream, VERR_INVALID_POINTER);
     94    AssertPtrReturn(pRegRec->pszName,               VERR_INVALID_POINTER);
     95    AssertPtrReturn(pRegRec->pfnValidate,           VERR_INVALID_POINTER);
     96    AssertPtrReturn(pRegRec->pfnInstantiate,        VERR_INVALID_POINTER);
     97    AssertPtrReturn(pRegRec->pfnCanReuseElement,    VERR_INVALID_POINTER);
    9298
    9399    /*
     
    99105        if (RT_FAILURE(rc))
    100106            return rc;
    101         rc = RTCritSectEnter(&g_rtVfsChainElementCritSect);
     107        rc = RTCritSectRwEnterExcl(&g_rtVfsChainElementCritSect);
    102108        if (RT_FAILURE(rc))
    103109            return rc;
    104110    }
     111    else if (!g_rtVfsChainElementProviderList.pNext)
     112        RTListInit(&g_rtVfsChainElementProviderList);
    105113
    106114    /*
     
    129137     */
    130138    if (!fFromCtor)
    131         RTCritSectLeave(&g_rtVfsChainElementCritSect);
     139        RTCritSectRwLeaveExcl(&g_rtVfsChainElementCritSect);
    132140    return rc;
    133141}
     
    144152    if (pSpec)
    145153    {
    146         pSpec->iActionElement = UINT32_MAX;
     154        pSpec->fOpenFile      = 0;
     155        pSpec->fOpenDir       = 0;
    147156        pSpec->cElements      = 0;
     157        pSpec->uProvider      = 0;
    148158        pSpec->paElements     = NULL;
    149159    }
     
    213223 *                      code shorter. -lazy bird)
    214224 */
    215 static PRTVFSCHAINELEMSPEC rtVfsChainSpecAddElement(PRTVFSCHAINSPEC pSpec, int *prc)
     225static PRTVFSCHAINELEMSPEC rtVfsChainSpecAddElement(PRTVFSCHAINSPEC pSpec, uint16_t offSpec, int *prc)
    216226{
    217227    AssertPtr(pSpec);
     
    240250    PRTVFSCHAINELEMSPEC pElement = &pSpec->paElements[iElement];
    241251    pElement->pszProvider = NULL;
    242     pElement->enmTypeIn   = iElement ? pSpec->paElements[iElement - 1].enmTypeOut : RTVFSOBJTYPE_INVALID;
    243     pElement->enmTypeOut  = RTVFSOBJTYPE_INVALID;
    244     pElement->enmAction   = RTVFSCHAINACTION_INVALID;
     252    pElement->enmTypeIn   = iElement ? pSpec->paElements[iElement - 1].enmType : RTVFSOBJTYPE_INVALID;
     253    pElement->enmType     = RTVFSOBJTYPE_INVALID;
     254    pElement->offSpec     = offSpec;
     255    pElement->cchSpec     = 0;
    245256    pElement->cArgs       = 0;
    246     pElement->papszArgs   = 0;
    247 
    248     pSpec->cElements  = iElement + 1;
     257    pElement->paArgs      = NULL;
     258    pElement->pProvider   = NULL;
     259    pElement->hVfsObj     = NIL_RTVFSOBJ;
     260
     261    pSpec->cElements = iElement + 1;
    249262    return pElement;
    250263}
     
    260273 *                              sequences counted twice.
    261274 */
    262 static int rtVfsChainSpecElementAddArg(PRTVFSCHAINELEMSPEC pElement, const char *psz, size_t cch)
     275static int rtVfsChainSpecElementAddArg(PRTVFSCHAINELEMSPEC pElement, const char *psz, size_t cch, uint16_t offSpec)
    263276{
    264277    uint32_t iArg = pElement->cArgs;
    265278    if ((iArg % 32) == 0)
    266279    {
    267         char **papszNew = (char **)RTMemTmpAlloc((iArg + 32 + 1) * sizeof(papszNew[0]));
    268         if (!papszNew)
     280        PRTVFSCHAINELEMENTARG paNew = (PRTVFSCHAINELEMENTARG)RTMemTmpAlloc((iArg + 32) * sizeof(paNew[0]));
     281        if (!paNew)
    269282            return VERR_NO_TMP_MEMORY;
    270         memcpy(papszNew, pElement->papszArgs, iArg * sizeof(papszNew[0]));
    271         RTMemTmpFree(pElement->papszArgs);
    272         pElement->papszArgs = papszNew;
     283        memcpy(paNew, pElement->paArgs, iArg * sizeof(paNew[0]));
     284        RTMemTmpFree(pElement->paArgs);
     285        pElement->paArgs = paNew;
    273286    }
    274287
    275288    int rc = VINF_SUCCESS;
    276     pElement->papszArgs[iArg] = rtVfsChainSpecDupStrN(psz, cch, &rc);
    277     pElement->papszArgs[iArg + 1] = NULL;
     289    pElement->paArgs[iArg].psz    = rtVfsChainSpecDupStrN(psz, cch, &rc);
     290    pElement->paArgs[iArg].offSpec = offSpec;
    278291    pElement->cArgs = iArg + 1;
    279292    return rc;
     
    291304        uint32_t iArg = pSpec->paElements[i].cArgs;
    292305        while (iArg-- > 0)
    293             RTMemTmpFree(pSpec->paElements[i].papszArgs[iArg]);
    294         RTMemTmpFree(pSpec->paElements[i].papszArgs);
     306            RTMemTmpFree(pSpec->paElements[i].paArgs[iArg].psz);
     307        RTMemTmpFree(pSpec->paElements[i].paArgs);
    295308        RTMemTmpFree(pSpec->paElements[i].pszProvider);
     309        if (pSpec->paElements[i].hVfsObj != NIL_RTVFSOBJ)
     310        {
     311            RTVfsObjRelease(pSpec->paElements[i].hVfsObj);
     312            pSpec->paElements[i].hVfsObj = NIL_RTVFSOBJ;
     313        }
    296314    }
    297315
     
    326344}
    327345
    328 /**
    329  * Look for action.
    330  *
    331  * @returns Action.
    332  * @param   pszSpec             The current spec position.
    333  * @param   pcchAction          Where to return the length of the action
    334  *                              string.
    335  */
    336 static RTVFSCHAINACTION rtVfsChainSpecEatAction(const char *pszSpec, size_t *pcchAction)
    337 {
    338     switch (*pszSpec)
    339     {
    340         case '|':
    341             *pcchAction = 1;
    342             return RTVFSCHAINACTION_PASSIVE;
    343         case '>':
    344             *pcchAction = 1;
    345             return RTVFSCHAINACTION_PUSH;
    346         default:
    347             *pcchAction = 0;
    348             return RTVFSCHAINACTION_NONE;
    349     }
    350 }
    351 
    352 
    353 RTDECL(int)     RTVfsChainSpecParse(const char *pszSpec, uint32_t fFlags, RTVFSCHAINACTION enmLeadingAction,
    354                                     RTVFSCHAINACTION enmTrailingAction,
    355                                     PRTVFSCHAINSPEC *ppSpec, const char **ppszError)
     346
     347RTDECL(int) RTVfsChainSpecParse(const char *pszSpec, uint32_t fFlags, RTVFSOBJTYPE enmDesiredType,
     348                                PRTVFSCHAINSPEC *ppSpec, const char **ppszError)
    356349{
    357350    AssertPtrNullReturn(ppszError, VERR_INVALID_POINTER);
     
    362355    AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
    363356    AssertReturn(!(fFlags & ~RTVFSCHAIN_PF_VALID_MASK), VERR_INVALID_PARAMETER);
    364     AssertReturn(enmLeadingAction > RTVFSCHAINACTION_INVALID && enmLeadingAction < RTVFSCHAINACTION_END, VERR_INVALID_PARAMETER);
     357    AssertReturn(enmDesiredType > RTVFSOBJTYPE_INVALID && enmDesiredType < RTVFSOBJTYPE_END, VERR_INVALID_PARAMETER);
    365358
    366359    /*
     
    369362    if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
    370363        return VERR_VFS_CHAIN_NO_PREFIX;
    371     pszSpec = RTStrStripL(pszSpec + sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1);
    372     if (!*pszSpec)
     364    const char *pszSrc = RTStrStripL(pszSpec + sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1);
     365    if (!*pszSrc)
    373366        return VERR_VFS_CHAIN_EMPTY;
    374367
     
    376369    if (!pSpec)
    377370        return VERR_NO_TMP_MEMORY;
     371    pSpec->enmDesiredType = enmDesiredType;
    378372
    379373    /*
    380374     * Parse the spec one element at a time.
    381375     */
    382     int         rc     = VINF_SUCCESS;
    383     const char *pszSrc = pszSpec;
     376    int rc = VINF_SUCCESS;
    384377    while (*pszSrc && RT_SUCCESS(rc))
    385378    {
    386379        /*
    387          * Pipe or redirection action symbol, except maybe the first time.
    388          * The pipe symbol may occur at the end of the spec.
     380         * Digest element separator, except for the first element.
    389381         */
    390         size_t           cch;
    391         RTVFSCHAINACTION enmAction = rtVfsChainSpecEatAction(pszSpec, &cch);
    392         if (enmAction != RTVFSCHAINACTION_NONE)
    393         {
    394             pszSrc = RTStrStripL(pszSrc + cch);
    395             if (!*pszSrc)
     382        if (*pszSrc == '|' || *pszSrc == ':')
     383        {
     384            if (pSpec->cElements != 0)
     385                pszSrc = RTStrStripL(pszSrc + 1);
     386            else
    396387            {
    397                 /* Fail if the caller does not approve of a trailing pipe (all
    398                    other actions non-trailing). */
    399                 if (   enmAction != enmTrailingAction
    400                     && !(fFlags & RTVFSCHAIN_PF_TRAILING_ACTION_OPTIONAL))
    401                     rc = VERR_VFS_CHAIN_EXPECTED_ELEMENT;
     388                rc = VERR_VFS_CHAIN_LEADING_SEPARATOR;
    402389                break;
    403390            }
    404 
    405             /* There can only be one real action atm. */
    406             if (enmAction != RTVFSCHAINACTION_PASSIVE)
    407             {
    408                 if (pSpec->iActionElement != UINT32_MAX)
    409                 {
    410                     rc = VERR_VFS_CHAIN_MULTIPLE_ACTIONS;
    411                     break;
    412                 }
    413                 pSpec->iActionElement = pSpec->cElements;
    414             }
    415         }
    416         else if (pSpec->cElements > 0)
    417         {
    418             rc = VERR_VFS_CHAIN_EXPECTED_ACTION;
    419             break;
    420         }
    421 
    422         /* Check the leading action. */
    423         if (   pSpec->cElements == 0
    424             && enmAction != enmLeadingAction
    425             && !(fFlags & RTVFSCHAIN_PF_LEADING_ACTION_OPTIONAL))
    426         {
    427             rc = VERR_VFS_CHAIN_UNEXPECTED_ACTION_TYPE;
     391        }
     392        else if (pSpec->cElements != 0)
     393        {
     394            rc = VERR_VFS_CHAIN_EXPECTED_SEPARATOR;
    428395            break;
    429396        }
     
    432399         * Ok, there should be an element here so add one to the return struct.
    433400         */
    434         PRTVFSCHAINELEMSPEC pElement = rtVfsChainSpecAddElement(pSpec, &rc);
     401        PRTVFSCHAINELEMSPEC pElement = rtVfsChainSpecAddElement(pSpec, (uint16_t)(pszSrc - pszSpec), &rc);
    435402        if (!pElement)
    436403            break;
    437         pElement->enmAction = enmAction;
    438404
    439405        /*
    440          * First up is the VFS object type followed by a parentheses.
     406         * First up is the VFS object type followed by a parentheses,
     407         * or this could be the trailing action.
    441408         */
     409        size_t cch;
    442410        if (strncmp(pszSrc, "base", cch = 4) == 0)
    443             pElement->enmTypeOut = RTVFSOBJTYPE_BASE;
     411            pElement->enmType = RTVFSOBJTYPE_BASE;
    444412        else if (strncmp(pszSrc, "vfs",  cch = 3) == 0)
    445             pElement->enmTypeOut = RTVFSOBJTYPE_VFS;
     413            pElement->enmType = RTVFSOBJTYPE_VFS;
    446414        else if (strncmp(pszSrc, "fss",  cch = 3) == 0)
    447             pElement->enmTypeOut = RTVFSOBJTYPE_FS_STREAM;
     415            pElement->enmType = RTVFSOBJTYPE_FS_STREAM;
    448416        else if (strncmp(pszSrc, "ios",  cch = 3) == 0)
    449             pElement->enmTypeOut = RTVFSOBJTYPE_IO_STREAM;
     417            pElement->enmType = RTVFSOBJTYPE_IO_STREAM;
    450418        else if (strncmp(pszSrc, "dir",  cch = 3) == 0)
    451             pElement->enmTypeOut = RTVFSOBJTYPE_DIR;
     419            pElement->enmType = RTVFSOBJTYPE_DIR;
    452420        else if (strncmp(pszSrc, "file", cch = 4) == 0)
    453             pElement->enmTypeOut = RTVFSOBJTYPE_FILE;
     421            pElement->enmType = RTVFSOBJTYPE_FILE;
    454422        else if (strncmp(pszSrc, "sym",  cch = 3) == 0)
    455             pElement->enmTypeOut = RTVFSOBJTYPE_SYMLINK;
     423            pElement->enmType = RTVFSOBJTYPE_SYMLINK;
    456424        else
    457425        {
    458             rc = VERR_VFS_CHAIN_UNKNOWN_TYPE;
     426            if (*pszSrc == '\0')
     427                rc = VERR_VFS_CHAIN_TRAILING_SEPARATOR;
     428            else
     429                rc = VERR_VFS_CHAIN_UNKNOWN_TYPE;
    459430            break;
    460431        }
     
    490461            pszSrc = RTStrStripL(pszSrc + 1);
    491462            cch = rtVfsChainSpecFindArgEnd(pszSrc);
    492             rc = rtVfsChainSpecElementAddArg(pElement, pszSrc, cch);
     463            rc = rtVfsChainSpecElementAddArg(pElement, pszSrc, cch, (uint16_t)(pszSrc - pszSpec));
     464            if (RT_FAILURE(rc))
     465                break;
    493466            pszSrc += cch;
    494467        }
     468        if (RT_FAILURE(rc))
     469            break;
    495470
    496471        /* Must end with a right parentheses. */
     
    500475            break;
    501476        }
     477        pElement->cchSpec = (uint16_t)(pszSrc - pszSpec) - pElement->offSpec + 1;
     478
    502479        pszSrc = RTStrStripL(pszSrc + 1);
    503480    }
     481
     482#if 1
     483    RTAssertMsg2("dbg: cElements=%d rc=%Rrc\n", pSpec->cElements, rc);
     484    for (uint32_t i = 0; i < pSpec->cElements; i++)
     485    {
     486        uint32_t const cArgs = pSpec->paElements[i].cArgs;
     487        RTAssertMsg2("dbg: #%u: enmTypeIn=%d enmType=%d cArgs=%d",
     488                     i, pSpec->paElements[i].enmTypeIn, pSpec->paElements[i].enmType, cArgs);
     489        for (uint32_t j = 0; j < cArgs; j++)
     490            RTAssertMsg2(j == 0 ? (cArgs > 1 ? " [%s" : " [%s]") : j + 1 < cArgs ? ", %s" : ", %s]",
     491                         pSpec->paElements[i].paArgs[j].psz);
     492        //RTAssertMsg2(" offSpec=%d cchSpec=%d", pSpec->paElements[i].offSpec, pSpec->paElements[i].cchSpec);
     493        RTAssertMsg2(" spec: %.*s\n", pSpec->paElements[i].cchSpec, &pszSpec[pSpec->paElements[i].offSpec]);
     494    }
     495#endif
    504496
    505497    /*
     
    519511
    520512
    521 
     513/**
     514 * Looks up @a pszProvider among the registered providers.
     515 *
     516 * @returns Pointer to registration record if found, NULL if not.
     517 * @param   pszProvider         The provider.
     518 */
     519static PCRTVFSCHAINELEMENTREG rtVfsChainFindProviderLocked(const char *pszProvider)
     520{
     521    PCRTVFSCHAINELEMENTREG pIterator;
     522    RTListForEach(&g_rtVfsChainElementProviderList, pIterator, RTVFSCHAINELEMENTREG, ListEntry)
     523    {
     524        if (strcmp(pIterator->pszName, pszProvider) == 0)
     525            return pIterator;
     526    }
     527    return NULL;
     528}
     529
     530
     531/**
     532 * Does reusable object type matching.
     533 *
     534 * @returns true if the types matches, false if not.
     535 * @param   pElement        The target element specification.
     536 * @param   pReuseElement   The source element specification.
     537 */
     538static bool rtVfsChainMatchReusableType(PRTVFSCHAINELEMSPEC pElement, PRTVFSCHAINELEMSPEC pReuseElement)
     539{
     540    if (pElement->enmType == pReuseElement->enmType)
     541        return true;
     542
     543    /* File objects can always be cast to I/O streams.  */
     544    if (   pElement->enmType == RTVFSOBJTYPE_IO_STREAM
     545        && pReuseElement->enmType == RTVFSOBJTYPE_FILE)
     546        return true;
     547
     548    /* I/O stream objects may be file objects. */
     549    if (   pElement->enmType == RTVFSOBJTYPE_FILE
     550        && pReuseElement->enmType == RTVFSOBJTYPE_IO_STREAM)
     551    {
     552        RTVFSFILE hVfsFile = RTVfsObjToFile(pReuseElement->hVfsObj);
     553        if (hVfsFile != NIL_RTVFSFILE)
     554        {
     555            RTVfsFileRelease(hVfsFile);
     556            return true;
     557        }
     558    }
     559    return false;
     560}
     561
     562
     563RTDECL(int) RTVfsChainSpecCheckAndSetup(PRTVFSCHAINSPEC pSpec, PCRTVFSCHAINSPEC pReuseSpec,
     564                                        PRTVFSOBJ phVfsObj, uint32_t *poffError)
     565{
     566    AssertPtrReturn(poffError, VERR_INVALID_POINTER);
     567    *poffError = 0;
     568    AssertPtrReturn(phVfsObj, VERR_INVALID_POINTER);
     569    *phVfsObj = NIL_RTVFSOBJ;
     570    AssertPtrReturn(pSpec, VERR_INVALID_POINTER);
     571
     572    /*
     573     * Enter the critical section after making sure it has been initialized.
     574     */
     575    int rc = RTOnce(&g_rtVfsChainElementInitOnce, rtVfsChainElementRegisterInit, NULL);
     576    if (RT_SUCCESS(rc))
     577        rc = RTCritSectRwEnterShared(&g_rtVfsChainElementCritSect);
     578    if (RT_SUCCESS(rc))
     579    {
     580        /*
     581         * Resolve and check each element first.
     582         */
     583        for (uint32_t i = 0; i < pSpec->cElements; i++)
     584        {
     585            PRTVFSCHAINELEMSPEC const pElement = &pSpec->paElements[i];
     586            *poffError = pElement->offSpec;
     587            pElement->pProvider = rtVfsChainFindProviderLocked(pElement->pszProvider);
     588            if (pElement->pProvider)
     589            {
     590                rc = pElement->pProvider->pfnValidate(pElement->pProvider, pSpec, pElement, poffError);
     591                if (RT_SUCCESS(rc))
     592                    continue;
     593            }
     594            else
     595                rc = VERR_VFS_CHAIN_PROVIDER_NOT_FOUND;
     596            break;
     597        }
     598
     599        /*
     600         * Check that the desired type is compatible with the last element.
     601         */
     602        if (RT_SUCCESS(rc))
     603        {
     604            if (pSpec->cElements > 0) /* paranoia */
     605            {
     606                PRTVFSCHAINELEMSPEC const pLast = &pSpec->paElements[pSpec->cElements - 1];
     607                if (   pLast->enmType == pSpec->enmDesiredType
     608                    || (   pLast->enmType == RTVFSOBJTYPE_FILE
     609                        && pSpec->enmDesiredType == RTVFSOBJTYPE_IO_STREAM) )
     610                    rc = VINF_SUCCESS;
     611                else
     612                {
     613                    *poffError = pLast->offSpec;
     614                    rc = VERR_VFS_CHAIN_FINAL_TYPE_MISMATCH;
     615                }
     616            }
     617            else
     618                rc = VERR_VFS_CHAIN_EMPTY;
     619        }
     620
     621        if (RT_SUCCESS(rc))
     622        {
     623            /*
     624             * Try construct the chain.
     625             */
     626            RTVFSOBJ hPrevVfsObj = NIL_RTVFSOBJ; /* No extra reference, kept in chain structure. */
     627            for (uint32_t i = 0; i < pSpec->cElements; i++)
     628            {
     629                PRTVFSCHAINELEMSPEC const pElement = &pSpec->paElements[i];
     630
     631                /*
     632                 * Try reuse the VFS objects at the start of the passed in reuse chain.
     633                 */
     634                if (!pReuseSpec)
     635                { /* likely */ }
     636                else
     637                {
     638                    if (i < pReuseSpec->cElements)
     639                    {
     640                        PRTVFSCHAINELEMSPEC const pReuseElement = &pReuseSpec->paElements[i];
     641                        if (pReuseElement->hVfsObj != NIL_RTVFSOBJ)
     642                        {
     643                            if (strcmp(pElement->pszProvider, pReuseElement->pszProvider) == 0)
     644                            {
     645                                if (rtVfsChainMatchReusableType(pElement, pReuseElement))
     646                                {
     647                                    if (pElement->pProvider->pfnCanReuseElement(pElement->pProvider, pSpec, pElement,
     648                                                                                pReuseSpec, pReuseElement))
     649                                    {
     650                                        uint32_t cRefs = RTVfsObjRetain(pReuseElement->hVfsObj);
     651                                        if (cRefs != UINT32_MAX)
     652                                        {
     653                                            pElement->hVfsObj = hPrevVfsObj = pReuseElement->hVfsObj;
     654                                            continue;
     655                                        }
     656                                    }
     657                                }
     658                            }
     659                        }
     660                    }
     661                    pReuseSpec = NULL;
     662                }
     663
     664                /*
     665                 * Instantiate a new VFS object.
     666                 */
     667                RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
     668                rc = pElement->pProvider->pfnInstantiate(pElement->pProvider, pSpec, pElement, hPrevVfsObj, &hVfsObj, poffError);
     669                if (RT_FAILURE(rc))
     670                    break;
     671                pElement->hVfsObj = hVfsObj;
     672                hPrevVfsObj = hVfsObj;
     673            }
     674
     675            /*
     676             * Add another reference to the final object and return.
     677             */
     678            if (RT_SUCCESS(rc))
     679            {
     680                uint32_t cRefs = RTVfsObjRetain(hPrevVfsObj);
     681                AssertStmt(cRefs != UINT32_MAX, rc = VERR_VFS_CHAIN_IPE);
     682                *phVfsObj = hPrevVfsObj;
     683            }
     684        }
     685
     686        int rc2 = RTCritSectRwLeaveShared(&g_rtVfsChainElementCritSect);
     687        if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
     688            rc = rc2;
     689    }
     690    return rc;
     691}
    522692
    523693
     
    538708     */
    539709    if (!fFromDtor)
    540         RTCritSectEnter(&g_rtVfsChainElementCritSect);
     710        RTCritSectRwEnterExcl(&g_rtVfsChainElementCritSect);
     711    else if (!g_rtVfsChainElementProviderList.pNext)
     712        RTListInit(&g_rtVfsChainElementProviderList);
    541713
    542714    /*
     
    559731     */
    560732    if (!fFromDtor)
    561         RTCritSectLeave(&g_rtVfsChainElementCritSect);
     733        RTCritSectRwLeaveExcl(&g_rtVfsChainElementCritSect);
    562734    return rc;
    563735}
     
    593765    {
    594766        PRTVFSCHAINSPEC pSpec;
    595         rc = RTVfsChainSpecParse(pszSpec,
    596                                    RTVFSCHAIN_PF_NO_REAL_ACTION
    597                                  | RTVFSCHAIN_PF_LEADING_ACTION_OPTIONAL,
    598                                  RTVFSCHAINACTION_PASSIVE,
    599                                  RTVFSCHAINACTION_NONE,
    600                                  &pSpec,
    601                                  ppszError);
     767        rc = RTVfsChainSpecParse(pszSpec,  0 /*fFlags*/, RTVFSOBJTYPE_FILE, &pSpec, ppszError);
    602768        if (RT_SUCCESS(rc))
    603769        {
    604             /** @todo implement this when needed. */
    605             rc = VERR_NOT_IMPLEMENTED;
     770            pSpec->fOpenFile = fOpen;
     771
     772            uint32_t offError = 0;
     773            RTVFSOBJ hVfsObj  = NIL_RTVFSOBJ;
     774            rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &offError);
     775            if (RT_SUCCESS(rc))
     776            {
     777                *phVfsFile = RTVfsObjToFile(hVfsObj);
     778                if (*phVfsFile)
     779                    rc = VINF_SUCCESS;
     780                else
     781                    rc = VERR_VFS_CHAIN_CAST_FAILED;
     782                RTVfsObjRelease(hVfsObj);
     783            }
     784            else if (ppszError)
     785                *ppszError = &pszSpec[offError];
     786
    606787            RTVfsChainSpecFree(pSpec);
    607788        }
     
    641822        }
    642823    }
     824    /*
     825     * Do the chain thing.
     826     */
    643827    else
    644828    {
    645829        PRTVFSCHAINSPEC pSpec;
    646         rc = RTVfsChainSpecParse(pszSpec,
    647                                    RTVFSCHAIN_PF_NO_REAL_ACTION
    648                                  | RTVFSCHAIN_PF_LEADING_ACTION_OPTIONAL,
    649                                  RTVFSCHAINACTION_PASSIVE,
    650                                  RTVFSCHAINACTION_NONE,
    651                                  &pSpec,
    652                                  ppszError);
     830        rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_IO_STREAM, &pSpec, ppszError);
    653831        if (RT_SUCCESS(rc))
    654832        {
    655 
    656 
    657             rc = VERR_NOT_IMPLEMENTED;
     833            pSpec->fOpenFile = fOpen;
     834
     835            uint32_t offError = 0;
     836            RTVFSOBJ hVfsObj  = NIL_RTVFSOBJ;
     837            rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &offError);
     838            if (RT_SUCCESS(rc))
     839            {
     840                *phVfsIos = RTVfsObjToIoStream(hVfsObj);
     841                if (*phVfsIos)
     842                    rc = VINF_SUCCESS;
     843                else
     844                    rc = VERR_VFS_CHAIN_CAST_FAILED;
     845                RTVfsObjRelease(hVfsObj);
     846            }
     847            else if (ppszError)
     848                *ppszError = &pszSpec[offError];
     849
    658850            RTVfsChainSpecFree(pSpec);
    659851        }
  • trunk/src/VBox/Runtime/common/vfs/vfsstdfile.cpp

    r62477 r66594  
    3232#include <iprt/vfslowlevel.h>
    3333
     34#include <iprt/assert.h>
    3435#include <iprt/err.h>
    3536#include <iprt/file.h>
    3637#include <iprt/poll.h>
     38#include <iprt/string.h>
    3739#include <iprt/thread.h>
    3840
     
    509511}
    510512
     513
     514
     515/**
     516 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
     517 */
     518static DECLCALLBACK(int) rtVfsChainStdFile_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
     519                                                    PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError)
     520{
     521    RT_NOREF(pProviderReg);
     522
     523    /*
     524     * Basic checks.
     525     */
     526    if (pElement->enmTypeIn != RTVFSOBJTYPE_INVALID)
     527        return VERR_VFS_CHAIN_MUST_BE_FIRST_ELEMENT;
     528    if (pElement->cArgs < 1)
     529        return VERR_VFS_CHAIN_AT_LEAST_ONE_ARG;
     530    if (pElement->cArgs > 4)
     531        return VERR_VFS_CHAIN_AT_MOST_FOUR_ARGS;
     532    if (!*pElement->paArgs[0].psz)
     533        return VERR_VFS_CHAIN_EMPTY_ARG;
     534    if (   pElement->enmType != RTVFSOBJTYPE_FILE
     535        && pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
     536        return VERR_VFS_CHAIN_ONLY_FILE_OR_IOS;
     537
     538    /*
     539     * Calculate the flags, storing them in the first argument.
     540     */
     541    const char *pszAccess = pElement->cArgs >= 2 ? pElement->paArgs[1].psz : "";
     542    if (!*pszAccess)
     543        pszAccess = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READWRITE ? "rw"
     544                  : (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ      ? "r"
     545                  : (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_WRITE     ? "w"
     546                  :                                                                   "rw";
     547
     548    const char *pszDisp = pElement->cArgs >= 3 ? pElement->paArgs[2].psz : "";
     549    if (!*pszDisp)
     550        pszDisp = strchr(pszAccess, 'w') != NULL ? "open-create" : "open";
     551
     552    const char *pszSharing = pElement->cArgs >= 4 ? pElement->paArgs[3].psz : "";
     553
     554    int rc = RTFileModeToFlagsEx(pszAccess, pszDisp, pszSharing, &pElement->paArgs[0].uProvider);
     555    if (RT_SUCCESS(rc))
     556        return VINF_SUCCESS;
     557
     558    /*
     559     * Now try figure out which argument offended us.
     560     */
     561    AssertReturn(pElement->cArgs > 1, VERR_VFS_CHAIN_IPE);
     562    if (   pElement->cArgs == 2
     563        || RT_FAILURE(RTFileModeToFlagsEx(pszAccess, "open-create", "", &pElement->paArgs[0].uProvider)))
     564        *poffError = pElement->paArgs[1].offSpec;
     565    else if (   pElement->cArgs == 3
     566             || RT_FAILURE(RTFileModeToFlagsEx(pszAccess, pszDisp, "", &pElement->paArgs[0].uProvider)))
     567        *poffError = pElement->paArgs[2].offSpec;
     568    else
     569        *poffError = pElement->paArgs[3].offSpec;
     570    return VERR_VFS_CHAIN_INVALID_ARGUMENT;
     571}
     572
     573
     574/**
     575 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
     576 */
     577static DECLCALLBACK(int) rtVfsChainStdFile_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
     578                                                       PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
     579                                                       PRTVFSOBJ phVfsObj, uint32_t *poffError)
     580{
     581    RT_NOREF(pProviderReg, pSpec, poffError);
     582    AssertReturn(hPrevVfsObj == NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
     583
     584    RTVFSFILE hVfsFile;
     585    int rc = RTVfsFileOpenNormal(pElement->paArgs[0].psz, pElement->paArgs[0].uProvider, &hVfsFile);
     586    if (RT_SUCCESS(rc))
     587    {
     588        *phVfsObj = RTVfsObjFromFile(hVfsFile);
     589        RTVfsFileRelease(hVfsFile);
     590        if (*phVfsObj != NIL_RTVFSOBJ)
     591            return VINF_SUCCESS;
     592        rc = VERR_VFS_CHAIN_CAST_FAILED;
     593    }
     594    return rc;
     595}
     596
     597
     598/**
     599 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
     600 */
     601static DECLCALLBACK(bool) rtVfsChainStdFile_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
     602                                                            PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
     603                                                            PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
     604{
     605    RT_NOREF(pProviderReg, pSpec, pReuseSpec);
     606    if (strcmp(pElement->paArgs[0].psz, pReuseElement->paArgs[0].psz) == 0)
     607        if (pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider)
     608            return true;
     609    return false;
     610}
     611
     612
     613/** VFS chain element 'file'. */
     614static RTVFSCHAINELEMENTREG g_rtVfsChainStdFileReg =
     615{
     616    /* uVersion = */            RTVFSCHAINELEMENTREG_VERSION,
     617    /* fReserved = */           0,
     618    /* pszName = */             "stdfile",
     619    /* ListEntry = */           { NULL, NULL },
     620    /* pszHelp = */             "Open a real file, providing either a file or an I/O stream object. Initial element.\n"
     621                                "First argument is the filename path.\n"
     622                                "Second argument is access mode, optional: r, w, rw.\n"
     623                                "Third argument is open disposition, optional: create, create-replace, open, open-create, open-append, open-truncate.\n"
     624                                "Forth argument is file sharing, optional: nr, nw, nrw, d.",
     625    /* pfnValidate = */         rtVfsChainStdFile_Validate,
     626    /* pfnInstantiate = */      rtVfsChainStdFile_Instantiate,
     627    /* pfnCanReuseElement = */  rtVfsChainStdFile_CanReuseElement,
     628    /* uEndMarker = */          RTVFSCHAINELEMENTREG_VERSION
     629};
     630
     631RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainStdFileReg, rtVfsChainStdFileReg);
     632
  • trunk/src/VBox/Runtime/common/zip/gzipvfs.cpp

    r62477 r66594  
    3333
    3434#include <iprt/assert.h>
     35#include <iprt/ctype.h>
    3536#include <iprt/file.h>
    3637#include <iprt/err.h>
     
    828829}
    829830
     831
     832
     833/**
     834 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
     835 */
     836static DECLCALLBACK(int) rtVfsChainGunzip_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
     837                                                   PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError)
     838{
     839    RT_NOREF(pProviderReg, poffError);
     840
     841    if (pElement->enmTypeIn == RTVFSOBJTYPE_INVALID)
     842        return VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT;
     843    if (pElement->cArgs != 0)
     844        return VERR_VFS_CHAIN_NO_ARGS;
     845    if (pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
     846        return VERR_VFS_CHAIN_ONLY_IOS;
     847    if (pSpec->fOpenFile & RTFILE_O_WRITE)
     848        return VERR_VFS_CHAIN_READ_ONLY_IOS;
     849
     850    return VINF_SUCCESS;
     851}
     852
     853
     854/**
     855 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
     856 */
     857static DECLCALLBACK(int) rtVfsChainGunzip_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
     858                                                      PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
     859                                                      PRTVFSOBJ phVfsObj, uint32_t *poffError)
     860{
     861    RT_NOREF(pProviderReg, pSpec, pElement, poffError);
     862    AssertReturn(hPrevVfsObj != NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
     863
     864    RTVFSIOSTREAM hVfsIosIn = RTVfsObjToIoStream(hPrevVfsObj);
     865    if (hVfsIosIn == NIL_RTVFSIOSTREAM)
     866        return VERR_VFS_CHAIN_CAST_FAILED;
     867
     868    RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM;
     869    int rc = RTZipGzipDecompressIoStream(hVfsIosIn, 0 /*fFlags*/, &hVfsIos);
     870    if (RT_SUCCESS(rc))
     871    {
     872        *phVfsObj = RTVfsObjFromIoStream(hVfsIos);
     873        RTVfsIoStrmRelease(hVfsIos);
     874        if (*phVfsObj != NIL_RTVFSOBJ)
     875            return VINF_SUCCESS;
     876        rc = VERR_VFS_CHAIN_CAST_FAILED;
     877    }
     878    return rc;
     879}
     880
     881
     882/**
     883 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
     884 */
     885static DECLCALLBACK(bool) rtVfsChainGunzip_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
     886                                                           PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
     887                                                           PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
     888{
     889    RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
     890    return false;
     891}
     892
     893
     894/** VFS chain element 'gunzip'. */
     895static RTVFSCHAINELEMENTREG g_rtVfsChainGunzipReg =
     896{
     897    /* uVersion = */            RTVFSCHAINELEMENTREG_VERSION,
     898    /* fReserved = */           0,
     899    /* pszName = */             "gunzip",
     900    /* ListEntry = */           { NULL, NULL },
     901    /* pszHelp = */             "Takes an I/O stream and gunzips it. No arguments.",
     902    /* pfnValidate = */         rtVfsChainGunzip_Validate,
     903    /* pfnInstantiate = */      rtVfsChainGunzip_Instantiate,
     904    /* pfnCanReuseElement = */  rtVfsChainGunzip_CanReuseElement,
     905    /* uEndMarker = */          RTVFSCHAINELEMENTREG_VERSION
     906};
     907
     908RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainGunzipReg, rtVfsChainGunzipReg);
     909
     910
     911
     912/**
     913 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
     914 */
     915static DECLCALLBACK(int) rtVfsChainGzip_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
     916                                                 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError)
     917{
     918    RT_NOREF(pProviderReg);
     919
     920    /*
     921     * Basics.
     922     */
     923    if (pElement->enmTypeIn == RTVFSOBJTYPE_INVALID)
     924        return VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT;
     925    if (pElement->cArgs > 1)
     926        return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
     927    if (pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
     928        return VERR_VFS_CHAIN_ONLY_IOS;
     929    if (pSpec->fOpenFile & RTFILE_O_READ)
     930        return VERR_VFS_CHAIN_WRITE_ONLY_IOS;
     931
     932    /*
     933     * Optional argument 1..9 indicating the compression level.
     934     * We store it in pSpec->uProvider.
     935     */
     936    if (pElement->cArgs > 0)
     937    {
     938        const char *psz = pElement->paArgs[0].psz;
     939        if (!*psz || !strcmp(psz, "default"))
     940            pSpec->uProvider = 6;
     941        else if (!strcmp(psz, "fast"))
     942            pSpec->uProvider = 3;
     943        else if (   RT_C_IS_DIGIT(*psz)
     944                 && *psz != '0'
     945                 && *RTStrStripL(psz + 1) == '\0')
     946            pSpec->uProvider = *psz - '0';
     947        else
     948        {
     949            *poffError = pElement->paArgs[0].offSpec;
     950            return VERR_VFS_CHAIN_INVALID_ARGUMENT;
     951        }
     952    }
     953    else
     954        pSpec->uProvider = 6;
     955
     956    return VINF_SUCCESS;
     957}
     958
     959
     960/**
     961 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
     962 */
     963static DECLCALLBACK(int) rtVfsChainGzip_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
     964                                                    PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
     965                                                    PRTVFSOBJ phVfsObj, uint32_t *poffError)
     966{
     967    RT_NOREF(pProviderReg, pSpec, pElement, poffError);
     968    AssertReturn(hPrevVfsObj != NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
     969
     970    RTVFSIOSTREAM hVfsIosOut = RTVfsObjToIoStream(hPrevVfsObj);
     971    if (hVfsIosOut == NIL_RTVFSIOSTREAM)
     972        return VERR_VFS_CHAIN_CAST_FAILED;
     973
     974    RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM;
     975    int rc = RTZipGzipCompressIoStream(hVfsIosOut, 0 /*fFlags*/, pSpec->uProvider, &hVfsIos);
     976    if (RT_SUCCESS(rc))
     977    {
     978        *phVfsObj = RTVfsObjFromIoStream(hVfsIos);
     979        RTVfsIoStrmRelease(hVfsIos);
     980        if (*phVfsObj != NIL_RTVFSOBJ)
     981            return VINF_SUCCESS;
     982        rc = VERR_VFS_CHAIN_CAST_FAILED;
     983    }
     984    return rc;
     985}
     986
     987
     988/**
     989 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
     990 */
     991static DECLCALLBACK(bool) rtVfsChainGzip_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
     992                                                         PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
     993                                                         PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
     994{
     995    RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
     996    return false;
     997}
     998
     999
     1000/** VFS chain element 'gzip'. */
     1001static RTVFSCHAINELEMENTREG g_rtVfsChainGzipReg =
     1002{
     1003    /* uVersion = */            RTVFSCHAINELEMENTREG_VERSION,
     1004    /* fReserved = */           0,
     1005    /* pszName = */             "gzip",
     1006    /* ListEntry = */           { NULL, NULL },
     1007    /* pszHelp = */             "Takes an I/O stream and gzips it.\n"
     1008                                "Optional argument specifying compression level: 1-9, default, fast",
     1009    /* pfnValidate = */         rtVfsChainGzip_Validate,
     1010    /* pfnInstantiate = */      rtVfsChainGzip_Instantiate,
     1011    /* pfnCanReuseElement = */  rtVfsChainGzip_CanReuseElement,
     1012    /* uEndMarker = */          RTVFSCHAINELEMENTREG_VERSION
     1013};
     1014
     1015RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainGzipReg, rtVfsChainGzipReg);
     1016
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