SparseExtent.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  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.Collections.Generic;
  9. using System.IO;
  10. using System.Text;
  11. using Utilities;
  12. namespace DiskAccessLibrary.VMDK
  13. {
  14. public partial class SparseExtent : DiskImage
  15. {
  16. private RawDiskImage m_file;
  17. private SparseExtentHeader m_header;
  18. private VirtualMachineDiskDescriptor m_descriptor;
  19. private Nullable<uint> m_grainTableStartSector;
  20. public SparseExtent(string path) : base(path)
  21. {
  22. m_file = new RawDiskImage(path, VirtualMachineDisk.BytesPerDiskSector);
  23. byte[] headerBytes = m_file.ReadSector(0);
  24. m_header = new SparseExtentHeader(headerBytes);
  25. if (m_header.IsValidAndSupported)
  26. {
  27. if (m_header.DescriptorOffset > 0)
  28. {
  29. byte[] descriptorBytes = m_file.ReadSectors((long)m_header.DescriptorOffset, (int)m_header.DescriptorSize);
  30. string text = ASCIIEncoding.ASCII.GetString(descriptorBytes);
  31. List<string> lines = VirtualMachineDiskDescriptor.GetLines(text);
  32. m_descriptor = new VirtualMachineDiskDescriptor(lines);
  33. }
  34. }
  35. }
  36. public override bool ExclusiveLock()
  37. {
  38. return m_file.ExclusiveLock();
  39. }
  40. public override bool ReleaseLock()
  41. {
  42. return m_file.ReleaseLock();
  43. }
  44. private KeyValuePairList<long, int> MapSectors(long sectorIndex, int sectorCount)
  45. {
  46. if (m_grainTableStartSector == null)
  47. {
  48. byte[] grainDirectoryBytes = m_file.ReadSectors((long)m_header.GDOffset, 1);
  49. m_grainTableStartSector = LittleEndianConverter.ToUInt32(grainDirectoryBytes, 0);
  50. }
  51. long grainIndex = sectorIndex / (long)m_header.GrainSize;
  52. long grainSectorIndexInTable = grainIndex / 128;
  53. int grainIndexInBuffer = (int)grainIndex % 128;
  54. int sectorsToReadFromTable = (int)Math.Max(Math.Ceiling((double)(sectorCount - (128 - grainIndexInBuffer)) / 4), 1);
  55. byte[] grainTableBuffer = m_file.ReadSectors(m_grainTableStartSector.Value + grainSectorIndexInTable, sectorsToReadFromTable);
  56. long sectorIndexInGrain = sectorIndex % (long)m_header.GrainSize;
  57. KeyValuePairList<long, int> result = new KeyValuePairList<long, int>();
  58. uint grainOffset = LittleEndianConverter.ToUInt32(grainTableBuffer, grainIndexInBuffer * 4);
  59. grainOffset += (uint)sectorIndexInGrain;
  60. int sectorsLeft = sectorCount;
  61. int sectorsProcessedInGrain = (int)Math.Min(sectorsLeft, (long)m_header.GrainSize - sectorIndexInGrain);
  62. result.Add(grainOffset, sectorsProcessedInGrain);
  63. sectorsLeft -= sectorsProcessedInGrain;
  64. while (sectorsLeft > 0)
  65. {
  66. grainIndexInBuffer++;
  67. grainOffset = LittleEndianConverter.ToUInt32(grainTableBuffer, grainIndexInBuffer * 4);
  68. sectorsProcessedInGrain = (int)Math.Min(sectorsLeft, (long)m_header.GrainSize);
  69. long lastSectorIndex = result[result.Count - 1].Key;
  70. int lastSectorCount = result[result.Count - 1].Value;
  71. if (lastSectorIndex + lastSectorCount == grainOffset)
  72. {
  73. result[result.Count - 1] = new KeyValuePair<long, int>(lastSectorIndex, lastSectorCount + sectorsProcessedInGrain);
  74. }
  75. else
  76. {
  77. result.Add(grainOffset, sectorsProcessedInGrain);
  78. }
  79. sectorsLeft -= sectorsProcessedInGrain;
  80. }
  81. return result;
  82. }
  83. public override byte[] ReadSectors(long sectorIndex, int sectorCount)
  84. {
  85. CheckBoundaries(sectorIndex, sectorCount);
  86. byte[] result = new byte[sectorCount * this.BytesPerSector];
  87. int offset = 0;
  88. KeyValuePairList<long, int> map = MapSectors(sectorIndex, sectorCount);
  89. foreach (KeyValuePair<long, int> entry in map)
  90. {
  91. byte[] temp;
  92. if (entry.Key == 0) // 0 means that the grain is not yet allocated
  93. {
  94. temp = new byte[entry.Value * this.BytesPerSector];
  95. }
  96. else
  97. {
  98. temp = m_file.ReadSectors(entry.Key, entry.Value);
  99. }
  100. Array.Copy(temp, 0, result, offset, temp.Length);
  101. offset += temp.Length;
  102. }
  103. return result;
  104. }
  105. public override void WriteSectors(long sectorIndex, byte[] data)
  106. {
  107. throw new NotImplementedException("The method or operation is not implemented.");
  108. }
  109. public override void Extend(long numberOfAdditionalBytes)
  110. {
  111. throw new NotImplementedException("The method or operation is not implemented.");
  112. }
  113. public override int BytesPerSector
  114. {
  115. get
  116. {
  117. return VirtualMachineDisk.BytesPerDiskSector;
  118. }
  119. }
  120. public override long Size
  121. {
  122. get
  123. {
  124. return (long)(m_header.Capacity * (ulong)this.BytesPerSector);
  125. }
  126. }
  127. public VirtualMachineDiskDescriptor Descriptor
  128. {
  129. get
  130. {
  131. return m_descriptor;
  132. }
  133. }
  134. }
  135. }