Exemple #1
0
/**
 * Validates the extension pack name.
 *
 * @returns true if valid, false if not.
 * @param   pszName             The name to validate.
 * @sa      VBoxExtPackExtractNameFromTarballPath
 */
bool VBoxExtPackIsValidName(const char *pszName)
{
    if (!pszName)
        return false;

    /*
     * Check the characters making up the name, only english alphabet
     * characters, decimal digits and spaces are allowed.
     */
    size_t off = 0;
    while (pszName[off])
    {
        if (!RT_C_IS_ALNUM(pszName[off]) && pszName[off] != ' ')
            return false;
        off++;
    }

    /*
     * Check min and max name limits.
     */
    if (   off > VBOX_EXTPACK_NAME_MAX_LEN
        || off < VBOX_EXTPACK_NAME_MIN_LEN)
        return false;

    return true;
}
Exemple #2
0
/**
 * Checks if an alledged manged extension pack name.
 *
 * @returns true if valid, false if not.
 * @param   pszMangledName      The mangled name to validate.
 * @param   cchMax              The max number of chars to test.
 * @sa      VBoxExtPackMangleName
 */
bool VBoxExtPackIsValidMangledName(const char *pszMangledName, size_t cchMax /*= RTSTR_MAX*/)
{
    if (!pszMangledName)
        return false;

    /*
     * Check the characters making up the name, only english alphabet
     * characters, decimal digits and underscores (=space) are allowed.
     */
    size_t off = 0;
    while (off < cchMax && pszMangledName[off])
    {
        if (!RT_C_IS_ALNUM(pszMangledName[off]) && pszMangledName[off] != '_')
            return false;
        off++;
    }

    /*
     * Check min and max name limits.
     */
    if (   off > VBOX_EXTPACK_NAME_MAX_LEN
        || off < VBOX_EXTPACK_NAME_MIN_LEN)
        return false;

    return true;
}
Exemple #3
0
/**
 * Get's the C word starting at the current position minus one.
 *
 * @returns Pointer to the word on success and the stream position advanced to
 *          the end of it.
 *          NULL on failure, stream position normally unchanged.
 * @param   pStream             The stream to get the C word from.
 * @param   pcchWord            Where to return the word length.
 */
const char *ScmStreamCGetWordM1(PSCMSTREAM pStream, size_t *pcchWord)
{
    /* Check stream state. */
    AssertReturn(!pStream->fWriteOrRead, NULL);
    AssertReturn(RT_SUCCESS(pStream->rc), NULL);
    AssertReturn(pStream->fFullyLineated, NULL);

    /* Get the number of chars left on the line and locate the current char. */
    size_t const    iLine   = pStream->iLine;
    size_t const    cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - (pStream->off - 1);
    const char     *psz     = &pStream->pch[pStream->off - 1];

    /* Is it a leading C character. */
    if (!RT_C_IS_ALPHA(*psz) && *psz != '_')
        return NULL;

    /* Find the end of the word. */
    char    ch;
    size_t  off = 1;
    while (     off < cchLeft
           &&  (   (ch = psz[off]) == '_'
                || RT_C_IS_ALNUM(ch)))
        off++;

    pStream->off += off - 1;
    *pcchWord = off;
    return psz;
}
Exemple #4
0
/**
 * Extract the extension pack name from the tarball path.
 *
 * @returns String containing the name on success, the caller must delete it.
 *          NULL if no valid name was found or if we ran out of memory.
 * @param   pszTarball          The path to the tarball.
 */
RTCString *VBoxExtPackExtractNameFromTarballPath(const char *pszTarball)
{
    /*
     * Skip ahead to the filename part and count the number of characters
     * that matches the criteria for a mangled extension pack name.
     */
    const char *pszSrc = RTPathFilename(pszTarball);
    if (!pszSrc)
        return NULL;

    size_t off = 0;
    while (RT_C_IS_ALNUM(pszSrc[off]) || pszSrc[off] == '_')
        off++;

    /*
     * Check min and max name limits.
     */
    if (   off > VBOX_EXTPACK_NAME_MAX_LEN
        || off < VBOX_EXTPACK_NAME_MIN_LEN)
        return NULL;

    /*
     * Return the unmangled name.
     */
    return VBoxExtPackUnmangleName(pszSrc, off);
}
Exemple #5
0
/**
 * If the given C word is at off - 1, return @c true and skip beyond it,
 * otherwise return @c false.
 *
 * @retval  true if the given C-word is at the current position minus one char.
 *          The stream position changes.
 * @retval  false if not. The stream position is unchanged.
 *
 * @param   pStream             The stream.
 * @param   cchWord             The length of the word.
 * @param   pszWord             The word.
 */
