@@ -730,6 +730,88 @@ bool pal::clr_palstring(const char* cstr, pal::string_t* out)
730730
731731// Return if path is valid and file exists, return true and adjust path as appropriate.
732732bool pal::realpath(string_t* path, bool skip_error_logging)
733+ {
734+ return fullpath(path, skip_error_logging);
735+ }
736+
737+ typedef std::unique_ptr<std::remove_pointer<HANDLE>::type, decltype(&::CloseHandle)> SmartHandle;
738+
739+ // Like realpath, but resolves symlinks.
740+ bool pal::realpath2(string_t* path, bool skip_error_logging)
741+ {
742+ if (path->empty())
743+ {
744+ return false;
745+ }
746+
747+ // Use CreateFileW + GetFinalPathNameByHandleW to resolve symlinks
748+
749+ SmartHandle file(
750+ ::CreateFileW(
751+ path->c_str(),
752+ 0, // Querying only
753+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
754+ nullptr, // default security
755+ OPEN_EXISTING, // existing file
756+ FILE_ATTRIBUTE_NORMAL, // normal file
757+ nullptr), // No attribute template
758+ &::CloseHandle);
759+
760+ char_t buf[MAX_PATH];
761+ size_t size;
762+
763+ if (file.get() == INVALID_HANDLE_VALUE)
764+ {
765+ // If we get "access denied" that may mean the path represents a directory.
766+ // Even if not, we can fall back to GetFullPathNameW, which doesn't require a HANDLE
767+
768+ auto error = ::GetLastError();
769+ file.release();
770+ if (ERROR_ACCESS_DENIED != error)
771+ {
772+ goto invalidPath;
773+ }
774+ }
775+ else
776+ {
777+ size = ::GetFinalPathNameByHandleW(file.get(), buf, MAX_PATH, FILE_NAME_NORMALIZED);
778+ // If size is 0, this call failed. Fall back to GetFullPathNameW, below
779+ if (size != 0)
780+ {
781+ string_t str;
782+ if (size < MAX_PATH)
783+ {
784+ str.assign(buf);
785+ }
786+ else
787+ {
788+ str.resize(size, 0);
789+ size = ::GetFinalPathNameByHandleW(file.get(), (LPWSTR)str.data(), static_cast<uint32_t>(size), FILE_NAME_NORMALIZED);
790+ assert(size <= str.size());
791+
792+ if (size == 0)
793+ {
794+ goto invalidPath;
795+ }
796+ }
797+
798+ // Remove the \\?\ prefix, unless it is necessary or was already there
799+ if (LongFile::IsExtended(str) && !LongFile::IsExtended(*path) &&
800+ !LongFile::ShouldNormalize(str.substr(LongFile::ExtendedPrefix.size())))
801+ {
802+ str.erase(0, LongFile::ExtendedPrefix.size());
803+ }
804+
805+ *path = str;
806+ return true;
807+ }
808+ }
809+
810+ // If the above fails, fall back to fullpath
811+ return fullpath(path, skip_error_logging);
812+ }
813+
814+ bool pal::fullpath(string_t* path, bool skip_error_logging)
733815{
734816 if (path->empty())
735817 {
0 commit comments