1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
7 #include "longfilepathwrappers.h"
15 static const WCHAR* ExtendedPrefix;
16 static const WCHAR* DevicePathPrefix;
17 static const WCHAR* UNCPathPrefix;
18 static const WCHAR* UNCExtendedPathPrefix;
19 static const WCHAR VolumeSeparatorChar;
20 #define UNCPATHPREFIX W("\\\\")
22 static const WCHAR DirectorySeparatorChar;
23 static const WCHAR AltDirectorySeparatorChar;
25 static BOOL IsExtended(SString & path);
26 static BOOL IsUNCExtended(SString & path);
27 static BOOL ContainsDirectorySeparator(SString & path);
28 static BOOL IsDirectorySeparator(WCHAR c);
29 static BOOL IsPathNotFullyQualified(SString & path);
30 static BOOL IsDevice(SString & path);
32 static HRESULT NormalizePath(SString& path);
35 static void NormalizeDirectorySeparators(SString& path);
41 LPCWSTR lpLibFileName,
59 LongPathString path(LongPathString::Literal, lpLibFileName);
61 if (LongFile::IsPathNotFullyQualified(path) || SUCCEEDED(LongFile::NormalizePath(path)))
64 //Adding the assert to ensure relative paths which are not just filenames are not used for LoadLibrary Calls
65 _ASSERTE(!LongFile::IsPathNotFullyQualified(path) || !LongFile::ContainsDirectorySeparator(path));
66 LongFile::NormalizeDirectorySeparators(path);
69 ret = LoadLibraryExW(path.GetUnicode(), hFile, dwFlags);
72 lastError = GetLastError();
82 SetLastError(lastError);
90 _In_ LPCWSTR lpFileName,
91 _In_ DWORD dwDesiredAccess,
92 _In_ DWORD dwShareMode,
93 _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
94 _In_ DWORD dwCreationDisposition,
95 _In_ DWORD dwFlagsAndAttributes,
96 _In_opt_ HANDLE hTemplateFile
107 HANDLE ret = INVALID_HANDLE_VALUE;
111 LongPathString path(LongPathString::Literal, lpFileName);
113 if (SUCCEEDED(LongFile::NormalizePath(path)))
115 ret = CreateFileW(path.GetUnicode(),
118 lpSecurityAttributes,
119 dwCreationDisposition,
120 dwFlagsAndAttributes,
125 lastError = GetLastError();
127 EX_CATCH_HRESULT(hr);
133 else if(ret == INVALID_HANDLE_VALUE)
135 SetLastError(lastError);
142 SetFileAttributesWrapper(
143 _In_ LPCWSTR lpFileName,
144 _In_ DWORD dwFileAttributes
159 LongPathString path(LongPathString::Literal, lpFileName);
161 if (SUCCEEDED(LongFile::NormalizePath(path)))
163 ret = SetFileAttributesW(
169 lastError = GetLastError();
171 EX_CATCH_HRESULT(hr);
177 else if(ret == FALSE)
179 SetLastError(lastError);
186 GetFileAttributesWrapper(
187 _In_ LPCWSTR lpFileName
197 DWORD ret = INVALID_FILE_ATTRIBUTES;
202 LongPathString path(LongPathString::Literal, lpFileName);
204 if (SUCCEEDED(LongFile::NormalizePath(path)))
206 ret = GetFileAttributesW(
211 lastError = GetLastError();
213 EX_CATCH_HRESULT(hr);
219 else if(ret == INVALID_FILE_ATTRIBUTES)
221 SetLastError(lastError);
228 GetFileAttributesExWrapper(
229 _In_ LPCWSTR lpFileName,
230 _In_ GET_FILEEX_INFO_LEVELS fInfoLevelId,
231 _Out_writes_bytes_(sizeof(WIN32_FILE_ATTRIBUTE_DATA)) LPVOID lpFileInformation
246 LongPathString path(LongPathString::Literal, lpFileName);
248 if (SUCCEEDED(LongFile::NormalizePath(path)))
250 ret = GetFileAttributesExW(
258 lastError = GetLastError();
260 EX_CATCH_HRESULT(hr);
266 else if(ret == FALSE)
268 SetLastError(lastError);
276 _In_ LPCWSTR lpFileName
291 LongPathString path(LongPathString::Literal, lpFileName);
293 if (SUCCEEDED(LongFile::NormalizePath(path)))
300 lastError = GetLastError();
302 EX_CATCH_HRESULT(hr);
308 else if(ret == FALSE)
310 SetLastError(lastError);
319 _In_ LPCWSTR lpExistingFileName,
320 _In_ LPCWSTR lpNewFileName,
321 _In_ BOOL bFailIfExists
336 LongPathString Existingpath(LongPathString::Literal, lpExistingFileName);
337 LongPathString Newpath(LongPathString::Literal, lpNewFileName);
339 if (SUCCEEDED(LongFile::NormalizePath(Existingpath)) && SUCCEEDED(LongFile::NormalizePath(Newpath)))
342 Existingpath.GetUnicode(),
343 Newpath.GetUnicode(),
348 lastError = GetLastError();
350 EX_CATCH_HRESULT(hr);
356 else if(ret == FALSE)
358 SetLastError(lastError);
366 _In_ LPCWSTR lpExistingFileName,
367 _In_opt_ LPCWSTR lpNewFileName,
383 LongPathString Existingpath(LongPathString::Literal, lpExistingFileName);
384 LongPathString Newpath(LongPathString::Literal, lpNewFileName);
386 if (SUCCEEDED(LongFile::NormalizePath(Existingpath)) && SUCCEEDED(LongFile::NormalizePath(Newpath)))
389 Existingpath.GetUnicode(),
390 Newpath.GetUnicode(),
395 lastError = GetLastError();
397 EX_CATCH_HRESULT(hr);
403 else if(ret == FALSE)
405 SetLastError(lastError);
414 _In_opt_ LPCWSTR lpPath,
415 _In_ LPCWSTR lpFileName,
416 _In_opt_ LPCWSTR lpExtension,
419 _Out_opt_ LPWSTR * lpFilePart
434 LongPathString Existingpath(LongPathString::Literal, lpPath);
438 if (FAILED(LongFile::NormalizePath(Existingpath)))
444 lpPath = Existingpath.GetUnicode();
461 COUNT_T size = lpBuffer.GetUnicodeAllocation() + 1;
468 lpBuffer.OpenUnicodeBuffer(size - 1),
474 lpBuffer.CloseBuffer();
480 lpBuffer.OpenUnicodeBuffer(ret - 1),
485 lpBuffer.CloseBuffer(ret);
489 lastError = GetLastError();
491 EX_CATCH_HRESULT(hr);
499 SetLastError(lastError);
507 GetShortPathNameWrapper(
508 _In_ LPCWSTR lpszLongPath,
509 SString& lpszShortPath
524 LongPathString longPath(LongPathString::Literal, lpszLongPath);
526 if (SUCCEEDED(LongFile::NormalizePath(longPath)))
528 COUNT_T size = lpszShortPath.GetUnicodeAllocation() + 1;
530 ret = GetShortPathNameW(
531 longPath.GetUnicode(),
532 lpszShortPath.OpenUnicodeBuffer(size - 1),
538 lpszShortPath.CloseBuffer();
539 ret = GetShortPathNameW(
540 longPath.GetUnicode(),
541 lpszShortPath.OpenUnicodeBuffer(ret -1),
546 lpszShortPath.CloseBuffer(ret);
549 lastError = GetLastError();
551 EX_CATCH_HRESULT(hr);
559 SetLastError(lastError);
566 GetLongPathNameWrapper(
567 _In_ LPCWSTR lpszShortPath,
568 SString& lpszLongPath
583 LongPathString shortPath(LongPathString::Literal, lpszShortPath);
585 if (SUCCEEDED(LongFile::NormalizePath(shortPath)))
587 COUNT_T size = lpszLongPath.GetUnicodeAllocation() + 1;
589 ret = GetLongPathNameW(
590 shortPath.GetUnicode(),
591 lpszLongPath.OpenUnicodeBuffer(size - 1),
597 lpszLongPath.CloseBuffer();
598 ret = GetLongPathNameW(
599 shortPath.GetUnicode(),
600 lpszLongPath.OpenUnicodeBuffer(ret - 1),
606 lpszLongPath.CloseBuffer(ret);
609 lastError = GetLastError();
611 EX_CATCH_HRESULT(hr);
619 SetLastError(lastError);
626 CreateDirectoryWrapper(
627 _In_ LPCWSTR lpPathName,
628 _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes
643 LongPathString path(LongPathString::Literal, lpPathName);
645 if (SUCCEEDED(LongFile::NormalizePath(path)))
647 ret = CreateDirectoryW(
653 lastError = GetLastError();
655 EX_CATCH_HRESULT(hr);
661 else if(ret == FALSE)
663 SetLastError(lastError);
670 RemoveDirectoryWrapper(
671 _In_ LPCWSTR lpPathName
686 LongPathString path(LongPathString::Literal, lpPathName);
688 if (SUCCEEDED(LongFile::NormalizePath(path)))
690 ret = RemoveDirectoryW(
695 lastError = GetLastError();
697 EX_CATCH_HRESULT(hr);
703 else if(ret == FALSE)
705 SetLastError(lastError);
711 GetModuleFileNameWrapper(
712 _In_opt_ HMODULE hModule,
728 COUNT_T size = buffer.GetUnicodeAllocation() + 1;
730 ret = GetModuleFileNameW(
732 buffer.OpenUnicodeBuffer(size - 1),
739 buffer.CloseBuffer();
741 ret = GetModuleFileNameW(
743 buffer.OpenUnicodeBuffer(size - 1),
750 lastError = GetLastError();
751 buffer.CloseBuffer(ret);
753 EX_CATCH_HRESULT(hr);
761 SetLastError(lastError);
767 UINT WINAPI GetTempFileNameWrapper(
768 _In_ LPCTSTR lpPathName,
769 _In_ LPCTSTR lpPrefixString,
771 SString& lpTempFileName
786 //Change the behaviour in Redstone to retry
787 COUNT_T size = MAX_LONGPATH;
788 WCHAR* buffer = lpTempFileName.OpenUnicodeBuffer(size - 1);
789 ret = GetTempFileNameW(
796 lastError = GetLastError();
797 size = (COUNT_T)wcslen(buffer);
798 lpTempFileName.CloseBuffer(size);
801 EX_CATCH_HRESULT(hr);
809 SetLastError(lastError);
814 DWORD WINAPI GetTempPathWrapper(
830 //Change the behaviour in Redstone to retry
831 COUNT_T size = MAX_LONGPATH;
835 lpBuffer.OpenUnicodeBuffer(size - 1)
838 lastError = GetLastError();
839 lpBuffer.CloseBuffer(ret);
841 EX_CATCH_HRESULT(hr);
849 SetLastError(lastError);
855 DWORD WINAPI GetCurrentDirectoryWrapper(
871 //Change the behaviour in Redstone to retry
872 COUNT_T size = MAX_LONGPATH;
874 ret = GetCurrentDirectoryW(
876 lpBuffer.OpenUnicodeBuffer(size - 1)
879 lastError = GetLastError();
880 lpBuffer.CloseBuffer(ret);
882 EX_CATCH_HRESULT(hr);
890 SetLastError(lastError);
896 DWORD WINAPI GetEnvironmentVariableWrapper(
897 _In_opt_ LPCTSTR lpName,
898 _Out_opt_ SString& lpBuffer
914 COUNT_T size = lpBuffer.GetUnicodeAllocation() + 1;
916 ret = GetEnvironmentVariableW(
918 lpBuffer.OpenUnicodeBuffer(size - 1),
922 // We loop round getting the length of the env var and then trying to copy
923 // the value into a the allocated buffer. Usually we'll go through this loop
924 // precisely once, but the caution is ncessary in case the variable mutates
925 // beneath us, as the environment variable can be modified by another thread
926 //between two calls to GetEnvironmentVariableW
931 lpBuffer.CloseBuffer();
932 ret = GetEnvironmentVariableW(
934 lpBuffer.OpenUnicodeBuffer(size - 1),
938 lastError = GetLastError();
939 lpBuffer.CloseBuffer(ret);
941 EX_CATCH_HRESULT(hr);
949 SetLastError(lastError);
959 CreateHardLinkWrapper(
960 _In_ LPCWSTR lpFileName,
961 _In_ LPCWSTR lpExistingFileName,
962 _Reserved_ LPSECURITY_ATTRIBUTES lpSecurityAttributes
977 LongPathString Existingpath(LongPathString::Literal, lpExistingFileName);
978 LongPathString FileName(LongPathString::Literal, lpFileName);
980 if (SUCCEEDED(LongFile::NormalizePath(Existingpath)) && SUCCEEDED(LongFile::NormalizePath(FileName)))
982 ret = CreateHardLinkW(
983 Existingpath.GetUnicode(),
984 FileName.GetUnicode(),
989 lastError = GetLastError();
991 EX_CATCH_HRESULT(hr);
997 else if(ret == FALSE)
999 SetLastError(lastError);
1007 _In_ LPCWSTR lpExistingFileName,
1008 _In_ LPCWSTR lpNewFileName,
1009 _In_opt_ LPPROGRESS_ROUTINE lpProgressRoutine,
1010 _In_opt_ LPVOID lpData,
1011 _When_(pbCancel != NULL, _Pre_satisfies_(*pbCancel == FALSE))
1012 _Inout_opt_ LPBOOL pbCancel,
1013 _In_ DWORD dwCopyFlags
1028 LongPathString Existingpath(LongPathString::Literal, lpExistingFileName);
1029 LongPathString Newpath(LongPathString::Literal, lpNewFileName);
1031 if (SUCCEEDED(LongFile::NormalizePath(Existingpath)) && SUCCEEDED(LongFile::NormalizePath(Newpath)))
1034 Existingpath.GetUnicode(),
1035 Newpath.GetUnicode(),
1043 lastError = GetLastError();
1045 EX_CATCH_HRESULT(hr);
1051 else if(ret == FALSE)
1053 SetLastError(lastError);
1060 FindFirstFileExWrapper(
1061 _In_ LPCWSTR lpFileName,
1062 _In_ FINDEX_INFO_LEVELS fInfoLevelId,
1063 _Out_writes_bytes_(sizeof(WIN32_FIND_DATAW)) LPVOID lpFindFileData,
1064 _In_ FINDEX_SEARCH_OPS fSearchOp,
1065 _Reserved_ LPVOID lpSearchFilter,
1066 _In_ DWORD dwAdditionalFlags
1076 HANDLE ret = INVALID_HANDLE_VALUE;
1081 LongPathString path(LongPathString::Literal, lpFileName);
1083 if (SUCCEEDED(LongFile::NormalizePath(path)))
1085 ret = FindFirstFileExW(
1095 lastError = GetLastError();
1097 EX_CATCH_HRESULT(hr);
1103 else if(ret == INVALID_HANDLE_VALUE)
1105 SetLastError(lastError);
1110 #endif //!FEATURE_PAL
1115 #if ! defined(DACCESS_COMPILE) && !defined(SELF_NO_HOST)
1116 extern HINSTANCE g_pMSCorEE;
1117 #endif// ! defined(DACCESS_COMPILE) && !defined(SELF_NO_HOST)
1119 BOOL PAL_GetPALDirectoryWrapper(SString& pbuffer)
1126 HINSTANCE hinst = NULL;
1128 #if ! defined(DACCESS_COMPILE) && !defined(SELF_NO_HOST)
1130 #endif// ! defined(DACCESS_COMPILE) && !defined(SELF_NO_HOST)
1132 #ifndef CROSSGEN_COMPILE
1133 _ASSERTE(hinst != NULL);
1136 dwPath = WszGetModuleFileName(hinst, pPath);
1140 hr = HRESULT_FROM_GetLastErrorNA();
1144 hr = CopySystemDirectory(pPath, pbuffer);
1147 return (hr == S_OK);
1152 BOOL PAL_GetPALDirectoryWrapper(SString& pbuffer)
1154 BOOL retval = FALSE;
1155 COUNT_T size = MAX_LONGPATH;
1157 if(!(retval = PAL_GetPALDirectoryW(pbuffer.OpenUnicodeBuffer(size - 1), &size)))
1159 pbuffer.CloseBuffer(0);
1160 retval = PAL_GetPALDirectoryW(pbuffer.OpenUnicodeBuffer(size - 1), &size);
1163 pbuffer.CloseBuffer(size);
1168 #endif // FEATURE_PAL
1171 //Implementation of LongFile Helpers
1172 const WCHAR LongFile::DirectorySeparatorChar = W('\\');
1173 const WCHAR LongFile::AltDirectorySeparatorChar = W('/');
1175 const WCHAR LongFile::VolumeSeparatorChar = W(':');
1176 const WCHAR* LongFile::ExtendedPrefix = W("\\\\?\\");
1177 const WCHAR* LongFile::DevicePathPrefix = W("\\\\.\\");
1178 const WCHAR* LongFile::UNCExtendedPathPrefix = W("\\\\?\\UNC\\");
1179 const WCHAR* LongFile::UNCPathPrefix = UNCPATHPREFIX;
1181 void LongFile::NormalizeDirectorySeparators(SString& path)
1183 for(SString::Iterator i = path.Begin(); i < path.End(); ++i)
1185 if (*i == AltDirectorySeparatorChar)
1187 path.Replace(i, DirectorySeparatorChar);
1192 BOOL LongFile::IsExtended(SString & path)
1194 return path.BeginsWith(ExtendedPrefix);
1197 BOOL LongFile::IsUNCExtended(SString & path)
1200 return path.BeginsWith(UNCExtendedPathPrefix);
1203 // Relative here means it could be relative to current directory on the relevant drive
1204 // NOTE: Relative segments ( \..\) are not considered relative
1205 // Returns true if the path specified is relative to the current drive or working directory.
1206 // Returns false if the path is fixed to a specific drive or UNC path. This method does no
1207 // validation of the path (URIs will be returned as relative as a result).
1208 // Handles paths that use the alternate directory separator. It is a frequent mistake to
1209 // assume that rooted paths (Path.IsPathRooted) are not relative. This isn't the case.
1211 BOOL LongFile::IsPathNotFullyQualified(SString & path)
1213 if (path.GetCount() < 2)
1215 return TRUE; // It isn't fixed, it must be relative. There is no way to specify a fixed path with one character (or less).
1218 if (IsDirectorySeparator(path[0]))
1220 return !IsDirectorySeparator(path[1]); // There is no valid way to specify a relative path with two initial slashes
1223 return !((path.GetCount() >= 3) //The only way to specify a fixed path that doesn't begin with two slashes is the drive, colon, slash format- i.e. "C:\"
1224 && (path[1] == VolumeSeparatorChar)
1225 && IsDirectorySeparator(path[2]));
1228 BOOL LongFile::IsDevice(SString & path)
1230 return path.BeginsWith(DevicePathPrefix);
1233 // This function will normalize paths if the path length exceeds MAX_PATH
1234 // The normalization examples are :
1235 // C:\foo\<long>\bar => \\?\C:\foo\<long>\bar
1236 // \\server\<long>\bar => \\?\UNC\server\<long>\bar
1237 HRESULT LongFile::NormalizePath(SString & path)
1241 COUNT_T prefixLen = 0;
1242 if (path.IsEmpty()|| IsDevice(path) || IsExtended(path) || IsUNCExtended(path))
1245 if (!IsPathNotFullyQualified(path) && path.GetCount() < MAX_LONGPATH)
1248 //Now the path will be normalized
1250 SString originalPath(path);
1251 SString prefix(ExtendedPrefix);
1252 prefixLen = prefix.GetCount();
1254 if (path.BeginsWith(UNCPathPrefix))
1256 prefix.Set(UNCExtendedPathPrefix);
1257 //In this case if path is \\server the extended syntax should be like \\?\UNC\server
1258 //The below logic populates the path from prefixLen offset from the start. This ensures that first 2 characters are overwritten
1260 prefixLen = prefix.GetCount() - (COUNT_T)wcslen(UNCPATHPREFIX);
1261 _ASSERTE(prefixLen > 0 );
1265 COUNT_T size = path.GetUnicodeAllocation() + 1;
1266 WCHAR* buffer = path.OpenUnicodeBuffer(size - 1);
1268 ret = GetFullPathNameW(
1269 originalPath.GetUnicode(),
1270 size - prefixLen, //memory avilable for path after reserving for prefix
1271 (buffer + prefixLen), //reserve memory for prefix
1280 if (ret > size - prefixLen)
1283 size = ret + prefixLen;
1284 buffer = path.OpenUnicodeBuffer(size -1);
1286 ret = GetFullPathNameW(
1287 originalPath.GetUnicode(),
1288 ret, // memory required for the path
1289 (buffer + prefixLen), //reserve memory for prefix
1293 _ASSERTE(ret < size - prefixLen);
1301 SString fullpath(SString::Literal,buffer + prefixLen);
1303 //Check if the resolved path is a UNC. By default we assume relative path to resolve to disk
1304 if (fullpath.BeginsWith(UNCPathPrefix) && prefixLen != prefix.GetCount() - (COUNT_T)wcslen(UNCPATHPREFIX))
1307 //Remove the leading '\\' from the UNC path to be replaced with UNCExtendedPathPrefix
1308 fullpath.Replace(fullpath.Begin(), (COUNT_T)wcslen(UNCPATHPREFIX), UNCExtendedPathPrefix);
1314 //wcscpy_s always termintes with NULL, so we are saving the character that will be overwriiten
1315 WCHAR temp = buffer[prefix.GetCount()];
1316 wcscpy_s(buffer, prefix.GetCount() + 1, prefix.GetUnicode());
1317 buffer[prefix.GetCount()] = temp;
1318 path.CloseBuffer(ret + prefixLen);
1324 BOOL LongFile::IsExtended(SString & path)
1329 BOOL LongFile::IsUNCExtended(SString & path)
1334 BOOL LongFile::IsPathNotFullyQualified(SString & path)
1339 BOOL LongFile::IsDevice(SString & path)
1344 //Don't need to do anything For XPlat
1345 HRESULT LongFile::NormalizePath(SString & path)
1349 #endif //FEATURE_PAL
1351 BOOL LongFile::ContainsDirectorySeparator(SString & path)
1353 return path.Find(path.Begin(), DirectorySeparatorChar) || path.Find(path.Begin(), AltDirectorySeparatorChar);
1356 BOOL LongFile::IsDirectorySeparator(WCHAR c)
1358 return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar;