PageRenderTime 54ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/src/VolumeManager.cs

#
C# | 321 lines | 197 code | 40 blank | 84 comment | 16 complexity | a744b902a999631e17fb6e3e7c620d7a MD5 | raw file
  1. //
  2. // Copyright (c) 2008-2011, Kenneth Bell
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a
  5. // copy of this software and associated documentation files (the "Software"),
  6. // to deal in the Software without restriction, including without limitation
  7. // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. // and/or sell copies of the Software, and to permit persons to whom the
  9. // Software is furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  20. // DEALINGS IN THE SOFTWARE.
  21. //
  22. namespace DiscUtils
  23. {
  24. using System;
  25. using System.Collections.Generic;
  26. using System.Globalization;
  27. using System.IO;
  28. using DiscUtils.Partitions;
  29. /// <summary>
  30. /// VolumeManager interprets partitions and other on-disk structures (possibly combining multiple disks).
  31. /// </summary>
  32. /// <remarks>
  33. /// <para>Although file systems commonly are placed directly within partitions on a disk, in some
  34. /// cases a logical volume manager / logical disk manager may be used, to combine disk regions in multiple
  35. /// ways for data redundancy or other purposes.</para>
  36. /// </remarks>
  37. public sealed class VolumeManager : MarshalByRefObject
  38. {
  39. private static List<LogicalVolumeFactory> s_logicalVolumeFactories;
  40. private List<VirtualDisk> _disks;
  41. private bool _needScan;
  42. private Dictionary<string, PhysicalVolumeInfo> _physicalVolumes;
  43. private Dictionary<string, LogicalVolumeInfo> _logicalVolumes;
  44. /// <summary>
  45. /// Initializes a new instance of the VolumeManager class.
  46. /// </summary>
  47. public VolumeManager()
  48. {
  49. _disks = new List<VirtualDisk>();
  50. _physicalVolumes = new Dictionary<string, PhysicalVolumeInfo>();
  51. _logicalVolumes = new Dictionary<string, LogicalVolumeInfo>();
  52. }
  53. /// <summary>
  54. /// Initializes a new instance of the VolumeManager class.
  55. /// </summary>
  56. /// <param name="initialDisk">The initial disk to add.</param>
  57. public VolumeManager(VirtualDisk initialDisk)
  58. : this()
  59. {
  60. AddDisk(initialDisk);
  61. }
  62. /// <summary>
  63. /// Initializes a new instance of the VolumeManager class.
  64. /// </summary>
  65. /// <param name="initialDiskContent">Content of the initial disk to add.</param>
  66. public VolumeManager(Stream initialDiskContent)
  67. : this()
  68. {
  69. AddDisk(initialDiskContent);
  70. }
  71. private static List<LogicalVolumeFactory> LogicalVolumeFactories
  72. {
  73. get
  74. {
  75. if (s_logicalVolumeFactories == null)
  76. {
  77. List<LogicalVolumeFactory> factories = new List<LogicalVolumeFactory>();
  78. foreach (var type in typeof(VolumeManager).Assembly.GetTypes())
  79. {
  80. foreach (LogicalVolumeFactoryAttribute attr in Attribute.GetCustomAttributes(type, typeof(LogicalVolumeFactoryAttribute), false))
  81. {
  82. factories.Add((LogicalVolumeFactory)Activator.CreateInstance(type));
  83. }
  84. }
  85. s_logicalVolumeFactories = factories;
  86. }
  87. return s_logicalVolumeFactories;
  88. }
  89. }
  90. /// <summary>
  91. /// Gets the physical volumes held on a disk.
  92. /// </summary>
  93. /// <param name="diskContent">The contents of the disk to inspect.</param>
  94. /// <returns>An array of volumes.</returns>
  95. /// <remarks>
  96. /// <para>By preference, use the form of this method that takes a disk parameter.</para>
  97. /// <para>If the disk isn't partitioned, this method returns the entire disk contents
  98. /// as a single volume.</para>
  99. /// </remarks>
  100. public static PhysicalVolumeInfo[] GetPhysicalVolumes(Stream diskContent)
  101. {
  102. return GetPhysicalVolumes(new Raw.Disk(diskContent, Ownership.None));
  103. }
  104. /// <summary>
  105. /// Gets the physical volumes held on a disk.
  106. /// </summary>
  107. /// <param name="disk">The disk to inspect.</param>
  108. /// <returns>An array of volumes.</returns>
  109. /// <remarks>If the disk isn't partitioned, this method returns the entire disk contents
  110. /// as a single volume.</remarks>
  111. public static PhysicalVolumeInfo[] GetPhysicalVolumes(VirtualDisk disk)
  112. {
  113. return new VolumeManager(disk).GetPhysicalVolumes();
  114. }
  115. /// <summary>
  116. /// Adds a disk to the volume manager.
  117. /// </summary>
  118. /// <param name="disk">The disk to add.</param>
  119. /// <returns>The GUID the volume manager will use to identify the disk.</returns>
  120. public string AddDisk(VirtualDisk disk)
  121. {
  122. _needScan = true;
  123. int ordinal = _disks.Count;
  124. _disks.Add(disk);
  125. return GetDiskId(ordinal);
  126. }
  127. /// <summary>
  128. /// Adds a disk to the volume manager.
  129. /// </summary>
  130. /// <param name="content">The contents of the disk to add.</param>
  131. /// <returns>The GUID the volume manager will use to identify the disk.</returns>
  132. public string AddDisk(Stream content)
  133. {
  134. return AddDisk(new Raw.Disk(content, Ownership.None));
  135. }
  136. /// <summary>
  137. /// Gets the physical volumes from all disks added to this volume manager.
  138. /// </summary>
  139. /// <returns>An array of physical volumes.</returns>
  140. public PhysicalVolumeInfo[] GetPhysicalVolumes()
  141. {
  142. if (_needScan)
  143. {
  144. Scan();
  145. }
  146. return new List<PhysicalVolumeInfo>(_physicalVolumes.Values).ToArray();
  147. }
  148. /// <summary>
  149. /// Gets the logical volumes from all disks added to this volume manager.
  150. /// </summary>
  151. /// <returns>An array of logical volumes.</returns>
  152. public LogicalVolumeInfo[] GetLogicalVolumes()
  153. {
  154. if (_needScan)
  155. {
  156. Scan();
  157. }
  158. return new List<LogicalVolumeInfo>(_logicalVolumes.Values).ToArray();
  159. }
  160. /// <summary>
  161. /// Gets a particular volume, based on it's identity.
  162. /// </summary>
  163. /// <param name="identity">The volume's identity.</param>
  164. /// <returns>The volume information for the volume, or returns <c>null</c>.</returns>
  165. public VolumeInfo GetVolume(string identity)
  166. {
  167. if (_needScan)
  168. {
  169. Scan();
  170. }
  171. PhysicalVolumeInfo pvi;
  172. if (_physicalVolumes.TryGetValue(identity, out pvi))
  173. {
  174. return pvi;
  175. }
  176. LogicalVolumeInfo lvi;
  177. if (_logicalVolumes.TryGetValue(identity, out lvi))
  178. {
  179. return lvi;
  180. }
  181. return null;
  182. }
  183. private static void MapPhysicalVolumes(IEnumerable<PhysicalVolumeInfo> physicalVols, Dictionary<string, LogicalVolumeInfo> result)
  184. {
  185. foreach (var physicalVol in physicalVols)
  186. {
  187. LogicalVolumeInfo lvi = new LogicalVolumeInfo(
  188. physicalVol.PartitionIdentity,
  189. physicalVol,
  190. physicalVol.Open,
  191. physicalVol.Length,
  192. physicalVol.BiosType,
  193. LogicalVolumeStatus.Healthy);
  194. result.Add(lvi.Identity, lvi);
  195. }
  196. }
  197. /// <summary>
  198. /// Scans all of the disks for their physical and logical volumes.
  199. /// </summary>
  200. private void Scan()
  201. {
  202. Dictionary<string, PhysicalVolumeInfo> newPhysicalVolumes = ScanForPhysicalVolumes();
  203. Dictionary<string, LogicalVolumeInfo> newLogicalVolumes = ScanForLogicalVolumes(newPhysicalVolumes.Values);
  204. _physicalVolumes = newPhysicalVolumes;
  205. _logicalVolumes = newLogicalVolumes;
  206. _needScan = false;
  207. }
  208. private Dictionary<string, LogicalVolumeInfo> ScanForLogicalVolumes(IEnumerable<PhysicalVolumeInfo> physicalVols)
  209. {
  210. List<PhysicalVolumeInfo> unhandledPhysical = new List<PhysicalVolumeInfo>();
  211. Dictionary<string, LogicalVolumeInfo> result = new Dictionary<string, LogicalVolumeInfo>();
  212. foreach (PhysicalVolumeInfo pvi in physicalVols)
  213. {
  214. bool handled = false;
  215. foreach (var volFactory in LogicalVolumeFactories)
  216. {
  217. if (volFactory.HandlesPhysicalVolume(pvi))
  218. {
  219. handled = true;
  220. break;
  221. }
  222. }
  223. if (!handled)
  224. {
  225. unhandledPhysical.Add(pvi);
  226. }
  227. }
  228. MapPhysicalVolumes(unhandledPhysical, result);
  229. foreach (var volFactory in LogicalVolumeFactories)
  230. {
  231. volFactory.MapDisks(_disks, result);
  232. }
  233. return result;
  234. }
  235. private Dictionary<string, PhysicalVolumeInfo> ScanForPhysicalVolumes()
  236. {
  237. Dictionary<string, PhysicalVolumeInfo> result = new Dictionary<string, PhysicalVolumeInfo>();
  238. // First scan physical volumes
  239. for (int i = 0; i < _disks.Count; ++i)
  240. {
  241. VirtualDisk disk = _disks[i];
  242. string diskId = GetDiskId(i);
  243. if (PartitionTable.IsPartitioned(disk.Content))
  244. {
  245. foreach (var table in PartitionTable.GetPartitionTables(disk))
  246. {
  247. foreach (var part in table.Partitions)
  248. {
  249. PhysicalVolumeInfo pvi = new PhysicalVolumeInfo(diskId, disk, part);
  250. result.Add(pvi.Identity, pvi);
  251. }
  252. }
  253. }
  254. else
  255. {
  256. PhysicalVolumeInfo pvi = new PhysicalVolumeInfo(diskId, disk);
  257. result.Add(pvi.Identity, pvi);
  258. }
  259. }
  260. return result;
  261. }
  262. private string GetDiskId(int ordinal)
  263. {
  264. VirtualDisk disk = _disks[ordinal];
  265. if (disk.IsPartitioned)
  266. {
  267. Guid guid = disk.Partitions.DiskGuid;
  268. if (guid != Guid.Empty)
  269. {
  270. return "DG" + guid.ToString("B");
  271. }
  272. }
  273. int sig = disk.Signature;
  274. if (sig != 0)
  275. {
  276. return "DS" + sig.ToString("X8", CultureInfo.InvariantCulture);
  277. }
  278. return "DO" + ordinal;
  279. }
  280. }
  281. }