internal static string NormalizePath(string path) { ErrorUtilities.VerifyThrowArgumentLength(path, "path"); int errorCode = 0; int num2 = NativeMethodsShared.MAX_PATH; StringBuilder buffer = new StringBuilder(num2 + 1); int num3 = NativeMethodsShared.GetFullPathName(path, buffer.Capacity, buffer, IntPtr.Zero); errorCode = Marshal.GetLastWin32Error(); if (num3 > num2) { num2 = num3; buffer = new StringBuilder(num2 + 1); num3 = NativeMethodsShared.GetFullPathName(path, buffer.Capacity, buffer, IntPtr.Zero); errorCode = Marshal.GetLastWin32Error(); ErrorUtilities.VerifyThrow((num3 + 1) < buffer.Capacity, "Final buffer capacity should be sufficient for full path name and null terminator."); } if (num3 <= 0) { errorCode = -2147024896 | errorCode; Marshal.ThrowExceptionForHR(errorCode); return(null); } string message = buffer.ToString(); if (message.Length >= 260) { throw new PathTooLongException(message); } message = Path.Combine(message, string.Empty); if (message.StartsWith(@"\\", StringComparison.OrdinalIgnoreCase)) { int num4 = 2; while (num4 < message.Length) { char ch = message[num4]; if (ch.Equals('\\')) { num4++; break; } num4++; } if ((num4 == message.Length) || (message.IndexOf(@"\\?\globalroot", StringComparison.OrdinalIgnoreCase) != -1)) { message = Path.GetFullPath(message); } } if (string.Equals(message, path, StringComparison.Ordinal)) { message = path; } return(message); }
/// <summary> /// Gets the canonicalized full path of the provided path. /// Path.GetFullPath The pre .Net 4.6.2 implementation of Path.GetFullPath is slow and creates strings in its work. /// Therefore MSBuild has its own implementation on full framework. /// Guidance for use: call this on all paths accepted through public entry /// points that need normalization. After that point, only verify the path /// is rooted, using ErrorUtilities.VerifyThrowPathRooted. /// ASSUMES INPUT IS ALREADY UNESCAPED. /// </summary> internal static string NormalizePath(string path) { ErrorUtilities.VerifyThrowArgumentLength(path, "path"); #if FEATURE_LEGACY_GETFULLPATH if (NativeMethodsShared.IsWindows) { int errorCode = 0; // 0 == success in Win32 #if _DEBUG // Just to make sure and exercise the code that sets the correct buffer size // we'll start out with it deliberately too small int lenDir = 1; #else int lenDir = MaxPath; #endif unsafe { char *finalBuffer = stackalloc char[lenDir + 1]; // One extra for the null terminator int length = NativeMethodsShared.GetFullPathName(path, lenDir + 1, finalBuffer, IntPtr.Zero); errorCode = Marshal.GetLastWin32Error(); // If the length returned from GetFullPathName is greater than the length of the buffer we've // allocated, then reallocate the buffer with the correct size, and repeat the call if (length > lenDir) { lenDir = length; char *tempBuffer = stackalloc char[lenDir]; finalBuffer = tempBuffer; length = NativeMethodsShared.GetFullPathName(path, lenDir, finalBuffer, IntPtr.Zero); errorCode = Marshal.GetLastWin32Error(); // If we find that the length returned from GetFullPathName is longer than the buffer capacity, then // something very strange is going on! ErrorUtilities.VerifyThrow( length <= lenDir, "Final buffer capacity should be sufficient for full path name and null terminator."); } if (length > 0) { // In order to prevent people from taking advantage of our ability to extend beyond MaxPath // since it is unlikely that the CLR fix will be a complete removal of maxpath madness // we reluctantly have to restrict things here. if (length >= MaxPath) { throw new PathTooLongException(); } // Avoid creating new strings unnecessarily string finalFullPath = AreStringsEqual(finalBuffer, length, path) ? path : new string( finalBuffer, startIndex : 0, length : length); // We really don't care about extensions here, but Path.HasExtension provides a great way to // invoke the CLR's invalid path checks (these are independent of path length) Path.HasExtension(finalFullPath); if (finalFullPath.StartsWith(@"\\", StringComparison.Ordinal)) { // If we detect we are a UNC path then we need to use the regular get full path in order to do the correct checks for UNC formatting // and security checks for strings like \\?\GlobalRoot int startIndex = 2; while (startIndex < finalFullPath.Length) { if (finalFullPath[startIndex] == '\\') { startIndex++; break; } else { startIndex++; } } /* * From Path.cs in the CLR * * Throw an ArgumentException for paths like \\, \\server, \\server\ * This check can only be properly done after normalizing, so \\foo\.. will be properly rejected. Also, reject \\?\GLOBALROOT\ * (an internal kernel path) because it provides aliases for drives. * * throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegalUNC")); * * // Check for \\?\Globalroot, an internal mechanism to the kernel * // that provides aliases for drives and other undocumented stuff. * // The kernel team won't even describe the full set of what * // is available here - we don't want managed apps mucking * // with this for security reasons. */ if (startIndex == finalFullPath.Length || finalFullPath.IndexOf(@"\\?\globalroot", PathComparison) != -1) { finalFullPath = Path.GetFullPath(finalFullPath); } } return(finalFullPath); } } NativeMethodsShared.ThrowExceptionForErrorCode(errorCode); return(null); } #endif return(FixFilePath(Path.GetFullPath(path))); }