Files
Aaru/DiscImageChef.DiscImages/DiscImageChef/DiscImageChef.cs

195 lines
8.9 KiB
C#

// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : DiscImageChef.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages DiscImageChef format disk images.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2019 Natalia Portillo
// ****************************************************************************/
/*
The idea of the format is being able to easily store, retrieve, and access any data that can be read from media.
At the start of a file there's a header that contains a format version, application creator name, and a pointer to
the index.
The index points to one or several DeDuplication Tables, or media tag blocks.
A deduplication table is a table of offsets to blocks and sectors inside blocks. Each entry equals to an LBA and points
to a byte offset in the file shift left to the number of sectors contained in a block, plus the number of sector inside
the block.
Each block must contain sectors of equal size, but that size can be different between blocks.
The deduplication table should be stored decompressed if its size is too big to be stored on-memory. This is chosen at
creation time but it is a good idea to set the limit to 256MiB (this allows for a device of 33 million sectors,
17Gb at 512 bps, 68Gb at 2048 bps and 137Gb at 4096 bps).
Sector tags that are simply too small to be deduplicated are contained in a single block pointed by the index (e.g.
Apple GCR sector tags).
Optical disks contain a track block that describes the tracks.
TODO: Streaming tapes contain a file block that describes the files and an optional partition block that describes the tape
partitions.
There are also blocks for image metadata, contents metadata and dump hardware information.
A differencing image will have all the metadata and deduplication tables, but the entries in these ones will be set to
0 if the block is stored in the parent image. TODO: This is not yet implemented.
Also because the file becomes useless without the index and deduplication table, each can be stored twice. In case of
the index it should just be searched for. In case of deduplication tables, both copies should be indexed.
Finally, writing new data to an existing image is just Copy-On-Write. Create a new block with the modified data, change
the pointer in the corresponding deduplication table.
P.S.: Data Position Measurement is doable, as soon as I know how to do it.
P.S.2: Support for floppy image containg bitslices and/or fluxes will be added soon.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using CUETools.Codecs.FLAKE;
using DiscImageChef.Checksums;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
using SharpCompress.Compressors.LZMA;
namespace DiscImageChef.DiscImages
{
public partial class DiscImageChef : IWritableOpticalImage, IVerifiableImage, IWritableTapeImage
{
bool alreadyWrittenZero;
/// <summary>Cache of uncompressed blocks.</summary>
Dictionary<ulong, byte[]> blockCache;
/// <summary>Cache of block headers.</summary>
Dictionary<ulong, BlockHeader> blockHeaderCache;
/// <summary>Stream used for writing blocks.</summary>
MemoryStream blockStream;
/// <summary>Provides checksum for deduplication of sectors.</summary>
SHA256 checksumProvider;
/// <summary>Provides CRC64.</summary>
Crc64Context crc64;
/// <summary>Header of the currently writing block.</summary>
BlockHeader currentBlockHeader;
/// <summary>Sector offset of writing position in currently writing block.</summary>
uint currentBlockOffset;
/// <summary>Current size in bytes of the block cache</summary>
uint currentCacheSize;
/// <summary>Cache of DDT entries.</summary>
Dictionary<ulong, ulong> ddtEntryCache;
MemoryStream decompressedStream;
bool deduplicate;
/// <summary>On-memory deduplication table indexed by checksum.</summary>
Dictionary<string, ulong> deduplicationTable;
/// <summary><see cref="CUETools.Codecs.FLAKE" /> writer.</summary>
FlakeWriter flakeWriter;
/// <summary><see cref="CUETools.Codecs.FLAKE" /> settings.</summary>
FlakeWriterSettings flakeWriterSettings;
/// <summary>Block with logical geometry.</summary>
GeometryBlock geometryBlock;
/// <summary>Image header.</summary>
DicHeader header;
/// <summary>Image information.</summary>
ImageInfo imageInfo;
/// <summary>Image data stream.</summary>
Stream imageStream;
/// <summary>Index.</summary>
List<IndexEntry> index;
/// <summary>If set to <c>true</c>, the DDT entries are in-memory.</summary>
bool inMemoryDdt;
bool isTape;
ulong lastWrittenBlock;
/// <summary>LZMA stream.</summary>
LzmaStream lzmaBlockStream;
/// <summary>LZMA properties.</summary>
LzmaEncoderProperties lzmaEncoderProperties;
Md5Context md5Provider;
/// <summary>Cache of media tags.</summary>
Dictionary<MediaTagType, byte[]> mediaTags;
byte[] mode2Subheaders;
bool nocompress;
/// <summary>If DDT is on-disk, this is the image stream offset at which it starts.</summary>
long outMemoryDdtPosition;
bool rewinded;
/// <summary>Cache for data that prefixes the user data on a sector (e.g. sync).</summary>
byte[] sectorPrefix;
uint[] sectorPrefixDdt;
MemoryStream sectorPrefixMs;
/// <summary>Cache for data that goes side by side with user data (e.g. CompactDisc subchannel).</summary>
byte[] sectorSubchannel;
/// <summary>Cache for data that suffixes the user data on a sector (e.g. edc, ecc).</summary>
byte[] sectorSuffix;
uint[] sectorSuffixDdt;
MemoryStream sectorSuffixMs;
Sha1Context sha1Provider;
Sha256Context sha256Provider;
/// <summary>Shift for calculating number of sectors in a block.</summary>
byte shift;
SpamSumContext spamsumProvider;
/// <summary>Cache for bytes to write/rad on-disk.</summary>
byte[] structureBytes;
/// <summary>Cache for pointer for marshaling structures.</summary>
IntPtr structurePointer;
Dictionary<ulong, ulong> tapeDdt;
/// <summary>Cache of CompactDisc track's flags</summary>
Dictionary<byte, byte> trackFlags;
/// <summary>Cache of CompactDisc track's ISRC</summary>
Dictionary<byte, string> trackIsrcs;
/// <summary>In-memory deduplication table</summary>
ulong[] userDataDdt;
bool writingLong;
ulong writtenSectors;
public DiscImageChef()
{
imageInfo = new ImageInfo
{
ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = false,
HasSessions = false,
Version = null,
Application = "DiscImageChef",
ApplicationVersion = null,
Creator = null,
Comments = null,
MediaManufacturer = null,
MediaModel = null,
MediaSerialNumber = null,
MediaBarcode = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}
}
}