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.
 
 

651 lines
38 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 SecurityNativeMethods = Alphaleonis.Win32.Security.NativeMethods;
  30. namespace Alphaleonis.Win32.Filesystem
  31. {
  32. /// <summary>The <see cref="BackupFileStream"/> provides access to data associated with a specific file or directory, including security information and alternative data streams, for backup and restore operations.</summary>
  33. /// <remarks>This class uses the <see href="http://msdn.microsoft.com/en-us/library/aa362509(VS.85).aspx">BackupRead</see>,
  34. /// <see href="http://msdn.microsoft.com/en-us/library/aa362510(VS.85).aspx">BackupSeek</see> and
  35. /// <see href="http://msdn.microsoft.com/en-us/library/aa362511(VS.85).aspx">BackupWrite</see> functions from the Win32 API to provide access to the file or directory.
  36. /// </remarks>
  37. public sealed class BackupFileStream : Stream
  38. {
  39. #region Private Fields
  40. private readonly bool _canRead;
  41. private readonly bool _canWrite;
  42. private readonly bool _processSecurity;
  43. [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]
  44. private IntPtr _context = IntPtr.Zero;
  45. #endregion // Private Fields
  46. #region Construction and Destruction
  47. /// <summary>Initializes a new instance of the <see cref="BackupFileStream"/> class with the specified path and creation mode.</summary>
  48. /// <param name="path">A relative or absolute path for the file that the current <see cref="BackupFileStream"/> object will encapsulate.</param>
  49. /// <param name="mode">A <see cref="FileMode"/> constant that determines how to open or create the file.</param>
  50. /// <remarks>The file will be opened for exclusive access for both reading and writing.</remarks>
  51. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
  52. [SecurityCritical]
  53. public BackupFileStream(string path, FileMode mode)
  54. : this(File.CreateFileCore(null, path, ExtendedFileAttributes.Normal, null, mode, FileSystemRights.Read | FileSystemRights.Write, FileShare.None, true, PathFormat.RelativePath), FileSystemRights.Read | FileSystemRights.Write)
  55. {
  56. }
  57. /// <summary>Initializes a new instance of the <see cref="BackupFileStream"/> class with the specified path, creation mode and access rights.</summary>
  58. /// <param name="path">A relative or absolute path for the file that the current <see cref="BackupFileStream"/> object will encapsulate.</param>
  59. /// <param name="mode">A <see cref="FileMode"/> constant that determines how to open or create the file.</param>
  60. /// <param name="access">A <see cref="FileSystemRights"/> constant that determines the access rights to use when creating access and audit rules for the file.</param>
  61. /// <remarks>The file will be opened for exclusive access.</remarks>
  62. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
  63. [SecurityCritical]
  64. public BackupFileStream(string path, FileMode mode, FileSystemRights access)
  65. : this(File.CreateFileCore(null, path, ExtendedFileAttributes.Normal, null, mode, access, FileShare.None, true, PathFormat.RelativePath), access)
  66. {
  67. }
  68. /// <summary>Initializes a new instance of the <see cref="BackupFileStream"/> class with the specified path, creation mode, access rights and sharing permission.</summary>
  69. /// <param name="path">A relative or absolute path for the file that the current <see cref="BackupFileStream"/> object will encapsulate.</param>
  70. /// <param name="mode">A <see cref="FileMode"/> constant that determines how to open or create the file.</param>
  71. /// <param name="access">A <see cref="FileSystemRights"/> constant that determines the access rights to use when creating access and audit rules for the file.</param>
  72. /// <param name="share">A <see cref="FileShare"/> constant that determines how the file will be shared by processes.</param>
  73. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
  74. [SecurityCritical]
  75. public BackupFileStream(string path, FileMode mode, FileSystemRights access, FileShare share)
  76. : this(File.CreateFileCore(null, path, ExtendedFileAttributes.Normal, null, mode, access, share, true, PathFormat.RelativePath), access)
  77. {
  78. }
  79. /// <summary>Initializes a new instance of the <see cref="BackupFileStream"/> class with the specified path, creation mode, access rights and sharing permission, and additional file attributes.</summary>
  80. /// <param name="path">A relative or absolute path for the file that the current <see cref="BackupFileStream"/> object will encapsulate.</param>
  81. /// <param name="mode">A <see cref="FileMode"/> constant that determines how to open or create the file.</param>
  82. /// <param name="access">A <see cref="FileSystemRights"/> constant that determines the access rights to use when creating access and audit rules for the file.</param>
  83. /// <param name="share">A <see cref="FileShare"/> constant that determines how the file will be shared by processes.</param>
  84. /// <param name="attributes">A <see cref="ExtendedFileAttributes"/> constant that specifies additional file attributes.</param>
  85. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
  86. [SecurityCritical]
  87. public BackupFileStream(string path, FileMode mode, FileSystemRights access, FileShare share, ExtendedFileAttributes attributes)
  88. : this(File.CreateFileCore(null, path, attributes, null, mode, access, share, true, PathFormat.RelativePath), access)
  89. {
  90. }
  91. /// <summary>Initializes a new instance of the <see cref="BackupFileStream"/> class with the specified path, creation mode, access rights and sharing permission, additional file attributes, access control and audit security.</summary>
  92. /// <param name="path">A relative or absolute path for the file that the current <see cref="BackupFileStream"/> object will encapsulate.</param>
  93. /// <param name="mode">A <see cref="FileMode"/> constant that determines how to open or create the file.</param>
  94. /// <param name="access">A <see cref="FileSystemRights"/> constant that determines the access rights to use when creating access and audit rules for the file.</param>
  95. /// <param name="share">A <see cref="FileShare"/> constant that determines how the file will be shared by processes.</param>
  96. /// <param name="attributes">A <see cref="ExtendedFileAttributes"/> constant that specifies additional file attributes.</param>
  97. /// <param name="security">A <see cref="FileSecurity"/> constant that determines the access control and audit security for the file. This parameter This parameter may be <see langword="null"/>.</param>
  98. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
  99. [SecurityCritical]
  100. public BackupFileStream(string path, FileMode mode, FileSystemRights access, FileShare share, ExtendedFileAttributes attributes, FileSecurity security)
  101. : this(File.CreateFileCore(null, path, attributes, security, mode, access, share, true, PathFormat.RelativePath), access)
  102. {
  103. }
  104. #region Transactional
  105. /// <summary>Initializes a new instance of the <see cref="BackupFileStream"/> class with the specified path and creation mode.</summary>
  106. /// <param name="transaction">The transaction.</param>
  107. /// <param name="path">A relative or absolute path for the file that the current <see cref="BackupFileStream"/> object will encapsulate.</param>
  108. /// <param name="mode">A <see cref="FileMode"/> constant that determines how to open or create the file.</param>
  109. /// <remarks>The file will be opened for exclusive access for both reading and writing.</remarks>
  110. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
  111. [SecurityCritical]
  112. public BackupFileStream(KernelTransaction transaction, string path, FileMode mode)
  113. : this(File.CreateFileCore(transaction, path, ExtendedFileAttributes.Normal, null, mode, FileSystemRights.Read | FileSystemRights.Write, FileShare.None, true, PathFormat.RelativePath), FileSystemRights.Read | FileSystemRights.Write)
  114. {
  115. }
  116. /// <summary>Initializes a new instance of the <see cref="BackupFileStream"/> class with the specified path, creation mode and access rights.</summary>
  117. /// <param name="transaction">The transaction.</param>
  118. /// <param name="path">A relative or absolute path for the file that the current <see cref="BackupFileStream"/> object will encapsulate.</param>
  119. /// <param name="mode">A <see cref="FileMode"/> constant that determines how to open or create the file.</param>
  120. /// <param name="access">A <see cref="FileSystemRights"/> constant that determines the access rights to use when creating access and audit rules for the file.</param>
  121. /// <remarks>The file will be opened for exclusive access.</remarks>
  122. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
  123. [SecurityCritical]
  124. public BackupFileStream(KernelTransaction transaction, string path, FileMode mode, FileSystemRights access)
  125. : this(File.CreateFileCore(transaction, path, ExtendedFileAttributes.Normal, null, mode, access, FileShare.None, true, PathFormat.RelativePath), access)
  126. {
  127. }
  128. /// <summary>Initializes a new instance of the <see cref="BackupFileStream"/> class with the specified path, creation mode, access rights and sharing permission.</summary>
  129. /// <param name="transaction">The transaction.</param>
  130. /// <param name="path">A relative or absolute path for the file that the current <see cref="BackupFileStream"/> object will encapsulate.</param>
  131. /// <param name="mode">A <see cref="FileMode"/> constant that determines how to open or create the file.</param>
  132. /// <param name="access">A <see cref="FileSystemRights"/> constant that determines the access rights to use when creating access and audit rules for the file.</param>
  133. /// <param name="share">A <see cref="FileShare"/> constant that determines how the file will be shared by processes.</param>
  134. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
  135. [SecurityCritical]
  136. public BackupFileStream(KernelTransaction transaction, string path, FileMode mode, FileSystemRights access, FileShare share)
  137. : this(File.CreateFileCore(transaction, path, ExtendedFileAttributes.Normal, null, mode, access, share, true, PathFormat.RelativePath), access)
  138. {
  139. }
  140. /// <summary>Initializes a new instance of the <see cref="BackupFileStream"/> class with the specified path, creation mode, access rights and sharing permission, and additional file attributes.</summary>
  141. /// <param name="transaction">The transaction.</param>
  142. /// <param name="path">A relative or absolute path for the file that the current <see cref="BackupFileStream"/> object will encapsulate.</param>
  143. /// <param name="mode">A <see cref="FileMode"/> constant that determines how to open or create the file.</param>
  144. /// <param name="access">A <see cref="FileSystemRights"/> constant that determines the access rights to use when creating access and audit rules for the file.</param>
  145. /// <param name="share">A <see cref="FileShare"/> constant that determines how the file will be shared by processes.</param>
  146. /// <param name="attributes">A <see cref="ExtendedFileAttributes"/> constant that specifies additional file attributes.</param>
  147. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
  148. [SecurityCritical]
  149. public BackupFileStream(KernelTransaction transaction, string path, FileMode mode, FileSystemRights access, FileShare share, ExtendedFileAttributes attributes)
  150. : this(File.CreateFileCore(transaction, path, attributes, null, mode, access, share, true, PathFormat.RelativePath), access)
  151. {
  152. }
  153. /// <summary>Initializes a new instance of the <see cref="BackupFileStream"/> class with the specified path, creation mode, access rights and sharing permission, additional file attributes, access control and audit security.</summary>
  154. /// <param name="transaction">The transaction.</param>
  155. /// <param name="path">A relative or absolute path for the file that the current <see cref="BackupFileStream"/> object will encapsulate.</param>
  156. /// <param name="mode">A <see cref="FileMode"/> constant that determines how to open or create the file.</param>
  157. /// <param name="access">A <see cref="FileSystemRights"/> constant that determines the access rights to use when creating access and audit rules for the file.</param>
  158. /// <param name="share">A <see cref="FileShare"/> constant that determines how the file will be shared by processes.</param>
  159. /// <param name="attributes">A <see cref="ExtendedFileAttributes"/> constant that specifies additional file attributes.</param>
  160. /// <param name="security">A <see cref="FileSecurity"/> constant that determines the access control and audit security for the file. This parameter This parameter may be <see langword="null"/>.</param>
  161. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
  162. [SecurityCritical]
  163. public BackupFileStream(KernelTransaction transaction, string path, FileMode mode, FileSystemRights access, FileShare share, ExtendedFileAttributes attributes, FileSecurity security)
  164. : this(File.CreateFileCore(transaction, path, attributes, security, mode, access, share, true, PathFormat.RelativePath), access)
  165. {
  166. }
  167. #endregion // Transacted
  168. #region Stream
  169. /// <summary>Initializes a new instance of the <see cref="BackupFileStream"/> class for the specified file handle, with the specified read/write permission.</summary>
  170. /// <param name="handle">A file handle for the file that this <see cref="BackupFileStream"/> object will encapsulate.</param>
  171. /// <param name="access">A <see cref="FileSystemRights"/> constant that gets the <see cref="CanRead"/> and <see cref="CanWrite"/> properties of the <see cref="BackupFileStream"/> object.</param>
  172. [SecurityCritical]
  173. public BackupFileStream(SafeFileHandle handle, FileSystemRights access)
  174. {
  175. if (handle == null)
  176. throw new ArgumentNullException("handle", Resources.Handle_Is_Invalid);
  177. if (handle.IsInvalid)
  178. {
  179. handle.Close();
  180. throw new ArgumentException(Resources.Handle_Is_Invalid);
  181. }
  182. if (handle.IsClosed)
  183. throw new ArgumentException(Resources.Handle_Is_Closed);
  184. SafeFileHandle = handle;
  185. _canRead = (access & FileSystemRights.ReadData) != 0;
  186. _canWrite = (access & FileSystemRights.WriteData) != 0;
  187. _processSecurity = true;
  188. }
  189. #endregion // Stream
  190. #endregion // Construction and Destruction
  191. #region NotSupportedException
  192. /// <summary>When overridden in a derived class, gets the length in bytes of the stream.</summary>
  193. /// <value>This method always throws an exception.</value>
  194. /// <exception cref="NotSupportedException"/>
  195. public override long Length
  196. {
  197. get { throw new NotSupportedException(Resources.No_Stream_Seeking_Support); }
  198. }
  199. /// <summary>When overridden in a derived class, gets or sets the position within the current stream.</summary>
  200. /// <value>This method always throws an exception.</value>
  201. /// <exception cref="NotSupportedException"/>
  202. public override long Position
  203. {
  204. get { throw new NotSupportedException(Resources.No_Stream_Seeking_Support); }
  205. set { throw new NotSupportedException(Resources.No_Stream_Seeking_Support); }
  206. }
  207. /// <summary>When overridden in a derived class, sets the position within the current stream.</summary>
  208. /// <param name="offset">A byte offset relative to the <paramref name="origin"/> parameter.</param>
  209. /// <param name="origin">A value of type <see cref="System.IO.SeekOrigin"/> indicating the reference point used to obtain the new position.</param>
  210. /// <returns>The new position within the current stream.</returns>
  211. /// <remarks><para><note><para>This stream does not support seeking using this method, and calling this method will always throw <see cref="NotSupportedException"/>. See <see cref="Skip"/> for an alternative way of seeking forward.</para></note></para></remarks>
  212. /// <exception cref="NotSupportedException"/>
  213. public override long Seek(long offset, SeekOrigin origin)
  214. {
  215. throw new NotSupportedException(Resources.No_Stream_Seeking_Support);
  216. }
  217. /// <summary>When overridden in a derived class, sets the length of the current stream.</summary>
  218. /// <param name="value">The desired length of the current stream in bytes.</param>
  219. /// <remarks>This method is not supported by the <see cref="BackupFileStream"/> class, and calling it will always generate a <see cref="NotSupportedException"/>.</remarks>
  220. /// <exception cref="NotSupportedException"/>
  221. public override void SetLength(long value)
  222. {
  223. throw new NotSupportedException(Resources.No_Stream_Seeking_Support);
  224. }
  225. #endregion // NotSupportedException
  226. #region Properties
  227. /// <summary>Gets a value indicating whether the current stream supports reading.</summary>
  228. /// <returns><see langword="true"/> if the stream supports reading, <see langword="false"/> otherwise.</returns>
  229. public override bool CanRead
  230. {
  231. get { return _canRead; }
  232. }
  233. /// <summary>Gets a value indicating whether the current stream supports seeking.</summary>
  234. /// <returns>This method always returns <see langword="false"/>.</returns>
  235. public override bool CanSeek
  236. {
  237. get { return false; }
  238. }
  239. /// <summary>Gets a value indicating whether the current stream supports writing.</summary>
  240. /// <returns><see langword="true"/> if the stream supports writing, <see langword="false"/> otherwise.</returns>
  241. public override bool CanWrite
  242. {
  243. get { return _canWrite; }
  244. }
  245. /// <summary>Gets a <see cref="SafeFileHandle"/> object that represents the operating system file handle for the file that the current <see cref="BackupFileStream"/> object encapsulates.</summary>
  246. /// <value>A <see cref="SafeFileHandle"/> object that represents the operating system file handle for the file that
  247. /// the current <see cref="BackupFileStream"/> object encapsulates.</value>
  248. private SafeFileHandle SafeFileHandle { get; set; }
  249. #endregion // Properties
  250. #region Methods
  251. /// <summary>Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.</summary>
  252. /// <remarks>This method will not backup the access-control list (ACL) data for the file or directory.</remarks>
  253. /// <param name="buffer">
  254. /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between
  255. /// <paramref name="offset"/> and (<paramref name="offset"/> + <paramref name="count"/> - 1) replaced by the bytes read from the
  256. /// current source.
  257. /// </param>
  258. /// <param name="offset">
  259. /// The zero-based byte offset in <paramref name="buffer"/> at which to begin storing the data read from the current stream.
  260. /// </param>
  261. /// <param name="count">The maximum number of bytes to be read from the current stream.</param>
  262. /// <returns>
  263. /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not
  264. /// currently available, or zero (0) if the end of the stream has been reached.
  265. /// </returns>
  266. ///
  267. /// <exception cref="System.ArgumentException"/>
  268. /// <exception cref="ArgumentNullException"/>
  269. /// <exception cref="System.ArgumentOutOfRangeException"/>
  270. /// <exception cref="NotSupportedException"/>
  271. /// <exception cref="ObjectDisposedException"/>
  272. public override int Read(byte[] buffer, int offset, int count)
  273. {
  274. return Read(buffer, offset, count, false);
  275. }
  276. /// <summary>When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.</summary>
  277. /// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values
  278. /// between <paramref name="offset"/> and (<paramref name="offset"/> + <paramref name="count"/> - 1) replaced by the bytes read from the current source.</param>
  279. /// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin storing the data read from the current stream.</param>
  280. /// <param name="count">The maximum number of bytes to be read from the current stream.</param>
  281. /// <param name="processSecurity">Indicates whether the function will backup the access-control list (ACL) data for the file or directory.</param>
  282. /// <returns>
  283. /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not
  284. /// currently available, or zero (0) if the end of the stream has been reached.
  285. /// </returns>
  286. /// <exception cref="ArgumentException"/>
  287. /// <exception cref="ArgumentNullException"/>
  288. /// <exception cref="ArgumentOutOfRangeException"/>
  289. /// <exception cref="NotSupportedException"/>
  290. /// <exception cref="ObjectDisposedException"/>
  291. [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
  292. [SecurityCritical]
  293. public int Read(byte[] buffer, int offset, int count, bool processSecurity)
  294. {
  295. if (buffer == null)
  296. throw new ArgumentNullException("buffer");
  297. if (!CanRead)
  298. throw new NotSupportedException("Stream does not support reading");
  299. if (offset + count > buffer.Length)
  300. throw new ArgumentException("The sum of offset and count is larger than the size of the buffer.");
  301. if (offset < 0)
  302. throw new ArgumentOutOfRangeException("offset", offset, Resources.Negative_Offset);
  303. if (count < 0)
  304. throw new ArgumentOutOfRangeException("count", count, Resources.Negative_Count);
  305. using (var safeBuffer = new SafeGlobalMemoryBufferHandle(count))
  306. {
  307. uint numberOfBytesRead;
  308. if (!NativeMethods.BackupRead(SafeFileHandle, safeBuffer, (uint)safeBuffer.Capacity, out numberOfBytesRead, false, processSecurity, ref _context))
  309. NativeError.ThrowException(Marshal.GetLastWin32Error());
  310. // See File.GetAccessControlCore(): .CopyTo() does not work there?
  311. safeBuffer.CopyTo(buffer, offset, count);
  312. return (int)numberOfBytesRead;
  313. }
  314. }
  315. /// <summary>Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.</summary>
  316. /// <overloads>
  317. /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
  318. /// </overloads>
  319. /// <param name="buffer">An array of bytes. This method copies <paramref name="count"/> bytes from <paramref name="buffer"/> to the current stream.</param>
  320. /// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin copying bytes to the current stream.</param>
  321. /// <param name="count">The number of bytes to be written to the current stream.</param>
  322. /// <exception cref="ArgumentException"/>
  323. /// <exception cref="System.ArgumentNullException"/>
  324. /// <exception cref="System.ArgumentOutOfRangeException"/>
  325. /// <exception cref="NotSupportedException"/>
  326. /// <exception cref="ObjectDisposedException"/>
  327. /// <remarks>This method will not process the access-control list (ACL) data for the file or directory.</remarks>
  328. public override void Write(byte[] buffer, int offset, int count)
  329. {
  330. Write(buffer, offset, count, false);
  331. }
  332. /// <summary>When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.</summary>
  333. /// <param name="buffer">An array of bytes. This method copies <paramref name="count"/> bytes from <paramref name="buffer"/> to the current stream.</param>
  334. /// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin copying bytes to the current stream.</param>
  335. /// <param name="count">The number of bytes to be written to the current stream.</param>
  336. /// <param name="processSecurity">Specifies whether the function will restore the access-control list (ACL) data for the file or directory.
  337. /// If this is <see langword="true"/> you need to specify <see cref="FileSystemRights.TakeOwnership"/> and <see cref="FileSystemRights.ChangePermissions"/> access when
  338. /// opening the file or directory handle. If the handle does not have those access rights, the operating system denies
  339. /// access to the ACL data, and ACL data restoration will not occur.</param>
  340. /// <exception cref="ArgumentException"/>
  341. /// <exception cref="ArgumentNullException"/>
  342. /// <exception cref="System.ArgumentOutOfRangeException"/>
  343. /// <exception cref="NotSupportedException"/>
  344. /// <exception cref="ObjectDisposedException"/>
  345. [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
  346. [SecurityCritical]
  347. public void Write(byte[] buffer, int offset, int count, bool processSecurity)
  348. {
  349. if (buffer == null)
  350. throw new ArgumentNullException("buffer");
  351. if (offset < 0)
  352. throw new ArgumentOutOfRangeException("offset", offset, Resources.Negative_Offset);
  353. if (count < 0)
  354. throw new ArgumentOutOfRangeException("count", count, Resources.Negative_Count);
  355. if (offset + count > buffer.Length)
  356. throw new ArgumentException(Resources.Buffer_Not_Large_Enough);
  357. using (var safeBuffer = new SafeGlobalMemoryBufferHandle(count))
  358. {
  359. safeBuffer.CopyFrom(buffer, offset, count);
  360. uint bytesWritten;
  361. if (!NativeMethods.BackupWrite(SafeFileHandle, safeBuffer, (uint)safeBuffer.Capacity, out bytesWritten, false, processSecurity, ref _context))
  362. NativeError.ThrowException(Marshal.GetLastWin32Error());
  363. }
  364. }
  365. /// <summary>Clears all buffers for this stream and causes any buffered data to be written to the underlying device.</summary>
  366. public override void Flush()
  367. {
  368. if (!NativeMethods.FlushFileBuffers(SafeFileHandle))
  369. NativeError.ThrowException(Marshal.GetLastWin32Error());
  370. }
  371. /// <summary>Skips ahead the specified number of bytes from the current stream.</summary>
  372. /// <remarks><para>This method represents the Win32 API implementation of <see href="http://msdn.microsoft.com/en-us/library/aa362509(VS.85).aspx">BackupSeek</see>.</para>
  373. /// <para>
  374. /// Applications use the <see cref="Skip"/> method to skip portions of a data stream that cause errors. This function does not
  375. /// seek across stream headers. For example, this function cannot be used to skip the stream name. If an application
  376. /// attempts to seek past the end of a substream, the function fails, the return value indicates the actual number of bytes
  377. /// the function seeks, and the file position is placed at the start of the next stream header.
  378. /// </para>
  379. /// </remarks>
  380. /// <param name="bytes">The number of bytes to skip.</param>
  381. /// <returns>The number of bytes actually skipped.</returns>
  382. [SecurityCritical]
  383. public long Skip(long bytes)
  384. {
  385. uint lowSought, highSought;
  386. if (!NativeMethods.BackupSeek(SafeFileHandle, NativeMethods.GetLowOrderDword(bytes), NativeMethods.GetHighOrderDword(bytes), out lowSought, out highSought, ref _context))
  387. {
  388. int lastError = Marshal.GetLastWin32Error();
  389. // Error Code 25 indicates a seek error, we just skip that here.
  390. if (lastError != Win32Errors.NO_ERROR && lastError != Win32Errors.ERROR_SEEK)
  391. NativeError.ThrowException(lastError);
  392. }
  393. return NativeMethods.ToLong(highSought, lowSought);
  394. }
  395. /// <summary>Gets a <see cref="FileSecurity"/> object that encapsulates the access control list (ACL) entries for the file described by the current <see cref="BackupFileStream"/> object.</summary>
  396. /// <exception cref="IOException"/>
  397. /// <returns>
  398. /// A <see cref="FileSecurity"/> object that encapsulates the access control list (ACL) entries for the file described by the current
  399. /// <see cref="BackupFileStream"/> object.
  400. /// </returns>
  401. [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
  402. [SecurityCritical]
  403. public FileSecurity GetAccessControl()
  404. {
  405. IntPtr pSidOwner, pSidGroup, pDacl, pSacl;
  406. SafeGlobalMemoryBufferHandle pSecurityDescriptor;
  407. uint lastError = SecurityNativeMethods.GetSecurityInfo(SafeFileHandle, ObjectType.FileObject,
  408. SecurityInformation.Group | SecurityInformation.Owner | SecurityInformation.Label | SecurityInformation.Dacl | SecurityInformation.Sacl,
  409. out pSidOwner, out pSidGroup, out pDacl, out pSacl, out pSecurityDescriptor);
  410. try
  411. {
  412. if (lastError != Win32Errors.ERROR_SUCCESS)
  413. NativeError.ThrowException((int)lastError);
  414. if (pSecurityDescriptor != null && pSecurityDescriptor.IsInvalid)
  415. {
  416. pSecurityDescriptor.Close();
  417. throw new IOException(Resources.Returned_Invalid_Security_Descriptor);
  418. }
  419. uint length = SecurityNativeMethods.GetSecurityDescriptorLength(pSecurityDescriptor);
  420. byte[] managedBuffer = new byte[length];
  421. // .CopyTo() does not work there?
  422. if (pSecurityDescriptor != null)
  423. pSecurityDescriptor.CopyTo(managedBuffer, 0, (int) length);
  424. var fs = new FileSecurity();
  425. fs.SetSecurityDescriptorBinaryForm(managedBuffer);
  426. return fs;
  427. }
  428. finally
  429. {
  430. if (pSecurityDescriptor != null)
  431. pSecurityDescriptor.Close();
  432. }
  433. }
  434. /// <summary>Applies access control list (ACL) entries described by a <see cref="FileSecurity"/> object to the file described by the current <see cref="BackupFileStream"/> object.</summary>
  435. /// <param name="fileSecurity">A <see cref="FileSecurity"/> object that describes an ACL entry to apply to the current file.</param>
  436. [SecurityCritical]
  437. public void SetAccessControl(ObjectSecurity fileSecurity)
  438. {
  439. File.SetAccessControlCore(null, SafeFileHandle, fileSecurity, AccessControlSections.All, PathFormat.LongFullPath);
  440. }
  441. /// <summary>Prevents other processes from changing the <see cref="BackupFileStream"/> while permitting read access.</summary>
  442. /// <param name="position">The beginning of the range to lock. The value of this parameter must be equal to or greater than zero (0).</param>
  443. /// <param name="length">The range to be locked.</param>
  444. /// <exception cref="ArgumentOutOfRangeException"/>
  445. /// <exception cref="ObjectDisposedException"/>
  446. [SecurityCritical]
  447. public void Lock(long position, long length)
  448. {
  449. if (position < 0)
  450. throw new ArgumentOutOfRangeException("position", position, Resources.Unlock_Position_Negative);
  451. if (length < 0)
  452. throw new ArgumentOutOfRangeException("length", length, Resources.Negative_Lock_Length);
  453. if (!NativeMethods.LockFile(SafeFileHandle, NativeMethods.GetLowOrderDword(position), NativeMethods.GetHighOrderDword(position), NativeMethods.GetLowOrderDword(length), NativeMethods.GetHighOrderDword(length)))
  454. NativeError.ThrowException(Marshal.GetLastWin32Error());
  455. }
  456. /// <summary>Allows access by other processes to all or part of a file that was previously locked.</summary>
  457. /// <param name="position">The beginning of the range to unlock.</param>
  458. /// <param name="length">The range to be unlocked.</param>
  459. /// <exception cref="ArgumentOutOfRangeException"/>
  460. /// <exception cref="ArgumentOutOfRangeException"/>
  461. /// <exception cref="ObjectDisposedException"/>
  462. [SecurityCritical]
  463. public void Unlock(long position, long length)
  464. {
  465. if (position < 0)
  466. throw new ArgumentOutOfRangeException("position", position, Resources.Unlock_Position_Negative);
  467. if (length < 0)
  468. throw new ArgumentOutOfRangeException("length", length, Resources.Negative_Lock_Length);
  469. if (!NativeMethods.UnlockFile(SafeFileHandle, NativeMethods.GetLowOrderDword(position), NativeMethods.GetHighOrderDword(position), NativeMethods.GetLowOrderDword(length), NativeMethods.GetHighOrderDword(length)))
  470. NativeError.ThrowException(Marshal.GetLastWin32Error());
  471. }
  472. /// <summary>Reads a stream header from the current <see cref="BackupFileStream"/>.</summary>
  473. /// <returns>The stream header read from the current <see cref="BackupFileStream"/>, or <see langword="null"/> if the end-of-file
  474. /// was reached before the required number of bytes of a header could be read.</returns>
  475. /// <exception cref="IOException"/>
  476. /// <remarks>The stream must be positioned at where an actual header starts for the returned object to represent valid
  477. /// information.</remarks>
  478. [SecurityCritical]
  479. public BackupStreamInfo ReadStreamInfo()
  480. {
  481. var sizeOf = Marshal.SizeOf(typeof(NativeMethods.WIN32_STREAM_ID));
  482. using (var hBuf = new SafeGlobalMemoryBufferHandle(sizeOf))
  483. {
  484. uint numberOfBytesRead;
  485. if (!NativeMethods.BackupRead(SafeFileHandle, hBuf, (uint) sizeOf, out numberOfBytesRead, false, _processSecurity, ref _context))
  486. NativeError.ThrowException();
  487. if (numberOfBytesRead == 0)
  488. return null;
  489. if (numberOfBytesRead < sizeOf)
  490. throw new IOException(Resources.Read_Incomplete_Header);
  491. var streamID = hBuf.PtrToStructure<NativeMethods.WIN32_STREAM_ID>(0);
  492. uint nameLength = (uint) Math.Min(streamID.dwStreamNameSize, hBuf.Capacity);
  493. if (!NativeMethods.BackupRead(SafeFileHandle, hBuf, nameLength, out numberOfBytesRead, false, _processSecurity, ref _context))
  494. NativeError.ThrowException();
  495. string name = hBuf.PtrToStringUni(0, (int) nameLength/2);
  496. return new BackupStreamInfo(streamID, name);
  497. }
  498. }
  499. #endregion // Methods
  500. #region Disposable Members
  501. /// <summary>Releases the unmanaged resources used by the <see cref="System.IO.Stream"/> and optionally releases the managed resources.</summary>
  502. /// <param name="disposing"><see langword="true"/> to release both managed and unmanaged resources; <see langword="false"/> to release only unmanaged resources.</param>
  503. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
  504. protected override void Dispose(bool disposing)
  505. {
  506. // If one of the constructors previously threw an exception,
  507. // than the object hasn't been initialized properly and call from finalize will fail.
  508. if (SafeFileHandle != null && !SafeFileHandle.IsInvalid)
  509. {
  510. if (_context != IntPtr.Zero)
  511. {
  512. try
  513. {
  514. uint temp;
  515. // MSDN: To release the memory used by the data structure, call BackupRead with the bAbort parameter set to TRUE when the backup operation is complete.
  516. if (!NativeMethods.BackupRead(SafeFileHandle, new SafeGlobalMemoryBufferHandle(), 0, out temp, true, false, ref _context))
  517. NativeError.ThrowException(Marshal.GetLastWin32Error());
  518. }
  519. finally
  520. {
  521. _context = IntPtr.Zero;
  522. SafeFileHandle.Close();
  523. }
  524. }
  525. }
  526. base.Dispose(disposing);
  527. }
  528. /// <summary>Releases unmanaged resources and performs other cleanup operations before the <see cref="BackupFileStream"/> is reclaimed by garbage collection.</summary>
  529. ~BackupFileStream()
  530. {
  531. Dispose(false);
  532. }
  533. #endregion // Disposable Members
  534. }
  535. }