|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
- /* 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.Security;
- using System.Text;
-
- namespace Alphaleonis.Win32.Filesystem
- {
- public static partial class Path
- {
- #region .NET
-
- /// <summary>Returns the absolute path for the specified path string.</summary>
- /// <returns>The fully qualified location of path, such as "C:\MyFile.txt".</returns>
- /// <remarks>
- /// <para>GetFullPathName merges the name of the current drive and directory with a specified file name to determine the full path and file name of a specified file.</para>
- /// <para>It also calculates the address of the file name portion of the full path and file name.</para>
- /// <para> </para>
- /// <para>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.</para>
- /// <para>The .NET Framework does not support direct access to physical disks through paths that are device names, such as "\\.\PHYSICALDRIVE0".</para>
- /// <para> </para>
- /// <para>MSDN: Multithreaded applications and shared library code should not use the GetFullPathName function and</para>
- /// <para>should avoid using relative path names. The current directory state written by the SetCurrentDirectory function is stored as a global variable in each process,</para>
- /// <para>therefore multithreaded applications cannot reliably use this value without possible data corruption from other threads that may also be reading or setting this value.</para>
- /// <para>This limitation also applies to the SetCurrentDirectory and GetCurrentDirectory functions. The exception being when the application is guaranteed to be running in a single thread,</para>
- /// <para>for example parsing file names from the command line argument string in the main thread prior to creating any additional threads.</para>
- /// <para>Using relative path names in multithreaded applications or shared library code can yield unpredictable results and is not supported.</para>
- /// </remarks>
- /// <exception cref="ArgumentNullException"/>
- /// <exception cref="ArgumentException"/>
- /// <exception cref="NotSupportedException"/>
- /// <param name="path">The file or directory for which to obtain absolute path information.</param>
- [SecurityCritical]
- public static string GetFullPath(string path)
- {
- return GetFullPathTackleCore(null, path, GetFullPathOptions.None);
- }
-
- #endregion // .NET
-
-
- #region AlphaFS
-
- /// <summary>Returns the absolute path for the specified path string.</summary>
- /// <returns>The fully qualified location of path, such as "C:\MyFile.txt".</returns>
- /// <remarks>
- /// <para>GetFullPathName merges the name of the current drive and directory with a specified file name to determine the full path and file name of a specified file.</para>
- /// <para>It also calculates the address of the file name portion of the full path and file name.</para>
- /// <para> </para>
- /// <para>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.</para>
- /// <para>The .NET Framework does not support direct access to physical disks through paths that are device names, such as "\\.\PHYSICALDRIVE0".</para>
- /// <para> </para>
- /// <para>MSDN: Multithreaded applications and shared library code should not use the GetFullPathName function and</para>
- /// <para>should avoid using relative path names. The current directory state written by the SetCurrentDirectory function is stored as a global variable in each process,</para>
- /// <para>therefore multithreaded applications cannot reliably use this value without possible data corruption from other threads that may also be reading or setting this value.</para>
- /// <para>This limitation also applies to the SetCurrentDirectory and GetCurrentDirectory functions. The exception being when the application is guaranteed to be running in a single thread,</para>
- /// <para>for example parsing file names from the command line argument string in the main thread prior to creating any additional threads.</para>
- /// <para>Using relative path names in multithreaded applications or shared library code can yield unpredictable results and is not supported.</para>
- /// </remarks>
- /// <exception cref="ArgumentNullException"/>
- /// <exception cref="ArgumentException"/>
- /// <exception cref="NotSupportedException"/>
- /// <param name="path">The file or directory for which to obtain absolute path information.</param>
- /// <param name="options">Options for controlling the full path retrieval.</param>
- [SecurityCritical]
- public static string GetFullPath(string path, GetFullPathOptions options)
- {
- return GetFullPathTackleCore(null, path, options);
- }
-
-
- /// <summary>[AlphaFS] Returns the absolute path for the specified path string.</summary>
- /// <returns>The fully qualified location of path, such as "C:\MyFile.txt".</returns>
- /// <remarks>
- /// <para>GetFullPathName merges the name of the current drive and directory with a specified file name to determine the full path and file name of a specified file.</para>
- /// <para>It also calculates the address of the file name portion of the full path and file name.</para>
- /// <para> </para>
- /// <para>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.</para>
- /// <para>The .NET Framework does not support direct access to physical disks through paths that are device names, such as "\\.\PHYSICALDRIVE0".</para>
- /// <para> </para>
- /// <para>MSDN: Multithreaded applications and shared library code should not use the GetFullPathName function and</para>
- /// <para>should avoid using relative path names. The current directory state written by the SetCurrentDirectory function is stored as a global variable in each process,</para>
- /// <para>therefore multithreaded applications cannot reliably use this value without possible data corruption from other threads that may also be reading or setting this value.</para>
- /// <para>This limitation also applies to the SetCurrentDirectory and GetCurrentDirectory functions. The exception being when the application is guaranteed to be running in a single thread,</para>
- /// <para>for example parsing file names from the command line argument string in the main thread prior to creating any additional threads.</para>
- /// <para>Using relative path names in multithreaded applications or shared library code can yield unpredictable results and is not supported.</para>
- /// </remarks>
- /// <exception cref="ArgumentException"/>
- /// <exception cref="ArgumentNullException"/>
- /// <exception cref="NotSupportedException"/>
- /// <param name="transaction">The transaction.</param>
- /// <param name="path">The file or directory for which to obtain absolute path information.</param>
- [SecurityCritical]
- public static string GetFullPathTransacted(KernelTransaction transaction, string path)
- {
- return GetFullPathTackleCore(transaction, path, GetFullPathOptions.None);
- }
-
- /// <summary>[AlphaFS] Returns the absolute path for the specified path string.</summary>
- /// <returns>The fully qualified location of path, such as "C:\MyFile.txt".</returns>
- /// <remarks>
- /// <para>GetFullPathName merges the name of the current drive and directory with a specified file name to determine the full path and file name of a specified file.</para>
- /// <para>It also calculates the address of the file name portion of the full path and file name.</para>
- /// <para> </para>
- /// <para>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.</para>
- /// <para>The .NET Framework does not support direct access to physical disks through paths that are device names, such as "\\.\PHYSICALDRIVE0".</para>
- /// <para> </para>
- /// <para>MSDN: Multithreaded applications and shared library code should not use the GetFullPathName function and</para>
- /// <para>should avoid using relative path names. The current directory state written by the SetCurrentDirectory function is stored as a global variable in each process,</para>
- /// <para>therefore multithreaded applications cannot reliably use this value without possible data corruption from other threads that may also be reading or setting this value.</para>
- /// <para>This limitation also applies to the SetCurrentDirectory and GetCurrentDirectory functions. The exception being when the application is guaranteed to be running in a single thread,</para>
- /// <para>for example parsing file names from the command line argument string in the main thread prior to creating any additional threads.</para>
- /// <para>Using relative path names in multithreaded applications or shared library code can yield unpredictable results and is not supported.</para>
- /// </remarks>
- /// <exception cref="ArgumentException"/>
- /// <exception cref="ArgumentNullException"/>
- /// <exception cref="NotSupportedException"/>
- /// <param name="transaction">The transaction.</param>
- /// <param name="path">The file or directory for which to obtain absolute path information.</param>
- /// <param name="options">Options for controlling the full path retrieval.</param>
- [SecurityCritical]
- public static string GetFullPathTransacted(KernelTransaction transaction, string path, GetFullPathOptions options)
- {
- return GetFullPathTackleCore(transaction, path, options);
- }
-
- #endregion // AlphaFS
-
- #region Internal Methods
-
- /// <summary>Retrieves the absolute path for the specified <paramref name="path"/> string.</summary>
- /// <returns>The fully qualified location of <paramref name="path"/>, such as "C:\MyFile.txt".</returns>
- /// <remarks>
- /// <para>GetFullPathName merges the name of the current drive and directory with a specified file name to determine the full path and file name of a specified file.</para>
- /// <para>It also calculates the address of the file name portion of the full path and file name.</para>
- /// <para> </para>
- /// <para>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.</para>
- /// <para>The .NET Framework does not support direct access to physical disks through paths that are device names, such as "\\.\PHYSICALDRIVE0".</para>
- /// <para> </para>
- /// <para>MSDN: Multithreaded applications and shared library code should not use the GetFullPathName function and</para>
- /// <para>should avoid using relative path names. The current directory state written by the SetCurrentDirectory function is stored as a global variable in each process,</para>
- /// <para>therefore multithreaded applications cannot reliably use this value without possible data corruption from other threads that may also be reading or setting this value.</para>
- /// <para>This limitation also applies to the SetCurrentDirectory and GetCurrentDirectory functions. The exception being when the application is guaranteed to be running in a single thread,</para>
- /// <para>for example parsing file names from the command line argument string in the main thread prior to creating any additional threads.</para>
- /// <para>Using relative path names in multithreaded applications or shared library code can yield unpredictable results and is not supported.</para>
- /// </remarks>
- /// <exception cref="ArgumentException"/>
- /// <exception cref="ArgumentNullException"/>
- /// <param name="transaction">The transaction.</param>
- /// <param name="path">The file or directory for which to obtain absolute path information.</param>
- /// <param name="options">Options for controlling the full path retrieval.</param>
- [SecurityCritical]
- 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)
- {
- 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.
-
- if (path == null || path.Length <= 3 || (!path.StartsWith(LongPathPrefix, StringComparison.OrdinalIgnoreCase) && 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 = (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);
- }
-
-
- 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(".", StringComparison.OrdinalIgnoreCase))
- finalFullPath += ".";
-
- 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);
- }
-
- /// <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)
- 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, false);
-
- 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;
- }
-
- #endregion // Internal Methods
- }
- }
|