internal static void CreateSymbolicLinkCore(KernelTransaction transaction, string symlinkFileName, string targetFileName, SymbolicLinkTarget targetType, PathFormat pathFormat) { if (!NativeMethods.IsAtLeastWindowsVista) { throw new PlatformNotSupportedException(Resources.Requires_Windows_Vista_Or_Higher); } const GetFullPathOptions options = GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.FullCheck; string symlinkFileNameLp = Path.GetExtendedLengthPathCore(transaction, symlinkFileName, pathFormat, options); string targetFileNameRp = Path.GetExtendedLengthPathCore(transaction, targetFileName, pathFormat, options); // Don't use long path notation, as it will be empty upon creation. targetFileNameRp = Path.GetRegularPathCore(targetFileNameRp, GetFullPathOptions.None, false); if (!(transaction == null // CreateSymbolicLink() / CreateSymbolicLinkTransacted() // In the ANSI version of this function, the name is limited to MAX_PATH characters. // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path. // 2014-02-14: MSDN does not confirm LongPath usage but a Unicode version of this function exists. // 2015-07-17: This function does not support long paths. ? NativeMethods.CreateSymbolicLink(symlinkFileNameLp, targetFileNameRp, targetType) : NativeMethods.CreateSymbolicLinkTransacted(symlinkFileNameLp, targetFileNameRp, targetType, transaction.SafeHandle))) { var lastError = Marshal.GetLastWin32Error(); if (lastError != 0) { NativeError.ThrowException(lastError, symlinkFileNameLp, targetFileNameRp); } } }
/// <summary>Applies the <seealso cref="GetFullPathOptions"/> to <paramref name="path"/>.</summary> /// <returns><paramref name="path"/> with applied <paramref name="options"/>.</returns> /// <exception cref="ArgumentException"/> /// <exception cref="ArgumentNullException"/> /// <param name="path"></param> /// <param name="options"></param> private static string ApplyFullPathOptions(string path, GetFullPathOptions options) { if ((options & GetFullPathOptions.TrimEnd) != 0) { if ((options & GetFullPathOptions.KeepDotOrSpace) == 0) { path = path.TrimEnd(); } } if ((options & GetFullPathOptions.AddTrailingDirectorySeparator) != 0) { path = AddTrailingDirectorySeparator(path, false); } if ((options & GetFullPathOptions.RemoveTrailingDirectorySeparator) != 0) { path = RemoveTrailingDirectorySeparator(path); } if ((options & GetFullPathOptions.CheckInvalidPathChars) != 0) { CheckInvalidPathChars(path, (options & GetFullPathOptions.CheckAdditional) != 0, false); } // Trim leading whitespace. if ((options & GetFullPathOptions.KeepDotOrSpace) == 0) { path = path.TrimStart(); } return(path); }
internal static string GetRegularPathCore(string path, GetFullPathOptions options) { if (path == null) { throw new ArgumentNullException("path"); } if (path.Length == 0 || Utils.IsNullOrWhiteSpace(path)) { throw new ArgumentException(Resources.Path_Is_Zero_Length_Or_Only_White_Space, "path"); } if (options != GetFullPathOptions.None) { path = ApplyFullPathOptions(path, options); } return(path.StartsWith(GlobalRootPrefix, StringComparison.OrdinalIgnoreCase) || path.StartsWith(VolumePrefix, StringComparison.OrdinalIgnoreCase) || !path.StartsWith(LongPathPrefix, StringComparison.OrdinalIgnoreCase) ? path : (path.StartsWith(LongPathUncPrefix, StringComparison.OrdinalIgnoreCase) ? UncPrefix + path.Substring(LongPathUncPrefix.Length) : path.Substring(LongPathPrefix.Length))); }
internal static void CreateHardlinkCore(KernelTransaction transaction, string fileName, string existingFileName, PathFormat pathFormat) { const GetFullPathOptions options = GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.FullCheck; string fileNameLp = Path.GetExtendedLengthPathCore(transaction, fileName, pathFormat, options); string existingFileNameLp = Path.GetExtendedLengthPathCore(transaction, existingFileName, pathFormat, options); if (!(transaction == null || !NativeMethods.IsAtLeastWindowsVista // CreateHardLink() / CreateHardLinkTransacted() // In the ANSI version of this function, the name is limited to MAX_PATH characters. // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path. // 2013-01-13: MSDN does not confirm LongPath usage but a Unicode version of this function exists. ? NativeMethods.CreateHardLink(fileNameLp, existingFileNameLp, IntPtr.Zero) : NativeMethods.CreateHardLinkTransacted(fileNameLp, existingFileNameLp, IntPtr.Zero, transaction.SafeHandle))) { int lastError = Marshal.GetLastWin32Error(); switch ((uint)lastError) { case Win32Errors.ERROR_INVALID_FUNCTION: throw new NotSupportedException(Resources.HardLinks_Not_Supported); default: NativeError.ThrowException(lastError, fileNameLp, existingFileName); break; } } }
internal static void ReplaceCore(string sourceFileName, string destinationFileName, string destinationBackupFileName, bool ignoreMetadataErrors, PathFormat pathFormat) { const GetFullPathOptions options = GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.FullCheck; string sourceFileNameLp = Path.GetExtendedLengthPathCore(null, sourceFileName, pathFormat, options); string destinationFileNameLp = Path.GetExtendedLengthPathCore(null, destinationFileName, pathFormat, options); // Pass null to the destinationBackupFileName parameter if you do not want to create a backup of the file being replaced. string destinationBackupFileNameLp = destinationBackupFileName == null ? null : Path.GetExtendedLengthPathCore(null, destinationBackupFileName, pathFormat, options); const int replacefileWriteThrough = 1; const int replacefileIgnoreMergeErrors = 2; FileSystemRights dwReplaceFlags = (FileSystemRights)replacefileWriteThrough; if (ignoreMetadataErrors) { dwReplaceFlags |= (FileSystemRights)replacefileIgnoreMergeErrors; } // ReplaceFile() // In the ANSI version of this function, the name is limited to MAX_PATH characters. // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path. // 2013-01-13: MSDN does not confirm LongPath usage but a Unicode version of this function exists. if (!NativeMethods.ReplaceFile(destinationFileNameLp, sourceFileNameLp, destinationBackupFileNameLp, dwReplaceFlags, IntPtr.Zero, IntPtr.Zero)) { NativeError.ThrowException(Marshal.GetLastWin32Error(), sourceFileNameLp, destinationFileNameLp); } }
internal static void CreateHardlinkCore(KernelTransaction transaction, string fileName, string existingFileName, PathFormat pathFormat) { if (pathFormat != PathFormat.LongFullPath) { const GetFullPathOptions options = GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.FullCheck; fileName = Path.GetExtendedLengthPathCore(transaction, fileName, pathFormat, options); existingFileName = Path.GetExtendedLengthPathCore(transaction, existingFileName, pathFormat, options); } if (!(transaction == null || !NativeMethods.IsAtLeastWindowsVista // CreateHardLink() / CreateHardLinkTransacted() // 2013-01-13: MSDN does not confirm LongPath usage but a Unicode version of this function exists. // 2017-05-30: CreateHardLink() MSDN confirms LongPath usage: Starting with Windows 10, version 1607 ? NativeMethods.CreateHardLink(fileName, existingFileName, IntPtr.Zero) : NativeMethods.CreateHardLinkTransacted(fileName, existingFileName, IntPtr.Zero, transaction.SafeHandle))) { var lastError = (uint)Marshal.GetLastWin32Error(); switch (lastError) { case Win32Errors.ERROR_INVALID_FUNCTION: throw new NotSupportedException(Resources.HardLinks_Not_Supported); default: NativeError.ThrowException(lastError, existingFileName, fileName); break; } } }
internal static string GetLongPathCore(string path, GetFullPathOptions options) { if (null == path) { throw new ArgumentNullException("path"); } if (path.Length == 0 || Utils.IsNullOrWhiteSpace(path)) { throw new ArgumentException(Resources.Path_Is_Zero_Length_Or_Only_White_Space, "path"); } if (options != GetFullPathOptions.None) { path = ApplyFullPathOptions(path, options); } // ".", "C:" if (path.Length <= 2 || path.StartsWith(LongPathPrefix, StringComparison.Ordinal) || path.StartsWith(LogicalDrivePrefix, StringComparison.Ordinal) || path.StartsWith(NonInterpretedPathPrefix, StringComparison.Ordinal)) { return(path); } if (path.StartsWith(UncPrefix, StringComparison.Ordinal)) { return(LongPathUncPrefix + path.Substring(UncPrefix.Length)); } return(IsPathRooted(path, false) && IsLogicalDriveCore(path, false, PathFormat.LongFullPath) ? LongPathPrefix + path : path); }
internal static void CreateSymbolicLinkCore(KernelTransaction transaction, string symlinkFileName, string targetFileName, SymbolicLinkTarget targetType, PathFormat pathFormat) { if (!NativeMethods.IsAtLeastWindowsVista) { throw new PlatformNotSupportedException(new Win32Exception((int)Win32Errors.ERROR_OLD_WIN_VERSION).Message); } if (pathFormat != PathFormat.LongFullPath) { const GetFullPathOptions options = GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.FullCheck; symlinkFileName = Path.GetExtendedLengthPathCore(transaction, symlinkFileName, pathFormat, options); targetFileName = Path.GetExtendedLengthPathCore(transaction, targetFileName, pathFormat, options); } // Don't use long path notation, as it will be empty upon creation. targetFileName = Path.GetRegularPathCore(targetFileName, GetFullPathOptions.None, false); if (targetType == SymbolicLinkTarget.Directory) { ThrowIOExceptionIfFsoExist(transaction, false, targetFileName, pathFormat); ThrowIOExceptionIfFsoExist(transaction, false, symlinkFileName, pathFormat); } else { ThrowIOExceptionIfFsoExist(transaction, true, targetFileName, pathFormat); ThrowIOExceptionIfFsoExist(transaction, true, symlinkFileName, pathFormat); } var success = null == transaction // CreateSymbolicLink() / CreateSymbolicLinkTransacted() // 2014-02-14: MSDN does not confirm LongPath usage but a Unicode version of this function exists. // 2015-07-17: This function does not support long paths. // 2017-05-30: CreateSymbolicLink() MSDN confirms LongPath usage: Starting with Windows 10, version 1607 ? NativeMethods.CreateSymbolicLink(symlinkFileName, targetFileName, targetType) : NativeMethods.CreateSymbolicLinkTransacted(symlinkFileName, targetFileName, targetType, transaction.SafeHandle); var lastError = (uint)Marshal.GetLastWin32Error(); if (!success) { NativeError.ThrowException(lastError, targetFileName, symlinkFileName); } }
public FileInfo Replace(string destinationFileName, string destinationBackupFileName, PathFormat pathFormat) { const GetFullPathOptions options = GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.FullCheck; string destinationFileNameLp = Path.GetExtendedLengthPathCore(Transaction, destinationFileName, pathFormat, options); string destinationBackupFileNameLp = destinationBackupFileName != null ? Path.GetExtendedLengthPathCore(Transaction, destinationBackupFileName, pathFormat, options) : null; File.ReplaceCore(LongFullName, destinationFileNameLp, destinationBackupFileNameLp, false, PathFormat.LongFullPath); return(new FileInfo(Transaction, destinationFileNameLp, PathFormat.LongFullPath)); }
internal static string GetLongPathInternal(string path, GetFullPathOptions options) { if (path == null) { throw new ArgumentNullException("path"); } if (path.Length == 0 || Utils.IsNullOrWhiteSpace(path)) { throw new ArgumentException(Resources.PathIsZeroLengthOrOnlyWhiteSpace, "path"); } if (options != GetFullPathOptions.None) { if ((options & GetFullPathOptions.TrimEnd) != 0) { path = path.TrimEnd(); } if ((options & GetFullPathOptions.AddTrailingDirectorySeparator) != 0) { path = AddTrailingDirectorySeparator(path, false); } if ((options & GetFullPathOptions.RemoveTrailingDirectorySeparator) != 0) { path = RemoveTrailingDirectorySeparator(path, false); } if ((options & GetFullPathOptions.CheckInvalidPathChars) != 0) { CheckInvalidPathChars(path, false); } } if (path.StartsWith(LongPathPrefix, StringComparison.OrdinalIgnoreCase) || path.StartsWith(LogicalDrivePrefix, StringComparison.OrdinalIgnoreCase)) { return(path); } // ".", "C:" return(path.Length > 2 && (IsLocalPath(path, false) || IsUncPath(path, false)) ? path.StartsWith(UncPrefix, StringComparison.OrdinalIgnoreCase) ? LongPathUncPrefix + path.Substring(UncPrefix.Length) : LongPathPrefix + path : path); // 2015-01-11 Issue #50: Path.GetLongPath() does not prefix on "C:", should it? }
internal static void ReplaceCore(string sourceFileName, string destinationFileName, string destinationBackupFileName, bool ignoreMetadataErrors, PathFormat pathFormat) { const GetFullPathOptions options = GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.FullCheck; var sourceFileNameLp = sourceFileName; var destinationFileNameLp = destinationFileName; if (pathFormat != PathFormat.LongFullPath) { sourceFileNameLp = Path.GetExtendedLengthPathCore(null, sourceFileName, pathFormat, options); destinationFileNameLp = Path.GetExtendedLengthPathCore(null, destinationFileName, pathFormat, options); } // Pass null to the destinationBackupFileName parameter if you do not want to create a backup of the file being replaced. var destinationBackupFileNameLp = null == destinationBackupFileName ? null : Path.GetExtendedLengthPathCore(null, destinationBackupFileName, pathFormat, options); const int replacefileWriteThrough = 1; const int replacefileIgnoreMergeErrors = 2; var dwReplaceFlags = (FileSystemRights)replacefileWriteThrough; if (ignoreMetadataErrors) { dwReplaceFlags |= (FileSystemRights)replacefileIgnoreMergeErrors; } // ReplaceFile() // 2013-01-13: MSDN does not confirm LongPath usage but a Unicode version of this function exists. // 2017-05-30: MSDN confirms LongPath usage: Starting with Windows 10, version 1607 var success = NativeMethods.ReplaceFile(destinationFileNameLp, sourceFileNameLp, destinationBackupFileNameLp, dwReplaceFlags, IntPtr.Zero, IntPtr.Zero); var lastError = (uint)Marshal.GetLastWin32Error(); if (!success) { NativeError.ThrowException(lastError, sourceFileNameLp, destinationFileNameLp); } }
internal static string GetRegularPathInternal(string path, GetFullPathOptions options) { if (path == null) { throw new ArgumentNullException("path"); } if (path.Length == 0 || Utils.IsNullOrWhiteSpace(path)) { throw new ArgumentException(Resources.PathIsZeroLengthOrOnlyWhiteSpace, "path"); } if (options != GetFullPathOptions.None) { if ((options & GetFullPathOptions.TrimEnd) != 0) { path = path.TrimEnd(); } if ((options & GetFullPathOptions.AddTrailingDirectorySeparator) != 0) { path = AddTrailingDirectorySeparator(path, false); } if ((options & GetFullPathOptions.RemoveTrailingDirectorySeparator) != 0) { path = RemoveTrailingDirectorySeparator(path, false); } if ((options & GetFullPathOptions.CheckInvalidPathChars) != 0) { CheckInvalidPathChars(path, false); } } if (!path.StartsWith(LongPathPrefix, StringComparison.OrdinalIgnoreCase)) { return(path); } return(path.StartsWith(LongPathUncPrefix, StringComparison.OrdinalIgnoreCase) ? UncPrefix + path.Substring(LongPathUncPrefix.Length) : path.Substring(LongPathPrefix.Length)); }
internal static string GetLongPathCore(string path, GetFullPathOptions options) { if (path == null) { throw new ArgumentNullException("path"); } if (path.Length == 0 || Utils.IsNullOrWhiteSpace(path)) { throw new ArgumentException(Resources.Path_Is_Zero_Length_Or_Only_White_Space, "path"); } if (options != GetFullPathOptions.None) { path = ApplyFullPathOptions(path, options); } // ".", "C:" if (path.Length <= 2 || path.StartsWith(LongPathPrefix, StringComparison.OrdinalIgnoreCase) || path.StartsWith(LogicalDrivePrefix, StringComparison.OrdinalIgnoreCase)) { return(path); } if (path.StartsWith(UncPrefix, StringComparison.OrdinalIgnoreCase)) { return(LongPathUncPrefix + path.Substring(UncPrefix.Length)); } // Don't use char.IsLetter() here as that can be misleading. // The only valid drive letters are: a-z and A-Z. char c = path[0]; return(IsPathRooted(path, false) && ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) ? LongPathPrefix + path : path); }
/// <summary>Applies the <seealso cref="GetFullPathOptions"/> to <paramref name="path"/></summary> /// <returns><paramref name="path"/> with applied <paramref name="options"/>.</returns> /// <exception cref="ArgumentNullException"/> /// <exception cref="ArgumentException"/> /// <param name="path"></param> /// <param name="options"></param> private static string ApplyFullPathOptions(string path, GetFullPathOptions options) { if ((options & GetFullPathOptions.TrimEnd) != 0) { path = path.TrimEnd(); } if ((options & GetFullPathOptions.AddTrailingDirectorySeparator) != 0) { path = AddTrailingDirectorySeparator(path, false); } if ((options & GetFullPathOptions.RemoveTrailingDirectorySeparator) != 0) { path = RemoveTrailingDirectorySeparator(path, false); } if ((options & GetFullPathOptions.CheckInvalidPathChars) != 0) { CheckInvalidPathChars(path, (options & GetFullPathOptions.CheckAdditional) != 0); } return(path); }
public static string GetFullPathTransacted(KernelTransaction transaction, string path, GetFullPathOptions options) { return(GetFullPathTackleCore(transaction, path, options)); }
/// <summary>Gets the path as a long full path.</summary> /// <returns>The path as an extended length path.</returns> /// <exception cref="ArgumentException"/> /// <param name="transaction">The transaction.</param> /// <param name="sourcePath">Full pathname of the source path to convert.</param> /// <param name="pathFormat">The path format to use.</param> /// <param name="options">Options for controlling the operation. Note that on .NET 3.5 the TrimEnd option has no effect.</param> internal static string GetExtendedLengthPathCore(KernelTransaction transaction, string sourcePath, PathFormat pathFormat, GetFullPathOptions options) { switch (pathFormat) { case PathFormat.LongFullPath: return(sourcePath); case PathFormat.FullPath: return(GetLongPathCore(sourcePath, GetFullPathOptions.None)); case PathFormat.RelativePath: #if NET35 // .NET 3.5 the TrimEnd option has no effect. options = options & ~GetFullPathOptions.TrimEnd; #endif return(GetFullPathCore(transaction, sourcePath, GetFullPathOptions.AsLongPath | options)); default: throw new ArgumentException("Invalid value for " + typeof(PathFormat).Name + ": " + pathFormat); } }
internal static string GetFullPathCore(KernelTransaction transaction, string path, GetFullPathOptions options) { if (path != null) { if (path.StartsWith(GlobalRootPrefix, StringComparison.OrdinalIgnoreCase) || path.StartsWith(VolumePrefix, StringComparison.OrdinalIgnoreCase)) { return(path); } } if (options != GetFullPathOptions.None) { if ((options & GetFullPathOptions.CheckInvalidPathChars) != 0) { bool checkAdditional = (options & GetFullPathOptions.CheckAdditional) != 0; CheckInvalidPathChars(path, checkAdditional); // Prevent duplicate checks. options &= ~GetFullPathOptions.CheckInvalidPathChars; if (checkAdditional) { options &= ~GetFullPathOptions.CheckAdditional; } } // Do not remove trailing directory separator when path points to a drive like: "C:\" // Doing so makes path point to the current directory. if (path == null || path.Length <= 3 || (!path.StartsWith(LongPathPrefix, StringComparison.OrdinalIgnoreCase) && path[1] != VolumeSeparatorChar)) { options &= ~GetFullPathOptions.RemoveTrailingDirectorySeparator; } } string pathLp = GetLongPathCore(path, options); uint bufferSize = NativeMethods.MaxPathUnicode; // ChangeErrorMode is for the Win32 SetThreadErrorMode() method, used to suppress possible pop-ups. using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors)) { startGetFullPathName: var buffer = new StringBuilder((int)bufferSize); uint returnLength = (transaction == null || !NativeMethods.IsAtLeastWindowsVista // GetFullPathName() / GetFullPathNameTransacted() // In the ANSI version of this function, the name is limited to MAX_PATH characters. // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path. // 2013-04-15: MSDN confirms LongPath usage. ? NativeMethods.GetFullPathName(pathLp, bufferSize, buffer, IntPtr.Zero) : NativeMethods.GetFullPathNameTransacted(pathLp, bufferSize, buffer, IntPtr.Zero, transaction.SafeHandle)); if (returnLength != Win32Errors.NO_ERROR) { if (returnLength > bufferSize) { bufferSize = returnLength; goto startGetFullPathName; } } else { if ((options & GetFullPathOptions.ContinueOnNonExist) != 0) { return(null); } NativeError.ThrowException(pathLp); } return((options & GetFullPathOptions.AsLongPath) != 0 ? GetLongPathCore(buffer.ToString(), GetFullPathOptions.None) : GetRegularPathCore(buffer.ToString(), GetFullPathOptions.None)); } }
private static string NormalizePath(string path, GetFullPathOptions options) { var newBuffer = new StringBuilder(NativeMethods.MaxPathUnicode); var index = 0; uint numSpaces = 0; uint numDots = 0; var fixupDirectorySeparator = false; // Number of significant chars other than potentially suppressible // dots and spaces since the last directory or volume separator char uint numSigChars = 0; var lastSigChar = -1; // Index of last significant character. // Whether this segment of the path (not the complete path) started // with a volume separator char. Reject "c:...". var startedWithVolumeSeparator = false; var firstSegment = true; var lastDirectorySeparatorPos = 0; // LEGACY: This code is here for backwards compatibility reasons. It // ensures that \\foo.cs\bar.cs stays \\foo.cs\bar.cs instead of being // turned into \foo.cs\bar.cs. if (path.Length > 0 && (path[0] == DirectorySeparatorChar || path[0] == AltDirectorySeparatorChar)) { newBuffer.Append(DirectorySeparatorChar); index++; lastSigChar = 0; } // Normalize the string, stripping out redundant dots, spaces, and slashes. while (index < path.Length) { var currentChar = path[index]; // We handle both directory separators and dots specially. For // directory separators, we consume consecutive appearances. // For dots, we consume all dots beyond the second in // succession. All other characters are added as is. In // addition we consume all spaces after the last other char // in a directory name up until the directory separator. if (currentChar == DirectorySeparatorChar || currentChar == AltDirectorySeparatorChar) { // If we have a path like "123.../foo", remove the trailing dots. // However, if we found "c:\temp\..\bar" or "c:\temp\...\bar", don't. // Also remove trailing spaces from both files & directory names. // This was agreed on with the OS team to fix undeletable directory // names ending in spaces. // If we saw a '\' as the previous last significant character and // are simply going to write out dots, suppress them. // If we only contain dots and slashes though, only allow // a string like [dot]+ [space]*. Ignore everything else. // Legal: "\.. \", "\...\", "\. \" // Illegal: "\.. .\", "\. .\", "\ .\" if (numSigChars == 0) { // Dot and space handling if (numDots > 0) { // Look for ".[space]*" or "..[space]*" var start = lastSigChar + 1; if (path[start] != CurrentDirectoryPrefixChar) { throw new ArgumentException(path, "path"); } // Only allow "[dot]+[space]*", and normalize the legal ones to "." or ".." if (numDots >= 2) { // Reject "C:..." if (startedWithVolumeSeparator && numDots > 2) { throw new ArgumentException(path, "path"); } if (path[start + 1] == CurrentDirectoryPrefixChar) { // Search for a space in the middle of the dots and throw for (var i = start + 2; i < start + numDots; i++) { if (path[i] != CurrentDirectoryPrefixChar) { throw new ArgumentException(path, "path"); } } numDots = 2; } else { if (numDots > 1) { throw new ArgumentException(path, "path"); } numDots = 1; } } if (numDots == 2) { newBuffer.Append(CurrentDirectoryPrefixChar); } newBuffer.Append(CurrentDirectoryPrefixChar); fixupDirectorySeparator = false; // Continue in this case, potentially writing out '\'. } if (numSpaces > 0 && firstSegment) { // Handle strings like " \\server\share". if (index + 1 < path.Length && (path[index + 1] == DirectorySeparatorChar || path[index + 1] == AltDirectorySeparatorChar)) { newBuffer.Append(DirectorySeparatorChar); } } } numDots = 0; numSpaces = 0; // Suppress trailing spaces if (!fixupDirectorySeparator) { fixupDirectorySeparator = true; newBuffer.Append(DirectorySeparatorChar); } numSigChars = 0; lastSigChar = index; startedWithVolumeSeparator = false; firstSegment = false; var thisPos = newBuffer.Length - 1; if (thisPos - lastDirectorySeparatorPos > NativeMethods.MaxDirectoryLength) { throw new PathTooLongException(path); } lastDirectorySeparatorPos = thisPos; } // if (Found directory separator) else if (currentChar == CurrentDirectoryPrefixChar) { // Reduce only multiple .'s only after slash to 2 dots. For // instance a...b is a valid file name. numDots++; // Don't flush out non-terminal spaces here, because they may in // the end not be significant. Turn "c:\ . .\foo" -> "c:\foo" // which is the conclusion of removing trailing dots & spaces, // as well as folding multiple '\' characters. } else if (currentChar == ' ') { numSpaces++; } else { // Normal character logic fixupDirectorySeparator = false; // To reject strings like "C:...\foo" and "C :\foo" if (firstSegment && currentChar == VolumeSeparatorChar) { // Only accept "C:", not "c :" or ":" // Get a drive letter or ' ' if index is 0. var driveLetter = index > 0 ? path[index - 1] : ' '; var validPath = numDots == 0 && numSigChars >= 1 && driveLetter != ' '; if (!validPath) { throw new ArgumentException(path, "path"); } startedWithVolumeSeparator = true; // We need special logic to make " c:" work, we should not fix paths like " foo::$DATA" if (numSigChars > 1) { // Common case, simply do nothing var spaceCount = 0; // How many spaces did we write out, numSpaces has already been reset. while (spaceCount < newBuffer.Length && newBuffer[spaceCount] == ' ') { spaceCount++; } if (numSigChars - spaceCount == 1) { //Safe to update stack ptr directly newBuffer.Length = 0; newBuffer.Append(driveLetter); // Overwrite spaces, we need a special case to not break " foo" as a relative path. } } numSigChars = 0; } else { numSigChars += 1 + numDots + numSpaces; } // Copy any spaces & dots since the last significant character // to here. Note we only counted the number of dots & spaces, // and don't know what order they're in. Hence the copy. if (numDots > 0 || numSpaces > 0) { var numCharsToCopy = lastSigChar >= 0 ? index - lastSigChar - 1 : index; if (numCharsToCopy > 0) { for (var i = 0; i < numCharsToCopy; i++) { newBuffer.Append(path[lastSigChar + 1 + i]); } } numDots = 0; numSpaces = 0; } newBuffer.Append(currentChar); lastSigChar = index; } index++; } if (newBuffer.Length - 1 - lastDirectorySeparatorPos > NativeMethods.MaxDirectoryLength) { throw new PathTooLongException(path); } // Drop any trailing dots and spaces from file & directory names, EXCEPT // we MUST make sure that "C:\foo\.." is correctly handled. // Also handle "C:\foo\." -> "C:\foo", while "C:\." -> "C:\" if (numSigChars == 0) { if (numDots > 0) { // Look for ".[space]*" or "..[space]*" var start = lastSigChar + 1; if (path[start] != CurrentDirectoryPrefixChar) { throw new ArgumentException(path, "path"); } // Only allow "[dot]+[space]*", and normalize the legal ones to "." or ".." if (numDots >= 2) { // Reject "C:..." if (startedWithVolumeSeparator && numDots > 2) { throw new ArgumentException(path, "path"); } if (path[start + 1] == CurrentDirectoryPrefixChar) { // Search for a space in the middle of the dots and throw for (var i = start + 2; i < start + numDots; i++) { if (path[i] != CurrentDirectoryPrefixChar) { throw new ArgumentException(path, "path"); } } numDots = 2; } else { if (numDots > 1) { throw new ArgumentException(path, "path"); } numDots = 1; } } if (numDots == 2) { newBuffer.Append(CurrentDirectoryPrefixChar); } newBuffer.Append(CurrentDirectoryPrefixChar); } } // If we ended up eating all the characters, bail out. if (newBuffer.Length == 0) { throw new ArgumentException(path, "path"); } // Disallow URL's here. Some of our other Win32 API calls will reject // them later, so we might be better off rejecting them here. // Note we've probably turned them into "file:\D:\foo.tmp" by now. // But for compatibility, ensure that callers that aren't doing a // full check aren't rejected here. if ((options & GetFullPathOptions.FullCheck) != 0) { var newBufferString = newBuffer.ToString(); if (newBufferString.StartsWith(Uri.UriSchemeHttp + ":", StringComparison.OrdinalIgnoreCase) || newBufferString.StartsWith(Uri.UriSchemeFile + ":", StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException(path, "path"); } } // Call the Win32 API to do the final canonicalization step. const int result = 1; /* Throw an ArgumentException for paths like \\, \\server, \\server\ * This check can only be properly done after normalizing, so \\foo\.. will be properly rejected. Also, reject \\?\GLOBALROOT\ * (an internal kernel path) because it provides aliases for drives. */ if (newBuffer.Length > 1 && newBuffer[0] == DirectorySeparatorChar && newBuffer[1] == DirectorySeparatorChar) { var startIndex = 2; while (startIndex < result) { if (newBuffer[startIndex] == DirectorySeparatorChar) { startIndex++; break; } startIndex++; } if (startIndex == result) { throw new ArgumentException(path, "path"); } } return(newBuffer.ToString()); }
internal static string GetRegularPathInternal(string path, GetFullPathOptions options) { if (path == null) throw new ArgumentNullException("path"); if (path.Length == 0 || Utils.IsNullOrWhiteSpace(path)) throw new ArgumentException(Resources.PathIsZeroLengthOrOnlyWhiteSpace, "path"); if (options != GetFullPathOptions.None) { if ((options & GetFullPathOptions.TrimEnd) != 0) path = path.TrimEnd(); if ((options & GetFullPathOptions.AddTrailingDirectorySeparator) != 0) path = AddTrailingDirectorySeparator(path, false); if ((options & GetFullPathOptions.RemoveTrailingDirectorySeparator) != 0) path = RemoveTrailingDirectorySeparator(path, false); if ((options & GetFullPathOptions.CheckInvalidPathChars) != 0) CheckInvalidPathChars(path, false); } if (!path.StartsWith(LongPathPrefix, StringComparison.OrdinalIgnoreCase)) return path; return path.StartsWith(LongPathUncPrefix, StringComparison.OrdinalIgnoreCase) ? UncPrefix + path.Substring(LongPathUncPrefix.Length) : path.Substring(LongPathPrefix.Length); }
internal static string GetHashCore(KernelTransaction transaction, string fileFullPath, HashType hashType, PathFormat pathFormat) { const GetFullPathOptions options = GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.FullCheck; var fileNameLp = Path.GetExtendedLengthPathCore(transaction, fileFullPath, pathFormat, options); byte[] hash = null; using (var fs = OpenCore(transaction, fileNameLp, FileMode.Open, FileAccess.Read, FileShare.Read, ExtendedFileAttributes.Normal, null, null, PathFormat.LongFullPath)) { switch (hashType) { case HashType.CRC32: using (var hType = new Crc32()) hash = hType.ComputeHash(fs); break; case HashType.CRC64ISO3309: using (var hType = new Crc64()) hash = hType.ComputeHash(fs); break; case HashType.MD5: using (var hType = MD5.Create()) hash = hType.ComputeHash(fs); break; case HashType.RIPEMD160: using (var hType = RIPEMD160.Create()) hash = hType.ComputeHash(fs); break; case HashType.SHA1: using (var hType = SHA1.Create()) hash = hType.ComputeHash(fs); break; case HashType.SHA256: using (var hType = SHA256.Create()) hash = hType.ComputeHash(fs); break; case HashType.SHA384: using (var hType = SHA384.Create()) hash = hType.ComputeHash(fs); break; case HashType.SHA512: using (var hType = SHA512.Create()) hash = hType.ComputeHash(fs); break; } } if (null != hash) { var sb = new StringBuilder(hash.Length); foreach (var b in hash) { sb.Append(b.ToString("X2", CultureInfo.InvariantCulture)); } return(sb.ToString().ToUpperInvariant()); } return(string.Empty); }
internal static string GetLongPathInternal(string path, GetFullPathOptions options) { if (path == null) throw new ArgumentNullException("path"); if (path.Length == 0 || Utils.IsNullOrWhiteSpace(path)) throw new ArgumentException(Resources.PathIsZeroLengthOrOnlyWhiteSpace, "path"); if (options != GetFullPathOptions.None) { if ((options & GetFullPathOptions.TrimEnd) != 0) path = path.TrimEnd(); if ((options & GetFullPathOptions.AddTrailingDirectorySeparator) != 0) path = AddTrailingDirectorySeparator(path, false); if ((options & GetFullPathOptions.RemoveTrailingDirectorySeparator) != 0) path = RemoveTrailingDirectorySeparator(path, false); if ((options & GetFullPathOptions.CheckInvalidPathChars) != 0) CheckInvalidPathChars(path, false); } if (path.StartsWith(LongPathPrefix, StringComparison.OrdinalIgnoreCase) || path.StartsWith(LogicalDrivePrefix, StringComparison.OrdinalIgnoreCase)) return path; // ".", "C:" return path.Length > 2 && (IsLocalPath(path, false) || IsUncPath(path, false)) ? path.StartsWith(UncPrefix, StringComparison.OrdinalIgnoreCase) ? LongPathUncPrefix + path.Substring(UncPrefix.Length) : LongPathPrefix + path : path; // 2015-01-11 Issue #50: Path.GetLongPath() does not prefix on "C:", should it? }
public static string LocalToUnc(string localPath, PathFormat pathFormat, GetFullPathOptions fullPathOptions) { return(LocalToUncCore(localPath, pathFormat, fullPathOptions)); }
internal static string LocalToUncCore(string localPath, PathFormat pathFormat, GetFullPathOptions fullPathOptions) { if (Utils.IsNullOrWhiteSpace(localPath)) { return(null); } if (pathFormat == PathFormat.RelativePath) { CheckSupportedPathFormat(localPath, true, true); } var addTrailingDirectorySeparator = (fullPathOptions & GetFullPathOptions.AddTrailingDirectorySeparator) != 0; var removeTrailingDirectorySeparator = (fullPathOptions & GetFullPathOptions.RemoveTrailingDirectorySeparator) != 0; if (addTrailingDirectorySeparator && removeTrailingDirectorySeparator) { throw new ArgumentException(Resources.GetFullPathOptions_Add_And_Remove_DirectorySeparator_Invalid, "fullPathOptions"); } if (!removeTrailingDirectorySeparator && !addTrailingDirectorySeparator) { // Add a trailing backslash when "localPath" ends with a backslash. if (localPath.EndsWith(DirectorySeparator, StringComparison.Ordinal)) { fullPathOptions &= ~GetFullPathOptions.RemoveTrailingDirectorySeparator; // Remove removal of trailing backslash. fullPathOptions |= GetFullPathOptions.AddTrailingDirectorySeparator; // Add adding trailing backslash. } } var getAsLongPath = (fullPathOptions & GetFullPathOptions.AsLongPath) != 0; var returnUncPath = GetRegularPathCore(localPath, fullPathOptions | GetFullPathOptions.CheckInvalidPathChars, false); if (!IsUncPathCore(returnUncPath, true, false)) { if (returnUncPath[0] == CurrentDirectoryPrefixChar || !IsPathRooted(returnUncPath, false)) { returnUncPath = GetFullPathCore(null, returnUncPath, GetFullPathOptions.None); } var drive = GetPathRoot(returnUncPath, false); if (Utils.IsNullOrWhiteSpace(drive)) { return(returnUncPath); } var remoteInfo = Host.GetRemoteNameInfoCore(returnUncPath, true); // Network share. if (!Utils.IsNullOrWhiteSpace(remoteInfo.lpUniversalName)) { return(getAsLongPath ? GetLongPathCore(remoteInfo.lpUniversalName, fullPathOptions) : GetRegularPathCore(remoteInfo.lpUniversalName, fullPathOptions, false)); } // Network root. if (!Utils.IsNullOrWhiteSpace(remoteInfo.lpConnectionName)) { return(getAsLongPath ? GetLongPathCore(remoteInfo.lpConnectionName, fullPathOptions) : GetRegularPathCore(remoteInfo.lpConnectionName, fullPathOptions, false)); } // Split: localDrive[0] = "C", localDrive[1] = "\Windows" var localDrive = returnUncPath.Split(VolumeSeparatorChar); // Return: "\\localhost\C$\Windows" returnUncPath = string.Format(CultureInfo.InvariantCulture, "{0}{1}{2}{3}{4}", Host.GetUncName(), DirectorySeparator, localDrive[0], NetworkDriveSeparator, localDrive[1]); } return(getAsLongPath ? GetLongPathCore(returnUncPath, fullPathOptions) : GetRegularPathCore(returnUncPath, fullPathOptions, false)); }
internal static string GetFullPathInternal(KernelTransaction transaction, string path, GetFullPathOptions options) { if (path != null) if (path.StartsWith(GlobalRootPrefix, StringComparison.OrdinalIgnoreCase) || path.StartsWith(VolumePrefix, StringComparison.OrdinalIgnoreCase)) return path; if (options != GetFullPathOptions.None) { if ((options & GetFullPathOptions.CheckInvalidPathChars) != 0) { bool checkAdditional = (options & GetFullPathOptions.CheckAdditional) != 0; CheckInvalidPathChars(path, checkAdditional); // Prevent duplicate checks. options &= ~GetFullPathOptions.CheckInvalidPathChars; if (checkAdditional) options &= ~GetFullPathOptions.CheckAdditional; } // Do not remove trailing directory separator when path points to a drive like: "C:\" // Doing so makes path point to the current directory. if (path == null || path.Length <= 3 || (!path.StartsWith(LongPathPrefix, StringComparison.OrdinalIgnoreCase) && path[1] != VolumeSeparatorChar)) options &= ~GetFullPathOptions.RemoveTrailingDirectorySeparator; } string pathLp = GetLongPathInternal(path, options); uint bufferSize = NativeMethods.MaxPathUnicode; // ChangeErrorMode is for the Win32 SetThreadErrorMode() method, used to suppress possible pop-ups. using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors)) { startGetFullPathName: var buffer = new StringBuilder((int)bufferSize); uint returnLength = (transaction == null || !NativeMethods.IsAtLeastWindowsVista // GetFullPathName() / GetFullPathNameTransacted() // In the ANSI version of this function, the name is limited to MAX_PATH characters. // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path. // 2013-04-15: MSDN confirms LongPath usage. ? NativeMethods.GetFullPathName(pathLp, bufferSize, buffer, IntPtr.Zero) : NativeMethods.GetFullPathNameTransacted(pathLp, bufferSize, buffer, IntPtr.Zero, transaction.SafeHandle)); if (returnLength != Win32Errors.NO_ERROR) { if (returnLength > bufferSize) { bufferSize = returnLength; goto startGetFullPathName; } } else { if ((options & GetFullPathOptions.ContinueOnNonExist) != 0) return null; NativeError.ThrowException(pathLp); } return (options & GetFullPathOptions.AsLongPath) != 0 ? GetLongPathInternal(buffer.ToString(), GetFullPathOptions.None) : GetRegularPathInternal(buffer.ToString(), GetFullPathOptions.None); } }
internal static string GetFullPathCore(KernelTransaction transaction, string path, GetFullPathOptions options) { // Skip the special paths recognised by Windows kernel only. if (null != path) { if (path.StartsWith(GlobalRootPrefix, StringComparison.OrdinalIgnoreCase) || path.StartsWith(VolumePrefix, StringComparison.OrdinalIgnoreCase) || path.StartsWith(NonInterpretedPathPrefix, StringComparison.Ordinal)) { return(path); } } if (options != GetFullPathOptions.None) { if ((options & GetFullPathOptions.CheckInvalidPathChars) != 0) { var checkAdditional = (options & GetFullPathOptions.CheckAdditional) != 0; CheckInvalidPathChars(path, checkAdditional, false); // Prevent duplicate checks. options &= ~GetFullPathOptions.CheckInvalidPathChars; if (checkAdditional) { options &= ~GetFullPathOptions.CheckAdditional; } } // Do not remove trailing directory separator when path points to a drive like: "C:\" // Doing so makes path point to the current directory. // ".", "C:", "C:\" if (null == path || path.Length <= 3 || !path.StartsWith(LongPathPrefix, StringComparison.Ordinal) && path[1] != VolumeSeparatorChar) { options &= ~GetFullPathOptions.RemoveTrailingDirectorySeparator; } } var pathLp = GetLongPathCore(path, options); uint bufferSize = NativeMethods.MaxPathUnicode; using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors)) { startGetFullPathName: var buffer = new StringBuilder((int)bufferSize); var returnLength = null == transaction || !NativeMethods.IsAtLeastWindowsVista // GetFullPathName() / GetFullPathNameTransacted() // 2013-04-15: MSDN confirms LongPath usage. ? NativeMethods.GetFullPathName(pathLp, bufferSize, buffer, IntPtr.Zero) : NativeMethods.GetFullPathNameTransacted(pathLp, bufferSize, buffer, IntPtr.Zero, transaction.SafeHandle); if (returnLength != Win32Errors.NO_ERROR) { if (returnLength > bufferSize) { bufferSize = returnLength; goto startGetFullPathName; } } else { if ((options & GetFullPathOptions.ContinueOnNonExist) != 0) { return(null); } NativeError.ThrowException(returnLength, pathLp); } var finalFullPath = (options & GetFullPathOptions.AsLongPath) != 0 ? GetLongPathCore(buffer.ToString(), GetFullPathOptions.None) : GetRegularPathCore(buffer.ToString(), GetFullPathOptions.None, false); finalFullPath = NormalizePath(finalFullPath, options); if ((options & GetFullPathOptions.KeepDotOrSpace) != 0) { if (pathLp.EndsWith(CurrentDirectoryPrefix, StringComparison.Ordinal)) { finalFullPath += CurrentDirectoryPrefix; } var lastChar = pathLp[pathLp.Length - 1]; if (char.IsWhiteSpace(lastChar)) { finalFullPath += lastChar; } } return(finalFullPath); } }
private static string GetFullPathTackleCore(KernelTransaction transaction, string path, GetFullPathOptions options) { if (path != null) { if (path.StartsWith(GlobalRootPrefix, StringComparison.OrdinalIgnoreCase) || path.StartsWith(VolumePrefix, StringComparison.OrdinalIgnoreCase)) { return(path); } CheckInvalidUncPath(path); } CheckSupportedPathFormat(path, true, true); return(GetFullPathCore(transaction, path, options)); }
internal static string GetExtendedLengthPathCore(KernelTransaction transaction, string path, PathFormat pathFormat, GetFullPathOptions options) { if (null == path) { return(null); } switch (pathFormat) { case PathFormat.LongFullPath: if (options != GetFullPathOptions.None) { // If pathFormat equals LongFullPath it is possible that the trailing backslashg ('\') is not added or removed. // Prevent that. options &= ~GetFullPathOptions.CheckAdditional; options &= ~GetFullPathOptions.CheckInvalidPathChars; options &= ~GetFullPathOptions.FullCheck; options &= ~GetFullPathOptions.TrimEnd; path = ApplyFullPathOptions(path, options); } return(path); case PathFormat.FullPath: return(GetLongPathCore(path, GetFullPathOptions.None)); case PathFormat.RelativePath: #if NET35 // .NET 3.5 the TrimEnd option has no effect. options = options & ~GetFullPathOptions.TrimEnd; #endif return(GetFullPathCore(transaction, path, GetFullPathOptions.AsLongPath | options)); default: throw new ArgumentException("Invalid value: " + pathFormat, "pathFormat"); } }
public static string GetFullPath(string path, GetFullPathOptions options) { return(GetFullPathTackleCore(null, path, options)); }
/// <summary>Gets the path as a long full path.</summary> /// <returns>The path as an extended length path.</returns> /// <exception cref="ArgumentException">Thrown when one or more arguments have unsupported or illegal values.</exception> /// <param name="transaction">The transaction.</param> /// <param name="sourcePath">Full pathname of the source path to convert.</param> /// <param name="pathFormat">The path format to use.</param> /// <param name="options">Options for controlling the operation. Note that on .NET 3.5 the TrimEnd option has no effect.</param> internal static string GetExtendedLengthPathInternal(KernelTransaction transaction, string sourcePath, PathFormat pathFormat, GetFullPathOptions options) { switch (pathFormat) { case PathFormat.LongFullPath: return sourcePath; case PathFormat.FullPath: return GetLongPathInternal(sourcePath, GetFullPathOptions.None); case PathFormat.RelativePath: #if NET35 // .NET 3.5 the TrimEnd option has no effect. options = options & ~GetFullPathOptions.TrimEnd; #endif return GetFullPathInternal(transaction, sourcePath, GetFullPathOptions.AsLongPath | options); default: throw new ArgumentException("Invalid value for " + typeof(PathFormat).Name + ": " + pathFormat); } }