add chovysign-cli

This commit is contained in:
Li 2023-04-17 07:48:32 +12:00
parent 9cb01e4a4b
commit f5e9d5eaee
288 changed files with 33274 additions and 57 deletions

7
.gitignore vendored
View File

@ -8,10 +8,15 @@
PbpResign/bin/*
PbpResign/obj/*
ChovySign-CLI/bin/*
ChovySign-CLI/obj/*
DiscUtils/bin/*
DiscUtils/obj/*
PopsBuilder/bin/*
PopsBuilder/obj/*
PspCrypto/bin/*
PspCrypto/obj/*

View File

@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>ChovySign_CLI</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\PopsBuilder\GameBuilder.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

272
ChovySign-CLI/Program.cs Normal file
View File

@ -0,0 +1,272 @@
using GameBuilder.Pops;
using GameBuilder.Progress;
using GameBuilder.Psp;
using GameBuilder.VersionKey;
namespace ChovySign_CLI
{
internal class Program
{
private static ArgumentParsingMode mode = ArgumentParsingMode.ARG;
private static List<string> parameters = new List<string>();
private static string[] discs;
private static bool pspCompress = false;
private static string? popsDiscName;
private static string? popsIcon0File;
private static string? popsPic0File;
private static PbpMode? pbpMode = null;
private static NpDrmInfo? drmInfo = null;
enum PbpMode
{
PSP = 0,
POPS = 1,
PCENGINE = 2,
NEOGEO = 3
}
enum ArgumentParsingMode
{
ARG = 0,
POPS_DISC = 1,
PSP_UMD = 2,
VERSIONKEY = 3,
VERSIONKEY_EXTRACT = 4,
VERSIONKEY_GENERATOR = 5,
POPS_INFO = 6
}
public static int Error(string errorMsg, int ret)
{
Console.Error.WriteLine("ERROR: "+errorMsg);
return ret;
}
public static byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length).Where(x => x % 2 == 0).Select(x => Convert.ToByte(hex.Substring(x, 2), 16)).ToArray();
}
private static void onProgress(ProgressInfo info)
{
Console.Write(info.CurrentProcess + " " + info.ProgressInt.ToString() + "% (" + info.Done + "/" + info.Remain + ") \r");
}
private static int complete()
{
switch (mode)
{
case ArgumentParsingMode.POPS_DISC:
if (parameters.Count > 5) return Error("--pops: no more than 5 disc images allowed in a single game (sony's rules, not mine)", 5);
if (parameters.Count < 1) return Error("--pops: at least 1 disc image file is required.", 5);
discs = parameters.ToArray();
break;
case ArgumentParsingMode.PSP_UMD:
if (parameters.Count < 1) return Error("--psp: a path to a disc image is required", 5);
if (parameters.Count > 2) return Error("--psp: no more than 2 arguments. ("+parameters.Count+" given)", 5);
discs = new string[1];
discs[0] = parameters[0];
if (parameters.Count > 1)
pspCompress = parameters[1].ToLowerInvariant() == "true";
else
pspCompress = false;
break;
case ArgumentParsingMode.VERSIONKEY:
if (parameters.Count != 3) return Error("--vkey: expect 3 arguments. ("+parameters.Count+" given)", 4);
drmInfo = new NpDrmInfo(StringToByteArray(parameters[0]), parameters[1], int.Parse(parameters[2]));
break;
case ArgumentParsingMode.VERSIONKEY_EXTRACT:
if (parameters.Count != 1) return Error("--vkey-extract: expect 1 arguments. ("+parameters.Count+" given)", 4);
drmInfo = EbootPbpMethod.GetVersionKey(File.OpenRead(parameters[0]));
break;
case ArgumentParsingMode.VERSIONKEY_GENERATOR:
if(parameters.Count != 4) return Error("--vkey-gen: expect 4 arguments. ("+parameters.Count+" given)", 4);
drmInfo = ActRifMethod.GetVersionKey(File.ReadAllBytes(parameters[0]), File.ReadAllBytes(parameters[1]), StringToByteArray(parameters[2]), int.Parse(parameters[3]));
break;
case ArgumentParsingMode.POPS_INFO:
if (parameters.Count < 2) return Error("--pops-info takes at least 2 arguments ("+parameters.Count+" given)", 4);
if (parameters.Count > 3) return Error("--pops-info takes no more than 3 arguments("+parameters.Count+" given)", 4);
popsDiscName = parameters[0];
popsIcon0File = parameters[1];
if (parameters.Count > 2)
popsPic0File = parameters[2];
break;
}
mode = ArgumentParsingMode.ARG;
parameters.Clear();
return 0;
}
public static int Main(string[] args)
{
if(args.Length == 0)
{
Console.WriteLine("Chovy-Sign v2 (CLI)");
Console.WriteLine("--pops [disc1.cue] [disc2.cue] [disc3.cue] ... (up to 5)");
Console.WriteLine("--pops-info [game title] [icon0.png] [pic1.png] (optional)");
Console.WriteLine("--psp [umd.iso] [compress; true/false] (optional)");
Console.WriteLine("--vkey [versionkey] [contentid] [key_index]");
Console.WriteLine("--vkey-extract [eboot.pbp]");
Console.WriteLine("--vkey-gen [act.dat] [license.rif] [console_id] [key_index]");
}
foreach (string arg in args)
{
if (arg.StartsWith("--")) { int ret = complete(); if (ret != 0) return ret; }
switch (mode)
{
case ArgumentParsingMode.ARG:
switch (arg)
{
case "--pops":
mode = ArgumentParsingMode.POPS_DISC;
if (pbpMode is not null)
return Error("pbpMode is already set to: " + pbpMode.ToString() + " cannot do that *and* POPS", 2);
pbpMode = PbpMode.POPS;
break;
case "--pops-info":
mode = ArgumentParsingMode.POPS_INFO;
break;
case "--psp":
mode = ArgumentParsingMode.PSP_UMD;
if (pbpMode is not null)
return Error("pbpMode is already set to: " + pbpMode.ToString() + " cannot do that *and* PSP", 2);
pbpMode = PbpMode.PSP;
break;
case "--vkey":
mode = ArgumentParsingMode.VERSIONKEY;
if (drmInfo is not null)
return Error("versionkey is already set", 3);
break;
case "--vkey-extract":
mode = ArgumentParsingMode.VERSIONKEY_EXTRACT;
if (drmInfo is not null)
return Error("versionkey is already set", 3);
break;
case "--vkey-gen":
mode = ArgumentParsingMode.VERSIONKEY_GENERATOR;
if (drmInfo is not null)
return Error("versionkey is already set", 3);
break;
default:
return Error("Unknown argument: " + arg, 1);
}
break;
case ArgumentParsingMode.VERSIONKEY:
case ArgumentParsingMode.VERSIONKEY_GENERATOR:
case ArgumentParsingMode.VERSIONKEY_EXTRACT:
case ArgumentParsingMode.PSP_UMD:
case ArgumentParsingMode.POPS_DISC:
case ArgumentParsingMode.POPS_INFO:
default:
parameters.Add(arg);
break;
}
}
int res = complete();
if(res != 0) return res;
if (drmInfo is null) return Error("no versionkey was found, exiting", 6);
if (pbpMode is null) return Error("no pbp mode was set, exiting", 7);
if (pbpMode == PbpMode.PSP && drmInfo.KeyType != 2)
return Error("KeyType is "+drmInfo.KeyType+", but PBP mode is PSP, you cant do that .. please use a type 1 versionkey.", 8);
if (pbpMode == PbpMode.POPS && drmInfo.KeyType != 1)
return Error("KeyType is " + drmInfo.KeyType + ", but PBP mode is POPS, you cant do that .. please use a type 1 versionkey.", 8);
if (pbpMode == PbpMode.POPS && (popsDiscName is null || popsIcon0File is null)) return Error("pbp mode is POPS, but you have not specified a disc title or icon file using --pops-info.", 9);
if (pbpMode == PbpMode.POPS)
{
DiscInfo[] discInfs = new DiscInfo[discs.Length];
for (int i = 0; i < discInfs.Length; i++)
discInfs[i] = new DiscInfo(discs[i], popsDiscName);
Sfo psfo = new Sfo();
psfo.AddKey("BOOTABLE", 1, 4);
psfo.AddKey("CATEGORY", "ME", 4);
psfo.AddKey("DISC_ID", discInfs.First().DiscId, 16);
psfo.AddKey("DISC_VERSION", "1.00", 8);
psfo.AddKey("LICENSE", "Chovy-Sign is licensed under the GPLv3, POPS stuff was done by Li and SquallATF.", 512);
psfo.AddKey("PARENTAL_LEVEL", 0, 4);
psfo.AddKey("PSP_SYSTEM_VER", "6.60", 8);
psfo.AddKey("REGION", 32768, 4);
psfo.AddKey("TITLE", popsDiscName, 128);
if (discs.Length == 1)
{
using (PsIsoImg psIsoImg = new PsIsoImg(drmInfo, discInfs.First()))
{
psIsoImg.RegisterCallback(onProgress);
psIsoImg.CreatePsar();
PbpBuilder.CreatePbp(psfo.WriteSfo(),
File.ReadAllBytes(popsIcon0File),
null,
(popsPic0File is not null) ? File.ReadAllBytes(popsPic0File) : null,
Resources.PIC1,
null,
psIsoImg,
"EBOOT.PBP",
0);
}
}
else
{
using (PsTitleImg psIsoImg = new PsTitleImg(drmInfo, discInfs))
{
psIsoImg.RegisterCallback(onProgress);
psIsoImg.CreatePsar();
PbpBuilder.CreatePbp(psfo.WriteSfo(),
File.ReadAllBytes(popsIcon0File),
null,
(popsPic0File is not null) ? File.ReadAllBytes(popsPic0File) : null,
Resources.PIC1,
null,
psIsoImg,
"EBOOT.PBP",
0);
}
}
}
else if(pbpMode == PbpMode.PSP)
{
using (UmdInfo umd = new UmdInfo(discs.First()))
{
using (NpUmdImg npUmd = new NpUmdImg(drmInfo, umd, pspCompress))
{
npUmd.RegisterCallback(onProgress);
npUmd.CreatePsar();
PbpBuilder.CreatePbp(umd.DataFiles["PARAM.SFO"],
umd.DataFiles["ICON0.PNG"],
umd.DataFiles["ICON1.PMF"],
umd.DataFiles["PIC0.PNG"],
umd.DataFiles["PIC1.PNG"],
umd.DataFiles["SND0.AT3"],
npUmd,
"EBOOT.PBP",
1);
}
}
}
return 0;
}
}
}

73
ChovySign-CLI/Resources.Designer.cs generated Normal file
View File

@ -0,0 +1,73 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ChovySign_CLI {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ChovySign_CLI.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] PIC1 {
get {
object obj = ResourceManager.GetObject("PIC1", resourceCulture);
return ((byte[])(obj));
}
}
}
}

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="PIC1" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\PIC1.PNG;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,61 @@
//
// 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 DiscUtils.Streams;
namespace DiscUtils.ApplePartitionMap
{
internal sealed class BlockZero : IByteArraySerializable
{
public uint BlockCount;
public ushort BlockSize;
public ushort DeviceId;
public ushort DeviceType;
public ushort DriverCount;
public uint DriverData;
public ushort Signature;
public int Size
{
get { return 512; }
}
public int ReadFrom(byte[] buffer, int offset)
{
Signature = EndianUtilities.ToUInt16BigEndian(buffer, offset + 0);
BlockSize = EndianUtilities.ToUInt16BigEndian(buffer, offset + 2);
BlockCount = EndianUtilities.ToUInt32BigEndian(buffer, offset + 4);
DeviceType = EndianUtilities.ToUInt16BigEndian(buffer, offset + 8);
DeviceId = EndianUtilities.ToUInt16BigEndian(buffer, offset + 10);
DriverData = EndianUtilities.ToUInt32BigEndian(buffer, offset + 12);
DriverCount = EndianUtilities.ToUInt16LittleEndian(buffer, offset + 16);
return 512;
}
public void WriteTo(byte[] buffer, int offset)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,153 @@
//
// 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.ObjectModel;
using System.IO;
using DiscUtils.Partitions;
using DiscUtils.Streams;
namespace DiscUtils.ApplePartitionMap
{
/// <summary>
/// Interprets Apple Partition Map structures that partition a disk.
/// </summary>
public sealed class PartitionMap : PartitionTable
{
private readonly PartitionMapEntry[] _partitions;
private readonly Stream _stream;
/// <summary>
/// Initializes a new instance of the PartitionMap class.
/// </summary>
/// <param name="stream">Stream containing the contents of a disk.</param>
public PartitionMap(Stream stream)
{
_stream = stream;
stream.Position = 0;
byte[] initialBytes = StreamUtilities.ReadExact(stream, 1024);
BlockZero b0 = new BlockZero();
b0.ReadFrom(initialBytes, 0);
PartitionMapEntry initialPart = new PartitionMapEntry(_stream);
initialPart.ReadFrom(initialBytes, 512);
byte[] partTableData = StreamUtilities.ReadExact(stream, (int)(initialPart.MapEntries - 1) * 512);
_partitions = new PartitionMapEntry[initialPart.MapEntries - 1];
for (uint i = 0; i < initialPart.MapEntries - 1; ++i)
{
_partitions[i] = new PartitionMapEntry(_stream);
_partitions[i].ReadFrom(partTableData, (int)(512 * i));
}
}
/// <summary>
/// Gets the GUID of the disk, always returns Guid.Empty.
/// </summary>
public override Guid DiskGuid
{
get { return Guid.Empty; }
}
/// <summary>
/// Gets the partitions present on the disk.
/// </summary>
public override ReadOnlyCollection<PartitionInfo> Partitions
{
get { return new ReadOnlyCollection<PartitionInfo>(_partitions); }
}
/// <summary>
/// Creates a new partition that encompasses the entire disk.
/// </summary>
/// <param name="type">The partition type.</param>
/// <param name="active">Whether the partition is active (bootable).</param>
/// <returns>The index of the partition.</returns>
/// <remarks>The partition table must be empty before this method is called,
/// otherwise IOException is thrown.</remarks>
public override int Create(WellKnownPartitionType type, bool active)
{
throw new NotImplementedException();
}
/// <summary>
/// Creates a new partition with a target size.
/// </summary>
/// <param name="size">The target size (in bytes).</param>
/// <param name="type">The partition type.</param>
/// <param name="active">Whether the partition is active (bootable).</param>
/// <returns>The index of the new partition.</returns>
public override int Create(long size, WellKnownPartitionType type, bool active)
{
throw new NotImplementedException();
}
/// <summary>
/// Creates a new aligned partition that encompasses the entire disk.
/// </summary>
/// <param name="type">The partition type.</param>
/// <param name="active">Whether the partition is active (bootable).</param>
/// <param name="alignment">The alignment (in byte).</param>
/// <returns>The index of the partition.</returns>
/// <remarks>The partition table must be empty before this method is called,
/// otherwise IOException is thrown.</remarks>
/// <remarks>
/// Traditionally partitions were aligned to the physical structure of the underlying disk,
/// however with modern storage greater efficiency is acheived by aligning partitions on
/// large values that are a power of two.
/// </remarks>
public override int CreateAligned(WellKnownPartitionType type, bool active, int alignment)
{
throw new NotImplementedException();
}
/// <summary>
/// Creates a new aligned partition with a target size.
/// </summary>
/// <param name="size">The target size (in bytes).</param>
/// <param name="type">The partition type.</param>
/// <param name="active">Whether the partition is active (bootable).</param>
/// <param name="alignment">The alignment (in byte).</param>
/// <returns>The index of the new partition.</returns>
/// <remarks>
/// Traditionally partitions were aligned to the physical structure of the underlying disk,
/// however with modern storage greater efficiency is achieved by aligning partitions on
/// large values that are a power of two.
/// </remarks>
public override int CreateAligned(long size, WellKnownPartitionType type, bool active, int alignment)
{
throw new NotImplementedException();
}
/// <summary>
/// Deletes a partition at a given index.
/// </summary>
/// <param name="index">The index of the partition.</param>
public override void Delete(int index)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,112 @@
//
// 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.IO;
using DiscUtils.Partitions;
using DiscUtils.Streams;
namespace DiscUtils.ApplePartitionMap
{
internal sealed class PartitionMapEntry : PartitionInfo, IByteArraySerializable
{
private readonly Stream _diskStream;
public uint BootBlock;
public uint BootBytes;
public uint Flags;
public uint LogicalBlocks;
public uint LogicalBlockStart;
public uint MapEntries;
public string Name;
public uint PhysicalBlocks;
public uint PhysicalBlockStart;
public ushort Signature;
public string Type;
public PartitionMapEntry(Stream diskStream)
{
_diskStream = diskStream;
}
public override byte BiosType
{
get { return 0xAF; }
}
public override long FirstSector
{
get { return PhysicalBlockStart; }
}
public override Guid GuidType
{
get { return Guid.Empty; }
}
public override long LastSector
{
get { return PhysicalBlockStart + PhysicalBlocks - 1; }
}
public override string TypeAsString
{
get { return Type; }
}
internal override PhysicalVolumeType VolumeType
{
get { return PhysicalVolumeType.ApplePartition; }
}
public int Size
{
get { return 512; }
}
public int ReadFrom(byte[] buffer, int offset)
{
Signature = EndianUtilities.ToUInt16BigEndian(buffer, offset + 0);
MapEntries = EndianUtilities.ToUInt32BigEndian(buffer, offset + 4);
PhysicalBlockStart = EndianUtilities.ToUInt32BigEndian(buffer, offset + 8);
PhysicalBlocks = EndianUtilities.ToUInt32BigEndian(buffer, offset + 12);
Name = EndianUtilities.BytesToString(buffer, offset + 16, 32).TrimEnd('\0');
Type = EndianUtilities.BytesToString(buffer, offset + 48, 32).TrimEnd('\0');
LogicalBlockStart = EndianUtilities.ToUInt32BigEndian(buffer, offset + 80);
LogicalBlocks = EndianUtilities.ToUInt32BigEndian(buffer, offset + 84);
Flags = EndianUtilities.ToUInt32BigEndian(buffer, offset + 88);
BootBlock = EndianUtilities.ToUInt32BigEndian(buffer, offset + 92);
BootBytes = EndianUtilities.ToUInt32BigEndian(buffer, offset + 96);
return 512;
}
public void WriteTo(byte[] buffer, int offset)
{
throw new NotImplementedException();
}
public override SparseStream Open()
{
return new SubStream(_diskStream, PhysicalBlockStart * 512, PhysicalBlocks * 512);
}
}
}

View File

@ -0,0 +1,66 @@
//
// 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.IO;
using DiscUtils.Partitions;
using DiscUtils.Streams;
namespace DiscUtils.ApplePartitionMap
{
[PartitionTableFactory]
internal sealed class PartitionMapFactory : PartitionTableFactory
{
public override bool DetectIsPartitioned(Stream s)
{
if (s.Length < 1024)
{
return false;
}
s.Position = 0;
byte[] initialBytes = StreamUtilities.ReadExact(s, 1024);
BlockZero b0 = new BlockZero();
b0.ReadFrom(initialBytes, 0);
if (b0.Signature != 0x4552)
{
return false;
}
PartitionMapEntry initialPart = new PartitionMapEntry(s);
initialPart.ReadFrom(initialBytes, 512);
return initialPart.Signature == 0x504d;
}
public override PartitionTable DetectPartitionTable(VirtualDisk disk)
{
if (!DetectIsPartitioned(disk.Content))
{
return null;
}
return new PartitionMap(disk.Content);
}
}
}

View File

@ -0,0 +1,38 @@
//
// 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.Archives
{
internal sealed class FileRecord
{
public long Length;
public string Name;
public long Start;
public FileRecord(string name, long start, long length)
{
Name = name;
Start = start;
Length = length;
}
}
}

View File

@ -0,0 +1,144 @@
//
// 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.IO;
using DiscUtils.Streams;
namespace DiscUtils.Archives
{
/// <summary>
/// Minimal tar file format implementation.
/// </summary>
public sealed class TarFile
{
private readonly Dictionary<string, FileRecord> _files;
private readonly Stream _fileStream;
/// <summary>
/// Initializes a new instance of the TarFile class.
/// </summary>
/// <param name="fileStream">The Tar file.</param>
public TarFile(Stream fileStream)
{
_fileStream = fileStream;
_files = new Dictionary<string, FileRecord>();
TarHeader hdr = new TarHeader();
byte[] hdrBuf = StreamUtilities.ReadExact(_fileStream, TarHeader.Length);
hdr.ReadFrom(hdrBuf, 0);
while (hdr.FileLength != 0 || !string.IsNullOrEmpty(hdr.FileName))
{
FileRecord record = new FileRecord(hdr.FileName, _fileStream.Position, hdr.FileLength);
_files.Add(record.Name, record);
_fileStream.Position += (hdr.FileLength + 511) / 512 * 512;
hdrBuf = StreamUtilities.ReadExact(_fileStream, TarHeader.Length);
hdr.ReadFrom(hdrBuf, 0);
}
}
/// <summary>
/// Tries to open a file contained in the archive, if it exists.
/// </summary>
/// <param name="path">The path to the file within the archive.</param>
/// <param name="stream">A stream containing the file contents, or null.</param>
/// <returns><c>true</c> if the file could be opened, else <c>false</c>.</returns>
public bool TryOpenFile(string path, out Stream stream)
{
if (_files.ContainsKey(path))
{
FileRecord file = _files[path];
stream = new SubStream(_fileStream, file.Start, file.Length);
return true;
}
stream = null;
return false;
}
/// <summary>
/// Open a file contained in the archive.
/// </summary>
/// <param name="path">The path to the file within the archive.</param>
/// <returns>A stream containing the file contents.</returns>
/// <exception cref="FileNotFoundException">Thrown if the file is not found.</exception>
public Stream OpenFile(string path)
{
if (_files.ContainsKey(path))
{
FileRecord file = _files[path];
return new SubStream(_fileStream, file.Start, file.Length);
}
throw new FileNotFoundException("File is not in archive", path);
}
/// <summary>
/// Determines if a given file exists in the archive.
/// </summary>
/// <param name="path">The file path to test.</param>
/// <returns><c>true</c> if the file is present, else <c>false</c>.</returns>
public bool FileExists(string path)
{
return _files.ContainsKey(path);
}
/// <summary>
/// Determines if a given directory exists in the archive.
/// </summary>
/// <param name="path">The file path to test.</param>
/// <returns><c>true</c> if the directory is present, else <c>false</c>.</returns>
public bool DirExists(string path)
{
string searchStr = path;
searchStr = searchStr.Replace(@"\", "/");
searchStr = searchStr.EndsWith(@"/", StringComparison.Ordinal) ? searchStr : searchStr + @"/";
foreach (string filePath in _files.Keys)
{
if (filePath.StartsWith(searchStr, StringComparison.Ordinal))
{
return true;
}
}
return false;
}
internal IEnumerable<FileRecord> GetFiles(string dir)
{
string searchStr = dir;
searchStr = searchStr.Replace(@"\", "/");
searchStr = searchStr.EndsWith(@"/", StringComparison.Ordinal) ? searchStr : searchStr + @"/";
foreach (string filePath in _files.Keys)
{
if (filePath.StartsWith(searchStr, StringComparison.Ordinal))
{
yield return _files[filePath];
}
}
}
}
}

View File

@ -0,0 +1,122 @@
//
// 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.IO;
using DiscUtils.Streams;
namespace DiscUtils.Archives
{
/// <summary>
/// Builder to create UNIX Tar archive files.
/// </summary>
public sealed class TarFileBuilder : StreamBuilder
{
private readonly List<UnixBuildFileRecord> _files;
/// <summary>
/// Initializes a new instance of the <see cref="TarFileBuilder"/> class.
/// </summary>
public TarFileBuilder()
{
_files = new List<UnixBuildFileRecord>();
}
/// <summary>
/// Add a file to the tar archive.
/// </summary>
/// <param name="name">The name of the file.</param>
/// <param name="buffer">The file data.</param>
public void AddFile(string name, byte[] buffer)
{
_files.Add(new UnixBuildFileRecord(name, buffer));
}
/// <summary>
/// Add a file to the tar archive.
/// </summary>
/// <param name="name">The name of the file.</param>
/// <param name="buffer">The file data.</param>
/// <param name="fileMode">The access mode of the file.</param>
/// <param name="ownerId">The uid of the owner.</param>
/// <param name="groupId">The gid of the owner.</param>
/// <param name="modificationTime">The modification time for the file.</param>
public void AddFile(
string name, byte[] buffer, UnixFilePermissions fileMode, int ownerId, int groupId,
DateTime modificationTime)
{
_files.Add(new UnixBuildFileRecord(name, buffer, fileMode, ownerId, groupId, modificationTime));
}
/// <summary>
/// Add a file to the tar archive.
/// </summary>
/// <param name="name">The name of the file.</param>
/// <param name="stream">The file data.</param>
public void AddFile(string name, Stream stream)
{
_files.Add(new UnixBuildFileRecord(name, stream));
}
/// <summary>
/// Add a file to the tar archive.
/// </summary>
/// <param name="name">The name of the file.</param>
/// <param name="stream">The file data.</param>
/// <param name="fileMode">The access mode of the file.</param>
/// <param name="ownerId">The uid of the owner.</param>
/// <param name="groupId">The gid of the owner.</param>
/// <param name="modificationTime">The modification time for the file.</param>
public void AddFile(
string name, Stream stream, UnixFilePermissions fileMode, int ownerId, int groupId,
DateTime modificationTime)
{
_files.Add(new UnixBuildFileRecord(name, stream, fileMode, ownerId, groupId, modificationTime));
}
protected override List<BuilderExtent> FixExtents(out long totalLength)
{
List<BuilderExtent> result = new List<BuilderExtent>(_files.Count * 2 + 2);
long pos = 0;
foreach (UnixBuildFileRecord file in _files)
{
BuilderExtent fileContentExtent = file.Fix(pos + TarHeader.Length);
result.Add(new TarHeaderExtent(
pos, file.Name, fileContentExtent.Length, file.FileMode, file.OwnerId, file.GroupId,
file.ModificationTime));
pos += TarHeader.Length;
result.Add(fileContentExtent);
pos += MathUtilities.RoundUp(fileContentExtent.Length, 512);
}
// Two empty 512-byte blocks at end of tar file.
result.Add(new BuilderBufferExtent(pos, new byte[1024]));
totalLength = pos + 1024;
return result;
}
}
}

View File

@ -0,0 +1,102 @@
//
// 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 DiscUtils.Streams;
namespace DiscUtils.Archives
{
internal sealed class TarHeader
{
public const int Length = 512;
public long FileLength;
public UnixFilePermissions FileMode;
public string FileName;
public int GroupId;
public DateTime ModificationTime;
public int OwnerId;
public void ReadFrom(byte[] buffer, int offset)
{
FileName = ReadNullTerminatedString(buffer, offset + 0, 100);
FileMode = (UnixFilePermissions)OctalToLong(ReadNullTerminatedString(buffer, offset + 100, 8));
OwnerId = (int)OctalToLong(ReadNullTerminatedString(buffer, offset + 108, 8));
GroupId = (int)OctalToLong(ReadNullTerminatedString(buffer, offset + 116, 8));
FileLength = OctalToLong(ReadNullTerminatedString(buffer, offset + 124, 12));
ModificationTime = OctalToLong(ReadNullTerminatedString(buffer, offset + 136, 12)).FromUnixTimeSeconds().DateTime;
}
public void WriteTo(byte[] buffer, int offset)
{
Array.Clear(buffer, offset, Length);
EndianUtilities.StringToBytes(FileName, buffer, offset, 99);
EndianUtilities.StringToBytes(LongToOctal((long)FileMode, 7), buffer, offset + 100, 7);
EndianUtilities.StringToBytes(LongToOctal(OwnerId, 7), buffer, offset + 108, 7);
EndianUtilities.StringToBytes(LongToOctal(GroupId, 7), buffer, offset + 116, 7);
EndianUtilities.StringToBytes(LongToOctal(FileLength, 11), buffer, offset + 124, 11);
EndianUtilities.StringToBytes(LongToOctal(Convert.ToUInt32((new DateTimeOffset(ModificationTime)).ToUnixTimeSeconds()), 11), buffer, offset + 136, 11);
// Checksum
EndianUtilities.StringToBytes(new string(' ', 8), buffer, offset + 148, 8);
long checkSum = 0;
for (int i = 0; i < 512; ++i)
{
checkSum += buffer[offset + i];
}
EndianUtilities.StringToBytes(LongToOctal(checkSum, 7), buffer, offset + 148, 7);
buffer[155] = 0;
}
private static string ReadNullTerminatedString(byte[] buffer, int offset, int length)
{
return EndianUtilities.BytesToString(buffer, offset, length).TrimEnd('\0');
}
private static long OctalToLong(string value)
{
long result = 0;
for (int i = 0; i < value.Length; ++i)
{
result = result * 8 + (value[i] - '0');
}
return result;
}
private static string LongToOctal(long value, int length)
{
string result = string.Empty;
while (value > 0)
{
result = (char)('0' + value % 8) + result;
value = value / 8;
}
return new string('0', length - result.Length) + result;
}
}
}

View File

@ -0,0 +1,68 @@
//
// 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 DiscUtils.Streams;
namespace DiscUtils.Archives
{
internal sealed class TarHeaderExtent : BuilderBufferExtent
{
private readonly long _fileLength;
private readonly string _fileName;
private readonly int _groupId;
private readonly UnixFilePermissions _mode;
private readonly DateTime _modificationTime;
private readonly int _ownerId;
public TarHeaderExtent(long start, string fileName, long fileLength, UnixFilePermissions mode, int ownerId,
int groupId, DateTime modificationTime)
: base(start, 512)
{
_fileName = fileName;
_fileLength = fileLength;
_mode = mode;
_ownerId = ownerId;
_groupId = groupId;
_modificationTime = modificationTime;
}
public TarHeaderExtent(long start, string fileName, long fileLength)
: this(start, fileName, fileLength, 0, 0, 0, DateTimeOffsetExtensions.UnixEpoch) {}
protected override byte[] GetBuffer()
{
byte[] buffer = new byte[TarHeader.Length];
TarHeader header = new TarHeader();
header.FileName = _fileName;
header.FileLength = _fileLength;
header.FileMode = _mode;
header.OwnerId = _ownerId;
header.GroupId = _groupId;
header.ModificationTime = _modificationTime;
header.WriteTo(buffer, 0);
return buffer;
}
}
}

View File

@ -0,0 +1,75 @@
//
// 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.IO;
using DiscUtils.Streams;
namespace DiscUtils.Archives
{
internal sealed class UnixBuildFileRecord
{
private readonly BuilderExtentSource _source;
public UnixBuildFileRecord(string name, byte[] buffer)
: this(name, new BuilderBufferExtentSource(buffer), 0, 0, 0, DateTimeOffsetExtensions.UnixEpoch) {}
public UnixBuildFileRecord(string name, Stream stream)
: this(name, new BuilderStreamExtentSource(stream), 0, 0, 0, DateTimeOffsetExtensions.UnixEpoch) {}
public UnixBuildFileRecord(
string name, byte[] buffer, UnixFilePermissions fileMode, int ownerId, int groupId,
DateTime modificationTime)
: this(name, new BuilderBufferExtentSource(buffer), fileMode, ownerId, groupId, modificationTime) {}
public UnixBuildFileRecord(
string name, Stream stream, UnixFilePermissions fileMode, int ownerId, int groupId,
DateTime modificationTime)
: this(name, new BuilderStreamExtentSource(stream), fileMode, ownerId, groupId, modificationTime) {}
public UnixBuildFileRecord(string name, BuilderExtentSource fileSource, UnixFilePermissions fileMode,
int ownerId, int groupId, DateTime modificationTime)
{
Name = name;
_source = fileSource;
FileMode = fileMode;
OwnerId = ownerId;
GroupId = groupId;
ModificationTime = modificationTime;
}
public UnixFilePermissions FileMode { get; }
public int GroupId { get; }
public DateTime ModificationTime { get; }
public string Name { get; }
public int OwnerId { get; }
public BuilderExtent Fix(long pos)
{
return _source.Fix(pos);
}
}
}

View File

@ -0,0 +1,99 @@
//
// 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
{
/// <summary>
/// Class whose instances represent a CHS (Cylinder, Head, Sector) address on a disk.
/// </summary>
/// <remarks>Instances of this class are immutable.</remarks>
public sealed class ChsAddress
{
/// <summary>
/// The address of the first sector on any disk.
/// </summary>
public static readonly ChsAddress First = new ChsAddress(0, 0, 1);
/// <summary>
/// Initializes a new instance of the ChsAddress class.
/// </summary>
/// <param name="cylinder">The number of cylinders of the disk.</param>
/// <param name="head">The number of heads (aka platters) of the disk.</param>
/// <param name="sector">The number of sectors per track/cylinder of the disk.</param>
public ChsAddress(int cylinder, int head, int sector)
{
Cylinder = cylinder;
Head = head;
Sector = sector;
}
/// <summary>
/// Gets the cylinder number (zero-based).
/// </summary>
public int Cylinder { get; }
/// <summary>
/// Gets the head (zero-based).
/// </summary>
public int Head { get; }
/// <summary>
/// Gets the sector number (one-based).
/// </summary>
public int Sector { get; }
/// <summary>
/// Determines if this object is equivalent to another.
/// </summary>
/// <param name="obj">The object to test against.</param>
/// <returns><c>true</c> if the <paramref name="obj"/> is equivalent, else <c>false</c>.</returns>
public override bool Equals(object obj)
{
if (obj == null || obj.GetType() != GetType())
{
return false;
}
ChsAddress other = (ChsAddress)obj;
return Cylinder == other.Cylinder && Head == other.Head && Sector == other.Sector;
}
/// <summary>
/// Calculates the hash code for this object.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode()
{
return Cylinder.GetHashCode() ^ Head.GetHashCode() ^ Sector.GetHashCode();
}
/// <summary>
/// Gets a string representation of this object, in the form (C/H/S).
/// </summary>
/// <returns>The string representation.</returns>
public override string ToString()
{
return "(" + Cylinder + "/" + Head + "/" + Sector + ")";
}
}
}

View File

@ -0,0 +1,75 @@
//
// 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.Collections.Generic;
namespace DiscUtils
{
/// <summary>
/// Class that identifies the role of each cluster in a file system.
/// </summary>
public sealed class ClusterMap
{
private readonly object[] _clusterToFileId;
private readonly ClusterRoles[] _clusterToRole;
private readonly Dictionary<object, string[]> _fileIdToPaths;
public ClusterMap(ClusterRoles[] clusterToRole, object[] clusterToFileId,
Dictionary<object, string[]> fileIdToPaths)
{
_clusterToRole = clusterToRole;
_clusterToFileId = clusterToFileId;
_fileIdToPaths = fileIdToPaths;
}
/// <summary>
/// Gets the role of a cluster within the file system.
/// </summary>
/// <param name="cluster">The cluster to inspect.</param>
/// <returns>The clusters role (or roles).</returns>
public ClusterRoles GetRole(long cluster)
{
if (_clusterToRole == null || _clusterToRole.Length < cluster)
{
return ClusterRoles.None;
}
return _clusterToRole[cluster];
}
/// <summary>
/// Converts a cluster to a list of file names.
/// </summary>
/// <param name="cluster">The cluster to inspect.</param>
/// <returns>A list of paths that map to the cluster.</returns>
/// <remarks>A list is returned because on file systems with the notion of
/// hard links, a cluster may correspond to multiple directory entries.</remarks>
public string[] ClusterToPaths(long cluster)
{
if ((GetRole(cluster) & (ClusterRoles.DataFile | ClusterRoles.SystemFile)) != 0)
{
object fileId = _clusterToFileId[cluster];
return _fileIdToPaths[fileId];
}
return new string[0];
}
}
}

View File

@ -0,0 +1,50 @@
using System;
namespace DiscUtils
{
/// <summary>
/// Enumeration of possible cluster roles.
/// </summary>
/// <remarks>A cluster may be in more than one role.</remarks>
[Flags]
public enum ClusterRoles
{
/// <summary>
/// Unknown, or unspecified role.
/// </summary>
None = 0x00,
/// <summary>
/// Cluster is free.
/// </summary>
Free = 0x01,
/// <summary>
/// Cluster is in use by a normal file.
/// </summary>
DataFile = 0x02,
/// <summary>
/// Cluster is in use by a system file.
/// </summary>
/// <remarks>This isn't a file marked with the 'system' attribute,
/// rather files that form part of the file system namespace but also
/// form part of the file system meta-data.</remarks>
SystemFile = 0x04,
/// <summary>
/// Cluster is in use for meta-data.
/// </summary>
Metadata = 0x08,
/// <summary>
/// Cluster contains the boot region.
/// </summary>
BootArea = 0x10,
/// <summary>
/// Cluster is marked bad.
/// </summary>
Bad = 0x20
}
}

View File

@ -0,0 +1,93 @@
//
// 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;
namespace DiscUtils.Compression
{
/// <summary>
/// Implementation of the Adler-32 checksum algorithm.
/// </summary>
public class Adler32
{
private uint _a;
private uint _b;
/// <summary>
/// Initializes a new instance of the Adler32 class.
/// </summary>
public Adler32()
{
_a = 1;
}
/// <summary>
/// Gets the checksum of all data processed so far.
/// </summary>
public int Value
{
get { return (int)(_b << 16 | _a); }
}
/// <summary>
/// Provides data that should be checksummed.
/// </summary>
/// <param name="buffer">Buffer containing the data to checksum.</param>
/// <param name="offset">Offset of the first byte to checksum.</param>
/// <param name="count">The number of bytes to checksum.</param>
/// <remarks>
/// Call this method repeatedly until all checksummed
/// data has been processed.
/// </remarks>
public void Process(byte[] buffer, int offset, int count)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentException("Offset outside of array bounds", nameof(offset));
}
if (count < 0 || offset + count > buffer.Length)
{
throw new ArgumentException("Array index out of bounds", nameof(count));
}
int processed = 0;
while (processed < count)
{
int innerEnd = Math.Min(count, processed + 2000);
while (processed < innerEnd)
{
_a += buffer[processed++];
_b += _a;
}
_a %= 65521;
_b %= 65521;
}
}
}
}

View File

@ -0,0 +1,141 @@
//
// 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.
//
//
// Based on "libbzip2", Copyright (C) 1996-2007 Julian R Seward.
//
using System.IO;
namespace DiscUtils.Compression
{
internal class BZip2BlockDecoder
{
private readonly InverseBurrowsWheeler _inverseBurrowsWheeler;
public BZip2BlockDecoder(int blockSize)
{
_inverseBurrowsWheeler = new InverseBurrowsWheeler(blockSize);
}
public uint Crc { get; private set; }
public int Process(BitStream bitstream, byte[] outputBuffer, int outputBufferOffset)
{
Crc = 0;
for (int i = 0; i < 4; ++i)
{
Crc = (Crc << 8) | bitstream.Read(8);
}
bool rand = bitstream.Read(1) != 0;
int origPtr = (int)bitstream.Read(24);
int thisBlockSize = ReadBuffer(bitstream, outputBuffer, outputBufferOffset);
_inverseBurrowsWheeler.OriginalIndex = origPtr;
_inverseBurrowsWheeler.Process(outputBuffer, outputBufferOffset, thisBlockSize, outputBuffer,
outputBufferOffset);
if (rand)
{
BZip2Randomizer randomizer = new BZip2Randomizer();
randomizer.Process(outputBuffer, outputBufferOffset, thisBlockSize, outputBuffer, outputBufferOffset);
}
return thisBlockSize;
}
private static int ReadBuffer(BitStream bitstream, byte[] buffer, int offset)
{
// The MTF state
int numInUse = 0;
MoveToFront moveFrontTransform = new MoveToFront();
bool[] inUseGroups = new bool[16];
for (int i = 0; i < 16; ++i)
{
inUseGroups[i] = bitstream.Read(1) != 0;
}
for (int i = 0; i < 256; ++i)
{
if (inUseGroups[i / 16])
{
if (bitstream.Read(1) != 0)
{
moveFrontTransform.Set(numInUse, (byte)i);
numInUse++;
}
}
}
// Initialize 'virtual' Huffman tree from bitstream
BZip2CombinedHuffmanTrees huffmanTree = new BZip2CombinedHuffmanTrees(bitstream, numInUse + 2);
// Main loop reading data
int readBytes = 0;
while (true)
{
uint symbol = huffmanTree.NextSymbol();
if (symbol < 2)
{
// RLE, with length stored in a binary-style format
uint runLength = 0;
int bitShift = 0;
while (symbol < 2)
{
runLength += (symbol + 1) << bitShift;
bitShift++;
symbol = huffmanTree.NextSymbol();
}
byte b = moveFrontTransform.Head;
while (runLength > 0)
{
buffer[offset + readBytes] = b;
++readBytes;
--runLength;
}
}
if (symbol <= numInUse)
{
// Single byte
byte b = moveFrontTransform.GetAndMove((int)symbol - 1);
buffer[offset + readBytes] = b;
++readBytes;
}
else if (symbol == numInUse + 1)
{
// End of block marker
return readBytes;
}
else
{
throw new InvalidDataException("Invalid symbol from Huffman table");
}
}
}
}
}

View File

@ -0,0 +1,134 @@
//
// 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.
//
//
// Based on "libbzip2", Copyright (C) 1996-2007 Julian R Seward.
//
using System.IO;
namespace DiscUtils.Compression
{
/// <summary>
/// Represents scheme used by BZip2 where multiple Huffman trees are used as a
/// virtual Huffman tree, with a logical selector every 50 bits in the bit stream.
/// </summary>
internal class BZip2CombinedHuffmanTrees
{
private HuffmanTree _activeTree;
private readonly BitStream _bitstream;
private int _nextSelector;
private byte[] _selectors;
private int _symbolsToNextSelector;
private HuffmanTree[] _trees;
public BZip2CombinedHuffmanTrees(BitStream bitstream, int maxSymbols)
{
_bitstream = bitstream;
Initialize(maxSymbols);
}
public uint NextSymbol()
{
if (_symbolsToNextSelector == 0)
{
_symbolsToNextSelector = 50;
_activeTree = _trees[_selectors[_nextSelector]];
_nextSelector++;
}
_symbolsToNextSelector--;
return _activeTree.NextSymbol(_bitstream);
}
private void Initialize(int maxSymbols)
{
int numTrees = (int)_bitstream.Read(3);
if (numTrees < 2 || numTrees > 6)
{
throw new InvalidDataException("Invalid number of tables");
}
int numSelectors = (int)_bitstream.Read(15);
if (numSelectors < 1)
{
throw new InvalidDataException("Invalid number of selectors");
}
_selectors = new byte[numSelectors];
MoveToFront mtf = new MoveToFront(numTrees, true);
for (int i = 0; i < numSelectors; ++i)
{
_selectors[i] = mtf.GetAndMove(CountSetBits(numTrees));
}
_trees = new HuffmanTree[numTrees];
for (int t = 0; t < numTrees; ++t)
{
uint[] lengths = new uint[maxSymbols];
uint len = _bitstream.Read(5);
for (int i = 0; i < maxSymbols; ++i)
{
if (len < 1 || len > 20)
{
throw new InvalidDataException("Invalid length constructing Huffman tree");
}
while (_bitstream.Read(1) != 0)
{
len = _bitstream.Read(1) == 0 ? len + 1 : len - 1;
if (len < 1 || len > 20)
{
throw new InvalidDataException("Invalid length constructing Huffman tree");
}
}
lengths[i] = len;
}
_trees[t] = new HuffmanTree(lengths);
}
_symbolsToNextSelector = 0;
_nextSelector = 0;
}
private byte CountSetBits(int max)
{
byte val = 0;
while (_bitstream.Read(1) != 0)
{
val++;
if (val >= max)
{
throw new InvalidDataException("Exceeded max number of consecutive bits");
}
}
return val;
}
}
}

View File

@ -0,0 +1,348 @@
//
// 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.
//
//
// Based on "libbzip2", Copyright (C) 1996-2007 Julian R Seward.
//
using System;
using System.IO;
using DiscUtils.Internal;
using DiscUtils.Streams;
namespace DiscUtils.Compression
{
/// <summary>
/// Implementation of a BZip2 decoder.
/// </summary>
public sealed class BZip2DecoderStream : Stream
{
private readonly BitStream _bitstream;
private readonly byte[] _blockBuffer;
private uint _blockCrc;
private readonly BZip2BlockDecoder _blockDecoder;
private Crc32 _calcBlockCrc;
private uint _calcCompoundCrc;
private uint _compoundCrc;
private Stream _compressedStream;
private bool _eof;
private readonly Ownership _ownsCompressed;
private long _position;
private BZip2RleStream _rleStream;
/// <summary>
/// Initializes a new instance of the BZip2DecoderStream class.
/// </summary>
/// <param name="stream">The compressed input stream.</param>
/// <param name="ownsStream">Whether ownership of stream passes to the new instance.</param>
public BZip2DecoderStream(Stream stream, Ownership ownsStream)
{
_compressedStream = stream;
_ownsCompressed = ownsStream;
_bitstream = new BigEndianBitStream(new BufferedStream(stream));
// The Magic BZh
byte[] magic = new byte[3];
magic[0] = (byte)_bitstream.Read(8);
magic[1] = (byte)_bitstream.Read(8);
magic[2] = (byte)_bitstream.Read(8);
if (magic[0] != 0x42 || magic[1] != 0x5A || magic[2] != 0x68)
{
throw new InvalidDataException("Bad magic at start of stream");
}
// The size of the decompression blocks in multiples of 100,000
int blockSize = (int)_bitstream.Read(8) - 0x30;
if (blockSize < 1 || blockSize > 9)
{
throw new InvalidDataException("Unexpected block size in header: " + blockSize);
}
blockSize *= 100000;
_rleStream = new BZip2RleStream();
_blockDecoder = new BZip2BlockDecoder(blockSize);
_blockBuffer = new byte[blockSize];
if (ReadBlock() == 0)
{
_eof = true;
}
}
/// <summary>
/// Gets an indication of whether read access is permitted.
/// </summary>
public override bool CanRead
{
get { return true; }
}
/// <summary>
/// Gets an indication of whether seeking is permitted.
/// </summary>
public override bool CanSeek
{
get { return false; }
}
/// <summary>
/// Gets an indication of whether write access is permitted.
/// </summary>
public override bool CanWrite
{
get { return false; }
}
/// <summary>
/// Gets the length of the stream (the capacity of the underlying buffer).
/// </summary>
public override long Length
{
get { throw new NotSupportedException(); }
}
/// <summary>
/// Gets and sets the current position within the stream.
/// </summary>
public override long Position
{
get { return _position; }
set { throw new NotSupportedException(); }
}
/// <summary>
/// Flushes all data to the underlying storage.
/// </summary>
public override void Flush()
{
throw new NotSupportedException();
}
/// <summary>
/// Reads a number of bytes from the stream.
/// </summary>
/// <param name="buffer">The destination buffer.</param>
/// <param name="offset">The start offset within the destination buffer.</param>
/// <param name="count">The number of bytes to read.</param>
/// <returns>The number of bytes read.</returns>
public override int Read(byte[] buffer, int offset, int count)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
if (buffer.Length < offset + count)
{
throw new ArgumentException("Buffer smaller than declared");
}
if (offset < 0)
{
throw new ArgumentException("Offset less than zero", nameof(offset));
}
if (count < 0)
{
throw new ArgumentException("Count less than zero", nameof(count));
}
if (_eof)
{
throw new IOException("Attempt to read beyond end of stream");
}
if (count == 0)
{
return 0;
}
int numRead = _rleStream.Read(buffer, offset, count);
if (numRead == 0)
{
// If there was an existing block, check it's crc.
if (_calcBlockCrc != null)
{
if (_blockCrc != _calcBlockCrc.Value)
{
throw new InvalidDataException("Decompression failed - block CRC mismatch");
}
_calcCompoundCrc = ((_calcCompoundCrc << 1) | (_calcCompoundCrc >> 31)) ^ _blockCrc;
}
// Read a new block (if any), if none - check the overall CRC before returning
if (ReadBlock() == 0)
{
_eof = true;
if (_calcCompoundCrc != _compoundCrc)
{
throw new InvalidDataException("Decompression failed - compound CRC");
}
return 0;
}
numRead = _rleStream.Read(buffer, offset, count);
}
_calcBlockCrc.Process(buffer, offset, numRead);
// Pre-read next block, so a client that knows the decompressed length will still
// have the overall CRC calculated.
if (_rleStream.AtEof)
{
// If there was an existing block, check it's crc.
if (_calcBlockCrc != null)
{
if (_blockCrc != _calcBlockCrc.Value)
{
throw new InvalidDataException("Decompression failed - block CRC mismatch");
}
}
_calcCompoundCrc = ((_calcCompoundCrc << 1) | (_calcCompoundCrc >> 31)) ^ _blockCrc;
if (ReadBlock() == 0)
{
_eof = true;
if (_calcCompoundCrc != _compoundCrc)
{
throw new InvalidDataException("Decompression failed - compound CRC mismatch");
}
return numRead;
}
}
_position += numRead;
return numRead;
}
/// <summary>
/// Changes the current stream position.
/// </summary>
/// <param name="offset">The origin-relative stream position.</param>
/// <param name="origin">The origin for the stream position.</param>
/// <returns>The new stream position.</returns>
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
/// <summary>
/// Sets the length of the stream (the underlying buffer's capacity).
/// </summary>
/// <param name="value">The new length of the stream.</param>
public override void SetLength(long value)
{
throw new NotSupportedException();
}
/// <summary>
/// Writes a buffer to the stream.
/// </summary>
/// <param name="buffer">The buffer to write.</param>
/// <param name="offset">The starting offset within buffer.</param>
/// <param name="count">The number of bytes to write.</param>
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
/// <summary>
/// Releases underlying resources.
/// </summary>
/// <param name="disposing">Whether this method is called from Dispose.</param>
protected override void Dispose(bool disposing)
{
try
{
if (disposing)
{
if (_compressedStream != null && _ownsCompressed == Ownership.Dispose)
{
_compressedStream.Dispose();
}
_compressedStream = null;
if (_rleStream != null)
{
_rleStream.Dispose();
_rleStream = null;
}
}
}
finally
{
base.Dispose(disposing);
}
}
private int ReadBlock()
{
ulong marker = ReadMarker();
if (marker == 0x314159265359)
{
int blockSize = _blockDecoder.Process(_bitstream, _blockBuffer, 0);
_rleStream.Reset(_blockBuffer, 0, blockSize);
_blockCrc = _blockDecoder.Crc;
_calcBlockCrc = new Crc32BigEndian(Crc32Algorithm.Common);
return blockSize;
}
if (marker == 0x177245385090)
{
_compoundCrc = ReadUint();
return 0;
}
throw new InvalidDataException("Found invalid marker in stream");
}
private uint ReadUint()
{
uint val = 0;
for (int i = 0; i < 4; ++i)
{
val = (val << 8) | _bitstream.Read(8);
}
return val;
}
private ulong ReadMarker()
{
ulong marker = 0;
for (int i = 0; i < 6; ++i)
{
marker = (marker << 8) | _bitstream.Read(8);
}
return marker;
}
}
}

View File

@ -0,0 +1,124 @@
//
// 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.
//
//
// Based on "libbzip2", Copyright (C) 1996-2007 Julian R Seward.
//
using System;
namespace DiscUtils.Compression
{
internal class BZip2Randomizer : DataBlockTransform
{
private static readonly int[] RandomVals =
{
619, 720, 127, 481, 931, 816, 813, 233, 566, 247,
985, 724, 205, 454, 863, 491, 741, 242, 949, 214,
733, 859, 335, 708, 621, 574, 73, 654, 730, 472,
419, 436, 278, 496, 867, 210, 399, 680, 480, 51,
878, 465, 811, 169, 869, 675, 611, 697, 867, 561,
862, 687, 507, 283, 482, 129, 807, 591, 733, 623,
150, 238, 59, 379, 684, 877, 625, 169, 643, 105,
170, 607, 520, 932, 727, 476, 693, 425, 174, 647,
73, 122, 335, 530, 442, 853, 695, 249, 445, 515,
909, 545, 703, 919, 874, 474, 882, 500, 594, 612,
641, 801, 220, 162, 819, 984, 589, 513, 495, 799,
161, 604, 958, 533, 221, 400, 386, 867, 600, 782,
382, 596, 414, 171, 516, 375, 682, 485, 911, 276,
98, 553, 163, 354, 666, 933, 424, 341, 533, 870,
227, 730, 475, 186, 263, 647, 537, 686, 600, 224,
469, 68, 770, 919, 190, 373, 294, 822, 808, 206,
184, 943, 795, 384, 383, 461, 404, 758, 839, 887,
715, 67, 618, 276, 204, 918, 873, 777, 604, 560,
951, 160, 578, 722, 79, 804, 96, 409, 713, 940,
652, 934, 970, 447, 318, 353, 859, 672, 112, 785,
645, 863, 803, 350, 139, 93, 354, 99, 820, 908,
609, 772, 154, 274, 580, 184, 79, 626, 630, 742,
653, 282, 762, 623, 680, 81, 927, 626, 789, 125,
411, 521, 938, 300, 821, 78, 343, 175, 128, 250,
170, 774, 972, 275, 999, 639, 495, 78, 352, 126,
857, 956, 358, 619, 580, 124, 737, 594, 701, 612,
669, 112, 134, 694, 363, 992, 809, 743, 168, 974,
944, 375, 748, 52, 600, 747, 642, 182, 862, 81,
344, 805, 988, 739, 511, 655, 814, 334, 249, 515,
897, 955, 664, 981, 649, 113, 974, 459, 893, 228,
433, 837, 553, 268, 926, 240, 102, 654, 459, 51,
686, 754, 806, 760, 493, 403, 415, 394, 687, 700,
946, 670, 656, 610, 738, 392, 760, 799, 887, 653,
978, 321, 576, 617, 626, 502, 894, 679, 243, 440,
680, 879, 194, 572, 640, 724, 926, 56, 204, 700,
707, 151, 457, 449, 797, 195, 791, 558, 945, 679,
297, 59, 87, 824, 713, 663, 412, 693, 342, 606,
134, 108, 571, 364, 631, 212, 174, 643, 304, 329,
343, 97, 430, 751, 497, 314, 983, 374, 822, 928,
140, 206, 73, 263, 980, 736, 876, 478, 430, 305,
170, 514, 364, 692, 829, 82, 855, 953, 676, 246,
369, 970, 294, 750, 807, 827, 150, 790, 288, 923,
804, 378, 215, 828, 592, 281, 565, 555, 710, 82,
896, 831, 547, 261, 524, 462, 293, 465, 502, 56,
661, 821, 976, 991, 658, 869, 905, 758, 745, 193,
768, 550, 608, 933, 378, 286, 215, 979, 792, 961,
61, 688, 793, 644, 986, 403, 106, 366, 905, 644,
372, 567, 466, 434, 645, 210, 389, 550, 919, 135,
780, 773, 635, 389, 707, 100, 626, 958, 165, 504,
920, 176, 193, 713, 857, 265, 203, 50, 668, 108,
645, 990, 626, 197, 510, 357, 358, 850, 858, 364,
936, 638
};
protected override bool BuffersMustNotOverlap
{
get { return false; }
}
protected override int DoProcess(byte[] input, int inputOffset, int inputCount, byte[] output, int outputOffset)
{
if (input != output || inputOffset != outputOffset)
{
Array.Copy(input, inputOffset, output, outputOffset, inputCount);
}
int randIndex = 1;
int nextByte = RandomVals[0] - 2;
while (nextByte < inputCount)
{
output[nextByte] ^= 1;
nextByte += RandomVals[randIndex++];
randIndex &= 0x1FF;
}
return inputCount;
}
protected override int MaxOutputCount(int inputCount)
{
return inputCount;
}
protected override int MinOutputCount(int inputCount)
{
return inputCount;
}
}
}

View File

@ -0,0 +1,157 @@
//
// 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.
//
//
// Based on "libbzip2", Copyright (C) 1996-2007 Julian R Seward.
//
using System;
using System.IO;
namespace DiscUtils.Compression
{
internal class BZip2RleStream : Stream
{
private byte[] _blockBuffer;
private int _blockOffset;
private int _blockRemaining;
private byte _lastByte;
private int _numSame;
private long _position;
private int _runBytesOutstanding;
public bool AtEof
{
get { return _runBytesOutstanding == 0 && _blockRemaining == 0; }
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return false; }
}
public override long Length
{
get { throw new NotSupportedException(); }
}
public override long Position
{
get { return _position; }
set { throw new NotSupportedException(); }
}
public void Reset(byte[] buffer, int offset, int count)
{
_position = 0;
_blockBuffer = buffer;
_blockOffset = offset;
_blockRemaining = count;
_numSame = -1;
_lastByte = 0;
_runBytesOutstanding = 0;
}
public override void Flush()
{
throw new NotSupportedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
int numRead = 0;
while (numRead < count && _runBytesOutstanding > 0)
{
int runCount = Math.Min(_runBytesOutstanding, count);
for (int i = 0; i < runCount; ++i)
{
buffer[offset + numRead] = _lastByte;
}
_runBytesOutstanding -= runCount;
numRead += runCount;
}
while (numRead < count && _blockRemaining > 0)
{
byte b = _blockBuffer[_blockOffset];
++_blockOffset;
--_blockRemaining;
if (_numSame == 4)
{
int runCount = Math.Min(b, count - numRead);
for (int i = 0; i < runCount; ++i)
{
buffer[offset + numRead] = _lastByte;
numRead++;
}
_runBytesOutstanding = b - runCount;
_numSame = 0;
}
else
{
if (b != _lastByte || _numSame <= 0)
{
_lastByte = b;
_numSame = 0;
}
buffer[offset + numRead] = b;
numRead++;
_numSame++;
}
}
_position += numRead;
return numRead;
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
}
}

View File

@ -0,0 +1,94 @@
//
// 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.IO;
namespace DiscUtils.Compression
{
/// <summary>
/// Converts a byte stream into a bit stream.
/// </summary>
internal class BigEndianBitStream : BitStream
{
private uint _buffer;
private int _bufferAvailable;
private readonly Stream _byteStream;
private readonly byte[] _readBuffer = new byte[2];
public BigEndianBitStream(Stream byteStream)
{
_byteStream = byteStream;
}
public override int MaxReadAhead
{
get { return 16; }
}
public override uint Read(int count)
{
if (count > 16)
{
uint result = Read(16) << (count - 16);
return result | Read(count - 16);
}
EnsureBufferFilled();
_bufferAvailable -= count;
uint mask = (uint)((1 << count) - 1);
return (_buffer >> _bufferAvailable) & mask;
}
public override uint Peek(int count)
{
EnsureBufferFilled();
uint mask = (uint)((1 << count) - 1);
return (_buffer >> (_bufferAvailable - count)) & mask;
}
public override void Consume(int count)
{
EnsureBufferFilled();
_bufferAvailable -= count;
}
private void EnsureBufferFilled()
{
if (_bufferAvailable < 16)
{
_readBuffer[0] = 0;
_readBuffer[1] = 0;
_byteStream.Read(_readBuffer, 0, 2);
_buffer = _buffer << 16 | (uint)(_readBuffer[0] << 8) | _readBuffer[1];
_bufferAvailable += 16;
}
}
}
}

View File

@ -0,0 +1,60 @@
//
// 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.Compression
{
/// <summary>
/// Base class for bit streams.
/// </summary>
/// <remarks>
/// The rules for conversion of a byte stream to a bit stream vary
/// between implementations.
/// </remarks>
internal abstract class BitStream
{
/// <summary>
/// Gets the maximum number of bits that can be peeked on the stream.
/// </summary>
public abstract int MaxReadAhead { get; }
/// <summary>
/// Reads bits from the stream.
/// </summary>
/// <param name="count">The number of bits to read.</param>
/// <returns>The bits as a UInt32.</returns>
public abstract uint Read(int count);
/// <summary>
/// Queries data from the stream.
/// </summary>
/// <param name="count">The number of bits to query.</param>
/// <returns>The bits as a UInt32.</returns>
/// <remarks>This method does not consume the bits (i.e. move the file pointer).</remarks>
public abstract uint Peek(int count);
/// <summary>
/// Consumes bits from the stream without returning them.
/// </summary>
/// <param name="count">The number of bits to consume.</param>
public abstract void Consume(int count);
}
}

View File

@ -0,0 +1,64 @@
//
// 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.Compression
{
/// <summary>
/// Base class for block compression algorithms.
/// </summary>
public abstract class BlockCompressor
{
/// <summary>
/// Gets or sets the block size parameter to the algorithm.
/// </summary>
/// <remarks>
/// Some algorithms may use this to control both compression and decompression, others may
/// only use it to control compression. Some may ignore it entirely.
/// </remarks>
public int BlockSize { get; set; }
/// <summary>
/// Compresses some data.
/// </summary>
/// <param name="source">The uncompressed input.</param>
/// <param name="sourceOffset">Offset of the input data in <c>source</c>.</param>
/// <param name="sourceLength">The amount of uncompressed data.</param>
/// <param name="compressed">The destination for the output compressed data.</param>
/// <param name="compressedOffset">Offset for the output data in <c>compressed</c>.</param>
/// <param name="compressedLength">The maximum size of the compressed data on input, and the actual size on output.</param>
/// <returns>Indication of success, or indication the data could not compress into the requested space.</returns>
public abstract CompressionResult Compress(byte[] source, int sourceOffset, int sourceLength, byte[] compressed,
int compressedOffset, ref int compressedLength);
/// <summary>
/// Decompresses some data.
/// </summary>
/// <param name="source">The compressed input.</param>
/// <param name="sourceOffset">Offset of the input data in <c>source</c>.</param>
/// <param name="sourceLength">The amount of compressed data.</param>
/// <param name="decompressed">The destination for the output decompressed data.</param>
/// <param name="decompressedOffset">Offset for the output data in <c>decompressed</c>.</param>
/// <returns>The amount of decompressed data.</returns>
public abstract int Decompress(byte[] source, int sourceOffset, int sourceLength, byte[] decompressed,
int decompressedOffset);
}
}

View File

@ -0,0 +1,50 @@
//
// 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.Compression
{
/// <summary>
/// Possible results of attempting to compress data.
/// </summary>
/// <remarks>
/// A compression routine <i>may</i> return <c>Compressed</c>, even if the data
/// was 'all zeros' or increased in size. The <c>AllZeros</c> and <c>Incompressible</c>
/// values are for algorithms that include special detection for these cases.
/// </remarks>
public enum CompressionResult
{
/// <summary>
/// The data compressed succesfully.
/// </summary>
Compressed,
/// <summary>
/// The data was all-zero's.
/// </summary>
AllZeros,
/// <summary>
/// The data was incompressible (could not fit into destination buffer).
/// </summary>
Incompressible
}
}

View File

@ -0,0 +1,70 @@
//
// 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.Globalization;
namespace DiscUtils.Compression
{
internal abstract class DataBlockTransform
{
protected abstract bool BuffersMustNotOverlap { get; }
public int Process(byte[] input, int inputOffset, int inputCount, byte[] output, int outputOffset)
{
if (output.Length < outputOffset + (long)MinOutputCount(inputCount))
{
throw new ArgumentException(
string.Format(
CultureInfo.InvariantCulture,
"Output buffer to small, must be at least {0} bytes may need to be {1} bytes",
MinOutputCount(inputCount),
MaxOutputCount(inputCount)));
}
if (BuffersMustNotOverlap)
{
int maxOut = MaxOutputCount(inputCount);
if (input == output
&& (inputOffset + (long)inputCount > outputOffset)
&& (inputOffset <= outputOffset + (long)maxOut))
{
byte[] tempBuffer = new byte[maxOut];
int outCount = DoProcess(input, inputOffset, inputCount, tempBuffer, 0);
Array.Copy(tempBuffer, 0, output, outputOffset, outCount);
return outCount;
}
}
return DoProcess(input, inputOffset, inputCount, output, outputOffset);
}
protected abstract int DoProcess(byte[] input, int inputOffset, int inputCount, byte[] output, int outputOffset);
protected abstract int MaxOutputCount(int inputCount);
protected abstract int MinOutputCount(int inputCount);
}
}

View File

@ -0,0 +1,102 @@
//
// 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.Compression
{
/// <summary>
/// A canonical Huffman tree implementation.
/// </summary>
/// <remarks>
/// A lookup table is created that will take any bit sequence (max tree depth in length),
/// indicating the output symbol. In WIM files, in practice, no chunk exceeds 32768 bytes
/// in length, so we often end up generating a bigger lookup table than the data it's
/// encoding. This makes for exceptionally fast symbol lookups O(1), but is inefficient
/// overall.
/// </remarks>
internal sealed class HuffmanTree
{
private readonly uint[] _buffer;
private readonly int _numBits; // Max bits per symbol
private readonly int _numSymbols; // Max symbols
public HuffmanTree(uint[] lengths)
{
Lengths = lengths;
_numSymbols = lengths.Length;
uint maxLength = 0;
for (int i = 0; i < Lengths.Length; ++i)
{
if (Lengths[i] > maxLength)
{
maxLength = Lengths[i];
}
}
_numBits = (int)maxLength;
_buffer = new uint[1 << _numBits];
Build();
}
public uint[] Lengths { get; }
public uint NextSymbol(BitStream bitStream)
{
uint symbol = _buffer[bitStream.Peek(_numBits)];
// We may have over-read, reset bitstream position
bitStream.Consume((int)Lengths[symbol]);
return symbol;
}
private void Build()
{
int position = 0;
// For each bit-length...
for (int i = 1; i <= _numBits; ++i)
{
// Check each symbol
for (uint symbol = 0; symbol < _numSymbols; ++symbol)
{
if (Lengths[symbol] == i)
{
int numToFill = 1 << (_numBits - i);
for (int n = 0; n < numToFill; ++n)
{
_buffer[position + n] = symbol;
}
position += numToFill;
}
}
}
for (int i = position; i < _buffer.Length; ++i)
{
_buffer[i] = uint.MaxValue;
}
}
}
}

View File

@ -0,0 +1,97 @@
//
// 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;
namespace DiscUtils.Compression
{
internal sealed class InverseBurrowsWheeler : DataBlockTransform
{
private readonly int[] _nextPos;
private readonly int[] _pointers;
public InverseBurrowsWheeler(int bufferSize)
{
_pointers = new int[bufferSize];
_nextPos = new int[256];
}
protected override bool BuffersMustNotOverlap
{
get { return true; }
}
public int OriginalIndex { get; set; }
protected override int DoProcess(byte[] input, int inputOffset, int inputCount, byte[] output, int outputOffset)
{
int outputCount = inputCount;
// First find the frequency of each value
Array.Clear(_nextPos, 0, _nextPos.Length);
for (int i = inputOffset; i < inputOffset + inputCount; ++i)
{
_nextPos[input[i]]++;
}
// We know they're 'sorted' in the first column, so now can figure
// out the position of the first instance of each.
int sum = 0;
for (int i = 0; i < 256; ++i)
{
int tempSum = sum;
sum += _nextPos[i];
_nextPos[i] = tempSum;
}
// For each value in the final column, put a pointer to to the
// 'next' character in the first (sorted) column.
for (int i = 0; i < inputCount; ++i)
{
_pointers[_nextPos[input[inputOffset + i]]++] = i;
}
// The 'next' character after the end of the original string is the
// first character of the original string.
int focus = _pointers[OriginalIndex];
// We can now just walk the pointers to reconstruct the original string
for (int i = 0; i < outputCount; ++i)
{
output[outputOffset + i] = input[inputOffset + focus];
focus = _pointers[focus];
}
return outputCount;
}
protected override int MaxOutputCount(int inputCount)
{
return inputCount;
}
protected override int MinOutputCount(int inputCount)
{
return inputCount;
}
}
}

View File

@ -0,0 +1,68 @@
//
// 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.Compression
{
internal class MoveToFront
{
private readonly byte[] _buffer;
public MoveToFront()
: this(256, false) {}
public MoveToFront(int size, bool autoInit)
{
_buffer = new byte[size];
if (autoInit)
{
for (byte i = 0; i < size; ++i)
{
_buffer[i] = i;
}
}
}
public byte Head
{
get { return _buffer[0]; }
}
public void Set(int pos, byte val)
{
_buffer[pos] = val;
}
public byte GetAndMove(int pos)
{
byte val = _buffer[pos];
for (int i = pos; i > 0; --i)
{
_buffer[i] = _buffer[i - 1];
}
_buffer[0] = val;
return val;
}
}
}

View File

@ -0,0 +1,64 @@
//
// Copyright (c) 2014, Quamotion
//
// 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.IO;
using System.IO.Compression;
namespace DiscUtils.Compression
{
internal class SizedDeflateStream : DeflateStream
{
private readonly int _length;
private int _position;
public SizedDeflateStream(Stream stream, CompressionMode mode, bool leaveOpen, int length)
: base(stream, mode, leaveOpen)
{
_length = length;
}
public override long Length
{
get { return _length; }
}
public override long Position
{
get { return _position; }
set
{
if (value != Position)
{
throw new NotImplementedException();
}
}
}
public override int Read(byte[] array, int offset, int count)
{
int read = base.Read(array, offset, count);
_position += read;
return read;
}
}
}

View File

@ -0,0 +1,86 @@
//
// Copyright (c) 2014, Quamotion
//
// 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.IO;
using DiscUtils.Streams;
using Buffer=DiscUtils.Streams.Buffer;
namespace DiscUtils.Compression
{
internal class ZlibBuffer : Buffer
{
private Ownership _ownership;
private readonly Stream _stream;
private int position;
public ZlibBuffer(Stream stream, Ownership ownership)
{
_stream = stream;
_ownership = ownership;
position = 0;
}
public override bool CanRead
{
get { return _stream.CanRead; }
}
public override bool CanWrite
{
get { return _stream.CanWrite; }
}
public override long Capacity
{
get { return _stream.Length; }
}
public override int Read(long pos, byte[] buffer, int offset, int count)
{
if (pos != position)
{
throw new NotSupportedException();
}
int read = _stream.Read(buffer, offset, count);
position += read;
return read;
}
public override void Write(long pos, byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
public override void SetCapacity(long value)
{
throw new NotImplementedException();
}
public override IEnumerable<StreamExtent> GetExtentsInRange(long start, long count)
{
yield return new StreamExtent(0, _stream.Length);
}
}
}

View File

@ -0,0 +1,240 @@
//
// 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.IO;
using System.IO.Compression;
using DiscUtils.Streams;
namespace DiscUtils.Compression
{
/// <summary>
/// Implementation of the Zlib compression algorithm.
/// </summary>
/// <remarks>Only decompression is currently implemented.</remarks>
public class ZlibStream : Stream
{
private readonly Adler32 _adler32;
private readonly DeflateStream _deflateStream;
private readonly CompressionMode _mode;
private readonly Stream _stream;
/// <summary>
/// Initializes a new instance of the ZlibStream class.
/// </summary>
/// <param name="stream">The stream to compress of decompress.</param>
/// <param name="mode">Whether to compress or decompress.</param>
/// <param name="leaveOpen">Whether closing this stream should leave <c>stream</c> open.</param>
public ZlibStream(Stream stream, CompressionMode mode, bool leaveOpen)
{
_stream = stream;
_mode = mode;
if (mode == CompressionMode.Decompress)
{
// We just sanity check against expected header values...
byte[] headerBuffer = StreamUtilities.ReadExact(stream, 2);
ushort header = EndianUtilities.ToUInt16BigEndian(headerBuffer, 0);
if (header % 31 != 0)
{
throw new IOException("Invalid Zlib header found");
}
if ((header & 0x0F00) != 8 << 8)
{
throw new NotSupportedException("Zlib compression not using DEFLATE algorithm");
}
if ((header & 0x0020) != 0)
{
throw new NotSupportedException("Zlib compression using preset dictionary");
}
}
else
{
ushort header =
(8 << 8) // DEFLATE
| (7 << 12) // 32K window size
| 0x80; // Default algorithm
header |= (ushort)(31 - header % 31);
byte[] headerBuffer = new byte[2];
EndianUtilities.WriteBytesBigEndian(header, headerBuffer, 0);
stream.Write(headerBuffer, 0, 2);
}
_deflateStream = new DeflateStream(stream, mode, leaveOpen);
_adler32 = new Adler32();
}
/// <summary>
/// Gets whether the stream can be read.
/// </summary>
public override bool CanRead
{
get { return _deflateStream.CanRead; }
}
/// <summary>
/// Gets whether the stream pointer can be changed.
/// </summary>
public override bool CanSeek
{
get { return false; }
}
/// <summary>
/// Gets whether the stream can be written to.
/// </summary>
public override bool CanWrite
{
get { return _deflateStream.CanWrite; }
}
/// <summary>
/// Gets the length of the stream.
/// </summary>
public override long Length
{
get { throw new NotSupportedException(); }
}
/// <summary>
/// Gets and sets the stream position.
/// </summary>
public override long Position
{
get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); }
}
/// <summary>
/// Closes the stream.
/// </summary>
protected override void Dispose(bool disposing)
{
if (_mode == CompressionMode.Decompress)
{
// Can only check Adler checksum on seekable streams. Since DeflateStream
// aggresively caches input, it normally has already consumed the footer.
if (_stream.CanSeek)
{
_stream.Seek(-4, SeekOrigin.End);
byte[] footerBuffer = StreamUtilities.ReadExact(_stream, 4);
if (EndianUtilities.ToInt32BigEndian(footerBuffer, 0) != _adler32.Value)
{
throw new InvalidDataException("Corrupt decompressed data detected");
}
}
_deflateStream.Dispose();
}
else
{
_deflateStream.Dispose();
byte[] footerBuffer = new byte[4];
EndianUtilities.WriteBytesBigEndian(_adler32.Value, footerBuffer, 0);
_stream.Write(footerBuffer, 0, 4);
}
base.Dispose(disposing);
}
/// <summary>
/// Flushes the stream.
/// </summary>
public override void Flush()
{
_deflateStream.Flush();
}
/// <summary>
/// Reads data from the stream.
/// </summary>
/// <param name="buffer">The buffer to populate.</param>
/// <param name="offset">The first byte to write.</param>
/// <param name="count">The number of bytes requested.</param>
/// <returns>The number of bytes read.</returns>
public override int Read(byte[] buffer, int offset, int count)
{
CheckParams(buffer, offset, count);
int numRead = _deflateStream.Read(buffer, offset, count);
_adler32.Process(buffer, offset, numRead);
return numRead;
}
/// <summary>
/// Seeks to a new position.
/// </summary>
/// <param name="offset">Relative position to seek to.</param>
/// <param name="origin">The origin of the seek.</param>
/// <returns>The new position.</returns>
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
/// <summary>
/// Changes the length of the stream.
/// </summary>
/// <param name="value">The new desired length of the stream.</param>
public override void SetLength(long value)
{
throw new NotSupportedException();
}
/// <summary>
/// Writes data to the stream.
/// </summary>
/// <param name="buffer">Buffer containing the data to write.</param>
/// <param name="offset">Offset of the first byte to write.</param>
/// <param name="count">Number of bytes to write.</param>
public override void Write(byte[] buffer, int offset, int count)
{
CheckParams(buffer, offset, count);
_adler32.Process(buffer, offset, count);
_deflateStream.Write(buffer, offset, count);
}
private static void CheckParams(byte[] buffer, int offset, int count)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentException("Offset outside of array bounds", nameof(offset));
}
if (count < 0 || offset + count > buffer.Length)
{
throw new ArgumentException("Array index out of bounds", nameof(count));
}
}
}
}

View File

@ -0,0 +1,23 @@
#if NETSTANDARD
using System.Text;
#endif
namespace DiscUtils.CoreCompat
{
internal static class EncodingHelper
{
private static bool _registered;
public static void RegisterEncodings()
{
if (_registered)
return;
_registered = true;
#if NETSTANDARD
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
#endif
}
}
}

View File

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
namespace DiscUtils.CoreCompat
{
internal static class ReflectionHelper
{
public static bool IsEnum(Type type)
{
#if NETSTANDARD1_5
return type.GetTypeInfo().IsEnum;
#else
return type.IsEnum;
#endif
}
public static Attribute GetCustomAttribute(PropertyInfo property, Type attributeType)
{
#if NETSTANDARD1_5
return property.GetCustomAttribute(attributeType);
#else
return Attribute.GetCustomAttribute(property, attributeType);
#endif
}
public static Attribute GetCustomAttribute(PropertyInfo property, Type attributeType, bool inherit)
{
#if NETSTANDARD1_5
return property.GetCustomAttribute(attributeType, inherit);
#else
return Attribute.GetCustomAttribute(property, attributeType, inherit);
#endif
}
public static Attribute GetCustomAttribute(FieldInfo field, Type attributeType)
{
#if NETSTANDARD1_5
return field.GetCustomAttribute(attributeType);
#else
return Attribute.GetCustomAttribute(field, attributeType);
#endif
}
public static Attribute GetCustomAttribute(Type type, Type attributeType)
{
#if NETSTANDARD1_5
return type.GetTypeInfo().GetCustomAttribute(attributeType);
#else
return Attribute.GetCustomAttribute(type, attributeType);
#endif
}
public static Attribute GetCustomAttribute(Type type, Type attributeType, bool inherit)
{
#if NETSTANDARD1_5
return type.GetTypeInfo().GetCustomAttribute(attributeType, inherit);
#else
return Attribute.GetCustomAttribute(type, attributeType);
#endif
}
public static IEnumerable<Attribute> GetCustomAttributes(Type type, Type attributeType, bool inherit)
{
#if NETSTANDARD1_5
return type.GetTypeInfo().GetCustomAttributes(attributeType, inherit);
#else
return Attribute.GetCustomAttributes(type, attributeType);
#endif
}
public static Assembly GetAssembly(Type type)
{
#if NETSTANDARD1_5
return type.GetTypeInfo().Assembly;
#else
return type.Assembly;
#endif
}
public static int SizeOf<T>()
{
#if NETSTANDARD1_5
return Marshal.SizeOf<T>();
#else
return Marshal.SizeOf(typeof(T));
#endif
}
}
}

View File

@ -0,0 +1,15 @@
#if NETSTANDARD1_5
using System.Globalization;
namespace DiscUtils.CoreCompat
{
internal static class StringExtensions
{
public static string ToUpper(this string value, CultureInfo culture)
{
return value.ToUpper();
}
}
}
#endif

View File

@ -0,0 +1,190 @@
//
// 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.IO;
using DiscUtils.Internal;
namespace DiscUtils
{
/// <summary>
/// Provides information about a directory on a disc.
/// </summary>
/// <remarks>
/// This class allows navigation of the disc directory/file hierarchy.
/// </remarks>
public sealed class DiscDirectoryInfo : DiscFileSystemInfo
{
/// <summary>
/// Initializes a new instance of the DiscDirectoryInfo class.
/// </summary>
/// <param name="fileSystem">The file system the directory info relates to.</param>
/// <param name="path">The path within the file system of the directory.</param>
internal DiscDirectoryInfo(DiscFileSystem fileSystem, string path)
: base(fileSystem, path) {}
/// <summary>
/// Gets a value indicating whether the directory exists.
/// </summary>
public override bool Exists
{
get { return FileSystem.DirectoryExists(Path); }
}
/// <summary>
/// Gets the full path of the directory.
/// </summary>
public override string FullName
{
get { return base.FullName + @"\"; }
}
/// <summary>
/// Creates a directory.
/// </summary>
public void Create()
{
FileSystem.CreateDirectory(Path);
}
/// <summary>
/// Deletes a directory, even if it's not empty.
/// </summary>
public override void Delete()
{
FileSystem.DeleteDirectory(Path, false);
}
/// <summary>
/// Deletes a directory, with the caller choosing whether to recurse.
/// </summary>
/// <param name="recursive"><c>true</c> to delete all child node, <c>false</c> to fail if the directory is not empty.</param>
public void Delete(bool recursive)
{
FileSystem.DeleteDirectory(Path, recursive);
}
/// <summary>
/// Moves a directory and it's contents to a new path.
/// </summary>
/// <param name="destinationDirName">The destination directory name.</param>
public void MoveTo(string destinationDirName)
{
FileSystem.MoveDirectory(Path, destinationDirName);
}
/// <summary>
/// Gets all child directories.
/// </summary>
/// <returns>An array of child directories.</returns>
public DiscDirectoryInfo[] GetDirectories()
{
return Utilities.Map(FileSystem.GetDirectories(Path),
p => new DiscDirectoryInfo(FileSystem, p));
}
/// <summary>
/// Gets all child directories matching a search pattern.
/// </summary>
/// <param name="pattern">The search pattern.</param>
/// <returns>An array of child directories, or empty if none match.</returns>
/// <remarks>The search pattern can include the wildcards * (matching 0 or more characters)
/// and ? (matching 1 character).</remarks>
public DiscDirectoryInfo[] GetDirectories(string pattern)
{
return GetDirectories(pattern, SearchOption.TopDirectoryOnly);
}
/// <summary>
/// Gets all descendant directories matching a search pattern.
/// </summary>
/// <param name="pattern">The search pattern.</param>
/// <param name="searchOption">Whether to search just this directory, or all children.</param>
/// <returns>An array of descendant directories, or empty if none match.</returns>
/// <remarks>The search pattern can include the wildcards * (matching 0 or more characters)
/// and ? (matching 1 character). The option parameter determines whether only immediate
/// children, or all children are returned.</remarks>
public DiscDirectoryInfo[] GetDirectories(string pattern, SearchOption searchOption)
{
return Utilities.Map(FileSystem.GetDirectories(Path, pattern, searchOption),
p => new DiscDirectoryInfo(FileSystem, p));
}
/// <summary>
/// Gets all files.
/// </summary>
/// <returns>An array of files.</returns>
public DiscFileInfo[] GetFiles()
{
return Utilities.Map(FileSystem.GetFiles(Path), p => new DiscFileInfo(FileSystem, p));
}
/// <summary>
/// Gets all files matching a search pattern.
/// </summary>
/// <param name="pattern">The search pattern.</param>
/// <returns>An array of files, or empty if none match.</returns>
/// <remarks>The search pattern can include the wildcards * (matching 0 or more characters)
/// and ? (matching 1 character).</remarks>
public DiscFileInfo[] GetFiles(string pattern)
{
return GetFiles(pattern, SearchOption.TopDirectoryOnly);
}
/// <summary>
/// Gets all descendant files matching a search pattern.
/// </summary>
/// <param name="pattern">The search pattern.</param>
/// <param name="searchOption">Whether to search just this directory, or all children.</param>
/// <returns>An array of descendant files, or empty if none match.</returns>
/// <remarks>The search pattern can include the wildcards * (matching 0 or more characters)
/// and ? (matching 1 character). The option parameter determines whether only immediate
/// children, or all children are returned.</remarks>
public DiscFileInfo[] GetFiles(string pattern, SearchOption searchOption)
{
return Utilities.Map(FileSystem.GetFiles(Path, pattern, searchOption),
p => new DiscFileInfo(FileSystem, p));
}
/// <summary>
/// Gets all files and directories in this directory.
/// </summary>
/// <returns>An array of files and directories.</returns>
public DiscFileSystemInfo[] GetFileSystemInfos()
{
return Utilities.Map(FileSystem.GetFileSystemEntries(Path),
p => new DiscFileSystemInfo(FileSystem, p));
}
/// <summary>
/// Gets all files and directories in this directory.
/// </summary>
/// <param name="pattern">The search pattern.</param>
/// <returns>An array of files and directories.</returns>
/// <remarks>The search pattern can include the wildcards * (matching 0 or more characters)
/// and ? (matching 1 character).</remarks>
public DiscFileSystemInfo[] GetFileSystemInfos(string pattern)
{
return Utilities.Map(FileSystem.GetFileSystemEntries(Path, pattern),
p => new DiscFileSystemInfo(FileSystem, p));
}
}
}

View File

@ -0,0 +1,200 @@
//
// 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.IO;
namespace DiscUtils
{
/// <summary>
/// Provides information about a file on a disc.
/// </summary>
public sealed class DiscFileInfo : DiscFileSystemInfo
{
internal DiscFileInfo(DiscFileSystem fileSystem, string path)
: base(fileSystem, path) {}
/// <summary>
/// Gets an instance of the parent directory.
/// </summary>
public DiscDirectoryInfo Directory
{
get { return Parent; }
}
/// <summary>
/// Gets a string representing the directory's full path.
/// </summary>
public string DirectoryName
{
get { return Directory.FullName; }
}
/// <summary>
/// Gets a value indicating whether the file exists.
/// </summary>
public override bool Exists
{
get { return FileSystem.FileExists(Path); }
}
/// <summary>
/// Gets or sets a value indicating whether the file is read-only.
/// </summary>
public bool IsReadOnly
{
get { return (Attributes & FileAttributes.ReadOnly) != 0; }
set
{
if (value)
{
Attributes = Attributes | FileAttributes.ReadOnly;
}
else
{
Attributes = Attributes & ~FileAttributes.ReadOnly;
}
}
}
/// <summary>
/// Gets the length of the current file in bytes.
/// </summary>
public long Length
{
get { return FileSystem.GetFileLength(Path); }
}
/// <summary>
/// Deletes a file.
/// </summary>
public override void Delete()
{
FileSystem.DeleteFile(Path);
}
/// <summary>
/// Creates a <see cref="StreamWriter" /> that appends text to the file represented by this <see cref="DiscFileInfo"/>.
/// </summary>
/// <returns>The newly created writer.</returns>
public StreamWriter AppendText()
{
return new StreamWriter(Open(FileMode.Append));
}
/// <summary>
/// Copies an existing file to a new file.
/// </summary>
/// <param name="destinationFileName">The destination file.</param>
public void CopyTo(string destinationFileName)
{
CopyTo(destinationFileName, false);
}
/// <summary>
/// Copies an existing file to a new file, allowing overwriting of an existing file.
/// </summary>
/// <param name="destinationFileName">The destination file.</param>
/// <param name="overwrite">Whether to permit over-writing of an existing file.</param>
public void CopyTo(string destinationFileName, bool overwrite)
{
FileSystem.CopyFile(Path, destinationFileName, overwrite);
}
/// <summary>
/// Creates a new file for reading and writing.
/// </summary>
/// <returns>The newly created stream.</returns>
public Stream Create()
{
return Open(FileMode.Create);
}
/// <summary>
/// Creates a new <see cref="StreamWriter"/> that writes a new text file.
/// </summary>
/// <returns>A new stream writer that can write to the file contents.</returns>
public StreamWriter CreateText()
{
return new StreamWriter(Open(FileMode.Create));
}
/// <summary>
/// Moves a file to a new location.
/// </summary>
/// <param name="destinationFileName">The new name of the file.</param>
public void MoveTo(string destinationFileName)
{
FileSystem.MoveFile(Path, destinationFileName);
}
/// <summary>
/// Opens the current file.
/// </summary>
/// <param name="mode">The file mode for the created stream.</param>
/// <returns>The newly created stream.</returns>
/// <remarks>Read-only file systems only support <c>FileMode.Open</c>.</remarks>
public Stream Open(FileMode mode)
{
return FileSystem.OpenFile(Path, mode);
}
/// <summary>
/// Opens the current file.
/// </summary>
/// <param name="mode">The file mode for the created stream.</param>
/// <param name="access">The access permissions for the created stream.</param>
/// <returns>The newly created stream.</returns>
/// <remarks>Read-only file systems only support <c>FileMode.Open</c> and <c>FileAccess.Read</c>.</remarks>
public Stream Open(FileMode mode, FileAccess access)
{
return FileSystem.OpenFile(Path, mode, access);
}
/// <summary>
/// Opens an existing file for read-only access.
/// </summary>
/// <returns>The newly created stream.</returns>
public Stream OpenRead()
{
return Open(FileMode.Open, FileAccess.Read);
}
/// <summary>
/// Opens an existing file for reading as UTF-8 text.
/// </summary>
/// <returns>The newly created reader.</returns>
public StreamReader OpenText()
{
return new StreamReader(OpenRead());
}
/// <summary>
/// Opens a file for writing.
/// </summary>
/// <returns>The newly created stream.</returns>
public Stream OpenWrite()
{
return Open(FileMode.Open, FileAccess.Write);
}
}
}

View File

@ -0,0 +1,93 @@
//
// 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.IO;
using DiscUtils.Internal;
namespace DiscUtils
{
internal sealed class DiscFileLocator : FileLocator
{
private readonly string _basePath;
private readonly DiscFileSystem _fileSystem;
public DiscFileLocator(DiscFileSystem fileSystem, string basePath)
{
_fileSystem = fileSystem;
_basePath = basePath;
}
public override bool Exists(string fileName)
{
return _fileSystem.FileExists(Utilities.CombinePaths(_basePath, fileName));
}
protected override Stream OpenFile(string fileName, FileMode mode, FileAccess access, FileShare share)
{
return _fileSystem.OpenFile(Utilities.CombinePaths(_basePath, fileName), mode, access);
}
public override FileLocator GetRelativeLocator(string path)
{
return new DiscFileLocator(_fileSystem, Utilities.CombinePaths(_basePath, path));
}
public override string GetFullPath(string path)
{
return Utilities.CombinePaths(_basePath, path);
}
public override string GetDirectoryFromPath(string path)
{
return Utilities.GetDirectoryFromPath(path);
}
public override string GetFileFromPath(string path)
{
return Utilities.GetFileFromPath(path);
}
public override DateTime GetLastWriteTimeUtc(string path)
{
return _fileSystem.GetLastWriteTimeUtc(Utilities.CombinePaths(_basePath, path));
}
public override bool HasCommonRoot(FileLocator other)
{
DiscFileLocator otherDiscLocator = other as DiscFileLocator;
if (otherDiscLocator == null)
{
return false;
}
// Common root if the same file system instance.
return ReferenceEquals(otherDiscLocator._fileSystem, _fileSystem);
}
public override string ResolveRelativePath(string path)
{
return Utilities.ResolveRelativePath(_basePath, path);
}
}
}

View File

@ -0,0 +1,509 @@
//
// 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.IO;
using DiscUtils.Streams;
namespace DiscUtils
{
/// <summary>
/// Provides the base class for all file systems.
/// </summary>
public abstract class DiscFileSystem :
#if !NETSTANDARD
MarshalByRefObject,
#endif
IFileSystem, IDisposable
{
/// <summary>
/// Initializes a new instance of the DiscFileSystem class.
/// </summary>
protected DiscFileSystem()
{
Options = new DiscFileSystemOptions();
}
/// <summary>
/// Initializes a new instance of the DiscFileSystem class.
/// </summary>
/// <param name="defaultOptions">The options instance to use for this file system instance.</param>
protected DiscFileSystem(DiscFileSystemOptions defaultOptions)
{
Options = defaultOptions;
}
/// <summary>
/// Finalizes an instance of the DiscFileSystem class.
/// </summary>
~DiscFileSystem()
{
Dispose(false);
}
/// <summary>
/// Gets the file system options, which can be modified.
/// </summary>
public virtual DiscFileSystemOptions Options { get; }
/// <summary>
/// Gets a friendly description of the file system type.
/// </summary>
public abstract string FriendlyName { get; }
/// <summary>
/// Gets a value indicating whether the file system is read-only or read-write.
/// </summary>
/// <returns>true if the file system is read-write.</returns>
public abstract bool CanWrite { get; }
/// <summary>
/// Gets the root directory of the file system.
/// </summary>
public virtual DiscDirectoryInfo Root
{
get { return new DiscDirectoryInfo(this, string.Empty); }
}
/// <summary>
/// Gets the volume label.
/// </summary>
public virtual string VolumeLabel
{
get { return string.Empty; }
}
/// <summary>
/// Gets a value indicating whether the file system is thread-safe.
/// </summary>
public virtual bool IsThreadSafe
{
get { return false; }
}
/// <summary>
/// Copies an existing file to a new file.
/// </summary>
/// <param name="sourceFile">The source file.</param>
/// <param name="destinationFile">The destination file.</param>
public virtual void CopyFile(string sourceFile, string destinationFile)
{
CopyFile(sourceFile, destinationFile, false);
}
/// <summary>
/// Copies an existing file to a new file, allowing overwriting of an existing file.
/// </summary>
/// <param name="sourceFile">The source file.</param>
/// <param name="destinationFile">The destination file.</param>
/// <param name="overwrite">Whether to permit over-writing of an existing file.</param>
public abstract void CopyFile(string sourceFile, string destinationFile, bool overwrite);
/// <summary>
/// Creates a directory.
/// </summary>
/// <param name="path">The path of the new directory.</param>
public abstract void CreateDirectory(string path);
/// <summary>
/// Deletes a directory.
/// </summary>
/// <param name="path">The path of the directory to delete.</param>
public abstract void DeleteDirectory(string path);
/// <summary>
/// Deletes a directory, optionally with all descendants.
/// </summary>
/// <param name="path">The path of the directory to delete.</param>
/// <param name="recursive">Determines if the all descendants should be deleted.</param>
public virtual void DeleteDirectory(string path, bool recursive)
{
if (recursive)
{
foreach (string dir in GetDirectories(path))
{
DeleteDirectory(dir, true);
}
foreach (string file in GetFiles(path))
{
DeleteFile(file);
}
}
DeleteDirectory(path);
}
/// <summary>
/// Deletes a file.
/// </summary>
/// <param name="path">The path of the file to delete.</param>
public abstract void DeleteFile(string path);
/// <summary>
/// Indicates if a directory exists.
/// </summary>
/// <param name="path">The path to test.</param>
/// <returns>true if the directory exists.</returns>
public abstract bool DirectoryExists(string path);
/// <summary>
/// Indicates if a file exists.
/// </summary>
/// <param name="path">The path to test.</param>
/// <returns>true if the file exists.</returns>
public abstract bool FileExists(string path);
/// <summary>
/// Indicates if a file or directory exists.
/// </summary>
/// <param name="path">The path to test.</param>
/// <returns>true if the file or directory exists.</returns>
public virtual bool Exists(string path)
{
return FileExists(path) || DirectoryExists(path);
}
/// <summary>
/// Gets the names of subdirectories in a specified directory.
/// </summary>
/// <param name="path">The path to search.</param>
/// <returns>Array of directories.</returns>
public virtual string[] GetDirectories(string path)
{
return GetDirectories(path, "*.*", SearchOption.TopDirectoryOnly);
}
/// <summary>
/// Gets the names of subdirectories in a specified directory matching a specified
/// search pattern.
/// </summary>
/// <param name="path">The path to search.</param>
/// <param name="searchPattern">The search string to match against.</param>
/// <returns>Array of directories matching the search pattern.</returns>
public virtual string[] GetDirectories(string path, string searchPattern)
{
return GetDirectories(path, searchPattern, SearchOption.TopDirectoryOnly);
}
/// <summary>
/// Gets the names of subdirectories in a specified directory matching a specified
/// search pattern, using a value to determine whether to search subdirectories.
/// </summary>
/// <param name="path">The path to search.</param>
/// <param name="searchPattern">The search string to match against.</param>
/// <param name="searchOption">Indicates whether to search subdirectories.</param>
/// <returns>Array of directories matching the search pattern.</returns>
public abstract string[] GetDirectories(string path, string searchPattern, SearchOption searchOption);
/// <summary>
/// Gets the names of files in a specified directory.
/// </summary>
/// <param name="path">The path to search.</param>
/// <returns>Array of files.</returns>
public virtual string[] GetFiles(string path)
{
return GetFiles(path, "*.*", SearchOption.TopDirectoryOnly);
}
/// <summary>
/// Gets the names of files in a specified directory.
/// </summary>
/// <param name="path">The path to search.</param>
/// <param name="searchPattern">The search string to match against.</param>
/// <returns>Array of files matching the search pattern.</returns>
public virtual string[] GetFiles(string path, string searchPattern)
{
return GetFiles(path, searchPattern, SearchOption.TopDirectoryOnly);
}
/// <summary>
/// Gets the names of files in a specified directory matching a specified
/// search pattern, using a value to determine whether to search subdirectories.
/// </summary>
/// <param name="path">The path to search.</param>
/// <param name="searchPattern">The search string to match against.</param>
/// <param name="searchOption">Indicates whether to search subdirectories.</param>
/// <returns>Array of files matching the search pattern.</returns>
public abstract string[] GetFiles(string path, string searchPattern, SearchOption searchOption);
/// <summary>
/// Gets the names of all files and subdirectories in a specified directory.
/// </summary>
/// <param name="path">The path to search.</param>
/// <returns>Array of files and subdirectories matching the search pattern.</returns>
public abstract string[] GetFileSystemEntries(string path);
/// <summary>
/// Gets the names of files and subdirectories in a specified directory matching a specified
/// search pattern.
/// </summary>
/// <param name="path">The path to search.</param>
/// <param name="searchPattern">The search string to match against.</param>
/// <returns>Array of files and subdirectories matching the search pattern.</returns>
public abstract string[] GetFileSystemEntries(string path, string searchPattern);
/// <summary>
/// Moves a directory.
/// </summary>
/// <param name="sourceDirectoryName">The directory to move.</param>
/// <param name="destinationDirectoryName">The target directory name.</param>
public abstract void MoveDirectory(string sourceDirectoryName, string destinationDirectoryName);
/// <summary>
/// Moves a file.
/// </summary>
/// <param name="sourceName">The file to move.</param>
/// <param name="destinationName">The target file name.</param>
public virtual void MoveFile(string sourceName, string destinationName)
{
MoveFile(sourceName, destinationName, false);
}
/// <summary>
/// Moves a file, allowing an existing file to be overwritten.
/// </summary>
/// <param name="sourceName">The file to move.</param>
/// <param name="destinationName">The target file name.</param>
/// <param name="overwrite">Whether to permit a destination file to be overwritten.</param>
public abstract void MoveFile(string sourceName, string destinationName, bool overwrite);
/// <summary>
/// Opens the specified file.
/// </summary>
/// <param name="path">The full path of the file to open.</param>
/// <param name="mode">The file mode for the created stream.</param>
/// <returns>The new stream.</returns>
public virtual SparseStream OpenFile(string path, FileMode mode)
{
return OpenFile(path, mode, FileAccess.ReadWrite);
}
/// <summary>
/// Opens the specified file.
/// </summary>
/// <param name="path">The full path of the file to open.</param>
/// <param name="mode">The file mode for the created stream.</param>
/// <param name="access">The access permissions for the created stream.</param>
/// <returns>The new stream.</returns>
public abstract SparseStream OpenFile(string path, FileMode mode, FileAccess access);
/// <summary>
/// Gets the attributes of a file or directory.
/// </summary>
/// <param name="path">The file or directory to inspect.</param>
/// <returns>The attributes of the file or directory.</returns>
public abstract FileAttributes GetAttributes(string path);
/// <summary>
/// Sets the attributes of a file or directory.
/// </summary>
/// <param name="path">The file or directory to change.</param>
/// <param name="newValue">The new attributes of the file or directory.</param>
public abstract void SetAttributes(string path, FileAttributes newValue);
/// <summary>
/// Gets the creation time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The creation time.</returns>
public virtual DateTime GetCreationTime(string path)
{
return GetCreationTimeUtc(path).ToLocalTime();
}
/// <summary>
/// Sets the creation time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
public virtual void SetCreationTime(string path, DateTime newTime)
{
SetCreationTimeUtc(path, newTime.ToUniversalTime());
}
/// <summary>
/// Gets the creation time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The creation time.</returns>
public abstract DateTime GetCreationTimeUtc(string path);
/// <summary>
/// Sets the creation time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
public abstract void SetCreationTimeUtc(string path, DateTime newTime);
/// <summary>
/// Gets the last access time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The last access time.</returns>
public virtual DateTime GetLastAccessTime(string path)
{
return GetLastAccessTimeUtc(path).ToLocalTime();
}
/// <summary>
/// Sets the last access time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
public virtual void SetLastAccessTime(string path, DateTime newTime)
{
SetLastAccessTimeUtc(path, newTime.ToUniversalTime());
}
/// <summary>
/// Gets the last access time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The last access time.</returns>
public abstract DateTime GetLastAccessTimeUtc(string path);
/// <summary>
/// Sets the last access time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
public abstract void SetLastAccessTimeUtc(string path, DateTime newTime);
/// <summary>
/// Gets the last modification time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The last write time.</returns>
public virtual DateTime GetLastWriteTime(string path)
{
return GetLastWriteTimeUtc(path).ToLocalTime();
}
/// <summary>
/// Sets the last modification time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
public virtual void SetLastWriteTime(string path, DateTime newTime)
{
SetLastWriteTimeUtc(path, newTime.ToUniversalTime());
}
/// <summary>
/// Gets the last modification time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The last write time.</returns>
public abstract DateTime GetLastWriteTimeUtc(string path);
/// <summary>
/// Sets the last modification time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
public abstract void SetLastWriteTimeUtc(string path, DateTime newTime);
/// <summary>
/// Gets the length of a file.
/// </summary>
/// <param name="path">The path to the file.</param>
/// <returns>The length in bytes.</returns>
public abstract long GetFileLength(string path);
/// <summary>
/// Gets an object representing a possible file.
/// </summary>
/// <param name="path">The file path.</param>
/// <returns>The representing object.</returns>
/// <remarks>The file does not need to exist.</remarks>
public virtual DiscFileInfo GetFileInfo(string path)
{
return new DiscFileInfo(this, path);
}
/// <summary>
/// Gets an object representing a possible directory.
/// </summary>
/// <param name="path">The directory path.</param>
/// <returns>The representing object.</returns>
/// <remarks>The directory does not need to exist.</remarks>
public virtual DiscDirectoryInfo GetDirectoryInfo(string path)
{
return new DiscDirectoryInfo(this, path);
}
/// <summary>
/// Gets an object representing a possible file system object (file or directory).
/// </summary>
/// <param name="path">The file system path.</param>
/// <returns>The representing object.</returns>
/// <remarks>The file system object does not need to exist.</remarks>
public virtual DiscFileSystemInfo GetFileSystemInfo(string path)
{
return new DiscFileSystemInfo(this, path);
}
/// <summary>
/// Reads the boot code of the file system into a byte array.
/// </summary>
/// <returns>The boot code, or <c>null</c> if not available.</returns>
public virtual byte[] ReadBootCode()
{
return null;
}
/// <summary>
/// Size of the Filesystem in bytes
/// </summary>
public abstract long Size { get; }
/// <summary>
/// Used space of the Filesystem in bytes
/// </summary>
public abstract long UsedSpace { get; }
/// <summary>
/// Available space of the Filesystem in bytes
/// </summary>
public abstract long AvailableSpace { get; }
#region IDisposable Members
/// <summary>
/// Disposes of this instance, releasing all resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Disposes of this instance.
/// </summary>
/// <param name="disposing">The value <c>true</c> if Disposing.</param>
protected virtual void Dispose(bool disposing) {}
#endregion
}
}

View File

@ -0,0 +1,43 @@
//
// 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.IO;
namespace DiscUtils
{
/// <summary>
/// Base class for objects that validate file system integrity.
/// </summary>
/// <remarks>Instances of this class do not offer the ability to fix/correct
/// file system issues, just to perform a limited number of checks on
/// integrity of the file system.</remarks>
public abstract class DiscFileSystemChecker
{
/// <summary>
/// Checks the integrity of a file system held in a stream.
/// </summary>
/// <param name="reportOutput">A report on issues found.</param>
/// <param name="levels">The amount of detail to report.</param>
/// <returns><c>true</c> if the file system appears valid, else <c>false</c>.</returns>
public abstract bool Check(TextWriter reportOutput, ReportLevels levels);
}
}

View File

@ -0,0 +1,219 @@
//
// 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.IO;
using DiscUtils.Internal;
namespace DiscUtils
{
/// <summary>
/// Provides the base class for both <see cref="DiscFileInfo"/> and <see cref="DiscDirectoryInfo"/> objects.
/// </summary>
public class DiscFileSystemInfo
{
internal DiscFileSystemInfo(DiscFileSystem fileSystem, string path)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
FileSystem = fileSystem;
Path = path.Trim('\\');
}
/// <summary>
/// Gets or sets the <see cref="System.IO.FileAttributes"/> of the current <see cref="DiscFileSystemInfo"/> object.
/// </summary>
public virtual FileAttributes Attributes
{
get { return FileSystem.GetAttributes(Path); }
set { FileSystem.SetAttributes(Path, value); }
}
/// <summary>
/// Gets or sets the creation time (in local time) of the current <see cref="DiscFileSystemInfo"/> object.
/// </summary>
public virtual DateTime CreationTime
{
get { return CreationTimeUtc.ToLocalTime(); }
set { CreationTimeUtc = value.ToUniversalTime(); }
}
/// <summary>
/// Gets or sets the creation time (in UTC) of the current <see cref="DiscFileSystemInfo"/> object.
/// </summary>
public virtual DateTime CreationTimeUtc
{
get { return FileSystem.GetCreationTimeUtc(Path); }
set { FileSystem.SetCreationTimeUtc(Path, value); }
}
/// <summary>
/// Gets a value indicating whether the file system object exists.
/// </summary>
public virtual bool Exists
{
get { return FileSystem.Exists(Path); }
}
/// <summary>
/// Gets the extension part of the file or directory name.
/// </summary>
public virtual string Extension
{
get
{
string name = Name;
int sepIdx = name.LastIndexOf('.');
if (sepIdx >= 0)
{
return name.Substring(sepIdx + 1);
}
return string.Empty;
}
}
/// <summary>
/// Gets the file system the referenced file or directory exists on.
/// </summary>
public DiscFileSystem FileSystem { get; }
/// <summary>
/// Gets the full path of the file or directory.
/// </summary>
public virtual string FullName
{
get { return Path; }
}
/// <summary>
/// Gets or sets the last time (in local time) the file or directory was accessed.
/// </summary>
/// <remarks>Read-only file systems will never update this value, it will remain at a fixed value.</remarks>
public virtual DateTime LastAccessTime
{
get { return LastAccessTimeUtc.ToLocalTime(); }
set { LastAccessTimeUtc = value.ToUniversalTime(); }
}
/// <summary>
/// Gets or sets the last time (in UTC) the file or directory was accessed.
/// </summary>
/// <remarks>Read-only file systems will never update this value, it will remain at a fixed value.</remarks>
public virtual DateTime LastAccessTimeUtc
{
get { return FileSystem.GetLastAccessTimeUtc(Path); }
set { FileSystem.SetLastAccessTimeUtc(Path, value); }
}
/// <summary>
/// Gets or sets the last time (in local time) the file or directory was written to.
/// </summary>
public virtual DateTime LastWriteTime
{
get { return LastWriteTimeUtc.ToLocalTime(); }
set { LastWriteTimeUtc = value.ToUniversalTime(); }
}
/// <summary>
/// Gets or sets the last time (in UTC) the file or directory was written to.
/// </summary>
public virtual DateTime LastWriteTimeUtc
{
get { return FileSystem.GetLastWriteTimeUtc(Path); }
set { FileSystem.SetLastWriteTimeUtc(Path, value); }
}
/// <summary>
/// Gets the name of the file or directory.
/// </summary>
public virtual string Name
{
get { return Utilities.GetFileFromPath(Path); }
}
/// <summary>
/// Gets the <see cref="DiscDirectoryInfo"/> of the directory containing the current <see cref="DiscFileSystemInfo"/> object.
/// </summary>
public virtual DiscDirectoryInfo Parent
{
get
{
if (string.IsNullOrEmpty(Path))
{
return null;
}
return new DiscDirectoryInfo(FileSystem, Utilities.GetDirectoryFromPath(Path));
}
}
/// <summary>
/// Gets the path to the referenced file.
/// </summary>
protected string Path { get; }
/// <summary>
/// Deletes a file or directory.
/// </summary>
public virtual void Delete()
{
if ((Attributes & FileAttributes.Directory) != 0)
{
FileSystem.DeleteDirectory(Path);
}
else
{
FileSystem.DeleteFile(Path);
}
}
/// <summary>
/// Indicates if <paramref name="obj"/> is equivalent to this object.
/// </summary>
/// <param name="obj">The object to compare.</param>
/// <returns><c>true</c> if <paramref name="obj"/> is equivalent, else <c>false</c>.</returns>
public override bool Equals(object obj)
{
DiscFileSystemInfo asInfo = obj as DiscFileSystemInfo;
if (obj == null)
{
return false;
}
return string.Compare(Path, asInfo.Path, StringComparison.Ordinal) == 0 &&
Equals(FileSystem, asInfo.FileSystem);
}
/// <summary>
/// Gets the hash code for this object.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode()
{
return Path.GetHashCode() ^ FileSystem.GetHashCode();
}
}
}

View File

@ -0,0 +1,41 @@
//
// 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;
namespace DiscUtils
{
/// <summary>
/// Common file system options.
/// </summary>
/// <remarks>Not all options are honoured by all file systems.</remarks>
public class DiscFileSystemOptions
{
/// <summary>
/// Gets or sets the random number generator the file system should use.
/// </summary>
/// <remarks>This option is normally <c>null</c>, which is fine for most purposes.
/// Use this option when you need to finely control the filesystem for
/// reproducibility of behaviour (for example in a test harness).</remarks>
public Random RandomNumberGenerator { get; set; }
}
}

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../common-library.props" />
<PropertyGroup>
<Description>Implementation of the ISO, UDF, FAT and NTFS file systems is now fairly stable. VHD, XVA, VMDK and VDI disk formats are implemented, as well as read/write Registry support. The library also includes a simple iSCSI initiator, for accessing disks via iSCSI and an NFS client implementation.</Description>
<AssemblyTitle>DiscUtils (for .NET and .NET Core), core library that supports parts of DiscUtils</AssemblyTitle>
<Authors>Kenneth Bell;Quamotion;LordMike</Authors>
<PackageTags>DiscUtils;VHD;VDI;XVA;VMDK;ISO;NTFS;EXT2FS</PackageTags>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DiscUtils.Streams\DiscUtils.Streams.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,126 @@
//
// 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 DiscUtils.CoreCompat;
using DiscUtils.Internal;
using DiscUtils.Streams;
namespace DiscUtils
{
/// <summary>
/// Base class for all disk image builders.
/// </summary>
public abstract class DiskImageBuilder
{
private static Dictionary<string, VirtualDiskFactory> _typeMap;
/// <summary>
/// Gets or sets the geometry of this disk, as reported by the BIOS, will be implied from the content stream if not set.
/// </summary>
public Geometry BiosGeometry { get; set; }
/// <summary>
/// Gets or sets the content for this disk, implying the size of the disk.
/// </summary>
public SparseStream Content { get; set; }
/// <summary>
/// Gets or sets the adapter type for created virtual disk, for file formats that encode this information.
/// </summary>
public virtual GenericDiskAdapterType GenericAdapterType { get; set; }
/// <summary>
/// Gets or sets the geometry of this disk, will be implied from the content stream if not set.
/// </summary>
public Geometry Geometry { get; set; }
/// <summary>
/// Gets a value indicating whether this file format preserves BIOS geometry information.
/// </summary>
public virtual bool PreservesBiosGeometry
{
get { return false; }
}
private static Dictionary<string, VirtualDiskFactory> TypeMap
{
get
{
if (_typeMap == null)
{
InitializeMaps();
}
return _typeMap;
}
}
/// <summary>
/// Gets an instance that constructs the specified type (and variant) of virtual disk image.
/// </summary>
/// <param name="type">The type of image to build (VHD, VMDK, etc).</param>
/// <param name="variant">The variant type (differencing/dynamic, fixed/static, etc).</param>
/// <returns>The builder instance.</returns>
public static DiskImageBuilder GetBuilder(string type, string variant)
{
VirtualDiskFactory factory;
if (!TypeMap.TryGetValue(type, out factory))
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Unknown disk type '{0}'", type), nameof(type));
}
return factory.GetImageBuilder(variant);
}
/// <summary>
/// Initiates the construction of the disk image.
/// </summary>
/// <param name="baseName">The base name for the disk images.</param>
/// <returns>A set of one or more logical files that constitute the
/// disk image. The first file is the 'primary' file that is normally attached to VMs.</returns>
/// <remarks>The supplied <c>baseName</c> is the start of the file name, with no file
/// extension. The set of file specifications will indicate the actual name corresponding
/// to each logical file that comprises the disk image. For example, given a base name
/// 'foo', the files 'foo.vmdk' and 'foo-flat.vmdk' could be returned.</remarks>
public abstract DiskImageFileSpecification[] Build(string baseName);
private static void InitializeMaps()
{
Dictionary<string, VirtualDiskFactory> typeMap = new Dictionary<string, VirtualDiskFactory>();
foreach (Type type in ReflectionHelper.GetAssembly(typeof(VirtualDisk)).GetTypes())
{
VirtualDiskFactoryAttribute attr = (VirtualDiskFactoryAttribute)ReflectionHelper.GetCustomAttribute(type, typeof(VirtualDiskFactoryAttribute), false);
if (attr != null)
{
VirtualDiskFactory factory = (VirtualDiskFactory)Activator.CreateInstance(type);
typeMap.Add(attr.Type, factory);
}
}
_typeMap = typeMap;
}
}
}

View File

@ -0,0 +1,54 @@
//
// 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 DiscUtils.Streams;
namespace DiscUtils
{
/// <summary>
/// Describes a particular file that is a constituent part of a virtual disk.
/// </summary>
public sealed class DiskImageFileSpecification
{
private readonly StreamBuilder _builder;
internal DiskImageFileSpecification(string name, StreamBuilder builder)
{
Name = name;
_builder = builder;
}
/// <summary>
/// Gets name of the file.
/// </summary>
public string Name { get; }
/// <summary>
/// Gets the object that provides access to the file's content.
/// </summary>
/// <returns>A stream object that contains the file's content.</returns>
public SparseStream OpenStream()
{
return _builder.Build();
}
}
}

View File

@ -0,0 +1,72 @@
//
// 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.IO;
using DiscUtils.Internal;
using DiscUtils.Setup;
namespace DiscUtils
{
public abstract class FileLocator
{
public abstract bool Exists(string fileName);
public Stream Open(string fileName, FileMode mode, FileAccess access, FileShare share)
{
var args = new FileOpenEventArgs(fileName, mode, access, share, OpenFile);
SetupHelper.OnOpeningFile(this, args);
if (args.Result != null)
return args.Result;
return OpenFile(args.FileName, args.FileMode, args.FileAccess, args.FileShare);
}
protected abstract Stream OpenFile(string fileName, FileMode mode, FileAccess access, FileShare share);
public abstract FileLocator GetRelativeLocator(string path);
public abstract string GetFullPath(string path);
public abstract string GetDirectoryFromPath(string path);
public abstract string GetFileFromPath(string path);
public abstract DateTime GetLastWriteTimeUtc(string path);
public abstract bool HasCommonRoot(FileLocator other);
public abstract string ResolveRelativePath(string path);
internal string MakeRelativePath(FileLocator fileLocator, string path)
{
if (!HasCommonRoot(fileLocator))
{
return null;
}
string ourFullPath = GetFullPath(string.Empty) + @"\";
string otherFullPath = fileLocator.GetFullPath(path);
return Utilities.MakeRelativePath(otherFullPath, ourFullPath);
}
}
}

View File

@ -0,0 +1,90 @@
//
// 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.IO;
namespace DiscUtils
{
/// <summary>
/// Base class holding information about a file system.
/// </summary>
/// <remarks>
/// File system implementations derive from this class, to provide information about the file system.
/// </remarks>
public abstract class FileSystemInfo
{
/// <summary>
/// Gets a one-line description of the file system.
/// </summary>
public abstract string Description { get; }
/// <summary>
/// Gets the name of the file system.
/// </summary>
public abstract string Name { get; }
/// <summary>
/// Opens a volume using the file system.
/// </summary>
/// <param name="volume">The volume to access.</param>
/// <returns>A file system instance.</returns>
public DiscFileSystem Open(VolumeInfo volume)
{
return Open(volume, null);
}
/// <summary>
/// Opens a stream using the file system.
/// </summary>
/// <param name="stream">The stream to access.</param>
/// <returns>A file system instance.</returns>
public DiscFileSystem Open(Stream stream)
{
return Open(stream, null);
}
/// <summary>
/// Opens a volume using the file system.
/// </summary>
/// <param name="volume">The volume to access.</param>
/// <param name="parameters">Parameters for the file system.</param>
/// <returns>A file system instance.</returns>
public abstract DiscFileSystem Open(VolumeInfo volume, FileSystemParameters parameters);
/// <summary>
/// Opens a stream using the file system.
/// </summary>
/// <param name="stream">The stream to access.</param>
/// <param name="parameters">Parameters for the file system.</param>
/// <returns>A file system instance.</returns>
public abstract DiscFileSystem Open(Stream stream, FileSystemParameters parameters);
/// <summary>
/// Gets the name of the file system.
/// </summary>
/// <returns>The file system name.</returns>
public override string ToString()
{
return Name;
}
}
}

View File

@ -0,0 +1,121 @@
//
// 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.IO;
using System.Reflection;
using DiscUtils.CoreCompat;
using DiscUtils.Vfs;
namespace DiscUtils
{
/// <summary>
/// FileSystemManager determines which file systems are present on a volume.
/// </summary>
/// <remarks>
/// The static detection methods detect default file systems. To plug in additional
/// file systems, create an instance of this class and call RegisterFileSystems.
/// </remarks>
public static class FileSystemManager
{
private static readonly List<VfsFileSystemFactory> _factories;
/// <summary>
/// Initializes a new instance of the FileSystemManager class.
/// </summary>
static FileSystemManager()
{
_factories = new List<VfsFileSystemFactory>();
}
/// <summary>
/// Registers new file systems with an instance of this class.
/// </summary>
/// <param name="factory">The detector for the new file systems.</param>
public static void RegisterFileSystems(VfsFileSystemFactory factory)
{
_factories.Add(factory);
}
/// <summary>
/// Registers new file systems detected in an assembly.
/// </summary>
/// <param name="assembly">The assembly to inspect.</param>
/// <remarks>
/// To be detected, the <c>VfsFileSystemFactory</c> instances must be marked with the
/// <c>VfsFileSystemFactoryAttribute</c>> attribute.
/// </remarks>
public static void RegisterFileSystems(Assembly assembly)
{
_factories.AddRange(DetectFactories(assembly));
}
/// <summary>
/// Detect which file systems are present on a volume.
/// </summary>
/// <param name="volume">The volume to inspect.</param>
/// <returns>The list of file systems detected.</returns>
public static FileSystemInfo[] DetectFileSystems(VolumeInfo volume)
{
using (Stream s = volume.Open())
{
return DoDetect(s, volume);
}
}
/// <summary>
/// Detect which file systems are present in a stream.
/// </summary>
/// <param name="stream">The stream to inspect.</param>
/// <returns>The list of file systems detected.</returns>
public static FileSystemInfo[] DetectFileSystems(Stream stream)
{
return DoDetect(stream, null);
}
private static IEnumerable<VfsFileSystemFactory> DetectFactories(Assembly assembly)
{
foreach (Type type in assembly.GetTypes())
{
Attribute attrib = ReflectionHelper.GetCustomAttribute(type, typeof(VfsFileSystemFactoryAttribute), false);
if (attrib == null)
continue;
yield return (VfsFileSystemFactory)Activator.CreateInstance(type);
}
}
private static FileSystemInfo[] DoDetect(Stream stream, VolumeInfo volume)
{
BufferedStream detectStream = new BufferedStream(stream);
List<FileSystemInfo> detected = new List<FileSystemInfo>();
foreach (VfsFileSystemFactory factory in _factories)
{
detected.AddRange(factory.Detect(detectStream, volume));
}
return detected.ToArray();
}
}
}

View File

@ -0,0 +1,49 @@
//
// 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.Text;
namespace DiscUtils
{
/// <summary>
/// Class with generic file system parameters.
/// </summary>
/// <remarks>Note - not all parameters apply to all types of file system.</remarks>
public sealed class FileSystemParameters
{
/// <summary>
/// Gets or sets the character encoding for file names, or <c>null</c> for default.
/// </summary>
/// <remarks>Some file systems, such as FAT, don't specify a particular character set for
/// file names. This parameter determines the character set that will be used for such
/// file systems.</remarks>
public Encoding FileNameEncoding { get; set; }
/// <summary>
/// Gets or sets the algorithm to convert file system time to UTC.
/// </summary>
/// <remarks>Some file system, such as FAT, don't have a defined way to convert from file system
/// time (local time where the file system is authored) to UTC time. This parameter determines
/// the algorithm to use.</remarks>
public TimeConverter TimeConverter { get; set; }
}
}

View File

@ -0,0 +1,73 @@
//
// 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.Globalization;
using System.IO;
using DiscUtils.Internal;
namespace DiscUtils
{
[VirtualDiskTransport("file")]
internal sealed class FileTransport : VirtualDiskTransport
{
private string _extraInfo;
private string _path;
public override bool IsRawDisk
{
get { return false; }
}
public override void Connect(Uri uri, string username, string password)
{
_path = uri.LocalPath;
_extraInfo = uri.Fragment.TrimStart('#');
if (!Directory.Exists(Path.GetDirectoryName(_path)))
{
throw new FileNotFoundException(
string.Format(CultureInfo.InvariantCulture, "No such file '{0}'", uri.OriginalString), _path);
}
}
public override VirtualDisk OpenDisk(FileAccess access)
{
throw new NotSupportedException();
}
public override FileLocator GetFileLocator()
{
return new LocalFileLocator(Path.GetDirectoryName(_path) + @"\");
}
public override string GetFileName()
{
return Path.GetFileName(_path);
}
public override string GetExtraInfo()
{
return _extraInfo;
}
}
}

View File

@ -0,0 +1,45 @@
//
// 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
{
/// <summary>
/// The supported Floppy Disk logical formats.
/// </summary>
public enum FloppyDiskType
{
/// <summary>
/// 720KiB capacity disk.
/// </summary>
DoubleDensity = 0,
/// <summary>
/// 1440KiB capacity disk.
/// </summary>
HighDensity = 1,
/// <summary>
/// 2880KiB capacity disk.
/// </summary>
Extended = 2
}
}

View File

@ -0,0 +1,40 @@
//
// 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
{
/// <summary>
/// Well known hard disk adaptor types.
/// </summary>
public enum GenericDiskAdapterType
{
/// <summary>
/// IDE adaptor.
/// </summary>
Ide = 0,
/// <summary>
/// SCSI adaptor.
/// </summary>
Scsi = 1
}
}

477
DiscUtils/Core/Geometry.cs Normal file
View File

@ -0,0 +1,477 @@
//
// 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.Globalization;
using DiscUtils.Streams;
namespace DiscUtils
{
/// <summary>
/// Class whose instances represent disk geometries.
/// </summary>
/// <remarks>Instances of this class are immutable.</remarks>
public sealed class Geometry
{
/// <summary>
/// Initializes a new instance of the Geometry class. The default 512 bytes per sector is assumed.
/// </summary>
/// <param name="cylinders">The number of cylinders of the disk.</param>
/// <param name="headsPerCylinder">The number of heads (aka platters) of the disk.</param>
/// <param name="sectorsPerTrack">The number of sectors per track/cylinder of the disk.</param>
public Geometry(int cylinders, int headsPerCylinder, int sectorsPerTrack)
{
Cylinders = cylinders;
HeadsPerCylinder = headsPerCylinder;
SectorsPerTrack = sectorsPerTrack;
BytesPerSector = 512;
}
/// <summary>
/// Initializes a new instance of the Geometry class.
/// </summary>
/// <param name="cylinders">The number of cylinders of the disk.</param>
/// <param name="headsPerCylinder">The number of heads (aka platters) of the disk.</param>
/// <param name="sectorsPerTrack">The number of sectors per track/cylinder of the disk.</param>
/// <param name="bytesPerSector">The number of bytes per sector of the disk.</param>
public Geometry(int cylinders, int headsPerCylinder, int sectorsPerTrack, int bytesPerSector)
{
Cylinders = cylinders;
HeadsPerCylinder = headsPerCylinder;
SectorsPerTrack = sectorsPerTrack;
BytesPerSector = bytesPerSector;
}
/// <summary>
/// Initializes a new instance of the Geometry class.
/// </summary>
/// <param name="capacity">The total capacity of the disk.</param>
/// <param name="headsPerCylinder">The number of heads (aka platters) of the disk.</param>
/// <param name="sectorsPerTrack">The number of sectors per track/cylinder of the disk.</param>
/// <param name="bytesPerSector">The number of bytes per sector of the disk.</param>
public Geometry(long capacity, int headsPerCylinder, int sectorsPerTrack, int bytesPerSector)
{
Cylinders = (int)(capacity / (headsPerCylinder * (long)sectorsPerTrack * bytesPerSector));
HeadsPerCylinder = headsPerCylinder;
SectorsPerTrack = sectorsPerTrack;
BytesPerSector = bytesPerSector;
}
/// <summary>
/// Gets the number of bytes in each sector.
/// </summary>
public int BytesPerSector { get; }
/// <summary>
/// Gets the total capacity of the disk (in bytes).
/// </summary>
public long Capacity
{
get { return TotalSectorsLong * BytesPerSector; }
}
/// <summary>
/// Gets the number of cylinders.
/// </summary>
public int Cylinders { get; }
/// <summary>
/// Gets the number of heads (aka platters).
/// </summary>
public int HeadsPerCylinder { get; }
/// <summary>
/// Gets a value indicating whether the Geometry is representable both by the BIOS and by IDE.
/// </summary>
public bool IsBiosAndIdeSafe
{
get { return Cylinders <= 1024 && HeadsPerCylinder <= 16 && SectorsPerTrack <= 63; }
}
/// <summary>
/// Gets a value indicating whether the Geometry is consistent with the values a BIOS can support.
/// </summary>
public bool IsBiosSafe
{
get { return Cylinders <= 1024 && HeadsPerCylinder <= 255 && SectorsPerTrack <= 63; }
}
/// <summary>
/// Gets a value indicating whether the Geometry is consistent with the values IDE can represent.
/// </summary>
public bool IsIdeSafe
{
get { return Cylinders <= 65536 && HeadsPerCylinder <= 16 && SectorsPerTrack <= 255; }
}
/// <summary>
/// Gets the address of the last sector on the disk.
/// </summary>
public ChsAddress LastSector
{
get { return new ChsAddress(Cylinders - 1, HeadsPerCylinder - 1, SectorsPerTrack); }
}
/// <summary>
/// Gets a null geometry, which has 512-byte sectors but zero sectors, tracks or cylinders.
/// </summary>
public static Geometry Null
{
get { return new Geometry(0, 0, 0, 512); }
}
/// <summary>
/// Gets the number of sectors per track.
/// </summary>
public int SectorsPerTrack { get; }
/// <summary>
/// Gets the total size of the disk (in sectors).
/// </summary>
[Obsolete("Use TotalSectorsLong instead, to support very large disks.")]
public int TotalSectors
{
get { return Cylinders * HeadsPerCylinder * SectorsPerTrack; }
}
/// <summary>
/// Gets the total size of the disk (in sectors).
/// </summary>
public long TotalSectorsLong
{
get { return Cylinders * (long)HeadsPerCylinder * SectorsPerTrack; }
}
/// <summary>
/// Gets the 'Large' BIOS geometry for a disk, given it's physical geometry.
/// </summary>
/// <param name="ideGeometry">The physical (aka IDE) geometry of the disk.</param>
/// <returns>The geometry a BIOS using the 'Large' method for calculating disk geometry will indicate for the disk.</returns>
public static Geometry LargeBiosGeometry(Geometry ideGeometry)
{
int cylinders = ideGeometry.Cylinders;
int heads = ideGeometry.HeadsPerCylinder;
int sectors = ideGeometry.SectorsPerTrack;
while (cylinders > 1024 && heads <= 127)
{
cylinders >>= 1;
heads <<= 1;
}
return new Geometry(cylinders, heads, sectors);
}
/// <summary>
/// Gets the 'LBA Assisted' BIOS geometry for a disk, given it's capacity.
/// </summary>
/// <param name="capacity">The capacity of the disk.</param>
/// <returns>The geometry a BIOS using the 'LBA Assisted' method for calculating disk geometry will indicate for the disk.</returns>
public static Geometry LbaAssistedBiosGeometry(long capacity)
{
int heads;
if (capacity <= 504 * Sizes.OneMiB)
{
heads = 16;
}
else if (capacity <= 1008 * Sizes.OneMiB)
{
heads = 32;
}
else if (capacity <= 2016 * Sizes.OneMiB)
{
heads = 64;
}
else if (capacity <= 4032 * Sizes.OneMiB)
{
heads = 128;
}
else
{
heads = 255;
}
int sectors = 63;
int cylinders = (int)Math.Min(1024, capacity / (sectors * (long)heads * Sizes.Sector));
return new Geometry(cylinders, heads, sectors, Sizes.Sector);
}
/// <summary>
/// Converts a geometry into one that is BIOS-safe, if not already.
/// </summary>
/// <param name="geometry">The geometry to make BIOS-safe.</param>
/// <param name="capacity">The capacity of the disk.</param>
/// <returns>The new geometry.</returns>
/// <remarks>This method returns the LBA-Assisted geometry if the given geometry isn't BIOS-safe.</remarks>
public static Geometry MakeBiosSafe(Geometry geometry, long capacity)
{
if (geometry == null)
{
return LbaAssistedBiosGeometry(capacity);
}
if (geometry.IsBiosSafe)
{
return geometry;
}
return LbaAssistedBiosGeometry(capacity);
}
/// <summary>
/// Calculates a sensible disk geometry for a disk capacity using the VHD algorithm (errs under).
/// </summary>
/// <param name="capacity">The desired capacity of the disk.</param>
/// <returns>The appropriate disk geometry.</returns>
/// <remarks>The geometry returned tends to produce a disk with less capacity
/// than requested (an exact capacity is not always possible). The geometry returned is the IDE
/// (aka Physical) geometry of the disk, not necessarily the geometry used by the BIOS.</remarks>
public static Geometry FromCapacity(long capacity)
{
return FromCapacity(capacity, Sizes.Sector);
}
/// <summary>
/// Calculates a sensible disk geometry for a disk capacity using the VHD algorithm (errs under).
/// </summary>
/// <param name="capacity">The desired capacity of the disk.</param>
/// <param name="sectorSize">The logical sector size of the disk.</param>
/// <returns>The appropriate disk geometry.</returns>
/// <remarks>The geometry returned tends to produce a disk with less capacity
/// than requested (an exact capacity is not always possible). The geometry returned is the IDE
/// (aka Physical) geometry of the disk, not necessarily the geometry used by the BIOS.</remarks>
public static Geometry FromCapacity(long capacity, int sectorSize)
{
int totalSectors;
int cylinders;
int headsPerCylinder;
int sectorsPerTrack;
// If more than ~128GB truncate at ~128GB
if (capacity > 65535 * (long)16 * 255 * sectorSize)
{
totalSectors = 65535 * 16 * 255;
}
else
{
totalSectors = (int)(capacity / sectorSize);
}
// If more than ~32GB, break partition table compatibility.
// Partition table has max 63 sectors per track. Otherwise
// we're looking for a geometry that's valid for both BIOS
// and ATA.
if (totalSectors > 65535 * 16 * 63)
{
sectorsPerTrack = 255;
headsPerCylinder = 16;
}
else
{
sectorsPerTrack = 17;
int cylindersTimesHeads = totalSectors / sectorsPerTrack;
headsPerCylinder = (cylindersTimesHeads + 1023) / 1024;
if (headsPerCylinder < 4)
{
headsPerCylinder = 4;
}
// If we need more than 1023 cylinders, or 16 heads, try more sectors per track
if (cylindersTimesHeads >= headsPerCylinder * 1024U || headsPerCylinder > 16)
{
sectorsPerTrack = 31;
headsPerCylinder = 16;
cylindersTimesHeads = totalSectors / sectorsPerTrack;
}
// We need 63 sectors per track to keep the cylinder count down
if (cylindersTimesHeads >= headsPerCylinder * 1024U)
{
sectorsPerTrack = 63;
headsPerCylinder = 16;
}
}
cylinders = totalSectors / sectorsPerTrack / headsPerCylinder;
return new Geometry(cylinders, headsPerCylinder, sectorsPerTrack, sectorSize);
}
/// <summary>
/// Converts a CHS (Cylinder,Head,Sector) address to a LBA (Logical Block Address).
/// </summary>
/// <param name="chsAddress">The CHS address to convert.</param>
/// <returns>The Logical Block Address (in sectors).</returns>
public long ToLogicalBlockAddress(ChsAddress chsAddress)
{
return ToLogicalBlockAddress(chsAddress.Cylinder, chsAddress.Head, chsAddress.Sector);
}
/// <summary>
/// Converts a CHS (Cylinder,Head,Sector) address to a LBA (Logical Block Address).
/// </summary>
/// <param name="cylinder">The cylinder of the address.</param>
/// <param name="head">The head of the address.</param>
/// <param name="sector">The sector of the address.</param>
/// <returns>The Logical Block Address (in sectors).</returns>
public long ToLogicalBlockAddress(int cylinder, int head, int sector)
{
if (cylinder < 0)
{
throw new ArgumentOutOfRangeException(nameof(cylinder), cylinder, "cylinder number is negative");
}
if (head >= HeadsPerCylinder)
{
throw new ArgumentOutOfRangeException(nameof(head), head, "head number is larger than disk geometry");
}
if (head < 0)
{
throw new ArgumentOutOfRangeException(nameof(head), head, "head number is negative");
}
if (sector > SectorsPerTrack)
{
throw new ArgumentOutOfRangeException(nameof(sector), sector,
"sector number is larger than disk geometry");
}
if (sector < 1)
{
throw new ArgumentOutOfRangeException(nameof(sector), sector,
"sector number is less than one (sectors are 1-based)");
}
return (cylinder * (long)HeadsPerCylinder + head) * SectorsPerTrack + sector - 1;
}
/// <summary>
/// Converts a LBA (Logical Block Address) to a CHS (Cylinder, Head, Sector) address.
/// </summary>
/// <param name="logicalBlockAddress">The logical block address (in sectors).</param>
/// <returns>The address in CHS form.</returns>
public ChsAddress ToChsAddress(long logicalBlockAddress)
{
if (logicalBlockAddress < 0)
{
throw new ArgumentOutOfRangeException(nameof(logicalBlockAddress), logicalBlockAddress,
"Logical Block Address is negative");
}
int cylinder = (int)(logicalBlockAddress / (HeadsPerCylinder * SectorsPerTrack));
int temp = (int)(logicalBlockAddress % (HeadsPerCylinder * SectorsPerTrack));
int head = temp / SectorsPerTrack;
int sector = temp % SectorsPerTrack + 1;
return new ChsAddress(cylinder, head, sector);
}
/// <summary>
/// Translates an IDE (aka Physical) geometry to a BIOS (aka Logical) geometry.
/// </summary>
/// <param name="translation">The translation to perform.</param>
/// <returns>The translated disk geometry.</returns>
public Geometry TranslateToBios(GeometryTranslation translation)
{
return TranslateToBios(0, translation);
}
/// <summary>
/// Translates an IDE (aka Physical) geometry to a BIOS (aka Logical) geometry.
/// </summary>
/// <param name="capacity">The capacity of the disk, required if the geometry is an approximation on the actual disk size.</param>
/// <param name="translation">The translation to perform.</param>
/// <returns>The translated disk geometry.</returns>
public Geometry TranslateToBios(long capacity, GeometryTranslation translation)
{
if (capacity <= 0)
{
capacity = TotalSectorsLong * 512L;
}
switch (translation)
{
case GeometryTranslation.None:
return this;
case GeometryTranslation.Auto:
if (IsBiosSafe)
{
return this;
}
return LbaAssistedBiosGeometry(capacity);
case GeometryTranslation.Lba:
return LbaAssistedBiosGeometry(capacity);
case GeometryTranslation.Large:
return LargeBiosGeometry(this);
default:
throw new ArgumentException(
string.Format(CultureInfo.InvariantCulture, "Translation mode '{0}' not yet implemented",
translation), nameof(translation));
}
}
/// <summary>
/// Determines if this object is equivalent to another.
/// </summary>
/// <param name="obj">The object to test against.</param>
/// <returns><c>true</c> if the <paramref name="obj"/> is equivalent, else <c>false</c>.</returns>
public override bool Equals(object obj)
{
if (obj == null || obj.GetType() != GetType())
{
return false;
}
Geometry other = (Geometry)obj;
return Cylinders == other.Cylinders && HeadsPerCylinder == other.HeadsPerCylinder
&& SectorsPerTrack == other.SectorsPerTrack && BytesPerSector == other.BytesPerSector;
}
/// <summary>
/// Calculates the hash code for this object.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode()
{
return Cylinders.GetHashCode() ^ HeadsPerCylinder.GetHashCode()
^ SectorsPerTrack.GetHashCode() ^ BytesPerSector.GetHashCode();
}
/// <summary>
/// Gets a string representation of this object, in the form (C/H/S).
/// </summary>
/// <returns>The string representation.</returns>
public override string ToString()
{
if (BytesPerSector == 512)
{
return "(" + Cylinders + "/" + HeadsPerCylinder + "/" + SectorsPerTrack + ")";
}
return "(" + Cylinders + "/" + HeadsPerCylinder + "/" + SectorsPerTrack + ":" + BytesPerSector + ")";
}
}
}

View File

@ -0,0 +1,9 @@
namespace DiscUtils
{
/// <summary>
/// Delegate for calculating a disk geometry from a capacity.
/// </summary>
/// <param name="capacity">The disk capacity to convert.</param>
/// <returns>The appropriate geometry for the disk.</returns>
public delegate Geometry GeometryCalculation(long capacity);
}

View File

@ -0,0 +1,50 @@
//
// 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
{
/// <summary>
/// Enumeration of standard BIOS disk geometry translation methods.
/// </summary>
public enum GeometryTranslation
{
/// <summary>
/// Apply no translation.
/// </summary>
None = 0,
/// <summary>
/// Automatic, based on the physical geometry select the most appropriate translation.
/// </summary>
Auto = 1,
/// <summary>
/// LBA assisted translation, based on just the disk capacity.
/// </summary>
Lba = 2,
/// <summary>
/// Bit-shifting translation, based on the physical geometry of the disk.
/// </summary>
Large = 3
}
}

View File

@ -0,0 +1,81 @@
//
// 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 DiscUtils.Streams;
namespace DiscUtils
{
/// <summary>
/// Base class for all file systems based on a cluster model.
/// </summary>
public interface IClusterBasedFileSystem : IFileSystem
{
/// <summary>
/// Gets the size (in bytes) of each cluster.
/// </summary>
long ClusterSize { get; }
/// <summary>
/// Gets the total number of clusters managed by the file system.
/// </summary>
long TotalClusters { get; }
/// <summary>
/// Converts a cluster (index) into an absolute byte position in the underlying stream.
/// </summary>
/// <param name="cluster">The cluster to convert.</param>
/// <returns>The corresponding absolute byte position.</returns>
long ClusterToOffset(long cluster);
/// <summary>
/// Converts an absolute byte position in the underlying stream to a cluster (index).
/// </summary>
/// <param name="offset">The byte position to convert.</param>
/// <returns>The cluster containing the specified byte.</returns>
long OffsetToCluster(long offset);
/// <summary>
/// Converts a file name to the list of clusters occupied by the file's data.
/// </summary>
/// <param name="path">The path to inspect.</param>
/// <returns>The clusters.</returns>
/// <remarks>Note that in some file systems, small files may not have dedicated
/// clusters. Only dedicated clusters will be returned.</remarks>
Range<long, long>[] PathToClusters(string path);
/// <summary>
/// Converts a file name to the extents containing its data.
/// </summary>
/// <param name="path">The path to inspect.</param>
/// <returns>The file extents, as absolute byte positions in the underlying stream.</returns>
/// <remarks>Use this method with caution - not all file systems will store all bytes
/// directly in extents. Files may be compressed, sparse or encrypted. This method
/// merely indicates where file data is stored, not what's stored.</remarks>
StreamExtent[] PathToExtents(string path);
/// <summary>
/// Gets an object that can convert between clusters and files.
/// </summary>
/// <returns>The cluster map.</returns>
ClusterMap BuildClusterMap();
}
}

View File

@ -0,0 +1,39 @@
//
// 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.IO;
namespace DiscUtils
{
/// <summary>
/// Interface exposed by objects that can provide a structured trace of their content.
/// </summary>
public interface IDiagnosticTraceable
{
/// <summary>
/// Writes a diagnostic report about the state of the object to a writer.
/// </summary>
/// <param name="writer">The writer to send the report to.</param>
/// <param name="linePrefix">The prefix to place at the start of each line.</param>
void Dump(TextWriter writer, string linePrefix);
}
}

View File

@ -0,0 +1,367 @@
//
// 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.IO;
using DiscUtils.Streams;
namespace DiscUtils
{
/// <summary>
/// Common interface for all file systems.
/// </summary>
public interface IFileSystem
{
/// <summary>
/// Gets a value indicating whether the file system is read-only or read-write.
/// </summary>
/// <returns>true if the file system is read-write.</returns>
bool CanWrite { get; }
/// <summary>
/// Gets a value indicating whether the file system is thread-safe.
/// </summary>
bool IsThreadSafe { get; }
/// <summary>
/// Gets the root directory of the file system.
/// </summary>
DiscDirectoryInfo Root { get; }
/// <summary>
/// Copies an existing file to a new file.
/// </summary>
/// <param name="sourceFile">The source file.</param>
/// <param name="destinationFile">The destination file.</param>
void CopyFile(string sourceFile, string destinationFile);
/// <summary>
/// Copies an existing file to a new file, allowing overwriting of an existing file.
/// </summary>
/// <param name="sourceFile">The source file.</param>
/// <param name="destinationFile">The destination file.</param>
/// <param name="overwrite">Whether to permit over-writing of an existing file.</param>
void CopyFile(string sourceFile, string destinationFile, bool overwrite);
/// <summary>
/// Creates a directory.
/// </summary>
/// <param name="path">The path of the new directory.</param>
void CreateDirectory(string path);
/// <summary>
/// Deletes a directory.
/// </summary>
/// <param name="path">The path of the directory to delete.</param>
void DeleteDirectory(string path);
/// <summary>
/// Deletes a directory, optionally with all descendants.
/// </summary>
/// <param name="path">The path of the directory to delete.</param>
/// <param name="recursive">Determines if the all descendants should be deleted.</param>
void DeleteDirectory(string path, bool recursive);
/// <summary>
/// Deletes a file.
/// </summary>
/// <param name="path">The path of the file to delete.</param>
void DeleteFile(string path);
/// <summary>
/// Indicates if a directory exists.
/// </summary>
/// <param name="path">The path to test.</param>
/// <returns>true if the directory exists.</returns>
bool DirectoryExists(string path);
/// <summary>
/// Indicates if a file exists.
/// </summary>
/// <param name="path">The path to test.</param>
/// <returns>true if the file exists.</returns>
bool FileExists(string path);
/// <summary>
/// Indicates if a file or directory exists.
/// </summary>
/// <param name="path">The path to test.</param>
/// <returns>true if the file or directory exists.</returns>
bool Exists(string path);
/// <summary>
/// Gets the names of subdirectories in a specified directory.
/// </summary>
/// <param name="path">The path to search.</param>
/// <returns>Array of directories.</returns>
string[] GetDirectories(string path);
/// <summary>
/// Gets the names of subdirectories in a specified directory matching a specified
/// search pattern.
/// </summary>
/// <param name="path">The path to search.</param>
/// <param name="searchPattern">The search string to match against.</param>
/// <returns>Array of directories matching the search pattern.</returns>
string[] GetDirectories(string path, string searchPattern);
/// <summary>
/// Gets the names of subdirectories in a specified directory matching a specified
/// search pattern, using a value to determine whether to search subdirectories.
/// </summary>
/// <param name="path">The path to search.</param>
/// <param name="searchPattern">The search string to match against.</param>
/// <param name="searchOption">Indicates whether to search subdirectories.</param>
/// <returns>Array of directories matching the search pattern.</returns>
string[] GetDirectories(string path, string searchPattern, SearchOption searchOption);
/// <summary>
/// Gets the names of files in a specified directory.
/// </summary>
/// <param name="path">The path to search.</param>
/// <returns>Array of files.</returns>
string[] GetFiles(string path);
/// <summary>
/// Gets the names of files in a specified directory.
/// </summary>
/// <param name="path">The path to search.</param>
/// <param name="searchPattern">The search string to match against.</param>
/// <returns>Array of files matching the search pattern.</returns>
string[] GetFiles(string path, string searchPattern);
/// <summary>
/// Gets the names of files in a specified directory matching a specified
/// search pattern, using a value to determine whether to search subdirectories.
/// </summary>
/// <param name="path">The path to search.</param>
/// <param name="searchPattern">The search string to match against.</param>
/// <param name="searchOption">Indicates whether to search subdirectories.</param>
/// <returns>Array of files matching the search pattern.</returns>
string[] GetFiles(string path, string searchPattern, SearchOption searchOption);
/// <summary>
/// Gets the names of all files and subdirectories in a specified directory.
/// </summary>
/// <param name="path">The path to search.</param>
/// <returns>Array of files and subdirectories matching the search pattern.</returns>
string[] GetFileSystemEntries(string path);
/// <summary>
/// Gets the names of files and subdirectories in a specified directory matching a specified
/// search pattern.
/// </summary>
/// <param name="path">The path to search.</param>
/// <param name="searchPattern">The search string to match against.</param>
/// <returns>Array of files and subdirectories matching the search pattern.</returns>
string[] GetFileSystemEntries(string path, string searchPattern);
/// <summary>
/// Moves a directory.
/// </summary>
/// <param name="sourceDirectoryName">The directory to move.</param>
/// <param name="destinationDirectoryName">The target directory name.</param>
void MoveDirectory(string sourceDirectoryName, string destinationDirectoryName);
/// <summary>
/// Moves a file.
/// </summary>
/// <param name="sourceName">The file to move.</param>
/// <param name="destinationName">The target file name.</param>
void MoveFile(string sourceName, string destinationName);
/// <summary>
/// Moves a file, allowing an existing file to be overwritten.
/// </summary>
/// <param name="sourceName">The file to move.</param>
/// <param name="destinationName">The target file name.</param>
/// <param name="overwrite">Whether to permit a destination file to be overwritten.</param>
void MoveFile(string sourceName, string destinationName, bool overwrite);
/// <summary>
/// Opens the specified file.
/// </summary>
/// <param name="path">The full path of the file to open.</param>
/// <param name="mode">The file mode for the created stream.</param>
/// <returns>The new stream.</returns>
SparseStream OpenFile(string path, FileMode mode);
/// <summary>
/// Opens the specified file.
/// </summary>
/// <param name="path">The full path of the file to open.</param>
/// <param name="mode">The file mode for the created stream.</param>
/// <param name="access">The access permissions for the created stream.</param>
/// <returns>The new stream.</returns>
SparseStream OpenFile(string path, FileMode mode, FileAccess access);
/// <summary>
/// Gets the attributes of a file or directory.
/// </summary>
/// <param name="path">The file or directory to inspect.</param>
/// <returns>The attributes of the file or directory.</returns>
FileAttributes GetAttributes(string path);
/// <summary>
/// Sets the attributes of a file or directory.
/// </summary>
/// <param name="path">The file or directory to change.</param>
/// <param name="newValue">The new attributes of the file or directory.</param>
void SetAttributes(string path, FileAttributes newValue);
/// <summary>
/// Gets the creation time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The creation time.</returns>
DateTime GetCreationTime(string path);
/// <summary>
/// Sets the creation time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
void SetCreationTime(string path, DateTime newTime);
/// <summary>
/// Gets the creation time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The creation time.</returns>
DateTime GetCreationTimeUtc(string path);
/// <summary>
/// Sets the creation time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
void SetCreationTimeUtc(string path, DateTime newTime);
/// <summary>
/// Gets the last access time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The last access time.</returns>
DateTime GetLastAccessTime(string path);
/// <summary>
/// Sets the last access time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
void SetLastAccessTime(string path, DateTime newTime);
/// <summary>
/// Gets the last access time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The last access time.</returns>
DateTime GetLastAccessTimeUtc(string path);
/// <summary>
/// Sets the last access time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
void SetLastAccessTimeUtc(string path, DateTime newTime);
/// <summary>
/// Gets the last modification time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The last write time.</returns>
DateTime GetLastWriteTime(string path);
/// <summary>
/// Sets the last modification time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
void SetLastWriteTime(string path, DateTime newTime);
/// <summary>
/// Gets the last modification time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The last write time.</returns>
DateTime GetLastWriteTimeUtc(string path);
/// <summary>
/// Sets the last modification time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
void SetLastWriteTimeUtc(string path, DateTime newTime);
/// <summary>
/// Gets the length of a file.
/// </summary>
/// <param name="path">The path to the file.</param>
/// <returns>The length in bytes.</returns>
long GetFileLength(string path);
/// <summary>
/// Gets an object representing a possible file.
/// </summary>
/// <param name="path">The file path.</param>
/// <returns>The representing object.</returns>
/// <remarks>The file does not need to exist.</remarks>
DiscFileInfo GetFileInfo(string path);
/// <summary>
/// Gets an object representing a possible directory.
/// </summary>
/// <param name="path">The directory path.</param>
/// <returns>The representing object.</returns>
/// <remarks>The directory does not need to exist.</remarks>
DiscDirectoryInfo GetDirectoryInfo(string path);
/// <summary>
/// Gets an object representing a possible file system object (file or directory).
/// </summary>
/// <param name="path">The file system path.</param>
/// <returns>The representing object.</returns>
/// <remarks>The file system object does not need to exist.</remarks>
DiscFileSystemInfo GetFileSystemInfo(string path);
/// <summary>
/// Reads the boot code of the file system into a byte array.
/// </summary>
/// <returns>The boot code, or <c>null</c> if not available.</returns>
byte[] ReadBootCode();
/// <summary>
/// Size of the Filesystem in bytes
/// </summary>
long Size { get; }
/// <summary>
/// Used space of the Filesystem in bytes
/// </summary>
long UsedSpace { get; }
/// <summary>
/// Available space of the Filesystem in bytes
/// </summary>
long AvailableSpace { get; }
}
}

View File

@ -0,0 +1,38 @@
//
// 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
{
/// <summary>
/// Provides the base class for all file systems that support Unix semantics.
/// </summary>
public interface IUnixFileSystem : IFileSystem
{
/// <summary>
/// Retrieves Unix-specific information about a file or directory.
/// </summary>
/// <param name="path">Path to the file or directory.</param>
/// <returns>Information about the owner, group, permissions and type of the
/// file or directory.</returns>
UnixFileSystemInfo GetUnixFileInfo(string path);
}
}

View File

@ -0,0 +1,129 @@
//
// 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.Security.AccessControl;
namespace DiscUtils
{
/// <summary>
/// Provides the base class for all file systems that support Windows semantics.
/// </summary>
public interface IWindowsFileSystem : IFileSystem
{
/// <summary>
/// Gets the security descriptor associated with the file or directory.
/// </summary>
/// <param name="path">The file or directory to inspect.</param>
/// <returns>The security descriptor.</returns>
RawSecurityDescriptor GetSecurity(string path);
/// <summary>
/// Sets the security descriptor associated with the file or directory.
/// </summary>
/// <param name="path">The file or directory to change.</param>
/// <param name="securityDescriptor">The new security descriptor.</param>
void SetSecurity(string path, RawSecurityDescriptor securityDescriptor);
/// <summary>
/// Gets the reparse point data associated with a file or directory.
/// </summary>
/// <param name="path">The file to query.</param>
/// <returns>The reparse point information.</returns>
ReparsePoint GetReparsePoint(string path);
/// <summary>
/// Sets the reparse point data on a file or directory.
/// </summary>
/// <param name="path">The file to set the reparse point on.</param>
/// <param name="reparsePoint">The new reparse point.</param>
void SetReparsePoint(string path, ReparsePoint reparsePoint);
/// <summary>
/// Removes a reparse point from a file or directory, without deleting the file or directory.
/// </summary>
/// <param name="path">The path to the file or directory to remove the reparse point from.</param>
void RemoveReparsePoint(string path);
/// <summary>
/// Gets the short name for a given path.
/// </summary>
/// <param name="path">The path to convert.</param>
/// <returns>The short name.</returns>
/// <remarks>
/// This method only gets the short name for the final part of the path, to
/// convert a complete path, call this method repeatedly, once for each path
/// segment. If there is no short name for the given path,<c>null</c> is
/// returned.
/// </remarks>
string GetShortName(string path);
/// <summary>
/// Sets the short name for a given file or directory.
/// </summary>
/// <param name="path">The full path to the file or directory to change.</param>
/// <param name="shortName">The shortName, which should not include a path.</param>
void SetShortName(string path, string shortName);
/// <summary>
/// Gets the standard file information for a file.
/// </summary>
/// <param name="path">The full path to the file or directory to query.</param>
/// <returns>The standard file information.</returns>
WindowsFileInformation GetFileStandardInformation(string path);
/// <summary>
/// Sets the standard file information for a file.
/// </summary>
/// <param name="path">The full path to the file or directory to query.</param>
/// <param name="info">The standard file information.</param>
void SetFileStandardInformation(string path, WindowsFileInformation info);
/// <summary>
/// Gets the names of the alternate data streams for a file.
/// </summary>
/// <param name="path">The path to the file.</param>
/// <returns>
/// The list of alternate data streams (or empty, if none). To access the contents
/// of the alternate streams, use OpenFile(path + ":" + name, ...).
/// </returns>
string[] GetAlternateDataStreams(string path);
/// <summary>
/// Gets the file id for a given path.
/// </summary>
/// <param name="path">The path to get the id of.</param>
/// <returns>The file id, or -1.</returns>
/// <remarks>
/// The returned file id uniquely identifies the file, and is shared by all hard
/// links to the same file. The value -1 indicates no unique identifier is
/// available, and so it can be assumed the file has no hard links.
/// </remarks>
long GetFileId(string path);
/// <summary>
/// Indicates whether the file is known by other names.
/// </summary>
/// <param name="path">The file to inspect.</param>
/// <returns><c>true</c> if the file has other names, else <c>false</c>.</returns>
bool HasHardLinks(string path);
}
}

View File

@ -0,0 +1,43 @@
//
// 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.Internal
{
internal abstract class Crc32
{
protected readonly uint[] Table;
protected uint _value;
protected Crc32(uint[] table)
{
Table = table;
_value = 0xFFFFFFFF;
}
public uint Value
{
get { return _value ^ 0xFFFFFFFF; }
}
public abstract void Process(byte[] buffer, int offset, int count);
}
}

View File

@ -0,0 +1,47 @@
//
// 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.Internal
{
internal enum Crc32Algorithm
{
/// <summary>
/// Used in Ethernet, PKZIP, BZIP2, Gzip, PNG, etc. (aka CRC32).
/// </summary>
Common = 0,
/// <summary>
/// Used in iSCSI, SCTP, Btrfs, Vhdx. (aka CRC32C).
/// </summary>
Castagnoli = 1,
/// <summary>
/// Unknown usage. (aka CRC32K).
/// </summary>
Koopman = 2,
/// <summary>
/// Used in AIXM. (aka CRC32Q).
/// </summary>
Aeronautical = 3
}
}

View File

@ -0,0 +1,94 @@
//
// 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.Internal
{
/// <summary>
/// Calculates CRC32 of buffers.
/// </summary>
internal sealed class Crc32BigEndian : Crc32
{
private static readonly uint[][] Tables;
static Crc32BigEndian()
{
Tables = new uint[4][];
Tables[(int)Crc32Algorithm.Common] = CalcTable(0x04C11DB7);
Tables[(int)Crc32Algorithm.Castagnoli] = CalcTable(0x1EDC6F41);
Tables[(int)Crc32Algorithm.Koopman] = CalcTable(0x741B8CD7);
Tables[(int)Crc32Algorithm.Aeronautical] = CalcTable(0x814141AB);
}
public Crc32BigEndian(Crc32Algorithm algorithm)
: base(Tables[(int)algorithm]) {}
public static uint Compute(Crc32Algorithm algorithm, byte[] buffer, int offset, int count)
{
return Process(Tables[(int)algorithm], 0xFFFFFFFF, buffer, offset, count) ^ 0xFFFFFFFF;
}
public override void Process(byte[] buffer, int offset, int count)
{
_value = Process(Table, _value, buffer, offset, count);
}
private static uint[] CalcTable(uint polynomial)
{
uint[] table = new uint[256];
for (uint i = 0; i < 256; ++i)
{
uint crc = i << 24;
for (int j = 8; j > 0; --j)
{
if ((crc & 0x80000000) != 0)
{
crc = (crc << 1) ^ polynomial;
}
else
{
crc <<= 1;
}
}
table[i] = crc;
}
return table;
}
private static uint Process(uint[] table, uint accumulator, byte[] buffer, int offset, int count)
{
uint value = accumulator;
for (int i = 0; i < count; ++i)
{
byte b = buffer[offset + i];
value = table[(value >> 24) ^ b] ^ (value << 8);
}
return value;
}
}
}

View File

@ -0,0 +1,98 @@
//
// 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.Internal
{
/// <summary>
/// Calculates CRC32 of buffers.
/// </summary>
internal sealed class Crc32LittleEndian : Crc32
{
private static readonly uint[][] Tables;
static Crc32LittleEndian()
{
Tables = new uint[4][];
Tables[(int)Crc32Algorithm.Common] = CalcTable(0xEDB88320);
Tables[(int)Crc32Algorithm.Castagnoli] = CalcTable(0x82F63B78);
Tables[(int)Crc32Algorithm.Koopman] = CalcTable(0xEB31D82E);
Tables[(int)Crc32Algorithm.Aeronautical] = CalcTable(0xD5828281);
}
public Crc32LittleEndian(Crc32Algorithm algorithm)
: base(Tables[(int)algorithm]) {}
public static uint Compute(Crc32Algorithm algorithm, byte[] buffer, int offset, int count)
{
return Process(Tables[(int)algorithm], 0xFFFFFFFF, buffer, offset, count) ^ 0xFFFFFFFF;
}
public override void Process(byte[] buffer, int offset, int count)
{
_value = Process(Table, _value, buffer, offset, count);
}
private static uint[] CalcTable(uint polynomial)
{
uint[] table = new uint[256];
table[0] = 0;
for (uint i = 0; i <= 255; ++i)
{
uint crc = i;
for (int j = 8; j > 0; --j)
{
if ((crc & 1) != 0)
{
crc = (crc >> 1) ^ polynomial;
}
else
{
crc >>= 1;
}
}
table[i] = crc;
}
return table;
}
private static uint Process(uint[] table, uint accumulator, byte[] buffer, int offset, int count)
{
uint value = accumulator;
for (int i = 0; i < count; ++i)
{
byte b = buffer[offset + i];
uint temp1 = (value >> 8) & 0x00FFFFFF;
uint temp2 = table[(value ^ b) & 0xFF];
value = temp1 ^ temp2;
}
return value;
}
}
}

View File

@ -0,0 +1,108 @@
//
// 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.IO;
namespace DiscUtils.Internal
{
public sealed class LocalFileLocator : FileLocator
{
private readonly string _dir;
public LocalFileLocator(string dir)
{
_dir = dir;
}
public override bool Exists(string fileName)
{
return File.Exists(Path.Combine(_dir, fileName));
}
protected override Stream OpenFile(string fileName, FileMode mode, FileAccess access, FileShare share)
{
return new FileStream(Path.Combine(_dir, fileName), mode, access, share);
}
public override FileLocator GetRelativeLocator(string path)
{
return new LocalFileLocator(Path.Combine(_dir, path));
}
public override string GetFullPath(string path)
{
string combinedPath = Path.Combine(_dir, path);
if (string.IsNullOrEmpty(combinedPath))
{
#if NETSTANDARD1_5
return Directory.GetCurrentDirectory();
#else
return Environment.CurrentDirectory;
#endif
}
return Path.GetFullPath(combinedPath);
}
public override string GetDirectoryFromPath(string path)
{
return Path.GetDirectoryName(path);
}
public override string GetFileFromPath(string path)
{
return Path.GetFileName(path);
}
public override DateTime GetLastWriteTimeUtc(string path)
{
return File.GetLastWriteTimeUtc(Path.Combine(_dir, path));
}
public override bool HasCommonRoot(FileLocator other)
{
LocalFileLocator otherLocal = other as LocalFileLocator;
if (otherLocal == null)
{
return false;
}
// If the paths have drive specifiers, then common root depends on them having a common
// drive letter.
string otherDir = otherLocal._dir;
if (otherDir.Length >= 2 && _dir.Length >= 2)
{
if (otherDir[1] == ':' && _dir[1] == ':')
{
return char.ToUpperInvariant(otherDir[0]) == char.ToUpperInvariant(_dir[0]);
}
}
return true;
}
public override string ResolveRelativePath(string path)
{
return Utilities.ResolveRelativePath(_dir, path);
}
}
}

View File

@ -0,0 +1,33 @@
//
// 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.Collections.Generic;
namespace DiscUtils.Internal
{
internal abstract class LogicalVolumeFactory
{
public abstract bool HandlesPhysicalVolume(PhysicalVolumeInfo volume);
public abstract void MapDisks(IEnumerable<VirtualDisk> disks, Dictionary<string, LogicalVolumeInfo> result);
}
}

View File

@ -0,0 +1,29 @@
//
// 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;
namespace DiscUtils.Internal
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
internal sealed class LogicalVolumeFactoryAttribute : Attribute {}
}

View File

@ -0,0 +1,149 @@
//
// 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;
namespace DiscUtils.Internal
{
/// <summary>
/// Caches objects.
/// </summary>
/// <typeparam name="K">The type of the object key.</typeparam>
/// <typeparam name="V">The type of the objects to cache.</typeparam>
/// <remarks>
/// Can be use for two purposes - to ensure there is only one instance of a given object,
/// and to prevent the need to recreate objects that are expensive to create.
/// </remarks>
internal class ObjectCache<K, V>
{
private const int MostRecentListSize = 20;
private const int PruneGap = 500;
private readonly Dictionary<K, WeakReference> _entries;
private int _nextPruneCount;
private readonly List<KeyValuePair<K, V>> _recent;
public ObjectCache()
{
_entries = new Dictionary<K, WeakReference>();
_recent = new List<KeyValuePair<K, V>>();
}
public V this[K key]
{
get
{
for (int i = 0; i < _recent.Count; ++i)
{
KeyValuePair<K, V> recentEntry = _recent[i];
if (recentEntry.Key.Equals(key))
{
MakeMostRecent(i);
return recentEntry.Value;
}
}
WeakReference wRef;
if (_entries.TryGetValue(key, out wRef))
{
V val = (V)wRef.Target;
if (val != null)
{
MakeMostRecent(key, val);
}
return val;
}
return default(V);
}
set
{
_entries[key] = new WeakReference(value);
MakeMostRecent(key, value);
PruneEntries();
}
}
internal void Remove(K key)
{
for (int i = 0; i < _recent.Count; ++i)
{
if (_recent[i].Key.Equals(key))
{
_recent.RemoveAt(i);
break;
}
}
_entries.Remove(key);
}
private void PruneEntries()
{
_nextPruneCount++;
if (_nextPruneCount > PruneGap)
{
List<K> toPrune = new List<K>();
foreach (KeyValuePair<K, WeakReference> entry in _entries)
{
if (!entry.Value.IsAlive)
{
toPrune.Add(entry.Key);
}
}
foreach (K key in toPrune)
{
_entries.Remove(key);
}
_nextPruneCount = 0;
}
}
private void MakeMostRecent(int i)
{
if (i == 0)
{
return;
}
KeyValuePair<K, V> entry = _recent[i];
_recent.RemoveAt(i);
_recent.Insert(0, entry);
}
private void MakeMostRecent(K key, V val)
{
while (_recent.Count >= MostRecentListSize)
{
_recent.RemoveAt(_recent.Count - 1);
}
_recent.Insert(0, new KeyValuePair<K, V>(key, val));
}
}
}

View File

@ -0,0 +1,467 @@
//
// 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.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace DiscUtils.Internal
{
public static class Utilities
{
/// <summary>
/// Converts between two arrays.
/// </summary>
/// <typeparam name="T">The type of the elements of the source array.</typeparam>
/// <typeparam name="U">The type of the elements of the destination array.</typeparam>
/// <param name="source">The source array.</param>
/// <param name="func">The function to map from source type to destination type.</param>
/// <returns>The resultant array.</returns>
public static U[] Map<T, U>(ICollection<T> source, Func<T, U> func)
{
U[] result = new U[source.Count];
int i = 0;
foreach (T sVal in source)
{
result[i++] = func(sVal);
}
return result;
}
/// <summary>
/// Converts between two arrays.
/// </summary>
/// <typeparam name="T">The type of the elements of the source array.</typeparam>
/// <typeparam name="U">The type of the elements of the destination array.</typeparam>
/// <param name="source">The source array.</param>
/// <param name="func">The function to map from source type to destination type.</param>
/// <returns>The resultant array.</returns>
public static U[] Map<T, U>(IEnumerable<T> source, Func<T, U> func)
{
List<U> result = new List<U>();
foreach (T sVal in source)
{
result.Add(func(sVal));
}
return result.ToArray();
}
/// <summary>
/// Filters a collection into a new collection.
/// </summary>
/// <typeparam name="C">The type of the new collection.</typeparam>
/// <typeparam name="T">The type of the collection entries.</typeparam>
/// <param name="source">The collection to filter.</param>
/// <param name="predicate">The predicate to select which entries are carried over.</param>
/// <returns>The new collection, containing all entries where the predicate returns <c>true</c>.</returns>
public static C Filter<C, T>(ICollection<T> source, Func<T, bool> predicate) where C : ICollection<T>, new()
{
C result = new C();
foreach (T val in source)
{
if (predicate(val))
{
result.Add(val);
}
}
return result;
}
/// <summary>
/// Indicates if two ranges overlap.
/// </summary>
/// <typeparam name="T">The type of the ordinals.</typeparam>
/// <param name="xFirst">The lowest ordinal of the first range (inclusive).</param>
/// <param name="xLast">The highest ordinal of the first range (exclusive).</param>
/// <param name="yFirst">The lowest ordinal of the second range (inclusive).</param>
/// <param name="yLast">The highest ordinal of the second range (exclusive).</param>
/// <returns><c>true</c> if the ranges overlap, else <c>false</c>.</returns>
public static bool RangesOverlap<T>(T xFirst, T xLast, T yFirst, T yLast) where T : IComparable<T>
{
return !((xLast.CompareTo(yFirst) <= 0) || (xFirst.CompareTo(yLast) >= 0));
}
#region Bit Twiddling
public static bool IsAllZeros(byte[] buffer, int offset, int count)
{
int end = offset + count;
for (int i = offset; i < end; ++i)
{
if (buffer[i] != 0)
{
return false;
}
}
return true;
}
public static bool IsPowerOfTwo(uint val)
{
if (val == 0)
{
return false;
}
while ((val & 1) != 1)
{
val >>= 1;
}
return val == 1;
}
public static bool IsPowerOfTwo(long val)
{
if (val == 0)
{
return false;
}
while ((val & 1) != 1)
{
val >>= 1;
}
return val == 1;
}
public static bool AreEqual(byte[] a, byte[] b)
{
if (a.Length != b.Length)
{
return false;
}
for (int i = 0; i < a.Length; ++i)
{
if (a[i] != b[i])
{
return false;
}
}
return true;
}
public static ushort BitSwap(ushort value)
{
return (ushort)(((value & 0x00FF) << 8) | ((value & 0xFF00) >> 8));
}
public static uint BitSwap(uint value)
{
return ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0x00FF0000) >> 8) |
((value & 0xFF000000) >> 24);
}
public static ulong BitSwap(ulong value)
{
return ((ulong)BitSwap((uint)(value & 0xFFFFFFFF)) << 32) | BitSwap((uint)(value >> 32));
}
public static short BitSwap(short value)
{
return (short)BitSwap((ushort)value);
}
public static int BitSwap(int value)
{
return (int)BitSwap((uint)value);
}
public static long BitSwap(long value)
{
return (long)BitSwap((ulong)value);
}
#endregion
#region Path Manipulation
/// <summary>
/// Extracts the directory part of a path.
/// </summary>
/// <param name="path">The path to process.</param>
/// <returns>The directory part.</returns>
public static string GetDirectoryFromPath(string path)
{
string trimmed = path.TrimEnd('\\');
int index = trimmed.LastIndexOf('\\');
if (index < 0)
{
return string.Empty; // No directory, just a file name
}
return trimmed.Substring(0, index);
}
/// <summary>
/// Extracts the file part of a path.
/// </summary>
/// <param name="path">The path to process.</param>
/// <returns>The file part of the path.</returns>
public static string GetFileFromPath(string path)
{
string trimmed = path.Trim('\\');
int index = trimmed.LastIndexOf('\\');
if (index < 0)
{
return trimmed; // No directory, just a file name
}
return trimmed.Substring(index + 1);
}
/// <summary>
/// Combines two paths.
/// </summary>
/// <param name="a">The first part of the path.</param>
/// <param name="b">The second part of the path.</param>
/// <returns>The combined path.</returns>
public static string CombinePaths(string a, string b)
{
if (string.IsNullOrEmpty(a) || (b.Length > 0 && b[0] == '\\'))
{
return b;
}
if (string.IsNullOrEmpty(b))
{
return a;
}
return a.TrimEnd('\\') + '\\' + b.TrimStart('\\');
}
/// <summary>
/// Resolves a relative path into an absolute one.
/// </summary>
/// <param name="basePath">The base path to resolve from.</param>
/// <param name="relativePath">The relative path.</param>
/// <returns>The absolute path. If no <paramref name="basePath"/> is specified
/// then relativePath is returned as-is. If <paramref name="relativePath"/>
/// contains more '..' characters than the base path contains levels of
/// directory, the resultant string be the root drive followed by the file name.
/// If no the basePath starts with '\' (no drive specified) then the returned
/// path will also start with '\'.
/// For example: (\TEMP\Foo.txt, ..\..\Bar.txt) gives (\Bar.txt).
/// </returns>
public static string ResolveRelativePath(string basePath, string relativePath)
{
if (string.IsNullOrEmpty(basePath))
{
return relativePath;
}
if (!basePath.EndsWith(@"\"))
basePath = Path.GetDirectoryName(basePath);
string merged = Path.GetFullPath(Path.Combine(basePath, relativePath));
if (basePath.StartsWith(@"\") && merged.Length > 2 && merged[1].Equals(':'))
{
return merged.Substring(2);
}
return merged;
}
public static string ResolvePath(string basePath, string path)
{
if (!path.StartsWith("\\", StringComparison.OrdinalIgnoreCase))
{
return ResolveRelativePath(basePath, path);
}
return path;
}
public static string MakeRelativePath(string path, string basePath)
{
List<string> pathElements =
new List<string>(path.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries));
List<string> basePathElements =
new List<string>(basePath.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries));
if (!basePath.EndsWith("\\", StringComparison.Ordinal) && basePathElements.Count > 0)
{
basePathElements.RemoveAt(basePathElements.Count - 1);
}
// Find first part of paths that don't match
int i = 0;
while (i < Math.Min(pathElements.Count - 1, basePathElements.Count))
{
if (pathElements[i].ToUpperInvariant() != basePathElements[i].ToUpperInvariant())
{
break;
}
++i;
}
// For each remaining part of the base path, insert '..'
StringBuilder result = new StringBuilder();
if (i == basePathElements.Count)
{
result.Append(@".\");
}
else if (i < basePathElements.Count)
{
for (int j = 0; j < basePathElements.Count - i; ++j)
{
result.Append(@"..\");
}
}
// For each remaining part of the path, add the path element
for (int j = i; j < pathElements.Count - 1; ++j)
{
result.Append(pathElements[j]);
result.Append(@"\");
}
result.Append(pathElements[pathElements.Count - 1]);
// If the target was a directory, put the terminator back
if (path.EndsWith(@"\", StringComparison.Ordinal))
{
result.Append(@"\");
}
return result.ToString();
}
#endregion
#region Filesystem Support
/// <summary>
/// Indicates if a file name matches the 8.3 pattern.
/// </summary>
/// <param name="name">The name to test.</param>
/// <returns><c>true</c> if the name is 8.3, otherwise <c>false</c>.</returns>
public static bool Is8Dot3(string name)
{
if (name.Length > 12)
{
return false;
}
string[] split = name.Split('.');
if (split.Length > 2 || split.Length < 1)
{
return false;
}
if (split[0].Length > 8)
{
return false;
}
foreach (char ch in split[0])
{
if (!Is8Dot3Char(ch))
{
return false;
}
}
if (split.Length > 1)
{
if (split[1].Length > 3)
{
return false;
}
foreach (char ch in split[1])
{
if (!Is8Dot3Char(ch))
{
return false;
}
}
}
return true;
}
public static bool Is8Dot3Char(char ch)
{
return (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || "_^$~!#%£-{}()@'`&".IndexOf(ch) != -1;
}
/// <summary>
/// Converts a 'standard' wildcard file/path specification into a regular expression.
/// </summary>
/// <param name="pattern">The wildcard pattern to convert.</param>
/// <returns>The resultant regular expression.</returns>
/// <remarks>
/// The wildcard * (star) matches zero or more characters (including '.'), and ?
/// (question mark) matches precisely one character (except '.').
/// </remarks>
public static Regex ConvertWildcardsToRegEx(string pattern)
{
if (!pattern.Contains("."))
{
pattern += ".";
}
string query = "^" + Regex.Escape(pattern).Replace(@"\*", ".*").Replace(@"\?", "[^.]") + "$";
return new Regex(query, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
}
public static FileAttributes FileAttributesFromUnixFileType(UnixFileType fileType)
{
switch (fileType)
{
case UnixFileType.Fifo:
return FileAttributes.Device | FileAttributes.System;
case UnixFileType.Character:
return FileAttributes.Device | FileAttributes.System;
case UnixFileType.Directory:
return FileAttributes.Directory;
case UnixFileType.Block:
return FileAttributes.Device | FileAttributes.System;
case UnixFileType.Regular:
return FileAttributes.Normal;
case UnixFileType.Link:
return FileAttributes.ReparsePoint;
case UnixFileType.Socket:
return FileAttributes.Device | FileAttributes.System;
default:
return 0;
}
}
#endregion
}
}

View File

@ -0,0 +1,56 @@
//
// 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.Collections.Generic;
using System.IO;
namespace DiscUtils.Internal
{
internal abstract class VirtualDiskFactory
{
public abstract string[] Variants { get; }
public abstract VirtualDiskTypeInfo GetDiskTypeInformation(string variant);
public abstract DiskImageBuilder GetImageBuilder(string variant);
public abstract VirtualDisk CreateDisk(FileLocator locator, string variant, string path,
VirtualDiskParameters diskParameters);
public abstract VirtualDisk OpenDisk(string path, FileAccess access);
public abstract VirtualDisk OpenDisk(FileLocator locator, string path, FileAccess access);
public virtual VirtualDisk OpenDisk(FileLocator locator, string path, string extraInfo,
Dictionary<string, string> parameters, FileAccess access)
{
return OpenDisk(locator, path, access);
}
public VirtualDisk OpenDisk(DiscFileSystem fileSystem, string path, FileAccess access)
{
return OpenDisk(new DiscFileLocator(fileSystem, @"\"), path, access);
}
public abstract VirtualDiskLayer OpenDiskLayer(FileLocator locator, string path, FileAccess access);
}
}

View File

@ -0,0 +1,40 @@
//
// 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;
namespace DiscUtils.Internal
{
[AttributeUsage(AttributeTargets.Class)]
internal sealed class VirtualDiskFactoryAttribute : Attribute
{
public VirtualDiskFactoryAttribute(string type, string fileExtensions)
{
Type = type;
FileExtensions = fileExtensions.Replace(".", string.Empty).Split(',');
}
public string[] FileExtensions { get; }
public string Type { get; }
}
}

View File

@ -0,0 +1,50 @@
//
// 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.IO;
namespace DiscUtils.Internal
{
internal abstract class VirtualDiskTransport : IDisposable
{
public abstract bool IsRawDisk { get; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public abstract void Connect(Uri uri, string username, string password);
public abstract VirtualDisk OpenDisk(FileAccess access);
public abstract FileLocator GetFileLocator();
public abstract string GetFileName();
public abstract string GetExtraInfo();
protected virtual void Dispose(bool disposing) {}
}
}

View File

@ -0,0 +1,37 @@
//
// 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;
namespace DiscUtils.Internal
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
internal sealed class VirtualDiskTransportAttribute : Attribute
{
public VirtualDiskTransportAttribute(string scheme)
{
Scheme = scheme;
}
public string Scheme { get; }
}
}

View File

@ -0,0 +1,72 @@
//
// 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.IO;
#if !NETSTANDARD1_5
using System.Runtime.Serialization;
#endif
namespace DiscUtils
{
/// <summary>
/// Exception thrown when some invalid file system data is found, indicating probably corruption.
/// </summary>
#if !NETSTANDARD1_5
[Serializable]
#endif
public class InvalidFileSystemException : IOException
{
/// <summary>
/// Initializes a new instance of the InvalidFileSystemException class.
/// </summary>
public InvalidFileSystemException() {}
/// <summary>
/// Initializes a new instance of the InvalidFileSystemException class.
/// </summary>
/// <param name="message">The exception message.</param>
public InvalidFileSystemException(string message)
: base(message) {}
/// <summary>
/// Initializes a new instance of the InvalidFileSystemException class.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="innerException">The inner exception.</param>
public InvalidFileSystemException(string message, Exception innerException)
: base(message, innerException) {}
#if !NETSTANDARD1_5
/// <summary>
/// Initializes a new instance of the InvalidFileSystemException class.
/// </summary>
/// <param name="info">The serialization info.</param>
/// <param name="context">The streaming context.</param>
protected InvalidFileSystemException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
#endif
}
}

View File

@ -0,0 +1,64 @@
//
// 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.LogicalDiskManager
{
internal sealed class ComponentRecord : DatabaseRecord
{
public uint LinkId; // Identical on mirrors
public ExtentMergeType MergeType; // (02 Spanned, Simple, Mirrored) (01 on striped)
public ulong NumExtents; // Could be num disks
public string StatusString;
public long StripeSizeSectors;
public long StripeStride; // aka num partitions
public uint Unknown1; // Zero
public uint Unknown2; // Zero
public ulong Unknown3; // 00 .. 00
public ulong Unknown4; // ??
public ulong VolumeId;
protected override void DoReadFrom(byte[] buffer, int offset)
{
base.DoReadFrom(buffer, offset);
int pos = offset + 0x18;
Id = ReadVarULong(buffer, ref pos);
Name = ReadVarString(buffer, ref pos);
StatusString = ReadVarString(buffer, ref pos);
MergeType = (ExtentMergeType)ReadByte(buffer, ref pos);
Unknown1 = ReadUInt(buffer, ref pos); // Zero
NumExtents = ReadVarULong(buffer, ref pos);
Unknown2 = ReadUInt(buffer, ref pos);
LinkId = ReadUInt(buffer, ref pos);
Unknown3 = ReadULong(buffer, ref pos); // Zero
VolumeId = ReadVarULong(buffer, ref pos);
Unknown4 = ReadVarULong(buffer, ref pos); // Zero
if ((Flags & 0x1000) != 0)
{
StripeSizeSectors = ReadVarLong(buffer, ref pos);
StripeStride = ReadVarLong(buffer, ref pos);
}
}
}
}

View File

@ -0,0 +1,178 @@
//
// 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.IO;
using DiscUtils.Streams;
namespace DiscUtils.LogicalDiskManager
{
internal class Database
{
private readonly Dictionary<ulong, DatabaseRecord> _records;
private readonly DatabaseHeader _vmdb;
public Database(Stream stream)
{
long dbStart = stream.Position;
byte[] buffer = new byte[Sizes.Sector];
stream.Read(buffer, 0, buffer.Length);
_vmdb = new DatabaseHeader();
_vmdb.ReadFrom(buffer, 0);
stream.Position = dbStart + _vmdb.HeaderSize;
buffer = StreamUtilities.ReadExact(stream, (int)(_vmdb.BlockSize * _vmdb.NumVBlks));
_records = new Dictionary<ulong, DatabaseRecord>();
for (int i = 0; i < _vmdb.NumVBlks; ++i)
{
DatabaseRecord rec = DatabaseRecord.ReadFrom(buffer, (int)(i * _vmdb.BlockSize));
if (rec != null)
{
_records.Add(rec.Id, rec);
}
}
}
internal IEnumerable<DiskRecord> Disks
{
get
{
foreach (DatabaseRecord record in _records.Values)
{
if (record.RecordType == RecordType.Disk)
{
yield return (DiskRecord)record;
}
}
}
}
internal IEnumerable<VolumeRecord> Volumes
{
get
{
foreach (DatabaseRecord record in _records.Values)
{
if (record.RecordType == RecordType.Volume)
{
yield return (VolumeRecord)record;
}
}
}
}
internal DiskGroupRecord GetDiskGroup(Guid guid)
{
foreach (DatabaseRecord record in _records.Values)
{
if (record.RecordType == RecordType.DiskGroup)
{
DiskGroupRecord dgRecord = (DiskGroupRecord)record;
if (new Guid(dgRecord.GroupGuidString) == guid || guid == Guid.Empty)
{
return dgRecord;
}
}
}
return null;
}
internal IEnumerable<ComponentRecord> GetVolumeComponents(ulong volumeId)
{
foreach (DatabaseRecord record in _records.Values)
{
if (record.RecordType == RecordType.Component)
{
ComponentRecord cmpntRecord = (ComponentRecord)record;
if (cmpntRecord.VolumeId == volumeId)
{
yield return cmpntRecord;
}
}
}
}
internal IEnumerable<ExtentRecord> GetComponentExtents(ulong componentId)
{
foreach (DatabaseRecord record in _records.Values)
{
if (record.RecordType == RecordType.Extent)
{
ExtentRecord extentRecord = (ExtentRecord)record;
if (extentRecord.ComponentId == componentId)
{
yield return extentRecord;
}
}
}
}
internal DiskRecord GetDisk(ulong diskId)
{
return (DiskRecord)_records[diskId];
}
internal VolumeRecord GetVolume(ulong volumeId)
{
return (VolumeRecord)_records[volumeId];
}
internal VolumeRecord GetVolume(Guid id)
{
return FindRecord<VolumeRecord>(r => r.VolumeGuid == id, RecordType.Volume);
}
internal IEnumerable<VolumeRecord> GetVolumes()
{
foreach (DatabaseRecord record in _records.Values)
{
if (record.RecordType == RecordType.Volume)
{
yield return (VolumeRecord)record;
}
}
}
internal T FindRecord<T>(Predicate<T> pred, RecordType typeId)
where T : DatabaseRecord
{
foreach (DatabaseRecord record in _records.Values)
{
if (record.RecordType == typeId)
{
T t = (T)record;
if (pred(t))
{
return t;
}
}
}
return null;
}
}
}

View File

@ -0,0 +1,94 @@
//
// 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 DiscUtils.Streams;
namespace DiscUtils.LogicalDiskManager
{
internal class DatabaseHeader
{
public uint BlockSize; // 00 00 00 80
public long CommittedSequence; // 0xA
public string DiskGroupId;
public string GroupName;
public uint HeaderSize; // 00 00 02 00
public uint NumVBlks; // 00 00 17 24
public long PendingSequence; // 0xA
public string Signature; // VMDB
public DateTime Timestamp;
public ushort Unknown1; // 00 01
public uint Unknown2; // 1
public uint Unknown3; // 1
public uint Unknown4; // 3
public uint Unknown5; // 3
public long Unknown6; // 0
public long Unknown7; // 1
public uint Unknown8; // 1
public uint Unknown9; // 3
public uint UnknownA; // 3
public long UnknownB; // 0
public uint UnknownC; // 0
public ushort VersionDenom; // 00 0a
public ushort VersionNum; // 00 04
public void ReadFrom(byte[] buffer, int offset)
{
Signature = EndianUtilities.BytesToString(buffer, offset + 0x00, 4);
NumVBlks = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x04);
BlockSize = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x08);
HeaderSize = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x0C);
Unknown1 = EndianUtilities.ToUInt16BigEndian(buffer, offset + 0x10);
VersionNum = EndianUtilities.ToUInt16BigEndian(buffer, offset + 0x12);
VersionDenom = EndianUtilities.ToUInt16BigEndian(buffer, offset + 0x14);
GroupName = EndianUtilities.BytesToString(buffer, offset + 0x16, 31).Trim('\0');
DiskGroupId = EndianUtilities.BytesToString(buffer, offset + 0x35, 0x40).Trim('\0');
// May be wrong way round...
CommittedSequence = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x75);
PendingSequence = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x7D);
Unknown2 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x85);
Unknown3 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x89);
Unknown4 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x8D);
Unknown5 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x91);
Unknown6 = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x95);
Unknown7 = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x9D);
Unknown8 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0xA5);
Unknown9 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0xA9);
UnknownA = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0xAD);
UnknownB = EndianUtilities.ToInt64BigEndian(buffer, offset + 0xB1);
UnknownC = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0xB9);
Timestamp = DateTime.FromFileTimeUtc(EndianUtilities.ToInt64BigEndian(buffer, offset + 0xBD));
}
////}
//// throw new NotImplementedException();
//// // Add all byte values for ?? bytes
//// // Zero checksum bytes (0x08, 4)
////{
////private static int CalcChecksum()
}
}

View File

@ -0,0 +1,154 @@
//
// 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 DiscUtils.Streams;
namespace DiscUtils.LogicalDiskManager
{
internal abstract class DatabaseRecord
{
public uint Counter;
public uint DataLength;
public uint Flags;
public ulong Id;
public uint Label;
public string Name;
public RecordType RecordType;
public string Signature; // VBLK
public uint Valid;
public static DatabaseRecord ReadFrom(byte[] buffer, int offset)
{
DatabaseRecord result = null;
if (EndianUtilities.ToInt32BigEndian(buffer, offset + 0xC) != 0)
{
switch ((RecordType)(buffer[offset + 0x13] & 0xF))
{
case RecordType.Volume:
result = new VolumeRecord();
break;
case RecordType.Component:
result = new ComponentRecord();
break;
case RecordType.Extent:
result = new ExtentRecord();
break;
case RecordType.Disk:
result = new DiskRecord();
break;
case RecordType.DiskGroup:
result = new DiskGroupRecord();
break;
default:
throw new NotImplementedException("Unrecognized record type: " + buffer[offset + 0x13]);
}
result.DoReadFrom(buffer, offset);
}
return result;
}
protected static ulong ReadVarULong(byte[] buffer, ref int offset)
{
int length = buffer[offset];
ulong result = 0;
for (int i = 0; i < length; ++i)
{
result = (result << 8) | buffer[offset + i + 1];
}
offset += length + 1;
return result;
}
protected static long ReadVarLong(byte[] buffer, ref int offset)
{
return (long)ReadVarULong(buffer, ref offset);
}
protected static string ReadVarString(byte[] buffer, ref int offset)
{
int length = buffer[offset];
string result = EndianUtilities.BytesToString(buffer, offset + 1, length);
offset += length + 1;
return result;
}
protected static byte ReadByte(byte[] buffer, ref int offset)
{
return buffer[offset++];
}
protected static uint ReadUInt(byte[] buffer, ref int offset)
{
offset += 4;
return EndianUtilities.ToUInt32BigEndian(buffer, offset - 4);
}
protected static long ReadLong(byte[] buffer, ref int offset)
{
offset += 8;
return EndianUtilities.ToInt64BigEndian(buffer, offset - 8);
}
protected static ulong ReadULong(byte[] buffer, ref int offset)
{
offset += 8;
return EndianUtilities.ToUInt64BigEndian(buffer, offset - 8);
}
protected static string ReadString(byte[] buffer, int len, ref int offset)
{
offset += len;
return EndianUtilities.BytesToString(buffer, offset - len, len);
}
protected static Guid ReadBinaryGuid(byte[] buffer, ref int offset)
{
offset += 16;
return EndianUtilities.ToGuidBigEndian(buffer, offset - 16);
}
protected virtual void DoReadFrom(byte[] buffer, int offset)
{
Signature = EndianUtilities.BytesToString(buffer, offset + 0x00, 4);
Label = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x04);
Counter = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x08);
Valid = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x0C);
Flags = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x10);
RecordType = (RecordType)(Flags & 0xF);
DataLength = EndianUtilities.ToUInt32BigEndian(buffer, 0x14);
}
}
}

View File

@ -0,0 +1,49 @@
//
// 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.LogicalDiskManager
{
internal sealed class DiskGroupRecord : DatabaseRecord
{
public string GroupGuidString;
public uint Unknown1;
protected override void DoReadFrom(byte[] buffer, int offset)
{
base.DoReadFrom(buffer, offset);
int pos = offset + 0x18;
Id = ReadVarULong(buffer, ref pos);
Name = ReadVarString(buffer, ref pos);
if ((Flags & 0xF0) == 0x40)
{
GroupGuidString = ReadBinaryGuid(buffer, ref pos).ToString();
}
else
{
GroupGuidString = ReadVarString(buffer, ref pos);
}
Unknown1 = ReadUInt(buffer, ref pos);
}
}
}

View File

@ -0,0 +1,47 @@
//
// 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.LogicalDiskManager
{
internal sealed class DiskRecord : DatabaseRecord
{
public string DiskGuidString;
protected override void DoReadFrom(byte[] buffer, int offset)
{
base.DoReadFrom(buffer, offset);
int pos = offset + 0x18;
Id = ReadVarULong(buffer, ref pos);
Name = ReadVarString(buffer, ref pos);
if ((Flags & 0xF0) == 0x40)
{
DiskGuidString = ReadBinaryGuid(buffer, ref pos).ToString();
}
else
{
DiskGuidString = ReadVarString(buffer, ref pos);
}
}
}
}

View File

@ -0,0 +1,144 @@
//
// 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.IO;
using DiscUtils.Partitions;
using DiscUtils.Streams;
namespace DiscUtils.LogicalDiskManager
{
internal class DynamicDisk : IDiagnosticTraceable
{
private readonly VirtualDisk _disk;
private readonly PrivateHeader _header;
internal DynamicDisk(VirtualDisk disk)
{
_disk = disk;
_header = GetPrivateHeader(_disk);
TocBlock toc = GetTableOfContents();
long dbStart = _header.ConfigurationStartLba * 512 + toc.Item1Start * 512;
_disk.Content.Position = dbStart;
Database = new Database(_disk.Content);
}
public SparseStream Content
{
get { return _disk.Content; }
}
public Database Database { get; }
public long DataOffset
{
get { return _header.DataStartLba; }
}
public Guid GroupId
{
get { return string.IsNullOrEmpty(_header.DiskGroupId) ? Guid.Empty : new Guid(_header.DiskGroupId); }
}
public Guid Id
{
get { return new Guid(_header.DiskId); }
}
public void Dump(TextWriter writer, string linePrefix)
{
writer.WriteLine(linePrefix + "DISK (" + _header.DiskId + ")");
writer.WriteLine(linePrefix + " Metadata Version: " + ((_header.Version >> 16) & 0xFFFF) + "." +
(_header.Version & 0xFFFF));
writer.WriteLine(linePrefix + " Timestamp: " + _header.Timestamp);
writer.WriteLine(linePrefix + " Disk Id: " + _header.DiskId);
writer.WriteLine(linePrefix + " Host Id: " + _header.HostId);
writer.WriteLine(linePrefix + " Disk Group Id: " + _header.DiskGroupId);
writer.WriteLine(linePrefix + " Disk Group Name: " + _header.DiskGroupName);
writer.WriteLine(linePrefix + " Data Start: " + _header.DataStartLba + " (Sectors)");
writer.WriteLine(linePrefix + " Data Size: " + _header.DataSizeLba + " (Sectors)");
writer.WriteLine(linePrefix + " Configuration Start: " + _header.ConfigurationStartLba + " (Sectors)");
writer.WriteLine(linePrefix + " Configuration Size: " + _header.ConfigurationSizeLba + " (Sectors)");
writer.WriteLine(linePrefix + " TOC Size: " + _header.TocSizeLba + " (Sectors)");
writer.WriteLine(linePrefix + " Next TOC: " + _header.NextTocLba + " (Sectors)");
writer.WriteLine(linePrefix + " Number of Configs: " + _header.NumberOfConfigs);
writer.WriteLine(linePrefix + " Config Size: " + _header.ConfigurationSizeLba + " (Sectors)");
writer.WriteLine(linePrefix + " Number of Logs: " + _header.NumberOfLogs);
writer.WriteLine(linePrefix + " Log Size: " + _header.LogSizeLba + " (Sectors)");
}
internal static PrivateHeader GetPrivateHeader(VirtualDisk disk)
{
if (disk.IsPartitioned)
{
long headerPos = 0;
PartitionTable pt = disk.Partitions;
if (pt is BiosPartitionTable)
{
headerPos = 0xc00;
}
else
{
foreach (PartitionInfo part in pt.Partitions)
{
if (part.GuidType == GuidPartitionTypes.WindowsLdmMetadata)
{
headerPos = part.LastSector * Sizes.Sector;
}
}
}
if (headerPos != 0)
{
disk.Content.Position = headerPos;
byte[] buffer = new byte[Sizes.Sector];
disk.Content.Read(buffer, 0, buffer.Length);
PrivateHeader hdr = new PrivateHeader();
hdr.ReadFrom(buffer, 0);
return hdr;
}
}
return null;
}
private TocBlock GetTableOfContents()
{
byte[] buffer = new byte[_header.TocSizeLba * 512];
_disk.Content.Position = _header.ConfigurationStartLba * 512 + 1 * _header.TocSizeLba * 512;
_disk.Content.Read(buffer, 0, buffer.Length);
TocBlock tocBlock = new TocBlock();
tocBlock.ReadFrom(buffer, 0);
if (tocBlock.Signature == "TOCBLOCK")
{
return tocBlock;
}
return null;
}
}
}

View File

@ -0,0 +1,322 @@
//
// 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 DiscUtils.Partitions;
using DiscUtils.Streams;
namespace DiscUtils.LogicalDiskManager
{
internal class DynamicDiskGroup : IDiagnosticTraceable
{
private readonly Database _database;
private readonly Dictionary<Guid, DynamicDisk> _disks;
private readonly DiskGroupRecord _record;
internal DynamicDiskGroup(VirtualDisk disk)
{
_disks = new Dictionary<Guid, DynamicDisk>();
DynamicDisk dynDisk = new DynamicDisk(disk);
_database = dynDisk.Database;
_disks.Add(dynDisk.Id, dynDisk);
_record = dynDisk.Database.GetDiskGroup(dynDisk.GroupId);
}
#region IDiagnosticTraceable Members
public void Dump(TextWriter writer, string linePrefix)
{
writer.WriteLine(linePrefix + "DISK GROUP (" + _record.Name + ")");
writer.WriteLine(linePrefix + " Name: " + _record.Name);
writer.WriteLine(linePrefix + " Flags: 0x" +
(_record.Flags & 0xFFF0).ToString("X4", CultureInfo.InvariantCulture));
writer.WriteLine(linePrefix + " Database Id: " + _record.Id);
writer.WriteLine(linePrefix + " Guid: " + _record.GroupGuidString);
writer.WriteLine();
writer.WriteLine(linePrefix + " DISKS");
foreach (DiskRecord disk in _database.Disks)
{
writer.WriteLine(linePrefix + " DISK (" + disk.Name + ")");
writer.WriteLine(linePrefix + " Name: " + disk.Name);
writer.WriteLine(linePrefix + " Flags: 0x" +
(disk.Flags & 0xFFF0).ToString("X4", CultureInfo.InvariantCulture));
writer.WriteLine(linePrefix + " Database Id: " + disk.Id);
writer.WriteLine(linePrefix + " Guid: " + disk.DiskGuidString);
DynamicDisk dynDisk;
if (_disks.TryGetValue(new Guid(disk.DiskGuidString), out dynDisk))
{
writer.WriteLine(linePrefix + " PRIVATE HEADER");
dynDisk.Dump(writer, linePrefix + " ");
}
}
writer.WriteLine(linePrefix + " VOLUMES");
foreach (VolumeRecord vol in _database.Volumes)
{
writer.WriteLine(linePrefix + " VOLUME (" + vol.Name + ")");
writer.WriteLine(linePrefix + " Name: " + vol.Name);
writer.WriteLine(linePrefix + " BIOS Type: " +
vol.BiosType.ToString("X2", CultureInfo.InvariantCulture) + " [" +
BiosPartitionTypes.ToString(vol.BiosType) + "]");
writer.WriteLine(linePrefix + " Flags: 0x" +
(vol.Flags & 0xFFF0).ToString("X4", CultureInfo.InvariantCulture));
writer.WriteLine(linePrefix + " Database Id: " + vol.Id);
writer.WriteLine(linePrefix + " Guid: " + vol.VolumeGuid);
writer.WriteLine(linePrefix + " State: " + vol.ActiveString);
writer.WriteLine(linePrefix + " Drive Hint: " + vol.MountHint);
writer.WriteLine(linePrefix + " Num Components: " + vol.ComponentCount);
writer.WriteLine(linePrefix + " Link Id: " + vol.PartitionComponentLink);
writer.WriteLine(linePrefix + " COMPONENTS");
foreach (ComponentRecord cmpnt in _database.GetVolumeComponents(vol.Id))
{
writer.WriteLine(linePrefix + " COMPONENT (" + cmpnt.Name + ")");
writer.WriteLine(linePrefix + " Name: " + cmpnt.Name);
writer.WriteLine(linePrefix + " Flags: 0x" +
(cmpnt.Flags & 0xFFF0).ToString("X4", CultureInfo.InvariantCulture));
writer.WriteLine(linePrefix + " Database Id: " + cmpnt.Id);
writer.WriteLine(linePrefix + " State: " + cmpnt.StatusString);
writer.WriteLine(linePrefix + " Mode: " + cmpnt.MergeType);
writer.WriteLine(linePrefix + " Num Extents: " + cmpnt.NumExtents);
writer.WriteLine(linePrefix + " Link Id: " + cmpnt.LinkId);
writer.WriteLine(linePrefix + " Stripe Size: " + cmpnt.StripeSizeSectors + " (Sectors)");
writer.WriteLine(linePrefix + " Stripe Stride: " + cmpnt.StripeStride);
writer.WriteLine(linePrefix + " EXTENTS");
foreach (ExtentRecord extent in _database.GetComponentExtents(cmpnt.Id))
{
writer.WriteLine(linePrefix + " EXTENT (" + extent.Name + ")");
writer.WriteLine(linePrefix + " Name: " + extent.Name);
writer.WriteLine(linePrefix + " Flags: 0x" +
(extent.Flags & 0xFFF0).ToString("X4", CultureInfo.InvariantCulture));
writer.WriteLine(linePrefix + " Database Id: " + extent.Id);
writer.WriteLine(linePrefix + " Disk Offset: " + extent.DiskOffsetLba +
" (Sectors)");
writer.WriteLine(linePrefix + " Volume Offset: " + extent.OffsetInVolumeLba +
" (Sectors)");
writer.WriteLine(linePrefix + " Size: " + extent.SizeLba + " (Sectors)");
writer.WriteLine(linePrefix + " Component Id: " + extent.ComponentId);
writer.WriteLine(linePrefix + " Disk Id: " + extent.DiskId);
writer.WriteLine(linePrefix + " Link Id: " + extent.PartitionComponentLink);
writer.WriteLine(linePrefix + " Interleave Order: " + extent.InterleaveOrder);
}
}
}
}
#endregion
public void Add(VirtualDisk disk)
{
DynamicDisk dynDisk = new DynamicDisk(disk);
_disks.Add(dynDisk.Id, dynDisk);
}
internal DynamicVolume[] GetVolumes()
{
List<DynamicVolume> vols = new List<DynamicVolume>();
foreach (VolumeRecord record in _database.GetVolumes())
{
vols.Add(new DynamicVolume(this, record.VolumeGuid));
}
return vols.ToArray();
}
internal VolumeRecord GetVolume(Guid volume)
{
return _database.GetVolume(volume);
}
internal LogicalVolumeStatus GetVolumeStatus(ulong volumeId)
{
return GetVolumeStatus(_database.GetVolume(volumeId));
}
internal SparseStream OpenVolume(ulong volumeId)
{
return OpenVolume(_database.GetVolume(volumeId));
}
private static int CompareExtentOffsets(ExtentRecord x, ExtentRecord y)
{
if (x.OffsetInVolumeLba > y.OffsetInVolumeLba)
{
return 1;
}
if (x.OffsetInVolumeLba < y.OffsetInVolumeLba)
{
return -1;
}
return 0;
}
private static int CompareExtentInterleaveOrder(ExtentRecord x, ExtentRecord y)
{
if (x.InterleaveOrder > y.InterleaveOrder)
{
return 1;
}
if (x.InterleaveOrder < y.InterleaveOrder)
{
return -1;
}
return 0;
}
private static LogicalVolumeStatus WorstOf(LogicalVolumeStatus x, LogicalVolumeStatus y)
{
return (LogicalVolumeStatus)Math.Max((int)x, (int)y);
}
private LogicalVolumeStatus GetVolumeStatus(VolumeRecord volume)
{
int numFailed = 0;
ulong numOK = 0;
LogicalVolumeStatus worst = LogicalVolumeStatus.Healthy;
foreach (ComponentRecord cmpnt in _database.GetVolumeComponents(volume.Id))
{
LogicalVolumeStatus cmpntStatus = GetComponentStatus(cmpnt);
worst = WorstOf(worst, cmpntStatus);
if (cmpntStatus == LogicalVolumeStatus.Failed)
{
numFailed++;
}
else
{
numOK++;
}
}
if (numOK < 1)
{
return LogicalVolumeStatus.Failed;
}
if (numOK == volume.ComponentCount)
{
return worst;
}
return LogicalVolumeStatus.FailedRedundancy;
}
private LogicalVolumeStatus GetComponentStatus(ComponentRecord cmpnt)
{
// NOTE: no support for RAID, so either valid or failed...
LogicalVolumeStatus status = LogicalVolumeStatus.Healthy;
foreach (ExtentRecord extent in _database.GetComponentExtents(cmpnt.Id))
{
DiskRecord disk = _database.GetDisk(extent.DiskId);
if (!_disks.ContainsKey(new Guid(disk.DiskGuidString)))
{
status = LogicalVolumeStatus.Failed;
break;
}
}
return status;
}
private SparseStream OpenExtent(ExtentRecord extent)
{
DiskRecord disk = _database.GetDisk(extent.DiskId);
DynamicDisk diskObj = _disks[new Guid(disk.DiskGuidString)];
return new SubStream(diskObj.Content, Ownership.None,
(diskObj.DataOffset + extent.DiskOffsetLba) * Sizes.Sector, extent.SizeLba * Sizes.Sector);
}
private SparseStream OpenComponent(ComponentRecord component)
{
if (component.MergeType == ExtentMergeType.Concatenated)
{
List<ExtentRecord> extents = new List<ExtentRecord>(_database.GetComponentExtents(component.Id));
extents.Sort(CompareExtentOffsets);
// Sanity Check...
long pos = 0;
foreach (ExtentRecord extent in extents)
{
if (extent.OffsetInVolumeLba != pos)
{
throw new IOException("Volume extents are non-contiguous");
}
pos += extent.SizeLba;
}
List<SparseStream> streams = new List<SparseStream>();
foreach (ExtentRecord extent in extents)
{
streams.Add(OpenExtent(extent));
}
return new ConcatStream(Ownership.Dispose, streams.ToArray());
}
if (component.MergeType == ExtentMergeType.Interleaved)
{
List<ExtentRecord> extents = new List<ExtentRecord>(_database.GetComponentExtents(component.Id));
extents.Sort(CompareExtentInterleaveOrder);
List<SparseStream> streams = new List<SparseStream>();
foreach (ExtentRecord extent in extents)
{
streams.Add(OpenExtent(extent));
}
return new StripedStream(component.StripeSizeSectors * Sizes.Sector, Ownership.Dispose, streams.ToArray());
}
throw new NotImplementedException("Unknown component mode: " + component.MergeType);
}
private SparseStream OpenVolume(VolumeRecord volume)
{
List<SparseStream> cmpntStreams = new List<SparseStream>();
foreach (ComponentRecord component in _database.GetVolumeComponents(volume.Id))
{
if (GetComponentStatus(component) == LogicalVolumeStatus.Healthy)
{
cmpntStreams.Add(OpenComponent(component));
}
}
if (cmpntStreams.Count < 1)
{
throw new IOException("Volume with no associated or healthy components");
}
if (cmpntStreams.Count == 1)
{
return cmpntStreams[0];
}
return new MirrorStream(Ownership.Dispose, cmpntStreams.ToArray());
}
}
}

View File

@ -0,0 +1,153 @@
//
// 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.Collections.Generic;
using System.IO;
using DiscUtils.Partitions;
namespace DiscUtils.LogicalDiskManager
{
/// <summary>
/// A class that understands Windows LDM structures, mapping physical volumes to logical volumes.
/// </summary>
public class DynamicDiskManager : IDiagnosticTraceable
{
private readonly Dictionary<string, DynamicDiskGroup> _groups;
/// <summary>
/// Initializes a new instance of the DynamicDiskManager class.
/// </summary>
/// <param name="disks">The initial set of disks to manage.</param>
public DynamicDiskManager(params VirtualDisk[] disks)
{
_groups = new Dictionary<string, DynamicDiskGroup>();
foreach (VirtualDisk disk in disks)
{
Add(disk);
}
}
/// <summary>
/// Writes a diagnostic report about the state of the disk manager.
/// </summary>
/// <param name="writer">The writer to send the report to.</param>
/// <param name="linePrefix">The prefix to place at the start of each line.</param>
public void Dump(TextWriter writer, string linePrefix)
{
writer.WriteLine(linePrefix + "DISK GROUPS");
foreach (DynamicDiskGroup group in _groups.Values)
{
group.Dump(writer, linePrefix + " ");
}
}
/// <summary>
/// Determines if a physical volume contains LDM data.
/// </summary>
/// <param name="volumeInfo">The volume to inspect.</param>
/// <returns><c>true</c> if the physical volume contains LDM data, else <c>false</c>.</returns>
public static bool HandlesPhysicalVolume(PhysicalVolumeInfo volumeInfo)
{
PartitionInfo pi = volumeInfo.Partition;
if (pi != null)
{
return IsLdmPartition(pi);
}
return false;
}
/// <summary>
/// Determines if a disk is 'dynamic' (i.e. contains LDM volumes).
/// </summary>
/// <param name="disk">The disk to inspect.</param>
/// <returns><c>true</c> if the disk contains LDM volumes, else <c>false</c>.</returns>
public static bool IsDynamicDisk(VirtualDisk disk)
{
if (disk.IsPartitioned)
{
foreach (PartitionInfo partition in disk.Partitions.Partitions)
{
if (IsLdmPartition(partition))
{
return true;
}
}
}
return false;
}
/// <summary>
/// Adds a new disk to be managed.
/// </summary>
/// <param name="disk">The disk to manage.</param>
public void Add(VirtualDisk disk)
{
PrivateHeader header = DynamicDisk.GetPrivateHeader(disk);
DynamicDiskGroup group;
if (_groups.TryGetValue(header.DiskGroupId, out group))
{
group.Add(disk);
}
else
{
group = new DynamicDiskGroup(disk);
_groups.Add(header.DiskGroupId, group);
}
}
/// <summary>
/// Gets the logical volumes held across the set of managed disks.
/// </summary>
/// <returns>An array of logical volumes.</returns>
public LogicalVolumeInfo[] GetLogicalVolumes()
{
List<LogicalVolumeInfo> result = new List<LogicalVolumeInfo>();
foreach (DynamicDiskGroup group in _groups.Values)
{
foreach (DynamicVolume volume in group.GetVolumes())
{
LogicalVolumeInfo lvi = new LogicalVolumeInfo(
volume.Identity,
null,
volume.Open,
volume.Length,
volume.BiosType,
volume.Status);
result.Add(lvi);
}
}
return result.ToArray();
}
private static bool IsLdmPartition(PartitionInfo partition)
{
return partition.BiosType == BiosPartitionTypes.WindowsDynamicVolume
|| partition.GuidType == GuidPartitionTypes.WindowsLdmMetadata
|| partition.GuidType == GuidPartitionTypes.WindowsLdmData;
}
}
}

View File

@ -0,0 +1,54 @@
//
// 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.Collections.Generic;
using DiscUtils.Internal;
namespace DiscUtils.LogicalDiskManager
{
[LogicalVolumeFactory]
internal class DynamicDiskManagerFactory : LogicalVolumeFactory
{
public override bool HandlesPhysicalVolume(PhysicalVolumeInfo volume)
{
return DynamicDiskManager.HandlesPhysicalVolume(volume);
}
public override void MapDisks(IEnumerable<VirtualDisk> disks, Dictionary<string, LogicalVolumeInfo> result)
{
DynamicDiskManager mgr = new DynamicDiskManager();
foreach (VirtualDisk disk in disks)
{
if (DynamicDiskManager.IsDynamicDisk(disk))
{
mgr.Add(disk);
}
}
foreach (LogicalVolumeInfo vol in mgr.GetLogicalVolumes())
{
result.Add(vol.Identity, vol);
}
}
}
}

View File

@ -0,0 +1,70 @@
//
// 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.IO;
using DiscUtils.Streams;
namespace DiscUtils.LogicalDiskManager
{
internal class DynamicVolume
{
private readonly DynamicDiskGroup _group;
internal DynamicVolume(DynamicDiskGroup group, Guid volumeId)
{
_group = group;
Identity = volumeId;
}
public byte BiosType
{
get { return Record.BiosType; }
}
public Guid Identity { get; }
public long Length
{
get { return Record.Size * Sizes.Sector; }
}
private VolumeRecord Record
{
get { return _group.GetVolume(Identity); }
}
public LogicalVolumeStatus Status
{
get { return _group.GetVolumeStatus(Record.Id); }
}
public SparseStream Open()
{
if (Status == LogicalVolumeStatus.Failed)
{
throw new IOException("Attempt to open 'failed' volume");
}
return _group.OpenVolume(Record.Id);
}
}
}

View File

@ -0,0 +1,31 @@
//
// 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.LogicalDiskManager
{
internal enum ExtentMergeType : byte
{
None = 0,
Interleaved = 1,
Concatenated = 2
}
}

View File

@ -0,0 +1,60 @@
//
// 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.LogicalDiskManager
{
internal sealed class ExtentRecord : DatabaseRecord
{
public ulong ComponentId;
public ulong DiskId;
public long DiskOffsetLba;
public ulong InterleaveOrder;
public long OffsetInVolumeLba;
public uint PartitionComponentLink;
public long SizeLba;
public uint Unknown1;
public uint Unknown2;
protected override void DoReadFrom(byte[] buffer, int offset)
{
base.DoReadFrom(buffer, offset);
int pos = offset + 0x18;
Id = ReadVarULong(buffer, ref pos);
Name = ReadVarString(buffer, ref pos);
Unknown1 = ReadUInt(buffer, ref pos);
Unknown2 = ReadUInt(buffer, ref pos);
PartitionComponentLink = ReadUInt(buffer, ref pos);
DiskOffsetLba = ReadLong(buffer, ref pos);
OffsetInVolumeLba = ReadLong(buffer, ref pos);
SizeLba = ReadVarLong(buffer, ref pos);
ComponentId = ReadVarULong(buffer, ref pos);
DiskId = ReadVarULong(buffer, ref pos);
if ((Flags & 0x0800) != 0)
{
InterleaveOrder = ReadVarULong(buffer, ref pos);
}
}
}
}

View File

@ -0,0 +1,90 @@
//
// 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 DiscUtils.Streams;
namespace DiscUtils.LogicalDiskManager
{
internal class PrivateHeader
{
public uint Checksum; // 00 00 2f 96
public long ConfigSizeLba;
public long ConfigurationSizeLba; // 08 00
public long ConfigurationStartLba; // 03 FF F8 00
public long DataSizeLba; // 03 FF F7 C1
public long DataStartLba; // 3F
public string DiskGroupId; // GUID string
public string DiskGroupName; // MAX_COMPUTER_NAME_LENGTH?
public string DiskId; // GUID string
public string HostId; // GUID string
public long LogSizeLba;
public long NextTocLba;
public long NumberOfConfigs;
public long NumberOfLogs;
public string Signature; // PRIVHEAD
public DateTime Timestamp;
public long TocSizeLba;
public long Unknown2; // Active TOC? 00 .. 00 01
public long Unknown3; // 00 .. 07 ff // 1 sector less than 2MB
public long Unknown4; // 00 .. 07 40
public uint Unknown5; // Sector Size?
public uint Version; // 2.12
public void ReadFrom(byte[] buffer, int offset)
{
Signature = EndianUtilities.BytesToString(buffer, offset + 0x00, 8);
Checksum = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x08);
Version = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x0C);
Timestamp = DateTime.FromFileTimeUtc(EndianUtilities.ToInt64BigEndian(buffer, offset + 0x10));
Unknown2 = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x18);
Unknown3 = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x20);
Unknown4 = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x28);
DiskId = EndianUtilities.BytesToString(buffer, offset + 0x30, 0x40).Trim('\0');
HostId = EndianUtilities.BytesToString(buffer, offset + 0x70, 0x40).Trim('\0');
DiskGroupId = EndianUtilities.BytesToString(buffer, offset + 0xB0, 0x40).Trim('\0');
DiskGroupName = EndianUtilities.BytesToString(buffer, offset + 0xF0, 31).Trim('\0');
Unknown5 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x10F);
DataStartLba = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x11B);
DataSizeLba = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x123);
ConfigurationStartLba = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x12B);
ConfigurationSizeLba = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x133);
TocSizeLba = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x13B);
NextTocLba = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x143);
// These two may be reversed
NumberOfConfigs = EndianUtilities.ToInt32BigEndian(buffer, offset + 0x14B);
NumberOfLogs = EndianUtilities.ToInt32BigEndian(buffer, offset + 0x14F);
ConfigSizeLba = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x153);
LogSizeLba = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x15B);
}
////}
//// throw new NotImplementedException();
//// // Add all byte values for 512 bytes
//// // Zero checksum bytes (0x08, 4)
////{
////private static int CalcChecksum()
}
}

View File

@ -0,0 +1,34 @@
//
// 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.LogicalDiskManager
{
internal enum RecordType : byte
{
None = 0,
Volume = 1,
Component = 2,
Extent = 3,
Disk = 4,
DiskGroup = 5
}
}

View File

@ -0,0 +1,72 @@
//
// 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 DiscUtils.Streams;
namespace DiscUtils.LogicalDiskManager
{
internal class TocBlock
{
public uint Checksum; // 00 00 08 B6
public long Item1Size; // Unit?
public long Item1Start; // Sector Offset from ConfigurationStart
public string Item1Str; // 'config', length 10
public long Item2Size; // Unit?
public long Item2Start; // Sector Offset from ConfigurationStart
public string Item2Str; // 'log', length 10
public long SequenceNumber; // 00 .. 01
public string Signature; // TOCBLOCK
public long Unknown1; // 0
public long Unknown2; // 00
public uint Unknown3; // 00 06 00 01 (may be two values?)
public uint Unknown4; // 00 00 00 00
public uint Unknown5; // 00 06 00 01 (may be two values?)
public uint Unknown6; // 00 00 00 00
public void ReadFrom(byte[] buffer, int offset)
{
Signature = EndianUtilities.BytesToString(buffer, offset + 0x00, 8);
Checksum = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x08);
SequenceNumber = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x0C);
Unknown1 = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x14);
Unknown2 = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x1C);
Item1Str = EndianUtilities.BytesToString(buffer, offset + 0x24, 10).Trim('\0');
Item1Start = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x2E);
Item1Size = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x36);
Unknown3 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x3E);
Unknown4 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x42);
Item2Str = EndianUtilities.BytesToString(buffer, offset + 0x46, 10).Trim('\0');
Item2Start = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x50);
Item2Size = EndianUtilities.ToInt64BigEndian(buffer, offset + 0x58);
Unknown5 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x60);
Unknown6 = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0x64);
}
////}
//// throw new NotImplementedException();
//// // Add all byte values for ?? bytes
//// // Zero checksum bytes (0x08, 4)
////{
////private static int CalcChecksum()
}
}

View File

@ -0,0 +1,78 @@
//
// 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 DiscUtils.Streams;
namespace DiscUtils.LogicalDiskManager
{
internal sealed class VolumeRecord : DatabaseRecord
{
public string ActiveString;
public byte BiosType;
public ulong ComponentCount;
public ulong DupCount; // ??Seen once after adding 'foreign disk', from broken mirror (identical links(P/V/C))
public string GenString;
public string MountHint;
public string NumberString; // 8000000000000000 sometimes...
public uint PartitionComponentLink;
public long Size;
public ulong Unknown1; // Zero
public uint Unknown2; // Zero
public ulong UnknownA; // Zero
public ulong UnknownB; // 00 .. 03
public uint UnknownC; // 00 00 00 11
public uint UnknownD; // Zero
public Guid VolumeGuid;
protected override void DoReadFrom(byte[] buffer, int offset)
{
base.DoReadFrom(buffer, offset);
int pos = offset + 0x18;
Id = ReadVarULong(buffer, ref pos);
Name = ReadVarString(buffer, ref pos);
GenString = ReadVarString(buffer, ref pos);
NumberString = ReadVarString(buffer, ref pos);
ActiveString = ReadString(buffer, 6, ref pos);
UnknownA = ReadVarULong(buffer, ref pos);
UnknownB = ReadULong(buffer, ref pos);
DupCount = ReadVarULong(buffer, ref pos);
UnknownC = ReadUInt(buffer, ref pos);
ComponentCount = ReadVarULong(buffer, ref pos);
UnknownD = ReadUInt(buffer, ref pos);
PartitionComponentLink = ReadUInt(buffer, ref pos);
Unknown1 = ReadULong(buffer, ref pos);
Size = ReadVarLong(buffer, ref pos);
Unknown2 = ReadUInt(buffer, ref pos);
BiosType = ReadByte(buffer, ref pos);
VolumeGuid = EndianUtilities.ToGuidBigEndian(buffer, pos);
pos += 16;
if ((Flags & 0x0200) != 0)
{
MountHint = ReadVarString(buffer, ref pos);
}
}
}
}

View File

@ -0,0 +1,120 @@
//
// 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 DiscUtils.Streams;
namespace DiscUtils
{
/// <summary>
/// Information about a logical disk volume, which may be backed by one or more physical volumes.
/// </summary>
public sealed class LogicalVolumeInfo : VolumeInfo
{
private Guid _guid;
private readonly SparseStreamOpenDelegate _opener;
private readonly PhysicalVolumeInfo _physicalVol;
internal LogicalVolumeInfo(Guid guid, PhysicalVolumeInfo physicalVolume, SparseStreamOpenDelegate opener,
long length, byte biosType, LogicalVolumeStatus status)
{
_guid = guid;
_physicalVol = physicalVolume;
_opener = opener;
Length = length;
BiosType = biosType;
Status = status;
}
/// <summary>
/// Gets the disk geometry of the underlying storage medium (as used in BIOS calls), may be null.
/// </summary>
public override Geometry BiosGeometry
{
get { return _physicalVol == null ? Geometry.Null : _physicalVol.BiosGeometry; }
}
/// <summary>
/// Gets the one-byte BIOS type for this volume, which indicates the content.
/// </summary>
public override byte BiosType { get; }
/// <summary>
/// The stable identity for this logical volume.
/// </summary>
/// <remarks>The stability of the identity depends the disk structure.
/// In some cases the identity may include a simple index, when no other information
/// is available. Best practice is to add disks to the Volume Manager in a stable
/// order, if the stability of this identity is paramount.</remarks>
public override string Identity
{
get
{
if (_guid != Guid.Empty)
{
return "VLG" + _guid.ToString("B");
}
return "VLP:" + _physicalVol.Identity;
}
}
/// <summary>
/// Gets the length of the volume (in bytes).
/// </summary>
public override long Length { get; }
/// <summary>
/// Gets the disk geometry of the underlying storage medium, if any (may be Geometry.Null).
/// </summary>
public override Geometry PhysicalGeometry
{
get { return _physicalVol == null ? Geometry.Null : _physicalVol.PhysicalGeometry; }
}
/// <summary>
/// Gets the offset of this volume in the underlying storage medium, if any (may be Zero).
/// </summary>
public override long PhysicalStartSector
{
get { return _physicalVol == null ? 0 : _physicalVol.PhysicalStartSector; }
}
/// <summary>
/// Gets the status of the logical volume, indicating volume health.
/// </summary>
public LogicalVolumeStatus Status { get; }
/// <summary>
/// Gets the underlying physical volume info
/// </summary>
public PhysicalVolumeInfo PhysicalVolume { get { return _physicalVol; } }
/// <summary>
/// Opens a stream with access to the content of the logical volume.
/// </summary>
/// <returns>The volume's content as a stream.</returns>
public override SparseStream Open()
{
return _opener();
}
}
}

View File

@ -0,0 +1,23 @@
namespace DiscUtils
{
/// <summary>
/// Enumeration of the health status of a logical volume.
/// </summary>
public enum LogicalVolumeStatus
{
/// <summary>
/// The volume is healthy and fully functional.
/// </summary>
Healthy = 0,
/// <summary>
/// The volume is completely accessible, but at degraded redundancy.
/// </summary>
FailedRedundancy = 1,
/// <summary>
/// The volume is wholey, or partly, inaccessible.
/// </summary>
Failed = 2
}
}

View File

@ -0,0 +1,804 @@
//
// DiscUtils Copyright (c) 2008-2011, Kenneth Bell
//
// Original NativeFileSystem contributed by bsobel:
// http://discutils.codeplex.com/workitem/5190
//
// 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.IO;
using DiscUtils.Internal;
using DiscUtils.Streams;
namespace DiscUtils
{
/// <summary>
/// Provides an implementation for OS-mounted file systems.
/// </summary>
public class NativeFileSystem : DiscFileSystem
{
private readonly bool _readOnly;
/// <summary>
/// Initializes a new instance of the NativeFileSystem class.
/// </summary>
/// <param name="basePath">The 'root' directory of the new instance.</param>
/// <param name="readOnly">Only permit 'read' activities.</param>
public NativeFileSystem(string basePath, bool readOnly)
{
BasePath = basePath;
if (!BasePath.EndsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
BasePath += @"\";
}
_readOnly = readOnly;
}
/// <summary>
/// Gets the base path used to create the file system.
/// </summary>
public string BasePath { get; }
/// <summary>
/// Indicates whether the file system is read-only or read-write.
/// </summary>
/// <returns>true if the file system is read-write.</returns>
public override bool CanWrite
{
get { return !_readOnly; }
}
/// <summary>
/// Provides a friendly description of the file system type.
/// </summary>
public override string FriendlyName
{
get { return "Native"; }
}
/// <summary>
/// Gets a value indicating whether the file system is thread-safe.
/// </summary>
/// <remarks>The Native File System is thread safe.</remarks>
public override bool IsThreadSafe
{
get { return true; }
}
/// <summary>
/// Gets the root directory of the file system.
/// </summary>
public override DiscDirectoryInfo Root
{
get { return new DiscDirectoryInfo(this, string.Empty); }
}
/// <summary>
/// Gets the volume label.
/// </summary>
public override string VolumeLabel
{
get { return string.Empty; }
}
/// <summary>
/// Copies an existing file to a new file.
/// </summary>
/// <param name="sourceFile">The source file.</param>
/// <param name="destinationFile">The destination file.</param>
public override void CopyFile(string sourceFile, string destinationFile)
{
CopyFile(sourceFile, destinationFile, true);
}
/// <summary>
/// Copies an existing file to a new file, allowing overwriting of an existing file.
/// </summary>
/// <param name="sourceFile">The source file.</param>
/// <param name="destinationFile">The destination file.</param>
/// <param name="overwrite">Whether to permit over-writing of an existing file.</param>
public override void CopyFile(string sourceFile, string destinationFile, bool overwrite)
{
if (_readOnly)
{
throw new UnauthorizedAccessException();
}
if (sourceFile.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
sourceFile = sourceFile.Substring(1);
}
if (destinationFile.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
destinationFile = destinationFile.Substring(1);
}
File.Copy(Path.Combine(BasePath, sourceFile), Path.Combine(BasePath, destinationFile), true);
}
/// <summary>
/// Creates a directory.
/// </summary>
/// <param name="path">The path of the new directory.</param>
public override void CreateDirectory(string path)
{
if (_readOnly)
{
throw new UnauthorizedAccessException();
}
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
Directory.CreateDirectory(Path.Combine(BasePath, path));
}
/// <summary>
/// Deletes a directory.
/// </summary>
/// <param name="path">The path of the directory to delete.</param>
public override void DeleteDirectory(string path)
{
if (_readOnly)
{
throw new UnauthorizedAccessException();
}
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
Directory.Delete(Path.Combine(BasePath, path));
}
/// <summary>
/// Deletes a directory, optionally with all descendants.
/// </summary>
/// <param name="path">The path of the directory to delete.</param>
/// <param name="recursive">Determines if the all descendants should be deleted.</param>
public override void DeleteDirectory(string path, bool recursive)
{
if (recursive)
{
foreach (string dir in GetDirectories(path))
{
DeleteDirectory(dir, true);
}
foreach (string file in GetFiles(path))
{
DeleteFile(file);
}
}
DeleteDirectory(path);
}
/// <summary>
/// Deletes a file.
/// </summary>
/// <param name="path">The path of the file to delete.</param>
public override void DeleteFile(string path)
{
if (_readOnly)
{
throw new UnauthorizedAccessException();
}
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
File.Delete(Path.Combine(BasePath, path));
}
/// <summary>
/// Indicates if a directory exists.
/// </summary>
/// <param name="path">The path to test.</param>
/// <returns>true if the directory exists.</returns>
public override bool DirectoryExists(string path)
{
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
return Directory.Exists(Path.Combine(BasePath, path));
}
/// <summary>
/// Indicates if a file exists.
/// </summary>
/// <param name="path">The path to test.</param>
/// <returns>true if the file exists.</returns>
public override bool FileExists(string path)
{
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
return File.Exists(Path.Combine(BasePath, path));
}
/// <summary>
/// Indicates if a file or directory exists.
/// </summary>
/// <param name="path">The path to test.</param>
/// <returns>true if the file or directory exists.</returns>
public override bool Exists(string path)
{
return FileExists(path) || DirectoryExists(path);
}
/// <summary>
/// Gets the names of subdirectories in a specified directory.
/// </summary>
/// <param name="path">The path to search.</param>
/// <returns>Array of directories.</returns>
public override string[] GetDirectories(string path)
{
return GetDirectories(path, "*.*", SearchOption.TopDirectoryOnly);
}
/// <summary>
/// Gets the names of subdirectories in a specified directory matching a specified
/// search pattern.
/// </summary>
/// <param name="path">The path to search.</param>
/// <param name="searchPattern">The search string to match against.</param>
/// <returns>Array of directories matching the search pattern.</returns>
public override string[] GetDirectories(string path, string searchPattern)
{
return GetDirectories(path, searchPattern, SearchOption.TopDirectoryOnly);
}
/// <summary>
/// Gets the names of subdirectories in a specified directory matching a specified
/// search pattern, using a value to determine whether to search subdirectories.
/// </summary>
/// <param name="path">The path to search.</param>
/// <param name="searchPattern">The search string to match against.</param>
/// <param name="searchOption">Indicates whether to search subdirectories.</param>
/// <returns>Array of directories matching the search pattern.</returns>
public override string[] GetDirectories(string path, string searchPattern, SearchOption searchOption)
{
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
try
{
return CleanItems(Directory.GetDirectories(Path.Combine(BasePath, path), searchPattern, searchOption));
}
catch (IOException)
{
return new string[0];
}
catch (UnauthorizedAccessException)
{
return new string[0];
}
}
/// <summary>
/// Gets the names of files in a specified directory.
/// </summary>
/// <param name="path">The path to search.</param>
/// <returns>Array of files.</returns>
public override string[] GetFiles(string path)
{
return GetFiles(path, "*.*", SearchOption.TopDirectoryOnly);
}
/// <summary>
/// Gets the names of files in a specified directory.
/// </summary>
/// <param name="path">The path to search.</param>
/// <param name="searchPattern">The search string to match against.</param>
/// <returns>Array of files matching the search pattern.</returns>
public override string[] GetFiles(string path, string searchPattern)
{
return GetFiles(path, searchPattern, SearchOption.TopDirectoryOnly);
}
/// <summary>
/// Gets the names of files in a specified directory matching a specified
/// search pattern, using a value to determine whether to search subdirectories.
/// </summary>
/// <param name="path">The path to search.</param>
/// <param name="searchPattern">The search string to match against.</param>
/// <param name="searchOption">Indicates whether to search subdirectories.</param>
/// <returns>Array of files matching the search pattern.</returns>
public override string[] GetFiles(string path, string searchPattern, SearchOption searchOption)
{
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
try
{
return CleanItems(Directory.GetFiles(Path.Combine(BasePath, path), searchPattern, searchOption));
}
catch (IOException)
{
return new string[0];
}
catch (UnauthorizedAccessException)
{
return new string[0];
}
}
/// <summary>
/// Gets the names of all files and subdirectories in a specified directory.
/// </summary>
/// <param name="path">The path to search.</param>
/// <returns>Array of files and subdirectories matching the search pattern.</returns>
public override string[] GetFileSystemEntries(string path)
{
return GetFileSystemEntries(path, "*.*");
}
/// <summary>
/// Gets the names of files and subdirectories in a specified directory matching a specified
/// search pattern.
/// </summary>
/// <param name="path">The path to search.</param>
/// <param name="searchPattern">The search string to match against.</param>
/// <returns>Array of files and subdirectories matching the search pattern.</returns>
public override string[] GetFileSystemEntries(string path, string searchPattern)
{
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
try
{
return CleanItems(Directory.GetFileSystemEntries(Path.Combine(BasePath, path), searchPattern));
}
catch (IOException)
{
return new string[0];
}
catch (UnauthorizedAccessException)
{
return new string[0];
}
}
/// <summary>
/// Moves a directory.
/// </summary>
/// <param name="sourceDirectoryName">The directory to move.</param>
/// <param name="destinationDirectoryName">The target directory name.</param>
public override void MoveDirectory(string sourceDirectoryName, string destinationDirectoryName)
{
if (_readOnly)
{
throw new UnauthorizedAccessException();
}
if (sourceDirectoryName.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
sourceDirectoryName = sourceDirectoryName.Substring(1);
}
if (destinationDirectoryName.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
destinationDirectoryName = destinationDirectoryName.Substring(1);
}
Directory.Move(Path.Combine(BasePath, sourceDirectoryName),
Path.Combine(BasePath, destinationDirectoryName));
}
/// <summary>
/// Moves a file.
/// </summary>
/// <param name="sourceName">The file to move.</param>
/// <param name="destinationName">The target file name.</param>
public override void MoveFile(string sourceName, string destinationName)
{
MoveFile(sourceName, destinationName, false);
}
/// <summary>
/// Moves a file, allowing an existing file to be overwritten.
/// </summary>
/// <param name="sourceName">The file to move.</param>
/// <param name="destinationName">The target file name.</param>
/// <param name="overwrite">Whether to permit a destination file to be overwritten.</param>
public override void MoveFile(string sourceName, string destinationName, bool overwrite)
{
if (_readOnly)
{
throw new UnauthorizedAccessException();
}
if (destinationName.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
destinationName = destinationName.Substring(1);
}
if (FileExists(Path.Combine(BasePath, destinationName)))
{
if (overwrite)
{
DeleteFile(Path.Combine(BasePath, destinationName));
}
else
{
throw new IOException("File already exists");
}
}
if (sourceName.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
sourceName = sourceName.Substring(1);
}
File.Move(Path.Combine(BasePath, sourceName), Path.Combine(BasePath, destinationName));
}
/// <summary>
/// Opens the specified file.
/// </summary>
/// <param name="path">The full path of the file to open.</param>
/// <param name="mode">The file mode for the created stream.</param>
/// <returns>The new stream.</returns>
public override SparseStream OpenFile(string path, FileMode mode)
{
return OpenFile(path, mode, FileAccess.ReadWrite);
}
/// <summary>
/// Opens the specified file.
/// </summary>
/// <param name="path">The full path of the file to open.</param>
/// <param name="mode">The file mode for the created stream.</param>
/// <param name="access">The access permissions for the created stream.</param>
/// <returns>The new stream.</returns>
public override SparseStream OpenFile(string path, FileMode mode, FileAccess access)
{
if (_readOnly && access != FileAccess.Read)
{
throw new UnauthorizedAccessException();
}
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
FileShare fileShare = FileShare.None;
if (access == FileAccess.Read)
{
fileShare = FileShare.Read;
}
var locator = new LocalFileLocator(BasePath);
return SparseStream.FromStream(locator.Open(path, mode, access, fileShare),
Ownership.Dispose);
}
/// <summary>
/// Gets the attributes of a file or directory.
/// </summary>
/// <param name="path">The file or directory to inspect.</param>
/// <returns>The attributes of the file or directory.</returns>
public override FileAttributes GetAttributes(string path)
{
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
return File.GetAttributes(Path.Combine(BasePath, path));
}
/// <summary>
/// Sets the attributes of a file or directory.
/// </summary>
/// <param name="path">The file or directory to change.</param>
/// <param name="newValue">The new attributes of the file or directory.</param>
public override void SetAttributes(string path, FileAttributes newValue)
{
if (_readOnly)
{
throw new UnauthorizedAccessException();
}
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
File.SetAttributes(Path.Combine(BasePath, path), newValue);
}
/// <summary>
/// Gets the creation time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The creation time.</returns>
public override DateTime GetCreationTime(string path)
{
return GetCreationTimeUtc(path).ToLocalTime();
}
/// <summary>
/// Sets the creation time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
public override void SetCreationTime(string path, DateTime newTime)
{
SetCreationTimeUtc(path, newTime.ToUniversalTime());
}
/// <summary>
/// Gets the creation time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The creation time.</returns>
public override DateTime GetCreationTimeUtc(string path)
{
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
return File.GetCreationTimeUtc(Path.Combine(BasePath, path));
}
/// <summary>
/// Sets the creation time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
public override void SetCreationTimeUtc(string path, DateTime newTime)
{
if (_readOnly)
{
throw new UnauthorizedAccessException();
}
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
File.SetCreationTimeUtc(Path.Combine(BasePath, path), newTime);
}
/// <summary>
/// Gets the last access time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The last access time.</returns>
public override DateTime GetLastAccessTime(string path)
{
return GetLastAccessTimeUtc(path).ToLocalTime();
}
/// <summary>
/// Sets the last access time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
public override void SetLastAccessTime(string path, DateTime newTime)
{
SetLastAccessTimeUtc(path, newTime.ToUniversalTime());
}
/// <summary>
/// Gets the last access time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The last access time.</returns>
public override DateTime GetLastAccessTimeUtc(string path)
{
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
return File.GetLastAccessTimeUtc(Path.Combine(BasePath, path));
}
/// <summary>
/// Sets the last access time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
public override void SetLastAccessTimeUtc(string path, DateTime newTime)
{
if (_readOnly)
{
throw new UnauthorizedAccessException();
}
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
File.SetLastAccessTimeUtc(Path.Combine(BasePath, path), newTime);
}
/// <summary>
/// Gets the last modification time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The last write time.</returns>
public override DateTime GetLastWriteTime(string path)
{
return GetLastWriteTimeUtc(path).ToLocalTime();
}
/// <summary>
/// Sets the last modification time (in local time) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
public override void SetLastWriteTime(string path, DateTime newTime)
{
SetLastWriteTimeUtc(path, newTime.ToUniversalTime());
}
/// <summary>
/// Gets the last modification time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <returns>The last write time.</returns>
public override DateTime GetLastWriteTimeUtc(string path)
{
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
return File.GetLastWriteTimeUtc(Path.Combine(BasePath, path));
}
/// <summary>
/// Sets the last modification time (in UTC) of a file or directory.
/// </summary>
/// <param name="path">The path of the file or directory.</param>
/// <param name="newTime">The new time to set.</param>
public override void SetLastWriteTimeUtc(string path, DateTime newTime)
{
if (_readOnly)
{
throw new UnauthorizedAccessException();
}
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
File.SetLastWriteTimeUtc(Path.Combine(BasePath, path), newTime);
}
/// <summary>
/// Gets the length of a file.
/// </summary>
/// <param name="path">The path to the file.</param>
/// <returns>The length in bytes.</returns>
public override long GetFileLength(string path)
{
if (path.StartsWith(@"\", StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(1);
}
return new FileInfo(Path.Combine(BasePath, path)).Length;
}
/// <summary>
/// Gets an object representing a possible file.
/// </summary>
/// <param name="path">The file path.</param>
/// <returns>The representing object.</returns>
/// <remarks>The file does not need to exist.</remarks>
public override DiscFileInfo GetFileInfo(string path)
{
return new DiscFileInfo(this, path);
}
/// <summary>
/// Gets an object representing a possible directory.
/// </summary>
/// <param name="path">The directory path.</param>
/// <returns>The representing object.</returns>
/// <remarks>The directory does not need to exist.</remarks>
public override DiscDirectoryInfo GetDirectoryInfo(string path)
{
return new DiscDirectoryInfo(this, path);
}
/// <summary>
/// Gets an object representing a possible file system object (file or directory).
/// </summary>
/// <param name="path">The file system path.</param>
/// <returns>The representing object.</returns>
/// <remarks>The file system object does not need to exist.</remarks>
public override DiscFileSystemInfo GetFileSystemInfo(string path)
{
return new DiscFileSystemInfo(this, path);
}
/// <summary>
/// Size of the Filesystem in bytes
/// </summary>
public override long Size
{
get
{
DriveInfo info = new DriveInfo(BasePath);
return info.TotalSize;
}
}
/// <summary>
/// Used space of the Filesystem in bytes
/// </summary>
public override long UsedSpace
{
get { return Size - AvailableSpace; }
}
/// <summary>
/// Available space of the Filesystem in bytes
/// </summary>
public override long AvailableSpace
{
get
{
DriveInfo info = new DriveInfo(BasePath);
return info.AvailableFreeSpace;
}
}
private string[] CleanItems(string[] dirtyItems)
{
string[] cleanList = new string[dirtyItems.Length];
for (int x = 0; x < dirtyItems.Length; x++)
{
cleanList[x] = dirtyItems[x].Substring(BasePath.Length - 1);
}
return cleanList;
}
}
}

View File

@ -0,0 +1,119 @@
//
// 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.Collections.Generic;
using System.IO;
using DiscUtils.Streams;
namespace DiscUtils.Partitions
{
internal class BiosExtendedPartitionTable
{
private readonly Stream _disk;
private readonly uint _firstSector;
public BiosExtendedPartitionTable(Stream disk, uint firstSector)
{
_disk = disk;
_firstSector = firstSector;
}
public BiosPartitionRecord[] GetPartitions()
{
List<BiosPartitionRecord> result = new List<BiosPartitionRecord>();
uint partPos = _firstSector;
while (partPos != 0)
{
_disk.Position = (long)partPos * Sizes.Sector;
byte[] sector = StreamUtilities.ReadExact(_disk, Sizes.Sector);
if (sector[510] != 0x55 || sector[511] != 0xAA)
{
throw new IOException("Invalid extended partition sector");
}
uint nextPartPos = 0;
for (int offset = 0x1BE; offset <= 0x1EE; offset += 0x10)
{
BiosPartitionRecord thisPart = new BiosPartitionRecord(sector, offset, partPos, -1);
if (thisPart.StartCylinder != 0 || thisPart.StartHead != 0 || thisPart.StartSector != 0 ||
(thisPart.LBAStart != 0 && thisPart.LBALength != 0))
{
if (thisPart.PartitionType != 0x05 && thisPart.PartitionType != 0x0F)
{
result.Add(thisPart);
}
else
{
nextPartPos = _firstSector + thisPart.LBAStart;
}
}
}
partPos = nextPartPos;
}
return result.ToArray();
}
/// <summary>
/// Gets all of the disk ranges containing partition table data.
/// </summary>
/// <returns>Set of stream extents, indicated as byte offset from the start of the disk.</returns>
public IEnumerable<StreamExtent> GetMetadataDiskExtents()
{
List<StreamExtent> extents = new List<StreamExtent>();
uint partPos = _firstSector;
while (partPos != 0)
{
extents.Add(new StreamExtent((long)partPos * Sizes.Sector, Sizes.Sector));
_disk.Position = (long)partPos * Sizes.Sector;
byte[] sector = StreamUtilities.ReadExact(_disk, Sizes.Sector);
if (sector[510] != 0x55 || sector[511] != 0xAA)
{
throw new IOException("Invalid extended partition sector");
}
uint nextPartPos = 0;
for (int offset = 0x1BE; offset <= 0x1EE; offset += 0x10)
{
BiosPartitionRecord thisPart = new BiosPartitionRecord(sector, offset, partPos, -1);
if (thisPart.StartCylinder != 0 || thisPart.StartHead != 0 || thisPart.StartSector != 0)
{
if (thisPart.PartitionType == 0x05 || thisPart.PartitionType == 0x0F)
{
nextPartPos = _firstSector + thisPart.LBAStart;
}
}
}
partPos = nextPartPos;
}
return extents;
}
}
}

View File

@ -0,0 +1,136 @@
//
// 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 DiscUtils.Streams;
namespace DiscUtils.Partitions
{
/// <summary>
/// Provides access to partition records in a BIOS (MBR) partition table.
/// </summary>
public sealed class BiosPartitionInfo : PartitionInfo
{
private readonly BiosPartitionRecord _record;
private readonly BiosPartitionTable _table;
internal BiosPartitionInfo(BiosPartitionTable table, BiosPartitionRecord record)
{
_table = table;
_record = record;
}
/// <summary>
/// Gets the type of the partition.
/// </summary>
public override byte BiosType
{
get { return _record.PartitionType; }
}
/// <summary>
/// Gets the end (inclusive) of the partition as a CHS address.
/// </summary>
public ChsAddress End
{
get { return new ChsAddress(_record.EndCylinder, _record.EndHead, _record.EndSector); }
}
/// <summary>
/// Gets the first sector of the partion (relative to start of disk) as a Logical Block Address.
/// </summary>
public override long FirstSector
{
get { return _record.LBAStartAbsolute; }
}
/// <summary>
/// Always returns <see cref="System.Guid"/>.Empty.
/// </summary>
public override Guid GuidType
{
get { return Guid.Empty; }
}
/// <summary>
/// Gets a value indicating whether this partition is active (bootable).
/// </summary>
public bool IsActive
{
get { return _record.Status != 0; }
}
/// <summary>
/// Gets a value indicating whether the partition is a primary (rather than extended) partition.
/// </summary>
public bool IsPrimary
{
get { return PrimaryIndex >= 0; }
}
/// <summary>
/// Gets the last sector of the partion (relative to start of disk) as a Logical Block Address (inclusive).
/// </summary>
public override long LastSector
{
get { return _record.LBAStartAbsolute + _record.LBALength - 1; }
}
/// <summary>
/// Gets the index of the partition in the primary partition table, or <c>-1</c> if not a primary partition.
/// </summary>
public int PrimaryIndex
{
get { return _record.Index; }
}
/// <summary>
/// Gets the start of the partition as a CHS address.
/// </summary>
public ChsAddress Start
{
get { return new ChsAddress(_record.StartCylinder, _record.StartHead, _record.StartSector); }
}
/// <summary>
/// Gets the type of the partition as a string.
/// </summary>
public override string TypeAsString
{
get { return _record.FriendlyPartitionType; }
}
internal override PhysicalVolumeType VolumeType
{
get { return PhysicalVolumeType.BiosPartition; }
}
/// <summary>
/// Opens a stream to access the content of the partition.
/// </summary>
/// <returns>The new stream.</returns>
public override SparseStream Open()
{
return _table.Open(_record);
}
}
}

Some files were not shown because too many files have changed in this diff Show More