FileStreamEx.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. /* Copyright (C) 2014 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
  2. *
  3. * You can redistribute this program and/or modify it under the terms of
  4. * the GNU Lesser Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. */
  7. using System;
  8. using System.Collections.Generic;
  9. using System.IO;
  10. using System.Runtime.InteropServices;
  11. using System.Text;
  12. using Microsoft.Win32.SafeHandles;
  13. namespace DiskAccessLibrary
  14. {
  15. // FileStream reads will try to fill the internal buffer,
  16. // this may cause issues when reading the last few sectors of the disk,
  17. // (FileStream will try to read sectors that do not exist).
  18. // This can be solved by setting the FileStream buffer size to the sector size.
  19. // An alternative is to use FileStreamEx which does not use an internal read buffer at all.
  20. public class FileStreamEx : FileStream
  21. {
  22. [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
  23. static extern bool ReadFile(SafeFileHandle handle, byte[] buffer, uint numberOfBytesToRead, out uint numberOfBytesRead, IntPtr lpOverlapped);
  24. private bool m_releaseHandle = true;
  25. public FileStreamEx(SafeFileHandle handle, FileAccess access) : base(handle, access)
  26. {
  27. }
  28. /// <param name="offset">The byte offset in array at which the read bytes will be placed</param>
  29. /// <param name="count">The maximum number of bytes to read</param>
  30. public override int Read(byte[] array, int offset, int count)
  31. {
  32. uint result;
  33. if (offset == 0)
  34. {
  35. ReadFile(this.SafeFileHandle, array, (uint)count, out result, IntPtr.Zero);
  36. }
  37. else
  38. {
  39. byte[] buffer = new byte[count];
  40. ReadFile(this.SafeFileHandle, buffer, (uint)buffer.Length, out result, IntPtr.Zero);
  41. Array.Copy(buffer, 0, array, offset, buffer.Length);
  42. }
  43. if (count == result)
  44. {
  45. return (int)result;
  46. }
  47. else
  48. {
  49. int errorCode = Marshal.GetLastWin32Error();
  50. string message = String.Format("Could not read from position {0} the requested number of bytes ({1}).", this.Position, count);
  51. ThrowIOError(errorCode, message);
  52. return 0; // this line will not be reached
  53. }
  54. }
  55. internal static void ThrowIOError(int errorCode, string defaultMessage)
  56. {
  57. if (errorCode == (int)Win32Error.ERROR_ACCESS_DENIED)
  58. {
  59. // UnauthorizedAccessException will be thrown if stream was opened only for writing or if a user is not an administrator
  60. throw new UnauthorizedAccessException(defaultMessage);
  61. }
  62. else if (errorCode == (int)Win32Error.ERROR_SHARING_VIOLATION)
  63. {
  64. throw new SharingViolationException(defaultMessage);
  65. }
  66. else if (errorCode == (int)Win32Error.ERROR_SECTOR_NOT_FOUND)
  67. {
  68. string message = defaultMessage + " The sector does not exist.";
  69. throw new IOException(message, (int)Win32Error.ERROR_SECTOR_NOT_FOUND);
  70. }
  71. else if (errorCode == (int)Win32Error.ERROR_CRC)
  72. {
  73. string message = defaultMessage + " Data Error (Cyclic Redundancy Check).";
  74. throw new CyclicRedundancyCheckException(message);
  75. }
  76. else if (errorCode == (int)Win32Error.ERROR_NO_SYSTEM_RESOURCES)
  77. {
  78. throw new OutOfMemoryException();
  79. }
  80. else
  81. {
  82. string message = defaultMessage + String.Format(" Win32 Error: {0}", errorCode);
  83. throw new IOException(message, errorCode);
  84. }
  85. }
  86. // we are working with disks, and we are only supposed to read sectors
  87. public override int ReadByte()
  88. {
  89. throw new NotImplementedException("Cannot read a single byte from disk");
  90. }
  91. public void Close(bool releaseHandle)
  92. {
  93. m_releaseHandle = releaseHandle;
  94. this.Close();
  95. }
  96. /// <summary>
  97. /// This will prevent the handle from being disposed
  98. /// </summary>
  99. protected override void Dispose(bool disposing)
  100. {
  101. if (m_releaseHandle)
  102. {
  103. base.Dispose(disposing);
  104. }
  105. else
  106. {
  107. try
  108. {
  109. this.Flush();
  110. }
  111. catch
  112. {
  113. }
  114. }
  115. }
  116. }
  117. }