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.
 
 

428 lines
24 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 Alphaleonis.Win32.Security;
  22. using Microsoft.Win32.SafeHandles;
  23. using System;
  24. using System.Diagnostics.CodeAnalysis;
  25. using System.IO;
  26. using System.Runtime.InteropServices;
  27. using System.Security;
  28. using System.Security.AccessControl;
  29. using FileStream = System.IO.FileStream;
  30. namespace Alphaleonis.Win32.Filesystem
  31. {
  32. public static partial class File
  33. {
  34. #region Non-Transactional
  35. /// <summary>Creates or overwrites a file in the specified path.</summary>
  36. /// <param name="path">The path and name of the file to create.</param>
  37. /// <returns>A <see cref="FileStream"/> that provides read/write access to the file specified in <paramref name="path"/>.</returns>
  38. [SecurityCritical]
  39. public static FileStream Create(string path)
  40. {
  41. return CreateFileStreamCore(null, path, ExtendedFileAttributes.Normal, null, FileMode.Create, FileAccess.ReadWrite, FileShare.None, NativeMethods.DefaultFileBufferSize, PathFormat.RelativePath);
  42. }
  43. /// <summary>Creates or overwrites the specified file.</summary>
  44. /// <param name="path">The name of the file.</param>
  45. /// <param name="bufferSize">The number of bytes buffered for reads and writes to the file.</param>
  46. /// <returns>
  47. /// A <see cref="FileStream"/> with the specified buffer size that provides read/write access to the file specified in
  48. /// <paramref name="path"/>.
  49. /// </returns>
  50. [SecurityCritical]
  51. public static FileStream Create(string path, int bufferSize)
  52. {
  53. return CreateFileStreamCore(null, path, ExtendedFileAttributes.Normal, null, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize, PathFormat.RelativePath);
  54. }
  55. /// <summary>
  56. /// Creates or overwrites the specified file, specifying a buffer size and a
  57. /// <see cref="FileOptions"/> value that describes how to create or overwrite the file.
  58. /// </summary>
  59. /// <param name="path">The name of the file.</param>
  60. /// <param name="bufferSize">The number of bytes buffered for reads and writes to the file.</param>
  61. /// <param name="options">One of the <see cref="FileOptions"/> values that describes how to create or overwrite the file.</param>
  62. /// <returns>A new file with the specified buffer size.</returns>
  63. [SecurityCritical]
  64. public static FileStream Create(string path, int bufferSize, FileOptions options)
  65. {
  66. return CreateFileStreamCore(null, path, (ExtendedFileAttributes) options, null, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize, PathFormat.RelativePath);
  67. }
  68. /// <summary>
  69. /// Creates or overwrites the specified file, specifying a buffer size and a
  70. /// <see cref="FileOptions"/> value that describes how to create or overwrite the file.
  71. /// </summary>
  72. /// <param name="path">The name of the file.</param>
  73. /// <param name="bufferSize">The number of bytes buffered for reads and writes to the file.</param>
  74. /// <param name="options">One of the <see cref="FileOptions"/> values that describes how to create or overwrite the file.</param>
  75. /// <param name="fileSecurity">
  76. /// One of the <see cref="FileSecurity"/> values that determines the access control and audit security for the file.
  77. /// </param>
  78. /// <returns>A new file with the specified buffer size, file options, and file security.</returns>
  79. [SecurityCritical]
  80. public static FileStream Create(string path, int bufferSize, FileOptions options, FileSecurity fileSecurity)
  81. {
  82. return CreateFileStreamCore(null, path, (ExtendedFileAttributes)options, fileSecurity, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize, PathFormat.RelativePath);
  83. }
  84. /// <summary>Creates or overwrites a file in the specified path.</summary>
  85. /// <param name="path">The path and name of the file to create.</param>
  86. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  87. /// <returns>
  88. /// A <see cref="FileStream"/> that provides read/write access to the file specified in
  89. /// <paramref name="path"/>.
  90. /// </returns>
  91. [SecurityCritical]
  92. public static FileStream Create(string path, PathFormat pathFormat)
  93. {
  94. return CreateFileStreamCore(null, path, ExtendedFileAttributes.Normal, null, FileMode.Create, FileAccess.ReadWrite, FileShare.None, NativeMethods.DefaultFileBufferSize, pathFormat);
  95. }
  96. /// <summary>Creates or overwrites the specified file.</summary>
  97. /// <param name="path">The name of the file.</param>
  98. /// <param name="bufferSize">The number of bytes buffered for reads and writes to the file.</param>
  99. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  100. /// <returns>
  101. /// A <see cref="FileStream"/> with the specified buffer size that provides read/write access to the file specified in
  102. /// <paramref name="path"/>.
  103. /// </returns>
  104. [SecurityCritical]
  105. public static FileStream Create(string path, int bufferSize, PathFormat pathFormat)
  106. {
  107. return CreateFileStreamCore(null, path, ExtendedFileAttributes.Normal, null, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize, pathFormat);
  108. }
  109. /// <summary>
  110. /// Creates or overwrites the specified file, specifying a buffer size and a
  111. /// <see cref="FileOptions"/> value that describes how to create or overwrite the file.
  112. /// </summary>
  113. /// <param name="path">The name of the file.</param>
  114. /// <param name="bufferSize">
  115. /// The number of bytes buffered for reads and writes to the file.
  116. /// </param>
  117. /// <param name="options">
  118. /// One of the <see cref="FileOptions"/> values that describes how to create or overwrite the
  119. /// file.
  120. /// </param>
  121. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  122. /// <returns>A new file with the specified buffer size.</returns>
  123. [SecurityCritical]
  124. public static FileStream Create(string path, int bufferSize, FileOptions options, PathFormat pathFormat)
  125. {
  126. return CreateFileStreamCore(null, path, (ExtendedFileAttributes)options, null, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize, pathFormat);
  127. }
  128. /// <summary>
  129. /// Creates or overwrites the specified file, specifying a buffer size and a
  130. /// <see cref="FileOptions"/> value that describes how to create or overwrite the file.
  131. /// </summary>
  132. /// <param name="path">The name of the file.</param>
  133. /// <param name="bufferSize">
  134. /// The number of bytes buffered for reads and writes to the file.
  135. /// </param>
  136. /// <param name="options">
  137. /// One of the <see cref="FileOptions"/> values that describes how to create or overwrite the
  138. /// file.
  139. /// </param>
  140. /// <param name="fileSecurity">
  141. /// One of the <see cref="FileSecurity"/> values that determines the access control and audit
  142. /// security for the file.
  143. /// </param>
  144. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  145. /// <returns>
  146. /// A new file with the specified buffer size, file options, and file security.
  147. /// </returns>
  148. [SecurityCritical]
  149. public static FileStream Create(string path, int bufferSize, FileOptions options, FileSecurity fileSecurity, PathFormat pathFormat)
  150. {
  151. return CreateFileStreamCore(null, path, (ExtendedFileAttributes)options, fileSecurity, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize, pathFormat);
  152. }
  153. #endregion // .NET
  154. #region Transactional
  155. /// <summary>Creates or overwrites a file in the specified path.</summary>
  156. /// <param name="transaction">The transaction.</param>
  157. /// <param name="path">The path and name of the file to create.</param>
  158. /// <returns>
  159. /// A <see cref="FileStream"/> that provides read/write access to the file specified in
  160. /// <paramref name="path"/>.
  161. /// </returns>
  162. [SecurityCritical]
  163. public static FileStream CreateTransacted(KernelTransaction transaction, string path)
  164. {
  165. return CreateFileStreamCore(transaction, path, ExtendedFileAttributes.Normal, null, FileMode.Create, FileAccess.ReadWrite, FileShare.None, NativeMethods.DefaultFileBufferSize, PathFormat.RelativePath);
  166. }
  167. /// <summary>Creates or overwrites the specified file.</summary>
  168. /// <param name="transaction">The transaction.</param>
  169. /// <param name="path">The name of the file.</param>
  170. /// <param name="bufferSize">
  171. /// The number of bytes buffered for reads and writes to the file.
  172. /// </param>
  173. /// <returns>
  174. /// A <see cref="FileStream"/> with the specified buffer size that provides read/write access
  175. /// to the file specified in <paramref name="path"/>.
  176. /// </returns>
  177. [SecurityCritical]
  178. public static FileStream CreateTransacted(KernelTransaction transaction, string path, int bufferSize)
  179. {
  180. return CreateFileStreamCore(transaction, path, ExtendedFileAttributes.Normal, null, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize, PathFormat.RelativePath);
  181. }
  182. /// <summary>
  183. /// Creates or overwrites the specified file, specifying a buffer size and a
  184. /// <see cref="FileOptions"/> value that describes how to create or overwrite the file.
  185. /// </summary>
  186. /// <param name="transaction">The transaction.</param>
  187. /// <param name="path">The name of the file.</param>
  188. /// <param name="bufferSize">The number of bytes buffered for reads and writes to the file.</param>
  189. /// <param name="options">One of the <see cref="FileOptions"/> values that describes how to create or overwrite the file.</param>
  190. /// <returns>A new file with the specified buffer size.</returns>
  191. [SecurityCritical]
  192. public static FileStream CreateTransacted(KernelTransaction transaction, string path, int bufferSize, FileOptions options)
  193. {
  194. return CreateFileStreamCore(transaction, path, (ExtendedFileAttributes)options, null, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize, PathFormat.RelativePath);
  195. }
  196. /// <summary>
  197. /// Creates or overwrites the specified file, specifying a buffer size and a
  198. /// <see cref="FileOptions"/> value that describes how to create or overwrite the file.
  199. /// </summary>
  200. /// <param name="transaction">The transaction.</param>
  201. /// <param name="path">The name of the file.</param>
  202. /// <param name="bufferSize">The number of bytes buffered for reads and writes to the file.</param>
  203. /// <param name="options">One of the <see cref="FileOptions"/> values that describes how to create or overwrite the file.</param>
  204. /// <param name="fileSecurity">
  205. /// One of the <see cref="FileSecurity"/> values that determines the access control and audit security for the file.
  206. /// </param>
  207. /// <returns>A new file with the specified buffer size, file options, and file security.</returns>
  208. [SecurityCritical]
  209. public static FileStream CreateTransacted(KernelTransaction transaction, string path, int bufferSize, FileOptions options, FileSecurity fileSecurity)
  210. {
  211. return CreateFileStreamCore(transaction, path, (ExtendedFileAttributes)options, fileSecurity, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize, PathFormat.RelativePath);
  212. }
  213. /// <summary>Creates or overwrites a file in the specified path.</summary>
  214. /// <param name="transaction">The transaction.</param>
  215. /// <param name="path">The path and name of the file to create.</param>
  216. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  217. /// <returns>
  218. /// A <see cref="FileStream"/> that provides read/write access to the file specified in
  219. /// <paramref name="path"/>.
  220. /// </returns>
  221. [SecurityCritical]
  222. public static FileStream CreateTransacted(KernelTransaction transaction, string path, PathFormat pathFormat)
  223. {
  224. return CreateFileStreamCore(transaction, path, ExtendedFileAttributes.Normal, null, FileMode.Create, FileAccess.ReadWrite, FileShare.None, NativeMethods.DefaultFileBufferSize, pathFormat);
  225. }
  226. /// <summary>Creates or overwrites the specified file.</summary>
  227. /// <param name="transaction">The transaction.</param>
  228. /// <param name="path">The name of the file.</param>
  229. /// <param name="bufferSize">The number of bytes buffered for reads and writes to the file.</param>
  230. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  231. /// <returns>
  232. /// A <see cref="FileStream"/> with the specified buffer size that provides read/write access to the file specified in
  233. /// <paramref name="path"/>.
  234. /// </returns>
  235. [SecurityCritical]
  236. public static FileStream CreateTransacted(KernelTransaction transaction, string path, int bufferSize, PathFormat pathFormat)
  237. {
  238. return CreateFileStreamCore(transaction, path, ExtendedFileAttributes.Normal, null, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize, pathFormat);
  239. }
  240. /// <summary>
  241. /// Creates or overwrites the specified file, specifying a buffer size and a
  242. /// <see cref="FileOptions"/> value that describes how to create or overwrite the file.
  243. /// </summary>
  244. /// <param name="transaction">The transaction.</param>
  245. /// <param name="path">The name of the file.</param>
  246. /// <param name="bufferSize">The number of bytes buffered for reads and writes to the file.</param>
  247. /// <param name="options">One of the <see cref="FileOptions"/> values that describes how to create or overwrite the file.</param>
  248. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  249. /// <returns>A new file with the specified buffer size.</returns>
  250. [SecurityCritical]
  251. public static FileStream CreateTransacted(KernelTransaction transaction, string path, int bufferSize, FileOptions options, PathFormat pathFormat)
  252. {
  253. return CreateFileStreamCore(transaction, path, (ExtendedFileAttributes)options, null, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize, pathFormat);
  254. }
  255. /// <summary>
  256. /// Creates or overwrites the specified file, specifying a buffer size and a
  257. /// <see cref="FileOptions"/> value that describes how to create or overwrite the file.
  258. /// </summary>
  259. /// <param name="transaction">The transaction.</param>
  260. /// <param name="path">The name of the file.</param>
  261. /// <param name="bufferSize">The number of bytes buffered for reads and writes to the file.</param>
  262. /// <param name="options">One of the <see cref="FileOptions"/> values that describes how to create or overwrite the file.</param>
  263. /// <param name="fileSecurity">
  264. /// One of the <see cref="FileSecurity"/> values that determines the access control and audit security for the file.
  265. /// </param>
  266. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  267. /// <returns>A new file with the specified buffer size, file options, and file security.</returns>
  268. [SecurityCritical]
  269. public static FileStream CreateTransacted(KernelTransaction transaction, string path, int bufferSize, FileOptions options, FileSecurity fileSecurity, PathFormat pathFormat)
  270. {
  271. return CreateFileStreamCore(transaction, path, (ExtendedFileAttributes)options, fileSecurity, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize, pathFormat);
  272. }
  273. #endregion
  274. #region Internal
  275. /// <summary>Creates or overwrites a file in the specified path.</summary>
  276. /// <param name="transaction">The transaction.</param>
  277. /// <param name="path">The name of the file.</param>
  278. /// <param name="attributes">The <see cref="ExtendedFileAttributes"/> additional advanced options to create a file.</param>
  279. /// <param name="fileSecurity">
  280. /// A <see cref="FileSecurity"/> instance that determines the access control and audit security for the file.
  281. /// </param>
  282. /// <param name="mode">The <see cref="FileMode"/> option gives you more precise control over how you want to create a file.</param>
  283. /// <param name="access">
  284. /// The <see cref="FileAccess"/> allow you additionally specify to default read/write capability - just write, bypassing any cache.
  285. /// </param>
  286. /// <param name="share">
  287. /// The <see cref="FileShare"/> option controls how you would like to share created file with other requesters.
  288. /// </param>
  289. /// <param name="pathFormat">Indicates the format of the <paramref name="path"/> parameter.</param>
  290. /// <param name="bufferSize">The number of bytes buffered for reads and writes to the file.</param>
  291. /// <returns>A <see cref="FileStream"/> that provides read/write access to the file specified in path.</returns>
  292. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "False positive")]
  293. [SecurityCritical]
  294. internal static FileStream CreateFileStreamCore(KernelTransaction transaction, string path, ExtendedFileAttributes attributes, FileSecurity fileSecurity, FileMode mode, FileAccess access, FileShare share, int bufferSize, PathFormat pathFormat)
  295. {
  296. SafeFileHandle safeHandle = null;
  297. try
  298. {
  299. safeHandle = CreateFileCore(transaction, path, attributes, fileSecurity, mode, (FileSystemRights) access, share, true, pathFormat);
  300. return new FileStream(safeHandle, access, bufferSize, (attributes & ExtendedFileAttributes.Overlapped) != 0);
  301. }
  302. catch
  303. {
  304. if (safeHandle != null)
  305. safeHandle.Dispose();
  306. throw;
  307. }
  308. }
  309. /// <summary>Creates or opens a file, directory or I/O device.</summary>
  310. /// <returns>A <see cref="SafeFileHandle"/> that provides read/write access to the file or directory specified by <paramref name="path"/>.</returns>
  311. /// <remarks>
  312. /// <para>To obtain a directory handle using CreateFile, specify the FILE_FLAG_BACKUP_SEMANTICS flag as part of dwFlagsAndAttributes.</para>
  313. /// <para>The most commonly used I/O devices are as follows: file, file stream, directory, physical disk, volume, console buffer, tape drive, communications resource, mailslot, and pipe.</para>
  314. /// </remarks>
  315. /// <exception cref="ArgumentException"/>
  316. /// <exception cref="ArgumentNullException"/>
  317. /// <exception cref="NotSupportedException"/>
  318. /// <param name="transaction">The transaction.</param>
  319. /// <param name="path">The path and name of the file or directory to create.</param>
  320. /// <param name="attributes">One of the <see cref="ExtendedFileAttributes"/> values that describes how to create or overwrite the file or directory.</param>
  321. /// <param name="fileSecurity">A <see cref="FileSecurity"/> instance that determines the access control and audit security for the file or directory.</param>
  322. /// <param name="fileMode">A <see cref="FileMode"/> constant that determines how to open or create the file or directory.</param>
  323. /// <param name="fileSystemRights">A <see cref="FileSystemRights"/> constant that determines the access rights to use when creating access and audit rules for the file or directory.</param>
  324. /// <param name="fileShare">A <see cref="FileShare"/> constant that determines how the file or directory will be shared by processes.</param>
  325. /// <param name="checkPath">.</param>
  326. /// <param name="pathFormat">Indicates the format of the <paramref name="path"/> parameter.</param>
  327. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Object needs to be disposed by caller.")]
  328. [SecurityCritical]
  329. internal static SafeFileHandle CreateFileCore(KernelTransaction transaction, string path, ExtendedFileAttributes attributes, FileSecurity fileSecurity, FileMode fileMode, FileSystemRights fileSystemRights, FileShare fileShare, bool checkPath, PathFormat pathFormat)
  330. {
  331. if (checkPath && pathFormat == PathFormat.RelativePath)
  332. Path.CheckSupportedPathFormat(path, true, true);
  333. // When isFile == null, we're working with a device.
  334. // When opening a VOLUME or removable media drive (for example, a floppy disk drive or flash memory thumb drive),
  335. // the path string should be the following form: "\\.\X:"
  336. // Do not use a trailing backslash (\), which indicates the root.
  337. var pathLp = Path.GetExtendedLengthPathCore(transaction, path, pathFormat, GetFullPathOptions.TrimEnd | GetFullPathOptions.RemoveTrailingDirectorySeparator);
  338. PrivilegeEnabler privilegeEnabler = null;
  339. var isAppend = fileMode == FileMode.Append;
  340. // CreateFileXxx() does not support FileMode.Append mode.
  341. if (isAppend)
  342. {
  343. fileMode = FileMode.OpenOrCreate;
  344. fileSystemRights &= FileSystemRights.AppendData; // Add right.
  345. }
  346. if (fileSecurity != null)
  347. fileSystemRights |= (FileSystemRights) 0x1000000; // Set right.
  348. // AccessSystemSecurity = 0x1000000 AccessSystemAcl access type.
  349. // MaximumAllowed = 0x2000000 MaximumAllowed access type.
  350. if ((fileSystemRights & (FileSystemRights) 0x1000000) != 0 ||
  351. (fileSystemRights & (FileSystemRights) 0x2000000) != 0)
  352. privilegeEnabler = new PrivilegeEnabler(Privilege.Security);
  353. using (privilegeEnabler)
  354. using (var securityAttributes = new Security.NativeMethods.SecurityAttributes(fileSecurity))
  355. {
  356. var handle = transaction == null || !NativeMethods.IsAtLeastWindowsVista
  357. // CreateFile() / CreateFileTransacted()
  358. // In the ANSI version of this function, the name is limited to MAX_PATH characters.
  359. // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path.
  360. // 2013-01-13: MSDN confirms LongPath usage.
  361. ? NativeMethods.CreateFile(pathLp, fileSystemRights, fileShare, securityAttributes, fileMode, attributes, IntPtr.Zero)
  362. : NativeMethods.CreateFileTransacted(pathLp, fileSystemRights, fileShare, securityAttributes, fileMode, attributes, IntPtr.Zero, transaction.SafeHandle, IntPtr.Zero, IntPtr.Zero);
  363. var lastError = Marshal.GetLastWin32Error();
  364. if (handle.IsInvalid)
  365. {
  366. handle.Close();
  367. NativeError.ThrowException(lastError, pathLp);
  368. }
  369. if (isAppend)
  370. {
  371. var stream = new FileStream(handle, FileAccess.Write, NativeMethods.DefaultFileBufferSize, (attributes & ExtendedFileAttributes.Overlapped) != 0);
  372. stream.Seek(0, SeekOrigin.End);
  373. }
  374. return handle;
  375. }
  376. }
  377. #endregion // CreateFileCore
  378. }
  379. }