static private Normalize ( string path, bool checkInvalidCharacters, bool expandShortPaths ) : string | ||
path | string | Path to normalize |
checkInvalidCharacters | bool | True to check for invalid characters |
expandShortPaths | bool | Attempt to expand short paths if true |
return | string |
// Returns a unique temporary file name, and creates a 0-byte file by that // name on disk. public static string GetTempFileName() { var tempPathBuilder = new ValueStringBuilder(stackalloc char[PathInternal.MaxShortPath]); GetTempPath(ref tempPathBuilder); var builder = new ValueStringBuilder(stackalloc char[PathInternal.MaxShortPath]); uint result = Interop.Kernel32.GetTempFileNameW( ref tempPathBuilder.GetPinnableReference(), "tmp", 0, ref builder.GetPinnableReference()); tempPathBuilder.Dispose(); if (result == 0) { throw Win32Marshal.GetExceptionForLastWin32Error(); } builder.Length = builder.RawChars.IndexOf('\0'); string path = PathHelper.Normalize(ref builder); builder.Dispose(); return(path); }
// Expands the given path to a fully qualified path. public static string GetFullPath(string path) { if (path == null) { throw new ArgumentNullException(nameof(path)); } // If the path would normalize to string empty, we'll consider it empty if (PathInternal.IsEffectivelyEmpty(path)) { throw new ArgumentException(SR.Arg_PathEmpty, nameof(path)); } // Embedded null characters are the only invalid character case we trully care about. // This is because the nulls will signal the end of the string to Win32 and therefore have // unpredictable results. if (path.IndexOf('\0') != -1) { throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path)); } if (PathInternal.IsExtended(path)) { // \\?\ paths are considered normalized by definition. Windows doesn't normalize \\?\ // paths and neither should we. Even if we wanted to GetFullPathName does not work // properly with device paths. If one wants to pass a \\?\ path through normalization // one can chop off the prefix, pass it to GetFullPath and add it again. return(path); } return(PathHelper.Normalize(path)); }
// Returns a unique temporary file name, and creates a 0-byte file by that // name on disk. public static string GetTempFileName() { Span <char> initialTempPathBuffer = stackalloc char[PathInternal.MaxShortPath]; ValueStringBuilder tempPathBuilder = new ValueStringBuilder(initialTempPathBuffer); GetTempPath(ref tempPathBuilder); Span <char> initialBuffer = stackalloc char[PathInternal.MaxShortPath]; var builder = new ValueStringBuilder(initialBuffer); uint result = 0; while ((result = Interop.Kernel32.GetTempFileNameW( ref tempPathBuilder.GetPinnableReference(), "tmp", 0, ref builder.GetPinnableReference())) > builder.Capacity) { // Reported size is greater than the buffer size. Increase the capacity. builder.EnsureCapacity(checked ((int)result)); } tempPathBuilder.Dispose(); if (result == 0) { throw Win32Marshal.GetExceptionForLastWin32Error(); } builder.Length = (int)result; string path = PathHelper.Normalize(ref builder); builder.Dispose(); return(path); }
private static unsafe string CreateTempSubdirectoryCore(string?prefix) { ValueStringBuilder builder = new ValueStringBuilder(stackalloc char[PathInternal.MaxShortPath]); Path.GetTempPath(ref builder); // ensure the base TEMP directory exists CreateDirectory(PathHelper.Normalize(ref builder)); builder.Append(prefix); const int RandomFileNameLength = 12; // 12 == 8 + 1 (for period) + 3 int initialTempPathLength = builder.Length; builder.EnsureCapacity(initialTempPathLength + RandomFileNameLength); // For generating random file names // 8 random bytes provides 12 chars in our encoding for the 8.3 name. const int RandomKeyLength = 8; byte * pKey = stackalloc byte[RandomKeyLength]; // to avoid an infinite loop, only try as many as GetTempFileNameW will create const int MaxAttempts = ushort.MaxValue; int attempts = 0; while (attempts < MaxAttempts) { // simulate a call to Path.GetRandomFileName() without allocating an intermediate string Interop.GetRandomBytes(pKey, RandomKeyLength); Path.Populate83FileNameFromRandomBytes(pKey, RandomKeyLength, builder.RawChars.Slice(builder.Length, RandomFileNameLength)); builder.Length += RandomFileNameLength; string path = PathHelper.Normalize(ref builder); bool directoryCreated = Interop.Kernel32.CreateDirectory(path, null); if (!directoryCreated) { // in the off-chance that the directory already exists, try again int error = Marshal.GetLastPInvokeError(); if (error == Interop.Errors.ERROR_ALREADY_EXISTS) { builder.Length = initialTempPathLength; attempts++; continue; } throw Win32Marshal.GetExceptionForWin32Error(error, path); } builder.Dispose(); return(path); } throw new IOException(SR.IO_MaxAttemptsReached); }
public static string GetTempPath() { var builder = new ValueStringBuilder(stackalloc char[PathInternal.MaxShortPath]); GetTempPath(ref builder); string path = PathHelper.Normalize(ref builder); builder.Dispose(); return(path); }
private static string NormalizePath(string path, bool fullCheck = true, bool expandShortPaths = true) { Debug.Assert(path != null, "path can't be null"); bool isExtended = PathInternal.IsExtended(path); if (fullCheck) { // Embedded null characters are the only invalid character case we want to check up front. // This is because the nulls will signal the end of the string to Win32 and therefore have // unpredictable results. Other invalid characters we give a chance to be normalized out. if (path.IndexOf('\0') != -1) { throw new ArgumentException(SR.Argument_InvalidPathChars, "path"); } // Toss out paths with colons that aren't a valid drive specifier. // Cannot start with a colon and can only be of the form "C:" or "\\?\C:". // (Note that we used to explicitly check "http:" and "file:"- these are caught by this check now.) int startIndex = PathInternal.PathStartSkip(path) + 2; if (isExtended) { startIndex += PathInternal.ExtendedPathPrefix.Length; } if ((path.Length > 0 && path[0] == VolumeSeparatorChar) || (path.Length >= startIndex && path[startIndex - 1] == VolumeSeparatorChar && !PathInternal.IsValidDriveChar(path[startIndex - 2])) || (path.Length > startIndex && path.IndexOf(VolumeSeparatorChar, startIndex) != -1)) { throw new NotSupportedException(SR.Argument_PathFormatNotSupported); } } if (isExtended) { return(NormalizeExtendedPath(path, fullCheck)); } else { // Technically this doesn't matter but we used to throw for this case if (String.IsNullOrWhiteSpace(path)) { throw new ArgumentException(SR.Arg_PathIllegal); } return(PathHelper.Normalize(path, fullCheck, expandShortPaths)); } }
// Gets the full path without argument validation private static string GetFullPathInternal(string path) { Debug.Assert(!string.IsNullOrEmpty(path)); Debug.Assert(!path.Contains('\0')); if (PathInternal.IsExtended(path.AsSpan())) { // \\?\ paths are considered normalized by definition. Windows doesn't normalize \\?\ // paths and neither should we. Even if we wanted to GetFullPathName does not work // properly with device paths. If one wants to pass a \\?\ path through normalization // one can chop off the prefix, pass it to GetFullPath and add it again. return(path); } return(PathHelper.Normalize(path)); }
private static string NormalizeStandardPath(string path, bool fullCheck, bool expandShortPaths) { // Technically this doesn't matter, but we used to throw for this case if (String.IsNullOrWhiteSpace(path)) { throw new ArgumentException(SR.Arg_PathIllegal); } PathHelper helper = new PathHelper(); string returnVal = helper.Normalize(path, fullCheck, expandShortPaths); if (String.Equals(returnVal, path, StringComparison.Ordinal)) { returnVal = path; } return(returnVal); }
// Expands the given path to a fully qualified path. public static string GetFullPath(string path) { if (path == null) { throw new ArgumentNullException(nameof(path)); } // Embedded null characters are the only invalid character case we want to check up front. // This is because the nulls will signal the end of the string to Win32 and therefore have // unpredictable results. Other invalid characters we give a chance to be normalized out. if (path.IndexOf('\0') != -1) { throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path)); } if (PathInternal.IsExtended(path)) { // We can't really know what is valid for all cases of extended paths. // // - object names can include other characters as well (':', '/', etc.) // - even file objects have different rules (pipe names can contain most characters) // // As such we will do no further analysis of extended paths to avoid blocking known and unknown // scenarios as well as minimizing compat breaks should we block now and need to unblock later. return(path); } bool isDevice = PathInternal.IsDevice(path); if (!isDevice) { // Toss out paths with colons that aren't a valid drive specifier. // Cannot start with a colon and can only be of the form "C:". // (Note that we used to explicitly check "http:" and "file:"- these are caught by this check now.) int startIndex = PathInternal.PathStartSkip(path); // Move past the colon startIndex += 2; if ((path.Length > 0 && path[0] == PathInternal.VolumeSeparatorChar) || (path.Length >= startIndex && path[startIndex - 1] == PathInternal.VolumeSeparatorChar && !PathInternal.IsValidDriveChar(path[startIndex - 2])) || (path.Length > startIndex && path.IndexOf(PathInternal.VolumeSeparatorChar, startIndex) != -1)) { throw new NotSupportedException(SR.Format(SR.Argument_PathFormatNotSupported_Path, path)); } } // Technically this doesn't matter but we used to throw for this case if (PathInternal.IsEffectivelyEmpty(path)) { throw new ArgumentException(SR.Arg_PathEmpty, nameof(path)); } // We don't want to check invalid characters for device format- see comments for extended above string fullPath = PathHelper.Normalize(path, checkInvalidCharacters: !isDevice, expandShortPaths: true); if (!isDevice) { // Emulate FileIOPermissions checks, retained for compatibility (normal invalid characters have already been checked) if (PathInternal.HasWildCardCharacters(fullPath)) { throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path)); } } return(fullPath); }
private static string NormalizeStandardPath(string path, bool fullCheck, bool expandShortPaths) { // Technically this doesn't matter, but we used to throw for this case if (String.IsNullOrWhiteSpace(path)) throw new ArgumentException(SR.Arg_PathIllegal); PathHelper helper = new PathHelper(); string returnVal = helper.Normalize(path, fullCheck, expandShortPaths); if (String.Equals(returnVal, path, StringComparison.Ordinal)) { returnVal = path; } return returnVal; }
/// <summary> /// Normalize the path and check for bad characters or other invalid syntax. /// </summary> /// <remarks> /// The legacy NormalizePath /// </remarks> private static string NormalizeAndValidatePath(string path) { Debug.Assert(path != null, "path can't be null"); // Embedded null characters are the only invalid character case we want to check up front. // This is because the nulls will signal the end of the string to Win32 and therefore have // unpredictable results. Other invalid characters we give a chance to be normalized out. if (path.IndexOf('\0') != -1) { throw new ArgumentException(SR.Argument_InvalidPathChars, "path"); } // Toss out paths with colons that aren't a valid drive specifier. // Cannot start with a colon and can only be of the form "C:" or "\\?\C:". // (Note that we used to explicitly check "http:" and "file:"- these are caught by this check now.) int startIndex = PathInternal.PathStartSkip(path); bool isExtended = path.Length >= PathInternal.ExtendedPathPrefix.Length + startIndex && path.IndexOf(PathInternal.ExtendedPathPrefix, startIndex, PathInternal.ExtendedPathPrefix.Length, StringComparison.Ordinal) >= 0; if (isExtended) { startIndex += PathInternal.ExtendedPathPrefix.Length; } // Move past the colon startIndex += 2; if ((path.Length > 0 && path[0] == VolumeSeparatorChar) || (path.Length >= startIndex && path[startIndex - 1] == VolumeSeparatorChar && !PathInternal.IsValidDriveChar(path[startIndex - 2])) || (path.Length > startIndex && path.IndexOf(VolumeSeparatorChar, startIndex) != -1)) { throw new NotSupportedException(SR.Argument_PathFormatNotSupported); } if (isExtended) { // If the path is in extended syntax, we don't need to normalize, but we still do some basic validity checks if (!ValidateExtendedPath(path)) { throw new ArgumentException(SR.Arg_PathIllegal); } // \\?\GLOBALROOT gives access to devices out of the scope of the current user, we // don't want to allow this for security reasons. // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx#nt_namespaces if (path.StartsWith(@"\\?\globalroot", StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException(SR.Arg_PathGlobalRoot); } // Look for illegal path characters. PathInternal.CheckInvalidPathChars(path); return(path); } else { // Technically this doesn't matter but we used to throw for this case if (String.IsNullOrWhiteSpace(path)) { throw new ArgumentException(SR.Arg_PathIllegal); } return(PathHelper.Normalize(path, checkInvalidCharacters: true, expandShortPaths: true)); } }