summaryrefslogtreecommitdiff
path: root/src/mscorlib/corefx/System/IO/PathHelper.Windows.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/corefx/System/IO/PathHelper.Windows.cs')
-rw-r--r--src/mscorlib/corefx/System/IO/PathHelper.Windows.cs245
1 files changed, 127 insertions, 118 deletions
diff --git a/src/mscorlib/corefx/System/IO/PathHelper.Windows.cs b/src/mscorlib/corefx/System/IO/PathHelper.Windows.cs
index 4c2cdff45e..e2ead93185 100644
--- a/src/mscorlib/corefx/System/IO/PathHelper.Windows.cs
+++ b/src/mscorlib/corefx/System/IO/PathHelper.Windows.cs
@@ -11,7 +11,7 @@ namespace System.IO
/// <summary>
/// Wrapper to help with path normalization.
/// </summary>
- unsafe internal class PathHelper
+ internal class PathHelper
{
// Can't be over 8.3 and be a short name
private const int MaxShortName = 12;
@@ -19,9 +19,6 @@ namespace System.IO
private const char LastAnsi = (char)255;
private const char Delete = (char)127;
- [ThreadStatic]
- private static StringBuffer t_fullPathBuffer;
-
/// <summary>
/// Normalize the given path.
/// </summary>
@@ -44,10 +41,11 @@ namespace System.IO
internal static string Normalize(string path, bool checkInvalidCharacters, bool expandShortPaths)
{
// Get the full path
- StringBuffer fullPath = t_fullPathBuffer ?? (t_fullPathBuffer = new StringBuffer(PathInternal.MaxShortPath));
+ StringBuffer fullPath = new StringBuffer(PathInternal.MaxShortPath);
+
try
{
- GetFullPathName(path, fullPath);
+ GetFullPathName(path, ref fullPath);
// Trim whitespace off the end of the string. Win32 normalization trims only U+0020.
fullPath.TrimEnd(PathInternal.s_trimEndChars);
@@ -82,17 +80,16 @@ namespace System.IO
// path that contains UNC or to see if the path was doing something like \\.\GLOBALROOT\Device\Mup\,
// \\.\GLOBAL\UNC\, \\.\GLOBALROOT\GLOBAL??\UNC\, etc.
bool specialPath = fullPath.Length > 1 && fullPath[0] == '\\' && fullPath[1] == '\\';
- bool isDevice = PathInternal.IsDevice(fullPath);
+ bool isDevice = PathInternal.IsDevice(ref fullPath);
bool possibleBadUnc = specialPath && !isDevice;
- uint index = specialPath ? 2u : 0;
- uint lastSeparator = specialPath ? 1u : 0;
- uint segmentLength;
- char* start = fullPath.CharPointer;
+ int index = specialPath ? 2 : 0;
+ int lastSeparator = specialPath ? 1 : 0;
+ int segmentLength;
char current;
while (index < fullPath.Length)
{
- current = start[index];
+ current = fullPath[index];
// Try to skip deeper analysis. '?' and higher are valid/ignorable except for '\', '|', and '~'
if (current < '?' || current == '\\' || current == '|' || current == '~')
@@ -111,7 +108,7 @@ namespace System.IO
break;
case '\\':
segmentLength = index - lastSeparator - 1;
- if (segmentLength > (uint)PathInternal.MaxComponentLength)
+ if (segmentLength > PathInternal.MaxComponentLength)
throw new PathTooLongException(SR.IO_PathTooLong + fullPath.ToString());
lastSeparator = index;
@@ -151,7 +148,7 @@ namespace System.IO
throw new ArgumentException(SR.Arg_PathIllegalUNC);
segmentLength = fullPath.Length - lastSeparator - 1;
- if (segmentLength > (uint)PathInternal.MaxComponentLength)
+ if (segmentLength > PathInternal.MaxComponentLength)
throw new PathTooLongException(SR.IO_PathTooLong);
if (foundTilde && segmentLength <= MaxShortName)
@@ -161,11 +158,11 @@ namespace System.IO
// this is how we've always done this. This expansion is costly so we'll continue to let other short paths slide.
if (expandShortPaths && possibleShortPath)
{
- return TryExpandShortFileName(fullPath, originalPath: path);
+ return TryExpandShortFileName(ref fullPath, originalPath: path);
}
else
{
- if (fullPath.Length == (uint)path.Length && fullPath.StartsWith(path))
+ if (fullPath.Length == path.Length && fullPath.StartsWith(path))
{
// If we have the exact same string we were passed in, don't bother to allocate another string from the StringBuilder.
return path;
@@ -184,12 +181,12 @@ namespace System.IO
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static bool IsDosUnc(StringBuffer buffer)
+ private static bool IsDosUnc(ref StringBuffer buffer)
{
- return !PathInternal.IsDevice(buffer) && buffer.Length > 1 && buffer[0] == '\\' && buffer[1] == '\\';
+ return !PathInternal.IsDevice(ref buffer) && buffer.Length > 1 && buffer[0] == '\\' && buffer[1] == '\\';
}
- private static void GetFullPathName(string path, StringBuffer fullPath)
+ private static unsafe void GetFullPathName(string path, ref StringBuffer fullPath)
{
// If the string starts with an extended prefix we would need to remove it from the path before we call GetFullPathName as
// it doesn't root extended paths correctly. We don't currently resolve extended paths, so we'll just assert here.
@@ -201,10 +198,10 @@ namespace System.IO
fixed (char* pathStart = path)
{
uint result = 0;
- while ((result = Interop.mincore.GetFullPathNameW(pathStart + startIndex, fullPath.CharCapacity, fullPath.GetHandle(), IntPtr.Zero)) > fullPath.CharCapacity)
+ while ((result = Interop.Kernel32.GetFullPathNameW(pathStart + startIndex, (uint)fullPath.Capacity, fullPath.UnderlyingArray, IntPtr.Zero)) > fullPath.Capacity)
{
- // Reported size (which does not include the null) is greater than the buffer size. Increase the capacity.
- fullPath.EnsureCharCapacity(result);
+ // Reported size is greater than the buffer size. Increase the capacity.
+ fullPath.EnsureCapacity(checked((int)result));
}
if (result == 0)
@@ -212,23 +209,23 @@ namespace System.IO
// Failure, get the error and throw
int errorCode = Marshal.GetLastWin32Error();
if (errorCode == 0)
- errorCode = Interop.mincore.Errors.ERROR_BAD_PATHNAME;
+ errorCode = Interop.Errors.ERROR_BAD_PATHNAME;
throw Win32Marshal.GetExceptionForWin32Error(errorCode, path);
}
- fullPath.Length = result;
+ fullPath.Length = checked((int)result);
}
}
- private static uint GetInputBuffer(StringBuffer content, bool isDosUnc, out StringBuffer buffer)
+ private static int GetInputBuffer(ref StringBuffer content, bool isDosUnc, ref StringBuffer buffer)
{
- uint length = content.Length;
+ int length = content.Length;
length += isDosUnc
- ? (uint)PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength
+ ? PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength
: PathInternal.DevicePrefixLength;
- buffer = new StringBuffer(length);
+ buffer.EnsureCapacity(length + 1);
if (isDosUnc)
{
@@ -238,28 +235,28 @@ namespace System.IO
// Copy the source buffer over after the existing UNC prefix
content.CopyTo(
bufferIndex: PathInternal.UncPrefixLength,
- destination: buffer,
+ destination: ref buffer,
destinationIndex: PathInternal.UncExtendedPrefixLength,
count: content.Length - PathInternal.UncPrefixLength);
// Return the prefix difference
- return (uint)PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength;
+ return PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength;
}
else
{
- uint prefixSize = (uint)PathInternal.ExtendedPathPrefix.Length;
+ int prefixSize = PathInternal.ExtendedPathPrefix.Length;
buffer.CopyFrom(bufferIndex: 0, source: PathInternal.ExtendedPathPrefix);
- content.CopyTo(bufferIndex: 0, destination: buffer, destinationIndex: prefixSize, count: content.Length);
+ content.CopyTo(bufferIndex: 0, destination: ref buffer, destinationIndex: prefixSize, count: content.Length);
return prefixSize;
}
}
- private static string TryExpandShortFileName(StringBuffer outputBuffer, string originalPath)
+ private static string TryExpandShortFileName(ref StringBuffer outputBuffer, string originalPath)
{
// We guarantee we'll expand short names for paths that only partially exist. As such, we need to find the part of the path that actually does exist. To
// avoid allocating like crazy we'll create only one input array and modify the contents with embedded nulls.
- Debug.Assert(!PathInternal.IsPartiallyQualified(outputBuffer), "should have resolved by now");
+ Debug.Assert(!PathInternal.IsPartiallyQualified(ref outputBuffer), "should have resolved by now");
// We'll have one of a few cases by now (the normalized path will have already:
//
@@ -271,119 +268,131 @@ namespace System.IO
//
// Note that we will never get \??\ here as GetFullPathName() does not recognize \??\ and will return it as C:\??\ (or whatever the current drive is).
- uint rootLength = PathInternal.GetRootLength(outputBuffer);
- bool isDevice = PathInternal.IsDevice(outputBuffer);
+ int rootLength = PathInternal.GetRootLength(ref outputBuffer);
+ bool isDevice = PathInternal.IsDevice(ref outputBuffer);
- StringBuffer inputBuffer = null;
- bool isDosUnc = false;
- uint rootDifference = 0;
- bool wasDotDevice = false;
-
- // Add the extended prefix before expanding to allow growth over MAX_PATH
- if (isDevice)
+ StringBuffer inputBuffer = new StringBuffer(0);
+ try
{
- // We have one of the following (\\?\ or \\.\)
- inputBuffer = new StringBuffer();
- inputBuffer.Append(outputBuffer);
+ bool isDosUnc = false;
+ int rootDifference = 0;
+ bool wasDotDevice = false;
- if (outputBuffer[2] == '.')
+ // Add the extended prefix before expanding to allow growth over MAX_PATH
+ if (isDevice)
{
- wasDotDevice = true;
- inputBuffer[2] = '?';
+ // We have one of the following (\\?\ or \\.\)
+ inputBuffer.Append(ref outputBuffer);
+
+ if (outputBuffer[2] == '.')
+ {
+ wasDotDevice = true;
+ inputBuffer[2] = '?';
+ }
+ }
+ else
+ {
+ isDosUnc = IsDosUnc(ref outputBuffer);
+ rootDifference = GetInputBuffer(ref outputBuffer, isDosUnc, ref inputBuffer);
}
- }
- else
- {
- isDosUnc = IsDosUnc(outputBuffer);
- rootDifference = GetInputBuffer(outputBuffer, isDosUnc, out inputBuffer);
- }
- rootLength += rootDifference;
- uint inputLength = inputBuffer.Length;
+ rootLength += rootDifference;
+ int inputLength = inputBuffer.Length;
- bool success = false;
- uint foundIndex = inputBuffer.Length - 1;
+ bool success = false;
+ int foundIndex = inputBuffer.Length - 1;
- while (!success)
- {
- uint result = Interop.mincore.GetLongPathNameW(inputBuffer.GetHandle(), outputBuffer.GetHandle(), outputBuffer.CharCapacity);
+ while (!success)
+ {
+ uint result = Interop.Kernel32.GetLongPathNameW(inputBuffer.UnderlyingArray, outputBuffer.UnderlyingArray, (uint)outputBuffer.Capacity);
- // Replace any temporary null we added
- if (inputBuffer[foundIndex] == '\0') inputBuffer[foundIndex] = '\\';
+ // Replace any temporary null we added
+ if (inputBuffer[foundIndex] == '\0') inputBuffer[foundIndex] = '\\';
- if (result == 0)
- {
- // Look to see if we couldn't find the file
- int error = Marshal.GetLastWin32Error();
- if (error != Interop.mincore.Errors.ERROR_FILE_NOT_FOUND && error != Interop.mincore.Errors.ERROR_PATH_NOT_FOUND)
+ if (result == 0)
{
- // Some other failure, give up
- break;
- }
+ // Look to see if we couldn't find the file
+ int error = Marshal.GetLastWin32Error();
+ if (error != Interop.Errors.ERROR_FILE_NOT_FOUND && error != Interop.Errors.ERROR_PATH_NOT_FOUND)
+ {
+ // Some other failure, give up
+ break;
+ }
- // We couldn't find the path at the given index, start looking further back in the string.
- foundIndex--;
+ // We couldn't find the path at the given index, start looking further back in the string.
+ foundIndex--;
- for (; foundIndex > rootLength && inputBuffer[foundIndex] != '\\'; foundIndex--) ;
- if (foundIndex == rootLength)
+ for (; foundIndex > rootLength && inputBuffer[foundIndex] != '\\'; foundIndex--) ;
+ if (foundIndex == rootLength)
+ {
+ // Can't trim the path back any further
+ break;
+ }
+ else
+ {
+ // Temporarily set a null in the string to get Windows to look further up the path
+ inputBuffer[foundIndex] = '\0';
+ }
+ }
+ else if (result > outputBuffer.Capacity)
{
- // Can't trim the path back any further
- break;
+ // Not enough space. The result count for this API does not include the null terminator.
+ outputBuffer.EnsureCapacity(checked((int)result));
+ result = Interop.Kernel32.GetLongPathNameW(inputBuffer.UnderlyingArray, outputBuffer.UnderlyingArray, (uint)outputBuffer.Capacity);
}
else
{
- // Temporarily set a null in the string to get Windows to look further up the path
- inputBuffer[foundIndex] = '\0';
+ // Found the path
+ success = true;
+ outputBuffer.Length = checked((int)result);
+ if (foundIndex < inputLength - 1)
+ {
+ // It was a partial find, put the non-existent part of the path back
+ outputBuffer.Append(ref inputBuffer, foundIndex, inputBuffer.Length - foundIndex);
+ }
}
}
- else if (result > outputBuffer.CharCapacity)
+
+ // Strip out the prefix and return the string
+ ref StringBuffer bufferToUse = ref Choose(success, ref outputBuffer, ref inputBuffer);
+
+ // Switch back from \\?\ to \\.\ if necessary
+ if (wasDotDevice)
+ bufferToUse[2] = '.';
+
+ string returnValue = null;
+
+ int newLength = (int)(bufferToUse.Length - rootDifference);
+ if (isDosUnc)
{
- // Not enough space. The result count for this API does not include the null terminator.
- outputBuffer.EnsureCharCapacity(result);
- result = Interop.mincore.GetLongPathNameW(inputBuffer.GetHandle(), outputBuffer.GetHandle(), outputBuffer.CharCapacity);
+ // Need to go from \\?\UNC\ to \\?\UN\\
+ bufferToUse[PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength] = '\\';
+ }
+
+ // We now need to strip out any added characters at the front of the string
+ if (bufferToUse.SubstringEquals(originalPath, rootDifference, newLength))
+ {
+ // Use the original path to avoid allocating
+ returnValue = originalPath;
}
else
{
- // Found the path
- success = true;
- outputBuffer.Length = result;
- if (foundIndex < inputLength - 1)
- {
- // It was a partial find, put the non-existent part of the path back
- outputBuffer.Append(inputBuffer, foundIndex, inputBuffer.Length - foundIndex);
- }
+ returnValue = bufferToUse.Substring(rootDifference, newLength);
}
- }
- // Strip out the prefix and return the string
- StringBuffer bufferToUse = success ? outputBuffer : inputBuffer;
-
- // Switch back from \\?\ to \\.\ if necessary
- if (wasDotDevice)
- bufferToUse[2] = '.';
-
- string returnValue = null;
-
- int newLength = (int)(bufferToUse.Length - rootDifference);
- if (isDosUnc)
- {
- // Need to go from \\?\UNC\ to \\?\UN\\
- bufferToUse[PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength] = '\\';
- }
-
- // We now need to strip out any added characters at the front of the string
- if (bufferToUse.SubstringEquals(originalPath, rootDifference, newLength))
- {
- // Use the original path to avoid allocating
- returnValue = originalPath;
+ return returnValue;
}
- else
+ finally
{
- returnValue = bufferToUse.Substring(rootDifference, newLength);
+ inputBuffer.Free();
}
+ }
- inputBuffer.Dispose();
- return returnValue;
+ // Helper method to workaround lack of operator ? support for ref values
+ private static ref StringBuffer Choose(bool condition, ref StringBuffer s1, ref StringBuffer s2)
+ {
+ if (condition) return ref s1;
+ else return ref s2;
}
}
}