Esempio n. 1
0
static int tstDirOpenFiltered(const char *pszFilter, unsigned *pcFilesMatch, int *pRc)
{
    int rcRet = 0;
    unsigned cFilesMatch = 0;
    PRTDIR pDir;
    int rc = RTDirOpenFiltered(&pDir, pszFilter, RTDIRFILTER_WINNT, 0);
    if (RT_SUCCESS(rc))
    {
        for (;;)
        {
            RTDIRENTRY DirEntry;
            rc = RTDirRead(pDir, &DirEntry, NULL);
            if (RT_FAILURE(rc))
                break;
            cFilesMatch++;
        }

        if (rc != VERR_NO_MORE_FILES)
        {
            RTPrintf("tstDir-3: Enumeration '%s' failed! rc=%Rrc\n", pszFilter, rc);
            rcRet = 1;
        }

        /* close up */
        rc = RTDirClose(pDir);
        if (RT_FAILURE(rc))
        {
            RTPrintf("tstDir-3: Failed to close dir '%s'! rc=%Rrc\n", pszFilter, rc);
            rcRet = 1;
        }
    }
    else
    {
        RTPrintf("tstDir-3: Failed to open '%s', rc=%Rrc\n", pszFilter, rc);
        rcRet = 1;
    }

    *pcFilesMatch = cFilesMatch;
    *pRc = rc;
    return rcRet;
}
Esempio n. 2
0
/**
 * Recursive worker for rtDbgSymCacheAddDir, for minimal stack wasting.
 *
 * @returns IPRT status code (fully bitched).
 * @param   pszPath             Pointer to a RTPATH_MAX size buffer containing
 *                              the path to the current directory ending with a
 *                              slash.
 * @param   cchPath             The size of the current directory path.
 * @param   pDirEntry           Pointer to the RTDIRENTRYEX structure to use.
 * @param   pCfg                The configuration.
 */
