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.
 
 

228 lines
11 KiB

  1. /* Copyright (C) 2008-2016 Peter Palotas, Jeffrey Jangli, Alexandr Normuradov
  2. *
  3. * Permission is hereby granted, free of charge, to any person obtaining a copy
  4. * of this software and associated documentation files (the "Software"), to deal
  5. * in the Software without restriction, including without limitation the rights
  6. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. * copies of the Software, and to permit persons to whom the Software is
  8. * furnished to do so, subject to the following conditions:
  9. *
  10. * The above copyright notice and this permission notice shall be included in
  11. * all copies or substantial portions of the Software.
  12. *
  13. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. * THE SOFTWARE.
  20. */
  21. using System;
  22. using System.Diagnostics.CodeAnalysis;
  23. using System.IO;
  24. using System.Runtime.InteropServices;
  25. using System.Security;
  26. namespace Alphaleonis.Win32.Filesystem
  27. {
  28. public static partial class File
  29. {
  30. #region GetAttributes
  31. /// <summary>Gets the <see cref="FileAttributes"/> of the file on the path.</summary>
  32. /// <param name="path">The path to the file.</param>
  33. /// <returns>The <see cref="FileAttributes"/> of the file on the path.</returns>
  34. [SecurityCritical]
  35. public static FileAttributes GetAttributes(string path)
  36. {
  37. return GetAttributesExCore<FileAttributes>(null, path, PathFormat.RelativePath, true);
  38. }
  39. /// <summary>[AlphaFS] Gets the <see cref="FileAttributes"/> of the file on the path.</summary>
  40. /// <param name="path">The path to the file.</param>
  41. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  42. /// <returns>The <see cref="FileAttributes"/> of the file on the path.</returns>
  43. [SecurityCritical]
  44. public static FileAttributes GetAttributes(string path, PathFormat pathFormat)
  45. {
  46. return GetAttributesExCore<FileAttributes>(null, path, pathFormat, true);
  47. }
  48. /// <summary>[AlphaFS] Gets the <see cref="FileAttributes"/> of the file on the path.</summary>
  49. /// <param name="transaction">The transaction.</param>
  50. /// <param name="path">The path to the file.</param>
  51. /// <returns>The <see cref="FileAttributes"/> of the file on the path.</returns>
  52. [SecurityCritical]
  53. public static FileAttributes GetAttributesTransacted(KernelTransaction transaction, string path)
  54. {
  55. return GetAttributesExCore<FileAttributes>(transaction, path, PathFormat.RelativePath, true);
  56. }
  57. /// <summary>[AlphaFS] Gets the <see cref="FileAttributes"/> of the file on the path.</summary>
  58. /// <param name="transaction">The transaction.</param>
  59. /// <param name="path">The path to the file.</param>
  60. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  61. /// <returns>The <see cref="FileAttributes"/> of the file on the path.</returns>
  62. [SecurityCritical]
  63. public static FileAttributes GetAttributesTransacted(KernelTransaction transaction, string path, PathFormat pathFormat)
  64. {
  65. return GetAttributesExCore<FileAttributes>(transaction, path, pathFormat, true);
  66. }
  67. #endregion
  68. #region Internal Methods
  69. /// <summary>Gets the <see cref="FileAttributes"/> or <see cref="NativeMethods.WIN32_FILE_ATTRIBUTE_DATA"/> of the specified file or directory.</summary>
  70. /// <returns>The <see cref="FileAttributes"/> or <see cref="NativeMethods.WIN32_FILE_ATTRIBUTE_DATA"/> of the specified file or directory.</returns>
  71. /// <exception cref="ArgumentException"/>
  72. /// <exception cref="NotSupportedException"/>
  73. /// <typeparam name="T">Generic type parameter.</typeparam>
  74. /// <param name="transaction">The transaction.</param>
  75. /// <param name="path">The path to the file or directory.</param>
  76. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  77. /// <param name="returnErrorOnNotFound"></param>
  78. [SuppressMessage("Microsoft.Interoperability", "CA1404:CallGetLastErrorImmediatelyAfterPInvoke", Justification = "Marshal.GetLastWin32Error() is manipulated.")]
  79. [SecurityCritical]
  80. internal static T GetAttributesExCore<T>(KernelTransaction transaction, string path, PathFormat pathFormat, bool returnErrorOnNotFound)
  81. {
  82. if (pathFormat == PathFormat.RelativePath)
  83. Path.CheckSupportedPathFormat(path, true, true);
  84. string pathLp = Path.GetExtendedLengthPathCore(transaction, path, pathFormat, GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.CheckInvalidPathChars);
  85. var data = new NativeMethods.WIN32_FILE_ATTRIBUTE_DATA();
  86. int dataInitialised = FillAttributeInfoCore(transaction, pathLp, ref data, false, returnErrorOnNotFound);
  87. if (dataInitialised != Win32Errors.ERROR_SUCCESS)
  88. NativeError.ThrowException(dataInitialised, pathLp);
  89. return (T) (typeof (T) == typeof (FileAttributes) ? (object) data.dwFileAttributes : data);
  90. }
  91. /// <summary>
  92. /// Calls NativeMethods.GetFileAttributesEx to retrieve WIN32_FILE_ATTRIBUTE_DATA.
  93. /// <para>Note that classes should use -1 as the uninitialized state for dataInitialized when relying on this method.</para>
  94. /// </summary>
  95. /// <remarks>No path (null, empty string) checking or normalization is performed.</remarks>
  96. /// <param name="transaction">.</param>
  97. /// <param name="pathLp">.</param>
  98. /// <param name="win32AttrData">[in,out].</param>
  99. /// <param name="tryagain">.</param>
  100. /// <param name="returnErrorOnNotFound">.</param>
  101. /// <returns>0 on success, otherwise a Win32 error code.</returns>
  102. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
  103. [SecurityCritical]
  104. internal static int FillAttributeInfoCore(KernelTransaction transaction, string pathLp, ref NativeMethods.WIN32_FILE_ATTRIBUTE_DATA win32AttrData, bool tryagain, bool returnErrorOnNotFound)
  105. {
  106. int dataInitialised = (int)Win32Errors.ERROR_SUCCESS;
  107. #region Try Again
  108. // Someone has a handle to the file open, or other error.
  109. if (tryagain)
  110. {
  111. NativeMethods.WIN32_FIND_DATA findData;
  112. using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors))
  113. {
  114. bool error = false;
  115. SafeFindFileHandle handle = transaction == null || !NativeMethods.IsAtLeastWindowsVista
  116. // FindFirstFileEx() / FindFirstFileTransacted()
  117. // In the ANSI version of this function, the name is limited to MAX_PATH characters.
  118. // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path.
  119. // 2013-01-13: MSDN confirms LongPath usage.
  120. // A trailing backslash is not allowed.
  121. ? NativeMethods.FindFirstFileEx(Path.RemoveTrailingDirectorySeparator(pathLp, false), NativeMethods.FindexInfoLevels, out findData, NativeMethods.FINDEX_SEARCH_OPS.SearchNameMatch, IntPtr.Zero, NativeMethods.LargeCache)
  122. : NativeMethods.FindFirstFileTransacted(Path.RemoveTrailingDirectorySeparator(pathLp, false), NativeMethods.FindexInfoLevels, out findData, NativeMethods.FINDEX_SEARCH_OPS.SearchNameMatch, IntPtr.Zero, NativeMethods.LargeCache, transaction.SafeHandle);
  123. try
  124. {
  125. if (handle.IsInvalid)
  126. {
  127. error = true;
  128. dataInitialised = Marshal.GetLastWin32Error();
  129. if (dataInitialised == Win32Errors.ERROR_FILE_NOT_FOUND ||
  130. dataInitialised == Win32Errors.ERROR_PATH_NOT_FOUND ||
  131. dataInitialised == Win32Errors.ERROR_NOT_READY) // Floppy device not ready.
  132. {
  133. if (!returnErrorOnNotFound)
  134. {
  135. // Return default value for backward compatibility
  136. dataInitialised = (int)Win32Errors.ERROR_SUCCESS;
  137. win32AttrData.dwFileAttributes = (FileAttributes)(-1);
  138. }
  139. }
  140. return dataInitialised;
  141. }
  142. }
  143. finally
  144. {
  145. try
  146. {
  147. if (handle != null)
  148. handle.Close();
  149. }
  150. catch
  151. {
  152. // If we're already returning an error, don't throw another one.
  153. if (!error)
  154. NativeError.ThrowException(dataInitialised, pathLp);
  155. }
  156. }
  157. }
  158. // Copy the attribute information.
  159. win32AttrData = new NativeMethods.WIN32_FILE_ATTRIBUTE_DATA(findData);
  160. }
  161. #endregion // Try Again
  162. else
  163. {
  164. using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors))
  165. {
  166. if (!(transaction == null || !NativeMethods.IsAtLeastWindowsVista
  167. // GetFileAttributesEx() / GetFileAttributesTransacted()
  168. // In the ANSI version of this function, the name is limited to MAX_PATH characters.
  169. // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path.
  170. // 2013-01-13: MSDN confirms LongPath usage.
  171. ? NativeMethods.GetFileAttributesEx(pathLp, NativeMethods.GetFileExInfoLevels.GetFileExInfoStandard, out win32AttrData)
  172. : NativeMethods.GetFileAttributesTransacted(pathLp, NativeMethods.GetFileExInfoLevels.GetFileExInfoStandard, out win32AttrData, transaction.SafeHandle)))
  173. {
  174. dataInitialised = Marshal.GetLastWin32Error();
  175. if (dataInitialised != Win32Errors.ERROR_FILE_NOT_FOUND &&
  176. dataInitialised != Win32Errors.ERROR_PATH_NOT_FOUND &&
  177. dataInitialised != Win32Errors.ERROR_NOT_READY) // Floppy device not ready.
  178. {
  179. // In case someone latched onto the file. Take the perf hit only for failure.
  180. return FillAttributeInfoCore(transaction, pathLp, ref win32AttrData, true, returnErrorOnNotFound);
  181. }
  182. if (!returnErrorOnNotFound)
  183. {
  184. // Return default value for backward compbatibility.
  185. dataInitialised = (int)Win32Errors.ERROR_SUCCESS;
  186. win32AttrData.dwFileAttributes = (FileAttributes)(-1);
  187. }
  188. }
  189. }
  190. }
  191. return dataInitialised;
  192. }
  193. #endregion // Internal Methods
  194. }
  195. }