/** * Get the absolute path (no symlinks, no . or .. components), assuming the * given base path as the current directory. The resulting path doesn't have * to exist. * * @returns iprt status code. * @param pszBase The base path to act like a current directory. * When NULL, the actual cwd is used (i.e. the call * is equivalent to RTPathAbs(pszPath, ...). * @param pszPath The path to resolve. * @param pszAbsPath Where to store the absolute path. * @param cchAbsPath Size of the buffer. */ RTDECL(int) RTPathAbsEx(const char *pszBase, const char *pszPath, char *pszAbsPath, size_t cchAbsPath) { if ( pszBase && pszPath && !rtPathVolumeSpecLen(pszPath) ) { #if defined(RT_OS_WINDOWS) /* The format for very long paths is not supported. */ if ( RTPATH_IS_SLASH(pszBase[0]) && RTPATH_IS_SLASH(pszBase[1]) && pszBase[2] == '?' && RTPATH_IS_SLASH(pszBase[3]) ) return VERR_INVALID_NAME; #endif /** @todo there are a couple of things which isn't 100% correct, although the * current code will have to do for now, no time to fix. * * 1) On Windows & OS/2 we confuse '/' with an abspath spec and will * not necessarily resolve it on the right drive. * 2) A trailing slash in the base might cause UNC names to be created. */ size_t const cchPath = strlen(pszPath); char szTmpPath[RTPATH_MAX]; if (RTPATH_IS_SLASH(pszPath[0])) { /* join the disk name from base and the path (DOS systems only) */ size_t const cchVolSpec = rtPathVolumeSpecLen(pszBase); if (cchVolSpec + cchPath + 1 > sizeof(szTmpPath)) return VERR_FILENAME_TOO_LONG; memcpy(szTmpPath, pszBase, cchVolSpec); memcpy(&szTmpPath[cchVolSpec], pszPath, cchPath + 1); } else { /* join the base path and the path */ size_t const cchBase = strlen(pszBase); if (cchBase + 1 + cchPath + 1 > sizeof(szTmpPath)) return VERR_FILENAME_TOO_LONG; memcpy(szTmpPath, pszBase, cchBase); szTmpPath[cchBase] = RTPATH_DELIMITER; memcpy(&szTmpPath[cchBase + 1], pszPath, cchPath + 1); } return RTPathAbs(szTmpPath, pszAbsPath, cchAbsPath); } /* Fallback to the non *Ex version */ return RTPathAbs(pszPath, pszAbsPath, cchAbsPath); }
RTDECL(int) RTDirCreateFullPath(const char *pszPath, RTFMODE fMode) { /* * Resolve the path. */ char szAbsPath[RTPATH_MAX]; int rc = RTPathAbs(pszPath, szAbsPath, sizeof(szAbsPath)); if (RT_FAILURE(rc)) return rc; /* * Iterate the path components making sure each of them exists. */ /* skip volume name */ char *psz = &szAbsPath[rtPathVolumeSpecLen(szAbsPath)]; /* skip the root slash if any */ if ( psz[0] == '/' #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) || psz[0] == '\\' #endif ) psz++; /* iterate over path components. */ do { /* the next component is NULL, stop iterating */ if (!*psz) break; #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) psz = strpbrk(psz, "\\/"); #else psz = strchr(psz, '/'); #endif if (psz) *psz = '\0'; /* * ASSUME that RTDirCreate will return VERR_ALREADY_EXISTS and not VERR_ACCESS_DENIED in those cases * where the directory exists but we don't have write access to the parent directory. */ rc = RTDirCreate(szAbsPath, fMode, 0); if (rc == VERR_ALREADY_EXISTS) rc = VINF_SUCCESS; if (!psz) break; *psz++ = RTPATH_DELIMITER; } while (RT_SUCCESS(rc)); return rc; }
RTDECL(int) RTSymlinkCreate(const char *pszSymlink, const char *pszTarget, RTSYMLINKTYPE enmType, uint32_t fCreate) { /* * Validate the input. */ AssertReturn(enmType > RTSYMLINKTYPE_INVALID && enmType < RTSYMLINKTYPE_END, VERR_INVALID_PARAMETER); AssertPtrReturn(pszSymlink, VERR_INVALID_POINTER); AssertPtrReturn(pszTarget, VERR_INVALID_POINTER); /* * Resolve the API. */ typedef BOOLEAN (WINAPI *PFNCREATESYMBOLICLINKW)(LPCWSTR, LPCWSTR, DWORD); static PFNCREATESYMBOLICLINKW s_pfnCreateSymbolicLinkW = NULL; static bool s_fTried = FALSE; if (!s_fTried) { HMODULE hmod = LoadLibrary("KERNEL32.DLL"); if (hmod) { PFNCREATESYMBOLICLINKW pfn = (PFNCREATESYMBOLICLINKW)GetProcAddress(hmod, "CreateSymbolicLinkW"); if (pfn) s_pfnCreateSymbolicLinkW = pfn; } s_fTried = true; } if (!s_pfnCreateSymbolicLinkW) { LogFlow(("RTSymlinkCreate(%p={%s}, %p={%s}, %d, %#x): returns VERR_NOT_SUPPORTED - Windows API not found\n", pszSymlink, pszSymlink, pszTarget, pszTarget, enmType, fCreate)); return VERR_NOT_SUPPORTED; } /* * Convert the paths. */ PRTUTF16 pwszNativeSymlink; int rc = RTStrToUtf16(pszSymlink, &pwszNativeSymlink); if (RT_SUCCESS(rc)) { PRTUTF16 pwszNativeTarget; rc = RTStrToUtf16(pszTarget, &pwszNativeTarget); if (RT_SUCCESS(rc)) { /* * Massage the target path, determin the link type. */ size_t cchTarget = strlen(pszTarget); size_t cchVolSpecTarget = rtPathVolumeSpecLen(pszTarget); #if 0 /* looks like this isn't needed after all. That makes everything much simper :-) */ if ( cchTarget > RT_MIN(cchVolSpecTarget, 1) && RTPATH_IS_SLASH(pszTarget[cchTarget - 1])) { size_t cwcNativeTarget = RTUtf16Len(pwszNativeTarget); size_t offFromEnd = 1; while ( offFromEnd < cchTarget && cchTarget - offFromEnd >= cchVolSpecTarget && RTPATH_IS_SLASH(pszTarget[cchTarget - offFromEnd])) { Assert(offFromEnd < cwcNativeTarget); pwszNativeTarget[cwcNativeTarget - offFromEnd] = 0; offFromEnd++; } } #endif if (enmType == RTSYMLINKTYPE_UNKNOWN) { if ( cchTarget > cchVolSpecTarget && RTPATH_IS_SLASH(pszTarget[cchTarget - 1])) enmType = RTSYMLINKTYPE_DIR; else if (cchVolSpecTarget) { /** @todo this is subject to sharing violations. */ DWORD dwAttr = GetFileAttributesW(pwszNativeTarget); if ( dwAttr != INVALID_FILE_ATTRIBUTES && (dwAttr & FILE_ATTRIBUTE_DIRECTORY)) enmType = RTSYMLINKTYPE_DIR; } else { /** @todo Join the symlink directory with the target and * look up the attributes on that. -lazy bird. */ } } /* * Create the link. */ if (s_pfnCreateSymbolicLinkW(pwszNativeSymlink, pwszNativeTarget, enmType == RTSYMLINKTYPE_DIR)) rc = VINF_SUCCESS; else rc = RTErrConvertFromWin32(GetLastError()); RTUtf16Free(pwszNativeTarget); } RTUtf16Free(pwszNativeSymlink); } LogFlow(("RTSymlinkCreate(%p={%s}, %p={%s}, %d, %#x): returns %Rrc\n", pszSymlink, pszSymlink, pszTarget, pszTarget, enmType, fCreate, rc)); return rc; }