// // 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. // using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Reflection; using DiscUtils.CoreCompat; using DiscUtils.Internal; using DiscUtils.Partitions; using DiscUtils.Raw; using DiscUtils.Streams; namespace DiscUtils { /// /// VolumeManager interprets partitions and other on-disk structures (possibly combining multiple disks). /// /// /// 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. /// public sealed class VolumeManager #if !NETCORE : MarshalByRefObject #endif { private static List s_logicalVolumeFactories; private readonly List _disks; private bool _needScan; private Dictionary _physicalVolumes; private Dictionary _logicalVolumes; private static readonly Assembly _coreAssembly = ReflectionHelper.GetAssembly(typeof(VolumeManager)); /// /// Initializes a new instance of the VolumeManager class. /// public VolumeManager() { _disks = new List(); _physicalVolumes = new Dictionary(); _logicalVolumes = new Dictionary(); } /// /// Initializes a new instance of the VolumeManager class. /// /// The initial disk to add. public VolumeManager(VirtualDisk initialDisk) : this() { AddDisk(initialDisk); } /// /// Initializes a new instance of the VolumeManager class. /// /// Content of the initial disk to add. public VolumeManager(Stream initialDiskContent) : this() { AddDisk(initialDiskContent); } private static List LogicalVolumeFactories { get { if (s_logicalVolumeFactories == null) { List factories = new List(); factories.AddRange(GetLogicalVolumeFactories(_coreAssembly)); s_logicalVolumeFactories = factories; } return s_logicalVolumeFactories; } } private static IEnumerable GetLogicalVolumeFactories(Assembly assembly) { foreach (Type type in assembly.GetTypes()) { foreach (LogicalVolumeFactoryAttribute attr in ReflectionHelper.GetCustomAttributes(type, typeof(LogicalVolumeFactoryAttribute), false)) { yield return (LogicalVolumeFactory)Activator.CreateInstance(type); } } } /// /// Register new LogicalVolumeFactories detected in an assembly /// /// The assembly to inspect public static void RegisterLogicalVolumeFactory(Assembly assembly) { if (assembly == _coreAssembly) return; LogicalVolumeFactories.AddRange(GetLogicalVolumeFactories(assembly)); } /// /// Gets the physical volumes held on a disk. /// /// The contents of the disk to inspect. /// An array of volumes. /// /// By preference, use the form of this method that takes a disk parameter. /// If the disk isn't partitioned, this method returns the entire disk contents /// as a single volume. /// public static PhysicalVolumeInfo[] GetPhysicalVolumes(Stream diskContent) { return GetPhysicalVolumes(new Disk(diskContent, Ownership.None)); } /// /// Gets the physical volumes held on a disk. /// /// The disk to inspect. /// An array of volumes. /// If the disk isn't partitioned, this method returns the entire disk contents /// as a single volume. public static PhysicalVolumeInfo[] GetPhysicalVolumes(VirtualDisk disk) { return new VolumeManager(disk).GetPhysicalVolumes(); } /// /// Adds a disk to the volume manager. /// /// The disk to add. /// The GUID the volume manager will use to identify the disk. public string AddDisk(VirtualDisk disk) { _needScan = true; int ordinal = _disks.Count; _disks.Add(disk); return GetDiskId(ordinal); } /// /// Adds a disk to the volume manager. /// /// The contents of the disk to add. /// The GUID the volume manager will use to identify the disk. public string AddDisk(Stream content) { return AddDisk(new Disk(content, Ownership.None)); } /// /// Gets the physical volumes from all disks added to this volume manager. /// /// An array of physical volumes. public PhysicalVolumeInfo[] GetPhysicalVolumes() { if (_needScan) { Scan(); } return new List(_physicalVolumes.Values).ToArray(); } /// /// Gets the logical volumes from all disks added to this volume manager. /// /// An array of logical volumes. public LogicalVolumeInfo[] GetLogicalVolumes() { if (_needScan) { Scan(); } return new List(_logicalVolumes.Values).ToArray(); } /// /// Gets a particular volume, based on it's identity. /// /// The volume's identity. /// The volume information for the volume, or returns null. 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 physicalVols, Dictionary result) { foreach (PhysicalVolumeInfo physicalVol in physicalVols) { LogicalVolumeInfo lvi = new LogicalVolumeInfo( physicalVol.PartitionIdentity, physicalVol, physicalVol.Open, physicalVol.Length, physicalVol.BiosType, LogicalVolumeStatus.Healthy); result.Add(lvi.Identity, lvi); } } /// /// Scans all of the disks for their physical and logical volumes. /// private void Scan() { Dictionary newPhysicalVolumes = ScanForPhysicalVolumes(); Dictionary newLogicalVolumes = ScanForLogicalVolumes(newPhysicalVolumes.Values); _physicalVolumes = newPhysicalVolumes; _logicalVolumes = newLogicalVolumes; _needScan = false; } private Dictionary ScanForLogicalVolumes(IEnumerable physicalVols) { List unhandledPhysical = new List(); Dictionary result = new Dictionary(); foreach (PhysicalVolumeInfo pvi in physicalVols) { bool handled = false; foreach (LogicalVolumeFactory volFactory in LogicalVolumeFactories) { if (volFactory.HandlesPhysicalVolume(pvi)) { handled = true; break; } } if (!handled) { unhandledPhysical.Add(pvi); } } MapPhysicalVolumes(unhandledPhysical, result); foreach (LogicalVolumeFactory volFactory in LogicalVolumeFactories) { volFactory.MapDisks(_disks, result); } return result; } private Dictionary ScanForPhysicalVolumes() { Dictionary result = new Dictionary(); // 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 (PartitionTable table in PartitionTable.GetPartitionTables(disk)) { foreach (PartitionInfo 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; } } }