Files
duplicati/Duplicati/Library/SharpRSync/ChecksumGeneratingStream.cs
T
kenneth.skovhede@gmail.com 8581a4496b Update issue #247
Status: Fixed

The implementation is a mix of the the three mentioned methods (issue #247).
There is a commandline option called --open-file-policy, which can be set to 
"ignore", "snapshot" or "copy". 

The "ignore" setting does the same as previous version, simply exclude the file from the 
backup set.

If the setting is either "snapshot" or "copy", and the file it locked, it is opened in non-excusive read mode.
Once open, a in-memory signature file is generated. This is compared to the previous signature to detect changes,
just as the normal operation. If the file has not changed, the file is skipped as it would normally be.

If the file has changed and the policy is "snapshot", the file will be processed as normal,
but during the file read, a new signature is generated. This new signature is stored
in the signature archives. When writing the new signature, it is compared to the original signature.
If the file has changed during the backup operation, a warning is written to the output log.

If the policy was set to "copy", the modified file is copied to a temporary location,
while a new signature is generated. If the copy fails due to a file change, the file 
is omitted from the backup set. If the copy succeeds, the file is known to not have changed
during the first access and the copy, which gives some assurance that the file is not
actively being written, at the expense of a copy operation.

The default setting is "snapshot", because this is likely to work in most scenarios,
and potentially produces a broken file rather than guarantee a missing file (by "ignore").

Note that when reading an open file, there is always a chance that the file is being written,
so you may end up with a broken file in the backup. This is also true for the VSS and LVM snapshots,
except for VSS-aware applications.

The "copy" method is meant to minimize this risk, but there is no guarantee that the file is in an
acceptable state, just because it has not been written for any period.

git-svn-id: https://duplicati.googlecode.com/svn/trunk@588 59da171f-624f-0410-aa54-27559c288bec
2010-11-20 13:57:12 +00:00

143 lines
4.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
namespace Duplicati.Library.SharpRSync
{
/// <summary>
/// This class represents a readable stream which generates a signature stream while being read
/// </summary>
public class ChecksumGeneratingStream : System.IO.Stream
{
/// <summary>
/// The checksum output stream
/// </summary>
private ChecksumFileWriter m_outstream;
/// <summary>
/// The stream being read
/// </summary>
private System.IO.Stream m_basestream;
/// <summary>
/// A buffer for storing checksum data
/// </summary>
private byte[] m_checksumBuffer;
/// <summary>
/// The current index into the checksum buffer
/// </summary>
private int m_checksumBufferIndex;
/// <summary>
/// Constructs a new checksum generating stream
/// </summary>
/// <param name="signatureOutput">The stream into which signature data is written</param>
/// <param name="inputData">The source data stream</param>
public ChecksumGeneratingStream(System.IO.Stream signatureOutput, System.IO.Stream inputData)
{
if (!signatureOutput.CanWrite)
throw new ArgumentException("signatureOutput");
if (!inputData.CanRead)
throw new ArgumentException("inputData");
m_outstream = new ChecksumFileWriter(signatureOutput);
m_basestream = inputData;
m_checksumBuffer = new byte[m_outstream.BlockLength];
m_checksumBufferIndex = 0;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return false; }
}
public override void Flush()
{
if (m_basestream != null)
{
m_basestream.Dispose();
if (m_checksumBufferIndex != 0)
m_outstream.AddChunk(m_checksumBuffer, 0, m_checksumBufferIndex);
}
m_outstream = null;
m_basestream = null;
}
public override long Length
{
get { return m_basestream.Length; }
}
public override long Position
{
get
{
return m_basestream.Position;
}
set
{
if (m_basestream.Position != value)
throw new InvalidOperationException();
}
}
public override int Read(byte[] buffer, int offset, int count)
{
int a = m_basestream.Read(buffer, offset, count);
int remains = a;
while (remains > 0)
{
int bytesToWrite = Math.Min(remains, m_checksumBuffer.Length - m_checksumBufferIndex);
Array.Copy(buffer, offset, m_checksumBuffer, m_checksumBufferIndex, bytesToWrite);
m_checksumBufferIndex += bytesToWrite;
if (m_checksumBufferIndex == m_checksumBuffer.Length)
{
m_outstream.AddChunk(m_checksumBuffer);
m_checksumBufferIndex = 0;
}
remains -= bytesToWrite;
offset += bytesToWrite;
}
return a;
}
public override long Seek(long offset, System.IO.SeekOrigin origin)
{
throw new InvalidOperationException();
}
public override void SetLength(long value)
{
throw new InvalidOperationException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new InvalidOperationException();
}
protected override void Dispose(bool disposing)
{
if (disposing)
Flush();
base.Dispose(disposing);
}
}
}