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; }
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 }
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; }
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; }
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; }
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; }
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; }
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; }
/** * 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); }
/** * 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); }
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); }
/** * 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; }
/** * 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; }
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); } }