/src/VolumeManager.cs
# · C# · 321 lines · 197 code · 40 blank · 84 comment · 16 complexity · a744b902a999631e17fb6e3e7c620d7a MD5 · raw file
- //
- // Copyright (c) 2008-2011, Kenneth Bell
- //
- // Permission is hereby granted, free of charge, to any person obtaining a
- // copy of this software and associated documentation files (the "Software"),
- // to deal in the Software without restriction, including without limitation
- // the rights to use, copy, modify, merge, publish, distribute, sublicense,
- // and/or sell copies of the Software, and to permit persons to whom the
- // Software is furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- // DEALINGS IN THE SOFTWARE.
- //
-
- namespace DiscUtils
- {
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.IO;
- using DiscUtils.Partitions;
-
- /// <summary>
- /// VolumeManager interprets partitions and other on-disk structures (possibly combining multiple disks).
- /// </summary>
- /// <remarks>
- /// <para>Although file systems commonly are placed directly within partitions on a disk, in some
- /// cases a logical volume manager / logical disk manager may be used, to combine disk regions in multiple
- /// ways for data redundancy or other purposes.</para>
- /// </remarks>
- public sealed class VolumeManager : MarshalByRefObject
- {
- private static List<LogicalVolumeFactory> s_logicalVolumeFactories;
- private List<VirtualDisk> _disks;
- private bool _needScan;
-
- private Dictionary<string, PhysicalVolumeInfo> _physicalVolumes;
- private Dictionary<string, LogicalVolumeInfo> _logicalVolumes;
-
- /// <summary>
- /// Initializes a new instance of the VolumeManager class.
- /// </summary>
- public VolumeManager()
- {
- _disks = new List<VirtualDisk>();
- _physicalVolumes = new Dictionary<string, PhysicalVolumeInfo>();
- _logicalVolumes = new Dictionary<string, LogicalVolumeInfo>();
- }
-
- /// <summary>
- /// Initializes a new instance of the VolumeManager class.
- /// </summary>
- /// <param name="initialDisk">The initial disk to add.</param>
- public VolumeManager(VirtualDisk initialDisk)
- : this()
- {
- AddDisk(initialDisk);
- }
-
- /// <summary>
- /// Initializes a new instance of the VolumeManager class.
- /// </summary>
- /// <param name="initialDiskContent">Content of the initial disk to add.</param>
- public VolumeManager(Stream initialDiskContent)
- : this()
- {
- AddDisk(initialDiskContent);
- }
-
- private static List<LogicalVolumeFactory> LogicalVolumeFactories
- {
- get
- {
- if (s_logicalVolumeFactories == null)
- {
- List<LogicalVolumeFactory> factories = new List<LogicalVolumeFactory>();
-
- foreach (var type in typeof(VolumeManager).Assembly.GetTypes())
- {
- foreach (LogicalVolumeFactoryAttribute attr in Attribute.GetCustomAttributes(type, typeof(LogicalVolumeFactoryAttribute), false))
- {
- factories.Add((LogicalVolumeFactory)Activator.CreateInstance(type));
- }
- }
-
- s_logicalVolumeFactories = factories;
- }
-
- return s_logicalVolumeFactories;
- }
- }
-
- /// <summary>
- /// Gets the physical volumes held on a disk.
- /// </summary>
- /// <param name="diskContent">The contents of the disk to inspect.</param>
- /// <returns>An array of volumes.</returns>
- /// <remarks>
- /// <para>By preference, use the form of this method that takes a disk parameter.</para>
- /// <para>If the disk isn't partitioned, this method returns the entire disk contents
- /// as a single volume.</para>
- /// </remarks>
- public static PhysicalVolumeInfo[] GetPhysicalVolumes(Stream diskContent)
- {
- return GetPhysicalVolumes(new Raw.Disk(diskContent, Ownership.None));
- }
-
- /// <summary>
- /// Gets the physical volumes held on a disk.
- /// </summary>
- /// <param name="disk">The disk to inspect.</param>
- /// <returns>An array of volumes.</returns>
- /// <remarks>If the disk isn't partitioned, this method returns the entire disk contents
- /// as a single volume.</remarks>
- public static PhysicalVolumeInfo[] GetPhysicalVolumes(VirtualDisk disk)
- {
- return new VolumeManager(disk).GetPhysicalVolumes();
- }
-
- /// <summary>
- /// Adds a disk to the volume manager.
- /// </summary>
- /// <param name="disk">The disk to add.</param>
- /// <returns>The GUID the volume manager will use to identify the disk.</returns>
- public string AddDisk(VirtualDisk disk)
- {
- _needScan = true;
- int ordinal = _disks.Count;
- _disks.Add(disk);
- return GetDiskId(ordinal);
- }
-
- /// <summary>
- /// Adds a disk to the volume manager.
- /// </summary>
- /// <param name="content">The contents of the disk to add.</param>
- /// <returns>The GUID the volume manager will use to identify the disk.</returns>
- public string AddDisk(Stream content)
- {
- return AddDisk(new Raw.Disk(content, Ownership.None));
- }
-
- /// <summary>
- /// Gets the physical volumes from all disks added to this volume manager.
- /// </summary>
- /// <returns>An array of physical volumes.</returns>
- public PhysicalVolumeInfo[] GetPhysicalVolumes()
- {
- if (_needScan)
- {
- Scan();
- }
-
- return new List<PhysicalVolumeInfo>(_physicalVolumes.Values).ToArray();
- }
-
- /// <summary>
- /// Gets the logical volumes from all disks added to this volume manager.
- /// </summary>
- /// <returns>An array of logical volumes.</returns>
- public LogicalVolumeInfo[] GetLogicalVolumes()
- {
- if (_needScan)
- {
- Scan();
- }
-
- return new List<LogicalVolumeInfo>(_logicalVolumes.Values).ToArray();
- }
-
- /// <summary>
- /// Gets a particular volume, based on it's identity.
- /// </summary>
- /// <param name="identity">The volume's identity.</param>
- /// <returns>The volume information for the volume, or returns <c>null</c>.</returns>
- public VolumeInfo GetVolume(string identity)
- {
- if (_needScan)
- {
- Scan();
- }
-
- PhysicalVolumeInfo pvi;
- if (_physicalVolumes.TryGetValue(identity, out pvi))
- {
- return pvi;
- }
-
- LogicalVolumeInfo lvi;
- if (_logicalVolumes.TryGetValue(identity, out lvi))
- {
- return lvi;
- }
-
- return null;
- }
-
- private static void MapPhysicalVolumes(IEnumerable<PhysicalVolumeInfo> physicalVols, Dictionary<string, LogicalVolumeInfo> result)
- {
- foreach (var physicalVol in physicalVols)
- {
- LogicalVolumeInfo lvi = new LogicalVolumeInfo(
- physicalVol.PartitionIdentity,
- physicalVol,
- physicalVol.Open,
- physicalVol.Length,
- physicalVol.BiosType,
- LogicalVolumeStatus.Healthy);
-
- result.Add(lvi.Identity, lvi);
- }
- }
-
- /// <summary>
- /// Scans all of the disks for their physical and logical volumes.
- /// </summary>
- private void Scan()
- {
- Dictionary<string, PhysicalVolumeInfo> newPhysicalVolumes = ScanForPhysicalVolumes();
- Dictionary<string, LogicalVolumeInfo> newLogicalVolumes = ScanForLogicalVolumes(newPhysicalVolumes.Values);
-
- _physicalVolumes = newPhysicalVolumes;
- _logicalVolumes = newLogicalVolumes;
-
- _needScan = false;
- }
-
- private Dictionary<string, LogicalVolumeInfo> ScanForLogicalVolumes(IEnumerable<PhysicalVolumeInfo> physicalVols)
- {
- List<PhysicalVolumeInfo> unhandledPhysical = new List<PhysicalVolumeInfo>();
- Dictionary<string, LogicalVolumeInfo> result = new Dictionary<string, LogicalVolumeInfo>();
-
- foreach (PhysicalVolumeInfo pvi in physicalVols)
- {
- bool handled = false;
- foreach (var volFactory in LogicalVolumeFactories)
- {
- if (volFactory.HandlesPhysicalVolume(pvi))
- {
- handled = true;
- break;
- }
- }
-
- if (!handled)
- {
- unhandledPhysical.Add(pvi);
- }
- }
-
- MapPhysicalVolumes(unhandledPhysical, result);
-
- foreach (var volFactory in LogicalVolumeFactories)
- {
- volFactory.MapDisks(_disks, result);
- }
-
- return result;
- }
-
- private Dictionary<string, PhysicalVolumeInfo> ScanForPhysicalVolumes()
- {
- Dictionary<string, PhysicalVolumeInfo> result = new Dictionary<string, PhysicalVolumeInfo>();
-
- // First scan physical volumes
- for (int i = 0; i < _disks.Count; ++i)
- {
- VirtualDisk disk = _disks[i];
- string diskId = GetDiskId(i);
-
- if (PartitionTable.IsPartitioned(disk.Content))
- {
- foreach (var table in PartitionTable.GetPartitionTables(disk))
- {
- foreach (var part in table.Partitions)
- {
- PhysicalVolumeInfo pvi = new PhysicalVolumeInfo(diskId, disk, part);
- result.Add(pvi.Identity, pvi);
- }
- }
- }
- else
- {
- PhysicalVolumeInfo pvi = new PhysicalVolumeInfo(diskId, disk);
- result.Add(pvi.Identity, pvi);
- }
- }
-
- return result;
- }
-
- private string GetDiskId(int ordinal)
- {
- VirtualDisk disk = _disks[ordinal];
- if (disk.IsPartitioned)
- {
- Guid guid = disk.Partitions.DiskGuid;
- if (guid != Guid.Empty)
- {
- return "DG" + guid.ToString("B");
- }
- }
-
- int sig = disk.Signature;
- if (sig != 0)
- {
- return "DS" + sig.ToString("X8", CultureInfo.InvariantCulture);
- }
-
- return "DO" + ordinal;
- }
- }
- }