bool ScmStreamCMatchingWordM1(PSCMSTREAM pStream, const char *pszWord, size_t cchWord)
{
    /* Check stream state. */
    AssertReturn(!pStream->fWriteOrRead, false);
    AssertReturn(RT_SUCCESS(pStream->rc), false);
    AssertReturn(pStream->fFullyLineated, false);

    /* Sufficient chars left on the line? */
    size_t const    iLine   = pStream->iLine;
    AssertReturn(pStream->off > pStream->paLines[iLine].off, false);
    size_t const    cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - (pStream->off - 1);
    if (cchWord > cchLeft)
        return false;

    /* Do they match? */
    const char     *psz     = &pStream->pch[pStream->off - 1];
    if (memcmp(psz, pszWord, cchWord))
        return false;

    /* Is it the end of a C word? */
    if (cchWord < cchLeft)
    {
        psz += cchWord;
        if (RT_C_IS_ALNUM(*psz) || *psz == '_')
            return false;
    }

    /* Skip ahead. */
    pStream->off += cchWord - 1;
    return true;
}
Exemple #6
0
void CollectorLinux::addRaidDisks(const char *pcszDevice, DiskList& listDisks)
{
    FILE *f = fopen("/proc/mdstat", "r");
    if (f)
    {
        char szBuf[128];
        while (fgets(szBuf, sizeof(szBuf), f))
        {
            char *pszBufName = szBuf;

            char *pszBufData = strchr(pszBufName, ' ');
            if (!pszBufData)
            {
                LogRel(("CollectorLinux::addRaidDisks() failed to parse disk stats: %s\n", szBuf));
                continue;
            }
            *pszBufData++ = '\0';
            if (!strcmp(pcszDevice, pszBufName))
            {
                while (*pszBufData == ':')         ++pszBufData; /* Skip delimiter */
                while (*pszBufData == ' ')         ++pszBufData; /* Skip spaces */
                while (RT_C_IS_ALNUM(*pszBufData)) ++pszBufData; /* Skip status */
                while (*pszBufData == ' ')         ++pszBufData; /* Skip spaces */
                while (RT_C_IS_ALNUM(*pszBufData)) ++pszBufData; /* Skip type */

                while (*pszBufData != '\0')
                {
                    while (*pszBufData == ' ') ++pszBufData; /* Skip spaces */
                    char *pszDisk = pszBufData;
                    while (RT_C_IS_ALPHA(*pszBufData))
                        ++pszBufData;
                    if (*pszBufData)
                    {
                        *pszBufData++ = '\0';
                        listDisks.push_back(RTCString(pszDisk));
                        while (*pszBufData != '\0' && *pszBufData != ' ')
                            ++pszBufData;
                    }
                    else
                        listDisks.push_back(RTCString(pszDisk));
                }
                break;
            }
        }
        fclose(f);
    }
}
Exemple #7
0
/**
 * Validates an extension pack module string.
 *
 * @returns true if valid, false if not.
 * @param   pszModule           The module string to validate.
 */
bool VBoxExtPackIsValidModuleString(const char *pszModule)
{
    if (!pszModule || *pszModule == '\0')
        return false;

    /* Restricted charset, no extensions (dots). */
    while (   RT_C_IS_ALNUM(*pszModule)
           || *pszModule == '-'
           || *pszModule == '_')
        pszModule++;

    return *pszModule == '\0';
}
Exemple #8
0
int GuestEnvironment::Set(const Utf8Str &strKey, const Utf8Str &strValue)
{
    /** @todo Do some validation using regex. */
    if (strKey.isEmpty())
        return VERR_INVALID_PARAMETER;

    int rc = VINF_SUCCESS;
    const char *pszString = strKey.c_str();
    while (*pszString != '\0' && RT_SUCCESS(rc))
    {
         if (   !RT_C_IS_ALNUM(*pszString)
             && !RT_C_IS_GRAPH(*pszString))
             rc = VERR_INVALID_PARAMETER;
         *pszString++;
    }

    if (RT_SUCCESS(rc))
        mEnvironment[strKey] = strValue;

    return rc;
}
Exemple #9
0
/**
 * Unmangle an extension pack name (reverses VBoxExtPackMangleName).
 *
 * @returns String containing the mangled name on success, the caller must
 *          delete it.  NULL on failure.
 * @param   pszMangledName      The mangled name.
 * @param   cchMax              The max name length.  RTSTR_MAX is fine.
 * @sa      VBoxExtPackMangleName, VBoxExtPackIsValidMangledName
 */
