OperatingSystemVolume.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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. using Utilities;
  14. namespace DiskAccessLibrary
  15. {
  16. public class OperatingSystemVolume : Volume
  17. {
  18. private Guid m_volumeGuid;
  19. private int m_bytesPerSector;
  20. private long m_size;
  21. private bool m_isReadOnly;
  22. public OperatingSystemVolume(Guid volumeGuid, int bytesPerSector, long size) : this(volumeGuid, bytesPerSector, size, false)
  23. {
  24. }
  25. public OperatingSystemVolume(Guid volumeGuid, int bytesPerSector, long size, bool isReadOnly)
  26. {
  27. m_volumeGuid = volumeGuid;
  28. m_bytesPerSector = bytesPerSector;
  29. m_size = size;
  30. m_isReadOnly = isReadOnly;
  31. }
  32. public override byte[] ReadSectors(long sectorIndex, int sectorCount)
  33. {
  34. if (sectorCount > PhysicalDisk.MaximumDirectTransferSizeLBA)
  35. {
  36. // we must read one segment at the time, and copy the segments to a big bufffer
  37. byte[] buffer = new byte[sectorCount * m_bytesPerSector];
  38. for (int sectorOffset = 0; sectorOffset < sectorCount; sectorOffset += PhysicalDisk.MaximumDirectTransferSizeLBA)
  39. {
  40. int leftToRead = sectorCount - sectorOffset;
  41. int sectorsToRead = (int)Math.Min(leftToRead, PhysicalDisk.MaximumDirectTransferSizeLBA);
  42. byte[] segment = ReadSectorsUnbuffered(sectorIndex + sectorOffset, sectorsToRead);
  43. Array.Copy(segment, 0, buffer, sectorOffset * m_bytesPerSector, segment.Length);
  44. }
  45. return buffer;
  46. }
  47. else
  48. {
  49. return ReadSectorsUnbuffered(sectorIndex, sectorCount);
  50. }
  51. }
  52. /// <summary>
  53. /// Sector refers to physical disk blocks, we can only read complete blocks
  54. /// </summary>
  55. public byte[] ReadSectorsUnbuffered(long sectorIndex, int sectorCount)
  56. {
  57. bool releaseHandle;
  58. SafeFileHandle handle = VolumeHandlePool.ObtainHandle(m_volumeGuid, FileAccess.Read, ShareMode.ReadWrite, out releaseHandle);
  59. if (!handle.IsInvalid)
  60. {
  61. FileStreamEx stream = new FileStreamEx(handle, FileAccess.Read);
  62. byte[] buffer = new byte[m_bytesPerSector * sectorCount];
  63. try
  64. {
  65. stream.Seek(sectorIndex * m_bytesPerSector, SeekOrigin.Begin);
  66. stream.Read(buffer, 0, m_bytesPerSector * sectorCount);
  67. }
  68. finally
  69. {
  70. stream.Close(releaseHandle);
  71. if (releaseHandle)
  72. {
  73. VolumeHandlePool.ReleaseHandle(m_volumeGuid);
  74. }
  75. }
  76. return buffer;
  77. }
  78. else
  79. {
  80. // we always release invalid handle
  81. VolumeHandlePool.ReleaseHandle(m_volumeGuid);
  82. // get error code and throw
  83. int errorCode = Marshal.GetLastWin32Error();
  84. string message = String.Format("Can't read sector {0} from volume {1}, Win32 Error: {2}", sectorIndex, m_volumeGuid, errorCode);
  85. throw new IOException(message);
  86. }
  87. }
  88. public override void WriteSectors(long sectorIndex, byte[] data)
  89. {
  90. if (data.Length % m_bytesPerSector > 0)
  91. {
  92. throw new IOException("Cannot write partial sectors");
  93. }
  94. int sectorCount = data.Length / m_bytesPerSector;
  95. if (sectorCount > PhysicalDisk.MaximumDirectTransferSizeLBA)
  96. {
  97. // we must write one segment at the time
  98. for (int sectorOffset = 0; sectorOffset < sectorCount; sectorOffset += PhysicalDisk.MaximumDirectTransferSizeLBA)
  99. {
  100. int leftToWrite = sectorCount - sectorOffset;
  101. int sectorsToWrite = (int)Math.Min(leftToWrite, PhysicalDisk.MaximumDirectTransferSizeLBA);
  102. byte[] segment = new byte[sectorsToWrite * m_bytesPerSector];
  103. Array.Copy(data, sectorOffset * m_bytesPerSector, segment, 0, sectorsToWrite * m_bytesPerSector);
  104. WriteSectorsUnbuffered(sectorIndex + sectorOffset, segment);
  105. }
  106. }
  107. else
  108. {
  109. WriteSectorsUnbuffered(sectorIndex, data);
  110. }
  111. }
  112. public void WriteSectorsUnbuffered(long sectorIndex, byte[] data)
  113. {
  114. if (data.Length % m_bytesPerSector > 0)
  115. {
  116. throw new IOException("Cannot write partial sectors");
  117. }
  118. if (!m_isReadOnly)
  119. {
  120. bool releaseHandle;
  121. SafeFileHandle handle = VolumeHandlePool.ObtainHandle(m_volumeGuid, FileAccess.ReadWrite, ShareMode.None, out releaseHandle);
  122. if (!handle.IsInvalid)
  123. {
  124. FileStreamEx stream = new FileStreamEx(handle, FileAccess.Write);
  125. try
  126. {
  127. stream.Seek(sectorIndex * m_bytesPerSector, SeekOrigin.Begin);
  128. stream.Write(data, 0, data.Length);
  129. stream.Flush();
  130. }
  131. finally
  132. {
  133. stream.Close(releaseHandle);
  134. if (releaseHandle)
  135. {
  136. VolumeHandlePool.ReleaseHandle(m_volumeGuid);
  137. }
  138. }
  139. }
  140. else
  141. {
  142. // we always release invalid handle
  143. VolumeHandlePool.ReleaseHandle(m_volumeGuid);
  144. // get error code and throw
  145. int errorCode = Marshal.GetLastWin32Error();
  146. string message = String.Format("Can't write to sector {0} of volume {1}, Win32 errorCode: {2}", sectorIndex, m_volumeGuid, errorCode);
  147. throw new IOException(message);
  148. }
  149. }
  150. }
  151. /// <summary>
  152. /// Volume should be locked at this point for this call to have any effect
  153. /// </summary>
  154. /// <returns></returns>
  155. public bool AllowExtendedIO()
  156. {
  157. bool releaseHandle;
  158. SafeFileHandle handle = VolumeHandlePool.ObtainHandle(m_volumeGuid, FileAccess.ReadWrite, ShareMode.None, out releaseHandle);
  159. if (!handle.IsInvalid)
  160. {
  161. bool result = VolumeControl.AllowExtendedIO(handle);
  162. if (releaseHandle)
  163. {
  164. VolumeHandlePool.ReleaseHandle(m_volumeGuid);
  165. }
  166. return result;
  167. }
  168. else
  169. {
  170. // we always release invalid handle
  171. VolumeHandlePool.ReleaseHandle(m_volumeGuid);
  172. return false;
  173. }
  174. }
  175. public override long Size
  176. {
  177. get
  178. {
  179. return m_size;
  180. }
  181. }
  182. public override int BytesPerSector
  183. {
  184. get
  185. {
  186. return m_bytesPerSector;
  187. }
  188. }
  189. public Guid VolumeGuid
  190. {
  191. get
  192. {
  193. return m_volumeGuid;
  194. }
  195. }
  196. public override List<DiskExtent> Extents
  197. {
  198. get
  199. {
  200. return new List<DiskExtent>();
  201. }
  202. }
  203. }
  204. }