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. 4 5 using System.Reflection; 6 using System.Runtime.InteropServices; 7 8 namespace System.IO 9 { 10 public static class PathFeatures 11 { 12 private enum State 13 { 14 Uninitialized, 15 True, 16 False 17 } 18 19 // Note that this class is using APIs that allow it to run on all platforms (including Core 5.0) 20 // That is why we have .GetTypeInfo(), don't use the Registry, etc... 21 22 private static State s_osEnabled; 23 private static State s_onCore; 24 25 /// <summary> 26 /// Returns true if you can use long paths, including long DOS style paths (e.g. over 260 without \\?\). 27 /// </summary> AreAllLongPathsAvailable()28 public static bool AreAllLongPathsAvailable() 29 { 30 // We have support built-in for all platforms in Core 31 if (RunningOnCoreLib) 32 return true; 33 34 // Otherwise we're running on Windows, see if we've got the capability in .NET, and that the feature is enabled in the OS 35 return !AreLongPathsBlocked() && AreOsLongPathsEnabled(); 36 } 37 IsUsingLegacyPathNormalization()38 public static bool IsUsingLegacyPathNormalization() 39 { 40 return HasLegacyIoBehavior("UseLegacyPathHandling"); 41 } 42 43 /// <summary> 44 /// Returns true if > MAX_PATH (260) character paths are blocked. 45 /// Note that this doesn't reflect that you can actually use long paths without device syntax when on Windows. 46 /// Use AreAllLongPathsAvailable() to see that you can use long DOS style paths if on Windows. 47 /// </summary> AreLongPathsBlocked()48 public static bool AreLongPathsBlocked() 49 { 50 return HasLegacyIoBehavior("BlockLongPaths"); 51 } 52 HasLegacyIoBehavior(string propertyName)53 private static bool HasLegacyIoBehavior(string propertyName) 54 { 55 // Core doesn't have legacy behaviors 56 if (RunningOnCoreLib) 57 return false; 58 59 Type t = typeof(object).GetTypeInfo().Assembly.GetType("System.AppContextSwitches"); 60 var p = t.GetProperty(propertyName, BindingFlags.Static | BindingFlags.Public); 61 62 // If the switch actually exists use it, otherwise we predate the switch and are effectively on 63 return (bool)(p?.GetValue(null) ?? true); 64 } 65 66 private static bool RunningOnCoreLib 67 { 68 get 69 { 70 // Not particularly elegant 71 if (s_onCore == State.Uninitialized) 72 s_onCore = typeof(object).GetTypeInfo().Assembly.GetName().Name == "System.Private.CoreLib" ? State.True : State.False; 73 74 return s_onCore == State.True; 75 } 76 } 77 AreOsLongPathsEnabled()78 private static bool AreOsLongPathsEnabled() 79 { 80 if (s_osEnabled == State.Uninitialized) 81 { 82 // No official way to check yet this is good enough for tests 83 try 84 { 85 s_osEnabled = RtlAreLongPathsEnabled() ? State.True : State.False; 86 } 87 catch 88 { 89 s_osEnabled = State.False; 90 } 91 } 92 93 return s_osEnabled == State.True; 94 } 95 96 [DllImport("ntdll", ExactSpelling = true)] RtlAreLongPathsEnabled()97 private static extern bool RtlAreLongPathsEnabled(); 98 } 99 } 100