#region Disclaimer / License // Copyright (C) 2011, Kenneth Skovhede // http://www.hexad.dk, opensource@hexad.dk // // 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, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #endregion using System; using System.Collections.Generic; using System.Text; namespace Duplicati.Library.SharpRSync { /// /// This class writes signature files in the same format as RDiff /// public class ChecksumFileWriter { /// /// The number of bytes to include in each checksum /// public const int DEFAULT_BLOCK_SIZE = 2048; /// /// The default number of bytes to use from the strong hash /// public const int DEFAULT_STRONG_LEN = 8; /// /// The default number of bytes generated per input block /// public const int DEFAULT_BYTES_PER_BLOCK = DEFAULT_STRONG_LEN + 4; /// /// The number of bytes used for the rdiff header /// public static readonly int HEADER_SIZE = RDiffBinary.SIGNATURE_MAGIC.Length + 4 + 4; /// /// The length of a datablock /// private int m_blocklen; /// /// The number of bytes used for storing the strong signature /// private int m_stronglen; /// /// The re-used MD4 algorithm /// private System.Security.Cryptography.HashAlgorithm m_hashAlgorithm; /// /// The stream into which the signature data is written /// private System.IO.Stream m_outstream; /// /// Constructs a new CheckSum file with default sizes /// /// The stream into which the checksum data is written public ChecksumFileWriter(System.IO.Stream outputstream) : this(outputstream, DEFAULT_BLOCK_SIZE, DEFAULT_STRONG_LEN) { } /// /// Constructs a new CheckSum file /// The length of a single block /// The number of bytes in the MD4 checksum /// The stream into which the checksum data is written /// public ChecksumFileWriter(System.IO.Stream outputstream, int blocklength, int stronglength) { if (outputstream == null) throw new ArgumentNullException("outputstream"); m_blocklen = blocklength; m_stronglen = stronglength; m_outstream = outputstream; m_hashAlgorithm = MD4Helper.Create(); m_hashAlgorithm.Initialize(); m_outstream.Write(RDiffBinary.SIGNATURE_MAGIC, 0, RDiffBinary.SIGNATURE_MAGIC.Length); m_outstream.Write(RDiffBinary.FixEndian(BitConverter.GetBytes(m_blocklen)), 0, 4); m_outstream.Write(RDiffBinary.FixEndian(BitConverter.GetBytes(m_stronglen)), 0, 4); } /// /// Adds a chunck of data to checksum list /// /// The data to add a checksum entry for public void AddChunk(byte[] buffer) { AddChunk(buffer, 0, buffer.Length); } /// /// Adds a chunck of data to checksum list /// /// The data to add a checksum entry for /// The index in the buffer to start reading from /// The number of bytes to extract from the array public void AddChunk(byte[] buffer, int index, int count) { if (!m_hashAlgorithm.CanReuseTransform) m_hashAlgorithm = Mono.Security.Cryptography.MD4.Create("MD4"); m_outstream.Write(RDiffBinary.FixEndian(BitConverter.GetBytes(Adler32Checksum.Calculate(buffer, index, count))), 0, 4); m_outstream.Write(m_hashAlgorithm.ComputeHash(buffer, index, count), 0, m_stronglen); } /// /// Adds all checksums in an entire stream /// /// The stream to read checksums from public void AddStream(System.IO.Stream stream) { byte[] buffer = new byte[m_blocklen]; int a; //These streams do not fragment data if (stream is System.IO.FileStream || stream is System.IO.MemoryStream) { while ((a = stream.Read(buffer, 0, buffer.Length)) != 0) AddChunk(buffer, 0, a); } else { while ((a = Utility.ForceStreamRead(stream, buffer, buffer.Length)) != 0) AddChunk(buffer, 0, a); } } /// /// Gets the number of bytes in each hashed block /// public int BlockLength { get { return m_blocklen; } } /// /// Gets the number of bytes in a signature file /// public int StrongLength { get { return m_stronglen; } } /// /// Gets the number of bytes generated per input block /// public int BytesPrBlock { get { return m_stronglen + 4; } } /// /// Returns the number of bytes generated when processing the specified amount of bytes /// /// The size of the file to process /// The expected size of the signature file public int BytesGeneratedForSignature(long filesize) { return (int)(SharpRSync.ChecksumFileWriter.HEADER_SIZE + (((filesize + m_blocklen - 1) / m_blocklen) * (m_stronglen + 4))); } } }