Files
duplicati/Duplicati/Library/SharpRSync/ChecksumFile.cs
T
kenneth.skovhede@gmail.com 3bfd015d62 Final changes to support a fully localized Duplicati,
next step is to generate a setup for editing the resx files.

git-svn-id: https://duplicati.googlecode.com/svn/trunk@197 59da171f-624f-0410-aa54-27559c288bec
2009-06-17 22:47:51 +00:00

208 lines
8.7 KiB
C#

#region Disclaimer / License
// Copyright (C) 2009, 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
{
/// <summary>
/// This class reads and writes signature files in the same format as RDiff
/// </summary>
public class ChecksumFile
{
private const int MD4_LENGTH = 16;
private int m_blocklen;
private int m_stronglen;
//This dictionary has the weak checksum as the key, and an index into the stronglist
private Dictionary<uint, List<int>> m_checksums;
//This list is ordered and each entry has the weak and strong checksum
private List<KeyValuePair<uint, byte[]>> m_strongList;
private Adler32Checksum m_adler;
private System.Security.Cryptography.HashAlgorithm m_md4;
//private System.Security.Cryptography.HashAlgorithm m_sha;
/// <summary>
/// Reads a ChecksumFile from a stream
/// </summary>
/// <param name="input">The stream to read from</param>
public ChecksumFile(System.IO.Stream input)
{
m_checksums = new Dictionary<uint, List<int>>();
m_strongList = new List<KeyValuePair<uint, byte[]>>();
byte[] sig = new byte[4];
if (Utility.ForceStreamRead(input, sig, 4) != 4)
throw new Exception(Strings.ChecksumFile.EndofstreamBeforeSignatureError);
for (int i = 0; i < sig.Length; i++)
if (RDiffBinary.SIGNATURE_MAGIC[i] != sig[i])
throw new Exception(Strings.ChecksumFile.InvalidSignatureHeaderError);
if (Utility.ForceStreamRead(input, sig, 4) != 4)
throw new Exception(Strings.ChecksumFile.EndofstreamInBlocksizeError);
m_blocklen = BitConverter.ToInt32(RDiffBinary.FixEndian(sig), 0);
if (m_blocklen < 1 || m_blocklen > int.MaxValue / 2)
throw new Exception(string.Format(Strings.ChecksumFile.InvalidBlocksizeError, m_blocklen));
if (Utility.ForceStreamRead(input, sig, 4) != 4)
throw new Exception(Strings.ChecksumFile.EndofstreamInStronglenError);
m_stronglen = BitConverter.ToInt32(RDiffBinary.FixEndian(sig), 0);
if (m_stronglen < 1 || m_stronglen > MD4_LENGTH)
throw new Exception(string.Format(Strings.ChecksumFile.InvalidStrongsizeError, m_stronglen));
while(true)
{
if (Utility.ForceStreamRead(input, sig, 4) != 4)
break;
uint weak = BitConverter.ToUInt32(RDiffBinary.FixEndian(sig), 0); ;
byte[] strongbuf = new byte[m_stronglen];
if (Utility.ForceStreamRead(input, strongbuf, strongbuf.Length) != strongbuf.Length)
throw new Exception(Strings.ChecksumFile.EndofstreamInStrongSignatureError);
if (!m_checksums.ContainsKey(weak))
m_checksums[weak] = new List<int>();
m_checksums[weak].Add(m_strongList.Count);
m_strongList.Add(new KeyValuePair<uint, byte[]>(weak, strongbuf));
}
}
/// <summary>
/// Constructs a new CheckSum file with default sizes
/// </summary>
public ChecksumFile()
: this(Adler32Checksum.DEFAULT_BLOCK_SIZE, 8)
{
}
/// <summary>
/// Constructs a new CheckSum file
/// <param name="blocklength">The length of a single block</param>
/// <param name="stronglength">The number of bytes in the MD4 checksum</param>
/// </summary>
public ChecksumFile(int blocklength, int stronglength)
{
m_blocklen = blocklength;
m_stronglen = stronglength;
m_checksums = new Dictionary<uint, List<int>>();
m_strongList = new List<KeyValuePair<uint, byte[]>>();
m_md4 = System.Security.Cryptography.MD4.Create("MD4");
m_md4.Initialize();
//m_sha = System.Security.Cryptography.HashAlgorithm.Create("SHA256");
//m_sha.Initialize();
}
/// <summary>
/// Adds all checksums in an entire stream
/// </summary>
/// <param name="stream">The stream to read checksums from</param>
public void AddStream(System.IO.Stream stream)
{
byte[] buffer = new byte[m_blocklen];
int a;
while ((a = Utility.ForceStreamRead(stream, buffer, buffer.Length)) != 0)
{
if (a != buffer.Length)
{
byte[] tmp = new byte[a];
Array.Copy(buffer, tmp, a);
AddChunk(tmp);
}
else
AddChunk(buffer);
}
}
/// <summary>
/// Adds a chunck of data to checksum list
/// </summary>
/// <param name="buffer">The data to add a checksum entry for</param>
public void AddChunk(byte[] buffer)
{
m_adler = new Adler32Checksum(new System.IO.MemoryStream(buffer), m_blocklen);
if (!m_md4.CanReuseTransform)
m_md4 = System.Security.Cryptography.MD4.Create("MD4");
m_md4.Initialize();
byte[] strong = m_md4.ComputeHash(buffer);
if (strong.Length > m_stronglen)
{
byte[] tmp = new byte[m_stronglen];
Array.Copy(strong, tmp, tmp.Length);
strong = tmp;
}
if (!m_checksums.ContainsKey(m_adler.Checksum))
m_checksums[m_adler.Checksum] = new List<int>();
m_checksums[m_adler.Checksum].Add(m_strongList.Count);
m_strongList.Add(new KeyValuePair<uint, byte[]>(m_adler.Checksum, strong));
//TODO: Figure out if we can get a complete file signature this way
//m_sha.ComputeHash(buffer);
}
/// <summary>
/// Writes a RDiff compatible file with the signatures
/// </summary>
/// <param name="output"></param>
public void Save(System.IO.Stream output)
{
output.Write(RDiffBinary.SIGNATURE_MAGIC, 0, RDiffBinary.SIGNATURE_MAGIC.Length);
output.Write(RDiffBinary.FixEndian(BitConverter.GetBytes(m_blocklen)), 0, 4);
output.Write(RDiffBinary.FixEndian(BitConverter.GetBytes(m_stronglen)), 0, 4);
foreach(KeyValuePair<uint, byte[]> k in m_strongList)
{
output.Write(RDiffBinary.FixEndian(BitConverter.GetBytes(k.Key)), 0, 4);
output.Write(k.Value, 0, m_stronglen);
}
output.Flush();
}
/// <summary>
/// Looks in the checksum table for a match to the weak entry
/// </summary>
/// <param name="weak">The entry to find a match for</param>
/// <returns>A list of possible MD4 matches, and their indexes</returns>
public List<KeyValuePair<int, byte[]>> FindChunk(uint weak)
{
if (!m_checksums.ContainsKey(weak))
return null;
else
{
List<KeyValuePair<int, byte[]>> lst = new List<KeyValuePair<int, byte[]>>();
foreach (int i in m_checksums[weak])
lst.Add(new KeyValuePair<int, byte[]>(i, m_strongList[i].Value));
return lst;
}
}
/// <summary>
/// Gets the number of bytes in each hashed block
/// </summary>
public int BlockLength { get { return m_blocklen; } }
/// <summary>
/// Gets the number of bytes in a signature file
/// </summary>
public int StrongLength { get { return m_stronglen; } }
}
}