mirror of https://github.com/raandree/NTFSSecurity
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
305 lines
18 KiB
305 lines
18 KiB
/* 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
|
|
}
|
|
}
|
|
|