Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'netcore/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs')
-rw-r--r--netcore/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs251
1 files changed, 0 insertions, 251 deletions
diff --git a/netcore/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs b/netcore/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs
deleted file mode 100644
index abf5c346148..00000000000
--- a/netcore/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs
+++ /dev/null
@@ -1,251 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#nullable enable
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Text;
-
-namespace System.IO
-{
- /// <summary>
- /// Wrapper to help with path normalization.
- /// </summary>
- internal static class PathHelper
- {
- /// <summary>
- /// Normalize the given path.
- /// </summary>
- /// <remarks>
- /// Normalizes via Win32 GetFullPathName().
- /// </remarks>
- /// <param name="path">Path to normalize</param>
- /// <exception cref="PathTooLongException">Thrown if we have a string that is too large to fit into a UNICODE_STRING.</exception>
- /// <exception cref="IOException">Thrown if the path is empty.</exception>
- /// <returns>Normalized path</returns>
- internal static string Normalize(string path)
- {
- var builder = new ValueStringBuilder(stackalloc char[PathInternal.MaxShortPath]);
-
- // Get the full path
- GetFullPathName(path.AsSpan(), ref builder);
-
- // If we have the exact same string we were passed in, don't allocate another string.
- // TryExpandShortName does this input identity check.
- string result = builder.AsSpan().IndexOf('~') >= 0
- ? TryExpandShortFileName(ref builder, originalPath: path)
- : builder.AsSpan().Equals(path.AsSpan(), StringComparison.Ordinal) ? path : builder.ToString();
-
- // Clear the buffer
- builder.Dispose();
- return result;
- }
-
- /// <summary>
- /// Normalize the given path.
- /// </summary>
- /// <remarks>
- /// Exceptions are the same as the string overload.
- /// </remarks>
- internal static string Normalize(ref ValueStringBuilder path)
- {
- var builder = new ValueStringBuilder(stackalloc char[PathInternal.MaxShortPath]);
-
- // Get the full path
- GetFullPathName(path.AsSpan(terminate: true), ref builder);
-
- string result = builder.AsSpan().IndexOf('~') >= 0
- ? TryExpandShortFileName(ref builder, originalPath: null)
- : builder.ToString();
-
- // Clear the buffer
- builder.Dispose();
- return result;
- }
-
- /// <summary>
- /// Calls GetFullPathName on the given path.
- /// </summary>
- /// <param name="path">The path name. MUST be null terminated after the span.</param>
- /// <param name="builder">Builder that will store the result.</param>
- private static void GetFullPathName(ReadOnlySpan<char> path, ref ValueStringBuilder builder)
- {
- // 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.
- Debug.Assert(PathInternal.IsPartiallyQualified(path) || !PathInternal.IsExtended(path));
-
- uint result;
- while ((result = Interop.Kernel32.GetFullPathNameW(ref MemoryMarshal.GetReference(path), (uint)builder.Capacity, ref builder.GetPinnableReference(), IntPtr.Zero)) > builder.Capacity)
- {
- // Reported size is greater than the buffer size. Increase the capacity.
- builder.EnsureCapacity(checked((int)result));
- }
-
- if (result == 0)
- {
- // Failure, get the error and throw
- int errorCode = Marshal.GetLastWin32Error();
- if (errorCode == 0)
- errorCode = Interop.Errors.ERROR_BAD_PATHNAME;
- throw Win32Marshal.GetExceptionForWin32Error(errorCode, path.ToString());
- }
-
- builder.Length = (int)result;
- }
-
- internal static int PrependDevicePathChars(ref ValueStringBuilder content, bool isDosUnc, ref ValueStringBuilder buffer)
- {
- int length = content.Length;
-
- length += isDosUnc
- ? PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength
- : PathInternal.DevicePrefixLength;
-
- buffer.EnsureCapacity(length + 1);
- buffer.Length = 0;
-
- if (isDosUnc)
- {
- // Is a \\Server\Share, put \\?\UNC\ in the front
- buffer.Append(PathInternal.UncExtendedPathPrefix);
-
- // Copy Server\Share\... over to the buffer
- buffer.Append(content.AsSpan(PathInternal.UncPrefixLength));
-
- // Return the prefix difference
- return PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength;
- }
- else
- {
- // Not an UNC, put the \\?\ prefix in front, then the original string
- buffer.Append(PathInternal.ExtendedPathPrefix);
- buffer.Append(content.AsSpan());
- return PathInternal.DevicePrefixLength;
- }
- }
-
- internal static string TryExpandShortFileName(ref ValueStringBuilder outputBuilder, 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 a lot we'll create only one input array and modify the contents with embedded nulls.
-
- Debug.Assert(!PathInternal.IsPartiallyQualified(outputBuilder.AsSpan()), "should have resolved by now");
-
- // We'll have one of a few cases by now (the normalized path will have already:
- //
- // 1. Dos path (C:\)
- // 2. Dos UNC (\\Server\Share)
- // 3. Dos device path (\\.\C:\, \\?\C:\)
- //
- // We want to put the extended syntax on the front if it doesn't already have it (for long path support and speed), which may mean switching from \\.\.
- //
- // Note that we will never get \??\ here as GetFullPathName() does not recognize \??\ and will return it as C:\??\ (or whatever the current drive is).
-
- int rootLength = PathInternal.GetRootLength(outputBuilder.AsSpan());
- bool isDevice = PathInternal.IsDevice(outputBuilder.AsSpan());
-
- // As this is a corner case we're not going to add a stackalloc here to keep the stack pressure down.
- ValueStringBuilder inputBuilder = default;
-
- bool isDosUnc = false;
- int rootDifference = 0;
- bool wasDotDevice = false;
-
- // Add the extended prefix before expanding to allow growth over MAX_PATH
- if (isDevice)
- {
- // We have one of the following (\\?\ or \\.\)
- inputBuilder.Append(outputBuilder.AsSpan());
-
- if (outputBuilder[2] == '.')
- {
- wasDotDevice = true;
- inputBuilder[2] = '?';
- }
- }
- else
- {
- isDosUnc = !PathInternal.IsDevice(outputBuilder.AsSpan()) && outputBuilder.Length > 1 && outputBuilder[0] == '\\' && outputBuilder[1] == '\\';
- rootDifference = PrependDevicePathChars(ref outputBuilder, isDosUnc, ref inputBuilder);
- }
-
- rootLength += rootDifference;
- int inputLength = inputBuilder.Length;
-
- bool success = false;
- int foundIndex = inputBuilder.Length - 1;
-
- while (!success)
- {
- uint result = Interop.Kernel32.GetLongPathNameW(
- ref inputBuilder.GetPinnableReference(terminate: true), ref outputBuilder.GetPinnableReference(), (uint)outputBuilder.Capacity);
-
- // Replace any temporary null we added
- if (inputBuilder[foundIndex] == '\0') inputBuilder[foundIndex] = '\\';
-
- if (result == 0)
- {
- // 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--;
-
- for (; foundIndex > rootLength && inputBuilder[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
- inputBuilder[foundIndex] = '\0';
- }
- }
- else if (result > outputBuilder.Capacity)
- {
- // Not enough space. The result count for this API does not include the null terminator.
- outputBuilder.EnsureCapacity(checked((int)result));
- }
- else
- {
- // Found the path
- success = true;
- outputBuilder.Length = checked((int)result);
- if (foundIndex < inputLength - 1)
- {
- // It was a partial find, put the non-existent part of the path back
- outputBuilder.Append(inputBuilder.AsSpan(foundIndex, inputBuilder.Length - foundIndex));
- }
- }
- }
-
- // If we were able to expand the path, use it, otherwise use the original full path result
- ref ValueStringBuilder builderToUse = ref (success ? ref outputBuilder : ref inputBuilder);
-
- // Switch back from \\?\ to \\.\ if necessary
- if (wasDotDevice)
- builderToUse[2] = '.';
-
- // Change from \\?\UNC\ to \\?\UN\\ if needed
- if (isDosUnc)
- builderToUse[PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength] = '\\';
-
- // Strip out any added characters at the front of the string
- ReadOnlySpan<char> output = builderToUse.AsSpan(rootDifference);
-
- string returnValue = ((originalPath != null) && output.Equals(originalPath.AsSpan(), StringComparison.Ordinal))
- ? originalPath : output.ToString();
-
- inputBuilder.Dispose();
- return returnValue;
- }
- }
-}