VirtualHardDisk.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. /* Copyright (C) 2014-2016 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.IO;
  9. using System.Collections.Generic;
  10. using System.Text;
  11. using Utilities;
  12. using DiskAccessLibrary.VHD;
  13. namespace DiskAccessLibrary
  14. {
  15. public partial class VirtualHardDisk : DiskImage, IDiskGeometry
  16. {
  17. // VHD sector size is set to 512 bytes.
  18. public const int BytesPerDiskSector = 512;
  19. private RawDiskImage m_file;
  20. private VHDFooter m_vhdFooter;
  21. // Dynamic VHD:
  22. private DynamicDiskHeader m_dynamicHeader;
  23. private BlockAllocationTable m_blockAllocationTable;
  24. // CHS:
  25. private long m_cylinders;
  26. private int m_tracksPerCylinder; // a.k.a. heads
  27. private int m_sectorsPerTrack;
  28. /// <exception cref="System.IO.IOException"></exception>
  29. /// <exception cref="System.IO.InvalidDataException"></exception>
  30. /// <exception cref="System.NotImplementedException"></exception>
  31. /// <exception cref="System.UnauthorizedAccessException"></exception>
  32. public VirtualHardDisk(string virtualHardDiskPath) : base(virtualHardDiskPath)
  33. {
  34. // We can't read the VHD footer using this.ReadSector() because it's out of the disk boundaries
  35. m_file = new RawDiskImage(virtualHardDiskPath, BytesPerDiskSector);
  36. byte[] buffer = m_file.ReadSector(m_file.Size / m_file.BytesPerSector - 1);
  37. m_vhdFooter = new VHDFooter(buffer);
  38. if (!m_vhdFooter.IsValid)
  39. {
  40. // check to see if a header is present (dynamic VHD) and use it instead
  41. buffer = m_file.ReadSector(0);
  42. m_vhdFooter = new VHDFooter(buffer);
  43. if (!m_vhdFooter.IsValid)
  44. {
  45. throw new InvalidDataException("Invalid VHD footer");
  46. }
  47. }
  48. if (m_vhdFooter.DiskType == VirtualHardDiskType.Fixed)
  49. {
  50. }
  51. else if (m_vhdFooter.DiskType == VirtualHardDiskType.Dynamic)
  52. {
  53. buffer = m_file.ReadSectors(1, 2);
  54. m_dynamicHeader = new DynamicDiskHeader(buffer);
  55. m_blockAllocationTable = BlockAllocationTable.ReadBlockAllocationTable(virtualHardDiskPath, m_dynamicHeader);
  56. this.IsReadOnly = true;
  57. }
  58. else
  59. {
  60. throw new NotImplementedException("Differencing VHD is not supported");
  61. }
  62. SetGeometry();
  63. }
  64. private void SetGeometry()
  65. {
  66. byte heads;
  67. byte sectorsPerTrack;
  68. ushort cylinders;
  69. GetDiskGeometry((ulong)this.TotalSectors, out heads, out sectorsPerTrack, out cylinders);
  70. m_cylinders = cylinders;
  71. m_tracksPerCylinder = heads;
  72. m_sectorsPerTrack = sectorsPerTrack;
  73. }
  74. public override bool ExclusiveLock()
  75. {
  76. return m_file.ExclusiveLock();
  77. }
  78. public override bool ReleaseLock()
  79. {
  80. return m_file.ReleaseLock();
  81. }
  82. /// <summary>
  83. /// Sector refers to physical disk sector, we can only read complete sectors
  84. /// </summary>
  85. public override byte[] ReadSectors(long sectorIndex, int sectorCount)
  86. {
  87. CheckBoundaries(sectorIndex, sectorCount);
  88. if (m_vhdFooter.DiskType == VirtualHardDiskType.Fixed)
  89. {
  90. return m_file.ReadSectors(sectorIndex, sectorCount);
  91. }
  92. else // dynamic
  93. {
  94. byte[] buffer = new byte[sectorCount * this.BytesPerSector];
  95. int sectorOffset = 0;
  96. while (sectorOffset < sectorCount)
  97. {
  98. uint blockIndex = (uint)((sectorIndex + sectorOffset) * this.BytesPerSector / m_dynamicHeader.BlockSize);
  99. int sectorOffsetInBlock = (int)(((sectorIndex + sectorOffset) * this.BytesPerSector % m_dynamicHeader.BlockSize) / this.BytesPerSector);
  100. int sectorsInBlock = (int)(m_dynamicHeader.BlockSize / this.BytesPerSector);
  101. int sectorsRemainingInBlock = sectorsInBlock - sectorOffsetInBlock;
  102. int sectorsToRead = Math.Min(sectorCount - sectorOffset, sectorsRemainingInBlock);
  103. if (m_blockAllocationTable.IsBlockInUse(blockIndex))
  104. {
  105. uint blockStartSector = m_blockAllocationTable.Entries[blockIndex];
  106. // each data block has a sector bitmap preceding the data, the bitmap is padded to a 512-byte sector boundary.
  107. int blockBitmapSectorCount = (int)Math.Ceiling((double)sectorsInBlock / (this.BytesPerSector * 8));
  108. // "All sectors within a block whose corresponding bits in the bitmap are zero must contain 512 bytes of zero on disk"
  109. byte[] temp = m_file.ReadSectors(blockStartSector + blockBitmapSectorCount + sectorOffsetInBlock, sectorsToRead);
  110. ByteWriter.WriteBytes(buffer, sectorOffset * this.BytesPerSector, temp);
  111. }
  112. sectorOffset += sectorsToRead;
  113. }
  114. return buffer;
  115. }
  116. }
  117. public override void WriteSectors(long sectorIndex, byte[] data)
  118. {
  119. CheckBoundaries(sectorIndex, data.Length / this.BytesPerSector);
  120. m_file.WriteSectors(sectorIndex, data);
  121. }
  122. public override void Extend(long numberOfAdditionalBytes)
  123. {
  124. if (numberOfAdditionalBytes % this.BytesPerSector > 0)
  125. {
  126. throw new ArgumentException("numberOfAdditionalBytes must be a multiple of BytesPerSector");
  127. }
  128. if (m_vhdFooter.DiskType == VirtualHardDiskType.Fixed)
  129. {
  130. long length = this.Size; // does not include the footer
  131. m_file.Extend(numberOfAdditionalBytes);
  132. m_vhdFooter.CurrentSize += (ulong)numberOfAdditionalBytes;
  133. byte[] footerBytes = m_vhdFooter.GetBytes();
  134. m_file.WriteSectors((length + numberOfAdditionalBytes) / this.BytesPerSector, footerBytes);
  135. }
  136. else
  137. {
  138. throw new NotImplementedException();
  139. }
  140. }
  141. public long Cylinders
  142. {
  143. get
  144. {
  145. return m_cylinders;
  146. }
  147. }
  148. public int TracksPerCylinder
  149. {
  150. get
  151. {
  152. return m_tracksPerCylinder;
  153. }
  154. }
  155. public int SectorsPerTrack
  156. {
  157. get
  158. {
  159. return m_sectorsPerTrack;
  160. }
  161. }
  162. public override int BytesPerSector
  163. {
  164. get
  165. {
  166. return BytesPerDiskSector;
  167. }
  168. }
  169. public override long Size
  170. {
  171. get
  172. {
  173. return (long)m_vhdFooter.CurrentSize;
  174. }
  175. }
  176. public VHDFooter Footer
  177. {
  178. get
  179. {
  180. return m_vhdFooter;
  181. }
  182. }
  183. // Taken From VHD format specs (Appendix)
  184. public static void GetDiskGeometry(ulong totalSectors, out byte heads, out byte sectorsPerTrack, out ushort cylinders)
  185. {
  186. int cylindersTimesHeads;
  187. // If more than ~128GB truncate at ~128GB
  188. if (totalSectors > 65535 * 16 * 255)
  189. {
  190. totalSectors = 65535 * 16 * 255;
  191. }
  192. // If more than ~32GB, break partition table compatibility.
  193. // Partition table has max 63 sectors per track. Otherwise
  194. // we're looking for a geometry that's valid for both BIOS
  195. // and ATA.
  196. if (totalSectors >= 65535 * 16 * 63)
  197. {
  198. sectorsPerTrack = 255;
  199. heads = 16;
  200. cylindersTimesHeads = (int)(totalSectors / sectorsPerTrack);
  201. }
  202. else
  203. {
  204. sectorsPerTrack = 17;
  205. cylindersTimesHeads = (int)(totalSectors / sectorsPerTrack);
  206. heads = (byte)((cylindersTimesHeads + 1023) / 1024);
  207. if (heads < 4)
  208. {
  209. heads = 4;
  210. }
  211. if (cylindersTimesHeads >= (heads * 1024) || heads > 16)
  212. {
  213. sectorsPerTrack = 31;
  214. heads = 16;
  215. cylindersTimesHeads = (int)(totalSectors / sectorsPerTrack);
  216. }
  217. if (cylindersTimesHeads >= (heads * 1024))
  218. {
  219. sectorsPerTrack = 63;
  220. heads = 16;
  221. cylindersTimesHeads = (int)(totalSectors / sectorsPerTrack);
  222. }
  223. }
  224. cylinders = (ushort)(cylindersTimesHeads / heads);
  225. }
  226. /// <param name="size">In bytes</param>
  227. /// <exception cref="System.IO.IOException"></exception>
  228. /// <exception cref="System.UnauthorizedAccessException"></exception>
  229. public static VirtualHardDisk Create(string path, long size)
  230. {
  231. VHDFooter footer = new VHDFooter();
  232. footer.OriginalSize = (ulong)size;
  233. footer.CurrentSize = (ulong)size;
  234. footer.SetCurrentTimeStamp();
  235. footer.SetDiskGeometry((ulong)size / BytesPerDiskSector);
  236. RawDiskImage diskImage = RawDiskImage.Create(path, size + VHDFooter.Length, BytesPerDiskSector);
  237. diskImage.WriteSectors(size / BytesPerDiskSector, footer.GetBytes());
  238. return new VirtualHardDisk(path);
  239. }
  240. }
  241. }