/* Copyright (C) 2008-2016 Peter Palotas, Jeffrey Jangli, Alexandr Normuradov * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ using System; using System.Diagnostics.CodeAnalysis; using System.Security; using System.Text; namespace Alphaleonis.Win32.Filesystem { public static partial class Path { #region GetLongPath /// Makes an extended long path from the specified by prefixing . /// The prefixed with a , the minimum required full path is: "C:\". /// This method does not verify that the resulting path and file name are valid, or that they see an existing file on the associated volume. /// /// /// The path to the file or directory, this can also be an UNC path. [SecurityCritical] public static string GetLongPath(string path) { return GetLongPathCore(path, GetFullPathOptions.None); } #endregion // GetLongPath #region GetLongFrom83ShortPath /// [AlphaFS] Converts the specified existing path to its regular long form. /// An existing path to a folder or file. /// The regular full path. [SecurityCritical] public static string GetLongFrom83ShortPath(string path) { return GetLongShort83PathCore(null, path, false); } /// [AlphaFS] Converts the specified existing path to its regular long form. /// The transaction. /// An existing path to a folder or file. /// The regular full path. [SecurityCritical] public static string GetLongFrom83ShortPathTransacted(KernelTransaction transaction, string path) { return GetLongShort83PathCore(transaction, path, false); } #endregion // GetLongFrom83ShortPath #region GetRegularPath /// [AlphaFS] Gets the regular path from long prefixed one. i.e.: "\\?\C:\Temp\file.txt" to C:\Temp\file.txt" or: "\\?\UNC\Server\share\file.txt" to "\\Server\share\file.txt". /// Regular form path string. /// This method does not handle paths with volume names, eg. \\?\Volume{GUID}\Folder\file.txt. /// The path. [SecurityCritical] public static string GetRegularPath(string path) { return GetRegularPathCore(path, GetFullPathOptions.CheckInvalidPathChars, false); } #endregion // GetRegularPath #region GetShort83Path /// [AlphaFS] Retrieves the short path form of the specified path. /// A path that has the 8.3 path form. /// Will fail on NTFS volumes with disabled 8.3 name generation. /// The path must actually exist to be able to get the short path name. /// An existing path to a folder or file. [SecurityCritical] public static string GetShort83Path(string path) { return GetLongShort83PathCore(null, path, true); } /// [AlphaFS] Retrieves the short path form of the specified path. /// A path that has the 8.3 path form. /// Will fail on NTFS volumes with disabled 8.3 name generation. /// The path must actually exist to be able to get the short path name. /// The transaction. /// An existing path to a folder or file. [SecurityCritical] public static string GetShort83PathTransacted(KernelTransaction transaction, string path) { return GetLongShort83PathCore(transaction, path, true); } #endregion // GetShort83Path #region IsLongPath /// [AlphaFS] Determines whether the specified path starts with a or . /// if the specified path has a long path (UNC) prefix, otherwise. /// The path to the file or directory. [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Utils.IsNullOrWhiteSpace validates arguments.")] [SecurityCritical] public static bool IsLongPath(string path) { return !Utils.IsNullOrWhiteSpace(path) && path.StartsWith(LongPathPrefix, StringComparison.OrdinalIgnoreCase); } #endregion // IsLongPath #region Internals Methods /// Makes an extended long path from the specified by prefixing . /// The prefixed with a , the minimum required full path is: "C:\". /// This method does not verify that the resulting path and file name are valid, or that they see an existing file on the associated volume. /// /// /// The path to the file or directory, this can also be an UNC path. /// Options for controlling the full path retrieval. [SecurityCritical] 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; } /// Retrieves the short path form, or the regular long form of the specified . /// If is , a path of the 8.3 form otherwise the regular long form. /// /// Will fail on NTFS volumes with disabled 8.3 name generation. /// The path must actually exist to be able to get the short- or long path name. /// /// The transaction. /// An existing path to a folder or file. /// to retrieve the short path form, to retrieve the regular long form from the 8.3 . [SecurityCritical] private static string GetLongShort83PathCore(KernelTransaction transaction, string path, bool getShort) { string pathLp = GetFullPathCore(transaction, path, GetFullPathOptions.AsLongPath | GetFullPathOptions.FullCheck); var buffer = new StringBuilder(); uint actualLength = getShort ? NativeMethods.GetShortPathName(pathLp, null, 0) : (uint) path.Length; while (actualLength > buffer.Capacity) { buffer = new StringBuilder((int)actualLength); actualLength = getShort // GetShortPathName() // 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-01-29: MSDN confirms LongPath usage. ? NativeMethods.GetShortPathName(pathLp, buffer, (uint)buffer.Capacity) : transaction == null || !NativeMethods.IsAtLeastWindowsVista // GetLongPathName() // 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-01-29: MSDN confirms LongPath usage. ? NativeMethods.GetLongPathName(pathLp, buffer, (uint)buffer.Capacity) : NativeMethods.GetLongPathNameTransacted(pathLp, buffer, (uint)buffer.Capacity, transaction.SafeHandle); if (actualLength == Win32Errors.ERROR_SUCCESS) NativeError.ThrowException(pathLp); } return GetRegularPathCore(buffer.ToString(), GetFullPathOptions.None, false); } /// Gets the regular path from a long path. /// /// Returns the regular form of a long . /// For example: "\\?\C:\Temp\file.txt" to: "C:\Temp\file.txt", or: "\\?\UNC\Server\share\file.txt" to: "\\Server\share\file.txt". /// /// /// MSDN: String.TrimEnd Method notes to Callers: http://msdn.microsoft.com/en-us/library/system.string.trimend%28v=vs.110%29.aspx /// /// /// /// The path. /// Options for controlling the full path retrieval. /// When , throws an . [SecurityCritical] internal static string GetRegularPathCore(string path, GetFullPathOptions options, bool allowEmpty) { if (path == null) throw new ArgumentNullException("path"); if (!allowEmpty && (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)); } /// Gets the path as a long full path. /// The path as an extended length path. /// /// The transaction. /// Full pathname of the source path to convert. /// The path format to use. /// Options for controlling the operation. Note that on .NET 3.5 the TrimEnd option has no effect. 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); } } #endregion // Internals Methods } }