RTCString *VBoxExtPackUnmangleName(const char *pszMangledName, size_t cchMax)
{
    AssertReturn(VBoxExtPackIsValidMangledName(pszMangledName, cchMax), NULL);

    char    szTmp[VBOX_EXTPACK_NAME_MAX_LEN + 1];
    size_t  off = 0;
    char    ch;
    while (   off < cchMax
           && (ch = pszMangledName[off]) != '\0')
    {
        if (ch == '_')
            ch = ' ';
        else
            AssertReturn(RT_C_IS_ALNUM(ch) || ch == ' ', NULL);
        szTmp[off++] = ch;
    }
    szTmp[off] = '\0';
    AssertReturn(VBoxExtPackIsValidName(szTmp), NULL);

    return new RTCString(szTmp, off);
}
Exemple #10
0
/**
 * @callback_method_impl{dtrace_pops_t,dtps_provide}
 */
static void     vboxDtPOps_Provide(void *pvProv, const dtrace_probedesc_t *pDtProbeDesc)
{
    PSUPDRVVDTPROVIDERCORE  pProv = (PSUPDRVVDTPROVIDERCORE)pvProv;
    AssertPtrReturnVoid(pProv);
    LOG_DTRACE(("%s: %p / %p pDtProbeDesc=%p\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, pDtProbeDesc));

    if (pDtProbeDesc)
        return;  /* We don't generate probes, so never mind these requests. */

    if (pProv->TracerData.DTrace.fZombie)
        return;

    dtrace_provider_id_t const idProvider = pProv->TracerData.DTrace.idProvider;
    AssertPtrReturnVoid(idProvider);

    AssertPtrReturnVoid(pProv->pHdr);
    AssertReturnVoid(pProv->pHdr->offProbeLocs != 0);
    uint32_t const  cProbeLocs    = pProv->pHdr->cbProbeLocs / sizeof(VTGPROBELOC);

    /* Need a buffer for extracting the function names and mangling them in
       case of collision. */
    size_t const cbFnNmBuf = _4K + _1K;
    char *pszFnNmBuf = (char *)RTMemAlloc(cbFnNmBuf);
    if (!pszFnNmBuf)
        return;

    /*
     * Itereate the probe location list and register all probes related to
     * this provider.
     */
    uint16_t const idxProv = (uint16_t)((PVTGDESCPROVIDER)((uintptr_t)pProv->pHdr + pProv->pHdr->offProviders) - pProv->pDesc);
    uint32_t idxProbeLoc;
    for (idxProbeLoc = 0; idxProbeLoc < cProbeLocs; idxProbeLoc++)
    {
        /* Skip probe location belonging to other providers or once that
           we've already reported. */
        PCVTGPROBELOC pProbeLocRO = &pProv->paProbeLocsRO[idxProbeLoc];
        PVTGDESCPROBE pProbeDesc  = pProbeLocRO->pProbe;
        if (pProbeDesc->idxProvider != idxProv)
            continue;

        uint32_t *pidProbe;
        if (!pProv->fUmod)
            pidProbe = (uint32_t *)&pProbeLocRO->idProbe;
        else
            pidProbe = &pProv->paR0ProbeLocs[idxProbeLoc].idProbe;
        if (*pidProbe != 0)
            continue;

        /* The function name may need to be stripped since we're using C++
           compilers for most of the code.  ASSUMES nobody are brave/stupid
           enough to use function pointer returns without typedef'ing
           properly them (e.g. signal). */
        const char *pszPrbName = vboxDtVtgGetString(pProv->pHdr, pProbeDesc->offName);
        const char *pszFunc    = pProbeLocRO->pszFunction;
        const char *psz        = strchr(pProbeLocRO->pszFunction, '(');
        size_t      cch;
        if (psz)
        {
            /* skip blanks preceeding the parameter parenthesis. */
            while (   (uintptr_t)psz > (uintptr_t)pProbeLocRO->pszFunction
                      && RT_C_IS_BLANK(psz[-1]))
                psz--;

            /* Find the start of the function name. */
            pszFunc = psz - 1;
            while ((uintptr_t)pszFunc > (uintptr_t)pProbeLocRO->pszFunction)
            {
                char ch = pszFunc[-1];
                if (!RT_C_IS_ALNUM(ch) && ch != '_' && ch != ':')
                    break;
                pszFunc--;
            }
            cch = psz - pszFunc;
        }
        else
            cch = strlen(pszFunc);
        RTStrCopyEx(pszFnNmBuf, cbFnNmBuf, pszFunc, cch);

        /* Look up the probe, if we have one in the same function, mangle
           the function name a little to avoid having to deal with having
           multiple location entries with the same probe ID. (lazy bird) */
        Assert(!*pidProbe);
        if (dtrace_probe_lookup(idProvider, pProv->pszModName, pszFnNmBuf, pszPrbName) != DTRACE_IDNONE)
        {
            RTStrPrintf(pszFnNmBuf+cch, cbFnNmBuf - cch, "-%u", pProbeLocRO->uLine);
            if (dtrace_probe_lookup(idProvider, pProv->pszModName, pszFnNmBuf, pszPrbName) != DTRACE_IDNONE)
            {
                unsigned iOrd = 2;
                while (iOrd < 128)
                {
                    RTStrPrintf(pszFnNmBuf+cch, cbFnNmBuf - cch, "-%u-%u", pProbeLocRO->uLine, iOrd);
                    if (dtrace_probe_lookup(idProvider, pProv->pszModName, pszFnNmBuf, pszPrbName) == DTRACE_IDNONE)
                        break;
                    iOrd++;
                }
                if (iOrd >= 128)
                {
                    LogRel(("VBoxDrv: More than 128 duplicate probe location instances %s at line %u in function %s [%s], probe %s\n",
                            pProbeLocRO->uLine, pProbeLocRO->pszFunction, pszFnNmBuf, pszPrbName));
                    continue;
                }
            }
        }

        /* Create the probe. */
        AssertCompile(sizeof(*pidProbe) == sizeof(dtrace_id_t));
        *pidProbe = dtrace_probe_create(idProvider, pProv->pszModName, pszFnNmBuf, pszPrbName,
                                        1 /*aframes*/, (void *)(uintptr_t)idxProbeLoc);
        pProv->TracerData.DTrace.cProvidedProbes++;
    }

    RTMemFree(pszFnNmBuf);
    LOG_DTRACE(("%s: returns\n", __FUNCTION__));
}
/**
 * Validates the TAR header.
 *
 * @returns VINF_SUCCESS if valid, VERR_TAR_ZERO_HEADER if all zeros, and
 *          the appropriate VERR_TAR_XXX otherwise.
 * @param   pTar                The TAR header.
 * @param   penmType            Where to return the type of header on success.
 */
static int rtZipTarHdrValidate(PCRTZIPTARHDR pTar, PRTZIPTARTYPE penmType)
{
    /*
     * Calc the checksum first since this enables us to detect zero headers.
     */
    int32_t i32ChkSum;
    int32_t i32ChkSumSignedAlt;
    if (rtZipTarCalcChkSum(pTar, &i32ChkSum, &i32ChkSumSignedAlt))
        return VERR_TAR_ZERO_HEADER;

    /*
     * Read the checksum field and match the checksums.
     */
    int64_t i64HdrChkSum;
    int rc = rtZipTarHdrFieldToNum(pTar->Common.chksum, sizeof(pTar->Common.chksum), true /*fOctalOnly*/, &i64HdrChkSum);
    if (RT_FAILURE(rc))
        return VERR_TAR_BAD_CHKSUM_FIELD;
    if (   i32ChkSum          != i64HdrChkSum
        && i32ChkSumSignedAlt != i64HdrChkSum) /** @todo test this */
        return VERR_TAR_CHKSUM_MISMATCH;

    /*
     * Detect the TAR type.
     */
    RTZIPTARTYPE enmType;
    if (   pTar->Common.magic[0] == 'u'
        && pTar->Common.magic[1] == 's'
        && pTar->Common.magic[2] == 't'
        && pTar->Common.magic[3] == 'a'
        && pTar->Common.magic[4] == 'r')
    {
/** @todo detect star headers */
        if (   pTar->Common.magic[5]   == '\0'
            && pTar->Common.version[0] == '0'
            && pTar->Common.version[1] == '0')
            enmType = RTZIPTARTYPE_POSIX;
        else if (   pTar->Common.magic[5]   == ' '
                && pTar->Common.version[0] == ' '
                && pTar->Common.version[1] == '\0')
            enmType = RTZIPTARTYPE_GNU;
        else
            return VERR_TAR_NOT_USTAR_V00;
    }
    else
        enmType = RTZIPTARTYPE_ANCIENT;
    *penmType = enmType;

    /*
     * Perform some basic checks.
     */
    switch (enmType)
    {
        case RTZIPTARTYPE_POSIX:
            if (   !RT_C_IS_ALNUM(pTar->Common.typeflag)
                && !pTar->Common.typeflag == '\0')
                return VERR_TAR_UNKNOWN_TYPE_FLAG;
            break;

        case RTZIPTARTYPE_GNU:
            switch (pTar->Common.typeflag)
            {
                case RTZIPTAR_TF_OLDNORMAL:
                case RTZIPTAR_TF_NORMAL:
                case RTZIPTAR_TF_CONTIG:
                case RTZIPTAR_TF_DIR:
                case RTZIPTAR_TF_CHR:
                case RTZIPTAR_TF_BLK:
                case RTZIPTAR_TF_LINK:
                case RTZIPTAR_TF_SYMLINK:
                case RTZIPTAR_TF_FIFO:
                    break;

                case RTZIPTAR_TF_GNU_LONGLINK:
                case RTZIPTAR_TF_GNU_LONGNAME:
                    break;

                case RTZIPTAR_TF_GNU_DUMPDIR:
                case RTZIPTAR_TF_GNU_MULTIVOL:
                case RTZIPTAR_TF_GNU_SPARSE:
                case RTZIPTAR_TF_GNU_VOLDHR:
                    /** @todo Implement full GNU TAR support. .*/
                    return VERR_TAR_UNSUPPORTED_GNU_HDR_TYPE;

                default:
                    return VERR_TAR_UNKNOWN_TYPE_FLAG;
            }
            break;

        case RTZIPTARTYPE_ANCIENT:
            switch (pTar->Common.typeflag)
            {
                case RTZIPTAR_TF_OLDNORMAL:
                case RTZIPTAR_TF_NORMAL:
                case RTZIPTAR_TF_CONTIG:
                case RTZIPTAR_TF_DIR:
                case RTZIPTAR_TF_LINK:
                case RTZIPTAR_TF_SYMLINK:
                case RTZIPTAR_TF_FIFO:
                    break;
                default:
                    return VERR_TAR_UNKNOWN_TYPE_FLAG;
            }
            break;
        default: /* shut up gcc */
            AssertFailedReturn(VERR_INTERNAL_ERROR_3);
    }

    return VINF_SUCCESS;
}
/**
 * Get the next token from the config stream and create a token structure.
 *
 * @returns VBox status code.
 * @param   pCfgTokenizer    The config tokenizer data.
 * @param   pCfgTokenUse     Allocated token structure to use or NULL to allocate
 *                           a new one. It will bee freed if an error is encountered.
 * @param   ppCfgToken       Where to store the pointer to the next token on success.
 */
static int autostartConfigTokenizerCreateToken(PCFGTOKENIZER pCfgTokenizer,
                                               PCFGTOKEN pCfgTokenUse, PCFGTOKEN *ppCfgToken)
{
    const char *pszToken = NULL;
    size_t cchToken = 1;
    size_t cchAdvance = 0;
    CFGTOKENTYPE enmType = CFGTOKENTYPE_INVALID;
    int rc = VINF_SUCCESS;

    for (;;)
    {
        pszToken = pCfgTokenizer->pszLineCurr;

        /* Skip all spaces. */
        while (RT_C_IS_BLANK(*pszToken))
        {
            pszToken++;
            cchAdvance++;
        }

        /* Check if we have to read a new line. */
        if (   *pszToken == '\0'
            || *pszToken == '#')
        {
            rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
            if (rc == VERR_EOF)
            {
                enmType = CFGTOKENTYPE_EOF;
                rc = VINF_SUCCESS;
                break;
            }
            else if (RT_FAILURE(rc))
                break;
            /* start from the beginning. */
            cchAdvance = 0;
        }
        else if (*pszToken == '=')
        {
            enmType = CFGTOKENTYPE_EQUAL;
            break;
        }
        else if (*pszToken == ',')
        {
            enmType = CFGTOKENTYPE_COMMA;
            break;
        }
        else if (*pszToken == '{')
        {
            enmType = CFGTOKENTYPE_CURLY_OPEN;
            break;
        }
        else if (*pszToken == '}')
        {
            enmType = CFGTOKENTYPE_CURLY_CLOSING;
            break;
        }
        else
        {
            const char *pszTmp = pszToken;
            cchToken = 0;
            enmType = CFGTOKENTYPE_ID;

            /* Get the complete token. */
            while (   RT_C_IS_ALNUM(*pszTmp)
                   || *pszTmp == '_'
                   || *pszTmp == '.')
            {
                pszTmp++;
                cchToken++;
            }
            break;
        }
    }

    Assert(RT_FAILURE(rc) || enmType != CFGTOKENTYPE_INVALID);

    if (RT_SUCCESS(rc))
    {
        /* Free the given token if it is an ID or the current one is an ID token. */
        if (   pCfgTokenUse
            && (   pCfgTokenUse->enmType == CFGTOKENTYPE_ID
                || enmType == CFGTOKENTYPE_ID))
        {
            autostartConfigTokenFree(pCfgTokenizer, pCfgTokenUse);
            pCfgTokenUse = NULL;
        }

        if (!pCfgTokenUse)
        {
            size_t cbToken = sizeof(CFGTOKEN);
            if (enmType == CFGTOKENTYPE_ID)
                cbToken += (cchToken + 1) * sizeof(char);

            pCfgTokenUse = (PCFGTOKEN)RTMemAllocZ(cbToken);
            if (!pCfgTokenUse)
                rc = VERR_NO_MEMORY;
        }

        if (RT_SUCCESS(rc))
        {
            /* Copy token data. */
            pCfgTokenUse->enmType  = enmType;
            pCfgTokenUse->cchStart = pCfgTokenizer->cchCurr;
            pCfgTokenUse->iLine    = pCfgTokenizer->iLine;
            if (enmType == CFGTOKENTYPE_ID)
            {
                pCfgTokenUse->u.Id.cchToken = cchToken;
                memcpy(pCfgTokenUse->u.Id.achToken, pszToken, cchToken);
            }
        }
        else if (pCfgTokenUse)
            autostartConfigTokenFree(pCfgTokenizer, pCfgTokenUse);

        if (RT_SUCCESS(rc))
        {
            /* Set new position in config stream. */
            pCfgTokenizer->pszLineCurr += cchToken + cchAdvance;
            pCfgTokenizer->cchCurr     += cchToken + cchAdvance;
            *ppCfgToken                 = pCfgTokenUse;
        }
    }

    return rc;
}
Exemple #13
0
static int rtUriParse(const char *pszUri, PRTURIPARSED pParsed)
{
    /*
     * Validate the input and clear the output.
     */
    AssertPtrReturn(pParsed, VERR_INVALID_POINTER);
    RT_ZERO(*pParsed);
    pParsed->uAuthorityPort = UINT32_MAX;

    AssertPtrReturn(pszUri, VERR_INVALID_POINTER);

    size_t const cchUri = strlen(pszUri);
    if (RT_LIKELY(cchUri >= 3)) { /* likely */ }
    else return cchUri ? VERR_URI_TOO_SHORT : VERR_URI_EMPTY;

    /*
     * Validating escaped text sequences is much simpler if we know that
     * that the base URI string is valid.  Also, we don't necessarily trust
     * the developer calling us to remember to do this.
     */
    int rc = RTStrValidateEncoding(pszUri);
    AssertRCReturn(rc, rc);

    /*
     * RFC-3986, section 3.1:
     *      scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
     *
     * The scheme ends with a ':', which we also skip here.
     */
    size_t off = 0;
    char ch = pszUri[off++];
    if (RT_LIKELY(RT_C_IS_ALPHA(ch))) { /* likely */ }
    else return VERR_URI_INVALID_SCHEME;
    for (;;)
    {
        ch = pszUri[off];
        if (ch == ':')
            break;
        if (RT_LIKELY(RT_C_IS_ALNUM(ch) || ch == '.' || ch == '-' || ch == '+')) { /* likely */ }
        else return VERR_URI_INVALID_SCHEME;
        off++;
    }
    pParsed->cchScheme = off;

    /* Require the scheme length to be at least two chars so we won't confuse
       it with a path starting with a DOS drive letter specification. */
    if (RT_LIKELY(off >= 2)) { /* likely */ }
    else return VERR_URI_INVALID_SCHEME;

    off++;                              /* (skip colon) */

    /*
     * Find the end of the path, we'll need this several times.
     * Also, while we're potentially scanning the whole thing, check for '%'.
     */
    size_t const offHash         = RTStrOffCharOrTerm(&pszUri[off], '#') + off;
    size_t const offQuestionMark = RTStrOffCharOrTerm(&pszUri[off], '?') + off;

    if (memchr(pszUri, '%', cchUri) != NULL)
        pParsed->fFlags |= RTURIPARSED_F_CONTAINS_ESCAPED_CHARS;

    /*
     * RFC-3986, section 3.2:
     *      The authority component is preceeded by a double slash ("//")...
     */
    if (   pszUri[off] == '/'
        && pszUri[off + 1] == '/')
    {
        off += 2;
        pParsed->offAuthority = pParsed->offAuthorityUsername = pParsed->offAuthorityPassword = pParsed->offAuthorityHost = off;
        pParsed->fFlags |= RTURIPARSED_F_HAVE_AUTHORITY;

        /*
         * RFC-3986, section 3.2:
         *      ...and is terminated by the next slash ("/"), question mark ("?"),
         *       or number sign ("#") character, or by the end of the URI.
         */
        const char *pszAuthority = &pszUri[off];
        size_t      cchAuthority = RTStrOffCharOrTerm(pszAuthority, '/');
        cchAuthority = RT_MIN(cchAuthority, offHash - off);
        cchAuthority = RT_MIN(cchAuthority, offQuestionMark - off);
        pParsed->cchAuthority     = cchAuthority;

        /* The Authority can be empty, like for: file:///usr/bin/grep  */
        if (cchAuthority > 0)
        {
            pParsed->cchAuthorityHost = cchAuthority;

            /*
             * If there is a userinfo part, it is ended by a '@'.
             */
            const char *pszAt = (const char *)memchr(pszAuthority, '@', cchAuthority);
            if (pszAt)
            {
                size_t cchTmp = pszAt - pszAuthority;
                pParsed->offAuthorityHost += cchTmp + 1;
                pParsed->cchAuthorityHost -= cchTmp + 1;

                /* If there is a password part, it's separated from the username with a colon. */
                const char *pszColon = (const char *)memchr(pszAuthority, ':', cchTmp);
                if (pszColon)
                {
                    pParsed->cchAuthorityUsername = pszColon - pszAuthority;
                    pParsed->offAuthorityPassword = &pszColon[1] - pszUri;
                    pParsed->cchAuthorityPassword = pszAt - &pszColon[1];
                }
                else
                {
                    pParsed->cchAuthorityUsername = cchTmp;
                    pParsed->offAuthorityPassword = off + cchTmp;
                }
            }

            /*
             * If there is a port part, its after the last colon in the host part.
             */
            const char *pszColon = (const char *)memrchr(&pszUri[pParsed->offAuthorityHost], ':', pParsed->cchAuthorityHost);
            if (pszColon)
            {
                size_t cchTmp = &pszUri[pParsed->offAuthorityHost + pParsed->cchAuthorityHost] - &pszColon[1];
                pParsed->cchAuthorityHost -= cchTmp + 1;

                pParsed->uAuthorityPort = 0;
                while (cchTmp-- > 0)
                {
                    ch = *++pszColon;
                    if (   RT_C_IS_DIGIT(ch)
                        && pParsed->uAuthorityPort < UINT32_MAX / UINT32_C(10))
                    {
                        pParsed->uAuthorityPort *= 10;
                        pParsed->uAuthorityPort += ch - '0';
                    }
                    else
                        return VERR_URI_INVALID_PORT_NUMBER;
                }
            }
        }

        /* Skip past the authority. */
        off += cchAuthority;
    }
    else
        pParsed->offAuthority = pParsed->offAuthorityUsername = pParsed->offAuthorityPassword = pParsed->offAuthorityHost = off;

    /*
     * RFC-3986, section 3.3: Path
     *      The path is terminated by the first question mark ("?")
     *      or number sign ("#") character, or by the end of the URI.
     */
    pParsed->offPath = off;
    pParsed->cchPath = RT_MIN(offHash, offQuestionMark) - off;
    off += pParsed->cchPath;

    /*
     * RFC-3986, section 3.4: Query
     *      The query component is indicated by the first question mark ("?")
     *      character and terminated by a number sign ("#") character or by the
     *      end of the URI.
     */
    if (   off == offQuestionMark
        && off < cchUri)
    {
        Assert(pszUri[offQuestionMark] == '?');
        pParsed->offQuery = ++off;
        pParsed->cchQuery = offHash - off;
        off = offHash;
    }
    else
    {
        Assert(!pszUri[offQuestionMark]);
        pParsed->offQuery = off;
    }

    /*
     * RFC-3986, section 3.5: Fragment
     *      A fragment identifier component is indicated by the presence of a
     *      number sign ("#") character and terminated by the end of the URI.
     */
    if (   off == offHash
        && off < cchUri)
    {
        pParsed->offFragment = ++off;
        pParsed->cchFragment = cchUri - off;
    }
    else
    {
        Assert(!pszUri[offHash]);
        pParsed->offFragment = off;
    }

    /*
     * If there are any escape sequences, validate them.
     *
     * This is reasonably simple as we already know that the string is valid UTF-8
     * before they get decoded.  Thus we only have to validate the escaped sequences.
     */
    if (pParsed->fFlags & RTURIPARSED_F_CONTAINS_ESCAPED_CHARS)
    {
        const char *pchSrc  = (const char *)memchr(pszUri, '%', cchUri);
        AssertReturn(pchSrc, VERR_INTERNAL_ERROR);
        do
        {
            char        szUtf8Seq[8];
            unsigned    cchUtf8Seq = 0;
            unsigned    cchNeeded  = 0;
            size_t      cchLeft    = &pszUri[cchUri] - pchSrc;
            do
            {
                if (cchLeft >= 3)
                {
                    char chHigh = pchSrc[1];
                    char chLow  = pchSrc[2];
                    if (   RT_C_IS_XDIGIT(chHigh)
                        && RT_C_IS_XDIGIT(chLow))
                    {
                        uint8_t b = RT_C_IS_DIGIT(chHigh) ? chHigh - '0' : (chHigh & ~0x20) - 'A' + 10;
                        b <<= 4;
                        b |= RT_C_IS_DIGIT(chLow) ? chLow - '0' : (chLow & ~0x20) - 'A' + 10;

                        if (!(b & 0x80))
                        {
                            /* We don't want the string to be terminated prematurely. */
                            if (RT_LIKELY(b != 0)) { /* likely */ }
                            else return VERR_URI_ESCAPED_ZERO;

                            /* Check that we're not expecting more UTF-8 bytes. */
                            if (RT_LIKELY(cchNeeded == 0)) { /* likely */ }
                            else return VERR_URI_MISSING_UTF8_CONTINUATION_BYTE;
                        }
                        /* Are we waiting UTF-8 bytes? */
                        else if (cchNeeded > 0)
                        {
                            if (RT_LIKELY(!(b & 0x40))) { /* likely */ }
                            else return VERR_URI_INVALID_ESCAPED_UTF8_CONTINUATION_BYTE;

                            szUtf8Seq[cchUtf8Seq++] = (char)b;
                            if (--cchNeeded == 0)
                            {
                                szUtf8Seq[cchUtf8Seq] = '\0';
                                rc = RTStrValidateEncoding(szUtf8Seq);
                                if (RT_FAILURE(rc))
                                    return VERR_URI_ESCAPED_CHARS_NOT_VALID_UTF8;
                                cchUtf8Seq = 0;
                            }
                        }
                        /* Start a new UTF-8 sequence. */
                        else
                        {
                            if ((b & 0xf8) == 0xf0)
                                cchNeeded = 3;
                            else if ((b & 0xf0) == 0xe0)
                                cchNeeded = 2;
                            else if ((b & 0xe0) == 0xc0)
                                cchNeeded = 1;
                            else
                                return VERR_URI_INVALID_ESCAPED_UTF8_LEAD_BYTE;
                            szUtf8Seq[0] = (char)b;
                            cchUtf8Seq = 1;
                        }
                        pchSrc  += 3;
                        cchLeft -= 3;
                    }
                    else
                        return VERR_URI_INVALID_ESCAPE_SEQ;
                }
                else
                    return VERR_URI_INVALID_ESCAPE_SEQ;
            } while (cchLeft > 0 && pchSrc[0] == '%');

            /* Check that we're not expecting more UTF-8 bytes. */
            if (RT_LIKELY(cchNeeded == 0)) { /* likely */ }
            else return VERR_URI_MISSING_UTF8_CONTINUATION_BYTE;

            /* next */
            pchSrc = (const char *)memchr(pchSrc, '%', cchLeft);
        } while (pchSrc);
    }

    pParsed->u32Magic = RTURIPARSED_MAGIC;
    return VINF_SUCCESS;
}