static int rtDbgSymCacheAddDirWorker(char *pszPath, size_t cchPath, PRTDIRENTRYEX pDirEntry, PCRTDBGSYMCACHEADDCFG pCfg)
{
    /*
     * Open the directory.
     */
    PRTDIR pDir;
    int rc, rc2;
    if (pCfg->pszFilter)
    {
        rc = RTStrCopy(&pszPath[cchPath], RTPATH_MAX - cchPath, pCfg->pszFilter);
        if (RT_FAILURE(rc))
        {
            pszPath[cchPath] = '\0';
            return RTMsgErrorRc(rc, "Filename too long (%Rrc): '%s" RTPATH_SLASH_STR "%s'", rc, pszPath, pCfg->pszFilter);
        }
        rc = RTDirOpenFiltered(&pDir, pszPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
    }
    else
        rc = RTDirOpen(&pDir, pszPath);
    if (RT_FAILURE(rc))
        return RTMsgErrorRc(rc, "RTDirOpen%s failed on '%s': %Rrc", pCfg->pszFilter ? "Filtered" : "", pszPath, rc);

    /*
     * Enumerate the files.
     */
    for (;;)
    {
        rc2 = RTDirReadEx(pDir, pDirEntry, NULL, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
        if (RT_FAILURE(rc2))
        {
            pszPath[cchPath] = '\0';
            if (rc2 != VERR_NO_MORE_FILES)
            {
                RTMsgError("RTDirReadEx failed in '%s': %Rrc\n", pszPath, rc2);
                rc = rc2;
            }
            break;
        }

        /* Skip dot and dot-dot. */
        if (RTDirEntryExIsStdDotLink(pDirEntry))
            continue;

        /* Construct a full path. */
        rc = RTStrCopy(&pszPath[cchPath], RTPATH_MAX, pDirEntry->szName);
        if (RT_FAILURE(rc))
        {
            pszPath[cchPath] = '\0';
            RTMsgError("File name too long in '%s': '%s' (%Rrc)", pszPath, pDirEntry->szName, rc);
            break;
        }

        switch (rtDbgSymCacheFigureType2(pszPath, &pDirEntry->Info))
        {
            case RTDBGSYMCACHEFILETYPE_DIR:
                if (!pCfg->fRecursive)
                    RTMsgInfo("Skipping directory '%s'...", pszPath);
                else
                {
                    if (cchPath + pDirEntry->cbName + 3 <= RTPATH_MAX)
                    {
                        pszPath[cchPath + pDirEntry->cbName] = RTPATH_SLASH;
                        pszPath[cchPath + pDirEntry->cbName + 1] = '\0';
                        rc2 = rtDbgSymCacheAddDirWorker(pszPath, cchPath + pDirEntry->cbName + 1, pDirEntry, pCfg);
                    }
                    else
                    {
                        RTMsgError("File name too long in '%s': '%s' (%Rrc)", pszPath, pDirEntry->szName, rc);
                        rc2 = VERR_FILENAME_TOO_LONG;
                    }
                }
                break;

            case RTDBGSYMCACHEFILETYPE_DEBUG_FILE:
                rc2 = rtDbgSymCacheAddDebugFile(pszPath, pCfg);
                break;

            case RTDBGSYMCACHEFILETYPE_IMAGE_FILE:
                rc2 = rtDbgSymCacheAddImageFile(pszPath, NULL /*pszExtraSuff*/, RTDBG_CACHE_UUID_MAP_DIR_IMAGES, pCfg);
                break;

            case RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE:
                rc2 = rtDbgSymCacheAddDebugBundle(pszPath, cchPath, pDirEntry->cbName, pCfg);
                break;

            case RTDBGSYMCACHEFILETYPE_IMAGE_BUNDLE:
                rc2 = rtDbgSymCacheAddImageBundle(pszPath, cchPath, pDirEntry->cbName, pDirEntry, pCfg);
                break;

            case RTDBGSYMCACHEFILETYPE_DIR_FILTER:
            case RTDBGSYMCACHEFILETYPE_INVALID:
                rc2 = RTMsgErrorRc(VERR_INTERNAL_ERROR_2, "Invalid: '%s'", pszPath);
                break;

            case RTDBGSYMCACHEFILETYPE_IGNORE:
                rc2 = VINF_SUCCESS;
                break;
        }

        if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
            rc = rc2;
    }

    /*
     * Clean up.
     */
    rc2 = RTDirClose(pDir);
    if (RT_FAILURE(rc2))
    {
        RTMsgError("RTDirClose failed in '%s': %Rrc", pszPath, rc);
        rc = rc2;
    }
    return rc;
}
Esempio n. 3
0
int main(int argc, char **argv)
{
    int rcRet = 0;
    RTR3InitExe(argc, &argv, 0);

    /*
     * Iterate arguments.
     */
    for (int i = 1; i < argc; i++)
    {
        /* open */
        PRTDIR pDir;
        int rc = RTDirOpenFiltered(&pDir, argv[i], RTDIRFILTER_WINNT, 0);
        if (RT_SUCCESS(rc))
        {
            for (;;)
            {
                RTDIRENTRY DirEntry;
                rc = RTDirRead(pDir, &DirEntry, NULL);
                if (RT_FAILURE(rc))
                    break;
                switch (DirEntry.enmType)
                {
                    case RTDIRENTRYTYPE_UNKNOWN:     RTPrintf("u"); break;
                    case RTDIRENTRYTYPE_FIFO:        RTPrintf("f"); break;
                    case RTDIRENTRYTYPE_DEV_CHAR:    RTPrintf("c"); break;
                    case RTDIRENTRYTYPE_DIRECTORY:   RTPrintf("d"); break;
                    case RTDIRENTRYTYPE_DEV_BLOCK:   RTPrintf("b"); break;
                    case RTDIRENTRYTYPE_FILE:        RTPrintf("-"); break;
                    case RTDIRENTRYTYPE_SYMLINK:     RTPrintf("l"); break;
                    case RTDIRENTRYTYPE_SOCKET:      RTPrintf("s"); break;
                    case RTDIRENTRYTYPE_WHITEOUT:    RTPrintf("w"); break;
                    default:
                        rcRet = 1;
                        RTPrintf("?");
                        break;
                }
                RTPrintf(" %#18llx  %3d %s\n", (uint64_t)DirEntry.INodeId,
                         DirEntry.cbName, DirEntry.szName);
            }

            if (rc != VERR_NO_MORE_FILES)
            {
                RTPrintf("tstDir-2: Enumeration failed! rc=%Rrc\n", rc);
                rcRet = 1;
            }

            /* close up */
            rc = RTDirClose(pDir);
            if (RT_FAILURE(rc))
            {
                RTPrintf("tstDir-2: Failed to close dir! rc=%Rrc\n", rc);
                rcRet = 1;
            }
        }
        else
        {
            RTPrintf("tstDir-2: Failed to open '%s', rc=%Rrc\n", argv[i], rc);
            rcRet = 1;
        }
    }

    return rcRet;
}
/**
 * Returns the path of the ACPI CPU device with the given core and package ID.
 *
 * @returns VBox status code.
 * @param   ppszPath     Where to store the path.
 * @param   idCpuCore    The core ID of the CPU.
 * @param   idCpuPackage The package ID of the CPU.
 */
static int VBoxServiceCpuHotPlugGetACPIDevicePath(char **ppszPath, uint32_t idCpuCore, uint32_t idCpuPackage)
{
    int rc = VINF_SUCCESS;

    AssertPtrReturn(ppszPath, VERR_INVALID_PARAMETER);

    rc = VBoxServiceCpuHotPlugProbePath();
    if (RT_SUCCESS(rc))
    {
        /* Build the path from all components. */
        bool fFound = false;
        unsigned iLvlCurr = 0;
        char *pszPath = NULL;
        char *pszPathDir = NULL;
        PSYSFSCPUPATH pAcpiCpuPathLvl = &g_aAcpiCpuPath[iLvlCurr];

        /* Init everything. */
        Assert(pAcpiCpuPathLvl->uId != ACPI_CPU_PATH_NOT_PROBED);
        pszPath = RTPathJoinA(SYSFS_ACPI_CPU_PATH, pAcpiCpuPathLvl->aComponentsPossible[pAcpiCpuPathLvl->uId].pcszName);
        if (!pszPath)
            return VERR_NO_STR_MEMORY;

        pAcpiCpuPathLvl->pszPath = RTStrDup(SYSFS_ACPI_CPU_PATH);
        if (!pAcpiCpuPathLvl->pszPath)
        {
            RTStrFree(pszPath);
            return VERR_NO_STR_MEMORY;
        }

        /* Open the directory */
        rc = RTDirOpenFiltered(&pAcpiCpuPathLvl->pDir, pszPath, RTDIRFILTER_WINNT, 0);
        if (RT_SUCCESS(rc))
        {
            RTStrFree(pszPath);

            /* Search for CPU */
            while (!fFound)
            {
                /* Get the next directory. */
                RTDIRENTRY DirFolderContent;
                rc = RTDirRead(pAcpiCpuPathLvl->pDir, &DirFolderContent, NULL);
                if (RT_SUCCESS(rc))
                {
                    /* Create the new path. */
                    char *pszPathCurr = RTPathJoinA(pAcpiCpuPathLvl->pszPath, DirFolderContent.szName);
                    if (!pszPathCurr)
                    {
                        rc = VERR_NO_STR_MEMORY;
                        break;
                    }

                    /* If this is the last level check for the given core and package id. */
                    if (iLvlCurr == RT_ELEMENTS(g_aAcpiCpuPath) - 1)
                    {
                        /* Get the sysdev */
                        uint32_t idCore    = RTLinuxSysFsReadIntFile(10, "%s/sysdev/topology/core_id",
                                                                     pszPathCurr);
                        uint32_t idPackage = RTLinuxSysFsReadIntFile(10, "%s/sysdev/topology/physical_package_id",
                                                                     pszPathCurr);
                        if (   idCore    == idCpuCore
                            && idPackage == idCpuPackage)
                        {
                            /* Return the path */
                            pszPath = pszPathCurr;
                            fFound = true;
                            VBoxServiceVerbose(3, "CPU found\n");
                            break;
                        }
                        else
                        {
                            /* Get the next directory. */
                            RTStrFree(pszPathCurr);
                            VBoxServiceVerbose(3, "CPU doesn't match, next directory\n");
                        }
                    }
                    else
                    {
                        /* Go deeper */
                        iLvlCurr++;

                        VBoxServiceVerbose(3, "Going deeper (iLvlCurr=%u)\n", iLvlCurr);

                        pAcpiCpuPathLvl = &g_aAcpiCpuPath[iLvlCurr];

                        Assert(!pAcpiCpuPathLvl->pDir);
                        Assert(!pAcpiCpuPathLvl->pszPath);
                        pAcpiCpuPathLvl->pszPath = pszPathCurr;
                        PCSYSFSCPUPATHCOMP pPathComponent = &pAcpiCpuPathLvl->aComponentsPossible[pAcpiCpuPathLvl->uId];

                        Assert(pAcpiCpuPathLvl->uId != ACPI_CPU_PATH_NOT_PROBED);

                        pszPathDir = RTPathJoinA(pszPathCurr, pPathComponent->pcszName);
                        if (!pszPathDir)
                        {
                            rc = VERR_NO_STR_MEMORY;
                            break;
                        }

                        VBoxServiceVerbose(3, "New path %s\n", pszPathDir);

                        /* Open the directory */
                        rc = RTDirOpenFiltered(&pAcpiCpuPathLvl->pDir, pszPathDir, RTDIRFILTER_WINNT, 0);
                        if (RT_FAILURE(rc))
                            break;
                    }
                }
                else
                {
                    /* Go back one level and try to get the next entry. */
                    Assert(iLvlCurr > 0);

                    RTDirClose(pAcpiCpuPathLvl->pDir);
                    RTStrFree(pAcpiCpuPathLvl->pszPath);
                    pAcpiCpuPathLvl->pDir = NULL;
                    pAcpiCpuPathLvl->pszPath = NULL;

                    iLvlCurr--;
                    pAcpiCpuPathLvl = &g_aAcpiCpuPath[iLvlCurr];
                    VBoxServiceVerbose(3, "Directory not found, going back (iLvlCurr=%u)\n", iLvlCurr);
                }
            } /* while not found */
        } /* Successful init */

        /* Cleanup */
        for (unsigned i = 0; i < RT_ELEMENTS(g_aAcpiCpuPath); i++)
        {
            if (g_aAcpiCpuPath[i].pDir)
                RTDirClose(g_aAcpiCpuPath[i].pDir);
            if (g_aAcpiCpuPath[i].pszPath)
                RTStrFree(g_aAcpiCpuPath[i].pszPath);
            g_aAcpiCpuPath[i].pDir = NULL;
            g_aAcpiCpuPath[i].pszPath = NULL;
        }
        if (pszPathDir)
            RTStrFree(pszPathDir);
        if (RT_FAILURE(rc) && pszPath)
            RTStrFree(pszPath);

        if (RT_SUCCESS(rc))
            *ppszPath = pszPath;
    }

    return rc;
}
/**
 * Probes for the correct path to the ACPI CPU object in sysfs for the
 * various different kernel versions and distro's.
 *
 * @returns VBox status code.
 */
static int VBoxServiceCpuHotPlugProbePath(void)
{
    int rc = VINF_SUCCESS;

    /* Probe for the correct path if we didn't already. */
    if (RT_UNLIKELY(g_aAcpiCpuPath[0].uId == ACPI_CPU_PATH_NOT_PROBED))
    {
        char *pszPath = NULL;   /** < Current path, increasing while we dig deeper. */

        pszPath = RTStrDup(SYSFS_ACPI_CPU_PATH);
        if (!pszPath)
            return VERR_NO_MEMORY;

        /*
         * Simple algorithm to find the path.
         * Performance is not a real problem because it is
         * only executed once.
         */
        for (unsigned iLvlCurr = 0; iLvlCurr < RT_ELEMENTS(g_aAcpiCpuPath); iLvlCurr++)
        {
            PSYSFSCPUPATH pAcpiCpuPathLvl = &g_aAcpiCpuPath[iLvlCurr];

            for (unsigned iCompCurr = 0; iCompCurr < pAcpiCpuPathLvl->cComponents; iCompCurr++)
            {
                PCSYSFSCPUPATHCOMP pPathComponent = &pAcpiCpuPathLvl->aComponentsPossible[iCompCurr];

                /* Open the directory */
                PRTDIR pDirCurr = NULL;
                char *pszPathTmp = RTPathJoinA(pszPath, pPathComponent->pcszName);
                if (pszPathTmp)
                {
                    rc = RTDirOpenFiltered(&pDirCurr, pszPathTmp, RTDIRFILTER_WINNT, 0);
                    RTStrFree(pszPathTmp);
                }
                else
                    rc = VERR_NO_STR_MEMORY;
                if (RT_FAILURE(rc))
                    break;

                /* Search if the current directory contains one of the possible parts. */
                size_t cchName = strlen(pPathComponent->pcszName);
                RTDIRENTRY DirFolderContent;
                bool fFound = false;

                /* Get rid of the * filter which is in the path component. */
                if (pPathComponent->fNumberedSuffix)
                    cchName--;

                while (RT_SUCCESS(RTDirRead(pDirCurr, &DirFolderContent, NULL))) /* Assumption that szName has always enough space */
                {
                    if (   DirFolderContent.cbName >= cchName
                        && !strncmp(DirFolderContent.szName, pPathComponent->pcszName, cchName))
                    {
                        /* Found, use the complete name to dig deeper. */
                        fFound = true;
                        pAcpiCpuPathLvl->uId = iCompCurr;
                        char *pszPathLvl = RTPathJoinA(pszPath, DirFolderContent.szName);
                        if (pszPathLvl)
                        {
                            RTStrFree(pszPath);
                            pszPath = pszPathLvl;
                        }
                        else
                            rc = VERR_NO_STR_MEMORY;
                        break;
                    }
                }
                RTDirClose(pDirCurr);

                if (fFound)
                    break;
            } /* For every possible component. */

            /* No matching component for this part, no need to continue */
            if (RT_FAILURE(rc))
                break;
        } /* For every level */

        VBoxServiceVerbose(1, "Final path after probing %s rc=%Rrc\n", pszPath, rc);
        RTStrFree(pszPath);
    }

    return rc;
}
/**
 * Corrects the casing of the final component
 *
 * @returns
 * @param   pClient             .
 * @param   pszFullPath         .
 * @param   pszStartComponent   .
 */
static int vbsfCorrectCasing(SHFLCLIENTDATA *pClient, char *pszFullPath, char *pszStartComponent)
{
    Log2(("vbsfCorrectCasing: %s %s\n", pszFullPath, pszStartComponent));

    AssertReturn((uintptr_t)pszFullPath < (uintptr_t)pszStartComponent - 1U, VERR_INTERNAL_ERROR_2);
    AssertReturn(pszStartComponent[-1] == RTPATH_DELIMITER, VERR_INTERNAL_ERROR_5);

    /*
     * Allocate a buffer that can hold really long file name entries as well as
     * the initial search pattern.
     */
    size_t cchComponent = strlen(pszStartComponent);
    size_t cchParentDir = pszStartComponent - pszFullPath;
    size_t cchFullPath  = cchParentDir + cchComponent;
    Assert(strlen(pszFullPath) == cchFullPath);

    size_t cbDirEntry   = 4096;
    if (cchFullPath + 4 > cbDirEntry - RT_OFFSETOF(RTDIRENTRYEX, szName))
        cbDirEntry = RT_OFFSETOF(RTDIRENTRYEX, szName) + cchFullPath + 4;

    PRTDIRENTRYEX pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
    if (pDirEntry == NULL)
        return VERR_NO_MEMORY;

    /*
     * Construct the search criteria in the szName member of pDirEntry.
     */
    /** @todo This is quite inefficient, especially for directories with many
     *        files.  If any of the typically case sensitive host systems start
     *        supporting opendir wildcard filters, it would make sense to build
     *        one here with '?' for case foldable charaters. */
    /** @todo Use RTDirOpen here and drop the whole uncessary path copying? */
    int rc = RTPathJoinEx(pDirEntry->szName, cbDirEntry - RT_OFFSETOF(RTDIRENTRYEX, szName),
                          pszFullPath, cchParentDir,
                          RT_STR_TUPLE("*"));
    AssertRC(rc);
    if (RT_SUCCESS(rc))
    {
        RTDIR hSearch = NULL;
        rc = RTDirOpenFiltered(&hSearch, pDirEntry->szName, RTDIRFILTER_WINNT, 0 /*fFlags*/);
        if (RT_SUCCESS(rc))
        {
            for (;;)
            {
                size_t cbDirEntrySize = cbDirEntry;

                rc = RTDirReadEx(hSearch, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
                if (rc == VERR_NO_MORE_FILES)
                    break;

                if (   rc != VINF_SUCCESS
                    && rc != VWRN_NO_DIRENT_INFO)
                {
                    if (   rc == VERR_NO_TRANSLATION
                        || rc == VERR_INVALID_UTF8_ENCODING)
                        continue;
                    AssertMsgFailed(("%Rrc\n", rc));
                    break;
                }

                Log2(("vbsfCorrectCasing: found %s\n", &pDirEntry->szName[0]));
                if (    pDirEntry->cbName == cchComponent
                    &&  !RTStrICmp(pszStartComponent, &pDirEntry->szName[0]))
                {
                    Log(("Found original name %s (%s)\n", &pDirEntry->szName[0], pszStartComponent));
                    strcpy(pszStartComponent, &pDirEntry->szName[0]);
                    rc = VINF_SUCCESS;
                    break;
                }
            }

            RTDirClose(hSearch);
        }
    }

    if (RT_FAILURE(rc))
        Log(("vbsfCorrectCasing %s failed with %Rrc\n", pszStartComponent, rc));

    RTMemFree(pDirEntry);

    return rc;
}
Esempio n. 7
0
int main(int argc, char **argv)
{
    int rcRet = 0;
    RTR3InitExe(argc, &argv, 0);

    /*
     * Iterate arguments.
     */
    bool fLong      = false;
    bool fShortName = false;
    bool fFiltered  = false;
    bool fQuiet     = false;
    for (int i = 1; i < argc; i++)
    {
        if (argv[i][0] == '-')
        {
            for (int j = 1; argv[i][j]; j++)
            {
                switch (argv[i][j])
                {
                    case 'l':
                        fLong = true;
                        break;
                    case 's':
                        fShortName = true;
                        break;
                    case 'f':
                        fFiltered = true;
                        break;
                    case 'q':
                        fQuiet = true;
                        break;
                    default:
                        RTPrintf("Unknown option '%c' ignored!\n", argv[i][j]);
                        break;
                }
            }
        }
        else
        {
            /* open */
            PRTDIR pDir;
            int rc;
            if (!fFiltered)
                rc = RTDirOpen(&pDir, argv[i]);
            else
                rc = RTDirOpenFiltered(&pDir, argv[i], RTDIRFILTER_WINNT, 0);
            if (RT_SUCCESS(rc))
            {
                /* list */
                if (!fLong)
                {
                    for (;;)
                    {
                        RTDIRENTRY DirEntry;
                        rc = RTDirRead(pDir, &DirEntry, NULL);
                        if (RT_FAILURE(rc))
                            break;
                        if (!fQuiet)
                        {
                            switch (DirEntry.enmType)
                            {
                                case RTDIRENTRYTYPE_UNKNOWN:     RTPrintf("u"); break;
                                case RTDIRENTRYTYPE_FIFO:        RTPrintf("f"); break;
                                case RTDIRENTRYTYPE_DEV_CHAR:    RTPrintf("c"); break;
                                case RTDIRENTRYTYPE_DIRECTORY:   RTPrintf("d"); break;
                                case RTDIRENTRYTYPE_DEV_BLOCK:   RTPrintf("b"); break;
                                case RTDIRENTRYTYPE_FILE:        RTPrintf("-"); break;
                                case RTDIRENTRYTYPE_SYMLINK:     RTPrintf("l"); break;
                                case RTDIRENTRYTYPE_SOCKET:      RTPrintf("s"); break;
                                case RTDIRENTRYTYPE_WHITEOUT:    RTPrintf("w"); break;
                                default:
                                    rcRet = 1;
                                    RTPrintf("?");
                                    break;
                            }
                            RTPrintf(" %#18llx  %3d %s\n", (uint64_t)DirEntry.INodeId,
                                     DirEntry.cbName, DirEntry.szName);
                        }
                    }
                }
                else
                {
                    for (;;)
                    {
                        RTDIRENTRYEX DirEntry;
                        rc = RTDirReadEx(pDir, &DirEntry, NULL, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
                        if (RT_FAILURE(rc))
                            break;

                        if (!fQuiet)
                        {
                            RTFMODE fMode = DirEntry.Info.Attr.fMode;
                            switch (fMode & RTFS_TYPE_MASK)
                            {
                                case RTFS_TYPE_FIFO:        RTPrintf("f"); break;
                                case RTFS_TYPE_DEV_CHAR:    RTPrintf("c"); break;
                                case RTFS_TYPE_DIRECTORY:   RTPrintf("d"); break;
                                case RTFS_TYPE_DEV_BLOCK:   RTPrintf("b"); break;
                                case RTFS_TYPE_FILE:        RTPrintf("-"); break;
                                case RTFS_TYPE_SYMLINK:     RTPrintf("l"); break;
                                case RTFS_TYPE_SOCKET:      RTPrintf("s"); break;
                                case RTFS_TYPE_WHITEOUT:    RTPrintf("w"); break;
                                default:
                                    rcRet = 1;
                                    RTPrintf("?");
                                    break;
                            }
                            /** @todo sticy bits++ */
                            RTPrintf("%c%c%c",
                                     fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
                                     fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
                                     fMode & RTFS_UNIX_IXUSR ? 'x' : '-');
                            RTPrintf("%c%c%c",
                                     fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
                                     fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
                                     fMode & RTFS_UNIX_IXGRP ? 'x' : '-');
                            RTPrintf("%c%c%c",
                                     fMode & RTFS_UNIX_IROTH ? 'r' : '-',
                                     fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
                                     fMode & RTFS_UNIX_IXOTH ? 'x' : '-');
                            RTPrintf(" %c%c%c%c%c%c%c%c%c%c%c%c%c%c",
                                     fMode & RTFS_DOS_READONLY          ? 'R' : '-',
                                     fMode & RTFS_DOS_HIDDEN            ? 'H' : '-',
                                     fMode & RTFS_DOS_SYSTEM            ? 'S' : '-',
                                     fMode & RTFS_DOS_DIRECTORY         ? 'D' : '-',
                                     fMode & RTFS_DOS_ARCHIVED          ? 'A' : '-',
                                     fMode & RTFS_DOS_NT_DEVICE         ? 'd' : '-',
                                     fMode & RTFS_DOS_NT_NORMAL         ? 'N' : '-',
                                     fMode & RTFS_DOS_NT_TEMPORARY      ? 'T' : '-',
                                     fMode & RTFS_DOS_NT_SPARSE_FILE    ? 'P' : '-',
                                     fMode & RTFS_DOS_NT_REPARSE_POINT  ? 'J' : '-',
                                     fMode & RTFS_DOS_NT_COMPRESSED     ? 'C' : '-',
                                     fMode & RTFS_DOS_NT_OFFLINE        ? 'O' : '-',
                                     fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
                                     fMode & RTFS_DOS_NT_ENCRYPTED      ? 'E' : '-');
                            RTPrintf(" %d %4d %4d %10lld %10lld %#llx %#llx %#llx %#llx",
                                     DirEntry.Info.Attr.u.Unix.cHardlinks,
                                     DirEntry.Info.Attr.u.Unix.uid,
                                     DirEntry.Info.Attr.u.Unix.gid,
                                     DirEntry.Info.cbObject,
                                     DirEntry.Info.cbAllocated,
                                     DirEntry.Info.BirthTime,
                                     DirEntry.Info.ChangeTime,
                                     DirEntry.Info.ModificationTime,
                                     DirEntry.Info.AccessTime);
                            if (fShortName)
                                RTPrintf(" %2d %-12ls ", DirEntry.cwcShortName, DirEntry.wszShortName);
                            RTPrintf(" %2d %s\n", DirEntry.cbName, DirEntry.szName);
                        }
                        if (rc != VINF_SUCCESS)
                            RTPrintf("^^ %Rrc\n", rc);
                    }
                }

                if (rc != VERR_NO_MORE_FILES)
                {
                    RTPrintf("tstDir: Enumeration failed! rc=%Rrc\n", rc);
                    rcRet = 1;
                }

                /* close up */
                rc = RTDirClose(pDir);
                if (RT_FAILURE(rc))
                {
                    RTPrintf("tstDir: Failed to close dir! rc=%Rrc\n", rc);
                    rcRet = 1;
                }
            }
            else
            {
                RTPrintf("tstDir: Failed to open '%s', rc=%Rrc\n", argv[i], rc);
                rcRet = 1;
            }
        }
    }

    return rcRet;
}
/**
 * Process the testcases found in the filter.
 *
 * @param   pszFilter   The filter (winnt) to pass to RTDirOpenFiltered for
 *                      selecting the testcases.
 * @param   pszDir      The directory we're processing.
 */
static void Process(const char *pszFilter, const char *pszDir)
{
    /*
     * Open and enumerate the directory.
     */
    PRTDIR pDir;
    int rc = RTDirOpenFiltered(&pDir, pszFilter, RTDIRFILTER_WINNT, 0);
    if (RT_SUCCESS(rc))
    {
        for (;;)
        {
            RTDIRENTRY DirEntry;
            rc = RTDirRead(pDir, &DirEntry, NULL);
            if (RT_FAILURE(rc))
            {
                if (rc == VERR_NO_MORE_FILES)
                    rc = VINF_SUCCESS;
                else
                    RTPrintf("tstRunTestcases: reading '%s' -> %Rrc\n", pszFilter, rc);
                break;
            }

            /*
             * Construct the testcase name.
             */
            char *pszTestcase;
            RTStrAPrintf(&pszTestcase, "%s/%s", pszDir, DirEntry.szName);
            if (!pszTestcase)
            {
                RTPrintf("tstRunTestcases: out of memory!\n");
                rc = VERR_NO_MEMORY;
                break;
            }
            if (IsTestcaseIncluded(pszTestcase))
            {
                /*
                 * Execute the testcase.
                 */
                RTPrintf("*** %s: Executing...\n", pszTestcase);  RTStrmFlush(g_pStdOut);
                const char *papszArgs[2];
                papszArgs[0] = pszTestcase;
                papszArgs[1] = NULL;
                RTPROCESS Process;
                rc = RTProcCreate(pszTestcase, papszArgs, RTENV_DEFAULT, 0, &Process);
                if (RT_SUCCESS(rc))
                {
                    /*
                     * Wait for the process and collect it's return code.
                     * If it takes too long, we'll terminate it and continue.
                     */
                    RTTIMESPEC Start;
                    RTTimeNow(&Start);
                    RTPROCSTATUS ProcStatus;
                    for (;;)
                    {
                        rc = RTProcWait(Process, RTPROCWAIT_FLAGS_NOBLOCK, &ProcStatus);
                        if (rc != VERR_PROCESS_RUNNING)
                            break;
                        RTTIMESPEC Now;
                        if (RTTimeSpecGetMilli(RTTimeSpecSub(RTTimeNow(&Now), &Start)) > 120*1000 /* 1 min */)
                        {
                            RTPrintf("*** %s: FAILED - timed out. killing it.\n", pszTestcase);
                            RTProcTerminate(Process);
                            RTThreadSleep(100);
                            RTProcWait(Process, RTPROCWAIT_FLAGS_NOBLOCK, &ProcStatus);
                            g_cFailures++;
                            break;
                        }
                        RTThreadSleep(100);
                    }

                    /*
                     * Examin the exit status.
                     */
                    if (RT_SUCCESS(rc))
                    {
                        if (    ProcStatus.enmReason == RTPROCEXITREASON_NORMAL
                            &&  ProcStatus.iStatus == 0)
                        {
                            RTPrintf("*** %s: PASSED\n", pszTestcase);
                            g_cPasses++;
                        }
                        else
                        {
                            RTPrintf("*** %s: FAILED\n", pszTestcase);
                            g_cFailures++;
                        }
                    }
                    else if (rc != VERR_PROCESS_RUNNING)
                    {
                        RTPrintf("tstRunTestcases: %s: RTProcWait failed -> %Rrc\n", pszTestcase, rc);
                        g_cFailures++;
                    }
                }
                else
                {
                    RTPrintf("tstRunTestcases: %s: failed to start -> %Rrc\n", pszTestcase, rc);
                    g_cFailures++;
                }

            }
            else
            {
                RTPrintf("tstRunTestcases: %s: SKIPPED\n", pszTestcase);
                g_cSkipped++;
            }
            RTStrFree(pszTestcase);
        } /* enumeration loop */

        RTDirClose(pDir);
    }
    else
        RTPrintf("tstRunTestcases: opening '%s' -> %Rrc\n", pszDir, rc);
}