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.Diagnostics; 6 using System.Text; 7 8 namespace System.IO 9 { 10 public static partial class Path 11 { GetInvalidFileNameChars()12 public static char[] GetInvalidFileNameChars() => new char[] 13 { 14 '\"', '<', '>', '|', '\0', 15 (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, 16 (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, 17 (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, 18 (char)31, ':', '*', '?', '\\', '/' 19 }; 20 GetInvalidPathChars()21 public static char[] GetInvalidPathChars() => new char[] 22 { 23 '|', '\0', 24 (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, 25 (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, 26 (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, 27 (char)31 28 }; 29 30 // The max total path is 260, and the max individual component length is 255. 31 // For example, D:\<256 char file name> isn't legal, even though it's under 260 chars. 32 internal const int MaxPath = 260; 33 34 // Expands the given path to a fully qualified path. GetFullPath(string path)35 public static string GetFullPath(string path) 36 { 37 if (path == null) 38 throw new ArgumentNullException(nameof(path)); 39 40 // Embedded null characters are the only invalid character case we want to check up front. 41 // This is because the nulls will signal the end of the string to Win32 and therefore have 42 // unpredictable results. Other invalid characters we give a chance to be normalized out. 43 if (path.IndexOf('\0') != -1) 44 throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path)); 45 46 if (PathInternal.IsExtended(path)) 47 { 48 // We can't really know what is valid for all cases of extended paths. 49 // 50 // - object names can include other characters as well (':', '/', etc.) 51 // - even file objects have different rules (pipe names can contain most characters) 52 // 53 // As such we will do no further analysis of extended paths to avoid blocking known and unknown 54 // scenarios as well as minimizing compat breaks should we block now and need to unblock later. 55 return path; 56 } 57 58 bool isDevice = PathInternal.IsDevice(path); 59 if (!isDevice) 60 { 61 // Toss out paths with colons that aren't a valid drive specifier. 62 // Cannot start with a colon and can only be of the form "C:". 63 // (Note that we used to explicitly check "http:" and "file:"- these are caught by this check now.) 64 int startIndex = PathInternal.PathStartSkip(path); 65 66 // Move past the colon 67 startIndex += 2; 68 69 if ((path.Length > 0 && path[0] == PathInternal.VolumeSeparatorChar) 70 || (path.Length >= startIndex && path[startIndex - 1] == PathInternal.VolumeSeparatorChar && !PathInternal.IsValidDriveChar(path[startIndex - 2])) 71 || (path.Length > startIndex && path.IndexOf(PathInternal.VolumeSeparatorChar, startIndex) != -1)) 72 { 73 throw new NotSupportedException(SR.Format(SR.Argument_PathFormatNotSupported_Path, path)); 74 } 75 } 76 77 // Technically this doesn't matter but we used to throw for this case 78 if (PathInternal.IsEffectivelyEmpty(path)) 79 throw new ArgumentException(SR.Arg_PathEmpty, nameof(path)); 80 81 // We don't want to check invalid characters for device format- see comments for extended above 82 string fullPath = PathHelper.Normalize(path, checkInvalidCharacters: !isDevice, expandShortPaths: true); 83 84 if (!isDevice) 85 { 86 // Emulate FileIOPermissions checks, retained for compatibility (normal invalid characters have already been checked) 87 if (PathInternal.HasWildCardCharacters(fullPath)) 88 throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path)); 89 } 90 91 return fullPath; 92 } 93 GetTempPath()94 public static string GetTempPath() 95 { 96 StringBuilder sb = StringBuilderCache.Acquire(MaxPath); 97 uint r = Interop.Kernel32.GetTempPathW(MaxPath, sb); 98 if (r == 0) 99 throw Win32Marshal.GetExceptionForLastWin32Error(); 100 return GetFullPath(StringBuilderCache.GetStringAndRelease(sb)); 101 } 102 103 // Returns a unique temporary file name, and creates a 0-byte file by that 104 // name on disk. GetTempFileName()105 public static string GetTempFileName() 106 { 107 string path = GetTempPath(); 108 109 StringBuilder sb = StringBuilderCache.Acquire(MaxPath); 110 uint r = Interop.Kernel32.GetTempFileNameW(path, "tmp", 0, sb); 111 if (r == 0) 112 throw Win32Marshal.GetExceptionForLastWin32Error(); 113 return StringBuilderCache.GetStringAndRelease(sb); 114 } 115 116 // Tests if the given path contains a root. A path is considered rooted 117 // if it starts with a backslash ("\") or a valid drive letter and a colon (":"). IsPathRooted(string path)118 public static bool IsPathRooted(string path) 119 { 120 if (path != null) 121 { 122 int length = path.Length; 123 if ((length >= 1 && PathInternal.IsDirectorySeparator(path[0])) || 124 (length >= 2 && PathInternal.IsValidDriveChar(path[0]) && path[1] == PathInternal.VolumeSeparatorChar)) 125 return true; 126 } 127 return false; 128 } 129 130 // Returns the root portion of the given path. The resulting string 131 // consists of those rightmost characters of the path that constitute the 132 // root of the path. Possible patterns for the resulting string are: An 133 // empty string (a relative path on the current drive), "\" (an absolute 134 // path on the current drive), "X:" (a relative path on a given drive, 135 // where X is the drive letter), "X:\" (an absolute path on a given drive), 136 // and "\\server\share" (a UNC path for a given server and share name). 137 // The resulting string is null if path is null. If the path is empty or 138 // only contains whitespace characters an ArgumentException gets thrown. GetPathRoot(string path)139 public static string GetPathRoot(string path) 140 { 141 if (path == null) return null; 142 if (PathInternal.IsEffectivelyEmpty(path)) 143 throw new ArgumentException(SR.Arg_PathEmpty, nameof(path)); 144 145 // Need to return the normalized directory separator 146 path = PathInternal.NormalizeDirectorySeparators(path); 147 148 int pathRoot = PathInternal.GetRootLength(path); 149 return pathRoot <= 0 ? string.Empty : path.Substring(0, pathRoot); 150 } 151 152 /// <summary>Gets whether the system is case-sensitive.</summary> 153 internal static bool IsCaseSensitive { get { return false; } } 154 } 155 } 156