DynamicDiskExtentHelper.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  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.Text;
  10. using DiskAccessLibrary.LogicalDiskManager;
  11. using Utilities;
  12. namespace DiskAccessLibrary
  13. {
  14. public class DynamicDiskExtentHelper
  15. {
  16. public static int GetIndexOfExtentID(List<DynamicDiskExtent> extents, ulong extentID)
  17. {
  18. for (int index = 0; index < extents.Count; index++)
  19. {
  20. if (extents[index].ExtentID == extentID)
  21. {
  22. return index;
  23. }
  24. }
  25. return -1;
  26. }
  27. public static DynamicDiskExtent GetByExtentID(List<DynamicDiskExtent> extents, ulong extentID)
  28. {
  29. int index = GetIndexOfExtentID(extents, extentID);
  30. if (index >= 0)
  31. {
  32. return extents[index];
  33. }
  34. else
  35. {
  36. return null;
  37. }
  38. }
  39. /// <summary>
  40. /// Support null disks
  41. /// </summary>
  42. public static DynamicDiskExtent GetDiskExtent(DynamicDisk dynamicDisk, ExtentRecord extentRecord)
  43. {
  44. ulong extentStartSector = GetExtentStartSector(dynamicDisk, extentRecord);
  45. int bytesPerSector = 512; // default for missing disk
  46. Disk disk = null;
  47. Guid diskGuid = Guid.Empty;
  48. if (dynamicDisk != null)
  49. {
  50. bytesPerSector = dynamicDisk.BytesPerSector;
  51. disk = dynamicDisk.Disk;
  52. diskGuid = dynamicDisk.DiskGuid;
  53. }
  54. DynamicDiskExtent extent = new DynamicDiskExtent(disk, (long)extentStartSector, (long)extentRecord.SizeLBA * bytesPerSector, extentRecord.ExtentId);
  55. extent.Name = extentRecord.Name;
  56. extent.DiskGuid = diskGuid;
  57. return extent;
  58. }
  59. /// <summary>
  60. /// Support null disks
  61. /// </summary>
  62. public static ulong GetExtentStartSector(DynamicDisk disk, ExtentRecord extentRecord)
  63. {
  64. ulong dataStartLBA = 0;
  65. if (disk != null)
  66. {
  67. PrivateHeader privateHeader = disk.PrivateHeader;
  68. dataStartLBA = privateHeader.PublicRegionStartLBA;
  69. }
  70. ulong extentStartSector = dataStartLBA + extentRecord.DiskOffsetLBA;
  71. return extentStartSector;
  72. }
  73. /// <param name="targetOffset">in bytes</param>
  74. public static bool IsMoveLocationValid(DynamicDisk disk, DynamicDiskExtent sourceExtent, long targetOffset)
  75. {
  76. List<DynamicDiskExtent> extents = GetDiskExtents(disk);
  77. // extents are sorted by first sector
  78. if (extents == null)
  79. {
  80. return false;
  81. }
  82. PrivateHeader privateHeader = disk.PrivateHeader;
  83. if (targetOffset % privateHeader.BytesPerSector > 0)
  84. {
  85. return false;
  86. }
  87. int index = GetIndexOfExtentID(extents, sourceExtent.ExtentID);
  88. extents.RemoveAt(index);
  89. long targetStartSector = targetOffset / disk.BytesPerSector;
  90. long publicRegionStartSector = (long)privateHeader.PublicRegionStartLBA;
  91. long startSector = publicRegionStartSector;
  92. long publicRegionSizeLBA = (long)privateHeader.PublicRegionSizeLBA;
  93. if (targetStartSector < publicRegionStartSector)
  94. {
  95. return false;
  96. }
  97. if (targetStartSector + sourceExtent.TotalSectors > publicRegionStartSector + publicRegionSizeLBA)
  98. {
  99. return false;
  100. }
  101. foreach (DynamicDiskExtent extent in extents)
  102. {
  103. long extentStartSector = extent.FirstSector;
  104. long extentEndSector = extent.FirstSector + extent.Size / disk.BytesPerSector - 1;
  105. if (extentStartSector >= targetStartSector &&
  106. extentStartSector <= targetStartSector + sourceExtent.TotalSectors)
  107. {
  108. // extent start within the requested region
  109. return false;
  110. }
  111. if (extentEndSector >= targetStartSector &&
  112. extentEndSector <= targetStartSector + sourceExtent.TotalSectors)
  113. {
  114. // extent end within the requested region
  115. return false;
  116. }
  117. }
  118. return true;
  119. }
  120. public static DiskExtent AllocateNewExtent(DynamicDisk disk, long allocationLength)
  121. {
  122. return AllocateNewExtent(disk, allocationLength, 0);
  123. }
  124. /// <param name="allocationLength">In bytes</param>
  125. /// <param name="alignInSectors">0 or 1 for no alignment</param>
  126. /// <returns>Allocated DiskExtent or null if there is not enough free disk space</returns>
  127. public static DiskExtent AllocateNewExtent(DynamicDisk disk, long allocationLength, long alignInSectors)
  128. {
  129. List<DiskExtent> unallocatedExtents = GetUnallocatedSpace(disk);
  130. if (unallocatedExtents == null)
  131. {
  132. return null;
  133. }
  134. for (int index = 0; index < unallocatedExtents.Count; index++)
  135. {
  136. DiskExtent extent = unallocatedExtents[index];
  137. if (alignInSectors > 1)
  138. {
  139. extent = DiskExtentHelper.GetAlignedDiskExtent(extent, alignInSectors);
  140. }
  141. if (extent.Size >= allocationLength)
  142. {
  143. return new DiskExtent(extent.Disk, extent.FirstSector, allocationLength);
  144. }
  145. }
  146. return null;
  147. }
  148. public static long GetMaxNewExtentLength(DynamicDisk disk)
  149. {
  150. return GetMaxNewExtentLength(disk, 0);
  151. }
  152. /// <returns>In bytes</returns>
  153. public static long GetMaxNewExtentLength(DynamicDisk disk, long alignInSectors)
  154. {
  155. List<DiskExtent> unallocatedExtents = GetUnallocatedSpace(disk);
  156. if (unallocatedExtents == null)
  157. {
  158. return -1;
  159. }
  160. long result = 0;
  161. for(int index = 0; index < unallocatedExtents.Count; index++)
  162. {
  163. DiskExtent extent = unallocatedExtents[index];
  164. if (alignInSectors > 1)
  165. {
  166. extent = DiskExtentHelper.GetAlignedDiskExtent(extent, alignInSectors);
  167. }
  168. if (extent.Size > result)
  169. {
  170. result = extent.Size;
  171. }
  172. }
  173. return result;
  174. }
  175. private static List<DiskExtent> GetUnallocatedSpace(DynamicDisk disk)
  176. {
  177. List<DynamicDiskExtent> extents = GetDiskExtents(disk);
  178. // extents are sorted by first sector
  179. if (extents == null)
  180. {
  181. return null;
  182. }
  183. List<DiskExtent> result = new List<DiskExtent>();
  184. PrivateHeader privateHeader = disk.PrivateHeader;
  185. long publicRegionStartSector = (long)privateHeader.PublicRegionStartLBA;
  186. long startSector = publicRegionStartSector;
  187. long publicRegionSize = (long)privateHeader.PublicRegionSizeLBA * disk.Disk.BytesPerSector;
  188. // see if there is room before each extent
  189. foreach (DynamicDiskExtent extent in extents)
  190. {
  191. long extentStartSector = extent.FirstSector;
  192. long nextStartSector = extent.FirstSector + extent.Size / disk.BytesPerSector;
  193. long freeSpaceInBytes = (extentStartSector - startSector) * disk.BytesPerSector;
  194. if (freeSpaceInBytes > 0)
  195. {
  196. result.Add(new DiskExtent(disk.Disk, startSector, freeSpaceInBytes));
  197. }
  198. startSector = nextStartSector;
  199. }
  200. // see if there is room after the last extent
  201. long spaceInBytes = publicRegionSize - (startSector - publicRegionStartSector) * disk.Disk.BytesPerSector;
  202. if (spaceInBytes > 0)
  203. {
  204. result.Add(new DiskExtent(disk.Disk, startSector, spaceInBytes));
  205. }
  206. return result;
  207. }
  208. /// <summary>
  209. /// Sorted by first sector
  210. /// </summary>
  211. /// <returns>null if there was a problem reading extent information from disk</returns>
  212. public static List<DynamicDiskExtent> GetDiskExtents(DynamicDisk disk)
  213. {
  214. List<DynamicDiskExtent> result = new List<DynamicDiskExtent>();
  215. PrivateHeader privateHeader = disk.PrivateHeader;
  216. if (privateHeader != null)
  217. {
  218. VolumeManagerDatabase database = VolumeManagerDatabase.ReadFromDisk(disk);
  219. if (database != null)
  220. {
  221. DiskRecord diskRecord = database.FindDiskByDiskGuid(privateHeader.DiskGuid);
  222. List<ExtentRecord> extentRecords = database.FindExtentsByDiskID(diskRecord.DiskId);
  223. foreach (ExtentRecord extentRecord in extentRecords)
  224. {
  225. DynamicDiskExtent extent = GetDiskExtent(disk, extentRecord);
  226. result.Add(extent);
  227. }
  228. SortExtentsByFirstSector(result);
  229. return result;
  230. }
  231. }
  232. return null;
  233. }
  234. /// <summary>
  235. /// Sort (in-place) extents by first sector
  236. /// </summary>
  237. public static void SortExtentsByFirstSector(List<DynamicDiskExtent> extents)
  238. {
  239. SortedList<long, DynamicDiskExtent> list = new SortedList<long, DynamicDiskExtent>();
  240. foreach (DynamicDiskExtent extent in extents)
  241. {
  242. list.Add(extent.FirstSector, extent);
  243. }
  244. extents.Clear();
  245. foreach (DynamicDiskExtent extent in list.Values)
  246. {
  247. extents.Add(extent);
  248. }
  249. }
  250. }
  251. }