RTDECL(int) RTPathIsSame(const char *pszPath1, const char *pszPath2)
{
    /*
     * Simple checks based on the path values.
     */
    if (pszPath1 == pszPath2)
        return true;
    if (!pszPath1)
        return false;
    if (!pszPath2)
        return false;
    if (!strcmp(pszPath1, pszPath2))
        return true;

    /*
     * If the files exist, try use the attributes.
     */
    RTFSOBJINFO ObjInfo1, ObjInfo2;
    int rc = RTPathQueryInfoEx(pszPath1, &ObjInfo1, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
    if (RT_SUCCESS(rc))
        rc = RTPathQueryInfoEx(pszPath2, &ObjInfo2, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
    if (RT_SUCCESS(rc))
    {
        if ((ObjInfo1.Attr.fMode & RTFS_TYPE_MASK) != (ObjInfo2.Attr.fMode & RTFS_TYPE_MASK))
            return false;
        if (ObjInfo1.Attr.u.Unix.INodeIdDevice       != ObjInfo2.Attr.u.Unix.INodeIdDevice)
            return false;
        if (ObjInfo1.Attr.u.Unix.INodeId             != ObjInfo2.Attr.u.Unix.INodeId)
            return false;
        if (ObjInfo1.Attr.u.Unix.GenerationId        != ObjInfo2.Attr.u.Unix.GenerationId)
            return false;
        if (   ObjInfo1.Attr.u.Unix.INodeIdDevice != 0
            && ObjInfo1.Attr.u.Unix.INodeId       != 0)
            return true;
    }

    /*
     * Fallback, compare absolute/real paths. Return failure on paths that are
     * too long.
     */
    char szPath1[RTPATH_MAX];
    rc = RTPathAbs(pszPath1, szPath1, sizeof(szPath1));
    AssertRCReturn(rc, VERR_FILENAME_TOO_LONG);

    char szPath2[RTPATH_MAX];
    rc = RTPathAbs(pszPath2, szPath2, sizeof(szPath2)); AssertRC(rc);
    AssertRCReturn(rc, VERR_FILENAME_TOO_LONG);

    if (RTPathCompare(szPath1, szPath2) == 0)
        return true;

    /** @todo Relsolve any symbolic links in the paths. Too lazy for that right
     *        now. */
    return false;
}
/**
 * Recursively changes the file mode.
 *
 * @returns exit code
 * @param   pOpts               The mkdir option.
 * @param   pszPath             The path to start changing the mode of.
 */
static int rtCmdChModRecursive(RTCMDCHMODOPTS const *pOpts, const char *pszPath)
{
    /*
     * Check if it's a directory first.  If not, join the non-recursive code.
     */
    int             rc;
    uint32_t        offError;
    RTFSOBJINFO     ObjInfo;
    RTERRINFOSTATIC ErrInfo;
    bool const      fUseChainApi = pOpts->fAlwaysUseChainApi || RTVfsChainIsSpec(pszPath);
    if (!fUseChainApi)
    {
        rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
        if (RT_FAILURE(rc))
            return RTMsgErrorExitFailure("RTPathQueryInfoEx failed on '%s': %Rrc", pszPath, rc);
    }
    else
    {
        rc = RTVfsChainQueryInfo(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK,
                                 &offError, RTErrInfoInitStatic(&ErrInfo));
        if (RT_FAILURE(rc))
            return RTVfsChainMsgErrorExitFailure("RTVfsChainQueryInfo", pszPath, rc, offError, &ErrInfo.Core);
    }

    if (!RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
    {
        /*
         * Don't bother redoing the above work if its not necessary.
         */
        RTFMODE fNewMode = rtCmdMkModCalcNewMode(pOpts, ObjInfo.Attr.fMode);
        if (fNewMode != ObjInfo.Attr.fMode)
            return rtCmdChModOne(pOpts, pszPath);
        if (pOpts->enmNoiseLevel >= kRTCmdChModNoise_Verbose)
            RTPrintf("%s\n", pszPath);
        return RTEXITCODE_SUCCESS;
    }

    /*
     * For recursion we always use the VFS layer.
     */
    RTVFSDIR hVfsDir;
    if (!fUseChainApi)
    {
        rc = RTVfsDirOpenNormal(pszPath, 0 /** @todo write attrib flag*/, &hVfsDir);
        if (RT_FAILURE(rc))
            return RTMsgErrorExitFailure("RTVfsDirOpenNormal failed on '%s': %Rrc", pszPath, rc);
    }
    else
    {
        rc = RTVfsChainOpenDir(pszPath, 0 /** @todo write attrib flag*/, &hVfsDir, &offError, RTErrInfoInitStatic(&ErrInfo));
        if (RT_FAILURE(rc))
            return RTVfsChainMsgErrorExitFailure("RTVfsChainQueryInfo", pszPath, rc, offError, &ErrInfo.Core);
    }

    RTMsgError("Recursion is not yet implemented\n");
    RTVfsDirRelease(hVfsDir);
    rc = VERR_NOT_IMPLEMENTED;

    return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
}
/**
 * Changes the file mode of one file system object.
 *
 * @returns exit code
 * @param   pOpts               The chmod options.
 * @param   pszPath             The path to the file system object to change the
 *                              file mode of.
 */
static RTEXITCODE rtCmdChModOne(RTCMDCHMODOPTS const *pOpts, const char *pszPath)
{
    int         rc;
    RTFSOBJINFO ObjInfo;
    bool        fChanges = false;
    if (!pOpts->fAlwaysUseChainApi && !RTVfsChainIsSpec(pszPath) )
    {
        rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
        if (RT_SUCCESS(rc))
        {
            RTFMODE fNewMode = rtCmdMkModCalcNewMode(pOpts, ObjInfo.Attr.fMode);
            fChanges = fNewMode != ObjInfo.Attr.fMode;
            if (fChanges)
            {
                rc = RTPathSetMode(pszPath, fNewMode);
                if (RT_FAILURE(rc))
                    RTMsgError("RTPathSetMode failed on '%s' with fNewMode=%#x: %Rrc", pszPath, fNewMode, rc);
            }
        }
        else
            RTMsgError("RTPathQueryInfoEx failed on '%s': %Rrc", pszPath, rc);
    }
    else
    {
        RTVFSOBJ        hVfsObj;
        uint32_t        offError;
        RTERRINFOSTATIC ErrInfo;
        rc = RTVfsChainOpenObj(pszPath, RTFILE_O_ACCESS_ATTR_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
                               RTVFSOBJ_F_OPEN_ANY | RTVFSOBJ_F_CREATE_NOTHING | RTPATH_F_FOLLOW_LINK,
                               &hVfsObj, &offError, RTErrInfoInitStatic(&ErrInfo));
        if (RT_SUCCESS(rc))
        {
            rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
            if (RT_SUCCESS(rc))
            {
                RTFMODE fNewMode = rtCmdMkModCalcNewMode(pOpts, ObjInfo.Attr.fMode);
                fChanges = fNewMode != ObjInfo.Attr.fMode;
                if (fChanges)
                {
                    rc = RTVfsObjSetMode(hVfsObj, fNewMode, RTCHMOD_SET_ALL_MASK);
                    if (RT_FAILURE(rc))
                        RTMsgError("RTVfsObjSetMode failed on '%s' with fNewMode=%#x: %Rrc", pszPath, fNewMode, rc);
                }
            }
            else
                RTVfsChainMsgError("RTVfsObjQueryInfo", pszPath, rc, offError, &ErrInfo.Core);
            RTVfsObjRelease(hVfsObj);
        }
        else
            RTVfsChainMsgError("RTVfsChainOpenObject", pszPath, rc, offError, &ErrInfo.Core);
    }

    if (RT_SUCCESS(rc))
    {
        if (pOpts->enmNoiseLevel >= (fChanges ? kRTCmdChModNoise_Changes : kRTCmdChModNoise_Verbose))
            RTPrintf("%s\n", pszPath);
        return RTEXITCODE_SUCCESS;
    }
    return RTEXITCODE_FAILURE;
}
Example #4
0
RTDECL(int) RTDirQueryUnknownTypeEx(const char *pszComposedName, bool fFollowSymlinks,
                                    RTDIRENTRYTYPE *penmType, PRTFSOBJINFO pObjInfo)
{
    int rc = RTPathQueryInfoEx(pszComposedName, pObjInfo, RTFSOBJATTRADD_NOTHING,
                               fFollowSymlinks ? RTPATH_F_FOLLOW_LINK : RTPATH_F_ON_LINK);
    if (RT_FAILURE(rc))
        return rc;

    if (RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode))
        *penmType = RTDIRENTRYTYPE_DIRECTORY;
    else if (RTFS_IS_FILE(pObjInfo->Attr.fMode))
        *penmType = RTDIRENTRYTYPE_FILE;
    else if (RTFS_IS_SYMLINK(pObjInfo->Attr.fMode))
        *penmType = RTDIRENTRYTYPE_SYMLINK;
    else if (RTFS_IS_FIFO(pObjInfo->Attr.fMode))
        *penmType = RTDIRENTRYTYPE_FIFO;
    else if (RTFS_IS_DEV_CHAR(pObjInfo->Attr.fMode))
        *penmType = RTDIRENTRYTYPE_DEV_CHAR;
    else if (RTFS_IS_DEV_BLOCK(pObjInfo->Attr.fMode))
        *penmType = RTDIRENTRYTYPE_DEV_BLOCK;
    else if (RTFS_IS_SOCKET(pObjInfo->Attr.fMode))
        *penmType = RTDIRENTRYTYPE_SOCKET;
    else if (RTFS_IS_WHITEOUT(pObjInfo->Attr.fMode))
        *penmType = RTDIRENTRYTYPE_WHITEOUT;
    else
        *penmType = RTDIRENTRYTYPE_UNKNOWN;

    return VINF_SUCCESS;
}
/* Temporary stand-in for RTPathExistEx. */
static int vbsfQueryExistsEx(const char *pszPath, uint32_t fFlags)
{
#if 0 /** @todo Fix the symlink issue on windows! */
    return RTPathExistsEx(pszPath, fFlags);
#else
    RTFSOBJINFO IgnInfo;
    return RTPathQueryInfoEx(pszPath, &IgnInfo, RTFSOBJATTRADD_NOTHING, fFlags);
#endif
}
Example #6
0
RTDECL(bool) RTSymlinkIsDangling(const char *pszSymlink)
{
    bool        fRc = false;
    RTFSOBJINFO ObjInfo;
    int rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
    if (RT_SUCCESS(rc))
    {
        fRc = RTFS_IS_SYMLINK(ObjInfo.Attr.fMode);
        if (fRc)
        {
            rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
            fRc = !RT_SUCCESS_NP(rc);
        }
    }

    LogFlow(("RTSymlinkIsDangling(%p={%s}): returns %RTbool\n", pszSymlink, pszSymlink, fRc));
    return fRc;
}
Example #7
0
RTDECL(bool) RTDirExists(const char *pszPath)
{
    RTFSOBJINFO ObjInfo;
    int rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
    bool fRc = RT_SUCCESS(rc)
            && RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode);
    LogFlow(("RTDirExists(%p:{%s}): returns %RTbool (%Rrc)\n", pszPath, pszPath, fRc, rc));
    return fRc;
}
Example #8
0
RTDECL(bool) RTFileExists(const char *pszPath)
{
    RTFSOBJINFO ObjInfo;
    int rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
    bool fRc = RT_SUCCESS(rc)
            && RTFS_IS_FILE(ObjInfo.Attr.fMode)
            && !(ObjInfo.Attr.fMode & (RTFS_DOS_NT_DEVICE | RTFS_DOS_NT_REPARSE_POINT)); /* paranoia */
    LogFlow(("RTFileExists(%p:{%s}): returns %RTbool (%Rrc)\n", pszPath, pszPath, fRc, rc));
    return fRc;
}
Example #9
0
RTDECL(bool) RTSymlinkExists(const char *pszSymlink)
{
    bool        fRc = false;
    RTFSOBJINFO ObjInfo;
    int rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
    if (RT_SUCCESS(rc))
        fRc = RTFS_IS_SYMLINK(ObjInfo.Attr.fMode);

    LogFlow(("RTSymlinkExists(%p={%s}): returns %RTbool\n", pszSymlink, pszSymlink, fRc));
    return fRc;
}
Example #10
0
RTR3DECL(int) RTPathGetMode(const char *pszPath, PRTFMODE pfMode)
{
    AssertPtrReturn(pfMode, VERR_INVALID_POINTER);

    RTFSOBJINFO ObjInfo;
    int rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
    if (RT_SUCCESS(rc))
        *pfMode = ObjInfo.Attr.fMode;

    return rc;
}
Example #11
0
RTDECL(int) RTDirRemoveRecursive(const char *pszPath, uint32_t fFlags)
{
    AssertReturn(!(fFlags & ~RTDIRRMREC_F_VALID_MASK), VERR_INVALID_PARAMETER);

    /* Get an absolute path because this is easier to work with. */
    /** @todo use RTPathReal here instead? */
    char szAbsPath[RTPATH_MAX];
    int rc = RTPathAbs(pszPath, szAbsPath, sizeof(szAbsPath));
    if (RT_FAILURE(rc))
        return rc;

    /* This API is not permitted applied to the root of anything. */
    if (RTPathCountComponents(szAbsPath) <= 1)
        return VERR_ACCESS_DENIED;

    /* Because of the above restriction, we never have to deal with the root
       slash problem and can safely strip any trailing slashes and add a
       definite one. */
    RTPathStripTrailingSlash(szAbsPath);
    size_t cchAbsPath = strlen(szAbsPath);
    if (cchAbsPath + 1 >= RTPATH_MAX)
        return VERR_FILENAME_TOO_LONG;
    szAbsPath[cchAbsPath++] = '/';
    szAbsPath[cchAbsPath] = 0;

    /* Check if it exists so we can return quietly if it doesn't. */
    RTFSOBJINFO SharedObjInfoBuf;
    rc = RTPathQueryInfoEx(szAbsPath, &SharedObjInfoBuf, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
    if (   rc == VERR_PATH_NOT_FOUND
        || rc == VERR_FILE_NOT_FOUND)
        return VINF_SUCCESS;
    if (RT_FAILURE(rc))
        return rc;
    if (!RTFS_IS_DIRECTORY(SharedObjInfoBuf.Attr.fMode))
        return VERR_NOT_A_DIRECTORY;

    /* We're all set for the recursion now, so get going. */
    RTDIRENTRY  SharedDirEntryBuf;
    rc = rtDirRemoveRecursiveSub(szAbsPath, cchAbsPath, &SharedDirEntryBuf, &SharedObjInfoBuf);

    /* Remove the specified directory if desired and removing the content was
       successful. */
    if (   RT_SUCCESS(rc)
        && !(fFlags & RTDIRRMREC_F_CONTENT_ONLY))
    {
        szAbsPath[cchAbsPath] = 0;
        rc = RTDirRemove(szAbsPath);
    }
    return rc;
}
Example #12
0
/**
 * Figure file type based on name, will stat the file/dir.
 *
 * @returns File type.
 * @param   pszPath             The path to the file/dir to figure.
 */
static RTDBGSYMCACHEFILETYPE rtDbgSymCacheFigureType(const char *pszPath)
{
    const char *pszName = RTPathFilename(pszPath);

    /* Trailing slash. */
    if (!pszName)
        return RTDBGSYMCACHEFILETYPE_DIR;

    /* Wildcard means listing directory and filtering.  */
    if (strpbrk(pszName, "?*"))
        return RTDBGSYMCACHEFILETYPE_DIR_FILTER;

    /* Get object info, following links. */
    RTFSOBJINFO ObjInfo;
    int rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
    if (RT_FAILURE(rc))
        return RTDBGSYMCACHEFILETYPE_INVALID;
    return rtDbgSymCacheFigureType2(pszPath, &ObjInfo);
}
Example #13
0
/**
 * Reads the extension pack descriptor.
 *
 * @returns NULL on success, pointer to an error message on failure (caller
 *          deletes it).
 * @param   a_pszDir        The directory containing the description file.
 * @param   a_pExtPackDesc  Where to store the extension pack descriptor.
 * @param   a_pObjInfo      Where to store the object info for the file (unix
 *                          attribs). Optional.
 */
RTCString *VBoxExtPackLoadDesc(const char *a_pszDir, PVBOXEXTPACKDESC a_pExtPackDesc, PRTFSOBJINFO a_pObjInfo)
{
    vboxExtPackClearDesc(a_pExtPackDesc);

    /*
     * Validate, open and parse the XML file.
     */
    char szFilePath[RTPATH_MAX];
    int vrc = RTPathJoin(szFilePath, sizeof(szFilePath), a_pszDir, VBOX_EXTPACK_DESCRIPTION_NAME);
    if (RT_FAILURE(vrc))
        return new RTCString("RTPathJoin failed with %Rrc", vrc);

    RTFSOBJINFO ObjInfo;
    vrc = RTPathQueryInfoEx(szFilePath, &ObjInfo,  RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
    if (RT_FAILURE(vrc))
        return &(new RTCString())->printf("RTPathQueryInfoEx failed with %Rrc", vrc);
    if (a_pObjInfo)
        *a_pObjInfo = ObjInfo;
    if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
    {
        if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
            return new RTCString("The XML file is symlinked, that is not allowed");
        return &(new RTCString)->printf("The XML file is not a file (fMode=%#x)", ObjInfo.Attr.fMode);
    }

    xml::Document       Doc;
    {
        xml::XmlFileParser  Parser;
        try
        {
            Parser.read(szFilePath, Doc);
        }
        catch (xml::XmlError Err)
        {
            return new RTCString(Err.what());
        }
    }

    /*
     * Hand the xml doc over to the common code.
     */
    return vboxExtPackLoadDescFromDoc(&Doc, a_pExtPackDesc);
}
Example #14
0
RTDECL(int) RTFileQuerySize(const char *pszPath, uint64_t *pcbFile)
{
    RTFSOBJINFO ObjInfo;
    int rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
    if (RT_SUCCESS(rc))
    {
        if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
        {
            *pcbFile = ObjInfo.cbObject;
            LogFlow(("RTFileQuerySize(%p:{%s}): returns %Rrc (%#RX64)\n", pszPath, pszPath, rc, *pcbFile));
            return rc;
        }

        if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
            rc = VERR_IS_A_DIRECTORY;
        else
            rc = VERR_FILE_NOT_FOUND; /** @todo VERR_NOT_A_FILE... */
    }
    LogFlow(("RTFileQuerySize(%p:{%s}): returns %Rrc\n", pszPath, pszPath, rc));
    return rc;
}
static void test1Worker(RTTEST hTest, const char *pszBaseDir,
                        const char *pszTarget, RTSYMLINKTYPE enmType, bool fDangling)
{
    char    szPath1[RTPATH_MAX];
    char    szPath2[RTPATH_MAX];
    size_t  cchTarget = strlen(pszTarget);
    char    szPath3[RTPATH_MAX];

    RTStrCopy(szPath3, sizeof(szPath3), pszTarget);

#ifdef RT_OS_WINDOWS
    /* see RTSymlinkCreate in symlink-win.cpp */
    char c;
    char *psz = szPath3;
    while ((c = *psz) != '\0')
    {
        if (c == '/')
            *psz = '\\';
        psz++;
    }
#endif

    /* Create it.*/
    RTTESTI_CHECK_RC_OK_RETV(RTPathJoin(szPath1, sizeof(szPath1), pszBaseDir, "tstRTSymlink-link-1"));
    RTSymlinkDelete(szPath1, 0); /* clean up previous run */
    RTTESTI_CHECK_RC_RETV(RTSymlinkCreate(szPath1, pszTarget, RTSYMLINKTYPE_FILE, 0), VINF_SUCCESS);

    /* Check the predicate functions. */
    RTTESTI_CHECK(RTSymlinkExists(szPath1));
    RTTESTI_CHECK(RTSymlinkIsDangling(szPath1) == fDangling);

    /* Read it. */
    memset(szPath2, 0xff, sizeof(szPath2));
    szPath2[sizeof(szPath2) - 1] = '\0';
    RTTESTI_CHECK_RC(RTSymlinkRead(szPath1, szPath2, sizeof(szPath2), 0), VINF_SUCCESS);
    RTTESTI_CHECK_MSG(strcmp(szPath2, szPath3) == 0, ("got=\"%s\" expected=\"%s\"", szPath2, szPath3));

    memset(szPath2, 0xff, sizeof(szPath2));
    szPath2[sizeof(szPath2) - 1] = '\0';
    RTTESTI_CHECK_RC(RTSymlinkRead(szPath1, szPath2, cchTarget + 1, 0), VINF_SUCCESS);
    RTTESTI_CHECK_MSG(strcmp(szPath2, szPath3) == 0, ("got=\"%s\" expected=\"%s\"", szPath2, szPath3));

    memset(szPath2, 0xff, sizeof(szPath2));
    szPath2[sizeof(szPath2) - 1] = '\0';
    RTTESTI_CHECK_RC(RTSymlinkRead(szPath1, szPath2, cchTarget, 0), VERR_BUFFER_OVERFLOW);
    RTTESTI_CHECK_MSG(   strncmp(szPath2, szPath3, cchTarget - 1) == 0
                      && szPath2[cchTarget - 1] == '\0',
                      ("got=\"%s\" expected=\"%.*s\"", szPath2, cchTarget - 1, szPath3));

    /* Other APIs that have to handle symlinks carefully. */
    int rc;
    RTFSOBJINFO ObjInfo;
    RTTESTI_CHECK_RC(rc = RTPathQueryInfo(szPath1, &ObjInfo, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
    if (RT_SUCCESS(rc))
        RTTESTI_CHECK(RTFS_IS_SYMLINK(ObjInfo.Attr.fMode));
    RTTESTI_CHECK_RC(rc = RTPathQueryInfoEx(szPath1, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
    if (RT_SUCCESS(rc))
        RTTESTI_CHECK(RTFS_IS_SYMLINK(ObjInfo.Attr.fMode));

    if (!fDangling)
    {
        RTTESTI_CHECK_RC(rc = RTPathQueryInfoEx(szPath1, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK), VINF_SUCCESS);
        if (RT_SUCCESS(rc))
            RTTESTI_CHECK(!RTFS_IS_SYMLINK(ObjInfo.Attr.fMode));
        else
            RT_ZERO(ObjInfo);

        if (enmType == RTSYMLINKTYPE_DIR)
        {
            RTTESTI_CHECK(RTDirExists(szPath1));
            RTTESTI_CHECK(RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode));
        }
        else if (enmType == RTSYMLINKTYPE_FILE)
        {
            RTTESTI_CHECK(RTFileExists(szPath1));
            RTTESTI_CHECK(RTFS_IS_FILE(ObjInfo.Attr.fMode));
        }

        /** @todo Check more APIs */
    }

    /* Finally, the removal of the symlink. */
    RTTESTI_CHECK_RC(RTSymlinkDelete(szPath1, 0), VINF_SUCCESS);
    RTTESTI_CHECK_RC(RTSymlinkDelete(szPath1, 0), VERR_FILE_NOT_FOUND);
}
RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
{
    return RTPathQueryInfoEx(pszPath, pObjInfo, enmAdditionalAttribs, RTPATH_F_ON_LINK);
}
RTR3DECL(int) RTPathSetTimesEx(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
                               PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime, uint32_t fFlags)
{
    /*
     * Validate input.
     */
    AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
    AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
    AssertPtrNullReturn(pAccessTime, VERR_INVALID_POINTER);
    AssertPtrNullReturn(pModificationTime, VERR_INVALID_POINTER);
    AssertPtrNullReturn(pChangeTime, VERR_INVALID_POINTER);
    AssertPtrNullReturn(pBirthTime, VERR_INVALID_POINTER);
    AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);

    /*
     * Convert the paths.
     */
    char const *pszNativePath;
    int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
    if (RT_SUCCESS(rc))
    {
        RTFSOBJINFO ObjInfo;

        /*
         * If it's a no-op, we'll only verify the existance of the file.
         */
        if (!pAccessTime && !pModificationTime)
            rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, fFlags);
        else
        {
            /*
             * Convert the input to timeval, getting the missing one if necessary,
             * and call the API which does the change.
             */
            struct timeval aTimevals[2];
            if (pAccessTime && pModificationTime)
            {
                RTTimeSpecGetTimeval(pAccessTime,       &aTimevals[0]);
                RTTimeSpecGetTimeval(pModificationTime, &aTimevals[1]);
            }
            else
            {
                rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags);
                if (RT_SUCCESS(rc))
                {
                    RTTimeSpecGetTimeval(pAccessTime        ? pAccessTime       : &ObjInfo.AccessTime,       &aTimevals[0]);
                    RTTimeSpecGetTimeval(pModificationTime  ? pModificationTime : &ObjInfo.ModificationTime, &aTimevals[1]);
                }
                else
                    Log(("RTPathSetTimes('%s',%p,%p,,): RTPathQueryInfo failed with %Rrc\n",
                         pszPath, pAccessTime, pModificationTime, rc));
            }
            if (RT_SUCCESS(rc))
            {
                if (fFlags & RTPATH_F_FOLLOW_LINK)
                {
                    if (utimes(pszNativePath, aTimevals))
                        rc = RTErrConvertFromErrno(errno);
                }
#if (defined(RT_OS_DARWIN) && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050) \
 || defined(RT_OS_FREEBSD) \
 || defined(RT_OS_LINUX) \
 || defined(RT_OS_OS2) /** @todo who really has lutimes? */
                else
                {
                    if (lutimes(pszNativePath, aTimevals))
                        rc = RTErrConvertFromErrno(errno);
                }
#else
                else
                {
                    if (pAccessTime && pModificationTime)
                        rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags);
                    if (RT_SUCCESS(rc) && RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
                        rc = VERR_NS_SYMLINK_SET_TIME;
                    else if (RT_SUCCESS(rc))
                    {
                        if (utimes(pszNativePath, aTimevals))
                            rc = RTErrConvertFromErrno(errno);
                    }
                }
#endif
                if (RT_FAILURE(rc))
                    Log(("RTPathSetTimes('%s',%p,%p,,): failed with %Rrc and errno=%d\n",
                         pszPath, pAccessTime, pModificationTime, rc, errno));
            }
        }
        rtPathFreeNative(pszNativePath, pszPath);
    }
Example #18
0
/**
 * Recursion worker for RTDirRemoveRecursive.
 *
 * @returns IPRT status code.
 * @param   pszBuf              The path buffer.  Contains the abs path to the
 *                              directory to recurse into.  Trailing slash.
 * @param   cchDir              The length of the directory we're cursing into,
 *                              including the trailing slash.
 * @param   pDirEntry           The dir entry buffer.  (Shared to save stack.)
 * @param   pObjInfo            The object info buffer.  (ditto)
 */
static int rtDirRemoveRecursiveSub(char *pszBuf, size_t cchDir, PRTDIRENTRY pDirEntry, PRTFSOBJINFO pObjInfo)
{
    AssertReturn(RTPATH_IS_SLASH(pszBuf[cchDir - 1]), VERR_INTERNAL_ERROR_4);

    /*
     * Enumerate the directory content and dispose of it.
     */
    PRTDIR pDir;
    int rc = RTDirOpen(&pDir, pszBuf);
    if (RT_FAILURE(rc))
        return rc;
    while (RT_SUCCESS(rc = RTDirRead(pDir, pDirEntry, NULL)))
    {
        if (   pDirEntry->szName[0] != '.'
            || pDirEntry->cbName > 2
            || (   pDirEntry->cbName == 2
                && pDirEntry->szName[1] != '.')
           )
        {
            /* Construct the full name of the entry. */
            if (cchDir + pDirEntry->cbName + 1 /* dir slash */ >= RTPATH_MAX)
            {
                rc = VERR_FILENAME_TOO_LONG;
                break;
            }
            memcpy(&pszBuf[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);

            /* Deal with the unknown type. */
            if (pDirEntry->enmType == RTDIRENTRYTYPE_UNKNOWN)
            {
                rc = RTPathQueryInfoEx(pszBuf, pObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
                if (RT_SUCCESS(rc) && RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode))
                    pDirEntry->enmType = RTDIRENTRYTYPE_DIRECTORY;
                else if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode))
                    pDirEntry->enmType = RTDIRENTRYTYPE_FILE;
                else if (RT_SUCCESS(rc) && RTFS_IS_SYMLINK(pObjInfo->Attr.fMode))
                    pDirEntry->enmType = RTDIRENTRYTYPE_SYMLINK;
            }

            /* Try the delete the fs object. */
            switch (pDirEntry->enmType)
            {
                case RTDIRENTRYTYPE_FILE:
                    rc = RTFileDelete(pszBuf);
                    break;

                case RTDIRENTRYTYPE_DIRECTORY:
                {
                    size_t cchSubDir = cchDir + pDirEntry->cbName;
                    pszBuf[cchSubDir++] = '/';
                    pszBuf[cchSubDir]   = '\0';
                    rc = rtDirRemoveRecursiveSub(pszBuf, cchSubDir, pDirEntry, pObjInfo);
                    if (RT_SUCCESS(rc))
                    {
                        pszBuf[cchSubDir] = '\0';
                        rc = RTDirRemove(pszBuf);
                    }
                    break;
                }

                //case RTDIRENTRYTYPE_SYMLINK:
                //    rc = RTSymlinkDelete(pszBuf, 0);
                //    break;

                default:
                    /** @todo not implemented yet. */
                    rc = VINF_SUCCESS;
                    break;
            }
            if (RT_FAILURE(rc))
               break;
        }
    }
    if (rc == VERR_NO_MORE_FILES)
        rc = VINF_SUCCESS;
    RTDirClose(pDir);
    return rc;
}
/**
 * Helper for vbsfBuildFullPath that performs case corrections on the path
 * that's being build.
 *
 * @returns VINF_SUCCESS at the moment.
 * @param   pClient                 The client data.
 * @param   pszFullPath             Pointer to the full path.  This is the path
 *                                  which may need case corrections.  The
 *                                  corrections will be applied in place.
 * @param   cchFullPath             The length of the full path.
 * @param   fWildCard               Whether the last component may contain
 *                                  wildcards and thus might require exclusion
 *                                  from the case correction.
 * @param   fPreserveLastComponent  Always exclude the last component from case
 *                                  correction if set.
 */
static int vbsfCorrectPathCasing(SHFLCLIENTDATA *pClient, char *pszFullPath, size_t cchFullPath,
                                 bool fWildCard, bool fPreserveLastComponent)
{
    /*
     * Hide the last path component if it needs preserving.  This is required
     * in the following cases:
     *    - Contains the wildcard(s).
     *    - Is a 'rename' target.
     */
    char *pszLastComponent = NULL;
    if (fWildCard || fPreserveLastComponent)
    {
        char *pszSrc = pszFullPath + cchFullPath - 1;
        Assert(strchr(pszFullPath, '\0') == pszSrc + 1);
        while ((uintptr_t)pszSrc > (uintptr_t)pszFullPath)
        {
            if (*pszSrc == RTPATH_DELIMITER)
                break;
            pszSrc--;
        }
        if (*pszSrc == RTPATH_DELIMITER)
        {
            if (   fPreserveLastComponent
                /* Or does it really have wildcards? */
                || strchr(pszSrc + 1, '*') != NULL
                || strchr(pszSrc + 1, '?') != NULL
                || strchr(pszSrc + 1, '>') != NULL
                || strchr(pszSrc + 1, '<') != NULL
                || strchr(pszSrc + 1, '"') != NULL )
            {
                pszLastComponent = pszSrc;
                *pszLastComponent = '\0';
            }
        }
    }

    /*
     * If the path/file doesn't exist, we need to attempt case correcting it.
     */
    /** @todo Don't check when creating files or directories; waste of time. */
    int rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient));
    if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
    {
        Log(("Handle case insensitive guest fs on top of host case sensitive fs for %s\n", pszFullPath));

        /*
         * Work from the end of the path to find a partial path that's valid.
         */
        char *pszSrc = pszLastComponent ? pszLastComponent - 1 : pszFullPath + cchFullPath - 1;
        Assert(strchr(pszFullPath, '\0') == pszSrc + 1);

        while ((uintptr_t)pszSrc > (uintptr_t)pszFullPath)
        {
            if (*pszSrc == RTPATH_DELIMITER)
            {
                *pszSrc = '\0';
                rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient));
                *pszSrc = RTPATH_DELIMITER;
                if (RT_SUCCESS(rc))
                {
#ifdef DEBUG
                    *pszSrc = '\0';
                    Log(("Found valid partial path %s\n", pszFullPath));
                    *pszSrc = RTPATH_DELIMITER;
#endif
                    break;
                }
            }

            pszSrc--;
        }
        Assert(*pszSrc == RTPATH_DELIMITER && RT_SUCCESS(rc));
        if (   *pszSrc == RTPATH_DELIMITER
            && RT_SUCCESS(rc))
        {
            /*
             * Turn around and work the other way case correcting the components.
             */
            pszSrc++;
            for (;;)
            {
                bool fEndOfString = true;

                /* Find the end of the component. */
                char *pszEnd = pszSrc;
                while (*pszEnd)
                {
                    if (*pszEnd == RTPATH_DELIMITER)
                        break;
                    pszEnd++;
                }

                if (*pszEnd == RTPATH_DELIMITER)
                {
                    fEndOfString = false;
                    *pszEnd = '\0';
#if 0 /** @todo Please, double check this. The original code is in the #if 0, what I hold as correct is in the #else. */
                    rc = RTPathQueryInfoEx(pszSrc, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
#else
                    rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient));
#endif
                    Assert(rc == VINF_SUCCESS || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND);
                }
                else if (pszEnd == pszSrc)
                    rc = VINF_SUCCESS;  /* trailing delimiter */
                else
                    rc = VERR_FILE_NOT_FOUND;

                if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
                {
                    /* Path component is invalid; try to correct the casing. */
                    rc = vbsfCorrectCasing(pClient, pszFullPath, pszSrc);
                    if (RT_FAILURE(rc))
                    {
                        /* Failed, so don't bother trying any further components. */
                        if (!fEndOfString)
                            *pszEnd = RTPATH_DELIMITER; /* Restore the original full path. */
                        break;
                    }
                }

                /* Next (if any). */
                if (fEndOfString)
                    break;

                *pszEnd = RTPATH_DELIMITER;
                pszSrc = pszEnd + 1;
            }
            if (RT_FAILURE(rc))
                Log(("Unable to find suitable component rc=%d\n", rc));
        }
        else
            rc = VERR_FILE_NOT_FOUND;

    }

    /* Restore the final component if it was dropped. */
    if (pszLastComponent)
        *pszLastComponent = RTPATH_DELIMITER;

    /* might be a new file so don't fail here! */
    return VINF_SUCCESS;
}
Example #20
0
/**
 * Creates a UUID mapping for the file.
 *
 * @returns IPRT status code.
 * @param   pszCacheFile    The path to the file in the cache.
 * @param   pFileUuid       The UUID of the file.
 * @param   pszUuidMapDir   The UUID map subdirectory in the cache, if this is
 *                          wanted, otherwise NULL.
 * @param   pCfg            The configuration.
 */
