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.
 
 

231 lines
14 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 Microsoft.Win32.SafeHandles;
  22. using System;
  23. using System.Collections.Generic;
  24. using System.IO;
  25. using System.Runtime.InteropServices;
  26. using System.Security;
  27. using System.Security.AccessControl;
  28. namespace Alphaleonis.Win32.Filesystem
  29. {
  30. partial class Directory
  31. {
  32. /// <summary>[AlphaFS] Retrieves information about files in the directory specified by <paramref name="path"/> in <see cref="FileShare.ReadWrite"/> mode.</summary>
  33. /// <returns>An enumeration of <see cref="FileIdBothDirectoryInfo"/> records for each file system entry in the specified diretory.</returns>
  34. /// <param name="path">A path to a directory from which to retrieve information.</param>
  35. [SecurityCritical]
  36. public static IEnumerable<FileIdBothDirectoryInfo> EnumerateFileIdBothDirectoryInfo(string path)
  37. {
  38. return EnumerateFileIdBothDirectoryInfoCore(null, null, path, FileShare.ReadWrite, false, PathFormat.RelativePath);
  39. }
  40. /// <summary>[AlphaFS] Retrieves information about files in the directory specified by <paramref name="path"/> in <see cref="FileShare.ReadWrite"/> mode.</summary>
  41. /// <returns>An enumeration of <see cref="FileIdBothDirectoryInfo"/> records for each file system entry in the specified diretory.</returns>
  42. /// <param name="path">A path to a directory from which to retrieve information.</param>
  43. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  44. [SecurityCritical]
  45. public static IEnumerable<FileIdBothDirectoryInfo> EnumerateFileIdBothDirectoryInfo(string path, PathFormat pathFormat)
  46. {
  47. return EnumerateFileIdBothDirectoryInfoCore(null, null, path, FileShare.ReadWrite, false, pathFormat);
  48. }
  49. /// <summary>[AlphaFS] Retrieves information about files in the directory specified by <paramref name="path"/> in specified <see cref="FileShare"/> mode.</summary>
  50. /// <returns>An enumeration of <see cref="FileIdBothDirectoryInfo"/> records for each file system entry in the specified diretory.</returns>
  51. /// <param name="path">A path to a directory from which to retrieve information.</param>
  52. /// <param name="shareMode">The <see cref="FileShare"/> mode with which to open a handle to the directory.</param>
  53. [SecurityCritical]
  54. public static IEnumerable<FileIdBothDirectoryInfo> EnumerateFileIdBothDirectoryInfo(string path, FileShare shareMode)
  55. {
  56. return EnumerateFileIdBothDirectoryInfoCore(null, null, path, shareMode, false, PathFormat.RelativePath);
  57. }
  58. /// <summary>[AlphaFS] Retrieves information about files in the directory specified by <paramref name="path"/> in specified <see cref="FileShare"/> mode.</summary>
  59. /// <returns>An enumeration of <see cref="FileIdBothDirectoryInfo"/> records for each file system entry in the specified diretory.</returns>
  60. /// <param name="path">A path to a directory from which to retrieve information.</param>
  61. /// <param name="shareMode">The <see cref="FileShare"/> mode with which to open a handle to the directory.</param>
  62. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  63. [SecurityCritical]
  64. public static IEnumerable<FileIdBothDirectoryInfo> EnumerateFileIdBothDirectoryInfo(string path, FileShare shareMode, PathFormat pathFormat)
  65. {
  66. return EnumerateFileIdBothDirectoryInfoCore(null, null, path, shareMode, false, pathFormat);
  67. }
  68. /// <summary>[AlphaFS] Retrieves information about files in the directory handle specified.</summary>
  69. /// <returns>An IEnumerable of <see cref="FileIdBothDirectoryInfo"/> records for each file system entry in the specified diretory.</returns>
  70. /// <param name="handle">An open handle to the directory from which to retrieve information.</param>
  71. [SecurityCritical]
  72. public static IEnumerable<FileIdBothDirectoryInfo> EnumerateFileIdBothDirectoryInfo(SafeFileHandle handle)
  73. {
  74. // FileShare has no effect since a handle is already opened.
  75. return EnumerateFileIdBothDirectoryInfoCore(null, handle, null, FileShare.ReadWrite, false, PathFormat.RelativePath);
  76. }
  77. #region Transactional
  78. /// <summary>[AlphaFS] Retrieves information about files in the directory specified by <paramref name="path"/> in <see cref="FileShare.ReadWrite"/> mode.</summary>
  79. /// <returns>An enumeration of <see cref="FileIdBothDirectoryInfo"/> records for each file system entry in the specified diretory.</returns>
  80. /// <param name="transaction">The transaction.</param>
  81. /// <param name="path">A path to a directory from which to retrieve information.</param>
  82. [SecurityCritical]
  83. public static IEnumerable<FileIdBothDirectoryInfo> EnumerateFileIdBothDirectoryInfoTransacted(KernelTransaction transaction, string path)
  84. {
  85. return EnumerateFileIdBothDirectoryInfoCore(transaction, null, path, FileShare.ReadWrite, false, PathFormat.RelativePath);
  86. }
  87. /// <summary>[AlphaFS] Retrieves information about files in the directory specified by <paramref name="path"/> in <see cref="FileShare.ReadWrite"/> mode.</summary>
  88. /// <returns>An enumeration of <see cref="FileIdBothDirectoryInfo"/> records for each file system entry in the specified diretory.</returns>
  89. /// <param name="transaction">The transaction.</param>
  90. /// <param name="path">A path to a directory from which to retrieve information.</param>
  91. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  92. [SecurityCritical]
  93. public static IEnumerable<FileIdBothDirectoryInfo> EnumerateFileIdBothDirectoryInfoTransacted(KernelTransaction transaction, string path, PathFormat pathFormat)
  94. {
  95. return EnumerateFileIdBothDirectoryInfoCore(transaction, null, path, FileShare.ReadWrite, false, pathFormat);
  96. }
  97. /// <summary>[AlphaFS] Retrieves information about files in the directory specified by <paramref name="path"/> in specified <see cref="FileShare"/> mode.</summary>
  98. /// <returns>An enumeration of <see cref="FileIdBothDirectoryInfo"/> records for each file system entry in the specified diretory.</returns>
  99. /// <param name="transaction">The transaction.</param>
  100. /// <param name="path">A path to a directory from which to retrieve information.</param>
  101. /// <param name="shareMode">The <see cref="FileShare"/> mode with which to open a handle to the directory.</param>
  102. [SecurityCritical]
  103. public static IEnumerable<FileIdBothDirectoryInfo> EnumerateFileIdBothDirectoryInfoTransacted(KernelTransaction transaction, string path, FileShare shareMode)
  104. {
  105. return EnumerateFileIdBothDirectoryInfoCore(transaction, null, path, shareMode, false, PathFormat.RelativePath);
  106. }
  107. /// <summary>[AlphaFS] Retrieves information about files in the directory specified by <paramref name="path"/> in specified <see cref="FileShare"/> mode.</summary>
  108. /// <returns>An enumeration of <see cref="FileIdBothDirectoryInfo"/> records for each file system entry in the specified diretory.</returns>
  109. /// <param name="transaction">The transaction.</param>
  110. /// <param name="path">A path to a directory from which to retrieve information.</param>
  111. /// <param name="shareMode">The <see cref="FileShare"/> mode with which to open a handle to the directory.</param>
  112. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  113. [SecurityCritical]
  114. public static IEnumerable<FileIdBothDirectoryInfo> EnumerateFileIdBothDirectoryInfoTransacted(KernelTransaction transaction, string path, FileShare shareMode, PathFormat pathFormat)
  115. {
  116. return EnumerateFileIdBothDirectoryInfoCore(transaction, null, path, shareMode, false, pathFormat);
  117. }
  118. #endregion // Transactional
  119. #region Internal Methods
  120. /// <summary>Returns an enumerable collection of information about files in the directory handle specified.</summary>
  121. /// <returns>An IEnumerable of <see cref="FileIdBothDirectoryInfo"/> records for each file system entry in the specified diretory.</returns>
  122. /// <remarks>
  123. /// <para>Either use <paramref name="path"/> or <paramref name="safeHandle"/>, not both.</para>
  124. /// <para>
  125. /// The number of files that are returned for each call to GetFileInformationByHandleEx depends on the size of the buffer that is passed to the function.
  126. /// Any subsequent calls to GetFileInformationByHandleEx on the same handle will resume the enumeration operation after the last file is returned.
  127. /// </para>
  128. /// </remarks>
  129. /// <param name="transaction">The transaction.</param>
  130. /// <param name="safeHandle">An open handle to the directory from which to retrieve information.</param>
  131. /// <param name="path">A path to the directory.</param>
  132. /// <param name="shareMode">The <see cref="FileShare"/> mode with which to open a handle to the directory.</param>
  133. /// <param name="continueOnException"><see langword="true"/> suppress any Exception that might be thrown as a result from a failure, such as ACLs protected directories or non-accessible reparse points.</param>
  134. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  135. [SecurityCritical]
  136. internal static IEnumerable<FileIdBothDirectoryInfo> EnumerateFileIdBothDirectoryInfoCore(KernelTransaction transaction, SafeFileHandle safeHandle, string path, FileShare shareMode, bool continueOnException, PathFormat pathFormat)
  137. {
  138. if (!NativeMethods.IsAtLeastWindowsVista)
  139. throw new PlatformNotSupportedException(Resources.Requires_Windows_Vista_Or_Higher);
  140. bool callerHandle = safeHandle != null;
  141. if (!callerHandle)
  142. {
  143. if (Utils.IsNullOrWhiteSpace(path))
  144. throw new ArgumentNullException("path");
  145. string pathLp = Path.GetExtendedLengthPathCore(transaction, path, pathFormat, GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.FullCheck);
  146. safeHandle = File.CreateFileCore(transaction, pathLp, ExtendedFileAttributes.BackupSemantics, null, FileMode.Open, FileSystemRights.ReadData, shareMode, true, PathFormat.LongFullPath);
  147. }
  148. try
  149. {
  150. if (!NativeMethods.IsValidHandle(safeHandle, Marshal.GetLastWin32Error(), !continueOnException))
  151. yield break;
  152. var fileNameOffset = (int)Marshal.OffsetOf(typeof(NativeMethods.FILE_ID_BOTH_DIR_INFO), "FileName");
  153. using (var safeBuffer = new SafeGlobalMemoryBufferHandle(NativeMethods.DefaultFileBufferSize))
  154. {
  155. while (true)
  156. {
  157. if (!NativeMethods.GetFileInformationByHandleEx(safeHandle, NativeMethods.FileInfoByHandleClass.FileIdBothDirectoryInfo, safeBuffer, (uint)safeBuffer.Capacity))
  158. {
  159. uint lastError = (uint)Marshal.GetLastWin32Error();
  160. switch (lastError)
  161. {
  162. case Win32Errors.ERROR_SUCCESS:
  163. case Win32Errors.ERROR_NO_MORE_FILES:
  164. case Win32Errors.ERROR_HANDLE_EOF:
  165. yield break;
  166. case Win32Errors.ERROR_MORE_DATA:
  167. continue;
  168. default:
  169. NativeError.ThrowException(lastError, path);
  170. yield break; // we should never get to this yield break.
  171. }
  172. }
  173. int offset = 0;
  174. NativeMethods.FILE_ID_BOTH_DIR_INFO fibdi;
  175. do
  176. {
  177. fibdi = safeBuffer.PtrToStructure<NativeMethods.FILE_ID_BOTH_DIR_INFO>(offset);
  178. string fileName = safeBuffer.PtrToStringUni(offset + fileNameOffset, (int)(fibdi.FileNameLength / 2));
  179. if (!fileName.Equals(Path.CurrentDirectoryPrefix, StringComparison.OrdinalIgnoreCase) &&
  180. !fileName.Equals(Path.ParentDirectoryPrefix, StringComparison.OrdinalIgnoreCase))
  181. yield return new FileIdBothDirectoryInfo(fibdi, fileName);
  182. offset += fibdi.NextEntryOffset;
  183. }
  184. while (fibdi.NextEntryOffset != 0);
  185. }
  186. }
  187. }
  188. finally
  189. {
  190. // Handle is ours, dispose.
  191. if (!callerHandle && safeHandle != null)
  192. safeHandle.Close();
  193. }
  194. }
  195. #endregion // Internal Methods
  196. }
  197. }