static int rtDbgSymCacheAddCreateUuidMapping(const char *pszCacheFile, PRTUUID pFileUuid,
                                             const char *pszUuidMapDir, PCRTDBGSYMCACHEADDCFG pCfg)
{
    /*
     * Create the UUID map entry first, deep.
     */
    char szMapPath[RTPATH_MAX];
    int rc = RTPathJoin(szMapPath, sizeof(szMapPath) - sizeof("/xxxx/yyyy/xxxx/yyyy/xxxx/zzzzzzzzzzzz") + 1,
                        pCfg->pszCache, pszUuidMapDir);
    if (RT_FAILURE(rc))
        return RTMsgErrorRc(rc, "Error constructing UUID map path (RTPathJoin): %Rrc", rc);

    size_t cch = strlen(szMapPath);
    szMapPath[cch] = '-';

    rc = RTUuidToStr(pFileUuid, &szMapPath[cch + 2], sizeof(szMapPath) - cch);
    if (RT_FAILURE(rc))
        return RTMsgErrorRc(rc, "Error constructing UUID map path (RTUuidToStr): %Rrc", rc);

    /* Uppercase the whole lot. */
    RTStrToUpper(&szMapPath[cch + 2]);

    /* Split the first dword in two. */
    szMapPath[cch + 1] = szMapPath[cch + 2];
    szMapPath[cch + 2] = szMapPath[cch + 3];
    szMapPath[cch + 3] = szMapPath[cch + 4];
    szMapPath[cch + 4] = szMapPath[cch + 5];
    szMapPath[cch + 5] = '-';

    /*
     * Create the directories in the path.
     */
    char chSaved = RTPATH_SLASH;
    for (unsigned i = 0; i < 6; i++, cch += 5)
    {
        Assert(szMapPath[cch] == '-');
        szMapPath[cch] = '\0';
        if (!RTDirExists(szMapPath))
        {
            rc = RTDirCreate(szMapPath, 0755, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
            if (RT_FAILURE(rc))
                return RTMsgErrorRc(rc, "RTDirCreate failed on '%s' (UUID map path): %Rrc", szMapPath, rc);
        }
        szMapPath[cch] = RTPATH_SLASH;
    }
    cch -= 5;

    /*
     * Calculate a relative path from there to the actual file.
     */
    char szLinkTarget[RTPATH_MAX];
    //szMapPath[cch] = '\0';
    rc = RTPathCalcRelative(szLinkTarget, sizeof(szLinkTarget), szMapPath, pszCacheFile);
    //szMapPath[cch] = RTPATH_SLASH;
    if (RT_FAILURE(rc))
        return RTMsgErrorRc(rc, "Failed to calculate relative path from '%s' to '%s': %Rrc", szMapPath, pszCacheFile, rc);

    /*
     * If there is already a link there, check if it matches or whether
     * perhaps it's target doesn't exist.
     */
    RTFSOBJINFO ObjInfo;
    rc = RTPathQueryInfoEx(szMapPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
    if (RT_SUCCESS(rc))
    {
        if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
        {
            rc = RTPathQueryInfoEx(szMapPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
            if (RT_SUCCESS(rc))
            {
                char *pszCurTarget = NULL;
                rc = RTSymlinkReadA(szMapPath, &pszCurTarget);
                if (RT_FAILURE(rc))
                    return RTMsgErrorRc(rc, "UUID map: failed to read existing symlink '%s': %Rrc", szMapPath, rc);
                if (RTPathCompare(pszCurTarget, szLinkTarget) == 0)
                    RTMsgInfo("UUID map: existing link '%s' has the same target ('%s').", szMapPath, pszCurTarget);
                else
                {
                    RTMsgError("UUID map: Existing mapping '%s' pointing to '%s' insted of '%s'",
                               szMapPath, pszCurTarget, szLinkTarget);
                    rc = VERR_ALREADY_EXISTS;
                }
                RTStrFree(pszCurTarget);
                return rc;
            }
            else
                RTMsgInfo("UUID map: replacing dangling link '%s'", szMapPath);
            RTSymlinkDelete(szMapPath, 0 /*fFlags*/);
        }
        else if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
            return RTMsgErrorRc(VERR_IS_A_FILE,
                                "UUID map: found file at '%s', expect symbolic link or nothing.", szMapPath);
        else if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
            return RTMsgErrorRc(VERR_IS_A_DIRECTORY,
                                "UUID map: found directory at '%s', expect symbolic link or nothing.", szMapPath);
        else
            return RTMsgErrorRc(VERR_NOT_SYMLINK,
                                "UUID map: Expected symbolic link or nothing at '%s', found: fMode=%#x",
                                szMapPath, ObjInfo.Attr.fMode);
    }

    /*
     * Create the symbolic link.
     */
    rc = RTSymlinkCreate(szMapPath, szLinkTarget, RTSYMLINKTYPE_FILE, 0);
    if (RT_FAILURE(rc))
        return RTMsgErrorRc(rc, "Failed to create UUID map symlink '%s' to '%s': %Rrc", szMapPath, szLinkTarget, rc);
    RTMsgInfo("UUID map: %s  =>  %s", szMapPath, szLinkTarget);
    return VINF_SUCCESS;
}
Example #21
0
RTDECL(int) RTDirReadEx(PRTDIR pDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
{
    /*
     * Validate and digest input.
     */
    if (!rtDirValidHandle(pDir))
        return VERR_INVALID_PARAMETER;
    AssertMsgReturn(VALID_PTR(pDirEntry), ("%p\n", pDirEntry), VERR_INVALID_POINTER);
    AssertMsgReturn(    enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
                    &&  enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
                    ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
                    VERR_INVALID_PARAMETER);
    AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
    size_t cbDirEntry = sizeof(*pDirEntry);
    if (pcbDirEntry)
    {
        AssertMsgReturn(VALID_PTR(pcbDirEntry), ("%p\n", pcbDirEntry), VERR_INVALID_POINTER);
        cbDirEntry = *pcbDirEntry;
        AssertMsgReturn(cbDirEntry >= (unsigned)RT_OFFSETOF(RTDIRENTRYEX, szName[2]),
                        ("Invalid *pcbDirEntry=%d (min %d)\n", *pcbDirEntry, RT_OFFSETOF(RTDIRENTRYEX, szName[2])),
                        VERR_INVALID_PARAMETER);
    }

    /*
     * Fetch more data if necessary and/or convert the name.
     */
    int rc = rtDirReadMore(pDir);
    if (RT_SUCCESS(rc))
    {
        /*
         * Check if we've got enough space to return the data.
         */
        const char  *pszName    = pDir->pszName;
        const size_t cchName    = pDir->cchName;
        const size_t cbRequired = RT_OFFSETOF(RTDIRENTRYEX, szName[1]) + cchName;
        if (pcbDirEntry)
            *pcbDirEntry = cbRequired;
        if (cbRequired <= cbDirEntry)
        {
            /*
             * Setup the returned data.
             */
            pDirEntry->cwcShortName = 0;
            pDirEntry->wszShortName[0] = 0;
            pDirEntry->cbName  = (uint16_t)cchName;
            Assert(pDirEntry->cbName == cchName);
            memcpy(pDirEntry->szName, pszName, cchName + 1);

            /* get the info data */
            size_t cch = cchName + pDir->cchPath + 1;
            char *pszNamePath = (char *)alloca(cch);
            if (pszNamePath)
            {
                memcpy(pszNamePath, pDir->pszPath, pDir->cchPath);
                memcpy(pszNamePath + pDir->cchPath, pszName, cchName + 1);
                rc = RTPathQueryInfoEx(pszNamePath, &pDirEntry->Info, enmAdditionalAttribs, fFlags);
            }
            else
                rc = VERR_NO_MEMORY;
            if (RT_FAILURE(rc))
            {
#ifdef HAVE_DIRENT_D_TYPE
                rtDirSetDummyInfo(&pDirEntry->Info, rtDirType(pDir->Data.d_type));
#else
                rtDirSetDummyInfo(&pDirEntry->Info, RTDIRENTRYTYPE_UNKNOWN);
#endif
                rc = VWRN_NO_DIRENT_INFO;
            }

            /* free cached data */
            pDir->fDataUnread  = false;
            rtPathFreeIprt(pDir->pszName, pDir->Data.d_name);
            pDir->pszName = NULL;
        }
        else
            rc = VERR_BUFFER_OVERFLOW;
    }

    return rc;
}
/**
 * Remove one user specified file or directory.
 *
 * @returns IPRT status code, errors go via rtPathRmError.
 * @param   pOpts               The RM options.
 * @param   pszPath             The path to the file, directory, whatever.
 */
static int rtPathRmOne(PRTPATHRMCMDOPTS pOpts, const char *pszPath)
{
    /*
     * RM refuses to delete some directories.
     */
    int rc = rtPathRmOneValidate(pOpts, pszPath);
    if (RT_FAILURE(rc))
        return rc;

    /*
     * Query file system object info.
     */
    RTFSOBJINFO ObjInfo;
    rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
    if (RT_FAILURE(rc))
    {
        if (pOpts->fForce && (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND))
            return VINF_SUCCESS;
        return rtPathRmError(pOpts, pszPath, rc, "Error deleting '%s': %Rrc", pszPath, rc);
    }

    /*
     * Take type specific action.
     */
    switch (ObjInfo.Attr.fMode & RTFS_TYPE_MASK)
    {
        case RTFS_TYPE_FILE:
            return rtPathRmOneFile(pOpts, pszPath, &ObjInfo);

        case RTFS_TYPE_DIRECTORY:
            if (pOpts->fRecursive)
            {
                char szPath[RTPATH_MAX];
                rc = RTPathAbs(pszPath, szPath, sizeof(szPath));
                if (RT_FAILURE(rc))
                    return rtPathRmError(pOpts, pszPath, rc, "RTPathAbs failed on '%s': %Rrc\n", pszPath, rc);

                union
                {
                    RTDIRENTRYEX    Core;
                    uint8_t         abPadding[RTPATHRM_DIR_MAX_ENTRY_SIZE];
                } DirEntry;

                return rtPathRmRecursive(pOpts, szPath, strlen(szPath), &DirEntry.Core);
            }
            if (pOpts->fDirsAndOther)
                return rtPathRmOneDir(pOpts, pszPath);
            return rtPathRmError(pOpts, pszPath, VERR_IS_A_DIRECTORY, "Cannot remove '%s': %Rrc\n", pszPath, VERR_IS_A_DIRECTORY);

        case RTFS_TYPE_SYMLINK:
            return rtPathRmOneSymlink(pOpts, pszPath);

        case RTFS_TYPE_FIFO:
        case RTFS_TYPE_DEV_CHAR:
        case RTFS_TYPE_DEV_BLOCK:
        case RTFS_TYPE_SOCKET:
            return rtPathRmOneFile(pOpts, pszPath, &ObjInfo);

        case RTFS_TYPE_WHITEOUT:
        default:
            return rtPathRmError(pOpts, pszPath, VERR_UNEXPECTED_FS_OBJ_TYPE,
                                 "Object '%s' has an unknown file type: %o\n", pszPath, ObjInfo.Attr.fMode & RTFS_TYPE_MASK);

    }
}